Cortex-M 处理器 hardfault 定位方法和步骤(基于Keil mdk)

一. 问题的产生

  Hard fault (硬错误,也有译为硬件错误的)是在STM32上编写程序中所产生的错误,造成Hard Fault错误的可能原因较多,排除硬件问题,如何在代码量较大的情况下,快速定位造成的hardfault的问题代码,就成为比较关键的问题。

  本文将基于STM32处理器(stm32f091),keil-MDK开发环境,总结hardfault的调试定位方法。在其他Cortex-M0 (m3,m4)内核处理器,和其他开发环境下,也可作为参考。

二. Cortex-M 处理器内核异常中断简介

1)错误种类

对于Cortex-M内核,架构采用错误异常的机制来检测问题,当核心检测到一个错误时,异常中断会被触发,并且核心会跳转到相应的异常终端处理函数执行,错误异常的终端分为以下四种:

HardFault
MemManage
BusFault
UsageFault

其中hardfault为最常见的错误类型,并且,在没有开启其他异常处理的情况下,默认进入hardfault异常中断处理函数:

void HardFault_Handler(void)
{
  /* USER CODE BEGIN HardFault_IRQn 0 */
  /* USER CODE END HardFault_IRQn 0 */
  while (1)
  {
    /* USER CODE BEGIN W1_HardFault_IRQn 0 */
    /* USER CODE END W1_HardFault_IRQn 0 */
  }
}

2) 可能的原因

从软件角度,产生hardfault的可能原因有:

(1) 数组越界
(2)野指针
(3)未初始化硬件却开始操作,或无中断服务函数等

(4)任务堆栈溢出

《ARM Cortex-M0权威指南》中提到,关于 Cortex M0+内核主要有以下几点引起 HardFault 的原因:


非法存储器访问
非对齐数据访问
从总线返回错误
异常处理中的栈被破坏
程序在某些 C 函数中崩溃
意外地试图切换至 ARM 状态
在错误的优先级上执行系统服务调用指令(SVC)

下面将以一个stm32f091上运行的数组越界代码为例,具体阐述定位步骤:

产生越界的代码段:

void StackFlow(void)
{
 
    int a[3],i;
 
    for(i=0; i<10000; i++)
    {
 
    a[i]=1;
 
    }
}

三. 人工查找hardfault 方法和步骤

方法1.查看寄存器

1)查看fault种类

通过菜单栏Peripherals >Core Peripherals >Fault Reports打开fault reports
这里写图片描述

2)调试定位步骤

查看fault种类有时可能对解决问题并没有直接帮助,关键是如何定位在进入异常中断前执行的代码段,步骤如下:

a. 确定当前使用堆栈是MSP还是PSP

异常发生后会把进入异常前的 R0-R3,R12, LR, PC,PSR 寄存器值栈入 Main Stack 或Process Stack(取决于异常发生时使用的哪个栈)。 进入异常后链接寄存器 LR 中存放异常返回值 EXC_RETURN, 如果其 bit 2=0 那么用的就是 Main Stack,如果 bit 2=1,那么用的就是 Process Stack。

 

由下图可以看出,当前使用的堆栈为PSP 

b.找到异常发生代码地址

在memory中,定位到堆栈地址:0x200020E0,依据:R0~R3、R12、LR、PC、XPRS 顺序,找到LR的值,即第6个寄存器值

LR = 0x0800632B

PC = 0x08006300

c.Disassembly中,查找定位代码

在反汇编窗口中点击右键,选中show disassembly at address 。

输入LR地址:为发生异常后调用的下一条指令的地址,可看到发生异常的为StackFlow()函数

 输入PC地址:可以定位到发生异常的调用语句

方法2:调试步骤
在仿真状态下,调出Call Stack Window,可直接跳转到调用代码

四. 程序查找hardfault 方法和步骤

  实际环境中,由于测试时产品常常无法连接调试器,故需要代码来定位目标语句地址,并通过一定手段保存:

在stm32中,需先修改启动文件startup_stm32f091xc.s:

HardFault_Handler\			
				MOVS r0, #4
				MOV r1, LR
				TST r0, r1
				BEQ stacking_used_MSP
				MRS R0, PSP
				B get_LR_and_branch
stacking_used_MSP
				MRS R0, MSP
get_LR_and_branch
				MOV R1, LR
				IMPORT  hard_fault_handler_c
				BL hard_fault_handler_c

