
1. 勘误手册中的“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的操作由于状态机的恢复都不会存在读写问题。 2. 怎样复现此问题 由于编译器生成的代码(startup code)会对全局变量进行初始化,因此要复现这个问题需要在编译器对全局变量进行初始化之前进行问题的复现。 发生“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开始的地方:
▲ UVISION工程使用的workaround代码
▲ STM32CUBEIDE工程使用的workaround代码
▲ IAR 工程使用的workaround代码 3. 问题概率分析 正常情况下出现“SRAM Write Error”问题的概率是非常低的,因此很多客户只有出现问题后对问题进行分析时考虑。“SRAM Write Error”属于非常低的概率事件的具体原因如下:
3.1. 实验一(IAR工程):为了验证上面的第2点、第3点和第5点,做了如下的实验 使用STM32G474开发板:Nucleo_G474RE PC 端编译调试软件:IAR 9.40.1 使用的软件工程为:…\STM32Cube_FW_G4_V1.5.0\Projects\NUCLEOG474RE\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):
对0x20010000初始化完成后的情况(都清零):
然后全速运行到对0x10000500的初始化(此案例中是运行到断点处),由于是带初值的,因此第一次对这个地址段的SRAM的操作为写入操作。
单步运行:
单步运行:
单步运行:
单步运行:
单步运行:
单步运行:
单步运行:
单步运行:
单步运行:
由于之前的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\NUCLEOG474RE\Examples\GPIO\GPIO_IOToggle Keil startup code运行情况,Keil工程比IAR工程存在“SRAM Write Error”的风险要高一些,因为Keil start up code会先对带初值的全局变量赋初值,之后才是全局变量的清零操作。如果栈空间和这个带初值的全局变量的空间不在同一个SRAM区间(32kbytes大小的空间),就会存在问题。https://developer.arm.com/documentation/100748/0618/Embedded-SoftwareDevelopment/Application-startup
需要修改main.c代码如下:
Keil软件工程的跟踪:*.map文件部分内容:Global Symbols
在汇编窗口中跟踪代码运行,下图中程序从reset起来,刚开始运行:
Keil第一次对SRAM进行写入的动作发生在压栈的时候。压栈之前:
压栈完成的状态:
之后继续跟踪代码:
继续跟踪代码的运行:
下来是进行test全局变量的加载: 如果这部分全局变量空间和栈空间不属于同一个32Kbytes SRAM空间,则在SRAM第一次写入如果出错,会造成代码运行问题。
继续跟踪代码,接下来是是包含对test0等的清零操作:
很大的一块区域被清零:
之后跳转到main.c中,之前压栈数据一直没有使用。在栈所处的32Kbytes SRAM空间,即使第一次SRAM写入出错,也不会有问题。
4. 应用代码建议 对于有些客户,代码已经稳定并不再修改,也通过了测试;在产品应用上也能接受看门狗复位(硬件看门狗),可以不添加workaround部分提到的代码。 对于不想添加这部分代码的客户,又担心存在“SRAM Write Error”问题对系统造成影响;可以结合*.map文件中全局变量在SRAM中的具体地址进行分析,并跟踪starup code代码的运行,看“SRAM Write Error”是否会对程序运行造成问题,如果没有问题,可以不添加workaround部分的代码。 对于安全性要求较高的应用,建议采用ES0430 2.2.7章节的workaround代码来提升系统的安全性。 对于使能了Parity check的SRAM,不管有没有可能出现“SRAM Write Error”都建议在代码开头用软件初始化整个SRAM内存,以避免读取未初始化的位置时出现奇偶校验错误。
▲ 使能了Parity check的SRAM区, 建议在程序运行开始处对整个SRAM区进行初始化 5. 小结 根据客户的实际使用情况,可以选择是否使用“SRAM Write Error”的workaround代码。但对于安全性要求较高的应用,还是建议使用SRAM状态机恢复代码(ES0430 2.2.7章节提到的workaround,或此文章的2.1章节),并对开启了Parity check的SRAM区在程序开始运行时进行初始化。 |