MMU简介
嵌入式系统中,存储系统差别很大,可包含多种类型的存储器件,如FLASH,SRAM,SDRAM,ROM等,这些不同类型的存储器件速度和宽度等各不相同;在访问存储单元时,可能采取平板式的地址映射机制对其操作,或需要使用虚拟地址对其进行读写;系统中,需引入存储保护机制,增强系统的安全性。为适应如此复杂的存储体系要求,ARM处理器中引入了存储管理单元来管理存储系统。
§1.3.1 内存管理单元(MMU)介绍
在ARM存储系统中,使用MMU实现虚拟地址到实际物理地址的映射。为何要实现这种映射?首先就要从一个嵌入式系统的基本构成和运行方式着手。系统上电时,处理器的程序指针从0x0(或者是由0Xffff_0000处高端启动)处启动,顺序执行程序,在程序指针(PC)启动地址,属于非易失性存储器空间范围,如ROM、FLASH等。然而与上百兆的嵌入式处理器相比,FLASH、ROM等存储器响应速度慢,已成为提高系统性能的一个瓶颈。而SDRAM具有很高的响应速度,为何不使用SDRAM来执行程序呢?为了提高系统整体速度,可以这样设想,利用FLASH、ROM对系统进行配置,把真正的应用程序下载到SDRAM中运行,这样就可以提高系统的性能。然而这种想法又遇到了另外一个问题,当ARM处理器响应异常事件时,程序指针将要跳转到一个确定的位置,假设发生了IRQ中断,PC将指向0x18(如果为高端启动,则相应指向0vxffff_0018处),而此时0x18处仍为非易失性存储器所占据的位置,则程序的执行还是有一部分要在FLASH或者ROM中来执行的。那么我们可不可以使程序完全都SDRAM中运行那?答案是肯定的,这就引入了MMU,利用MMU,可把SDRAM的地址完全映射到0x0起始的一片连续地址空间,而把原来占据这片空间的FLASH或者ROM映射到其它不相冲突的存储空间位置。例如,FLASH的地址从0x0000_0000-0x00ff_ffff,而SDRAM的地址范围是0x3000_0000-0x31ff_ffff,则可把SDRAM地址映射为0x0000_0000-0x1fff_ffff而FLASH的地址可以映射到0x9000_0000-0x90ff_ffff(此处地址空间为空闲,未被占用)。映射完成后,如果处理器发生异常,假设依然为IRQ中断,PC指针指向0x18处的地址,而这个时候PC实际上是从位于物理地址的0x3000_0018处读取指令。通过MMU的映射,则可实现程序完全运行在SDRAM之中。
在实际的应用中,可能会把两片不连续的物理地址空间分配给SDRAM。而在操作系统中,习惯于把SDRAM的空间连续起来,方便内存管理,且应用程序申请大块的内存时,操作系统内核也可方便地分配。通过MMU可实现不连续的物理地址空间映射为连续的虚拟地址空间。
操作系统内核或者一些比较关键的代码,一般是不希望被用户应用程序所访问的。通过MMU可以控制地址空间的访问权限,从而保护这些代码不被破坏。
MMU的实现过程,实际上就是一个查表映射的过程。建立页表(translate table)是实现MMU功能不可缺少的一步。页表是位于系统的内存中,页表的每一项对应于一个虚拟地址到物理地址的映射。每一项的长度即是一个字的长度(在ARM中,一个字的长度被定义为4字节)。页表项除完成虚拟地址到物理地址的映射功能之外,还定义了访问权限和缓冲特性等。
MMU的映射分为两种,一级页表的变换和二级页表变换。两者的不同之处就是所实现的变换地址空间大小不同。一级页表变换支持1M大小的存储空间的映射,而二级可以支持64KB、4KB和1KB大小地址空间的映射。
要实现从虚拟地址到物理地址的映射,必然会遇到一个问题,如何找到这个页表。对于表的查找,要知道这个表的基地址和偏移地址,在具有MMU功能的处理器中,集成了一个被称为CP15的协处理器,该协处理器的C2寄存器中用于保存页表的基地址,下面以一级页表变换为例说明MMU实现地址变换的过程。
如图1.1所示,当处理器访问一个虚拟地址时,该虚拟地址的[31:20]作为偏移地址与页基地址结合(基地址必须是64KB对齐的,因此基地址的[13:0]位都为0),得到一个32位的页表项地址(因为页表项为4字节对齐,[1:0]两位为0)。通过这个页表项地址可以检索到该页表项。页表项的格式如下:
表 1.3
表 1.4
查找到页表项后,根据页表项的访问特性(缓冲以及是否允许访问等)协处理器决定是否允许访问。如不允许访问,则协处理器向CPU报告出错信息;反之,由页表项的[31:20]位与虚拟地址的[19:0]一起组成实际的物理地址,实现从虚拟地址到物理地址的映射。
对于实际编程工作而言,主要是确定如何编写页表中的内容并如何确定页表项地址。现举例如下:
假设物理地址为0x36B0_0000~0x36Bf_ffff(1M空间)的一块连续空间需映射为0x0100_0000~0x010f_ffff的一块连续空间:
1.确定页表项中的内容:把物理地址的基地址作为页表项的高12位(31bit~21bit),填写访问属性。假设可以读写,可以读缓存、写缓冲,这样该页表项内容为0x36B0_0C00E;
2.确定页表基地址,填写页表基地址到CP15寄存器的C2中。页表的基地址要为64KB对齐;
3.计算出偏移地址,把内容填写到页表项地址中。页表项地址=页表基地址+(物理地址基地址>>18),如页表基地址为0xA100_0000,那么,页表项地址=0xA100_0DAC;
4.将页表项数值写到对应的页表项地址中。上例中,需要向地址0xA100_0DAC中写入0x36B0_0C00E。
ARM920T的MMU与Cache
Cache是高性能CPU解决总线访问速度瓶颈的方法,然而它的使用却是需要权衡的,因为缓存本身的动作,如块拷贝和替换等,也是很消耗CPU时间的。MMU的重要性勿庸置疑,ARM920T(和ARM720T)集成了MMU是其最大的卖点;有了MMU,高级的操作系统(虚拟地址空间,平面地址,进程保护等)才得以实现。二者都挺复杂,并且在920T中又高度耦合,相互配合操作,所以需要结合起来研究。同时,二者的操作对象都是内存,内存的使用是使用MMU/Cache的关键。另外,MMU和Cache的控制寄存器不占用地址空间,CP15是操纵MMU/Cache的唯一途径。
Cache/Write Buffer的功能
Cache通过预测CPU即将要访问的内存地址(一般都是顺序的),预先读取大块内存供CPU访问,来减少后续的内存总线上的读写操作,以提高速度。然而,如果程序中长跳转的次数很多,Cache的命中率就会显著降低,随之而来,大量的替换操作发生,于是,过多的内存操作反而降低了程序的性能。
ARM920T内部采用哈佛结构,将内部指令总线和数据总线分开,分别连接到ICache和DCache,再通过AMBA总线接口连接到ASB总线上去访问内存。Cache由Line组成,Line是Cache进行块读取和替换的单位。
Writer Buffer是和DCache相逆过程的一块硬件,目的也是通过减少memory bus的访问来提高性能。
MMU的功能
在内存中维护一张或几张表,就看你怎么给内存划分page和section了。通过CP15指定好转换表的位置,920T的硬件会自动将转换表的一部分读到TLB中。CPU每次进行内存读写时,发出虚拟地址,参照TLB中的转换表转换到物理地址,并读取相应entry中的信息,以决定是否可以有权限读写和缓存。
mmugen这个工具就是帮你构造这个表的,省的自己写程序了。
操作MMU,实际上就是如何分配和使用你的内存,并记录在translationtable里。
ARM920T中,MMU的每条entry包括Cachable和Buffable位来指定相应的内存是否可以用Cache缓存。此处就是MMU与Cache的交互作用处。
实际上,MMU和Cache的使用是操作系统设计者根据系统软硬件配置而考虑的事情。操作系统针对分配给应用程序的地址空间作内存保护和缓存优化。在没有操作系统的情况下,就需要我们自己来掌控它们了。其中,主要是合理分配内存。
我认为,以下几点需要着重考虑:
1) 安全第一! -- 避免MMU和Cache的副作用。
当你在无OS的裸机上开发程序时,初始化运行环境的代码很重要,比如:各种模式堆栈指针的初始化;将代码和RW data从ROM拷贝到RAM;初始化.bss段(zero initialized)空间等。此时会有大量的内存操作,如果你enable了Cache,那么在拷贝完代码之后,一定要invalidate ICache和flush DCache。否则将会出现缓存中的代码或数据与内存中的不一致,程序跑飞。
另外,有时候我们需要自己作loader来直接运行ELF文件,情况也是一样,拷贝完代码后一定要刷新Cache,以免不测。
还有,对硬件的操作要小心。很多寄存器值都是被硬件改变的,读写时,要保证确实访问到它的地址。首先,在C语言代码中声明为volatile变量,以防止内存读写被编译器优化掉;另外,设置好TLB,使得寄存器映射的地址空间不被缓存。
总之,缓存和内存中代码的不一致,是一定要避免的。
2) 弄巧成拙! -- 只对频繁访问的地址空间进行Cache优化。
我们很清楚自己的程序中,那里有大量的运算,哪里有无数的循环或递归,而这正是Cache的用武之地,我们将这些空间进行缓存将大大提高运行速度。但是,很多函数或子程序往往仅仅运行很少几次,若是对它们也缓存,只会捡了芝麻丢了西瓜,造成不必要的缓存和替换操作,反而增加了系统负担,降低了整体性能。
3) 断点哪儿去了? -- 如何调试“加速”了的代码?
据我所知,一般,debugger都是通过扫描地址总线,在断点处暂停CPU。ARM9TDMI中集成的JTAG调试口,也是这样。
当我们调试使用Cache的代码时,将会出现问题。比如:CPU访问某断点所在地址之前的地址时,发生缓存操作,断点处代码被提前读入Cache,此时地址总线上出现了断点地址,CPU被debugger暂停,并且断点之后的指令也被Cache缓存。于是,当你从断点处step时,程序却停不了了,因为地址总线上不再出现断点之后的下一个地址了。
再举个例子:
int i,a;
for (i=0; i<100; i++) {
-> a++; /* set breakpoints */
}
当地址总线上第一次出现断点地址时,CPU暂停;之后,就再也不会停了。因为,之后CPU会从cache中直接去代码了。(当然,后来,Cache的代码有可能会被替换掉,断点又可到达。) 所幸的是,我用的debugger提供JTAG Monitor,允许断点跟踪使用cache的程序。
全部参考来源于ARM的手册,建议详细阅读1)的PartB及其在2)中的具体实现。
1) DDI0100E_ARM_ARM.pdf (ARM Architecture Reference Manual)
2) DDI0151C_920T_TRM.pdf (ARM920T Technical Reference Manual)
专栏文章内容及配图由作者撰写发布,仅供工程师学习之用,如有侵权或者其他违规问题,请联系本站处理。 联系我们
相关推荐
尼得科传动技术携新品参展德国国际自动化展览会“Automatica 2025”
两种适合自制的文氏桥低频信号发生器
中国品牌领跑一季度全球扫地机器人市场
基础视频: 琐相环的基本原理
2025年1季度中国智能眼镜市场同比增长116.1%
请问各位大虾
ASIL-B认证落地!莱迪思通过汽车功能安全大考
Supermicro推出采用AMD Instinct™ MI350系列GPU与平台的性能、效率优化液冷与气冷AI解决方案
P89C51 52 54 58 flash单片机使用指南 (英二)
文氏桥式正弦波发生器三
P89C51RA2 RB2 RC2 RD2xx 单片机使用指南 (英)
用硬件产生随机数
尼得科机床发售搭载齿轮加工功能的复合加工中心“MGC300”
RBW,VBW ,SPAN and SWEEP 的关系和物理意义
2Ghz的电路布线要注意些什么
P89C51 52 54 58 flash单片机使用指南 (英一)
P89C51RB2 RC2 RD2 flash单片机 (英)
Qorvo紧凑型方案可优化射频尺寸与散热性能,简化5G基础设施部署
兆易创新推出500W单级光伏微逆方案
“asean”的NRF24L01半双工通信视频
F007构成的低成本文氏振荡器
巴斯夫连续第十七年发布大中华区年度报告
ADI IIC-China 2012现场花絮
F007构成的文氏电桥正弦波发生器
周易易学视频教学光盘曲炜中级四柱面授班曲炜高级四柱面授班曲炜六爻中级面授班
猜猜Big Daddy的火箭上升过程中的加速度,赢取iPad!
HTA8128内置升压大功率50W立体声户外蓝牙音箱D类功放IC方案
基于AD9822和CIS传感器的验钞机AFE方案
文氏桥式正弦波发生器二
P89C51RA2 RB2 RC2 RD2xx 单片机使用指南