4. 异常值判断标准:到底什么是正常值范围?

很多工程师拿到 PHY 寄存器数据后,第一反应是拿手册一页页翻。说实话,这效率太低了。判断一个值是不是异常,你心里得有把尺子。这把尺子就是:正常值范围。

那到底什么是正常值范围?不是手册里随便写个 default 值就能算完的。我习惯把它拆成三层来看——硬性规范约束、上下文配置依赖、电气环境反馈。今天咱们就重点说第一层:硬性规范约束。说白了,就是 IEEE 802.3 这些协议明文写死的、物理上必须遵守的底线。

4.1 正常值范围的三个维度

我个人习惯用一个三角模型来理解这件事:

  • 维度一:协议定义的标准值。比如 10M/100M 的寄存器 ID 位、版本号位,这些出厂就焊死了,不能变。
  • 维度二:配置决定的期望值。比如你软件配置了全双工、千兆模式,那对应的 control 寄存器 0.13 位(1000Mbps)、0.8 位(Duplex Mode)就应该是 1。
  • 维度三:电气环境决定的实时值。例如链路状态寄存器(BMSR)、MDI/MDIX 状态、信号质量指示。这些会随网线连接场景而变,但变化范围是有逻辑的。

判断的第一步,先把维度一和维度二搞清楚。
很多定位困难,误报异常,根源都是没分清:这个寄存器到底该是固定值,还是配置值?

PHY 正常值范围三层判断流程 物理层标准规范值 (固定不变) 软件配置期望值 (可控) 电气环境反馈值 (动态) PHY ID Reg(高/低) BMCR(0x00) BMSR / ANLPAR 综合判定 正常 / 异常 快速定位 只看是否 命中规范

4.2 维度一:协议强制要求的固定值

这类寄存器,我称为“死值”。比如 PHY Identifier 寄存器,地址 0x02 和 0x03。每个 PHY 厂商 OUI 是唯一的,你说它能不能是 0x00000000?绝不可能。只要读到全 0 或全 F,基本可以断定 MDIO 通信异常。

// 伪代码示例:判断 PHY ID 是否可信
uint16_t phy_id_low = read_phy_reg(phy_addr, 0x02);
uint16_t phy_id_high = read_phy_reg(phy_addr, 0x03);

if (phy_id_low == 0x0000 && phy_id_high == 0x0000) {
    printf("⚠️ PHY ID 全零: MDIO 通信可能失败,或 PHY 未复位完成。\n");
} else if (phy_id_low == 0xFFFF && phy_id_high == 0xFFFF) {
    printf("⚠️ PHY ID 全 F: 总线拉高状态,PHY 可能未响应。\n");
} else {
    // 这才进入正常判断
}

同样的,Status 寄存器(0x01)里的 bit 0(100BASE-T4,已废弃)、bit 1(100BASE-TX 支持)、bit 2(10BASE-T 支持),对固定型号芯片来说,这些位也是出厂后再也不变的。你想想看,如果你手上的 PHY 只支持千兆,那 BMSR 里的 10M 支持位必然是 0,这不能叫异常。但如果你读到一个千兆 PHY 的 bit 2 = 0(不支持 10M),实际上它却支持,那才是问题。

💡 我的一个小习惯:
我会在项目初期,先录制一份 PHY 正常联网状态下的全寄存器快照(Snapshot)。之后所有异常定位,直接拿 snapshot 做 Diff。比你每次翻手册一一核对快得多。

4.3 维度二:配置期望值(BMCR 和 ANAR)

这一层是定位中最容易出错的一块。我记得有一次排查,同事反馈 100M 不通,读 BMCR 发现 0x1340。他怀疑是 “Speed Select bit” 异常。我看了下,0x1340 实际上是 bit 13 = 1,bit 6 = 1(100M 全双工),看起来很对。那为什么不通?后来发现,他板子上用的是纯千兆 PHY,Autoneg 完成了,但软件还把 BMCR 手动设为 100M,等价于强制模式覆盖了协商结果。

