Jump to content

    
Sign in to follow this  
maxntf

Исследование HardFault, вставка ассемблера

Recommended Posts

Хочу включить в проект на Keil исследование причины вылета в HardFault. Но в ассемблере никак не могу разобраться. Смысл я в общем понял в ассемблерном куске кода копируются данные регистров которые нужно проверить, а в Си функции уже их разбираемым.

Есть startupxxx.s файл, в нем есть описание этой функции по умолчанию и там стоит атрибут WEAK. Посмотрел он указывает на то, что эта функция может быть переопределена в другом месте и будет иметь приоритет. 

В общем создаю новый asm файл, и компилятор начинает ругаться на то, что функция дублируется.

Подскажите как правильно прикрутить asm файл с функцией HardFault, чтоб в main.c описать функцию void HardFaultHandler(unsigned int* pStack){...}

;----------------------------------------------------------------------
;File : HardFaultHandler.S
;Purpose : HardFault exception handler for Keil assembler.
;-------- END-OF-HEADER ---------------------------------------------
;*/

 AREA OSKERNEL, CODE, READONLY, ALIGN=2
 PRESERVE8
 EXPORT HardFault_Handler

 IMPORT HardFault_Handler

 THUMB


HardFault_Handler

 ;// This version is for Cortex M0
 movs R0, #4
 mov R1, LR
 tst R0, R1 ;// Check EXC_RETURN in Link register bit 2.
 bne Uses_PSP
 mrs R0, MSP ;// Stacking was using MSP.
 b Pass_StackPtr

Uses_PSP

 mrs R0, PSP ;// Stacking was using PSP.

Pass_StackPtr

 ALIGN

 ldr R2,=HardFaultHandler
 bx R2 ;// Stack pointer passed through R0.


 END

;/****** End Of File *************************************************/

 

Или второй вариант. Пробую написать этот код в startup файле

; Dummy Exception Handlers (infinite loops which can be modified)

NMI_Handler     PROC
                EXPORT  NMI_Handler                    [WEAK]
                B       .
                ENDP
HardFault_Handler\
                PROC
                EXPORT  HardFault_Handler              [WEAK]
;// This version is for Cortex M0
 movs R0, #4
 mov R1, LR
 tst R0, R1 ;// Check EXC_RETURN in Link register bit 2.
 bne Uses_PSP
 mrs R0, MSP ;// Stacking was using MSP.
 b Pass_StackPtr

Uses_PSP

 mrs R0, PSP ;// Stacking was using PSP.

Pass_StackPtr

 ALIGN

 ldr R2,=HardFaultHandler
 bx R2 ;// Stack pointer passed through R0.
					
                B       .
                ENDP
SVC_Handler     PROC
...

А в main.c прописываю

extern void HardFaultHandler(unsigned int* pStack);
void HardFaultHandler(unsigned int* pStack){
...	
}

Тогда компилятор ругается что в startup файле символ HardFaultHandler не определен. Может его нужно как то определить в startup.s а не в main.c

Share this post


Link to post
Share on other sites

Прошу прощения, только создал сообщение и нашел ошибку. 

IMPORT HardFault_Handler

Брал из готового примера. Неудобно когда похожие названия.

В общем исправил, вроде все собралось, буду проверять.

EXPORT HardFault_Handler

IMPORT HardFaultHandler_с

...

ldr R2,=HardFaultHandler_с

И в main.c

extern void HardFaultHandler_с(unsigned int* pStack);
void HardFaultHandler_с(unsigned int* pStack){
	
}

 

Edited by maxntf

Share this post


Link to post
Share on other sites

Вот полный пример кода исследования HatdFault.

HardFaultHandler.s

AREA OSKERNEL, CODE, READONLY, ALIGN=2
 PRESERVE8
 EXPORT HardFault_Handler
 IMPORT HardFaultHandler_c
 THUMB

HardFault_Handler
 movs R0, #4
 mov R1, LR
 tst R0, R1 ;// Check EXC_RETURN in Link register bit 2.
 bne Uses_PSP
 mrs R0, MSP ;// Stacking was using MSP.
 b Pass_StackPtr

Uses_PSP
 mrs R0, PSP ;// Stacking was using PSP.

Pass_StackPtr
 ALIGN
 ldr R2,=HardFaultHandler_c
 bx R2 ;// Stack pointer passed through R0.

END

HardFaultHandler_c.c

