4. MDIO 读写时序:读操作时序、写操作时序、时序参数要求
好,咱们这一节开始啃 MDIO 的时序。
说实话,协议这个东西,光看文字描述可能会觉得有点虚。你想想看,时序图摆在那,高低电平来回跳,到底哪一段是地址,哪一段是数据,什么时候该采样,什么时候可以放松一点,我刚开始玩 PHY 的时候也被绕晕过。后来硬着头皮拿着示波器去抓波形,一个一个比特对照规格书,心里才踏实下来。
这一节,我就把读操作、写操作,还有那些容易踩坑的时序参数要求,掰开了跟你讲。
4.1 读操作时序:从发起请求到拿到数据
MDIO 的读操作不是一次就完成的,它分两个阶段:先写地址,后读数据。协议本身是利用同一个帧结构来承载的。
一个完整的读操作帧长 64 个时钟周期(这里指的是 MDC 时钟的上升沿),具体我们拆开来看:
- Preamble(前导码):32 个连续的 '1'。用来让 PHY 同步到 MDC 时钟上。我个人习惯用一段循环去发,别偷懒少发,有的老 PHY 对前导码数量有要求。
- Start(起始位):2 个比特 '01'。这是帧开始的标志。
- OP Code(操作码):2 个比特 '10'。表示这是一个读操作。
- PHYAD(PHY 地址):5 个比特。选择你要跟哪个 PHY 芯片通信。
- REGAD(寄存器地址):5 个比特。选择 PHY 内部的哪个寄存器。
- TA(转向时间):2 个比特 'Z0'。这里是个特殊的地方。第一个比特是高阻态(由 MAC 释放总线),第二个比特是 PHY 把数据线拉低,表示准备输出数据。
- Data(数据):16 个比特。PHY 在这个时间段内把寄存器的值放到总线上,由 MAC 端读取。
重点理解 TA 状态
TA 的第一个比特是总线控制权的交接点。MAC 在发完 REGAD 后,必须把 MDIO 输出置为高阻态,然后 PHY 会在下一个时钟周期把驱动权拿过来。如果 MAC 没有释放总线,数据线就会打架,你读回来的数据铁定是错的。我以前遇到过一次,查了半天才发现是 FPGA 代码里输出使能没拉低。
4.2 写操作时序:把数据配进寄存器
写操作相比读操作要简单一点,因为全程都是 MAC 在驱动总线,没有控制权交接的问题。它的帧结构也是 64 个时钟周期:
- Preamble(前导码):32 个连续的 '1'。跟读操作一样,不能少。
- Start(起始位):2 个比特 '01'。
- OP Code(操作码):2 个比特 '01'。注意,这里是 '01',不是 '10',这是区分读和写的关键。
- PHYAD(PHY 地址):5 个比特。
- REGAD(寄存器地址):5 个比特。
- TA(转向时间):2 个比特 '10'。写操作的 TA 没有高阻态,MAC 直接驱动为 '1' 和 '0'。
- Data(数据):16 个比特。MAC 把要写的数据发出去,PHY 在此时采样并更新内部寄存器。
一个小技巧:
写操作的时候,有些 PHY 会要求你在写完数据后,再发一个“空读”操作(读同一个地址),来确保数据已经被正确地 latch 进去了。我一般会在写操作后面加一个读操作做回读校验,用代码来验证写值。
4.3 时序参数要求
空谈时序没意义,我们必须看具体的数字。MDIO 标准(IEEE 802.3)给了一些推荐参数,但实际项目中,由于 PCB 走线、温度、电压的差异,你最好留点余量。
下面这个表格是我从多份 PHY 数据手册里整理出来的典型值,你可以收藏一下:
| 参数名称 | 符号 | 最小值 | 典型值 | 最大值 | 单位 |
|---|---|---|---|---|---|
| MDC 时钟频率 | fMDC | - | 2.5 | 15 | MHz |
| MDC 高电平时间 | tHIGH | 30 | 200 | - | ns |
| MDC 低电平时间 | tLOW | 30 | 200 | - | ns |
| MDIO 数据建立时间(对 MDC 上升沿) | tSU | 10 | - | - | ns |
| MDIO 数据保持时间(对 MDC 上升沿) | tHOLD | 10 | - | - | ns |
坑点一:时钟频率别跑满
有些 PHY 宣称支持 15 MHz MDC,但当你真跑到 15 MHz 的时候,如果 PCB 走线长了或者芯片温度高了,时序余量会变得很小。我曾经在一个产品上发现,跑到 12.5 MHz 以上时,偶尔会有寄存器读出来是 0xFFFF 的异常现象。后来降频到 8 MHz,问题就消失了。所以,除非你确认布线非常好,否则我建议把 MDC 控制在 2.5 MHz 到 10 MHz 之间。
坑点二:数据建立和保持时间
tSU 和 tHOLD 是相对 MDC 上升沿而言的。MAC 在 MDC 上升沿之前,必须把 MDIO 的数据稳定下来;PHY 在上升沿采样。对于读操作,PHY 会保证在 MDC 下降沿之后输出数据,然后在下一个上升沿之前数据已经稳定。所以,你的硬件设计一定要保证 MDC 和 MDIO 两根线在板级上的时延差尽量小。如果有条件,可以让 MDC 走线比 MDIO 短一些(或者用蛇形线让两者等长),减少 skew。
4.4 MDIO 读写时序流程图
下面这张图,我把读和写的帧结构放在一起对比,方便你直观理解:
4.5 实际调试中的几个建议
嗯,理论讲完了,说点实战中我总结的想法:
- 读操作一定要做超时处理:万一 PHY 挂死或者没有应答,你的 MDC 时钟不能一直卡在那。我习惯在代码里加一个计数器,如果 64 个时钟后还没收到有效的 TA 就判超时,然后重新发前导码去复位总线状态。
- 写操作可以适当放宽时钟占空比:有些 PHY 对高电平时间要求不那么严,只要别低于 30 ns 就好。我一般让高电平和低电平各占一半,但在某些低功耗场景下,我会把高电平拉长一点(比如 40% 占空比),让 PHY 有更充裕的采样窗口。
- 尽量避免连续背靠背操作:我遇到过一种情况,连续发读操作,帧之间的 Idle 位太少,PHY 的内部状态机没复位过来,导致第二帧的地址被当成数据解析了。所以,我通常在两次操作之间插入一个至少 2 个 MDC 周期的 Idle(让 MDIO 保持高电平),让总线回归空闲状态。
一个小工具:
如果你手头有逻辑分析仪,可以把 MDC 和 MDIO 两根线夹上去,然后用协议解析功能直接看。很多分析仪默认就支持 MDIO 协议解析,一眼就能看出 OP code 是不是 '10' 还是 '01',再也不用手动数比特了。