Source code for hwt.synthesizer.vectorUtils

from functools import reduce
from math import ceil
from typing import Union

from hwt.doc_markers import internal
from hwt.hdl.types.bits import Bits
from hwt.hdl.types.utils import walkFlattenFields
from hwt.hdl.value import HValue
from hwt.synthesizer.rtlLevel.mainBases import RtlSignalBase


[docs]class BitWidthErr(Exception): """ Wrong bit width of signal/value """
[docs]def fitTo_t(what: Union[RtlSignalBase, HValue], where_t: Bits, extend: bool=True, shrink: bool=True): """ Slice signal "what" to fit in "where" or arithmetically (for signed by MSB / unsigned, vector with 0) extend "what" to same width as "where" little-endian impl. :param extend: allow increasing of the signal width :param shrink: allow shrinking of the signal width """ whatWidth = what._dtype.bit_length() toWidth = where_t.bit_length() if toWidth == whatWidth: return what elif toWidth < whatWidth: # slice if not shrink: raise BitWidthErr() return what[toWidth:] else: if not extend: raise BitWidthErr() w = toWidth - whatWidth if what._dtype.signed: # signed extension msb = what[whatWidth - 1] ext = reduce(lambda a, b: a._concat(b), [msb for _ in range(w)]) else: # 0 extend ext = Bits(w).from_py(0) res = ext._concat(what) if where_t.signed is not None: return res._reinterpret_cast(where_t) return res
[docs]def fitTo(what: Union[RtlSignalBase, HValue], where: Union[RtlSignalBase, HValue], extend: bool=True, shrink: bool=True): return fitTo_t(what, where._dtype, extend, shrink)
[docs]class NotEnoughtBitsErr(Exception): """ More bits is required for such an operation """
[docs]class BitWalker(): """ Walker which can walk chunks of bits on signals/values of all types :ivar ~.sigOrVal: signal or value to iterate over :ivar ~.fillup: flag that means that if there is not enough bits for last item fill it up with invalid bits (otherwise raise) """
[docs] def __init__(self, sigOrVal: Union[RtlSignalBase, HValue], skipPadding: bool=True, fillup: bool=False): """ :param skipPadding: if true padding is skipped in dense types """ self.it = walkFlattenFields(sigOrVal, skipPadding=skipPadding) self.fillup = fillup self.actuallyHave = 0 self.actual = None self.actualOffset = 0
[docs] @internal def _get(self, numberOfBits: int, doCollect: bool): """ :param numberOfBits: number of bits to get from actual position :param doCollect: if False output is not collected just iterator moves in data structure """ if not isinstance(numberOfBits, int): numberOfBits = int(numberOfBits) while self.actuallyHave < numberOfBits: # accumulate while not has enough try: f = next(self.it) except StopIteration: if self.fillup and self.actual is not None: break else: raise NotEnoughtBitsErr() thisFieldLen = f._dtype.bit_length() if self.actual is None: if not doCollect and thisFieldLen <= numberOfBits: numberOfBits -= thisFieldLen else: self.actual = f self.actuallyHave = thisFieldLen else: if not doCollect and self.actuallyHave < numberOfBits: self.actuallyHave = thisFieldLen self.actual = f else: self.actuallyHave += thisFieldLen self.actual = f._concat(self.actual) # slice out from actual actual = self.actual actualOffset = self.actualOffset if self.actuallyHave < numberOfBits: assert self.fillup if doCollect: t = self.actual._dtype fillupW = numberOfBits - self.actuallyHave padding_t = Bits(fillupW, signed=t.signed, negated=t.negated) padding = padding_t.from_py(None) actual = padding._concat(actual) self.actuallyHave = 0 self.actualOffset = 0 else: # update about what was taken self.actuallyHave -= numberOfBits self.actualOffset += numberOfBits if self.actuallyHave == 0: self.actual = None self.actualOffset = 0 if doCollect: if numberOfBits == 1: return actual[actualOffset] else: return actual[(actualOffset + numberOfBits):actualOffset]
[docs] def get(self, numberOfBits: int) -> Union[RtlSignalBase, HValue]: """ :param numberOfBits: number of bits to get from actual position :return: chunk of bits of specified size (instance of Value or RtlSignal) """ return self._get(numberOfBits, True)
[docs] def skip(self, numberOfBits: int) -> None: """ Move this iterator without care about item :param numberOfBits: number of bits to get from actual position """ self._get(numberOfBits, False)
[docs] def assertIsOnEnd(self): """ Assert there is nothing left in this iterator """ try: next(self.it) except StopIteration: return raise AssertionError("there are still some items")
[docs]def iterBits(sigOrVal: Union[RtlSignalBase, HValue], bitsInOne: int=1, skipPadding: bool=True, fillup: bool=False): """ Iterate over bits in vector :param sigOrVal: signal or value to iterate over :param bitsInOne: number of bits in one part :param skipPadding: if true padding is skipped in dense types :param fillup: flag that means that if there is not enough bits for last item fill it up with invalid bits (otherwise raise) """ bw = BitWalker(sigOrVal, skipPadding, fillup) try: bit_len = sigOrVal._dtype.bit_length() except TypeError: bit_len = None if bit_len is None: try: while True: yield bw.get(bitsInOne) except NotEnoughtBitsErr: return else: for _ in range(ceil(bit_len / bitsInOne)): yield bw.get(bitsInOne) bw.assertIsOnEnd()