artiq/artiq/gateware/drtio/rt_packet_satellite.py

239 lines
8.0 KiB
Python

"""Real-time packet layer for satellites"""
from migen import *
from migen.genlib.fsm import *
from artiq.gateware.drtio.rt_serializer import *
class RTPacketSatellite(Module):
def __init__(self, link_layer):
self.unknown_packet_type = Signal()
self.packet_truncated = Signal()
self.tsc_load = Signal()
self.tsc_load_value = Signal(64)
self.tsc_input = Signal(64)
self.reset = Signal(reset=1)
self.reset_phy = Signal(reset=1)
self.fifo_space_channel = Signal(16)
self.fifo_space_update = Signal()
self.fifo_space = Signal(16)
# write parameters are stable one cycle before stb is asserted,
# and when stb is asserted.
self.write_stb = Signal()
self.write_timestamp = Signal(64)
self.write_channel = Signal(16)
self.write_address = Signal(16)
self.write_data = Signal(512)
self.write_overflow = Signal()
self.write_underflow = Signal()
self.read_channel = Signal(16)
self.read_readable = Signal()
self.read_consume = Signal()
self.read_data = Signal(32)
self.read_timestamp = Signal(64)
self.read_overflow = Signal()
self.read_overflow_ack = Signal()
# # #
# 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()
fifo_space_set = Signal()
fifo_space_req = Signal()
fifo_space_ack = Signal()
self.sync += [
If(fifo_space_ack, fifo_space_req.eq(0)),
If(fifo_space_set, fifo_space_req.eq(1)),
]
# RX FSM
self.comb += [
self.tsc_load_value.eq(
rx_dp.packet_as["set_time"].timestamp),
self.fifo_space_channel.eq(
rx_dp.packet_as["fifo_space_request"].channel),
self.write_timestamp.eq(
rx_dp.packet_as["write"].timestamp),
self.write_channel.eq(
rx_dp.packet_as["write"].channel),
self.write_address.eq(
rx_dp.packet_as["write"].address),
self.write_data.eq(
Cat(rx_dp.packet_as["write"].short_data, write_data_buffer)),
]
reset = Signal()
reset_phy = Signal()
self.sync += [
self.reset.eq(reset),
self.reset_phy.eq(reset_phy)
]
load_read_request = Signal()
clear_read_request = Signal()
read_request_pending = Signal()
read_request_time_limit = Signal(64)
read_request_timeout = Signal()
read_request_wait = Signal() # 1 cycle latency channel→(data,overflow) and time_limit→timeout
self.sync += [
If(clear_read_request | self.reset,
read_request_pending.eq(0)
),
read_request_wait.eq(0),
If(load_read_request,
read_request_pending.eq(1),
read_request_wait.eq(1),
self.read_channel.eq(rx_dp.packet_as["read_request"].channel),
read_request_time_limit.eq(rx_dp.packet_as["read_request"].timeout)
),
read_request_timeout.eq(self.tsc_input >= read_request_time_limit),
]
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)
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["reset"]: NextState("RESET"),
rx_plm.types["write"]: NextState("WRITE"),
rx_plm.types["fifo_space_request"]: NextState("FIFO_SPACE"),
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")
)
rx_fsm.act("RESET",
If(rx_dp.packet_as["reset"].phy,
reset_phy.eq(1)
).Else(
reset.eq(1)
),
NextState("INPUT")
)
rx_fsm.act("WRITE",
If(write_data_buffer_cnt == rx_dp.packet_as["write"].extra_data_cnt,
self.write_stb.eq(1),
NextState("INPUT")
).Else(
write_data_buffer_load.eq(1),
If(~rx_dp.frame_r,
self.packet_truncated.eq(1),
NextState("INPUT")
)
)
)
rx_fsm.act("FIFO_SPACE",
fifo_space_set.eq(1),
self.fifo_space_update.eq(1),
NextState("INPUT")
)
rx_fsm.act("READ_REQUEST",
load_read_request.eq(1),
NextState("INPUT")
)
# TX FSM
tx_fsm = FSM(reset_state="IDLE")
self.submodules += tx_fsm
tx_fsm.act("IDLE",
If(echo_req, NextState("ECHO")),
If(fifo_space_req, NextState("FIFO_SPACE")),
If(~read_request_wait & read_request_pending,
If(read_request_timeout, NextState("READ_TIMEOUT")),
If(self.read_overflow, NextState("READ_OVERFLOW")),
If(self.read_readable, NextState("READ"))
)
)
tx_fsm.act("ECHO",
tx_dp.send("echo_reply"),
If(tx_dp.packet_last, NextState("IDLE"))
)
tx_fsm.act("FIFO_SPACE",
fifo_space_ack.eq(1),
tx_dp.send("fifo_space_reply", space=self.fifo_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,
self.read_overflow_ack.eq(1),
NextState("IDLE")
)
)
tx_fsm.act("READ",
tx_dp.send("read_reply",
timestamp=self.read_timestamp,
data=self.read_data),
clear_read_request.eq(1),
If(tx_dp.packet_last,
self.read_consume.eq(1),
NextState("IDLE")
)
)