3. PHY 寄存器访问方式:MDIO/MDC、内存映射、SPI
咱们做硬件调试,最常打交道的就是怎么“够到”PHY芯片内部的寄存器。
说白了,PHY芯片就像一栋黑漆漆的大楼,寄存器就是楼里的房间。你得先找到大门,才能进去查东西。我这些年调过的板子,很多问题都卡在“门没找对”上——协议选错了,时序没对齐,结果读回来的数据全是乱码。
常见的访问门路有三种:MDIO/MDC、内存映射、以及SPI。这一章咱们就挨个拆开看看。
3.1 MDIO/MDC 管理接口
这是最正统、也最通用的方式。IEEE 802.3 标准里专门定义了它,用于管理 MAC 层和 PHY 层之间的交互。你几乎所有的千兆以太网 PHY 芯片都会支持它。
基本引脚与拓扑
- MDC:管理时钟,由 MAC 或 CPU 侧主动提供。频率最高通常到 2.5 MHz,但现在很多芯片也能跑到 25 MHz 或更高,看具体支持。
- MDIO:管理数据,双向传输。
我经常把它比作 I2C 的简化版:一条时钟线,一条数据线,分时读取写入。但它的帧结构比 I2C 要明确得多。
帧结构与时序
MDIO 的读写帧是固定的 64 位。我建议大家死记住这个结构,调试时抓波形能救命:
| 位域 | 位宽 | 描述 |
|---|---|---|
| PREAMBLE | 32 | 32 个连续的 1,表示帧起始 |
| ST | 2 | 启动码:固定 01 |
| OP | 2 | 操作码:10 表示读,01 表示写 |
| PHYAD | 5 | PHY 地址,一般由芯片外围管脚决定 |
| REGAD | 5 | 寄存器地址,一次只访问一个寄存器 |
| TA | 2 | 转向时间:读操作时为 Z0,写操作时为 10 |
| DATA | 16 | 数据 |
实际读写流程
以读操作为例,我一般会按这几步走:
- 确保 MDC 时钟稳定输出。
- CPU 通过 GPIO 或 MAC 内置的 MDIO 控制器,发出 32 个 1 的 PREAMBLE。
- 发送 ST、OP、PHYAD、REGAD。
- 第 33-34 位是 TA 阶段:CPU 将 MDIO 设置为高阻,由 PHY 驱动拉低 Z。
- PHY 在 TA 的第二个周期将 MDIO 拉为 0,表示准备输出数据。
- 随后的 16 个周期,PHY 把寄存器的值送出来。
你想想看,如果 TA 阶段电平不对,多半是 PHY 地址或寄存器地址写错了。我早期用逻辑分析仪抓过一次,才发现原来自己 REGAD 写成了 6 位,多了一位就把后面的时序全搞偏了。
// 伪代码:MDIO 读操作
uint16_t mdio_read(uint8_t phy_addr, uint8_t reg_addr) {
uint16_t data = 0;
// 1. 发送 32 个 1 作为 PREAMBLE
for (int i = 0; i < 32; i++) {
mdio_clk_cycle(1);
}
// 2. 发送 ST=01, OP=10, PHYAD, REGAD
send_bits(0x01, 2); // ST
send_bits(0x10, 2); // OP
send_bits(phy_addr, 5);
send_bits(reg_addr, 5);
// 3. TA 阶段:设置方向为输入,读 2 位(Z0)
set_mdio_dir(INPUT);
wait_ta(); // 等待 PHY 拉低
// 4. 读 16 位数据
for (int i = 0; i < 16; i++) {
data = (data << 1) | mdio_read_bit();
}
return data;
}
3.2 内存映射访问方式
对于集成度更高的 SoC,MDIO 有时太慢了。你就会发现很多设计直接把 PHY 的寄存空间映射到 CPU 的内存地址空间里。
我遇到过一些高端交换芯片,内部集成了几十个 PHY。如果每个都用 MDIO 轮询,光是读一遍状态就要花不少时间。所以它们会用内存映射,CPU 直接读写某个内存地址就能访问寄存器。
与 MDIO 的对比
| 特性 | MDIO | 内存映射 |
|---|---|---|
| 速度 | 慢(受限于 MDC 频率) | 快(直接内存总线速率) |
| 实现复杂度 | 简单,适用于独立 PHY | 依赖系统内存控制器 |
| 调试难度 | 需要外部逻辑分析仪 | 可以直接用 print 或调试器查看内存 |
| 典型场景 | 外接独立 PHY 芯片 | 集成 SoC 内部 PHY |
3.3 SPI 访问方式
某些 PHY 芯片也支持 SPI 接口来访问管理寄存器。这通常用在以太网供电(PoE)或一些工业级 PHY 上。SPI 的好处是速度快、接口简单,而且支持多个寄存器连续访问(burst 模式)。
一张 SPI 帧的典型格式:
- CS 拉低:开始事务。
- CPU 发送 8 位命令(高 1 位为 R/W,后面为地址或片段)。
- 接着发送或者接收 16 位数据(有些芯片是 8 位,但标准 PHY 寄存器是 16 位)。
- CS 拉高:结束事务。
3.4 三种方式的取舍
上面讲了三种方法,它们不是互相排斥的。在我做过的项目中,同一个 PHY 芯片可能同时提供 MDIO 和 SPI 两种接口,芯片内部会有一个模式选择管脚。我通常是:
- 能用 MDIO 的地方优先用 MDIO,兼容性最好,调试工具也多。
- 如果系统对延迟要求高、寄存器访问频繁,我会换成内存映射。
- SPI 适合需要高速连续读写的场景,比如批量配置 PHY 的初始参数。
3.5 知识体系总览
下面这张 SVG 图把三种方式的知识要点都串起来了。你可以对着图,回忆每一种方式的入口、时序关键点和避坑事项。
3.6 避坑指南与调试建议
前面讲了不少,最后我把这几年踩过的坑列一列,给你提个醒:
- PHY 地址要确认两遍:上电后先用逻辑分析仪抓一下 MDIO 总线,确认 PHY 返回的回应是不是你期望的地址。我曾经遇到过芯片的地址跟 datasheet 不一致,最后发现是某个保留管脚默认电平跟手册对不上。
- MDC 频率兼容性:有些老 PHY 只能跑 2.5 MHz,你拿 25 MHz 去打他,它可能根本不响应。建议先从最低频率开始,稳定后再提高。
- 跨页寄存器怎么办:很多 PHY 支持扩展页(page),在 MDIO 协议里通过写入特定的扩展页地址寄存器来切换。我建议你在调试时先读一次扩展页寄存器,确认当前页面,否则你读到的可能根本不是你想看的那些寄存器。
- 内存映射的保护:如果 PHY 寄存器映射到了系统内存,小心你的代码不要意外写入了不该写的地方。有一次我调试 DMA 时,一个指针越界直接把 PHY 的速率配置寄存器踩成了 0,整条链路瞬间断了。从此我在映射区域加了内存保护。
掌握这三种访问方式,就等于拿到了打开 PHY 芯片大门的钥匙。你可以根据板子的具体资源、调试工具和性能要求灵活选用。接下来我们就要进到“房间”里面,看看那些寄存器到底是干嘛的了。