1.了解虚拟地址和物理地址的关系

2.掌握如何控制MMU来控制虚拟地址到物理地址的转化

3.了解MMU内存方位权限

4.了解TLB、Cache、Write buffer的原理,使用时的注意事项

Cache简述及协处理器指令Cache简述及协处理器指令

在2440芯片里面除了CPU之外, Instruction MMU 指令MMU;

Data MMU 数据MMU;

InstructionC ACHE(16KB) 指令cache;

Data CACHE (16KB) 数据cache

全都通过CP15协处理器来进行操作这些

协处理器的含义作用 coprocessor协助主处理器做某些事情, 比如在ARM系统中有cp0 – cp15一共16个协处理器,其中cp15负责管理mmu icache

 1 int sum()
 2 {
 3     int I;
 4     int sum = 0;
 5     for(I = 0; I <= 100; I++ )
 6         {
 7             sum += I;
 8         }
 9     return sum;
10 }

1 不断的读写地址A和B

2 不断的执行for循环里面代码

2.1 取指令

2.2 执行指令

问SDRAM非常慢,那么怎么提高程序执行效率? 先引入一个感念,程序局部性原理

  • 时间局部性:在同一段时间里,有极大的概率访问同一地址的指令或数据
  • 空间局部性: 有极大概率访问到相邻空间的指令/数据

我们在一个比较慢的SDRAM上能不能在CPU上开一个高速缓存,把这些指令放进高速缓存icache

指令cache只有16KB 数据cache也只有16KB 而我们的SDRAM有64MB空间,显然不可能存储SDRAM中所有的内容,它只能存储一部分

cache的示意图

以读数据为例

  • 1 程序要读地址A的数据
ldr r0, [A的数据]

a. cpu以地址A查找cache,一开始cache无数据,导致cache miss

b.cpu以地址A去sdram中查找数据,存入cache,并返回给cpu

c.程序再次读取地址A的数据,cpu以地址A查找cache,cache中有数据,成为cache hit,直接从cache返回给cpu

d.程序读地址B的数据,cpu以地址B查找cache,cache直接返回给cpu

e.cache满了,cpu访问C,导致1.cache中的数据替换2.填充新的数据

  • 第一种不使用cache buffer 适用于直接硬件操作 gpio 得到最新数据
  • 第二种 不使用cache使用write buffer, cpu把写发给buffer,cpu就可以直接下一条指令
  • 第三种 WT 写通方式 使用cache不使用buffer,马上写硬件,CPU直接写给write buffer 由write执行缓慢写操作
  • 第四种 写回方式
miss:cpu写给write buffer,然后由write buffer写给硬件
hit:cpu数据写入cache标记为dirty1.cache替换时,dirty->write buffer->硬件2.强刷flash时,cache->write buffer->硬件

开启ICache代码示例

CPU中还有许多协处理器来协助主处理功能 比如2440有CP0 ~ CP15一共16个协处理器

CP15管理cache mmu 我们启动cache需要操作CP15 协处理器指令 先看硬件结构

1 mov r1,r0
2 是把r0传给r1
3 mrc
4 c coprocessor =传给=> register

mcr 是把主处理器的值发给协处理器 register =传给=> coprocessor

<MCR|MRC>{cond} p#,<expression1>,Rd,cn,cm{,<expression2>}
mcr P15, 0, r1,c1
expression1 值设置为0,表示用不到
r1 是主cpu寄存器里面的值
c1 是cp15寄存器里的值
cm, 用不到,写为c0
expression2 值设置为0,表示用不到
cm和expression2用来区分哪一个c1,一般写为c0, 0

反过来要从cp15寄存器读到主cpu寄存器

mrc p15, 0, r1, c1, c0, 0 

这条命令表示协处理器cp15 c1寄存器的值读出来写入主cpu的r1寄存器

