Source code for hwtLib.structManipulators.arrayBuff_writer

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

from hwt.code import If, Concat, FsmBuilder
from hwt.hdl.types.bits import Bits
from hwt.hdl.types.enum import HEnum
from hwt.interfaces.std import Handshaked, VectSignal, RegCntrl
from hwt.interfaces.utils import addClkRstn, propagateClkRstn
from hwt.math import log2ceil
from hwt.serializer.mode import serializeParamsUniq
from hwt.synthesizer.param import Param
from hwt.synthesizer.unit import Unit
from hwt.synthesizer.vectorUtils import fitTo
from hwtLib.amba.datapump.intf import AddrSizeHs, AxiWDatapumpIntf
from hwtLib.handshaked.fifo import HandshakedFifo
from hwtLib.handshaked.streamNode import StreamNode
from pyMathBitPrecise.bit_utils import mask


stT = HEnum("st_t", ["waitOnInput", "waitOnDataTx", "waitOnAck"])

# [TODO] better fit of items on bus
# [TODO] fully pipeline
# [TODO] use AxiVirtualDma

[docs]@serializeParamsUniq class ArrayBuff_writer(Unit): """ Write data in to a circula buffer allocated as an array. Collect items and send them over wDatapump when buffer is full or on timeout Maximum overlap of transactions is 1 items -> buff -> internal logic -> axi datapump .. hwt-autodoc:: """ def _config(self): AddrSizeHs._config(self) self.ID = Param(3) self.MAX_LEN = 15 self.ITEM_WIDTH = Param(16) self.BUFF_DEPTH = Param(16) self.TIMEOUT = Param(1024) self.ITEMS = Param(4096 // 8) def _declr(self): addClkRstn(self) self.items = Handshaked() self.items.DATA_WIDTH = self.ITEM_WIDTH with self._paramsShared(): self.wDatapump = AxiWDatapumpIntf()._m() self.uploaded = VectSignal(16)._m() self.baseAddr = RegCntrl() self.baseAddr.DATA_WIDTH = self.ADDR_WIDTH self.buff_remain = VectSignal(16)._m() b = HandshakedFifo(Handshaked) b.DATA_WIDTH = self.ITEM_WIDTH b.EXPORT_SIZE = True b.DEPTH = self.BUFF_DEPTH self.buff = b
[docs] def uploadedCntrHandler(self, st, reqAckHasCome, sizeOfitems): uploadedCntr = self._reg( "uploadedCntr", self.uploaded._dtype, def_val=0) self.uploaded(uploadedCntr) If(st._eq(stT.waitOnAck) & reqAckHasCome, uploadedCntr(uploadedCntr + fitTo(sizeOfitems, uploadedCntr)) )
def _impl(self): ALIGN_BITS = log2ceil(self.DATA_WIDTH // 8 - 1) TIMEOUT_MAX = self.TIMEOUT - 1 ITEMS = self.ITEMS buff = self.buff reqAck = self.wDatapump.ack req = self.wDatapump.req w = self.wDatapump.w propagateClkRstn(self) sizeOfitems = self._reg("sizeOfItems", Bits( buff.size._dtype.bit_length())) # aligned base addr baseAddr = self._reg("baseAddrReg", Bits(self.ADDR_WIDTH - ALIGN_BITS)) If(self.baseAddr.dout.vld, baseAddr(self.baseAddr.dout.data[:ALIGN_BITS]) ) self.baseAddr.din(Concat(baseAddr, Bits(ALIGN_BITS).from_py(0))) # offset in buffer and its complement offset_t = Bits(log2ceil(ITEMS + 1), signed=False) offset = self._reg("offset", offset_t, def_val=0) remaining = self._reg("remaining", Bits( log2ceil(ITEMS + 1), signed=False), def_val=ITEMS) self.buff_remain(remaining, fit=True) addrTmp = self._sig("baseAddrTmp", baseAddr._dtype) addrTmp(baseAddr + fitTo(offset, baseAddr)) # req values logic req.id(self.ID) req.addr(Concat(addrTmp, Bits(ALIGN_BITS).from_py(0))) req.rem(0) sizeTmp = self._sig("sizeTmp", buff.size._dtype) assert (req.len._dtype.bit_length() == buff.size._dtype.bit_length() - 1), ( req.len._dtype.bit_length(), buff.size._dtype.bit_length()) buffSizeAsLen = self._sig("buffSizeAsLen", buff.size._dtype) buffSizeAsLen(buff.size - 1) buffSize_tmp = self._sig("buffSize_tmp", remaining._dtype) buffSize_tmp(buff.size, fit=True) endOfLenBlock = (remaining - 1) < buffSize_tmp remainingAsLen = self._sig("remainingAsLen", remaining._dtype) remainingAsLen(remaining - 1) If(endOfLenBlock, req.len(remainingAsLen, fit=True), sizeTmp(remaining, fit=True) ).Else( req.len(buffSizeAsLen, fit=True), sizeTmp(buff.size) ) lastWordCntr = self._reg("lastWordCntr", buff.size._dtype, 0) w_last = lastWordCntr._eq(1) w_ack = w.ready & buff.dataOut.vld # timeout logic timeoutCntr = self._reg("timeoutCntr", Bits(log2ceil(self.TIMEOUT), False), def_val=TIMEOUT_MAX) # buffer is full or timeout beginReq = buff.size._eq(self.BUFF_DEPTH) | timeoutCntr._eq(0) reqAckHasCome = self._sig("reqAckHasCome") reqAckHasCome(reqAck.vld & reqAck.data._eq(self.ID)) st = FsmBuilder(self, stT)\ .Trans(stT.waitOnInput, (beginReq & req.rd, stT.waitOnDataTx) ).Trans(stT.waitOnDataTx, (w_last & w_ack, stT.waitOnAck) ).Trans(stT.waitOnAck, (reqAckHasCome, stT.waitOnInput) ).stateReg If(st._eq(stT.waitOnInput) & beginReq, # timeout is counting only when there is pending data # start new request req.vld(1), If(req.rd, If(endOfLenBlock, offset(0), remaining(ITEMS) ).Else( offset(offset + fitTo(buff.size, offset)), remaining(remaining - fitTo(buff.size, remaining)) ), sizeOfitems(sizeTmp), timeoutCntr(TIMEOUT_MAX) ) ).Else( req.vld(0), If(buff.dataOut.vld & st._eq(stT.waitOnInput) & (timeoutCntr != 0), timeoutCntr(timeoutCntr - 1) ) ) reqAck.rd(st._eq(stT.waitOnAck)) self.uploadedCntrHandler(st, reqAckHasCome, sizeOfitems) # it does not matter when lastWordCntr is changing when there is no # request startSendingData = st._eq(stT.waitOnInput) & beginReq & req.rd If(startSendingData, lastWordCntr(sizeTmp) ).Elif((lastWordCntr != 0) & w_ack, lastWordCntr(lastWordCntr - 1) ) buff.dataIn(self.items) w.data(buff.dataOut.data, fit=True) StreamNode( masters=[buff.dataOut], slaves=[w] ).sync(st._eq(stT.waitOnDataTx)) w.strb(mask(w.strb._dtype.bit_length())) w.last(w_last)
if __name__ == "__main__": from hwt.synthesizer.utils import to_rtl_str u = ArrayBuff_writer() u.TIMEOUT = 32 print(to_rtl_str(u))