1. PHY 寄存器基础:概述、地址空间与 MII 基本寄存器组

各位好,我是老周。做硬件调试这些年,我最怕的就是 PHY 芯片死活不通,抓个 MDIO 波形一看,寄存器读回来的值全是 0xFF 或者 0x0000。说真的,这种问题十有八九是对 PHY 寄存器的基础结构没吃透。今天咱们就把这块骨头啃下来。

1.1 什么是 PHY 寄存器?

说白了,PHY 芯片内部的寄存器就是它的大脑。CPU 或 MAC 通过 MDIO/MDC 接口去读写这些寄存器,从而控制 PHY 的链路状态、速率、双工模式、协商结果,甚至拿到诊断信息。

我习惯把 PHY 寄存器比作一个“配置面板”。每个地址对应一个旋钮或指示灯。你读它,知道当前状态;你写它,改变 PHY 行为。这个面板长得什么样?IEEE 802.3 规定了基本框架,但每家芯片厂商还会自己加料。

一个小经验: 很多新手上来就翻 datasheet 找某个寄存器地址,结果越看越晕。其实大部分 PHY 的前 16 个寄存器是标准化的,你先把 0x00~0x0F 搞懂,就能解决 80% 的问题。

1.2 寄存器地址空间:别乱闯

PHY 寄存器的地址宽度通常为 5 位,也就是 0~31,共 32 个地址。

为啥是 32 个?因为 MDIO 帧结构里地址字段只给了 5 位(实际上支持 Clause 22 的规范)。当然,后来有了 Clause 45,地址扩展到 16 位,那是后话。我们做以太网调试最常见的还是 Clause 22 的 5 位地址。

那么这 32 个地址怎么分配?我整理了一张表,大家存着:

地址范围寄存器类型说明
0x00~0x0F基本寄存器组IEEE 802.3 强制要求,标准功能
0x10~0x1F扩展寄存器组芯片厂商自定义,或扩展标准功能

注意,0x10~0x1F 这部分每个厂家的定义都不一样。你在调试时如果发现某个地址读回来是 0,别急着认为芯片坏了——很可能是这个地址在你这颗 PHY 上没有实现,或者实现的功能与数据手册不一致。

⚠️ 避坑: 我曾经遇到一个项目,芯片 A 的寄存器 0x12 控制 LED 模式,但换了芯片 B 后同一个地址却是保留位。代码没改,灯死活不亮。后来老老实实对着数据手册一个一个地址对,才发现问题。所以千万别默认“全兼容”。

1.3 MII 基本寄存器组(0x00~0x0F)

这 16 个寄存器是所有 Clause 22 兼容 PHY 必须支持的。它们定义了 PHY 的“身份证”和行为。我按照地址顺序,把他们分成了几个功能组:

  • 控制与状态(0x00~0x01):主控。0x00 写复位、自协商使能、速率双工设置;0x01 读链路状态、协商完成、远端错误等。
  • PHY 标识(0x02~0x03):生产商 ID 和设备型号。这两个寄存器是 OUI 的后 24 位,用来区分不同 PHY。我习惯在读通基本寄存器后先看一下 0x02、0x03,确认芯片型号对不对。
  • 自协商通告与能力(0x04~0x05):4 通告自己的能力,5 对端的能力。抓协商问题就看这两个。
  • 自动协商扩展 & 下一页(0x06~0x07):用于 1000BASE-T 等高速协商。
  • 扩展状态 & 保留(0x08~0x0F):其中 0x0F 常用来做扩展页选择。

下面这张图概括了整个寄存器地址空间和基本寄存器的角色。你看,基本寄存器就像一个标准工具箱,扩展寄存器是厂商自定义工具包。

