本工程代码由于比较大,不能仅仅使用开始的4K 空间。因此把它放到了0x30000000 开始的地方。
在连接文件 lds 中可以看到:
SECTIONS { . = 0x30000000; .text : { *(.text) } .rodata ALIGN(4):{ *(.rodata)} .data ALIGN(4) : { *(.data) } .bss ALIGN(4) : { *(.bss) *(COMMON) } }
这里把开头的代码,以及以后的数据 bss等部分都放在了地址 0x30000000之后。
通过分析OBJDUMP生成的ucos.dis文件也可以验证这点
ucos-elf: file format elf32-littlearm Disassembly of section .text: 30000000 <_start>: 30000000: ea000012 b 30000050 <ResetHandler> 30000004: eafffffe b 30000004 <_start+0x4> 30000008: eafffffe b 30000008 <_start+0x8> 3000000c: eafffffe b 3000000c <_start+0xc>
这里就有个问题,由于在系统中用到中断,而产生中断之后PC指针访问的是0x00 处的中断向量。而我们把中断向量放在了0x30000000 处,如果要让中断向量起作用就要映射代码了。
MMU_Init函数可以完成这个功能
void MMU_Init(void) { int i,j; //========================== IMPORTANT NOTE ========================= //The current stack and code area can't be re-mapped in this routine. //If you want memory map mapped freely, your own sophiscated MMU //initialization code is needed. //=================================================================== MMU_DisableDCache(); /// 关闭数据Cache MMU_DisableICache(); /// 关闭指令Cache //If write-back is used,the DCache should be cleared. for(i=0;i<64;i++) /// 16K的Cache由512条Cache Line组成 for(j=0;j<8;j++) /// ARM920T的一个Cache Line是32字节 MMU_CleanInvalidateDCacheIndex((i<<26)|(j<<5)); /// 清除无效的数据cache内的列表 MMU_InvalidateICache(); /// 无效整个指令cache #if 0 //To complete MMU_Init() fast, Icache may be turned on here. MMU_En:ableICache(); #endif MMU_DisableMMU(); //// 无效指令cache MMU_InvalidateTLB(); /// 无效整个TLB //MMU_SetMTT(int vaddrStart,int vaddrEnd,int paddrStart,int attr) ///attr为访问权限,页缓冲等属性配置 //MMU_SetMTT(0x00000000,0x07f00000,0x00000000,RW_CNB); //bank0 MMU_SetMTT(0x00000000,0x03f00000,(uint32)_start,RW_CB); //bank0 /// Important note, redirect IRQ vector to reset entry !!! MMU_SetMTT(0x04000000,0x07f00000,0,RW_NCNB); //bank0 MMU_SetMTT(0x08000000,0x0ff00000,0x08000000,RW_CNB); //bank1 MMU_SetMTT(0x10000000,0x17f00000,0x10000000,RW_NCNB); //bank2 MMU_SetMTT(0x18000000,0x1ff00000,0x18000000,RW_NCNB); //bank3 //MMU_SetMTT(0x20000000,0x27f00000,0x20000000,RW_CB); //bank4 MMU_SetMTT(0x20000000,0x27f00000,0x20000000,RW_CNB); //bank4 for STRATA Flash MMU_SetMTT(0x28000000,0x2ff00000,0x28000000,RW_NCNB); //bank5 //30f00000->30100000, 31000000->30200000 MMU_SetMTT(0x30000000,0x30100000,0x30000000,RW_NCNB); //bank6-1 MMU_SetMTT(0x30200000,0x33e00000,0x30200000,RW_NCNB); //bank6-2 // MMU_SetMTT(0x33f00000,0x33f00000,0x33f00000,RW_NCNB); //bank6-3 MMU_SetMTT(0x38000000,0x3ff00000,0x38000000,RW_NCNB); //bank7 MMU_SetMTT(0x40000000,0x47f00000,0x40000000,RW_NCNB); //SFR MMU_SetMTT(0x48000000,0x5af00000,0x48000000,RW_NCNB); //SFR MMU_SetMTT(0x5b000000,0x5b000000,0x5b000000,RW_NCNB); //SFR MMU_SetMTT(0x5b100000,0xfff00000,0x5b100000,RW_FAULT);//not used MMU_SetTTBase(MMU_TABLE_BASE); /// 设置基地址:C2用于保存页表的在内存中的基地址 MMU_SetDomain(0x55555550|DOMAIN1_ATTR|DOMAIN0_ATTR); //// MMU将整个存储空间分成16个域(domain),每个域具有相同的访问属性 //DOMAIN1: no_access, DOMAIN0,2~15=client(AP is checked) MMU_SetProcessId(0x0); /// 快速上下文切换,禁止 MVA="VA" MMU_EnableAlignFault(); /// 是否开启地址对齐检查功能 MMU_EnableMMU(); MMU_EnableICache(); MMU_EnableDCache(); //DCache should be turned on after MMU is turned on. }
其中数据缓存必须在MMU打开之后打开
启动MMU后,S3C2440的CPU核看到的、用到的只是虚拟地址VA,至于VA如何最终落实到物理地址PA上,CPU是不理会的。
MMU_SetMTT :
/**************************************************************************************** 设置页表函数: vaddrStart: 虚拟起始地址 vaddrEnd: 虚拟结束地址 paddrStart: 物理起始地址 attr: 访问属性 虚拟存储空间到物理存储空间的映射是以内存块为单位的:分为1M/64Kb/4Kb/1kB 虚拟存储空间中的一块连续的存储空间被映射成物理存储空间中同样大小的一块连续存储空间页表中,每一个地址变换 条目实际上就记录了一个虚拟存储空间的存储块的基地址与物理存储空间相应的一个存储块的基地址的对应关系。 ****************************************************************************************/ void MMU_SetMTT(int vaddrStart,int vaddrEnd,int paddrStart,int attr) { volatile uint32 *pTT; volatile int i,nSec; pTT=(uint32 *)MMU_TABLE_BASE+(vaddrStart>>20); //// 基址加VA高12位得到该描述符在页表中的地址 //// 页表中每一行对应一个虚地址页对应的实地址页的地址、该位的方位权限和该页的缓冲特性 nSec=(vaddrEnd>>20)-(vaddrStart>>20); /// 段大小 以M 为单位 for(i=0;i<=nSec;i++)*pTT++=attr |(((paddrStart>>20)+i)<<20); /// 映射规则:在映射页表中条目中写上相应的要映射的物理地址及相关属性。 /// 由虚拟地址可以找到在映射页表中的相应条目,再由条目中的内容找到相应的物理地址。 /// 注:条目中的内容:高12为物理地址段基址,映射规则的根本之处;低12位为相应的访问属性等 }
禁止数据cache
@************************************************************** @void MMU_DisableDCache(void) @************************************************************** .global MMU_DisableDCache MMU_DisableDCache: mrc p15,0,r0,c1,c0,0 /// 读协处理器中的寄存器到ARM处理器的r0里面,一般cp15就是MMU bic r0,r0,#R1_C /// 将MMU寄存器中的第2位清零,用于禁止数据cache. R1_C EQU (1<<2) mcr p15,0,r0,c1,c0,0 /// 写回到协处理器MMU寄存器 mov pc ,lr /// 程序返回
其中mrc是协处理器命令。用于读取协处理器中的寄存器的数据到ARM处理器的寄存器里面。
继续执行MMU_DisableICache();这段代码用来禁止指令cache
禁止mmu
@************************************************************** @void MMU_DisableMMU(void) @************************************************************** .global MMU_DisableMMU /// 关闭MMU MMU_DisableMMU: mrc p15,0,r0,c1,c0,0 bic r0,r0,#R1_M mcr p15,0,r0,c1,c0,0 mov pc ,lr
CP15协处理器(MMU)的第1位清零,则关闭了MMU, R1_M EQU (1)
无效TLB
@************************************************************** @ TLB functions @voic MMU_InvalidateTLB(void) @************************************************************** .global MMU_InvalidateTLB /// 无效整个TLB MMU_InvalidateTLB: mcr p15,0,r0,c8,c7,0 mov pc ,lr
MMU是内存管理单元,该单元通常是属于处理器的硬件,用于从虚拟地址到物理地址的映射。在典型的二级页表内存管理的系统中,映射过程为:系统(OS)为MMU依次准备
好页目录表地址,页表地址,MMU通过虚拟地址的各个段作为索引寻找到物理页面地址,与页内偏移地址一起构成最终物理地址。
从上可见,从MMU映射获得最终数据,需要访问三次内存(页目录表,页表,取数据),在分页级别更高的系统中,访问一次数据要更多的内存访问。为了提高访问速度,MMU中设置了一个叫TLB的高速缓存,存储了CPU最近访问内存的虚拟地址和物理地址。每当MMU得到一个要访问的虚拟地址时,先从TLB中检查有没有对应的项,如果有,则直接取出其物理地址,如果没有,则产生一个中断,由上述的MMU映射过程计算出物理地址,并将这一个新的(虚拟地址,物理地址)对替换掉TLB中某一项。 分页式管理
可以给每个应用程序虚拟的分配4G的内存空间;程序运行时物理内存不足时可以将不常用的内存页交换到硬盘上,当需要交换到硬盘上的页时,会触发处理器的中断将硬盘上的页读到内存里。
相关文章
http://embeddedlinux.org.cn/html/xinshourumen/200910/30-696.html