2016-07-27 131 views
2

我正試圖在我的代碼中檢索IRQ處理程序的返回地址。 我的目標是使用WDT_IRQHandler(),在看門狗定時器到期之前以及用於調試目的的復位之前保存PC的值。我也正在用其他IRQ測試這種方法,以檢查我是否掌握了這個想法。 但似乎我沒有。檢索ARM Cortex M0上的異常的返回地址

我已閱讀documentation可用。 我明白,當發生異常時,8個寄存器被推送到堆棧: R0,R1,R2,R3,R12,LR,PC和XPSR。

我也讀過堆棧自動雙字對齊。所以在我看來,檢索返回地址就像:

  • 使用__builtin_frame_address(0)檢索sp地址;
  • 增加了堆棧PC(0x18)的偏移量,並讀取值,這應該是處理程序返回時將恢復到PC的值。

使用附加的調試器進行檢查,情況似乎並非如此,該內存地址的內容並不總是指向閃存區域,甚至不指向有效區域,並且在任何情況下它都不是該值PC將在POP指令之後承擔。

該代碼工作正常,所以我認爲這是我瞭解它如何工作的問題。

如果我檢查拆卸,在一些的IRQ常數彈出(?)之前加入到SP

00001924: 0x000009b0 ...TE_IRQHandler+280 add  sp, #36 ; 0x24 
00001926: 0x0000f0bd ...TE_IRQHandler+282 pop  {r4, r5, r6, r7, pc} 

在其他的IRQ不會發生這種情況。

據我所知,可能會發生更多寄存器被推入堆棧,所以我如何確定在哪個偏移量來檢索PC?

如果我在代碼仍然在IRQ處理程序中時檢查SP周圍的內存轉儲,我可以發現返回地址,但它總是位於一個奇怪的位置,與SP相比具有負偏移量。我無法理解如何獲得正確的地址。

回答

3

你不能依靠堆棧指針的原因有兩個C的處理器的內部:

  1. 寄存器總是被推到活動堆棧被搶佔的代碼。處理程序始終使用主堆棧(MSP)。如果中斷搶佔了從進程堆棧(PSP)運行的線程模式代碼,那麼寄存器將被推送到PSP,並且您將永遠不會在處理程序堆棧中找到它們;
  2. C例程可能會爲本地變量保留一些堆棧空間,並且您不知道這是多少,所以您將無法找到寄存器。

這是怎麼了,我通常做:

void WDT_IRQHandler_real(uint32_t *sp) 
{ 
    /* PC is sp[6] (sp + 0x18) */ 
    /* ... your code ... */ 
} 

/* Cortex M3/4 */ 
__attribute__((naked)) void WDT_IRQHandler() 
{ 
    asm volatile (
     "TST LR, #4\n\t" 
     "ITE EQ\n\t" 
     "MRSEQ R0, MSP\n\t" 
     "MRSNE R0, PSP\n\t" 
     "LDR R1, =WDT_IRQHandler_real\n\t" 
     "BX R1" 
    ); 
} 

/* Cortex M0/1 */ 
__attribute__((naked)) void WDT_IRQHandler() 
{ 
    asm volatile (
     "MRS R0, MSP\n\t" 
     "MOV R1, LR\n\t" 
     "MOV R2, #4\n\t" 
     "TST R1, R2\n\t" 
     "BEQ WDT_IRQHandler_call_real\n\t" 
     "MRS R0, PSP\n" 
    "WDT_IRQHandler_call_real:\n\t" 
     "LDR R1, =WDT_IRQHandler_real\n\t" 
     "BX R1" 
    ); 
} 

這裏的竅門是,處理程序是一小片組裝的(我用GCC彙編裸體功能,您還可以使用單獨的asm文件)將堆棧指針傳遞給真正的處理程序。下面是它如何工作的(對於M3/4):

  • LR在異常處理的初始值被稱爲EXC_RETURN(更多信息here)。它的位具有不同的含義,我們感興趣的是EXC_RETURN[2]0,如果活動堆棧是MSP1(如果活動堆棧是PSP;
  • TST LR, #4檢查EXC_RETURN[2]並設置條件標誌;
  • MRSEQ R0, MSPMSP移動到R0如果EXC_RETURN[2] == 0;
  • MRSNE R0, PSPPSP移動到R0如果EXC_RETURN[2] == 1;
  • 最後,LDR/BX跳轉到實函數(R0是第一個參數)。

該M0/1變種是類似的,但使用分支自核心does not support IT blocks

這解決了問題,因爲它在任何編譯器生成的堆棧操作之前運行,它將提供一個可靠的指針。 我使用了一個簡單的(非鏈接)分支,因爲我不需要做任何事情,LR已經很好了。它節省了幾個週期和一個LR推/彈。所有使用的寄存器都在R0-R3刮傷範圍內,所以不需要保存它們。

+0

謝謝。對於點1)我想我可以使用__get_MSP()和__get_PSP()從C中檢查MSP和PSP。對於2)我知道推後代碼可以將本地變量添加到堆棧,並且這些在編譯時已知,所以我不能依賴於固定的偏移量。 我會嘗試你的方法。 – Vitomakes

+0

@Vitomakes從C中檢查'MSP'和'PSP'並不能告訴你哪一個是正確的。當然,如果你只使用'MSP'(非常常見),那麼你可以跳過檢查。 –

+1

cortex M0不支持「ITE」和「MRSEQ/MRSNE」,並且編譯器抱怨處於拇指模式並且無法使用TST。但我明白了,我會把它整理出來 – Vitomakes