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的关系串起来了。你盯着看一分钟,就能明白数字逻辑的全貌。

数字逻辑基础知识体系 数字电路 组合逻辑(无记忆) 时序逻辑(有记忆) 加法器 多路选择器 锁存器 触发器 有限状态机(FSM) Moore型 Mealy型 核心:组合逻辑负责计算,时序逻辑负责存储,FSM负责控制流程 RISC-V处理器 = 组合逻辑(ALU)+ 时序逻辑(寄存器)+ FSM(控制器)

3.5 实战避坑:我的血泪史

最后,分享几个我踩过的坑,你们记下来,能省半年调试时间。

  1. 锁存器陷阱: 我曾经在写一个AXI总线控制器时,因为case语句少了一个default,综合出16个锁存器。结果芯片功耗超标,重新流片花了50万。从那以后,我写组合逻辑必加default。
  2. 状态机跑飞: 有一次,我的FSM在某个非法状态下卡死了。原因是状态编码用了二进制,而外部干扰让状态跳到了未定义值。解决方案:用格雷码或独热码,并且加一个“看门狗”复位。
  3. 时钟域问题: 触发器对时钟边沿敏感。如果你把两个不同时钟域的触发器直接连起来,大概率出亚稳态。我的习惯是:跨时钟域必加两级同步器

小技巧: 写RTL时,先画状态转移图,再写代码。我一般用纸笔画,画清楚了再动手。这样能避免80%的逻辑错误。

好了,这一章的内容就到这里。数字逻辑是RISC-V的基石,你把这些搞懂了,后面看流水线、看Cache,就像看搭积木一样简单。


专注资料整理