FPGA 进阶 02:时序收敛方法——打拍/retiming、策略、物理优化与拥塞治理
本文是
fpga/进阶层第 06 篇。前两篇讲清了 STA 怎么读报告、约束怎么写全。这篇解决最实际的问题:约束都对、WNS 还是负的,怎么一步步收敛到全绿。通用的打拍/retiming 原理可对照 时序收敛基本手法,本篇专注 FPGA 的工具策略、物理优化和离散资源特性。
本文要回答的问题
- WNS 为负,先动 RTL 还是先动工具?
- Logic Delay 大和 Net Delay 大,分别怎么治?
- Vivado 的综合/实现策略(Performance_Explore 等)怎么选,真有用吗?
- retiming 是什么,和手动打拍怎么取舍?
- 高扇出、布线拥塞、利用率天花板怎么破?
- hold 违例为什么通常不用自己管?
一、收敛总原则:先诊断、再分层下手
时序收敛不是乱试,是按成本从低到高逐层加码。顺序如下,每层做完重新实现看 WNS/TNS:

铁律:第 0 步先排除「假违例」。很多 WNS 为负根本不是时序问题,而是约束缺失:跨时钟域漏 set_clock_groups(异步路径被当同步收敛)、PLL 输出漏 create_generated_clock、IO 漏 input/output delay。先按 约束实战篇 第八节 check_timing/report_clock_interaction 清零,再谈优化。约束错的前提下做的所有收敛都是白费。
二、先分流:看 Logic Delay 还是 Net Delay
用 report_timing 打开最坏路径(STA 篇第五节),按延迟构成分流,两类治法完全不同:
| 违例特征 | 病根 | 主治手段 |
|---|---|---|
| Logic Delay 占比大、路径穿多级 LUT/进位链 | 逻辑太深(大 MUX、宽加法器/比较器、长 if-else) | RTL 打拍/流水线、retiming、拆 case |
| Net Delay 占比大、扇出高、两点布得远 | 布线问题(高扇出、拥塞、利用率高) | MAX_FANOUT 复制、Pblock、降利用率 |
| 违例集中在 IO/跨时钟 | 约束问题 | 补 input/output delay、clock_groups(回到第 0 步) |
FPGA 上 Net Delay 常占一半以上,所以**「布线绕」比「逻辑深」更常见**,别一看到违例就改 RTL。
三、零成本第一层:换综合/实现策略
Vivado 在综合和实现(布局布线)阶段提供不同 strategy,本质是让工具用更激进/更耗时的算法去优化。违例小(WNS 在 -0.3 以内)时,这一层经常直接救回,不改一行代码:
| 阶段 | 策略 | 适用 |
|---|---|---|
| 综合 synth_design | 默认 default;Flow_AlternateRoutability、开 -retiming | 逻辑易拥塞、想让综合先平衡寄存器 |
| 实现 opt/place/route | Performance_Explore | 通用提性能,最常用的「救时序」策略 |
| 实现 | Performance_NetDelay_high/medium | Net Delay 大时,布局更重布线延迟 |
| 实现 | Performance_Retiming | 允许 post-route 重定时 |
| 实现 | Area_Explore / Area_ExploreHigh | 拥塞/利用率高时先压缩面积 |
用法:Vivado 工程里 Implementation → Strategies 下拉选择,或 Tcl set_property strategy Performance_Explore [get_runs impl_1] 后 launch_runs。实用技巧:用 launch_runs impl_1 -to_step write_bitstream -jobs N 配合多个 run(impl_1/impl_2…)并行跑不同 strategy,取 WNS 最好的——布局布线有随机性,同一策略多跑几次结果也会浮动。
Quartus 对应:开启 Physical Synthesis Optimization(Perform physical synthesis for fitting / register retiming)、用 Fast Fit 关、Fitter 努力度设为 Extra/Maximum,以及多 seed 编译(SEED 不同取最好)。
四、网级与物理优化:降 Net Delay
4.1 高扇出复制(MAX_FANOUT)
一个信号(复位、使能、全局状态机状态)驱动几百上千个负载时,布线器被迫把它拖成一张大网、延迟和 skew 都差。让工具复制多份、每份只带一部分负载:
// RTL 属性:把这个信号扇出限制在 200,超出工具自动复制
(* MAX_FANOUT = 200 *) reg enable;# 或在 XDC 里对某网络设
set_property MAX_FANOUT 200 [get_nets -hierarchical *enable*]典型受益对象:复位、时钟使能、状态机输出、广播 valid。复制后逻辑功能不变(每份负载看的是同一个值),只是物理上多了几个驱动源,布线大网拆小。
4.2 物理位置约束 Pblock
关键路径的两个寄存器被布到芯片对角,Net Delay 自然大。用 Pblock 把强相关的模块「圈」在一块物理区域,强制就近:
# 画一个矩形区域,把关键模块放进去
create_pblock pblock_fast
add_cells_to_pblock [get_pblocks pblock_fast] [get_cells u_dsp_chain/*]
resize_pblock [get_pblocks pblock_fast] -add CLOCKREGION_X0Y0:CLOCKREGION_X1Y1五、RTL 级:打拍、流水线与 retiming
当违例大(WNS −0.5 以下)、TNS 很高,或换策略稳定不过,说明组合逻辑深度超过了一个周期能容纳的量,必须在 RTL 切短。
5.1 手动流水线打拍
在长组合逻辑中间插入一级寄存器,把「N 级 LUT 一周期走完」切成「两段、每段半周期」。典型对象:
- 宽加法/比较/乘法后的长进位链(乘法尽量让工具推断 DSP48,别用 LUT 堆);
- 一个 clock 沿里做「读 BRAM → 组合运算 → 再判断 → 再选」的串行长链;
- 大
case/多层if-else的优先级 MUX。
代价:多一级延迟(latency +1)。数据通路打拍要同步给所有并行信号加相同拍数(对齐),valid/ready 反压逻辑要相应加拍或用 Skid Buffer(见 valid/ready 握手)。
5.2 retiming:让工具自动搬寄存器
retiming 让综合/实现工具自动在组合逻辑间前后移动寄存器,在不改变周期级输入输出行为的前提下,把寄存器从「两级之间逻辑过满」处挪到「逻辑过空」处,平衡每级延迟:
# 综合阶段开 retiming
set_property STEPS.SYNTH_DESIGN.ARGS.RETIMING true [get_runs synth_1]
# 或实现用 Performance_Retiming / Performance_Explore 策略- 优点:零 RTL 改动、自动平衡,对「逻辑级数分布不均」的路径效果好。
- 限制:寄存器不能跨模块/黑盒(IP)边界搬、不能越过有特殊属性的寄存器、也不会凭空增加总吞吐(延迟周期数不变,只是重新分布)。
- 取舍:先开 retiming 让工具免费试;真正吞吐受限、需要增加流水级的大模块,仍要手动打拍。实践是「retiming 兜底 + 关键路径手动打拍」。
5.3 让资源走「快车道」
- 乘法/乘加写清楚位宽,让工具推断 DSP48E1/E2(而非用 LUT 搭乘法器),DSP 内置寄存器还能顺手流水;
- 大存储用 BRAM/URAM(输出自带寄存器、打拍几乎免费),别用分布式 RAM 拼大数组;
- 移位寄存器用 SRL(
srl_style)或 BRAM,别用一长串 FF。 这些既降 Logic Delay 又省资源,是「写法决定能否收敛」的典型。
六、拥塞与利用率:收敛的天花板
如果 Net Delay 特别大、布局布线时间暴涨、换什么策略都差一点点,大概率撞上布线拥塞(congestion)。FPGA 布线资源预制有限,利用率超过 ~70~80% 后,布线器很难把相关逻辑放近,被迫绕长线,Net Delay 失控——这是工具再努力也突破不了的天花板。
判断与破解:
- 看利用率报告(Report Utilization):LUT/FF/BRAM/DSP 各多少。某一项 >80% 尤其危险。
- 看 Congestion 报告(Vivado
report_route_status/ Device 视图里的拥塞热区):大片红色即拥塞。 - 破解手段:
- 砍冗余:没用到的位宽/状态/调试逻辑删掉;
- 资源复用:大 RAM 归并到 BRAM、乘法归并到 DSP、重复逻辑
generate复用; - 降规模:降数据位宽、降并行度、多时钟分时复用;
- 换更大器件 / 留 20~30% 布线余量给工具。
七、hold 违例:多数工具自动修,别降频
和 setup 不同,FPGA 上 hold 违例大多不用手动修:
- 全局时钟网(BUFG)skew 很小,hold 本就比 ASIC 少;
- 布线器能在数据路径上插入可编程延迟(LUT 输入延迟、布线延迟)把过快的短路径拖慢,自动满足 hold。
所以实现后 THS 通常为 0。若 hold 仍红,按序排查:① 是不是跨时钟域漏约束造成的假 hold(回到第 0 步);② 有没有非全局时钟(逻辑门控、普通布线走时钟)破坏低 skew;③ 相邻寄存器物理上太近、路径短到无法再插延迟——极端情况用位置约束把两点拉开。记住 hold 与频率无关,降频对 hold 毫无帮助。
八、一个真实排查顺序:WNS 从 −0.8 到 +0.3
1. check_timing + report_clock_interaction
→ 发现 125MHz 恢复时钟与 100MHz 系统时钟漏 set_clock_groups
→ 补上,跨域假违例消失,WNS -0.8 → -0.45
2. report_timing 看最坏路径:Net Delay 占 60%、扇出 2300 的全局 valid
→ 加 (* MAX_FANOUT=250 *),WNS -0.45 → -0.28
3. 实现策略换 Performance_NetDelay_high,并行 3 个 run
→ 最好一个 WNS -0.12
4. 剩余违例路径 Logic Delay 大:一条「BRAM读出→乘→比较→选」单周期长链
→ 在乘法输出手动打一拍,DSP 输出寄存器也用上
→ WNS +0.18,TNS 0
5. setup 全绿后确认 hold:THS 0(工具自动插延迟)
→ 收敛完成,留约 0.2ns 余量上板稳定九、收敛的经济学:什么时候该停止优化
时序收敛不是一个纯技术问题,也是资源分配问题;学会判断「什么时候 WNS 已经够好、应该收手」和知道怎么修违例同样重要。先建立量级概念:内部同步设计在目标周期留有 10% 左右余量(比如目标 10ns、WNS 稳定在 +1ns 上下)通常足以覆盖工具版本波动、后期小改动和温度电压变化;追求 WNS=+3ns 可能要付出多打两级流水线、面积增加、延迟改变的代价,而这些代价对功能和其他路径是真实的伤害。停止优化的信号:违例路径集中在一两个确实复杂的模块且已用合理手段处理、多轮构建结果稳定(不是这版 +0.4、下版 −0.3 的随机抖动)、资源利用率在健康区(LUT 低于 70%、拥塞报告无热点)、WHS 为正、降档风险评估通过。不该停的信号:WNS 靠极端策略(高成本 place_design、物理优化开满)勉强为正、换个种子就翻负、利用率贴线——这种「绿色」是脆弱的,任何小改动都会让它崩盘,应该回头降架构级风险(降目标频率、改微架构)而不是继续榨工具。收益递减曲线:第一遍诊断和流水线修改通常解决 80% 的违例;中间 15% 靠布局约束和拥塞治理,耗时数倍;最后几个 ps 可能耗掉和前面全部一样的时间。成熟工程师把时间投在「让系统对随机种子不敏感」(降利用率、消除热点)而非「让某一次构建变绿」。频率目标本身要可谈判:当收敛成本显示目标不现实时,拿数据(最差路径清单、需要插多少级、面积代价)去和系统团队谈降频,而不是一个人默默和工具搏斗到深夜;很多项目的频率指标是拍脑袋定的,时序报告就是最有力的谈判材料。最后记住:收敛的交付物不是「一次绿灯截图」,而是「在版本演进中可持续保持正裕量的设计状态」。
十、跨时钟域路径的时序收敛:规则不同的特殊地带
CDC 路径出现在时序报告里时,处理哲学与同步路径完全不同——目标不是让它「跑得快」,而是让它「被正确地豁免、且电路本身可靠」。在 07/08 两篇 CDC 专题之外,这里从收敛视角讲几个与工具搏斗的要点。先确认它真的异步:最常见的错误之一是对同源分频时钟误用 clock_groups,把本来可以也应该收敛的路径豁免了。收敛流程的第一步是拿到 report_clock_interaction,逐对时钟标注同源关系(由 MMCM 配置自动推导,人工复核);只有真正异步(无固定相位/频率关系,如两个独立晶振、恢复时钟与本地时钟)才进入异步处理流程。异步路径的「收敛」标准:单 bit 路径标异步后,时序报告不再要求 setup 满足,但良好实践仍给同步器路径加 ASYNC_REG + 短 max_delay,防止第一级采样窗过大;检查项变成「所有异步路径都落在已知同步器/异步 FIFO 上」,没有一条裸奔的多 bit 路径。复位相关路径:异步复位断言不需要时序收敛,但复位释放必须同步——跨域复位路径的豁免和同步器要成对审查,这是 09 篇的主题。收敛报告的呈现:签收时时序报告应清楚分区——同步路径组(全部正裕量)、已识别异步组(列出同步机制证据)、故意例外(false path/multicycle 及依据);评审者要能在十分钟内确认「没有红色,且没有不该绿的地方绿了」。偶发问题的归因:系统在温度变化或批次间偶发失败、时序报告却全绿时,CDC 永远是头号嫌疑——它本来就是概率性故障,STA 全绿不代表异步电路正确,这是 CDC lint 工具和长时间压力测试存在的理由。把「时序收敛」和「CDC 正确性」当成两份独立的签收清单,是避免虚假安全感的关键。
十一、签核前的时序回归:让绿灯在迭代中保持绿色
项目后期 RTL 每周小改,时序签收不是一次性事件而是持续状态;没有回归机制,团队会反复经历「上版还绿、这版突然红了」的惊吓。基线管理:选定一个已签收版本作为时序基线,保存其完整报告:每对时钟的 WNS/WHS、最差十条路径、各路径组裕量分布、利用率、拥塞等级、工具版本和策略种子。新版本构建后自动与基线 diff——重点不是只看全局 WNS,而是看哪些路径的裕量显著移动(变差 0.5ns 以上的路径即使仍为正也要关注,它们是下一版的候选违规者)、有没有新冒出来的路径组、利用率变化趋势。变更影响预判:RTL 改动评审时,改动者应说明是否触及已知关键模块、是否增加逻辑层级、是否改时钟/复位;CI 可做快速综合预估(不跑完整 P&R,先看综合后逻辑延迟趋势),把明显会炸的改动挡在完整过夜构建之前。构建矩阵的最小化:全量多策略多种子构建成本高,日常回归跑一个固定的「签收配置」(同一工具版本、同一策略、固定种子),保证结果可比;在里程碑和最终签收时才跑多种子/多策略确认健壮性。配置漂移(有人在 IDE 里偷偷改了策略又没提交)是基线对比失真的常见原因,因此策略、约束、器件型号、工具版本全部纳入版本控制,构建从干净检出出发。温度与电压角:工业级应用要在工具支持的慢角模型下签收,并知道 FPGA 的时序分析基于器件实际速度等级(签收时应按最慢的预期等级约束)。签核包归档:每次对外发布(给软件团队的比特流、给客户的版本)附完整签核包——报告、约束、工具版本、构建号、未关闭项清单(带豁免签字)。时序回归的价值在于把收敛从「英雄式冲刺」变成「可预期的维护」:团队睡得着觉,因为任何一次退步都在入库当天被自动发现,而不是在演示前一晚。
十二、流水线改造的方法论:在哪里插拍、插完怎么验证
打拍流水线化是 RTL 级收敛里收益最大、也最容易改错的手段,值得把决策流程讲透。第一步,在路径图上找可切分点:一条长组合路径通常由几段天然功能块组成(译码、选择、运算、移位、比较),在功能块之间而不是函数中间插级,新增寄存器才有明确语义。跨模块边界的路径优先考虑在源头模块出口或目的模块入口加级,避免改动扩散。第二步,分类处理路径语义:纯数据流(滤波器、运算链)加级几乎无副作用,只增加延迟,把延迟值参数化、在验证模型里同步;带反馈的路径(累加、线性反馈、状态机自身)不能简单插级——插入的拍数改变回路行为,必须和算法/微架构负责人共同确认,必要时调整控制时序;带握手的通路(valid/ready)加级要成对处理 valid、数据和 ready 反压,成熟做法是用标准 skid buffer 模板而不是手工各改各的。第三步,照顾下游期望:某些协议对延迟敏感(固定拍数的接口),插级要整体平移所有相关路径保持相对时序;内存映射总线的访问延迟改变要更新软件/文档中的等待周期。第四步,验证不是重跑全部仿真就完事:定向检查加级后协议的反压行为(满时停、空时空转)、延迟计数器参数、乱序/保序属性;插级最常见的 bug 不是数据算错,而是 valid 提前/滞后导致丢拍或重复。与时序工具配合:加级后重新实现,观察违例路径是否如预期消失,且新级没有制造新的长路径(寄存器放得太靠近一端,只是把压力转移)。保留退路:流水线级数用参数控制,便于在频率目标变化或换到更快器件时减级,减级同样要过完整验证。把插级当作一次正式的微架构变更来评审(有方案、有影响面、有验证计划),而不是收敛会议上「这里塞两个寄存器试试」的临时动作,是团队成熟度的标志。
十三、hold 违例、负裕量与多角分析的深入处理
第七节说 hold 多由工具自动修,这里展开几个自动修不了、必须人工判断的场景。收敛顺序的铁律是先 setup 后 hold:修复 setup(插级、降频、优化布局)会改变布线,可能让原本过的 hold 翻负;因此在 setup 还在剧烈波动时死磕 hold 是浪费。到 setup 稳定后再开 hold 修复并复查。真正顽固的 hold 问题往往是结构性的:一是时钟偏斜超过数据延迟——接收端时钟比发送端早到太多,工具加缓冲也补不平,通常源于不该走通用布线的时钟被数据化(手工造的门控、时钟经组合逻辑),根治要改时钟结构;二是异步/跨域路径被错误地当成同步路径检查,表现为大堆无法解释的 hold 红,解决方法是修正约束(clock_groups/同步器 max_delay),不是加延迟;三是复位、低使能信号等特殊网络被工具优化策略忽略,需要显式处理。负裕量报告的解读:WNS 为负对应 setup(采样时数据没到,典型表现是高频错、降频后好转),WHS 为负对应 hold(数据太快穿透,典型表现是「甚至很低频还错」,因为加周期不解决 hold——这是区分两类问题的现场判据)。多角多模:FPGA 虽无 ASIC 那样多工艺角,但工具有慢角(setup 签核)和快角(hold 签核),工业温度范围大的设计要在两个角都签收;慢角过不代表快角 hold 安全。FPGA 里少见但存在的 OCV:高级器件支持片上变化分析,对极高速设计应开启,别用单角单模的乐观结果签收。当工具放弃修复:报告中标记 unroutable/unsupported 的违例,先看是不是约束自相矛盾、资源被锁死(Pblock 把模块钉得太小)、IO 位置强迫超长走线——这些情况下优化器无路可走,松绑约束比加缓冲有效。最终签收纪律:setup 与 hold 在签核角同时为正、最差路径有记录、所有例外有依据、降频结论与系统团队书面确认。
十四、一个完整收敛案例的复盘:从负 2.4 纳秒到签收
把方法论落到一个具体案例上复盘,比抽象原则更能建立手感。某数据采集板升级后增加了一路 4K 视频处理,综合实现后全局最差时序 WNS 为负 2.4 纳秒,路径集中在新模块到 DDR 控制之间;hold 全绿,资源利用率约 62%,看似不拥塞。分诊:报告显示最差路径逻辑延迟与布线延迟约四六开,路径终点是 DDR 侧寄存器,跨了两个时钟使能域,中间有一大段地址译码加比较的组合链。第一层尝试(零成本):换实现策略为性能探索、提高物理优化等级、跑三个种子——最好结果负 1.6 纳秒,且种子间波动 0.4 纳秒,说明结构性问题仍在,继续调策略是浪费。第二层(物理):检查发现新模块被布到远离 DDR IO 的另一侧,加了适度的 Pblock 引导靠近,不加锁死,布线延迟下降约 0.5 纳秒,WNS 负 1.1。第三层(RTL):联合设计师走查路径,地址译码和突发长度比较可以提前一拍在源模块完成、寄存后再跨模块,等价于在长链中段插一级;同时确认该接口协议本来就允许这一级延迟(参数化延迟并更新说明)。重实现后 WNS 转正 0.2,但有两条路径仍在负 0.1 边缘徘徊。收尾:对边缘路径做小幅逻辑重组(把一个宽多路选择树重排为平衡树),最终最差 WNS 正 0.6、WHS 正 0.2,种子间波动收窄到 0.1 以内,利用率未明显上升,频率目标不变即签收。复盘要点:顺序严格按「先诊断后动手、先零成本后 RTL」执行,没有一上来降频;每步只改一类因素并记录效果,负 2.4 到正 0.6 的每一分贡献都可解释;Pblock 是引导而非钉死;插级经过协议确认;签收时关注裕量稳定性而非单次绿色。这个案例没有任何奇技淫巧,它的教育意义正在于此——绝大多数收敛问题死于纪律,而不是死于天赋。
十五、结语:收敛是一场与物理现实的谈判
时序收敛的所有技巧最终都在同一件事上汇合:承认物理延迟不可商量,然后决定在哪个层面付出代价——让工具多算一会、让布局更讲道理、让流水线多一级、让频率目标更诚实。最差的策略是否认这场谈判的存在:不看报告只看红绿、用极端策略硬压、靠降频掩盖结构问题、把偶然一次绿色当结论。最好的策略是带着数据逐层推进、每一步的收益和代价可解释、让最终的正裕量对种子和版本变更都保持鲁棒。还要记住收敛不是终点而是状态:签收之后 RTL 仍会变、工具会升级、器件批次有差异,因此真正交付的是一套基线、回归和签收纪律,而不是一张截图。当团队里的每个设计师都能说出自己模块最差路径在哪、为什么、还剩多少裕量,收敛会议就不再是通宵仪式,而变成十几分钟的数据确认——这是时序工程成熟度最直观的信号。把本文的排查顺序打印贴在工位上,下一次红色报告出现时按层走一遍,你会发现绝大多数夜晚其实不需要存在。时序问题从不针对个人,它只惩罚跳过诊断直接动手的人。
常见坑对照
| 坑 | 现象 | 正确做法 |
|---|---|---|
| 约束没清就猛改 RTL | 越改越乱、收效甚微 | 先 check_timing 清零、异步域/IO 约束补齐 |
| 不看 Logic/Net 比例 | Net 问题却去打拍,没用 | Logic 大打拍、Net 大降扇出/就近布局 |
| 一上来就降频 | 掩盖问题、浪费性能 | 降频是最后手段;先策略/复制/打拍 |
| 高扇出全局使能不处理 | Net Delay 巨大、时好时坏 | MAX_FANOUT 复制,复位/使能重点查 |
| 利用率 >85% 硬收敛 | 换策略也差一点点 | 降利用率/换器件,留 20~30% 余量 |
| hold 违例去降频 | 毫无改善 | hold 与频率无关;查约束/非全局时钟 |
| 乘法用 LUT 堆 | 又慢又占资源 | 写清位宽推断 DSP48,大存储用 BRAM |
相关与延伸
前置:静态时序分析(看 WNS/Logic/Net)、约束实战(先保证约束完整)。 工程规范:工程实践——CDC/复位/功耗、常用 IP 核(DSP/BRAM/FIFO)。 通用原理(ASIC 视角):时序收敛基本手法——打拍、流水线与 retiming 的芯片侧讲法;真实收敛案例见后续专家篇 [时序收敛实战案例](规划 #47)。
一句话总结
FPGA 时序收敛按成本从低到高分五层加码,且第 0 步永远是先确认约束完整(check_timing 清零、异步域 clock_groups、IO delay 补齐,假违例不修白修):然后用 report_timing 分流——Logic Delay 大=逻辑深,先开 retiming/换 Performance_Explore 等实现策略并行多 run 捡便宜,违例大再回 RTL 手动打拍/流水线、让乘法进 DSP48、存储进 BRAM;Net Delay 大=布线绕,对高扇出复位/使能加 MAX_FANOUT 复制、对紧耦合模块用 Pblock 就近布局;换策略、复制、打拍都救不动且布线时间暴涨,是撞上拥塞天花板(利用率 >70~80%),要降位宽/复用资源/换更大器件留 20~30% 余量;hold 多数由布线器自动插延迟修好、且与频率无关,降频无用——核心心法:先诊断后下手、零成本策略先行、RTL 打拍兜底、利用率是天花板。