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


    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 GuideJLink用户使用手册重点看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_RETURNstackmodestack frame
    0xFFFFFFF1MSPHandlernon-floating-point state
    0xFFFFFFF9MSPThreadnon-floating-point state
    0xFFFFFFFDPSPThreadnon-floating-point state
    0xFFFFFFE1MSPHandlerfloating-point-state
    0xFFFFFFE9MSPThreadfloating-point state
    0xFFFFFFEDPSPThreadfloating-point state

    2.2.2. Fault

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

    2.3.1.搭建环境

    设置环境变量PATH

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

    在命令行中运行

    jlink
    
    • 1

    可以看到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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    退出

    exit #关闭电路板连接
    #或
    q #关闭JLink连接
    #或
    qc #退出jlink工具
    
    • 1
    • 2
    • 3
    • 4
    • 5

    2.3.1.常用命令整理

    命令说明备注
    hhalt
    ggo
    IsHaltedReturns the current CPU state (halted / running)
    SleepWaits the given time (in milliseconds).
    Syntax: `Sleep
    写脚本时语句间等待时间
    sSingle step the target chip
    RegsDisplay contents of registers
    wregWrite register.
    Syntax: wreg ,
    memRead memory.
    Syntax:mem [:], (hex)
    mem8Read 8-bit items.
    Syntax: mem8 [:], (hex)
    mem16Read 16-bit items.
    Syntax: mem16 [:], (hex)
    mem32Read 32-bit items.
    Syntax: mem32 [:], (hex)
    w1Write 8-bit items.
    Syntax: w1 [:], (hex)
    w2Write 16-bit items.
    Syntax: w2 [:], (hex)
    w4Write 32-bit items.
    Syntax: w4 [:], (hex)
    rReset target (RESET)
    rxReset target (RESET).
    Syntax: rx
    RSetTypeSet the current reset type.
    Syntax: RSetType
    loadfileLoad data file into target memory.
    Syntax: loadfile , []
    Supported extensions: *.bin, *.mot, *.hex, *.srec; is needed for bin files only.
    loadbinLoad *.bin file into target memory.
    Syntax: loadbin ,
    savebinSaves target memory into binary file.
    Syntax: savebin ,,
    verifybinVerfies if the specified binary is already in the target memory at the specified address.
    Syntax: verifybin ,
    SetPCSet the PC to specified value.
    Syntax: SetPC
    logEnables log to file.
    Syntax: log
    SetBPSet breakpoint.
    Syntax: SetBP [A/T] [S/H]
    ClrBPClear breakpoint.
    Syntax: ClrBP
    SetWPSet Watchpoint.
    Syntax: [R/W] [ [] [A-Mask]]
    ClrWPClear watchpoint.
    Syntax: ClrWP
    VCatchWrite vector catch.
    Syntax: VCatch
    moeShows mode-of-entry, meaning: Reason why CPU is halted
    wmWrite 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]单位
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    寄存器名称功能说明
    PC指令指针
    R0-7通用低寄存器
    R8-12通用高寄存器
    SP(R13)stack寄存器
    MSPthread和handler模式都使用MSP寄存器
    PSP没有使用
    R14(LR)返回地址寄存器
    XPSR
    APSR应用程序状态寄存器nzcvq大写字母为1,小写字母为0
    EPSR执行程序状态寄存器
    IPSR中断状态寄存器查看中断号
    CFBP
    CONTROL控制寄存器04浮点上下文打开
    thread mode使用MSP
    thread mode使用privileged
    FAULTMASK00所有中断都起作用
    BASEPRI00所有优先级中断都起作用
    PRIMASK00所有可配置中断都起作用
    FPS0-31浮点通用寄存器
    FPSCR浮点状态控制寄存器

    3.解决问题

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

    3.1.准备工程

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

    需要修改的工作如下:

    //1.根据实际电路板设置PLL时钟为正确,system_stm32f4xx.c中修改
    #define PLL_M      25
    
    • 1
    • 2
    #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
    
    • 1
    • 2
    • 3
    • 4

    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)
    		{
    			; //模拟死循环
    		}
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    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]单位
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    在这里插入图片描述

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

    在这里插入图片描述

    3.2.3.获取寄存器信息

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

    h
    
    • 1

    在这里插入图片描述
    根据PC,R14(LR)可以看出程序运行在FLASH中代码,没有死机。
    为了不破坏环境,使用另一台设备,进入在线调试模式。

    打开disassambly window

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

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

    0x800075E,0x80006CB

    可以定位到死循环位置
    在这里插入图片描述

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

    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
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    3.3.1.获取寄存器信息

    在这里插入图片描述
    根据PC寄存器定位,代码位置

    0x08000378
    
    • 1

    在这里插入图片描述
    可以看出发生了异常。

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

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

    EXC_RETURNstackmodestack frame
    0xFFFFFFF1MSPHandlernon-floating-point state
    0xFFFFFFF9MSPThreadnon-floating-point state
    0xFFFFFFFDPSPThreadnon-floating-point state
    0xFFFFFFE1MSPHandlerfloating-point-state
    0xFFFFFFE9MSPThreadfloating-point state
    0xFFFFFFEDPSPThreadfloating-point state

    3.3.2.栈内容

    根据Cortex-M4 stack frame layout 读出栈内容
    在这里插入图片描述

    MSP内容0x200006F8
    
    • 1

    在这里插入图片描述

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

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

    3.3.3.定位问题

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

    打开disassambly window

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

    输入PC寄存器地址

    0x08000732

    可以定位到产生异常的位置。
    在这里插入图片描述

    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 程序烧写的开始地址
    
    • 1
    • 2
    • 3
    • 4

    在这里插入图片描述
    尽量不要在代码中使用每次编译不一样的宏定义,例如

    __DATE__
    __TIME__
    
    • 1
    • 2
  • 相关阅读:
    java计算机毕业设计食用菌菌棒溯源系统的开发与设计源程序+mysql+系统+lw文档+远程调试
    Transformer的一点理解,附一个简单例子理解attention中的QKV
    医疗保健领域的 7 个拯救生命的 AI 用例。从早期疾病检测到增强医疗决策再到更好的患者治疗效果——这就是人工智能技术如何改变医疗保健行业。
    如何设计鞋材出库入账管理系统
    SpringBoot---------@Value,@ConfigurationProperyies以及多环境开发配置
    Log4j additivity属性简介说明
    ⑩⑦【MySQL】锁:全局锁、表级锁、行级锁
    Java自学网站--十几个网站的分析与评测
    aws msk加密方式和问控制连接方式
    枚举类型知识点
  • 原文地址:https://blog.csdn.net/fedorayang/article/details/127716679