99import os
1010
1111from migen import *
12+ from migen .genlib .cdc import PulseSynchronizer
1213
1314from litex .gen import *
1415
2324from litedram .modules import MT40A512M8
2425from 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
2832from litepcie .phy .usppciephy import USPPCIEPHY
2933from 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
3558DDRAM_CHANNELS = tuple (range (4 ))
3659DDRAM_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+
58102def 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
126239class _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