diff --git a/src/gateware/cxp_downconn.py b/src/gateware/cxp_downconn.py index dad5e96..a6c9cf6 100644 --- a/src/gateware/cxp_downconn.py +++ b/src/gateware/cxp_downconn.py @@ -36,7 +36,7 @@ class CXP_RXPHYs(Module, AutoCSR): rx_mode = "single" else: rx_mode = "master" if i == master else "slave" - rx = Receiver(qpll, pad, sys_clk_freq, "single", rx_mode, debug_sma, pmod_pads) + rx = Receiver(qpll, pad, sys_clk_freq, rx_mode) self.phys.append(rx) setattr(self.submodules, "rx"+str(i), rx) @@ -52,8 +52,8 @@ class CXP_RXPHYs(Module, AutoCSR): self.submodules.rx_phase_alignment = GTXInitPhaseAlignment([rx_phy.gtx.rx_init for rx_phy in self.phys]) class Receiver(Module): - def __init__(self, qpll, pad, sys_clk_freq, tx_mode, rx_mode, debug_sma, pmod_pads): - self.submodules.gtx = gtx = GTX(qpll, pad, sys_clk_freq, tx_mode, rx_mode) + def __init__(self, qpll, pad, sys_clk_freq, rx_mode): + self.submodules.gtx = gtx = GTX(qpll, pad, sys_clk_freq, None, rx_mode) self.source = stream.Endpoint(word_layout) @@ -294,13 +294,21 @@ class Comma_Aligner(Module): class GTX(Module): - def __init__(self, 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"] + """ + A reconfigurable linerate GTX with QPLL + Designed for 12.5, 10, 6.25, 5, 3.125, 2.5, 1.25Gpbs + + To change the linerate: + 1) Change the VCO frequency + - 12.5, 6.25, 3.125Gbps: QPLL VCO @ 12.5GHz, + - 10, 6.25, 5, 2.5, 1.25Gbps: QPLL VCO @ 10GHz + 2) Update the xXOUT_DIV and TXUSRCLK frequency if using tx + 3) Reset the entire rx and tx + """ + def __init__(self, qpll, pads, sys_clk_freq, tx_mode="single", rx_mode="single"): + assert tx_mode in ["single", "master", "slave", None] + assert rx_mode in ["single", "master", "slave", None] - # linerate = USRCLK * datawidth - pll_fbout_mult = 8 - txusr_pll_div = pll_fbout_mult*sys_clk_freq/qpll.tx_usrclk_freq self.tx_restart = Signal() self.rx_manual_restart = Signal() @@ -318,9 +326,6 @@ class GTX(Module): self.dout = Signal(16) self.dready = Signal() - self.submodules.encoder = ClockDomainsRenamer("cxp_gtx_tx")(Encoder(4, True)) - self.submodules.decoders = [ClockDomainsRenamer("cxp_gtx_rx")( - (Decoder(True))) for _ in range(4)] # transceiver direct clock outputs @@ -330,18 +335,29 @@ class GTX(Module): # # # - # 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) - self.submodules.rx_init = rx_init = GTXInit(sys_clk_freq, True, mode=rx_mode) - - self.comb += [ - tx_init.cplllock.eq(qpll.lock), - rx_init.cplllock.eq(qpll.lock) - ] - txdata = Signal(40) rxdata = Signal(40) + if tx_mode: + self.submodules.tx_init = tx_init = GTXInit(sys_clk_freq, False, mode=tx_mode) + self.comb += tx_init.cplllock.eq(qpll.lock), + + self.submodules.encoder = ClockDomainsRenamer("cxp_gtx_tx")(Encoder(4, True)) + self.comb += txdata.eq(Cat(self.encoder.output[0], self.encoder.output[1], self.encoder.output[2], self.encoder.output[3])), + + if rx_mode: + self.submodules.rx_init = rx_init = GTXInit(sys_clk_freq, True, mode=rx_mode) + self.comb += rx_init.cplllock.eq(qpll.lock) + + self.submodules.decoders = [ClockDomainsRenamer("cxp_gtx_rx")( + (Decoder(True))) for _ in range(4)] + self.comb += [ + self.decoders[0].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:]), + ] + comma_aligned = Signal() comma_realigned = Signal() comma_det = Signal() @@ -383,17 +399,17 @@ class GTX(Module): # TX Startup/Reset i_TXPHDLYRESET=0, i_TXDLYBYPASS=0, - i_TXPHALIGNEN=1 if tx_mode != "single" else 0, - i_GTTXRESET=tx_init.gtXxreset, - o_TXRESETDONE=tx_init.Xxresetdone, - i_TXDLYSRESET=tx_init.Xxdlysreset, - o_TXDLYSRESETDONE=tx_init.Xxdlysresetdone, - i_TXPHINIT=tx_init.txphinit if tx_mode != "single" else 0, - o_TXPHINITDONE=tx_init.txphinitdone if tx_mode != "single" else Signal(), - i_TXPHALIGN=tx_init.Xxphalign if tx_mode != "single" else 0, - i_TXDLYEN=tx_init.Xxdlyen if tx_mode != "single" else 0, - o_TXPHALIGNDONE=tx_init.Xxphaligndone, - i_TXUSERRDY=tx_init.Xxuserrdy, + i_TXPHALIGNEN=1 if tx_mode in ["master", "slave"] else 0, + i_GTTXRESET=tx_init.gtXxreset if tx_mode else 0, + o_TXRESETDONE=tx_init.Xxresetdone if tx_mode else 0, + i_TXDLYSRESET=tx_init.Xxdlysreset if tx_mode else 0, + o_TXDLYSRESETDONE=tx_init.Xxdlysresetdone if tx_mode else 0, + i_TXPHINIT=tx_init.txphinit if tx_mode in ["master", "slave"] else 0, + o_TXPHINITDONE=tx_init.txphinitdone if tx_mode in ["master", "slave"] else Signal(), + i_TXPHALIGN=tx_init.Xxphalign if tx_mode in ["master", "slave"] else 0, + i_TXDLYEN=tx_init.Xxdlyen if tx_mode in ["master", "slave"] else 0, + o_TXPHALIGNDONE=tx_init.Xxphaligndone if tx_mode else 0, + i_TXUSERRDY=tx_init.Xxuserrdy if tx_mode else 0, p_TXPMARESET_TIME=1, p_TXPCSRESET_TIME=1, i_TXINHIBIT=~self.txenable, @@ -404,8 +420,8 @@ class GTX(Module): 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"), + i_TXUSRCLK=ClockSignal("cxp_gtx_tx") if tx_mode else 0, + i_TXUSRCLK2=ClockSignal("cxp_gtx_tx") if tx_mode else 0, # TX electrical i_TXBUFDIFFCTRL=0b100, @@ -414,15 +430,15 @@ class GTX(Module): # RX Startup/Reset i_RXPHDLYRESET=0, i_RXDLYBYPASS=0, - i_RXPHALIGNEN=1 if rx_mode != "single" else 0, - i_GTRXRESET=rx_init.gtXxreset, - o_RXRESETDONE=rx_init.Xxresetdone, - i_RXDLYSRESET=rx_init.Xxdlysreset, - o_RXDLYSRESETDONE=rx_init.Xxdlysresetdone, - i_RXPHALIGN=rx_init.Xxphalign if rx_mode != "single" else 0, - i_RXDLYEN=rx_init.Xxdlyen if rx_mode != "single" else 0, - o_RXPHALIGNDONE=rx_init.Xxphaligndone, - i_RXUSERRDY=rx_init.Xxuserrdy, + i_RXPHALIGNEN=1 if rx_mode in ["master", "slave"] else 0, + i_GTRXRESET=rx_init.gtXxreset if rx_mode else 0, + o_RXRESETDONE=rx_init.Xxresetdone if rx_mode else 0, + i_RXDLYSRESET=rx_init.Xxdlysreset if rx_mode else 0, + o_RXDLYSRESETDONE=rx_init.Xxdlysresetdone if rx_mode else 0, + i_RXPHALIGN=rx_init.Xxphalign if rx_mode in ["master", "slave"] else 0, + i_RXDLYEN=rx_init.Xxdlyen if rx_mode in ["master", "slave"] else 0, + o_RXPHALIGNDONE=rx_init.Xxphaligndone if rx_mode else 0, + i_RXUSERRDY=rx_init.Xxuserrdy if rx_mode else 0, p_RXPMARESET_TIME=1, p_RXPCSRESET_TIME=1, @@ -454,8 +470,8 @@ class GTX(Module): # i_RXSYSCLKSEL=0b00, i_RXOUTCLKSEL=0b010, o_RXOUTCLK=self.rxoutclk, - i_RXUSRCLK=ClockSignal("cxp_gtx_rx"), - i_RXUSRCLK2=ClockSignal("cxp_gtx_rx"), + i_RXUSRCLK=ClockSignal("cxp_gtx_rx") if rx_mode else 0, + i_RXUSRCLK2=ClockSignal("cxp_gtx_rx") if rx_mode else 0, # RX Clock Correction Attributes p_CLK_CORRECT_USE="FALSE", @@ -545,7 +561,7 @@ class GTX(Module): o_DRPDO=self.dout, o_DRPRDY=self.dready, - # ! loopback for debugging + # Nearend Loopback i_LOOPBACK = self.loopback_mode, p_TX_LOOPBACK_DRIVE_HIZ = "FALSE", p_RXPRBS_ERR_LOOPBACK = 0b0, @@ -565,52 +581,58 @@ class GTX(Module): # TX clocking - # A PLL is used to generate the correct frequency for TXUSRCLK (UG476 Equation 3-1) - self.clock_domains.cd_cxp_gtx_tx = ClockDomain() - txpll_fb_clk = Signal() - txoutclk_buf = Signal() - txpll_clkout = Signal() + # As TX buffer is bypass, freq txoutclk_buf == refclk + # To match the require frequency TXUSRCLK = linerate/datewidth (UG476 Equation 3-1) + # A PLL need to be used to generate the correct frequency for TXUSRCLK + if tx_mode: + self.clock_domains.cd_cxp_gtx_tx = ClockDomain() + txpll_fb_clk = Signal() + txoutclk_buf = Signal() + txpll_clkout = Signal() - self.txpll_reset = Signal() - self.pll_daddr = Signal(7) - self.pll_dclk = Signal() - self.pll_den = Signal() - self.pll_din = Signal(16) - self.pll_dwen = Signal() + self.txpll_reset = Signal() + self.pll_daddr = Signal(7) + self.pll_dclk = Signal() + self.pll_den = Signal() + self.pll_din = Signal(16) + self.pll_dwen = Signal() - self.txpll_locked = Signal() - self.pll_dout = Signal(16) - self.pll_dready = Signal() - self.specials += [ - Instance("PLLE2_ADV", - p_BANDWIDTH="HIGH", - o_LOCKED=self.txpll_locked, - i_RST=self.txpll_reset, + self.txpll_locked = Signal() + self.pll_dout = Signal(16) + self.pll_dready = Signal() + + pll_fbout_mult = 8 + txusr_pll_div = pll_fbout_mult*sys_clk_freq/qpll.tx_usrclk_freq + self.specials += [ + Instance("PLLE2_ADV", + p_BANDWIDTH="HIGH", + o_LOCKED=self.txpll_locked, + i_RST=self.txpll_reset, - p_CLKIN1_PERIOD=1e9/sys_clk_freq, # ns - i_CLKIN1=txoutclk_buf, + p_CLKIN1_PERIOD=1e9/sys_clk_freq, # ns + i_CLKIN1=txoutclk_buf, - # VCO @ 1.25GHz - p_CLKFBOUT_MULT=pll_fbout_mult, p_DIVCLK_DIVIDE=1, - i_CLKFBIN=txpll_fb_clk, o_CLKFBOUT=txpll_fb_clk, + # VCO @ 1.25GHz + p_CLKFBOUT_MULT=pll_fbout_mult, p_DIVCLK_DIVIDE=1, + i_CLKFBIN=txpll_fb_clk, o_CLKFBOUT=txpll_fb_clk, - # frequency = linerate/40 - p_CLKOUT0_DIVIDE=txusr_pll_div, p_CLKOUT0_PHASE=0.0, o_CLKOUT0=txpll_clkout, + # frequency = linerate/40 + p_CLKOUT0_DIVIDE=txusr_pll_div, p_CLKOUT0_PHASE=0.0, o_CLKOUT0=txpll_clkout, - # Dynamic Reconfiguration Ports - i_DADDR = self.pll_daddr, - i_DCLK = self.pll_dclk, - i_DEN = self.pll_den, - i_DI = self.pll_din, - i_DWE = self.pll_dwen, - o_DO = self.pll_dout, - o_DRDY = self.pll_dready, - ), - Instance("BUFG", i_I=self.txoutclk, o_O=txoutclk_buf), - Instance("BUFG", i_I=txpll_clkout, o_O=self.cd_cxp_gtx_tx.clk), - AsyncResetSynchronizer(self.cd_cxp_gtx_tx, ~self.txpll_locked & ~tx_init.done) - ] - self.comb += tx_init.restart.eq(self.tx_restart) + # Dynamic Reconfiguration Ports + i_DADDR = self.pll_daddr, + i_DCLK = self.pll_dclk, + i_DEN = self.pll_den, + i_DI = self.pll_din, + i_DWE = self.pll_dwen, + o_DO = self.pll_dout, + o_DRDY = self.pll_dready, + ), + Instance("BUFG", i_I=self.txoutclk, o_O=txoutclk_buf), + Instance("BUFG", i_I=txpll_clkout, o_O=self.cd_cxp_gtx_tx.clk), + AsyncResetSynchronizer(self.cd_cxp_gtx_tx, ~self.txpll_locked & ~tx_init.done) + ] + self.comb += tx_init.restart.eq(self.tx_restart) # RX clocking # the CDR matches the required frequency for RXUSRCLK, no need for PLL @@ -624,15 +646,6 @@ class GTX(Module): self.comb += rx_init.restart.eq(self.rx_manual_restart) - # 8b10b Encoder/Decoder - self.comb += [ - 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:20]), - self.decoders[2].input.eq(rxdata[20:30]), - self.decoders[3].input.eq(rxdata[30:]), - ] - self.submodules.comma_aligner = comma_aligner = Comma_Aligner(0b0101111100) self.comb += [ comma_aligner.data.eq(rxdata),