FPGA 进阶 01:约束实战——generated clock、IO delay、虚时钟与 clock_groups
本文是
fpga/进阶层第 05 篇。入门篇 XDC 约束入门 只写了两条最小约束(create_clock+PACKAGE_PIN/IOSTANDARD),枢纽页 时序约束与收敛 列了约束分类。这篇把真实工程里必须会写的时序约束补齐,让 STA 的四张路径表都有约束可依。通用 SDC 原理可对照 SDC 约束实战,本篇专注 FPGA/XDC 写法与 Vivado/Quartus 差异。
本文要回答的问题
- PLL/MMCM 出来的时钟怎么约束?自己写分频器当时钟呢?
set_input_delay/set_output_delay的数字到底填多少、从哪查?- 为什么 IO 约束常配一个虚时钟(virtual clock)?
- 异步时钟域到底用
set_clock_groups还是set_false_path? set_multicycle_path为什么 setup 和 hold 要成对写?- 写完怎么自检「有没有漏约束」?
一、约束全景:四类命令各管一张路径表
上一篇讲了 STA 把路径分四组,约束就是为这四张表提供时间基准:
| 路径组 | 需要的约束 | 不写的后果 |
|---|---|---|
| reg-to-reg(内部) | create_clock + create_generated_clock | unconstrained clock,内部时序不分析 |
| pin-to-reg(输入) | set_input_delay(常配虚时钟) | 输入路径按最宽松放过,板级可能错 |
| reg-to-pin(输出) | set_output_delay(常配虚时钟) | 输出路径不考虑外部建立/保持 |
| pin-to-pin(纯组合) | set_max_delay | 纯组合穿过 FPGA 无时钟基准,漏分析 |
| 跨时钟域 | set_clock_groups / set_false_path | 异步域被当同步路径硬收敛,误报/浪费优化 |
XDC 本质是 Tcl,命令都遵循 SDC 标准(Synopsys Design Constraints),所以 Vivado 的 .xdc 和 Quartus 的 .sdc 命令名几乎一致,差异主要在管脚/电平的物理约束(那部分在 Quartus 里放 .qsf)。
二、衍生时钟:create_generated_clock
create_clock 定义从端口进入的主时钟。时钟经 PLL/MMCM 倍频分频、或被逻辑分频后,会产生新时钟,STA 必须追踪它的周期和相位,否则输出域路径缺基准。
首选:用 Clocking Wizard IP,让它自动产生。 Vivado 的 MMCM/PLL IP 在综合时会自动生成派生时钟约束,report_clocks 里能看到自动命名的 clk_out1_clk_wiz...,无需手写。这也是为什么 FPGA 推荐「时钟一律走 MMCM/PLL + BUFG」。
手写场景:逻辑分频 / 外部反馈时钟。 若你用计数器分频、或一个时钟由另一个时钟门控/分频得到:
# 主时钟 100MHz 从 clk 端口进
create_clock -name clk_in -period 10.000 [get_ports clk]
# clk_div 是由 clk_in 二分频得到、在某 FF 的输出引脚上
create_generated_clock -name clk_div \
-source [get_pins u_divider/q_reg/Q] \
-divide_by 2 \
[get_pins u_divider/q_reg/Q]要点:
-source指源时钟传播到的那个引脚(生成时钟的参考点),-divide_by/-multiply_by给分频/倍频系数,-invert/-edge可描述相位。- 最容易漏的是
-source:不写或写错,工具不知道这个时钟从谁派生,时钟关系断裂。 - 强烈不建议用逻辑门控/分频造时钟——这会让时钟不走专用全局网络、skew 大、且派生关系难约束。绝大多数「分频」需求用**时钟使能(clock enable)**实现:时钟还是那个高速时钟,用一个节拍信号控制 FF 何时更新,既安全又免约束。
三、IO 约束:set_input_delay / set_output_delay
这两条把板级外部那段时间告诉工具,让 pin-to-reg / reg-to-pin 路径能和片内一起分析。
set_input_delay:数据从外部芯片到达 FPGA 输入引脚时,相对于某个时钟沿的延迟(= 外部芯片 clk-to-out + 板级走线延迟)。set_output_delay:外部芯片要求 FPGA 输出数据在采样沿前/后保持的时间(= 外部芯片的建立/保持要求 + 板级走线)。
# 输入:数据相对 clk 端口,最快 min=1.5ns、最慢 max=4.0ns 到达
set_input_delay -clock vclk -max 4.0 [get_ports {data_in[*]}]
set_input_delay -clock vclk -min 1.5 [get_ports {data_in[*]}]
# 输出:外部要求输出在采样沿前 2.0ns 稳定、沿后保持 0.5ns
set_output_delay -clock vclk -max 2.0 [get_ports data_out]
set_output_delay -clock vclk -min 0.5 [get_ports data_out]数值从对接芯片的数据手册时序参数 + 板级走线延迟估算得来;max/min 分别对应 setup(最慢情况)和 hold(最快情况),两个都要写,否则 hold 分析缺数据。源同步接口(数据和随路时钟一起传,如 SPI、RGMII)还要根据 SDR/DDR、边沿对齐还是中心对齐决定用哪个时钟沿、是否加 -clock_fall/-add_delay。
四、虚时钟(virtual clock):IO 约束的参照
注意上例 -clock vclk 引用的 vclk 没有绑在任何 FPGA 端口/引脚上——这就是虚时钟:
# 只定义周期,不指定 get_ports(没有物理来源)
create_clock -name vclk -period 10.000五、异步时钟域:set_clock_groups 优先
两个异步时钟域之间(比如 100MHz 系统时钟和 125MHz 恢复时钟,无固定相位关系)的路径,不能也不该按同一时钟做 STA 收敛——工具若硬收敛会产生无意义的违例和过度优化。要告诉工具「这些路径不做时序检查」:
# 推荐:成组声明,clk_a 组和 clk_b 组两两之间全部异步
set_clock_groups -asynchronous \
-group {clk_a} \
-group {clk_b clk_div}
# 等价的逐条写法(容易漏方向,少用)
set_false_path -from [get_clocks clk_a] -to [get_clocks clk_b]
set_false_path -from [get_clocks clk_b] -to [get_clocks clk_a]- 优先
set_clock_groups -asynchronous:一次声明整组两两异步,语义清晰、不易漏;set_false_path要自己保证双向、且-from/-to选错对象(port/pin/clock)容易漏。 - 约束只关掉时序检查,不保证功能正确——跨域数据仍要靠电路保证:单 bit 用两级同步器、多 bit 用异步 FIFO(Gray 码)或握手(见后续 CDC/异步 FIFO 篇)。
- 同源分频的同步时钟不要标 asynchronous:比如同一 MMCM 出来的 100MHz 和 200MHz 有固定相位关系,是同步的,标成异步会错误放松真实需要收敛的路径。
report_clock_interaction能看工具判定的时钟关系,异步对应显示 async/false,同步对应显示 timed。
set_false_path 还适合确定不走时序的静态/配置路径,比如上电才写一次的配置寄存器、测试逻辑:
set_false_path -from [get_ports cfg_mode] # 静态配置脚
set_false_path -through [get_pins u_test/*] # 测试逻辑六、多周期路径:set_multicycle_path
有些路径本来就不需要一个周期完成(比如使能每隔 N 拍才采样一次的慢路径),强行按单周期收敛会让工具过度优化。用 multicycle 放宽:
# 数据每 2 个周期才被采一次:setup 用 2 周期
set_multicycle_path 2 -setup -from [get_clocks clk_a] -to [get_clocks clk_b]
# 通常 hold 要相应改成 1,保持「在正确的沿检查保持」
set_multicycle_path 1 -hold -from [get_clocks clk_a] -to [get_clocks clk_b]铁律:改 setup 多周期,hold 要配套调整。setup 放宽 N 个周期后,工具默认的 hold 检查沿也会移动,若不写 hold multicycle,往往会出现一批 hold 违例。经验法则:setup N → 多数情况 hold 写 N−1(跨同频时钟)或按时钟关系推导。multicycle 是最容易写错的约束,写错不会报错但会让时序分析基准错误,写前一定画 launch/capture 沿确认,写完用 report_timing 核对检查沿确实移到了预期位置。
七、静态选择与纯组合兜底
set_case_analysis:告诉工具某信号在本工作模式下恒为定值(如 mux 选择端、测试模式脚),工具据此只分析实际会走的路径、关掉另一支:
set_case_analysis 0 [get_ports test_mode] # 正常工作模式 test_mode=0set_max_delay:给没有时钟的路径(纯组合 pin-to-pin、或异步接口中你想手动定一个时间预算的路径)兜底:
set_max_delay 5.0 -from [get_ports din] -to [get_ports dout]它直接指定「这条路径延迟不得超过 X ns」,绕过时钟基准。异步 FIFO 内部的读指针→写时钟域这类路径,Xilinx IP 内部已用 max delay + 同步器处理,自己写异步电路时也常用它而非完全 false path(保留一个时间上限,比完全不检查安全)。
八、约束自检:check_timing 与 report_methodology
写完约束,综合/实现后必跑三条自检:
| 命令 | 查什么 | 关注点 |
|---|---|---|
check_timing | 未约束端口、无时钟驱动的时序单元、多个时钟驱动同一节点等 | unconstrained internal ports / no clock 必须清零 |
report_clocks | 所有时钟(含 MMCM 派生)周期/波形对不对 | 派生时钟是否自动生成、周期是否和晶振一致 |
report_clock_interaction | 时钟两两关系 | 异步对应为 async/false,同步对应为 timed |
report_methodology | 设计规范告警(门控时钟、高扇出等) | 配合 时序收敛 治理 |
出现 unconstrained clock/port 说明某张路径表没基准,STA 结果不可信——必须先补齐约束,再谈时序是否收敛。
九、从约束到签收:一条真实约束的生命周期
在规范团队里,一条时序约束不是写完丢进 XDC 就结束,它和 RTL 一样要经历提出、评审、合入、验证、维护的生命周期,理解这条生命周期能解释为什么「约束也有 bug,而且约束 bug 比 RTL bug 更阴险」。提出:约束的来源只有两个是正当的——时钟拓扑事实(晶振频率、PLL 配置,来自原理图和时钟架构文档)和外部器件时序(来自对接芯片手册的 tco/tSU/tH)。工程师写每一条 input/output delay 时应能指出它对应手册的哪个参数表、哪个工作条件;凭感觉填的「差不多 5ns」是约束缺陷的头号来源。评审:约束评审看四件事——完整性(该有的时钟和端口约束有没有漏,check_timing 说了算)、正确性(数值有无来源、max/min 是否成对)、最小性(false path/multicycle 是不是豁免过宽,一条 -from [get_clocks *] 的伪路径可能悄悄放过几百条真实路径)、可维护性(为什么豁免的注释、适用的模块边界)。例外类约束必须写明「为什么这条路径不适用单周期检查」,没有理由的豁免在评审里一律打回。合入与回归:XDC 随 RTL 进版本库,CI 里跑约束 lint(Vivado 的 report_exceptions、report_exceptions -ignored 能发现互相覆盖或从未生效的例外)和「违例差异对比」——新合入的约束如果让一大批路径从时序报告里消失,必须解释是真的伪路径还是写错对象。维护:端口增删、PLL 改频率、芯片升级后约束要同步更新;僵尸约束(指向已不存在的时钟/端口)在严格流程里是错误而非警告,因为它说明这份文件已经与设计脱节、没人再敢信任。签收:流片/发布前的时序签收报告应列出完整时钟清单及关系、所有例外及其依据、未约束项为零、WNS/WHS 为正且有裕量。把约束当「设计意图的可执行规格」对待,而不是让时序变绿的咒语,是从业余到工程化的分水岭。
十、约束与电路必须一起设计:伪路径背后的同步器纪律
新手最容易产生的错觉是「异步路径报红,加一句 false path / clock_groups 就解决了」。实际上约束什么都不解决——它只是告诉时序分析工具「别检查了」,数据能否正确跨域完全取决于电路本身。这一节把约束与电路的对应关系钉死。单 bit 控制信号跨域:电路上必须是两级(或多级)触发器同步器,约束上把「源时钟域触发器输出→同步器第一级」之间的路径标为异步(clock_groups 或该路径 max_delay 约束到一个很短的值,配合 ASYNC_REG 属性让工具把两级同步器放近、平衡布线延迟)。注意 false path 与 max_delay 两派的差别:完全 false path 放弃一切检查,maxdelay 派保留「别让单 bit 走线绕半个芯片拖到亚稳态决断窗之外」的下限检查,后者更稳健,Vivado 推荐用 set_max_delay -datapath_only。多 bit 数据跨域:绝不能对多 bit 总线打两拍——各 bit 延迟不同会采样到撕裂值。合法电路只有三种:异步 FIFO(数据通路)、握手 req/ack(控制面,每个跨域的仍是单 bit)、Gray 码指针(多 bit 但每次只变 1 位,配合同步器)。约束相应地落在 FIFO 内部或同步器路径上;Xilinx 异步 FIFO IP 内部已处理好,用户不要在外围重复约束打架。复位跨域:异步复位、同步释放电路里,复位同步器的路径同样需要相应约束,且不能被误当普通数据路径。CDC 审查的正确姿势:先用 CDC lint 工具(或 Vivado report_clock_interaction + 人工)列出所有跨域路径,逐条回答「电路同步机制是什么」,确认机制后再核对约束是否与之一致——是先有电路、后有约束,而不是反过来。凡是「约束说异步、但电路里直接连了多 bit 总线」的地方,就是一颗每隔几十万次上电偶发一次、仿真永远抓不到的雷。时序约束工程师和做 CDC 电路的设计师必须是同一个对话里的人,这也是下一篇收敛方法的前置条件。
十一、跨工具移植与长期演进:约束资产的可移植性
一个有生命周期的产品会经历 FPGA 换代、工具升级甚至厂商迁移,约束文件是迁移成本最集中的资产之一,写的时候就应为演进留路。SDC 核心是跨厂商通用的:时钟定义、IO delay、false/multicycle/max delay 这些命令在 Vivado XDC 与 Quartus SDC 间语义一致(第一篇已述),把「时序约束」与「物理约束」分文件存放是迁移纪律的基础——一个 timing.xdc/timing.sdc 放 SDC 语义命令,一个 physical.xdc/.qsf 放管脚电平;迁移时前者大部分可复用,后者必然重写,分文件让差异边界清晰。用对象查询而非裸名字:[get_ports ...]、get_pins -hierarchical、get_clocks 的层次无关写法比硬编码线网名健壮;RTL 改层次名时,基于端口/层次引脚和通配规则的约束比绑定综合后网线名的约束存活率高得多。避免依赖综合生成的诡异网名(xx[0]_i_1_n__23)做例外对象——那是工具版本的人质。IP 自带约束不要复制进主文件:MMCM、DDR、收发器 IP 生成的 XDC 由 IP 管理,复制出来手改会在 IP 升级时全部过时;主约束只补充 IP 之外的部分。版本升级回归:Vivado/Quartus 大版本升级后,约束语义和默认策略可能微变,必须重新跑完整时序签收并 diff 违例报告,不能因为「约束没动」就假设结果不变。换代器件重估一切:从 28nm 器件迁到 16nm 器件,同一份 RTL 的延迟构成大变(布线延迟占比上升、新时钟结构),旧的 multicycle 和例外要重新证明仍然成立——有些当年为绕工具缺陷加的约束,在新工具里反而是有害的。约束文档化:在约束文件里用注释维护一张「时钟拓扑图 + 例外清单 + 数值来源表」,新人能自助核对,而不是必须找到三年前那位离职工程师。把约束当作长期资产经营,产品第二次、第三次硬件迭代时省下的时间会数倍回报第一次的严谨。
十二、真实接口约束演练:源同步与系统同步的完整算法
前面讲了 IO 约束的命令语法,这一节把最常用的源同步接口(数据与时钟一起从对端发出,如 RGMII、并行 ADC、很多 CMOS 传感器)从头到尾算一遍,让「delay 数字从哪来」不再是黑话。先分清两种采样结构:边沿对齐(时钟边沿与数据跳变同时到达,FPGA 要用反相或相移后的时钟在数据中央采样)和中心对齐(对端已经把时钟移到数据中央,FPGA 直接采样)。算法分四步。第一步,从手册拿到四个数:发送端时钟到输出延迟 tco 的最小/最大值、数据相对于该时钟的偏斜、板级走线延迟(时钟线与数据线各一组)、要求的建立保持时间(FPGA 输入触发器的 tsu/th,器件手册给出)。第二步,计算数据在 FPGA 引脚上相对于随路时钟的到达窗口:数据最早到达 = 板级数据延迟最小 + tco 最小,最晚 = 板级数据延迟最大 + tco 最大;时钟到达 = 板级时钟延迟。第三步,以这个随路时钟(或经 MMCM 相移后的采样时钟)为基准写 input delay 的 min/max,并在需要时用 IDELAY 原语把采样点微调进窗口中央——中心对齐时 delay 较小,边沿对齐时通常先靠 MMCM 移相 90 度再用 delay 精修。第四步,实现后读 report_timing 的输入路径,确认建立保持都有正裕量且大致居中(两边裕量接近最稳健,只贴着一边过意味着温度漂移时会翻)。输出方向镜像同样的过程:output delay 由对端的 tsu/th 和板级延迟反推。常见错误清单:把典型值当最大最小值(必须按极限温度/电压角取值)、忘记板级延迟而只填芯片参数、min/max 只写一个(保持时间分析直接缺数据)、改了 IDELAY 值却没在约束里反映、同一随路时钟既做发送又做接收时相位关系想当然。系统同步接口(共用一个板载时钟、数据跨器件后由同源时钟采)更简单,按普通周期路径填 delay 即可,但要求时钟树偏斜可控,高速下基本都让位给源同步。把这条四步法在一个低速接口(如 SPI 屏、I2S 音频)上亲手算一次、量一次、对一次报告,胜过背十遍命令手册。
十三、约束排错实战:报告对不上时的检查顺序
工程师约束方面最耗时的不是写命令,而是「我明明约束了,为什么时序报告/行为和预期不一致」。按以下顺序排查,能覆盖绝大多数情况。第一查对象是否命中:SDC 命令的对象集合可能为空或比你以为的大——get_clocks/get_pins/get_ports 的通配写错时,Vivado 默认对空集合可能静默继续。养成在 Tcl 里 puts 集合大小、用 get_* -regexp 后核对成员的习惯;report_exceptions -ignored 专门列出被更优先约束覆盖、实际从未生效的例外,「我加的 false path 怎么还有违例」多半在这里有答案。第二查时钟树事实:report_clocks 看周期和波形是否真是你以为的值,report_clock_networks 看时钟有没有走到预期引脚;逻辑分频未约束、MMCM 输出没自动派生、时钟经过缓冲后改名,都会让约束落在一个没人用的时钟上。第三查优先级与冲突:clock_groups、false_path、max_delay 对同一路径的作用有明确优先级(更具体、更晚或特定命令优先),两条互相矛盾的约束只有一条活着;把所有例外集中成文件并注释,能避免半年后没人记得为什么有它。第四查分析角与路径类型:报告默认看 setup、最慢角,hold 问题要单独看;多周期改了沿却没出现在报告里时,用 report_timing -from ... -to ... 点查具体路径,看 launch/capture 沿是否如预期移动。第五查物理现实:约束不会改善物理路径——约束错了只会让工具「不检查」,偶发错误先怀疑电路(CDC、同步器、电源)而不是怀疑 STA。最后保留一份「最小复现工程」:当怀疑是工具问题时,把相关时钟和路径裁到最小工程验证,比在巨型设计里争论高效得多。整个排错过程的元规则是:先确认工具看到的电路和约束与你脑中的模型一致(对象、时钟、优先级),再讨论数字;九成的「约束玄学」死在第一步的模型不一致上。
十四、把约束讲给团队:评审会上最常被问的八个问题
约束写得对不够,还要在评审和交接时经得住问;把最常见的八个问题和标准答法准备好,约束评审会从「逐行念文件」变成真正的风险检查。一,「这个时钟从哪来、周期多少、谁和它同源」——用时钟拓扑图回答,图上每个时钟标注源头、频率、派生关系和异步边界,它是整个 XDC 的总纲。二,「为什么这条路径是伪路径/多周期,电路上靠什么保证正确」——任何例外都必须有电路机制的陈述,只说「时序太紧」不构成理由。三,「input/output delay 的数值出处」——指向具体芯片手册参数、板级延迟估算和工作角,min/max 成对。四,「虚时钟为什么存在,它代表哪个外部时间基准」。五,「异步域怎么同步,lint 报告里有没有裸跨域的多 bit」——约束与 CDC 电路一起评审。六,「有没有被覆盖、被忽略、从未生效的例外」——用例外报告证明没有僵尸和打架的命令。七,「换器件/升级工具后哪些约束要重新证明」——物理约束必改,时序例外要按新延迟构成复核。八,「未约束端口和无时钟单元是否清零,check_timing 结果在哪」。这八个问题覆盖了约束错误的绝大多数进入路径。评审组织上还有两个经验:第一,让不熟悉本模块的工程师读拓扑和例外清单,读不懂的地方往往就是文档不足或约束过度聪明的地方;第二,评审产出一页纸结论(时钟数、异步对数、例外数及分类、未清零项),随版本签收归档,后人不必重新通读 XDC 才能建立信心。约束最终是一份跨人、跨年的沟通文档,命令只是它的编码形式。
十五、结语:约束是设计意图,不是过关咒语
走到这里可以收束一个核心观点:时序约束文件的本质,是设计师对「时间关系」这一设计意图的正式声明——哪些事件同时钟相关、哪些本质独立、外部世界以什么节奏与芯片交换数据、哪些路径被电路协议允许跨周期。综合和时序工具只是这份声明的执行者,它们不会也不能替你判断意图。把约束当「让报告变绿的咒语」的团队,迟早会在一次工具升级、一次器件迁移或一次偶发上板故障中发现,绿灯从来只证明「按你声明的意图,时间满足」,而错误的意图声明本身不被检查。正确的工作姿态因此是三句话:先设计时钟和跨域电路,再写与之严格对应的约束;每条豁免都能指向电路机制和文档依据;用 check_timing、例外报告、时钟交互表和签收回归持续验证声明与现实一致。当约束、电路、评审、回归四者闭环之后,时序收敛就从个人经验主义变成团队可复制的工程能力——这正是从「会写 XDC」到「能对一个产品的时序负责」之间真正的距离。最后给学习者一条实践路线:先在一个带外部芯片接口的小工程上,独立完成时钟定义、源同步 IO 约束、异步域声明和 check_timing 清零的全过程;再尝试故意制造三类经典错误(漏约束一个端口、给同源时钟误加异步组、multicycle 只写 setup),观察工具报告和行为如何变化;最后把这份约束交给同事评审并回答那八个问题。走完这一轮,约束就不再是文档里的命令列表,而会成为你看任何一块新板子时脑子里自动浮现的时钟拓扑图。能在评审会上随手画出这张图、报出每个异步对的同步机制和每个 IO 延迟出处的工程师,在任何团队里都是时序问题的最后一道防线,也是最稀缺的那类人。约束写得严谨的人并不神秘,他们只是把每个时钟、每条例外的来历都问过一遍,并且不接受没有答案的命令。守住这条朴素标准,时序报告上的每一个绿色对勾才真正有分量。
常见坑对照
| 坑 | 现象 | 正确做法 |
|---|---|---|
| 逻辑分频造时钟不约束 | 该时钟域路径无基准、随机错 | 改时钟使能,或手写 create_generated_clock -source |
| 只写 max 不写 min | hold 分析缺数据 | input/output delay 的 max/min 成对写 |
| 同源分频时钟标 asynchronous | 真实该收敛的路径被放松 | 同步时钟走 timed,仅真异步用 clock_groups |
| multicycle 只写 setup | 冒出一批 hold 违例 | hold 配套(常为 N−1),用 report_timing 核沿 |
| 约束漏了靠「能跑就行」 | 上板时好时坏 | 综合后 check_timing 清零、时钟关系表核对 |
Intel Quartus 差异
命令同源 SDC,.sdc 里 create_clock/create_generated_clock/set_input_delay/set_false_path/set_clock_groups/set_multicycle_path/set_case_analysis 写法基本一致;差别在物理约束(管脚/电平)放在 .qsf 用 set_location_assignment/set_instance_assignment,且 Quartus 用 TimeQuest/Timing Analyzer,自检命令为 report_timing、derive_pll_clocks(PLL 时钟用 derive_pll_clocks 自动派生,等价于 Vivado IP 自动生成)。
相关与延伸
前置:XDC 约束入门(
create_clock/管脚)、静态时序分析(这些约束喂给 STA 怎么算)。 后续:时序收敛方法(约束都对了仍违例怎么办)。 通用原理(ASIC 视角):SDC 约束实战——时钟/IO delay/false path/multicycle 的芯片侧讲法。
一句话总结
约束是给 STA 的四张路径表发时间基准:内部 reg-to-reg 用 create_clock + create_generated_clock(PLL/MMCM 让 IP 自动派生,逻辑分频才手写 -source/-divide_by,更推荐用时钟使能别造时钟);IO 两组用 set_input_delay/set_output_delay 按外部芯片手册填 max/min,参照时钟常用虚时钟 create_clock -name vclk(不绑端口、代表外部时间基准);异步时钟域优先 set_clock_groups -asynchronous(比 set_false_path 不易漏),跨域功能正确仍靠同步器/异步 FIFO;set_multicycle_path 放宽慢路径但 setup/hold 要成对(常 N 与 N−1);set_case_analysis 定模式、set_max_delay 兜底无时钟纯组合路径;写完用 check_timing(未约束清零)、report_clocks、report_clock_interaction(异步 async、同步 timed)自检——XDC 与 Quartus SDC 命令同源,物理管脚才是各家差异所在。