4、GMII时钟拓扑分析:源同步时钟与系统同步时钟的区别

好,咱们开始聊时钟拓扑。这章内容挺关键——GMII接口里时钟是怎么走的,直接决定你后续的时序约束怎么建。我见过不少工程师上来就写约束,结果跑STA一堆违例,回头一看,时钟拓扑根本就没画对。

时钟拓扑说白了就是:时钟从哪来,经过哪,最后到哪。GMII接口里主要有两种拓扑方式——源同步时钟和系统同步时钟。这俩听着像,实际差老远了。

4.1 源同步时钟:随路时钟

源同步时钟,也叫随路时钟。就是发送端把数据和时钟打包一起送出去,接收端直接用这个时钟采样数据。GMII接口里,MAC发给PHY的TX_CLK,以及PHY发给MAC的RX_CLK,都是典型的源同步时钟。

我刚开始做GMII约束时,总以为时钟就是板子上统一分配的。后来发现不对——TX_CLK是MAC自己产生的,频率跟PHY的工作模式有关。比如1000Mbps时,TX_CLK是125MHz;100Mbps时,是25MHz。这个时钟跟着数据一起走,接收端(PHY)内部用PLL或BUFR把时钟转成内部工作时钟,但接口部分直接用这个源同步时钟采数据。

源同步时钟最大的特点是:时钟和数据有一个确定的相位关系。发送端会保证时钟边沿对齐到数据的中间或者边缘,接收端不需要额外恢复时钟。这样做的好处是频率能做高,因为时钟和数据在PCB上走线长度匹配,skew可控。我记得有一次项目,GMII跑1000Mbps时,PHY端死活采不到正确数据,后来发现TX_CLK走线跟数据线差了200mil,时序预算直接炸了——嗯,走等长线真的很重要。

4.2 系统同步时钟:全局共同时钟

系统同步时钟呢,就是整个系统的各模块都吃同一个时钟源,或者从同一个参考时钟分频、倍频出来。比如MAC和PHY的时钟都来自同一个晶振或PLL,然后各自用自己的内部PLL做去抖动、产生内部时钟。

在GMII接口中,如果MAC和PHY共用同一个参考时钟(比如25MHz或125MHz的晶振),这个参考时钟再分别送给MAC和PHY的PLL,那这两个PLL输出的TX_CLK和RX_CLK实际上是有频率倍数关系的,但不是同源同相位——因为两个PLL的锁相环输出之间会有随机相位偏移。

你想想看,系统同步时钟的难点在哪?MAC和PHY之间的数据交互,时钟不是严格同步的。MAC发数据时用TX_CLK,PHY采样时可能用的是自己PLL生成的RX_CLK。这两个时钟之间必然有时钟域切换,需要异步FIFO或者握手逻辑。如果直接跨时钟域采数据,那setup/hold必崩。

核心差异:

  • 源同步:发送端同时发送时钟和数据,接收端直接用该时钟采样
  • 系统同步:各模块共用一个参考时钟源,但各自生成内部时钟,存在相位不确定性

4.3 两种拓扑的对比

为了让你看得更清楚,我做了一个表格,把关键差异列出来。这个表你在写时序约束前一定要对着看一遍。

对比项 源同步时钟 系统同步时钟
时钟来源 发送端自身产生(MAC产生TX_CLK,PHY产生RX_CLK) 来自同一参考时钟源(晶振或PLL输出)
相位关系 确定,时钟边沿与数据边沿有固定对齐关系 不确定,两个PLL输出之间相位随机
时序约束方式 使用 set_input_delay / set_output_delay 建模 需要 create_generated_clock + set_clock_groups -async
跨时钟域处理 不需要(同一时钟域) 必须异步FIFO或握手
PCB走线要求 时钟和数据等长(skew控制严格) 参考时钟走线长度匹配即可
适用场景 GMII、RGMII、DDR等高速接口 SPI、I2C、UART等低速或同频接口

4.4 GMII时钟拓扑结构图

下面这张图展示了两种拓扑在GMII中的实际连接方式。你可以看到:源同步路径(实线)中,MAC的TX_CLK是MAC自己的时钟,PHY的RX_CLK是PHY自己的时钟;而系统同步路径(虚线)中,MAC和PHY共用一个系统参考时钟,各自内部PLL产生内部时钟。注意图中标出了时钟域划分——源同步下TX_CLK和RX_CLK是独立的时钟域,系统同步下两个PLL输出也是异步的。

