class AxiS_FrameJoin_3x_in_1B_on_5B_TC(AxiS_FrameJoin_1x_2B_TC): D_B = 5 T = HStruct( (HStream(Bits(8 * 1), (1, inf), [0]), "frame0"), (HStream(Bits(8 * 1), (1, inf), [0]), "frame1"), (HStream(Bits(8 * 1), (1, inf), [0]), "frame2"), )
def _config(self): self.T = Param(HStruct( (HStream(Bits(8), frame_len=(1, inf), start_offsets=[0]), "f0"), (HStream(Bits(16), frame_len=(1, 1)), "f1"), )) AxiStream._config(self) self.DATA_WIDTH = 16 self.USE_KEEP = True self.OUT_OFFSET = Param(0)
def test_separate_streams_simple(self): t = HStruct( (HStream(Bits(8)), "data"), (Bits(32), "footer"), ) sep = list(separate_streams(t)) self.assertSequenceEqual(sep, [(True, HStruct( (HStream(Bits(8)), "data"), )), (False, HStruct((Bits(32), "footer"), ))])
def to_primitive_stream_t(t: HdlType): """ Convert type to a HStream of Bits With proper frame len, offset etc. """ if isinstance(t, HStruct) and len(t.fields) == 1: return to_primitive_stream_t(t.fields[0].dtype) frame_len = (1, 1) start_offsets = [0, ] if isinstance(t, HStream): e_t = t.element_t if isinstance(e_t, Bits): return t else: frame_len = t.frame_len start_offsets = t.start_offsets t = e_t try: bit_len = t.bit_length() except TypeError: bit_len = None if bit_len is not None: return HStream(Bits(bit_len), frame_len=frame_len, start_offsets=start_offsets) else: raise NotImplementedError(t)
def test_fsm0(self): word_bytes = 2 f_len = (1, 1) streams = [ HStream(Bits(8), frame_len=f_len, start_offsets=[1]), ] out_offset = 0 sju = FrameAlignmentUtils(word_bytes, out_offset) input_B_dst = sju.resolve_input_bytes_destinations(streams) tt = input_B_dst_to_fsm(word_bytes, len(streams), input_B_dst) def st(d): return StateTransItem.from_dict(tt, d) ref = [ [ st({ 'st': '0->0', 'in': [[{ 'keep': [0, 1], 'relict': 0, 'last': 1 }]], 'in.keep_mask': [[[0, 0]]], 'in.rd': [1], 'out.keep': [1, 0], 'out.mux': [(0, 0, 1), None], 'out.last': 1 }) ], ] self.assertSequenceEqual(tt.state_trans, ref)
def get_packet_data_t(usb_ver: USB_VER): max_frame_len = USB_MAX_FRAME_LEN[usb_ver] # pid has to be one of DATA_0, DATA_1, DATA_2, DATA_M return HStruct( (pid_t, "pid"), (HStream(Bits(8), frame_len=(1, max_frame_len)), "data"), (crc16_t, "crc"), )
def test_fsm_1x3B_on_2B_offset_1(self): word_bytes = 2 streams = [ HStream(Bits(8 * 3), (1, 1), [1]), ] out_offset = 0 sju = FrameAlignmentUtils(word_bytes, out_offset) input_B_dst = sju.resolve_input_bytes_destinations(streams) tt = input_B_dst_to_fsm(word_bytes, len(streams), input_B_dst) def st(d): return StateTransItem.from_dict(tt, d) ref = [[ st({ 'st': '0->0', 'in': [[{ 'keep': [0, 1], 'relict': 1, 'last': 1 }, { 'keep': ['X', 'X'], 'relict': 'X', 'last': 'X' }]], 'in.keep_mask': [[[0, 0], [1, 1]]], 'in.rd': [1], 'out.keep': [1, 0], 'out.mux': [(0, 0, 1), None], 'out.last': 1 }), st({ 'st': '0->0', 'in': [[{ 'keep': [0, 1], 'relict': 'X', 'last': 0 }, { 'keep': [1, 1], 'relict': 'X', 'last': 1 }]], 'in.keep_mask': [[[0, 0], [0, 1]]], 'in.rd': [1], 'out.keep': [1, 1], 'out.mux': [(0, 0, 1), (0, 1, 0)], 'out.last': 0 }) ]] self.assertSequenceEqual(tt.state_trans, ref)
def _example_AxiS_frameParser(): from hwtLib.types.ctypes import uint8_t, uint16_t, uint32_t, uint64_t # t = HStruct( # (uint64_t, "item0"), # tuples (type, name) where type has to be instance of Bits type # (uint64_t, None), # name = None means this field will be ignored # (uint64_t, "item1"), # (uint64_t, None), # (uint16_t, "item2"), # (uint16_t, "item3"), # (uint32_t, "item4"), # (uint32_t, None), # (uint64_t, "item5"), # this word is split on two bus words # (uint32_t, None), # (uint64_t, None), # (uint64_t, None), # (uint64_t, None), # (uint64_t, "item6"), # (uint64_t, "item7"), # (HStruct( # (uint64_t, "item0"), # (uint64_t, "item1"), # ), # "struct0") # ) #t = HUnion( # (uint32_t, "a"), # (uint32_t, "b") # ) # t = HUnion( # (HStruct( # (uint64_t, "itemA0"), # (uint64_t, "itemA1") # ), "frameA"), # (HStruct( # (uint32_t, "itemB0"), # (uint32_t, "itemB1"), # (uint32_t, "itemB2"), # (uint32_t, "itemB3") # ), "frameB") # ) t = HStruct( (HStream(uint8_t), "frame0"), (uint16_t, "footer") ) u = AxiS_frameParser(t) u.USE_STRB = True u.DATA_WIDTH = 32 return u
def test_const_size_stream(self, dataWidth, frame_len, randomize): T = HStruct( (HStream(Bits(8), frame_len=frame_len), "frame0"), (uint16_t, "footer"), ) u = self.mySetUp(dataWidth, T, randomize, use_strb=True) u.dataIn._ag.data.extend( packAxiSFrame(dataWidth, T.from_py({"frame0": [i + 1 for i in range(frame_len)], "footer": 2}), withStrb=True, ) ) t = 20 if randomize: t *= 3 self.runMatrixSim2(t, dataWidth, frame_len, randomize) off, f = axis_recieve_bytes(u.dataOut.frame0) self.assertEqual(off, 0) self.assertValSequenceEqual(f, [i + 1 for i in range(frame_len)]) self.assertValSequenceEqual(u.dataOut.footer._ag.data, [2])
def test_stream_and_footer(self, dataWidth, frame_len, prefix_suffix_as_padding, randomize): """ :note: Footer separation is tested in AxiS_footerSplitTC and this test only check that the AxiS_frameParser can connect wires correctly """ prefix_padding, suffix_padding = prefix_suffix_as_padding T = HStruct( (HStream(Bits(8)), "frame0"), (uint16_t, "footer"), ) fieldsToUse = set() if not prefix_padding: fieldsToUse.add("frame0") if not suffix_padding: fieldsToUse.add("footer") _T = HdlType_select(T, fieldsToUse) u = self.mySetUp(dataWidth, _T, randomize, use_strb=True) v = T.from_py({ "frame0": [i + 1 for i in range(frame_len)], "footer": frame_len + 1 }) u.dataIn._ag.data.extend( packAxiSFrame(dataWidth, v, withStrb=True) ) t = 20 if randomize: t *= 3 self.runMatrixSim2(t, dataWidth, frame_len, randomize) if not prefix_padding: off, f = axis_recieve_bytes(u.dataOut.frame0) self.assertEqual(off, 0) self.assertValSequenceEqual(f, [i + 1 for i in range(frame_len)]) if not suffix_padding: self.assertValSequenceEqual(u.dataOut.footer._ag.data, [frame_len + 1])
1 |<union>| 1 | a.a1 | 1 | b | --------- >""" union0_16b_str = """<FrameTmpl start:0, end:16 15 0 ----------------- 0 | <union> | 0 | a.a1 | a.a0 | 0 | b | ----------------- >""" stream0 = HStream(uint8_t, frame_len=2) stream0_8bit_str = """\ <FrameTmpl start:0, end:16 7 0 --------- 0 | [0] | 1 | [1] | --------- >""" stream0_16bit_str = """<FrameTmpl start:0, end:16 15 0 ----------------- 0 | [1] | [0] | -----------------
), "frameA"), (HStruct( (uint32_t, "itemB0"), (uint32_t, "itemB1"), (uint32_t, "itemB2"), (uint32_t, "itemB3") ), "frameB") ) unionSimple = HUnion( (uint32_t, "a"), (int32_t, "b") ) structStream64 = HStruct( (HStream(uint64_t), "streamIn") ) structStream64before = HStruct( (HStream(uint64_t), "streamIn"), (uint64_t, "item0"), ) structStream64after = HStruct( (uint64_t, "item0"), (HStream(uint64_t), "streamIn"), ) struct2xStream64 = HStruct( (HStream(uint64_t), "streamIn0"), (HStream(uint64_t), "streamIn1")
def _impl(self): regs = [] keep_masks = [] ready = [] max_lookahead_for_input = self.state_trans_table.max_lookahead_for_input # lookahead specifies how many words from inputs has to be loaded # in order to resolve output word, it corresponds to a number of input registers-1 for input_i, stage_cnt in enumerate(max_lookahead_for_input): _regs, _keep_masks, _ready = self.generate_input_register( input_i, stage_cnt + 1) regs.append(_regs) keep_masks.append(_keep_masks) ready.append(_ready) out_sel, out_mux_values = self.generate_output_byte_mux(regs) self.generate_fsm(regs, out_sel, out_mux_values, keep_masks, ready) propagateClkRstn(self) if __name__ == "__main__": from hwt.synthesizer.utils import to_rtl_str u = AxiS_FrameJoin() D_B = 4 u.DATA_WIDTH = 8 * D_B u.USE_STRB = True u.T = HStruct( (HStream(Bits(8*1), (1, inf), [0, 1, 2, 3]), "frame0"), (HStream(Bits(8*4), (1, 1), [0]), "frame1"), ) print(to_rtl_str(u))
class AxiS_FrameJoin_1x_2B_TC(SimTestCase): D_B = 2 FRAME_CNT = 1 T = HStruct( (HStream(Bits(8 * D_B), (1, inf), [0]), "frame0"), ) @classmethod def setUpClass(cls): u = cls.u = AxiS_FrameJoin() u.T = cls.T u.DATA_WIDTH = cls.D_W = cls.D_B * 8 cls.compileSim(u) def send(self, input_i, data_B, offset): axis_send_bytes(self.u.dataIn[input_i], data_B, offset=offset) def recive(self): return axis_recieve_bytes(self.u.dataOut) def randomize_all(self): for din in self.u.dataIn: self.randomize(din) self.randomize(self.u.dataOut) def gen_data(self, data_cntr, size): return [(data_cntr + d) & 0xff for d in range(size)] def test_nop(self): self.randomize_all() self.runSim(CLK_PERIOD * 20) self.assertEmpty(self.u.dataOut._ag.data) def _test_pass_data(self, IN_FRAMES): OUT_FRAMES = [] for f_i in range(len(IN_FRAMES[0])): offset = self.u.OUT_OFFSET data = [] for in_frames in IN_FRAMES: fdata = in_frames[f_i][1] data.extend(fdata) OUT_FRAMES.append((offset, data)) for i, frames in enumerate(IN_FRAMES): for f_offset, frame in frames: self.send(i, frame, offset=f_offset) self.runSim(CLK_PERIOD * ( len(IN_FRAMES) * len(OUT_FRAMES[0]) * 20 + 100)) for (ref_offset, ref_frame) in OUT_FRAMES: offset, frame = axis_recieve_bytes(self.u.dataOut) self.assertEqual(offset, ref_offset) self.assertSequenceEqual(frame, ref_frame) self.assertEmpty(self.u.dataOut._ag.data) def test_pass_data_min_len(self, repeat=10): self.randomize_all() # [offset, [data]] IN_FRAMES = [[] for _ in self.u.dataIn] data_cntr = 0 for _ in range(repeat): for f, frames in zip(self.T.fields, IN_FRAMES): data_B = f.dtype.element_t.bit_length() // 8 lmin = f.dtype.len_min # lmax = f.dtype.len_max # if len(f.dtype.start_offsets) != 1: # raise NotImplementedError() for offset in f.dtype.start_offsets: size = data_B * lmin data = self.gen_data(data_cntr, size) frames.append((offset, data)) data_cntr += size self._test_pass_data(IN_FRAMES) def test_pass_data_min_len_plus1(self, repeat=10): self.randomize_all() # [offset, [data]] IN_FRAMES = [[] for _ in self.u.dataIn] data_cntr = 0 for _ in range(repeat): for f, frames in zip(self.T.fields, IN_FRAMES): data_B = f.dtype.element_t.bit_length() // 8 lmin = f.dtype.len_min + 1 lmax = f.dtype.len_max if lmin > lmax: lmin = lmax # if len(f.dtype.start_offsets) != 1: # raise NotImplementedError() for offset in f.dtype.start_offsets: size = data_B * lmin data = self.gen_data(data_cntr, size) frames.append((offset, data)) data_cntr += size self._test_pass_data(IN_FRAMES) def test_pass_data_min_len_plus1_for_non_first(self, repeat=10): if len(self.u.dataIn) == 1: # test tested by test_pass_data_min_len return self.randomize_all() # [offset, [data]] IN_FRAMES = [[] for _ in self.u.dataIn] data_cntr = 0 for _ in range(repeat): for i, (f, frames) in enumerate(zip(self.T.fields, IN_FRAMES)): data_B = f.dtype.element_t.bit_length() // 8 lmax = f.dtype.len_max lmin = f.dtype.len_min if i > 0: lmin += 1 if lmin > lmax: lmin = lmax # if len(f.dtype.start_offsets) != 1: # raise NotImplementedError() for offset in f.dtype.start_offsets: size = data_B * lmin data = self.gen_data(data_cntr, size) frames.append((offset, data)) data_cntr += size self._test_pass_data(IN_FRAMES)
class AxiS_FrameJoin_1x_2B_len1_TC(AxiS_FrameJoin_1x_2B_TC): D_B = 2 T = HStruct( (HStream(Bits(8 * D_B), (1, 1), [0]), "frame0"), )
def dataHandler(self, rErrFlag: RtlSignal, rmSizeOut: TransEndInfo): rIn = self.axi.r rOut = self.driver.r if self.axi.LEN_WIDTH: last = rIn.last else: last = BIT.from_py(1) rInLast = last if self.useTransSplitting(): last = rmSizeOut.propagateLast & last if self.isAlwaysAligned(): # without shift logic *([ self.remSizeToStrb(rmSizeOut.rem, rOut.strb, False, rIn.valid & last), ] if self.USE_STRB else []), rOut.data(rIn.data) rOut.last(last) StreamNode(masters=[rIn, rmSizeOut], slaves=[rOut], extraConds={ rmSizeOut: rInLast, rOut: ~rErrFlag }).sync() else: # align shifted incoming read data and optionally merge frames aligner = AxiS_FrameJoin() aligner.T = HStruct((HStream( Bits(self.CHUNK_WIDTH), start_offsets=[i // 8 for i in self.getShiftOptions()], frame_len=(1, self.MAX_CHUNKS)), "f0"), ) aligner.USE_STRB = False aligner.DATA_WIDTH = self.DATA_WIDTH self.aligner = aligner isSingleWordOnly = self.CHUNK_WIDTH * self.MAX_CHUNKS <= self.DATA_WIDTH and self.ALIGNAS % ( self.CHUNK_WIDTH * self.MAX_CHUNKS) == 0 if isSingleWordOnly: first = BIT.from_py(1) else: # first beat of output frame (not necessary input frame, as multiple input # frames could be merged in to a single output frame) first = self._reg(f"first", def_val=1) If( StreamNode([rIn, rmSizeOut], [ aligner.dataIn[0], ]).ack(), first(last), ) aligner.dataIn[0].data(rIn.data) aligner.dataIn[0].last(last) self.remSizeToStrb(rmSizeOut.rem, aligner.dataIn[0].keep, first, last) StreamNode([rIn, rmSizeOut], [ aligner.dataIn[0], ], extraConds={ rmSizeOut: ~rErrFlag & rInLast, }).sync() if self.USE_STRB: rOut.strb(aligner.dataOut.keep) rOut.data(aligner.dataOut.data) rOut.last(aligner.dataOut.last) StreamNode(masters=[ aligner.dataOut, ], slaves=[rOut], extraConds={ rOut: ~rErrFlag }).sync()
def delegate_to_children(self): if self.SHARED_READY: raise NotImplementedError() assert len(self.sub_t) == len(self.children),\ (self.sub_t, self.children) if self.OVERFLOW_SUPPORT: raise NotImplementedError() din = self.dataIn if len(self.children) == 2: c0, c1 = self.children t0, t1 = self.sub_t t0_is_padding, t1_is_padding = self.sub_t_is_padding t0_const_sized, t1_const_sized = self.sub_t_is_const_sized if not t0_const_sized and t1_const_sized: # suffix parser, split suffix and parse it in child sub component fs = AxiS_footerSplit() fs._updateParamsFrom(self) fs.FOOTER_WIDTH = t1.bit_length() self.footer_split = fs fs.dataIn(din) prefix_t, prefix = drill_down_in_HStruct_fields(t0, self.dataOut) if t0_is_padding: # padding fs.dataOut[0].ready(1) else: assert isinstance(prefix_t, HStream), prefix_t prefix(fs.dataOut[0]) suffix = fs.dataOut[1] if t1_is_padding: # ignore suffix entirely suffix.ready(1) else: # parse suffix in child component suffix_offsets = next_frame_offsets(prefix_t, self.DATA_WIDTH) if suffix_offsets != [0, ]: # add aligment logic align = AxiS_FrameJoin() align._updateParamsFrom( self, exclude=({"T"}, {})) align.USE_KEEP = True align.USE_STRB = False align.OUT_OFFSET = 0 align.T = HStruct( (HStream(t1, frame_len=1, start_offsets=[x // 8 for x in suffix_offsets]), "f0")) self.suffix_align = align align.dataIn[0](suffix, exclude=[suffix.strb, align.dataIn[0].keep]) align.dataIn[0].keep(suffix.strb) suffix = align.dataOut c1.dataIn(suffix, exclude=[suffix.keep, c1.dataIn.strb]) c1.dataIn.strb(suffix.keep) else: c1.dataIn(suffix) if not t1_is_padding: connect_optional(c1.dataOut, self.dataOut) elif t0_const_sized and not t1_const_sized: # prefix parser, parser prefix in subcomponent # and let rest to a suffix if not t0_is_padding: connect_optional(c0.dataOut, self.dataOut) masters = [din] if t1_is_padding: slaves = [c0.dataIn, ] extraConds = { c0.dataIn: ~c0.parsing_overflow, } skipWhen = { c0.dataIn: ~c0.parsing_overflow, } else: suffix_t, suffix = drill_down_in_HStruct_fields(t1, self.dataOut) assert isinstance(suffix_t, HStream), suffix_t t1_offset = c0._structT.bit_length() % self.DATA_WIDTH if t1_offset == 0: # t1 is aligned on word boundary # and does not require any first word mask modification slaves = [c0.dataIn, suffix] extraConds = { c0.dataIn: ~c0.parsing_overflow, suffix: c0.parsing_overflow, } skipWhen = { c0.dataIn: ~c0.parsing_overflow, suffix: c0.parsing_overflow, } else: raise NotImplementedError("prefix parser- modify mask for suffix") StreamNode(masters, slaves, extraConds=extraConds, skipWhen=skipWhen).sync() c0.dataIn(din, exclude=[din.valid, din.ready]) else: raise NotImplementedError("multiple con-constant size segments") else: raise NotImplementedError("multiple con-constant size segments") propagateClkRstn(self)
class AxiS_FrameJoin_2x_1B_TC(AxiS_FrameJoin_1x_2B_TC): D_B = 1 T = HStruct( (HStream(Bits(8 * D_B), (1, inf), [0]), "frame0"), (HStream(Bits(8 * D_B), (1, inf), [0]), "frame1"), )
class AxiS_FrameJoin_1x_in_2B_offset_0__1_TC(AxiS_FrameJoin_1x_2B_TC): D_B = 2 T = HStruct( (HStream(Bits(8 * D_B), (1, inf), [0]), "frame0"), (HStream(Bits(8 * D_B), (1, inf), [1]), "frame1"), )
class AxiS_FrameJoin_1x_3B_offset_0_1_TC(AxiS_FrameJoin_1x_2B_TC): D_B = 2 T = HStruct( (HStream(Bits(8 * 3), (1, 1), [0, 1, ]), "frame0"), )
class AxiS_FrameJoin_2x_1B_on_2B_offset_1_1_TC(AxiS_FrameJoin_1x_2B_TC): D_B = 2 T = HStruct( (HStream(Bits(8 * 1), (1, inf), [1]), "frame0"), (HStream(Bits(8 * 1), (1, inf), [1]), "frame1"), )
def test_separate_streams_no_fotter(self): t = HStruct((HStream(Bits(8)), "data"), ) sep = list(separate_streams(t)) self.assertSequenceEqual(sep, [ (True, t), ])
def axiS_strFormat(parent: Unit, name: str, data_width: int, format_str: str, *args, **kwargs): """ Instanciate an :class:`hwtLib.amba.axis_comp.strformat.AxiS_strFormat` using simplified str.format syntax The syntax is allows for an utf-8 string with a variable format groups and several escape sequences in addition to normal string escape sequences. The escape sequences are (same as :func:`str.format`) +=======+=================+ | char | escape sequence | +=======+=================+ | { | {{ | +-------+-----------------+ | } | }} | +-------+-----------------+ The syntax for format group is as folowing: .. code-block:: text {[index/name]:[nuber_of_digits][type]} * The index or name specifies the name or the index of the input parameter. * The width specifies how mahy digits should the output have. * Format types can be found at :class:`hwtLib.amba.axis_comp.strformat.AxiS_strFormatItem` * If nuber_of_digits starts with 0 the leading zeros will be used instead of default space char (' ') * The sign char is included in nuber_of_digits ('{0:04X}'.format(-1) == '-001') * The type is described in :class:`hwtLib.amba.axis_comp.strformat.AxiS_strFormatItem` """ f = AxiS_strFormat() f.DATA_WIDTH = data_width # construct input t for configuration arg_prefix = "arg_" while True: arg_names = [f"{arg_prefix}{a_i}" for a_i, _ in enumerate(args)] if not kwargs: break else: colliding = False for a in arg_names: if a in kwargs.keys(): colliding = True if colliding: arg_prefix = f"{arg_prefix}0_" else: break in_intf_name_tuples = [ *zip(args, arg_names), *[(a, a_name) for a_name, a in sorted(kwargs.items(), key=lambda x: x[0])] ] format_items = tuple(_parse_format_groups(format_str)) for fi in format_items: if isinstance(fi, str): continue elif isinstance(fi.member_path[0], int): # convert arg index to name in input interface fi.member_path = TypePath(arg_names[fi.member_path[0]]) arg_usage = {} for fi in format_items: if isinstance(fi, str): continue usage_cnt = arg_usage.get(fi.member_path, 0) + 1 arg_usage[fi.member_path] = usage_cnt if fi.format_type == 's': assert usage_cnt == 1, ( "string arguments may be used only once as string is consumed") for i, (a, a_name) in enumerate(in_intf_name_tuples): assert arg_usage.get(TypePath(a_name), 0) > 0, ( "arg ", i, " named ", a_name, " not used during formating") if in_intf_name_tuples: struct_members = [] for a, a_name in in_intf_name_tuples: if isinstance(a, AxiStream): t = HStream(Bits(8), start_offsets=[0]) else: t = Interface_to_HdlType().apply(a) struct_members.append((t, a_name)) f.INPUT_T = HStruct(*struct_members) else: f.INPUT_T = None f.FORMAT = tuple(format_items) # connect inputs setattr(parent, name, f) for a, a_name in in_intf_name_tuples: a_in = getattr(f.data_in, a_name) a_in(a) return f.data_out