逻辑综合进阶:面积、时序、功耗的三角博弈
综合做完基础约束,能跑出网表了,对吧?但那个网表能不能用,差得远呢。我见过太多新手,跑完综合一看没报错,就兴冲冲往后端走,结果布局布线后时序一团糟,面积大得离谱,功耗也压不住。
说白了,逻辑综合不是「跑通就行」,而是要在面积、时序、功耗这三个维度上找到平衡点。这三者就像跷跷板,你压了一头,另一头就翘起来。今天我们就聊聊怎么在 OpenROAD 里做这个平衡。
核心观点:综合阶段对芯片最终 PPA(Performance, Power, Area)的影响超过 60%。布局布线能优化的空间其实有限,根子在综合。
面积优化:别让芯片「虚胖」
面积直接决定成本。晶圆厂按片卖,你芯片面积大,每片切出来的 die 就少,成本自然高。我做过一个项目,综合时没管面积,结果后端发现芯片比预期大了 15%,最后只能砍功能——教训深刻。
OpenROAD 的 yosys 里,面积优化主要靠这几招:
- 资源共享(Resource Sharing):多个运算共用同一个硬件模块。比如两个加法器,如果不同时使用,综合器可以合并成一个。
- 操作数重排序(Operator Reordering):改变运算顺序,减少中间结果的位宽。
- 常量传播(Constant Propagation):把编译期能算出来的值直接算好,省掉硬件。
在 yosys 脚本里,我习惯这样写:
# 开启资源共享
synth -top top_module -flatten -shared
# 或者更细粒度控制
proc; opt; techmap; opt
share -fast_mode
我的经验:资源共享不是越多越好。共享太狠,控制逻辑变复杂,反而可能增大面积。我一般先跑一版不加共享的,看看 baseline,再加共享看效果。对比着来。
时序优化:让芯片跑得快
时序优化,说白了就是让信号在时钟周期内能稳定到达目的地。你想想看,如果路径太长,数据还没到,下一个时钟沿就来了——那就出错了。
OpenROAD 里时序优化主要靠:
- 逻辑层级压缩(Logic Level Reduction):把多级逻辑合并成更少的级数。比如把 A+B+C+D 的四级加法,改成 (A+B)+(C+D) 的两级。
- 关键路径重定时(Retiming):在寄存器之间移动逻辑,平衡路径延迟。
- 单元尺寸调整(Cell Sizing):把关键路径上的小驱动单元换成大驱动单元。
在 OpenROAD 里,时序约束是核心。我一般这样写:
# 设置时钟周期为 10ns
create_clock -name clk -period 10 [get_ports clk]
# 设置输入输出延迟
set_input_delay -clock clk 2 [all_inputs]
set_output_delay -clock clk 2 [all_outputs]
# 设置 false path(异步路径)
set_false_path -from [get_clocks clk_a] -to [get_clocks clk_b]
注意:set_false_path 别乱用。我曾经有个同事,把一条其实有约束的路径设成了 false path,结果芯片回来跑不到目标频率,查了三天才发现。false path 只用于真正的异步跨时钟域路径。
功耗优化:让芯片「省着吃」
功耗现在越来越重要。手机芯片、IoT 芯片,功耗高了根本没法用。综合阶段的功耗优化,主要分动态功耗和静态功耗。
| 功耗类型 | 来源 | 综合优化手段 |
|---|---|---|
| 动态功耗 | 信号翻转时充放电 | 门控时钟(Clock Gating)、操作数隔离(Operand Isolation) |
| 静态功耗 | 漏电流 | 多阈值单元(Multi-Vt)、电源门控(Power Gating) |
OpenROAD 里,yosys 本身对功耗优化支持有限,但我们可以通过 RTL 编码来辅助。比如:
// 手动插入门控时钟
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
data_q <= '0;
else if (en)
data_q <= data_d;
end
这样写,综合器就能识别出 en 信号,自动插入门控时钟单元。比让综合器自己去猜要靠谱得多。
我的习惯:在 RTL 里就把门控时钟写清楚。虽然综合器也能自动推断,但手动写出来的控制更精确,面积也更小。我做过对比,手动门控比自动推断能省 10-15% 的功耗。
综合报告解读:别只看数字
综合跑完,会生成一堆报告。很多人只看「有没有违规」,其实报告里信息量很大。
OpenROAD 里,我主要看这几份报告:
- 时序报告(timing.rpt):看最差路径的 slack,以及路径的组成。
- 面积报告(area.rpt):看各模块的面积占比,找「大块头」。
- 功耗报告(power.rpt):看动态和静态功耗的占比。
举个例子,时序报告里如果看到:
Startpoint: reg_a
Endpoint: reg_b
Path Group: clk
Path Type: max
Level Delay Description
----------------------------------------
0 0.00 clock network delay
1 0.35 reg_a/Q (DFF)
2 1.20 U1/A (AND2)
3 1.45 U2/A (OR2)
4 0.80 reg_b/D (DFF)
----------------------------------------
Total: 3.80 ns (slack: -0.30 ns)
slack 是负的,说明这条路径超了。看路径组成,AND2 和 OR2 各占了不少延迟。这时候我会考虑:能不能把 AND2+OR2 合并成一个 AOI 复合门?或者把逻辑挪到寄存器前面去?
解读技巧:不要只看最差路径。我习惯看 top 10 最差路径,如果它们都集中在同一个模块,那问题大概率出在那个模块的 RTL 设计上。如果分散在各处,可能是约束太紧或者时钟周期设得太短。
知识体系总览
下面这张图,是我自己总结的综合进阶知识框架。每次做项目前,我都会对照着过一遍:
实战中的权衡策略
说了这么多,到底怎么选?我个人的做法是:
- 先定时序目标:芯片能不能跑,时序是第一位的。先满足时序,再看面积和功耗。
- 再压面积:时序满足后,用资源共享和常量传播压面积。每次改完都重新检查时序。
- 最后调功耗:面积和时序都差不多了,再上功耗优化。门控时钟、多阈值单元,一步步加。
避坑指南:不要一次性把所有优化选项全打开。我曾经这么干过,结果综合跑了 8 小时,出来的网表时序反而更差了。优化选项之间有冲突,要一个一个试,对比着来。