Appearance
题目
在采用"取指、译码/取数、执行、访存、写回"5 段流水线的 RISC 处理器中,执行如下指令序列(第一列为指令序号),其中 s0、s1、s2、s3 和 t2 表示寄存器编号。
I1 add s2, s1, s0 // R[s2] ← R[s1] + R[s0]
I2 load s3, 0(s2) // R[s3] ← M[R[s2] + 0]
I3 beq t2, s3, L1 // if R[t2] = R[s3] jump to L1
I4 addi t2, t2, 20 // R[t2] ← R[t2] + 20
I5 L1:若采用转发(旁路)技术处理数据冒险,采用硬件阻塞方式处理控制冒险,则在 I1~I4 执行过程中,发生流水线阻塞的指令有( )。
错因
A
只看到了 I2→I3 的 load-use 冒险(要让 I3 阻塞),漏掉了 I3 是分支指令带来的控制冒险对 I4 的影响。题目说"采用硬件阻塞处理控制冒险",所以分支没解决之前 I4 不能继续往前跑,I4 也要阻塞。
B
把 I2 当成阻塞主体,是误以为"装载指令本身要等访存"——但 I2 自己作为流水线第一条 load,只要前面 EX→EX 转发解决了 s2 的依赖(I1 算出 s2 后通过转发给 I2 用),I2 就能正常推进,不阻塞。阻塞发生在用 load 结果的下一条指令,不是 load 自己。
D
I2 多算了一次(同 B 的错),I3、I4 选对。这是把"涉及数据冒险的两条指令都阻塞"误当成判定。实际上转发已经处理了 EX-to-EX 的数据冒险(I1→I2),只有 load-use(I2→I3)需要插入气泡。
总解析
第一步:识别每条指令的"产出"和"需要"
| 指令 | 写哪个寄存器(WB 时刻) | 读哪些寄存器(ID 时刻) |
|---|---|---|
I1: add s2, s1, s0 | s2 | s1, s0 |
I2: load s3, 0(s2) | s3 | s2 |
I3: beq t2, s3, L1 | — | t2, s3 |
I4: addi t2, t2, 20 | t2 | t2 |
第二步:逐对检查依赖
① I1 → I2(s2 的依赖):
- I1 在 EX 阶段就算出 s2 的新值。
- I2 在 EX 阶段需要 s2 用于地址计算。
- 用 EX-to-EX 转发(I1 的 EX 输出 → I2 的 EX 输入)即可——无需阻塞 ✓
② I2 → I3(s3 的依赖,load-use):
- I2 是 load 指令,s3 在 MEM 结束才有效(不是 EX 结束)。
- I3 在 EX 阶段需要 s3 做比较。
- 即使有转发,I2 的 MEM 输出最早在 I3 的 EX 输入"同一周期才到"——做不到同周期转发,必须阻塞 1 个周期插入气泡。
这就是经典的 load-use 冒险——是转发也救不了的硬冒险,必须阻塞。
③ I3 → I4(控制冒险):
- I3 是分支指令
beq,要等到 EX(甚至 MEM)才能算出条件、决定 PC。 - 在分支决议之前,I4 不能确定要不要执行(如果跳转 L1,I4 就不该跑)。
- 题目规定"硬件阻塞方式处理控制冒险"——分支没解决前 I4 必须阻塞等待。
第三步:汇总
| 指令 | 是否阻塞 | 原因 |
|---|---|---|
| I1 | 否 | 无前置依赖 |
| I2 | 否 | EX-to-EX 转发解决了 s2 的依赖 |
| I3 | 是 | load-use 冒险(s3 来自 I2 的 load) |
| I4 | 是 | I3 是分支,控制冒险,硬件阻塞 |
最终答案是 C(仅 I3、I4)。
关键判定口诀:
- EX-to-EX 转发能救 ALU → ALU 的依赖,所以普通 add 后的 add 不阻塞;
- load-use 必阻塞 1 周期(转发救不了,因为 load 结果到 MEM 末尾才出来);
- 硬件阻塞处理控制冒险时,分支后的下一条指令都要阻塞到分支决议为止。