ARM Cortex-M系列 内核HardFault错误调试定位方法

未来已来2018-09-06 09:29
作者:刘建国

1、首先更改startup.s的启动文件,把里面的HardFault_Handler代码段换成下面的代码:
HardFault_Handler\
                                PROC
                                IMPORT  hard_fault_handler_c
                                TST LR, #4
                                ITE EQ
                                MRSEQ R0, MSP
                                MRSNE R0, PSP                               
                                B  hard_fault_handler_c
                ENDP
2.添加处理函数 hard_fault_handler_c,并且将crash log同步保存到了flash上,即使掉电重启也不会丢失。注意在写flash之前需要先确保文件系统是否写完整,这个非常关键,低概率下会出现,死机的时候,正在写文件,如果此时死机重启,可能会破坏文件系统,导致系统起不来,这点也是往往开发过程中,容易忽视的地方。如果crash的时候,发现文件正在写,就等文件写完再保存死机log,然后才能重启。

/*****************************************************************************
  FUNCTION NAME:    hard_fault_handler_c

  DESCRIPTION:      Print hard_fault handler info on the HyperTerminal.

  PARAMETERS:     
                    hardfault_args: hard fault args in sp

  RET VALUES:     
                    None
*****************************************************************************/
void hard_fault_handler_c(uint32* hardfault_args)
{
    uint8* pBuff = (uint8*)OsMalloc(MEM_POOL_1_BLOCK_SIZE);
    uint8 Len = FillBuffwithCurrTime(pBuff);//打印死机时间
    uint16 i;
    
    IsCrashed = TRUE;

    HaltLogInit();//初始化log区域,同步保存到flash,以便重新开机也能读取到死机log
    
    ArmCoreRegs.r0      = ((uint32) hardfault_args[0]);
    ArmCoreRegs.r1      = ((uint32) hardfault_args[1]);
    ArmCoreRegs.r2      = ((uint32) hardfault_args[2]);
    ArmCoreRegs.r3      = ((uint32) hardfault_args[3]);
    ArmCoreRegs.r12     = ((uint32) hardfault_args[4]);
    /*R14 is return address*/ 
    ArmCoreRegs.lr      = ((uint32) hardfault_args[5]);
    ArmCoreRegs.pc      = ((uint32) hardfault_args[6]);
    ArmCoreRegs.psr     = ((uint32) hardfault_args[7]);
    ArmCoreRegs.sp      = ((uint32) hardfault_args);
    
    ArmCoreRegs.CPUID   = SCB->CPUID;   /*CPUID Base Register                       */
    ArmCoreRegs.ICSR    = SCB->ICSR;    /*Interrupt Control and State Register      */
    ArmCoreRegs.SCR     = SCB->SCR;     /*System Control Register                   */
    ArmCoreRegs.CCR     = SCB->CCR;     /*Configuration Control Register            */
    ArmCoreRegs.SHCSR   = SCB->SHCSR;   /*System Handler Control and State Register */
    ArmCoreRegs.CFSR    = SCB->CFSR;    /*Configurable Fault Status Registers       */
    ArmCoreRegs.HFSR    = SCB->HFSR;    /*HardFault Status Register                 */
    ArmCoreRegs.DFSR    = SCB->DFSR;    /*Debug Fault Status Register               */
    ArmCoreRegs.MMFAR   = SCB->MMFAR;   /*MemManage Fault Address Register          */
    ArmCoreRegs.BFAR    = SCB->BFAR;    /*BusFault Address Register                 */

    Debug_IOP_BufQ_Output();
    HAL_UART_ResetState(GetUartHandle(DEBUG_UART));
    Serial_PutString(pBuff, Len);
    OsFree(pBuff);
    Critical_printf("hard_fault_handler:\r\n");
    Critical_printf("---------------------------------------------------------------------------------------------\r\n");
    Critical_printf("r0 = 0x%08x \tr1 = 0x%08x \tr2 = 0x%08x \tr3 = 0x%08x\r\n",
                        ArmCoreRegs.r0,ArmCoreRegs.r1,ArmCoreRegs.r2,ArmCoreRegs.r3);
    Critical_printf("r12 = 0x%08x \tlr = 0x%08x \tpc = 0x%08x \tpsr = 0x%08x\r\n",
                        ArmCoreRegs.r12,ArmCoreRegs.lr,ArmCoreRegs.pc,ArmCoreRegs.psr);
    Critical_printf("sp = 0x%08x\r\n\r\n", ArmCoreRegs.sp);
    
    Critical_printf("CPUID = 0x%08x\tICSR = 0x%08x\tSCR = 0x%08x\tCCR = 0x%08x\r\n",
                        ArmCoreRegs.CPUID,ArmCoreRegs.ICSR,ArmCoreRegs.SCR,ArmCoreRegs.CCR);
    Critical_printf("SHCSR = 0x%08x\tCFSR = 0x%08x\tHFSR = 0x%08x\tDFSR = 0x%08x\r\n",
                        ArmCoreRegs.SHCSR,ArmCoreRegs.CFSR,ArmCoreRegs.HFSR,ArmCoreRegs.DFSR);
    Critical_printf("MMFAR = 0x%08x\tBFAR = 0x%08x\r\n",
                        ArmCoreRegs.MMFAR,ArmCoreRegs.BFAR);
    Critical_printf("---------------------------------------------------------------------------------------------\r\n");
    Critical_printf("hard fault Task stack:\r\n");
    for(i=0; i< CUR_TASK_PRINTF_DUMP_SIZE; i++)
    {
        Critical_printf("0x%08x  ", *((uint32*)ArmCoreRegs.sp + i));
        if((i+1)%8 == 0)
            Critical_printf("\r\n");
    }
    Critical_printf("---------------------------------------------------------------------------------------------\r\n\r\n");

    CheckFsmLastOptAtCrash();
    FreeRtosStatusDebug();
    HaltLogWriteEnd();
    NAL_System_Reset();
    while(1);
}

