Cortex-M4 存储模型(Memory Model)与MPU(Memory Protection Unit)

8 篇文章 1 订阅
4 篇文章 0 订阅

Cortex-M4 存储模型要点:

Cortex-M4提供了4GB的可寻址空间,在SRAM和外设区域还存在2MB的“位域”,支持映射位寻址。

                         

Cortex-M4的寻址空间类型被地址映射规则(上图)和MPU程序划分成了不用的区域,这些区域有以下3大类型内存:

                                    一般(Normal)类、设备(Device)类、强有序(Strongly-Ordered)类

此外,还有特殊属性:

                                                                   禁止执行(Execute Never,XN)

若CPU尝试从具有XN属性的空间中取指令(Instruction)来执行的话,将会引发“内存管理故障异常”。

在Cortex-M4的可寻址空间上按照内存类型的分区情况如下:

                    

具有3级流水线机制的Cortex-M4内核在访问指令时,会采取指令预取(Prefetch)策略,而在遇到分支指令时还会对分支目标指令采取投机预取(Speculative Prefetch)策略。从而提高CPU的运行效率。

正是由于Cortex-M4的效能优化和缓冲机制,使其对内存的访问不一定会按照相关指令的书写顺序来完成。

因此,对于需要严格访问次序来保证正常功能的程序,必须在相邻的内存访问指令之间“人为”地插入内存屏障(Memory Barrier)指令来保证访问顺序。

然而对于设备(Device)类和强制有序(Strongly-Ordered)类内存的访问次序,Cortex-M4则提供了硬件上的保证,假设有占内存访问指令:

          A1;

          A2;

那么此时CPU对内存的实际访问次序与所访问内存的类型有关:

                           

可见对于设备(Device)类强有序(Strongly-Ordered)类的内存,程序员可放心地设计程序。

而对于Normal类型的内存又怎么办呢?

Cortex-M4为此提供了3大内存屏障(Memory Barrier)指令,用于插入到相邻的内存访问指令之间,分别为:

          DMB(Data Memory Barrier)——用于隔离两次内存访问

          DSB(Data Synchronization Barrier)——用于隔离指令访问与内存访问

          ISB(Instruction Synchronization Barrier)——用于隔离两次指令访问

位域映射方面,其映射关系如下:

                    

当程序直接给某个“位”的“别名”赋值时,该别名的bit[0]将映射到该位上,非常方便。

Cortex-M4内核默认采用小端(Little-Endian)模式,既字的最低位(LSB)位于低地址字节:

                                        

但通过配置也能逆转为大端(Big-Endian)模式

                                        

最后,Cortex-M4还为用户提供了一系列“同步原语”指令,使得操作系统中的进程和线程可以使用这些“同步原语”来实现对内存的无打断访问(无锁编程?),从而实现有保障的“read-modify-write”内存刷新机制和信号量机制。

这些同步原语通常要成对使用,分为“加载专用原语(Load-Exclusive)”和“存储专用原语(Store-Exclusive)”,前者用于无打断地访问内存地址,后者用于无打断地写入内存地址(有返回值,0表示成功写入,1表示地址被锁写入失败)。

具体的同步原语有:

          LDREX,STREX——用于字的无打断读写操作

          LDREXH,STREXH——用于半字的无打断操作

          LDREXB,STREXB——用于字节的无打断操作

然而,上述原语无法直接通过一般的C编译器生成,用户可以使用由CMSIS提供的内建函数来调用:

                         


Cortex-M4 MPU(Memory Protection Unit)要点:

MPU属于Cortex-M4内核的一个外设,它根据Cortex-M4可寻址空间模型(Memory Model)对内存空间定义(分区、地址、大小、属性等)来限制CPU的访问行为,起到保护内存数据的作用。

MPU支持以下主要功能:

  • 独立设置内存空间分区的属性;
  • 内存分区重合;
  • 输出内存分区属性到系统。

Cortex-M4的MPU定义了:

  • 8块可被独立定义、配置的存储区,编号0-7;
  • 1块背景存储区。

