def _impl(self): c = self.c a = fitTo(self.a, c) b = fitTo(self.b, c) for dst in [c, self.d]: dst(a + b, fit=True)
def wReqDriver(self, en, baseIndex, lenByPtrs, inBlockRemain): s = self._sig wReq = self.wDatapump.req BURST_LEN = self.BUFFER_CAPACITY // 2 - 1 inBlockRemain_asPtrSize = fitTo(inBlockRemain, lenByPtrs) # wReq driver ringSpace_t = Bits(self.PTR_WIDTH) constraingLen = s("constraingSpace", ringSpace_t) If(inBlockRemain_asPtrSize < lenByPtrs, constraingLen(inBlockRemain_asPtrSize) ).Else( constraingLen(lenByPtrs) ) reqLen = s("reqLen", wReq.len._dtype) If(constraingLen > BURST_LEN, reqLen(BURST_LEN) ).Else( connect(constraingLen, reqLen, fit=True) ) wReq.id(self.ID) wReq.addr(self.indexToAddr(baseIndex)) wReq.rem(0) wReq.len(reqLen) wReq.vld(en) return reqLen
def _connectToIter(self, master, exclude=None, fit=False): # [todo] implementatino for RtlSignals of HStruct type if exclude and (self in exclude or master in exclude): return if self._interfaces: for ifc in self._interfaces: if exclude and ifc in exclude: continue mIfc = getattr(master, ifc._name) if exclude and mIfc in exclude: continue if mIfc._masterDir == DIRECTION.OUT: if ifc._masterDir != mIfc._masterDir: raise IntfLvlConfErr("Invalid connection", ifc, "<=", mIfc) yield from ifc._connectTo(mIfc, exclude=exclude, fit=fit) else: if ifc._masterDir != mIfc._masterDir: raise IntfLvlConfErr("Invalid connection", mIfc, "<=", ifc) yield from mIfc._connectTo(ifc, exclude=exclude, fit=fit) else: dstSig = toHVal(self) srcSig = toHVal(master) if fit: srcSig = fitTo(srcSig, dstSig) yield dstSig(srcSig)
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)) )
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 _connectToIter(self, master, exclude, fit): # [todo] implementation for RtlSignals of HStruct type if exclude and (self in exclude or master in exclude): return if self._interfaces: seen_master_intfs = [] for ifc in self._interfaces: if exclude and ifc in exclude: mIfc = getattr(master, ifc._name, None) if mIfc is not None: seen_master_intfs.append(mIfc) continue mIfc = getattr(master, ifc._name) seen_master_intfs.append(mIfc) if exclude and mIfc in exclude: continue if mIfc._masterDir == DIRECTION.OUT: if ifc._masterDir != mIfc._masterDir: raise IntfLvlConfErr("Invalid connection", ifc, "<=", mIfc) yield from ifc._connectToIter(mIfc, exclude, fit) else: if ifc._masterDir != mIfc._masterDir: raise IntfLvlConfErr("Invalid connection", mIfc, "<=", ifc) yield from mIfc._connectToIter(ifc, exclude, fit) if len(seen_master_intfs) != len(master._interfaces): if exclude: # there is a possiblity that the master interface was excluded, # but we did not see it as the interface of the same name was not present on self for ifc in self._interfaces: if ifc in exclude or ifc not in seen_master_intfs: continue else: # ifc is an interface which is extra on master and is missing an equivalent on slave raise InterfaceStructureErr(self, master, exclude) else: raise InterfaceStructureErr(self, master, exclude) else: if master._interfaces: raise InterfaceStructureErr(self, master, exclude) dstSig = toHVal(self) srcSig = toHVal(master) if fit: srcSig = fitTo(srcSig, dstSig) yield dstSig(srcSig)
def _impl(self): propagateClkRstn(self) ITEM_WIDTH = int(self.ITEM_WIDTH) DATA_WIDTH = int(self.DATA_WIDTH) ITEMS_IN_DATA_WORD = self.ITEMS_IN_DATA_WORD ITEM_SIZE_IN_WORDS = 1 if ITEM_WIDTH % 8 != 0 or ITEM_SIZE_IN_WORDS * DATA_WIDTH != ITEMS_IN_DATA_WORD * ITEM_WIDTH: raise NotImplementedError(ITEM_WIDTH) req = self.rDatapump.req req.id(self.ID) req.len(ITEM_SIZE_IN_WORDS - 1) req.rem(0) if ITEMS_IN_DATA_WORD == 1: addr = Concat(self.index.data, vec(0, log2ceil(ITEM_WIDTH // 8))) req.addr(self.base + fitTo(addr, req.addr)) StreamNode(masters=[self.index], slaves=[req]).sync() self.item.data(self.rDatapump.r.data) StreamNode(masters=[self.rDatapump.r], slaves=[self.item]).sync() else: r = self.rDatapump.r.data f = self.itemSubIndexFifo subIndexBits = f.dataIn.data._dtype.bit_length() itemAlignBits = log2ceil(ITEM_WIDTH // 8) addr = Concat(self.index.data[:subIndexBits], vec(0, itemAlignBits + subIndexBits)) req.addr(self.base + fitTo(addr, req.addr)) f.dataIn.data(self.index.data[subIndexBits:]) StreamNode(masters=[self.index], slaves=[req, f.dataIn]).sync() Switch(f.dataOut.data).addCases([ (ITEMS_IN_DATA_WORD - i - 1, self.item.data(r[(ITEM_WIDTH * (i + 1)): (ITEM_WIDTH * i)])) for i in range(ITEMS_IN_DATA_WORD) ]) StreamNode(masters=[self.rDatapump.r, f.dataOut], slaves=[self.item]).sync()
def _impl(self): propagateClkRstn(self) ITEM_WIDTH = int(self.ITEM_WIDTH) DATA_WIDTH = int(self.DATA_WIDTH) ITEMS_IN_DATA_WORD = self.ITEMS_IN_DATA_WORD ITEM_SIZE_IN_WORDS = 1 if ITEM_WIDTH % 8 != 0 or ITEM_SIZE_IN_WORDS * DATA_WIDTH != ITEMS_IN_DATA_WORD * ITEM_WIDTH: raise NotImplementedError(ITEM_WIDTH) req = self.rDatapump.req req.id(self.ID) req.len(ITEM_SIZE_IN_WORDS - 1) req.rem(0) if ITEMS_IN_DATA_WORD == 1: addr = Concat(self.index.data, vec(0, log2ceil(ITEM_WIDTH // 8))) req.addr(self.base + fitTo(addr, req.addr)) StreamNode(masters=[self.index], slaves=[req]).sync() self.item.data(self.rDatapump.r.data) StreamNode(masters=[self.rDatapump.r], slaves=[self.item]).sync() else: r = self.rDatapump.r.data f = self.itemSubIndexFifo subIndexBits = f.dataIn.data._dtype.bit_length() itemAlignBits = log2ceil(ITEM_WIDTH // 8) addr = Concat(self.index.data[:subIndexBits], vec(0, itemAlignBits + subIndexBits)) req.addr(self.base + fitTo(addr, req.addr)) f.dataIn.data(self.index.data[subIndexBits:]) StreamNode(masters=[self.index], slaves=[req, f.dataIn]).sync() Switch(f.dataOut.data).addCases([ (ITEMS_IN_DATA_WORD - i - 1, self.item.data(r[(ITEM_WIDTH * (i + 1)):(ITEM_WIDTH * i)])) for i in range(ITEMS_IN_DATA_WORD) ]) StreamNode(masters=[self.rDatapump.r, f.dataOut], slaves=[self.item]).sync()
def _connectToIter(self, master, masterIndex=None, slaveIndex=None, exclude=None, fit=False): if exclude and (self in exclude or master in exclude): return if self._interfaces: for ifc in self._interfaces: if exclude and ifc in exclude: continue mIfc = getattr(master, ifc._name) if exclude and mIfc in exclude: continue if mIfc._masterDir == DIRECTION.OUT: if ifc._masterDir != mIfc._masterDir: raise IntfLvlConfErr("Invalid connection %s <= %s" % (repr(ifc), repr(mIfc))) yield from ifc._connectTo(mIfc, masterIndex=masterIndex, slaveIndex=slaveIndex, exclude=exclude, fit=fit) else: if ifc._masterDir != mIfc._masterDir: raise IntfLvlConfErr("Invalid connection %s <= %s" % (repr(mIfc), repr(ifc))) yield from mIfc._connectTo(ifc, masterIndex=slaveIndex, slaveIndex=masterIndex, exclude=exclude, fit=fit) else: dstSig = toHVal(self) srcSig = toHVal(master) if masterIndex is not None: srcSig = aplyIndexOnSignal(srcSig, dstSig._dtype, masterIndex) if slaveIndex is not None: dstSig = aplyIndexOnSignal(dstSig, srcSig._dtype, slaveIndex) if fit: srcSig = fitTo(srcSig, dstSig) yield dstSig.__pow__(srcSig)
def mvDataToW(self, prepareEn, dataMoveEn, reqLen, inBlockRemain, nextBlockTransition_out, dataCntr_out): f = self.dataFifo.dataOut w = self.wDatapump.w nextBlockTransition = self._sig("mvDataToW_nextBlockTransition") nextBlockTransition(inBlockRemain <= fitTo(reqLen, inBlockRemain) + 1) If( prepareEn, dataCntr_out(fitTo(reqLen, dataCntr_out)), If(nextBlockTransition_out, inBlockRemain(self.ITEMS_IN_BLOCK)).Else( inBlockRemain(inBlockRemain - (fitTo(reqLen, inBlockRemain) + 1)))).Elif( dataMoveEn, If( StreamNode(masters=[f], slaves=[w]).ack(), dataCntr_out(dataCntr_out - 1))) StreamNode(masters=[f], slaves=[w]).sync(dataMoveEn) w.data(f.data) w.last(dataCntr_out._eq(0)) w.strb(mask(w.strb._dtype.bit_length())) self.dataFifo.dataIn(self.dataIn) nextBlockTransition_out(nextBlockTransition & prepareEn)
def mvDataToW(self, prepareEn, dataMoveEn, reqLen, inBlockRemain, nextBlockTransition_out, dataCntr_out): f = self.dataFifo.dataOut w = self.wDatapump.w nextBlockTransition = self._sig("mvDataToW_nextBlockTransition") nextBlockTransition(inBlockRemain <= fitTo(reqLen, inBlockRemain) + 1) If(prepareEn, dataCntr_out(fitTo(reqLen, dataCntr_out)), If(nextBlockTransition_out, inBlockRemain(self.ITEMS_IN_BLOCK) ).Else( inBlockRemain(inBlockRemain - (fitTo(reqLen, inBlockRemain) + 1)) ) ).Elif(dataMoveEn, If(StreamNode(masters=[f], slaves=[w]).ack(), dataCntr_out(dataCntr_out - 1) ) ) StreamNode(masters=[f], slaves=[w]).sync(dataMoveEn) w.data(f.data) w.last(dataCntr_out._eq(0)) w.strb(mask(w.strb._dtype.bit_length())) self.dataFifo.dataIn(self.dataIn) nextBlockTransition_out(nextBlockTransition & prepareEn)
def baseAddrLogic(self, nextBlockTransition_in): """ Logic for downloading address of next block :param nextBlockTransition_in: signal which means that baseIndex should be changed to nextBaseIndex if nextBaseAddrReady is not high this signal has no effect (= regular handshake) :return: (baseIndex, nextBaseIndex, nextBaseReady is ready and nextBlockTransition_in can be used) """ r = self._reg rIn = self.rDatapump.r rReq = self.rDatapump.req addr_index_t = Bits(self.ADDR_WIDTH - self.ALIGN_BITS) baseIndex = r("baseIndex_backup", addr_index_t) nextBaseIndex = r("nextBaseIndex", addr_index_t) t = HEnum("nextBaseFsm_t", ["uninitialized", "required", "pending", "prepared"]) isNextBaseAddr = rIn.valid & rIn.id._eq(self.ID) nextBaseFsm = FsmBuilder(self, t, "baseAddrLogic_fsm")\ .Trans(t.uninitialized, (self.baseAddr.dout.vld, t.required) ).Trans(t.required, (rReq.rd, t.pending) ).Trans(t.pending, (isNextBaseAddr, t.prepared) ).Trans(t.prepared, (nextBlockTransition_in, t.required) ).stateReg If(self.baseAddr.dout.vld, baseIndex(self.addrToIndex(self.baseAddr.dout.data)), ).Elif(nextBlockTransition_in, baseIndex(nextBaseIndex) ) self.baseAddr.din(self.indexToAddr(baseIndex)) If(isNextBaseAddr, nextBaseIndex(self.addrToIndex(fitTo(rIn.data, rReq.addr))) ) rIn.ready(1) self.rReqHandler(baseIndex, nextBaseFsm._eq(t.required)) nextBaseReady = nextBaseFsm._eq(t.prepared) return baseIndex, nextBaseIndex, nextBaseReady
def baseAddrLogic(self, nextBlockTransition_in): """ Logic for downloading address of next block :param nextBlockTransition_in: signal which means that baseIndex should be changed to nextBaseIndex if nextBaseAddrReady is not high this signal has no effect (= regular handshake) :return: (baseIndex, nextBaseIndex, nextBaseReady is ready and nextBlockTransition_in can be used) """ r = self._reg rIn = self.rDatapump.r rReq = self.rDatapump.req addr_index_t = Bits(self.ADDR_WIDTH - self.ALIGN_BITS) baseIndex = r("baseIndex_backup", addr_index_t) nextBaseIndex = r("nextBaseIndex", addr_index_t) t = HEnum("nextBaseFsm_t", ["uninitialized", "required", "pending", "prepared"]) isNextBaseAddr = rIn.valid & rIn.id._eq(self.ID) nextBaseFsm = FsmBuilder(self, t, "baseAddrLogic_fsm")\ .Trans(t.uninitialized, (self.baseAddr.dout.vld, t.required) ).Trans(t.required, (rReq.rd, t.pending) ).Trans(t.pending, (isNextBaseAddr, t.prepared) ).Trans(t.prepared, (nextBlockTransition_in, t.required) ).stateReg If( self.baseAddr.dout.vld, baseIndex(self.addrToIndex(self.baseAddr.dout.data)), ).Elif(nextBlockTransition_in, baseIndex(nextBaseIndex)) self.baseAddr.din(self.indexToAddr(baseIndex)) If(isNextBaseAddr, nextBaseIndex(self.addrToIndex(fitTo(rIn.data, rReq.addr)))) rIn.ready(1) self.rReqHandler(baseIndex, nextBaseFsm._eq(t.required)) nextBaseReady = nextBaseFsm._eq(t.prepared) return baseIndex, nextBaseIndex, nextBaseReady
def _connect(src, dst, exclude, fit): if isinstance(src, InterfaceBase): if isinstance(dst, InterfaceBase): return dst._connectTo(src, exclude=exclude, fit=fit) src = src._sig assert not exclude, "this intf. is just a signal" if src is None: src = dst._dtype.fromPy(None) else: src = toHVal(src) if fit: src = fitTo(src, dst) src = src._auto_cast(dst._dtype) return dst(src)
def _connect(src, dst, exclude, fit): # [TODO]: support for RtlSignals of struct type + interface with same signal structure if isinstance(src, InterfaceBase): if isinstance(dst, InterfaceBase): return dst._connectTo(src, exclude=exclude, fit=fit) assert not exclude, ("dst does not contain subinterfaces," " excluded should be already processed in this state", src, dst, exclude) if src is None: src = dst._dtype.from_py(None) else: src = toHVal(src) if fit: src = fitTo(src, dst) src = src._auto_cast(dst._dtype) return dst(src)
def axiWAddrHandler(self, st, baseAddr, actualAddr, lenRem): """ AXI write addr logic """ axi = self.axi st_t = st._dtype axi.aw.valid(st._eq(st_t.writeAddr)) axi.aw.addr(actualAddr) axi.aw.id(0) axi.aw.burst(BURST_INCR) axi.aw.cache(CACHE_DEFAULT) If(lenRem > self.MAX_BUTST_LEN, axi.aw.len(self.MAX_BUTST_LEN - 1) ).Else( connect(lenRem - 1, axi.aw.len, fit=True) ) axi.aw.lock(LOCK_DEFAULT) axi.aw.prot(PROT_DEFAULT) axi.aw.size(BYTES_IN_TRANS(self.DATA_WIDTH // 8)) axi.aw.qos(QOS_DEFAULT) # lenRem, actualAddr logic Switch(st)\ .Case(st_t.fullIdle, lenRem(self.DATA_LEN), actualAddr(baseAddr) ).Case(st_t.writeAddr, If(axi.aw.ready, If(lenRem > self.MAX_BUTST_LEN, actualAddr(actualAddr + (self.MAX_BUTST_LEN * self.DATA_WIDTH // 8)), lenRem(lenRem - self.MAX_BUTST_LEN) ).Else( actualAddr(actualAddr + fitTo(lenRem, actualAddr)), lenRem(0) ) ) )
def axiWAddrHandler(self, st, baseAddr, actualAddr, lenRem): """ AXI write addr logic """ axi = self.axi st_t = st._dtype axi.aw.valid(st._eq(st_t.writeAddr)) axi.aw.addr(actualAddr) axi.aw.id(0) axi.aw.burst(BURST_INCR) axi.aw.cache(CACHE_DEFAULT) If(lenRem > self.MAX_BUTST_LEN, axi.aw.len(self.MAX_BUTST_LEN - 1) ).Else( axi.aw.len(lenRem - 1, fit=True) ) axi.aw.lock(LOCK_DEFAULT) axi.aw.prot(PROT_DEFAULT) axi.aw.size(BYTES_IN_TRANS(self.DATA_WIDTH // 8)) axi.aw.qos(QOS_DEFAULT) # lenRem, actualAddr logic Switch(st)\ .Case(st_t.fullIdle, lenRem(self.DATA_LEN), actualAddr(baseAddr) ).Case(st_t.writeAddr, If(axi.aw.ready, If(lenRem > self.MAX_BUTST_LEN, actualAddr(actualAddr + (self.MAX_BUTST_LEN * self.DATA_WIDTH // 8)), lenRem(lenRem - self.MAX_BUTST_LEN) ).Else( actualAddr(actualAddr + fitTo(lenRem, actualAddr)), lenRem(0) ) ) )
def create_char_mux(self, in_, out_, char_i, digits, bits_per_digit): """ Create a MUX which select the degit from input vector. Also perform necessary type casting for corner cases. """ in_w = in_._dtype.bit_length() Switch(char_i)\ .add_cases([ (digits - i - 1, out_( fitTo( in_[min((i + 1) * bits_per_digit, in_w): i * bits_per_digit], out_, # moast significant digits may overlap the input size extend=True, ) ) if i * bits_per_digit < in_w else # case where there are more digits than in the input domain out_(0) ) for i in range(digits)])\ .Default(out_(None))
def wReqDriver(self, en, baseIndex, lenByPtrs, inBlockRemain): s = self._sig wReq = self.wDatapump.req BURST_LEN = self.BUFFER_CAPACITY // 2 - 1 inBlockRemain_asPtrSize = fitTo(inBlockRemain, lenByPtrs) # wReq driver ringSpace_t = Bits(self.PTR_WIDTH) constraingLen = s("constraingSpace", ringSpace_t) If(inBlockRemain_asPtrSize < lenByPtrs, constraingLen(inBlockRemain_asPtrSize)).Else( constraingLen(lenByPtrs)) reqLen = s("reqLen", wReq.len._dtype) If(constraingLen > BURST_LEN, reqLen(BURST_LEN)).Else(reqLen(constraingLen, fit=True)) wReq.id(self.ID) wReq.addr(self.indexToAddr(baseIndex)) wReq.rem(0) wReq.len(reqLen) wReq.vld(en) return reqLen
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)
def itemUploadLogic(self, baseIndex, nextBaseIndex, nextBaseReady, nextBlockTransition_out): r, s = self._reg, self._sig f = self.dataFifo w = self.wDatapump BURST_LEN = self.BUFFER_CAPACITY // 2 bufferHasData = s("bufferHasData") bufferHasData(f.size > (BURST_LEN - 1)) # we are counting base next addr as item as well addr_index_t = Bits(self.ADDR_WIDTH - self.ALIGN_BITS) baseIndex = r("baseIndex", addr_index_t) dataCntr_t = Bits(log2ceil(BURST_LEN + 1), signed=False) # counter of uploading data dataCntr = r("dataCntr", dataCntr_t, def_val=0) reqLen_backup = r("reqLen_backup", w.req.len._dtype, def_val=0) gotWriteAck = w.ack.vld & w.ack.data._eq(self.ID) queueHasSpace, lenByPtrs = self.queuePtrLogic( fitTo(reqLen_backup, self.wrPtr.din) + 1, gotWriteAck) timeout = s("timeout") fsm_t = HEnum("itemUploadingFsm_t", [ "idle", "reqPending", "dataPending_prepare", "dataPending_send", "waitForAck" ]) fsm = FsmBuilder(self, fsm_t, "itemUploadLogic_fsm")\ .Trans(fsm_t.idle, (timeout | (bufferHasData & queueHasSpace), fsm_t.reqPending) ).Trans(fsm_t.reqPending, (w.req.rd, fsm_t.dataPending_prepare) ).Trans(fsm_t.dataPending_prepare, fsm_t.dataPending_send ).Trans(fsm_t.dataPending_send, ((~nextBlockTransition_out | nextBaseReady) & dataCntr._eq(0), fsm_t.waitForAck) ).Trans(fsm_t.waitForAck, (gotWriteAck, fsm_t.idle) ).stateReg timeout( self.timeoutHandler(fsm != fsm_t.idle, (f.size != 0) & queueHasSpace)) inBlock_t = Bits(log2ceil(self.ITEMS_IN_BLOCK + 1)) inBlockRemain = r("inBlockRemain_reg", inBlock_t, def_val=self.ITEMS_IN_BLOCK) wReqEn = fsm._eq(fsm_t.reqPending) reqLen = self.wReqDriver(wReqEn, baseIndex, lenByPtrs, inBlockRemain) If(wReqEn & w.req.rd, reqLen_backup(reqLen)) dataMoveEn = fsm._eq(fsm_t.dataPending_send) prepareEn = fsm._eq(fsm_t.dataPending_prepare) self.mvDataToW(prepareEn, dataMoveEn, reqLen_backup, inBlockRemain, nextBlockTransition_out, dataCntr) If( self.baseAddr.dout.vld, baseIndex(self.addrToIndex(self.baseAddr.dout.data)), ).Elif(prepareEn, baseIndex(baseIndex + fitTo(reqLen_backup, baseIndex) + 1)).Elif(nextBlockTransition_out, baseIndex(nextBaseIndex)) w.ack.rd(fsm._eq(fsm_t.waitForAck))
def _impl(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 = Bits(log2ceil(self.ITEMS_IN_BLOCK + 1)) ringSpace_t = Bits(self.PTR_WIDTH) downloadPending = r("downloadPending", defVal=0) baseIndex = r("baseIndex", Bits(self.ADDR_WIDTH - ALIGN_BITS)) inBlockRemain = r("inBlockRemain_reg", inBlock_t, defVal=self.ITEMS_IN_BLOCK) self.inBlockRemain(inBlockRemain) # Logic of tail/head rdPtr = r("rdPtr", ringSpace_t, defVal=0) wrPtr = r("wrPtr", ringSpace_t, defVal=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, vec(0, ALIGN_BITS)) 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), connect(constraingSpace, req.len, fit=True), If(doReq, inBlockRemain(self.ITEMS_IN_BLOCK) ) ).Else( # download data leftover req.id(ID), connect(constraingSpace - 1, req.len, 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()
def _impl(self) -> None: avalon: AvalonMM = self.m axi = self.s addr_tmp = self._reg( "addr_tmp", HStruct( (axi.ar.addr._dtype, "addr"), (axi.ar.id._dtype, "id"), (axi.ar.len._dtype, "len"), (BIT, "vld"), (BIT, "is_w"), ), def_val={"vld": 0} ) r_data_ack = self.connect_r_fifo(avalon, axi) # contains the available space in read data fifo # used to prevent the dispatch of the reads which would # cause overflow of read data fifo r_data_fifo_capacity = self._reg( "r_data_fifo_capacity", Bits(log2ceil(self.R_DATA_FIFO_DEPTH + 1)), def_val=self.R_DATA_FIFO_DEPTH) will_be_idle = ~addr_tmp.vld | \ ~addr_tmp.is_w | \ (addr_tmp.vld & addr_tmp.is_w & ~avalon.waitRequest & addr_tmp.len._eq(0) & axi.w.valid & axi.b.ready) will_be_idle = rename_signal(self, will_be_idle, "will_be_idle") r_size_in = self.r_size_fifo.dataIn ar_en = will_be_idle & \ axi.ar.valid & (r_data_fifo_capacity > fitTo(axi.ar.len, r_data_fifo_capacity)) & \ r_size_in.rd is_w = addr_tmp.vld & addr_tmp.is_w ready_for_addr = ~addr_tmp.vld | ~avalon.waitRequest addr_tmp_ack = rename_signal(self, ~avalon.waitRequest & ~( is_w & ~(axi.w.valid & ((addr_tmp.len != 0) | axi.b.ready)) ), "addr_tmp_ack") aw_en = ready_for_addr & axi.w.valid & (~addr_tmp.vld | ~addr_tmp.is_w | (addr_tmp.len != 0) | axi.b.ready) is_not_last_w = rename_signal(self, is_w & (addr_tmp.len != 0), "is_not_last_w") if self.RW_PRIORITY == READ: aw_en = ~axi.ar.valid & aw_en ar_en = rename_signal(self, ar_en, "ar_en") aw_en = rename_signal(self, aw_en, "aw_en") If(ar_en, # start new read transaction self.load_addr_tmp(addr_tmp, axi.ar) ).Elif(~avalon.waitRequest & is_not_last_w & axi.w.valid, # finishing write transaction (except last word) addr_tmp.len(addr_tmp.len - 1), ).Elif(will_be_idle & axi.aw.valid & axi.w.valid, # start new write transaction self.load_addr_tmp(addr_tmp, axi.aw) ).Elif(addr_tmp_ack, # all transaction finished, clear addr_tmp register self.load_addr_tmp(addr_tmp, None) ) else: ar_en = ar_en & \ ~(is_not_last_w) & \ ~(axi.aw.valid & axi.w.valid) ar_en = rename_signal(self, ar_en, "ar_en") aw_en = rename_signal(self, aw_en, "aw_en") If(~avalon.waitRequest & is_not_last_w & axi.w.valid, # finishing write transaction (except last word) addr_tmp.len(addr_tmp.len - 1), ).Elif(will_be_idle & axi.aw.valid & axi.w.valid, # start new write transaction self.load_addr_tmp(addr_tmp, axi.aw) ).Elif(ar_en, # start new read transaction self.load_addr_tmp(addr_tmp, axi.ar) ).Elif(addr_tmp_ack, # all transaction finished, clear addr_tmp register self.load_addr_tmp(addr_tmp, None) ) r_size_in.id(axi.ar.id) r_size_in.len(axi.ar.len) r_size_in.vld(ar_en) If(r_data_ack & ar_en, r_data_fifo_capacity(r_data_fifo_capacity - fitTo(axi.ar.len, r_data_fifo_capacity)) ).Elif(r_data_ack, r_data_fifo_capacity(r_data_fifo_capacity + 1) ).Elif(ar_en, r_data_fifo_capacity(r_data_fifo_capacity - 1 - fitTo(axi.ar.len, r_data_fifo_capacity)) ) axi.aw.ready(will_be_idle & aw_en) axi.ar.ready(will_be_idle & ar_en) avalon.address(addr_tmp.addr) avalon.burstCount(fitTo(addr_tmp.len, avalon.burstCount) + 1) avalon.read(addr_tmp.vld & ~addr_tmp.is_w) avalon.write(is_w & axi.w.valid & ((addr_tmp.len != 0) | axi.b.ready)) avalon.writeData(axi.w.data) avalon.byteEnable(axi.w.strb) axi.w.ready( addr_tmp.vld & addr_tmp.is_w & ~avalon.waitRequest & ((addr_tmp.len != 0) | axi.b.ready) ) axi.b.id(addr_tmp.id) axi.b.resp(RESP_OKAY) axi.b.valid(addr_tmp.vld & addr_tmp.is_w & ~avalon.waitRequest & axi.w.ready & axi.w.valid & axi.w.last) propagateClkRstn(self)
def addrHandler(self, req: AddrSizeHs, axiA: Union[Axi3_addr, Axi3Lite_addr, Axi4_addr, Axi4Lite_addr], transInfo: HandshakeSync, errFlag: RtlSignal): """ Propagate read/write requests from req to axi address channel and store extra info using transInfo interface. """ r, s = self._reg, self._sig self.axiAddrDefaults(axiA) if self.ID_WIDTH: axiA.id(self.ID_VAL) alignmentError = self.hasAlignmentError(req.addr) HAS_LEN = self._axiCls.LEN_WIDTH > 0 if self.useTransSplitting(): # if axi len is smaller we have to use transaction splitting # that means we need to split requests from driver.req to multiple axi requests transPartPending = r("transPartPending", def_val=0) addrTmp = r("addrTmp", req.addr._dtype) dispatchNode = StreamNode([ req, ], [axiA, transInfo], skipWhen={req: transPartPending.next}, extraConds={ req: ~transPartPending.next, axiA: req.vld & ~alignmentError, transInfo: req.vld & ~alignmentError }) dispatchNode.sync(~errFlag) ack = s("ar_ack") ack(dispatchNode.ack() & ~errFlag) LEN_MAX = max(mask(self._axiCls.LEN_WIDTH), 0) reqLen = s("reqLen", self.getLen_t()) reqLenRemaining = r("reqLenRemaining", reqLen._dtype) If(reqLen > LEN_MAX, *([axiA.len(LEN_MAX)] if HAS_LEN else []), self.storeTransInfo(transInfo, 0)).Else( # connect only lower bits of len *([axiA.len(reqLen, fit=True)] if HAS_LEN else []), self.storeTransInfo(transInfo, 1)) # dispatchNode not used because of combinational loop If( StreamNode([ req, ], [axiA, transInfo]).ack() & ~errFlag, If(reqLen > LEN_MAX, reqLenRemaining(reqLen - (LEN_MAX + 1)), transPartPending(1)).Else(transPartPending(0))) reqLenSwitch = If( ~req.vld, reqLen(None), ) if not self.isAlwaysAligned(): crossesWordBoundary = self.isCrossingWordBoundary( req.addr, req.rem) reqLenSwitch.Elif( ~self.addrIsAligned(req.addr) & crossesWordBoundary, reqLen(fitTo(req.len, reqLen, shrink=False) + 1), ) reqLenSwitch.Else(reqLen(fitTo(req.len, reqLen, shrink=False)), ) ADDR_STEP = self.getBurstAddrOffset() If( transPartPending, axiA.addr(self.addrAlign(addrTmp)), If(ack, addrTmp(addrTmp + ADDR_STEP)), reqLen(reqLenRemaining), ).Else( axiA.addr(self.addrAlign(req.addr)), addrTmp(req.addr + ADDR_STEP), reqLenSwitch, ) else: # if axi len is wider we can directly translate requests to axi axiA.addr(self.addrAlign(req.addr)) if req.MAX_LEN > 0: lenDrive = axiA.len(fitTo(req.len, axiA.len, shrink=False)) if not self.isAlwaysAligned(): crossesWordBoundary = self.isCrossingWordBoundary( req.addr, req.rem) If( ~self.addrIsAligned(req.addr) & crossesWordBoundary, axiA.len(fitTo(req.len, axiA.len) + 1), ).Else(lenDrive, ) else: if HAS_LEN: axiA.len(0) self.storeTransInfo(transInfo, 1) StreamNode(masters=[req], slaves=[axiA, transInfo], extraConds={ axiA: ~alignmentError, transInfo: ~alignmentError, }).sync(~errFlag)
def itemUploadLogic(self, baseIndex, nextBaseIndex, nextBaseReady, nextBlockTransition_out): r, s = self._reg, self._sig f = self.dataFifo w = self.wDatapump BURST_LEN = self.BUFFER_CAPACITY // 2 bufferHasData = s("bufferHasData") bufferHasData(f.size > (BURST_LEN - 1)) # we are counting base next addr as item as well addr_index_t = Bits(self.ADDR_WIDTH - self.ALIGN_BITS) baseIndex = r("baseIndex", addr_index_t) dataCntr_t = Bits(log2ceil(BURST_LEN + 1), signed=False) dataCntr = r("dataCntr", dataCntr_t, defVal=0) # counter of uploading data reqLen_backup = r("reqLen_backup", w.req.len._dtype, defVal=0) gotWriteAck = w.ack.vld & w.ack.data._eq(self.ID) queueHasSpace, lenByPtrs = self.queuePtrLogic(fitTo(reqLen_backup, self.wrPtr.din) + 1, gotWriteAck) timeout = s("timeout") fsm_t = HEnum("itemUploadingFsm_t", ["idle", "reqPending", "dataPending_prepare", "dataPending_send", "waitForAck"]) fsm = FsmBuilder(self, fsm_t, "itemUploadLogic_fsm")\ .Trans(fsm_t.idle, (timeout | (bufferHasData & queueHasSpace), fsm_t.reqPending) ).Trans(fsm_t.reqPending, (w.req.rd, fsm_t.dataPending_prepare) ).Trans(fsm_t.dataPending_prepare, fsm_t.dataPending_send ).Trans(fsm_t.dataPending_send, ((~nextBlockTransition_out | nextBaseReady) & dataCntr._eq(0), fsm_t.waitForAck) ).Trans(fsm_t.waitForAck, (gotWriteAck, fsm_t.idle) ).stateReg timeout(self.timeoutHandler(fsm != fsm_t.idle, (f.size != 0) & queueHasSpace)) inBlock_t = Bits(log2ceil(self.ITEMS_IN_BLOCK + 1)) inBlockRemain = r("inBlockRemain_reg", inBlock_t, defVal=self.ITEMS_IN_BLOCK) wReqEn = fsm._eq(fsm_t.reqPending) reqLen = self.wReqDriver(wReqEn, baseIndex, lenByPtrs, inBlockRemain) If(wReqEn & w.req.rd, reqLen_backup(reqLen) ) dataMoveEn = fsm._eq(fsm_t.dataPending_send) prepareEn = fsm._eq(fsm_t.dataPending_prepare) self.mvDataToW(prepareEn, dataMoveEn, reqLen_backup, inBlockRemain, nextBlockTransition_out, dataCntr) If(self.baseAddr.dout.vld, baseIndex(self.addrToIndex(self.baseAddr.dout.data)), ).Elif(prepareEn, baseIndex(baseIndex + fitTo(reqLen_backup, baseIndex) + 1) ).Elif(nextBlockTransition_out, baseIndex(nextBaseIndex) ) w.ack.rd(fsm._eq(fsm_t.waitForAck))
def connect_single_format_group(self, f_i: int, f: Union[str, AxiS_strFormatItem], strings_offset_and_size: Dict[Union[int, str], Tuple[int, int]], string_rom: RtlSignal, char_i: RtlSignal, to_bcd_inputs: List[Tuple[RtlSignal, HdlStatement]], en: RtlSignal): """ Connect a single formating group or string chunk to output. Depending on item type this may involve: * iterate the string characters stored in string_rom * iterate and translate bin/oct/hex characters * register bcd input for later connection * connect an input string from an input AxiStream """ dout = self.data_out in_vld = BIT.from_py(1) res = [] in_last = None string_rom_index_t = Bits(log2ceil(string_rom._dtype.size), signed=False) if isinstance(f, str): str_offset, str_size = strings_offset_and_size[f_i] str_offset = string_rom_index_t.from_py(str_offset) res.append( # read char of the string from string_rom dout.data(string_rom[str_offset + fitTo(char_i, str_offset, shrink=False)])) else: assert isinstance(f, AxiS_strFormatItem), f in_ = self.data_in._fieldsToInterfaces[f.member_path] if f.format_type in ('d', 'b', 'o', 'x', 'X'): if f.format_type == 'd': # first use BCD convertor to convert to BCD to_bcd_inputs.append((en, in_)) bcd = self.bin_to_bcd.dout in_vld = bcd.vld bcd.rd(dout.ready & en & char_i._eq(f.digits - 1)) in_ = bcd.data bits_per_char = AxiS_strFormatItem.BITS_PER_CHAR[f.format_type] actual_digit = self._sig(f"f_{f_i}_actual_digit", Bits(bits_per_char)) to_str_table_offset, _ = strings_offset_and_size[f.format_type] to_str_table_offset = string_rom_index_t.from_py( to_str_table_offset) # iterate output digits using char_i self.create_char_mux(in_, actual_digit, char_i, f.digits, bits_per_char) res.append( # use char translation table from string_rom to translate digit in to a char dout.data(string_rom[ to_str_table_offset + fitTo(actual_digit, to_str_table_offset, shrink=False)] )) str_size = f.digits else: # connect a string from an input AxiStream assert f.format_type == 's', f.format_type assert in_.DATA_WIDTH == 8, in_.DATA_WIDTH assert in_.USE_STRB == False, in_.USE_STRB assert in_.USE_KEEP == False, in_.USE_KEEP in_vld = in_.valid in_.ready(dout.ready & en) res.append(dout.data(in_.data)) in_last = in_.last if in_last is None: # if signal to detect last character is not overriden use conter to resolve it in_last = char_i._eq(str_size - 1) return res, in_vld, in_last,
def _impl(self): c = self.c a = fitTo(self.a, c) b = fitTo(self.b, c) connect(a + b, c, self.d, fit=True)
def _impl(self) -> None: _string_rom, strings_offset_and_size, max_chars_per_format, max_bcd_digits = self.build_string_rom( ) if self.DATA_WIDTH != 8: # it self.DATA_WIDTH != 1B we need to handle all possible alignments and shifts, precompute some strings # because number of string memory ports is limited etc. raise NotImplementedError() # instanciate bin_to_bcd if required if max_bcd_digits > 0: bin_to_bcd = BinToBcd() bin_to_bcd.INPUT_WIDTH = log2ceil(10**max_bcd_digits - 1) self.bin_to_bcd = bin_to_bcd # tuples (cond, input) to_bcd_inputs = [] string_rom = self._sig("string_rom", Bits(8)[len(_string_rom)], def_val=[int(c) for c in _string_rom]) char_i = self._reg("char_i", Bits(log2ceil(max_chars_per_format), signed=False), def_val=0) # create an iterator over all characters element_cnt = len(self.FORMAT) dout = self.data_out if element_cnt == 1: en = 1 f_i = 0 f = self.FORMAT[f_i] _, out_vld, out_last = self.connect_single_format_group( f_i, f, strings_offset_and_size, string_rom, char_i, to_bcd_inputs, en) char_i_rst = out_last else: main_st = self._reg("main_st", Bits(log2ceil(element_cnt), signed=False), def_val=0) char_i_rst = out_last = out_vld = BIT.from_py(0) main_st_fsm = Switch(main_st) for is_last_f, (f_i, f) in iter_with_last(enumerate(self.FORMAT)): en = main_st._eq(f_i) data_drive, in_vld, in_last = self.connect_single_format_group( f_i, f, strings_offset_and_size, string_rom, char_i, to_bcd_inputs, en) # build out vld from all input valids out_vld = out_vld | (en & in_vld) # keep only last of the last part out_last = en & in_last char_i_rst = char_i_rst | out_last main_st_fsm.Case( f_i, If(dout.ready & in_vld & in_last, main_st(0) if is_last_f else main_st(main_st + 1)), *data_drive) main_st_fsm.Default(main_st(None), dout.data(None)) dout.valid(out_vld) dout.last(out_last) If(dout.ready & out_vld, If(char_i_rst, char_i(0)).Else(char_i(char_i + 1))) if to_bcd_inputs: in_ = bin_to_bcd.din SwitchLogic( # actual value may be smaller, because bcd is shared among # multiple input formats [(c, in_.data(fitTo(v, in_.data, shrink=False))) for c, v in to_bcd_inputs], default=in_.data(None)) in_.vld(char_i._eq(0) & Or(*(c for c, _ in to_bcd_inputs))) propagateClkRstn(self)
def _impl(self): REG_IN = self.REG_IN REQUIRES_CASCADE = self.REG_OUT and self.DATA_WIDTH > 48 dsps = HObjList() for i in range(ceil(self.DATA_WIDTH / 48)): dsp = DSP48E1() dsp.A_INPUT = "DIRECT" dsp.B_INPUT = "DIRECT" dsp.USE_DPORT = "FALSE" dsp.ACASCREG = \ dsp.ALUMODEREG = \ dsp.AREG = \ dsp.BCASCREG = \ dsp.BREG = \ dsp.CARRYINSELREG = \ dsp.CREG = int(REG_IN or (REQUIRES_CASCADE and i > 0)) dsp.ADREG = 0 dsp.CARRYINSELREG = 0 dsp.DREG = 0 dsp.INMODEREG = 0 dsp.MREG = 0 dsp.OPMODEREG = 1 dsp.PREG = int(self.REG_OUT) dsp.USE_MULT = "NONE" dsp.USE_SIMD = "ONE48" dsps.append(dsp) self.dsp = dsps carry = 0 carry_column = 0 dsp_outputs = [] din = self.data_in dout = self.data_out dsp_clock_enables = [] dsp_inputs = [] for i, dsp in enumerate(dsps): offset = i * 48 width = min(48, self.DATA_WIDTH - offset) if width <= 18: a = 0 b = din.b[offset + width:offset] else: a = din.b[offset + width:18 + offset] b = din.b[offset + 18:offset] c = din.a[offset + width:offset] dsp_inputs.append((a, b, c)) # register to wait 1 clock before 1st operation to load operation and operand selection registers mode_preload = self._reg("mode_preload", def_val=1) mode_preload(0) if REQUIRES_CASCADE: # cascade is required because carry out signal has register and thus the input data # to a next DSP has to be delayed assert self.REG_OUT stage_cnt = int(self.REG_IN) + int(self.REG_OUT) + ceil(self.DATA_WIDTH / 48) - 1 clock_enables, _, in_ready, out_valid = generate_handshake_pipe_cntrl( self, stage_cnt, "pipe_reg", din.vld & ~mode_preload, dout.rd) delayed_dsp_inputs = [] for i, (dsp, (a, b, c)) in enumerate(zip(dsps, dsp_inputs)): if self.REG_IN: input_ces = clock_enables[:i] ce_0 = clock_enables[i] ce_1 = clock_enables[i + 1] else: if i == 0: # :note: only first does not have in registers ce_0 = 1 ce_1 = clock_enables[i] input_ces = [] else: ce_0 = clock_enables[i - 1] ce_1 = clock_enables[i] input_ces = clock_enables[:i - 1] # because the last register is a part of DPS dsp_clock_enables.append((ce_0, ce_1)) a_delayed = self.postpone_val(a, input_ces, f"a{i:d}_") b_delayed = self.postpone_val(b, input_ces, f"b{i:d}_") c_delayed = self.postpone_val(c, input_ces, f"c{i:d}_") delayed_dsp_inputs.append((a_delayed, b_delayed, c_delayed)) dsp_inputs = delayed_dsp_inputs else: stage_cnt = int(self.REG_IN) + int(self.REG_OUT) clock_enables, _, in_ready, out_valid = generate_handshake_pipe_cntrl( self, stage_cnt, "pipe_reg", din.vld & ~mode_preload, dout.rd) if self.REG_IN: ce_0 = clock_enables[0] else: ce_0 = 1 if self.REG_OUT: if self.REG_IN: ce_1 = clock_enables[1] else: ce_1 = clock_enables[0] else: ce_1 = 1 for i, dsp in enumerate(dsps): dsp_clock_enables.append((mode_preload | ce_0, mode_preload | ce_1)) dout.vld(out_valid & ~mode_preload) din.rd(in_ready & ~mode_preload) for i, (dsp, (ce_0, ce_1), (a, b, c)) in enumerate(zip(dsps, dsp_clock_enables, dsp_inputs)): offset = i * 48 width = min(48, self.DATA_WIDTH - offset) dsp.CLK(self.clk) dsp.ACIN(0) dsp.BCIN(0) dsp.MULTSIGNIN(1) dsp.PCIN(0) dsp.CEINMODE(1) self.set_mode(dsp) dsp.RSTINMODE(0) if i == 0: dsp.CARRYINSEL(CARRYIN_SEL.CARRYIN.value) dsp.CARRYIN(carry) dsp.CARRYCASCIN(carry_column) carry = dsp.CARRYOUT[3] carry_column = dsp.CARRYCASCOUT else: dsp.CARRYINSEL(CARRYIN_SEL.CARRYCASCIN.value) dsp.CARRYIN(0) dsp.CARRYCASCIN(carry_column) carry_column = dsp.CARRYCASCOUT if width <= 18: assert isinstance(a, int) and a == 0, a dsp.A(a) dsp.B(fitTo(b, dsp.B, shrink=False)) dsp.C(fitTo(c, dsp.C, shrink=False)) elif width < 48: dsp.A(fitTo(a, dsp.A, shrink=False)) dsp.B(b) dsp.C(fitTo(c, dsp.C, shrink=False)) else: dsp.A(a) dsp.B(b) dsp.C(c) dsp.D(0) dsp_outputs.append(dsp.P[width:]) for _ce in [ dsp.CEA1, dsp.CEA2, dsp.CEALUMODE, dsp.CEB2, dsp.CEB1, dsp.CEC, dsp.CECARRYIN, dsp.CECTRL, ]: _ce(ce_0) for _ce in [dsp.CEAD, dsp.CED, dsp.CEM]: _ce(1) dsp.CEP(ce_1) for _rst in [dsp.RSTA, dsp.RSTALLCARRYIN, dsp.RSTALUMODE, dsp.RSTB, dsp.RSTC, dsp.RSTCTRL, dsp.RSTD, dsp.RSTM, dsp.RSTP,]: _rst(~self.rst_n) if REQUIRES_CASCADE: delayed_dsp_outputs = [] max_delay = len(dsp_outputs) - 1 for i, out in enumerate(dsp_outputs): delay = max_delay - i if delay > 0: # select n last ces = clock_enables[-delay:] delayed_out = self.postpone_val(out, ces, f"out_delay_{i:d}") else: delayed_out = out delayed_dsp_outputs.append(delayed_out) dsp_outputs = delayed_dsp_outputs dout.data(Concat(*reversed(dsp_outputs)))
def _impl(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 = Bits(log2ceil(self.ITEMS_IN_BLOCK + 1)) ringSpace_t = Bits(self.PTR_WIDTH) downloadPending = r("downloadPending", def_val=0) baseIndex = r("baseIndex", Bits(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, Bits(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()
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) # 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) 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)