4. Clause 22 vs Clause 45:区别、应用场景

做MDIO通信,你一定会遇到两个标准:Clause 22和Clause 45。说白了,这就是MDIO协议的两种“方言”。很多人把这两个混着用,结果通信失败,还找不到原因。今天我带你把它们彻底搞清楚。

一句话总结:Clause 22是老标准,简单够用;Clause 45是新标准,功能更强。选错了,设备根本不理你。

4.1 为什么会有两个标准?

早期的以太网PHY芯片,功能简单。IEEE 802.3在1998年定义了Clause 22,规定了MDIO总线怎么访问PHY的内部寄存器。那时候,32个PHY地址,每个PHY有32个寄存器,够用了。

但是后来,10G、40G、100G光模块出现了。这些设备内部的寄存器多得多,32个根本不够用。于是IEEE在2000年之后推出了Clause 45,把地址空间扩展到了很大。我个人习惯把Clause 45看作是“加强版”。

你想想看,如果你的PHY设备支持Clause 45,你却用Clause 22的帧格式去读它,它可能直接忽略你,或者返回乱码。

4.2 帧格式的区别(核心差异)

两者最根本的区别,就在读写帧的格式上。

Clause 22 帧格式

它只有一种帧结构。对于读写操作,通过操作码(OP码)来区分:

  • 写操作:OP码 = 01
  • 读操作:OP码 = 10

帧结构是:Preamble + Start + OP + PHY_ADDR + REG_ADDR + TA + DATA

这里PHY地址占5位,寄存器地址占5位。

Clause 45 帧格式

它引入了“端口地址”和“设备地址”的概念。帧格式变得复杂,有四种类型:

  • 类型0(地址帧):先写入一个22位的寄存器地址,告诉PHY你要访问哪个寄存器。
  • 类型1(写数据帧):写入数据到之前指定的地址。
  • 类型2(读数据帧,后增量):读出数据,地址自动加1。
  • 类型3(读数据帧,无增量):读出数据,地址不变。

我记得有一次调试40G光模块,用逻辑分析仪抓波形,发现Clause 45的帧里包含了一个很长的地址段,这跟Clause 22完全不同。

我的避坑指南:如果你用Clause 45访问Clause 22的设备,或者反过来,最好的结果是读回0xFFFF或0x0000。我曾经被这个坑搞了整整一个下午。

4.3 地址空间与寄存器映射

这一点非常关键,直接决定你能不能读到正确的寄存器。

特性 Clause 22 Clause 45
PHY设备地址空间 32 (5位) 32 (5位)
内部寄存器地址 32 (5位) 65536 (16位,通过类型0地址帧指定)
标准寄存器 寄存器0-31,前16个有标准定义 设备类型+地址,前32个有标准定义,但每个设备类型都有自己的空间
扩展能力 弱,基本是固定的 强,支持厂商自定义扩展

举个例子。你想读PHY的基本状态寄存器。Clause 22下,地址就是0x01。Clause 45下,你需要先发一个类型0帧,设置设备类型为0(标准MMD),寄存器地址为0x0001,再发一个类型2帧去读。

4.4 应用场景:什么时候该用哪个?

这不是你可以随便选的。设备支持哪个,你就得用哪个。

Clause 22 的典型场景:

  • 百兆、千兆以太网PHY(比如RTL8211、KSZ9031)
  • 简单的SFP光模块(只读DDM信息)
  • 低成本的交换芯片
  • 兼容性要求高的老设计

Clause 45 的典型场景:

  • 10G/40G/100G光模块(如SFP+、QSFP+)
  • 复杂的光模块(带数字诊断、调顶功能的)
  • 多端口、高密度交换芯片
  • 支持EEE(节能以太网)的先进PHY
注意:有些设备同时支持两种协议!你需要查看芯片手册中的“MDIO Interface”章节。通常在复位后,设备默认工作在Clause 22模式。如果你想用Clause 45,可能需要先通过Clause 22写一个特殊寄存器来切换模式。别忘了这一步。

4.5 如何识别设备工作在哪个标准下?

