浅谈指令流水线
??指令流水線作為計算機(jī)組成原理中一個重要的組成部分,弄清指令流水線的操作步驟對制作CPU有著很大的幫助。本文主要講述下指令流水線的相關(guān)知識。
??指令流水線是較單周期指令和多周期指令更有效率的一種方式。指令流水線并沒有減少每條指令執(zhí)行的時間,反而可能增加一定的時間(原因下文解釋),指令流水線改變的是指令的吞吐量,從而加快指令的執(zhí)行速率。對于指令流水線的介紹可分為三部分:數(shù)據(jù)通路設(shè)計、控制器設(shè)計、冒險處理。本文先以緒論為引導(dǎo),再以三部分為主線,介紹指令流水線。
??注:作者學(xué)習(xí)的教材是《計算機(jī)組成與系統(tǒng)結(jié)構(gòu)(第二版)》由袁春風(fēng)老師主編,清華大學(xué)出版社出版。本書中設(shè)計的指令體系結(jié)構(gòu)是MIPS體系結(jié)構(gòu),本文所有內(nèi)容都是圍繞MIPS體系結(jié)構(gòu)進(jìn)行介紹。
緒論:
?? 先介紹一下什么是流水線?
??在此舉一個簡單的例子(一看就懂):洗衣服分為三步:洗、烘干、疊。三部分都有獨(dú)立的三臺機(jī)器進(jìn)行操作。所謂流水線就是當(dāng)?shù)谝慌路赐赀M(jìn)入烘干時,可以放第二批衣服進(jìn)入洗的環(huán)節(jié)。因為各個步驟相互獨(dú)立,不會有影響。等到第一批衣服進(jìn)入疊的環(huán)節(jié)時,第二批衣服進(jìn)入烘干,第三批衣服進(jìn)入洗的環(huán)節(jié)。確保了在絕大多數(shù)時間,三臺機(jī)器在同時工作。提高了吞吐率,這個方法就叫做流水線。流水線的名字因三個環(huán)節(jié)叫做三級流水線。
??再介紹一下為什么指令可適用于流水線?
??在執(zhí)行指令的過程中,可將過程拆分為5個步驟:取指、譯碼、取操作數(shù)、計算、寫回。這五個步驟分別可有不同的部件進(jìn)行實施,滿足了流水線的基本要求。既然流水線可提高指令的執(zhí)行速度,那何樂而不為呢?
??再解釋一下為什么指令流水線可能增加每條指令的執(zhí)行時間?
??在流水線設(shè)計中,為達(dá)到吞吐量提高的目的,在同一時間段不同部件執(zhí)行不同的命令。這對部件執(zhí)行的規(guī)整性提出了要求,所謂規(guī)整性就是任何部件完成任何一個操作的時間都是相同的。類似于短板效應(yīng)的原理,每個階段的時間都要選擇最長的,加起來單條指令的時間就多了起來。
數(shù)據(jù)通路設(shè)計:
??根據(jù)單周期指令的執(zhí)行和MIPS體系結(jié)構(gòu)的特點(diǎn)可知,在所有指令中l(wèi)oad指令包含5個步驟,使用時間最長。故以其為基礎(chǔ)設(shè)計五級流水線。
??五級流水線數(shù)據(jù)通路基本框架如下圖(以下將對此圖進(jìn)行介紹,以理解數(shù)據(jù)通路的設(shè)計):
??對該圖做一個基本的介紹:上文說過五級流水線分為五個步驟,體現(xiàn)在圖中即為IF、ID、EX、Mem、Wr五階段。在每兩個階段之間有長方形(例如IF/ID,ID/EX等)代表流水段寄存器,記錄上階段傳入下階段的一些數(shù)據(jù)和控制信息。本圖中控制信息使用虛直線代表。為了更好的執(zhí)行指令,使用時鐘周期的方式統(tǒng)一控制,寫操作發(fā)生在時鐘周期的下降沿。
??下面分別對五個階段進(jìn)行介紹:
1)Ifetch(IF)段:
??IF段主要內(nèi)容是進(jìn)行取指操作,集成在取指令部件IUnit進(jìn)行。指令取出后需要改變PC的值,順序情況下PC直接加4或者加1(取決于是以字節(jié)編址還是以字編址,以字編址加1,以字節(jié)編址加4。因為一個字等于四個字節(jié))。但在branch和jump指令中PC需要跳轉(zhuǎn),則PC加4后直接改變?yōu)樘D(zhuǎn)的地址。
??如上圖,進(jìn)入PC的地址由一多路選擇器決定來源,正常指令使用PC+4,branch或jump使用額外指定地址。
??解釋IUnit通往IF/ID流水段寄存器兩條線的意義:第一條線保存的是PC+4的地址,第二條線保存的是取出的指令。
??在此解釋一下保存PC+4的原因:在大部分情況中,beq指令用于函數(shù)調(diào)用,函數(shù)調(diào)用過后需要返回原來跳轉(zhuǎn)的位置繼續(xù)執(zhí)行下面的內(nèi)容,所以提前保存PC+4的值,保證跳轉(zhuǎn)后還能回到原位置。
1) Reg/Dec(ID)段:
??ID段進(jìn)行指令的譯碼,以及取操作數(shù)。根據(jù)不同類型的指令(R型,I型,J型)對指令進(jìn)行切分,從不同的位置得到操作數(shù)地址。這里的操作數(shù)地址理解為操作數(shù)所在寄存器的編號。
??如上圖,RFile代表寄存器組,在MIPS體系結(jié)構(gòu)中共32個寄存器。所以Rs、Rt、Rd都是用5位表示(2^5 = 32)
??解釋各條線的含義:譯碼結(jié)束后,已知指令類型操作數(shù)位置。PC+4原因與IF段相同,imm16用于I型指令時,Rs和Rt(上面的),用于R型指令,從寄存器組中取出兩個源操作數(shù),目的地址Rd需要等指令執(zhí)行完之后采用,先傳入下一流水段寄存器。Rt(下面的)代表I型指令的目的寄存器,指令執(zhí)行完采用,所以傳入下一流水段寄存器。Rw、Di、WE是Wr階段才用到,后面進(jìn)行解釋。
??目前為止,ID/EX流水段寄存器中包含操作數(shù),并且已知操作類型,下一步可進(jìn)行指定操作的計算。
3) Exec(EX)段:
??本段主要進(jìn)行運(yùn)算,不同指令操作數(shù)的源地址不同。
??如上圖,Exec Unit是運(yùn)算執(zhí)行部件。不單單是一個加法器(因為會支持or/and等操作)
??依次解釋各線的內(nèi)容:(Exec Unit左,從上到下)PC+4內(nèi)容與IF段相同。無論I型還是R型busA的來源是相同的(指令中的rs),但對于busB來說I型來源是imm16經(jīng)過擴(kuò)展和的結(jié)果,R型是之前的rt地址中取出的操作數(shù),這個會通過多選器進(jìn)行選擇。RegDst是經(jīng)過ALU計算后的目的地址,R型指令送到rd中,I型指令送到rt中,二者選其一放入Ex/Mem流水段寄存器。(Exec Unit右,從上到下)PC+4依舊,第二根是zero信號用于判斷是否進(jìn)行branch跳轉(zhuǎn)。第三根是16位立即數(shù),無論是branch跳轉(zhuǎn)還是jump都需要對16位立即數(shù)做修改得到跳轉(zhuǎn)地址。第四根代表ALU運(yùn)算的結(jié)果。busB引出一根進(jìn)入Ex/Mem流水段寄存器是為了在sw指令中,busB輸出的是需要存入存儲器的數(shù)據(jù)。
4)Mem段
??關(guān)于Mem段是訪問存儲器,在MIPS中,只有sw/sh/sb/lw/lh/lb兩類指令可以訪問存儲器,在其他類型指令中是不需要有這步的。為了規(guī)整性設(shè)計,在數(shù)據(jù)通路設(shè)計中也設(shè)計了旁路。
??解釋一下各線的內(nèi)容:Data Mem左側(cè)第一根線是寫回的PC+4。下面一根Zero代表是否為零,在beq指令中,只有zero信號有效(為1)才會跳轉(zhuǎn)。Overflow代表是否溢出,只有在有符號操作中才涉及是否溢出。例如在加法指令中可能存在操作數(shù)結(jié)果溢出的情況。這時需要引發(fā)異常。(對于異常的介紹不在本文的范圍內(nèi))RA/WA即為ALU運(yùn)算的結(jié)果,作為讀/寫存儲器的讀地址/寫地址。Di是sw指令中待存的數(shù)據(jù),Do是lw指令中待取的數(shù)據(jù),與RA/WA相同,直接進(jìn)入Mem/Wr流水段寄存器的線代表當(dāng)指令是非訪存存儲器類型的時,ALU計算出的結(jié)果就是要寫到寄存器中的內(nèi)容。下面兩個流水段寄存器之間的直達(dá)線代表的是上一階段選出的目的地址。
5) Wr段
??Wr段就是將得到的結(jié)果寫回寄存器,兩根線分別對應(yīng)于寫回的數(shù)據(jù)訪存存儲器獲得還是通過ALU運(yùn)算獲得。
控制器設(shè)計:
??帶控制器通路圖如下所示:
??流水線的控制器設(shè)計類似于單周期。控制信號的來源是指令,所以只有在指令譯碼后才會產(chǎn)生控制信號,也即在流水線前兩個階段的時候是沒有控制信號的,所有指令是通用的。控制信號在流水段寄存器中相互傳遞,并在合適的階段作用于數(shù)據(jù)通路的各個模塊,大多是各個模塊的多選器。可以簡化控制信號的傳遞過程如下圖:
??其中控制信號的全拼可在我另一篇博客中查看(網(wǎng)址為:https://blog.csdn.net/gls_nuaa/article/details/106200686)link
??在此對各個控制信號的作用地點(diǎn)做一下說明:
??ExtOp:當(dāng)指令為I型/J型時,需要進(jìn)行擴(kuò)展。擴(kuò)展分為兩類,分別是符號擴(kuò)展和零擴(kuò)展。在R型指令中不需要擴(kuò)展。MIPS指令中只有andi/ori/xori需要進(jìn)行零擴(kuò)展,其余I型指令都是符擴(kuò)展。
??ALUSrc:代表ALU操作數(shù)運(yùn)算的來源,在R型指令中操作數(shù)來源于寄存器,I型指令中操作數(shù)來源于擴(kuò)展器
??ALUOp:代表ALU運(yùn)算的種類(加法/減法/異或/。。。),是ALU-control模塊的輸出。同時ALU-control模塊的來源是指令,對于I型指令來說是opcode(31:26),對于R型指令來說需要結(jié)合funcode(5:0)。
??RegDst:代表結(jié)果存儲的位置,R型指令存于rd,I型指令存于rt。
??R-type:是否是R型指令,關(guān)乎到ALUOp等控制信號是否取決于后六位指令。
??MemWr:是否需要向存儲器中寫,在所有指令中只有sw類指令該控制信號有效(為1),其余都是無效(為0)
??Branch:Branch是由beq類信號決定,并結(jié)合zero信號,最終決定下個pc的數(shù)值。
??MemToReg:該信號是決定寫回寄存器數(shù)值的來源,來源分為兩種:由ALU計算得到或者從存儲器取得。
??RegWr:該信號決定是否向寄存器中寫,需要寫的時候為1,不需要寫的時候為0。
??值得注意的是在有些指令中,部分控制信號不起到作用(選哪個都可以)。這類控制信號可記為x,但在是否寫例如MemWr/ RegWr這種是必須嚴(yán)格決定是0或是1的。
冒險(沖突/Hazard)處理:
??如果說單周期相較于流水線有優(yōu)勢的地方可能就是單周期不需要關(guān)注是否有冒險產(chǎn)生的問題。冒險可以理解為前后指令執(zhí)行過程中與流水線結(jié)構(gòu)所產(chǎn)生的沖突。大致分為三類:結(jié)構(gòu)冒險、數(shù)據(jù)冒險、控制冒險。
1. 結(jié)構(gòu)冒險:
??所謂結(jié)構(gòu)冒險就是在同一時間,不同指令處于不同階段,但對寄存器/存儲器又讀又寫。(寄存器一臉懵:我到底該干嘛?)。如下圖所示:
??解決辦法:
???簡單的想法是:只要保證不在同一時間讀寫即可。可以通過優(yōu)化指令的執(zhí)行順序,讓指令之間在不同階段讀寫即可。但沒有一萬,也有萬一。萬一無法做出這樣的優(yōu)化怎么辦呢?所以我們需要一種更為通用的辦法:將讀寫端口分開,在寄存器/存儲器設(shè)計中加入時鐘,在時鐘下降沿(前半周期)寫數(shù)據(jù),上升沿(后半周期)讀數(shù)據(jù)。這樣就完美解決了二者之間的矛盾。而且一定要注意是先寫后讀。同時對于存儲器中劃分指令存儲器和數(shù)據(jù)存儲器也是為了解決結(jié)構(gòu)冒險的問題。
2. 數(shù)據(jù)冒險
??下面舉一個例子引入數(shù)據(jù)冒險:
???add $t1 $t2 $t3
???sub $t4 $t1 $t2
??觀察這個例子,把2和3中相加,結(jié)果放到1中。再用1減去2,結(jié)果放在4中。根據(jù)我們對于流水線的簡單了解可以知道,指令只有在完成五個階段后(即Wr階段)才可以被寫回使用,但sub指令執(zhí)行的時候,在Reg/Dec(第二個階段)就需要讀出1的值。在流水線中相鄰指令相差的是一個階段。如果不做處理,那么sub讀出的就會是1的舊值。那計算不就是錯了嘛!
??問題總會有解決的辦法,在MIPS中解決的辦法就是使用轉(zhuǎn)發(fā)/forwarding(也叫旁路/bypass)。轉(zhuǎn)發(fā)就是改動數(shù)據(jù)通路,提前將結(jié)果拿出做操作。
??轉(zhuǎn)發(fā)的原理如下:看上面的例子,在add指令執(zhí)行過程中在Exe階段就已經(jīng)算出1的新值。如果這個時候把結(jié)果移到Reg/Dec階段去執(zhí)行sub指令。剛好滿足階段相差1的流水線特性。由于要實現(xiàn)過程轉(zhuǎn)發(fā),就需要從硬件上解決問題。
??那么接下來的問題就轉(zhuǎn)移為了:什么時候使用轉(zhuǎn)發(fā),并且如何控制轉(zhuǎn)發(fā)?
??如下圖所示,可能有兩種情況需要轉(zhuǎn)發(fā)(圖中的兩個add指令,分別是下面一條指令/兩條指令發(fā)生數(shù)據(jù)冒險),這個時候我們就可以使用轉(zhuǎn)發(fā)。但不同的需要階段決定了轉(zhuǎn)發(fā)的階段也不同。在綠色中ALU以后需要轉(zhuǎn)發(fā),在紅色中可以ALU隔一個階段再轉(zhuǎn)發(fā)。
??以上解釋了轉(zhuǎn)發(fā)的兩種可能性,那么數(shù)據(jù)通路的設(shè)計就需要適應(yīng)兩種可能性。當(dāng)檢測到滿足轉(zhuǎn)發(fā)條件的時候,通過以上兩條通路進(jìn)行轉(zhuǎn)發(fā)。
??那么問題來了:**轉(zhuǎn)發(fā)條件是什么呢?**下面一起來看看吧,對以上圖做以下標(biāo)注(C1(a)/C1(b)/C2(a)/C2(b))。如下圖所示:
??對于轉(zhuǎn)發(fā)檢測,可簡單做以下規(guī)定:
??如果本條指令源操作數(shù)和只上條指令的目的寄存器一樣,而與其他指令間無冒險關(guān)系,則有以下公式:
??如果本條指令源操作數(shù)和上條指令的目的寄存器一樣,且和上上條指令也有數(shù)據(jù)冒險沖突,則不轉(zhuǎn)發(fā)上上條指令的結(jié)果,那么有如下公式:
??但轉(zhuǎn)發(fā)是萬能的嗎?看看下面這個例子:
??轉(zhuǎn)發(fā)的克星:load-use冒險
???lw r3,100(r1)
???or r6,r3,r1
?? lw的目的寄存器是or操作的操作數(shù)寄存器。但lw指令中r3只有在mem階段(第四階段)之后才會得到。但or指令第二階段就需要。由前面的轉(zhuǎn)發(fā)可知轉(zhuǎn)發(fā)最多提前一個階段,所以在這種情況中光靠轉(zhuǎn)發(fā)是不行的。這種情況就叫Load-use冒險。解決的方法就是中間再引入一個阻塞(no
operation/bubble)來延遲一個周期再使用轉(zhuǎn)發(fā)。
3. 控制冒險
??所謂控制冒險是指在beq/jump等跳轉(zhuǎn)指令或異常指令中,在第四階段才能夠知道是否跳轉(zhuǎn)。不跳的話一切正常,跳的話后兩條指令都已經(jīng)被讀入。需要被清空以執(zhí)行跳轉(zhuǎn)后的指令。最簡單的方法是直接清空前面的流水段寄存器。(但是簡單的方法必定沒有效率)。由于控制冒險只發(fā)生在轉(zhuǎn)移指令中,所以可以通過分支預(yù)測的方法來判斷下一條指令是否是跳轉(zhuǎn)指令。如果符合跳轉(zhuǎn)指令則按照直接清空的方法處理。既然是預(yù)測,那么結(jié)果就會有兩種:預(yù)測正確和預(yù)測錯誤。
??分支預(yù)測分為兩種:靜態(tài)分支預(yù)測(也叫簡單分支預(yù)測)和動態(tài)分支預(yù)測。
??靜態(tài)分支預(yù)測:類似于轉(zhuǎn)發(fā)的思想,講分支指令提前。在譯碼階段的時候就直接判斷是否轉(zhuǎn)移,原因是此時已經(jīng)知道指令是什么并結(jié)合運(yùn)算可決定是否轉(zhuǎn)移。這樣降低了損失(3條指令減低為1條指令)。
??動態(tài)分支預(yù)測:記錄預(yù)測的結(jié)果,并結(jié)合之前的記錄和當(dāng)前的指令預(yù)測結(jié)果改變預(yù)測下條指令的結(jié)果(類似于算法中動態(tài)規(guī)劃的填表思想,當(dāng)前的操作受之前的結(jié)果影響)。
??動態(tài)分支預(yù)測可分為一位和兩位(當(dāng)然還有多位,不過課本上只介紹了比較簡單的)
??一位分支預(yù)測是假定開始預(yù)測指令不轉(zhuǎn)移,如果預(yù)測正確則下條指令繼續(xù)保持該預(yù)測,若預(yù)測錯誤則下條指令預(yù)測相反的結(jié)果。對于預(yù)測失敗的指令,則從失敗的下條指令開始預(yù)測相反結(jié)果。狀態(tài)轉(zhuǎn)移圖如下:
??兩位分支預(yù)測是給兩次機(jī)會。錯了一次(第一次)先不變狀態(tài),看再下一次(第二次)的情況,如果第二次仍是錯的那么就改變狀態(tài),第二次是對的就恢復(fù)第一次的狀態(tài)。狀態(tài)轉(zhuǎn)移圖如下:
??在現(xiàn)實應(yīng)用中,動態(tài)預(yù)測精度更高,得到更廣闊的應(yīng)用。但不同處理器可能使用不同的動態(tài)預(yù)測方法(預(yù)測位數(shù)不同,如Pentium 4使用4位預(yù)測位)
??流水線的學(xué)習(xí)就告一段落,但學(xué)到的大多都是理想情況。關(guān)于各種特殊情況的處理,希望在以后的學(xué)習(xí)過程中慢慢完善。
ACKNOWLEAGEMENTS:
The author would like to thank Prof. Xiangping Bryce Zhai,a teacher of computer composition principles, for his careful teaching and Prof. Chunfeng Yuan for compiling excellent textbooks to enhance understanding.
本文作者水平有限,如有不足之處,請在下方評論區(qū)指正,謝謝!
新人創(chuàng)作打卡挑戰(zhàn)賽發(fā)博客就能抽獎!定制產(chǎn)品紅包拿不停!總結(jié)