码迷,mamicode.com
首页 > 其他好文 > 详细

内存管理单元MMU

时间:2015-01-28 21:15:57      阅读:312      评论:0      收藏:0      [点我收藏+]

标签:

一、

技术分享
.text
.global _start
_start:
       ldr  sp, =4*1024                 @调用C函数之前,要设置栈指针;栈的作用:保存上下文,传递参数,保存临时变量;因为堆栈向下生长,所以要将栈指针设置到地址空间的顶层;总共可用的只有4K,也就是4*1024  
       bl   close_watchdog              @关看门狗
       bl   mem_ctrl_set                @设置存储控制器
       bl   copy_code_sdram             @复制代码到sdram
       bl   set_page                    @设置页表
       bl   start_mmu                   @启动mmu
       ldr  sp, =0xb4000000             @sdram的地址范围:0x30000000 ~ 0x3fffffff ; 取虚拟地址:0xb0000000 ~ 0xbfffffff 与之相对应映射;设置栈指针指向虚拟地址顶层
       ldr  pc, =0xb0004000             @pc也就是下一条指令执行前的地址,设置后就接着运行下一条指令,所以,功能也就是跳到sdram中继续运行;虚拟地址VA,变换后的虚拟地址MVA,物理地址PA,CPU看到的是VA,
                                        @caches和MMU利用MVA转换得到PA,设备看到的是PA;32位CPU也就是虚拟寻址:2^32=4G内存,将4G内存分为4096个段,每个段为1M,每个段对应一个描述符用于索引该段,每个描述符
                                        @的大小是4字节(4B),共有4096个描述符,所以描述符需要4*4096=16K的内存来存放,把段当作一本书的所有章,描述符也就是这本书的目录,也就是用16k的内存来存放它的目录,
                                        @在sdram中,用开始的16K的地址来存放这个目录,剩余的就用来去存放代码,所以,运行代码的物理地址是0xb0000000+16k(0x4000)=0xb0004000作为开始首地址的
halt_loop:
       b  halt_loop:                    @b和bl的区别就是跳转不返回下一条指令地址到lr(r14连接寄存器)寄存器,直接死循环
        
View Code

二、

技术分享
/*********************************************************************************************************************
         第一部分,关看门狗,设置控制寄存器
*********************************************************************************************************************/
/**************
  宏定义地址
**************/
#define  WTCON   (*(volatile unsigned long *) 0x53000000 )                     //看门狗控制寄存器
#define  mem_ctrl_reg     0x48000000                                           //存储控制器首地址
/************
  关看门狗
************/
void close_watchdog(void)
{
   WTCON=0;
}
/******************
  设置存储控制器
******************/
void mem_ctrl_set(void)
{
   int i;
   unsigned long const ctrl_list[]={                                          //const,变量不允许改变
                                      0x22011110,
                                      0x00000700,
                                      0x00000700,
                                      0x00000700,
                                      0x00000700,
                                      0x00000700,
                                      0x00000700,
                                      0x00018005,
                                      0x00018005,
                                      0x008c07a3,
                                      0x000000b1,
                                      0x00000030,
                                      0x00000030,
                                   };
   volatile unsigned long *p = (volatile unsigned long *) mem_ctrl_reg      //volatile是一个修饰符,告诉编译器此段代码不要优化; unsigned long *p 表示无符号长整形指针p; (volatile unsigned long *) mem_ctrl_reg 也就是把
                                                                            //这个指针指向一个地址;相当于前面定义一个指针,然后后面把这个指针指向一个地址 
   for(i=0;i<13;i++)
         p[i]=ctrl_list[i];                                                 //内存单位: 1Byte =4bit,1KB=1024Byte,1M=1024kb,1G=1024M ,最小的是bit(称位或者比特),二进制中每个0或者1就是一位,Byte称为字节;
                                                                            //一个指针通常分配4个字节,为什么呢?因为,首先,计算机的地址我们可以看作是一个十六进制数,一般由6位数(也就是6位16进制数)构成,换成二进制就是
}                                                                           //6*4=24bit,指针要保存这个地址,那么指针的内存一定要大于等于24bit=3Byte才行,因此,4个字节,能完全保存地址,并留有余地

/************************************************************************************************************************
       第二部分,把steppingstone中的代码(也就是led.c实现点灯功能的代码)复制到SDRAM
************************************************************************************************************************/
void  copy_code_sdram(void)
{
    unsigned int *p_step=(unsigned int *)2048                              //启动过程:1、上电,判断启动方式,因为本板里只有nandflash启动,所以设置启动方式为nandflash;2、设置为nandflash启动方式后,nanflash控制器自动将
                                                                           //存放在 bootloader的最前面4k代码复制到stepping stone中(stepping stone俗称起步石,也就是个4k的sdram,相当于起缓冲作用),stepping stone被映射到
                                                                           //NGCS0对应的bank0存储空间(地址就是0-4*1024内)
    unsigned int *p_sdram=(unsigned int *)0x30004000                       //因为前面16k(0X400)已经用来存放目录了,所以sdram中地址要从0x30004000开始   
    while(1)
     {
        *p_sdram=*p_step;                                                  //对指向的地址赋值
         p_step++;                                                         //地址+1
         p_sdram++;                                                        //地址+1
     } 
}

/*
C语言指针
1、指针: 指针就是地址,指向一个变量的指针,也就是这个变量的地址
2、指针变量: 本质是变量,也就是把另一个变量的地址(指针)放入这个变量
3、指针变量定义:  数据类型   *指针变量        如:int  *p
    在int *p中,p是指针变量,*表示该变量是指针变量,仅仅这样定义并没说明地址,因为变量没赋值,对它赋值后所赋的值就是地址
4、对指针变量赋值:
   如:int x=5 ,*p;   
       p=&x;           &x也就表示p的地址
5、p和*p的区别:
   p是指针变量,也就是所指的那个值的地址;*p就相当于是所指的那个值
6、指针和数组:
   定义指针变量: 如: int *p, a[5]={1,2,3,4,5};
                       p=a;
   引用数组元素: 1、第k个元素:a[k];第k个元素的地址:&a[k]
                  2、第k个元素:*(a+k);第k个元素的地址:a+k
                  3、第k个元素:*(p+k);第k个元素的地址:p+k
*/

/***************************************************************************************************************************
        第三部分,设置页表,启动MMU
***************************************************************************************************************************/
/*****************
   设置页表
*****************/
void set_page(void)
{
/* 
    C语言移位:
    1、<<,左移,低位补0;    _crol_循环左移,低位补之前移走的高位
    2、>>,右移,高位补0;    _cror_循环右移,高位补之前移走的低位
*/
     #define  MMU_FULL_ACCESS            (3<<10)          //倒数第[11:10]位,描述符为段时是这两位是AP,AP用于设置权限,取11时,表示权限在所有模式下都允许访问,因此先取这两位为1,后面一定要或上
     #define  MMU_MOMAIN                 (0<<5)          //域的地址[8:5]位,段描述符domain为0000(必须的),表示这1MB内存属于域0,因此先给这四位取0,后面一定要或上
     #define  MMU_SPECIAL                (1<<4)           //倒数第4位在描述符无效时是0,任何一种描述都是1,先取1选择,所以后面一定要或上
     #define  MMU_CACHABLE               (1<<3)           //倒数第3位是C即cacheable,先取1选择,后面需要就或上它也就是加上它,不需要就不或上它
     #define  MMU_BUFFERABLE             (1<<2)           //倒数第2位是B即BUFFERABLE,先取1选择,后面需要就或上它也就是加上它,不需要就不或上它
     #define  MMU_SECTION                (2)              //最低2位为是选择描述符方式,00无效,01粗页表,10段,11细页表,表示这是段描述符
     #define  MMU_SECDESC                (MMU_FULL_ACCESS | MMU_DOMAIN | MMU—SPECIAL | MMU_SECTION)      //不使用B和C,映射寄存器空间时使用
     #define  MMU_SECDESC_WB             (MMU_FULL_ACCESS | MMU_DOMAIN | MMU—SPECIAL | MMU—CACHEABLE | MMU_BUFFERABLE | MMU_SPECTION)   //回写式,映射steppingstone和sdram等内存时使用
     #define  MMU_SECTION_SIZE           0X100000

     unsigned long virtuladdr, physicaladdr;                           //定义虚拟地址和物理地址
     unsigned long *mu_tlb_base =(unsigned long *)0x30000000           //SDRAM的起始地址,用来存放段的目录
     /* steppingstone的起始物理地址是0,第一部分程序的起始运行地址也是0,为了在开启MMU后仍能运行第一部分的程序,将0-1M的虚拟地址映射到同样的物理地址*/
     virtuladdr=0;                                                    //
     physicaladdr=0;
     *(mmu_tlb_base + (virtuladdr>>20)) = (physicaladdr & 0xfff00000) | MMU_SECDESC_WB;    // 一级页表:物理地址=段基址+段内地址(p2), 段基址=一级页表索引(p1)+页表基址(TTB Base),                                                                                                                                             // 页表基址也就是SDRAM的首地址,p1也就是虚拟地址MVA[31:20]即virtuladdr>>20,段基址也就是物理地址的[31:20],[19:0]用来段内寻址(1M)
                                                                                           //虚拟地址32位=2^32=4G=4096*1M,因此共4096个段,每个段1M;4096=2^12,因此4096需要12位地址来存放,1M=2^20,因此段内的1M需要20位来存放;
                                                                                           //用物理地址的高12位来存放4096,物理地址的低20位来存放1M
 
   
     //将虚拟地址0xA0000000开始的1M虚拟空间映射到从0x56000000开始的1M物理地址空间
     virtuladdr = 0xA0000000;                                                              //虚拟地址的起始位置
     physicaladdr=0x56000000;                                                              //GPIO的起始位置
     *(mmu_tlb_base + (virtuladdr>>20))=(physicaladdr & 0xfff00000) | MMU_SECDESC;        //把从0xA0000000开始1M的虚拟地址空间映射到从0x56000000开始的1MB的物理地址空间
     
     
     //将虚拟地址0xB0000000开始的1M虚拟空间映射到0x3000000开始的1M物理地址空间
     virtuladdr=0xB0000000;         //虚拟地址的起始地址
     physicaladdr=0x3000000;        //物理地址的起始地址
     while(virtuladdr<0xB4000000)
     {
        *(num_tlb_base + (virtuladdr>>20))=(physicaladdr & 0xfff00000) | MMU_SEDESC_WB;
        virtuladdr +=0x100000;
        physicaladdr+=0x100000;
     }
}

