YTM32B1ME15 MCAL 0.8.0:EEPROM ECC 错误处理
-
环境:
YTM32B1ME15;
Yuntu MCAL 0.8.0;1、问题
EEPROM 单比特 ECC 错误可由硬件纠正;双比特错误不可纠正,实测读取时会触发精确 BusFault,且先于 EEPROM 中断处理。仅在
EEPROM_IRQHandler中处理,会因故障指令反复执行而升级为 HardFault。MCAL 0.8.0 的 Eep 驱动提供 ECC 中断使能接口,但未实现完整的错误处理和日志记录。
2、处理方案
示例在应用层实现:
- 单比特:在 EEPROM 中断中记录错误地址、计数等信息并清除标志,数据由硬件纠正。
- 双比特:通过 RAM 向量表接管 BusFault,识别 EEPROM 不可纠正错误,按 Thumb-2 指令长度跳过故障读取指令,记录错误,并将错误地址按 4 字节对齐后擦除对应字。
- 前提:启动代码已将向量表复制到 RAM;示例在初始化时安装 BusFault 处理函数。
3、验证结果
实板测试中,正常读写、单比特纠错、双比特检测及后续重写恢复四步均通过:
Eep_EccStepResult=0x01010101,Eep_EccTestFailCnt=0。双比特错误地址为0x02000040,程序未再出现 BusFault 升级为 HardFault。


5、注意事项
- 跳过故障加载指令不会产生有效读取值。实测 Eep 作业仍可能返回
MEMIF_JOB_OK,不能仅凭作业结果判断数据有效。 - 擦除会破坏整个 4 字节字的数据,需由应用恢复;示例未向上层报告擦除超时或命令失败,且异常上下文操作 EEPROM 可能与 Eep 作业竞争。量产前须结合芯片手册验证失败处理及数据恢复。
- 错误注入仅用于测试,测试会写入并擦除 EEPROM 测试区域。
6、代码实现
6.1 初始化
main.c 初始化 MCU 时钟,随后 Board_Init 调用 Platform_Init、Fls_Init、Eep_Init 和 Eep_EccDemoInit。启动代码须先将向量表复制到 RAM 并设置 VTOR。
IVT_RAM_start[5U] = (uint32)Eep_BusFault_Handler; __DSB(); __ISB(); SCB->SHCSR |= SCB_SHCSR_BUSFAULTENA_Msk; SCB->CCR |= SCB_CCR_BFHFNMIGN_Msk; EEPROM->STS = EEPROM_STS_UNRECOVERR_MASK | EEPROM_STS_RECOVERR_MASK | EEPROM_STS_CMD_FAIL_MASK | EEPROM_STS_ACCERR_MASK; EEPROM->CTRL |= EEPROM_CTRL_RECOVERRIE_MASK | EEPROM_CTRL_UNRECOVERRIE_MASK; (void)Platform_SetIrq(EEPROM_IRQn, TRUE);6.2 注入与读取
源码使用偏移 0x40、长度 8 字节,先写入测试数据。单比特模式为 1,双比特模式为 2,模式 0 关闭注入;Eep_EccReadJob 通过 Eep_MainFunction 等待作业完成。
EEPROM->ERR_INJECT_CTRL = EEPROM_ERR_INJECT_CTRL_ECCERR_INJ_EN(Mode) | EEPROM_ERR_INJECT_CTRL_ECCERR_INJ_ADDR(Offset); Eep_EccInject(EEP_ECC_INJ_MULTI_BIT, EEP_ECC_TEST_ADDR); Eep_EccJobResultAfterMulti = (uint32)Eep_EccReadJob( EEP_ECC_TEST_ADDR, ReadBuffer, EEP_ECC_TEST_LEN); Eep_EccInject(EEP_ECC_INJ_DISABLE, 0U); Eep_EccPollMultiBit();上述寄存器赋值为非零模式分支摘录。注入仅供测试,示例会写入并擦除 EEPROM 测试区域。
6.3 BusFault 处理
应用的 naked 异常入口根据 EXC_RETURN 位 2 选择 MSP 或 PSP,将堆栈指针传给 Eep_BusFaultHandlerC。当前实现使用 pStack[6] 作为保存的 PC,下面为对应 C 代码摘录。
FaultPc = pStack[6]; if (0U != (EEPROM->STS & EEPROM_STS_UNRECOVERR_MASK)) { if (0x1DU <= (*(volatile uint16 *)(FaultPc) >> 11U)) InsnLen = 4U; else InsnLen = 2U; pStack[6] = FaultPc + InsnLen; Eep_EccHandleBusFault(); } else if (TRUE == Fls_Lld_GetTwoBitEccErrFlag()) { pStack[6] = FaultPc + 2U; } SCB->CFSR = SCB_CFSR_BUSFAULTSR_Msk | SCB_CFSR_PRECISERR_Msk;重要限制:跳过加载指令不会生成有效读取值,也不保证 Eep 作业失败。
6,4 错误记录与对齐擦除
Eep_EccHandleBusFault 读取错误地址与计数,更新日志,关闭注入并清状态后调用 Eep_EccEraseWord。以下摘录省略地址范围校验及状态清理。
ErrAddr = EEPROM->ECCERR_ADDR; Eep_EccLog.MultiBitErrCnt++; Eep_EccLog.LastErrAddr = ErrAddr; Eep_EccLog.LastErrCnt = EEPROM->ECCERR_CNT & EEPROM_ECCERR_CNT_ECCERR_CNT_MASK; Eep_EccLog.LastErrType = EEP_ECC_ERR_TYPE_MULTI; Eep_EccLog.NotifyCnt++; EEPROM->ERR_INJECT_CTRL = 0U; EEPROM->STS = EEPROM_STS_UNRECOVERR_MASK; Eep_EccEraseWord(ErrAddr); WordAddr = HwAddr & (uint32)(~0x3U); WordPtr = (volatile uint32 *)WordAddr; EEPROM->CTRL |= EEPROM_CTRL_WE_MASK; /* 源码在写入前后执行 ISB 和 DSB */ *WordPtr = 0x00000000U; EEPROM->CTRL &= (uint32)(~EEPROM_CTRL_WE_MASK); EEPROM->CMD_UNLOCK = EEP_ECC_CMD_UNLOCK_KEY; EEPROM->CMD = EEP_ECC_ERASE_SECTOR_CMD; /* 轮询 DONE 或等待超时,然后清状态 */擦除会破坏整个对齐字的数据。当前代码未向上层报告擦除超时或命令失败,且在异常上下文直接操作 EEPROM,可能与正在进行的 Eep 作业竞争;产品化前须核对芯片手册并验证失败处理和数据恢复,不能仅凭日志认定擦除成功。
6.5 EEPROM ECC 中断
EEPROM_IRQHandler 根据 STS 处理:单比特分支记录 RECOVERR 并清标志;双比特分支临时关闭 UNRECOVERRIE,记录并调用 Eep_EccEraseWord,最后清状态并重开。双比特读取可能先进入 BusFault。
6b63415c-6681-4394-8366-8aae3e5fef6d-ME15_ECC_EccErrorHandling_Demo.zip
快速上手云途开发生态
发帖前请查看
帮助改进和优化YT CONFIG TOOL,有机会抽取YTM32B1ME0 EVB哦...