diff --git a/src/gateware/cxp_downconn.py b/src/gateware/cxp_downconn.py index 0cbbe8e..e8cf1c3 100644 --- a/src/gateware/cxp_downconn.py +++ b/src/gateware/cxp_downconn.py @@ -1,6 +1,5 @@ from migen import * from migen.genlib.cdc import MultiReg, PulseSynchronizer -from migen.genlib.misc import WaitTimer from migen.genlib.resetsync import AsyncResetSynchronizer from misoc.cores.code_8b10b import Encoder, Decoder @@ -155,43 +154,20 @@ class CXP_DownConn(Module, AutoCSR): self.loopback_mode = CSRStorage(3) self.comb += gtx.loopback_mode.eq(self.loopback_mode.storage) - # DEBUG: RXDATA checking - aligned = Signal() - valid_data = Signal() - rx1cnt = Signal(max=11) - - comma = 0b0101111100 - comma_n = ~comma & 0b1111111111 - - self.sync.cxp_gtx_rx += [ - If(self.gtx.comma_det.check_ps.o, - aligned.eq(0), - ).Elif((gtx.comma_det.data[:10] == comma) | (gtx.comma_det.data[:10] == comma_n), - aligned.eq(1), - ), - - rx1cnt.eq(reduce(add, [gtx.comma_det.data[i] for i in range(10)])), - If(self.gtx.comma_det.check_ps.o, - valid_data.eq(0) - ).Elif((rx1cnt == 4) | (rx1cnt == 5) | (rx1cnt == 6), - valid_data.eq(1) - ), - ] - # DEBUG: IO SMA & PMOD self.specials += [ Instance("OBUF", i_I=gtx.rxoutclk, o_O=debug_sma.p_tx), Instance("OBUF", i_I=gtx.cd_cxp_gtx_tx.clk, o_O=debug_sma.n_rx), # pmod 0-7 pin - Instance("OBUF", i_I=gtx.comma_det.aligner_en_rxclk, o_O=pmod_pads[0]), - Instance("OBUF", i_I=gtx.comma_det.rxinit_done, o_O=pmod_pads[1]), - Instance("OBUF", i_I=gtx.comma_det.restart, o_O=pmod_pads[2]), - Instance("OBUF", i_I=aligned, o_O=pmod_pads[3]), - Instance("OBUF", i_I=gtx.comma_det.ready, o_O=pmod_pads[4]), - Instance("OBUF", i_I=gtx.comma_det.check_ps.o, o_O=pmod_pads[5]), - Instance("OBUF", i_I=valid_data, o_O=pmod_pads[6]), - # Instance("OBUF", i_I=, o_O=pmod_pads[7]), + Instance("OBUF", i_I=gtx.comma_det.rxinit_done, o_O=pmod_pads[0]), + Instance("OBUF", i_I=gtx.comma_det.restart, o_O=pmod_pads[1]), + Instance("OBUF", i_I=gtx.comma_det.aligner_en_rxclk, o_O=pmod_pads[2]), + Instance("OBUF", i_I=gtx.comma_det.check_reset, o_O=pmod_pads[3]), + Instance("OBUF", i_I=gtx.comma_det.comma_aligned, o_O=pmod_pads[4]), + Instance("OBUF", i_I=gtx.comma_det.comma_seen, o_O=pmod_pads[5]), + Instance("OBUF", i_I=gtx.comma_det.has_error, o_O=pmod_pads[6]), + Instance("OBUF", i_I=gtx.comma_det.ready, o_O=pmod_pads[7]), # Instance("OBUF", i_I=gtx.dclk, o_O=pmod_pads[0]), # Instance("OBUF", i_I=gtx.den, o_O=pmod_pads[1]), @@ -200,8 +176,6 @@ class CXP_DownConn(Module, AutoCSR): ] # DEBUG: datain - counter_max = 2 - counter = Signal(max=counter_max) self.data_0 = CSRStorage(8) self.data_1 = CSRStorage(8) @@ -222,31 +196,34 @@ class CXP_DownConn(Module, AutoCSR): self.decoded_k_1 = CSRStatus() self.sync.cxp_gtx_tx += [ - If(counter == 0, - self.gtx.encoder.d[0].eq(self.data_0.storage), - self.gtx.encoder.k[0].eq(self.control_bit_0.storage), - self.gtx.encoder.d[1].eq(self.data_1.storage), - self.gtx.encoder.k[1].eq(self.control_bit_1.storage), - counter.eq(counter+1), - ).Elif(counter == 1, - self.gtx.encoder.d[0].eq(self.data_2.storage), - self.gtx.encoder.k[0].eq(self.control_bit_2.storage), - self.gtx.encoder.d[1].eq(self.data_3.storage), - self.gtx.encoder.k[1].eq(self.control_bit_3.storage), - counter.eq(0), - ), + self.gtx.encoder.d[0].eq(0xBC), + self.gtx.encoder.k[0].eq(1), + self.gtx.encoder.d[1].eq(0x3C), + self.gtx.encoder.k[1].eq(1), + self.gtx.encoder.d[2].eq(0x3C), + self.gtx.encoder.k[2].eq(1), + self.gtx.encoder.d[3].eq(0xB5), + self.gtx.encoder.k[3].eq(0), + self.encoded_0.status.eq(self.gtx.encoder.output[0]), self.encoded_1.status.eq(self.gtx.encoder.output[1]), ] + # keep it odd, so it will show data[n] where n is odd + stb_timer = Signal(reset=10, max=11) self.sync.cxp_gtx_rx += [ - self.rxdata_0.status.eq(self.gtx.decoders[0].input), - self.decoded_data_0.status.eq(self.gtx.decoders[0].d), - self.decoded_k_0.status.eq(self.gtx.decoders[0].k), + If(stb_timer == 0, + self.rxdata_0.status.eq(self.gtx.decoders[0].input), + self.decoded_data_0.status.eq(self.gtx.decoders[0].d), + self.decoded_k_0.status.eq(self.gtx.decoders[0].k), - self.rxdata_1.status.eq(self.gtx.decoders[1].input), - self.decoded_data_1.status.eq(self.gtx.decoders[1].d), - self.decoded_k_1.status.eq(self.gtx.decoders[1].k), + self.rxdata_1.status.eq(self.gtx.decoders[1].input), + self.decoded_data_1.status.eq(self.gtx.decoders[1].d), + self.decoded_k_1.status.eq(self.gtx.decoders[1].k), + stb_timer.eq(stb_timer.reset), + ).Else( + stb_timer.eq(stb_timer - 1), + ) ] @@ -295,7 +272,7 @@ class QPLL(Module): # refclk_div = 2 # self.Xxout_div = 2 - self.tx_usrclk_freq = (sys_clk_freq*fbdiv_real/self.Xxout_div)/20 + self.tx_usrclk_freq = (sys_clk_freq*fbdiv_real/self.Xxout_div)/40 self.specials += [ Instance("GTXE2_COMMON", @@ -352,7 +329,7 @@ class QPLL(Module): # Warning: Xilinx transceivers are LSB first, and comma needs to be flipped # compared to the usual 8b10b binary representation. class Comma_Detector(Module): - def __init__(self, comma, check_period=150_000): + def __init__(self, comma, check_period=1_000_000): self.data = Signal(20) self.rxinit_done = Signal() @@ -368,85 +345,205 @@ class Comma_Detector(Module): # - UG476 (v1.12.1) p.228 # The validity of data & comma are checked externally - aligned = Signal() - valid_data = Signal() - aligned_rxclk = Signal() - valid_data_rxclk = Signal() - rx1cnt = Signal(max=11) - self.specials += [ - MultiReg(aligned_rxclk, aligned), - MultiReg(valid_data_rxclk, valid_data), - ] - self.submodules.check_ps = check_ps = PulseSynchronizer("sys", "cxp_gtx_rx") + # aligned = Signal() + # valid_data = Signal() + # aligned_rxclk = Signal() + # valid_data_rxclk = Signal() + # rx1cnt = Signal(max=11) + # self.specials += [ + # MultiReg(aligned_rxclk, aligned), + # MultiReg(valid_data_rxclk, valid_data), + # ] + # self.submodules.check_reset = check_reset = PulseSynchronizer("sys", "cxp_gtx_rx") - comma_n = ~comma & 0b1111111111 - self.sync.cxp_gtx_rx += [ - If(check_ps.o, - aligned_rxclk.eq(0), - ).Elif((self.data[:10] == comma) | (self.data[:10] == comma_n), - aligned_rxclk.eq(1), - ), + # comma_n = ~comma & 0b1111111111 + # self.sync.cxp_gtx_rx += [ + # If(check_reset.o, + # aligned_rxclk.eq(0), + # ).Elif((self.data[:10] == comma) | (self.data[:10] == comma_n), + # aligned_rxclk.eq(1), + # ), - rx1cnt.eq(reduce(add, [self.data[i] for i in range(10)])), - If(check_ps.o, - valid_data_rxclk.eq(0), - ).Elif((rx1cnt == 4) | (rx1cnt == 5) | (rx1cnt == 6), - valid_data_rxclk.eq(1), - ), - ] + # rx1cnt.eq(reduce(add, [self.data[i] for i in range(10)])), + # If(check_reset.o, + # valid_data_rxclk.eq(0), + # ).Elif((rx1cnt == 4) | (rx1cnt == 5) | (rx1cnt == 6), + # valid_data_rxclk.eq(1), + # ), + # ] + + # self.aligned = Signal() + # self.valid_data = Signal() + # self.comb +=[ + # self.aligned.eq(aligned), + # self.valid_data.eq(valid_data) + # ] check_counter = Signal(reset=check_period-1, max=check_period) - check = Signal() + # check = Signal() self.sync += [ - check.eq(0), - If(check_counter == 0, - check_counter.eq(check_counter.reset), - check.eq(1), - ).Else( - check_counter.eq(check_counter - 1), + self.restart.eq(0), + # check.eq(0), + # check_reset.i.eq(0), + If(~self.ready, + If(check_counter == 0, + check_counter.eq(check_counter.reset), + # check.eq(1), + self.restart.eq(1), + # check_reset.i.eq(1), + ).Else( + check_counter.eq(check_counter - 1), + ) ) ] - self.submodules.fsm = fsm = FSM(reset_state="WAIT_COMMA_ALIGN") + # WIP: - aligner_en = Signal() - self.specials += MultiReg(aligner_en, self.aligner_en_rxclk, odomain="cxp_gtx_rx") - fsm.act("WAIT_COMMA_ALIGN", - aligner_en.eq(1), + comma_n = ~comma & 0b1111111111 + + has_error = Signal() + comma_aligned = Signal() + comma_seen = Signal() + error_seen = Signal() + one_counts = Signal(max=11) + + counter_period = 5000 + counter = Signal(reset=counter_period-1, max=counter_period) + check_reset = Signal() + check = Signal() + self.sync.cxp_gtx_rx += [ + check.eq(0), + If(counter == 0, + counter.eq(counter.reset), + check.eq(1), + ).Else( + counter.eq(counter - 1), + + ), + + + has_error.eq(0), + one_counts.eq(reduce(add, [self.data[i] for i in range(10, 20)])), + If((one_counts != 4) & (one_counts != 5) & (one_counts != 6), + has_error.eq(1) + ), + + comma_aligned.eq(0), + If((self.data[:10] == comma) | (self.data[:10] == comma_n), + comma_aligned.eq(1) + ), + + # signal that need to be manually cleared + If(check_reset, + comma_seen.eq(0), + ).Elif((self.data[:10] == comma) | (self.data[:10] == comma_n), + comma_seen.eq(1) + ), + + one_counts.eq(reduce(add, [self.data[i] for i in range(10, 20)])), + If(check_reset, + error_seen.eq(0), + ).Elif((one_counts != 4) & (one_counts != 5) & (one_counts != 6), + error_seen.eq(1), + ), + ] + + # DEBUG: expose signal + self.check_reset = Signal() + self.comma_aligned = Signal() + self.comma_seen = Signal() + self.has_error = Signal() + self.error_seen = Signal() + self.comb +=[ + self.check_reset.eq(check_reset), + self.comma_aligned.eq(comma_aligned), + self.comma_seen.eq(comma_seen), + self.has_error.eq(has_error), + self.error_seen.eq(error_seen), + ] + + self.submodules.rxfsm = rxfsm = ClockDomainsRenamer("cxp_gtx_rx")(FSM(reset_state="ALIGNING")) + + rxfsm.act("ALIGNING", + self.aligner_en_rxclk.eq(1), + If(comma_aligned & (~has_error), + check_reset.eq(1), + NextState("DOUBLE_CHECK"), + ) + ) + + rxfsm.act("DOUBLE_CHECK", If(check, - check_ps.i.eq(1), - If(aligned, - NextState("WAIT_VALID_DATA"), + check_reset.eq(1), + If(~error_seen, + NextState("READY"), ).Else( - self.restart.eq(1), + NextState("ALIGNING") ) ) ) - fsm.act("WAIT_VALID_DATA", - aligner_en.eq(1), + ready = Signal() + self.specials += MultiReg(ready, self.ready) + rxfsm.act("READY", + ready.eq(1), If(check, - check_ps.i.eq(1), - If(aligned & valid_data, - NextState("READY"), - ).Else( - self.restart.eq(1), - NextState("WAIT_COMMA_ALIGN"), + check_reset.eq(1), + If(~comma_seen, + NextState("ALIGNING"), ) ) ) - fsm.act("READY", - self.ready.eq(1), - If(check, - check_ps.i.eq(1), - If(~(aligned & valid_data), - self.restart.eq(1), - NextState("WAIT_COMMA_ALIGN"), - ) - ) - ) + + # self.submodules.fsm = fsm = FSM(reset_state="WAIT_RXINIT") + + # aligner_en = Signal() + # self.specials += MultiReg(aligner_en, self.aligner_en_rxclk, odomain="cxp_gtx_rx") + + # fsm.act("WAIT_RXINIT", + # If(self.rxinit_done, + # NextState("WAIT_COMMA_ALIGN"), + # ) + # ) + + # fsm.act("WAIT_COMMA_ALIGN", + # aligner_en.eq(1), + # If(check, + # check_reset.i.eq(1), + # If(aligned, + # NextState("WAIT_VALID_DATA"), + # ).Else( + # self.restart.eq(1), + # NextState("WAIT_RXINIT") + # ) + # ) + # ) + + # fsm.act("WAIT_VALID_DATA", + # aligner_en.eq(1), + # If(check, + # check_reset.i.eq(1), + # If(aligned & valid_data, + # NextState("READY"), + # ).Else( + # self.restart.eq(1), + # NextState("WAIT_RXINIT"), + # ) + # ) + # ) + + # fsm.act("READY", + # self.ready.eq(1), + # If(check, + # check_reset.i.eq(1), + # If(~(aligned & valid_data), + # self.restart.eq(1), + # NextState("WAIT_RXINIT"), + # ) + # ) + # ) @@ -463,7 +560,7 @@ class GTX(Module): # linerate = USRCLK * datawidth pll_fbout_mult = 8 - txusr_pll_div = pll_fbout_mult*sys_clk_freq/qpll.tx_usrclk_freq # 20 is datawidth + txusr_pll_div = pll_fbout_mult*sys_clk_freq/qpll.tx_usrclk_freq self.tx_restart = Signal() self.rx_restart = Signal() @@ -484,9 +581,9 @@ class GTX(Module): self.dout = Signal(16) self.dready = Signal() - self.submodules.encoder = ClockDomainsRenamer("cxp_gtx_tx")(Encoder(2, True)) + self.submodules.encoder = ClockDomainsRenamer("cxp_gtx_tx")(Encoder(4, True)) self.submodules.decoders = [ClockDomainsRenamer("cxp_gtx_rx")( - (Decoder(True))) for _ in range(2)] + (Decoder(True))) for _ in range(4)] # transceiver direct clock outputs @@ -509,8 +606,8 @@ class GTX(Module): rx_init.cplllock.eq(qpll.lock) ] - txdata = Signal(20) - rxdata = Signal(20) + txdata = Signal(40) + rxdata = Signal(40) comma_aligner_en = Signal() # Note: the following parameters were set after consulting AR45360 @@ -570,11 +667,11 @@ class GTX(Module): i_TXINHIBIT=~self.txenable, # TX data - p_TX_DATA_WIDTH=20, - p_TX_INT_DATAWIDTH=0, - i_TXCHARDISPMODE=Cat(txdata[9], txdata[19]), - i_TXCHARDISPVAL=Cat(txdata[8], txdata[18]), - i_TXDATA=Cat(txdata[:8], txdata[10:18]), + p_TX_DATA_WIDTH=40, + p_TX_INT_DATAWIDTH=1, + i_TXCHARDISPMODE=Cat(txdata[9], txdata[19], txdata[29], txdata[39]), + i_TXCHARDISPVAL=Cat(txdata[8], txdata[18], txdata[28], txdata[38]), + i_TXDATA=Cat(txdata[:8], txdata[10:18], txdata[20:28], txdata[30:38]), i_TXUSRCLK=ClockSignal("cxp_gtx_tx"), i_TXUSRCLK2=ClockSignal("cxp_gtx_tx"), @@ -633,27 +730,28 @@ class GTX(Module): p_CLK_COR_SEQ_2_ENABLE=0b1111, # RX data - p_RX_DATA_WIDTH=20, - p_RX_INT_DATAWIDTH=0, - o_RXDISPERR=Cat(rxdata[9], rxdata[19]), - o_RXCHARISK=Cat(rxdata[8], rxdata[18]), - o_RXDATA=Cat(rxdata[:8], rxdata[10:18]), + p_RX_DATA_WIDTH=40, + p_RX_INT_DATAWIDTH=1, + o_RXDISPERR=Cat(rxdata[9], rxdata[19], rxdata[29], rxdata[39]), + o_RXCHARISK=Cat(rxdata[8], rxdata[18], rxdata[28], rxdata[38]), + o_RXDATA=Cat(rxdata[:8], rxdata[10:18], rxdata[20:28], rxdata[30:38]), # RX Byte and Word Alignment Attributes p_ALIGN_COMMA_DOUBLE="FALSE", p_ALIGN_COMMA_ENABLE=0b1111111111, - p_ALIGN_COMMA_WORD=2, # align comma to rxdata[:10] only + p_ALIGN_COMMA_WORD=4, # align comma to rxdata[:10] only p_ALIGN_MCOMMA_DET="TRUE", p_ALIGN_MCOMMA_VALUE=0b1010000011, p_ALIGN_PCOMMA_DET="TRUE", p_ALIGN_PCOMMA_VALUE=0b0101111100, - p_SHOW_REALIGN_COMMA="FALSE", + p_SHOW_REALIGN_COMMA="TRUE", p_RXSLIDE_AUTO_WAIT=7, p_RXSLIDE_MODE="OFF", p_RX_SIG_VALID_DLY=10, i_RXPCOMMAALIGNEN=comma_aligner_en, i_RXMCOMMAALIGNEN=comma_aligner_en, i_RXCOMMADETEN=1, + i_RXSLIDE=0, # RX 8B/10B Decoder Attributes p_RX_DISPERR_SEQ_MATCH="FALSE", @@ -758,7 +856,7 @@ class GTX(Module): p_CLKFBOUT_MULT=pll_fbout_mult, p_DIVCLK_DIVIDE=1, i_CLKFBIN=txpll_fb_clk, o_CLKFBOUT=txpll_fb_clk, - # frequency = linerate/20 + # frequency = linerate/40 p_CLKOUT0_DIVIDE=txusr_pll_div, p_CLKOUT0_PHASE=0.0, o_CLKOUT0=txpll_clkout, # Dynamic Reconfiguration Ports @@ -784,9 +882,11 @@ class GTX(Module): ] self.comb += [ - txdata.eq(Cat(self.encoder.output[0], self.encoder.output[1])), + txdata.eq(Cat(self.encoder.output[0], self.encoder.output[1], self.encoder.output[2], self.encoder.output[3])), self.decoders[0].input.eq(rxdata[:10]), - self.decoders[1].input.eq(rxdata[10:]) + self.decoders[1].input.eq(rxdata[10:20]), + self.decoders[2].input.eq(rxdata[20:30]), + self.decoders[3].input.eq(rxdata[30:]), ]