RGMII 信号组与时钟关系——先搞懂谁和谁对齐

我个人习惯,不管做什么高速接口设计,上来先把信号分组理清楚。RGMII 也不例外。它看上去只有 12 根线,但每根线什么时候有效、跟哪个时钟对齐,这里头门道不少。

我之前带过一个刚入行的同事,布线的时候把 TX_CLK 跟 RXD 拉得老长,结果板子回来死活调不通。查了三天,最后发现,其实就是没搞懂 RGMII 的“时钟-数据”归属关系。嗯,这节咱们就把这件事掰扯清楚。

核心要点:RGMII 采用源同步时钟方案。发送端随路送出时钟,接收端用该时钟采样数据。数据与时钟之间存在严格的 内部 Skew 约定,而不是简单的“时钟沿对齐”。这一点跟普通并行总线最大的不同。

信号列表:看起来少,但每根都有分工

先列个表,把 6 组 12 根信号说清楚。你把它当作设计时的信号清单核对表就行。

信号组 方向 位宽 主要功能
TXD[3:0] MAC → PHY 4 bit 发送数据,DDR 上下沿各 2 bit
TX_CTL MAC → PHY 1 bit 发送控制,DDR 编码:上沿 TX_EN,下沿 TX_ER
TX_CLK MAC → PHY 1 bit 发送参考时钟,频率为数据速率的一半
RXD[3:0] PHY → MAC 4 bit 接收数据,DDR 上下沿各 2 bit
RX_CTL PHY → MAC 1 bit 接收控制,DDR 编码:上沿 RX_DV,下沿 RX_ER
RX_CLK PHY → MAC 1 bit 接收参考时钟,由 PHY 恢复并随路送出

你看,发送路径的信号——TXD、TX_CTL、TX_CLK——全部由 MAC 输出到 PHY;接收路径则是反过来。发送和接收是完全独立的两个时钟域,互不干扰。

双沿采样:时钟频率为什么只有数据速率的一半?

RGMII 全称是 Reduced Gigabit Media Independent Interface,那个 “Reduced” 就体现在这里。它用 DDR 技术,在每个时钟的上升沿和下降沿都传输数据。所以,如果你想跑 1 Gbps,时钟只需要 125 MHz 就够了,而不是像传统 DDR 接口一样需要 250 MHz。你想想看,这对 PCB 的 SI 设计要友好得多。

发射端 MAC 在 TX_CLK 的上下沿同时送出数据,接收端 PHY 在同样的上下沿采样。但问题来了:PHY 内部怎么区分上下沿的数据属于哪一组?答案在控制信号里。

时钟沿 TXD[3:0] 含义 TX_CTL 含义
上升沿 TXD[3:2] 低 2 位(bit1:0) TX_EN(发送使能)
下降沿 TXD[1:0] 高 2 位(bit3:2) TX_ER(发送错误)

接收端逻辑同理。搞清楚这个对应关系,后面写 RTL 的时候才不会把上下沿数据拼反。

避坑经验:我在项目里遇到过好几回,同事把 TXD 上下沿的 4 bit 拼成 8 bit 时顺序搞反了。嗯,一定要先确认 PHY 的 datasheet,有些 PHY 的上沿对应低 2 位,有些对应高 2 位,虽然绝大多数标准是前者,但总有例外的器件。

时钟与数据的 Relative Skew:RGMII 的精髓

RGMII 最微妙的地方来了:数据相对于时钟不是简单的边沿对齐,而是 有固定的偏移量,也就是 internal skew。根据 RGMII v2.0 标准,这种偏移一般在 1.0 ns 到 2.6 ns 之间——对于 125 MHz 时钟(周期 8 ns)来说,相当于把数据相对时钟偏移了约 1/8 周期。

为什么这么做?我个人的理解是:DDR 模式下,如果数据和时钟边沿完全对齐,接收端的建立/保持时间窗口几乎为零,对 PCB 走线长度、芯片 PVT 变化都非常敏感。所以标准定义了一个内部 skew,让数据跳变沿避开了时钟采样沿,给了接收端一点“呼吸空间”。

参数 最小值 典型值 最大值 说明
Tskew(T) 1.0 ns 1.5 ns 2.6 ns TXD/TX_CTL 相对于 TX_CLK 的偏移
Tskew(R) 1.0 ns 1.5 ns 2.6 ns RXD/RX_CTL 相对于 RX_CLK 的偏移
时钟频率 — 125 MHz — 对应 1 Gbps 模式
总线速率 — 1 Gbps — 每个时钟沿 2 bit,共 4 bit

