Skip to content

Commit

Permalink
serdes/xilinx: Allow tx/rx_clk sharing between transceivers (and tx_c…
Browse files Browse the repository at this point in the history
…lk/rx_clk parameters to it).

On some designs where several transceivers are used aggregated (ex multi-lane JESD204B),
sharing tx/rx clocks between transceivers is useful to reduce number of clocks in regions
and ease routing.
  • Loading branch information
enjoy-digital committed Aug 4, 2023
1 parent 151cc07 commit e48140d
Show file tree
Hide file tree
Showing 5 changed files with 231 additions and 126 deletions.
51 changes: 37 additions & 14 deletions liteiclink/serdes/gth3_ultrascale.py
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ def __init__(self, refclk, refclk_freq, linerate):
# GTH3 ---------------------------------------------------------------------------------------------

class GTH3(LiteXModule):
def __init__(self, pll, tx_pads, rx_pads, sys_clk_freq,
def __init__(self, pll, tx_pads, rx_pads, sys_clk_freq, tx_clk=None, rx_clk=None,
data_width = 20,
tx_buffer_enable = False,
rx_buffer_enable = False,
Expand Down Expand Up @@ -903,26 +903,49 @@ def __init__(self, pll, tx_pads, rx_pads, sys_clk_freq,
tx_reset_deglitched.attr.add("no_retiming")
self.sync += tx_reset_deglitched.eq(~tx_init.done)
self.cd_tx = ClockDomain()
if not tx_buffer_enable:
tx_bufg_div = pll.config["clkin"]/self.tx_clk_freq


# Use/generate local tx_clk.
# --------------------------
if tx_clk is None:
if not tx_buffer_enable:
tx_bufg_div = pll.config["clkin"]/self.tx_clk_freq
else:
tx_bufg_div = 1
assert tx_bufg_div == int(tx_bufg_div)
self.specials += [
Instance("BUFG_GT", i_I=self.txoutclk, o_O=self.cd_tx.clk,
i_DIV=int(tx_bufg_div)-1),
AsyncResetSynchronizer(self.cd_tx, tx_reset_deglitched)
]

# Use provided/shared tx_clk.
# ---------------------------
else:
tx_bufg_div = 1
assert tx_bufg_div == int(tx_bufg_div)
self.specials += [
Instance("BUFG_GT", i_I=self.txoutclk, o_O=self.cd_tx.clk,
i_DIV=int(tx_bufg_div)-1),
AsyncResetSynchronizer(self.cd_tx, tx_reset_deglitched)
]
assert tx_buffer_enable
self.cd_tx.clk = tx_clk # Override instead of assign to only keep one real clk.
self.specials += AsyncResetSynchronizer(self.cd_tx, tx_reset_deglitched)

# RX clocking ------------------------------------------------------------------------------
rx_reset_deglitched = Signal()
rx_reset_deglitched.attr.add("no_retiming")
self.sync.tx += rx_reset_deglitched.eq(~rx_init.done)
self.cd_rx = ClockDomain()
self.specials += [
Instance("BUFG_GT", i_I=self.rxoutclk, o_O=self.cd_rx.clk),
AsyncResetSynchronizer(self.cd_rx, rx_reset_deglitched)
]


# Use/generate local rx_clk.
# --------------------------
if rx_clk is None:
self.specials += [
Instance("BUFG_GT", i_I=self.rxoutclk, o_O=self.cd_rx.clk),
AsyncResetSynchronizer(self.cd_rx, rx_reset_deglitched)
]
# Use provided/shared rx_clk.
# ---------------------------
else:
assert rx_buffer_enable
self.cd_rx.clk = rx_clk # Override instead of assign to only keep one real clk.
self.specials += AsyncResetSynchronizer(self.cd_rx, rx_reset_deglitched)

# TX Datapath and PRBS ---------------------------------------------------------------------
self.tx_prbs = ClockDomainsRenamer("tx")(PRBSTX(data_width, reverse=True))
Expand Down
49 changes: 35 additions & 14 deletions liteiclink/serdes/gth4_ultrascale.py
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ def __init__(self, refclk, refclk_freq, linerate):
# GTH4 ---------------------------------------------------------------------------------------------

class GTH4(LiteXModule):
def __init__(self, pll, tx_pads, rx_pads, sys_clk_freq,
def __init__(self, pll, tx_pads, rx_pads, sys_clk_freq, tx_clk=None, rx_clk=None,
data_width = 20,
tx_buffer_enable = False,
rx_buffer_enable = False,
Expand Down Expand Up @@ -1077,26 +1077,47 @@ def __init__(self, pll, tx_pads, rx_pads, sys_clk_freq,
tx_reset_deglitched.attr.add("no_retiming")
self.sync += tx_reset_deglitched.eq(~tx_init.done)
self.cd_tx = ClockDomain()
if not tx_buffer_enable:
tx_bufg_div = pll.config["clkin"]/self.tx_clk_freq

# Use/generate local tx_clk.
# --------------------------
if tx_clk is None:
if not tx_buffer_enable:
tx_bufg_div = pll.config["clkin"]/self.tx_clk_freq
else:
tx_bufg_div = 1
assert tx_bufg_div == int(tx_bufg_div)
self.specials += [
Instance("BUFG_GT", i_I=self.txoutclk, o_O=self.cd_tx.clk,
i_DIV=int(tx_bufg_div)-1),
AsyncResetSynchronizer(self.cd_tx, tx_reset_deglitched)
]

# Use provided/shared tx_clk.
# ---------------------------
else:
tx_bufg_div = 1
assert tx_bufg_div == int(tx_bufg_div)
self.specials += [
Instance("BUFG_GT", i_I=self.txoutclk, o_O=self.cd_tx.clk,
i_DIV=int(tx_bufg_div)-1),
AsyncResetSynchronizer(self.cd_tx, tx_reset_deglitched)
]
assert tx_buffer_enable
self.cd_tx.clk = tx_clk # Override instead of assign to only keep one real clk.
self.specials += AsyncResetSynchronizer(self.cd_tx, tx_reset_deglitched)

# RX clocking ------------------------------------------------------------------------------
rx_reset_deglitched = Signal()
rx_reset_deglitched.attr.add("no_retiming")
self.sync.tx += rx_reset_deglitched.eq(~rx_init.done)
self.cd_rx = ClockDomain()
self.specials += [
Instance("BUFG_GT", i_I=self.rxoutclk, o_O=self.cd_rx.clk),
AsyncResetSynchronizer(self.cd_rx, rx_reset_deglitched)
]

# Use/generate local rx_clk.
# --------------------------
if rx_clk is None:
self.specials += [
Instance("BUFG_GT", i_I=self.rxoutclk, o_O=self.cd_rx.clk),
AsyncResetSynchronizer(self.cd_rx, rx_reset_deglitched)
]
# Use provided/shared rx_clk.
# ---------------------------
else:
assert rx_buffer_enable
self.cd_rx.clk = rx_clk # Override instead of assign to only keep one real clk.
self.specials += AsyncResetSynchronizer(self.cd_rx, rx_reset_deglitched)

# TX Datapath and PRBS ---------------------------------------------------------------------
self.tx_prbs = ClockDomainsRenamer("tx")(PRBSTX(data_width, reverse=True))
Expand Down
104 changes: 62 additions & 42 deletions liteiclink/serdes/gtp_7series.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ def __repr__(self):
# GTP ----------------------------------------------------------------------------------------------

class GTP(LiteXModule):
def __init__(self, qpll, tx_pads, rx_pads, sys_clk_freq, qpll_reset=True,
def __init__(self, qpll, tx_pads, rx_pads, sys_clk_freq, qpll_reset=True, tx_clk=None, rx_clk=None,
data_width = 20,
tx_buffer_enable = False,
rx_buffer_enable = False,
Expand Down Expand Up @@ -934,56 +934,76 @@ def __init__(self, qpll, tx_pads, rx_pads, sys_clk_freq, qpll_reset=True,
self.sync += tx_reset_deglitched.eq(~tx_init.done)
self.cd_tx = ClockDomain()

txoutclk_bufg = Signal()
self.specials += Instance("BUFG",
i_I = self.txoutclk,
o_O = txoutclk_bufg,
)
# Use/generate local tx_clk.
# --------------------------
if tx_clk is None:
txoutclk_bufg = Signal()
self.specials += Instance("BUFG",
i_I = self.txoutclk,
o_O = txoutclk_bufg,
)

if not tx_buffer_enable:
txoutclk_div = qpll.config["clkin"]/self.tx_clk_freq
if not tx_buffer_enable:
txoutclk_div = qpll.config["clkin"]/self.tx_clk_freq
else:
txoutclk_div = 1
# Use txoutclk_bufg when divider is 1
if txoutclk_div == 1:
self.comb += self.cd_tx.clk.eq(txoutclk_bufg)
self.specials += AsyncResetSynchronizer(self.cd_tx, tx_reset_deglitched)
# Use a BUFR when integer divider (with BUFR_DIVIDE)
elif txoutclk_div == int(txoutclk_div):
txoutclk_bufr = Signal()
self.specials += [
Instance("BUFR",
p_BUFR_DIVIDE = str(int(txoutclk_div)),
i_CE = 1,
i_I = txoutclk_bufg,
o_O = txoutclk_bufr,
),
Instance("BUFG",
i_I = txoutclk_bufr,
o_O = self.cd_tx.clk,
),
AsyncResetSynchronizer(self.cd_tx, tx_reset_deglitched)
]
# Use a PLL when non-integer divider
else:
txoutclk_pll = S7PLL()
self.comb += txoutclk_pll.reset.eq(tx_reset_deglitched)
self.submodules += txoutclk_pll
txoutclk_pll.register_clkin(txoutclk_bufg, qpll.config["clkin"])
txoutclk_pll.create_clkout(self.cd_tx, self.tx_clk_freq)

# Use provided/shared tx_clk.
# ---------------------------
else:
txoutclk_div = 1
# Use txoutclk_bufg when divider is 1
if txoutclk_div == 1:
self.comb += self.cd_tx.clk.eq(txoutclk_bufg)
assert tx_buffer_enable
self.cd_tx.clk = tx_clk # Override instead of assign to only keep one real clk.
self.specials += AsyncResetSynchronizer(self.cd_tx, tx_reset_deglitched)
# Use a BUFR when integer divider (with BUFR_DIVIDE)
elif txoutclk_div == int(txoutclk_div):
txoutclk_bufr = Signal()
self.specials += [
Instance("BUFR",
p_BUFR_DIVIDE = str(int(txoutclk_div)),
i_CE = 1,
i_I = txoutclk_bufg,
o_O = txoutclk_bufr,
),
Instance("BUFG",
i_I = txoutclk_bufr,
o_O = self.cd_tx.clk,
),
AsyncResetSynchronizer(self.cd_tx, tx_reset_deglitched)
]
# Use a PLL when non-integer divider
else:
txoutclk_pll = S7PLL()
self.comb += txoutclk_pll.reset.eq(tx_reset_deglitched)
self.submodules += txoutclk_pll
txoutclk_pll.register_clkin(txoutclk_bufg, qpll.config["clkin"])
txoutclk_pll.create_clkout(self.cd_tx, self.tx_clk_freq)

# RX clocking ------------------------------------------------------------------------------
rx_reset_deglitched = Signal()
rx_reset_deglitched.attr.add("no_retiming")
self.sync.tx += rx_reset_deglitched.eq(~rx_init.done)
self.cd_rx = ClockDomain()
self.specials += [
Instance("BUFG",
i_I = self.rxoutclk,
o_O = self.cd_rx.clk,
),
AsyncResetSynchronizer(self.cd_rx, rx_reset_deglitched)
]

# Use/generate local rx_clk.
# --------------------------
if rx_clk is None:
self.specials += [
Instance("BUFG",
i_I = self.rxoutclk,
o_O = self.cd_rx.clk,
),
AsyncResetSynchronizer(self.cd_rx, rx_reset_deglitched)
]
# Use provided/shared rx_clk.
# ---------------------------
else:
assert rx_buffer_enable
self.cd_rx.clk = rx_clk # Override instead of assign to only keep one real clk.
self.specials += AsyncResetSynchronizer(self.cd_rx, rx_reset_deglitched)

# TX Datapath and PRBS ---------------------------------------------------------------------
self.tx_prbs = ClockDomainsRenamer("tx")(PRBSTX(data_width, reverse=True))
Expand Down
104 changes: 62 additions & 42 deletions liteiclink/serdes/gtx_7series.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ def __repr__(self):
# GTX ----------------------------------------------------------------------------------------------

class GTX(LiteXModule):
def __init__(self, pll, tx_pads, rx_pads, sys_clk_freq,
def __init__(self, pll, tx_pads, rx_pads, sys_clk_freq, tx_clk=None, rx_clk=None,
data_width = 20,
tx_buffer_enable = False,
rx_buffer_enable = False,
Expand Down Expand Up @@ -965,56 +965,76 @@ def __init__(self, pll, tx_pads, rx_pads, sys_clk_freq,
self.sync += tx_reset_deglitched.eq(~tx_init.done)
self.cd_tx = ClockDomain()

txoutclk_bufg = Signal()
self.specials += Instance("BUFG",
i_I = self.txoutclk,
o_O = txoutclk_bufg,
)
# Use/generate local tx_clk.
# --------------------------
if tx_clk is None:
txoutclk_bufg = Signal()
self.specials += Instance("BUFG",
i_I = self.txoutclk,
o_O = txoutclk_bufg,
)

if not tx_buffer_enable:
txoutclk_div = pll.config["clkin"]/self.tx_clk_freq
if not tx_buffer_enable:
txoutclk_div = pll.config["clkin"]/self.tx_clk_freq
else:
txoutclk_div = 1
# Use txoutclk_bufg when divider is 1
if txoutclk_div == 1:
self.comb += self.cd_tx.clk.eq(txoutclk_bufg)
self.specials += AsyncResetSynchronizer(self.cd_tx, tx_reset_deglitched)
# Use a BUFR when integer divider (with BUFR_DIVIDE)
elif txoutclk_div == int(txoutclk_div):
txoutclk_bufr = Signal()
self.specials += [
Instance("BUFR",
p_BUFR_DIVIDE = str(int(txoutclk_div)),
i_CE = 1,
i_I = txoutclk_bufg,
o_O = txoutclk_bufr,
),
Instance("BUFG",
i_I = txoutclk_bufr,
o_O = self.cd_tx.clk,
),
AsyncResetSynchronizer(self.cd_tx, tx_reset_deglitched)
]
# Use a PLL when non-integer divider
else:
txoutclk_pll = S7PLL()
self.comb += txoutclk_pll.reset.eq(tx_reset_deglitched)
self.submodules += txoutclk_pll
txoutclk_pll.register_clkin(txoutclk_bufg, pll.config["clkin"])
txoutclk_pll.create_clkout(self.cd_tx, self.tx_clk_freq)

# Use provided/shared tx_clk.
# ---------------------------
else:
txoutclk_div = 1
# Use txoutclk_bufg when divider is 1
if txoutclk_div == 1:
self.comb += self.cd_tx.clk.eq(txoutclk_bufg)
assert tx_buffer_enable
self.cd_tx.clk = tx_clk # Override instead of assign to only keep one real clk.
self.specials += AsyncResetSynchronizer(self.cd_tx, tx_reset_deglitched)
# Use a BUFR when integer divider (with BUFR_DIVIDE)
elif txoutclk_div == int(txoutclk_div):
txoutclk_bufr = Signal()
self.specials += [
Instance("BUFR",
p_BUFR_DIVIDE = str(int(txoutclk_div)),
i_CE = 1,
i_I = txoutclk_bufg,
o_O = txoutclk_bufr,
),
Instance("BUFG",
i_I = txoutclk_bufr,
o_O = self.cd_tx.clk,
),
AsyncResetSynchronizer(self.cd_tx, tx_reset_deglitched)
]
# Use a PLL when non-integer divider
else:
txoutclk_pll = S7PLL()
self.comb += txoutclk_pll.reset.eq(tx_reset_deglitched)
self.submodules += txoutclk_pll
txoutclk_pll.register_clkin(txoutclk_bufg, pll.config["clkin"])
txoutclk_pll.create_clkout(self.cd_tx, self.tx_clk_freq)

# RX clocking ------------------------------------------------------------------------------
rx_reset_deglitched = Signal()
rx_reset_deglitched.attr.add("no_retiming")
self.sync.tx += rx_reset_deglitched.eq(~rx_init.done)
self.cd_rx = ClockDomain()
self.specials += [
Instance("BUFG",
i_I = self.rxoutclk,
o_O = self.cd_rx.clk,
),
AsyncResetSynchronizer(self.cd_rx, rx_reset_deglitched)
]

# Use/generate local rx_clk.
# --------------------------
if rx_clk is None:
self.specials += [
Instance("BUFG",
i_I = self.rxoutclk,
o_O = self.cd_rx.clk,
),
AsyncResetSynchronizer(self.cd_rx, rx_reset_deglitched)
]
# Use provided/shared rx_clk.
# ---------------------------
else:
assert rx_buffer_enable
self.cd_rx.clk = rx_clk # Override instead of assign to only keep one real clk.
self.specials += AsyncResetSynchronizer(self.cd_rx, rx_reset_deglitched)

# TX Datapath and PRBS ---------------------------------------------------------------------
self.tx_prbs = ClockDomainsRenamer("tx")(PRBSTX(data_width, reverse=True))
Expand Down
Loading

0 comments on commit e48140d

Please sign in to comment.