調試一塊ZYNQ板卡的千兆網口時,我遇到了從業以來最“偽裝”的一次Bug。設計上很常規:PS內建GEM(MAC)配置為RGMII接口,通過PL-EMIO把RGMII信號引到PL,再從PL的IO引腳出來,直接連接外部PHY芯片。結果網絡一路不穩定——link忽上忽下,ping通兩三包就斷,抓包全是CRC錯誤,跑數據性能慘不忍睹。我把寄存器翻了個底朝天,甚至懷疑過PHY芯片本身,最後用示波器一量才發現,問題根源是RGMII電平標準給錯了:PL Bank的VCCO和IOSTANDARD都往3.3V上靠,而PHY的RGMII接口工作電平要求是2.5V。這篇文章把現象、排查過程、根因原理和修復方法拆開講,給用ZYNQ做PS-MAC + PL-EMIO網口設計的工程師一個完整參考。
1. 項目背景:為什麼要用PL-EMIO引出RGMII
1.1 方案價值與適用場景
ZYNQ的PS側帶有兩個千兆以太網MAC(GEM),默認最舒服的用法是把RGMII信號直接走PS的MIO固定引腳,在SDK裡簡單配置一下就能跑,不佔PL資源。但MIO引腳是有限的資源,一個項目裡既要QSPI Flash、SD卡、UART,又要兩路網口,MIO很快就擠不下了。更現實的問題是PCB佈局:PHY芯片的位置受到接口方向、連接器位置、散熱等因素限制,MIO固定引腳的走線往往繞不過去。這種時候,把GEM信號通過EMIO引到PL再出去,就成了唯一的靈活方案。
EMIO從架構上說,是PS與PL之間的內部信號橋樑。GEM控制器使能後選擇EMIO模式,RGMII的TX/RX信號就會出現在PL內部。之後你可以選擇把它們直接連到PL的任意IO引腳,也可以在PL內部插入自己的邏輯——比如幀過濾、時間戳處理、隊列調度,甚至自己寫一個GMII轉RGMII的橋接模塊。這種設計特別適合三類項目:一是MIO資源緊張但需要多路網絡的;二是PHY必須放在特定位置、對走線長度有要求;三是需要在MAC和PHY之間做數據面處理的。
不過,這種方案的隱形成本也高。EMIO信號到了PL,就不再享有PS“默認配置正確”的保護。信號完整性、電平標準、時序約束,全都變成開發者自己的責任。很多工程師(包括當年的我)會下意識認為“EMIO引出的信號嘛,接到IO上就行了”,但實際上PL側Bank電壓、IO標準、驅動強度、延遲補償,每一步都可能埋雷。
1.2 具體項目設計與信號連接
我這個項目的網絡結構不復雜:PS的GEM1選擇EMIO模式,在Block Design裡把相關端口連到PL頂層;PL側,RGMII信號分配在BANK 35的多個IO上,直接接到一顆瑞昱RTL8211E千兆PHY。為了保證鏈路時序,原理圖上對RGMII信號做了長度匹配,TXD和TX_CLK的長度差控制在500密爾以內,RX方向也做了類似處理。硬件上還單獨拉了MDIO管理接口、PHY的復位和時鐘,但這些都不是出問題的點。
當時我對電平的理解停留在“RGMII是普通的LVCMOS信號,3.3V接受度廣”這種粗淺層面。加上這個板子的BANK 35上還有幾個復位信號默認用了3.3V,原理圖裡直接就把該BANK的VCCO接到了3.3V。PHY的VDDIO則是2.5V——我查手冊時主要看了Core電源,忽略了IO供電對接口電平的決定作用。這兩個電壓不一致,就成了後續數據異常的根源。
1.3 異常現象的迷惑性
異常表現是典型的“間歇性故障”。板卡啟動後,Linux的eth1接口默認是DOWN的,手動ifconfig up後,PHY的link燈有時能亮,但維持幾秒就滅,反覆幾次後偶爾穩定住。穩定狀態下ping內網閘道,丟包率仍然高得離譜,能回包的幾個也帶著幾百毫秒的延遲。我試過強制100M模式,勉強能link,但數據面依然不乾淨;協商到1000M則幾乎完全不通。
PC端抓包看到的情況更直觀:目標板卡發過來的幀,大量被標記為CRC錯誤,TCP握手反覆重傳,根本建立不起連接。關鍵是,ARP請求偶爾能成功——ARP是廣播幀,即使物理層有少量誤碼,接收端也可能容錯處理。這種“通而不暢”的表現,特別容易讓你把問題往軟件方向引,以為是驅動配置、PHY寄存器、中斷處理出了問題,實際上鏈路層的信號質量已經到了崩潰邊緣。
2. RGMII協議層面:為什麼電平不對會引發數據異常
2.1 DDR雙沿傳輸的苛刻時序
RGMII的全稱是Reduced Gigabit Media Independent Interface,核心設計就是DDR:時鐘的上升沿和下降沿都採樣數據。千兆模式下TX_CLK是125MHz,TXD[3:0]和TX_CTL在一個時鐘週期內傳輸兩拍數據,等效數據率翻倍,這樣4根數據線就能湊夠千兆帶寬。
DDR採樣對信號質量非常敏感。普通單沿傳輸對setup/hold時間的要求相對寬鬆,DDR則要求接收端在時鐘兩個邊沿都能準確捕獲數據。任何一點時序margin被吃掉——不管是電平擺幅不足、邊沿變慢、還是時鐘與數據的skew增大——都會直接體現為採樣錯誤。RGMII v2.0規範引入了內部延遲(ID模式),讓PHY或MAC自己做延遲補償,降低對PCB走線長度匹配的要求,但電平如果不滿足接收端的輸入閾值,延遲補償做得再準也沒用。
這裡需要強調的是,RGMII不能當成普通GPIO來看待。它工作在125MHz的雙沿,對過沖、振鈴、邊沿退化的容忍度遠低於低速控制信號。哪怕電平“電氣上也許算對”,只要邊沿不夠乾淨,一樣會出現錯誤採樣。
2.2 電平標準從來就不是固定的
很多朋友會下意識認為“網口嘛,一串GPIO,3.3V LVCMOS唄”。這是最大的誤解。RGMII只定義了信號功能和時序關係,電氣電平完全取決於PHY芯片的IO供電設計。我整理了一下市面上常見PHY的RGMII電平配置:
| PHY型號 | 常見RGMII電平 | IO供電引腳 |
|---|---|---|
| RTL8211E | 2.5V/3.3V可配 | VDDIO |
| RTL8211F | 2.5V/3.3V可配 | VDDIO |
| KSZ9031 | 1.8V/2.5V/3.3V可配 | IOVDD |
| AR8031 | 2.5V/3.3V可配 | VDDIO |
| Marvell 88E1512 | 1.8V/2.5V/3.3V可配 | VDDIO |
同一顆PHY,接不同的VDDIO,RGMII電平就完全不同。所以在任何設計裡,第一步必須翻開PHY的Datasheet,找到IO供電引腳的推薦電壓,再去配FPGA端的Bank電壓和IOSTANDARD。我踩坑的RTL8211E,VDDIO=2.5V時RGMII接口是2.5V LVCMOS,VDDIO=3.3V時才是3.3V LVCMOS。板卡上PHY的VDDIO接了2.5V,但FPGA對面的Bank卻給了3.3V,兩邊根本不對等。
2.3 電平不匹配如何一步步破壞鏈路
電平不匹配的物理後果可以拆成三層看。
第一層是輸入端閾值不匹配。FPGA以3.3V驅動PHY的2.5V輸入引腳,高電平超出PHY的絕對最大額定值(通常VDDIO+0.3V≈2.8V),PHY內部的保護二極管可能導通,波形頂部被削平,反射分量增加,邊沿出現振鈴。第二層是噪聲容限不足。PHY以2.5V驅動FPGA的3.3V輸入,FPGA的VIH閾值通常在2.0V左右,2.5V高電平勉強能檢測到,但噪聲裕量比正常配置小很多,低速下還能混混,到了125MHz DDR就是誤碼。
第三層最隱蔽:電平不匹配往往伴隨著驅動強度和slew rate設置不合適。3.3V Bank的驅動器輸出阻抗與2.5V負載的特性阻抗匹配關係變差,導致上升沿變慢,數據有效窗口變窄。DDR模式下,時鐘和數據的任何額外skew都會決定採樣是否落在窗口內。這三層效應疊加起來,就是link偶爾能up(自動協商對信號質量要求相對低),但真正傳輸數據時CRC全錯、丟包嚴重、握手失敗。
3. 排查根因:我的電平到底哪裡給錯了
3.1 ZYNQ中EMIO信號的電平由誰決定
要理解這個問題,先要搞清楚一個關鍵概念:當GEM通過EMIO把RGMII信號引到PL時,這些信號在PL內部是普通邏輯信號,本身沒有“電平”一說。真正決定PHY和FPGA之間電氣電平的,是PL側的IO資源配置,具體包括三件事:信號分配到哪個BANK、該BANK的VCCO供電電壓、XDC裡對應引腳的IOSTANDARD約束。
這裡有個硬約束:一個BANK只有一個VCCO電壓,同一BANK內所有使用IO的信號,都必須遵循同一電平標準。如果你的RGMII信號所在的BANK同時還有其他3.3V的信號,而RGMII本身要求2.5V,那就必須把其中一組信號挪到別的BANK,或者統一改成兼容方案。否則,無論XDC裡寫什麼,硬件上這組IO的電平已經被VCCO定死了。
我這次的問題,從架構上就屬於這個硬約束被違反的情況。原理圖上BANK 35的VCCO被其他3.3V控制信號“綁架”,而RGMII信號也分在這個BANK,於是整組IO的電平被拉到3.3V,與PHY的2.5V接口完全錯位。
3.2 我實際的配置錯誤在哪裡
具體來說,BANK 35的VCCO接到3.3V電源軌,XDC裡對RGMII引腳設置的是LVCMOS33,驅動強度默認12mA,slew rate默認。PHY側,RTL8211E的VDDIO接了2.5V。這個組合帶來的直接後果是:FPGA輸出到PHY的TXD/TX_CTL/TX_CLK信號,高電平達3.3V,超過PHY RGMII引腳的2.5V容忍範圍;PHY輸出到FPGA的RXD/RX_CTL/RX_CLK信號,高電平只有2.5V,對3.3V Bank而言margin不足。兩個方向都不健康。
用示波器測量時,我在FPGA引腳上看到TX信號的擺幅是3.3V,而且邊沿帶明顯的過沖,最高跳到3.7V左右;RX信號則只有2.5V擺幅,相對乾淨。一對比就非常清楚:發送方向過驅,接收方向margin不足,數據在兩個方向上都有機會出錯。
3.3 為什麼錯誤配置沒有被工具攔下
這是最讓人鬱悶的地方:整套工具鏈全程綠燈,沒有報任何錯誤。原因是,Vivado的DRC檢查的是“IOSTANDARD與VCCO是否匹配”。你告訴它這個Bank是3.3V,XDC寫LVCMOS33,工具認為完全合理,它不知道也不關心對面PHY需要幾伏。就算你從不寫IOSTANDARD,工具按默認值也能生成比特流,同樣不報警。
換句話說,工具只檢查你告訴它的參數內部是否自洽,不檢查你的參數是否和外部硬件匹配。硬件設計裡這類“默認肯定對”的地方,恰恰是最容易埋雷的。我當時的心理就是“BANK 35之前就用過,2.5V/3.3V都跑過”,項目時間一緊,電平矩陣這種細節就被跳過了。教訓很直接:原理圖審查時,必須逐個BANK、逐個芯片核對IO電平。
4. 完整排查流程:從現象到根因
4.1 第一步:先定位是MAC的問題還是PHY的問題
排查這種數據異常,最忌諱一上來就懷疑協議棧。我做的第一件事,是啟用GEM的內部迴環測試——讓MAC的發送數據在內部直接回環到接收通道,完全繞開PHY。方法是讀寫GEM的配置寄存器,把迴環模式打開,然後用自定義的網絡驅動發送測試幀。
內部迴環測試結果一切正常:幀能發、能收、中斷能觸發、DMA搬運無錯誤。這就排除了PS側MAC配置、DMA描述符、中斷處理、驅動代碼的可能。既然MAC內部是乾淨的,問題就指向PHY和FPGA之間的物理通道。到這一步,我才把注意力完全放到PL引腳到PHY引腳之間的鏈路上。
4.2 第二步:ILA的侷限性
在PL側,我把EMIO引出的RGMII信號接到ILA核裡,觀察TX_CLK、TXD、TX_CTL的跳變。ILA顯示信號都有,波形看起來“有數據在跑”,看不出明顯異常。這裡必須提醒大家:ILA採樣的是FPGA內部的邏輯節點,它看到的是已經被內部邏輯驅動的“0/1”,完全無法反映外部引腳上的真實信號質量。你看到TX_CLK在翻轉,但根本不知道它在外部是3.3V還是2.5V,有沒有過沖,邊沿是快是慢。
所以ILA適合確認“邏輯上信號有沒有來”,不適合判斷“物理上信號好不好”。真正要定位電平問題,必須依靠示波器或邏輯分析儀去物理引腳上測。
4.3 第三步:示波器測量RGMII波形
我用了500MHz帶寬、1GSa/s採樣率的示波器,探頭用彈簧針接地,儘量減小地線電感引入的干擾。測量位置選在FPGA引腳到PHY的串阻前端,分別測了TX_CLK、TXD[0]、TX_CTL、RX_CLK、RXD[0]。
結果非常清晰:
- TX_CLK頻率125MHz,穩定;高電平3.3V
- TXD[0]高電平3.3V,上升沿約1.2ns,過沖超過3.6V,振鈴明顯
- RX_CLK高電平2.5V,邊沿相對乾淨,過沖小
- RXD[0]高電平2.5V,波形正常
兩個方向的電平明顯不一致,信號完整性隱患一目瞭然。再看時序關係,TX_CLK相對於TXD的延遲也有波動,這解釋了為什麼數據有時候能通有時候完全斷——採樣窗口邊緣不穩定,稍微受溫度或噪聲影響就翻車。
4.4 第四步:對照PHY手冊,確認電平要求
翻開RTL8211E的Datasheet,電氣特性表裡寫得很清楚:RGMII接口的IO供電由VDDIO引腳決定,板卡上VDDIO=2.5V,因此RGMII信號電平等級為2.5V LVCMOS,最大輸入電壓不能超過VDDIO+0.3V≈2.8V。我們用3.3V驅動PHY輸入,超出了約0.5V,這在信號完整性上已經是明確的違規操作。
到這裡,根因已經確認。問題不在時序延遲,不在PHY寄存器配置,更不在Linux驅動,而是最基礎的電平標準錯了。
4.5 第五步:修改後驗證
修改方案分兩步:硬件上把BANK 35的VCCO從3.3V改為2.5V,同時把同BANK的幾根3.3V復位信號挪到別的BANK或改成開漏上拉;軟件上把XDC裡的IOSTANDARD統一改為LVCMOS25。重新綜合、生成比特流、上電測試,link穩定,自協商到1000M成功,ping 10000個包零丟包,iperf TCP測速穩定在940Mbps以上,連續跑48小時無掉線。問題徹底解決。
5. 修復方案與具體操作方法
5.1 硬件層面的修改方法
如果你的板子還沒投板,修改的成本最低。設計階段就應該做三件事:第一,查清楚每顆PHY的IO供電引腳(VDDIO/IOVDD)在哪、推薦電壓是多少;第二,把RGMII信號分配到獨立的BANK,不要和其他電平的信號混用;第三,建立一份“BANK電平矩陣”,每個BANK的VCCO、每個芯片的IO電平、每組信號的IOSTANDARD全部列出來,逐項核對。
如果板子已經投了,那就得看具體情況。我這次的情況是BANK 35除了RGMII還有幾根復位信號,改板時把它們挪到另一個3.3V的BANK即可,RGMII信號獨佔BANK 35,VCCO改為2.5V。修改後一定要重新確認PCB上該BANK的電源網絡沒有其他3.3V器件掛在同一個網絡上,否則電壓會被拉高。
5.2 XDC中的電平與IO約束
硬件正確的前提下,XDC約束也要寫對。RGMII引腳的IOSTANDARD必須和Bank的VCCO匹配,同時建議明確設置驅動強度和slew rate。下面是2.5V電平下的一組典型約束:
set_property PACKAGE_PIN AK16 [get_ports {rgmii_txd[0]}] set_property IOSTANDARD LVCMOS25 [get_ports {rgmii_txd[0]}] set_property DRIVE 12 [get_ports {rgmii_txd[0]}] set_property SLEW FAST [get_ports {rgmii_txd[0]}]驅動強度不是越大越好。RGMII這種高速DDR接口,驅動太強會加劇過沖,驅動太弱會讓邊沿變慢。以常見的FPGA Bank為例,12mA配合FAST slew是比較穩妥的起點,實測後如有振鈴可以降到8mA。如果你的PHY支持VOD調整,也可以在PHY寄存器層面配合微調。
5.3 RGMII時序約束示例
電平解決後,時序約束依然不能漏。RGMII是DDR接口,數據在時鐘的雙沿採樣,XDC裡的時序約束必須如實反映外部延遲關係。一個簡化的千兆模式約束如下:
set rgmii_clk [get_clocks -of_objects [get_ports rgmii_tx_clk]] create_generated_clock -name rgmii_txc \ -source [get_pins clkgen_i/clk_out] -divide_by 1 \ [get_ports rgmii_tx_clk] # 輸出延遲約束 set_output_delay -clock $rgmii_clk -max 1.5 \ [get_ports {rgmii_txd[*] rgmii_tx_ctl}] set_output_delay -clock $rgmii_clk -min 0.0 \ [get_ports {rgmii_txd[*] rgmii_tx_ctl}]需要注意的是,DDR接口的setup和hold約束都要設,max對應setup,min對應hold。具體延遲數值要根據PHY的datasheet和PCB走線長度反推。如果你的設計在PL內部用了IDDR/ODDR自己處理RGMII信號,那還需要額外考慮片內延遲和跨時鐘域的時序路徑,這種場景比單純透傳更複雜,務必在綜合後仔細檢查時序報告。
5.4 驗證環節
修復完成後,我建議按以下順序驗證:
- 示波器確認波形:高電平2.5V,過沖小於100mV,邊沿時間正常
- PHY寄存器讀取:通過MDIO讀0x1寄存器,確認link狀態、速度、雙工模式
- ethtool對比:確認自協商結果與PHY寄存器一致
- ping測試:10000個包,零丟包
- 吞吐測試:iperf TCP持續5分鐘以上,千兆環境下穩定在900Mbps以上
- 長時間老化:高溫箱或長時間上電運行,確認沒有溫度敏感性
這些步驟都過了,才說明這個問題真正畫上句號。
6. 常見問題速查與避坑心得
6.1 RGMII異常排查速查表
調試中遇到RGMII數據異常,可以按下面這張表快速對照:
| 現象 | 可能原因 | 排查方法 |
|---|---|---|
| link時有時無 | 電平不匹配、信號質量差 | 示波器測波形,確認擺幅和過沖 |
| link up但ping不通 | PHY配置錯、延遲補償錯 | 讀PHY寄存器,檢查TX/RX延遲 |
| 大量CRC錯誤 | 時序約束缺失、電平不匹配 | 添加XDC時序約束;示波器確認 |
| 只能100M不能1000M | TX/RX延遲配置錯誤 | 檢查RGMII ID模式設置 |
| 大流量才出錯 | 串擾、接地不良 | 檢查PCB阻抗、參考平面、地彈 |
6.2 設計階段的避坑清單
結合這次教訓,我在新項目的硬件設計裡固定加入了以下檢查項,建議大家直接複製到自己的checklist裡:
- 確認每一顆PHY的IO供電電壓(VDDIO/IOVDD),不要只看Core電源
- 為RGMII信號分配獨立的BANK,避免與其他電平的信號混放
- 建立BANK電平矩陣,逐BANK確認VCCO與對接芯片電平一致
- XDC中每個RGMII引腳必須明確IOSTANDARD、DRIVE、SLEW
- PCB走線做長度匹配,或依賴PHY的ID模式並正確配置
- 投板前安排硬件評審,把電平矩陣作為評審必查項
6.3 調試小技巧
最後分享幾個實戰中驗證過的小技巧。
用示波器測量RGMII信號時,一定要用彈簧針接地,不要用鱷魚夾接地線。鱷魚夾地線本身有電感,會讓測到的波形帶上虛假的振鈴和過沖,容易誤判。
如果沒有邏輯分析儀,可以在PL裡加一個簡單的計數器,統計RX_CTL有效幀的數量,再配合PS側讀GEM的統計寄存器,能很快判斷是物理層收到壞幀,還是MAC層就沒有正確接收。
讀PHY狀態寄存器是一切調試的起點。RTL8211E的寄存器0x1能直接顯示link、速度、雙工狀態,讀出來和ethtool輸出做對比,能第一時間確認PHY是否真的完成了自協商。如果PHY寄存器顯示link up但你ping不通,那問題大概率在FPGA和PHY之間的信號質量,而不是協議棧。
我個人在這次調試裡最大的收穫,不是學會了怎麼改電平,而是明白了硬件設計中那些“默認肯定對”的地方最容易埋雷。RGMII電平這個問題聽起來小,但它藏在PHY手冊的某一頁、藏在BANK電壓矩陣的交叉點,平時誰也不會多看一眼,一出問題卻能讓整個網絡功能癱瘓。現在我做任何ZYNQ網絡方案,都會在原理圖階段就把每個BANK的VCCO、PHY的IO電平、XDC的IOSTANDARD三者列成對照表,並在投板前逐項確認。如果你也遇到RGMII數據異常,先別急著懷疑驅動,拿起示波器看看電平,可能幾分鐘就能定位。