内存管理单元编程实例【转】

转自:http://blog.chinaunix.net/uid-29091195-id-4020919.html

一、MMU初步了解
    概念:内存管理单元(memory management unit),简称MMU
    主要作用:负责虚拟地址到物理地址之间的转换
             提供硬件机制的内存访问权限检查
    
    发展由来:由于程序的大小逐步发展到大于内存的大小这一问题的出现
    导致了虚拟存储器的产生,虚拟存储器的基本思想是:数据、堆栈的总
    大小可以超越物理存储器(大于内存的程序在执行的时候只将其中需要
    运行的一部分调入内存,其他部分在外部存储器中)。由于虚拟存储器
    的出现导致cpu发出的虚拟地址不能够直接送至地址总线,这是就需要
    一个将虚拟地址转换物理地址的设备,它就是内存管理单元(MMU)。
    
    32位处理器最大可以虚拟出的地址范围是0~0xffffffff
    
二、工作特点
    1、没有启用MMU时候,cpu发出的地址是物理地址
    2、启用MMU后,cpu发出的是虚拟地址
    注:另一说法其实是cpu总发出虚拟地址,只不过是在没有进行空间映射
    时候,虚拟地址到物理地址的转换是一种线性转换的机制。
    
    虚拟地址到物理地址的转换方法一般有两种:
    其一、使用一个固定的函数式
    其二、建立一个表格进行映射。(页表由一个个条目组成)
    
    ARM cpu使用页表进行地址转换的过程:
    1)根据给定的虚拟地址找到一级页表中的条目
    2)如果此条目是段描述符,直接返回物理地址,转换结束
    3)否则此条目是二级页表,继续在二级表中查找下一条目
    4)如果此条目是页描述符,则返回物理地址,转换结束
    5)其他的情况,则转换出错
    
    注:有上述的过程不难知道一般的ARM处理器最多用到二级页表。
    
三、TLB了解
    概念:转译查找缓存(translation lookaside buffer)
    作用:解决MMU每次转换都需查表,降低了cpu性能的问题
    工作过程:当cpu发出一个虚拟地址的时候,MMU会首先访问TLB,如果
    在TLB中有这一虚拟地址的描述符,则直接使用该描述符进行地址转换
    。否则MMU访问页表找到对应的描述符在进行地址转换和权限检查,同时
    使用一定的算法将这一描述填入TLB中。
    
四、Cache了解
    概念:在主存和cpu的寄存器之间的告诉小容量存储器,简称高速缓冲存
    储器,也有的将其叫做快取
    作用:将正在执行的指令地址附近的一部分指令或者数据从主存调入这
    个存储器,供cpu在一段时间内使用,从而提高程序运行的速度。
    
    一般离cpu越近的cache速度越高,但是其容量一般比较小(按一级、二级
    cache来说,一级更高速,二级容量更大一些)
    
    cache有指令快取(ICache)和数据快取(DCache)
    
五、mmu编程实例:(实验平台mini2440)
    下面的实验是将GPB的地址0x56000000映射到0xA0000000,然后再对虚拟地址
    进行操作,点亮led的实例,实验目的在于学习mmu的页表建立,以及的段址映射
    的方法。
    
    1、启动代码(start.S)
    .text
    .global _start
    _start:
        ldr sp, =0x4096            @以下是c程序,运行前需要设置栈指针
        bl  disable_watch_dog
        bl  memsetup
        bl  copy_2th_to_sdram
        bl  create_page_table
        bl  mmu_init
        ldr sp, =0xB4000000
        ldr pc, =0xB0000000     @这个地址很重要,他必须是第二阶段代码的重载地址(运行地址)
    halt:
        bl  halt
        
    2、初始化事件(init.c)