该段代码会判断当前堆栈使用的是MSP或PSP,然后将堆栈参数传递给hard_fault_handler_c函数,该函数定义如下:

void hard_fault_handler_c(unsigned int * hardfault_args, unsigned lr_value)
{
			unsigned int stacked_r0;
			unsigned int stacked_r1;
			unsigned int stacked_r2;
			unsigned int stacked_r3;
			unsigned int stacked_r12;
			unsigned int stacked_lr;
			unsigned int stacked_pc;
			unsigned int stacked_psr;
			stacked_r0 = ((unsigned long) hardfault_args[0]);
			stacked_r1 = ((unsigned long) hardfault_args[1]);
			stacked_r2 = ((unsigned long) hardfault_args[2]);
			stacked_r3 = ((unsigned long) hardfault_args[3]);
			stacked_r12 = ((unsigned long) hardfault_args[4]);
			stacked_lr = ((unsigned long) hardfault_args[5]);
			stacked_pc = ((unsigned long) hardfault_args[6]);
			stacked_psr = ((unsigned long) hardfault_args[7]);
			while(1)
			{
                printf ("[Hard fault handler]\n");
			printf ("R0 = %x\n", stacked_r0);
			printf ("R1 = %x\n", stacked_r1);
			printf ("R2 = %x\n", stacked_r2);
			printf ("R3 = %x\n", stacked_r3);
			printf ("R12 = %x\n", stacked_r12);
			printf ("Stacked LR = %x\n", stacked_lr);
			printf ("Stacked PC = %x\n", stacked_pc);
			printf ("Stacked PSR = %x\n", stacked_psr);
			printf ("Current LR = %x\n", lr_value);
 
			for(int i = 10000;i>0;i--)
			    for(int j = 1000;j>0;j--);
			}
			
			//while(1); // endless loop
}

代码跑死后,会将R0~R3、R12、LR、PC信息通过串口打印,之后根据寄存器信息排查问题代码即可~

参考资料:

(1)https://blog.csdn.net/geek_liyang/article/details/83510518

(2)https://blog.csdn.net/Maple_Leaf_15/article/details/51443310

(3)https://www.freertos.org/Debugging-Hard-Faults-On-Cortex-M-Microcontrollers.html

(4)https://blog.csdn.net/Wang_yf_/article/details/75003611

(5)http://blog.sina.com.cn/s/blog_908da74601011g31.html

(6)https://blog.csdn.net/guozhongwei1/article/details/88418760

(7)https://community.nxp.com/thread/306244

(8)http://www.keil.com/appnotes/files/apnt209.pdf

(9)《ARM Cortex-M0权威指南》Joseph Yiu 著,宋岩 译

  • 5
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
基于ARM Cortex-A8处理器的开发设计可以广泛应用于多种领域,如智能手机、平板电脑、嵌入式系统等。ARM Cortex-A8是ARM公司推出的一款高性能、低功耗的处理器核心。 首先,在ARM Cortex-A8处理器的开发设计中,需要深入了解处理器的架构和指令集。Cortex-A8基于ARMv7架构,采用了流水线、超标量设计,具备高性能和高吞吐量的特点。它支持32位和64位操作模式,具有丰富的指令集,可以运行多种操作系统,如Android、Linux等。 其次,开发设计过程中需要熟悉ARM嵌入式开发环境。ARM公司提供了一系列工具和软件包,如ARM Development Studio、ARM Keil MDK等,用于开发、调试和优化ARM Cortex-A8处理器的软件。同时,还需要了解ARM架构相关的编程语言和开发工具链,如C/C++语言、GCC编译器等。 在ARM Cortex-A8处理器的开发设计中,还需要考虑功耗和性能优化。Cortex-A8具有较低的功耗特性,但在实际应用中,为了提高处理器的性能和效率,需要进行功耗优化和性能优化。例如,可以采用功耗管理机制、优化算法和数据结构等方法,来实现功耗和性能的平衡。 此外,安全性和可靠性也是ARM Cortex-A8处理器开发设计中需要关注的重要方面。针对不同的应用场景,可以采取相应的安全措施和防护机制,如加密算法、访问控制等,保护系统和数据的安全。 综上所述,基于ARM Cortex-A8处理器的开发设计需要深入了解处理器架构和指令集,熟悉ARM嵌入式开发环境,考虑功耗和性能优化,并关注安全性和可靠性。通过合理的设计和优化,可以实现高性能、低功耗的嵌入式系统。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值