Skip to content

Commit 6b67a48

Browse files
committed
targets: add SQRL XCU1525 2.5G/10G Ethernet
1 parent ae8c813 commit 6b67a48

12 files changed

Lines changed: 2658 additions & 9 deletions

litex_boards/targets/sqrl_xcu1525.py

Lines changed: 189 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import os
1010

1111
from migen import *
12+
from migen.genlib.cdc import PulseSynchronizer
1213

1314
from litex.gen import *
1415

@@ -23,14 +24,36 @@
2324
from litedram.modules import MT40A512M8
2425
from litedram.phy import usddrphy
2526

26-
from liteeth.phy.usp_gty_1000basex import USP_GTY_1000BASEX
27+
from liteiclink.serdes.gty_ultrascale import GTYQuadPLL
28+
from liteeth.phy.usp_gty_1000basex import USP_GTY_1000BASEX, USP_GTY_2500BASEX
29+
from liteeth.phy.usp_gty_10g_baser import USP_GTY_10G_BASER
30+
from liteeth.phy.xgmii import LiteEthPHYXGMIIPads, LiteEthPHYXGMIITX, LiteEthPHYXGMIIRX
2731

2832
from litepcie.phy.usppciephy import USPPCIEPHY
2933
from litepcie.software import generate_litepcie_software
3034

3135
# QSFP ---------------------------------------------------------------------------------------------
3236

33-
QSFP_PORTS = tuple(f"qsfp{qsfp}_sfp{sfp}" for qsfp in range(2) for sfp in range(4))
37+
QSFP_PORTS = tuple(f"qsfp{qsfp}_sfp{sfp}" for qsfp in range(2) for sfp in range(4))
38+
QSFP_REFCLKS = tuple(f"qsfp{qsfp}_refclk{ref}" for qsfp in range(2) for ref in range(2))
39+
40+
ETHERNET_RATES = ("1g", "2.5g", "10g")
41+
ETHERBONE_RATES = ("1g", "2.5g")
42+
43+
XCU1525_10G_RTL_DIR = os.path.join(os.path.dirname(__file__), "sqrl_xcu1525_10g", "rtl")
44+
XCU1525_10G_RTL_SOURCES = [
45+
"lfsr.v",
46+
"eth_phy_10g_tx_if.v",
47+
"eth_phy_10g_rx_if.v",
48+
"eth_phy_10g_rx_frame_sync.v",
49+
"eth_phy_10g_rx_ber_mon.v",
50+
"eth_phy_10g_rx_watchdog.v",
51+
"xgmii_baser_enc_64.v",
52+
"xgmii_baser_dec_64.v",
53+
"eth_phy_10g_tx.v",
54+
"eth_phy_10g_rx.v",
55+
"eth_phy_10g.v",
56+
]
3457

3558
DDRAM_CHANNELS = tuple(range(4))
3659
DDRAM_CHANNEL_SIZE = 0x40000000
@@ -55,6 +78,27 @@ def parse_qsfp_port(port):
5578
qsfp, sfp = port.replace("qsfp", "").split("_sfp")
5679
return int(qsfp), int(sfp)
5780

81+
def get_qsfp_refclk_name(port, refclk):
82+
if refclk == "auto":
83+
qsfp, _ = parse_qsfp_port(port)
84+
return f"qsfp{qsfp}_refclk0"
85+
if refclk not in QSFP_REFCLKS:
86+
raise ValueError("QSFP refclk must be auto or one of: " + ", ".join(QSFP_REFCLKS))
87+
return refclk
88+
89+
def parse_qsfp_refclk(refclk):
90+
qsfp, refclk_id = refclk.replace("qsfp", "").split("_refclk")
91+
return int(qsfp), int(refclk_id)
92+
93+
def is_156m25(freq):
94+
return abs(freq - 156.25e6) < 1
95+
96+
def get_basex_phy(rate):
97+
return {
98+
"1g" : USP_GTY_1000BASEX,
99+
"2.5g" : USP_GTY_2500BASEX,
100+
}[rate]
101+
58102
def parse_ddram_channels(channels):
59103
if isinstance(channels, int):
60104
parsed_channels = [channels]
@@ -121,6 +165,75 @@ def remap_ddram_origins_around_vexii_io(origins, main_channel):
121165
remapped[channel] = origin
122166
return remapped
123167

