***关键字:*STM32H5, data flash, high-cycle data, NMI
问题描述
客户反馈,使用 STM32H563 的 data flash(high-cycle data flash),在还没有写入任何数据之前去读取 data flash, 会触发 hardfault 异常。
1. 问题分析
我们尝试在 NUCLEO-H563ZI 板上重现问题,直接使用 CubeH5 包内的示例工程 :
STM32Cube_FW_H5_V1.4.0\Projects\NUCLEO-H563ZI\Examples\FLASH\FLASH_EDATA_EraseProgram
此示例代码的大体流程如下 :
1> 系统启动后,初始化系统时钟。
2> 配置 OB, 将 FLASH bank1 的最末尾的 8 个 sector 配置为 high-cycle flash.如下红框所示 :

图1. 配置末尾 8 个 sector 为 data flash
3> 擦除所有 8 个 high-cycle data flash 扇区
4> 往 8 个 data flash 扇区写入数据 0xAA55(半字).
5> 最后读出 8 个 data flash 扇区的数据进行检查
这里有一个细节,每次编程只写半字数据,为什么是半字呢?那是因为 data flash 区域每16bit 对应一个 6 位的 ECC 校验。当然你要是写一个 32bit 的字也是可以的。
为了模拟客户的问题,我们将第 4>步骤跳过,即在擦除扇区后,直接读取扇区内数据。最终重现了问题 :

图2. 触发了 NMI 异常
此时查看 FLASH 的 FLASH_ECCDETR 寄存器 :

图3. FLASH_ECCDETR 寄存器
从寄存器内容可看出,此时触发了 EDATA_ECC double error 错误。刚擦除了 data flash不能立即读取吗?于是在参考手册上找到如下对应内容:

如上图参考手册内容所描述,当 data flash 为 virgin word 时(比如刚擦除完,还未写入任何数据), 此时若去访问它,当触发 ECC 错误,只有编程后(no more virgin), ECC 错误才会消失。
至于读取 data flash 时,触发 ECC 错误时,寄存器中显示的内容为什么是 0xf000? 这个在参考手册中也能找到对应内容:

可见,FLASH_ECCDETR 寄存器的 ADDR_ECC 中显示的地址并不是简单地将 flash 地址直接显示,而是有一定的规则。代码中访问的是 0x09000000U 位置就触发了 data flash ECCD 错误,它对应的是 Data area sector 7 的起始位置,如上表所示,对应 0xF000, 此扇区对 ECC 错误记录范围为 0xF000~0xF1FF.
2. 解决方法
知道了原因,再去解决就比较容易了。解决方法有两个:
1> 确保代码中每次读取 data flash 之前必须先写入数据。
2> 屏蔽 ECC 错误
在读取 data flash 之前,执行如下代码:
__HAL_RCC_SBS_CLK_ENABLE();
HAL_SBS_FLASH_DisableECCNMI();
如此一来,虽 ECC 错误仍然存在(已忽略),但不再触发 NMI 中断。
3. 其它
与 data flash 具体类似特性的还有 OTP, 它也是若写任何数据前就去读取其内容也会触发 ECCD 错误,从而导致 NMI 异常。
另外,对于 data flash,访问它要关闭其对应的缓存属性。这个在示例中也有相关代码,比如:


若不如此做,则当访问 data flash 时,将触发 hardfault 中断。

图4. 若不关闭缓存属性,访问 data flash 将触发 hardfault
与这个类似的还有 OTP, 和 readonly data(比如芯片 UID). 若对应地址没关闭缓存直接读取也会触发 hardfault.
