diff --git a/artiq/gateware/drtio/transceiver/gtp_7series.py b/artiq/gateware/drtio/transceiver/gtp_7series.py index 62d2728ef..98360ca2d 100644 --- a/artiq/gateware/drtio/transceiver/gtp_7series.py +++ b/artiq/gateware/drtio/transceiver/gtp_7series.py @@ -13,8 +13,10 @@ from artiq.gateware.drtio.transceiver.gtp_7series_init import * class GTPSingle(Module): def __init__(self, qpll_channel, pads, sys_clk_freq, rtio_clk_freq, mode): - if mode != "master": - raise NotImplementedError + assert mode in ["single", "master", "slave"] + self.mode = mode + + # # # self.stable_clkin = Signal() self.submodules.encoder = encoder = ClockDomainsRenamer("rtio_tx")( @@ -31,10 +33,10 @@ class GTPSingle(Module): # # # # TX generates RTIO clock, init must be in system domain - tx_init = GTPTXInit(sys_clk_freq) + self.submodules.tx_init = tx_init = GTPTXInit(sys_clk_freq, mode) # RX receives restart commands from RTIO domain rx_init = ClockDomainsRenamer("rtio_tx")(GTPRXInit(rtio_clk_freq)) - self.submodules += tx_init, rx_init + self.submodules += rx_init self.comb += [ tx_init.stable_clkin.eq(self.stable_clkin), @@ -353,7 +355,7 @@ class GTPSingle(Module): # TX Buffer Attributes p_TXSYNC_MULTILANE =0b0, - p_TXSYNC_OVRD =0b0, + p_TXSYNC_OVRD =0b1, p_TXSYNC_SKIP_DA =0b0 ) gtp_params.update( @@ -438,7 +440,7 @@ class GTPSingle(Module): #o_RXNOTINTABLE =, # Receive Ports - RX AFE Ports i_GTPRXN =pads.rxn, - i_GTPRXP =pads.rxp, + i_GTPRXP =pads.rxp, i_PMARSVDIN2 =0b0, #o_PMARSVDOUT0 =, #o_PMARSVDOUT1 =, @@ -667,7 +669,7 @@ class GTPSingle(Module): tx_reset_deglitched.attr.add("no_retiming") self.sync += tx_reset_deglitched.eq(~tx_init.done) self.clock_domains.cd_rtio_tx = ClockDomain() - if mode == "master": + if mode == "master" or mode == "single": self.specials += Instance("BUFG", i_I=self.txoutclk, o_O=self.cd_rtio_tx.clk) self.specials += AsyncResetSynchronizer(self.cd_rtio_tx, tx_reset_deglitched) @@ -698,30 +700,52 @@ class GTPSingle(Module): ] +class GTPTXPhaseAlignement(Module): + # TX Buffer Bypass in Single-Lane/Multi-Lane Auto Mode (ug482) + def __init__(self, gtps): + master_phaligndone = Signal() + slaves_phaligndone = Signal(reset=1) + # Specific to Slave transceivers + for gtp in gtps: + if gtp.mode == "slave": + self.comb += gtp.tx_init.master_phaligndone.eq(master_phaligndone) + slaves_phaligndone = slaves_phaligndone & gtp.tx_init.done + # Specific to Master transceivers + for gtp in gtps: + if gtp.mode == "master": + self.comb += [ + master_phaligndone.eq(gtp.tx_init.master_phaligndone), + gtp.tx_init.slaves_phaligndone.eq(slaves_phaligndone) + ] + + class GTP(Module, TransceiverInterface): def __init__(self, qpll_channel, data_pads, sys_clk_freq, rtio_clk_freq, master=0): self.nchannels = nchannels = len(data_pads) self.gtps = [] - if nchannels > 1: - raise NotImplementedError # # # rtio_tx_clk = Signal() channel_interfaces = [] for i in range(nchannels): - mode = "master" if i == master else "slave" - gtp = GTPSingle(qpll_channel, data_pads[i], sys_clk_freq, rtio_clk_freq, mode) - if mode == "master": - self.comb += rtio_tx_clk.eq(gtp.cd_rtio_tx.clk) + if nchannels == 1: + mode = "single" else: + mode = "master" if i == master else "slave" + gtp = GTPSingle(qpll_channel, data_pads[i], sys_clk_freq, rtio_clk_freq, mode) + if mode == "slave": self.comb += gtp.cd_rtio_tx.clk.eq(rtio_tx_clk) + else: + self.comb += rtio_tx_clk.eq(gtp.cd_rtio_tx.clk) self.gtps.append(gtp) setattr(self.submodules, "gtp"+str(i), gtp) channel_interface = ChannelInterface(gtp.encoder, gtp.decoders) self.comb += channel_interface.rx_ready.eq(gtp.rx_ready) channel_interfaces.append(channel_interface) + self.submodules.tx_phase_alignment = GTPTXPhaseAlignement(self.gtps) + TransceiverInterface.__init__(self, channel_interfaces) for gtp in self.gtps: self.comb += gtp.stable_clkin.eq(self.stable_clkin.storage) diff --git a/artiq/gateware/drtio/transceiver/gtp_7series_init.py b/artiq/gateware/drtio/transceiver/gtp_7series_init.py index 18b7a08a4..8916c2c36 100644 --- a/artiq/gateware/drtio/transceiver/gtp_7series_init.py +++ b/artiq/gateware/drtio/transceiver/gtp_7series_init.py @@ -9,7 +9,7 @@ __all__ = ["GTPTXInit", "GTPRXInit"] class GTPTXInit(Module): - def __init__(self, sys_clk_freq): + def __init__(self, sys_clk_freq, mode="single"): self.stable_clkin = Signal() self.done = Signal() self.restart = Signal() @@ -29,6 +29,9 @@ class GTPTXInit(Module): self.txdlyen = Signal() self.txuserrdy = Signal() + self.master_phaligndone = Signal() + self.slaves_phaligndone = Signal() + # # # # Double-latch transceiver asynch outputs @@ -106,6 +109,16 @@ class GTPTXInit(Module): txuserrdy.eq(1), txdlysreset.eq(1), If(txdlysresetdone, + If(mode == "slave", + NextState("WAIT_MASTER") + ).Else( + NextState("PHALIGN") + ) + ) + ) + startup_fsm.act("WAIT_MASTER", + txuserrdy.eq(1), + If(self.master_phaligndone, NextState("PHALIGN") ) ) @@ -117,16 +130,39 @@ class GTPTXInit(Module): NextState("WAIT_FIRST_ALIGN_DONE") ) ) - # Wait 2 rising edges of Xxphaligndone - # (from UG482 in TX Buffer Bypass in Single-Lane Auto Mode) + # Wait N rising edges of Xxphaligndone + # N=2 for Single, 3 for Master, 1 for Slave + # (from UGB482 in TX Buffer Bypass in Multi/Single-Lane Auto Mode) startup_fsm.act("WAIT_FIRST_ALIGN_DONE", txuserrdy.eq(1), txphalign.eq(1), If(txphaligndone_rising, - NextState("WAIT_SECOND_ALIGN_DONE") + If(mode == "slave", + NextState("READY") + ).Else( + NextState("WAIT_SECOND_ALIGN_DONE") + ) ) ) startup_fsm.act("WAIT_SECOND_ALIGN_DONE", + txuserrdy.eq(1), + txdlyen.eq(1), + If(txphaligndone_rising, + If(mode == "master", + NextState("WAIT_SLAVES") + ).Else( + NextState("READY") + ) + ) + ) + startup_fsm.act("WAIT_SLAVES", + txuserrdy.eq(1), + self.master_phaligndone.eq(1), + If(self.slaves_phaligndone, + NextState("WAIT_THIRD_ALIGN_DONE") + ) + ) + startup_fsm.act("WAIT_THIRD_ALIGN_DONE", txuserrdy.eq(1), txdlyen.eq(1), If(txphaligndone_rising,