1
0
Fork 0

Compare commits

...

12 Commits

2 changed files with 569 additions and 254 deletions

View File

@ -1,6 +1,5 @@
from migen import * from migen 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)
@ -132,28 +195,24 @@ class CXP_DownConn(Module, AutoCSR):
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 += [
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),
@ -161,9 +220,10 @@ class CXP_DownConn(Module, AutoCSR):
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,37 +244,35 @@ 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
# QPLL reset # fbdiv_real = 100
pll_reset_cycles = ceil(sys_clk_freq/125e6) # refclk_div = 2
pll_reset_timer = WaitTimer(pll_reset_cycles) # self.Xxout_div = 2
self.submodules += pll_reset_timer
reset = Signal() self.tx_usrclk_freq = (sys_clk_freq*fbdiv_real/self.Xxout_div)/40
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",
@ -222,7 +280,7 @@ class QPLL(Module):
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,93 +341,142 @@ 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),
# check.eq(0),
# check_reset.i.eq(0),
If(~self.ready,
If(check_counter == 0, If(check_counter == 0,
check_counter.eq(check_counter.reset), check_counter.eq(check_counter.reset),
check.eq(1), # check.eq(1),
self.restart.eq(1),
# check_reset.i.eq(1),
).Else( ).Else(
check_counter.eq(check_counter - 1), 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()
If(check, comma_seen = Signal()
recheck_ps.i.eq(1), error_seen = Signal()
If(aligned, one_counts = Signal(max=11)
NextState("WAIT_NO_ERROR"),
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( ).Else(
self.restart.eq(1), counter.eq(counter - 1),
)
),
has_error.eq(0),
one_counts.eq(reduce(add, [self.data[i] for i in range(10, 20)])),
If((one_counts != 4) & (one_counts != 5) & (one_counts != 6),
has_error.eq(1)
),
comma_aligned.eq(0),
If((self.data[:10] == comma) | (self.data[:10] == comma_n),
comma_aligned.eq(1)
),
# signal that need to be manually cleared
If(check_reset,
comma_seen.eq(0),
).Elif((self.data[:10] == comma) | (self.data[:10] == comma_n),
comma_seen.eq(1)
),
one_counts.eq(reduce(add, [self.data[i] for i in range(10, 20)])),
If(check_reset,
error_seen.eq(0),
).Elif((one_counts != 4) & (one_counts != 5) & (one_counts != 6),
error_seen.eq(1),
),
]
# DEBUG: expose signal
self.check_reset = Signal()
self.comma_aligned = Signal()
self.comma_seen = Signal()
self.has_error = Signal()
self.error_seen = Signal()
self.comb +=[
self.check_reset.eq(check_reset),
self.comma_aligned.eq(comma_aligned),
self.comma_seen.eq(comma_seen),
self.has_error.eq(has_error),
self.error_seen.eq(error_seen),
]
self.submodules.rxfsm = rxfsm = ClockDomainsRenamer("cxp_gtx_rx")(FSM(reset_state="ALIGNING"))
# the data from gtxe2 is delayed and checking at the output may not reflect the alignment inside the aligner
# thus, failing to alignment on high linerate >5Gbps is common due to the aligner_en being asserted longer than necessary and lead to a lose of lock
# a timer is used to wait till the "aligned" data to arrive and do a system check like the datasheet suggested
self.submodules.timer = timer = ClockDomainsRenamer("cxp_gtx_rx")(WaitTimer(5000))
rxfsm.act("ALIGNING",
self.aligner_en_rxclk.eq(1),
If(self.word_aligned,
check_reset.eq(1),
NextState("WAIT_ALIGNED_DATA"),
) )
) )
fsm.act("WAIT_NO_ERROR", rxfsm.act("WAIT_ALIGNED_DATA",
aligner_en_sys.eq(1), 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 & valid_data, If(comma_seen & (~error_seen),
NextState("READY"), NextState("READY"),
).Else( ).Else(
self.restart.eq(1), NextState("ALIGNING")
NextState("IDLE"),
) )
) )
) )
fsm.act("READY", ready = Signal()
self.ready.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)),
self.restart.eq(1), NextState("ALIGNING"),
NextState("IDLE"),
) )
) )
) )
class GTX(Module): class GTX(Module):
# Settings: # Settings:
# * GTX reference clock @ 125MHz # * GTX reference clock @ 125MHz
@ -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),

