處理器體繫結構
ISA
一個處理器支援的指令和指令的位元組級編碼稱為它的指令集體繫結構ISA。
雖然每個廠商製造的處理器效能和複雜性不斷提高,但是不同型號在ISA層級上都保持著相容。因此,ISA在編譯器編寫者和處理器設計人員之間提供了一個概念抽象層。
這個概念抽象層即ISA模型:CPU允許的指令集編碼,且順序地執行指令,也就是先取出一條指令,等到她執行完畢,再開始下一條。然而,現代處理器的實際工作方式可能跟ISA隱含的計算模型大相徑庭。通過同時處理多條指令的不同部分,處理器可以獲得較高的效能。但其必須對外表現出符合ISA模型的執行結果。
在電腦科學中,用巧妙的方法在提高效能的同時,又保持一個更簡單、更抽象模型的功能,這種思想是眾所周知的(抽象)。
CPU硬體簡介
大多數現代電路設計都是用訊號線上的高電壓和低電壓來表示不同的位值。
要實現一個數字系統需要三個主要的組成部分:
①計算對位進行操作的函數的組合邏輯(ALU)
②儲存位的儲存空間元素(寄存器)
③控制儲存空間元素更新的時鐘訊號
邏輯門是數字電路的基本計算元素,它們產生的輸出,等於它們輸入位值的某個布爾函數。
將很多邏輯門組合成一個網,就能構建計算塊,稱為組合電路。(相當於一個運算式)
算術/邏輯單元(ALU)是一種很重要的組合電路,這個電路有三個輸入:兩個資料輸入及一個控制輸入。根據控制輸入的設定,電路會對資料輸入執行不同的算術或邏輯操作。
儲存空間和時鐘
組合電路從本質上講,不儲存任何資訊。它們只是簡單地響應輸入訊號,產生等於輸入的某個函數的輸出。為了產生時序電路,也就是有狀態並且在這個狀態上進行計算的系統,我們必須引入按位儲存資訊的裝置。
存放裝置都是由同一個時鐘控制,時鐘是一個周期性訊號,決定了什麼時候要把新值載入到裝置中。
大多數時候,寄存器都保持在穩定點(用x表示),產生的輸出等於它的目前狀態。訊號沿著寄存器前面的組合邏輯傳播,這時,產生了一個新的寄存器輸入(用y表示),但只要時鐘是低電位的,寄存器的輸出就仍然保持不變。當時鐘變成高電位的時候,輸入訊號才載入到寄存器中,成為下一個狀態y,直至下一個時鐘的上升沿。
寄存器是作為電路不同部分中的組合邏輯之間的屏障。每當每個時鐘到達上升沿時,值才會從寄存器的輸入傳送到輸出。
寄存器檔案(通用寄存器組成的邏輯塊) 有兩個讀連接埠,還有一個寫連接埠。電路可以讀兩個程式寄存器的值,同時更新第三個寄存器的狀態。每個連接埠都有一個地址輸入,表明選擇哪個程式寄存器。
雖然寄存器檔案不是組合電路,因為它有內部儲存。不過,從寄存器檔案讀資料就好像它是一個以地址為輸入、資料為輸出的一個組合邏輯塊。
指令編碼
指令集的一個重要性質就是位元組編碼必須有唯一的解釋。任意一個位元組序列要麼是一個唯一的指令序列的編碼,要麼就不是一個合法的位元組序列。因為每條指令的第一個位元組有唯一的代碼和功能組合,給定這個位元組,我們就可以決定所有其他附加位元組的長度和含義。
每條指令需要1——6個位元組不等,這取決於需要哪些欄位。每條指令的第一個位元組表明指令的類型:高4位是代碼部分(例:6為整數類操作指令),低4位是功能部分(例:1為整數類中的減法指令) 61合起來即為sub指令。
處理一條指令的序列:
取指(fetch)
取值階段從儲存空間讀取指令位元組,放到指令儲存空間(CPU中)中,地址為程式計數器(PC)的值。
它按順序的方式計算當前指令的下一條指令的地址(即PC的值加上已取出指令的長度)
解碼(decode)
ALU從寄存器檔案(通用寄存器的集合)讀入最多兩個運算元。(即一次最多讀取兩個寄存器中的內容)
執行(execute)
在執行階段會根據指令的類型,將算數/邏輯單元(ALU)用於不同的目的。對其他指令,它會作為一個加法器來計算增加或減少棧指標,或者計算有效地址,或者只是簡單地加0,將一個輸入傳遞到輸出。
條件碼寄存器(CC)有三個條件位。ALU負責計算條件碼新值。當執行一條跳轉指令時,會根據條件碼和跳轉類型來計算分支訊號cnd。
訪存(memory)
訪存階段,資料存放區器(CPU中)讀出或寫入一個儲存空間字。指令和資料存放區器訪問的是相同的儲存空間位置,但是用於不同的目的。
寫回(write back)
寫回階段最多可以寫兩個結果到寄存器檔案。寄存器檔案有兩個寫連接埠。連接埠E用來寫ALU計算出來的值,而連接埠M用來寫從資料存放區器中讀出的值。
更新PC(PC update)
根據指令代碼和分支標誌,從前幾步得出的訊號值中,選出下一個PC的值。
我們以SEQ(sequential 順序的)處理器為例講解CPU的基本原理。每個刻度上,SEQ執行處理一條完整指令所需的所有步驟。不過這需要一個很長的刻度時間,因此刻度頻率會低到不可接受。
SEQ的時序
組合邏輯不需要任何時序或控制——只要輸入變化了,值就通過邏輯門網路傳播。
我們也將讀隨機訪問儲存空間(寄存器檔案、指令儲存空間和資料存放區器)看成和組合邏輯一樣的操作。(寫隨機訪問儲存空間需要等待高電平)
由於指令儲存空間只用來讀指令,因此我們可以將這個單元看成是組合邏輯。(記憶體向指令儲存空間中寫指令是CPU外部的事件 不屬於CPU內的時序)
每個刻度,程式計數器都會裝載新的指令地址。
只有在執行整數運算指令時,才會裝載條件碼寄存器。
只有在執行mov、push、call指令時,才會寫資料存放區器。
要控制處理器中活動的時序,只需要寄存器和儲存空間的時鐘控制。
因為指令運行計算的結果,寫入寄存器或儲存空間中。
我們可以把取指、解碼、執行等過程看做是組合邏輯的處理過程(因為它們不涉及寫入寄存器)。把寫回看做是另一個過程。
則整個過程可簡化為所示:
【舉例詳解】
有如下指令:
0x000 : irmovl $0x100, %ebx
0x006 : irmovl $0x200, %edx
0x00c : addl %edx, %ebx
0x00e : je dest
0x013 : rmmovl %ebx, 0(%edx)
0x019 : dest: halt
在我們的SEQ處理器中,一個刻度(即兩次高電平時間的時間間隔)執行一條指令。
刻度3開始時(點1處),一個高電平打入,地址0x00c載入程式計數器PC中。這樣,與PC相連的MCU(主存控制單元)就在記憶體中把地址0x00c處的addl指令提取出來,載入到指令儲存空間中。(從記憶體中讀取資料很慢,這個過程會很久,所以我們的刻度要很長,才能做到一個刻度執行一條指令)同時,PC的值加上addl指令的長度,得出新PC值,新PC值通過匯流排傳播,等待下次高電平時寫入PC。
組合邏輯指令儲存空間中的輸入一變化,值(addl指令)就通過邏輯門網路傳播。故,瞬間讀出了寄存器檔案中%edx、%ebx的值(因為讀寄存器檔案不需要高電平觸發)
讀出的%edx、%ebx的值瞬間流動到組合邏輯ALU中,ALU根據之前傳播的addl指令,知道此為加法指令,瞬間計算出這兩個值的結果valE。valE通過匯流排傳播瞬間到達寄存器檔案,但是此時還不能向寄存器檔案寫入,必須等待下次的高電平。
故此時,寄存器檔案和儲存空間中儲存的還都是上條指令的結果值。(點1、2處)
刻度4開始時(點3處),一個高電平打入,上周期產生的新PC值寫入程式計數器,上周期計算得到的addl的結果valE值寫入寄存器檔案中的%ebx中。
因為地址0x00e載入了程式計數器中,故會取出並執行跳轉指令je。因為條件碼ZF為0,所以不會選擇分支。在這個周期末尾(點4),程式計數器已經產生了新值0x013。但是直到下個周期開始之前,寄存器和儲存空間中的狀態還是保持著addl指令設定的值。
【如此例所示,用時鐘來控制狀態元素的更新,以及值通過組合邏輯來傳播,足夠控制我們SEQ實現中每條指令執行的計算了。每次時鐘由低變高時,處理器開始執行一條新指令。】
【讀操作沿著這些單元傳播,就好像它們是組合邏輯,而寫操作是由時鐘控制的。】
【注意】(個人理解)
早期的沒有流水線的CPU可不是一個周期執行一條指令,我們的SEQ處理器只不過是為了講解CPU的時序而特意做成的一個周期執行一條指令,這樣做,使得一個刻度的時間特別長(因為我們要等主存把指令載入到指令寄存器,有的指令還要等待資料寄存器把資料寫入到主存)。
若按照執行時間最長的指令的執行時間作為刻度,則時鐘的粒度太大,因為不同的指令需要的執行時間不同,時鐘粒度太大會導致有些指令早早執行完畢,但CPU還得閑著,等待本周期結束。(我們的六步劃分是針對所有指令整體而言的,很多指令只經曆其中幾步)
故早期的CPU設計者,把由一個大組合邏輯完成的執行,分割成幾個階段,由幾個小組合邏輯完成。中間插入寄存器儲存中間結果,就像後面講的流水線機制那樣,只是指令順序進入,一條指令運行完,下條指令才開始進入。
這樣做的好處是,由於各種指令涉及的階段不同,有的指令經曆較少的階段就完成了,有的指令要經曆較多的階段,運行一條指令所需的時間不同了,小於等於最耗時指令的時間。(而我們設計的SEQ處理器每條指令都要經曆同樣的大組合邏輯,刻度只能定為最耗時指令的時間)
[類比記憶體管理的分頁機制,提高記憶體利用率。]
1978年的Intel 8086,需要多個(通常是3~10個)刻度來執行一條指令。比較先進的處理器可以保持每個時鐘2~4條指令的執行速率。其實每條指令從開始到結束需要長的多的時間,大約20個或者更多的周期,但是處理器使用了非常多的聰明技巧來同時處理多達100條的指令。
---------------------------------------------------------------------------------------分割線-------------------------------------------------------------------------------------------
流水線原理
我們通過將執行每條指令所需的步驟組織成一個統一的流程,就可以用很少量的各種硬體單元以及一個時鐘來控制計算的順序,從而實現整個處理器。不過這樣一來,控制邏輯就必須要在這些單元之間路由訊號,並根據指令類型和分支條件產生適當的控制訊號。(CPU內有三種匯流排:控制匯流排、地址匯流排、資料匯流排)
SEQ處理器不能充分利用硬體單元,因為每個單元只在整個刻度的一部分時間內才被使用。我們會看到引入流水線能獲得更好的效能。
在流水線化的系統中,待執行的任務被劃分成了若干獨立的階段。
例如在汽車清洗中,這些階段包括噴水、打肥皂、擦洗、上蠟和烘乾。通常都會允許多個顧客同時經過系統,而不是要等到一個使用者完成了所有從頭至尾的過程才讓下一個開始。
當前面一輛汽車從噴水階段進入擦洗階段時,下一輛就可以進入噴水階段了。通常,汽車必須以相同的速度通過這個系統,以避免撞車。
流水線化的一個重要特性就是增加了系統的輸送量,也就是單位時間內服務的顧客總數,不過它也會輕微地增加延遲,也就是服務一個使用者所需要的時間。(例如一個只需要噴水的汽車,在非流水線的系統,它噴完水就可以走了。而在流水線化的系統,不管你是什麼需求,都要走完整個流程的時間)
(我們之前的設計是一條指令執行完,下條指令才能進入CPU,(所不同的是刻度的粒度)。流水線化是允許多條指令在CPU中,每條指令在CPU中的時間是一樣的,哪怕你一個周期就執行完了,你也得等剩下的階段結束,使後面的指令被延遲了。
雖然流水線化,所有指令在CPU中待的時間都一樣(且都按最耗時指令算的),但它們的時間是重疊的。假設一條指令在CPU中待6ms,那麼12ms能處理7條指令,而非流水線,雖然一條指令最多執行6ms,但它們的時間是相加的,12ms可能只執行3條。12=6+2+4)
【例如】
一個簡單地非流水化的硬體系統:
它是由一些執行計算的邏輯以及一個儲存計算結果的寄存器組成的。時鐘訊號控制在每個特定的時間間隔載入寄存器。
(CD播放器中的解碼器就是這樣的一個系統。輸入訊號是從CD表面讀出的位,邏輯電路對這些位進行解碼,產生音頻訊號。圖中的計算塊是用組合邏輯來實現的,意味著訊號會穿過一系列邏輯門,在一定時間的延遲之後,輸出就成為了輸入的某個函數)
在這個例子中,我們假設組合邏輯需要300ps,而載入寄存器需要20ps。這個實現中,在開始下一條指令之前必須完成前一個。執行一條指令需要320ps,即每秒鐘系統輸送量3.12GIPS。
流水化的硬體系統
假設將系統執行的計算分成三個階段(A、B和C),每個階段需要100ps,、然後在各個階段之間放上流水線寄存器,這樣每條指令都會按照三步經過這個系統,從頭到尾需要三個刻度。
(流水線寄存器的作用:作為電路不同部分中的
組合邏輯之間的屏障。儲存每步組合邏輯的運算結果。這是為了分割流水而插入的寄存器。)
流水線,在穩定點下,三個階段應該都是活動的,每個刻度,一條指令離開系統,一條新的進入。
這樣,我們一個階段的時間,相當於運行了一條指令,在這個系統中,我們將刻度設為100+20=120ps,得到的輸送量大約為8.33GIPS。(這是在)
因為處理一條指令需要3個刻度,所以這條流水線的延遲就是3*120=360ps。非流水運行一條完整指令需要320ps。
(從宏觀整體上看,一個刻度運行了一條指令(這條指令是由多條指令的各階段拼合的),而從單條指令的執行看,需要3個刻度執行一條完整指令。)
我們將系統輸送量提高到原來的8.33/3.12=2.67倍,代價是增加一些硬體(流水線寄存器),以及延遲的少量增加(360/320=1.12)。延遲變大是由於增加的流水線寄存器的時間開銷。
刻度的時間就是流水線分割的一個階段的時間,這樣,從宏觀上看,是一個刻度執行一條指令。
【注意】
如果時鐘運行得太快,就會有災難性的後果。值可能會來不及通過組合邏輯,並且當時鐘上升時,寄存器的輸入還不是合法的值。(即刻度比流水線一個階段的時間短)
而我們減緩時鐘不會影響流水線的行為。訊號傳播到流水線寄存器的輸入,但是直到時鐘上升時才會改變寄存器的狀態。(即刻度比流水線一個階段的時間長)
故,我們通過改變倍頻器的值來提高時鐘頻率的超頻手段,其提高是有限的。
流水線的局限性
1、不一致的劃分
之前的是一個理想的流水線化的系統,每個階段需要的時間都相同。而實際系統通過各階段的延遲一般是不同的。且運行時鐘的速率是由最慢階段的延遲限制的。(即系統輸送量受最慢階段的速度所限制)
2、流水線過深,收益反而下降
例如,我們把計算分成6個階段,每個階段需要50ps。在每對階段之間插入流水線寄存器就得到了一個六階段流水線。
這個系統的最小刻度為50+20=70ps,輸送量為14.29GIPS。效能比3階段流水提高了14.29/8.33=1.71倍。由於通過流水線寄存器的延遲,輸送量並沒有加倍。這個延遲成了流水線輸送量的一個制約因素。
為了提高時鐘頻率,現代處理器採用了很深的(15或更多的階段)流水線。
分支預測
流水線化設計的目的就是每個刻度都發射一條新指令,要做到這一點,我們必須在取出當前指令之後,馬上確定下一條指令的位置。
但如果取出的指令是條件分支指令,要到幾個周期後,也就是指令通過執行階段之後,我們才能知道是否要選擇分支。類似的,如果取出的指令是ret,要到指令通過訪存階段,才能確定返回地址。
對條件轉移來說,我們既可以預測選擇了分支,那麼新PC值應為valC,也可以預測沒有選擇分支,那麼新PC值應為valP。
對ret指令,可能的返回值幾乎是無限的,因為返回地址位於棧頂的字,其內容可以是任意的。在設計中,我們不會試圖對返回地址做任何預測。只是簡單地暫停處理新指令,直到ret指令通過寫回階段。
無論哪種情況,我們都必須以某種方式來處理預測錯誤的情況,因為此時已經取出並部分執行了錯誤的指令。
(流水線懲罰待寫)
流水線冒險
使用流水線技術,當相鄰指令間存在相關時會導致出現問題。
這些相關有:
1、資料相關:下一條指令會用到這一條指令計算出的結果
2、控制相關:一條指令要確定下一條指令的位置,例如在執行跳轉、調用或返回指令時。
這些相關可能會導致流水線產生計算錯誤,稱為冒險。
用
暫停來避免資料冒險
暫停(stalling)是避免冒險的一種常用技術。讓一條指令停頓在解碼階段,直到產生它的源運算元的指令通過了寫回階段,這樣我們的處理器就能避免資料冒險。
暫停技術就是讓一組指令阻塞在它們所處的階段,而允許其他指令繼續通過流水線。
【例】
irmovl $10, %edx
irmovl $3, %eax
addl %edx, %eax
halt
當對addl指令解碼之後,暫停控制邏輯發現了對兩個源寄存器的資料冒險。(其發現前面的執行、訪存或寫回階段中至少有一條指令會更新寄存器%edx或%eax 我們addl下一階段就要取%eax和%edx的值,但卻不能保證其是更新過的值)
暫停控制邏輯就在執行階段中插入一個氣泡,並在下個周期重複對addl的解碼。
它再次發現對兩個源寄存器的冒險,就在執行階段中插入一個氣泡,並在下個周期重複對addl的解碼。
實際上,機器是動態地插入3條nop指令。(插到執行階段,而不是從取指開始)
irmovl $10, %edx
irmovl $3, %eax
bubble
bubble
bubble
addl %edx, %eax
halt
(這個過程就像,排隊的時候前面的人前進了一步,但這時有另一個人插在了你前面的空缺中,你的位置保持不動,但前面的人都前進了一步。不斷的有空缺,但不斷地有人插入,你就一直在原地不動)
當確定前面的指令已經更新過了我們要的兩個寄存器的值,則addl開始前行。
但是這樣的解決方案得到的效能並不好,一條指令更新一個寄存器,緊跟其後的指令就使用被更新的寄存器,像這樣的情況不勝枚舉。這會導致流水線暫停長達三個周期,嚴重降低了整體的輸送量。
用
轉寄來避免資料冒險
在解碼階段從寄存器檔案中讀入源運算元,但是對這些源寄存器的寫有可能要在寫回階段才能進行。與其暫停直到寫完成,不如簡單地將要寫的值傳到流水線寄存器E作為源運算元。
(即,我們不必等到irmovl $10, %edx和irmovl $3, %eax 完成對寄存器的寫更新之後再繼續addl,而是在addl解碼階段發現需要%edx、%eax值,解碼邏輯不從寄存器檔案中去讀,而是用前面階段未寫入寄存器的值。)
這種將結果直接從一個流水線階段傳到較早階段的技術稱為
資料轉寄。
在周期4中,解碼階段邏輯發現有在訪存階段中對寄存器%edx未進行的寫,還發現在執行階段中正在計算寄存器%eax的新值。它用這些值,而不是從寄存器檔案中讀出的值,作為valA和valB的值。
載入/使用資料冒險
有一類資料冒險不能單純用轉寄來解決,因為儲存空間讀(訪存階段)在流水線發生的比較晚。
例:
mrmovl 0(%edx), %eax
addl %ebx, %eax
halt
指令mrmovl讀取儲存空間0(%edx)處的值,發生在訪存階段,而此時指令addl已經在執行階段了!其已經讀取了%eax的值了。即由於mrmovl指令擷取的運算元值比較晚,來不及發送給後面需要用的指令了。
我們可以將暫停和轉寄結合起來,避免載入/使用資料冒險。(既然是來不及發送給後面的指令,那就讓後面的指令暫停幾個周期,再發送)
當mrmovl指令通過執行階段時,流水線控制邏輯發現解碼階段中的指令(addl)需要從儲存空間中讀出的結果。它會將解碼階段中的addl指令暫停一個周期,導致執行階段中插入一個氣泡。 mrmovl指令從儲存空間中讀出的值可以從訪存階段轉寄到解碼階段中的addl指令。
這種用暫停來處理載入/使用冒險的方法稱為載入互鎖。載入互鎖和轉寄技術結合起來足以處理所有可能類型的資料冒險。
異常處理
異常可以由程式執行從內部產生,也可以由某個外部訊號從外部產生。
簡單的三種內部異常:
1、halt指令
2、非法指令
3、訪問非法地址
(還有一些外部例外狀況:網口收到新包、使用者點擊滑鼠等)
在簡化的ISA模型中,當處理器遇到異常時,會停止,設定適當的狀態代碼,且應該是到異常指令之前的所有指令都已經完成,而其後的指令都不應該對程式員可見的狀態產生任何影響。
在一個更完整的設計中,處理器會繼續調用例外處理常式,這是作業系統的一部分。
★一般地,通過在流水線結構中加入異常處理邏輯,我們會在每個流水線寄存器中包括一個狀態代碼Stat。如果一條指令在其處理器中於某個階段產生了一個異常,這個狀態欄位就被設定成指示異常的種類。
異常狀態和該指令的其他資訊一起沿著流水線傳播,直到它到達寫回階段。在此,流水線控制邏輯發現了異常,並停止執行。
例外狀況事件不會對流水線中的指令流有任何影響,除了會禁止流水線中後面的指令更新程式員的可見狀態(條件碼寄存器和儲存空間),直到異常指令到達最後的流水線階段。
因為指令到達寫回階段的順序與它們在非流水化的處理器中執行的順序相同,所以我們可以保證第一條遇到異常的指令會第一個到達寫回階段,此時程式執行會停止,流水線寄存器(W寫回)中的狀態代碼會被記錄為程式狀態。
其他問題多周期指令
我們之前設計的處理器指令集中的所有指令都包括一些簡單的操作,例如數字加法。這些操作可以在執行階段中一個周期內處理完。
在一個更完整的指令集中,還有整數乘法除法、以及浮點運算。在我們之前設計的流水化處理器中,浮點加法需要3、4個周期,整數除法需要32個周期。
實現多周期指令的一種簡單方法就是簡單地擴充執行階段邏輯的功能,添加一些整數和浮點算數運算單元。一條指令在執行階段中逗留它所需要的多個刻度,會導致取指和解碼階段暫停。這種方法實現起來很簡單,但是得到的效能並不是太好。
通過採用獨立於主流水線的特殊硬體功能單元來處理較為複雜的操作,可以得到更好的效能。通常,有一個功能單元來執行整數乘法和除法,還有一個來執行浮點操作(副處理器)。
當一條指令進入到解碼階段時,它可以被發射到特殊單元。在這個特殊單元執行該操作時,流水線會繼續處理其他指令。通常,浮點單位本身也是流水線化的,因此多條指令可以在主流水線和各個單元中並存執行。
不同單元的操作必須同步,以避免出錯。
如果在不同單元執行的各個指令之間有資料相關,控制邏輯可能需要暫停系統的某個部分,直到由系統其他部分處理的操作的結果完成。
使用各種形式的轉寄,將結果從系統的一部分傳遞到其他部分,這和前面的PIPE流水線各個階段之間的轉寄一樣。雖然與PIPE相比,整個設計變得更複雜,但還是可以使用暫停、轉寄、以及流水線控制等同樣的技術,使整體行為與順序的ISA模型相匹配。
與儲存系統的介面
在我們之前的流水化CPU中,我們假設取指單元和資料存放區器都可以在一個刻度內讀或是寫儲存空間中任意的位置。
但是,實際情況是,我們以儲存空間位置的虛擬位址來引用資料,這就要求在執行實際的讀寫操作之前,要將虛擬位址翻譯成物理地址。顯然,要在一個刻度內完成所有這些處理是不現實的。更糟糕的是,要訪問的儲存空間的的值可能位於磁碟上,這會需要上百萬個刻度才能把資料讀入到處理器儲存空間中。
儲存系統:
CPU的儲存系統是由多種硬體儲存空間和管理虛擬儲存空間的作業系統軟體共同組成的。
儲存系統被組織成一個階層,較快但是較小的儲存空間保持著儲存空間的一個子集,而較慢但是較大的儲存空間作為它的後備。
最靠近處理器的一層是快取(cache)儲存空間,它提供對最常使用的儲存空間位置的快速存取。一般有2個一層cache——一個用於讀指令,一個用於讀寫資料。
還有另一種類型的快取儲存空間,稱為TLB(Translation Look-aside Buffer翻譯後備緩衝器),它提供了從虛擬位址到物理地址的快速翻譯。
將TLB和cache結合起來使用,在大多數時候,確實可能在一個刻度內讀指令並讀或是寫資料。
緩衝不命中:有些引用的位置不在快取中,即出現快取不命中。在最好的情況下,可以沖=從較高層的cache或處理器的主存中找到不命中的資料,這需要3--20個刻度。同時,流水線會簡單地暫停,將指令保持在取值或訪存階段,直到快取能夠執行讀或寫操作。
缺頁異常:當被引用的儲存空間位置實際上是在磁碟儲存空間上的,硬體會產生一個缺頁異常訊號。同其他異常一樣,這個異常會導致處理器叫用作業系統的例外處理常式代碼。然後這段代碼會發起一個從磁碟到主存的傳送操作。
讓硬體叫用作業系統常式,然後作業系統常式又會將控制返回給硬體,這就使得硬體和系統軟體在處理缺頁時能協同工作。
從處理器的角度來看,將用暫停來處理短時間的快取不命中和用異常處理來處理長時間的缺頁結合起來,能夠顧及到儲存空間訪問時由於儲存空間階層引起的所有不可預測性。