diff --git a/noc/LinkOrRTL.py b/noc/LinkOrRTL.py index 2efd7a06..ba6b376a 100644 --- a/noc/LinkOrRTL.py +++ b/noc/LinkOrRTL.py @@ -23,6 +23,7 @@ def construct(s, DataType): # Interface s.recv_fu = RecvIfcRTL(DataType) s.recv_xbar = RecvIfcRTL(DataType) + s.fu_xbar_rdy = InPort(b1) s.send = SendIfcRTL(DataType) @update @@ -30,20 +31,26 @@ def process(): # Initializes the delivered message. s.send.msg @= DataType() - # The messages from two sources (i.e., xbar and FU) won't be valid - # simultaneously (confliction would be caused if they both are valid), - # which is guaranteed by the compiler/software. - s.send.msg.predicate @= s.recv_fu.msg.predicate | s.recv_xbar.msg.predicate - s.send.msg.payload @= s.recv_xbar.msg.payload | s.recv_fu.msg.payload + fu_active = s.recv_fu.val & s.fu_xbar_rdy + xbar_active = s.recv_xbar.val + + if fu_active: + s.send.msg.predicate @= s.send.msg.predicate | s.recv_fu.msg.predicate + s.send.msg.payload @= s.send.msg.payload | s.recv_fu.msg.payload + if xbar_active: + s.send.msg.predicate @= s.send.msg.predicate | s.recv_xbar.msg.predicate + s.send.msg.payload @= s.send.msg.payload | s.recv_xbar.msg.payload # FIXME: bypass won't be necessary any more with separate xbar design. # s.send.msg.bypass @= 0 # s.send.msg.delay @= s.recv_fu.msg.delay | s.recv_xbar.msg.delay - s.send.val @= s.recv_fu.val | s.recv_xbar.val - s.recv_fu.rdy @= s.send.rdy + # Only let FU traffic win once the FU crossbar has actually committed + # the multicast/send for this cycle; otherwise the link can expose + # transient FU output and create cross-tile bubbles. + s.send.val @= fu_active | xbar_active + s.recv_fu.rdy @= s.send.rdy & s.fu_xbar_rdy s.recv_xbar.rdy @= s.send.rdy def line_trace(s): return f"from_fu:{s.recv_fu.msg} or from_xbar:{s.recv_xbar.msg} => out:{s.send.msg} ## " - diff --git a/noc/test/LinkOrRTL_test.py b/noc/test/LinkOrRTL_test.py index 1ae08b6c..29ee6355 100644 --- a/noc/test/LinkOrRTL_test.py +++ b/noc/test/LinkOrRTL_test.py @@ -33,6 +33,7 @@ def construct(s, MsgType, src_msgs_0, src_msgs_1, sink_msgs): s.src0.send //= s.dut.recv_fu s.src1.send //= s.dut.recv_xbar s.dut.send //= s.sink.recv + s.dut.fu_xbar_rdy //= 1 def done(s): return s.src0.done() and s.src1.done() and s.sink.done() @@ -83,3 +84,38 @@ def test_simple(): th = TestHarness(DataType, test_msgs_0, test_msgs_1, sink_msgs) run_sim(th) +def test_invalid_fu_msg_does_not_pollute_xbar_output(): + dut = LinkOrRTL(DataType) + dut.elaborate() + dut.apply(DefaultPassGroup()) + dut.sim_reset() + + dut.recv_fu.val @= 0 + dut.recv_fu.msg @= DataType(0, 1) + dut.recv_xbar.val @= 1 + dut.recv_xbar.msg @= DataType(1, 1) + dut.fu_xbar_rdy @= 1 + dut.send.rdy @= 1 + dut.sim_eval_combinational() + + assert dut.send.val == b1(1) + assert dut.send.msg == DataType(1, 1) + +def test_uncommitted_fu_msg_is_not_consumed(): + dut = LinkOrRTL(DataType) + dut.elaborate() + dut.apply(DefaultPassGroup()) + dut.sim_reset() + + dut.recv_fu.val @= 1 + dut.recv_fu.msg @= DataType(7, 1) + dut.recv_xbar.val @= 1 + dut.recv_xbar.msg @= DataType(1, 1) + dut.fu_xbar_rdy @= 0 + dut.send.rdy @= 1 + dut.sim_eval_combinational() + + assert dut.send.val == b1(1) + assert dut.send.msg == DataType(1, 1) + assert dut.recv_fu.rdy == b1(0) + assert dut.recv_xbar.rdy == b1(1)