共通 - 多周期 v.s.流水线
同一条指令通常要经过取指、译码、执行、访存和写回等步骤。多周期 CPU 和流水线 CPU 的主要区别,是这些步骤如何在时间上安排。
一条指令的执行阶段
后文使用以下缩写:
| 阶段 | 名称 | 主要工作 |
|---|---|---|
| IF | 取指(Instruction Fetch) | 根据 PC 从内存读取指令 |
| ID | 译码(Instruction Decode) | 识别指令,读取源寄存器,生成控制信号 |
| EXE | 执行(Execute) | 进行运算、计算访存地址或判断分支 |
| MEM | 访存(Memory Access) | 按需读取或写入内存、外设 |
| WB | 写回(Write Back) | 按需将结果写入目的寄存器 |
PC(程序计数器)保存取指地址。对于本实验中的 32 位指令,顺序执行时下一条指令位于当前地址加 4 的位置。当分支成立时,PC 改为分支目标地址。
注意
并不是每条指令都需要完成表中的全部工作。例如 addi 不需要访存,sw 不需要写回。
多周期 CPU
多周期 CPU 在一条指令完成后,才开始执行下一条指令。控制器用状态机安排各阶段的工作,中间结果保存在寄存器中,供后面的状态使用。
例如,执行 addi 时可以依次经历 IF → ID → EXE → WB,再回到 IF。取指需要等待总线应答,因此 IF 状态可能停留多个时钟周期。
注意
“多周期”不意味着每条指令都恰好使用相同的周期数。
由于不同指令不重叠执行,不需要处理流水线中指令之间的冲突,但仍然需要处理总线请求的等待。
流水线 CPU
流水线 CPU 让不同指令同时处于不同阶段。例如一条指令正在执行时,下一条可以进行译码,再下一条可以取指。相邻阶段之间使用流水线寄存器,保存每条指令的数据和控制信号。
假设各阶段都只需一个周期即可完成,三条普通运算指令的推进过程可以示意如下。实际运行时,访存等待和冲突会使某些阶段暂停。
| 周期 | IF | ID | EXE | MEM | WB |
|---|---|---|---|---|---|
| 1 | A | — | — | — | — |
| 2 | B | A | — | — | — |
| 3 | C | B | A | — | — |
| 4 | — | C | B | A | — |
| 5 | — | — | C | B | A |
重叠执行带来三类需要处理的冲突(暂时不完全理解也没关系):
- 结构冲突:IF 和 MEM 同时请求同一条总线,需要仲裁访问顺序。
- 数据冲突:后一条指令要读的寄存器,前一条还没有写回,需要等待结果。
- 控制冲突:分支改变了执行方向,已经取入的后续指令可能需要作废。
本次实验允许通过暂停和插入气泡处理冲突,不需要为提高性能实现数据旁路。具体方法见气泡和冲刷流水线。
多周期 CPU 和流水线 CPU 的区别
多周期由于不需要处理冲突,控制逻辑会更加简单,因此相比流水线会更好实现。而流水线 CPU 在同一时刻会有多条指令在 CPU 中执行,因此效率更高。
注意
流水线并不保证单条指令执行得更快,其实际性能还要取决于访存等待和暂停次数。
在本次实验中,同学们可以根据自己的情况,以及之后分组大实验的开发计划来决定在这个实验中选择实现多周期 CPU 还是实现流水线 CPU。
总结
两种 CPU 都要完成指令规定的操作,也都需要用寄存器保存跨周期的信息。多周期通过状态机依次完成一条指令,流水线通过阶段之间的寄存器让多条指令重叠执行,并处理随之产生的冲突。
学完这节内容我需要做什么?
分清“一个阶段”和“一个时钟周期”,理解两种 CPU 实现的控制方式,接下来选择其中一种并阅读对应的设计说明。