之前介绍了了ARM异常处理(1):异常类型、优先级分组和异常向量表,里面有很多异常类型,其中有几个异常在错误处理中非常有用:
当在AHB接口上传输期间收到错误响应时,就会产生Bus fault
。它可能发生在以下几个阶段:
prefetch abort
data abort
在Cortex-M3中,出现下面几种情况也会产生Bus fault
:
stacking error
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
的内存位置。
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 |
内存管理错误可能由非法访问MPU(内存保护单元Memory Protection Unit
)或某些非法访问(如执行某些不可执行的内存区域的代码)引起。常见的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 |
FSRs
,向错误状态位写入1时将清除错误状态Usage faults
可能由以下几种情况引起:
ARM state
(可以使用这种机制来测试处理器是否支持ARM state
;Cortex-M3不支持,如果切换会发生使用错误)LR
寄存器包含无效/不正确的值)load
/store
指令通过设置NVIC的某些位,也可以产生下面两种Usage fault
:
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
会被置位。
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 | 尝试执行一个未定义的指令 |
FSRs
,向错误状态位写入1时将清除该位的错误状态如果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 引起的 |
- | - | - | - | - |