接下来写程序使能cache 注意2440里有data cache和指令cache 其中data cache要启用地址映射才可以使用,只能使用指令cache

  1 reset:
  2     /* 关闭看门狗 */
  3     ldr r0, =0x53000000
  4     ldr r1, =0
  5     str r1, [r0]
  6
  7     /* 设置MPLL, FCLK : HCLK : PCLK = 400m : 100m : 50m */
  8     /* LOCKTIME(0x4C000000) = 0xFFFFFFFF */
  9     ldr r0, =0x4C000000
 10     ldr r1, =0xFFFFFFFF
 11     str r1, [r0]
 12
 13     /* CLKDIVN(0x4C000014) = 0X5, tFCLK:tHCLK:tPCLK = 1:4:8  */
 14     ldr r0, =0x4C000014
 15     ldr r1, =0x5
 16     str r1, [r0]
 17
 18     /* 设置CPU工作于异步模式 */
 19     mrc p15,0,r0,c1,c0,0
 20     orr r0,r0,#0xc0000000   //R1_nF:OR:R1_iA
 21     mcr p15,0,r0,c1,c0,0
 22
 23     /* 设置MPLLCON(0x4C000004) = (92<<12)|(1<<4)|(1<<0)
 24      *  m = MDIV+8 = 92+8=100
 25      *  p = PDIV+2 = 1+2 = 3
 26      *  s = SDIV = 1
 27      *  FCLK = 2*m*Fin/(p*2^s) = 2*100*12/(3*2^1)=400M
 28      */
 29     ldr r0, =0x4C000004
 30     ldr r1, =(92<<12)|(1<<4)|(1<<0)
 31     str r1, [r0]
 32
 33     /* 一旦设置PLL, 就会锁定lock time直到PLL输出稳定
 34      * 然后CPU工作于新的频率FCLK
 35      */
 36         /*
 37         使能icache
 38     */
 39     bl enable_icache
 40
 41     /* 设置内存: sp 栈 */
 42     /* 分辨是nor/nand启动
 43      * 写0到0地址, 再读出来
 44      * 如果得到0, 表示0地址上的内容被修改了, 它对应ram, 这就是nand启动
 45      * 否则就是nor启动
 46      */
 47     mov r1, #0
 48     ldr r0, [r1] /* 读出原来的值备份 */
 49     str r1, [r1] /* 0->[0] */
 50     ldr r2, [r1] /* r2=[0] */
 51     cmp r1, r2   /* r1==r2? 如果相等表示是NAND启动 */
 52     ldr sp, =0x40000000+4096 /* 先假设是nor启动 */
 53     moveq sp, #4096  /* nand启动 */
 54     streq r0, [r1]   /* 恢复原来的值 */
 55
 56     bl sdram_init
 57     //bl sdram_init2     /* 用到有初始值的数组, 不是位置无关码 */
 58
 59     /* 重定位text, rodata, data段整个程序 */
 60     bl copy2sdram
 61
 62     /* 清除BSS段 */
 63     bl clean_bss
 64
 65     /* 复位之后, cpu处于svc模式
 66      * 现在, 切换到usr模式
 67      */
 68     mrs r0, cpsr         /* 读出cpsr */
 69     bic r0, r0, #0xf     /* 修改M4-M0为0b10000, 进入usr模式 */
 70     bic r0, r0, #(1<<7)  /* 清除I位, 使能中断 */
 71     msr cpsr, r0
 72
 73     /* 设置 sp_usr */
 74     ldr sp, =0x33f00000
 75
 76     ldr pc, =sdram
 77 sdram:
 78     bl uart0_init
 79
 80     bl print1
 81     /* 故意加入一条未定义指令 */
 82 und_code:
 83     .word 0xdeadc0de  /* 未定义指令 */
 84     bl print2
 85
 86     swi 0x123  /* 执行此命令, 触发SWI异常, 进入0x8执行 */
 87
 88     //bl main  /* 使用BL命令相对跳转, 程序仍然在NOR/sram执行 */
 89     ldr lr, =halt
 90     ldr pc, =main  /* 绝对跳转, 跳到SDRAM */
 91
 92 halt:
 93     b halt
 94 如何使能icache 打开2410芯片手册
 95 enable_icache:
 96     /* 设置协处理器使能icache */
 97     mrc p15, 0, r0, c1, c0, 0
 98     orr r0, r0, #(1<<12)  /* r0 = r0 or (1<<12) */
 99     mcr p15, 0, r0, c1, c0, 0    //吧修改好的r0写给cp15的c1寄存器
