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 translate_addr_signal(self, mem_map, sig_in, sig_out): cases = [] AW = sig_in._dtype.bit_length() for (offset_in, size, offset_out) in mem_map: in_is_aligned = offset_in % size == 0 and isPow2(size) out_is_aligned = offset_out % size == 0 and isPow2(size) if in_is_aligned: L = (size - 1).bit_length() en_sig = sig_in[:L]._eq(offset_in >> L) _sig_in = sig_in[L:] if out_is_aligned: addr_drive = Concat( Bits(AW - L).from_py(offset_out), _sig_in) else: addr_drive = Concat(Bits(AW - L).from_py(0), _sig_in) + offset_out else: en_sig = inRange(sig_in, offset_in, offset_in + size) if offset_in == offset_out: addr_drive = sig_in elif offset_in < offset_out: addr_drive = sig_in + (offset_out - offset_in) else: # offset_in > offset_out: addr_drive = sig_in - (offset_in - offset_out) cases.append((en_sig, sig_out(addr_drive))) SwitchLogic(cases, default=sig_out(sig_in))
def lookupResOfTablesDriver(self, resRead, resAck): tables = self.tables # one hot encoded index where item should be stored (where was found # or where is place) targetOH = self._reg("targetOH", Bits(self.TABLE_CNT)) res = list(map(lambda t: t.lookupRes, tables)) # synchronize all lookupRes from all tables StreamNode(masters=res).sync(resAck) insertFinal = self._reg("insertFinal") # select empty space or victim witch which current insert item # should be swapped with lookupResAck = StreamNode(masters=map( lambda t: t.lookupRes, tables)).ack() insertFoundOH = list(map(lambda t: t.lookupRes.found, tables)) isEmptyOH = list(map(lambda t:~t.lookupRes.occupied, tables)) _insertFinal = Or(*insertFoundOH, *isEmptyOH) If(resRead & lookupResAck, If(Or(*insertFoundOH), targetOH(Concat(*reversed(insertFoundOH))) ).Else( SwitchLogic([(empty, targetOH(1 << i)) for i, empty in enumerate(isEmptyOH) ], default=If(targetOH, targetOH(ror(targetOH, 1)) ).Else( targetOH(1 << (self.TABLE_CNT - 1)) )) ), insertFinal(_insertFinal) ) return lookupResAck, insertFinal, insertFoundOH, targetOH
def _impl(self) -> None: if len(self.MASTERS) > 1: raise NotImplementedError() m = self.s[0] wrack = rdack = err = BIT.from_py(0) AW = int(self.ADDR_WIDTH) rdata = [] for i, (s, (s_offset, s_size)) in\ enumerate(zip(self.m, self.SLAVES)): s.bus2ip_addr(m.bus2ip_addr, fit=True) s.bus2ip_be(m.bus2ip_be) s.bus2ip_rnw(m.bus2ip_rnw) s.bus2ip_data(m.bus2ip_data) bitsOfSubAddr = log2ceil(s_size - 1) prefix = get_bit_range(s_offset, bitsOfSubAddr, AW - bitsOfSubAddr) cs = self._sig(f"m_cs_{i:d}") cs(m.bus2ip_addr[AW:bitsOfSubAddr]._eq(prefix)) s.bus2ip_cs(m.bus2ip_cs & cs) err = err | (cs & s.ip2bus_error) rdack = rdack | (cs & s.ip2bus_rdack) wrack = wrack | (cs & s.ip2bus_wrack) rdata.append((cs, s.ip2bus_data)) m.ip2bus_error(err) m.ip2bus_rdack(rdack) m.ip2bus_wrack(wrack) SwitchLogic([(sel, m.ip2bus_data(data)) for sel, data in rdata], default=m.ip2bus_data(None))
def _generate_driver_for_state_trans_dependent_out( self, st_transs: List[StateTransItem], input_regs, value_getter: Callable[[StateTransItem], object], connect_out_fn, make_defult_case: Optional[Callable[[], object]]): """ specific variant of :func:`generate_driver_for_state_trans_dependent_out` for a single state """ cases = [] for cond, value in self.get_conds_for_unique_values( st_transs, input_regs, key=value_getter): cases.append((cond, connect_out_fn(value))) if make_defult_case is None: return SwitchLogic(cases) else: return SwitchLogic(cases, default=make_defult_case())
def is_footer_mask_set_values(self, LOOK_AHEAD, regs): D_W = self.DATA_WIDTH BYTE_CNT = D_W // 8 FOOTER_WIDTH = self.FOOTER_WIDTH din = self.dataIn if self.USE_KEEP: in_mask = din.keep elif self.USE_STRB: in_mask = din.strb elif self.DATA_WIDTH == 8: in_mask = BIT.from_py(1, 1) else: raise NotImplementedError( "keep/strb can be ignored only for DATA_WIDTH=8") set_is_footer = self._sig("set_is_footer") set_is_footer(din.valid & din.last) mask_cases = [] for last_B_valid, bytes_in_last_input_word in iter_with_last( range(1, BYTE_CNT + 1)): footer_end = (LOOK_AHEAD * BYTE_CNT + bytes_in_last_input_word) * 8 footer_start = footer_end - FOOTER_WIDTH assert footer_start > 0, ( "otherwise we would not be able to send last for previous frame", footer_start) assert footer_start < D_W * 3, ( "footer start can appear only in last-1 or last-2 regster," " last register is output register", footer_start, D_W) _is_footer = set_bit_range(0, footer_start // 8, FOOTER_WIDTH // 8, mask(FOOTER_WIDTH // 8)) set_flags = [] for i, (_, _, is_footer_set_val, _, _) in enumerate(regs): if i == 0: is_footer_val = 0 is_footer_val_last_word = get_bit_range( _is_footer, (LOOK_AHEAD - i) * BYTE_CNT, BYTE_CNT) set_flags.append( If(set_is_footer, is_footer_set_val(is_footer_val_last_word)).Else( is_footer_set_val(is_footer_val))) else: is_footer_val = get_bit_range( _is_footer, (LOOK_AHEAD - i + 1) * BYTE_CNT, BYTE_CNT) set_flags.append(is_footer_set_val(is_footer_val)) if last_B_valid: # last byte also valid mask_default = set_flags else: # last 0 from the end of the validity mask mask_cases.append( (~in_mask[bytes_in_last_input_word], set_flags)) SwitchLogic(mask_cases, mask_default) return set_is_footer
def insertAddrSelect(self, targetOH, state, cleanAddr): insertIndex = self._sig("insertIndex", Bits(self.HASH_WITH)) If(state._eq(state._dtype.cleaning), insertIndex(cleanAddr) ).Else( SwitchLogic([(targetOH[i], insertIndex(t.lookupRes.hash)) for i, t in enumerate(self.tables)], default=insertIndex(None)) ) return insertIndex
def _impl(self): propagateClkRstn(self) dIn = AxiSBuilder(self, self.dataIn).buff().end sb = self.sizesBuff db = self.dataBuff wordCntr = self._reg("wordCntr", Bits(log2ceil(self.MAX_LEN) + 1), def_val=0) overflow = wordCntr._eq(self.MAX_LEN) last = dIn.last | overflow If( StreamNode(masters=[dIn], slaves=[sb.dataIn, db.dataIn]).ack(), If(last, wordCntr(0)).Else(wordCntr(wordCntr + 1))) length = self._sig("length", wordCntr._dtype) BYTE_CNT = dIn.data._dtype.bit_length() // 8 if dIn.USE_STRB: # compress strb mask as binary number rem = self._sig("rem", Bits(log2ceil(BYTE_CNT))) SwitchLogic(cases=[(dIn.strb[i], rem(0 if i == BYTE_CNT - 1 else i + 1)) for i in reversed(range(BYTE_CNT))], default=[ rem(0), ]) if self.EXPORT_ALIGNMENT_ERROR: errorAlignment = self._reg("errorAlignment_reg", def_val=0) self.errorAlignment(errorAlignment) If(dIn.valid & (dIn.strb != mask(BYTE_CNT)) & ~dIn.last, errorAlignment(1)) If(last & (dIn.strb != mask(BYTE_CNT)), length(wordCntr)).Else(length(wordCntr + 1)) else: length(wordCntr + 1) rem = Bits(log2ceil(BYTE_CNT)).from_py(0) sb.dataIn.data(Concat(length, rem)) db.dataIn(dIn, exclude=[dIn.valid, dIn.ready, dIn.last]) db.dataIn.last(last) StreamNode(masters=[dIn], slaves=[sb.dataIn, db.dataIn], extraConds={ sb.dataIn: last }).sync() self.sizes(sb.dataOut) self.dataOut(db.dataOut)
def inputMuxLogic(self, isSelectedFlags): vld = self.get_valid_signal dout = self.dataOut # data out mux dataCases = [] for isSelected, din in zip(isSelectedFlags, self.dataIn): dataConnectExpr = self.dataConnectionExpr(din, dout) cond = vld(din) & isSelected dataCases.append((cond, dataConnectExpr)) dataDefault = self.dataConnectionExpr(None, dout) SwitchLogic(dataCases, dataDefault)
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 lookupRes_driver(self, state: RtlSignal, lookupFoundOH: RtlSignal): """ If lookup request comes from external interface "lookup" propagate results from tables to "lookupRes". """ fsm_t = state._dtype lookupRes = self.lookupRes lookupResVld = StreamNode(masters=[t.lookupRes for t in self.tables]).ack() lookupRes.vld(state._eq(fsm_t.idle) & lookupResVld) SwitchLogic([(lookupFoundOH[i], lookupRes(t.lookupRes, exclude={lookupRes.vld, lookupRes.rd})) for i, t in enumerate(self.tables)], default=[ lookupRes(self.tables[0].lookupRes, exclude={lookupRes.vld, lookupRes.rd})] )
def tables_lookupRes_resolver(self, insertResRead: RtlSignal): """ Control lookupRes interface for each table """ tables = self.tables # one hot encoded index where item should be stored (where was found # or where is place) insertTargetOH = self._reg("insertTargetOH", Bits(self.TABLE_CNT, force_vector=True)) res = [t.lookupRes for t in tables] insertFinal = self._reg("insertFinal") # select empty space or victim which which current insert item # should be swapped with lookupResVld = StreamNode(masters=res).ack() lookupFoundOH = [t.lookupRes.found for t in tables] isEmptyOH = [~t.lookupRes.occupied for t in tables] If(insertResRead & lookupResVld, # resolve in which table the item should be stored If(Or(*lookupFoundOH), # an item found in some table, set target to this table insertTargetOH(Concat(*reversed(lookupFoundOH))) ).Else( # set target to first table with an empty item SwitchLogic( [(isEmpty, insertTargetOH(1 << i)) for i, isEmpty in enumerate(isEmptyOH)], # if there is no empty place in any table, swap # item with an item from next table or last table # if this is a first insert default=If(insertTargetOH != 0, insertTargetOH(ror(insertTargetOH, 1)) ).Else( insertTargetOH(1 << (self.TABLE_CNT - 1)) ) ) ), # final if the item was already somewhere or there is an empty place in some table insertFinal(Or(*lookupFoundOH, *isEmptyOH)) ) return lookupResVld, insertFinal, lookupFoundOH, insertTargetOH
def _impl(self): rd = self.get_ready_signal vld = self.get_valid_signal dout = self.dataOut vldSignals = [vld(d) for d in self.dataIn] # data out mux dataCases = [] for i, din in enumerate(self.dataIn): allLowerPriorNotReady = map(lambda x: ~x, vldSignals[:i]) rd(din)(And(rd(dout), *allLowerPriorNotReady)) cond = vld(din) dataConnectExpr = self.dataConnectionExpr(din, dout) dataCases.append((cond, dataConnectExpr)) dataDefault = self.dataConnectionExpr(None, dout) SwitchLogic(dataCases, dataDefault) vld(dout)(Or(*vldSignals))
def _impl(self) -> None: if len(self._masters) > 1: raise NotImplementedError() m_offset, _ = self._masters[0] if m_offset != 0: raise NotImplementedError() m = self.s[0] err = hBit(0) rdack = hBit(0) wrack = hBit(0) AW = int(self.ADDR_WIDTH) wdata = [] for i, (s, (s_offset, s_size, _)) in enumerate(zip(self.m, self._slaves)): connect(m.bus2ip_addr, s.bus2ip_addr, fit=True) s.bus2ip_be(m.bus2ip_be) s.bus2ip_rnw(m.bus2ip_rnw) s.bus2ip_data(m.bus2ip_data) bitsOfSubAddr = int(log2ceil(s_size - 1)) prefix = selectBitRange(s_offset, bitsOfSubAddr, AW - bitsOfSubAddr) cs = self._sig("m_cs_%d" % i) cs(m.bus2ip_addr[AW:bitsOfSubAddr]._eq(prefix)) s.bus2ip_cs(m.bus2ip_cs & cs) err = err | (cs & s.ip2bus_error) rdack = rdack | (cs & s.ip2bus_rdack) wrack = wrack | (cs & s.ip2bus_wrack) wdata.append((cs, s.ip2bus_data)) m.ip2bus_error(err) m.ip2bus_rdack(rdack) m.ip2bus_wrack(wrack) SwitchLogic([(sel, m.ip2bus_data(data)) for sel, data in wdata], default=m.ip2bus_data(None))
def lookupResDriver(self, state, lookupOrigin, lookupAck, insertFoundOH): """ If lookup request comes from external interface "lookup" propagate results from tables to "lookupRes". """ fsm_t = state._dtype lookupRes = self.lookupRes lookupRes.vld(state._eq(fsm_t.lookupResAck) & lookupOrigin._eq(ORIGIN_TYPE.LOOKUP) & lookupAck) SwitchLogic([(insertFoundOH[i], connect(t.lookupRes, lookupRes, exclude={lookupRes.vld, lookupRes.rd})) for i, t in enumerate(self.tables)], default=[ connect(self.tables[0].lookupRes, lookupRes, exclude={lookupRes.vld, lookupRes.rd})] )
def apply_data_write_forwarding(self, st: OOOOpPipelineStage, st_load_en: RtlSignal, data_modifier=lambda dst_st, src_st: dst_st.data(src_st.data)): """ :param st_collision_detect: in format stages X pipeline[WRITE_BACK-1:], if bit = 1 it means that the stage data should be updated from stage on that index """ st_prev = self.pipeline[st.index - 1] def is_not_0(sig): return not (isinstance(sig, int) and sig == 0) # we can write forward to a stages from STATE_LOAD to WRITE_BACK # however we can not replace the valid value in WRITE_BACK stage # and we need to wait on load_en res = SwitchLogic([ ( # the previous which is beeing loaded into this is colliding with src (st_load_en & st_prev.collision_detect[src_i]) | (~st_load_en & st.collision_detect[src_i]), # forward data instead of data from previous stage data_modifier(st, src_st) ) for src_i, src_st in enumerate(self.pipeline) if ( # filter out stage combinations which do not have forwarding is_not_0(st.collision_detect[src_i]) or is_not_0(st_prev.collision_detect[src_i]) ) ], default=\ If(st_load_en, data_modifier(st, st_prev) ) ) return res
def _impl(self): In = self.dataIn rd = self.get_ready_signal sel = self.selectOneHot r = HandshakedReg(Handshaked) r.DATA_WIDTH = sel.data._dtype.bit_length() self.selReg = r r.dataIn(sel) propagateClkRstn(self) sel = r.dataOut for index, outIntf in enumerate(self.dataOut): for ini, outi in zip(In._interfaces, outIntf._interfaces): if ini == self.get_valid_signal(In): # out.vld outi(sel.vld & ini & sel.data[index]) elif ini == rd(In): pass else: # data outi(ini) din = self.dataIn SwitchLogic( cases=[(~sel.vld, [sel.rd(0), rd(In)(0)]) ] + [(sel.data[index], [rd(In)(rd(out)), sel.rd(rd(out) & self.get_valid_signal(din) & sel.vld & self._select_consume_en())]) for index, out in enumerate(self.dataOut)], default=[ sel.rd(None), rd(In)(None) ] )
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 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 _impl(self) -> None: if len(self.MASTERS) > 1: raise NotImplementedError() m = self.s[0] # :note: theoretically not required b = Mi32Buff() b.ADDR_BUFF_DEPTH = 1 b.DATA_BUFF_DEPTH = 1 b._updateParamsFrom(self) self.s_0_buff = b b.s(m) m = b.m propagateClkRstn(self) r_order = self.r_data_order.dataIn AW = int(self.ADDR_WIDTH) rdata = [] r_data_t = HStruct( (m.drd._dtype, "data"), (BIT, "vld"), ) for i, (s, (s_offset, s_size)) in\ enumerate(zip(self.m, self.SLAVES)): # s = Mi32() s.addr(m.addr, fit=True) s.be(m.be) s.dwr(m.dwr) bitsOfSubAddr = log2ceil(s_size - 1) prefix = get_bit_range(s_offset, bitsOfSubAddr, AW - bitsOfSubAddr) cs = self._sig(f"m_cs_{i:d}") cs(m.addr[AW:bitsOfSubAddr]._eq(prefix)) s.rd(m.rd & cs & r_order.rd) s.wr(m.wr & cs & r_order.rd) # we have to add 1 read data latency because the slave index would not be ready # oder fifo r_data_tmp = self._reg(f"r_data{i:d}_tmp", r_data_t, def_val={"vld": 0}) r_data_tmp.data(s.drd) r_data_tmp.vld(s.drdy) rdata.append((i, cs & s.ardy, r_data_tmp)) # r_data_order feed SwitchLogic([(addr_en, r_order.data(i)) for i, addr_en, _ in rdata], default=r_order.data(None)) addr_ack = Or(*[x[1] for x in rdata]) r_order.vld(addr_ack & m.rd) # m = Mi32() m.ardy(addr_ack & r_order.rd & (m.rd | m.wr)) r_order = self.r_data_order.dataOut If( r_order.vld, Switch(r_order.data).add_cases( [(slave_i, [m.drd(data.data), m.drdy(1)]) for slave_i, _, data in rdata], ).Default( # this case can not happen unless bug in code m.drd(None), m.drdy(None))).Else( m.drd(None), m.drdy(False), ) r_order.rd(Or(*[data.vld for _, _, data in rdata]))
def data_array_io( self, aw_lru_incr: IndexWayHs, # out aw_tagRes: AxiCacheTagArrayLookupResIntf, # in victim_req: AddrHs, victim_way: Handshaked, # out, in data_arr_read_req: IndexWayHs, data_arr_read: Axi4_r, # in, out data_arr_r_port: BramPort_withoutClk, data_arr_w_port: BramPort_withoutClk, # out, out tag_update: AxiCacheTagArrayUpdateIntf # out ): """ :ivar aw_lru_incr: an interface to increment LRU for write channel :ivar victim_req: an interface to get a victim from LRU array for a specified index :ivar victim_way: return interface for victim_req :ivar aw_tagRes: an interface with a results from tag lookup :ivar data_arr_read_req: an input interface with read requests from read section :ivar data_arr_read: an output interface with a read data to read section :ivar data_arr_r_port: read port of main data array :ivar data_arr_w_port: write port of main data array """ # note that the lru update happens even if the data is stalled # but that is not a problem because it wont change the order of the usage # of the cahceline self.incr_lru_on_hit(aw_lru_incr, aw_tagRes) st0 = self._reg( "victim_load_status0", HStruct( (self.s.aw.id._dtype, "write_id" ), # the original id and address of a write transaction (self.s.aw.addr._dtype, "replacement_addr"), (aw_tagRes.TAG_T[aw_tagRes.WAY_CNT], "tags"), (BIT, "tag_found"), (BIT, "had_empty"), # had some empty tag (aw_tagRes.way._dtype, "found_way"), (BIT, "valid"), ), def_val={ "valid": 0, }) # resolve if we need to select a victim and optianally ask for it st0_ready = self._sig("victim_load_status0_ready") has_empty = rename_signal(self, Or(*(~t.valid for t in aw_tagRes.tags)), "has_empty") If( st0_ready, st0.write_id(aw_tagRes.id), st0.replacement_addr(aw_tagRes.addr), st0.tags(aw_tagRes.tags), st0.tag_found(aw_tagRes.found), st0.found_way(aw_tagRes.way), st0.had_empty(has_empty), # this register is beeing flushed, the values can become invalid # the st0.valid is used to detect this state st0.valid(aw_tagRes.vld), ) victim_req.addr(self.parse_addr(aw_tagRes.addr)[1]) tag_check_node = StreamNode( [aw_tagRes], [victim_req], skipWhen={ victim_req: aw_tagRes.vld & (aw_tagRes.found | has_empty) }, extraConds={victim_req: ~aw_tagRes.found & ~has_empty}) st1_ready = self._sig("victim_load_status1_ready") tag_check_node.sync(~st0.valid | st1_ready) tag_check_node_ack = rename_signal(self, tag_check_node.ack(), "tag_check_node_ack") st0_ready((st0.valid & tag_check_node_ack & st1_ready) | ~st0.valid | st1_ready) victim_load_st = HStruct( # and address constructed from an original tag in cache which is beeing replaced (self.s.aw.addr._dtype, "victim_addr"), # new data to write to data_array # (replacement data is still in in_w buffer because it was not consumed # if the tag was not found) (aw_tagRes.way._dtype, "victim_way"), (self.s.ar.id._dtype, "read_id"), (self.s.aw.id._dtype, "write_id"), (self.s.aw.addr._dtype, "replacement_addr" ), # the original address used to resolve new tag (Bits(2), "data_array_op"), # type of operation with data_array ) ########################## st1 - pre (read request resolution, victim address resolution) ############## d_arr_r, d_arr_w = self.instantiate_data_array_to_hs( data_arr_r_port, data_arr_w_port) # :note: flush with higher priority than regular read need_to_flush = rename_signal( self, st0.valid & (~st0.had_empty & ~st0.tag_found), "need_to_flush") If( need_to_flush, d_arr_r.addr.data( self.addr_in_data_array( victim_way.data, self.parse_addr(st0.replacement_addr)[1])), ).Else( d_arr_r.addr.data( self.addr_in_data_array(data_arr_read_req.way, data_arr_read_req.index))) _victim_way = self._sig("victim_way_tmp", Bits(log2ceil(self.WAY_CNT))) _victim_tag = self._sig("victim_tag_tmp", Bits(self.TAG_W)) SwitchLogic( [ # select first empty tag (~tag.valid, [ _victim_way(i), _victim_tag(tag.tag), ]) for i, tag in enumerate(st0.tags) ], default=[ # select an victim specified by victim_way _victim_way(victim_way.data), SwitchLogic([(victim_way.data._eq(i), _victim_tag(tag.tag)) for i, tag in enumerate(st0.tags)], default=_victim_tag(None)) ]) victim_load_status = HObjList( HandshakedReg(HsStructIntf) for _ in range(2)) for i, st in enumerate(victim_load_status): st.T = victim_load_st if i == 0: st.LATENCY = (1, 2) # to break a ready chain self.victim_load_status = victim_load_status st1_in = victim_load_status[0].dataIn.data # placed between st0, st1 pure_write = rename_signal( self, st0.valid & ~need_to_flush & ~data_arr_read_req.vld, "pure_write") pure_read = rename_signal(self, ~st0.valid & data_arr_read_req.vld, "pure_read") read_plus_write = rename_signal( self, st0.valid & ~need_to_flush & data_arr_read_req.vld, "read_plus_write") flush_write = rename_signal( self, st0.valid & need_to_flush & ~data_arr_read_req.vld, "flush_write") read_flush_write = rename_signal( self, st0.valid & need_to_flush & data_arr_read_req.vld, "read_flush_write") # not dispatched at once read_req_node = StreamNode( [victim_way, data_arr_read_req], [d_arr_r.addr, victim_load_status[0].dataIn], extraConds={ victim_way: flush_write | read_flush_write, # 0 # only write without flush not write at all but read request data_arr_read_req: pure_read | read_plus_write, # pure_read | read_plus_write, # d_arr_r.addr: pure_read | read_plus_write | flush_write | read_flush_write, # need_to_flush | data_arr_read_req.vld, # 1 # victim_load_status[0].dataIn: st0.valid | data_arr_read_req.vld, }, skipWhen={ victim_way: pure_write | pure_read | read_plus_write, data_arr_read_req: pure_write | flush_write | read_flush_write, d_arr_r.addr: pure_write, }) read_req_node.sync() st1_ready(victim_load_status[0].dataIn.rd & read_req_node.ack()) st1_in.victim_addr( self.deparse_addr(_victim_tag, self.parse_addr(st0.replacement_addr)[1], 0)) st1_in.victim_way(st0.tag_found._ternary(st0.found_way, _victim_way)), st1_in.read_id(data_arr_read_req.id) st1_in.write_id(st0.write_id) st1_in.replacement_addr(st0.replacement_addr) If(pure_write, st1_in.data_array_op(data_trans_t.write)).Elif( pure_read, st1_in.data_array_op(data_trans_t.read)).Elif( read_plus_write, st1_in.data_array_op(data_trans_t.read_and_write) ).Else( # .Elif(flush_write | read_flush_write, st1_in.data_array_op(data_trans_t.write_and_flush)) # If(st0.valid, # If(need_to_flush, # st1_in.data_array_op(data_trans_t.write_and_flush) # ).Elif(st0.tag_found & data_arr_read_req.vld, # st1_in.data_array_op(data_trans_t.read_and_write) # ).Else( # st1_in.data_array_op(data_trans_t.write) # ) # ).Else( # st1_in.data_array_op(data_trans_t.read) # ) victim_load_status[1].dataIn(victim_load_status[0].dataOut) self.flush_or_read_node(d_arr_r, d_arr_w, victim_load_status[1].dataOut, data_arr_read, tag_update)
def connectPartsOfWord(self, wordData_out: RtlSignal, tPart: Union[TransPart, ChoicesOfFrameParts, StreamOfFramePars], inPorts_out: List[Union[Handshaked, StreamNode]], lastInPorts_out: List[Union[Handshaked, StreamNode]]): """ Connect transactions parts to signal for word of output stream :param wordData_out: signal for word of output stream :param tPart: instance of TransPart or ChoicesOfFrameParts to connect :param inPorts_out: input interfaces to this transaction part :param lastInPorts_out: input interfaces for last parts of transactions """ tToIntf = self.dataIn._fieldsToInterfaces if isinstance(tPart, ChoicesOfFrameParts): # connnect parts of union to output signal w = tPart.bit_length() high, low = tPart.getBusWordBitRange() parentIntf = tToIntf[tPart.origin.parent.origin] if parentIntf not in self._tmpRegsForSelect.keys(): sel = HsBuilder(self, parentIntf._select).buff().end self._tmpRegsForSelect[parentIntf] = sel inPortGroups = ExclusiveStreamGroups() lastInPortsGroups = ExclusiveStreamGroups() # tuples (cond, part of data mux for dataOut) unionChoices = [] # for all union choices for choice in tPart: tmp = self._sig("union_tmp_", Bits(w)) intfOfChoice = tToIntf[choice.tmpl.origin] _, isSelected, isSelectValid = AxiS_frameParser.choiceIsSelected( self, intfOfChoice) unionChoices.append((isSelected, wordData_out[high:low](tmp))) isSelected = isSelected & isSelectValid inPortsNode = StreamNode() lastPortsNode = StreamNode() inPortGroups.append((isSelected, inPortsNode)) lastInPortsGroups.append((isSelected, lastPortsNode)) # walk all parts in union choice for _tPart in choice: self.connectPartsOfWord(tmp, _tPart, inPortsNode.masters, lastPortsNode.masters) # generate data out mux SwitchLogic(unionChoices, default=wordData_out(None)) inPorts_out.append(inPortGroups) lastInPorts_out.append(lastInPortsGroups) elif isinstance(tPart, StreamOfFramePars): if len(tPart) != 1: raise NotImplementedError( "Structuralized streams not implemented yiet") p = tPart[0] intf = tToIntf[p.tmpl.origin] if int(intf.DATA_WIDTH) != wordData_out._dtype.bit_length(): raise NotImplementedError( "Dynamic resizing of streams not implemented yiet") if len(self._frames) > 1: raise NotImplementedError( "Dynamic splitting on frames not implemented yet") wordData_out(self.byteOrderCare(intf.data)) inPorts_out.append(intf) if tPart.isLastPart(): lastInPorts_out.append(intf) return intf.strb else: # connect parts of fields to output signal high, low = tPart.getBusWordBitRange() if tPart.isPadding: wordData_out[high:low](None) else: intf = tToIntf[tPart.tmpl.origin] fhigh, flow = tPart.getFieldBitRange() wordData_out[high:low](self.byteOrderCare( intf.data)[fhigh:flow]) inPorts_out.append(intf) if tPart.isLastPart(): lastInPorts_out.append(intf)
def remSizeToStrb(self, remSize: RtlSignal, strb: RtlSignal, isFirstWord, isLastWord): sizeRm = self.sizeRmFifo.dataOut STRB_W = strb._dtype.bit_length() if self.isAlwaysAligned(): STRB_ALL = mask(STRB_W) strbSwitch = Switch(remSize)\ .Case(0, strb(STRB_ALL) ).add_cases( [(i + 1, strb(mask(i + 1))) for i in range(STRB_W - 1)] ).Default( strb(None) ) if isinstance(isLastWord, (bool, int, HValue)): if isLastWord: return strbSwitch else: return strb(STRB_ALL) else: return If(isLastWord, strbSwitch).Else(strb(STRB_ALL)) else: CHUNK = self.CHUNK_WIDTH // 8 MAX_BYTES = CHUNK * self.MAX_CHUNKS STRB_ALL = mask(min(STRB_W, MAX_BYTES)) ALIGNAS = self.ALIGNAS possibleBytesInLastWord = set() assert self.DATA_WIDTH % ALIGNAS == 0, ( "Required to resolve number of bytes in last word", self.DATA_WIDTH, ALIGNAS) for CHUNK_CNT in range( 1, min(self.MAX_CHUNKS, max(3, self.DATA_WIDTH // CHUNK * 3)) + 1): for o in range(0, STRB_W, ALIGNAS // 8): bytesInLastWord = (o + CHUNK * CHUNK_CNT) % ( self.DATA_WIDTH // 8) if bytesInLastWord in possibleBytesInLastWord: break possibleBytesInLastWord.add(bytesInLastWord) possibleBytesInLastWord = sorted(possibleBytesInLastWord) offsetsAlignmentCombinations = set([ # bytesInLastWord, offset value of value in last word, index of shift option (min(bytesInLastWord, MAX_BYTES), sh // 8, sh_i) for bytesInLastWord in possibleBytesInLastWord for sh_i, sh in enumerate(sizeRm.SHIFT_OPTIONS) if bytesInLastWord <= MAX_BYTES ]) offsetsAlignmentCombinations = sorted(offsetsAlignmentCombinations) t = strb._dtype.from_py # :attention: last word can be first word as well MASK_ALL = mask(STRB_W) WORD_W = strb._dtype.bit_length() return \ SwitchLogic([ (remSize._eq(0 if bytesInLastWord == STRB_W else bytesInLastWord) & sizeRm.shift._eq(shift_i), strb( # dissable prefix bytes if this is first word isFirstWord._ternary(t((MASK_ALL << shift) & MASK_ALL), t(MASK_ALL)) & # dissable suffix bytes if this last word isLastWord._ternary(t(MASK_ALL >> ((WORD_W - bytesInLastWord - shift) % WORD_W)), t(MASK_ALL)) ) ) for bytesInLastWord, shift, shift_i in offsetsAlignmentCombinations ], default=strb(None) )
def connectPartsOfWord(self, wordData_out: RtlSignal, tPart: Union[TransPart, ChoicesOfFrameParts], inPorts_out: List[Union[Handshaked, StreamNode]], lastInPorts_out: List[Union[Handshaked, StreamNode]])\ ->Tuple[Optional[RtlSignal], Optional[RtlSignal]]: """ Connect transactions parts to signal for word of output stream :param wordData_out: signal for word of output stream :param tPart: instance of TransPart or ChoicesOfFrameParts to connect :param inPorts_out: input interfaces to this transaction part :param lastInPorts_out: input interfaces for last parts of transactions :return: tuple (strb, keep) if strb/keep driven by input stream, else (None, None) """ tToIntf = self.dataIn._fieldsToInterfaces if isinstance(tPart, ChoicesOfFrameParts): # connect parts of union to output signal high, low = tPart.getBusWordBitRange() parentIntf = tToIntf[tPart.origin.parent.getFieldPath()] if parentIntf not in self._tmpRegsForSelect.keys(): sel = HsBuilder(self, parentIntf._select).buff().end self._tmpRegsForSelect[parentIntf] = sel inPortGroups = ExclusiveStreamGroups() lastInPortsGroups = ExclusiveStreamGroups() w = tPart.bit_length() # tuples (cond, part of data mux for dataOut) unionChoices = [] sk_stashes = [] # for all choices in union for choice in tPart: tmp = self._sig("union_tmp_", Bits(w)) intfOfChoice = tToIntf[choice.tmpl.getFieldPath()] _, _isSelected, isSelectValid = \ AxiS_frameParserFieldConnector.choiceIsSelected(self, intfOfChoice) unionChoices.append((_isSelected, wordData_out[high:low](tmp))) isSelected = _isSelected & isSelectValid # build meta for handshake logic sync inPortsNode = StreamNode() inPortGroups.append((isSelected, inPortsNode)) lastPortsNode = StreamNode() lastInPortsGroups.append((isSelected, lastPortsNode)) sk_stash = StrbKeepStash() # walk all parts in union choice start = tPart.startOfPart for choicePart in choice: if start != choicePart.startOfPart: # add padding because there is a hole in data _w = choicePart.startOfPart - start assert _w > 0, _w sk_stash.push((_w, 0), (_w, 0)) _strb, _keep = self.connectPartsOfWord( tmp, choicePart, inPortsNode.masters, lastPortsNode.masters) sk_stash.push(_strb, _keep) start = choicePart.endOfPart if start != tPart.endOfPart: # add padding because there is a hole after _w = tPart.endOfPart - start assert _w > 0, _w sk_stash.push((_w, 0), (_w, 0)) # store isSelected sig and strb/keep value for later strb/keep resolving sk_stashes.append((isSelected, sk_stash)) # generate data out mux SwitchLogic(unionChoices, default=wordData_out(None)) inPorts_out.append(inPortGroups) lastInPorts_out.append(lastInPortsGroups) # resolve strb/keep from strb/keep and isSelected of union members if w % 8 != 0: raise NotImplementedError(w) strb, keep = reduce_conditional_StrbKeepStashes(sk_stashes) else: # connect parts of fields to output signal high, low = tPart.getBusWordBitRange() if tPart.isPadding: wordData_out[high:low](None) else: intf = tToIntf[tPart.tmpl.getFieldPath()] fhigh, flow = tPart.getFieldBitRange() wordData_out[high:low](self.byteOrderCare( intf.data)[fhigh:flow]) inPorts_out.append(intf) if tPart.isLastPart(): lastInPorts_out.append(intf) w = tPart.bit_length() strb = int(not tPart.isPadding) keep = int(not tPart.canBeRemoved) return ((w, strb), (w, keep))
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 stash_load(self, isIdle, lookupResNext, insertTargetOH, stash, lookup_not_in_progress, another_lookup_possible): """ load a stash register from lookup/insert/delete interface """ lookup = self.lookup insert = self.insert delete = self.delete table_lookup_ack = StreamNode(slaves=[t.lookup for t in self.tables]).ack() lookup_currently_executed = stash.origin_op._eq(ORIGIN_TYPE.LOOKUP) assert self.MAX_REINSERT > 0, self.MAX_REINSERT If(isIdle, If(lookup_not_in_progress & self.clean.vld, stash.origin_op(ORIGIN_TYPE.DELETE), stash.item_vld(0) ).Elif(lookup_not_in_progress & delete.vld, stash.origin_op(ORIGIN_TYPE.DELETE), stash.key(delete.key), stash.item_vld(0), ).Elif(lookup_not_in_progress & insert.vld, stash.origin_op(ORIGIN_TYPE.INSERT), stash.key(insert.key), stash.data(insert.data), stash.reinsert_cntr(self.MAX_REINSERT), stash.item_vld(1), ).Elif(lookup.vld & lookup.rd, stash.origin_op(ORIGIN_TYPE.LOOKUP), stash.key(lookup.key), ).Elif(table_lookup_ack, stash.origin_op(ORIGIN_TYPE.DELETE), # need to set something else than lookup stash.key(None), ) ).Elif(lookupResNext, SwitchLogic([ (insertTargetOH[i], [ # load stash from item found previously # :note: happens in same time as write to table # so the stash and item in table is swapped stash.key(t.lookupRes.key), stash.data(t.lookupRes.data), stash.reinsert_cntr(stash.reinsert_cntr - 1), stash.item_vld(t.lookupRes.occupied), ] ) for i, t in enumerate(self.tables) ], default=[ stash.origin_op(ORIGIN_TYPE.DELETE), stash.key(None), stash.data(None), stash.reinsert_cntr(None), stash.item_vld(None), ]) ) cmd_priority = [self.clean, self.delete, self.insert, lookup] for i, intf in enumerate(cmd_priority): withLowerPrio = cmd_priority[:i] rd = And(isIdle, *[~x.vld for x in withLowerPrio]) if intf is lookup: rd = rd & (~lookup_currently_executed | # the stash not loaded yet table_lookup_ack # stash will be consumed ) & another_lookup_possible else: rd = rd & lookup_not_in_progress intf.rd(rd)