`
sfp69sfp
  • 浏览: 19802 次
最近访客 更多访客>>
社区版块
存档分类
最新评论

S3C2410内存管理单元MMU基础实验

阅读更多

S3C2410内存管理单元MMU基础实验
2010年07月28日
  ARM芯片S3C2410内存管理单元MMU基础实验
  (11)实验十一:MMU
  在理论上概括或解释MMU,这不是我能胜任的。我仅基于为了理解本实验中操作MMU的代码而
  对MMU做些说明,现在先简单地描述虚拟地址(VA)、变换后的虚拟地址(MVA)、物理地址(PA)
  之间的关系:
  启动MMU后,S3C2410的CPU核看到的、用到的只是虚拟地址VA,至于VA如何最终落实到物理
  地址PA上,CPU是不理会的。而caches和MMU也是看不见VA的,它们利用VA变换得来的MVA
  去进行后续操作--转换成PA去读/写实际内存芯片,MVA是除CPU外的其他部分看见的虚拟地
  址。对于VA与MVA之间的变换关系,请打开数据手册551页,我摘取了"Figure 2-8. Address
  Mapping Using CP15 Register 13":
  图4 VA与MVA的关系
  如果VA= 32M
  利用PID生成MVA的目的是为了减少切换进程时的代价:如果两个进程占用的虚拟地址空间(VA)
  有重叠,不进行上述处理的话,当进行进程切换时必须进行虚拟地址到物理地址的重新影射,这
  需要重建页表、使无效caches和TLBS等等,代价非常大。但是如果像上述那样处理的话,进程切
  换就省事多了:假设两个进程1、2运行时的VA都是0-32M,则它们的MVA分别是(0x02000000-
  0x03ffffff)、(0x04000000-0x05ffffff)--前面说过MMU、Caches使用MVA而不使用VA,这样就不必
  进行重建页表等工作了。
  现在来讲讲MVA到PA的变换过程:请打开数据手册557页,"Figure 3-1. Translating Page
  Tables"(见下述图5)非常精练地概括了对于不同类型的页表,MVA是如何转换为PA的。图中的页
  表"Translation table"起始地址为"TTB base",在建立页表后,写入CP15的寄存器C2。
  使用MVA[31:20]检索页表"Translation table"得到一个页表项(entry,4字节),根据此entry的低2
  位,可分为以下4种:
  1、0b00:无效
  2、0b01:粗表(Coarse page)
  entry[31:10]为粗表基址(Coarse page table base address),据此可以确定一块1K大小的内存--称为
  粗页表(Coarse page table,见图5)。
  粗页表含256个页表项,每个页表项对应一块4K大小的内存,每个页表项又可以分为大页描述
  符、小页描述符。MVA[19:12]用来确定页表项。一个大页(64K)对应16个大页描述符,这16个大
  页描述符相邻且完全相同,entry[31:16]为大页基址(Large page base)。MVA[15:0]是大页内的偏移地
  址。一个小页(4K)对应1个小页描述符,entry[31:12]为小页基址(Small page base)。MVA[11:0]是小
  页内的偏移地址。
  3、0b10:段(Section)
  段的操作最为简单,entry[31:20]为段基址(Section base),据此可以确定一块1M大小的内存
  (Section,见图5),而MVA[19:0]则是块内偏移地址
  4、0b11:细表(Fine page)
  entry[31:12]为细表基址(Fine page table base address),据此可以确定一块4K大小的内存--称为细
  页表(Fine page table,见图5)。
  细页表含1024个页表项,每个页表项对应一块1K大小的内存,每个页表项又可以分为大页描述
  符、小页描述符、极小页描述符。MVA[19:10]用来确定页表项。一个大页(64K)对应64个大页描
  述符,这64个大页描述符相邻且完全相同,entry[31:16]为大页基址(Large page base)。MVA[15:0]是
  大页内的偏移地址。一个小页(4K)对应4个小页描述符,entry[31:12]为小页基址(Small page base)。
  MVA[11:0]是小页内的偏移地址。极小页(1K)对应1个极小页描述符,entry[31:10]为极小页基址
  (Tiny page base)。MVA[9:0]是极小页内的偏移地址。
  图5 Translating Page Tables
  访问权限的检查是MMU主要功能之一,它由描述符的AP和domain、CP15寄存器C1的R/S/A位、
  CP15寄存器C3(域访问控制)等联合作用。本实验不使用权限检查(令C3为全1)。
  下面简单介绍一下使用Cache和Write buffer:
  1、"清空"(clean)的意思是把Cache或Write buffer中已经脏的(修改过,但未写入主存)数据写入主
  存
  2、"使无效"(invalidate):使之不能再使用,并不将脏的数据写入主存
  3、对于I/O影射的地址空间,不使用Cache和Write buffer
  4、在使用MMU前,使无效Cache和drain write buffer
  与cache类似,在使用MMU前,使无效TLB。
  上面有些部分讲得很简略,除了作者水平不足之外,还在于本书的侧重点--实验。理论部分就
  麻烦各位自己想办法了。不过这些内容也足以了解本实验的代码了。本实验与实验9完成同样的
  功能:使用按键K1-K4作为4个外部中断--EINT1-3、EINT7,当Kn按下时,通过串口输
  出"EINTn,Kn pressed!",主程序让4个LED轮流从0到15计数。代码在目录MMU下。下面摘取与
  MMU相关的代码详细说明。
  先看看head.s代码(将一些注释去掉了):
  1 b Reset
  2 HandleUndef:
  3 b HandleUndef
  4 HandleSWI:
  5 b HandleSWI
  6 HandlePrefetchAbort:
  7 b HandlePrefetchAbort
  8 HandleDataAbort:
  9 b HandleDataAbort
  10 HandleNotUsed:
  11 b HandleNotUsed
  12 ldr pc, HandleIRQAddr
  13 HandleFIQ:
  14 b HandleFIQ
  15 HandleIRQAddr:
  16 .long HandleIRQ
  17 Reset: @函数disable_watch_dog, memsetup, init_nand,
  @nand_read_ll在init.c中定义
  18 ldr sp, =4096 @设置堆栈
  19 bl disable_watch_dog @关WATCH DOG
  20 bl memsetup_2 @初始化SDRAM
  21 bl init_nand @初始化NAND Flash
  22 bl copy_vectors_from_nand_to_sdram @在init.c中
  23 bl copy_process_from_nand_to_sdram @在init.c中
  24 ldr sp, =0x30100000 @重新设置堆栈
  @(因为下面就要跳到SDRAM中执行了)
  25 ldr pc, =run_on_sdram @跳到SDRAM中
  26 run_on_sdram:
  27 bl mmu_tlb_init @调用C函数mmu_tlb_init(mmu.c中),建立页表
  28 bl mmu_init @调用C函数mmu_init(mmu.c中),使能MMU
  29 msr cpsr_c, #0xd2 @进入中断模式
  30 ldr sp, =0x33000000 @设置中断模式堆栈
  31 msr cpsr_c, #0xdf @进入系统模式
  32 ldr sp, =0x30100000 @设置系统模式堆栈
  33 bl init_irq @调用中断初始化函数,在init.c中
  34 msr cpsr_c, #0x5f @设置I-bit=0,开IRQ中断
  35 ldr lr, =halt_loop @设置返回地址
  36 ldr pc, =main @b指令和bl指令只能前后跳转32M的范围,
  @所以这里使用向pc赋值的方法进行跳转
  37 halt_loop:
  38 b halt_loop
  39 HandleIRQ:
  40 sub lr, lr, #4 @计算返回地址
  41 stmdb sp!, { r0-r12,lr } @保存使用到的寄存器
  42 ldr lr, =int_return @设置返回地址
  43 ldr pc,=EINT_Handle @调用中断处理函数,在interrupt.c中
  44 int_return:
  45 ldmia sp!, { r0-r12,pc }^ @中断返回,
  @^表示将spsr的值复制到cpsr
  请注意第12、15行,我们将IRQ中断向量由以前的"b HandleIRQ"换成了:
  12 ldr pc, HandleIRQAddr
  15 HandleIRQAddr:
  16 .long HandleIRQ
  这是因为b跳转指令只能前后跳转32M的范围,而本实验中中断向量将重新放在VA=0xffff0000开始
  处(而不是通常的0x00000000),到HandleIRQAddr的距离远远超过了32M。将中断向量重新定位在
  0xffff0000处,是因为MMU使能后,中断发生时:
  1、如果中断向量放在0x00000000处,则对于不同的进程(PID),中断向量的MVA将不同
  2、如果中断向量放在0xffff0000处,则对于不同的进程(PID),中断向量的MVA也相同
  显然,如果使用1,则带来的麻烦非常大--对于每个进程,都得设置自己的中断向量。所以
  MMU使能后,处理中断的方法应该是2。
  第22行copy_vectors_from_nand_to_sdram函数将中断向量复制到内存物理地址0x33ff0000处,在
  mmu_tlb_init函数中会把0x33ff0000影射为虚拟地址0xffff0000。
  第23行copy_process_from_nand_to_sdram函数将存在nand flash开头的4K代码全部复制到0x30004000
  处(本实验的连接地址为0x30004000)。请注意SDRAM起始地址为0x30000000,前面的16K空间用来
  存放一级页表(在mmu_tlb_init中设置)。
  第27行mmu_tlb_init函数设置页表。本实验以段的方式使用内存,所以仅使用一级页表,且页表
  中所有页表项均为段描述符。
  mmu_tlb_init代码(在mmu.c中)如下:
  1 void mmu_tlb_init()
  2 {
  3 unsigned long entry_index;
  4 /*SDRAM*/
  5 for(entry_index = 0x30000000; entry_index >20)) =
  entry_index |(0x03>20)) =
  entry_index |(0x03>20)) =
  (VECTORS_PHY_BASE) |(0x03代码为:
  1 void mmu_init()
  2 {
  3 unsigned long ttb = MMU_TABLE_BASE;
  4 __asm__(
  5 "mov r0, #0 "
  6 /* invalidate I,D caches on v4 */
  7 "mcr p15, 0, r0, c7, c7, 0 "
  8 /* drain write buffer on v4 */
  9 "mcr p15, 0, r0, c7, c10, 4 "
  10 /* invalidate I,D TLBs on v4 */
  11 "mcr p15, 0, r0, c8, c7, 0 "
  12 /* Load page table pointer */
  13 "mov r4, %0 "
  14 "mcr p15, 0, r4, c2, c0, 0 "
  15 /* Write domain id (cp15_r3) */
  16 "mvn r0, #0 " /*0b11=Manager,不进行权限检查*/
  17 "mcr p15, 0, r0, c3, c0, 0 "
  18 /* Set control register v4 */
  19 mrc p15, 0, r0, c1, c0, 0 "
  20 /* Clear out 'unwanted' bits */
  21 "ldr r1, =0x1384 "
  22 "bic r0, r0, r1 "
  23 /* Turn on what we want */
  24 /*Base location of exceptions = 0xffff0000*/
  25 "orr r0, r0, #0x2000 "
  26 /* Fault checking enabled */
  27 "orr r0, r0, #0x0002 "
  28 #ifdef CONFIG_CPU_D_CACHE_ON /*is not set*/
  29 "orr r0, r0, #0x0004 "
  30 #endif
  31 #ifdef CONFIG_CPU_I_CACHE_ON /*is not set*/
  32 "orr r0, r0, #0x1000 "
  33 #endif
  34 /* MMU enabled */
  35 "orr r0, r0, #0x0001 "
  36 /* write control register *//*write control register P545*/
  37 "mcr p15, 0, r0, c1, c0, 0 "
  38 : /* no outputs */
  39 : "r" (ttb) );
  40 }
  此函数使用嵌入汇编的方式,第29行的"r" (ttb)表示变量ttb的值赋给一个寄存起作为输入参数,这
  个寄存器由编译器自动分配;第13行的"%0"表示这个寄存器。MMU控制寄存器C1中各位的含
  义(第18-37行),可以参考539页"Table 2-10. Control Register 1-bit Functions"。如果想详细了解本
  函数用到的操作协处理器的指令,可以参考数据手册529页"Appendix 2 PROGRAMMER'S
  MODEL"。
  本实验代码在CLOCK目录下,运行make命令后将可执行文件mmu下载、运行。然后将mmu.h文
  件中如下两行的注释去掉,重新make后下载运行mmu,可以发现LED闪烁的速度变快了很多--
  这是因为使用了cache(见上面代码28-33行):
  #define CONFIG_CPU_D_CACHE_ON 1
  #define CONFIG_CPU_I_CACHE_ON 1
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics