Cortex-M3/M4 不可屏蔽硬件异常处理之非法内存访问

Cortex-M3/M4 不可屏蔽硬件异常处理之非法内存访问

记录使用 RT-Thread 开发 STM32F407 期间出现的硬件异常错误、排查思路及相关内容合集。

一、问题

  • 报错信息

image-20240807144700943

aht10_thread栈空间余量充足排除堆栈溢出,继续根据报错信息进行排除。

  • 最后报错 bus fault:SCB_CFS_BFSR:0x82 PRECISERR SCB->BFAR:BB000010

那么SCB是什么,SCB_CFS_BFSR、SCB->BFAR又是什么?

根据ARM32标准4GB内存空间分配如下,可见SBC为系统控制块其中包含了各个系统控制寄存器。

image-20240806151651562

查看程序可以找到Cotex-M4的SBC基地址定义,如下:

image-20240806160708914

PRECISERR SCB->BFAR:BB000010,代表在0xBB000010内存地址出现了非法访问,显然0xBB000010不在我们所能访问的内存访问内。 --> 结论PC值指向出错指令

image-20240806150110024

image-20240806161518623

scb_cfsr_bfsr:0x82 --> 1000 0010 对应MFSR/MMFSR寄存器 --> 结论PC值指向出错指令

image-20240806152952878

image-20240806153852603

image-20240806161208699

  • 查看PC寄存器及LR寄存器对应内容

关闭debug编译优化等级 -O0

复现错误,但是无法单步执行到具体位置,函数反复横跳,全速运行报错才会出现。

image-20240806180807209

直接根据报错信息PC寄存器和LR寄存器,查看.map映射,可以发现是在函数lv_obj_get_event_dsc期间出现错误,上级调用为event_send_code,最终能够给到应用层调用的只有lv_event_send函数,故排除一切有调用lv_event_send的地方。

