第三章 常用PHY芯片寄存器:基本寄存器、状态寄存器、控制寄存器

好,聊到MDIO调试,寄存器是最底层的语言。PHY芯片的寄存器空间虽然大,但真正在调试中用得顺手的,其实就那么几个核心位置。我个人习惯把这些寄存器称作“三板斧”——基本寄存器、状态寄存器、控制寄存器。掌握这三类,大部分链路问题都可以理出头绪。

3.1 基本寄存器——PHY的身份信息

调试时最先要看的是什么?不是说马上查错误,而是先确认:我到底在和谁对话?PHY的基本寄存器就干这个事。

基本寄存器包括两个:

  • 寄存器0(Control Register)——控制PHY的启动、复位、环回等操作
  • 寄存器1(Status Register)——读取PHY的基本能力和当前状态
  • 寄存器2/3(PHY ID Registers)——厂商标识和型号标识

这三个寄存器组成了PHY的身份档案。其实,IEEE 802.3标准规定所有PHY都必须实现寄存器0和1,所以这两兄弟是调试的起点。

核心概念:基本寄存器是PHY的“身份证”,调试任何PHY前,第一步先读寄存器2/3确认型号。

我在项目中遇到过几次这样的情况:板子回来,MDIO能读到数据,但链路死活不通。我习惯先读一下寄存器2,发现读到的值是0xFFFF——这说明总线其实在浮空,读到的全是假数据。从那以后,我调试任何新板子,第一件事就是读PHY ID。

3.2 状态寄存器——PHY当前在干什么

状态寄存器(Register 1)是我日常调试点检次数最多的寄存器。说白了,它就是PHY的实时仪表盘。

我们重点看几个位段:

位段 含义 调试意义
Bit 15 100BASE-T4能力 通过该位可以判断PHY是否支持T4模式
Bit 14 100BASE-TX全双工能力 检查PHY是否支持100M全双工
Bit 13 100BASE-TX半双工能力 检查PHY是否支持100M半双工
Bit 12 10BASE-T全双工能力 检查PHY是否支持10M全双工
Bit 11 10BASE-T半双工能力 检查PHY是否支持10M半双工
Bit 5 远端错误 对端是否有错误信号
Bit 2 链接状态 一票否决位:1=链接正常,0=链接断开
Bit 0 扩展能力 是否有扩展寄存器可用

我个人在调试时,会先看Bit 2(链接状态)。为什么?链接状态是PHY硬件层直接给出的,不受软件干扰。如果是0,说明物理层就没通,不用去查上层协议了。如果是1但网络还是不通,那再往上查。

调试技巧:读寄存器1时别只盯着链接状态。我习惯把整个寄存器的值都读回来存下来。比如新板子第一次上电时寄存器1是0x7869,后面出问题再读变成了0x7849,位5变了——远端错误置位了。这个变化告诉你:对端PHY在报错。

嗯,这里要注意,状态寄存器很多位是“锁存”的。也就是说,某个错误发生过后,除非你读它一次,否则它会一直保持置位状态。我遇到过很多朋友在调试时连续读寄存器1,发现某个位一会儿1一会儿0,跑来问是不是PHY坏了。其实不是,是第一次读时清掉了锁存。

曾被忽略的问题:读状态寄存器会清空某些锁存位。如果软件在循环中反复读状态寄存器,可能会丢失掉一些偶发错误的信息。我建议:只有在需要捕获关键状态时才读寄存器1,或者在进入调试模式时才频繁读。

3.3 控制寄存器——可以做什么改变

状态寄存器只是看,控制寄存器(Register 0)才是动手的。