点击(此处)折叠或打开

  1. /*
  2. * init.c: 进行一些初始化,在Steppingstone中运行
  3. * 它和head.S同属第一部分程序,此时MMU未开启,使用物理地址
  4. */
  5. /* WATCHDOG寄存器 */
  6. #define WTCON (*(volatile unsigned long *)0x53000000)
  7. /* 存储控制器的寄存器起始地址 */
  8. #define MEM_CTL_BASE 0x48000000
  9. /*
  10. * 关闭WATCHDOG,否则CPU会不断重启
  11. */
  12. void disable_watch_dog(void)
  13. {
  14. WTCON = 0; // 关闭WATCHDOG很简单,往这个寄存器写0即可
  15. }
  16. /*
  17. * 设置存储控制器以使用SDRAM
  18. */
  19. void memsetup(void)
  20. {
  21. /* SDRAM 13个寄存器的值 */
  22. unsigned long const mem_cfg_val[]={ 0x22011110, //BWSCON
  23. 0x00000700, //BANKCON0
  24. 0x00000700, //BANKCON1
  25. 0x00000700, //BANKCON2
  26. 0x00000700, //BANKCON3
  27. 0x00000700, //BANKCON4
  28. 0x00000700, //BANKCON5
  29. 0x00018005, //BANKCON6
  30. 0x00018005, //BANKCON7
  31. 0x008C07A3, //REFRESH
  32. 0x000000B1, //BANKSIZE
  33. 0x00000030, //MRSRB6
  34. 0x00000030, //MRSRB7
  35. };
  36. int i = 0;
  37. volatile unsigned long *p = (volatile unsigned long *)MEM_CTL_BASE;
  38. for(; i < 13; i++)
  39. p[i] = mem_cfg_val[i];
  40. }
  41. /*
  42. * 将第二部分代码复制到SDRAM
  43. */
  44. void copy_2th_to_sdram(void)
  45. {
  46. unsigned int *pdwSrc = (unsigned int *)2048;       //这个地址必须是第二阶段代码的加载地址
  47. unsigned int *pdwDest = (unsigned int *)0x30000000;
  48. while (pdwSrc < (unsigned int *)4096)
  49. {
  50. *pdwDest = *pdwSrc;
  51. pdwDest++;
  52. pdwSrc++;
  53. }
  54. }
  55. /*
  56. * 设置页表
  57. */
  58. void create_page_table(void)
  59. {
  60. /*
  61. * 用于段描述符的一些宏定义
  62. */
  63. #define MMU_FULL_ACCESS (3 << 10) /* 访问权限 */
  64. #define MMU_DOMAIN (0 << 5) /* 属于哪个域 */
  65. #define MMU_SPECIAL (1 << 4) /* 必须是1 */
  66. #define MMU_CACHEABLE (1 << 3) /* cacheable */
  67. #define MMU_BUFFERABLE (1 << 2) /* bufferable */
  68. #define MMU_SECTION (2) /* 表示这是段描述符 */
  69. #define MMU_SECDESC (MMU_FULL_ACCESS | MMU_DOMAIN | MMU_SPECIAL | \
  70. MMU_SECTION)
  71. #define MMU_SECDESC_WB (MMU_FULL_ACCESS | MMU_DOMAIN | MMU_SPECIAL | \
  72. MMU_CACHEABLE | MMU_BUFFERABLE | MMU_SECTION)
  73. #define MMU_SECTION_SIZE 0x00100000
  74. unsigned long virtuladdr, physicaladdr;
  75. unsigned long *mmu_tlb_base = (unsigned long *)0x30000000;
  76. /*
  77. * Steppingstone的起始物理地址为0,第一部分程序的起始运行地址也是0,
  78. * 为了在开启MMU后仍能运行第一部分的程序,
  79. * 将0~1M的虚拟地址映射到同样的物理地址
  80. */
  81. virtuladdr = 0;
  82. physicaladdr = 0;
  83. *(mmu_tlb_base + (virtuladdr >> 20)) = (physicaladdr & 0xFFF00000) | \
  84. MMU_SECDESC_WB;
  85. /*
  86. * 0x56000000是GPIO寄存器的起始物理地址,
  87. * GPFCON和GPFDAT这两个寄存器的物理地址0x56000050、0x56000054,
  88. * 为了在第二部分程序中能以地址0xA0000050、0xA0000054来操作GPFCON、GPFDAT,
  89. * 把从0xA0000000开始的1M虚拟地址空间映射到从0x56000000开始的1M物理地址空间
  90. */
  91. virtuladdr = 0xA0000000;
  92. physicaladdr = 0x56000000;
  93. *(mmu_tlb_base + (virtuladdr >> 20)) = (physicaladdr & 0xFFF00000) | \
  94. MMU_SECDESC;
  95. /*
  96. * SDRAM的物理地址范围是0x30000000~0x33FFFFFF,
  97. * 将虚拟地址0xB0000000~0xB3FFFFFF映射到物理地址0x30000000~0x33FFFFFF上,
  98. * 总共64M,涉及64个段描述符
  99. */
  100. virtuladdr = 0xB0000000;
  101. physicaladdr = 0x30000000;
  102. while (virtuladdr < 0xB4000000)
  103. {
  104. *(mmu_tlb_base + (virtuladdr >> 20)) = (physicaladdr & 0xFFF00000) | \
  105. MMU_SECDESC_WB;
  106. virtuladdr += 0x100000;
  107. physicaladdr += 0x100000;
  108. }
  109. }
  110. /*
  111. * 启动MMU
  112. */
  113. void mmu_init(void)
  114. {
  115. unsigned long ttb = 0x30000000;
  116. // ARM休系架构与编程
  117. // 嵌入汇编:LINUX内核完全注释
  118. __asm__(
  119. "mov r0, #0\n"
  120. "mcr p15, 0, r0, c7, c7, 0\n" /* 使无效ICaches和DCaches */
  121. "mcr p15, 0, r0, c7, c10, 4\n" /* drain write buffer on v4 */
  122. "mcr p15, 0, r0, c8, c7, 0\n" /* 使无效指令、数据TLB */
  123. "mov r4, %0\n" /* r4 = 页表基址 */
  124. "mcr p15, 0, r4, c2, c0, 0\n" /* 设置页表基址寄存器 */
  125. "mvn r0, #0\n"
  126. "mcr p15, 0, r0, c3, c0, 0\n" /* 域访问控制寄存器设为0xFFFFFFFF,
  127. * 不进行权限检查
  128. */
  129. /*
  130. * 对于控制寄存器,先读出其值,在这基础上修改感兴趣的位,
  131. * 然后再写入
  132. */
  133. "mrc p15, 0, r0, c1, c0, 0\n" /* 读出控制寄存器的值 */
  134. /* 控制寄存器的低16位含义为:.RVI ..RS B... .CAM
  135. * R : 表示换出Cache中的条目时使用的算法,
  136. * 0 = Random replacement;1 = Round robin replacement
  137. * V : 表示异常向量表所在的位置,
  138. * 0 = Low addresses = 0x00000000;1 = High addresses = 0xFFFF0000
  139. * I : 0 = 关闭ICaches;1 = 开启ICaches
  140. * R、S : 用来与页表中的描述符一起确定内存的访问权限
  141. * B : 0 = CPU为小字节序;1 = CPU为大字节序
  142. * C : 0 = 关闭DCaches;1 = 开启DCaches
  143. * A : 0 = 数据访问时不进行地址对齐检查;1 = 数据访问时进行地址对齐检查
  144. * M : 0 = 关闭MMU;1 = 开启MMU
  145. */
  146. /*
  147. * 先清除不需要的位,往下若需要则重新设置它们
  148. */
  149. /* .RVI ..RS B... .CAM */
  150. "bic r0, r0, #0x3000\n" /* ..11 .... .... .... 清除V、I位 */
  151. "bic r0, r0, #0x0300\n" /* .... ..11 .... .... 清除R、S位 */
  152. "bic r0, r0, #0x0087\n" /* .... .... 1... .111 清除B/C/A/M */
  153. /*
  154. * 设置需要的位
  155. */
  156. "orr r0, r0, #0x0002\n" /* .... .... .... ..1. 开启对齐检查 */
  157. "orr r0, r0, #0x0004\n" /* .... .... .... .1.. 开启DCaches */
  158. "orr r0, r0, #0x1000\n" /* ...1 .... .... .... 开启ICaches */
  159. "orr r0, r0, #0x0001\n" /* .... .... .... ...1 使能MMU */
  160. "mcr p15, 0, r0, c1, c0, 0\n" /* 将修改的值写入控制寄存器 */
  161. : /* 无输出 */
  162. : "r" (ttb) );
  163. }

