前阵子在一款新板卡上调试网络PHY用的裕太微YT8521SH。最初我的预判是半小时搞定U-Boot起来以后设置好IPping通PC然后交给kernel完事。结果现实给了我一巴掌——RGMII时序和LED灯配置两个问题硬是从下午折腾到了晚上。这篇文章把整个过程记录下来包括RGMII时序的底层逻辑、U-Boot下读写PHY寄存器的具体操作、YT8521SH的LED配置方法以及几个很容易踩的坑。如果你也在做嵌入式网络调试尤其是用国产PHY配RGMII接口这篇应该能帮你省掉一大半查资料的功夫。先说清楚本文不是datasheet的翻译而是基于实际调试经验的复盘。我手里的平台是一颗ARM SoC加YT8521SHSoC的MAC侧走RGMIIU-Boot版本是2021.xxPHY地址通过硬件strap配成了0x0。下面的命令输出和寄存器值都是我当时实打实敲出来的你可以直接对照着手里的板子操作。1. 项目背景与问题描述1.1 YT8521SH是一颗什么样的PHYYT8521SH是裕太微电子的一款单端口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 调试环境与必要工具动手之前先把环境列清楚后面所有命令操作都基于这套环境。主控SoCARM Cortex-A系列内置GMAC支持RGMII接口PHY裕太微YT8521SHPHY地址0x0BootloaderU-Boot 2021.xx调试串口UART115200-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是125MHzTXD[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连接。这种应用场景在测试治具、光电转换模块里很常见但对时序的要求比MACPHY的标准组合更苛刻因为双方都需要明确各自的延时策略。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 listmdio read addr reg读取指定地址PHY的寄存器mdio read 0x0 0x0mdio write addr reg val写指定地址PHY的寄存器mdio write 0x0 0x0 0x8000mii info显示PHY芯片信息mii infomii dump addr reg以bit方式显示寄存器内容mii dump 0x0 0x0mii read/write老版读写命令mii read 0x0 0x0如果你用的是老一点的U-Boot可能只支持mii命令。如果PHY挂在MDIO总线的某个固定地址上mdio list的输出类似MDIO bus: eth0 0x0: YT8521SH有了这个输出就可以放心读写这个地址了。实际调试中我最常用的几个寄存器是0x0控制寄存器bit8是duplexbit13是速度选择bit14是loopbackbit15是软复位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种写法rgmiiMAC不加延时PHY也不加延时完全靠PCB走线延迟。这种模式对layout要求极高一般不建议。rgmii-idMAC不加PHY负责TX和RX两侧延时相当于让PHY内部把延时都做了。rgmii-txidPHY只负责TX方向延时RX方向由MAC或者PCB处理。rgmii-rxidPHY只负责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一定接linkLED1一定接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链路状态Link01收发活动Activity10速率指示Speed11双工状态Duplex这块我必须说明不同封装版本或固件rev的YT8521SHLED模式定义可能略有不同。上面这张表是我在具体芯片上验证过的但不保证所有批次都完全一致。拿到新批次芯片后最好先读寄存器再用网线插拔和收发数据观察灯的变化确认实际映射关系。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”、“TXRX都开”三种配置在常温、高温、低温下各跑一轮压力测试选出最稳定的组合。另外如果板子有多个同样的网口设计建议每个口都验证一下因为不同走线长度会导致每个口的时序窗口不完全一致。我亲眼见过同一块板子两个网口的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时钟线和数据线之间可能同时出现两段不同的延迟调整。问题排查难度比标准MACPHY高很多有时甚至需要把一侧的延时关掉靠另一侧单独提供足够的延迟量。这种应用我建议在硬件设计阶段就预留调试电阻至少能改strap否则后期只能靠寄存器硬调。5.5 常见问题速查表最后整理一张速查表方便后面直接照着查。现象可能原因处理建议U-Boot搜不到PHYPHY地址不对、复位脚未释放、MDIO上拉异常查strap电阻、量复位电平、确认MDIO地址link正常但千兆不通RGMII时序余量不够开启PHY内部TX/RX delay做压力测试百兆通千兆不通时钟和数据相对位置在千兆下恶化调整延时配置检查PCB等长ping通但大文件传输失败数据通路偶发错误CRC失败检查RX delay是否足够降低网线干扰LED行为和丝印不符PHY寄存器里的LED模式配置与硬件预期不一致读LED控制寄存器重新映射模式高温下丢包延时余量不足温度漂移导致时序劣化对比多组配置留marginFPGA对接失败IDDR约束不合理或两侧delay叠加统一延时策略FPGA侧加input delay约束6. 一点超出调试本身的体会这次YT8521SH的调试经历让我重新意识到一个问题很多嵌入式网络问题根源不在“芯片坏”或“代码错”而在“两侧默认配置不一致”。RGMII时序是这样LED配置也是这样。MAC和PHY各自都有默认行为但没有哪个默认行为能保证适配所有硬件设计。我现在的习惯是拿到新板子第一步先读PHY所有关键寄存器的出厂值拍照存档然后再动手改配置。这样做的好处是改出问题时能随时回到原点也能用出厂值和“正常工作时的值”做对比方便排查是哪个配置位起了作用。如果你也在调YT8521SH建议先把本文提到的寄存器配置方法在U-Boot命令行里跑一遍。U-Boot阶段是排查PHY问题的最佳环境它轻量、可控、排除操作系统干扰能直接暴露最底层的硬件行为。等U-Boot下完全稳定了再进入内核阶段做进一步验证你会省掉后面很多难以定位的疑难杂症。