1. 从一块跑不通千兆的RK3566板子说起第一次在RK3566上调试以太网很多人会遇到一个很迷惑的现象PHY芯片识别正常MDIO读写没问题ethtool看链路状态也是1000Mbps全双工但一跑iperf3就露馅——丢包、重传、速率忽高忽低严重的时候干脆link up之后几秒钟就down掉。更让人抓狂的是换一块同型号的板子同样的固件有的稳如老狗有的就是不行。这个现象背后十有八九是RGMII延迟线delay line配置没搞对。RK3566这颗芯片在国产嵌入式圈子里用得非常多平板、一体机、工控板、边缘计算盒子到处都能见到它的身影。它内部集成了GMAC控制器对外支持RGMII接口连接PHY。RGMII这个接口本身设计得挺巧妙——用4根数据线在时钟的上下沿同时传数据把原本需要8根线的MII接口压缩到了4根引脚省了一半。但代价是数据和时钟之间必须有精确的1.5ns到2ns的延迟关系否则接收端采样就会采到数据跳变的边沿上误码率飙升。问题在于这个延迟到底由谁来提供PHY提供还是MAC提供延迟加在TX方向还是RX方向加多少这些问题的答案取决于你用的PHY型号、PCB走线长度、甚至芯片的批次。而RK3566的DTS里恰好提供了tx_delay和rx_delay两个参数来微调这个值范围是0到0x7f。调对了千兆满速稳定调错了就是上面说的那种玄学故障。这篇内容就是围绕RK3566的RGMII延迟线配置展开把原理、DTS配置、实测调试方法、以及我在多个项目上踩过的坑完整地梳理一遍。不管你是刚拿到RK3566开发板的新手还是已经在量产项目上被以太网稳定性折磨过的老工程师应该都能从中找到有用的东西。2. RGMII延迟线到底在延迟什么2.1 先搞清楚RGMII的时序约定要理解延迟线得先把RGMII的时序模型建立起来。RGMII接口在1000Mbps模式下时钟频率是125MHz数据在时钟的上升沿和下降沿都有效所以理论带宽是125M × 2双沿× 4数据线 1000Mbps。TXC是发送时钟RXC是接收时钟TD[3:0]是发送数据RD[3:0]是接收数据另外还有TX_CTL和RX_CTL两根控制线在双沿上分别承载TXEN/TXER和RXDV/RXER。关键点来了RGMII标准规定发送端输出的数据信号其跳变沿应该比时钟跳变沿延迟1.5ns到2ns。这样接收端在时钟跳变沿采样时正好采在数据的稳定窗口正中间。这个延迟就是所谓的delay。那这个延迟由谁产生RGMII规范里其实留了两个选项PHY内部延迟很多现代PHY芯片比如Realtek RTL8211F、Motorcomm YT8531等内部集成了可配置的延迟单元可以通过PHY寄存器或者引脚strap来开启TX/RX延迟。MAC侧延迟也就是SoC的GMAC控制器来提供这个延迟RK3566的tx_delay/rx_delay就是干这个的。最忌讳的情况是两边都加了延迟或者两边都没加。两边都加总延迟变成3ns以上采样点跑到数据边沿外面去了两边都不加延迟为0采样点正好在数据跳变沿上同样出错。这就是为什么同一套固件在不同板子上表现不一样——因为不同批次的PHY可能strap引脚状态不同或者PHY内部默认延迟配置不同。2.2 RK3566的延迟线是怎么实现的RK3566的GMAC控制器内部对TX和RX路径各有一组可编程的延迟单元。DTS里的tx_delay和rx_delay就是写进这些延迟单元的配置值每个step大约对应几十皮秒的延迟量具体值跟芯片的工艺和时钟频率有关。0x7f是最大值0是关闭延迟。在设备树里典型的配置长这样gmac1 { phy-mode rgmii; clock_in_out output; tx_delay 0x3c; rx_delay 0x2a; phy-handle rgmii_phy; status okay; };这里有几个容易混淆的点需要说清楚第一phy-mode的取值。如果你写的是rgmii表示MAC和PHY都不额外加延迟延迟由外部或者PHY的默认配置决定如果写rgmii-id表示MAC内部同时给TX和RX加延迟rgmii-txid只加TX延迟rgmii-rxid只加RX延迟。但RK3566的驱动实现里tx_delay和rx_delay这两个参数是独立生效的跟phy-mode的组合关系需要看具体内核版本的驱动逻辑。我一般建议直接用rgmii配合显式的tx_delay/rx_delay这样最可控。第二clock_in_out的含义。output表示RK3566的GMAC输出TXC给PHY这是最常见的模式input表示时钟方向反过来。这个配置错了连最基本的时钟都没有PHY根本不会link up。第三延迟值的方向。tx_delay影响的是RK3566发给PHY的数据相对TXC的延迟rx_delay影响的是RK3566接收PHY数据时对RXC的采样延迟。这两个是独立的需要分别调试。2.3 为什么PCB走线也会影响延迟配置有人会问既然延迟是芯片内部配置的那跟PCB有什么关系关系大了。RGMII的125MHz时钟在PCB上的传播速度大约是6英寸/ns取决于板材和走线结构。如果TXC和TD[3:0]的走线长度不一致就会引入额外的skew。假设TXC比数据线长了0.5英寸那就相当于时钟比数据多延迟了约83ps。这个值虽然不大但叠加到芯片内部延迟上就可能把总延迟推出1.5ns到2ns的窗口。所以严格来说延迟线配置是在补偿芯片内部延迟 PCB走线skew PHY内部延迟这三者的总和。这也是为什么同样的DTS配置换一块PCB layout不同的板子就可能不work。在量产项目里如果硬件工程师没有做等长走线软件调试就会非常痛苦。我个人的经验是RGMII的TXC/RXC与对应数据线之间的走线长度差最好控制在5mil以内如果做不到至少也要在10mil以内。超过这个范围延迟线调试的窗口会变得非常窄甚至调不出来。3. 在RK3566上配置延迟线的完整流程3.1 确认硬件连接和PHY型号动手改DTS之前先把硬件信息确认清楚。需要知道的东西包括PHY的具体型号和datasheet。不同PHY的默认延迟配置差别很大。比如RTL8211F的TX延迟默认是关闭的需要通过寄存器或者strap开启而YT8531的默认配置又不一样。PHY的strap引脚状态。很多PHY用几个引脚在上电时的电平来决定内部延迟是否开启。如果硬件上拉了某个strap引脚PHY可能已经自带了延迟这时候MAC侧就不应该再加。RGMII走线的等长情况。如果拿不到PCB文件至少用万用表或者TDR测一下TXC和TD0的长度差。这一步看起来是废话但我见过太多人跳过这步直接改DTS结果调了一整天都没调通。先搞清楚PHY当前是否已经加了延迟是调试延迟线的第一步。一个实用的判断方法如果PHY的datasheet里写了默认开启内部延迟那MAC侧的tx_delay和rx_delay就应该设得比较小甚至为0反之则需要在MAC侧补上。3.2 DTS节点的正确写法RK3566的GMAC节点在arch/arm64/boot/dts/rockchip/rk3566.dtsi里定义具体板级的DTS里覆盖。一个完整的配置示例如下gmac1 { phy-mode rgmii; clock_in_out output; snps,reset-gpio gpio3 RK_PB7 GPIO_ACTIVE_LOW; snps,reset-active-low; snps,reset-delays-us 0 20000 100000; assigned-clocks cru SCLK_GMAC1_RX_TX, cru SCLK_GMAC1_RGMII_SPEED; assigned-clock-parents cru SCLK_GMAC1_RGMII_SPEED, cru SCLK_GMAC1; pinctrl-names default; pinctrl-0 gmac1m1_miim gmac1m1_tx_bus2 gmac1m1_rx_bus2 gmac1m1_rgmii_clk gmac1m1_rgmii_bus; tx_delay 0x3c; rx_delay 0x2a; phy-handle rgmii_phy1; status okay; };几个关键点展开说一下**snps,reset-delays-us**这个参数控制PHY复位时序格式是复位前延迟 复位保持时间 复位后延迟单位微秒。这个值不对PHY可能根本没复位成功后面什么配置都白搭。我一般用0 20000 100000也就是复位拉低保持20ms释放后等100ms再访问。**assigned-clocks和assigned-clock-parents**这两行是给GMAC的时钟树指定父时钟和分频。RK3566的GMAC时钟源选择比较多配错了会导致RGMII时钟频率不对PHY直接不link。这部分建议直接参考Rockchip的官方EVB DTS不要自己瞎改。**tx_delay和rx_delay**就是本文的主角。初始值可以先参考官方EVB或者同PHY型号的参考设计然后根据实测调整。3.3 延迟值的初始估算方法如果没有任何参考值怎么给一个合理的初始值我的做法是分三步第一步确定PHY是否自带延迟。查PHY datasheet看默认的delay配置。如果PHY默认开启了TX和RX延迟那MAC侧先都设成0测一下能不能通如果不通再逐步加。第二步根据PCB走线差做粗调。假设TXC比TD线短了200mil那时钟到达PHY的时间比数据早约33ps相当于数据相对时钟延迟了33ps。这个值换算成RK3566的delay step假设每step约50ps大约是0.66个step四舍五入就是1。所以tx_delay可以设成1左右起步。当然这只是粗调实际还要看PHY内部延迟。第三步用扫描法找最优值。这是最靠谱的方法下面单独讲。3.4 用扫描法找到最佳延迟值扫描法的思路很简单固定一个方向比如先调TX把rx_delay设成一个已知能工作的值或者先不管RX然后让tx_delay从0到0x7f逐步递增每个值都跑一次iperf3测试记录吞吐量和丢包率。找到吞吐量最高、最稳定的那个区间取中间值。具体操作步骤准备一个稳定的测试环境。PC端跑iperf3 -s板子端跑iperf3 -c PC_IP -t 30 -P 4测试30秒4个并发流。修改DTS里的tx_delay值重新编译dtb重启板子。每次重启后先ethtool eth0确认link状态是1000Mbps全双工然后跑iperf3记录结果。把tx_delay从0扫到0x7f步进可以先大一点比如8找到大致区间后再用小步进比如2细化。这个过程听起来很笨但确实是最可靠的方法。我一般会写一个脚本自动化这个过程通过串口控制板子重启和修改dtb把结果记录到CSV里。一个完整的扫描大概需要两三个小时但一旦找到最佳值后面就一劳永逸了。扫描结果通常会呈现一个窗口形状在某个区间内吞吐量稳定在940Mbps左右千兆的理论上限窗口两边逐渐下降超出窗口后直接link down或者大量丢包。最佳值应该取窗口的正中间而不是边缘因为中间值的容错空间最大温度变化、电压波动、批次差异都不容易把它推出窗口。TX和RX要分别扫描。先扫TX固定RX为一个中间值TX确定后再扫RX。两轮下来基本就能找到最优组合。4. 调试过程中最容易踩的几个坑4.1 PHY和MAC同时加延迟导致的不稳定这是最常见的问题。硬件工程师在设计时可能参考了某个参考设计把PHY的strap引脚配置成了开启内部延迟而软件工程师在DTS里又加了tx_delay和rx_delay。两边一叠加总延迟超标链路能link up但跑数据就出错。判断方法把MAC侧的tx_delay和rx_delay都设成0如果链路反而更稳定了那基本就是PHY自带延迟。这时候要么保持MAC侧为0要么把PHY的内部延迟关掉二选一。我个人的偏好是优先使用PHY内部延迟MAC侧设0。因为PHY的延迟配置通常更精确而且换PHY型号时只需要改PHY寄存器不用动DTS。但有些PHY的内部延迟精度不够或者配置起来麻烦那就反过来用MAC侧延迟。4.2 只调TX不调RX很多人调延迟线时只关注tx_delay因为发送方向的延迟概念比较直观。但实际上rx_delay同样重要而且往往更难调。RX方向的问题表现得更隐蔽TX方向有问题时通常是对端收到大量CRC错误RX方向有问题时是本端收到CRC错误ethtool -S里的rx_crc_errors会持续增长。调试时一定要两个方向都测。用iperf3双向测试加-R参数反向测试或者用ethtool -S分别看TX和RX的错误计数。4.3 忽略温度和电压的影响延迟线的窗口宽度是有限的而芯片的延迟特性会随温度和电压变化。在常温下调好的值到了高温环境比如夏天户外或者密闭机箱内可能就偏出窗口了。所以扫描时最好在高温环境下也测一遍或者至少留足够的余量。如果窗口宽度只有几个step那量产时的风险就很大需要跟硬件工程师沟通看能不能通过调整PCB走线来扩大窗口。4.4 不同内核版本的驱动差异Rockchip的内核版本比较多4.19、5.10、6.1都有RK3566的支持。不同版本里GMAC驱动的实现有差异tx_delay和rx_delay的生效逻辑可能不一样。有的版本里这两个参数只在phy-mode为特定值时才生效有的版本里延迟值的step大小也不同。遇到配置不生效的情况先去驱动源码里确认一下参数是怎么被解析和写入寄存器的。路径一般在drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c或者dwmac-rockchip.c里。花十分钟看一遍驱动比盲目试半天强。5. 实测数据与优化建议5.1 一组典型的扫描数据下面是我在一块RK3566工控板上实测的TX延迟扫描数据PHY是RTL8211FPHY内部延迟关闭PCB走线等长控制在5mil以内tx_delayiperf3吞吐量(Mbps)重传次数链路状态0x00无法link-down0x10312大量up0x20687较多up0x2c9410up0x349380up0x3c9400up0x449350up0x4c702较多up0x5c298大量up0x70无法link-down可以看到稳定窗口大约在0x2c到0x44之间宽度约24个step。最佳值取0x38左右也就是窗口正中间。这个窗口宽度算是比较健康的量产风险不大。如果窗口宽度小于10个step就要警惕了。这时候需要检查PCB走线是否等长、PHY的电源是否干净、晶振是否稳定。5.2 优化延迟配置的几个实用技巧技巧一用ethtool -S看错误计数。除了吞吐量错误计数也是重要指标。rx_crc_errors、rx_errors、tx_errors这些计数在稳定配置下应该都是0。如果吞吐量看起来还行但错误计数在涨说明延迟值虽然在窗口内但偏边缘需要往中间调。技巧二用不同包长测试。iperf3默认用大包但实际网络里小包也很多。用-l参数指定包长分别测64字节、512字节、1518字节看不同包长下的表现。有时候大包没问题但小包丢包说明延迟值需要微调。技巧三长时间稳定性测试。找到最佳值后至少跑24小时的iperf3或者ping -f确认没有偶发错误。我遇到过常温下跑1小时没问题、跑8小时后开始丢包的案例最后发现是散热不好导致芯片温度升高延迟特性漂移。技巧四保留调试接口。量产固件里最好保留通过/sys或者debugfs动态调整延迟值的能力方便现场排查问题。Rockchip的驱动里有些版本支持通过/sys/class/net/eth0/phy_delay之类的接口读写延迟值不用重新烧固件。5.3 什么情况下应该考虑换方案如果延迟线怎么调都不稳定窗口宽度始终很窄可能需要考虑以下替代方案改用RGMII-ID模式让PHY和MAC的延迟配置更明确减少歧义。换用自带延迟且精度更高的PHY比如某些型号的延迟step更细配置更灵活。检查PCB设计特别是RGMII走线的参考平面是否完整、是否有跨分割、终端匹配电阻是否正确。降低到百兆模式测试如果百兆稳定但千兆不行基本可以确定是时序问题而不是硬件连接问题。6. 几个实际项目中的经验体会在多个RK3566项目上调试以太网之后我总结了几条不太容易从文档里找到的经验。关于延迟值的默认配置。Rockchip官方EVB的DTS里给的tx_delay和rx_delay值只能作为参考不能直接照搬到自己的板子上。EVB的PCB走线和PHY型号跟你的板子大概率不一样延迟值必须重新调。我见过直接抄EVB配置然后抱怨以太网不稳定的案例不在少数。关于PHY的复位时序。有些PHY对复位脉冲的宽度有要求太短了复位不彻底太长了也没必要。snps,reset-delays-us这个参数一定要按PHY datasheet来设。另外复位GPIO的极性也要确认GPIO_ACTIVE_LOW还是GPIO_ACTIVE_HIGH搞反了PHY一直处于复位状态当然link不上。关于MDIO总线的上拉电阻。RGMII接口的MDIO和MDC两根线需要上拉电阻通常是1.5k到10k。如果上拉电阻缺失或者阻值不对MDIO通信会不稳定表现为PHY时有时无。这个问题在硬件调试阶段容易被忽略因为有时候能读到PHY ID就以为没问题了但实际上通信余量已经很小了。关于内核log的利用。RK3566的GMAC驱动在调试时会打印一些有用信息比如dwmac-rk相关的log。把内核log级别调到7可以看到延迟值的实际写入情况和PHY的协商过程。这些log对于定位问题非常有帮助。关于量产的一致性。如果项目要量产延迟值不能只在一两块板子上调好就完事。至少要拿10块以上的板子验证确认所有板子都能在同一个延迟值下稳定工作。如果个别板子需要不同的值说明硬件一致性有问题需要跟硬件团队一起分析。最后说一个我自己的习惯每次调好一个项目的延迟值我都会把PHY型号、PCB走线长度、最终的tx_delay/rx_delay值、以及测试条件记录到一个表格里。下次遇到类似配置的板子可以拿这个表格做参考初始值就能给得比较准省去大量扫描时间。这个习惯看起来不起眼但积累几年之后就是一笔很宝贵的经验财富。