?
15.3??高速緩沖存儲器Cache
當?shù)谝淮鶵ISC微處理器剛出現(xiàn)時,標準存儲器元件的速度比當時微處理器的速度快。很快,半導體工藝技術的進展被用來提高微處理器的速度。標準DRAM部件雖然也快了一些,但其發(fā)展的主要精力則放在提高存儲容量上。
1980年,典型DRAM部件的容量為4KB。1981年和1982年開發(fā)出了16KB芯片。這些部件的隨機訪問速率為3MHz或4MHz,局部訪問(頁模式)時速率大約快1倍。當時的微處理器每秒需要訪問存儲器2M次。
到2000年,DRAM部件每片的容量到達256Mbit,隨機訪問速率在30MHz左右。微處理器每秒需要訪問存儲器幾百兆次。如果處理器速率遠高于存儲器,那么只能借助Cache才能滿足其全部性能。
Cache存儲器是一個容量小但存取速度非??斓拇鎯ζ?,它保存最近用到的存儲器數(shù)據(jù)拷貝。對于程序員來說,Cache是透明的。它自動決定保存哪些數(shù)據(jù)、覆蓋哪些數(shù)據(jù)?,F(xiàn)在Cache通常與處理器在同一芯片上實現(xiàn)。Cache能夠發(fā)揮作用是因為程序具有局部性特性。所謂局部性就是指,在任何特定的時間,微處理器趨于對相同區(qū)域的數(shù)據(jù)(如堆棧)多次執(zhí)行相同的指令(如循環(huán))。
Cache經(jīng)常與寫緩存器(write?buffer)一起使用。寫緩存器是一個非常小的先進先出(FIFO)存儲器,位于處理器核與主存之間。使用寫緩存的目的是,將處理器核和Cache從較慢的主存寫操作中解脫出來。當CPU向主存儲器做寫入操作時,它先將數(shù)據(jù)寫入到寫緩存區(qū)中,由于寫緩存器的速度很高,這種寫入操作的速度也將很高。寫緩存區(qū)在CPU空閑時,以較低的速度將數(shù)據(jù)寫入到主存儲器中相應的位置。
通過引入Cache和寫緩存區(qū),存儲系統(tǒng)的性能得到了很大的提高,但同時也帶來了一些問題。比如,由于數(shù)據(jù)將存在于系統(tǒng)中的不同的物理位置,可能造成數(shù)據(jù)的不一致性;由于寫緩存區(qū)的優(yōu)化作用,可能有些寫操作的執(zhí)行順序不是用戶期望的順序,從而造成操作錯誤。
15.3.1??Cache的分類
Cache有多種構造方法。在最高層次,微處理器可以采用下面兩種組織中的一組。
(1)統(tǒng)一Cache。指令和數(shù)據(jù)用同一個Cache。結構如圖15.8所示。
圖15.8??統(tǒng)一的指令Cache和數(shù)據(jù)Cache
(2)指令和數(shù)據(jù)分開的Cache。有時這種組織方式也被稱為改進的哈佛結構。
圖15.9顯示了這種組織方式。
這兩種組織方式各有優(yōu)缺點。統(tǒng)一Cache能夠根據(jù)當前程序的需要自動調(diào)整指令在Cache存儲器的比例,比固定劃分的有更好的性能。另一方面,分開的Cache使Load/Store指令能夠單周期執(zhí)行。
15.3.2??Cache性能的衡量
只有當所需要的Cache存儲器內(nèi)容已經(jīng)在Cache時,微處理器才能以高時鐘速率工作。因此,系統(tǒng)的總體性能就可以用存儲器訪問中命中Cache的比例來衡量。當要訪問的內(nèi)容在Cache時稱為命中(hit),而要訪問的內(nèi)容不在Cache時稱為未命中(miss)。在給定時間間隔內(nèi),Cache命中的次數(shù)與總的存儲器請求次數(shù)的比值被稱為命中率。
圖15.9??指令Cache和數(shù)據(jù)分開的Cache
命中率用下面的公式進行計算:
命中率=(Cache命中次數(shù)÷存儲器請求次數(shù))×100%
未命中率與命中率形式相似,即在給定時間間隔內(nèi),Cache未命中的總次數(shù)除以總的存儲器請求次數(shù)所得的百分比。未命中率與命中率之和等于100。
目前設計良好的處理器,Cache的未命中率只有百分之幾。未命中率依賴多個Cache參數(shù),包括Cache大小和組織。
?
15.3.3??Cache工作原理
Cache的基本存儲單元為Cache行(Cache?line)。存儲系統(tǒng)把Cache和主存儲器都劃分為相同大小的行。Cache與主存儲器交換數(shù)據(jù)是以行為基本單位進行的。每一個Cache行都對應于主存中的一個存儲塊(memory?block)。
Cache行的大小通常是2L字節(jié)。通常情況下是16字節(jié)(4個字)和32字節(jié)(8個字)。如果Cache行的大小為2L字節(jié),那么對主存的訪問通常是2L字節(jié)對齊的。所以對于一個虛擬地址來說,它的bit[31∶L]位,是Cache行的一個標識。當CPU發(fā)出的虛擬地址的bit[31∶L]和Cache中的某行bit[31∶L]相同,那么Cache中包含CPU要訪問的數(shù)據(jù),即成為一次Cache命中。
為了加快Cache訪問的速度,又將多個Cache行劃分成一個Cache組(Cache?Set)。Cache組中包含的Cache行的個數(shù)通常也為2的N次方的倍數(shù)。為了方便起見,取N=S。這樣,一個Cache組中就包含2S個Cache行。這時,虛擬地址中的bit[L+S-1∶L]為Cache組的標識。虛擬地址中余下的位bit[31∶L+S]成為一個Cache標(Cache-tag)。它標識了Cache行中的內(nèi)容和主存間的對應關系。
圖15.10顯示了Cache的訪問過程。
圖15.10??Cache訪問過程
15.3.4??Cache與主存的關系
在Cache中采用地址映射將主存中的內(nèi)容映射到Cache地址空間。具體的說,就是把存放在主存中的程序按照某種規(guī)則裝入到Cache中,并建立主存地址到Cache地址之間的對應關系。而地址變換是指當程序已經(jīng)裝入到Cache后,在實際運行過程中,把主存地址變換成Cache地址。
地址的映射和變換是密切相關的。采用什么樣的地址映射方法,就必然有與之對應的地址變換。
常用的地址映射和變換方式包括直接映射和變換方式、組相聯(lián)映射和變換方式以及全相聯(lián)和變換方式。
(1)直接(direct-mapped)映射方式
直接映射是一種最簡單,也是最直接的映射方式。主存中的每個地址都對應Cache存儲器中惟一的一行。由于主存的容量遠遠大于Cache存儲器,所以在主存中很多地址被映射到同一個Cache行。
圖15.11顯示了主存與Cache的直接映射關系。
圖15.11??主存和Cache的直接映射
直接映射Cache是一種簡單的解決方法,但這種設計使得每個主存塊在Cache中只有一個特定的行可以存放。如果程序同時用到對應于Cache同一行的兩個主存塊,那么就會發(fā)生沖突,沖突的結果是導致Cache行的頻繁變換。這種由直接映射導致的Cache存儲器中的軟件沖突稱為顛簸(thrashing)問題。
(2)組相聯(lián)映射方式
為了減少顛簸問題,有些Cache使用了組相聯(lián)的映射策略。在組相聯(lián)的地址映射和變換中,把主存和Cache按同樣大小劃分成組(set),每個組都由相同的行數(shù)組成。
由于主存的容量比Cache容量大得多,因此,主存的組數(shù)要比Cache的組數(shù)多。從主存的組到Cache的組之間采用直接映射方式。主存中的一組與Cache中的一組之間建立了之間映射方式后,在兩個對應的組內(nèi)部采用全相聯(lián)映射方式。
在ARM中采用的是組相聯(lián)的地址映射和變換方式。如果Cache的行大小為2L,則同一行中各地址的bit[31∶L]是相同的。如果Cache中組的大?。拷M中包含的行數(shù))為2S,則虛地址位bit[L+S∶L]用于選擇Cache中的某個組。
圖15.12顯示了一個Cache與主存儲器的組相聯(lián)映射
圖15.12??Cache與主存儲器組相聯(lián)映射
擁有相同組索引的Cache行稱為組相聯(lián)的(set?associative)。主存中的程序或代碼段可以在不影響程序執(zhí)行的情況下被分配到Cache中的某一組中。也就是說,將數(shù)據(jù)或代碼存入Cache行中的操作不會影響程序的執(zhí)行。
(3)全相聯(lián)映射方式
隨著Cache控制器的相聯(lián)度的提高,沖突的可能性減少了。理想的目標是,盡量提高組相聯(lián)程度,使主存地址能夠映射到任意Cache行。這樣的Cache被稱為全相聯(lián)Cache。然而,隨著相聯(lián)度的提高,與之相匹配的硬件的復雜度也在提高。硬件設計者提高Cache相聯(lián)度的一種方法就是使用內(nèi)容尋址寄存器CAM(Content?Addressable?Memory)。
CAM使用一組比較器,以比較輸入的標簽地址和存儲在每一個有效Cache行中的標簽位。CAM采取了與RAM相反的工作方式;RAM在得到一個地址后再給出數(shù)據(jù);而CAM則是在檢測到給定的數(shù)據(jù)值在存儲器中后,再給出該數(shù)據(jù)的地址。使用CAM允許同時比較更多的地址中的標簽位,從而增加了可以包含在一個組中的Cache行數(shù)。
在ARM920T和ARM940T存儲器核中,ARM使用了CAM來定位地址中的標簽域。ARM920T和ARM940T中的Cache是64組組相聯(lián)的。圖15.13所示為ARM940T的Cache結構圖。Cache控制器把地址標簽域作為CAM的輸入,它的輸出選擇了包含有效Cache行的組。
圖15.13??ARM940T64路組相聯(lián)Cache
訪問地址的標簽部分被作為4個CAM的輸入,輸入標簽的同時與存儲在64組中的所有Cache標簽比較。如果有一個匹配,那么數(shù)據(jù)就由Cache寄存器提供;如果沒有匹配,存儲器就會產(chǎn)生一個失效(misss)信號。
控制器使用組索引位(set?index)在4個CAM中選擇一個。被選中的CAM會在Cache存儲器中選擇一個Cache行,該地址的數(shù)據(jù)索引部分(data?index)在該Cache行中選擇出所需的字、半字或者字節(jié)。
15.3.5??Cache的寫策略
當CPU更新了Cache內(nèi)容時,要將結果寫回到主存中,通常有兩種方法:
·??直寫法(write-through);
·??回寫法(write-back)。
直寫法是指,當CPU在執(zhí)行寫操作時,必須把數(shù)據(jù)同時寫入Cache和主存,以確保Cache和主存數(shù)據(jù)一致。在這種寫策略下,處理器在每次寫Cache時也要寫相應的主存單元。由于要訪問主存,直寫法的速度比回寫法要慢一些。
回寫法是指,當處理器和寫Cache命中時,只向Cache存儲器寫數(shù)據(jù),而不立即寫入主存。這樣,主存儲器與相應的Cache行數(shù)據(jù)有可能不一致。Cache中的數(shù)據(jù)是新的,而主存中的數(shù)據(jù)可能是較早的、沒有被更新過的。
配置成回寫法的Cache要使用Cache行的狀態(tài)信息塊中的一個或多個臟位(dirty?bit)。當回寫Cache控制器向Cache存儲器中的某一行寫入數(shù)據(jù)時,它會將臟位設置為1。如果控制器內(nèi)核此后訪問該Cache行,那么通過臟位的狀態(tài)就可以知道該Cache行中含有主存儲器中沒有的數(shù)據(jù)。如果Cache控制器要將一個臟位被設置的Cache行替換出Cache存儲器,那么該Cache行數(shù)據(jù)會自動被寫入主存單元中。控制器通過這種方法來防止只存在于Cache中而主存中沒有的重要信息的丟失。
表15.12比較了直寫法和回寫法的優(yōu)缺點。
表15.12 直寫法與回寫法
寫??策??略 |
直??寫??法 |
回??寫??法 |
可靠性 |
高 |
低 |
與主存的通信量 |
多 |
少 |
控制的復雜性 |
簡單 |
復雜 |
硬件實現(xiàn)代價 |
大 |
小 |
下面分析產(chǎn)生這些性能差異的原因。
·??可靠性。直寫法要優(yōu)于回寫法。這是因為直寫法始終保證Cache是主存的正確副本。當Cache發(fā)生錯誤時,可以從主存中糾正。
·??與主存的通信量。一般情況下,回寫法少于直寫法。這是因為,一方面,Cache的命中率很高,對于回寫法來說,CPU絕大多數(shù)操作只需要寫Cache,不必寫主存。另一方面,當Cache失效時,要將Cache中的行替換到主存,而直寫法每次只寫一個字到主存。總的來說,由于直寫法在每次寫Cache時,同時寫主存,從而增加了寫操作的開銷。而回寫法是把與主存的數(shù)據(jù)交換集中到一次主存操作,可能要一次性的進行多個字的操作。
·??控制的復雜性。直寫法必回寫法簡單。直寫法在Cache的行狀態(tài)表中不需要修改位。同時,直寫法的糾錯技術相對簡單。
·??硬件代價。回寫法比直寫法好。因為直寫法中,每次寫操作都要寫主存,因此為了節(jié)省寫主存所花費的時間,通常要采用一個高速小容量的緩存存儲器,把要寫的數(shù)據(jù)和地址寫到這個緩存中。在每次讀主存時,也要首先判斷所讀的數(shù)據(jù)是否在這個緩存中。而回寫法不需要上述操作,相對硬件代價要小。
?
15.3.6??Cache的替換策略
在Cache訪問過程中,發(fā)現(xiàn)查找的Cache行已經(jīng)失效,則需要從主存中調(diào)入新的行到Cache中。在采用組相聯(lián)的Cache中,一個來自主存的行可以放入多個Cache組中。當所有組中的對應行都已經(jīng)裝滿時,就要使用Cache替換算法,從這些組中找出一個Cache,把它調(diào)回到主存中原來存放它的地方,騰出新行來存放新調(diào)入的行。被選中替換的Cache行被稱為丟棄者(victim)。如果丟棄者中包含有效的臟數(shù)據(jù),那么在該行被寫入新數(shù)據(jù)之前,控制器必須把該行中的數(shù)據(jù)寫到主存。選擇和替換丟棄Cache行的過程被稱為淘汰(eviction)。
Cache控制器選擇下一個丟棄Cache行的策略被稱為替換策略。在ARM常用的替換算法有兩種:輪轉算法和隨機替換算法。
輪轉算法又叫循環(huán)法,這種算法維護一個邏輯計數(shù)器,每進行一次替換,計算器加1,當計算器達到最大值時,就被復位成預先定義好的一個基值。這種算法容易預測最壞情況下的Cache性能。但它一個明顯缺點就是,在程序發(fā)生很小變化時,可能造成Cache性能急劇下降。
隨機算法從特定的位置上隨機地選出一行替換出去。它通過一個隨機發(fā)生器來完成上述操作。當每次需要替換Cache行時,隨機發(fā)生器將產(chǎn)生一個隨機數(shù),用新行將編號為該隨機數(shù)的行替換出去。這種算法與輪轉算法最大的區(qū)別在于它在每次產(chǎn)生替換行時,增加的是一個非連續(xù)值,這個值是由控制器隨機產(chǎn)生的。同樣,當丟棄計算器達到最大值時,會被復位成預先定義好的一個基值。
相比之下,隨機算法沒有考慮到程序的局部性特點,因而效果有時不盡人意,同時這種算法不易預測最壞情況下Cache性能。而輪轉法就有更好的可預測性,容易預測最壞情況下Cache性能,在一些實時系統(tǒng)中,十分重視這一點。但是,輪轉法替換策略在存儲器訪問發(fā)生很小變化時,可能造成Cache性能有較大變化。
表15.13顯示了目前比較流行的ARM核所使用的策略。
表15.13 常見ARM核使用的替換策略
內(nèi)????核 |
寫??策??略 |
替?換?策?略 |
ARM720T |
直寫法 |
隨機 |
ARM740T |
直寫法 |
隨機 |
ARM920T |
直寫法、回寫法 |
隨機、輪轉 |
ARM940T |
直寫法、回寫法 |
隨機 |
ARM926EJ-S |
直寫法、回寫法 |
隨機、輪轉 |
ARM946E |
直寫法、回寫法 |
隨機、輪轉 |
ARM1020E |
直寫法、回寫法 |
隨機、輪轉 |
ARM1026EJS |
直寫法、回寫法 |
隨機、輪轉 |
Intel?Strong?ARM |
回寫法 |
輪轉 |
Intel?Xscale |
直寫法 |
輪轉 |
?
15.3.7??與Cache相關的編程接口
與Cache編程相關的CP15的寄存器共有3個,它們分別為c1、c7及c9。
(1)寄存器c1中與Cache相關的位
c1寄存器在前面CP15寄存器一節(jié)中已經(jīng)介紹過,下面對Cache的控制位進行詳細介紹。
表15.14顯示了c1中與Cache有關位的作用。
表15.14 c1中與Cache相關的位
相??關??位 |
作????用 |
C(bit[2]) |
當數(shù)據(jù)Cache和指令Cache分開時,本控制位禁止/使能數(shù)據(jù)Cache 當數(shù)據(jù)Cache和指令Cache統(tǒng)一時,本控制位禁止/使能整個Cache 0:禁止Cache 1:使能Cache 如果系統(tǒng)中不含Cache,讀取時該位返回0,寫入時忽略該位 當系統(tǒng)中Cache不能禁止時,讀取返回1,寫入時忽略該位 |
續(xù)表
相??關??位 |
作????用 |
I(bit[12]) |
當數(shù)據(jù)Cache和指令Cache是分開的,本控制位禁止/使能指令Cache 0:禁止指令Cache 1:使能指令Cache 如果系統(tǒng)中使用統(tǒng)一的指令Cache和數(shù)據(jù)Cache或者系統(tǒng)中不含Cache,讀取該位時返回0,寫入時忽略該位 當系統(tǒng)中的指令Cache不能禁止時,讀取該位返回1,寫入時忽略該位 |
RR(bit[14]) |
如果系統(tǒng)中Cache的淘汰算法可以選擇的話,本控制位選擇淘汰算法 0:選擇常規(guī)的淘汰算法,如隨機淘汰算法 1:選擇預測性的淘汰算法,如輪轉(round-robin)淘汰算法 如果系統(tǒng)中淘汰算法不可選擇,寫入該位時被忽略,讀取該位時,根據(jù)其淘汰算法可以簡單地預測最壞情況,并返回1或者0 |
?
(2)寄存器c7
CP15中的寄存器c7主要用于控制Cache和寫緩存。
注意 |
c7有時也用于其他相似的功能,如果系統(tǒng)中存在預測緩存(prefetch?buffers)和分支目標(branch?target)Cache,c7也將負責對它們進行控制。 |
c7是一個只寫存儲器,可以使用協(xié)處理器指令MCR對其進行操作。如果程序中包含讀c7的操作,那么指令的結果不可預知。
使用MCR指令寫該寄存器的命令格式如下所示。
MCR??P15,0,<Rd>,<c7>,<CRm>,<opcode2>
其中,CRm和opcode2的不同組合,決定指令執(zhí)行的不同操作。具體組合與操作的對應關系見表15.15。
表15.15 CRm與opcode2不同組合與操作的應用關系
CRm |
Opcode2 |
含????義 |
數(shù)????據(jù) |
c0 |
4 |
等待中斷 |
0(SBZ,should?be?zero) |
c5 |
0 |
使整個指令Cache無效 |
0 |
c5 |
1 |
使指令Cache中某行無效 |
虛擬地址 |
c5 |
2 |
使指令Cache中某行無效 |
組號/索引 |
c5 |
4 |
清空預取緩存區(qū) |
0 |
c5 |
6 |
清空整個分支目標Cache |
0 |
c5 |
7 |
清空分支目標Cache中的某入口項 |
生產(chǎn)商定義 |
c6 |
0 |
使整個數(shù)據(jù)Cache無效 |
0 |
續(xù)表
CRm |
Opcode2 |
含????義 |
數(shù)????據(jù) |
c6 |
1 |
使數(shù)據(jù)Cache中的某行無效 |
虛擬地址 |
c6 |
2 |
使數(shù)據(jù)Cache中的某行無效 |
組號/索引 |
c7 |
0 |
使整個統(tǒng)一Cache無效 哈佛結構中,使整個數(shù)據(jù)Cache和指令Cache無效 |
0 |
c7 |
1 |
使統(tǒng)一Cache中某行無效 |
虛擬地址 |
c7 |
2 |
使統(tǒng)一Cache中某行無效 |
組號/索引 |
c8 |
2 |
等待中斷 |
0 |
c10 |
1 |
清理數(shù)據(jù)Cache行 |
虛擬地址 |
c10 |
2 |
清理數(shù)據(jù)Cache行 |
組號/索引 |
c10 |
4 |
清除寫緩存區(qū) |
0 |
c11 |
1 |
清理統(tǒng)一Cache行 |
虛擬地址 |
c11 |
2 |
清理統(tǒng)一Cache行 |
組號/索引 |
c13 |
1 |
預取指令Cache中的某行 |
虛擬地址 |
c14 |
1 |
清理并使數(shù)據(jù)Cache中的某行無效 |
虛擬地址 |
c14 |
2 |
清理并使數(shù)據(jù)Cache中的某行無效 |
組號/索引 |
c15 |
1 |
清理并使統(tǒng)一Cache中的某行無效 |
虛擬地址 |
c15 |
2 |
清理并使統(tǒng)一Cache中的某行無效 |
組號/索引 |
(3)寄存器c9
將Cache進入存儲系統(tǒng)的注意目的是要提高系統(tǒng)的平均訪問速度。但Cache是一把雙刃劍,在某些情況下,可能使系統(tǒng)的性能更遭。下面列出了3種使Cache性能明顯下降的原因。
①?Cache訪問未命中,處理器轉向主存尋址數(shù)據(jù),這期間的延時對系統(tǒng)性能影響很大。
②?在回寫型Cache中,如果Cache中的數(shù)據(jù)所在地址被存儲管理單元重新定位(即Cache中存儲的為虛地址數(shù)據(jù)),那么數(shù)據(jù)回寫的操作延時很大。
③?當處理器需要一個字節(jié)數(shù)據(jù),而此數(shù)據(jù)恰好不在Cache中,那么Cache的替換策略就會將整個Cache行換進,增加了系統(tǒng)不必要的開銷。
?
以上3點對實時系統(tǒng)來說,影響更為明顯。
為了減少這種不利的影響,在ARM系統(tǒng)中引入了Cache內(nèi)容鎖定技術。這種技術允許編程人員人為地將一些關鍵代碼或數(shù)據(jù)預取到Cache中后,通過寄存器操作對其設定一定的屬性,這樣當有Cache未命中發(fā)生,需要進行Cache替換時,將這些數(shù)據(jù)保護起來,使這些關鍵代碼或數(shù)據(jù)不會被換出。
這種策略在很大程度上保證了處理器對關鍵代碼或數(shù)據(jù)訪問時的性能。
Cache的鎖定操作是分組塊(block)為單位進行的,它的分塊方法如下。
為了敘述方便,作下述假設。
L(Length?of?the?Line):Cache的基本存儲單元行的大小。
A(Associativity):表示每個Cache組中的行數(shù)。
N(Number?of?Sets):Cache中的組數(shù)。
M表示Cache中的鎖定塊。
每個鎖定塊(lockdown?block)包括Cache每組中的一行。這樣Cache中共有A個鎖定塊,其編號為從0到A-1。其中編號為0的鎖定塊中包含Cache組0中的0#行,組1中的0#行,直到組A-1中的0#行。依此類推,鎖定塊1包含Cache組0中的1#行,組1中的1#行,直到組A-1中的1#行。這樣每個鎖定塊中包含了N個Cache行。
當編號為0~M的鎖定塊被鎖定在Cache中,編號為M+1~A的鎖定塊可以用于正常的Cache替換操作。
注意 |
編程中不能將全部Cache鎖定,至少要留出一個未鎖定的塊來支持存儲器的正常操作。 |
每一個鎖定塊都包含有N個不同組中的Cache行。建議程序在使用Cache鎖定時,使每個Cache塊中的N個Cache行映射的為存儲器中的連續(xù)地址。也就是說,存儲器中N×L大小的聯(lián)系區(qū)域被映射到Cache中鎖定,這塊區(qū)域是Cache行邊界對齊的(如果一個Cache行包含4字節(jié),那么被鎖定的區(qū)域要是4字節(jié)對齊的,如果一個Cache行包含8字節(jié),那么被鎖定的Cache行就是8字節(jié)對齊的)。
在ARM的存儲管理體系中,主要依靠系統(tǒng)協(xié)處理器和協(xié)處理器的寄存器c9來實現(xiàn)和管理Cache鎖定。如果系統(tǒng)中使用的是數(shù)據(jù)和指令分離的Cache,那么就依靠協(xié)處理器指令MCR和MRC中的<opcode>2來區(qū)分:
·??<opcode>=0使用數(shù)據(jù)Cache鎖定寄存器;
·??<opcode>=1使用指令Cache鎖定寄存器。
如果系統(tǒng)使用的是數(shù)據(jù)和指令統(tǒng)一的Cache,那么<opcode2>要設置成0。
另外,無論是MCR指令還是MCR指令,指令中的<CRm>通常設為c0。
寄存器c9有兩種主要的格式:格式A和格式B。
格式A的編碼如圖15.14所示。
圖15.14??格式A編碼
?
程序員通過指令對寄存器中的Cache組內(nèi)行號進行操作。讀取格式A的寄存器c9,將返回最后一次寫入寄存器c9的值。將數(shù)據(jù)index寫入寄存器c9,就是對要鎖定的Cache行進行設置。當用MCR指令向寄存器寫入數(shù)據(jù)時,執(zhí)行以下操作。
①?當下一次發(fā)生Cache未命中時,將預取的存儲器行存入Cache中與該行相對應的組中編號為index的Cache行中。
②?這時被鎖定的Cache塊包括序號為0~index-1的鎖定塊。當發(fā)生Cache替換時,從編號為index到A-1的塊中選擇被替換的塊。
格式B的編碼如圖15.15所示。
程序員通過指令對寄存器中的Cache組內(nèi)行號進行操作。讀取格式B的寄存器c9,將返回最后一次寫入寄存器c9的值。將數(shù)據(jù)index寫入寄存器c9,就是對要鎖定的Cache行進行設置。當用MCR指令向寄存器寫入數(shù)據(jù)時,執(zhí)行以下操作。
圖15.15??格式B編碼
①?當L=0時,如果方式Cache未命中,將預取的存儲行存入Cache中與該行對應的組中序號為index的Cache行中。
②?當L=1時,如果本次寫操作之前L=0,并且index值小于本次寫入的index,本次寫操作執(zhí)行的結果不可預知;否則,這時被鎖定的Cache塊包括序號為0~index-1的塊。當發(fā)生Cache替換時,從序號為index~A-1的塊中選擇被替換的塊。
下面以鎖定塊N來說明要鎖定一個Cache塊的步驟。
①?首先確保在下面的整個Cache鎖定過程不會被中斷打斷。如果程序要求中斷不能關閉,那么必須確保被打開的中斷相關代碼和數(shù)據(jù)位于非緩存(uncachable)的存儲區(qū)域。
關中斷的典型做法如下所示。
MRS??r2,CPSR????????????????????????;讀出當前程序狀態(tài)字?CPSR
ORR??r2,r2,#?0x000000C0???????????;關中斷
MSR??CPSR_cxsf,r2??????????????????;設置當前程序狀態(tài)字
②?如果鎖定是指令Cache或者統(tǒng)一Cache,必須保證鎖定過程所執(zhí)行的代碼位于非緩存的存儲域。
③?如果鎖定的是數(shù)據(jù)Cache或者統(tǒng)一的Cache,必須保證鎖定過程所執(zhí)行的數(shù)據(jù)位于非緩存的存儲域。
④?保證要鎖定的代碼和數(shù)據(jù)位于緩存的存儲區(qū)域中。
⑤?如果要鎖定的代碼和數(shù)據(jù)不在Cache中,使用Cache清除或清理指令,將其置換到Cache中。
⑥?N次循環(huán)執(zhí)行下面的操作。
·??index=I寫入寄存器c9,當使用B格式的鎖定寄存器時,令L=0。
·??如果鎖定的是數(shù)據(jù)Cache或數(shù)據(jù)和指令統(tǒng)一Cache,使用LDR指令將數(shù)據(jù)從內(nèi)存讀出,這個讀操作將使要鎖定的內(nèi)容存在于Cache行中。
·??如果鎖定的是指令Cache,那么要借助c7寄存器,相關指令詳細內(nèi)容,參見c7寄存器一節(jié)。
⑦??將index=N寫入寄存器c9,當使用B格式的鎖定寄存器時,令L=0。
如果要解除對N鎖定塊的鎖定,執(zhí)行以下操作。
·??將index=0寫入寄存器c9。
·??當使用格式B的鎖定寄存器時,令L=0。
15.3.8??內(nèi)存一致性
當一個系統(tǒng)中同時使用了Cache、寫緩存時,同一地址的數(shù)據(jù)可能同時出現(xiàn)在包括系統(tǒng)內(nèi)存在內(nèi)的多個不同的物理位置中。如果Cache引入了哈佛架構,使用數(shù)據(jù)和指令分類的Cache,那情況將更復雜。
由于上述存儲系統(tǒng)的多樣性特點,當從內(nèi)存中讀取數(shù)據(jù)時,不能保證讀取的是數(shù)據(jù)的最新值(即有可能出現(xiàn)下述情況:寫操作將數(shù)據(jù)寫入到Cache中,但更新數(shù)據(jù)還沒有被回寫到內(nèi)存)。
ARM存儲系統(tǒng)中,數(shù)據(jù)不一致問題一方面可以通過存儲系統(tǒng)自動保證解決,另一方面編寫程序時要遵循一定的規(guī)則,防止數(shù)據(jù)不一致性發(fā)生。
下面就幾個常出現(xiàn)數(shù)據(jù)不一致的地方進行討論。
·??地址映射發(fā)生變化時
·??指令和數(shù)據(jù)分離的Cache
·??系統(tǒng)執(zhí)行DMA(Direct?Memory?Access)操作
(1)地址映射發(fā)生的變換
當系統(tǒng)中使用MMU時,Cache行對應的地址可能是:
①?內(nèi)存中的實際地址;
②?經(jīng)過地址轉換后的虛擬地址。
如果查詢Cache時相聯(lián)地址比較使用的是虛擬地址,則當系統(tǒng)地址到物理地址的映射發(fā)生變換時,可能造成Cache中數(shù)據(jù)與主存中的不一致。
同時,當系統(tǒng)中使用了寫緩存,處理器對寫緩存中的數(shù)據(jù)處理也是按虛擬地址進行的,所以同樣會發(fā)生數(shù)據(jù)不統(tǒng)一的問題。比如,當前處理器使用虛擬地址向某個內(nèi)存單元寫數(shù)據(jù),該寫操作已經(jīng)將虛擬地址和數(shù)據(jù)寫入到寫緩存區(qū)中,此時,虛擬地址到物理地址的映射關系發(fā)生變換,使先前要寫入數(shù)據(jù)的虛擬地址發(fā)生了變化,當寫緩存將上面被延時的寫操作寫到主存時,使用的是變換后的地址,從而寫操作執(zhí)行失敗。
為了避免發(fā)生這種數(shù)據(jù)不統(tǒng)一的情況,在系統(tǒng)虛擬地址到物理地址的映射關系發(fā)生變換前,根據(jù)系統(tǒng)的具體情況,執(zhí)行下面的操作序列中的一種或幾種。
·??如果數(shù)據(jù)Cache為寫回型Cache,清空該數(shù)據(jù)Cache。
·??使數(shù)據(jù)Cache中相應的行無效。
·??使指令Cache中相應的行無效。
·??將寫緩存區(qū)中被延時的操作全部執(zhí)行。
·??有些情況可能還要求相關的存儲區(qū)域被置換成非緩存的。
?
(2)指令Cache
當系統(tǒng)中采用分離的數(shù)據(jù)Cache和指令Cache時,下面的幾種情況可能造成指令不一致情況的發(fā)生。
①?地址為A1的指令被預取,該指令的數(shù)據(jù)行被取到Cache中。
②?和A1同在一個數(shù)據(jù)行的地址為A2的數(shù)據(jù)被一條存儲器寫操作修改。這個數(shù)據(jù)寫操作可能影響數(shù)據(jù)Cache中、寫緩存中和主存的地址為A2的存儲單元內(nèi)容,但不影響指令Cache中地址為A2的存儲單元中的內(nèi)容。
③?如果地址A2存放的是指令,當該指令執(zhí)行時,就可能發(fā)生指令不一致問題。如果地址A2所在的行還在指令Cache中,系統(tǒng)將執(zhí)行修改前的指令;如果地址A2所在的行不在指令Cache中,地址將執(zhí)行修改后的指令。
為了避免這種指令不一致的情況發(fā)生,要在地址A2的數(shù)據(jù)被修改前執(zhí)行一些防護性的操作。也就是說,在步驟①和②之間插入下面必要的操作。
·??如果系統(tǒng)中使用的數(shù)據(jù)、指令統(tǒng)一的Cache,程序跳到步驟②繼續(xù)執(zhí)行。
·??對于使用數(shù)據(jù)和指令分離Cache的系統(tǒng),使指令Cache的內(nèi)容無效。
·??對于使用數(shù)據(jù)和指令分離Cache的系統(tǒng),如果數(shù)據(jù)Cache是寫回類型的,清空數(shù)據(jù)Cache。
上述操作系列可作為一種標準,應用于一些典型的場合。
注意 |
當可執(zhí)行文件加載到主存中后,在程序跳轉到入口點處開始執(zhí)行之前,先執(zhí)行上述操作序列,以保證新加載的可執(zhí)行代碼正確執(zhí)行。 |
(3)DMA造成的數(shù)據(jù)不一致
DMA操作直接訪問內(nèi)存,不更新Cache和寫緩存區(qū)中相應內(nèi)容,這樣就很可能造成數(shù)據(jù)不一致。
為了避免DMA造成的數(shù)據(jù)不統(tǒng)一,根據(jù)系統(tǒng)情況,執(zhí)行下面操作的一種和幾種。
·??將DMA訪問的存儲器設置成非緩存的
·??將DMA訪問的存儲區(qū)所涉及的數(shù)據(jù)Cache中的行設置成無效,或者清空數(shù)據(jù)Cache。
·??清空寫緩存區(qū)(將寫緩存區(qū)中延時操作全部執(zhí)行)。
·??在DMA訪問期間限制存儲器訪問DMA所訪問的存儲區(qū)域。
15.3.9??Cache初始化子程序示例
下面給出了一段例子代碼,此代碼以ARM740T芯片為參考,顯示了Cache初始化的標準過程。
;下面代碼必須運行于處理器的特權模式下。
????????AREA???INIT740,?CODE,?READONLY ;設置段屬性
????????ENTRY
????????EXPORT?Cache_Init ;以便作為子程序被其他程序使用
Cache_Init
;禁止MMU/MPU
;清理數(shù)據(jù)Cache
;
;
???????MRC?????p15,?0,?r0,?c1,?c0,?0 ;讀CP15寄存器c1到r0
???????BIC?????r0,?r0,?#0x1 ;清除bit[0]?
???????MCR?????p15,?0,?r0,?c1,?c0,?0 ;將設置的新值寫回
???????MOV?????r0,#0 ;準備禁止其他域
???????MCR?????p15,?0,?r0,?c6,?c1,?0
???????MCR?????p15,?0,?r0,?c6,?c2,?0
???????MCR?????p15,?0,?r0,?c6,?c3,?0
???????MCR?????p15,?0,?r0,?c6,?c4,?0
;???????MCR?????p15,?0,?r0,?c6,?c5,?0
;???????MCR?????p15,?0,?r0,?c6,?c6,?0
;???????MCR?????p15,?0,?r0,?c6,?c7,?0??
;
;?區(qū)域0:背景區(qū):從0x0地址開始的4GB存儲空間
;?區(qū)域1:SRAM區(qū):從0x0地址開始的0x4000字節(jié)存儲空間
;?區(qū)域2:FLASH:從0x24000000開始的0x02000000字節(jié)存儲空間
;?區(qū)域3:外設區(qū):從0x10000000地址開始的0x10000000字節(jié)存儲空間
;開啟?region?0?
?????????MOV?????r0,#2_111111
?????????MCR?????p15,?0,?r0,?c6,?c0,?0 ;?region?0區(qū)域0
;?開啟region?1
?????????MOV?????r0,#2_100011
?????????MCR?????p15,?0,?r0,?c6,?c1,?0 ;?region?1區(qū)域1
;?開啟region?2
?????????LDR?????r0,=2_110001+0x24000000
?????????MCR?????p15,?0,?r0,?c6,?c2,?0 ;?region?2區(qū)域2
;?開啟region?3
?????????LDR?????r0,=2_110111?+?0x10000000
?????????MCR?????p15,?0,?r0,?c6,?c3,?0 ;?region?3區(qū)域3
;?開啟Cache/寫緩存?
?????????MOV?????r0,?#2_0110
?????????MCR?????p15,?0,?r0,?c2,?c0,?0 ;Cache
?????????MCR?????p15,?0,?r0,?c3,?c0,?0 ;寫緩存
;?開啟access?允許
?????????MOV?????r0,?#2_11111100
?????????MCR?????p15,?0,?r0,?c5,?c0,?0 ;允許訪問
;
;?設置全局配置?
;
?????????MRC?????p15,?0,?r0,?c1,?c0,?0 ;讀CP15寄存器到r0
?????????ORR?????r0,?r0,?#(0x1?<<2)? ;開啟Cache????????
?????????ORR?????r0,?r0,?#0x1 ;開啟MPU
?
;
;增加的配置選項
;????????
;????????ORR?????r0,?r0,?#(0x1?<<13)? ;開啟Hi?Vectors
;????????ORR?????r0,?r0,?#(0x1?<<7)? ;開啟大端模式
?????????MCR?????p15,?0,?r0,?c1,?c0,?0 ;寫CP15寄存器c1
?????????MOV?????pc,lr ;返回
?????????END
上述程序端可作為參考,可以在程序中直接使用,也可以作為子程序被其他程序調(diào)用。如果作為子程序調(diào)用,下面兩種調(diào)用方式作為參考。
①?在匯編程序中調(diào)用。
????IMPORT?Cache_Init
????BL?Cache_Init
②?在C語言中調(diào)用。
extern?void?Cache_Init(void);
Cache_Init();