我建议各位工程师,拿到一个新设备,别急着写代码。先做下面三件事:

  1. 看数据手册:找到MDIO接口描述,确认支持的协议标准。
  2. 读寄存器0:Clause 22和Clause 45都有标准寄存器定义。Clause 22的寄存器0(控制寄存器)的bit 15是“复位”位。如果你能正常读写这个位,可能工作在Clause 22下。
  3. 尝试特殊读:Clause 45的设备,如果你用Clause 22的帧去读寄存器0x1D或0x1E,可能会读回错误数据。而Clause 22的设备,这些地址通常是厂商自定义的。

4.6 协议复用与冲突

你可能会遇到一个情况:在同一个MDIO总线上,挂了Clause 22的设备,也挂了Clause 45的设备。理论上,只要PHY地址不冲突,可以共存。但实际上,风险很大。

因为Clause 45的帧前导码和起始码与Clause 22不同。如果你的Master(通常是CPU或FPGA)没有明确切换协议模式,它发出的帧格式可能是固定的。例如,老式DSP的MDIO控制器只支持Clause 22。你硬要去访问Clause 45的设备,就得用GPIO模拟时序。

我曾经在一个项目中,CPU的MDIO控制器硬件上只支持Clause 22。为了调试Clause 45的QSPF+光模块,我不得不外挂一个CPLD来做协议转换。那真是一段苦日子。

下面这张图,清晰地展示了两种协议在帧结构和地址空间上的区别:

Clause 22 vs Clause 45 核心区别 Clause 22 帧格式(单帧类型) Preamble + Start + OP + PHY_ADDR(5) + REG_ADDR(5) + TA + DATA(16) 地址空间 设备数:32(5位地址) 内部寄存器:32(5位地址) 典型应用 百兆/千兆PHY 简单SFP模块 低成本交换芯片 Clause 45 帧格式(多种类型) 类型0:写入16位寄存器地址 类型1:写数据帧(数据+地址) 类型2:读数据帧(后增量) 类型3:读数据帧(无增量) 地址空间 设备数:32(5位地址) 内部寄存器:65536(16位地址) 典型应用 10G/40G/100G光模块 复杂SFP+/QSFP+模块 多端口高密度交换芯片

4.7 代码层面的处理

在你写MDIO驱动时,一定要做一个抽象层。把协议选择作为参数传递进去。下面是一个伪代码思路:

// 伪代码:MDIO读写抽象层
typedef enum {
    MDIO_CLAUSE_22,
    MDIO_CLAUSE_45
} mdio_protocol_t;

typedef struct {
    mdio_protocol_t protocol;
    uint8_t phy_addr;
    uint16_t reg_addr;  // Clause 22只用低5位
    uint16_t data;
    // Clause 45特有
    uint8_t dev_type;   // 设备类型(MMD)
} mdio_transaction_t;

int mdio_read(mdio_transaction_t *trans) {
    if (trans->protocol == MDIO_CLAUSE_22) {
        // 构造Clause 22帧,发送
        // 接收数据
    } else if (trans->protocol == MDIO_CLAUSE_45) {
        // 先发地址帧(类型0)
        // 再发读数据帧(类型2或3)
        // 接收数据
    }
    return trans->data;
}

这样设计,以后换设备,只需要改协议类型,上层代码不需要动。我在做FPGA MDIO控制器时,就是用这个思路,后来换了好几种PHY,代码基本没改。

我建议:即使你的设备只支持Clause 22,驱动层也预留一个protocol字段。为未来的升级做准备。经验告诉我,项目后期换PHY芯片是常有的事。

4.8 避坑总结

关于Clause 22和Clause 45,我把坑埋在这,你千万别踩:

  • 先确认设备手册:不要凭经验猜测。有的10G PHY也支持Clause 22兼容模式。
  • 注意寄存器的默认映射:Clause 45的寄存器0和1,其定义与Clause 22的寄存器0和1不完全一样。不要混用。
  • 不要忘了地址帧:Clause 45读写数据前,必须先发一个类型0帧来设置寄存器地址。这是初学者最容易犯的错误。
  • 时序参数不同:虽然都在MDIO上,但Clause 45对于帧间距、时钟频率的要求可能更严格。我在40G模块调试中就遇到过,必须降低MDC频率才能稳定读写。

嗯,Clause 22和Clause 45的区别,就讲这么多。你在实际项目中,先定位设备支持哪个协议,再下手。这点搞清楚了,后面的通信排查才能找到方向。