drtio/transceiver/gtp: implement tx multi lane phase alignment sequence

This commit is contained in:
Florent Kermarrec 2018-02-27 12:32:25 +01:00
parent e565d3fa59
commit 1f0d955ce4
2 changed files with 77 additions and 17 deletions

View File

@ -13,8 +13,10 @@ from artiq.gateware.drtio.transceiver.gtp_7series_init import *
class GTPSingle(Module): class GTPSingle(Module):
def __init__(self, qpll_channel, pads, sys_clk_freq, rtio_clk_freq, mode): def __init__(self, qpll_channel, pads, sys_clk_freq, rtio_clk_freq, mode):
if mode != "master": assert mode in ["single", "master", "slave"]
raise NotImplementedError self.mode = mode
# # #
self.stable_clkin = Signal() self.stable_clkin = Signal()
self.submodules.encoder = encoder = ClockDomainsRenamer("rtio_tx")( 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 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 receives restart commands from RTIO domain
rx_init = ClockDomainsRenamer("rtio_tx")(GTPRXInit(rtio_clk_freq)) rx_init = ClockDomainsRenamer("rtio_tx")(GTPRXInit(rtio_clk_freq))
self.submodules += tx_init, rx_init self.submodules += rx_init
self.comb += [ self.comb += [
tx_init.stable_clkin.eq(self.stable_clkin), tx_init.stable_clkin.eq(self.stable_clkin),
@ -353,7 +355,7 @@ class GTPSingle(Module):
# TX Buffer Attributes # TX Buffer Attributes
p_TXSYNC_MULTILANE =0b0, p_TXSYNC_MULTILANE =0b0,
p_TXSYNC_OVRD =0b0, p_TXSYNC_OVRD =0b1,
p_TXSYNC_SKIP_DA =0b0 p_TXSYNC_SKIP_DA =0b0
) )
gtp_params.update( gtp_params.update(
@ -667,7 +669,7 @@ class GTPSingle(Module):
tx_reset_deglitched.attr.add("no_retiming") tx_reset_deglitched.attr.add("no_retiming")
self.sync += tx_reset_deglitched.eq(~tx_init.done) self.sync += tx_reset_deglitched.eq(~tx_init.done)
self.clock_domains.cd_rtio_tx = ClockDomain() 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 += Instance("BUFG", i_I=self.txoutclk, o_O=self.cd_rtio_tx.clk)
self.specials += AsyncResetSynchronizer(self.cd_rtio_tx, tx_reset_deglitched) 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): class GTP(Module, TransceiverInterface):
def __init__(self, qpll_channel, data_pads, sys_clk_freq, rtio_clk_freq, master=0): def __init__(self, qpll_channel, data_pads, sys_clk_freq, rtio_clk_freq, master=0):
self.nchannels = nchannels = len(data_pads) self.nchannels = nchannels = len(data_pads)
self.gtps = [] self.gtps = []
if nchannels > 1:
raise NotImplementedError
# # # # # #
rtio_tx_clk = Signal() rtio_tx_clk = Signal()
channel_interfaces = [] channel_interfaces = []
for i in range(nchannels): for i in range(nchannels):
if nchannels == 1:
mode = "single"
else:
mode = "master" if i == master else "slave" mode = "master" if i == master else "slave"
gtp = GTPSingle(qpll_channel, data_pads[i], sys_clk_freq, rtio_clk_freq, mode) gtp = GTPSingle(qpll_channel, data_pads[i], sys_clk_freq, rtio_clk_freq, mode)
if mode == "master": if mode == "slave":
self.comb += rtio_tx_clk.eq(gtp.cd_rtio_tx.clk)
else:
self.comb += gtp.cd_rtio_tx.clk.eq(rtio_tx_clk) 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) self.gtps.append(gtp)
setattr(self.submodules, "gtp"+str(i), gtp) setattr(self.submodules, "gtp"+str(i), gtp)
channel_interface = ChannelInterface(gtp.encoder, gtp.decoders) channel_interface = ChannelInterface(gtp.encoder, gtp.decoders)
self.comb += channel_interface.rx_ready.eq(gtp.rx_ready) self.comb += channel_interface.rx_ready.eq(gtp.rx_ready)
channel_interfaces.append(channel_interface) channel_interfaces.append(channel_interface)
self.submodules.tx_phase_alignment = GTPTXPhaseAlignement(self.gtps)
TransceiverInterface.__init__(self, channel_interfaces) TransceiverInterface.__init__(self, channel_interfaces)
for gtp in self.gtps: for gtp in self.gtps:
self.comb += gtp.stable_clkin.eq(self.stable_clkin.storage) self.comb += gtp.stable_clkin.eq(self.stable_clkin.storage)

View File

@ -9,7 +9,7 @@ __all__ = ["GTPTXInit", "GTPRXInit"]
class GTPTXInit(Module): class GTPTXInit(Module):
def __init__(self, sys_clk_freq): def __init__(self, sys_clk_freq, mode="single"):
self.stable_clkin = Signal() self.stable_clkin = Signal()
self.done = Signal() self.done = Signal()
self.restart = Signal() self.restart = Signal()
@ -29,6 +29,9 @@ class GTPTXInit(Module):
self.txdlyen = Signal() self.txdlyen = Signal()
self.txuserrdy = Signal() self.txuserrdy = Signal()
self.master_phaligndone = Signal()
self.slaves_phaligndone = Signal()
# # # # # #
# Double-latch transceiver asynch outputs # Double-latch transceiver asynch outputs
@ -106,6 +109,16 @@ class GTPTXInit(Module):
txuserrdy.eq(1), txuserrdy.eq(1),
txdlysreset.eq(1), txdlysreset.eq(1),
If(txdlysresetdone, 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") NextState("PHALIGN")
) )
) )
@ -117,16 +130,39 @@ class GTPTXInit(Module):
NextState("WAIT_FIRST_ALIGN_DONE") NextState("WAIT_FIRST_ALIGN_DONE")
) )
) )
# Wait 2 rising edges of Xxphaligndone # Wait N rising edges of Xxphaligndone
# (from UG482 in TX Buffer Bypass in Single-Lane Auto Mode) # 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", startup_fsm.act("WAIT_FIRST_ALIGN_DONE",
txuserrdy.eq(1), txuserrdy.eq(1),
txphalign.eq(1), txphalign.eq(1),
If(txphaligndone_rising, If(txphaligndone_rising,
If(mode == "slave",
NextState("READY")
).Else(
NextState("WAIT_SECOND_ALIGN_DONE") NextState("WAIT_SECOND_ALIGN_DONE")
) )
) )
)
startup_fsm.act("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), txuserrdy.eq(1),
txdlyen.eq(1), txdlyen.eq(1),
If(txphaligndone_rising, If(txphaligndone_rising,