控制寄存器主要控制PHY的工作模式和行为:

  • Bit 15:软复位 —— 写入1可触发PHY软复位,复位后自动回0
  • Bit 14:环回模式 —— 写入1进入环回模式,常用于MAC/PCS层调试
  • Bit 13:速度选择高 —— 选择速率(与Bit 6配合)
  • Bit 12:自协商使能 —— 1=开启自协商,0=关闭自协商强制速率/双工
  • Bit 11:下电模式 —— 1=进入低功耗模式
  • Bit 10:隔离模式 —— 1=MDI/MDI-X隔离
  • Bit 9:重启自协商 —— 写入1重新开始协商过程
  • Bit 8:全双工 —— 强制全双工模式(仅自协商关闭时有效)
  • Bit 6:速度选择低 —— 与Bit 13组合选择速率
  • Bit 3:碰撞测试使能 —— 用于自主测试
  • Bit 0:重启 —— 某些PHY厂商用于软重启

速度选择位(Bit 13 + Bit 6)的组合挺重要:

Bit 13 Bit 6 速率
0 0 10BASE-T
0 1 100BASE-TX
1 0 1000BASE-T
1 1 保留

我调试时常做的事:如果怀疑自协商有问题,我会先关掉自协商(Bit 12写0),然后强制设一个速率和双工。然后把寄存器1读回来——如果能正常链接,说明PHY硬件没问题,问题出在协商过程里。如果是协商过程的问题,再去查寄存器4、5、6、7这些协商伙伴能力寄存器。

3.4 控制寄存器扩展——1000BASE-T特有的页

1000BASE-T由于速率更高,控制内容更复杂。标准在寄存器9(1000BASE-T Control Register)和寄存器10(1000BASE-T Status Register)中增加了扩展内容。

寄存器9包含几个关键位:

  • Bit 12:主从配置——决定谁发时钟谁收时钟
  • Bit 11:主从自动协商使能
  • Bit 9:发送器测试模式
  • Bit 8:接收器测试模式
// 读PHY ID的伪代码示例
uint16_t read_phy_id(int phy_addr) {
    uint16_t id_high = mdio_read(phy_addr, 2);  // 寄存器2:高16位ID
    uint16_t id_low  = mdio_read(phy_addr, 3);  // 寄存器3:低16位ID

    // 组合成32位ID
    uint32_t phy_id = ((uint32_t)id_high << 16) | id_low;
    
    // 标准PHY ID结构:
    // Bit 31:24 = OUI第3-10位
    // Bit 23:18 = OUI第11-16位
    // Bit 17:12 = 厂商型号
    // Bit 11:6  = 版本号
    // Bit 5:0   = 厂商自定义
    
    return phy_id;
}
经验之谈:读寄存器9和10时,一定要先读寄存器1的扩展能力位(Bit 0)。如果该位为0,说明这个PHY不支持扩展寄存器,读取的结果可能是垃圾值。我曾在某款古老的PHY上踩过这个坑,读寄存器9返回0xFFFF,查了半天PHY规格书才发现它根本不支持1000BASE-T。

还有一点,寄存器0的Bit 15,软复位。操作完后需要等待一段时间再访问PHY。不同厂商等待时间不一样,有的100微秒,有的要2毫秒。我曾经犯过一个错:写复位后立刻读状态,结果全0。后来加了个毫秒延时,一切正常。所以,复位后的等待不能省。

最后,我想说说寄存器操作的一个总的原则——“写后读验证”。任何写寄存器操作,都读回来确认一遍。MDIO总线上可能被其他设备干扰,写成功的假象我踩过不止一次。

常用PHY寄存器体系——调试主干 核心寄存器(按调试优先级排列) 基本寄存器 R2/R3: PHY ID 状态寄存器 R1: 实时状态+能力 控制寄存器 R0: 复位/环回/协商 扩展控制 R9: 1000BASE-T控制 协商能力寄存器 R4/R5: 本地/对端能力 扩展状态/诊断 R10/R17: 状态+诊断 调试切入点 ① 确认PHY地址 → ② 读PHY ID确认通信正常 ③ 读状态寄存器1:先看链接状态位(Bit 2) ④ 如果链接正常,检查协商结果 ⑤ 如果链接异常,检查控制寄存器0的复位/协商配置 IEEE 802.3 标准定义 扩展/厂商自定义空间