MII 时钟与数据时序基础

MII 接口的时钟,说白了就是整个链路的节拍器。PHY 把时钟送出来,MAC 跟着这个节拍采样数据。

我刚开始做 100M 以太网的时候,总觉得时钟频率是固定的,不就是 25MHz 嘛。结果第一次调板子,抓出来的数据全是乱码……后来才发现,时钟的占空比和相位,比我想象的要敏感得多。

时钟频率:25MHz 与 2.5MHz 的由来

MII 有两种工作模式:

  • 100Mbps 模式:时钟频率 25MHz,时钟周期 40ns。数据线 TXD[3:0] 和 RXD[3:0] 每个时钟沿传输 4 bit,所以线速 = 25M × 4 = 100Mbps。
  • 10Mbps 模式:时钟频率 2.5MHz,时钟周期 400ns。同样每时钟 4 bit,线速 = 2.5M × 4 = 10Mbps。

你想想看,同样一根数据线,100M 模式下每秒要跳变 25M 次,2.5M 模式下只跳变 2.5M 次。时序裕量完全不是一个量级。

模式时钟频率时钟周期每时钟采样 bit 数数据速率
100Mbps25 MHz40 ns4100 Mbps
10Mbps2.5 MHz400 ns410 Mbps

我的习惯:项目初期先确定 PHY 的工作模式,然后计算时钟周期和建立保持时间裕量。100M 模式下,40ns 的周期里做时序收敛并不难,但 2.5M 模式因为周期太长,反而容易忽略毛刺和噪声——信号抖动在 400ns 窗口里看起来很小,但积累效应会导致偶发错误。

数据与时钟的相位关系

MII 标准规定:数据在时钟的上升沿被采样。也就是说,MAC 在接收端看到时钟上升沿时,数据线上的电平必须是稳定的,并且要在上升沿之后继续保持一段时间。

这里有三个关键参数:

  1. 建立时间(Tsu):数据必须在时钟上升沿之前 至少 稳定多长时间。MII 典型值:2ns(100M)或 10ns(10M)。
  2. 保持时间(Thd):数据必须在时钟上升沿之后 至少 保持稳定多长时间。典型值:2ns(100M)或 10ns(10M)。
  3. 时钟到数据输出延迟(Tco):PHY 在时钟边沿之后多久才把数据推到输出引脚上。这也是影响时序裕量的关键。

我在项目中遇到过一个问题:PHY 输出的时钟和数据的相位关系完全符合规范,但因为 PCB 走线长度不匹配,数据信号比时钟信号晚了将近 4ns。直接导致建立时间只剩 0.5ns,FPGA 内部时序检查疯狂报错。嗯,后来老老实实做了等长走线,问题就解决了。

核心要点:MII 时钟是 MAC 的采样参考。PHY 保证数据相对于时钟的边沿关系,但 PCB 布局布线会改变实际相位。千万不要期望 PHY 输出的时钟和数据在芯片引脚上就完美对齐——走线延迟、驱动器差异、温度变化都会带来偏斜。

时序参数速查表

参数100Mbps10Mbps说明
时钟周期40 ns400 ns——
时钟高电平脉宽20 ns ± 5%200 ns ± 5%PHY 输出占空比影响采样窗口
Tsu(建立时间)≥ 2 ns≥ 10 ns数据在时钟前稳定的最小时间
Thd(保持时间)≥ 2 ns≥ 10 ns数据在时钟后保持的最小时间
Tco(时钟→数据输出)≤ 10 ns≤ 50 nsPHY 内部延迟,随温度和电压变化

上面这些值是我从 IEEE 802.3 标准里摘出来的,但实际芯片的数据手册会给出更严格的数值。我曾经踩过一个坑:某款 PHY 手册上写的 Tco 典型值是 8ns,但实测在高温下跑到了 12ns。本来 2ns 的建立时间裕量直接变成负的。从那以后,我设计时为 Tco 留了 50% 的余量,并做了温度仿真。

常见的相位问题与对策

  • 问题1:时钟抖动过大
    抖动会让采样边沿位置不确定。如果抖动峰峰值超过 1ns,在 100M 模式下就很危险。
    我的做法:在 FPGA 内部用 PLL 重新生成采样时钟,滤掉高频抖动。
  • 问题2:数据与时钟的走线延迟不匹配
    我之前调试的一块四层板,时钟走线比数据走线长了 5cm,延迟差约 0.25ns,虽然不大,但加上 PHY 本身延迟后,建立时间只剩 0.8ns。后来我把时钟走线绕了回来,长度误差控制在 ±2mm 以内。
  • 问题3:PHY 输出时钟的占空比不是 50%
    有的 PHY 在 100M 模式下输出时钟占空比只有 45%/55%。这意味着高电平脉宽变成 18ns,低电平 22ns。如果采样边沿正好卡在低电平的中间,数据稳定窗口会缩小。
    我曾经遇到:某个 PHY 数据手册写占空比 45%~55%,我以为没问题,结果板子上电后偶发丢包。最后加了可编程延迟线,每个板子都做校准。

特别提醒:不要盲目相信 PHY 芯片的默认寄存器配置。很多 PHY 内部有时钟偏斜调整寄存器(比如 RTL8211 的 TX skew 控制寄存器)。我习惯在初始化代码里强制将输出时钟与数据的时序关系设置为“中心对齐”或“边沿对齐”,然后通过眼图测试确认。

时序眼图与相位关系

下面这张图展示了 MII 在 100M 模式下时钟与数据的理想相位关系。注意数据在时钟边沿附近必须稳定,且建立时间和保持时间窗口要足够。

MII 100Mbps 时序:时钟与数据相位关系 0ns 10ns 20ns 30ns 40ns 50ns 60ns 70ns 80ns 数据(RXD[3:0] / TXD[3:0]) 上升沿 Tsu Thd 数据抖动 建立/保持时间窗口 数据必须在上升沿前后稳定

坦白说,上面这张 SVG 图只是一个理想化的示意。实际示波器抓到的波形会有过冲、振铃和噪声。我习惯在 PCB 设计阶段就预留时钟和数据信号的串阻位置,通过调整串阻值来改善信号质量,间接优化时序。

避坑指南:我曾经犯过的错

  • 把时钟当普通信号处理:MII 时钟是高速单端信号,必须走内层(如果可能),临层要有完整地平面。我之前一块板子把时钟走在表层,旁边还有一根大电流电源线,结果时钟上耦合了 200mV 的噪声,直接导致采样错误。
  • 忽略 PHY 输出时钟的抖动规格:某次选用的 PHY 手册没写抖动,我默认“没问题”。结果量产时 10% 的板子在 85°C 下丢包。后来查下来是 PHY 内部 PLL 产生的时钟抖动超标。从那以后,我要求供应商提供抖动实测报告。
  • 没有做时序裕量计算:很多人以为只要在 FPGA 里用 always @(posedge clk) 就能抓到数,完全不管建立保持时间。我建议:在综合报告里找到 MII 输入 register 的 setup/hold slack,如果小于 0.5ns 就要警惕。我曾经有个项目 slack 只有 0.2ns,结果换了另一批 FPGA(工艺角不同)就直接失效了。

一句话总结:MII 的时钟和数据时序并不复杂——记住 25MHz / 2.5MHz 两种频率、数据在时钟上升沿采样、建立保持时间至少 2ns(100M)就够了。难的是把理论转换成可靠的物理设计。走线等长、电源去耦、时钟源抖动控制,每个环节都会影响你的时序裕量。