100     mov pc, lr

View Code

MMU及地址映射

对于JZ2440它有64M内存(SDRAM),假设现在有N个APP同时运行,则:

①它们同时保存在SDRAM里;

②它们的地址各不相同;

之前我们讲过链接地址,链接地址就是程序运行时所处地址。

假设APP1所处的地址是Addr1,APP2所处的地址是Addr2,APPn所处的地址是Addrn,则编译某个App时,需要单独指定它的的链接地址,这是一个不可能完成的任务。因为,假如只有几个程序,为每个程序单独的指定地址还能够实现,但对于一个开放式的嵌入式系统,应用程序可能有成百上千个,你不可能重新编译这成百上千的应用程序,并且这些应用程序运行时保存的地址,也是不可预料的。为了解决上述问题,于是就引入了虚拟地址

也就是说虽然这些应用程序它们保存在内存中的位置各不一样,但对于CPU,它们运行时,都在同一个虚拟地址上。

举个例子,如视频中的两个hello应用程序,编译后查看反汇编代码,可以看到这两个程序的起始地址都是0x80A4。于是CPU运行两个APP时,都会去0x80A4读指令,然后经过MMU转换成Addr1、Addr2。这样,不同的APP可以在相同的虚拟地址,经过MMU地址转换后,在内存上是不同的物理地址,互不干扰。

这里说的同时运行,并不是真正的同时运行,CPU是分时操作,APP1先工作很短一段时间,再APP2工作很短的一段时间,宏观的来看就是两个在同时工作。

因此,引入虚拟机地址的原因之一:让APP可以以同样的链接地址来编译;

在电子系统里面,内存都是有限的,无论是嵌入式系统还是电脑,比如我们的JZ2440内存就只有64M,这时假如有一个APP,需要1G的内存。应用程序执行时,不是一次性将所有代码都放入内存,而是将要运行的部分依次放入,当放入的代码指令大于64M后,会先将SDRAM里暂时用不到代码指令先置换出来,再放入需要运行的代码指令。这样尽管SDRAM很小,也可以运行内存需要很大的应用程序,而这个置换管理的工作,就是由MMU完成

因此,引入虚拟机地址的原因之二:让大容量APP可以在资源少的系统上运行;

此外,不同的APP之间应该相互独立,避免APP1能直接访问到APP2,以防止APP1影响APP2。

因此,引入虚拟机地址的原因之三:权限管理,禁止访问其它空间;

CPU发出虚拟地址(VA)到达MMU,MMU转换成物理地址(PA)发给硬件,那么MMU怎么根据什么将一个虚拟地址转换成物理地址?

a.表格

最简单的方法就是弄一个表格,将VA和PA对应起来,根据VA就能找到PA。这种方法优点是简单,缺点是有点浪费空间,需要同时记录VA和PA的地址。

b.改进

在表格里面,我们只保存PA,PA1对应的VA是0~1M-1,PA2对应的VA是1M~2M-1,以此类推。这样改进后,只需要原来表格容量的一半即可。最后还需要把基地址告诉MMU,启动MMU。

怎么使用MMU?

1.在内存中创建这些表格(页表);

2.把页表基地址告诉MMU;

3.设置CP15,启动MMU;

