日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

超标量处理器设计 姚永斌 第9章 指令执行 摘录

發布時間:2024/3/12 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 超标量处理器设计 姚永斌 第9章 指令执行 摘录 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

9.1?概述

執行階段負責指令的執行,在流水線的之前階段做了那么多的事情,就是為了將指令送到這個階段進行執行。在執行階段,接受指令的源操作數,對其進行規定的操作,例如加減法、訪問存儲器、判斷條件等,然后這個執行的結果會大于處理器的狀態進行更新,例如寫到物理寄存器堆,寫到存儲器中,或者從特定的地址取指令等,同時這個執行結果可以作為其他指令的源操作數(這就是旁路)。

一般RISC指令集都包括下面的操作類型:

(1)算術運算,例如加減法、乘除法、邏輯運算和移位運算等;

(2)訪問存儲器;

(3)控制程序流的操作,包括分支、跳轉、子程序調用、子程序返回等類型;

(4)特殊指令,用來實現一些特殊功能的指令,例如對于支持軟件處理TLB缺失的架構,訪問協處理器,存儲器隔離等。

不同類型的指令有著不同的復雜度,因此在FU中的執行時間也是不同的,這稱為不同的latency,并且在現代的處理器當中,為了獲得更大的并行度,一般都會同時使用幾個FU進行并行的運算。

每個FU都有不同的延遲時間,FU的個數決定了每周期最大并行執行的指令個數,也就是前文所說的issue?width。

FU在運算完成后,并不會使用它的結果馬上對處理器的狀態進行更新(Architecture state),例如它不會馬上將結果寫到邏輯寄存器中,而是將結果寫到臨時的地方,例如寫到物理寄存器中,這些狀態稱為推測狀態(speculative state),等到一條指令順利離開流水線的時候,它才會真正地對處理器的狀態進行更新。

?其中,FPU用來對浮點數進行運算;ALU用來對整數進行算術和邏輯運算;AGU(Address Genneration Unit)用來計算訪問存儲器的地址,當使用虛擬存儲器時,AGU計算的地址是虛擬地址,還需要將其轉換為物理地址;BRU(Branch Unit)用來對控制程序流的指令計算目標地址。

執行階段另一個重要部分就是旁路網絡bypassing?network,它復雜將FU的運算結果馬上送到需要它的地方,例如物理寄存器堆,所有FU的輸入端,Store Buffer中等。在現代超標量處理器中,如果想要背靠背地執行相鄰的相關指令,旁路網絡是必須的,但是隨著每周期可以并行執行的指令個數的增多,旁路網絡變得越來越復雜,已經成為處理器中中制約速度提升的一個關鍵部分了。

假設先不考慮旁路網絡,那么指令的操作數可以來自于物理寄存器堆(對應非數據捕捉結構),或者來自于payload RAM(對應數據捕捉結構),那么此處仍需要考慮一個問題:每個FU和物理寄存器堆或payload RAM的各個讀端口應該怎樣對應起來?

其實,這是通過之前講過的仲裁電路關聯起來的,每個FU都和一個1-of-M的仲裁電路是一一對應的,每個仲裁電路如果選擇了一條指令,這條指令就會讀取物理寄存器堆或者payload,從而得到相應的操作數,然后就可以將這條指令送到相應的FU中執行了。

?圖中每個FU都有一個1-of-M的仲裁電路,這個仲裁電路對應著物理寄存器堆棧固定的讀端口。物理寄存器堆??偣残枰淖x端口的個數和issue?width是直接相關的,如果對處理器追求更大的并行度,就需要更大的issue?width,也就意味著物理寄存器堆棧需要更多的讀端口,這又會制約處理器速度的提高,因此現代的處理器為了解決這個矛盾,多采用cluster結構。

9.2 FU的類型

9.2.1 ALU

它負責對整數類型的數據進行計算,得到整數類型的結果,它一般被稱作ALU。整數的加減,邏輯,移位運算,甚至是簡單的乘除法,數據傳輸指令,例如mov指令和數據交換類型的指令,分支指令的目標地址計算,訪問存儲器的地址計算等,都會在這個FU中完成,具體的運算類型取決于處理器微架構的設計。

在ALU中加入乘除法操作后,會使ALU的執行時間是一個變化的值,例如執行簡單的加減法指令需要一個周期,而執行乘除法需要32個周期,這樣會給旁路的功能帶來一定的麻煩。

一個典型的ALU可能是:

?ALU中所有計算單元都會收到同一條指令的操作數,因此它們都會進行計算,最后需要根據這條指令的類型來選擇合適的結果,由于一條指令只需要一個計算單元計算,但是實際上所有的計算單元都進行了運算,這樣會浪費一部分功耗,可以在每個計算單元之前都加入寄存器來鎖定指令的操作數,根據指令的類型來選擇性更新這些操作數寄存器,可以節省一定功耗。

