XDC 约束与时序收敛
目标:把真实系统翻译成 STA 模型
跳转到“目标:把真实系统翻译成 STA 模型”XDC 不是为了“消除红色数字”,而是向 Vivado 描述真实硬件环境:
- 时钟从哪里来,周期、波形、相位和抖动是什么。
- FPGA 外部器件何时发出数据,何时采样数据。
- 哪些路径必须在一个周期内完成,哪些路径确实有特殊协议。
- 顶层端口连接到哪个封装引脚,使用什么电气标准。
flowchart LR A["原理图与器件手册"] --> B["时钟 / 接口时序模型"] B --> C["XDC"] C --> D["对象命中与覆盖检查"] D --> E["STA 报告"] E --> F["修改 RTL / 约束 / 物理实现"] F --> D一份合格的 XDC 必须同时满足三件事:数值有物理来源、命令命中正确对象、报告证明没有漏约束或误切路径。
先理解 STA 在检查什么
跳转到“先理解 STA 在检查什么”典型寄存器到寄存器路径:
launch clock -> source register -> combinational path -> destination register <- capture clock| 检查 | 直观问题 | Slack |
|---|---|---|
| Setup / max delay | 新数据能否在目标采样边沿前稳定 | required time - arrival time |
| Hold / min delay | 旧数据能否在目标采样边沿后保持足够久 | arrival time - required time |
Slack 大于等于 0 表示该项通过。Vivado 还会分析 input-to-register、register-to-output、input-to-output、异步控制恢复/移除和时钟脉宽等路径。
约束不能修复功能错误。异步输入没有同步器、握手协议错误、总线撕裂或复位释放不可靠时,即使时序报告全绿,设计仍可能失败。
写 XDC 前先填两张表
跳转到“写 XDC 前先填两张表”时钟表
跳转到“时钟表”| 字段 | 示例 | 来源 |
|---|---|---|
| 名称 | sys_clk | 工程命名 |
| 物理来源 | 板载 100 MHz 晶振 | 原理图 |
| FPGA 端口/引脚 | sys_clk / E3 | 顶层 RTL、原理图 |
| 周期与占空比 | 10 ns / 50% | 振荡器手册 |
| 抖动 | 0.08 ns | 振荡器、PLL 手册 |
| 衍生关系 | MMCM 输出 200 MHz | Clocking Wizard 配置 |
| 与其他时钟关系 | 同源、异步或互斥 | 系统架构 |
接口表
跳转到“接口表”| 字段 | 输入接口示例 | 输出接口示例 |
|---|---|---|
| 数据端口 | adc_data[7:0] | dac_data[7:0] |
| 参考时钟 | ADC 转发 adc_dco | FPGA 转发 dac_clk |
| 采样边沿 | 双边沿 | 上升沿 |
| 外部参数 | tCO(min/max) | tSU、tH |
| 数据走线 | min/max 延迟 | min/max 延迟 |
| 时钟走线 | min/max 延迟 | min/max 延迟 |
| 电压与引脚 | Bank、VCCO、IOSTANDARD、PACKAGE_PIN | 同左 |
没有这两张表时,不要凭经验填写 set_input_delay、set_output_delay 或时序例外。
按接口类型选模型
跳转到“按接口类型选模型”| 接口类型 | 典型例子 | 时钟模型 | 主要约束 |
|---|---|---|---|
| 单一板载时钟驱动内部逻辑 | MCU/控制 FPGA | 端口上的 primary clock | create_clock |
| System-synchronous | FPGA 与外设共享板级参考时钟 | 物理时钟 + 同波形 virtual clock | input/output delay |
| Source-synchronous 输入 | ADC DCO、RGMII RX | 数据随外部转发时钟进入 | 端口 clock + input delay,DDR 时双边沿 |
| Source-synchronous 输出 | DAC、RGMII TX | FPGA 输出数据和转发时钟 | generated forwarded clock + output delay |
| 完全异步控制 | 按键、中断、UART RX | 与内部时钟无固定相位 | CDC 结构 + 精确例外 |
| 异步多位数据 | 双时钟 FIFO、Gray pointer | 两个独立时钟域 | CDC/FIFO + 必要的 max delay/bus skew |
| 多工作模式 | 调试/运行时钟 MUX | 每个模式的真实时钟 | exclusive clock group 或逐模式 case analysis |
推荐的文件结构
跳转到“推荐的文件结构”小型工程可以使用一个 top.xdc。大型工程按职责拆分:
constraints/ 00_clocks.xdc # 主时钟、虚拟时钟、生成时钟、抖动 10_io_timing.xdc # input/output delay 20_exceptions.xdc # CDC、false path、multicycle、max delay 30_io_physical.xdc # 引脚、电气标准、DRIVE、SLEW约束按顺序解释,引用时钟前必须先创建时钟。Non-Project Mode 应显式按顺序 read_xdc;Project Mode 应检查 Constraints Sources 的处理顺序。不要仅依赖文件名排序。
每个 XDC 文件只约束一个明确边界。板卡固定引脚可单独维护,IP 自带 XDC 不要复制到顶层后再重复约束。
第一原则:先保证对象匹配正确
跳转到“第一原则:先保证对象匹配正确”Vivado XDC 使用 Tcl 对象集合,不是简单字符串。.xdc 采用受限的 Tcl/SDC 命令集,不支持把任意 Tcl 程序都写进去;例如 Vivado 2020.2 会拒绝 XDC 内的自定义 proc。复杂检查应放到综合后执行的独立 .tcl 脚本。
| 查询 | 对象范围 | 常见用途 |
|---|---|---|
get_ports | 顶层端口 | 时钟输入、I/O delay、封装引脚 |
get_pins | 实例引脚 | 生成时钟源、寄存器 D/C 引脚 |
get_cells | 层次实例/综合网表单元 | 同步器、特定寄存器组 |
get_nets | 网表连线 | 少数物理或调试场景 |
get_clocks | 已创建/自动推导的时钟 | I/O delay、时钟组、报告 |
在 Tcl Console 或综合后验证脚本中,可先观察匹配结果。总线和通配模式必须用大括号保护,避免 [] 被 Tcl 当作命令替换:
set adc_data [get_ports -quiet {adc_data[*]}]set sync_ff1 [get_cells -quiet -hier -filter {NAME =~ */sync_ff1_reg[*]}]还要区分层级搜索与完整路径:get_cells -hierarchical 在逐层搜索时对每一级的名称匹配,普通 pattern 不应再带 u_slow/ 这样的层级前缀。已知相对于当前实例的完整路径时,使用 get_cells {u_slow/src_reg[*]};需要跨层筛选时,用 -hierarchical -filter {NAME =~ ...} 或经核对的 -regexp。get_pins 的层级匹配同样须按命令规则处理。下面带完整实例路径的例子假定 current_instance 为顶层,不再混用 -hier。见 Vivado 2020.2 get_cells。
不要在正式 XDC 中普遍使用 -quiet,否则空集合更难发现。关键对象应在综合后的验证 Tcl 中主动检查:
proc require_objects {label objects} { if {[llength $objects] == 0} { error "XDC: no objects matched for $label" } return $objects}
set sys_clk_port [require_objects sys_clk [get_ports -quiet sys_clk]]set adc_data [require_objects adc_data [get_ports -quiet {adc_data[*]}]]运行方式:先 synth_design 或 open_run synth_1,再 source scripts/validate_constraints.tcl。XDC 本身保持为直接约束命令;构建脚本应把 Critical Warning、验证脚本异常和约束报告失败视为构建失败。
顶层端口名通常稳定;综合后的寄存器名可能因优化改变。对内部对象施加例外时,应保持清晰层次、给同步器加 ASYNC_REG 属性,并在每次综合后用 get_* 和 report_exceptions 复核命中范围。
主时钟:定义真实入口
跳转到“主时钟:定义真实入口”100 MHz、50% 占空比:
create_clock -name sys_clk -period 10.000 [get_ports sys_clk]125 MHz、非 50% 波形,第一上升沿为 0 ns、下降沿为 3 ns:
create_clock -name eth_clk -period 8.000 -waveform {0.000 3.000} \ [get_ports eth_clk]- 周期和波形单位默认是 ns。
create_clock应放在时钟进入 FPGA 的根端口或合法根引脚。- 不要为了“让报告有时钟”在普通数据网上随意
create_clock。 - 不要用更宽松的周期掩盖设计达不到目标频率的事实。
抖动与额外裕量
跳转到“抖动与额外裕量”set_input_jitter [get_clocks sys_clk] 0.080set_clock_uncertainty -setup 0.100 [get_clocks sys_clk]set_input_jitter 描述输入时钟源的随机/周期抖动。set_clock_uncertainty 是额外的不确定性预算;不要重复计算工具已传播的抖动,也不要把它当作随意“加保险”的数字。
虚拟时钟:描述 FPGA 外部参考
跳转到“虚拟时钟:描述 FPGA 外部参考”虚拟时钟没有连接到设计端口,适合作为外部器件 I/O 时序的参考:
create_clock -name ext_sys_clk -period 10.000系统同步接口中,板级同一个时钟源可能分别送到 FPGA 和外部器件。物理 sys_clk 驱动 FPGA 内部逻辑,虚拟 ext_sys_clk 表示外部器件边界。二者的周期、波形和相位必须来自同一个真实关系。
虚拟时钟不是“异步时钟”的同义词。不要把它放进异步时钟组,除非外部参考与 FPGA 时钟确实无固定关系。
生成时钟:保留来源和相位关系
跳转到“生成时钟:保留来源和相位关系”Vivado 通常能从 MMCM、PLL、BUFR 等专用资源自动推导输出时钟。先查看报告,再决定是否补充或重命名:
report_clocksreport_clock_networks普通分频逻辑或工具无法自动推导的时钟,用 create_generated_clock:
create_generated_clock -name clk_div2 \ -source [get_ports sys_clk] \ -divide_by 2 \ [get_pins u_clk_div/clk_div_reg/Q]-source 是主时钟经过的源端口/引脚,最后一个对象是生成时钟实际出现的位置。复杂相位关系可用 -edges、-edge_shift、-invert;一个源上存在多个主时钟时需要 -master_clock 和 -add。
不要在分频输出上另建无关的 create_clock。那样会丢失父时钟插入延迟和抖动传播,工具也无法正确分析跨时钟路径。
I/O delay:把板外路径放进时序分析
跳转到“I/O delay:把板外路径放进时序分析”set_input_delay 描述数据到达 FPGA 输入端口的时间,相对于指定参考时钟边沿。set_output_delay 描述 FPGA 输出必须为外部接收器预留的时间。
同步接口的统一计算
跳转到“同步接口的统一计算”定义:
| 符号 | 含义 |
|---|---|
tCO_min/max | 外部发送器从时钟边沿到数据输出的最小/最大延迟 |
tSU、tH | 外部接收器的 setup/hold 要求 |
Ddata_min/max | 数据在选定板级参考点之间的最小/最大延迟 |
Dclk_min/max | 参考时钟在同一组参考点之间的最小/最大延迟 |
Smin/max | 数据相对参考时钟的板级 skew:Ddata - Dclk |
先保证 data path 和 clock path 使用相同起点/终点,再计算最坏相对 skew:
Smax = Ddata_max - Dclk_minSmin = Ddata_min - Dclk_max在时钟和数据使用兼容采样边沿的前提下:
| 约束 | 计算式 |
|---|---|
input -max | tCO_max + Smax |
input -min | tCO_min + Smin |
output -max | tSU + Smax |
output -min | -tH + Smin |
-min 可以为负数,这是时钟/数据相对关系的正常结果,不应强行改成 0。
示例计算:
# 外部发送器:tCO=0.6..2.4 ns# 数据走线:0.35..0.55 ns;时钟走线:0.25..0.40 ns# input_max = 2.4 + 0.55 - 0.25 = 2.70 ns# input_min = 0.6 + 0.35 - 0.40 = 0.55 nsset_input_delay -clock [get_clocks ext_sys_clk] -max 2.700 \ [get_ports {rx_data[*]}]set_input_delay -clock [get_clocks ext_sys_clk] -min 0.550 \ [get_ports {rx_data[*]}]
# 外部接收器:tSU=1.8 ns,tH=0.4 ns# 数据走线:0.30..0.50 ns;时钟走线:0.20..0.35 ns# output_max = 1.8 + 0.50 - 0.20 = 2.10 ns# output_min = -0.4 + 0.30 - 0.35 = -0.45 nsset_output_delay -clock [get_clocks ext_sys_clk] -max 2.100 \ [get_ports {tx_data[*]}]set_output_delay -clock [get_clocks ext_sys_clk] -min -0.450 \ [get_ports {tx_data[*]}]这些公式假设参考点和边沿定义一致。Source-synchronous 接口通常从发送器引脚量到接收器引脚;共同振荡器分叉的 system-synchronous 接口必须把两个时钟分支的相对延迟都纳入 Smin/Smax。若器件在相反边沿采样、时钟经过移相或接口规范直接给出窗口,应按真实时序图重新推导,不能只套公式。
Source-synchronous 输入
跳转到“Source-synchronous 输入”外部器件把时钟与数据一起送入 FPGA,例如 ADC 的 DCO:
create_clock -name adc_dco -period 5.000 [get_ports adc_dco]
set_input_delay -clock [get_clocks adc_dco] -max 0.850 \ [get_ports {adc_data[*]}]set_input_delay -clock [get_clocks adc_dco] -min -0.150 \ [get_ports {adc_data[*]}]数值描述数据相对 ADC 时钟到达 FPGA 边界的窗口,应包含器件 skew 和板级 data-to-clock skew。若接口在 FPGA 内使用 IDELAY/ISERDES 居中采样,还要把实际延迟设置和采样相位纳入分析。
DDR 输入
跳转到“DDR 输入”DDR 需要同时约束参考时钟上升沿和下降沿:
set adc_ports [get_ports {adc_data[*]}]set adc_clock [get_clocks adc_dco]
set_input_delay -clock $adc_clock -max 0.850 $adc_portsset_input_delay -clock $adc_clock -min -0.150 $adc_ports
set_input_delay -clock $adc_clock -clock_fall -max 0.900 \ -add_delay $adc_portsset_input_delay -clock $adc_clock -clock_fall -min -0.100 \ -add_delay $adc_ports-clock_fall 选择参考时钟下降沿,-rise/-fall 选择数据转换方向,两者不是一回事。对同一端口保留第二组边沿约束时使用 -add_delay,否则可能覆盖已有约束。
Source-synchronous 输出
跳转到“Source-synchronous 输出”FPGA 同时输出数据和转发时钟时,先让转发时钟保持与内部源时钟的关系:
create_generated_clock -name dac_fwd_clk \ -source [get_pins u_dac_clk_oddr/C] \ -divide_by 1 \ [get_ports dac_clk]
set_output_delay -clock [get_clocks dac_fwd_clk] -max 1.200 \ [get_ports {dac_data[*]}]set_output_delay -clock [get_clocks dac_fwd_clk] -min -0.300 \ [get_ports {dac_data[*]}]转发时钟通常通过 ODDR 和专用时钟资源输出。不要用普通逻辑翻转信号代替转发时钟。
异步输入不是 I/O delay 问题
跳转到“异步输入不是 I/O delay 问题”按键、外部中断和 UART RX 等与内部时钟没有固定相位关系。不要为它们编造相对于 sys_clk 的 input delay。正确流程是:
- 在 RTL 中使用合适的同步器、脉冲同步或异步 FIFO。
- 标记同步器寄存器,例如
(* ASYNC_REG = "TRUE" *)。 - 只对异步端口到第一级同步器的路径施加精确例外。
- 对同步器之后的逻辑继续执行正常同步时序分析。
set_false_path -from [get_ports async_irq] \ -to [get_pins u_irq_sync/sync_ff1_reg/D]时序例外:默认不使用,证明后再使用
跳转到“时序例外:默认不使用,证明后再使用”例外会减少或改变分析范围。每一条例外都应附带协议理由、起点、终点和验证方法。
| 需求 | 优先考虑 | 不能解决的问题 |
|---|---|---|
| 两个时钟完全无固定相位关系 | 正确 CDC 结构,再按范围异步分组/切路径 | 亚稳态、丢脉冲、总线撕裂 |
| 某条功能路径永不采样 | 精确 set_false_path | 错误模式控制或错误握手 |
| 接收端按协议每 N 周期采样 | setup/hold 成对的 multicycle | 组合逻辑本来就太深 |
| 异步总线还需限制传播/偏斜 | set_max_delay -datapath_only、set_bus_skew | 缺少 Gray/握手/FIFO 协议 |
| 不同工作模式选择不同路径 | set_case_analysis,分别分析各模式 | 实际硬件中的动态切换错误 |
异步时钟组
跳转到“异步时钟组”set_clock_groups -asynchronous \ -group [get_clocks {sys_clk}] \ -group [get_clocks {adc_dco}]这会双向切断两个时钟组之间的普通时序分析,适合组内关系明确、组间确实异步且所有 CDC 已审核的场景。
set_clock_groups 范围很大,且会压过同一路径上的普通 set_max_delay 等低优先级时序例外。若某个 CDC 总线仍需要最大数据路径限制,不要先用宽泛的异步时钟组把它完全切掉;改为缩小切路径范围。set_bus_skew 是独立的总线偏斜检查,但也必须用报告确认实际覆盖对象和最终有效约束。
-logically_exclusive 和 -physically_exclusive 用于互斥时钟模式,不等于异步。只有硬件保证时钟不会同时作用于相关路径时才使用。
精确 false path
跳转到“精确 false path”set src_regs [get_cells -hier -filter {NAME =~ */status_src_reg[*]}]set sync_d [get_pins -hier -filter {NAME =~ */status_sync_ff1_reg[*]/D}]set_false_path -from $src_regs -to $sync_d优先约束到第一级同步器 D 引脚,不要用 -through 一个常见网名切掉大量未知路径。set_false_path -from [get_clocks A] -to [get_clocks B] 只切一个方向;双向无关时钟可使用经过审核的 set_clock_groups -asynchronous。
多周期路径
跳转到“多周期路径”若同一时钟域内,目标寄存器按协议每 3 个周期才采样一次:
set src_regs [get_cells {u_slow/src_reg[*]}]set dst_regs [get_cells {u_slow/dst_reg[*]}]
set_multicycle_path 3 -setup -from $src_regs -to $dst_regsset_multicycle_path 2 -hold -from $src_regs -to $dst_regs常见同周期场景中,N 周期 setup 对应 N-1 周期 hold 修正。若源/目的时钟周期不同,必须重新确认 -start/-end 的边沿移动规则,不能机械套用。
只有 RTL 中存在真实、可验证的采样使能或协议保证时才能设置 multicycle。单纯因为路径时序失败而设置多周期,会把硬件错误隐藏起来。
异步总线的最大延迟与偏斜
跳转到“异步总线的最大延迟与偏斜”Gray 计数器等 CDC 总线不仅需要功能协议,还可能需要限制各位到达时间:
set gray_src [get_cells {u_fifo/wgray_reg[*]}]set gray_dst [get_pins {u_fifo/wgray_sync_ff1_reg[*]/D}]
set_max_delay 4.000 -datapath_only -from $gray_src -to $gray_dstset_bus_skew 1.000 -from $gray_src -to $gray_dst具体上限应由源端更新速率、源和目的时钟周期、Gray 协议以及实现预算共同推导,不能只看接收周期。Vivado 2020.2 的 set_bus_skew 帮助给出以两域较短周期为参考的常见选择,并以源时钟周期的 set_max_delay -datapath_only 辅助限制寄存器距离;这些仍需按实际协议确定。这里的 4 ns 与 1 ns 仅演示语法,不是任意 FIFO 的通用数值。set_bus_skew 限制各位到达时间之差,不等于限制整条路径的绝对延迟,两者用途不同。
复位路径
跳转到“复位路径”异步复位应采用“异步置位/复位、同步释放”的结构。不要对所有复位路径一刀切后忽略 recovery/removal 风险。
若外部复位只连接到同步释放电路的异步控制端,可精确约束该已审核边界;同步释放后的复位网络仍按各时钟域正常分析。复位策略见 时钟、复位与 CDC。
物理与电气约束
跳转到“物理与电气约束”set_property -dict { \ PACKAGE_PIN E3 \ IOSTANDARD LVCMOS33 \} [get_ports sys_clk]
set_property -dict { \ PACKAGE_PIN H5 \ IOSTANDARD LVCMOS33 \ DRIVE 8 \ SLEW SLOW \} [get_ports status_led]差分输入示例:
set_property PACKAGE_PIN K4 [get_ports refclk_p]set_property PACKAGE_PIN K3 [get_ports refclk_n]set_property IOSTANDARD LVDS_25 [get_ports {refclk_p refclk_n}]set_property DIFF_TERM TRUE [get_ports refclk_p]| 属性 | 决定依据 |
|---|---|
PACKAGE_PIN | 原理图、封装和 PCB 连接 |
IOSTANDARD | 外部电平标准与 Bank VCCO |
DRIVE | 负载、边沿和信号完整性预算 |
SLEW | EMI、串扰、时序与信号完整性权衡 |
PULLUP/PULLDOWN | 板级上拉/下拉与上电状态 |
DIFF_TERM | 差分接口拓扑和终端方案 |
不要从其他开发板复制引脚。IOSTANDARD 必须与目标 Bank 电压和器件规则一致,最后运行 report_drc。
可迁移的 XDC 骨架
跳转到“可迁移的 XDC 骨架”下面的骨架适用于“一个系统时钟、一个同步外设接口、一个异步输入”的起点。先替换所有参数和对象名,再根据接口类型删减,不要原样照搬数值。
# ----------------------------------------------------------------------------# 0. Primary and virtual clocks# ----------------------------------------------------------------------------create_clock -name sys_clk -period 10.000 [get_ports sys_clk]create_clock -name ext_sys_clk -period 10.000set_input_jitter [get_clocks sys_clk] 0.080
# ----------------------------------------------------------------------------# 1. Board-level I/O timing (replace with calculated values)# ----------------------------------------------------------------------------set_input_delay -clock [get_clocks ext_sys_clk] -max 2.700 \ [get_ports {rx_data[*]}]set_input_delay -clock [get_clocks ext_sys_clk] -min 0.550 \ [get_ports {rx_data[*]}]set_output_delay -clock [get_clocks ext_sys_clk] -max 2.100 \ [get_ports {tx_data[*]}]set_output_delay -clock [get_clocks ext_sys_clk] -min -0.450 \ [get_ports {tx_data[*]}]
# ----------------------------------------------------------------------------# 2. Reviewed asynchronous boundary# ----------------------------------------------------------------------------set_false_path -from [get_ports async_irq] \ -to [get_pins u_irq_sync/sync_ff1_reg/D]
# ----------------------------------------------------------------------------# 3. Physical pins (replace from schematic)# ----------------------------------------------------------------------------set_property PACKAGE_PIN E3 [get_ports sys_clk]set_property IOSTANDARD LVCMOS33 [get_ports sys_clk]
# Add every used top-level I/O pin and its verified IOSTANDARD here.不要把上面的 proc 对象检查写回 .xdc。配套的 validate_constraints.tcl 在综合后执行:
proc require_objects {label objects expected_min} { set count [llength $objects] if {$count < $expected_min} { error "Constraint validation: $label matched $count object(s)" } puts "Constraint validation: $label matched $count object(s)" return $objects}
require_objects sys_clk_port [get_ports -quiet sys_clk] 1require_objects rx_data [get_ports -quiet {rx_data[*]}] 1require_objects tx_data [get_ports -quiet {tx_data[*]}] 1require_objects irq_sync_d \ [get_pins -quiet u_irq_sync/sync_ff1_reg/D] 1
if {[llength [get_clocks -quiet sys_clk]] != 1} { error "Constraint validation: sys_clk was not created exactly once"}
report_exceptions -summaryreport_exceptions -coveragereport_exceptions -ignoredcheck_timing -verbose总线宽度已知时,应把 expected_min 改为精确数量判断,例如 rx_data 必须正好命中 8 个端口。若外设不是 system-synchronous,应替换虚拟时钟和 I/O delay 部分:source-synchronous 使用转发时钟;异步控制使用 CDC;没有采样关系的静态配置端口不要伪造周期约束。
每次构建都要验证约束
跳转到“每次构建都要验证约束”综合后至少运行:
report_clocksreport_clock_networkscheck_timing -verbosereport_clock_interactionreport_exceptions -summaryreport_exceptions -coveragereport_exceptions -ignoredreport_timing_summary -delay_type min_max \ -report_unconstrained -check_timing_verbosereport_drc| 检查 | 合格标准 |
|---|---|
report_clocks | 所有预期时钟存在,周期、波形和 generated source 正确 |
check_timing -verbose | 无意外 no_clock、partial I/O delay、multiple clock、unconstrained endpoint |
report_clock_interaction | 时钟关系与架构一致,每个不安全交互都有 CDC 解释 |
report_exceptions -coverage | 每条例外命中预期起点/终点,没有过宽覆盖 |
report_exceptions -ignored | 没有因对象缺失或高优先级约束而被忽略的关键例外 |
report_timing_summary | setup/hold 均检查,无未约束关键路径,WNS/TNS/WHS/THS 达标 |
report_drc | 无未解决的时钟、I/O、电气和实现 DRC |
如果设计使用了 set_bus_skew,实现完成后还应单独执行 report_bus_skew,核对每组起终点、requirement、actual 与 slack;不能用 report_timing_summary 代替它。报告中没有相应路径也不是通过,需先检查约束是否命中正确对象。这一独立签核要求可查 AMD UG906 的 Report Bus Skew 说明。
对某个接口做点对点检查:
report_timing -from [get_ports {rx_data[*]}] \ -to [get_cells {u_rx/data_reg[*]}] \ -delay_type min_max -max_paths 20
report_timing -from [get_cells {u_tx/data_reg[*]}] \ -to [get_ports {tx_data[*]}] \ -delay_type min_max -max_paths 20只看到 WNS >= 0 不足以签核:路径可能根本没有被约束,或被错误 false path 切掉。
如何读一条失败路径
跳转到“如何读一条失败路径”按以下顺序拆解,而不是立即更换实现策略:
- 确认 startpoint、endpoint、launch/capture clock 正确。
- 确认 path requirement、I/O delay 和时序例外来自真实协议。
- 分开查看 data path、clock path、logic delay、route delay 和 clock skew。
- 判断是 setup、hold、恢复/移除还是脉宽问题。
- 找出重复出现的逻辑结构、扇出源和物理区域,而不是只修最差一条路径。
常见信号:
| 报告特征 | 优先怀疑 |
|---|---|
| 逻辑级数很深 | RTL 组合链、优先级选择、大比较/算术 |
| routing delay 占比高 | 高扇出、拥塞、跨区域/SLR、宏单元距离 |
| 大负 clock skew | 时钟拓扑、跨区域、时钟资源或约束关系 |
| input/output 路径失败 | 外部时序模型、IOB 寄存器、IDELAY/ODELAY、板级 skew |
| hold 大量失败 | 时钟偏斜、生成时钟关系、过短数据路径或错误 min delay |
| 路径 requirement 异常小 | 时钟波形/相位、错误 generated clock、错误边沿或例外 |
Setup 收敛顺序
跳转到“Setup 收敛顺序”- 修正漏约束、错误时钟关系和不可信例外。
- 确认路径确实应该在当前采样边沿完成。
- 减少逻辑级数、串行优先级、宽运算和不必要转换。
- 在自然边界增加流水级,并同步延迟
valid、地址和标签。 - 复制或分层驱动高扇出控制信号,避免一个源跨全芯片。
- 检查 BRAM/DSP/URAM 是否启用合适内部寄存器。
- 检查拥塞、跨区域/SLR 距离和关键 IP 的相对位置。
- 最后再尝试综合/实现 directive、
phys_opt_design或增量实现。
架构修复通常比反复更换策略稳定。若每次实现结果波动很大,应先处理拥塞、高扇出和物理距离。
Hold 收敛顺序
跳转到“Hold 收敛顺序”- 检查 generated clock、时钟相位、I/O
-min和 multicycle hold 是否正确。 - 确认没有把本应异步的路径误当同步,或把同源时钟误分异步。
- 检查异常 clock skew、专用时钟资源使用和物理跨区。
- 让实现工具进行合法的 hold 修复,并复查 setup 是否退化。
不要在 RTL 中插入组合延迟链或依赖门延迟修 hold。FPGA 逻辑和布线延迟会随器件、PVT 和实现变化,无法作为可靠功能延迟。
常见误区
跳转到“常见误区”| 错误做法 | 后果 | 正确做法 |
|---|---|---|
| 给所有输入输出填同一个 delay | 板外模型失真 | 按接口和器件手册分别计算 min/max |
get_ports adc_data[*] 不加大括号 | Tcl 可能解释 [] | 使用 [get_ports {adc_data[*]}] |
在 XDC 中定义任意 proc | Vivado 拒绝不受支持命令,后续约束可能失效 | XDC 只放约束;对象断言放综合后 Tcl |
get_* -quiet 后不检查空集合 | 约束看似执行但没有命中 | 在验证 Tcl 中对关键集合检查 llength |
用 create_clock 描述内部分频时钟 | 丢失父时钟相位、延迟和抖动关系 | 使用自动推导或 create_generated_clock |
| 对异步时钟只设 false path | CDC 电路仍可能错误 | 先实现同步器/FIFO/握手,再约束 |
| 为失败路径直接设 multicycle | 隐藏真实单周期违例 | 证明接收协议确实隔 N 周期采样 |
| 只写 multicycle setup | hold 检查边沿错误 | 同周期场景配套 N-1 hold |
| 对时钟组全局 asynchronous 后再限 Gray 总线 | 高优先级切路径可能让后续约束无效 | 缩小切路径范围并查 ignored/coverage |
| 只检查 WNS | 可能存在 hold 或未约束路径 | 同时查 WNS/TNS/WHS/THS 和 unconstrained |
| 从其他开发板复制 pin/IOSTANDARD | 引脚或 Bank 电压错误 | 以当前原理图、封装、VCCO 为准 |
项目签核清单
跳转到“项目签核清单”时钟
跳转到“时钟”- 每个时序单元都有预期时钟。
- primary、virtual、generated clock 的来源、周期、波形和关系正确。
- MMCM/PLL 自动生成时钟已在
report_clocks中核对。 - 异步、逻辑互斥和物理互斥没有混用。
I/O
跳转到“I/O”- 每个同步接口都有独立的 min/max 计算记录。
- SDR/DDR 的参考边沿和
-add_delay使用正确。 - 异步输入使用了匹配协议的 CDC 结构。
- 每个已使用端口的引脚、电平标准和 Bank 电压一致。
例外
跳转到“例外”- 每条例外有协议依据、精确对象和审核人可理解的注释。
- multicycle setup/hold 成对且边沿含义已验证。
report_exceptions -ignored无意外结果,coverage 不过宽。- false path 没有切掉应该分析的同步逻辑。
报告
跳转到“报告”check_timing -verbose无未解释问题。- setup、hold、恢复/移除和脉宽检查均通过。
- 无未约束关键路径,时钟交互与 CDC 设计一致。
- 关键 timing、DRC、utilization 报告进入构建产物并可追溯。
Vivado 终端化运行方式见 Vivado 终端命令与 Tcl 批处理,完整实现阶段见 Vivado 从 RTL 到上板。