RISC-V CPU数据冒险疑问:or与sw指令间的数据冲突如何处理
首先纠正你的理解误区:数据冒险的核心是指令间的寄存器依赖关系,而非仅局限于相邻流水线寄存器(ID/EX、EX/MEM、MEM/WB)之间。你提到的“仅发生在这些阶段之间”,特指需要通过旁路(转发)单元处理的冒险场景——也就是前面指令在EX或MEM阶段已计算出结果,但尚未写回寄存器堆,后续指令在EX阶段需要直接使用该结果的情况。而你遇到的场景,是后续指令在ID阶段读取寄存器,此时前面的写操作处于WB阶段,属于另一种RAW(写后读)冒险的处理范畴。
针对你的指令序列的流水线时序分析
假设使用经典5级流水线(IF/ID/EX/MEM/WB),各指令的阶段时序如下:
| 时钟周期 | or x4, x3, x4 | bne x4, x4, error | addi x2, x0, 1060 | sw x4, 0(x2) |
|---|---|---|---|---|
| 1 | IF | - | - | - |
| 2 | ID(读x3/x4) | IF | - | - |
| 3 | EX(执行或运算) | ID(读x4/x4) | IF | - |
| 4 | MEM(无操作) | EX(比较,不跳转) | ID(读x0) | IF |
| 5 | WB(写x4到寄存器堆) | MEM(无操作) | EX(执行加立即数) | ID(读x4/x2) |
| 6 | - | WB(无写操作) | MEM(无操作) | EX(计算地址) |
| 7 | - | - | WB(写x2) | MEM(写内存) |
| 8 | - | - | - | WB(无操作) |
可以看到:sw指令在周期5的ID阶段读取x4,而or指令在周期5的WB阶段完成对x4的写回。此时的关键是寄存器堆的设计是否支持同周期写后读(RAW)。
这类数据冒险的处理方式
寄存器堆内置写后读支持
现代RISC-V实现的寄存器堆几乎都支持:在同一个时钟周期内,写端口的数据可以直接转发到读端口。也就是说,or在周期5 WB阶段写入x4的新值,能直接被同周期ID阶段的sw读取到,无需额外处理,不会产生错误。若寄存器堆不支持同周期写后读
这种情况极少,但如果存在,需要让sw指令停顿一个周期:将sw的ID阶段延迟到周期6,此时or已经完成WB,寄存器堆中的x4已更新,sw就能读到正确的新值。
补充说明:你的bne指令为何无影响
bne x4, x4, error在周期3的ID阶段读取x4时,or还处于EX阶段(尚未计算出新x4),但该指令比较的是同一个寄存器的旧值,无论新旧结果都是相等的,因此不跳转的判断不会出错,也不会干扰后续流水线。
内容的提问来源于stack exchange,提问作者Hypernova

