4. 输入输出延迟约束:set_input_delay / set_output_delay 在收发器中的应用

收发器接口的时序约束,跟普通GPIO完全是两码事。我刚开始做SerDes项目时,就吃过这个亏——以为set_input_delay和set_output_delay跟以前一样用就行,结果仿真全绿,上板子就挂。后来才明白,收发器的数据是跟随恢复时钟的,这玩意儿跟源同步接口有本质区别。

说白了,收发器的输入输出延迟约束,核心在于理解两个东西:时钟是哪里来的,以及数据相对于时钟的相位关系。你想想看,普通接口的时钟是PLL分出来的,收发器的时钟却是CDR从数据流里硬生生恢复出来的。这能一样吗?

4.1 收发器时序分析的特殊性

收发器内部有PMA和PCS两层。PMA负责串并转换,PCS负责编解码和时钟补偿。时序约束主要发生在两个地方:

  • FPGA逻辑到收发器TX接口:数据从FPGA fabric送到TX的PCS层
  • 收发器RX接口到FPGA逻辑:数据从RX的PCS层送到FPGA fabric

这两个接口的时钟域完全不同。TX侧用的是发送时钟(通常由参考时钟分频得到),RX侧用的是恢复时钟(CDR从数据里提取的)。

核心要点:收发器的输入输出延迟约束,本质上是约束FPGA逻辑与收发器内部PCS接口之间的时序路径。不是约束外部引脚到FPGA逻辑的路径——那是普通IO的玩法。

4.2 set_input_delay 在收发器中的应用

set_input_delay用在RX侧。数据从外部进入收发器,经过CDR和PCS,最终到达FPGA逻辑。我们需要告诉工具:数据相对于恢复时钟的相位关系是怎样的。

我习惯这样写约束:

# 假设恢复时钟是 rx_clk,数据总线是 rx_data[31:0]
# 数据在恢复时钟的上升沿被采样

create_clock -name rx_clk -period 3.2 [get_pins -hierarchical *rx_clk*]

set_input_delay -clock rx_clk -max 0.5 [get_pins -hierarchical *rx_data*]
set_input_delay -clock rx_clk -min -0.2 [get_pins -hierarchical *rx_data*]

嗯,这里要注意:恢复时钟的周期不是固定的。收发器内部有时钟补偿机制,会插入或删除时钟脉冲来匹配数据速率。所以set_input_delay里的时钟周期,应该用标称值,而不是测量值。

我在项目中遇到过一个问题:某次用10G BASE-R协议,恢复时钟的标称周期是3.2ns(322.265625MHz)。我一开始用了3.2ns,时序全过。后来换了更精确的3.1992ns,反而报了一堆违例。为什么?因为工具在做STA时,用的是理想时钟模型,你给个非整数周期,它反而算不准了。

个人经验:对于收发器接口,set_input_delay的时钟周期用标称值取整即可。收发器内部的弹性缓冲会处理时钟补偿,STA不需要关心那点ppm差异。

4.3 set_output_delay 在收发器中的应用

set_output_delay用在TX侧。数据从FPGA逻辑送到TX的PCS层,然后经过PMA串化后发送出去。约束的是FPGA逻辑到TX接口的路径。

典型写法:

# 假设发送时钟是 tx_clk,数据总线是 tx_data[31:0]
# 数据在发送时钟的上升沿被发送

create_clock -name tx_clk -period 3.2 [get_pins -hierarchical *tx_clk*]

set_output_delay -clock tx_clk -max 0.8 [get_pins -hierarchical *tx_data*]
set_output_delay -clock tx_clk -min -0.3 [get_pins -hierarchical *tx_data*]

这里有个坑:收发器的TX接口通常有内置的FIFO。这个FIFO的读写时钟不同步,写时钟是FPGA逻辑时钟,读时钟是发送时钟。set_output_delay约束的是写时钟域到FIFO的路径,不是读时钟域。

我曾经犯过一个错误:把set_output_delay的时钟设成了发送时钟,但实际数据是跟着FPGA逻辑时钟走的。结果时序分析完全乱套,修了三天才发现是时钟选错了。

避坑指南:约束TX接口时,先搞清楚数据是跟着哪个时钟走的。大多数收发器IP核会提供两个时钟:一个给FPGA逻辑用(比如txusrclk),一个给PCS内部用(比如txclk)。set_output_delay应该用FPGA逻辑侧的时钟。

