forked from M-Labs/artiq-zynq
Compare commits
No commits in common. "2aef11143a6a919c1eb86611bf1bbf70b483dcf0" and "6c53447808e4bf95fe935664f6191594eda91294" have entirely different histories.
2aef11143a
...
6c53447808
|
@ -2,20 +2,131 @@ from migen import *
|
|||
from misoc.interconnect.csr import *
|
||||
from misoc.cores.liteeth_mini.mac.crc import LiteEthMACCRCEngine
|
||||
|
||||
from artiq.gateware.drtio.core import ChannelInterface
|
||||
|
||||
from cxp_downconn import CXP_DownConn
|
||||
from cxp_upconn import CXP_UpConn
|
||||
|
||||
class CXP(Module, AutoCSR):
|
||||
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)
|
||||
|
||||
# FIFOs with transmission priority
|
||||
# 0: Trigger packet
|
||||
# 1: IO acknowledgment for trigger packet
|
||||
# 2: All other packets
|
||||
|
||||
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):
|
||||
width = 32
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
from migen import *
|
||||
from migen.genlib.resetsync import AsyncResetSynchronizer
|
||||
from migen.genlib.cdc import MultiReg, PulseSynchronizer
|
||||
|
||||
from misoc.cores.code_8b10b import Encoder, Decoder
|
||||
from misoc.interconnect.csr import *
|
||||
|
@ -7,352 +8,140 @@ from misoc.interconnect.csr import *
|
|||
from artiq.gateware.drtio.transceiver.gtx_7series_init import *
|
||||
|
||||
from operator import add
|
||||
from math import ceil
|
||||
from functools import reduce
|
||||
|
||||
class CXP_DownConn(Module, AutoCSR):
|
||||
def __init__(self, refclk, pads, sys_clk_freq, debug_sma, pmod_pads):
|
||||
self.rx_start_init = CSRStorage()
|
||||
self.rx_restart = CSR()
|
||||
|
||||
self.tx_start_init = CSRStorage()
|
||||
self.tx_restart = CSR()
|
||||
self.txenable = CSRStorage()
|
||||
|
||||
|
||||
self.txinit_phaligndone = CSRStatus()
|
||||
self.rxinit_phaligndone = CSRStatus()
|
||||
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,
|
||||
)
|
||||
]
|
||||
|
||||
# Changes the phase of the transceiver RX clock to align the comma to
|
||||
# the LSBs of RXDATA, fixing the latency.
|
||||
#
|
||||
# This is implemented by repeatedly resetting the transceiver until it
|
||||
# gives out the correct phase. Each reset gives a random phase.
|
||||
#
|
||||
# If Xilinx had designed the GTX transceiver correctly, RXSLIDE_MODE=PMA
|
||||
# would achieve this faster and in a cleaner way. But:
|
||||
# * 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.
|
||||
# Those design flaws make RXSLIDE_MODE=PMA yet another broken and useless
|
||||
# transceiver "feature".
|
||||
#
|
||||
# Warning: Xilinx transceivers are LSB first, and comma needs to be flipped
|
||||
# compared to the usual 8b10b binary representation.
|
||||
class Manual_Aligner(Module):
|
||||
def __init__(self, comma, check_cycles=20000):
|
||||
|
||||
self.rxslide = Signal()
|
||||
class CXP_BruteforceClockAligner(Module):
|
||||
def __init__(self, comma, sys_clk_freq, check_period):
|
||||
self.rxdata = Signal(20)
|
||||
self.restart = Signal()
|
||||
|
||||
self.ready = Signal()
|
||||
|
||||
# # #
|
||||
|
||||
checks_reset = Signal()
|
||||
error_seen = Signal()
|
||||
comma_seen = Signal()
|
||||
|
||||
rx1cnt = Signal(max=11)
|
||||
|
||||
comma_n = ~comma & 0b1111111111
|
||||
self.sync.cxp_gtx_rx += [
|
||||
rx1cnt.eq(reduce(add, [self.rxdata[i] for i in range(10)])),
|
||||
If(checks_reset,
|
||||
error_seen.eq(0)
|
||||
).Elif((rx1cnt != 4) & (rx1cnt != 5) & (rx1cnt != 6),
|
||||
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)
|
||||
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)
|
||||
)
|
||||
)
|
||||
]
|
||||
|
||||
# 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)
|
||||
checks_reset = PulseSynchronizer("sys", "cxp_gtx_rx")
|
||||
self.submodules += checks_reset
|
||||
|
||||
fsm = ClockDomainsRenamer("cxp_gtx_rx")(FSM(reset_state="IDLE"))
|
||||
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 += [
|
||||
rx1cnt.eq(reduce(add, [self.rxdata[i] for i in range(10)])),
|
||||
If(checks_reset.o,
|
||||
error_seen_rxclk.eq(0)
|
||||
).Elif((rx1cnt != 4) & (rx1cnt != 5) & (rx1cnt != 6),
|
||||
error_seen_rxclk.eq(1)
|
||||
)
|
||||
]
|
||||
|
||||
fsm = FSM(reset_state="WAIT_COMMA")
|
||||
self.submodules += fsm
|
||||
|
||||
fsm.act("IDLE",
|
||||
slide_timer.wait.eq(1),
|
||||
If(slide_timer.done,
|
||||
fsm.act("WAIT_COMMA",
|
||||
If(check,
|
||||
# Errors are still OK at this stage, as the transceiver
|
||||
# has just been reset and may output garbage data.
|
||||
If(comma_seen,
|
||||
NextState("READY"),
|
||||
NextState("WAIT_NOERROR")
|
||||
).Else(
|
||||
NextState("SLIDING")
|
||||
self.restart.eq(1)
|
||||
),
|
||||
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",
|
||||
reset_check_counter.eq(1),
|
||||
self.ready.eq(1),
|
||||
If(counter == 0,
|
||||
NextValue(counter, check_cycles - 1),
|
||||
If(error_seen,
|
||||
NextState("IDLE"),
|
||||
)
|
||||
).Else(
|
||||
NextValue(counter, counter - 1),
|
||||
checks_reset.i.eq(1),
|
||||
self.restart.eq(1),
|
||||
NextState("WAIT_COMMA")
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class GTX(Module):
|
||||
|
||||
class CXP_DownConn(Module):
|
||||
# Settings:
|
||||
# * GTX reference clock @ 125MHz
|
||||
# * GTX data width = 20
|
||||
# * GTX PLL frequency @ 3.125GHz
|
||||
# * GTX line rate (TX & RX) @ 3.125Gb/s
|
||||
# * GTX TX/RX USRCLK @ PLL/datawidth = 156MHz
|
||||
def __init__(self, qpll, pads, sys_clk_freq, tx_mode="single", rx_mode="single"):
|
||||
def __init__(self, refclk, pads, sys_clk_freq, tx_mode="single", rx_mode="single"):
|
||||
assert tx_mode in ["single", "master", "slave"]
|
||||
assert rx_mode in ["single", "master", "slave"]
|
||||
|
||||
# linerate = USRCLK * datawidth
|
||||
pll_fbout_mult = 8
|
||||
txusr_pll_div = pll_fbout_mult*sys_clk_freq/qpll.tx_usrclk_freq # 20 is datawidth
|
||||
cpll_div = 4
|
||||
pll_div = int(40/cpll_div)
|
||||
|
||||
self.tx_restart = Signal()
|
||||
self.rx_restart = Signal()
|
||||
self.tx_restart = Signal()
|
||||
self.loopback_mode = Signal(3)
|
||||
|
||||
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.decoders = [ClockDomainsRenamer("cxp_gtx_rx")(
|
||||
(Decoder(True))) for _ in range(2)]
|
||||
|
||||
self.rx_ready = Signal()
|
||||
|
||||
# transceiver direct clock outputs
|
||||
# useful to specify clock constraints in a way palatable to Vivado
|
||||
|
@ -361,23 +150,20 @@ class GTX(Module):
|
|||
|
||||
# # #
|
||||
|
||||
cpllreset = Signal()
|
||||
cplllock = Signal()
|
||||
# TX generates cxp_tx clock, init must be in system domain
|
||||
# FIXME: 500e6 is used to fix Xx reset by holding gtxXxreset for a couple cycle more
|
||||
self.submodules.tx_init = tx_init = GTXInit(500e6, False, mode=tx_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.submodules.tx_init = tx_init = GTXInit(sys_clk_freq, False, mode=tx_mode)
|
||||
# RX receives restart commands from RTIO domain
|
||||
self.submodules.rx_init = rx_init = GTXInit(sys_clk_freq, True, mode=rx_mode)
|
||||
self.comb += [
|
||||
tx_init.cplllock.eq(qpll.lock),
|
||||
rx_init.cplllock.eq(qpll.lock)
|
||||
cpllreset.eq(tx_init.cpllreset),
|
||||
tx_init.cplllock.eq(cplllock),
|
||||
rx_init.cplllock.eq(cplllock)
|
||||
]
|
||||
|
||||
txdata = Signal(20)
|
||||
rxdata = Signal(20)
|
||||
|
||||
rxslide = Signal()
|
||||
# Note: the following parameters were set after consulting AR45360
|
||||
self.specials += \
|
||||
Instance("GTXE2_CHANNEL",
|
||||
|
@ -395,25 +181,29 @@ class GTX(Module):
|
|||
p_PD_TRANS_TIME_FROM_P2=0x3c,
|
||||
p_PD_TRANS_TIME_NONE_P2=0x3c,
|
||||
p_PD_TRANS_TIME_TO_P2=0x64,
|
||||
i_CPLLPD=1,
|
||||
|
||||
# Dynamic Tx/Rx divider
|
||||
i_TXRATE=self.tx_rate,
|
||||
i_RXRATE=self.rx_rate,
|
||||
|
||||
# QPLL
|
||||
i_QPLLCLK=qpll.clk,
|
||||
i_QPLLREFCLK=qpll.refclk,
|
||||
p_RXOUT_DIV=qpll.Xxout_div,
|
||||
p_TXOUT_DIV=qpll.Xxout_div,
|
||||
i_RXSYSCLKSEL=0b11, # use QPLL & QPLL's REFCLK
|
||||
i_TXSYSCLKSEL=0b11, # use QPLL & CPLL's REFCLK
|
||||
# CPLL
|
||||
p_CPLL_CFG=0xBC07DC,
|
||||
p_CPLL_FBDIV=cpll_div,
|
||||
p_CPLL_FBDIV_45=5,
|
||||
p_CPLL_REFCLK_DIV=1,
|
||||
p_RXOUT_DIV=2,
|
||||
p_TXOUT_DIV=2,
|
||||
p_CPLL_INIT_CFG=0x00001E,
|
||||
p_CPLL_LOCK_CFG=0x01E8,
|
||||
i_CPLLRESET=cpllreset,
|
||||
i_CPLLPD=cpllreset,
|
||||
o_CPLLLOCK=cplllock,
|
||||
i_CPLLLOCKEN=1,
|
||||
i_CPLLREFCLKSEL=0b001,
|
||||
i_TSTIN=2**20-1,
|
||||
i_GTREFCLK0=refclk,
|
||||
|
||||
# TX clock
|
||||
p_TXBUF_EN="FALSE",
|
||||
p_TX_XCLK_SEL="TXUSR",
|
||||
o_TXOUTCLK=self.txoutclk,
|
||||
# i_TXSYSCLKSEL=0b00,
|
||||
i_TXSYSCLKSEL=0b00,
|
||||
i_TXOUTCLKSEL=0b11,
|
||||
|
||||
# TX Startup/Reset
|
||||
|
@ -484,7 +274,7 @@ class GTX(Module):
|
|||
|
||||
# RX clock
|
||||
i_RXDDIEN=1,
|
||||
# i_RXSYSCLKSEL=0b00,
|
||||
i_RXSYSCLKSEL=0b00,
|
||||
i_RXOUTCLKSEL=0b010,
|
||||
o_RXOUTCLK=self.rxoutclk,
|
||||
i_RXUSRCLK=ClockSignal("cxp_gtx_rx"),
|
||||
|
@ -507,22 +297,16 @@ class GTX(Module):
|
|||
# RX Byte and Word Alignment Attributes
|
||||
p_ALIGN_COMMA_DOUBLE="FALSE",
|
||||
p_ALIGN_COMMA_ENABLE=0b1111111111,
|
||||
p_ALIGN_COMMA_WORD=2,
|
||||
p_ALIGN_MCOMMA_DET="FALSE",
|
||||
p_ALIGN_COMMA_WORD=1,
|
||||
p_ALIGN_MCOMMA_DET="TRUE",
|
||||
p_ALIGN_MCOMMA_VALUE=0b1010000011,
|
||||
p_ALIGN_PCOMMA_DET="FALSE",
|
||||
p_ALIGN_PCOMMA_DET="TRUE",
|
||||
p_ALIGN_PCOMMA_VALUE=0b0101111100,
|
||||
p_SHOW_REALIGN_COMMA="FALSE",
|
||||
p_RXSLIDE_AUTO_WAIT=7,
|
||||
p_RXSLIDE_MODE="PCS",
|
||||
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
|
||||
p_RX_DISPERR_SEQ_MATCH="FALSE",
|
||||
p_DEC_MCOMMA_DETECT="TRUE",
|
||||
|
@ -590,47 +374,31 @@ class GTX(Module):
|
|||
# A PLL is used to generate the correct frequency for TXUSRCLK (UG476 Equation 3-1)
|
||||
self.clock_domains.cd_cxp_gtx_tx = ClockDomain()
|
||||
txpll_fb_clk = Signal()
|
||||
txpll_reset = Signal()
|
||||
txpll_locked = Signal()
|
||||
txoutclk_buf = Signal()
|
||||
txpll_clkout = Signal()
|
||||
|
||||
self.txpll_reset = Signal()
|
||||
self.pll_daddr = Signal(7)
|
||||
self.pll_dclk = Signal()
|
||||
self.pll_den = Signal()
|
||||
self.pll_din = Signal(16)
|
||||
self.pll_dwen = Signal()
|
||||
|
||||
self.txpll_locked = Signal()
|
||||
self.pll_dout = Signal(16)
|
||||
self.pll_dready = Signal()
|
||||
self.specials += [
|
||||
Instance("PLLE2_ADV",
|
||||
p_BANDWIDTH="HIGH",
|
||||
o_LOCKED=self.txpll_locked,
|
||||
i_RST=self.txpll_reset,
|
||||
o_LOCKED=txpll_locked,
|
||||
i_RST=txpll_reset,
|
||||
|
||||
p_CLKIN1_PERIOD=1e9/sys_clk_freq, # ns
|
||||
i_CLKIN1=txoutclk_buf,
|
||||
|
||||
# VCO @ 1.25GHz
|
||||
p_CLKFBOUT_MULT=pll_fbout_mult, p_DIVCLK_DIVIDE=1,
|
||||
p_CLKFBOUT_MULT=10, p_DIVCLK_DIVIDE=1,
|
||||
i_CLKFBIN=txpll_fb_clk, o_CLKFBOUT=txpll_fb_clk,
|
||||
|
||||
# frequency = linerate/20
|
||||
p_CLKOUT0_DIVIDE=txusr_pll_div, p_CLKOUT0_PHASE=0.0, o_CLKOUT0=txpll_clkout,
|
||||
# 156.25MHz
|
||||
p_CLKOUT0_DIVIDE=pll_div, p_CLKOUT0_PHASE=0.0, o_CLKOUT0=txpll_clkout,
|
||||
|
||||
# Dynamic Reconfiguration Ports
|
||||
i_DADDR = self.pll_daddr,
|
||||
i_DCLK = self.pll_dclk,
|
||||
i_DEN = self.pll_den,
|
||||
i_DI = self.pll_din,
|
||||
i_DWE = self.pll_dwen,
|
||||
o_DO = self.pll_dout,
|
||||
o_DRDY = self.pll_dready,
|
||||
# TODO: DRP for line rate change
|
||||
),
|
||||
Instance("BUFG", i_I=self.txoutclk, o_O=txoutclk_buf),
|
||||
Instance("BUFG", i_I=txpll_clkout, o_O=self.cd_cxp_gtx_tx.clk),
|
||||
AsyncResetSynchronizer(self.cd_cxp_gtx_tx, ~self.txpll_locked & ~tx_init.done)
|
||||
AsyncResetSynchronizer(self.cd_cxp_gtx_tx, ~txpll_locked & ~tx_init.done)
|
||||
]
|
||||
|
||||
# RX clocking
|
||||
|
@ -647,13 +415,12 @@ class GTX(Module):
|
|||
self.decoders[1].input.eq(rxdata[10:])
|
||||
]
|
||||
|
||||
|
||||
self.submodules.clk_aligner = clk_aligner = Manual_Aligner(0b0101111100)
|
||||
# 6e-3 is too slow for 3.25Gbps line rate
|
||||
clock_aligner = CXP_BruteforceClockAligner(0b0101111100, sys_clk_freq, check_period=1e-2)
|
||||
self.submodules += clock_aligner
|
||||
self.comb += [
|
||||
clk_aligner.rxdata.eq(rxdata),
|
||||
rxslide.eq(clk_aligner.rxslide),
|
||||
self.rx_ready.eq(clk_aligner.ready),
|
||||
|
||||
rx_init.restart.eq(self.rx_restart),
|
||||
tx_init.restart.eq(self.tx_restart),
|
||||
clock_aligner.rxdata.eq(rxdata),
|
||||
rx_init.restart.eq(clock_aligner.restart),
|
||||
self.rx_ready.eq(clock_aligner.ready),
|
||||
tx_init.restart.eq(self.tx_restart)
|
||||
]
|
||||
|
|
|
@ -162,22 +162,22 @@ class CXP_UpConn(Module, AutoCSR):
|
|||
p3.eq(self.tx_idle.word_idx == 3),
|
||||
]
|
||||
self.specials += [
|
||||
# # debug sma
|
||||
# Instance("OBUF", i_I=o, o_O=pads.p_tx),
|
||||
# Instance("OBUF", i_I=self.cd_cxp_upconn.clk, o_O=pads.n_rx),
|
||||
# debug sma
|
||||
Instance("OBUF", i_I=o, o_O=pads.p_tx),
|
||||
Instance("OBUF", i_I=self.cd_cxp_upconn.clk, o_O=pads.n_rx),
|
||||
|
||||
# # pmod 0-7 pin
|
||||
# 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.tx_fifos.pe.n, o_O=pmod[2]),
|
||||
# Instance("OBUF", i_I=ninth_bit, o_O=pmod[3]),
|
||||
# Instance("OBUF", i_I=word_bound, o_O=pmod[4]),
|
||||
# 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[2], o_O=pmod[6]),
|
||||
# # 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=p3, o_O=pmod[7]),
|
||||
# pmod 0-7 pin
|
||||
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.tx_fifos.pe.n, o_O=pmod[2]),
|
||||
Instance("OBUF", i_I=ninth_bit, o_O=pmod[3]),
|
||||
Instance("OBUF", i_I=word_bound, o_O=pmod[4]),
|
||||
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[2], o_O=pmod[6]),
|
||||
# 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=p3, o_O=pmod[7]),
|
||||
]
|
||||
self.symbol0 = CSR(9)
|
||||
self.symbol1 = CSR(9)
|
||||
|
|
|
@ -4,431 +4,67 @@ use log::info;
|
|||
|
||||
use crate::pl::csr;
|
||||
|
||||
pub struct CXP_DownConn_Settings {
|
||||
pub rxdiv: u8,
|
||||
pub qpll_fbdiv: u8,
|
||||
}
|
||||
pub fn main(timer: &mut GlobalTimer) {
|
||||
unsafe {
|
||||
info!("turning on pmc loopback mode...");
|
||||
csr::cxp::loopback_mode_write(0b010); // Near-End PMA Loopback
|
||||
|
||||
#[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,
|
||||
}
|
||||
// enable cxp gtx clock domains
|
||||
csr::cxp::tx_start_init_write(1);
|
||||
csr::cxp::rx_start_init_write(1);
|
||||
|
||||
fn loopback_testing(timer: &mut GlobalTimer, data: u8, control_bit: u8) {
|
||||
info!("waiting for QPLL/CPLL to lock...");
|
||||
timer.delay_us(50_000);
|
||||
info!("tx_phaligndone = {} ", csr::cxp::txinit_phaligndone_read(),);
|
||||
|
||||
// enable txdata tranmission thought MGTXTXP, required by PMA loopback
|
||||
csr::cxp::txenable_write(1);
|
||||
|
||||
loopback_testing(timer, 0x00, 0);
|
||||
}
|
||||
|
||||
fn loopback_testing(timer: &mut GlobalTimer, data: u8, control_bit: u8) {
|
||||
unsafe {
|
||||
// send K28_5 for CDR to align
|
||||
const K28_5: u8 = 0xBC;
|
||||
const K28_1: u8 = 0x3C;
|
||||
const D21_5: u8 = 21 | 5 << 5;
|
||||
|
||||
// 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);
|
||||
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...");
|
||||
timer.delay_us(50_000);
|
||||
// while csr::cxp::downconn_rx_ready_read() != 1 {}
|
||||
// info!("rx ready!");
|
||||
while csr::cxp::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);
|
||||
csr::cxp::data_1_write(data);
|
||||
csr::cxp::control_bit_1_write(control_bit);
|
||||
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()
|
||||
"data[0] = {:#04x} control bit = {:#b} encoded = {:#012b}",
|
||||
csr::cxp::data_0_read(),
|
||||
csr::cxp::control_bit_0_read(),
|
||||
csr::cxp::encoded_0_read(),
|
||||
);
|
||||
timer.delay_us(1_000_000);
|
||||
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 {
|
||||
timer.delay_us(100);
|
||||
// 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!(
|
||||
"decoded_data[0] = {:#04x} decoded_k[0] = {:#b} decoded_data[1] = {:#04x} decoded_k[1] = {:#b}",
|
||||
csr::cxp::decoded_data_0_read(),
|
||||
csr::cxp::decoded_k_0_read(),
|
||||
csr::cxp::decoded_data_1_read(),
|
||||
csr::cxp::decoded_k_1_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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue