forked from M-Labs/artiq
drtio/transceiver/gtp: implement tx multi lane phase alignment sequence
This commit is contained in:
parent
e565d3fa59
commit
1f0d955ce4
|
@ -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(
|
||||||
|
@ -438,7 +440,7 @@ class GTPSingle(Module):
|
||||||
#o_RXNOTINTABLE =,
|
#o_RXNOTINTABLE =,
|
||||||
# Receive Ports - RX AFE Ports
|
# Receive Ports - RX AFE Ports
|
||||||
i_GTPRXN =pads.rxn,
|
i_GTPRXN =pads.rxn,
|
||||||
i_GTPRXP =pads.rxp,
|
i_GTPRXP =pads.rxp,
|
||||||
i_PMARSVDIN2 =0b0,
|
i_PMARSVDIN2 =0b0,
|
||||||
#o_PMARSVDOUT0 =,
|
#o_PMARSVDOUT0 =,
|
||||||
#o_PMARSVDOUT1 =,
|
#o_PMARSVDOUT1 =,
|
||||||
|
@ -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):
|
||||||
mode = "master" if i == master else "slave"
|
if nchannels == 1:
|
||||||
gtp = GTPSingle(qpll_channel, data_pads[i], sys_clk_freq, rtio_clk_freq, mode)
|
mode = "single"
|
||||||
if mode == "master":
|
|
||||||
self.comb += rtio_tx_clk.eq(gtp.cd_rtio_tx.clk)
|
|
||||||
else:
|
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)
|
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)
|
||||||
|
|
|
@ -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,
|
||||||
NextState("WAIT_SECOND_ALIGN_DONE")
|
If(mode == "slave",
|
||||||
|
NextState("READY")
|
||||||
|
).Else(
|
||||||
|
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,
|
||||||
|
|
Loading…
Reference in New Issue