forked from M-Labs/artiq
1
0
Fork 0

LaneDistributor: style and signal consolidation [NFC]

This commit is contained in:
Robert Jördens 2018-03-06 16:50:21 +00:00
parent 916197c4d7
commit 2cbd597416
2 changed files with 37 additions and 18 deletions

View File

@ -28,7 +28,8 @@ class LaneDistributor(Module):
# an underflow, at the time when the CRI write happens, and to a channel # 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 # with zero latency compensation. This is synchronous to the system clock
# domain. # 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)) self.output = [Record(layouts.fifo_ingress(seqn_width, layout_payload))
for _ in range(lane_count)] for _ in range(lane_count)]
@ -50,10 +51,10 @@ class LaneDistributor(Module):
current_lane = Signal(max=lane_count) current_lane = Signal(max=lane_count)
# The last coarse timestamp received from the CRI, after compensation. # The last coarse timestamp received from the CRI, after compensation.
# Used to determine when to switch lanes. # 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 # The last coarse timestamp written to each lane. Used to detect
# sequence errors. # 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)) for _ in range(lane_count))
# Sequence number counter. The sequence number is used to determine which # Sequence number counter. The sequence number is used to determine which
# event wins during a replace. # event wins during a replace.
@ -72,7 +73,6 @@ class LaneDistributor(Module):
self.comb += lio.payload.data.eq(self.cri.o_data) self.comb += lio.payload.data.eq(self.cri.o_data)
# when timestamp and channel arrive in cycle #1, prepare computations # when timestamp and channel arrive in cycle #1, prepare computations
us_timestamp_width = 64 - glbl_fine_ts_width
coarse_timestamp = Signal(us_timestamp_width) coarse_timestamp = Signal(us_timestamp_width)
self.comb += coarse_timestamp.eq(self.cri.timestamp[glbl_fine_ts_width:]) self.comb += coarse_timestamp.eq(self.cri.timestamp[glbl_fine_ts_width:])
min_minus_timestamp = Signal((us_timestamp_width + 1, True)) min_minus_timestamp = Signal((us_timestamp_width + 1, True))
@ -96,6 +96,7 @@ class LaneDistributor(Module):
for channel in quash_channels: for channel in quash_channels:
self.sync += If(self.cri.chan_sel[:16] == channel, quash.eq(1)) 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 = Memory(14, len(compensation), init=compensation)
latency_compensation_port = latency_compensation.get_port() latency_compensation_port = latency_compensation.get_port()
self.specials += latency_compensation, latency_compensation_port self.specials += latency_compensation, latency_compensation_port
@ -105,6 +106,7 @@ class LaneDistributor(Module):
compensation = Signal((14, True)) compensation = Signal((14, True))
self.comb += compensation.eq(latency_compensation_port.dat_r) self.comb += compensation.eq(latency_compensation_port.dat_r)
timestamp_above_min = Signal() timestamp_above_min = Signal()
timestamp_above_last = Signal()
timestamp_above_laneA_min = Signal() timestamp_above_laneA_min = Signal()
timestamp_above_laneB_min = Signal() timestamp_above_laneB_min = Signal()
timestamp_above_lane_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_min.eq(min_minus_timestamp - compensation < 0),
timestamp_above_laneA_min.eq(laneAmin_minus_timestamp - compensation < 0), timestamp_above_laneA_min.eq(laneAmin_minus_timestamp - compensation < 0),
timestamp_above_laneB_min.eq(laneBmin_minus_timestamp - compensation < 0), timestamp_above_laneB_min.eq(laneBmin_minus_timestamp - compensation < 0),
If(force_laneB | (last_minus_timestamp - compensation >= 0), timestamp_above_last.eq(last_minus_timestamp - compensation < 0),
use_lanen.eq(current_lane + 1), If(force_laneB | ~timestamp_above_last,
use_lanen.eq(current_lane_plus_one),
use_laneB.eq(1) use_laneB.eq(1)
).Else( ).Else(
use_lanen.eq(current_lane), 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)), timestamp_above_lane_min.eq(Mux(use_laneB, timestamp_above_laneB_min, timestamp_above_laneA_min)),
If(~quash, If(~quash & (self.cri.cmd == cri.commands["write"]),
do_write.eq((self.cri.cmd == cri.commands["write"]) & timestamp_above_min & timestamp_above_lane_min), If(timestamp_above_min,
do_underflow.eq((self.cri.cmd == cri.commands["write"]) & ~timestamp_above_min), If(timestamp_above_lane_min,
do_sequence_error.eq((self.cri.cmd == cri.commands["write"]) & timestamp_above_min & ~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) 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.comb += compensated_timestamp.eq(self.cri.timestamp + (compensation << glbl_fine_ts_width))
self.sync += [ self.sync += [
If(do_write, 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_coarse_timestamp.eq(compensated_timestamp[glbl_fine_ts_width:]),
last_lane_coarse_timestamps[use_lanen].eq(compensated_timestamp[glbl_fine_ts_width:]), last_lane_coarse_timestamps[use_lanen].eq(compensated_timestamp[glbl_fine_ts_width:]),
seqn.eq(seqn + 1), seqn.eq(seqn + 1),

View File

@ -77,12 +77,15 @@ def simulate(input_events, compensation=None, wait=True):
class TestLaneDistributor(unittest.TestCase): class TestLaneDistributor(unittest.TestCase):
def test_regular(self): def test_regular(self):
# N sequential events, all on lane 0
N = 16 N = 16
output, access_results = simulate([(42+n, (n+1)*8) for n in range(N)], wait=False) 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(output, [(0, n, 42+n, (n+1)*8) for n in range(N)])
self.assertEqual(access_results, [("ok", 0)]*N) self.assertEqual(access_results, [("ok", 0)]*N)
def test_wait_time(self): 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)]) 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)]) self.assertEqual(output, [(n, n, 42+n, 8) for n in range(LANE_COUNT)])
expected_access_results = [("ok", 0)]*LANE_COUNT expected_access_results = [("ok", 0)]*LANE_COUNT
@ -91,33 +94,40 @@ class TestLaneDistributor(unittest.TestCase):
self.assertEqual(access_results, expected_access_results) self.assertEqual(access_results, expected_access_results)
def test_lane_switch(self): def test_lane_switch(self):
# N events separated by one fine timestamp distributed onto lanes
# LANE_COUNT == 1 << fine_ts_width
N = 32 N = 32
output, access_results = simulate([(42+n, n+8) for n in range(N)], wait=False) 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(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) self.assertEqual([ar[0] for ar in access_results], ["ok"]*N)
def test_sequence_error(self): 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 = [(42+n, 8) for n in range(LANE_COUNT+1)]
input_events.append((42+LANE_COUNT+1, 16)) input_events.append((42+LANE_COUNT+1, 16))
output, access_results = simulate(input_events) output, access_results = simulate(input_events)
self.assertEqual(len(output), len(input_events)-1) # event with sequence error must get discarded 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([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][0], "sequence_error")
self.assertEqual(access_results[LANE_COUNT + 1][0], "ok")
def test_underflow(self): def test_underflow(self):
# N sequential events except the penultimate which underflows
N = 16 N = 16
input_events = [(42+n, (n+1)*8) for n in range(N-2)] input_events = [(42+n, (n+1)*8) for n in range(N)]
input_events.append((0, 0)) # timestamp < 8 underflows input_events[-2] = (0, 0) # timestamp < 8 underflows
input_events.append((42+N-2, N*8))
output, access_results = simulate(input_events) output, access_results = simulate(input_events)
self.assertEqual(len(output), len(input_events)-1) # event with underflow must get discarded 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([ar[0] for ar in access_results[:N-2]], ["ok"]*(N-2))
self.assertEqual(access_results[N-2][0], "underflow") 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") self.assertEqual(access_results[N-1][0], "ok")
def test_spread(self): 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 = [(42+n, 8) for n in range(7)]
input_events.append((100, 16)) input_events.append((100, 16))
input_events.append((100, 32)) input_events.append((100, 32))