實驗目的:啟用MMU,映射SDRAM的地址空間,操作虛擬地址實現“點燈大法”,借此掌握MMU的使用。
實驗環境及說明:恒頤S3C2410開發板H2410。H2410核心板擴展有64MB的K4S561632 SDRAM(4M*16bit*4BANK),地址范圍是0x30000000~0x33FFFFFF。GPIO端口的地址范圍是0x56000000~0X560000B0。
實驗思路:開發板上電啟動后,自動將NandFlash開始的4K數據復制到SRAM中,然后跳轉到0地址開始執行,然后初始化存儲控制器SDRAM,把2K后的代碼從SRAM中復制到SDRAM中(存放在0x30004000,前16KB用來存放頁表)、設置頁表、啟動MMU實現虛擬地址映射GPIO寄存器和SDRAM,最后跳轉到SDRAM中(地址0xB0004000)運行。重新設置棧指針,跳到點燈代碼的入口點實現點燈操作。
知識掌握:MMU地址轉換、內存訪問權限檢查、TLB及Cache的使用
一、MMU地址轉換:
1.首先弄清除為什么要使用MMU納?MMU即內存管理單元,直白一點的講,就像食堂的餐具,所有的學生一起吃飯時不夠用,但食堂又不想再出資購買新的餐具(原因很明顯:一方面要成本,另一方面又占地方。這就像增加內存一樣),那么有沒有解決辦法?根據以往經驗得知不可能全學校的學習一起都到食堂吃飯,于是食堂就找幾個人負責餐具的管理(相當于MMU),他們一方面發放餐具,保證來的同學有餐具可用,另一方面又回收用完的餐具(這就相當于虛擬地址到物理地址之間建立了一個映射一樣,內存還是那么多,但從任意單個程序角度都好像用不完一樣)。當然如果有同學一個人拿好幾套餐具肯定不允許的(這就相當于內存的權限檢查)。MMU在地址轉換過程中涉及到三種地址:(VA---Virtual Address,虛擬地址)---這個就相當于餐具存放的地方(大家都可以領到餐具)。CPU核心看到和用到的只是虛擬地址VA,至于VA如果去對應物理地址PA,CPU核心不理會,大家也不會去關心總共有多少餐具吧;(MVA---Modified Virtual Address,變換后的虛擬地址)---這個相當于放假的時候,人很少,只發餐具好了,用過的就不先回收了,節省人員了。Caches和MMU看不到VA,他們利用MVA轉換得到PA,放假了回收餐具的人也不需要一直尋找用完的餐具;(PA---Physical Address,物理地址)---實際的餐具量,就那些。實際設備看不到VA、MVA,讀寫它們使用的是物理地址PA,同學們就餐一般會領到餐具。
2.虛擬地址到物理地址的轉換過程。ARM使用頁表來進行轉換,S3C2410最多會用到兩級頁表,以段(Section,1M)的方式進行轉換時只用到一級頁表,以頁(Page)的方式進行轉換時用到兩級頁表。頁的大小有3種:大頁(64KB)、小頁(4KB)和極小頁(1KB)。本文只是以段地址轉換過程為例來講解一下,頁的轉換大同小異。
★首先有個頁表基址寄存器(位置為協處理器CP15的寄存器C2),它里面寫入的就是一級頁表的地址,通過讀取它就可以找到一級頁表存放的起始位置。一級頁表的地址是16K對齊(所以[13:0]為0,使用[31:14]存儲頁表基址)。一級頁表使用4096個描述符來表示4GB空間,所以每個描述符對應1MB的虛擬地址,存儲它對應的1MB物理空間的起始地址,或者存儲下一級頁表的地址。使用MVA[31:20]來索引一級頁表(31-20一共12位,2^12=4096,所以是4096個描述符),得到一個描述符,每個描述符占4個字節。
★描述符最后兩位為0B10時,即是段的方式映射。[31:20]為段基址,此描述符低20位填充0后就是一塊1MB物理地址空間的起始地址。MVA[19:0]用來在這1MB空間中尋址。描述符的位[31:20]和MVA[19:0]構成了這個虛擬地址MVA對應的物理地址。以段的方式進行映射時,虛擬地址MVA到物理地址PA的轉換過程如下:①頁表基址寄存器位[31:14]和MVA[31:20]組成一個低兩位為0的32位地址,MMU利用這個地址找到段描述符;②取出段描述符的位[31:20](段基址),它和MVA[19:0]組成一個32位的物理地址(這就是MVA對應的PA)。
id="iframe_0.04625395219773054" src="data:text/html;charset=utf8,%3Cimg%20id=%22img%22%20src=%22http://www.arm79.com/attachment/Mon_1005/73_67_c19a93f3ccea9b3.jpg?_=1758289%22%20style=%22border:none;max-width:1505px%22%3E%3Cscript%3Ewindow.onload%20=%20function%20()%20%7Bvar%20img%20=%20document.getElementById('img');%20window.parent.postMessage(%7BiframeId:'iframe_0.04625395219773054',width:img.width,height:img.height%7D,%20'http://www.cnblogs.com');%7D%3C/script%3E" frameborder="0" scrolling="no" style="margin: 0px; padding: 0px; border-style: none; border-width: initial; width: 20px; height: 20px;">
id="iframe_0.06444169906899333" src="data:text/html;charset=utf8,%3Cimg%20id=%22img%22%20src=%22http://www.arm79.com/attachment/Mon_1005/73_67_ba0dd29d824d17a.jpg?_=1758289%22%20style=%22border:none;max-width:1505px%22%3E%3Cscript%3Ewindow.onload%20=%20function%20()%20%7Bvar%20img%20=%20document.getElementById('img');%20window.parent.postMessage(%7BiframeId:'iframe_0.06444169906899333',width:img.width,height:img.height%7D,%20'http://www.cnblogs.com');%7D%3C/script%3E" frameborder="0" scrolling="no" style="margin: 0px; padding: 0px; border-style: none; border-width: initial; width: 0px; height: 0px;"> ??
二、內存的訪問權限檢查
內存的訪問權限檢查決定一塊內存是否允許讀/寫。這由CP15寄存器C3(域訪問控制)、描述符的域(Domain)、CP15寄存器C1的R/S/A位和描述符的AP位共同決定。“域”決定是否對某塊內存進行權限檢查,"AP"決定如何對某塊內容進行權限檢查。S3C2440有16個域,CP15寄存器C3中每兩位對應一個域(一共32位),用來表示這個域是否進行權限檢查。
每兩位數據的含義:00---無訪問權限(任何訪問都將導致"Domain fault"異常);01---客戶模式(使用段描述符、頁描述符進行權限檢查);10---保留(保留,目前相當于“無訪問權限”);11---管理模式(不進行權限檢查,允許任何訪問)。"Domain"占用4位,用來表示內存屬于0-15哪一個域。
三、TLB和Cache
首先說兩者都是利用程序訪問的局部性原理,通過設置高速、小容量的存儲器來提高性能。
1.(TLB---Translation Lookaside Buffers,轉譯查找緩存):由于從MVA到PA的轉換需要訪問多次內存,大大降低了CPU的性能,故提出TLB辦法改進。當CPU發出一個虛擬地址時,MMU首先訪問TLB。如果TLB中含有能轉換這個虛擬地址的描述符,則直接利用此描述符進行地址轉換和權限檢查,否則MMU訪問頁表找到描述符后再進行地址轉換和權限檢查,并將這個描述符填入TLB中,下次再使用這個虛擬地址時就直接使用TLB用的描述符。使用TLB需要保證TLB中的內容與頁表一致,在啟動MMU之前,頁表中的內容發生變化后,尤其要注意。一般的做法是在啟動MMU之前使整個TLB無效,改變頁表時,使所涉及的虛擬地址對應的TLB中條目無效。?
2.(Cache,高速緩存):為提高程序的運行速度,在主存和CPU通用寄存器之間設置一個高速的、容量相對較小的存儲器,把正在執行的指令地址附近的一部分指令或數據從主存調入這個存儲器,供CPU在一段時間內使用。
★寫數據的兩種方式:①(Write Through,寫穿式)---任一CPU發出寫信號送到Cache的同時,也寫入主存,保證主存的數據同步更新。優點是操作簡單,但由于主存速度慢,降低了系統的寫速度并占用了總線的時間。②(Write Back,回寫式)---數據一般只寫到Cache,這樣可能出現Cache中的數據得到更新而主存中的數據不變(數據陳舊)的情況。此時可在Cache中設一個標志地址及數據陳舊的信息,只有當Cache中的數據被換出或強制進行”清空“操作時,才將原更新的數據寫入主存響應的單元中,保證了Cache和主存中數據一致。
★Cache有以下兩個操作:①(Clean,清空)---把Cache或Write buffer中已經臟的(修改過,但未寫入主存)數據寫入主存。②(Invalidate,使無效)---使之不能再使用,并不將臟的數據寫入主存。?
★S2C2440內置了(ICaches,指令Cache)、(DCaches,數據Cache)和(Write buffer,寫緩存),操作時需要用到描述符中的C位(Ctt)和B位(Btt)。①(ICaches,指令Cache)---系統剛上電或復位時,ICaches中的內容是無效的,并且ICaches功能關閉。往Icr位(CP15協處理器中寄存器1的第12位)寫1可以啟動ICaches,寫0停止ICaches。ICaches一般在MMU開啟后使用,此時描述符的C位用來表示一段內存是否可以被Cache。若Ctt=1,允許Cache,否則不允許。如果MMU沒有開啟,ICaches也可以被使用,此時CPU讀取指令時所涉及的內存都被當做允許Cache。ICaches關閉時,CPU每次取指都要讀取主存,性能低,所以通常盡早啟動ICaches。ICaches開啟后,CPU每次取指時都會先在ICaches中查看是否能找到所用指令,而不管Ctt是0還是1。如果找到成為Cache命中,找不到稱為Cache丟失,ICaches被開啟后,CPU的取指有如下三種情況:Cache命中且Ctt為1時,從ICaches中取指,返回CPU;Cache丟失且Ctt為1時,CPU從主存中取指,并且把指令緩存到Cache中;Ctt為0時,CPU從主存中取指。②(DCaches,數據Cache)---與ICaches相似,系統剛上電或復位時,DCaches中的內容無效,并且DCaches功能關閉,Write buffer中的內容也是被廢棄不用的。往Ccr位(CP15協處理器 中寄存器1的第二位)寫1啟動DCaches,寫0停止DCaches。Write buffer和DCaches緊密結合,額米有專門的控制來開啟和停止它。與ICaches不同,DCaches功能必須在MMU開啟之后才能被使用。DCaches被關閉時,CPU每次都去內存取數據。DCaches被開啟后,CPU每次讀寫數據時都會先在DCaches中查看是否能找到所要的數據,不管Ctt是0還是1,找到了稱為Cache命中,找不到稱為Cache丟失。
★使用Cache時需要保證Cache、Write buffer的內容和主存內容一致,保證下面兩個原則:①清空DCaches,使主存數據得到更新。②使無效ICaches,使CPU取指時重新讀取主存。
在實際編寫程序時,要注意如下幾點:①開啟MMU前,使無效ICaches,DCaches和Write buffer。②關閉MMU前,清空ICaches、DCaches,即將“臟”數據寫到主存上。③如果代碼有變,使無效ICaches,這樣CPU取指時會從新讀取主存。④使用DMA操作可以被Cache的內存時:將內存的數據發送出去時,要清空Cache;將內存的數據讀入時,要使無效Cache。⑤改變頁表中地址映射關系時也要慎重考慮。⑥開啟ICaches或DCaches時,要考慮ICaches或DCaches中的內容是否與主存保持一致。⑦對于I/O地址空間,不使用Cache和Write buffer。
四、MMU、TLB及Cache的控制指令
S3C2410除了ARM920T的CPU核心外,還有若干個協處理器,用來幫助主CPU完成一些特殊功能,對MMU、TLB及Cache等的操作就涉及到協處理器。格式如下:
<MCR|MRC>{條件} 協處理器編碼,協處理器操作碼1,目的寄存器,源寄存器1,源寄存器2,協處理器操作碼2
<MCR|MRC> {cond} p#,<expression1>,Rd,cn,cm{,<expression2>}
MRC??//從協處理器獲得數據,傳給ARM920T CPU核心寄存器
MCR??//數據從ARM920T CPU核心寄存器傳給協處理器
{cond}??//執行條件,省略時表示無條件執行
p#??//協處理器序號
<expression1>??//一個常數
Rd??//ARM920T CPU核心的寄存器
cn和cm??//協處理器中的寄存器
<expression2>??//一個常數
其中,<expression1>、cn、cm、<expression2>僅供協處理器使用,它們的作用如何取決于具體的協處理器。
示例代碼解析:
開啟MMU,并將虛擬地址0xA0000000~0xA0100000映射到物理地址0x56000000~0x56100000(GPFCON物理地址為0x56000050,GPFDAT物理地址為0x56000054);將虛擬地址0xB0000000~0xB3FFFFFF映射到物理地址0x30000000~0x33FFFFFF。本示例以段的方式進行地址映射,只使用一級頁表,通過上面內容可知一級頁表使用4096個描述符來表示4G空間(每個描述符對應1MB),每個描述符占4字節,所以一級頁表占16KB。使用SDRAM的開始16KB存放一級頁表,所以剩下的內存開始地址就為0x30004000,這個地址最終會對應虛擬地址0xB0004000(所以代碼運行地址為0xB0004000)。
★程序執行主要流程的示例代碼。
.text
.global _start
_start:
??? bl??disable_watch_dog?????????????????? @ 關閉WATCHDOG,否則CPU會不斷重啟
????bl??mem_control_setup??????????????????@ 設置存儲控制器以使用SDRAM
????ldr sp, =4096????????????????????????????????????@ 設置棧指針,以下是C函數調用前需要設好棧
????bl??copy_2th_to_sdram???????????????????@ 將第二部分代碼復制到SDRAM
????bl??create_page_table?????????????????????@ 設置頁表
????bl??mmu_init??????????????????????????????????????@ 啟動MMU,啟動以后下面代碼都用虛擬地址
????ldr sp, =0xB4000000?????????????????????? @ 重設棧指針,指向SDRAM頂端(使用虛擬地址)
????ldr pc, =0xB0004000????????????????????????@ 跳到SDRAM中繼續執行第二部分代碼
halt_loop:
????b?? halt_loop
★設置頁表。
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)?? /* 訪問權限AP */
#define MMU_DOMAIN??????????(0 << 5)????/* 屬于哪個域 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????????/*每個段描述符對應1MB大小空間*/
????unsigned long virtuladdr, physicaladdr;
????unsigned long *mmu_tlb_base = (unsigned long *)0x30000000;????????/*SDRAM開始地址存放頁表*/
????
????/*
???? * Steppingstone的起始物理地址為0,第一部分程序的起始運行地址也是0, 為了在開啟MMU后仍能運行第一部分的程序, 將0~1M的虛擬地址映射到同樣的物理地址
???? */
????virtuladdr = 0;
????physicaladdr = 0;
????/*虛擬地址[31:20]用于索引一級頁表,找到它對應的描述符,對應于(virtualaddr>>20)。段描述符中[31:20]保存段的物理地址,對應(physicaladdr & 0xFFF00000)*/
????*(mmu_tlb_base + (virtuladdr >> 20)) = (physicaladdr & 0xFFF00000) | MMU_SECDESC_WB;
????/*
???? * 0x56000000是GPIO寄存器的起始物理地址,GPBCON和GPBDAT這兩個寄存器的物理地址0x56000010、0x56000014, 為了在第二部分程序中能以地址0xA0000010、0xA0000014來操作GPBCON、GPBDAT,
???? * 把從0xA0000000開始的1M虛擬地址空間映射到從0x56000000開始的1M物理地址空間
???? */
????virtuladdr = 0xA0000000;
????physicaladdr = 0x56000000;
????*(mmu_tlb_base + (virtuladdr >> 20)) = (physicaladdr & 0xFFF00000) | MMU_SECDESC;
????/*
???? * SDRAM的物理地址范圍是0x30000000~0x33FFFFFF, 將虛擬地址0xB0000000~0xB3FFFFFF映射到物理地址0x30000000~0x33FFFFFF上, 總共64M,涉及64個段描述符
???? */
????virtuladdr = 0xB0000000;
????physicaladdr = 0x30000000;
????while (virtuladdr < 0xB4000000)
????{
????????*(mmu_tlb_base + (virtuladdr >> 20)) = (physicaladdr & 0xFFF00000) | MMU_SECDESC_WB;
????????virtuladdr += MMU_SECTION_SIZE;?
????????physicaladdr += MMU_SECTION_SIZE;?
????}
}
★ 啟動MMU。
void mmu_init(void)
{
????unsigned long ttb = 0x30000000;
__asm__(
????"mov????r0, #0\n"
????"mcr????p15, 0, r0, c7, c7, 0\n"????/* 使無效ICaches和DCaches */
????
????"mcr????p15, 0, r0, c7, c10, 4\n"?? /* drain write buffer on v4 */
????"mcr????p15, 0, r0, c8, c7, 0\n"????/* 使無效指令、數據TLB */
????
????"mov????r4, %0\n"?????????????????? /* r4 = 頁表基址 */
????"mcr????p15, 0, r4, c2, c0, 0\n"????/* 設置頁表基址寄存器 */
????
????"mvn????r0, #0\n"???????????????????
????"mcr????p15, 0, r0, c3, c0, 0\n"????/* 域訪問控制寄存器設為0xFFFFFFFF, 不進行權限檢查*/????
????/*?
???? * 對于控制寄存器,先讀出其值,在這基礎上修改感興趣的位,然后再寫入
???? */
????"mrc????p15, 0, r0, c1, c0, 0\n"????/* 讀出控制寄存器的值 */
????
????/* 控制寄存器的低16位含義為:.RVI ..RS B... .CAM
???? * R : 表示換出Cache中的條目時使用的算法,0 = Random replacement;1 = Round robin replacement
???? * V : 表示異常向量表所在的位置,0 = Low addresses = 0x00000000;1 = High addresses = 0xFFFF0000
???? * I : 0 = 關閉ICaches;1 = 開啟ICaches
???? * R、S : 用來與頁表中的描述符一起確定內存的訪問權限
???? * B : 0 = CPU為小字節序;1 = CPU為大字節序
???? * C : 0 = 關閉DCaches;1 = 開啟DCaches
???? * A : 0 = 數據訪問時不進行地址對齊檢查;1 = 數據訪問時進行地址對齊檢查
???? * M : 0 = 關閉MMU;1 = 開啟MMU
???? */
????
????/*??
???? * 先清除不需要的位,往下若需要則重新設置它們????
???? */
????????????????????????????????????????/* .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 */
????/*
???? * 設置需要的位
???? */
????"orr????r0, r0, #0x0002\n"??????????/* .... .... .... ..1. 開啟對齊檢查 */
????"orr????r0, r0, #0x0004\n"??????????/* .... .... .... .1.. 開啟DCaches */
????"orr????r0, r0, #0x1000\n"??????????/* ...1 .... .... .... 開啟ICaches */
????"orr????r0, r0, #0x0001\n"??????????/* .... .... .... ...1 使能MMU */
????
????"mcr????p15, 0, r0, c1, c0, 0\n"????/* 將修改的值寫入控制寄存器 */
????: /* 無輸出 */
????: "r" (ttb) );
}
實驗環境及說明:恒頤S3C2410開發板H2410。H2410核心板擴展有64MB的K4S561632 SDRAM(4M*16bit*4BANK),地址范圍是0x30000000~0x33FFFFFF。GPIO端口的地址范圍是0x56000000~0X560000B0。
實驗思路:開發板上電啟動后,自動將NandFlash開始的4K數據復制到SRAM中,然后跳轉到0地址開始執行,然后初始化存儲控制器SDRAM,把2K后的代碼從SRAM中復制到SDRAM中(存放在0x30004000,前16KB用來存放頁表)、設置頁表、啟動MMU實現虛擬地址映射GPIO寄存器和SDRAM,最后跳轉到SDRAM中(地址0xB0004000)運行。重新設置棧指針,跳到點燈代碼的入口點實現點燈操作。
知識掌握:MMU地址轉換、內存訪問權限檢查、TLB及Cache的使用
一、MMU地址轉換:
1.首先弄清除為什么要使用MMU納?MMU即內存管理單元,直白一點的講,就像食堂的餐具,所有的學生一起吃飯時不夠用,但食堂又不想再出資購買新的餐具(原因很明顯:一方面要成本,另一方面又占地方。這就像增加內存一樣),那么有沒有解決辦法?根據以往經驗得知不可能全學校的學習一起都到食堂吃飯,于是食堂就找幾個人負責餐具的管理(相當于MMU),他們一方面發放餐具,保證來的同學有餐具可用,另一方面又回收用完的餐具(這就相當于虛擬地址到物理地址之間建立了一個映射一樣,內存還是那么多,但從任意單個程序角度都好像用不完一樣)。當然如果有同學一個人拿好幾套餐具肯定不允許的(這就相當于內存的權限檢查)。MMU在地址轉換過程中涉及到三種地址:(VA---Virtual Address,虛擬地址)---這個就相當于餐具存放的地方(大家都可以領到餐具)。CPU核心看到和用到的只是虛擬地址VA,至于VA如果去對應物理地址PA,CPU核心不理會,大家也不會去關心總共有多少餐具吧;(MVA---Modified Virtual Address,變換后的虛擬地址)---這個相當于放假的時候,人很少,只發餐具好了,用過的就不先回收了,節省人員了。Caches和MMU看不到VA,他們利用MVA轉換得到PA,放假了回收餐具的人也不需要一直尋找用完的餐具;(PA---Physical Address,物理地址)---實際的餐具量,就那些。實際設備看不到VA、MVA,讀寫它們使用的是物理地址PA,同學們就餐一般會領到餐具。
2.虛擬地址到物理地址的轉換過程。ARM使用頁表來進行轉換,S3C2410最多會用到兩級頁表,以段(Section,1M)的方式進行轉換時只用到一級頁表,以頁(Page)的方式進行轉換時用到兩級頁表。頁的大小有3種:大頁(64KB)、小頁(4KB)和極小頁(1KB)。本文只是以段地址轉換過程為例來講解一下,頁的轉換大同小異。
★首先有個頁表基址寄存器(位置為協處理器CP15的寄存器C2),它里面寫入的就是一級頁表的地址,通過讀取它就可以找到一級頁表存放的起始位置。一級頁表的地址是16K對齊(所以[13:0]為0,使用[31:14]存儲頁表基址)。一級頁表使用4096個描述符來表示4GB空間,所以每個描述符對應1MB的虛擬地址,存儲它對應的1MB物理空間的起始地址,或者存儲下一級頁表的地址。使用MVA[31:20]來索引一級頁表(31-20一共12位,2^12=4096,所以是4096個描述符),得到一個描述符,每個描述符占4個字節。
★描述符最后兩位為0B10時,即是段的方式映射。[31:20]為段基址,此描述符低20位填充0后就是一塊1MB物理地址空間的起始地址。MVA[19:0]用來在這1MB空間中尋址。描述符的位[31:20]和MVA[19:0]構成了這個虛擬地址MVA對應的物理地址。以段的方式進行映射時,虛擬地址MVA到物理地址PA的轉換過程如下:①頁表基址寄存器位[31:14]和MVA[31:20]組成一個低兩位為0的32位地址,MMU利用這個地址找到段描述符;②取出段描述符的位[31:20](段基址),它和MVA[19:0]組成一個32位的物理地址(這就是MVA對應的PA)。
id="iframe_0.04625395219773054" src="data:text/html;charset=utf8,%3Cimg%20id=%22img%22%20src=%22http://www.arm79.com/attachment/Mon_1005/73_67_c19a93f3ccea9b3.jpg?_=1758289%22%20style=%22border:none;max-width:1505px%22%3E%3Cscript%3Ewindow.onload%20=%20function%20()%20%7Bvar%20img%20=%20document.getElementById('img');%20window.parent.postMessage(%7BiframeId:'iframe_0.04625395219773054',width:img.width,height:img.height%7D,%20'http://www.cnblogs.com');%7D%3C/script%3E" frameborder="0" scrolling="no" style="margin: 0px; padding: 0px; border-style: none; border-width: initial; width: 20px; height: 20px;">
id="iframe_0.06444169906899333" src="data:text/html;charset=utf8,%3Cimg%20id=%22img%22%20src=%22http://www.arm79.com/attachment/Mon_1005/73_67_ba0dd29d824d17a.jpg?_=1758289%22%20style=%22border:none;max-width:1505px%22%3E%3Cscript%3Ewindow.onload%20=%20function%20()%20%7Bvar%20img%20=%20document.getElementById('img');%20window.parent.postMessage(%7BiframeId:'iframe_0.06444169906899333',width:img.width,height:img.height%7D,%20'http://www.cnblogs.com');%7D%3C/script%3E" frameborder="0" scrolling="no" style="margin: 0px; padding: 0px; border-style: none; border-width: initial; width: 0px; height: 0px;"> ??
二、內存的訪問權限檢查
內存的訪問權限檢查決定一塊內存是否允許讀/寫。這由CP15寄存器C3(域訪問控制)、描述符的域(Domain)、CP15寄存器C1的R/S/A位和描述符的AP位共同決定。“域”決定是否對某塊內存進行權限檢查,"AP"決定如何對某塊內容進行權限檢查。S3C2440有16個域,CP15寄存器C3中每兩位對應一個域(一共32位),用來表示這個域是否進行權限檢查。
每兩位數據的含義:00---無訪問權限(任何訪問都將導致"Domain fault"異常);01---客戶模式(使用段描述符、頁描述符進行權限檢查);10---保留(保留,目前相當于“無訪問權限”);11---管理模式(不進行權限檢查,允許任何訪問)。"Domain"占用4位,用來表示內存屬于0-15哪一個域。
三、TLB和Cache
首先說兩者都是利用程序訪問的局部性原理,通過設置高速、小容量的存儲器來提高性能。
1.(TLB---Translation Lookaside Buffers,轉譯查找緩存):由于從MVA到PA的轉換需要訪問多次內存,大大降低了CPU的性能,故提出TLB辦法改進。當CPU發出一個虛擬地址時,MMU首先訪問TLB。如果TLB中含有能轉換這個虛擬地址的描述符,則直接利用此描述符進行地址轉換和權限檢查,否則MMU訪問頁表找到描述符后再進行地址轉換和權限檢查,并將這個描述符填入TLB中,下次再使用這個虛擬地址時就直接使用TLB用的描述符。使用TLB需要保證TLB中的內容與頁表一致,在啟動MMU之前,頁表中的內容發生變化后,尤其要注意。一般的做法是在啟動MMU之前使整個TLB無效,改變頁表時,使所涉及的虛擬地址對應的TLB中條目無效。?
2.(Cache,高速緩存):為提高程序的運行速度,在主存和CPU通用寄存器之間設置一個高速的、容量相對較小的存儲器,把正在執行的指令地址附近的一部分指令或數據從主存調入這個存儲器,供CPU在一段時間內使用。
★寫數據的兩種方式:①(Write Through,寫穿式)---任一CPU發出寫信號送到Cache的同時,也寫入主存,保證主存的數據同步更新。優點是操作簡單,但由于主存速度慢,降低了系統的寫速度并占用了總線的時間。②(Write Back,回寫式)---數據一般只寫到Cache,這樣可能出現Cache中的數據得到更新而主存中的數據不變(數據陳舊)的情況。此時可在Cache中設一個標志地址及數據陳舊的信息,只有當Cache中的數據被換出或強制進行”清空“操作時,才將原更新的數據寫入主存響應的單元中,保證了Cache和主存中數據一致。
★Cache有以下兩個操作:①(Clean,清空)---把Cache或Write buffer中已經臟的(修改過,但未寫入主存)數據寫入主存。②(Invalidate,使無效)---使之不能再使用,并不將臟的數據寫入主存。?
★S2C2440內置了(ICaches,指令Cache)、(DCaches,數據Cache)和(Write buffer,寫緩存),操作時需要用到描述符中的C位(Ctt)和B位(Btt)。①(ICaches,指令Cache)---系統剛上電或復位時,ICaches中的內容是無效的,并且ICaches功能關閉。往Icr位(CP15協處理器中寄存器1的第12位)寫1可以啟動ICaches,寫0停止ICaches。ICaches一般在MMU開啟后使用,此時描述符的C位用來表示一段內存是否可以被Cache。若Ctt=1,允許Cache,否則不允許。如果MMU沒有開啟,ICaches也可以被使用,此時CPU讀取指令時所涉及的內存都被當做允許Cache。ICaches關閉時,CPU每次取指都要讀取主存,性能低,所以通常盡早啟動ICaches。ICaches開啟后,CPU每次取指時都會先在ICaches中查看是否能找到所用指令,而不管Ctt是0還是1。如果找到成為Cache命中,找不到稱為Cache丟失,ICaches被開啟后,CPU的取指有如下三種情況:Cache命中且Ctt為1時,從ICaches中取指,返回CPU;Cache丟失且Ctt為1時,CPU從主存中取指,并且把指令緩存到Cache中;Ctt為0時,CPU從主存中取指。②(DCaches,數據Cache)---與ICaches相似,系統剛上電或復位時,DCaches中的內容無效,并且DCaches功能關閉,Write buffer中的內容也是被廢棄不用的。往Ccr位(CP15協處理器 中寄存器1的第二位)寫1啟動DCaches,寫0停止DCaches。Write buffer和DCaches緊密結合,額米有專門的控制來開啟和停止它。與ICaches不同,DCaches功能必須在MMU開啟之后才能被使用。DCaches被關閉時,CPU每次都去內存取數據。DCaches被開啟后,CPU每次讀寫數據時都會先在DCaches中查看是否能找到所要的數據,不管Ctt是0還是1,找到了稱為Cache命中,找不到稱為Cache丟失。
★使用Cache時需要保證Cache、Write buffer的內容和主存內容一致,保證下面兩個原則:①清空DCaches,使主存數據得到更新。②使無效ICaches,使CPU取指時重新讀取主存。
在實際編寫程序時,要注意如下幾點:①開啟MMU前,使無效ICaches,DCaches和Write buffer。②關閉MMU前,清空ICaches、DCaches,即將“臟”數據寫到主存上。③如果代碼有變,使無效ICaches,這樣CPU取指時會從新讀取主存。④使用DMA操作可以被Cache的內存時:將內存的數據發送出去時,要清空Cache;將內存的數據讀入時,要使無效Cache。⑤改變頁表中地址映射關系時也要慎重考慮。⑥開啟ICaches或DCaches時,要考慮ICaches或DCaches中的內容是否與主存保持一致。⑦對于I/O地址空間,不使用Cache和Write buffer。
四、MMU、TLB及Cache的控制指令
S3C2410除了ARM920T的CPU核心外,還有若干個協處理器,用來幫助主CPU完成一些特殊功能,對MMU、TLB及Cache等的操作就涉及到協處理器。格式如下:
<MCR|MRC>{條件} 協處理器編碼,協處理器操作碼1,目的寄存器,源寄存器1,源寄存器2,協處理器操作碼2
<MCR|MRC> {cond} p#,<expression1>,Rd,cn,cm{,<expression2>}
MRC??//從協處理器獲得數據,傳給ARM920T CPU核心寄存器
MCR??//數據從ARM920T CPU核心寄存器傳給協處理器
{cond}??//執行條件,省略時表示無條件執行
p#??//協處理器序號
<expression1>??//一個常數
Rd??//ARM920T CPU核心的寄存器
cn和cm??//協處理器中的寄存器
<expression2>??//一個常數
其中,<expression1>、cn、cm、<expression2>僅供協處理器使用,它們的作用如何取決于具體的協處理器。
示例代碼解析:
開啟MMU,并將虛擬地址0xA0000000~0xA0100000映射到物理地址0x56000000~0x56100000(GPFCON物理地址為0x56000050,GPFDAT物理地址為0x56000054);將虛擬地址0xB0000000~0xB3FFFFFF映射到物理地址0x30000000~0x33FFFFFF。本示例以段的方式進行地址映射,只使用一級頁表,通過上面內容可知一級頁表使用4096個描述符來表示4G空間(每個描述符對應1MB),每個描述符占4字節,所以一級頁表占16KB。使用SDRAM的開始16KB存放一級頁表,所以剩下的內存開始地址就為0x30004000,這個地址最終會對應虛擬地址0xB0004000(所以代碼運行地址為0xB0004000)。
★程序執行主要流程的示例代碼。
.text
.global _start
_start:
??? bl??disable_watch_dog?????????????????? @ 關閉WATCHDOG,否則CPU會不斷重啟
????bl??mem_control_setup??????????????????@ 設置存儲控制器以使用SDRAM
????ldr sp, =4096????????????????????????????????????@ 設置棧指針,以下是C函數調用前需要設好棧
????bl??copy_2th_to_sdram???????????????????@ 將第二部分代碼復制到SDRAM
????bl??create_page_table?????????????????????@ 設置頁表
????bl??mmu_init??????????????????????????????????????@ 啟動MMU,啟動以后下面代碼都用虛擬地址
????ldr sp, =0xB4000000?????????????????????? @ 重設棧指針,指向SDRAM頂端(使用虛擬地址)
????ldr pc, =0xB0004000????????????????????????@ 跳到SDRAM中繼續執行第二部分代碼
halt_loop:
????b?? halt_loop
★設置頁表。
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)?? /* 訪問權限AP */
#define MMU_DOMAIN??????????(0 << 5)????/* 屬于哪個域 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????????/*每個段描述符對應1MB大小空間*/
????unsigned long virtuladdr, physicaladdr;
????unsigned long *mmu_tlb_base = (unsigned long *)0x30000000;????????/*SDRAM開始地址存放頁表*/
????
????/*
???? * Steppingstone的起始物理地址為0,第一部分程序的起始運行地址也是0, 為了在開啟MMU后仍能運行第一部分的程序, 將0~1M的虛擬地址映射到同樣的物理地址
???? */
????virtuladdr = 0;
????physicaladdr = 0;
????/*虛擬地址[31:20]用于索引一級頁表,找到它對應的描述符,對應于(virtualaddr>>20)。段描述符中[31:20]保存段的物理地址,對應(physicaladdr & 0xFFF00000)*/
????*(mmu_tlb_base + (virtuladdr >> 20)) = (physicaladdr & 0xFFF00000) | MMU_SECDESC_WB;
????/*
???? * 0x56000000是GPIO寄存器的起始物理地址,GPBCON和GPBDAT這兩個寄存器的物理地址0x56000010、0x56000014, 為了在第二部分程序中能以地址0xA0000010、0xA0000014來操作GPBCON、GPBDAT,
???? * 把從0xA0000000開始的1M虛擬地址空間映射到從0x56000000開始的1M物理地址空間
???? */
????virtuladdr = 0xA0000000;
????physicaladdr = 0x56000000;
????*(mmu_tlb_base + (virtuladdr >> 20)) = (physicaladdr & 0xFFF00000) | MMU_SECDESC;
????/*
???? * SDRAM的物理地址范圍是0x30000000~0x33FFFFFF, 將虛擬地址0xB0000000~0xB3FFFFFF映射到物理地址0x30000000~0x33FFFFFF上, 總共64M,涉及64個段描述符
???? */
????virtuladdr = 0xB0000000;
????physicaladdr = 0x30000000;
????while (virtuladdr < 0xB4000000)
????{
????????*(mmu_tlb_base + (virtuladdr >> 20)) = (physicaladdr & 0xFFF00000) | MMU_SECDESC_WB;
????????virtuladdr += MMU_SECTION_SIZE;?
????????physicaladdr += MMU_SECTION_SIZE;?
????}
}
★ 啟動MMU。
void mmu_init(void)
{
????unsigned long ttb = 0x30000000;
__asm__(
????"mov????r0, #0\n"
????"mcr????p15, 0, r0, c7, c7, 0\n"????/* 使無效ICaches和DCaches */
????
????"mcr????p15, 0, r0, c7, c10, 4\n"?? /* drain write buffer on v4 */
????"mcr????p15, 0, r0, c8, c7, 0\n"????/* 使無效指令、數據TLB */
????
????"mov????r4, %0\n"?????????????????? /* r4 = 頁表基址 */
????"mcr????p15, 0, r4, c2, c0, 0\n"????/* 設置頁表基址寄存器 */
????
????"mvn????r0, #0\n"???????????????????
????"mcr????p15, 0, r0, c3, c0, 0\n"????/* 域訪問控制寄存器設為0xFFFFFFFF, 不進行權限檢查*/????
????/*?
???? * 對于控制寄存器,先讀出其值,在這基礎上修改感興趣的位,然后再寫入
???? */
????"mrc????p15, 0, r0, c1, c0, 0\n"????/* 讀出控制寄存器的值 */
????
????/* 控制寄存器的低16位含義為:.RVI ..RS B... .CAM
???? * R : 表示換出Cache中的條目時使用的算法,0 = Random replacement;1 = Round robin replacement
???? * V : 表示異常向量表所在的位置,0 = Low addresses = 0x00000000;1 = High addresses = 0xFFFF0000
???? * I : 0 = 關閉ICaches;1 = 開啟ICaches
???? * R、S : 用來與頁表中的描述符一起確定內存的訪問權限
???? * B : 0 = CPU為小字節序;1 = CPU為大字節序
???? * C : 0 = 關閉DCaches;1 = 開啟DCaches
???? * A : 0 = 數據訪問時不進行地址對齊檢查;1 = 數據訪問時進行地址對齊檢查
???? * M : 0 = 關閉MMU;1 = 開啟MMU
???? */
????
????/*??
???? * 先清除不需要的位,往下若需要則重新設置它們????
???? */
????????????????????????????????????????/* .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 */
????/*
???? * 設置需要的位
???? */
????"orr????r0, r0, #0x0002\n"??????????/* .... .... .... ..1. 開啟對齊檢查 */
????"orr????r0, r0, #0x0004\n"??????????/* .... .... .... .1.. 開啟DCaches */
????"orr????r0, r0, #0x1000\n"??????????/* ...1 .... .... .... 開啟ICaches */
????"orr????r0, r0, #0x0001\n"??????????/* .... .... .... ...1 使能MMU */
????
????"mcr????p15, 0, r0, c1, c0, 0\n"????/* 將修改的值寫入控制寄存器 */
????: /* 無輸出 */
????: "r" (ttb) );
}