北京蓝海微芯S3C2410开发板启动源码中MMU初始化部分试分析

void MMU_Init(MMU_Table table[])
 {
  //========================== 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初始化代码。

 

MMU_DisableDCache();
  MMU_DisableICache();
   MMU_InvalidateDCache(); //invalidate data cache all
  MMU_DCacheCleanInvalidateAll();
  MMU_InvalidateICache();
  
  #if 0
  //To complete MMU_Init() fast, Icache may be turned on here.
  MMU_EnableICache(); 
  #endif
     
  MMU_DisableMMU();
  MMU_InvalidateTLB();
  
  for(; table->vEnd; table++)
   MMU_SetMTT(table->vStart, table->vEnd, table->pStart, table->attr);
  
  MMU_SetTTBase(_MMUTT_STARTADDRESS);
  MMU_SetDomain(0x55555550|DOMAIN1_ATTR|DOMAIN0_ATTR); 
  //DOMAIN1: no_access, DOMAIN0,2~15=client(AP is checked)
  MMU_SetProcessId(0x0);
  MMU_EnableAlignFault();
  
  MMU_EnableMMU();
  MMU_EnableICache();
  MMU_EnableDCache(); //DCache should be turned on after MMU is turned on.
 }

从上至下一条一条分析
首先执行MMU_DisableDCache();程序跳转到2410slib.s文件的函数体

MMU_DisableDCache       
    mrc  p15,0,r0,c1,c0,0       
    bic  r0,r0,#R1_C
    mcr  p15,0,r0,c1,c0,0
    MOV_PC_LR


其中mrc是协处理器命令。用于读取协处理器中的寄存器的数据到ARM处理器的寄存器里面。
mrc  p15,0,r0,c1,c0,0 这句话的意思应该是读协处理器中的寄存器到ARM处理器的r0里面
。应该是ARM访问MMU,因为一般cp15就是MMU。
 bic  r0,r0,#R1_C 其中有如下定义R1_C    EQU     (1<<2)
则此句话是将MMU寄存器中的第2位清零,用于禁止数据cache
mcr  p15,0,r0,c1,c0,0写回到协处理器MMU寄存器 
MOV_PC_LR 程序返回

继续执行MMU_DisableICache();这段代码用来禁止指令cache
MMU_InvalidateDCache();无效数据缓存

顺序执行MMU_DCacheCleanInvalidateAll();这个函数如下:

void MMU_DCacheCleanInvalidateAll(void)
 {
  int i, j;
  
  //If write-back is used,the DCache should be cleared.
  for(i=0; i<64; i++)
   for(j=0; j<8; j++)
    MMU_CleanInvalidateDCacheIndex((i<<26)|(j<<5));
  __asm {
   mov r0, #0
   mcr p15, 0, r0, c7, c10, 4 // drain WB
  }
 }
 清除无效的数据cache内的列表,r0=index。
 其中MMU_CleanInvalidateDCacheIndex函数原型如下
 MMU_CleanInvalidateDCacheIndex  
    ;r0=index
 mcr  p15,0,r0,c7,c14,2
 MOV_PC_LR

接着执行MMU_InvalidateICache,该函数作用是无效指令cache
该函数源码如下:

MMU_InvalidateICache
  mov r0, #0 ;add by hzh
 mcr p15,0,r0,c7,c5,0
 MOV_PC_LR

继续执行MMU_DisableMMU();  关闭MMU
其函数原型如下:
  

EXPORT MMU_DisableMMU
 MMU_DisableMMU
    mrc p15,0,r0,c1,c0,0
    bic r0,r0,#R1_M
    mcr p15,0,r0,c1,c0,0
    MOV_PC_LR


其中R1_M    EQU (1)
看来应该是CP15协处理器(MMU)的第1位清零,则关闭了MMU

继续执行MMU_InvalidateTLB();该函数的作用是无效整个TLB
其中关于TLB的理解

  MMU是内存管理单元,该单元通常是属于处理器的硬件,用于从虚拟地址到物理
地址的映射。在典型的二级页表内存管理的系统中,映射过程为:系统(OS)为MMU
依次准备好页目录表地址,页表地质,MMU通过虚拟地址的各个段作为索引寻找到
物理页面地址,与页内偏移地址一起构成最终物理地址。
  从上可见,从MMU映射获得最终数据,需要访问三次内存(页目录表,页表,取
数据),在分页级别更高的系统中,访问一次数据要更多的内存访问。为了提高访
问速度,MMU中设置了一个叫TLB的高速缓存,存储了CPU最近访问内存的虚拟地址
和物理地址。每当MMU得到一个要访问的虚拟地址时,先从TLB中检查有没有对应
的项,如果有,则直接取出其物理地址,如果没有,则产生一个中断,由上述的
MMU映射过程计算出物理地址,并将这一个新的(虚拟地址,物理地址)对替换掉TLB
中某一项。
  分页式管理可以给每个应用程序虚拟的分配4G的内存空间;程序运行时物理内存
