forked from M-Labs/artiq
LaneDistributor: style and signal consolidation [NFC]
This commit is contained in:
parent
916197c4d7
commit
2cbd597416
|
@ -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,15 +96,17 @@ 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
|
||||||
self.comb += latency_compensation_port.adr.eq(self.cri.chan_sel[:16])
|
self.comb += latency_compensation_port.adr.eq(self.cri.chan_sel[:16])
|
||||||
|
|
||||||
# cycle #2, write
|
# cycle #2, write
|
||||||
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),
|
||||||
|
|
|
@ -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))
|
||||||
|
|
Loading…
Reference in New Issue