瞭解虛擬地址和物理地址的關係; 掌握如何通過設置MMU來控制虛擬地址到物理地址的轉化; 瞭解MMU的記憶體訪問許可權機制; 瞭解TLB、Cache、Write buffer的原理,使用時的註意事項; 通過實例深刻掌握上述要點; ...
本章目標: 瞭解虛擬地址和物理地址的關係; 掌握如何通過設置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,它們的格式如下:
1 <MCR | MRC>{cond} p#, <expression1>,Rd,cn,cm{ ,<expression2>} 2 MRC //從協處理器獲取數據,傳給CPU核的寄存器 3 MCR //數據從CPU核的寄存器傳給協處理器 4 {cond} //執行條件,省略時表示無條件執行 5 p# //協處理器序號 6 <expression1> //一個常數 7 Rd //CPU核的寄存器 8 cn和cm //協處理器中的寄存器 9 <expression2> //一個常數協處理器指令 其中,<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 20head.S 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以方便讀者查閱。
1 /* 2 *init.c:進行一些初始化,在Steppingstone中運行 3 *它和head.S同屬第一部分程式,此時MMU未開啟,使用物理地址 4 */ 5 6 /*WATCHDOG寄存器*/ 7 #define WTCON (*(volatile unsigned long *)0x53000000) 8 /*存儲控制器的寄存器起始地址*/ 9 #define MEM_CTL_BASE 0x48000000 10 11 12 /* 13 *關閉WATCHDOG,否則CPU會不斷重啟 14 */ 15 void disable_watch_dog(void) 16 { 17 WTCON = 0; //關閉WATCHDOG很簡單,往這個寄存器寫0即可 18 } 19 20 /* 21 *設置存儲控制器以使用SDRAM 22 */ 23 void memsetup(void) 24 { 25 /*SDRAM 13個寄存器的值*/ 26 unsigned long const mem_cfg_val[] = {0x22011110, //BWSCON 27 0x00000700, //BANKCON0 28 0x00000700, //BANKCON1 29 0x00000700, //BANKCON2 30 0x00000700, //BANKCON3 31 0x00000700, //BANKCON4 32 0x00000700, //BANKCON5 33 0x00018005, //BANKCON6 34 0x00018005, //BANKCON7 35 0x008c07a3, //REFRESH 36 0x000000b1, //BANKSIZE 37 0x00000030, //MRSRB6 38 0x00000030, //MRSRB7 39 }; 40 int i = 0; 41 volatile unsigned long *p = (volatile unsigned long *)MEM_CTL_BASE; 42 for(; i < 13; i++) 43 p[i] = mem_cfg_val[i]; 44 }init.c 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 }copy_2th_to_sdram() 剩下的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函數代碼如下:
1 /* 2 *設置頁表 3 */ 4 void create_page_table(void) 5 { 6 7 /* 8 *用於段描述符的一些巨集定義 9 */ 10 #define MMU_FULL_ACCESS (3 << 10) /*訪問許可權*/ 11 #define MMU_DOMAIN (0 << 5) /*屬於哪個域*/ 12 #define MMU_SPECIAL (1 << 4) /*必須是1*/ 13 #define MMU_CACHEABLE (1 << 3) /*cacheable*/ 14 #define MMU_BUFFERABLE (1 << 2) /*bufferable*/ 15 #define MMU_SECTION (2) /*表示這是段描述符*/ 16 #define MMU_SECDESC (MMU_FULL_ACCESS | MMU_DOMAIN | MMU_SPECIAL | \ 17 MMU_SECTION) 18 #define MMU_SECDESC_WB (MMU_FULL_ACCESS | MMU_DOMAIN | MMU_SPECIAL | \ 19 MMU_CACHEABLE | MMU_BUFFERABLE | MMU_SECTION) 20 #define MMU_SECTION_SIZE 0x00100000 21 22 unsigned long virtualaddr, physicaladdr; 23 unsigned long *mmu_tlb_base = (unsigned long *)0x30000000; 24 25 /* 26 *Steppingstone的起始物理地址為0,第一部分程式的起始運行地址也是0, 27 *為了在開啟MMU後仍然運行第一部分的程式, 28 *將0~1M的虛擬地址映射到同樣的物理地址 29 */ 30 virtualaddr = 0; 31 physicaladdr = 0; 32 *(mmu_tlb_base + (virtualaddr >> 20)) = (physicaladdr & 0xFFF00000) |\ 33 MMU_SECDESC_WB; 34 35 /* 36 *0x5600 0000時GPIO寄存器的起始物理地址, 37 *GPFCON和GPFDAT這兩個寄存器的物理地址0x56000050、0x56000054, 38 *為了在第二部分程式中能以地址0xA0000050、0xA0000054來操作GPFCON、GPFDAT, 39 *把從0xA000 0000開始的1MB虛擬地址空間映射到從0x5600 0000開始的1MB物理地址空間 40 */ 41 virtualaddr = 0xA0000000; 42 physicaladdr = 0x56000000; 43 *(mmu_tlb_base + (virtualaddr >> 20)) = (physicaladdr & 0xFFF00000) |\ 44 MMU_SECDESC; 45 46 /* 47 *SDRAM的物理地址範圍是0x3000 0000~0x33ff ffff, 48 *將虛擬地址0xB000 0000~0xB3ff ffff映射到物理地址0x3000 0000~0x33ff ffff上, 49 *總共64MB,涉及64個段描述符。 50 */ 51 virtualaddr = 0xB0000000; 52 physicaladdr = 0x30000000; 53 while(virtualaddr < 0xB4000000) 54 { 55 *(mmu_tlb_base + (virtualaddr >> 20)) = (physicaladdr & 0xFFF00000) |\ 56 MMU_SECDESC_WB; 57 virtualaddr += 0x100000; 58 physicaladdr += 0x100000; 59 } 60 }create_page_table() mmu_tlb_base被定義為unsigned long指針,所指向的記憶體為4位元組,剛好是一個描述符 的大小。在SDRAM的開始存放頁表—— “unsigned long *mmu_tlb_base = (unsigned long *)0x30000000;”。 其中最能體現頁表結構的代碼是:
*(mmu_tlb_base + (virtualaddr >> 20)) = (physicaladdr & 0xFFF00000) |\
MMU_SECDESC_WB;
*(mmu_tlb_base + (virtualaddr >> 20)) = (physicaladdr & 0xFFF00000) |\
MMU_SECDESC;
*(mmu_tlb_base + (virtualaddr >> 20)) = (physicaladdr & 0xFFF00000) |\
MMU_SECDESC_WB;