注:这部分代码是linux应用手册中的实例程序,只是修改了第二阶段的代码拷贝的目的地址

3、点亮led灯(led.c使用虚拟地址)

点击(此处)折叠或打开

  1. #define GPBCON                (*(volatile unsigned long *)0xA0000010) /* this is a virtual address */
  2. #define GPBDAT                (*(volatile unsigned long *)0xA0000014) /* this is a virtual address */
  3. #define GPBCON_5678_OUTPUT 0x1<<5*2 | 0x1<<6*2 | 0x1<<7*2 | 0x1<<8*2
  4. #define GPBDAT_5678_LOW        0x0<<5*1 | 0x0<<6*1 | 0x0<<7*1 | 0x0<<8*1
  5. int main(int argc, char *argv[])
  6. {
  7. GPBCON &= ~(0x3<<5*2 | 0x3<<6*2 | 0x3<<7*2 | 0x3<<8*2);
  8. GPBCON |= GPBCON_5678_OUTPUT;
  9. GPBDAT &= ~(0x1<<5*1 | 0x1<<6*1 | 0x1<<7*1 | 0x1<<8*1);
  10. GPBDAT |= GPBDAT_5678_LOW;
  11. GPBDAT |= 0x1<<6*1;
  12. while (1);
  13. return 0;
  14. }

