diff --git a/artiq/gateware/serwb/phy.py b/artiq/gateware/serwb/phy.py index 09b17763f..49beb9240 100644 --- a/artiq/gateware/serwb/phy.py +++ b/artiq/gateware/serwb/phy.py @@ -17,7 +17,7 @@ from artiq.gateware.serwb.s7phy import S7Serdes # 6) Link is ready. class _SerdesMasterInit(Module): - def __init__(self, serdes, taps): + def __init__(self, serdes, taps, timeout=1024): self.reset = Signal() self.ready = Signal() self.error = Signal() @@ -31,7 +31,7 @@ class _SerdesMasterInit(Module): self.delay_max_found = delay_max_found = Signal() self.bitslip = bitslip = Signal(max=40) - timer = WaitTimer(1024) + timer = WaitTimer(timeout) self.submodules += timer self.submodules.fsm = fsm = ResetInserter()(FSM(reset_state="IDLE")) @@ -157,7 +157,7 @@ class _SerdesMasterInit(Module): class _SerdesSlaveInit(Module, AutoCSR): - def __init__(self, serdes, taps): + def __init__(self, serdes, taps, timeout=1024): self.reset = Signal() self.ready = Signal() self.error = Signal() @@ -171,7 +171,7 @@ class _SerdesSlaveInit(Module, AutoCSR): self.delay_max_found = delay_max_found = Signal() self.bitslip = bitslip = Signal(max=40) - timer = WaitTimer(1024) + timer = WaitTimer(timeout) self.submodules += timer self.comb += self.reset.eq(serdes.rx_idle) diff --git a/artiq/gateware/test/serwb/test_serwb_phy_init.py b/artiq/gateware/test/serwb/test_serwb_phy_init.py new file mode 100644 index 000000000..ea807f97d --- /dev/null +++ b/artiq/gateware/test/serwb/test_serwb_phy_init.py @@ -0,0 +1,164 @@ +#!/usr/bin/env python3 +import unittest + +from migen import * + +from artiq.gateware.serwb import packet +from artiq.gateware.serwb import etherbone +from artiq.gateware.serwb.phy import _SerdesMasterInit, _SerdesSlaveInit + + +class SerdesModel(Module): + def __init__(self, taps, mode="slave"): + self.tx_idle = Signal() + self.tx_comma = Signal() + self.rx_idle = Signal() + self.rx_comma = Signal() + + self.rx_bitslip_value = Signal(6) + self.rx_delay_rst = Signal() + self.rx_delay_inc = Signal() + self.rx_delay_ce = Signal() + + self.valid_bitslip = Signal(6) + self.valid_delays = Signal(taps) + + # # # + + delay = Signal(max=taps) + bitslip = Signal(6) + + valid_delays = Array(Signal() for i in range(taps)) + for i in range(taps): + self.comb += valid_delays[taps-1-i].eq(self.valid_delays[i]) + + self.sync += [ + bitslip.eq(self.rx_bitslip_value), + If(self.rx_delay_rst, + delay.eq(0) + ).Elif(self.rx_delay_inc & self.rx_delay_ce, + delay.eq(delay + 1) + ) + ] + + if mode == "master": + self.submodules.fsm = fsm = ResetInserter()(FSM(reset_state="IDLE")) + self.comb += self.fsm.reset.eq(self.tx_idle) + fsm.act("IDLE", + If(self.tx_comma, + NextState("SEND_COMMA") + ), + self.rx_idle.eq(1) + ) + fsm.act("SEND_COMMA", + If(valid_delays[delay] & + (bitslip == self.valid_bitslip), + self.rx_comma.eq(1) + ), + If(~self.tx_comma, + NextState("READY") + ) + ) + fsm.act("READY") + elif mode == "slave": + self.submodules.fsm = fsm = FSM(reset_state="IDLE") + fsm.act("IDLE", + self.rx_idle.eq(1), + NextState("SEND_COMMA") + ) + fsm.act("SEND_COMMA", + If(valid_delays[delay] & + (bitslip == self.valid_bitslip), + self.rx_comma.eq(1) + ), + If(~self.tx_idle, + NextState("READY") + ) + ) + fsm.act("READY") + + +class DUTMaster(Module): + def __init__(self, taps=32): + self.submodules.serdes = SerdesModel(taps, mode="master") + self.submodules.init = _SerdesMasterInit(self.serdes, taps, timeout=1) + + +class DUTSlave(Module): + def __init__(self, taps=32): + self.submodules.serdes = SerdesModel(taps, mode="slave") + self.submodules.init = _SerdesSlaveInit(self.serdes, taps, timeout=1) + + +def generator(test, dut, valid_bitslip, valid_delays, check_success): + yield dut.serdes.valid_bitslip.eq(valid_bitslip) + yield dut.serdes.valid_delays.eq(valid_delays) + while not ((yield dut.init.ready) or + (yield dut.init.error)): + yield + if check_success: + ready = (yield dut.init.ready) + error = (yield dut.init.error) + delay_min = (yield dut.init.delay_min) + delay_max = (yield dut.init.delay_max) + delay = (yield dut.init.delay) + bitslip = (yield dut.init.bitslip) + test.assertEqual(ready, 1) + test.assertEqual(error, 0) + test.assertEqual(delay_min, 4) + test.assertEqual(delay_max, 9) + test.assertEqual(delay, 6) + test.assertEqual(bitslip, valid_bitslip) + else: + ready = (yield dut.init.ready) + error = (yield dut.init.error) + test.assertEqual(ready, 0) + test.assertEqual(error, 1) + + +class TestPHYInit(unittest.TestCase): + def test_master_init_success(self): + dut = DUTMaster() + valid_bitslip = 2 + valid_delays = 0b10001111100000111110000011111000 + run_simulation(dut, generator(self, dut, valid_bitslip, valid_delays, True)) + + def test_master_init_failure(self): + # partial window at the beginning + dut = DUTMaster() + valid_bitslip = 2 + valid_delays = 0b11000000000000000000000000000000 + run_simulation(dut, generator(self, dut, valid_bitslip, valid_delays, False)) + # partial window at the end + dut = DUTMaster() + valid_bitslip = 2 + valid_delays = 0b00000000000000000000000000000011 + run_simulation(dut, generator(self, dut, valid_bitslip, valid_delays, False)) + # too small window + dut = DUTMaster() + valid_bitslip = 2 + valid_delays = 0b00000000000000010000000000000000 + run_simulation(dut, generator(self, dut, valid_bitslip, valid_delays, False)) + + def test_slave_init_success(self): + dut = DUTSlave() + valid_bitslip = 2 + valid_delays = 0b10001111100000111110000011111000 + run_simulation(dut, generator(self, dut, valid_bitslip, valid_delays, True)) + + def test_slave_init_failure(self): + # partial window at the beginning + dut = DUTSlave() + valid_bitslip = 2 + valid_delays = 0b11000000000000000000000000000000 + run_simulation(dut, generator(self, dut, valid_bitslip, valid_delays, False)) + # partial window at the end + dut = DUTSlave() + valid_bitslip = 2 + valid_delays = 0b00000000000000000000000000000011 + run_simulation(dut, generator(self, dut, valid_bitslip, valid_delays, False)) + # too small window + dut = DUTSlave() + valid_bitslip = 2 + valid_delays = 0b00000000000000010000000000000000 + run_simulation(dut, generator(self, dut, valid_bitslip, valid_delays, False))