from itertools import chain
from typing import Union, Optional, Tuple
from hwt.doc_markers import internal
from hwt.hdl.operatorDefs import OpDefinition
from hwt.hdl.types.array import HArray
from hwt.hdl.types.defs import BIT
from hwt.hdl.types.hdlType import HdlType
from hwt.hdl.types.struct import HStruct
from hwt.interfaces.std import Signal, Clk, Rst, Rst_n
from hwt.interfaces.structIntf import HdlType_to_Interface, StructIntf
from hwt.synthesizer.hObjList import HObjList
from hwt.synthesizer.interfaceLevel.getDefaultClkRts import getClk, getRst
from hwt.synthesizer.interfaceLevel.mainBases import UnitBase, InterfaceBase
from hwt.synthesizer.rtlLevel.constants import NOT_SPECIFIED
from hwt.synthesizer.rtlLevel.mainBases import RtlSignalBase
from hwt.synthesizer.rtlLevel.netlist import RtlNetlist
from hwt.synthesizer.rtlLevel.rtlSignal import RtlSignal
from hwt.synthesizer.rtlLevel.rtlSyncSignal import RtlSyncSignal
from ipCorePackager.constants import INTF_DIRECTION
[docs]def getSignalName(sig):
"""
Name getter which works for RtlSignal and Interface instances as well
"""
try:
return sig._name
except AttributeError:
pass
return sig.name
[docs]def getInterfaceName(top: "Unit", io: Union[InterfaceBase, RtlSignal,
Tuple[Union[InterfaceBase, RtlSignal]]]):
if isinstance(io, InterfaceBase):
prefix = []
parent = io._parent
while parent is not None:
if parent is top:
break
prefix.append(parent._name)
parent = parent._parent
n = io._getFullName()
if prefix:
prefix.reverse()
prefix.append(n)
return ".".join(prefix)
else:
return n
elif isinstance(io, tuple):
return f"({', '.join(getInterfaceName(top, _io) for _io in io)})"
else:
return getSignalName(io)
[docs]@internal
def _default_param_updater(self, myP, otherP_val):
myP.set_value(otherP_val)
[docs]@internal
def _normalize_default_value_dict_for_interface_array(root_val: dict,
val: Union[dict, list, None],
name_prefix: str,
hobj_list: HObjList,
neutral_value):
"""
This function is called to convert data in format
.. code-block:: python
{"x": [3, 4]}
# into
{"x_0": 3, "x_1": 4}
This is required because the items of HObjList are stored in _interfaces as a separate items
and thus we can not resolve the value association otherwise.
"""
for i, intf in enumerate(hobj_list):
if val is neutral_value:
continue
elif isinstance(val, dict):
_val = val.get(i, neutral_value)
else:
_val = val[i]
if _val is neutral_value:
continue
elm_name = f"{name_prefix:s}_{i:d}"
if isinstance(intf, HObjList):
_normalize_default_value_dict_for_interface_array(root_val, _val, elm_name, intf, neutral_value)
else:
root_val[elm_name] = _val
[docs]@internal
def _instantiate_signals(intf: Union[Signal, HObjList, StructIntf],
clk: Clk, rst: Union[Rst, Rst_n], def_val, nop_val, signal_create_fn):
intf._direction = INTF_DIRECTION.UNKNOWN
if isinstance(intf, Signal):
name = intf._getHdlName()
intf._sig = signal_create_fn(
name,
intf._dtype,
clk, rst, def_val, nop_val)
intf._sig._interface = intf
elif isinstance(intf, HObjList):
intf_len = len(intf)
if isinstance(def_val, dict):
for k in def_val.keys():
assert k > 0 and k < intf_len, ("Default value for", intf, " specifies ", k, " which is not present on interface")
elif def_val is not None:
assert len(def_val) == intf_len, ("Default value does not have same size, ", len(def_val), intf_len, intf)
if isinstance(nop_val, dict):
for k in nop_val.keys():
assert k > 0 and k < intf_len, ("Nop value for", intf, " specifies ", k, " which is not present on interface")
elif nop_val is not NOT_SPECIFIED:
assert len(nop_val) == intf_len, ("Nop value does not have same size, ", len(nop_val), intf_len, intf)
for i, elm in enumerate(intf):
if def_val is None:
_def_val = None
elif isinstance(def_val, dict):
_def_val = def_val.get(i, None)
else:
_def_val = def_val[i]
if nop_val is NOT_SPECIFIED:
_nop_val = NOT_SPECIFIED
elif isinstance(nop_val, dict):
_nop_val = nop_val.get(i, NOT_SPECIFIED)
else:
_nop_val = nop_val[i]
_instantiate_signals(elm, clk, rst, _def_val, _nop_val, signal_create_fn)
else:
if def_val is not None:
for k in tuple(def_val.keys()):
_i = getattr(intf, k, NOT_SPECIFIED)
assert _i is not NOT_SPECIFIED, ("Default value for", intf, " specifies ", k, " which is not present on interface")
if isinstance(_i, HObjList):
_normalize_default_value_dict_for_interface_array(
def_val, def_val[k], k, _i, None)
if nop_val is not NOT_SPECIFIED:
for k in tuple(nop_val.keys()):
_i = getattr(intf, k, NOT_SPECIFIED)
assert _i is not NOT_SPECIFIED, ("Nop value for", intf, " specifies ", k, " which is not present on interface")
if isinstance(_i, HObjList):
_normalize_default_value_dict_for_interface_array(
nop_val, nop_val[k],
k, _i, NOT_SPECIFIED)
for elm in intf._interfaces:
name = elm._name
if def_val is None:
_def_val = None
else:
_def_val = def_val.get(name, None)
if nop_val is NOT_SPECIFIED:
_nop_val = NOT_SPECIFIED
else:
_nop_val = nop_val.get(name, NOT_SPECIFIED)
_instantiate_signals(elm, clk, rst, _def_val, _nop_val, signal_create_fn)
[docs]@internal
def _loadDeclarations(intf_or_list: Union[HObjList, InterfaceBase], suggested_name: str):
if isinstance(intf_or_list, HObjList):
for i, intf in enumerate(intf_or_list):
_loadDeclarations(intf, f"{suggested_name:s}_{i:d}")
else:
intf_or_list._name = suggested_name
intf_or_list._loadDeclarations()
[docs]def Interface_without_registration(
parent:UnitBase,
container: Union[InterfaceBase, HObjList],
suggested_name:str,
def_val: Union[int, None, dict, list]=None,
nop_val: Union[int, None, dict, list, "NOT_SPECIFIED"]=NOT_SPECIFIED):
"""
Load all parts of interface and construct signals in RtlNetlist context with an automatic name check,
without need to explicitly add the interface in _interfaces list.
"""
_loadDeclarations(container, suggested_name)
_instantiate_signals(
container, None, None, def_val, nop_val,
lambda name, dtype, clk, rst, def_val, nop_val: parent._sig(name, dtype,
def_val=def_val,
nop_val=nop_val))
container._parent = parent
parent._private_interfaces.append(container)
return container
[docs]class UnitImplHelpers(UnitBase):
[docs] def _reg(self, name: str,
dtype: HdlType=BIT,
def_val: Union[int, None, dict, list]=None,
clk: Union[RtlSignalBase, None, Tuple[RtlSignalBase, OpDefinition]]=None,
rst: Optional[RtlSignalBase]=None) -> RtlSyncSignal:
"""
Create RTL FF register in this unit
:param def_val: s default value of this register,
if this value is specified reset signal of this component is used
to generate a reset logic
:param clk: optional clock signal specification,
(signal or tuple(signal, edge type (AllOps.RISING_EDGE/FALLING_EDGE)))
:param rst: optional reset signal specification
:note: rst/rst_n resolution is done from signal type,
if it is negated type the reset signal is interpreted as rst_n
:note: if clk or rst is not specified default signal
from parent unit instance will be used
"""
if clk is None:
clk = getClk(self)
if def_val is None:
# if no value is specified reset is not required
rst = None
elif rst is None:
rst = getRst(self)
if isinstance(dtype, (HStruct, HArray)):
container = HdlType_to_Interface().apply(dtype)
_loadDeclarations(container, name)
_instantiate_signals(
container, clk, rst, def_val, NOT_SPECIFIED,
lambda name, dtype, clk, rst, def_val, nop_val: self._reg(name, dtype,
def_val=def_val,
clk=clk, rst=rst))
container._parent = self
return container
else:
# primitive data type signal
return self._ctx.sig(
name,
dtype=dtype,
clk=clk,
syncRst=rst,
def_val=def_val
)
[docs] def _sig(self, name: str,
dtype: HdlType=BIT,
def_val: Union[int, None, dict, list]=None,
nop_val: Union[int, None, dict, list, "NOT_SPECIFIED"]=NOT_SPECIFIED) -> RtlSignal:
"""
Create signal in this unit
:see: :func:`hwt.synthesizer.rtlLevel.netlist.RtlNetlist.sig`
"""
if isinstance(dtype, HStruct):
container = HdlType_to_Interface().apply(dtype)
return Interface_without_registration(self, container, name, def_val=def_val, nop_val=nop_val)
else:
# primitive data type signal
return self._ctx.sig(name, dtype=dtype, def_val=def_val, nop_val=nop_val)
[docs] @internal
def _cleanAsSubunit(self):
"""
Disconnect internal signals so unit can be reused by parent unit
"""
for i in chain(self._interfaces, self._private_interfaces):
i._clean()
[docs] @internal
def _signalsForSubUnitEntity(self, context: RtlNetlist, prefix: str):
"""
generate signals in this context for all ports of this subunit
"""
for i in self._interfaces:
if i._isExtern:
i._signalsForInterface(context, None, None, prefix=prefix + i._NAME_SEPARATOR)