4、工程链接脚本(mmu.lds)
    SECTIONS {
        first          0x00000000  :                    { start.o init.o }
        second     0xB0000000  :   AT(2048)    { led.o }
    }

注:second段的加载地址必须与拷贝代码中的起始地址一致,否则拷贝后代码无法执行
          second段的运行地址(重载地址)必须与启动代码start.S中的跳转地址一致,否则
          pc指针无法正确的跳转到第二阶段执行点亮led灯的程序。

5、工程Makefile
    objs := start.o init.o led.o

mmu.bin : $(objs)
        arm-linux-ld -Tmmu.lds -o mmu_elf $^
        arm-linux-objcopy -O binary -S mmu_elf [email protected]
        arm-linux-objdump -D -m arm mmu_elf > mmu.dis
    
    %.o:%.c
       arm-linux-gcc -Wall -O2 -c -o [email protected] $<

%.o:%.S
        arm-linux-gcc -Wall -O2 -c -o [email protected] $<

clean:
        rm -f mmu.bin mmu_elf mmu.dis *.o

时间: 2024-11-05 12:45:24

内存管理单元编程实例【转】的相关文章

MMU内存管理单元相关知识点总结

1.MMU是Memory Management Unit的缩写,中文名是内存管理单元,它是中央处理器(CPU)中用来管理虚拟存储器.物理存储器的控制线路,同时也负责虚拟地址映射为物理地址,以及提供硬件机制的内存访问授权,多用户多进程操作系统. 2.虚拟内存由来:许多年以前,当人们还在使用DOS或是更古老的操作系统的时候,计算机的内存还非常小,一般都是以K为单位进行计算,相应的,当时的程序规模也不大,所以内存容量虽然小,但还是可以容纳当时的程序.但随着图形界面的兴起还有用户需求的不断增大,应用程序

关于内存管理单元须要掌握的相关知识

1.怎样将虚拟地址转换为物理地址, 2.MMU的内存訪问权限机制, 3.TLB,Catch和write buffer原理. ---------- 内存管理单元MMU的功能:将虚拟地址映射到物理地址,并提供内存訪问权限. 没启动MMU时.cpu核.cache,MMU和外设等全部部件使用的都是物理地址. 启动MMU后,cpu核发出虚拟地址.由MVA转换成物理地址供cache和MMU使用. 最后使用物理地址訪问实际设备. 问题: 为什么是从虚拟地址到物理地址的转换. NAND Flash控制器须要掌握

