JZ2440 第7章 內存管理單元 MMU

本章目標:

    瞭解虛擬地址和物理地址的關係;
    掌握如何通過設置MMU來控制虛擬地址到物理地址的轉化;
    瞭解MMU的內存訪問權限機制;
    瞭解TLB、Cache、Write buffer的原理,使用時的注意事項;
    通過實例深刻掌握上述要點;

7.1 內存管理單元MMU介紹

7.1.1 S3C2410/S3C2440 MMU特性

    內存管理單元(Memory Management Unit),簡稱MMU,它負責虛擬地址到物理
址的映射,並提供硬件機制的內存訪問權限檢查。現代的多用戶進程操作系統通
MMU使得各個用戶進程都擁有自己獨立的地址空間:地址映射功能使得各進程擁
“看起來”一樣的地址空間,而內存訪問權限的檢查可以保護每個進程所用的內存不
被其他進程破壞。
    S3C2410/S3C2440有如下特性:
    ① 與ARM V4兼容的映射長度、域、訪問權限檢查機制;
    ② 4種映射長度:段(1MB)、大頁(64KB)、小頁(4KB)、極小頁(1KB);
    ③ 對每段都可以設置訪問權限;
    ④ 大頁、小頁的每個子頁(sub-page,即被映射頁的1/4)都可以單獨設置訪問權限;
    ⑤ 硬件實現16個域;
    ⑥ 指令TLB(含64個條目),數據TLB(含64個條目);
    ⑦ 硬件訪問頁表(地址映射、權限檢查由硬件自動進行);
    ⑧ TLB中條目的替換採用round-robin算法(也稱cyclic算法);
    ⑨ 可以使無效整個TLB;
    ⑩ 可以單獨使無效某個TLB條目;
    ⑪ 可以在TLB中鎖定某個條目,指令TLB、數據TLB互相獨立。
    本章重點在於地址映射;頁表的結構與建立、映射的過程,對於訪問權限、TLB、
Cache只粗略介紹。

7.1.2 S3C2410/S3C2440 MMU地址變換過程

1.地址的分類
     以前的程序是非常小的,可以全部裝入內存中。隨着技術的發展,出現以下兩種情況。
    (1)有的程序很大,它所要求的內存空間超過了內存總容量,不能一次性裝入內存;
    (2)多道系統中有很多程序需要同時執行,它們要求的內存空間超過了內存總容量,
能把所有程序全部裝入內存。
    實際上,一個程序在運行之前,沒有必要全部載入內存,而僅需要將那些當前需要運行
部分先裝入內存,其餘部分在用到時再從磁盤調入,而當內存耗光時再將暫時不同的部
分調出到磁盤。這使得一個大程序可以在較小的內存空間中運行,也使得內存中可以同時
裝入更多的程序併發執行,從用戶的角度看,該系統所具有的內存容量將比實際內存容量
大得多,人們把這樣的存儲器稱爲虛擬存儲器。
    虛擬存儲器從邏輯上對內存容量進行了擴充,用戶看到的大容量只是一種感覺,是虛的,
在32位的CPU系統中,這個虛擬內存地址範圍爲0~0xFFFF FFFF,我們把這個地址範圍稱
爲虛擬地址空間,其中某個地址稱爲虛擬地址。與虛擬地址空間、虛擬地址對應的是物理
址空間、物理地址,他們對應實際的內存。
    虛擬地址最終需要轉換爲物理地址才能讀寫實際的數據,這通過將虛擬地址空間、物理
地址空間劃分爲同樣大小的一塊塊小空間(稱爲段或頁),然後爲這兩類小空間建立映射關係。
由於虛擬地址空間遠大於物理空間,有可能多塊虛擬地址空間映射到同一塊物理地址空間。
或者有些虛擬地址空間沒有映射到具體的物理地址空間上去(可以在使用到時在映射)。如圖
7.1所示爲這些映射關係。
    
    ARM CPU的地址轉換過程涉及3個概念:虛擬地址(VA, Virtual Address)、變換
後的虛擬地址(MVA,Modified Virtual Address)、物理地址(PA,Physical Address)。
    沒有啓動MMU時,CPU核、cache、MMU、外設等所有部件使用的都是物理地址。
    啓動MMU後,CPU覈對外發出虛擬地址:VA;VA被轉換爲MVA供cache、MMU使
用,在這裏MVA被轉換爲PA;最後使用PA讀寫實際設備(S3C2410/S3C2440內部寄存
或外接的設備):
    (1)CPU核看到的、用到的只是虛擬地址VA,至於VA如何最終落實到物理地址PA
上,CPU不理會。
    (2)cache和MMU也看不見VA,它們利用由MVA轉換得到PA。
    (3)實際設備看不到VA,MVA,讀寫它們時使用的是物理地址PA。
    MVA是除CPU核外的其他部分看見的虛擬地址,VA和MVA之間的變換關係如圖7.2
所示。
     
    如果VA < 32M,需要使用進程標識號PID(通過讀CP15的C13獲得)來轉換爲MVA。