用户可以为不同的存储区配置不同的属性(如:内存类型、访问许可),这些属性将决定了CPU访问这些内存的行为(若发生了分区重合,那么重合区域的属性以编号数字更大的存储区的属性为准)。

若用户使能了MPU但未为其配置内存分区规则时,整个系统内存空间都会被默认为是一整块背景存储区,此时该空间仅能被“特权级”程序访问。

观察Cortex-M4结构图中MPU所在的位置(下图红圈)。

                         

MPU位于CPU内核的内存接口(Memory Interface)与各总线内部连接矩阵之间,监视着CPU的一切内存访问行为。若某个程序命令CPU去访问被MPU禁止访问的空间,MPU将会产生一个“内存管理故障(Memory Management Fault)”异常,这有可能导致在OS上运行的相关线程被直接终结。

可见,MPU实际上就是Cortex-M4可寻址空间模型/映射规则(Memory Model/Map)的实际监督者。

实际上,OS的内核可以利用MPU提供的动态分区更新功能,在应用程序运行的同时动态地划分内存空间,从而获得对应用内存数据的硬件保护机制。

通过配置MPU的一系列控制寄存器,就能使其约束CPU的内存访问行为。

MPU的控制寄存器有:

                         

对某个存储分区(REGION)进行定义或设置其属性和访问规则的过程如下:

  • 写MPU_CTRL(控制)寄存器使能MPU;

                         

  • 在MPU_RNR(分区编号索引)寄存器中将要定义、配置的REGION[i](i = 0~7)位置1;

                         

  • 在MPU_RBAR(分区基地址配置)寄存器中为REGION[i]分区定义其基地址;

                         

  • 在MPU_RASR(分区属性配置)寄存器中为REGION[i]分区配置各种属性;

                         

 

通过MPU_RBAR(分区基地址配置)寄存器定义分区基地址需要遵循以下原则:

  • 以所定义的分区大小来自然对齐(既,基地址为分区大小的整数倍);
  • ADDR[31:N]位段中的N的取值案例如下:

                         

通过MPU_RASR(分区属性配置)寄存器,用户可以为不同映射规则下的内存分区所配置的“类型属性”有以下几类:

                         

“访问规则(Access Permission)”有以下几类:

                         

当CPU的行为所触犯了这些“访问许可”规则时,MPU将触发“内存管理故障”异常。

 

此外,只要某个REGION ≥ 256 Bytes(MPU支持的最小分区大小为32B),Cortex-M4 MPU还可在该REGION内划分8个大小相等的子分区(SubRegion)。

用户需要MPU_RASR(分区属性配置)寄存器的SRD[7:0]位段来Disable某个REGION内被其他REGION重叠的子分区。

                         

但若在没有分区重叠发生的情况下Disable了某个子分区,这将会引发MPU故障。

 

对于STM32 MCU,官方推荐的MPU内存分区属性配置方案如下:

                         

需要注意的是,MPU无法监控DMA控制器的内存访问行为,为了防止DMA的意外访问对内存数据造成破坏,MPU要控制CPU或软件对DMA寄存器的访问


关于CPU对齐访问的基础知识补充:

现代计算机是以Byte 为主要单位存储数据的,CPU在访问数据寻址时用到的地址是指向各个数据的首个Byte的地址。

如:一个Char类型数据占用8个Bits,存放在一个Byte内,该Byte的地址即为该数据的存储地址;而一个int类型数据占用32个Bits,就需要存放在4个Bytes内,并以第1个Byte的地址作为该数据的存储地址。

以下转载自:https://yangwang.hk/?p=773

最近在研究CPU的微内核,遇到了一个问题:为何CPU要求内存访问对齐?

换句话说:CPU访问非对齐的内存时为何需要多次读取再拼接?

首先简单说一下何为内存对齐。

例如,当cpu需要取4个连续的字节时,若内存起始位置的地址可以被4整除,那么我们称其对齐访问。反之,则为未对齐访问。比如从地址0xf1取4字节就是非对齐(地址)访问。

