1.3 存储程序与指令周期
计算器与简单控制器的硬件可能十分相似。使它们表现不同的是各自执行的指令序列。存储程序思想认为,指令可以表示为比特模式,并保存在存储器中。
这个思想带来一个重要结果:只要改变程序,就能改变系统行为,而不必重新搭建处理器的物理电路。
指令存放在地址中
设想一个小型存储器,其中保存下面的教学程序:
| 地址 | 指令 | 通俗解释 |
|---|---|---|
| 0 | LOAD 5 | 把数据地址 5 中的值复制到累加器 |
| 1 | ADD 6 | 加上数据地址 6 中的值 |
| 2 | STORE 7 | 把结果复制到数据地址 7 |
| 3 | OUT | 把结果发送给输出 |
| 4 | HALT | 停止 |
这些可读的操作名代表真实的比特模式。实际处理器的指令集架构规定哪些比特模式表示 LOAD、ADD 或其他操作;后续章节会深入学习这个边界。
例子还使用了累加器,它是保存工作数值的 CPU 寄存器。如果存储器位置 5 保存 4,位置 6 保存 3,程序就会得到 7。
三项小型 CPU 状态
要跟踪指令周期,需要观察:
- 程序计数器(PC):下一条待取指令的地址;
- 指令寄存器(IR):当前已经取出的指令;
- 累加器(ACC):这个教学 CPU 使用的工作寄存器。
寄存器是 CPU 内部的存储位置。它们远少于主存储器中的位置,但可以直接供处理器电路使用。
取指、译码、执行
CPU 不断重复一个指令周期。
1. 取指
PC 提供地址,存储器返回该地址中的指令,CPU 再把它复制到 IR。通常,PC 随后前进到下一个地址。
IR ← Memory[PC]
PC ← PC + 1这里的箭头表示“左侧目标接收右侧数值的副本”,并不是数学等式。
2. 译码
控制逻辑检查 IR 中的指令,识别操作类型与操作数信息。例如,对 ADD 6 译码后,CPU 知道要取得地址 6 中的值,并使用加法电路。
3. 执行
CPU 完成操作。执行可能会:
- 改变寄存器;
- 读取或写入存储器;
- 与 I/O 设备传送数据;
- 改变 PC;
- 使处理器停机。
执行结束后,CPU 通常会取出当前 PC 指向的指令。
实验 1——亲自成为时钟
发条 CPU 会公开 PC、IR、ACC、存储器、输出和轨迹。你可以逐个微阶段前进,也可以一次执行完整指令,并比较发生了什么变化。
按下控制按钮前先预测下一项变化。取指时应关注 IR 和 PC;执行 ADD 时应关注 ACC;执行 STORE 时应关注某个存储位置。
一次完整轨迹
当 Memory[5] = 4、Memory[6] = 3 时,各条指令的效果如下:
| 已执行指令 | 取指后的 PC | 执行后的 ACC | Memory[7] | 输出 |
|---|---|---|---|---|
LOAD 5 | 1 | 4 | 0 | — |
ADD 6 | 2 | 7 | 0 | — |
STORE 7 | 3 | 7 | 7 | — |
OUT | 4 | 7 | 7 | 7 |
HALT | 5 | 7 | 7 | 7 |
请注意三个细节:
1. PC 在取指阶段前进,早于该指令的执行阶段。 2. STORE 改变存储器,但不必清空累加器。 3. HALT 改变控制状态:处理器不再开始新周期。
精确行为取决于处理器设计。这里的轨迹是为了学习顺序而刻意简化的模型。
指令是控制后续动作的数据
存储程序与其他比特模式一样占据存储空间。CPU 把取出的模式当作指令,是因为该模式通过取指过程到达,而且其中的比特字段符合架构定义的编码。
这不表示所有数据值都应被执行,而是表示硬件可以从存储介质读取程序表示。程序位置与控制流共同决定哪些位置会被当作指令取出。
顺序非常重要:
LOAD X → ADD X → ADD ONE → STORE OUT → OUTPUT这段程序计算并显示 2x + 1。如果 OUTPUT 发生在 STORE OUT 之前,显示器可能得到旧值。即使单条指令完全相同,不同顺序也可能产生不同结果。
实验 2——修补存储程序
编辑五个指令槽,使程序计算 2x + 1。每次修改后,实验都会测试多个输入,并显示当前输入的完整执行轨迹。
只对 x = 0 正确并不算完成。使用全部测试区分正确规则与偶然结果,然后交换两条指令并解释失败原因。
检查点
存储程序模型把存储与控制连接起来:
1. 指令经过编码,保存在存储地址中; 2. PC 标识下一条指令; 3. 取指把指令复制到 IR,并通常使 PC 前进; 4. 译码确定所需操作; 5. 执行改变数据、状态、I/O 或控制流; 6. 这个周期不断重复,直到控制流要求停止或跳转。
到目前为止,图中的箭头表示抽象传送。下一节将研究实现这些传送的共享电气通道——总线。