
勘误手册中的 “SRAM Write Error”
在 STM32G474 的勘误手册 ES0430 的第 2.2.7 章节中提到 SRAM Write Error 问题,具体内容如下:
图1. 勘误手册 ES0430 中对 SRAM Write Error 问题的说明 对于大于 32kbytes 的 SRAM,它的每 32kbytes 空间对应一个 SRAM 状态机,对于SRAM 独立空间但没有超过 32kbytes 的空间,比如 SRAM2,CCM SRAM,都对应各自的一个 SRAM 状态机。正是由于这个原因,在 workaround 中需要对每个空间进行一次状态机的恢复操作。以确保每个 SRAM 状态机都能正常工作。 芯片复位系统起来后,即使存在“SRAM Write Error”,对 SRAM 的第一次读操作是没有问题的(对 SRAM 的读操作只能恢复对应这个地址段的 SRAM 的状态机,不影响其他地址段的 SRAM 状态机),并且这次读操作可以恢复属于这个地址段的有问题的 SRAM 状态机到正常状态;如果复位后的 SRAM 的第一次操作是写操作,则发生“SRAM Write Error”时对SRAM 的第一次写操作会出错。但之后对 SRAM 的操作由于状态机的恢复都不会存在读写问题。
发生“SRAM Write Error”问题的 SRAM 空间地址段即为对这个 SRAM 进行写入同时又异常复位的 SRAM 空间。 在软件工程文件中会有一个 Startup code ,比如 STM32G474 的 IAR 工程会有startup_stm32g474xx.s 文件,处理器从复位起来后首先执行的是这部分代码: 图2. 初始化代码 复现问题需要在 Reset_Handler 的开始就去对 SRAM 进行循环写入的动作,可以使用汇编代码或者是 C 代码,C 代码可以放置在 SystemInit 函数中,但要注意不要进行函数的调用(函数调用会压栈,即对栈所在的 32kbytes 区域进行了写入,这部分空间对应的 SRAM 状态机会恢复到正常状态(存在问题的情况下),SRAM 问题就不能复现了)。 在对 SRAM 进行写入的同时对 MCU 进行 NRST 的复位操作就可以复现问题,复现概率大概是千分之一左右,取决于代码运行主频、异常复位信号产生的频率等因素(这个 NRST 信号来自于另外一块开发板或是信号发生器)。 对写入数据的判断要放置在数据写入之后。 2.1. STM32G474 的 workaround 代码 在 ES0430 2.2.7 章节中提到 workaround,下面的代码就是处理器复位后需要首先执行 的代码,放置 Reset_Handler 开始的地方: 图3. UVISION 工程使用的 workaround 代码 图4. STM32CUBEIDE 工程使用的 workaround 代码 图5. IAR 工程使用的 workaround 代码
(1)正在运行的处理器发生异常复位的概率比较低。然后发生复位时 SRAM 正处于写入阶段的概率会更低一些,再者发生复位时 SRAM 正在写入并恰巧引发“SRAM Write Error”的概率又进一步降低。 (2)对于一般的编译器,都有自动生成的 Startup code,在这部分代码中会对不带初值的全局变量(SRAM 地址空间)进行初始化(写 0),使得有问题的 SRAM 状态机转为正常(“SRAM Write Error”恰巧出现在这部分地址段)。 (3)在普通应用中,代码执行到 main()函数之前要运行 startup code,第一次压栈的数据直到代码运行到用户自己的 main 函数中时也永远不会有弹出的情况。因此即使第一次对栈空间进行写入发生 SRAM write error 造成写入错误,也不会对这部分 SRAM 地址段造成影响。 (4)在 167945 (bugzilla) 里,提到如下的问题复现概率: /the problem arises when the product is reset (NRST signal) during a phase of writing into SRAM. I had a nucleo board constantly writing into SRAM while other nucleo was toggling its NRST pin. The first board was logging the cases when the reset was causing the problem. The test rig counted 188571 resets of STM32G4 to get 100 errors./ (5)下面 3.1 章节的一种情况会产生“SRAM Write Error”可能影响系统运行。 在有些特殊情况下,比如在客户处发现的一个 Keil 工程,第一次对 SRAM 的写入动作出现在 startup code 代码中的一处压栈操作中,并且这个压栈的数据在跳转到 main.c 之前存在出栈操作,这样的压栈数据是有用的,“SRAM Write Error”问题的存在会对程序运行造成风险(客户代码和跟踪情况无法在此处展示)。 3.1. 实验一(IAR 工程):为了验证上面的第 2 点、第 3 点和第 5 点,做了如下的实验 使用 STM32G474 开发板:Nucleo_G474RE PC 端编译调试软件: IAR 9.40.1 使用的软件工程为:…\STM32Cube_FW_G4_V1.5.0\Projects\NUCLEO-G474RE\Examples\GPIO\GPIO_IOToggle 需要修改 main.c 代码如下:
对于 SRAM 空间:0x20010000, 属于不带初值的全局变量,startup code 会进行写“0”操作。 对于 CCM SRAM 空间:0x10000500,属于带初值的全局变量。产生“SRAM Write Error”时,可能会有错误的初值。反汇编代码跟踪如下: Trace information:
由于存在断点,系统运行起来后会停在: “Reset_Handler”,此时栈指针指向 0x20000428 ,然后先执行 SystemInit:
单步跟踪代码,发现第一次对 SRAM 的操作为压栈。见下图第一次压栈:
单步运行一步后, 栈指针为 0x20000420,R0 和 R1 中的数据入栈。请留意这是对 SRAM 的写操作,之后程序运行到 main()函数后一直不会返回,这个值一直会在栈中,因此这个 SRAM 地址段即使发生了“SRAM Write Error”也不会对程序造成不好的影响,并且能使有问题的 SRAM 状态机恢复到正常状态 :
之后继续跟踪代码(代码在 Startup code 中),寻找每个 SRAM 状态机地址段的第一次读出或是写入的位置。 执行的代码流程大概如下: 如果对应的 MCU 有 FPU,iar_program_start 首先会调用iar_init_vfp 对 FPU 进行初始化:然后iar_program_start 会调用cmain:
cmain 首先会调用low_level_init: cmain 然后会调用iar_data_init3 进行全局和静态变量的初始化:
iar_data_init3 首先会调用iar_zero_init3 进行初始值为 0 的全局和静态变量的初始化: 然后iar_data_init3 会调用iar_copy_init3 进行初始值为非 0 的全局和静态变量的初始化: 最后__call_main 会调用 main 函数跳转到 main 函数: 至此 MCU 从复位向量开始,运行启动代码之后就跳转到 main 函数,然后开始运行用户的代码: 跟踪代码对 SRAM 的操作,下面图片是对没有初始值的全局变量的初始化(写入 0), 此位置对应的 SRAM 状态机也能转为正常(如果存在 SRAM Write Error): pragma location=0x20010000char Data1[16];
对 0x20010000 初始化完成后的情况(都清零):
然后全速运行到对 0x10000500 的初始化(此案例中是运行到断点处),由于是带初值的,因此第一次对这个地址段的 SRAM 的操作为写入操作。 pragma location=0x10000500char Data4[4]= {0x11, 0x22, 0x33, 0x44};
单步运行:
单步运行:
单步运行:
单步运行:
单步运行:
单步运行:
单步运行:
单步运行:
单步运行:
由于之前的 0x44332211 已经写入,并且程序复位起来后仍然存在,在再次写入时会影响代码的跟踪和判断,因此我们先给它写入其它区别于 0x44332211 的值。 直接在 memory 窗口中的 0x10000500 地址中写入 0xaaaaaaaa:
然后再激活反汇编窗口(使用鼠标在反汇编窗口中单击一下),并单步运行,就可以看到0x44332211 被写入到地址为 0x10000500 的 CCM SRAM 中。 在这个工程中,对于 CCM SRAM 空间来说,属于第一次写入操作,如果存在“SRAM Write Error”问题,那么程序继续运行下去可能会运行不正常,因为一个全局变量拥有了异常的初始值。 如果之后程序跑飞并且客户开启了硬件看门狗,那么看门狗复位后程序才能正常运行(已经有了第一次写入动作,SRAM 状态机已经正常了)。
然后程序继续运行,一直运行到 Main();:
我们可以看到,进入 main 函数后,之前压栈的数据是不会弹出的,在这个工程中 main 函数永远不会返回,因此对于处于此处栈空间的 32kbytes 地址段,即使存在“SRAM Write Error”问题,系统也能正常运行。 3.2. 实验二(Keil 工程):使用 Keil 工程进行实验 使用 STM32G474 开发板:Nucleo_G474RE PC 端编译调试软件: Keil V5.40 使用的软件工程为:…\STM32Cube_FW_G4_V1.5.0\Projects\NUCLEO-G474RE\Examples\GPIO\GPIO_IOToggle Keil startup code 运行情况,Keil 工程比 IAR 工程存在“SRAM Write Error”的风险要高一些,因为 Keil start up code 会先对带初值的全局变量赋初值,之后才是全局变量的清零操作。如果栈空间和这个带初值的全局变量的空间不在同一个 SRAM 区间(32kbytes 大小的空间),就会存在问题。 需要修改 main.c 代码如下:
Keil 软件工程的跟踪: *.map 文件部分内容: Global Symbols
在汇编窗口中跟踪代码运行,下图中程序从 reset 起来,刚开始运行:
Keil 第一次对 SRAM 进行写入的动作发生在压栈的时候。压栈之前:
压栈完成的状态:
之后继续跟踪代码: image.png 继续跟踪代码的运行:
下来是进行 test 全局变量的加载:
如果这部分全局变量空间和栈空间不属于同一个 32Kbytes SRAM 空间,则在 SRAM 第一次写入如果出错,会造成代码运行问题。
继续跟踪代码,接下来是是包含对 test0 等的清零操作:
很大的一块区域被清零:
之后跳转到 main.c 中,之前压栈数据一直没有使用。在栈所处的 32Kbytes SRAM 空间,即使第一次 SRAM 写入出错,也不会有问题。
对于不想添加这部分代码的客户,又担心存在“SRAM Write Error”问题对系统造成影响;可以结合*.map 文件中全局变量在 SRAM 中的具体地址进行分析,并跟踪 starup code 代码的运行,看“SRAM Write Error”是否会对程序运行造成问题,如果没有问题,可以不添加 workaround 部分的代码。 对于安全性要求较高的应用,建议采用 ES0430 2.2.7 章节的 workaround 代码来提升系统的安全性。 对于使能了 Parity check 的 SRAM,不管有没有可能出现“SRAM Write Error”都建议在代码开头用软件初始化整个 SRAM 内存,以避免读取未初始化的位置时出现奇偶校验错误。
图6. 使能了 Parity check 的 SRAM 区,建议在程序运行开始处对整个 SRAM 区进行初始化
原文链接:https://blog.csdn.net/2501_92678806/article/details/149740096 |
经验分享 | 基于STM32G4芯片不同程序区的跳转话题
经验分享 | 利用TIMER测量信号周期及占空比的精度话题
经验分享 | Flash 全片自检过程中巧用 Linker 自定义变量
经验分享 | 一个 STM32U5 SBSFU SecureFault 问题定位与分析
经验分享 | 高精度定时器如何实现 PWM 零满占空比的连续切换
经验分享 | 定时器触发ADC时可能遇到的几种情形
STM32总结概述
实战经验 | 1小时在STM32MPU上运行YOLOv8——训练篇
经验分享 | STM32CubeMX 生成时钟获取函数的分析
经验分享 | STM32双定时器+ADC+DMA实战案例