打印
[Cortex-M0技术交流]

M0问题之疑问篇 2

[复制链接]
3076|9
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
在nuc 中,如何定位一个变量在Ram的位置?

这个问题的提出,是我想做一个程序,是在ldrom中,设置一个变量参数,启动的时候,放在RAM中。然后在aprom中调用它,


这样,只有通过一个变量地址来实现,但变量是如何定位到这个指定的地址呢?

相关帖子

沙发
john_lee| | 2012-5-9 09:34 | 只看该作者
最标准的做法,是修改链接脚本(mdk叫scatter file,gcc叫ld script),把RAM空间让出一部分,存放需要绝对定位的数据,这部分的地址由你定,一般是在RAM的头部或尾部比较好。
你的程序中,就可以按照这个指定的地址访问数据了。
注意,这部分空间的初始值,你只能认为是随机值,就是未定义的。

使用特权

评论回复
板凳
呆板书生|  楼主 | 2012-5-9 11:16 | 只看该作者
谢谢李老师,,

查了一下,scatter file的用法,准备测试

----------------------------------------------------------------------------
对于嵌入式开发来说,scatter file显得异常重要,尤其是想把某段内容链接到指定的地址区域的时候,这些内容可以是code、const常量和变量。

如果是ARM平台的话,在ARM的linker guide里有详细介绍scatter file的用法。其实DSP程序的开发也会有类似scatter file的东西,记得当时用TI DSP的时候有个叫做cmd文件的东西,里面会要求指定各个段的链接地址,包括起始地址和size。从理论上来说,可以指定每一个变量,每一段代码链接的 位置,当然,其实许多时候并没有必要这样做。但是scatter file确实提供了这样一种精确控制的方法。记得有看过一点tms3202812的例子程序,好像在cmd文件中进行了精细的控制,为每个寄存器定义了一 个名字(一个变量),然后在cmd文件中指定了这个变量的链接链接地址,这样就可以实现操作这个变量即是操作指定的寄存器,用起来很方便。(只是记得是这 样,具体没有仔细看)

在基于ARM的嵌入式开发中,scatter file是一个文本文件,其为linker所用,linker会按照指定的原则来进行链接。为了讲解如何将某个变量或者某段代 码链接到指定位置,我们先来看几个概念。

段(Section):段分为输入段(input section)和输出段(output section),段是连接器操作的基本单位。

输入段(Input Section):输入段作为linker的输入,分布在多个目标文件或者库中。

输出段(Output Section):Linker的输出是一个可执行的映像,在这个映像中各个变量等被链接到指定的地址区域,这些地址区域就是 scatter file中指定的执行区。

段的属性:段的属性有三种,包括Read-Only(简称RO,只读,包括只读的数据和代码)、Read- Write(简称RW,可读写)、Zero-Initialized(简称ZI,初始化为0的可读写数据,通常未初始化的数据也包含在内)。另外段还可以 有一个名字,通过这个名字来区分同一个段的不同部分。

讲完段(section)以后,我们看一下以下几个概念,Image(映像)、Load region(加载区)、Execution region(执行区)。


Image(映像):Linker将目标文件(object)和库(lib)链接之后的输出即是 Image,Image通常是可执行的二进制文件(当然也可能是不可执行的资源文件等)。在Image中通常包含了只读的code和data、初始化的数据。(可以想一下为什么不包含ZI数据?)

Load Region(加载区):在系统上电以后,Image被加载到目标系统中(通常是bootloader将Image搬到RAM中),这个时候Image还没有开始执行(即内核文件还未解压,可以想一下为什么需要解压),此时各个段在RAM中的区域就是Load Region。


Execution Region(执行区):系统上电加载到RAM中以后开始执行,这个时候内核就需要解压。为什么需要解压?之前有讲到Image并没有包含ZI数据,因为ZI数据都是0,只要在解压的时候将这些变量初始化为0就行了,没有必要把这些0值都放在Image里面来占用空间;而RW数据都是初 始化的数据,这些变量如果不保存一个初始值linker就不知道该初始化为多少。内核解压就是要将RW数据和ZI数据在指定的区域进行初始化,初始化为初 始值或者0。这些指定的区域就是Execution Region(执行区)。另外RO(只读的数据和代码)也有可能会搬动。

对于scatter file的用法这里不会做过多介绍,具体内容可以参考ADS Linker Guide,在ARM网站上就有。下面主要讲如何将指定的段链接到指定的地址区域上去。

这里假设我有一个文件叫做example.c,编译结束后会生成目标文件example.obj,我打算将RW数据链接到0x80000000开始的 4KB(0x1000)区域内,将ZI数据链接到0x40000000开始的8KB(0x2000)区域内,RO data和code保留在原有位置不动。这样的设置只要设置scatter file即可达到目的,scatter file如下所示:

ROM 0x0 0x1000000    ;该行指定映像加载区的起始位置为0x0,最大32MB
{
        RAM_A 0x0 0x100000    ;该行指定执行区的起始位置在0x0,最大1MB
        {
                Example.obj (+RO)
                * (+RO)            ;使用通配符*将其余文件的RO段也放在RAM_A区域内
        }
        RAM_B 0x80000000 0x1000    ;RW数据放在2GB开始的4KB区域
        {
                Example.obj (+RW)
        }
        RAM_C 0x40000000 0x2000    ;ZI数据放在1GB开始的8KB区域
        {
                Example.obj (+ZI)
        }
        ……
}

