Cortex-M系列处理器偶发死机定位方法

Cortex-M系列处理器偶发死机定位方法

  • 1.简介
  • 2.准备知识
    • 2.1.Cortex-M4 处理器
    • 2.2.中断
      • 2.2.1 EXC_RETURN
      • 2.2.2. Fault
    • 2.3.JLink调试器命令行方式使用
      • 2.3.1.搭建环境
      • 2.3.1.常用命令整理
      • 2.3.2.使用KLink命令行连接电路板
      • 2.3.3.寄存器解读
  • 3.解决问题
    • 3.1.准备工程
    • 3.2.模拟死循环并解决
      • 3.2.1.编译、下载、运行
      • 3.2.2.JLink连接电路板
      • 3.2.3.获取寄存器信息
    • 3.3.模拟异常情况并解决
      • 3.3.1.获取寄存器信息
      • 3.3.2.栈内容
      • 3.3.3.定位问题
    • 3.4.解决实际问题
      • 3.4.1.先使用模拟实验搭建环境
      • 3.4.2.准备一套实验环境
      • 3.4.3.保证代码一致

1.简介

处理器偶发死机时有发生,让人头疼,不知道如何下手,因为没有打印,不知道如何定位。

现在介绍一种方法,不管使用什么开发环境keil,iar,gcc等,不管使用什么系列的处理器M1,M4,M7等,原理上都是一样的。

现在用STM32F407处理器,keil开发环境为例介绍。

主要方法是:使用Jlink命令行工具对发生死机的设备读取寄存器和内存,根据反汇编定位问题。

2.准备知识

资料 说明 备注
STM32 Cortex®-M4 MCUs and MPUs programming manual 熟悉cortex-M4体系结构和指令集 对arm公司的资料进行了提取缩减
STM32F4xx英文参考手册.pdf 介绍各个功能模块 重点看flash和RAM
STM32F407_datasheet.pdf 芯片手册 重点看Memory mapping
J-Link / J-Trace User Guide JLink用户使用手册 重点看Jlink命令行操作

根据教程操作可以不熟悉上述资料,需要进一步理解,必须熟悉上述资料。

以下是总结的需要用到的知识,每个处理器不同。

2.1.Cortex-M4 处理器

处理器只有两个模式:Thread mode,Handler mode;

Thread mode平时程序运行;

Handler mode异常处理;

处理器有两个级别:Unprivileged,Privileged;

Unprivileged 保护资源不被访问;

Privileged 访问所有资源;

处理器有两个stack:Main stack,process stack

处理器运行在Handler mode强制Privileged级别, 使用Main stack;

处理器运行在Thread mode可选Unprivileged或Privileged级别,使用Main stack或process stack栈.

基于RTOS的软件实现Thread mode一般配置成: Unprivileged级别,使用process stack;使用系统调用访问硬件资源.

基于裸机的软件实现Thread mode一般配置成: Privileged级别,使用process stack;应用可以直接访问所有资源.

2.2.中断

stacking

stack frame

中断是先减,再存

2.2.1 EXC_RETURN

根据LR的值可以判断发生死机时,软件在执行的状态:

  1. LR为FLASH范围内的地址时,说明程序发生了死循环
  2. LR不为FLASHI范围内的地址时,只能为EXC_RETURN,根据EXC_RETURN可以判断进入interrupt或fault前处理器是什么模式,使用什么stack。

关于浮点单元lazy工作原理参考文章 Cortex-M FPU的Lazy Stacking机制,以前学习linux源码时,对浮点数lazy机制似是而非,现在终于明白了.

EXC_RETURN stack mode stack frame
0xFFFFFFF1 MSP Handler non-floating-point state
0xFFFFFFF9 MSP Thread non-floating-point state
0xFFFFFFFD PSP Thread non-floating-point state
0xFFFFFFE1 MSP Handler floating-point-state
0xFFFFFFE9 MSP Thread floating-point state
0xFFFFFFED PSP Thread floating-point state

2.2.2. Fault

2.3.JLink调试器命令行方式使用

2.3.1.搭建环境

设置环境变量PATH

C:\Program Files (x86)\SEGGER\JLink_V642

在命令行中运行

jlink

可以看到JLink正常功能

