第4章 MII数据发送时序:发送时钟与数据对齐关系,波形图详解,时序参数范围
做MII接口设计,发送时序是绕不开的第一道坎。说白了,你要把数据从MAC侧送到PHY侧,时钟怎么对齐、数据什么时候动、使能信号怎么配合——这些搞不清楚,调板子的时候就会一脸懵。我自己刚接触MII时,就被发送时序坑过一次,后面细说。
4.1 发送时钟TX_CLK是谁提供的?
先明确一个基本点:TX_CLK由PHY产生,送给MAC。频率取决于你的速率——10Mbps模式下2.5 MHz,100Mbps模式下25 MHz。这个时钟是所有发送信号的基准,MAC必须在TX_CLK的边沿把数据准备好。
核心原则:MAC侧输出的TXD[3:0]和TX_EN,必须与TX_CLK的某个边沿保持固定的时序关系。标准MII要求以TX_CLK的上升沿为参考。
嗯,这里要注意:有些初学者会以为时钟是MAC送出去的,翻数据手册才发现反了。我当年第一次调MII,拿着示波器到处找TX_CLK的源头,折腾了半天才意识到是PHY给的——避坑指南:先看时钟方向,再谈时序。
4.2 数据与时钟的对齐关系
MII发送时序里,数据怎么对齐?我画个简单的图帮你理解。下图展示了TX_CLK、TXD和TX_EN之间的相对位置关系。
波形里你能看到几个关键点:
- TX_CLK:连续方波,上升沿是采样时刻。PHY在每个上升沿锁存TXD和TX_EN。
- TXD[3:0]:数据在时钟上升沿前后必须稳定。图中每个时钟周期对应一个半字节(nibble)数据。
- TX_EN:发送使能,高电平时表示TXD上的数据有效。帧开始前拉高,帧结束后拉低。
我的一个小技巧:看波形时别只盯着数据,把TX_EN当成“数据有效”的旗帜。TX_EN为低时,TXD上的值可以被忽略,PHY不会把它当成有效数据。这能帮你快速定位帧边界。
4.3 建立时间与保持时间——PHY对MAC的“硬约束”
你想想看,PHY在TX_CLK上升沿抓数据,数据必须在上升沿前后各稳定一段时间。这就是建立时间(tsu)和保持时间(thold)。MII标准对这两个参数有明确规定,我整理了一个表格:
| 参数 | 符号 | 最小值 | 最大值 | 说明 |
|---|---|---|---|---|
| 建立时间 | tsu | 10 ns | — | 数据在时钟上升沿前需稳定 |
| 保持时间 | thold | 10 ns | — | 数据在时钟上升沿后需保持 |
| 时钟周期(100M) | Tclk | 40 ns | 40 ns | 25 MHz ± 100 ppm |
| 时钟周期(10M) | Tclk | 400 ns | 400 ns | 2.5 MHz ± 100 ppm |
| 时钟高电平宽度 | thigh | 14 ns(100M) | — | 占空比35%~65% |
| 时钟低电平宽度 | tlow | 14 ns(100M) | — | 占空比35%~65% |
从表格能看出来:100M模式下,建立时间和保持时间都是10 ns。时钟周期40 ns,留给数据的有效窗口其实只有20 ns左右。对于FPGA或ASIC的IO时序约束来说,这个窗口并不宽裕。
避坑指南:我曾经在一个项目中,MAC侧输出的数据在TX_CLK上升沿之前只稳定了7 ns,PHY芯片的 datasheet 写的 tsu 要求10 ns。结果就是跑10M没问题,一上100M就随机丢包。用示波器一抓,建立时间违规了。后来在RTL里加了一级流水线输出,才把时序调回来。所以,一定要在综合阶段给IO路径留够余量。
4.4 TX_EN与TXD的配合时序
TX_EN和TXD的配合,说白了就一句话:TX_EN拉高的时刻,TXD上的数据必须已经有效。但实际设计时有个细节容易被忽略——TX_EN的拉高时机和TXD的第一字节之间,可能有半个到一个时钟周期的偏差。
我个人的处理习惯是这样的:
- 帧开始:在TX_CLK上升沿之前,先把TX_EN拉高,同时把第一个半字节数据放到TXD上。
- 帧中间:每个时钟上升沿,TXD切换为下一个半字节,TX_EN保持高电平。
- 帧结束:最后一个数据送出后,在下一个时钟上升沿之前把TX_EN拉低。TXD可以变为任意值(通常置0或高阻)。
这段逻辑写成RTL的话,大概是这样:
// MII发送接口 - 时序控制示例
always @(posedge tx_clk or negedge rst_n) begin
if (!rst_n) begin
tx_en_reg <= 1'b0;
txd_reg <= 4'b0;
end else begin
tx_en_reg <= tx_en_next; // 下一拍使能
txd_reg <= txd_next; // 下一拍数据
end
end
// 组合逻辑:根据帧状态机生成下一拍的值
always @(*) begin
case (tx_state)
TX_IDLE: begin
tx_en_next = 1'b0;
txd_next = 4'b0;
end
TX_ACTIVE: begin
tx_en_next = 1'b1;
txd_next = fifo_data; // 从FIFO取数
end
TX_END: begin
tx_en_next = 1'b0;
txd_next = 4'b0;
end
default: begin
tx_en_next = 1'b0;
txd_next = 4'b0;
end
endcase
end
这段代码里,我用了寄存器输出,保证tx_en_reg和txd_reg在时钟上升沿之后才变化。这样做的好处是:PHY在上升沿采样时,数据刚好稳定。说白了就是让数据晚半个周期输出,给建立时间留余量。
我的设计习惯:MII发送路径上,我几乎不用纯组合逻辑输出。寄存器打一拍再出去,虽然多了一个时钟周期的延迟,但时序收敛会轻松很多。尤其是当PCB走线较长、信号完整性不太理想时,这一拍能救你命。
4.5 时序参数范围——你不能越过的红线
MII标准定义的时序参数,本质上是MAC和PHY之间的“握手协议”。我把它归纳成三个核心维度:
- 时钟边沿对齐:数据变化必须与TX_CLK的上升沿错开,不能刚好在边沿上跳变。标准要求数据变化发生在上升沿之后至少thold时间,以及下一个上升沿之前至少tsu时间。
- 使能与数据对齐:TX_EN和TXD在同一个时钟周期内同时变化。如果你把TX_EN提前一个周期拉高,PHY可能会把无效数据当成有效帧处理。
- 抖动容限:时钟抖动不能超过±1 ns(100M模式下)。抖动过大会吃掉建立/保持时间的余量。
我见过一个案例:有人把MII的TX路径当作普通GPIO来约束,没设输入延迟和输出延迟。结果综合工具认为路径有20 ns的余量,实际上PCB走线长了5英寸,再加上温漂,建立时间只剩2 ns。板子一开始能跑,温度一高就挂。避坑指南:一定要在SDC里给MII接口加上正式的IO时序约束,不要偷懒。
4.6 一个小结——发送时序的3个“别忘”
发送时序设计的三个核心点:
- 别忘:数据必须在TX_CLK上升沿之前tsu稳定,之后thold保持——这是PHY能正确采样的前提。
- 别忘:TX_EN是数据的“合法性标签”,它的拉高拉低时刻必须和数据对齐,不能超前或滞后一个周期。
- 别忘:PCB走线和时钟抖动会吃掉时序余量,设计时至少要留30%的余量,别卡着最小值算。
嗯,MII发送时序看起来简单,但实际调起来往往是小问题卡人。记住:时钟是PHY给的,数据是MAC送的,使能是看门的。这三者对齐了,发送通路就稳了。