不过注意:上述参数是 PHY 端的要求。实际在 FPGA 做 MAC 时,数据是内部逻辑产生的,通常我们会先让数据在内部与时钟对齐,然后通过 ODDR 原语输出,再依靠 PCB 走线或 IODELAY 来创造这个 1.0–2.6 ns 的偏移。

// 典型的 ODDR 输出,数据与时钟边沿对齐
ODDR #(
  .DDR_CLK_EDGE("SAME_EDGE")
) u_oddr_txd (
  .Q(txd_o),
  .D0(txd_int_rise), // 上升沿数据
  .D1(txd_int_fall), // 下降沿数据
  .C(tx_clk),
  .CE(1'b1),
  .R(1'b0),
  .S(1'b0)
);

发送与接收时序——谁先谁后?

最后我们来画一张图,把上面这些关系串起来。我个人觉得,时序关系用文字描述再清楚也不如图来得直观。

RGMII 信号组与时钟关系总图 发送端 (MAC → PHY) TX_CLK TXD[3:0] + TX_CTL Tskew(T) 偏移 接收端 (PHY → MAC) RX_CLK RXD[3:0] + RX_CTL Tskew(R) 偏移 时钟与数据 DDR 时序示例 上升沿 下降沿 上升沿 数据有效窗口 数据有效窗口 Tskew 说明:发送端数据在时钟边沿附近的偏移量根据标准要求在 1.0–2.6 ns 之间

避坑清单:PCB 工程师常犯的错

讲完基础关系,说几个我实际踩过的坑。

  • 组内等长没做好。 TXD 四根线内部不等长,导致 Skew 超标。标准要求组内延迟差 < 50 ps,我建议按 < 30 ps 做余量。
  • TX_CLK 和 TXD 没做组间等长。 经验值是时钟比数据略长 5–10 mm(对应 0.3–0.6 ns 的偏移),这样到 PHY 时才能满足 Tskew(T) 要求。
  • 误以为数据要对齐时钟沿。 恰恰相反,RGMII 要求数据错开时钟沿。对齐反而会出时序违例。
  • RX_CLK 和 RXD 的长度匹配被忽略。 接收路径一样重要,我见过有人只调整发送端,忘了接收端同样需要控制。

实战技巧:在布板前,建议先用仿真工具跑一下快速时序分析。把 TX_CLK 到每组 TXD 的延迟差、以及他们与 TX_CTL 之间的偏差都算出来。我曾经有一个项目就是这样提前发现了 200 ps 的违例,改版一次就过了。

接收端:PHY 怎么处理时钟与数据?

PHY 内部的接收逻辑会先根据 RX_CLK 采样 RXD 和 RX_CTL,然后做上下沿数据拼接。但在 FPGA 做 MAC 时,我们接收的是 PHY 输出的信号。这时候,FPGA 输入接口必须正确处理这个 Tskew(R)。

我个人的推荐做法是:在 RX_CLK 的上升沿做一次高扇出的采样,然后再用一个 IDDR 原语做上下沿分离。不要直接用内部逻辑去采样 DDR 信号,那会非常脆弱。

// 接收端 IDDR 分离上下沿
IDDR #(
  .DDR_CLK_EDGE("SAME_EDGE_PIPELINED")
) u_iddr_rxd (
  .Q1(rxd_rise), // 上升沿数据
  .Q2(rxd_fall), // 下降沿数据
  .D(rxd_i),
  .C(rx_clk),
  .CE(1'b1),
  .R(1'b0),
  .S(1'b0)
);

这样分离出来的两个 4 bit 信号,再按高位和低位拼接成一个 8 bit 的完整数据字。

避坑提醒:曾经有个设计人员为了省资源,直接用组合逻辑在 RX_CLK 的下降沿采样数据。结果因为 PVT 变化,下降沿采样窗口几乎为零,FPGA 频繁误码。后来换成 IDDR 原语,问题彻底解决。硬件设计,该花的资源不能省。

小结——信号与时钟的关系一句话说透

RGMII 的信号和时钟关系,一句话就能概括:源同步时钟随路传输,数据相对时钟有特定偏移,接收端利用这个偏移窗口实现可靠采样。 设计中抓住 TX_CLK 和 TXD 的组内等长、组间长度差以及 PHY 支持的 Tskew 范围,基本就不会出大岔子。