← 返回地图
A5 · ⭐⭐⭐ · 1.5 小时

A5 组合逻辑

上回你用继电器搭出了一整套逻辑门——与、或、非、异或。发小对你的 3-8 解码器佩服得不行。现在你打算再挑战一件更刺激的事:让电路自己会算加法。

计算机的所有算术,归根结底都是加法。

谁还不会加法呢?但你一想到要让一堆灯泡和开关自己算出 13 + 9,就觉得这事没那么简单。先从最简单的开始——两个 1 位数的加法。

两张似曾相识的表

二进制只有 0 和 1,所以加法表比十进制简单太多了。两个 1 位相加,一共只有四种情况:

AB和进位
0000
0110
1010
1101

你盯着这两列看了半天,突然一拍大腿——这两列,怎么这么眼熟?

发现 一个能算 1 位加法的电路,就是 1 个 XOR + 1 个 AND。XOR 算"和位",AND 算"进位"。

半加器:会算一半的加法器

你把 XOR 和 AND 接上 A、B 两个输入。下面这个是真的能动的——试试点 A、B 两个开关,看电流怎么传:

拨 A/B 开关,看导线逐级变色、灯延迟亮起(门的传播延迟约 100ms)

但这玩意儿有个毛病:它只认 A 和 B 两个输入,不接受别人传来的进位。可你做多位加法时,每一位都得接收右边那位传来的进位啊。

所以这个只能算"一半"的加法器,你给它取名叫 半加器(Half Adder)——意思是只加了一半,剩下的一半被"鸽"了。

全加器:把进位也加进来

既然半加器只加了一半,那"半半得全"。你拿来两个半加器,把第一个的和位接到第二个的输入,再让第二个也接收外部进位 Cin,最后用 OR 门把两次产生的进位合起来——这就是 全加器(Full Adder):

全加器 = 两个半加器 + 一个 OR 门。拨 A/B/Cin 开关,看 Sum 和 Cout 如何随真值表变化

Sum = (A XOR B) XOR Cin | Cout = (A AND B) OR ((A XOR B) AND Cin)

你来试试:让加法器跑起来

下面这个加法器已经搭好了 4 个全加器,串成一条链。输入两个二进制数(只填 0 和 1),看进位怎么一位一位往左传:

数 A
+
数 B

把全加器串起来:纹波进位

要做多位加法,就把全加器一个接一个串起来——低位的进位输出,直接接到高位的进位输入。下面是 4 位纹波进位加法器,拨开关试试进位怎么逐级"涟漪":

4 位纹波进位加法器(分组布局):每个 bit 的 A/B 开关和全加器包成一组,4 组水平并置,进位链在组间逐级传播。拨 A0/B0 看进位涟漪

进位从 bit0 一路传到 bit3,像水波纹一样向高位"涟漪"——所以这种加法器叫 纹波进位加法器(Ripple Carry Adder)。

以 13 + 9(1101 + 1001)为例:

位0: 1+1        = 0,进位 1
位1: 0+0+进位1   = 1,进位 0
位2: 1+0+进位0   = 1,进位 0
位3: 1+1+进位0   = 0,进位 1 ← 最终溢出

结果 10110 = 22。

进位的麻烦:它得一位一位等

到这里你发现一个闹心的问题。看上面那个链:位 1 必须等位 0 算完、把进位传过来,才能开始算;位 2 得等位 1……位 3 得等位 2。

延迟 假设一个全加器从收到输入到算出进位,需要 1 纳秒。那么 4 位加法最坏要等 4 纳秒,8 位要 8 纳秒,64 位要 64 纳秒——位数越多,等得越久。这就是进位传播延迟(Carry Propagation Delay)。

这个延迟不是小问题。CPU 一个时钟周期可能就几百皮秒,如果一次加法要等进位从头传到尾,那 64 位 CPU 的加法指令会慢得没法用。纹波进位的延迟随位数线性增长,这是个会拖垮性能的坑。

你和发小对着这条长长的进位链犯愁:能不能别一位一位等?

超前进位:别等了,提前算

你盯着真值表看了半天,突然想到一个事:每一位到底进不进位,其实不完全取决于前一位。

拆开看一位全加器的进位输出 Cout,它由两部分相或而成:

Cout = (A AND B)  OR  ((A XOR B) AND Cin)

这两项各有名字,是超前进位的核心:

关键洞察 G 和 P 只取决于这一位自己的 A、B——它们不需要等任何进位输入就能立刻算出来。于是乎:能不能用所有位的 G、P,一次性算出每位的进位,而不是一位等一位?

能。把展开式一层层代入,你会发现每一位的进位都可以写成"几个 G、P 的与或式"。比如:

C1 = G0 + P0·C0          (只看第 0 位)
C2 = G1 + P1·G0 + P1·P0·C0   (前两位的 G、P)
C3 = G2 + P2·G1 + P2·P1·G0 + P2·P1·P0·C0   (前三位)

每一级进位都是一组 G、P 的组合,只要门足够多,所有进位可以同一时刻算出来。这就是 超前进位器(Carry-Lookahead Adder,CLA)。

代价是:位越高,公式越长、需要的门越多(电路面积和门延迟都会涨)。所以现实里的 CPU 用的是"分组超前进位"——每 4 位一组用 CLA,组之间再级联,既不是纯纹波也不是纯 CLA,是两者折中。

直观感受:纹波 vs 超前,差多少?

拖动下面的滑块选位数,看看两种加法器的进位延迟差几个数量级(假设每级门延迟 0.1ns):

纹波进位
0.8 ns
超前进位
0.4 ns

纹波进位延迟 = 位数 × 0.1ns(线性增长)|超前进位 ≈ log₂(位数) × 0.1ns(对数增长)

套娃,然后抽象

有了全加器,你就可以继续套娃——把全加器一个接一个串起来:

全加器 → 全全加器 → 全全全加器 → 全全全全加器 → 全全全全全全全全加器

"全全全全全全全全加法器"这个名字太长了,所以你干脆叫它:8 位加法器。两个 8 位加法器级联,又能算 16 位。

为了不让父母发现你没写作业而是在摸鱼,也为了省事,你决定把这一堆电路藏进一个盒子里,只露出输入和输出——这种把复杂集合体藏进简单包装里的做法,叫做 抽象(Abstraction),也叫封装。

把刚才那 4 位纹波进位加法器封进一个盒子,只留 A、B 两组输入开关和 Sum 输出灯,就成了一个 ADD4 黑盒。拨 A/B 开关,灯照样逐级亮起——你不必再关心里面那一堆全加器和进位链:

4 位加法器黑盒(ADD4):内部 4 个全加器的进位链被封装,只露输入和输出

同理,8 个全加器串起来封进盒子,就是 ADD8。两个 8 位加法器级联,又能算 16 位。盒子越套越大,但你看到的接口始终只有"输入 → 输出":

8 位加法器黑盒(ADD8):8 级进位涟漪全部藏进盒子,拨开关直接看 8 位结果

抽象,果然是人类智慧进步的阶梯。

从一个"通/断"的开关,到能做加法的电路,你已经走完了从信号到算术的整条路。但加法器只会算——它不会记。算完就忘,下一次还得从头来。怎么让电路记住上一次的结果?下一章,我们让输出绕回来,接回输入。

小结 & 思考题(点开深入)

本章你经历了什么

思考题

练习 1 · 算算延迟

一个 32 位纹波进位加法器,每个全加器进位延迟 0.1ns。最坏情况下,从最低位输入到最高位输出要等多久?如果改成 4 位一组的超前进位,延迟大约能降到多少?

纹波:32 × 0.1ns = 3.2ns。
超前(4 位一组):32 位 = 8 组,组内 CLA 约 2 级门延迟(0.2ns),组间再级联约 8 级 → 约 8 × 0.2 + 0.2 ≈ 1.8ns(不同实现略有差异,但都比纹波快不少)。位数越大,超前优势越明显。
练习 2 · G 和 P 的直觉

某一位 A=1, B=1。它的 G 和 P 分别是多少?这一位会不会产生进位?会接收进位吗?

G = A AND B = 1(这一位自己就产生进位)。P = A XOR B = 0(A、B 相同,不传播进位)。所以无论 Cin 是什么,这一位都会向高位送出进位——这正是"生成"的含义。
练习 3 · 为什么不全用超前进位?

既然超前进位这么快,为什么 64 位 CPU 不直接做一个 64 位的纯超前进位加法器,而要用"分组超前进位"?

因为电路面积爆炸。C63 的公式会包含 64 项的与或式,需要天文数字的逻辑门和扇入,物理上造不出来、延迟也会因为门太复杂而反弹。分组(如 4 位一组 CLA + 组间级联)是在"快"和"造得出"之间的工程折中。计算机里到处都是这种权衡。