diff --git a/src/gateware/cxp_upconn.py b/src/gateware/cxp_upconn.py index a6c0e03..ba1ecad 100644 --- a/src/gateware/cxp_upconn.py +++ b/src/gateware/cxp_upconn.py @@ -1,42 +1,58 @@ from math import ceil 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_ClockGen(Module): +IDLE_CHARS = Array([ + #[char, k] + [0xBC, 1], #K28.5 + [0x3C, 1], #K28.1 + [0x3C, 1], #K28.1 + [0xB5, 0], #D21.5 +]) + +class 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 + self.clk_10x = Signal() # 20.83MHz 48ns or 41.66MHz 24ns + + self.freq2x_enable = Signal() # # # 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) + clk_div = Signal(max=10, reset=9) self.sync += [ - self.clk_10x.eq(0), self.clk.eq(0), + self.clk_10x.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), + If(self.freq2x_enable, + counter.eq(int(max_count/2)-1), ).Else( - divided.eq(divided-1) - ) + counter.eq(counter.reset), + ), ).Else( counter.eq(counter-1), + ), + + If(counter == 0, + If(clk_div == 0, + self.clk.eq(1), + clk_div.eq(clk_div.reset), + ).Else( + clk_div.eq(clk_div-1), + ) ) + ] class SERDES_10bits(Module): @@ -58,6 +74,7 @@ class SERDES_10bits(Module): self.sync += [ If(self.oe, + # send LSB first o.eq(tx_reg[0]), tx_reg.eq(Cat(tx_reg[1:], 0)), tx_bitcount.eq(tx_bitcount + 1), @@ -72,8 +89,8 @@ class SERDES_10bits(Module): ) ] -class Priority_8b10b_Machine(Module): - def __init__(self, tx_fifos, nfifos): +class Transmission_Scheduler(Module): + def __init__(self, tx_fifos): self.tx_enable = Signal() self.oe = Signal() @@ -83,19 +100,11 @@ class Priority_8b10b_Machine(Module): 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) + priorities = Signal.like(tx_fifos.pe.o) # DEBUG: self.idling = Signal() @@ -168,258 +177,6 @@ class Priority_8b10b_Machine(Module): ) ] -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): - self.clock_domains.cd_cxp_upconn = ClockDomain() - self.clk_reset = Signal() - self.bitrate2x_enable = Signal() - self.tx_enable = Signal() - - # # # - - 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, - o_O=self.cd_cxp_upconn.clk - ), - AsyncResetSynchronizer(self.cd_cxp_upconn, ~pll_locked | self.clk_reset) - ] - - self.submodules.startup_fsm = startup_fsm = ClockDomainsRenamer("cxp_upconn")(FSM(reset_state="WAIT_TX_ENABLE")) - self.submodules.encoder = encoder = ClockDomainsRenamer("cxp_upconn")(SingleEncoder(True)) - self.submodules.tx_fifos = tx_fifos = TxFIFOs(nfifos, fifo_depth) - self.submodules.tx_idle = tx_idle = TxIdle() - - o = Signal() - self.specials += Instance("OBUF", i_I=o, o_O=pad), - - tx_en = Signal() - tx_bitcount = Signal(max=10) - tx_charcount = Signal(max=4) - tx_wordcount = Signal(max=10000) - tx_reg = Signal(10) - - priorities = Signal(max=nfifos) - idling = Signal() - - - startup_fsm.act("WAIT_TX_ENABLE", - If(self.tx_enable, - NextValue(tx_idle.word_idx, 0), - NextState("ENCODE_CHAR") - ) - ) - - startup_fsm.act("ENCODE_CHAR", - NextValue(tx_idle.source_ack, 1), - NextValue(encoder.d, tx_idle.source_data), - NextValue(encoder.k, tx_idle.source_k), - NextState("LOAD_CHAR"), - ) - - startup_fsm.act("LOAD_CHAR", - NextValue(idling, 1), - NextValue(tx_charcount, 0), - NextValue(tx_bitcount, 0), - - NextValue(tx_reg, encoder.output), - NextValue(encoder.disp_in, encoder.disp_out), - NextState("START_TX") - ) - - startup_fsm.act("START_TX", - tx_en.eq(1), - If((~self.tx_enable) & (tx_charcount == 3), - NextState("WAIT_TX_ENABLE") - ) - ) - - # OSERDESE2 is not used as PLLE2 can't output the required 2.083MHz (480ns) for the CLKDIV - self.sync.cxp_upconn += [ - If(tx_en, - 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(encoder.output), - encoder.disp_in.eq(encoder.disp_out), - ), - - # packet insertion and idle word - If(tx_bitcount == 9, - # Section 9.2.4 (CXP-001-2021) - # trigger packets should be inserted at char boundary - 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), - - tx_idle.source_ack.eq(1), - encoder.d.eq(tx_idle.source_data), - encoder.k.eq(tx_idle.source_k), - ) - ).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( - tx_idle.source_ack.eq(1), - encoder.d.eq(tx_idle.source_data), - encoder.k.eq(tx_idle.source_k), - ) - ), - ) - ) - ).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), - ) - ] - - 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(tx_charcount == 3), - p0.eq(tx_idle.word_idx == 0), - p3.eq(tx_idle.word_idx == 3), - ] - self.specials += [ - # # debug sma - Instance("OBUF", i_I=o, o_O=debug_sma.p_tx), - Instance("OBUF", i_I=self.cd_cxp_upconn.clk, o_O=debug_sma.n_rx), - - # # pmod 0-7 pin - Instance("OBUF", i_I=o, o_O=pmod_pads[0]), - Instance("OBUF", i_I=self.cd_cxp_upconn.clk, 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=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 TxFIFOs(Module): def __init__(self, nfifos, fifo_depth): @@ -434,11 +191,9 @@ class TxFIFOs(Module): # # # - non_empty = Signal(nfifos) + not_empty_reg = Signal(nfifos) for i in range(nfifos): - # 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) @@ -459,45 +214,70 @@ class TxFIFOs(Module): fifo.source.ack.eq(0), ), - non_empty[i].eq(fifo.source.stb), + not_empty_reg[i].eq(fifo.source.stb), self.source_data[i].eq(fifo.source.data), self.source_k[i].eq(fifo.source.k), ] # FIFOs transmission priority self.submodules.pe = PriorityEncoder(nfifos) - self.comb += self.pe.i.eq(non_empty) + self.comb += self.pe.i.eq(not_empty_reg) + +class CXP_UpConn(Module): + def __init__(self, pad, sys_clk_freq, debug_sma, pmod_pads, fifo_depth, nfifos=3): + self.bitrate2x_enable = Signal() + self.tx_enable = Signal() -class TxIdle(Module): - def __init__(self): - self.source_ack = Signal() - self.source_data = Signal(8) - self.source_k = Signal() - - self.word_idx = Signal(max=4) # # # - # section 9.2.5 (CXP-001-2021) - IDLE_CHARS = Array([ - #[char, k] - [0b10111100, 1], #K28.5 - [0b00111100, 1], #K28.1 - [0b00111100, 1], #K28.1 - [0b10111100, 0], #D28.5 - ]) + self.submodules.cg = cg = UpConn_ClockGen(sys_clk_freq) + self.submodules.tx_fifos = tx_fifos = TxFIFOs(nfifos, fifo_depth) - self.sync += [ - self.source_data.eq(IDLE_CHARS[self.word_idx][0]), - self.source_k.eq(IDLE_CHARS[self.word_idx][1]), + self.submodules.scheduler = scheduler = CEInserter()(Transmission_Scheduler(tx_fifos)) + self.submodules.serdes = serdes = CEInserter()(SERDES_10bits(pad)) - If(self.source_ack, - # reset after asserted - self.source_ack.eq(0), + self.comb += [ + cg.freq2x_enable.eq(self.bitrate2x_enable), - If(self.word_idx != 3, - self.word_idx.eq(self.word_idx + 1), - ).Else( - self.word_idx.eq(self.word_idx.reset), - ) - ), + scheduler.ce.eq(cg.clk), + scheduler.tx_enable.eq(self.tx_enable), + + serdes.ce.eq(cg.clk_10x), + serdes.d.eq(scheduler.encoder.output), + serdes.oe.eq(scheduler.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(scheduler.tx_charcount == 3), + + # because of clk delay + p0.eq(scheduler.tx_charcount == 2), + p3.eq(scheduler.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=scheduler.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]), + ] +