為了追求比較簡單的并行度,高性能的處理器都會選擇將乘法器單獨使用一個FU來實現,并且在這個FU中支持乘累加的功能,這樣可以快速執行指令集中乘累加類型的指令。有時候處理器出于功耗和成本的考慮,會將整數類型的乘法功能在浮點運算的FU中完成。這樣肯定會導致乘法指令需要的執行周期數變大,但是考慮到這種做法會節省面積,而且很多應用并不會使用太多的乘除法,所以也能接受。

9.2.2 AGU

AGU用來計算地址,訪問存儲器類型的指令(load/store)通常會在指令中攜帶它們想使用的存儲器地址,AGU負責對這些指令進行處理,計算出指令中所攜帶的地址。其實在普通流水線的處理器中,都是在ALU中計算這個地址,但是在超標量處理器中,由于需要并行地執行指令,而且訪問存儲器類型指令的執行效率直接影響了處理器的性能,所以單獨使用一個FU來計算它的地址。AGU的計算過程取決于指令集。

如果處理器支持虛擬尋址,那么經過AGU運算得到的地址就是虛擬地址,還需要經過TLB等部件轉換為物理地址,只有物理地址才可以直接訪問存儲器(在一般的處理器中,L2 Cache以及更下層的存儲器都是使用物理地址進行尋址的),因此在支持虛擬存儲器的處理器中,AGU只是完成了地址轉換的一小部分,它只是冰山下的一角,真正的重頭戲是從虛擬地址轉換為物理地址,以及從物理地址得到數據的過程。

9.2.3 BRU

BRU負責處理程序控制流control?flow類型的指令,如分支指令branch,跳轉指令jump,子程序調用和子程序返回等指令。這個FU負責將這些指令所攜帶的目的地址計算出來,并根據一定條件來決定是否使用這些定制,同時在這個FU中還會對分支預測正確與否進行檢查,一旦發現分支預測失敗了,就需要啟動相應的恢復機制。對于RISC處理器的PC寄存器來說,它的來源有三種:

(1)順序執行時,next_PC = PC + N, N等于每次取指令的字長;

(2)直接類型的跳轉,next_PC = PC +?offset, offset是指令所攜帶的立即數,它指定了相對于當前分支指令的的PC值的偏移量,由于這個立即數不會隨著程序的執行而改變,因此這種類型的指令的目標地址也是比較容易預測的。

(3)間接指令,指令中直接指定一個通用寄存器的值作為PC值,next_PC=GPR[Rs],這種類型的指令也被稱為絕對類型跳轉。由于隨著程序的執行,通用寄存器的值會變化,所以這種類型指令的目標地址不容易被預測,如果可以直接使用直接類型的跳轉指令實現同樣的功能,就盡量不要使用這種間接式類型的跳轉指令。

?BRU運算單元的實現原理圖,這個FU其主要完成了兩部分工作,即計算分支指令的目標地址,并判斷分支條件是否成立。

一般來說,分支指令可以分為兩種,一種是有條件的,一種是無條件的。

ARM和PowerPC等處理器則使用了不同的方法,在每條指令的編碼中都加入了條件碼,根據條件碼的值來決定指令是否執行。因為每條指令都有這個條件碼,所以每條指令其實都可以無條件執行,而不僅限于分支類型的指令,這樣相當于把程序中的控制相關性用數據相關性替代了。

對于每條指令都是用條件執行的好處是可以降低分支指令使用的頻率,而在超標量處理器中,只要使用分支指令,就有可能存在預測錯誤的風險,因此從這個角度看,這種條件執行的實現方式可以獲得更好的性能,但是它也是一把雙刃劍,因為條件碼占據了指令解碼的一部分,導致指令中實際可以分配給通用寄存器的部分變少了。

當需要條件執行的指令很多時,流水線會存在大量無效的指令,這樣反而使效率降低了,從這些角度來看,對每條指令都使用條件執行時降低了性能。

在超標量處理器中使用條件執行,會給寄存器重命名的過程帶來額外的麻煩。由于寄存器重命名階段無法得到需要的條件值,因此無法有選擇地對寄存器進行重命名,導致了問題發生。

超標量處理器如果實現條件執行,要解決寄存器重命名的問題,最簡單的方法就是停止流水線。等等到這條指令的條件被計算出來,才對它后面的指令進行重命名,雖然不會發生錯誤,但是效率的確不高。當然,也可以采用預測的方法來解決這個問題,而預測錯誤時,需要對處理器的狀態進行恢復,將其和之后的指令從流水線抹掉,并將這些指令對RAT的更改進行恢復,然后重新將這些指令取到流水線中。

條件執行的指令需要根據條件寄存器的值來決定是否執行,例如ARM處理器中的每個條件執行的指令都需要讀取條件寄存器CPSR。

對于每一條要修改CPSR寄存器的指令來說,CPSR寄存器都相當于這條指令的一個目的寄存器。在超標量處理器中,也需要到CPSR寄存器重命名,后續的條件執行指令都會將CPSR寄存器作為一個源寄存器來看待。不過,由于CPSR寄存器的寬度小于普通的通用寄存器,所以一般會為CPSR寄存器的重命名過程單獨使用一個物理寄存器堆。這就相當于在普通指令的增加了一個目的寄存器和一個源寄存器,使寄存器重命名的復雜度有所上升,導致了功耗增大。