对于一级页表,我们只需要关系“Section”这一行,里面的PA是物理地址,剩下的AP、Domain、C、B用来进行权限管理。

  • 权限管理:

权限管理就是是否允许程序访问某块内存,有以下几种情况:

a.完全不允许访问;

b.允许系统模式访问,不允许用户模式访问;

c.用户模式下,根据描述符中的AP决定怎么访问;

  • 域:

在CP15寄存器有个C3,用来进行域控制。

ARM9中,有16个域,每个域用2位来表示4种权限。

  • 条目/描述符(AP):

①设置domain;查看CP15 C3,确定域权限;

②如果域权限是01,使用AP来决定;

AP来自页表中的描述符,S、R来自CP15中的C1;

最后再来补充一个概念,前面我们运行多个APP,切换进程时,需要重新把0x80B4地址对应到不同的物理地址上,也就是说,每切换一个进程,你都需要重新修改下页表,这个开销非常的大,那有什么办法优化呢?

引入MVA,也就是修改后的虚拟地址。

if (VA<32M)MVA=VA|(pid<<25);
elseMVA=VA;

当虚拟地址小于32M时,MVA和进程的PID有关,否则等于VA,这就可以解决切换进程,频繁构造页表的问题。 假设现在有两个APP,分别是APP1和APP2,链接地址都是0x80b4,PID分别是1和2。

①当CPU运行APP1时,发出VA,MVA=VA(1<<25),对应的页表是PA=APP1所在的内存;

②当CPU运行APP2时,发出VA,MVA=VA(2<<25),对应的页表是PA=APP2所在的内存;

虽然我们发出的都是同一个VA,但因为PID不一样,所对应的页表项也就不一样,也就不需要重新去构造页表,这样进程从APP1切换到APP2时,只需要修改PID即可,不需要去重新创建页表,这样就可以提高切换效率。

MMU代码示例

在创建一个一级页表前,我们要先确定要映射哪些虚拟地址(VA),映射到哪个物理地址(PA),类型是否使用Cache和Buffer(CB)。

我们程序一开始运行是从0地址开始运行,为了保证使能MMU后,前后的地址保持一致,0地址这段我们需要映射。

在做了一些初始化后,会用到栈,如果是nor启动,栈是0x40000000开始。

VA                   PA                   CB
0                    0                    00
0x40000000           0x40000000           11

然后映射64M的SDRAM:

64M sdram:VA                   PA                   CB0x30000000           0x30000000           11......0x33f00000           0x33f00000           11

接着是映射寄存器,且不应该使用Cache和Buffer:

register: 0x48000000~0x5B00001CVA                   PA                   CB0x48000000           0x48000000           00.......0x5B000000           0x5B000000           00

涉及LCD的话,还有Framebuffer:

 Framebuffer : 0x33c00000VA                   PA                   CB0x33c00000           0x33c00000           00

同时,为了验证映射成功,先修改链接脚本中的链接地址为0xB00000000,再对应的映射0xB00000000到原来的0x300000000:

link address:VA                   PA                   CB0xB0000000           0x30000000           11
#define MMU_SECDESC_AP      (3<<10)
#define MMU_SECDESC_DOMAIN  (0<<5)
#define MMU_SECDESC_NCNB    (0<<2)
#define MMU_SECDESC_WB      (3<<2)
#define MMU_SECDESC_TYPE    ((1<<4) | (1<<1))#define MMU_SECDESC_FOR_IO   (MMU_SECDESC_AP | MMU_SECDESC_DOMAIN | MMU_SECDESC_NCNB | MMU_SECDESC_TYPE)
#define MMU_SECDESC_FOR_MEM   (MMU_SECDESC_AP | MMU_SECDESC_DOMAIN | MMU_SECDESC_WB | MMU_SECDESC_TYPE)

设置页表的第一步,就是设置页表保存的位置在哪,随便选择一个没使用过的空间即可,大小为16K:

/* ttb: translation table base */
unsigned int *ttb = (unsigned int *)0x32000000;

