From d55bd8113755fe35a0063b2d97d87bfdcd2da77b Mon Sep 17 00:00:00 2001 From: morgan Date: Wed, 28 Aug 2024 12:38:56 +0800 Subject: [PATCH] cxp upconn: rewrite oserdes using CEInserter IDLE word: fix p3 to D21.5 CLKGEN: missing 24ns --- src/gateware/cxp_upconn.py | 230 ++++++++++++++++++++++++++++++++++++- 1 file changed, 226 insertions(+), 4 deletions(-) diff --git a/src/gateware/cxp_upconn.py b/src/gateware/cxp_upconn.py index ce9b59b..a6c0e03 100644 --- a/src/gateware/cxp_upconn.py +++ b/src/gateware/cxp_upconn.py @@ -1,3 +1,5 @@ +from math import ceil + from migen import * from migen.genlib.resetsync import AsyncResetSynchronizer from migen.genlib.coding import PriorityEncoder @@ -6,6 +8,224 @@ from misoc.cores.code_8b10b import SingleEncoder from misoc.interconnect import stream from misoc.interconnect.csr import * +class CXP_UpConn_ClockGen(Module): + def __init__(self, sys_clk_freq): + self.clk = Signal() + self.clk_10x = Signal() # 20.83MHz 48ns + # self.clk2x = Signal() # 41.66MHz 24ns + # # # + + period = 1e9/sys_clk_freq + max_count = ceil(48/period) + counter = Signal(max=max_count, reset=max_count-1) + + divided = Signal(max=10, reset=9) + + self.sync += [ + self.clk_10x.eq(0), + self.clk.eq(0), + + If(counter == 0, + counter.eq(counter.reset), + self.clk_10x.eq(1), + If(divided == 0, + self.clk.eq(1), + divided.eq(divided.reset), + ).Else( + divided.eq(divided-1) + ) + ).Else( + counter.eq(counter-1), + ) + ] + +class SERDES_10bits(Module): + def __init__(self, pad): + self.oe = Signal() + self.d = Signal(10) + + # # # + + o = Signal() + tx_bitcount = Signal(max=10) + tx_reg = Signal(10) + + # DEBUG: + self.o = Signal() + self.comb += self.o.eq(o) + + self.specials += Instance("OBUF", i_I=o, o_O=pad), + + self.sync += [ + If(self.oe, + o.eq(tx_reg[0]), + tx_reg.eq(Cat(tx_reg[1:], 0)), + tx_bitcount.eq(tx_bitcount + 1), + + If(tx_bitcount == 9, + tx_bitcount.eq(0), + tx_reg.eq(self.d), + ), + ).Else( + o.eq(0), + tx_bitcount.eq(0), + ) + ] + +class Priority_8b10b_Machine(Module): + def __init__(self, tx_fifos, nfifos): + self.tx_enable = Signal() + + self.oe = Signal() + + # # # + + self.submodules.startup_fsm = startup_fsm = FSM(reset_state="WAIT_TX_ENABLE") + self.submodules.encoder = encoder = SingleEncoder(True) + + IDLE_CHARS = Array([ + #[char, k] + [0xBC, 1], #K28.5 + [0x3C, 1], #K28.1 + [0x3C, 1], #K28.1 + [0xB5, 0], #D21.5 + ]) + + tx_charcount = Signal(max=4) + tx_wordcount = Signal(max=10000) + + idling = Signal() + priorities = Signal(max=nfifos) + + # DEBUG: + self.idling = Signal() + self.tx_charcount = Signal(max=4) + self.comb += [ + self.idling.eq(idling), + self.tx_charcount.eq(tx_charcount), + ] + + startup_fsm.act("WAIT_TX_ENABLE", + If(self.tx_enable, + NextValue(idling, 1), + NextValue(tx_charcount, 0), + NextValue(encoder.d, IDLE_CHARS[0][0]), + NextValue(encoder.k, IDLE_CHARS[0][1]), + NextState("START_TX"), + ) + ) + + startup_fsm.act("START_TX", + self.oe.eq(1), + If((~self.tx_enable) & (tx_charcount == 3), + NextState("WAIT_TX_ENABLE") + ) + ) + + self.sync += [ + If(self.oe, + encoder.disp_in.eq(encoder.disp_out), + If((~tx_fifos.pe.n) & (tx_fifos.pe.o == 0), + # trigger packets are inserted at char boundary and don't contribute to word count + tx_fifos.source_ack[0].eq(1), + encoder.d.eq(tx_fifos.source_data[0]), + encoder.k.eq(tx_fifos.source_k[0]), + ).Else( + If(tx_charcount == 3, + tx_charcount.eq(0), + + # Section 9.2.4 (CXP-001-2021) + # other priorities packets are inserted at word boundary + If((~tx_fifos.pe.n) & (tx_wordcount != 9999), + idling.eq(0), + priorities.eq(tx_fifos.pe.o), + tx_wordcount.eq(tx_wordcount + 1), + + tx_fifos.source_ack[tx_fifos.pe.o].eq(1), + encoder.d.eq(tx_fifos.source_data[tx_fifos.pe.o]), + encoder.k.eq(tx_fifos.source_k[tx_fifos.pe.o]), + ).Else( + # Section 9.2.5.1 (CXP-001-2021) + # IDLE word shall be transmitted at least once every 10,000 words, but should not be inserted into trigger packet + idling.eq(1), + tx_wordcount.eq(0), + + encoder.d.eq(IDLE_CHARS[0][0]), + encoder.k.eq(IDLE_CHARS[0][1]), + ) + ).Else( + tx_charcount.eq(tx_charcount + 1), + If(~idling, + tx_fifos.source_ack[priorities].eq(1), + encoder.d.eq(tx_fifos.source_data[priorities]), + encoder.k.eq(tx_fifos.source_k[priorities]), + ).Else( + encoder.d.eq(IDLE_CHARS[tx_charcount + 1][0]), + encoder.k.eq(IDLE_CHARS[tx_charcount + 1][1]), + ) + ), + ), + ) + ] + +class CXP_with_CE(Module): + def __init__(self, pad, sys_clk_freq, debug_sma, pmod_pads, fifo_depth, nfifos=3): + self.bitrate2x_enable = Signal() + self.tx_enable = Signal() + + # # # + + self.submodules.cg = cg = CXP_UpConn_ClockGen(sys_clk_freq) + self.submodules.tx_fifos = tx_fifos = TxFIFOs(nfifos, fifo_depth) + + # TODO: rename machine plz + self.submodules.machine = machine = CEInserter()(Priority_8b10b_Machine(tx_fifos, nfifos)) + self.submodules.serdes = serdes = CEInserter()(SERDES_10bits(pad)) + + self.comb += [ + machine.ce.eq(cg.clk), + machine.tx_enable.eq(self.tx_enable), + + serdes.ce.eq(cg.clk_10x), + serdes.d.eq(machine.encoder.output), + serdes.oe.eq(machine.oe), + ] + + # DEBUG: remove pads + + prioity_0 = Signal() + word_bound = Signal() + + p0 = Signal() + p3 = Signal() + self.comb += [ + prioity_0.eq((~tx_fifos.pe.n) & (tx_fifos.pe.o == 0)), + word_bound.eq(machine.tx_charcount == 3), + + # because of clk delay + p0.eq(machine.tx_charcount == 2), + p3.eq(machine.tx_charcount == 1), + ] + self.specials += [ + # # debug sma + Instance("OBUF", i_I=cg.clk, o_O=debug_sma.p_tx), + Instance("OBUF", i_I=cg.clk_10x, o_O=debug_sma.n_rx), + + # # pmod 0-7 pin + Instance("OBUF", i_I=serdes.o, o_O=pmod_pads[0]), + Instance("OBUF", i_I=cg.clk_10x, o_O=pmod_pads[1]), + Instance("OBUF", i_I=~tx_fifos.pe.n, o_O=pmod_pads[2]), + Instance("OBUF", i_I=prioity_0, o_O=pmod_pads[3]), + Instance("OBUF", i_I=word_bound, o_O=pmod_pads[4]), + Instance("OBUF", i_I=machine.idling, o_O=pmod_pads[5]), + # Instance("OBUF", i_I=tx_fifos.source_ack[0], o_O=pmod[6]), + # Instance("OBUF", i_I=tx_fifos.source_ack[2], o_O=pmod[6]), + # Instance("OBUF", i_I=tx_fifos.source_ack[1], o_O=pmod[7]), + Instance("OBUF", i_I=p0, o_O=pmod_pads[6]), + Instance("OBUF", i_I=p3, o_O=pmod_pads[7]), + ] + + class CXP_UpConn(Module, AutoCSR): def __init__(self, pad, sys_clk_freq, debug_sma, pmod_pads, fifo_depth, nfifos=3): @@ -217,8 +437,10 @@ class TxFIFOs(Module): non_empty = Signal(nfifos) for i in range(nfifos): - cdr = ClockDomainsRenamer({"write": "sys", "read": "cxp_upconn"}) - fifo = cdr(stream.AsyncFIFO([("data", 8), ("k", 1)], fifo_depth)) + # cdr = ClockDomainsRenamer({"write": "sys", "read": "cxp_upconn"}) + # fifo = cdr(stream.AsyncFIFO([("data", 8), ("k", 1)], fifo_depth)) + fifo = stream.SyncFIFO([("data", 8), ("k", 1)], fifo_depth) + setattr(self.submodules, "tx_fifo" + str(i), fifo) self.sync += [ @@ -228,7 +450,7 @@ class TxFIFOs(Module): fifo.sink.k.eq(self.sink_k[i]), ] - self.sync.cxp_upconn += [ + self.sync += [ If(self.source_ack[i], # reset ack after asserted self.source_ack[i].eq(0), @@ -264,7 +486,7 @@ class TxIdle(Module): [0b10111100, 0], #D28.5 ]) - self.sync.cxp_upconn += [ + self.sync += [ self.source_data.eq(IDLE_CHARS[self.word_idx][0]), self.source_k.eq(IDLE_CHARS[self.word_idx][1]),