def _impl(self): self._parseTemplate() bus = self.bus def connectRegIntfAlways(regIntf, _addr): return ( c(bus.din, regIntf.dout.data) + c(bus.we & bus.en & bus.addr._eq(_addr), regIntf.dout.vld) ) ADDR_STEP = self._getAddrStep() if self._directlyMapped: readReg = self._reg("readReg", dtype=bus.dout._dtype) # tuples (condition, assign statements) readRegInputs = [] for t in self._directlyMapped: port = self.getPort(t) _addr = t.bitAddr // ADDR_STEP connectRegIntfAlways(port, _addr) readRegInputs.append((bus.addr._eq(_addr), readReg(port.din) )) SwitchLogic(readRegInputs) else: readReg = None if self._bramPortMapped: BRAMS_CNT = len(self._bramPortMapped) bramIndxCases = [] readBramIndx = self._reg("readBramIndx", Bits( log2ceil(BRAMS_CNT + 1), False)) outputSwitch = Switch(readBramIndx) for i, t in enumerate(self._bramPortMapped): # if we can use prefix instead of addr comparing do it _addr = t.bitAddr // ADDR_STEP _addrEnd = t.bitAddrEnd // ADDR_STEP port = self.getPort(t) _addrVld, _ = self.propagateAddr(bus.addr, ADDR_STEP, port.addr, port.dout._dtype.bit_length(), t) port.we(bus.we & _addrVld & bus.en) port.en(bus.en & _addrVld & bus.en) port.din(bus.din) bramIndxCases.append((_addrVld, readBramIndx(i))) outputSwitch.Case(i, bus.dout(port.dout)) outputSwitch.Default(bus.dout(readReg)) SwitchLogic(bramIndxCases, default=readBramIndx(BRAMS_CNT)) else: bus.dout(readReg)
def _impl(self): self._parseTemplate() bus = self.bus ADDR_STEP = self._getAddrStep() if self._directly_mapped_words: readReg = self._reg("readReg", dtype=bus.dout._dtype) # tuples (condition, assign statements) If(bus.en, self.connect_directly_mapped_read(bus.addr, readReg, []) ) self.connect_directly_mapped_write(bus.addr, bus.din, bus.en & bus.we) else: readReg = None if self._bramPortMapped: BRAMS_CNT = len(self._bramPortMapped) bramIndxCases = [] readBramIndx = self._reg("readBramIndx", Bits( log2ceil(BRAMS_CNT + 1), False)) outputSwitch = Switch(readBramIndx) for i, ((_, _), t) in enumerate(self._bramPortMapped): # if we can use prefix instead of addr comparing do it _addr = t.bitAddr // ADDR_STEP _addrEnd = t.bitAddrEnd // ADDR_STEP port = self.getPort(t) _addrVld, _ = self.propagateAddr(bus.addr, ADDR_STEP, port.addr, port.dout._dtype.bit_length(), t) port.we(bus.en & bus.we & _addrVld) port.en(bus.en & _addrVld) port.din(bus.din) bramIndxCases.append((_addrVld, readBramIndx(i))) outputSwitch.Case(i, bus.dout(port.dout)) outputSwitch.Default(bus.dout(readReg)) SwitchLogic(bramIndxCases, default=readBramIndx(BRAMS_CNT)) else: bus.dout(readReg)
def readPart(self, awAddr, w_hs): ADDR_STEP = self._getAddrStep() # build read data output mux r = self.bus.r ar = self.bus.ar rSt_t = HEnum('rSt_t', ['rdIdle', 'bramRd', 'rdData']) isBramAddr = self._sig("isBramAddr") rSt = FsmBuilder(self, rSt_t, stateRegName='rSt')\ .Trans(rSt_t.rdIdle, (ar.valid & ~isBramAddr & ~w_hs, rSt_t.rdData), (ar.valid & isBramAddr & ~w_hs, rSt_t.bramRd) ).Trans(rSt_t.bramRd, (~w_hs, rSt_t.rdData) ).Trans(rSt_t.rdData, (r.ready, rSt_t.rdIdle) ).stateReg arRd = rSt._eq(rSt_t.rdIdle) ar.ready(arRd & ~w_hs) # save ar addr arAddr = self._reg('arAddr', ar.addr._dtype) If(ar.valid & arRd, arAddr(ar.addr)) isInAddrRange = self.isInMyAddrRange(arAddr) r.valid(rSt._eq(rSt_t.rdData)) self.driveResp(isInAddrRange, r.resp) if self._bramPortMapped: rdataReg = self._reg("rdataReg", r.data._dtype) _isInBramFlags = [] # list of tuples (cond, rdataReg assignment) rregCases = [] # index of bram from where we reads from bramRdIndx = self._reg("bramRdIndx", Bits(log2ceil(len(self._bramPortMapped)))) bramRdIndxSwitch = Switch(bramRdIndx) for bramIndex, ((base, end), t) in enumerate(self._bramPortMapped): port = self.getPort(t) # map addr for bram ports dstAddrStep = port.dout._dtype.bit_length() (_isMyArAddr, arAddrConnect) = self.propagateAddr(ar.addr, ADDR_STEP, port.addr, dstAddrStep, t) (_, ar2AddrConnect) = self.propagateAddr(arAddr, ADDR_STEP, port.addr, dstAddrStep, t) (_isMyAwAddr, awAddrConnect) = self.propagateAddr(awAddr, ADDR_STEP, port.addr, dstAddrStep, t) prioritizeWrite = w_hs & _isMyAwAddr If(prioritizeWrite, awAddrConnect).Elif(rSt._eq(rSt_t.rdIdle), arAddrConnect).Else(ar2AddrConnect) _isInBramFlags.append(_isMyArAddr) port.en((ar.valid & _isMyArAddr) | prioritizeWrite) port.we(prioritizeWrite) rregCases.append((_isMyArAddr, bramRdIndx(bramIndex))) bramRdIndxSwitch.Case(bramIndex, rdataReg(port.dout)) bramRdIndxSwitch.Default(rdataReg(rdataReg)) If(arRd, SwitchLogic(rregCases)) isBramAddr(Or(*_isInBramFlags)) else: rdataReg = None isBramAddr(0) self.connect_directly_mapped_read(arAddr, r.data, r.data(rdataReg))
def _impl(self): """ Purpose of this method In this method all public interfaces and configuration has been made and they can not be edited. """ # create local variable to make code shorter a = self.a b = self.b # "call" is overloaded to do assignment # it means c = a + b in target HDL # type conversion is can be done by _auto_cast or _reinterpret_cast method call self.c(a + b._auto_cast(a._dtype)) # width of signals is not same, this would raise TypeError on regular assignment, # this behavior can be overriden by calling connect with fit=True self.fitted(a, fit=True) # every signal/value has _dtype attribute which is parent type # most of the types have physical size, bit_lenght returns size of this type in bits assert self.a._dtype.bit_length() == 32 # it is possible to create signal explicitly by calling ._sig method # result of every operator is signal const_private_signal = self._sig("const_private_signal", dtype=uint32_t, def_val=123) self.contOut(const_private_signal) # this signal will be optimized out because it has no effect on any output # self.d will remain because it is part of interface self._sig("optimizedOut", dtype=uint32_t, def_val=123) # by _reg function usual d-register can be instantiated # to be able to use this this unit has to have clock defined # (you can force any signal as clock if you call self._ctx._reg directly) # default type is BIT r = self._reg("r", def_val=0) # HDL If statement is object # ~ is negation operator If(~r, # you can directly assign to register and it will assign to its next value # (assigned value appears in it in second clk tick) r(self.e) ) # again signals has to affect output or they will be optimized out self.f(r) # instead of and, or, xor use &, |, ^ because they are overridden to do the job tmp0 = a[1] & b[1] tmp1 = (a[0] ^ b[0]) | a[1] # bit concatenation is done by Concat function, python like slicing supported self.g(Concat(tmp0, tmp1, a[6:])) # results of comparison operators assigned to bits of cmp signal cmp = self.cmp cmp[0](a < 4) cmp[1](a > 4) cmp[2](b <= 4) cmp[3](b >= 4) cmp[4](b != 4) # _eq() is used as ==, # overriding == would have many unintended consequences in python # (it would make all signals unhashable) cmp[5](b._eq(4)) h = self.h # all statements are just objects statements0 = h(0) statements1 = h(1) statements2 = h(2) statements3 = foo(r, statements0, a[1], statements1, statements2) assert isinstance(statements3, If) If(a[2], # also when there is not value specified in the branch of dataflow # (in this case there is missing else branch) this signal will become latched statements3 ) # all statements like If, Switch, For and others are in hwt.code # names of generated signals are patched to avoid collisions automatically r0 = self._reg("r", Bits(2), def_val=0) r1 = self._reg("r", Bits(2), def_val=0) r0(self.i) r1(r0) # type of signal can be array as well, this allow to create memories like BRAM... # ROM will be synchronous ROM in this case rom = self._sig("rom", uint8_t[4], def_val=[i for i in range(4)]) If(self.clk._onRisingEdge(), self.j(rom[r1]) ) self.out(0) # None is converted to value with zero validity mask # same as self.output._dtype.from_py(0, vld_mask=0) self.output(None) # statements are code-generator frendly stm = \ Switch(a).Case(1, self.sc_signal(0) ).Case(2, self.sc_signal(1) ) compileTimeCondition = True if compileTimeCondition: stm.Case(3, self.sc_signal(3) ).Default( self.sc_signal(4) ) # ram working on falling edge of clk # note that rams are usually working on rising edge fRam = self._sig("fallingEdgeRam", int8_t[4]) If(self.clk._onFallingEdge(), # fit can extend signal and also shrink it fRam[r1](a, fit=True), self.k(fRam[r1]._unsigned(), fit=True) )
def readPart(self, awAddr, w_hs): ADDR_STEP = self._getAddrStep() DW = int(self.DATA_WIDTH) # build read data output mux r = self.bus.r ar = self.bus.ar rSt_t = HEnum('rSt_t', ['rdIdle', 'bramRd', 'rdData']) isBramAddr = self._sig("isBramAddr") rSt = FsmBuilder(self, rSt_t, stateRegName='rSt')\ .Trans(rSt_t.rdIdle, (ar.valid & ~isBramAddr & ~w_hs, rSt_t.rdData), (ar.valid & isBramAddr & ~w_hs, rSt_t.bramRd) ).Trans(rSt_t.bramRd, (~w_hs, rSt_t.rdData) ).Trans(rSt_t.rdData, (r.ready, rSt_t.rdIdle) ).stateReg arRd = rSt._eq(rSt_t.rdIdle) ar.ready(arRd & ~w_hs) # save ar addr arAddr = self._reg('arAddr', ar.addr._dtype) If(ar.valid & arRd, arAddr(ar.addr)) isInAddrRange = self.isInMyAddrRange(arAddr) r.valid(rSt._eq(rSt_t.rdData)) If(isInAddrRange, r.resp(RESP_OKAY)).Else(r.resp(RESP_SLVERR)) if self._bramPortMapped: rdataReg = self._reg("rdataReg", r.data._dtype) _isInBramFlags = [] # list of tuples (cond, rdataReg assignment) rregCases = [] # index of bram from where we reads from bramRdIndx = self._reg("bramRdIndx", Bits(log2ceil(len(self._bramPortMapped)))) bramRdIndxSwitch = Switch(bramRdIndx) for bramIndex, t in enumerate(self._bramPortMapped): port = self.getPort(t) # map addr for bram ports dstAddrStep = port.dout._dtype.bit_length() (_isMyArAddr, arAddrConnect) = self.propagateAddr(ar.addr, ADDR_STEP, port.addr, dstAddrStep, t) (_, ar2AddrConnect) = self.propagateAddr(arAddr, ADDR_STEP, port.addr, dstAddrStep, t) (_isMyAwAddr, awAddrConnect) = self.propagateAddr(awAddr, ADDR_STEP, port.addr, dstAddrStep, t) prioritizeWrite = _isMyAwAddr & w_hs If(prioritizeWrite, awAddrConnect).Elif(rSt._eq(rSt_t.rdIdle), arAddrConnect).Else(ar2AddrConnect) _isInBramFlags.append(_isMyArAddr) port.en((_isMyArAddr & ar.valid) | prioritizeWrite) port.we(prioritizeWrite) rregCases.append((_isMyArAddr, bramRdIndx(bramIndex))) bramRdIndxSwitch.Case(bramIndex, rdataReg(port.dout)) bramRdIndxSwitch.Default(rdataReg(rdataReg)) If(arRd, SwitchLogic(rregCases)) isBramAddr(Or(*_isInBramFlags)) else: rdataReg = None isBramAddr(0) directlyMappedWors = [] for w, items in sorted(groupedby( self._directlyMapped, lambda t: t.bitAddr // DW * (DW // ADDR_STEP)), key=lambda x: x[0]): lastBit = 0 res = [] items.sort(key=lambda t: t.bitAddr) for t in items: b = t.bitAddr % DW if b > lastBit: # add padding pad_w = b - lastBit pad = Bits(pad_w).fromPy(None) res.append(pad) lastBit += pad_w din = self.getPort(t).din res.append(din) lastBit += din._dtype.bit_length() if lastBit != DW: # add at end padding pad = Bits(DW - lastBit).fromPy(None) res.append(pad) directlyMappedWors.append((w, Concat(*reversed(res)))) Switch(arAddr).addCases([ (w[0], r.data(w[1])) for w in directlyMappedWors ]).Default(r.data(rdataReg))
def _create_frame_build_logic(self): self.byteOrderCare = get_byte_order_modifier(self.dataOut) STRB_ALL = mask(int(self.DATA_WIDTH // 8)) words = list(self.chainFrameWords()) dout = self.dataOut maxWordIndex = words[-1][0] multipleWords = maxWordIndex > 0 if multipleWords: # multiple word frame wordCntr_inversed = self._reg("wordCntr_inversed", Bits(log2ceil(maxWordIndex + 1), False), def_val=maxWordIndex) wcntrSw = Switch(wordCntr_inversed) # inversed indexes of ends of frames endsOfFrames = [] extra_strbs = [] extra_keeps = [] for i, transParts, isLast in words: inversedIndx = maxWordIndex - i # input ports for value of this output word inPorts = [] # input ports which value should be consumed on this word lastInPorts = [] if multipleWords: wordData = self._sig(f"word{i:d}", dout.data._dtype) else: wordData = self.dataOut.data sk_stash = StrbKeepStash() for tPart in transParts: strb, keep = self.connectPartsOfWord(wordData, tPart, inPorts, lastInPorts) sk_stash.push(strb, keep) sk_stash.pop(inversedIndx, extra_strbs, extra_keeps, STRB_ALL) if multipleWords: en = wordCntr_inversed._eq(inversedIndx) else: en = True en = self.dataOut.ready & en ack = self.handshakeLogicForWord(inPorts, lastInPorts, en) inStreamLast = True for p in inPorts: if isinstance(p, AxiStream): inStreamLast = p.last & inStreamLast if multipleWords: # word cntr next logic if i == maxWordIndex: nextWordIndex = maxWordIndex else: nextWordIndex = wordCntr_inversed - 1 _ack = dout.ready & ack & inStreamLast a = [ If(_ack, wordCntr_inversed(nextWordIndex)), ] else: a = [] a.append(dout.valid(ack)) # frame with multiple words (using wordCntr_inversed) if multipleWords: # data out logic a.append(dout.data(wordData)) wcntrSw.Case(inversedIndx, a) # is last word in frame if isLast: endsOfFrames.append((inversedIndx, inStreamLast)) # to prevent latches if not multipleWords: pass elif not isPow2(maxWordIndex + 1): default = [ wordCntr_inversed(maxWordIndex), ] default.append(dout.valid(0)) default.append(dout.data(None)) wcntrSw.Default(default) if multipleWords: last = False last_last = last for indexOrTuple in endsOfFrames: i, en = indexOrTuple last_last = wordCntr_inversed._eq(i) & en last = (last_last) | last selectRegLoad = last_last & dout.ready & ack else: last = endsOfFrames[0][1] selectRegLoad = dout.ready & ack for r in self._tmpRegsForSelect.values(): r.rd(selectRegLoad) dout.last(last) if multipleWords: if self.USE_STRB: strb = dout.strb Switch(wordCntr_inversed).add_cases([ (i, strb(v)) for i, v in extra_strbs ]).Default(strb(STRB_ALL)) if self.USE_KEEP: keep = dout.keep Switch(wordCntr_inversed).add_cases([ (i, keep(v)) for i, v in extra_keeps ]).Default(keep(STRB_ALL)) else: if extra_strbs: m = extra_strbs[0][1] else: m = STRB_ALL if self.USE_STRB: dout.strb(m) if extra_keeps: m = extra_keeps[0][1] else: m = STRB_ALL if self.USE_KEEP: dout.keep(m)
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): """ Iterate over words in template and create stream output mux and fsm. Frame specifier can contains unions/streams/padding/unaligned items and other features which makes code below complex. Frame specifier can also describe multiple frames. """ if self.IS_BIGENDIAN: byteOrderCare = reverseByteOrder else: def byteOrderCare(sig): return sig self.byteOrderCare = byteOrderCare words = list(self.chainFrameWords()) dout = self.dataOut self.parseTemplate() maxWordIndex = words[-1][0] multipleWords = maxWordIndex > 0 if multipleWords: # multiple word frame wordCntr_inversed = self._reg("wordCntr_inversed", Bits(log2ceil(maxWordIndex + 1), False), defVal=maxWordIndex) wcntrSw = Switch(wordCntr_inversed) # inversed indexes of ends of frames endsOfFrames = [] extra_strbs = [] for i, transParts, isLast in words: inversedIndx = maxWordIndex - i # input ports for value of this output word inPorts = [] # input ports witch value should be consumed on this word lastInPorts = [] if multipleWords: wordData = self._sig("word%d" % i, dout.data._dtype) else: wordData = self.dataOut.data for tPart in transParts: extra_strb = self.connectPartsOfWord(wordData, tPart, inPorts, lastInPorts) if extra_strb is not None: if len(transParts) > 1: raise NotImplementedError( "Construct rest of the strb signal") extra_strbs.append((inversedIndx, extra_strb)) if multipleWords: en = wordCntr_inversed._eq(inversedIndx) else: en = True en = self.dataOut.ready & en ack = self.handshakeLogicForWord(inPorts, lastInPorts, en) inStreamLast = True for p in inPorts: if isinstance(p, AxiStream): inStreamLast = p.last & inStreamLast if multipleWords: # word cntr next logic if i == maxWordIndex: nextWordIndex = maxWordIndex else: nextWordIndex = wordCntr_inversed - 1 _ack = dout.ready & ack & inStreamLast a = [ If(_ack, wordCntr_inversed(nextWordIndex)), ] else: a = [] a.append(dout.valid(ack)) # frame with multiple words (using wordCntr_inversed) if multipleWords: # data out logic a.append(dout.data(wordData)) wcntrSw.Case(inversedIndx, a) # is last word in frame if isLast: endsOfFrames.append((inversedIndx, inStreamLast)) # to prevent latches if not multipleWords: pass elif not isPow2(maxWordIndex + 1): default = wordCntr_inversed(maxWordIndex) default.append(dout.valid(0)) default.append(dout.data(None)) wcntrSw.Default(default) if multipleWords: last = False last_last = last for indexOrTuple in endsOfFrames: i, en = indexOrTuple last_last = wordCntr_inversed._eq(i) & en last = (last_last) | last selectRegLoad = last_last & dout.ready & ack else: last = endsOfFrames[0][1] selectRegLoad = dout.ready & ack for r in self._tmpRegsForSelect.values(): r.rd(selectRegLoad) dout.last(last) strb = dout.strb STRB_ALL = mask(int(self.DATA_WIDTH // 8)) if multipleWords: Switch(wordCntr_inversed).addCases([ (i, strb(v)) for i, v in extra_strbs ]).Default(strb(STRB_ALL)) else: if extra_strbs: strb(extra_strbs[0][1]) else: strb(STRB_ALL)