linker command file
把数据和指令进行分类,每一类地址在哪里存放(首地址是多少),用多少空间来存放,地址范围是多少
为指令和数据分配空间的文件就是CMD文件
1.通过伪指令定义段
2.给段分配存储空间
多个.c源文件都会被编译成对应的.obj目标文件(有各种不同的段),把不同obj文件中的相同的段链接在一起,就形成了.out文件。
C语言生成的段分为两个部分:
已初始化的段,有真实的指令和数据,存放在程序存储空间。
未初始化的段,保留变量的存储空间,在调用crt0之前没有什么真实的内容, 存放在数据存储空间。
常见的已初始化段:
.text 汇编指令代码
.cinit 存放对全局和静态变量初始化
.const const声明字符串常量,全局,静态变量
.econst 由far const声明字符串常量,全局,静态变量
常见的未初始化段:
.bss 为全局和局部变量保存的空间。就是.cinit中的数据复制到.bss中
.ebss 为使用大寄存器模式时预留的空间,在程序上电的时候,也是cinit中的数据复制到.bss
中,库文件选择.ml后缀的。largememory。库文件在CCS安装目录的C2000 cgtools lib
.stack 堆栈函数的传递变量和局部变量,每个段就是一个空间,不同的段里存放的是不同的数据。
用伪指令来定义段:
#pragma CODE_SECTION(symbol,"section name").为代码定义段
#pragma DATA_SECTION(symbol,"section name").为数据定义段
symbol可以是函数名,也可以是全局变量名,不能在函数体内用上面两条语句定义段。在symbol定义和使用之前一定要为其定义段。
举例:将全局变量s[100]单独编译成一个新的段,取名为“newsect”;
#pragma DATA_SECTION(s,“newsect”);
unsigned int s[100];
void main()
{
...
}
.OBJ文件打开可以看到是由各个不同的段组成的。将各个不同目标文件中的相同段链接就可以形成一个可执行文件.out文件。
CMD文件的编写:(写注释的话只支持/**/,不支持//),目的如下:
1.指示存储空间
2.分配段到存储空间
步骤:
第一步:用MEMORY伪指令来指示存储空间
MEMORY
{
PAGE0:
name0[]:origin=const,length=const;
PAGEn:
namen[]:origin=const,length=const;
}
name后面的【】可以什么都不写,表示可以执行许多操作。理论中PAGE的取值可以到255,实际中只用了0和1.
PAGE0用来存放程序的,是一个程序空间。PAGE1是用来存放数据的,是一个数据空间。
第二步:通过SECTIONS伪指令来把段分配到存储空间:
SECTIONS
{
name:[property,property...]
name:[property,property...]
}
property可以是:
load,语法是load=allocation(load>allocation) 指示装载到哪里,allocation可以是一个代号,也可以是一个绝对地址。
run,语法是run=allocation(run>allocation) 指示从何处开始运行。
PAGE,指示段是分派到数据空间还是分配到程序空间。
看DSP28_GlobalVariableDefs.c这个文件编译后将各个寄存器名和段名对应起来。
再来看.cmd的文件,有分配到ram的也有分配到Flash的。调试选择ram相关的.cmd文件(CCS中的File,load program)。固化到flash里面的时候,选择和flash相关的cmd文件(CCS中的 Tools)。
寄存器文件的空间分配:
首先,将DSP的寄存器按照C语言中位域定义和寄存器结构体的方式组织了数据结构,当编译时,编译器会把这些变量分配到存储空间中,但是很显然还有一个问题需要解决,就是如何将这些代表寄存器数据的变量同实实在在的物理寄存器结合起来。这个工作要两步完成。
第一步:使用DATA_SECTION的方法将寄存器文件分配到数据空间中的某个数据段。
编译器产生可以重新定位的数据和代码模块,这些模块就称之为段。这些段可以根据不同的系统配置分配到响应的地址空间,各段的具体分配方式在CMD文件中有定义。在采用硬件抽象层设计方法的情况下,变量可以采用“pragma DATA_SECTION”命令分配到特殊的数据空间。
#pragma DATA_SECTION(symbol,"section name");
其中,symbol是变量名称,而section name是数据段名。下面以变量SciaRegs为例,将这两个变量分配到名字为SciaRegsFile的数据段。
#pragma DATA_SECTION(SciaRegs,"SciaRegsFile");
volatile struct SCI_REGS SciaRegs;
之后CMD文件会将每个数据段直接映射到响应的存储空间里。
第二步:在CMD文件中,将这个数据段直接映射到这个外设寄存器所占用的存储空间。
在SRAM.CMD文件中,将SCI寄存器文件结构分配到响应的存储空间
MEMORY
{
...
...
PAGE 1:
SCI_A :orgin=0x007050,length=0x000010 这个是SCIA实实在在存在的物理存储器
...
...
}
SECTIONS
{
...
...
SciaRegsFile :>SCI_A, PAGE=1 将SciaRegsFile映射到上面的那个实在的物理存储器中
...
...
}
这样就可以将寄存器文件通物理寄存器相结合了。这样就可以使用我们自己定义的数据结构(上面的那个SciaRegsFile,就是自己定义的一个结构体),来对实实在在的物理存储器中的内容进行操作。
在一个工程里面:
头文件:
基本上都是在各个外设的对应的头文件中,采用位域(一个寄存器中各个位的功能),结构体+联合体(为了既可以对整体操作也可以对各个功能位单独操作),结构体(一个外设是由多个何其设置相关的寄存器组成的)
DSP20_GlobalVariableDefs.c 在采用硬件抽象层设计方法的前提下,利用pragma将自己定义的结构体作为symbol,和对应的段(数据段,程序段,这些对应的段就是symbol编译以后所占用的存储器的别名)关联起来。
CMD文件中,首先给不同的物理空间分割,并取个标号。然后将段分配到物理空间的标号中。