2. PHY寄存器结构:标准与基本类型

寄存器的定义,说白了就是PHY芯片的“脑回路”。你调试网络不通、速率不对、自协商失败,最后都得回到寄存器层面找根源。我个人习惯是,拿到一个新PHY,先看它的寄存器map,而不是上来就跑测试。为什么呢?因为标准规定了底线,但每家厂商还有自己的“私货”。

好,咱们先聊聊标准。

2.1 遵循的标准——IEEE 802.3的“硬规矩”

大部分工业级PHY都遵循IEEE 802.3规范,具体来说是第22条(Clause 22)定义的MII(Media Independent Interface)管理接口。这个接口通过MDC(时钟)和MDIO(数据)两条线,就能读写PHY内部寄存器。

802.3里规定了几件事:

  • 寄存器地址范围:0~31,但前16个(0~15)是“标准强制”的,谁家PHY都得有。
  • 每个寄存器宽度:16位,这也是为什么你读出来的数值总是0x0000~0xFFFF。
  • 地址0和地址1(控制/状态寄存器)的比特位定义是固定的——比如bit 15是复位,bit 13是速度选择等等。

我遇到过的一个坑: 曾有个国产PHY,它号称兼容Marvell,但Clause 22的地址0 bit 13(速度选择)逻辑是反的。调试了整整一天,最后读到手册里一行小字才明白。从此我拿到新片子,第一件事就是对着802.3表,把控制寄存器的每个比特读一遍,确认没有“惊喜”。

当然,802.3还有Clause 45,用于更高速的PHY(千兆以上),地址空间更大(设备地址+寄存器地址两层),但基本思想一致。面试时,面试官通常只问Clause 22的寄存器结构,因为十有八九你调的是百兆或千兆PHY。

2.2 基本寄存器类型——0~15号寄存器

下面这张表是我手边常备的“速查手册”。你把它记熟了,80%的寄存器调试都能应付。

寄存器地址名称主要用途
0 (R0)控制寄存器 (Control)复位、回环、速度选择、自动协商使能、全双工等
1 (R1)状态寄存器 (Status)链接状态、能力、自协商完成、远程故障等
2 (R2)PHY ID 低位OUI(组织唯一标识符)的低16位
3 (R3)PHY ID 高位OUI高位 + 厂商型号 + 修订号
4 (R4)自协商通告 (Auto-Negotiation Advertisement)告诉对端我能支持哪些速率和双工模式
5 (R5)自协商对端能力 (Link Partner Ability)读取对端PHY通告的能力
6 (R6)自协商扩展 (Auto-Negotiation Expansion)对端是否支持自协商、下一页协商等
7 (R7)下一页协商 (Next Page)
(可选)
802.3扩展能力,比如EEE、流控
8~15扩展状态 / 厂商自定义R8是扩展状态(千兆主从、1000BASE-T等),R9~R15多数厂家留着做扩展,或放具体误差计数器

你想想看,面试官最喜欢的开场问题就是:“PHY的基本寄存器0和1分别控制什么?” 如果你能按比特位背出来,基本就过关了。但光背不行,得理解怎么用。

举几个常用场景:

  • 软复位:写R0的bit 15 = 1,然后等待bit 15自行清零(大概50ms),才算复位完成。我见过有人忘了轮询清零,直接读后续寄存器,读出一堆0xFF。
  • 强制100M全双工:写R0,把bit 13设为1(100M),bit 12设为1(全双工),bit 12(自协商)设为0。注意,不同PHY对bit 0(自协商)与速率位的关系可能有微妙差别,最好先看手册。
  • 读取链路状态:读R1,看bit 2(链接状态)。但很多PHY在链接断掉时,这个位可能不是立刻变0,需要读两次(两次值一致才算真的变)。这是IEEE建议的“去抖动”做法。