168+
# 10GBASE-R PHY ------------------------------------------------------------------------------------
169+
170+
class LiteEthPHYXCU152510G(LiteXModule):
171+
dw = 64
172+
tx_clk_freq = 156.25e6
173+
rx_clk_freq = 156.25e6
174+
integrated_ifg_inserter = True
175+
176+
def __init__(self, platform, refclk, data_pads, sys_clk_freq, refclk_freq=156.25e6):
177+
for filename in XCU1525_10G_RTL_SOURCES:
178+
platform.add_source(os.path.join(XCU1525_10G_RTL_DIR, filename))
179+
180+
self.pll = pll = GTYQuadPLL(refclk, refclk_freq, 10.3125e9)
181+
self.serdes = serdes = USP_GTY_10G_BASER(pll, data_pads=data_pads, sys_clk_freq=sys_clk_freq)
182+
self.cd_eth_tx = serdes.cd_eth_tx
183+
self.cd_eth_rx = serdes.cd_eth_rx
184+
185+
xgmii = LiteEthPHYXGMIIPads()
186+
self.tx = ClockDomainsRenamer("eth_tx")(LiteEthPHYXGMIITX(xgmii, dw=64))
187+
self.rx = ClockDomainsRenamer("eth_rx")(LiteEthPHYXGMIIRX(xgmii, dw=64))
188+
self.sink, self.source = self.tx.sink, self.rx.source
189+
190+
self.tx_bad_block = Signal()
191+
self.rx_error_count = Signal(7)
192+
self.rx_bad_block = Signal()
193+
self.rx_sequence_error = Signal()
194+
self.rx_block_lock = Signal()
195+
self.rx_high_ber = Signal()
196+
self.rx_status = Signal()
197+
self.link_up = Signal()
198+
self.comb += self.link_up.eq(self.rx_status)
199+
200+
rx_reset_req = Signal()
201+
rx_restart = PulseSynchronizer("eth_rx", "sys")
202+
self.submodules += rx_restart
203+
self.comb += [
204+
rx_restart.i.eq(rx_reset_req),
205+
serdes.rx_init.restart.eq(rx_restart.o),
206+
]
207+
208+
self.specials += Instance("eth_phy_10g",
209+
p_DATA_WIDTH = 64,
210+
p_CTRL_WIDTH = 8,
211+
p_HDR_WIDTH = 2,
212+
i_tx_clk = ClockSignal("eth_tx"),
213+
i_tx_rst = ResetSignal("eth_tx"),
214+
i_rx_clk = ClockSignal("eth_rx"),
215+
i_rx_rst = ResetSignal("eth_rx"),
216+
i_xgmii_txd = xgmii.tx_data,
217+
i_xgmii_txc = xgmii.tx_ctl,
218+
o_xgmii_rxd = xgmii.rx_data,
219+
o_xgmii_rxc = xgmii.rx_ctl,
220+
o_serdes_tx_data = serdes.tx_data,
221+
o_serdes_tx_hdr = serdes.tx_header,
222+
i_serdes_rx_data = serdes.rx_data,
223+
i_serdes_rx_hdr = serdes.rx_header,
224+
o_serdes_rx_bitslip = serdes.rx_slip,
225+
o_serdes_rx_reset_req = rx_reset_req,
226+
o_tx_bad_block = self.tx_bad_block,
227+
o_rx_error_count = self.rx_error_count,
228+
o_rx_bad_block = self.rx_bad_block,
229+
o_rx_sequence_error = self.rx_sequence_error,
230+
o_rx_block_lock = self.rx_block_lock,
231+
o_rx_high_ber = self.rx_high_ber,
232+
o_rx_status = self.rx_status,
233+
i_cfg_tx_prbs31_enable = 0,
234+
i_cfg_rx_prbs31_enable = 0,
235+
)
236+
124237
# CRG ----------------------------------------------------------------------------------------------
125238

