4. Clause 22 vs Clause 45:区别、应用场景
做MDIO通信,你一定会遇到两个标准:Clause 22和Clause 45。说白了,这就是MDIO协议的两种“方言”。很多人把这两个混着用,结果通信失败,还找不到原因。今天我带你把它们彻底搞清楚。
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完全不同。
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
4.5 如何识别设备工作在哪个标准下?
我建议各位工程师,拿到一个新设备,别急着写代码。先做下面三件事:
- 看数据手册:找到MDIO接口描述,确认支持的协议标准。
- 读寄存器0:Clause 22和Clause 45都有标准寄存器定义。Clause 22的寄存器0(控制寄存器)的bit 15是“复位”位。如果你能正常读写这个位,可能工作在Clause 22下。
- 尝试特殊读: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来做协议转换。那真是一段苦日子。
下面这张图,清晰地展示了两种协议在帧结构和地址空间上的区别:
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,代码基本没改。
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的区别,就讲这么多。你在实际项目中,先定位设备支持哪个协议,再下手。这点搞清楚了,后面的通信排查才能找到方向。