
搞嵌入式网络通讯的兄弟应该都有过这种被“通讯异常”四个字支配的体验板子开机RGMII0这个口一会儿通一会儿断有时候PHY协商成百兆有时候直接link down运气好ping通了一跑iperf吞吐量却惨不忍睹。我在E2000平台上正是被RGMII0的通讯异常折磨了大半个迭代周期前前后后排查了设备树配置、PHY芯片复位、RGMII延时、甚至PCB走线最后才发现问题出在一个特别不起眼的时序参数上。这篇就把整个E2000 RGMII0通讯异常的定位过程、关键原理和最终修复方案完整记录下来给正在跟嵌入式以太网口较劲的人一个可复用的排查思路。1. 问题现象与排查起点RGMII0“能通但不正常”的三种典型表现1.1 故障复现从启动日志到实际传输的异常先说现象。我这块E2000板卡默认启动后内核能识别到网卡ifconfig -a里也能看到eth0对应RGMII0但状态很不稳定。正常时ethtool eth0能看到Speed: 1000Mb/sDuplex: Full可一旦重启或者在高负载场景下就会随机出现下面几种情况ethtool显示Speed: 100Mb/s甚至Duplex: Half。ip link show eth0显示state DOWN但PHY interrupt没触发。ping 192.168.1.1连通但延迟抖动特别大从0.3ms跳到几十ms丢包率在2%~8%之间徘徊。如果只是偶尔ping不通大家可能第一反应是网络没配好或者交换机问题。但我们的板子并不仅仅做调试用还要承担业务数据传输RGMII0一旦吞吐量掉到几兆整个系统就不堪重负。这种“能通但不正常”的状态往往比完全不通更让人头疼因为问题不会稳定复现抓不到规律团队内部容易互相甩锅。为了把问题锁定我列了一个初步排查计划先确认是硬件链路问题还是MAC/PHY配置问题再看设备树里RGMII0的模式、复位引脚、时钟分配是否正确最后才对PHY寄存器做细粒度抓取。这个顺序是不能乱的。1.2 先分三层物理层、PHY配置层、MAC驱动层RGMII0这个接口从硬件到软件基本可以拆成三个层次层次涉及对象常见异常表现物理层PCB走线、连接器、网络变压器、PHY晶振速率协商失败、丢包、CRC错误PHY配置层PHY复位、MDIO访问、模式配置、RGMII延时PHY ID读不对、link up但配置无效MAC驱动层设备树、DMA/描述符、时钟、中断能通但吞吐量低、驱动报超时我的经验是RGMII0通讯异常首先要做的是通过dmesg和ethtool -S把现象数字化而不是凭感觉猜。比如dmesg | grep eth0如果出现link down、link up反复循环那大概率是物理层或PHY配置的问题如果日志很干净但吞吐量上不去那MAC侧DMA或者时钟配置的风险就更大。这一步看着基础但很关键。我自己在前期就浪费过两天时间一直在调MAC驱动结果后来用示波器一量发现PHY的复位低电平时间根本不够导致PHY在MDIO写寄存器时经常“部分成功”。所以先给问题分层能少走很多弯路。2. 设备树、时钟与RGMII信号完整性逐项拆解根因候选2.1 E2000 RGMII0的设备树节点到底该怎么配定位到PHY配置层之后我第一件事就是把E2000配套SDK的设备树翻出来逐项核对RGMII0节点。这块板子使用的是标准RGMII接口外接PHY是RTL8211F。设备树关键节点大致长这样具体reg地址以实际SDK为准原理一致rgmii0 { status okay; pinctrl-names default; pinctrl-0 rgmii0_pins; phy-mode rgmii-id; phy-handle phy0; phy-reset-gpios gpio2 7 GPIO_ACTIVE_LOW; phy-reset-duration-us 15000; }; mdio0 { status okay; phy0: ethernet-phy1 { reg 1; compatible ethernet-phy-id; }; };这里最容易被忽略的就是phy-mode。RGMII有一种“苹果味”的坑MAC和PHY之间数据和控制信号是源同步的时钟边沿需要和对应的数据窗口对齐。为了满足时序可以分别让MAC或PHY内部加入发送/接收延时。phy-mode常见有四种rgmii不加延时适合外部PCB走线已经做了严格等长、时钟路径有额外延时的情况。rgmii-idMAC侧和PHY侧一起处理常见做法是PHY内部打开TX和RX delay。rgmii-txid只加发送延时。rgmii-rxid只加接收延时。在我这块板子上PHY的硬件参考设计里已经默认通过strap引脚打开了TX/RX内部延时那么设备树的phy-mode就应该是rgmii-id如果PHY侧没开MAC侧也没有配置内部delay那就要靠外部PCB走线补偿。实际排查时我发现前期同事把phy-mode配成了rgmii导致RGMII0时钟采样沿与数据错位产生大量CRC错误链路虽然能协商上千兆但包几乎全丢。这就是一个典型的配置和硬件不匹配。2.2 时钟树和复位引脚一个很容易被忽略的启动顺序问题除了phy-mode复位时序也是RGMII0的隐形杀手。很多工程师在设备树里加phy-reset-gpios以为把复位引脚接上就完事了。实际上PHY芯片从上电到内部PLL稳定、从而能正确响应MDIO读写需要满足复位低电平的最小时间和复位释放后到稳定可访问的延时。以RTL8211F为例典型参考设计里要求硬复位低电平时间不低于10ms释放后至少需要再等一段时间才能进行MDIO访问。如果这个时间不够PHY可能不会真正进入“ready”状态导致驱动程序在启动阶段读取PHY ID失败或者后续配置PHY寄存器时被中断从而出现随机的链路异常。我们当时用示波器实测PHY reset引脚波形发现复位低电平只有大约50us差了两个数量级多。对照E2000 SDK里的PHY驱动默认的reset延时确实只用了一个很小的值这在大多数情况下可以工作但在部分PHY型号和电源上电顺序配合时就会出问题。后来我把设备树的复位延时调到了15ms并在驱动里加了复位释放后的额外等待问题才稳定消失。这里补充一点如果你用的是PHY芯片自带的上电自动加载strap配置功能那复位信号不只是“复位”它还承担着锁存PHY地址、RGMII延时模式等重要功能。复位时间不够意味着strap引脚状态没有被正确锁存后续MDIO读出来的PHY能力寄存器可能是错的。这也能解释为什么我们读到的PHY ID有时正确、有时错误。2.3 信号完整性与RGMII调校TX delay/RX delay怎么判断软件配置只是一个方面RGMII0是并行同步接口时钟频率125MHz上升沿和下降沿都在传输数据。PCB走线稍微长一点或者延时设置不对都可能让数据眼图闭合。如果手头有示波器可以量一下MAC发出的RGMII_TXC和RGMII_TXD[3:0]之间的关系。正确情况下时钟边沿应当打在数据的中间位置。如果边沿和数据边界贴得很近说明需要调整TX delay接收方向同理要看PHY送给MAC的RXC和RXD的相位关系。没有示波器的话也可以借助PHY的寄存器来粗调。RTL8211F这一类PHY通常提供TXDLY和RXDLY控制位你可以通过mdio命令分别打开或关闭延时然后观察ethtool -S里的CRC错误计数# 读PHY寄存器的扩展页配置 mdio eth0 0x1f 0x0b mdio eth0 0x10 0x0 mdio eth0 0x1f 0x00 mdio eth0 0x14 0x0不过具体寄存器的含义厂商之间不太一样最稳妥的方式还是用设备树的phy-mode来表达延时的组合让对应驱动库去设置PHY。毕竟手改寄存器只能临时验证一旦复位又恢复默认值治标不治本。3. 定位根因一次从“丢包率5%”到“寄存器转储”的排查复盘3.1 复现条件控制为什么固定丢包率比时通时断更好查RGMII0通讯异常最烦人的是偶发性。我这边一开始是“十个包丢一个”随机性很强很难判断是驱动栈的问题还是物理层问题。后来我刻意把测试条件固定下来不经过交换机直接拿一根短网线把E2000开发板的RGMII0口连到PC的千兆网卡上PC的网卡强制千兆全双工然后再从PC向板子打大包。这样复现之后丢包率稳定在3%~5%。虽然还有一定随机性但至少能通过大量统计观察规律。我还在板子上同时开了一个ping -f 192.168.1.10针对PC的IP的持续流量然后观察ifconfig eth0里的RX errors、RX dropped、CRC等计数。实际上这一步我已经开始用数据量化问题。如果错误计数一直在涨说明链路层就有误码如果错误计数不涨但业务丢包那更可能发生在IP层或驱动DMA部分。随后我执行了ethtool -S eth0发现rx_crc_errors这个值高得离谱这就把矛头指向了物理层和PHY配置层而不是协议栈。3.2 抓取MDIO读写日志与PHY状态寄存器排除了协议栈之后下一步就是读PHY寄存器。E2000的MDIO控制器在Linux下可以通过mdio工具直接访问。我写了一个循环脚本每秒钟读取一次PHY的BMCR、BMSR、PHYIDR和状态寄存器同时抓业务流量while true; do date /tmp/phy_reg.log echo BMCR: $(mdio read eth0 0x0) /tmp/phy_reg.log echo BMSR: $(mdio read eth0 0x1) /tmp/phy_reg.log echo PHYIDR1: $(mdio read eth0 0x2) /tmp/phy_reg.log echo PHYIDR2: $(mdio read eth0 0x3) /tmp/phy_reg.log sleep 1 done正常PHY在千兆全双工的BMSR值应该包含link状态位和1000BASE-T能力位。但日志显示链路偶尔会突然从千兆掉到百兆而且PHYIDR低16位的值在某些日志段里读到的是0x0000过一阵又恢复正常。这说明MDIO访问出现了部分失败PHY没能在正确的时间窗口里稳定响应。这时候有个容易踩的坑很多人看到MDIO读返回错误就以为是PHY地址不对反复改设备树的reg值。如果PHY地址不对通常是一次都读不到而我们这个是“大部分能读到、偶发读到全0或错值”更符合复位释放后PHY还没完全ready驱动程序就在初始化阶段踩进去的时序问题。3.3 最终定位PHY复位信号宽度不足有了上面的日志我再回看启动阶段的dmesg发现存在多次mdio_bus: phy1: mdio read timeout或者Reset timeout之类的警告。但由于内核PHY子系统对这类错误有一定容错板子并没有直接崩溃而是以“半配置状态”继续运行等到应用层访问网络时再暴露问题。最终定位是综合了三方面的证据示波器实测PHY复位低电平只有50us左右远低于PHY手册要求的10ms。PHY技术手册里明确写了复位释放后需要额外的稳定时间期间不能访问MDIO。修改设备树的复位延时和复位后等待时间后持续跑48小时都不再出现丢包错误计数也归零。这也是为什么我说这个坑“特别不起眼”它不在业务代码里不在MAC驱动主逻辑里甚至不在PHY驱动核心逻辑里而是在一个从设备树到平台驱动的“初始化时序”里。大多数情况下复位时间短也能碰巧跑起来一旦PHY型号、环境温度、电源纹波稍有变化问题就会以各种随机形式冒出来。4. 修复落地驱动、设备树与硬件协同调整4.1 在设备树里把PHY复位时序改到位找到根因后修复本身并不复杂但细节要做对。我把RGMII0设备树节点的复位参数改了rgmii0 { status okay; pinctrl-names default; pinctrl-0 rgmii0_pins; phy-mode rgmii-id; phy-handle phy0; phy-reset-gpios gpio2 7 GPIO_ACTIVE_LOW; phy-reset-duration-us 15000; phy-reset-post-delay-us 1000; }; mdio0 { status okay; phy0: ethernet-phy1 { reg 1; compatible ethernet-phy-id; }; };其中phy-reset-duration-us代表复位低电平的持续时间我设为15ms保证留足余量phy-reset-post-delay-us代表复位释放后到MDIO访问前的等待时间我额外留了1ms。如果内核PHY框架不支持后面这个参数也可以在PHY驱动的phy_reset回调里添加延时。总而言之要保证PHY有足够时间进入稳定的工作状态。这里有个细节值得多说一句不要为了追求启动速度把这个延时压得太极限。PHY复位时间多一点点代价是毫秒级的启动延迟但要是少一点点换来的可能就是成百上千次的网络异常。工业场景里稳定性远比省这几毫秒重要。4.2 验证网络性能稳定跑满千兆线速修好后我做了三轮验证。第一轮先看基本连通性ping -f -c 10000 192.168.1.10结果非常干净10000个包全是0% loss延迟也稳定在0.2ms左右没有任何抖动尖刺。第二轮用iperf3测吞吐量# 板端作为服务端 iperf3 -s # PC端作为客户端开4个并发流 iperf3 -c 192.168.1.101 -t 60 -P 4结果能跑到940Mbps左右已经接近千兆以太网的实际有效线速CPU占用也正常。第三轮用ethtool -S eth0观察协议栈错误计数连续跑了一天rx_crc_errors、rx_frame_errors、rx_error_bytes全部是0链路协商也稳定在1000Mb/s Full。4.3 高低温测试与长时间稳定性验证嵌入式产品不能只看常温表现。复位时序这类参数对温度和电源比较敏感我特意做了高低温循环测试高温85度、低温-40度每个温度点各跑4小时并在温度变化过程中同步打流量。结果RGMII0一直保持千兆连接没有出现一次link down或CRC错误。在测试过程中我也发现一个小问题如果设备树里把PHY的compatible写得太具体比如直接写某个厂商的型号内核PHY驱动可能因为id匹配失败而回退到通用PHY驱动同样会导致RGMII0的配置不全。正确做法是用ethernet-phy-id这种通用compatible让内核自己去匹配PHY ID或者在mdio节点里指定reset-deassert-us等参数以兼容不同批次的PHY。5. 给你的RGMII0故障排查清单与一些通用经验5.1 一条命令快速看状态的现场救急方法以后再遇到E2000 RGMII0通讯异常别急着改代码。先按顺序执行下面几条命令基本能定位80%的问题# 查看链路协商状态和基本速率 ethtool eth0 # 查看MAC侧统计错误 ethtool -S eth0 # 查看内核日志里PHY相关输出 dmesg | grep -E eth0|mdio|phy # 查看设备树实际生效的phy-mode ls /proc/device-tree/soc/ethernet*/phy-mode | xargs cat如果ethtool显示速率是100Mb/s而不是1000Mb/s基本可以认为是PHY模式配置或者RGMII延时不对如果ethtool -S里CRC错误不断增长优先查时钟相位和信号完整性如果dmesg里有MDIO读写超时优先查PHY复位时序。5.2 硬件设计阶段就该避开的4个坑等到软件排错阶段才发现问题往往已经晚了。在原理图和PCB设计阶段有几个坑我建议尽早规避PHY的复位信号不要和主控的复位直接连在一起最好由GPIO单独控制保证时序独立可调。PHY的时钟源不要随便取MAC侧的输出时钟要看手册里对抖动和相位噪声的要求。RGMII的数据线、时钟线要做到组内等长时钟线尽量不要经过过孔换层换层了要保证回流路径连续。网络变压器中心抽头的电源去耦和共模电感布局要严格遵循参考设计否则EMI会导致千兆协商不稳定。这些看着是小细节实际上我这次问题里如果硬件上复位信号由GPIO控制软件调整会方便很多如果硬件把PHY的strap电阻配置错误后面连软件补救的余地都没有。5.3 复盘这类问题为什么那么多回到本课题E2000 RGMII0通讯异常之所以常见客观原因是RGMII接口本身就是一个高速并行同步接口对时序、延时、复位都有严格约束主观原因是它的初始化链路很长涉及设备树、PHY驱动、时钟框架、MDIO总线、GPIO控制器等多个模块任何一环有偏差都可能表现为“时通时断、吞吐量低、CRC错误”这类模糊现象。我个人复盘后最深的体会是排查这类问题一定要把“看指标”和“看时序”结合起来。先通过统计指标缩小范围再通过示波器和寄存器转储锁定时序最后用改动量最小、语义最清晰的方案去修复。而不是一上来就在驱动代码里加各种hack那样只会让问题更加隐蔽。最后分享一个比较实用的测试技巧在RGMII0 PHY的复位GPIO旁边预留一个测试点板上丝印标清楚方便以后用示波器勾波形。很多PHY异常在明面上看是软件问题实际测一下复位、时钟和数据线的波形两三分钟就能判断出一大半。再配合本文的排查链路RGMII0通讯异常基本都能在一天之内定界。