RGMII接口调试,十个坑有八个都出在时钟延迟上。前阵子帮同事查一块新板子,现象非常典型:网络能够协商上百兆、千兆,ping网关却只有 30% 左右成功率,抓包全是 RX CRC Error。一开始怀疑 PCB 走线,Layout 反馈 RGMII 信号组已经做了等长控制,串阻也加了;又怀疑 PHY 供电纹波,量了一圈也正常。查了两天才发现,MAC 侧和 PHY 侧的 TX delay 同时被打开了,时钟相对数据多延迟了接近 4ns,正好落在采样窗口边缘。这种问题在 RGMII 接口上几乎每天都能遇到,根源就是 RGMII 这个接口本身的设计机制——它把"时钟和数据如何对齐"这个矛盾从硬件设计转嫁给了时钟延迟配置。这篇文章就把 RGMII 时钟延迟的来龙去脉、MAC/PHY 两侧的配置方法、以及实际排查流程完整拆开讲一遍,适合正在调 RGMII 的嵌入式工程师、做 FPGA+PHY 方案的朋友,以及所有被"时通时不通"折磨过的人。
1. RGMII 的"时钟陷阱"到底在哪儿:从接口本质看问题根源
1.1 RGMII 为什么要把时钟和数据同步关系搞得这么复杂
RGMII(Reduced Gigabit Media Independent Interface)是对 GMII 接口的缩减。GMII 用 8 根数据线加上收发时钟、控制信号,一共 24 根;RGMII 砍到 12 根,数据线从 8 根变 4 根,发送和接收方向各只需要 TXD[3:0] 和 RXD[3:0],外加 TX_CLK、RX_CLK、TX_CTL、RX_CTL。它的诀窍是用了 DDR 双沿采样:TXD[3:0] 在 TX_CLK 的上升沿送低 4 位数据,下降沿送高 4 位,等于把 4 根线当 8 根用。千兆模式下 TX_CLK 频率是 125MHz,由于双沿采样,等效数据吞吐就是 1Gbps。
问题就出在这个双沿采样上。数据是沿着时钟边沿发送的(edge-aligned),也就是说,发送端在时钟上升沿或下降沿的瞬间把数据放到总线上。如果接收端也在这个边沿去采样,采到的很可能就是正处于跳变过程中的电平,结果完全不可预测。标准 RGMII 在定义时就意识到了这个问题,所以引入了"时钟延迟"或"数据延迟"的机制:允许发送端把时钟相对于数据延迟约 2ns,或者让接收端把输入数据延迟约 2ns,让采样沿落在数据电平稳定的区域中央,也就是把"边沿对齐"变成"中心对齐"。
很多人第一次接触这个概念会觉得绕:既然要中心对齐,为什么发射端不直接做成中心对齐?因为 RGMII 要从兼容 GMII 的时序出发,而且 2ns 延迟通过芯片内部 delay 链比通过复杂的时钟管理实现起来便宜得多。理解了这一点,就理解后续所有配置冲突的根源了:延迟只能补在一端,或者 MAC 端、或者 PHY 端,目的是让最终到达接收端采样触发器的时钟沿和数据窗口相对位置正确,而不是两端都补。
1.2 "延迟"到底是谁造成的:三笔账
要让 RGMII 正常工作,需要把三笔延迟账加在一起看。
第一笔是芯片内部 delay。MAC 和 PHY 芯片内部通常都集成了可选的延迟单元,典型值在 1.5ns 到 2ns 之间。这些延迟单元通过寄存器位或设备树属性来控制开关,这也是绝大多数工程师口中的"RGMII delay 配置"。
第二笔是 PCB 走线延迟。电信号在 FR4 板材中的传播速度大约是每英寸 6 英寸/纳秒,反过来说,每英寸走线大约带来 160ps 到 200ps 的延迟。RGMII 信号组要求数据线之间等长、数据线与时钟线之间也要控制长度差。等长控制不是消除延迟,而是让同一组信号到达接收端时"相对位置"保持一致。如果时钟线比数据线短 5 英寸,时钟就会比数据早到约 1ns,这 1ns 偏移会叠加在内部 delay 上。
第三笔是数据有效窗口。千兆 RGMII 的时钟周期是 8ns,DDR 模式下每个半周期只有 4ns。考虑接收端的建立时间和保持时间(通常各需要 0.8ns 到 1.2ns),数据实际有效稳定的窗口大约只有 2ns 左右。要让时钟采样沿稳稳地落在窗口中央,理想情况下时钟沿要相对数据窗口延迟约 2ns。这也是为什么所有 RGMII delay 配置都围绕 2ns 这个数字转。
把三笔账加在一起,真正需要关心的是到达接收端时"时钟沿与数据窗口中心"的偏差。如果 MAC 和 PHY 都往时钟路径上加 2ns,时钟就比数据多走了约 4ns,采样沿跑到了数据窗口外面,轻则偶发 CRC 错误,重则完全不通。如果两端都不加,采样沿贴着数据跳变沿,板子可能今天能通、明天不能通,换个批次芯片又不能通。
1.3 哪些现象属于时钟延迟问题
RGMII 时钟延迟配置出问题,表现通常不是"百分百断网",而是带有一系列特征:
- 完全不通,PHY 的 link 都建立不起来,或者 link 起来但 MAC 侧没有任何收包
- ping 时通时不通,或者 ping 小包正常、ping 大包(超过 MTU 分片)丢包严重
- MAC 统计寄存器里 RX CRC Error、FCS Error、Alignment Error 数量持续上涨
- 百兆正常、千兆异常,或者 10/100M 都正常,一到 1000M 就出问题
- 常温调试没问题,高低温箱里跑几小时开始丢包
如果你的板子出现了上面任何一种情况,时钟延迟配置就是首要怀疑对象。但要注意,RGMII 时钟问题不是唯一会导致这些现象的原因,MDIO 配置异常、PHY 复位时序、电源纹波过大、参考时钟异常、引脚复用错误,都会表现出类似症状。所以在动手调 delay 之前,先把这些基础项确认掉,否则容易白忙一场。
2. 时钟延迟配置的两条路线:硬件层修与软件层补
2.1 硬件层的"物理修法":PCB 设计与端接
先讲硬件层,因为很多工程师一上来就改设备树,忽略了 PCB 层面的问题。RGMII 是源同步接口,设计上要求 TX 方向和 RX 方向的信号组分别做等长控制。具体控制多少,不同厂家的参考设计不太一样,比较常见的规则是组内数据线之间控制在 ±50mil 以内,时钟线与数据线的长度差控制在 ±100mil 左右。
这里有个经验:时钟线的走线延迟不要刻意做得比数据线"长很多"来做相位补偿。有些 Layout 工程师习惯把时钟线做成蛇形线、拉长 1.5 英寸(约 250ps)来补偿,这在 RGMII 上并不推荐。因为芯片内部 delay 的精度和可控性远高于 PCB 走线,PCB 上的蛇形补偿很难精确控制,还会引入额外的串扰和回流问题。正确的做法是:等长控制做好,把精确的 2ns 延迟交给芯片内部的 delay 单元。
串阻也是 RGMII 硬件设计里容易忽略的点。RGMII 通常是 3.3V/2.5V/1.8V 的 CMOS 电平或 HSTL 类电平,驱动器的输出阻抗一般不高,为了减小反射,源端串阻通常取 22Ω 到 33Ω。但要注意,有些 PHY 芯片内部已经做了阻抗匹配,外部再加串阻反而会让信号幅值降低、边沿变缓。拿到新 PHY 时,先看数据手册里的 RGMII 接口输出阻抗描述和参考设计,再决定加不加、加多大。
2.2 MAC 侧的软件修法:设备树 phy-mode 详解
在 Linux 系统里,RGMII 时钟延迟配置首选是通过设备树phy-mode属性来声明。Linux 网络子系统和 PHYLIB 框架定义了几种和 RGMII delay 相关的模式:
| phy-mode | 含义 |
|---|---|
rgmii | 不启用任何内部延迟,认为时钟和数据通过外部 PCB 走线自然对齐 |
rgmii-id | 同时启用 TX 和 RX 内部延迟 |
rgmii-txid | 只启用 TX 方向内部延迟 |
rgmii-rxid | 只启用 RX 方向内部延迟 |
这个属性的语义是面向"MAC 与 PHY 之间的整体延迟分配"的,但具体行为由两端共同决定。一部分 SoC 的 MAC 驱动会读取这个模式,去配置 MAC 内部的时钟延迟寄存器;另一部分 SoC 的 MAC 没有内部延迟能力,驱动会把模式传递给 PHY 驱动,由 PHY 驱动去配置 PHY 芯片的 delay 寄存器;还有些情况是 MAC 和 PHY 都具备延迟能力,设备树只是告诉两端"你们谁也别加",或者"某一端加"。
我在实际项目中见过最容易踩的坑就是:设备树写的是rgmii-id,但内核版本较老,MAC 驱动对rgmii-id支持不完整,只配置了 TX 方向的延迟;PHY 驱动看到rgmii-id后又把 PHY 的 TX/RX delay 也开了。这样 TX 方向被双重延迟,RX 方向只靠 PHY 单端延迟,最终表现就是"ping 通但 iperf 打流大量丢包"。所以收到一个开发板的设备树,不要直接抄,先确认 MAC 驱动源码对四种phy-mode的处理分支,再确认 PHY 驱动对PHY_INTERFACE_MODE_RGMII_ID的处理逻辑。
除了phy-mode,部分 SoC 还支持更精细的延迟量控制。比如 Xilinx Zynq UltraScale+ 的 GEM 驱动支持tx-internal-delay-ps和rx-internal-delay-ps,可以直接指定延迟皮秒数;Rockchip 的 gmac 驱动则用tx_delay/rx_delay十六进制编码,配合 GRF 寄存器实现细微调节。如果你的板子出现"所有组合都试了仍然不稳定",很可能就是固定 2ns 延迟不够精确,需要用到这类细调接口。
2.3 PHY 侧的软件修法:寄存器与 PHY 驱动
如果说 MAC 侧配置还算是"设备树写对就差不多",PHY 侧的 delay 配置才是真正五花八门的地方。不同厂商、不同型号的 PHY 芯片,delay 控制位的位置、默认值、甚至复位后的行为都不一样。更麻烦的是,同一颗 PHY 芯片的不同封装版本或硅版本,delay 行为也可能有差异。
拿几颗常见的 PHY 举例。Marvell 88E1512 主要通过 MMD 扩展寄存器和标准寄存器配合控制 RGMII delay;Realtek RTL8211F 的 RGMII TX/RX delay 控制在寄存器 0x1C 的特定 bit;TI DP83867 则有自己的延迟控制寄存器,支持以更细的步进调节延迟量,并且可以通过 strap 引脚在复位时锁定初始配置。这三颗 PHY 的 delay 寄存器位完全不同,没有任何通用写法,只能在各自的数据手册里找 RGMII 相关章节。
如果你在内核里用的 PHY 驱动比较新,通常 PHY 驱动会在config_init阶段根据phydev->interface(也就是设备树里的phy-mode)自动完成 delay 寄存器配置。比如老牌的 marvell PHY 驱动、realtek 驱动都有类似逻辑。这种情况下,设备树phy-mode写对了,PHY 侧就不用手动操作。
但嵌入式项目里经常遇到一种情况:PHY 不在内核 PHY 驱动的默认支持列表里,或者厂商只给了裸机寄存器操作代码。这时候就需要在板级代码里手动配置 PHY。我的建议是:优先用 Linux 的 mdio-tools 在用户态验证寄存器值,确认哪个 bit 能解决问题,再写进驱动。直接用 devmem 或者 ioctl 硬怼 PHY 寄存器,容易一次写错、来回烧录调试,效率很低。
2.4 推荐组合与选型时的"坑前预警"
那么在 MAC 和 PHY 之间,delay 到底应该放在哪一边?我的经验是首版验证时优先"PHY 侧开 delay、MAC 侧关 delay"。原因有两点:一是大部分 PHY 芯片的 RGMII delay 默认值就是为 2ns 设计的,和协议推荐值最接近;二是很多 SoC MAC 的内部 delay 实际延迟量受 PVT(工艺、电压、温度)影响较大,稳定性往往不如 PHY 内部的 delay 单元。
如果把所有可能组合列出来的话,大概是这样:
| 方案 | MAC TX delay | PHY TX delay | 效果 |
|---|---|---|---|
| 方案 A | 关 | 开 | 推荐,PHY 的 delay 精度通常更好 |
| 方案 B | 开 | 关 | 也可行,但需确认 MAC 内部 delay 实际值 |
| 方案 C | 开 | 开 | 双重延迟,时钟相对数据偏移约 4ns,大概率异常 |
| 方案 D | 关 | 关 | 完全依赖 PCB 走线,只对极短走线可行,不推荐 |
另外,选 PHY 的时候建议看看它的 delay 默认值和可配置范围。有些低成本 PHY 只支持"开/关"固定的 2ns 延迟,不具备细调能力,在 PCB layout 不太理想的情况下会很被动。预算允许的情况下,优先选支持延迟细调的 PHY,比如 DP83867 这类,调试余量会大很多。
3. 配置实战:从规格书到设备树的全流程
3.1 第一步:把 PHY 规格书里的 RGMII 章节读透
拿到一颗新 PHY,配置 delay 之前先花半小时读数据手册,重点找这几个关键词:RGMII、TX delay、RX delay、Internal delay、Clock skew、RGMII timing。一般数据手册会有一个 RGMII 接口章节,里面有两类信息最关键。
第一类是功能描述,说明这颗 PHY 的 RGMII delay 由哪些 bit 控制、上电默认值是多少、是 strap 引脚决定还是寄存器决定。第二类是时序参数表,里面有setup time、hold time、clock to data skew这类数值。这两个信息决定了你在设备树里写rgmii-id还是rgmii-txid,决定了要不要在板级代码里额外写 PHY 寄存器。
注意一个容易踩的细节:同一颗 PHY 芯片,可能既有"RGMII with internal delay"的版本,也有"RGMII without internal delay"的版本,后缀不一样配置方式就完全不一样。比如有些 PHY 型号带后缀-Z或不带后缀,其内部 delay 能力就不同。不要只看主型号,要连后缀一起对清楚。
3.2 第二步:配置 MAC 端(设备树 + 驱动确认)
以 Zynq UltraScale+ 的 GEM + Marvell 88E1512 为例。设备树里这样写:
&gem0 { status = "okay"; phy-mode = "rgmii-id"; phy-handle = <ðernet_phy0>; mdio { #address-cells = <1>; #size-cells = <0>; ethernet_phy0: ethernet-phy@7 { reg = <7>; device_type = "ethernet-phy"; }; }; };这里phy-mode = "rgmii-id"会让 GEM 驱动去配置 MAC 内部的 TX/RX delay。不过新版本内核和 Xilinx SDK 的驱动行为不完全一样,有的驱动里rgmii-id只作为"告诉 PHY 驱动去配 delay"的信号,MAC 侧不动;有的驱动里则需要额外写tx-internal-delay-ps和rx-internal-delay-ps来明确延迟量。我在调试 ZynqMP 时遇到过一次:设备树只写了rgmii-id,但驱动判断需要tx-internal-delay-ps属性才真正写寄存器,结果就是 MAC 侧 delay 一直没有生效。所以在改完设备树之后,一定要去对应驱动源码里搜rgmii-id和PHY_INTERFACE_MODE_RGMII_ID,确认这个平台具体是怎么处理的。
PHY 地址7则是根据 PHY 的 strap 引脚确定的。PHY 上电时 AD0/AD1/AD2 这些引脚的电平组合决定 MDIO 地址,很多 PHY 默认地址是 0 或 7,但也可能是 1、4、8 等。地址不对的话,MDIO 通信完全不通,设备树改再多 delay 也没用。
3.3 第三步:配置 PHY 端(寄存器操作与驱动验证)
如果 PHY 驱动没有自动处理 delay,或者你想跳过驱动、先确认 PHY 侧的 delay 行为,可以用 mdio-tools 直接在用户态操作。假设 PHY 地址是 7,寄存器 0x1C 里包含 RGMII delay 控制位,可以这样读:
# 读取 PHY 地址 7、寄存器 0x1C mdio eth0 phy 7 0x1c以 Realtek RTL8211F 为例,0x1C 是 RGMII control 寄存器,其中 TX delay 和 RX delay 各由不同的 bit 控制。把对应位置 1 后,再回读确认写入成功:
# 假设原值是 0x40,现在读出来看 bit 是否变化 mdio eth0 phy 7 0x1c 0x50在开发阶段用这种方式验证 PHY 的 delay 开关对网络稳定性的影响,比反复编译内核快得多。确认具体 bit 和值有效后,再落地到驱动代码里,避免每次都手动敲命令。
如果你用的 PHY 驱动已经是新版内核自带且支持这个 PHY,那么驱动会根据phy-mode自动配置。这时候反而要注意别"双重配置":驱动自动配了一遍,你自己的板级代码又写了一遍,如果两次写反,板子会在莫名其妙的时机变得不稳定。
3.4 第四步:验证配置是否真正生效
配置完成后,至少做三层验证:
第一层是寄存器回读。MAC 侧的 delay 寄存器、PHY 侧的 delay 寄存器都要回读确认,不能只看配置代码执行了没有。很多芯片的寄存器写入是有条件的,比如只有在复位释放后一段时间内可写,或者需要先解锁保护位。回读是最直接的证据。
第二层是功能验证。先 ping 大包,ping -s 1472(超过 1472 就需要分片,能验证大包路径),再到 iperf3 打流,看 UDP 丢包率有没有归零。iperf3 的 UDP 打流在 RGMII 这样的源同步接口上比 ping 敏感得多,丢包率在千兆线速下能到 0,才能说明时钟余量够。
第三层是示波器测量。用 500MHz 以上带宽的示波器,测 MAC 的 TX_CLK 和 TXD[0] 之间的相位关系。如果 delay 生效,时钟沿相对数据跳变沿应该有约 2ns 的偏移;如果测出来几乎为 0ns,说明延迟没有加上。测量 RX 方向时,要在 PHY 的 RX_CLK 和 RXD[0] 上量,而不是在 MAC 发射端量。探头地线要短,否则地环路会引入额外噪声,把本来就小的 2ns 相位差掩盖掉。
4. 线上问题排查全链路:从心跳不通到稳定复现
4.1 第一刀:先排除掉"伪 RGMII 时序问题"
遇到 RGMII 通信故障,第一件事不是查 delay,而是把基础项排除掉。我见过太多人在 delay 配置上折腾几天,最后发现 PHY 的复位脚被 CPU 的 GPIO 拉低了,或者 MDC/MDIO 引脚根本没有做 pinmux 复用。
基础项就这几项:
- MDIO 能不能正常读写 PHY。如果能读出 PHY ID 寄存器,说明管理通道已经通了
- PHY 的 link 状态。ethtool 或 mii-tool 看 speed/duplex 是否正常
- PHY 复位时序。有些 PHY 需要复位脚保持低电平至少 10ms,释放后再等 100ms 才能访问 MDIO
- 参考时钟是否稳定。RGMII 的 TX_CLK 从哪来,是 MAC 输出还是 PHY 提供,频率和幅值对不对
- 引脚复用是否配置了。很多 SoC 的 RGMII 引脚默认是 GPIO,bootloader 没初始化的话,数据根本出不来
这些项目确认完,再往时钟延迟方向排查。跳过这一步的代价,往往是浪费一整天在错误的维度上反复组合配置。
4.2 第二步:用 MAC 统计寄存器和环回测试缩小范围
如果基础项正常,接下来要弄清楚问题出在 TX 方向还是 RX 方向,这决定了你去调rgmii-txid还是rgmii-rxid。
linux 下可以用 ethtool 的统计接口或者直接读 MAC 的寄存器。重点看这几类错误:
- RX CRC Error:接收方向数据采样出错,优先怀疑 RX delay
- TX 方向如果对端一直回包不正常、或者对端看到大量 FCS 错误,那是 TX delay 的问题
- 如果 TX/RX 方向同时有错误,先看时钟整体质量,再考虑是不是两端重复配置
环回测试是定位方向的好工具。多数 PHY 支持内部环回模式(PHY 内部把 TX 数据直接环回给 RX),在 PHY 侧环回能验证 MAC->PHY 这个链路。MAC 侧如果需要,也可以用 internal loopback 模式验证 MAC 自身的 RGMII TX/RX 通路。具体做法是:设置 PHY 的 loopback 寄存器位,然后用 ping 检测。环回通了,说明数据面链路没问题;环回不通,再用示波器量时钟和数据相位,看到底是采样窗口跑偏还是电平问题。
4.3 第三步:寄存器遍历法,快速定位 delay 组合
当你怀疑 delay 配置有问题,但又不确定具体是哪一端没生效时,推荐用"寄存器遍历法":把设备树phy-mode在rgmii、rgmii-id、rgmii-txid、rgmii-rxid之间切换,每种模式跑一次打流测试,对比结果。
这个是排查 delay 问题最高效的方法。通常你会发现:
- 某一种模式下完全不通,另一种模式下丢包率降到 0
- 两种模式都通,但一种模式下 CRC 错误持续增长
我曾经处理过一个 FPGA 平台的问题,设备树写的是rgmii-id,但 FPGA 厂商的 MAC 驱动根本没有实现对这个模式的处理,实际寄存器里 delay 位一直是 0。切换模式后发现所有模式表现一模一样,才意识到 MAC 驱动是个"空壳",真正起作用的只有 PHY 侧的寄存器。后来直接在板级代码里给 PHY 写死 delay 配置,问题才解决。
另一个经典案例就是开头提到的"MAC 和 PHY 同时开 TX delay"。现象是能 link、偶尔通、CRC 错误多,示波器测量发现时钟比数据超前约 4ns,明显是 2+2 的叠加。把 MAC 侧 delay 关掉,只保留 PHY 侧 delay,问题立刻消失。这种双重配置发生的原因,往往是设备树写了rgmii-txid,而 PHY 驱动默认就在config_init里打开了自身 delay,两边都不知道对方做了什么。
4.4 边界情况:千兆、百兆与高低温漂移
RGMII 时钟延迟的敏感度和速率强相关。千兆模式下时钟周期是 8ns,数据窗口只有 2ns 左右,对 delay 极其敏感;百兆模式下时钟周期变成 40ns,数据窗口宽裕得多,2ns 的偏差根本不算什么。所以"百兆正常、千兆不通"是 RGMII delay 配置异常的典型指标。
反过来也有一种情况:千兆勉强能通,百兆反而持续报错。这时候问题往往不在 delay,而在 MAC 或 PHY 对百兆模式下 RGMII 时钟的处理。有些 SoC 在切到百兆时需要改变 TX_CLK 的频率来源或分频系数,配置没跟上就会导致百兆模式时钟异常。查这种问题不要死磕 delay,先把时钟树和 PHY 的 100M speed 配置捋一遍。
高低温变化会让 delay 问题从"隐性"变成"显性"。芯片内部的 delay 单元受温度影响明显,PCB 走线延迟也会随温度轻微变化。常温下采样窗口余量可能还有 500ps,到 85°C 就只剩 100ps 甚至归零,这时就出现高低温测试中的随机丢包。所以验证 RGMII 稳定性,一定要安排高低温循环测试,只做常温验证等于没有验证。
5. 不同硬件组合的特别提醒:FPGA、SoC 与 PHY 的搭配细节
5.1 SoC MAC + 外置 PHY:配置接口千差万别
同样是 SoC 的 MAC 接外置 PHY,不同厂商的 delay 配置接口完全不一样。Zynq 系列用phy-mode加tx-internal-delay-ps/rx-internal-delay-ps;NXP i.MX 系主要靠设备树phy-mode配合 FEC 驱动内部处理;Rockchip 的 gmac 设备树里常见tx_delay = <0x26>这种编码值,需要查 TRM 确认编码和实际延迟量的对应关系;全志的 EMAC 驱动则用allwinner,tx-delay-ps这类厂商私有属性。
从调试角度,我特别想提醒 Rockchip 平台的读者:tx_delay和rx_delay的值不是随便填的,它对应 GRF 寄存器里的一段区域,编码和延迟时间有确定但非线性的关系。有人从某篇博客抄了0x26和0x1b到自己的板子上,结果问题依旧,因为不同 PHY 需要的最佳延迟值根本不一样。拿到平台后,先找到 TRM 中 delay 编码表,再结合示波器测量来选值,不要盲抄。不同 SoC 的设备树属性名各不相同,内核版本升级后还可能出现兼容性变化,最好以源码中的device tree binding文档为准。
5.2 FPGA + PHY:时序约束才是主战场
FPGA 做 MAC 接外置 PHY 时,delay 配置的责任不完全在寄存器,更多在 FPGA 工程的时序约束上。
RGMII 是源同步 DDR 接口,在 FPGA 里使用 IDELAY 或 ODELAY 原语来补偿数据与时钟的关系。比如 Xilinx 7 系列里常用的做法是:RX 方向把 RXD 和 RX_CTL 经过 IDELAY,延迟值通常设到约 2ns,让数据相对 RX_CLK 中心对齐;TX 方向用 OSERDES 的 DDR 输出,再配合 ODELAY 调整输出数据与 TX_CLK 的相位关系。
时序约束上,需要给 RGMII 的输入和输出分别做约束。重点是可用的 Vivado 或 Quartus 参考设计。Vivado 里 Xilinx 官方提供 Zynq-7000 和 7 系列的 RGMII 参考设计,包含完整的约束文件;Intel 有个 AN-477 应用笔记专门讲源同步接口的时序分析,里面就有 RGMII 的set_input_delay/set_output_delay示例。这些资料比自己在网上搜碎片化教程靠谱得多。
FPGA 调试还有一个特殊优势:delay 值可以做成可调的寄存器接口,在上位机里在线调整。我在 FPGA 方案里习惯把 RX delay 做成 32 级可调,调试时通过串口或者 JTAG 实时改延迟值,观察哪种组合下错误计数归零,确定后再固定下来。这个手段在 SoC 方案里很难实现,属于 FPGA 红利。
5.3 电压型、电流型 PHY 与接口电平匹配
热搜里有个词是"电压型和电流型 PHY",这个维度和时钟 delay 是两个独立的问题,但处理不当也会表现为类似的现象。简单说,PHY 的 RGMII 输出驱动器实现方式分两类:一类是电压型推挽输出,输出高低电平由内部电源轨决定,外接串阻和端接相对简单;另一类是电流型输出,内部用电流源驱动,对输出端接网络有明确要求,通常需要在引脚外部加偏置或端接电阻,才能获得标准的摆幅和共模电平。
如果硬件设计师把电流型 PHY 当电压型 PHY 处理,省了端接电阻,RGMII 信号幅度可能只有正常值的 60%,接收端判决器在大部分时间内处于亚稳态区间,表现出来就是随机 CRC 错误、误码、偶发丢包,和时钟 delay 问题非常像。排查手段也很简单:用示波器量 RGMII 引脚波形,看高电平是不是足够高、信号边沿是否在噪声中抖动。如果波形质量很差,先修硬件再调 delay,因为 delay 配得再好,也救不了烂信号。
另一个常见坑是 MAC 和 PHY 的电平域不匹配。有的 SoC RGMII 引脚是 1.8V,PHY 却是 3.3V,中间没有做电平转换,或者只是简单串阻分压。这种情况在长时间运行后特别容易出间歇性故障。选型阶段就要把 MAC 引脚的电平能力和 PHY RGMII 引脚的电平要求对齐,不要指望软件能绕过。
5.4 引脚复用、strap 引脚和参考时钟的隐性坑
最后提醒几个容易被忽略的隐性配置项。
第一是 pinmux。不少 SoC 的 RGMII 信号引脚和 GPIO、CAN、UART 等功能复用。bootloader 里只初始化了核心板需要的外设,没有给 MAC 做 pinmux 配置,或者 pinmux 配置了但电压域没有切换,RGMII 信号就是出不来。这种问题光看设备树 phy-mode 是找不出原因的,要看芯片 reference manual 的 IOMUX 章节。
第二是 PHY 的 strap 引脚。很多 PHY 在上电复位时会锁存 bootstrap 引脚,这些引脚决定了 PHY 的默认寄存器值,包括 delay 是否开启、PHY 地址、接口模式等。软件后续可以通过 MDIO 覆盖这些默认值,但如果 PHY 的 strap 电平设计有问题——比如本该拉高的引脚被错误地拉低——软件无论如何配置 delay,行为都可能和预期相反。遇到"寄存器回读明明设置了,但现象没改善"的情况,去查 strap 引脚的电平。硬件设计时还要注意 strap 引脚不能和 PHY 的其它外设共用一个强上拉/下拉,否则上电顺序稍微一变,PHY 配置就锁存成不同状态。
第三是参考时钟来源。RGMII 的 TX_CLK 通常由 MAC 提供,但有些平台允许配置成由 PHY 反馈或外部时钟输入。设备树里像 Rockchip 的clock_in_out = "input"或"output"就控制这一点。配置不对时,PHY 的 link 可能正常,但数据通路上完全没有时钟,表现为 MAC 发出去的数据对端永远收不到。这种问题很隐蔽,因为 link 状态是好的,排查时会绕很大一圈。
6. 沉淀下来的避坑清单与调试建议
6.1 常见问题速查表
把 RGMII 时钟延迟和周边相关的问题整理成一张表,方便在现场快速对照:
| 现象 | 最常见原因 | 优先处理动作 |
|---|---|---|
| 完全不通、MDIO 也不通 | PHY 复位、电源、strap 地址不对 | 确认 strap、复位时序、MDC/MDIO 上拉 |
| link 正常但 ping 不通 | RGMII delay 配置错误或不匹配 | 切换 phy-mode 四种模式对比 |
| ping 小包通、打流丢包 | 采样窗口余量不足 | 用示波器量相位,微调 delay 值 |
| 大量 RX CRC Error | RX delay 缺失或双重延迟 | 回读两端 delay 寄存器,确认单端配置 |
| 百兆正常、千兆不通 | 千兆时钟窗口敏感,delay 偏移 | 重点检查 TX/RX delay 和信号质量 |
| 高温失效、常温正常 | delay 值余量不足,温度漂移 | 增加 delay 或换支持细调的 PHY |
| CRC 间歇性出现、波形很差 | 电平失配、端接错误、电源纹波 | 先修硬件信号质量,再调 delay |
6.2 项目前期就该做的验证项
RGMII delay 问题大部分可以在项目早期就避免,不需要等到板子回来再排查。原理图阶段就要确认三件事:
第一,PHY 的 RGMII delay 控制方式是 strap、寄存器还是两者都支持,strap 引脚在原理图上是如何接的。第二,MAC 侧是否支持内部 delay,设备树规划用哪个phy-mode。第三,RGMII 信号组电平域是否匹配,端接方式是否符合 PHY 数据手册要求。
Layout 完成后,对照 PHY 厂商的参考设计检查一遍 RGMII 走线:等长是否收敛、参考平面是否完整、有没有跨越分割槽。信号完整性上,RGMII 的时钟线不要走内层换层太多,过孔会引入额外延迟和不连续阻抗。
Bring-up 阶段按顺序来:先调通 MDIO 读 PHY ID,再看 link 状态,然后在最简单配置(比如只有 MAC 和 PHY 短距离直连)下验证 RGMII 通信,最后再接变压器和 RJ45。网络上有一个环节不通过,别为了方便直接跳过,否则后面所有测试结果都不可信。
6.3 现场调试的三条实用经验
最后分享三条我自己的习惯,都是在实际项目里被验证过的。
第一条,调试 RGMII 时维护一张 delay 配置记录表,至少记录四列:MAC 侧配置、PHY 侧配置、PCB 走线长度差、实测结果。每次组合都记录在案,不要靠脑子记。我吃过亏:同一块板子,上午测 A 组合丢包,下午测 B 组合正常,晚上又发现电源噪声影响复现,不记录就会浪费大量时间重复验证。
第二条,修改设备树phy-mode后不要只跑一次 ping 就算验证完成,要跑至少五分钟的 iperf UDP 双向打流。RGMII 的采样余量不足时,错误是概率性的,短时间测试可能完全正常。我习惯用iperf3 -u -b 900M -t 300这类"接近线速"的长时打流来压测,能在短时间内把采样窗口边缘的问题逼出来。
第三条,示波器测量时注意测量点位置。TX 方向在 MAC 引脚附近量,RX 方向在 PHY 引脚附近量,并且用差分探头或短地弹簧探头,别用长接地夹。2ns 的相位差在普通探头下很容易被噪声淹没,量出来的结果会误导调试方向。有条件的话,把示波器的采样率开到 5GS/s 以上,测量时钟上升沿和数据跳变沿的时间差,多测几组取平均,再判断 delay 的方向和大小。
RGMII 时钟延迟配置说难也难,说简单也简单。难在它牵扯 MAC 驱动、PHY 寄存器、设备树、PCB 走线、参考时钟多个维度,任何一个环节不一致,表象都是同一个;简单在只要理解了"延迟只能单端补、目标是采样窗口中心对齐"这个核心原则,再按本文的步骤逐项验证,大部分问题都能在一个小时内定位。下次再遇到"时通时不通"的板子,先别急着怀疑人品,拿起示波器量一下时钟和数据的相位差,多半答案就在那里。