stack contents我已经编写了一个简单的IO中断例程来测试ARM cortex m4(cm408F)中的IO引脚。代码在下面并且非常简单并填充向量表(还包括编译指示弱和其他东西)。
我通过设置NVIC_ISER0和NVIC_ISPR0中的相应位强制中断。发出时刻中断,处理器给出了以下硬错误异常,并卡在L1引导ROM的循环中。
处理器已将可配置优先级异常升级为HardFault。使用无效的EPSR.T或EPSR.IT字段(CFSR.INVSTATE)执行的指令。在PC = 0xffffffff,LR = 0x0时发生异常
在调用堆栈窗口中,我看到:
__iar_systems$$modulde + 0x1451
这有用吗?
我还为HardFault_Handler添加了while(1)循环。因此,如果处理器实际上断言HardFault_Handler它应该进入这个无限循环,但它永远不会去那里。无论哪个中断被激活(通过NVIC_ISER0和NVIC_ISPR0)都会发生同样的问题(接收到中断后它会跳转并卡在L1引导ROM线路中:1452!)。
一旦引发中断,我附加了堆栈快照。在发出中断之前,我已经改变了R12和R0-R3(0x1238 ...... 0x1234)的内容,以便在堆栈中更好地实现它们。正如我所说,当中断被提出时,程序永远不会返回,所以我暂停了它并查看了堆栈(附图)。似乎第一次推进很好;我们可以看到xPSR,PC,LR,R12,R0到R3都正确堆叠(FPU被禁用)。但在第二次推入堆栈PC是零(LR很好)!我想这表明了问题。 PC不应该为零。通过将正确的返回地址推送到PC,它为什么不从中断返回。我想第三次进入堆栈是这个问题的结果。
中断前:SP = 0x2005FFF0中断后:SP = 0x2005FFA4
............................
// My code is very simple as follows.
// main.c
#include <intrinsics.h>
int main()
{
int k1=123;
k1=k1+2*k1;
while(1)
{
k1=k1;
}
return 0;
}
// =========================================
// my_int_Routines.c
void PINT0_BLOCK_Int_Handler(void)
{
while(1)
{
asm("nop");
}
}
void PINT1_BLOCK_Int_Handler(void)
{
while(1)
{
asm("nop");
}
}
void PINT2_BLOCK_Int_Handler(void)
{
while(1)
{
asm("nop");
}
}
void PINT3_BLOCK_Int_Handler(void)
{
while(1)
{
asm("nop");
}
}
void PINT4_BLOCK_Int_Handler(void)
{
while(1)
{
asm("nop");
}
}
// =====================================
// my_startup.c
// This is ARM standard cstartup.c in IAR folder. I only added the relevant lines
// (marked as Reza)
/**************************************************
*
* This file contains an interrupt vector for Cortex-M written in C.
* The actual interrupt functions must be provided by the application developer.
*
* Copyright 2007-2017 IAR Systems AB.
*
* $Revision: 112610 $
*
**************************************************/
#pragma language=extended
#pragma segment="CSTACK"
extern void __iar_program_start( void );
extern void NMI_Handler( void );
extern void HardFault_Handler( void );
extern void MemManage_Handler( void );
extern void BusFault_Handler( void );
extern void UsageFault_Handler( void );
extern void SVC_Handler( void );
extern void DebugMon_Handler( void );
extern void PendSV_Handler( void );
extern void SysTick_Handler( void );
extern void PINT0_BLOCK_Int_Handler(void); // 18 Pin Interrupt Block Reza
extern void PINT1_BLOCK_Int_Handler(void); // 19 Pin Interrupt Block Reza
extern void PINT2_BLOCK_Int_Handler(void); // 20 Pin Interrupt Block Reza
extern void PINT3_BLOCK_Int_Handler(void); // 21 Pin Interrupt Block Reza
extern void PINT4_BLOCK_Int_Handler(void); // 22 Pin Interrupt Block Reza
typedef void( *intfunc )( void );
typedef union { intfunc __fun; void * __ptr; } intvec_elem;
// The vector table is normally located at address 0.
// When debugging in RAM, it can be located in RAM, aligned to at least 2^6.
// If you need to define interrupt service routines,
// make a copy of this file and include it in your project.
// The name "__vector_table" has special meaning for C-SPY, which
// is where to find the SP start value.
// If vector table is not located at address 0, the user has to initialize
// the NVIC vector table register (VTOR) before using interrupts.
#pragma location = ".intvec"
const intvec_elem __vector_table[] =
{
{ .__ptr = __sfe( "CSTACK" ) },
__iar_program_start,
NMI_Handler,
HardFault_Handler,
MemManage_Handler,
BusFault_Handler,
UsageFault_Handler,
0,
0,
0,
0,
SVC_Handler,
DebugMon_Handler,
0,
PendSV_Handler,
SysTick_Handler,
// ******* Reza (all zeros below)
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
PINT0_BLOCK_Int_Handler, // 18 Pin Interrupt Block Reza
PINT1_BLOCK_Int_Handler, // 19 Pin Interrupt Block Reza
PINT2_BLOCK_Int_Handler, // 20 Pin Interrupt Block Reza
PINT3_BLOCK_Int_Handler, // 21 Pin Interrupt Block Reza
PINT4_BLOCK_Int_Handler // 22 Pin Interrupt Block Reza
};
#pragma call_graph_root = "interrupt"
__weak void NMI_Handler( void ) { while (1) {} }
#pragma call_graph_root = "interrupt"
__weak void HardFault_Handler( void ) { while (1) {} }
#pragma call_graph_root = "interrupt"
__weak void MemManage_Handler( void ) { while (1) {} }
#pragma call_graph_root = "interrupt"
__weak void BusFault_Handler( void ) { while (1) {} }
#pragma call_graph_root = "interrupt"
__weak void UsageFault_Handler( void ) { while (1) {} }
#pragma call_graph_root = "interrupt"
__weak void SVC_Handler( void ) { while (1) {} }
#pragma call_graph_root = "interrupt"
__weak void DebugMon_Handler( void ) { while (1) {} }
#pragma call_graph_root = "interrupt"
__weak void PendSV_Handler( void ) { while (1) {} }
#pragma call_graph_root = "interrupt"
__weak void SysTick_Handler( void ) { while (1) {} }
// ====================== Reza
#pragma call_graph_root = "interrupt"
__weak void PINT0_BLOCK_Int_Handler( void ) { while (1) {} }
#pragma call_graph_root = "interrupt"
__weak void PINT1_BLOCK_Int_Handler( void ) { while (1) {} }
#pragma call_graph_root = "interrupt"
__weak void PINT2_BLOCK_Int_Handler( void ) { while (1) {} }
#pragma call_graph_root = "interrupt"
__weak void PINT3_BLOCK_Int_Handler( void ) { while (1) {} }
#pragma call_graph_root = "interrupt"
__weak void PINT4_BLOCK_Int_Handler( void ) { while (1) {} }
void __cmain( void );
__weak void __iar_init_core( void );
__weak void __iar_init_vfp( void );
#pragma required=__vector_table
void __iar_program_start( void )
{
__iar_init_core();
__iar_init_vfp();
__cmain();
}
在Cortex-M中,PC实际上不可能是0xffffffff,寄存器中只有物理位[31:1]。当您观察到地址为0xfffffffe的执行时,这是一个'LOCKUP'地址,这是一个archtectural livelock状态(是一个无效的提取地址,它强制PC到锁定地址,这是一个无效的提取地址)。
一旦处于锁定地址的锁定状态,唯一的出路是使用调试器来更改PC或重置核心。
要调试Cortex-M锁定方案,查看堆栈很重要,但您无法确定上次成功堆叠的内容。您也无法确定故障的顺序,但有一个合理的假设是在故障处理程序期间发生了故障(LOCKUP意味着异常模型超出了其他选项)。
首先要检查的是这些堆叠的PC值与代码有什么关系,以及与您尝试触发ISR相关的位置。也许你的main()已经返回(它什么也没做),这个0x1452是一个UNDEF
。工具链有3个选项,用于'after main()',循环永久,崩溃,或者只是继续执行任意指令。除非您拆卸/逐步浏览图像,否则这不会很明显。
我指的是规范,http://infocenter.arm.com/help/topic/com.arm.doc.ddi0439b/DDI0439B_cortex_m4_r0p0_trm.pdf
这里提到矢量表的默认位置是0x00000000,它必须在VTOR寄存器中提到。如果该寄存器未被修改,则在获得中断后,CPU将读取LOC1的存储器地址:0x00000000 +对应于中断号的某个偏移量,并将跳转到LOC1。
现在我的猜测是,ISR未正确定位且LOC1未正确设置,而是包含一些垃圾值0x00001452。现在CPU读取并跳转到该位置。
我想你可以解决这个问题,
以这样的方式配置链接器,使ISR位于正确的位置,并且LOC1将填充适当的值。此外,您可能需要使用某个自定义值配置VTOR寄存器。