From 2cbd597416368268dd2adb275f89164d325badbb Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Tue, 6 Mar 2018 16:50:21 +0000 Subject: [PATCH] LaneDistributor: style and signal consolidation [NFC] --- artiq/gateware/rtio/sed/lane_distributor.py | 35 ++++++++++++------- .../test/rtio/test_sed_lane_distributor.py | 20 ++++++++--- 2 files changed, 37 insertions(+), 18 deletions(-) diff --git a/artiq/gateware/rtio/sed/lane_distributor.py b/artiq/gateware/rtio/sed/lane_distributor.py index 8fe7af477..a34e98519 100644 --- a/artiq/gateware/rtio/sed/lane_distributor.py +++ b/artiq/gateware/rtio/sed/lane_distributor.py @@ -28,7 +28,8 @@ class LaneDistributor(Module): # an underflow, at the time when the CRI write happens, and to a channel # with zero latency compensation. This is synchronous to the system clock # domain. - self.minimum_coarse_timestamp = Signal(64-glbl_fine_ts_width) + us_timestamp_width = 64 - glbl_fine_ts_width + self.minimum_coarse_timestamp = Signal(us_timestamp_width) self.output = [Record(layouts.fifo_ingress(seqn_width, layout_payload)) for _ in range(lane_count)] @@ -50,10 +51,10 @@ class LaneDistributor(Module): current_lane = Signal(max=lane_count) # The last coarse timestamp received from the CRI, after compensation. # Used to determine when to switch lanes. - last_coarse_timestamp = Signal(64-glbl_fine_ts_width) + last_coarse_timestamp = Signal(us_timestamp_width) # The last coarse timestamp written to each lane. Used to detect # sequence errors. - last_lane_coarse_timestamps = Array(Signal(64-glbl_fine_ts_width) + last_lane_coarse_timestamps = Array(Signal(us_timestamp_width) for _ in range(lane_count)) # Sequence number counter. The sequence number is used to determine which # event wins during a replace. @@ -72,7 +73,6 @@ class LaneDistributor(Module): self.comb += lio.payload.data.eq(self.cri.o_data) # when timestamp and channel arrive in cycle #1, prepare computations - us_timestamp_width = 64 - glbl_fine_ts_width coarse_timestamp = Signal(us_timestamp_width) self.comb += coarse_timestamp.eq(self.cri.timestamp[glbl_fine_ts_width:]) min_minus_timestamp = Signal((us_timestamp_width + 1, True)) @@ -96,15 +96,17 @@ class LaneDistributor(Module): for channel in quash_channels: self.sync += If(self.cri.chan_sel[:16] == channel, quash.eq(1)) + assert all(abs(c) < 1 << 14 - 1 for c in compensation) latency_compensation = Memory(14, len(compensation), init=compensation) latency_compensation_port = latency_compensation.get_port() - self.specials += latency_compensation, latency_compensation_port - self.comb += latency_compensation_port.adr.eq(self.cri.chan_sel[:16]) + self.specials += latency_compensation, latency_compensation_port + self.comb += latency_compensation_port.adr.eq(self.cri.chan_sel[:16]) # cycle #2, write compensation = Signal((14, True)) self.comb += compensation.eq(latency_compensation_port.dat_r) timestamp_above_min = Signal() + timestamp_above_last = Signal() timestamp_above_laneA_min = Signal() timestamp_above_laneB_min = Signal() timestamp_above_lane_min = Signal() @@ -119,8 +121,9 @@ class LaneDistributor(Module): timestamp_above_min.eq(min_minus_timestamp - compensation < 0), timestamp_above_laneA_min.eq(laneAmin_minus_timestamp - compensation < 0), timestamp_above_laneB_min.eq(laneBmin_minus_timestamp - compensation < 0), - If(force_laneB | (last_minus_timestamp - compensation >= 0), - use_lanen.eq(current_lane + 1), + timestamp_above_last.eq(last_minus_timestamp - compensation < 0), + If(force_laneB | ~timestamp_above_last, + use_lanen.eq(current_lane_plus_one), use_laneB.eq(1) ).Else( use_lanen.eq(current_lane), @@ -128,10 +131,16 @@ class LaneDistributor(Module): ), timestamp_above_lane_min.eq(Mux(use_laneB, timestamp_above_laneB_min, timestamp_above_laneA_min)), - If(~quash, - do_write.eq((self.cri.cmd == cri.commands["write"]) & timestamp_above_min & timestamp_above_lane_min), - do_underflow.eq((self.cri.cmd == cri.commands["write"]) & ~timestamp_above_min), - do_sequence_error.eq((self.cri.cmd == cri.commands["write"]) & timestamp_above_min & ~timestamp_above_lane_min), + If(~quash & (self.cri.cmd == cri.commands["write"]), + If(timestamp_above_min, + If(timestamp_above_lane_min, + do_write.eq(1) + ).Else( + do_sequence_error.eq(1) + ) + ).Else( + do_underflow.eq(1) + ) ), Array(lio.we for lio in self.output)[use_lanen].eq(do_write) ] @@ -139,7 +148,7 @@ class LaneDistributor(Module): self.comb += compensated_timestamp.eq(self.cri.timestamp + (compensation << glbl_fine_ts_width)) self.sync += [ If(do_write, - If(use_laneB, current_lane.eq(current_lane + 1)), + current_lane.eq(use_lanen), last_coarse_timestamp.eq(compensated_timestamp[glbl_fine_ts_width:]), last_lane_coarse_timestamps[use_lanen].eq(compensated_timestamp[glbl_fine_ts_width:]), seqn.eq(seqn + 1), diff --git a/artiq/gateware/test/rtio/test_sed_lane_distributor.py b/artiq/gateware/test/rtio/test_sed_lane_distributor.py index c02b6c4bb..609877643 100644 --- a/artiq/gateware/test/rtio/test_sed_lane_distributor.py +++ b/artiq/gateware/test/rtio/test_sed_lane_distributor.py @@ -77,12 +77,15 @@ def simulate(input_events, compensation=None, wait=True): class TestLaneDistributor(unittest.TestCase): def test_regular(self): + # N sequential events, all on lane 0 N = 16 output, access_results = simulate([(42+n, (n+1)*8) for n in range(N)], wait=False) self.assertEqual(output, [(0, n, 42+n, (n+1)*8) for n in range(N)]) self.assertEqual(access_results, [("ok", 0)]*N) def test_wait_time(self): + # LANE_COUNT simultaneous events should be distributed and written to + # the lanes when the latter are writable output, access_results = simulate([(42+n, 8) for n in range(LANE_COUNT)]) self.assertEqual(output, [(n, n, 42+n, 8) for n in range(LANE_COUNT)]) expected_access_results = [("ok", 0)]*LANE_COUNT @@ -91,33 +94,40 @@ class TestLaneDistributor(unittest.TestCase): self.assertEqual(access_results, expected_access_results) def test_lane_switch(self): + # N events separated by one fine timestamp distributed onto lanes + # LANE_COUNT == 1 << fine_ts_width N = 32 output, access_results = simulate([(42+n, n+8) for n in range(N)], wait=False) self.assertEqual(output, [((n-n//8) % LANE_COUNT, n, 42+n, n+8) for n in range(N)]) self.assertEqual([ar[0] for ar in access_results], ["ok"]*N) def test_sequence_error(self): + # LANE_COUNT + 1 simultaneous events, the last one being discarded due + # to sequence error, followed by a valid event input_events = [(42+n, 8) for n in range(LANE_COUNT+1)] input_events.append((42+LANE_COUNT+1, 16)) output, access_results = simulate(input_events) self.assertEqual(len(output), len(input_events)-1) # event with sequence error must get discarded + self.assertEqual(output[-1], (0, LANE_COUNT, 42+LANE_COUNT+1, 16)) self.assertEqual([ar[0] for ar in access_results[:LANE_COUNT]], ["ok"]*LANE_COUNT) self.assertEqual(access_results[LANE_COUNT][0], "sequence_error") + self.assertEqual(access_results[LANE_COUNT + 1][0], "ok") def test_underflow(self): + # N sequential events except the penultimate which underflows N = 16 - input_events = [(42+n, (n+1)*8) for n in range(N-2)] - input_events.append((0, 0)) # timestamp < 8 underflows - input_events.append((42+N-2, N*8)) + input_events = [(42+n, (n+1)*8) for n in range(N)] + input_events[-2] = (0, 0) # timestamp < 8 underflows output, access_results = simulate(input_events) self.assertEqual(len(output), len(input_events)-1) # event with underflow must get discarded self.assertEqual([ar[0] for ar in access_results[:N-2]], ["ok"]*(N-2)) self.assertEqual(access_results[N-2][0], "underflow") - self.assertEqual(output[N-2], (0, N-2, 42+N-2, N*8)) + self.assertEqual(output[N-2], (0, N-2, 42+N-1, N*8)) self.assertEqual(access_results[N-1][0], "ok") def test_spread(self): - # get to lane 6 + # 6 simultaneous events to reach lane 6 and 7 which are not writable + # for 1 and 4 cycles respectively causing a forced lane switch input_events = [(42+n, 8) for n in range(7)] input_events.append((100, 16)) input_events.append((100, 32))