現(xiàn)代CPU的流水線一般包括取指、譯碼、發(fā)射、執(zhí)行、訪存、寫回、提交這幾級pipeline。本文主要介紹最后一級流水:提交(commit)。
1. 指令提交
高性能CPU是亂序(out of order)執(zhí)行指令的,為了保持程序執(zhí)行結果的串行性,在流水線中增加最后一級,稱為提交(Commit)階段。當指令到達提交階段,會將指令在重排序緩存(Re-order buffer, ROB)中標記為已完成的(complete)狀態(tài),需要注意,complete狀態(tài)只表示這條指令已經(jīng)計算完畢,并不表示它可以離開流水線。
流水線中所有的指令都按照進入流水線的順序在ROB中進行了記錄,只有ROB中最舊的那條指令變?yōu)橐淹瓿傻臓顟B(tài)時,這條指令才允許離開流水線,并使用它的結果對處理器的狀態(tài)進行更新,此時稱這條指令退休(retire)了。
因此,一條指令可能要在提交階段等待一段時間,才可以退休,ROB負責完成這個功能,它是提交階段最重要的部件。指令到達提交階段并不代表這條指令一定正確,由于分支預測失敗(mis-prediction) 和異常(exception)等原因,一條處于已完成狀態(tài)的指令可能會從流水線中被抹掉,只有在這條指令之前進入到流水線中的所有指令都已經(jīng)retire了,并且這條指令處于已完成狀態(tài)時,它才可以退休而離開流水線,這樣使程序在處理器中執(zhí)行時有串行的效果 (亂序執(zhí)行,順序提交)。
當指令沒有退休時,它的狀態(tài)都是推測的(speculative),只有當指令真正退休而離開流水線時,才可以將它的結果更新到處理器的狀態(tài)中,這樣即使有分支預測失敗或者異常,也不會將錯誤的狀態(tài)暴露給程序員。流水線的分發(fā)(Dispatch)階段是處理器從順序執(zhí)行(in order)到亂序執(zhí)行(out-of-order)的分界點,流水線的提交階段又將處理器從亂序狀態(tài)拉回到順序狀態(tài)。從處理器外部看,它總是按照程序中指定的順序執(zhí)行,任何預測技術所產(chǎn)生的錯誤,在處理器內(nèi)部都會解決掉。因此,異常處理是提交階段的一個重要任務。
對于一個N-way的超標量處理器,流水線的提交階段每周期至少需要將N條指令退休,才能保證流水線不會被堵塞。
2. ROB介紹
ROB的結構
在提交階段,將亂序執(zhí)行的指令變回程序中指定的順序狀態(tài),主要通過Reorder Buffer, 即ROB來實現(xiàn),ROB本質(zhì)上是一個FIFO。ROB中存儲了指令的相關信息,例如這條指令的類型、結果、目的寄存器和異常的類型等,如下圖。
Reorder Buffer
ROB的entry數(shù)決定了流水線中最多可以亂序執(zhí)行的指令個數(shù),即亂序窗口OoO(Out-of-Order) window的大小,上圖中每個ROB的表項包括的內(nèi)容如下。
Complete:表示一條指令是否已經(jīng)執(zhí)行完畢;
Areg:指令在原始程序中指定的目的寄存器,它以邏輯寄存器的形式給出;
Preg:指令的Areg經(jīng)過寄存器重命名之后,對應的物理奇存器的編號;
OPreg:指令的Areg被重命名為新的Preg之前,對應的舊的Preg,當指令發(fā)生異常而進行狀態(tài)恢復時,會使用到這個值;
PC:指令對應的PC值,當指令發(fā)生中斷或者異常時,需要保存這條指令的PC值,以便能夠重新執(zhí)行程序;
Exception:如果指令發(fā)生異常,會將異常類型寫到這里,當指令要退休的時候,會對這個異常進行處理;
Type:指令的類型。當指令退休的時候,不同類型的指令會有不同的動作,例如store指令要寫D-Cache、分支指令要釋放Checkpoint 資源等。
在分發(fā)(Dispatch)階段,指令會按照進入流水線的順序?qū)懙絉OB中,同時ROB中對應的complete狀態(tài)位會被置0,表示這些指令沒有執(zhí)行完畢。當某條指令執(zhí)行結束,它就變?yōu)閏omplete狀態(tài),此時會將 ROB中對應的complete狀態(tài)位置為 1,這條指令的計算結果可以放在ROB 中,也可以放在物理寄存器堆(PRF)中,取決于架構實現(xiàn)。指令在執(zhí)行過程中如果發(fā)生異常,也會將異常類型記錄在ROB中,異常的處理會統(tǒng)一放在提交階段。指令一旦在流水線的分發(fā)階段占據(jù)了ROB中的一個表項,這個表項的編號(ROB ID)會一直隨著這條指令在流水線中流動,這樣指令在之后的任何時刻,都可以在ROB中找到自己。
一條指令一旦變?yōu)镽OB中最舊的指令(上圖中,使用head pointer來指示最舊的指令),并且它的complete狀態(tài)位為1,并且這條指令在之前沒有發(fā)生過異常,則這條指令可以順利地離開流水線,它的結果可以對處理器的狀態(tài)進行更新;如果這條指令發(fā)生過異常,那么就要啟動異常的處理過程。
端口需求
對于一個4-way的超標量處理器來說,在ROB中每周期可以retire的指令個數(shù)應該是不小于4的。處理器從ROB中最舊的一些指令中(由head pointer 指定),選擇那些已經(jīng)變?yōu)閏omplete狀態(tài)的指令,使其從流水線中退休。例如圖 10.4給出的ROB,在最舊的指令中,有三條指令變?yōu)榱薱omplete狀態(tài),那么在一個周期內(nèi)就可以將這三條指令都退休。
要實現(xiàn)如圖10.4所示的功能,需要對ROB中最舊的指令開始(由head pointer指定)連續(xù)的四個complete信號進行判斷,如果某個complete信號為0,那么它后面的所有指令都不允許在本周期退休,這個功能可以用如圖10.5所示的電路實現(xiàn)。
對于一個4-way的超標量處理器來說,ROB至少需要支持4個讀端口,但是這遠遠不是ROB真正需要的端口個數(shù),流水線的其他階段還對ROB有端口需求,它和處理器所采用的架構有關系,如果處理器采用ROB進行寄存器重命名的方式,此時對ROB的端口需求是最多的,這些端口包括:
1. 在寄存器重命名階段,需要從ROB中讀取4條指令的源操作數(shù),因此需要ROB至少支持8個讀端口(假設每條指令有兩個源寄存器)。
2. 在分發(fā)階段,需要向ROB寫入4條指令,因此需要ROB支持4個寫端口。
3. 在寫回階段,需要向ROB寫入最少4條指令,之所以使用至少4個寫端口,是因為很多處理器的issue width要大于machine width,處理器會放置更多的FU來提高運算的并行度,這樣導致每周期運算得到的結果要大于4個。
4. 在提交階段,需要讀4條指令,因此需要4個讀端口。
所以,ROB需要支持12個讀端口和最少8個寫端口,這種多端口FIFO在面積和延遲方面很難進行優(yōu)化,這也是采用ROB進行寄存器重命名的架構所面臨的問題:ROB會成為處理器中最繁忙的部件,需要進行詳細設計和優(yōu)化。