首先声明本文不准备详细地介绍Cache和MMU的概念和用法,主要是为了厘清两者之间的相互关系和依赖。
1. MMU管理cache访问属性
在没有MMU的时候,cache本身的模型比较简单,如下所示,在使用的时候重点关注Cache数据的一致性问题。
但是这样用也有个缺点就是:cache只能整体操作,比如为了使用DMA,要关掉所有的DCache,这就是没有内存管理单元的坏处,MMU所提供的第一个作用就是对内存进行分区的权限管理,比如说外设所用的内存、DMA所用的内存区域可以设置为不允许Cache,其他区域设置为开启。这一步就开始导致了MMU与Cache的绑定,因为MMU管理了内存区域是否可以被缓存,如果不开MMU,只打开DCache或者Unified Cache(dcache和icache不分开),将会在外设访问上出现bug。
其实icache打开也有一定的数据不一致风险,让我们看下一位博主的文章:
例如你的引导程序在地址0x8000有一些代码,它至少要运行一次,然后你选择把它作为引导程序,引导程序可能在比如地址0x10000000允许你在0x8000加载一个新的程序,这个加载使用数据访问,所以它不通过指令缓存。所以有可能指令缓存中有一些或所有你上次在0x8000区域的代码,当你在0x8000处分支到启动加载的代码时,你会得到缓存中的旧程序或旧程序和新程序的讨厌的混合部分,这些部分被缓存和不被缓存。因此,如果你的引导程序允许i-cache开启,你需要在分支到引导的代码之前使cache失效。说到底就是代码已经变了但是没有通过cache,比如我们反复运行0x8000这个地址,但是第一次运行0x8000的时候代码是从nand加载的,第二次是从emmc加载的,只要没有断电,cahce是感知不到你这样玩的。
上面这段话稍微有点绕,我画了一个图来解释下:
简单来说就是在0x8000这个地址处有了新的代码,但是icache中的代码还是旧的,是跟dcache一样的数据一致性错误。
总而言之,上面就是说明一个问题,现在的CPU,由于MMU管理着cache的访问属性,要开cache就必须开MMU!
裸跑不开MMU行不行
行,但效率很低。
现在的CPU,要想使用Cache,必须使能MMU,MMU页表里有cache访问属性配置。
在ARM里,如果不开MMU,不仅不能开启cache,连内存属性都不是normal,而是device,device属性不允许硬件对AXI总线的信号进行合并、乱序等,效率较低。
参考:MMU开关与CACHE开关的联系——https://blog.csdn.net/u011011827/article/details/125331796
为什么Uboot阶段要关MMU,关DCache、开ICache——https://blog.csdn.net/u013372900/article/details/122454478
A53的mmu配置说明——https://www.cnblogs.com/liuwanpeng/p/14372976.html
2. MMU与Cache配合CPU读写数据
MMU的第二个作用就是将处理器发出的虚拟地址(VA)转换为物理内存中的物理地址(PA),当然这是在跑linux之类的操作系统才有的,跑逻辑代码,VA=PA。
这个时候两者实际上是配合关系,因为两者的功能并不相同,MMU接收虚拟地址并将其转换为物理地址后,这个地址被送到cache或者内存中获取具体的数据。cache中存放的是某些虚拟地址或物理地址对应的实际内容。cache的索引地址可以是虚拟地址可能有点冷门知识,如下图所示,cache的位置可以有两个。
一般来说速度比较快的Level1 cache被用作虚拟寻址cache,如下图。
参考:Computer Organization and Architecture——http://aturing.umcs.maine.edu/~meadow/courses/cos335/COA04.pdf
Cache——https://xerxes.cs.manchester.ac.uk/comp251/kb/Cache
Memory Management Units (MMUs)——https://xerxes.cs.manchester.ac.uk/comp251/kb/MMU
ARMv8 MMU及Linux页表映射——https://blog.csdn.net/weixin_38428439/article/details/122438845