3、数字电路基础回顾:组合逻辑与时序逻辑、触发器与寄存器、时钟与复位信号
好,咱们正式开始动手之前,得先把地基打牢。你想想看,CPU 再复杂,拆到最底层,也就是一堆门电路和存储单元的组合。今天这一节,我们就来聊聊数字电路里最核心的三个概念:组合逻辑与时序逻辑、触发器与寄存器、时钟与复位信号。
我个人习惯,讲新东西之前,先画一张图把知识脉络理清楚。这样你脑子里有个框架,后面学起来就不容易迷路。
3.1 组合逻辑 vs 时序逻辑
说白了,数字电路就两种干活的方式。
组合逻辑,就是「有输入就有输出,没有记忆」。比如一个加法器,你给 1+1,它立马给你 2。输入一变,输出马上跟着变,不拖泥带水。常见的组合逻辑电路有:与门、或门、非门、加法器、多路选择器、译码器。
时序逻辑,就是「有记忆,能记住过去的状态」。它不光看当前输入,还得看之前发生了什么。比如一个计数器,你给它一个脉冲,它从 0 变成 1,再给一个变成 2。它得记住自己数到哪了。时序逻辑的核心元件就是触发器。
一句话区分:
- 组合逻辑:输出 = f(当前输入)
- 时序逻辑:输出 = f(当前输入, 历史状态)
我在项目中遇到过一个问题:一个同学写 RTL 代码,把所有逻辑都塞在 always 块里,结果综合出来一堆锁存器。为什么?因为他把组合逻辑和时序逻辑混在一起写了。记住,组合逻辑用 assign 或者 always@(*) ,时序逻辑用 always@(posedge clk) ,别搞混。
3.2 触发器与寄存器
触发器是时序逻辑的基本单元。最常见的叫 D 触发器。它长什么样?
// D 触发器的 Verilog 描述
module d_flip_flop (
input wire clk,
input wire rst_n,
input wire d,
output reg q
);
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
q <= 1'b0;
else
q <= d;
end
endmodule
你看,时钟上升沿的时候,它把输入 d 的值「抓」到输出 q 上。其他时间,q 保持不变。这就是「记忆」的由来。
寄存器,其实就是一组 D 触发器并排放在一起。比如 32 位的寄存器,就是 32 个 D 触发器共用同一个时钟和复位。RISC-V 里的通用寄存器组,就是一堆这样的寄存器。
我的小经验:
写 RTL 时,我习惯把寄存器的复位写成同步复位,也就是只在时钟沿检查复位信号。异步复位虽然响应快,但容易出时序问题。曾经有一次,我用了异步复位,结果复位信号毛刺导致寄存器误复位,查了两天才找到原因。从那以后,非必要不用异步复位。
3.3 时钟与复位信号
时钟是整个数字系统的「心跳」。所有时序逻辑都跟着时钟的节拍走。时钟信号在 0 和 1 之间来回跳变,我们通常关心的是它的上升沿(从 0 变 1 的那一瞬间)和下降沿(从 1 变 0 的那一瞬间)。
复位信号则是系统的「总开关」。上电的时候,所有寄存器必须处于一个已知的初始状态,否则系统一跑起来就是乱的。复位有两种常见方式:
| 类型 | 特点 | 适用场景 |
|---|---|---|
| 同步复位 | 只在时钟沿检查复位信号 | 对毛刺不敏感,时序易收敛 |
| 异步复位 | 复位信号随时生效,不依赖时钟 | 需要快速复位,但要注意毛刺 |
⚠️ 避坑指南:
我曾经在一个项目里,把异步复位信号直接连到了所有触发器的复位端。结果仿真没问题,流片回来发现有些寄存器在正常工作时莫名其妙被复位了。后来查出来,是复位路径上有个毛刺。所以我现在做设计,异步复位信号一定要先做同步处理,再送给寄存器。
3.4 建立时间与保持时间
这两个概念,是数字电路里最容易被忽视但又最重要的时序参数。
- 建立时间(setup time):在时钟沿到来之前,数据必须保持稳定的最短时间。
- 保持时间(hold time):在时钟沿到来之后,数据必须保持稳定的最短时间。
为什么要有这两个时间?因为触发器内部是个模拟电路,它需要时间把数据「锁存」住。如果数据在时钟沿附近变化太快,触发器可能读到错误的值,甚至进入亚稳态——也就是既不是 0 也不是 1 的中间状态。
记住这个口诀:
建立时间看路径延迟,保持时间看时钟偏斜。
说白了,建立时间决定了你的电路能跑多快,保持时间决定了你的电路会不会因为时钟歪了而出错。
3.5 小结
嗯,这一节的内容就这些。总结一下:
- 组合逻辑算结果,没有记忆;时序逻辑存状态,有记忆。
- D 触发器是时序逻辑的基本单元,多个触发器组成寄存器。
- 时钟是系统的节拍,复位是系统的初始状态开关。
- 建立时间和保持时间是保证电路正确工作的关键参数。
这些概念,后面写 RTL 代码、做时序分析的时候会反复用到。你现在有个印象就行,后面遇到具体问题我们再细聊。