繁体   English   中英

查找在cortex-m4上发生中断的位置

[英]find where the interrupt happened on cortex-m4

我试图找到我的代码中发生了特定的中断。 在这种情况下,它在stm32f4微控制器上,并且中断是SysTick_Handler。

我想要的基本上是弄清楚从哪里发生了systick中断。 我正在使用arm-none-eabi-gdb尝试查找回溯,但是我从那里得到的唯一信息是:

(gdb) bt
#0  SysTick_Handler () at modules/profiling.c:66
#1  <signal handler called>
#2  0x55555554 in ?? () Backtrace stopped: previous frame identical to this frame (corrupt stack?)

如何获得有关中断触发之前程序所在位置的信息?

这里查看 arm文档,看来我应该能够读取堆栈指针,并从那里获取PC。 但这就是GDB的取消器正在执行的工作吗?

问题的结尾您走在正确的轨道上。 ARM Cortex-M内核具有两个堆栈指针,主堆栈指针(MSP,用于中断)和进程堆栈指针(PSP,用于任务)。

当具有优先级的中断进入时,当前寄存器值(用于大多数寄存器)被压入当前堆栈(如果中断后台应用程序,则为PSP;如果中断较低优先级的中断,则为MSP),然后将堆栈切换到MSP(如果尚未存在)。

首次输入中断时,链接寄存器(LR,返回地址)将具有一个大多数为F的值,而不是实际的返回地址。 该值告诉内核分支时如何退出。 通常情况下,你会看到的值0xFFFFFFFD如果后台任务被中断,或0xFFFFFFF1如果一个低优先级的中断被中断。 如果使用浮点单位,这些值将有所不同。 不过,此值的神奇之处在于,位2( 0x4 )告诉您堆栈帧是在PSP还是MSP上。

一旦确定了框架所在的堆栈,就可以通过查看适当的堆栈指针减24(6个32位位置)来找到执行地址。 请参见链接中的图2.3。 这会将您指向被中断的PC。

我们一直以各种形式看到这个问题,人们一直说有两个堆栈。 所以我自己用操纵杆尝试了一下。

该文档说我们处于退出重置的线程模式,如果您用openocd暂停,它表示

target halted due to debug-request, current mode: Thread 

我有一些转储寄存器的代码:

20000000 APSR
00000000 IPSR
00000000 EPSR
00000000 CONTROL
00000000 SP_PROCESS
20000D00 SP_PROCESS after I modified it
20000FF0 SP_MAIN
20000FF0 mov r0,sp  
then I dump the stack up to 0x20001000 which is where I know my stack started
20000FF0 00000000 
20000FF4 00000000 
20000FF8 00000000 
20000FFC 0100005F 

我设置并等待一个systick中断,处理程序转储寄存器和ram,然后进入无限循环。 通常是不好的做法,但这里只是调试/学习。 在中断之前,我准备了一些寄存器:

.thumb_func
.globl iwait
iwait:
    mov r0,#1
    mov r1,#2
    mov r2,#3
    mov r3,#4
    mov r4,#13
    mov r12,r4
    mov r4,#15
    mov r14,r4
    b .

在处理程序中,我看到

20000000 APSR
0000000F IPSR
00000000 EPSR
00000000 CONTROL
20000D00 SP_PROCESS
20000FC0 SP_MAIN
20000FC0 mov r0,sp
20000FC0 0000000F 
20000FC4 20000FFF 
20000FC8 00000000 
20000FCC FFFFFFF9  this is our special lr (not one rjp mentioned)
20000FD0 00000001  this is r0
20000FD4 00000002  this is r1
20000FD8 00000003  this is r2
20000FDC 00000004  this is r3
20000FE0 0000000D  this is r12
20000FE4 0000000F  this is r14/lr
20000FE8 01000074  and this is where we were interrupted from
20000FEC 21000000  this is probably the xpsr mentioned
20000FF0 00000000  stuff that was there before
20000FF4 00000000 
20000FF8 00000000 
20000FFC 0100005F 