無條件的分支指令總是將調整地址寫到PC中,用來進行子程序調用的CALL指令和子程序返回的Return指令。

對于實現了分支預測功能的超標量處理器來說,BRU功能單元還有一個更為重要的功能,那就是負責對分支預測是否正確進行檢查。在流水線取指令階段或解碼階段會將所有預測跳轉的分支指令按照程序中指定的順序保存到一個緩存中,這個緩存可以成為分支緩存branch?stack,在分支緩存中存儲了所有預測跳轉的分支指令。

檢查分支預測的正確性,會有四種結果:

(1)一條分支指令在BRU功能單元中得到的結果是發生跳轉,并且在分支緩存中找到了這條分支指令,跳轉地址也相同,證明分支預測正確。

(2)一條分支指令在BRU功能單元中得到的結果是發生跳轉,并且在分支緩存中沒有找到了這條分支指令,跳轉地址也相同,證明分支預測失敗。

(3)一條分支指令在BRU功能單元中得到的結果是不發生跳轉,并且在分支緩存中沒有找到了這條分支指令,跳轉地址也相同,證明分支預測正確。

(4)一條分支指令在BRU功能單元中得到的結果是不發生跳轉,并且在分支緩存中找到了這條分支指令,跳轉地址也相同,證明分支預測失敗。

如果發現分支指令預測失敗,那么就需要啟動處理器的狀態恢復流程。如果發現分支預測正確,并且這條分支指令之前所有的分支指令都已經正確執行,那么只需要釋放這條分支指令所占據的資源就哭了。

還需要考慮的一個問題是分支指令需要亂序執行嗎?

在流水線中可以同時存在多條分支指令,這些分支指令都會被進行分支預測,然后統一放到BRU功能單元對應的額發射隊列中,由于分支指令有如下兩個主要的源操作數。

(1)條件寄存器,分支指令根據條件寄存器的值來決定是否進行跳轉,條件寄存器的值在分支指令進入到發射隊列時可能還沒有被計算出來。

(2)源操作數,對于直接跳轉的分支指令,目標地址的計算來自于PC+offset,其中offset一般以立即數的形式存在于指令當中。

進入到發射隊列的所有分支指令,有可能出現的情況是后進入到流水線的分支指令的所有源操作數都已經準備好 ,可以進入BRU功能單元進行計算,而先進入的分支指令可能還未準備好。從這個方面來說,對分支指令采用亂序執行是可以提高一些性能,但是后進入到流水線的分支指令嚴重依賴前面的分支指令的結果,若前面的分支指令發現自己分支預測失敗了,后續這些分支指令在BRU中被執行的過程就是做了無用功,浪費了功耗。

綜合看起來,如果將性能放在第一位,那么可以使用亂序的方式來執行分支指令,而如果要顧及功耗,那么順序執行分支指令是一個明智的選擇。

9.2.4?其他FU

例如處理器如果支持浮點運算,那么就需要浮點運算的FU;很多處理器還支持多媒體擴展指令,則也需要相應的FU來處理。

9.3?旁路網絡

由于超標量處理器中的指令時亂序執行的,而且存在分支預測,所以這條指令的結果未必正確,此時稱這個結果時推測狀態,一條指令只有在順利地離開流水線的時候,才會被允許將它的結果對處理器進行更新,此時這條指令的狀態就變為了正確狀態(Architecture?state),此時可能距離這個結果被計算出來已經很久了,后續的指令不可能等到這條順利地離開流水線的時候才是用它的結果。

一條指令只有到了流水線的執行階段才真正需要操作數了,到了執行階段的末尾就可以得到它的結果,因此只需要從FU的輸出端到輸入端架起一個通路,就可以將FU的結果送到所有FU的輸入端。當然,在處理器內部的很多地方可能也需要這個結果,例如物理寄存器堆、payload RAM等,因此需要將FU的結果也送到這些地方,這些通路是由連線和多路選擇器組成的,通常被稱為旁路網絡,它是超標量處理器能夠在如此深的流水線情況下,可以背靠背執行相鄰的相關指令的關鍵技術。

為了降低處理器的周期時間的影響,源操作數從物理寄存器堆讀取出來后,還需要經過一個周期時間,才能夠到達FU的輸入端,這個周期在流水線中稱為Source Derive階段。同理,FU將一條指令的結果計算出來之后,還需要經過復雜的旁路網絡才能到達所有FU的輸入端,因此將這個階段頁單獨做成流水線。

?現在可以知道,要使兩條存在寫后讀相關性的指令背靠背的執行,必須有兩個條件,指令被仲裁電路選中的那個周期進行喚醒,還有就是旁路網絡。

一個周期內進行仲裁和喚醒操作會嚴重地制約處理器的周期時間,而現在又引入旁路網絡,需要將FU的結果送到每個可能需要的地方,在真實的處理器中,旁路網絡需要大量的布線和多路選擇器,已經成為現代處理器當中一個關鍵的部分,它影響了處理器的面積、功耗、關鍵路徑和物理上的布局。

