最近在调试一块嵌入式开发板时遇到了一个奇怪的问题:使用4芯网线连接千兆交换机时,uboot阶段无法ping通主机,导致网络烧写失败。但更诡异的是,当Linux内核启动后,同样的硬件连接却能正常通信。这就像你家WiFi在手机能用却在平板上连不上——明明硬件没变,表现却完全不同。
通过抓取网络协商日志发现,uboot下4芯网线居然协商出了千兆链路!这显然不合理,因为4芯网线(通常只有1,2,3,6四根线有效)物理上根本不支持千兆传输所需的四对差分信号。这就好比试图用USB2.0的线跑出USB3.0的速度,注定会失败。
深入分析RTL8211这款常用PHY芯片的规格书后,我注意到两个关键寄存器:
当使用8芯网线时,寄存器09的bit9会置1,表示物理层支持千兆;而4芯网线时该位保持为0。但uboot原始代码仅检查寄存器0A,导致误判为千兆链路。
以太网自协商就像两个人在握手前先确认对方能说哪种语言。RTL8211通过以下流程完成协商:
对于4芯网线场景,关键点在于:
通过逻辑分析仪抓取的数据显示,不同场景下寄存器值有明显差异:
| 场景 | 寄存器09值 | 寄存器0A值 |
|---|---|---|
| 8芯网线接千兆交换机 | 0x0200 | 0x7801 |
| 4芯网线接千兆交换机 | 0x0000 | 0x7801 |
特别注意寄存器09的bit9(0x0200对应第9位)在不同线序下的变化。这就是导致误判的根源所在。
在uboot的miiphyutil.c中,原始链路检测逻辑如下:
c复制static int rtl8211_parse_status(struct phy_device *phydev)
{
int val = phy_read(phydev, MDIO_DEVAD_NONE, MII_STAT1000);
if (val & (PHY_1000BTSR_1000FD | PHY_1000BTSR_1000HD)) {
phydev->speed = SPEED_1000;
} else {
/* 检查100M/10M状态 */
}
return 0;
}
这段代码仅检查了寄存器0A(MII_STAT1000),完全忽略了PHY自身的物理层能力。
修改后的判断逻辑需要增加对寄存器09的检查:
c复制static int rtl8211_parse_status(struct phy_device *phydev)
{
int gsr = phy_read(phydev, MDIO_DEVAD_NONE, MII_STAT1000);
int adv = phy_read(phydev, MDIO_DEVAD_NONE, MII_CTRL1000);
/* 只有PHY自身支持千兆且对方也支持时才判断为千兆 */
if ((adv & ADVERTISE_1000FULL) &&
(gsr & (PHY_1000BTSR_1000FD | PHY_1000BTSR_1000HD))) {
phydev->speed = SPEED_1000;
} else {
/* 降级到百兆协商 */
phydev->speed = SPEED_100;
}
return 0;
}
关键修改点:
在实际测试中发现,首次上电时经常需要多次ping才能成功。通过示波器抓取MDIO总线发现,RTL8211在链路不稳定时会进行多次自协商尝试。修改uboot的PHY初始化配置可以改善这个问题:
c复制#define RTL8211_PHYSR_DELAY 30 /* 增加协商等待时间 */
int rtl8211_config(struct phy_device *phydev)
{
/* 增加自动协商完成等待时间 */
phy_write(phydev, MDIO_DEVAD_NONE, 0x1f, 0x0000); //切回page0
phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, BMCR_RESET);
mdelay(RTL8211_PHYSR_DELAY);
/* 强制重启自协商 */
phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR,
BMCR_ANENABLE | BMCR_ANRESTART);
mdelay(RTL8211_PHYSR_DELAY * 2);
return 0;
}
对于需要兼容4芯网线的产品,建议在硬件设计时:
在uboot中可以通过以下命令实时查看PHY状态:
bash复制# 读取寄存器09
mdio read eth0 0x09
# 读取寄存器0A
mdio read eth0 0x0A
# 强制设置为百兆全双工
mdio write eth0 0x00 0x2100
典型调试流程:
使用iperf测试不同模式下的实际吞吐量:
| 模式 | 理论带宽 | 实测带宽(4芯网线) |
|---|---|---|
| 强制百兆全双工 | 100Mbps | 94.7Mbps |
| 自动协商千兆 | 1000Mbps | 连接失败 |
测试证明强制百兆模式在4芯网线下能提供稳定的网络性能。这个案例给我的启示是:网络协议栈的每一层判断都必须与物理层实际情况严格匹配,任何假设都可能导致难以调试的兼容性问题。