1
0
Fork 0

Compare commits

...

22 Commits

Author SHA1 Message Date
morgan 2aef11143a cxp downconn fw: doc clean up 2024-08-02 12:40:16 +08:00
morgan 77bff39212 cxp downconn fw: add manual alignment test 2024-08-02 12:36:46 +08:00
morgan 160fcc657a cxp downconn: remove bruteforce aligner 2024-08-02 12:36:46 +08:00
morgan bf1fd2d79b cxp downconn: add manual alignment & rxrestart 2024-08-02 12:36:46 +08:00
morgan 452c3cee64 cxp downconn fw: add CXP datarate update printout 2024-08-01 17:38:12 +08:00
morgan 4436dc930e cxp downconn fw: add qpll linerate change 2024-08-01 12:33:22 +08:00
morgan 5a40422f1d cxp downconn: fix tx reset & cleanup 2024-08-01 11:21:36 +08:00
morgan e0369d2eb2 cxp downconn fw: add DRP for linerate 2024-07-31 16:10:25 +08:00
morgan 1e87428c68 cxp downconn: add DRP for tx/rx divider 2024-07-31 16:10:06 +08:00
morgan 0e4fb4cbfe cxp downconn fw: add QPLL setup 2024-07-29 17:23:00 +08:00
morgan ccd3e6618a 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
2024-07-29 17:22:32 +08:00
morgan 563e06ca8d cxp upconn: disable debug sma & pmod output 2024-07-29 17:20:10 +08:00
morgan 57d6e9a669 cxp downconn fw: update csr function name 2024-07-26 17:36:29 +08:00
morgan 1f0154d5b2 cxp: remove all cxp downconn interface 2024-07-26 17:36:16 +08:00
morgan a41e78d3d3 cxp downconn: add csr interface here 2024-07-26 17:36:06 +08:00
morgan 52f77a6331 cxp downconn fw: remove data[4:8] 2024-07-26 17:01:54 +08:00
morgan 1a210a010a cxp: remove data[4:8] 2024-07-26 17:01:26 +08:00
morgan 398eb135c7 cxp downconn: push IDLE word to buf b4 tx&rx init 2024-07-26 16:26:08 +08:00
morgan 865434763d cxp downconn: fix bruteforcealigner 2024-07-26 16:24:18 +08:00
morgan 4ba1c5ef2e cxp downconn fw: add idle data 2024-07-26 15:24:59 +08:00
morgan faef1d2dff cxp downconn: add interface for buildin aligner 2024-07-26 15:24:58 +08:00
morgan 20e388d043 cxp downconn: explore cpll & txusrclk_pll divider 2024-07-25 17:39:11 +08:00
4 changed files with 795 additions and 309 deletions

View File

@ -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

View File

@ -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,
If(comma_seen & ~error_seen,
NextState("READY")
).Else(
self.restart.eq(1),
NextState("WAIT_COMMA")
),
checks_reset.i.eq(1)
) )
fsm.act("SLIDING",
self.rxslide.eq(1),
checks_reset.eq(1),
NextState("IDLE")
) )
fsm.act("READY", fsm.act("READY",
reset_check_counter.eq(1),
self.ready.eq(1), self.ready.eq(1),
If(counter == 0,
NextValue(counter, check_cycles - 1),
If(error_seen, If(error_seen,
checks_reset.i.eq(1), NextState("IDLE"),
self.restart.eq(1), )
NextState("WAIT_COMMA") ).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),
] ]

View File

@ -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)

View File

