4. 基本寄存器1(BMSR):状态寄存器的位定义
BMSR,全称是 Basic Mode Status Register,地址为 0x01。这是 PHY 芯片里最常用的状态寄存器之一。我个人的习惯是,每次拿到一块新的板子,先读这个寄存器,看看 PHY 的基本健康状况。
说白了,它就像一个“体检报告”。告诉你物理层有没有连上,自协商跑没跑完,有没有检测到远端错误。面试的时候,面试官经常会问:“你怎么判断网线插没插好?” 答案就在 BMSR 里。
4.1 BMSR 寄存器位定义速览
BMSR 是一个 16 位的寄存器,但真正用到的位并不多。很多位是保留位,读出来永远是 0。咱们只看核心的几位。我习惯把它的位定义整理成一张表,方便快速查阅。
| 位 (Bit) | 名称 | 描述 | 我常用的调试场景 |
|---|---|---|---|
| 15 | 100BASE-T4 | 支持 T4 模式(基本不用管) | 忽略,现在几乎见不到 |
| 14 | 100BASE-TX 全双工 | 支持 100M 全双工 | 确认 PHY 能力 |
| 13 | 100BASE-TX 半双工 | 支持 100M 半双工 | 确认 PHY 能力 |
| 12 | 10BASE-T 全双工 | 支持 10M 全双工 | 确认 PHY 能力 |
| 11 | 10BASE-T 半双工 | 支持 10M 半双工 | 确认 PHY 能力 |
| 9 | 100BASE-T2 全双工 | 支持 T2 模式(基本不用管) | 忽略 |
| 8 | 100BASE-T2 半双工 | 支持 T2 模式(基本不用管) | 忽略 |
| 6 | MF 帧前缀抑制 | 管理帧处理能力 | 极少使用 |
| 5 | 自动协商完成 (AN Complete) | 自协商流程结束,1 表示完成 | ⭐ 核心调试位 |
| 4 | 远端错误 (Remote Fault) | 对端 PHY 报错 | ⭐ 定位链路对端问题 |
| 3 | 自动协商能力 (AN Ability) | PHY 是否支持自协商 | 确认 PHY 功能 |
| 2 | 链接状态 (Link Status) | 1 表示链路建立,0 表示断开 | ⭐ 最最核心的调试位 |
| 1 | Jabber 检测 | 检测到超长帧 | 排查特定问题 |
| 0 | 扩展能力 | 是否支持扩展寄存器 | 确认是否需要读扩展寄存器 |
核心要点: 在实际调试中,你只需要关注 Bit 5(自动协商完成)、Bit 4(远端错误)和 Bit 2(链接状态)。其他的位,大部分时候你看都不用看。
4.2 链接状态位 (Bit 2) - 最常用的“心跳”信号
这个位,我几乎每天都在用。它就是 PHY 的“心跳”。值为 1,说明物理层已经和对方协商好了链路。值为 0,就是网线没插好或者对面没开机。
特点: 这个位是锁存的 (Latching)。什么意思?就是当链路断开时,它立刻变成 0。但当你重新插上网线,它不会立刻变回 1。你必须读一次寄存器,它才会更新。这是一个很经典的“陷阱”。
⚠ 避坑指南: 我曾经在调试一个交换机项目时,发现链路明明已经恢复了,但软件一直报 Link Down。折腾了半天,发现是驱动代码只读了一次寄存器。解决方案是:连续读两次 BMSR,丢弃第一次的结果,用第二次的值来判断。你想想看,很多面试题就喜欢考这个点。
// 正确的读链接状态流程
uint16_t bmsr_val;
// 第一次读,清除锁存位
bmsr_val = phy_read(phy_addr, 0x01);
// 第二次读,获取真实状态
bmsr_val = phy_read(phy_addr, 0x01);
if (bmsr_val & (1 << 2)) {
printf("Link is UP\n");
} else {
printf("Link is DOWN\n");
}
4.3 自动协商完成位 (Bit 5) - 自协商的“终点线”
这个位告诉你,自协商流程是否已经跑完了。值为 1,表示双方已经商量好了速率和双工模式。如果自协商没完成,你读到的速率和双工模式寄存器是无效的。
面试常见问题: “你怎么知道自协商完成了?” 答案就是读这个位,而不是去读自协商能力寄存器。
我的经验是:在驱动初始化时,不要假设自协商一下子就完成。需要轮询等待这个位置 1。通常需要等待 1-3 秒。如果等了很久还是 0,那就要考虑硬件问题了,比如网线质量差,或者对端不支持自协商。
// 等待自协商完成的轮询代码
int wait_for_an_complete(uint8_t phy_addr, int timeout_ms) {
uint16_t bmsr;
while (timeout_ms > 0) {
bmsr = phy_read(phy_addr, 0x01);
if (bmsr & (1 << 5)) {
return 0; // 成功
}
delay_ms(100);
timeout_ms -= 100;
}
return -1; // 超时失败
}
4.4 远端错误位 (Bit 4) - 对端 PHY 的“求救信号”
这个位比较有意思。它不是一个本地状态,而是对端 PHY 通过 FLP 脉冲发给你的。说白了,就是对面的 PHY 出问题了,通知你一声。
什么情况下会触发?
- 对端链路断开: 如果对端 PHY 检测到自己的链路丢了,它会发远端错误。
- 对端自协商失败: 对端 PHY 自协商没能达成一致。
- 对端 PHY 进入低功耗模式: 某些节能模式会触发这个。
💡 调试技巧: 当你的板子和对端设备死活连不上时,如果读到的链接状态是 0,但远端错误位是 1,那问题很可能出在对端。我曾经遇到一个案例,板子连交换机死活连不上,读远端错误位为 1。最后发现是交换机的那个端口被管理员 shutdown 了。所以,这个位能帮你快速定位问题是出在本地还是对端。
4.5 BMSR 与其他状态寄存器协同
光读 BMSR 还不够,它只是给你一个高层次的概览。想要拿到具体的速率和双工模式,你还需要配合其他寄存器。这里我画了一张流程图。
这张图展示了我的调试思路。第一步永远是读 BMSR,看链接状态。如果没连上,马上看远端错误位,初步判断是本地还是对端问题。如果连上了,再进一步确认自协商是否完成,最后再去读其他寄存器确认具体的速率和双工模式。
4.6 面试常见问题与举例
我整理了几个面试中高频出现的 BMSR 相关问题。你在准备的时候,可以重点看看。
- 问题: BMSR 的 Bit 2 (Link Status) 是锁存的,如何正确读取?
回答: 连续读两次,第一次用于清除锁存状态,第二次读取真实值。这是必考的,答应我别踩坑。 - 问题: 自协商没完成,但链接状态却为 1,可能吗?
回答: 可能。比如强制设置了速率和双工模式,不经过自协商也能 Link Up。这时 BMSR 的 Bit 5 会是 0。 - 问题: 如何区分是本地 PHY 坏了还是对端设备坏了?
回答: 读 BMSR。如果本地链接状态为 0,同时远端错误位 (Bit 4) 为 1,大概率是对端的问题。 - 问题: BMSR 的高位 (Bit 15-11) 有什么作用?
回答: 这些位表示 PHY 支持的工作模式。比如 100BASE-TX 全双工。一般用于初始化时确认 PHY 的能力。
总结: BMSR 是 PHY 调试的入口。我的习惯是,遇到网络不通的问题,第一个读的就是它。记住它的核心三位:Bit 2(链接状态)、Bit 5(自协商完成)、Bit 4(远端错误)。把这三位吃透了,你能解决 80% 的物理层问题。