forked from M-Labs/artiq-zynq
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
This commit is contained in:
parent
563e06ca8d
commit
ccd3e6618a
|
@ -24,17 +24,29 @@ class CXP_DownConn(Module, AutoCSR):
|
||||||
self.rxinit_phaligndone = CSRStatus()
|
self.rxinit_phaligndone = CSRStatus()
|
||||||
self.rx_ready = CSRStatus()
|
self.rx_ready = CSRStatus()
|
||||||
|
|
||||||
|
self.qpll_reset = CSR()
|
||||||
|
self.qpll_locked = CSRStatus()
|
||||||
|
|
||||||
# # #
|
# # #
|
||||||
|
|
||||||
# TODO: QPLL (GTXE2_COMMON) here
|
self.submodules.qpll = QPLL(refclk, sys_clk_freq)
|
||||||
# CPLL too slow for 12.5Gbps :(
|
|
||||||
|
|
||||||
# single & master tx_mode can lock with rx in loopback
|
# 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")
|
||||||
|
|
||||||
self.comb += [
|
# 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.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.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.rx_ready.status.eq(self.gtx.rx_ready),
|
||||||
|
|
||||||
self.gtx.txenable.eq(self.txenable.storage[0]),
|
self.gtx.txenable.eq(self.txenable.storage[0]),
|
||||||
|
@ -52,7 +64,17 @@ class CXP_DownConn(Module, AutoCSR):
|
||||||
# DEBUG:SMA
|
# DEBUG:SMA
|
||||||
self.specials += [
|
self.specials += [
|
||||||
Instance("OBUF", i_I=self.gtx.rxoutclk, o_O=debug_sma.p_tx),
|
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
|
# DEBUG: datain
|
||||||
|
@ -117,6 +139,88 @@ class CXP_DownConn(Module, AutoCSR):
|
||||||
# checkout channel interfaces & drtio_gtx
|
# checkout channel interfaces & drtio_gtx
|
||||||
# checkout GTPTXPhaseAlignement for inspiration
|
# 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
|
# Changes the phase of the transceiver RX clock to align the comma to
|
||||||
# the LSBs of RXDATA, fixing the latency.
|
# the LSBs of RXDATA, fixing the latency.
|
||||||
#
|
#
|
||||||
|
@ -224,26 +328,20 @@ class CXP_BruteforceClockAligner(Module):
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class CXP_GTX(Module):
|
class GTX(Module):
|
||||||
# Settings:
|
# Settings:
|
||||||
# * GTX reference clock @ 125MHz
|
# * GTX reference clock @ 125MHz
|
||||||
# * GTX data width = 20
|
# * GTX data width = 20
|
||||||
# * GTX PLL frequency @ 3.125GHz
|
# * GTX PLL frequency @ 3.125GHz
|
||||||
# * GTX line rate (TX & RX) @ 3.125Gb/s
|
# * GTX line rate (TX & RX) @ 3.125Gb/s
|
||||||
# * GTX TX/RX USRCLK @ PLL/datawidth = 156MHz
|
# * 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 tx_mode in ["single", "master", "slave"]
|
||||||
assert rx_mode in ["single", "master", "slave"]
|
assert rx_mode in ["single", "master", "slave"]
|
||||||
|
|
||||||
cpll_div = 4
|
# linerate = USRCLK * datawidth
|
||||||
cpll_div45 = 5
|
|
||||||
refclk_div = 1
|
|
||||||
Xxout_div = 2
|
|
||||||
|
|
||||||
# linerate = sys * cpll_mult
|
|
||||||
cpll_mult = 2 * cpll_div * cpll_div45 / (Xxout_div * refclk_div)
|
|
||||||
pll_fbout_mult = 10
|
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.rx_restart = Signal()
|
||||||
self.tx_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
|
# 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.tx_init = tx_init = GTXInit(sys_clk_freq, False, mode=tx_mode)
|
||||||
# RX receives restart commands from RTIO domain
|
# 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 += [
|
self.comb += [
|
||||||
cpllreset.eq(tx_init.cpllreset),
|
tx_init.cplllock.eq(qpll.lock),
|
||||||
tx_init.cplllock.eq(cplllock),
|
rx_init.cplllock.eq(qpll.lock)
|
||||||
rx_init.cplllock.eq(cplllock)
|
|
||||||
]
|
]
|
||||||
|
|
||||||
txdata = Signal(20)
|
txdata = Signal(20)
|
||||||
|
@ -293,29 +391,21 @@ class CXP_GTX(Module):
|
||||||
p_PD_TRANS_TIME_FROM_P2=0x3c,
|
p_PD_TRANS_TIME_FROM_P2=0x3c,
|
||||||
p_PD_TRANS_TIME_NONE_P2=0x3c,
|
p_PD_TRANS_TIME_NONE_P2=0x3c,
|
||||||
p_PD_TRANS_TIME_TO_P2=0x64,
|
p_PD_TRANS_TIME_TO_P2=0x64,
|
||||||
|
i_CPLLPD=1,
|
||||||
|
|
||||||
# CPLL
|
# QPLL
|
||||||
p_CPLL_CFG=0xBC07DC,
|
i_QPLLCLK=qpll.clk,
|
||||||
p_CPLL_FBDIV=cpll_div,
|
i_QPLLREFCLK=qpll.refclk,
|
||||||
p_CPLL_FBDIV_45=cpll_div45,
|
p_RXOUT_DIV=qpll.Xxout_div,
|
||||||
p_CPLL_REFCLK_DIV=refclk_div,
|
p_TXOUT_DIV=qpll.Xxout_div,
|
||||||
p_RXOUT_DIV=Xxout_div,
|
i_RXSYSCLKSEL=0b11, # use QPLL & QPLL's REFCLK
|
||||||
p_TXOUT_DIV=Xxout_div,
|
i_TXSYSCLKSEL=0b11, # use QPLL & CPLL's REFCLK
|
||||||
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,
|
|
||||||
|
|
||||||
# TX clock
|
# TX clock
|
||||||
p_TXBUF_EN="FALSE",
|
p_TXBUF_EN="FALSE",
|
||||||
p_TX_XCLK_SEL="TXUSR",
|
p_TX_XCLK_SEL="TXUSR",
|
||||||
o_TXOUTCLK=self.txoutclk,
|
o_TXOUTCLK=self.txoutclk,
|
||||||
i_TXSYSCLKSEL=0b00,
|
# i_TXSYSCLKSEL=0b00,
|
||||||
i_TXOUTCLKSEL=0b11,
|
i_TXOUTCLKSEL=0b11,
|
||||||
|
|
||||||
# TX Startup/Reset
|
# TX Startup/Reset
|
||||||
|
@ -386,7 +476,7 @@ class CXP_GTX(Module):
|
||||||
|
|
||||||
# RX clock
|
# RX clock
|
||||||
i_RXDDIEN=1,
|
i_RXDDIEN=1,
|
||||||
i_RXSYSCLKSEL=0b00,
|
# i_RXSYSCLKSEL=0b00,
|
||||||
i_RXOUTCLKSEL=0b010,
|
i_RXOUTCLKSEL=0b010,
|
||||||
o_RXOUTCLK=self.rxoutclk,
|
o_RXOUTCLK=self.rxoutclk,
|
||||||
i_RXUSRCLK=ClockSignal("cxp_gtx_rx"),
|
i_RXUSRCLK=ClockSignal("cxp_gtx_rx"),
|
||||||
|
@ -531,7 +621,7 @@ class CXP_GTX(Module):
|
||||||
# 125MHz: align <1s
|
# 125MHz: align <1s
|
||||||
# 156.25MHz: align <15s
|
# 156.25MHz: align <15s
|
||||||
# 250MHz: cannot align
|
# 250MHz: cannot align
|
||||||
clock_aligner = CXP_BruteforceClockAligner(0b0101111100, 1_000_000)
|
clock_aligner = CXP_BruteforceClockAligner(0b0101111100, 800_000)
|
||||||
self.submodules += clock_aligner
|
self.submodules += clock_aligner
|
||||||
self.comb += [
|
self.comb += [
|
||||||
clock_aligner.rxdata.eq(rxdata),
|
clock_aligner.rxdata.eq(rxdata),
|
||||||
|
|
Loading…
Reference in New Issue