基本寄存器0(BMCR):控制寄存器的位定义
BMCR 是 PHY 的“总开关”。你想想看,一个芯片上电后总得有个地方告诉它:你要不要自检?跑多快?跟谁握手?这些决定都写进这个 16 位寄存器里。
我刚开始调 PHY 时总觉得这寄存器太简单——不就是置位嘛。直到有一次量产回来的板子死活协商不上,最后发现是某个软件工程师把复位位多清了 0.5ms。嗯,从那以后我再也不小看这个寄存器了。
BMCR 的位定义总览
先给张速查表,让你一眼知道每个位是干嘛的。后面我会挨个说坑在哪。
| 位号 | 名称 | 默认值 | 关键说明 |
|---|---|---|---|
| 15 | 软复位 | 0 | 自清位,写1触发复位,完成后自动归0 |
| 14 | 环回模式 | 0 | 1=内部环回,用于调试数据通路 |
| 13 | 速度选择(LSB) | 依物理层 | 配合位6(速度MSB),10=1000M,01=100M,00=10M |
| 12 | 自动协商使能 | 1 | 1=开启自动协商,0=手动模式 |
| 11 | 掉电模式 | 0 | 1=进入低功耗,链路断开 |
| 10 | 隔离 | 0 | 1=电隔离GMII/MII接口,调试用 |
| 9 | 重启自动协商 | 0 | 写1触发重新协商,完成后自清 |
| 8 | 双工模式 | 1 | 1=全双工,0=半双工 |
| 7 | 碰撞测试 | 0 | 仅用于测试设备 |
| 6 | 速度选择(MSB) | 依物理层 | 与位13组合成2bit速度值 |
| 5:0 | 保留/厂家自定义 | 0 | 一般只读或用于厂测,不要乱写 |
几个关键位的实战理解
位15:软复位 —— 千万别只写一次就以为完了
写1触发复位,芯片内部逻辑会立即开始重新初始化。但它是个自清位——你写入1后,必须轮询直到它变回0才算复位完成。
我曾经踩过的坑: 某次在产品代码里写完寄存器后直接去配其他 PHY 寄存器,结果前几笔配置被复位过程覆盖了。后来我习惯在复位后加一个超时循环,确认软复位位为0了再往下走。
// 建议的软复位流程(伪代码)
write_phy(REG_BMCR, 0x8000); // 写位15=1
timeout = 1000;
while ( (read_phy(REG_BMCR) & 0x8000) && timeout-- ) {
delay_us(10);
}
if (timeout == 0) {
// 处理复位超时异常
}
位14:环回模式 —— 隔离问题的利器
把这个位置1后,PHY 的发送数据输出会被环回到接收端。说白了就是自我对话,不依赖外部链路。
我一般在调试板卡硬件问题时会优先用这个:先开环回看 MAC→PHY 的数据回路是否正常。如果环回通信正常,那问题大概率在变压器、连接器或对端设备上。
小技巧: 环回模式下链路状态寄存器里的 Link Status 可能依然显示 Down,别慌——这是正常的,因为物理层根本没接外部介质。
位12与位13+位6:自动协商与速度选择 —— 二选一的关系
自动协商使能位(位12)等于1时,速度选择位(位13和位6)会被硬件忽略,芯片完全靠协商结果决定速度。只有自动协商关闭时,手动指定的速度才生效。
我记得调试一个交换机的级联端口时遇到一个问题:明明强制了1000M全双工,但 peer 端就是协商成100M。后来发现是对方开启了自动协商,我这边位12被意外置1了。你看,一个小位搞垮整条链路。
速度编码对照:
- 00(Z) —— 10BASE-T 半双工(实际很少这么设)
- 01(LSB) —— 100BASE-TX 半双工
- 10(MSB) —— 1000BASE-T 半双工
- 11 —— 1000BASE-T 全双工(最常见)
位11掉电 & 位10隔离 —— 调试阶段的“隐形杀手”
这两个位容易被忽视。调试时如果 PHY 链路老是 down,可以检查一下是不是误写了掉电位。我遇到过因为驱动框架调用了某个 suspend 函数导致位11被意外置1的案例,查了三天才定位到。
位10隔离在带外管理或者多 PHY 共享 MDIO 时比较有用,但普通应用一般保持0。如果不小心置1,MII 接口会变成高阻,读出来全是错数据。
几个面试中高频的问题实战
- 为什么软复位后读 BMCR 还是 0x1000?
很多 PHY 在上电后会自动协商使能默认为1(位12=1),而复位不会改变这个默认值。如果你期望手动模式,必须在复位完成后再次写入位12=0。 - 环回模式下能抓到 link up 中断吗?
通常不能。因为链路检测逻辑被旁路了。不要指望环回能触发链路状态变化。 - 不小心把掉电位写成1,卡死了怎么办?
重新用软复位位15复位一次可以恢复。但前提是 MDIO 时钟还没丢。如果掉电后 MDIO 也被停掉……那就只能断电重启了。
调试总结(我个人的排查顺序):
- 第一步:读 BMCR 看当前配置(特别是位12、位15和掉电位)
- 第二步:写个干净的初值 —— 先复位,再关闭自动协商,手动设好速度和双工
- 第三步:确认链路状态寄存器(BMSR)里的 link 位,排除外部问题
- 第四步:还不行就开环回,验证 PHY 核心通路是否正常
BMCR 虽小,但它是整个 PHY 行为的总指挥。你花半小时吃透这一页,后面调试任何 PHY 寄存器都会顺手很多。