VA和MVA轉換方法如下(這是硬件自動完成的):
    if(VA < 32M)    then
        MVA = VA | (PID << 25)
    else
        MVA = VA
    利用PID生成MVA的目的是爲了減少切換進程時的代價:
        不使用MVA而直接使用VA的話,當兩個進程所用的虛擬地址空間(VA)有重疊時,
在切換進程時爲了把重疊的VA映射到不同的PA上去,需要重建頁表、使無效caches和
TLBS等,代價非常大。使用MVA後,進程切換就省事多了。
        假設兩個進程1、2運行時的VA都是0~(32M-1),但是它們的MVA並不重疊,分別
是0x0200 0000~0x03ff ffff、0x0400 0000~0x05ff ffff,這樣就不必進行重建頁表等工作了。
    下面說道的虛擬地址,若沒有特別指出,就是指MVA。
 2.虛擬地址到物理地址的轉換過程
    將一個虛擬地址轉換爲物理地址,一般有兩種方法:用一個確定的數學公式進行轉換或用
表格存儲虛擬地址對應的物理地址。這類表格稱爲頁表(Page table),頁表由一個個條目(Entry)
組成;每個條目存儲了一段虛擬地址對應的物理地址及其訪問權限,或者下一級頁表的地址。
    在ARM CPU中使用第二種方法。
    S3C2410/S3C2440最多會用到兩級頁表:
        以段(Section,1MB)的方式進行轉換時只用到一級頁表;
        以頁(Page,大頁(64KB)、小頁(4KB)、極小頁(1KB))的方式進行轉換時用到兩級頁表。
    條目也稱爲“描述符”(Descriptor),有段描述符、大頁描述符、小頁描述符、極小頁描述符
——它們保存段、大頁、小頁或極小頁的起始物理地址;粗頁表描述符、細頁表描述符——它
保存二級頁表的物理地址。
    大概的轉換過程如下:請參考圖7.3(通用轉換過程)、圖7.4(針對ARM CPU細化的轉換過程):
    (1)根據給定的虛擬地址找到一級頁表中的條目;
    (2)如果此條目是段描述符,則返回物理地址,轉換結束;
    (3)否則如果是二級頁表描述符,繼續利用虛擬地址在此二級頁表中找到下一個條目;
    (4)如果這第二個條目是頁描述符,則返回物理地址,轉換結束;
    (5)其他情況出錯。


     圖7.3/7.4中“TTB base”代表一級頁表的地址,將它寫入協處理器CP15的寄存器C2
(稱爲頁表基址寄存器)即可,如圖7.5所示,一級頁表的地址必須是16K對應的(位[14:0]
爲0)。

     現在先介紹一級頁表。32位CPU的虛擬地址空間達到4GB,一級頁表中使用4096個描
述符來表達着4GB空間——每個描述符對應1MB的虛擬地址,要麼存儲了它對應的1MB
物理空間的起始地址,要麼存儲了下一級頁表的地址。使用MVA[31:20]來索引一級頁表,
得到一個描述符,每個描述符佔據4字節,格式如圖7.6所示。

     根據一級描述符的最低兩位,可分爲以下4種。
    (1)0b00:無效。
    (2)0b01:粗頁表(Coarse page table)。
    位[31:10]稱爲粗頁表基地址(Coarse page table base address),此描述符的低10位填充0
後就是一個二級頁表的物理地址。此二級頁表含256個條目(所以大小爲1KB),稱爲粗頁
表(Coarse page table,見圖7.4)。其中每個條目表示大小爲4KB的物理地址空間,所以一
個粗頁表表示1MB的物理空間。
    (3)0b10:段(Section)。
    位[31:20]稱爲段基址(Section base),此描述符的低20位填充0後就是一塊1MB物理
地址空間的起始地址。MVA[19:0]用來在這1MB空間中尋址。所以,描述符的位[31:20]和
MVA[19:0]就構成了這個虛擬地址:MVA對應的物理地址。
    以段的方式進行映射時,虛擬地址MVA到物理地址PA的轉換過程如下(參考圖7.7)。

    ① 頁表基址寄存器位[31:14]和MVA[31:20]組成一個低2位爲0的32位地址,MMU利
用這個地址找到段描述符。
    ② 取出段描述符的位[31:20]——即段基址,它和MVA[19:0]組成一個32位的物理地
址——這就是MVA對應的PA
    (4)0b11:細頁表(Fine page table)。
    位[31:12]稱爲細頁表基址(Fine page table base address),此描述符的低12位填充0後
就是一個二級頁表的物理地址。此二級頁表含1024個條目(所以大小爲4KB),稱爲細頁表
(Fine page table,如圖7.4所示)。其中每個條目表示大小爲1KB的物理地址空間,所以一
個細頁表表示1MB的物理地址空間。
    以大頁(64KB)、小頁(4KB)或極小頁(1KB)進行地址映射時,需要用到兩級頁表。
