可能很多工程师在使用Cortex-M处理器做开发的时候最怕遇到的一类错误就是调试时遇到Hard Fault。引发Fault异常的情况有很多,除了程序本身的因素以为,设备外部环境的原因,比如静电,电磁干扰,极端的运行温度、震荡,辐射等也可能会导致系统出现错误。
实际上,为了尽早的检测到问题,Cortex-M处理器才是增加了Fault异常处理机制。所有的错误默认都会触发HardFault异常,包括在Cortex-M0/M0+HardFault都是支持的。Cortex-M3/M3/M7另外还有3个可配置的错误异常处理:
🔸MemManage存储器管理错误
🔸Bus Fault总线错误
🔸Usage Fault用法错误
需要触发这些异常首先需要将它们使能,它们的优先级要大于当前异常的优先级。为了便于检测错误的类型,CortexM3/M4/M7还提供了多个错误状态寄存器(FSR),利用FSR中的状态位定位到问题也会更加容易。在一些情况下错误地址还会被错误地址寄存器(FAR)捕获,通过FAR可以获知精确读/写时导致的错误。
1、存储管理错误
MemManage错误由MPU配置定义的访问规则引发。例如,非特权的代码访问了只支持特权访问的存储器区域,向被MPU定义为只读的存储器位置进行写操作,试图从不可执行区域(XN)执行代码。这些访问可以是在指令执行,取指期间的数据访问或执行过程中的栈操作。
2、总线错误
Bus Fault是由存储器访问期间从处理器总线接口上收到的错误响应触发。包括取值,数据读写和栈操作。如果取指产生了总线错误,只有错误的位置进入执行阶段时才会触发总线错误。
总线错误分为精确的总线错误和不精确的总线错误两类。精确的总线错误是在存储器访问指令执行后错误异常立即产生。不精确的总线错误是因为处理器总线接口上存在写缓冲,存储器访问指令执行一段时间后才产生错误异常。有时候为了方便调试可以使用辅助控制寄存器中的DISDEFWBUF位禁止写缓冲。
3、用法错误
Usage fault可由多种情况引发。包括执行未定义的指令,非对齐操作,除零,非法指令(试图切换到ARM状态,执行协处理器指令),使用多重加载/存储指令时地址没对齐。
这些Fault异常默认都是禁止的,所以都会上访成HardFault。可以通过系统处理控制和状态寄存器(SCB->SHCSR)来使能,将位16~位18置1就会使用这些Fault异常。
SCB->SHCSR |= SCB_SHCSR_MEMFAULTENA_Msk | SCB_SHCSR_BUSFAULTENA_Msk | SCB_SHCSR_USGFAULTENA_Msk; //Set bit 16~18
发生存储管理错误时,可以查看存储器管理错误状态寄存器了解异常的类型,SCB->CFSR中的最低字节是Memory Manage Fault(MFSR),地址:0xE000ED28。
若MFSR中的DACCVIOL为1或IACCVIOL为1,则产生错误的代码可由栈帧中的PC定位。
若MMARVALID位为1,可以利用MemManage错误地址寄存器(SCB->MMFAR)确定引发错误的存储器地址。
发Bus Fault时,可以查看总线错误状态寄存器了解异常的大致类型,供异常处理程序分析。SCB->CFSR中的第二字节是Bus Fault(BFSR),地址:0xE000ED29。
IBUSERR表示总线错误由取指令期间的错误引发,错误的指令地址可以由栈帧中的PC确定。PRECISERR和IMPRECISERR都用于数据访问,PRECISERR=1表示是精确的总线错误,且错误的地址会被写入总线错误地址寄存器(SCB->BFAR)。IMPRECISERR=1表示不是精确的总线错误,压栈的PC无法反映错误的指令地址,且错误传输的地址也不会显示在BFAR中。
发生用法错误时,可通过查看寄存器了解出错的原因SCB->CFSR的高半字是 Usage Fault(UFSR),地址:0xE000ED2A。
Hard错误状态寄存器中记录有产生硬错误异常的诱因,SCB->HFSR,地址:0xE000ED2C。
当异常发生时,R0~R3, R12, LR,PC,xPSR被硬件自动入栈保存,也称为栈帧。栈帧保存在哪个堆栈中此时的LR决定,当LR的bit2为1,保存在主堆栈,由MSP指向;当LR的bit2为0,则保存在进程栈,由PSP指向。
因此,通过对栈帧进行分析,结合上面的各个Fault寄存器中的信息就能分析出多种引发错误的原因了。