/*************************************************************************************************************************
                         第四部分   启动MMU
*************************************************************************************************************************/
/*************
   启动MMU
*************/
void mmu_init(void)
{
   unsigned long ttb=0x30000000;
 
_asm_            //在C语言中可以嵌入汇编,_asm_(),括号内的是汇编指令,里面每条指令都要用双引号括起来
(
/*
CP15包括15个具体寄存器:
C0:ID号寄存器,
C0:缓冲类型寄存器(有两个R0,根据MCR操作数的不同传送不同的值,这也一个只读寄存器)
C1:控制寄存器
C2:转换表基址寄存器(TTB)
C3:域访问控制寄存器
C4:保留
C5:异常状态寄存器
C6:异常地址寄存器
C7:缓存操作寄存器
C8:TLB操作寄存器
C9:缓存锁存寄存器
C10:TLB锁存寄存器
C11:12、14:保留
C13:处理器ID
C15:测试配置寄存器2-24
*/
    
    "mov r0,#0\n"           //mov r0,#0 是指把立即数0给r0;mov r0, 0 是指把0单元的值送给R0*
    "mcr p15, 0, r0, c7, c7, 0\n"  //功能是使无效ICaches和DCaches;  mcr p15 0 这前面是必须的,mcr就是把ARM处理器寄存器r0的值放到协处理器P15的寄存器c7中,c7是缓存操作寄存器
    "mcr p15, 0, r0, c7, c10, 4\n"  // 使无效write buffer
    "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"       //mvn跟mov一样都是把一个立即数给一个寄存器,不同在于mov是给的这个立即数+1取负,如:MVN R0, #4;  R0 = -5 
    "mcr p15, 0, r0, c3, c0, 0\n"   //访问控制寄存器设为0xffffffff,不进行权限检查


/*对于控制寄存器,先读出其值,然后在这基础上修改感兴趣的位,然后再写入*/

   "mrc p15, 0, r0, c1, c0, 0\n"      //读取c1控制寄存器内容到主处理器r0内,mrc的过程跟mcr相反,mcr是主处理器数据到协处理器,mrc是协处理器数据到主处理器
   
/*
控制寄存器C1共32位:
M[0]: 0禁止MMU或者MPU,1使能MMU或MPU;
A[1]: 0禁止地址对齐检查功能,1使能地址对齐检查功能;
C[2]: 0禁止Catche,1使能Catche
W[3]: 0禁止,1使能写缓冲
P[4]: 0异常终端处理程序进入32位地址模式,异常中断处理程序进入26位地址模式
D[5]: 0禁止26位地址异常检查,使能26位地址异常检查
L[6]: 0选择早期中止模式
B[7]: 0使用小端,1使用大端
S[8]: 支持MMU的存储系统中,本控制位用作系统保护
R[9]: 支持MMU的存储系统中,本控制位用作ROM保护
F[10]: 本控制位由生产厂商定义,对于支持跳转预测的ARM系统,本控制位禁止/使能跳转预测功能
Z[11]: 0禁止跳转预测功能,1使能跳转预测功能
I[12]: 0禁止指令Catche,1使能指令Catche
V[13]: 0选择0x00000000 - 0x0000001c, 1选择0Xffff0000~0xffff001c
RR[14]: 0选择常规的淘汰算法,1选择预测性的淘汰算法
L4[15]: 0保持当前ARM版本的正常功能,1对于一些根据跳转地址的bit[10]进行状态切换的指令,忽略bit[0],不进行状态切换
Bit[31:16]:  保留
*/


/*清除不需要的位*/
     "bic  r0, r0, #0x3000\n"
     "bic  r0, r0, #0x300\n"
     "bic  r0, r0, #0x0087\n"

/*设置不需要的位*/
     "orr  r0, r0, #0x0002\n"
     "orr  r0, r0, #0x0004\n"
     "orr  r0, r0, #0x1000\n"
     "orr  r0, r0, #0x0001\n"
      
     "mcr  p15, 0, r0, c1, c0, 0\n"           //设定值写入寄存器
     :  //无输入" r " (ttb)
)
}

           
View Code

