[信息] STM32H5擦写FLASH时意外触发hardfault

[复制链接]
107|0
STM新闻官 发表于 2025-11-2 09:12 | 显示全部楼层 |阅读模式
1. 问题描述 客户反馈在使用STM32H503RB进行flash编程时,在flash擦写之前读取当前扇区Sector时,意外触发hardfault。 2. 问题复现  使用NUCLO-H503RB 和STM32CubeH5 提供的例程“FLASH_EraseProgram”,并没有复现问题,要求客户提供出现问题的代码。对比客户代码和例程,发现在客户代码中,客户为了更高的代码执行效率,使能了ICACHE :

  1.   if (HAL_ICACHE_Enable() != HAL_OK)
  2.   {
  3.     Error_Handler();
  4.   }
在例程中,可以看到会在flash的编程之前,关掉了ICACHE ,在完成flash的所有操作之后,才再次使能ICACHE。
  1.   /* Disable instruction cache prior to internal cacheable memory update */
  2.   if (HAL_ICACHE_Disable() != HAL_OK)
  3.   {
  4.     Error_Handler();
  5.   }


  6. 操作flash


  7.   /* Re-enable instruction cache */
  8.   if (HAL_ICACHE_Enable() != HAL_OK)
  9.   {
  10.     Error_Handler();
  11.   }
按照客户的设置,去掉关掉ICACHE的部分,问题复现。

3. 问题分析
从手册来看, STM32H503RB是dual-bank,支持read-while-write,意思是说它支持在一个bank中读取代码,而在另外一个bank中擦 /写flash。手册中也没有提到,在擦/写flash时一定要关掉ICACHE。 使用能复现问题的代码,进行单步调试发现,进入hardfault的时候,应用代码并不是在调用擦除flash 或者编程flash的时候,而是在查找要编程的flash地址所在的扇区时。
  1. FirstSector = GetSector(FLASH_USER_START_ADDR);

  2. 函数原型:
  3. /**
  4.   * [url=/u/brief]@brief[/url]  Gets the sector of a given address
  5.   * @param  Addr: Address of the FLASH Memory
  6.   * @retval The sector of a given address
  7.   */
  8. static uint32_t GetSector(uint32_t Address)
  9. {
  10.   uint32_t sector = 0;

  11.   if((Address >= FLASH_BASE) && (Address < FLASH_BASE + FLASH_BANK_SIZE))
  12.   {
  13.     sector = (Address & ~FLASH_BASE) / FLASH_SECTOR_SIZE;
  14.   }
  15.   else if ((Address >= FLASH_BASE + FLASH_BANK_SIZE) && (Address < FLASH_BASE + FLASH_SIZE))
  16.   {
  17.     sector = ((Address & ~FLASH_BASE) - FLASH_BANK_SIZE) / FLASH_SECTOR_SIZE;
  18.   }
  19.   else
  20.   {
  21.     sector = 0xFFFFFFFF; /* Address out of range */
  22.   }

  23.   return sector;
  24. }
表面上看只是仅仅读取一下宏定义的值,并将它们进行一些简单运算或者判断,它不应该产生hardfault。为什么会进入hardfault呢? 再进一步查看这些宏定义,如下:
1. #define FLASH_BASE                         FLASH_BASE_NS
2. #define FLASH_BASE_NS                  (0x08000000UL) /*!< FLASH (up to 128 KB) non-secure base address       */
3. #define FLASH_BANK_SIZE               (FLASH_SIZE >> 1U)
4. #define FLASH_SIZE                          ((((*((uint16_t *)FLASHSIZE_BASE)) == 0xFFFFU)) ? FLASH_SIZE_DEFAULT : \                                                           ((((*((uint16_t *)FLASHSIZE_BASE)) == 0x0000U)) ? FLASH_SIZE_DEFAULT : \                                                          (((uint32_t)(*((uint16_t *)FLASHSIZE_BASE)) & (0xFFFFU)) << 10U)))
5.  #define FLASH_SECTOR_SIZE          0x2000U
6. #define FLASH_SIZE_DEFAULT         (0x20000U)
7. #define FLASHSIZE_BASE          (0x08FFF80CUL) /*!< Flash size data register base address  */   

  从这些宏中来看,似乎也没有什么特别的地方,理论上也不应该产生hardfault,但是如果指定一个扇区(sector)进行擦除和编程flash,它的确不会产生hardfault,只有通过地址在查找某个扇区(sector)之后,在进行擦除或编程,才会进入hardfault。

从上面的结果来看,问题应该还是出在了对这些宏定义的地址读取出问题,再次分析这些宏,可以看到除了“FLASH_BASE_NS ”和“FLASHSIZE_BASE”,其它都是一些常量值,应该不会导致这个问题,我们尝试在代码中去逐个读取两个地址的值,当在读取“FLASHSIZE_BASE”时,产生了hardfault。那为什么读取“FLASHSIZE_BASE”寄存器会跟ICACHE有关呢? 我们查看参考相关flash和ICACHE的相关章节进行查找,发现在flash 章节中有如下的描述:
158176906afb7f1df3.png
所以,对于FLASHSIZE_BASE(0x08FFF80CUL)它是 属于RO区,在读取时,是不能使能Cache的。   同时我们也注意到这个RO区域还包括一些其它的宏定义,大家需要注意,调用的时候记得关掉CACHE。        #define PACKAGE_BASE           #define UID_BASE                #define FLASHSIZE_BASE          (0x08FFF80EUL) /*!< Package data register base address    (0x08FFF800UL) /*!< Unique device ID register base address */  */  (0x08FFF80CUL) /*!< Flash size data register base address  */ 当然,除了直接关闭Cache,我们也可以通过设置MPU,将这部分地址区域设置为Non-cacheable 的属性,这样即使开启了Cache,在读取RO区域时也是未经过cache,以Non-cacheable 的方式直接读取的。
475296906afcf86e6d.png
4. 总结 在ST的MCU带cache的型号中,在遇到一些异常情况(比如,内存不刷新,两次运行效果不一致,hardfault等等),我们可以先关掉相关Cache,看是否可以恢复正常,然后再一步一步查找问题。
588486906afe33327d.png
您需要登录后才可以回帖 登录 | 注册

本版积分规则

认证:意法半导体(中国)投资有限公司
简介:您的嵌入式应用将得益于意法半导体领先的产品架构、技术、多源产地和全方位支持。意法半导体微控制器和微处理器拥有广泛的产品线,包含低成本的8位单片机和基于ARM® Cortex®-M0、M0+、M3、M4、M33、M7及A7内核并具备丰富外设选择的32位微控制器及微处理器。

1428

主题

1758

帖子

25

粉丝
快速回复 在线客服 返回列表 返回顶部