五级 RISC 流水线与三类冒险
更新时间:2026-09-12。本文是「集成电路大类 · 芯片设计」**进阶层(intermediate)**数字逻辑第一百零六篇。微架构入门给了流水线和「气泡」的直觉,RISC-V分清了 ISA 与微架构;这一篇把教科书经典的五级 RISC(MIPS/RV32 同款)讲透,重点是为什么会「冒险」、硬件又如何用转发、停顿、预测把流水线喂满。
本文要回答的问题
- 一条指令在 IF/ID/EXE/MEM/WB 五级各做什么,级间寄存器存什么?
- 结构、数据、控制三类冒险分别冲突在哪个资源/时刻?
- forwarding(旁路)能救哪些依赖,为什么 load 后面紧跟用它的指令仍要插气泡?
- 分支/跳转没算出目标前,已经取进来的指令怎么办?
- 气泡、转发、预测对 CPI 和吞吐各是什么代价?
一、五级与流水线寄存器
| 级 | 名称 | 做的事 |
|---|---|---|
| IF | 取指 Instruction Fetch | 按 PC 从指令存储器读指令,PC+4 |
| ID | 译码 Decode | 解析操作码、从寄存器堆读源操作数 |
| EXE | 执行 | ALU 算结果/有效地址/比较分支 |
| MEM | 访存 Memory | load/store 读写数据存储器 |
| WB | 写回 Write Back | 把结果写回寄存器堆 |
级与级之间是一组流水线寄存器(IF/ID、ID/EXE、EXE/MEM、MEM/WB),把指令、PC、控制信号和中间结果往下一级带。理想情况下 5 条指令在五级中重叠执行,每个时钟周期完成一条(CPI→1),而时钟周期只取决于最慢的一级(这也是切短组合路径提频的典型)。
二、结构冒险:争用同一个硬件
两条指令同一拍需要同一资源,如单端口存储器同时被 IF 取指和 MEM 访存。常见解法是哈佛结构(指令/数据缓存分开)或寄存器堆「前半拍写、后半拍读」;实在解不开就停顿一拍。现代核用分离 L1 I-cache/D-cache 基本消除结构冒险。
三、数据冒险与转发(最常考)
后一条指令的操作数依赖前一条还没写回的结果(RAW 真相关)。例如:
add x1,x2,x3 # x1 在第 5 级 WB 才写回
sub x4,x1,x5 # sub 在第 2 级 ID 就要读 x1 —— 时间差 3 拍其实 add 的结果在 EXE 结束就已算出,不必等 WB。旁路/转发 forwarding:把 EXE/MEM、MEM/WB 流水线寄存器里的新结果直接「抄近路」送回 ALU 输入端,比较目的寄存器号与源寄存器号决定从哪一级转发,绝大多数 RAW 因此无停顿。
唯一转发也救不了的是 load-use 冒险:
lw x1,0(x2) # 数据要到 MEM 级结束才拿到
add x4,x1,x5 # 紧接着的 EXE 就需要 —— 来不及硬件检测到「上一条是 load 且下一条用其结果」时,插入一个气泡(stall 一拍 + 之后转发):冻结 PC 和 IF/ID、向流水线塞一个空操作。编译器也能通过指令调度在 load 槽插一条不相关指令掩盖延迟。
四、控制冒险:分支改变 PC
跳转/分支的目标地址和条件往往要到 EXE(或更后)才确定,但后面几条指令已经按顺序取进流水线,若跳转成立这些就是错的。处理手段由简到繁:
- 冲刷 flush:确定分支后把已取的错误指令作废(清成气泡),代价是分支延迟槽;
- 假设不发生(predict not-taken):继续顺序取,真发生再冲刷;
- 提前到 ID 级判断并算目标,把损失从多拍降到 1 拍;
- 静态/动态分支预测 + BTB:用历史猜方向、用分支目标缓冲猜地址,猜中零气泡、猜错再冲刷回收(更深流水和乱序核依赖它,见 robust FSM/控制中的异常冲刷思想)。
控制信号也要随指令在流水线寄存器里一起传播(寄存器号、是否写回、ALU 源选择),否则下游级不知道该做什么——这是数据冒险之外的「控制传递」。
五、性能与小结
理想 CPI=1,每条分支误预测、每个 load-use 气泡都增加周期;IPC≈1/(1+各类停顿率)。设计就是在「加转发/预测省气泡」与「比较器/预测器的面积、建立时间」间取舍。五级是理解一切超标量/乱序核的底座:再深的流水、再多发射,处理的仍是这三类冒险。
与现有篇目的衔接
总结
一句话总结:经典 RISC 把一条指令分成 IF 取指、ID 译码并读寄存器、EXE 用 ALU 运算或算地址、MEM 访存、WB 写回五级,级间用 IF/ID、ID/EXE、EXE/MEM、MEM/WB 流水线寄存器携带指令和控制信号,使五条指令重叠、理想每拍完成一条(CPI 趋近 1),时钟周期只取决于最慢一级;并行后产生三类冒险——结构冒险是同时争用单口存储等资源,用分离指令/数据缓存和寄存器堆分半拍读写消除;数据冒险是后指令要用前指令尚未写回的结果(RAW),多数可由旁路转发把 EXE/MEM 或 MEM/WB 里的新结果直接送回 ALU 输入而零停顿,但 load 之后紧跟使用其结果的 load-use 情形数据要到 MEM 末才有,必须冻结前段插入一个气泡再转发,编译器也可调度无关指令填充延迟槽;控制冒险源于分支跳转目标和条件未定时已顺序取入错误指令,用冲刷、假设不发生、把比较提前到 ID 级以及动态分支预测加 BTB 来减少误预测气泡;实际 IPC 由各类停顿率决定,五级模型是理解更深流水、多发射和乱序核如何处理同样三类冒险的基础。