第二章 数字电路基础回顾:布尔代数、组合逻辑、时序逻辑、状态机基础
各位同学,欢迎来到第二章。说实话,很多刚入行的朋友觉得数字电路基础是“水课”,觉得不就是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 状态机设计步骤
- 画状态转移图:把所有的状态和转移条件画出来。这一步别省,我见过太多人直接写代码,结果漏了状态。
- 状态编码:给每个状态分配一个二进制码。常用的是二进制编码、格雷码、独热码。
- 写代码:三段式状态机——状态寄存器、次态逻辑、输出逻辑。
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的世界。到时候,这些基础概念都会变成一行行代码。别急,先把今天的内容消化掉。
有什么问题,欢迎在评论区留言。咱们下章见。