二級頁表有粗頁表、細頁表兩種,圖7.4中“Coarse page table”和“Fine page table”就是這
兩種頁表。二級頁表中描述符的格式如圖7.8所示。


    根據二級描述符的最低兩位,可分爲以下4種情況。
    (1)0b00:無效
    (2)0b01:大頁描述符。
    位[31:16]稱爲大頁基址(Large page base address),此描述符的低16位填充0後就是一
個64KB物理地址空間的起始地址。粗頁表中每個條目只能表示4KB的物理空間,如果大頁
描述符保存在粗頁表中,則連續16(4KB*16 = 64KB)個條目都保存同一個大頁描述符。類似
的,細頁表中每個條目只能表示1KB的物理空間,如果大頁描述符保存在細頁表中,則連續
64(1KB*64 = 64KB)個條目都保存同一個大頁描述符。
    下面以保存在粗頁表中的大頁描述符爲例,說明地址轉換的過程(參考圖7.9)。

    ① 頁表基地址寄存器位[31:14]和MVA[31:20]組成一個低兩位爲0的32位地址,MMU利
用這個地址找到粗頁表描述符。
    ② 取出粗頁表描述符的位[31:10]——即粗頁表基址,它和MVA[19:12]組成一個低兩位
爲0的32位物理地址——據此即可找到大頁描述符。
    ③ 取出大頁描述符的位[31:16]——即大頁基址,它和MVA[15:0]組成一個32位的物理
地址——這就是MVA對應的PA。
    上面步驟②和③中,用於在粗頁表中索引的MVA[19:12]、用於在大頁內尋址的MVA
[15:0]有重合的位:位[15:12]。當[15:12]從0b0000變爲0b1111時,步驟②返回的大頁描
述符相同——所以,粗頁表中連續16個條目都保存同一個大頁描述符。
    大頁描述符保存在細頁表中時,地址轉換過程與上面類似,如圖7.9所示,詳細過程不
再贅述。
    (3)0b10:小頁描述符。
    位[31:12]稱爲小頁基址(Small page base address),此描述符的低12位填充0後就是一
塊4KB物理地址空間的起始地址。粗頁表中每個條目表示4KB的物理空間,如果小頁描述符
保存在粗頁表中,則只需要用一個條目來保存一個小頁描述符。類似的,細頁表中每個條目
只能表示1KB的物理空間,如果小頁描述符保存在細頁表中,則連續4個條目都保存同一個
小頁描述符。
    下面以保存在粗頁表中的小頁描述符爲例,說明地址轉換過程(參考圖7.10)。

    ① 頁表基址寄存器位[31:14]和MVA[31:20]組成一個低兩位爲0的32位地址,MMU利
用這個地址找到粗頁表描述符。
    ② 取出粗頁表描述符的位[31:10]——即粗頁表基址,它和MVA[19:12]組成一個低兩
爲0的32位物理地址——據此即可找到小頁描述符。
    ③ 取出大頁描述符的[31:12]——即小頁基址,它和MVA[11:0]組成一個32位的物理地
——這就是MVA對應的PA。
    小頁描述符保存在細頁表中時,地址轉換過程與上面類似,不再贅述。
    (4)0b11:極小頁描述符。
    位[31:10]稱爲極小頁基址(Tiny page base address),此描述符的低10位填充0後就是
塊1KB物理地址空間的起始地址。極小頁描述符只能保存在細頁表中,用一個條目來保存
一個極小頁描述符。 
    下面是極小頁的地址轉換過程(如圖7.11所示)。

    ① 頁表基址寄存器[31:14]和MVA[31:20]組成一個低兩位爲0的32位地址,MMU利
用這個地址找到細頁表描述符。
    ② 取出細頁表描述符的位[31:12]——即細頁表基址,它和MVA[19:10]組成一個低
兩位爲0的32位物理地址——據此即可找到極小頁描述符。
    ③ 取出極小頁描述符的位[31:10]——即極小頁基址,它和MVA[9:0]組成一個32位
的物理地址——這就是MVA對應的PA。
    從段、大頁、小頁、極小頁的地址轉換過程可知。
    (1)以段進行映射時,通過MVA[31:20]結合頁表得到一段(1MB)的起始物理地址,
MVA[19:0]用來在段中尋址。
    (2)以大頁進行映射時,通過MVA[31:16]結合頁表得到一個大頁(64KB)的起始物理地址,
MVA[15:0]用來在大頁中尋址。
    (3)以小頁進行映射時,通過MVA[31:12]結合頁表得到一個小頁(4KB)的起始物理地址,
MVA[11:0]用來在小頁中尋址。
    (4)以極小頁進行映射時,通過MVA[31:10]結合頁表得到一個極小頁(1KB)的起始物理地址,
MVA[9:0]用來在極小頁中尋址。

7.1.3 內存的訪問權限檢查

    內存的訪問權限檢查是MMU的主要功能之一,簡單地說,他就是決定一塊內存是否允
許讀寫。這由CP15寄存器C3(域訪問控制)、描述符的域(Domain)、CP15寄存器C1的R/S/A
位、描述符的AP位等聯合作用。
    CP15寄存器的C1中的A位表示是否對地址進行對齊檢查。所謂對齊檢查就是,訪問字
