#!/usr/bin/env python3
# -*- coding: utf-8 -
from hwt.bitmask import mask
from hwt.code import If, Concat, connect, FsmBuilder, log2ceil
from hwt.hdl.typeShortcuts import vec
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.serializer.mode import serializeParamsUniq
from hwt.synthesizer.unit import Unit
from hwt.synthesizer.param import Param
from hwtLib.amba.axiDatapumpIntf import AddrSizeHs, AxiWDatapumpIntf
from hwtLib.handshaked.fifo import HandshakedFifo
from hwtLib.handshaked.streamNode import StreamNode
from hwt.synthesizer.vectorUtils import fitTo
stT = HEnum("st_t", ["waitOnInput", "waitOnDataTx", "waitOnAck"])
[docs]@serializeParamsUniq
class ArrayBuff_writer(Unit):
"""
Collect items and send them over wDatapump when buffer is full or on timeout
Cyclically writes items into array over wDatapump
Maximum overlap of transactions is 1
[TODO] better fit of items on bus
[TODO] fully pipeline
items -> buff -> internal logic -> axi datapump
"""
[docs] def _config(self):
AddrSizeHs._config(self)
self.ID = Param(3)
self.MAX_LEN.set(16)
self.SIZE_WIDTH = Param(16)
self.BUFF_DEPTH = Param(16)
self.TIMEOUT = Param(1024)
self.ITEMS = Param(4096 // 8)
[docs] def _declr(self):
addClkRstn(self)
self.items = Handshaked()
self.items.DATA_WIDTH.set(self.SIZE_WIDTH)
with self._paramsShared():
self.wDatapump = AxiWDatapumpIntf()
self.uploaded = VectSignal(16)
self.baseAddr = RegCntrl()
self.baseAddr.DATA_WIDTH.set(self.ADDR_WIDTH)
self.buff_remain = VectSignal(16)
b = HandshakedFifo(Handshaked)
b.DATA_WIDTH.set(self.SIZE_WIDTH)
b.EXPORT_SIZE.set(True)
b.DEPTH.set(self.BUFF_DEPTH)
self.buff = b
[docs] def getControlInterfaces(self):
return (
self.baseAddr,
self.uploaded,
(Bits(16), None), # padding
self.buff_remain,
(Bits(16), None), # padding
)
[docs] def uploadedCntrHandler(self, st, reqAckHasCome, sizeOfitems):
uploadedCntr = self._reg(
"uploadedCntr", self.uploaded._dtype, defVal=0)
self.uploaded(uploadedCntr)
If(st._eq(stT.waitOnAck) & reqAckHasCome,
uploadedCntr(uploadedCntr + fitTo(sizeOfitems, uploadedCntr))
)
[docs] def _impl(self):
ALIGN_BITS = log2ceil(self.DATA_WIDTH // 8 - 1).val
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, vec(0, ALIGN_BITS)))
# offset in buffer and its complement
offset_t = Bits(log2ceil(ITEMS + 1), signed=False)
offset = self._reg("offset", offset_t, defVal=0)
remaining = self._reg("remaining", Bits(
log2ceil(ITEMS + 1), signed=False), defVal=ITEMS)
connect(remaining, self.buff_remain, fit=True)
addrTmp = self._sig("baseAddrTmp", baseAddr._dtype)
addrTmp(baseAddr + fitTo(offset, baseAddr))
# req values logic
req.id(self.ID)
req.addr(Concat(addrTmp, vec(0, ALIGN_BITS)))
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)
connect(buff.size, buffSize_tmp, fit=True)
endOfLenBlock = (remaining - 1) < buffSize_tmp
remainingAsLen = self._sig("remainingAsLen", remaining._dtype)
remainingAsLen(remaining - 1)
If(endOfLenBlock,
connect(remainingAsLen, req.len, fit=True),
connect(remaining, sizeTmp, fit=True)
).Else(
connect(buffSizeAsLen, req.len, 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),
defVal=TIMEOUT_MAX)
beginReq = buff.size._eq(self.BUFF_DEPTH) | timeoutCntr._eq(
0) # buffer is full or timeout
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)
connect(buff.dataOut.data, w.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 toRtl
u = ArrayBuff_writer()
u.TIMEOUT.set(32)
print(toRtl(u))