本篇基本是韋東山書(shū)上的
一、內(nèi)存管理單元MMU介紹
內(nèi)存管理單元簡(jiǎn)稱(chēng)MMU,它負(fù)責(zé)虛擬地址到物理地址的映射,并提供硬件機(jī)制的內(nèi)存訪(fǎng)問(wèn)權(quán)限檢查。MMU使得每個(gè)用戶(hù)進(jìn)程擁有自己獨(dú)立的地址空間,并通過(guò)內(nèi)存訪(fǎng)問(wèn)權(quán)限的檢查保護(hù)每個(gè)進(jìn)程所用的內(nèi)存不被其他進(jìn)程破壞。
重點(diǎn)就在于地址映射:頁(yè)表的結(jié)構(gòu)與建立、映射的過(guò)程。
1、S3C2440 MMU地址變換過(guò)程
1)地址的分類(lèi)
一個(gè)程序在運(yùn)行之前,沒(méi)有必要全部裝入內(nèi)存,僅需要將那些要運(yùn)行的部分先裝入內(nèi)存,其余部分在用到時(shí)從磁盤(pán)載入,當(dāng)內(nèi)存不足時(shí),再將暫時(shí)不用的部分調(diào)出到磁盤(pán)。
這使得大程序可以在較小的內(nèi)存空間中運(yùn)行,也使得內(nèi)存中可以同時(shí)裝入更多的程序并發(fā)執(zhí)行,這樣的存儲(chǔ)器一般稱(chēng)為虛擬存儲(chǔ)器。
虛擬地址最終需要轉(zhuǎn)換為物理地址才能讀寫(xiě)實(shí)際的數(shù)據(jù),通過(guò)將虛擬地址空間和物理空間劃分為同樣大小的空間(段或頁(yè)),然后兩個(gè)空間建立映射關(guān)系。
由于虛擬地址空間遠(yuǎn)大于物理地址,可能多塊虛擬地址空間映射到同一塊物理地址空間,或者有些虛擬地址空間沒(méi)有映射到具體的物理地址空間上去(使用到時(shí)再映射)。
ARM cpu地址轉(zhuǎn)換涉及三種地址:虛擬地址(VA,Virtual Address)、變換后的虛擬地址(MVA,Modified Virtual Address)、物理地址(PA,Physical Address)
沒(méi)有啟動(dòng)MMU時(shí),CPU核心,cache,MMU,外設(shè)等所有部件使用的都是物理地址。
啟動(dòng)MMU后,CPU核心對(duì)外發(fā)出虛擬地址VA;VA被轉(zhuǎn)換為MVA供cache,MMU使用,在這里MVA被轉(zhuǎn)換成PA;最后使用PA讀取實(shí)際設(shè)備
①CPU核心看到和用到的只是虛擬地址VA,至于VA如果去對(duì)應(yīng)物理地址PA,CPU核心不理會(huì)
②caches和MMU看不到VA,他們利用MVA轉(zhuǎn)換得到PA
③實(shí)際設(shè)備看不到VA、MVA,讀寫(xiě)它們使用的是物理地址PA
MVA是除CPU核心外的其他部分看到的虛擬地址,VA與MVA的變化關(guān)系
如果VA<32M,需要使用進(jìn)程標(biāo)識(shí)號(hào)PID(通過(guò)讀CP15的C13獲得)來(lái)轉(zhuǎn)換為MVA
if (VA < 32M) then
MVA = VA | (PID << 25)
else
MVA = VA
使用MVA,而不使用VA的目的是,當(dāng)有重疊的VA時(shí),轉(zhuǎn)換為MVA地址并不重疊,減小轉(zhuǎn)換為PA的代價(jià)
比如兩個(gè)進(jìn)程1、2,VA都是0-(32M-1),則MVA分別為0x02000000-0x03ffffff,0x04000000-0x05ffffff。
下文說(shuō)到虛擬地址,如果沒(méi)有特別指出,就是指MVA
2)虛擬地址到物理地址的轉(zhuǎn)換過(guò)程
arm cpu使用頁(yè)表來(lái)進(jìn)行轉(zhuǎn)換,頁(yè)表由一個(gè)個(gè)條目組成,每個(gè)條目存儲(chǔ)一段虛擬地址對(duì)應(yīng)的物理地址及訪(fǎng)問(wèn)權(quán)限,或者下一級(jí)頁(yè)表的地址
S3C2440最多會(huì)用到兩級(jí)頁(yè)表,以段(Section,1M)的方式進(jìn)行轉(zhuǎn)換時(shí)只用到一級(jí)頁(yè)表,以頁(yè)(Page)的方式進(jìn)行轉(zhuǎn)換時(shí)用到兩級(jí)頁(yè)表。
頁(yè)的大小有3種:大頁(yè)(64KB),小頁(yè)(4KB),極小頁(yè)(1KB)。條目也稱(chēng)為描述符,有:段描述符、大頁(yè)描述符、小頁(yè)描述符、極小頁(yè)描述符-保存段、大頁(yè)、小頁(yè)、極小頁(yè)的起始物理地址;粗頁(yè)表描述符、細(xì)頁(yè)表描述符,它們保存二級(jí)頁(yè)表的物理地址。
下圖為S3C2440的地址轉(zhuǎn)換圖
一級(jí)頁(yè)表描述符
最低兩位:
0b00:無(wú)效
0b01:粗頁(yè)表(Coarse page table)
[31:10]為粗頁(yè)表基址,此描述符低10位填充0后就是一個(gè)二級(jí)頁(yè)表的物理地址,二級(jí)頁(yè)表含256個(gè)條目(使用[9:2],2^8=256個(gè)),稱(chēng)為粗頁(yè)表(Coarse page table)。其中每個(gè)條目表示4KB大小的物理地址空間,一個(gè)粗頁(yè)表表示1MB物理地址
0b10:段(Section)
[31:20]為段基址,、此描述符低20位填充0后就是一塊1MB物理地址空間的起始地址。MVA[19:0],用來(lái)在這1MB空間中尋址。描述符的位[31:20]和MVA[19:0]構(gòu)成了這個(gè)虛擬地址MVA對(duì)應(yīng)的物理地址
以段的方式進(jìn)行映射時(shí),虛擬地址MVA到物理地址PA的轉(zhuǎn)換過(guò)程如下:
①頁(yè)表基址寄存器位[31:14]和MVA[31:20]組成一個(gè)低兩位為0的32位地址,MMU利用這個(gè)地址找到段描述符
②取出段描述符的位[31:20](段基址),它和MVA[19:0]組成一個(gè)32位的物理地址(這就是MVA對(duì)應(yīng)的PA)
段地址轉(zhuǎn)換過(guò)程
0b11:細(xì)頁(yè)表(Fine page table)
[31:12]為細(xì)頁(yè)表基址(Fine page table base address),此描述符的低12位填充0后,就是一個(gè)二級(jí)頁(yè)表的物理地址。此二級(jí)頁(yè)表含1024個(gè)條目(使用[11:2],10位),其中每個(gè)條目表示大小1kb的物理地址空間,一個(gè)細(xì)頁(yè)表表示1MB物理地址空間
以大頁(yè)(64KB),小頁(yè)(4KB)或極小頁(yè)(1KB)進(jìn)行地址映射時(shí),需要用到二級(jí)頁(yè)表,二級(jí)頁(yè)表有粗頁(yè)表、細(xì)頁(yè)表兩種,二級(jí)頁(yè)表描述符格式如下:
二級(jí)頁(yè)表描述符
最低兩位:
0b00:無(wú)效
0b01:大頁(yè)描述符
位[31:16]為大頁(yè)基址,此描述符的低16位填充0后就是一塊64KB物理地址空間的起始地址粗頁(yè)表中的每個(gè)條目只能表示4KB物理空間,如果大頁(yè)描述符保存在粗頁(yè)表中,則連續(xù)16個(gè)條目都保存同一個(gè)大頁(yè)描述符。類(lèi)似的,細(xì)頁(yè)表中每個(gè)條目只能表示1KB的物理空間,如果大頁(yè)描述符保存在細(xì)頁(yè)表中,則連續(xù)64個(gè)條目都保存同一個(gè)大頁(yè)描述符。
下面以保存在粗頁(yè)表中的大頁(yè)描述符為例,說(shuō)明地址轉(zhuǎn)化那過(guò)程
①頁(yè)表基址寄存器[31:14]和MVA[31:20]組成一個(gè)低兩位為0的32位地址,MMU利用這個(gè)地址找到粗頁(yè)表描述符
②取出粗頁(yè)表描述符的[31:10](即粗頁(yè)表基址),它和MVA[19:12]組成一個(gè)低兩位為0的32位物理地址,通過(guò)這個(gè)地址找到大頁(yè)描述符
③取出大頁(yè)描述符的[31:16](即大頁(yè)基址),它和MVA[15:0]組成一個(gè)32位的物理地址,即MVA對(duì)應(yīng)的PA
步驟②和③中,用于在粗頁(yè)表中索引的MVA[19:12]、用于在大頁(yè)內(nèi)尋址的MVA[15:0]有重合的位[15:12],當(dāng)位[15:12]從0b0000變化到0b1111時(shí),步驟②得到的大頁(yè)描述符相同,所以粗頁(yè)表中有連續(xù)16個(gè)條目保存同一個(gè)大頁(yè)描述符
大頁(yè)的地址轉(zhuǎn)換過(guò)程(大頁(yè)描述符保存在粗頁(yè)表中)
0b10:小頁(yè)描述符
[31:12]為小頁(yè)基址(Small page base address),此描述符的低12位填充0后就是一塊4kb([11:0],一共12位,2^12=4096)物理地址空間的起始地址。粗頁(yè)表中每個(gè)條目表示4kb的物理空間,如果小頁(yè)描述符保存在粗頁(yè)表中,則只需要用一個(gè)條目來(lái)保存一個(gè)小頁(yè)描述符。類(lèi)似的,細(xì)頁(yè)表中每個(gè)條目只能表示1kb的物理空間,如果小頁(yè)保存在細(xì)頁(yè)表中,則連續(xù)4個(gè)條目都保存同一個(gè)小頁(yè)描述符。
下面以保存在粗頁(yè)表中的小頁(yè)描述符為例,說(shuō)明地址轉(zhuǎn)換過(guò)程:
①頁(yè)表基址[31:14]和MVA[31:20]組成一個(gè)低兩位為0的32位地址,MMU利用這個(gè)地址找到粗頁(yè)表描述符
②取出粗頁(yè)表描述符[31:10](即粗頁(yè)表基址),它和MVA[19:12]組成一個(gè)低兩位為0的32位物理地址,用這個(gè)地址找到小頁(yè)描述符
③取出小頁(yè)描述符的位[31:12](即小頁(yè)基址),它和MVA[11:0]組成一個(gè)32位物理地址(即MVA對(duì)應(yīng)的PA)
小頁(yè)描述符保存在細(xì)頁(yè)表中,地址轉(zhuǎn)換過(guò)程和上面類(lèi)似。
小頁(yè)的地址轉(zhuǎn)換過(guò)程(小頁(yè)描述符保存在粗頁(yè)表中)
0b11:極小頁(yè)描述符
[31:10]為極小頁(yè)基址(Tiny page base address),此描述符的低10位填充0后就是一塊1KB物理地址空間的起始地址。極小頁(yè)描述符只能保存在細(xì)頁(yè)表中,用一個(gè)條目來(lái)保存一耳光極小頁(yè)描述符
下面是極小頁(yè)的地址轉(zhuǎn)換過(guò)程:
①頁(yè)表基址寄存器[31:14]和MVA[31:20]組成一個(gè)低兩位為0的32位地址,MMU通過(guò)這個(gè)地址找到細(xì)頁(yè)表描述符
②取出細(xì)頁(yè)表描述符[31:12](即細(xì)頁(yè)表基址),它和MVA[19:10]組成一個(gè)低兩位為0的32位物理地址,通過(guò)這個(gè)地址即可找到極小頁(yè)描述符
③取出極小頁(yè)描述符[31:10](即極小頁(yè)基址),它和MVA[9:0]組成一個(gè)32位的物理地址(即MVA對(duì)應(yīng)的PA)
極小頁(yè)的地址轉(zhuǎn)換過(guò)程(極小頁(yè)描述符保存在粗頁(yè)表中)
從段、大頁(yè)、小頁(yè)、極小頁(yè)的地址轉(zhuǎn)換過(guò)程可知
①以段進(jìn)行映射時(shí),通過(guò)MVA[31:20]結(jié)合頁(yè)表得到一段(1MB)的起始物理地址,MVA[19:0]用來(lái)在段中尋址
②以大頁(yè)進(jìn)行映射時(shí),通過(guò)MVA[31:16]結(jié)合頁(yè)表得到一個(gè)大頁(yè)(64KB)的起始物理地址,MVA[15:0]用來(lái)在小頁(yè)中尋址
③以小頁(yè)進(jìn)行映射時(shí),通過(guò)MVA[31:12]結(jié)合頁(yè)表得到一個(gè)小頁(yè)(4KB)的起始物理地址,MVA[11:0]用來(lái)在小頁(yè)中尋址
④以極小頁(yè)進(jìn)行映射時(shí),通過(guò)MVA[31:10]結(jié)合頁(yè)表得到一個(gè)極小頁(yè)(1KB)的起始物理地址,MVA[9:0]用來(lái)在極小頁(yè)中尋址
2、內(nèi)存的訪(fǎng)問(wèn)權(quán)限檢查
它決定一塊內(nèi)存是否允許讀、是否允許寫(xiě)。這由CP15寄存器C3(域訪(fǎng)問(wèn)控制)、描述符的域(Domain)、CP15寄存器C1的R/S/A位、描述符的AP位共同決定。
“域”決定是否對(duì)某塊內(nèi)存進(jìn)行權(quán)限檢查,“AP”決定如何對(duì)某塊內(nèi)容進(jìn)行權(quán)限檢查。
S3C2440有16個(gè)域,CP15寄存器C3中每?jī)晌粚?duì)應(yīng)一個(gè)域(一共32位),用來(lái)表示這個(gè)域是否進(jìn)行權(quán)限檢查
每?jī)晌粩?shù)據(jù)的含義
00:無(wú)訪(fǎng)問(wèn)權(quán)限(任何訪(fǎng)問(wèn)都將導(dǎo)致“Domain fault”異常)
01:客戶(hù)模式(使用段描述符、頁(yè)描述符進(jìn)行權(quán)限檢查)
10:保留(保留,目前相當(dāng)于“無(wú)訪(fǎng)問(wèn)權(quán)限”)
11:管理模式(不進(jìn)行權(quán)限檢查,允許任何訪(fǎng)問(wèn))
Domain占用4位,用來(lái)表示內(nèi)存屬于0-15,哪一個(gè)域
例如:
①段描述符中的“Domain”為0b0010,表示1MB內(nèi)存屬于域2,如果域訪(fǎng)問(wèn)控制寄存器的[5:4]等于0b00,則訪(fǎng)問(wèn)這1MB空間都會(huì)產(chǎn)生“Domain fault”異常,如果等于0b01,則使用描述符中的“Ap”位進(jìn)行權(quán)限檢查
②粗頁(yè)表中的“Domain”為0b1010,表示1MB內(nèi)存屬于域10,如果域訪(fǎng)問(wèn)控制寄存器的[21:20]等于0b01,則使用二級(jí)頁(yè)表中的大頁(yè)/小頁(yè)描述符中的"ap3"、"ap2"、"ap1"、"ap0"位進(jìn)行權(quán)限檢查,如果等于0b11,則允許任何訪(fǎng)問(wèn),不進(jìn)行權(quán)限檢查。
如下圖:
3、TLB的作用
從MVA到PA的轉(zhuǎn)換需要訪(fǎng)問(wèn)多次內(nèi)存,大大降低了CPU的性能,有沒(méi)有辦法改進(jìn)呢?
程序執(zhí)行過(guò)程中,用到的指令和數(shù)據(jù)的地址往往集中在一個(gè)很小的范圍內(nèi),其中的地址、數(shù)據(jù)經(jīng)常使用,這是程序訪(fǎng)問(wèn)的局部性。
由此,通過(guò)使用一個(gè)高速、容量相對(duì)較小的存儲(chǔ)器來(lái)存儲(chǔ)近期用到的頁(yè)表?xiàng)l目(段、大頁(yè)、小頁(yè)、極小頁(yè)描述符),避免每次地址轉(zhuǎn)換都到主存中查找,這樣就大幅提高性能。這個(gè)存儲(chǔ)器用來(lái)幫助快速地進(jìn)行地址轉(zhuǎn)換,成為轉(zhuǎn)譯查找緩存(Translation Lookaside Buffers, TLB)
當(dāng)CPU發(fā)出一個(gè)虛擬地址時(shí),MMU首先訪(fǎng)問(wèn)TLB。如果TLB中含有能轉(zhuǎn)換這個(gè)虛擬地址的描述符,則直接利用此描述符進(jìn)行地址轉(zhuǎn)換和權(quán)限檢查,否則MMU訪(fǎng)問(wèn)頁(yè)表找到描述符后再進(jìn)行地址轉(zhuǎn)換和權(quán)限檢查,并將這個(gè)描述符填入TLB中,下次再使用這個(gè)虛擬地址時(shí)就直接使用TLB用的描述符。
使用TLB需要保證TLB中的內(nèi)容與頁(yè)表一致,在啟動(dòng)MMU之前,頁(yè)表中的內(nèi)容發(fā)生變化后,尤其要注意。一般的做法是在啟動(dòng)MMU之前使整個(gè)TLB無(wú)效,改變頁(yè)表時(shí),使所涉及的虛擬地址對(duì)應(yīng)的TLB中條目無(wú)效。
4、Cache的作用
同樣基于程序訪(fǎng)問(wèn)的局部性,在主存和CPU通用寄存器之間設(shè)置一個(gè)高速的、容量相對(duì)較小的存儲(chǔ)器,把正在執(zhí)行的指令地址附近的一部分指令或數(shù)據(jù)從主存調(diào)入這個(gè)存儲(chǔ)器,供CPU在一段時(shí)間內(nèi)使用,對(duì)提高程序的運(yùn)行速度有很大作用。這個(gè)cache一般稱(chēng)為高速緩存。
①寫(xiě)穿式(Write Through)
任一CPU發(fā)出寫(xiě)信號(hào)送到Cache的同時(shí),也寫(xiě)入主存,保證主存的數(shù)據(jù)同步更新。優(yōu)點(diǎn)是操作簡(jiǎn)單,但由于主存速度慢,降低了系統(tǒng)的寫(xiě)速度并占用了總線(xiàn)的時(shí)間。
②回寫(xiě)式(Write Back)
數(shù)據(jù)一般只寫(xiě)到Cache,這樣可能出現(xiàn)Cache中的數(shù)據(jù)得到更新而主存中的數(shù)據(jù)不變(數(shù)據(jù)陳舊)的情況。此時(shí)可在Cache中設(shè)一個(gè)標(biāo)志地址及數(shù)據(jù)陳舊的信息,只有當(dāng)Cache中的數(shù)據(jù)被換出或強(qiáng)制進(jìn)行”清空“操作時(shí),才將原更新的數(shù)據(jù)寫(xiě)入主存響應(yīng)的單元中,保證了Cache和主存中數(shù)據(jù)一致。
Cache有以下兩個(gè)操作:
①”清空“(clean):把Cache或Write buffer中已經(jīng)臟的(修改過(guò),但未寫(xiě)入主存)數(shù)據(jù)寫(xiě)入主存
②”使無(wú)效“(Invalidate):使之不能再使用,并不將臟的數(shù)據(jù)寫(xiě)入主存。
S2C2440內(nèi)置了指令Cache(ICaches)、數(shù)據(jù)Cache(DCaches)、寫(xiě)緩存(Write buffer),需要用到描述符中的C位(Ctt)和B位(Btt)
1)指令Cache(ICaches)
系統(tǒng)剛上電或復(fù)位時(shí),ICaches中的內(nèi)容是無(wú)效的,并且ICaches功能關(guān)閉。往Icr位(CP15協(xié)處理器中寄存器1的第12位)寫(xiě)1可以啟動(dòng)ICaches,寫(xiě)0停止ICaches
ICaches一般在MMU開(kāi)啟后使用,此時(shí)描述符的C位用來(lái)表示一段內(nèi)存是否可以被Cache。若Ctt=1,允許Cache,否則不允許。如果MMU沒(méi)有開(kāi)啟,ICaches也可以被使用,此時(shí)CPU讀取指令時(shí)所涉及的內(nèi)存都被當(dāng)做允許Cache
ICaches關(guān)閉時(shí),CPU每次取指都要讀取主存,性能低,所以通常盡早啟動(dòng)ICaches
ICaches開(kāi)啟后,CPU每次取指時(shí)都會(huì)先在ICaches中查看是否能找到所用指令,而不管Ctt是0還是1。如果找到成為Cache命中,找不到稱(chēng)為Cache丟失,ICaches被開(kāi)啟后,CPU的取指有如下三種情況:
①Cache命中且Ctt為1時(shí),從ICaches中取指,返回CPU
②Cache丟失且Ctt為1時(shí),CPU從主存中取指,并且把指令緩存到Cache中
③Ctt為0時(shí),CPU從主存中取指
2)數(shù)據(jù)Cache(DCaches)
與ICaches相似,系統(tǒng)剛上電或復(fù)位時(shí),DCaches中的內(nèi)容無(wú)效,并且DCaches功能關(guān)閉,Write buffer中的內(nèi)容也是被廢棄不用的。往Ccr位(CP15協(xié)處理器 中寄存器1的第二位)寫(xiě)1啟動(dòng)DCaches,寫(xiě)0停止DCaches。Write buffer和DCaches緊密結(jié)合,額米有專(zhuān)門(mén)的控制來(lái)開(kāi)啟和停止它
與ICaches不同,DCaches功能必須在MMU開(kāi)啟之后才能被使用。
DCaches被關(guān)閉時(shí),CPU每次都去內(nèi)存取數(shù)據(jù)。
DCaches被開(kāi)啟后,CPU每次讀寫(xiě)數(shù)據(jù)時(shí)都會(huì)先在DCaches中查看是否能找到所要的數(shù)據(jù),不管Ctt是0還是1,找到了成為Cache命中,找不到成為Cache丟失。
通過(guò)下表可知DCaches和Write buffer在Ccr,Ctt,Btt各種取值下,如何工作,Ctt and Ccr 意為 Ctt與Ccr進(jìn)行邏輯與后的值
Ctt and Ccr Btt DCaches、Write buffer 和主存的訪(fǎng)問(wèn)方式
0 0 Non-cached,non-buffered(NCNB)
讀寫(xiě)數(shù)據(jù)時(shí)都是直接操作主存,并且可以被外設(shè)中止;
寫(xiě)數(shù)據(jù)時(shí)不使用Write buffer,CPU會(huì)等待寫(xiě)操作完成;
不會(huì)出現(xiàn)Cache命中
0 1 Non-Cached buffered(NCB)
讀數(shù)據(jù)時(shí)都是直接操作主存;
不會(huì)出現(xiàn)Cache命中;
寫(xiě)數(shù)據(jù)時(shí),數(shù)據(jù)線(xiàn)存入Write buffer,并在隨后寫(xiě)入主存;
數(shù)據(jù)存入Write buffer后,CPU立即繼續(xù)執(zhí)行;
讀數(shù)據(jù)時(shí),可以被外設(shè)中止;
寫(xiě)數(shù)據(jù)時(shí),無(wú)法被外設(shè)中止
1 0 Cached,write-through(寫(xiě)通)mode
讀數(shù)據(jù)時(shí),如果Cache命中則從Cache中返回?cái)?shù)據(jù),不讀取主存;
讀數(shù)據(jù)時(shí),如果Cache丟失則從讀主存中返回?cái)?shù)據(jù),并導(dǎo)致“l(fā)inefill”的動(dòng)作;
寫(xiě)數(shù)據(jù)時(shí),數(shù)據(jù)先存入Write buffer,并在隨后寫(xiě)入主存;
數(shù)據(jù)存入Write buffer后,CPU立即繼續(xù)執(zhí)行;
寫(xiě)數(shù)據(jù)時(shí),如果Cache命中則新數(shù)據(jù)也寫(xiě)入Cache中;
寫(xiě)數(shù)據(jù)時(shí),無(wú)法被外設(shè)中止
1 1 Cached,write-back(寫(xiě)回) mode
讀數(shù)據(jù)時(shí),如果Cache命中則從Cache中返回?cái)?shù)據(jù),不讀取主存;
讀數(shù)據(jù)時(shí),如果Cache丟失則從讀主存中返回?cái)?shù)據(jù),并導(dǎo)致“l(fā)inefile”的動(dòng)作;
寫(xiě)數(shù)據(jù)時(shí),如果Cache丟失則將數(shù)據(jù)先存入Write buffer,存儲(chǔ)完畢后CPu立即繼續(xù)執(zhí)行,這些數(shù)據(jù)在隨后寫(xiě)入主存;
寫(xiě)數(shù)據(jù)時(shí),如果Cache命中則在Cache中更新數(shù)據(jù),并設(shè)置這些數(shù)據(jù)為”臟的“,但是不會(huì)寫(xiě)入主存;
無(wú)論Cache命中與否,寫(xiě)數(shù)據(jù)都無(wú)法被外設(shè)中止
使用Cache時(shí)需要保證Cache、Write buffer的內(nèi)容和主存內(nèi)容一致,保證下面兩個(gè)原則:
①清空DCaches,使主存數(shù)據(jù)得到更新
②使無(wú)效ICaches,使CPU取指時(shí)重新讀取主存
在實(shí)際編寫(xiě)程序時(shí),要注意如下幾點(diǎn):
①開(kāi)啟MMU前,十五小ICaches,DCaches和Write buffer
②關(guān)閉MMU前,清空ICaches、DCaches,即將”臟“數(shù)據(jù)寫(xiě)到主存上
③如果代碼有變,使無(wú)效ICaches,這樣CPU取指時(shí)會(huì)從新讀取主存
④使用DMA操作可以被Cache的內(nèi)存時(shí),將內(nèi)存的數(shù)據(jù)發(fā)送出去時(shí),要清空Cache;將內(nèi)存的數(shù)據(jù)讀入時(shí),要使無(wú)效Cache
⑤改變頁(yè)表中地址映射關(guān)系時(shí)也要慎重考慮
⑥開(kāi)啟ICaches或DCaches時(shí),要考慮ICaches或DCaches中的內(nèi)容是否與主存保持一致
⑦對(duì)于I/O地址空間,不使用Cache和Write buffer
5、S3C2440 MMU、TLB、Cache的控制指令
S3C2440除了ARM920T的CPU核心外,還有若干個(gè)協(xié)處理器,用來(lái)幫助主CPu完成一些特殊功能。對(duì)MMU、TLB、Cache等的操作涉及到協(xié)處理器。
<MCR|MRC>{條件} 協(xié)處理器編碼,協(xié)處理器操作碼1,目的寄存器,源寄存器1,源寄存器2,協(xié)處理器操作碼2
<MCR|MRC> {cond} p#,<expression1>,Rd,cn,cm{,<expression2>}
MRC //從協(xié)處理器獲得數(shù)據(jù),傳給ARM920T CPU核心寄存器
MCR //數(shù)據(jù)從ARM920T CPU核心寄存器傳給協(xié)處理器
{cond} //執(zhí)行條件,省略時(shí)表示無(wú)條件執(zhí)行
p# //協(xié)處理器序號(hào)
<expression1> //一個(gè)常數(shù)
Rd //ARM920T CPU核心的寄存器
cn和cm //協(xié)處理器中的寄存器
<expression2> //一個(gè)常數(shù)
其中,<expression1>、cn、cm、<expression2>僅供協(xié)處理器使用,它們的作用如何取決于具體的協(xié)處理器
二、MMU使用實(shí)例:地址映射
這個(gè)實(shí)例將開(kāi)啟MMU,并將虛擬地址0xA0000000-0xA0100000映射到物理地址0x56000000-0x56100000(GPBCON物理地址為0x56000010,GPBDAT物理地址為0x56000014),來(lái)驅(qū)動(dòng)LED。
將虛擬地址0xB0000000-0xB3FFFFFF映射到物理地址0x30000000-0x33FFFFFF,在連接程序時(shí),將一部分代碼的運(yùn)行地址指定為0xB0004000.
這個(gè)程序只使用一級(jí)頁(yè)表,以段的方式進(jìn)行地址映射,32位CPU虛擬地址空間達(dá)到4G,一級(jí)頁(yè)表使用4096個(gè)描述符來(lái)表示4G空間(每個(gè)描述符對(duì)應(yīng)1MB),每個(gè)描述符占4字節(jié),所以一級(jí)頁(yè)表占16KB。這個(gè)程序使用SDRAM的開(kāi)始16KB存放一級(jí)頁(yè)表,所以剩下的內(nèi)存開(kāi)始地址就為0x30004000,這個(gè)地址最終會(huì)對(duì)應(yīng)虛擬地址0xB0004000(所以代碼運(yùn)行地址為0xB0004000)
程序分為兩部分:第一部分的運(yùn)行地址為0,它用來(lái)初始化SDRAM,復(fù)制第二部分的代碼到SDRAM中(存放在0x30004000)、設(shè)置頁(yè)表、啟動(dòng)MMU,最后跳到SDRAM中(地址0xB0004000),第二部分運(yùn)行地址設(shè)為0xB0004000,用來(lái)驅(qū)動(dòng)LED
先看連接文件mmu.lds
SECTIONS {
firtst 0x00000000 : { head.o init.o }
second 0xB0004000 : AT(2048) { leds.o }
}
程序分兩個(gè)段:first和second。first由head.o和init.o組成,加載和運(yùn)行地址都是0,second由leds.o組成,加載地址為2048,重定位地址為0xB0004000。
@*************************************************************************
@ File:head.S
@ 功能:設(shè)置SDRAM,將第二部分代碼復(fù)制到SDRAM,設(shè)置頁(yè)表,啟動(dòng)MMU,
@ 然后跳到SDRAM繼續(xù)執(zhí)行
@*************************************************************************
.text
.global _start
_start:
ldr sp, =4096 @ 設(shè)置棧指針,以下都是C函數(shù),調(diào)用前需要設(shè)好棧
bl disable_watch_dog @ 關(guān)閉WATCHDOG,否則CPU會(huì)不斷重啟
bl memsetup @ 設(shè)置存儲(chǔ)控制器以使用SDRAM
bl copy_2th_to_sdram @ 將第二部分代碼復(fù)制到SDRAM
bl create_page_table @ 設(shè)置頁(yè)表
bl mmu_init @ 啟動(dòng)MMU,啟動(dòng)以后下面代碼都用虛擬地址
ldr sp, =0xB4000000 @ 重設(shè)棧指針,指向SDRAM頂端(使用虛擬地址)
ldr pc, =0xB0004000 @ 跳到SDRAM中繼續(xù)執(zhí)行第二部分代碼
halt_loop:
b halt_loop
/*
* init.c: 進(jìn)行一些初始化,在Steppingstone中運(yùn)行
* 它和head.S同屬第一部分程序,此時(shí)MMU未開(kāi)啟,使用物理地址
*/
/* WATCHDOG寄存器 */
#define WTCON (*(volatile unsigned long *)0x53000000)
/* 存儲(chǔ)控制器的寄存器起始地址 */
#define MEM_CTL_BASE 0x48000000
/*
* 關(guān)閉WATCHDOG,否則CPU會(huì)不斷重啟
*/
void disable_watch_dog(void)
{
WTCON = 0; // 關(guān)閉WATCHDOG很簡(jiǎn)單,往這個(gè)寄存器寫(xiě)0即可
}
/*
* 設(shè)置存儲(chǔ)控制器以使用SDRAM
*/
void memsetup(void)
{
/* SDRAM 13個(gè)寄存器的值 */
unsigned long const mem_cfg_val[]={ 0x22011110, //BWSCON
0x00000700, //BANKCON0
0x00000700, //BANKCON1
0x00000700, //BANKCON2
0x00000700, //BANKCON3
0x00000700, //BANKCON4
0x00000700, //BANKCON5
0x00018005, //BANKCON6
0x00018005, //BANKCON7
0x008C07A3, //REFRESH
0x000000B1, //BANKSIZE
0x00000030, //MRSRB6
0x00000030, //MRSRB7
};
int i = 0;
volatile unsigned long *p = (volatile unsigned long *)MEM_CTL_BASE;
for(; i < 13; i++)
p[i] = mem_cfg_val[i]; //循環(huán)復(fù)制13個(gè)寄存器到內(nèi)存控制器基址
}
/*
* 將第二部分代碼復(fù)制到SDRAM
*/
void copy_2th_to_sdram(void)
{
unsigned int *pdwSrc = (unsigned int *)2048; //第二段代碼加載地址2048
unsigned int *pdwDest = (unsigned int *)0x30004000; //0x30004000前放頁(yè)表
while (pdwSrc < (unsigned int *)4096) //4kb最大4096
{
*pdwDest = *pdwSrc;
pdwDest++;
pdwSrc++;
}
}
/*
* 設(shè)置頁(yè)表
*/
void create_page_table(void)
{
/*
* 用于段描述符的一些宏定義
*[31:20]段基址,[11:10]AP,[8:5]Domain,[3]C,[2]B,[1:0]0b10為段描述符
*/
#define MMU_FULL_ACCESS (3 << 10) /* 訪(fǎng)問(wèn)權(quán)限AP */
#define MMU_DOMAIN (0 << 5) /* 屬于哪個(gè)域 Domain*/
#define MMU_SPECIAL (1 << 4) /* 必須是1 */
#define MMU_CACHEABLE (1 << 3) /* cacheable C位*/
#define MMU_BUFFERABLE (1 << 2) /* bufferable B位*/
#define MMU_SECTION (2) /* 表示這是段描述符 */
#define MMU_SECDESC (MMU_FULL_ACCESS | MMU_DOMAIN | MMU_SPECIAL | \
MMU_SECTION)
#define MMU_SECDESC_WB (MMU_FULL_ACCESS | MMU_DOMAIN | MMU_SPECIAL | \
MMU_CACHEABLE | MMU_BUFFERABLE | MMU_SECTION)
#define MMU_SECTION_SIZE 0x00100000 /*每個(gè)段描述符對(duì)應(yīng)1MB大小空間*/
unsigned long virtuladdr, physicaladdr;
unsigned long *mmu_tlb_base = (unsigned long *)0x30000000; /*SDRAM開(kāi)始地址存放頁(yè)表*/
/*
* Steppingstone的起始物理地址為0,第一部分程序的起始運(yùn)行地址也是0,
* 為了在開(kāi)啟MMU后仍能運(yùn)行第一部分的程序,
* 將0~1M的虛擬地址映射到同樣的物理地址
*/
virtuladdr = 0;
physicaladdr = 0;
//虛擬地址[31:20]用于索引一級(jí)頁(yè)表,找到它對(duì)應(yīng)的描述符,對(duì)應(yīng)于(virtualaddr>>20)
//段描述符中[31:20]保存段的物理地址,對(duì)應(yīng)(physicaladdr & 0xFFF00000)
*(mmu_tlb_base + (virtuladdr >> 20)) = (physicaladdr & 0xFFF00000) | \
MMU_SECDESC_WB;
/*
* 0x56000000是GPIO寄存器的起始物理地址,
* GPBCON和GPBDAT這兩個(gè)寄存器的物理地址0x56000010、0x56000014,
* 為了在第二部分程序中能以地址0xA0000010、0xA0000014來(lái)操作GPBCON、GPBDAT,
* 把從0xA0000000開(kāi)始的1M虛擬地址空間映射到從0x56000000開(kāi)始的1M物理地址空間
*/
virtuladdr = 0xA0000000;
physicaladdr = 0x56000000;
*(mmu_tlb_base + (virtuladdr >> 20)) = (physicaladdr & 0xFFF00000) | \
MMU_SECDESC;
/*
* SDRAM的物理地址范圍是0x30000000~0x33FFFFFF,
* 將虛擬地址0xB0000000~0xB3FFFFFF映射到物理地址0x30000000~0x33FFFFFF上,
* 總共64M,涉及64個(gè)段描述符
*/
virtuladdr = 0xB0000000;
physicaladdr = 0x30000000;
while (virtuladdr < 0xB4000000)
{
*(mmu_tlb_base + (virtuladdr >> 20)) = (physicaladdr & 0xFFF00000) | \
MMU_SECDESC_WB;
virtuladdr += 0x100000; //右移20位就是1
physicaladdr += 0x100000; //右移20位就是1
}
}
/*
* 啟動(dòng)MMU
*/
void mmu_init(void)
{
unsigned long ttb = 0x30000000;
__asm__(
"mov r0, #0\n"
"mcr p15, 0, r0, c7, c7, 0\n" /* 使無(wú)效ICaches和DCaches */
"mcr p15, 0, r0, c7, c10, 4\n" /* drain write buffer on v4 */
"mcr p15, 0, r0, c8, c7, 0\n" /* 使無(wú)效指令、數(shù)據(jù)TLB */
"mov r4, %0\n" /* r4 = 頁(yè)表基址 */
"mcr p15, 0, r4, c2, c0, 0\n" /* 設(shè)置頁(yè)表基址寄存器 */
"mvn r0, #0\n"
"mcr p15, 0, r0, c3, c0, 0\n" /* 域訪(fǎng)問(wèn)控制寄存器設(shè)為0xFFFFFFFF, 不進(jìn)行權(quán)限檢查*/
/*
* 對(duì)于控制寄存器,先讀出其值,在這基礎(chǔ)上修改感興趣的位,
* 然后再寫(xiě)入
*/
"mrc p15, 0, r0, c1, c0, 0\n" /* 讀出控制寄存器的值 */
/* 控制寄存器的低16位含義為:.RVI ..RS B... .CAM
* R : 表示換出Cache中的條目時(shí)使用的算法,
* 0 = Random replacement;1 = Round robin replacement
* V : 表示異常向量表所在的位置,
* 0 = Low addresses = 0x00000000;1 = High addresses = 0xFFFF0000
* I : 0 = 關(guān)閉ICaches;1 = 開(kāi)啟ICaches
* R、S : 用來(lái)與頁(yè)表中的描述符一起確定內(nèi)存的訪(fǎng)問(wèn)權(quán)限
* B : 0 = CPU為小字節(jié)序;1 = CPU為大字節(jié)序
* C : 0 = 關(guān)閉DCaches;1 = 開(kāi)啟DCaches
* A : 0 = 數(shù)據(jù)訪(fǎng)問(wèn)時(shí)不進(jìn)行地址對(duì)齊檢查;1 = 數(shù)據(jù)訪(fǎng)問(wèn)時(shí)進(jìn)行地址對(duì)齊檢查
* M : 0 = 關(guān)閉MMU;1 = 開(kāi)啟MMU
*/
/*
* 先清除不需要的位,往下若需要?jiǎng)t重新設(shè)置它們
*/
/* .RVI ..RS B... .CAM */
"bic r0, r0, #0x3000\n" /* ..11 .... .... .... 清除V、I位 */
"bic r0, r0, #0x0300\n" /* .... ..11 .... .... 清除R、S位 */
"bic r0, r0, #0x0087\n" /* .... .... 1... .111 清除B/C/A/M */
/*
* 設(shè)置需要的位
*/
"orr r0, r0, #0x0002\n" /* .... .... .... ..1. 開(kāi)啟對(duì)齊檢查 */
"orr r0, r0, #0x0004\n" /* .... .... .... .1.. 開(kāi)啟DCaches */
"orr r0, r0, #0x1000\n" /* ...1 .... .... .... 開(kāi)啟ICaches */
"orr r0, r0, #0x0001\n" /* .... .... .... ...1 使能MMU */
"mcr p15, 0, r0, c1, c0, 0\n" /* 將修改的值寫(xiě)入控制寄存器 */
: /* 無(wú)輸出 */
: "r" (ttb) );
}
/*
* leds.c: 循環(huán)點(diǎn)亮4個(gè)LED
* 屬于第二部分程序,此時(shí)MMU已開(kāi)啟,使用虛擬地址
*/
#define GPBCON (*(volatile unsigned long *)0xA0000010) // 物理地址0x56000010
#define GPBDAT (*(volatile unsigned long *)0xA0000014) // 物理地址0x56000014
#define GPB5_out (1<<(5*2))
#define GPB6_out (1<<(6*2))
#define GPB7_out (1<<(7*2))
#define GPB8_out (1<<(8*2))
/*
* wait函數(shù)加上“static inline”是有原因的,
* 這樣可以使得編譯leds.c時(shí),wait嵌入main中,編譯結(jié)果中只有main一個(gè)函數(shù)。
* 于是在連接時(shí),main函數(shù)的地址就是由連接文件指定的運(yùn)行時(shí)裝載地址。
* 而連接文件mmu.lds中,指定了leds.o的運(yùn)行時(shí)裝載地址為0xB4004000,
* 這樣,head.S中的“l(fā)dr pc, =0xB4004000”就是跳去執(zhí)行main函數(shù)。
*/
static inline void wait(unsigned long dly)
{
for(; dly > 0; dly--);
}
int main(void)
{
unsigned long i = 0;
// 將LED1-4對(duì)應(yīng)的GPB5/6/7/8四個(gè)引腳設(shè)為輸出
GPBCON = GPB5_out|GPB6_out|GPB7_out|GPB8_out;
while(1){
wait(30000);
GPBDAT = (~(i<<5)); // 根據(jù)i的值,點(diǎn)亮LED1-4
if(++i == 16)
i = 0;
}
return 0;
}
最后是Makefile
objs := head.o init.o leds.o
mmu.bin : $(objs)
arm-linux-ld -Tmmu.lds -o mmu_elf $^
arm-linux-objcopy -O binary -S mmu_elf $@
arm-linux-objdump -D -m arm mmu_elf > mmu.dis
%.o:%.c
arm-linux-gcc -Wall -O2 -c -o $@ $<
%.o:%.S
arm-linux-gcc -Wall -O2 -c -o $@ $<
clean:
rm -f mmu.bin mmu_elf mmu.dis *.o
聯(lián)系客服