建议:将 MPU Region 0 配置为覆盖全 4 GB 地址空间的 non-cacheable 背景区,并采用 AP_NONE + XN 作为默认拒绝策略;再使用更高编号的 Region 显式开放实际需要访问的内存。下文MPU配置只针对于SDK,MCAL中MPU默认配置和下文一致,无需修改。
1. 问题背景
在已知案例中,系统使能 Cache 后偶发 IBUSERR(Instruction Bus Error);分析怀疑 Cache 预取访问越过有效区域并触及 CUS_NVR 或 其他未开放的NVR区域。将相关 NVR 区域显式配置为 non-cacheable,或关闭 MPU 的特权默认内存映射(PRIVDEFENA = 0)后,问题未再复现。
IBUSERR 故障现场,BFSR.IBUSERR 置位
上述现象用于说明配置风险,不代表所有 IBUSERR 都由同一原因引起。实际故障仍应结合 CFSR、HFSR、BFAR、MMFAR 和异常现场进一步确认。
ARMv7-M 在 MPU 未覆盖区域可使用默认内存映射;其中部分地址范围具有 Cache 属性。如果使能 PRIVDEFENA,特权访问可能继续使用这些默认属性,导致未显式配置的地址仍可被访问。
ARMv7-M 默认内存映射及 Cache 属性
2. 推荐配置原则
Region 0 覆盖全 4 GB 地址空间:基址为 0x00000000,大小为 4 GB。
Region 0 默认禁用 Cache:配置为 Strongly-Ordered,TEX=0、C=0、B=0。
Region 0 默认禁止访问和执行:配置 AP_NONE 和 XN=1。因此它不只是一个 non-cacheable 区域,也是所有未授权地址的兜底拒绝区。
有效内存使用更高编号 Region 覆盖:MPU 区域重叠时,编号更高的 Region 优先。例如 Region 7/8 可覆盖 Region 6 的部分地址,提供 non-cacheable 映射。
配置前清除全部 Region:根据 MPU->TYPE 获取硬件实现的 Region 数量并逐个清除,避免 Bootloader 或前序代码留下的高编号 Region 覆盖当前配置。
关闭特权默认内存映射:调用 ARM_MPU_Enable(0U),确保 MPU_CTRL.PRIVDEFENA=0,未被白名单 Region 开放的地址不得访问。
先配置 MPU,再使能 Cache:避免在内存属性尚未确定时产生 Cache 行填充或预取。
注意: 如果把 Region 0 改为 AP_FULL,虽然仍可设置 non-cacheable,但会失去“未配置地址默认禁止访问”的白名单保护效果,建议使用白名单控制更精确。
3. Region 配置摘要
Region
地址和大小
内存属性
访问/执行属性
用途
0
0x00000000, 4 GB
Strongly-Ordered, non-cacheable
No Access, XN
全地址空间背景区
1
0x00000000, 32 KB
Normal, non-cacheable
Full Access, executable
ITCM
2
0x02000000, 2 MB
Normal, Write-Through
Full Access, executable
PFlash
3
0x06000000, 256 KB
Shareable Device
Full Access, XN
DFlash
4
0x10000000, 16 KB
Device
Privileged RW, XN
NVR0
5
0x10020000, 16 KB
Device
Privileged RW, XN
NVR1
6
0x20000000, 512 KB
Normal, WBWA
Full Access, executable
SRAM 主区域
7
0x20000000, 128 KB
Normal, non-cacheable, shareable
Full Access, executable
DTCM,覆盖 Region 6
8
0x20020000, 64 KB
Normal, non-cacheable
Full Access, executable
OCRAM non-cacheable 区
9
0x40000000, 2 MB
Device
Privileged RW, XN
APB 外设
10
0x67000000, 128 B
Device, non-shareable
Full Access, executable
QSPI RX Buffer
11
0xE0000000, 1 MB
Device, non-shareable
Privileged RW, XN
PPB
4. MPU 参考配置
void MPU_Config(void)
{
uint32_t region;
uint32_t region_count;
ARM_MPU_Disable();
/* Clear all implemented regions to remove any previous MPU configuration. */
region_count = (MPU->TYPE & MPU_TYPE_DREGION_Msk) >> MPU_TYPE_DREGION_Pos;
for (region = 0U; region < region_count; ++region)
{
ARM_MPU_ClrRegion(region);
}
/* Region 0: 4GB background, strongly-ordered, no access, XN */
MPU->RBAR = ARM_MPU_RBAR(0, 0x00000000U);
MPU->RASR = ARM_MPU_RASR(1, ARM_MPU_AP_NONE, 0, 0, 0, 0, 0,
ARM_MPU_REGION_SIZE_4GB);
/* Region 1: ITCM 0x00000000 32KB, Normal non-cacheable, RWX */
MPU->RBAR = ARM_MPU_RBAR(1, 0x00000000U);
MPU->RASR = ARM_MPU_RASR(0, ARM_MPU_AP_FULL, 1, 0, 0, 0, 0,
ARM_MPU_REGION_SIZE_32KB);
/* Region 2: PFlash 0x02000000 2MB, Normal write-through, RWX */
MPU->RBAR = ARM_MPU_RBAR(2, 0x02000000U);
MPU->RASR = ARM_MPU_RASR(0, ARM_MPU_AP_FULL, 0, 0, 1, 0, 0,
ARM_MPU_REGION_SIZE_2MB);
/* Region 3: DFlash 0x06000000 256KB, Shareable device, XN */
MPU->RBAR = ARM_MPU_RBAR(3, 0x06000000U);
MPU->RASR = ARM_MPU_RASR(1, ARM_MPU_AP_FULL, 0, 1, 0, 1, 0,
ARM_MPU_REGION_SIZE_256KB);
/* Region 4: NVR0 0x10000000 16KB, Device, privileged RW, XN */
MPU->RBAR = ARM_MPU_RBAR(4, 0x10000000U);
MPU->RASR = ARM_MPU_RASR(1, ARM_MPU_AP_PRIV, 0, 0, 0, 1, 0,
ARM_MPU_REGION_SIZE_16KB);
/* Region 5: NVR1 0x10020000 16KB, Device, privileged RW, XN */
MPU->RBAR = ARM_MPU_RBAR(5, 0x10020000U);
MPU->RASR = ARM_MPU_RASR(1, ARM_MPU_AP_PRIV, 0, 0, 0, 1, 0,
ARM_MPU_REGION_SIZE_16KB);
/* Region 6: SRAM 0x20000000 512KB, Normal WBWA, RWX */
MPU->RBAR = ARM_MPU_RBAR(6, 0x20000000U);
MPU->RASR = ARM_MPU_RASR(0, ARM_MPU_AP_FULL, 5, 0, 0, 1, 0,
ARM_MPU_REGION_SIZE_512KB);
/* Region 7: DTCM 0x20000000 128KB, Normal non-cacheable shareable, RWX */
MPU->RBAR = ARM_MPU_RBAR(7, 0x20000000U);
MPU->RASR = ARM_MPU_RASR(0, ARM_MPU_AP_FULL, 1, 1, 0, 0, 0,
ARM_MPU_REGION_SIZE_128KB);
/* Region 8: OCRAM 0x20020000 64KB, Normal non-cacheable, RWX */
MPU->RBAR = ARM_MPU_RBAR(8, 0x20020000U);
MPU->RASR = ARM_MPU_RASR(0, ARM_MPU_AP_FULL, 1, 0, 0, 0, 0,
ARM_MPU_REGION_SIZE_64KB);
/* Region 9: APB 0x40000000 2MB, Device, privileged RW, XN */
MPU->RBAR = ARM_MPU_RBAR(9, 0x40000000U);
MPU->RASR = ARM_MPU_RASR(1, ARM_MPU_AP_PRIV, 0, 0, 0, 1, 0,
ARM_MPU_REGION_SIZE_2MB);
/* Region 10: QSPI RX buffer 0x67000000 128B, Device non-shareable, RWX */
MPU->RBAR = ARM_MPU_RBAR(10, 0x67000000U);
MPU->RASR = ARM_MPU_RASR(0, ARM_MPU_AP_FULL, 2, 0, 0, 0, 0,
ARM_MPU_REGION_SIZE_128B);
/* Region 11: PPB 0xE0000000 1MB, Device non-shareable, privileged RW, XN */
MPU->RBAR = ARM_MPU_RBAR(11, 0xE0000000U);
MPU->RASR = ARM_MPU_RASR(1, ARM_MPU_AP_PRIV, 2, 0, 0, 0, 0,
ARM_MPU_REGION_SIZE_1MB);
/*
* DefaultMapEn = FALSE: MPU_CTRL.PRIVDEFENA remains 0.
* RunHFNMIEn = FALSE: MPU_CTRL.HFNMIENA remains 0.
* ARM_MPU_Enable() enables MemManage faults through SCB->SHCSR.
*/
ARM_MPU_Enable(0U);
}
5. 集成检查项
确认芯片实现的 MPU Region 数量满足配置需求,可读取 MPU->TYPE 核对。
确认每个 Region 的基址按 Region 大小对齐,大小为 MPU 支持的 2 的幂。
对照芯片 Memory Map 和链接脚本,覆盖所有 CPU 会访问的代码、数据、启动区、外设、NVR、外部存储及别名地址。
禁用 MPU 后先清除硬件实现的全部 Region,再写入当前配置,避免残留的高编号 Region 取得更高优先级。
保持 Region 0 为最低编号;需要覆盖其属性的有效区域使用更高编号。
NVR、外设寄存器和其他不允许 Cache 的区域必须显式配置为 Device、Strongly-Ordered 或合适的 Normal non-cacheable 类型。
DMA 缓冲区应使用 non-cacheable 区域,或严格执行 Cache clean/invalidate;不能只依赖 Region 0。
根据最小权限原则复核 AP 和 XN。数据区通常应设置 XN,只读程序 Flash 通常不应开放不必要的写权限。
在 MPU 配置完成后再使能 I-Cache/D-Cache;修改运行中的内存属性前后按 CMSIS/芯片手册要求执行 Cache 维护及 DSB/ISB。
若应用确实需要在 HardFault、NMI 中使用 MPU 白名单外的地址,应单独评估 HFNMIENA,不要直接照搬示例。
6. 验证建议
完成配置后,至少执行以下验证:
启动验证:系统可正常完成启动,向量表、启动代码、栈、堆和所有初始化访问均被白名单覆盖。
功能验证:覆盖 Flash/NVR 擦写、外设访问、DMA、QSPI、通信和低功耗唤醒等实际场景。
负向验证:在受控测试中访问一个未开放地址,应触发 MemManage Fault,而不是继续采用默认内存属性访问。
稳定性验证:在客户实际负载、编译优化等级和工作温度下进行长时间压力测试,确认 CFSR.IBUSERR、PRECISERR、IMPRECISERR 等故障位不再出现。
7. 结论
Region 0 建议统一配置为覆盖全 4 GB 的 non-cacheable、No Access、XN 背景区,并关闭 PRIVDEFENA。这种白名单方式可以避免未显式配置区域继承 ARM 默认内存映射和 Cache 属性;具体可访问、可执行及可缓存范围由更高编号 Region 精确开放。