From cce5a079c130ef06483eda6d109aa9c617fcb0bd Mon Sep 17 00:00:00 2001 From: morgan Date: Mon, 17 Jun 2024 13:19:37 +0800 Subject: [PATCH] cxp upconn: add low speed serial cxp upconn: add low speed serial cxp upconn: add pll for bitrate2x mode cxp upconn: add tx_fifos & idle with encoder cxp upconn: add priority packet that send in word/char boundary cxp upconn: disable debug sma & pmod output --- src/gateware/cxp_upconn.py | 279 +++++++++++++++++++++++++++++++++++++ 1 file changed, 279 insertions(+) create mode 100644 src/gateware/cxp_upconn.py diff --git a/src/gateware/cxp_upconn.py b/src/gateware/cxp_upconn.py new file mode 100644 index 0000000..d9e01ec --- /dev/null +++ b/src/gateware/cxp_upconn.py @@ -0,0 +1,279 @@ +from migen import * +from migen.genlib.resetsync import AsyncResetSynchronizer +from migen.genlib.coding import PriorityEncoder + +from misoc.cores.code_8b10b import SingleEncoder +from misoc.interconnect import stream +from misoc.interconnect.csr import * + + +class CXP_UpConn(Module, AutoCSR): + nfifos = 3 + def __init__(self, pads, sys_clk_freq, pmod, fifo_depth=32): + self.clock_domains.cd_cxp_upconn = ClockDomain() + self.clk_reset = CSRStorage(reset=1) + self.bitrate2x_enable = CSRStorage() + self.tx_enable = CSRStorage() + + # # # + + pll_locked = Signal() + pll_fb_clk = Signal() + pll_cxpclk = Signal() + pll_cxpclk2x = Signal() + + self.specials += [ + Instance("PLLE2_ADV", + p_BANDWIDTH="HIGH", + o_LOCKED=pll_locked, + i_RST=ResetSignal("sys"), + + p_CLKIN1_PERIOD=1e9/sys_clk_freq, # ns + i_CLKIN1=ClockSignal("sys"), + + # VCO @ 1.25GHz + p_CLKFBOUT_MULT=1.25e9/sys_clk_freq, p_DIVCLK_DIVIDE=1, + i_CLKFBIN=pll_fb_clk, o_CLKFBOUT=pll_fb_clk, + + # 20.83MHz (48ns) + p_CLKOUT0_DIVIDE=60, p_CLKOUT0_PHASE=0.0, o_CLKOUT0=pll_cxpclk, + + # 41.66MHz (24ns) for downconnection over 6.25Gpbs + p_CLKOUT1_DIVIDE=30, p_CLKOUT1_PHASE=0.0, o_CLKOUT1=pll_cxpclk2x, + ), + Instance("BUFGMUX", + i_I0=pll_cxpclk, + i_I1=pll_cxpclk2x, + i_S=self.bitrate2x_enable.storage, + o_O=self.cd_cxp_upconn.clk + ), + AsyncResetSynchronizer(self.cd_cxp_upconn, ~pll_locked | self.clk_reset.storage) + ] + + self.submodules.fsm = ClockDomainsRenamer("cxp_upconn")(FSM(reset_state="WAIT_TX_ENABLE")) + self.submodules.tx_fifos = TxFIFOs(self.nfifos, fifo_depth) + self.submodules.tx_idle = TxIdle() + + o = Signal() + tx_en = Signal() + tx_bitcount = Signal(max=10) + tx_wordcount = Signal(max=4) + tx_reg = Signal(10) + + disp = Signal() + priorities = Signal(max=self.nfifos) + idling = Signal() + + # startup sequence + self.fsm.act("WAIT_TX_ENABLE", + If(self.tx_enable.storage, + NextValue(self.tx_idle.word_idx, 0), + NextValue(tx_wordcount, 0), + NextValue(tx_bitcount, 0), + NextState("LOAD_CHAR") + ) + ) + + self.fsm.act("LOAD_CHAR", + NextValue(idling, 1), + NextValue(self.tx_idle.source_ack, 1), + NextValue(tx_reg, self.tx_idle.source_data), + NextValue(disp, self.tx_idle.disp_out), + NextState("START_TX") + ) + + self.fsm.act("START_TX", + tx_en.eq(1), + If((~self.tx_enable.storage) & (tx_wordcount == 3), + NextState("WAIT_TX_ENABLE") + ) + ) + + self.sync.cxp_upconn += [ + self.tx_fifos.disp_in.eq(disp), + self.tx_idle.disp_in.eq(disp), + + If(tx_en, + o.eq(tx_reg[0]), + tx_reg.eq(Cat(tx_reg[1:], 0)), + tx_bitcount.eq(tx_bitcount + 1), + + + # char boundary + If(tx_bitcount == 9, + tx_bitcount.eq(0), + If((~self.tx_fifos.pe.n) & (self.tx_fifos.pe.o == 0), + # trigger packets are inserted at char boundary and don't contribute to word count + tx_reg.eq(self.tx_fifos.source_data[0]), + self.tx_fifos.source_ack[0].eq(1), + disp.eq(self.tx_fifos.disp_out[0]), + ).Else( + # word boundary + If(tx_wordcount == 3, + tx_wordcount.eq(0), + If(~self.tx_fifos.pe.n, + # priority lv 1 & 2 packets are inserted at word boundary + idling.eq(0), + priorities.eq(self.tx_fifos.pe.o), + self.tx_fifos.source_ack[self.tx_fifos.pe.o].eq(1), + tx_reg.eq(self.tx_fifos.source_data[self.tx_fifos.pe.o]), + disp.eq(self.tx_fifos.disp_out[self.tx_fifos.pe.o]), + ).Else( + idling.eq(1), + self.tx_idle.source_ack.eq(1), + tx_reg.eq(self.tx_idle.source_data), + disp.eq(self.tx_idle.disp_out), + ) + ).Else( + tx_wordcount.eq(tx_wordcount + 1), + If(~idling, + self.tx_fifos.source_ack[priorities].eq(1), + tx_reg.eq(self.tx_fifos.source_data[priorities]), + disp.eq(self.tx_fifos.disp_out[priorities]), + ).Else( + self.tx_idle.source_ack.eq(1), + tx_reg.eq(self.tx_idle.source_data), + disp.eq(self.tx_idle.disp_out), + ) + ), + ) + ) + ).Else( + o.eq(0) + ) + ] + # DEBUG: remove pads + self.encoded_data = CSRStatus(10) + self.sync.cxp_upconn +=[ + If(tx_bitcount == 0, + self.encoded_data.status.eq(tx_reg), + ) + ] + + ninth_bit = Signal() + word_bound = Signal() + + p0 = Signal() + p3 = Signal() + self.comb += [ + ninth_bit.eq(tx_bitcount == 9), + word_bound.eq(tx_wordcount == 3), + p0.eq(self.tx_idle.word_idx == 0), + p3.eq(self.tx_idle.word_idx == 3), + ] + self.specials += [ + # # debug sma + # Instance("OBUF", i_I=o, o_O=pads.p_tx), + # Instance("OBUF", i_I=self.cd_cxp_upconn.clk, o_O=pads.n_rx), + + # # pmod 0-7 pin + # Instance("OBUF", i_I=o, o_O=pmod[0]), + # Instance("OBUF", i_I=self.cd_cxp_upconn.clk, o_O=pmod[1]), + # Instance("OBUF", i_I=~self.tx_fifos.pe.n, o_O=pmod[2]), + # Instance("OBUF", i_I=ninth_bit, o_O=pmod[3]), + # Instance("OBUF", i_I=word_bound, o_O=pmod[4]), + # Instance("OBUF", i_I=idling, o_O=pmod[5]), + # # Instance("OBUF", i_I=self.tx_fifos.source_ack[0], o_O=pmod[6]), + # # Instance("OBUF", i_I=self.tx_fifos.source_ack[2], o_O=pmod[6]), + # # Instance("OBUF", i_I=self.tx_fifos.source_ack[1], o_O=pmod[7]), + # Instance("OBUF", i_I=p0, o_O=pmod[6]), + # Instance("OBUF", i_I=p3, o_O=pmod[7]), + ] + self.symbol0 = CSR(9) + self.symbol1 = CSR(9) + self.symbol2 = CSR(9) + + self.sync += [ + self.tx_fifos.sink_stb[0].eq(self.symbol0.re), + self.tx_fifos.sink_data[0].eq(self.symbol0.r), + self.tx_fifos.sink_stb[1].eq(self.symbol1.re), + self.tx_fifos.sink_data[1].eq(self.symbol1.r), + self.tx_fifos.sink_stb[2].eq(self.symbol2.re), + self.tx_fifos.sink_data[2].eq(self.symbol2.r), + ] + +class TxFIFOs(Module): + def __init__(self, nfifos, fifo_depth): + self.disp_in = Signal() + self.disp_out = Array(Signal() for _ in range(nfifos)) + + self.sink_stb = Signal(nfifos) + self.sink_ack = Signal(nfifos) + self.sink_data = [Signal(9) for _ in range(nfifos)] + + self.source_ack = Array(Signal() for _ in range(nfifos)) + self.source_data = Array(Signal(10) for _ in range(nfifos)) + + # # # + + source_stb = Signal(nfifos) + + for i in range(nfifos): + cdr = ClockDomainsRenamer({"write": "sys", "read": "cxp_upconn"}) + fifo = cdr(stream.AsyncFIFO([("data", 9)], fifo_depth)) + encoder = ClockDomainsRenamer("cxp_upconn")(SingleEncoder(True)) + setattr(self.submodules, "tx_fifo" + str(i), fifo) + setattr(self.submodules, "tx_encoder" + str(i), encoder) + self.sync += [ + fifo.sink.stb.eq(self.sink_stb[i]), + self.sink_ack[i].eq(fifo.sink.ack), + fifo.sink.data.eq(self.sink_data[i]), + ] + self.sync.cxp_upconn += [ + encoder.d.eq(fifo.source.data[:8]), + encoder.k.eq(fifo.source.data[8]), + encoder.disp_in.eq(self.disp_in), + self.disp_out[i].eq(encoder.disp_out), + + source_stb[i].eq(fifo.source.stb), + fifo.source.ack.eq(self.source_ack[i]), + self.source_data[i].eq(encoder.output), + # reset ack after asserted + If(self.source_ack[i], self.source_ack[i].eq(0)), + ] + + # FIFOs transmission priority + self.submodules.pe = PriorityEncoder(nfifos) + self.comb += self.pe.i.eq(source_stb) + +class TxIdle(Module): + def __init__(self): + self.disp_in = Signal() + self.disp_out = Signal() + + self.word_idx = Signal(max=4) + self.source_ack = Signal() + self.source_data = Signal(10) + + # # # + + # CXP 2.1 section 9.2.5 + IDLE_CHARS = Array([ + #[char, k] + [0b10111100, 1], #K28.5 + [0b00111100, 1], #K28.1 + [0b00111100, 1], #K28.1 + [0b10111100, 0], #D28.5 + ]) + + encoder = ClockDomainsRenamer("cxp_upconn")(SingleEncoder(True)) + self.submodules += encoder + + self.sync.cxp_upconn += [ + encoder.d.eq(IDLE_CHARS[self.word_idx][0]), + encoder.k.eq(IDLE_CHARS[self.word_idx][1]), + encoder.disp_in.eq(self.disp_in), + self.disp_out.eq(encoder.disp_out), + self.source_data.eq(encoder.output), + + If(self.source_ack, + # reset after asserted + self.source_ack.eq(0), + + If(self.word_idx != 3, + self.word_idx.eq(self.word_idx + 1), + ).Else( + self.word_idx.eq(0), + ) + ), + ]