一、cache分類及應用場合
cache是內存和CPU之間的高速緩衝存儲器,其分爲icache(指令緩存)和dcache(數據緩存)。如果開啓了cache,當CPU運行時會將正在運行的指令地址附近的指令或者數據調入cache,這樣當運行下一條指令或用到下一條數據時直接從cache中查找,如果查找不到再訪問內存,以此加快CPU執行速度。icache可以直接開啓,而dcache需要開啓MMU之後才能開啓。
在啓動文件中開啓icache的代碼可以放在時鐘速度配置完成之後,代碼如下:
bl enable_icache
enable_icache:
mrc p15, 0, r0, c1, c0, 0
orr r0, r0, #(1<<12)
mcr p15, 0, r0, c1, c0, 0
mov pc, lr
二、MMU的應用
當CPU執行小的應用程序時,只要代碼大小在芯片存儲容量範圍內,CPU每次都可以直接訪問內存執行指令,但是當代碼很大超出芯片存儲容量時,則不能運行。此時需要MMU(內存管理單元),MMU可以將物理地址重映射,開啓MMU之後CPU每次訪問的都是虛擬地址,而每一個虛擬地址或者多個虛擬地址對應一個物理地址。32位的CPU虛擬地址範圍爲4G,映射方式爲在物理存儲地址上建立映射表。每個表有多個條目,每個條目大小1M,所以表的大小爲: 條目數 = 4G/1M = 4096 ,一個條目32位佔4Byte, 表大小 = 4096 * 4Byte = 16KB。因而只要在nand上佔據16KB的空間即可重映射4G的訪問範圍。
CPU發出的VA(虛擬地址)經過CP13的C13轉換爲MVA(modified virtual address),MMU所看到的地址其實是MVA,通常外在來看不加以細分,權當VA處理。MMU常用section轉換方式進行虛擬地址到物理地址的轉換,其格式如下:
高12位爲PA(物理地址)的高12位,AP設置訪問權限(常設置爲11,特權模式/用戶模式下可讀可寫,所有模式下允許任何訪問),domain(域設置,arm9有16個域,此處設置0選擇域0),C(是否開啓cache),B(是否開啓Write buffer),其他位用默認值。
重映射代碼如下:
#define MMU_SECDESC_AP (3<<10)
#define MMU_SECDESC_DOMAIN (0<<5)
#define MMU_SECDESC_NCNB (0<<2)
#define MMU_SECDESC_WB (3<<2)
#define MMU_SECDESC_TYPE ((1<<4) | (1<<1))
#define MMU_SECDESC_FOR_IO (MMU_SECDESC_AP | MMU_SECDESC_DOMAIN | MMU_SECDESC_NCNB | MMU_SECDESC_TYPE)
#define MMU_SECDESC_FOR_MEM (MMU_SECDESC_AP | MMU_SECDESC_DOMAIN | MMU_SECDESC_WB | MMU_SECDESC_TYPE)
#define IO 1
#define MEM 0
void create_secdesc(unsigned int *ttb, unsigned int va, unsigned int pa, int io)
{
int index;
index = va / 0x100000;
if (io)
ttb[index] = (pa & 0xfff00000) | MMU_SECDESC_FOR_IO;
else
ttb[index] = (pa & 0xfff00000) | MMU_SECDESC_FOR_MEM;
}
/*
* VA PA CB
* 0 0 00
* 0x40000000 0x40000000 11
*
* 64M sdram:
* 0x30000000 0x30000000 11
* ......
* 0x33f00000 0x33f00000 11
*
* register: 0x48000000~0x5B00001C
* 0x48000000 0x48000000 00
* .......
* 0x5B000000 0x5B000000 00
*
* Framebuffer : 0x33c00000
* 0x33c00000 0x33c00000 00
*
* link address:
* 0xB0000000 0x30000000 11
*/
void create_page_table(void)
{
/* ttb: translation table base */
unsigned int *ttb = (unsigned int *)0x32000000;
unsigned int va, pa;
int index;
/* 2.1 for sram/nor flash */
create_secdesc(ttb, 0, 0, IO);
/* 2.2 for sram when nor boot */
create_secdesc(ttb, 0x40000000, 0x40000000, MEM);
/* 2.3 for 64M sdram */
va = 0x30000000;
pa = 0x30000000;
for (; va < 0x34000000;)
{
create_secdesc(ttb, va, pa, MEM);
va += 0x100000;
pa += 0x100000;
}
/* 2.4 for register: 0x48000000~0x5B00001C */
va = 0x48000000;
pa = 0x48000000;
for (; va <= 0x5B000000;)
{
create_secdesc(ttb, va, pa, IO);
va += 0x100000;
pa += 0x100000;
}
/* 2.5 for Framebuffer : 0x33c00000 */
create_secdesc(ttb, 0x33c00000, 0x33c00000, IO);
/* 2.6 for link address */
create_secdesc(ttb, 0xB0000000, 0x30000000, MEM);
}
啓動文件中代碼如下:
/* 創建頁表 */
bl create_page_table
/* 啓動MMU */
bl mmu_enable
mmu_enable:
/* 把頁表基址告訴cp15 */
ldr r0, =0x32000000
mcr p15, 0, r0, c2, c0, 0
/* 設置域爲0xffffffff, 不進行權限檢查 */
ldr r0, =0xffffffff
mcr p15, 0, r0, c3, c0, 0
/* 使能icache,dcache,mmu */
mrc p15, 0, r0, c1, c0, 0
orr r0, r0, #(1<<12) /* enable icache */
orr r0, r0, #(1<<2) /* enable dcache */
orr r0, r0, #(1<<0) /* enable mmu */
mcr p15, 0, r0, c1, c0, 0
mov pc, lr
有幾個需要注意的關鍵點:
1. 由於代碼重定位腳本中將nand中的代碼重定位至0xB0000000,所以應該在重定位之前MMU進行地址的重映射,將0XB0000000映射到0X30000000,也就是nnad啓動時SDRAM的起始地址,有關重定位參考此鏈接S3C2440代碼重定位分析
2.在makefile時注意將mmu.c放至靠前的位置,必須在nand的前4K範圍完成地址重映射,代碼重定位,否則芯片將直接無法啓動。
3.當使用NOR啓動時,0地址映射時不能開啓cache和write buffer。
代碼重定位腳本:
SECTIONS{
. = 0xB0000000;
__code_start = .;
. = ALIGN(4);
.text : {*(.text)}
. = ALIGN(4);
.rodata : {*(.rodata)}
. = ALIGN(4);
.data : {*(.data)}
. = ALIGN(4);
__bss_start = .;
.bss : { *(.bss) *(.COMMON) }
_end = .;
}