前一篇整理了逻辑门的规则,以及怎样根据真值表组合出需要的电路。接下来要把一位信号扩展成多位数据,再让电路保存计算结果,继续参与下一次运算。
二进制计算、选择数据和保存状态,都是构成 CPU 核心所需的基础能力。先把各个部件负责什么、彼此怎样配合梳理出来,后面搭加法器或寄存器时,就知道正在补上哪一块。
1. 一个简单 CPU 核心需要哪些部件?
先以一个简单的同步整数处理器为例。它需要读取指令,按指令要求处理数据,并保存结果和执行进度。下面按功能列部件,具体实现时,有些功能可以共用电路。
这里“整数”表示先处理整数运算;“同步”表示保存新状态的时刻由时钟协调。可以先把指令理解成一条操作要求,例如“把这两个位置里的数相加,再存到第三个位置”。程序就是按一定顺序组织起来的这些指令。
| 部件或功能 | 负责什么 | 一个直接的例子 |
|---|---|---|
| 算术逻辑单元(ALU) | 根据控制信号,对输入的数据做指定运算 | 这次把两个数相加,下次对它们逐位做 AND |
| 寄存器与寄存器组 | 在核心内部保存正在使用的数据 | 保留两个操作数,把运算结果写入某个寄存器 |
| 选择器(MUX)与数据通路 | 选择数据来源,把数据送到需要的位置 | ALU 的一个输入来自寄存器,或来自指令中的常数 |
| 译码与控制逻辑 | 识别指令,产生选路、运算、读写等控制信号 | 指定 ALU 做加法,并允许目标寄存器写入 |
| 程序计数器(PC)及更新逻辑 | 保存取指位置,决定后续从哪里取指令 | 顺序前进,或者按跳转条件换到另一处 |
| 存储器访问接口 | 传递地址、读写数据和控制信号 | 从某个地址读一个数,或把结果写回该地址 |
| 时钟、复位与状态更新机制 | 协调什么时候保存新状态,以及从什么状态开始 | 在规定的时钟边沿写入结果,复位后从指定位置执行 |
这里的数据通路是数据流经的寄存器、运算电路、选择器和连线的整体;控制逻辑决定每一步怎样使用这条通路。ALU 是其中负责运算的一部分。这样的划分也出现在 MIT 的处理器与指令集讲义中。
寄存器通常位于核心内部,主存通常位于核心之外,由访问接口连接。缓存用于缓解速度差异,它在具体芯片中怎样分布,属于后续的存储层次问题。先搭一个能运行程序的简单核心,可以从寄存器和基本存储器接口开始。
2. 计算单元里面,先认识哪些电路?
ALU 的名字里有「算术」和「逻辑」,所以它既处理数字,也处理位。下面这些运算能力可以由不同电路实现,也可能共用部分硬件。
| 运算部件 | 处理什么 | 先这样理解 |
|---|---|---|
| 加法器 | 把两个二进制数相加 | 从低位开始算,装不下的部分进到高一位,和竖式加法类似 |
| 减法电路 | 求两个数的差 | 把减去 B 改成加上负 B;补码让加法器也能处理这件事 |
| 按位逻辑电路 | 对各位做 AND、OR、XOR、NOT 等运算 | 每个位置各算各的,例如 8 位 AND 就让 8 对输入分别做 AND |
| 比较电路 | 判断相等、大小,或结果是否为零 | 比较前先明确这些位按什么数来读;同一串位可能表示正数,也可能表示负数 |
| 移位电路 | 把位向左或向右移动 | 左移通常在右边补 0;右移可以在左边补 0,也可以补原来的最高位,要看采用哪种运算 |
| 运算结果选择电路 | 从几种候选结果中选出本次结果 | 控制信号指定哪一路结果送到输出 |
进位、零值、有符号溢出等也可能形成供控制逻辑使用的信号。有些指令集会把其中一部分保存在标志寄存器里,有些采用其他方式表达比较结果,具体要看指令集和实现。
乘除法器、浮点运算单元、向量运算单元可以在后面继续了解。它们是否独立设置、支持哪些操作,取决于处理器的目标。当前先理解整数运算和数据保存,足够支撑最初的核心设计。
3. 用一次加法,把这些部件串起来
用 R3 ← R1 + R2 表示「取出 R1、R2 保存的数,相加后写入 R3」。这只是说明操作含义的记法,尚未指定某种机器指令的编码。
R1、R2、R3 是三个寄存器的名字,像三个有编号的小格子,名字中的数字不是格子里保存的数值。箭头 ← 表示把右侧算出的结果写入左侧的位置。这次操作只写 R3,不会因为读了 R1、R2,就把它们原来的内容拿走。
- 取指令: 根据 PC 给出的位置,从指令存储器取出指令。
- 读懂要求: 译码后确定这次做加法、读取 R1 和 R2、把结果交给 R3。
- 送入数据: 寄存器组提供两个值,选择器把它们送到 ALU 的输入端。
- 算出结果: 加法电路计算各位及进位,经过传播延迟后结果稳定。
- 保存并继续: 在规定的更新时刻把结果写入 R3,并按设计更新 PC,继续执行。
假设 R1 保存 3,R2 保存 5,ALU 算出 8;写入之前,R3 仍保留自己的旧值。这个区别就是「已经算出一个结果」与「已经保存这个结果」的区别。
例如 R3 原来保存 9,这次操作可以这样核对:
| 时刻 | R1 的内容 | R2 的内容 | R3 的内容 |
|---|---|---|---|
| 操作前 | 3 | 5 | 9 |
| 已算出 8,但尚未写入 | 3 | 5 | 9 |
| 完成写入后 | 3 | 5 | 8 |
上述步骤描述的是各部件的职责。一次指令需要几个时钟周期,取决于核心采用单周期、多周期还是流水线等实现,后面再结合具体电路讨论。
如果数最初放在主存里,还需要通过存储器读取它;若要求把结果存回主存,也需要一次相应的写入。怎样把这些操作组织成指令,与选择的指令集有关。
4. 从计算到保存数据
二进制的表示、进制转换和计算,单独记在第三篇:二进制与二进制速算里。这里继续看计算结果怎样保存,并交给后续操作使用。
4.1 算出结果,还要把它保存下来
计算电路根据输入给出结果;如果输入变了,结果也可能跟着变。要把某一次的结果留住,就需要存储电路。
例如,把结果 8 写进寄存器以后,即使加法器开始算别的数,这个寄存器也可以继续保存 8,直到下一次写入新值。保存下来的值,就是后续计算可以继续使用的状态。
一个存储单元保存一位,八个一起就能保存一个八位数。把多个保存位置按编号组织起来,就可以按地址读写数据。地址负责指出位置,数据是这个位置里保存的内容。
例如某块存储器的地址 12 中放着 8:“读地址 12”得到的是数据 8;“把 5 写到地址 12”之后,位置仍叫 12,内容变成 5。地址不是这次要参与加法的数,它是在指出去哪里取数或存数。
在这里先考虑有时钟的设计:计算电路先算出结果,存储部件再按约定的时刻接收它。算好与存好是两个步骤,这也是后面需要同时考虑运算和时钟的原因。
4.2 后面的内容,用到时再展开
下面这些先知道用途就够了,具体推导和接线跟着实践补充:
| 内容 | 现在先知道什么 | 到哪一步再详细讲 |
|---|---|---|
| 半加器、全加器 | 加法要同时算本位结果和进位;全加器还接收低位送来的进位 | 搭一位和多位加法器时 |
| 补码、减法与溢出 | 负数需要约定表示规则;补码让加法器可以参与完成减法;固定的位数也可能装不下结果 | 开始处理负数和减法时 |
| 选择器、译码器 | 选择器从几路数据中挑一路;译码器根据编号选中对应位置 | 需要选 ALU 输入、运算结果或存储位置时 |
| 反馈、锁存器、触发器 | 反馈是把输出接回前面的输入;合适的结构能够保存状态,存储部件还要控制何时接收新值 | 开始搭保存一位数据的电路时 |
| 寄存器组、RAM 的读写规则 | 按编号选位置,读取其中的数据,或在允许写入时放入新数据 | 把多个保存位置组织起来时 |
| 指令、控制与 PC | 指令规定做什么,控制信号安排各部件,PC 记录从哪里取指令 | 把运算和存储连接成完整核心时 |
这些内容会在后续搭建中用到,但需要配合解释、推导和验证来理解。先看懂输入输出的含义,再动手接线;做到哪个部件,就把那个部件的原理补完整。
5. 与《CS:APP》怎样对应?
这些内容涉及数字逻辑、计算机组成与指令集。《CS:APP》第三版中,可以按正在解决的问题回看相应章节。下面的对应关系依据作者公开的目录与前言整理:
| 当前问题 | 对应章节 | 重点 |
|---|---|---|
| 计算机有哪些主要部件,程序怎样开始运行? | 第 1 章:计算机系统漫游 | 处理器、主存与系统整体关系 |
| 位怎样表示数,加减法为什么会溢出? | 第 2 章:信息的表示和处理 | 位运算、整数编码、补码与整数运算 |
| 程序怎样使用寄存器、内存和跳转? | 第 3 章:程序的机器级表示 | 从机器指令观察状态和数据操作 |
| 门、寄存器和控制怎样组成处理器? | 第 4 章:处理器体系结构 | 组合逻辑、存储与时钟、数据通路及处理器实现 |
| 寄存器、缓存和主存为什么需要分层? | 第 6 章:存储器层次结构 | 存储技术、局部性、缓存和层次关系 |
近期先以第 2 章的整数表示和运算、第 4 章的逻辑与处理器组成为主线。锁存器、反馈电路和加法器的具体搭建,再结合数字逻辑材料展开;缓存、流水线、浮点数等内容留到基本的数据计算与保存流程清楚之后。
这一阶段先建立的联系是:位组成数,逻辑门计算这些位,存储电路保留结果,控制逻辑安排下一步。