簡體   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