diff --git a/.gitignore b/.gitignore index ff57468..f83811e 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,6 @@ __pycache__ test *.vcd *.cfg -*.txt \ No newline at end of file +*.txt +# Bitstream build directories have _build as their suffixes +*_build \ No newline at end of file diff --git a/bidirectionalIO.py b/bidirectionalIO.py new file mode 100644 index 0000000..6f93e72 --- /dev/null +++ b/bidirectionalIO.py @@ -0,0 +1,25 @@ +from migen import * + + +class BiDirectionalIO(Module): + def __init__(self, i_pads, o_pads): + self.i = Signal(4) + self.o = Signal(4) + self.t = Signal(4) + + for i in range(4): + self.specials += Instance("OBUFTDS", + i_I=self.i[i], + o_O=o_pads[i].p, + o_OB=o_pads[i].n, + # Always chain the 3-states input to serializer + # Vivado will complain otherwise + i_T=self.t[i], + ) + + for i in range(4): + self.specials += Instance("IBUFDS", + i_I=i_pads[i].p, + i_IB=i_pads[i].n, + o_O=self.o[i], + ) diff --git a/flash.py b/flash.py new file mode 100644 index 0000000..64b772c --- /dev/null +++ b/flash.py @@ -0,0 +1,60 @@ +import argparse +import tempfile +import subprocess + + +def get_load_cmd(platform, variant): + return \ +'''target create xc7.spi0.proxy testee -chain-position xc7.tap +flash bank spi0 jtagspi 0 0 0 0 xc7.spi0.proxy 0x2 +gdb_port disabled +tcl_port disabled +telnet_port disabled +set error_msg "Trying to use configured scan chain anyway" +if {[string first $error_msg [capture "init"]] != -1} { +puts "Found error and exiting" +exit} +xadc_report xc7.tap +pld load 0 {bscan_spi_xc7a100t.bit} +flash probe spi0 +flash erase_sector spi0 0 42 +flash write_bank spi0 {''' + "{}".format(get_bitstream_path(platform, variant)) + '''} 0x0 +flash verify_bank spi0 {''' + "{}".format(get_bitstream_path(platform, variant)) + '''} 0x0 +xc7_program xc7.tap +exit''' + +def get_build_dir(platform, variant): + return "{}_{}_build".format(platform, variant) + +def get_bitstream_path(platform, variant): + return get_build_dir(platform, variant)+"/top.bit" + +def run(platform, script): + # Dump script to a temp file + with tempfile.NamedTemporaryFile(mode="w", delete=False) as f: + f.write(script) + + board_path = { + "kasli": "board/kasli.cfg", + "efc": "efc.cfg", + } + subprocess.run([ + "openocd", "-f", board_path[platform], "-f", f.name + ]) + +supported_platform = [ "kasli", "efc" ] +supported_variants = [ "master", "satellite" ] + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("platform") + parser.add_argument("variant") + args = parser.parse_args() + + if args.platform not in supported_platform: + raise ValueError("Unsupported platform") + if args.variant not in supported_variants: + raise ValueError("Unsupported variant") + + script = get_load_cmd(args.platform, args.variant) + run(args.platform, script) diff --git a/kasli_crg.py b/kasli_crg.py index a76ae4c..e4f1166 100644 --- a/kasli_crg.py +++ b/kasli_crg.py @@ -4,7 +4,7 @@ from migen.genlib.resetsync import AsyncResetSynchronizer class TransceiverCRG(Module): - def __init__(self, platform, clk125): + def __init__(self, platform, clk125, gte=True): self.platform = platform # Generated clock domains @@ -19,11 +19,35 @@ class TransceiverCRG(Module): clk125_buf = Signal() clk125_div2 = Signal() - self.specials += Instance("IBUFDS_GTE2", - i_CEB=0, - i_I=clk125.p, i_IB=clk125.n, - o_O=clk125_buf, - o_ODIV2=clk125_div2) + if gte: + self.specials += Instance("IBUFDS_GTE2", + i_CEB=0, + i_I=clk125.p, i_IB=clk125.n, + o_O=clk125_buf, + o_ODIV2=clk125_div2) + else: + self.specials += Instance("IBUFDS", + i_I=clk125.p, + i_IB=clk125.n, + o_O=clk125_buf) + + div2_pll_fb = Signal() + inner_pll_locked = Signal() + + self.specials += Instance("PLLE2_BASE", + p_CLKIN1_PERIOD=8.0, + i_CLKIN1=clk125_buf, + + i_CLKFBIN=div2_pll_fb, + o_CLKFBOUT=div2_pll_fb, + o_LOCKED=inner_pll_locked, + + # VCO @ 1GHz + p_CLKFBOUT_MULT=8, p_DIVCLK_DIVIDE=1, + + # 200MHz for IDELAYCTRL + p_CLKOUT0_DIVIDE=16, p_CLKOUT0_PHASE=0.0, o_CLKOUT0=clk125_div2, + ), # MMCM to generate different frequencies mmcm_fb = Signal() diff --git a/minimal_loopback.py b/minimal_loopback.py new file mode 100644 index 0000000..8c00123 --- /dev/null +++ b/minimal_loopback.py @@ -0,0 +1,180 @@ +from migen import * +from sync_serdes import * +from migen.genlib.fifo import SyncFIFO +from migen.build.platforms.sinara import kasli +from migen.genlib.misc import WaitTimer +from kasli_crg import TransceiverCRG +from eem_helpers import generate_pads +from uart import UART +from io_loopback import SingleIOLoopback + + +class SingleSerDesLoopBack(Module): + def __init__(self, io_pad, sys_clk_freq, debug): + 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.rx = SingleLineRX() + + # Debugging reader + self.submodules.bitslip_reader = BitSlipReader() + self.submodules.postslip_reader = BitSlipReader() + + # Alignment modules + self.submodules.slave_aligner = SlaveAligner() + + # 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.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), + ] + + # Debugging logics + self.comb += [ + self.bitslip_reader.loopback_rxdata.eq(self.rx.rxdata), + self.postslip_reader.loopback_rxdata.eq(self.rx.rxdata), + self.slave_aligner.loopback_rxdata.eq(self.rx.rxdata), + + self.rx.master_bitslip.eq( + self.bitslip_reader.bitslip + | self.slave_aligner.master_bitslip + | self.postslip_reader.bitslip + ), + self.rx.slave_bitslip.eq( + self.bitslip_reader.bitslip + | self.slave_aligner.master_bitslip + | self.postslip_reader.bitslip + ), + ] + + bitslip_count = Signal(3) + + fsm = FSM(reset_state="WAIT_DONE") + self.submodules += fsm + + fsm.act("WAIT_DONE", + self.bitslip_reader.start.eq(1), + If(self.bitslip_reader.done, + NextValue(bitslip_count, 0), + NextState("WRITE_UPPER"), + ), + ) + + fsm.act("WRITE_UPPER", + If(bitslip_count == 5, + NextState("WAIT_BITSLIP_ALIGNED"), + ).Elif(self.tx_fifo.writable, + self.tx_fifo.we.eq(1), + self.tx_fifo.din.eq(self.bitslip_reader.data_result[bitslip_count][8:]), + NextState("WRITE_LOWER"), + ), + ) + + fsm.act("WRITE_LOWER", + If(self.tx_fifo.writable, + self.tx_fifo.we.eq(1), + self.tx_fifo.din.eq(self.bitslip_reader.data_result[bitslip_count][:8]), + NextValue(bitslip_count, bitslip_count + 1), + NextState("WRITE_UPPER"), + ) + ) + + fsm.act("WAIT_BITSLIP_ALIGNED", + self.slave_aligner.start.eq(1), + If(self.slave_aligner.done, + NextState("BARRIER_UPPER"), + ), + ) + + fsm.act("BARRIER_UPPER", + If(self.tx_fifo.writable, + self.tx_fifo.we.eq(1), + self.tx_fifo.din.eq(0xFF), + NextState("BARRIER_LOWER"), + ), + ) + + fsm.act("BARRIER_LOWER", + If(self.tx_fifo.writable, + self.tx_fifo.we.eq(1), + self.tx_fifo.din.eq(0xFF), + NextState("REWAIT_BITSLIP_READ_DONE"), + ), + ) + + fsm.act("REWAIT_BITSLIP_READ_DONE", + self.postslip_reader.start.eq(1), + If(self.postslip_reader.done, + NextValue(bitslip_count, 0), + NextState("REWRITE_UPPER"), + ), + ) + + fsm.act("REWRITE_UPPER", + If(bitslip_count == 5, + NextState("TERMINATE"), + ).Elif(self.tx_fifo.writable, + self.tx_fifo.we.eq(1), + self.tx_fifo.din.eq(self.postslip_reader.data_result[bitslip_count][8:]), + NextState("REWRITE_LOWER"), + ), + ) + + fsm.act("REWRITE_LOWER", + If(self.tx_fifo.writable, + self.tx_fifo.we.eq(1), + self.tx_fifo.din.eq(self.postslip_reader.data_result[bitslip_count][:8]), + NextValue(bitslip_count, bitslip_count + 1), + NextState("REWRITE_UPPER"), + ) + ) + + fsm.act("TERMINATE", + NextState("TERMINATE"), + ) + + self.comb += self.tx.txdata.eq(0b00100) + + +if __name__ == "__main__": + platform = kasli.Platform(hw_rev="v2.0") + + # Generate pads for the I/O blocks + eem = 3 + generate_pads(platform, eem) + pad = platform.request("dio{}".format(eem), 0) + + crg = TransceiverCRG(platform, platform.request("clk125_gtp")) + top = SingleSerDesLoopBack(pad, crg.sys_clk_freq, True) + + # 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/multi_serdes_channel.py b/multi_serdes_channel.py index c0f84ea..2a795e2 100644 --- a/multi_serdes_channel.py +++ b/multi_serdes_channel.py @@ -33,7 +33,7 @@ class MultiTransceiverChannel(Module): self.submodules.channel = IOLoopBack(io_pads) # FIFO to record transmission received - rx_records = SyncFIFO(16, 32) + rx_records = SyncFIFO(16, 128) self.submodules += rx_records # Attach FIFO to UART TX, send rate is too slow w.r.t sysclk @@ -154,52 +154,66 @@ class MultiTransceiverChannel(Module): NextState("WAIT_GROUP_ALIGN"), ) + data = [ Signal(8) for _ in range(2) ] + tx_fsm.act("WAIT_GROUP_ALIGN", If(self.rx.delay_done, - NextState("SEND_ARB_DATA1"), + NextValue(data[0], 0x80), + NextValue(data[1], 0x7F), + NextState("TERMINATE"), ), ) - 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.d[0].eq(data[0]), + self.encoder.d[1].eq(data[1]), self.encoder.k[0].eq(0), self.encoder.k[1].eq(0), + NextValue(data[0], data[0] + 1), + NextValue(data[1], data[1] - 1), NextState("TERMINATE"), ) + # 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 @@ -224,7 +238,7 @@ if __name__ == "__main__": # Generate pads for the I/O blocks # Using EEM1 for both as both EFC and Kasli has EEM1 # EEM1 are not interconnected - eem = 0 + eem = 4 generate_pads(platform, eem) pads = [ platform.request("dio{}".format(eem), i) for i in range(4) diff --git a/pulse_read_rx.py b/pulse_read_rx.py new file mode 100644 index 0000000..161e029 --- /dev/null +++ b/pulse_read_rx.py @@ -0,0 +1,255 @@ +from migen import * +from migen.genlib.fifo import SyncFIFO +from migen.genlib.misc import WaitTimer +from migen.build.platforms.sinara import kasli, efc +from eem_helpers import generate_pads +from serdes_comm import BiDirectionalIO +from sync_serdes import SingleLineRX, BitSlipReader, SlaveAligner +from uart_log import UARTLogger +from kasli_crg import TransceiverCRG + + +class SingleLineReader(Module): + def __init__(self, uart_log, i_pads, o_pads): + # UART Logger + self.submodules.uart_logger = uart_log + + # Recorder + self.submodules.rx_buffer = SyncFIFO(10, 64) + + # PHY Channel + self.submodules.channel = BiDirectionalIO(i_pads, o_pads) + + # Single line RX SERDES + self.submodules.rx = SingleLineRX() + + # bitslip reader + self.submodules.bitslip_reader = BitSlipReader() + self.submodules.postslip_reader = BitSlipReader() + + # Alignment modules + self.submodules.slave_aligner = SlaveAligner() + + # Connect SERDES to PHY + self.comb += [ + self.rx.ser_in_no_dly.eq(self.channel.o[0]), + ] + + # Debugging logics + self.comb += [ + self.bitslip_reader.loopback_rxdata.eq(self.rx.rxdata), + self.postslip_reader.loopback_rxdata.eq(self.rx.rxdata), + self.slave_aligner.loopback_rxdata.eq(self.rx.rxdata), + + self.rx.master_bitslip.eq( + self.bitslip_reader.bitslip + | self.slave_aligner.master_bitslip + | self.postslip_reader.bitslip + ), + self.rx.slave_bitslip.eq( + self.bitslip_reader.bitslip + | self.slave_aligner.master_bitslip + | self.postslip_reader.bitslip + ), + ] + + bitslip_count = Signal(3) + + fsm = FSM(reset_state="WAIT_DONE") + self.submodules += fsm + + fsm.act("WAIT_DONE", + self.bitslip_reader.start.eq(1), + If(self.bitslip_reader.done, + NextValue(bitslip_count, 0), + NextState("WRITE_UPPER"), + ), + ) + + fsm.act("WRITE_UPPER", + If(bitslip_count == 5, + NextState("WAIT_BITSLIP_ALIGNED"), + ).Elif(self.uart_logger.tx_fifo.writable, + self.uart_logger.tx_fifo.we.eq(1), + self.uart_logger.tx_fifo.din.eq(self.bitslip_reader.data_result[bitslip_count][8:]), + NextState("WRITE_LOWER"), + ), + ) + + fsm.act("WRITE_LOWER", + If(self.uart_logger.tx_fifo.writable, + self.uart_logger.tx_fifo.we.eq(1), + self.uart_logger.tx_fifo.din.eq(self.bitslip_reader.data_result[bitslip_count][:8]), + NextValue(bitslip_count, bitslip_count + 1), + NextState("WRITE_UPPER"), + ) + ) + + fsm.act("WAIT_BITSLIP_ALIGNED", + self.slave_aligner.start.eq(1), + If(self.slave_aligner.done, + NextState("BARRIER_UPPER"), + ), + ) + + fsm.act("BARRIER_UPPER", + If(self.uart_logger.tx_fifo.writable, + self.uart_logger.tx_fifo.we.eq(1), + self.uart_logger.tx_fifo.din.eq(0xFF), + NextState("BARRIER_LOWER"), + ), + ) + + fsm.act("BARRIER_LOWER", + If(self.uart_logger.tx_fifo.writable, + self.uart_logger.tx_fifo.we.eq(1), + self.uart_logger.tx_fifo.din.eq(0xFF), + NextState("REWAIT_BITSLIP_READ_DONE"), + ), + ) + + fsm.act("REWAIT_BITSLIP_READ_DONE", + self.postslip_reader.start.eq(1), + If(self.postslip_reader.done, + NextValue(bitslip_count, 0), + NextState("REWRITE_UPPER"), + ), + ) + + fsm.act("REWRITE_UPPER", + If(bitslip_count == 5, + NextState("TERMINATE"), + ).Elif(self.uart_logger.tx_fifo.writable, + self.uart_logger.tx_fifo.we.eq(1), + self.uart_logger.tx_fifo.din.eq(self.postslip_reader.data_result[bitslip_count][8:]), + NextState("REWRITE_LOWER"), + ), + ) + + fsm.act("REWRITE_LOWER", + If(self.uart_logger.tx_fifo.writable, + self.uart_logger.tx_fifo.we.eq(1), + self.uart_logger.tx_fifo.din.eq(self.postslip_reader.data_result[bitslip_count][:8]), + NextValue(bitslip_count, bitslip_count + 1), + NextState("REWRITE_UPPER"), + ) + ) + + fsm.act("TERMINATE", + NextState("TERMINATE"), + ) + + # self.submodules.wait_timer = WaitTimer(127) + # shift_count = Signal(5) + + # self.submodules.fsm = FSM(reset_state="WAIT") + + # self.fsm.act("WAIT", + # self.wait_timer.wait.eq(1), + # If(shift_count == 10, + # NextState("DUMP_UPPER"), + # ).Elif(self.wait_timer.done, + # NextState("MEASURE"), + # ), + # ) + + # self.fsm.act("MEASURE", + # If(self.rx_buffer.writable, + # self.rx_buffer.we.eq(1), + # self.rx_buffer.din.eq(self.rx.rxdata), + # NextState("SHIFT1"), + # ), + # ) + + # self.fsm.act("SHIFT1", + # self.rx.master_bitslip.eq(1), + # self.rx.slave_bitslip.eq(1), + # NextState("GAP"), + # ) + + # self.fsm.act("GAP", + # NextState("SHIFT2"), + # ) + + # self.fsm.act("SHIFT2", + # self.rx.master_bitslip.eq(1), + # self.rx.slave_bitslip.eq(1), + # NextValue(shift_count, shift_count + 1), + # NextState("WAIT"), + # ) + + # self.fsm.act("DUMP_UPPER", + # If(self.rx_buffer.readable, + # If(self.uart_logger.tx_fifo.writable, + # self.uart_logger.tx_fifo.we.eq(1), + # self.uart_logger.tx_fifo.din.eq(self.rx_buffer.dout[8:]), + # NextState("DUMP_LOWER"), + # ), + # ).Else( + # NextState("TERMINATE"), + # ) + # ) + + # self.fsm.act("DUMP_LOWER", + # If(self.uart_logger.tx_fifo.writable, + # self.uart_logger.tx_fifo.we.eq(1), + # self.uart_logger.tx_fifo.din.eq(self.rx_buffer.dout[:8]), + # self.rx_buffer.re.eq(1), + # NextState("DUMP_UPPER"), + # ), + # ) + + # self.fsm.act("TERMINATE", + # NextState("TERMINATE"), + # ) + + +if __name__ == "__main__": + import argparse + import functools + import os + + 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 + for eem in range(2): + generate_pads(platform, eem) + data_eem = 0 + + i_pads = [ + platform.request("dio{}".format(data_eem), i) for i in range(4) + ] + o_pads = [ + platform.request("dio{}".format(data_eem), i+4) for i in range(4) + ] + + crg = TransceiverCRG(platform, sysclk) + uart_logger = UARTLogger(crg.sys_clk_freq) + top = SingleLineReader(uart_logger, i_pads, o_pads) + + # Wire up UART core to the pads + uart_pads = platform.request("serial") + top.comb += [ + uart_logger.uart_rx.eq(uart_pads.rx), + uart_pads.tx.eq(uart_logger.uart_tx), + ] + + top.submodules += crg + + output_dir = "{}_{}_build".format(args.platform, "satellite") + platform.build(top, build_dir=output_dir) diff --git a/serdes_comm.py b/serdes_comm.py new file mode 100644 index 0000000..5dab0fd --- /dev/null +++ b/serdes_comm.py @@ -0,0 +1,395 @@ +from migen import * +from sync_serdes import MultiLineRX, MultiLineTX +from multi_coders import MultiEncoder, CrossbarDecoder +from migen.build.platforms.sinara import kasli, efc +from eem_helpers import generate_pads +from kasli_crg import TransceiverCRG +from uart import UART +from migen.genlib.fifo import SyncFIFO + + +class BiDirectionalIO(Module): + def __init__(self, i_pads, o_pads): + self.i = Signal(4) + self.o = Signal(4) + self.t = Signal(4) + + for i in range(4): + self.specials += Instance("OBUFTDS", + i_I=self.i[i], + o_O=o_pads[i].p, + o_OB=o_pads[i].n, + # Always chain the 3-states input to serializer + # Vivado will complain otherwise + i_T=self.t[i], + ) + + for i in range(4): + self.specials += Instance("IBUFDS", + i_I=i_pads[i].p, + i_IB=i_pads[i].n, + o_O=self.o[i], + ) + + +class ClockOut(Module): + def __init__(self, clk_out): + self.clk = Signal() + # clk_buffer = Signal() + # clk_se = Signal() + + # self.specials += Instance("BUFIO", + # i_I=self.clk, + # o_O=clk_buffer, + # ) + + # self.specials += Instance("ODDR", + # i_C=clk_buffer, i_CE=1, i_D1=0, i_D2=1, o_Q=clk_se), + + self.specials += Instance("OBUFDS", + i_I=self.clk, + o_O=clk_out.p, + o_OB=clk_out.n, + ) + + +class Master(Module): + def __init__(self, i_pads, o_pads): + self.sysclk = ClockSignal("sys") + self.submodules.tx = MultiLineTX() + self.submodules.rx = MultiLineRX() + self.submodules.channel = BiDirectionalIO(i_pads, o_pads) + + self.submodules.encoder = MultiEncoder(lsb_first=False) + decoders = [ CrossbarDecoder(lsb_first=False) for _ in range(2) ] + self.submodules += decoders + + self.comb += [ + # Transmitter to SERDES + self.channel.i.eq(self.tx.ser_out), + self.channel.t.eq(self.tx.t_out), + + # SERDES to receiver + self.rx.ser_in_no_dly.eq(self.channel.o), + + # Connect encoders & decoders by default + # Overrule the encoder connection during alignment + self.tx.txdata.eq(Cat(self.encoder.output[0], self.encoder.output[1])), + decoders[0].raw_input.eq(self.rx.rxdata[:10]), + decoders[1].raw_input.eq(self.rx.rxdata[10:]), + ] + + tx_fsm = FSM(reset_state="SEND_TRAINING") + self.submodules += tx_fsm + + tx_fsm.act("SEND_TRAINING", + self.tx.txdata.eq(0b00100001000010000100), + # Keep sending the training sequence unless + # an identifier is received + NextState("SEND_TRAINING"), + # If(self.rx.rxdata == 0b11111111111111111111, + # NextState("WAIT_IDENT_END"), + # ), + ) + + tx_fsm.act("WAIT_IDENT_END", + self.tx.txdata.eq(0), + If(self.rx.rxdata != 0b11111111111111111111, + NextState("SEND_ZERO"), + ), + ) + + send_zero_duration = Signal(3) + + tx_fsm.act("SEND_ZERO", + self.tx.txdata.eq(0), + If(send_zero_duration == 0b111, + 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), + # Slave decoder start will be triggered by this state + NextState("WAIT_GROUP_ALIGN"), + ) + + data = [ Signal(8) for _ in range(2) ] + + tx_fsm.act("WAIT_GROUP_ALIGN", + # Wait for the identifier from the slave + # TODO: Align the master receiver after + If(self.rx.rxdata == 0b11111111111111111111, + NextValue(data[0], 0x80), + NextValue(data[1], 0x7F), + NextState("TERMINATE"), + ), + ) + + tx_fsm.act("TERMINATE", + self.encoder.d[0].eq(0x89), + self.encoder.d[1].eq(0x75), + NextValue(data[0], data[0] + 1), + NextValue(data[1], data[1] - 1), + NextState("TERMINATE"), + ) + + +class Satellite(Module): + def __init__(self, i_pads, o_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), + ] + + # Attach FIFO to UART TX, send rate is too slow w.r.t sysclk + self.submodules.tx_fifo = SyncFIFO(8, 64) + + self.comb += [ + # 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), + ] + + self.submodules.tx = MultiLineTX() + self.submodules.rx = MultiLineRX() + self.submodules.channel = BiDirectionalIO(i_pads, o_pads) + + self.submodules.encoder = MultiEncoder(lsb_first=False) + decoders = [ CrossbarDecoder(lsb_first=False) for _ in range(2) ] + self.submodules += decoders + + self.comb += [ + # Transmitter to SERDES + self.channel.i.eq(self.tx.ser_out), + self.channel.t.eq(self.tx.t_out), + + # SERDES to receiver + self.rx.ser_in_no_dly.eq(self.channel.o), + + # Immediately start alignment for RX + self.rx.start.eq(1), + + # Connect encoders & decoders by default + # Overrule the encoder connection during alignment + self.tx.txdata.eq(Cat(self.encoder.output[0], self.encoder.output[1])), + decoders[0].raw_input.eq(self.rx.rxdata[:10]), + decoders[1].raw_input.eq(self.rx.rxdata[10:]), + + # Start decoder after delay_done is set + decoders[0].start.eq(self.rx.delay_done), + decoders[1].start.eq(self.rx.delay_done), + ] + + rx_fsm = FSM(reset_state="WAIT_ALIGN_DELAY") + self.submodules += rx_fsm + + log_buffer = SyncFIFO(20, 128) + self.submodules += log_buffer + + rx_fsm.act("WAIT_ALIGN_DELAY", + If(self.rx.align_done & log_buffer.writable, + log_buffer.we.eq(1), + log_buffer.din.eq(self.rx.rxdata), + ), + If(~log_buffer.writable, + NextState("DUMP_LOG_UPPER"), + ), + If(self.rx.delay_done, + NextState("LOG_TRAFFIC"), + ), + ) + + rx_fsm.act("LOG_TRAFFIC", + If(log_buffer.writable, + log_buffer.we.eq(1), + log_buffer.din[0:8].eq(decoders[0].d), + log_buffer.din[10:18].eq(decoders[1].d), + # log_buffer.din.eq(self.rx.rxdata), + ).Else( + NextState("DUMP_LOG_UPPER"), + ), + ) + + rx_fsm.act("DUMP_LOG_UPPER", + If(log_buffer.readable, + If(self.tx_fifo.writable, + self.tx_fifo.we.eq(1), + self.tx_fifo.din.eq(log_buffer.dout[18:20]), + NextState("DUMP_LOG_LOWER"), + ), + ).Else( + NextState("TERMINATE") + ), + ) + + rx_fsm.act("DUMP_LOG_LOWER", + If(self.tx_fifo.writable, + self.tx_fifo.we.eq(1), + self.tx_fifo.din.eq(log_buffer.dout[10:18]), + # log_buffer.re.eq(1), + NextState("DUMP_LOG_UPPER_SECOND"), + ), + ) + + rx_fsm.act("DUMP_LOG_UPPER_SECOND", + If(self.tx_fifo.writable, + self.tx_fifo.we.eq(1), + self.tx_fifo.din.eq(log_buffer.dout[8:10]), + NextState("DUMP_LOG_LOWER_SECOND"), + ), + ) + + rx_fsm.act("DUMP_LOG_LOWER_SECOND", + If(self.tx_fifo.writable, + self.tx_fifo.we.eq(1), + self.tx_fifo.din.eq(log_buffer.dout[:8]), + log_buffer.re.eq(1), + NextState("DUMP_LOG_UPPER"), + ), + ) + + rx_fsm.act("TERMINATE", + NextState("TERMINATE"), + ) + + tx_fsm = FSM(reset_state="WAIT_RX_INTRA_ALIGN") + self.submodules += tx_fsm + + tx_fsm.act("WAIT_RX_INTRA_ALIGN", + # Note: (Redundant) Only send something when aligned + self.tx.txdata.eq(0), + If(self.rx.align_done, + NextState("SEND_INTRA_ALIGN_IDENT"), + ), + ) + + # Master receiver is not aligned yet + # We need to account for the possibility of group delay + # and SERDES misalignment + # 4 cycles of full 1s seems sufficiently detectable + ident_timer = Signal(2) + + tx_fsm.act("SEND_INTRA_ALIGN_IDENT", + self.tx.txdata.eq(0b11111111111111111111), + If(ident_timer == 0b11, + NextValue(ident_timer, 0), + NextState("WAIT_RX_GROUP_ALIGN"), + ).Else( + NextValue(ident_timer, ident_timer + 1) + ), + ) + + tx_fsm.act("WAIT_RX_GROUP_ALIGN", + self.tx.txdata.eq(0), + If(self.rx.delay_done, + NextState("SEND_RX_DELAY_IDENT"), + ), + ) + + tx_fsm.act("SEND_RX_DELAY_IDENT", + self.tx.txdata.eq(0b11111111111111111111), + If(ident_timer == 0b11, + NextValue(ident_timer, 0), + NextState("TERMINATE"), + ).Else( + NextValue(ident_timer, ident_timer + 1) + ), + ) + + tx_fsm.act("TERMINATE", + # TODO: Release the TX serdes to the encoders + self.tx.txdata.eq(0), + NextState("TERMINATE"), + ) + + +if __name__ == "__main__": + import argparse + import functools + import os + + parser = argparse.ArgumentParser() + parser.add_argument("platform") + parser.add_argument("variant") + 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] + + # Generate pads for the I/O blocks + for eem in range(2): + generate_pads(platform, eem) + data_eem = 0 + clk_eem = 1 + + clk_pad = platform.request("dio{}".format(clk_eem), 0) + + # Manage clock source + if args.variant == "master": + sysclk = platform.request(sysclk_name[args.platform]) + # Fan out clock through EEM1 pair 0 + # clkout = ClockOut(clk_pad) + elif args.variant == "satellite": + # Receive clock from EEM1 pair 0 + sysclk = clk_pad + + if args.variant == "master": + i_pads = [ + platform.request("dio{}".format(data_eem), i) for i in range(4) + ] + o_pads = [ + platform.request("dio{}".format(data_eem), i+4) for i in range(4) + ] + elif args.variant == "satellite": + i_pads = [ + platform.request("dio{}".format(data_eem), i) for i in range(4) + ] + o_pads = [ + platform.request("dio{}".format(data_eem), i+4) for i in range(4) + ] + else: + raise ValueError("variant {} not implemented".format(args.variant)) + + crg = TransceiverCRG(platform, sysclk, gte=(args.variant == "master")) + + variant = { + "master": functools.partial(Master, i_pads, o_pads), + "satellite": functools.partial(Satellite, i_pads, o_pads, crg.sys_clk_freq), + } + module_cls = variant[args.variant] + + top = module_cls() + + # Wire up UART core to the pads + if args.variant == "satellite": + uart_pads = platform.request("serial") + top.comb += [ + top.uart_rx.eq(uart_pads.rx), + uart_pads.tx.eq(top.uart_tx), + ] + + top.submodules += crg + # if args.variant == "master": + # top.submodules += clkout + # top.comb += clkout.clk.eq(top.sysclk) + + output_dir = "{}_{}_build".format(args.platform, args.variant) + platform.build(top, build_dir=output_dir) diff --git a/short_pulse_tx.py b/short_pulse_tx.py new file mode 100644 index 0000000..f46c98d --- /dev/null +++ b/short_pulse_tx.py @@ -0,0 +1,50 @@ +from migen import * +from migen.build.platforms.sinara import kasli, efc +from sync_serdes import MultiLineTX +from bidirectionalIO import BiDirectionalIO +from eem_helpers import generate_pads +from kasli_crg import TransceiverCRG + + +class ShortPulseTX(Module): + def __init__(self, i_pads, o_pads): + # TX serdes + self.submodules.tx = MultiLineTX() + # TX PHY + self.submodules.channel = BiDirectionalIO(i_pads, o_pads) + + self.comb += [ + # Transmitter to SERDES + self.channel.i.eq(self.tx.ser_out), + self.channel.t.eq(self.tx.t_out), + + # # SERDES to receiver + # self.rx.ser_in_no_dly.eq(self.channel.o), + + # Hardwire TX with 1 pulse signal + self.tx.txdata.eq(0b00000000000000010000) + ] + + +if __name__ == "__main__": + platform = efc.Platform() + + # Generate pads for the I/O blocks + for eem in range(2): + generate_pads(platform, eem) + data_eem = 0 + + i_pads = [ + platform.request("dio{}".format(data_eem), i) for i in range(4) + ] + o_pads = [ + platform.request("dio{}".format(data_eem), i+4) for i in range(4) + ] + + crg = TransceiverCRG(platform, platform.request("gtp_clk")) + top = ShortPulseTX(i_pads, o_pads) + + top.submodules += crg + + output_dir = "{}_{}_build".format("efc", "master") + platform.build(top, build_dir=output_dir) diff --git a/single_serdes_loopback.py b/single_serdes_loopback.py index 6d37b97..c1646e8 100644 --- a/single_serdes_loopback.py +++ b/single_serdes_loopback.py @@ -1,7 +1,7 @@ from migen import * from sync_serdes import * from migen.genlib.fifo import SyncFIFO -from migen.build.platforms.sinara import kasli +from migen.build.platforms.sinara import kasli, efc from migen.genlib.misc import WaitTimer from kasli_crg import TransceiverCRG from eem_helpers import generate_pads @@ -470,14 +470,15 @@ class SingleSerDesLoopBack(Module): if __name__ == "__main__": - platform = kasli.Platform(hw_rev="v2.0") + # platform = kasli.Platform(hw_rev="v2.0") + platform = efc.Platform() # Generate pads for the I/O blocks eem = 1 generate_pads(platform, eem) pad = platform.request("dio{}".format(eem), 0) - crg = TransceiverCRG(platform, platform.request("clk125_gtp")) + crg = TransceiverCRG(platform, platform.request("gtp_clk")) top = SingleSerDesLoopBack(pad, crg.sys_clk_freq, True) # Wire up UART core to the pads diff --git a/uart_log.py b/uart_log.py new file mode 100644 index 0000000..00b3350 --- /dev/null +++ b/uart_log.py @@ -0,0 +1,25 @@ +from migen import * +from migen.genlib.fifo import SyncFIFO +from uart import UART + + +class UARTLogger(Module): + def __init__(self, sys_clk_freq): + # UART PHY interface + self.uart_rx = Signal() + self.uart_tx = Signal() + + # Attach FIFO to UART TX, send rate is too slow w.r.t sysclk + self.submodules.tx_fifo = SyncFIFO(8, 64) + + self.submodules.uart = UART(round((115200/sys_clk_freq)*2**32)) + self.comb += [ + # PHY connection + self.uart.phy_rx.eq(self.uart_rx), + self.uart_tx.eq(self.uart.phy_tx), + + # 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), + ]