View File

@ -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 = {}",
(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!( // println!(
// "data[0] = {:#012b} data[1] = {:#012b}", // "data[0] = {:#012b} comma = {} | rx ready = {}",
// csr::cxp::rxdata_0_read(), // data0,
// csr::cxp::rxdata_1_read(), // data0 == 0b0101111100 || data0 == 0b1010000011,
// rxready,
// ); // );
// timer.delay_us(1_000_000);
// } else if data0 == 0b1001111100 || data0 == 0b0110000011 {
// println!(
// "data[0] = {:#012b} K28.1 | rx ready = {}",
// data0,
// rxready,
// );
// timer.delay_us(1_000_000);
// } else {
// println!(
// "data[0] = {:#012b} | rx ready = {}",
// data0,
// rxready,
// );
// timer.delay_us(1_000_000);
// }
println!("0b{:010b}{:010b}", data0, data1);
timer.delay_us(1_000_000);
// NOTE:decode data
// let data0_decoded = csr::cxp::downconn_decoded_data_0_read();
// let data0_k = csr::cxp::downconn_decoded_k_0_read();
// let data1_decoded = csr::cxp::downconn_decoded_data_1_read();
// let data1_k = csr::cxp::downconn_decoded_k_1_read();
// println!( // 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,19 +150,30 @@ 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 mod CXP_GTX {
use super::*;
struct RX_CDR_CFG {
pub cfg_reg0: u16, //0x0A8
pub cfg_reg1: u16, //0x0A9
pub cfg_reg2: u16, //0x0AA
pub cfg_reg3: u16, //0x0AB
pub cfg_reg4: u16, //0x0AC
}
pub fn change_linerate(timer: &mut GlobalTimer, speed: CXP_SPEED) { pub fn change_linerate(timer: &mut GlobalTimer, speed: CXP_SPEED) {
info!("Changing datarate to {:?}", speed); info!("Changing datarate to {:?}", speed);
// DEBUG: DRP pll for TXUSRCLK = freq(linerate)/20 // DEBUG: DRP pll for TXUSRCLK = freq(linerate)/20
let settings = txusrclk::get_txusrclk_config(speed); let settings = txusrclk::get_txusrclk_config(speed);
txusrclk::setup(timer, settings); txusrclk::setup(timer, settings);
// TODO: set QPLL_FBDIV via DRP
change_qpll_settings(speed); change_qpll_settings(speed);
change_cdr_cfg(speed);
unsafe { unsafe {
csr::cxp::downconn_qpll_reset_write(1); csr::cxp::downconn_qpll_reset_write(1);
@ -162,11 +189,58 @@ pub fn change_linerate(timer: &mut GlobalTimer, speed: CXP_SPEED) {
} }
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
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 { 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_1 => 0b100, // Divided by 8
CXP_SPEED::CXP_2 | CXP_SPEED::CXP_3 => 0b011, // Divided by 4 CXP_SPEED::CXP_2 => 0b011, // Divided by 4
CXP_SPEED::CXP_5 | CXP_SPEED::CXP_6 => 0b010, // Divided by 2 CXP_SPEED::CXP_5 | CXP_SPEED::CXP_3 => 0b010, // Divided by 2
CXP_SPEED::CXP_10 | CXP_SPEED::CXP_12 => 0b010, // Divided by 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 {
@ -175,6 +249,107 @@ fn change_qpll_settings(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 {
// Divided by 8
CXP_SPEED::CXP_1 => {
RX_CDR_CFG {
cfg_reg0: 0x0020, //0x0A8
cfg_reg1: 0x1008, //0x0A9
cfg_reg2: 0x23FF, //0x0AA
cfg_reg3: 0x0000, //0x0AB
cfg_reg4: 0x0003, //0x0AC
}
}
// Divided by 4
CXP_SPEED::CXP_2 => {
RX_CDR_CFG {
cfg_reg0: 0x0020, //0x0A8
cfg_reg1: 0x1010, //0x0A9
cfg_reg2: 0x23FF, //0x0AA
cfg_reg3: 0x0000, //0x0AB
cfg_reg4: 0x0003, //0x0AC
}
}
// Divided by 2
CXP_SPEED::CXP_3 | CXP_SPEED::CXP_5 => {
RX_CDR_CFG {
cfg_reg0: 0x0020, //0x0A8
cfg_reg1: 0x1020, //0x0A9
cfg_reg2: 0x23FF, //0x0AA
cfg_reg3: 0x0000, //0x0AB
cfg_reg4: 0x0003, //0x0AC
}
}
// Divided by 1
CXP_SPEED::CXP_6 => {
RX_CDR_CFG {
cfg_reg0: 0x0020, //0x0A8
cfg_reg1: 0x1040, //0x0A9
cfg_reg2: 0x23FF, //0x0AA
cfg_reg3: 0x0000, //0x0AB
cfg_reg4: 0x0003, //0x0AC
}
}
// Divided by 1
CXP_SPEED::CXP_10 | CXP_SPEED::CXP_12 => {
RX_CDR_CFG {
cfg_reg0: 0x0020, //0x0A8
cfg_reg1: 0x1040, //0x0A9
cfg_reg2: 0x23FF, //0x0AA
cfg_reg3: 0x0000, //0x0AB
cfg_reg4: 0x000B, //0x0AC
}
}
};
gtx_write(0x0A8, cdr_cfg.cfg_reg0);
gtx_write(0x0A9, cdr_cfg.cfg_reg1);
gtx_write(0x0AA, cdr_cfg.cfg_reg2);
gtx_write(0x0AB, cdr_cfg.cfg_reg3);
gtx_write(0x0AC, cdr_cfg.cfg_reg4);
}
fn gtx_read(address: u16) -> u16 {
// DEBUG: DRPCLK need to be on for a few cycle before accessing other DRP ports
unsafe {
csr::cxp::downconn_gtx_daddr_write(address);
csr::cxp::downconn_gtx_dread_write(1);
while (csr::cxp::downconn_gtx_dready_read() != 1) {}
csr::cxp::downconn_gtx_dout_read()
}
}
fn gtx_write(address: u16, value: u16) {
// DEBUG: DRPCLK need to be on for a few cycle before accessing other DRP ports
unsafe {
csr::cxp::downconn_gtx_daddr_write(address);
csr::cxp::downconn_gtx_din_write(value);
csr::cxp::downconn_gtx_din_stb_write(1);
while (csr::cxp::downconn_gtx_dready_read() != 1) {}
}
}
fn qpll_read(address: u8) -> u16 {
unsafe {
csr::cxp::downconn_qpll_daddr_write(address);
csr::cxp::downconn_qpll_dread_write(1);
while (csr::cxp::downconn_qpll_dready_read() != 1) {}
csr::cxp::downconn_qpll_dout_read()
}
}
fn qpll_write(address: u8, value: u16) {
unsafe {
csr::cxp::downconn_qpll_daddr_write(address);
csr::cxp::downconn_qpll_din_write(value);
csr::cxp::downconn_qpll_din_stb_write(1);
while (csr::cxp::downconn_qpll_dready_read() != 1) {}
}
}
}
pub mod txusrclk { pub mod txusrclk {
use super::*; use super::*;
@ -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
}
}
} }
} }
} }