From 9406bf7388f4eba3e0f779dd0a5325731a459851 Mon Sep 17 00:00:00 2001 From: tancheng Date: Mon, 2 Dec 2024 07:31:05 +0000 Subject: [PATCH] [feature] Test ring with CGRA data type --- lib/messages.py | 122 +++++++++++++++++++------ multi_cgra/__init__.py | 0 multi_cgra/test/RingNetworkRTL_test.py | 104 +++++++++++++++++++++ 3 files changed, 196 insertions(+), 30 deletions(-) create mode 100644 multi_cgra/__init__.py create mode 100644 multi_cgra/test/RingNetworkRTL_test.py diff --git a/lib/messages.py b/lib/messages.py index 3ab11c1..fb51e4f 100644 --- a/lib/messages.py +++ b/lib/messages.py @@ -63,7 +63,8 @@ def str_func( s ): # Generic config message #========================================================================= -def mk_ctrl( num_fu_in=2, num_inports=5, num_outports=5, prefix="CGRAConfig" ): +def mk_ctrl(num_fu_in = 2, num_inports = 5, num_outports = 5, + prefix = "CGRAConfig"): ctrl_nbits = 6 CtrlType = mk_bits( ctrl_nbits ) @@ -72,7 +73,8 @@ def mk_ctrl( num_fu_in=2, num_inports=5, num_outports=5, prefix="CGRAConfig" ): FuInType = mk_bits( clog2( num_fu_in + 1 ) ) PredicateType = mk_bits( 1 ) - new_name = f"{prefix}_{ctrl_nbits}_{num_fu_in}_{num_inports}_{num_outports}" + new_name = f"{prefix}_{ctrl_nbits}_{num_fu_in}_{num_inports}_" \ + f"{num_outports}" def str_func( s ): out_str = '(in)' @@ -100,27 +102,31 @@ def str_func( s ): return f"(opt){s.ctrl}|{out_str}" field_dict = {} - field_dict[ 'ctrl' ] = CtrlType - # The 'predicate' indicates whether the current operation is based on the partial - # predication or not. Note that 'predicate' is different from the following - # 'predicate_in', which contributes to the 'predicate' at the next cycle. - field_dict[ 'predicate' ] = PredicateType - # The fu_in indicates the input register ID (i.e., operands) for the operation. - field_dict[ 'fu_in' ] = [ FuInType for _ in range( num_fu_in ) ] - - field_dict[ 'outport' ] = [ InportsType for _ in range( num_outports ) ] - # I assume one tile supports single predicate during the entire execution time, as - # it is hard to distinguish predication for different operations (we automatically - # update, i.e., 'or', the predicate stored in the predicate register). This should - # be guaranteed by the compiler. - field_dict[ 'predicate_in' ] = [ PredicateType for _ in range( num_inports ) ] + field_dict['ctrl'] = CtrlType + # The 'predicate' indicates whether the current operation is based on + # the partial predication or not. Note that 'predicate' is different + # from the following 'predicate_in', which contributes to the + # 'predicate' at the next cycle. + field_dict['predicate'] = PredicateType + # The fu_in indicates the input register ID (i.e., operands) for the + # operation. + field_dict['fu_in'] = [FuInType for _ in range(num_fu_in)] + + field_dict['outport'] = [InportsType for _ in range(num_outports)] + # I assume one tile supports single predicate during the entire + # execution time, as it is hard to distinguish predication for + # different operations (we automatically update, i.e., 'or', the + # predicate stored in the predicate register). This should be + # guaranteed by the compiler. + field_dict['predicate_in'] = [PredicateType for _ in range( + num_inports)] # TODO: to support multiple predicate # field_dict[ 'predicate_in0' ] = ... # field_dict[ 'predicate_in1' ] = ... - return mk_bitstruct( new_name, field_dict, - namespace = { '__str__': str_func } + return mk_bitstruct(new_name, field_dict, + namespace = {'__str__': str_func} ) @@ -139,7 +145,9 @@ def mk_separate_ctrl(num_fu_inports = 4, FuOutType = mk_bits(clog2(num_fu_outports + 1)) PredicateType = mk_bits(1) - new_name = f"{prefix}_{operation_nbits}_{num_fu_inports}_{num_fu_outports}_{num_tile_inports}_{num_tile_outports}" + new_name = f"{prefix}_{operation_nbits}_{num_fu_inports}_" \ + f"{num_fu_outports}_{num_tile_inports}_" \ + f"{num_tile_outports}" def str_func(s): out_str = '(fu_in)' @@ -180,20 +188,25 @@ def str_func(s): field_dict = {} field_dict['ctrl'] = OperationType # TODO: need fix to pair `predicate` with specific operation. - # The 'predicate' indicates whether the current operation is based on the partial - # predication or not. Note that 'predicate' is different from the following - # 'predicate_in', which contributes to the 'predicate' at the next cycle. + # The 'predicate' indicates whether the current operation is based on + # the partial predication or not. Note that 'predicate' is different + # from the following 'predicate_in', which contributes to the 'predicate' + # at the next cycle. field_dict['predicate'] = PredicateType - # The fu_in indicates the input register ID (i.e., operands) for the operation. + # The fu_in indicates the input register ID (i.e., operands) for the + # operation. field_dict['fu_in'] = [FuInType for _ in range(num_fu_inports)] - field_dict['routing_xbar_outport'] = [TileInportsType for _ in range(num_routing_outports)] - field_dict['fu_xbar_outport'] = [FuOutType for _ in range(num_routing_outports)] - # I assume one tile supports single predicate during the entire execution time, as - # it is hard to distinguish predication for different operations (we automatically - # update, i.e., 'or', the predicate stored in the predicate register). This should - # be guaranteed by the compiler. - field_dict['routing_predicate_in'] = [PredicateType for _ in range(num_tile_inports)] + field_dict['routing_xbar_outport'] = [TileInportsType for _ in range( + num_routing_outports)] + field_dict['fu_xbar_outport'] = [FuOutType for _ in range( + num_routing_outports)] + # I assume one tile supports single predicate during the entire execution + # time, as it is hard to distinguish predication for different operations + # (we automatically update, i.e., 'or', the predicate stored in the + # predicate register). This should be guaranteed by the compiler. + field_dict['routing_predicate_in'] = [PredicateType for _ in range( + num_tile_inports)] # TODO: to support multiple predicate # field_dict[ 'predicate_in0' ] = ... @@ -203,3 +216,52 @@ def str_func(s): namespace = { '__str__': str_func } ) + +#========================================================================= +# Ring multi-CGRA data/config/cmd packet +#========================================================================= + +def mk_ring_multi_cgra_pkt(nrouters = 4, opaque_nbits = 8, vc = 2, + payload_nbits = 16, predicate_nbits = 1, + prefix="RingMultiCGRAPacket" ): + + IdType = mk_bits(clog2(nrouters)) + OpqType = mk_bits(opaque_nbits) + PayloadType = mk_bits(payload_nbits) + PredicateType = mk_bits(predicate_nbits) + + new_name = f"{prefix}_{nrouters}_{vc}_{opaque_nbits}_{payload_nbits}_" \ + f"{predicate_nbits}" + + if vc > 1: + VcIdType = mk_bits( clog2( vc ) ) + + def str_func( s ): + return f"{s.src}>{s.dst}:{s.opaque}:{s.vc_id}:{s.payload}." \ + f"{s.predicate}" + + return mk_bitstruct( new_name, { + 'src': IdType, + 'dst': IdType, + 'opaque': OpqType, + 'vc_id': VcIdType, + 'payload': PayloadType, + 'predicate': PredicateType, + }, + namespace = { '__str__': str_func } + ) + + else: + def str_func( s ): + return f"{s.src}>{s.dst}:{s.opaque}:{s.payload}.{s.predicate}" + + return mk_bitstruct( new_name, { + 'src': IdType, + 'dst': IdType, + 'opaque': OpqType, + 'payload': PayloadType, + 'predicate': PredicateType, + }, + namespace = { '__str__': str_func } + ) + diff --git a/multi_cgra/__init__.py b/multi_cgra/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/multi_cgra/test/RingNetworkRTL_test.py b/multi_cgra/test/RingNetworkRTL_test.py new file mode 100644 index 0000000..25f8a85 --- /dev/null +++ b/multi_cgra/test/RingNetworkRTL_test.py @@ -0,0 +1,104 @@ +""" +========================================================================= +RingNetworkRTL_test.py +========================================================================= +Test for RingNetworkRTL with CGRA message. + +Author : Cheng Tan + Date : Dec 1, 2024 +""" + + +from pymtl3 import * +from ...lib.basic.val_rdy.SourceRTL import SourceRTL as TestSrcRTL +from ...lib.messages import * +from ...lib.opt_type import * +# from ...noc.PyOCN.pymtl3_net.ocnlib.ifcs.packets import mk_ring_pkt +from ...noc.PyOCN.pymtl3_net.ocnlib.ifcs.positions import mk_ring_pos +from ...noc.PyOCN.pymtl3_net.ocnlib.utils import run_sim +from ...noc.PyOCN.pymtl3_net.ocnlib.test.stream_sinks import NetSinkRTL as TestNetSinkRTL +from ...noc.PyOCN.pymtl3_net.ringnet.RingNetworkFL import ringnet_fl +from ...noc.PyOCN.pymtl3_net.ringnet.RingNetworkRTL import RingNetworkRTL + + +#------------------------------------------------------------------------- +# TestHarness +#------------------------------------------------------------------------- + +class TestHarness(Component): + + def construct(s, MsgType, num_routers, src_msgs, sink_msgs): + + s.num_routers = num_routers + RingPos = mk_ring_pos(num_routers) + cmp_fn = lambda a, b : a.payload == b.payload + + s.srcs = [TestSrcRTL(MsgType, src_msgs[i]) + for i in range(num_routers)] + s.dut = RingNetworkRTL(MsgType, RingPos, num_routers, 0) + s.sinks = [TestNetSinkRTL(MsgType, sink_msgs[i], cmp_fn = cmp_fn) + for i in range( num_routers)] + + # Connections + for i in range (s.dut.num_routers): + s.srcs[i].send //= s.dut.recv[i] + s.dut.send[i] //= s.sinks[i].recv + + def done(s): + srcs_done = True + sinks_done = True + for i in range(s.num_routers): + srcs_done = srcs_done and s.srcs[i].done() + sinks_done = sinks_done and s.sinks[i].done() + return srcs_done and sinks_done + + def line_trace(s): + return s.dut.line_trace() + +#------------------------------------------------------------------------- +# mk_src_pkts +#------------------------------------------------------------------------- + +def mk_src_pkts( nterminals, lst ): + src_pkts = [ [] for _ in range( nterminals ) ] + src = 0 + for pkt in lst: + if hasattr(pkt, 'fl_type'): + if pkt.fl_type == 0: + src = pkt.src + else: + src = pkt.src + src_pkts[ src ].append( pkt ) + return src_pkts + +#========================================================================= +# Test cases +#========================================================================= + +class RingNetwork_Tests: + + @classmethod + def setup_class(cls): + cls.DutType = RingNetworkRTL + + def _test_cgra_data(s, translation = ''): + DataType = mk_data(16, 1) + nterminals = 4 + Pkt = mk_ring_multi_cgra_pkt(nterminals, payload_nbits = 32, + predicate_nbits = 1) + src_pkts = mk_src_pkts(nterminals, [ + # src dst opq vc payload predicate + Pkt(0, 1, 0, 0, 0xfaceb00c, 1), + Pkt(1, 2, 1, 0, 0xdeadbeef, 0), + Pkt(2, 3, 2, 0, 0xbaadface, 1), + Pkt(3, 0, 0, 0, 0xfaceb00c, 0), + ]) + dst_pkts = ringnet_fl(src_pkts) + th = TestHarness(Pkt, nterminals, src_pkts, dst_pkts) + cmdline_opts={'dump_vcd': False, 'test_verilog': translation, + 'dump_vtb': False} + run_sim(th, cmdline_opts) + + def test_cgra_data(self): + self._test_cgra_data('zeros') +