1
0
Fork 0

cxp downconn: use 40bits

This commit is contained in:
morgan 2024-08-14 16:48:36 +08:00
parent 17277504f1
commit 49d5cad5fd
1 changed files with 233 additions and 133 deletions

View File

@ -1,6 +1,5 @@
from migen import *
from migen.genlib.cdc import MultiReg, PulseSynchronizer
from migen.genlib.misc import WaitTimer
from migen.genlib.resetsync import AsyncResetSynchronizer
from misoc.cores.code_8b10b import Encoder, Decoder
@ -155,43 +154,20 @@ class CXP_DownConn(Module, AutoCSR):
self.loopback_mode = CSRStorage(3)
self.comb += gtx.loopback_mode.eq(self.loopback_mode.storage)
# DEBUG: RXDATA checking
aligned = Signal()
valid_data = Signal()
rx1cnt = Signal(max=11)
comma = 0b0101111100
comma_n = ~comma & 0b1111111111
self.sync.cxp_gtx_rx += [
If(self.gtx.comma_det.check_ps.o,
aligned.eq(0),
).Elif((gtx.comma_det.data[:10] == comma) | (gtx.comma_det.data[:10] == comma_n),
aligned.eq(1),
),
rx1cnt.eq(reduce(add, [gtx.comma_det.data[i] for i in range(10)])),
If(self.gtx.comma_det.check_ps.o,
valid_data.eq(0)
).Elif((rx1cnt == 4) | (rx1cnt == 5) | (rx1cnt == 6),
valid_data.eq(1)
),
]
# 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.comma_det.aligner_en_rxclk, o_O=pmod_pads[0]),
Instance("OBUF", i_I=gtx.comma_det.rxinit_done, o_O=pmod_pads[1]),
Instance("OBUF", i_I=gtx.comma_det.restart, o_O=pmod_pads[2]),
Instance("OBUF", i_I=aligned, o_O=pmod_pads[3]),
Instance("OBUF", i_I=gtx.comma_det.ready, o_O=pmod_pads[4]),
Instance("OBUF", i_I=gtx.comma_det.check_ps.o, o_O=pmod_pads[5]),
Instance("OBUF", i_I=valid_data, o_O=pmod_pads[6]),
# Instance("OBUF", i_I=, o_O=pmod_pads[7]),
Instance("OBUF", i_I=gtx.comma_det.rxinit_done, o_O=pmod_pads[0]),
Instance("OBUF", i_I=gtx.comma_det.restart, o_O=pmod_pads[1]),
Instance("OBUF", i_I=gtx.comma_det.aligner_en_rxclk, o_O=pmod_pads[2]),
Instance("OBUF", i_I=gtx.comma_det.check_reset, o_O=pmod_pads[3]),
Instance("OBUF", i_I=gtx.comma_det.comma_aligned, o_O=pmod_pads[4]),
Instance("OBUF", i_I=gtx.comma_det.comma_seen, o_O=pmod_pads[5]),
Instance("OBUF", i_I=gtx.comma_det.has_error, o_O=pmod_pads[6]),
Instance("OBUF", i_I=gtx.comma_det.ready, o_O=pmod_pads[7]),
# Instance("OBUF", i_I=gtx.dclk, o_O=pmod_pads[0]),
# Instance("OBUF", i_I=gtx.den, o_O=pmod_pads[1]),
@ -200,8 +176,6 @@ class CXP_DownConn(Module, AutoCSR):
]
# DEBUG: datain
counter_max = 2
counter = Signal(max=counter_max)
self.data_0 = CSRStorage(8)
self.data_1 = CSRStorage(8)
@ -222,24 +196,23 @@ class CXP_DownConn(Module, AutoCSR):
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.gtx.encoder.d[0].eq(0xBC),
self.gtx.encoder.k[0].eq(1),
self.gtx.encoder.d[1].eq(0x3C),
self.gtx.encoder.k[1].eq(1),
self.gtx.encoder.d[2].eq(0x3C),
self.gtx.encoder.k[2].eq(1),
self.gtx.encoder.d[3].eq(0xB5),
self.gtx.encoder.k[3].eq(0),
self.encoded_0.status.eq(self.gtx.encoder.output[0]),
self.encoded_1.status.eq(self.gtx.encoder.output[1]),
]
# keep it odd, so it will show data[n] where n is odd
stb_timer = Signal(reset=10, max=11)
self.sync.cxp_gtx_rx += [
If(stb_timer == 0,
self.rxdata_0.status.eq(self.gtx.decoders[0].input),
self.decoded_data_0.status.eq(self.gtx.decoders[0].d),
self.decoded_k_0.status.eq(self.gtx.decoders[0].k),
@ -247,6 +220,10 @@ class CXP_DownConn(Module, AutoCSR):
self.rxdata_1.status.eq(self.gtx.decoders[1].input),
self.decoded_data_1.status.eq(self.gtx.decoders[1].d),
self.decoded_k_1.status.eq(self.gtx.decoders[1].k),
stb_timer.eq(stb_timer.reset),
).Else(
stb_timer.eq(stb_timer - 1),
)
]
@ -295,7 +272,7 @@ class QPLL(Module):
# refclk_div = 2
# self.Xxout_div = 2
self.tx_usrclk_freq = (sys_clk_freq*fbdiv_real/self.Xxout_div)/20
self.tx_usrclk_freq = (sys_clk_freq*fbdiv_real/self.Xxout_div)/40
self.specials += [
Instance("GTXE2_COMMON",
@ -352,7 +329,7 @@ class QPLL(Module):
# Warning: Xilinx transceivers are LSB first, and comma needs to be flipped
# compared to the usual 8b10b binary representation.
class Comma_Detector(Module):
def __init__(self, comma, check_period=150_000):
def __init__(self, comma, check_period=1_000_000):
self.data = Signal(20)
self.rxinit_done = Signal()
@ -368,87 +345,207 @@ class Comma_Detector(Module):
# - UG476 (v1.12.1) p.228
# The validity of data & comma are checked externally
aligned = Signal()
valid_data = Signal()
aligned_rxclk = Signal()
valid_data_rxclk = Signal()
rx1cnt = Signal(max=11)
self.specials += [
MultiReg(aligned_rxclk, aligned),
MultiReg(valid_data_rxclk, valid_data),
]
self.submodules.check_ps = check_ps = PulseSynchronizer("sys", "cxp_gtx_rx")
# aligned = Signal()
# valid_data = Signal()
# aligned_rxclk = Signal()
# valid_data_rxclk = Signal()
# rx1cnt = Signal(max=11)
# self.specials += [
# MultiReg(aligned_rxclk, aligned),
# MultiReg(valid_data_rxclk, valid_data),
# ]
# self.submodules.check_reset = check_reset = PulseSynchronizer("sys", "cxp_gtx_rx")
comma_n = ~comma & 0b1111111111
self.sync.cxp_gtx_rx += [
If(check_ps.o,
aligned_rxclk.eq(0),
).Elif((self.data[:10] == comma) | (self.data[:10] == comma_n),
aligned_rxclk.eq(1),
),
# comma_n = ~comma & 0b1111111111
# self.sync.cxp_gtx_rx += [
# If(check_reset.o,
# aligned_rxclk.eq(0),
# ).Elif((self.data[:10] == comma) | (self.data[:10] == comma_n),
# aligned_rxclk.eq(1),
# ),
rx1cnt.eq(reduce(add, [self.data[i] for i in range(10)])),
If(check_ps.o,
valid_data_rxclk.eq(0),
).Elif((rx1cnt == 4) | (rx1cnt == 5) | (rx1cnt == 6),
valid_data_rxclk.eq(1),
),
]
# rx1cnt.eq(reduce(add, [self.data[i] for i in range(10)])),
# If(check_reset.o,
# valid_data_rxclk.eq(0),
# ).Elif((rx1cnt == 4) | (rx1cnt == 5) | (rx1cnt == 6),
# valid_data_rxclk.eq(1),
# ),
# ]
# self.aligned = Signal()
# self.valid_data = Signal()
# self.comb +=[
# self.aligned.eq(aligned),
# self.valid_data.eq(valid_data)
# ]
check_counter = Signal(reset=check_period-1, max=check_period)
check = Signal()
# check = Signal()
self.sync += [
check.eq(0),
self.restart.eq(0),
# check.eq(0),
# check_reset.i.eq(0),
If(~self.ready,
If(check_counter == 0,
check_counter.eq(check_counter.reset),
check.eq(1),
# check.eq(1),
self.restart.eq(1),
# check_reset.i.eq(1),
).Else(
check_counter.eq(check_counter - 1),
)
)
]
self.submodules.fsm = fsm = FSM(reset_state="WAIT_COMMA_ALIGN")
# WIP:
aligner_en = Signal()
self.specials += MultiReg(aligner_en, self.aligner_en_rxclk, odomain="cxp_gtx_rx")
fsm.act("WAIT_COMMA_ALIGN",
aligner_en.eq(1),
If(check,
check_ps.i.eq(1),
If(aligned,
NextState("WAIT_VALID_DATA"),
comma_n = ~comma & 0b1111111111
has_error = Signal()
comma_aligned = Signal()
comma_seen = Signal()
error_seen = Signal()
one_counts = Signal(max=11)
counter_period = 5000
counter = Signal(reset=counter_period-1, max=counter_period)
check_reset = Signal()
check = Signal()
self.sync.cxp_gtx_rx += [
check.eq(0),
If(counter == 0,
counter.eq(counter.reset),
check.eq(1),
).Else(
self.restart.eq(1),
)
counter.eq(counter - 1),
),
has_error.eq(0),
one_counts.eq(reduce(add, [self.data[i] for i in range(10, 20)])),
If((one_counts != 4) & (one_counts != 5) & (one_counts != 6),
has_error.eq(1)
),
comma_aligned.eq(0),
If((self.data[:10] == comma) | (self.data[:10] == comma_n),
comma_aligned.eq(1)
),
# signal that need to be manually cleared
If(check_reset,
comma_seen.eq(0),
).Elif((self.data[:10] == comma) | (self.data[:10] == comma_n),
comma_seen.eq(1)
),
one_counts.eq(reduce(add, [self.data[i] for i in range(10, 20)])),
If(check_reset,
error_seen.eq(0),
).Elif((one_counts != 4) & (one_counts != 5) & (one_counts != 6),
error_seen.eq(1),
),
]
# DEBUG: expose signal
self.check_reset = Signal()
self.comma_aligned = Signal()
self.comma_seen = Signal()
self.has_error = Signal()
self.error_seen = Signal()
self.comb +=[
self.check_reset.eq(check_reset),
self.comma_aligned.eq(comma_aligned),
self.comma_seen.eq(comma_seen),
self.has_error.eq(has_error),
self.error_seen.eq(error_seen),
]
self.submodules.rxfsm = rxfsm = ClockDomainsRenamer("cxp_gtx_rx")(FSM(reset_state="ALIGNING"))
rxfsm.act("ALIGNING",
self.aligner_en_rxclk.eq(1),
If(comma_aligned & (~has_error),
check_reset.eq(1),
NextState("DOUBLE_CHECK"),
)
)
fsm.act("WAIT_VALID_DATA",
aligner_en.eq(1),
rxfsm.act("DOUBLE_CHECK",
If(check,
check_ps.i.eq(1),
If(aligned & valid_data,
check_reset.eq(1),
If(~error_seen,
NextState("READY"),
).Else(
self.restart.eq(1),
NextState("WAIT_COMMA_ALIGN"),
NextState("ALIGNING")
)
)
)
fsm.act("READY",
self.ready.eq(1),
ready = Signal()
self.specials += MultiReg(ready, self.ready)
rxfsm.act("READY",
ready.eq(1),
If(check,
check_ps.i.eq(1),
If(~(aligned & valid_data),
self.restart.eq(1),
NextState("WAIT_COMMA_ALIGN"),
check_reset.eq(1),
If(~comma_seen,
NextState("ALIGNING"),
)
)
)
# self.submodules.fsm = fsm = FSM(reset_state="WAIT_RXINIT")
# aligner_en = Signal()
# self.specials += MultiReg(aligner_en, self.aligner_en_rxclk, odomain="cxp_gtx_rx")
# fsm.act("WAIT_RXINIT",
# If(self.rxinit_done,
# NextState("WAIT_COMMA_ALIGN"),
# )
# )
# fsm.act("WAIT_COMMA_ALIGN",
# aligner_en.eq(1),
# If(check,
# check_reset.i.eq(1),
# If(aligned,
# NextState("WAIT_VALID_DATA"),
# ).Else(
# self.restart.eq(1),
# NextState("WAIT_RXINIT")
# )
# )
# )
# fsm.act("WAIT_VALID_DATA",
# aligner_en.eq(1),
# If(check,
# check_reset.i.eq(1),
# If(aligned & valid_data,
# NextState("READY"),
# ).Else(
# self.restart.eq(1),
# NextState("WAIT_RXINIT"),
# )
# )
# )
# fsm.act("READY",
# self.ready.eq(1),
# If(check,
# check_reset.i.eq(1),
# If(~(aligned & valid_data),
# self.restart.eq(1),
# NextState("WAIT_RXINIT"),
# )
# )
# )
class GTX(Module):
# Settings:
@ -463,7 +560,7 @@ class GTX(Module):
# linerate = USRCLK * datawidth
pll_fbout_mult = 8
txusr_pll_div = pll_fbout_mult*sys_clk_freq/qpll.tx_usrclk_freq # 20 is datawidth
txusr_pll_div = pll_fbout_mult*sys_clk_freq/qpll.tx_usrclk_freq
self.tx_restart = Signal()
self.rx_restart = Signal()
@ -484,9 +581,9 @@ class GTX(Module):
self.dout = Signal(16)
self.dready = Signal()
self.submodules.encoder = ClockDomainsRenamer("cxp_gtx_tx")(Encoder(2, True))
self.submodules.encoder = ClockDomainsRenamer("cxp_gtx_tx")(Encoder(4, True))
self.submodules.decoders = [ClockDomainsRenamer("cxp_gtx_rx")(
(Decoder(True))) for _ in range(2)]
(Decoder(True))) for _ in range(4)]
# transceiver direct clock outputs
@ -509,8 +606,8 @@ class GTX(Module):
rx_init.cplllock.eq(qpll.lock)
]
txdata = Signal(20)
rxdata = Signal(20)
txdata = Signal(40)
rxdata = Signal(40)
comma_aligner_en = Signal()
# Note: the following parameters were set after consulting AR45360
@ -570,11 +667,11 @@ class GTX(Module):
i_TXINHIBIT=~self.txenable,
# TX data
p_TX_DATA_WIDTH=20,
p_TX_INT_DATAWIDTH=0,
i_TXCHARDISPMODE=Cat(txdata[9], txdata[19]),
i_TXCHARDISPVAL=Cat(txdata[8], txdata[18]),
i_TXDATA=Cat(txdata[:8], txdata[10:18]),
p_TX_DATA_WIDTH=40,
p_TX_INT_DATAWIDTH=1,
i_TXCHARDISPMODE=Cat(txdata[9], txdata[19], txdata[29], txdata[39]),
i_TXCHARDISPVAL=Cat(txdata[8], txdata[18], txdata[28], txdata[38]),
i_TXDATA=Cat(txdata[:8], txdata[10:18], txdata[20:28], txdata[30:38]),
i_TXUSRCLK=ClockSignal("cxp_gtx_tx"),
i_TXUSRCLK2=ClockSignal("cxp_gtx_tx"),
@ -633,27 +730,28 @@ class GTX(Module):
p_CLK_COR_SEQ_2_ENABLE=0b1111,
# RX data
p_RX_DATA_WIDTH=20,
p_RX_INT_DATAWIDTH=0,
o_RXDISPERR=Cat(rxdata[9], rxdata[19]),
o_RXCHARISK=Cat(rxdata[8], rxdata[18]),
o_RXDATA=Cat(rxdata[:8], rxdata[10:18]),
p_RX_DATA_WIDTH=40,
p_RX_INT_DATAWIDTH=1,
o_RXDISPERR=Cat(rxdata[9], rxdata[19], rxdata[29], rxdata[39]),
o_RXCHARISK=Cat(rxdata[8], rxdata[18], rxdata[28], rxdata[38]),
o_RXDATA=Cat(rxdata[:8], rxdata[10:18], rxdata[20:28], rxdata[30:38]),
# RX Byte and Word Alignment Attributes
p_ALIGN_COMMA_DOUBLE="FALSE",
p_ALIGN_COMMA_ENABLE=0b1111111111,
p_ALIGN_COMMA_WORD=2, # align comma to rxdata[:10] only
p_ALIGN_COMMA_WORD=4, # align comma to rxdata[:10] only
p_ALIGN_MCOMMA_DET="TRUE",
p_ALIGN_MCOMMA_VALUE=0b1010000011,
p_ALIGN_PCOMMA_DET="TRUE",
p_ALIGN_PCOMMA_VALUE=0b0101111100,
p_SHOW_REALIGN_COMMA="FALSE",
p_SHOW_REALIGN_COMMA="TRUE",
p_RXSLIDE_AUTO_WAIT=7,
p_RXSLIDE_MODE="OFF",
p_RX_SIG_VALID_DLY=10,
i_RXPCOMMAALIGNEN=comma_aligner_en,
i_RXMCOMMAALIGNEN=comma_aligner_en,
i_RXCOMMADETEN=1,
i_RXSLIDE=0,
# RX 8B/10B Decoder Attributes
p_RX_DISPERR_SEQ_MATCH="FALSE",
@ -758,7 +856,7 @@ class GTX(Module):
p_CLKFBOUT_MULT=pll_fbout_mult, p_DIVCLK_DIVIDE=1,
i_CLKFBIN=txpll_fb_clk, o_CLKFBOUT=txpll_fb_clk,
# frequency = linerate/20
# frequency = linerate/40
p_CLKOUT0_DIVIDE=txusr_pll_div, p_CLKOUT0_PHASE=0.0, o_CLKOUT0=txpll_clkout,
# Dynamic Reconfiguration Ports
@ -784,9 +882,11 @@ class GTX(Module):
]
self.comb += [
txdata.eq(Cat(self.encoder.output[0], self.encoder.output[1])),
txdata.eq(Cat(self.encoder.output[0], self.encoder.output[1], self.encoder.output[2], self.encoder.output[3])),
self.decoders[0].input.eq(rxdata[:10]),
self.decoders[1].input.eq(rxdata[10:])
self.decoders[1].input.eq(rxdata[10:20]),
self.decoders[2].input.eq(rxdata[20:30]),
self.decoders[3].input.eq(rxdata[30:]),
]