forked from M-Labs/artiq-zynq
Compare commits
22 Commits
6c53447808
...
2aef11143a
Author | SHA1 | Date |
---|---|---|
morgan | 2aef11143a | |
morgan | 77bff39212 | |
morgan | 160fcc657a | |
morgan | bf1fd2d79b | |
morgan | 452c3cee64 | |
morgan | 4436dc930e | |
morgan | 5a40422f1d | |
morgan | e0369d2eb2 | |
morgan | 1e87428c68 | |
morgan | 0e4fb4cbfe | |
morgan | ccd3e6618a | |
morgan | 563e06ca8d | |
morgan | 57d6e9a669 | |
morgan | 1f0154d5b2 | |
morgan | a41e78d3d3 | |
morgan | 52f77a6331 | |
morgan | 1a210a010a | |
morgan | 398eb135c7 | |
morgan | 865434763d | |
morgan | 4ba1c5ef2e | |
morgan | faef1d2dff | |
morgan | 20e388d043 |
|
@ -2,131 +2,20 @@ from migen import *
|
||||||
from misoc.interconnect.csr import *
|
from misoc.interconnect.csr import *
|
||||||
from misoc.cores.liteeth_mini.mac.crc import LiteEthMACCRCEngine
|
from misoc.cores.liteeth_mini.mac.crc import LiteEthMACCRCEngine
|
||||||
|
|
||||||
from artiq.gateware.drtio.core import ChannelInterface
|
|
||||||
|
|
||||||
from cxp_downconn import CXP_DownConn
|
from cxp_downconn import CXP_DownConn
|
||||||
from cxp_upconn import CXP_UpConn
|
from cxp_upconn import CXP_UpConn
|
||||||
|
|
||||||
class CXP(Module, AutoCSR):
|
class CXP(Module, AutoCSR):
|
||||||
def __init__(self, refclk, pads, sys_clk_freq, debug_sma, pmod_pads):
|
def __init__(self, refclk, pads, sys_clk_freq, debug_sma, pmod_pads):
|
||||||
nchannels = len(pads)
|
|
||||||
self.rx_start_init = CSRStorage()
|
|
||||||
self.rx_restart = CSRStatus()
|
|
||||||
self.rx_bypass_clk_alignment = CSRStorage()
|
|
||||||
|
|
||||||
self.tx_start_init = CSRStorage()
|
|
||||||
self.tx_restart = CSRStorage()
|
|
||||||
|
|
||||||
self.loopback_mode = CSRStorage(3)
|
|
||||||
|
|
||||||
self.txinit_phaligndone = CSRStatus()
|
|
||||||
self.rxinit_phaligndone = CSRStatus()
|
|
||||||
self.rx_ready = CSRStatus()
|
|
||||||
|
|
||||||
self.data_0 = CSRStorage(8)
|
|
||||||
self.data_1 = CSRStorage(8)
|
|
||||||
self.control_bit_0 = CSRStorage()
|
|
||||||
self.control_bit_1 = CSRStorage()
|
|
||||||
self.encoded_0 = CSRStatus(10)
|
|
||||||
self.encoded_1 = CSRStatus(10)
|
|
||||||
|
|
||||||
self.rxdata_0 = CSRStatus(10)
|
|
||||||
self.rxdata_1 = CSRStatus(10)
|
|
||||||
self.decoded_data_0 = CSRStatus(8)
|
|
||||||
self.decoded_data_1 = CSRStatus(8)
|
|
||||||
self.decoded_k_0 = CSRStatus()
|
|
||||||
self.decoded_k_1 = CSRStatus()
|
|
||||||
|
|
||||||
# # #
|
|
||||||
|
|
||||||
self.submodules.crc = CXP_CRC(8)
|
self.submodules.crc = CXP_CRC(8)
|
||||||
|
|
||||||
# FIFOs with transmission priority
|
# FIFOs with transmission priority
|
||||||
# 0: Trigger packet
|
# 0: Trigger packet
|
||||||
# 1: IO acknowledgment for trigger packet
|
# 1: IO acknowledgment for trigger packet
|
||||||
# 2: All other packets
|
# 2: All other packets
|
||||||
|
|
||||||
self.submodules.upconn = CXP_UpConn(debug_sma, sys_clk_freq, pmod_pads)
|
self.submodules.upconn = CXP_UpConn(debug_sma, sys_clk_freq, pmod_pads)
|
||||||
|
|
||||||
|
self.submodules.downconn = CXP_DownConn(refclk, pads, sys_clk_freq, debug_sma, pmod_pads)
|
||||||
|
|
||||||
# single & master tx_mode can lock with rx in loopback
|
|
||||||
self.submodules.gtx = gtx = CXP_DownConn(refclk, pads, sys_clk_freq, tx_mode="single", rx_mode="single")
|
|
||||||
|
|
||||||
# DEBUG:loopback
|
|
||||||
self.sync += gtx.loopback_mode.eq(self.loopback_mode.storage)
|
|
||||||
|
|
||||||
# # ! debug sma
|
|
||||||
# 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)
|
|
||||||
# ]
|
|
||||||
|
|
||||||
self.comb += [
|
|
||||||
self.txinit_phaligndone.status.eq(self.gtx.tx_init.Xxphaligndone),
|
|
||||||
self.rxinit_phaligndone.status.eq(self.gtx.rx_init.Xxphaligndone),
|
|
||||||
self.rx_ready.status.eq(self.gtx.rx_ready),
|
|
||||||
]
|
|
||||||
|
|
||||||
self.sync.cxp_gtx_tx += [
|
|
||||||
self.gtx.encoder.d[0].eq(self.data_0.storage),
|
|
||||||
self.gtx.encoder.k[0].eq(self.control_bit_0.storage),
|
|
||||||
self.encoded_0.status.eq(self.gtx.encoder.output[0]),
|
|
||||||
|
|
||||||
self.gtx.encoder.d[1].eq(self.data_1.storage),
|
|
||||||
self.gtx.encoder.k[1].eq(self.control_bit_1.storage),
|
|
||||||
self.encoded_1.status.eq(self.gtx.encoder.output[1]),
|
|
||||||
]
|
|
||||||
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),
|
|
||||||
|
|
||||||
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),
|
|
||||||
]
|
|
||||||
|
|
||||||
# TODO: rip encoder & rx clockalignment out of CXP_GTX
|
|
||||||
|
|
||||||
# TODO: use expose encoder & decoder from CXP
|
|
||||||
# encoder.k = 1 if sending control bit, different calculation
|
|
||||||
# encoder.d = data 8 bit
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
channel_interface = ChannelInterface(gtx.encoder, gtx.decoders)
|
|
||||||
self.comb += channel_interface.rx_ready.eq(gtx.rx_ready)
|
|
||||||
channel_interfaces = []
|
|
||||||
channel_interfaces.append(channel_interface)
|
|
||||||
|
|
||||||
# TransceiverInterface, just adding cxp_rx_<num>
|
|
||||||
self.stable_clkin = CSRStorage()
|
|
||||||
self.txenable = CSRStorage(len(channel_interfaces))
|
|
||||||
for i in range(len(channel_interfaces)):
|
|
||||||
name = "cxp_gtx_rx" + str(i)
|
|
||||||
setattr(self.clock_domains, "cd_"+name, ClockDomain(name=name))
|
|
||||||
self.channels = channel_interfaces
|
|
||||||
|
|
||||||
|
|
||||||
# TODO: add tx_phase_alignment for multi CXP
|
|
||||||
# The TX phase alignment will fail with a wrong TXUSRCLK frequency
|
|
||||||
|
|
||||||
|
|
||||||
self.comb += [
|
|
||||||
gtx.rx_init.clk_path_ready.eq(self.rx_start_init.storage),
|
|
||||||
|
|
||||||
gtx.tx_init.clk_path_ready.eq(self.tx_start_init.storage),
|
|
||||||
gtx.txenable.eq(self.txenable.storage[0]),
|
|
||||||
gtx.tx_restart.eq(self.tx_restart.storage),
|
|
||||||
]
|
|
||||||
|
|
||||||
# TODO: Connect multilane cxp_tx
|
|
||||||
|
|
||||||
# TODO: Connect slave i's `cxp_gtx_rx` clock to `cxp_gtx_rxi` clock
|
|
||||||
self.comb += [
|
|
||||||
getattr(self, "cd_cxp_gtx_rx" + str(0)).clk.eq(self.gtx.cd_cxp_gtx_rx.clk),
|
|
||||||
getattr(self, "cd_cxp_gtx_rx" + str(0)).rst.eq(self.gtx.cd_cxp_gtx_rx.rst)
|
|
||||||
]
|
|
||||||
|
|
||||||
class CXP_CRC(Module, AutoCSR):
|
class CXP_CRC(Module, AutoCSR):
|
||||||
width = 32
|
width = 32
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
from migen import *
|
from migen import *
|
||||||
from migen.genlib.resetsync import AsyncResetSynchronizer
|
from migen.genlib.resetsync import AsyncResetSynchronizer
|
||||||
from migen.genlib.cdc import MultiReg, PulseSynchronizer
|
|
||||||
|
|
||||||
from misoc.cores.code_8b10b import Encoder, Decoder
|
from misoc.cores.code_8b10b import Encoder, Decoder
|
||||||
from misoc.interconnect.csr import *
|
from misoc.interconnect.csr import *
|
||||||
|
@ -8,140 +7,352 @@ from misoc.interconnect.csr import *
|
||||||
from artiq.gateware.drtio.transceiver.gtx_7series_init import *
|
from artiq.gateware.drtio.transceiver.gtx_7series_init import *
|
||||||
|
|
||||||
from operator import add
|
from operator import add
|
||||||
from math import ceil
|
|
||||||
from functools import reduce
|
from functools import reduce
|
||||||
|
|
||||||
# Changes the phase of the transceiver RX clock to align the comma to
|
class CXP_DownConn(Module, AutoCSR):
|
||||||
# the LSBs of RXDATA, fixing the latency.
|
def __init__(self, refclk, pads, sys_clk_freq, debug_sma, pmod_pads):
|
||||||
#
|
self.rx_start_init = CSRStorage()
|
||||||
# This is implemented by repeatedly resetting the transceiver until it
|
self.rx_restart = CSR()
|
||||||
# gives out the correct phase. Each reset gives a random phase.
|
|
||||||
#
|
self.tx_start_init = CSRStorage()
|
||||||
# If Xilinx had designed the GTX transceiver correctly, RXSLIDE_MODE=PMA
|
self.tx_restart = CSR()
|
||||||
# would achieve this faster and in a cleaner way. But:
|
self.txenable = CSRStorage()
|
||||||
# * the phase jumps are of 2 UI at every second RXSLIDE pulse, instead
|
|
||||||
# of 1 UI at every pulse. It is unclear what the latency becomes.
|
|
||||||
# * RXSLIDE_MODE=PMA cannot be used with the RX buffer bypassed.
|
self.txinit_phaligndone = CSRStatus()
|
||||||
# Those design flaws make RXSLIDE_MODE=PMA yet another broken and useless
|
self.rxinit_phaligndone = CSRStatus()
|
||||||
# transceiver "feature".
|
self.rx_ready = CSRStatus()
|
||||||
#
|
|
||||||
|
self.tx_div = CSRStorage(3)
|
||||||
|
self.rx_div = CSRStorage(3)
|
||||||
|
|
||||||
|
self.qpll_reset = CSR()
|
||||||
|
self.qpll_locked = CSRStatus()
|
||||||
|
|
||||||
|
# # #
|
||||||
|
|
||||||
|
self.submodules.qpll = qpll = QPLL(refclk, sys_clk_freq)
|
||||||
|
|
||||||
|
# single & master tx_mode can lock with rx in loopback
|
||||||
|
self.submodules.gtx = gtx = GTX(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.sync += [
|
||||||
|
# PLL
|
||||||
|
qpll.reset.eq(self.qpll_reset.re),
|
||||||
|
self.qpll_locked.status.eq(qpll.lock),
|
||||||
|
|
||||||
|
# GTX
|
||||||
|
self.txinit_phaligndone.status.eq(gtx.tx_init.Xxphaligndone),
|
||||||
|
self.rxinit_phaligndone.status.eq(gtx.rx_init.Xxphaligndone),
|
||||||
|
self.rx_ready.status.eq(gtx.rx_ready),
|
||||||
|
|
||||||
|
gtx.txenable.eq(self.txenable.storage[0]),
|
||||||
|
gtx.tx_restart.eq(self.tx_restart.re),
|
||||||
|
gtx.rx_restart.eq(self.rx_restart.re),
|
||||||
|
gtx.tx_init.clk_path_ready.eq(self.tx_start_init.storage),
|
||||||
|
gtx.rx_init.clk_path_ready.eq(self.rx_start_init.storage),
|
||||||
|
# gtx.rx_alignment_en.eq(self.rx_data_alignment.storage),
|
||||||
|
|
||||||
|
# GTX DRP
|
||||||
|
gtx.tx_rate.eq(self.tx_div.storage),
|
||||||
|
gtx.rx_rate.eq(self.rx_div.storage),
|
||||||
|
]
|
||||||
|
|
||||||
|
# DEBUG: txusrclk PLL DRG
|
||||||
|
|
||||||
|
self.txpll_reset = CSRStorage()
|
||||||
|
self.pll_daddr = CSRStorage(7)
|
||||||
|
self.pll_dclk = CSRStorage()
|
||||||
|
self.pll_den = CSRStorage()
|
||||||
|
self.pll_din = CSRStorage(16)
|
||||||
|
self.pll_dwen = CSRStorage()
|
||||||
|
|
||||||
|
self.txpll_locked = CSRStatus()
|
||||||
|
self.pll_dout = CSRStatus(16)
|
||||||
|
self.pll_dready = CSRStatus()
|
||||||
|
|
||||||
|
self.comb += [
|
||||||
|
gtx.txpll_reset.eq(self.txpll_reset.storage),
|
||||||
|
gtx.pll_daddr.eq(self.pll_daddr.storage),
|
||||||
|
gtx.pll_dclk.eq(self.pll_dclk.storage),
|
||||||
|
gtx.pll_den.eq(self.pll_den.storage),
|
||||||
|
gtx.pll_din.eq(self.pll_din.storage),
|
||||||
|
gtx.pll_dwen.eq(self.pll_dwen.storage),
|
||||||
|
|
||||||
|
self.txpll_locked.status.eq(gtx.txpll_locked),
|
||||||
|
self.pll_dout.status.eq(gtx.pll_dout),
|
||||||
|
self.pll_dready.status.eq(gtx.pll_dready),
|
||||||
|
]
|
||||||
|
|
||||||
|
# DEBUG:loopback
|
||||||
|
self.loopback_mode = CSRStorage(3)
|
||||||
|
self.comb += gtx.loopback_mode.eq(self.loopback_mode.storage)
|
||||||
|
|
||||||
|
# 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.clk_aligner.rxslide, o_O=pmod_pads[0]),
|
||||||
|
Instance("OBUF", i_I=gtx.clk_aligner.ready, o_O=pmod_pads[1]),
|
||||||
|
# Instance("OBUF", i_I=gtx.tx_init.Xxdlysresetdone , o_O=pmod_pads[2]),
|
||||||
|
# Instance("OBUF", i_I=gtx.tx_init.Xxphaligndone , 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
|
||||||
|
counter_max = 2
|
||||||
|
counter = Signal(max=counter_max)
|
||||||
|
|
||||||
|
self.data_0 = CSRStorage(8)
|
||||||
|
self.data_1 = CSRStorage(8)
|
||||||
|
self.data_2 = CSRStorage(8)
|
||||||
|
self.data_3 = CSRStorage(8)
|
||||||
|
self.control_bit_0 = CSRStorage()
|
||||||
|
self.control_bit_1 = CSRStorage()
|
||||||
|
self.control_bit_2 = CSRStorage()
|
||||||
|
self.control_bit_3 = CSRStorage()
|
||||||
|
self.encoded_0 = CSRStatus(10)
|
||||||
|
self.encoded_1 = CSRStatus(10)
|
||||||
|
|
||||||
|
self.rxdata_0 = CSRStatus(10)
|
||||||
|
self.rxdata_1 = CSRStatus(10)
|
||||||
|
self.decoded_data_0 = CSRStatus(8)
|
||||||
|
self.decoded_data_1 = CSRStatus(8)
|
||||||
|
self.decoded_k_0 = CSRStatus()
|
||||||
|
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.encoded_0.status.eq(self.gtx.encoder.output[0]),
|
||||||
|
self.encoded_1.status.eq(self.gtx.encoder.output[1]),
|
||||||
|
]
|
||||||
|
|
||||||
|
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),
|
||||||
|
|
||||||
|
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),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
# The TX phase alignment will fail with a wrong TXUSRCLK frequency
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# NOTE: No need to connect cxp_gtx_tx, we don't use tx anyway (just for loopback)
|
||||||
|
|
||||||
|
# TODO: Connect slave cxp_gtx_rx clock tgt
|
||||||
|
# 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 = 8
|
||||||
|
|
||||||
|
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,
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
# Warning: Xilinx transceivers are LSB first, and comma needs to be flipped
|
# Warning: Xilinx transceivers are LSB first, and comma needs to be flipped
|
||||||
# compared to the usual 8b10b binary representation.
|
# compared to the usual 8b10b binary representation.
|
||||||
class CXP_BruteforceClockAligner(Module):
|
class Manual_Aligner(Module):
|
||||||
def __init__(self, comma, sys_clk_freq, check_period):
|
def __init__(self, comma, check_cycles=20000):
|
||||||
|
|
||||||
|
self.rxslide = Signal()
|
||||||
self.rxdata = Signal(20)
|
self.rxdata = Signal(20)
|
||||||
self.restart = Signal()
|
|
||||||
|
|
||||||
self.ready = Signal()
|
self.ready = Signal()
|
||||||
|
|
||||||
check_max_val = ceil(check_period*sys_clk_freq)
|
# # #
|
||||||
check_counter = Signal(max=check_max_val+1)
|
|
||||||
check = Signal()
|
|
||||||
reset_check_counter = Signal()
|
|
||||||
self.sync += [
|
|
||||||
check.eq(0),
|
|
||||||
If(reset_check_counter,
|
|
||||||
check_counter.eq(check_max_val)
|
|
||||||
).Else(
|
|
||||||
If(check_counter == 0,
|
|
||||||
check.eq(1),
|
|
||||||
check_counter.eq(check_max_val)
|
|
||||||
).Else(
|
|
||||||
check_counter.eq(check_counter-1)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
]
|
|
||||||
|
|
||||||
checks_reset = PulseSynchronizer("sys", "cxp_gtx_rx")
|
checks_reset = Signal()
|
||||||
self.submodules += checks_reset
|
error_seen = Signal()
|
||||||
|
comma_seen = Signal()
|
||||||
|
|
||||||
|
rx1cnt = Signal(max=11)
|
||||||
|
|
||||||
comma_n = ~comma & 0b1111111111
|
comma_n = ~comma & 0b1111111111
|
||||||
comma_seen_rxclk = Signal()
|
|
||||||
comma_seen = Signal()
|
|
||||||
comma_seen_rxclk.attr.add("no_retiming")
|
|
||||||
self.specials += MultiReg(comma_seen_rxclk, comma_seen)
|
|
||||||
self.sync.cxp_gtx_rx += \
|
|
||||||
If(checks_reset.o,
|
|
||||||
comma_seen_rxclk.eq(0)
|
|
||||||
).Elif((self.rxdata[:10] == comma) | (self.rxdata[:10] == comma_n),
|
|
||||||
comma_seen_rxclk.eq(1)
|
|
||||||
)
|
|
||||||
|
|
||||||
error_seen_rxclk = Signal()
|
|
||||||
error_seen = Signal()
|
|
||||||
error_seen_rxclk.attr.add("no_retiming")
|
|
||||||
self.specials += MultiReg(error_seen_rxclk, error_seen)
|
|
||||||
rx1cnt = Signal(max=11)
|
|
||||||
self.sync.cxp_gtx_rx += [
|
self.sync.cxp_gtx_rx += [
|
||||||
rx1cnt.eq(reduce(add, [self.rxdata[i] for i in range(10)])),
|
rx1cnt.eq(reduce(add, [self.rxdata[i] for i in range(10)])),
|
||||||
If(checks_reset.o,
|
If(checks_reset,
|
||||||
error_seen_rxclk.eq(0)
|
error_seen.eq(0)
|
||||||
).Elif((rx1cnt != 4) & (rx1cnt != 5) & (rx1cnt != 6),
|
).Elif((rx1cnt != 4) & (rx1cnt != 5) & (rx1cnt != 6),
|
||||||
error_seen_rxclk.eq(1)
|
error_seen.eq(1)
|
||||||
|
),
|
||||||
|
If(checks_reset,
|
||||||
|
comma_seen.eq(0)
|
||||||
|
).Elif((self.rxdata[:10] == comma) | (self.rxdata[:10] == comma_n),
|
||||||
|
comma_seen.eq(1)
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
fsm = FSM(reset_state="WAIT_COMMA")
|
# minimum of 32 RXUSRCLK2 cycles are required between two RXSLIDE pulses
|
||||||
|
slide_timer = ClockDomainsRenamer("cxp_gtx_rx")(WaitTimer(64))
|
||||||
|
self.submodules += slide_timer
|
||||||
|
counter = Signal(reset=check_cycles-1, max=check_cycles)
|
||||||
|
|
||||||
|
fsm = ClockDomainsRenamer("cxp_gtx_rx")(FSM(reset_state="IDLE"))
|
||||||
self.submodules += fsm
|
self.submodules += fsm
|
||||||
|
|
||||||
fsm.act("WAIT_COMMA",
|
fsm.act("IDLE",
|
||||||
If(check,
|
slide_timer.wait.eq(1),
|
||||||
# Errors are still OK at this stage, as the transceiver
|
If(slide_timer.done,
|
||||||
# has just been reset and may output garbage data.
|
|
||||||
If(comma_seen,
|
If(comma_seen,
|
||||||
NextState("WAIT_NOERROR")
|
NextState("READY"),
|
||||||
).Else(
|
).Else(
|
||||||
self.restart.eq(1)
|
NextState("SLIDING")
|
||||||
),
|
)
|
||||||
checks_reset.i.eq(1)
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
fsm.act("WAIT_NOERROR",
|
|
||||||
If(check,
|
fsm.act("SLIDING",
|
||||||
If(comma_seen & ~error_seen,
|
self.rxslide.eq(1),
|
||||||
NextState("READY")
|
checks_reset.eq(1),
|
||||||
).Else(
|
NextState("IDLE")
|
||||||
self.restart.eq(1),
|
|
||||||
NextState("WAIT_COMMA")
|
|
||||||
),
|
|
||||||
checks_reset.i.eq(1)
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
fsm.act("READY",
|
fsm.act("READY",
|
||||||
reset_check_counter.eq(1),
|
|
||||||
self.ready.eq(1),
|
self.ready.eq(1),
|
||||||
If(error_seen,
|
If(counter == 0,
|
||||||
checks_reset.i.eq(1),
|
NextValue(counter, check_cycles - 1),
|
||||||
self.restart.eq(1),
|
If(error_seen,
|
||||||
NextState("WAIT_COMMA")
|
NextState("IDLE"),
|
||||||
|
)
|
||||||
|
).Else(
|
||||||
|
NextValue(counter, counter - 1),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class GTX(Module):
|
||||||
class CXP_DownConn(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, 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
|
||||||
pll_div = int(40/cpll_div)
|
pll_fbout_mult = 8
|
||||||
|
txusr_pll_div = pll_fbout_mult*sys_clk_freq/qpll.tx_usrclk_freq # 20 is datawidth
|
||||||
|
|
||||||
self.rx_restart = Signal()
|
|
||||||
self.tx_restart = Signal()
|
self.tx_restart = Signal()
|
||||||
|
self.rx_restart = Signal()
|
||||||
self.loopback_mode = Signal(3)
|
self.loopback_mode = Signal(3)
|
||||||
|
|
||||||
self.txenable = Signal()
|
self.txenable = Signal()
|
||||||
|
self.rx_ready = Signal()
|
||||||
|
|
||||||
|
self.tx_rate = Signal(3)
|
||||||
|
self.rx_rate = Signal(3)
|
||||||
|
|
||||||
self.submodules.encoder = ClockDomainsRenamer("cxp_gtx_tx")(Encoder(2, True))
|
self.submodules.encoder = ClockDomainsRenamer("cxp_gtx_tx")(Encoder(2, True))
|
||||||
self.submodules.decoders = [ClockDomainsRenamer("cxp_gtx_rx")(
|
self.submodules.decoders = [ClockDomainsRenamer("cxp_gtx_rx")(
|
||||||
(Decoder(True))) for _ in range(2)]
|
(Decoder(True))) for _ in range(2)]
|
||||||
self.rx_ready = Signal()
|
|
||||||
|
|
||||||
# transceiver direct clock outputs
|
# transceiver direct clock outputs
|
||||||
# useful to specify clock constraints in a way palatable to Vivado
|
# useful to specify clock constraints in a way palatable to Vivado
|
||||||
|
@ -150,20 +361,23 @@ class CXP_DownConn(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)
|
# FIXME: 500e6 is used to fix Xx reset by holding gtxXxreset for a couple cycle more
|
||||||
# RX receives restart commands from RTIO domain
|
self.submodules.tx_init = tx_init = GTXInit(500e6, False, mode=tx_mode)
|
||||||
self.submodules.rx_init = rx_init = GTXInit(sys_clk_freq, True, mode=rx_mode)
|
self.submodules.rx_init = rx_init = GTXInit(500e6, True, mode=rx_mode)
|
||||||
|
|
||||||
|
# RX receives restart commands from txusrclk domain
|
||||||
|
# self.submodules.rx_init = rx_init = ClockDomainsRenamer("cxp_gtx_tx")(GTXInit(500e6, 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)
|
||||||
rxdata = Signal(20)
|
rxdata = Signal(20)
|
||||||
|
|
||||||
|
rxslide = Signal()
|
||||||
# Note: the following parameters were set after consulting AR45360
|
# Note: the following parameters were set after consulting AR45360
|
||||||
self.specials += \
|
self.specials += \
|
||||||
Instance("GTXE2_CHANNEL",
|
Instance("GTXE2_CHANNEL",
|
||||||
|
@ -181,29 +395,25 @@ class CXP_DownConn(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
|
# Dynamic Tx/Rx divider
|
||||||
p_CPLL_CFG=0xBC07DC,
|
i_TXRATE=self.tx_rate,
|
||||||
p_CPLL_FBDIV=cpll_div,
|
i_RXRATE=self.rx_rate,
|
||||||
p_CPLL_FBDIV_45=5,
|
|
||||||
p_CPLL_REFCLK_DIV=1,
|
# QPLL
|
||||||
p_RXOUT_DIV=2,
|
i_QPLLCLK=qpll.clk,
|
||||||
p_TXOUT_DIV=2,
|
i_QPLLREFCLK=qpll.refclk,
|
||||||
p_CPLL_INIT_CFG=0x00001E,
|
p_RXOUT_DIV=qpll.Xxout_div,
|
||||||
p_CPLL_LOCK_CFG=0x01E8,
|
p_TXOUT_DIV=qpll.Xxout_div,
|
||||||
i_CPLLRESET=cpllreset,
|
i_RXSYSCLKSEL=0b11, # use QPLL & QPLL's REFCLK
|
||||||
i_CPLLPD=cpllreset,
|
i_TXSYSCLKSEL=0b11, # use QPLL & CPLL's REFCLK
|
||||||
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
|
||||||
|
@ -274,7 +484,7 @@ class CXP_DownConn(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"),
|
||||||
|
@ -297,16 +507,22 @@ class CXP_DownConn(Module):
|
||||||
# RX Byte and Word Alignment Attributes
|
# RX Byte and Word Alignment Attributes
|
||||||
p_ALIGN_COMMA_DOUBLE="FALSE",
|
p_ALIGN_COMMA_DOUBLE="FALSE",
|
||||||
p_ALIGN_COMMA_ENABLE=0b1111111111,
|
p_ALIGN_COMMA_ENABLE=0b1111111111,
|
||||||
p_ALIGN_COMMA_WORD=1,
|
p_ALIGN_COMMA_WORD=2,
|
||||||
p_ALIGN_MCOMMA_DET="TRUE",
|
p_ALIGN_MCOMMA_DET="FALSE",
|
||||||
p_ALIGN_MCOMMA_VALUE=0b1010000011,
|
p_ALIGN_MCOMMA_VALUE=0b1010000011,
|
||||||
p_ALIGN_PCOMMA_DET="TRUE",
|
p_ALIGN_PCOMMA_DET="FALSE",
|
||||||
p_ALIGN_PCOMMA_VALUE=0b0101111100,
|
p_ALIGN_PCOMMA_VALUE=0b0101111100,
|
||||||
p_SHOW_REALIGN_COMMA="FALSE",
|
p_SHOW_REALIGN_COMMA="FALSE",
|
||||||
p_RXSLIDE_AUTO_WAIT=7,
|
p_RXSLIDE_AUTO_WAIT=7,
|
||||||
p_RXSLIDE_MODE="PCS",
|
p_RXSLIDE_MODE="PCS",
|
||||||
p_RX_SIG_VALID_DLY=10,
|
p_RX_SIG_VALID_DLY=10,
|
||||||
|
|
||||||
|
# Manual Word Alignment
|
||||||
|
i_RXPCOMMAALIGNEN=0,
|
||||||
|
i_RXMCOMMAALIGNEN=0,
|
||||||
|
i_RXCOMMADETEN=1, # enable word alignment, but breaks rxrestart if gtxXxreset hold too short
|
||||||
|
i_RXSLIDE=rxslide,
|
||||||
|
|
||||||
# RX 8B/10B Decoder Attributes
|
# RX 8B/10B Decoder Attributes
|
||||||
p_RX_DISPERR_SEQ_MATCH="FALSE",
|
p_RX_DISPERR_SEQ_MATCH="FALSE",
|
||||||
p_DEC_MCOMMA_DETECT="TRUE",
|
p_DEC_MCOMMA_DETECT="TRUE",
|
||||||
|
@ -374,31 +590,47 @@ class CXP_DownConn(Module):
|
||||||
# A PLL is used to generate the correct frequency for TXUSRCLK (UG476 Equation 3-1)
|
# A PLL is used to generate the correct frequency for TXUSRCLK (UG476 Equation 3-1)
|
||||||
self.clock_domains.cd_cxp_gtx_tx = ClockDomain()
|
self.clock_domains.cd_cxp_gtx_tx = ClockDomain()
|
||||||
txpll_fb_clk = Signal()
|
txpll_fb_clk = Signal()
|
||||||
txpll_reset = Signal()
|
|
||||||
txpll_locked = Signal()
|
|
||||||
txoutclk_buf = Signal()
|
txoutclk_buf = Signal()
|
||||||
txpll_clkout = 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_locked = Signal()
|
||||||
|
self.pll_dout = Signal(16)
|
||||||
|
self.pll_dready = Signal()
|
||||||
self.specials += [
|
self.specials += [
|
||||||
Instance("PLLE2_ADV",
|
Instance("PLLE2_ADV",
|
||||||
p_BANDWIDTH="HIGH",
|
p_BANDWIDTH="HIGH",
|
||||||
o_LOCKED=txpll_locked,
|
o_LOCKED=self.txpll_locked,
|
||||||
i_RST=txpll_reset,
|
i_RST=self.txpll_reset,
|
||||||
|
|
||||||
p_CLKIN1_PERIOD=1e9/sys_clk_freq, # ns
|
p_CLKIN1_PERIOD=1e9/sys_clk_freq, # ns
|
||||||
i_CLKIN1=txoutclk_buf,
|
i_CLKIN1=txoutclk_buf,
|
||||||
|
|
||||||
# VCO @ 1.25GHz
|
# VCO @ 1.25GHz
|
||||||
p_CLKFBOUT_MULT=10, p_DIVCLK_DIVIDE=1,
|
p_CLKFBOUT_MULT=pll_fbout_mult, p_DIVCLK_DIVIDE=1,
|
||||||
i_CLKFBIN=txpll_fb_clk, o_CLKFBOUT=txpll_fb_clk,
|
i_CLKFBIN=txpll_fb_clk, o_CLKFBOUT=txpll_fb_clk,
|
||||||
|
|
||||||
# 156.25MHz
|
# frequency = linerate/20
|
||||||
p_CLKOUT0_DIVIDE=pll_div, p_CLKOUT0_PHASE=0.0, o_CLKOUT0=txpll_clkout,
|
p_CLKOUT0_DIVIDE=txusr_pll_div, p_CLKOUT0_PHASE=0.0, o_CLKOUT0=txpll_clkout,
|
||||||
|
|
||||||
# TODO: DRP for line rate change
|
# 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=self.txoutclk, o_O=txoutclk_buf),
|
||||||
Instance("BUFG", i_I=txpll_clkout, o_O=self.cd_cxp_gtx_tx.clk),
|
Instance("BUFG", i_I=txpll_clkout, o_O=self.cd_cxp_gtx_tx.clk),
|
||||||
AsyncResetSynchronizer(self.cd_cxp_gtx_tx, ~txpll_locked & ~tx_init.done)
|
AsyncResetSynchronizer(self.cd_cxp_gtx_tx, ~self.txpll_locked & ~tx_init.done)
|
||||||
]
|
]
|
||||||
|
|
||||||
# RX clocking
|
# RX clocking
|
||||||
|
@ -415,12 +647,13 @@ class CXP_DownConn(Module):
|
||||||
self.decoders[1].input.eq(rxdata[10:])
|
self.decoders[1].input.eq(rxdata[10:])
|
||||||
]
|
]
|
||||||
|
|
||||||
# 6e-3 is too slow for 3.25Gbps line rate
|
|
||||||
clock_aligner = CXP_BruteforceClockAligner(0b0101111100, sys_clk_freq, check_period=1e-2)
|
self.submodules.clk_aligner = clk_aligner = Manual_Aligner(0b0101111100)
|
||||||
self.submodules += clock_aligner
|
|
||||||
self.comb += [
|
self.comb += [
|
||||||
clock_aligner.rxdata.eq(rxdata),
|
clk_aligner.rxdata.eq(rxdata),
|
||||||
rx_init.restart.eq(clock_aligner.restart),
|
rxslide.eq(clk_aligner.rxslide),
|
||||||
self.rx_ready.eq(clock_aligner.ready),
|
self.rx_ready.eq(clk_aligner.ready),
|
||||||
tx_init.restart.eq(self.tx_restart)
|
|
||||||
|
rx_init.restart.eq(self.rx_restart),
|
||||||
|
tx_init.restart.eq(self.tx_restart),
|
||||||
]
|
]
|
||||||
|
|
|
@ -162,22 +162,22 @@ class CXP_UpConn(Module, AutoCSR):
|
||||||
p3.eq(self.tx_idle.word_idx == 3),
|
p3.eq(self.tx_idle.word_idx == 3),
|
||||||
]
|
]
|
||||||
self.specials += [
|
self.specials += [
|
||||||
# debug sma
|
# # debug sma
|
||||||
Instance("OBUF", i_I=o, o_O=pads.p_tx),
|
# Instance("OBUF", i_I=o, o_O=pads.p_tx),
|
||||||
Instance("OBUF", i_I=self.cd_cxp_upconn.clk, o_O=pads.n_rx),
|
# Instance("OBUF", i_I=self.cd_cxp_upconn.clk, o_O=pads.n_rx),
|
||||||
|
|
||||||
# pmod 0-7 pin
|
# # pmod 0-7 pin
|
||||||
Instance("OBUF", i_I=o, o_O=pmod[0]),
|
# Instance("OBUF", i_I=o, o_O=pmod[0]),
|
||||||
Instance("OBUF", i_I=self.cd_cxp_upconn.clk, o_O=pmod[1]),
|
# Instance("OBUF", i_I=self.cd_cxp_upconn.clk, o_O=pmod[1]),
|
||||||
Instance("OBUF", i_I=~self.tx_fifos.pe.n, o_O=pmod[2]),
|
# Instance("OBUF", i_I=~self.tx_fifos.pe.n, o_O=pmod[2]),
|
||||||
Instance("OBUF", i_I=ninth_bit, o_O=pmod[3]),
|
# Instance("OBUF", i_I=ninth_bit, o_O=pmod[3]),
|
||||||
Instance("OBUF", i_I=word_bound, o_O=pmod[4]),
|
# Instance("OBUF", i_I=word_bound, o_O=pmod[4]),
|
||||||
Instance("OBUF", i_I=idling, o_O=pmod[5]),
|
# Instance("OBUF", i_I=idling, o_O=pmod[5]),
|
||||||
# Instance("OBUF", i_I=self.tx_fifos.source_ack[0], o_O=pmod[6]),
|
# # Instance("OBUF", i_I=self.tx_fifos.source_ack[0], o_O=pmod[6]),
|
||||||
# Instance("OBUF", i_I=self.tx_fifos.source_ack[2], o_O=pmod[6]),
|
# # Instance("OBUF", i_I=self.tx_fifos.source_ack[2], o_O=pmod[6]),
|
||||||
# Instance("OBUF", i_I=self.tx_fifos.source_ack[1], o_O=pmod[7]),
|
# # Instance("OBUF", i_I=self.tx_fifos.source_ack[1], o_O=pmod[7]),
|
||||||
Instance("OBUF", i_I=p0, o_O=pmod[6]),
|
# Instance("OBUF", i_I=p0, o_O=pmod[6]),
|
||||||
Instance("OBUF", i_I=p3, o_O=pmod[7]),
|
# Instance("OBUF", i_I=p3, o_O=pmod[7]),
|
||||||
]
|
]
|
||||||
self.symbol0 = CSR(9)
|
self.symbol0 = CSR(9)
|
||||||
self.symbol1 = CSR(9)
|
self.symbol1 = CSR(9)
|
||||||
|
|
|
@ -4,67 +4,431 @@ use log::info;
|
||||||
|
|
||||||
use crate::pl::csr;
|
use crate::pl::csr;
|
||||||
|
|
||||||
pub fn main(timer: &mut GlobalTimer) {
|
pub struct CXP_DownConn_Settings {
|
||||||
|
pub rxdiv: u8,
|
||||||
|
pub qpll_fbdiv: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
pub enum CXP_SPEED {
|
||||||
|
CXP_1,
|
||||||
|
CXP_2,
|
||||||
|
CXP_3,
|
||||||
|
CXP_5,
|
||||||
|
CXP_6,
|
||||||
|
CXP_10,
|
||||||
|
CXP_12,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn loopback_testing(timer: &mut GlobalTimer, data: u8, control_bit: u8) {
|
||||||
unsafe {
|
unsafe {
|
||||||
info!("turning on pmc loopback mode...");
|
// send K28_5 for CDR to align
|
||||||
csr::cxp::loopback_mode_write(0b010); // Near-End PMA Loopback
|
const K28_5: u8 = 0xBC;
|
||||||
|
const K28_1: u8 = 0x3C;
|
||||||
|
const D21_5: u8 = 21 | 5 << 5;
|
||||||
|
|
||||||
// enable cxp gtx clock domains
|
// NOTE: testing with IDLE word + manual comma alignment
|
||||||
csr::cxp::tx_start_init_write(1);
|
// 62.5MHz OK
|
||||||
csr::cxp::rx_start_init_write(1);
|
// 125MHz OK
|
||||||
|
// 156.25MHz
|
||||||
|
// 250MHz OK
|
||||||
|
// 312.5MHz
|
||||||
|
// 500MHz ?? havn't change CDR config yet
|
||||||
|
const LEN: usize = 4;
|
||||||
|
const DATA: [[u8; LEN]; 2] = [
|
||||||
|
// [K28_5, K28_1, K28_5, K28_1],
|
||||||
|
// [1, 1, 1, 1],
|
||||||
|
[K28_5, K28_1, K28_1, D21_5],
|
||||||
|
[1, 1, 1, 0],
|
||||||
|
];
|
||||||
|
|
||||||
info!("waiting for QPLL/CPLL to lock...");
|
csr::cxp::downconn_data_0_write(DATA[0][0]);
|
||||||
|
csr::cxp::downconn_data_1_write(DATA[0][1]);
|
||||||
|
csr::cxp::downconn_data_2_write(DATA[0][2]);
|
||||||
|
csr::cxp::downconn_data_3_write(DATA[0][3]);
|
||||||
|
|
||||||
|
csr::cxp::downconn_control_bit_0_write(DATA[1][0]);
|
||||||
|
csr::cxp::downconn_control_bit_1_write(DATA[1][1]);
|
||||||
|
csr::cxp::downconn_control_bit_2_write(DATA[1][2]);
|
||||||
|
csr::cxp::downconn_control_bit_3_write(DATA[1][3]);
|
||||||
|
|
||||||
|
info!("waiting for tx&rx setup...");
|
||||||
timer.delay_us(50_000);
|
timer.delay_us(50_000);
|
||||||
info!("tx_phaligndone = {} ", csr::cxp::txinit_phaligndone_read(),);
|
info!(
|
||||||
|
"tx_phaligndone = {} | rx_phaligndone = {}",
|
||||||
|
csr::cxp::downconn_txinit_phaligndone_read(),
|
||||||
|
csr::cxp::downconn_rxinit_phaligndone_read(),
|
||||||
|
);
|
||||||
|
|
||||||
// enable txdata tranmission thought MGTXTXP, required by PMA loopback
|
// enable txdata tranmission thought MGTXTXP, required by PMA loopback
|
||||||
csr::cxp::txenable_write(1);
|
csr::cxp::downconn_txenable_write(1);
|
||||||
|
|
||||||
loopback_testing(timer, 0x00, 0);
|
info!("waiting for rx to align...");
|
||||||
|
timer.delay_us(50_000);
|
||||||
|
// while csr::cxp::downconn_rx_ready_read() != 1 {}
|
||||||
|
// info!("rx ready!");
|
||||||
|
|
||||||
|
// csr::cxp::data_3_write(data);
|
||||||
|
// csr::cxp::control_bit_3_write(control_bit);
|
||||||
|
// println!(
|
||||||
|
// "data[0] = {:#04x} control bit = {:#b} encoded = {:#012b}",
|
||||||
|
// csr::cxp::downconn_data_0_read(),
|
||||||
|
// csr::cxp::downconn_control_bit_0_read(),
|
||||||
|
// csr::cxp::downconn_encoded_0_read(),
|
||||||
|
// );
|
||||||
|
// println!(
|
||||||
|
// "data[1] = {:#04x} control bit = {:#b} encoded = {:#012b}",
|
||||||
|
// csr::cxp::downconn_data_1_read(),
|
||||||
|
// csr::cxp::downconn_control_bit_1_read(),
|
||||||
|
// csr::cxp::downconn_encoded_1_read(),
|
||||||
|
// );
|
||||||
|
|
||||||
|
// for _ in 0..20 {
|
||||||
|
loop {
|
||||||
|
// timer.delay_us(100);
|
||||||
|
println!(
|
||||||
|
"data = {:#022b} | rx ready = {}",
|
||||||
|
(csr::cxp::downconn_rxdata_0_read() as u32 | ((csr::cxp::downconn_rxdata_1_read() as u32) << 10)),
|
||||||
|
csr::cxp::downconn_rx_ready_read()
|
||||||
|
);
|
||||||
|
timer.delay_us(1_000_000);
|
||||||
|
// println!(
|
||||||
|
// "data[0] = {:#012b} data[1] = {:#012b}",
|
||||||
|
// csr::cxp::rxdata_0_read(),
|
||||||
|
// csr::cxp::rxdata_1_read(),
|
||||||
|
// );
|
||||||
|
// println!(
|
||||||
|
// "decoded_data[0] = {:#04x} decoded_k[0] = {:#b} decoded_data[1] = {:#04x} decoded_k[1] = {:#b}",
|
||||||
|
// csr::cxp::downconn_decoded_data_0_read(),
|
||||||
|
// csr::cxp::downconn_decoded_k_0_read(),
|
||||||
|
// csr::cxp::downconn_decoded_data_1_read(),
|
||||||
|
// csr::cxp::downconn_decoded_k_1_read(),
|
||||||
|
// );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setup(timer: &mut GlobalTimer, speed: CXP_SPEED) {
|
||||||
|
unsafe {
|
||||||
|
info!("turning on pmc loopback mode...");
|
||||||
|
csr::cxp::downconn_loopback_mode_write(0b010); // Near-End PMA Loopback
|
||||||
|
|
||||||
|
// QPLL setup
|
||||||
|
csr::cxp::downconn_qpll_reset_write(1);
|
||||||
|
info!("waiting for QPLL/CPLL to lock...");
|
||||||
|
while csr::cxp::downconn_qpll_locked_read() != 1 {}
|
||||||
|
info!("QPLL locked");
|
||||||
|
|
||||||
|
// tx/rx setup
|
||||||
|
csr::cxp::downconn_tx_start_init_write(1);
|
||||||
|
csr::cxp::downconn_rx_start_init_write(1);
|
||||||
|
|
||||||
|
info!("waiting for tx & rx setup...");
|
||||||
|
timer.delay_us(50_000);
|
||||||
|
info!(
|
||||||
|
"tx_phaligndone = {} | rx_phaligndone = {}",
|
||||||
|
csr::cxp::downconn_txinit_phaligndone_read(),
|
||||||
|
csr::cxp::downconn_rxinit_phaligndone_read(),
|
||||||
|
);
|
||||||
|
println!("==============================================================================");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn loopback_testing(timer: &mut GlobalTimer, data: u8, control_bit: u8) {
|
change_linerate(timer, speed);
|
||||||
|
|
||||||
|
loopback_testing(timer, 0x00, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn change_linerate(timer: &mut GlobalTimer, speed: CXP_SPEED) {
|
||||||
|
info!("Changing datarate to {:?}", speed);
|
||||||
|
// DEBUG: DRP pll for TXUSRCLK = freq(linerate)/20
|
||||||
|
let settings = txusrclk::get_txusrclk_config(speed);
|
||||||
|
txusrclk::setup(timer, settings);
|
||||||
|
|
||||||
|
// TODO: set QPLL_FBDIV via DRP
|
||||||
|
change_qpll_settings(speed);
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
csr::cxp::downconn_qpll_reset_write(1);
|
||||||
|
info!("waiting for QPLL/CPLL to lock...");
|
||||||
|
while csr::cxp::downconn_qpll_locked_read() != 1 {}
|
||||||
|
info!("QPLL locked");
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
csr::cxp::downconn_tx_restart_write(1);
|
||||||
|
csr::cxp::downconn_rx_restart_write(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn change_qpll_settings(speed: CXP_SPEED) {
|
||||||
|
let divider = match speed {
|
||||||
|
CXP_SPEED::CXP_1 => 0b100, // Divided by 8
|
||||||
|
CXP_SPEED::CXP_2 | CXP_SPEED::CXP_3 => 0b011, // Divided by 4
|
||||||
|
CXP_SPEED::CXP_5 | CXP_SPEED::CXP_6 => 0b010, // Divided by 2
|
||||||
|
CXP_SPEED::CXP_10 | CXP_SPEED::CXP_12 => 0b010, // Divided by 1
|
||||||
|
};
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
csr::cxp::downconn_rx_div_write(divider);
|
||||||
|
csr::cxp::downconn_tx_div_write(divider);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod txusrclk {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub struct PLLSetting {
|
||||||
|
pub clkout0_reg1: u16, //0x08
|
||||||
|
pub clkout0_reg2: u16, //0x09
|
||||||
|
pub clkfbout_reg1: u16, //0x14
|
||||||
|
pub clkfbout_reg2: u16, //0x15
|
||||||
|
pub div_reg: u16, //0x16
|
||||||
|
pub lock_reg1: u16, //0x18
|
||||||
|
pub lock_reg2: u16, //0x19
|
||||||
|
pub lock_reg3: u16, //0x1A
|
||||||
|
pub power_reg: u16, //0x28
|
||||||
|
pub filt_reg1: u16, //0x4E
|
||||||
|
pub filt_reg2: u16, //0x4F
|
||||||
|
}
|
||||||
|
|
||||||
|
fn one_clock_cycle() {
|
||||||
unsafe {
|
unsafe {
|
||||||
// send K28_5 for CDR to align
|
csr::cxp::downconn_pll_dclk_write(1);
|
||||||
const K28_5: u8 = 0xBC;
|
csr::cxp::downconn_pll_dclk_write(0);
|
||||||
csr::cxp::data_0_write(K28_5);
|
}
|
||||||
csr::cxp::control_bit_0_write(1);
|
}
|
||||||
csr::cxp::data_1_write(K28_5);
|
|
||||||
csr::cxp::control_bit_1_write(1);
|
|
||||||
|
|
||||||
info!("waiting for rx to align...");
|
fn set_addr(address: u8) {
|
||||||
while csr::cxp::rx_ready_read() != 1 {}
|
unsafe {
|
||||||
info!("rx ready!");
|
csr::cxp::downconn_pll_daddr_write(address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
csr::cxp::data_1_write(data);
|
fn set_data(value: u16) {
|
||||||
csr::cxp::control_bit_1_write(control_bit);
|
unsafe {
|
||||||
println!(
|
csr::cxp::downconn_pll_din_write(value);
|
||||||
"data[0] = {:#04x} control bit = {:#b} encoded = {:#012b}",
|
}
|
||||||
csr::cxp::data_0_read(),
|
}
|
||||||
csr::cxp::control_bit_0_read(),
|
|
||||||
csr::cxp::encoded_0_read(),
|
|
||||||
);
|
|
||||||
println!(
|
|
||||||
"data[1] = {:#04x} control bit = {:#b} encoded = {:#012b}",
|
|
||||||
csr::cxp::data_1_read(),
|
|
||||||
csr::cxp::control_bit_1_read(),
|
|
||||||
csr::cxp::encoded_1_read(),
|
|
||||||
);
|
|
||||||
|
|
||||||
for _ in 0..20 {
|
fn set_enable(en: bool) {
|
||||||
timer.delay_us(100);
|
unsafe {
|
||||||
// println!(
|
let val = if en { 1 } else { 0 };
|
||||||
// "data[0] = {:#012b} data[1] = {:#012b}",
|
csr::cxp::downconn_pll_den_write(val);
|
||||||
// csr::cxp::rxdata_0_read(),
|
}
|
||||||
// csr::cxp::rxdata_1_read(),
|
}
|
||||||
// );
|
|
||||||
println!(
|
fn set_write_enable(en: bool) {
|
||||||
"decoded_data[0] = {:#04x} decoded_k[0] = {:#b} decoded_data[1] = {:#04x} decoded_k[1] = {:#b}",
|
unsafe {
|
||||||
csr::cxp::decoded_data_0_read(),
|
let val = if en { 1 } else { 0 };
|
||||||
csr::cxp::decoded_k_0_read(),
|
csr::cxp::downconn_pll_dwen_write(val);
|
||||||
csr::cxp::decoded_data_1_read(),
|
}
|
||||||
csr::cxp::decoded_k_1_read(),
|
}
|
||||||
);
|
|
||||||
|
fn get_data() -> u16 {
|
||||||
|
unsafe { csr::cxp::downconn_pll_dout_read() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn drp_ready() -> bool {
|
||||||
|
unsafe { csr::cxp::downconn_pll_dready_read() == 1 }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
fn read(address: u8) -> u16 {
|
||||||
|
set_addr(address);
|
||||||
|
set_enable(true);
|
||||||
|
// Set DADDR on the mmcm and assert DEN for one clock cycle
|
||||||
|
one_clock_cycle();
|
||||||
|
|
||||||
|
set_enable(false);
|
||||||
|
while !drp_ready() {
|
||||||
|
// keep the clock signal until data is ready
|
||||||
|
one_clock_cycle();
|
||||||
|
}
|
||||||
|
get_data()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write(address: u8, value: u16) {
|
||||||
|
set_addr(address);
|
||||||
|
set_data(value);
|
||||||
|
set_write_enable(true);
|
||||||
|
set_enable(true);
|
||||||
|
// Set DADDR, DI on the mmcm and assert DWE, DEN for one clock cycle
|
||||||
|
one_clock_cycle();
|
||||||
|
|
||||||
|
set_write_enable(false);
|
||||||
|
set_enable(false);
|
||||||
|
while !drp_ready() {
|
||||||
|
// keep the clock signal until write is finished
|
||||||
|
one_clock_cycle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset(rst: bool) {
|
||||||
|
unsafe {
|
||||||
|
let val = if rst { 1 } else { 0 };
|
||||||
|
csr::cxp::downconn_txpll_reset_write(val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setup(timer: &mut GlobalTimer, settings: PLLSetting) {
|
||||||
|
if false {
|
||||||
|
info!("0x08 = {:#06x}", read(0x08));
|
||||||
|
info!("0x09 = {:#06x}", read(0x09));
|
||||||
|
info!("0x14 = {:#06x}", read(0x14));
|
||||||
|
info!("0x15 = {:#06x}", read(0x15));
|
||||||
|
info!("0x16 = {:#06x}", read(0x16));
|
||||||
|
info!("0x18 = {:#06x}", read(0x18));
|
||||||
|
info!("0x19 = {:#06x}", read(0x19));
|
||||||
|
info!("0x1A = {:#06x}", read(0x1A));
|
||||||
|
info!("0x28 = {:#06x}", read(0x28));
|
||||||
|
info!("0x4E = {:#06x}", read(0x4E));
|
||||||
|
info!("0x4F = {:#06x}", read(0x4F));
|
||||||
|
} else {
|
||||||
|
// Based on "DRP State Machine" from XAPP888
|
||||||
|
// hold reset HIGH during pll config
|
||||||
|
reset(true);
|
||||||
|
write(0x08, settings.clkout0_reg1);
|
||||||
|
write(0x09, settings.clkout0_reg2);
|
||||||
|
write(0x14, settings.clkfbout_reg1);
|
||||||
|
write(0x15, settings.clkfbout_reg2);
|
||||||
|
write(0x16, settings.div_reg);
|
||||||
|
write(0x18, settings.lock_reg1);
|
||||||
|
write(0x19, settings.lock_reg2);
|
||||||
|
write(0x1A, settings.lock_reg3);
|
||||||
|
write(0x28, settings.power_reg);
|
||||||
|
write(0x4E, settings.filt_reg1);
|
||||||
|
write(0x4F, settings.filt_reg2);
|
||||||
|
reset(false);
|
||||||
|
|
||||||
|
// wait for the pll to lock
|
||||||
|
timer.delay_us(100);
|
||||||
|
|
||||||
|
let locked = unsafe { csr::cxp::downconn_txpll_locked_read() == 1 };
|
||||||
|
info!("txusrclk locked = {}", locked);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_txusrclk_config(speed: CXP_SPEED) -> PLLSetting {
|
||||||
|
match speed {
|
||||||
|
CXP_SPEED::CXP_1 => {
|
||||||
|
// CLKFBOUT_MULT = 8, DIVCLK_DIVIDE = 1 , CLKOUT0_DIVIDE = 16
|
||||||
|
// TXUSRCLK=62.5MHz
|
||||||
|
PLLSetting {
|
||||||
|
clkout0_reg1: 0x1208, //0x08
|
||||||
|
clkout0_reg2: 0x0000, //0x09
|
||||||
|
clkfbout_reg1: 0x1104, //0x14
|
||||||
|
clkfbout_reg2: 0x0000, //0x15
|
||||||
|
div_reg: 0x1041, //0x16
|
||||||
|
lock_reg1: 0x03e8, //0x18
|
||||||
|
lock_reg2: 0x5801, //0x19
|
||||||
|
lock_reg3: 0xdbe9, //0x1A
|
||||||
|
power_reg: 0x0000, //0x28
|
||||||
|
filt_reg1: 0x9808, //0x4E
|
||||||
|
filt_reg2: 0x9100, //0x4F
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CXP_SPEED::CXP_2 => {
|
||||||
|
// CLKFBOUT_MULT = 8, DIVCLK_DIVIDE = 1 , CLKOUT0_DIVIDE = 8
|
||||||
|
// TXUSRCLK=125MHz
|
||||||
|
PLLSetting {
|
||||||
|
clkout0_reg1: 0x1104, //0x08
|
||||||
|
clkout0_reg2: 0x0000, //0x09
|
||||||
|
clkfbout_reg1: 0x1104, //0x14
|
||||||
|
clkfbout_reg2: 0x0000, //0x15
|
||||||
|
div_reg: 0x1041, //0x16
|
||||||
|
lock_reg1: 0x03e8, //0x18
|
||||||
|
lock_reg2: 0x5801, //0x19
|
||||||
|
lock_reg3: 0xdbe9, //0x1A
|
||||||
|
power_reg: 0x0000, //0x28
|
||||||
|
filt_reg1: 0x9808, //0x4E
|
||||||
|
filt_reg2: 0x9100, //0x4F
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CXP_SPEED::CXP_3 => {
|
||||||
|
// CLKFBOUT_MULT = 10, DIVCLK_DIVIDE = 1 , CLKOUT0_DIVIDE = 8
|
||||||
|
// TXUSRCLK=125MHz
|
||||||
|
PLLSetting {
|
||||||
|
clkout0_reg1: 0x1104, //0x08
|
||||||
|
clkout0_reg2: 0x0000, //0x09
|
||||||
|
clkfbout_reg1: 0x1145, //0x14
|
||||||
|
clkfbout_reg2: 0x0000, //0x15
|
||||||
|
div_reg: 0x1041, //0x16
|
||||||
|
lock_reg1: 0x03e8, //0x18
|
||||||
|
lock_reg2: 0x7001, //0x19
|
||||||
|
lock_reg3: 0xf3e9, //0x1A
|
||||||
|
power_reg: 0x0000, //0x28
|
||||||
|
filt_reg1: 0x9908, //0x4E
|
||||||
|
filt_reg2: 0x1900, //0x4F
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CXP_SPEED::CXP_5 => {
|
||||||
|
// CLKFBOUT_MULT = 8, DIVCLK_DIVIDE = 1 , CLKOUT0_DIVIDE = 4
|
||||||
|
// TXUSRCLK=250MHz
|
||||||
|
PLLSetting {
|
||||||
|
clkout0_reg1: 0x1082, //0x08
|
||||||
|
clkout0_reg2: 0x0000, //0x09
|
||||||
|
clkfbout_reg1: 0x1104, //0x14
|
||||||
|
clkfbout_reg2: 0x0000, //0x15
|
||||||
|
div_reg: 0x1041, //0x16
|
||||||
|
lock_reg1: 0x03e8, //0x18
|
||||||
|
lock_reg2: 0x5801, //0x19
|
||||||
|
lock_reg3: 0xdbe9, //0x1A
|
||||||
|
power_reg: 0x0000, //0x28
|
||||||
|
filt_reg1: 0x9808, //0x4E
|
||||||
|
filt_reg2: 0x9100, //0x4F
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CXP_SPEED::CXP_6 => {
|
||||||
|
// CLKFBOUT_MULT = 10, DIVCLK_DIVIDE = 1 , CLKOUT0_DIVIDE = 4
|
||||||
|
// TXUSRCLK=312.5MHz
|
||||||
|
PLLSetting {
|
||||||
|
clkout0_reg1: 0x1082, //0x08
|
||||||
|
clkout0_reg2: 0x0000, //0x09
|
||||||
|
clkfbout_reg1: 0x1145, //0x14
|
||||||
|
clkfbout_reg2: 0x0000, //0x15
|
||||||
|
div_reg: 0x1041, //0x16
|
||||||
|
lock_reg1: 0x03e8, //0x18
|
||||||
|
lock_reg2: 0x7001, //0x19
|
||||||
|
lock_reg3: 0xf3e9, //0x1A
|
||||||
|
power_reg: 0x0000, //0x28
|
||||||
|
filt_reg1: 0x9908, //0x4E
|
||||||
|
filt_reg2: 0x1900, //0x4F
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CXP_SPEED::CXP_10 => {
|
||||||
|
// CLKFBOUT_MULT = 8, DIVCLK_DIVIDE = 1 , CLKOUT0_DIVIDE = 2
|
||||||
|
// TXUSRCLK=500MHz
|
||||||
|
PLLSetting {
|
||||||
|
clkout0_reg1: 0x1041, //0x08
|
||||||
|
clkout0_reg2: 0x0000, //0x09
|
||||||
|
clkfbout_reg1: 0x1104, //0x14
|
||||||
|
clkfbout_reg2: 0x0000, //0x15
|
||||||
|
div_reg: 0x1041, //0x16
|
||||||
|
lock_reg1: 0x03e8, //0x18
|
||||||
|
lock_reg2: 0x5801, //0x19
|
||||||
|
lock_reg3: 0xdbe9, //0x1A
|
||||||
|
power_reg: 0x0000, //0x28
|
||||||
|
filt_reg1: 0x9808, //0x4E
|
||||||
|
filt_reg2: 0x9100, //0x4F
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CXP_SPEED::CXP_12 => {
|
||||||
|
// CLKFBOUT_MULT = 10, DIVCLK_DIVIDE = 1 , CLKOUT0_DIVIDE = 2
|
||||||
|
// TXUSRCLK=625MHz
|
||||||
|
PLLSetting {
|
||||||
|
clkout0_reg1: 0x1041, //0x08
|
||||||
|
clkout0_reg2: 0x0000, //0x09
|
||||||
|
clkfbout_reg1: 0x1145, //0x14
|
||||||
|
clkfbout_reg2: 0x0000, //0x15
|
||||||
|
div_reg: 0x1041, //0x16
|
||||||
|
lock_reg1: 0x03e8, //0x18
|
||||||
|
lock_reg2: 0x7001, //0x19
|
||||||
|
lock_reg3: 0xf3e9, //0x1A
|
||||||
|
power_reg: 0x0000, //0x28
|
||||||
|
filt_reg1: 0x9908, //0x4E
|
||||||
|
filt_reg2: 0x1900, //0x4F
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue