简介
上一篇(【NUCLEO-H533RE评测】CmBacktrace 移植适配)已经适配了CmBacktrace,MCU 发生了hardfault 时会将hardfault信息输出至串口,但是在实际的量产项目中,在客户手中的设备基本不会接着串口查看异常信息,而且有的死机问题复现率不高的,如果我们能将异常信息信息存储到flash中,这样就能将现场的信息保存下来,方便我们点差之前死机的原因。
FLASH 控制
STM32H533 芯片内部集成了512KB的flash 空间,内部有两个bank,每个bank 包含32个section,每个sector 的大小为8KB,内部使用的128bit 的总线对FLASH进行访问,以下是flash 的内部框图,可以方便了解内部结构。
本次试验使用flash 的bank2 的 31 sector 的末端的8K FLASH 存储异常信息,每个异常信息块分配2K的空间,最多可以保存四次coredump 的异常信息。
对应代码如下,在cmbtrace 处理的入口调用cmt_recorder_start 函数查找合适的存储空间,在cmbtrace 处理函数退出时,调用cmt_recorder_end 接口lock flash 访问,将cmt_recorder_putc 对接到CmBacktrace 信息输出接口将信息写入flash,对应代码如下:
/** ************************************************************************************************
* @file cmback_trace_recoder.c *
* @brief recoder cortex-m hardfault infomation *
* *
**************************************************************************************************/
/***************************************************************************************************
* Include header files *
**************************************************************************************************/
#include <stdint.h>
#include <string.h>
#include <stm32h5xx_hal.h>
/***************************************************************************************************
* Global Macro definition *
**************************************************************************************************/
#define CMT_HEADER_SIZE 16U
#define CMT_PG_SIZE 16U
#define CMT_FLASH_BASE_START ((uint32_t)0x0807e000u) /* Base @ of SECTOR 31, 8 Kbytes */
#define CMT_FLASH_BASE_END (CMT_FLASH_BASE_START + FLASH_SECTOR_SIZE - 1)
#define CMT_INFO_BLOCK_SIZE 2048U
#define CMT_INFO_BLOCK_NUM FLASH_SECTOR_SIZE/CMT_INFO_BLOCK_SIZE
#define CMT_INFO_MAGIC 0xa1b2c3d4u
/***************************************************************************************************
* Global Types definition *
**************************************************************************************************/
/** @brief Structure defining the recoder block header */
typedef struct cmt_recorder_header{
union{
struct{
uint32_t magic;
uint32_t pflash_start;
uint16_t pflash_offset;
uint16_t size;
uint32_t crc;
};
uint8_t parray[CMT_HEADER_SIZE];
};
} cmt_recorder_header_t;
static cmt_recorder_header_t header = {0x00u};
static uint8_t cmt_check_block_is_dirty(uint32_t* p)
{
int i = 0;
for(; i < CMT_INFO_BLOCK_SIZE/sizeof(uint32_t);i++)
{
if(p[i] != 0xffffffffu)
break;
}
return i == CMT_INFO_BLOCK_SIZE/sizeof(uint32_t) ? 0 : 1;
}
static uint32_t cmt_get_free_block_addr(void)
{
uint32_t * p_flash = (uint32_t *)CMT_FLASH_BASE_START;
uint8_t i = 0;
for(; i < CMT_INFO_BLOCK_NUM;i++)
{
if(p_flash[0] == CMT_INFO_MAGIC)
{
/* the block is used to find next */
p_flash += CMT_INFO_BLOCK_SIZE/4;
}
else
{
if( 0 == cmt_check_block_is_dirty(p_flash))
break;
}
}
return i == CMT_INFO_BLOCK_NUM ? 0 : (CMT_FLASH_BASE_START + i*CMT_INFO_BLOCK_SIZE);
}
static uint8_t pg_array[CMT_PG_SIZE] = {0x00};
static uint8_t index = 0;
static uint8_t cmstart = 0;
uint8_t cmt_recorder_putc(uint8_t ch)
{
if(cmstart == 0)
return ch;
pg_array[index++] = ch;
if(index == CMT_PG_SIZE)
{
HAL_FLASH_Program(FLASH_TYPEPROGRAM_QUADWORD, header.pflash_start + header.pflash_offset, ((uint32_t)pg_array));
header.pflash_offset += CMT_PG_SIZE;
header.size += CMT_PG_SIZE;
/* reset index & cache buffer */
index = 0;
memset((void *)pg_array,0,CMT_PG_SIZE);
}
return ch;
}
/**
* @brief Gets the sector of a given address
* @param Addr: Address of the FLASH Memory
* @retval The sector of a given address
*/
static uint32_t cmt_get_sector(uint32_t address)
{
uint32_t sector = 0;
if((address >= FLASH_BASE) && (address < FLASH_BASE + FLASH_BANK_SIZE))
{
sector = (address & ~FLASH_BASE) / FLASH_SECTOR_SIZE;
}
else if ((address >= FLASH_BASE + FLASH_BANK_SIZE) && (address < FLASH_BASE + FLASH_SIZE))
{
sector = ((address & ~FLASH_BASE) - FLASH_BANK_SIZE) / FLASH_SECTOR_SIZE;
}
else
{
sector = 0xFFFFFFFF; /* Address out of range */
}
return sector;
}
/**
* @brief Gets the bank of a given address
* @param Addr: Address of the FLASH Memory
* @retval The bank of a given address
*/
static uint32_t cmt_get_bank(uint32_t addr)
{
uint32_t bank = 0;
if (READ_BIT(FLASH->OPTSR_CUR, FLASH_OPTSR_SWAP_BANK) == 0)
{
/* No Bank swap */
if (addr < (FLASH_BASE + FLASH_BANK_SIZE))
{
bank = FLASH_BANK_1;
}
else
{
bank = FLASH_BANK_2;
}
}
else
{
/* Bank swap */
if (addr < (FLASH_BASE + FLASH_BANK_SIZE))
{
bank = FLASH_BANK_2;
}
else
{
bank = FLASH_BANK_1;
}
}
return bank;
}
uint32_t cmt_recorder_start(void)
{
FLASH_EraseInitTypeDef EraseInitStruct;
cmstart = 1;
uint32_t sectorerr;
HAL_ICACHE_Disable();
/* Unlock the Flash to enable the flash control register access *************/
HAL_FLASH_Unlock();
header.pflash_start = cmt_get_free_block_addr();
if(0u == header.pflash_start)
{
EraseInitStruct.TypeErase = FLASH_TYPEERASE_SECTORS;
EraseInitStruct.Banks = cmt_get_bank(CMT_FLASH_BASE_START);
EraseInitStruct.Sector = cmt_get_sector(CMT_FLASH_BASE_START);
EraseInitStruct.NbSectors = 1;
HAL_FLASHEx_Erase(&EraseInitStruct, §orerr);
header.pflash_start = CMT_FLASH_BASE_START;
}
header.pflash_offset = CMT_HEADER_SIZE;
return 0;
}
uint32_t cmt_recorder_end(void)
{
if(index != 0)
{
HAL_FLASH_Program(FLASH_TYPEPROGRAM_QUADWORD, header.pflash_start + header.pflash_offset, ((uint32_t)pg_array));
header.pflash_offset += CMT_PG_SIZE;
header.size += CMT_PG_SIZE;
}
header.magic = CMT_INFO_MAGIC;
HAL_FLASH_Program(FLASH_TYPEPROGRAM_QUADWORD, header.pflash_start, ((uint32_t)header.parray));
/* Lock the Flash to disable the flash control register access (recommended
to protect the FLASH memory against possible unwanted operation) *********/
HAL_FLASH_Lock();
HAL_ICACHE_Enable();
return 0;
}
#include "littleshell.h"
#include <stdio.h>
int cmbdump(char argc,char ** argv)
{
uint32_t * p_flash = (uint32_t *)CMT_FLASH_BASE_START;
uint8_t i = 0;
for(; i < CMT_INFO_BLOCK_NUM ; i++, p_flash += CMT_INFO_BLOCK_SIZE/4)
{
if(p_flash[0] == CMT_INFO_MAGIC)
{
/* the block is used to find next */
printf("%s",(char *)(p_flash+CMT_HEADER_SIZE));
}
}
return 0;
}
LTSH_FUNCTION_EXPORT(cmbdump,"dump hard fault infomation")
int erase(char argc,char ** argv)
{
FLASH_EraseInitTypeDef EraseInitStruct;
uint32_t sectorerr;
HAL_ICACHE_Disable();
/* Unlock the Flash to enable the flash control register access *************/
HAL_FLASH_Unlock();
EraseInitStruct.TypeErase = FLASH_TYPEERASE_SECTORS;
EraseInitStruct.Banks = cmt_get_bank(CMT_FLASH_BASE_START);
EraseInitStruct.Sector = cmt_get_sector(CMT_FLASH_BASE_START);
EraseInitStruct.NbSectors = 1;
HAL_FLASHEx_Erase(&EraseInitStruct, §orerr);
/* Lock the Flash to disable the flash control register access (recommended
to protect the FLASH memory against possible unwanted operation) *********/
HAL_FLASH_Lock();
HAL_ICACHE_Enable();
return 0;
}
LTSH_FUNCTION_EXPORT(erase,"erase test flash")
功能验证
我们使用之前集成的测试命令触发hard fault,发现异常信息已经按照预期的写入了flash了。
异常信息存储到flash ,我们可以添加命令dump 异常信息至串口方便 我们查看之前发生的死机现场,用于定位异常问题。
支持一下