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

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 registerBUSFAULTENA位,在此之前,需要保证在中断向量表中设置了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处,它的字段如下:

  • 访问该寄存器可以按字节访问0xE000ED29,也可以按字访问0xE000ED28后得到第二个字节
  • 向错误指示位写入1时将清除该位的状态

2 Memory Management Fault

内存管理错误可能由非法访问MPU(内存保护单元Memory Protection Unit)或某些非法访问(如执行某些不可执行的内存区域的代码)引起。常见的MPU错误如下:

  • 访问MPU中未定义的内存区域
  • 往只读区域写入数据
  • 用户状态下访问特权模式下才能访问的内存

内存管理错误处理函数与Bus fault一样,有一样的特点,这里不再重复介绍。使能内存管理错误的处理函数,需要设置NVIC中的System Handler Control and State registerMEMFAULTENA位。

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

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

  • 访问该寄存器可以按字节或按字访问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 registerUSGFAULTENA位。

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


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

  • 最低位LSB为1表示使用Thumb state

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

  • 访问该寄存器可以按字节访问0xE000ED2A,也可以按字访问0xE000ED28后得到其最高字节
  • 对于其它FSRs,向错误状态位写入1时将清除该位的错误状态

4 Hard Faults

如果Usage faultsBus faultsMemory 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,其字段如下:

12-06 02:42