YTM32B1HA0 Cache预取偶发造成IBUSERR(及MPU 推荐配置)
-
建议:将 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都由同一原因引起。实际故障仍应结合CFSR、HFSR、BFAR、MMFAR和异常现场进一步确认。ARMv7-M 在 MPU 未覆盖区域可使用默认内存映射;其中部分地址范围具有 Cache 属性。如果使能
PRIVDEFENA,特权访问可能继续使用这些默认属性,导致未显式配置的地址仍可被访问。
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 GBStrongly-Ordered, non-cacheable No Access, XN 全地址空间背景区 1 0x00000000, 32 KBNormal, non-cacheable Full Access, executable ITCM 2 0x02000000, 2 MBNormal, Write-Through Full Access, executable PFlash 3 0x06000000, 256 KBShareable Device Full Access, XN DFlash 4 0x10000000, 16 KBDevice Privileged RW, XN NVR0 5 0x10020000, 16 KBDevice Privileged RW, XN NVR1 6 0x20000000, 512 KBNormal, WBWA Full Access, executable SRAM 主区域 7 0x20000000, 128 KBNormal, non-cacheable, shareable Full Access, executable DTCM,覆盖 Region 6 8 0x20020000, 64 KBNormal, non-cacheable Full Access, executable OCRAM non-cacheable 区 9 0x40000000, 2 MBDevice Privileged RW, XN APB 外设 10 0x67000000, 128 BDevice, non-shareable Full Access, executable QSPI RX Buffer 11 0xE0000000, 1 MBDevice, 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 精确开放。 - Region 0 覆盖全 4 GB 地址空间:基址为
快速上手云途开发生态
发帖前请查看
帮助改进和优化YT CONFIG TOOL,有机会抽取YTM32B1ME0 EVB哦...