diff --git a/artiq/gateware/drtio/transceiver/gtx_7series.py b/artiq/gateware/drtio/transceiver/gtx_7series.py index 5b8828f41..a977d2b4c 100644 --- a/artiq/gateware/drtio/transceiver/gtx_7series.py +++ b/artiq/gateware/drtio/transceiver/gtx_7series.py @@ -346,66 +346,3 @@ class GTX(Module, TransceiverInterface): getattr(self, "cd_rtio_rx" + str(i)).clk.eq(self.gtxs[i].cd_rtio_rx.clk), getattr(self, "cd_rtio_rx" + str(i)).rst.eq(self.gtxs[i].cd_rtio_rx.rst) ] - - -class RXSynchronizer(Module, AutoCSR): - """Delays the data received in the rtio_rx domain by a configurable amount - so that it meets s/h in the rtio domain, and recapture it in the rtio - domain. This has fixed latency. - - Since Xilinx doesn't provide decent on-chip delay lines, we implement the - delay with MMCM that provides a clock and a finely configurable phase, used - to resample the data. - - The phase has to be determined either empirically or by making sense of the - Xilinx scriptures (when existent) and should be constant for a given design - placement. - """ - def __init__(self, rtio_clk_freq, initial_phase=0.0): - self.phase_shift = CSR() - self.phase_shift_done = CSRStatus() - - self.clock_domains.cd_rtio_delayed = ClockDomain() - - mmcm_output = Signal() - mmcm_fb = Signal() - mmcm_locked = Signal() - # maximize VCO frequency to maximize phase shift resolution - mmcm_mult = 1200e6//rtio_clk_freq - self.specials += [ - Instance("MMCME2_ADV", - p_CLKIN1_PERIOD=1e9/rtio_clk_freq, - i_CLKIN1=ClockSignal("rtio_rx"), - i_RST=ResetSignal("rtio_rx"), - i_CLKINSEL=1, # yes, 1=CLKIN1 0=CLKIN2 - - p_CLKFBOUT_MULT_F=mmcm_mult, - p_CLKOUT0_DIVIDE_F=mmcm_mult, - p_CLKOUT0_PHASE=initial_phase, - p_DIVCLK_DIVIDE=1, - - # According to Xilinx, there is no guarantee of input/output - # phase relationship when using internal feedback. We assume - # here that the input/ouput skew is constant to save BUFGs. - o_CLKFBOUT=mmcm_fb, - i_CLKFBIN=mmcm_fb, - - p_CLKOUT0_USE_FINE_PS="TRUE", - o_CLKOUT0=mmcm_output, - o_LOCKED=mmcm_locked, - - i_PSCLK=ClockSignal(), - i_PSEN=self.phase_shift.re, - i_PSINCDEC=self.phase_shift.r, - o_PSDONE=self.phase_shift_done.status, - ), - Instance("BUFR", i_I=mmcm_output, o_O=self.cd_rtio_delayed.clk), - AsyncResetSynchronizer(self.cd_rtio_delayed, ~mmcm_locked) - ] - - def resync(self, signal): - delayed = Signal.like(signal, related=signal) - synchronized = Signal.like(signal, related=signal) - self.sync.rtio_delayed += delayed.eq(signal) - self.sync.rtio += synchronized.eq(delayed) - return synchronized diff --git a/artiq/gateware/drtio/transceiver/gtx_7series_init.py b/artiq/gateware/drtio/transceiver/gtx_7series_init.py index 9e3f65a15..0536cf47a 100644 --- a/artiq/gateware/drtio/transceiver/gtx_7series_init.py +++ b/artiq/gateware/drtio/transceiver/gtx_7series_init.py @@ -250,110 +250,3 @@ class GTXInitPhaseAlignment(Module): master_phaligndone.eq(gtx_init.master_phaligndone), gtx_init.slaves_phaligndone.eq(slaves_phaligndone) ] - - -# 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 BruteforceClockAligner(Module): - def __init__(self, comma, rtio_clk_freq, check_period=6e-3): - self.rxdata = Signal(20) - self.restart = Signal() - - self.ready = Signal() - - check_max_val = ceil(check_period*rtio_clk_freq) - check_counter = Signal(max=check_max_val+1) - check = Signal() - reset_check_counter = Signal() - self.sync.rtio += [ - 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) - ) - ) - ] - - checks_reset = PulseSynchronizer("rtio", "rtio_rx") - self.submodules += checks_reset - - 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.rtio_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.rtio_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 = ClockDomainsRenamer("rtio")(FSM(reset_state="WAIT_COMMA")) - self.submodules += fsm - - 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("WAIT_NOERROR") - ).Else( - self.restart.eq(1) - ), - checks_reset.i.eq(1) - ) - ) - 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(error_seen, - checks_reset.i.eq(1), - self.restart.eq(1), - NextState("WAIT_COMMA") - ) - )