(4字節的數據)時,地址是否爲4字節對齊,訪問半字(2字節的數據)時地址是否2字節對齊,
如果地址不對齊則產生“Alignment fault”異常。無論MMU是否被開啓,都可以進行對齊檢
查。CPU讀取指令時不進行對齊檢查,以字節爲單位訪問時也不進行對齊檢查。對齊檢查
在MMU的權限檢查、地址映射前進行。
    內存的訪問權限檢查可以概括爲以下兩點。
    (1)“域”決定是否對某塊內存進行權限檢查。
    (2)“AP”決定如何對某塊內存進行權限檢查。


    如圖7.12所示,S3C2410/S3C2440有16個域,CP15寄存器C3中每兩位對應一個域,
用來表示這個域是否進行權限檢查。圖7.12表示CP15寄存器C3中哪兩位對應哪個域,表
7.1給出了CP15寄存器C3中這些“兩位的數據”的含義。

     圖7.13中的“Domain”佔據4字節,用來表示這塊內存屬於上面定義的16個域中國的哪
一個。舉例如下:
    (1)段描述符中“Domain”爲0b0000時,表示這1MB內存屬於域0,如果域訪問控制寄
存器的位[1:0]等於0b00,則訪問這1MB空間時都會產生”Domain fault“的異常;如果域訪
問控制寄存器的位[1:0]等於0b11,則使用描述符中的“AP”位進行權限檢查。
    (2)粗頁表中的“Domain”爲0b1111時,表示這1MB內存屬於域15,如果域訪問控制器
的位[31:30]爲0b0b00,則訪問這1MB空間時都會產生“Domain fault”的異常;如果域訪問
控制寄存器的位[31:30]爲0b11,則使用二級頁表中的大頁/小頁描述符中的"ap3"、“ap2”、
“ap1”、“ap0”位進行權限檢查。
    圖7.13中的“AP”、"ap3"、“ap2”、“ap1”、“ap0”結合CP15寄存器C1的R/S位,決定如何
進行訪問權限檢查。
    首先說明,段描述符中的“AP”控制整個段(1MB)的訪問權限;
    大頁描述符中的每個“apx”(x爲0~3)控制一個大頁(64KB)中1/4內存的訪問權限,即“ap3”
對應大頁高端的16KB;“ap0”對應大頁低端的16KB;小頁描述符與大頁描述符相似,每個
“apx”控制一個小頁(4KB)的1/4內存的訪問權限;極小頁中的"ap"就控制整個極小頁(1KB)
的訪問權限。

    如表7.2所示,AP位、S位和R位的組合,可以產生多種訪問權限。需要指出的是,ARM
CPU有7中工作模式,其中6種屬於特權模式,1種屬於用戶模式。在特權模式和用戶模式下,
相同的AP位、S位和R位的結合,其訪問權限也不相同。

7.1.4 TLB的作用

    從虛擬地址到物理地址的轉換過程可知:
        使用一級頁表進行地址轉換時,每次讀/寫數據需要訪問兩次內存,第一次訪問一級頁
表獲得物理地址,第二次纔是真正的讀/寫數據;
        使用二級頁表時,每次讀/寫數據需要訪問3次內存,訪問兩次頁表(一級頁表和二級頁
表)獲得物理地址,第三次纔是真正的讀/寫數據。
    上述的地址轉換過程大大降低了CPU的性能,有沒有辦法改進呢?
    程序執行過程中,所用到的指令、數據的地址往往集中在一個很小的範圍內,其中的地址、
數據經常多次使用,這稱爲程序訪問的侷限性。由此,通過使用一個高速、容量相對較小的
存儲器來存儲近期用到的頁表條目(段、大頁、小頁、極小頁描述符),以避免每次地址轉換時
都到主存去查找,這樣可以大幅度地提高性能。這個存儲器用來幫助快速地進行地址轉換,
稱爲“轉譯查找緩存”(Translation Lookaside Buffers,TLB)。
    當CPU發出一個虛擬地址時,MMU首先訪問TLB。如果TLB中含有能轉換這個虛擬地址的
描述符,則直接利用此描述符進行地址轉換或權限檢查;否則MMU訪問頁表找到描述符後再
進行地址轉換和權限檢查,並將這個描述符填入TLB中(如果TLB已滿,則利用rount-robin算
法找到一個條目,然後覆蓋它),下次再使用這個虛擬地址時就可以直接使用TLB中的描述符
了。
    使用TLB需要保證TLB中的內容與頁表一致,在啓動MMU之前、在頁表中的內容發
化後,尤其要注意這點。S3C2410/S3C2440可以使無效(Invalidate)整個TLB,或者通
過某個虛擬地址使無效TLB中的某個條目。一般的做法是:在啓動MMU之前使無效整個
TLB,改變頁表時,使無效所涉及的虛擬地址對應的TLB中的條目。

7.1.5 Cache的作用

    同樣基於程序訪問的侷限性,在主存和CPU通用寄存器之間設置一個高速、容量相對
