artiq/artiq/gateware/test/drtio/test_full_stack.py

313 lines
10 KiB
Python

import unittest
from types import SimpleNamespace
import random
from migen import *
from artiq.gateware.drtio import *
from artiq.gateware.drtio import rt_serializer
from artiq.gateware import rtio
from artiq.gateware.rtio import rtlink
from artiq.gateware.rtio.phy import ttl_simple
from artiq.coredevice.exceptions import *
class DummyTransceiverPair:
def __init__(self, nwords):
a2b_k = [Signal() for _ in range(nwords)]
a2b_d = [Signal(8) for _ in range(nwords)]
b2a_k = [Signal() for _ in range(nwords)]
b2a_d = [Signal(8) for _ in range(nwords)]
self.alice = SimpleNamespace(
encoder=SimpleNamespace(k=a2b_k, d=a2b_d),
decoders=[SimpleNamespace(k=k, d=d) for k, d in zip(b2a_k, b2a_d)],
rx_ready=1
)
self.bob = SimpleNamespace(
encoder=SimpleNamespace(k=b2a_k, d=b2a_d),
decoders=[SimpleNamespace(k=k, d=d) for k, d in zip(a2b_k, a2b_d)],
rx_ready=1
)
class DummyRXSynchronizer:
def resync(self, signal):
return signal
class SimpleIOPHY(Module):
def __init__(self, o_width, i_width):
self.rtlink = rtlink.Interface(
rtlink.OInterface(o_width),
rtlink.IInterface(i_width, timestamped=True))
self.received_data = Signal(o_width)
self.sync.rio_phy += If(self.rtlink.o.stb,
self.received_data.eq(self.rtlink.o.data))
class DUT(Module):
def __init__(self, nwords):
self.ttl0 = Signal()
self.ttl1 = Signal()
self.transceivers = DummyTransceiverPair(nwords)
self.submodules.tsc_master = rtio.TSC()
self.submodules.master = DRTIOMaster(self.tsc_master,
self.transceivers.alice)
self.submodules.master_ki = rtio.KernelInitiator(self.tsc_master,
self.master.cri)
rx_synchronizer = DummyRXSynchronizer()
self.submodules.phy0 = ttl_simple.Output(self.ttl0)
self.submodules.phy1 = ttl_simple.Output(self.ttl1)
self.submodules.phy2 = SimpleIOPHY(512, 32) # test wide output data
rtio_channels = [
rtio.Channel.from_phy(self.phy0),
rtio.Channel.from_phy(self.phy1),
rtio.Channel.from_phy(self.phy2),
]
self.submodules.tsc_satellite = rtio.TSC()
self.submodules.satellite = DRTIOSatellite(
self.tsc_satellite, self.transceivers.bob, rx_synchronizer)
self.satellite.reset.storage.reset = 0
self.satellite.reset.storage_full.reset = 0
self.satellite.reset_phy.storage.reset = 0
self.satellite.reset_phy.storage_full.reset = 0
self.submodules.satellite_rtio = SyncRTIO(
self.tsc_satellite, rtio_channels, lane_count=4, fifo_depth=8)
self.comb += [
self.satellite.cri.connect(self.satellite_rtio.cri),
self.satellite.async_errors.eq(self.satellite_rtio.async_errors),
]
class OutputsTestbench:
def __init__(self):
self.dut = DUT(2)
self.now = 0
def init(self):
yield from self.dut.master.rt_controller.csrs.underflow_margin.write(100)
while not (yield from self.dut.master.link_layer.rx_up.read()):
yield
yield from self.get_buffer_space()
def get_buffer_space(self):
csrs = self.dut.master.rt_controller.csrs
yield from csrs.o_get_buffer_space.write(1)
yield
while (yield from csrs.o_wait.read()):
yield
r = (yield from csrs.o_dbg_buffer_space.read())
return r
def delay(self, dt):
self.now += dt
def sync(self):
t = self.now + 15
while (yield self.dut.tsc_master.full_ts_cri) < t:
yield
def write(self, channel, data):
kcsrs = self.dut.master_ki
yield from kcsrs.target.write(channel << 8)
yield from kcsrs.now_hi.write(self.now >> 32)
yield from kcsrs.now_lo.write(self.now & 0xffffffff)
yield from kcsrs.o_data.write(data)
yield
status = 1
wlen = 0
while status:
status = yield from kcsrs.o_status.read()
if status & 0x2:
raise RTIOUnderflow
if status & 0x4:
raise RTIODestinationUnreachable
yield
wlen += 1
return wlen
@passive
def check_ttls(self, ttl_changes):
cycle = 0
old_ttls = [0, 0]
while True:
ttls = [(yield self.dut.ttl0), (yield self.dut.ttl1)]
for n, (old_ttl, ttl) in enumerate(zip(old_ttls, ttls)):
if ttl != old_ttl:
ttl_changes.append((cycle, n))
old_ttls = ttls
yield
cycle += 1
class TestFullStack(unittest.TestCase):
clocks = {"sys": 8, "rtio_rx": 8,
"rio": 8, "rio_phy": 8}
def test_pulses(self):
tb = OutputsTestbench()
ttl_changes = []
correct_ttl_changes = [
(208, 0),
(213, 0),
(213, 1),
(219, 1),
]
def test():
yield from tb.init()
tb.delay(200)
yield from tb.write(0, 1)
tb.delay(5)
yield from tb.write(0, 0)
yield from tb.write(1, 1)
tb.delay(6)
yield from tb.write(1, 0)
yield from tb.sync()
run_simulation(tb.dut,
{"sys": [test(), tb.check_ttls(ttl_changes)]}, self.clocks)
self.assertEqual(ttl_changes, correct_ttl_changes)
def test_underflow(self):
tb = OutputsTestbench()
def test():
yield from tb.init()
with self.assertRaises(RTIOUnderflow):
yield from tb.write(0, 0)
run_simulation(tb.dut, {"sys": test()}, self.clocks)
def test_large_data(self):
tb = OutputsTestbench()
def test():
yield from tb.init()
correct_large_data = random.Random(0).randrange(2**512-1)
self.assertNotEqual((yield tb.dut.phy2.received_data), correct_large_data)
tb.delay(200)
yield from tb.write(2, correct_large_data)
yield from tb.sync()
self.assertEqual((yield tb.dut.phy2.received_data), correct_large_data)
run_simulation(tb.dut, {"sys": test()}, self.clocks)
def test_buffer_space(self):
tb = OutputsTestbench()
ttl_changes = []
correct_ttl_changes = [(258 + 40*i, 0) for i in range(10)]
def test():
yield from tb.init()
tb.delay(250)
max_wlen = 0
for i in range(10):
wlen = yield from tb.write(0, (i + 1) % 2)
max_wlen = max(max_wlen, wlen)
tb.delay(40)
# check that some writes caused buffer space requests
self.assertGreater(max_wlen, 5)
yield from tb.sync()
run_simulation(tb.dut,
{"sys": [test(), tb.check_ttls(ttl_changes)]}, self.clocks)
self.assertEqual(ttl_changes, correct_ttl_changes)
def test_write_underflow(self):
tb = OutputsTestbench()
def test():
saterr = tb.dut.satellite.rt_errors
csrs = tb.dut.master.rt_controller.csrs
yield from tb.init()
errors = yield from saterr.protocol_error.read()
self.assertEqual(errors, 0)
yield from csrs.underflow_margin.write(0)
tb.delay(80)
yield from tb.write(42, 1)
for i in range(21):
yield
errors = yield from saterr.protocol_error.read()
underflow_channel = yield from saterr.underflow_channel.read()
underflow_timestamp_event = yield from saterr.underflow_timestamp_event.read()
self.assertEqual(errors, 8) # write underflow
self.assertEqual(underflow_channel, 42)
self.assertEqual(underflow_timestamp_event, 80)
yield from saterr.protocol_error.write(errors)
yield
errors = yield from saterr.protocol_error.read()
self.assertEqual(errors, 0)
run_simulation(tb.dut, {"sys": test()}, self.clocks)
def test_inputs(self):
dut = DUT(2)
kcsrs = dut.master_ki
def get_input(timeout):
yield from kcsrs.target.write(2 << 8)
yield from kcsrs.i_timeout.write(10)
yield
status = yield from kcsrs.i_status.read()
while status & 0x4:
yield
status = yield from kcsrs.i_status.read()
if status & 0x1:
return "timeout"
if status & 0x2:
return "overflow"
if status & 0x8:
return "destination unreachable"
return ((yield from kcsrs.i_data.read()),
(yield from kcsrs.i_timestamp.read()))
def test():
while not (yield from dut.master.link_layer.rx_up.read()):
yield
i1 = yield from get_input(10)
i2 = yield from get_input(20)
self.assertEqual(i1, (0x600d1dea, 6))
self.assertEqual(i2, "timeout")
def generate_input():
for i in range(5):
yield
yield dut.phy2.rtlink.i.data.eq(0x600d1dea)
yield dut.phy2.rtlink.i.stb.eq(1)
yield
yield dut.phy2.rtlink.i.data.eq(0)
yield dut.phy2.rtlink.i.stb.eq(0)
run_simulation(dut,
{"sys": [test(), generate_input()]}, self.clocks)
def test_echo(self):
dut = DUT(2)
packet = dut.master.rt_packet
def test():
while not (yield from dut.master.link_layer.rx_up.read()):
yield
self.assertEqual((yield dut.master.rt_packet.packet_cnt_tx), 0)
self.assertEqual((yield dut.master.rt_packet.packet_cnt_rx), 0)
yield dut.master.rt_packet.echo_stb.eq(1)
yield
while not (yield dut.master.rt_packet.echo_ack):
yield
yield dut.master.rt_packet.echo_stb.eq(0)
for i in range(17):
yield
self.assertEqual((yield dut.master.rt_packet.packet_cnt_tx), 1)
self.assertEqual((yield dut.master.rt_packet.packet_cnt_rx), 1)
run_simulation(dut, test(), self.clocks)