From ccd3e6618aa3ab9bc1aeeada1c8e46133b50aa2e Mon Sep 17 00:00:00 2001 From: morgan Date: Mon, 29 Jul 2024 17:22:32 +0800 Subject: [PATCH] cxp downconn: replace QPLL with CPLL cxp downconn: add QPLL with its reset CSR cxp downconn: set QPLL as CLK source for GTX cxp downconn: remove PLL_reset signal from tx_init to prevent race condition cxp downconn: rename cxp_gtx to GTX --- src/gateware/cxp_downconn.py | 172 ++++++++++++++++++++++++++--------- 1 file changed, 131 insertions(+), 41 deletions(-) diff --git a/src/gateware/cxp_downconn.py b/src/gateware/cxp_downconn.py index 962560e..228c1a2 100644 --- a/src/gateware/cxp_downconn.py +++ b/src/gateware/cxp_downconn.py @@ -24,17 +24,29 @@ class CXP_DownConn(Module, AutoCSR): self.rxinit_phaligndone = CSRStatus() self.rx_ready = CSRStatus() + self.qpll_reset = CSR() + self.qpll_locked = CSRStatus() + # # # - # TODO: QPLL (GTXE2_COMMON) here - # CPLL too slow for 12.5Gbps :( + self.submodules.qpll = QPLL(refclk, sys_clk_freq) # single & master tx_mode can lock with rx in loopback - self.submodules.gtx = CXP_GTX(refclk, pads, sys_clk_freq, tx_mode="single", rx_mode="single") + self.submodules.gtx = GTX(refclk, self.qpll, pads, sys_clk_freq, tx_mode="single", rx_mode="single") + + # TEST: txusrclk alignment + # 1) use GTREFCLK with TXSYSCLKSEL = 0b10 -> still inconsistant + # 2) tie qpllPDEN with ~qpll.reset, -> inconsistant + # 3) seems like tx_init gtxXreset fall too soon (cplllock hold hi too long) <--- this a cross clk domain issue :< - self.comb += [ + self.sync += [ + # PLL + self.qpll.reset.eq(self.qpll_reset.re), + self.qpll_locked.status.eq(self.qpll.lock), + + # GTX self.txinit_phaligndone.status.eq(self.gtx.tx_init.Xxphaligndone), - # self.rxinit_phaligndone.status.eq(self.gtx.rx_init.Xxphaligndone), + self.rxinit_phaligndone.status.eq(self.gtx.rx_init.Xxphaligndone), self.rx_ready.status.eq(self.gtx.rx_ready), self.gtx.txenable.eq(self.txenable.storage[0]), @@ -52,7 +64,17 @@ class CXP_DownConn(Module, AutoCSR): # DEBUG:SMA self.specials += [ Instance("OBUF", i_I=self.gtx.rxoutclk, o_O=debug_sma.p_tx), - Instance("OBUF", i_I=self.gtx.cd_cxp_gtx_tx.clk, o_O=debug_sma.n_rx) + Instance("OBUF", i_I=self.gtx.cd_cxp_gtx_tx.clk, o_O=debug_sma.n_rx), + + # pmod 0-7 pin + Instance("OBUF", i_I=self.qpll.lock, o_O=pmod_pads[0]), + Instance("OBUF", i_I=self.qpll.reset, o_O=pmod_pads[1]), + Instance("OBUF", i_I=self.gtx.tx_init.gtXxreset, o_O=pmod_pads[2]), + # Instance("OBUF", i_I=, o_O=pmod_pads[3]), + # Instance("OBUF", i_I=, o_O=pmod_pads[4]), + # Instance("OBUF", i_I=, o_O=pmod_pads[5]), + # Instance("OBUF", i_I=, o_O=pmod_pads[6]), + # Instance("OBUF", i_I=, o_O=pmod_pads[7]), ] # DEBUG: datain @@ -117,6 +139,88 @@ class CXP_DownConn(Module, AutoCSR): # checkout channel interfaces & drtio_gtx # checkout GTPTXPhaseAlignement for inspiration +class QPLL(Module): + def __init__(self, refclk, sys_clk_freq): + self.clk = Signal() + self.refclk = Signal() + self.lock = Signal() + self.reset = Signal() + + # # # + + # WARNING: VCO cannot do 12.5GHz on ZC706 + # VCO freq = sys*qpll_fbdiv + # PLL output = VCO/2 + qpll_fbdiv = 0b0100100000 + qpll_fbdiv_ratio = 1 + + fbdiv_real = 80 + refclk_div = 1 + self.Xxout_div = 4 + + self.tx_usrclk_freq = (sys_clk_freq*fbdiv_real/self.Xxout_div)/20 + + # QPLL reset + pll_reset_cycles = ceil(sys_clk_freq/125e6) + pll_reset_timer = WaitTimer(pll_reset_cycles) + self.submodules += pll_reset_timer + + reset = Signal() + startup_fsm = FSM(reset_state="IDLE") + self.submodules += startup_fsm + + startup_fsm.act("IDLE", + If(self.reset, NextState("RESET_PLL")) + ) + startup_fsm.act("RESET_PLL", + reset.eq(1), + pll_reset_timer.wait.eq(1), + If(pll_reset_timer.done, NextState("IDLE")) + ) + + self.specials += [ + Instance("GTXE2_COMMON", + i_QPLLREFCLKSEL=0b001, + i_GTREFCLK0=refclk, + + i_QPLLPD=0, + i_QPLLRESET=reset, + i_QPLLLOCKEN=1, + o_QPLLLOCK=self.lock, + o_QPLLOUTCLK=self.clk, + o_QPLLOUTREFCLK=self.refclk, + + # See UG476 (v1.12.1) Table 2-16 + p_QPLL_FBDIV=qpll_fbdiv, + p_QPLL_FBDIV_RATIO=qpll_fbdiv_ratio, + p_QPLL_REFCLK_DIV=refclk_div, + + # From 7 Series FPGAs Transceivers Wizard + p_BIAS_CFG=0x0000040000001000, + p_COMMON_CFG=0x00000000, + p_QPLL_CFG=0x0680181, + p_QPLL_CLKOUT_CFG=0b0000, + p_QPLL_COARSE_FREQ_OVRD=0b010000, + p_QPLL_COARSE_FREQ_OVRD_EN=0b0, + p_QPLL_CP=0b0000011111, + p_QPLL_CP_MONITOR_EN=0b0, + p_QPLL_DMONITOR_SEL=0b0, + p_QPLL_FBDIV_MONITOR_EN= 0b0, + p_QPLL_INIT_CFG=0x000006, + p_QPLL_LOCK_CFG=0x21E8, + p_QPLL_LPF=0b1111, + + # Reserved, values cannot be modified + i_BGBYPASSB=0b1, + i_BGMONITORENB=0b1, + i_BGPDB=0b1, + i_BGRCALOVRD=0b11111, + i_RCALENB=0b1, + i_QPLLRSVD1=0b0, + i_QPLLRSVD2=0b11111, + ) + ] + # Changes the phase of the transceiver RX clock to align the comma to # the LSBs of RXDATA, fixing the latency. # @@ -224,26 +328,20 @@ class CXP_BruteforceClockAligner(Module): -class CXP_GTX(Module): +class GTX(Module): # Settings: # * GTX reference clock @ 125MHz # * GTX data width = 20 # * GTX PLL frequency @ 3.125GHz # * GTX line rate (TX & RX) @ 3.125Gb/s # * GTX TX/RX USRCLK @ PLL/datawidth = 156MHz - def __init__(self, refclk, pads, sys_clk_freq, tx_mode="single", rx_mode="single"): + def __init__(self, refclk, qpll, pads, sys_clk_freq, tx_mode="single", rx_mode="single"): assert tx_mode in ["single", "master", "slave"] assert rx_mode in ["single", "master", "slave"] - cpll_div = 4 - cpll_div45 = 5 - refclk_div = 1 - Xxout_div = 2 - - # linerate = sys * cpll_mult - cpll_mult = 2 * cpll_div * cpll_div45 / (Xxout_div * refclk_div) + # linerate = USRCLK * datawidth pll_fbout_mult = 10 - txusr_pll_div = pll_fbout_mult*20/cpll_mult # 20 is datawidth + txusr_pll_div = pll_fbout_mult*sys_clk_freq/qpll.tx_usrclk_freq # 20 is datawidth self.rx_restart = Signal() self.tx_restart = Signal() @@ -262,16 +360,16 @@ class CXP_GTX(Module): # # # - cpllreset = Signal() - cplllock = Signal() # TX generates cxp_tx clock, init must be in system domain self.submodules.tx_init = tx_init = GTXInit(sys_clk_freq, False, mode=tx_mode) # RX receives restart commands from RTIO domain - self.submodules.rx_init = rx_init = ClockDomainsRenamer("cxp_gtx_tx")(GTXInit(sys_clk_freq, True, mode=rx_mode)) + self.submodules.rx_init = rx_init = ClockDomainsRenamer("cxp_gtx_tx")(GTXInit(qpll.tx_usrclk_freq, True, mode=rx_mode)) + # DEBUG: change back to cxp_gtx_tx once QPLL works + # self.submodules.rx_init = rx_init = GTXInit(sys_clk_freq, True, mode=rx_mode) + self.comb += [ - cpllreset.eq(tx_init.cpllreset), - tx_init.cplllock.eq(cplllock), - rx_init.cplllock.eq(cplllock) + tx_init.cplllock.eq(qpll.lock), + rx_init.cplllock.eq(qpll.lock) ] txdata = Signal(20) @@ -293,29 +391,21 @@ class CXP_GTX(Module): p_PD_TRANS_TIME_FROM_P2=0x3c, p_PD_TRANS_TIME_NONE_P2=0x3c, p_PD_TRANS_TIME_TO_P2=0x64, + i_CPLLPD=1, - # CPLL - p_CPLL_CFG=0xBC07DC, - p_CPLL_FBDIV=cpll_div, - p_CPLL_FBDIV_45=cpll_div45, - p_CPLL_REFCLK_DIV=refclk_div, - p_RXOUT_DIV=Xxout_div, - p_TXOUT_DIV=Xxout_div, - p_CPLL_INIT_CFG=0x00001E, - p_CPLL_LOCK_CFG=0x01E8, - i_CPLLRESET=cpllreset, - i_CPLLPD=cpllreset, - o_CPLLLOCK=cplllock, - i_CPLLLOCKEN=1, - i_CPLLREFCLKSEL=0b001, - i_TSTIN=2**20-1, - i_GTREFCLK0=refclk, + # QPLL + i_QPLLCLK=qpll.clk, + i_QPLLREFCLK=qpll.refclk, + p_RXOUT_DIV=qpll.Xxout_div, + p_TXOUT_DIV=qpll.Xxout_div, + i_RXSYSCLKSEL=0b11, # use QPLL & QPLL's REFCLK + i_TXSYSCLKSEL=0b11, # use QPLL & CPLL's REFCLK # TX clock p_TXBUF_EN="FALSE", p_TX_XCLK_SEL="TXUSR", o_TXOUTCLK=self.txoutclk, - i_TXSYSCLKSEL=0b00, + # i_TXSYSCLKSEL=0b00, i_TXOUTCLKSEL=0b11, # TX Startup/Reset @@ -386,7 +476,7 @@ class CXP_GTX(Module): # RX clock i_RXDDIEN=1, - i_RXSYSCLKSEL=0b00, + # i_RXSYSCLKSEL=0b00, i_RXOUTCLKSEL=0b010, o_RXOUTCLK=self.rxoutclk, i_RXUSRCLK=ClockSignal("cxp_gtx_rx"), @@ -531,7 +621,7 @@ class CXP_GTX(Module): # 125MHz: align <1s # 156.25MHz: align <15s # 250MHz: cannot align - clock_aligner = CXP_BruteforceClockAligner(0b0101111100, 1_000_000) + clock_aligner = CXP_BruteforceClockAligner(0b0101111100, 800_000) self.submodules += clock_aligner self.comb += [ clock_aligner.rxdata.eq(rxdata),