PHY 寄存器地址空间结构 基本寄存器组(0x00~0x0F) IEEE 802.3 强制标准 0x00 控制寄存器 0x01 状态寄存器 0x02~0x03 PHY标识符 0x04~0x05 自协商能力 0x06~0x07 扩展页/拓展 0x08~0x0F 保留/扩展状态 扩展寄存器组(0x10~0x1F) 厂商自定义 / Clause 45 扩展 0x10~0x14 常用:LED/中断 0x15~0x18 诊断/统计 0x19~0x1C 高级控制 0x1D~0x1F 供应商专用 ⚠️ 不同厂商定义不同 不能随意套用 调试要点 先读 0x00~0x0F 确认芯片身份和基本状态 → 再查扩展寄存器实现特殊功能 遇到异常值:检查 MDIO 时序、PHY 地址配置、是否进入测试模式

1.4 控制寄存器 0x00——你第一个要掌握的寄存器

地址 0x00(BMCR) 是 PHY 的遥控器。写这个寄存器可以复位、自协商、强制速率/双工。我建议你调试时第一个动作就是读 0x00,看看是不是处于复位状态(bit 15 = 1)。

它的比特位含义(以常见的 RTL8211 为例):

  • Bit 15:软件复位(写 1 复位,自清除)
  • Bit 14:环回模式
  • Bit 13:速率选择高位(1000M)
  • Bit 12:自协商使能
  • Bit 11:掉电
  • Bit 10:隔离
  • Bit 9:重启自协商
  • Bit 8:双工模式
  • Bit 6/5/2:协同决定速率
// 举例:读当前配置
uint16_t bmcr = phy_read(phy_addr, 0x00);
if(bmcr & (1<<15)) {
    printf("警告:PHY 正在复位中\n");
}
if(bmcr & (1<<12)) {
    printf("自协商已使能\n");
} else {
    printf("强制模式,检查 bit13, bit8, bit6 等\n");
}

1.5 状态寄存器 0x01——看 PHY 好不好

地址 0x01(BMSR) 是只读寄存器,告诉你 PHY 的基础能力和当前状态。

我每次读状态寄存器时,会特别关注下面几个比特:

  • Bit 5:自协商完成(1 = 完成)——这是最重要的标志位。如果一直不置位,说明协商失败。
  • Bit 2:链路状态(1 = 已建立)。
  • Bit 0~3:扩展能力(支持 100BASE-T4、100BASE-X、10BASE-T 等)。

我的调试口诀:“一读 0x00 看控制,二读 0x01 看状态,三读 0x02 查 ID,四读 0x04 对能力。”这个习惯跟了我十多年,极少失手。

1.6 PHY 标识符寄存器(0x02、0x03)

这两个寄存器合起来组成 32 位的 PHY 标识。其中 0x02 是高 16 位(OUI 后 16 位 + 厂商型号 6 位),0x03 是低 16 位(型号 + 修订版本)。

我做过一个工具,启动后自动读 0x02 和 0x03,然后查表匹配芯片型号。如果匹配不上,直接报错“未知 PHY,请检查硬件”。这个方法帮我挡掉了很多因贴错芯片导致的故障。

1.7 自协商相关寄存器(0x04~0x07)

0x04(ANAR)通告自己的能力,0x05(ANLPAR)显示对端能力。通过比较这两个,就能知道为什么协商结果是 100M 半双工而不是 1000M 全双工——很可能有一端没支持。

0x06(ANER)和 0x07(ANNPTR)用于扩展页,1000BASE-T 的协商需要这些寄存器做下一页交换。这块比较深,我们后面章节再细聊。

⚠️ 再次提醒: 读 0x04 前一定要先确认自协商完成(0x01 bit 5 = 1)!否则读回来的通告可能还没更新。我吃过这个亏:抓了一下午波形,结果是读早了,数据是 PHY 上电默认值。从那以后,我都会加一段等待协商完成的循环。

1.8 回顾与实用建议

这一章的内容其实很基础,但基础的东西最容易被忽视。很多工程师遇到 PHY 不通就量产去换芯片,结果换了还不行——最后发现是寄存器地址配错了。

我建议你在调试 PHY 之前,先准备好一张标准寄存器表(0x00~0x0F),并把你用的 PHY 的 datasheet 扩展寄存器表也打印出来贴墙上。每次读回值后,对照着看,异常值很快就能圈定。

嗯,这一章就到这里。你把这些概念吃透了,后面讲异常值定位方法时才不会发懵。