Пример #1
0
 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
Пример #2
0
    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
Пример #3
0
    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")]
Пример #4
0
    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")]
Пример #5
0
    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
Пример #6
0
    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)))
Пример #7
0
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
Пример #8
0
    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))))
Пример #9
0
    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
Пример #10
0
 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)
Пример #11
0
    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
Пример #12
0
    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)
Пример #13
0
 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)
Пример #14
0
 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")
Пример #15
0
        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
Пример #16
0
    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
Пример #17
0
    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
Пример #18
0
    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
Пример #19
0
    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})]
                    )
Пример #20
0
    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
Пример #21
0
 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
Пример #22
0
Файл: r.py Проект: mfkiwl/hwtLib
    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)
                )
Пример #23
0
    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,