实战经验 | 读取STM32H5的UID触发hardfault问题分析

1. 问题现象

客户反馈在STM32H5中通过代码读取UID时,会触发Hardfault。

2. 问题重现及分析

我手动通过STM32CubeMx生成一个基于STM32H563的测试工程。运行在NUCLEOH563RE板上。在代码中写了一个读取UID函数:

uint32_t deviceserial0, deviceserial1, deviceserial2; 

void ReadUIDTest(void) 
{ 
    deviceserial0 = HAL_GetUIDw0(); 
    deviceserial1 = HAL_GetUIDw1(); 
    deviceserial2 = HAL_GetUIDw2(); 
}

然后在main函数中调用这个ReadUIDTest()函数,发现正如客户所说,触发了hardfault。Keil的Call Stack如下所示:

0.png

▲ 图1 读取UID时触发了Hardfault

这个问题能100%重现问题,于是在Keil调试下查看故障报告窗口:

1.png

▲ 图2 故障报告

上图信息解析如下:

当前触发了Bus Fault中断,由于此中断在代码中并未使能,所以才升级为hardfault。触发Bus Fault中断的原因是数据访问导致的,此数据地址为:0x08FFF800。

通过代码中的宏定义:

#define UID_BASE (0x08FFF800UL) /*!< Unique device ID register base address */

可知,这个地址0x08FFF800刚好是芯片的UID地址。

那么问题来了,为什么读取UID地址就会触发hardfault呢?

查看芯片对应的参考手册RM0481(Rev 4),在第7.3.2节看到如下内容:

2.png

UID所在FLASH区域为RO区域,它默认不支持Cacheable特性的,而在代码中若激活了ICache(通过STM32CubeMx生成的工程默认会默认提示用户开户I-Cache以提高代码效率),那么代码访问AHB存储范围内的所有地址默认都是缓存的,也就是说,这种缓存机制跟UID所在特殊区域的缓存特性冲突。要解决这种冲突,得借助MPU来改变局部区域(UID的在地址)的缓存策略才行。

知道了问题所在,那么对应的解决代码如下所示:

/** 
    * @brief Configures the main MPU regions. 
    * @param None 
    * @retval None 
*/
void MPU_Config(void)
{ 
    MPU_Region_InitTypeDef MPU_InitStruct; 
    MPU_Attributes_InitTypeDef MPU_AttributesInit; 
    /* Disable MPU */ 
    HAL_MPU_Disable(); 
    /* Define cacheable memory via MPU */ 
    MPU_AttributesInit.Number = 0; 
    MPU_AttributesInit.Attributes = MPU_NOT_CACHEABLE; 
    HAL_MPU_ConfigMemoryAttributes(&MPU_AttributesInit); 
    /* Configure UID region as Region Number 0 */ 
    MPU_InitStruct.Enable = MPU_REGION_ENABLE; 
    MPU_InitStruct.BaseAddress = 0x08FFF800; 
    MPU_InitStruct.LimitAddress = 0x08FFFFFF; 
    MPU_InitStruct.AccessPermission = MPU_REGION_ALL_RO; 
    MPU_InitStruct.AttributesIndex = MPU_ATTRIBUTES_NUMBER0; 
    MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE; 
    MPU_InitStruct.Number = MPU_REGION_NUMBER0; 
    MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE; 
    HAL_MPU_ConfigRegion(&MPU_InitStruct); 
    /* Enable MPU (any access not covered by any enabled region will cause a fault) */ 
    HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
}

在代码访问UID之前,先用MPU将UID所在地址区域的缓存属性关闭,如此,即可解决问题。

除UID之外,EData,OTP区域也有类似问题,且这种问题在以前传统的MCU中并不会遇到的,这也是STM32H5比较特别的地方之一,大家需要注意这一点。

来源:STM32

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。