cortex-m3和cortex-m4处理器支持一种名为MPU的特性。MPU是一种可编程的部件,用于定义不同存储区域的存储器访问权限(如只支持特权访问或全访问)和存储器属性(如可缓冲、可缓存)。cortex-m3和cortex-m4处理器中的MPU支持多达8个可编程存储器的区域,每个都具有自己可编程的起始地址、大小及设置,另外还支持一种背景区域特性。MPU可以提高嵌入式系统的健壮性,可以使系统更加安全:
①避免应用任务破坏其他任务或OS内核使用的栈或数据存储器
②避免非特权任务访问对系统可靠性和安全性很重要的外设
③将SRAM或RAM空间定义为不可执行的,防止代码注入攻击。
还可以利用MPU定义其他存储器属性,如可被输出到系统级缓存单元或存储器控制器的可缓存性。若存储器访问和MPU定义的访问权限冲突,或者访问的存储器位置未在已编程的MPU区域中定义,则传输会被阻止且触发一次错误异常。触发的错误异常处理可以是MemManage错误或HardFault异常,实际情况取决于当前的优先级及MemManage错误是否使能。然后异常处理就可以确定系统是否应该复位或只是OS环境中的攻击任务。在使用MPU前需要对其进行设置和使能,若未使能MPU,处理器会认为MPU不存在。若MPU区域可以出现重叠,且同一个存储器位置落在两个MPU区域中,则存储器访问属性和权限会基于编号最大的那个区域。例如,若某传输的地址位于区域1和区域4定义的地址范围内,则会使用区域4的设置。
MPU设置方法有多种:
对于没有OS的系统,MPU可以被编程为静态配置。该配置可用于以下功能:
①将RAM/SRAM区域设置为只读,避免重要数据被意外破坏。
②将栈底部的一部分RAM/SRAM空间设置为不可访问,以检测栈溢出。
③将RAM/SRAM区域设置为XN,避免代码注入攻击。
④定义可被系统级缓存(2级)或存储器控制器使用的存储器属性配置。
对于具有嵌入式OS的系统,在每次上下文切换时都可以配置MPU,每个应用任务都有不同的MPU配置。这样可以:
①定义存储器访问权限,使得应用任务只能访问分配给自己的栈空间,因此可以避免因为栈泄漏而破坏其他栈。
②定义存储器访问权限,使得应用任务只能访问有限的外设
③定义存储器访问权限,使得应用任务只能访问自己的数据或自己的程序数据(设置起来可能会有些麻烦,因为多数情况下OS和程序代码是一起编译的,因此数据在存储器映射中也可能是混在一起的)。
如果需要的话,具有嵌入式OS的系统还可以使用静态配置的。
MPU中存在多个寄存器。这些寄存器位于系统控制空间(SCS)。CMSIS-Core头文件为MPU寄存器定义了一个数据结构体,方便对这些寄存器的访问。下表对这些寄存器进行了总结:
地址 | 寄存器 | CMSIS-Core符号 | 功能 |
---|---|---|---|
0xE000ED90 | MPU类型寄存器 | MPU->TYPE | 提供MPU方面的信息 |
0xE000ED94 | MPU控制寄存器 | MPU->CTRL | MPU使能/禁止和背景区域控制 |
0xE000ED98 | MPU区域编号寄存器 | MPU->RNR | 选择待配置的MPU区域 |
0xE000ED9C | MPU基地址寄存器 | MPU->RBAR | 定义MPU区域的基地址 |
0xE000EDA0 | MPU区域属性和大小寄存器 | MPU->RASR | 定义MPU区域的属性和大小 |
0xE000EDA4 | MPU别名1区域属性和大小寄存器 | MPU->RBAR_A1 | MPU->RBAR的别名 |
0xE000EDA8 | MPU别名1区域属性和大小寄存器 | MPU->RASR_A1 | MPU->RASR的别名 |
0xE000EDAC | MPU别名2区域属性和大小寄存器 | MPU->RBAR_A2 | MPU->RBAR的别名 |
0xE000EDB0 | MPU别名2区域属性和大小寄存器 | MPU->RASR_A2 | MPU->RASR的别名 |
0xE000EDB4 | MPU别名3区域属性和大小寄存器 | MPU->RBAR_A3 | MPU->RBAR的别名 |
0xE000EDB8 | MPU别名3区域属性和大小寄存器 | MPU->RASR_A3 | MPU->RASR的别名 |
第一个为MPU类型寄存器,可以利用它确定MPU是否存在。若DREGION域读出为0,则说明MPU不存在,如下表所示
位 | 名称 | 类型 | 复位值 | 描述 |
---|---|---|---|---|
23:16 | IREGION | R | 0 | 本MPU支持的指令区域数。由于ARMv7-M架构使用统一的MPU,其总为0 |
15:8 | DREGION | R | 0或8 | MPU支持的区域数。在cortex-M3中,其为0(MPU不存在)或8(MPU存在) |
0 | SEPARATE | R | 0 | 由于MPU为统一的,其总为0 |
MPU由多个寄存器控制。第一个为MPU控制寄存器,如下表所示。它具有3个控制位。复位后,该寄存器的数值为0,表示MPU禁止。要使能MPU,软件应该首先设置每个MPU区域,然后再设置MPU控制寄存器的ENABLE位。
位 | 名称 | 类型 | 复位值 | 描述 |
---|---|---|---|---|
2 | PRIVDEFENA | R/W | 0 | 特权等级的默认存储器映射使能,当其为1且MPU使能时,特权访问时会将默认的存储器映射用作背景区域;若其未置位,则背景区域被禁止且对不属于任何使能区域的访问会引发错误。 |
1 | HFNMIENA | R/W | 0 | 若为1,则MPU在硬件错误处理和不可屏蔽中断(NMI)处理中也是使能的,否则,硬件错误及NMI中MPU使能 |
0 | ENABLE | R/W | 0 | 若为1则使能MPU |
MPU控制寄存器中的PRIVDEFENA位用于背景区域的使能(区域-1)。若未设置其他区域,那么通过PRIVDEFENA,特权程序可以访问所有的存储器位置,且只有非特权程序会被阻止。不过,若设置并使能了其他的MPU区域,背景区域可能会被覆盖。例如,若具有类似区域设置的两个系统中只有一个的PRIVDEFENA置1(下图右侧),则PRIVDEFENA为1的那个允许对背景区域的特权访问。
HFNMIENA定义了NMI、HardFault异常执行期间或FAULTMASK置位时MPU的行为,MPU在这些情况下默认被旁路(禁止)。即便MPU设置得不正确,它也可以使HardFault和NMI异常处理正常执行。设置MPU控制寄存器中的使能位通常是MPU设置代码的最后一步,否则MPU可能会在区域配置完成前意外产生错误。许多情况下,特别是在具有动态MPU配置的嵌入式OS中,MPU配置程序开头应该将MPU禁止,以免在MPU区域配置期间意外触发MemManage错误。
下一个MPU控制寄存器为MPU区域编号寄存器,如下表所示。在设置每个区域前,写入这个寄存器可以选择要编程的区域。
位 | 名称 | 类型 | 复位值 | 描述 |
---|---|---|---|---|
7:0 | REGION | R/W | — | 选择待编程的区域,由于MPU支持8个区域,该寄存器只使用了bit[2:0] |
每个区域的起始地址在MPU区域基地址寄存器中定义,如下表所示。利用该寄存器中的VALID和REGION区域,可以跳过设置MPU区域编号寄存器这一步。这样可以降低程序代码的复杂度,特别是整个MPU设置定义在一个查找表中时。
位 | 名称 | 类型 | 复位值 | 描述 |
---|---|---|---|---|
31:N | ADDR | R/W | ---- | 区域的基地址,N取决于区域大小。例如,64K大小的区域基地址域为[31:16] |
4 | VALID | R/W | ---- | 若为1,则bit[3:0]定义的REGION会用在编译阶段,否则就会使用MPU区域编号寄存器选择的区域 |
3:0 | REGION | R/2 | ---- | 若VALID为1,则该域会覆盖MPU区域编号寄存器,否则会被忽略。由于cortex-M3和cortex-M4的MPU支持8个区域,若REGION域大于7,则不会进行区域编号覆盖 |
MPU区域基本属性和大小寄存器用来定义每个区域的属性,如下表所示:
位 | 名称 | 类型 | 复位值 | 描述 |
---|---|---|---|---|
31:29 | 保留 | — | — | ---- |
28 | XN | R/W | 0 | 指令访问禁止(1=静止该区域的取指,非要这么做会引发存储器管理错误) |
27 | 保留 | |||
26:24 | AP | R/W | 000 | 数据访问允许域 |
23:22 | 保留 | |||
21:19 | TEX | R/W | 000 | 类型展开域 |
18 | S | R/W | 可共用 | |
17 | C | R/W | 可缓存 | |
16 | B | R/W | 可缓冲 | |
15:8 | SRD | R/W | 0x00 | 子区域禁止 |
7:6 | 保留 | |||
5:1 | REGION大小 | R/W | MPU保护区域大小 | |
0 | ENABLE | R/W | 0 | 区域使能 |
MPU区域基本属性和大小寄存器中的REGION SIZE域决定区域的大小,如下表所示:
REGION 大小 | 大小 | More ActionsREGION 大小 | 大小 |
---|---|---|---|
b00000 | 保留 | b10000 | 128KB |
b00001 | 保留 | b10001 | 256KB |
b00010 | 保留 | b10010 | 512KB |
b00011 | 保留 | b10011 | 1MB |
b00100 | 32B | b10100 | 2MB |
b00101 | 64B | b10101 | 4MB |
b00110 | 128B | b10110 | 8MB |
b00111 | 256B | b10111 | 16MB |
b01000 | 512B | b11000 | 32MB |
b01001 | 1KB | b11001 | 64MB |
b01010 | 2KB | b11010 | 128MB |
b01011 | 4KB | b11011 | 256MB |
b01100 | 8KB | b11100 | 512MB |
b01101 | 16KB | b11101 | 1GB |
b01110 | 32KB | b11110 | 2GB |
b01111 | 64KB | b11111 | 4GB |
子区域禁止域(MPU区域基本属性和大小寄存器的bit[15:8])用于将一个区域分为8个相等的子区域并定义每个部分为使能或禁止的。若一个子区域被禁止且和另一区域重叠,则另一区域的访问规则会起作用。若子区域禁止但未和其他区域重叠,则对该存储器区域的访问会导致MemManage错误。若区域大小为128字节或更小,则子区域无法使用。数据访问权限(AP)定义了区域的AP,如下表所示:
AP数值 | 特权访问 | 用户访问 | 描述 |
---|---|---|---|
000 | 无访问 | 无访问 | 无访问 |
001 | 读/写 | 无访问 | 只支持特权访问 |
010 | 读/写 | 只读 | 用户程序中的写操作会引发错误 |
011 | 读/写 | 读/写 | 全访问 |
100 | 无法预测 | 无法预测 | 无法预测 |
101 | 只读 | 无访问 | 只支持特权读 |
110 | 只读 | 只读 | 只读 |
111 | 只读 | 只读 | 只读 |
XN(永不执行)域(bit[28])决定是否允许从该区域取指。若该域为1,则所有从本区域取出的指令在进入执行阶段时都会触发MemManage错误。TEX(类型展开)、S(可共享)、B(可缓冲)及C(可缓存)域(bit[21:16])要复杂一些。这些存储器属性在每次指令和数据访问时都会被输出到总线系统,而且该信息可被写缓冲或缓存单元等总线系统使用,如下图所示:
尽管Ccortex-M3和Cortex-M4处理器中不存在缓存控制器,它们的设计遵循ARMv7-M架构,该架构支持系统总线级的外部缓存控制器,其中包括具有缓存能力的高级存储器系统。另外,受可缓冲属性影响的处理器内部总线系统中存在一个写缓冲。因此,为了支持不同类型的存储器或设备,应该正确地设置TEX、S、B和C等区域访问属性,这些位域的定义如下表所示。不过,对于许多微控制器来说,总线系统是不会使用这些存储器属性的,只有B属性会影响到处理器中的写缓冲。
TEX | C | B | 描述 | 区域可共享性 |
---|---|---|---|---|
b000 | 0 | 0 | 强序(传输按照程序顺序执行后完成) | 可共享 |
b000 | 0 | 1 | 共享设备(写可以缓冲) | 可共享 |
b000 | 1 | 0 | 外部和内部写通,非写分配 | [S] |
b000 | 1 | 1 | 外部和内部写回,非写分配 | [S] |
b001 | 0 | 0 | 外部和内部不可缓存 | [S] |
b001 | 0 | 1 | 保留 | 保留 |
b001 | 1 | 0 | 由具体实现定义 | — |
b001 | 1 | 1 | 外部和内部写回,写和读分配 | [S] |
b010 | 0 | 0 | 不可共享设备 | 不可共享 |
b010 | 0 | 1 | 保留 | 保留 |
b010 | 1 | X | 保留 | 保留 |
b1BB | A | A | 缓存存储器, BB=外部策略, AA=内部策略 | [S] |
[S]:表示可共享性由S位决定(多个处理器共用)
存储器属性设置支持两个缓存等级:内部缓存和外部缓存。它们可以具有不同的缓存策略。若实现了系统级缓存,它可以使用内部缓存属性也可以使用外部缓存属性,实际选择由设备决定,因此需要参考芯片供应商提供的文档。多数情况下,存储器属性可以配置为下表所示的形式,且两个等级的属性相同。
类型 | 存储器类型 | 常用的存储器属性 |
---|---|---|
ROM,Flash(可编程存储器) | 普通存储器 | 不可共用,写通C=1,B=0,TEX=0,S=0 |
内部SRAM | 普通存储器 | 可共用,写通 C=1,B=0,TEX=0,S=1 |
外部RAM | 普通存储器 | 可共用,写回 C=1,B=1,TEX=0,S=1 |
外设 | 设备 | 可共用,设备C=0,B=1,TEX=0,S=1 |
有些情况下内部和外部缓存可能需要具有不同的策略,此时需要将TEX的第2位设置为1.这样TEX[1:0]的定义就会变为外部策略,而C和B位则会变为内部策略。缓存策略的定义如下表所示:
存储器属性编码(AA和BB) | 缓存策略 |
---|---|
00 | 不可共享 |
01 | 写回,写和读分配 |
10 | 写通,无写分配 |
11 | 写回,无写分配 |
若正在使用的微控制器具有缓存存储器,且在应用中利用MPU定义了访问权限,那么应该确认存储器属性是否和要使用的存储器类型及缓存策略相匹配(如缓存禁止、写通缓存或写回缓存)。可共享属性对于具有缓存的多处理器系统非常重要。在这些系统中,若传输被标记为可共享的,那么缓存系统可能需要做一些额外的工作以确保不同处理器间的数据一致性,对于单处理器系统,可共享属性则不会被用到。
备注:参考ARM Cortex-M3与Cortex-M4权威指南