三、

技术分享
/*************************************************************************************************************************************************
                     C语言点亮LED
*************************************************************************************************************************************************/
#define  GPBCON    (*(volatile unsigned long *) 0xa0000010
#define  GPBDAT    (*(volatile unsigned long *) 0xa0000014

#define GPB5_out    (1<<(5*2))            //GPB5设置输出
#define GPB6_out    (1<<(6*2))            //GPB6设置输出
#define GPB7_out    (1<<(7*2))            //GPB7设置输出
#define GPB8_out    (1<<(8*2))            //GPB8设置输出

static inline void wait(unsigned long dly)  // static inline的内联函数,一般情况下不会产生函数本身的代码,而是全部被嵌入在被调用的地方,调用几次就嵌入几次,如果不加static,则表示该函数有可能会被其他编译单元所调用,
                                            //所以一定会产生函数本身的代码。所以加了static,一般可令可执行文件变小,编译结果只有一个main函数
{
    for(;dly>0;dly--);
}


int main(void)
{
   unsigned long i=0;
   GPBCON=GPB5_out | GPB6_out | GPB7_out | GPB8_out ;           //全部设置为输出
   while(1)
   {
      wait(3000000);
      GPBDAT = (~ (i<<5) );
      if(++i==16)
        i=0;
   }
   return 0;
}
View Code

四、

技术分享
SECTIONS
{
   firtst    0x00000000 : {  head.o  init.o}
   second    0xB0004000 :  AT(2048) { leds.o}
}
View Code

五、

技术分享
 1 objs: = head.o  init.o  leds.o                #使用变量objs代替后面的,后面使用时要用$(objs)
 2 
 3 mmu.bin:$(objs)  #关联
 4     arm-linux-ld  -Tmmu.lds -o mmu_elf $^
 5     arm-linux-objcopy -O binary -S mmu_elf $@
 6     arm-linux-objump -D -m arm mmu_elf>mmu.dis
 7     
 8 %.o:%.c
 9     arm-linux-gcc -Wall -O2 -c -o $@ $<
10 %.o:%.c
11     arm-linux-gcc -Wall -O2 -c -o $@<
12 clean:
13     rm -f mmu.bin mmu_elf mmu.dis *.o
14 
15       
View Code

 

内存管理单元MMU

标签:

原文地址:http://www.cnblogs.com/liubo118/p/4256806.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!