ARM异常处理(3):Bus faults、Memory management faults、Usage faults、Hard faults详解

之前介绍了了ARM异常处理(1):异常类型、优先级分组和异常向量表,里面有很多异常类型,其中有几个异常在错误处理中非常有用:

文章目录
1 Bus Fault
2 Memory Management Fault
3 Uage faults
4 Hard Faults
1 Bus Fault
当在AHB接口上传输期间收到错误响应时,就会产生Bus fault。它可能发生在以下几个阶段:

指令预取阶段,通常称为prefetch abort
数据读/写阶段,通常称为data abort
在Cortex-M3中,出现下面几种情况也会产生Bus fault:

堆栈在中断处理的开始处PUSH,称为stacking error
堆栈在中断处理的结束处POP,称为unstacking error
当处理器开始中断处理序列时,读取中断向量地址(vector fetch)(Hard fault的一种特殊情况)
特点:
如果没有使能Bus fault的处理程序或总线错误发生在其它的比Bus fault优先级高的异常的异常处理程序时,Hard fault的中断处理函数会取代bus fault执行。如果在处理Hard fault的过程中产生了另一个Bus fault,则内核将会进入锁定状态。

导致AHB错误响应的常见原因

尝试访问一个无效的内存区域
设备没有准备好传输(如没有初始化外设的情况下访问外设)
目标设备不支持的单词传输大小(如某个外设寄存器仅支持按字访问,而程序中按字节访问)
设备不接受传输(如某些外设只能在特权访问级别编程)
1、如何使能Bus fault的处理程序?
设置NVIC中的System Handler Control and State register的BUSFAULTENA位,在此之前,需要保证在中断向量表中设置了Bus fault处理程序的起始地址。

2、进入Bus fault处理程序时,如何判断是什么出错了?
    NVIC有很多错误状态寄存器(Fault Statuc registers),其中有一个就是总线错误状态寄存器(Bus Fault Status register,BFSR),从这个寄存器中可以知道错误时由数据/指令访问还是中断堆栈等原因产生的。
    如果想更精确地定位Bus fault,可以通过堆栈程序计数器来定位错误的指令,且如果BFSR中的BFARVALID位为1的话,则还可以通过读取总线错误地址寄存器(Bus Fault Address Register,BFAR)来确定导致Bus fault的内存位置。

在有些情况下,这些寄存器的信息是不准确的,因为当处理器接收到错误时,可能已经执行的了很多其它指令。比如,对于往一个buffer写入数据而言,如果产生错误可能在多个时钟周期后才产生Bus fault,这称为unprecise bus fault ;而对于读取内存来说,下一条指令在读取完之前无法执行,故此时产生错误会立即产生Bus fault,这称为precise bus fault。
Bus Fault Status Register位于内存地址的0xE000ED29处,它的字段如下:

Bits    Name    Type    Reset Value    Description
7    BFARVALID    -    0    指示BFAR是否有效
6:5    -    -    -    -
4    STKERR    R/Wc    0    Stacking error
3    UNSTKERR    R/Wc    0    Unstacking error
2    IMPRECISERR    R/Wc    0    Imprecise data access violation
1    PRECISERR    R/Wc    0    Precise data access violation
0    IBUSERR    R/Wc    0    Instruction access violation
访问该寄存器可以按字节访问0xE000ED29,也可以按字访问0xE000ED28后得到第二个字节
向错误指示位写入1时将清除该位的状态
2 Memory Management Fault
内存管理错误可能由非法访问MPU(内存保护单元Memory Protection Unit)或某些非法访问(如执行某些不可执行的内存区域的代码)引起。常见的MPU错误如下:

访问MPU中未定义的内存区域
往只读区域写入数据
用户状态下访问特权模式下才能访问的内存
内存管理错误处理函数与Bus fault一样,有一样的特点,这里不再重复介绍。使能内存管理错误的处理函数,需要设置NVIC中的System Handler Control and State register的MEMFAULTENA位。

NVIC包含一个内存管理错误状态寄存器(Memory Management Fault Status Register,MFSR)来指示内存管理故障的原因。如果状态寄存器中的数据访问非法(DACCVIOL)位或指令访问违反(IACCVIOL位)为1,则可以通过堆栈程序计数器定位错误的代码。如果设置了MFSR中的MMARVALID位,也可以从NVIC中的内存管理地址寄存器(Memory Management Address Register,MMAR)中确定引起错误的内存地址位置。

MFSR寄存器如下表所示,它的地址为0xE000ED28,字段如下:

Bits    Name    Type    Reset Value    Description
7    MMARVALID    -    0    指示MMAR是否有效
6:5    -    -    -    -
4    MSTKERR    R/Wc    0    Stacking error
3    MUNSTKERR    R/Wc    0    Unstacking error
2    -    -    -    -
1    DACCVIOL    R/Wc    0    Data access violation
0    IACCVIOL    R/Wc    0    Instruction access violation
访问该寄存器可以按字节或按字访问0xE000ED28
对于其它FSRs,向错误状态位写入1时将清除错误状态
3 Uage faults
Usage faults可能由以下几种情况引起:

未定义的指令
协处理器指令(Cortex-M3不支持协处理器)
尝试切换到ARM state(可以使用这种机制来测试处理器是否支持ARM state;Cortex-M3不支持,如果切换会发生使用错误)
无效的中断返回(LR寄存器包含无效/不正确的值)
对未对齐的内存使用多个load/store指令
通过设置NVIC的某些位,也可以产生下面两种Usage fault:

除法除以0
访问任何不对齐的内存
Usage faults处理程序与Bus fault一样,有一样的特点,使能Usage faults的处理程序,需要设置NVIC中的System Handler Control and State register的USGFAULTENA位。

NVIC包含一个使用错误状态寄存器(Usage Fault Status Register,UFSR)来指示使用错误的原因。在处理程序内部,还可以使用堆栈的程序计数器值来定位导致错误的程序代码。

导致Usage fault的原因之一:切换到ARM state
Usage fault最常见的原因之一就是无意中将处理器切换到ARM state,这会在用户将一个LSB为0的地址加载到PC之后产生。比如,我们想使用BX或BLX指令跳转到一个没有设置地址的LSB,在异常向量表中向量的LSB为0;或者要POP已经在堆栈中的PC值,而LSB为0。在这些情况下,Usage fault会产生,UFSR寄存器中的INVSTATE会被置位。

最低位LSB为1表示使用Thumb state
UFSR寄存器如下表所示,它的地址为0xE000ED2A,字段如下:

Bits    Name    Type    Reset Value    Description
9    DIVBYZERO    R/Wc    0    指示是否是除以0引起的错误(需要设置DIV_0_TRP)
8    UNALIGNED    R/Wc    0    指示是否是字节没对齐引起的错误
7:4    -    -    -    -
3    NOCP    R/Wc    0    尝试执行协处理器指令
2    INVPC    R/Wc    0    尝试执行EXC_RETURN中错误返回值的异常
1    INVSTATE    R/Wc    0    尝试切换为一个无效的状态(如ARM state)
0    UNDEFINSTR    R/Wc    0    尝试执行一个未定义的指令
访问该寄存器可以按字节访问0xE000ED2A,也可以按字访问0xE000ED28后得到其最高字节
对于其它FSRs,向错误状态位写入1时将清除该位的错误状态
4 Hard Faults
如果Usage faults、Bus faults和Memory management faults没有其对应的错误处理程序,都将产生一个Hard fault。此外,它也会由在异常处理程序执行过程中读向量表(vector fetch)产生的Bus fault而引起。NVIC中有一个硬件错误状态寄存器(HFSR,Hard Fault Status Register),可用于确定错误是否由vector fetch引起。如果不是,则Hard fault的错误处理程序需要检查其他FSRs以确定Hard fault的原因。

HFSR寄存器与其它FSRs一样,错误状态可以通过写入1来清除,该寄存器的地址为0xE000ED2C,其字段如下:

Bits    Name    Type    Reset Value    Description
31    DIVBYZERO    R/Wc    0    指示错误是否由debug事件引起的
30    FORCED    R/Wc    0    指示错误是否是由Usage faults、Bus faults和Memory management faults产生的
29:2    -    -    -    -
1    VECTBL    R/Wc    0    指示错误是由vector fetch引起的
-    -    -    -    -
————————————————
版权声明:本文为CSDN博主「tilblackout」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/tilblackout/article/details/128185998

背景
由于文章【ARMv8M Cortex-M33 系列 7.2 – HardFault 问题定位 1】 中提到了HardFault 的发生是由于其它异常所升级导致的,所以就需要调查下如何是能其它异常中断。

异常使能配置
在 ARM Cortex-M33 核心上启用 UsageFault、MemManageFault 和 BusFault 异常的方法是通过设置系统控制块 (System Control Block, SCB) 中的配置寄存器。以下是如何启用这些异常中断的步骤:

设置 SCB 中的 SHCSR (System Handler Control and State Register): - SCB->SHCSR 寄存器包含控制系统处理程序(如 UsageFault、MemManage 和 BusFault)的使能位。
配置对应的使能位: - 通过设置 SCB->SHCSR 中的以下位来使能对应的异常:
SCB_SHCSR_MEMFAULTENA_Msk:使能 MemManage 异常。
SCB_SHCSR_BUSFAULTENA_Msk:使能 BusFault 异常。
SCB_SHCSR_USGFAULTENA_Msk:使能 UsageFault 异常。
以下是相应的代码示例:

#include "core_cm33.h" // 包含 CMSIS 核心寄存器定义和功能 
void EnableFaults(void) 

    // 使能 MemManage 异常 
    SCB->SHCSR |= SCB_SHCSR_MEMFAULTENA_Msk; 

    // 使能 BusFault 异常 
    SCB->SHCSR |= SCB_SHCSR_BUSFAULTENA_Msk; 

    // 使能 UsageFault 异常 
    SCB->SHCSR |= SCB_SHCSR_USGFAULTENA_Msk; 

1
2
3
4
5
6
7
8
9
10
11
12
当你运行上面的代码后,MemManageFault、BusFault 和 UsageFault 异常将被使能,并且在相应的故障条件发生时,处理器将进入对应的异常处理程序。

还有一点需注意的是,在默认情况下,Cortex-M 处理器的 MemManage 和 BusFault 异常是使能的,但 UsageFault 异常可能默认是禁用的。因此,如果你希望捕获所有可能的异常状况,不要忘记使能 UsageFault 异常。

启用这些异常后,你还需要实现相应的异常处理函数(例如 MemManage_Handler、BusFault_Handler 和 UsageFault_Handler),以便在异常发生时执行适当的错误处理或恢复操作。

如果使用的是 RT-Thread 或其他类似的实时操作系统,通常这些异常处理函数已经由操作系统提供,或者你可以在操作系统框架内自定义它们。

【ARMv8M Cortex-M33 系列 7.2 – HardFault 问题定位 1】 中提到了提到了通过 CFSR 寄存器可以看到UFSR 位域 INVPC 置位了,所以接下来又需要调查下,这个是个什么东东?

UFSR 位域 INVPC 中的 “Invalid PC flag. Sticky flag indicating whether an integrity check error has occurred.

在 ARM Cortex-M33 微控制器系列中,UFSR(UsageFault Status Register)是一个寄存器,其中包含了使用错误(UsageFault)的状态信息。UFSR的位域 INVPC(Invalid PC Load UsageFault)是 UFSR 寄存器的一个特定标志位,用于捕获程序计数器 (PC) 载入时的完整性检查错误。

当 INVPC 标志被设置时,它表示处理器试图执行一个无效或损坏的指令。这通常是因为 PC 包含了一个非法的地址,这可能发生在以下情况:

PC 被加载了一个未能正确对齐的地址。在 Cortex-M33 架构中,所有指令必须是 2 字节或 4 字节对齐的。如果 PC 的值不是 2 字节或 4 字节对齐的,会触发 INVPC 错误。

PC 被加载了一个不能作为指令执行的地址。例如,如果 PC 被设置为指向一个数据区域或者一个禁止执行的内存区域,将无法执行有效指令,从而导致完整性错误。

INVPC 是一个粘滞(sticky)标志,这意味着一旦设置,它将保持被设置的状态,直到软件明确清除它。这允许在调试期间或通过故障处理例程来检测到这类问题的发生。

如果在 Cortex-M33 微控制器上触发了一个 UsageFault,并且 INVPC 标志被设置,需要检查以下几点来确定问题的原因,并采取相应的解决措施:

检查程序计数器的当前值,查看它是否指向了一个有效的指令地址。
确认最近执行的指令和代码路径,检查有无可能导致 PC 被错误加载的逻辑。
审查你的代码,确保所有函数指针、中断向量和跳转地址都是有效且正确对齐的。
使用调试器进行单步执行,以更精确地定位导致 PC 载入无效地址的具体指令。
由于 INVPC 错误通常是严重的程序错误,可能需要对系统进行全面的审查和测试,以确保代码的健壮性和稳定性。
————————————————

                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
                        
原文链接:https://blog.csdn.net/sinat_32960911/article/details/135677070

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值