不足时可以将不常用的内存页交换到硬盘上,当需要交换到硬盘上的页时,会触发
处理器的中断将硬盘上的页读到内存里。

MMU是虚地址转换到物理地址的一个机制。大部分的CPU都支持虚地址。当然也有一
些没有虚地址的CPU。TLB是虚地址到物理地址映射表的一个缓存,一个高速的cach
e,用于快速地实现地址转换。完整的映射表在内存里面,CPU会把最近访问的映射
表的一部分放到TLB里面。TLB是和CPU在一起的,所以访问速度高。TLB可以自动更
新,也可以手动更新。具体要看CPU的实现。

函数原型如下:

MMU_InvalidateTLB
  mov r0, #0 ;add by hzh
    mcr p15,0,r0,c8,c7,0
    MOV_PC_LR

继续执行

for(; table->vEnd; table++)
  MMU_SetMTT(table->vStart, table->vEnd, table->pStart, table->attr);

 /****虚拟存储空间到物理存储空间的映射是以内存块为单位的:分为1M/64Lb/4Kb/1kB
虚拟存储空间中的一块连续的存储空间被映射成物理存储空间中同样大小的一块连续
存储空间页表中,每一个地址变换条目实际上就记录了一个虚拟存储空间的存储块的
基地址与物理存储空间相应的一个存储块的基地址的对应关系C2:存放的是内存中页
表的基地址[31:14]--查找页表 而C2寄存器的位[31:14]和虚拟地址的位[31:20]
(页表内的序号)结合作为一个32位数的高30位作为合成建立的页表中相应的地址转换
条目的地址,低2位为00
 此设置详细查看 <ARM结构和编程>的page 193,基于段的****/

MMU_SetMTT函数原型如下:

void MMU_SetMTT(U32 vaddrStart, U32 vaddrEnd, U32 paddrStart, int attr)
 {
  U32 *pTT;
  int i, nSec;
  
  pTT = (U32 *)_MMUTT_STARTADDRESS+(vaddrStart>>20);
  nSec = (vaddrEnd>>20)-(vaddrStart>>20);
  for(i=0; i<=nSec; i++)
   *pTT++ = attr|(((paddrStart>>20)+i)<<20);
 }

这些都是些地址映射方面的代码。

唉。先搞个大概的流程吧。要把这些都弄懂,看来得好好看看ARM体系结构了。
杜春雷的书这回该派上用场了。

接下来是MMU_SetTTBase(_MMUTT_STARTADDRESS);

;//C2用于保存页表的在内存中的基地址
;//页表中每一行对应一个虚地址页对应的实地址页的地址、该位的方位权限和该页的缓冲特性
;//通常把部分页表放在页表缓冲器TLB(translation lookaside buffer)中,换页表时,TLB要清空,因为。。。。
;//C8控制清空TLB
;//C10用于控制TLB中内容的锁定

;=========================
 ; Set TTBase
 ;=========================
 ;void MMU_SetTTBase(int base)
    EXPORT MMU_SetTTBase
 MMU_SetTTBase
    ;ro=TTBase
    mcr p15,0,r0,c2,c0,0
    MOV_PC_LR

顺序执行MMU_SetDomain(0x55555550|DOMAIN1_ATTR|DOMAIN0_ATTR); 
;//MMU将整个存储空间分成16个域(domain),每个域具有相同的访问属性
;//C3用于设置域的属性

;=========================
 ; Set Domain
 ;=========================
 ;void MMU_SetDomain(int domain)
    EXPORT MMU_SetDomain
 MMU_SetDomain
    ;ro=domain
    mcr p15,0,r0,c3,c0,0
    MOV_PC_LR

接下来
MMU_SetProcessId(0x0);      //快速上下文切换,禁止  MVA="VA"  《ARM结构和编程》 page 220 
MMU_EnableAlignFault();    //是否开启地址对齐检查功能     《ARM结构和编程》page 222

MMU_EnableMMU();
 MMU_EnableICache();
 MMU_EnableDCache(); //DCache should be turned on after MMU is turned on.


然后先后时能了MMU,指令缓存,数据缓存。
其中数据缓存必须在MMU打开之后打开