背景:我用的S32K3系列单片机,这芯片的RAM有ECC,就是说上电后如果你不写RAM,直接去读,就会触发ECC故障,然后进入hardfault.于是我就想实现Try Catch,想办法从Hardfault中跳出来。理论上支持CM系列,我只实现了Free RTOS的. 裸机也可以的。。包括STM32...实现代码:
核心汇编部分:
#if (configNumberOfExceptionStacks > 0)
int xPortUpdateSignalCode(ExceptionContext_t *pxExceptionContext)
{
// 根据寄存器判断是哪些错误码,然后返回对应的信号。这里简单返回总线错误
return SIGBUS;
}
__attribute__((naked)) HardFault_Handler(void)
{
__asm volatile(
"TST lr, #0x04 \n" // ; if(!EXC_RETURN[2])
"ITE EQ \n"
"MRSEQ r0, msp \n" //; [2]=0 ==> Z=1, get fault context from handler."
"MRSNE r0, psp \n" //; [2]=1 ==> Z=0, get fault context from thread."
/*" tst lr, #0x10 \n" Is the task using the FPU context? If so, push high vfp registers. */
/*" it eq \n"*/
/*" vstmdbeq r0!, {s16-s31} \n"*/
"stmdb r0!, {r4-r11,lr} \n"
"push {r0} \n"
"BL vExceptionHappened \n" // vExceptionHappened(r0); r0=异常长下文的栈结构体地址,r1=是否发生在中断
"pop {r0} \n"
"ldmia r0!, {r4-r11,lr} \n" /* Pop the core registers. */
"TST lr, #0x04 \n" // ; if(!EXC_RETURN[2])
"ITE EQ \n"
"MSREQ msp, r0 \n" //; [2]=0 ==> Z=1, update stack pointer to MSP."
"MSRNE psp, r0 \n" //; [2]=1 ==> Z=0, update stack pointer to PSP."
"isb \n"
"bx lr\n");
}
#else
void HardFault_Handler(void)
{
while (1)
{
}
}
#endif
FreeRTOS.h:
#if ( configNumberOfExceptionStacks >0 )
#include
#define SIGHUP 1 /* hangup */
#define SIGINT 2 /* interrupt */
#define SIGQUIT 3 /* quit */
#define SIGILL 4 /* illegal instruction (not reset when caught) */
#define SIGTRAP 5 /* trace trap (not reset when caught) */
#define SIGIOT 6 /* IOT instruction */
#define SIGABRT 6 /* used by abort, replace SIGIOT in the future */
#define SIGEMT 7 /* EMT instruction */
#define SIGFPE 8 /* floating point exception */
#define SIGKILL 9 /* kill (cannot be caught or ignored) */
#define SIGBUS 10 /* bus error */
#define SIGSEGV 11 /* segmentation violation */
#define SIGSYS 12 /* bad argument to system call */
#define SIGPIPE 13 /* write on a pipe with no one to read it */
#define SIGALRM 14 /* alarm clock */
#define SIGTERM 15 /* software termination signal from kill */
#define SIGURG 16 /* urgent condition on IO channel */
#define SIGSTOP 17 /* sendable stop signal not from tty */
#define SIGTSTP 18 /* stop signal from tty */
#define SIGCONT 19 /* continue a stopped process */
#define SIGCHLD 20 /* to parent on child stop or exit */
#define SIGCLD 20 /* System V name for SIGCHLD */
#define SIGTTIN 21 /* to readers pgrp upon background tty read */
#define SIGTTOU 22 /* like TTIN for output if (tp->t_local<OSTOP) */
#define SIGIO 23 /* input/output possible signal */
#define SIGPOLL SIGIO /* System V name for SIGIO */
#define SIGWINCH 24 /* window changed */
#define SIGUSR1 25 /* user defined signal 1 */
#define SIGUSR2 26 /* user defined signal 2 */
#endif
task.c修改:
PRIVILEGED_DATA TCB_t * volatile pxCurrentTCB = NULL; //原内核代码
PRIVILEGED_DATA UBaseType_t volatile xExceptionHappen = 0;//我加的
#if ( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 )//原内核代码
void * pvThreadLocalStoragePointers[ configNUM_THREAD_LOCAL_STORAGE_POINTERS ];
#endif
//我加的
#if ( configNumberOfExceptionStacks > 0 )
UBaseType_t uxJmpStacksCounter;
jmp_buf JmpStacks[configNumberOfExceptionStacks];
volatile ExceptionContext_t xExceptionContext;
volatile int xThrowCode;
#endif
#if ( configGENERATE_RUN_TIME_STATS == 1 )
configRUN_TIME_COUNTER_TYPE ulRunTimeCounter; /*< Stores the amount of time the task has spent in the Running state. */
#endif
task.c:
#if (configNumberOfExceptionStacks >0)
inline jmp_buf *xGetJmpStackOfCurrentTask()
{
TCB_t *self=(TCB_t *)xTaskGetCurrentTaskHandle();
configASSERT(self!=NULL);
return self->JmpStacks[self->uxJmpStacksCounter];
}
inline void jmp_stack_push()
{
TCB_t *self=(TCB_t *)xTaskGetCurrentTaskHandle();
configASSERT(self!=NULL);
self->uxJmpStacksCounter++;
}
inline jmp_buf* jmp_stack_pop()
{
TCB_t *self=(TCB_t *)xTaskGetCurrentTaskHandle();
configASSERT(self!=NULL);
return &self->JmpStacks[--self->uxJmpStacksCounter];
}
void vInternalExceptionProcess ()
{
taskENTER_CRITICAL();
{
xExceptionHappen--;
}
taskEXIT_CRITICAL();
TCB_t *self=(TCB_t *)xTaskGetCurrentTaskHandle();
configASSERT(self!=NULL);
UBaseType_t tempJmpStacksCounter=0;
taskENTER_CRITICAL();
{
tempJmpStacksCounter=self->uxJmpStacksCounter;
}
taskEXIT_CRITICAL();
if(tempJmpStacksCounter==0)
{
extern void vExceptionProcess(TaskHandle_t pxCurrentTask,volatile ExceptionContext_t *pxExceptionContext);
vExceptionProcess(self,&self->xExceptionContext);
}
else
{
_throw_(self->xThrowCode);//to do
}
while(1)
{
vTaskDelay(1000);
}
}
/*
*/
void vExceptionHappened(ExceptionContext_t *pxExceptionContext)
{
xExceptionHappen++;
if(xExceptionHappen>1)
{
while(1);//no use configASSERT
}
TCB_t *self=xTaskGetCurrentTaskHandle();
configASSERT(self!=NULL);
extern int xPortUpdateSignalCode(ExceptionContext_t *pxExceptionContext);
self->xThrowCode=xPortUpdateSignalCode(pxExceptionContext);
memcpy((ExceptionContext_t *)&self->xExceptionContext,pxExceptionContext,sizeof(ExceptionContext_t));
pxExceptionContext->exception_stack_frame.pc=(BaseType_t)vInternalExceptionProcess;
}
ExceptionContext_t* xGetExceptionContext(void)
{
TCB_t *self=xTaskGetCurrentTaskHandle();
configASSERT(self!=NULL);
return &self->xExceptionContext;
}
#endif
task.h:
#if (configNumberOfExceptionStacks >0)
void jmp_stack_push() PRIVILEGED_FUNCTION;
jmp_buf* jmp_stack_pop() PRIVILEGED_FUNCTION;
jmp_buf *xGetJmpStackOfCurrentTask() PRIVILEGED_FUNCTION;
#define _try_ \
jmp_buf *__jmp_buf= xGetJmpStackOfCurrentTask(); \
int __jmp_result = setjmp(*__jmp_buf); \
if (!__jmp_result) jmp_stack_push(); \
if (!__jmp_result)
#define _catch_(x) \
int x = __jmp_result; \
if (!x) jmp_stack_pop(); \
else
#define _throw_(x) longjmp(*jmp_stack_pop(), x);
void vExceptionHappened(ExceptionContext_t *pxExceptionContext);
ExceptionContext_t* xGetExceptionContext(void);
#else
#define _try_
#define _catch_(x)
#define _throw_(x)
#endif
portmacro.h: (我用S32K自带的LIBC库的Setjmp和longJmp反汇编没有保存浮点,所以我也没保存,实际你需要保存,我try块里用不到浮点,所以没保存,如果要保存自行修改结构体和hardfault汇编哦)
#if (configNumberOfExceptionStacks >0)
#include "Mcal.h"
#include "core_cm7.h"
struct exception_stack_frame
{
BaseType_t r0;
BaseType_t r1;
BaseType_t r2;
BaseType_t r3;
BaseType_t r12;
BaseType_t lr;//这个LR是异常发生后,硬件自动入栈的LR
BaseType_t pc;
BaseType_t psr;
};
typedef struct stack_frame
{
BaseType_t exc_return;//这个LR是进入异常函数后,LR的实时状态。
/* r4 ~ r11 register */
BaseType_t r4;
BaseType_t r5;
BaseType_t r6;
BaseType_t r7;
BaseType_t r8;
BaseType_t r9;
BaseType_t r10;
BaseType_t r11;
#if 0
BaseType_t s16;
BaseType_t s17;
BaseType_t s18;
BaseType_t s19;
BaseType_t s20;
BaseType_t s21;
BaseType_t s22;
BaseType_t s23;
BaseType_t s24;
BaseType_t s25;
BaseType_t s26;
BaseType_t s27;
BaseType_t s28;
BaseType_t s29;
BaseType_t s30;
BaseType_t s31;
#endif
struct exception_stack_frame exception_stack_frame;
}ExceptionContext_t;
#endif
调用:
_try_
{
uint32_t *addr = (uint32_t *)(&__BootAppComSramStart__);
return *addr;
}
_catch_(x)
{
return 0;
}