SEGGER J-Link Commander V6.42 (Compiled Jan 30 2019 17:51:00)
DLL version V6.42, compiled Jan 30 2019 17:50:21

Connecting to J-Link via USB...O.K.
Firmware: J-Link V11 compiled Apr 27 2041 16:36:21
Hardware version: V11.00
S/N: 941000024
License(s): GDB, JFlash, FlashDL, RDI, FlashBP
VTref=3.348V


Type "connect" to establish a target connection, '?' for help

退出

exit #关闭电路板连接
#或
q #关闭JLink连接
#或
qc #退出jlink工具

2.3.1.常用命令整理

命令 说明 备注
h halt
g go
IsHalted Returns the current CPU state (halted / running)
Sleep Waits the given time (in milliseconds).
Syntax: `Sleep
写脚本时语句间等待时间
s Single step the target chip
Regs Display contents of registers
wreg Write register.
Syntax: wreg ,
mem Read memory.
Syntax:mem [:], (hex)
mem8 Read 8-bit items.
Syntax: mem8 [:], (hex)
mem16 Read 16-bit items.
Syntax: mem16 [:], (hex)
mem32 Read 32-bit items.
Syntax: mem32 [:], (hex)
w1 Write 8-bit items.
Syntax: w1 [:], (hex)
w2 Write 16-bit items.
Syntax: w2 [:], (hex)
w4 Write 32-bit items.
Syntax: w4 [:], (hex)
r Reset target (RESET)
rx Reset target (RESET).
Syntax: rx
RSetType Set the current reset type.
Syntax: RSetType
loadfile Load data file into target memory.
Syntax: loadfile , []
Supported extensions: *.bin, *.mot, *.hex, *.srec; is needed for bin files only.
loadbin Load *.bin file into target memory.
Syntax: loadbin ,
savebin Saves target memory into binary file.
Syntax: savebin ,,
verifybin Verfies if the specified binary is already in the target memory at the specified address.
Syntax: verifybin ,
SetPC Set the PC to specified value.
Syntax: SetPC
log Enables log to file.
Syntax: log
SetBP Set breakpoint.
Syntax: SetBP [A/T] [S/H]
ClrBP Clear breakpoint.
Syntax: ClrBP
SetWP Set Watchpoint.
Syntax: [R/W] [ [] [A-Mask]]
ClrWP Clear watchpoint.
Syntax: ClrWP
VCatch Write vector catch.
Syntax: VCatch
moe Shows mode-of-entry, meaning: Reason why CPU is halted
wm Write test words.
Syntax: wm

2.3.2.使用KLink命令行连接电路板

#切换工作目录
cd E:\test\jlink_workspace
#使用jlink连接电路板
jlink -device STM32F407VG -if SWD -speed 50000 
#-device STM32F407VG 选择处理器
#-if SWD 选择调试接口
#-speed 50000 设置调试速度[kHz]单位

2.3.3.寄存器解读

使用JLink读取到的寄存器如下:

PC = 0803C38E, CycleCnt = 484513CD
R0 = 00000000, R1 = 2000A634, R2 = 00000000, R3 = 00000002
R4 = 2000A5A0, R5 = 2000A634, R6 = 08010000, R7 = AA55AA55
R8 = 00000008, R9 = FFFFFFFF, R10= 080068B0, R11= 00000000
R12= 001DD9AD
SP(R13)= 2001C3D8, MSP= 2001C3D8, PSP= 00000000, R14(LR) = 0803C385
XPSR = 01000000: APSR = nzcvq, EPSR = 01000000, IPSR = AB805800000000 (?2~?
CFBP = 04000000, CONTROL = 04, FAULTMASK = 00, BASEPRI = 00, PRIMASK = 00

FPS0 = 00000000, FPS1 = 40E00000, FPS2 = 40000000, FPS3 = 00000000
FPS4 = 00000000, FPS5 = 00000000, FPS6 = 00000000, FPS7 = 00000000
FPS8 = 00000000, FPS9 = 00000000, FPS10= 00000000, FPS11= 00000000
FPS12= 00000000, FPS13= 00000000, FPS14= 00000000, FPS15= 00000000
FPS16= 00000000, FPS17= 00000000, FPS18= 00000000, FPS19= 00000000
FPS20= 00000000, FPS21= 00000000, FPS22= 00000000, FPS23= 00000000
FPS24= 00000000, FPS25= 00000000, FPS26= 00000000, FPS27= 00000000
FPS28= 00000000, FPS29= 00000000, FPS30= 00000000, FPS31= 00000000
FPSCR= 83000010
寄存器名称 功能 说明
PC 指令指针
R0-7 通用低寄存器
R8-12 通用高寄存器
SP(R13) stack寄存器
MSP thread和handler模式都使用MSP寄存器
PSP 没有使用
R14(LR) 返回地址寄存器
XPSR
APSR 应用程序状态寄存器 nzcvq 大写字母为1,小写字母为0
EPSR 执行程序状态寄存器
IPSR 中断状态寄存器 查看中断号
CFBP
CONTROL 控制寄存器 04 浮点上下文打开
thread mode使用MSP
thread mode使用privileged
FAULTMASK 00 所有中断都起作用
BASEPRI 00 所有优先级中断都起作用
PRIMASK 00 所有可配置中断都起作用
FPS0-31 浮点通用寄存器
FPSCR 浮点状态控制寄存器

3.解决问题

准备工作完成后,就开始解决问题吧.

3.1.准备工程

为了简便直接使用正点原子的库函数版本的跑马灯例子.

需要修改的工作如下:

//1.根据实际电路板设置PLL时钟为正确,system_stm32f4xx.c中修改
#define PLL_M      25
#2.设置生成bin文件
#Options Target for "LED" => User => After Build/Rebuild
#Run1 选择,设置内容为: 
C:\Keil_v5\ARM\ARMCC\bin\fromelf.exe  --bin -o  ..\OBJ\LED.bin ..\OBJ\LED.axf

3.2.模拟死循环并解决

在实际解决问题之前模拟各种死机情况,使用命令行解决。

int main(void)
{ 
 
	delay_init(168);		  //初始化延时函数
	LED_Init();		        //初始化LED端口
	
  /**下面是通过直接操作库函数的方式实现IO控制**/	
	
	while(1)
	{
	GPIO_ResetBits(GPIOF,GPIO_Pin_9);  //LED0对应引脚GPIOF.9拉低,亮  等同LED0=0;
	GPIO_SetBits(GPIOF,GPIO_Pin_10);   //LED1对应引脚GPIOF.10拉高,灭 等同LED1=1;
	delay_ms(500);  		   //延时300ms
	GPIO_SetBits(GPIOF,GPIO_Pin_9);	   //LED0对应引脚GPIOF.0拉高,灭  等同LED0=1;
	GPIO_ResetBits(GPIOF,GPIO_Pin_10); //LED1对应引脚GPIOF.10拉低,亮 等同LED1=0;
	delay_ms(500);                     //延时300ms
		while(1)
		{
			; //模拟死循环
		}
	}
}

3.2.1.编译、下载、运行

3.2.2.JLink连接电路板

#切换工作目录
cd E:\test\jlink_workspace
#使用jlink连接电路板
jlink -device STM32F407VG -if SWD -speed 50000 
#-device STM32F407VG 选择处理器
#-if SWD 选择调试接口
#-speed 50000 设置调试速度[kHz]单位

Cortex-M系列处理器偶发死机定位方法_第1张图片

#根据提示输入 "connect"建立连接
connect

Cortex-M系列处理器偶发死机定位方法_第2张图片

3.2.3.获取寄存器信息

在命令行中输入"h"命令会显示当前寄存器信息

h

Cortex-M系列处理器偶发死机定位方法_第3张图片
根据PC,R14(LR)可以看出程序运行在FLASH中代码,没有死机。
为了不破坏环境,使用另一台设备,进入在线调试模式。

打开disassambly window

在disassambly window中右键打开"Show disassambly at address …",

分别输入PC寄存器地址和R14(LR)寄存器地址。

0x800075E,0x80006CB

可以定位到死循环位置
Cortex-M系列处理器偶发死机定位方法_第4张图片

#在真实世界中死循环可能运行一大段程序,多次运行停止可以定位到程序大概位置。
h #停止打印所有寄存器
g #全速运行
h #停止打印所有寄存器

3.3.模拟异常情况并解决

如下模拟访问非法地址。

int32_t g_u32Test[10];

int main(void)
{ 
	int32_t i;
	
	delay_init(168);		  //初始化延时函数
	LED_Init();		        //初始化LED端口
	
  /**下面是通过直接操作库函数的方式实现IO控制**/	
	
	while(1)
	{
		for(i=0;i<100000000;i++)
		{
			g_u32Test[i]=i; //访问非法地址
		}
	GPIO_ResetBits(GPIOF,GPIO_Pin_9);  //LED0对应引脚GPIOF.9拉低,亮  等同LED0=0;
	GPIO_SetBits(GPIOF,GPIO_Pin_10);   //LED1对应引脚GPIOF.10拉高,灭 等同LED1=1;
	delay_ms(500);  		   //延时300ms
	GPIO_SetBits(GPIOF,GPIO_Pin_9);	   //LED0对应引脚GPIOF.0拉高,灭  等同LED0=1;
	GPIO_ResetBits(GPIOF,GPIO_Pin_10); //LED1对应引脚GPIOF.10拉低,亮 等同LED1=0;
	delay_ms(500);                     //延时300ms
	}
}

3.3.1.获取寄存器信息

Cortex-M系列处理器偶发死机定位方法_第5张图片
根据PC寄存器定位,代码位置

0x08000378

Cortex-M系列处理器偶发死机定位方法_第6张图片
可以看出发生了异常。

根据R14(LR)寄存器"0xFFFFFFE9"查找EXC_RETURN码表

可以知道stack frame存储在MSP指向的stack中,程序从thread模式进入异常,stack frame中没有浮点寄存器。

EXC_RETURN stack mode stack frame
0xFFFFFFF1 MSP Handler non-floating-point state
0xFFFFFFF9 MSP Thread non-floating-point state
0xFFFFFFFD PSP Thread non-floating-point state
0xFFFFFFE1 MSP Handler floating-point-state
0xFFFFFFE9 MSP Thread floating-point state
0xFFFFFFED PSP Thread floating-point state

3.3.2.栈内容

根据Cortex-M4 stack frame layout 读出栈内容
Cortex-M系列处理器偶发死机定位方法_第7张图片

MSP内容0x200006F8

在这里插入图片描述

mem32 0x200006F8 8
200006F8 = 05F5E100 00000600 00000600 01000301
			R0        R1        R2       R3
20000708 = 2000013C 080003B7 08000732 01000000
			 R12      LR        PC      xPSR

可以看出发生异常前PC寄存器地址是:0x08000732

3.3.3.定位问题

为了不破坏环境,使用另一台设备,进入在线调试模式。

打开disassambly window

在disassambly window中右键打开"Show disassambly at address …",

输入PC寄存器地址

0x08000732

可以定位到产生异常的位置。
Cortex-M系列处理器偶发死机定位方法_第8张图片

3.4.解决实际问题

根据以上的模拟实验,解决实际问题时就比较简单了,这里有几个注意事项.

3.4.1.先使用模拟实验搭建环境

偶发死机很难复现,为了不破坏环境,最好使用另外一套设备模拟一遍。

3.4.2.准备一套实验环境

抓取信息后,需要到实验环境中进入在线调试模式定位代码,保持问题设备维持现状,能继续观测调试。

3.4.3.保证代码一致

代码不一致,读取的寄存器信息,stack信息,flash地址信息和C语言代码对应不上,没法定位。

需要保证三点:1.保证代码一致;2.保证编译器版本和编译器选项一致;3.保证使用的固件库,依赖库一致。

可以在有问题的设备上验证程序是否一致。

把编译生成LED.bin文件复制到JLink工作目录E:\test\jlink_workspace下。

使用命令验证程序是否一致

verifybin LED.bin, 0x8000000
#verifybin 验证命令
#LED.bin 程序二进制文件
#0x8000000 程序烧写的开始地址

Cortex-M系列处理器偶发死机定位方法_第9张图片
尽量不要在代码中使用每次编译不一样的宏定义,例如

__DATE__
__TIME__

你可能感兴趣的:(cortex-M,单片机,stm32,嵌入式硬件)