Chinaunix首页 | 论坛 | 博客
  • 博客访问: 498895
  • 博文数量: 223
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 2145
  • 用 户 组: 普通用户
  • 注册时间: 2014-03-01 10:23
个人简介

该坚持的时候坚持,该妥协的时候妥协,该放弃的时候放弃

文章分类

全部博文(223)

文章存档

2017年(56)

2016年(118)

2015年(3)

2014年(46)

我的朋友

分类: 嵌入式

2016-10-01 14:31:14

一、MMU功能解析
1.1虚拟地址的使用
p1.c
  1. #include <stdio.h>

  2. int a = 1;

  3. void main()
  4. {

  5.     while(1)
  6.     {
  7.         printf("&a = %p, a = %d\n", &a,a);
  8.     }
  9.     sleep(3);
  10. }
输出:&a = 0x8049664, a = 1
p2.c
  1. #include <stdio.h>

  2. int b = 2;

  3. void main()
  4. {

  5.     while(1)
  6.     {
  7.         printf("&b = %p, b = %d\n", &b,b);
  8.     }
  9.     sleep(3);
  10. }
输出:&b = 0x8049664, b = 2

为什么a,b地址一样呢,明明值都不一样。
软件在使用物理地址时,需要通过mmu来实现管理。同样的用户空间,通过MMU后实际上对应了不同的物理地址


1.2 MMU的作用
1、将虚拟地址转化为物理地址
2、访问权限管理

二、深入剖析内存转化
2.1 地址转化流程总体分析
ARM920T(核手册) ———> memory management unit
一级转换(Level one fetch)-->TTB(translation table base address)
二级转换(Level two fetch)-->Section、Corase page table、Fine page table

地址转化类型
上图可以整理出ARM系统支持3种类型的地址转化:
1. 段式转化
2. 粗粒度页面转化
3. 细粒度页面转化

2.2 TTB配置
MMU要自动进行虚拟地址到物理地址的转化,首先要找到一级页表,而一级页表的基地址(TTB:translation table base)则是保存在CP15的C2寄存器中。因此,当程序员创建好相应的页表后,需要将页表基地址写入该寄存器。

2.段式转化分析
如果一级页表项中最后两位为’10’,接下的来转化过程将按照段方式来进行。

具体的转换方式:TTB的基地址保存在cp15中的c2寄存器中,VA(virtual address)的高12位作为TTB表中的偏移量,找到对应的一级页表。
一级页表后2位是'10',知道这是一个段式转化。然后将TTB中所对应的段基地址,加上MVA中的Section index。就得到了PA(physical address)的地址。(个人理解)

2.细页转化分析
如果一级页表项中最后两位为’11’,接下的来转化过程将按照细页方式来进行。(见上面Figure3-4)
转换方式:前12位找到TTB偏移后的地址,找到的地址中存放的是PA的基地址。这里多了10位的二级表index,同样的找到二级表中的偏移量对应的地址。
三个表相加得到PA的地址。


三、MMU的配置与使用
1.任务分解
建立一级页表
写入TTB
打开MMU

  1. #define GPBCON (volatile unsigned long*) 0xA0000010
  2. #define GPBDAT (volatile unsigned long*) 0xA0000014
  3. /*
  4.  * 用于段描述符的一些宏定义
  5.  */
  6. #define MMU_DOMAIN (0<<5) /*属于哪个域*/
  7. #define MMU_FULL_ACCESS (3<<10) /*访问权限*/
  8. #define MMU_SPECIAL (1<<4) /*必须使1*/
  9. #define MMU_CACHEABLE (1<<3) /*使能cache*/
  10. #define MMU_BUFFERABLE (1<<2) /*使能buffer*/
  11. #define MMU_SECTION (2<<0) /*段描述符*/

  12. #define MMU_SECDESC (MMU_SECTION|MMU_SPECIAL|MMU_DOMAIN|MMU_FULL_ACCESS)
  13. #define MMU_SECDESC_WB (MMU_SECTION|MMU_SPECIAL|MMU_DOMAIN|MMU_FULL_ACCESS|MMU_CACHEABLE|MMU_BUFFERABLE)

  14. void create_page_table()
  15. {
  16.     //设置ttb的起始位置
  17.     unsigned long *ttb = (unsigned long *)0x30000000;
  18.     //定义虚拟地址、物理地址
  19.     unsigned long vaddr, paddr;
  20.     //定义虚拟地址起始地址、物理起始地址
  21.     vaddr = 0xA0000000;
  22.     paddr = 0x56000000;
  23.     /* ttb加上虚拟地址高12位所指的地址就是PA的基地址位置,物理地址取前12位,依次设置后面的值(见AMR9核手册)
  24.      * 然后我们就建立的从vaddr的地址对应于paddr
  25.      */

  26.     *(ttb + (vaddr>>20) ) = (paddr & 0xfff00000)|MMU_SECDESC;

  27.     // 上面是对GPIO的映射,这里是对内存的映射
  28.     vaddr = 0x30000000;
  29.     paddr = 0x30000000;
  30.     //循环了64M的内存空间
  31.     while(vaddr < 0x34000000)
  32.     {
  33.         //建立内存映射表
  34.         *(ttb + (vaddr>>20)) = (paddr & 0xfff00000)|MMU_SECDESC_WB;
  35.         //加上1M,这个1M使手册中MVA[19:0]的大小
  36.         vaddr += 0x100000;
  37.         paddr += 0x100000;
  38.     }
  39. }

  40. void mmu_init()
  41. {
  42.     __asm__(
  43.     /*设置TTB*/
  44.     "ldr r0, =0x30000000\n"                    这里获取TTB对应的起始物理地址
  45.     "mcr p15, 0, r0, c2, c0, 0\n"              将这个地址写进p15的c2寄存器(在register2)
  46.     /*不进行权限检查*/
  47.     "mvn r0, #0 \n"                            设置r0为全1
  48.     "mcr p15, 0, r0, c3, c0, 0\n"              将r0写入到c3(cp15中的r3),全1就是仍何人都可读
  49.     /*使能mmu*/
  50.     "mrc p15, 0, r0, c1, c0, 0\n"              读取c1中的值
  51.     "orr r0, r0, #0x0001\n"
  52.     "mcr p15, 0, r0, c1, c0, 0\n"              设置开启mmu
  53.     :
  54.     :
  55.     );
  56. }

  57. int gboot_main()
  58. {
  59.     //建立页表、写入ttb
  60.     create_page_table();
  61.     //使能mmu
  62.     mmu_init();

  63.     *(GPBCON) = 0x15400;
  64.     *(GPBDAT) = 0x6bf;

  65.     return 0;
  66. }

扩展阅读:
http://blog.csdn.net/jianchi88/article/details/6988145

阅读(908) | 评论(0) | 转发(0) |
0

上一篇:C与汇编混合编程

下一篇:什么是GPIO?

给主人留下些什么吧!~~