寄存器基础:PHY寄存器访问方式、MDIO/MDC协议、寄存器地址映射
好,我们接着往下走。上一章聊了眼图的基本概念,今天我们来补一个基础——寄存器访问。你可能会想:“这些底层协议有啥好讲的?” 嗯,我刚开始做PHY调试时也是这么想的。直到有一天,我在项目里死活读不到某个寄存器的正确值,查了三天,最后发现是MDIO时序差了半个周期——从那以后,我再也不敢小看这玩意儿了。
说白了,PHY芯片就是一堆寄存器。你要调均衡、调摆幅、调预加重,全得通过寄存器读写来完成。所以,搞明白怎么访问这些寄存器,是眼图优化的基本功。
为什么要通过MDIO来访问?
PHY芯片本身没有键盘、没有屏幕,它需要和MAC(媒体访问控制器)通信。通信的桥梁就是MDIO接口。MDIO的全称是Management Data Input/Output,直译就是管理数据输入输出。它只有两根线:
- MDC:管理时钟,由MAC提供。
- MDIO:管理数据,双向传输。
我个人习惯把这两根线看作“串行总线的精简版”。不需要复杂的地址选通,一根时钟、一根数据,就能把PHY所有的寄存器给管起来。
核心要点: MDIO/MDC是IEEE 802.3标准定义的PHY管理接口。所有符合标准的PHY芯片,都通过这个接口暴露寄存器。你这辈子调PHY,基本就靠这两根线吃饭。
MDIO帧格式——读写的数据骨架
MDIO的通信是帧驱动的。每一帧包含64个时钟周期(在Clause 22模式下)。我刚开始记这个帧结构时觉得好麻烦,后来画了个图,一眼就明白了。你们看这个帧结构表:
| 字段 | 位宽 | 说明 |
|---|---|---|
| PREAMBLE | 32位 | 同步头,32个连续的"1" |
| ST | 2位 | 帧起始,固定"01" |
| OP | 2位 | 操作码:"10"为读,"01"为写 |
| PHYAD | 5位 | PHY地址,最多32个 |
| REGAD | 5位 | 寄存器地址,最多32个 |
| TA | 2位 | 转向时间,读操作由MAC驱动"Z"后PHY驱动"0" |
| DATA | 16位 | 读写数据 |
为什么要有这2位的TA?这里有个小坑。读操作时,MDIO线在TA周期需要切换驱动方向——MAC先释放总线,PHY再接管驱动数据。这个切换需要时间,TA就是用来做缓冲的。我曾经碰到过一款PHY,它的TA时序特别严格,MAC的GPIO模拟时序稍微慢了一点,读回来的数据全是乱的。你想想看,多冤枉。
寄存器地址映射——怎么找到你要的寄存器
每个PHY芯片内部寄存器按地址编址。标准Clause 22定义了前16个寄存器(地址0x00~0x0F)为基本寄存器,包括控制寄存器、状态寄存器、速度指示等。剩下的16个地址(0x10~0x1F)给厂商扩展用。地址映射结构大致是这样:
个人经验: 别以为标准寄存器就能通用。不同厂商对状态寄存器的位定义偶尔会有差异。我踩过最深的坑是在调试一款台湾厂商的PHY时,它的速度指示位和标准是反的——标准是"0"表示100M,它家是"1"表示100M。那一次我对着手册对了半天。
来看寄存器地址的分配:
- 0x00: 控制寄存器 — 复位、环回、速度选择、使能
- 0x01: 状态寄存器 — 链接状态、速度能力、自动协商完成
- 0x02~0x04: PHY标识符 — 厂商ID和型号
- 0x05~0x0F: 扩展状态/能力寄存器
- 0x10~0x1F: 厂商自定义 — 这才是调眼图的主战场(均衡器、摆幅、预加重)
避坑指南: 我曾经在访问0x10~0x1F的扩展寄存器时,发现每次读写的延迟必须大于一定阈值(比如1微秒)。有些MAC的MDIO控制器默认没有加这个延迟,连续读写就会丢数据。解决方案是在每次读写之间加一个微小的延迟。嗯,这个我踩过,所以特意提一下。
读写时序——怎么让数据正确传输
MDIO的时序并不复杂。MAC控制MDC时钟,数据在MDC的上升沿采样,在下降沿改变。看一个简单的写操作流程:
// 伪代码示例:模拟MDIO写操作
void mdio_write(uint8_t phy_addr, uint8_t reg_addr, uint16_t data) {
// 1. 发送32位前导码
for (int i = 0; i < 32; i++) {
mdc_toggle(); // 每个MDC周期发送1位"1"
mdio_out(1);
}
// 2. 发送帧起始和操作码
send_bits(0x01, 2); // ST = "01"
send_bits(0x01, 2); // OP = "01" 表示写
// 3. 发送PHY地址和寄存器地址
send_bits(phy_addr, 5);
send_bits(reg_addr, 5);
// 4. 转向时间(写操作是MAC持续驱动)
send_bits(0x10, 2); // TA = "10" 写操作
// 5. 发送数据
send_bits(data, 16);
}
读操作和写操作的差别主要在TA阶段:写操作MAC一直驱动,读操作在TA的前半周期MAC释放总线,后半周期PHY开始驱动。如果你是用CPU的GPIO模拟MDIO,一定要处理好这个切换。有一次在调试25G光口时,我发现读回来的寄存器值每隔几次就跳变一次,就是TA阶段的GPIO方向切换慢了半拍。
本章知识结构
我把本章的核心逻辑梳理成了一张图。你看,从MAC到PHY,通过MDIO/MDC协议桥接,最终映射到寄存器地址空间。而寄存器地址从标准寄存器区跨越到厂商自定义区,后者才是眼图优化的真正宝藏。
从这张图你可以看到:MAC通过MDIO/MDC协议访问PHY,PHY内部按地址映射展开寄存器。我们调试时,大部分时间泡在厂商自定义区(0x10-0x1F)。
嗯,再补充一点。有些新的PHY支持Clause 45协议(主要是10G以上的速率)。Clause 45的帧结构更长,支持更大的寄存器地址空间。但基本原理一样,只是帧格式更复杂。如果你的项目应用在10G/25G甚至更高速率,建议直接翻Clause 45。不过当前讲的基础是通用的,把Clause 22搞明白,Clause 45上手就快了。