forked from M-Labs/artiq-zynq
Compare commits
12 Commits
1f8ef6bf96
...
a6b1701de3
Author | SHA1 | Date |
---|---|---|
morgan | a6b1701de3 | |
morgan | aa128e1467 | |
morgan | 49d5cad5fd | |
morgan | 17277504f1 | |
morgan | a715b66f9d | |
morgan | 692edeadfc | |
morgan | 6e4ae2e1d1 | |
morgan | 5ddd2cd730 | |
morgan | 5dfef7e457 | |
morgan | bfc8f065f7 | |
morgan | 2cd1da81c1 | |
morgan | e055a11e1c |
|
@ -1,6 +1,5 @@
|
||||||
from migen import *
|
from migen import *
|
||||||
from migen.genlib.cdc import MultiReg, PulseSynchronizer
|
from migen.genlib.cdc import MultiReg, PulseSynchronizer
|
||||||
from migen.genlib.misc import WaitTimer
|
|
||||||
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
|
||||||
|
@ -20,14 +19,10 @@ class CXP_DownConn(Module, AutoCSR):
|
||||||
self.tx_restart = CSR()
|
self.tx_restart = CSR()
|
||||||
self.txenable = CSRStorage()
|
self.txenable = CSRStorage()
|
||||||
|
|
||||||
|
|
||||||
self.txinit_phaligndone = CSRStatus()
|
self.txinit_phaligndone = CSRStatus()
|
||||||
self.rxinit_phaligndone = CSRStatus()
|
self.rxinit_phaligndone = CSRStatus()
|
||||||
self.rx_ready = CSRStatus()
|
self.rx_ready = CSRStatus()
|
||||||
|
|
||||||
self.tx_div = CSRStorage(3)
|
|
||||||
self.rx_div = CSRStorage(3)
|
|
||||||
|
|
||||||
self.qpll_reset = CSR()
|
self.qpll_reset = CSR()
|
||||||
self.qpll_locked = CSRStatus()
|
self.qpll_locked = CSRStatus()
|
||||||
|
|
||||||
|
@ -47,7 +42,6 @@ class CXP_DownConn(Module, AutoCSR):
|
||||||
# PLL
|
# PLL
|
||||||
qpll.reset.eq(self.qpll_reset.re),
|
qpll.reset.eq(self.qpll_reset.re),
|
||||||
self.qpll_locked.status.eq(qpll.lock),
|
self.qpll_locked.status.eq(qpll.lock),
|
||||||
|
|
||||||
# GTX
|
# GTX
|
||||||
self.txinit_phaligndone.status.eq(gtx.tx_init.Xxphaligndone),
|
self.txinit_phaligndone.status.eq(gtx.tx_init.Xxphaligndone),
|
||||||
self.rxinit_phaligndone.status.eq(gtx.rx_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.rx_restart.eq(self.rx_restart.re),
|
||||||
gtx.tx_init.clk_path_ready.eq(self.tx_start_init.storage),
|
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_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.tx_rate.eq(self.tx_div.storage),
|
||||||
gtx.rx_rate.eq(self.rx_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
|
# 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),
|
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.aligner_en_rxclk, o_O=pmod_pads[0]),
|
Instance("OBUF", i_I=gtx.comma_det.word_aligned, 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[1]),
|
||||||
Instance("OBUF", i_I=gtx.comma_det.restart, o_O=pmod_pads[2]),
|
Instance("OBUF", i_I=gtx.comma_det.aligner_en_rxclk, 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.check_reset, 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.comma_aligned, o_O=pmod_pads[4]),
|
||||||
Instance("OBUF", i_I=gtx.comma_det.valid_data, o_O=pmod_pads[5]),
|
Instance("OBUF", i_I=gtx.comma_det.comma_seen, o_O=pmod_pads[5]),
|
||||||
# Instance("OBUF", i_I=, o_O=pmod_pads[7]),
|
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
|
# DEBUG: datain
|
||||||
counter_max = 2
|
|
||||||
counter = Signal(max=counter_max)
|
|
||||||
|
|
||||||
self.data_0 = CSRStorage(8)
|
self.data_0 = CSRStorage(8)
|
||||||
self.data_1 = CSRStorage(8)
|
self.data_1 = CSRStorage(8)
|
||||||
|
@ -131,39 +194,36 @@ class CXP_DownConn(Module, AutoCSR):
|
||||||
self.decoded_data_1 = CSRStatus(8)
|
self.decoded_data_1 = CSRStatus(8)
|
||||||
self.decoded_k_0 = CSRStatus()
|
self.decoded_k_0 = CSRStatus()
|
||||||
self.decoded_k_1 = CSRStatus()
|
self.decoded_k_1 = CSRStatus()
|
||||||
|
|
||||||
self.shifted = CSRStatus(9)
|
|
||||||
|
|
||||||
|
|
||||||
self.sync.cxp_gtx_tx += [
|
self.sync.cxp_gtx_tx += [
|
||||||
If(counter == 0,
|
self.gtx.encoder.d[0].eq(0xBC),
|
||||||
self.gtx.encoder.d[0].eq(self.data_0.storage),
|
self.gtx.encoder.k[0].eq(1),
|
||||||
self.gtx.encoder.k[0].eq(self.control_bit_0.storage),
|
self.gtx.encoder.d[1].eq(0x3C),
|
||||||
self.gtx.encoder.d[1].eq(self.data_1.storage),
|
self.gtx.encoder.k[1].eq(1),
|
||||||
self.gtx.encoder.k[1].eq(self.control_bit_1.storage),
|
self.gtx.encoder.d[2].eq(0x3C),
|
||||||
counter.eq(counter+1),
|
self.gtx.encoder.k[2].eq(1),
|
||||||
).Elif(counter == 1,
|
self.gtx.encoder.d[3].eq(0xB5),
|
||||||
self.gtx.encoder.d[0].eq(self.data_2.storage),
|
self.gtx.encoder.k[3].eq(0),
|
||||||
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_0.status.eq(self.gtx.encoder.output[0]),
|
||||||
self.encoded_1.status.eq(self.gtx.encoder.output[1]),
|
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 += [
|
self.sync.cxp_gtx_rx += [
|
||||||
self.rxdata_0.status.eq(self.gtx.decoders[0].input),
|
If(stb_timer == 0,
|
||||||
self.decoded_data_0.status.eq(self.gtx.decoders[0].d),
|
self.rxdata_0.status.eq(self.gtx.decoders[0].input),
|
||||||
self.decoded_k_0.status.eq(self.gtx.decoders[0].k),
|
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.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),
|
||||||
# If(self.gtx.clk_aligner.comma_det.detected,
|
stb_timer.eq(stb_timer.reset),
|
||||||
# self.shifted.status.eq(self.gtx.clk_aligner.comma_det.bitshift),
|
).Else(
|
||||||
# )
|
stb_timer.eq(stb_timer - 1),
|
||||||
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -184,45 +244,43 @@ class QPLL(Module):
|
||||||
self.lock = Signal()
|
self.lock = Signal()
|
||||||
self.reset = 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
|
# WARNING: VCO cannot do 12.5GHz on ZC706
|
||||||
# VCO freq = sys*qpll_fbdiv
|
# VCO freq = sys*qpll_fbdiv
|
||||||
# PLL output = VCO/2
|
# PLL output = VCO/2
|
||||||
qpll_fbdiv = 0b0100100000
|
qpll_fbdiv = 0b0100100000 # 80 div
|
||||||
qpll_fbdiv_ratio = 1
|
qpll_fbdiv_ratio = 1
|
||||||
|
|
||||||
fbdiv_real = 80
|
fbdiv_real = 80
|
||||||
refclk_div = 1
|
refclk_div = 1
|
||||||
self.Xxout_div = 8
|
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
|
||||||
|
|
||||||
|
# fbdiv_real = 100
|
||||||
|
# refclk_div = 2
|
||||||
|
# self.Xxout_div = 2
|
||||||
|
|
||||||
|
self.tx_usrclk_freq = (sys_clk_freq*fbdiv_real/self.Xxout_div)/40
|
||||||
|
|
||||||
# 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 += [
|
self.specials += [
|
||||||
Instance("GTXE2_COMMON",
|
Instance("GTXE2_COMMON",
|
||||||
i_QPLLREFCLKSEL=0b001,
|
i_QPLLREFCLKSEL=0b001,
|
||||||
i_GTREFCLK0=refclk,
|
i_GTREFCLK0=refclk,
|
||||||
|
|
||||||
i_QPLLPD=0,
|
i_QPLLPD=0,
|
||||||
i_QPLLRESET=reset,
|
i_QPLLRESET=self.reset,
|
||||||
i_QPLLLOCKEN=1,
|
i_QPLLLOCKEN=1,
|
||||||
o_QPLLLOCK=self.lock,
|
o_QPLLLOCK=self.lock,
|
||||||
o_QPLLOUTCLK=self.clk,
|
o_QPLLOUTCLK=self.clk,
|
||||||
|
@ -256,15 +314,24 @@ class QPLL(Module):
|
||||||
i_RCALENB=0b1,
|
i_RCALENB=0b1,
|
||||||
i_QPLLRSVD1=0b0,
|
i_QPLLRSVD1=0b0,
|
||||||
i_QPLLRSVD2=0b11111,
|
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
|
# Warning: Xilinx transceivers are LSB first, and comma needs to be flipped
|
||||||
|
# compared to the usual 8b10b binary representation.
|
||||||
class Comma_Detector(Module):
|
class Comma_Detector(Module):
|
||||||
def __init__(self, comma, check_period=50_000, width=20):
|
def __init__(self, comma, check_period=1_000_000):
|
||||||
self.data = Signal(width)
|
self.data = Signal(20)
|
||||||
self.rxinit_done = Signal()
|
self.word_aligned = Signal()
|
||||||
|
|
||||||
self.aligner_en_rxclk = Signal()
|
self.aligner_en_rxclk = Signal()
|
||||||
self.ready = Signal()
|
self.ready = Signal()
|
||||||
|
@ -274,92 +341,141 @@ class Comma_Detector(Module):
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
self.comma_aligned = Signal()
|
# The built-in RXBYTEISALIGNED can be falsely asserted at linerate higher than 5Gbps
|
||||||
self.valid_data = Signal()
|
# - UG476 (v1.12.1) p.228
|
||||||
|
|
||||||
self.submodules.recheck_ps = recheck_ps = PulseSynchronizer("sys", "cxp_gtx_rx")
|
# The validity of data & comma are checked externally
|
||||||
|
|
||||||
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)
|
|
||||||
),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
check_counter = Signal(reset=check_period-1, max=check_period)
|
check_counter = Signal(reset=check_period-1, max=check_period)
|
||||||
check = Signal()
|
# check = Signal()
|
||||||
|
|
||||||
aligner_en_sys = Signal()
|
|
||||||
self.specials += MultiReg(aligner_en_sys, self.aligner_en_rxclk, odomain="cxp_gtx_rx")
|
|
||||||
|
|
||||||
self.sync += [
|
self.sync += [
|
||||||
check.eq(0),
|
self.restart.eq(0),
|
||||||
If(check_counter == 0,
|
# check.eq(0),
|
||||||
check_counter.eq(check_counter.reset),
|
# check_reset.i.eq(0),
|
||||||
check.eq(1),
|
If(~self.ready,
|
||||||
).Else(
|
If(check_counter == 0,
|
||||||
check_counter.eq(check_counter - 1),
|
check_counter.eq(check_counter.reset),
|
||||||
|
# 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",
|
has_error = Signal()
|
||||||
aligner_en_sys.eq(1),
|
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(
|
||||||
|
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"),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
rxfsm.act("WAIT_ALIGNED_DATA",
|
||||||
|
timer.wait.eq(1),
|
||||||
|
If(timer.done,
|
||||||
|
check_reset.eq(1),
|
||||||
|
NextState("CHECKING"),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
rxfsm.act("CHECKING",
|
||||||
If(check,
|
If(check,
|
||||||
recheck_ps.i.eq(1),
|
check_reset.eq(1),
|
||||||
If(aligned,
|
If(comma_seen & (~error_seen),
|
||||||
NextState("WAIT_NO_ERROR"),
|
NextState("READY"),
|
||||||
).Else(
|
).Else(
|
||||||
self.restart.eq(1),
|
NextState("ALIGNING")
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
fsm.act("WAIT_NO_ERROR",
|
ready = Signal()
|
||||||
aligner_en_sys.eq(1),
|
self.specials += MultiReg(ready, self.ready)
|
||||||
|
rxfsm.act("READY",
|
||||||
|
ready.eq(1),
|
||||||
If(check,
|
If(check,
|
||||||
recheck_ps.i.eq(1),
|
check_reset.eq(1),
|
||||||
If(aligned & valid_data,
|
If(~(comma_seen & (~error_seen)),
|
||||||
NextState("READY"),
|
NextState("ALIGNING"),
|
||||||
).Else(
|
|
||||||
self.restart.eq(1),
|
|
||||||
NextState("IDLE"),
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
fsm.act("READY",
|
|
||||||
self.ready.eq(1),
|
|
||||||
If(check,
|
|
||||||
recheck_ps.i.eq(1),
|
|
||||||
If(~(aligned & valid_data),
|
|
||||||
self.restart.eq(1),
|
|
||||||
NextState("IDLE"),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class GTX(Module):
|
class GTX(Module):
|
||||||
# Settings:
|
# Settings:
|
||||||
|
@ -374,7 +490,7 @@ class GTX(Module):
|
||||||
|
|
||||||
# linerate = USRCLK * datawidth
|
# linerate = USRCLK * datawidth
|
||||||
pll_fbout_mult = 8
|
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.tx_restart = Signal()
|
||||||
self.rx_restart = Signal()
|
self.rx_restart = Signal()
|
||||||
|
@ -386,9 +502,18 @@ class GTX(Module):
|
||||||
self.tx_rate = Signal(3)
|
self.tx_rate = Signal(3)
|
||||||
self.rx_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")(
|
self.submodules.decoders = [ClockDomainsRenamer("cxp_gtx_rx")(
|
||||||
(Decoder(True))) for _ in range(2)]
|
(Decoder(True))) for _ in range(4)]
|
||||||
|
|
||||||
|
|
||||||
# transceiver direct clock outputs
|
# transceiver direct clock outputs
|
||||||
|
@ -411,10 +536,11 @@ class GTX(Module):
|
||||||
rx_init.cplllock.eq(qpll.lock)
|
rx_init.cplllock.eq(qpll.lock)
|
||||||
]
|
]
|
||||||
|
|
||||||
txdata = Signal(20)
|
txdata = Signal(40)
|
||||||
rxdata = Signal(20)
|
rxdata = Signal(40)
|
||||||
|
|
||||||
comma_align_en = Signal()
|
word_aligned = 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",
|
||||||
|
@ -472,11 +598,11 @@ class GTX(Module):
|
||||||
i_TXINHIBIT=~self.txenable,
|
i_TXINHIBIT=~self.txenable,
|
||||||
|
|
||||||
# TX data
|
# TX data
|
||||||
p_TX_DATA_WIDTH=20,
|
p_TX_DATA_WIDTH=40,
|
||||||
p_TX_INT_DATAWIDTH=0,
|
p_TX_INT_DATAWIDTH=1,
|
||||||
i_TXCHARDISPMODE=Cat(txdata[9], txdata[19]),
|
i_TXCHARDISPMODE=Cat(txdata[9], txdata[19], txdata[29], txdata[39]),
|
||||||
i_TXCHARDISPVAL=Cat(txdata[8], txdata[18]),
|
i_TXCHARDISPVAL=Cat(txdata[8], txdata[18], txdata[28], txdata[38]),
|
||||||
i_TXDATA=Cat(txdata[:8], txdata[10:18]),
|
i_TXDATA=Cat(txdata[:8], txdata[10:18], txdata[20:28], txdata[30:38]),
|
||||||
i_TXUSRCLK=ClockSignal("cxp_gtx_tx"),
|
i_TXUSRCLK=ClockSignal("cxp_gtx_tx"),
|
||||||
i_TXUSRCLK2=ClockSignal("cxp_gtx_tx"),
|
i_TXUSRCLK2=ClockSignal("cxp_gtx_tx"),
|
||||||
|
|
||||||
|
@ -535,27 +661,29 @@ class GTX(Module):
|
||||||
p_CLK_COR_SEQ_2_ENABLE=0b1111,
|
p_CLK_COR_SEQ_2_ENABLE=0b1111,
|
||||||
|
|
||||||
# RX data
|
# RX data
|
||||||
p_RX_DATA_WIDTH=20,
|
p_RX_DATA_WIDTH=40,
|
||||||
p_RX_INT_DATAWIDTH=0,
|
p_RX_INT_DATAWIDTH=1,
|
||||||
o_RXDISPERR=Cat(rxdata[9], rxdata[19]),
|
o_RXDISPERR=Cat(rxdata[9], rxdata[19], rxdata[29], rxdata[39]),
|
||||||
o_RXCHARISK=Cat(rxdata[8], rxdata[18]),
|
o_RXCHARISK=Cat(rxdata[8], rxdata[18], rxdata[28], rxdata[38]),
|
||||||
o_RXDATA=Cat(rxdata[:8], rxdata[10:18]),
|
o_RXDATA=Cat(rxdata[:8], rxdata[10:18], rxdata[20:28], rxdata[30:38]),
|
||||||
|
|
||||||
# RX Byte and Word Alignment Attributes
|
# RX Byte and Word Alignment Attributes
|
||||||
p_ALIGN_COMMA_DOUBLE="FALSE",
|
p_ALIGN_COMMA_DOUBLE="FALSE",
|
||||||
p_ALIGN_COMMA_ENABLE=0b1111111111,
|
p_ALIGN_COMMA_ENABLE=0b1111111111,
|
||||||
p_ALIGN_COMMA_WORD=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_DET="TRUE",
|
||||||
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="FALSE",
|
p_SHOW_REALIGN_COMMA="TRUE",
|
||||||
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,
|
||||||
i_RXPCOMMAALIGNEN=comma_align_en,
|
i_RXPCOMMAALIGNEN=comma_aligner_en,
|
||||||
i_RXMCOMMAALIGNEN=comma_align_en,
|
i_RXMCOMMAALIGNEN=comma_aligner_en,
|
||||||
i_RXCOMMADETEN=1, # enable auto word alignment
|
i_RXCOMMADETEN=1,
|
||||||
|
i_RXSLIDE=0,
|
||||||
|
o_RXBYTEISALIGNED=word_aligned,
|
||||||
|
|
||||||
# RX 8B/10B Decoder Attributes
|
# RX 8B/10B Decoder Attributes
|
||||||
p_RX_DISPERR_SEQ_MATCH="FALSE",
|
p_RX_DISPERR_SEQ_MATCH="FALSE",
|
||||||
|
@ -601,6 +729,16 @@ class GTX(Module):
|
||||||
o_GTXTXP=pads.txp,
|
o_GTXTXP=pads.txp,
|
||||||
o_GTXTXN=pads.txn,
|
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
|
# ! loopback for debugging
|
||||||
i_LOOPBACK = self.loopback_mode,
|
i_LOOPBACK = self.loopback_mode,
|
||||||
p_TX_LOOPBACK_DRIVE_HIZ = "FALSE",
|
p_TX_LOOPBACK_DRIVE_HIZ = "FALSE",
|
||||||
|
@ -650,7 +788,7 @@ class GTX(Module):
|
||||||
p_CLKFBOUT_MULT=pll_fbout_mult, p_DIVCLK_DIVIDE=1,
|
p_CLKFBOUT_MULT=pll_fbout_mult, p_DIVCLK_DIVIDE=1,
|
||||||
i_CLKFBIN=txpll_fb_clk, o_CLKFBOUT=txpll_fb_clk,
|
i_CLKFBIN=txpll_fb_clk, o_CLKFBOUT=txpll_fb_clk,
|
||||||
|
|
||||||
# frequency = linerate/20
|
# frequency = linerate/40
|
||||||
p_CLKOUT0_DIVIDE=txusr_pll_div, p_CLKOUT0_PHASE=0.0, o_CLKOUT0=txpll_clkout,
|
p_CLKOUT0_DIVIDE=txusr_pll_div, p_CLKOUT0_PHASE=0.0, o_CLKOUT0=txpll_clkout,
|
||||||
|
|
||||||
# Dynamic Reconfiguration Ports
|
# Dynamic Reconfiguration Ports
|
||||||
|
@ -676,17 +814,19 @@ class GTX(Module):
|
||||||
]
|
]
|
||||||
|
|
||||||
self.comb += [
|
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[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.submodules.comma_det = comma_det = Comma_Detector(0b0101111100)
|
||||||
self.comb += [
|
self.comb += [
|
||||||
comma_det.data.eq(rxdata),
|
comma_det.data.eq(rxdata),
|
||||||
comma_det.rxinit_done.eq(rx_init.done),
|
comma_det.word_aligned.eq(word_aligned),
|
||||||
comma_align_en.eq(comma_det.aligner_en_rxclk),
|
comma_aligner_en.eq(comma_det.aligner_en_rxclk),
|
||||||
self.rx_ready.eq(comma_det.ready),
|
self.rx_ready.eq(comma_det.ready),
|
||||||
|
|
||||||
rx_init.restart.eq(self.rx_restart | comma_det.restart),
|
rx_init.restart.eq(self.rx_restart | comma_det.restart),
|
||||||
|
|
|
@ -2,6 +2,7 @@ use embedded_hal::prelude::_embedded_hal_blocking_delay_DelayUs;
|
||||||
use libboard_zynq::{println, timer::GlobalTimer};
|
use libboard_zynq::{println, timer::GlobalTimer};
|
||||||
use log::info;
|
use log::info;
|
||||||
|
|
||||||
|
// use log::info;
|
||||||
use crate::pl::csr;
|
use crate::pl::csr;
|
||||||
|
|
||||||
pub struct CXP_DownConn_Settings {
|
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);
|
csr::cxp::downconn_txenable_write(1);
|
||||||
|
|
||||||
info!("waiting for rx to align...");
|
info!("waiting for rx to align...");
|
||||||
timer.delay_us(50_000);
|
// 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!");
|
||||||
|
|
||||||
// csr::cxp::data_3_write(data);
|
// println!("0xA8 = {:#06x}", read(0x62));
|
||||||
// csr::cxp::control_bit_3_write(control_bit);
|
// write(0x62, 0x001A);
|
||||||
// println!(
|
// println!("0xA8 = {:#06x}", read(0x62));
|
||||||
// "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 {
|
// for _ in 0..20 {
|
||||||
loop {
|
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);
|
// timer.delay_us(100);
|
||||||
println!(
|
// if data0 == 0b0101111100 || data0 == 0b1010000011 {
|
||||||
"data = {:#022b} | rx ready = {}",
|
// println!(
|
||||||
(csr::cxp::downconn_rxdata_0_read() as u32 | ((csr::cxp::downconn_rxdata_1_read() as u32) << 10)),
|
// "data[0] = {:#012b} comma = {} | rx ready = {}",
|
||||||
csr::cxp::downconn_rx_ready_read()
|
// 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);
|
timer.delay_us(1_000_000);
|
||||||
// println!(
|
|
||||||
// "data[0] = {:#012b} data[1] = {:#012b}",
|
// NOTE:decode data
|
||||||
// csr::cxp::rxdata_0_read(),
|
// let data0_decoded = csr::cxp::downconn_decoded_data_0_read();
|
||||||
// csr::cxp::rxdata_1_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!(
|
// println!(
|
||||||
// "decoded_data[0] = {:#04x} decoded_k[0] = {:#b} decoded_data[1] = {:#04x} decoded_k[1] = {:#b}",
|
// "decoded_data[0] = {:#04x} decoded_k[0] = {:#b} decoded_data[1] = {:#04x} decoded_k[1] = {:#b}",
|
||||||
// csr::cxp::downconn_decoded_data_0_read(),
|
// data0_decoded,
|
||||||
// csr::cxp::downconn_decoded_k_0_read(),
|
// data0_k,
|
||||||
// csr::cxp::downconn_decoded_data_1_read(),
|
// data1_decoded,
|
||||||
// csr::cxp::downconn_decoded_k_1_read(),
|
// data1_k,
|
||||||
// );
|
// );
|
||||||
|
// timer.delay_us(1_000_000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -134,44 +150,203 @@ pub fn setup(timer: &mut GlobalTimer, speed: CXP_SPEED) {
|
||||||
println!("==============================================================================");
|
println!("==============================================================================");
|
||||||
}
|
}
|
||||||
|
|
||||||
change_linerate(timer, speed);
|
CXP_GTX::change_linerate(timer, speed);
|
||||||
|
|
||||||
loopback_testing(timer, 0x00, 0);
|
loopback_testing(timer, 0x00, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn change_linerate(timer: &mut GlobalTimer, speed: CXP_SPEED) {
|
pub mod CXP_GTX {
|
||||||
info!("Changing datarate to {:?}", speed);
|
use super::*;
|
||||||
// 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
|
struct RX_CDR_CFG {
|
||||||
change_qpll_settings(speed);
|
pub cfg_reg0: u16, //0x0A8
|
||||||
|
pub cfg_reg1: u16, //0x0A9
|
||||||
unsafe {
|
pub cfg_reg2: u16, //0x0AA
|
||||||
csr::cxp::downconn_qpll_reset_write(1);
|
pub cfg_reg3: u16, //0x0AB
|
||||||
info!("waiting for QPLL/CPLL to lock...");
|
pub cfg_reg4: u16, //0x0AC
|
||||||
while csr::cxp::downconn_qpll_locked_read() != 1 {}
|
|
||||||
info!("QPLL locked");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe {
|
pub fn change_linerate(timer: &mut GlobalTimer, speed: CXP_SPEED) {
|
||||||
csr::cxp::downconn_tx_restart_write(1);
|
info!("Changing datarate to {:?}", speed);
|
||||||
csr::cxp::downconn_rx_restart_write(1);
|
// DEBUG: DRP pll for TXUSRCLK = freq(linerate)/20
|
||||||
|
let settings = txusrclk::get_txusrclk_config(speed);
|
||||||
|
txusrclk::setup(timer, settings);
|
||||||
|
|
||||||
|
change_qpll_settings(speed);
|
||||||
|
change_cdr_cfg(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) {
|
fn change_qpll_settings(speed: CXP_SPEED) {
|
||||||
let divider = match speed {
|
match speed {
|
||||||
CXP_SPEED::CXP_1 => 0b100, // Divided by 8
|
CXP_SPEED::CXP_12 => {
|
||||||
CXP_SPEED::CXP_2 | CXP_SPEED::CXP_3 => 0b011, // Divided by 4
|
panic!("CXP 12.5Gbps is not supported on zc706");
|
||||||
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 {
|
// this switches between High and Low band VCO
|
||||||
csr::cxp::downconn_rx_div_write(divider);
|
// NOT needed if VCO can do 12.5GHz
|
||||||
csr::cxp::downconn_tx_div_write(divider);
|
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));
|
||||||
|
|
||||||
|
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 => 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) {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -312,6 +487,23 @@ pub mod txusrclk {
|
||||||
pub fn get_txusrclk_config(speed: CXP_SPEED) -> PLLSetting {
|
pub fn get_txusrclk_config(speed: CXP_SPEED) -> PLLSetting {
|
||||||
match speed {
|
match speed {
|
||||||
CXP_SPEED::CXP_1 => {
|
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
|
// CLKFBOUT_MULT = 8, DIVCLK_DIVIDE = 1 , CLKOUT0_DIVIDE = 16
|
||||||
// TXUSRCLK=62.5MHz
|
// TXUSRCLK=62.5MHz
|
||||||
PLLSetting {
|
PLLSetting {
|
||||||
|
@ -328,7 +520,24 @@ pub mod txusrclk {
|
||||||
filt_reg2: 0x9100, //0x4F
|
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
|
// CLKFBOUT_MULT = 8, DIVCLK_DIVIDE = 1 , CLKOUT0_DIVIDE = 8
|
||||||
// TXUSRCLK=125MHz
|
// TXUSRCLK=125MHz
|
||||||
PLLSetting {
|
PLLSetting {
|
||||||
|
@ -345,9 +554,9 @@ pub mod txusrclk {
|
||||||
filt_reg2: 0x9100, //0x4F
|
filt_reg2: 0x9100, //0x4F
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CXP_SPEED::CXP_3 => {
|
CXP_SPEED::CXP_6 => {
|
||||||
// CLKFBOUT_MULT = 10, DIVCLK_DIVIDE = 1 , CLKOUT0_DIVIDE = 8
|
// CLKFBOUT_MULT = 10, DIVCLK_DIVIDE = 1 , CLKOUT0_DIVIDE = 8
|
||||||
// TXUSRCLK=125MHz
|
// TXUSRCLK=156.25MHz
|
||||||
PLLSetting {
|
PLLSetting {
|
||||||
clkout0_reg1: 0x1104, //0x08
|
clkout0_reg1: 0x1104, //0x08
|
||||||
clkout0_reg2: 0x0000, //0x09
|
clkout0_reg2: 0x0000, //0x09
|
||||||
|
@ -362,7 +571,7 @@ pub mod txusrclk {
|
||||||
filt_reg2: 0x1900, //0x4F
|
filt_reg2: 0x1900, //0x4F
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CXP_SPEED::CXP_5 => {
|
CXP_SPEED::CXP_10 => {
|
||||||
// CLKFBOUT_MULT = 8, DIVCLK_DIVIDE = 1 , CLKOUT0_DIVIDE = 4
|
// CLKFBOUT_MULT = 8, DIVCLK_DIVIDE = 1 , CLKOUT0_DIVIDE = 4
|
||||||
// TXUSRCLK=250MHz
|
// TXUSRCLK=250MHz
|
||||||
PLLSetting {
|
PLLSetting {
|
||||||
|
@ -379,7 +588,7 @@ pub mod txusrclk {
|
||||||
filt_reg2: 0x9100, //0x4F
|
filt_reg2: 0x9100, //0x4F
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CXP_SPEED::CXP_6 => {
|
CXP_SPEED::CXP_12 => {
|
||||||
// CLKFBOUT_MULT = 10, DIVCLK_DIVIDE = 1 , CLKOUT0_DIVIDE = 4
|
// CLKFBOUT_MULT = 10, DIVCLK_DIVIDE = 1 , CLKOUT0_DIVIDE = 4
|
||||||
// TXUSRCLK=312.5MHz
|
// TXUSRCLK=312.5MHz
|
||||||
PLLSetting {
|
PLLSetting {
|
||||||
|
@ -396,40 +605,6 @@ pub mod txusrclk {
|
||||||
filt_reg2: 0x1900, //0x4F
|
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