三、操作码(Op Code):访问类型与控制
操作码是MDIO帧里最核心的域。它只有2个比特,但决定了整帧干的是读还是写。嗯,说白了,它就像一把钥匙,告诉PHY芯片你要开门取数据,还是把数据送进去关门。
我在调试第一块PHY芯片时,曾经因为操作码写反了,导致读回来的寄存器值永远不对。后来花了整整一个下午才定位到问题——发送了01(写入)却想读取应答数据。从那以后,我对这2个比特格外小心。
2比特的定位
操作码位于帧的第3和第4个比特位,紧跟在起始码后面。你想想看,MDIO帧总共才46比特(包括空闲周期),这2个比特就像行程单上的目的地。错了,整个通信就全乱了。
编码其实很简单,只有四种组合:
| OP码[1:0] | 访问类型 | 数据方向 | 数据阶段长度 |
|---|---|---|---|
| 10 | 读取 | 从PHY到MAC | 16位数据 + 可选扩展位 |
| 01 | 写入 | 从MAC到PHY | 16位数据 + 可选扩展位 |
| 00 | 地址扩展 | 从MAC到PHY | 16位地址扩展 + 必须扩展位 |
| 11 | 地址扩展 | 从MAC到PHY | 16位地址扩展 + 必须扩展位 |
读取操作码(10):从PHY获取数据
读取操作码是10,注意它不是二进制两位的数字10,而是比特组合:最高位是1,最低位是0。这个我也记错过很多次。
执行读取时,MAC控制器按照以下顺序发送:
- 前同步码(32个1)
- 起始码(00)
- 操作码(10)
- PHY地址(5比特)
- 寄存器地址(5比特)
- 转向码(Z状态)
- 数据(16比特,从PHY输出)
我建议初学者注意一个细节:读取操作在转向码之后,数据线方向会发生变化。MAC突然变成输入,PHY开始驱动数据。如果时序设计不好,这里容易产生竞争——两边同时驱动。
避坑指南:我曾经在一个项目中,因为MAC端的输出使能没有及时关闭,导致读取数据时总线冲突。PHY还在发送Z状态时,MAC却已经释放了总线——但释放速度太慢。结果地址最后一位与数据第一位重叠了。解决办法就是在转向码周期,强制MAC的输出使能提前一个周期关闭。
写入操作码(01):将数据发送到PHY
写入操作码是01,这个组合很有意思——它和读取正好相反。MAC在发送完寄存器地址后,继续保持驱动,直接输出16位数据。所以写入帧里没有转向码,MAC从头到尾都是主机。
写入操作的一个好处是,你不用等PHY应答。写完后直接发下一个帧就行。但是,嗯,要注意——PHY内部写操作的状态机可能还没完成。我一般会在连续写入之间加一个少量延时,或者读回刚写的数据确认一下。
个人习惯:我在做PHY驱动时,只要遇到关键寄存器(比如复位、掉电、速度切换),会在写入后立刻读回验证。这个习惯帮我抓住过两次硬件bug——一次是PHY的写入地址线虚焊,另一次是MDIO总线上有毛刺导致写入不对。
地址扩展操作码(00和11):非常规用法
地址扩展是MDIO协议里一个冷门设计。为什么需要它?因为标准的5比特PHY地址和5比特寄存器地址只能访问32个设备、每个设备32个寄存器。对于复杂的芯片(比如带有多个端口的PHY),可能不够用。
地址扩展操作码有00和11两种,它们的作用是一样的:将当前帧扩展为地址帧,不包含数据,而是包含额外16位地址信息。扩展地址可以级联,用于访问更深层次的寄存器空间。
但我在实际项目中,坦白说——基本没见过有人用这个功能。为什么呢?因为大多数商用PHY芯片都把寄存器映射在了前32个地址内,不需要扩展。我待过的公司,产品手册里地址扩展操作码那一章几乎都是空的。
注意:如果在不支持地址扩展的PHY上发送00或11,PHY会怎么处理?嗯,大部分PHY会忽略这个帧,但有些PHY(尤其是一些老型号)会进入一个不确定状态。我遇到过一次,给某款PHY发了地址扩展帧,结果它卡死了,必须要硬件复位才恢复。所以我的建议是:除非芯片手册明确支持,否则不要碰地址扩展操作码。
操作码与帧结构的对应关系
为了让你更直观地理解,我画了一张图,展示不同操作码在帧中的相对位置。
总结一下
- 读取(10):最常用,从PHY读取寄存器值。数据方向改变一次。
- 写入(01):把数据写进PHY寄存器。数据方向不变。
- 地址扩展(00+11):用于访问更深地址空间,实际很少用。
操作码虽然简单,但它是整帧语义的开关。你想想看,就2个比特,决定了整个读取还是写入。在MDIO协议里,这种简洁的设计随处可见——用最小的开销完成最大的功能。