Skip to content

Groups support for entity and component ports, and architecture signal declarations #99

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 30 additions & 1 deletion pyVHDLModel/Base.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
Base-classes for the VHDL language model.
"""
from enum import unique, Enum
from typing import Type, Tuple, Iterable, Optional as Nullable, Union, cast
from typing import Type, Tuple, Iterable, Optional as Nullable, Union, cast, TypeVar, Generic, Dict, List

from pyTooling.Decorators import export, readonly
from pyTooling.MetaClasses import ExtendedType
Expand Down Expand Up @@ -450,3 +450,32 @@ def Expression(self) -> ExpressionUnion:
@property
def After(self) -> Expression:
return self._after


T = TypeVar("T")

@export
class Groups(Generic[T], dict):
"""A typed dictionary for grouping lists of objects by name (string keys) or None for ungrouped items."""

def __init__(self, data: Dict[str | None, List[T]], item_type: Type[T]):
self._item_type = item_type
# Validate the input data before calling super().__init__
for key, value in data.items():
self._validate_key_value(key, value)
super().__init__(data)

def _validate_key_value(self, key: str | None, value: List[T]) -> None:
"""Validate key and value types."""
if not isinstance(key, (str, type(None))):
raise TypeError("Keys must be strings or None")
if not isinstance(value, list) or not all(isinstance(item, self._item_type) for item in value):
raise TypeError(f"Values must be lists of {self._item_type.__name__}")

def __setitem__(self, key: str | None, value: List[T]) -> None:
"""Override to add type checking when setting items."""
self._validate_key_value(key, value)
super().__setitem__(key, value)

def __repr__(self) -> str:
return f"{self.__class__.__name__}({dict(self)!r})"
108 changes: 52 additions & 56 deletions pyVHDLModel/DesignUnit.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@
from pyVHDLModel.Namespace import Namespace
from pyVHDLModel.Regions import ConcurrentDeclarationRegionMixin
from pyVHDLModel.Symbol import Symbol, PackageSymbol, EntitySymbol, LibraryReferenceSymbol
from pyVHDLModel.Interface import GenericInterfaceItemMixin, PortInterfaceItemMixin
from pyVHDLModel.Object import DeferredConstant
from pyVHDLModel.Interface import GenericInterfaceItemMixin, PortInterfaceItemMixin, PortGroups
from pyVHDLModel.Object import DeferredConstant, SignalGroups
from pyVHDLModel.Concurrent import ConcurrentStatement, ConcurrentStatementsMixin


Expand Down Expand Up @@ -581,8 +581,52 @@ def __repr__(self) -> str:
return f"{lib}.{self._identifier}(body)"


class DesignUnitInterfaceMixin(metaclass=ExtendedType, mixin=True):
_genericItems: List[GenericInterfaceItemMixin]
_portItems: List[PortInterfaceItemMixin]
_portGroups: PortGroups

def __init__(
self,
genericItems: Nullable[Iterable[GenericInterfaceItemMixin]] = None,
portItems: Nullable[Union[Iterable[PortInterfaceItemMixin], PortGroups]] = None
) -> None:
self._genericItems = []
if genericItems is not None:
for item in genericItems:
self._genericItems.append(item)
item._parent = self
self._portItems = []
self._portGroups = {}
if isinstance(portItems, PortGroups):
for group in portItems.keys():
self._portGroups[group] = [] # Initialize as empty list
for item in portItems[group]:
self._portGroups[group].append(item) # Append to the list
if item not in self._portItems:
self._portItems.append(item)
item._parent = self
elif portItems is not None:
self._portGroups[None] = []
for item in portItems:
self._portGroups[None].append(item)
self._portItems.append(item)
item._parent = self

@property
def GenericItems(self) -> List[GenericInterfaceItemMixin]:
return self._genericItems

@property
def PortItems(self) -> List[PortInterfaceItemMixin]:
return self._portItems

@property
def PortGroups(self) -> PortGroups:
return self._portGroups

@export
class Entity(PrimaryUnit, DesignUnitWithContextMixin, ConcurrentDeclarationRegionMixin, ConcurrentStatementsMixin):
class Entity(PrimaryUnit, DesignUnitWithContextMixin, ConcurrentDeclarationRegionMixin, ConcurrentStatementsMixin, DesignUnitInterfaceMixin):
"""
Represents an entity declaration.