你看,BMCR 本身并没有错,错的是配置和器件能力的错配。寄存器值本身是正常的,但从规范视角去看,它却是异常的。所以判断标准里必须引入一条:配置期望值必须小于等于器件能力。

拿 BMCR 0x00 举例:

Bit 域 正常值范围 异常判断阈值
Bit 14(Loopback) 0(正常) 诊断时可置1,否则异常
Bit 13(Speed Select MSB) 0 = 100M,1 = 1000M 与当前 Autoneg 状态不一致时标黄
Bit 12(Autoneg Enable) 1 或 0 均可 若强制模式但 partner 要求协商,则异常
Bit 8(Duplex Mode) 0 = Half,1 = Full 强制半双工在千兆下属于异常
Bit 6(Speed Select LSB) 0 = 10M,1 = 100M 与 Bit 13 组合读;10/100/1000 规则
⚠️ 避坑指南:
我曾经在调试一块百兆 PHY 时,软件误将 BMCR 的 bit 13 写成了 1。硬件支持不了千兆,导致链路一直 down。读 BMCR 看不出异常,但读 BMSR(0x01)的 bit 7(1000BASE-T 支持位)为 0,才判定为配置超范围。所以记得:配置期望值要对照器件能力值做回调校验。

4.4 维度三:电气环境反馈的动态范围

这类最让人头疼。不像前两类要么固定要么可控,它随环境变。典型的就是 BMSR 的 bit 5(Link Status)。Link 状态不能长期不变——正常上电后它应该从 0 变 1,如果拔网线又从 1 变 0。如果你读了一百次全是 1,要么没拔线要么寄存器锁存了。注意,很多 PHY 的 Link Status 是锁存位,需要读两次才能得到实时值。

// 典型锁存位处理
uint16_t status1 = read_phy_reg(phy_addr, 0x01);
// 第一次读后,锁存翻转,内部更新为实时状态
uint16_t status2 = read_phy_reg(phy_addr, 0x01);

if ( (status2 & 0x04) == 0 ) {
    // 实时 Link 断
}

还有 ANLPAR(Auto-Negotiation Link Partner Ability Register,0x05),它反馈的是对端能力。你想想看,如果你接的是一台千兆交换机,那读到的 ANLPAR 的 bit 7:0 应该显示至少支持 1000BASE-T。如果对方是百兆交换机,那就只能读到 100BASE-TX。这些不是非对即错,是相对正确。但判断异常的一个诀窍是:如果 ANLPAR 的 bit 15(Next Page)连续翻转,或者协商结果与 BMSR 的 link partner 能力严重冲突,那基本可以判断 Autoneg 出错或者链路信号质量极差。

4.5 核心归纳:一张表搞定异常判断三级标准

为了方便你快速查表,我基于 IEEE 802.3 Clause 22 和多年实践,把最常见的判断框架做成了一个表格。你可以贴在调试台旁边。

判断层级 典型寄存器 正常值判定原则 常用异常阈值
L1:协议标准 PHY ID Registers (0x02, 0x03) 严格等于已知 OUI + 厂商型号 全 0、全 F、与规格书差异 3bit 以上
L2:配置期望 BMCR (0x00)、ANAR (0x04) 写入值 = 期望值(允许一次回读延迟) 读回值与写入值不一致超过 2bit
L3:电气反馈 BMSR (0x01)、ANLPAR (0x05)
MII Status (0x11)、1000BASE-T Status
在合理的状态机序列内变化 Link 状态在 5 秒内无变化 / 协商结果与已知能力冲突

总结一句话:

正常值 ≠ 手册首页的 default 值。
它是一个 “规范基线 + 配置逻辑 + 环境反馈” 的综合判据。
遇到异常读数,先定级,再对照,不要一拳打到 MDIO 通信故障上。