1. 问题现场:网口死活不通,数据全是乱码
ZYNQ 平台上用 PS 端 MAC 控制器,通过 PL 侧的 EMIO 把 RGMII 信号引到外部 PHY,结果网口数据异常——要么链路起不来,要么能协商但丢包严重,要么收到的全是错帧。这个场景在 ZYNQ 开发里其实非常典型,尤其是当你第一次尝试把 PS 的千兆网口通过 EMIO 绕到 PL 引脚上时。
我遇到这个问题的时候,第一反应是查约束、查时钟、查 PHY 配置,折腾了大半天,最后发现根因特别朴素:RGMII 的 IO 电平标准给错了。PS 侧 MIO Bank 的电平和 PL 侧 Bank 的电平是两套独立的供电域,EMIO 把信号从 PS 引到 PL 之后,信号的实际电平由 PL Bank 的 VCCO 决定,而不是 PS 侧。如果你在约束文件里给 RGMII 引脚指定的 IOSTANDARD 和实际 Bank 供电电压不匹配,信号幅度就会出问题,轻则时序裕量不够,重则 PHY 根本识别不了。
这篇文章适合正在用 ZYNQ 做网口扩展、或者准备用 EMIO 把 PS 外设引到 PL 的开发者。不管你是刚上手 ZYNQ 的新手,还是做过几个项目但没深究过电平细节的老手,这里面的排查思路和实操方法都能直接拿去用。我会把整个问题的来龙去脉、电平标准的底层逻辑、约束文件的正确写法、以及我踩过的坑全部摊开讲清楚。
2. 先搞清楚 PS-MAC + PL-EMIO 这条链路到底怎么走
2.1 PS 端 MAC 和 EMIO 的关系
ZYNQ 的 PS 端自带两个千兆以太网 MAC 控制器,通常叫 GEM0 和 GEM1。这两个 MAC 的引脚可以走两条路:一条是直接通过 MIO 引到芯片外部引脚,另一条是通过 EMIO 绕到 PL 侧,再从 PL 的 IO 引出去。走 MIO 的时候,引脚的电平标准由 MIO Bank 的供电决定,一般是 1.8V 或 3.3V,这个在 Vivado 的 PS 配置里选好就行,不太容易出错。
但走 EMIO 就不一样了。EMIO 本质上是 PS 和 PL 之间的一条内部信号通道,它把 PS 的 MAC 信号送到 PL 的逻辑资源里,然后你需要把这些信号再分配到 PL 的 IO 引脚上。关键点来了:信号一旦到了 PL 侧,它的电平标准就由 PL Bank 的 VCCO 决定了,跟 PS 侧 MIO 的供电没有直接关系。很多人在这里会想当然地以为“PS 配了 1.8V,那 EMIO 出来的也是 1.8V”,这个理解是错的。
RGMII 接口本身需要的信号包括:TXC、TXD[3:0]、TX_CTL、RXC、RXD[3:0]、RX_CTL,另外还有 MDC、MDIO 用于管理。千兆模式下,TXC 和 RXC 是 125MHz 的时钟,数据在时钟的上下沿都采样,所以实际数据率是 125M × 2 × 4bit = 1000Mbps。这个时序对电平质量和走线延迟非常敏感,电平给错了,时序直接就崩了。
2.2 EMIO 引出后电平由谁决定
这里必须把逻辑理清楚。PS 侧的 MAC 控制器输出的是数字逻辑信号,它在 PS 内部工作。当这些信号通过 EMIO 进入 PL 时,PL 侧接收到的是一组逻辑电平,这组逻辑电平的参考电压取决于 PL Bank 的 VCCO。也就是说,你在 PL 侧约束文件里给这些引脚写的 IOSTANDARD,必须和该 Bank 实际焊接的 VCCO 电压一致。
举个例子,如果你的 PL Bank 供电是 3.3V,但你在 XDC 里给 RGMII 引脚写了IOSTANDARD LVCMOS18,那 Vivado 在实现时就会按 1.8V 的标准去驱动这些引脚。实际输出电压会试图往 1.8V 的 VIH 靠,但 Bank 供电是 3.3V,这会导致输出电平异常——可能高电平不够高,可能低电平不够低,也可能上下冲严重。PHY 那边看到的信号质量一塌糊涂,自然就出问题了。
反过来说,如果 Bank 供电是 1.8V,你写了LVCMOS33,那更危险,因为 3.3V 的驱动标准在 1.8V 供电下根本达不到,输出幅度会严重不足。所以这个匹配关系是硬性的,不能拍脑袋。
2.3 RGMII 电平标准的基本要求
RGMII 规范里,信号电平通常是 1.8V 或 2.5V 的 LVCMOS,具体取决于 PHY 芯片的 VDDIO 供电。常见的千兆 PHY,比如 Realtek RTL8211 系列、Marvell 88E1512 等,它们的 RGMII 接口 VDDIO 可以配成 1.8V、2.5V 或 3.3V,但大多数设计里为了兼顾功耗和时序,会选 1.8V。这时候你的 PL Bank VCCO 也必须是 1.8V,XDC 里写LVCMOS18,三者一致才行。
有些开发板为了兼容多种外设,PL Bank 会分成好几个,每个 Bank 的 VCCO 可以独立供电。你接线的时候一定要确认 RGMII 信号落在哪个 Bank,那个 Bank 的 VCCO 实际接了多少伏。我见过有人把 RGMII 信号分配到 Bank 34,结果 Bank 34 的 VCCO 接的是 3.3V,但 PHY 那边是 1.8V 的 VDDIO,两边电平不匹配,信号直接过不去。
3. 电平标准给错之后,现象为什么这么诡异
3.1 链路协商阶段的异常表现
电平不对的时候,最先出问题的往往是链路协商。PHY 和 MAC 之间通过 RGMII 传数据之前,要先建立链路。如果 TXC 或者 RXC 的时钟信号电平不对,PHY 可能根本检测不到有效的时钟,链路就起不来。你会在ethtool里看到Link detected: no,或者dmesg里反复打印eth0: Link is Down。
还有一种情况是链路能起来,但速率协商错误。比如千兆协商成千兆,但因为电平裕量不够,实际跑起来丢包率极高。你用ping测试,可能前几个包能通,后面就开始大量超时。这种间歇性故障最折磨人,因为你会怀疑是驱动问题、是 PHY 配置问题、是时钟问题,唯独容易忽略电平。
3.2 数据帧错误的典型特征
电平问题导致的数据错误有几个典型特征。第一,错帧率高但并非全错,因为电平不是完全不可用,只是裕量不够,某些比特位偶尔会翻转。第二,错误往往集中在特定数据模式上,比如连续多个 1 或者连续多个 0 的时候更容易出错,因为电平的上升沿和下降沿质量差。第三,温度变化或者电源波动时错误率会明显变化,因为电平裕量本来就小,外界干扰一放大就崩了。
我在调试的时候用 ILA 抓过 RGMII 的 TXD 信号,发现高电平只有 1.2V 左右,而 PHY 的 VIH 最小值是 1.17V(1.8V 供电时),理论上勉强够,但实际上因为过冲和振铃,有效窗口很窄。这种“看起来能用但实际不稳”的状态,就是电平给错的典型后果。
3.3 为什么容易误判为时序问题
RGMII 本身对时序要求很严,尤其是千兆模式下,TXC 和数据的 skew 要控制在很小范围内。所以一旦网口出问题,大家第一反应往往是查时序约束、查 IDDR 配置、查 clock delay。我一开始也是这个思路,在 XDC 里反复调set_input_delay和set_output_delay,结果怎么调都不对。
后来才意识到,时序约束是建立在电平正确的前提下的。如果电平本身就不对,信号的边沿位置和幅度都变了,你再怎么调延迟参数都是在错误的基础上修修补补。所以排查顺序应该是:先确认电平标准和供电匹配,再查时序约束,最后调 PHY 的 delay 参数。这个顺序搞反了,就会浪费大量时间。
4. 一步步排查:从供电到约束的完整检查链
4.1 确认 PL Bank 的实际供电电压
第一步,拿万用表量。不要看原理图上写的,要看实际板子上那个 Bank 的 VCCO 引脚接了多少伏。有些开发板有跳线帽可以选择 Bank 电压,出厂默认可能和原理图标注不一致。我遇到过一块板子,原理图上写 Bank 34 是 1.8V,但跳线帽实际接在 3.3V 档位上,量出来就是 3.3V。
量的时候要注意,VCCO 引脚通常在 BGA 封装的下方,不好直接量。你可以找该 Bank 的任意一个 IO 引脚,配置成输出高电平,然后量那个引脚对地的电压。如果输出高电平接近 VCCO,说明供电正常;如果明显偏低,可能是 Bank 供电有问题。
4.2 核对 XDC 中的 IOSTANDARD 设置
第二步,打开 Vivado 的 XDC 文件,找到 RGMII 相关的引脚约束。每一行set_property IOSTANDARD都要和实际 Bank 供电对照。比如:
set_property IOSTANDARD LVCMOS18 [get_ports {eth_rgmii_txc}] set_property IOSTANDARD LVCMOS18 [get_ports {eth_rgmii_txd[0]}] set_property IOSTANDARD LVCMOS18 [get_ports {eth_rgmii_txd[1]}] set_property IOSTANDARD LVCMOS18 [get_ports {eth_rgmii_txd[2]}] set_property IOSTANDARD LVCMOS18 [get_ports {eth_rgmii_txd[3]}] set_property IOSTANDARD LVCMOS18 [get_ports {eth_rgmii_tx_ctl}] set_property IOSTANDARD LVCMOS18 [get_ports {eth_rgmii_rxc}] set_property IOSTANDARD LVCMOS18 [get_ports {eth_rgmii_rxd[0]}] set_property IOSTANDARD LVCMOS18 [get_ports {eth_rgmii_rxd[1]}] set_property IOSTANDARD LVCMOS18 [get_ports {eth_rgmii_rxd[2]}] set_property IOSTANDARD LVCMOS18 [get_ports {eth_rgmii_rxd[3]}] set_property IOSTANDARD LVCMOS18 [get_ports {eth_rgmii_rx_ctl}]如果 Bank 供电是 1.8V,这些写LVCMOS18是对的。如果供电是 2.5V,要改成LVCMOS25;如果是 3.3V,改成LVCMOS33。注意,RGMII 一般不用LVTTL,虽然LVTTL和LVCMOS33在 3.3V 下电气特性接近,但规范上还是用LVCMOS系列更稳妥。
4.3 检查 PHY 侧的 VDDIO 供电
第三步,确认 PHY 芯片的 RGMII 接口 VDDIO 是多少伏。这个信息在 PHY 的数据手册里,通常有一个专门的引脚叫VDDIO或者DVDDIO。有些 PHY 支持多种电压,通过引脚配置或者寄存器设置来选择。你要确保 PHY 的 VDDIO 和 PL Bank 的 VCCO 一致,否则两边电平标准不同,信号无法正确识别。
比如 PHY 的 VDDIO 是 1.8V,PL Bank 是 3.3V,那 MAC 输出的 3.3V 信号加到 PHY 的 1.8V 输入引脚上,可能超过 PHY 的绝对最大额定值,长期运行会损坏 PHY。反过来,PHY 输出 1.8V 信号给 PL 的 3.3V 输入,PL 可能识别不到高电平,因为 1.8V 达不到 3.3V LVCMOS 的 VIH 最小值(通常是 0.7×VCCO = 2.31V)。
4.4 用 ILA 实测信号质量
第四步,如果条件允许,用 Vivado 的 ILA 抓一下 RGMII 信号。ILA 可以抓 PL 内部的信号,也就是 EMIO 出来之后、到达 IO 引脚之前的信号。这个信号的电平是 PL 内部逻辑电平,通常是 VCCO 域的逻辑值,但你可以通过观察它的跳变和占空比来判断时序是否正常。
不过 ILA 抓的是数字逻辑值,看不到模拟的电平幅度。要看实际电平质量,还是得用示波器。把探头接到 RGMII 的 TXC 或者 TXD 引脚上,看高电平幅度、低电平幅度、上升沿时间、过冲和振铃。如果高电平明显低于 VCCO,或者上升沿很缓,那就是电平标准或者驱动能力的问题。
5. 正确配置 RGMII 电平的完整实操
5.1 Vivado 中 PS 端 GEM 的 EMIO 配置
在 Vivado 的 Block Design 里,双击 ZYNQ Processing System,进入 PS 配置。找到Peripheral I/O Pins或者MIO Configuration,在里面使能 GEM0 或 GEM1。关键一步是选择EMIO而不是MIO。选 EMIO 之后,GEM 的 RGMII 信号会出现在 PS 的 EMIO 接口上,你需要把这些信号引出到 PL 的顶层端口。
具体操作是:在 PS 配置界面里,找到GEM0或者GEM1,把GMII/MII to RGMII的选项勾上,然后把MDIO也配上。然后在EMIO分类里,你会看到GEM0的 RGMII 信号列表,包括GEM0_RGMII_TXD、GEM0_RGMII_TXC等等。这些信号默认是内部信号,你需要把它们 Make External,变成顶层端口。
5.2 顶层端口命名与 XDC 约束编写
Make External 之后,Vivado 会自动生成端口名,比如GEM0_rgmii_txd[0]。你可以改成自己习惯的名字,比如eth_rgmii_txd[0]。改完之后,在 XDC 文件里给这些端口指定引脚位置和 IOSTANDARD。
引脚位置根据你的原理图来,比如 TXC 接在 PL 的某个引脚上,就写set_property PACKAGE_PIN <pin> [get_ports eth_rgmii_txc]。IOSTANDARD 根据 Bank 供电来,1.8V 就写LVCMOS18。这里有个细节:RGMII 的时钟信号建议加上SLEW和DRIVE属性,控制边沿速率和驱动电流。比如:
set_property IOSTANDARD LVCMOS18 [get_ports eth_rgmii_txc] set_property SLEW FAST [get_ports eth_rgmii_txc] set_property DRIVE 8 [get_ports eth_rgmii_txc]SLEW FAST让边沿更陡,适合高速信号;DRIVE 8表示驱动电流 8mA,对于 RGMII 这种 125MHz 的信号,8mA 通常够用,如果走线较长可以加到 12mA。但驱动电流越大,过冲和振铃也越严重,所以要权衡。
5.3 时钟约束与时序收敛
RGMII 的时序约束是另一个关键点。千兆模式下,TXC 是 125MHz,数据在上下沿采样。你需要创建时钟约束:
create_clock -name rgmii_txc -period 8.000 [get_ports eth_rgmii_txc]8ns 周期对应 125MHz。然后设置输出延迟:
set_output_delay -clock rgmii_txc -max 1.0 [get_ports {eth_rgmii_txd[*] eth_rgmii_tx_ctl}] set_output_delay -clock rgmii_txc -min -1.0 [get_ports {eth_rgmii_txd[*] eth_rgmii_tx_ctl}]具体数值要根据 PHY 的建立保持时间要求和 PCB 走线延迟来算。一般 PHY 的数据手册会给出 RGMII 的时序参数,比如建立时间 1.0ns、保持时间 0.5ns 之类的。你要把这些参数和 PCB 延迟一起考虑,算出合适的 output delay。
对于接收方向,RXC 是 PHY 送过来的时钟,你需要用create_clock创建,然后设置set_input_delay。接收方向通常还需要在 PS 端或者 PL 端做 IDDR 采样,把双沿数据转成单沿数据。ZYNQ 的 PS 端 GEM 控制器内部已经处理了 RGMII 的双沿采样,所以你不需要在 PL 里额外写 IDDR,只要把信号正确引到 PS 的 EMIO 接口就行。
5.4 PHY 侧 delay 参数的配合调整
RGMII 有一个经典问题:TXC 和数据之间的 skew 需要控制。PHY 芯片通常内置了 delay 功能,可以通过寄存器配置或者引脚 strapping 来调整。比如 Realtek RTL8211 可以通过寄存器 0x1C 来设置 TX delay 和 RX delay。
如果 MAC 侧输出的 TXC 和数据是对齐的(skew 为 0),那 PHY 侧通常需要开启 RX delay,把 RXC 延迟 2ns 左右,让数据在时钟中间被采样。反过来,如果 MAC 侧已经做了 delay,PHY 侧就不需要再加。这个配合关系要在调试时确认,否则即使电平对了,时序也可能不满足。
我一般会先用示波器看 TXC 和 TXD 的相位关系,确认 MAC 侧输出的 skew 是多少,然后决定 PHY 侧要不要开 delay。如果示波器不方便,也可以通过读 PHY 的寄存器或者用 ILA 抓内部信号来间接判断。
6. 常见问题速查与避坑经验
6.1 电平标准匹配速查表
| PL Bank VCCO | PHY VDDIO | XDC IOSTANDARD | 是否匹配 |
|---|---|---|---|
| 1.8V | 1.8V | LVCMOS18 | 匹配 |
| 2.5V | 2.5V | LVCMOS25 | 匹配 |
| 3.3V | 3.3V | LVCMOS33 | 匹配 |
| 1.8V | 3.3V | LVCMOS18 | 不匹配,PHY 可能识别不到高电平 |
| 3.3V | 1.8V | LVCMOS33 | 不匹配,可能损坏 PHY |
| 2.5V | 1.8V | LVCMOS25 | 不匹配,电平过高 |
| 1.8V | 2.5V | LVCMOS18 | 不匹配,电平偏低 |
这张表建议在调试前先对照一遍,把不匹配的组合排除掉。
6.2 常见问题与排查方法
问题一:链路起不来,ethtool显示 Link Down。
排查顺序:先量 PL Bank VCCO 和 PHY VDDIO 是否一致;再查 XDC 的 IOSTANDARD 是否和 VCCO 匹配;然后查 TXC 是否有 125MHz 时钟输出,用示波器或者频率计量;最后查 PHY 的复位和配置引脚是否正常。
问题二:链路能起来但丢包严重。
排查顺序:先看电平幅度是否足够,用示波器量高电平和低电平;再查时序约束是否收敛,看 Vivado 的 timing report;然后查 PHY 的 delay 配置是否合适;最后查 PCB 走线是否等长,RGMII 的时钟和数据走线偏差要控制在很小范围内。
问题三:接收方向正常但发送方向异常。
排查顺序:先确认 TXC 和 TXD 的相位关系;再查 PHY 的 TX delay 配置;然后查 MAC 侧的 output delay 约束;最后查 PHY 的发送使能信号是否正常。
问题四:温度升高后网口不稳定。
排查顺序:先量电平裕量,看高电平和低电平是否接近阈值;再查电源纹波,VCCO 和 PHY VDDIO 的纹波要控制在几十毫伏以内;然后查散热,PHY 温度过高会导致内部延迟变化;最后考虑降低驱动电流或者调整 slew rate。
6.3 我踩过的坑
第一个坑:以为 PS 配了 1.8V,EMIO 出来就是 1.8V。实际上 EMIO 只是内部信号通道,到了 PL 侧电平由 PL Bank VCCO 决定。这个理解偏差让我在约束文件里写错了 IOSTANDARD,折腾了很久。
第二个坑:只看原理图不看实物。原理图上写 Bank 34 是 1.8V,但板子上跳线帽接在 3.3V,实际量出来是 3.3V。后来养成习惯,每次调试前先拿万用表量一遍关键供电。
第三个坑:时序约束调了半天,结果电平是错的。时序约束是在电平正确的前提下才有意义,电平不对的时候,边沿位置和幅度都变了,调延迟参数是白费功夫。所以排查顺序一定要先电平后时序。
第四个坑:忽略了 PHY 的 delay 配置。MAC 侧和 PHY 侧的 delay 要配合,如果两边都开或者都不开,可能导致采样点落在数据跳变沿上。我一般会先用示波器确认相位关系,再决定 PHY 侧怎么配。
6.4 实操心得
调试 RGMII 的时候,我习惯先做一个最简单的回环测试。把 TXD 和 RXD 短接,TXC 和 RXC 短接,然后让 MAC 发送数据,看能不能收到。这个测试可以排除 PHY 的问题,直接验证 MAC 和 PL 侧的电平、时序是否正确。如果回环能通,说明 MAC 和 PL 侧没问题,问题在 PHY 或者 PCB;如果回环不通,说明 MAC 或者 PL 侧还有问题。
另外,Vivado 的 timing report 一定要看。即使电平对了,如果时序不收敛,网口也会出问题。重点看set_output_delay和set_input_delay的 slack 是否为正,如果为负,说明时序不满足,需要调整约束或者优化逻辑。
还有一点,RGMII 的 PCB 走线要尽量等长,尤其是 TXC 和 TXD 之间、RXC 和 RXD 之间。偏差控制在 5mil 以内比较稳妥。如果走线偏差太大,即使电平对了,时序也会出问题。这个在画板子的时候就要注意,后期调试很难补救。
7. 最后再分享几个实用技巧
如果你用的是 Xilinx 官方的 ZYNQ 开发板,比如 ZC702 或者 ZC706,它们的 PS 端 GEM 通常走 MIO 到板载 PHY,不需要 EMIO。但如果你要扩展第二个网口,或者板载 PHY 坏了想外接一个,那就得走 EMIO。这时候电平匹配就是第一道坎。
对于 PHY 的选择,我建议选那些 VDDIO 支持 1.8V 的型号,因为 1.8V 的电平在 PL Bank 上更容易实现,功耗也低。3.3V 的 RGMII 虽然也能用,但高速信号在 3.3V 下过冲和振铃更严重,时序裕量更小。
还有一个小技巧:在 XDC 里给 RGMII 信号加上IBUF或者OBUF的约束,确保 Vivado 正确推断 IO 缓冲。有时候 Vivado 会自动推断,但显式指定更稳妥。比如:
set_property IOB TRUE [get_ports {eth_rgmii_txd[*]}]IOB TRUE会把寄存器放到 IO 单元里,减少时钟到输出的延迟,对时序收敛有帮助。但这个也要看具体情况,不是所有设计都适合。
最后,如果你在调试过程中发现电平怎么都对不上,不妨检查一下 EMIO 的约束是否正确。有时候 Vivado 会自动给 EMIO 信号分配引脚,如果你没有手动约束,它可能分配到错误的 Bank 上。所以一定要在 XDC 里显式指定每个 RGMII 信号的 PACKAGE_PIN 和 IOSTANDARD,不要依赖自动分配。