STM32H7读Flash出错-ECC校验出错

STM32H743,Read,CRC

Views:  times Updated on November 9, 2023 Posted by elmagnifico on June 14, 2022

Foreword

之前使用的是不带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

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

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

分析

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

Flash出错如有没有正确处理就会是一个bus错误,手册中没有详细说明具体是什么bus错误。

Debug

image-20231108170232816

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

处理

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

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

image-20231108160119950

设置中断,需要先解锁配置,然后再设置标记位

默认ECC_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-20231108170512441

然而实际上BusFault_Handler 确实触发了,但是我把他转为HardFault_Handler来处理了

无奈,不能通过正确的途径直接处理了,那只好在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校验或者优先响应对应中断。

正常Flash中断

后来发现实际ECC出错了 触发的是Flash中断而不是ECC中断,ECC是给SRAM中出错使用的

正确触发Flash中断如下,就可以解决ECC出错的问题了

// 解锁配置
HAL_FLASHEx_Unlock_Bank2();
// 打开中断
__HAL_FLASH_ENABLE_IT_BANK2(FLASH_IT_SNECCERR_BANK2|FLASH_IT_DBECCERR_BANK2);
// 上锁
HAL_FLASHEx_Lock_Bank2();

// 开启中断
HAL_NVIC_SetPriority(FLASH_IRQn, 2, 0);
HAL_NVIC_EnableIRQ(FLASH_IRQn);

// 添加对应的中断函数处理
void FLASH_IRQHandler(void)
{
    HAL_FLASH_IRQHandler();
}

这里依然出现了新的问题,发现FLASH_IRQHandler有时候是不会触发的,会直接触发到bus error

image-20231108231055784

官方文档中只有这两种其他错误,但是实际上这两种我都不涉及

只能在HardFault_Handler里额外加一个检测,如果是ecc问题,解决了就返回到之前的地方继续运行

HardFault_Handler:
    STMDB   r13!,{r0-r12,r14}
    BL      check_ecc_error
    CMP     R0,1
    BEQ     Quit
Fault_Loop:
    BL      Fault_Loop              /* while(1) */
Quit:
    LDMIA   r13!,{r0-r12,r14}

ECC出错地址

image-20230725193252529

ECC出错以后,会自动将出错地址写入ECC_FA1R或者ECC_FA2R中,但是这里很明显,看到了一个0x4000的地址,这个地址到底是啥意思,翻遍了手册没有说明。ST官方论坛上也搜不到具体说明,这就很难受了。

其次这个0x4000地址,是指实际出错的读取地址,但是这里出错实际读取的至少也是0x080x开头的地址,而不可能是这个位置

image-20230727163320395

其他问题

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

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

Summary

MCU越高级,细节也越多了

Quote

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

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