
調試一塊ZYNQ板卡的千兆網口時我遇到了從業以來最“偽裝”的一次Bug。設計上很常規PS內建GEMMAC配置為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引出RGMII1.1 方案價值與適用場景ZYNQ的PS側帶有兩個千兆以太網MACGEM默認最舒服的用法是把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是125MHzTXD[3:0]和TX_CTL在一個時鐘週期內傳輸兩拍數據等效數據率翻倍這樣4根數據線就能湊夠千兆帶寬。DDR採樣對信號質量非常敏感。普通單沿傳輸對setup/hold時間的要求相對寬鬆DDR則要求接收端在時鐘兩個邊沿都能準確捕獲數據。任何一點時序margin被吃掉——不管是電平擺幅不足、邊沿變慢、還是時鐘與數據的skew增大——都會直接體現為採樣錯誤。RGMII v2.0規範引入了內部延遲ID模式讓PHY或MAC自己做延遲補償降低對PCB走線長度匹配的要求但電平如果不滿足接收端的輸入閾值延遲補償做得再準也沒用。這裡需要強調的是RGMII不能當成普通GPIO來看待。它工作在125MHz的雙沿對過沖、振鈴、邊沿退化的容忍度遠低於低速控制信號。哪怕電平“電氣上也許算對”只要邊沿不夠乾淨一樣會出現錯誤採樣。2.2 電平標準從來就不是固定的很多朋友會下意識認為“網口嘛一串GPIO3.3V LVCMOS唄”。這是最大的誤解。RGMII只定義了信號功能和時序關係電氣電平完全取決於PHY芯片的IO供電設計。我整理了一下市面上常見PHY的RGMII電平配置PHY型號常見RGMII電平IO供電引腳RTL8211E2.5V/3.3V可配VDDIORTL8211F2.5V/3.3V可配VDDIOKSZ90311.8V/2.5V/3.3V可配IOVDDAR80312.5V/3.3V可配VDDIOMarvell 88E15121.8V/2.5V/3.3V可配VDDIO同一顆PHY接不同的VDDIORGMII電平就完全不同。所以在任何設計裡第一步必須翻開PHY的Datasheet找到IO供電引腳的推薦電壓再去配FPGA端的Bank電壓和IOSTANDARD。我踩坑的RTL8211EVDDIO2.5V時RGMII接口是2.5V LVCMOSVDDIO3.3V時才是3.3V LVCMOS。板卡上PHY的VDDIO接了2.5V但FPGA對面的Bank卻給了3.3V兩邊根本不對等。2.3 電平不匹配如何一步步破壞鏈路電平不匹配的物理後果可以拆成三層看。第一層是輸入端閾值不匹配。FPGA以3.3V驅動PHY的2.5V輸入引腳高電平超出PHY的絕對最大額定值通常VDDIO0.3V≈2.8VPHY內部的保護二極管可能導通波形頂部被削平反射分量增加邊沿出現振鈴。第二層是噪聲容限不足。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驅動強度默認12mAslew 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.3VXDC寫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.3VTXD[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引腳決定板卡上VDDIO2.5V因此RGMII信號電平等級為2.5V LVCMOS最大輸入電壓不能超過VDDIO0.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 35VCCO改為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對應setupmin對應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不能1000MTX/RX延遲配置錯誤檢查RGMII ID模式設置大流量才出錯串擾、接地不良檢查PCB阻抗、參考平面、地彈6.2 設計階段的避坑清單結合這次教訓我在新項目的硬件設計裡固定加入了以下檢查項建議大家直接複製到自己的checklist裡確認每一顆PHY的IO供電電壓VDDIO/IOVDD不要只看Core電源為RGMII信號分配獨立的BANK避免與其他電平的信號混放建立BANK電平矩陣逐BANK確認VCCO與對接芯片電平一致XDC中每個RGMII引腳必須明確IOSTANDARD、DRIVE、SLEWPCB走線做長度匹配或依賴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數據異常先別急著懷疑驅動拿起示波器看看電平可能幾分鐘就能定位。