这一篇在干嘛?

综合完成之后,工具还要把逻辑”摆”进芯片(布局 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°C45°C

书中例子:Virtex-II 上一个 16 位计数器,默认条件下最高跑 276 MHz;把电压/温度改成符合实际系统的取值后,直接跑到近 290 MHz——不改任何设计白赚约 14 MHz

两个注意点:

  1. 这个方法不改 RTL、不重跑实现,是”零成本”提升,应该最先尝试。
  2. 前提是厂商已完成按温度/电压的特性表征(characterization)。刚发布的新工艺器件往往没做完,工具里这个选项会是灰的——所以”成熟一点的器件”反而有这个优势。

16.2 布局与布线的关系

现代工具的自动布局布线已经很强,很多工程师因此从不关心后端选项。但这里有一个关键事实:

布局 effort 对性能的影响是第一位的(一阶效应),布线 effort 的影响相对次要。

原因在于 FPGA 的布线矩阵(routing matrix,芯片内预先铺好的导线开关网络)是”粗粒度”的,灵活性远不如 ASIC。好的布线只能建立在好的布局之上——位置摆得不好,再怎么努力绕线也绕不出好时序。

如果你把”布局 effort × 布线 effort”的所有组合跑一遍并画图,会看到类似这样的曲线:

性能随布局/布线 effort 的变化 图16-3 布局/布线努力程度与性能的关系:布局曲线陡峭(主导),布线曲线平缓

推荐的调优流程:

  1. 布局、布线 effort 都设为最低;
  2. 跑一遍,确认最差时序确实是布局问题,而不是逻辑级数太多;
  3. 逐步提高布局 effort,直到满足时序或到最大档;
  4. 布局到最大档仍不满足时,再逐步提高布线 effort;
  5. 还不行,回头改架构;
  6. 如果需要很高的布线 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(建立)时序更好。

寄存器打包进 I/O 缓冲器 图16-8 寄存器被打包到 I/O 缓冲器内

代价:I/O 在芯片边缘,打包进去的寄存器离内部逻辑可能很远,造成 I/O 时序好、内部时序差的不均衡:

I/O 打包造成的布线延迟不均衡 图16-9 寄存器被拉到边缘后,与内部逻辑之间的布线延迟失衡

对于 I/O 和内部时序都紧的高速设计,可以再加一级流水寄存器(前提是协议允许)来均衡:

I/O 打包时增加一级流水寄存器 图16-10 在 I/O 附近再加一级流水,分割较长的布线延迟

注意:如果 I/O 寄存器数量很多,额外一层流水会显著增加寄存器用量,甚至造成拥塞。如果 I/O 时序不紧且 I/O 寄存器很多,就不建议打包进 I/O。

16.6 Pack Factor(打包系数)

Pack factor 是一个百分比,人为限制工具可用的逻辑资源总量:100% 表示全部可用,50% 表示只允许用一半。

用途不算多,但有两个进阶玩法:

  1. 给未来逻辑预留空间:比如后续还要集成一个核,就按其估计大小把 pack factor 降下来——现在实现起来费不费劲,就等于提前预演了将来集成的难度,能更早暴露资源问题。
  2. 测出”真实利用率”:工具报告的利用率通常偏高——一个逻辑元素只要被用到一部分就算”已用”。想要真实余量,可以逐步降低 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 打包、跨层次优化,以及何时必须关掉它们(门级仿真、加过流水寄存器等场景)。