1. 问题现象与初步分析
最近在调试一块嵌入式开发板时,遇到了一个奇怪的问题:使用4芯网线连接时,uboot下无法ping通主机,网络烧写功能完全失效。但更奇怪的是,当Linux内核启动后,同样的硬件连接却能正常通信。这种"uboot不行,内核可以"的现象引起了我的注意。
通过反复测试发现,在uboot环境下:
- 使用4芯网线连接百兆交换机时,网络通信正常
- 使用4芯网线连接千兆交换机时,网络完全不通
- 使用8芯网线时,无论百兆还是千兆都能正常工作
查看uboot的网络初始化日志时,发现一个关键线索:4芯网线连接千兆交换机时,PHY芯片RTL8211的自协商结果竟然显示为"1000M全双工"。这显然不符合物理规律 - 4芯网线理论上最多只能支持百兆通信。
2. RTL8211的自协商机制深入解析
2.1 关键寄存器解读
RTL8211PHY芯片有两个关键寄存器控制着自协商行为:
REG09 (PHY Specific Status Register)
- BIT9(0x200):千兆协商能力建议位
- 该位为1时表示建议使用千兆模式
- 该位为0时表示不建议使用千兆模式
REG0A (PHY Specific Control Register)
- 用于指示当前实际协商结果
- 可以判断最终协商速率是10M/100M/1000M
通过实际测量发现:
- 使用8芯网线连接千兆交换机时,REG09.BIT9=1,REG0A显示千兆
- 使用4芯网线连接千兆交换机时,REG09.BIT9=0,但REG0A仍显示千兆
2.2 自协商过程分析
RTL8211的自协商机制比较特殊:
- 芯片会尝试在两组MDI(Medium Dependent Interface)上都进行协商
- 只有当两组MDI都协商成功时,REG09.BIT9才会置1
- 使用4芯网线时,虽然只有一组MDI能正常工作,但芯片仍会错误地报告千兆协商成功
这解释了为什么uboot下会错误地协商成千兆模式 - 原始的uboot驱动只检查REG0A寄存器,而没有考虑REG09.BIT9的建议位。
3. uboot驱动修改方案
3.1 修改miiphyutil.c
需要在uboot的网络驱动中增加对REG09.BIT9的判断。以下是关键修改点:
int rtl8211_parse_status(struct phy_device *phydev) { /* 读取REG0A寄存器获取当前协商结果 */ int reg0a = phy_read(phydev, MDIO_DEVAD_NONE, 0x0a); /* 读取REG09寄存器检查千兆建议位 */ int reg09 = phy_read(phydev, MDIO_DEVAD_NONE, 0x09); /* 如果是4芯网线连接千兆交换机 */ if ((reg0a & 0x8000) && !(reg09 & 0x0200)) { /* 强制降级为百兆全双工 */ phydev->speed = SPEED_100; phydev->duplex = DUPLEX_FULL; return 0; } /* 其他情况按正常流程处理 */ return genphy_parse_status(phydev); }3.2 协商超时优化
另一个常见问题是第一次ping失败:
- RTL8211的自协商需要较长时间
- uboot默认的重试次数可能不足
解决方法是在驱动中增加协商超时时间:
#define PHY_ANEG_TIMEOUT 10000 /* 将超时时间延长到10秒 */ int rtl8211_startup(struct phy_device *phydev) { int ret; int timeout = PHY_ANEG_TIMEOUT; /* 等待自协商完成 */ do { ret = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMSR); if (ret < 0) return ret; if (ret & BMSR_ANEGCOMPLETE) break; udelay(1000); } while (--timeout); if (!timeout) printf("Warning: PHY auto-negotiation timeout\n"); return rtl8211_parse_status(phydev); }4. 实际测试与验证
4.1 测试环境搭建
为了验证修改效果,我搭建了以下测试环境:
- 开发板:搭载RTL8211 PHY的ARM开发板
- 测试设备:
- 千兆交换机(支持自协商)
- 百兆交换机(支持自协商)
- 网线类型:
- 标准8芯CAT5e网线
- 自制4芯网线(仅连接1,2,3,6引脚)
4.2 测试结果对比
| 测试场景 | 原始驱动 | 修改后驱动 |
|---|---|---|
| 8芯+千兆交换机 | 千兆成功 | 千兆成功 |
| 4芯+千兆交换机 | 连接失败 | 百兆成功 |
| 8芯+百兆交换机 | 百兆成功 | 百兆成功 |
| 4芯+百兆交换机 | 百兆成功 | 百兆成功 |
| 首次ping成功率 | 约50% | 100% |
4.3 关键寄存器值记录
在调试过程中,记录了关键寄存器的值变化:
8芯网线连接千兆交换机
- REG09 = 0x786d (BIT9=1)
- REG0A = 0xc1f0 (千兆全双工)
4芯网线连接千兆交换机
- REG09 = 0x786d (BIT9=0)
- REG0A = 0xc1f0 (错误报告千兆)
- 修改后驱动强制设置为百兆
协商失败时
- REG09 = 0x780d (BIT9=0)
- REG0A = 0x0000 (未连接)
5. 经验总结与进阶建议
在实际项目中,PHY芯片的调试往往需要结合硬件特性和软件驱动。针对RTL8211这类PHY芯片,我有几点经验分享:
寄存器分析要全面不能只看状态寄存器(如REG0A),还要关注配置寄存器和建议位(如REG09.BIT9)
物理层限制要考虑当物理连接(如4芯网线)存在限制时,驱动应该主动降级而不是盲目相信协商结果
调试技巧
- 在uboot中增加PHY寄存器打印功能
- 使用示波器测量MDI信号质量
- 对比不同网线类型下的寄存器变化
稳定性优化
- 适当增加自协商超时时间
- 考虑增加链路状态变化的重试机制
- 对于工业环境,可以固定速率而非依赖自协商
这个案例也提醒我们,在嵌入式网络开发中,硬件知识和软件调试能力同样重要。只有深入理解PHY芯片的工作原理,才能快速定位和解决这类"看似软件,实为硬件"的疑难问题。