超详细的CMD文件讲解(DSP28035)
2019-07-13 10:32发布
生成海报
一、 CMD文件基本概念及语法
CMD的专业名称叫链接器配置文件,是存放链接器的配置信息的,我们简称为命令文件。从其名称可以看出,该文件的作用是指明如何链接程序的。
那么我们知道,在编写TI DSP程序时,是可以将程序分为很多段,比如text、bss等,各段的作用均不相同。实际在片中运行时,所处的位置也不相同。比如text代码一般应该放在flash内,而bss的变量应该放在ram内。等等。但是对于不同的芯片,其各存储器的起止地址都是不一样的,而且,用户希望将某一段,尤其是自定义段,放在什么存储器的什么位置,这也是链接器不知道的。为了告诉链接器,即将使用的芯片其内部存储空间的分配和程序各段的具体存放位置,这就需要编写一个配置文件,即CMD文件了。
所以,CMD文件里面最重要的就是两段,即由MEMORY和SECTIONS两个伪指令指定的两段配置。简单的说,MEMORY就是用来建立目标存储器的模型,而SECTIONS指令就是根据这个模型来安排各个段的位置。
MEMORY 伪指令
MEMORY 用来建立目标存储器的模型,SECTIONS 指令就可以根据这个模型来安排各个段的位置,MEMORY 指令可以定义目标系统的各种类型的存储器及容量。MEMORY 的语法如下:
MEMORY
{
PAGE 0 : name1[(attr)] : origin = constant,length = constant
name1n[(attr)] : origin = constant,length = constant
PAGE 1 : name2[(attr)] : origin = constant,length = constant
name2n[(attr)] : origin = constant,length = constant
PAGE n : namen[(attr)] : origin = constant,length = constant
namenn[(attr)] : origin = constant,length = constant
}
PAGE 关键词对独立的存储空间进行标记,页号 n 的最大值为 255,实际应用中一般分为两页,PAGE0 程序存储器和 PAGE1 数据存储器。name 存储区间的名字,不超过 8 个字符,不同的 PAGE 上可以出现相同的名字(最好不用,免的搞混),一个 PAGE 内不许有相同的 name。attr 的属性标识,为 R 表示可读;W 可写 X 表示区间可以装入可执行代码;I 表示存储器可以进行初始话,什么属性代码也不写,表示存储区间具有上述的四种属性,基本上我们都选择这种写法。下面为例子:
MEMORY
{
PAGE 0 :
BEGIN : origin = 0x000000, length = 0x000002
RAMM0 : origin = 0x000050, length = 0x0003B0
PAGE 1 :
RAMM1 : origin = 0x000480, length = 0x000380
RAML2 : origin = 0x008C00, length = 0x000400
}
SECTIONS 伪指令
SECTIONS 指令的语法如下:
SECTIONS
{
.text: {所有.text 输入段名} load=加载地址 run =运行地址
.cinit: {所有.data 输入段名} load=加载地址 run =运行地址
.bss: {所有.bss 输入段名} load=加载地址 run =运行地址
.other: {所有.other 输入段名} load=加载地址 run =运行地址
} 例如: .text : > RAML0L1, PAGE = 0
SECTIONS 必须用大写字母,其后的大括号里是输出段的说明性语句,每一个输出段的说明都是从段名开始,段名之后是如何对输入段进行组织和给段分配存储器的参数说明:
以.text 段的属性语句为例,“{所有.text 输入段名}”这段内容用来说明连接器输出段的.text 段由哪些子目标文件的段组成,举例如下
SECTIONS
{
.text:{ file1.obj(.text) file2(.text) file3(.text,cinit)}略
}
指明输出段.text 要链接 file1.obj 的.text 和 file2 的.text 还有 file3 的.text 和.cinit。在 CCS 的 SECTIONS里通常只写一个中间没有内容的“{ }”就表示所有的目标文件的相应段接下来说明“load=加载地址 run =运行地址”链接器为每个输出段都在目标存储器里分配两个地址:一个是加载地址,一个是运行地址。通常情况下两个地址是相同的,可以认为输出段只有一个地址,这时就可以不加“run =运行地址”这条语句了;但有时需要将两个地址分开,比如将程序加载到 FLASH,然后放到 RAM中高速运行,这就用到了运行地址和加载地址的分别配置了,如下例所示:
.const :{略} load = PROG run = 0x0800
常量加载在程序存储区,配置为在 RAM 里调用。“load=加载地址”的几种写法需要说明一下,首先“load”关键字可以省略,“=”可以写成“>”, “加载地址”可以是:地址值、存储区间的名字、PAGE 关键词等,所以大家见到“.text:{ } > 0x0080”这样的语句可千万不要奇怪。“run =运行地址”中的“ = ”可以用“>”,其它的简化写法就没有了。大家不要乱用。
自定义段(C 语言)
#pragma DATA_SECTION(全局变量名,"用户自定义在数据空间的段名");
#pragma CODE_SECTION(函数名,"用户自定义在程序空间的段名");
CODE_SECTION用来定义代码段
DATA_SECTION用来定义数据段
例如:在c文件中定义了一个data段my_sect和一个code段ramfuncs,如下:
#pragma DATA_SECTION(bufferB, ”my_sect”)
char bufferB[512];
#pragma CODE_SECTION(dragon_update,"ramfuncs");
Uint16 dragon_update(UPDATE_SOURCE_TYPE *update_flag)
{
。。。
}
然后再在cmd文件中指定这两个section的位置就可以了。
如果想在汇编中指定段,使用方法,在代码前用.sect “XXX”开始则标示接下来的一段代码都是在xxx的代码段中。
例子:
PieVectTableFile : > PIE_VECT, PAGE = 1
#pragma DATA_SECTION(PieVectTable,"PieVectTableFile");
struct PIE_VECT_TABLE PieVectTable;
注意:
不能再函数体内声明#pragma;
必须在符号被定义和使用之前声明#pragma
#pragma 可以阻止对未调用的函数的优化
备注:
CMD 文件中还可以写上注释,用“/”和“/”包围起来,但不允许用“//” ,这一点和 C 语言不同。
可以直接在CMD文件中写编译命令,如:
-l rts2800_ml.lib 连接系统文件rts2800_ml.lib
-o filename.out 最终生成的二进制文件命名为filename.out
-m filename.map 生成映射文件filename.map
二、DSP 28035的内存映射图如下:
三、 烧写FLASH的CMD文件示例及注解(F28035.cmd)
//
MEMORY
{
PAGE 0:
RAML0 : origin = 0x008000, length = 0x000800
RAML1 : origin = 0x008800, length = 0x000400
OTP : origin = 0x3D7800, length = 0x000400
FLASHH : origin = 0x3E8000, length = 0x002000
FLASHG : origin = 0x3EA000, length = 0x002000
FLASHF : origin = 0x3EC000, length = 0x002000
FLASHE : origin = 0x3EE000, length = 0x002000
FLASHD : origin = 0x3F0000, length = 0x002000
FLASHC : origin = 0x3F2000, length = 0x002000
FLASHA : origin = 0x3F6000, length = 0x001F80
CSM_RSVD : origin = 0x3F7F80, length = 0x000076
BEGIN : origin = 0x3F7FF6, length = 0x000002
CSM_PWL_P0 : origin = 0x3F7FF8, length = 0x000008
IQTABLES : origin = 0x3FE000, length = 0x000B50
IQTABLES2 : origin = 0x3FEB50, length = 0x00008C
IQTABLES3 : origin = 0x3FEBDC, length = 0x0000AA
ROM : origin = 0x3FF27C, length = 0x000D44
RESET : origin = 0x3FFFC0, length = 0x000002
VECTORS : origin = 0x3FFFC2, length = 0x00003E
PAGE 1 :
BOOT_RSVD : origin = 0x000000, length = 0x000050
RAMM0 : origin = 0x000050, length = 0x0003B0
RAMM1 : origin = 0x000400, length = 0x000400
RAML2 : origin = 0x008C00, length = 0x000400
RAML3 : origin = 0x009000, length = 0x001000
FLASHB : origin = 0x3F4000, length = 0x002000
}
SECTIONS
{
.cinit : > FLASHA PAGE = 0
.pinit : > FLASHA PAGE = 0
.text : > FLASHA PAGE = 0
codestart : > BEGIN PAGE = 0
ramfuncs : LOAD = FLASHD,
RUN = RAML0,
LOAD_START(_RamfuncsLoadStart),
LOAD_END(_RamfuncsLoadEnd),
RUN_START(_RamfuncsRunStart),
PAGE = 0
csmpasswds: > CSM_PWL_P0 PAGE = 0
csm_rsvd : > CSM_RSVD PAGE = 0
.stack : > RAMM0 PAGE = 1
.ebss : > RAML2 | RAML3 PAGE = 1
.esysmem : > RAML2 | RAML3 PAGE = 1
.econst : > FLASHA PAGE = 0
.switch : > FLASHA PAGE = 0
IQmath : > FLASHA PAGE = 0
IQmathTables : > IQTABLES, PAGE = 0, TYPE = NOLOAD
.reset : > RESET, PAGE = 0, TYPE = DSECT
vectors : > VECTORS PAGE = 0, TYPE = DSECT
}
打开微信“扫一扫”,打开网页后点击屏幕右上角分享按钮