diff --git a/multi_serdes_loopback.py b/multi_serdes_loopback.py new file mode 100644 index 0000000..c82c080 --- /dev/null +++ b/multi_serdes_loopback.py @@ -0,0 +1,104 @@ +from migen import * +from sync_serdes import SyncSingleRX, SingleLineTX +from migen.genlib.fifo import SyncFIFO +from migen.build.platforms.sinara import kasli +from kasli_crg import KasliCRG +from eem_helpers import generate_pads +from uart import UART +from io_loopback import SingleIOLoopback + + +class MultiSerDesLoopBack(Module): + def __init__(self, io_pad, sys_clk_freq): + self.uart_rx = Signal() + self.uart_tx = Signal() + + self.submodules.uart = UART(round((115200/sys_clk_freq)*2**32)) + self.comb += [ + self.uart.phy_rx.eq(self.uart_rx), + self.uart_tx.eq(self.uart.phy_tx), + ] + + self.submodules.tx = SingleLineTX() + self.submodules.sync_rx = SyncSingleRX() + + # The actual channel + self.submodules.channel = SingleIOLoopback(io_pad) + + # Attach FIFO to UART TX, send rate is too slow w.r.t sysclk + self.submodules.tx_fifo = SyncFIFO(8, 64) + + self.comb += [ + # Repetitively send 0b00100 + self.tx.txdata.eq(0b00100), + + # Loopback channel + self.channel.i.eq(self.tx.ser_out), + self.sync_rx.ser_in_no_dly.eq(self.channel.o), + self.channel.t.eq(self.tx.t_out), + + # TX path + self.uart.tx_data.eq(self.tx_fifo.dout), + self.uart.tx_stb.eq(self.tx_fifo.readable), + self.tx_fifo.re.eq(self.uart.tx_ack), + ] + + self.submodules.fsm = FSM(reset_state="WAIT_SELF_ALIGN") + + self.fsm.act("WAIT_SELF_ALIGN", + If(self.sync_rx.align_done, + NextState("SAMPLE_RXDATA"), + ), + ) + + sampled_rxdata = Signal(5) + + self.fsm.act("SAMPLE_RXDATA", + NextValue(sampled_rxdata, self.sync_rx.rxdata), + NextState("WRITE_PATTERN_UPPER"), + ) + + self.fsm.act("WRITE_PATTERN_UPPER", + If(self.tx_fifo.writable, + self.tx_fifo.we.eq(1), + self.tx_fifo.din.eq(sampled_rxdata), + NextState("WRITE_PATTERN_LOWER"), + ), + ) + + self.fsm.act("WRITE_PATTERN_LOWER", + If(self.tx_fifo.writable, + self.tx_fifo.we.eq(1), + self.tx_fifo.din.eq(sampled_rxdata), + NextState("TERMINATE"), + ), + ) + + self.fsm.act("TERMINATE", + NextState("TERMINATE"), + ) + + +if __name__ == "__main__": + platform = kasli.Platform(hw_rev="v2.0") + + # Generate pads for the I/O blocks + eem = 3 + generate_pads(platform, eem) + # pads = [ + # platform.request("dio{}".format(eem), i) for i in range(4) + # ] + pad = platform.request("dio{}".format(eem), 0) + + crg = KasliCRG(platform) + top = MultiSerDesLoopBack(pad, crg.sys_clk_freq) + + # Wire up UART core to the pads + uart_pads = platform.request("serial") + top.comb += [ + top.uart_rx.eq(uart_pads.rx), + uart_pads.tx.eq(top.uart_tx), + ] + + top.submodules += crg + platform.build(top) diff --git a/sync_serdes.py b/sync_serdes.py index fd87ead..1b88233 100644 --- a/sync_serdes.py +++ b/sync_serdes.py @@ -36,7 +36,6 @@ class SingleLineRX(Module): self.ce = Signal() self.cnt_in = Signal(5) self.cnt_out = Signal(5) - self.opt_delay = Signal(5) self.master_bitslip = Signal() self.slave_bitslip = Signal() @@ -560,3 +559,84 @@ class DelayOptimizer(Module): self.select_odd.eq(self.expected_pulse[0]), NextState("TERMINATE"), ) + + +class SyncSingleRX(Module): + def __init__(self): + # Ports + # IN: Undelayed serial signal + self.ser_in_no_dly = Signal() + # OUT: Received data after self-alignment, decimation + self.rxdata = Signal(5) + # OUT: RXDATA from this channel is self-aligned + self.align_done = Signal() + + # Components + self.submodules.rx = SingleLineRX() + self.submodules.slave_aligner = SlaveAligner() + self.submodules.delay_solver = DelayOptimizer() + + # Sample decimation + select_odd = Signal() + decimated_rxdata = Signal(5) + + # Dataflow + self.comb += [ + # Delay and oversample the original signal + self.rx.ser_in_no_dly.eq(self.ser_in_no_dly), + + # Use the deserialized signals for alignment + self.slave_aligner.loopback_rxdata.eq(self.rx.rxdata), + self.delay_solver.loopback_rxdata.eq(self.rx.rxdata), + + # Decimate the oversampled signals + If(select_odd, + decimated_rxdata.eq(self.rx.rxdata[1::2]), + ).Else( + decimated_rxdata.eq(self.rx.rxdata[::2]), + ), + + # Send it to the output + self.rxdata.eq(decimated_rxdata), + ] + + # Control signals + self.comb += [ + # Bitslip alignment + self.rx.master_bitslip.eq(self.slave_aligner.master_bitslip), + self.rx.slave_bitslip.eq(self.slave_aligner.slave_bitslip), + + # Tap delay optimization + self.rx.ce.eq(self.delay_solver.inc_en), + self.rx.ld.eq(self.delay_solver.ld), + self.rx.cnt_in.eq(self.delay_solver.opt_delay_tap), + self.delay_solver.delay_tap.eq(self.rx.cnt_out), + ] + + self.submodules.fsm = FSM(reset_state="WAIT_SIGNAL") + + self.fsm.act("WAIT_SIGNAL", + If(self.rx.rxdata != 0, + NextState("WAIT_ALIGNER") + ), + ) + + self.fsm.act("WAIT_ALIGNER", + self.slave_aligner.start.eq(1), + If(self.slave_aligner.done, + NextState("WAIT_DELAY_OPT"), + ), + ) + + self.fsm.act("WAIT_DELAY_OPT", + self.delay_solver.start.eq(1), + If(self.delay_solver.done, + NextValue(select_odd, self.delay_solver.select_odd), + NextState("INTRA_ALIGN_DONE"), + ), + ) + + self.fsm.act("INTRA_ALIGN_DONE", + self.align_done.eq(1), + NextState("INTRA_ALIGN_DONE"), + )