1
0
Fork 0

Compare commits

..

No commits in common. "2aef11143a6a919c1eb86611bf1bbf70b483dcf0" and "6c53447808e4bf95fe935664f6191594eda91294" have entirely different histories.

4 changed files with 316 additions and 802 deletions

View File

@ -2,20 +2,131 @@ from migen import *
from misoc.interconnect.csr import *
from misoc.cores.liteeth_mini.mac.crc import LiteEthMACCRCEngine
from artiq.gateware.drtio.core import ChannelInterface
from cxp_downconn import CXP_DownConn
from cxp_upconn import CXP_UpConn
class CXP(Module, AutoCSR):
def __init__(self, refclk, pads, sys_clk_freq, debug_sma, pmod_pads):
nchannels = len(pads)
self.rx_start_init = CSRStorage()
self.rx_restart = CSRStatus()
self.rx_bypass_clk_alignment = CSRStorage()
self.tx_start_init = CSRStorage()
self.tx_restart = CSRStorage()
self.loopback_mode = CSRStorage(3)
self.txinit_phaligndone = CSRStatus()
self.rxinit_phaligndone = CSRStatus()
self.rx_ready = CSRStatus()
self.data_0 = CSRStorage(8)
self.data_1 = CSRStorage(8)
self.control_bit_0 = CSRStorage()
self.control_bit_1 = CSRStorage()
self.encoded_0 = CSRStatus(10)
self.encoded_1 = CSRStatus(10)
self.rxdata_0 = CSRStatus(10)
self.rxdata_1 = CSRStatus(10)
self.decoded_data_0 = CSRStatus(8)
self.decoded_data_1 = CSRStatus(8)
self.decoded_k_0 = CSRStatus()
self.decoded_k_1 = CSRStatus()
# # #
self.submodules.crc = CXP_CRC(8)
# FIFOs with transmission priority
# 0: Trigger packet
# 1: IO acknowledgment for trigger packet
# 2: All other packets
self.submodules.upconn = CXP_UpConn(debug_sma, sys_clk_freq, pmod_pads)
self.submodules.downconn = CXP_DownConn(refclk, pads, sys_clk_freq, debug_sma, pmod_pads)
# single & master tx_mode can lock with rx in loopback
self.submodules.gtx = gtx = CXP_DownConn(refclk, pads, sys_clk_freq, tx_mode="single", rx_mode="single")
# DEBUG:loopback
self.sync += gtx.loopback_mode.eq(self.loopback_mode.storage)
# # ! debug sma
# self.specials += [
# Instance("OBUF", i_I=gtx.rxoutclk, o_O=debug_sma.p_tx),
# Instance("OBUF", i_I=gtx.cd_cxp_gtx_tx.clk, o_O=debug_sma.n_rx)
# ]
self.comb += [
self.txinit_phaligndone.status.eq(self.gtx.tx_init.Xxphaligndone),
self.rxinit_phaligndone.status.eq(self.gtx.rx_init.Xxphaligndone),
self.rx_ready.status.eq(self.gtx.rx_ready),
]
self.sync.cxp_gtx_tx += [
self.gtx.encoder.d[0].eq(self.data_0.storage),
self.gtx.encoder.k[0].eq(self.control_bit_0.storage),
self.encoded_0.status.eq(self.gtx.encoder.output[0]),
self.gtx.encoder.d[1].eq(self.data_1.storage),
self.gtx.encoder.k[1].eq(self.control_bit_1.storage),
self.encoded_1.status.eq(self.gtx.encoder.output[1]),
]
self.sync.cxp_gtx_rx += [
self.rxdata_0.status.eq(self.gtx.decoders[0].input),
self.decoded_data_0.status.eq(self.gtx.decoders[0].d),
self.decoded_k_0.status.eq(self.gtx.decoders[0].k),
self.rxdata_1.status.eq(self.gtx.decoders[1].input),
self.decoded_data_1.status.eq(self.gtx.decoders[1].d),
self.decoded_k_1.status.eq(self.gtx.decoders[1].k),
]
# TODO: rip encoder & rx clockalignment out of CXP_GTX
# TODO: use expose encoder & decoder from CXP
# encoder.k = 1 if sending control bit, different calculation
# encoder.d = data 8 bit
channel_interface = ChannelInterface(gtx.encoder, gtx.decoders)
self.comb += channel_interface.rx_ready.eq(gtx.rx_ready)
channel_interfaces = []
channel_interfaces.append(channel_interface)
# TransceiverInterface, just adding cxp_rx_<num>
self.stable_clkin = CSRStorage()
self.txenable = CSRStorage(len(channel_interfaces))
for i in range(len(channel_interfaces)):
name = "cxp_gtx_rx" + str(i)
setattr(self.clock_domains, "cd_"+name, ClockDomain(name=name))
self.channels = channel_interfaces
# TODO: add tx_phase_alignment for multi CXP
# The TX phase alignment will fail with a wrong TXUSRCLK frequency
self.comb += [
gtx.rx_init.clk_path_ready.eq(self.rx_start_init.storage),
gtx.tx_init.clk_path_ready.eq(self.tx_start_init.storage),
gtx.txenable.eq(self.txenable.storage[0]),
gtx.tx_restart.eq(self.tx_restart.storage),
]
# TODO: Connect multilane cxp_tx
# TODO: Connect slave i's `cxp_gtx_rx` clock to `cxp_gtx_rxi` clock
self.comb += [
getattr(self, "cd_cxp_gtx_rx" + str(0)).clk.eq(self.gtx.cd_cxp_gtx_rx.clk),
getattr(self, "cd_cxp_gtx_rx" + str(0)).rst.eq(self.gtx.cd_cxp_gtx_rx.rst)
]
class CXP_CRC(Module, AutoCSR):
width = 32

View File