在IBM的Power處理器中,兩條相鄰的相關指令在執行的時候,它們之間存在氣泡bubble,但是這些氣泡可以使用其他不相干的指令來代替,因為它們都是亂序執行的處理器,只要能夠找到不相干的指令,就能夠緩解這種設計對性能的負面影響。

9.3.1?簡單設計的旁路網絡

?在不實現旁路網絡中,FU的操作數直接來自于物理寄存器堆,FU的結果也直接送到物理寄存器堆中,一個FU要想使用另一個FU的計算結果只能通過物理寄存器獲得。

在實現旁路網絡中,每個FU的操作數可以有三個來源,即物理寄存器,自身FU的結果,其他FU的結果。同時,每個FU的輸出處理送到物理寄存器堆之外,還需要通過一個總線送到所有FU輸入端的多路選擇器中。

很多FU都有多個功能,也就是有多個計算單元。對這一的FU需要使用一個多路選擇器,從不同的計算單元中選擇出合適的結果輸送到旁路網絡上,這樣的設計成為bypass?sharing。但是,當一個FU,不同的計算單元需要的周期數不同時,例如乘法操作需要32個周期,邏輯運算需要1個周期,此時如果采用正常的執行,就可以出現同一個FU中兩個計算單元的結果在同一個周期內被計算出來,都想通過這個FU對應的旁路網絡進行傳送,這樣就產生了沖突。

?對于旁路網絡使用的沖突而言,最簡單的方法就是先假設不會存在沖突。

假設一個仲裁電路對應的FU可以計算三種類型的指令,在這個FU中就有三個計算單元,它們的latency分別是1,2和3,則:

(1)當本周期執行latency=3時指令,在下個周期不允許執行latency=2,在下下個周期不允許執行latency=1的指令;

(2)當本周期執行latency=2時指令,在下下周期不允許執行latency=1的指令;

(3)當本周期執行latency=1時指令,沒有限制;

為了達到這個功能,對每一個仲裁電路都設置一個位寬為兩位的控制寄存器,高位用來攔截所有latency=2的指令,低位用來攔截所有latency=1的指令,這兩個二位控制寄存器每周期都會邏輯右移移位。相應的,在發射隊列中每個表項中都增加兩個型號,分別用來指示它其中的指令的latency是1還是2。

9.3.2?復雜設計的旁路網絡

在比較復雜的流水線中,數據從物理寄存器中被讀取出來之后,需要經過一個周期Source Drive階段才可以到達FU的輸入端,而FU輸出的結果也需要經過一個周期Result Derive才能夠到達需要的地方,這些變化導致了旁路網絡也需要相應的變化。

在復雜的流水線中加入旁路網絡,使得設計的復雜度增大,對處理器的周期時間造成一定的負面影響。

在流水線的執行階段,其操作數除了來自于上一級流水線,還可以來自于兩個FU計算的結果,它們來自于流水線的Result Derive階段;而在流水線Source Drive階段,其操作數除了來自于上一級流水線,還可以來自于以前指令的結果,從之前的流水線示意圖可以看出,這些結果分布在流水線的Result Drive階段和Write?back結果。要實現這樣的旁路網絡,需要復雜的布線資源和多路選擇器的配合才可以完成。

在復雜流水線使用旁路網絡的過程,一條指令從FU中將結果計算出來,需要經過流水線的兩個階段Result Derive和Write Back,才可以寫到通用寄存器中,在這兩個周期內,這條指令的結果都可以輸出到旁路網絡上;而一條指令讀完通用寄存器后,直到真正到FU中執行之前的兩個階段Source Drive和Execute,都可以接收旁路網絡送出來的值。

當一條指令處于流水線的Data Read階段,需要讀取物理寄存器時,而產生它的操作數的指令此時正處于流水線的Write?back階段,此種情況下不需要使用旁路網絡,因為這條指令可以直接從物理寄存器中讀到所需要的操作數(假設物理寄存器時前半個周期寫,后半個周期讀)。

當兩條指令之間相隔的指令超過兩條時,就不在需要通過旁路網格獲得操作數了,而是可以直接通過寄存器獲得操作數,對于兩條相關性指令而言:

(1)當兩條相關的指令處于相鄰周期,這個旁路網絡只能發生在流水線的Execute和Result Derive兩個階段之間。

(2)當兩條相關指令之間相差一個周期時,一條指令使用它前面的前面那個周期的指令的結果,因此旁路網絡可能發生在流水線的Source Derive和Result Derive兩個階段之間,也可以發生在Execute和Write?back之間。

(3)當兩條相關指令之間間隔了兩個周期時,一條指令使用它前面的前面的前面的指令的結果,因此這旁路網格只可能發生在Source Derive和Write Back之間。

流水線增加了兩級,導致此時旁路網絡的復雜度已經很高,處理需要數量比較多的多路選擇器之外,還需要更多的更長的總線用來傳輸需要旁路的值。

現代處理器都追求很快的速度,很高的并行度,也就是需要更多的FU,因此不可能在任意的FU之間都設置旁路的路徑,這樣會需要大量的布線資源,造成嚴重的連線延遲。