// System Handler Control and State Register
#define SYSHND_CTRL (*(volatile unsigned int*) (0xE000ED24u))
// Memory Management Fault Status Register
#define NVIC_MFSR (*(volatile unsigned char*) (0xE000ED28u))
// Bus Fault Status Register
#define NVIC_BFSR (*(volatile unsigned char*) (0xE000ED29u))
// Usage Fault Status Register
#define NVIC_UFSR (*(volatile unsigned short*)(0xE000ED2Au))
// Hard Fault Status Register
#define NVIC_HFSR (*(volatile unsigned int*) (0xE000ED2Cu))
// Debug Fault Status Register
#define NVIC_DFSR (*(volatile unsigned int*) (0xE000ED30u))
// Bus Fault Manage Address Register
#define NVIC_BFAR (*(volatile unsigned int*) (0xE000ED38u))
// Auxiliary Fault Status Register
#define NVIC_AFSR (*(volatile unsigned int*) (0xE000ED3Cu))

static volatile unsigned int _Continue; // Set this variable to 1 to run further
static struct {
 struct {
 volatile unsigned int r0; // Register R0
 volatile unsigned int r1; // Register R1
 volatile unsigned int r2; // Register R2
 volatile unsigned int r3; // Register R3
 volatile unsigned int r12; // Register R12
 volatile unsigned int lr; // Link register
 volatile unsigned int pc; // Program counter
 union {
 volatile unsigned int byte;
 struct {
 unsigned int IPSR : 8; // Interrupt Program Status register (IPSR)
 unsigned int EPSR : 19; // Execution Program Status register (EPSR)
 unsigned int APSR : 5; // Application Program Status register (APSR)
 } bits;
 } psr; // Program status register.
 } SavedRegs;
 union {
 volatile unsigned int byte;
 struct {
 unsigned int MEMFAULTACT : 1; // Read as 1 if memory management fault
 // is active
 unsigned int BUSFAULTACT : 1; // Read as 1 if bus fault exception is active
 unsigned int UnusedBits1 : 1;
 unsigned int USGFAULTACT : 1; // Read as 1 if usage fault exception
 // is active
 unsigned int UnusedBits2 : 3;
 unsigned int SVCALLACT : 1; // Read as 1 if SVC exception is active
 unsigned int MONITORACT : 1; // Read as 1 if debug monitor exception
 // is active
 unsigned int UnusedBits3 : 1;
 unsigned int PENDSVACT : 1; // Read as 1 if PendSV exception is active
 unsigned int SYSTICKACT : 1; // Read as 1 if SYSTICK exception is active
 unsigned int USGFAULTPENDED : 1; // Usage fault pended; usage fault started
 // but was replaced by a higher-priority
 // exception
 unsigned int MEMFAULTPENDED : 1; // Memory management fault pended; memory
 // management fault started but was
 // replaced by a higher-priority exception
 unsigned int BUSFAULTPENDED : 1; // Bus fault pended; bus fault handler was
 // started but was replaced by a
 // higher-priority exception
 unsigned int SVCALLPENDED : 1; // SVC pended; SVC was started but was
 // replaced by a higher-priority exception
 unsigned int MEMFAULTENA : 1; // Memory management fault handler enable
 unsigned int BUSFAULTENA : 1; // Bus fault handler enable
 unsigned int USGFAULTENA : 1; // Usage fault handler enable
 } bits;
 } syshndctrl; // System Handler Control and State
 // Register (0xE000ED24)
 union {
 volatile unsigned char byte;
 struct {
 unsigned char IACCVIOL : 1; // Instruction access violation
 unsigned char DACCVIOL : 1; // Data access violation
 unsigned char UnusedBits : 1;
 unsigned char MUNSTKERR : 1; // Unstacking error
 unsigned char MSTKERR : 1; // Stacking error
 unsigned char UnusedBits2 : 2;
 unsigned char MMARVALID : 1; // Indicates the MMAR is valid
 } bits;
 } mfsr; // Memory Management Fault Status
 // Register (0xE000ED28)
 union {
 volatile unsigned int byte;
 struct {
 unsigned int IBUSERR : 1; // Instruction access violation
 unsigned int PRECISERR : 1; // Precise data access violation
 unsigned int IMPREISERR : 1; // Imprecise data access violation
 unsigned int UNSTKERR : 1; // Unstacking error
 unsigned int STKERR : 1; // Stacking error
 unsigned int UnusedBits : 2;
 unsigned int BFARVALID : 1; // Indicates BFAR is valid
 } bits;
 } bfsr; // Bus Fault Status Register (0xE000ED29)
 volatile unsigned int bfar; // Bus Fault Manage Address Register
 // (0xE000ED38)
 union {
 volatile unsigned short byte;
 struct {
 unsigned short UNDEFINSTR : 1; // Attempts to execute an undefined
 // instruction
 unsigned short INVSTATE : 1; // Attempts to switch to an invalid state
 // (e.g., ARM)
 unsigned short INVPC : 1; // Attempts to do an exception with a bad
 // value in the EXC_RETURN number
 unsigned short NOCP : 1; // Attempts to execute a coprocessor
 // instruction
 unsigned short UnusedBits : 4;
 unsigned short UNALIGNED : 1; // Indicates that an unaligned access fault
 // has taken place
 unsigned short DIVBYZERO : 1; // Indicates a divide by zero has taken
 // place (can be set only if DIV_0_TRP
 // is set)
 } bits;
 } ufsr; // Usage Fault Status Register (0xE000ED2A)
 union {
 volatile unsigned int byte;
 struct {
 unsigned int UnusedBits : 1;
 unsigned int VECTBL : 1; // Indicates hard fault is caused by failed
 // vector fetch
 unsigned int UnusedBits2 : 28;
 unsigned int FORCED : 1; // Indicates hard fault is taken because of
 // bus fault/memory management fault/usage
 // fault
 unsigned int DEBUGEVT : 1; // Indicates hard fault is triggered by
 // debug event
 } bits;
 } hfsr; // Hard Fault Status Register (0xE000ED2C)
 union {
 volatile unsigned int byte;
 struct {
 unsigned int HALTED : 1; // Halt requested in NVIC
 unsigned int BKPT : 1; // BKPT instruction executed
 unsigned int DWTTRAP : 1; // DWT match occurred
 unsigned int VCATCH : 1; // Vector fetch occurred
 unsigned int EXTERNAL : 1; // EDBGRQ signal asserted
 } bits;
 } dfsr; // Debug Fault Status Register (0xE000ED30)
 volatile unsigned int afsr; // Auxiliary Fault Status Register
 // (0xE000ED3C) Vendor controlled (optional)
} HardFaultRegs;

