zoukankan      html  css  js  c++  java
  • 嵌入式学习-MMU(UBOOT)

    原文:https://blog.csdn.net/u011003120/article/details/51812188

    参考:

    https://blog.csdn.net/ipmux/article/details/19167605

    https://blog.csdn.net/wangdapao12138/article/details/79889461

    https://www.cnblogs.com/yangjiguang/p/7647801.html

    https://www.cnblogs.com/biaohc/p/6292376.html?utm_source=itdadao&utm_medium=referral

     

    MMU 存储器管理单元,在之前因为是操作物理地址,不需要MMU,因此是处于关闭状态的,而这次则是打开MMU并且使用MMU.

    一、MMU的作用

    • 1.将虚拟地址转化为物理地址
    • 2.进行访问权限的管理

    看上图可以得知,有三个运行的程序,他们的虚拟地址都为0x400000,但是若要使用物理地址,他们的物理地址不能够相同,因此就需要一个机制,使他们的相同的虚拟地址对应不同的物理地址,这个机制就是上图中的Page tables(即页表),虚拟地址通过查表的方式对应到不同的物理地址上。

    二、地址转化
    首先需要知道的是,以段(Section,1M)的方式进行转换时只用到一级页表,而页(Page)的方式进行转换时用到两级页表,有粗也转换和细页转换两种,页的大小有3种:大页(64KB)、小页(4KB)和极小页(1KB)。

    1.地址转化总体分析

    整个地址转换的过程分为了两步,为一级转换和二级转换。
    虚拟地址的[31:20]位作为一个表的索引,表的名字为translation table,即TTB,如果表的后两位为00,则为无效的转换,如上图,如果后两位为01,则表示第二级转换为粗页方式转换,如果为10,则表示接下来会按照段的方式转换,若是11,则第二级为细页方式转换。

    2.如何找到一级页表
    要想找到一级页表,首先需要知道的是一级页表的地址,即TTB,它是保存在CP15的C2寄存器中,

    看上面偷来的图^_^,一共有4096个转换描述符,即虚拟地址的[31:20]一共12位的最大寻址空间,虚拟地址的[31:20]再加上TTB,就是相对应的描述符,这样就找到了虚拟地址对应的描述符。

    3.段式转化简单分析
    还是上面的图,找到了虚拟地址相对应的地址描述符之后,描述符的[31:20]位便是物理地址的[31:20]位,虚拟地址的[19:0]位便是物理地址的[19:0]位,
    至于页的转换方式,则不再细讲。

    4.TTB
    MMU要自动进行虚拟地址到物理地址的转化,首先要找到一级页表,而一级页表的基地址(TTB:translation table base)则是保存在CP15的C2寄存器中。因此,当程序员创建好相应的页表后,需要将页表基地址写入该寄存器。
    把TTB 的值写入CP15 C2寄存器中后,MMU工作的时候,会从C2中取出TTB的值,因此MMU就会知道这张表的基地址,MMU就会工作了。
    这张表是放在内存里面的,
    这张表是工程师事先建立好的

    三MMU的配置与使用
    在本次课程中使用段式转化的方式,因此需要做以下几个工作:
    建立一级页表,写入TTB,打开MMU.

    本实验是通过点亮LED来完成的,LED的寄存器地址是

    #define GPMCON (volatile unsigned long *)0x7F008820
    #define GPMDAT (volatile unsigned long *)0x7F008824 

    因此,本实验的目的就是将LED的物理地址映射为虚拟地址

    #define GPMCON (volatile unsigned long *)0xA0008820
    #define GPMDAT (volatile unsigned long *)0xA0008824 

    1.建立一个一级页表

     

     如上图,对描述符的每一个寄存器进行讲解。

    [1:0]   最后两位固定为10 即使用段式的方式
    [2]      B 是否使用write buffer
    [3]      C 是否使用CACHE
    [4]      XN设置为1
    [8:5]   Domain,用来说明该段是属于16个域中的哪一个域,
    [9]      P表示段区间有ECC,ARM11不支持
    [11:10]   AP:访问权限,这个配合域,说明该段地址的访问权限。
    [14:12]   TEX   略
    [15]    APX 略
    [16]    S  表示是否共享
    [17]    nG 略
    [18]    0 
    [19]   nS 略
    
    

    各个寄存器的值如下:

    #define MMU_FULL_ACCESS     (3 << 10)   /* 访问权限 [11:10]*/
    #define MMU_DOMAIN          (0 << 5)    /* 属于哪个域 [8:5]*/
    #define MMU_SPECIAL         (1 << 4)    /* 必须是1 [4]*/
    #define MMU_CACHEABLE       (1 << 3)    /* cacheable  [3]*/
    #define MMU_BUFFERABLE      (1 << 2)    /* bufferable [2]*/
    #define MMU_SECTION         (2)         /* 表示这是段描述符  [1:0]*/
    #define MMU_SECDESC         (MMU_FULL_ACCESS | MMU_DOMAIN | MMU_SPECIAL | MMU_SECTION)

    建立页表

    void creat_page_table()
    {
        unsigned long *ttb = (unsigned long *)0x50000000; //表在内存的基地址处
        unsigned long vaddr; //虚拟地址
        unsigned long paddr; //物理地址
    
        vaddr = 0xa0000000; //虚拟地址
        paddr = 0x7F000000;
        *(ttb + (vaddr >> 20)) = (paddr&0xfff00000) | MMU_SECDESC;
        //*(ttb + (vaddr >> 20))  为表项的位置 
        //(paddr&0xfff00000) 获取高12位数据
        //MMU_SECDESC  访问led的gpio很简单,就不需要cache和buffer
    
    }

    *(ttb + (vaddr >> 20)) 即为页表的描述符,就是上面所说的 TTB + 虚拟地址[31:20]位,建立了上述的页表后,访问虚拟地址0xA0008824 通过页表,即可找到物理地址0x7F008824。

    2设置TTB和使能mmu
    设置TTB,其实就是将基地址写入CP15的C2寄存器中,因此使用汇编将其写入。
    设置访问权限,就是将域的权限设置为0B11,即不进行权限检查,主要是设置CP15的C3寄存器
    使能MMU,即打开MMU

    void mmu_init()
    {
        __asm__(
    
        /*设置TTB*/
        "ldr    r0, =0x50000000
    "                  
        "mcr    p15, 0, r0, c2, c0, 0
    "    
    
        /*不进行权限检查*/
        "mvn    r0, #0
    "                   
        "mcr    p15, 0, r0, c3, c0, 0
    "    
    
    
       /*使能MMU*/
        "mrc    p15, 0, r0, c1, c0, 0
    "    
        "orr    r0, r0, #0x0001
    "          
        "mcr    p15, 0, r0, c1, c0, 0
    "    
        : 
        : 
      );
    }

    3.内存的映射
    关于内存映射这一点不太清楚,大致的理解就是,将虚拟地址和物理地址的 0x50000000 - 0x540000000 这一段进行映射,这样直接操作虚拟地址就相当于操作物理地址。

        //映射内存
        vaddr = 0x50000000;
        paddr = 0x50000000;  //其虚拟地址和物理地址是一致的
        while (vaddr < 0x54000000)  //映射64mb
        {
            *(ttb + (vaddr >> 20)) = (paddr & 0xFFF00000) | MMU_SECDESC_WB;
            vaddr += 0x100000;
            paddr += 0x100000;
        }   

    全部代码

    /********************************************
    *file name: main.c
    *author   : stone
    *date     : 2016.6.30
    *function : MMU进行相关的操作
    *********************************************/
    #define GPMCON (volatile unsigned long *)0xA0008820  //虚拟地址
    #define GPMDAT (volatile unsigned long *)0xA0008824 
    
    #define MMU_FULL_ACCESS     (3 << 10)   /* 访问权限 [11:10]*/
    #define MMU_DOMAIN          (0 << 5)    /* 属于哪个域 [8:5]*/
    #define MMU_SPECIAL         (1 << 4)    /* 必须是1 [4]*/
    #define MMU_CACHEABLE       (1 << 3)    /* cacheable  [3]*/
    #define MMU_BUFFERABLE      (1 << 2)    /* bufferable [2]*/
    #define MMU_SECTION         (2)         /* 表示这是段描述符  [1:0]*/
    #define MMU_SECDESC         (MMU_FULL_ACCESS | MMU_DOMAIN | MMU_SPECIAL | MMU_SECTION)
    #define MMU_SECDESC_WB      (MMU_FULL_ACCESS | MMU_DOMAIN | MMU_SPECIAL | MMU_CACHEABLE | MMU_BUFFERABLE | MMU_SECTION)
    
    void creat_page_table()
    {
        unsigned long *ttb = (unsigned long *)0x50000000; //表在内存的基地址处
        unsigned long vaddr; //虚拟地址
        unsigned long paddr; //物理地址
    
        vaddr = 0xa0000000; //虚拟地址
        paddr = 0x7F000000;
        *(ttb + (vaddr >> 20)) = (paddr&0xfff00000) | MMU_SECDESC;
        //*(ttb + (vaddr >> 20))  为表项的位置 
        //(paddr&0xfff00000) 获取高12位数据
        //MMU_SECDESC  访问led的gpio很简单,就不需要cache和buffer
    
        //映射内存
        vaddr = 0x50000000;
        paddr = 0x50000000;  //其虚拟地址和物理地址是一致的
        while (vaddr < 0x54000000)  //映射64mb
        {
            *(ttb + (vaddr >> 20)) = (paddr & 0xFFF00000) | MMU_SECDESC_WB;
            vaddr += 0x100000;
            paddr += 0x100000;
        }   
    
    }
    
    void mmu_init()
    {
        __asm__(
    
        /*设置TTB*/
        "ldr    r0, =0x50000000
    "     /* 页表的基地址   */         
        "mcr    p15, 0, r0, c2, c0, 0
    "    
    
        /*不进行权限检查*//*cp15 c3 domain 控制权限*/
        "mvn    r0, #0
    "                   
        "mcr    p15, 0, r0, c3, c0, 0
    "    
    
       /*使能MMU*/
        "mrc    p15, 0, r0, c1, c0, 0
    "    
        "orr    r0, r0, #0x0001
    "          
        "mcr    p15, 0, r0, c1, c0, 0
    "    
        : 
        : 
      );
    }
    
    
    
    int gboot_main()
    {
        //*(GPMCON) = 0x1111;
        //*(GPMDAT) = 0x00;
    
    
        //1.建立页表
        creat_page_table();
    
        //2.写入TTB
    
    
    
        //3.使能
        mmu_init(); 
    
        *(GPMCON) = 0x1111;
        *(GPMDAT) = 0x00;   
    
        return 0;
    
    }
  • 相关阅读:
    Delphi TcxComboBox控件说明
    Delphi ComboBox的属性和事件、及几个鼠标事件的触发
    Delphi XE5的新功能“ TListView内置搜索过滤”
    可能是全网最全的解决摄像头无法创建视频捕捉过滤器问题?
    Delphi – TDataSet确定它是否在插入/编辑状态时被修改
    Delphi 获取DataSet传入参数后的SQL命令
    TClientDataSet[7]: 辨析 Field、FieldDef、Fields、FieldDefs、FieldList、FieldDefList
    枚举HasFlag函数实现的内部逻辑是什么
    在DBGrid中用代码实现按回车键跳到下一格的方法
    eclipse下没有Dynamic Web Project的处理方法
  • 原文地址:https://www.cnblogs.com/lh03061238/p/12625484.html
Copyright © 2011-2022 走看看