實際上,并不需要在所有的FU之間都設置旁路網絡。例如,AGU進行地址計算的時候,一般都會使用ALU的計算結果,反之,ALU不會使用AGU的計算結果,因此在ALU和AGU之間的旁路網絡是單向的。

同時load/store單元,只有load指令的結果才會被其他指令所使用,store指令則不會有這種需求。

浮點單元一般都有自己專用的旁路網絡,整數指令不會直接使用浮點運算結果,因此浮點FU到整數FU的旁路網絡也并不需要。隨著流水線級數的增大,旁路網絡無法繼續跟隨下去,這也是無法任意妄為地增加流水線級數的一個原因。

9.4?操作數的選擇

在FU的輸入端,需要從物理接存起的輸出或者所有的旁路網絡中進行選擇,找到FU的真正需要的源操作數,這個任務是由多路選擇器來完成的,既然有那么多的源頭可供選擇,就需要對應的信號來控制這個多路選擇器,那么這個控制信號來自于何處?

所有的物理寄存器的這些信息可以保存在一個表格中,這個表格就是ScoreBoard,在這個表格中,記錄了一個物理寄存器在它的生命周期內經過的地方。

?在ScoreBoard中,對每個寄存器來說,記錄了兩個內容:

(1)FU#:這個寄存器會從哪個FU中被計算出來,當需要從旁路網絡中取得物理寄存器的值時,需要知道他來自于哪個FU,這樣可以控制多路選擇器來選擇對應的值,當一條指令被仲裁電路選中的時候,如果這條指令存在目的寄存器,就將這條指令在哪個FU中執行的信息寫到上面的表格中。

(2)R:表示這個物理寄存器的值已經從FU中計算出來,并且被寫到了物理寄存器堆中,后續的指令如果要是用這個物理寄存器,就可以直接從PRF中讀取,由于只需要表示這個寄存器是否在PRF中,使用一位的信號就可以了,0表示這個物理寄存器不再PRF中,需要從旁路網絡中取得這個值,1表示可以從PRF中讀取這個值。

某指令在流水線的Select階段,會將它在那個FU中執行的信息寫到SocreBoard表格的狀態為FU#中,在流水線的Write Back階段,會將計算的結果寫到物理寄存器堆中,同時會對ScoreBoard也進行更新,會讀取這個表格,就可以得到指令在那個FU中執行的信息了,也就可以從對應的旁路網絡中選擇合適的值。

由于讀取ScoreBoard的過程發生在流水線的Execute階段,這會對處理器的周期時間造成一定的負面影響,當處理器的頻率要求比較高時,這種做法可能就無法滿足要求。

每個FU在將一條指令的計算結果進行廣播的同時,也將這條指令的目的寄存器編號一并跟隨進行廣播。

在FU輸入端的多路選擇器旁邊增加了很多比較器,相應地也會增大一些面積和功耗,但是這樣設計結構簡單,不需要任何控制邏輯,在一定程度上也能減少設計的復雜度,進而減少面積和功耗。

隨著可以生產生旁路數據的流水段的增多,這些多路選擇器的輸入源也會隨之增阿基,這樣就在每個多路選擇器旁邊產生了大量的比較邏輯,這也是深流水線帶來的負面影響。

現代的處理器提高頻率的一大利器就是依靠工藝尺寸的縮小,同時處理器性能的提高主要依靠一些架構方面的預測算法。

9.5?cluster

當前的超標量設計方法導致硬件越來越復雜,例如需要更多端口的物理寄存器和存儲器、更多的旁路網絡等,這些多端口的部件會導致處理器的面積和功耗增加,嚴重地制約處理器頻率的提高。因此產生了一種設計理念-Cluster

之前在發射隊列中,通過將一個統一的發射隊列分開為多個獨立的發射隊列,能夠減少仲裁電路等部件的設計復雜度,并加快速度。在FU的設計中,將浮點FU和整數FU的旁路網絡分開,可以大大減少旁路網絡的復雜度等。

Cluster結構將上面這種理念進行擴展,應用到處理器內部的物理寄存器堆,發射隊列,旁路網絡等各種部件。

9.5.1 Cluster IQ

隨著每周期可以并行執行的指令個數的增多,對于采用集中式的發射隊列來說,要求更多的讀寫端口和更大的容量,導致面積和延遲都會增大,再加上發射隊列本來就處于處理器內部的關鍵路徑上,所以采用集中式發射隊列很難滿足現代處理器對性能的要求。通過將它分為多個曉得分布式發射隊列,每個發射隊列對應一個或少數幾個仲裁電路和FU,這樣每個分布式的發射隊列只需要存儲對應的FU中能夠執行的指令,使復雜度得以降低,這種Cluster IQ優點在于:

(1)可以減少每個分布式發射隊列的端口個數;

(2)每個分布式發射隊列的仲裁電路只需要從少量的指令中進行選擇,因此可以加快每個沖裁電路的速度。

(3)由于分布式發射隊列的容量比較小,它其中被喚醒的速度也會比較快。

