forked from M-Labs/artiq
drtio: add repeater input support
This commit is contained in:
parent
c33f74dabe
commit
2b44786f73
|
@ -103,6 +103,61 @@ class RTPacketRepeater(Module):
|
||||||
timeout_counter = ClockDomainsRenamer("rtio")(WaitTimer(8191))
|
timeout_counter = ClockDomainsRenamer("rtio")(WaitTimer(8191))
|
||||||
self.submodules += timeout_counter
|
self.submodules += timeout_counter
|
||||||
|
|
||||||
|
# Read
|
||||||
|
rb_chan_sel = Signal(24)
|
||||||
|
rb_timeout = Signal(64)
|
||||||
|
self.sync.rtio += If(self.cri.cmd == cri.commands["read"],
|
||||||
|
rb_chan_sel.eq(self.cri.chan_sel),
|
||||||
|
rb_timeout.eq(self.cri.timestamp))
|
||||||
|
|
||||||
|
read_not = Signal()
|
||||||
|
read_no_event = Signal()
|
||||||
|
read_is_overflow = Signal()
|
||||||
|
read_data = Signal(32)
|
||||||
|
read_timestamp = Signal(64)
|
||||||
|
rtio_read_not = Signal()
|
||||||
|
rtio_read_not_ack = Signal()
|
||||||
|
rtio_read_no_event = Signal()
|
||||||
|
rtio_read_is_overflow = Signal()
|
||||||
|
rtio_read_data = Signal(32)
|
||||||
|
rtio_read_timestamp = Signal(64)
|
||||||
|
self.submodules += CrossDomainNotification("rtio_rx", "rtio",
|
||||||
|
read_not,
|
||||||
|
Cat(read_no_event, read_is_overflow, read_data, read_timestamp),
|
||||||
|
|
||||||
|
rtio_read_not, rtio_read_not_ack,
|
||||||
|
Cat(rtio_read_no_event, rtio_read_is_overflow,
|
||||||
|
rtio_read_data, rtio_read_timestamp))
|
||||||
|
self.comb += [
|
||||||
|
read_is_overflow.eq(rx_dp.packet_as["read_reply_noevent"].overflow),
|
||||||
|
read_data.eq(rx_dp.packet_as["read_reply"].data),
|
||||||
|
read_timestamp.eq(rx_dp.packet_as["read_reply"].timestamp)
|
||||||
|
]
|
||||||
|
|
||||||
|
# input status
|
||||||
|
i_status_wait_event = Signal()
|
||||||
|
i_status_overflow = Signal()
|
||||||
|
i_status_wait_status = Signal()
|
||||||
|
self.comb += self.cri.i_status.eq(Cat(
|
||||||
|
i_status_wait_event, i_status_overflow, i_status_wait_status))
|
||||||
|
|
||||||
|
load_read_reply = Signal()
|
||||||
|
self.sync.rtio += [
|
||||||
|
If(load_read_reply,
|
||||||
|
i_status_wait_event.eq(0),
|
||||||
|
i_status_overflow.eq(0),
|
||||||
|
If(rtio_read_no_event,
|
||||||
|
If(rtio_read_is_overflow,
|
||||||
|
i_status_overflow.eq(1)
|
||||||
|
).Else(
|
||||||
|
i_status_wait_event.eq(1)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
self.cri.i_data.eq(rtio_read_data),
|
||||||
|
self.cri.i_timestamp.eq(rtio_read_timestamp)
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
# Missed commands
|
# Missed commands
|
||||||
cri_ready = Signal()
|
cri_ready = Signal()
|
||||||
self.sync.rtio += [
|
self.sync.rtio += [
|
||||||
|
@ -111,7 +166,7 @@ class RTPacketRepeater(Module):
|
||||||
self.command_missed_cmd.eq(self.cri.cmd)
|
self.command_missed_cmd.eq(self.cri.cmd)
|
||||||
]
|
]
|
||||||
|
|
||||||
# TX FSM
|
# TX and CRI FSM
|
||||||
tx_fsm = ClockDomainsRenamer("rtio")(FSM(reset_state="IDLE"))
|
tx_fsm = ClockDomainsRenamer("rtio")(FSM(reset_state="IDLE"))
|
||||||
self.submodules += tx_fsm
|
self.submodules += tx_fsm
|
||||||
|
|
||||||
|
@ -126,9 +181,11 @@ class RTPacketRepeater(Module):
|
||||||
).Else(
|
).Else(
|
||||||
cri_ready.eq(1),
|
cri_ready.eq(1),
|
||||||
If(self.cri.cmd == cri.commands["write"], NextState("WRITE")),
|
If(self.cri.cmd == cri.commands["write"], NextState("WRITE")),
|
||||||
If(self.cri.cmd == cri.commands["get_buffer_space"], NextState("BUFFER_SPACE"))
|
If(self.cri.cmd == cri.commands["get_buffer_space"], NextState("BUFFER_SPACE")),
|
||||||
|
If(self.cri.cmd == cri.commands["read"], NextState("READ"))
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
tx_fsm.act("SET_TIME",
|
tx_fsm.act("SET_TIME",
|
||||||
tx_dp.send("set_time", timestamp=tsc_value),
|
tx_dp.send("set_time", timestamp=tsc_value),
|
||||||
If(tx_dp.packet_last,
|
If(tx_dp.packet_last,
|
||||||
|
@ -136,6 +193,7 @@ class RTPacketRepeater(Module):
|
||||||
NextState("IDLE")
|
NextState("IDLE")
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
tx_fsm.act("WRITE",
|
tx_fsm.act("WRITE",
|
||||||
tx_dp.send("write",
|
tx_dp.send("write",
|
||||||
timestamp=wb_timestamp,
|
timestamp=wb_timestamp,
|
||||||
|
@ -158,26 +216,46 @@ class RTPacketRepeater(Module):
|
||||||
NextState("IDLE")
|
NextState("IDLE")
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
tx_fsm.act("BUFFER_SPACE",
|
tx_fsm.act("BUFFER_SPACE",
|
||||||
tx_dp.send("buffer_space_request", destination=self.buffer_space_destination),
|
tx_dp.send("buffer_space_request", destination=self.buffer_space_destination),
|
||||||
If(tx_dp.packet_last,
|
If(tx_dp.packet_last,
|
||||||
buffer_space_not_ack.eq(1),
|
buffer_space_not_ack.eq(1),
|
||||||
NextState("WAIT_BUFFER_SPACE")
|
NextState("GET_BUFFER_SPACE_REPLY")
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
tx_fsm.act("WAIT_BUFFER_SPACE",
|
tx_fsm.act("GET_BUFFER_SPACE_REPLY",
|
||||||
timeout_counter.wait.eq(1),
|
timeout_counter.wait.eq(1),
|
||||||
If(timeout_counter.done,
|
If(timeout_counter.done,
|
||||||
self.err_buffer_space_timeout.eq(1),
|
self.err_buffer_space_timeout.eq(1),
|
||||||
NextState("IDLE")
|
NextState("READY")
|
||||||
).Else(
|
).Else(
|
||||||
If(buffer_space_not,
|
If(buffer_space_not,
|
||||||
self.cri.o_buffer_space_valid.eq(1),
|
self.cri.o_buffer_space_valid.eq(1),
|
||||||
NextState("IDLE")
|
NextState("READY")
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
tx_fsm.act("READ",
|
||||||
|
i_status_wait_status.eq(1),
|
||||||
|
tx_dp.send("read_request",
|
||||||
|
chan_sel=rb_chan_sel,
|
||||||
|
timeout=rb_timeout),
|
||||||
|
rtio_read_not_ack.eq(1),
|
||||||
|
If(tx_dp.packet_last,
|
||||||
|
NextState("GET_READ_REPLY")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
tx_fsm.act("GET_READ_REPLY",
|
||||||
|
i_status_wait_status.eq(1),
|
||||||
|
rtio_read_not_ack.eq(1),
|
||||||
|
If(rtio_read_not,
|
||||||
|
load_read_reply.eq(1),
|
||||||
|
NextState("READY")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
# RX FSM
|
# RX FSM
|
||||||
rx_fsm = ClockDomainsRenamer("rtio_rx")(FSM(reset_state="INPUT"))
|
rx_fsm = ClockDomainsRenamer("rtio_rx")(FSM(reset_state="INPUT"))
|
||||||
self.submodules += rx_fsm
|
self.submodules += rx_fsm
|
||||||
|
@ -192,6 +270,8 @@ class RTPacketRepeater(Module):
|
||||||
If(rx_dp.packet_last,
|
If(rx_dp.packet_last,
|
||||||
Case(rx_dp.packet_type, {
|
Case(rx_dp.packet_type, {
|
||||||
rx_plm.types["buffer_space_reply"]: NextState("BUFFER_SPACE"),
|
rx_plm.types["buffer_space_reply"]: NextState("BUFFER_SPACE"),
|
||||||
|
rx_plm.types["read_reply"]: NextState("READ_REPLY"),
|
||||||
|
rx_plm.types["read_reply_noevent"]: NextState("READ_REPLY_NOEVENT"),
|
||||||
"default": self.err_unknown_packet_type.eq(1)
|
"default": self.err_unknown_packet_type.eq(1)
|
||||||
})
|
})
|
||||||
).Else(
|
).Else(
|
||||||
|
@ -207,3 +287,13 @@ class RTPacketRepeater(Module):
|
||||||
rx_buffer_space.eq(rx_dp.packet_as["buffer_space_reply"].space),
|
rx_buffer_space.eq(rx_dp.packet_as["buffer_space_reply"].space),
|
||||||
NextState("INPUT")
|
NextState("INPUT")
|
||||||
)
|
)
|
||||||
|
rx_fsm.act("READ_REPLY",
|
||||||
|
read_not.eq(1),
|
||||||
|
read_no_event.eq(0),
|
||||||
|
NextState("INPUT")
|
||||||
|
)
|
||||||
|
rx_fsm.act("READ_REPLY_NOEVENT",
|
||||||
|
read_not.eq(1),
|
||||||
|
read_no_event.eq(1),
|
||||||
|
NextState("INPUT")
|
||||||
|
)
|
||||||
|
|
|
@ -128,3 +128,65 @@ class TestRepeater(unittest.TestCase):
|
||||||
current_request = field_dict["destination"]
|
current_request = field_dict["destination"]
|
||||||
|
|
||||||
run_simulation(dut, [send_requests(), send_replies(), pr.receive(receive)])
|
run_simulation(dut, [send_requests(), send_replies(), pr.receive(receive)])
|
||||||
|
|
||||||
|
def test_input(self):
|
||||||
|
for nwords in range(1, 8):
|
||||||
|
pt, pr, ts, dut = create_dut(nwords)
|
||||||
|
|
||||||
|
def read(chan_sel, timeout):
|
||||||
|
yield dut.cri.chan_sel.eq(chan_sel)
|
||||||
|
yield dut.cri.timestamp.eq(timeout)
|
||||||
|
yield dut.cri.cmd.eq(cri.commands["read"])
|
||||||
|
yield
|
||||||
|
yield dut.cri.cmd.eq(cri.commands["nop"])
|
||||||
|
yield
|
||||||
|
status = yield dut.cri.i_status
|
||||||
|
while status & 4:
|
||||||
|
yield
|
||||||
|
status = yield dut.cri.i_status
|
||||||
|
if status & 0x1:
|
||||||
|
return "timeout"
|
||||||
|
if status & 0x2:
|
||||||
|
return "overflow"
|
||||||
|
if status & 0x8:
|
||||||
|
return "destination unreachable"
|
||||||
|
return ((yield dut.cri.i_data),
|
||||||
|
(yield dut.cri.i_timestamp))
|
||||||
|
|
||||||
|
def send_requests():
|
||||||
|
for timeout in range(20, 200000, 100000):
|
||||||
|
for chan_sel in range(3):
|
||||||
|
data, timestamp = yield from read(chan_sel, timeout)
|
||||||
|
self.assertEqual(data, chan_sel*2)
|
||||||
|
self.assertEqual(timestamp, timeout//2)
|
||||||
|
|
||||||
|
i2 = yield from read(10, 400000)
|
||||||
|
self.assertEqual(i2, "timeout")
|
||||||
|
i3 = yield from read(11, 400000)
|
||||||
|
self.assertEqual(i3, "overflow")
|
||||||
|
|
||||||
|
current_request = None
|
||||||
|
|
||||||
|
@passive
|
||||||
|
def send_replies():
|
||||||
|
nonlocal current_request
|
||||||
|
while True:
|
||||||
|
while current_request is None:
|
||||||
|
yield
|
||||||
|
chan_sel, timeout = current_request
|
||||||
|
if chan_sel == 10:
|
||||||
|
yield from pt.send("read_reply_noevent", overflow=0)
|
||||||
|
elif chan_sel == 11:
|
||||||
|
yield from pt.send("read_reply_noevent", overflow=1)
|
||||||
|
else:
|
||||||
|
yield from pt.send("read_reply", data=chan_sel*2, timestamp=timeout//2)
|
||||||
|
current_request = None
|
||||||
|
|
||||||
|
def receive(packet_type, field_dict, trailer):
|
||||||
|
nonlocal current_request
|
||||||
|
self.assertEqual(packet_type, "read_request")
|
||||||
|
self.assertEqual(trailer, [])
|
||||||
|
self.assertEqual(current_request, None)
|
||||||
|
current_request = (field_dict["chan_sel"], field_dict["timeout"])
|
||||||
|
|
||||||
|
run_simulation(dut, [send_requests(), send_replies(), pr.receive(receive)])
|
||||||
|
|
Loading…
Reference in New Issue