1、Hard fault中断
Cortex-M内核具有一个不可以关闭的硬件错误中断(Hard fault),这个中断的功能就是当内核运行到了错误的代码会导致产生硬件故障,进而引发中断,用于指示程序出现了重大不可修复的异常,需要程序员去重点关注与处理。本文以结合实际的故障代码来带你分析一下硬件故障产生的种类及如何根据异常的现象寄存器信息来分析找到出现故障问题的代码位置。
2、Hard fault的种类
产生硬件故障的原因有,除法的除数为0、总线错误(非法的内存地址访问),非对齐访问故障,无效的PC指针等。这些故障是编写程序时经常会遇到代码编写错误产生的故障。还有一些其他故障,如指令总线错误,数据总线错误,执行未定义的指令等,这些一般由于芯片故障产生。下面结合实际代码来展示各种故障现象,以及从故障现场的寄存器信息查找到出现故障的代码。
3、Hard fault触发与分析
这次测试的代码通过人为编写代码来进行触发故障中断,代码基于rt thread 操作系统运行,基于cortex-m3内核的STM32F207VE处理器运行,代码由于操作系统的命令行线程tshell来进行调用。下面是触发故障中断的代码。
int cmb_test(int argc, char **argv) {
volatile int * SCB_CCR = (volatile int *) 0xE000ED14; // SCB->CCR
int x, y, z;
if (argc < 2)
{
rt_kprintf("Please input 'cmb_test <DIVBYZERO|UNALIGNED>' \n");
return 0;
}
/*除法除数为0硬件故障*/
if (!strcmp(argv[1], "DIVBYZERO"))
{
*SCB_CCR |= (1 << 4); /* bit4: DIV_0_TRP. */
x = 10;
y = rt_strlen("");
z = x / y;
rt_kprintf("z:%d\n", z);
return 0;
}
/*非对齐访问故障*/
else if (!strcmp(argv[1], "UNALIGNED"))
{
volatile int * p;
volatile int value;
*SCB_CCR |= (1 << 3); /* bit3: UNALIGN_TRP. */
p = (int *) 0x00;
value = *p;
rt_kprintf("addr:0x%02X value:0x%08X\r\n", (int) p, value);
p = (int *) 0x04;
value = *p;
rt_kprintf("addr:0x%02X value:0x%08X\r\n", (int) p, value);
p = (int *) 0x03;
value = *p;
rt_kprintf("addr:0x%02X value:0x%08X\r\n", (int) p, value);
return 0;
}
/*无效的PC指针故障*/
else if(!strcmp(argv[1], "INVSTATE"))
{
typedef void (*pFunction)(void);
pFunction func;
func = (pFunction)0x0;
func();
}
/*访问非法地址导致的总线异常*/
else if(!strcmp(argv[1], "BUSFAULT"))
{
int *p = (int *)0xDFFF0000;
x = *p;
}
return 0;
}
3.1 除法除数为0故障
在串口命令行接口,执行cmb_test DIVBYZERO, 执行上面的除法操作,芯片产生硬件故障中断,打印中断前的寄存器信息,如下图,可以看出故障发生中tshell线程中,故障类型为除法除数为0故障,发生故障的时的PC指针为0x0800509c, LR为0x0800509b。
根据PC值在keil软件中找到出现异常的代码位置,通过汇编语言窗口查看,如下图,正好发生中执行除法的位置。
此时LR进入异常中断前的返回地址,即记录的是z=x/y上一条代码的地址,就是调用完strlen函数后的返回地址。
3.2 非对齐访问故障
在串口命令行接口,执行cmb_test UNALIGNED, 执行上面的非对齐内存访问操作,芯片产生硬件故障中断,打印中断前的寄存器信息,如下图,可以看出故障发生中tshell线程中,故障类型为非对齐访问故障,发生故障的时的PC指针为0x080050e2, LR为0x080060dd。
LR存储的是返回地址0x080060dd,这个地址通过查看代码是串口命令行发送数据完成后的地址,为什么是这里呢?因为执行非对齐访问的代码为value=*p,这个LR实际记录的是在value=*p这句代码上面执行的函数的调用的后的返回地址,从代码可以看出value=*p上面的一个函数就是rt_kprintf("addr:0x%02X value:0x%08X\r\n", (int) p, value),这一句,位于85行。
3.3 无效的PC指针故障
在串口命令行接口,执行cmb_test INVSTATE, 执行上面的对PC赋值非法,并进行执行操作,芯片产生硬件故障中断,打印中断前的寄存器信息,如下图,可以看出故障发生中tshell线程中,故障类型为非法的PC故障,发生故障的时的PC指针为0x00, LR为0x08005103。
LR存储的是函数调用后的返回地址,一般就是原来程序调用函数地址的下一地址,在这段故障代码中就是func()执行后的下一地址。由于发生故障时的PC指针是人为设置为0,所以引址故障的PC地址是0,而故障代码发生的位置是0x080050FE的位置就是执行func()的位置。
3.4 数据总线故障
在串口命令行接口,执行cmb_test INVSTATE, 执行上面的对PC赋值非法,并进行执行操作,芯片产生硬件故障中断,打印中断前的寄存器信息,如下图,可以看出故障发生中tshell线程中,故障类型为非法的PC故障,发生故障的时的PC指针为0x08005110, LR为0x0800510D。