、缺點則是:一個分布式發射隊列中,被仲裁電路選中的指令對其他發射隊列中隊列進行喚醒時,由于需要經過更長的主線,所以這部分的延遲會增大??缭讲煌腃luster之間進行喚醒的這個過程需要增加一級流水線,這樣當兩條存在相關性的相鄰指令恰巧屬于不同的Cluster時,它們就不能背靠背地執行了,而是在流水線中引入了一個bubble。

對每個采用cluster結構的發射隊列使用一個物理寄存器堆。這相當于將物理寄存器堆復制了一份,對于每個cluster內部的指令來說,都可以直接從物理寄存器堆中讀取操作數,每個FU都在自己所屬的cluster內使用旁路網絡,減少了旁路網絡的復雜度。當然,這樣的設計要求兩個物理寄存器堆的內容要保持一致,要求每個FU在更新自己的Cluster的寄存器的堆棧時,還需要更新另一個cluster內的寄存器,因此中的看來,這樣的設計可以將寄存器堆的讀端口個數減少一半,但是寄存器堆的寫端口個數并沒有減少。

通過減少寄存器堆的端口個數,避免了一個過于臃腫的寄存器堆成為處理器中的關鍵路徑。

物理寄存器cluster設計的缺點是很明顯的,為了減少復雜度,旁路網絡不能跨越cluster,當兩個存在相關性的連續的指令屬于兩個不同的cluster時,后續的指令要等到前面的指令更新完寄存器堆之后,才能夠從寄存器堆中讀取操作數。

9.5.2 Cluster?bypass

要提高處理器的性能,需要每周期可以并行執行更多的指令,也就需要更多的FU來支持,這會導致旁路網絡的復雜度也隨之顯著增加。

對旁路網絡使用cluster結構,將兩個FU分布在兩個Cluster中,每個FU不能將它的結果送到其他的FU中,只能送到自身的旁路網絡中,也就是說,旁路網絡只能分布在每個cluster內部。

當旁路網絡復雜度降低,流水線中的Source Drive和Result Drive兩個流水段都可以去掉了,這就相當于節省了兩級流水線,加快了跨越不同的cluster之間的相關指令執行的速度。如果相關指令屬于不同的cluster,則只能通過寄存器堆傳遞操作數,當流水線沒有了Source Drive和Result Drive階段后,兩條屬于不同的相關指令之間只需間隔一個周期就可以了。硬件很容易找到一條不相干的指令插入這個周期來執行,這樣簡化旁路網絡的同時,并沒有造成性能的明顯下降。

同時處理器其主頻的提高又要求周期時間越來越小,可能導致寄存器不能像之前那樣,在前半個周期寫入,在后半個周期讀取,這樣就導致相鄰的相關性指令需要間隔的周期數有所增加。

在順序執行的處理器中,一般都不會對旁路網絡采用激進cluster結構,而是盡量會采用完全的旁路網絡,以降低相鄰的相關性指令所引入的流水線氣泡。

目前講述的Cluster IQ的設計方法中,如果兩條相鄰的相關指令屬于兩個不同的發射隊列,則跨越發射隊列間的喚醒過程會引入一個周期的延遲,而兩條相關指令屬于兩個不同的cluster FU,則跨越FU之間的旁路網絡也需要一個周期的延遲,那么綜合來看,是不是兩個延遲會進行疊加呢?答案是可以的!

9.6?存儲器指令的加速

9.6.1?memory?disambiguation

在講述寄存器之間的相關性時,即RAW,WAW,WAR,都是可以在流水線解碼階段發現并解決的。而對于存儲器類型指令來說,訪問存儲器的地址是在執行過程動態地計算出來的,只有經過流水線的執行階段,才可以得到訪問存儲器的真正地址,例如:

ST R0 #15[R1]

LD R3 #10[R2]

在流水線的解碼階段是不可能知道這兩條訪問存儲器的指令不存在RAW相關性,只有過了流水線的執行階段,當這條指令的地址都被計算出來之后,才可以知道。

對于訪問存儲器的指令來說,是沒有辦法對這些地址也進行重命名來消除WAW和WAR這兩種相關性。

對于訪問存儲器的指令來說,這三種相關性WAW,WAR, RAW都是需要考慮的,雖然從理論上地說,訪問存儲器的指令之間也是可以亂序執行,但是一旦發現這三種相關性有任何一種發生了違例。就需要處理。這在一定程度上增加了設計難度,也為了保持存儲器的正確性,大部分處理器中的store指令都是順序執行的,這樣可以避免WAW相關性,load指令可以有不同的實現方式,主要分為三種:

(1)完全的順序執行,這是最保守的一種方法了;

(2)部分亂序執行,順序執行的store指令將程序劃分了不同的塊,每當一條store指令的地址被計算出來之后,這條store指令和后面的store指令之間所有的load指令可以亂序執行,這種方式避免WAR相關性的發生,同時也可以降低對RAW相關性的檢測難度。

(3)完全亂序執行,load指令不再受到store指令的限制,只要load指令準備好了,就可以送到FU中執行。WAR和RAW這兩種相關性都是需要在流水線中處理。

1.?完全的順序執行

