3. MDIO 地址空间:PHY 地址分配与寄存器映射规则

调试 MDIO 总线,你绕不开一个核心问题:地址。

我见过不少新手,拿着逻辑分析仪抓了一堆波形,却看不懂数据的含义。为什么?因为他没搞清楚 PHY 地址是怎么分配的,更不清楚寄存器地址到底在映射什么。说白了,MDIO 协议里的地址空间,就是 PHY 芯片的“门牌号”和“房间布局”。门牌号找不对,你连门都进不去;房间布局不清楚,你拿到数据也是白搭。

3.1 PHY 地址:每个 PHY 的“门牌号”

MDIO 帧结构里有个 5 位的 PHY 地址(PHYAD),范围从 0 到 31。这意味着一条 MDIO 总线上最多只能挂 32 个 PHY 芯片。嗯,这不算多,但绝大多数嵌入式板子上也就一到两个 PHY,所以够用了。

PHY 地址通常由硬件引脚决定。比如常见的 RTL8211 系列,它有 PHYAD[4:0] 五个引脚,板上用上拉或下拉电阻设置电平,组合出最终的地址。我习惯在设计阶段就把地址分配表画出来,防止后面板子贴出来才发现冲突。

常见 PHY 地址配置示例(以 RTL8211 为例)
PHYAD[4:0] 引脚电平二进制地址实际 PHY 地址典型场景
00001000011主 PHY
00010000102从 PHY
011000110012光口 PHY
注意:同一个 MDIO 总线上不能有两个 PHY 地址相同。我曾经遇到过一块板子,两个 PHY 的地址都默认是 1,结果总线冲突,通信完全乱掉。后来飞线改了其中一个的配置引脚才算完事。你想想看,要是等到量产才发现这个问题,那就真成“坑”了。

3.2 寄存器地址:每个 PHY 内部的“房间号”

确定了 PHY 地址之后,接下来要访问的就是 PHY 内部的寄存器。MDIO 帧里的 5 位 寄存器地址(REGAD),最多寻址 32 个寄存器(0-31)。但别以为只有 32 个,那只是“基本访问空间”。

IEEE 802.3 规范把寄存器分成了两大部分:标准寄存器(0-15)和扩展寄存器(16-31)。标准寄存器是所有 PHY 都必须实现的,定义好了每个比特的含义。比如寄存器 0 是基本控制寄存器(BMCR),寄存器 1 是基本状态寄存器(BMSR)。这些是通用的,不管哪家厂商都一样。

// 寄存器 0(BMCR)关键位域举例:
Bit 15: Reset         – 写 1 复位 PHY
Bit 13: Speed         – 100M/10M 选择
Bit 12: Auto-Negotiation Enable – 自协商使能
Bit 8:  Duplex Mode   – 全双工/半双工

// 寄存器 1(BMSR)关键位域举例:
Bit 5:  Link Status   – 1 表示链路已建立
Bit 3:  Auto-Negotiation Complete – 自协商完成

我个人觉得,新手最容易忽略的是第 0 和第 1 个寄存器。因为很多调试场景第一步就是读这两个寄存器,确定 PHY 是否活着,链路是否已经建立。我自己的习惯是,初始化 MDIO 之后第一件事就是读 BMSR 的 Bit 5,看看链路状态是不是 1。如果不是,后面再怎么调都是浪费时间。

3.3 扩展寄存器与寻址技巧

标准寄存器只有 16 个,不够用。好在规范留了后门:寄存器 13(MMD 地址寄存器)和寄存器 14(MMD 数据寄存器)。

通过这两个寄存器,可以实现“变址访问” —— 寄存器 13 写子地址,寄存器 14 读写数据。这个方法可以访问到 PHY 内部隐藏的大片寄存器空间。比如高通的 QCA8035,它的很多调试功能都藏在扩展寄存器里。

// 读取 PHY 扩展寄存器示例(以 MMD 方式访问地址 0x1A 为例):
// 1. 写寄存器 13,设置 MMD 地址为 0x1A
// 2. 读/写寄存器 14,获取对应数据

// 伪代码:
MDIO_Write(PHY_ADDR, 0x0D, 0x001A);  // 设置子地址
uint16_t val = MDIO_Read(PHY_ADDR, 0x0E); // 读取扩展数据
小技巧:很多 PHY 数据手册里会写“此寄存器通过 MMD 地址 0xXXXX 访问”。我在项目里经常遇到手册写得含糊的情况,这时我建议直接用示波器抓一下 MDIO 波形,对照标准帧格式推算,反而比死磕手册快。

3.4 MDIO 地址空间结构总览

上面讲了很多,你可能会觉得有点乱。别担心,我专门画了一张图,把 MDIO 地址空间的结构梳理出来。你一看就明白了。

MDIO 地址空间结构图 MDIO 地址空间结构 MDIO 总线 PHY 地址 0 ~ 31(硬件引脚决定) PHY 芯片 (地址 = 1) 标准寄存器 0-15 扩展寄存器 16-31 Reg 0: BMCR Reg 1: BMSR ... Reg 15: 标准扩展 Reg 13: MMD 地址 Reg 14: MMD 数据 访问扩展寄存器方式 1. 写子地址到 Reg 13 2. 读/写数据通过 Reg 14

这张图可以帮你快速建立整体认知。从上往下看:MDIO 总线 → 多个 PHY(靠 PHY 地址区分)→ 每个 PHY 内部有标准寄存器和扩展寄存器。扩展寄存器通过寄存器 13/14 的“门中门”方式访问。

3.5 调试中的常见陷阱

讲了这么多理论,我再分享几个实际调试中踩过的坑,希望能让你少走弯路。

  • 陷阱一:PHY 地址对不上。硬件原理图上标的 PHY 地址是十进制,但 MDIO 帧里地址是二进制,我经常看到有人把地址 2 写成二进制 10,结果发成了 01(也就是地址 1)。每次调试前,我建议你先把 PHY 地址的二进制值写出来,对着数据手册确认。
  • 陷阱二:寄存器地址超出 0x1F。标准 MDIO 帧寄存器地址只有 5 位,最大值 31。有些开发板设计时用了扩展访问,但代码里直接写寄存器号 0x1A,结果 MDIO 控制器发出去的是低 5 位,导致读写错误。
  • 陷阱三:读寄存器 0 总是返回 0xFFFF。这通常不是 PHY 坏了,而是 MDIO 总线没接对,或者 PHY 压根没上电。我曾经花了一下午排查代码,最后发现是 PHY 的电源域没使能 — 这种低级错误最容易让人崩溃。
核心要点:
- PHY 地址 = 硬件引脚电平组合(0–31)
- 寄存器地址 = 功能寄存器编号(0–31,其中 0–15 为 IEEE 标准)
- 扩展寄存器通过寄存器 13/14 的“MMD 变址”方式访问
- 调试的第一步:先确认 PHY 地址正确,再读寄存器 1 的链路状态 Bit 5

好啦,MDIO 地址空间的那些事儿,说白了就是找准门牌号、摸清房间布局。下次你在调试时遇到数据不对,不妨先对照这张图,看看是自己走错了门,还是把房间号看花了眼。