超标量处理器设计 姚永斌 第6章 指令解码 摘录
在流水線中,指令解碼decode階段的任務是將指令中攜帶的信息提取出來,處理器使用這些信息控制后續流水線來執行這條指令。
指令集的復雜程度直接決定了這部分任務的工作量,對于CISC指令集來說,例如x86來說,指令的長度是不固定的,解碼階段首先需要分辨指令的邊界,這樣才能夠找到有效的指令,而且x86指令的尋址方式也很復雜,這增加了解碼的難度。對于RISC指令集來說,指令的長度是固定,例如MIPS和ARM,都是32位,這樣很容易分辨出來,而且RISC處理器的尋址方式相對也比較簡單,這些因素導致RISC處理器的解碼難度要遠低于CISC處理器,因此RISC處理器在成本和功耗方面天生就比CISC處理器占據優勢。影響處理器解碼復雜度還有一個因素,那就是每周期可以解碼的指令個數,由于每條指令都需要一個完整的解碼電路,所以對于一個每周期可以解碼n條的超標量處理器來說,就需要n個解碼電路。
總結來看,一個RISC處理器的解碼部分如下
?在超標量處理器中,即使是RISC指令集,仍存在一些另類的指令,這些指令不能使用一般指令的處理方式,需要特殊處理,例如有些指令的目的寄存器個數多于一個乘累加和LDM/STM等,這會影響寄存器重命名register?renaming過程。再例如,有些RISC指令集支持每條指令都條件執行,不過這些另類指令使用的頻率并不高,沒有必要讓后面的流水線增加硬件來對它們進行處理,這需要在解碼階段將它們轉換為普通的指令,后面的流水線按照一般指令的處理方式來執行它們。
6.1?指令緩存
為了減少I-Cache缺失帶來的影響,處理器可以在取指令階段從I-Cache中取出的指令個數多于每周期可以解碼的指令個數,例如MIPS 74kf處理器中,每周期取出4條指令,但是每周期只可以解碼2條指令,這就需要在取指令和解碼階段之間加入一個緩存,用來將I-Cache取出的所有指令保存起來,這個緩存稱為指令緩存instruction?buffer。
(1)四條指令,當然這四條指令未必全部都是有效的;
(2)有效指令的個數,當取指令的地址落在Cache?line的最后三個字,或者指令組fetch?group中存在被預測跳轉的分支指令時,會導致取指令階段無法向指令緩存中寫入四條指令,此時使用這個型號來告訴指令緩存,那些指令時有效的。
指令緩存本質上是一個FIFO,它能夠將指令按照程序中指定的順序存儲起來,這樣指令在解碼的時候,仍舊可以按照程序中指定的順序進行解碼,便于找到指令間存在的相關性。指令緩存是超標量處理器中必須的部件,其原因有二:
(1)在很多超標量處理中,每周期可以取指令的個數大于每周期可以解碼指令的個數,這樣即使在I-Cache缺失的時候,指令緩存中仍然有一些余量的指令,如果I-Cache的缺失可以很快的解決,那么基本上不會引起流水線暫停。
(2)在超標量處理器中,即使每周期取指令的個數等于每周期解碼指令的個數,例如都是4條指令,在流水線的解碼階段卻有一些特殊的指令要處理,這回導致取指令階段得到所有指令沒有辦法在解碼階段全部得到處理。例如乘累加指令有兩個目的寄存器,為了減少對寄存器重命名階段的影響,都會將其拆分成兩條普通的指令,每條指令都只有一個目的寄存器,因此,一旦在取指令階段得到的指令包括乘累加指令時,如果不進行處理,會導致解碼之后的指令個數多于4條,而后續的流水線的處理能力都是按照每周期處理4條指令來設計的,不可能因為這種不常出現的情況而增阿基后續流水線的處理能力,這樣會占用更多的硅片面積,因此就需要在解碼階段對乘累加類型的指令進行處理。
超標量處理器中限制的一種折中思想是不為很少出現的情況增加硬件的復雜度。由于這些限制的存在,在每周期內,取指令階段送出的指令不一定能夠在解碼階段全部被解碼,因此就需要一個地方來暫存這些不能被解碼的指令,這個地方就是指令緩存,使用它,可以使解碼部分的設計更加靈活。
由于指令緩存每周期內可以寫入多條指令,也可以讀出多條指令,因此它是一個多端口的FIFO,實際是使用交疊interleaving方式,使用多個單端口的SRAM來實現,從而避免了使用多端口SRAM所帶來的硬件和速度上的限制。
6.2?一般情況
MIPS指令一般包括兩個源寄存器Rs和Rt和一個目的寄存器Rd;ARM指令一般包括三個源寄存器Rn,Rs和Rm和一個目的寄存器Rd,情況遠不止如此,一些特殊情況為:
(1)對于條件執行的指令來說,還包括第四個源寄存器,也就是CPSR。
(2)所有改變狀態寄存器的指令,還包括第二個目的寄存器,即CPSR。
(3)對于前后變址的load/store指令,也包括第二個目的寄存器,即用來作為地址的寄存器,當指令執行完成后,除了將正常地數據寫到目的寄存器之外,還需要將地址寄存器的內容也進行更新。
(4)還有很另類的LDM/STM指令,包括了數量不確定的目的寄存器和源寄存器。
在上面特殊情況中,前兩條體現在CPSR寄存器,它可以作為源寄存器,也可以作為目的寄存器。在超標量實現中,如果將CPSR寄存器作為一個普通的寄存器來對待,那么此時ARM的每條指令就包括四個源寄存器和兩個目的寄存器,這對寄存器重命名register?renaming過程的映射表的影響非常,本來映射表的端口個數已經很多了,面積和速度都很難進行優化,如果在加入額外的端口,那么情況就會更加糟糕;而且CPSR寄存器的寬度只有4位(N,C,Z,V),而普通通用寄存器是32位,如果將CPSR寄存器和通用寄存器統一對待,會造成很多寄存器不能得到有效利用,32位寄存器只裝入4位的數據。考慮到這些限制,一般都是將CPSR寄存器單獨處理,為CPSR寄存器設置一個獨立的物理寄存器堆Physical?register?file,用來存放重命名之后的CPSR寄存器,并使用一個單獨地映射表mapping?table來進行管理。
對于(3)和(4)所體現的特殊性在于,將這些特殊的指令拆分為多條普通的RISC指令,這樣就可以利用一般的硬件對這些特殊的指令進行處理,在超標量處理器中,一條指令所攜帶的源寄存器和目的寄存器的個數直接決定了寄存器重命名電路在實現上的難易,例如映射表的端口個數,指令間相關性檢查電路的復雜度等。
?一般情況下,每周期都會從指令緩存instruction?buffer中讀取多條指令。從指令緩存中讀取的4條指令未必是4字對齊,這就需要將4條指令按照程序中原始的順序排序好,送給解碼電路進行解碼,這是采用交疊結構的多端口存儲器所必須考慮的問題。
由于RISC指令比較規整,很容易找出指令中的操作碼op和操作數operand,在解碼階段產生的流水線控制信號也會比較少,因此RISC處理器的解碼一般都可以在一個周期內完成,突出RISC秉承通過簡化的指令集來簡化硬件設計的復雜度,從而獲得更高的性能。
RISC處理在解碼解碼完成任務可以概括為三個what,它們解釋如下:
(1)what?type,例如指令時算術指令,訪問存儲器指令還是分支指令;
(2)what?operation,例如當算術指令時進行何種算術運算
(3)what?resource,例如對于算術指令來說,源寄存器和目的寄存器是那些,指令中是否有立即數等。
6.3?特殊情況
對于RISC中特殊指令,例如LDM/STM來說,需要多個周期才能完成,而且他們的目的/源寄存器有多個,如果在超標處理器中對它們按照普通指令對待的話,就會使映射表mapping?table、發射隊列issue?queue和重排序緩存ROB等部件面臨眾多的端口要求,大大增加了硬件消耗,并且降低了速度,因此在超標量處理器中不會直接處理復雜的LDM/STM指令,而是將它們轉換為多條簡單指令。
在x86指令集中,是允許指令中的一個數來自于存儲器的,這也是CISC指令的一個特點,而且為了便于訪問存儲器的指令之間進行相關性檢查,會將store指令拆分成STA和STD兩條指令,STA指令用于計算地址,而STD指令用來找到數據。
不管對于CISC指令集還是RISC指令集,使用比較復雜的指令可以獲得更高的額代碼密度和更低的I-Cache缺失率。但是在超標量處理器中這些復雜的指令引入了額外的麻煩,需要使用更多的硬件資源才可以對它們進行處理。最初在設計這些復雜指令的時候并沒有考慮超標量的實現,例如ARM指令集是為了在普通的標量scalar流水線而設計,x86則誕生在編譯器compiler很不發達的年代,需要硬件來承擔更多的責任。
在現在的高性能應用領域,代碼密度這個寶刀已經銹跡斑斑,失去了曾經的魅力。
而ARM指令集中這些復雜指令則需要消耗更多的硅片資源來實現,導致了功耗的增加,與低功耗設計愿景相違背。
在新的ARMv8指令集中,LDM/STM這樣的復雜指令已經被砍掉了,而且通用寄存器的個數也變為32個,也不再有指令內嵌的移位運算,條件執行也被限制在少數的指令上。
6.3.1?分支指令的處理
采用Checkpoint的方式對分支預測失敗的處理器進行狀態恢復時,為了減少分支編號分配電路的復雜度,需要限制每周期進行解碼的分支指令個數,例如最多有一條分支指令。
面對多條分支指令的一個簡單解決方法,即遇到分支指令時,就不在本周期對這條分支指令后面的指令進行解碼,而是將它們放到下個周期,這個功能只需要通過改變指令緩存的讀指針即可。
如果采用基于ROB的方法對分支預測失敗時的處理器進行狀態恢復,就不在需要進行編號的分配工作了。
在解碼階段還有一個重要任務,就是對分支預測是否正確進行初步的檢查,越早地發現錯誤的分支預測,引起的懲罰penalty越小,而一些直接跳轉類型的分支指令在解碼階段就可以計算出目標地址,因此可以在解碼階段對這些分支指令的目標地址是否正確進行檢查。
分支指令在解碼階段是無法得到實際方向的(除了Jump指令,因為它總是跳轉的,一般不會預測錯誤),因為在解碼階段也就無法對分支預測的方向進行檢查,這需要等到后續的流水線階段才可以完成。
6.3.2?乘累加/乘法指令的處理
一般來說,越是簡單的指令,越容易在超標量處理器中實現。MIPS中的特殊指令乘累加和乘法是一種特殊的指令,它的特殊之處在于指令中包括兩個目的寄存器,且不屬于通用寄存器。
乘法和目的寄存器有兩個目的寄存器,非常規的情況就為重命名電路帶來了麻煩。不僅如此,如果把這條乘累加.乘法指令直接放到ROB中,則ROB需要能夠存放兩個目的寄存器,這種非常規情況也增加了ROB的面積,而且由于大部分指令都不是乘累加類型的指令,所以ROB中增加的部分絕大部分時間都是沒有使用的,采用以下兩部來解決這個問題:
(1)將寄存器Hi和Lo分配給MIPS處理的第33個和34個通用寄存器,當然在指令集中是看不到這個過程的,這種分配只是在處理器內部進行,寄存器重命名過程的重映射表也相應地需要支持34個通用寄存器。
(2)將乘法/乘累加指令拆分成兩條指令。
這兩條指令經過重命名后并寫到ROB中,會占用了ROB的兩個表項entry,需要讀取四個源寄存器,兩個目的寄存器。其實在處理器內部,被拆分的乘累加指令并不是單獨去完成運算的,而是在流水線執行階段,仍舊以一個完整的乘累加指令來完成運算,因此指令的拆分只是更有利于寄存器重命名,以及便于在ROB中存放,每條被拆分的指令并不是單獨地進行運算。
當然,要實現上述功能,還需要對發射隊列issue?queue做特殊處理,將乘法指令和乘累加指令使用一個運算單元Function Unit,這個FU的發射隊列和其他的有所不同,它包括四個源操作數,兩個目的寄存器。在解碼階段被拆分的指令,經過寄存器重命名后,就要寫到ROB和發射隊列中,這個階段就是Dispatch,此時寫到ROB中仍舊是以兩條指令的方式寫入,占據ROB中兩個連續的空間;而寫發射隊列的時候,則是將兩條指令進行了融合,這兩條指令在發射隊列中變成一條完整的乘累加或乘法指令,這樣就能保證FU在執行的時候,能夠執行一個完整的乘累加或者乘法指令。
對于一個周期內解碼的四條指令中包括乘累加指令,采用下面兩種方法來解決;
方法一:在解碼階段和寄存器重命名階段之間加入一個緩存,用它來替換流水線寄存器,暫存解碼階段產生的指令信息。但是,由于指令經過解碼之后會得到很多的信息,例如流水線的控制信號燈,導致這個緩存需要的位寬很大,在一定程度上增加了硬件面積。
方法二:限制每周期可以解碼的個數,一旦在解碼階段發現乘累加指令,例如MADD,那么只有在MADD1指令及其之前的指令可以解碼,在MADD2及其之后的指令需要等到下個周期才可以解碼。
6.3.3?前后變址指令的處理
在ARM指令集中還有一種前后變址的尋址方式,能夠在一條指令中完成兩個任務,普通流水線處理器是容易實現的,但是在超標量實現中,會帶來額外的麻煩。例如ARM中的一條load指令:
LDR R2,[R1,#4];
此指令執行了兩個操作,即從存儲器中將地址[R1+4]中的數據放到寄存器R2中,并且把用作地址的寄存器R1更新為R1+4的值。此時目的寄存器包括R1和R2,而兩個目的寄存器會給寄存器重命名以及之后例如喚醒帶來麻煩,因為在超標量處理器實現時,仍舊會在處理器內部將這個復雜的load指令拆分成兩條普通的指令。
一條普通的load指令實現load數據的功能;
一條加法指令,用來改變地址寄存器。
這個拆分過程是在解碼階段完成的,
實現同樣的功能,ARM處理器占用更少的指令存儲器空間,使得ARM處理的I-Cache?miss?rate低于MIPS處理器;但是ARM需要在處理器內部使用硬件將指令進行拆分,這需要占據更多的硅片面積,也導致了功耗增加。
6.3.4 LDM/STM指令的處理
STM指令將多個寄存器的內容保存在存儲器中一片連續的空間內,LDM指令可以將存儲器中一片連續地址空間上的數據加載到多個寄存器中,在超標量處理器中,需要將它們拆分為多條普通的load/store指令。例如LDM指令;
LDM R5!, {R0~R3}
這條指令將存儲器中四個地址的數據memory[R5],memory[R5+4],memory[R5+8],memory[R5+12]讀取到R0,R1,R2,R3中,并將作為地址的寄存器R5更新為新值R5+12,因此這條指令相當于完成了五個任務。超標量處理器的解碼階段會將其拆分為4條普通的load指令和一條普通的add指令。
LDR R0, #0[R5]
LDR R0, #4[R5]
LDR R0, #8[R5]
LDR R0, #12[R5]
ADD R5, R5,12
拆分之后,LDM指令仍舊需要多個周期才可以完成,而且這種拆分需要一些邏輯電路,并對處理器的周期時間產生負面影響。
為什么要沖寄存器列表的最低位開始,順序進行查找呢?這是因為在LDM/STM指令中,寄存器按照編號從小到大的順序和存儲器中一片連續的地址空間相對應,因此必須按照順序找到寄存器列表中的寄存器,才可以正確的和存儲器的地址相對應。
6.3.5?條件指令的處理
在ARM中,每條指令的編碼中,Bit[31:28]用作了條件碼,用來判斷當前CPSR寄存器中的值是否是想要的值,從而決定這條指令是否執行。
ARM處理器中,條件執行的本質將CPSR寄存器也作為一個源目的寄存器來對待,當一條指令的執行需要改變狀態寄存器時,CPSR作為一個目的寄存器,而當一條指令需要條件執行時,狀態寄存器CPSR就作為源寄存器。
超標量處理器中指令都是亂序執行,條件執行的指令讀取到CPSR寄存器的內容未必就是自己想要的。
在超標量寄存器的ARM處理器中,需要對條件執行的情況加以處理。當一條指令要改寫CPSR寄存器時,使用一個新的CPSR寄存器來保存這個指令的狀態,并給這個CPSR寄存器賦予一個新的名字,與之對應的條件執行指令都會使用這個新的CPSR寄存器作為它們的一個源寄存器。
總結
以上是生活随笔為你收集整理的超标量处理器设计 姚永斌 第6章 指令解码 摘录的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 文本编辑控件FreeTextBoxFCK
- 下一篇: CentOS7.2安装linux版QQ