Expand All @@ -597,17 +641,14 @@ class Entity(PrimaryUnit, DesignUnitWithContextMixin, ConcurrentDeclarationRegio

_allowBlackbox: Nullable[bool] #: Allow blackboxes for components in this package.

_genericItems: List[GenericInterfaceItemMixin]
_portItems: List[PortInterfaceItemMixin]

_architectures: Dict[str, 'Architecture']

def __init__(
self,
identifier: str,
contextItems: Nullable[Iterable[ContextUnion]] = None,
genericItems: Nullable[Iterable[GenericInterfaceItemMixin]] = None,
portItems: Nullable[Iterable[PortInterfaceItemMixin]] = None,
portItems: Nullable[Union[Iterable[PortInterfaceItemMixin], PortGroups]] = None,
declaredItems: Nullable[Iterable] = None,
statements: Nullable[Iterable[ConcurrentStatement]] = None,
documentation: Nullable[str] = None,
Expand All @@ -621,19 +662,7 @@ def __init__(

self._allowBlackbox = allowBlackbox

# TODO: extract to mixin
self._genericItems = []
if genericItems is not None:
for item in genericItems:
self._genericItems.append(item)
item._parent = self

# TODO: extract to mixin
self._portItems = []
if portItems is not None:
for item in portItems:
self._portItems.append(item)
item._parent = self
DesignUnitInterfaceMixin.__init__(self, genericItems, portItems)

self._architectures = {}

Expand All @@ -653,16 +682,6 @@ def AllowBlackbox(self) -> bool:
def AllowBlackbox(self, value: Nullable[bool]) -> None:
self._allowBlackbox = value

# TODO: extract to mixin for generics
@property
def GenericItems(self) -> List[GenericInterfaceItemMixin]:
return self._genericItems

# TODO: extract to mixin for ports
@property
def PortItems(self) -> List[PortInterfaceItemMixin]:
return self._portItems

@property
def Architectures(self) -> Dict[str, 'Architecture']:
return self._architectures
Expand Down Expand Up @@ -704,7 +723,7 @@ def __init__(
identifier: str,
entity: EntitySymbol,
contextItems: Nullable[Iterable[Context]] = None,
declaredItems: Nullable[Iterable] = None,
declaredItems: Nullable[Union[Iterable, SignalGroups]] = None,
statements: Iterable['ConcurrentStatement'] = None,
documentation: Nullable[str] = None,
allowBlackbox: Nullable[bool] = None,
Expand Down Expand Up @@ -754,7 +773,7 @@ def __repr__(self) -> str:


@export
class Component(ModelEntity, NamedEntityMixin, DocumentedEntityMixin):
class Component(ModelEntity, NamedEntityMixin, DocumentedEntityMixin, DesignUnitInterfaceMixin):
"""
Represents a configuration declaration.

Expand All @@ -770,9 +789,6 @@ class Component(ModelEntity, NamedEntityMixin, DocumentedEntityMixin):
_allowBlackbox: Nullable[bool] #: Allow component to be a blackbox.
_isBlackBox: Nullable[bool] #: Component is a blackbox.

_genericItems: List[GenericInterfaceItemMixin]
_portItems: List[PortInterfaceItemMixin]

_entity: Nullable[Entity]

def __init__(
Expand All @@ -792,19 +808,7 @@ def __init__(
self._isBlackBox = None
self._entity = None

# TODO: extract to mixin
self._genericItems = []
if genericItems is not None:
for item in genericItems:
self._genericItems.append(item)
item._parent = self

# TODO: extract to mixin
self._portItems = []
if portItems is not None:
for item in portItems:
self._portItems.append(item)
item._parent = self
DesignUnitInterfaceMixin.__init__(self, genericItems, portItems)

@property
def AllowBlackbox(self) -> bool:
Expand All @@ -831,14 +835,6 @@ def IsBlackbox(self) -> bool:
"""
return self._isBlackBox

@property
def GenericItems(self) -> List[GenericInterfaceItemMixin]:
return self._genericItems

@property
def PortItems(self) -> List[PortInterfaceItemMixin]:
return self._portItems

@property
def Entity(self) -> Nullable[Entity]:
return self._entity
Expand Down
12 changes: 10 additions & 2 deletions pyVHDLModel/Interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,13 @@

Interface items are used in generic, port and parameter declarations.
"""
from typing import Iterable, Optional as Nullable
from typing import Iterable, Optional as Nullable, Dict, List

from pyTooling.Decorators import export, readonly
from pyTooling.MetaClasses import ExtendedType

from pyVHDLModel.Symbol import Symbol
from pyVHDLModel.Base import ModelEntity, DocumentedEntityMixin, ExpressionUnion, Mode, NamedEntityMixin
from pyVHDLModel.Base import ModelEntity, DocumentedEntityMixin, ExpressionUnion, Mode, NamedEntityMixin, Groups
from pyVHDLModel.Object import Constant, Signal, Variable, File
from pyVHDLModel.Subprogram import Procedure, Function
from pyVHDLModel.Type import Type
Expand Down Expand Up @@ -218,3 +218,11 @@ def __init__(
) -> None:
super().__init__(identifiers, subtype, documentation, parent)
ParameterInterfaceItemMixin.__init__(self)


@export
class PortGroups(Groups[PortInterfaceItemMixin]):
"""A typed dictionary for grouping lists of port interface items by name (string keys) or None for ungrouped items."""

def __init__(self, data: Dict[str | None, List[PortInterfaceItemMixin]]):
super().__init__(data, PortInterfaceItemMixin)
12 changes: 10 additions & 2 deletions pyVHDLModel/Object.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,13 @@

Objects are constants, variables, signals and files.
"""
from typing import Iterable, Optional as Nullable
from typing import Iterable, Optional as Nullable, Dict, List

from pyTooling.Decorators import export, readonly
from pyTooling.MetaClasses import ExtendedType
from pyTooling.Graph import Vertex

from pyVHDLModel.Base import ModelEntity, MultipleNamedEntityMixin, DocumentedEntityMixin, ExpressionUnion
from pyVHDLModel.Base import ModelEntity, MultipleNamedEntityMixin, DocumentedEntityMixin, ExpressionUnion, Groups
from pyVHDLModel.Symbol import Symbol


Expand Down Expand Up @@ -244,3 +244,11 @@ class File(Obj):

.. todo:: File object not implemented.
"""


@export
class SignalGroups(Groups[Signal]):
"""A typed dictionary for grouping lists of signal objects by name (string keys) or None for ungrouped items."""

def __init__(self, data: Dict[str | None, List[Signal]] = {}):
super().__init__(data, Signal)
29 changes: 24 additions & 5 deletions pyVHDLModel/Regions.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,16 @@

tbd.
"""
from typing import List, Dict, Iterable, Optional as Nullable
from typing import List, Dict, Iterable, Optional as Nullable, Any

from pyTooling.Decorators import export, readonly
from pyTooling.MetaClasses import ExtendedType

from pyVHDLModel.Object import Constant, SharedVariable, File, Variable, Signal
from pyVHDLModel.Base import Groups
from pyVHDLModel.Object import Constant, SharedVariable, File, Variable, Signal, SignalGroups
from pyVHDLModel.Subprogram import Subprogram, Function, Procedure
from pyVHDLModel.Type import Subtype, FullType


@export
class ConcurrentDeclarationRegionMixin(metaclass=ExtendedType, mixin=True):
# FIXME: define list prefix type e.g. via Union
Expand All @@ -62,13 +62,28 @@ class ConcurrentDeclarationRegionMixin(metaclass=ExtendedType, mixin=True):
_functions: Dict[str, Dict[str, Function]] #: Dictionary of all functions declared in this concurrent declaration region.
_procedures: Dict[str, Dict[str, Procedure]] #: Dictionary of all procedures declared in this concurrent declaration region.

_signalGroups: SignalGroups #: Dictionary of all signal groups declared in this concurrent declaration region.

def __init__(self, declaredItems: Nullable[Iterable] = None) -> None:
# TODO: extract to mixin
self._declaredItems = [] # TODO: convert to dict
self._signalGroups = SignalGroups()
if declaredItems is not None:
for item in declaredItems:
self._declaredItems.append(item)
item._parent = self
if isinstance(item, Groups):
if isinstance(item, SignalGroups):
groups = self._signalGroups
else:
raise ValueError(f"Unsupported group type: {type(item)}")
for groupName in item.keys():
groups[groupName] = []
for groupItem in item[groupName]:
groups[groupName].append(groupItem)
self._declaredItems.append(groupItem)
groupItem._parent = self
else:
self._declaredItems.append(item)
item._parent = self

self._types = {}
self._subtypes = {}
Expand Down Expand Up @@ -125,6 +140,10 @@ def Functions(self) -> Dict[str, Dict[str, Function]]:
def Procedures(self) -> Dict[str, Dict[str, Procedure]]:
return self._procedures

@readonly
def SignalGroups(self) -> SignalGroups:
return self._signalGroups

def IndexDeclaredItems(self) -> None:
"""
Index declared items listed in the concurrent declaration region.
Expand Down
Loading