2016-08-02 2 views
-1

코드에서 특정 인터럽트가 발생한 위치를 찾으려고합니다. 이 경우에는 stm32f4 마이크로 컨트롤러에 있고 인터럽트는 SysTick_Handler입니다.cortex-m4에서 인터럽트가 발생한 곳을 찾으십시오.

내가 원하는 것은 기본적으로 시스템 인터럽트가 발생한 곳을 파악하는 것입니다. 나는 역 추적을 찾으려고 팔 - 없음 - 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?) 

어떻게 인터럽트 해고하기 전에 프로그램이 어디에 있는지에 대한 정보를 얻을 수 있습니다 ?

무기 문서 here을 보면 스택 포인터를 읽고 거기에서 PC를 가져올 수 있어야합니다. 그렇다면 이것이 바로 GDB의 unwinders가하는 일입니다.

+0

중단 된 코드가 메인 스택과 함께 실행 되었습니까? 프로세스 스택에서 실행 중이면 잘못된 핸들을 풀고 있습니다 (처리기 모드는 항상 MSP를 사용하기 때문에). 연결된 문서마다 관련 스택이 EXC_RETURN 값으로 인코딩됩니다. – Notlikethat

+0

예, 제가 SysTick_Handler에 실제로 지난번 내가 있던 곳에서 정확한 PC를 읽는 코드가 있습니다.여전히 GDB가 올바르게 풀리지 않는 것은 나쁜 것입니다. – Kristoffer

+0

이렇게하기 위해'gdb' 매크로를 작성할 수 있습니다. MSP는 cortex-m에 고유하며 GDB는 여러 플랫폼에서 실행되도록 작성되었으며 주로 사용자 응용 프로그램과 관련이 있습니다. 즉 오픈 소스이며 기부금을 환영한다고 확신합니다. –

답변

0

많은 분들이 댓글을 달았습니다. 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가 스택을 자동으로 풀어 주도록 관리하지 못했다. 그러나 적어도 인터럽트가 어디에서 발사되고 있는지를 알아 냈습니다. 이것이 목표였습니다.

-2

이것을 디버깅 (DEBUGGING)이라고합니다. 시작하는 가장 쉬운 방법은 코드 전체에 printf() 호출을 여기저기서 붙이는 것입니다. 프로그램을 실행하십시오. 이 출력합니다 경우


는 B
을 가리 키도록 얻었다는

C

을 가리 키도록 가지고 지적되었고, 죽으면, 당신은 "C"와 "D."사이에 사망 알고 좀 더 밀접하게 간격을 둔 printf() 호출로 "C"와 "D"사이의 코드를 장식하여 아래쪽으로 수정할 수 있습니다.

초보자를위한 최고의 방법입니다. 노련한 많은 전문가들은 디버깅을 위해 printf()를 선호합니다. 디버거가 방해가 될 수 있습니다.

+4

내 프로그램이 부숴지기 때문에 이것은 도움이되지 않습니다. printf가 없기 때문에 인터럽트가 어디서 발생했는지 구체적으로 알고 싶습니다. – Kristoffer

1

질문이 끝나면 올바른 길을 걷고있었습니다. ARM Cortex-M 코어에는 주 스택 포인터 (MSP, 인터럽트에 사용됨)와 프로세스 스택 포인터 (PSP, 작업에 사용됨)의 두 스택 포인터가 있습니다.

우선 순위가있는 인터럽트가 들어 오면 현재 레지스터 값이 현재 스택 (백그라운드 응용 프로그램을 중단하면 PSP, 우선 순위가 낮은 인터럽트를 인터럽트하는 경우 MSP)으로 푸시됩니다. 스택이 MSP로 전환됩니다 (아직없는 경우).

처음 인터럽트를 입력하면 링크 레지스터 (LR, 반송 주소)는 실제 반송 주소가 아닌 대부분 F 값을 갖게됩니다. 이 값은 분기 할 때 코어를 종료하는 방법을 알려줍니다. 일반적으로 백그라운드 작업이 중단되면 0xFFFFFFFD 값이 표시되고 우선 순위가 낮은 인터럽트가 중단되면 0xFFFFFFF1 값이 표시됩니다. 부동 소수점 단위를 사용하는 경우이 값은 달라집니다. 하지만이 값의 마술은 비트 2 (0x4)가 스택 프레임이 PSP인지 MSP인지를 알려줍니다.

프레임이 어느 스택에 있는지 결정한 후에는 적절한 스택 포인터에서 24를 뺀 (32 비트 위치 6 개)을보고 실행중인 주소를 찾을 수 있습니다. 링크에서 그림 2.3을 참조하십시오. 이렇게하면 중단 된 PC로 연결됩니다.

1

우리는 여러 가지 형태로이 질문을 계속하고 있습니다. 사람들은 두 개의 스택이 계속 있다고 말합니다. 그래서 나는 그것을 시스템으로 직접 시도했다.

문서는 우리가 리셋에서 스레드 모드에서, 그리고 말한다 당신과 함께 정지 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 

I 설치 그리고 systick 인터럽트를 기다리면, 핸들러는 레지스터와 램을 덤프 한 다음 무한 루프로 들어갑니다. 나쁜 연습이지만 일반적으로 여기서 디버깅/학습. 인터럽트 전에 좀 레지스터 수험 :

.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 . 

및 핸들러에 내가 바로 ARM 문서에서,이 경우

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 

그래서 참조, 그것은 sp_main을 사용하는를 SP_process를 사용하지 않는 . 그것은 설명서에 0x1000074 인 중단/복귀 주소를 포함하여 푸시한다고 말하는 항목을 밀고 있습니다.

이제 SPSEL 비트를 설정하면 (PSP를 먼저 설정해야 함) 응용 프로그램/스레드 모드의 mov r0, sp는 MSP가 아닌 PSP를 사용하는 것처럼 보입니다. 그러나 핸들러는이 위치에 핸들러 그래서

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 

지금/전경

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 

를 특검팀하는 MOV r0을위한 MSP를 사용하지만 스레드에서 전에

을 넣어 나타납니다 사람들이 계속 언급하고있는 대체 스택을 다룰 때, 당신은 그 위치 (또는 당신이 의존하는 코드)에 자신을 두어야 만합니다. 왜 모든 베어러 컨트롤 레지스터가 좋고 쉬운 지 알 수있는 간단한 베어 메탈 프로그램을 원한다면 한 스택을 잘 공유 할 수 있습니다.

나는 gdb를 사용하지 않지만 모든 레지스터 sp_process와 sp_main을 찾은 다음 찾은 것에 따라 덤프해야한다. 그런 다음 각각에 12 개 정도의 단어를 덤프하면 0xFFFFFFFx가 마커로 표시된다. 그 다음부터 다시 계산하여 반송 주소를 확인하십시오. 당신은 핸들러가 두 개의 스택 포인터를 읽도록 할 수 있습니다. 그러면 gpr을 볼 수 있습니다. gnu 어셈블러 mrs rX, psp; mrs rX, msp; 프로세스와 메인 스택 포인터들.

+0

에서 이것은 ti msp432 런치 패드의 cortex-m4입니다. 나는 다른 것을 가지고 있지만 이것은 이중 스택을 다루지 않을 수도 있음을 증명하기에 충분했다. 특히 스택 스택을 설정하지 않았다면 더욱 그렇다. –

관련 문제