4.4 收发器特有的约束技巧

收发器接口的约束,跟普通IO有几个关键区别:

对比项 普通IO 收发器接口
时钟来源 PLL/MMCM CDR恢复或参考时钟分频
数据宽度 单bit或窄总线 8/10/16/32/64位并行总线
延迟值 通常0.5~2ns 通常0.1~1ns,跟线延迟有关
时钟抖动 由PLL决定 由CDR和参考时钟共同决定

我个人习惯的做法是:

  1. 先看IP核的用户手册,找到PCS接口的时序图。手册里通常会给出setup/hold时间要求。
  2. 用set_input_delay/set_output_delay模拟这个时序。比如手册说数据在时钟上升沿前0.3ns有效,那set_input_delay -max就设0.3。
  3. 留余量。收发器接口的线延迟很难精确估算,我一般会加20%~30%的余量。

重要提醒:收发器内部的PCS层有弹性缓冲,可以吸收时钟相位偏移。但弹性缓冲的深度有限,如果set_input_delay/set_output_delay的约束太松,工具可能会把路径优化到弹性缓冲之外,导致实际工作时数据溢出或下溢。

4.5 实战:一个完整的收发器约束示例

假设我们用的是Xilinx的GTH收发器,配置成10G BASE-R模式。数据宽度是32位,内部时钟是322.265625MHz。

# 1. 创建时钟
create_clock -name rx_clk -period 3.2 [get_pins -hierarchical *rx_clk*]
create_clock -name tx_clk -period 3.2 [get_pins -hierarchical *tx_clk*]

# 2. 约束RX输入路径
# 数据在rx_clk上升沿前0.4ns有效,保持0.2ns
set_input_delay -clock rx_clk -max 0.4 [get_pins -hierarchical *rx_data*]
set_input_delay -clock rx_clk -min -0.2 [get_pins -hierarchical *rx_data*]

# 3. 约束TX输出路径
# 数据在tx_clk上升沿后0.3ns输出,保持0.1ns
set_output_delay -clock tx_clk -max 0.7 [get_pins -hierarchical *tx_data*]
set_output_delay -clock tx_clk -min -0.1 [get_pins -hierarchical *tx_data*]

# 4. 异步时钟域约束(如果RX和TX用不同参考时钟)
set_clock_groups -asynchronous -group [get_clocks rx_clk] -group [get_clocks tx_clk]

这个例子里,我特意把RX和TX的时钟设成了异步。为什么?因为10G BASE-R的RX和TX可能来自不同的参考时钟源,它们之间没有确定的相位关系。如果不加set_clock_groups,工具会去分析跨时钟域路径,报一堆假违例。

嗯,最后说一句:收发器的时序约束,没有标准答案。每个IP核的PCS接口时序都不一样,甚至同一个IP核的不同配置,时序要求也不同。我的建议是:拿到IP核后,先跑一个简单的回环测试,用chipscope抓一下数据,看看实际时序余量有多少。然后再调整约束值,做到既不过紧也不过松。

小技巧:如果你不确定set_input_delay/set_output_delay该设多少,可以先设一个保守值(比如0.5ns),然后看时序报告里的slack。如果slack很大,说明约束太松,可以收紧;如果slack为负,说明约束太紧,需要放松。反复迭代几次,就能找到合适的值。

收发器输入输出延迟约束知识体系 收发器(SerDes) PMA(串并转换) PCS(编解码/时钟补偿) RX路径(输入) 外部数据 → CDR恢复时钟 → PCS → FPGA逻辑 TX路径(输出) FPGA逻辑 → PCS → PMA串化 → 外部发送 set_input_delay 约束数据相对于恢复时钟的相位 set_output_delay 约束数据相对于发送时钟的相位 关键注意事项 时钟来源(CDR vs PLL) | 弹性缓冲深度 | 时钟域异步处理 | 留余量20%~30% 实战建议:先看IP核手册 → 模拟时序 → 留余量 → 回环测试验证 没有标准答案,每个IP核的PCS接口时序都不一样

这张图把收发器输入输出延迟约束的整个知识体系串起来了。从PMA/PCS分层,到RX/TX路径,再到具体的约束命令,最后落到实战建议。你写约束的时候,脑子里要有这张图,知道自己在约束哪一段路径。

可可精品资料库