目录
一、最原始的指令处理理解
31~26 25~21 20~16 15~11 ---
二、流水线
三、冒险问题
1.解决数据冒险
(从流水线寄存器通过旁路传回数据 也叫作 转发)
若产生冒险,则更前面的指令中 需要写入的目的寄存器刚好是 当前指令需要读取的源寄存器,此时对于当前源寄存器而言,这个寄存器内部的数据并不是准确的,因为它需要用到前一个指令产生的结果。此时可以用到旁路,解决这个问题。
观察指令可知:
R型指令,需要用到rs,rt源寄存器,写入rd目的寄存器
beq指令,需要用到rs,rt源寄存器,不需要写入
sw指令,需要用到rs,rt源寄存器,不需要写入
lw指令,需要用到rs源寄存器,写入rt目的寄存器
可知,beq和sw指令 不会对后面指令产生影响,因为它们仅仅使用该寄存器值,并不修改该寄存器值,后面指令访问该源寄存器的值依然是准确的。
会产生冒险的只有R型-X 或 lw-X。
旁路能解决的是 R型-X 或者 Lw-X(X不用到lw的目的寄存器)-R型
不能直接通过旁路而需要流水线阻塞的是 取数-使用型数据冒险
两种旁路,第一种答案可从EX/MEM中取得,第二种答案可从MEM/WB中取得。
情况:
一个指令的两个源寄存器分别是上两个指令的目的寄存器。如:
add $s0,$t0,$t1
add $t1,$t2,$t0
add $s1,$s0,$t1
这时,当第三条add到ALU输入时,第二条已经ALU运算完了,结果在EX/MEM里可以取出,第一条已经到MEM/WB里,可以取出
一个指令的一个源寄存器是上两个指令的目的寄存器。如:
add $t1,$t0,$t1
add $t1,$t2,$t0
add $s1,$s0,$t1
这时第二条已经ALU运算完了,结果在EX/MEM里可以取出,第一条已经到MEM/WB里,可以取出,但是第三条需要的t1是最新的t1也就是第二条指令里面的t1,所以它需要从最新的EX/MEM中取出。阻断MEM/WB的。
一个指令的一个源寄存器是上一个指令的目的寄存器或者是上上一个指令的目的寄存器。
旁路解决:在ALU输入处加选择门的选择端(多选器)
必须为写入指令才会影响下面的指令才需要旁路,所以RegWrite=1
①EX/MEM转发条件:(即应当从EX/MEM流水线寄存器通过旁路得到结果)
(1)当上一条指令为写入时,写指令应当为1
EX/MEM.RegWrite= 1
(2)当上一条指令的目的寄存器与当前源寄存器相同时
ID/EX.RS(RT)=EX/MEM.Rd (这里的Rd一定为15~11的目的寄存器,因为对于lw指令的rt目的寄存器是没办法在这里进行旁路的,在这里进行旁路是无效的,如果这里的下一条指令的RS为lw的Rt时,实际上已经产生了阻塞(这在后面我们会知道),加入了空指令,也就是说这时的EX/MEM.Rd为空,也就是在EX/MEM阶段不可能要取的是lw的rt,如果要取那么产生阻塞,也是在MEM/WB中取,当且仅当这个EX/MEM.Rd是15~11的R型目的寄存器才有效,所以无论如何不会出现ID/EX.RS=lw.Rt 然后取出ALU运算后的的地址的问题。)
(3)当上一条指令的目的寄存器不是zero寄存器时
EX/MEM.Rd≠$zero(为什么上一条指令的目的寄存器不能为$zero?其实并不是$zero的值被修改了,实际上没被修改。上一条指令的ALU的计算结果放在了EX/MEM流水线寄存器组里面,这个时候的结果并没有传给$zero,而此时目的寄存器实实在在确实是$zero,也就是说,它会误认为现在EX/MEM流水线寄存器组里面的值 就是需要的结果值,也就是ID/EX.RS=EX/MEM.Rd成立,所以会把结果值拿出来,但实际上$zero还是那个zero。它只判断 目的寄存器是否为源寄存器,然后把流水线的值转发给ALU,这个流水线的值 实际上并不是目的寄存器的现有值。)
②MEM/WB转发条件:
(1)MEM/WB.RegWrite=1
(2)ID/EX.RS(RT)=MEM/WB.Rd(Rt)
(3)MEM/WB.Rd≠$zero
2.取数-使用型数据冒险
流水线阻塞:lw指令的目的寄存器rt,只有当它经过 数据存储器取出数据之后,才是正确的,也就是说,对于lw型指令,它如果能用到旁路,正确的数据也仅仅会在MEM/WB流水线寄存器组里面。如果有一条指令如下:
lw $t0,200($s0)
add $t1,$t0,$s1
lw指令的目的寄存器,刚好就是下一条指令的源寄存器,根据流水线的实现原理可知,当lw执行到取出数据存储器的值 放入MEX/WB流水线寄存器组中时,add指令不得不执行到EX/MEM了,也就是它已经算完了,这显然不正确。换个思路,当add指令到需要输入ALU计算时,lw才执行到MEM阶段,还未取出值,这显然是不行的。
所以此时不得不产生流水线阻塞。
产生气泡:由于指令在ID阶段译码成功,则在ID/EX流水线寄存器里就已经知道了,该条指令是什么指令,如果此时是一条lw指令,并且前一条在IF/ID指令的源寄存器用到了lw指令中的rt那么必然会阻塞,所以可以在这里判断。
判断什么?
IF(ID/EX.MemRead=1 and //唯一的寄存器写入操作 lw
(ID/EX.Rt=IF/ID.Rs or ID/EX.Rt=IF/ID.Rt) //rt 与 后一条的源寄存器重合
PS:这里仍然会有一点不必要的气泡,但是不必要的气泡一定是没错的,只是并不必要而已,比如 若连续出现了lw指令,且后一个lw不用到前一个lw写入的目的寄存器,且同时写入同一个寄存器,这样也会插入一个气泡,因为前后rt相同,但显然并不需要气泡。还有一个就是jump指令,jump指令的rs和rt无效,所以并不需要气泡。(不考虑)