from copy import copy
from typing import Union, Self
from hwt.doc_markers import internal
from hwt.hdl.const import HConst
from hwt.hdl.operator import HOperatorNode
from hwt.hdl.operatorDefs import HwtOps
from hwt.hdl.statements.assignmentContainer import HdlAssignmentContainer
from hwt.hdl.types.bitConstFunctions import HBitsAnyIndexCompatibleValue
from hwt.hdl.types.bits import HBits
from hwt.hdl.types.defs import BIT, INT
from hwt.hdl.types.slice import HSlice
from hwt.hdl.types.typeCast import toHVal
from hwt.mainBases import RtlSignalBase
from hwt.synthesizer.rtlLevel.rtlSignal import RtlSignal
[docs]
def _HArrayGetitem(self: Union["HArrayRtlSignal", "HArrayConst"], iamVal: bool, key):
key = toHVal(key)
isSLICE = isinstance(key, HSlice.getConstCls())
if isSLICE:
raise NotImplementedError()
elif isinstance(key, (HConst, RtlSignalBase)):
pass
else:
raise NotImplementedError(
f"Index operation not implemented for index {key}")
if iamVal and isinstance(key, HConst):
return self._getitem__const(key)
return HOperatorNode.withRes(HwtOps.INDEX, [self, key], self._dtype.element_t)
[docs]
class HArrayRtlSignal(RtlSignal):
def __getitem__(self, key):
try:
return _HArrayGetitem(self, False, key)
except Exception as e:
# simplification of previous exception traceback
e_simplified = copy(e)
raise e_simplified
def __iter__(self):
mySize = len(self)
for i in range(mySize):
yield self[i]
def __call__(self, source,
dst_resolve_fn=lambda x:x._getDestinationSignalForAssignmentToThis(),
exclude=None,
fit=False) -> list[HdlAssignmentContainer]:
assert len(self) == len(source), ("source and destination array must be of the same size", len(self) == len(source))
res = []
for src, dst in zip(source, self):
a = dst.__call__(src, dst_resolve_fn=dst_resolve_fn, exclude=exclude, fit=fit)
if isinstance(a, (list, tuple)):
res.extend(a)
else:
res.append(a)
return res
def __len__(self):
return int(self._dtype.size)
[docs]
class HArrayConst(HConst):
"""
Class for values of array HDL type
"""
[docs]
@classmethod
def from_py(cls, typeObj, val, vld_mask=None):
"""
:param val: None or dictionary {index:HConst} or iterrable of values
:param vld_mask: if is None validity is resolved from val
if is 0 value is invalidated
if is 1 value has to be valid
"""
size = typeObj.size
if isinstance(size, HConst):
size = int(size)
elements = {}
if vld_mask == 0:
val = None
if val is None:
vld_mask = 0
elif isinstance(val, dict):
if vld_mask is None:
vld_mask = 1
for k, v in val.items():
if not isinstance(k, int):
k = int(k) # expecting {index: value} dict
if k >= size:
raise ValueError("Initialization value dictionary contains index which is larger than size of initialized array", typeObj, k)
e = elements[k] = typeObj.element_t.from_py(v)
vld_mask &= e._is_full_valid()
else:
if vld_mask is None:
vld_mask = 1
for k, v in enumerate(val):
if k >= size:
raise ValueError("Initialization value sequence is larger than size of initialized array", typeObj, val)
if isinstance(v, RtlSignalBase): # is signal
assert v._dtype == typeObj.element_t
e = v
else:
e = typeObj.element_t.from_py(v)
elements[k] = e
vld_mask &= e._is_full_valid()
if len(elements) != size:
vld_mask = 0
return cls(typeObj, elements, vld_mask)
[docs]
def to_py(self) -> list:
v = self.val
invalid_elm = self._dtype.element_t.from_py(None)
return [v.get(i, invalid_elm).to_py()
for i in range(self._dtype.size)]
@internal
def __hash__(self):
return hash(self._dtype)
# return hash((self._dtype, self.val, self.vld_mask))
[docs]
def _is_full_valid(self):
return self.vld_mask == 1
[docs]
def _is_partially_valid(self) -> bool:
return self._is_full_valid() or any(v._is_partially_valid() for v in self.val.values())
[docs]
@internal
def _getitem__const(self, key):
"""
:attention: this will clone item from array, iterate over .val
if you need to modify items
"""
try:
kv = key.val
if not key._is_full_valid():
raise KeyError()
else:
if kv >= self._dtype.size:
raise KeyError()
return self.val[kv].__copy__()
except KeyError:
return self._dtype.element_t.from_py(None)
def __getitem__(self, key):
try:
return _HArrayGetitem(self, True, key)
except Exception as e:
# simplification of previous exception traceback
e_simplified = copy(e)
raise e_simplified
def __setitem__(self, index: HBitsAnyIndexCompatibleValue, value) -> Self:
"""
Only syntax sugar for user, not used inside HWT
* Not used in HW design (__getitem__ and overloaded call operator is used instead for item assigning)
"""
try:
if isinstance(index, int):
index = INT.from_py(index)
else:
assert isinstance(index._dtype, HBits), index._dtype
if isinstance(value, HConst):
assert value._dtype == self._dtype.element_t, (
value._dtype, self._dtype.element_t)
else:
value = self._dtype.element_t.from_py(value)
if index._is_full_valid():
self.val[index.val] = value.__copy__()
else:
self.val = {}
self.vld_mask = int(
len(self.val) == self._dtype.size and
all(e._is_full_valid() for e in self.val.values())
)
return self
except Exception as e:
# simplification of previous exception traceback
e_simplified = copy(e)
raise e_simplified
def __iter__(self):
mySize = len(self)
for i in range(mySize):
yield self[i]
def __len__(self):
return int(self._dtype.size)
[docs]
def _eq(self, other):
assert self._dtype.element_t == other._dtype.element_t
assert self._dtype.size == other._dtype.size
try:
eq = True
vld = 1
keysA = set(self.val)
keysB = set(other.val)
sharedKeys = keysA.union(keysB)
lsh = len(sharedKeys)
if (lsh == int(self._dtype.size)
and len(keysA) == lsh
and len(keysB) == lsh):
for k in sharedKeys:
a = self.val[k]
b = other.val[k]
eq = eq and bool(a) == bool(b)
if not eq:
break
vld = vld & a.vld_mask & b.vld_mask
else:
eq = False
vld = 0
return BIT.getConstCls()(BIT, int(eq), vld)
except Exception as e:
# simplification of previous exception traceback
e_simplified = copy(e)
raise e_simplified