2016-07-27 9 views
2

Я пытаюсь получить обратный адрес обработчика IRQ в моем коде. Моя цель - сохранить значение ПК до того, как таймер сторожевого таймера истечет и до сброса для целей отладки, используя WDT_IRQHandler(). Я также проверяю этот подход с другими IRQ, чтобы проверить, понял ли я эту идею. Но, похоже, я этого не делал.Получение адреса возврата исключения в ARM Cortex M0

Я ознакомился с documentation. Я понял, что когда происходит исключение, в стек помещается 8 регистров: R0, R1, R2, R3, R12, LR, PC и XPSR.

Я также прочитал, что стек автоматически выравнивается по двойному слову. Поэтому, на мой взгляд, получение обратного адреса так же просто:

  • получить адрес sp с помощью __builtin_frame_address (0);
  • добавьте к нему смещение уложенного ПК (0x18) и прочитайте значение, которое предположительно является значением, которое будет восстановлено на ПК при возврате обработчика.

Проверка с прикрепленным отладчиком, похоже, это не так, содержимое на этом адресе памяти не всегда указывает на область флэш-памяти или даже на допустимую область, и в любом случае это никогда не значение что ПК будет принимать после инструкции POP.

Код работает нормально, поэтому я думаю, что это проблема, которую я имею в понимании того, как это работает.

Если я проверить разборки, в некотором IRQs постоянный добавляются к зру до расчековки (?)

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

В другом IRQs это не происходит.

Я понимаю, что может случиться, что в стек попадают больше регистров, поэтому как я могу быть уверен в том, какое смещение для извлечения ПК?

Если я проверяю дамп памяти вокруг SP, когда код по-прежнему находится в обработчике IRQ, я могу определить адрес возврата, но он всегда находится в странном месте с отрицательным смещением по сравнению с 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 file), который передает указатель стека к реальному обработчику.Вот как это работает (для M3/4):

  • Начальное значение LR в обработчик исключений известен как EXC_RETURN (подробнее here). Его биты имеют различный смысл, нас интересует тот факт, что EXC_RETURN[2] равен 0, если активным стеком является MSP и 1, если активным стеком является PSP;
  • TST LR, #4 проверяет EXC_RETURN[2] и устанавливает флаги условий;
  • MRSEQ R0, MSP перемещает MSP в R0, если EXC_RETURN[2] == 0;
  • MRSNE R0, PSP перемещает PSP в R0, если EXC_RETURN[2] == 1;
  • Наконец, LDR/BX переходит к реальной функции (R0 - это первый аргумент).

Вариант M0/1 аналогичен, но использует ветви с сердечником does not support IT blocks.

Это решает проблему MSP/PSP и, поскольку она выполняется до любой операции с генерируемым компилятором, она обеспечит надежный указатель. Я использовал простую (не связанную) ветвь функции, потому что после этого мне не нужно ничего делать, и LR уже хорош. Он экономит несколько циклов и LR push/pop. Также используются все регистры в диапазоне R0-R3, поэтому их не нужно сохранять.

+0

Спасибо. для точки 1) Я предположил, что могу проверить как MSP, так и PSP из C с помощью __get_MSP() и __get_PSP(). для 2) Я понимаю, что после нажатия код может добавить локальные переменные в стек, и они известны во время компиляции, поэтому я не могу полагаться на фиксированное смещение. Я попробую ваш подход. – Vitomakes

+0

@Vitomakes Проверка 'MSP' и' PSP' с C не говорит вам, какой из них правильный. Конечно, если вы используете только «MSP» (очень часто), вы можете пропустить чек. –

+1

«ITE» и «MRSEQ/MRSNE» не поддерживаются корой M0, и компилятор жалуется на то, что находится в режиме большого пальца и не может использовать TST. Но у меня есть идея, я разобраю ее – Vitomakes