STM32H7读Flash出错-ECC校验出错

STM32H743,Read,CRC

Updated on June 23, 2022 Posted by elmagnifico on June 14, 2022

Forward

之前使用的是不带ECC的Flash,升级到H7以后,发现了读取Flash卡死的情况,并且卡死地址非常随机

类似例子

https://community.st.com/s/question/0D50X0000Bq9fV9SQI/hard-fault-when-reading-flash

https://community.st.com/s/question/0D53W00000Jkw8ZSAR/stm32h7-flash-write-returns-ok-yet-hard-fault-during-readback

https://community.st.com/s/question/0D53W00000DJj2tSAD/stm32h743-random-hard-fault-while-reading-flash

https://community.st.com/s/question/0D53W00001OQ3kbSAD/reading-from-flash-causes-hardfault

https://community.st.com/s/question/0D50X00009XkhJbSAJ/hard-fault-writing-flash

发现一个奇怪问题,H系列的比例怎么这么高,有鬼

常见原因分析

  1. MPU保护,这个比较常见经常是读写了被保护的区域导致出错了
  2. 中断冲突,其实不太可能,写Flash的时候就挂起了整个系统了,中断都不响应了
  3. 多核冲突,具有多个核心的板子,可能会在读写的时候,另一个核心也在做相反或者类似的操作,会造成冲突
  4. Cache,SCB Enable以后可能数据Cache或者指令Cache和DMA等一起使用造成了冲突
  5. Flash延迟错误,这种配置Flash Delay不正确,现在基本不太可能,都是Cube直接生成了

平常基本就是这几种可能,不过在我这里都直接排除了,他们发生的条件都不存在。

发生问题的地方非常有特点,是在PVD的记录区域出现了Flash Read以后HardFault,PVD记录区域是当系统断电的时候自动向Flash写入一条记录。由于是断电时写入,所以有一些trick的东西。

正常写入都会先解锁Flash,先读回来,存起来,擦除Flash,再写入,写完再上锁。但是断电的时候时间不够用,所以会直接写入,而不进行擦除操作,更不可能还做什么读回来存起来这种操作了。这种方式在F7及以下的各种芯片里都可以正常工作,而不会引起大问题。

H7这里就开始出问题了,大概率H7的Flash有什么不一样的设定。

ECC

image-20220614105107703

对比F7的,明显多了一条ECC,H7是40nm工艺,制成越小,不带ECC就无法保证可靠性。

目前能做到的是1bit纠正,2bit检测,每32字节,10bit记录值。

image-20220614110113749

分析

由于缺少了Erase操作,这就导致PVD写入后,出现Flash写入了,但是ECC区域没有写入,当上电以后,自动会读取一遍PVD记录区域,这就造成读的时候发现ECC校验不正确,进而进入到HardFault

image-20220614110926635

image-20220614111717869

Debug

image-20220623173441150

调试后,发现Flash SR2中DBECCERR2确实被置位了,那么就是ECC计算出错了,剩下就是解决这个问题就行了

处理

image-20220614112345152

如果出现了ECC错误,自动擦除对应的区块就能解决了,但是实际上处理起来发现一堆问题。

首先想到直接使能ECC ERROR中断,在中断中进行擦除即可

image-20220623173614752

  • 设置中断位需要先解锁flash,然后才能设置,同理擦除中断标记也是

默认ECC_IRQHandler应该是没定义的,所以要定义一个(刚开始以为是FLASH_IRQHandler中断,后来发现不是)

   .weak      ECC_IRQHandler
   .thumb_set ECC_IRQHandler,Default_Handler
void ECC_IRQHandler(void)
{
	//do something
}

然而实际测试下来,发现根本进不去ECC_IRQHandler,还是触发了HardFault_Handler

反复测试好几次发现都不能触发ECC_IRQHandler,仔细看了一下手册,发现说同时还会触发BusFault_Handler

image-20220623174406597

然而实际上BusFault_Handler也没触发,还是先触发HardFault_Handler,唯一有关于HardFault_Handler的地方就这么点说明

image-20220623174222896

无奈,不能通过正确的途径直接处理了,那只好在HardFault_Handler里额外判定一下是否发生了Ecc错误,发生了的话就直接擦除对应flash

.global HardFault_Handler
.type HardFault_Handler, %function
HardFault_Handler:
    BL      check_ecc_error
    BL      faultinfo_init          /* init faultinfo, so info can write to flash */
    MOV     r0, lr                  /* get lr */
    MOV     r1, sp                  /* get stack pointer (current is MSP) */
    BL      cm_backtrace_fault
    BL      faultinfo_finit         /* mainly lock the flash */

由于我这里必然是bank2发生这个问题,所以只判断了SR2的ecc错误

void check_ecc_error()
{ 
    // ecc error
    if(FLASH->SR2&(1<<26))
    {
      erase_flash();
    }
}

对于没有ECC功能的F系列,函数就直接留空了,测试了一下擦完以后重启,就又能正常工作了。

但是出现Ecc错误,没法优先响应相关中断,感觉也很奇怪,不知道有没有办法禁止ecc校验或者优先响应对应中断。

其他问题

同时也发现官方库还是不够完善,ECC相关的设置或者寄存器在库中就根本没有。

无论是HAL库还是LL库,连寄存器都没有define,更别说对应的操作API了。

官方的Example中也是只有3个简单的例子,仿佛ECC功能完全不存在式的。

CubeMX中直接不存在Flash相关的寄存器设置或者选项

Summary

唔,FLASH里面还是有不少小细节的

Quote

https://blog.csdn.net/qq_37868856/article/details/120704379

https://www.armbbs.cn/forum.php?mod=viewthread&tid=86777