mirror of https://github.com/m-labs/artiq.git
drtio: add repeater (WIP, write only)
This commit is contained in:
parent
6057cb797c
commit
078c862618
|
@ -1,2 +1,2 @@
|
|||
from artiq.gateware.drtio.core import SyncRTIO, DRTIOSatellite, DRTIOMaster
|
||||
from artiq.gateware.drtio.core import SyncRTIO, DRTIOSatellite, DRTIOMaster, DRTIORepeater
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ from artiq.gateware.drtio.rx_synchronizer import GenericRXSynchronizer
|
|||
|
||||
__all__ = ["ChannelInterface", "TransceiverInterface",
|
||||
"SyncRTIO",
|
||||
"DRTIOSatellite", "DRTIOMaster"]
|
||||
"DRTIOSatellite", "DRTIOMaster", "DRTIORepeater"]
|
||||
|
||||
|
||||
class ChannelInterface:
|
||||
|
@ -180,3 +180,25 @@ class DRTIOMaster(Module):
|
|||
@property
|
||||
def cri(self):
|
||||
return self.rt_controller.cri
|
||||
|
||||
|
||||
class DRTIORepeater(Module):
|
||||
def __init__(self, chanif):
|
||||
self.submodules.link_layer = link_layer.LinkLayer(
|
||||
chanif.encoder, chanif.decoders)
|
||||
self.comb += self.link_layer.rx_ready.eq(chanif.rx_ready)
|
||||
|
||||
self.submodules.link_stats = link_layer.LinkLayerStats(self.link_layer, "rtio_rx")
|
||||
self.submodules.rt_packet = rt_packet_repeater.RTPacketRepeater(self.link_layer)
|
||||
|
||||
self.submodules.aux_controller = aux_controller.AuxController(
|
||||
self.link_layer)
|
||||
|
||||
def get_csrs(self):
|
||||
return (self.link_layer.get_csrs() +
|
||||
self.link_stats.get_csrs() +
|
||||
self.aux_controller.get_csrs())
|
||||
|
||||
@property
|
||||
def cri(self):
|
||||
return self.rt_packet.cri
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
from migen import *
|
||||
from migen.genlib.fsm import *
|
||||
|
||||
from artiq.gateware.rtio import cri
|
||||
from artiq.gateware.drtio.rt_serializer import *
|
||||
|
||||
|
||||
class RTPacketRepeater(Module):
|
||||
def __init__(self, link_layer):
|
||||
self.cri = 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)
|
||||
tx_plm = get_m2s_layouts(ws)
|
||||
tx_dp = ClockDomainsRenamer("rtio")(TransmitDatapath(
|
||||
link_layer.tx_rt_frame, link_layer.tx_rt_data, tx_plm))
|
||||
self.submodules += tx_dp
|
||||
rx_plm = get_s2m_layouts(ws)
|
||||
rx_dp = ClockDomainsRenamer("rtio_rx")(ReceiveDatapath(
|
||||
link_layer.rx_rt_frame, link_layer.rx_rt_data, rx_plm))
|
||||
self.submodules += rx_dp
|
||||
|
||||
# Write buffer and extra data count
|
||||
wb_timestamp = Signal(64)
|
||||
wb_channel = Signal(16)
|
||||
wb_address = Signal(16)
|
||||
wb_data = Signal(512)
|
||||
self.sync.rtio += If(self.cri.cmd == cri.commands["write"],
|
||||
wb_timestamp.eq(self.cri.timestamp),
|
||||
wb_channel.eq(self.cri.chan_sel),
|
||||
wb_address.eq(self.cri.o_address),
|
||||
wb_data.eq(self.cri.o_data))
|
||||
|
||||
wb_extra_data_cnt = Signal(8)
|
||||
short_data_len = tx_plm.field_length("write", "short_data")
|
||||
wb_extra_data_a = Signal(512)
|
||||
self.comb += wb_extra_data_a.eq(self.cri.o_data[short_data_len:])
|
||||
for i in range(512//ws):
|
||||
self.sync.rtio += If(self.cri.cmd == cri.commands["write"],
|
||||
If(wb_extra_data_a[ws*i:ws*(i+1)] != 0, wb_extra_data_cnt.eq(i+1)))
|
||||
|
||||
wb_extra_data = Signal(512)
|
||||
self.sync.rtio += If(self.cri.cmd == cri.commands["write"],
|
||||
wb_extra_data.eq(wb_extra_data_a))
|
||||
|
||||
extra_data_ce = Signal()
|
||||
extra_data_last = Signal()
|
||||
extra_data_counter = Signal(max=512//ws+1)
|
||||
self.comb += [
|
||||
Case(extra_data_counter,
|
||||
{i+1: tx_dp.raw_data.eq(wb_extra_data[i*ws:(i+1)*ws])
|
||||
for i in range(512//ws)}),
|
||||
extra_data_last.eq(extra_data_counter == wb_extra_data_cnt)
|
||||
]
|
||||
self.sync.rtio += \
|
||||
If(extra_data_ce,
|
||||
extra_data_counter.eq(extra_data_counter + 1),
|
||||
).Else(
|
||||
extra_data_counter.eq(1)
|
||||
)
|
||||
|
||||
# TX FSM
|
||||
tx_fsm = ClockDomainsRenamer("rtio")(FSM(reset_state="IDLE"))
|
||||
self.submodules += tx_fsm
|
||||
|
||||
tx_fsm.act("IDLE",
|
||||
If(self.cri.cmd == cri.commands["write"], NextState("WRITE"))
|
||||
)
|
||||
tx_fsm.act("WRITE",
|
||||
tx_dp.send("write",
|
||||
timestamp=wb_timestamp,
|
||||
channel=wb_channel,
|
||||
address=wb_address,
|
||||
extra_data_cnt=wb_extra_data_cnt,
|
||||
short_data=wb_data[:short_data_len]),
|
||||
If(tx_dp.packet_last,
|
||||
If(wb_extra_data_cnt == 0,
|
||||
NextState("IDLE")
|
||||
).Else(
|
||||
NextState("WRITE_EXTRA")
|
||||
)
|
||||
)
|
||||
)
|
||||
tx_fsm.act("WRITE_EXTRA",
|
||||
tx_dp.raw_stb.eq(1),
|
||||
extra_data_ce.eq(1),
|
||||
If(extra_data_last,
|
||||
NextState("IDLE")
|
||||
)
|
||||
)
|
|
@ -0,0 +1,72 @@
|
|||
from migen import *
|
||||
|
||||
from artiq.gateware.drtio.rt_serializer import *
|
||||
|
||||
|
||||
class PacketInterface:
|
||||
def __init__(self, direction, ws):
|
||||
if direction == "m2s":
|
||||
self.plm = get_m2s_layouts(ws)
|
||||
elif direction == "s2m":
|
||||
self.plm = get_s2m_layouts(ws)
|
||||
else:
|
||||
raise ValueError
|
||||
self.frame = Signal()
|
||||
self.data = Signal(ws)
|
||||
|
||||
def send(self, ty, **kwargs):
|
||||
idx = 8
|
||||
value = self.plm.types[ty]
|
||||
for field_name, field_size in self.plm.layouts[ty][1:]:
|
||||
try:
|
||||
fvalue = kwargs[field_name]
|
||||
del kwargs[field_name]
|
||||
except KeyError:
|
||||
fvalue = 0
|
||||
value = value | (fvalue << idx)
|
||||
idx += field_size
|
||||
if kwargs:
|
||||
raise ValueError
|
||||
|
||||
ws = len(self.data)
|
||||
yield self.frame.eq(1)
|
||||
for i in range(idx//ws):
|
||||
yield self.data.eq(value)
|
||||
value >>= ws
|
||||
yield
|
||||
yield self.frame.eq(0)
|
||||
yield
|
||||
|
||||
@passive
|
||||
def receive(self, callback):
|
||||
previous_frame = 0
|
||||
frame_words = []
|
||||
while True:
|
||||
frame = yield self.frame
|
||||
if frame:
|
||||
frame_words.append((yield self.data))
|
||||
if previous_frame and not frame:
|
||||
packet_type = self.plm.type_names[frame_words[0] & 0xff]
|
||||
packet_nwords = layout_len(self.plm.layouts[packet_type]) \
|
||||
//len(self.data)
|
||||
packet, trailer = frame_words[:packet_nwords], \
|
||||
frame_words[packet_nwords:]
|
||||
|
||||
n = 0
|
||||
packet_int = 0
|
||||
for w in packet:
|
||||
packet_int |= (w << n)
|
||||
n += len(self.data)
|
||||
|
||||
field_dict = dict()
|
||||
idx = 0
|
||||
for field_name, field_size in self.plm.layouts[packet_type]:
|
||||
v = (packet_int >> idx) & (2**field_size - 1)
|
||||
field_dict[field_name] = v
|
||||
idx += field_size
|
||||
|
||||
callback(packet_type, field_dict, trailer)
|
||||
|
||||
frame_words = []
|
||||
previous_frame = frame
|
||||
yield
|
|
@ -0,0 +1,62 @@
|
|||
import unittest
|
||||
from types import SimpleNamespace
|
||||
|
||||
from migen import *
|
||||
|
||||
from artiq.gateware.rtio import cri
|
||||
from artiq.gateware.test.drtio.packet_interface import PacketInterface
|
||||
from artiq.gateware.drtio.rt_packet_repeater import RTPacketRepeater
|
||||
|
||||
|
||||
def create_dut(nwords):
|
||||
pt = PacketInterface("s2m", nwords*8)
|
||||
pr = PacketInterface("m2s", nwords*8)
|
||||
dut = ClockDomainsRenamer({"rtio": "sys", "rtio_rx": "sys"})(
|
||||
RTPacketRepeater(SimpleNamespace(
|
||||
rx_rt_frame=pt.frame, rx_rt_data=pt.data,
|
||||
tx_rt_frame=pr.frame, tx_rt_data=pr.data)))
|
||||
return pt, pr, dut
|
||||
|
||||
|
||||
class TestRepeater(unittest.TestCase):
|
||||
def test_output(self):
|
||||
test_writes = [
|
||||
(1, 10, 21, 0x42),
|
||||
(2, 11, 34, 0x2342),
|
||||
(3, 12, 83, 0x2345566633),
|
||||
(4, 13, 25, 0x98da14959a19498ae1),
|
||||
(5, 14, 75, 0x3998a1883ae14f828ae24958ea2479)
|
||||
]
|
||||
|
||||
for nwords in range(1, 8):
|
||||
pt, pr, dut = create_dut(nwords)
|
||||
|
||||
def send():
|
||||
for channel, timestamp, address, data in test_writes:
|
||||
yield dut.cri.chan_sel.eq(channel)
|
||||
yield dut.cri.timestamp.eq(timestamp)
|
||||
yield dut.cri.o_address.eq(address)
|
||||
yield dut.cri.o_data.eq(data)
|
||||
yield dut.cri.cmd.eq(cri.commands["write"])
|
||||
yield
|
||||
yield dut.cri.cmd.eq(cri.commands["nop"])
|
||||
yield
|
||||
for i in range(30):
|
||||
yield
|
||||
for i in range(50):
|
||||
yield
|
||||
|
||||
short_data_len = pr.plm.field_length("write", "short_data")
|
||||
|
||||
received = []
|
||||
def receive(packet_type, field_dict, trailer):
|
||||
self.assertEqual(packet_type, "write")
|
||||
self.assertEqual(len(trailer), field_dict["extra_data_cnt"])
|
||||
data = field_dict["short_data"]
|
||||
for n, te in enumerate(trailer):
|
||||
data |= te << (n*nwords*8 + short_data_len)
|
||||
received.append((field_dict["channel"], field_dict["timestamp"],
|
||||
field_dict["address"], data))
|
||||
|
||||
run_simulation(dut, [send(), pr.receive(receive)])
|
||||
self.assertEqual(test_writes, received)
|
|
@ -3,81 +3,11 @@ from types import SimpleNamespace
|
|||
|
||||
from migen import *
|
||||
|
||||
from artiq.gateware.drtio.rt_serializer import *
|
||||
from artiq.gateware.test.drtio.packet_interface import PacketInterface
|
||||
from artiq.gateware.drtio.rt_packet_satellite import RTPacketSatellite
|
||||
|
||||
|
||||
class PacketInterface:
|
||||
def __init__(self, direction, ws):
|
||||
if direction == "m2s":
|
||||
self.plm = get_m2s_layouts(ws)
|
||||
elif direction == "s2m":
|
||||
self.plm = get_s2m_layouts(ws)
|
||||
else:
|
||||
raise ValueError
|
||||
self.frame = Signal()
|
||||
self.data = Signal(ws)
|
||||
|
||||
def send(self, ty, **kwargs):
|
||||
idx = 8
|
||||
value = self.plm.types[ty]
|
||||
for field_name, field_size in self.plm.layouts[ty][1:]:
|
||||
try:
|
||||
fvalue = kwargs[field_name]
|
||||
del kwargs[field_name]
|
||||
except KeyError:
|
||||
fvalue = 0
|
||||
value = value | (fvalue << idx)
|
||||
idx += field_size
|
||||
if kwargs:
|
||||
raise ValueError
|
||||
|
||||
ws = len(self.data)
|
||||
yield self.frame.eq(1)
|
||||
for i in range(idx//ws):
|
||||
yield self.data.eq(value)
|
||||
value >>= ws
|
||||
yield
|
||||
yield self.frame.eq(0)
|
||||
yield
|
||||
|
||||
@passive
|
||||
def receive(self, callback):
|
||||
previous_frame = 0
|
||||
frame_words = []
|
||||
while True:
|
||||
frame = yield self.frame
|
||||
if frame:
|
||||
frame_words.append((yield self.data))
|
||||
if previous_frame and not frame:
|
||||
packet_type = self.plm.type_names[frame_words[0] & 0xff]
|
||||
packet_nwords = layout_len(self.plm.layouts[packet_type]) \
|
||||
//len(self.data)
|
||||
packet, trailer = frame_words[:packet_nwords], \
|
||||
frame_words[packet_nwords:]
|
||||
|
||||
n = 0
|
||||
packet_int = 0
|
||||
for w in packet:
|
||||
packet_int |= (w << n)
|
||||
n += len(self.data)
|
||||
|
||||
field_dict = dict()
|
||||
idx = 0
|
||||
for field_name, field_size in self.plm.layouts[packet_type]:
|
||||
v = (packet_int >> idx) & (2**field_size - 1)
|
||||
field_dict[field_name] = v
|
||||
idx += field_size
|
||||
|
||||
callback(packet_type, field_dict, trailer)
|
||||
|
||||
frame_words = []
|
||||
previous_frame = frame
|
||||
yield
|
||||
|
||||
|
||||
class TestSatellite(unittest.TestCase):
|
||||
def create_dut(self, nwords):
|
||||
def create_dut(nwords):
|
||||
pt = PacketInterface("m2s", nwords*8)
|
||||
pr = PacketInterface("s2m", nwords*8)
|
||||
dut = RTPacketSatellite(SimpleNamespace(
|
||||
|
@ -85,9 +15,11 @@ class TestSatellite(unittest.TestCase):
|
|||
tx_rt_frame=pr.frame, tx_rt_data=pr.data))
|
||||
return pt, pr, dut
|
||||
|
||||
|
||||
class TestSatellite(unittest.TestCase):
|
||||
def test_echo(self):
|
||||
for nwords in range(1, 8):
|
||||
pt, pr, dut = self.create_dut(nwords)
|
||||
pt, pr, dut = create_dut(nwords)
|
||||
completed = False
|
||||
def send():
|
||||
yield from pt.send("echo_request")
|
||||
|
@ -102,7 +34,7 @@ class TestSatellite(unittest.TestCase):
|
|||
|
||||
def test_set_time(self):
|
||||
for nwords in range(1, 8):
|
||||
pt, _, dut = self.create_dut(nwords)
|
||||
pt, _, dut = create_dut(nwords)
|
||||
tx_times = [0x12345678aabbccdd, 0x0102030405060708,
|
||||
0xaabbccddeeff1122]
|
||||
def send():
|
||||
|
|
Loading…
Reference in New Issue