void HardFaultHandler_c(unsigned int* pStack);
void HardFaultHandler_c(unsigned int* pStack) {

if (NVIC_HFSR & (1uL << 31)) {
 NVIC_HFSR |= (1uL << 31); // Reset Hard Fault status
 *(pStack + 6u) += 2u; // PC is located on stack at SP + 24 bytes;
 // increment PC by 2 to skip break instruction.
 return; // Return to interrupted application
}
 //
 // Read NVIC registers
 //
 HardFaultRegs.syshndctrl.byte = SYSHND_CTRL; // System Handler Control and
 // State Register
 HardFaultRegs.mfsr.byte = NVIC_MFSR; // Memory Fault Status Register
 HardFaultRegs.bfsr.byte = NVIC_BFSR; // Bus Fault Status Register
 HardFaultRegs.bfar = NVIC_BFAR; // Bus Fault Manage Address Register
 HardFaultRegs.ufsr.byte = NVIC_UFSR; // Usage Fault Status Register
 HardFaultRegs.hfsr.byte = NVIC_HFSR; // Hard Fault Status Register
 HardFaultRegs.dfsr.byte = NVIC_DFSR; // Debug Fault Status Register
 HardFaultRegs.afsr = NVIC_AFSR; // Auxiliary Fault Status Register
 //
 // Halt execution
 // If NVIC registers indicate readable memory, change the variable value
 // to != 0 to continue execution.
 //
 _Continue = 0u;
 while (_Continue == 0u);
 //
 // Read saved registers from the stack
 //
 HardFaultRegs.SavedRegs.r0 = pStack[0]; // Register R0
 HardFaultRegs.SavedRegs.r1 = pStack[1]; // Register R1
 HardFaultRegs.SavedRegs.r2 = pStack[2]; // Register R2
 HardFaultRegs.SavedRegs.r3 = pStack[3]; // Register R3
 HardFaultRegs.SavedRegs.r12 = pStack[4]; // Register R12
 HardFaultRegs.SavedRegs.lr = pStack[5]; // Link register LR
 HardFaultRegs.SavedRegs.pc = pStack[6]; // Program counter PC
 HardFaultRegs.SavedRegs.psr.byte = pStack[7]; // Program status word PSR
 //
 // Halt execution
 // To step out of the HardFaultHandler, change the variable value to != 0.
 //
 _Continue = 0u;
 while (_Continue == 0u) {
 }
}

В общем после вылета в HardFault в режиме отладки проверяю структуру HardFaultRegs и там все значения 0. Где то ошибка в функции, как узнать причину?

Share this post


Link to post
Share on other sites

Покажу мой обработчик. :)

/*!*****************************************************************************
  @brief	Simple HardFault Handler
  @details	В пошаговом режиме переставить программный счетчик на BX и шагнуть
  @note		PC будет содержать команду, следующую за вызвавшей сбой
  */
__asm void HardFault_Handler(void)
{
	B .
	BX LR
}

 

Share this post


Link to post
Share on other sites
23 minutes ago, ViKo said:

Покажу мой обработчик. :)

Ага, он будет особенно полезен для удаленной диагностики, или когда у МК задействовано 20 из 20 выводов, например.

