计算机组成笔记
关于计算机组成原理的笔记。持续更新
写在前面:本笔记侧重于概念和底层的理解。建议先夯实了基础再做题,事半功倍
当前进度:程序执行概览、整数表示与运算电路。 先认识一条指令的执行过程,再解释数怎样编码、加法怎样实现;寄存器与时钟的细节留待后续。
阅读路线:程序执行 → 数的表示 → 运算电路。
| 教材模块 | 本文位置 |
|---|---|
| 计算机系统概述、指令执行入门 | 第一章 |
| 整数表示与补码运算 | 第二章 |
| 加法器、ALU 与溢出检测 | 第三章 |
| 数据通路与状态保存入门 | 第一章第三部分;第三章第五部分 |
第一章:程序是怎样执行的
第一部分:计算机里的各部件怎样分工?
1. 计算机到底在干什么?
假设一家工厂接到订单,要把两批原料加工成一件产品。它需要有地方放原料、有机器加工、有运输通道,还要有人安排先后顺序。
计算机也是这样。先抓住三件事:存储数据、加工数据、搬运数据。
比如一行代码:
1 | c = a + b; |
在一个简化的机器模型里,相当于从存储位置取出 a 和 b,交给运算部件相加,再把结果保存起来。它涉及一连串实际的数据传送和状态变化,不能只用抽象的“算一下”来理解。
2. 五大功能部件各自负责什么?
- 运算器:工厂里的加工机器。 对数据做算术运算和逻辑运算。ALU(算术逻辑单元)是其中的重要部件,负责加法、与、或等操作。
- 控制器:安排生产的调度员。 根据指令决定现在该读哪个数据、做什么操作、把结果送到哪里。
- 存储器:仓库。 保存程序和数据,供执行时读取,也接收需要保存的结果。
- 输入设备:进料口。 把外部信息送入计算机,例如通过键盘输入内容。
- 输出设备:出料口。 把处理结果交给外部,例如显示到屏幕上。
⚠️ 易混点:控制器不等于运算器。 调度员安排“把这两份数据相加”,真正做加法的是运算电路。
CPU 通常包含运算器、控制器和寄存器等部件。不能把 CPU 理解成只有一个计算器,也不能把整台计算机都叫作 CPU。
3. 寄存器、内存、硬盘有什么区别?—— 手边工作台与仓库
如果每加工一步都要跑去远处仓库拿东西,工人再快也没用。所以工位旁边需要小工作台,常用材料先放在手边。
- 寄存器: CPU 内部的小型存储部件,像工位上的工作格,放当前操作数、地址或状态。
- 主存(内存): 程序运行时的重要存储空间,像车间仓库,容量比手边工作格大。
- 辅存(如 SSD): 用于长期保存文件和程序,像大仓库。程序和数据通常需要经过加载等过程,才能被 CPU 使用。
Cache 可以理解成 CPU 与主存之间的近处备料区,用来缓解速度差距。这里先分清角色,不展开其内部规则。
核心矛盾:速度、容量和成本很难同时做到理想。 存储层次就是围绕这个矛盾作出的安排。
第二部分:程序怎样变成机器指令?
1. 存储程序:把“加工步骤”也放进仓库
假设每加工一件产品,都要有人站旁边喊:“先拿 a,再拿 b,然后相加。”这就谈不上自动化。
解决办法是把操作步骤写成工单,让机器自己按工单执行。
存储程序思想:把程序的指令预先存入存储器,机器运行时自动取出并执行。 在经典冯·诺依曼模型中,指令和数据都以二进制形式存储。
⚠️ 同样是比特,不代表含义相同。 一张纸可以是加工说明,也可以是数量记录;机器取它来作指令还是作数据,要看执行上下文。
2. 机器指令是什么?—— 一张具体的加工工单
下面这条教学用汇编指令:
1 | ADD R3, R1, R2 |
表示:
1 | R3 ← R1 + R2 |
可以把它翻译成工厂语言:“取工作格 R1 和 R2 中的材料,做加法,把结果放进 R3。”
- 做什么: 加法。
- 输入在哪里: R1、R2。
- 结果放哪里: R3。
机器里保存的不是字符 ADD,而是对应的二进制编码。操作类型、寄存器编号等信息都要按规则编码。
ISA(指令集体系结构)就像机器对外公布的操作手册: 支持哪些操作、有哪些可用寄存器、执行应产生什么效果。具体 CPU 再用硬件实现这些约定。
3. 高级语言、汇编和机器指令是什么关系?
可以把高级语言想成“我要完成什么”的生产要求,汇编更接近逐条机器工单,机器编码则是硬件能够识别的格式。
例如前面的加法,在简化模型中可以写成:
1 | LOAD R1, [a] |
底层过程:搬进来 → 计算 → 保存回去。 这只是教学示意,实际编译器可能利用寄存器中已有的数据,生成不同指令。
第三部分:跟着一条指令走完执行过程
1. PC 和 IR 为什么要分开?—— 工单位置与当前工单
工厂自动执行工单,需要同时知道两件事:去哪里拿下一张,以及现在手里拿的是哪一张。
- PC(程序计数器): 保存取指所需的地址,像记录工单位置的指针。
- IR(指令寄存器): 保存取出的当前指令,像工位上正在处理的工单夹。
⚠️ PC 不是“已经执行了几条指令”的计数器,也不是源代码行号。 它保存的是指令地址。
下面约定一个简单模型:按字节编址,每条指令占 4 字节,当前 PC 为 1008,该地址处是一条 ADD。这里只用于说明过程,不代表所有指令集都采用固定的 4 字节指令。
2. 取指:先把工单拿到手
CPU 根据 PC 中的地址取出指令,放入 IR。在本例的顺序执行过程中,PC 可以更新为 1012,为下一次取指作准备。
1 | PC 给出 1008 → 取出指令 → IR 保存当前 ADD |
为什么当前操作没做完,PC 就能先往后移? 像工人已经把工单夹在面前,就可以先把取单指针移到下一张,当前工作不会因此丢失。具体更新时机由硬件设计决定。
3. 译码:控制器把工单变成具体动作
看到“把 R1 和 R2 相加”,控制器需要安排:
- 选择 R1、R2 作为输入。
- 让 ALU 执行加法。
- 选择 ALU 的结果作为写回数据。
- 允许 R3 接收结果。
控制信号就像车间里的开关和调度指令。 它决定哪些通路接通、哪个部件执行什么操作。数据通路负责搬运和处理,控制器负责协调它们。
4. 执行与写回:加工完,还得放到指定位置
假设 R1 保存 5,R2 保存 7。为了看清运算,只画低 8 位:
1 | 00000101 |
ALU 得到 12,再把结果写进 R3。
类比: 产品刚从加工机器出来,还不等于已经入库。必须送进指定工作格,下一步才能从那里取到它。
⚠️ ADD 写回 R3,不会自动写回内存中的 c。 示例里还需要 STORE,才能把 R3 中的结果存到 c 对应的位置。
5. 不同工单会走不同路线
| 指令 | 工厂类比 | 主要作用 |
|---|---|---|
| LOAD | 从仓库领料到工作格 | 内存 → 寄存器 |
| ADD | 把工作格中的材料送去加工 | 寄存器 → ALU → 寄存器 |
| STORE | 把结果送回仓库 | 寄存器 → 内存 |
| JUMP | 改变接下来要领的工单位置 | 改变后续取指地址 |
JUMP 为什么能改变执行顺序?因为它改变了后续取指的位置。条件分支、循环等控制流,就与比较和跳转密切相关。
注意:ADD 不需要读取数据内存,不代表它不需要取指。 取加工工单和取加工原料,是两个不同动作。
本章自测
假设当前 PC 为 400,每条指令占 4 字节,机器刚取出一条 ADD。
- 在上述顺序执行模型中,PC 接下来指向哪里?当前 ADD 又保存在哪里?
- 控制器和 ALU 的工作有什么区别?用调度员与加工机器解释。
- 为什么 ADD 得到结果之后,还可能需要一条 STORE?
第二章:计算机怎样表示和运算整数
上一章把数据交给 ALU 就能得到结果。先不拆电路,先弄清同一串比特怎样代表不同的数,以及运算规则从哪里来。
第一部分:二进制怎样表示数值?
1. 为什么一串 0 和 1 能表示数?
十进制里的 345,并不是把 3、4、5 简单摆在一起,而是:
1 | 3 个百 + 4 个十 + 5 个一 |
二进制使用同样的位权思想,只是每一位只能填 0 或 1,相邻位置的权重相差两倍。
把 4 位二进制想成四个格子,从左到右分别能放一张面值 8、4、2、1 的卡片。填 1 表示放入,填 0 表示不放。
例如 1011:
$$ 1\times 8+0\times 4+1\times 2+1\times 1=11 $$
核心理解:同一个 1 放在不同位置,贡献的数值不同。 不要只记“逢二进一”,还要知道每一位的权重。
2. 位数有限,能表示的范围也有限
四个格子,每格两种状态,一共有 $2^4=16$ 种组合。全部用于表示非负整数,就可以表示 0~15。
一般 n 位无符号整数的范围是:
$$ 0\le x\le 2^n-1 $$
类比: 只有两位的十进制计数器最多显示 99,无法直接显示 100;4 位二进制也没有无限的位置可以继续写。
第二部分:怎样给二进制数加上正负?
1. 最直观的方案:留一位表示符号
如果还要表示欠款,就不能只记录“有多少钱”,还得记录是拥有还是欠着。
原码的办法很直观:最高位表示符号,0 为正、1 为负,其余位表示绝对值。用 4 位举例:
1 | 0101:+5 |
类比: 两张金额都是 5 元的单据,一张贴“收入”,另一张贴“支出”。金额部分相同,标签不同。
2. 直观不等于适合直接运算
现在要算 $5+(-5)$。按常识应该是零,但如果把上面的整串编码直接送进普通二进制加法器:
1 | 0101 |
只留低 4 位,得到 0010,也就是 2,结果错了。
问题在哪里? 加法器按位权处理整串比特,并不知道最高位被人另外贴上了“负号”的含义。就像会计把“支出标签的编号”也当金额加进账里,当然对不上。
原码可以表示负数,但做有符号运算时,需要额外处理符号、大小关系等情况。于是就有了新的需求:能不能设计一种编码,让负数也能更直接地使用加法硬件?
第三部分:补码为什么能统一加减法?
1. 先看钟表:往回走,也能换成向前走
在 12 小时的钟面上,从 3 点往回走 5 格,会到 10 点;从 3 点向前走 7 格,也会到 10 点。因为 7 和 -5 相差整整一圈。
固定宽度的二进制加法也有类似的“绕圈”现象。4 位只能保存 16 种状态,所以可以把它想成标了 0~15 的钟面:
1 | 1111 + 0001 = 1 0000 |
底层原理:保留 n 位加法结果,就是保留模 $2^n$ 的结果。 超出位宽的进位可以单独记录,但不会自动成为这 n 位的一部分。
2. 负数怎样对应到钟面上的位置?
在模 16 的意义下,往回走 1 格和向前走 15 格等效:
$$ -1\equiv15\pmod{16} $$
同样,往回走 5 格等于向前走 11 格:
$$ -5\equiv11\pmod{16} $$
于是可以用 1111 表示 -1,用 1011 表示 -5。4 位补码把 16 种状态解释成:
| 编码 | 补码含义 | 可以怎样理解 |
|---|---|---|
| 0000~0111 | 0~7 | 非负数 |
| 1000 | -8 | 从零反向走 8 格 |
| 1011 | -5 | 从零反向走 5 格 |
| 1110 | -2 | 从零反向走 2 格 |
| 1111 | -1 | 从零反向走 1 格 |
⚠️ 同一串比特,解释规则不同,数值就不同。 1110 按无符号数解释是 14,按 4 位补码解释是 -2。不是比特自己发生了变化。
3. “取反加一”为什么成立?
4 位正数 5 是 0101。逐位取反,相当于用 1111 减去它,也就是 $15-5$;再加 1,就得到 $16-5$:
1 | 0101 → 取反得到 1010 → 加 1 得到 1011 |
这正是 -5 在模 16 中对应的编码。
所以“取反加一”有它的来由:先得到差一圈的补数,再补上那一个 1。 记住模运算的原因,比孤立地背步骤更容易理解。
4. 用同一个加法器完成减法
重新计算 $5+(-5)$:
1 | 0101 (+5) |
低 4 位是 0000,结果为零。
再计算 $7-3$,把它改成 $7+(-3)$:
1 | 0111 (+7) |
低 4 位是 0100,结果为 4。
类比: 机器只擅长把钟表往前拨,我们就把“倒拨几格”换成“向前拨剩下的格数”。这样,加正数和加负数都能利用同一套加法电路。
5. 为什么 4 位补码只能表示 -8~7?
16 种编码分配给有符号整数后,0000~0111 表示 0~7,1000~1111 表示 -8~-1。零只占一个编码,因此负数一边比正数一边多一个。
一般 n 位补码的范围是:
$$ -2^{n-1}\le x\le 2^{n-1}-1 $$
⚠️ 补码没有让位数变多。 它让编码适合运算,但不能突破有限位宽的容量限制。
6. 进位和溢出不是一回事
刚才算 $5+(-5)$ 时,最高位之外有进位,但低 4 位的结果零是正确的。
再看 $7+1$:
1 | 0111 + 0001 = 1000 |
没有多出第五位,但 1000 按补码解释成 -8。两个正数相加却得到负数,这是因为真正的结果 8 已经超出 4 位补码的上限。
- 进位: 从某一位向更高位传出的位。
- 有符号溢出: 数学结果超出了当前有符号编码能表示的范围。
类比: 钟表能绕回去,不代表账本里“欠的钱”和“有的钱”也能随便绕。硬件留下了一个合法编码,不等于它还能表达你想要的数学结果。
本章自测
- 1011 按 4 位无符号整数、4 位原码、4 位补码解释,分别是多少?为什么 -5 的四位补码是 1011?
- 为什么补码可以把减法转成加法?
- 为什么最高位有进位,不一定表示有符号溢出?
先解释编码规则,再用具体数值检查结果。
第三章:从逻辑门到 ALU
已经知道比特怎样表示数,接着把加法器拆开,从逻辑门逐步搭到 ALU,最后回到第一章的 ADD 指令。
第一部分:逻辑门怎样实现一位加法?
前面一直说“把两个数交给加法器”,但工厂里不能只有一块写着“我会加法”的牌子。现在要把这台加工机器拆开,看它究竟靠什么工作。
1. 逻辑门:只按固定规则办事的小开关
数字电路用不同的电平范围表示 0 和 1。逻辑门由晶体管等器件组成;在数字逻辑这一层,可以先把晶体管理解成受电信号控制的开关,再研究开关组合后的输入、输出关系。
类比: 车间门口有几个只认固定规则的检查员。它们不会读整张订单,只判断眼前的条件是否满足:
- AND(与): 两个条件都满足才放行,相当于“有工牌,并且有通行许可”。
- OR(或): 至少一个条件满足就放行,相当于“走人工验证或电子验证,任意一种通过即可”。两个都满足也通过。
- XOR(异或): 两个输入不同时输出 1,相当于两盏灯中“恰好亮一盏”才算通过。
- NOT(非): 把结果翻转,0 变 1,1 变 0。
| A | B | AND:$A\land B$ | OR:$A\lor B$ | XOR:$A\oplus B$ |
|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 |
| 0 | 1 | 0 | 1 | 1 |
| 1 | 0 | 0 | 1 | 1 |
| 1 | 1 | 1 | 1 | 0 |
⚠️ 或与异或的区别,就在两个输入都是 1 时:OR 输出 1,XOR 输出 0。 后面做加法,正好会用到这个区别。
2. 一位加法要给出两个答案
小学列竖式时,个位算出 12,要在个位写 2,再向十位进 1。二进制也是一样,只是满 2 就进位。
因此 $1+1$ 的二进制结果是 10:本位写 0,向高位进 1。 一个一位加法部件需要两个输出:
- Sum(S): 留在本位的结果。
- Carry(C): 送给高一位的进位。
把两个一位输入的情况全部列出来:
| A | B | 二进制和 | S:本位结果 | C:进位 |
|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 |
| 0 | 1 | 1 | 1 | 0 |
| 1 | 0 | 1 | 1 | 0 |
| 1 | 1 | 10 | 0 | 1 |
观察本位结果这一列:输入不同时是 1,相同时是 0,正好就是 XOR。再看进位:只有两个输入都是 1 时才产生,正好就是 AND。
$$ S=A\oplus B,\qquad C=A\land B $$
这就得到了半加器(Half Adder):同一对 A、B 同时送入异或门和与门,分别得到 S 和 C。
核心原理:不需要电路“理解数学”。只要输入、输出规则与一位加法完全一致,电路就实现了这一位的加法。
3. 为什么只叫“半”加器?
假设算到十位时,个位已经向上进了 1。十位的工人不能只加两张原始单据,还要接收低位递过来的那张进位单。
半加器只有 A、B 两个输入,无法直接处理第三个输入。对于多位加法,还需要一个能同时处理 A、B 和低位进位 的部件。
第二部分:全加器怎样串起多位运算?
1. 全加器:把低位递来的进位也算进去
**全加器(Full Adder,FA)**有三个输入、两个输出:
- 输入:A、B、$C_{in}$(低位传来的进位)。
- 输出:S(本位结果)、$C_{out}$(传向高位的进位)。
类比: 每个工位收到两张本位单据,再接收前一个工位送来的进位单。凑够两个“1”,就向下一站换出一个进位,剩下的留在本位。
| A | B | $C_{in}$ | S | $C_{out}$ |
|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 |
| 0 | 0 | 1 | 1 | 0 |
| 0 | 1 | 0 | 1 | 0 |
| 0 | 1 | 1 | 0 | 1 |
| 1 | 0 | 0 | 1 | 0 |
| 1 | 0 | 1 | 0 | 1 |
| 1 | 1 | 0 | 0 | 1 |
| 1 | 1 | 1 | 1 | 1 |
本位结果的规律是:三个输入里有奇数个 1,S 就是 1。因此:
$$ S=A\oplus B\oplus C_{in} $$
进位的规律是:三个输入中至少两个是 1,就要进位。因此:
$$ C_{out}=(A\land B)\lor(A\land C_{in})\lor(B\land C_{in}) $$
⚠️ 有些教材写成 $C_{out}=AB+AC_{in}+BC_{in}$,这里的乘表示与,加号表示或,不是普通算术加法。 三个输入全为 1 时,进位输出仍然只有一个比特 1。
2. 两个半加器加一个或门,就能组成全加器
不用从头发明一台机器,把刚做好的半加器组合起来即可:
- 第一个半加器: 计算 A、B,得到临时和 T、进位 P。
- 第二个半加器: 计算 T、$C_{in}$,得到最终的 S、另一条进位 Q。
- 或门: 只要 P、Q 中有一个产生进位,就输出 $C_{out}=P\lor Q$。
1 | A、B ──→ 半加器 1 ──→ T ──┐ |
类比: 第一张工作台先处理两份原料,第二张再把前一站送来的补料加进去;两处产生的进位统一交给高位。复杂部件就是这样由简单部件拼起来的。
3. 四个全加器怎样处理一个四位数?
每一位安排一个全加器,把低位的 $C_{out}$ 接到高一位的 $C_{in}$。普通加法没有外部进位时,最低位的初始进位 $C_0=0$。

