1. 什么是數(shù)據(jù)冒險(xiǎn)
當(dāng)前指令需要使用之前指令的運(yùn)算結(jié)果,但是結(jié)果還沒(méi)有寫(xiě)回。
2. 數(shù)據(jù)冒險(xiǎn)舉例
比如下圖中,第2條add指令需要使用第1條sub指令的結(jié)果,第1條sub指令在900ps時(shí)才會(huì)將運(yùn)算結(jié)果$t0寫(xiě)回寄存器堆,而第2條add指令在500ps時(shí)就需要從寄存器堆中讀取sub指令的結(jié)果了。這里就造成了數(shù)據(jù)冒險(xiǎn)。

3. 數(shù)據(jù)冒險(xiǎn)的解決
3.1 解決方法一:人為的延后add指令-軟件延后
如下圖所示,軟件加入2條nop指令,add指令在第4拍進(jìn)入流水線(xiàn),當(dāng)add指令需要讀取寄存器的值時(shí),sub指令剛好已經(jīng)完成了寄存器的寫(xiě)。

軟件插入nop指令解決數(shù)據(jù)冒險(xiǎn)的方法并不好,因?yàn)檐浖恢缿?yīng)該插入幾條nop指令。上面這樣插入2條nop指令可以解決5級(jí)流水線(xiàn)的數(shù)據(jù)冒險(xiǎn)問(wèn)題,那么如果軟件移植到8級(jí)流水就不可行了。
3.2 解決方法二:人為的延后add指令-硬件延后
對(duì)于5級(jí)流水,可能插入2個(gè)nop指令就可以解決這個(gè)數(shù)據(jù)冒險(xiǎn),但是如果移植程序到一個(gè)新型CPU上執(zhí)行,該新型CPU采用8級(jí)流水,這個(gè)方法可能就不行了。一般對(duì)于軟件來(lái)說(shuō),都要屏蔽硬件的實(shí)現(xiàn)細(xì)節(jié),因此這里考慮第二種解決數(shù)據(jù)冒險(xiǎn)的方法,硬件插入bubble,如下圖所示,

這里有個(gè)問(wèn)題需要解決,就是硬件如何知道哪里會(huì)產(chǎn)生數(shù)據(jù)冒險(xiǎn)和應(yīng)該插入幾個(gè)bubble?
流水線(xiàn)每個(gè)階段都在為不同的指令服務(wù),那么檢查ID階段也就是寄存器的讀口上的地址,再檢查后面各個(gè)階段的硬件操作的寄存器是否和寄存器的讀地址相等,就可以判斷數(shù)據(jù)冒險(xiǎn)。
這種解決方法不好,因?yàn)檫@樣降低了流水線(xiàn)的效率。
3.3 解決方法三:數(shù)據(jù)前遞Forwarding
如下圖所示,sub指令實(shí)際上在600ps時(shí)就已經(jīng)通過(guò)ALU計(jì)算出了t0的值,不需要等sub完成寫(xiě)回,就可以將ALU的計(jì)算結(jié)果傳遞給下一條指令,即add指令,這就是數(shù)據(jù)前遞。如下圖所示,600ps時(shí),sub指令計(jì)算結(jié)果存放在了EX/MEM流水線(xiàn)寄存器中,將這個(gè)結(jié)果傳遞給ALU的輸入端完成數(shù)據(jù)前遞。

從硬件實(shí)現(xiàn)上來(lái)看,如下圖所示,直接將ALU經(jīng)過(guò)流水線(xiàn)寄存器的輸出接回ALU的輸入,完成數(shù)據(jù)前遞。ALU的輸入端需要加2選1多路器,控制信號(hào)是數(shù)據(jù)冒險(xiǎn)的檢測(cè)結(jié)果。

再后一級(jí)的流水線(xiàn)寄存器的輸出也可以做成數(shù)據(jù)前遞,如下圖所示,

上面紫色線(xiàn)所指示的前遞可以解決下面這個(gè)數(shù)據(jù)冒險(xiǎn)的例子,第三條指令是一個(gè)and指令,當(dāng)這個(gè)and指令A(yù)LU需要使用t0的值時(shí),t0剛好完成MEM階段操作,在MEM/WB流水線(xiàn)寄存器中。

我們注意,再往后的指令,例如instruction 3如果也要使用t0,ID階段讀取寄存器時(shí),sub指令剛好已經(jīng)完成了WB,就不存在數(shù)據(jù)冒險(xiǎn)了。
因此,對(duì)于運(yùn)算指令,綠色和紫色的前遞已經(jīng)可以解決數(shù)據(jù)冒險(xiǎn)的問(wèn)題。但是還有一種例外情況。
3.4 load-use冒險(xiǎn)
上面對(duì)于運(yùn)算指令的數(shù)據(jù)冒險(xiǎn)已經(jīng)可以通過(guò)前遞或者說(shuō)旁路bypass解決。但是下面這個(gè)例子,使用前遞就無(wú)法解決,

第4條指令是一個(gè)load指令,lw指令使用t0寄存器,這部分已經(jīng)不存在數(shù)據(jù)冒險(xiǎn),因?yàn)閘w指令在ID階段讀取t0寄存器時(shí),sub指令剛好已經(jīng)完成了WB。
注意這條lw指令在A(yíng)LU階段計(jì)算出了要訪(fǎng)問(wèn)數(shù)據(jù)存儲(chǔ)器的地址,接著在1400ps時(shí)完成了數(shù)據(jù)存儲(chǔ)器的讀取,但是lw指令的下一條指令or指令最晚在1200ps就需要t1的數(shù)據(jù),這樣無(wú)論如何都不能將1400ps才可以產(chǎn)生的數(shù)據(jù)前遞至1200ps使用。
解決方法就是那個(gè)最簡(jiǎn)單又最笨的辦法,插入bubble。如下圖所示,流水線(xiàn)stall一個(gè)周期后,再使用紫色的數(shù)據(jù)前遞就可以解決這個(gè)load-use冒險(xiǎn)。
