
1. 从一次接口调试翻车说起为什么set_output_delay值得单独拎出来讲前阵子帮一个朋友看一块FPGA板子的时序问题现象很典型功能仿真全过上板之后数据偶尔错一位换了几块板子故障率还不一样。他一开始怀疑是PCB信号完整性问题折腾了两周最后定位到约束文件里——set_output_delay写错了。这个参数在时序约束里属于那种“看起来简单、写起来随意、出问题最难查”的角色。很多刚接触FPGA或者ASIC后端的朋友对set_input_delay和set_output_delay的理解停留在“照着模板抄一个值”但真到了RGMII、DDR、SPI这类源同步接口上抄来的值往往就是错的。set_output_delay是SDCSynopsys Design Constraints时序约束体系里的一个核心命令用在FPGA和ASIC设计流程中告诉静态时序分析工具从FPGA输出端口到外部器件捕获端这段板级路径占用了多少时间预算。它不描述FPGA内部逻辑而是描述“芯片外面那一段”的时序需求。工具拿到这个值之后会反推FPGA内部输出寄存器到输出引脚之间允许的最大延迟从而判断是否满足建立时间和保持时间。这篇文章适合三类人看一是刚入门FPGA、正在学时序约束的开发者二是做接口调试、被RGMII或SPI时序折磨过的工程师三是想从“会抄约束”进阶到“理解约束”的从业者。我会把set_output_delay的计算逻辑、参数含义、不同接口下的取值方法、常见踩坑点全部拆开讲尽量做到你看完就能自己算、自己写、自己查。2. set_output_delay到底在约束什么核心原理拆解2.1 时序路径的完整视角从FPGA内部到外部器件要理解set_output_delay得先把一条完整的输出时序路径画出来。数据从FPGA内部的源寄存器出发经过FPGA内部布线、输出缓冲器到达FPGA的引脚然后经过PCB走线到达外部器件的输入引脚最终被外部器件的捕获寄存器采样。这条路径可以分成两段芯片内段和芯片外段。芯片内段由FPGA的布局布线决定工具可以通过调整布线来优化。芯片外段由PCB走线、外部器件建立/保持时间要求决定FPGA工具管不着。set_output_delay的作用就是把芯片外段的时序需求“告诉”工具让工具知道外面这段路需要花多少时间你内部必须在这个预算内完成。打个比方你要从家里寄一个快递到朋友家快递公司只负责从你家到你们小区门口这一段从小区门口到朋友家那一段是另一家快递公司负责。set_output_delay就相当于告诉第一家快递公司从小区门口到朋友家需要2小时你必须在剩下的时间里把包裹送到小区门口。如果第一家快递公司送得太慢总时间就超了。2.2 建立时间与保持时间两个方向都要管set_output_delay的值不是随便写一个数它需要同时覆盖建立时间和保持时间两个检查。对于建立时间检查工具关心的是数据到达外部器件的最晚时间对于保持时间检查工具关心的是数据到达外部器件的最早时间。具体来说set_output_delay -max用于建立时间分析它表示从FPGA引脚到外部器件捕获端的最长延迟加上外部器件的建立时间。set_output_delay -min用于保持时间分析它表示从FPGA引脚到外部器件捕获端的最短延迟减去外部器件的保持时间。这两个值通常不一样因为PCB走线的延迟有最大值和最小值受工艺、温度、电压影响外部器件的建立时间和保持时间也是不同的参数。很多初学者只写一个-max值不写-min值结果保持时间检查直接跳过上板后出现亚稳态或者数据错位。2.3 与set_input_delay的对称关系set_output_delay和set_input_delay是一对镜像命令。set_input_delay约束的是外部器件到FPGA输入引脚的路径set_output_delay约束的是FPGA输出引脚到外部器件的路径。两者的计算逻辑类似但方向相反。对于输入路径工具关心的是数据到达FPGA内部捕获寄存器的时间对于输出路径工具关心的是数据从FPGA内部源寄存器发出后能否在外部器件要求的时间窗口内到达。理解了这个对称性你在写约束的时候就不容易搞混方向。2.4 时钟定义是前提没有时钟就没有延迟set_output_delay必须配合时钟约束使用。工具需要知道参考时钟的周期、边沿位置才能计算出延迟预算。如果你没有用create_clock或者create_generated_clock定义时钟set_output_delay就是悬空的工具无法进行有意义的分析。在实际项目中输出接口的参考时钟通常有两种来源一种是FPGA内部产生的时钟通过输出引脚送给外部器件另一种是外部器件提供的时钟FPGA用它来同步输出数据。这两种情况下set_output_delay的参考时钟是不同的需要根据具体接口类型来选择。3. 参数逐项拆解-max、-min、-clock、-clock_fall怎么用3.1 -max与-min建立和保持的分水岭-max和-min是set_output_delay最核心的两个选项。-max用于建立时间分析表示外部路径的最大延迟。-min用于保持时间分析表示外部路径的最小延迟。以RGMII接口为例。RGMII在1000Mbps模式下数据在时钟的上升沿和下降沿都采样时钟周期125MHz对应8ns。外部PHY芯片的建立时间要求通常是1.2ns保持时间要求是0.8ns。PCB走线的延迟取决于长度和材料假设走线延迟范围为0.5ns到1.5ns。那么建立时间检查的set_output_delay -max应该是PCB最大延迟 PHY建立时间 1.5 1.2 2.7ns。保持时间检查的set_output_delay -min应该是PCB最小延迟 - PHY保持时间 0.5 - 0.8 -0.3ns。注意-min可以是负值这表示外部路径的延迟比PHY的保持时间还短FPGA输出数据需要“晚一点”变化才能满足保持时间要求。很多初学者看到负值就慌了其实这是正常的。3.2 -clock与-clock_fall参考时钟的选择-clock指定参考时钟的名称这个时钟必须是已经定义过的。对于源同步接口参考时钟通常是随数据一起输出的那个时钟。对于系统同步接口参考时钟是系统时钟。-clock_fall用于指定参考时钟的下降沿。在DDR类型的接口中数据在时钟的上升沿和下降沿都变化需要分别对上升沿和下降沿写约束。比如RGMII接口通常需要写两组约束一组参考上升沿一组参考下降沿。# RGMII输出约束示例 set_output_delay -clock rgmii_txc -max 2.7 [get_ports rgmii_txd*] set_output_delay -clock rgmii_txc -min -0.3 [get_ports rgmii_txd*] set_output_delay -clock rgmii_txc -clock_fall -max 2.7 [get_ports rgmii_txd*] -add_delay set_output_delay -clock rgmii_txc -clock_fall -min -0.3 [get_ports rgmii_txd*] -add_delay注意-add_delay选项它表示在已有约束的基础上追加而不是覆盖。如果不加这个选项后面的约束会覆盖前面的导致只有一组约束生效。3.3 -rise与-fall数据边沿的区分-rise和-fall用于指定数据是在时钟的上升沿还是下降沿变化。在大多数源同步接口中数据在时钟的上升沿变化所以用-rise。但在一些特殊接口中数据可能在下降沿变化需要用-fall。这个选项容易和-clock_fall混淆。-clock_fall指定的是参考时钟的边沿-rise和-fall指定的是数据本身的边沿。两者是不同的概念需要根据接口协议来区分。3.4 -reference_pin高级用法-reference_pin用于指定一个参考引脚而不是参考时钟。这在一些复杂的接口中使用比如输出时钟和数据不是同一个引脚但需要以某个引脚的信号作为参考。这个选项在实际项目中用得不多但了解它的存在有助于你阅读别人的约束文件。3.5 参数组合的常见写法在实际项目中set_output_delay的写法通常有以下几种组合接口类型参考时钟需要-max需要-min需要-clock_fall需要-add_delay系统同步系统时钟是是否否源同步SDR输出时钟是是否否源同步DDR输出时钟是是是是带输出时钟输出时钟是是视情况视情况这个表格可以作为你写约束时的快速参考。但具体值还是要根据外部器件的datasheet和PCB走线来算不能照搬。4. 不同接口场景下的取值方法与实操计算4.1 系统同步接口以SPI为例SPI是一种典型的系统同步接口FPGA作为主机输出SCK、MOSI外部从机用SCK采样MOSI。在这种情况下set_output_delay的参考时钟是FPGA内部产生的SCK时钟。假设SCK频率为50MHz周期20ns。外部从机的建立时间要求为5ns保持时间要求为3ns。PCB走线延迟范围为0.2ns到0.8ns。建立时间检查set_output_delay -max PCB最大延迟 从机建立时间 0.8 5 5.8ns。 保持时间检查set_output_delay -min PCB最小延迟 - 从机保持时间 0.2 - 3 -2.8ns。create_clock -name spi_sck -period 20 [get_ports spi_sck] set_output_delay -clock spi_sck -max 5.8 [get_ports spi_mosi] set_output_delay -clock spi_sck -min -2.8 [get_ports spi_mosi]这里有一个细节SCK本身也是输出信号它的输出延迟也需要约束。通常SCK的输出延迟约束为PCB走线延迟因为从机是用SCK的边沿来采样数据的。set_output_delay -clock spi_sck -max 0.8 [get_ports spi_sck] set_output_delay -clock spi_sck -min 0.2 [get_ports spi_sck]4.2 源同步SDR接口以RGMII 100Mbps为例RGMII在100Mbps模式下时钟频率25MHz数据在时钟上升沿采样。外部PHY的建立时间要求为1.2ns保持时间要求为0.8ns。PCB走线延迟范围为0.5ns到1.5ns。建立时间检查set_output_delay -max 1.5 1.2 2.7ns。 保持时间检查set_output_delay -min 0.5 - 0.8 -0.3ns。create_clock -name rgmii_txc -period 40 [get_ports rgmii_txc] set_output_delay -clock rgmii_txc -max 2.7 [get_ports rgmii_txd*] set_output_delay -clock rgmii_txc -min -0.3 [get_ports rgmii_txd*]4.3 源同步DDR接口以RGMII 1000Mbps为例RGMII在1000Mbps模式下时钟频率125MHz周期8ns数据在时钟的上升沿和下降沿都采样。外部PHY的建立时间要求为1.2ns保持时间要求为0.8ns。PCB走线延迟范围为0.5ns到1.5ns。对于上升沿采样set_output_delay -max 1.5 1.2 2.7nsset_output_delay -min 0.5 - 0.8 -0.3ns。 对于下降沿采样同样的计算但需要加-clock_fall和-add_delay。create_clock -name rgmii_txc -period 8 [get_ports rgmii_txc] set_output_delay -clock rgmii_txc -max 2.7 [get_ports rgmii_txd*] set_output_delay -clock rgmii_txc -min -0.3 [get_ports rgmii_txd*] set_output_delay -clock rgmii_txc -clock_fall -max 2.7 [get_ports rgmii_txd*] -add_delay set_output_delay -clock rgmii_txc -clock_fall -min -0.3 [get_ports rgmii_txd*] -add_delay这里有一个容易忽略的点RGMII的时钟和数据之间通常有一个90度相位偏移这个偏移需要在时钟约束中体现。有些设计会在FPGA内部对时钟做相位调整有些则依赖PCB走线来做延迟匹配。无论哪种方式set_output_delay的值都需要根据实际相位关系来调整。4.4 参数计算中的常见误区第一个误区是忽略PCB走线延迟的工艺角变化。PCB走线的延迟不是固定值它会随温度、电压、制造工艺变化。在计算-max和-min时需要用走线延迟的最大值和最小值而不是典型值。第二个误区是把外部器件的建立时间和保持时间搞反。建立时间是数据必须在时钟边沿之前稳定的时间保持时间是数据必须在时钟边沿之后保持稳定的时间。-max对应建立时间-min对应保持时间不能弄反。第三个误区是忘记考虑时钟本身的输出延迟。在源同步接口中时钟也是从FPGA输出的时钟的输出延迟会影响数据采样窗口。有些设计需要对时钟和数据分别写约束并确保两者之间的相位关系正确。5. 实操流程从datasheet到约束文件的完整落地5.1 第一步确认接口类型和时序参数拿到一个接口首先要确认它是系统同步还是源同步是SDR还是DDR。然后从外部器件的datasheet中找到建立时间tsu、保持时间th、时钟频率等参数。这些参数通常在datasheet的“AC Timing Characteristics”章节中。如果datasheet中没有明确给出建立时间和保持时间可能需要根据器件的输入电容、采样电路特性来估算。这种情况下建议留足够的余量或者用示波器实测。5.2 第二步获取PCB走线延迟PCB走线延迟可以通过以下公式估算延迟 走线长度 × 单位长度延迟。单位长度延迟取决于PCB材料常见的FR-4材料在表层走线的单位延迟约为140ps/inch到180ps/inch内层走线约为170ps/inch到200ps/inch。更准确的方法是使用PCB设计工具如嘉立创EDA、KiCad等的传输线计算功能输入叠层参数和走线几何尺寸得到更精确的延迟值。如果PCB已经打样可以用TDR时域反射计实测走线延迟。5.3 第三步计算-max和-min值根据前面的公式计算建立时间检查和保持时间检查的延迟值。注意要使用走线延迟的最大值和最小值而不是典型值。如果走线延迟范围未知可以按±20%估算。5.4 第四步编写约束并验证把计算好的值写入SDC文件然后在EDA工具中运行时序分析。检查时序报告确认建立时间和保持时间都满足要求。如果时序违例需要调整FPGA内部逻辑的布局布线或者修改约束值。# 完整的RGMII输出约束示例 create_clock -name rgmii_txc -period 8 [get_ports rgmii_txc] set_output_delay -clock rgmii_txc -max 2.7 [get_ports rgmii_txd*] set_output_delay -clock rgmii_txc -min -0.3 [get_ports rgmii_txd*] set_output_delay -clock rgmii_txc -clock_fall -max 2.7 [get_ports rgmii_txd*] -add_delay set_output_delay -clock rgmii_txc -clock_fall -min -0.3 [get_ports rgmii_txd*] -add_delay set_output_delay -clock rgmii_txc -max 0.8 [get_ports rgmii_txc] set_output_delay -clock rgmii_txc -min 0.2 [get_ports rgmii_txc]5.5 第五步上板实测与迭代时序分析通过不代表上板一定没问题。PCB的实际走线延迟、外部器件的实际参数、电源噪声等因素都可能影响时序。上板后需要用示波器或者逻辑分析仪实测数据采样窗口确认建立时间和保持时间都有足够的余量。如果实测发现时序余量不足可以尝试以下方法调整FPGA内部输出寄存器的位置、修改PCB走线长度、调整时钟相位、降低接口速率。6. 常见问题与排查技巧实录6.1 时序报告中的setup和hold违例怎么读时序报告中的setup违例表示数据到达太晚hold违例表示数据到达太早。对于输出路径setup违例通常意味着set_output_delay -max值太小或者FPGA内部延迟太大。hold违例通常意味着set_output_delay -min值太大或者FPGA内部延迟太小。排查时先确认约束值是否正确再检查FPGA内部逻辑的布局布线。如果约束值没问题可以尝试调整输出寄存器的位置或者插入流水线寄存器来改善时序。6.2 约束写了但工具不认常见语法错误最常见的语法错误是忘记定义时钟或者时钟名称写错。set_output_delay的-clock选项必须引用一个已经定义的时钟否则工具会报错或者忽略这条约束。另一个常见错误是端口名称写错。get_ports中的端口名称必须和顶层模块的端口名称完全一致包括大小写。如果端口是总线需要用通配符或者明确列出每个位。6.3 上板后数据偶尔出错时序余量不足的隐蔽表现数据偶尔出错是最难查的问题之一。时序余量不足时数据在大多数情况下是正确的但在温度变化、电压波动、或者特定数据模式下会出错。这种问题用功能仿真很难复现需要做时序仿真或者上板实测。排查时可以先降低接口速率看问题是否消失。如果降速后问题消失基本可以确定是时序余量不足。然后逐步调整约束值或者PCB走线找到问题根源。6.4 多die FPGA中的set_output_delay特殊处理在多die FPGA中输出路径可能跨越多个diedie之间的互连延迟需要额外考虑。这种情况下set_output_delay的值需要加上die间互连的延迟。有些工具会自动处理die间延迟有些则需要手动添加。6.5 常见问题速查表问题现象可能原因排查方法解决方案setup违例-max值太小检查datasheet和走线延迟增大-max值或优化内部逻辑hold违例-min值太大检查走线最小延迟减小-min值或调整输出寄存器数据偶尔出错时序余量不足降速测试、实测采样窗口增加余量、调整相位工具忽略约束时钟未定义或名称错误检查create_clock正确定义时钟约束冲突多条约束覆盖检查-add_delay添加-add_delay选项6.6 几个我踩过的坑第一个坑是忘记写-min约束。早期做SPI接口时只写了-max觉得保持时间应该没问题。结果上板后在某些温度下数据出错查了很久才发现是保持时间违例。后来养成习惯-max和-min必须成对写。第二个坑是PCB走线延迟估算太乐观。有一次做RGMII接口按典型值算了走线延迟结果PCB打样后实测延迟比估算值大了30%时序余量直接不够。后来在计算时都会留20%到30%的余量。第三个坑是忽略时钟输出延迟。在源同步接口中时钟和数据是一起输出的时钟的输出延迟会影响数据采样窗口。有一次只约束了数据没约束时钟结果时钟偏斜导致采样窗口偏移数据出错。后来在约束中把时钟也加上set_output_delay问题解决。7. 从约束到验证如何确认set_output_delay写对了7.1 时序报告的关键指标时序报告中需要关注以下几个指标WNS最差负裕量、TNS总负裕量、WHS最差保持裕量、THS总保持裕量。WNS和TNS用于建立时间分析WHS和THS用于保持时间分析。理想情况下这些值都应该是正数或者零。对于输出路径还需要关注输出端口到外部器件的延迟报告。有些工具会单独列出输出路径的时序分析结果包括内部延迟、输出缓冲延迟、外部延迟等。7.2 时序仿真的必要性时序仿真是在功能仿真基础上加入延迟信息可以更真实地反映上板后的时序行为。对于高速接口建议做时序仿真确认数据在外部器件采样窗口内稳定。时序仿真需要用到布局布线后的网表和SDF标准延迟格式文件。仿真时需要注意外部器件的模型是否准确PCB走线延迟是否包含在内。7.3 上板实测的几种方法上板实测可以用示波器、逻辑分析仪、或者FPGA内部的ILA集成逻辑分析仪。示波器可以直接观察数据采样窗口但需要引出测试点。逻辑分析仪可以抓取多路信号但采样深度有限。ILA可以在FPGA内部抓取信号不需要额外测试设备但只能观察FPGA内部的信号。对于输出接口建议用示波器实测外部器件输入引脚处的数据采样窗口确认建立时间和保持时间都有足够余量。如果条件允许可以在不同温度下测试确认时序余量在全温度范围内都满足。7.4 约束文件的版本管理约束文件是设计的一部分需要纳入版本管理。每次修改约束都要记录修改原因和修改内容方便回溯。特别是在多人协作的项目中约束文件的冲突和覆盖是常见问题需要建立规范的管理流程。8. 一些个人体会set_output_delay这个参数看起来只是SDC文件里的一行命令但它背后涉及的是整个接口的时序预算分配。写对了接口稳定可靠写错了上板后各种玄学问题。我自己的经验是不要怕麻烦每个输出接口都老老实实从datasheet查参数、从PCB算走线延迟、从时序报告看余量。前期多花半小时算清楚后期少花两天查问题。另外约束值不是一成不变的。PCB改版、外部器件换型号、接口速率调整都需要重新计算set_output_delay。把计算过程记录下来下次改版时直接复用能省不少时间。最后分享一个小技巧在约束文件里给每个set_output_delay加上注释写明计算依据和参数来源。过几个月再回头看你还能想起来当时为什么写这个值。