From 1248a46a5458ca8ec2a02a3af222056c7910e651 Mon Sep 17 00:00:00 2001 From: occheung Date: Mon, 8 May 2023 12:43:02 +0800 Subject: [PATCH] minimal serdes communication loopback --- multi_serdes_channel.py | 242 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 242 insertions(+) create mode 100644 multi_serdes_channel.py diff --git a/multi_serdes_channel.py b/multi_serdes_channel.py new file mode 100644 index 0000000..3bdda41 --- /dev/null +++ b/multi_serdes_channel.py @@ -0,0 +1,242 @@ +from migen import * +from sync_serdes import MultiLineRX, MultiLineTX +from migen.genlib.fifo import SyncFIFO +from migen.build.platforms.sinara import kasli, efc +from multi_coders import MultiEncoder, CrossbarDecoder +from kasli_crg import TransceiverCRG +from eem_helpers import generate_pads +from uart import UART +from io_loopback import SingleIOLoopback, IOLoopBack + + +class MultiTransceiverChannel(Module): + def __init__(self, io_pads, 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), + ] + + # SERDES impl + self.submodules.tx = MultiLineTX() + self.submodules.rx = MultiLineRX() + + # 8b10b encoder & decoder + self.submodules.encoder = MultiEncoder(lsb_first=False) + decoders = [ CrossbarDecoder(lsb_first=False) for _ in range(2) ] + self.submodules += decoders + + # The actual channel + self.submodules.channel = IOLoopBack(io_pads) + + # FIFO to record transmission received + rx_records = SyncFIFO(16, 32) + self.submodules += rx_records + + # Attach FIFO to UART TX, send rate is too slow w.r.t sysclk + self.submodules.tx_fifo = SyncFIFO(8, 64) + + self.comb += [ + # Loopback channel + self.channel.i.eq(self.tx.ser_out), + self.channel.t.eq(self.tx.t_out), + self.rx.ser_in_no_dly.eq(self.channel.o), + + # Link decoders + decoders[0].raw_input.eq(self.rx.rxdata[:10]), + decoders[1].raw_input.eq(self.rx.rxdata[10:]), + # Default encoder linkage + self.tx.txdata.eq(Cat(self.encoder.output[0], self.encoder.output[1])), + + # UART 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), + ] + + rx_fsm = FSM(reset_state="WAIT_GROUP_ALIGN") + self.submodules += rx_fsm + + rx_fsm.act("WAIT_GROUP_ALIGN", + If(self.rx.align_done & rx_records.writable, + rx_records.din.eq(self.rx.rxdata), + rx_records.we.eq(1), + ), + If(self.rx.err, + NextState("WRITE_ERR_UPPER"), + ).Elif(self.rx.rxdata == 0b11111111111111111111, + decoders[0].start.eq(1), + decoders[1].start.eq(1), + NextState("RECORD_TRANSMISSION"), + ), + ) + + rx_fsm.act("WRITE_ERR_UPPER", + If(self.tx_fifo.writable, + self.tx_fifo.we.eq(1), + self.tx_fifo.din.eq(0b01010101), + NextState("WRITE_ERR_LOWER"), + ), + ) + + rx_fsm.act("WRITE_ERR_LOWER", + If(self.tx_fifo.writable, + self.tx_fifo.we.eq(1), + self.tx_fifo.din.eq(0b10101010), + NextState("TERMINATE"), + ), + ) + + rx_fsm.act("RECORD_TRANSMISSION", + If(rx_records.writable, + rx_records.din.eq(Cat(decoders[0].d, decoders[1].d)), + rx_records.we.eq(1), + ).Else( + NextState("DUMP_TRANSMISSION_UPPER"), + ) + ) + + rx_fsm.act("DUMP_TRANSMISSION_UPPER", + If(rx_records.readable, + If(self.tx_fifo.writable, + self.tx_fifo.we.eq(1), + self.tx_fifo.din.eq(rx_records.dout[8:]), + NextState("DUMP_TRANSMISSION_LOWER"), + ) + ).Else( + NextState("TERMINATE"), + ), + ) + + rx_fsm.act("DUMP_TRANSMISSION_LOWER", + If(self.tx_fifo.writable, + self.tx_fifo.we.eq(1), + self.tx_fifo.din.eq(rx_records.dout[:8]), + rx_records.re.eq(1), + NextState("DUMP_TRANSMISSION_UPPER"), + ) + ) + + rx_fsm.act("TERMINATE", + NextState("TERMINATE") + ) + + tx_fsm = FSM(reset_state="SEND_TRAINING") + self.submodules += tx_fsm + + tx_fsm.act("SEND_TRAINING", + self.tx.txdata.eq(0b00100001000010000100), + If(self.rx.align_done, + NextState("SEND_ZERO"), + ), + ) + + send_zero_duration = Signal(2) + + tx_fsm.act("SEND_ZERO", + self.tx.txdata.eq(0), + If(send_zero_duration == 0b11, + NextState("SEND_PULSE"), + ).Else( + NextValue(send_zero_duration, send_zero_duration + 1), + ), + ) + + tx_fsm.act("SEND_PULSE", + self.tx.txdata.eq(0b11111111111111111111), + self.encoder.start.eq(1), + NextState("WAIT_GROUP_ALIGN"), + ) + + tx_fsm.act("WAIT_GROUP_ALIGN", + If(self.rx.delay_done, + NextState("SEND_ARB_DATA1"), + ), + ) + + tx_fsm.act("SEND_ARB_DATA1", + self.encoder.d[0].eq(0xDE), + self.encoder.d[1].eq(0xAD), + self.encoder.k[0].eq(0), + self.encoder.k[1].eq(0), + NextState("SEND_ARB_DATA2"), + ) + + tx_fsm.act("SEND_ARB_DATA2", + self.encoder.d[0].eq(0xBE), + self.encoder.d[1].eq(0xEF), + self.encoder.k[0].eq(0), + self.encoder.k[1].eq(0), + NextState("SEND_ARB_DATA3"), + ) + + tx_fsm.act("SEND_ARB_DATA3", + self.encoder.d[0].eq(0xBA), + self.encoder.d[1].eq(0xD0), + self.encoder.k[0].eq(0), + self.encoder.k[1].eq(0), + NextState("SEND_ARB_DATA4"), + ) + + tx_fsm.act("SEND_ARB_DATA4", + self.encoder.d[0].eq(0xCA), + self.encoder.d[1].eq(0xFE), + self.encoder.k[0].eq(0), + self.encoder.k[1].eq(0), + NextState("TERMINATE"), + ) + + tx_fsm.act("TERMINATE", + self.encoder.d[0].eq(0xAD), + self.encoder.d[1].eq(0xDE), + self.encoder.k[0].eq(0), + self.encoder.k[1].eq(0), + NextState("TERMINATE"), + ) + + +if __name__ == "__main__": + import argparse + + parser = argparse.ArgumentParser() + parser.add_argument("platform") + args = parser.parse_args() + + platform_dict = { + "kasli": kasli.Platform(hw_rev="v2.0"), + "efc": efc.Platform(), + } + + sysclk_name = { + "kasli": "clk125_gtp", + "efc": "gtp_clk", + } + + platform = platform_dict[args.platform] + sysclk = platform.request(sysclk_name[args.platform]) + + # Generate pads for the I/O blocks + # Using EEM1 for both as both EFC and Kasli has EEM1 + # EEM1 are not interconnected + eem = 0 + generate_pads(platform, eem) + pads = [ + platform.request("dio{}".format(eem), i) for i in range(4) + ] + # pad = platform.request("dio{}".format(eem), 0) + + crg = TransceiverCRG(platform, sysclk) + top = MultiTransceiverChannel(pads, 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)