02-JZ2440裸机学习之MMU内存管理单元【转】

本文转载自:http://blog.csdn.net/fengyuwuzu0519/article/details/66479248 版权声明:本文为博主原创文章,转载请注明http://blog.csdn.net/fengyuwuzu0519. 1.MMU定义: MMU是Memory Management Unit的缩写,中文名是内存管理单元,它是中央处理器(CPU)中用来管理虚拟存储器.物理存储器的控制线路,同时也负责虚拟地址映射为物理地址,以及提供硬件机制的内存访问授权,多用户多进程操作系

ARM4412的MMU内存管理单元

由于在裸板驱动的时候要中断>>异常>>MMU MMU是一个内存管理单元,在CP15协处理器里面,而CP15处理器是嵌入在ARM芯片里.功能是将虚拟地址映射到物理地址里面. 在使用MMU寄存器机制之前首先要配置寄存器.将寄存器的功能打开 映射的机制支持段模式1M,小段模式,小页模式(更精细) 000000-------1000000  rom的地址 TLB虚拟地址与物理地址缓存的对应关系 页表的产生:假如将4个G的虚拟地址映射到1G的物理地址,假如有100个线程同时使用表,此时,建立

关于内存管理单元需要掌握的相关知识

1.如何将虚拟地址转换为物理地址, 2.MMU的内存访问权限机制, 3.TLB,Catch和write buffer原理. ---------- 内存管理单元MMU的功能:将虚拟地址映射到物理地址,并提供内存访问权限. 没启动MMU时,cpu核,cache,MMU和外设等所有部件使用的都是物理地址. 启动MMU后,cpu核发出虚拟地址,由MVA转换成物理地址供cache和MMU使用. 最后使用物理地址访问实际设备. 问题: 为什么是从虚拟地址到物理地址的转换. NAND Flash控制器需要掌握

SoC软件架构设计之二:内存管理单元的硬件设计实现

程序的大部分代码都可以在必要的时候才加载到内存去执行,运行完后可以被直接丢弃或者被其他代码覆盖.我们PC上同时跑着很多的应用程序,每个应用程序使用的虚拟地址空间几乎可以整个线性地址空间(除了部分留给操作系统或者预留它用),可以认为每个应用程序都独占了整个虚拟地址空间(字长是32的CPU是4G的虚拟地址空间),但我们的物理内存只是1G或者2G.即多个应用程序在同时竞争使用这块物理内存,其必然会导致某个时刻只存在程序的某个片段在执行,也即是所有程序代码和数据分时复用物理内存空间-这就是内存管理单元(

MMU内存管理单元

arm-linux学习-(MMU内存管理单元) 什么是MMU MMU(Memory Management Unit)主要用来管理虚拟存储器.物理存储器的控制线路,同时也负责虚拟地址映射为物理地址,以及提供硬件机制的内存访问授权.多任务多进程操作系统.(来自百度百科,对其几个点不熟悉,因此可以只考虑加粗部分) 发展历史 注意:学习一个知识点,很重要的一步是了解其为什么而存在?它的存在是为了解决什么问题?然后,在学习的过程中带着这些问题去理解.去思考. 在许多年以前,还是使用DOS或一些古老的操作系

C++内存管理4-Windows编程中的堆管理(转)

1 引言 在大多数Windows应用程序设计中,都几乎不可避免的要对内存进行操作和管理.在进行大尺寸内存的动态分配时尤其显的重要.本文即主要对内存管理中的堆管理技术进行论述. 堆(Heap)实际是位于保留的虚拟地址空间中的一个区域.刚开始时,保留区域中的多数页面并没有被提交物理存储器.随着从堆中越来越多的进行内存分配,堆管理器将逐渐把更多的物理存储器提交给堆.堆的物理存储器从系统页文件中分配,在释放时有专门的堆管理器负责对已占用物理存储器的回收.堆管理也是Windows提供的一种内存管理机制.主

内存管理单元MMU

一. .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 @启动mm