由于load指令一般處于相關性的頂端,這種方法不能使load指令盡可能地提前執行,導致所有相關指令的執行都比較晚,使用這種方法的處理器,性能自然就比較低了,尤其在超標量處理器中,基本上不會采用這樣的保守的設計方法。

2.?部分的亂序執行

雖然store指令是in-order執行,但是出于兩條store指令之間的所有load指令卻可以亂序執行,當一條store指令被仲裁電路選中之后,位于它后面的所有load指令就有資格參與仲裁的過程。

這個方法的本質就是當一條store指令所攜帶的地址被計算出來之后,在它之后進入到流水線的所有load指令就可以具備條件去判斷RAW的相關性,每條load指令將它攜帶的地址計算出來之后,需要和前面store指令的攜帶地址進行比較。為了實現這個功能,就需要一個緩存來保存那些已經被沖裁電路選擇,但是還沒有順利地離開流水線的store指令,可以將這個緩存稱為store?buffer。

即使laod指令和store指令攜帶的地址相等,它們之間也沒有RAW相關性,load指令所需要的數據不應該來自于store指令,所以這需要一種機制。當load指令和store?buffer的store指令進行比較地址時,需要知道哪些store指令在自己前面,哪些store指令在自己后面,對于RAW而言,只需要關注哪些在自己前面的store指令即可,因此需要對這些load/store指令前后順序進行標記,標記的來源有如下幾種:

(a)PC值,但是當store指令之后有一個向前跳轉指令時,通過PC值就無法分辨真實的先后順序了。

(b)ROB的編號,在ROB中記錄著所有指令進入流水線的先后順序,因此指令在ROB的地址可以用來表示他們之間的先后順序。但是ROB中存儲的所有指令,其中只有一部分是load/store指令,如果使用這個標號,必然是很稀疏的,而這些標號要與大小比較,這樣造成比較器使用比較大的位寬,浪費了面積和功耗。

(c)在流水線的解碼階段,為每一條load/store指令分配一個編號,這個編號的寬度需要根據流水線中最多支持的load/store指令的個數來決定的。

不管怎么說,這樣部分地將load指令進行亂序執行的方法雖然可以提高一些性能,但是在很多時候仍然不能夠以最大的限度挖掘程序之間存在并行性。

3.完全的亂序執行

store指令仍然是in-order,但是load指令將不再受限于它前面的store指令,只要laod操作數準備好了,就可以向仲裁電路根據一定的原則,例如oldest-first原則,選擇一條合適load指令送到FU中執行。在這種方法中,可以使load/store指令共用一個發射隊列,采用每周期只選擇一條load/store指令的設計,也可以采用每周期同時選擇一條laod和一條store指令的設計。

還可以使load/store指令使用獨立的發射隊列,也就是說,load指令單獨使用發射隊列,store指令也單獨使用一個發射隊列,這樣store指令可以簡答地使用FIFO結構,不必使用年齡比較類型的仲裁電路。

對于RISC處理器來說,有數量豐富的通用寄存器,所以程序中很多變量可以直接放到寄存器中,這樣在實際的程序當中,store/load指令之間存在RAW相關性的情況并不是很多,而且,通過提前執行load指令,可以盡快地喚醒更多的相關指令。

反觀CISC處理器,因為可用的寄存器很少,所以經常需要與存儲器打交道,許多操作數都需要放到存儲器中,這樣store/load之間存在RAW相關性就很多了。

9.6.2?非阻塞Cache

I-Cache由于只需要讀取,而且取指令要求串行的順序,所以對他的處理是特殊的,不能簡答地采用非阻塞的方式。故本小節只介紹D-Cache。

在存儲器中,只有訪問存儲器的指令,例如load/store指令,才可以訪問D-Cache。

(1)對于laod指令來說,如果需要的數據不在D-Cache中,就發生了缺失,需要從下一級的物理存儲器中取得數據,并在D-Cache中按照某種算法,找到一個Cache?line進行寫入,如果被寫入的Cache?line是dirty,還需要將這個line的data?block先寫回到物理內存中。

(2)對于store指令來說,如果它攜帶的地址不在D-Cache中,那么對于write back+write?allocate類型的Cache來說,需要首先從物理內存中找到這個地址對應的datablock,將其讀取出來,和store指令所攜帶的數據進行合并,并從D-Cache中按照某種算法,找到一個Cache?line,將合并之后的數據寫到這個Cache?line中;如果被替換的這個Cache?line已經被標記為dirty,那么在被寫入之前,還需要先將這個被覆蓋的line中data?block寫回到物理內存中,這樣才能放心地將合并之后的數據寫到這個Cache?line中。

不管是load還是store指令,當發生D-Cache?miss時,D-Cache和物理內存都是需要交換數據,這個過程一般需要多個周期才能完成。如果在這個周期之內,又發生了D-Cache?miss,該如何處理?

最簡單的方法就是在D-Cache發生缺失并且被解決之前,使D-Cache與物理內存之間的數據通路被鎖定,只處理當前這個缺失的數據,處理器不能夠再執行其他load/store指令。這樣的阻塞大大減少了程序執行時可以尋找的并行性。使處理器的性能無法提高。

