第1章 · RTL设计基础与综合概念

各位同学好,我是老周。在芯片设计这行摸爬滚打了十几年,今天咱们来聊聊最基础但也最容易被忽略的东西——RTL 到底是什么?可综合和不可综合的代码怎么区分?综合工具到底在背后干了啥?

你可能会觉得,这些概念书上都写过。但说实话,我在项目里见过不少工程师,写了几年 Verilog,却搞不清为什么某个写法综合出来一堆奇怪的逻辑。嗯,这一章咱们就把地基打牢。

RTL设计基础与综合概念 · 知识结构 RTL 设计 什么是 RTL 可综合 vs 不可综合 综合工具基本流程 寄存器传输级 行为级描述 组合/时序逻辑 禁止: initial RTL 编译 逻辑映射 always @(posedge clk) 可综合 #延迟 / 循环不可综合 优化 + 门级网表输出 核心概念 细分/举例

1.1 到底什么是 RTL?

RTL 全称是 Register Transfer Level,寄存器传输级。说白了,就是描述数据在寄存器之间怎么流动、怎么处理。你写的 Verilog 代码,大部分都是在做这件事:时钟沿来了,数据从寄存器 A 跑到寄存器 B,中间经过一些组合逻辑。

我个人习惯把 RTL 理解成“数字电路的乐高说明书”。你告诉工具:我要一个 D 触发器,我要一个加法器,然后连起来。工具就照着你的描述去生成真正的电路。

💡 我的经验: 刚入行时我总把 RTL 当成软件写,动不动就用 for 循环套 for 循环。后来综合出来一个巨大的组合逻辑,时序直接崩了。记住,RTL 是硬件的抽象,不是 C 语言。

1.2 可综合 vs 不可综合代码

这是新手最容易踩的坑。可综合,就是综合工具能把你写的代码变成真实的门级电路。不可综合,就是工具看了直摇头——这东西我没法用硬件实现。

举个例子:

// 可综合 —— 标准的 D 触发器
always @(posedge clk or negedge rst_n) begin
    if (!rst_n)
        q <= 1'b0;
    else
        q <= d;
end

// 不可综合 —— 用了 #10 延迟,综合工具直接报错
initial begin
    #10 clk = 0;
    forever #5 clk = ~clk;
end

为什么会这样?因为 #10 是仿真用的时间延迟,真实芯片里没有“等 10 纳秒”这个操作。你想想看,综合工具怎么把一个 #10 映射到晶体管上?没法弄。

🔴 常见不可综合写法(避坑):
  • initial 块(除了 testbench)
  • #delay 时间延迟
  • fork / join
  • wait 语句
  • 动态数组、类、文件 I/O
  • 循环次数非常量的 for / while(综合工具可能展开,但危险)

我记得有一次,团队里一个同事用 while 循环写了一个仲裁器,仿真怎么跑都正确。结果综合时工具直接卡死,因为循环次数取决于输入,工具没法确定边界。从那以后,我们规定:所有循环必须用固定次数,或者用 generate 展开。

1.3 综合工具的基本流程

综合工具(比如 Synopsys Design Compiler、Cadence Genus)到底对你的 RTL 做了什么?我把它拆成三步,简单明了:

步骤做什么输出
① 编译 (Compile)读入 RTL 代码,解析语法,建立内部数据库GTECH 网表(通用逻辑门)
② 优化 (Optimize)逻辑化简、资源共享、时序优化、面积优化优化后的逻辑网表
③ 映射 (Mapping)将通用门映射到目标工艺库(比如台积电 7nm)门级网表 (Netlist)

说白了,第一步是“读懂你的代码”,第二步是“想办法让它更快更小”,第三步是“换成真正的标准单元”。

⚠️ 注意: 综合不是万能的。如果你的 RTL 写得很烂,比如组合逻辑路径太长,工具再怎么优化也救不了。我曾经接手过一个模块,综合报告显示最差路径有 50 级逻辑,最后只能大改代码。所以,可综合的代码 ≠ 好代码,还要考虑时序、面积、功耗。

嗯,这一章的内容就是这些。RTL 基础、可综合与不可综合的界限、综合工具的三步走,都是后面所有章节的基石。你把这些搞明白,后面学 Lint 检查、综合约束才会顺手。