“转发解决已经算出但尚未写回的值,互锁处理还没算出的值,分支清除处理根本不该执行的指令。这三个机制的对象不同。”
形式陈述
在本文五级模型中,RAW(read after write)依赖要求消费者读取程序顺序中最近一次较老写者的结果。若ID读寄存器时该写者尚未WB,寄存器旧值就不能直接作为EX操作数。转发从已经产生结果的流水寄存器旁路取值。
对EX需要的一个源寄存器
- 若
,值恒为零 - 若EX/MEM有效、RegWrite为真、rd非零且rd=rs,它是最近候选;非load时取它的ALU结果,load时该来源尚未就绪,必须由互锁保证消费者此时不进入EX
- 否则,若MEM/WB有效、RegWrite为真、rd非零且rd=rs,取最终写回值
- 否则取ID/EX保存的寄存器读值
较近候选若尚未就绪,不能退回较远候选的旧写值。比较还必须使用解码后的真实源使用标记:立即数字段恰好等于某个寄存器号,不代表一次寄存器读取。
直觉
值的产生时刻和写回寄存器的时刻不相同。add在EX末已算好,下一周期的消费者可直接取EX/MEM中的结果,无需等待WB。load的EX结果只是地址,真正数据要等MEM末,不能使用同一条旁路规则冒充已经到货。
转发选择的是“这次读应该来自谁”,不是“哪里碰巧有同名寄存器”。两条较老指令都写同一个rd时,最近的那条会覆盖更早的值,所以必须优先。
例子与边界
两个写者的优先级
执行 addi x5,x0,1; addi x5,x0,2; add x6,x5,x5。第三条在EX时,第二条在MEM、第一条在WB。两个候选都匹配x5,正确来源是EX/MEM的2,结果x6=4。若MEM/WB优先,会取1并得到2。
若把第二条改为 lw x5,0(x1),就不能因为这个较近候选还未得到数据,转而使用第一条的1。必须先停顿,等load完成后取load值。
store也有要转发的数据
执行 add x3,x2,x6; sw x3,4(x1)。本文store在EX读取base和data,第二条EX时应把第一条EX/MEM的结果接到store-data输入,再随EX/MEM送往下一周期的MEM写端口。只对ALU地址输入加旁路、漏掉store数据路径,会把旧x3写入正确地址。
x0与伪依赖
addi x0,x0,9; add x7,x0,x0 的结果必须是x7=0。若只比较rd=rs而不排除rd=0,会把被丢弃的9转发成18。另一种伪依赖来自S格式没有rd、部分I格式没有rs2:它们的相应位是立即数,必须由解码屏蔽。
推论与应用
load-use互锁和转发是一套共同协议:互锁保证EX不会要求尚未可用的最近写值,转发保证可用时选到它。单独证明其中一个选择器的优先级正确,还不能证明流水线无数据错误。
在本页顺序发射、顺序读源、顺序写回且固定阶段的模型中,WAR和WAW不会产生需要另行重命名来修复的时序冒险。加入乱序、变延迟或提前写回后,该结论需重审;编译器的寄存器分配又是另一层的名称映射问题。
参考资料
- UC Berkeley CS61C,Data Hazards,课程笔记,访问于 2026-10-08,转发与写后读寄存器文件。
- David A. Patterson and John L. Hennessy, Computer Organization and Design RISC-V Edition, 2nd ed., 2021,Ch. 4。本文双写者与store-data反例按明示的EX消费时机推导。