GMII 时钟拓扑:源同步 vs 系统同步 MAC PHY 参考时钟源 TX_CLK (125MHz/25MHz) RX_CLK (125MHz/25MHz) 系统时钟 (25/125MHz) 系统时钟 (25/125MHz) PLL PLL 时钟域:TX_CLK域 时钟域:RX_CLK域 源同步时钟路径(实线) 系统同步时钟路径(虚线) PLL

小提示: 实际项目中,GMII接口通常混合使用两种拓扑:MAC的发送部分使用源同步(TX_CLK与数据一起到PHY),而接收部分PHY使用源同步(RX_CLK与数据一起到MAC)。参考时钟仅用于PLL锁定。

4.5 时序约束的差异

搞清楚了拓扑,才能写对约束。源同步时钟下,我们用 set_input_delay/set_output_delay 来描述数据相对于时钟的相位关系。举个例子:

# 约束GMII TX接口(源同步输出)
create_clock -name TX_CLK -period 8.0 [get_ports TX_CLK]
set_output_delay -clock TX_CLK -max 2.0 [get_ports {TXD[3:0] TX_EN TX_ER}]
set_output_delay -clock TX_CLK -min 0.5 [get_ports {TXD[3:0] TX_EN TX_ER}]

系统同步时钟下,通常需要创建多个生成的时钟,并声明异步关系:

# 假设系统参考时钟是25MHz晶振
create_clock -name sys_clk -period 40.0 [get_ports sys_clk]

# MAC内部PLL产生125MHz
create_generated_clock -name mac_tx_clk -source [get_ports sys_clk] \
    -multiply_by 5 [get_pins u_mac/pll/tx_clk]

# PHY内部PLL产生125MHz
create_generated_clock -name phy_rx_clk -source [get_ports sys_clk] \
    -multiply_by 5 [get_pins u_phy/pll/rx_clk]

# 声明异步
set_clock_groups -asynchronous -group mac_tx_clk -group phy_rx_clk

我看过不少新手把这两种约束搞混。明明是用源同步拓扑,却去写 set_clock_groups;或者明明是系统同步拓扑,却没加异步声明,结果STA报了一大堆违例,还以为是设计问题。嗯,要搞清楚你板子上到底是怎么走的,不要想当然。

4.6 避坑指南

我曾经遇到过: 一个项目里,MAC和PHY用的是同一个125MHz参考时钟(系统同步拓扑),但我在约束时用了源同步的 set_input_delay 建模PHY发上来的数据。结果时序分析认为数据都在同一时钟域内,setup/hold检查全是错的。最后发现:PHY内部的PLL输出RX_CLK和参考时钟之间有 ±5ns 的相位抖动,实际上数据跨了两个异步时钟域。

正确做法: 如果MAC和PHY的PLL都是从同一个参考时钟派生,但各自独立锁相,那就把这两个PLL输出视为异步时钟域。用 set_clock_groups -asynchronous 把TX_CLK和RX_CLK分开,然后在RTL里加异步FIFO或双寄存器同步。千万不能用源同步的方式建模。

我的调试习惯: 拿到一个新板子,我会先看原理图,找到参考时钟的走线。如果MAC和PHY都直接连到同一个晶振(中间没有全局缓冲),那就是系统同步;如果MAC的TX_CLK直接去PHY的RX_CLK引脚(中间有0Ω电阻或直接连),那就是源同步。拿示波器看一下时钟波形更靠谱——频率一样,但相位可能调不了。

4.7 调试实战:如何判断你的拓扑类型

如果你已经有一个跑起来的系统,但不确定怎么建约束。我给你一个简单方法:

  1. 看TX_CLK和RX_CLK在板子上是怎么连的。如果它们独立连接到MAC和PHY的时钟输入,大概率是系统同步。
  2. 用示波器同时测量TX_CLK和RX_CLK,看它们的边沿是否对齐。如果对齐(skew < 1ns),可能是源同步;如果歪歪扭扭,就是异步。
  3. 读PHY的数据手册:GMII的TX_CLK通常由MAC提供,RX_CLK由PHY提供。这本身就是源同步接口——PHY在RX_CLK上升沿发数据,MAC在同一个上升沿收数据。但如果MAC内部把RX_CLK又送进了PLL产生另外的时钟用于内部逻辑,那接收侧就变成了异步。

说白了,实践出真知。你把板子上最快的那个接口的时钟拓扑画出来,再对照表格,就知道该怎么约束了。