MemManage fault发生的原因
2026/9/29大约 2 分钟
Cortex-M MemManage Fault:SHCSR CFSR 与 MMFAR
1. 什么是 MemManage Fault?
SCB寄存器分配图

Cortex-M3/M4 提供三类可配置 Fault:
MemManage Fault → 异常 #4
BusFault → 异常 #5
UsageFault → 异常 #6需要通过 SHCSR 单独使能。

SCB->SHCSR |= SCB_SHCSR_MEMFAULTENA_Msk;如果没有使能,相关错误通常会升级为 HardFault。
2. CFSR 与 MMFAR
CFSR 用来判断 Fault 原因:

31 16 15 8 7 0
┌─────────────────┬──────────┬─────────┐
│ UFSR │ BFSR │ MMFSR │
└─────────────────┴──────────┴─────────┘MemManage 主要看 MMFSR:

IACCVIOL → 非法取指
DACCVIOL → 非法数据访问
MUNSTKERR → 异常返回出栈错误
MSTKERR → 异常进入压栈错误如果:
MMARVALID = 1那么 MMFAR 中保存的故障地址才有效。
所以:
CFSR / MMFSR → 什么错误
MMFAR → 哪个地址
异常栈 PC → 异常现场3. 完整异常处理代码
异常栈帧
异常进入时,硬件自动保存:
typedef struct
{
uint32_t r0;
uint32_t r1;
uint32_t r2;
uint32_t r3;
uint32_t r12;
uint32_t lr;
uint32_t pc;
uint32_t xpsr;
} stack_frame_t;C 处理函数
static void fault_show_regs(stack_frame_t *frame)
{
printf("Registers:\n");
printf("R0 = 0x%08lx\n", frame->r0);
printf("R1 = 0x%08lx\n", frame->r1);
printf("R2 = 0x%08lx\n", frame->r2);
printf("R3 = 0x%08lx\n", frame->r3);
printf("R12 = 0x%08lx\n", frame->r12);
printf("LR = 0x%08lx\n", frame->lr);
printf("PC = 0x%08lx\n", frame->pc);
printf("xPSR = 0x%08lx\n", frame->xpsr);
}
void MemManage_C_Handler(stack_frame_t *frame)
{
uint32_t cfsr = SCB->CFSR;
uint32_t mmfsr = cfsr & SCB_CFSR_MEMFAULTSR_Msk;
printf("\nMemManage Fault!\n");
if (mmfsr & SCB_CFSR_IACCVIOL_Msk)
printf("Instruction access violation\n");
if (mmfsr & SCB_CFSR_DACCVIOL_Msk)
printf("Data access violation\n");
if (mmfsr & SCB_CFSR_MSTKERR_Msk)
printf("Exception entry stacking error\n");
if (mmfsr & SCB_CFSR_MUNSTKERR_Msk)
printf("Exception return unstacking error\n");
if (mmfsr & SCB_CFSR_MMARVALID_Msk)
printf("Fault address = 0x%08lx\n", SCB->MMFAR);
else
printf("Fault address is invalid\n");
fault_show_regs(frame);
while (1);
}Handler
异常进入后,先判断异常现场使用 MSP 还是 PSP:
__attribute__((naked))
void MemManage_Handler(void)
{
__asm volatile(
"TST LR, #4 \n"
"ITE EQ \n"
"MRSEQ R0, MSP \n"
"MRSNE R0, PSP \n"
"B MemManage_C_Handler\n"
);
}这里:
LR bit2 = 0 → 异常现场在 MSP
LR bit2 = 1 → 异常现场在 PSP然后把栈地址作为第一个参数传给 C 函数。
4. 测试
先使能 MemManage:
SCB->SHCSR |= SCB_SHCSR_MEMFAULTENA_Msk;然后执行一个禁止执行区域的地址:
((void (*)(void))0xF0000000)();如果该区域不可执行,就可能产生:
MemManage Fault
↓
IACCVIOL
↓
MemManage_Handler
↓
读取 CFSR / MMFAR
↓
解析异常栈
↓
得到 PC / LR / xPSR5. 最终形成完整故障定位链
Fault发生
↓
SHCSR使能对应Fault
↓
进入MemManage_Handler
↓
根据EXC_RETURN选择MSP/PSP
↓
取得异常栈
↓
CFSR → 判断错误类型
MMFAR → 判断故障地址
PC → 分析异常现场
LR → 分析调用关系核心记忆:
CFSR 看“为什么错”,MMFAR 看“哪个地址”,异常栈看“当时 CPU 在干什么”。

