from itertools import chain
from hwt.bitmask import mask, selectBitRange
from hwt.hdl.transTmpl import TransTmpl
from hwt.hdl.types.array import HArray
from hwt.hdl.types.bits import Bits
from hwt.hdl.types.struct import HStruct
from hwt.simulator.types.simBits import simBitsT
from collections import deque
[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 characteristik
"""
if actualCellSize < requestedCellSize and requestedCellSize % actualCellSize == 0:
itemsInCell = requestedCellSize // actualCellSize
itemAlign = len(values) % itemsInCell
if itemAlign != 0:
values = chain(values, [0 for _ in range(itemsInCell - itemAlign)])
for itemsInWord in zip(*[iter(values)] * itemsInCell):
item = 0
for iIndx, i2 in enumerate(itemsInWord):
subIndx = itemsInCell - iIndx - 1
_i2 = (mask(actualCellSize * 8) & i2) << (subIndx * actualCellSize * 8)
item |= _i2
yield item
else:
raise NotImplementedError("Reshaping of array from cell size %d to %d" % (
actualCellSize, requestedCellSize))
[docs]class DenseMemory():
"""
Dense memory for simulation purposes with datapump interfaces
:ivar data: memory dict
"""
[docs] def __init__(self, cellWidth, clk, rDatapumpIntf=None, wDatapumpIntf=None, parent=None):
"""
:param cellWidth: width of items in memmory
:param clk: clk signal for synchronization
:param parent: parent instance of DenseMemory (memory will be shared with this instance)
"""
assert cellWidth % 8 == 0
self.cellSize = cellWidth // 8
self.allMask = mask(self.cellSize)
self.parent = parent
if parent is None:
self.data = {}
else:
self.data = parent.data
assert rDatapumpIntf is not None or wDatapumpIntf is not None
if rDatapumpIntf is None:
arAg = rAg = None
else:
arAg = rDatapumpIntf.req._ag
rAg = rDatapumpIntf.r._ag
self.arAg = arAg
self.rAg = rAg
self.rPending = deque()
if wDatapumpIntf is None:
awAg = wAg = wAckAg = None
else:
awAg = wDatapumpIntf.req._ag
wAg = wDatapumpIntf.w._ag
wAckAg = wDatapumpIntf.ack._ag
self.awAg = awAg
self.wAg = wAg
self.wAckAg = wAckAg
self.wPending = deque()
self._registerOnClock(clk)
[docs] def _registerOnClock(self, clk):
clk._sigInside.simRisingSensProcs.add(self.checkRequests)
[docs] def checkRequests(self, simulator, _):
"""
Check if any request has appeared on interfaces
"""
if self.arAg is not None:
if self.arAg.data:
self.onReadReq()
if self.rPending:
self.doRead()
if self.awAg is not None:
if self.awAg.data:
self.onWriteReq()
if self.wPending and self.wPending[0][2] <= len(self.wAg.data):
self.doWrite()
[docs] def parseReq(self, req):
for i, v in enumerate(req):
assert v._isFullVld(), (i, v)
assert len(req) == 4
_id = req[0].val
addr = req[1].val
size = req[2].val + 1
if req[3].val == 0:
lastWordBitmask = self.allMask
else:
lastWordBitmask = mask(req[3].val)
size += 1
return (_id, addr, size, lastWordBitmask)
[docs] def onReadReq(self):
readReq = self.arAg.data.pop()
self.rPending.append(self.parseReq(readReq))
[docs] def onWriteReq(self):
writeReq = self.awAg.data.pop()
self.wPending.append(self.parseReq(writeReq))
[docs] def doRead(self):
_id, addr, size, lastWordBitmask = self.rPending.popleft()
baseIndex = addr // self.cellSize
if baseIndex * self.cellSize != addr:
raise NotImplementedError("unaligned transaction not implemented (0x%x)" % addr)
for i in range(size):
isLast = i == size - 1
try:
data = self.data[baseIndex + i]
except KeyError:
data = None
if data is None:
raise AssertionError("Invalid read of uninitialized value on addr 0x%x" %
(addr + i * self.cellSize))
if isLast:
strb = lastWordBitmask
else:
strb = self.allMask
self.rAg.data.append((_id, data, strb, isLast))
[docs] def doWriteAck(self, _id):
self.wAckAg.data.append(_id)
[docs] def doWrite(self):
_id, addr, size, lastWordBitmask = self.wPending.popleft()
baseIndex = addr // self.cellSize
if baseIndex * self.cellSize != addr:
raise NotImplementedError("unaligned transaction not implemented")
for i in range(size):
data, strb, last = self.wAg.data.popleft()
# assert data._isFullVld()
assert strb._isFullVld()
assert last._isFullVld()
data, strb, last = data.val, strb.val, bool(last.val)
isLast = i == size - 1
assert last == isLast, "write 0x%x, size %d, expected last:%d in word %d" % (addr, size, isLast, i)
if data is None:
raise AssertionError("Invalid read of uninitialized value on addr 0x%x" %
(addr + i * self.cellSize))
if isLast:
expectedStrb = lastWordBitmask
else:
expectedStrb = self.allMask
if expectedStrb != self.allMask:
raise NotImplementedError()
assert strb == expectedStrb
self.data[baseIndex + i] = data
self.doWriteAck(_id)
[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 each element.
:param keepOut: optional memory spacing between this memory region and lastly allocated
:return: address of allocated memory
"""
addr = 0
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("unaligned allocations not implemented (0x%x)" % addr)
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
return addr
[docs] def calloc(self, num, size, keepOut=None, initValues=None):
"""
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
:param initValues: iterable of word values to init memory with
:return: address of allocated memory
"""
addr = 0
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("unaligned allocations not implemented (0x%x)" % addr)
d = self.data
wordCnt = (num * size) // self.cellSize
if initValues is not None:
if size != self.cellSize:
initValues = list(reshapedInitItems(size, self.cellSize, initValues))
assert len(initValues) == wordCnt, (len(initValues), wordCnt)
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]
return addr
[docs] def getArray(self, addr, itemSize, itemCnt):
"""
Get array stored in memory
"""
if itemSize != self.cellSize:
raise NotImplementedError()
baseIndex = addr // self.cellSize
if baseIndex * self.cellSize != addr:
raise NotImplementedError("unaligned not implemented")
out = []
for i in range(baseIndex, baseIndex + itemCnt):
try:
v = self.data[i]
except KeyError:
v = None
out.append(v)
return out
[docs] def getBits(self, start, end):
"""
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 = simBitsT(end - start, None).fromPy(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
vldMask = 0
updateTime = -1
elif isinstance(v, int):
val = selectBitRange(v, offset, width)
vldMask = allMask
updateTime = -1
else:
val = selectBitRange(v.val, offset, width)
vldMask = selectBitRange(v.vldMask, offset, width)
updateTime = v.updateTime
m = mask(width)
value.val |= (val & m) << inFieldOffset
value.vldMask |= (vldMask & m) << inFieldOffset
value.updateMask = max(value.updateTime, updateTime)
inFieldOffset += width
start += 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.name
if isinstance(t, Bits):
value = self.getBits(subTmpl.bitAddr + offset, subTmpl.bitAddrEnd + offset)
elif isinstance(t, HArray):
raise NotImplementedError()
elif isinstance(t, HStruct):
value = self._getStruct(offset, subTmpl)
else:
raise NotImplementedError(t)
dataDict[name] = value
return transTmpl.dtype.fromPy(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 resove structure of data
:param bitAddr: optional bit precisse 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)