Share this post


Link to post
Share on other sites
10 минут назад, aaarrr сказал:

Ага, он будет особенно полезен для удаленной диагностики, или когда у МК задействовано 20 из 20 выводов, например.

Для имитации сбоя от удаленного пользователя у меня на столе стоит такой же прибор.

И у топикстартера тоже не видно передачи наружу отладочной информации.

А "МК 20 из 20" обязательно предполагает наличие внешнего интерфейса, хотя бы для удаленной диагностики? А если не вмещается?

В остальном - согласен с вами. :)

Share this post


Link to post
Share on other sites
6 minutes ago, ViKo said:

Для имитации сбоя от удаленного пользователя у меня на столе стоит такой же прибор.

Возможно, поможет воспроизвести ситуацию от удаленного пользователя, но не поможет, если у удаленного прибора непосредственного пользователя не предусмотрено.

 

6 minutes ago, ViKo said:

А "МК 20 из 20" обязательно предполагает наличие внешнего интерфейса, хотя бы для удаленной диагностики? А если не вмещается?

Если есть возможность так или иначе зафиксировать сам факт сбоя, то можно и во флеш данные о последней ошибке сохранять.

Share this post


Link to post
Share on other sites

У моих приборов пользователи есть, обычно сидят напротив и втыкают в происходящее.

8 минут назад, aaarrr сказал:

Если есть возможность так или иначе зафиксировать сам факт сбоя, то можно и во флеш данные о последней ошибке сохранять.

А потом что с ней (флэш-памятью) делать? Особенно удаленно и без интерфейса.

Share this post


Link to post
Share on other sites

Еще меня заставляет задуматься следующее. Допустим, получил дистанционно всю информацию о сбое. Надо же знать, какая версия программы записана в приборе. И держать в полной готовности все версии программы, чтобы достоверно находить адрес сбоя. Не слишком ли? Хорошо, если приборы и обновляются автоматически до последней версии.

Share this post


Link to post
Share on other sites
1 hour ago, ViKo said:

У моих приборов пользователи есть, обычно сидят напротив и втыкают в происходящее.

Даже квалифицированный пользователь не всегда сможет внятно описать проблему и метод её воспроизведения. Он же не тестировщик.

 

1 hour ago, ViKo said:

А потом что с ней (флэш-памятью) делать? Особенно удаленно и без интерфейса.

Прочитать, когда на оленях назад привезут. Лучше иметь хоть какой-то источник информации, чем совсем никакого.

 

1 hour ago, ViKo said:

Еще меня заставляет задуматься следующее. Допустим, получил дистанционно всю информацию о сбое. Надо же знать, какая версия программы записана в приборе. И держать в полной готовности все версии программы, чтобы достоверно находить адрес сбоя. Не слишком ли? Хорошо, если приборы и обновляются автоматически до последней версии.

На то есть системы контроля версий. Но даже и без них как минимум безответственно выпускать релиз и не сохранять архив.

Share this post


Link to post
Share on other sites
4 минуты назад, aaarrr сказал:

Даже квалифицированный пользователь не всегда сможет внятно описать проблему и метод её воспроизведения. Он же не тестировщик.

Прочитать, когда на оленях назад привезут. Лучше иметь хоть какой-то источник информации, чем совсем никакого.

На то есть системы контроля версий. Но даже и без них как минимум безответственно выпускать релиз и не сохранять архив.

Если человек не может внятно описать, какая последовательность действий привела к ошибке (а такое бывает), то сам процессор тем более не сохранит в лог, какие сигналы приходили, какие органы управления использовались, какие режимы задавались, по какому боку его хлопали.

Получить прибор с зафиксированным глюком не будет быстрее, чем сымитировать его. Особенно, учитывая сказанное выше.

Систему контроля версий использую. Всплывают тонкости, когда добавляешь-убираешь файл из проекта. Приходится руками править проект, сначала в одну сторону, потом обратно. Неприятное действо.

Share this post


Link to post
Share on other sites
3 часа назад, ViKo сказал:

Так отладчик и сам показывает HF регистры. Главное - определить адрес программы, откуда улетели.

Очень часто это не даёт ровным счётом ничего. Ну увидите Вы что улетели изнутри функции printf или memcpy - что это даст?

Нужны все регистры + кадр стека. А также содержимое-расшифровка регистров HF.

Share this post


Link to post
Share on other sites
3 минуты назад, jcxz сказал:

Очень часто это не даёт ровным счётом ничего. Ну увидите Вы что улетели изнутри функции printf или memcpy - что это даст?

Нужны все регистры + кадр стека. А также содержимое-расшифровка регистров HF.

Так я же зависаю в обработчике. Все регистры, все ошибки на своем месте. И в отладчике вижу их всех.

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Sign in to follow this