切换到宽版
  • 6304阅读
  • 23回复

[ARM]STM32:将运行代码装入RAM中运行问题和实现 [复制链接]

上一主题 下一主题
在线huaweiwx
 

发帖
1235
M币
2784
专家
53
粉丝
232
只看楼主 倒序阅读 我要置顶 楼主  发表于: 2017-10-31

将STM32运行代码将装入RAM中运行,至今没有完整而详细文档和实现代码,我在完善maple、hal 构架的STM32 ARDUINO过程中,根据点滴零碎和有限资料,终于弄明白的其中的奥妙,现在把关键部分在此向坛友作个介绍。

问题一、为何要将运行代码装入STM32的RAM中运行?
A:  STM32代码在RAM中运行比在flash(rom)中运行的速度更快(带有ICACHE/DCATHE的芯片如F4系列除外)(技术贴:GD32F103  vs STM3 性能测试比较 http://bbs.mydigit.cn/read.php?tid=1597532),说明: GD32 ram运行反而比flash运行要慢,以下只对STM32F1系列来说。
我的测试显示:
    内存变量运算10000次加法:                            stm32f103vct6  72M  ram 1112us   flash 1251us

B: 偶尔用一下代码,如果装入flash,用后不擦写干净则将永久占据这个空间,而擦写则磨损flash缩短使用寿命,如果对付很多不确定却频繁这样操作显然对芯片是个考验,而ram则不同,要用那个功能就装入那个功能的代码,ram可反复读写而不影响寿命。如我们调试一段程序,需要反复改,有些需要反复试,显然每次将其放入ram运行是个好主意;

C:  留给ST自己检测用和自家(包括合作大用户)的产品上用(猜测)。
   猜想 a: ST公司在芯片检测时,大部分的检测代码全部是放在ram中运行的,早期的mcu包括arm芯片的擦写次数并不多,如刚推出的LM系列,有报道极端的情形 有玩家数十次甚至数次擦写就死翘翘了;我想ST公司的产品也是如此,早期的手册上的寿命才写1000次,如果几十道工序对每个作个检查,这种一次性行为都写到flash中运行代码,既不必要,也不经济(降低了产品率);
    猜想b:为了自家产品不被轻易的仿制,代码不会轻易的被人取得,不要看现在的stm32 已经有了很多保护功能,早期可没有这么多,这方面都说清楚了,给人就多了些手段,因为我们从那些开源的调试器可见,通过JTAG(SWD)可以向mcu发出写到某个地址的某个数据,芯片内部的bootloader也能接收这样的指令如写 某个地址指定的数据和go某个地址(ram)运行;须知那个时候玩arm的都是高大上,要买个调试器是很贵的!如果手册说得太清楚了,自有聪明好事的把它开源了,这是st和那些合作的大用户不愿看到的。

问题二、STM32能运行RAM中的代码吗?
这显然不是问题,我们在keil中已经这样在ram中调试了,stm32也支持在ram中运行代码,这是ST手册关于启动的:

它告诉我们,boot1/boot0 开机电平的高低决定了mcu从那里开始执行程序:
00/10   就是我们通常写的代码,放在flash的开头处,对于stm32,这个地址所有stm32 mcu都是相同的:0x08000000;
01   就是我们就是从stm32的系统flash开始执行代码,这里st为我们烧了个bootloader的固件,对stm32f1 支持usb烧写和串口烧写,这就是通常所说的在系统编程 isp;而该地址不同产品线的mcu是不同的,其中stm32f1xxxx 是  0x1FFFF000~0x1FFFF800;
11   这个启动模式是我们要关注的,就是从内部ram运行代码;内部ram地址是: 0x20000000;
对于上述表3中列举的三种启动方式,下面的说明还向我们表明了:


A  对应到各种启动模式的不同物理地址将被映像到第0块(启动存储区);
B  即使被映像到启动存储区,仍然可以在它原先的存储器空间内访问相关的存储器。
C  CPU从位于0000_0000h开始的启动存储区执行代码。

上述三个说明,让我脑洞大开,先用我们通俗一点的不是太专业的深刻理解一下:
A  就是说,如果是00/10 用户内存存储器启动模式,flash地址0x080000000开始只读存储器 映射到了0x00000000地址开始,而11内部SRAM启动则0x200000000开始的SRAM映射到了0x00000000地址开始。
B 在00/01启动模式下,仍可读写执行0x08xxxxxx中的数据和代码, 并和读写执行0x00xxxxxx是一回事;在11启动模式下,仍可读写执行0x20xxxxxx 并和读写执行0x00xxxxxx是一回事;也就是说:
在keil中,以xET6为例:图 2

和 图3


编译连接的运行是一样的(ram运行类似);

C 总是从0x00000000启动,只不过,如果flash启动,则也可从0x08000000开始;而SRAM启动,则也可从0x20000000开始运行;
(以上所说的启动地址是指中断矢量表开始地址,实际第一个pc地址是表中系统复位矢量RESET所指向的位置)

好奇,问题三,为何我们找不到一个例子把IROM开始地址直接写成0x00000000?
这样能准确的连接和运行吗?
回答是肯定的,因为我们一试就知道了;

问题四:这样做存在什么问题及限制?
这就是官方没有提供这样做例子的原因,也就是我没有看到有人这样做的原因:
A  烧写文件如果是带地址的如.hex和.dfu;,这个文件烧写时开头地址变成了0,当不在运行模式,0地址开始既没有flahs也没有sram,无法烧入;
但我们知道,还有个 .bin格式的二进制文件,可以在使用stlink或jlink烧写时指定地址,选0x08000000(flash运行)还是 0x20000000(ram运行) ;

问题五:现在我们回到ram运行问题,代码放入ram有什么限制和不足?这到是简单了:
A  显然,ram是易失性,刚上电时,里面本没有东西,即使是我们可以用stlink或jlink将代码装入,但每次要运行,先用slink或jlink装入,麻烦一点;
B  RAM容量远小于flash,代码用用掉一些,剩下能放数据的也就少了,因此装入一些小程序,或对部件一个一个调试。
C 对于AB,如 F103ZET6的有些板子,还外扩了512k或1024k外部Sram;而F4、sram有64k、 128k、196k,F746 有320k,即使是大程序都可以调试;至于装入,看红牛等板子的例子,可以用个小程序(我将它称为用户化bootloader),我们可以从nand/nor/spiflash甚至sd卡上装入并运行;

问题六:还有个问题,为什么至今ST公司都在mcu上保留了问题二中的 11模式(ram)运行?
许多博客、资料总是重复了手册上的一句话,sram启动,就没有下文了,还有吐槽一下:ST给我们一个没有用的鸡肋,这没有用;许多板子干脆把boot1直接接地了。boot1 直接接地,我们仍可以从ram中执行代码,撇开用户自己的bootloader装入并跳转外,我们直接用jlink、stlink在调试时是可以设定栈地址、pc地址和跳转的,从这个角度看确实看是没有用的;
但我就弄不明白:那么为什么至今ST公司都在mcu上保留了sram启动运行模式?使用这个模式一定可以达到通常做不到的事! 下面是我的第一个应用:

问题七:我们能否做成个执行代码,即可用于flash烧写或直接运行,也可装入sram临时用一下?
先说结果:可以生产这样的二进制执行文件:即可烧入flash运行,也可装入SRAM临时用一下!
先看一下官方示例 Sram运行设置:


这个设置将xET6 64kSram分成两个部分,
A         0x20000000开始长度为0x8000(32k)为代码IROM1,剩下的0x20008000 长度为0x8000(32k)为数据段IRAM1;
这是官方及大家都这样用的配置,因为SRAM是绝对地址,因此无论在00/10 模式,都是能正确运行的;也能做成hex文件装入sram,因为地址正确;这个物理地址的代码是不能直接烧入flash运行的,0x20000000开始长度为0x8000(32k)指向sram,而实际放在flash地址错误。
如果改成:

B        0x00000000开始长度为0x8000(32k)为代码IROM1,剩下的0x00008000 长度为0x8000(32k)为数据段IRAM1;
这是我的试验,在11模式启动时,SRAM映射地址,在SRAM模式启动运行也是正确的;

C  B模式的代码显然也不能烧入flash运行,因为:当flash映射时,地址0x00008000 长度为0x8000(32k)为数据段IRAM1,这显然是错误的,因为这区域不可写!
D 我们将地址改为:


即 将A中的代码段IROM1地址改变,而数据段IRAM1不变:
  0x00000000开始长度为0x8000(32k)为代码IROM1,0x20008000 长度为0x8000(32k)为数据段IRAM1;
这将变成无论是flash总的代码还是sram中的代码,根据启动模式的不同,IRAM1始终指向绝对地址0x20008000~0x20010000这个sram物理地址是可读写的,而运行代码总是0x00000000~0x00008000这是映射地址,也是正确的。
这样的配置,无论怎样都能正确运行,而区别只是装入时根据不同的启动模式采用用不同的绝对地址,执行文件是同一个。

(注:在我的下一个版本的 STM32 HALMX ARDUINO中,有个开发者版本,内含了ram调试的生成、装载和运行,一键完成,正在最后调试中)




本文内容包含图片或附件,获取更多资讯,请 登录 后查看;或者 注册 成为会员获得更多权限
本帖最近打赏记录:共7条打赏M币+129
在线huaweiwx

发帖
1235
M币
2784
专家
53
粉丝
232
只看该作者 1楼 发表于: 2017-10-31
顺便说一下,使用GCC(如arduino),则是在连接配置文件 *.ld中用:(RET6为例)
_estack = 0x20010000;    /* 将堆栈开始地址设为 end of RAM */
而内存使用则是:
MEMORY
{
FLASH (rx)      : ORIGIN = 0x8000000, LENGTH = 512K
RAM (xrw)      : ORIGIN = 0x20000000, LENGTH = 64K
}
这样设定,改变ORIGIN=XXXXXX  和 LENGTH=XXXX  就可以设定了。
离线旧宅毛毛

发帖
645
M币
717
专家
3
粉丝
63
只看该作者 2楼 发表于: 2017-10-31
留个标记,有空来学习一下,感谢分享

内容来自Android手机客户端

离线apq

发帖
233
M币
1479
专家
2
粉丝
10
只看该作者 3楼 发表于: 2017-10-31
期盼着spi和i2c。字数补丁

楼主留言:

spi/i2c  我抽空写一个,但你可以试试我在库中的例子,读取spiflash和读取i2ceeprom。
另外,oled 成功没?如没有,你可试试我包中的库:OLED_I2C中的例子,看看能否成功。

离线cao57508

发帖
2190
M币
4137
专家
9
粉丝
74
只看该作者 4楼 发表于: 2017-10-31
很详细的说,让我这个半瓶,非常受益,谢谢分享。
离线amo73

发帖
747
M币
1287
专家
9
粉丝
18
只看该作者 5楼 发表于: 2017-10-31
可能你不认真看手册,心思想复杂了……
ARM是统一编址,程序运行没有RAM、Flash的分别。
RAM相对Flash来说速度没什么优势,麻烦一大堆(你总得先把程序从Flash搬移到RAM、修改SP才能跑,恢复时又要来一遍)。通用的引导程序里会提供,目的是把每1字节的Flash都交给用户——但是实际项目里不会有人这么干,升级过程掉电的话设备就要返厂了;而几十KB甚至几百KB大的Flash根本用不完,留2KB给引导程序完全没问题,还不怕掉电。

至于Flash寿命那更是搞笑,最早出的M3(流明诺瑞)都是至少能擦写10万次的。实际上我用M3十多年还没用坏过MCU——我们的设备都划分一块Flash用于存储参数的,经常擦除、写入。
离线xueyuking

发帖
1263
M币
2389
专家
0
粉丝
18
只看该作者 6楼 发表于: 2017-10-31
好高深的样子,想学。
离线qiuchen0403

发帖
5440
M币
7726
专家
3
粉丝
16
只看该作者 7楼 发表于: 2017-11-01
透露一下,GD的F1系列是两个DIE绑定在一起的,一个是Cortex_M3内核+RAM,一个是串口FLASH。他把RAM做的比较大,在运行时会把FLASH中的内容先load到RAM中,然后执行。所以测试的时候发现GD falsh中运行和ST在RAM中运行效果是一样的,其实针对GD32F1XX来说没有FLASH运行的概念。
这样带来的好处是运行速度快,带来的问题是有32K假死问题。也就是说GD代码RAM空间是32K的,每次从FLASH中装载32K的代码来运行。这样就不难理解了,如果代码执行过程中,有个函数在32K之外,那就必须重新装在代码。而且这个时间是不确定的,可能会达到秒级。

楼主留言:

你所说的32kRAM,是否应该是充当ICACHE用,专用于代码,所以比内部flash还快,以前我看到过一个帖子说数据SRAM是外挂在外部总线上?所以反而要慢些。

本帖最近打赏记录:共2条打赏M币+40
在线huaweiwx

发帖
1235
M币
2784
专家
53
粉丝
232
只看该作者 8楼 发表于: 2017-11-01
回 qiuchen0403 的帖子
qiuchen0403:透露一下,GD的F1系列是两个DIE绑定在一起的,一个是Cortex_M3内核+RAM,一个是串口FLASH。他把RAM做的比较大,在运行时会把FLASH中的内容先load到RAM中,然后执行。所以测试的时候发现GD falsh中运行和ST在RAM中运行效果是一样的,其实针对GD32F1XX来说没有FLASH运行的概念。
这 .. (2017-11-01 00:00) 回 qiuchen0403 的帖子

用pos上那个GD32F103Ret6,我将执行代码放在ram中调试总是一用串口就死,不正常,不知是什么原因,st就没有这个问题。
另外,用串口下载到ram时,代码长度只能小于0x08005000,即20k,去除串口下载从system启动要使用0x08000200前 512个字节,实际运行代码只有20k-512。
[ 此帖被huaweiwx在2017-11-01 00:32重新编辑 ]
本帖最近打赏记录:共1条打赏M币+15
在线2545889167

发帖
12048
M币
9006
专家
274
粉丝
4624
只看该作者 9楼 发表于: 2017-11-01
相当厉害,佩服
快速回复
限80 字节
温馨提示:所有技术区严禁灌水,“沙发”“顶”字样;禁止广告贴;以免被删除
 
上一个 下一个