lv_res_t lv_event_send(lv_obj_t * obj, lv_event_code_t event_code, void * param)static lv_res_t event_send_core
<think>好的,用户现在问的是关于HardFault异常的问题。之前他询问的是如何通过控制寄存器来控制外设,涉及到时钟使能和Flash代码执行流程。现在他想了解HardFault异常,可能是因为在底层编程中遇到了崩溃或错误,需要排查原因。 首先,我需要确定用户的基本背景。他可能在开发嵌入式系统,特别是使用STM32这类微控制器,直接操作寄存器时容易出错,导致HardFault。所以需要解释清楚HardFault是什么,常见原因,以及如何调试。 用户的需求可能是解决当前遇到的HardFault问题,或者预防此类问题。深层需求可能包括理解异常机制,掌握调试方法,避免程序崩溃。 接下来,我需要组织回答的结构。先定义HardFault,说明其严重性,然后列举常见原因,如内存访问错误、堆栈溢出、未对齐访问、未处理的中断或异常、指令执行错误等。每个原因需要具体例子,比如访问未使能时钟的外设寄存器,这正是用户之前提到的内容,可以联系起来。 然后,需要给出调试方法,比如检查调用栈、查看相关寄存器(SCB中的CFSR、HFSR、MMAR、BFAR等),使用断点或仿真。还要提供解决方案,如检查指针、堆栈大小、对齐问题、中断处理等。 最后,结合用户之前的兴趣,可以给出一个示例代码,展示如何故意触发和捕获HardFault,帮助他理解。同时提醒参考权威资料,如ARM文档和芯片手册。 需要注意使用用户之前提到的寄存器操作示例,比如时钟使能,如果在没有使能时钟的情况下访问外设寄存器,可能触发HardFault,这样可以和之前的讨论联系起来,加强理解。 还要确保语言清晰,结构分明,逐步引导用户理解原因和解决方法,并提供实际可行的调试步骤,比如使用调试器查看特定寄存器。避免使用过于专业的术语,但必要时给出术语解释,确保用户能够跟上。</think>好的!**HardFault异常**是ARM Cortex-M内核中最严重的错误类型之一,通常由非法硬件操作或软件错误触发。以下是其原理、触发原因与调试方法的深度解析: --- ### 一、HardFault的本质 - **定位**:属于ARM处理器异常体系中的第3级异常(优先级仅次于NMI和复位) - **核心作用**:捕获所有未被正确处理的其他异常(如总线错误、未定义指令等) - **关键特性**: $$ \text{不可屏蔽} + \text{优先级固定} = \text{系统最后的安全防线} $$ --- ### 二、常见触发原因 #### 1. **非法内存访问** - 访问未启用的外设寄存器(例如:未开启GPIO时钟时操作GPIO寄存器) - 访问无效地址(如空指针解引用) ```c volatile uint32_t *p = (uint32_t*)0xDEADBEEF; // 非法地址 *p = 0x1234; // 触发BusFault → 升级为HardFault ``` #### 2. **堆栈溢出(Stack Overflow)** - 局部变量过多或递归调用过深导致堆栈越界 - **检测公式**: $$ \text{SP指针} < \text{堆栈起始地址} \rightarrow \text{内存践踏} $$ #### 3. **未对齐的内存访问** - Cortex-M3/M4要求对4字节数据按字对齐访问 ```c uint32_t *p = (uint32_t*)(0x20000001); // 非4字节对齐地址 *p = 0x12345678; // 触发UsageFault → HardFault ``` #### 4. **未处理的其他异常** - 未定义指令(如跳转到非代码区) - 除零操作(需配置SCB->CCR的DIV_0_TRP位才会触发) #### 5. **中断服务函数(ISR)缺失** - 未实现已启用的中断处理函数 - 中断服务函数执行时间过长导致嵌套错误 --- ### 三、调试HardFault的关键步骤 #### 1. **定位异常现场** - **查看LR寄存器**: - 发生异常时,LR会被自动设置为`0xFFFFFFF9`(主堆栈)或`0xFFFFFFFD`(进程堆栈) - **获取异常时的PC值**: ```c void HardFault_Handler(void) { uint32_t *sp = __get_MSP(); // 获取主堆栈指针 uint32_t pc = sp[6]; // PC位于栈帧第7个位置 } ``` #### 2. **分析系统控制块(SCB)寄存器** - **CFSR(Configurable Fault Status Register)**: | 位域 | 作用 | |-----------|----------------------| | UFSR | UsageFault状态(高16位)| | BFSR | BusFault状态(低8位) | | MMFSR | MemManageFault状态(低8位)| - **HFSR(HardFault Status Register)**: - `FORCED`位(第30位):表示其他异常升级为HardFault - `VECTTBL`位(第1位):向量表读取错误 - **MMAR/BFAR(Fault Address Registers)**: - 当`MMFSR/BFSR`的`MMARVALID/BFARVALID`位为1时,存储引发错误的地址 #### 3. 使用调试器捕获现场 - **Keil/IAR**:在HardFault_Handler处设置断点 - **GDB**:通过`monitor arm hardfault`命令自动停止在错误位置 - **查看反汇编**:检查触发异常的汇编指令 --- ### 四、解决方案与预防措施 #### 1. 内存访问保护 - 启用MPU(Memory Protection Unit)限制非法地址访问 ```c // 配置MPU禁止访问0x00000000-0x1FFFFFFF区域 MPU->RBAR = 0x00000000 | MPU_RBAR_VALID_Msk; MPU->RASR = MPU_RASR_ENABLE_Msk | (0x00 << MPU_RASR_SIZE_Pos); ``` #### 2. 堆栈监控 - 添加栈顶哨兵值(Stack Canary)检测溢出 ```c #define STACK_CANARY 0xDEADBEEF volatile uint32_t __stack_chk_guard = STACK_CANARY; void __stack_chk_fail(void) { while(1); // 堆栈损坏时死循环 } ``` #### 3. 对齐检查 - 编译器选项强制对齐(如GCC的`-munaligned-access`) - 使用`__attribute__((aligned(4)))`修饰敏感变量 #### 4. 异常处理增强 - 重写默认的HardFault_Handler以记录错误信息 ```c __attribute__((naked)) void HardFault_Handler(void) { __asm volatile( "tst lr, #4 \n" "ite eq \n" "mrseq r0, msp \n" "mrsne r0, psp \n" "ldr r1, [r0, #24] \n" // 提取PC值 "b HardFault_Debug \n" ); } ``` --- ### 五、示例:故意触发HardFault ```c void Trigger_HardFault(void) { // 方法1:访问未映射的地址 *(volatile uint32_t*)0xE000ED88 = 0xFFFFFFFF; // 方法2:执行未定义指令 __asm volatile (".short 0xDE00"); // 生成非法操作码 } ``` --- ### 六、关键参考资料 1. **《ARM Cortex-M3/M4权威指南》**:第12章“异常处理” 2. **STM32参考手册**:第10章“中断与异常” 3. **ARM文档**: - [ARMv7-M Architecture Reference Manual](https://developer.arm.com/documentation/ddi0403/latest/) - [Cortex-M4 Technical Reference Manual](https://developer.arm.com/documentation/ddi0439/b/) 通过理解HardFault的触发机制并掌握调试工具,开发者可以快速定位底层错误,显著提高系统稳定性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值