diff --git a/fu/single/AdderRTL.py b/fu/single/AdderRTL.py index 65997cd..dc076fe 100644 --- a/fu/single/AdderRTL.py +++ b/fu/single/AdderRTL.py @@ -71,7 +71,7 @@ def comb_logic(): # s.send_out[0].en = s.recv_opt.en if s.recv_opt.msg.ctrl == OPT_ADD: - s.send_out[0].msg.payload @= s.recv_in[s.in0_idx].msg.payload + s.recv_in[s.in1_idx].msg.payload + s.send_out[0].msg.payload @= s.recv_in[s.in0_idx].msg.payload + s.recv_in[s.in1_idx].msg.payload s.send_out[0].msg.predicate @= s.recv_in[s.in0_idx].msg.predicate & s.recv_in[s.in1_idx].msg.predicate if s.recv_opt.en & ( (s.recv_in_count[s.in0_idx] == 0) | \ (s.recv_in_count[s.in1_idx] == 0) ): @@ -79,21 +79,21 @@ def comb_logic(): s.recv_in[s.in1_idx].rdy @= b1( 0 ) s.send_out[0].msg.predicate @= b1( 0 ) elif s.recv_opt.msg.ctrl == OPT_ADD_CONST: - s.send_out[0].msg.payload @= s.recv_in[s.in0_idx].msg.payload + s.recv_const.msg.payload + s.send_out[0].msg.payload @= s.recv_in[s.in0_idx].msg.payload + s.recv_const.msg.payload s.send_out[0].msg.predicate @= s.recv_in[s.in0_idx].msg.predicate elif s.recv_opt.msg.ctrl == OPT_INC: - s.send_out[0].msg.payload @= s.recv_in[s.in0_idx].msg.payload + s.const_one.payload + s.send_out[0].msg.payload @= s.recv_in[s.in0_idx].msg.payload + s.const_one.payload s.send_out[0].msg.predicate @= s.recv_in[s.in0_idx].msg.predicate elif s.recv_opt.msg.ctrl == OPT_SUB: - s.send_out[0].msg.payload @= s.recv_in[s.in0_idx].msg.payload - s.recv_in[s.in1_idx].msg.payload + s.send_out[0].msg.payload @= s.recv_in[s.in0_idx].msg.payload - s.recv_in[s.in1_idx].msg.payload s.send_out[0].msg.predicate @= s.recv_in[s.in0_idx].msg.predicate if s.recv_opt.en & ( (s.recv_in_count[s.in0_idx] == 0) | \ (s.recv_in_count[s.in1_idx] == 0) ): - s.recv_in[s.in0_idx].rdy @= b1( 0 ) - s.recv_in[s.in1_idx].rdy @= b1( 0 ) + s.recv_in[s.in0_idx].rdy @= b1( 0 ) + s.recv_in[s.in1_idx].rdy @= b1( 0 ) s.send_out[0].msg.predicate @= b1( 0 ) elif s.recv_opt.msg.ctrl == OPT_PAS: - s.send_out[0].msg.payload @= s.recv_in[s.in0_idx].msg.payload + s.send_out[0].msg.payload @= s.recv_in[s.in0_idx].msg.payload s.send_out[0].msg.predicate @= s.recv_in[s.in0_idx].msg.predicate else: for j in range( num_outports ): diff --git a/fu/single/BranchRTL.py b/fu/single/BranchRTL.py index 917bc26..0b13a88 100644 --- a/fu/single/BranchRTL.py +++ b/fu/single/BranchRTL.py @@ -64,8 +64,8 @@ def comb_logic(): if s.recv_opt.msg.ctrl == OPT_BRH: # Branch is only used to set predication rather than delivering value. - s.send_out[0].msg @= DataType(ZeroType( 0 ), b1( 0 ), b1( 0 ) ) - s.send_out[1].msg @= DataType(ZeroType( 0 ), b1( 0 ), b1( 0 ) ) + s.send_out[0].msg @= DataType(ZeroType( 0 ), b1( 0 ), b1( 0 ), b1( 0 ) ) + s.send_out[1].msg @= DataType(ZeroType( 0 ), b1( 0 ), b1( 0 ), b1( 0 ) ) if s.recv_in[s.in0_idx].msg.payload == s.const_zero.payload: s.send_out[0].msg.predicate @= Bits1( 1 ) s.send_out[1].msg.predicate @= Bits1( 0 ) @@ -73,8 +73,8 @@ def comb_logic(): s.send_out[0].msg.predicate @= Bits1( 0 ) s.send_out[1].msg.predicate @= Bits1( 1 ) elif s.recv_opt.msg.ctrl == OPT_BRH_START: - s.send_out[0].msg @= DataType(ZeroType( 0 ), b1( 0 ), b1( 0 ) ) - s.send_out[1].msg @= DataType(ZeroType( 0 ), b1( 0 ), b1( 0 ) ) + s.send_out[0].msg @= DataType(ZeroType( 0 ), b1( 0 ), b1( 0 ), b1( 0 ) ) + s.send_out[1].msg @= DataType(ZeroType( 0 ), b1( 0 ), b1( 0 ), b1( 0 ) ) if s.first: s.send_out[0].msg.predicate @= Bits1( 1 ) s.send_out[1].msg.predicate @= Bits1( 0 ) diff --git a/lib/messages.py b/lib/messages.py index e9ac9d3..db6944b 100644 --- a/lib/messages.py +++ b/lib/messages.py @@ -22,16 +22,18 @@ def mk_data( payload_nbits=16, predicate_nbits=1, bypass_nbits=1, PayloadType = mk_bits( payload_nbits ) PredicateType = mk_bits( predicate_nbits ) BypassType = mk_bits( bypass_nbits ) + DelayType = mk_bits( 1 ) - new_name = f"{prefix}_{payload_nbits}_{predicate_nbits}_{bypass_nbits}" + new_name = f"{prefix}_{payload_nbits}_{predicate_nbits}_{bypass_nbits}_1" def str_func( s ): - return f"{s.payload}.{s.predicate}.{s.bypass}" + return f"{s.payload}.{s.predicate}.{s.bypass}.{s.delay}" return mk_bitstruct( new_name, { 'payload' : PayloadType, 'predicate': PredicateType, 'bypass' : BypassType, + 'delay' : DelayType, }, namespace = { '__str__': str_func } ) diff --git a/noc/BlockChannelRTL.py b/noc/BlockChannelRTL.py new file mode 100644 index 0000000..bf6d7c8 --- /dev/null +++ b/noc/BlockChannelRTL.py @@ -0,0 +1,80 @@ +#========================================================================= +# BlockChannelRTL.py +#========================================================================= +# RTL channel module for testing the delayed data receiving. Basically, it +# set the recv.rdy signal after a few cycles. +# +# Author : Cheng Tan +# Date : Aug 26, 2023 + +from pymtl3 import * +from pymtl3.stdlib.dstruct.queues import BypassQueue, NormalQueue +from ..lib.ifcs import RecvIfcRTL, SendIfcRTL + + +class BlockChannelRTL( Component ): + def construct(s, DataType, delay = 5 ): + + # Constant + s.num_entries = 2 + s.data = DataType(0, 0) + s.count = OutPort( mk_bits( clog2( s.num_entries+1 ) ) ) + + # Interface + s.recv = RecvIfcRTL( DataType ) + s.send = SendIfcRTL( DataType ) + + + # Component + s.queue = NormalQueue( DataType, s.num_entries ) + s.bypass_q = BypassQueue( DataType, num_entries=1 ) + + s.bypass_q.enq_en //= s.recv.en + s.bypass_q.enq_msg //= s.recv.msg + + s.count //= s.queue.count + + # Tracks the cycles spent in this channel.. + s.timing = Wire( mk_bits( clog2( delay + 1 ) ) ) + + @update + def process(): + s.recv.rdy @= s.bypass_q.enq_rdy & (s.timing >= delay) + + if ~s.bypass_q.deq_msg.bypass: + s.queue.enq_msg @= s.bypass_q.deq_msg + s.bypass_q.deq_en @= s.queue.enq_rdy & s.bypass_q.deq_rdy + s.queue.enq_en @= s.queue.enq_rdy & s.bypass_q.deq_rdy + + s.send.msg @= s.queue.deq_msg + s.send.msg.delay @= s.queue.deq_msg.delay | ~s.queue.deq_rdy + s.send.en @= s.send.rdy & s.queue.deq_rdy + s.queue.deq_en @= s.send.rdy & s.queue.deq_rdy + + else: + s.queue.enq_en @= 0 + s.queue.enq_msg @= DataType() + s.queue.deq_en @= 0 + + s.send.msg @= DataType() + s.send.msg.payload @= s.bypass_q.deq_msg.payload + s.send.msg.predicate @= s.bypass_q.deq_msg.predicate + s.send.msg.bypass @= 0 + s.send.msg.delay @= s.bypass_q.deq_msg.delay | ~s.bypass_q.deq_rdy + s.send.en @= s.send.rdy & s.bypass_q.deq_rdy + s.bypass_q.deq_en @= s.send.rdy & s.bypass_q.deq_rdy + + @update_ff + def update_timing(): + if s.timing == delay + 1: + s.timing <<= 0 + else: + s.timing <<= s.timing + 1 + + def line_trace( s ): + trace = '>' + trace += s.bypass_q.line_trace() + '>' + trace += s.queue.line_trace() + '>' + return trace # f'{s.bypass_q.line_trace()}' + return f"in:{s.recv.msg}({trace})out:{s.send.msg}.count:{s.count} ## " + diff --git a/noc/BypassChannelRTL.py b/noc/BypassChannelRTL.py new file mode 100644 index 0000000..3af70a4 --- /dev/null +++ b/noc/BypassChannelRTL.py @@ -0,0 +1,44 @@ +#========================================================================= +# BypassChannelRTL.py +#========================================================================= +# RTL channel module for connecting basic components to form accelerator. +# This simple channel has latency insensitive send/recv interfaces. +# +# Author : Cheng Tan +# Date : Feb 22, 2020 + +from pymtl3 import * +from pymtl3.stdlib.dstruct.queues import BypassQueue +from ..lib.ifcs import RecvIfcRTL, SendIfcRTL + + +class BypassChannelRTL( Component ): + def construct(s, DataType ): + + # Constant + s.data = DataType(0, 0) + + # Interface + s.recv = RecvIfcRTL( DataType ) + s.send = SendIfcRTL( DataType ) + + # Component + s.bypass_q = BypassQueue( DataType, num_entries=1 ) + s.bypass_q.enq_en //= s.recv.en + s.bypass_q.enq_msg //= s.recv.msg + s.bypass_q.enq_rdy //= s.recv.rdy + + @update + def process(): + s.send.msg @= DataType() + s.send.msg.payload @= s.bypass_q.deq_msg.payload + s.send.msg.predicate @= s.bypass_q.deq_msg.predicate + s.send.msg.bypass @= 0 + s.send.msg.valid @= s.bypass_q.deq_rdy + s.send.en @= s.send.rdy & s.bypass_q.deq_rdy + s.bypass_q.deq_en @= s.send.rdy & s.bypass_q.deq_rdy + + def line_trace( s ): + trace = '>' + return f'{s.bypass_q.line_trace()}' + diff --git a/noc/ChannelRTL.py b/noc/ChannelRTL.py index 9101246..9804cc0 100644 --- a/noc/ChannelRTL.py +++ b/noc/ChannelRTL.py @@ -51,8 +51,19 @@ def process(): s.queues[0].enq_en @= s.queues[0].enq_rdy & s.bypass_q.deq_rdy s.send.msg @= s.queues[s.latency-1].deq_msg + # s.send.msg.delay @= s.queues[s.latency-1].deq_rdy + # s.send.msg.delay @= s.queues[s.latency-1].deq_msg.delay + # Set the delay if FU not yet completes pipeline (the msg.delay is + # set by the FU) or the data has not yet arrived due to the delay + # propagation. + # s.send.msg.delay @= s.queues[s.latency-1].deq_msg.delay | \ + # ~s.queues[s.latency-1].deq_rdy + s.send.msg.delay @= s.queues[s.latency-1].deq_msg.delay s.send.en @= s.send.rdy & s.queues[s.latency-1].deq_rdy s.queues[s.latency-1].deq_en @= s.send.rdy & s.queues[s.latency-1].deq_rdy + # print("s.queues[0].deq_en: ", s.queues[0].deq_en) + # print("s.queues[0].enq_en: ", s.queues[0].enq_en) + # print("s.bypass_q.deq_rdy: ", s.bypass_q.deq_rdy) else: s.queues[0].enq_en @= 0 @@ -63,13 +74,20 @@ def process(): s.send.msg.payload @= s.bypass_q.deq_msg.payload s.send.msg.predicate @= s.bypass_q.deq_msg.predicate s.send.msg.bypass @= 0 + # s.send.msg.delay @= s.bypass_q.deq_rdy + # Set the delay if FU not yet completes pipeline (the msg.delay is + # set by the FU) or the data has not yet arrived due to the delay + # propagation. + # s.send.msg.delay @= s.bypass_q.deq_msg.delay | ~s.bypass_q.deq_rdy + s.send.msg.delay @= s.bypass_q.deq_msg.delay s.send.en @= s.send.rdy & s.bypass_q.deq_rdy s.bypass_q.deq_en @= s.send.rdy & s.bypass_q.deq_rdy def line_trace( s ): trace = '>' + trace += s.bypass_q.line_trace() + '>' for i in range( s.latency ): trace += s.queues[i].line_trace() + '>' - return f'{s.bypass_q.line_trace()}' + return trace # f'{s.bypass_q.line_trace()}' return f"in:{s.recv.msg}({trace})out:{s.send.msg}.count:{s.count} ## " diff --git a/noc/CrossbarRTL.py b/noc/CrossbarRTL.py index 6731c38..ecac9d3 100644 --- a/noc/CrossbarRTL.py +++ b/noc/CrossbarRTL.py @@ -2,9 +2,10 @@ ========================================================================= Crossbar.py ========================================================================= +Data-driven crossbar. Valid data is sent out only when Author : Cheng Tan - Date : Dec 8, 2019 + Date : August 26, 2023 """ from pymtl3 import * @@ -17,46 +18,52 @@ class CrossbarRTL( Component ): def construct( s, DataType, PredicateType, CtrlType, num_inports=5, num_outports=5, bypass_point=4, id=0 ): - OutType = mk_bits( clog2( num_inports + 1 ) ) + InType = mk_bits( clog2( num_inports + 1 ) ) s.bypass_point = bypass_point # Interface s.recv_opt = RecvIfcRTL( CtrlType ) - s.recv_data = [ RecvIfcRTL( DataType ) for _ in range ( num_inports ) ] - s.send_data = [ SendIfcRTL( DataType ) for _ in range ( num_outports ) ] + s.recv_data = [ RecvIfcRTL( DataType ) for _ in range( num_inports ) ] + s.send_data = [ SendIfcRTL( DataType ) for _ in range( num_outports ) ] s.send_predicate = SendIfcRTL( PredicateType ) # TODO: should include position information or not # s.pos = InPort( PositionType ) - s.in_dir = Wire( OutType ) - s.in_dir_local = Wire( OutType ) + s.in_dir = [ Wire( InType ) for _ in range( num_outports ) ] + s.in_dir_local = [ Wire( InType ) for _ in range( num_outports ) ] s.out_rdy_vector = Wire( num_outports ) + s.recv_predicate_vector = Wire( num_inports ) + # Used to indicate whether the recv_data could be popped. + s.recv_blocked_vector = Wire( num_inports ) + # received or sent once but there are still some others pending. So the + # one already done should not proceed the next to avoid overwriting. + s.recv_but_block_by_others = [ Wire( b1 ) for _ in range( num_inports ) ] + s.send_but_block_by_others = [ Wire( b1 ) for _ in range( num_outports ) ] # Routing logic @update def update_signal(): - s.out_rdy_vector @= 0 - s.in_dir @= 0 - s.in_dir_local @= 0 - s.send_predicate.en @= 0 - s.send_predicate.msg @= PredicateType() + s.out_rdy_vector @= 0 + s.recv_predicate_vector @= 0 + s.send_predicate.en @= 0 + s.recv_blocked_vector @= 0 + s.send_predicate.msg @= PredicateType() for i in range( num_inports ): s.recv_data[i].rdy @= 0 for i in range( num_outports ): - s.send_data[i].en @= 0 + s.in_dir[i] @= 0 + s.in_dir_local[i] @= 0 + s.send_data[i].en @= 0 s.send_data[i].msg @= DataType() - # predicate_out_rdy = b1( 0 ) # For predication register update. 'predicate' and 'predicate_in' no need # to be active at the same time. Specifically, the 'predicate' is for # the operation at the current cycle while the 'predicate_in' accumulates # the predicate and pushes into the predicate register that will be used # in the future. if s.recv_opt.msg.predicate: - # s.send_predicate.msg.payload = b1( 0 ) - # s.send_predicate.msg.predicate = b1( 0 ) s.send_predicate.msg @= PredicateType( b1(0), b1(0) ) if s.recv_opt.msg.ctrl != OPT_START: @@ -65,42 +72,77 @@ def update_signal(): if s.recv_opt.msg.predicate_in[i] & s.recv_data[i].en: s.send_predicate.en @= b1( 1 ) s.send_predicate.msg.payload @= b1( 1 ) - s.send_predicate.msg.predicate @= s.send_predicate.msg.predicate | s.recv_data[i].msg.predicate - # predicate_out_rdy = b1( 1 ) + s.recv_predicate_vector[i] @= s.recv_data[i].msg.predicate + + for i in range( num_inports ): + s.recv_blocked_vector[i] @= (s.recv_data[i].msg.delay == 1) + + # The predicate_in might not be issued to other ports on the xbar, + # but it also needs to be drained from the recv_data, otherwise, + # it would block the recv_data channel/buffer. + if s.recv_opt.msg.predicate_in[i] & \ + ~s.recv_blocked_vector[i] & \ + ~s.recv_but_block_by_others[i]: + s.recv_data[i].rdy @= 1 + for i in range( num_outports ): - s.in_dir @= s.recv_opt.msg.outport[i] s.out_rdy_vector[i] @= s.send_data[i].rdy -# s.send_data[i].msg.bypass = b1( 0 ) - if (s.in_dir > 0) & s.send_data[i].rdy: - s.in_dir_local @= s.in_dir - 1 - s.recv_data[s.in_dir_local].rdy @= s.send_data[i].rdy - s.send_data[i].en @= s.recv_data[s.in_dir_local].en - if s.send_data[i].en & s.recv_data[s.in_dir_local].rdy: - s.send_data[i].msg.payload @= s.recv_data[s.in_dir_local].msg.payload - s.send_data[i].msg.predicate @= s.recv_data[s.in_dir_local].msg.predicate - s.send_data[i].msg.bypass @= s.recv_data[s.in_dir_local].msg.bypass + s.in_dir[i] @= s.recv_opt.msg.outport[i] + if s.in_dir[i] > 0: + s.send_data[i].msg.delay @= s.recv_data[s.in_dir_local[i]].msg.delay + else: + s.out_rdy_vector[i] @= 1 + + for i in range( num_outports ): + s.in_dir[i] @= s.recv_opt.msg.outport[i] + if (s.in_dir[i] > 0) & s.send_data[i].rdy: + s.in_dir_local[i] @= s.in_dir[i] - 1 + + s.recv_data[s.in_dir_local[i]].rdy @= \ + s.send_data[i].rdy & \ + ~s.recv_blocked_vector[s.in_dir_local[i]] & \ + ~s.recv_but_block_by_others[s.in_dir_local[i]] & \ + ~s.send_but_block_by_others[i] + + s.send_data[i].en @= s.recv_data[s.in_dir_local[i]].en + if s.send_data[i].en & s.recv_data[s.in_dir_local[i]].rdy: + s.send_data[i].msg.payload @= s.recv_data[s.in_dir_local[i]].msg.payload + s.send_data[i].msg.predicate @= s.recv_data[s.in_dir_local[i]].msg.predicate + s.send_data[i].msg.bypass @= s.recv_data[s.in_dir_local[i]].msg.bypass + s.send_data[i].msg.delay @= 0 # The generate one can be send to other tile without buffering, # but buffering is still needed when 'other tile' is yourself # (i.e., generating output to self input). Here we avoid self # connecting by checking whether the inport belongs to FU and # outport be towards to remote tiles to eliminate combinational # loop. - if (s.in_dir_local >= s.bypass_point) & (i < s.bypass_point): + if (s.in_dir_local[i] >= s.bypass_point) & (i < s.bypass_point): s.send_data[i].msg.bypass @= b1( 1 ) -# print("in crossbar ", s, " set bypass ... s.recv_opt.msg.outport[", i, "]: ", s.recv_opt.msg.outport[i]) else: s.send_data[i].msg.bypass @= b1( 0 ) -# print("in crossbar if... s.send_data[", i, "].msg: ", s.send_data[i].msg, "; recv.rdy: ", s.recv_data[in_dir].rdy) else: s.send_data[i].en @= b1( 0 ) - #s.send_data[i].msg = b1( 0 ) -# print("in crossbar else... s.send_data[", i, "].msg: ", s.send_data[i].msg) else: for i in range( num_outports ): -# s.send_data[i].msg.bypass = b1( 0 ) s.send_data[i].en @= b1( 0 ) - s.recv_opt.rdy @= reduce_or( s.out_rdy_vector ) + s.recv_opt.rdy @= reduce_and( s.out_rdy_vector ) & ~reduce_or( s.recv_blocked_vector ) + s.send_predicate.msg.predicate @= reduce_or( s.recv_predicate_vector ) + + @update_ff + def update_blocked_by_others(): + for i in range( num_inports ): + if reduce_or( s.recv_blocked_vector ) & ~s.recv_blocked_vector[i]: + s.recv_but_block_by_others[i] <<= 1 + elif ~reduce_or( s.recv_blocked_vector ): + s.recv_but_block_by_others[i] <<= 0 + + for i in range( num_outports ): + if ~reduce_and( s.out_rdy_vector ) & s.out_rdy_vector[i]: + s.send_but_block_by_others[i] <<= 1 + elif reduce_and( s.out_rdy_vector ): + s.send_but_block_by_others[i] <<= 0 + # Line trace def line_trace( s ): diff --git a/noc/DelayChannelRTL.py b/noc/DelayChannelRTL.py new file mode 100644 index 0000000..f78aac4 --- /dev/null +++ b/noc/DelayChannelRTL.py @@ -0,0 +1,81 @@ +#========================================================================= +# DelayChannelRTL.py +#========================================================================= +# RTL channel module for testing the delayed data arrival. Basically, it +# set the send.msg.delay signal for a few cycles. The consumer shouldn't +# accept/consume the data (i.e., set the rdy) before the delay signal is +# clear. +# +# Author : Cheng Tan +# Date : Aug 26, 2023 + +from pymtl3 import * +from pymtl3.stdlib.dstruct.queues import BypassQueue, NormalQueue +from ..lib.ifcs import RecvIfcRTL, SendIfcRTL + + +class DelayChannelRTL( Component ): + def construct(s, DataType, delay = 5 ): + + # Constant + s.num_entries = 2 + s.data = DataType(0, 0) + s.count = OutPort( mk_bits( clog2( s.num_entries+1 ) ) ) + + # Interface + s.recv = RecvIfcRTL( DataType ) + s.send = SendIfcRTL( DataType ) + + + # Component + s.queue = NormalQueue( DataType, s.num_entries ) + s.bypass_q = BypassQueue( DataType, num_entries=1 ) + + s.bypass_q.enq_en //= s.recv.en + s.bypass_q.enq_msg //= s.recv.msg + s.bypass_q.enq_rdy //= s.recv.rdy + + s.count //= s.queue.count + + # Tracks the cycles spent in this channel.. + s.timing = Wire( mk_bits( clog2( delay + 1 ) ) ) + + @update + def process(): + if ~s.bypass_q.deq_msg.bypass: + s.queue.enq_msg @= s.bypass_q.deq_msg + s.bypass_q.deq_en @= s.queue.enq_rdy & s.bypass_q.deq_rdy + s.queue.enq_en @= s.queue.enq_rdy & s.bypass_q.deq_rdy + + s.send.msg @= s.queue.deq_msg + s.send.msg.delay @= (s.timing != delay) + s.send.en @= s.send.rdy & s.queue.deq_rdy + s.queue.deq_en @= s.send.rdy & s.queue.deq_rdy + + else: + s.queue.enq_en @= 0 + s.queue.enq_msg @= DataType() + s.queue.deq_en @= 0 + + s.send.msg @= DataType() + s.send.msg.payload @= s.bypass_q.deq_msg.payload + s.send.msg.predicate @= s.bypass_q.deq_msg.predicate + s.send.msg.bypass @= 0 + s.send.msg.delay @= (s.timing != delay) + s.send.en @= s.send.rdy & s.bypass_q.deq_rdy + s.bypass_q.deq_en @= s.send.rdy & s.bypass_q.deq_rdy + + @update_ff + def update_timing(): + if s.timing == delay + 1: + s.timing <<= 0 + else: + s.timing <<= s.timing + 1 + + def line_trace( s ): + trace = '>' + trace += s.bypass_q.line_trace() + '>' + trace += s.queue.line_trace() + '>' + return trace # f'{s.bypass_q.line_trace()}' + return f"in:{s.recv.msg}({trace})out:{s.send.msg}.count:{s.count} ## " + diff --git a/noc/test/BlockChannelRTL_test.py b/noc/test/BlockChannelRTL_test.py new file mode 100644 index 0000000..b78c97d --- /dev/null +++ b/noc/test/BlockChannelRTL_test.py @@ -0,0 +1,83 @@ +#========================================================================= +# BlockChannelRTL_test.py +#========================================================================= +# Test for BlockChannel +# +# Author : Cheng Tan +# Date : August 26, 2023 + +import pytest +from pymtl3 import * +from pymtl3.stdlib.test_utils import TestVectorSimulator + +from ...lib.test_sinks import TestSinkRTL +from ...lib.test_srcs import TestSrcRTL +from ...lib.messages import * +from ..BlockChannelRTL import BlockChannelRTL + +#------------------------------------------------------------------------- +# TestHarness +#------------------------------------------------------------------------- + +class TestHarness( Component ): + + def construct( s, MsgType, src_msgs, sink_msgs, latency ): + + s.src = TestSrcRTL ( MsgType, src_msgs ) + s.sink = TestSinkRTL( MsgType, sink_msgs ) + s.dut = BlockChannelRTL( MsgType, latency ) + + # Connections + s.src.send //= s.dut.recv + s.dut.send //= s.sink.recv + # s.dut.send.rdy //= 0 + + def done( s ): + return s.src.done() and s.sink.done() + + def line_trace( s ): + # return s.src.line_trace() + "-> | " + s.dut.line_trace() + return s.src.line_trace() + "-> | " + s.dut.line_trace() + \ + " | -> " + s.sink.line_trace() + +#------------------------------------------------------------------------- +# run_rtl_sim +#------------------------------------------------------------------------- + +def run_sim( test_harness, max_cycles=100 ): + + # Create a simulator + test_harness.elaborate() + test_harness.apply( DefaultPassGroup() ) + test_harness.sim_reset() + + # Run simulation + ncycles = 0 + print() + print( "{}:{}".format( ncycles, test_harness.line_trace() )) + while not test_harness.done() and ncycles < max_cycles: + test_harness.sim_tick() + ncycles += 1 + print( "{}:{}".format( ncycles, test_harness.line_trace() )) + if ncycles >= 4: + test_harness.dut.send.rdy @= 1 + + # Check timeout + assert ncycles < max_cycles + + test_harness.sim_tick() + test_harness.sim_tick() + test_harness.sim_tick() + +#------------------------------------------------------------------------- +# Test cases +#------------------------------------------------------------------------- + +DataType = mk_data( 16, 1 ) +test_msgs = [ DataType(7,1,1), DataType(4,1), DataType(1,1), DataType(2,1), DataType(3,1) ] +sink_msgs = [ DataType(7,1), DataType(4,1), DataType(1,1), DataType(2,1), DataType(3,1) ] + +def test_latency(): + latency = 5 + th = TestHarness( DataType, test_msgs, sink_msgs, latency) + run_sim( th ) diff --git a/noc/test/ChannelRTL_test.py b/noc/test/ChannelRTL_test.py index 27628c1..53a59c1 100644 --- a/noc/test/ChannelRTL_test.py +++ b/noc/test/ChannelRTL_test.py @@ -21,20 +21,22 @@ class TestHarness( Component ): - def construct( s, MsgType, src_msgs, sink_msgs ): + def construct( s, MsgType, src_msgs, sink_msgs, latency ): s.src = TestSrcRTL ( MsgType, src_msgs ) s.sink = TestSinkRTL( MsgType, sink_msgs ) - s.dut = ChannelRTL( MsgType ) + s.dut = ChannelRTL( MsgType, latency ) # Connections s.src.send //= s.dut.recv s.dut.send //= s.sink.recv + # s.dut.send.rdy //= 0 def done( s ): return s.src.done() and s.sink.done() def line_trace( s ): + # return s.src.line_trace() + "-> | " + s.dut.line_trace() return s.src.line_trace() + "-> | " + s.dut.line_trace() + \ " | -> " + s.sink.line_trace() @@ -57,6 +59,8 @@ def run_sim( test_harness, max_cycles=100 ): test_harness.sim_tick() ncycles += 1 print( "{}:{}".format( ncycles, test_harness.line_trace() )) + if ncycles >= 4: + test_harness.dut.send.rdy @= 1 # Check timeout assert ncycles < max_cycles @@ -71,8 +75,14 @@ def run_sim( test_harness, max_cycles=100 ): DataType = mk_data( 16, 1 ) test_msgs = [ DataType(7,1,1), DataType(4,1), DataType(1,1), DataType(2,1), DataType(3,1) ] -sink_msgs = [ DataType(7,1,0), DataType(4,1), DataType(1,1), DataType(2,1), DataType(3,1) ] +sink_msgs = [ DataType(7,1), DataType(4,1), DataType(1,1), DataType(2,1), DataType(3,1) ] def test_simple(): - th = TestHarness( DataType, test_msgs, sink_msgs) + latency = 1 + th = TestHarness( DataType, test_msgs, sink_msgs, latency) + run_sim( th ) + +def test_latency(): + latency = 2 + th = TestHarness( DataType, test_msgs, sink_msgs, latency) run_sim( th ) diff --git a/noc/test/CrossbarBlockRTL_test.py b/noc/test/CrossbarBlockRTL_test.py new file mode 100644 index 0000000..645ad8a --- /dev/null +++ b/noc/test/CrossbarBlockRTL_test.py @@ -0,0 +1,117 @@ +""" +========================================================================== +CrossbarBlockRTL_test.py +========================================================================== +Test cases for Crossbar. Output is blocked for a few cycles. + +Author : Cheng Tan + Date : August 26, 2023 + +""" + +from pymtl3 import * +from ...lib.test_sinks import TestSinkRTL +from ...lib.test_srcs import TestSrcRTL + +from ..CrossbarRTL import CrossbarRTL +from ..BlockChannelRTL import BlockChannelRTL +from ...lib.opt_type import * +from ...lib.messages import * + +#------------------------------------------------------------------------- +# Test harness +#------------------------------------------------------------------------- + +class TestHarness( Component ): + + def construct( s, CrossbarUnit, DataType, PredicateType, CtrlType, + num_inports, num_outports, + src_data, src_routing, sink_out ): + + s.num_inports = num_inports + s.num_outports = num_outports + + s.src_opt = TestSrcRTL( CtrlType, src_routing ) + s.src_data = [ TestSrcRTL( DataType, src_data[i] ) + for i in range( num_inports ) ] + s.sink_out = [ TestSinkRTL( DataType, sink_out[i] ) + for i in range( num_outports ) ] + + s.dut = CrossbarUnit( DataType, PredicateType, CtrlType, num_inports, + num_outports, num_inports ) + + # Delayed latency. + latency = 9 + s.channel = BlockChannelRTL( DataType, latency ) + + for i in range( num_inports ): + connect( s.src_data[i].send, s.dut.recv_data[i] ) + if i != 0: + s.dut.send_data[i] //= s.sink_out[i].recv + else: + s.dut.send_data[i] //= s.channel.recv + connect( s.src_opt.send, s.dut.recv_opt ) + connect( s.channel.send, s.sink_out[0].recv ) + + def done( s ): + done = True + for i in range( s.num_inports ): + if not s.src_data[i].done(): + done = False + break + for i in range( s.num_outports ): + if not s.sink_out[i].done(): + done = False + break + return done + + def line_trace( s ): + return s.dut.line_trace() + +def run_sim( test_harness, max_cycles=100 ): + test_harness.elaborate() + test_harness.apply( DefaultPassGroup() ) + test_harness.sim_reset() + + # Run simulation + ncycles = 0 + print() + print( "{}:{}".format( ncycles, test_harness.line_trace() )) + while not test_harness.done() and ncycles < max_cycles: + test_harness.sim_tick() + ncycles += 1 + print( "{}:{}".format( ncycles, test_harness.line_trace() )) + + # Check timeout + + assert ncycles < max_cycles + + test_harness.sim_tick() + test_harness.sim_tick() + test_harness.sim_tick() + +def test_multi(): + FU = CrossbarRTL + num_fu_in = 3 + num_inports = 3 + num_outports = 3 + DataType = mk_data( 16, 1 ) + PredicateType = mk_predicate( 1, 1 ) + CtrlType = mk_ctrl( num_fu_in, num_inports, num_outports ) + FuInType = mk_bits( clog2( num_inports + 1 ) ) + pickRegister = [ FuInType( x+1 ) for x in range( num_inports ) ] + RouteType = mk_bits( clog2( num_inports + 1 ) ) + src_opt = [ CtrlType( OPT_ADD, b1(0), pickRegister, [RouteType(2), + RouteType(1), + RouteType(1)]), + CtrlType( OPT_SUB, b1(0), pickRegister, [RouteType(3), + RouteType(2), + RouteType(1)]) ] + src_data = [ [DataType(3, 1), DataType(4, 1)], [DataType(2, 1), DataType(7, 1)], [DataType(9, 1)] ] + sink_out = [ [DataType(2, 1), DataType(9, 1)], + [DataType(3, 1), DataType(7, 1)], + [DataType(3, 1), DataType(4, 1)] ] + th = TestHarness( FU, DataType, PredicateType, CtrlType, num_inports, num_outports, + src_data, src_opt, sink_out ) + run_sim( th ) + diff --git a/noc/test/CrossbarDelayRTL_test.py b/noc/test/CrossbarDelayRTL_test.py new file mode 100644 index 0000000..d85ca1c --- /dev/null +++ b/noc/test/CrossbarDelayRTL_test.py @@ -0,0 +1,186 @@ +""" +========================================================================== +CrossbarDelayRTL_test.py +========================================================================== +Test cases for Crossbar. The input data is delayed for arrival. In +practice, the delay is caused by the functional units (e.g., a pipeline or +multi-stage FU, a LD/ST operation hitting bank conflict or cache miss). +In these cases, the FU would set the msg.delay before the real data is +sent out. The delay signal/msg should be propagated to the others. + +Author : Cheng Tan + Date : August 26, 2023 + +""" + +from pymtl3 import * +from ...lib.test_sinks import TestSinkRTL +from ...lib.test_srcs import TestSrcRTL + +from ..CrossbarRTL import CrossbarRTL +from ..DelayChannelRTL import DelayChannelRTL +from ...lib.opt_type import * +from ...lib.messages import * + +#------------------------------------------------------------------------- +# Test harness +#------------------------------------------------------------------------- + +class TestHarness( Component ): + + def construct( s, CrossbarUnit, DataType, PredicateType, CtrlType, + num_inports, num_outports, + src_data, src_routing, sink_out ): + + s.num_inports = num_inports + s.num_outports = num_outports + + s.src_opt = TestSrcRTL( CtrlType, src_routing ) + s.src_data = [ TestSrcRTL( DataType, src_data[i] ) + for i in range( num_inports ) ] + s.sink_out = [ TestSinkRTL( DataType, sink_out[i] ) + for i in range( num_outports ) ] + + s.dut = CrossbarUnit( DataType, PredicateType, CtrlType, num_inports, + num_outports, num_inports - 1 ) + # Delayed latency. + latency = 5 + s.channel = DelayChannelRTL( DataType, latency ) + + assert( num_inports == 3 ) + connect( s.src_data[0].send, s.dut.recv_data[0] ) + connect( s.dut.send_data[0], s.sink_out[0].recv ) + + # The channel/fifo connecting with inport 1 has latency of 2. + connect( s.src_data[1].send, s.channel.recv ) + connect( s.channel.send, s.dut.recv_data[1] ) + connect( s.dut.send_data[1], s.sink_out[1].recv ) + + connect( s.src_data[2].send, s.dut.recv_data[2] ) + connect( s.dut.send_data[2], s.sink_out[2].recv ) + + connect( s.src_opt.send, s.dut.recv_opt ) + + def done( s ): + done = True + for i in range( s.num_inports ): + if not s.src_data[i].done(): + done = False + break + for i in range( s.num_outports ): + if not s.sink_out[i].done(): + done = False + break + return done + + def line_trace( s ): + return s.dut.line_trace() + +def run_sim( test_harness, max_cycles=30 ): + test_harness.elaborate() + test_harness.apply( DefaultPassGroup() ) + test_harness.sim_reset() + + # Run simulation + ncycles = 0 + print() + print( "{}:{}".format( ncycles, test_harness.line_trace() )) + while not test_harness.done() and ncycles < max_cycles: + test_harness.sim_tick() + ncycles += 1 + print( "{}:{}".format( ncycles, test_harness.line_trace() )) + + # Check timeout + + assert ncycles <= max_cycles + + test_harness.sim_tick() + test_harness.sim_tick() + test_harness.sim_tick() + +def test_mul_with_long_latency_input(): + FU = CrossbarRTL + num_fu_in = 3 + num_inports = 3 + num_outports = 3 + DataType = mk_data( 16, 1 ) + PredicateType = mk_predicate( 1, 1 ) + CtrlType = mk_ctrl( num_fu_in, num_inports, num_outports ) + FuInType = mk_bits( clog2( num_inports + 1 ) ) + pickRegister = [ FuInType( x+1 ) for x in range( num_inports ) ] + RouteType = mk_bits( clog2( num_inports + 1 ) ) + + src_opt = [ CtrlType( OPT_ADD, b1(0), pickRegister, + [RouteType(2), RouteType(1), RouteType(1)]), + CtrlType( OPT_SUB, b1(0), pickRegister, + [RouteType(3), RouteType(2), RouteType(1)]) ] + src_data = [ [DataType(3, 1), DataType(4, 1)], [DataType(2, 1), DataType(7, 1)], [DataType(9, 1)] ] + sink_out = [ [DataType(2, 1), DataType(9, 1, 1)], + [DataType(3, 1), DataType(7, 1)], + [DataType(3, 1), DataType(4, 1)] ] + + # src_opt = [ CtrlType( OPT_ADD, b1(0), pickRegister, [RouteType(2), RouteType(1), RouteType(1)]) ] + # src_data = [ [DataType(3, 1), DataType(7, 1)], [DataType(2, 1, 1)], [DataType(9, 1)] ] + # sink_out = [ [DataType(2, 1)], [DataType(3, 1)], [DataType(3, 1)] ] + th = TestHarness( FU, DataType, PredicateType, CtrlType, num_inports, num_outports, + src_data, src_opt, sink_out ) + run_sim( th ) + +def test_latency_with_predicate(): + FU = CrossbarRTL + num_fu_in = 3 + num_inports = 3 + num_outports = 3 + DataType = mk_data( 16, 1 ) + PredicateType = mk_predicate( 1, 1 ) + CtrlType = mk_ctrl( num_fu_in, num_inports, num_outports ) + FuInType = mk_bits( clog2( num_inports + 1 ) ) + pickRegister = [ FuInType( x+1 ) for x in range( num_inports ) ] + RouteType = mk_bits( clog2( num_inports + 1 ) ) + + src_opt = [ CtrlType( OPT_ADD, b1(0), pickRegister, + [RouteType(2), RouteType(1), RouteType(1)], + [b1(1), b1(0), b1(0)] ), + CtrlType( OPT_SUB, b1(0), pickRegister, + [RouteType(3), RouteType(2), RouteType(0)], + [b1(1), b1(0), b1(0)] ), + CtrlType( OPT_ADD, b1(0), pickRegister, + [RouteType(0), RouteType(0), RouteType(2)], + [b1(1), b1(0), b1(0)] ), + CtrlType( OPT_SUB, b1(0), pickRegister, + [RouteType(0), RouteType(0), RouteType(1)], + [b1(0), b1(0), b1(0)] ) ] + + src_data = [ [DataType(3, 1), DataType(4, 1), DataType(5, 1), DataType(6, 1)], + [DataType(2, 1), DataType(7, 1), DataType(8, 1)], + [DataType(9, 1)] ] + sink_out = [ [DataType(2, 1), DataType(9, 1, 1)], + [DataType(3, 1), DataType(7, 1)], + [DataType(3, 1), DataType(8, 1), DataType(6, 1)] ] + + # src_opt = [ CtrlType( OPT_ADD, b1(0), pickRegister, [RouteType(2), RouteType(1), RouteType(1)]) ] + # src_data = [ [DataType(3, 1), DataType(7, 1)], [DataType(2, 1, 1)], [DataType(9, 1)] ] + # sink_out = [ [DataType(2, 1)], [DataType(3, 1)], [DataType(3, 1)] ] + th = TestHarness( FU, DataType, PredicateType, CtrlType, num_inports, num_outports, + src_data, src_opt, sink_out ) + run_sim( th ) + +# 141 def test_predicate(): +# 142 FU = CrossbarRTL +# 143 num_fu_in = 3 +# 144 num_inports = 3 +# 145 num_outports = 3 +# 146 DataType = mk_data( 16, 1 ) +# 147 PredicateType = mk_predicate( 1, 1 ) +# 148 CtrlType = mk_ctrl( num_fu_in, num_inports, num_outports ) +# 149 FuInType = mk_bits( clog2( num_inports + 1 ) ) +# 150 pickRegister = [ FuInType( 0 ) for x in range( num_inports ) ] +# 151 RouteType = mk_bits( clog2( num_inports + 1 ) ) +# 152 src_opt = [ CtrlType( OPT_ADD, b1(0), pickRegister, [RouteType(2), RouteType(1), RouteType(0)], [b1(0), b1(0), b1(1)]), +# 153 CtrlType( OPT_SUB, b1(0), pickRegister, [RouteType(0), RouteType(3), RouteType(2)], [b1(0), b1(0), b1(0)]), +# 154 CtrlType( OPT_ADD, b1(1), pickRegister, [RouteType(0), RouteType(0), RouteType(0)], [b1(1), b1(1), b1(0)]) ] +# 155 src_data = [ [DataType(1, 1), DataType(2, 1)], [DataType(3, 1), DataType(4, 1), DataType(5, 0)], [DataType(6, 1), DataType(7, 0)] ] +# 156 sink_out = [ [DataType(3, 1)], [DataType(1, 1), DataType(7, 0, 1)], [DataType(4, 1)] ] +# 157 th = TestHarness( FU, DataType, PredicateType, CtrlType, num_inports, num_outports, +# 158 src_data, src_opt, sink_out ) +# 159 run_sim( th ) diff --git a/noc/test/CrossbarRTL_test.py b/noc/test/CrossbarRTL_test.py index 108d8d4..da40027 100644 --- a/noc/test/CrossbarRTL_test.py +++ b/noc/test/CrossbarRTL_test.py @@ -131,7 +131,7 @@ def test_multi2(): # RouteType(0) indicates no data movement. Reg ID starts from 1, which will get "minus 1" # the logic. src_opt = [ CtrlType( OPT_ADD, b1(0), pickRegister, [RouteType(2), RouteType(1), RouteType(0)]), - CtrlType( OPT_ADD, b1(0), pickRegister, [RouteType(0), RouteType(3), RouteType(2)]) ] + CtrlType( OPT_SUB, b1(0), pickRegister, [RouteType(0), RouteType(3), RouteType(2)]) ] src_data = [ [DataType(3, 1)], [DataType(2, 1), DataType(20, 1)], [DataType(9, 1)] ] sink_out = [ [DataType(2, 1)], [DataType(3, 1), DataType(9, 1, 1)], [DataType(20, 1)] ] th = TestHarness( FU, DataType, PredicateType, CtrlType, num_inports, num_outports, @@ -150,10 +150,10 @@ def test_predicate(): pickRegister = [ FuInType( 0 ) for x in range( num_inports ) ] RouteType = mk_bits( clog2( num_inports + 1 ) ) src_opt = [ CtrlType( OPT_ADD, b1(0), pickRegister, [RouteType(2), RouteType(1), RouteType(0)], [b1(0), b1(0), b1(1)]), - CtrlType( OPT_ADD, b1(0), pickRegister, [RouteType(0), RouteType(3), RouteType(2)], [b1(0), b1(0), b1(0)]), - CtrlType( OPT_ADD, b1(1), pickRegister, [RouteType(0), RouteType(0), RouteType(0)], [b1(1), b1(1), b1(0)]) ] + CtrlType( OPT_SUB, b1(0), pickRegister, [RouteType(0), RouteType(3), RouteType(2)], [b1(0), b1(0), b1(0)]), + CtrlType( OPT_ADD, b1(1), pickRegister, [RouteType(0), RouteType(0), RouteType(0)], [b1(1), b1(1), b1(0)]) ] src_data = [ [DataType(1, 1), DataType(2, 1)], [DataType(3, 1), DataType(4, 1), DataType(5, 0)], [DataType(6, 1), DataType(7, 0)] ] - sink_out = [ [DataType(3, 1)], [DataType(1, 1), DataType(6, 1, 1)], [DataType(4, 1)] ] + sink_out = [ [DataType(3, 1)], [DataType(1, 1), DataType(7, 0, 1)], [DataType(4, 1)] ] th = TestHarness( FU, DataType, PredicateType, CtrlType, num_inports, num_outports, src_data, src_opt, sink_out ) run_sim( th ) diff --git a/tile/TileRTL.py b/tile/TileRTL.py index 10f8250..cc5bc26 100644 --- a/tile/TileRTL.py +++ b/tile/TileRTL.py @@ -21,6 +21,7 @@ from ..noc.CrossbarRTL import CrossbarRTL from ..noc.ChannelRTL import ChannelRTL from ..rf.RegisterRTL import RegisterRTL +# from ..noc.BypassChannelRTL import BypassChannelRTL class TileRTL( Component ): @@ -62,6 +63,8 @@ def construct( s, DataType, PredicateType, CtrlType, num_xbar_inports, num_xbar_outports ) s.ctrl_mem = CtrlMemRTL( CtrlType, ctrl_mem_size, num_ctrl, total_steps ) s.channel = [ ChannelRTL( DataType ) for _ in range( num_xbar_outports ) ] + # # Added to break the combinational loops + # s.bypass_channel = [ BypassChannelRTL( DataType ) for _ in range( num_fu_outports ) ] # Additional one register for partial predication s.reg_predicate = RegisterRTL( PredicateType ) @@ -73,7 +76,6 @@ def construct( s, DataType, PredicateType, CtrlType, s.ctrl_mem.recv_ctrl //= s.recv_wopt # Data - s.element.recv_const //= s.const_queue.send_const for i in range( len( FuList ) ): @@ -108,6 +110,8 @@ def construct( s, DataType, PredicateType, CtrlType, for i in range( num_fu_outports ): s.element.send_out[i] //= s.crossbar.recv_data[num_connect_outports+i] + # s.element.send_out[i] //= s.bypass_channel[i].recv + # s.bypass_channel[i].send //= s.crossbar.recv_data[num_connect_outports+i] @update def update_opt(): diff --git a/tile/test/TileRTL_test.py b/tile/test/TileRTL_test.py index 4513c8c..8461d99 100644 --- a/tile/test/TileRTL_test.py +++ b/tile/test/TileRTL_test.py @@ -97,7 +97,8 @@ def test_tile_alu( cmdline_opts ): RouteType = mk_bits( clog2( num_xbar_inports + 1 ) ) AddrType = mk_bits( clog2( ctrl_mem_size ) ) FuInType = mk_bits( clog2( num_fu_in + 1 ) ) - pickRegister = [ FuInType( x+1 ) for x in range( num_fu_in ) ] + pickRegister0 = [ FuInType( 0 ) for x in range( num_fu_in ) ] + pickRegister1 = [ FuInType( 1 ), FuInType( 2 ), FuInType( 0 ), FuInType( 0 ) ] DUT = TileRTL FunctionUnit = FlexibleFuRTL FuList = [AdderRTL, MulRTL, MemUnitRTL] @@ -105,13 +106,13 @@ def test_tile_alu( cmdline_opts ): PredicateType = mk_predicate( 1, 1 ) CtrlType = mk_ctrl( num_fu_in, num_xbar_inports, num_xbar_outports ) opt_waddr = [ AddrType( 0 ), AddrType( 1 ), AddrType( 2 ) ] - src_opt = [ CtrlType( OPT_NAH, b1(0), pickRegister, [ + src_opt = [ CtrlType( OPT_NAH, b1(0), pickRegister0, [ RouteType(0), RouteType(0), RouteType(0), RouteType(0), RouteType(4), RouteType(3), RouteType(0), RouteType(0)] ), - CtrlType( OPT_ADD, b1(0), pickRegister, [ + CtrlType( OPT_ADD, b1(0), pickRegister1, [ RouteType(0), RouteType(0), RouteType(0), RouteType(5), RouteType(4), RouteType(1), RouteType(0), RouteType(0)] ), - CtrlType( OPT_SUB, b1(0), pickRegister, [ + CtrlType( OPT_SUB, b1(0), pickRegister1, [ RouteType(5), RouteType(0), RouteType(0), RouteType(5), RouteType(0), RouteType(0), RouteType(0), RouteType(0)] ) ] src_data = [ [DataType(2, 1)],# DataType( 3, 1)],