?產生D-Cache?miss的laod/store指令阻塞了后面的load/store指令的執行,所以這種設計方法為阻塞 Cache,在發生阻塞的這段時間,處理器只能暫停執行,無法做其他有用的事情。

如果在發生D-Cache缺失的時候,處理器可以繼續執行后面的load/store指令,這種設計方法就稱為非阻塞Cache,有時候也稱為lookup-free Cache,非阻塞Cache允許處理器在發生D-Cache?miss時候繼續執行新的load/store指令。

其實非阻塞Cache并不是亂序執行的超標量處理器的專屬品,在順序執行的處理器中也有這種方法。

面對store指令來說,當發生misss時,需要將存儲的數據和這個數據塊進行合并,然后將合并之后的數據寫到D-Cache中。

藥支持非阻塞的操作方式,在處理器中需要將那些已經產生D-Cache?miss?的load/store指令保存起來。利用Miss Status Holding Register部件,該部件由兩部分組成:

(a)首次缺失:對于一個給定的地址來說,訪問D-Cache時第一次產生的缺失稱為首次缺失;

(b)再次缺失:在發生首次缺失并且沒有被解決完畢之前,后續的訪問存儲器的指令再次訪問這個缺失的Cache?line,這時就稱為再次缺失,這里需要注意兩點,一是再次缺失并不僅僅是指單獨的一次缺失,在這個Cache?line被取回到D-Cache之前,后續訪問這個Cache?line的所有load/store指令都會再次缺失;二是再次缺失使用的地址未必和首次缺失使用的地址是一樣的,只要它們屬于同一個Cache?line即可。

?MSHR主體包括三項內容:

(a)V:valid位,用來指示當前的表項是否被占用,當發生首次缺失時,MSHR本體中的一個表項會被占用,此時valid位會被標記為1,當所需要的Cache?line從下級存儲器中取回來時,會釋放MSHR本體中被占用的表項,因此valid位會被清零。

(b)Block Address:指的是Cache?line中數據塊的公共地址,假設物理地址是32位,對于一個大小為64字節的數據塊來說,需要6位的地址才能找到數據塊的某個字節,因此數據塊的公共地址就需要32-6=26位。每次當load/store指令發生D-Cache?miss時,都會在MSHR的本體中查到它所需要的數據塊是否處于正在被取回的過程中,這需要個Block Address這一項進行比較才知道,通過這種方式,所有訪問同一個數據塊的指令只需要處理一次就可以了,避免存儲帶寬浪費。

(c)Issueed:表示首次缺失的load/store指令是否已經開始處理,即是否已經開始從下一級存儲器取回數據的過程,由于存儲器的帶寬有限,占用MSHR本體的首次缺失不一定馬上就會被處理,而是需要等到條件滿足的時候,才會向下一級存儲器發出讀數據的請求。

還有一點需要注意,對一個發生缺失的數據來說,如果訪問這個數據的所有load/store指令都處于分支預測失敗的路徑上,那么即使這個數據被下一級存儲器中取出來,也不應該寫到D-Cache中,這樣可以保護D-Cache不會受到分支預測失敗指令的影響。

在超標量處理器中,由于亂序執行的原因,可能會導致過多的load/store指令處于分支預測失敗的路徑上,這樣增加了D-Cache缺失的概率,導致有比實際更多的D-Cache缺失需要處理。

9.6.3?關鍵字優先

當執行一條訪問存儲器的指令而發生D-Cache?miss時,會將這條指令所需要整個數據塊都從下一級存儲器中取出來,如果考慮到預取,還需要將相鄰的下一個數據塊頁取出來,通常一個數據塊中包括的數據是比較多的,例如64字節。

如果等到數據塊中所有數據都寫到D-Cache之后才將所需要的數據送給CPU,這可能會讓CPU等待一段時間,為了加快執行速度,可以對D-Cache的下一級存儲器進行改造,使數據的讀取順序發生改變。

如果訪問存儲器的指令所需要的數據位于數據塊的第6個字,那么此時可以使下一級存儲器的讀取從第6個字開始,當讀取到數據末尾時,再從頭開始將剩下的第0~5個字讀取出來,這樣做的好處是當下級存儲器返回第一個字的時候,CPU就可以得到所需要的數據而繼續執行了,而D-Cache會繼續完成其他數據的填充工作,這就相當于將CPU的執行和Cache的填充兩部分工作進行了重疊,提高了整體的執行效率,這就是關鍵字有限Critical Word First。當然,下級存儲器系統需要增阿基硬件才能夠支持這種特性,這在一定程度上增大了硅片面積和功耗。

9.6.4?提前開始

如果不相付出成本,那么可以采用提前開始的方法Early Restart的方法。這種方法不會改變存儲器系統對于數據的讀取順序。

當指令所需要的數據,也就是第6個字,被從下一級存儲器取出來時,就可以讓CPU恢復執行了,此時數據塊剩余的數據可以繼續進行讀取,這部分時間和CPU執行時間進行了交疊,這樣提高了CPU的執行效率。

總結

以上是生活随笔為你收集整理的超标量处理器设计 姚永斌 第9章 指令执行 摘录的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。