这里面需要注意的一点是ROM和RAM_A的起始位置必须要相同,RAM_A是系统上电后要执行的第一段代码,中断向量表和内核解压的代码也就在这里面。 如果这个区域链接到了其他的位置,那么系统从ROM的起始地址开始执行,第一条指令就不是RAM_A的第一条指令了。

上面的例子是一种简单的情况,如果我有这样一种需求,我想把example.c文件中定义的某个数组和部分的RO Data和Code链接到内部RAM(假设从0×20000000开始的32KB区域),而把其他的RW和ZI变量链接到到外部RAM(假设从 0×40000000开始的32MB区域)。对于这种需求就需要将同一个段中不同的部分进行区分,这就需要为段命名。可按照如下步骤进行操作:

1、在将要链接到INTSRAM_CODE区域的code用下面一对预编译指令包起来

#pragma arm section code = "INTSRAMCODE"

#pragma arm section code
2、在要链接到INTSRAM_CONST区域的常量用下面一对预编译指令包起来

#pragma arm section rodata = "INTSRAMCONST"

#pragma arm section rodata
3、在要链接到INTSRAM_DATA区域的变量用下面一对预编译指令包起来


#pragma arm section rwdata = "INTERNRW"

#pragma arm section rwdata
4、设置scatter file如下所示:

ROM 0x0 0x1000000
{
        RAM_A 0x0 0x100000
        {
                * (+RO)    ;默认情况下所有的code都放在RAM_A中
        }
        INTSRAM_CODE 0x20000000 0x1000    ;内部RAM的前4KB区域放指定的code
        {
                Example.obj (INTSRAMCODE)      ;名为INTSRAMCODE段中code都会放在这里
        }
        INTSRAM_CONST +0x0 0x4000  ;前一个区域之后接着就是16KB的INTSRAM_CONST区域
        {
                Example.obj (INTSRAMCONST)    ;名为INTSRAMCONST的rodata都会放在这里
        }
        INTSRAM_DATA +0x0 0x2000   ;前一个区域之后紧接着就是8KB的INTSRAM_DATA区域
        {
                Example.obj (INTERNRW)  ;名为INTERNRW的rwdata都会放在这里
        }
        EXTRAM 0x40000000 0x1000000    ;起始于0x40000000的32MB外部RAM
        {
                * (+RW +ZI)    ;其他的RW和ZI数据都会默认放在这里
        }
}
5、这样设定后重新编译链接工程即可。

需要注意的几点:

1、上面声明的一些section name,可以任意起名字,只要保持与scatter file中设置相同即可,当然起一些有意义的名字更容易理解。

2、可以将code、rodata、rwdata、zidata组合在一起写,只要将要搬的内容包起来即可,如下所示

#pragma arm section code = "INTSRAMCODE", rodata = "INTSRAMCONST", rwdata = "INTSRAMRW" , zidata = "INTSRAMZI"

#pragma arm section code, rodata, rwdata , zidata
本文结束!

使用特权

评论回复
地板
john_lee| | 2012-5-9 12:14 | 只看该作者
本帖最后由 john_lee 于 2012-5-9 12:22 编辑

uVision对于MDK的scatter file,有图形化的配置支持,非常不错。
具体这样做:
这是默认的Target配置,16K RAM:

例如,你想在RAM尾部分出16个字节的空间,就可以这样配置:

在RAM头部,就这样配置:

注意,要选中IRAM2区域的 NoInit 框,否则 C/C++启动代码可能会把这个区域的数据清除为 0。
然后:

然后,你在一个单独的C文件里,定义你需要共享通信的数据变量,不要设初始值。
例如:
int shared_data;
注意,在这个C文件里,不要再定义其它东西了。
再然后,你需要单独配置这个C文件的选项(在project中的这个文件名上点右键,点菜单“Option for File ....”,出现:

按图配置,然后编译。

使用特权

评论回复
5
呆板书生|  楼主 | 2012-5-10 08:11 | 只看该作者
尝试了几个方法,最后是4楼的方法可行,

不过,链接后会有个警告性提示。

使用特权

评论回复
6
呆板书生|  楼主 | 2012-7-16 07:26 | 只看该作者
Warning: L6340W: options first and last are ignored for link type of scattered

由于选择了部分变量的分开放置,产生一个警告信息

使用特权

评论回复
7
lixiang656| | 2012-7-20 09:08 | 只看该作者
学习!!!

使用特权

评论回复
8
zhouwaiting| | 2012-8-6 17:45 | 只看该作者
很有用,收藏备用

使用特权

评论回复
9
yjcclove| | 2012-10-29 15:33 | 只看该作者
厉害

使用特权

评论回复
10
香如故| | 2012-11-11 14:59 | 只看该作者
学习了、☆1`学习了

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

30

主题

868

帖子

2

粉丝