@ -1,5 +1,6 @@
from migen import *
from migen.genlib.resetsync import AsyncResetSynchronizer
from migen.genlib.cdc import MultiReg, PulseSynchronizer
from misoc.cores.code_8b10b import Encoder, Decoder
from misoc.interconnect.csr import *
@ -7,352 +8,140 @@ from misoc.interconnect.csr import *
from artiq.gateware.drtio.transceiver.gtx_7series_init import *
from operator import add
from math import ceil
from functools import reduce
class CXP_DownConn(Module, AutoCSR):
def __init__(self, refclk, pads, sys_clk_freq, debug_sma, pmod_pads):
self.rx_start_init = CSRStorage()
self.rx_restart = CSR()
self.tx_start_init = CSRStorage()
self.tx_restart = CSR()
self.txenable = CSRStorage()
self.txinit_phaligndone = CSRStatus()
self.rxinit_phaligndone = CSRStatus()
self.rx_ready = CSRStatus()
self.tx_div = CSRStorage(3)
self.rx_div = CSRStorage(3)
self.qpll_reset = CSR()
self.qpll_locked = CSRStatus()
# # #
self.submodules.qpll = qpll = QPLL(refclk, sys_clk_freq)
# single & master tx_mode can lock with rx in loopback
self.submodules.gtx = gtx = GTX(self.qpll, pads, sys_clk_freq, tx_mode="single", rx_mode="single")
# TEST: txusrclk alignment
# 1) use GTREFCLK with TXSYSCLKSEL = 0b10 -> still inconsistant
# 2) tie qpllPDEN with ~qpll.reset, -> inconsistant
# 3) seems like tx_init gtxXreset fall too soon (cplllock hold hi too long) <--- this a cross clk domain issue :<
self.sync += [
# PLL
qpll.reset.eq(self.qpll_reset.re),
self.qpll_locked.status.eq(qpll.lock),
# GTX
self.txinit_phaligndone.status.eq(gtx.tx_init.Xxphaligndone),
self.rxinit_phaligndone.status.eq(gtx.rx_init.Xxphaligndone),
self.rx_ready.status.eq(gtx.rx_ready),
gtx.txenable.eq(self.txenable.storage[0]),
gtx.tx_restart.eq(self.tx_restart.re),
gtx.rx_restart.eq(self.rx_restart.re),
gtx.tx_init.clk_path_ready.eq(self.tx_start_init.storage),
gtx.rx_init.clk_path_ready.eq(self.rx_start_init.storage),
# gtx.rx_alignment_en.eq(self.rx_data_alignment.storage),
# GTX DRP
gtx.tx_rate.eq(self.tx_div.storage),
gtx.rx_rate.eq(self.rx_div.storage),
]
# DEBUG: txusrclk PLL DRG
self.txpll_reset = CSRStorage()
self.pll_daddr = CSRStorage(7)
self.pll_dclk = CSRStorage()
self.pll_den = CSRStorage()
self.pll_din = CSRStorage(16)
self.pll_dwen = CSRStorage()
self.txpll_locked = CSRStatus()
self.pll_dout = CSRStatus(16)
self.pll_dready = CSRStatus()
self.comb += [
gtx.txpll_reset.eq(self.txpll_reset.storage),
gtx.pll_daddr.eq(self.pll_daddr.storage),
gtx.pll_dclk.eq(self.pll_dclk.storage),
gtx.pll_den.eq(self.pll_den.storage),
gtx.pll_din.eq(self.pll_din.storage),
gtx.pll_dwen.eq(self.pll_dwen.storage),
self.txpll_locked.status.eq(gtx.txpll_locked),
self.pll_dout.status.eq(gtx.pll_dout),
self.pll_dready.status.eq(gtx.pll_dready),
]
# DEBUG:loopback
self.loopback_mode = CSRStorage(3)
self.comb += gtx.loopback_mode.eq(self.loopback_mode.storage)
# DEBUG: IO SMA & PMOD
self.specials += [
Instance("OBUF", i_I=gtx.rxoutclk, o_O=debug_sma.p_tx),
Instance("OBUF", i_I=gtx.cd_cxp_gtx_tx.clk, o_O=debug_sma.n_rx),
# pmod 0-7 pin
Instance("OBUF", i_I=gtx.clk_aligner.rxslide, o_O=pmod_pads[0]),
Instance("OBUF", i_I=gtx.clk_aligner.ready, o_O=pmod_pads[1]),
# Instance("OBUF", i_I=gtx.tx_init.Xxdlysresetdone , o_O=pmod_pads[2]),
# Instance("OBUF", i_I=gtx.tx_init.Xxphaligndone , o_O=pmod_pads[3]),
# Instance("OBUF", i_I=, o_O=pmod_pads[4]),
# Instance("OBUF", i_I=, o_O=pmod_pads[5]),
# Instance("OBUF", i_I=, o_O=pmod_pads[6]),
# Instance("OBUF", i_I=, o_O=pmod_pads[7]),
]
# DEBUG: datain
counter_max = 2
counter = Signal(max=counter_max)
self.data_0 = CSRStorage(8)
self.data_1 = CSRStorage(8)
self.data_2 = CSRStorage(8)
self.data_3 = CSRStorage(8)
self.control_bit_0 = CSRStorage()
self.control_bit_1 = CSRStorage()
self.control_bit_2 = CSRStorage()
self.control_bit_3 = CSRStorage()
self.encoded_0 = CSRStatus(10)
self.encoded_1 = CSRStatus(10)
self.rxdata_0 = CSRStatus(10)
self.rxdata_1 = CSRStatus(10)
self.decoded_data_0 = CSRStatus(8)
self.decoded_data_1 = CSRStatus(8)
self.decoded_k_0 = CSRStatus()
self.decoded_k_1 = CSRStatus()
self.sync.cxp_gtx_tx += [
If(counter == 0,
self.gtx.encoder.d[0].eq(self.data_0.storage),
self.gtx.encoder.k[0].eq(self.control_bit_0.storage),
self.gtx.encoder.d[1].eq(self.data_1.storage),
self.gtx.encoder.k[1].eq(self.control_bit_1.storage),
counter.eq(counter+1),
).Elif(counter == 1,
self.gtx.encoder.d[0].eq(self.data_2.storage),
self.gtx.encoder.k[0].eq(self.control_bit_2.storage),
self.gtx.encoder.d[1].eq(self.data_3.storage),
self.gtx.encoder.k[1].eq(self.control_bit_3.storage),
counter.eq(0),
),
self.encoded_0.status.eq(self.gtx.encoder.output[0]),
self.encoded_1.status.eq(self.gtx.encoder.output[1]),
]
self.sync.cxp_gtx_rx += [
self.rxdata_0.status.eq(self.gtx.decoders[0].input),
self.decoded_data_0.status.eq(self.gtx.decoders[0].d),
self.decoded_k_0.status.eq(self.gtx.decoders[0].k),
self.rxdata_1.status.eq(self.gtx.decoders[1].input),
self.decoded_data_1.status.eq(self.gtx.decoders[1].d),
self.decoded_k_1.status.eq(self.gtx.decoders[1].k),
]
# The TX phase alignment will fail with a wrong TXUSRCLK frequency
# NOTE: No need to connect cxp_gtx_tx, we don't use tx anyway (just for loopback)
# TODO: Connect slave cxp_gtx_rx clock tgt
# checkout channel interfaces & drtio_gtx
# checkout GTPTXPhaseAlignement for inspiration
class QPLL(Module):
def __init__(self, refclk, sys_clk_freq):
self.clk = Signal()
self.refclk = Signal()
self.lock = Signal()
self.reset = Signal()
# # #
# WARNING: VCO cannot do 12.5GHz on ZC706
# VCO freq = sys*qpll_fbdiv
# PLL output = VCO/2
qpll_fbdiv = 0b0100100000
qpll_fbdiv_ratio = 1
fbdiv_real = 80
refclk_div = 1
self.Xxout_div = 8
self.tx_usrclk_freq = (sys_clk_freq*fbdiv_real/self.Xxout_div)/20
# QPLL reset
pll_reset_cycles = ceil(sys_clk_freq/125e6)
pll_reset_timer = WaitTimer(pll_reset_cycles)
self.submodules += pll_reset_timer
reset = Signal()
startup_fsm = FSM(reset_state="IDLE")
self.submodules += startup_fsm
startup_fsm.act("IDLE",
If(self.reset, NextState("RESET_PLL"))
)
startup_fsm.act("RESET_PLL",
reset.eq(1),
pll_reset_timer.wait.eq(1),
If(pll_reset_timer.done, NextState("IDLE"))
)
self.specials += [
Instance("GTXE2_COMMON",
i_QPLLREFCLKSEL=0b001,
i_GTREFCLK0=refclk,
i_QPLLPD=0,
i_QPLLRESET=reset,
i_QPLLLOCKEN=1,
o_QPLLLOCK=self.lock,
o_QPLLOUTCLK=self.clk,
o_QPLLOUTREFCLK=self.refclk,
# See UG476 (v1.12.1) Table 2-16
p_QPLL_FBDIV=qpll_fbdiv,
p_QPLL_FBDIV_RATIO=qpll_fbdiv_ratio,
p_QPLL_REFCLK_DIV=refclk_div,
# From 7 Series FPGAs Transceivers Wizard
p_BIAS_CFG=0x0000040000001000,
p_COMMON_CFG=0x00000000,
p_QPLL_CFG=0x0680181,
p_QPLL_CLKOUT_CFG=0b0000,
p_QPLL_COARSE_FREQ_OVRD=0b010000,
p_QPLL_COARSE_FREQ_OVRD_EN=0b0,
p_QPLL_CP=0b0000011111,
p_QPLL_CP_MONITOR_EN=0b0,
p_QPLL_DMONITOR_SEL=0b0,
p_QPLL_FBDIV_MONITOR_EN= 0b0,
p_QPLL_INIT_CFG=0x000006,
p_QPLL_LOCK_CFG=0x21E8,
p_QPLL_LPF=0b1111,
# Reserved, values cannot be modified
i_BGBYPASSB=0b1,
i_BGMONITORENB=0b1,
i_BGPDB=0b1,
i_BGRCALOVRD=0b11111,
i_RCALENB=0b1,
i_QPLLRSVD1=0b0,
i_QPLLRSVD2=0b11111,
)
]
# Changes the phase of the transceiver RX clock to align the comma to
# the LSBs of RXDATA, fixing the latency.
#
# This is implemented by repeatedly resetting the transceiver until it
# gives out the correct phase. Each reset gives a random phase.
#
# If Xilinx had designed the GTX transceiver correctly, RXSLIDE_MODE=PMA
# would achieve this faster and in a cleaner way. But:
# * the phase jumps are of 2 UI at every second RXSLIDE pulse, instead
# of 1 UI at every pulse. It is unclear what the latency becomes.
# * RXSLIDE_MODE=PMA cannot be used with the RX buffer bypassed.
# Those design flaws make RXSLIDE_MODE=PMA yet another broken and useless
# transceiver "feature".
#
# Warning: Xilinx transceivers are LSB first, and comma needs to be flipped
# compared to the usual 8b10b binary representation.
class Manual_Aligner(Module):
def __init__(self, comma, check_cycles=20000):
self.rxslide = Signal()
class CXP_BruteforceClockAligner(Module):
def __init__(self, comma, sys_clk_freq, check_period):
self.rxdata = Signal(20)
self.restart = Signal()
self.ready = Signal()
# # #
checks_reset = Signal()
error_seen = Signal()
comma_seen = Signal()
rx1cnt = Signal(max=11)
comma_n = ~comma & 0b1111111111
self.sync.cxp_gtx_rx += [
rx1cnt.eq(reduce(add, [self.rxdata[i] for i in range(10)])),
If(checks_reset,
error_seen.eq(0)
).Elif((rx1cnt != 4) & (rx1cnt != 5) & (rx1cnt != 6),
error_seen.eq(1)
),
If(checks_reset,
comma_seen.eq(0)
).Elif((self.rxdata[:10] == comma) | (self.rxdata[:10] == comma_n),
comma_seen.eq(1)
check_max_val = ceil(check_period*sys_clk_freq)
check_counter = Signal(max=check_max_val+1)
check = Signal()
reset_check_counter = Signal()
self.sync += [
check.eq(0),
If(reset_check_counter,
check_counter.eq(check_max_val)
).Else(
If(check_counter == 0,
check.eq(1),
check_counter.eq(check_max_val)
).Else(
check_counter.eq(check_counter-1)
)
)
]
# minimum of 32 RXUSRCLK2 cycles are required between two RXSLIDE pulses
slide_timer = ClockDomainsRenamer("cxp_gtx_rx")(WaitTimer(64))
self.submodules += slide_timer
counter = Signal(reset=check_cycles-1, max=check_cycles)
checks_reset = PulseSynchronizer("sys", "cxp_gtx_rx")
self.submodules += checks_reset
fsm = ClockDomainsRenamer("cxp_gtx_rx")(FSM(reset_state="IDLE"))
comma_n = ~comma & 0b1111111111
comma_seen_rxclk = Signal()
comma_seen = Signal()
comma_seen_rxclk.attr.add("no_retiming")
self.specials += MultiReg(comma_seen_rxclk, comma_seen)
self.sync.cxp_gtx_rx += \
If(checks_reset.o,
comma_seen_rxclk.eq(0)
).Elif((self.rxdata[:10] == comma) | (self.rxdata[:10] == comma_n),
comma_seen_rxclk.eq(1)
)
error_seen_rxclk = Signal()
error_seen = Signal()
error_seen_rxclk.attr.add("no_retiming")
self.specials += MultiReg(error_seen_rxclk, error_seen)
rx1cnt = Signal(max=11)
self.sync.cxp_gtx_rx += [
rx1cnt.eq(reduce(add, [self.rxdata[i] for i in range(10)])),
If(checks_reset.o,
error_seen_rxclk.eq(0)
).Elif((rx1cnt != 4) & (rx1cnt != 5) & (rx1cnt != 6),
error_seen_rxclk.eq(1)
)
]
fsm = FSM(reset_state="WAIT_COMMA")
self.submodules += fsm
fsm.act("IDLE",
slide_timer.wait.eq(1),
If(slide_timer.done,
fsm.act("WAIT_COMMA",
If(check,
# Errors are still OK at this stage, as the transceiver
# has just been reset and may output garbage data.
If(comma_seen,
NextState("READY"),
NextState("WAIT_NOERROR")
).Else(
NextState("SLIDING")
)
self.restart.eq(1)
),
checks_reset.i.eq(1)
)
)
fsm.act("SLIDING",
self.rxslide.eq(1),
checks_reset.eq(1),
NextState("IDLE")
fsm.act("WAIT_NOERROR",
If(check,
If(comma_seen & ~error_seen,
NextState("READY")
).Else(
self.restart.eq(1),
NextState("WAIT_COMMA")
),
checks_reset.i.eq(1)
)
)
fsm.act("READY",
reset_check_counter.eq(1),
self.ready.eq(1),
If(counter == 0,
NextValue(counter, check_cycles - 1),
If(error_seen,
NextState("IDLE"),
)
).Else(
NextValue(counter, counter - 1),
If(error_seen,
checks_reset.i.eq(1),
self.restart.eq(1),
NextState("WAIT_COMMA")
)
)
class GTX(Module):
class CXP_DownConn(Module):
# Settings:
# * GTX reference clock @ 125MHz
# * GTX data width = 20
# * GTX PLL frequency @ 3.125GHz
# * GTX line rate (TX & RX) @ 3.125Gb/s
# * GTX TX/RX USRCLK @ PLL/datawidth = 156MHz
def __init__(self, qpll, pads, sys_clk_freq, tx_mode="single", rx_mode="single"):
def __init__(self, refclk, pads, sys_clk_freq, tx_mode="single", rx_mode="single"):
assert tx_mode in ["single", "master", "slave"]
assert rx_mode in ["single", "master", "slave"]
# linerate = USRCLK * datawidth
pll_fbout_mult = 8
txusr_pll_div = pll_fbout_mult*sys_clk_freq/qpll.tx_usrclk_freq # 20 is datawidth
cpll_div = 4
pll_div = int(40/cpll_div)
self.tx_restart = Signal()
self.rx_restart = Signal()
self.tx_restart = Signal()
self.loopback_mode = Signal(3)
self.txenable = Signal()
self.rx_ready = Signal()
self.tx_rate = Signal(3)
self.rx_rate = Signal(3)
self.submodules.encoder = ClockDomainsRenamer("cxp_gtx_tx")(Encoder(2, True))
self.submodules.decoders = [ClockDomainsRenamer("cxp_gtx_rx")(
(Decoder(True))) for _ in range(2)]
self.rx_ready = Signal()
# transceiver direct clock outputs
# useful to specify clock constraints in a way palatable to Vivado
@ -361,23 +150,20 @@ class GTX(Module):
# # #
cpllreset = Signal()
cplllock = Signal()
# TX generates cxp_tx clock, init must be in system domain
# FIXME: 500e6 is used to fix Xx reset by holding gtxXxreset for a couple cycle more
self.submodules.tx_init = tx_init = GTXInit(500e6, False, mode=tx_mode)
self.submodules.rx_init = rx_init = GTXInit(500e6, True, mode=rx_mode)
# RX receives restart commands from txusrclk domain
# self.submodules.rx_init = rx_init = ClockDomainsRenamer("cxp_gtx_tx")(GTXInit(500e6, True, mode=rx_mode))
self.submodules.tx_init = tx_init = GTXInit(sys_clk_freq, False, mode=tx_mode)
# RX receives restart commands from RTIO domain
self.submodules.rx_init = rx_init = GTXInit(sys_clk_freq, True, mode=rx_mode)
self.comb += [
tx_init.cplllock.eq(qpll.lock),
rx_init.cplllock.eq(qpll.lock)
cpllreset.eq(tx_init.cpllreset),
tx_init.cplllock.eq(cplllock),
rx_init.cplllock.eq(cplllock)
]
txdata = Signal(20)
rxdata = Signal(20)
rxslide = Signal()
# Note: the following parameters were set after consulting AR45360
self.specials += \
Instance("GTXE2_CHANNEL",
@ -395,25 +181,29 @@ class GTX(Module):
p_PD_TRANS_TIME_FROM_P2=0x3c,
p_PD_TRANS_TIME_NONE_P2=0x3c,
p_PD_TRANS_TIME_TO_P2=0x64,
i_CPLLPD=1,
# Dynamic Tx/Rx divider
i_TXRATE=self.tx_rate,
i_RXRATE=self.rx_rate,
# QPLL
i_QPLLCLK=qpll.clk,
i_QPLLREFCLK=qpll.refclk,
p_RXOUT_DIV=qpll.Xxout_div,
p_TXOUT_DIV=qpll.Xxout_div,
i_RXSYSCLKSEL=0b11, # use QPLL & QPLL's REFCLK
i_TXSYSCLKSEL=0b11, # use QPLL & CPLL's REFCLK
# CPLL
p_CPLL_CFG=0xBC07DC,
p_CPLL_FBDIV=cpll_div,
p_CPLL_FBDIV_45=5,
p_CPLL_REFCLK_DIV=1,
p_RXOUT_DIV=2,
p_TXOUT_DIV=2,
p_CPLL_INIT_CFG=0x00001E,
p_CPLL_LOCK_CFG=0x01E8,
i_CPLLRESET=cpllreset,
i_CPLLPD=cpllreset,
o_CPLLLOCK=cplllock,
i_CPLLLOCKEN=1,
i_CPLLREFCLKSEL=0b001,
i_TSTIN=2**20-1,
i_GTREFCLK0=refclk,
# TX clock
p_TXBUF_EN="FALSE",
p_TX_XCLK_SEL="TXUSR",
o_TXOUTCLK=self.txoutclk,
# i_TXSYSCLKSEL=0b00,
i_TXSYSCLKSEL=0b00,
i_TXOUTCLKSEL=0b11,
# TX Startup/Reset
@ -484,7 +274,7 @@ class GTX(Module):
# RX clock
i_RXDDIEN=1,
# i_RXSYSCLKSEL=0b00,
i_RXSYSCLKSEL=0b00,
i_RXOUTCLKSEL=0b010,
o_RXOUTCLK=self.rxoutclk,
i_RXUSRCLK=ClockSignal("cxp_gtx_rx"),
@ -507,22 +297,16 @@ class GTX(Module):
# RX Byte and Word Alignment Attributes
p_ALIGN_COMMA_DOUBLE="FALSE",
p_ALIGN_COMMA_ENABLE=0b1111111111,
p_ALIGN_COMMA_WORD=2,
p_ALIGN_MCOMMA_DET="FALSE",
p_ALIGN_COMMA_WORD=1,
p_ALIGN_MCOMMA_DET="TRUE",
p_ALIGN_MCOMMA_VALUE=0b1010000011,
p_ALIGN_PCOMMA_DET="FALSE",
p_ALIGN_PCOMMA_DET="TRUE",
p_ALIGN_PCOMMA_VALUE=0b0101111100,
p_SHOW_REALIGN_COMMA="FALSE",
p_RXSLIDE_AUTO_WAIT=7,
p_RXSLIDE_MODE="PCS",
p_RX_SIG_VALID_DLY=10,
# Manual Word Alignment
i_RXPCOMMAALIGNEN=0,
i_RXMCOMMAALIGNEN=0,
i_RXCOMMADETEN=1, # enable word alignment, but breaks rxrestart if gtxXxreset hold too short
i_RXSLIDE=rxslide,
# RX 8B/10B Decoder Attributes
p_RX_DISPERR_SEQ_MATCH="FALSE",
p_DEC_MCOMMA_DETECT="TRUE",
@ -590,47 +374,31 @@ class GTX(Module):
# A PLL is used to generate the correct frequency for TXUSRCLK (UG476 Equation 3-1)
self.clock_domains.cd_cxp_gtx_tx = ClockDomain()
txpll_fb_clk = Signal()
txpll_reset = Signal()
txpll_locked = Signal()
txoutclk_buf = Signal()
txpll_clkout = Signal()
self.txpll_reset = Signal()
self.pll_daddr = Signal(7)
self.pll_dclk = Signal()
self.pll_den = Signal()
self.pll_din = Signal(16)
self.pll_dwen = Signal()
self.txpll_locked = Signal()
self.pll_dout = Signal(16)
self.pll_dready = Signal()
self.specials += [
Instance("PLLE2_ADV",
p_BANDWIDTH="HIGH",
o_LOCKED=self.txpll_locked,
i_RST=self.txpll_reset,
o_LOCKED=txpll_locked,
i_RST=txpll_reset,
p_CLKIN1_PERIOD=1e9/sys_clk_freq, # ns
i_CLKIN1=txoutclk_buf,
# VCO @ 1.25GHz
p_CLKFBOUT_MULT=pll_fbout_mult, p_DIVCLK_DIVIDE=1,
p_CLKFBOUT_MULT=10, p_DIVCLK_DIVIDE=1,
i_CLKFBIN=txpll_fb_clk, o_CLKFBOUT=txpll_fb_clk,
# frequency = linerate/20
p_CLKOUT0_DIVIDE=txusr_pll_div, p_CLKOUT0_PHASE=0.0, o_CLKOUT0=txpll_clkout,
# 156.25MHz
p_CLKOUT0_DIVIDE=pll_div, p_CLKOUT0_PHASE=0.0, o_CLKOUT0=txpll_clkout,
# Dynamic Reconfiguration Ports
i_DADDR = self.pll_daddr,
i_DCLK = self.pll_dclk,
i_DEN = self.pll_den,
i_DI = self.pll_din,
i_DWE = self.pll_dwen,
o_DO = self.pll_dout,
o_DRDY = self.pll_dready,
# TODO: DRP for line rate change
),
Instance("BUFG", i_I=self.txoutclk, o_O=txoutclk_buf),
Instance("BUFG", i_I=txpll_clkout, o_O=self.cd_cxp_gtx_tx.clk),
AsyncResetSynchronizer(self.cd_cxp_gtx_tx, ~self.txpll_locked & ~tx_init.done)
AsyncResetSynchronizer(self.cd_cxp_gtx_tx, ~txpll_locked & ~tx_init.done)
]
# RX clocking
@ -647,13 +415,12 @@ class GTX(Module):
self.decoders[1].input.eq(rxdata[10:])
]
self.submodules.clk_aligner = clk_aligner = Manual_Aligner(0b0101111100)
# 6e-3 is too slow for 3.25Gbps line rate
clock_aligner = CXP_BruteforceClockAligner(0b0101111100, sys_clk_freq, check_period=1e-2)
self.submodules += clock_aligner
self.comb += [
clk_aligner.rxdata.eq(rxdata),
rxslide.eq(clk_aligner.rxslide),
self.rx_ready.eq(clk_aligner.ready),
rx_init.restart.eq(self.rx_restart),
tx_init.restart.eq(self.tx_restart),
clock_aligner.rxdata.eq(rxdata),
rx_init.restart.eq(clock_aligner.restart),
self.rx_ready.eq(clock_aligner.ready),
tx_init.restart.eq(self.tx_restart)
]

View File

@ -162,22 +162,22 @@ class CXP_UpConn(Module, AutoCSR):
p3.eq(self.tx_idle.word_idx == 3),
]
self.specials += [
# # debug sma
# Instance("OBUF", i_I=o, o_O=pads.p_tx),
# Instance("OBUF", i_I=self.cd_cxp_upconn.clk, o_O=pads.n_rx),
# debug sma
Instance("OBUF", i_I=o, o_O=pads.p_tx),
Instance("OBUF", i_I=self.cd_cxp_upconn.clk, o_O=pads.n_rx),
# # pmod 0-7 pin
# Instance("OBUF", i_I=o, o_O=pmod[0]),
# Instance("OBUF", i_I=self.cd_cxp_upconn.clk, o_O=pmod[1]),
# Instance("OBUF", i_I=~self.tx_fifos.pe.n, o_O=pmod[2]),
# Instance("OBUF", i_I=ninth_bit, o_O=pmod[3]),
# Instance("OBUF", i_I=word_bound, o_O=pmod[4]),
# Instance("OBUF", i_I=idling, o_O=pmod[5]),
# # Instance("OBUF", i_I=self.tx_fifos.source_ack[0], o_O=pmod[6]),
# # Instance("OBUF", i_I=self.tx_fifos.source_ack[2], o_O=pmod[6]),
# # Instance("OBUF", i_I=self.tx_fifos.source_ack[1], o_O=pmod[7]),
# Instance("OBUF", i_I=p0, o_O=pmod[6]),
# Instance("OBUF", i_I=p3, o_O=pmod[7]),
# pmod 0-7 pin
Instance("OBUF", i_I=o, o_O=pmod[0]),
Instance("OBUF", i_I=self.cd_cxp_upconn.clk, o_O=pmod[1]),
Instance("OBUF", i_I=~self.tx_fifos.pe.n, o_O=pmod[2]),
Instance("OBUF", i_I=ninth_bit, o_O=pmod[3]),
Instance("OBUF", i_I=word_bound, o_O=pmod[4]),
Instance("OBUF", i_I=idling, o_O=pmod[5]),
# Instance("OBUF", i_I=self.tx_fifos.source_ack[0], o_O=pmod[6]),
# Instance("OBUF", i_I=self.tx_fifos.source_ack[2], o_O=pmod[6]),
# Instance("OBUF", i_I=self.tx_fifos.source_ack[1], o_O=pmod[7]),
Instance("OBUF", i_I=p0, o_O=pmod[6]),
Instance("OBUF", i_I=p3, o_O=pmod[7]),
]
self.symbol0 = CSR(9)
self.symbol1 = CSR(9)

View File

@ -4,431 +4,67 @@ use log::info;
use crate::pl::csr;
pub struct CXP_DownConn_Settings {
pub rxdiv: u8,
pub qpll_fbdiv: u8,
}
#[derive(Clone, Copy, Debug)]
#[allow(non_camel_case_types)]
pub enum CXP_SPEED {
CXP_1,
CXP_2,
CXP_3,
CXP_5,
CXP_6,
CXP_10,
CXP_12,
}
fn loopback_testing(timer: &mut GlobalTimer, data: u8, control_bit: u8) {
unsafe {
// send K28_5 for CDR to align
const K28_5: u8 = 0xBC;
const K28_1: u8 = 0x3C;
const D21_5: u8 = 21 | 5 << 5;
// NOTE: testing with IDLE word + manual comma alignment
// 62.5MHz OK
// 125MHz OK
// 156.25MHz
// 250MHz OK
// 312.5MHz
// 500MHz ?? havn't change CDR config yet
const LEN: usize = 4;
const DATA: [[u8; LEN]; 2] = [
// [K28_5, K28_1, K28_5, K28_1],
// [1, 1, 1, 1],
[K28_5, K28_1, K28_1, D21_5],
[1, 1, 1, 0],
];
csr::cxp::downconn_data_0_write(DATA[0][0]);
csr::cxp::downconn_data_1_write(DATA[0][1]);
csr::cxp::downconn_data_2_write(DATA[0][2]);
csr::cxp::downconn_data_3_write(DATA[0][3]);
csr::cxp::downconn_control_bit_0_write(DATA[1][0]);
csr::cxp::downconn_control_bit_1_write(DATA[1][1]);
csr::cxp::downconn_control_bit_2_write(DATA[1][2]);
csr::cxp::downconn_control_bit_3_write(DATA[1][3]);
info!("waiting for tx&rx setup...");
timer.delay_us(50_000);
info!(
"tx_phaligndone = {} | rx_phaligndone = {}",
csr::cxp::downconn_txinit_phaligndone_read(),
csr::cxp::downconn_rxinit_phaligndone_read(),
);
// enable txdata tranmission thought MGTXTXP, required by PMA loopback
csr::cxp::downconn_txenable_write(1);
info!("waiting for rx to align...");
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(),
// );
// for _ in 0..20 {
loop {
// timer.delay_us(100);
println!(
"data = {:#022b} | rx ready = {}",
(csr::cxp::downconn_rxdata_0_read() as u32 | ((csr::cxp::downconn_rxdata_1_read() as u32) << 10)),
csr::cxp::downconn_rx_ready_read()
);
timer.delay_us(1_000_000);
// println!(
// "data[0] = {:#012b} data[1] = {:#012b}",
// csr::cxp::rxdata_0_read(),
// csr::cxp::rxdata_1_read(),
// );
// println!(
// "decoded_data[0] = {:#04x} decoded_k[0] = {:#b} decoded_data[1] = {:#04x} decoded_k[1] = {:#b}",
// csr::cxp::downconn_decoded_data_0_read(),
// csr::cxp::downconn_decoded_k_0_read(),
// csr::cxp::downconn_decoded_data_1_read(),
// csr::cxp::downconn_decoded_k_1_read(),
// );
}
}
}
pub fn setup(timer: &mut GlobalTimer, speed: CXP_SPEED) {
pub fn main(timer: &mut GlobalTimer) {
unsafe {
info!("turning on pmc loopback mode...");
csr::cxp::downconn_loopback_mode_write(0b010); // Near-End PMA Loopback
csr::cxp::loopback_mode_write(0b010); // Near-End PMA Loopback
// enable cxp gtx clock domains
csr::cxp::tx_start_init_write(1);
csr::cxp::rx_start_init_write(1);
// QPLL setup
csr::cxp::downconn_qpll_reset_write(1);
info!("waiting for QPLL/CPLL to lock...");
while csr::cxp::downconn_qpll_locked_read() != 1 {}
info!("QPLL locked");
// tx/rx setup
csr::cxp::downconn_tx_start_init_write(1);
csr::cxp::downconn_rx_start_init_write(1);
info!("waiting for tx & rx setup...");
timer.delay_us(50_000);
info!(
"tx_phaligndone = {} | rx_phaligndone = {}",
csr::cxp::downconn_txinit_phaligndone_read(),
csr::cxp::downconn_rxinit_phaligndone_read(),
);
println!("==============================================================================");
info!("tx_phaligndone = {} ", csr::cxp::txinit_phaligndone_read(),);
// enable txdata tranmission thought MGTXTXP, required by PMA loopback
csr::cxp::txenable_write(1);
loopback_testing(timer, 0x00, 0);
}
change_linerate(timer, speed);
loopback_testing(timer, 0x00, 0);
}
pub fn change_linerate(timer: &mut GlobalTimer, speed: CXP_SPEED) {
info!("Changing datarate to {:?}", speed);
// DEBUG: DRP pll for TXUSRCLK = freq(linerate)/20
let settings = txusrclk::get_txusrclk_config(speed);
txusrclk::setup(timer, settings);
// TODO: set QPLL_FBDIV via DRP
change_qpll_settings(speed);
unsafe {
csr::cxp::downconn_qpll_reset_write(1);
info!("waiting for QPLL/CPLL to lock...");
while csr::cxp::downconn_qpll_locked_read() != 1 {}
info!("QPLL locked");
}
unsafe {
csr::cxp::downconn_tx_restart_write(1);
csr::cxp::downconn_rx_restart_write(1);
}
}
fn change_qpll_settings(speed: CXP_SPEED) {
let divider = match speed {
CXP_SPEED::CXP_1 => 0b100, // Divided by 8
CXP_SPEED::CXP_2 | CXP_SPEED::CXP_3 => 0b011, // Divided by 4
CXP_SPEED::CXP_5 | CXP_SPEED::CXP_6 => 0b010, // Divided by 2
CXP_SPEED::CXP_10 | CXP_SPEED::CXP_12 => 0b010, // Divided by 1
};
unsafe {
csr::cxp::downconn_rx_div_write(divider);
csr::cxp::downconn_tx_div_write(divider);
}
}
pub mod txusrclk {
use super::*;
pub struct PLLSetting {
pub clkout0_reg1: u16, //0x08
pub clkout0_reg2: u16, //0x09
pub clkfbout_reg1: u16, //0x14
pub clkfbout_reg2: u16, //0x15
pub div_reg: u16, //0x16
pub lock_reg1: u16, //0x18
pub lock_reg2: u16, //0x19
pub lock_reg3: u16, //0x1A
pub power_reg: u16, //0x28
pub filt_reg1: u16, //0x4E
pub filt_reg2: u16, //0x4F
}
fn one_clock_cycle() {
fn loopback_testing(timer: &mut GlobalTimer, data: u8, control_bit: u8) {
unsafe {
csr::cxp::downconn_pll_dclk_write(1);
csr::cxp::downconn_pll_dclk_write(0);
}
}
// send K28_5 for CDR to align
const K28_5: u8 = 0xBC;
csr::cxp::data_0_write(K28_5);
csr::cxp::control_bit_0_write(1);
csr::cxp::data_1_write(K28_5);
csr::cxp::control_bit_1_write(1);
fn set_addr(address: u8) {
unsafe {
csr::cxp::downconn_pll_daddr_write(address);
}
}
info!("waiting for rx to align...");
while csr::cxp::rx_ready_read() != 1 {}
info!("rx ready!");
fn set_data(value: u16) {
unsafe {
csr::cxp::downconn_pll_din_write(value);
}
}
csr::cxp::data_1_write(data);
csr::cxp::control_bit_1_write(control_bit);
println!(
"data[0] = {:#04x} control bit = {:#b} encoded = {:#012b}",
csr::cxp::data_0_read(),
csr::cxp::control_bit_0_read(),
csr::cxp::encoded_0_read(),
);
println!(
"data[1] = {:#04x} control bit = {:#b} encoded = {:#012b}",
csr::cxp::data_1_read(),
csr::cxp::control_bit_1_read(),
csr::cxp::encoded_1_read(),
);
fn set_enable(en: bool) {
unsafe {
let val = if en { 1 } else { 0 };
csr::cxp::downconn_pll_den_write(val);
}
}
fn set_write_enable(en: bool) {
unsafe {
let val = if en { 1 } else { 0 };
csr::cxp::downconn_pll_dwen_write(val);
}
}
fn get_data() -> u16 {
unsafe { csr::cxp::downconn_pll_dout_read() }
}
fn drp_ready() -> bool {
unsafe { csr::cxp::downconn_pll_dready_read() == 1 }
}
#[allow(dead_code)]
fn read(address: u8) -> u16 {
set_addr(address);
set_enable(true);
// Set DADDR on the mmcm and assert DEN for one clock cycle
one_clock_cycle();
set_enable(false);
while !drp_ready() {
// keep the clock signal until data is ready
one_clock_cycle();
}
get_data()
}
fn write(address: u8, value: u16) {
set_addr(address);
set_data(value);
set_write_enable(true);
set_enable(true);
// Set DADDR, DI on the mmcm and assert DWE, DEN for one clock cycle
one_clock_cycle();
set_write_enable(false);
set_enable(false);
while !drp_ready() {
// keep the clock signal until write is finished
one_clock_cycle();
}
}
fn reset(rst: bool) {
unsafe {
let val = if rst { 1 } else { 0 };
csr::cxp::downconn_txpll_reset_write(val)
}
}
pub fn setup(timer: &mut GlobalTimer, settings: PLLSetting) {
if false {
info!("0x08 = {:#06x}", read(0x08));
info!("0x09 = {:#06x}", read(0x09));
info!("0x14 = {:#06x}", read(0x14));
info!("0x15 = {:#06x}", read(0x15));
info!("0x16 = {:#06x}", read(0x16));
info!("0x18 = {:#06x}", read(0x18));
info!("0x19 = {:#06x}", read(0x19));
info!("0x1A = {:#06x}", read(0x1A));
info!("0x28 = {:#06x}", read(0x28));
info!("0x4E = {:#06x}", read(0x4E));
info!("0x4F = {:#06x}", read(0x4F));
} else {
// Based on "DRP State Machine" from XAPP888
// hold reset HIGH during pll config
reset(true);
write(0x08, settings.clkout0_reg1);
write(0x09, settings.clkout0_reg2);
write(0x14, settings.clkfbout_reg1);
write(0x15, settings.clkfbout_reg2);
write(0x16, settings.div_reg);
write(0x18, settings.lock_reg1);
write(0x19, settings.lock_reg2);
write(0x1A, settings.lock_reg3);
write(0x28, settings.power_reg);
write(0x4E, settings.filt_reg1);
write(0x4F, settings.filt_reg2);
reset(false);
// wait for the pll to lock
timer.delay_us(100);
let locked = unsafe { csr::cxp::downconn_txpll_locked_read() == 1 };
info!("txusrclk locked = {}", locked);
}
}
pub fn get_txusrclk_config(speed: CXP_SPEED) -> PLLSetting {
match speed {
CXP_SPEED::CXP_1 => {
// CLKFBOUT_MULT = 8, DIVCLK_DIVIDE = 1 , CLKOUT0_DIVIDE = 16
// TXUSRCLK=62.5MHz
PLLSetting {
clkout0_reg1: 0x1208, //0x08
clkout0_reg2: 0x0000, //0x09
clkfbout_reg1: 0x1104, //0x14
clkfbout_reg2: 0x0000, //0x15
div_reg: 0x1041, //0x16
lock_reg1: 0x03e8, //0x18
lock_reg2: 0x5801, //0x19
lock_reg3: 0xdbe9, //0x1A
power_reg: 0x0000, //0x28
filt_reg1: 0x9808, //0x4E
filt_reg2: 0x9100, //0x4F
}
}
CXP_SPEED::CXP_2 => {
// CLKFBOUT_MULT = 8, DIVCLK_DIVIDE = 1 , CLKOUT0_DIVIDE = 8
// TXUSRCLK=125MHz
PLLSetting {
clkout0_reg1: 0x1104, //0x08
clkout0_reg2: 0x0000, //0x09
clkfbout_reg1: 0x1104, //0x14
clkfbout_reg2: 0x0000, //0x15
div_reg: 0x1041, //0x16
lock_reg1: 0x03e8, //0x18
lock_reg2: 0x5801, //0x19
lock_reg3: 0xdbe9, //0x1A
power_reg: 0x0000, //0x28
filt_reg1: 0x9808, //0x4E
filt_reg2: 0x9100, //0x4F
}
}
CXP_SPEED::CXP_3 => {
// CLKFBOUT_MULT = 10, DIVCLK_DIVIDE = 1 , CLKOUT0_DIVIDE = 8
// TXUSRCLK=125MHz
PLLSetting {
clkout0_reg1: 0x1104, //0x08
clkout0_reg2: 0x0000, //0x09
clkfbout_reg1: 0x1145, //0x14
clkfbout_reg2: 0x0000, //0x15
div_reg: 0x1041, //0x16
lock_reg1: 0x03e8, //0x18
lock_reg2: 0x7001, //0x19
lock_reg3: 0xf3e9, //0x1A
power_reg: 0x0000, //0x28
filt_reg1: 0x9908, //0x4E
filt_reg2: 0x1900, //0x4F
}
}
CXP_SPEED::CXP_5 => {
// CLKFBOUT_MULT = 8, DIVCLK_DIVIDE = 1 , CLKOUT0_DIVIDE = 4
// TXUSRCLK=250MHz
PLLSetting {
clkout0_reg1: 0x1082, //0x08
clkout0_reg2: 0x0000, //0x09
clkfbout_reg1: 0x1104, //0x14
clkfbout_reg2: 0x0000, //0x15
div_reg: 0x1041, //0x16
lock_reg1: 0x03e8, //0x18
lock_reg2: 0x5801, //0x19
lock_reg3: 0xdbe9, //0x1A
power_reg: 0x0000, //0x28
filt_reg1: 0x9808, //0x4E
filt_reg2: 0x9100, //0x4F
}
}
CXP_SPEED::CXP_6 => {
// CLKFBOUT_MULT = 10, DIVCLK_DIVIDE = 1 , CLKOUT0_DIVIDE = 4
// TXUSRCLK=312.5MHz
PLLSetting {
clkout0_reg1: 0x1082, //0x08
clkout0_reg2: 0x0000, //0x09
clkfbout_reg1: 0x1145, //0x14
clkfbout_reg2: 0x0000, //0x15
div_reg: 0x1041, //0x16
lock_reg1: 0x03e8, //0x18
lock_reg2: 0x7001, //0x19
lock_reg3: 0xf3e9, //0x1A
power_reg: 0x0000, //0x28
filt_reg1: 0x9908, //0x4E
filt_reg2: 0x1900, //0x4F
}
}
CXP_SPEED::CXP_10 => {
// CLKFBOUT_MULT = 8, DIVCLK_DIVIDE = 1 , CLKOUT0_DIVIDE = 2
// TXUSRCLK=500MHz
PLLSetting {
clkout0_reg1: 0x1041, //0x08
clkout0_reg2: 0x0000, //0x09
clkfbout_reg1: 0x1104, //0x14
clkfbout_reg2: 0x0000, //0x15
div_reg: 0x1041, //0x16
lock_reg1: 0x03e8, //0x18
lock_reg2: 0x5801, //0x19
lock_reg3: 0xdbe9, //0x1A
power_reg: 0x0000, //0x28
filt_reg1: 0x9808, //0x4E
filt_reg2: 0x9100, //0x4F
}
}
CXP_SPEED::CXP_12 => {
// CLKFBOUT_MULT = 10, DIVCLK_DIVIDE = 1 , CLKOUT0_DIVIDE = 2
// TXUSRCLK=625MHz
PLLSetting {
clkout0_reg1: 0x1041, //0x08
clkout0_reg2: 0x0000, //0x09
clkfbout_reg1: 0x1145, //0x14
clkfbout_reg2: 0x0000, //0x15
div_reg: 0x1041, //0x16
lock_reg1: 0x03e8, //0x18
lock_reg2: 0x7001, //0x19
lock_reg3: 0xf3e9, //0x1A
power_reg: 0x0000, //0x28
filt_reg1: 0x9908, //0x4E
filt_reg2: 0x1900, //0x4F
}
for _ in 0..20 {
timer.delay_us(100);
// println!(
// "data[0] = {:#012b} data[1] = {:#012b}",
// csr::cxp::rxdata_0_read(),
// csr::cxp::rxdata_1_read(),
// );
println!(
"decoded_data[0] = {:#04x} decoded_k[0] = {:#b} decoded_data[1] = {:#04x} decoded_k[1] = {:#b}",
csr::cxp::decoded_data_0_read(),
csr::cxp::decoded_k_0_read(),
csr::cxp::decoded_data_1_read(),
csr::cxp::decoded_k_1_read(),
);
}
}
}