/*****************************************************************************
FUNCTION NAME: CheckFsmLastOptAtCrash

DESCRIPTION: check the fsm whether is writing when crash.

PARAMETERS:
None

RET VALUES:
None
*****************************************************************************/
void CheckFsmLastOptAtCrash(void)
{
if(IsSpiFlashInWriting())
{
Critical_printf("Spi flash filesystem is in writing while halt, will finish it...\r\n");
HwdSpiFlashReInitWhenCrash();
SpiFlash_write_NO_Int();
Critical_printf("Spi flash filesystem write end\r\n");
}
}


其中SHCSR寄存器:


对于task堆栈的打印,在st提供的库上修改:

#if ( ( configUSE_TRACE_FACILITY == 1 ) && ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) )

void vTaskList( char * pcWriteBuffer )
{
TaskStatus_t *pxTaskStatusArray;
volatile UBaseType_t uxArraySize, x, i;
char cStatus;
TCB_t *xTCB;
TCB_t *currTCB;
uint16 Dump_Size;

pcWriteBuffer = pcWriteBuffer;

/*
* PLEASE NOTE:
*
* This function is provided for convenience only, and is used by many
* of the demo applications. Do not consider it to be part of the
* scheduler.
*
* vTaskList() calls uxTaskGetSystemState(), then formats part of the
* uxTaskGetSystemState() output into a human readable table that
* displays task names, states and stack usage.
*
* vTaskList() has a dependency on the sprintf() C library function that
* might bloat the code size, use a lot of stack, and provide different
* results on different platforms. An alternative, tiny, third party,
* and limited functionality implementation of sprintf() is provided in
* many of the FreeRTOS/Demo sub-directories in a file called
* printf-stdarg.c (note printf-stdarg.c does not provide a full
* snprintf() implementation!).
*
* It is recommended that production systems call uxTaskGetSystemState()
* directly to get access to raw stats data, rather than indirectly
* through a call to vTaskList().
*/


/* Make sure the write buffer does not contain a string. */
//*pcWriteBuffer = 0x00;

currTCB = pxCurrentTCB;
Critical_printf("Current running Task:%s \r\n", currTCB->pcTaskName);
Critical_printf("---------------------------------------------------------------------------------------------\r\n");

/* Take a snapshot of the number of tasks in case it changes while this
function is executing. */
uxArraySize = uxCurrentNumberOfTasks;

/* Allocate an array index for each task. */
pxTaskStatusArray = pvPortMalloc( uxCurrentNumberOfTasks * sizeof( TaskStatus_t ) );

if( pxTaskStatusArray != NULL )
{
/* Generate the (binary) data. */
uxArraySize = uxTaskGetSystemState( pxTaskStatusArray, uxArraySize, NULL );

/* Create a human readable table from the binary data. */
for( x = 0; x < uxArraySize; x++ )
{
xTCB = pxTaskStatusArray[ x ].xHandle;
Critical_printf("task name:%s\r\n",xTCB->pcTaskName);
Critical_printf("---------------------------------------------------------------------------------------------\r\n");
Critical_printf("Stack = 0x%x, TopOfStack = 0x%x\r\n", xTCB->pxStack, xTCB->pxTopOfStack);

if(xTCB == currTCB)
Dump_Size = CUR_TASK_PRINTF_DUMP_SIZE;
else
Dump_Size = TASK_PRINTF_DUMP_SIZE;

for(i=0; i< Dump_Size; i++)
{
Critical_printf("0x%08x ", (uint32)*(xTCB->pxTopOfStack + i));
if((i+1)%8 == 0)
Critical_printf("\r\n");
}
Critical_printf("---------------------------------------------------------------------------------------------\r\n\r\n");
}
Critical_printf("---------------------------------------------------------------------------------------------\r\n");
Critical_printf(" task_name\tstatus\tPriotity\tMinFreeStack(U32)\ttask_Num\r\n");
Critical_printf("---------------------------------------------------------------------------------------------\r\n");

/* Create a human readable table from the binary data. */
for( x = 0; x < uxArraySize; x++ )
{
switch( pxTaskStatusArray[ x ].eCurrentState )
{
case eReady: cStatus = tskREADY_CHAR;
break;

case eBlocked: cStatus = tskBLOCKED_CHAR;
break;

case eSuspended: cStatus = tskSUSPENDED_CHAR;
break;

case eDeleted: cStatus = tskDELETED_CHAR;
break;

default: /* Should not get here, but it is included
to prevent static checking errors. */
cStatus = 0x00;
break;
}

/* Write the task name to the string, padding with spaces so it
can be printed in tabular form more easily. */
//pcWriteBuffer = prvWriteNameToBuffer( pcWriteBuffer, pxTaskStatusArray[ x ].pcTaskName );
Critical_printf("%16s",pxTaskStatusArray[ x ].pcTaskName);

/* Write the rest of the string. */
Critical_printf("\t%c\t%u\t\t%u\t\t\t%u\r\n", cStatus, ( unsigned int ) pxTaskStatusArray[ x ].uxCurrentPriority, ( unsigned int ) pxTaskStatusArray[ x ].usStackHighWaterMark, ( unsigned int ) pxTaskStatusArray[ x ].xTaskNumber );
}
Critical_printf("---------------------------------------------------------------------------------------------\r\n");

/* Free the array again. */
vPortFree( pxTaskStatusArray );
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}

#endif /* ( ( configUSE_TRACE_FACILITY == 1 ) && ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) ) */


参考文献:

st公司的<Hard Fault的诊断>
Cortex-M3权威指南



网易云大礼包:https://www.163yun.com/gift

本文来自网易实践者社区,经作者刘国建授权发布