def createTmpVarFn(suggestedName, dtype): s = RtlSignal(None, None, dtype, virtual_only=True) s.name = childCtx.scope.checkedName(suggestedName, s) s.hidden = False serializedS = cls.SignalItem(s, childCtx, declaration=True) extraVars.append(s) extraVarsSerialized.append(serializedS) return s
def withRes(opDef, operands, resT, outputs=[]): """ Create operator with result signal """ op = Operator(opDef, operands) out = RtlSignal(getCtxFromOps(operands), None, resT) out.drivers.append(op) out.origin = op op.result = out op.registerSignals(outputs) return out
def test_BitsIndexTypes(self): t = Bits(8) v = t.fromPy(1) with self.assertRaises(TypeError): v[object()] with self.assertRaises(IndexError): v[9:] with self.assertRaises(IndexError): v[:-1] p = Param(2) self.assertIsInstance(v[p], RtlSignalBase) self.assertEqual(v[p]._dtype.bit_length(), 1) p2 = p._downto(0) self.assertIsInstance(v[p2], RtlSignalBase) self.assertEqual(v[p2]._dtype.bit_length(), 2) p3 = Param("abc") with self.assertRaises(TypeError): v[p3] a = RtlSignal(None, "a", BIT) a._const = False with self.assertRaises(TypeError): v[p] = a with self.assertRaises(TypeError): v[a] = p v[p] = 1 self.assertEqual(v, 5) v[p2] = 2 self.assertEqual(v, 6) with self.assertRaises(TypeError): v[hInt(None)] = 2 v[:] = 0 self.assertEqual(v, 0) v[2] = 1 self.assertEqual(v, 4) v[3:] = p self.assertEqual(v, 2) v._setitem__val(hInt(None), hInt(1)) with self.assertRaises(ValueError): int(v) with self.assertRaises(TypeError): v[hStr("asfs")]
def test_BitsIndexTypes(self): t = Bits(8) v = t.fromPy(1) with self.assertRaises(TypeError): v[object()] with self.assertRaises(IndexError): v[9:] with self.assertRaises(IndexError): v[:-1] p = Param(2) self.assertIsInstance(v[p], RtlSignalBase) self.assertEqual(v[p]._dtype.bit_length(), 1) p2 = p._downto(0) self.assertIsInstance(v[p2], RtlSignalBase) self.assertEqual(v[p2]._dtype.bit_length(), 2) p3 = Param("abc") with self.assertRaises(TypeError): v[p3] a = RtlSignal(None, "a", BIT) a._const = False with self.assertRaises(TypeError): v[p] = a with self.assertRaises(TypeError): v[a] = p v[p] = 1 self.assertEqual(v, 5) v[p2] = 2 self.assertEqual(v, 6) with self.assertRaises(TypeError): v[hInt(None)] = 2 v[:] = 0 self.assertEqual(v, 0) v[2] = 1 self.assertEqual(v, 4) v[3:] = p self.assertEqual(v, 2) v._setitem__val(hInt(None), hInt(1)) with self.assertRaises(ValueError): int(v) with self.assertRaises(TypeError): v[hStr("asfs")]
def sig(self, name, dtype=BIT, clk=None, syncRst=None, def_val=None, nop_val=NO_NOPVAL) -> Union[RtlSignal, RtlSyncSignal]: """ Create new signal in this context :param clk: clk signal, if specified signal is synthesized as SyncSignal :param syncRst: synchronous reset signal :param def_val: default value used for reset and intialization :param nop_val: value used a a driver if signal is not driven by any driver """ _def_val = self._try_cast_any_to_HdlType(def_val, dtype) if nop_val is not NO_NOPVAL: nop_val = self._try_cast_any_to_HdlType(nop_val, dtype) if clk is not None: s = RtlSyncSignal(self, name, dtype, _def_val, nop_val) if syncRst is not None and def_val is None: raise SigLvlConfErr( "Probably forgotten default value on sync signal %s", name) if syncRst is not None: r = If(syncRst._isOn(), RtlSignal.__call__(s, _def_val)).Else( RtlSignal.__call__(s, s.next)) else: r = [RtlSignal.__call__(s, s.next)] if isinstance(clk, (InterfaceBase, RtlSignal)): clk_trigger = clk._onRisingEdge() else: # has to be tuple of (clk_sig, AllOps.RISING/FALLING_EDGE) clk, clk_edge = clk if clk_edge is AllOps.RISING_EDGE: clk_trigger = clk._onRisingEdge() elif clk_edge is AllOps.FALLING_EDGE: clk_trigger = clk._onRisingEdge() else: raise ValueError("Invalid clock edge specification", clk_edge) If(clk_trigger, r) else: if syncRst: raise SigLvlConfErr("Signal %s has reset but has no clk" % name) s = RtlSignal(self, name, dtype, def_val=_def_val, nop_val=nop_val) return s
def count_leading(cls, data_in: RtlSignal, data_out: RtlSignal, bit_to_count: int): in_width = data_in._dtype.bit_length() assert bit_to_count in (0, 1), bit_to_count if bit_to_count == 0: full = data_in._eq(0) else: full = data_in._eq(mask(in_width)) half_count = cls._count_leading_recurse(data_in, bit_to_count) If( full, data_out(in_width), ).Else(data_out(Concat(BIT.from_py(0), half_count)))
def makeTestbenchTemplate(unit: Unit, name: str=None): """ :param unit: synthesized unit :return: (entity, arch, context) of testbench """ if name is None: name = unit._name + "_tb" entity = Entity(name) arch = Architecture(entity) arch.components.append(unit._entity) arch.componentInstances.append(unit._entity) nl = RtlNetlist() ctx = {} for p in unit._entity.ports: t = p._dtype if isinstance(t, Bits) and not t == BIT: t = Bits(t.bit_length(), signed=t.signed, forceVector=t.forceVector) s = RtlSignal(nl, p.name, t, t.fromPy(0)) ctx[p._interface] = s p.connectSig(s) arch.variables.extend(ctx.values()) return entity, arch, ctx
def connect_directly_mapped_write(self, aw_addr: RtlSignal, w_data: RtlSignal, en: RtlSignal): """ Connect the RegCntrl.dout interfaces to a bus """ DW = int(self.DATA_WIDTH) addrWidth = int(self.ADDR_WIDTH) ADDR_STEP = self._getAddrStep() for w_i, items in self._directly_mapped_words: for tpart in items: if tpart.tmpl is None: # padding continue out = self.getPort(tpart.tmpl) if not isinstance(out, RegCntrl): continue else: out = out.dout field_range = tpart.getFieldBitRange() if field_range != (out.DATA_WIDTH, 0): raise NotImplementedError( "Write on field not aligned to a word boundary", tpart) bus_range = tpart.getBusWordBitRange() out.data(w_data[bus_range[0]:bus_range[1]]) addr = w_i * (DW // ADDR_STEP) out.vld(en & (aw_addr._eq(Bits(addrWidth).from_py(addr))))
def sig(self, name, dtype=BIT, clk=None, syncRst=None, def_val=None): """ Create new signal in this context :param clk: clk signal, if specified signal is synthesized as SyncSignal :param syncRst: synchronous reset signal """ if isinstance(def_val, RtlSignal): assert def_val._const, \ "Initial value of register has to be constant" _def_val = def_val._auto_cast(dtype) elif isinstance(def_val, Value): _def_val = def_val._auto_cast(dtype) elif isinstance(def_val, InterfaceBase): _def_val = def_val._sig else: _def_val = dtype.from_py(def_val) if clk is not None: s = RtlSyncSignal(self, name, dtype, _def_val) if syncRst is not None and def_val is None: raise SigLvlConfErr( "Probably forgotten default value on sync signal %s", name) if syncRst is not None: r = If(syncRst._isOn(), RtlSignal.__call__(s, _def_val) ).Else( RtlSignal.__call__(s, s.next) ) else: r = [RtlSignal.__call__(s, s.next)] If(clk._onRisingEdge(), r ) else: if syncRst: raise SigLvlConfErr( "Signal %s has reset but has no clk" % name) s = RtlSignal(self, name, dtype, def_val=_def_val) self.signals.add(s) return s
def __init__(self, ctx, name, var_type, defaultVal=None): """ :param ctx: context in which is sig. created (instance of RtlNetlist) :param name: suggested name :param var_type: type of signal :param defaultVal: default value for signal (used as def. val in hdl and for reset) """ super().__init__(ctx, name, var_type, defaultVal) self.next = RtlSignal(ctx, name + "_next", var_type, nopVal=self, useNopVal=True)
def sig(self, name, dtype=BIT, clk=None, syncRst=None, def_val=None, nop_val=NO_NOPVAL) -> Union[RtlSignal, RtlSyncSignal]: """ Create new signal in this context :param clk: clk signal, if specified signal is synthesized as SyncSignal :param syncRst: synchronous reset signal :param def_val: a default value used for reset and intialization :param nop_val: a value which is used to drive the signal if there is no other drive (used to prevent latches and to specify default values for unconnected signals) """ _def_val = self._try_cast_any_to_HdlType(def_val, dtype) if nop_val is not NO_NOPVAL: nop_val = self._try_cast_any_to_HdlType(nop_val, dtype) if clk is not None: s = RtlSyncSignal(self, name, dtype, _def_val, nop_val) if syncRst is not None and def_val is None: raise SigLvlConfErr( "Probably forgotten default value on sync signal %s", name) # dst_resolve_fn is overriden because default assign would assign to the "next" signal if syncRst is not None: r = If(syncRst._isOn(), s(_def_val, dst_resolve_fn=lambda x: x)).Else( s(s.next, dst_resolve_fn=lambda x: x)) else: r = [s(s.next, dst_resolve_fn=lambda x: x)] if isinstance(clk, (InterfaceBase, RtlSignal)): clk_trigger = clk._onRisingEdge() else: # has to be tuple of (clk_sig, AllOps.RISING/FALLING_EDGE) clk, clk_edge = clk if clk_edge is AllOps.RISING_EDGE: clk_trigger = clk._onRisingEdge() elif clk_edge is AllOps.FALLING_EDGE: clk_trigger = clk._onRisingEdge() else: raise ValueError("Invalid clock edge specification", clk_edge) If(clk_trigger, r) else: if syncRst: raise SigLvlConfErr("Signal %s has reset but has no clk" % name) s = RtlSignal(self, name, dtype, def_val=_def_val, nop_val=nop_val) return s
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)
def __init__(self, ctx, name, var_type, def_val=None, nop_val=NO_NOPVAL): """ :param ~.ctx: context in which is sig. created (instance of RtlNetlist) :param ~.name: suggested name :param ~.var_type: type of signal :param ~.def_val: default value for signal (used as def. val in hdl and for reset) """ super().__init__(ctx, name, var_type, def_val) if nop_val is NO_NOPVAL: nop_val = self self.next = RtlSignal(ctx, name + "_next", var_type, nop_val=nop_val)
def connect_port(clk: RtlSignal, port: BramPort_withoutClk, mem: RtlSignal): if port.HAS_R and port.HAS_W: If(clk._onRisingEdge(), If(port.en, *RamSingleClock.mem_write(mem, port), port.dout(mem[port.addr]) ) ) elif port.HAS_R: If(clk._onRisingEdge(), If(port.en, port.dout(mem[port.addr]) ) ) elif port.HAS_W: If(clk._onRisingEdge(), If(port.en, *RamSingleClock.mem_write(mem, port), ) ) else: raise AssertionError("Bram port has to have at least write or read part")
def createTmpVarInCurrentBlock(suggestedName, dtype, const=False, def_val=None): # create a new tmp variable in current process s = RtlSignal(None, None, dtype, virtual_only=True) s.name = self.name_scope.checked_name(suggestedName, s) s.hidden = False s._const = const if def_val is not None: s.def_val = def_val s._set_def_val() as_hdl = self.as_hdl_SignalItem(s, declaration=True) extraVars.append(s) extraVarsHdl.append(as_hdl) return s
def sig(self, name, typ=BIT, clk=None, syncRst=None, defVal=None): """ generate new signal in context :param clk: clk signal, if specified signal is synthesized as SyncSignal :param syncRst: reset """ if not isinstance(defVal, (Value, RtlSignal, InterfaceBase)): if isinstance(defVal, (InterfaceBase)): _defVal = defVal._sig else: _defVal = typ.fromPy(defVal) else: _defVal = defVal._convert(typ) if clk is not None: s = RtlSyncSignal(self, name, typ, _defVal) if syncRst is not None and defVal is None: raise Exception( "Probably forgotten default value on sync signal %s", name) if syncRst is not None: r = If(syncRst._isOn(), [RtlSignal.__pow__(s, _defVal)]).Else( [RtlSignal.__pow__(s, s.next)]) else: r = [RtlSignal.__pow__(s, s.next)] If(clk._onRisingEdge(), r) else: if syncRst: raise SigLvlConfErr("Signal %s has reset but has no clk" % name) s = RtlSignal(self, name, typ, defaultVal=_defVal) self.signals.add(s) return s
def withRes(opDef, operands, resT): """ Create operator with result signal :ivar ~.resT: data type of result signal :ivar ~.outputs: iterable of signals which are outputs from this operator """ # try return existing operator result for i, o in enumerate(operands): if isinstance(o, RtlSignalBase): if i == 0: k = (opDef, i, *operands[1:]) else: k = (opDef, i, *operands[:i], *operands[i + 1:]) try: return o._usedOps[k] except KeyError: pass break # instanciate new Operator op = Operator(opDef, operands) out = RtlSignal(getCtxFromOps(operands), None, resT) out._const = arr_all(op.operands, isConst) out.drivers.append(op) out.origin = op op.result = out # Register potential signals to drivers/endpoints first_signal = True for i, o in enumerate(op.operands): if isinstance(o, RtlSignalBase): o.endpoints.append(op) if first_signal: # register operator in _usedOps operator cache if i == 0: k = (opDef, i, *operands[1:]) else: k = (opDef, i, *operands[:i], *operands[i + 1:]) o._usedOps[k] = out o._usedOpsAlias[k] = { k, } first_signal = False elif isinstance(o, HValue): pass else: raise NotImplementedError("Operator operands can be" " only signal or values got:%r" % (o)) if out._const: out.staticEval() return out
def create_var(self, suggestedName: str, dtype: HdlType, const=False, def_val: Optional[Union[RtlSignalBase, HValue]] = None, postponed_init=False) -> RtlSignal: # create a new tmp variable in current process s = RtlSignal(None, None, dtype, virtual_only=True) s.name = self.name_scope.checked_name(suggestedName, s) s.hidden = False s._const = const if def_val is not None: s.def_val = def_val if not (isinstance(def_val, RtlSignalBase) and not def_val._const): s._set_def_val() if not postponed_init: self.finish_var_init(s) return s
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 withRes(opDef, operands, resT, outputs=[]): """ Create operator with result signal :ivar resT: data type of result signal :ivar outputs: iterable of singnals which are outputs from this operator """ op = Operator(opDef, operands) out = RtlSignal(getCtxFromOps(operands), None, resT) out._const = arr_all(op.operands, isConst) out.drivers.append(op) out.origin = op op.result = out op.registerSignals(outputs) if out._const: out.staticEval() return out
def split_subaddr(self, addr: RtlSignal): assert self.data_words_in_axi_word > 1, "Should be called only if there are multiple data words in single axi word" in_word_sub_addr_bits = log2ceil(self.data_words_in_axi_word - 1) main_addr = addr & bit_field(in_word_sub_addr_bits, addr._bit_length()) sub_addr = addr[in_word_sub_addr_bits:] return main_addr, sub_addr
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 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,