图中从左到右是低位到高位,蓝色横线传递进位,黑色竖线分别表示操作数输入和本位结果输出。最后读数要按高位到低位排列,即 S3、S2、S1、S0。
这叫串行进位加法器,也叫涟波进位加法器(Ripple Carry Adder)。就像接力:本位产生的进位,成为下一位的一个输入。
⚠️ “串行”说的是进位的依赖关系。 不是 CPU 每过一个时钟才算下一位,也不是所有输入必须排队才能进入电路。各位输入可以同时到达,但某些高位结果要等进位传播后才能稳定。
4. 逐位算一遍 5 + 7
这里先按 4 位无符号整数解释:$5=0101$、$7=0111$,从最低位 bit 0 开始。
| 位次 | A | B | 输入进位 | 本位计算 | S | 输出进位 |
|---|---|---|---|---|---|---|
| bit 0 | 1 | 1 | 0 | $1+1+0=2$ | 0 | 1 |
| bit 1 | 0 | 1 | 1 | $0+1+1=2$ | 0 | 1 |
| bit 2 | 1 | 1 | 1 | $1+1+1=3$ | 1 | 1 |
| bit 3 | 0 | 0 | 1 | $0+0+1=1$ | 1 | 0 |
按高位到低位把 S 拼起来,得到 1100,即无符号整数 12,最高位输出进位为 0。
⚠️ 如果改按 4 位补码解释,结论就不同。 5、7 都能表示,但结果 12 超出 -8~7 的范围,1100 会被解释成 -4,说明发生了有符号溢出。电路输出的比特没变,数值解释和是否越界的判断变了。
5. 位数多了,为什么会慢?
把四个工位增加到几十个,最坏情况下进位要一路传过去。例如 1111 + 0001,最低位产生的进位会不断影响高位。
逻辑门有传播延迟,导线传递信号也不是瞬间完成。进位依赖链越长,最坏情况下等待结果稳定的时间就越长。 这种延迟会影响电路能采用多短的时钟周期。
先行进位等更快的设计,就是设法减少逐级等待。这里先知道串行进位的瓶颈,不能据此认为所有真实 CPU 的加法器都简单串着几十个全加器。
第三部分:同一套电路怎样完成加减法?
1. 补码怎样变成实际电路?
前面知道减法可以转成加负数。在固定 n 位、保留低 n 位结果的意义下:
$$ A-B\equiv A+\overline{B}+1\pmod{2^n} $$
横线表示对 B 的 n 个比特逐位取反。现在已经有加法器,只要解决两件事:需要减法时把 B 取反,并且额外加 1。
类比: 工厂已经有一台加法机器,不必为了减法再建一条完全独立的生产线;先在进料口转换原料,再让同一台机器加工。
2. XOR:受控制的翻转开关
异或有两条特别有用的性质:
$$ B_i\oplus0=B_i,\qquad B_i\oplus1=\overline{B_i} $$
可以把它想成一个“翻面开关”:控制位为 0,卡片原样通过;控制位为 1,每张卡片的 0、1 全部翻转。
设置控制信号 SUB,规定 0 做加法、1 做减法。把 SUB 同时接到 B 每一位对应的异或门,得到:
$$ B'_i=B_i\oplus SUB,\qquad i=0,1,\ldots,n-1 $$
再把最低位全加器的输入进位设为 $C_0=SUB$,整台机器计算的就是 $A+B'+C_0$。
| SUB | 各位异或后的 B′ | 最低位输入进位 | 实际运算 |
|---|---|---|---|
| 0 | B | 0 | $A+B$ |
| 1 | B 的逐位取反 | 1 | $A+\overline{B}+1$,即减法的低 n 位结果 |
⚠️ SUB 要广播到 B 的每一位,不是只翻转最低位。额外的 1 则只送到最低位的输入进位端,不是给每一位都加 1。 其余各位接收前一级实际产生的进位。
3. 完整跑一次 5 - 3
设 A 为 0101,B 为 0011,SUB 为 1:
1 | B = 0011 |
保留低 4 位得到 0010,即 2。如果忘记让 C0 等于 1,就只做了取反,没有完成补码取负,结果会差 1。
这正是补码在硬件上的价值:加法和减法可以共用加法电路,用少量控制逻辑切换。
第四部分:电路怎样判断进位和溢出?
1. 同一组比特,两本不同的账
考虑四位加法:
1 | 1111 + 0001 = 1 0000 |
- 按无符号数解释: $15+1=16$,超出四位的 0~15 范围。最高位进位为 1,低四位不能独立表示完整结果。
- 按补码解释: $-1+1=0$,低四位
0000正确,没有有符号溢出。
类比: 同一张编码单据交给两种记账规则,会得到不同的金额含义。加法电路计算比特,是否超范围必须结合位宽和数值解释判断。
2. 补码加法的判断:同号相加,结果变号
四位补码的范围是 -8~7。如果算 $7+3$:
1 | 0111 + 0011 = 1010 |
两个正数加出了被解释为 -6 的编码,发生溢出。同样,两个负数相加,结果若变成非负数,也发生溢出。例如 $-8+(-1)$ 的低四位会变成 0111,即 7。
补码加法的规则:两个操作数同号,结果的符号却与它们不同,就发生有符号溢出;异号相加不会发生有符号加法溢出。 这里讨论的是加法,不能把同一句判断不加区分地套到减法上。
3. 电路怎样检查溢出?
对 n 位加法,记 $C_{n-1}$ 为进入最高符号位的进位,$C_n$ 为符号位送出的进位。两者不同时,发生补码溢出:
$$ V=C_{n-1}\oplus C_n $$
又是一个 XOR。 这次它比较的是符号位两侧的进位,而不是两个操作数。看前面的四位 $5+7$:进入 bit 3 的 $C_3=1$,输出的 $C_4=0$,因此 V 为 1,和数值范围判断一致。
| 四位加法 | 低四位结果 | 最高位进位 $C_4$ | 补码溢出 V |
|---|---|---|---|
| 1111 + 0001 | 0000 | 1 | 0 |
| 0111 + 0011 | 1010 | 0 | 1 |
| 0101 + 0111 | 1100 | 0 | 1 |
结论要分开说:最高位进位反映无符号加法是否超范围,有符号溢出反映补码结果是否超范围。 不能只盯着一个 Carry,就判断所有整数运算是否正确。
第五部分:ALU 怎样参与指令执行?
1. 加减法器不是完整的 ALU
CPU 除了加减法,还需要与、或、异或等操作。可以先构造一个简单的 ALU:把输入送给加减法模块和若干逻辑运算模块,再从这些模块的输出中选择一个作为结果。
类比: 同一批材料可以送到不同加工工位。调度员说“这次要加法结果”,出料口就接通加法工位;说“这次做按位与”,就接通与运算工位。
负责选择的部件叫 MUX(Multiplexer,多路选择器):根据选择信号,从多个输入中选一个输出。它本身不负责把两个数相加。
1 | A、B ──→ 加减法模块 ──┐ |
在这个教学模型里,控制器既要决定加减法模块的 SUB,又要决定 MUX 选择哪个模块。例如:
| 需要的操作 | 加减法模块的 SUB | MUX 选择 |
|---|---|---|
| ADD | 0 | 加减法模块 |
| SUB | 1 | 加减法模块 |
| AND | 对该结果无影响 | AND 模块 |
| OR | 对该结果无影响 | OR 模块 |
| XOR | 对该结果无影响 | XOR 模块 |
⚠️ ALUControl 的具体编码由设计决定。 不是所有处理器都用某组固定的 00、01 编码,也不是每种 ALU 都让所有模块一直同时工作。比较、移位等功能的组织方式也可能不同。
2. 回到 ADD:控制器到底控制了什么?
执行 ADD R3, R1, R2 时,现在可以把原先一句“ALU 做加法”拆开:
- 寄存器堆提供 R1、R2 的值。
- 控制器让加减法模块处于加法模式,即 SUB 为 0。
- 加法电路根据各位输入和进位产生结果。
- MUX 选择加法结果,写回通路在规定时刻把它保存到 R3。
对于能容纳 12 的位宽,例如 8 位,5 和 7 相加得到 00001100。至此,从指令到逻辑门的联系就具体了:操作码决定控制信号,控制信号选择数据通路,逻辑电路产生结果,寄存器保存结果。
3. 组合逻辑与时序逻辑:加工机器不等于仓库
刚才的半加器、全加器、MUX,以及这里的简单 ALU 数据运算部分,都属于组合逻辑:经过传播延迟、输出稳定后,结果由当前输入决定,不保存上次运算的状态。
类比: 计算器的加工部分负责“当前这批原料能算出什么”,它不是负责保存旧结果的账本。输入一变,输出也会随之变化,而不是自动把前一次答案留下来。
寄存器则必须能够保存之前写入的值,在允许更新时才接收新值。这需要时序逻辑,其行为与已有状态有关。PC、IR 也需要这种存储能力。
| 类型 | 可以怎样理解 | 本节例子 |
|---|---|---|
| 组合逻辑 | 按当前输入加工,不记旧账 | 半加器、全加器、MUX |
| 时序逻辑 | 保存状态,再按条件更新 | 寄存器、PC、IR |
⚠️ “没有记忆”不等于“没有延迟”;“能保存状态”也不等于“断电不丢失”。 寄存器通常需要供电维持状态。至于触发器和时钟怎样实现受控更新,下一部分再继续拆。
本章自测
- 半加器为什么需要 S 和 C 两个输出?它还缺哪个输入,才需要扩展成全加器?
- 全加器输入 A、B、Cin 都为 1 时,S 和 Cout 分别是什么?能否用两个半加器推一遍?
- 画四个全加器计算
0101 + 0111,标出每一级的进位。为什么按无符号数解释得到 12,按四位补码解释却发生溢出? - 设 A 为
0101、B 为0011。SUB 为 0 和 1 时,B′、C0、低四位结果分别是什么?哪一种情况发生补码溢出? - 减法时为什么必须翻转 B 的每一位,又为什么只把额外的 1 接到最低位?
- MUX 会做加法吗?如果加法器已经算出结果,为什么还需要寄存器和写回控制?
- 为什么
1111 + 0001有最高位进位却没有补码溢出,而0111 + 0010没有最高位进位却发生了补码溢出?
答题时分别说清电路怎样连、编码怎样解释、结果有没有超出范围,不要把三个层面混在一起。
更新于 2026-10-02。按程序执行、数的表示、运算电路组织已学内容。