@ -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 {
unsafe { pub rxdiv: u8,
info!("turning on pmc loopback mode..."); pub qpll_fbdiv: u8,
csr::cxp::loopback_mode_write(0b010); // Near-End PMA Loopback }
// enable cxp gtx clock domains #[derive(Clone, Copy, Debug)]
csr::cxp::tx_start_init_write(1); #[allow(non_camel_case_types)]
csr::cxp::rx_start_init_write(1); pub enum CXP_SPEED {
CXP_1,
info!("waiting for QPLL/CPLL to lock..."); CXP_2,
timer.delay_us(50_000); CXP_3,
info!("tx_phaligndone = {} ", csr::cxp::txinit_phaligndone_read(),); CXP_5,
CXP_6,
// enable txdata tranmission thought MGTXTXP, required by PMA loopback CXP_10,
csr::cxp::txenable_write(1); CXP_12,
loopback_testing(timer, 0x00, 0);
} }
fn loopback_testing(timer: &mut GlobalTimer, data: u8, control_bit: u8) { fn loopback_testing(timer: &mut GlobalTimer, data: u8, control_bit: u8) {
unsafe { unsafe {
// send K28_5 for CDR to align // send K28_5 for CDR to align
const K28_5: u8 = 0xBC; const K28_5: u8 = 0xBC;
csr::cxp::data_0_write(K28_5); const K28_1: u8 = 0x3C;
csr::cxp::control_bit_0_write(1); const D21_5: u8 = 21 | 5 << 5;
csr::cxp::data_1_write(K28_5);
csr::cxp::control_bit_1_write(1); // NOTE: testing with IDLE word + manual comma alignment
// 62.5MHz OK
// 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],
];
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);
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
csr::cxp::downconn_txenable_write(1);
info!("waiting for rx to align..."); info!("waiting for rx to align...");
while csr::cxp::rx_ready_read() != 1 {} timer.delay_us(50_000);
info!("rx ready!"); // while csr::cxp::downconn_rx_ready_read() != 1 {}
// info!("rx ready!");
csr::cxp::data_1_write(data); // csr::cxp::data_3_write(data);
csr::cxp::control_bit_1_write(control_bit); // csr::cxp::control_bit_3_write(control_bit);
println!( // println!(
"data[0] = {:#04x} control bit = {:#b} encoded = {:#012b}", // "data[0] = {:#04x} control bit = {:#b} encoded = {:#012b}",
csr::cxp::data_0_read(), // csr::cxp::downconn_data_0_read(),
csr::cxp::control_bit_0_read(), // csr::cxp::downconn_control_bit_0_read(),
csr::cxp::encoded_0_read(), // csr::cxp::downconn_encoded_0_read(),
); // );
println!( // println!(
"data[1] = {:#04x} control bit = {:#b} encoded = {:#012b}", // "data[1] = {:#04x} control bit = {:#b} encoded = {:#012b}",
csr::cxp::data_1_read(), // csr::cxp::downconn_data_1_read(),
csr::cxp::control_bit_1_read(), // csr::cxp::downconn_control_bit_1_read(),
csr::cxp::encoded_1_read(), // csr::cxp::downconn_encoded_1_read(),
); // );
for _ in 0..20 { // for _ in 0..20 {
timer.delay_us(100); 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!( // println!(
// "data[0] = {:#012b} data[1] = {:#012b}", // "data[0] = {:#012b} data[1] = {:#012b}",
// csr::cxp::rxdata_0_read(), // csr::cxp::rxdata_0_read(),
// csr::cxp::rxdata_1_read(), // csr::cxp::rxdata_1_read(),
// ); // );
println!( // println!(
"decoded_data[0] = {:#04x} decoded_k[0] = {:#b} decoded_data[1] = {:#04x} decoded_k[1] = {:#b}", // "decoded_data[0] = {:#04x} decoded_k[0] = {:#b} decoded_data[1] = {:#04x} decoded_k[1] = {:#b}",
csr::cxp::decoded_data_0_read(), // csr::cxp::downconn_decoded_data_0_read(),
csr::cxp::decoded_k_0_read(), // csr::cxp::downconn_decoded_k_0_read(),
csr::cxp::decoded_data_1_read(), // csr::cxp::downconn_decoded_data_1_read(),
csr::cxp::decoded_k_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!("==============================================================================");
}
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 {
csr::cxp::downconn_pll_dclk_write(1);
csr::cxp::downconn_pll_dclk_write(0);
}
}
fn set_addr(address: u8) {
unsafe {
csr::cxp::downconn_pll_daddr_write(address);
}
}
fn set_data(value: u16) {
unsafe {
csr::cxp::downconn_pll_din_write(value);
}
}
fn set_enable(en: bool) {
unsafe {
let val = if en { 1 } else { 0 };
csr::cxp::downconn_pll_den_write(val);
}
}
fn set_write_enable(en: bool) {
unsafe {
let val = if en { 1 } else { 0 };
csr::cxp::downconn_pll_dwen_write(val);
}
}
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
}
} }
} }
} }