Source code for hwt.hdl.types.bitsVal

from copy import copy
from operator import eq
from typing import Union

from hwt.doc_markers import internal
from hwt.hdl.operator import Operator
from hwt.hdl.operatorDefs import AllOps
from hwt.hdl.types.bitValFunctions import bitsCmp, \
    bitsBitOp, bitsArithOp
from hwt.hdl.types.bitVal_opReduce import tryReduceOr, tryReduceAnd, \
    tryReduceXor, reduceSigCheckFnAnd, reduceSigCheckFnOr, reduceSigCheckFnXor
from hwt.hdl.types.bits import Bits
from hwt.hdl.types.defs import BOOL, INT, BIT, SLICE, BIT_N
from hwt.hdl.types.eventCapableVal import EventCapableVal
from hwt.hdl.types.slice import HSlice
from hwt.hdl.types.sliceUtils import slice_to_SLICE
from hwt.hdl.types.typeCast import toHVal
from hwt.hdl.value import HValue, areHValues
from hwt.synthesizer.interfaceLevel.mainBases import InterfaceBase
from hwt.synthesizer.rtlLevel.mainBases import RtlSignalBase
from hwt.synthesizer.rtlLevel.signalUtils.exceptions import SignalDriverErr
from pyMathBitPrecise.bit_utils import ValidityError
from pyMathBitPrecise.bits3t import Bits3val
from pyMathBitPrecise.bits3t_vld_masks import vld_mask_for_xor, vld_mask_for_and, \
    vld_mask_for_or


