写下这篇文章的目的是写给现在清楚将来或许忘记的自己。
前言
以arm926ej-s内核为例来说,为什么说有MMU和I-Cache以及D-Cache的芯片具有更好的运行处理效率,相比于其他没有MMU和I-Cache和D-Cache的芯片来说,有MMU,访问的地址更加安全,有I-Cache取指令的速率更高效,因为Cache的访问速度远比内存的访问速度快,如果内核想访问的指令已经事先在Cache中暂存了,那么内核就不需要大费周章的去访问内存,因为我们都知道,现今,处理器的工作频率都比内存的工作频率高许多,也就是处理器在访问内存时候会经常出现空等待的现象,而Cache比内存的访问速率快,这才表征了Cache的高效性。
1.为何要关闭MMU?
研究过U-Boot的人大概都知道,我们的MPU在复位后,通常都会经历这几个阶段:设置模式为svc32->屏蔽所有的中断->关闭看门狗->关闭MMU以及Cache->… , 这几个步骤或许不是所有芯片必须经历的上电后初始化过程,但绝对是大多数的ARM微处理器上电后必须经历的。
关于为何要关闭MMU和Cache?我认为还是为了效率,为了效率?有人或许不解,不是说MMU和Cache会提供访问效率吗?不错,MMU和Cache确实会增加arm926ej-s内核访问的效率,但,在我们系统复位阶段,我们访问的都是一些不连贯的而且地址固定的寄存器,何况,我们的相关寄存器初始化都是用汇编指令程序设计的(用汇编设计还是为了高效,一切为了高效率!!!),换句话说,我们在初始化阶段对一些重要的寄存器设置值的时候,不需要你MMU和Cache,为啥呢?因为arm926ej-s的内核每条指令的访问都经过3个阶段(如果你开着MMU和Cache的话),如果你没开启MMU的话,那就直接执行第三步骤,访问实际地址PA。
- 首先arm926ej-s会给出访问指令的虚拟地址VA
- 然后FCSE PID会将地址修改成MVA(即修改后的虚拟地址),然后MMU和I-Cache会去查找这个地址是否已经在Cache中缓存了,如果找到了,那么直接返回给arm926ej-s,高效率了
- 如果找了一遍,没有找到,那么MMU将MVA地址修改成PA(实际的物理地址),并将实际的物理地址PA送给AMBA总线接口去执行一个外部的访问,那这样就低效率了
上面3个步骤中,有人或许为好奇,MMU在这里干了啥都?其实啊,我们的MMU存在的最重要的价值就是都arm926ej-s的内核访问地址进行检查,如果你开了MMU,那么arm926ej-s所有的访问地址都会经过MMU的检查保护,检查地址访问合法,才会让arm926ej-s的访问继续往下走。
2.怎样去关闭MMU?
arm926ej-s对内核的访问提供了一组寄存器结构,不过不同于总线访问,它的访问比较特殊,又称之为CP15单元(就是所谓的协处理器单元),并且官方给出了访问的接口格式指令,如下:
mcr {cond} p15, opcode_1, Rn, cRn, cRm, opcode_2
mrc {cond} p15, opcode_1, Rn, cRn, cRm, opcode_2
通常情况下,除了一些特定的访问,有特定的值之外,其他情况下,cRm,opcode_1, opcode_2,这三个的值都为0,即,
mcr p15, 0, Rn, cRn, 0, 0
mrc p15, 0, Rn, cRn, 0, 0
在协处理单元有一个控制寄存器c1,对这个寄存器进行读写操作时候,就可以实现对MMU以及cache禁止或者使能了。但,这个寄存器比较特殊,因为涉及到了具体操作时,要遵循 读-改写-回写的原则。
非常重要的一句话,D-Cache在关闭使能前,必须要先清理在关闭,这样做是为了保证Cache与内存的一致性。
下面的代码将演示如何关闭MMU:
#This is a demo to show how to disable MMU with ARM926ej-s kernel.
mov r0, #0
mrc p15, 0, r0, c1, c0, 0 @先读取协处理中的控制寄存器c1的值
bic r0, r0, #0x00000002 @清除到协处理中的控制寄存器c1的bit1,置为0,表示关闭mmu, 置一,表示打开mmu
mcr p15, 0, r0, c1, c0, 0 @回写到c1寄存器中去
说明,汇编程序单行最后使用@后面的是注释,根据GNU arm汇编的注释规则,在单行后面注释的用@,在正行进行注释的就用#,这个要知道一下就好。
————————未完待续