較小的存儲器,把正在執行的指令地址附近的一部分指令或數據從主存調入這個存儲器,
供CPU在一段時間內使用,這對提高程序的運行速度有很大的作用。這個介於主存和CPU
之間的高速小容量存儲器稱爲高速緩衝存儲器(Cache)。
    啓用Cache後,CPU讀取數據時,如果Cache中有這個數據的副本,則直接返回,否則
從主存中讀入數據,並存入Cache中,下次再使用(讀/寫)這個數據時,可以直接使用Cache
中的副本。
    啓用Cache後,CPU寫數據時有寫穿式和回寫式兩種方式。
    (1)寫穿式(Write Through)。
    任一從CPU發出的寫信號送到Cache的同時,也寫入主存,以保證主存的數據能同步地更新。
    它的優點是操作簡單,但由於主存的慢速,降低了系統的寫速度並佔用了總線的時間。
    (2)回寫式(Write Back)。
    爲了克服寫穿式中每次數據寫入時都要訪問主存,從而導致系統寫速度降低並佔用總線
時間,儘量減少對主存的訪問次數,又有了回寫式。
    它是這樣工作的:數據一般只寫到Cache,這樣有可能出現Cache中的數據得到更新而
主存中的數據不變(數據陳舊)的情況。但此時可在Cache中設一標誌地址和數據陳舊的信息,
只有當Cache中的數據被換出或強制進行“清空”操作時,纔將原更新的數據寫入主存相應的
單元中。這樣保證了Cache和主存中的數據保持一致。
    先介紹Cache的兩個操作。
    (1)“清空”(clean):把Cache或Write Buffer中已經髒的(修改過,但未寫入主存)數據寫入
主存。
    (2)“使無效”(Invalidate):使之不能再使用,並不將髒的數據寫入主存。
    S3C2410/S3C2440內置了指令Cache(ICaches)、數據Cache(DCaches)、寫緩存(Write 
buffer)。下面的內容需要用到頁表中描述的C、B位,爲了方便讀者,先把這些描述符用
圖7.14表示出來。下文中,描述符的C位稱爲Ctt,B位稱爲Btt。

    1.指令Cache(ICaches)
    ICaches的使用比較簡單。系統剛上電或復位時,ICaches中的內容是無效的,並且ICaches
功能是關閉着的。往Icr位(即CP15協處理器中寄存器1)寫1可以啓動ICaches,寫0可以停止ICaches。
    ICaches一般在MMU開啓之後被使用,此時頁表中描述符的C位(稱爲Ctt)用來表示一段內存
是否可以被Cache。若Ctt = 1,則允許Cache,否則不允許被Cache。但是,即使MMU沒有開
啓,ICaches也是可以被使用的,這時CPU讀取指令(以後簡稱“取指”)時所涉及的內存都被當做
是允許Cache的。
    ICaches被關閉時,CPU每次取指都要讀取主存,性能非常低。所以通常儘早啓動ICaches。
    ICaches被打開時,CPU每次取指都會先在ICache中查看是否能找到所要的的指令,而不管
Ctt是0還是1。如果找到了,稱爲Cache命中(Cache hit);如果找不到,稱爲Cache缺失(Cache
miss)。ICaches被開啓後,CPU的取值分爲如下3種情況。
    (1)Cache命中且Ctt爲1時,從ICache中取出指令,返回CPU;
    (2)Cache缺失且Ctt爲1時,CPU從主存中讀出指令。同時,一個稱爲“8-word linefill”的動作
將發生,該動作把該指令所處區域的8個word寫進ICache的某個條目中。這有可能會覆蓋某個條
目,可以使用Pseudo-random算法或round-robin算法在ICaches中選出某個沒有被鎖定的條目。
可以通過CP15協處理器中的寄存器1的第14位來選擇使用哪種算法。
    (3)Ctt爲0時,CPU從主存中讀出指令。
    2.數據Cache(DCaches)
    與ICaches相似,系統剛上電或復位時,DCaches中內容也是無效的,並且DCaches功能也是
關閉着的,而Write buffer中的內容也是被廢棄不用的。往Ccr位(即CP15協處理器中寄存器1的第
2位)寫1可以啓動DCaches,寫0可以停止DCaches。Write buffer與DCaches緊密結合,沒有專門
的控制位來開啓、停止它。
    與ICaches不同,DCaches功能必須在MMU開啓之後才能被使用,因爲開啓MMU之後,才能
使用頁表中的描述符來定義一塊內存如何使用DCaches和Write buffer。
    DCaches被關閉時,CPU每次讀寫數據都要操作主存,DCaches和Write buffer被完全忽略。
    DCaches被開啓後,CPU每次讀寫數據時都會先在DCaches中查看是否能找到所要的數據,
而不管Ctt是0還是1,。如果找到了,稱爲Cache命中(Cache hit);如果找不到,稱爲Cache缺失
(Cache miss)。
 

    通過表7.3可以知道DCaches和Write buffer在CCr、Ctt、和Btt的各種取值下,如何工作。
