Appearance
题目
在采用"取指、译码/取数、执行、访存、写回"5 段流水线的 RISC 处理器中,下列关于指令流水线数据冒险处理的叙述中,错误的是( )。
错因
A
A 在描述"相邻指令操作数相关 → 数据冒险"。这是 RAW 冒险的标准定义:前一条指令写某寄存器、紧接着后一条指令读同一寄存器,就可能引发冒险("可能"很关键——是否真冒险还要看流水线是否能转发)。A 叙述正确,不选 A。
B
B 在描述"插入气泡(stall)能避免冒险"。这是处理数据冒险最朴素的硬件方式——让后续指令在 ID 阶段等若干周期,等到前一条指令的 WB 完成再读寄存器。B 叙述正确,不选 B。
D
D 在描述"通过调整指令顺序 + 插 nop 解决"。这是软件层面(编译器)的处理方式——对 load-use 这种转发也救不了的冒险,编译器可以调度其他无关指令填进 load 后的位置,或不得已时插入 nop 让流水线"自然 stall"。D 叙述正确,不选 D。
总解析
题目问"错误的"叙述,C 是错的。
C 错在哪里:
"所有数据冒险都可以通过加入转发(旁路)电路解决"——load-use 冒险即便有转发也救不了。
反例:load-use 冒险
I1: load R2, 0(R1) # R2 ← M[R1]
I2: add R5, R2, R3 # 用 R2 算 R5I1 的 R2 值要到第 4 周期 MEM 阶段末尾才从内存读出来;I2 的 EX 阶段需要 R2 在第 4 周期开始时就到位——同一周期内,转发也来不及("未来值不能转发到当前")。
所以 load-use 必须 stall 一个周期:让 I2 的 EX 推后一个周期,让 I1 的 MEM 在第 4 周期末出结果,第 5 周期再转发给 I2 的 EX。
逐项核验:
| 选项 | 内容 | 对/错 | 理由 |
|---|---|---|---|
| A | 操作数相关可能引起冒险 | ✓ | RAW 标准定义 |
| B | 插入气泡能避免冒险 | ✓ | 最朴素的硬件方式 |
| C | 所有数据冒险都能转发解决 | ✗ | load-use 冒险必须 stall,转发救不了 |
| D | 调整指令顺序 + 插 nop 解决 | ✓ | 软件层面(编译器调度)的标准做法 |
最终答案是 C。
冒险处理方式三层次速记:
| 层次 | 手段 | 能解决什么 |
|---|---|---|
| 硬件转发 | EX→EX、MEM→EX 旁路 | 多数 ALU→ALU 类的 RAW(不需要 stall) |
| 硬件气泡 | ID 阶段等待 | load-use 等转发救不了的 RAW(必须 stall) |
| 软件调度 | 调整指令顺序 + 必要时插 nop | 编译期已知的所有 RAW(用其它独立指令填空) |
关键判断:转发不是万能的——只能"提前送已有的值",不能"送一个还没出现的值"。load-use 里 R2 还在内存路上,转发本质上"等不到"它。