第二步就是根据va,pa依次设置页表条目,这里我们写个函数来完成对应关系:

#define IO  1
#define MEM 0void create_secdesc(unsigned int *ttb, unsigned int va, unsigned int pa, int io)
{int index;index = va / 0x100000;if (io)ttb[index] = (pa & 0xfff00000) | MMU_SECDESC_FOR_IO;elsettb[index] = (pa & 0xfff00000) | MMU_SECDESC_FOR_MEM;
}
#define IO  1
#define MEM 0void create_secdesc(unsigned int *ttb, unsigned int va, unsigned int pa, int io)
{int index;index = va / 0x100000;if (io)ttb[index] = (pa & 0xfff00000) | MMU_SECDESC_FOR_IO;elsettb[index] = (pa & 0xfff00000) | MMU_SECDESC_FOR_MEM;
}

然后依次映射每个页表条目:

 1 create_secdesc(ttb,0,0,IO);
 2
 3 create_secdesc(ttb,0x40000000,0x40000000,MEM);
 4
 5 va = 0x30000000;
 6
 7 pa = 0x30000000;
 8
 9 for(;va < 0x34000000;)
10 {
11     create_secdesc(ttb,va,pa,MEM);
12     va += 0x100000;
13     pa += 0x100000;
14 }
15
16 va = 0x48000000;
17
18 pa = 0x48000000;
19
20 for(;va < 0x5B000000;)
21 {
22     create_secdesc(ttb,va,pa,IO);
23     va += 0x100000;
24     pa += 0x100000;
25 }
26
27 create_secdesc(ttb,0x33c00000,0x33c00000,IO);
28
29 create_secdesc(ttb,0xB0000000,0x30000000,MEM);

至此,我们完成了MMU的设置,还需要使能MMU。 在Start.S里面添加mmu_enable,需要做的步骤有: 1.把页表基址告诉cp15 2.设置域为0xffffffff, 不进行权限检查 3.使能icache,dcache,mmu 4.返回到之前位置

mmu_enable: /* 把页表基址告诉cp15 */ldr r0, =0x32000000mcr p15, 0, r0, c2, c0, 0/* 设置域为0xffffffff, 不进行权限检查 */ldr r0, =0xffffffffmcr p15, 0, r0, c3, c0, 0/* 使能icache,dcache,mmu */mrc p15, 0, r0, c1, c0, 0orr r0, r0, #(1<<12)  /* enable icache */orr r0, r0, #(1<<2)  /* enable dcache */orr r0, r0, #(1<<0)  /* enable mmu */mcr p15, 0, r0, c1, c0, 0 mov pc, lr

a. cpu以地址A查找cache,一开始cache无数据,导致cache miss

转载于:https://www.cnblogs.com/yekongdebeijixing/p/10491326.html

