Coverage for pyVHDLModel/DesignUnit.py: 72%
315 statements
« prev ^ index » next coverage.py v7.6.9, created at 2024-12-20 22:13 +0000
« prev ^ index » next coverage.py v7.6.9, created at 2024-12-20 22:13 +0000
1# ==================================================================================================================== #
2# __ ___ _ ____ _ __ __ _ _ #
3# _ __ _ \ \ / / | | | _ \| | | \/ | ___ __| | ___| | #
4# | '_ \| | | \ \ / /| |_| | | | | | | |\/| |/ _ \ / _` |/ _ \ | #
5# | |_) | |_| |\ V / | _ | |_| | |___| | | | (_) | (_| | __/ | #
6# | .__/ \__, | \_/ |_| |_|____/|_____|_| |_|\___/ \__,_|\___|_| #
7# |_| |___/ #
8# ==================================================================================================================== #
9# Authors: #
10# Patrick Lehmann #
11# #
12# License: #
13# ==================================================================================================================== #
14# Copyright 2017-2024 Patrick Lehmann - Boetzingen, Germany #
15# Copyright 2016-2017 Patrick Lehmann - Dresden, Germany #
16# #
17# Licensed under the Apache License, Version 2.0 (the "License"); #
18# you may not use this file except in compliance with the License. #
19# You may obtain a copy of the License at #
20# #
21# http://www.apache.org/licenses/LICENSE-2.0 #
22# #
23# Unless required by applicable law or agreed to in writing, software #
24# distributed under the License is distributed on an "AS IS" BASIS, #
25# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
26# See the License for the specific language governing permissions and #
27# limitations under the License. #
28# #
29# SPDX-License-Identifier: Apache-2.0 #
30# ==================================================================================================================== #
31#
32"""
33This module contains parts of an abstract document language model for VHDL.
35Design units are contexts, entities, architectures, packages and their bodies as well as configurations.
36"""
37from typing import List, Dict, Union, Iterable, Optional as Nullable
39from pyTooling.Decorators import export, readonly
40from pyTooling.MetaClasses import ExtendedType
41from pyTooling.Graph import Vertex
43from pyVHDLModel.Exception import VHDLModelException
44from pyVHDLModel.Base import ModelEntity, NamedEntityMixin, DocumentedEntityMixin
45from pyVHDLModel.Namespace import Namespace
46from pyVHDLModel.Regions import ConcurrentDeclarationRegionMixin
47from pyVHDLModel.Symbol import Symbol, PackageSymbol, EntitySymbol, LibraryReferenceSymbol
48from pyVHDLModel.Interface import GenericInterfaceItemMixin, PortInterfaceItemMixin
49from pyVHDLModel.Object import DeferredConstant
50from pyVHDLModel.Concurrent import ConcurrentStatement, ConcurrentStatementsMixin
53@export
54class Reference(ModelEntity):
55 """
56 A base-class for all references.
58 .. seealso::
60 * :class:`~pyVHDLModel.DesignUnit.LibraryClause`
61 * :class:`~pyVHDLModel.DesignUnit.UseClause`
62 * :class:`~pyVHDLModel.DesignUnit.ContextReference`
63 """
65 _symbols: List[Symbol]
67 def __init__(self, symbols: Iterable[Symbol], parent: ModelEntity = None) -> None:
68 """
69 Initializes a reference by taking a list of symbols and a parent reference.
71 :param symbols: A list of symbols this reference references to.
72 :param parent: Reference to the logical parent in the model hierarchy.
73 """
74 super().__init__(parent)
76 self._symbols = [s for s in symbols]
78 @readonly
79 def Symbols(self) -> List[Symbol]:
80 """
81 Read-only property to access the symbols this reference references to (:attr:`_symbols`).
83 :returns: A list of symbols.
84 """
85 return self._symbols
88@export
89class LibraryClause(Reference):
90 """
91 Represents a library clause.
93 .. admonition:: Example
95 .. code-block:: VHDL
97 library std, ieee;
98 """
100 @readonly
101 def Symbols(self) -> List[LibraryReferenceSymbol]:
102 """
103 Read-only property to access the symbols this library clause references to (:attr:`_symbols`).
105 :returns: A list of library reference symbols.
106 """
107 return self._symbols
110@export
111class UseClause(Reference):
112 """
113 Represents a use clause.
115 .. admonition:: Example
117 .. code-block:: VHDL
119 use std.text_io.all, ieee.numeric_std.all;
120 """
123@export
124class ContextReference(Reference):
125 """
126 Represents a context reference.
128 .. hint:: It's called *context reference* not *context clause* by the LRM.
130 .. admonition:: Example
132 .. code-block:: VHDL
134 context ieee.ieee_std_context;
135 """
138ContextUnion = Union[
139 LibraryClause,
140 UseClause,
141 ContextReference
142]
145@export
146class DesignUnitWithContextMixin(metaclass=ExtendedType, mixin=True):
147 """
148 A mixin-class for all design units with a context.
149 """
152@export
153class DesignUnit(ModelEntity, NamedEntityMixin, DocumentedEntityMixin):
154 """
155 A base-class for all design units.
157 .. seealso::
159 * :class:`Primary design units <pyVHDLModel.DesignUnit.PrimaryUnit>`
161 * :class:`~pyVHDLModel.DesignUnit.Context`
162 * :class:`~pyVHDLModel.DesignUnit.Entity`
163 * :class:`~pyVHDLModel.DesignUnit.Package`
164 * :class:`~pyVHDLModel.DesignUnit.Configuration`
166 * :class:`Secondary design units <pyVHDLModel.DesignUnit.SecondaryUnit>`
168 * :class:`~pyVHDLModel.DesignUnit.Architecture`
169 * :class:`~pyVHDLModel.DesignUnit.PackageBody`
170 """
172 _document: 'Document' #: The VHDL library, the design unit was analyzed into.
174 # Either written as statements before (e.g. entity, architecture, package, ...), or as statements inside (context)
175 _contextItems: List['ContextUnion'] #: List of all context items (library, use and context clauses).
176 _libraryReferences: List['LibraryClause'] #: List of library clauses.
177 _packageReferences: List['UseClause'] #: List of use clauses.
178 _contextReferences: List['ContextReference'] #: List of context clauses.
180 _referencedLibraries: Dict[str, 'Library'] #: Referenced libraries based on explicit library clauses or implicit inheritance
181 _referencedPackages: Dict[str, Dict[str, 'Package']] #: Referenced packages based on explicit use clauses or implicit inheritance
182 _referencedContexts: Dict[str, 'Context'] #: Referenced contexts based on explicit context references or implicit inheritance
184 _dependencyVertex: Vertex[None, None, str, 'DesignUnit', None, None, None, None, None, None, None, None, None, None, None, None, None] #: Reference to the vertex in the dependency graph representing the design unit. |br| This reference is set by :meth:`~pyVHDLModel.Design.CreateDependencyGraph`.
185 _hierarchyVertex: Vertex[None, None, str, 'DesignUnit', None, None, None, None, None, None, None, None, None, None, None, None, None] #: The vertex in the hierarchy graph
187 _namespace: 'Namespace'
189 def __init__(self, identifier: str, contextItems: Nullable[Iterable[ContextUnion]] = None, documentation: Nullable[str] = None, parent: ModelEntity = None) -> None:
190 """
191 Initializes a design unit.
193 :param identifier: Identifier (name) of the design unit.
194 :param contextItems: A sequence of library, use or context clauses.
195 :param documentation: Associated documentation of the design unit.
196 :param parent: Reference to the logical parent in the model hierarchy.
197 """
198 super().__init__(parent)
199 NamedEntityMixin.__init__(self, identifier)
200 DocumentedEntityMixin.__init__(self, documentation)
202 self._document = None
204 self._contextItems = []
205 self._libraryReferences = []
206 self._packageReferences = []
207 self._contextReferences = []
209 if contextItems is not None:
210 for item in contextItems:
211 self._contextItems.append(item)
212 if isinstance(item, UseClause):
213 self._packageReferences.append(item)
214 elif isinstance(item, LibraryClause):
215 self._libraryReferences.append(item)
216 elif isinstance(item, ContextReference): 216 ↛ 210line 216 didn't jump to line 210 because the condition on line 216 was always true
217 self._contextReferences.append(item)
219 self._referencedLibraries = {}
220 self._referencedPackages = {}
221 self._referencedContexts = {}
223 self._dependencyVertex = None
224 self._hierarchyVertex = None
226 self._namespace = Namespace(self._normalizedIdentifier)
228 @readonly
229 def Document(self) -> 'Document':
230 return self._document
232 @Document.setter
233 def Document(self, document: 'Document') -> None:
234 self._document = document
236 @property
237 def Library(self) -> 'Library':
238 return self._parent
240 @Library.setter
241 def Library(self, library: 'Library') -> None:
242 self._parent = library
244 @property
245 def ContextItems(self) -> List['ContextUnion']:
246 """
247 Read-only property to access the sequence of all context items comprising library, use and context clauses
248 (:attr:`_contextItems`).
250 :returns: Sequence of context items.
251 """
252 return self._contextItems
254 @property
255 def ContextReferences(self) -> List['ContextReference']:
256 """
257 Read-only property to access the sequence of context clauses (:attr:`_contextReferences`).
259 :returns: Sequence of context clauses.
260 """
261 return self._contextReferences
263 @property
264 def LibraryReferences(self) -> List['LibraryClause']:
265 """
266 Read-only property to access the sequence of library clauses (:attr:`_libraryReferences`).
268 :returns: Sequence of library clauses.
269 """
270 return self._libraryReferences
272 @property
273 def PackageReferences(self) -> List['UseClause']:
274 """
275 Read-only property to access the sequence of use clauses (:attr:`_packageReferences`).
277 :returns: Sequence of use clauses.
278 """
279 return self._packageReferences
281 @property
282 def ReferencedLibraries(self) -> Dict[str, 'Library']:
283 return self._referencedLibraries
285 @property
286 def ReferencedPackages(self) -> Dict[str, 'Package']:
287 return self._referencedPackages
289 @property
290 def ReferencedContexts(self) -> Dict[str, 'Context']:
291 return self._referencedContexts
293 @property
294 def DependencyVertex(self) -> Vertex:
295 """
296 Read-only property to access the corresponding dependency vertex (:attr:`_dependencyVertex`).
298 The dependency vertex references this design unit by its value field.
300 :returns: The corresponding dependency vertex.
301 """
302 return self._dependencyVertex
304 @property
305 def HierarchyVertex(self) -> Vertex:
306 """
307 Read-only property to access the corresponding hierarchy vertex (:attr:`_hierarchyVertex`).
309 The hierarchy vertex references this design unit by its value field.
311 :returns: The corresponding hierarchy vertex.
312 """
313 return self._hierarchyVertex
316@export
317class PrimaryUnit(DesignUnit):
318 """
319 A base-class for all primary design units.
321 .. seealso::
323 * :class:`~pyVHDLModel.DesignUnit.Context`
324 * :class:`~pyVHDLModel.DesignUnit.Entity`
325 * :class:`~pyVHDLModel.DesignUnit.Package`
326 * :class:`~pyVHDLModel.DesignUnit.Configuration`
327 """
330@export
331class SecondaryUnit(DesignUnit):
332 """
333 A base-class for all secondary design units.
335 .. seealso::
337 * :class:`~pyVHDLModel.DesignUnit.Architecture`
338 * :class:`~pyVHDLModel.DesignUnit.PackageBody`
339 """
342@export
343class Context(PrimaryUnit):
344 """
345 Represents a context declaration.
347 A context contains a generic list of all its items (library clauses, use clauses and context references) in
348 :data:`_references`.
350 Furthermore, when a context gets initialized, the item kinds get separated into individual lists:
352 * :class:`~pyVHDLModel.DesignUnit.LibraryClause` |rarr| :data:`_libraryReferences`
353 * :class:`~pyVHDLModel.DesignUnit.UseClause` |rarr| :data:`_packageReferences`
354 * :class:`~pyVHDLModel.DesignUnit.ContextReference` |rarr| :data:`_contextReferences`
356 When :meth:`pyVHDLModel.Design.LinkContexts` got called, these lists were processed and the fields:
358 * :data:`_referencedLibraries` (:pycode:`Dict[libName, Library]`)
359 * :data:`_referencedPackages` (:pycode:`Dict[libName, [pkgName, Package]]`)
360 * :data:`_referencedContexts` (:pycode:`Dict[libName, [ctxName, Context]]`)
362 are populated.
364 .. admonition:: Example
366 .. code-block:: VHDL
368 context ctx is
369 -- ...
370 end context;
371 """
373 _references: List[ContextUnion]
375 def __init__(self, identifier: str, references: Nullable[Iterable[ContextUnion]] = None, documentation: Nullable[str] = None, parent: ModelEntity = None) -> None:
376 super().__init__(identifier, None, documentation, parent)
378 self._references = []
379 self._libraryReferences = []
380 self._packageReferences = []
381 self._contextReferences = []
383 if references is not None:
384 for reference in references:
385 self._references.append(reference)
386 reference._parent = self
388 if isinstance(reference, LibraryClause):
389 self._libraryReferences.append(reference)
390 elif isinstance(reference, UseClause): 390 ↛ 392line 390 didn't jump to line 392 because the condition on line 390 was always true
391 self._packageReferences.append(reference)
392 elif isinstance(reference, ContextReference):
393 self._contextReferences.append(reference)
394 else:
395 raise VHDLModelException() # FIXME: needs exception message
397 @property
398 def LibraryReferences(self) -> List[LibraryClause]:
399 return self._libraryReferences
401 @property
402 def PackageReferences(self) -> List[UseClause]:
403 return self._packageReferences
405 @property
406 def ContextReferences(self) -> List[ContextReference]:
407 return self._contextReferences
409 def __str__(self) -> str:
410 lib = self._parent._identifier + "?" if self._parent is not None else ""
412 return f"Context: {lib}.{self._identifier}"
415@export
416class Package(PrimaryUnit, DesignUnitWithContextMixin, ConcurrentDeclarationRegionMixin):
417 """
418 Represents a package declaration.
420 .. admonition:: Example
422 .. code-block:: VHDL
424 package pkg is
425 -- ...
426 end package;
427 """
429 _packageBody: Nullable["PackageBody"]
431 _genericItems: List[GenericInterfaceItemMixin]
433 _deferredConstants: Dict[str, DeferredConstant]
434 _components: Dict[str, 'Component']
436 def __init__(
437 self,
438 identifier: str,
439 contextItems: Nullable[Iterable[ContextUnion]] = None,
440 genericItems: Nullable[Iterable[GenericInterfaceItemMixin]] = None,
441 declaredItems: Nullable[Iterable] = None,
442 documentation: Nullable[str] = None,
443 parent: ModelEntity = None
444 ) -> None:
445 super().__init__(identifier, contextItems, documentation, parent)
446 DesignUnitWithContextMixin.__init__(self)
447 ConcurrentDeclarationRegionMixin.__init__(self, declaredItems)
449 self._packageBody = None
451 # TODO: extract to mixin
452 self._genericItems = [] # TODO: convert to dict
453 if genericItems is not None: 453 ↛ 454line 453 didn't jump to line 454 because the condition on line 453 was never true
454 for generic in genericItems:
455 self._genericItems.append(generic)
456 generic._parent = self
458 self._deferredConstants = {}
459 self._components = {}
461 @property
462 def PackageBody(self) -> Nullable["PackageBody"]:
463 return self._packageBody
465 @property
466 def GenericItems(self) -> List[GenericInterfaceItemMixin]:
467 return self._genericItems
469 @property
470 def DeclaredItems(self) -> List:
471 return self._declaredItems
473 @property
474 def DeferredConstants(self):
475 return self._deferredConstants
477 @property
478 def Components(self):
479 return self._components
481 def _IndexOtherDeclaredItem(self, item):
482 if isinstance(item, DeferredConstant):
483 for normalizedIdentifier in item.NormalizedIdentifiers:
484 self._deferredConstants[normalizedIdentifier] = item
485 elif isinstance(item, Component):
486 self._components[item.NormalizedIdentifier] = item
487 else:
488 super()._IndexOtherDeclaredItem(item)
490 def __str__(self) -> str:
491 lib = self._parent._identifier if self._parent is not None else "%"
493 return f"Package: '{lib}.{self._identifier}'"
495 def __repr__(self) -> str:
496 lib = self._parent._identifier if self._parent is not None else "%"
498 return f"{lib}.{self._identifier}"
501@export
502class PackageBody(SecondaryUnit, DesignUnitWithContextMixin, ConcurrentDeclarationRegionMixin):
503 """
504 Represents a package body declaration.
506 .. admonition:: Example
508 .. code-block:: VHDL
510 package body pkg is
511 -- ...
512 end package body;
513 """
515 _package: PackageSymbol
517 def __init__(
518 self,
519 packageSymbol: PackageSymbol,
520 contextItems: Nullable[Iterable[ContextUnion]] = None,
521 declaredItems: Nullable[Iterable] = None,
522 documentation: Nullable[str] = None,
523 parent: ModelEntity = None
524 ) -> None:
525 super().__init__(packageSymbol.Name.Identifier, contextItems, documentation, parent)
526 DesignUnitWithContextMixin.__init__(self)
527 ConcurrentDeclarationRegionMixin.__init__(self, declaredItems)
529 self._package = packageSymbol
530 packageSymbol._parent = self
532 @property
533 def Package(self) -> PackageSymbol:
534 return self._package
536 @property
537 def DeclaredItems(self) -> List:
538 return self._declaredItems
540 def LinkDeclaredItemsToPackage(self) -> None:
541 pass
543 def __str__(self) -> str:
544 lib = self._parent._identifier + "?" if self._parent is not None else ""
546 return f"Package Body: {lib}.{self._identifier}(body)"
548 def __repr__(self) -> str:
549 lib = self._parent._identifier + "?" if self._parent is not None else ""
551 return f"{lib}.{self._identifier}(body)"
554@export
555class Entity(PrimaryUnit, DesignUnitWithContextMixin, ConcurrentDeclarationRegionMixin, ConcurrentStatementsMixin):
556 """
557 Represents an entity declaration.
559 .. admonition:: Example
561 .. code-block:: VHDL
563 entity ent is
564 -- ...
565 end entity;
566 """
568 _genericItems: List[GenericInterfaceItemMixin]
569 _portItems: List[PortInterfaceItemMixin]
571 _architectures: Dict[str, 'Architecture']
573 def __init__(
574 self,
575 identifier: str,
576 contextItems: Nullable[Iterable[ContextUnion]] = None,
577 genericItems: Nullable[Iterable[GenericInterfaceItemMixin]] = None,
578 portItems: Nullable[Iterable[PortInterfaceItemMixin]] = None,
579 declaredItems: Nullable[Iterable] = None,
580 statements: Nullable[Iterable[ConcurrentStatement]] = None,
581 documentation: Nullable[str] = None,
582 parent: ModelEntity = None
583 ) -> None:
584 super().__init__(identifier, contextItems, documentation, parent)
585 DesignUnitWithContextMixin.__init__(self)
586 ConcurrentDeclarationRegionMixin.__init__(self, declaredItems)
587 ConcurrentStatementsMixin.__init__(self, statements)
589 # TODO: extract to mixin
590 self._genericItems = []
591 if genericItems is not None: 591 ↛ 592line 591 didn't jump to line 592 because the condition on line 591 was never true
592 for item in genericItems:
593 self._genericItems.append(item)
594 item._parent = self
596 # TODO: extract to mixin
597 self._portItems = []
598 if portItems is not None: 598 ↛ 599line 598 didn't jump to line 599 because the condition on line 598 was never true
599 for item in portItems:
600 self._portItems.append(item)
601 item._parent = self
603 self._architectures = {}
605 # TODO: extract to mixin for generics
606 @property
607 def GenericItems(self) -> List[GenericInterfaceItemMixin]:
608 return self._genericItems
610 # TODO: extract to mixin for ports
611 @property
612 def PortItems(self) -> List[PortInterfaceItemMixin]:
613 return self._portItems
615 @property
616 def Architectures(self) -> Dict[str, 'Architecture']:
617 return self._architectures
619 def __str__(self) -> str:
620 lib = self._parent._identifier if self._parent is not None else "%"
621 archs = ', '.join(self._architectures.keys()) if self._architectures else "%"
623 return f"Entity: '{lib}.{self._identifier}({archs})'"
625 def __repr__(self) -> str:
626 lib = self._parent._identifier if self._parent is not None else "%"
627 archs = ', '.join(self._architectures.keys()) if self._architectures else "%"
629 return f"{lib}.{self._identifier}({archs})"
632@export
633class Architecture(SecondaryUnit, DesignUnitWithContextMixin, ConcurrentDeclarationRegionMixin, ConcurrentStatementsMixin):
634 """
635 Represents an architecture declaration.
637 .. admonition:: Example
639 .. code-block:: VHDL
641 architecture rtl of ent is
642 -- ...
643 begin
644 -- ...
645 end architecture;
646 """
648 _entity: EntitySymbol
650 def __init__(
651 self,
652 identifier: str,
653 entity: EntitySymbol,
654 contextItems: Nullable[Iterable[Context]] = None,
655 declaredItems: Nullable[Iterable] = None,
656 statements: Iterable['ConcurrentStatement'] = None,
657 documentation: Nullable[str] = None,
658 parent: ModelEntity = None
659 ) -> None:
660 super().__init__(identifier, contextItems, documentation, parent)
661 DesignUnitWithContextMixin.__init__(self)
662 ConcurrentDeclarationRegionMixin.__init__(self, declaredItems)
663 ConcurrentStatementsMixin.__init__(self, statements)
665 self._entity = entity
666 entity._parent = self
668 @property
669 def Entity(self) -> EntitySymbol:
670 return self._entity
672 def __str__(self) -> str:
673 lib = self._parent._identifier if self._parent is not None else "%"
674 ent = self._entity._name._identifier if self._entity is not None else "%"
676 return f"Architecture: {lib}.{ent}({self._identifier})"
678 def __repr__(self) -> str:
679 lib = self._parent._identifier if self._parent is not None else "%"
680 ent = self._entity._name._identifier if self._entity is not None else "%"
682 return f"{lib}.{ent}({self._identifier})"
685@export
686class Component(ModelEntity, NamedEntityMixin, DocumentedEntityMixin):
687 """
688 Represents a configuration declaration.
690 .. admonition:: Example
692 .. code-block:: VHDL
694 component ent is
695 -- ...
696 end component;
697 """
699 _genericItems: List[GenericInterfaceItemMixin]
700 _portItems: List[PortInterfaceItemMixin]
702 _entity: Nullable[Entity]
704 def __init__(
705 self,
706 identifier: str,
707 genericItems: Nullable[Iterable[GenericInterfaceItemMixin]] = None,
708 portItems: Nullable[Iterable[PortInterfaceItemMixin]] = None,
709 documentation: Nullable[str] = None,
710 parent: ModelEntity = None
711 ) -> None:
712 super().__init__(parent)
713 NamedEntityMixin.__init__(self, identifier)
714 DocumentedEntityMixin.__init__(self, documentation)
716 # TODO: extract to mixin
717 self._genericItems = []
718 if genericItems is not None:
719 for item in genericItems:
720 self._genericItems.append(item)
721 item._parent = self
723 # TODO: extract to mixin
724 self._portItems = []
725 if portItems is not None:
726 for item in portItems:
727 self._portItems.append(item)
728 item._parent = self
730 @property
731 def GenericItems(self) -> List[GenericInterfaceItemMixin]:
732 return self._genericItems
734 @property
735 def PortItems(self) -> List[PortInterfaceItemMixin]:
736 return self._portItems
738 @property
739 def Entity(self) -> Nullable[Entity]:
740 return self._entity
742 @Entity.setter
743 def Entity(self, value: Entity) -> None:
744 self._entity = value
747@export
748class Configuration(PrimaryUnit, DesignUnitWithContextMixin):
749 """
750 Represents a configuration declaration.
752 .. admonition:: Example
754 .. code-block:: VHDL
756 configuration cfg of ent is
757 for rtl
758 -- ...
759 end for;
760 end configuration;
761 """
763 def __init__(
764 self,
765 identifier: str,
766 contextItems: Nullable[Iterable[Context]] = None,
767 documentation: Nullable[str] = None,
768 parent: ModelEntity = None
769 ) -> None:
770 super().__init__(identifier, contextItems, documentation, parent)
771 DesignUnitWithContextMixin.__init__(self)
773 def __str__(self) -> str:
774 lib = self._parent._identifier if self._parent is not None else "%"
776 return f"Configuration: {lib}.{self._identifier}"
778 def __repr__(self) -> str:
779 lib = self._parent._identifier if self._parent is not None else "%"
781 return f"{lib}.{self._identifier}"