artiq/artiq/gateware/drtio/rt_packet_satellite.py

250 lines
8.3 KiB
Python

"""Real-time packet layer for satellites"""
from migen import *
from migen.genlib.fsm import *
from migen.genlib.misc import WaitTimer
from artiq.gateware.rtio import cri
from artiq.gateware.drtio.rt_serializer import *
class RTPacketSatellite(Module):
def __init__(self, link_layer, interface=None):
self.reset = Signal()
self.unknown_packet_type = Signal()
self.packet_truncated = Signal()
self.buffer_space_timeout = Signal()
self.tsc_load = Signal()
self.tsc_load_value = Signal(64)
self.async_msg_stb = Signal()
self.async_msg_ack = Signal()
if interface is None:
interface = cri.Interface()
self.cri = interface
# # #
# RX/TX datapath
assert len(link_layer.tx_rt_data) == len(link_layer.rx_rt_data)
assert len(link_layer.tx_rt_data) % 8 == 0
ws = len(link_layer.tx_rt_data)
rx_plm = get_m2s_layouts(ws)
rx_dp = ReceiveDatapath(
link_layer.rx_rt_frame, link_layer.rx_rt_data, rx_plm)
self.submodules += rx_dp
tx_plm = get_s2m_layouts(ws)
tx_dp = TransmitDatapath(
link_layer.tx_rt_frame, link_layer.tx_rt_data, tx_plm)
self.submodules += tx_dp
# RX write data buffer
write_data_buffer_load = Signal()
write_data_buffer_cnt = Signal(max=512//ws+1)
write_data_buffer = Signal(512)
self.sync += \
If(write_data_buffer_load,
Case(write_data_buffer_cnt,
{i: write_data_buffer[i*ws:(i+1)*ws].eq(rx_dp.data_r)
for i in range(512//ws)}),
write_data_buffer_cnt.eq(write_data_buffer_cnt + 1)
).Else(
write_data_buffer_cnt.eq(0)
)
# RX->TX
echo_req = Signal()
buffer_space_set = Signal()
buffer_space_req = Signal()
buffer_space_ack = Signal()
self.sync += [
If(buffer_space_ack, buffer_space_req.eq(0)),
If(buffer_space_set, buffer_space_req.eq(1)),
]
buffer_space_update = Signal()
buffer_space = Signal(16)
self.sync += If(buffer_space_update, buffer_space.eq(self.cri.o_buffer_space))
load_read_request = Signal()
clear_read_request = Signal()
read_request_pending = Signal()
self.sync += [
If(clear_read_request | self.reset,
read_request_pending.eq(0)
),
If(load_read_request,
read_request_pending.eq(1),
)
]
self.sync += If(self.async_msg_ack, self.async_msg_stb.eq(0))
# RX FSM
cri_read = Signal()
cri_buffer_space = Signal()
self.comb += [
self.tsc_load_value.eq(
rx_dp.packet_as["set_time"].timestamp),
If(cri_read | read_request_pending,
self.cri.chan_sel.eq(
rx_dp.packet_as["read_request"].chan_sel),
).Elif(cri_buffer_space,
self.cri.chan_sel.eq(
rx_dp.packet_as["buffer_space_request"].destination << 16)
).Else(
self.cri.chan_sel.eq(
rx_dp.packet_as["write"].chan_sel),
),
self.cri.i_timeout.eq(
rx_dp.packet_as["read_request"].timeout),
self.cri.o_timestamp.eq(
rx_dp.packet_as["write"].timestamp),
self.cri.o_address.eq(
rx_dp.packet_as["write"].address),
self.cri.o_data.eq(
Cat(rx_dp.packet_as["write"].short_data, write_data_buffer)),
]
rx_fsm = FSM(reset_state="INPUT")
self.submodules += rx_fsm
ongoing_packet_next = Signal()
ongoing_packet = Signal()
self.sync += ongoing_packet.eq(ongoing_packet_next)
timeout_counter = WaitTimer(8191)
self.submodules += timeout_counter
rx_fsm.act("INPUT",
If(rx_dp.frame_r,
rx_dp.packet_buffer_load.eq(1),
If(rx_dp.packet_last,
Case(rx_dp.packet_type, {
# echo must have fixed latency, so there is no memory
# mechanism
rx_plm.types["echo_request"]: echo_req.eq(1),
rx_plm.types["set_time"]: NextState("SET_TIME"),
rx_plm.types["write"]: NextState("WRITE"),
rx_plm.types["buffer_space_request"]: NextState("BUFFER_SPACE_REQUEST"),
rx_plm.types["read_request"]: NextState("READ_REQUEST"),
"default": self.unknown_packet_type.eq(1)
})
).Else(
ongoing_packet_next.eq(1)
),
If(~rx_dp.frame_r & ongoing_packet,
self.packet_truncated.eq(1)
)
)
)
rx_fsm.act("SET_TIME",
self.tsc_load.eq(1),
NextState("INPUT")
)
# CRI mux defaults to write information
rx_fsm.act("WRITE",
If(write_data_buffer_cnt == rx_dp.packet_as["write"].extra_data_cnt,
NextState("WRITE_CMD")
).Else(
write_data_buffer_load.eq(1),
If(~rx_dp.frame_r,
self.packet_truncated.eq(1),
NextState("INPUT")
)
)
)
rx_fsm.act("WRITE_CMD",
self.cri.cmd.eq(cri.commands["write"]),
NextState("INPUT")
)
rx_fsm.act("BUFFER_SPACE_REQUEST",
cri_buffer_space.eq(1),
NextState("BUFFER_SPACE_REQUEST_CMD")
)
rx_fsm.act("BUFFER_SPACE_REQUEST_CMD",
cri_buffer_space.eq(1),
self.cri.cmd.eq(cri.commands["get_buffer_space"]),
NextState("BUFFER_SPACE")
)
rx_fsm.act("BUFFER_SPACE",
cri_buffer_space.eq(1),
timeout_counter.wait.eq(1),
If(timeout_counter.done,
self.buffer_space_timeout.eq(1),
NextState("INPUT")
).Elif(self.cri.o_buffer_space_valid,
buffer_space_set.eq(1),
buffer_space_update.eq(1),
NextState("INPUT")
)
)
rx_fsm.act("READ_REQUEST",
cri_read.eq(1),
NextState("READ_REQUEST_CMD")
)
rx_fsm.act("READ_REQUEST_CMD",
load_read_request.eq(1),
cri_read.eq(1),
self.cri.cmd.eq(cri.commands["read"]),
NextState("INPUT")
)
# TX FSM
tx_fsm = FSM(reset_state="IDLE")
self.submodules += tx_fsm
tx_fsm.act("IDLE",
If(echo_req, NextState("ECHO")),
If(self.async_msg_stb, NextState("ASYNC_MESSAGES_READY")),
If(buffer_space_req, NextState("BUFFER_SPACE")),
If(read_request_pending & ~self.cri.i_status[2],
NextState("READ"),
If(self.cri.i_status[0], NextState("READ_TIMEOUT")),
If(self.cri.i_status[1], NextState("READ_OVERFLOW"))
)
)
tx_fsm.act("ECHO",
tx_dp.send("echo_reply"),
If(tx_dp.packet_last, NextState("IDLE"))
)
tx_fsm.act("ASYNC_MESSAGES_READY",
self.async_msg_ack.eq(1),
tx_dp.send("async_messages_ready"),
If(tx_dp.packet_last, NextState("IDLE"))
)
tx_fsm.act("BUFFER_SPACE",
buffer_space_ack.eq(1),
tx_dp.send("buffer_space_reply", space=buffer_space),
If(tx_dp.packet_last, NextState("IDLE"))
)
tx_fsm.act("READ_TIMEOUT",
tx_dp.send("read_reply_noevent", overflow=0),
clear_read_request.eq(1),
If(tx_dp.packet_last, NextState("IDLE"))
)
tx_fsm.act("READ_OVERFLOW",
tx_dp.send("read_reply_noevent", overflow=1),
clear_read_request.eq(1),
If(tx_dp.packet_last, NextState("IDLE"))
)
tx_fsm.act("READ",
tx_dp.send("read_reply",
timestamp=self.cri.i_timestamp,
data=self.cri.i_data),
If(tx_dp.packet_last,
clear_read_request.eq(1),
NextState("IDLE")
)
)