from copy import copy
from typing import Dict, Optional, Union, List, Generator, Callable
from hdlConvertorAst.translate.common.name_scope import NameScope
from hwt.doc_markers import internal
from hwt.hdl.constants import DIRECTION, INTF_DIRECTION
from hwt.hdl.statements.assignmentContainer import HdlAssignmentContainer
from hwt.hdl.types.typeCast import toHVal
from hwt.hdl.value import HValue
from hwt.synthesizer.exceptions import IntfLvlConfErr, InterfaceStructureErr
from hwt.synthesizer.hObjList import HObjList
from hwt.synthesizer.interfaceLevel.interfaceUtils.directionFns import \
InterfaceDirectionFns
from hwt.synthesizer.interfaceLevel.interfaceUtils.implDependent import\
InterfaceceImplDependentFns
from hwt.synthesizer.interfaceLevel.mainBases import InterfaceBase
from hwt.synthesizer.interfaceLevel.propDeclrCollector import\
PropDeclrCollector
from hwt.synthesizer.rtlLevel.netlist import RtlNetlist
from hwt.synthesizer.rtlLevel.rtlSignal import RtlSignal
from hwt.synthesizer.rtlLevel.utils import portItemfromSignal
from hwt.synthesizer.vectorUtils import fitTo
from hwtSimApi.agents.base import AgentBase
from hwt.hdl.portItem import HdlPortItem
[docs]def _default_param_updater(self, myP, parentPval):
myP.set_value(parentPval)
[docs]class Interface(InterfaceBase, InterfaceceImplDependentFns,
PropDeclrCollector, InterfaceDirectionFns):
"""
Base class for all interfaces in interface synthesizer
:cvar _NAME_SEPARATOR: separator for nested interface names
:ivar ~._params: [] of parameter
:ivar ~._interfaces: [] sub interfaces
:ivar ~._name: name assigned during synthesis
:ivar ~._parent: parent object (Unit or Interface instance)
:ivar ~._isExtern: If true synthesizer sets it as external port of unit
:ivar ~._associatedClk: clock Signal (interface) associated with
this interface if is none simulation agent try to search it on parent
:ivar ~._associatedRst: rst(_n) Signal (interface) associated
with this interface if is none simulation agent try to search
it on parent
:note: only interfaces without _interfaces have
:ivar ~._boundedSigLvlUnit: RTL unit for which was this interface created
Agenda of directions and HDL
:ivar ~._masterDir: specifies which direction has this interface at master
:ivar ~._direction: means actual direction of this interface resolved
by its drivers
:ivar ~._ctx: RTL netlist context of all signals and params
on this interface after interface is registered on parent _ctx
is merged
:ivar ~._hdl_port: a HdlPortItem instance available once the unit is synthesized
Agenda of simulations
:ivar ~._ag: agent object connected to this interface
(initialized only before simulation)
"""
_NAME_SEPARATOR = "_"
[docs] def __init__(self, masterDir=DIRECTION.OUT,
hdl_name:Optional[Union[str, Dict[str, str]]]=None,
loadConfig=True):
"""
This constructor is called when constructing new interface,
it is usually done manually while creating :class:`hwt.synthesizer.unit.Unit` or
automatically while extracting interfaces from UnitWithSoure
:param masterDir: direction which this interface should have for master
:param multiplyedBy: this can be instance of integer or Param,
this mean the interface is array of the interfaces
where multiplyedBy is the size
:param loadConfig: do load config in __init__
"""
self._setAttrListener: Optional[Callable[[str, object], None]] = None
self._associatedClk: Optional[Interface] = None
self._associatedRst: Optional[Interface] = None
self._parent: Optional["Unit"] = None
self._name: Optional[str] = None
super().__init__()
self._masterDir: DIRECTION = masterDir
# Interface is instantiated inside of :class:`hwt.synthesizer.unit.Unit` first,
# master direction actually means slave from outside view
self._direction: INTF_DIRECTION = INTF_DIRECTION.UNKNOWN
self._ctx: Optional[RtlNetlist] = None
if loadConfig:
self._loadConfig()
# flags for better design error detection
self._isExtern = False
self._ag: Optional[AgentBase] = None
self._hdl_port: Optional[HdlPortItem] = None
self._hdl_name_override = hdl_name
[docs] def _m(self):
"""
Note that this interface will be master
:return: self
"""
assert not hasattr(self, "_interfaces") or not self._interfaces, \
"Too late to change direction of interface"
self._direction = DIRECTION.asIntfDirection(DIRECTION.opposite(self._masterDir))
return self
def __call__(self, other, exclude=None, fit=False) -> List[HdlAssignmentContainer]:
"""
:attention: it is not call of function it is operator of assignment
"""
assert self._direction != INTF_DIRECTION.MASTER
try:
return self._connectTo(other, exclude, fit)
except Exception as e:
# simplification of previous exception traceback
e_simplified = copy(e)
raise e_simplified
[docs] @internal
def _loadDeclarations(self):
"""
load declarations from _declr method
This function is called first for parent and then for children
"""
if not hasattr(self, "_interfaces"):
self._interfaces = []
self._setAttrListener = self._declrCollector
self._declr()
self._setAttrListener = None
for i in self._interfaces:
i._loadDeclarations()
i._setAsExtern(self._isExtern)
if self._isExtern:
# direction from inside of unit (reverset compared to outside direction)
if self._direction == INTF_DIRECTION.UNKNOWN:
self._direction = INTF_DIRECTION.MASTER
self._setDirectionsLikeIn(self._direction)
[docs] @internal
def _clean(self, lockNonExternal=True):
"""
Remove all signals from this interface (used after unit is synthesized
and its parent is connecting its interface to this unit)
"""
if self._interfaces:
for i in self._interfaces:
i._clean(lockNonExternal=lockNonExternal)
[docs] def _connectTo(self, master, exclude=None, fit=False) -> List[HdlAssignmentContainer]:
"""
connect to another interface interface (on RTL level)
works like self <= master in VHDL
"""
return list(self._connectToIter(master, exclude, fit))
[docs] @internal
def _connectToIter(self, master, exclude, fit) -> Generator[HdlAssignmentContainer, None, None]:
if exclude and (self in exclude or master in exclude):
return
if self._interfaces:
seen_master_intfs = []
for ifc in self._interfaces:
if exclude and ifc in exclude:
mIfc = getattr(master, ifc._name, None)
if mIfc is not None:
seen_master_intfs.append(mIfc)
continue
if master is None:
mIfc = ifc._dtype.from_py(None)
else:
try:
mIfc = getattr(master, ifc._name)
except AttributeError:
raise IntfLvlConfErr("Invalid interface structure", ifc, "<=", master, "src missing", ifc._name)
seen_master_intfs.append(mIfc)
if exclude and mIfc in exclude:
continue
if isinstance(mIfc, HValue):
# HStruct values
if (ifc._masterDir in (DIRECTION.OUT, DIRECTION.INOUT) and ifc._direction == INTF_DIRECTION.MASTER) or\
(ifc._masterDir == DIRECTION.IN and ifc._direction == INTF_DIRECTION.SLAVE):
raise IntfLvlConfErr(
"Invalid connection", ifc, "<=", mIfc)
yield from ifc._connectToIter(mIfc,
exclude,
fit)
elif mIfc._masterDir == DIRECTION.OUT:
if ifc._masterDir != mIfc._masterDir:
raise IntfLvlConfErr(
"Invalid connection", ifc, "<=", mIfc)
yield from ifc._connectToIter(mIfc,
exclude,
fit)
else:
if ifc._masterDir != mIfc._masterDir:
raise IntfLvlConfErr(
"Invalid connection", mIfc, "<=", ifc)
yield from mIfc._connectToIter(ifc,
exclude,
fit)
if master is None:
master_intf_cnt = len(self._interfaces)
elif isinstance(master, HValue):
master_intf_cnt = len(master._dtype.fields)
else:
master_intf_cnt = len(master._interfaces)
if len(seen_master_intfs) != master_intf_cnt:
if exclude:
# there is a possibility that the master interface was excluded,
# but we did not see it as the interface of the same name was not present on self
for ifc in self._interfaces:
if ifc in exclude or ifc not in seen_master_intfs:
continue
else:
# ifc is an interface which is extra on master and is missing an equivalent on slave
raise InterfaceStructureErr(self, master, exclude)
else:
raise InterfaceStructureErr(self, master, exclude)
else:
if not isinstance(master, HValue) and master._interfaces:
raise InterfaceStructureErr(self, master, exclude)
dstSig = toHVal(self)
srcSig = toHVal(master)
if fit:
srcSig = fitTo(srcSig, dstSig)
yield dstSig(srcSig)
[docs] @internal
def _signalsForInterface(self,
ctx: RtlNetlist,
res: Optional[Dict[RtlSignal, DIRECTION]],
name_scope: Optional[NameScope],
prefix='', typeTransform=None,
reverse_dir=False):
"""
Generate RtlSignal _sig and HdlPortInstance _hdl_port
for each interface which has no subinterface
:note: if already has _sig return use it instead
:param ctx: instance of RtlNetlist where signals should be created
:param res: output dictionary where result should be stored
:param prefix: name prefix for created signals
:param name_scope: name scope used to check collisions on port names
if this a current top (every component is checked
when it is seen first time)
:param typeTransform: optional function (type) returns modified type
for signal
"""
if self._interfaces:
for intf in self._interfaces:
intf._signalsForInterface(ctx, res, name_scope,
prefix=prefix,
typeTransform=typeTransform,
reverse_dir=reverse_dir)
else:
assert self._sig is None, self
t = self._dtype
if typeTransform is not None:
t = typeTransform(t)
hdl_name = prefix + self._getHdlName()
s = ctx.sig(hdl_name, t)
s._interface = self
self._sig = s
if self._isExtern:
d = INTF_DIRECTION.asDirection(self._direction)
u = ctx.parent
if reverse_dir:
d = DIRECTION.opposite(d)
assert self._hdl_port is None, (
"Now creating a hdl interface for top"
" it but seems that it was already created")
if res is not None:
res[s] = d
if reverse_dir:
pi = portItemfromSignal(s, u, d)
# port of current top component
s.name = name_scope.checked_name(s.name, s)
pi.connectInternSig(s)
ctx.ent.ports.append(pi)
else:
pi = self._hdl_port
# port of some subcomponent which names were already checked
pi.connectOuterSig(s)
self._hdl_port = pi
[docs] def _getHdlName(self) -> str:
"""Get name in HDL """
return HObjList._getHdlName(self)
[docs] def _getFullName(self) -> str:
"""get all name hierarchy separated by '.' """
return HObjList._getFullName(self)
[docs] def _updateParamsFrom(self, otherObj, updater=_default_param_updater,
exclude=None, prefix=""):
"""
:note: doc in :func:`~hwt.synthesizer.interfaceLevel.propDeclCollector._updateParamsFrom`
"""
PropDeclrCollector._updateParamsFrom(
self, otherObj, updater, exclude, prefix)
return self
[docs] def _bit_length(self) -> int:
"""Sum of all width of interfaces in this interface"""
try:
interfaces = self._interfaces
except AttributeError:
interfaces = None
if interfaces is None:
# not loaded interface
_intf = self.__copy__()
_intf._loadDeclarations()
interfaces = _intf._interfaces
if interfaces:
w = 0
for i in interfaces:
w += i._bit_length()
return w
else:
return self._dtype.bit_length()
def __repr__(self) -> str:
if hasattr(self, "_dtype"):
t = f" {self._dtype}"
else:
t = ""
if hasattr(self, '_width'):
w = " _width=%s" % str(self._width)
else:
w = ""
name = self._getFullName()
return f"<{self.__class__.__name__} {name:s}{w:s}{t:s}>"