Source code for hwt.synthesizer.interfaceLevel.propDeclrCollector

from types import MethodType
from typing import Tuple, Set, Optional, Callable, Self

from hdlConvertorAst.translate.common.name_scope import WithNameScope
from hwt.doc_markers import internal
from hwt.hObjList import HObjList
from hwt.hwParam import HwParam
from hwt.mainBases import HwModuleBase, HwIOBase
from hwt.synthesizer.exceptions import IntfLvlConfErr
from hwt.synthesizer.typePath import TypePath


[docs] @internal def nameAvailabilityCheck(obj: "PropDeclrCollector", propName: str, prop: object): """ Check if not redefining property on obj but allow to cast current property to a HwParam """ cur = getattr(obj, propName, None) if cur is not None and (not isinstance(prop, HwParam) or cur is not prop._initval): p = getattr(obj, propName) raise IntfLvlConfErr(f"{obj} already has property {propName:s} old:{p} new:{prop}")
[docs] @internal class MakeParamsShared(object): """ All newly added interfaces and units will share all parametes with unit specified in constructor of this object. """
[docs] def __init__(self, module: "HwModule", exclude:Optional[Tuple[Set[str], Set[str]]], prefix:str): self.module = module self.exclude = exclude self.prefix = prefix
def __enter__(self): orig = self.module._setAttrListener self.orig = orig exclude = self.exclude prefix = self.prefix def MakeParamsSharedWrap(self: "HwModule", iName: str, i: object): if isinstance(i, (HwIOBase, HwModuleBase, HObjList)): i._updateHwParamsFrom(self, exclude=exclude, prefix=prefix) return orig(iName, i) self.module._setAttrListener = MethodType(MakeParamsSharedWrap, self.module) return self def __exit__(self, exc_type, exc_val, exc_tb): if exc_type is None: self.module._setAttrListener = self.orig
[docs] @internal class MakeClkRstAssociations(object): """ All newly added interfaces will be associated with clk, rst specified in constructor of this object. """
[docs] def __init__(self, module: "HwModule", clk=None, rst=None): self.module = module self.clk = clk self.rst = rst
def __enter__(self): orig = self.module._setAttrListener self.orig = orig clk = self.clk rst = self.rst def MakeClkRstAssociationsWrap(self, iName, i): if isinstance(i, (HwIOBase, HObjList)): i._make_association(clk=clk, rst=rst) return orig(iName, i) self.module._setAttrListener = MethodType(MakeClkRstAssociationsWrap, self.module) return self def __exit__(self, exc_type, exc_val, exc_tb): if exc_type is None: self.module._setAttrListener = self.orig
[docs] class PropDeclrCollector(object): """ Class which manages the registration of components and interfaces in specified elaboration phases. It uses __setattr__ listeners to detect new properties and then calls a listener function to process the registration. Used for HwModule, HwIO classes to detect and load interfaces and components. """
[docs] def hwConfig(self) -> None: """ Configure object parameters * setup all parameters on this object, use HwParam class instances to allow use of parameter inheritance * called in __init__ of class """ pass
[docs] def hwDeclr(self) -> None: """ In this function user should specify the declaration of interfaces for communication with outside word. It is also better to declare sub components there as it allows for better parallelization during the build. * _declr method is called after _config * if this object is :class:`hwt.hwModule.HwModule` all interfaces are treated as externally accessible interfaces if this object is HwIO instance all subinterfaces are loaded as well """ pass
[docs] def hwImpl(self) -> None: """ Implementation - construct main body of the component in this function. * called after _declr """ pass
@internal def __setattr__(self, attr:str, value) -> None: """setattr with listener injector""" try: saListerner = self._setAttrListener except AttributeError: super().__setattr__(attr, value) return if saListerner: value = saListerner(attr, value) super().__setattr__(attr, value) # configuration phase
[docs] @internal def _loadConfig(self) -> None: """ Load params in hwConfig() """ if not hasattr(self, '_hwParams'): self._hwParams = [] self._setAttrListener = self._paramCollector self.hwConfig() self._setAttrListener = None
[docs] @internal def _registerParameter(self, pName:str, parameter: HwParam) -> None: """ Register HwParam object on interface level object """ nameAvailabilityCheck(self, pName, parameter) # resolve name in this scope assert parameter._name is None, ( "HwParam object is already assigned to %r.%s" % (parameter.module, parameter._name)) # add name in this scope parameter._name = pName parameter._parent = self self._hwParams.append(parameter)
[docs] def _hwParamsShared(self, exclude: Optional[Tuple[Set[str], Set[str]]]=None, prefix="") -> MakeParamsShared: """ Auto-propagate params by name to child components and interfaces Usage: .. code-block:: python with self._hwParamsShared(): # your interfaces and unit which should share all params with "self" there :param exclude: tuple (src param names to exclude, dst param names to exclude) :param prefix: prefix which should be added to name of child parameters before parameter name matching """ return MakeParamsShared(self, exclude=exclude, prefix=prefix)
[docs] def _make_association(self, clk=None, rst=None) -> None: """ Associate this object with specified clk/rst """ if clk is not None: assert self._associatedClk is None, ("Already associated with clock", self._associatedClk) self._associatedClk = clk if rst is not None: assert self._associatedRst is None, ("Already associated with reset", self._associatedRst) self._associatedRst = rst
[docs] def _associated(self, clk=None, rst=None) -> MakeClkRstAssociations: """ associate newly added interfaces to "self" with selected clk, rst (if interface is not associated agents try to find clk/rst by _getAssociatedClk/_getAssociatedRst which will search for any clk/rst on parent recursively) Usage: .. code-block:: python with self._associated(clk=self.myClk, rst=self.myRst): self.myAxi = Axi4Stream() # this interface is associated with myClk and myRst # simulation agents and component builders will use them :param exclude: params which should not be shared """ return MakeClkRstAssociations(self, clk, rst)
[docs] def _updateHwParamsFrom(self, otherObj: "PropDeclrCollector", updater: Callable[["PropDeclrCollector", HwParam, HwParam], None], exclude: Optional[Tuple[Set[str], Set[str]]], prefix: str) -> "PropDeclrCollector": """ Update all parameters which are defined on self from otherObj :param otherObj: other object which HwParam instances should be updated :param updater: updater function(self, myParameter, otherParameter) :param exclude: tuple of set of param names for src and dst which which should be excluded :param prefix: prefix which should be added to name of paramters of this object before matching parameter name on parent """ excluded_src = set() excluded_dst = set() if exclude is not None: exclude_src = set(exclude[0]) exclude_dst = set(exclude[1]) for myP in self._hwParams: if exclude is not None and myP._name in exclude_dst: excluded_dst.add(myP._name) continue pPName = prefix + myP._name try: otherP = getattr(otherObj, pPName) # if not isinstance(otherP, HwParam): # continue except AttributeError: continue if exclude is not None and pPName in exclude_src: excluded_src.add(pPName) continue updater(self, myP, otherP) if exclude is not None: # assert that what should be excluded really exists assert exclude_src == excluded_src, (exclude_src, excluded_src) assert exclude_dst == excluded_dst, (exclude_dst == excluded_dst) return self
# declaration phase
[docs] @internal def _registerSubmodule(self, mName:str, submodule:"HwModule", onParentPropertyPath: TypePath): """ Register unit object on interface level object """ nameAvailabilityCheck(self, mName, submodule) assert submodule._parent is None submodule._parent = self submodule._name = mName submodule._onParentPropertyPath = onParentPropertyPath self._subHwModules.append(submodule)
[docs] @internal def _registerHwIO(self, hwIOName: str, hwIO: HwIOBase, onParentPropertyPath: TypePath, isPrivate:bool): """ Register HwIO object on interface level object """ nameAvailabilityCheck(self, hwIOName, hwIO) assert hwIO._parent is None hwIO._parent = self hwIO._name = hwIOName assert isinstance(onParentPropertyPath, TypePath), onParentPropertyPath hwIO._onParentPropertyPath = onParentPropertyPath hwIO._rtlCtx = self._rtlCtx # _setAsExtern() not used because _hwIOs are not initialized yet if isPrivate: self._private_hwIOs.append(hwIO) hwIO._isExtern = False else: self._hwIOs.append(hwIO) hwIO._isExtern = True
[docs] @internal def _declrCollector(self, name: str, prop: object): if name in ("_associatedClk", "_associatedRst"): object.__setattr__(self, name, prop) return prop onParentPropertyPath = TypePath(name,) if isinstance(prop, HwIOBase): self._registerHwIO(name, prop, onParentPropertyPath, False) elif isinstance(prop, HwModuleBase): self._registerSubmodule(name, prop, onParentPropertyPath) elif isinstance(prop, HObjList): self._registerArray(name, prop, onParentPropertyPath) return prop
[docs] @internal def _registerArray(self, name: Optional[str], items: HObjList[Self], onParentPropertyPath: TypePath): """ Register array of items on interface level object """ items._parent = self items._name = name items._on_append = self._registerArray_append assert isinstance(onParentPropertyPath, TypePath), onParentPropertyPath items._onParentPropertyPath = onParentPropertyPath for i, item in enumerate(items): self._registerArray_append(items, item, i)
[docs] @internal def _registerArray_append(self, arr: HObjList[Self], item: Self, index: int): """ Register a single object in the list """ saListerner = self._setAttrListener if arr is self: # register item in the array (parrent) if saListerner: item = saListerner(f"{index:d}", item) if isinstance(item, PropDeclrCollector): item._onParentPropertyPath = TypePath(index,) else: # register item for a list which is stored inside of parent setattr(self, f"{arr._name:s}_{index:d}", item) if isinstance(item, PropDeclrCollector): item._onParentPropertyPath = arr._onParentPropertyPath / index
# implementation phase
[docs] @internal def _loadImpl(self): self._setAttrListener = self._implCollector self.hwImpl() self._setAttrListener = None
[docs] @internal def _registerSubmoduleInImpl(self, name: str, m: HwModuleBase, onParentPropertyPath: TypePath): """ :attention: unit has to be parametrized before it is registered (some components can change interface by parametrization) """ self._registerSubmodule(name, m, onParentPropertyPath) m._loadHwDeclarations() sm = self._store_manager with WithNameScope(sm, sm.name_scope.parent): self._lazy_loaded.extend(m._to_rtl( self._target_platform, self._store_manager)) m._signalsForSubHwModuleEntity(self._rtlCtx, "sig_" + name)
[docs] @internal def _registerHwIOInHwImpl(self, hwIOName: str, hwio: HwIOBase, onParentPropertyPath: TypePath): """ Register interface in implementation phase """ raise NotImplementedError()
[docs] @internal def _paramCollector(self, pName: str, prop): if isinstance(prop, HwParam): self._registerParameter(pName, prop) return prop._initval else: return prop
[docs] @internal def _implCollector(self, name: str, prop): """ Handle property definitions in _impl phase """ onParentPropertyPath = TypePath(name,) if isinstance(prop, HwIOBase): if prop._parent is self: return prop self._registerHwIOInHwImpl(name, prop, onParentPropertyPath) elif isinstance(prop, HwModuleBase): if prop._parent is self: return prop self._registerSubmoduleInImpl(name, prop, onParentPropertyPath) elif isinstance(prop, HObjList): if prop._parent is self: return prop self._registerArray(name, prop, onParentPropertyPath) return prop