
1. 项目背景ZYNQ 用 PS-MAC PL-EMIO 引出网口踩坑开场最近做一块自研的 ZYNQ 板卡需要把千兆网口引到板边。按常规思路ZYNQ-7000 的 PS 侧自带 GEM千兆以太网 MAC也叫 PS-MACGEM 的 RGMII 信号可以走 MIO 固定引脚也可以走 EMIO 引到 PL 动态布线。MIO 引脚位置是固定的和我的板卡布局冲突所以我选择了 GEM0 的 EMIO 通路把 RGMII 总线引到 PL Bank再从 PL 引脚接到外部 PHY 芯片。刚开始评估这个方案的时候我典型地只关心了“逻辑层面能不能通”PS 里使能 GEM0接口类型选 RGMII模式选 EMIOVivado 里把 pl_eth_gem0 相关的端口引出来分配好 Package Pin生成 bitstream 下载连着调试串口和 PHY 一起跑起来。第一次上电网口 link 能起来PHY 的协商结果也能读到 1000Mbps Full当时还挺开心以为这事就这么过了。结果真正开始跑数据就出问题了。先是 ping 大包不稳定ping -s 1472 能通几包然后开始丢接着 iperf 测吞吐量惨不忍睹综合结果只有几十到一百多 Mbps而且还伴随网卡 FIFO overrun 和 CRC error 报错。一开始我怀疑是驱动参数没调好或者中断配置有问题甚至怀疑 Vivado 里约束没给对导致布线延迟不满足建立保持时间。把这几个方向挨个排查过之后基本都能排除。后来在示波器上把所有 RGMII 关键信号一遍采样下来才看清楚问题不在时序而在最容易被忽略的物理层我那块的 PHY 芯片 RGMII 电源域是 2.5VZYNQ 侧 PL Bank 的 VCCO 却是 3.3VXDC 里又统一写成了 LVCMOS33。这个“RGMII 电平给错了”的配置直接让 RX 方向的数据采样判决裕量不足在 125MHz 翻转频率下产生误码最终表现为数据异常、丢包、带宽上不去。这篇文章把完整链路拆开讲一遍重点说清楚几个问题PS-MAC PL-EMIO 的 RGMII 通路到底长什么样为什么 RGMII 电平没给对会从“能协商通过”变成“数据全是错”遇到这种问题怎么快速定位到电平层面而不是在软件里瞎调以及 XDC、原理图、PHY 电源域之间到底该怎么对齐。内容主要面向正在做 ZYNQ 网口、或者准备用 EMIO 外接千兆 PHY 的嵌入式工程师也包括刚接触这块还没踩过完整坑的 FPGA 开发者。2. 原理拆解RGMII 电平出错为什么会导致数据异常2.1 RGMII 不是“0 和 1 就行”的接口很多人一说到数字接口觉得只要电平逻辑没错数据怎么都能传。RGMII 恰恰是个反面教材。RGMII 是源同步接口数据线、控制线、时钟线之间是严格的建立保持时间关系而且数据在时钟的上下沿都采样等效数据速率是 125MHz 时钟下的 250Mbps 每线。TXD[3:0]、TX_CTL 跟着 GTX_CLK 的上下沿同时变化接收端在 IDDR 模式下把上下沿数据分别采出来再拼回完整字节。这种 DDR 双沿采样对信号质量的要求比普通单沿接口高得多。如果发送端输出的高电平静态值不够高接收端采到的高电平就可能在 VIH 阈值附近抖动如果输出低电平不够低也可能在 VIL 阈值附近来回穿哪怕只发生一次错误采样整个包就废了最后在 MAC 或者 PHY 里报 CRC error、alignment error用户侧看到的就是丢包和带宽暴跌。所以 RGMII 的电平不是“差不多就行”而是必须保证足够的噪声容限和干净的边沿。RGMII 的电平标准常见有三种LVCMOS18、LVCMOS25、LVCMOS33。每种标准的输出高电平最低值、输入高电平阈值、输入低电平阈值都不一样。以 LVCMOS33 为例输出高电平 VOH 一般要求不低于 2.4V输入高电平阈值 VIH 最低约 2.0V而 LVCMOS18 的输入高电平阈值大约只有 1.17V 左右。两边如果一个是 LVCMOS25 的 PHY一个是 LVCMOS33 的 MAC高压侧输入对低压侧输出虽然可能“勉强能判”但噪声容限只有几百毫伏跑低速还行跑 RGMII 的 125MHz 就是灾难。2.2 EMIO 引出后电气标准由 PL 引脚 Bank 决定ZYNQ-7000 的 PS 内部以太网 MACGEM本来是一个硬核模块逻辑上非常稳定。但当你把 GEM 的 RGMII 信号从 EMIO 引出来物理引脚实际上是走 PL 侧的 I/O Bank。也就是说信号在 PS 内部确实是“MAC 输出的数字信号”但到了芯片引脚上它要经过 PL Bank 的 I/O 驱动器和输入缓冲器此时实际电气标准由以下三件事共同决定第一你在 XDC 约束里给这些 EMIO 端口设置的 IOSTANDARD比如 LVCMOS33、LVCMOS25 或者 LVCMOS18。第二这个引脚所在的 PL Bank 的 VCCO 电压是多少。第三PCB 上连接到 ZYNQ 这一组引脚的外部 PHY 芯片它的 RGMII 电源域和收发器的电平标准是多少。这三者只要有一个对不上就会出问题。很多人容易有一个误解PS 的 EMIO 是不是像 PL 内部信号一样只要逻辑值正确就行并不是。EMIO 是跨时钟域、跨逻辑域后再出去的到了 PL 的 I/O 引脚必须按照 FPGA 引脚约束的规则来定义电平。PS 侧内部虽然能生成 RGMII 的数据和时钟但最终驱动到板级的是 PL 的 I/O buffer。所以你不能只在 Vitis 里把 GEM 配对还必须保证 Vivado 工程里的引脚约束和 Bank 电压都是对的。我的这次问题恰恰就是这里错了。2.3 RGMII 电平矩阵与噪声裕量如果只是“电平标准不同但逻辑不变”是不是最多兼容性差一点问题没有那么简单。我整理了一个常见组合的对照表可以直观看出为什么某个方向容易挂组合PL IOSTANDARDPHY 电源域风险ALVCMOS333.3V正常BLVCMOS252.5V正常CLVCMOS181.8V正常常见于 88E1512 之类DLVCMOS332.5VPHY 输出 2.5VMAC 输入阈值约 2.0V噪声容限不足容易错ELVCMOS253.3VPHY 输出 3.3VMAC 接收端承受稍高电压有损坏风险且电平匹配不佳FLVCMOS183.3V过压明显必须避免GLVCMOS331.8V输入高电平可能无法满足 VIHMAC 采不到高电平我这次踩的就是组合 D。PL Bank 被设置成 3.3VXDC 写 LVCMOS33理论上 PL 输入端的 VIH 门槛约 2.0V而 PHY 在 2.5V 电源域下输出的高电平大概只有 2.5V。听起来 2.5V 比 2.0V 高了 0.5V好像没问题但在 125MHz DDR 双沿采样下加上 PCB 走线反射、引脚寄生电容、同时翻转的串扰实际到达输入引脚的波形上沿会掉到 2.1V 甚至 1.9V 附近。这个电平直接处于阈值徘徊区采出来的数据就是随机的。反方向的 TX 路径也不好PL 输出 3.3V而 PHY 的 RGMII 输入工作范围是按 2.5V 设计的虽然很多 PHY 的输入引脚有钳位保护不至于马上烧掉但长时间高电平过驱同样会形成反射反过来恶化信号质量。所以不是只有一端错而是整个 RGMII 电平配置都错得离谱只是因为方向不同表现出的问题严重程度不一样。3. 故障定位实录从“链路通”到“电平错”的排查链路3.1 现场现象与第一反应先记录一下现场的真实表现。板子上电后PHY 的 link 指示灯正常亮串口里看到 Linux 的 eth0 状态是 1000 MbpsFull DuplexAuto-negotiation done。此时用网线把板卡连到 PCPC 端也能识别到千兆链路。但是问题在数据层爆发。我用 ping 做基础连通性测试小包 64 字节还能偶尔通几发换成 ping -s 1472 后丢包率直接上到 80% 以上。再用 iperf3 测试开一个默认 TCP 流吞吐量起不来只有 50Mbps 到 150Mbps 之间跳而且持续跑一会后链路还经常 down 了又 up。与此同时串口终端不断刷出类似 “eth0: Link is Down” 和 “eth0: Link is Up” 的动态。第一反应肯定是软件问题。我把 Vitis 里 GEM 的初始化代码检查了一遍确认了 MDIO 地址读的是 PHY 地址 0PHY ID 寄存器读出来数值正常说明 MDIO 通路没毛病又确认了 GEM 的 DMA 描述符、接收缓冲、中断使能这些配置也没找到明显的逻辑漏洞。把 Linux 内核里的 macb / cadence 驱动参数调来调去关闭 TX checksum offload调整 ring buffer size都没能从根上解决问题。这时候才意识到问题可能不在驱动而在物理层。3.2 反向验证把可疑点收敛到 RX 电平失配既然链路协商是好的软件层也没问题剩下最可疑的就是信号质量和电气参数。先看 ethtool -S 输出的统计计数这是定位方向的关键一步。在现场我执行 ethtool -S eth0重点关注几个计数器rx_errors、rx_crc_errors、rx_align_errors、tx_errors。结果 rx_crc_errors 在持续增长增长速率和 ping 丢包频率基本一致说明接收方向上正在大量丢错包tx_errors 却基本是 0。这个现象非常重要方向性极强。如果是普通软件配置错误往往收发都会异常现在只有 RX 方向错问题大概率出在“PHY 发给 ZYNQ PL 引脚”的接收路径上。沿着这个方向我用示波器去测量 PL Bank 引脚上的 RX 信号。我挂的是 RXD0 数据线、RX_CTL 控制线还有 RX_CLK。在 PHY 侧已经处于持续发送 ARP 请求的状态下抓取波形后发现一个问题RXD0 信号的高电平只有 2.5V 左右而且波形上升沿漂亮的方波后面有明显的过冲和回勾在阈值附近来回停留的时间明显过长。这时候我心里基本有底了。接着去量 ZYNQ 引脚所在 Bank 的 VCCO实测是 3.3V。也就是说ZYNQ 引脚的输入阈值按 3.3V 标准执行但 PHY 送过来的信号只有 2.5V 幅度。我又查了 PHY 芯片手册这款 PHY 的 RGMII 接口 VDDIO 引脚在板子上确实只接了 2.5V。而且我后来还发现原理图上国产这颗 PHY 旁边有预留 3.3V 的磁珠位置结果焊接时被默认贴了 2.5V 的 LDO。这个细节当时没有在评审里暴露出来等板子调试时才知道。3.3 根因确认为了不看错我用两根飞线临时把 PHY 的 VDDIO 电源从 2.5V 强制改到 3.3V并将 PL Bank 对应引脚约束调整到 LVCMOS33当前 Bank 本来就是 3.3V所以不用动。重新生成 bitstream 后简单跑 ping -s 1472 两百次居然一发都没丢。再用 iperf3 测吞吐量几乎满速。这一下就确认了问题根源不是时序问题不是软件驱动问题就是 RGMII 电平配置和 PHY 电源域不匹配。这里给一个非常重要的原则RGMII 的配置不能只看“能不能协商”要承认链路协商层面的 PHY 状态机只是低速控制通道很多 PHY 芯片即使在数据链路电平不匹配的情况下也能正常完成自协商并报 link up。但数据通路一旦跑起来电平问题就会彻底暴露。所以遇到“能协商、不能跑数据”的网口问题优先怀疑电平域和信号完整性而不是一上来就改驱动。4. 正确配置姿势RGMII 电平和 EMIO 管脚的实操指南4.1 RGMII 电平匹配四步法既然问题找到了就得把正确姿势写清楚避免下次再踩。我在后来的项目里总结了一套 RGMII 电平匹配的四步法每次都按这个流程来对。第一步看 PHY 芯片手册确认它的 RGMII 接口电源引脚叫什么。很多 PHY 的 VDDIO 和核心供电是分开的。比如 RTL8211E 系列的 VDDIO 可能是 2.5V 或 3.3V88E1512 则更复杂需要根据 strapping 去选择 1.8V 或 2.5V 工作模式。以实际原理图为准别拿参考设计当圣旨。第二步翻 ZYNQ PL Bank 的电源设计。ZYNQ-7000 的 PL Bank 分成 HR Bank 和 HP BankHR Bank 支持常见的 1.8V/2.5V/3.3VHP Bank 更多是 1.8V 等高效率低压域。RGMII 在 EMIO 模式下通常建议优先选 HR Bank。你必须在原理图中确认这组引脚最终接到哪个 Bank以及该 Bank 的 VCCO 电压是多少这个值必须和 PHY 的 VDDIO 一致。第三步确认 XDC 里面的 IOSTANDARD 是否与 Bank 电压一致。比如 Bank VCCO 是 3.3V那么这组 EMIO 引脚的 IOSTANDARD 应该写 LVCMOS33如果 Bank VCCO 是 2.5V就写 LVCMOS25如果 Bank VCCO 是 1.8V就写 LVCMOS18。注意有些工具在综合时不会强制要求“IOSTANDARD 必须匹配 VCCO”但板级实际电气行为由 VCCO 决定XDC 写错会影响输入阈值模型和仿真千万不要只停留在“工具没报错就算过了”。第四步上电之后用万用表和示波器做一次实物确认。万用表量 ZYNQ Bank VCCO 和 PHY VDDIO两者差值应该控制在 0.1V 以内示波器看 RXD/RX_CTL 的高电平稳态幅度以及 TXD/TX_CTL 的输出幅度。这一步虽然费几分钟但能避免我在这次项目里踩的坑。4.2 XDC 约束示例与注意事项如果你已经在 Vivado Block Design 里使能了 PS GEM0 的 EMIO RGMIIVivado 生成的端口中会有 pl_eth_gem0_rgmii_txd、pl_eth_gem0_rgmii_tx_clk、pl_eth_gem0_rgmii_tx_ctl、pl_eth_gem0_rgmii_rxd、pl_eth_gem0_rgmii_rx_clk、pl_eth_gem0_rgmii_rx_ctl以及 pl_eth_gem0_mdio_mdc、pl_eth_gem0_mdio_mdio_o/i/t 等信号。你需要把这些信号连接到顶层端口或模块端口然后在 XDC 里统一约束。以我用 LVCMOS33 为例约束大概是这样的set_property PACKAGE_PIN Y9 [get_ports txd_0] set_property IOSTANDARD LVCMOS33 [get_ports txd_0] set_property PACKAGE_PIN Y10 [get_ports rxd_0] set_property IOSTANDARD LVCMOS33 [get_ports rxd_0]注意几个细节。第一RGMII 的时钟端口一样要写 IOSTANDARD不能漏。第二这些 EMIO 信号通常是单端的不要误加差分约束RGMII 并不是 LVDS它只是普通单端电平标准。第三XDC 中最好加一个 set_input_delay / set_output_delay 约束来约束源同步时钟关系否则时序收敛可能不稳这也是另一个坑只是它跟本篇的主题不同。更关键的是在 Vivado 工程里检查 Bank 电压配置。你可以在 Vivado 的 Device 窗口看到每个 Bank 的电压也可以在 XDC 里用约束去限定。我这里写下常用约束create_pblock pblock_eth0 add_cells_to_pblock pblock_eth0 [get_cells [list PL_GEM0_INST]] set_property CONFIG_VOLTAGE 3.3 [get_banks 36]这个 CONFIG_VOLTAGE 要和实际 PCB 上该 Bank 接的电压一致否则工具可能会给出 DRC warning。虽然它不是硬性错误但一定要看。4.3 软件初始化里也要盯紧 PHY 配置电平匹配是硬件层面的前提但 RGMII 的数据能不能正常传还取决于 PHY 侧是否工作在正确的 RGMII 模式。很多 PHY 默认是 MII 或 GMII 模式需要通过 strapping 引脚或 MDIO 寄存器把它切到 RGMII。如果 strapping 不对即使电平完全匹配MAC 和 PHY 之间的协议也会对不上。以常见的 PHY 为例初始化代码至少要做三件事第一通过 MDIO 读 PHY ID确认 MDIO 地址正确第二写控制寄存器把 PHY 设为 RGMII 模式第三确认时钟延迟模式。RGMII 有两种延时常用的处理方式TX 时钟延迟和 RX 时钟延迟通常由 PHY 的寄存器或者硬件 strapping 控制。如果你 MAC 侧用的 IDDR 结构需要数据相对时钟有 90 度相移就需要 PHY 配合把时钟或数据延迟调整好。在 Vitis standalone 中GEM 驱动初始化时会调一个函数设置 MDIO 地址然后通过 mdio_read 去读取 PHY 状态。如果这一步读取失败说明 MDIO 或者 PHY 电压域可能都有问题。我就见过有人把 MDIO 的上拉电阻接错接到 1.8V而 PHY 是 3.3V 版本导致 MDIO 读回 0xFFFF进而误认为 PHY 地址错误折腾很久。所以软件初始化这里不是一句“MAC 没问题”就够的还要配合 PHY 手册把模式、延迟、电压域全部确认一遍形成一个完整闭环。5. 问题排查速查与避坑清单5.1 现象-原因对照表调试过程中整理了一个速查表以后遇到类似的网口异常可以先照着对一遍能省很多时间。异常现象常见原因优先排查项能协商千兆但 ping 大包丢包RGMII 电平不匹配、RX 信号噪声容限不足示波器看 RXD/RX_CTL 幅度对比 PHY VDDIO只有 PHY 发往 MAC 方向错包PHY 电源域低于 MAC 输入阈值量 PHY VDDIO 和 PL Bank VCCOTX 正常RX 持续 CRC errorRX 方向电平失配、RX_CLK 延迟不当RX_CLK 与 RXD 相位关系、IOSTANDARDMDIO 读 ID 全 0xFFFFMDIO 上拉电压错误、PHY 地址错误万用表量 MDIO 高电平确认地址板子跑热后开始掉线电平裕量小温漂后触碰阈值提高电平匹配度减小噪声只有 100M 能协商PHY 未切到 RGMII 模式查 PHY strapping 与寄存器高通量应用时 FIFO overrun中断和 DMA 配置不合理附带电平问题ethtool -S 看 tx_fifo_errors5.2 排查清单我把实际踩坑过程中用到的排查步骤整理成了清单顺序基本按照“软件到硬件、从逻辑到物理”的逻辑走但一旦看到方向性问题立刻跳到电平检查不要恋战。第一步确认 Linux 下网卡状态。ifconfig 和 ethtool eth0 看速率、双工、link detected。如果速率模式不对先查 PHY 模式配置。第二步看 ethtool -S统计 RX/TX 错误分布这是判断方向的最快手段。第三步确认 MDIO 能正常读写 PHY 寄存器并核对 PHY 的 RGMII 模式。第四步检查 Vivado XDC 中的 IOSTANDARD 和 PACKAGE_PIN。第五步量板级电压特别是 ZYNQ Bank VCCO 和 PHY VDDIO。第六步示波器看 RGMII 数据线高电平和 RX_CLK/RX_CTL 波形重点关注摆幅和过冲。第七步用 iperf3 双向测试和 ping -s 1472 做最终确认。很多工程师在第一、第二步就能排查很久但我建议如果 RX 方向错误计数异常直接把时间花在第四、第五步上。因为我这次的问题恰恰是软件层怎么配都正常只有硬件电平错。5.3 踩坑后的三条心得踩完这个坑我总结三条最直接的体会。第一条原理图评审阶段就把“电源域一致性”列进检查表。PS-MAC 通过 EMIO 引出 RGMII 时ZYNQ PL Bank 的 VCCO、PHY VDDIO、XDC IOSTANDARD 三者必须写在同一行缺一个不看都算评审不过。第二条上电后先测电平再跑软件别急着把问题归给驱动。RGMII 跑 125MHz DDR对电平的依赖远高于普通控制信号测试优先级应该高于驱动调参。比如 iperf3 第一次测出来只有 80Mbps就应该立刻想到电平裕量而不是去调 dma 的 burst 长度。第三条示波器要抓真实的 RXD/TXD 波形不能只看逻辑分析仪解码。逻辑分析仪解出来是“正确”的帧完全无法暴露 2.5V 高电平和 3.3V 阈值之间的那几百毫伏问题。只有示波器真正看到了波形幅度你才算是把物理层证据拿齐了。6. 修复验证与自用调试脚本6.1 本次修复动作诊断明确后的修复分为硬件和配置两步。硬件上我把 PHY 的 VDDIO 供电从 2.5V 改到 3.3V因为这款 PHY 的数据手册明确支持 3.3V 供电而且 RGMII 接口的电平也随之变成 3.3V。这就让 ZYNQ Bank VCCO、PHY VDDIO、XDC IOSTANDARD 三者完全对齐。由于 ZYNQ PL Bank 本来就是 3.3VXDC 里继续保持 LVCMOS33不需要再改。软件和 FPGA 配置这一侧我只做了几个小调整。一个是在 XDC 里给 RGMII 的数据、控制、时钟信号补上 input/output delay 约束让时序收敛更稳妥一些。另一个是把 PHY 的 RX 时钟延迟寄存器确认了一遍确保 RGMII 源同步采样窗口在中间位置。Vitis 里的驱动代码本身没改动太多还是原来的 DMA 配置、中断配置和 PHY 初始化序列。6.2 验证结果修复之后重新生成 bitstream、烧写、启动系统再做一轮完整的网络验证。先是 ping -s 1472 打两百个包全部通过零丢包。接着 ping -M do -s 8972 测巨型帧 MTU 9000 下的连通性也稳定通过。然后跑 iperf3单向 TCP 吞吐量稳定在 940Mbps 左右千兆网口基本跑满。最后再看 ethtool -Srx_crc_errors 和 rx_align_errors 都不再增长说明数据通路已经干净了。我平时在验证网口时会写一个小脚本把常用项目一次跑完。这里贴出来供参考#!/bin/bash # eth_quick_test.sh 网口快速测试脚本 IFeth0 SERVER_IP192.168.1.10 echo link status ethtool $IF | grep -E Speed|Duplex|Link detected echo ping small packet ping -c 20 -i 0.2 $SERVER_IP echo ping large packet ping -s 1472 -c 100 -i 0.2 $SERVER_IP echo ping Jumbo 8972 ping -M do -s 8972 -c 50 -i 0.2 $SERVER_IP echo iperf3 TCP iperf3 -c $SERVER_IP -t 10 echo ethtool -S before/after ethtool -S $IF | grep -E rx_crc|rx_align|rx_error|tx_error建议测试时先把防火墙关掉避免干扰。另外要特别注意如果 PHY 的供电电压改了要重新确认 MDIO 的上拉电阻位置和电压因为 MDIO 是开漏极信号上拉电阻的一端通常要接到 PHY 的 VDDIO 电源域否则电平也可能不对。6.3 把电平核对写进硬件评审清单这次问题的根源其实可以追溯到硬件设计阶段。如果当时设计人员在原理图上就明确标注“RGMII 接口电平ZYNQ PL Bank VCCO 3.3V PHY VDDIO 3.3V XDC IOSTANDARD LVCMOS33”就不会出现现在的飞线改板。所以我把这个教训沉淀成了新的硬件评审检查项凡是 ZYNQ 通过 EMIO 引出 RGMII必须在评审单上同时给出三个参数硬件设计、FPGA 约束、软件初始化各一条。这三条分别是ZYNQ PL Bank 的 VCCO 接到多少伏PHY 的 VDDIO 接到多少伏XDC 中这组 EMIO 端口定义的 IOSTANDARD 是什么。三者必须一致。当这三条全部对齐之后你才算真正把 RGMII 电平这个隐患从设计源头排掉了。从实际调试经验来说RGMII 的电平问题在低速控制接口上经常表现不明显但一到高速数据通路就原形毕露。所以比起事后抓丢包、调示波器我更建议大家在设计阶段就把电平列成显式的接口协议的一部分。调试到深夜才发现是电源域错了这种事体验过一次就够了。