1
0
Fork 0

Compare commits

..

6 Commits

Author SHA1 Message Date
morgan cc8ae30303 cxp downconn firmware: GTX setup
testing: add loopmode mode & tsusrclk mmcm drp bitbang
testing: add IDLE word printout
downconn: add QPLL and GTX setup
downconn: add DRP to support all CXP linerate up to 12.5Gbps
2024-08-21 15:27:15 +08:00
morgan f8a2920033 cxp upconn firmware: init 2024-08-21 15:26:15 +08:00
morgan 95493c2ea7 cxp: add upconn, downconn & crc
cxp: add crc32 for cxp
cxp: add upconn & downconn
2024-08-21 15:26:15 +08:00
morgan 6922934897 cxp upconn: add low speed serial
cxp upconn: add low speed serial
cxp upconn: add pll for bitrate2x mode
cxp upconn: add tx_fifos & idle with encoder
cxp upconn: add priority packet that send in word/char boundary

cxp upconn: disable debug sma & pmod output
2024-08-21 15:26:15 +08:00
morgan ecd2d6a790 zc706: add constraint to fix comma alignment issue 2024-08-21 15:26:15 +08:00
morgan 870f4fdf96 cxp downconn: add high speed serial up to 12.5Gbps
testing: add txusrclk mmcm & loopback mode
testing: add debug output
downconn: add GTX and QPLL support
downconn: add DRP for GTX and QPLL to support all CXP linerates
GTX: add gtx with mmcm for TXUSRCLK freq requirement
GTX: add loopback mode parameter for testing
GTX: add gtx with 40bits internal width
GTX: use built-in comma aligner
GTX: add comma checker to ensure comma is aligner on highest linerate
GTX: set QPLL as CLK source for GTX
2024-08-21 15:26:15 +08:00
3 changed files with 208 additions and 273 deletions

View File

