第二章 数字电路基础回顾:布尔代数、组合逻辑、时序逻辑、状态机基础

各位同学,欢迎来到第二章。说实话,很多刚入行的朋友觉得数字电路基础是“水课”,觉得不就是0和1嘛,有什么好学的?

但我得说句实在话——我见过太多项目翻车,根源就是基础不牢。有一次,一个同事设计的计数器在特定条件下跳错了状态,查了三天,最后发现是状态机编码时漏了一个“无关项”。嗯,这种坑,踩过一次就忘不了。

所以,咱们今天把底子打扎实。别急,慢慢来。

2.1 布尔代数:数字世界的“语法”

布尔代数,说白了就是0和1之间的运算规则。你想想看,计算机里所有东西——加法、比较、跳转——最终都能拆成与、或、非这三种基本操作。

2.1.1 基本运算

  • 与(AND):两个都是1,结果才是1。用乘号表示:Y = A · B
  • 或(OR):只要有一个是1,结果就是1。用加号表示:Y = A + B
  • 非(NOT):取反。用上划线表示:Y = ¬A

我个人习惯把“与”想象成串联开关,“或”想象成并联开关。这样画电路图时,脑子里就有画面了。

2.1.2 常用定律

定律表达式我的理解
交换律A·B = B·A顺序不重要
结合律(A·B)·C = A·(B·C)括号随便加
分配律A·(B+C) = A·B + A·C和普通代数一样
德摩根定律¬(A·B) = ¬A + ¬B这个最常用,也最容易错
小技巧:德摩根定律记不住?我教你个土办法——把长条“非”符号拆开时,中间的运算符要变号。与变或,或变与。每次化简逻辑时,先检查能不能用德摩根,往往能省一半门电路。

2.2 组合逻辑:没有记忆的“直性子”

组合逻辑的特点是什么?输入一变,输出立刻跟着变。没有时钟,没有记忆,纯粹是“看门狗”式的响应。

2.2.1 常用组合逻辑模块

  • 编码器:把多个输入压缩成二进制码。比如8线-3线编码器。
  • 译码器:反过来,把二进制码展开成多个输出。3线-8线译码器。
  • 多路选择器(MUX):从多个输入中选一个输出。选通信号决定选谁。
  • 加法器:半加器、全加器,构成ALU的基础。

我在项目中遇到过一个问题:一个8位加法器,综合后面积超标。后来发现是用了“行波进位”结构,改成“超前进位”后,速度提上去了,面积也降下来了。你看,同样的功能,不同的实现,差别就是这么大。

2.2.2 组合逻辑的“坑”

我曾经踩过的坑:组合逻辑最怕“竞争冒险”。简单说,就是信号经过不同路径到达的时间不一样,导致输出出现毛刺。解决办法?加个寄存器打一拍,或者用格雷码编码。别问我怎么知道的——那次示波器上看到毛刺时,我后背都凉了。

2.3 时序逻辑:会“记住”的电路

时序逻辑和组合逻辑最大的区别是什么?它有状态。说白了,它知道“之前发生了什么”。

2.3.1 触发器(Flip-Flop)

最常用的就是D触发器。它的行为很简单:每个时钟上升沿,把输入D的值“拍”到输出Q上。

// D触发器的行为描述(Verilog)
always @(posedge clk) begin
    q <= d;
end

你想想看,这个“<=”非阻塞赋值,就是时序逻辑的精髓。它表示:所有触发器在同一个时钟沿同时更新。我刚开始学的时候,总写成“=”,结果仿真波形一塌糊涂。嗯,这个教训很深刻。

2.3.2 寄存器和移位寄存器

  • 寄存器:多个D触发器并排,存一个多位数据。
  • 移位寄存器:每个时钟沿,数据向左或向右移动一位。常用于串并转换。

我记得有一次做SPI接口,需要把串行数据转成并行。用移位寄存器,8个时钟周期搞定。如果当时用组合逻辑硬拼,估计代码要写一页纸。

2.4 状态机:数字系统的“大脑”

状态机,说白了就是“根据当前状态和输入,决定下一步做什么”。几乎所有复杂的数字系统——CPU、通信协议、控制器——背后都是状态机。

2.4.1 状态机的分类

类型输出特点我的建议
Moore型输出只取决于当前状态稳定,不容易出毛刺
Mealy型输出取决于当前状态和输入响应快,但容易有毛刺

我个人习惯:能用Moore就用Moore。为什么?因为Mealy的输出直接受输入影响,一旦输入有毛刺,输出也跟着抖。我在一个项目里吃过这个亏——Mealy状态机的输出直接驱动了外部设备,结果设备乱跳。后来改成Moore,加了一级寄存器,问题解决。

2.4.2 状态机设计步骤

  1. 画状态转移图:把所有的状态和转移条件画出来。这一步别省,我见过太多人直接写代码,结果漏了状态。
  2. 状态编码:给每个状态分配一个二进制码。常用的是二进制编码、格雷码、独热码。
  3. 写代码:三段式状态机——状态寄存器、次态逻辑、输出逻辑。
避坑指南:我曾经犯过一个错误——状态机里有一个“非法状态”没处理。结果芯片上电后,状态机跳到了一个未定义的状态,死在那里了。从那以后,我每个状态机都加一个default分支,把非法状态引导回初始状态。记住:硬件没有“未定义行为”,它只会“乱来”。

2.4.3 三段式状态机示例

// 三段式状态机模板
// 第一段:状态寄存器
always @(posedge clk or negedge rst_n) begin
    if (!rst_n)
        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);

你看,三段式把“状态跳转”和“输出”分开了。这样改输出时,不会影响状态机本身。我建议所有初学者都按这个模板来,等熟练了再考虑优化。

2.5 本章小结

好了,这一章的内容就这些。布尔代数是工具,组合逻辑是“手脚”,时序逻辑是“记忆”,状态机是“大脑”。四者结合起来,就能搭出任何数字系统。

下一章,我们会进入Verilog的世界。到时候,这些基础概念都会变成一行行代码。别急,先把今天的内容消化掉。

有什么问题,欢迎在评论区留言。咱们下章见。