简单的看来,对于一个数据总线宽度为32位的cpu,它一次拥有取出四字节数据的能力,理论上cpu应该是可以从任意的内存地址取四个连续字节的,而且是否对齐硬件的设计是相同的(如果内存和CPU都是字节组织的话,那么内存应当可以返回任意地址开始连续的四字节,CPU处理起来也没有任何差异)。

然而,很多cpu并不支持非对齐的内存访问,甚至在访问的时候会发生例外(例如arm架构的某些CPU)!而某些复杂指令集的cpu(比如x86架构),可以完成非对齐的内存访问,然而CPU也不是一次性读出四个字节,而是采取多次读取对齐的内存,然后进行数据拼接,从而实现非对齐数据访问的。如下图:

                                                       

 

如果我们的数据存于内存的2-5中,在读取时实际上是先读取0-3,再读取4-7字节,再分别将2-3字节和4-5字节合并,最后得到所需的四字节数据。

那么为什么CPU不直接读取2-5,而是要么不提供支持,要么甚至不惜花大力气执行多次访问再拼接访问非对齐的内存呢(如此访问一则增加访问时间,二则增加电路的复杂性)?这背后一定有它的原因!

经过一番互联网搜索,但是在国内只能找到为什么写程序的时候要对齐的解释(因为CPU要么不支持,要么访问效率下降),然后是如何实现对齐。没有一篇文章从硬件原理上去分析为何访问非对齐内存如此麻烦。

最后我在神奇的StackOverflow网站上找到了相关的问题,以及合理的解答(看来并不是只有我一个人有类似的疑问)。


实际上,访问非对齐内存并没有我们想象的那么“简单”,例如,在一个常见的pc上,内存实际上是有多个内存芯片共同组成的(也就是内存条上那些黑色的内存颗粒)

为了提高访存的带宽,通常的做法是将地址分开,放到不同的芯片上。比如,第0-7bit在芯片0上储存,8-15bit在芯片2上组成,以此类推,如下图:

                                       

这意味内存实际上并不是完全以byte形式组织的,而是以偏移量(offset)来给出具体地址的。

这样当我们采用对齐的地址访问时,比如从0x00开始访问四字节,显然四个字节储存于4个芯片,而且他们都有同样的偏移量(offset),这时我们就能一次获得所需的数据。

但是当从0x01开始读取4字节呢?此时前三个字节也是按顺序分别储存在1-3芯片中的,而且偏移量都是0,但是第四个字节却储存在偏移量为1的芯片0中。

在访问内存时,CPU需要给出偏移量offset,而发送偏移量的总线宽度大约是40位(64bit环境下),通常这样的总线只有一个。这意味着在一次内存访问周期内我们只能读取一个结果。

当然,要想一次读取两个offset的内容也不是不能实现,你可以增加用于发送地址的bus数量。对于一个64位的cpu,如果你希望在一个访问周期内读取未对齐的内存,你需要增加到8根总线。这意味着需要增加接近300个io。而通常cpu的管脚数量在700-2000之间,在这基础之上增加300将会是一个很大的改动。换句话说,就是会大大增加硬件的复杂程度。

同时,内存访问信号的频率是非常高的,增加的总线也会造成额外的噪声干扰。

当然,还有一种方法。由于非对齐访问最多也就访问两个不同的offset,而且这两个offset总是连续,我们可以再给内存内部加一根额外的线,这样就可以同时返回offset和offset+1两个偏移量上的数据了。

但是,这样意味着芯片内多了一些额外的加法器(用于给offset加一,得到下一个偏移量),所有的读操作都会在读取前增加一个计算操作。这一步会降低内存的时钟。于是乎,我们可能为了千分之一概率出现的非对齐访问,增加了99.9%的对齐访问的访问延时。显然这并不是一个明智的选择。

因此,CPU不支持,或者通过两次读取来实现非对齐访问也就有理有据了。

此外,访问非对齐的数据还存在一个问题:cache

通常来说,cache是和offset相关联的,不同的offset被不同的cache line缓存,因此,访问非对齐的数据也意味着多次的cache读取,同样会降低效率。

综上所述,这些也基本上是为何CPU要求内存访问对齐的原因了。//转载完

 

  • 2
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值