1
0
Fork 0

Compare commits

...

12 Commits

2 changed files with 569 additions and 254 deletions

View File

@ -1,6 +1,5 @@
from migen import *
from migen.genlib.cdc import MultiReg, PulseSynchronizer
from migen.genlib.misc import WaitTimer
from migen.genlib.resetsync import AsyncResetSynchronizer
from misoc.cores.code_8b10b import Encoder, Decoder
@ -20,14 +19,10 @@ class CXP_DownConn(Module, AutoCSR):
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()
@ -47,7 +42,6 @@ class CXP_DownConn(Module, AutoCSR):
# 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),
@ -58,11 +52,76 @@ class CXP_DownConn(Module, AutoCSR):
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 Channels DRP
self.tx_div = CSRStorage(3)
self.rx_div = CSRStorage(3)
self.gtx_daddr = CSRStorage(9)
self.gtx_dread = CSR()
self.gtx_din_stb = CSR()
self.gtx_din = CSRStorage(16)
self.gtx_dout = CSRStatus(16)
self.gtx_dready = CSR()
self.comb += gtx.dclk.eq(ClockSignal("sys"))
self.sync += [
gtx.tx_rate.eq(self.tx_div.storage),
gtx.rx_rate.eq(self.rx_div.storage),
gtx.den.eq(0),
gtx.dwen.eq(0),
If(self.gtx_dread.re,
gtx.den.eq(1),
gtx.daddr.eq(self.gtx_daddr.storage),
).Elif(self.gtx_din_stb.re,
gtx.den.eq(1),
gtx.dwen.eq(1),
gtx.daddr.eq(self.gtx_daddr.storage),
gtx.din.eq(self.gtx_din.storage),
),
If(gtx.dready,
self.gtx_dready.w.eq(1),
self.gtx_dout.status.eq(gtx.dout),
),
If(self.gtx_dready.re,
self.gtx_dready.w.eq(0),
),
]
# QPLL DRP
self.qpll_daddr = CSRStorage(8)
self.qpll_dread = CSR()
self.qpll_din_stb = CSR()
self.qpll_din = CSRStorage(16)
self.qpll_dout = CSRStatus(16)
self.qpll_dready = CSR()
self.comb += qpll.dclk.eq(ClockSignal("sys"))
self.sync += [
qpll.den.eq(0),
qpll.dwen.eq(0),
If(self.qpll_dread.re,
qpll.den.eq(1),
qpll.daddr.eq(self.qpll_daddr.storage),
).Elif(self.qpll_din_stb.re,
qpll.den.eq(1),
qpll.dwen.eq(1),
qpll.daddr.eq(self.qpll_daddr.storage),
qpll.din.eq(self.qpll_din.storage),
),
If(qpll.dready,
self.qpll_dready.w.eq(1),
self.qpll_dout.status.eq(qpll.dout),
),
If(self.qpll_dready.re,
self.qpll_dready.w.eq(0),
),
]
# DEBUG: txusrclk PLL DRG
@ -101,18 +160,22 @@ class CXP_DownConn(Module, AutoCSR):
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.comma_det.aligner_en_rxclk, o_O=pmod_pads[0]),
Instance("OBUF", i_I=gtx.comma_det.rxinit_done, o_O=pmod_pads[1]),
Instance("OBUF", i_I=gtx.comma_det.restart, o_O=pmod_pads[2]),
Instance("OBUF", i_I=gtx.comma_det.comma_aligned, o_O=pmod_pads[3]),
Instance("OBUF", i_I=gtx.comma_det.ready, o_O=pmod_pads[4]),
Instance("OBUF", i_I=gtx.comma_det.valid_data, o_O=pmod_pads[5]),
# Instance("OBUF", i_I=, o_O=pmod_pads[7]),
Instance("OBUF", i_I=gtx.comma_det.word_aligned, o_O=pmod_pads[0]),
Instance("OBUF", i_I=gtx.comma_det.restart, o_O=pmod_pads[1]),
Instance("OBUF", i_I=gtx.comma_det.aligner_en_rxclk, o_O=pmod_pads[2]),
Instance("OBUF", i_I=gtx.comma_det.check_reset, o_O=pmod_pads[3]),
Instance("OBUF", i_I=gtx.comma_det.comma_aligned, o_O=pmod_pads[4]),
Instance("OBUF", i_I=gtx.comma_det.comma_seen, o_O=pmod_pads[5]),
Instance("OBUF", i_I=gtx.comma_det.has_error, o_O=pmod_pads[6]),
Instance("OBUF", i_I=gtx.comma_det.ready, o_O=pmod_pads[7]),
# Instance("OBUF", i_I=gtx.dclk, o_O=pmod_pads[0]),
# Instance("OBUF", i_I=gtx.den, o_O=pmod_pads[1]),
# Instance("OBUF", i_I=gtx.dwen, o_O=pmod_pads[2]),
# Instance("OBUF", i_I=gtx.dready, o_O=pmod_pads[3]),
]
# DEBUG: datain
counter_max = 2
counter = Signal(max=counter_max)
self.data_0 = CSRStorage(8)
self.data_1 = CSRStorage(8)
@ -132,28 +195,24 @@ class CXP_DownConn(Module, AutoCSR):
self.decoded_k_0 = CSRStatus()
self.decoded_k_1 = CSRStatus()
self.shifted = CSRStatus(9)
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.gtx.encoder.d[0].eq(0xBC),
self.gtx.encoder.k[0].eq(1),
self.gtx.encoder.d[1].eq(0x3C),
self.gtx.encoder.k[1].eq(1),
self.gtx.encoder.d[2].eq(0x3C),
self.gtx.encoder.k[2].eq(1),
self.gtx.encoder.d[3].eq(0xB5),
self.gtx.encoder.k[3].eq(0),
self.encoded_0.status.eq(self.gtx.encoder.output[0]),
self.encoded_1.status.eq(self.gtx.encoder.output[1]),
]
# keep it odd, so it will show data[n] where n is odd
stb_timer = Signal(reset=10, max=11)
self.sync.cxp_gtx_rx += [
If(stb_timer == 0,
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),
@ -161,9 +220,10 @@ class CXP_DownConn(Module, AutoCSR):
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),
# If(self.gtx.clk_aligner.comma_det.detected,
# self.shifted.status.eq(self.gtx.clk_aligner.comma_det.bitshift),
# )
stb_timer.eq(stb_timer.reset),
).Else(
stb_timer.eq(stb_timer - 1),
)
]
@ -184,37 +244,35 @@ class QPLL(Module):
self.lock = Signal()
self.reset = Signal()
# Dynamic Reconfiguration Ports
self.daddr = Signal(8)
self.dclk = Signal()
self.den = Signal()
self.dwen = Signal()
self.din = Signal(16)
self.dout = Signal(16)
self.dready = Signal()
# # #
# WARNING: VCO cannot do 12.5GHz on ZC706
# VCO freq = sys*qpll_fbdiv
# PLL output = VCO/2
qpll_fbdiv = 0b0100100000
qpll_fbdiv = 0b0100100000 # 80 div
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_fbdiv = 0b0101110000 # 100 div
# qpll_fbdiv_ratio = 1
# QPLL reset
pll_reset_cycles = ceil(sys_clk_freq/125e6)
pll_reset_timer = WaitTimer(pll_reset_cycles)
self.submodules += pll_reset_timer
# fbdiv_real = 100
# refclk_div = 2
# self.Xxout_div = 2
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.tx_usrclk_freq = (sys_clk_freq*fbdiv_real/self.Xxout_div)/40
self.specials += [
Instance("GTXE2_COMMON",
@ -222,7 +280,7 @@ class QPLL(Module):
i_GTREFCLK0=refclk,
i_QPLLPD=0,
i_QPLLRESET=reset,
i_QPLLRESET=self.reset,
i_QPLLLOCKEN=1,
o_QPLLLOCK=self.lock,
o_QPLLOUTCLK=self.clk,
@ -256,15 +314,24 @@ class QPLL(Module):
i_RCALENB=0b1,
i_QPLLRSVD1=0b0,
i_QPLLRSVD2=0b11111,
# Dynamic Reconfiguration Ports
i_DRPADDR=self.daddr,
i_DRPCLK=self.dclk,
i_DRPEN=self.den,
i_DRPWE=self.dwen,
i_DRPDI=self.din,
o_DRPDO=self.dout,
o_DRPRDY=self.dready,
)
]
# Warning: Xilinx transceivers are LSB first, and comma needs to be flipped
# compared to the usual 8b10b binary representation.
class Comma_Detector(Module):
def __init__(self, comma, check_period=50_000, width=20):
self.data = Signal(width)
self.rxinit_done = Signal()
def __init__(self, comma, check_period=1_000_000):
self.data = Signal(20)
self.word_aligned = Signal()
self.aligner_en_rxclk = Signal()
self.ready = Signal()
@ -274,93 +341,142 @@ class Comma_Detector(Module):
self.comma_aligned = Signal()
self.valid_data = Signal()
# The built-in RXBYTEISALIGNED can be falsely asserted at linerate higher than 5Gbps
# - UG476 (v1.12.1) p.228
self.submodules.recheck_ps = recheck_ps = PulseSynchronizer("sys", "cxp_gtx_rx")
aligned = Signal()
self.specials += MultiReg(self.comma_aligned, aligned)
valid_data = Signal()
self.specials += MultiReg(self.valid_data, valid_data)
comma_n = ~comma & 0b1111111111
rx1cnt = Signal(max=11)
self.sync.cxp_gtx_rx += [
rx1cnt.eq(reduce(add, [self.data[i] for i in range(10)])),
If(recheck_ps.o,
self.comma_aligned.eq(0)
).Elif((self.data[:10] == comma) | (self.data[:10] == comma_n),
self.comma_aligned.eq(1)
),
If(recheck_ps.o,
self.valid_data.eq(0)
).Elif((rx1cnt == 4) | (rx1cnt == 5) | (rx1cnt == 6),
self.valid_data.eq(1)
),
]
# The validity of data & comma are checked externally
check_counter = Signal(reset=check_period-1, max=check_period)
check = Signal()
aligner_en_sys = Signal()
self.specials += MultiReg(aligner_en_sys, self.aligner_en_rxclk, odomain="cxp_gtx_rx")
# check = Signal()
self.sync += [
check.eq(0),
self.restart.eq(0),
# check.eq(0),
# check_reset.i.eq(0),
If(~self.ready,
If(check_counter == 0,
check_counter.eq(check_counter.reset),
check.eq(1),
# check.eq(1),
self.restart.eq(1),
# check_reset.i.eq(1),
).Else(
check_counter.eq(check_counter - 1),
)
)
]
# WIP:
self.submodules.fsm = fsm = FSM(reset_state="IDLE")
comma_n = ~comma & 0b1111111111
fsm.act("IDLE",
aligner_en_sys.eq(1),
If(check,
recheck_ps.i.eq(1),
If(aligned,
NextState("WAIT_NO_ERROR"),
has_error = Signal()
comma_aligned = Signal()
comma_seen = Signal()
error_seen = Signal()
one_counts = Signal(max=11)
counter_period = 5000
counter = Signal(reset=counter_period-1, max=counter_period)
check_reset = Signal()
check = Signal()
self.sync.cxp_gtx_rx += [
check.eq(0),
If(counter == 0,
counter.eq(counter.reset),
check.eq(1),
).Else(
self.restart.eq(1),
)
counter.eq(counter - 1),
),
has_error.eq(0),
one_counts.eq(reduce(add, [self.data[i] for i in range(10, 20)])),
If((one_counts != 4) & (one_counts != 5) & (one_counts != 6),
has_error.eq(1)
),
comma_aligned.eq(0),
If((self.data[:10] == comma) | (self.data[:10] == comma_n),
comma_aligned.eq(1)
),
# signal that need to be manually cleared
If(check_reset,
comma_seen.eq(0),
).Elif((self.data[:10] == comma) | (self.data[:10] == comma_n),
comma_seen.eq(1)
),
one_counts.eq(reduce(add, [self.data[i] for i in range(10, 20)])),
If(check_reset,
error_seen.eq(0),
).Elif((one_counts != 4) & (one_counts != 5) & (one_counts != 6),
error_seen.eq(1),
),
]
# DEBUG: expose signal
self.check_reset = Signal()
self.comma_aligned = Signal()
self.comma_seen = Signal()
self.has_error = Signal()
self.error_seen = Signal()
self.comb +=[
self.check_reset.eq(check_reset),
self.comma_aligned.eq(comma_aligned),
self.comma_seen.eq(comma_seen),
self.has_error.eq(has_error),
self.error_seen.eq(error_seen),
]
self.submodules.rxfsm = rxfsm = ClockDomainsRenamer("cxp_gtx_rx")(FSM(reset_state="ALIGNING"))
# the data from gtxe2 is delayed and checking at the output may not reflect the alignment inside the aligner
# thus, failing to alignment on high linerate >5Gbps is common due to the aligner_en being asserted longer than necessary and lead to a lose of lock
# a timer is used to wait till the "aligned" data to arrive and do a system check like the datasheet suggested
self.submodules.timer = timer = ClockDomainsRenamer("cxp_gtx_rx")(WaitTimer(5000))
rxfsm.act("ALIGNING",
self.aligner_en_rxclk.eq(1),
If(self.word_aligned,
check_reset.eq(1),
NextState("WAIT_ALIGNED_DATA"),
)
)
fsm.act("WAIT_NO_ERROR",
aligner_en_sys.eq(1),
rxfsm.act("WAIT_ALIGNED_DATA",
timer.wait.eq(1),
If(timer.done,
check_reset.eq(1),
NextState("CHECKING"),
)
)
rxfsm.act("CHECKING",
If(check,
recheck_ps.i.eq(1),
If(aligned & valid_data,
check_reset.eq(1),
If(comma_seen & (~error_seen),
NextState("READY"),
).Else(
self.restart.eq(1),
NextState("IDLE"),
NextState("ALIGNING")
)
)
)
fsm.act("READY",
self.ready.eq(1),
ready = Signal()
self.specials += MultiReg(ready, self.ready)
rxfsm.act("READY",
ready.eq(1),
If(check,
recheck_ps.i.eq(1),
If(~(aligned & valid_data),
self.restart.eq(1),
NextState("IDLE"),
check_reset.eq(1),
If(~(comma_seen & (~error_seen)),
NextState("ALIGNING"),
)
)
)
class GTX(Module):
# Settings:
# * GTX reference clock @ 125MHz
@ -374,7 +490,7 @@ class GTX(Module):
# linerate = USRCLK * datawidth
pll_fbout_mult = 8
txusr_pll_div = pll_fbout_mult*sys_clk_freq/qpll.tx_usrclk_freq # 20 is datawidth
txusr_pll_div = pll_fbout_mult*sys_clk_freq/qpll.tx_usrclk_freq
self.tx_restart = Signal()
self.rx_restart = Signal()
@ -386,9 +502,18 @@ class GTX(Module):
self.tx_rate = Signal(3)
self.rx_rate = Signal(3)
self.submodules.encoder = ClockDomainsRenamer("cxp_gtx_tx")(Encoder(2, True))
# Dynamic Reconfiguration Ports
self.daddr = Signal(9)
self.dclk = Signal()
self.den = Signal()
self.dwen = Signal()
self.din = Signal(16)
self.dout = Signal(16)
self.dready = Signal()
self.submodules.encoder = ClockDomainsRenamer("cxp_gtx_tx")(Encoder(4, True))
self.submodules.decoders = [ClockDomainsRenamer("cxp_gtx_rx")(
(Decoder(True))) for _ in range(2)]
(Decoder(True))) for _ in range(4)]
# transceiver direct clock outputs
@ -411,10 +536,11 @@ class GTX(Module):
rx_init.cplllock.eq(qpll.lock)
]
txdata = Signal(20)
rxdata = Signal(20)
txdata = Signal(40)
rxdata = Signal(40)
comma_align_en = Signal()
word_aligned = Signal()
comma_aligner_en = Signal()
# Note: the following parameters were set after consulting AR45360
self.specials += \
Instance("GTXE2_CHANNEL",
@ -472,11 +598,11 @@ class GTX(Module):
i_TXINHIBIT=~self.txenable,
# TX data
p_TX_DATA_WIDTH=20,
p_TX_INT_DATAWIDTH=0,
i_TXCHARDISPMODE=Cat(txdata[9], txdata[19]),
i_TXCHARDISPVAL=Cat(txdata[8], txdata[18]),
i_TXDATA=Cat(txdata[:8], txdata[10:18]),
p_TX_DATA_WIDTH=40,
p_TX_INT_DATAWIDTH=1,
i_TXCHARDISPMODE=Cat(txdata[9], txdata[19], txdata[29], txdata[39]),
i_TXCHARDISPVAL=Cat(txdata[8], txdata[18], txdata[28], txdata[38]),
i_TXDATA=Cat(txdata[:8], txdata[10:18], txdata[20:28], txdata[30:38]),
i_TXUSRCLK=ClockSignal("cxp_gtx_tx"),
i_TXUSRCLK2=ClockSignal("cxp_gtx_tx"),
@ -535,27 +661,29 @@ class GTX(Module):
p_CLK_COR_SEQ_2_ENABLE=0b1111,
# RX data
p_RX_DATA_WIDTH=20,
p_RX_INT_DATAWIDTH=0,
o_RXDISPERR=Cat(rxdata[9], rxdata[19]),
o_RXCHARISK=Cat(rxdata[8], rxdata[18]),
o_RXDATA=Cat(rxdata[:8], rxdata[10:18]),
p_RX_DATA_WIDTH=40,
p_RX_INT_DATAWIDTH=1,
o_RXDISPERR=Cat(rxdata[9], rxdata[19], rxdata[29], rxdata[39]),
o_RXCHARISK=Cat(rxdata[8], rxdata[18], rxdata[28], rxdata[38]),
o_RXDATA=Cat(rxdata[:8], rxdata[10:18], rxdata[20:28], rxdata[30:38]),
# RX Byte and Word Alignment Attributes
p_ALIGN_COMMA_DOUBLE="FALSE",
p_ALIGN_COMMA_ENABLE=0b1111111111,
p_ALIGN_COMMA_WORD=2, # allow rxslide to shift 20 times
p_ALIGN_COMMA_WORD=4, # align comma to rxdata[:10] only
p_ALIGN_MCOMMA_DET="TRUE",
p_ALIGN_MCOMMA_VALUE=0b1010000011,
p_ALIGN_PCOMMA_DET="TRUE",
p_ALIGN_PCOMMA_VALUE=0b0101111100,
p_SHOW_REALIGN_COMMA="FALSE",
p_SHOW_REALIGN_COMMA="TRUE",
p_RXSLIDE_AUTO_WAIT=7,
p_RXSLIDE_MODE="OFF",
p_RX_SIG_VALID_DLY=10,
i_RXPCOMMAALIGNEN=comma_align_en,
i_RXMCOMMAALIGNEN=comma_align_en,
i_RXCOMMADETEN=1, # enable auto word alignment
i_RXPCOMMAALIGNEN=comma_aligner_en,
i_RXMCOMMAALIGNEN=comma_aligner_en,
i_RXCOMMADETEN=1,
i_RXSLIDE=0,
o_RXBYTEISALIGNED=word_aligned,
# RX 8B/10B Decoder Attributes
p_RX_DISPERR_SEQ_MATCH="FALSE",
@ -601,6 +729,16 @@ class GTX(Module):
o_GTXTXP=pads.txp,
o_GTXTXN=pads.txn,
# Dynamic Reconfiguration Ports
p_IS_DRPCLK_INVERTED=0b0,
i_DRPADDR=self.daddr,
i_DRPCLK=self.dclk,
i_DRPEN=self.den,
i_DRPWE=self.dwen,
i_DRPDI=self.din,
o_DRPDO=self.dout,
o_DRPRDY=self.dready,
# ! loopback for debugging
i_LOOPBACK = self.loopback_mode,
p_TX_LOOPBACK_DRIVE_HIZ = "FALSE",
@ -650,7 +788,7 @@ class GTX(Module):
p_CLKFBOUT_MULT=pll_fbout_mult, p_DIVCLK_DIVIDE=1,
i_CLKFBIN=txpll_fb_clk, o_CLKFBOUT=txpll_fb_clk,
# frequency = linerate/20
# frequency = linerate/40
p_CLKOUT0_DIVIDE=txusr_pll_div, p_CLKOUT0_PHASE=0.0, o_CLKOUT0=txpll_clkout,
# Dynamic Reconfiguration Ports
@ -676,17 +814,19 @@ class GTX(Module):
]
self.comb += [
txdata.eq(Cat(self.encoder.output[0], self.encoder.output[1])),
txdata.eq(Cat(self.encoder.output[0], self.encoder.output[1], self.encoder.output[2], self.encoder.output[3])),
self.decoders[0].input.eq(rxdata[:10]),
self.decoders[1].input.eq(rxdata[10:])
self.decoders[1].input.eq(rxdata[10:20]),
self.decoders[2].input.eq(rxdata[20:30]),
self.decoders[3].input.eq(rxdata[30:]),
]
self.submodules.comma_det = comma_det = Comma_Detector(0b0101111100)
self.comb += [
comma_det.data.eq(rxdata),
comma_det.rxinit_done.eq(rx_init.done),
comma_align_en.eq(comma_det.aligner_en_rxclk),
comma_det.word_aligned.eq(word_aligned),
comma_aligner_en.eq(comma_det.aligner_en_rxclk),
self.rx_ready.eq(comma_det.ready),
rx_init.restart.eq(self.rx_restart | comma_det.restart),

View File

@ -2,6 +2,7 @@ use embedded_hal::prelude::_embedded_hal_blocking_delay_DelayUs;
use libboard_zynq::{println, timer::GlobalTimer};
use log::info;
// use log::info;
use crate::pl::csr;
pub struct CXP_DownConn_Settings {
@ -65,46 +66,61 @@ fn loopback_testing(timer: &mut GlobalTimer, data: u8, control_bit: u8) {
csr::cxp::downconn_txenable_write(1);
info!("waiting for rx to align...");
timer.delay_us(50_000);
// 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(),
// );
// println!("0xA8 = {:#06x}", read(0x62));
// write(0x62, 0x001A);
// println!("0xA8 = {:#06x}", read(0x62));
// for _ in 0..20 {
loop {
// NOTE: raw data
let data0 = csr::cxp::downconn_rxdata_0_read();
let data1 = csr::cxp::downconn_rxdata_1_read();
// let rxready = csr::cxp::downconn_rx_ready_read();
// 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);
// if data0 == 0b0101111100 || data0 == 0b1010000011 {
// println!(
// "data[0] = {:#012b} data[1] = {:#012b}",
// csr::cxp::rxdata_0_read(),
// csr::cxp::rxdata_1_read(),
// "data[0] = {:#012b} comma = {} | rx ready = {}",
// data0,
// data0 == 0b0101111100 || data0 == 0b1010000011,
// rxready,
// );
// timer.delay_us(1_000_000);
// } else if data0 == 0b1001111100 || data0 == 0b0110000011 {
// println!(
// "data[0] = {:#012b} K28.1 | rx ready = {}",
// data0,
// rxready,
// );
// timer.delay_us(1_000_000);
// } else {
// println!(
// "data[0] = {:#012b} | rx ready = {}",
// data0,
// rxready,
// );
// timer.delay_us(1_000_000);
// }
println!("0b{:010b}{:010b}", data0, data1);
timer.delay_us(1_000_000);
// NOTE:decode data
// let data0_decoded = csr::cxp::downconn_decoded_data_0_read();
// let data0_k = csr::cxp::downconn_decoded_k_0_read();
// let data1_decoded = csr::cxp::downconn_decoded_data_1_read();
// let data1_k = csr::cxp::downconn_decoded_k_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(),
// data0_decoded,
// data0_k,
// data1_decoded,
// data1_k,
// );
// timer.delay_us(1_000_000);
}
}
}
@ -134,19 +150,30 @@ pub fn setup(timer: &mut GlobalTimer, speed: CXP_SPEED) {
println!("==============================================================================");
}
change_linerate(timer, speed);
CXP_GTX::change_linerate(timer, speed);
loopback_testing(timer, 0x00, 0);
}
pub fn change_linerate(timer: &mut GlobalTimer, speed: CXP_SPEED) {
pub mod CXP_GTX {
use super::*;
struct RX_CDR_CFG {
pub cfg_reg0: u16, //0x0A8
pub cfg_reg1: u16, //0x0A9
pub cfg_reg2: u16, //0x0AA
pub cfg_reg3: u16, //0x0AB
pub cfg_reg4: u16, //0x0AC
}
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);
change_cdr_cfg(speed);
unsafe {
csr::cxp::downconn_qpll_reset_write(1);
@ -159,20 +186,168 @@ pub fn change_linerate(timer: &mut GlobalTimer, speed: CXP_SPEED) {
csr::cxp::downconn_tx_restart_write(1);
csr::cxp::downconn_rx_restart_write(1);
}
}
}
fn change_qpll_settings(speed: CXP_SPEED) {
match speed {
CXP_SPEED::CXP_12 => {
panic!("CXP 12.5Gbps is not supported on zc706");
}
_ => {}
}
// this switches between High and Low band VCO
// NOT needed if VCO can do 12.5GHz
let qpll_cfg_reg0 = match speed {
// NOTE: for ZC706 QPLL VCO that cannot go up to 12.5GHz
CXP_SPEED::CXP_1 | CXP_SPEED::CXP_2 | CXP_SPEED::CXP_5 | CXP_SPEED::CXP_10 => 0x0181,
CXP_SPEED::CXP_3 | CXP_SPEED::CXP_6 | CXP_SPEED::CXP_12 => 0x01C1,
};
// Change QPLL_REFCLK_DIV
let qpll_div_reg0 = match speed {
// NOTE: for ZC706 QPLL VCO that cannot go up to 12.5GHz
CXP_SPEED::CXP_1 | CXP_SPEED::CXP_2 | CXP_SPEED::CXP_5 | CXP_SPEED::CXP_10 => 0x8068,
CXP_SPEED::CXP_3 | CXP_SPEED::CXP_6 | CXP_SPEED::CXP_12 => 0x0068,
};
// Change QPLL_FBDIV
let qpll_div_reg1 = match speed {
CXP_SPEED::CXP_1 | CXP_SPEED::CXP_2 | CXP_SPEED::CXP_5 | CXP_SPEED::CXP_10 => 0x0120,
CXP_SPEED::CXP_3 | CXP_SPEED::CXP_6 | CXP_SPEED::CXP_12 => 0x0170,
};
println!("0x32 = {:#018b}", qpll_read(0x32));
qpll_write(0x32, qpll_cfg_reg0);
println!("0x32 = {:#018b}", qpll_read(0x32));
println!("0x33 = {:#018b}", qpll_read(0x33));
qpll_write(0x33, qpll_div_reg0);
println!("0x33 = {:#018b}", qpll_read(0x33));
println!("0x36 = {:#018b}", qpll_read(0x36));
qpll_write(0x36, qpll_div_reg1);
println!("0x36 = {:#018b}", qpll_read(0x36));
fn change_qpll_settings(speed: CXP_SPEED) {
let divider = match speed {
// NOTE: for ZC706 QPLL VCO that cannot go up to 12.5GHz
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
CXP_SPEED::CXP_2 => 0b011, // Divided by 4
CXP_SPEED::CXP_5 | CXP_SPEED::CXP_3 => 0b010, // Divided by 2
CXP_SPEED::CXP_10 | CXP_SPEED::CXP_6 => 0b001, // Divided by 1
CXP_SPEED::CXP_12 => 0b000,
// NOTE: for ZC706 QPLL VCO that go up to 12.5GHz
// 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 => 0b001, // Divided by 1
};
unsafe {
csr::cxp::downconn_rx_div_write(divider);
csr::cxp::downconn_tx_div_write(divider);
}
}
fn change_cdr_cfg(speed: CXP_SPEED) {
// NOTE: for ZC706 QPLL VCO that cannot go up to 12.5GHz
let cdr_cfg = match speed {
// Divided by 8
CXP_SPEED::CXP_1 => {
RX_CDR_CFG {
cfg_reg0: 0x0020, //0x0A8
cfg_reg1: 0x1008, //0x0A9
cfg_reg2: 0x23FF, //0x0AA
cfg_reg3: 0x0000, //0x0AB
cfg_reg4: 0x0003, //0x0AC
}
}
// Divided by 4
CXP_SPEED::CXP_2 => {
RX_CDR_CFG {
cfg_reg0: 0x0020, //0x0A8
cfg_reg1: 0x1010, //0x0A9
cfg_reg2: 0x23FF, //0x0AA
cfg_reg3: 0x0000, //0x0AB
cfg_reg4: 0x0003, //0x0AC
}
}
// Divided by 2
CXP_SPEED::CXP_3 | CXP_SPEED::CXP_5 => {
RX_CDR_CFG {
cfg_reg0: 0x0020, //0x0A8
cfg_reg1: 0x1020, //0x0A9
cfg_reg2: 0x23FF, //0x0AA
cfg_reg3: 0x0000, //0x0AB
cfg_reg4: 0x0003, //0x0AC
}
}
// Divided by 1
CXP_SPEED::CXP_6 => {
RX_CDR_CFG {
cfg_reg0: 0x0020, //0x0A8
cfg_reg1: 0x1040, //0x0A9
cfg_reg2: 0x23FF, //0x0AA
cfg_reg3: 0x0000, //0x0AB
cfg_reg4: 0x0003, //0x0AC
}
}
// Divided by 1
CXP_SPEED::CXP_10 | CXP_SPEED::CXP_12 => {
RX_CDR_CFG {
cfg_reg0: 0x0020, //0x0A8
cfg_reg1: 0x1040, //0x0A9
cfg_reg2: 0x23FF, //0x0AA
cfg_reg3: 0x0000, //0x0AB
cfg_reg4: 0x000B, //0x0AC
}
}
};
gtx_write(0x0A8, cdr_cfg.cfg_reg0);
gtx_write(0x0A9, cdr_cfg.cfg_reg1);
gtx_write(0x0AA, cdr_cfg.cfg_reg2);
gtx_write(0x0AB, cdr_cfg.cfg_reg3);
gtx_write(0x0AC, cdr_cfg.cfg_reg4);
}
fn gtx_read(address: u16) -> u16 {
// DEBUG: DRPCLK need to be on for a few cycle before accessing other DRP ports
unsafe {
csr::cxp::downconn_gtx_daddr_write(address);
csr::cxp::downconn_gtx_dread_write(1);
while (csr::cxp::downconn_gtx_dready_read() != 1) {}
csr::cxp::downconn_gtx_dout_read()
}
}
fn gtx_write(address: u16, value: u16) {
// DEBUG: DRPCLK need to be on for a few cycle before accessing other DRP ports
unsafe {
csr::cxp::downconn_gtx_daddr_write(address);
csr::cxp::downconn_gtx_din_write(value);
csr::cxp::downconn_gtx_din_stb_write(1);
while (csr::cxp::downconn_gtx_dready_read() != 1) {}
}
}
fn qpll_read(address: u8) -> u16 {
unsafe {
csr::cxp::downconn_qpll_daddr_write(address);
csr::cxp::downconn_qpll_dread_write(1);
while (csr::cxp::downconn_qpll_dready_read() != 1) {}
csr::cxp::downconn_qpll_dout_read()
}
}
fn qpll_write(address: u8, value: u16) {
unsafe {
csr::cxp::downconn_qpll_daddr_write(address);
csr::cxp::downconn_qpll_din_write(value);
csr::cxp::downconn_qpll_din_stb_write(1);
while (csr::cxp::downconn_qpll_dready_read() != 1) {}
}
}
}
pub mod txusrclk {
@ -312,6 +487,23 @@ pub mod txusrclk {
pub fn get_txusrclk_config(speed: CXP_SPEED) -> PLLSetting {
match speed {
CXP_SPEED::CXP_1 => {
// CLKFBOUT_MULT = 8, DIVCLK_DIVIDE = 1 , CLKOUT0_DIVIDE = 32
// TXUSRCLK=62.5MHz
PLLSetting {
clkout0_reg1: 0x1410, //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 = 16
// TXUSRCLK=62.5MHz
PLLSetting {
@ -328,7 +520,24 @@ pub mod txusrclk {
filt_reg2: 0x9100, //0x4F
}
}
CXP_SPEED::CXP_2 => {
CXP_SPEED::CXP_3 => {
// CLKFBOUT_MULT = 10, DIVCLK_DIVIDE = 1 , CLKOUT0_DIVIDE = 16
// TXUSRCLK=78.125MHz
PLLSetting {
clkout0_reg1: 0x1208, //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 = 8
// TXUSRCLK=125MHz
PLLSetting {
@ -345,9 +554,9 @@ pub mod txusrclk {
filt_reg2: 0x9100, //0x4F
}
}
CXP_SPEED::CXP_3 => {
CXP_SPEED::CXP_6 => {
// CLKFBOUT_MULT = 10, DIVCLK_DIVIDE = 1 , CLKOUT0_DIVIDE = 8
// TXUSRCLK=125MHz
// TXUSRCLK=156.25MHz
PLLSetting {
clkout0_reg1: 0x1104, //0x08
clkout0_reg2: 0x0000, //0x09
@ -362,7 +571,7 @@ pub mod txusrclk {
filt_reg2: 0x1900, //0x4F
}
}
CXP_SPEED::CXP_5 => {
CXP_SPEED::CXP_10 => {
// CLKFBOUT_MULT = 8, DIVCLK_DIVIDE = 1 , CLKOUT0_DIVIDE = 4
// TXUSRCLK=250MHz
PLLSetting {
@ -379,7 +588,7 @@ pub mod txusrclk {
filt_reg2: 0x9100, //0x4F
}
}
CXP_SPEED::CXP_6 => {
CXP_SPEED::CXP_12 => {
// CLKFBOUT_MULT = 10, DIVCLK_DIVIDE = 1 , CLKOUT0_DIVIDE = 4
// TXUSRCLK=312.5MHz
PLLSetting {
@ -396,40 +605,6 @@ pub mod txusrclk {
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
}
}
}
}
}