2016-07-27 127 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