4. MII 发送时序分析(TX)

MII 的发送通道,说白了就是把 FPGA 里的数据,稳稳当当送到 PHY 那边去。发错了,整包数据就废了。咱们今天就把 TX_CLK 上升沿对齐这件事彻底说透。

4.1 数据对齐的“硬规矩”

先看接口定义。MII 发送侧有这几根关键线:

  • TX_CLK:发送时钟,25 MHz(100M)或 2.5 MHz(10M)。
  • TXD[3:0]:4 位并行数据。
  • TX_EN:发送使能。
  • TX_ER:发送错误指示。

所有数据信号,都必须在 TX_CLK 上升沿被 PHY 正确采样。换句话说,你的数据必须在时钟上升沿之前和之后,保持一段时间不跳变。这俩时间就是:

  • 建立时间(Tsu):数据在时钟沿之前必须稳定的最小时间。
  • 保持时间(Th):数据在时钟沿之后必须稳定的最小时间。

IEEE 802.3 对 MII 接口的要求是:

参数 符号 最小值 单位
TXD / TX_EN / TX_ER 到 TX_CLK 建立时间 Tsu 10 ns
TXD / TX_EN / TX_ER 到 TX_CLK 保持时间 Th 10 ns

这是我个人觉得最容易踩坑的点。很多新手以为只要 Tsu 够就行,其实 Th 不够,数据一样会采错。

4.2 为什么时钟沿对齐“看起来简单,做起来难”?

你想想看,FPGA 内部用的是自己的时钟域(比如 PLL 生成的 25 MHz 或者 50 MHz),而 TX_CLK 是 PHY 给过来的。这两个时钟频率一样,但边沿对齐关系呢?

  • 理想情况:TX_CLK 和你的发送逻辑时钟同相,数据直接送出去。
  • 实际情况:PCB 走线、PHY 内部 PLL、温度电压变化,都会导致相位偏差。

为什么会有偏差?TX_CLK 是从 PHY 晶振或 PLL 出来的,经过 PHY 内部延时、PCB 走线、过孔、到 FPGA 输入引脚,再到内部全局时钟网络。同样的路径,数据信号也要走。物理环境一变,相位就不确定了。

我给个经验值:

因素 典型抖动 / 偏差 备注
PHY 内部输出抖动 ±200 ps 不同 PHY 差异大
PCB 走线延时偏差 ±100 ps (短线) 等长走线可减小
FPGA IO 单元延时偏差 ±300 ps 不同温度变化
总可用裕度 (10M/100M) 约 35~39 ns 相对于 40 ns 周期

我记得有一次调试 100M 以太网,板子跑起来时不时丢包。查了三天,发现是 TX_CLK 走线太长,和 TXD 之间的延迟差超过了保持时间。换了一颗更近的 PHY,问题就消失了。

4.3 T/TX_CLK 周期:别想着偷懒

MII 要求 TX_CLK 的周期是 40 ns(100M)或 400 ns(10M),占空比最好 50%。但更关键的是:数据必须在每个 TX_CLK 上升沿都有效,不能中间“跳过去”。

举个例子:

// 错误写法:数据不连续
always @(posedge tx_clk) begin
  if (send_enable)
    tx_data <= data_buffer[3:0];
  // 如果 send_enable 置低,tx_data 保持不变
  // 但对 PHY 来说,只要有 TX_EN 为高,它就会采 TXD
  // 这会导致 PHY 采到旧数据
end

正确的做法应该是:**只要 TX_EN 有效,TXD 必须跟着有效数据走,不能挂死**。常见的实现是:

// 正确姿势:数据连续输出
always @(posedge tx_clk) begin
  if (tx_enable)
    {txd, tx_en} <= {fifo_data, 1'b1};
  else
    {txd, tx_en} <= {4'b0, 1'b0};
end

另外有个知识点:

100M 下 TX_CLK 周期 40 ns,但 MII 的建立+保持时间要求 10 ns + 10 ns = 20 ns。如果没有处理好相位,你的有效窗口可能只剩 20 ns。配合抖动和走线偏差,一不小心就踩线。

4.4 剖析发送延时链

一条数据从 FPGA 逻辑到 PHY 采样,经过的延时链如下:

  • Tco:FPGA 内部 DFF 时钟到输出 Q 的时间(通常 1~5 ns)。
  • Tpcb:PCB 走线延迟(每英寸约 150 ps)。
  • Tsetup:PHY 的建立时间要求(10 ns)。
  • Thold:PHY 的保持时间要求(10 ns)。

你最终要满足的等式:

Tco + Tpcb ≤ Tperiod – Tsetup(建立时间约束)

Tco + Tpcb ≥ Thold(保持时间约束)

说白了,你的 Tco 不能太大(建立时间不够),也不能太小(保持时间不够)。我在项目中遇到过 Tco 太小导致保持时间违例的情况——FPGA 输出太快,PCB 走线又短,数据还没等 PHY 采样完就跳变了。解决办法是给数据加一点输出延迟。

这里有一张我自己总结的时序约束路径图,你可以参考:

MII发送时序约束核心逻辑 MII 发送时序约束核心逻辑 TX_CLK 上升沿 源同步输出 (TXD/TX_EN) 接口时序方程约束 建立时间分析 Tco + Tpcb ≤ T - Tsu 保持时间分析 Tco + Tpcb ≥ Th 周期边界检查:40ns (100M) / 400ns (10M) 关键参数说明 Tco: FPGA输出延迟 / Tpcb: PCB走线延迟 / Tsu: 建立时间 / Th: 保持时间 注意:所有参数需要预留至少 2 ns 裕度,应对温漂和制造偏差

这张图的核心要表达的是:建立时间和保持时间是一对矛盾体。你不能只抓一个,放掉另一个。

4.5 一个真实的“避坑”案例

我曾经接手过一个项目,用 Altera Cyclone IV 接 Marvell 的 PHY。100M 模式下,偶尔会丢包。波形抓到一看:TXD 和 TX_EN 在 TX_CLK 上升沿之后 8 ns 就变了,而那个 PHY 的保持时间要求是 10 ns。也就是说:

  • FPGA 输出太快了(Tco 小)。
  • PCB 走线又短(Tpcb 只有 300 ps)。
  • 导致保持时间违例。

解决办法是:给发送逻辑插入一级寄存器,增加一个时钟周期的输出延迟。就是让数据多打一拍再出去。这样 Tco 变大了,保持时间就满了。

// 修正方案
reg [3:0] txd_1d;
reg tx_en_1d;

always @(posedge tx_clk) begin
  txd_1d <= txd;
  tx_en_1d <= tx_en;
end

assign TXD = txd_1d;
assign TX_EN = tx_en_1d;
注意:如果 Tco 太大导致建立时间违例,那就要反过来——关掉输出寄存器,或者把输出逻辑放在靠近 IO 的单元里,缩短路径。

4.6 小结:发送时序的“三板斧”

  1. 先看 Tsu 和 Th:计算你剩下的裕度,小于 5 ns 就要特别注意。
  2. 走线要等长:TX_CLK 和 TXD 尽量一样长,减小差异。
  3. 输出延迟可调节:很多 PHY 支持调整采样沿,或者加/减延迟。如果 FPGA 端能插寄存器,就用寄存器。