这一篇在干嘛?
综合完成之后,工具还要把逻辑”摆”进芯片(布局 Place)再用导线连起来(布线 Route)。本章告诉你:哪些优化选项真正影响性能、哪些是浪费时间,帮你避免”乱调参数不见效”的困境。
16.1 先把约束写完整
任何优化开始之前,必须先有一套完整的时序约束——所有时钟定义、I/O 延迟、引脚位置,以及多周期路径(multicycle path,数据可以用多个时钟周期传输的路径)和伪路径(false path,逻辑上根本不会发生传输的路径)等放宽约束。
为什么”放宽”也算优化?因为把非关键路径的约束放松后,布局布线工具会把时间和布线资源省下来,留给真正紧的关键路径。
容易被忽视的电压/温度约束
FPGA 时序分析默认使用最坏情况:最高结温(junction temperature,芯片内部实际温度)+ 最低供电电压。因为温度越高、电压越低,晶体管越慢,传播延迟越大。以 Xilinx 商用温度范围为例:
- 最坏结温:85°C
- 最坏电压:标称电压的 95% 左右(1.2 V 轨取 1.14 V,3.3 V 轨取 3.0 V 等)
但你的实际系统未必这么极端。如果供电是线性稳压(波动远小于 5%),芯片也不会跑到 85°C,就应该按真实条件做时序分析:
| 场景 | 默认设置 | 现实设置 |
|---|---|---|
| 电压偏差 | 95% | 98%(仅 2% 波动) |
| 结温 | 85°C | 45°C |
书中例子:Virtex-II 上一个 16 位计数器,默认条件下最高跑 276 MHz;把电压/温度改成符合实际系统的取值后,直接跑到近 290 MHz——不改任何设计白赚约 14 MHz。
两个注意点:
- 这个方法不改 RTL、不重跑实现,是”零成本”提升,应该最先尝试。
- 前提是厂商已完成按温度/电压的特性表征(characterization)。刚发布的新工艺器件往往没做完,工具里这个选项会是灰的——所以”成熟一点的器件”反而有这个优势。
16.2 布局与布线的关系
现代工具的自动布局布线已经很强,很多工程师因此从不关心后端选项。但这里有一个关键事实:
布局 effort 对性能的影响是第一位的(一阶效应),布线 effort 的影响相对次要。
原因在于 FPGA 的布线矩阵(routing matrix,芯片内预先铺好的导线开关网络)是”粗粒度”的,灵活性远不如 ASIC。好的布线只能建立在好的布局之上——位置摆得不好,再怎么努力绕线也绕不出好时序。
如果你把”布局 effort × 布线 effort”的所有组合跑一遍并画图,会看到类似这样的曲线:
图16-3 布局/布线努力程度与性能的关系:布局曲线陡峭(主导),布线曲线平缓
推荐的调优流程:
- 布局、布线 effort 都设为最低;
- 跑一遍,确认最差时序确实是布局问题,而不是逻辑级数太多;
- 逐步提高布局 effort,直到满足时序或到最大档;
- 布局到最大档仍不满足时,再逐步提高布线 effort;
- 还不行,回头改架构;
- 如果需要很高的布线 effort 才能过时序,说明架构本身该优化了(见第 1 章)。
16.3 逻辑复制(Logic Replication)
问题场景:一个寄存器的输出扇出(fan-out,一个信号驱动多少个负载)到两个相距很远的模块。无论驱动器摆在哪,总有一路布线很长。
解决办法:把驱动器复制一份,让每个副本靠近各自的负载,从而缩短布线、减小延迟。
图16-4 驱动器只有一个,两路负载距离远,必有一路布线很长
图16-5 复制寄存器,让每个驱动器靠近各自的负载
适用条件与代价:
- 只对关键路径上、多负载且无法物理集中的网络使用;
- 复制会增加面积,如果器件利用率已经很高,效果可能反而变差;
- 如果工具不够聪明(对非关键路径也乱复制),可以在 RTL 里手动复制并加
don't touch属性,防止综合把重复结构优化掉。
16.4 跨层次边界优化(Optimization Across Hierarchy)
图16-6 关键路径跨越了模块(hierarchy)边界
模块边界处的逻辑往往填不满一个 LUT。例如两个模块交界处各有一个 NAND:
图16-7 两个边界上的 NAND 各占一个 LUT,合并后只需一个
开启”跨层次优化”后,工具可以跨越模块边界合并逻辑,把两个 NAND 揉进一个 LUT,这一小块的面积直接省一半。
但有一个重要例外:如果你需要做门级仿真(gate-level simulation,用实现后网表仿真)或后调试,要保持层次结构完整——层次保留后,你能沿着模块结构遍历设计,也容易找到边界上的信号来调试。此时不要开这个选项。
16.5 I/O 寄存器(I/O Registers)
很多 FPGA 的输入/输出缓冲器(I/O buffer)里内置了触发器。把寄存器”打包”进 I/O 有四个好处:
- 芯片 I/O 处的延迟最小化;
- 内部逻辑资源更多可用;
- clock-to-out(时钟到输出)时序更好;
- setup(建立)时序更好。
图16-8 寄存器被打包到 I/O 缓冲器内
代价:I/O 在芯片边缘,打包进去的寄存器离内部逻辑可能很远,造成 I/O 时序好、内部时序差的不均衡:
图16-9 寄存器被拉到边缘后,与内部逻辑之间的布线延迟失衡
对于 I/O 和内部时序都紧的高速设计,可以再加一级流水寄存器(前提是协议允许)来均衡:
图16-10 在 I/O 附近再加一级流水,分割较长的布线延迟
注意:如果 I/O 寄存器数量很多,额外一层流水会显著增加寄存器用量,甚至造成拥塞。如果 I/O 时序不紧且 I/O 寄存器很多,就不建议打包进 I/O。
16.6 Pack Factor(打包系数)
Pack factor 是一个百分比,人为限制工具可用的逻辑资源总量:100% 表示全部可用,50% 表示只允许用一半。
用途不算多,但有两个进阶玩法:
- 给未来逻辑预留空间:比如后续还要集成一个核,就按其估计大小把 pack factor 降下来——现在实现起来费不费劲,就等于提前预演了将来集成的难度,能更早暴露资源问题。
- 测出”真实利用率”:工具报告的利用率通常偏高——一个逻辑元素只要被用到一部分就算”已用”。想要真实余量,可以逐步降低 pack factor,直到设计无法布通为止,那时的百分比更接近真实利用极限。
16.7 把逻辑塞进 RAM?
SRAM 工艺的 FPGA 用大量小 LUT(本质是小 SRAM 单元)实现逻辑。那资源紧张时,能不能把逻辑放进大块专用 RAM(block RAM)?
概念上可行,性能上不行:分布式小 RAM(LUT)延迟极小,而大 block RAM 延迟大得多,逻辑放进去会非常慢。只有在”超大规模 + 速度极慢”的设计里,作为最后手段才考虑。
16.8 寄存器排序(Register Ordering)
多比特寄存器的相邻位如果被摆进同一个逻辑单元(多数单元里有多个触发器),时序可以更优。布局工具的”寄存器排序”就是把相邻位归组到一起。
但它有个反作用:如果你为了分摊布线延迟特意加过流水触发器,排序可能把新加的触发器排到驱动器旁边——原本想用它切割长布线的意图就落空了:
图16-12 为均衡布线加的寄存器被排到驱动器旁边,分割延迟的作用消失
凡是加了额外触发器来分割布线延迟的场合,应关闭寄存器排序。
16.9 布局种子(Placement Seed)
听起来不舒服但必须接受:布局不是完全确定的。给定同样的设计和约束,不同”种子”(seed,类似随机数生成的初始值)会给出不同的起始摆放,进而长出完全不同的布局结果。
图16-13 布局算法的初始摆放并没有 obviously 的最优起点
图16-14 不同种子给出不同的初始条件,后续布局随之分叉
Xilinx 把种子及若干”随机”参数(约束权重、摆放距离、拥塞估计等)抽象成一个整数,叫cost table。换一个整数 = 换一组初始条件。
改变种子的价值:不改设计就能”再试一次”时序收敛。配合**多趟布局布线(multipass place and route)**可以自动跑多次、每次换种子——很耗时,适合过夜跑,用于时序一直不达标的设计。
但要泼冷水:
- 布局算法本身已经很好,换种子带来的变化其实很小;
- 新手最容易在这里浪费时间——只有在架构改无可改、约束加无可加、违例只剩几百皮秒以内时,才轮到调种子;
- 靠种子过时序意味着不可重复:任何一点 RTL 或约束的改动都会让”好种子”失效,得重新碰运气。
多趟布局布线换种子,只能作为最后手段。
16.10 引导布局布线(Guided Place and Route)
常见噩梦:花几天甚至几周调时序终于收敛,结果来了一个小改动,整个布局被”多米诺式”打乱,一切重来。
引导模式可以避免:它以上一次实现结果为参考,把所有匹配上的组件锁定在原来的位置。好处是:
- 运行时间大幅缩短;
- 被引导的元素时序特性保持不变。
所以,小改动之后的重新实现,应该使用 guide 文件,以最大化一致性、最小化运行时间。
要点小结
- 优化之前先建完整约束集。
- 调整电压/温度设置不动实现就能轻松改善最坏性能。
- 布局 effort 主导性能,布线 effort 影响相对较小。
- 逻辑复制只用于关键路径上无法物理集中的多负载网络。
- 需要门级仿真时不要跨层次优化。
- 高速设计打包 I/O 寄存器时可能需要额外的流水寄存器。
- 设置 pack factor 可以帮助测出真实利用率。
- 加过分割布线延迟的触发器后不要用寄存器排序。
- 多趟布局布线换种子只是最后手段。
- 小改动后的重新实现应使用 guide 文件。
常见坑
时序违例几十皮秒就急着提高布线 effort、反复换种子跑一夜——方向完全错了。布线 effort 和种子对性能的影响都很小,正确顺序是:先检查电压/温度设置是否过于悲观,再逐步加布局 effort,最后才考虑布线 effort 和种子。把力气花在布局之前,先看看是不是逻辑级数太多、该改架构了。
通关标准:
学完本篇你应该能做到:① 在做任何布局布线优化前,先核对约束完整性和电压/温度设置是否过于悲观;② 按”布局 effort 优先、布线 effort 靠后”的正确顺序调优;③ 判断何时该用逻辑复制、I/O 打包、跨层次优化,以及何时必须关掉它们(门级仿真、加过流水寄存器等场景)。
自测:为什么布局 effort 比布线 effort 对性能影响大得多?
FPGA 的布线矩阵是粗粒度、预先铺好的开关网络,灵活性远不如 ASIC 布线。单元摆放位置决定了线网长度,布线只能在既定位置之间”绕”。位置差,布线怎么努力都没用,所以布局是一阶效应。
自测:调整电压/温度约束为什么能"白赚"时序余量?
工具默认用最坏情况(最高结温 85°C、最低电压)做分析,而实际系统往往远达不到这么极端。把分析条件改成系统真实的工作范围后,器件速度评估会变快——设计本身没有任何改动,只是排除了不切实际的悲观假设。
自测:什么情况下不应开启跨层次优化?
需要对实现后网表做门级仿真或后调试时。层次被打平后无法按模块结构遍历设计、难以定位边界信号;此时应保持层次完整。
自测:I/O 打包寄存器有什么副作用?怎么解决?
寄存器被放到芯片边缘后,离内部逻辑变远,内部路径布线延迟失衡。对 I/O 和内部时序都紧的高速设计,可在 I/O 附近再加一级流水寄存器分摊延迟(前提是协议允许);若 I/O 时序不紧且 I/O 寄存器很多,则干脆不用 I/O 打包。
自测:为什么靠"换种子"过时序很危险?
一方面收益本来就小,只适合违例在几百皮秒内的最后挣扎;另一方面它不具备可重复性——任何 RTL 或约束的小改动都会让原来的好种子失效,时序收敛变成反复碰运气的过程。