Source code for hwtLib.abstract.sim_ram

from math import ceil
from typing import Optional, Union

from hwt.hdl.transTmpl import TransTmpl
from hwt.hdl.types.array import HArray
from hwt.hdl.types.bits import HBits
from hwt.hdl.types.bitsConst import HBitsConst
from hwt.hdl.types.struct import HStruct
from hwt.math import shiftIntArray
from hwt.pyUtils.arrayQuery import grouper
from pyMathBitPrecise.bit_utils import mask, get_bit_range, int_list_to_int


[docs] class AllocationError(Exception): """ Exception which says that requested allocation can not be performed """ pass
[docs] def reshapedInitItems(actualCellSize, requestedCellSize, values): """ Convert array item size and items cnt while size of array remains unchanged :param actualCellSize: actual size of item in array :param requestedCellSize: requested size of item in array :param values: input array :return: generator of new items of specified characteristic """ if (actualCellSize < requestedCellSize and requestedCellSize % actualCellSize == 0): itemsInCell = requestedCellSize // actualCellSize for itemsInWord in grouper(itemsInCell, values, padvalue=0): yield int_list_to_int(itemsInWord, actualCellSize * 8) else: raise NotImplementedError( f"Reshaping of array from cell size {actualCellSize:d} to {requestedCellSize:d}")
[docs] class SimRam(): """ Dense memory for simulation purposes with data pump interfaces :ivar ~.data: memory dict """
[docs] def __init__(self, cellSize:int, parent=None): """ :param cellSize: specifies the number of bytes of word (byte is unit of the addres and it does not have to be 8b) :param clk: clk signal for synchronization :param parent: parent instance of SimRam (memory will be shared with this instance) """ self.parent = parent if parent is None: self.data: dict[int, Union[int, HBitsConst]] = {} else: self.data: dict[int, Union[int, HBitsConst]] = parent.data self.cellSize = cellSize self.prevAllocatedAddrEnd = 0
[docs] def malloc(self, size, keepOut=None): """ Allocates a block of memory of size and initialize it with None (invalid value) :param size: Size of memory block to allocate. :param keepOut: optional memory spacing between this memory region and lastly allocated :return: address of allocated memory """ addr = self.prevAllocatedAddrEnd k = self.data.keys() if k: addr = (max(k) + 1) * self.cellSize if keepOut: addr += keepOut indx = addr // self.cellSize if indx * self.cellSize != addr: NotImplementedError( f"unaligned allocations not implemented (0x{addr:x})") d = self.data for i in range(size // self.cellSize): tmp = indx + i if tmp in d.keys(): raise AllocationError( "Address 0x%x is already occupied" % (tmp * self.cellSize)) d[tmp] = None self.prevAllocatedAddrEnd = addr return addr
[docs] def calloc(self, num, size, keepOut=None, initValues=None) -> int: """ Allocates a block of memory for an array of num elements, each of them size bytes long, and initializes all its bits to zero. :param num: Number of elements to allocate. :param size: Size of each element. :param keepOut: optional memory spacing between this memory region and lastly allocated (number of bit between last allocated segment to avoid) :param initValues: iterable of word values to init memory with :return: address (byte step) of allocated memory """ addr = self.prevAllocatedAddrEnd k = self.data.keys() if k: addr = (max(k) + 1) * self.cellSize if keepOut: addr += keepOut indx = addr // self.cellSize shift = addr % self.cellSize wordCnt = ceil((num * size) / self.cellSize) if shift: # shift all data in init values initValues = shiftIntArray(initValues, self.cellSize * 8, shift * 8) wordCnt += 1 if initValues is not None: if size != self.cellSize: initValues = list(reshapedInitItems( size, self.cellSize, initValues)) assert len(initValues) == wordCnt, (len(initValues), wordCnt) d = self.data for i in range(wordCnt): tmp = indx + i if tmp in d.keys(): raise AllocationError( "Address 0x%x is already occupied" % (tmp * self.cellSize)) if initValues is None: d[tmp] = 0 else: d[tmp] = initValues[i] self.prevAllocatedAddrEnd = (indx + wordCnt) * self.cellSize return addr
[docs] def getArray(self, addr: int, item_size: int, item_cnt: int): """ Get array stored in memory """ baseIndex = addr // self.cellSize if item_size != self.cellSize or baseIndex * self.cellSize != addr: return self._getArray(addr * 8, TransTmpl(HBits(item_size * 8)[item_cnt])) else: out = [] for i in range(baseIndex, baseIndex + item_cnt): try: v = self.data[i] except KeyError: v = None out.append(v) return out
[docs] def getBits(self, start: int, end: int, sign:Optional[bool]) -> HBitsConst: """ Gets value of bits between selected range from memory :param start: bit address of start of bit of bits :param end: bit address of first bit behind bits :return: instance of BitsVal (derived from SimBits type) which contains copy of selected bits """ wordWidth = self.cellSize * 8 inFieldOffset = 0 allMask = mask(wordWidth) value = HBits(end - start, signed=sign).from_py(None) while start != end: assert start < end, (start, end) dataWordIndex = start // wordWidth v = self.data.get(dataWordIndex, None) endOfWord = (dataWordIndex + 1) * wordWidth width = min(end, endOfWord) - start offset = start % wordWidth if v is None: val = 0 vld_mask = 0 elif isinstance(v, int): val = get_bit_range(v, offset, width) vld_mask = allMask else: val = get_bit_range(v.val, offset, width) vld_mask = get_bit_range(v.vld_mask, offset, width) m = mask(width) value.val |= (val & m) << inFieldOffset value.vld_mask |= (vld_mask & m) << inFieldOffset inFieldOffset += width start += width return value
[docs] def _getArray(self, offset, transTmpl): """ :param offset: global offset of this transTmpl (and struct) :param transTmpl: instance of TransTmpl which specifies items in array """ t = transTmpl.dtype c = transTmpl.children arr_offset = offset item_width = c.bitAddrEnd - c.bitAddr value = [] if not isinstance(t.element_t, HBits): raise NotImplementedError(t.element_t) for _ in range(t.size): v = self.getBits(arr_offset, arr_offset + item_width, None) value.append(v) arr_offset += item_width return value
[docs] def _getStruct(self, offset, transTmpl): """ :param offset: global offset of this transTmpl (and struct) :param transTmpl: instance of TransTmpl which specifies items in struct """ dataDict = {} for subTmpl in transTmpl.children: t = subTmpl.dtype name = subTmpl.origin[-1].name if isinstance(t, HBits): value = self.getBits( subTmpl.bitAddr + offset, subTmpl.bitAddrEnd + offset, t.signed) elif isinstance(t, HArray): value = self._getArray(subTmpl.bitAddr + offset, subTmpl) elif isinstance(t, HStruct): value = self._getStruct(offset, subTmpl) else: raise NotImplementedError(t) dataDict[name] = value return transTmpl.dtype.from_py(dataDict)
[docs] def getStruct(self, addr, structT, bitAddr=None): """ Get HStruct from memory :param addr: address where get struct from :param structT: instance of HStruct or FrameTmpl generated from it to resolve structure of data :param bitAddr: optional bit precise address is is not None param addr has to be None """ if bitAddr is None: assert bitAddr is None bitAddr = addr * 8 else: assert addr is not None if isinstance(structT, TransTmpl): transTmpl = structT structT = transTmpl.origin else: assert isinstance(structT, HStruct) transTmpl = TransTmpl(structT) return self._getStruct(bitAddr, transTmpl)