表中“Ctt and Ccr”一項裏面的值是Ctt和Ccr進行邏輯與之後的值(Ctt && Ccr)。
    與TLB類似,使用Cache時需要保證Cache、Write buffer的內容和主存內容保持一致,
需要遵守如下兩個原則:
    (1)清空DCaches,使得主存數據得到更新;
    (2)使無效ICaches,使得CPU取值時重新讀取主存。
    在實際編寫程序時,要注意如下幾點:
    (1)開啓MMU前,使無效ICaches、DCaches和Write buffer;
    (2)關閉MMU前,清空ICaches、DCaches,即將“髒”數據寫到主存上;
    (3)如果代碼有變,使無效ICaches,這樣CPU取值時會重新讀寫主存;
    (4)使用DMA操作可以被Cache的內存時:
                將內存的數據發送出去時,要清空Cache;
                將內存的數據讀入時,要使無效Cache。
    (5)改變頁表中的地址映射關係時也要慎重考慮;
    (6)開啓ICaches或DCaches時,要考慮ICaches和DCaches中的內容是否與主存保持一致;
    (7)對於I/O地址空間,不使用Cache和Write buffer。所謂I/O地址空間,就是對於其中的地
址連續兩次的寫操作不能合併在一起,每次讀寫操作都必須直接訪問設備,否則程序的運行結果
無法預測。比如寄存器、非內存的外設(擴展串口、網卡等)。
    S3C2410/S3C2440提供了相關指令來操作Cache和Write buffer,可以使無效整個ICaches或
其中的某個條目,可以清空、使無效整個DCaches或其中的某個條目。這些指令在下面介紹。

7.1.6 S3C2410/S3C2440 MMU、TLB、Cache的控制指令

    S3C2410/S3C2440中,除了有一個ARM920T的CPU核外,還有若干個協處理器。協處
理器也是一個微處理器,它們被用來幫助主CPU完成一些特殊功能,比如浮點計算等。對MMU、
TLB、Cache等的操作就涉及到協處理器。CPU核與協處理器間傳送數據時使用這兩條指令:
MRC和MCR,它們的格式如下:
    其中,<expression1>、cn、cm、<expression2>僅供協處理器使用,它們的作用如何取決於
具體的協處理器。

7.2 MMU使用實例:地址映射

7.2.1 程序設計

    程序源碼位於/work/hardware/mmu目錄下。
    本開發板SDRAM的物理地址範圍出於0x3000 0000 ~ 0x33ff ffff,S3C2410/S3C2440的寄存器
地址範圍都處於0x4800 0000 ~ 0x5fff ffff。
    第5章通過往GPBCON和GPBDAT這兩個寄存器的物理地址0x5600 0010、0x5600 0014寫入特
定的數據來驅動4個LED。
    本章的實例將開啓MMU,並將虛擬地址空間0xA000 0000~0xA010 0000映射到物理地址空間
0x5600 0000~0x5610 0000上,這樣,就可以通過操作地址0xA000 0010、0xA000 0014來達到驅
動這4個LED的同樣效果。
    另外,將虛擬地址空間0xb000 0000~0xb3ff ffff映射到物理地址空間0x3000 0000~0x33ff ffff上,
並在連接程序時將一部分代碼的運行地址指定爲0xb000 4000(這個數值有點奇怪,看下去就會明白),
看看能否令程序跳轉到0xb000 4000處執行。
    本章程序只使用一級頁表,以段的方式進行地址映射。32位CPU的虛擬地址空間達到4GB,一級
頁表中使用4096個描述符來表示這4GB空間(每個描述符對應1MB的虛擬地址),每個描述符佔用4字
節,所以一級頁表佔16KB。本實例使用SDRAM的開始16KB來存放一級頁表,剩下的內存開始物理
地址位0x3000 4000。
    將程序代碼分爲兩部分:第一部分的運行地址設爲0,它用來初始化SDRAM、複製第二部分的代
碼到SDRAM中(存放在0x3000 4000處)、設置頁表、啓動MMU,最後跳轉到SDRAM中(地址
0xb000 4000)去繼續執行;第二部分的運行地址設爲0xb000 4000,它用來驅動LED。
    根據上面的敘述,程序流程圖如圖7.15所示。

7.2.2 代碼詳解

1. 第一部分代碼分析
程序源碼分爲3個文件:head.S、init.c、leds.c
(1)head.S代碼詳解。
head.S文件如下:
1
@*******************************
2
@ File:head.S
3
@ 功能:設置SDRAM,將第二部分代碼複製到SDRAM,設置頁表,啓動MMU,
4
@ 然後跳轉到SDRAM繼續執行。
5
@*******************************
6
7
.text
8
.global _start
9
_start:
10
    ldr sp, =4096            @設置棧指針,以下都是C函數,調用前需要設置好棧 
11
    bl  disable_watch_dog    @關閉看門狗
12
    bl  memsetup             @設置存儲控制器以使用SDRAM
13
    bl  copy_2th_to_sdram    @將第二部分代碼複製到SDRAM