面试高频点: 标准寄存器0~15是强制实现的,但并不是每个比特都必须有效。比如R4的bit 7(100BASE-T4),现在基本不用,厂商可能直接固定为0。调试时别被老文档带偏。

2.3 扩展寄存器与厂商自定义

地址16~31是留给厂商扩展的。你调驱动时,真正棘手的问题往往藏在这些“非标准”寄存器里。比如:

  • LED控制寄存器:怎么改灯闪模式?
  • 中断状态寄存器:当链接变化时,到底触发了哪个事件?
  • MDI/MDIX交换控制:自动翻转模式是否开启?
  • 千兆测试寄存器:像眼图参数、信号幅度调整。

我曾经维护过一个产品,在某个极端温度下千兆偶尔掉线。折腾了半个月,最后是在地址25(厂商自定义)里发现了“交叉耦合补偿阈值”的魔术位,调了一下就稳定了。所以说,别只盯着0~15,扩展寄存器才是真正体现“调试深度”的地方。

下面这张图把整个寄存器体系的结构画出来,你一看就明白:

PHY寄存器结构层级图 PHY寄存器结构(IEEE 802.3 Clause 22) 控制 / 状态组 R0 控制寄存器 R1 状态寄存器 复位、回环、链路状态 PHY ID 组 R2 PHY ID 低16位 R3 PHY ID 高16位 OUI + 厂商型号 + 版本 自协商组 R4 本端能力 R5 对端能力 R6 扩展 R7 下一页(可选) 扩展状态 / 标准扩展组 R8 扩展状态(千兆主从等) R9 ~ R15 厂商定义 / 误差计数器 部分PHY在此放流控、EEE状态 厂商自定义组 R16 ~ R31 LED、中断、测试模式、 MDIX、均衡器等 标准强制 0~15 可选/自定义 16~31 核心要点:0~15寄存器是802.3强制标准,调试时优先检查;16~31则千奇百怪,必须看对应手册。 面试常问:R0/R1的比特位定义、自协商流程中R4/R5的读取时机、PHY ID的解析方法。

看到没?整个体系分成三层:下层是标准的0~15,上层是扩展的16~31。调试时要分清楚,别拿扩展寄存器当标准用,也别以为标准寄存器在所有PHY上行为完全一致——事实上,各个厂商对某些比特位的实现细节会有差异。

2.4 面试常问的寄存器调试场景

最后归纳几个最常见的面试问题,你可以在回答时直接套用上面的知识:

  1. “PHY的软复位流程是什么?”
    答:写R0 bit15=1,然后轮询读R0直到bit15为0(通常几到几十ms)。注意,复位后其他寄存器也会恢复默认值,特别是自协商使能位可能被复位。
  2. “怎么通过寄存器判断双绞线是直连还是交叉?”
    答:先看厂商自定义寄存器中是否有MDIX状态位(通常地址16~31);如果支持自动翻转,读R1的某个厂商扩展位。没有的话,只能通过自协商结果和链路信号极性反推。
  3. “为什么我读R0想改速率,但写完之后链路断了?”
    答:很可能在改写速率时,你没有关闭自协商(R0 bit12=0)。自协商开启时,速率位被内部逻辑锁定,写入会被忽略。我建议先读R0,修改对应比特,再写回去,不要直接整字赋值。
  4. “PHY ID有什么用?”
    答:用于驱动匹配。Linux内核里很多PHY驱动就是根据R2和R3组合的PHY ID来绑定对应的驱动函数。如果ID不对,驱动可能加载错误,导致寄存器读写不匹配。
特别提醒: 有些PHY在读取R1状态时,如果刚掉电或复位,会返回一个“旧状态”维持一段时间。代码里要做“两次读取比较”的防抖,否则你可能读到错误的链接状态。

好了,这一章把寄存器标准、基本类型、实用场景都串起来了。下回你再碰到PHY调试,记得先查地址0和1,再翻厂商手册找扩展寄存器。结构化地读,比盲猜高效得多。