Source code for hwtLib.structManipulators.cLinkedListReader

#!/usr/bin/env python3
# -*- coding: utf-8 -

from hwt.code import If, In, Concat
from hwt.hdl.types.bits import HBits
from hwt.hdl.types.bitsCastUtils import fitTo
from hwt.hwIOs.std import HwIODataRdVld, HwIORegCntrl, HwIOVectSignal
from hwt.hwIOs.utils import addClkRstn, propagateClkRstn
from hwt.hwModule import HwModule
from hwt.hwParam import HwParam
from hwt.math import log2ceil
from hwt.pyUtils.typingFuture import override
from hwtLib.amba.datapump.intf import HwIOAxiRDatapump
from hwtLib.handshaked.fifo import HandshakedFifo
from hwtLib.handshaked.streamNode import StreamNode


[docs] class CLinkedListReader(HwModule): """ This unit reads items from (circular) linked list like structure .. code-block:: c struct node { item_t items[ITEMS_IN_BLOCK], struct node * next; }; synchronization is obtained by rdPtr/wrPtr (tail/head) pointer baseAddr is address of actual node :attention: device reads only chunks of size <= BUFFER_CAPACITY/2, .. hwt-autodoc:: """ @override def hwConfig(self): self.ID_WIDTH = HwParam(4) self.ID = HwParam(3) # id of packet where last item is next addr self.ID_LAST = HwParam(4) self.BUFFER_CAPACITY = HwParam(32) self.ITEMS_IN_BLOCK = HwParam(4096 // 8 - 1) self.ADDR_WIDTH = HwParam(32) self.DATA_WIDTH = HwParam(64) self.PTR_WIDTH = HwParam(16) @override def hwDeclr(self): addClkRstn(self) with self._hwParamsShared(): # interface which sending requests to download data # and interface which is collecting all data and only data with specified id are processed self.rDatapump = HwIOAxiRDatapump()._m() self.rDatapump.MAX_BYTES = self.BUFFER_CAPACITY // 2 * self.DATA_WIDTH // 8 self.dataOut = HwIODataRdVld()._m() # (how much of items remains in block) self.inBlockRemain = HwIOVectSignal(log2ceil(self.ITEMS_IN_BLOCK + 1))._m() # interface to control internal register a = self.baseAddr = HwIORegCntrl() a.DATA_WIDTH = self.ADDR_WIDTH self.rdPtr = HwIORegCntrl() self.wrPtr = HwIORegCntrl() for ptr in [self.rdPtr, self.wrPtr]: ptr.DATA_WIDTH = self.PTR_WIDTH f = self.dataFifo = HandshakedFifo(HwIODataRdVld) f.EXPORT_SIZE = True f.DATA_WIDTH = self.DATA_WIDTH f.DEPTH = self.BUFFER_CAPACITY
[docs] def addrAlignBits(self): return log2ceil(self.DATA_WIDTH // 8)
@override def hwImpl(self): propagateClkRstn(self) r, s = self._reg, self._sig req = self.rDatapump.req f = self.dataFifo dIn = self.rDatapump.r dBuffIn = f.dataIn ALIGN_BITS = self.addrAlignBits() ID = self.ID BUFFER_CAPACITY = self.BUFFER_CAPACITY BURST_LEN = BUFFER_CAPACITY // 2 ID_LAST = self.ID_LAST bufferHasSpace = s("bufferHasSpace") bufferHasSpace(f.size < (BURST_LEN + 1)) # we are counting base next addr as item as well inBlock_t = HBits(log2ceil(self.ITEMS_IN_BLOCK + 1)) ringSpace_t = HBits(self.PTR_WIDTH) downloadPending = r("downloadPending", def_val=0) baseIndex = r("baseIndex", HBits(self.ADDR_WIDTH - ALIGN_BITS)) inBlockRemain = r("inBlockRemain_reg", inBlock_t, def_val=self.ITEMS_IN_BLOCK) self.inBlockRemain(inBlockRemain) # Logic of tail/head rdPtr = r("rdPtr", ringSpace_t, def_val=0) wrPtr = r("wrPtr", ringSpace_t, def_val=0) If(self.wrPtr.dout.vld, wrPtr(self.wrPtr.dout.data) ) self.wrPtr.din(wrPtr) self.rdPtr.din(rdPtr) # this means items are present in memory hasSpace = s("hasSpace") hasSpace(wrPtr != rdPtr) doReq = s("doReq") doReq(bufferHasSpace & hasSpace & ~downloadPending & req.rd) req.rem(0) self.dataOut(f.dataOut) # logic of baseAddr and baseIndex baseAddr = Concat(baseIndex, HBits(ALIGN_BITS).from_py(0)) req.addr(baseAddr) self.baseAddr.din(baseAddr) dataAck = dIn.valid & In(dIn.id, [ID, ID_LAST]) & dBuffIn.rd If(self.baseAddr.dout.vld, baseIndex(self.baseAddr.dout.data[:ALIGN_BITS]) ).Elif(dataAck & downloadPending, If(dIn.last & dIn.id._eq(ID_LAST), baseIndex(dIn.data[self.ADDR_WIDTH:ALIGN_BITS]) ).Else( baseIndex(baseIndex + 1) ) ) sizeByPtrs = s("sizeByPtrs", ringSpace_t) sizeByPtrs(wrPtr - rdPtr) inBlockRemain_asPtrSize = fitTo(inBlockRemain, sizeByPtrs) constraingSpace = s("constraingSpace", ringSpace_t) If(inBlockRemain_asPtrSize < sizeByPtrs, constraingSpace(inBlockRemain_asPtrSize) ).Else( constraingSpace(sizeByPtrs) ) constrainedByInBlockRemain = s("constrainedByInBlockRemain") constrainedByInBlockRemain(fitTo(sizeByPtrs, inBlockRemain) >= inBlockRemain) If(constraingSpace > BURST_LEN, # download full burst req.id(ID), req.len(BURST_LEN - 1), If(doReq, inBlockRemain(inBlockRemain - BURST_LEN) ) ).Elif(constrainedByInBlockRemain & (inBlockRemain < BURST_LEN), # we know that sizeByPtrs <= inBlockRemain thats why we can resize it # we will download next* as well req.id(ID_LAST), req.len(constraingSpace, fit=True), If(doReq, inBlockRemain(self.ITEMS_IN_BLOCK) ) ).Else( # download data leftover req.id(ID), req.len(constraingSpace - 1, fit=True), If(doReq, inBlockRemain(inBlockRemain - fitTo(constraingSpace, inBlockRemain)) ) ) # logic of req dispatching If(downloadPending, req.vld(0), If(dataAck & dIn.last, downloadPending(0) ) ).Else( req.vld(bufferHasSpace & hasSpace), If(req.rd & bufferHasSpace & hasSpace, downloadPending(1) ) ) # into buffer pushing logic dBuffIn.data(dIn.data) isMyData = s("isMyData") isMyData(dIn.id._eq(ID) | (~dIn.last & dIn.id._eq(ID_LAST))) If(self.rdPtr.dout.vld, rdPtr(self.rdPtr.dout.data) ).Else( If(dIn.valid & downloadPending & dBuffIn.rd & isMyData, rdPtr(rdPtr + 1) ) ) # push data into buffer and increment rdPtr StreamNode(masters=[dIn], slaves=[dBuffIn], extraConds={dIn: downloadPending, dBuffIn: (dIn.id._eq(ID) | (dIn.id._eq(ID_LAST) & ~dIn.last) ) & downloadPending }).sync()
if __name__ == "__main__": from hwt.synth import to_rtl_str m = CLinkedListReader() m.BUFFER_CAPACITY = 8 m.ITEMS_IN_BLOCK = 31 m.PTR_WIDTH = 8 print(to_rtl_str(m))