内存保护单元(Memory Protection Unit,MPU)是ARM处理器中用于实现内存访问控制的关键硬件模块。与MMU(内存管理单元)不同,MPU不提供地址转换功能,而是专注于内存区域的访问权限控制和属性管理。这种设计使其在实时性要求高的嵌入式系统中具有显著优势。
MPU通过划分多个独立的内存区域(通常ARMv8-M支持8-16个可配置区域),为每个区域设置特定的访问规则。当处理器访问内存时,MPU会实时检查当前访问是否符合预设规则。如果检测到违规访问(如无权限写入或从不可执行区域取指),将触发MemManage异常。
关键区别:MMU通常用于通用计算系统,支持虚拟内存管理;而MPU专为嵌入式实时系统优化,具有更低的配置延迟和确定性行为。
MPU的核心价值体现在三个方面:
在Cortex-M系列处理器中,MPU配置通过一组专用寄存器完成,主要包括:
MPU Region Base Address Register(RBAR)定义内存区域的起始地址和基础属性。其32位字段按功能划分如下:
| 比特位 | 字段名 | 取值说明 |
|---|---|---|
| 31:5 | BASE | 区域基址(必须32字节对齐) |
| 4:3 | SH | 共享属性: 00-不共享 01-外部共享 10-内部共享 |
| 2:1 | AP | 访问权限: 00-仅特权模式可读写 01-所有模式可读写 10-仅特权模式可读 11-所有模式可读 |
| 0 | XN | 执行权限: 0-允许执行 1-禁止执行 |
典型配置示例:
c复制// 设置基址0x20000000,内部共享,全特权读写,允许执行
MPU->RBAR = (0x20000000 & MPU_RBAR_ADDR_Msk)
| MPU_RBAR_SH_INNER
| MPU_RBAR_AP_RW_PRIV
| MPU_RBAR_XN_EXEC;
关键细节:AP[2:1]字段中的bit[2]控制特权级访问,bit[1]控制用户级访问。这种编码方式允许灵活组合权限设置。
MPU Region Limit Address Register(RLAR)定义区域结束地址和扩展属性:
| 比特位 | 字段名 | 功能说明 |
|---|---|---|
| 31:5 | LIMIT | 区域结束地址(包含) |
| 3:1 | AttrIndx | 属性索引(指向MAIR中的配置) |
| 0 | EN | 区域使能位 |
地址对齐要求:
size = (LIMIT - BASE + 32)属性索引(AttrIndx)指向MPU_MAIR0/1寄存器中预定义的内存类型。这种间接寻址方式允许单个属性设置被多个区域共享。
MAIR(Memory Attribute Indirection Register)提供8个8位的属性槽位,每个槽位定义一种内存类型:
c复制// 典型MAIR0配置示例
#define NORMAL_NC 0x44 // 非缓存正常内存
#define DEVICE_nGnRE 0x00 // 严格有序设备内存
#define WRITE_THROUGH 0xAA // 写通模式
MPU->MAIR0 = (DEVICE_nGnRE << 0) | // Attr0
(NORMAL_NC << 8) | // Attr1
(WRITE_THROUGH << 16); // Attr2
内存属性编码规则:
CMSIS-CORE为MPU寄存器提供了统一的访问接口,消除了底层硬件差异。关键数据结构如下:
c复制typedef struct {
__IOM uint32_t TYPE; // MPU类型寄存器
__IOM uint32_t CTRL; // 控制寄存器
__IOM uint32_t RNR; // 区域编号寄存器
__IOM uint32_t RBAR; // 基址寄存器
__IOM uint32_t RLAR; // 限界寄存器
__IOM uint32_t MAIR0; // 属性寄存器0
__IOM uint32_t MAIR1; // 属性寄存器1
} ARM_MPU_Type;
完整MPU初始化代码示例:
c复制void MPU_Config(void) {
// 1. 确保所有内存访问完成
__DMB();
// 2. 禁用MPU
MPU->CTRL = 0;
// 3. 配置内存属性
MPU->MAIR0 = (0x00 << 0) | // Attr0: 设备内存
(0x04 << 8); // Attr1: 非缓存正常内存
// 4. 配置区域0(代码区)
MPU->RNR = 0;
MPU->RBAR = (0x00000000 & MPU_RBAR_ADDR_Msk) |
MPU_RBAR_AP_RO_PRIV |
MPU_RBAR_SH_INNER;
MPU->RLAR = (0x1FFFFFFF & MPU_RLAR_LIMIT_Msk) |
(0 << MPU_RLAR_AttrIndx_Pos) |
MPU_RLAR_ENABLE;
// 5. 配置区域1(外设区)
MPU->RNR = 1;
MPU->RBAR = (0x40000000 & MPU_RBAR_ADDR_Msk) |
MPU_RBAR_AP_RW_PRIV |
MPU_RBAR_XN_EXEC_NEVER;
MPU->RLAR = (0x5FFFFFFF & MPU_RLAR_LIMIT_Msk) |
(1 << MPU_RLAR_AttrIndx_Pos) |
MPU_RLAR_ENABLE;
// 6. 启用MPU
MPU->CTRL = MPU_CTRL_ENABLE_Msk;
// 7. 屏障指令确保配置生效
__DSB();
__ISB();
}
ARMv8-M提供了RBAR_A1/A2/A3和RLAR_A1/A2/A3别名寄存器,允许不修改RNR的情况下连续配置多个区域:
c复制// 一次性配置区域4-7
MPU->RNR = 4; // 设置起始区域号
// 使用别名寄存器快速配置
MPU->RBAR_A1 = ...; // 区域4
MPU->RLAR_A1 = ...;
MPU->RBAR_A2 = ...; // 区域5
MPU->RLAR_A2 = ...;
MPU->RBAR_A3 = ...; // 区域6
MPU->RLAR_A3 = ...;
// 标准寄存器配置区域7
MPU->RBAR = ...;
MPU->RLAR = ...;
有效的MPU配置需要合理规划内存区域:
经验法则:优先保护关键区域,如内核数据、任务控制块等。普通任务内存可采用默认域保护。
MPU配置中必须正确使用三种屏障指令:
常见错误模式:
c复制MPU->CTRL = 1; // 错误!缺少屏障指令
// 可能在此处出现权限检查不一致
当发生MemManage异常时,可通过以下寄存器诊断问题:
典型故障场景处理:
在RTOS集成中的典型应用:
c复制void vTaskSwitchContext(void) {
// 保存当前任务MPU配置
Save_MPU_Settings();
// 切换到新任务
pxCurrentTCB = pxNewTCB;
// 恢复新任务MPU配置
Load_MPU_Settings();
// 更新区域基址(如任务专用堆栈)
MPU->RBAR = pxCurrentTCB->uxMPURBAR;
MPU->RLAR = pxCurrentTCB->uxMPURLAR;
__DSB();
__ISB();
}
通过合理配置MPU,开发者可以构建具有严格内存隔离的可靠嵌入式系统。在实际项目中,建议结合芯片参考手册和CMSIS文档,针对具体应用场景优化MPU区域设置。