@ -1,5 +1,5 @@
from migen import * from migen import *
from migen.genlib.cdc import MultiReg, PulseSynchronizer from migen.genlib.cdc import MultiReg
from migen.genlib.resetsync import AsyncResetSynchronizer from migen.genlib.resetsync import AsyncResetSynchronizer
from misoc.cores.code_8b10b import Encoder, Decoder from misoc.cores.code_8b10b import Encoder, Decoder
@ -32,12 +32,13 @@ class CXP_DownConn(Module, AutoCSR):
# single & master tx_mode can lock with rx in loopback # 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") 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 :<
# 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
self.sync += [ self.sync += [
# PLL # PLL
qpll.reset.eq(self.qpll_reset.re), qpll.reset.eq(self.qpll_reset.re),
@ -156,18 +157,18 @@ class CXP_DownConn(Module, AutoCSR):
# DEBUG: IO SMA & PMOD # DEBUG: IO SMA & PMOD
self.specials += [ self.specials += [
Instance("OBUF", i_I=gtx.rxoutclk, o_O=debug_sma.p_tx), Instance("OBUF", i_I=gtx.cd_cxp_gtx_rx.clk, o_O=debug_sma.p_tx),
Instance("OBUF", i_I=gtx.cd_cxp_gtx_tx.clk, o_O=debug_sma.n_rx), Instance("OBUF", i_I=gtx.cd_cxp_gtx_tx.clk, o_O=debug_sma.n_rx),
# pmod 0-7 pin # pmod 0-7 pin
Instance("OBUF", i_I=gtx.comma_det.word_aligned, o_O=pmod_pads[0]), Instance("OBUF", i_I=gtx.comma_checker.comma_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_checker.comma_det, 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_checker.restart_sys, 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_checker.aligner_en, 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_checker.check_reset, 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_checker.has_comma, 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_checker.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.comma_checker.ready_sys, o_O=pmod_pads[7]),
# Instance("OBUF", i_I=gtx.dclk, o_O=pmod_pads[0]), # 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.den, o_O=pmod_pads[1]),
@ -177,23 +178,6 @@ class CXP_DownConn(Module, AutoCSR):
# DEBUG: datain # DEBUG: datain
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 += [ self.sync.cxp_gtx_tx += [
self.gtx.encoder.d[0].eq(0xBC), self.gtx.encoder.d[0].eq(0xBC),
@ -204,39 +188,40 @@ class CXP_DownConn(Module, AutoCSR):
self.gtx.encoder.k[2].eq(1), self.gtx.encoder.k[2].eq(1),
self.gtx.encoder.d[3].eq(0xB5), self.gtx.encoder.d[3].eq(0xB5),
self.gtx.encoder.k[3].eq(0), 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 self.rxdata_0 = CSRStatus(10)
stb_timer = Signal(reset=10, max=11) self.rxdata_1 = CSRStatus(10)
self.rxdata_2 = CSRStatus(10)
self.rxdata_3 = CSRStatus(10)
self.decoded_data_0 = CSRStatus(8)
self.decoded_data_1 = CSRStatus(8)
self.decoded_data_2 = CSRStatus(8)
self.decoded_data_3 = CSRStatus(8)
self.decoded_k_0 = CSRStatus()
self.decoded_k_1 = CSRStatus()
self.decoded_k_2 = CSRStatus()
self.decoded_k_3 = CSRStatus()
self.sync.cxp_gtx_rx += [ self.sync.cxp_gtx_rx += [
If(stb_timer == 0, self.rxdata_0.status.eq(self.gtx.decoders[0].input),
self.rxdata_0.status.eq(self.gtx.decoders[0].input), self.decoded_data_0.status.eq(self.gtx.decoders[0].d),
self.decoded_data_0.status.eq(self.gtx.decoders[0].d), self.decoded_k_0.status.eq(self.gtx.decoders[0].k),
self.decoded_k_0.status.eq(self.gtx.decoders[0].k),
self.rxdata_1.status.eq(self.gtx.decoders[1].input), self.rxdata_1.status.eq(self.gtx.decoders[1].input),
self.decoded_data_1.status.eq(self.gtx.decoders[1].d), self.decoded_data_1.status.eq(self.gtx.decoders[1].d),
self.decoded_k_1.status.eq(self.gtx.decoders[1].k), self.decoded_k_1.status.eq(self.gtx.decoders[1].k),
stb_timer.eq(stb_timer.reset),
).Else( self.rxdata_2.status.eq(self.gtx.decoders[2].input),
stb_timer.eq(stb_timer - 1), self.decoded_data_2.status.eq(self.gtx.decoders[2].d),
) self.decoded_k_2.status.eq(self.gtx.decoders[2].k),
self.rxdata_3.status.eq(self.gtx.decoders[3].input),
self.decoded_data_3.status.eq(self.gtx.decoders[3].d),
self.decoded_k_3.status.eq(self.gtx.decoders[3].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): class QPLL(Module):
def __init__(self, refclk, sys_clk_freq): def __init__(self, refclk, sys_clk_freq):
self.clk = Signal() self.clk = Signal()
@ -255,23 +240,15 @@ class QPLL(Module):
self.dready = Signal() self.dready = Signal()
# # # # # #
# WARNING: VCO cannot do 12.5GHz on ZC706 # VCO @ 10GHz, linerate = 1.25Gbps
# VCO freq = sys*qpll_fbdiv # feedback divider = 80
# PLL output = VCO/2 qpll_fbdiv = 0b0100100000
qpll_fbdiv = 0b0100100000 # 80 div
qpll_fbdiv_ratio = 1 qpll_fbdiv_ratio = 1
fbdiv_real = 80
refclk_div = 1 refclk_div = 1
self.Xxout_div = 8 self.Xxout_div = 8
# qpll_fbdiv = 0b0101110000 # 100 div # DEBUG: txuserclk
# qpll_fbdiv_ratio = 1 fbdiv_real = 80
# fbdiv_real = 100
# refclk_div = 2
# self.Xxout_div = 2
self.tx_usrclk_freq = (sys_clk_freq*fbdiv_real/self.Xxout_div)/40 self.tx_usrclk_freq = (sys_clk_freq*fbdiv_real/self.Xxout_div)/40
self.specials += [ self.specials += [
@ -328,123 +305,123 @@ class QPLL(Module):
# 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 Comma_Detector(Module): class Comma_Checker(Module):
def __init__(self, comma, check_period=1_000_000): def __init__(self, comma, reset_period=10_000_000):
self.data = Signal(20) self.data = Signal(20)
self.word_aligned = Signal() self.comma_aligned = Signal()
self.comma_realigned = Signal()
self.comma_det = Signal()
self.aligner_en_rxclk = Signal() self.aligner_en = Signal()
self.ready = Signal() self.ready_sys = Signal()
self.restart = Signal() self.restart_sys = Signal()
# # # # # #
# periodically reset rx until rx is connected and receiving valid data
# The built-in RXBYTEISALIGNED can be falsely asserted at linerate higher than 5Gbps # as after connecting RXP/RXN, the whole RX need to be reset
# - UG476 (v1.12.1) p.228
reset_counter = Signal(reset=reset_period-1, max=reset_period)
# The validity of data & comma are checked externally
check_counter = Signal(reset=check_period-1, max=check_period)
# check = Signal()
self.sync += [ self.sync += [
self.restart.eq(0), self.restart_sys.eq(0),
# check.eq(0), If(~self.ready_sys,
# check_reset.i.eq(0), If(reset_counter == 0,
If(~self.ready, reset_counter.eq(reset_counter.reset),
If(check_counter == 0, self.restart_sys.eq(1),
check_counter.eq(check_counter.reset),
# check.eq(1),
self.restart.eq(1),
# check_reset.i.eq(1),
).Else( ).Else(
check_counter.eq(check_counter - 1), reset_counter.eq(reset_counter - 1),
) )
) )
] ]
# WIP:
# Data and comma checker
# From UG476 (v1.12.1) p.228
# The built-in RXBYTEISALIGNED can be falsely asserted at linerate higher than 5Gbps
# The validity of data and comma needed to be checked externally
comma_n = ~comma & 0b1111111111 comma_n = ~comma & 0b1111111111
has_error = Signal() # DEBUG: remove after use
comma_aligned = Signal() self.has_comma = Signal()
self.has_error = Signal()
comma_seen = Signal() comma_seen = Signal()
error_seen = Signal() error_seen = Signal()
one_counts = Signal(max=11) one_counts = Signal(max=11)
counter_period = 5000 # From CXP-001-2021 section 9.2.5.1
# For high speed connection an IDLE word shall be transmitted at least once every 100 words
counter_period = 200
counter = Signal(reset=counter_period-1, max=counter_period) counter = Signal(reset=counter_period-1, max=counter_period)
check_reset = Signal() check_reset = Signal()
check = Signal() check = Signal()
self.sync.cxp_gtx_rx += [ self.sync.cxp_gtx_rx += [
check.eq(0), If(check_reset,
If(counter == 0,
counter.eq(counter.reset), counter.eq(counter.reset),
check.eq(0),
).Elif(counter == 0,
check.eq(1), check.eq(1),
).Else( ).Else(
counter.eq(counter - 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, If(check_reset,
comma_seen.eq(0), comma_seen.eq(0),
).Elif((self.data[:10] == comma) | (self.data[:10] == comma_n), ).Elif((self.data[:10] == comma) | (self.data[:10] == comma_n),
comma_seen.eq(1) comma_seen.eq(1)
), ),
one_counts.eq(reduce(add, [self.data[i] for i in range(10, 20)])), one_counts.eq(reduce(add, [self.data[i] for i in range(10)])),
If(check_reset, If(check_reset,
error_seen.eq(0), error_seen.eq(0),
).Elif((one_counts != 4) & (one_counts != 5) & (one_counts != 6), ).Elif((one_counts != 4) & (one_counts != 5) & (one_counts != 6),
error_seen.eq(1), error_seen.eq(1),
), ),
# DEBUG:
self.has_comma.eq(0),
If((self.data[:10] == comma) | (self.data[:10] == comma_n),
self.has_comma.eq(1),
),
self.has_error.eq(0),
If((one_counts != 4) & (one_counts != 5) & (one_counts != 6),
self.has_error.eq(1),
),
] ]
# DEBUG: expose signal # DEBUG: expose signal
self.check_reset = Signal() self.check_reset = Signal()
self.comma_aligned = Signal()
self.comma_seen = Signal()
self.has_error = Signal()
self.error_seen = Signal()
self.comb +=[ self.comb +=[
self.check_reset.eq(check_reset), 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")) self.submodules.rxfsm = rxfsm = ClockDomainsRenamer("cxp_gtx_rx")(FSM(reset_state="WAIT_COMMA"))
# the data from gtxe2 is delayed and checking at the output may not reflect the alignment inside the aligner rxfsm.act("WAIT_COMMA",
# 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 If(self.comma_det,
# a timer is used to wait till the "aligned" data to arrive and do a system check like the datasheet suggested # # start aligner early, so word aligned will fall
self.submodules.timer = timer = ClockDomainsRenamer("cxp_gtx_rx")(WaitTimer(5000)) # self.aligner_en_rxclk.eq(1),
NextState("ALIGNING"),
rxfsm.act("ALIGNING",
self.aligner_en_rxclk.eq(1),
If(self.word_aligned,
check_reset.eq(1),
NextState("WAIT_ALIGNED_DATA"),
) )
) )
rxfsm.act("ALIGNING",
If(self.comma_aligned & (~self.comma_realigned),
NextState("WAIT_ALIGNED_DATA"),
).Else(
self.aligner_en.eq(1),
)
)
# wait for the aligned data to arrive at the FPGA RX interface
# as there is a delay before the data is avaiable after RXBYTEISALIGNED is asserted
self.submodules.timer = timer = ClockDomainsRenamer("cxp_gtx_rx")(WaitTimer(10_000))
rxfsm.act("WAIT_ALIGNED_DATA", rxfsm.act("WAIT_ALIGNED_DATA",
timer.wait.eq(1), timer.wait.eq(1),
If(timer.done, If(timer.done,
@ -457,21 +434,21 @@ class Comma_Detector(Module):
If(check, If(check,
check_reset.eq(1), check_reset.eq(1),
If(comma_seen & (~error_seen), If(comma_seen & (~error_seen),
NextState("READY"), NextState("READY"),
).Else( ).Else(
NextState("ALIGNING") NextState("WAIT_COMMA")
) )
) )
) )
ready = Signal() ready = Signal()
self.specials += MultiReg(ready, self.ready) self.specials += MultiReg(ready, self.ready_sys)
rxfsm.act("READY", rxfsm.act("READY",
ready.eq(1), ready.eq(1),
If(check, If(check,
check_reset.eq(1), check_reset.eq(1),
If(~(comma_seen & (~error_seen)), If(~(comma_seen & (~error_seen)),
NextState("ALIGNING"), NextState("WAIT_COMMA"),
) )
) )
) )
@ -539,13 +516,15 @@ class GTX(Module):
txdata = Signal(40) txdata = Signal(40)
rxdata = Signal(40) rxdata = Signal(40)
word_aligned = Signal() comma_aligned = Signal()
comma_realigned = Signal()
comma_det = Signal()
comma_aligner_en = Signal() comma_aligner_en = 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",
# PMA Attributes # PMA Attributes
p_PMA_RSV=0x00018480, p_PMA_RSV=0x001E7080,
p_PMA_RSV2=0x2050, # PMA_RSV2[5] = 0: Eye scan feature disabled p_PMA_RSV2=0x2050, # PMA_RSV2[5] = 0: Eye scan feature disabled
p_PMA_RSV3=0, p_PMA_RSV3=0,
p_PMA_RSV4=1, # PMA_RSV[4],RX_CM_TRIM[2:0] = 0b1010: Common mode 800mV p_PMA_RSV4=1, # PMA_RSV[4],RX_CM_TRIM[2:0] = 0b1010: Common mode 800mV
@ -599,7 +578,7 @@ class GTX(Module):
# TX data # TX data
p_TX_DATA_WIDTH=40, p_TX_DATA_WIDTH=40,
p_TX_INT_DATAWIDTH=1, p_TX_INT_DATAWIDTH=1, # 1 if a line rate is greater than 6.6 Gbps
i_TXCHARDISPMODE=Cat(txdata[9], txdata[19], txdata[29], txdata[39]), i_TXCHARDISPMODE=Cat(txdata[9], txdata[19], txdata[29], txdata[39]),
i_TXCHARDISPVAL=Cat(txdata[8], txdata[18], txdata[28], txdata[38]), 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_TXDATA=Cat(txdata[:8], txdata[10:18], txdata[20:28], txdata[30:38]),
@ -632,7 +611,10 @@ class GTX(Module):
i_RXDFEXYDEN=1, i_RXDFEXYDEN=1,
i_RXDFEXYDHOLD=0, i_RXDFEXYDHOLD=0,
i_RXDFEXYDOVRDEN=0, i_RXDFEXYDOVRDEN=0,
i_RXLPMEN=0, # RXLPMEN = 0: DFE mode is enabled for non scramble data i_RXLPMEN=1, # RXLPMEN = 1: LPM mode is enable for non scramble 8b10b data
p_RXLPM_HF_CFG=0b00000011110000,
p_RXLPM_LF_CFG=0b00000011110000,
p_RX_DFE_GAIN_CFG=0x0207EA, p_RX_DFE_GAIN_CFG=0x0207EA,
p_RX_DFE_VP_CFG=0b00011111100000011, p_RX_DFE_VP_CFG=0b00011111100000011,
p_RX_DFE_UT_CFG=0b10001000000000000, p_RX_DFE_UT_CFG=0b10001000000000000,
@ -662,7 +644,7 @@ class GTX(Module):
# RX data # RX data
p_RX_DATA_WIDTH=40, p_RX_DATA_WIDTH=40,
p_RX_INT_DATAWIDTH=1, p_RX_INT_DATAWIDTH=1, # 1 if a line rate is greater than 6.6 Gbps
o_RXDISPERR=Cat(rxdata[9], rxdata[19], rxdata[29], rxdata[39]), o_RXDISPERR=Cat(rxdata[9], rxdata[19], rxdata[29], rxdata[39]),
o_RXCHARISK=Cat(rxdata[8], rxdata[18], rxdata[28], rxdata[38]), 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]), o_RXDATA=Cat(rxdata[:8], rxdata[10:18], rxdata[20:28], rxdata[30:38]),
@ -675,7 +657,7 @@ class GTX(Module):
p_ALIGN_MCOMMA_VALUE=0b1010000011, p_ALIGN_MCOMMA_VALUE=0b1010000011,
p_ALIGN_PCOMMA_DET="TRUE", p_ALIGN_PCOMMA_DET="TRUE",
p_ALIGN_PCOMMA_VALUE=0b0101111100, p_ALIGN_PCOMMA_VALUE=0b0101111100,
p_SHOW_REALIGN_COMMA="TRUE", p_SHOW_REALIGN_COMMA="FALSE",
p_RXSLIDE_AUTO_WAIT=7, p_RXSLIDE_AUTO_WAIT=7,
p_RXSLIDE_MODE="OFF", p_RXSLIDE_MODE="OFF",
p_RX_SIG_VALID_DLY=10, p_RX_SIG_VALID_DLY=10,
@ -683,7 +665,9 @@ class GTX(Module):
i_RXMCOMMAALIGNEN=comma_aligner_en, i_RXMCOMMAALIGNEN=comma_aligner_en,
i_RXCOMMADETEN=1, i_RXCOMMADETEN=1,
i_RXSLIDE=0, i_RXSLIDE=0,
o_RXBYTEISALIGNED=word_aligned, o_RXBYTEISALIGNED=comma_aligned,
o_RXBYTEREALIGN=comma_realigned,
o_RXCOMMADET=comma_det,
# RX 8B/10B Decoder Attributes # RX 8B/10B Decoder Attributes
p_RX_DISPERR_SEQ_MATCH="FALSE", p_RX_DISPERR_SEQ_MATCH="FALSE",
@ -716,8 +700,8 @@ class GTX(Module):
p_RX_DEFER_RESET_BUF_EN="TRUE", p_RX_DEFER_RESET_BUF_EN="TRUE",
# CDR Attributes # CDR Attributes
p_RXCDR_CFG=0x03_0000_23FF_1040_0020, # DFE @ <= 6.6Gb/s, 8B/10B encoded data, CDR setting < +/- 200ppm p_RXCDR_CFG=0x03_0000_23FF_1008_0020, # LPM @ 0.5G-1.5625G , 8B/10B encoded data, CDR setting < +/- 200ppm
# (See UG476 (v1.12.1), p.205) # (See UG476 (v1.12.1), p.206)
p_RXCDR_FR_RESET_ON_EIDLE=0b0, p_RXCDR_FR_RESET_ON_EIDLE=0b0,
p_RXCDR_HOLD_DURING_EIDLE=0b0, p_RXCDR_HOLD_DURING_EIDLE=0b0,
p_RXCDR_PH_RESET_ON_EIDLE=0b0, p_RXCDR_PH_RESET_ON_EIDLE=0b0,
@ -822,13 +806,15 @@ class GTX(Module):
] ]
self.submodules.comma_det = comma_det = Comma_Detector(0b0101111100) self.submodules.comma_checker = comma_checker = Comma_Checker(0b0101111100)
self.comb += [ self.comb += [
comma_det.data.eq(rxdata), comma_checker.data.eq(rxdata),
comma_det.word_aligned.eq(word_aligned), comma_checker.comma_aligned.eq(comma_aligned),
comma_aligner_en.eq(comma_det.aligner_en_rxclk), comma_checker.comma_realigned.eq(comma_realigned),
self.rx_ready.eq(comma_det.ready), comma_checker.comma_det.eq(comma_det),
comma_aligner_en.eq(comma_checker.aligner_en),
self.rx_ready.eq(comma_checker.ready_sys),
rx_init.restart.eq(self.rx_restart | comma_det.restart), rx_init.restart.eq(self.rx_restart | comma_checker.restart_sys),
tx_init.restart.eq(self.tx_restart), tx_init.restart.eq(self.tx_restart),
] ]

View File

@ -691,6 +691,11 @@ class CXP_FMC():
) )
self.csr_devices.append("cxp") self.csr_devices.append("cxp")
# max freq of cxp_gtx_rx = linerate/internal_datawidth = 12.5Gbps/40 = 312.5MHz
platform.add_period_constraint(self.cxp.downconn.gtx.cd_cxp_gtx_tx.clk, 3.2)
platform.add_period_constraint(self.cxp.downconn.gtx.cd_cxp_gtx_rx.clk, 3.2)
platform.add_false_path_constraints(self.cxp.downconn.gtx.cd_cxp_gtx_tx.clk, self.cxp.downconn.gtx.cd_cxp_gtx_rx.clk)
rtio_channels = [] rtio_channels = []
# FIXME remove this placeholder RTIO channel # FIXME remove this placeholder RTIO channel
# There are too few RTIO channels and cannot be compiled (adr width issue of the lane distributor) # There are too few RTIO channels and cannot be compiled (adr width issue of the lane distributor)

View File

@ -22,38 +22,11 @@ pub enum CXP_SPEED {
CXP_12, CXP_12,
} }
fn loopback_testing(timer: &mut GlobalTimer, data: u8, control_bit: u8) { pub fn loopback_testing(timer: &mut GlobalTimer, speed: CXP_SPEED) {
println!("==============================================================================");
CXP_GTX::change_linerate(timer, speed);
unsafe { 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..."); info!("waiting for tx&rx setup...");
timer.delay_us(50_000); timer.delay_us(50_000);
info!( info!(
@ -66,19 +39,16 @@ fn loopback_testing(timer: &mut GlobalTimer, data: u8, control_bit: u8) {
csr::cxp::downconn_txenable_write(1); csr::cxp::downconn_txenable_write(1);
info!("waiting for rx to align..."); info!("waiting for rx to align...");
// timer.delay_us(50_000); while csr::cxp::downconn_rx_ready_read() != 1 {}
// while csr::cxp::downconn_rx_ready_read() != 1 {} info!("rx ready!");
// info!("rx ready!");
// println!("0xA8 = {:#06x}", read(0x62)); // loop {
// write(0x62, 0x001A); for _ in 0..20 {
// println!("0xA8 = {:#06x}", read(0x62)); // NOTE: raw bits
// let data0 = csr::cxp::downconn_rxdata_0_read();
// for _ in 0..20 { // let data1 = csr::cxp::downconn_rxdata_1_read();
loop { // let data2 = csr::cxp::downconn_rxdata_2_read();
// NOTE: raw data // let data3 = csr::cxp::downconn_rxdata_3_read();
let data0 = csr::cxp::downconn_rxdata_0_read();
let data1 = csr::cxp::downconn_rxdata_1_read();
// let rxready = csr::cxp::downconn_rx_ready_read(); // let rxready = csr::cxp::downconn_rx_ready_read();
// timer.delay_us(100); // timer.delay_us(100);
// if data0 == 0b0101111100 || data0 == 0b1010000011 { // if data0 == 0b0101111100 || data0 == 0b1010000011 {
@ -105,14 +75,30 @@ fn loopback_testing(timer: &mut GlobalTimer, data: u8, control_bit: u8) {
// timer.delay_us(1_000_000); // timer.delay_us(1_000_000);
// } // }
println!("0b{:010b}{:010b}", data0, data1); // timer.delay_us(1_000_000);
timer.delay_us(1_000_000); // NOTE: raw bits
// let data0 = csr::cxp::downconn_rxdata_0_read();
// let data1 = csr::cxp::downconn_rxdata_1_read();
// let data2 = csr::cxp::downconn_rxdata_2_read();
// let data3 = csr::cxp::downconn_rxdata_3_read();
// println!(
// "0b{:010b} {:010b} {:010b} {:010b}",
// data0, data1, data2, data3
// );
// NOTE:decode data // NOTE:decode data
// let data0_decoded = csr::cxp::downconn_decoded_data_0_read();
// let data0_k = csr::cxp::downconn_decoded_k_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(); // let data1_k = csr::cxp::downconn_decoded_k_1_read();
// let data2_k = csr::cxp::downconn_decoded_k_2_read();
// let data3_k = csr::cxp::downconn_decoded_k_3_read();
let data0_decoded = csr::cxp::downconn_decoded_data_0_read();
let data1_decoded = csr::cxp::downconn_decoded_data_1_read();
let data2_decoded = csr::cxp::downconn_decoded_data_2_read();
let data3_decoded = csr::cxp::downconn_decoded_data_3_read();
println!(
"{:#04x} {:#04x} {:#04x} {:#04x}",
data0_decoded, data1_decoded, data2_decoded, data3_decoded,
);
// 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}",
// data0_decoded, // data0_decoded,
@ -120,12 +106,11 @@ fn loopback_testing(timer: &mut GlobalTimer, data: u8, control_bit: u8) {
// data1_decoded, // data1_decoded,
// data1_k, // data1_k,
// ); // );
// timer.delay_us(1_000_000);
} }
} }
} }
pub fn setup(timer: &mut GlobalTimer, speed: CXP_SPEED) { pub fn setup(timer: &mut GlobalTimer) {
unsafe { unsafe {
info!("turning on pmc loopback mode..."); info!("turning on pmc loopback mode...");
csr::cxp::downconn_loopback_mode_write(0b010); // Near-End PMA Loopback csr::cxp::downconn_loopback_mode_write(0b010); // Near-End PMA Loopback
@ -147,12 +132,9 @@ pub fn setup(timer: &mut GlobalTimer, speed: CXP_SPEED) {
csr::cxp::downconn_txinit_phaligndone_read(), csr::cxp::downconn_txinit_phaligndone_read(),
csr::cxp::downconn_rxinit_phaligndone_read(), csr::cxp::downconn_rxinit_phaligndone_read(),
); );
println!("==============================================================================");
} }
CXP_GTX::change_linerate(timer, speed); CXP_GTX::change_linerate(timer, CXP_SPEED::CXP_1);
loopback_testing(timer, 0x00, 0);
} }
pub mod CXP_GTX { pub mod CXP_GTX {
@ -189,70 +171,32 @@ pub mod CXP_GTX {
} }
fn change_qpll_settings(speed: CXP_SPEED) { 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 // Change QPLL_FBDIV
let qpll_div_reg1 = match speed { let qpll_div_reg = match speed {
CXP_SPEED::CXP_1 | CXP_SPEED::CXP_2 | CXP_SPEED::CXP_5 | CXP_SPEED::CXP_10 => 0x0120, CXP_SPEED::CXP_1 | CXP_SPEED::CXP_2 | CXP_SPEED::CXP_5 | CXP_SPEED::CXP_10 => 0x0120, // FB_Divider = 80
CXP_SPEED::CXP_3 | CXP_SPEED::CXP_6 | CXP_SPEED::CXP_12 => 0x0170, CXP_SPEED::CXP_3 | CXP_SPEED::CXP_6 | CXP_SPEED::CXP_12 => 0x0170, // FB_Divider = 100
}; };
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)); println!("0x36 = {:#018b}", qpll_read(0x36));
qpll_write(0x36, qpll_div_reg1); qpll_write(0x36, qpll_div_reg);
println!("0x36 = {:#018b}", qpll_read(0x36)); println!("0x36 = {:#018b}", qpll_read(0x36));
let divider = match speed { let rxout_div = match speed {
// NOTE: for ZC706 QPLL VCO that cannot go up to 12.5GHz CXP_SPEED::CXP_1 => 0b100, // 8
CXP_SPEED::CXP_1 => 0b100, // Divided by 8 CXP_SPEED::CXP_2 | CXP_SPEED::CXP_3 => 0b011, // 4
CXP_SPEED::CXP_2 => 0b011, // Divided by 4 CXP_SPEED::CXP_5 | CXP_SPEED::CXP_6 => 0b010, // 2
CXP_SPEED::CXP_5 | CXP_SPEED::CXP_3 => 0b010, // Divided by 2 CXP_SPEED::CXP_10 | CXP_SPEED::CXP_12 => 0b001, // 1
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 { unsafe {
csr::cxp::downconn_rx_div_write(divider); csr::cxp::downconn_rx_div_write(rxout_div);
csr::cxp::downconn_tx_div_write(divider); csr::cxp::downconn_tx_div_write(rxout_div);
} }
} }
fn change_cdr_cfg(speed: CXP_SPEED) { fn change_cdr_cfg(speed: CXP_SPEED) {
// NOTE: for ZC706 QPLL VCO that cannot go up to 12.5GHz
let cdr_cfg = match speed { let cdr_cfg = match speed {
// Divided by 8 // rxout_div = 8
CXP_SPEED::CXP_1 => { CXP_SPEED::CXP_1 => {
RX_CDR_CFG { RX_CDR_CFG {
cfg_reg0: 0x0020, //0x0A8 cfg_reg0: 0x0020, //0x0A8
@ -262,8 +206,8 @@ pub mod CXP_GTX {
cfg_reg4: 0x0003, //0x0AC cfg_reg4: 0x0003, //0x0AC
} }
} }
// Divided by 4 // rxout_div = 4
CXP_SPEED::CXP_2 => { CXP_SPEED::CXP_2 | CXP_SPEED::CXP_5 => {
RX_CDR_CFG { RX_CDR_CFG {
cfg_reg0: 0x0020, //0x0A8 cfg_reg0: 0x0020, //0x0A8
cfg_reg1: 0x1010, //0x0A9 cfg_reg1: 0x1010, //0x0A9
@ -272,8 +216,8 @@ pub mod CXP_GTX {
cfg_reg4: 0x0003, //0x0AC cfg_reg4: 0x0003, //0x0AC
} }
} }
// Divided by 2 // rxout_div = 2
CXP_SPEED::CXP_3 | CXP_SPEED::CXP_5 => { CXP_SPEED::CXP_3 | CXP_SPEED::CXP_6 => {
RX_CDR_CFG { RX_CDR_CFG {
cfg_reg0: 0x0020, //0x0A8 cfg_reg0: 0x0020, //0x0A8
cfg_reg1: 0x1020, //0x0A9 cfg_reg1: 0x1020, //0x0A9
@ -282,17 +226,17 @@ pub mod CXP_GTX {
cfg_reg4: 0x0003, //0x0AC cfg_reg4: 0x0003, //0x0AC
} }
} }
// Divided by 1 // // Divided by 1
CXP_SPEED::CXP_6 => { // CXP_SPEED::CXP_6 => {
RX_CDR_CFG { // RX_CDR_CFG {
cfg_reg0: 0x0020, //0x0A8 // cfg_reg0: 0x0020, //0x0A8
cfg_reg1: 0x1040, //0x0A9 // cfg_reg1: 0x1040, //0x0A9
cfg_reg2: 0x23FF, //0x0AA // cfg_reg2: 0x23FF, //0x0AA
cfg_reg3: 0x0000, //0x0AB // cfg_reg3: 0x0000, //0x0AB
cfg_reg4: 0x0003, //0x0AC // cfg_reg4: 0x0003, //0x0AC
} // }
} // }
// Divided by 1 // rxout_div = 1
CXP_SPEED::CXP_10 | CXP_SPEED::CXP_12 => { CXP_SPEED::CXP_10 | CXP_SPEED::CXP_12 => {
RX_CDR_CFG { RX_CDR_CFG {
cfg_reg0: 0x0020, //0x0A8 cfg_reg0: 0x0020, //0x0A8