[docs]@internal def _get_operator_i_am_the_result_of(val_or_sig: Union[RtlSignalBase, HValue]): if not isinstance(val_or_sig, HValue) and len(val_or_sig.drivers) == 1 and isinstance(val_or_sig.origin, Operator): return val_or_sig.origin.operator else: return None
[docs]class BitsVal(Bits3val, EventCapableVal, HValue): """ :attention: operator on signals are using value operator functions as well """ _BOOL = Bits(1, name="bool")
[docs] @classmethod def from_py(cls, typeObj, val, vld_mask=None): val, vld_mask = typeObj._normalize_val_and_mask(val, vld_mask) return cls(typeObj, val, vld_mask=vld_mask)
[docs] @internal def _convSign__val(self, signed:Union[bool, None]): v = Bits3val.cast_sign(self, signed) if signed is not None: if v._dtype is self._dtype: # can modify shared type instance v._dtype = copy(v._dtype) v._dtype.force_vector = True return v
[docs] @internal def _convSign(self, signed): """ Convert signum, no bit manipulation just data are represented differently :param signed: if True value will be signed, if False value will be unsigned, if None value will be vector without any sign specification """ if isinstance(self, HValue): return self._convSign__val(signed) else: if self._dtype.signed == signed: return self t = copy(self._dtype) t.signed = signed if t.signed is not None: t.force_vector = True if signed is None: cnv = AllOps.BitsAsVec elif signed: cnv = AllOps.BitsAsSigned else: cnv = AllOps.BitsAsUnsigned return Operator.withRes(cnv, [self], t)
[docs] def _auto_cast(self, dtype): return HValue._auto_cast(self, dtype)
[docs] def _signed(self): return self._convSign(True)
[docs] def _unsigned(self): return self._convSign(False)
[docs] def _vec(self): return self._convSign(None)
[docs] @internal def _concat__val(self, other): return Bits3val._concat(self, other)
[docs] def _concat(self, other): """ Concatenate this with other to one wider value/signal """ w = self._dtype.bit_length() try: other._dtype.bit_length except AttributeError: raise TypeError("Can not concat Bits and", other) self = self._vec() if areHValues(self, other): return self._concat__val(other) else: w = self._dtype.bit_length() other_w = other._dtype.bit_length() resWidth = w + other_w Bits = self._dtype.__class__ resT = Bits(resWidth, signed=self._dtype.signed, force_vector=True) # is instance of signal if isinstance(other, InterfaceBase): other = other._sig if other._dtype == BOOL: other = other._auto_cast(BIT) elif isinstance(other._dtype, Bits): if other._dtype.signed is not None: other = other._vec() else: raise TypeError(other._dtype) if self._dtype.signed is not None: self = self._vec() return Operator.withRes(AllOps.CONCAT, [self, other], resT)\ ._auto_cast(Bits(resWidth, signed=self._dtype.signed))
def __getitem__(self, key): """ [] operator :attention: Table below is for litle endian bit order (MSB:LSB) which is default. This is **reversed** as it is in pure python where it is [0, len(self)]. :attention: slice on slice f signal is automatically reduced to single slice +-----------------------------+----------------------------------------------------------------------------------+ | a[up:low] | items low through up; a[16:8] selects upper byte from 16b vector a | +-----------------------------+----------------------------------------------------------------------------------+ | a[up:] | low is automatically substituted with 0; a[8:] will select lower 8 bits | +-----------------------------+----------------------------------------------------------------------------------+ | a[:end] | up is automatically substituted; a[:8] will select upper byte from 16b vector a | +-----------------------------+----------------------------------------------------------------------------------+ | a[:], a[-1], a[-2:], a[:-2] | raises NotImplementedError (not implemented due to complicated support in hdl) | +-----------+----------------------------------------------------------------------------------------------------+ """ st = self._dtype length = st.bit_length() if isinstance(key, slice): key = slice_to_SLICE(key, length) isSLICE = True else: isSLICE = isinstance(key, HSlice.getValueCls()) is1bScalar = length == 1 and not st.force_vector if not isSLICE: if is1bScalar and \ ((isinstance(key, int) and key == 0) or\ (isinstance(key, BitsVal) and key._is_full_valid() and int(key) == 0)): return self key = toHVal(key, INT) else: if is1bScalar and key.val.start == 1 and key.val.stop == 0 and key.val.step == -1: return self if is1bScalar: # assert not indexing on single bit raise IndexError("indexing on single bit") iamVal = isinstance(self, HValue) iAmResultOf = _get_operator_i_am_the_result_of(self) Bits = self._dtype.__class__ if isSLICE: # :note: downto notation start = key.val.start stop = key.val.stop if key.val.step != -1: raise NotImplementedError() startIsVal = isinstance(start, HValue) stopIsVal = isinstance(stop, HValue) indexesareHValues = startIsVal and stopIsVal if indexesareHValues and start.val == length and stop.val == 0: # selecting all bits no conversion needed return self # check start boundaries if startIsVal: _start = int(start) if _start < 0 or _start > length: raise IndexError(_start, length) # check end boundaries if stopIsVal: _stop = int(stop) if _stop < 0 or _stop > length: raise IndexError(_stop, length) # check width of selected range if startIsVal and stopIsVal and _start - _stop <= 0: raise IndexError(_start, _stop) if iAmResultOf == AllOps.INDEX: # try reduce self and parent slice to one original, parentIndex = self.origin.operands if isinstance(parentIndex._dtype, HSlice): parentLower = parentIndex.val.stop start = start + parentLower stop = stop + parentLower return original[start:stop] elif startIsVal and stopIsVal: # index directly in the member of concatenation # :note: start points at MSB and stop on LSB (start:stop, eg 8:0) stop = int(stop) start = int(start) if iAmResultOf == AllOps.CONCAT: op_h, op_l = self.origin.operands op_l_w = op_l._dtype.bit_length() assert start > stop, (start, stop, "Should be in MSB:LSB format") if start <= op_l_w: # entirely in first operand of concat if op_l_w == 1: if isinstance(key._dtype, HSlice): assert int(key.val.start) == 1 and int(key.val.stop) == 0 and int(key.val.step) == -1, key return op_l else: assert int(key) == 0, key return op_l else: return op_l[key] elif stop >= op_l_w: # intirely in second operand of concat start -= op_l_w stop -= op_l_w if op_h._dtype.bit_length() == 1: assert start - stop == 1 return op_h else: return op_h[key._dtype.from_py(slice(start, stop, -1))] else: # partially in op_h and op_l, allpy slice on concat operands and return concatenation of it if stop != 0 or op_l._dtype.bit_length() > 1: op_l = op_l[:stop] if op_h._dtype.bit_length() == 1: assert start - op_l_w == 1, ("Out of range slice (but this error should be catched sooner)", self, key) else: op_h = op_h[start - op_l_w:0] return op_h._concat(op_l) if iamVal: if isinstance(key, SLICE.getValueCls()): key = key.val v = Bits3val.__getitem__(self, key) if v._dtype.bit_length() == 1 and not v._dtype.force_vector: assert v._dtype is not self._dtype v._dtype.force_vector = True return v else: key = SLICE.from_py(slice(start, stop, -1)) _resWidth = start - stop resT = Bits(bit_length=_resWidth, force_vector=True, signed=st.signed, negated=st.negated) elif isinstance(key, Bits.getValueCls()): # int like value addressing a single bit if key._is_full_valid(): # check index range _v = int(key) if _v < 0 or _v > length - 1: raise IndexError(_v) if iAmResultOf == AllOps.INDEX: # index directly in parent signal original, parentIndex = self.origin.operands if isinstance(parentIndex._dtype, HSlice): parentLower = parentIndex.val.stop return original[parentLower + _v] else: # index directly in the member of concatenation update_key = False while iAmResultOf == AllOps.CONCAT: op_h, op_l = self.origin.operands op_l_w = op_l._dtype.bit_length() if _v < op_l_w: self = op_l else: self = op_h _v -= op_l_w update_key = True iamVal = isinstance(self, HValue) iAmResultOf = _get_operator_i_am_the_result_of(self) st = self._dtype if update_key: key = key._dtype.from_py(_v) if iamVal: return Bits3val.__getitem__(self, key) resT = BIT elif isinstance(key, RtlSignalBase): t = key._dtype if isinstance(t, HSlice): resT = Bits(bit_length=key.staticEval()._size(), force_vector=True, signed=st.signed, negated=st.negated) elif isinstance(t, Bits): resT = BIT else: raise TypeError( "Index operation not implemented" f" for index of type {t}") else: raise TypeError( f"Index operation not implemented for index {key}") if st.negated and resT is BIT: resT = BIT_N return Operator.withRes(AllOps.INDEX, [self, key], resT) def __setitem__(self, index, value): """ this []= operator can not be called in desing description, it can be only used to update HValues """ if not isinstance(self, HValue): raise TypeError("To assign a member of hdl arrray/vector/list/... use a[index](val) instead of a[index] = val") # convert index to HSlice or hInt if isinstance(index, HValue): index = index elif isinstance(index, slice): length = self._dtype.bit_length() index = slice_to_SLICE(index, length) if not index._is_full_valid(): raise ValueError("invalid index", index) else: index = INT.from_py(index) # convert value to bits of length specified by index if index._dtype == SLICE: Bits = self._dtype.__class__ itemT = Bits(index._size()) else: itemT = BIT if isinstance(value, HValue): value = value._auto_cast(itemT) else: value = itemT.from_py(value) return Bits3val.__setitem__(self, index, value) def __invert__(self): if isinstance(self, HValue): return Bits3val.__invert__(self) else: try: # double negation d = self.singleDriver() if isinstance(d, Operator) and d.operator == AllOps.NOT: return d.operands[0] except SignalDriverErr: pass return Operator.withRes(AllOps.NOT, [self], self._dtype) def __hash__(self): if isinstance(self, RtlSignalBase): return hash(id(self)) else: return Bits3val.__hash__(self) # comparisons
[docs] def _isOn(self): return self._auto_cast(BOOL)
[docs] def _eq(self, other): return bitsCmp(self, other, AllOps.EQ, BIT.from_py(1), eq)
def __ne__(self, other): return bitsCmp(self, other, AllOps.NE, BIT.from_py(0)) def __lt__(self, other): return bitsCmp(self, other, AllOps.LT, BIT.from_py(0)) def __gt__(self, other): return bitsCmp(self, other, AllOps.GT, BIT.from_py(0)) def __ge__(self, other): return bitsCmp(self, other, AllOps.GE, BIT.from_py(1)) def __le__(self, other): return bitsCmp(self, other, AllOps.LE, BIT.from_py(1)) def __xor__(self, other): return bitsBitOp(self, other, AllOps.XOR, vld_mask_for_xor, tryReduceXor, reduceSigCheckFnXor) def __and__(self, other): return bitsBitOp(self, other, AllOps.AND, vld_mask_for_and, tryReduceAnd, reduceSigCheckFnAnd) def __or__(self, other): return bitsBitOp(self, other, AllOps.OR, vld_mask_for_or, tryReduceOr, reduceSigCheckFnOr) def __lshift__(self, other): """ shift left :note: arithmetic sift if type is signed else logical shift """ width = self._dtype.bit_length() return self[(width - int(other)):]._concat(Bits(int(other)).from_py(0)) def __rshift__(self, other): """ shift right :note: arithmetic sift if type is signed else logical shift """ if self._dtype.signed: return self[:self._dtype.bit_length() - other]._concat(self[:other]) return Bits(int(other)).from_py(0)._concat(self[:other]) def __neg__(self): if isinstance(self, HValue): return Bits3val.__neg__(self) else: if not self._dtype.signed: self = self._signed() resT = self._dtype o = Operator.withRes(AllOps.MINUS_UNARY, [self], self._dtype) return o._auto_cast(resT) def __sub__(self, other): return bitsArithOp(self, other, AllOps.SUB) def __add__(self, other): return bitsArithOp(self, other, AllOps.ADD) def __floordiv__(self, other) -> "Bits3val": other = toHVal(other, suggestedType=self._dtype) if isinstance(self, HValue) and isinstance(other, HValue): return Bits3val.__floordiv__(self, other) else: if not isinstance(other._dtype, self._dtype.__class__): raise TypeError() return Operator.withRes(AllOps.DIV, [self, other], self._dtype.__copy__()) def __pow__(self, other): raise TypeError(f"** operator not implemented for instance of {self.__class__}") def __mod__(self, other): raise TypeError(f"% operator not implemented for instance of {self.__class__}")
[docs] def _ternary(self, a, b): if isinstance(self, HValue): if self: return a else: return b else: a = toHVal(a) b = toHVal(b, suggestedType=a._dtype) try: if a == b: return a except ValidityError: pass if not (a._dtype == b._dtype): # all case values of ternary has to have same type b = b._auto_cast(a._dtype) return Operator.withRes( AllOps.TERNARY, [self, a, b], a._dtype.__copy__())
def __mul__(self, other): Bits = self._dtype.__class__ other = toHVal(other, suggestedType=self._dtype) if not isinstance(other._dtype, Bits): raise TypeError(other) self_is_val = isinstance(self, HValue) other_is_val = isinstance(other, HValue) if self_is_val and other_is_val: return Bits3val.__mul__(self, other) else: # reduce *1 and *0 if self_is_val and self._is_full_valid(): _s = int(self) if _s == 0: return self._dtype.from_py(0) elif _s: return other._auto_cast(self._dtype) if other_is_val and other._is_full_valid(): _o = int(other) if _o == 0: return self._dtype.from_py(0) elif _o == 1: return self myT = self._dtype if self._dtype.signed is None: self = self._unsigned() if isinstance(other._dtype, Bits): s = other._dtype.signed if s is None: other = other._unsigned() else: raise TypeError(f"{self} {AllOps.MUL} {other}") if other._dtype == INT: res_w = myT.bit_length() res_sign = self._dtype.signed subResT = resT = myT else: res_w = max(myT.bit_length(), other._dtype.bit_length()) res_sign = self._dtype.signed or other._dtype.signed subResT = Bits(res_w, signed=res_sign) resT = Bits(res_w, signed=myT.signed) o = Operator.withRes(AllOps.MUL, [self, other], subResT) return o._auto_cast(resT) def __len__(self): return self._dtype.bit_length()