2016-08-02 1 views
-1

J'essaie de trouver où dans mon code une interruption spécifique s'est produite. Dans ce cas, il est sur un microcontrôleur stm32f4 et l'interruption est le SysTick_Handler.trouver où l'interruption s'est produite sur cortex-m4

Ce que je veux, c'est de savoir d'où provient l'interruption systick. J'utilise bras none-eabi-gdb pour essayer de trouver la trace, mais les seules informations que je reçois de là est:

(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?) 

Comment puis-je obtenir des informations sur l'endroit où le programme était avant l'interruption tiré ? En regardant la documentation du bras here, il semble que je devrais être capable de lire le pointeur de pile, et obtenir le PC à partir de là. Mais alors c'est exactement ce que le déroutant dans GDB fait n'est-ce pas?

+0

Le code interrompu était-il exécuté avec la pile principale? S'il était en cours d'exécution avec la pile de processus, alors vous déroulez le mauvais (puisque le mode Gestionnaire utilise toujours MSP). Selon les documents que vous avez liés, la pile concernée est codée dans la valeur EXC_RETURN. – Notlikethat

+0

Ouais j'ai remarqué, j'ai maintenant un code dans mon SysTick_Handler qui lit réellement le bon PC d'où j'étais la dernière fois.Pourtant, c'est mauvais que GDB ne se déroule pas correctement. – Kristoffer

+0

Vous pouvez écrire des macros 'gdb' pour ce faire. MSP est spécifique du cortex m et GDB est écrit pour fonctionner sur de nombreuses plates-formes différentes et est principalement concerné par les applications utilisateur. Cela dit, c'est une source ouverte et je suis sûr que les contributions sont les bienvenues. Cela ne –

Répondre

0

Comme beaucoup d'entre vous l'ont commenté, le PC serait dans deux piles différentes, la façon dont je l'ai résolu était en trouvant un code HardFault_Handling en assemblée et en prenant ce dont j'avais besoin à partir de là. Pour obtenir la valeur du PC correctement, j'utilise le code suivant.

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 
    ); 

La valeur de l'endroit où l'interruption peut ne est venu maintenant accessible par

uint32_t PC = *r0; 

et peut maintenant être utilisé pour tout ce que je veux. Malheureusement, je n'ai pas réussi à obtenir GDB pour dérouler la pile automatiquement pour moi. Mais au moins, j'ai découvert où l'interruption tirait, quel était le but.

-2

Cela s'appelle DEBUGGING. La meilleure façon de commencer est de coller un tas d'appels printf() ici et là dans le code. Exécutez le programme. Si elle imprime:

obtenu au point A
obtenu au point B
obtenu au point C

et meurt, alors vous savez qu'il est mort entre "C" et "D" Vous pouvez maintenant affiner cela en festonnant le code entre "C" et "D" avec des appels printf() plus rapprochés.

C'est la meilleure façon pour un débutant de commencer. De nombreux experts chevronnés préfèrent également printf() pour le débogage. Les débogueurs peuvent gêner.

+4

Je n'ai pas aidé pas moi, puisque mon programme isnt plantage, printf, et je veux savoir précisément où l'interruption est passé de – Kristoffer

1

Vous étiez sur la bonne voie à la fin de votre question. Les cœurs ARM Cortex-M ont deux pointeurs de pile, le pointeur de pile principal (MSP, utilisé pour les interruptions) et le pointeur de pile de processus (PSP, utilisé pour les tâches). Lorsqu'une interruption avec priorité intervient, les valeurs de registre actuelles (pour la plupart des registres) sont poussées sur la pile en cours (PSP en cas d'interruption de l'application en arrière-plan ou MSP en cas d'interruption d'une interruption de priorité inférieure), puis la pile est commutée sur le MSP (si ce n'est déjà fait). Lorsque vous entrez une interruption pour la première fois, le registre de liaison (LR, adresse de retour) aura une valeur qui est principalement F plutôt qu'une adresse de retour réelle. Cette valeur indique au noyau comment quitter lorsqu'il est ramifié. En règle générale, vous verrez une valeur de 0xFFFFFFFD si la tâche d'arrière-plan a été interrompue ou 0xFFFFFFF1 si une interruption de priorité inférieure a été interrompue. Ces valeurs différeront si vous utilisez l'unité à virgule flottante. La magie dans cette valeur, cependant, est que le bit 2 (0x4) vous indique si votre cadre de pile est sur la PSP ou MSP. Une fois que vous avez déterminé la pile de votre cadre, vous pouvez trouver l'adresse à partir de laquelle vous avez exécuté en regardant le pointeur de pile approprié moins 24 (6 emplacements 32 bits). Voir la figure 2.3 dans votre lien. Cela vous dirigera vers le PC à partir duquel vous avez été interrompu.

1

Nous continuons à voir cette question sous diverses formes et les gens continuent à dire qu'il y a deux piles. J'ai donc essayé moi-même avec le systick.

La documentation dit que nous sommes en mode fil de remise à zéro, et si vous arrêtiez avec openocd il est dit que

target halted due to debug-request, current mode: Thread 

J'ai un code pour vider les registres:

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 configuration et attendez une interruption systick, le gestionnaire déverse les registres et ram et puis va dans une boucle infinie. mauvaise pratique en général mais juste déboguer/apprendre ici. Avant l'interruption I de préparation des registres:

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

et dans le gestionnaire je vois

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 

Donc dans ce cas, tout droit sorti de la documentation ARM, il n'utilise pas le sp_process qu'il utilise sp_main . Il pousse les éléments que le manuel indique qu'il pousse y compris l'adresse interrompue/retour qui est 0x1000074. Maintenant, si je mets le bit SPSEL (attention à paramétrer la PSP en premier), il apparaît qu'un mode mov r0, sp in application/thread utilise le PSP non MSP. Mais le gestionnaire utilise msp pour une r0 mov, sp mais semble mettre le

avant en fil/premier plan

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 

maintenant dans le gestionnaire

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 

Donc, pour être dans cette position de traiter avec la pile de rechange que les gens continuent de mentionner, vous devez vous mettre dans cette position (ou un code que vous comptez). Pourquoi vous voudriez faire cela pour les programmes simples de métal nu, qui sait, le registre de contrôle de tous les zéros est gentil et facile, peut partager une pile juste très bien. Je n'utilise pas gdb, mais vous devez l'obtenir pour vider tous les registres sp_process et sp_main, puis en fonction de ce que vous trouvez, puis vider une douzaine de mots à chaque et là vous devriez voir le 0xFFFFFFFx comme marqueur puis compter à partir de cela pour voir l'adresse de retour. Vous pouvez demander à votre gestionnaire de lire les deux pointeurs de pile, vous pouvez également regarder gprs. Avec gnu assembleur mrs rX, psp; mrs rX, msp; Pour les pointeurs de processus et de pile principale.

+0

noter que ceci est un cortex m4 dans un launchpad ti msp432. J'ai d'autres, mais ce fut assez bon pour démontrer que vous ne pouvez pas être nécessairement affaire à la chose à double pile, en particulier si vous na pas mis en place la pile alternative. –