前阵子在一款新板卡上调试网络,PHY用的裕太微YT8521SH。最初我的预判是半小时搞定:U-Boot起来以后,设置好IP,ping通PC,然后交给kernel完事。结果现实给了我一巴掌——RGMII时序和LED灯配置两个问题,硬是从下午折腾到了晚上。这篇文章把整个过程记录下来,包括RGMII时序的底层逻辑、U-Boot下读写PHY寄存器的具体操作、YT8521SH的LED配置方法,以及几个很容易踩的坑。如果你也在做嵌入式网络调试,尤其是用国产PHY配RGMII接口,这篇应该能帮你省掉一大半查资料的功夫。
先说清楚,本文不是datasheet的翻译,而是基于实际调试经验的复盘。我手里的平台是一颗ARM SoC加YT8521SH,SoC的MAC侧走RGMII,U-Boot版本是2021.xx,PHY地址通过硬件strap配成了0x0。下面的命令输出和寄存器值都是我当时实打实敲出来的,你可以直接对照着手里的板子操作。
1. 项目背景与问题描述
1.1 YT8521SH是一颗什么样的PHY
YT8521SH是裕太微电子的一款单端口10/100/1000M自适应以太网PHY,支持RGMII和SGMII两种MAC接口,QFN封装,单路千兆PHY。它最大的特点是:国产、便宜、供货相对稳定,所以这两年在各种交换机、工业控制板、核心板上出现频率非常高。
从功能框图来看,这芯片内部集成了物理层编解码、ADC/DAC、均衡器、以及自协商模块。对外提供的接口除了MDIO管理接口,就是RGMII/SGMII的数据通路,另外还有两个LED引脚用于指示link、activity、速率等状态。
但便宜和好用是两回事。我在调这块板子的时候发现,YT8521SH的RGMII时序余量设计得比较紧,如果MAC侧和PHY侧都不主动加时钟延迟,千兆模式很容易出现“能link但收不到数据”的诡异现象。另外它的LED引脚是复用引脚,默认行为和你板子上的丝印标注不一定一致,这也会造成“明明网通了,灯却不亮”的误判。
1.2 我遇到的三个典型故障现象
这次调试我撞上的问题,基本可以归为三类,我先把现象列出来,后面逐个展开。
第一,U-Boot启动后,以太网控制器能够检测到PHY存在,mdio list也能列出设备,但执行ping时完全不通,报文发出后无任何回应,同时串口日志里看不到底层错误信息。这里最大的困扰是“看起来一切正常,实际数据全是坏的”。
第二,更诡异的是,我把PC网卡强制降到百兆后,板子居然能ping通了,但切回千兆自协商就完全不行。这个现象非常典型,基本可以断定是RGMII千兆速率下的时序问题,因为千兆模式时钟频率是125MHz,对延迟窗口的要求远高于百兆的25MHz。
第三,单独拿LED说事。板子上的丝印把其中一个灯标注为“Link”,但通电后真正指示link状态的却是另一个引脚,导致结构同事看了一眼直接来问我“网口是不是焊反了”。这说明YT8521SH的LED默认模式和硬件设计者预期的不一致,需要在PHY寄存器层面对LED功能做重新映射。
1.3 调试环境与必要工具
动手之前,先把环境列清楚,后面所有命令操作都基于这套环境。
- 主控SoC:ARM Cortex-A系列,内置GMAC,支持RGMII接口
- PHY:裕太微YT8521SH,PHY地址0x0
- Bootloader:U-Boot 2021.xx
- 调试串口:UART,115200-8-N-1
- 对端设备:PC,自带的Intel千兆网卡
- 连接方式:普通Cat 5e网线直连
工具方面,除了串口终端,我还准备了一台示波器,用来抓RGMII的时钟和数据相对位置。如果你手头没有示波器,纯靠U-Boot命令和PC端现象判断也行,只是会慢一些。另外强烈建议准备一个USB转千兆网卡的转接器,因为有些PC板载网卡和PHY之间的兼容性一般,容易掩盖问题。
提示:调试网络问题前,先用
mii info或者mdio list确认U-Boot已经正确识别到PHY地址。如果PHY地址和硬件strap对不上,后续所有寄存器操作都是在跟空气互动。
2. RGMII时序问题深度拆解
2.1 RGMII接口的采样机制
RGMII全称是Reduced Gigabit Media Independent Interface,相比GMII,它把数据线从8位减到4位,把时钟从双沿采样减到单时钟双沿采样。千兆模式下,TX_CLK/RX_CLK是125MHz,TXD[3:0]在时钟上升沿发送低4位,下降沿发送高4位,相当于用一根时钟同时采两路数据。
这里的关键点来了:既然数据在时钟的两个边沿都被采样,那么时钟沿就必须落在数据的稳定区间内。如果时钟沿抬起来或者落下去的时候,数据线恰好也在翻转,接收端采到的就是个不确定值,表现出来就是随机丢包、ping不通、或者干脆link不起来。
打个比方,这就好比两个人约定好每次响铃就交换手里的牌,但一个人总是卡在对方刚换完牌的瞬间响铃。听上去是小事,实际执行起来就是灾难。RGMII要求发送端提供大约2ns左右的时钟偏移(clock skew),让数据稳定后再送时钟边沿,接收端才有足够的setup/hold时间去采样。
2.2 为什么MAC和PHY之间容易“打架”
问题就在这个“2ns偏移”到底由谁来完成。RGMII标准其实允许两种做法:一种是由MAC在发送方向加延时,另一种是由PHY在内部加延时。但很多SoC的MAC默认是不加延时的,或者说它的RGMII控制器里的延时配置默认是关闭状态。
YT8521SH同样有内部延时选项,但它默认的行为取决于strap引脚和寄存器配置。不同批次、不同封装的芯片默认值可能还不一样。于是常见的情况就是:MAC说“我不加延时的,等PHY加”,PHY说“我默认没开延时,等MAC加”,两边都在等对方,结果数据永远采不对。
这种现象在“百兆通、千兆不通”的故障上表现最明显。百兆模式下时钟只有25MHz,一个数据位的时间窗口有40ns,稍微有点偏移也能忍。但千兆模式下数据窗口只有8ns,如果时钟边沿和数据翻转位置重叠,基本必挂。
还有一种极端情况,就是两块板子用RGMII信号“背靠背”直连,比如两个PHY直接对接,或者PHY和FPGA之间通过RGMII连接。这种应用场景在测试治具、光电转换模块里很常见,但对时序的要求比MAC+PHY的标准组合更苛刻,因为双方都需要明确各自的延时策略。
2.3 YT8521SH内部延时的寄存器配置实操
我这边验证下来,YT8521SH的RGMII延时控制是通过扩展寄存器完成的。先说怎么用U-Boot的mdio命令操作。
先确认PHY地址,然后用mdio read读取PHY ID寄存器,确认U-Boot看到的确实是我们这颗芯片:
# 查看MDIO总线上有哪些PHY mdio list # 读取PHY地址0x0的寄存器2和寄存器3,得到PHY ID mdio read 0x0 0x2 mdio read 0x0 0x3正常会读到0x0000和0x0000以外的值。YT8521SH的具体ID各位可以从datasheet里查,但更重要的不是这个,而是找到延时的控制位。
我手里的芯片,延时控制寄存器在扩展寄存器空间里,访问之前需要先选择页面(page)。以我这次使用的驱动流程为例,操作顺序是:
# 写页选择寄存器,进入扩展寄存器页 mdio write 0x0 0x1F 0x0001 # 读取延时控制寄存器,地址记作0xA001(映射到当前页的16bit寄存器) mdio read 0x0 0xA001读取出来的默认值通常是0x0000,这意味着TX和RX方向都没有内部延时。我当时的处理是把它设置成同时开启TX和RX delay,即bit15和bit14都置1:
# 将bit15(TX delay)和bit14(RX delay)置1,其余位保持不变 mdio write 0x0 0xA001 0xC000写完寄存器后,一定要让PHY重新复位或者重新自协商一次,否则新配置不会立即生效。可以在U-Boot下让网络接口先down再up,或者直接给PHY写软复位命令:
# 向控制寄存器0写入0x8000,触发软复位 mdio write 0x0 0x0 0x8000注意:不同版本/批次的YT8521SH,扩展寄存器的访问方式和位定义可能略有差异。上面给出的0xA001和bit15/bit14是我在实调中确认可用的配置,但你在量产前一定要对着手里的datasheet重新核对一次,特别是芯片revision不同时,某些保留位可能会有行为变化。
2.4 RGMII时序调试的判断流程
时序问题最怕就是瞎试。我后来总结了一套固定流程,每次遇到RGMII类的问题都按这个顺序走,能少走很多弯路。
第一步,先分清楚是“不能link”还是“link但不通”。如果link都建立不起来,优先查PHY地址、电源、时钟和复位,别急着调时序。如果link正常但不通,尤其是百兆千兆表现不一致,基本就是时序问题。
第二步,确认当前两侧的延时策略。读一下SoC侧RGMII控制器寄存器里关于delay的配置,再用U-Boot读PHY侧的延时控制寄存器,双方必须有一侧承担发送方向的延时,并且每一侧的接收方向也要有对应的延时策略。
第三步,按“先TX后RX”的顺序调整。发送方向对link和协商影响最大,建议先把TX delay打开,测试千兆能否通;如果不通,再打开RX delay。一次只改一个变量,避免改乱了不知道是哪边的问题。
第四步,验证时不要只ping几个包就收工,用连续的大包测试。
# 设置IP并ping对端,建议ping大包并且多ping几轮 setenv ipaddr 192.168.1.10 setenv serverip 192.168.1.100 ping 192.168.1.100我实际测试时会循环ping 1000次以上,再用tftpboot从TFTP服务器拉一个几MB的文件,确认长时间、大批量数据下不会出现CRC错误或丢包。
3. U-Boot环境下的PHY调试实操
3.1 U-Boot命令行里的PHY读写下细节
U-Boot下操作PHY的工具有两套:老版本常用mii系列命令,新版本逐渐统一到mdio系列命令。两套命令的核心动作都是读寄存器、写寄存器、列出PHY设备。
常用命令我整理一下:
| 命令 | 作用 | 示例 |
|---|---|---|
mdio list | 列出MDIO总线上的PHY地址 | mdio list |
mdio read <addr> <reg> | 读取指定地址PHY的寄存器 | mdio read 0x0 0x0 |
mdio write <addr> <reg> <val> | 写指定地址PHY的寄存器 | mdio write 0x0 0x0 0x8000 |
mii info | 显示PHY芯片信息 | mii info |
mii dump <addr> <reg> | 以bit方式显示寄存器内容 | mii dump 0x0 0x0 |
mii read/write | 老版读写命令 | mii read 0x0 0x0 |
如果你用的是老一点的U-Boot,可能只支持mii命令。如果PHY挂在MDIO总线的某个固定地址上,mdio list的输出类似:
MDIO bus: eth0 0x0: YT8521SH有了这个输出,就可以放心读写这个地址了。实际调试中,我最常用的几个寄存器是:
0x0控制寄存器:bit8是duplex,bit13是速度选择,bit14是loopback,bit15是软复位0x1状态寄存器:bit5/bit6是自协商完成和link状态0x2、0x3PHY ID寄存器,用来确认芯片型号0x4、0x5自协商advertise能力
先读状态寄存器判断link状态,是每次调试的第一动作:
# 读状态寄存器,确认link状态 mdio read 0x0 0x1如果返回的bit2是1,表示link已经建立。如果link没起来,后面谈时序、谈吞吐都是空的。
3.2 用PHY回环模式快速划清责任边界
时序问题有个麻烦:到底是MAC侧没发对,还是PHY侧没收到?用回环模式可以快速划清责任。
PHY回环(PHY Loopback)的原理是:在PHY内部把发送通路直接对接回接收通路,数据从MAC发下来,经过PHY内部绕一圈后回到MAC的接收侧。如果你在U-Boot里配置了PHY回环后能收到自己发的数据,说明MAC到PHY的通路大体没问题。
设置PHY回环的操作如下:
# 读当前控制寄存器值 mdio read 0x0 0x0 # 将bit14(loopback)置1,写入控制寄存器 mdio write 0x0 0x0 0x4000配置完成后,从MAC侧发起数据,看能否收回来。如果在回环模式下数据都收不到,问题基本出在MAC这边;如果回环模式没问题,但和外部设备通信还是失败,那基本可以锁定在PHY的外部链路或PHY的对外收发方向。
真实调试中,我用这个办法成功把问题范围缩小了一倍。尤其在怀疑RGMII时序问题的时候,先跑回环可以省掉大量外部环境排查。
注意:做完回环测试后,务必把loopback位清零,否则后续正常工作会受影响。写完寄存器后我习惯再读一次,确认写入生效。
3.3 设备树phy-mode配置与寄存器配置的配合
U-Boot里的GMAC驱动会读取设备树中phy-mode这个属性,不同的值对应不同的延时策略,这个和PHY内部寄存器的配置必须保持一致,否则会出现“U-Boot里明明改了PHY寄存器,但一旦重启又回到原点”的现象。
phy-mode常见有4种写法:
rgmii:MAC不加延时,PHY也不加延时,完全靠PCB走线延迟。这种模式对layout要求极高,一般不建议。rgmii-id:MAC不加,PHY负责TX和RX两侧延时,相当于让PHY内部把延时都做了。rgmii-txid:PHY只负责TX方向延时,RX方向由MAC或者PCB处理。rgmii-rxid:PHY只负责RX方向延时,TX方向由MAC或PCB处理。
我当时在设备树里配的是rgmii-id,但YT8521SH默认寄存器值是0x0000,没开任何delay,等于说设备树和PHY真实状态不一致。这种情况下,你有两条路:
一是改设备树为rgmii,让U-Boot驱动不去依赖PHY内部延时,但PCB走线必须足够好,能提供接近2ns的固有延迟。这条路对大多数板子来说风险太高,不推荐。
二是保持设备树为rgmii-id,然后让PHY驱动在初始化时把延时寄存器配置好,或者在U-Boot启动脚本里加一段mdio write把寄存器值固化掉。我最终采用的是修改PHY驱动初始化函数的方式,保证每次启动时PHY都会进入带延时的工作状态。
3.4 配置持久化的几种办法
U-Boot里通过命令行mdio write改的寄存器,只在当前运行期间有效,重启就没了。如果你的板子只靠临时命令调试,那没问题,但是要交付给测试甚至量产,必须有持久化方案。
我见过三种常见做法:
第一种,改U-Boot设备树,把phy-mode改成带延时策略的,并确保PHY驱动在config阶段做了内部寄存器初始化。这是最干净的方式,代码提交后所有人生效。
第二种,在U-Boot环境变量里加一段启动脚本,每次启动后自动执行mdio write。适合不想改代码、快速验证的场景,但不适合正式发布。
第三种,在Linux内核的PHY驱动里加上config_init回调,让内核在驱动PHY时自动配置延时和LED参数。这个做法的好处是最终系统和U-Boot阶段行为一致,但U-Boot阶段仍然需要临时手动配置来调试。
我自己的习惯是:U-Boot阶段先用命令行把寄存器摸清楚,找到最优配置后,再把配置固化到设备树或者驱动代码里,最后在U-Boot和Linux两个阶段都验证一遍。
4. LED灯配置的完整解密
4.1 YT8521SH的LED引脚不是死的
很多做硬件的人容易默认PHY芯片的LED引脚功能是固定的,比如LED0一定接link,LED1一定接activity。但实际上YT8521SH的LED引脚是高度复用的,它的默认行为由芯片的strap配置和寄存器共同决定。
换句话说,你板子上丝印写“Link”的那个灯,底层引脚可能被配置成显示“Speed”,或者“Activity”。这种情况在前期设计没仔细核对时非常常见。
YT8521SH通常会有两个LED驱动引脚,每个引脚可以配置成多种指示源。常见选项包括:
- 链路状态指示(Link)
- 收发活动指示(Activity)
- 速率指示(Speed,千兆/百兆/十兆区分)
- 双工状态指示(Duplex)
- 协商结果指示(自协商完成)
具体支持哪些映射,以芯片版本对应的datasheet为准。但你要理解它的底層逻辑:LED引脚输出的不是固定信号,而是一个由寄存器控制的mux,选择把哪个内部状态送到引脚上。
4.2 寄存器配置LED显示逻辑
我这次调试用的YT8521SH寄存器,LED模式控制通过扩展寄存器完成。要进入扩展寄存器空间,还是先通过页选择,然后在目标寄存器中配置LED模式字段。
以我手里的芯片为例,LED控制寄存器中通常有多个字段,每个字段负责一个LED引脚的功能映射。操纵顺序如下:
# 进入扩展寄存器页 mdio write 0x0 0x1F 0x0001 # 读取LED控制寄存器,记作0xA00D,具体地址请以手册为准 mdio read 0x0 0xA00D读取后返回的16bit值中,低两位对应LED0的模式,接着两位对应LED1的模式。最常见的几种取值我整理了一下:
| LED模式值 | 功能 |
|---|---|
| 00 | 链路状态(Link) |
| 01 | 收发活动(Activity) |
| 10 | 速率指示(Speed) |
| 11 | 双工状态(Duplex) |
这块我必须说明:不同封装版本或固件rev的YT8521SH,LED模式定义可能略有不同。上面这张表是我在具体芯片上验证过的,但不保证所有批次都完全一致。拿到新批次芯片后,最好先读寄存器,再用网线插拔和收发数据观察灯的变化,确认实际映射关系。
4.3 一个实际LED调试案例
我们板子上的现象是:丝印标注为“Link”的灯,在网络建立后没有亮,反而另一个标注为“Act”的灯在闪烁。从现象看,很可能两个引脚的功能映射反了,或者当前配置里LED0根本不是link。
我用U-Boot把LED控制寄存器读出来,发现LED0的模式并不是链路状态,而是被配置成了Activity。这样的话,板子上接到LED0脚的丝印虽然写着Link,但底层信号实际上是活动指示,当然不会常亮。
处理方式很简单,把LED0模式配置改成Link,写入寄存器后验证:
# 假设之前读到LED0模式字段不是Link,将其改为Link模式 # 先读原值,再只修改对应字段,避免动到其他配置 mdio read 0x0 0xA00D mdio write 0x0 0xA00D 0xxxxx改完后,插上网线,link灯常亮,有数据通过时活动灯闪烁,整个指示逻辑终于和丝印对上了。
这个案例给我最大的教训是:不要迷信丝印,也不要迷信“LED引脚默认就是link”的惯性思维。调试时先读寄存器,确认引脚的当前映射,再判断是否需要修改。
提示:修改LED寄存器后,同样需要重新初始化PHY或者软复位一次,部分版本的芯片LED输出逻辑不会立即刷新。
5. 常见问题与排查技巧实录
5.1 能协商上千兆但ping不通
这是RGMII调试里最常见的怪问题。link状态正常,自协商也显示1000M Full Duplex,但ping就是不通。
我的排查顺序是先确认PC侧网卡是否真的协商到了千兆。如果PC显示千兆,而板子ping不通,基本就是数据通路问题。这时候用回环模式排除MAC侧后,直接去调PHY的RX延时。
YT8521SH内部开启RX delay后,等于把PHY送给MAC的RX_CLK往后推了一小段,让MAC采样时数据已经稳定。我实测中,只开TX delay不开RX delay时,千兆依然会间歇性丢包;只有TX/RX都开启后,长时间大包才完全稳定。
如果PHY内部延时已经开了还不行,那就需要考虑PCB走线。RGMII每组信号之间的等长差建议控制在50mil以内,时钟线和数据线的长度差也会影响最终效果。这个只能回到硬件上解决。
5.2 温度变化后link闪断或丢包
有一类问题在实验室常温下测不出来,但一到高温箱或者户外低温环境就现原形——link偶发闪断,或者长时间运行后开始丢包。
原因是温度变化会导致PCB走线阻抗和芯片内部延时漂移。如果当初调时序时余量留得不够,比如时钟沿刚好卡在数据稳定的临界点,温度一漂,数据窗口就塌了。
解决办法只有一个:调时序时不要把寄存器值刚好调到“能通”就收手,要多留一些margin。我一般会对比“只开TX”、“只开RX”、“TX+RX都开”三种配置,在常温、高温、低温下各跑一轮压力测试,选出最稳定的组合。
另外,如果板子有多个同样的网口设计,建议每个口都验证一下,因为不同走线长度会导致每个口的时序窗口不完全一致。我亲眼见过同一块板子,两个网口的PHY配置完全不同才能正常工作。
5.3 与FPGA对接时的RGMII时序约束问题
有相当多项目不是用ARM SoC的MAC,而是用Xilinx FPGA来实现RGMII接口,尤其是要做多路网络处理、协议转换的时候。FPGA这边RGMI I通常通过IDDR原语在时钟上升沿和下降沿各采一次数据,这时代序约束就非常关键。
FPGA侧接收PHY送来的RX_CLK和RXD时,需要在约束文件里明确设置input delay,让工具知道时钟和数据之间的相位关系。否则综合工具按理想情况布线,布局布线后的实际时序可能跟设计预期差很多,表现出来就是FPGA和PHY之间的数据偶尔不对。
典型的约束做法是设置set_input_delay,把数据相对于时钟的延迟约束在2ns附近。如果PHY内部开了RX delay,那么FPGA侧看到的时序窗口会整体后移,约束值也要相应调整。反之,如果FPGA侧用IDDR采样并做了时钟相位偏移,PHY侧的RX delay可能就要关闭,否则过大的延迟会把数据采样点推出有效窗口。
这块我建议FPGA工程师和嵌入式工程师在项目早期就对好:到底由PHY加延时,还是由FPGA内部逻辑/约束加延时。最怕两边各加各的,加的延时叠加起来反而把时序搞坏。
5.4 两个PHY背靠背直连时的特别提醒
还有一种场景容易让人困惑,就是两个设备之间没有MAC,直接把两个PHY的RGMII信号交叉对接,俗称“PHY背靠背”。这种接法常见于光模块转换板、信号调理板,两个PHY的RGMII侧都不经过MAC,而是直连。
这种模式下,没有MAC帮你做时钟管理,两组RGMII信号完全靠两个PHY内部的延时来保证采样窗口。你需要保证:发送侧的PHY打开了TX delay,接收侧的PHY打开了RX delay,或者两侧都使用类似“完整延时”的配置,确保数据到对端时,时钟沿仍然落在数据稳定区间。
如果你用示波器抓两个PHY对接点的信号,会发现RGMII时钟线和数据线之间可能同时出现两段不同的延迟调整。问题排查难度比标准MAC+PHY高很多,有时甚至需要把一侧的延时关掉,靠另一侧单独提供足够的延迟量。这种应用我建议在硬件设计阶段就预留调试电阻,至少能改strap,否则后期只能靠寄存器硬调。
5.5 常见问题速查表
最后整理一张速查表,方便后面直接照着查。
| 现象 | 可能原因 | 处理建议 |
|---|---|---|
| U-Boot搜不到PHY | PHY地址不对、复位脚未释放、MDIO上拉异常 | 查strap电阻、量复位电平、确认MDIO地址 |
| link正常但千兆不通 | RGMII时序余量不够 | 开启PHY内部TX/RX delay,做压力测试 |
| 百兆通千兆不通 | 时钟和数据相对位置在千兆下恶化 | 调整延时配置,检查PCB等长 |
| ping通但大文件传输失败 | 数据通路偶发错误,CRC失败 | 检查RX delay是否足够,降低网线干扰 |
| LED行为和丝印不符 | PHY寄存器里的LED模式配置与硬件预期不一致 | 读LED控制寄存器,重新映射模式 |
| 高温下丢包 | 延时余量不足,温度漂移导致时序劣化 | 对比多组配置,留margin |
| FPGA对接失败 | IDDR约束不合理或两侧delay叠加 | 统一延时策略,FPGA侧加input delay约束 |
6. 一点超出调试本身的体会
这次YT8521SH的调试经历,让我重新意识到一个问题:很多嵌入式网络问题,根源不在“芯片坏”或“代码错”,而在“两侧默认配置不一致”。RGMII时序是这样,LED配置也是这样。MAC和PHY各自都有默认行为,但没有哪个默认行为能保证适配所有硬件设计。
我现在的习惯是,拿到新板子第一步先读PHY所有关键寄存器的出厂值,拍照存档,然后再动手改配置。这样做的好处是,改出问题时能随时回到原点,也能用出厂值和“正常工作时的值”做对比,方便排查是哪个配置位起了作用。
如果你也在调YT8521SH,建议先把本文提到的寄存器配置方法在U-Boot命令行里跑一遍。U-Boot阶段是排查PHY问题的最佳环境,它轻量、可控、排除操作系统干扰,能直接暴露最底层的硬件行为。等U-Boot下完全稳定了,再进入内核阶段做进一步验证,你会省掉后面很多难以定位的疑难杂症。