4、组合逻辑设计:根据真值表设计最小化逻辑电路(卡诺图入门)
各位同学,今天我们来聊聊组合逻辑设计里一个非常实用的技能——从真值表到最小化电路。说白了,就是给你一张真值表,你怎么用最少的门电路把它实现出来。
我刚开始做FPGA设计那会儿,总觉得逻辑表达式能写出来就行,管它是不是最简。结果有一次项目里逻辑资源吃紧,就差那么几十个LUT,硬是优化不下去。后来老工程师跟我说:「你试试卡诺图化简。」嗯,从那以后,我再也不敢小看这张「小格子图」了。
4.1 为什么要最小化?
先问大家一个问题:同样的功能,用10个门和用4个门,区别在哪?
- 面积更小:芯片上省下来的空间,可以做更多功能
- 速度更快:门少了,信号传播路径短了,延迟自然降低
- 功耗更低:每个门都在耗电,少一个门就省一份电
- 可靠性更高:器件越少,出问题的概率越低
所以,最小化不是「锦上添花」,而是「雪中送炭」。尤其在做ASIC设计时,每一平方毫米的面积都意味着成本。
4.2 从真值表到逻辑表达式
我们先回顾一下基础。给定一个真值表,怎么写出逻辑表达式?
举个例子,一个三输入的函数F(A,B,C):
| A | B | C | F |
|---|---|---|---|
| 0 | 0 | 0 | 0 |
| 0 | 0 | 1 | 1 |
| 0 | 1 | 0 | 1 |
| 0 | 1 | 1 | 0 |
| 1 | 0 | 0 | 1 |
| 1 | 0 | 1 | 0 |
| 1 | 1 | 0 | 0 |
| 1 | 1 | 1 | 1 |
标准做法是:把F=1的那些行挑出来,写成「与或式」。
- 第2行:A=0, B=0, C=1 →
~A · ~B · C - 第3行:A=0, B=1, C=0 →
~A · B · ~C - 第5行:A=1, B=0, C=0 →
A · ~B · ~C - 第8行:A=1, B=1, C=1 →
A · B · C
所以:F = ~A·~B·C + ~A·B·~C + A·~B·~C + A·B·C
这个表达式对不对?当然对。但能不能更简单?能。这就是卡诺图要干的事。
4.3 卡诺图入门
卡诺图(Karnaugh Map)本质上是一个「重新排列的真值表」。它把相邻的输入组合放在一起,让你一眼看出哪些项可以合并。
我个人习惯把卡诺图叫做「逻辑拼图」。你想想看,相邻的两个格子如果都是1,说明这两个输入组合只差一个变量,那这个变量就可以消掉。
4.3.1 二变量卡诺图
先来个简单的。两个输入A和B,卡诺图是2×2的格子:
B
0 1
┌────┬────┐
A 0│ m0 │ m1 │
├────┼────┤
1│ m2 │ m3 │
└────┴────┘
每个格子对应一个最小项。相邻的格子(上下或左右)只有一个变量不同。
4.3.2 三变量卡诺图
三变量是2×4的格子。注意顺序:00, 01, 11, 10。为什么不是00,01,10,11?因为格雷码相邻只变一位。
BC
00 01 11 10
┌────┬────┬────┬────┐
A 0│ m0 │ m1 │ m3 │ m2 │
├────┼────┼────┼────┤
1│ m4 │ m5 │ m7 │ m6 │
└────┴────┴────┴────┘
注意看,m3(011)和m2(010)相邻,m7(111)和m6(110)相邻。这种排列方式保证了「几何相邻」就是「逻辑相邻」。
4.3.3 四变量卡诺图
四变量是4×4的格子,同样用格雷码排列:
CD
00 01 11 10
┌────┬────┬────┬────┐
AB 00│ m0 │ m1 │ m3 │ m2 │
├────┼────┼────┼────┤
01│ m4 │ m5 │ m7 │ m6 │
├────┼────┼────┼────┤
11│ m12│ m13│ m15│ m14│
├────┼────┼────┼────┤
10│ m8 │ m9 │ m11│ m10│
└────┴────┴────┴────┘
4.4 用卡诺图化简
回到刚才那个三变量的例子。把F=1的格子填进去:
BC
00 01 11 10
┌────┬────┬────┬────┐
A 0│ 0 │ 1 │ 0 │ 1 │
├────┼────┼────┼────┤
1│ 1 │ 0 │ 1 │ 0 │
└────┴────┴────┴────┘
现在开始画圈。规则很简单:
- 圈2的幂次个1:1个、2个、4个、8个...
- 圈要尽可能大
- 圈可以重叠
- 每个1至少被圈一次
你看,四个角上的1(m1, m2, m4, m7)能不能圈在一起?不能,因为它们不是相邻的。但我们可以这样圈:
- 圈m1(001)和m2(010)?不行,它们不相邻(001和010差两位)
- 圈m1(001)和m5(101)?也不行
实际上,这个例子中每个1都是孤立的,没法合并。所以化简后的表达式就是原来的四个项:
F = ~A·~B·C + ~A·B·~C + A·~B·~C + A·B·C
嗯,这个例子有点「倒霉」,化简不了。我们换个能化简的例子。
4.4.1 能化简的例子
假设真值表如下:
| A | B | C | F |
|---|---|---|---|
| 0 | 0 | 0 | 0 |
| 0 | 0 | 1 | 1 |
| 0 | 1 | 0 | 1 |
| 0 | 1 | 1 | 1 |
| 1 | 0 | 0 | 0 |
| 1 | 0 | 1 | 0 |
| 1 | 1 | 0 | 0 |
| 1 | 1 | 1 | 0 |
填卡诺图:
BC
00 01 11 10
┌────┬────┬────┬────┐
A 0│ 0 │ 1 │ 1 │ 1 │
├────┼────┼────┼────┤
1│ 0 │ 0 │ 0 │ 0 │
└────┴────┴────┴────┘
现在看,m1(001)、m3(011)、m2(010)这三个1是相邻的!它们可以圈成一个2×2的方块(实际上是一行中的三个连续格子,但卡诺图里我们圈2个或4个)。
圈m1和m3:消掉B(因为B从0变1),剩下~A·C
圈m3和m2:消掉C(因为C从1变0),剩下~A·B
所以化简结果:F = ~A·C + ~A·B = ~A·(B + C)
你看,从4个门变成了2个门,省了一半!
4.5 卡诺图化简的完整流程
我总结了一个四步法,你照着做基本不会错:
- 画图:根据变量个数画出卡诺图框架
- 填值:把真值表里的结果填进去
- 画圈:圈出所有相邻的1,圈要尽可能大
- 读式:每个圈对应一个乘积项,所有乘积项加起来
读式的时候,看圈里哪些变量是固定的(0或1不变),哪些是变化的(0和1都有)。固定的变量保留,变化的变量消掉。
4.6 实战:设计一个多数表决电路
讲个我实际遇到过的场景。一个三输入多数表决器,输出为1当且仅当至少两个输入为1。
真值表:
| A | B | C | F |
|---|---|---|---|
| 0 | 0 | 0 | 0 |
| 0 | 0 | 1 | 0 |
| 0 | 1 | 0 | 0 |
| 0 | 1 | 1 | 1 |
| 1 | 0 | 0 | 0 |
| 1 | 0 | 1 | 1 |
| 1 | 1 | 0 | 1 |
| 1 | 1 | 1 | 1 |
填卡诺图:
BC
00 01 11 10
┌────┬────┬────┬────┐
A 0│ 0 │ 0 │ 1 │ 0 │
├────┼────┼────┼────┤
1│ 0 │ 1 │ 1 │ 1 │
└────┴────┴────┴────┘
画圈:
- 圈m3(011)和m7(111):消掉A,剩下
B·C - 圈m5(101)和m7(111):消掉B,剩下
A·C - 圈m6(110)和m7(111):消掉C,剩下
A·B
最终结果:F = A·B + A·C + B·C
这个电路只需要3个与门和1个或门,非常简洁。我在一个温度监控项目里就用过这个电路,三个传感器只要有两个报警就触发冷却系统,效果很好。
4.7 卡诺图的知识体系
下面这张图帮你理清卡诺图在整个组合逻辑设计中的位置:
4.8 避坑指南
最后,分享几个我踩过的坑:
- 圈不能是奇数个:只能圈1、2、4、8...个1。我曾经圈了3个1,结果化简出来是错的。
- 别忘了边界相邻:卡诺图是循环的,最左边和最右边相邻,最上边和最下边相邻。
- 无关项(don't care):如果某些输入组合永远不会出现,可以当作1来圈,能进一步化简。但要注意,不能把无关项当作必须实现的项。
- 多输出电路:多个输出共享输入时,可以整体优化,比单独优化每个输出更省资源。
好了,卡诺图入门就讲到这里。记住,它不是什么高深的理论,就是一个帮你「找规律」的工具。下次拿到真值表,别急着写表达式,先画个卡诺图试试,说不定能省下不少门电路。
可可精品资料库