14
    bl  create_page_table    @設置頁表
15
    bl  mmu_init             @啓動MMU
16
    ldr sp, =0xB4000000      @重設棧指針,指向SDRAM頂端(使用虛擬地址)
17
    ldr pc, =0xB0004000      @跳到SDRAM中繼續執行第二部分代碼
18
halt_loop:
19
    b   halt_loop
20
    
    head.S中調用的函數都在init.c中實現。
    值的注意的是,在第15行開啓MMU之後,無論是CPU取值還是CPU讀寫數據,使用
的都是虛擬地址。
    在第14行設置頁表時,在create_page_table函數中令head.S、init.c程序所在內存的虛
擬地址和物理地址一樣,這使得head.S和init.c中的代碼在開啓MMU後能夠沒有任何障礙
地繼續運行。
    (2)init.c代碼詳解。
    init.c中的disable_watch_dog、memsetup函數實現的功能在前面兩章已經討論過,不在
重複,下面列出diam以方便讀者查閱。
    copy_2th_to_sdram函數用來將第二部分代碼(即由leds.c編譯得到的代碼)從Steppingstone
中複製到SDRAM中。在連接程序時,第二部分代碼的加載地址唄指定爲2048,重定位地址爲
0xB000 4000,。所以系統從NAND Flash啓動後,第二部分代碼就存儲在Steppingstone中地址
2048之後,需要把它複製到0x3000 4000處(此時尚未開啓MMU,虛擬地址0xB000 4000對應
的物理地址在後面設爲0x3000 4000)。Steppingstone總大小爲4KB,不妨把地址2048之後的
所有數據複製到SDRAM中,所以源數據的結束地址爲4096。
    copy_2th_to_sdram函數的代碼如下:
1
/*
2
* 將第二部分代碼複製到SDRAM
3
*/
4
void copy_2th_to_sdram(void)
5
{
6
    unsigned int * pdwSrc = (unsigned int *)2048;
7
    unsigned int * pdwDest = (unsigned int *)0x30004000;
8
    
9
    while(pdwSrc < (unsigned int *)4096)
10
    {
11
        *pdwDest = *pdwSrc;
12
        pdwDest++;
13
        pdwSrc++;
14
    }
15
}
    剩下的create_page_table、mmu_init就是本章的重點了,前者用來設置頁表,後者用來
開啓MMU。
    先看看create_page_table函數。它用於設置3個區域的地址映射關係。
    (1)將虛擬地址0~(1M - 1)映射到同樣的物理地址去,Steppingstone(從0地址開始
的4KB內存)就處於這個範圍中。使虛擬地址等於物理地址,可以讓Steppingstone中的
程序(head.s和init.c)在開啓MMU前後不需要考慮太多的事情。
    (2)GPIO寄存器的起始物理地址範圍爲0x5600 0000,將虛擬地址0xA000 0000~
(0xA000 0000 + 1M - 1)映射到物理地址0x5600 0000~(0x5600 0000 + 1M -1)。
    (3)本開發板中SDRAM的物理地址範圍爲0x3000 0000~0x33ff ffff,將虛擬地址
0xB000 0000~0xB3FF FFFF映射到物理地址0x3000 0000~0x33FF FFFF。
    create_page_table函數代碼如下:
    mmu_tlb_base被定義爲unsigned long指針,所指向的內存爲4字節,剛好是一個描述符
的大小。在SDRAM的開始存放頁表——
    “unsigned long *mmu_tlb_base = (unsigned long *)0x30000000;”。
    其中最能體現頁表結構的代碼是:
1
*(mmu_tlb_base + (virtualaddr >> 20)) = (physicaladdr & 0xFFF00000) |\
2
                                         MMU_SECDESC_WB;
3
*(mmu_tlb_base + (virtualaddr >> 20)) = (physicaladdr & 0xFFF00000) |\
4
                                         MMU_SECDESC;
5
*(mmu_tlb_base + (virtualaddr >> 20)) = (physicaladdr & 0xFFF00000) |\
6
                                         MMU_SECDESC_WB;
    虛擬地址的位[31:20]用於索引一級頁表,找到它所對應的描述符,對應於“virtualaddr
 >> 20”。
    如圖7.13所示,段描述符中位[31:20]中保存段的物理地址,對應於“physicaladdr & 
0xFFF0 FFFF”。
    位[11:0]中用來設置段的訪問權限,包括所屬的域、AP位、C位(是否可Cache)、B位
(是否使用Write buffer)——這對應“MMU_SECDESC”或“MMU_SECDESC_WB”,它們
的域都被設爲0,AP位被設爲0b11(根據表7.2可知它所在的域進行權限檢查,則讀寫操作
都被允許)。“MMU_SECDESC”中的C/B位都沒有設置,表示不使用Cache和Write buffer,
所以映射寄存器空間時使用“MMU_SECDESC”。“MMU_SECDESC_WB”中C/B位都設置
了,表示使用Cache和Write buffer,即所謂的寫回式,在映射Steppingstone和SDRAM等
內存時使用“MMU_SECDESC_WB”。
    現在來看看mmu_init函數。create_page_table函數設置好了頁表,還需要把頁表地址