第一期_内存管理单元MMU相关推荐

  1. ARM920T内存管理单元MMU

    作为程序员已经有4-5个年头了,发现学的知识杂且乱,很多学习过的东西,有时也会忘记.索性开始整理,由于是电子专业出身,于是想把之前玩过的2440开发板,重新再玩一遍.顺便对各个知识点进行较全面的总结. ...

  2. 操作系统 内存管理单元MMU TLB

    前言 在了解操作系统 内存管理 分页/分段/段页式管理.操作系统 虚拟内存技术两篇文章后,接下来继续看看现代操作系统基本内存管理方式,本文详细介绍Linux操作系统下的内存管理单元MMU和TLB. d ...

  3. 内存管理单元——MMU

    一.基本概念介绍 MMU是Memory Management Unit的缩写,中文名是内存管理单元,有时称作分页内存管理单元(英语:paged memory management unit,缩写为PM ...

  4. 内存管理单元--MMU

    现代操作系统普遍采用虚拟内存管理(Virtual Memory Management)机制,这需要处理器中的MMU(Memory Management Unit,内存管理单元)提供支持,本节简要介绍M ...

  5. 内存管理单元MMU学习

    MMU MMU是Memory Management Unit的缩写,中文名是内存管理单元,有时称作分页内存管理单元(英语:paged memory management unit,缩写为PMMU). ...

  6. Linux 中的内存管理单元MMU

    MMU (内存管理单元) 基础概念 1.TLB – 转换旁路缓存,里面存放着少量的虚拟内存与实际物理内存之间的对应关系,被称为快表. 2.TTW – 漫游转换表,当TLB中没有对应的转换关系,通过对内 ...

  7. 内存管理单元MMU - ARM内核扩展(一)

    了解物理地址和虚拟地址的关系 掌握如何通过设置MMU来控制虚拟地址到物理地址的转化 了解MMU的内存访问权限机制 了解TLB.Cache.Writebuffer的原理,使用时的注意事项 在计算机开始阶 ...

  8. 内存管理单元MMU简介

    1. MMU MMU在CPU的配合下(通过页异常触发),实现了线性地址到物理地址的动态映射,为正在CPU上运行的应用程序(进程)提供了一个独立的连续内存空间(线性地址空间,或称虚拟内存空间,其中放置了 ...

  9. 【软件开发底层知识修炼】三 深入浅出处理器之三 内存管理与内存管理单元(MMU)

    学习交流加 个人qq: 1126137994 个人微信: liu1126137994 学习交流资源分享qq群: 962535112 上一篇文章学习了中断的概念与意义,以及中断的应用-断点调试原理.点击 ...

  10. Linux内存管理之内存管理单元(MMU)(二)

    Linux内存管理之内存管理单元(二) 1.1.什么是MMU 在CPU内部,有一个专门的硬件单元来负责这个虚拟页面到物理页面的转换,它被被称为内存管理单元(Memory Management Unit ...

最新文章

  1. 判断二进制数1的个数
  2. hdu 3788 字符串
  3. python 调试命令
  4. c++hello world代码_在 Rust 代码中编写 Python 是种怎样的体验?
  5. 分布式的Key-Value存储系统Cassandra
  6. vbox里面的Ubuntu虚拟机与主机win7之间设置共享文件夹
  7. 995. K 连续位的最小翻转次数
  8. python中集合的元素可以是_python中的集合
  9. VMware:未能将管道连接到虚拟机, 所有的管道范例都在使用中
  10. PHP高级教程-Session
  11. oracle 获取多个序列值,一次性获取多个oracle序列值问题
  12. 【点云预处理】10种点云数据数据预处理增强方法 — 持续总结和更新(一)
  13. XRD测试的68个问题(三)
  14. 苹果6plus几核处理器_iOS 13.4 Beta3发布:苹果在布局,越狱软件也更新!
  15. 基于tiny4412的u-boot移植(二)_ git clone
  16. java多线程------锁
  17. js中利用prompt和parseFloat来实现用户体温华氏和摄氏的提取(18)
  18. cas单点登录学习笔记 .
  19. Linux:安装AnyConnect客户端教程
  20. 计算机近几年自考本科题,近几年自考管理系统中计算机硬应用题汇总.doc

热门文章

  1. iOS 网络/本地 图片 按自定义比例缩放 不失真 方法
  2. 前后端分离djangorestframework—— 接入支付宝支付平台
  3. python 进行后端分页详细代码
  4. 基于Hexo+Node.js+github+coding搭建个人博客——基础篇
  5. WCF NetTcpBinding Transport安全模式(7) ClientCredentialType证书验证模式---- ChainTrust验证模式...
  6. CentOS 7 配置Java环境变量
  7. 推荐!手把手教你使用Git(转)
  8. Android中gravity与layout_gravity的区别--Padding 与 margin 区别
  9. 五个免费国外流量统计工具
  10. python处理pdf文件_python处理操作pdf全攻略