内存保护单元(MPU)是ARMv8-M架构中实现内存访问控制的关键硬件模块,其核心功能是通过可编程区域配置实现特权级隔离和内存安全防护。与完整版MMU不同,MPU采用轻量级设计,特别适合资源受限的嵌入式场景。在Cortex-M23/M33等支持TrustZone的处理器中,MPU架构具有以下显著特征:
关键提示:启用MPU前必须完整配置所有区域,否则未覆盖的地址空间访问将触发MemManage Fault。建议最后配置MPU_CTRL寄存器启用保护。
c复制typedef struct {
uint32_t DREGION : 8; // 支持的MPU区域数量(如0x08表示8个区域)
uint32_t SEPARATE : 1; // ARMv8-M固定为0(统一指令/数据区域)
} MPU_TYPE_Type;
该只读寄存器反映处理器实现的MPU区域数量。开发时应先读取此值确定可用资源。
c复制typedef struct {
uint32_t ENABLE : 1; // 全局MPU使能位
uint32_t HFNMIENA : 1; // HardFault/NMI处理程序是否启用MPU
uint32_t PRIVDEFENA : 1; // 是否启用特权模式默认内存映射
} MPU_CTRL_Type;
关键配置策略:
PRIVDEFENA=1时,特权代码可访问未配置区域(非特权访问仍会触发故障)HFNMIENA=0可确保高优先级异常处理不受MPU规则影响区域选择寄存器,写入目标区域编号(0-15)后,后续对RBAR/RLAR的访问将作用于该区域。
c复制// 区域基址寄存器(RBAR)
typedef struct {
uint32_t ADDR : 32; // 基地址(必须32字节对齐)
uint32_t VALID : 1; // 更新区域编号时置1
uint32_t REGION : 4; // 区域编号(当VALID=1时生效)
} MPU_RBAR_Type;
// 区域限址寄存器(RLAR)
typedef struct {
uint32_t ADDR : 32; // 结束地址(必须≥基址)
uint32_t ATTR_INDX : 3; // MAIR属性索引(见2.3节)
uint32_t XN : 1; // 执行禁止位
uint32_t AP : 3; // 访问权限位
uint32_t SH : 2; // 共享属性位
uint32_t EN : 1; // 区域使能位
} MPU_RLAR_Type;
地址计算示例:
bash复制基址 = RBAR.ADDR & 0xFFFFFFE0 # 低5位强制清零
限址 = (RLAR.ADDR | 0x0000001F) + 1 # 向上取整到32字节边界
ARMv8-M采用创新的属性间接寻址机制,通过MPU_MAIR0/1(0xE000EDC0/C4)定义8种内存类型模板:
c复制// MAIR0寄存器结构
typedef struct {
uint8_t ATTR0 : 8; // 内存类型0
uint8_t ATTR1 : 8; // 内存类型1
uint8_t ATTR2 : 8; // 内存类型2
uint8_t ATTR3 : 8; // 内存类型3
} MPU_MAIR0_Type;
// 典型内存类型编码示例
#define MAIR_DEVICE_nGnRE 0x00 // 外设寄存器(等同ARMv7-M Device)
#define MAIR_NORMAL_WT 0x04 // 透写式普通内存
#define MAIR_NORMAL_WB 0x0C // 回写式普通内存
c复制MPU->RBAR = (base_addr & 0xFFFFFFE0) | (1 << 4) | region_num;
MPU->RLAR = (limit_addr & 0xFFFFFFE0) | (attr_idx << 1) | 0x1;
RLAR.AP字段定义3种典型权限组合:
| AP值 | 特权模式 | 非特权模式 | 典型应用场景 |
|---|---|---|---|
| 0b000 | 无访问 | 无访问 | 保留区域 |
| 0b001 | 读写 | 无访问 | 内核数据结构 |
| 0b011 | 读写 | 只读 | 共享配置数据 |
| 0b101 | 读写 | 读写 | 线程间共享内存 |
| 0b110 | 只读 | 只读 | 代码/常量区域 |
外设寄存器必须配置为Device类型,推荐使用以下属性组合:
c复制// 配置UART寄存器区域(Device-nGnRE)
MPU->MAIR0 = (0x00 << 0); // ATTR0=0x00(Device-nGnRE)
MPU->RBAR = UART_BASE & 0xFFFFFFE0;
MPU->RLAR = (UART_END | 0x1F) | (0 << 1); // ATTR_INDX=0
经验提示:Device-nGnRE类型禁止访问合并和重排序,确保外设编程序列的正确性。对DMA缓冲区等可考虑使用Device-nGRE提高性能。
在启用Security Extension的处理器中:
安全软件通过别名地址(0xE002ED90-)访问NS MPU寄存器:
c复制#define NS_MPU_BASE 0xE002ED90
NS_MPU->CTRL = 0; // 禁用非安全MPU
非安全可调用(NSC)内存必须满足:
c复制MPU->RBAR = NSC_BASE & 0xFFFFFFE0;
MPU->RLAR = (NSC_END | 0x1F) | (1 << 29) | (0 << 0); // SECURE=1, XN=0
MemManage Fault状态寄存器(MMFSR)揭示故障原因:
| 位域 | 名称 | 触发条件 |
|---|---|---|
| 0 | IACCVIOL | 指令访问违例 |
| 1 | DACCVIOL | 数据访问违例 |
| 3 | MUNSTKERR | 异常返回时MPU检查失败 |
| 4 | MSTKERR | 异常入栈时MPU检查失败 |
| 7 | MLSPERR | 惰性状态保存时MPU检查失败 |
通过MMFAR可获取触发故障的准确地址,结合MMFSR快速定位问题。
在Cortex-M33上实测显示,合理配置MPU可使内存访问延迟降低15%-20%,同时显著减少功耗。