告訴CPU,並且在開啓MMU之前做好一些準備工作,比如使無效ICaches、DCaches,設
置域訪問控制寄存器等。代碼的註釋就可以幫助讀者很好地理解mmu_init函數,不再重複。
代碼如下:
    mmu_init函數在C語言中嵌入了彙編指令。

2. 第二部分代碼分析
    第二部分代碼leds.c中只有兩個函數:wait和main。wait函數用來延遲時間,main函數
用來循環點亮LED。與前面兩章所用的leds.c有兩點不同。
    (1)操作GPFCON、GPFDAT兩個寄存器時使用虛擬地址0xA0000050、0xA0000054,
在init.c中已經把虛擬地址0xA000 0000~(0xA000 0000 + 1M - 1)映射到物理地址0x5600 0000
~ (0x5600 0000 + 1M - 1);
    (2)在定義wait函數時使用了一個小技巧,將它定義成“static inline”類型,原因在源碼第
15行給出。
    leds.c代碼如下:
1
/*
2
 * leds.c: 循環點亮3個LED
3
 * 屬於第二部分程序,此時MMU已開啓,使用虛擬地址
4
 */ 
5
6
#define GPFCON      (*(volatile unsigned long *)0xA0000050)     // 物理地址0x56000050
7
#define GPFDAT      (*(volatile unsigned long *)0xA0000054)     // 物理地址0x56000054
8
9
#define GPF4_out    (1<<(4*2))
10
#define GPF5_out    (1<<(5*2))
11
#define GPF6_out    (1<<(6*2))
12
13
/*
14
 * wait函數加上“static inline”是有原因的,
15
 * 這樣可以使得編譯leds.c時,wait嵌入main中,編譯結果中只有main一個函數。
16
 * 於是在連接時,main函數的地址就是由連接文件指定的運行時裝載地址。
17
 * 而連接文件mmu.lds中,指定了leds.o的運行時裝載地址爲0xB4004000,
18
 * 這樣,head.S中的“ldr pc, =0xB4004000”就是跳去執行main函數。
19
 */
20
static inline void wait(unsigned long dly)
21
{
22
    for(; dly > 0; dly--);
23
}
24
25
int main(void)
26
{
27
    unsigned long i = 0;
28
    
29
    GPFCON = GPF4_out|GPF5_out|GPF6_out;        // 將LED1,2,4對應的GPF4/5/6三個引腳設爲輸出
30
31
    while(1){
32
        wait(30000);
33
        GPFDAT = (~(i<<4));     // 根據i的值,點亮LED1,2,4
34
        if(++i == 8)
35
            i = 0;
36
    }
37
38
    return 0;
39
}
40
注:實驗表明,代碼功能有問題,但沒找到哪裏有問題!!!_20171021

3. Makefile 和 連接腳本 mmu.lds 
Makefile內容如下:
    在源碼目錄下執行make命令時,make命令讀取Makefile文件發現目標文件mmu.bin
不存在,所以試圖使用它的依賴文件head.o、init.o、leds.o來生成mmu.bin;可是,這些
依賴文件也不存在,於是先使用其他規則來生成這些文件;使用第11、12行的規則來編譯
head.S以生成head.o,使用第8、9行的命令來生成mmu.bin。
    Makefile中第4行命令用來連接程序,它使用連接腳本mmu.lds來控制連接器的行爲。
文件mmu.lds內容如下:
1
SECTIONS{
2
    first  0x00000000 : { head.o init.o }
3
    second 0xb0004000 : AT(2048) { leds.o }
4
}
    連接腳本mmu.lds將程序分成兩個段:first和second。前者由head.o和init.o組
成,的加載地址和運行地址都是0,所以運行前不需要重新移動代碼。後者由leds.o組
成,它的加載地址爲2048,重定位地址爲0xB0004000,這表明段second存放在編譯所得
映像文件地址2048處,在運行前需要將它複製到地址0xB0004000處,這由init.c中的
copy_2th_to_sdram函數完成(注意:次函數將代碼複製開始地址爲0x30004000的內存中,
這是開啓MMU後虛擬地址0xB0004000對應的物理地址)。
    本實例程序中涉及代碼的複製、開啓MMU前使用物理地址尋址,開啓MMU後使用虛擬
地址尋址,相對複雜。爲了更形象地講解本程序,下面用圖7.16延時代碼的執行過程。
 
                                 圖7.16 程序複製代碼、設置頁表、啓動MMU的執行過程

7.2.3 實例測試

    在源碼目錄下執行make命令生成可執行程序mmu.bin,使用JTAG工具燒入NAND Flash
後,按復位鍵啓動系統。可以看見3個LED被輪流點亮,用來從0~7重複計數。LED閃爍的速
度比第6章的SDRAM實驗快,這是因爲開啓了Cache。
附:代碼:
鏈接: https://pan.baidu.com/s/1kV24a9L 密碼: tfab
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章