3、数字逻辑基础速通:组合逻辑与时序逻辑、触发器与锁存器、有限状态机(FSM)设计要点
各位同学,欢迎来到第三讲。说实话,很多刚接触RISC-V的朋友,一上来就盯着指令集和流水线看,结果卡在底层逻辑上。我个人习惯是,先把数字逻辑的底子打牢,后面看处理器架构就像看地图一样清晰。
这一章,咱们不讲虚的。直接上硬菜:组合逻辑、时序逻辑、触发器、锁存器,还有有限状态机。这些都是数字芯片的“细胞”,搞不懂它们,RISC-V就是空中楼阁。
核心观点: 数字电路 = 组合逻辑(算得快) + 时序逻辑(记得住)。处理器就是靠这两样东西,一步步完成指令的。
3.1 组合逻辑 vs 时序逻辑:一个“没记忆”,一个“有记忆”
先问个问题:为什么CPU能记住你上一秒算的结果?答案就在时序逻辑里。
- 组合逻辑: 输出只取决于当前输入。比如一个加法器,输入1+1,输出就是2。它不关心过去,也不关心未来。说白了,就是“没心没肺”。
- 时序逻辑: 输出不仅取决于当前输入,还取决于电路之前的状态。比如一个计数器,它记得自己数到几了。这就是“有记忆”。
我在项目中遇到过一个问题:一个同学写Verilog,把所有逻辑都塞进always块里,结果综合出来一堆锁存器。为什么?因为他把组合逻辑和时序逻辑混在一起了。记住:组合逻辑用 assign 或 always@(*),时序逻辑用 always@(posedge clk)。
| 特性 | 组合逻辑 | 时序逻辑 |
|---|---|---|
| 记忆能力 | 无 | 有(靠触发器) |
| 输出延迟 | 仅门延迟 | 受时钟控制 |
| 典型电路 | 加法器、多路选择器 | 寄存器、计数器 |
| RTL写法 | assign / always@(*) | always@(posedge clk) |
3.2 触发器与锁存器:别搞混了,它们不一样
很多初学者把触发器和锁存器当成一回事。其实差别大了去了。你想想看,锁存器是电平敏感,触发器是边沿敏感。这直接决定了你的芯片能不能跑到高频。
3.2.1 锁存器(Latch)
锁存器在使能信号为高电平时,输出跟随输入变化;使能信号为低电平时,输出保持。说白了,它像个“透明”的窗户,使能打开时,数据直接透过去。
避坑指南: 我曾经在状态机里不小心写了个不完整的if-else,综合工具直接给我插了个锁存器。结果芯片时序一塌糊涂。所以,写组合逻辑时,if-else 必须写全,case 必须加 default。
3.2.2 触发器(Flip-Flop)
触发器只在时钟的上升沿(或下降沿)采样输入。它像个“快门”,只在那一瞬间抓拍数据。现代数字芯片99%都用触发器,因为它抗干扰能力强,时序好分析。
我的经验: 设计RISC-V处理器时,所有寄存器(比如PC、通用寄存器)都用D触发器。锁存器?尽量别用。除非你做低功耗设计,或者面积实在紧张,否则锁存器就是定时炸弹。
来个简单的Verilog对比:
// 锁存器(不推荐)
always @(*) begin
if (en)
q = d;
end
// 触发器(推荐)
always @(posedge clk) begin
if (rst)
q <= 1'b0;
else
q <= d;
end
3.3 有限状态机(FSM)设计要点
FSM是数字设计的灵魂。RISC-V的指令译码、流水线控制、Cache控制器,全是FSM。说白了,FSM就是让电路“按剧本走”。
3.3.1 两种FSM:Moore vs Mealy
- Moore型: 输出只取决于当前状态。稳定,但可能需要更多状态。
- Mealy型: 输出取决于当前状态和输入。响应快,但容易出毛刺。
我个人习惯:控制路径用Moore,数据路径用Mealy。比如RISC-V的译码器,我倾向于用Moore,因为输出稳定,不容易误触发。
3.3.2 三段式状态机写法
这是业界标准写法,也是我面试必考的。三段式把状态跳转、状态寄存、输出逻辑分开,代码清晰,综合结果也好。
// 三段式FSM示例
// 第一段:状态寄存器
always @(posedge clk or posedge rst) begin
if (rst)
state <= IDLE;
else
state <= next_state;
end
// 第二段:次态逻辑(组合逻辑)
always @(*) begin
case (state)
IDLE: if (start) next_state = RUN;
else next_state = IDLE;
RUN: if (done) next_state = DONE;
else next_state = RUN;
DONE: next_state = IDLE;
default: next_state = IDLE;
endcase
end
// 第三段:输出逻辑(组合逻辑)
assign busy = (state == RUN);
assign finish = (state == DONE);
警告: 写状态机时,一定要加 default 分支!我曾经见过一个同事,因为漏了default,状态机跑飞了,整个芯片死机。复位都救不回来,只能重新上电。
3.4 本章知识体系:一张图看懂
下面这张SVG图,是我自己梳理的。它把组合逻辑、时序逻辑、触发器、锁存器、FSM的关系串起来了。你盯着看一分钟,就能明白数字逻辑的全貌。
3.5 实战避坑:我的血泪史
最后,分享几个我踩过的坑,你们记下来,能省半年调试时间。
- 锁存器陷阱: 我曾经在写一个AXI总线控制器时,因为case语句少了一个default,综合出16个锁存器。结果芯片功耗超标,重新流片花了50万。从那以后,我写组合逻辑必加default。
- 状态机跑飞: 有一次,我的FSM在某个非法状态下卡死了。原因是状态编码用了二进制,而外部干扰让状态跳到了未定义值。解决方案:用格雷码或独热码,并且加一个“看门狗”复位。
- 时钟域问题: 触发器对时钟边沿敏感。如果你把两个不同时钟域的触发器直接连起来,大概率出亚稳态。我的习惯是:跨时钟域必加两级同步器。
小技巧: 写RTL时,先画状态转移图,再写代码。我一般用纸笔画,画清楚了再动手。这样能避免80%的逻辑错误。
好了,这一章的内容就到这里。数字逻辑是RISC-V的基石,你把这些搞懂了,后面看流水线、看Cache,就像看搭积木一样简单。