FPGA 入门 34:时序报告入门——Slack、WNS/WHS 与失败路径
更新时间:2026-09-12。本文是
fpga/入门层第 36 篇。约束怎么写见 XDC 管脚与时序,为什么级数深频率低见 LUT 与 FF 深入,收敛手法见进阶 时序收敛方法。
本文要回答的问题
- STA 静态时序分析为什么不需要仿真激励、却能保证「所有路径」都检查到?它和门级仿真、动态时序验证的区别是什么?
- 一条寄存器到寄存器路径的 Slack 公式里每一项是什么:发射沿、捕获沿、Tco、逻辑延迟、布线延迟、时钟偏斜、不确定度、jitter、建立时间?
- 为什么保持时间检查与周期无关、工具几乎总能自动修?而建立时间失败只能靠改结构或降频?
- Intra-clock、inter-clock、源同步输入/输出、异步路径四类时序报告分别看什么?为什么报告里有时出现负的 requirement?
- Logic Levels 多少级算深?100MHz、200MHz 下各级延迟预算大致怎么分(时钟到输出、布线、LUT、建立)?
- 时序报告里的 pulse width、clock uncertainty、generated clock、derating 各是什么?为什么「Timing met」之外还要看这几项?
- 综合后 WNS 为负但布线后可能变正、或反之,应该在哪个阶段介入优化?多周期/假路径用错了会掩盖什么真实问题?
读完本文,你应当能独立读懂一条完整失败路径的每一行字段,判断它属于「约束问题、结构问题、物理实现问题」中的哪一类,并按正确顺序采取动作而不是盲目降频。
一、setup / hold 直觉
- 建立 setup:数据要在时钟沿之前稳定;
- 保持 hold:数据在时钟沿之后还要保持一会;
- Slack 裕量 = 要求时间 − 到达时间,≥0 满足、<0 违例。
二、Timing Summary 关键指标
| 指标 | 含义 | 要求 |
|---|---|---|
| WNS | 最坏建立裕量(最慢路径) | ≥0 |
| WHS | 最坏保持裕量 | ≥0(工具常自修) |
| TNS/THS | 所有失败路径负裕量总和 | 0 最好 |
Vivado 直接给 Timing met / not met;先看 WNS/WHS 是否都为正。
三、失败路径怎么读
- Source/Destination Clock:同域还是跨域(跨域报红多为缺 clock_groups);
- Source/Endpoint FF:哪段逻辑;
- Data Path Delay:logic delay(LUT)+ routing delay(布线);
- Logic Levels:穿过几级 LUT——太深是 setup 失败主因。
四、WNS<0 处理顺序
- 查约束:时钟周期/管脚、跨时钟域设 set_clock_groups;
- 看路径:定位大加法/宽比较/大 mux/if 链;
- 插流水线:长组合逻辑中间打拍,降低每拍逻辑级数;
- 工具优化:retiming、提高努力度、phys_opt;
- 降频:放宽时钟约束;
- 高扇出/复位网络可复制优化。
五、STA 是怎么做到「不仿真也能查全部路径」的
静态时序分析(Static Timing Analysis)名字里的「静态」,是相对于「动态仿真」而言的:它不给设计喂任何激励,而是把电路看成一张有向图——节点是引脚,边是经过单元和布线的时序弧,每条弧上标注延迟;再给时钟网络加上周期和相位信息。然后对每一对「有时序关系的起点和终点」,枚举所有路径,用延迟累加算出最坏情况,与要求比较。因为不依赖输入向量能不能「走到」某条路径,只要路径在拓扑上存在就会被检查,所以 STA 能做到全覆盖;代价是它不知道逻辑功能,只看时间——这正是为什么功能正确性归仿真、时间正确性归 STA,两者互补。
理解这一点能消除几个常见误解:①「我仿真跑了很多用例没问题,时序就应该没问题」——动态仿真在零延迟模型下根本不检查建立保持;即使跑时序反标仿真,用例没覆盖到的路径也不会被发现。②「STA 报违例的路径逻辑上不可能同时走通,可以忽略」——工具不知道功能约束,你若确信,应该用 false_path 等约束显式声明并在评审里说明,而不是无视报告。③「STA 太保守,实际芯片不会坏」——它的延迟来自晶圆厂在不同工艺、电压、温度角下的特征化数据,悲观余量是故意留的,量产一致性靠的就是这份保守。
完整 STA 会在多个「角(corner)」下进行:慢角(低温或高温、低电压、慢工艺)查建立,快角(高电压、快工艺)查保持,实现工具默认跑主角,签核工具(PrimeTime/Tempus)跑全角。入门阶段只需知道:你在 Vivado 里看到的 WNS 是按设定角算的悲观值,不是某颗芯片的实测。
六、Slack 公式逐行拆解
以最常见的同域寄存器到寄存器路径为例,建立检查的直觉公式是:
建立裕量 = 捕获沿时刻 - 建立时间要求 - 数据最晚到达时刻
数据到达 = 发射沿 + 源寄存器时钟到输出 Tco + 逻辑延迟 + 布线延迟
数据要求 = 捕获沿 - 目标端建立时间 - 时钟不确定度
+ (捕获时钟偏斜 - 发射时钟偏斜)逐项解释:Tco 是触发器从时钟脚到 Q 的内部延迟(几百皮秒量级);逻辑延迟是路径穿过的 LUT、进位链、MUX 延迟之和,Logic Levels 就是 LUT 级数;布线延迟是金属连线加开关延迟,在先进工艺和拥塞设计里经常超过逻辑延迟;时钟偏斜 skew 是同一时钟到达两个寄存器的时间差,捕获时钟晚到其实「借」给数据更多时间(公式里为正贡献),但工具用悲观方式同时考虑反向偏斜;不确定度 uncertainty 是为抖动(jitter)、时钟误差、分析误差预留的扣减;建立时间是终点触发器要求的前置稳定窗口。
保持检查发生在同一个沿(或下一发射沿,视模型而定):
保持裕量 = 数据最早到达时刻 - 保持时间要求
= 发射沿 + Tco + 最小逻辑/布线延迟 - 沿时刻 - 保持时间 - 偏斜项注意式子里没有时钟周期——这解释了「降频修不好 hold」。保持违例意味着数据太快,新一发射的数据在同一个捕获沿之前就冲穿了终点寄存器。FPGA 工具靠自动在布线上插入延迟单元、合理布局来修 hold,正常同步设计几乎不需要手工介入;hold 长期不过通常是跨时钟域缺约束、门控时钟、异步复位释放这类结构性问题的信号(同步设计纪律见 同步设计原则)。
读报告时把 Data Arrival 和 Data Required 两栏的每一行与上面公式对应起来,任何不懂的数字先在图里找到它是哪一段物理延迟,比死记字段名有效得多。
七、四类路径,四种读法
Timing Summary 把路径按检查类型分组,新手最容易只盯 Intra-clock:
- Intra-clock(同时钟域内):数量最多,setup 失败基本都在这里,按逻辑级数和布线占比定位。
- Inter-clock(跨时钟域):起点和终点在不同时钟。若两时钟有确定的相位频率关系(如同源 PLL 的 100M 与 50M),工具按关系分析是合理的;若两时钟异步,这类路径必须用 set_clock_groups 或等价约束切开,否则报出的违例数字没有物理意义,还会逼工具去优化不存在的需求、浪费资源。看到 inter-clock 报红,第一反应是查 CDC 约束,第二反应才是看路径(CDC 直觉见进阶 CDC 亚稳态)。
- Inputs(端口到内部寄存器):检查外部器件送来的数据相对你板上时钟是否来得及,需要用 input delay 描述板级走线和外部 Tco;没有输入延迟约束时,工具假定端口零延迟,报出的结论默认偏乐观,正经板级项目必须补。
- Outputs(内部寄存器到端口):同理用 output delay 描述下游器件的建立要求和板级走线。
此外报告还有 pulse width(时钟高低电平最小宽度,门控时钟、占空比异常会报)、min period(特殊单元如 RAM/DSP 输入的最小周期)等检查。负的 requirement 一般出现在带相位移动的同源时钟或多周期修正后,先确认时钟关系再判断,不一定是错误。
八、Logic Levels 与频率预算:多少级算深
建立违例的根因大多是「一个周期内串联的逻辑太多」。建立直觉量级:7 系器件在普通速度等级下,一个 LUT 加局部布线大约 0.5~1ns,触发器 Tco 与建立合计约 1ns 上下。100MHz 周期 10ns,扣除触发器开销、偏斜、不确定度约 2~3ns,剩 7ns 可容纳 7~12 级逻辑;200MHz 周期 5ns,只剩 2~3ns,超过 4~5 级就很危险。所以同样的代码在 50MHz「时序很好」、提到 150MHz 全面飘红,不是工具变差,是预算被压缩了三四倍。
Data Path Delay 里 logic 与 routing 的占比给出另一维线索:logic 占比高(比如 70% 以上)说明是结构问题——插流水线、重构宽 mux、用进位链、把运算移进 DSP 才有效;routing 占比高说明是物理问题——逻辑太挤、扇出太大、跨芯片对角线走线,靠加寄存器切分反而帮助有限,应该降扇出(复制寄存器、加 MAX_FANOUT)、改善布局、降利用率压力。很多新手见违例就加一级寄存器,结果路径换个地方继续红,就是因为没区分这两类。
常见的「级数黑洞」:级联比较器(几十个 if/else if 串成优先链)、宽位加法后再比较再选择(算术+判断挤一拍)、case 不全导致的宽 mux、循环展开生成的组合链、乘法后接组合查表。看到这些结构出现在 Endpoint 附近的逻辑里,直接重构比调工具策略有效得多。
九、约束错误伪装成时序问题
相当比例的「时序不过」其实是约束问题,学会先排约束能少走弯路:
- 主时钟周期写错或漏写:漏约束的时钟被当零周期或不分析,报告出现 unconstrained paths,这些路径完全没被检查,比违例更危险。
- generated clock 未自动派生:PLL/MMCM 输出一般由 XDC 原语自动生成时钟,但逻辑分频、外部时钟缓冲后的时钟要手工 create_generated_clock,否则下游路径按错误关系分析。
- CDC 没切:异步时钟互相分析产生海量假违例(见第七节)。
- 端口延迟缺失或瞎填:input/output delay 必须来自板级实际(外部器件手册加走线估算),不填等于不检查,瞎填一个数可能比不填更误导。
- 例外约束过度使用:false_path 一刀切到复位以外的路径、multicycle 方向写反,都会把真实违例藏起来。每条例外约束都要回答「为什么功能上真的不需要检查」,并代码评审。
- 多时钟同名或重复约束:例化多个 clk_wiz 时时钟名冲突,工具对同一对象施加两次约束,报告会有冲突警告。
养成先看报告顶部的「Check Timing」(未约束端点、无输入时钟、恒定时钟等)再看 WNS 的习惯:Check Timing 不清零,后面的绿色 Timing met 也可能是假的。
十、什么时候靠工具,什么时候必须改 RTL
优化手段按代价从低到高排列:
- 约束修正:零成本,先做。
- 策略与努力度:综合 strategy 选性能导向、实现开 explore/Performance_ExtraTimingOpt、post-route phys_opt,属于不改代码的手段,能榨出几个百分点到十几。
- retiming 重定时:允许工具把寄存器在组合逻辑间前后移动,前向移动等效自动插流水。前提是电路语义允许(无复位依赖的平衡边界),对滤波器、累加器这类规整数据路径有效;状态机和带握手的控制路径工具通常不敢动。
- 降扇出与手工布局:复制高扇出使能/状态、对关键模块加 Pblock 或 LOC 近似,属于物理手段。
- 微结构改造(主要手段):插流水线寄存器、拆分宽总线逻辑、状态机输出打拍、宽 mux 改多级、运算进 DSP、关键比较提前一拍做。每加一拍都要同步处理数据有效指示和延迟对齐,控制与数据面一起改。
- 降频或换器件:最后手段,但不必羞耻——在系统层用两个较低频接口桥接、或选快一档速度等级,常常比硬撑一个不合理的频率目标更省总工时。
阶段策略:综合后违例很大(负 1ns 以上)基本是结构问题,直接回 RTL,不要指望布线救;布局后小违例(负几百皮秒)可以让 phys_opt 和策略试;布线后才出现的零星违例多是物理拥塞,查利用率和扇出。绝不要把「综合后不管、布线后祈祷」当流程。
十一、把时序做进日常:报告自查清单
- Timing Summary 是否 Timing met?WNS/WHS/TNS/THS 三个数分别是多少,而不只是看绿灯?
- Check Timing 里还有没有 unconstrained、no clock、constant clock 条目?
- 失败路径前几名属于四类中的哪一类?跨域路径是否都有正确的 clock_groups?
- 最差路径的 Logic Levels、logic/routing 占比、起止模块是哪个?
- 端口约束是否按板级实际填写,还是空着或抄来的数字?
- 例外约束(false_path/multicycle/max_delay)每条都有功能依据吗?
- 综合后、布线后两个阶段都看过吗?改动 RTL 后违例数字如何变化,有没有记录趋势?
- 发布版本在去掉 ILA 后重新跑过时序吗(调试核会改变布线)?
时序收敛是贯穿架构、RTL、约束、物理实现的系统工程,入门篇只需先把「读懂报告、分类问题、按顺序动手」练扎实;量化方法和案例在进阶 STA 静态时序分析、约束实战、时序收敛方法 和 时序收敛案例 中展开。
十二、一个完整的收敛实例
把本文方法串成一个故事。假设工程从 50MHz 升到 100MHz 后,Timing Summary 报 WNS 为负 1.8ns、TNS 负四十多纳秒,违例路径三十多条。新手常见反应是直接降频或狂加策略,正确动作是分步收敛。
第一步,清约束:打开 Check Timing,发现一个逻辑分频时钟没有派生约束,先补上时钟定义,假违例少了一半;再看 inter-clock 分组,UART 接收时钟和系统时钟本就是异步关系却没切,补上 clock groups,又消掉一批。此时 WNS 还剩负 0.9ns,且违例集中在同一模块。
第二步,读最差路径:起点是数据缓冲读地址寄存器,终点是 FIR 输出的比较器,Logic Levels 显示 14 级,logic delay 占七成。结构判断:BRAM 读出后,同一个周期里做了乘法、加法、绝对值比较三级运算,典型的「一拍塞太多」。
第三步,改微结构:乘法输出加一级寄存,比较运算再推后一拍,相应把数据有效信号延迟两拍对齐下游。重综合后 WNS 转正 0.3ns,但布线阶段偶发两条路径报负 0.1ns。
第四步,看物理报告:这两条路径 routing 占比高、扇出都超过五千,源头是一个全局模式寄存器。把模式信号复制四份分区驱动(用综合属性限扇出或手工复制),布线后 WNS 稳定正 0.5ns、WHS 正、TNS 归零。
第五步,固化:在多个实现种子下各跑两三次确认稳定,而不是一次绿灯就收工;记录最终频率、最差路径、策略组合到项目文档。这个例子展示了收敛的正确节奏:先消灭假问题(约束),再改真结构(流水线),最后处理物理实现(扇出/布局),并用可重复性验证收尾。顺序反了就会陷入「改布线策略救结构问题、降频掩盖约束错误」的泥潭。
还要建立一个正确心态:时序违例不是工具在刁难你,它是芯片在说「在这个工艺、电压、温度的悲观组合下,我无法保证每个样本都正确」。忽视它上板,后果不是必然崩溃,而是偶发、随温度电压漂移、难以复现的现场事故——这类故障的排查成本是设计阶段收敛成本的几十上百倍。所以「Timing met 再上板」和「上车系安全带」一样,应成为不需要讨论的底线(这条底线在 仿真过上板挂 里还会从故障现象侧再印证一次)。
最后再给一个看报告的小习惯:每次实现后,不只看数字,还把最差的五条路径的起止模块名记下来。连续几版如果最差路径总在同一处,说明那里是架构级瓶颈,应该专门立项重构;如果每版最差路径都在变、且裕量只是零星为负,说明设计已经贴着频率上限跑,要么系统性降目标频率,要么整体加流水而不是到处打补丁。带着「瓶颈演化」的视角看时序报告,你就从被动救火变成了主动管理设计的频率能力。
补充几个容易被忽略的报告细节。第一,Timing Summary 里还有「TPWS 脉冲宽度检查」和时钟不确定性汇总,如果主时钟本身来自噪声大的板级振荡器,工具默认的不确定度可能不够,需要按器件手册加 set_clock_uncertainty,看到 WNS 余量普遍只有一两百皮秒时尤其要注意。第二,跨时钟域路径即使写了 clock_groups,也建议用独立的 CDC 报告或工具(Vivado 的 report_cdc、第三方 SpyGlass CDC)再扫一遍,时序例外只能让 STA 闭嘴,不能保证同步器真的放对位置、两级 FF 中间没插逻辑。第三,IP 核常带来自己的约束文件,综合后检查 report_clock_interaction,确认自动生成时钟的关系(同源、异步、不可分析)符合你的系统设计,IP 升级后这张表可能变化。第四,留意 Endpoint 出现在 IOB、IDELAY、IDDR 等 IO 原语上的路径,那是源同步接口的时序,要用专门的输入输出延迟约束和读回校准,和片内路径的收敛手法完全不同(接口时序见进阶 IO 规划)。时序报告读到最后,你会发现它其实是一份「设计与物理现实之间的对账单」:约束表达你的意图,报告表达硅片的能力,工程师的工作就是让两者对齐。
最后给一个读报告的顺序口诀,方便上手时照着走:先绿后红、先约束后路径、先结构后布线、先最差后批量。先绿后红是先确认 Timing met 总状态和 Check Timing 是否干净,再点进红色路径;先约束后路径是排除假问题再修真问题;先结构后布线是先问「这拍逻辑是不是太多」再问「是不是布局太挤」;先最差后批量是修掉 WNS 路径后,TNS 往往成片消失,因为很多违例路径共享同一段逻辑,不要被违例条数吓到。跟着这个顺序处理几轮,你会发现时序收敛从「看不懂的玄学」变成「有固定套路的工程动作」,这正是从能写 RTL 到能交付 FPGA 的关键台阶之一。再补一句经验之谈:修时序时每改一版只动一个变量(加一级流水、或降扇出、或换策略),并记录 WNS 变化;同时改三样东西然后时序绿了,你永远不知道是哪样起的作用,下次同类问题还是不会修。工程能力就是这样从一次次有记录的对照实验里积累出来的。最后提醒:时序报告是迭代产物,不是一次性作业。每次大改结构后重新读一遍最差路径,就像每次改完代码跑一遍测试一样自然;等整个工程写完才第一次看时序,往往要面对几十条跨十几个模块的违例,那时再流水化改造,牵涉数据对齐和握手改动,成本是随手维护的好几倍。把时序当作持续反馈,而不是终点验收。再补充两个团队协作中的报告实践。第一,代码评审里附时序截图或文本摘要:修改数据通路的合并请求,作者应附上修改前后最差三条路径的对比,评审人才能判断这次改动有没有把瓶颈从一个模块推到另一个模块;只写一句「时序过了」等于没说。第二,给关键模块建立「时序档案」:记录每个版本在目标频率下的 WNS、最差路径端点、采用的结构(几级流水、几个 DSP),当别人要复用这个模块到更高频率场景时,档案直接告诉他瓶颈在哪、还能不能加半拍,而不必重新踩一遍坑。时序分析能力的成长,就体现在这些可复用的判断沉淀里。再补一个实操建议:每次处理完一条难搞的违例路径,把报告导出存进项目的「时序档案」目录,文件名写上模块、版本和最终修法。积累十几份之后你会发现自己开始能预判:看到某种端点组合和逻辑级数,还没动手就知道大概要插几级流水、会不会波及相邻路径。这种基于案例的直觉,和老医生看片子一样,只能从真实病例积累——时序报告就是你的病例库。最后再给一个预防性的设计习惯:在写 RTL 的阶段就给关键路径预算拍数。例如一次「BRAM 读出 → 乘法 → 比较 → 更新状态」的操作,在目标频率下大约需要几级流水,应该在动手前写在微架构笔记里,而不是等综合报告红了才发现一拍塞不下三拍的逻辑。这种「先预算、后编码、再对账」的节奏,本质是把时序收敛从末端测试前移到设计阶段,是区分初级和成熟工程师的重要标志。报告是最后的验收,不是第一次思考时序的地方。
再展开讲一个新手最容易误判的点:WNS 只是「最差那条路径」的数字,它不告诉你违例的分布。两个设计都报 WNS 负 0.3ns,一个可能只有一条路径违例(某个特殊控制译码),另一个可能有两千条违例(整条数据通路全面超标),修法完全不同:前者微调局部逻辑即可,后者必须系统性插流水或降频。所以读报告一定要把 WNS 和 TNS、违例路径条数一起看。同理,WHS 为负 0.01ns 和负 0.5ns 是两个故事:前者通常布线器在某个角下没收敛干净,换 directive、加 phy_opt 多半解决;后者说明存在结构性快路径(常常是时钟偏斜很大的跨区域路径或漏约束的同源分频时钟),要回到时钟结构和约束上找原因。学会「看数字的形状而不是只看正负号」,时序报告才真正开始为你工作。
常见坑对照
| 坑 | 后果 | 正确做法 |
|---|---|---|
| 时序不过还上板 | 偶发难复现错误 | WNS/WHS 收敛到 ≥0 |
| 时钟约束写错 | 报告结论无效 | 先核对 create_clock |
| 跨时钟域没切分 | 假失败 | set_clock_groups/false_path |
| 长组合逻辑不打拍 | 逻辑级数深、WNS 负 | 插流水线 |
| 只看综合不看实现 | 乐观估计 | 以实现后时序为准 |
| 忽视高扇出 | 布线拖慢 | 复制/优化大扇出 |
相关与延伸
入门:XDC 管脚与时序、LUT 与 FF 深入(逻辑级数)。 深入:进阶 STA 静态时序分析、约束实战、时序收敛方法、时序收敛案例。
一句话总结
时序报告判定“在目标频率下数据能不能在一个周期内稳定到达”:建立时间要求数据在时钟沿前稳定、保持时间要求沿后保持一会,Slack(裕量)=要求−到达,为正满足、为负违例。Timing Summary 先看 WNS(最坏建立裕量)和 WHS(最坏保持裕量)是否都 ≥0(Timing met),TNS 反映违例面有多大。失败路径重点读 Source/Destination Clock(跨域报红多半是缺 set_clock_groups 而非真慢)、起点/终点触发器、Data Path Delay(逻辑+布线)、Logic Levels(穿过几级 LUT,太深是 setup 失败主因)。WNS 为负的处理顺序:先查时钟/管脚约束和跨时钟域切分,再定位大加法/宽比较/大 mux 等长组合逻辑并插流水线打拍降逻辑级数,辅以 retiming/物理优化/提高努力度,实在不行降频——时序不过绝不能直接上板,否则是偶发难复现的 bug。
上一篇:36 资源利用率报告(LUT/FF/BRAM/DSP) | 下一篇:38 同步设计原则(避免门控时钟/组合环) | FPGA 系列目录