126239
class _CRG(LiteXModule):
@@ -183,6 +296,10 @@ def __init__(self, sys_clk_freq=125e6, ddram_channel=0,
183296
with_etherbone = False,
184297
ethernet_port = "qsfp0_sfp0",
185298
etherbone_port = "qsfp0_sfp1",
299+
ethernet_rate = "1g",
300+
etherbone_rate = "1g",
301+
ethernet_refclk = "auto",
302+
ethernet_refclk_freq = 156.25e6,
186303
eth_ip = "192.168.1.50",
187304
eth_dynamic_ip = False,
188305
remote_ip = None,
@@ -221,6 +338,10 @@ def __init__(self, sys_clk_freq=125e6, ddram_channel=0,
221338
pcie_address_width = 64
222339
if with_ethernet and with_etherbone and ethernet_port == etherbone_port:
223340
raise ValueError("Ethernet and Etherbone QSFP ports must be different")
341+
if ethernet_rate not in ETHERNET_RATES:
342+
raise ValueError("Ethernet rate must be one of: " + ", ".join(ETHERNET_RATES))
343+
if etherbone_rate not in ETHERBONE_RATES:
344+
raise ValueError("Etherbone rate must be one of: " + ", ".join(ETHERBONE_RATES))
224345

225346
# CRG --------------------------------------------------------------------------------------
226347
with_qsfp = with_ethernet or with_etherbone
@@ -293,27 +414,76 @@ def __init__(self, sys_clk_freq=125e6, ddram_channel=0,
293414
with_dma_monitor = with_pcie_dma_monitor)
294415

295416
# Ethernet / Etherbone ---------------------------------------------------------------------
296-
qsfp_in_use = [False, False]
417+
qsfp_in_use = [False, False]
418+
qsfp_refclk_156m25 = [False, False]
419+
qsfp_refclks = {}
420+
421+
def get_qsfp_external_refclk(refclk_name, refclk_freq):
422+
if refclk_name not in qsfp_refclks:
423+
refclk = Signal()
424+
refclk_pads = platform.request(refclk_name)
425+
self.specials += Instance("IBUFDS_GTE4",
426+
p_REFCLK_HROW_CK_SEL = 0,
427+
i_I = refclk_pads.p,
428+
i_IB = refclk_pads.n,
429+
i_CEB = 0,
430+
o_O = refclk,
431+
)
432+
qsfp_refclks[refclk_name] = refclk
433+
qsfp_id, _ = parse_qsfp_refclk(refclk_name)
434+
qsfp_refclk_156m25[qsfp_id] |= is_156m25(refclk_freq)
435+
return qsfp_refclks[refclk_name]
436+
437+
def get_qsfp_156m25_refclk(qsfp_id):
438+
return get_qsfp_external_refclk(f"qsfp{qsfp_id}_refclk0", 156.25e6)
297439

298440
if with_ethernet:
299441
qsfp_id, sfp_lane = parse_qsfp_port(ethernet_port)
300-
self.ethphy = USP_GTY_1000BASEX(self.crg.cd_eth.clk,
301-
data_pads = platform.request(f"qsfp{qsfp_id}_sfp{sfp_lane}"),
302-
sys_clk_freq = self.clk_freq,
303-
refclk_from_fabric = True)
442+
data_pads = platform.request(f"qsfp{qsfp_id}_sfp{sfp_lane}")
443+
if ethernet_rate == "10g":
444+
refclk_name = get_qsfp_refclk_name(ethernet_port, ethernet_refclk)
445+
refclk = get_qsfp_external_refclk(refclk_name, ethernet_refclk_freq)
446+
self.ethphy = LiteEthPHYXCU152510G(
447+
platform = platform,
448+
refclk = refclk,
449+
data_pads = data_pads,
450+
sys_clk_freq = self.clk_freq,
451+
refclk_freq = ethernet_refclk_freq)
452+
else:
453+
basex_refclk = self.crg.cd_eth.clk
454+
basex_refclk_freq = 200e6
455+
basex_from_fabric = True
456+
if ethernet_rate == "2.5g":
457+
basex_refclk = get_qsfp_156m25_refclk(qsfp_id)
458+
basex_refclk_freq = 156.25e6
459+
basex_from_fabric = False
460+
self.ethphy = get_basex_phy(ethernet_rate)(basex_refclk,
461+
data_pads = data_pads,
462+
sys_clk_freq = self.clk_freq,
463+
refclk_freq = basex_refclk_freq,
464+
refclk_from_fabric = basex_from_fabric)
304465
self.add_ethernet(
305466
phy = self.ethphy,
467+
data_width = 32 if ethernet_rate == "10g" else 8,
306468
local_ip = eth_ip if not eth_dynamic_ip else None,
307469
dynamic_ip = eth_dynamic_ip,
308470
remote_ip = remote_ip)
309471
qsfp_in_use[qsfp_id] = True
310472

311473
if with_etherbone:
312474
qsfp_id, sfp_lane = parse_qsfp_port(etherbone_port)
313-
self.bonephy = USP_GTY_1000BASEX(self.crg.cd_eth.clk,
475+
basex_refclk = self.crg.cd_eth.clk
476+
basex_refclk_freq = 200e6
477+
basex_from_fabric = True
478+
if etherbone_rate == "2.5g":
479+
basex_refclk = get_qsfp_156m25_refclk(qsfp_id)
480+
basex_refclk_freq = 156.25e6
481+
basex_from_fabric = False
482+
self.bonephy = get_basex_phy(etherbone_rate)(basex_refclk,
314483
data_pads = platform.request(f"qsfp{qsfp_id}_sfp{sfp_lane}"),
315484
sys_clk_freq = self.clk_freq,
316-
refclk_from_fabric = True)
485+
refclk_freq = basex_refclk_freq,
486+
refclk_from_fabric = basex_from_fabric)
317487
self.add_etherbone(phy=self.bonephy, ip_address=etherbone_ip)
318488
qsfp_in_use[qsfp_id] = True
319489

@@ -369,6 +539,8 @@ def __init__(self, sys_clk_freq=125e6, ddram_channel=0,
369539
resetl.eq(~(ResetSignal("sys") | (reset_count != 0))),
370540
lpmode.eq(0),
371541
]
542+
if qsfp_refclk_156m25[qsfp_id]:
543+
self.comb += platform.request(f"qsfp{qsfp_id}_fs").eq(0b01)
372544

373545
# Leds -------------------------------------------------------------------------------------
374546
if with_led_chaser:
@@ -396,6 +568,10 @@ def main():
396568
parser.add_target_argument("--with-etherbone", action="store_true", help="Enable Etherbone support over QSFP/SFP.")
397569
parser.add_target_argument("--ethernet-port", default="qsfp0_sfp0", choices=QSFP_PORTS, help="Ethernet QSFP/SFP port.")
398570
parser.add_target_argument("--etherbone-port", default="qsfp0_sfp1", choices=QSFP_PORTS, help="Etherbone QSFP/SFP port.")
571+
parser.add_target_argument("--ethernet-rate", default="1g", choices=ETHERNET_RATES, help="Ethernet line rate.")
572+
parser.add_target_argument("--etherbone-rate", default="1g", choices=ETHERBONE_RATES, help="Etherbone line rate.")
573+
parser.add_target_argument("--ethernet-refclk", default="auto", choices=("auto",) + QSFP_REFCLKS, help="QSFP refclk for 10G Ethernet.")
574+
parser.add_target_argument("--ethernet-refclk-freq", default=156.25e6, type=float, help="QSFP refclk frequency for 10G Ethernet.")
399575
parser.add_target_argument("--ethernet-ip", default="192.168.1.50", help="Ethernet IP address.")
400576
parser.add_target_argument("--remote-ip", default="192.168.1.100", help="Remote IP address of TFTP server.")
401577
parser.add_target_argument("--eth-dynamic-ip", action="store_true", help="Enable dynamic Ethernet IP assignment.")
@@ -444,6 +620,10 @@ def main():
444620
with_etherbone = args.with_etherbone,
445621
ethernet_port = args.ethernet_port,
446622
etherbone_port = args.etherbone_port,
623+
ethernet_rate = args.ethernet_rate,
624+
etherbone_rate = args.etherbone_rate,
625+
ethernet_refclk = args.ethernet_refclk,
626+
ethernet_refclk_freq = args.ethernet_refclk_freq,
447627
eth_ip = args.ethernet_ip,
448628
eth_dynamic_ip = args.eth_dynamic_ip,
449629
remote_ip = args.remote_ip,

0 commit comments

Comments
 (0)