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] 五个引脚,板上用上拉或下拉电阻设置电平,组合出最终的地址。我习惯在设计阶段就把地址分配表画出来,防止后面板子贴出来才发现冲突。
| PHYAD[4:0] 引脚电平 | 二进制地址 | 实际 PHY 地址 | 典型场景 |
|---|---|---|---|
| 00001 | 00001 | 1 | 主 PHY |
| 00010 | 00010 | 2 | 从 PHY |
| 01100 | 01100 | 12 | 光口 PHY |
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); // 读取扩展数据
3.4 MDIO 地址空间结构总览
上面讲了很多,你可能会觉得有点乱。别担心,我专门画了一张图,把 MDIO 地址空间的结构梳理出来。你一看就明白了。
这张图可以帮你快速建立整体认知。从上往下看: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 地址空间的那些事儿,说白了就是找准门牌号、摸清房间布局。下次你在调试时遇到数据不对,不妨先对照这张图,看看是自己走错了门,还是把房间号看花了眼。