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 wHandler(self): w = self.wDatapump.w fWOut = self.orderInfoFifoW.dataOut fAckIn = self.orderInfoFifoAck.dataIn driversW = list(map(lambda d: d.w, self.drivers)) selectedDriverVld = self._sig("selectedDriverWVld") selectedDriverVld(Or(*map(lambda d: fWOut.data._eq(d[0]) & d[1].valid, enumerate(driversW)) )) selectedDriverLast = self._sig("selectedDriverLast") selectedDriverLast(Or(*map(lambda d: fWOut.data._eq(d[0]) & d[1].last, enumerate(driversW)) )) Switch(fWOut.data).addCases( [(i, connect(d, w, exclude=[d.valid, d.ready])) for i, d in enumerate(driversW)] ).Default( w.data(None), w.strb(None), w.last(None) ) fAckIn.data(fWOut.data) # handshake logic fWOut.rd(selectedDriverVld & selectedDriverLast & w.ready & fAckIn.rd) for i, d in enumerate(driversW): d.ready(fWOut.data._eq(i) & w.ready & fWOut.vld & fAckIn.rd) w.valid(selectedDriverVld & fWOut.vld & fAckIn.rd) fAckIn.vld(selectedDriverVld & selectedDriverLast & w.ready & fWOut.vld)
def __ne__(self, other: Union["StructIntf", StructValBase]): if isinstance(other, self.__class__): assert self._dtype == other._dtype return Or( *(si != oi for si, oi in zip(self._interfaces, other._interfaces))) else: return Or(*(si != getattr(other, si._name) for si in self._interfaces))
def _impl(self): rdSignals = self.isSelectedLogic() for dout in self.dataOut: connect(self.dataIn, dout, exclude={self.getRd(dout), self.getVld(dout)}) if self.EXPORT_SELECTED: self.getRd(self.dataIn)(Or(*rdSignals) & self.selectedOneHot.rd) else: self.getRd(self.dataIn)(Or(*rdSignals))
def handler_dout_vld(self, dataOut_channels, dataIn_channels, order_dout_index_for_din, order_din_index_for_dout): for dout_i, (dataOut, order_s_for_m, connected_inputs) in enumerate( zip(dataOut_channels, order_din_index_for_dout, self.OUTPUTS)): selected_dataIn_valid = [] for din_i, (dataIn, order_m_for_s) in enumerate( zip(dataIn_channels, order_dout_index_for_din)): if din_i not in connected_inputs: continue vld = dataIn.valid if order_s_for_m is not None: vld = vld & order_s_for_m.data._eq(din_i) if order_m_for_s is not None: vld = vld & order_m_for_s.vld \ & order_m_for_s.data._eq(dout_i) selected_dataIn_valid.append(vld) assert selected_dataIn_valid, ( dataOut, "entirely disconnected from crossbar, this should have been handled before" ) selected_dataIn_valid = rename_signal( self, Or(*selected_dataIn_valid), f"dataOut_{dout_i:d}_selected_dataIn_valid") if order_s_for_m is None: dataOut.valid(selected_dataIn_valid) else: dataOut.valid(selected_dataIn_valid & order_s_for_m.vld) selected_dataIn_last = [] for din_i, (dataIn, order_m_for_s) in enumerate( zip(dataIn_channels, order_dout_index_for_din)): if din_i not in connected_inputs: continue last = dataIn.valid & self.get_last(dataIn) if order_s_for_m is not None: last = last & order_s_for_m.vld\ & order_s_for_m.data._eq(din_i) selected_dataIn_last.append(last) selected_dataIn_last = rename_signal( self, Or(*selected_dataIn_last), f"dataOut_{dout_i:d}_selected_dataIn_last") if order_s_for_m is not None: order_s_for_m.rd(selected_dataIn_valid & selected_dataIn_last & dataOut.ready)
def _impl(self): din = self.dataIn rdSignals = self.isSelectedLogic(din) for dout in self.dataOut: dout(din, exclude={ self.get_ready_signal(dout), self.get_valid_signal(dout) }) if self.EXPORT_SELECTED: self.get_ready_signal(din)(Or(*rdSignals) & self.selectedOneHot.rd) else: self.get_ready_signal(din)(Or(*rdSignals))
def _impl(self): assert int(self.DRIVER_CNT ) > 1, "It makes no sense to use interconnect in this case" propagateClkRstn(self) self.reqHandler(self.rDatapump.req, self.orderInfoFifo.dataIn) fifoOut = self.orderInfoFifo.dataOut r = self.rDatapump.r driversR = [d.r for d in self.drivers] selectedDriverReady = self._sig("selectedDriverReady") selectedDriverReady( Or(*[ fifoOut.data._eq(di) & d.ready for di, d in enumerate(driversR) ])) # extra enable signals based on selected driver from orderInfoFifo # extraHsEnableConds = { # r : fifoOut.vld # on end of frame pop new item # } for i, d in enumerate(driversR): # extraHsEnableConds[d] d.valid(r.valid & fifoOut.vld & fifoOut.data._eq(i)) d(r, exclude=[d.valid, d.ready]) r.ready(fifoOut.vld & selectedDriverReady) fifoOut.rd(r.valid & r.last & selectedDriverReady)
def handler_din_rd(self, dataOut_channels, dataIn_channels, order_dout_index_for_din, order_din_index_for_dout): for din_i, (order_m_for_s, dataIn, connected_outputs) in enumerate( zip(order_dout_index_for_din, dataIn_channels, self.OUTS_FOR_IN)): selected_dataOut_ready = [] for dout_i, (d, order_s_for_m) in enumerate( zip(dataOut_channels, order_din_index_for_dout)): if dout_i not in connected_outputs: continue rd = d.ready if order_m_for_s is not None: rd = rd & order_m_for_s.data._eq(dout_i) if order_s_for_m is not None: rd = rd & order_s_for_m.data._eq(din_i)\ & order_s_for_m.vld selected_dataOut_ready.append(rd) assert selected_dataOut_ready, ( dataIn, "entirely disconnected from crossbar," " this should have been handled before") selected_dataOut_ready = rename_signal( self, Or(*selected_dataOut_ready), f"dataIn_{din_i:d}_selected_dataOut_ready") if order_m_for_s is not None: dataIn.ready(order_m_for_s.vld & selected_dataOut_ready) order_m_for_s.rd(dataIn.valid & self.get_last(dataIn) & selected_dataOut_ready) else: dataIn.ready(selected_dataOut_ready)
def get_lru(self): """ To find LRU, we can perform a depth-first-search starting from root, and traverse nodes in lower levels. If the node is 0, then we traverse the left sub-tree; otherwise, we traverse the right sub-tree. In the diagram above, the LRU is set 3. """ # node_index: bits rlu register node_paths = {} self._build_node_paths(node_paths, 0, tuple()) # also number of levels of rlu tree bin_index_w = log2ceil( self.lru_reg_items(self.lru_regs._dtype.bit_length())) lru_index_bin = [] # msb first in lru binary index for output_bit_i in range(bin_index_w): items_on_current_level = int(2**output_bit_i) current_level_offset = 2**output_bit_i - 1 possible_paths = [] for node_i in range(current_level_offset, current_level_offset + items_on_current_level): p = node_paths[node_i] possible_paths.append(And(*p)) lru_index_bin.append(Or(*possible_paths)) # MSB was first so the result is in little endian MSB..LSB return Concat(*lru_index_bin)
def isSelectedLogic(self): """ Resolve isSelected signal flags for each input, when isSelected flag signal is 1 it means input has clearance to make transaction """ vld = self.getVld rd = self.getRd dout = self.dataOut priority = self._reg("priority", Bits(self.INPUTS), defVal=1) priority(rol(priority, 1)) vldSignals = list(map(vld, self.dataIn)) isSelectedFlags = [] for i, din in enumerate(self.dataIn): isSelected = self._sig("isSelected_%d" % i) isSelected(self.priorityAck(priority, vldSignals, i)) isSelectedFlags.append(isSelected) rd(din)(isSelected & rd(dout)) if self.EXPORT_SELECTED: self.selectedOneHot.data[i](isSelected & vld(din)) if self.EXPORT_SELECTED: self.selectedOneHot.vld(Or(*vldSignals) & rd(dout)) return isSelectedFlags, vldSignals
def isSelectedLogic(self, din): """ Resolve isSelected signal flags for each input, when isSelected flag signal is 1 it means input has clearance to make transaction """ vld = self.get_valid_signal rd = self.get_ready_signal EXPORT_SELECTED = bool(self.EXPORT_SELECTED) priority = self._reg("priority", Bits(self.OUTPUTS), def_val=1) priority(rol(priority, 1)) rdSignals = [rd(d) for d in self.dataOut] for i, dout in enumerate(self.dataOut): isSelected = self._sig(f"isSelected_{i:d}") isSelected(HsJoinFairShare.priorityAck(priority, rdSignals, i)) if EXPORT_SELECTED: self.selectedOneHot.data[i](isSelected & vld(din)) vld(dout)(isSelected & vld(din) & self.selectedOneHot.rd) else: vld(dout)(isSelected & vld(din)) if EXPORT_SELECTED: self.selectedOneHot.vld(Or(*rdSignals) & vld(din)) return rdSignals
def isSelectedLogic(self, din_vlds, dout_rd, selectedOneHot): """ Resolve isSelected signal flags for each input, when isSelected flag signal is 1 it means input has clearance to make transaction """ assert din_vlds if len(din_vlds) == 1: isSelectedFlags = [ BIT.from_py(1), ] if selectedOneHot is not None: selectedOneHot.data(1) else: priority = self._reg("priority", Bits(len(din_vlds)), def_val=1) priority(rol(priority, 1)) isSelectedFlags = [] for i, din_vld in enumerate(din_vlds): isSelected = self._sig(f"isSelected_{i:d}") isSelected(self.priorityAck(priority, din_vlds, i)) isSelectedFlags.append(isSelected) if selectedOneHot is not None: selectedOneHot.data[i](isSelected & din_vld) if selectedOneHot is not None: selectedOneHot.vld(Or(*din_vlds) & dout_rd) return isSelectedFlags
def isSelectedLogic(self): """ Resolve isSelected signal flags for each input, when isSelected flag signal is 1 it means input has clearance to make transaction """ vld = self.getVld rd = self.getRd din = self.dataIn EXPORT_SELECTED = bool(self.EXPORT_SELECTED) priority = self._reg("priority", Bits(self.OUTPUTS), defVal=1) priority(rol(priority, 1)) rdSignals = list(map(rd, self.dataOut)) for i, dout in enumerate(self.dataOut): isSelected = self._sig("isSelected_%d" % i) isSelected(HsJoinFairShare.priorityAck(priority, rdSignals, i)) if EXPORT_SELECTED: self.selectedOneHot.data[i](isSelected & vld(din)) vld(dout)(isSelected & vld(din) & self.selectedOneHot.rd) else: vld(dout)(isSelected & vld(din)) if EXPORT_SELECTED: self.selectedOneHot.vld(Or(*rdSignals) & vld(din)) return rdSignals
def ack(self) -> RtlSignal: ack = hBit(1) if self: acks = list( map(lambda x: x[1].ack() & self.selectorIntf.data._eq(x[0]), self)) ack = Or(*acks) return ack & self.selectorIntf.vld
def ack(self) -> RtlSignal: ack = BIT.from_py(1) if self: acks = [ x[1].ack() & self.selectorIntf.data._eq(x[0]) for x in self ] ack = Or(*acks) return ack & self.selectorIntf.vld
def ack(self): """ :return: expression which's value is high when transaction can be made over interfaces """ # every interface has to have skip flag or it has to be ready/valid # and extraCond has to be True if present acks = [] for m in self.masters: extra, skip = self.getExtraAndSkip(m) if isinstance(m, ExclusiveStreamGroups): a = m.ack() else: a = _get_valid_signal(m) if extra: a = And(a, *extra) if skip is not None: a = Or(a, skip) acks.append(a) for s in self.slaves: extra, skip = self.getExtraAndSkip(s) if isinstance(s, ExclusiveStreamGroups): a = s.ack() else: a = _get_ready_signal(s) if extra: a = And(a, *extra) if skip is not None: a = Or(a, skip) acks.append(a) if not acks: return True return And(*acks)
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 ack(self) -> RtlSignal: if self.wordIndexReg is None: getAck = self._getAck_no_wordIndex else: getAck = self._getAck_with_wordIndex acks = [getAck(w) for w in self.words] if acks: return Or(*acks) else: return BIT.from_py(1)
def get_conds_for_unique_values(self, st_ts: List[StateTransItem], input_regs, key: Callable[[StateTransItem], None]): # output value : List[RtlSignal] value_conds = {} for st_t in st_ts: k = key(st_t) cond_list = value_conds.setdefault(k, []) cond = self.state_trans_cond(st_t, input_regs) cond_list.append(cond) return [(Or(*v), k) for k, v in value_conds.items()]
def is_mask_byte_unaligned(mask_signal: RtlSignal) -> RtlSignal: # True if each byte of the mask is all 0 or all 1 we_bytes = list(iterBits(mask_signal, bitsInOne=8, fillup=True)) write_mask_not_aligned = [] for last, b in iter_with_last(we_bytes): if last: # cut off padding if required mask_rem_w = mask_signal._dtype.bit_length() % 8 if mask_rem_w: b = b[mask_rem_w:] write_mask_not_aligned.append((b != 0) & (b != mask(b._dtype.bit_length()))) return Or(*write_mask_not_aligned)
def inputMuxLogic(self, isSelectedFlags, vldSignals): vld = self.getVld 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) vld(dout)(Or(*vldSignals))
def _impl(self): dataOut = list(reversed(self.dataOut)) self.getRd(self.dataIn)(Or(*map(lambda x: self.getRd(x), dataOut))) for i, out in enumerate(dataOut): allWitLowerPriority = dataOut[i + 1:] vld = self.getVld(self.dataIn) for _vld in map(lambda x: ~self.getRd(x), allWitLowerPriority): vld = vld & _vld connect(self.dataIn, out, exclude={self.getRd(out), self.getVld(out)}) self.getVld(out)(vld)
def pop_mask_value(mask_val_to_en_dict: Dict[HValue, List[RtlSignal]]): if len(mask_val_to_en_dict) == 1: v, _ = mask_val_to_en_dict.popitem() # there is only a single possible value, that means that the decision to a different mask # can be done on top level, but on this level the selection signals does not matter return v # return Or(*ens)._ternary(v, v._dtype.from_py(0)) else: assert mask_val_to_en_dict masks = sorted(mask_val_to_en_dict.items(), key=lambda x: x[0]) m = masks[0][0]._dtype.from_py(0) for v, ens in masks: assert ens, ens m = Or(*ens)._ternary(v, m) return m
def _push_mask_vec(res, data_len, data_valid): assert data_len % 8 == 0, "mask generated from padding is byte aligned" assert data_len > 0, data_len if isinstance(data_valid, tuple): m, ens = data_valid data_valid = Or(*ens)._ternary( m, Bits(m._dtype.bit_length()).from_py(0)) if isinstance(data_valid, RtlSignal): res.append(data_valid) else: assert isinstance(data_valid, (int, BitsVal)), data_valid w = data_len // 8 v = mask(w) if data_valid else 0 res.append(Bits(w).from_py(v))
def ackHandler(self): ack = self.wDatapump.ack fAckOut = self.orderInfoFifoAck.dataOut driversAck = [d.ack for d in self.drivers] selectedDriverAckReady = self._sig("selectedDriverAckReady") selectedDriverAckReady( Or(*map(lambda d: fAckOut.data._eq(d[0]) & d[1].rd, enumerate(driversAck)))) ack.rd(fAckOut.vld & selectedDriverAckReady) fAckOut.rd(ack.vld & selectedDriverAckReady) for i, d in enumerate(driversAck): d(ack, exclude=[d.vld, d.rd]) d.vld(ack.vld & fAckOut.vld & fAckOut.data._eq(i))
def ack(self) -> RtlSignal: if self.wordIndexReg is None: def getAck(x): return x[1].ack() else: def getAck(x): return self.wordIndexReg._eq(x[0]) & x[1].ack() acks = list(map(getAck, self.words)) if acks: return Or(*acks) else: return hBit(1)
def _impl(self): dataOut = list(reversed(self.dataOut)) self.get_ready_signal(self.dataIn)( Or(*map(lambda x: self.get_ready_signal(x), dataOut))) for i, out in enumerate(dataOut): allWitLowerPriority = dataOut[i + 1:] vld = self.get_valid_signal(self.dataIn) for _vld in map(lambda x: ~self.get_ready_signal(x), allWitLowerPriority): vld = vld & _vld out(self.dataIn, exclude={ self.get_ready_signal(out), self.get_valid_signal(out) }) self.get_valid_signal(out)(vld)
def _impl(self): if self.EXPORT_SELECTED: selectedOneHot = self.selectedOneHot else: selectedOneHot = None rd = self.get_ready_signal vld = self.get_valid_signal dout = self.dataOut din_vlds = [vld(d) for d in self.dataIn] # round-robin isSelectedFlags = self.isSelectedLogic(din_vlds, rd(dout), selectedOneHot) self.inputMuxLogic(isSelectedFlags) # handshake logic with injected round-robin for din, isSelected in zip(self.dataIn, isSelectedFlags): rd(din)(isSelected & rd(dout)) vld(dout)(Or(*din_vlds))
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 tables_insert_driver(self, state: RtlSignal, insertTargetOH: RtlSignal, insertIndex: RtlSignal, stash: RtlSignal): """ :param state: state register of main FSM :param insertTargetOH: index of table where insert should be performed, one hot encoding :param insertIndex: address for table where item should be placed :param stash: stash register with data for insert/lookup/delete from table """ fsm_t = state._dtype for i, t in enumerate(self.tables): ins = t.insert ins.hash(insertIndex) ins.key(stash.key) if self.DATA_WIDTH: ins.data(stash.data) ins.vld(Or(state._eq(fsm_t.cleaning), state._eq(fsm_t.lookupResAck) & insertTargetOH[i])) ins.item_vld(stash.item_vld)