diff --git a/src/gateware/cxp.py b/src/gateware/cxp.py new file mode 100644 index 0000000..e6d54fe --- /dev/null +++ b/src/gateware/cxp.py @@ -0,0 +1,230 @@ +from migen import * +from misoc.interconnect.csr import * + +from cxp_downconn import CXP_DownConn_PHY +from cxp_upconn import CXP_UpConn_PHY +from cxp_pipeline import * + +buffer_depth = 128 + +@FullMemoryWE() +class CXP(Module, AutoCSR): + def __init__(self, refclk, downconn_pads, upconn_pads, sys_clk_freq, debug_sma, pmod_pads): + self.submodules.upconn = UpConn_Interface(upconn_pads, sys_clk_freq, debug_sma, pmod_pads) + + self.submodules.downconn = DownConn_Interface(refclk, downconn_pads, sys_clk_freq, debug_sma, pmod_pads) + # TODO: support the option high speed upconn + + self.submodules.transmitter = Transmitter() + + # TODO: add link layer + + def get_tx_port(self): + return self.transmitter.mem.get_port(write_capable=True) + + def get_mem_size(self): + return buffer_depth*downconn_dw + +@FullMemoryWE() +class Transmitter(Module, AutoCSR): + def __init__(self): + self.cxp_tx_word_len = CSRStorage(bits_for(buffer_depth)) + self.cxp_tx = CSR() + + # # # + + self.specials.mem = mem = Memory(downconn_dw, buffer_depth) + self.specials.mem_port = mem_port = mem.get_port() + self.source = stream.Endpoint(downconn_layout) + + + tx_done = Signal() + addr_next = Signal(bits_for(buffer_depth)) + addr = Signal.like(addr_next) + addr_rst = Signal() + addr_inc = Signal() + + # increment addr in the same cycle the moment addr_inc is rise + # since memory takes one cycle to shift to the correct addr + self.sync += [ + addr.eq(addr_next), + If(self.cxp_tx.re, self.cxp_tx.w.eq(1)), + If(tx_done, self.cxp_tx.w.eq(0)), + ] + + self.comb += [ + addr_next.eq(addr), + If(addr_rst, + addr_next.eq(addr_next.reset), + ).Elif(addr_inc, + addr_next.eq(addr + 1), + ), + mem_port.adr.eq(addr_next), + self.source.data.eq(mem_port.dat_r) + ] + + self.submodules.fsm = fsm = FSM(reset_state="IDLE") + + fsm.act("IDLE", + addr_rst.eq(1), + If(self.cxp_tx.re, NextState("TRANSMIT")) + ) + fsm.act("TRANSMIT", + self.source.stb.eq(1), + If(self.source.ack, + addr_inc.eq(1), + ), + If(addr_next == self.cxp_tx_word_len.storage, + tx_done.eq(1), + NextState("IDLE") + ) + ) + + self.submodules.debug_out = debug_out = RX_Debug_Buffer() + self.comb += self.source.connect(debug_out.sink) + +class DownConn_Interface(Module, AutoCSR): + def __init__(self, refclk, downconn_pads, sys_clk_freq, debug_sma, pmod_pads): + # # # + + self.submodules.phy = phy = CXP_DownConn_PHY(refclk, downconn_pads, sys_clk_freq, debug_sma, pmod_pads) + self.gtxs = phy.gtxs + + # DEBUG: TX pipeline + self.submodules.debug_src = debug_src = TX_Command_Packet() + self.submodules.trig_ack = trig_ack = Trigger_ACK() + self.submodules.testseq = testseq = TX_Test_Packet() + self.submodules.mux = mux = stream.Multiplexer(upconn_layout, 3) + self.submodules.conv = conv = stream.StrideConverter(upconn_layout, downconn_layout) + + self.ack = CSR() + self.mux_sel = CSRStorage(4) + self.sync += trig_ack.ack.eq(self.ack.re), + + self.comb += [ + debug_src.source.connect(mux.sink0), + trig_ack.source.connect(mux.sink1), + testseq.source.connect(mux.sink2), + mux.sel.eq(self.mux_sel.storage) + ] + + tx_pipeline = [mux , conv, phy.sinks[0]] + for s, d in zip(tx_pipeline, tx_pipeline[1:]): + self.comb += s.source.connect(d.sink) + + # NOTE: RX pipeline + self.submodules.debug_out = debug_out = RX_Debug_Buffer() + self.submodules.recv_path = recv_path = Receiver_Path() + + rx_pipeline = [phy.sources[0], recv_path, debug_out] + for s, d in zip(rx_pipeline, rx_pipeline[1:]): + self.comb += s.source.connect(d.sink) + + + self.packet_type = CSRStatus(8) + self.decoder_error = CSRStatus() + self.test_error = CSRStatus() + self.comb += [ + self.packet_type.status.eq(recv_path.packet_type), + self.decoder_error.status.eq(recv_path.decoder_err), + self.test_error.status.eq(recv_path.test_err), + ] + + # DEBUG: CSR + self.trig_ack = CSRStatus() + self.trig_clr = CSR() + self.comb += [ + self.trig_ack.status.eq(recv_path.trig_ack), + recv_path.trig_clr.eq(self.trig_clr.re), + ] + + pak_start = Signal() + self.sync += [ + pak_start.eq(recv_path.packet_decoder.sink.data == 0xFBFBFBFB), + + ] + + self.specials += [ + # # pmod 0-7 pin + Instance("OBUF", i_I=recv_path.packet_decoder.test_err, o_O=pmod_pads[0]), + Instance("OBUF", i_I=pak_start, o_O=pmod_pads[1]), + # Instance("OBUF", i_I=fifo_in.source.ack, o_O=pmod_pads[2]), + # Instance("OBUF", i_I=gtx.comma_checker.aligner_en, o_O=pmod_pads[3]), + # Instance("OBUF", i_I=gtx.comma_checker.check_reset, o_O=pmod_pads[4]), + # Instance("OBUF", i_I=gtx.comma_checker.has_comma, o_O=pmod_pads[5]), + # Instance("OBUF", i_I=gtx.comma_checker.has_error, o_O=pmod_pads[6]), + # Instance("OBUF", i_I=gtx.comma_checker.ready_sys, o_O=pmod_pads[7]), + ] + + +class UpConn_Interface(Module, AutoCSR): + def __init__(self, upconn_pads, sys_clk_freq, debug_sma, pmod_pads): + self.clk_reset = CSRStorage(reset=1) + self.bitrate2x_enable = CSRStorage() + self.tx_enable = CSRStorage() + self.tx_busy = CSRStatus() + + self.tx_testmode_en = CSRStorage() + + # # # + + self.submodules.phy = phy = CXP_UpConn_PHY(upconn_pads, sys_clk_freq, debug_sma, pmod_pads) + + self.sync += [ + phy.bitrate2x_enable.eq(self.bitrate2x_enable.storage), + phy.tx_enable.eq(self.tx_enable.storage), + phy.clk_reset.eq(self.clk_reset.re), + self.tx_busy.status.eq(phy.tx_busy), + ] + + + # TODO: rewrite the transmite path into pipeline + # + # test pak ----+ + # from gw | 32 32 8 + # |---/---> mux -----> trig ack -----> idle word ---/--> conv ---/---> trig -----> PHY + # | inserter inserter inserter + # data pak ----+ + # from fw + + # Packet FIFOs with transmission priority + # 0: Trigger packet + self.submodules.trig = trig = TX_Trigger() + self.comb += trig.source.connect(phy.sinks[0]) + + # DEBUG: INPUT + self.trig_stb = CSR() + self.trig_delay = CSRStorage(8) + self.linktrigger = CSRStorage(2) + + self.sync += [ + trig.trig_stb.eq(self.trig_stb.re), + trig.delay.eq(self.trig_delay.storage), + trig.linktrig_mode.eq(self.linktrigger.storage), + ] + + + # 1: IO acknowledgment for trigger packet + self.submodules.trig_ack = trig_ack = Trigger_ACK() + self.comb += trig_ack.source.connect(phy.sinks[1]) + + # DEBUG: INPUT + self.ack = CSR() + self.sync += trig_ack.ack.eq(self.ack.re), + + + # 2: All other packets + # Control is not timing dependent, all the link layer is done in firmware + self.submodules.command = command = TX_Command_Packet() + self.submodules.testseq = testseq = TX_Test_Packet() + + + self.submodules.mux = mux = stream.Multiplexer(upconn_layout, 2) + + self.comb += [ + command.source.connect(mux.sink0), + testseq.source.connect(mux.sink1), + mux.sel.eq(self.tx_testmode_en.storage), + + mux.source.connect(phy.sinks[2]) + ]