forked from M-Labs/artiq-zynq
cxp downconn: cleanup & use bruteforce aligner
This commit is contained in:
parent
2aef11143a
commit
dbce74d831
|
@ -1,4 +1,6 @@
|
|||
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
|
||||
|
@ -100,12 +102,12 @@ class CXP_DownConn(Module, AutoCSR):
|
|||
|
||||
# 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=gtx.clk_aligner.rxinit_done, o_O=pmod_pads[1]),
|
||||
Instance("OBUF", i_I=gtx.clk_aligner.restart, o_O=pmod_pads[2]),
|
||||
Instance("OBUF", i_I=gtx.clk_aligner.comma_det.comma_aligned, o_O=pmod_pads[3]),
|
||||
Instance("OBUF", i_I=gtx.clk_aligner.ready, o_O=pmod_pads[4]),
|
||||
Instance("OBUF", i_I=gtx.clk_aligner.comma_det.reset, o_O=pmod_pads[5]),
|
||||
Instance("OBUF", i_I=gtx.clk_aligner.comma_det.detected, o_O=pmod_pads[6]),
|
||||
# Instance("OBUF", i_I=, o_O=pmod_pads[7]),
|
||||
]
|
||||
|
||||
|
@ -131,6 +133,8 @@ class CXP_DownConn(Module, AutoCSR):
|
|||
self.decoded_k_0 = CSRStatus()
|
||||
self.decoded_k_1 = CSRStatus()
|
||||
|
||||
self.shifted = CSRStatus(9)
|
||||
|
||||
|
||||
self.sync.cxp_gtx_tx += [
|
||||
If(counter == 0,
|
||||
|
@ -158,6 +162,9 @@ 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),
|
||||
If(self.gtx.clk_aligner.comma_det.detected,
|
||||
self.shifted.status.eq(self.gtx.clk_aligner.comma_det.bitshift),
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
|
@ -253,75 +260,192 @@ class QPLL(Module):
|
|||
)
|
||||
]
|
||||
|
||||
# detect if the comma is located at data[0:10]
|
||||
class Comma_Detector(Module):
|
||||
def __init__(self, comma, width):
|
||||
self.reset = Signal()
|
||||
self.data = Signal(width)
|
||||
|
||||
self.detected = Signal()
|
||||
self.bitshift = Signal(max=width)
|
||||
self.comma_aligned = Signal()
|
||||
|
||||
# # #
|
||||
last_data = Signal(10)
|
||||
data = Signal(width+10)
|
||||
|
||||
comma_n = ~comma & 0b1111111111
|
||||
self.sync += [
|
||||
last_data.eq(self.data[:10]),
|
||||
data.eq(Cat(self.data, last_data)),
|
||||
If(self.reset,
|
||||
self.bitshift.eq(0),
|
||||
self.detected.eq(0),
|
||||
),
|
||||
|
||||
If(self.reset,
|
||||
self.comma_aligned.eq(0)
|
||||
).Elif((self.data[:10] == comma) | (self.data[:10] == comma_n),
|
||||
self.comma_aligned.eq(1)
|
||||
),
|
||||
]
|
||||
|
||||
for n in range(width):
|
||||
self.sync += \
|
||||
If((data[n:n+10] == comma) | (data[n:n+10] == comma_n),
|
||||
self.bitshift.eq(width-n),
|
||||
self.detected.eq(1),
|
||||
)
|
||||
|
||||
# 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):
|
||||
|
||||
def __init__(self, comma, check_period=50_000, width=20):
|
||||
self.rxslide = Signal()
|
||||
self.rxdata = Signal(20)
|
||||
self.data = Signal(width)
|
||||
self.rxinit_done = Signal()
|
||||
|
||||
self.ready = Signal()
|
||||
self.restart = Signal()
|
||||
|
||||
# # #
|
||||
|
||||
checks_reset = Signal()
|
||||
error_seen = Signal()
|
||||
comma_seen = Signal()
|
||||
self.submodules.restart_ps = restart_ps = PulseSynchronizer("cxp_gtx_rx", "sys")
|
||||
|
||||
rx1cnt = Signal(max=11)
|
||||
timerout_period = 5_000_000
|
||||
timerout = Signal(reset=timerout_period-1, max=timerout_period)
|
||||
|
||||
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)
|
||||
ready_sys = Signal()
|
||||
self.specials += MultiReg(self.ready, ready_sys)
|
||||
|
||||
# NOTE: be careful of all the timeout values!!! It should be much larger than the longest fsm
|
||||
# TODO: fix comma fall too fast for 500MHz (10Gpbs) -> need to change CDR_CFG via DRP
|
||||
# Restart rx periodically since rx need to be restart when connecting RXN/RXP
|
||||
self.sync += [
|
||||
self.restart.eq(0),
|
||||
If(restart_ps.o,
|
||||
timerout.eq(timerout.reset),
|
||||
self.restart.eq(1),
|
||||
),
|
||||
If(checks_reset,
|
||||
comma_seen.eq(0)
|
||||
).Elif((self.rxdata[:10] == comma) | (self.rxdata[:10] == comma_n),
|
||||
comma_seen.eq(1)
|
||||
If(~ready_sys,
|
||||
If((timerout == 0),
|
||||
timerout.eq(timerout.reset),
|
||||
self.restart.eq(1),
|
||||
).Else(
|
||||
timerout.eq(timerout - 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)
|
||||
|
||||
fsm = ClockDomainsRenamer("cxp_gtx_rx")(FSM(reset_state="IDLE"))
|
||||
self.submodules += fsm
|
||||
self.submodules.comma_det = comma_det = ClockDomainsRenamer("cxp_gtx_rx")(Comma_Detector(comma, width))
|
||||
self.comb += comma_det.data.eq(self.data)
|
||||
|
||||
# minimum of 32 RXUSRCLK2 cycles are required between two RXSLIDE pulses
|
||||
# There is latency between the slide and the slide result at RXDATA
|
||||
# Max = 967.5 UI
|
||||
# see 42662 - 7 Series GTX Transceivers - TX and RX Latency Values
|
||||
# https://support.xilinx.com/s/article/42662?language=en_US
|
||||
self.submodules.timer = timer = ClockDomainsRenamer("cxp_gtx_rx")(WaitTimer(64))
|
||||
|
||||
# DS191 (v1.18.1) Table 95
|
||||
# Tlock = 50000 UI
|
||||
self.submodules.cdr_stable_timer = cdr_stable_timer = ClockDomainsRenamer("cxp_gtx_rx")(WaitTimer(check_period))
|
||||
self.submodules.fsm = fsm = ClockDomainsRenamer("cxp_gtx_rx")(FSM(reset_state="IDLE"))
|
||||
|
||||
|
||||
# NOTE: if this is connected to PMOD it will work on 250MHz
|
||||
# BUG: somehow the comma det doesn't work after the cdr_stable_timer :( thus, waiting for the timeout to happen
|
||||
# (which I know from bruteforcealigner, doesn't work well :( )
|
||||
rxinit_done_rxclk = Signal()
|
||||
self.specials += MultiReg(self.rxinit_done, rxinit_done_rxclk, odomain="cxp_gtx_rx")
|
||||
|
||||
check_timer = Signal(reset=check_period-1,max=check_period)
|
||||
slide_counter = Signal(max=width)
|
||||
|
||||
fsm.act("IDLE",
|
||||
slide_timer.wait.eq(1),
|
||||
If(slide_timer.done,
|
||||
If(comma_seen,
|
||||
NextState("READY"),
|
||||
).Else(
|
||||
NextState("SLIDING")
|
||||
)
|
||||
)
|
||||
comma_det.reset.eq(1),
|
||||
If(rxinit_done_rxclk,
|
||||
NextState("WAIT_COMMA_DET"),
|
||||
),
|
||||
)
|
||||
|
||||
fsm.act("SLIDING",
|
||||
self.rxslide.eq(1),
|
||||
checks_reset.eq(1),
|
||||
NextState("IDLE")
|
||||
fsm.act("WAIT_COMMA_DET",
|
||||
cdr_stable_timer.wait.eq(1),
|
||||
If(cdr_stable_timer.done,
|
||||
If(comma_det.comma_aligned,
|
||||
NextState("READY")
|
||||
).Else(
|
||||
restart_ps.i.eq(1),
|
||||
NextState("IDLE"),
|
||||
)
|
||||
),
|
||||
)
|
||||
|
||||
fsm.act("READY",
|
||||
self.ready.eq(1),
|
||||
If(counter == 0,
|
||||
NextValue(counter, check_cycles - 1),
|
||||
If(error_seen,
|
||||
If(check_timer == 0,
|
||||
NextValue(check_timer, check_timer.reset),
|
||||
comma_det.reset.eq(1),
|
||||
If(~comma_det.comma_aligned,
|
||||
restart_ps.i.eq(1),
|
||||
NextState("IDLE"),
|
||||
)
|
||||
).Else(
|
||||
NextValue(counter, counter - 1),
|
||||
NextValue(check_timer, check_timer - 1),
|
||||
)
|
||||
)
|
||||
|
||||
# fsm.act("IDLE",
|
||||
# comma_det.reset.eq(1),
|
||||
# If(rxinit_done_rxclk,
|
||||
# cdr_stable_timer.wait.eq(1),
|
||||
# If(cdr_stable_timer.done,
|
||||
# NextState("WAIT_COMMA_DET"),
|
||||
# ),
|
||||
# ),
|
||||
# )
|
||||
|
||||
# fsm.act("WAIT_COMMA_DET",
|
||||
# If(comma_det.detected,
|
||||
# NextValue(slide_counter, comma_det.bitshift),
|
||||
# NextState("SLIDING")
|
||||
# )
|
||||
# )
|
||||
|
||||
# fsm.act("SLIDING",
|
||||
# comma_det.reset.eq(1),
|
||||
# If(slide_counter == 0,
|
||||
# NextValue(check_timer, check_timer.reset),
|
||||
# NextState("READY")
|
||||
# ).Else(
|
||||
# NextValue(slide_counter, slide_counter - 1),
|
||||
# self.rxslide.eq(1),
|
||||
# NextState("WAIT_DATA_SLIDE"),
|
||||
# )
|
||||
# )
|
||||
|
||||
# fsm.act("WAIT_DATA_SLIDE",
|
||||
# comma_det.reset.eq(1),
|
||||
# timer.wait.eq(1),
|
||||
# If(timer.done,
|
||||
# NextState("SLIDING"),
|
||||
# )
|
||||
# )
|
||||
|
||||
# fsm.act("READY",
|
||||
# self.ready.eq(1),
|
||||
# If(check_timer == 0,
|
||||
# NextValue(check_timer, check_timer.reset),
|
||||
# comma_det.reset.eq(1),
|
||||
# If(~comma_det.comma_aligned,
|
||||
# restart_ps.i.eq(1),
|
||||
# NextState("IDLE"),
|
||||
# )
|
||||
# ).Else(
|
||||
# NextValue(check_timer, check_timer - 1),
|
||||
# )
|
||||
# )
|
||||
|
||||
|
||||
class GTX(Module):
|
||||
|
@ -364,7 +488,7 @@ class GTX(Module):
|
|||
# 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)
|
||||
self.submodules.rx_init = rx_init = GTXInit(sys_clk_freq, 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))
|
||||
|
@ -507,7 +631,7 @@ 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_COMMA_WORD=2, # allow rxslide to shift 20 times
|
||||
p_ALIGN_MCOMMA_DET="FALSE",
|
||||
p_ALIGN_MCOMMA_VALUE=0b1010000011,
|
||||
p_ALIGN_PCOMMA_DET="FALSE",
|
||||
|
@ -516,11 +640,9 @@ class GTX(Module):
|
|||
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_RXCOMMADETEN=0, # enable manual word alignment
|
||||
i_RXSLIDE=rxslide,
|
||||
|
||||
# RX 8B/10B Decoder Attributes
|
||||
|
@ -650,10 +772,11 @@ class GTX(Module):
|
|||
|
||||
self.submodules.clk_aligner = clk_aligner = Manual_Aligner(0b0101111100)
|
||||
self.comb += [
|
||||
clk_aligner.rxdata.eq(rxdata),
|
||||
clk_aligner.data.eq(rxdata),
|
||||
clk_aligner.rxinit_done.eq(rx_init.done),
|
||||
rxslide.eq(clk_aligner.rxslide),
|
||||
self.rx_ready.eq(clk_aligner.ready),
|
||||
|
||||
rx_init.restart.eq(self.rx_restart),
|
||||
rx_init.restart.eq(self.rx_restart | clk_aligner.restart),
|
||||
tx_init.restart.eq(self.tx_restart),
|
||||
]
|
||||
|
|
Loading…
Reference in New Issue