01000064 <iwait>:
 1000064:   2001        movs    r0, #1
 1000066:   2102        movs    r1, #2
 1000068:   2203        movs    r2, #3
 100006a:   2304        movs    r3, #4
 100006c:   240d        movs    r4, #13
 100006e:   46a4        mov ip, r4
 1000070:   240f        movs    r4, #15
 1000072:   46a6        mov lr, r4
 1000074:   e7fe        b.n 1000074 <iwait+0x10>
 1000076:   bf00        nop

因此,在这种情况下,出于ARM文档的考虑,它没有使用sp_process,而是使用了sp_main。 它正在推送手册说正在推送的项目,包括中断/返回地址0x1000074。

现在,如果我将SPSEL位置1(请小心先设置PSP),则看来在应用程序/线程模式下的mov r0,sp使用的是PSP而不是MSP。 但随后处理程序将msp用于mov r0,sp,但似乎将

在线程/前景之前

20000000 APSR
00000000 IPSR
00000000 EPSR
00000000 SP_PROCESS
20000D00 SP_PROCESS modified
00000000 CONTROL
00000002 CONTROL modified
20000FF0 SP_MAIN
20000D00 mov r0,sp

现在在处理程序中

20000000 APSR
0000000F IPSR
00000000 EPSR
00000000 CONTROL (interesting!)
20000CE0 SP_PROCESS
20000FE0 SP_MAIN
20000FE0 mov r0,sp
dump of that stack
20000FE0 0000000F 
20000FE4 20000CFF 
20000FE8 00000000 
20000FEC FFFFFFFD 
20000FF0 00000000 
20000FF4 00000000 
20000FF8 00000000 
20000FFC 0100005F 
dump of sp_process stack
20000CE0 00000001 
20000CE4 00000002 
20000CE8 00000003 
20000CEC 00000004 
20000CF0 0000000D 
20000CF4 0000000F 
20000CF8 01000074 our return value
20000CFC 21000000 

因此,要处在人们不断提及的替代堆栈的位置,您必须将自己置于该位置(或您所依赖的某些代码)。 为什么要对简单的裸机程序执行此操作,谁知道,全零的控制寄存器又好又容易,可以共享一个堆栈。

我不使用gdb,但是您需要获取它以转储所有寄存器sp_process和sp_main,然后根据找到的内容转储,然后在每个处转储十几个单词,在那里您应该看到0xFFFFFFFx作为标记,然后从看到寄信人地址。 您也可以让处理程序读取两个堆栈指针,然后再查看gprs。 使用gnu汇编程序mrs rX,psp; rX,msp先生; 用于进程和主堆栈的指针。

正如你们中许多人所评论的那样,PC将放在两个不同的堆栈中,而我解决它的方法是,在组装中实际找到HardFault_Handling代码,然后从那里获取所需信息。 为了正确获得PC值,我使用以下代码。

register int *r0 __asm("r0");

__asm(  "TST lr, #4\n"
        "ITE EQ\n"
        "MRSEQ r0, MSP\n"
        "MRSNE r0, PSP\n" // stack pointer now in r0
        "ldr r0, [r0, #0x18]\n" // stored pc now in r0
        //"add r0, r0, #6\n" // address to stored pc now in r0
     );

现在可以通过以下方式访问发生中断的位置的值:

uint32_t PC = *r0;

现在可以用于我想要的任何东西。 不幸的是,我没有设法让GDB为我自动释放堆栈。 但是至少我发现了中断在哪里触发,这就是目标。

这称为DEBUGGING。 最简单的入门方法是在整个代码中到处粘贴一堆printf()调用。 运行程序。 如果打印出来:

指向A点
到了B点
指向C点

死了,那么你知道它死在“ C”和“ D”之间。 现在,您可以通过使用间距更近的printf()调用“ C”和“ D”之间的代码来向下精简。

这是初学者入门的最佳方法。 许多经验丰富的专家还喜欢使用printf()进行调试。 调试器可能会妨碍您。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM