本节摘要:逻辑综合是硬件界的"编译器"——把 Verilog 行为描述翻译成由 LUT、触发器、DSP 组成的网表。本节讲清综合的三个阶段(细化、逻辑优化、技术映射),揭示哪些代码能综合、哪些不能,以及为什么工具会悄悄改你的结构。顺带覆盖工程输入管理:模块划分、参数化设计与代码组织如何影响综合质量。
软件工程师听到"综合"会本能地联想到编译。方向对了,但差异很关键:C 编译器把高级语言翻成机器指令,机器照着执行;综合工具把 Verilog 翻成电路结构,不是执行文件。同一个 a > b,软件里是一条比较指令,硬件里是几张 LUT 级联出来的比较电路。所以综合的输出叫网表——一张记录"哪些逻辑单元、怎么连"的清单,对应到芯片上就是 LUT、触发器、BRAM、DSP 的实例和它们的连线关系。
综合一般分三个阶段:
理解三个阶段,你就知道综合报告里那些数字是怎么来的。也就能理解:你写的代码只是"行为约束",最终电路是工具按它的优化策略长出来的。
可综合子集是每个 FPGA 工程师的必修边界。仿真允许的语法,综合不一定认:
| 代码模式 | 仿真 | 综合 | 说明 |
|---|---|---|---|
| always、assign、if/case | 支持 | 支持 | 标准可综合 |
| # 延时控制 | 支持 | 忽略 | 电路延时由布线决定,不由代码指定 |
| initial 块 | 支持 | 大多忽略 | 用作初始化寄存器,综合一般不支持 |
| for 循环(固定次数) | 支持 | 支持 | 会展开成并行结构 |
| while/不定长循环 | 支持 | 不支持 | 无法确定电路规模 |
| 函数递归 | 支持 | 不支持 | 电路无法无限展开 |
| 文件 IO、系统任务 | 支持 | 不支持 | testbench 专用 |
最容易被忽略的是 # 延时:仿真里 #5 表示等 5 个时间单位,写起来很顺手,但综合工具直接无视它——物理电路的延迟是布线决定的,不是你能用 # 指定的。凡是依赖 # 才能"跑对"的设计,都是仿真与综合不一致的温床,能不用就不用。
// 综合会出问题的一段:initial 里给寄存器赋初值 module bad_init( input clk, output reg [7:0] q ); initial q = 8'd0; // 仿真里有效,综合后多数器件不认 always @(posedge clk) q <= q + 1'b1; endmodule
上板后 q 的初值是不确定的,直到第一个复位到来。正确的初始化必须走复位信号——这就是 2.2 强调"时序逻辑要有复位分支"的另一面。
综合优化最反直觉的一点:工具可能在你眼皮底下改变逻辑结构。两个典型例子。
资源共享。如果代码里两个互斥分支各做一次加法,工具可能合并成一个加法器,用选择器分时复用——省资源,代价是多一级选择逻辑。
// 工具可能把两处乘法合并:sel 决定走哪个结果 always @(*) begin if (sel) y = a * c; // 乘法器 1 else y = b * c; // 乘法器 2 end // 优化后:一个乘法器,输入 a/b 用选择器切换
逻辑复制(寄存器复制)。反过来,如果某根线扇出太大(驱动了上千个触发器),布线延迟会暴涨。工具会复制几份同样的寄存器,分别就近驱动一批触发器,缩短关键路径。这是后边时序收敛的重要手段。
理解这些优化,是为了让你看懂两件事:一是资源报告会比你数代码得出的更准(工具自作主张了),二是某些时序问题可以通过调整代码结构来"引诱"工具做出更好的优化(比如重定时、打拍)。第 4 章会深入这一层。
综合质量不只看单模块代码,还看你怎么组织工程。设计输入管理(工程结构、版本、复用)是 3.1 最容易忽略的软技能,但它直接决定综合与调试的效率:
// 参数化模块示例:任意位宽的同步 FIFO 接口骨架 module param_fifo #( parameter DATA_W = 8, // 数据位宽 parameter DEPTH = 16 // 深度(2 的幂) )( input clk, input rst_n, input wr_en, input [DATA_W-1:0] wr_data, input rd_en, output [DATA_W-1:0] rd_data, output full, output empty ); // 位宽来自参数:改参数即改电路,无需动逻辑代码 localparam ADDR_W = $clog2(DEPTH); // ...(完整的 FIFO 实现放到第 4 章跨时钟域再展开) endmodule
注意 $clog2——取以 2 为底的对数,由深度自动算出地址位宽。参数化设计 + 系统函数,是让综合工具少出错的工程实践。
⚠️ 常见坑:综合报告里资源爆了,先别急着怀疑芯片不够用。八成是某个 always 块写成了"双沿触发"或"无复位寄存器",或某个大 case 没有 default 被推断成巨型锁存器。对照资源报告倒查模块,通常一两分钟就能定位。
下一步进入 3.2:网表有了,怎么把它摆到芯片上?布局布线是全程最耗时、也最依赖经验的一步。