本文摘抄自网络作者,感谢原作者对CMD文件的通解,获益匪浅!
最近开始研究TI的DSP。初步接触,对CMD文件很有些头疼,经过多方学习,也和TI的一些第三方支持如南京傅立叶等接触学习了一下,终于弄明白的CMD文件的相关信息,特此共享之。
CMD的专业名称叫链接器配置文件,是存放链接器的配置信息的,我们简称为命令文件。从其名称可以看出,该文件的作用是指明如何链接程序的。
那么我们知道,在编写TI DSP程序时,是可以将程序分为很多段,比如text、bss等,各段的作用均不相同。实际在片中运行时,所处的位置也不相同。比如text代码一般应该放在flash内,而bss的变量应该放在ram内。等等。但是对于不同的芯片,其各存储器的起止地址都是不一样的,而且,用户希望将某一段,尤其是自定义段,放在什么存储器的什么位置,这也是链接器不知道的。为了告诉链接器,即将使用的芯片其内部存储空间的分配和程序各段的具体存放位置,这就需要编写一个配置文件,即CMD文件了。
所以,CMD文件里面最重要的就是两段,即由MEMORY和SECTIONS两个伪指令指定的两段配置。简单的说,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,实际应用中一般分为三页,PAGE 0程序存储器、PAGE 1数据存储器和PAGE 2 IO空间存储器。name为自定义的存储区间的名字,不超过8个字符,不同的PAGE上可以出现相同的名字(最好不用,免的搞混),一个PAGE内不许有相同的name。 attr的属性标识,为R表示可读;W可写X表示区间可以装入可执行代码;I表示存储器可以进行初始化,什么属性代码也不写,表示存储区间具有上述的四种属性,基本上我们都选择这种写法。origin标识了该段存储区间的起始地址,而length则是标识了该段存储区间的长度。以2407A为例,根据这个定义,参考2407A的Datasheet,可以得出2407A内存储器的标准描述为:
MEMORY
{
PAGE 0: /* PROGRAM MEMORY */
PM: ORIGIN=0h, LENGTH=08000h /* 32k on-chip flash memory */
SARAM_P:ORIGIN=08000h, LENGTH=0800h /* 2k saram in program space */
EX1_PM: ORIGIN=08800h, LENGTH=07600h /* extern ram*/
B0_PM: ORIGIN=0FF00h, LENGTH=0100h /* on-chip daram if cnf = 1,else
extern B0 =FF00h TO FFFFh*/
PAGE 1: /* DATA MEMORY */
REGS: ORIGIN=0h, LENGTH=60h /* memory mapped registers */
BLK_B2: ORIGIN=60h, LENGTH=20h /* block b2 */
BLK_B0: ORIGIN=200h, LENGTH=100h /* block b0,if cnf=0 */
BLK_B1: ORIGIN=300h, LENGTH=100h /* block b1 */
SARAM_D:ORIGIN=0800h, LENGTH=0800h /* 2K SARAM in data space */
PERIPH: ORIGIN=7000h, LENGTH=1000h /* peripheral registers space */
EX2_DM: ORIGIN=8000h, LENGTH=8000h /* external data ram */
PAGE 2:
IO_EX: ORIGIN=0000h, LENGTH=0FFF0h /* external io mapped peripherals */
IO_IN: ORIGIN=0FFF0h, LENGTH=0Fh /* on-chip io mapped peripheral */
}
有了这个模型。我们就可以定义各个代码段在内存的具体位置了。就是使用SECTIONS伪指令。其格式为:
SECTIONS
{
.text: {所有.text输入段名} load=加载地址 run =运行地址
.data: {所有.data输入段名} load=加载地址 run =运行地址
.bss: {所有.bss输入段名} load=加载地址 run =运行地址
.other: {所有.other输入段名} load=加载地址 run =运行地址
}
SECTIONS必须用大写字母,其后的大括号里是输出段的说明性语句,每一个输出段的说明都是从段名开始,段名之后是如何对输入段进行组织和给段分配存储器的参数说明。以.text段的属性语句为例,“{所有.text输入段名}”这段内容用来说明连接器输出段的.text段由哪些子目标文件的段组成。接下来的load和run,链接器为每个输出段都在目标存储器里分配两个地址:一个是加载地址,一个是运行地址。通常情况下两个地址是相同的,可以认为输出段只有一个地址,这时就可以不加“run =运行地址”这条语句了;但有时需要将两个地址分开,比如将程序加载到FLASH,然后放到RAM中高速运行,这就用到了运行地址和加载地址的分别配置了。
load和run有一些简化写法,首先“load”关键字可以省略,“=”可以写成“>”, “加载地址”可以是:地址值、存储区间的名字、PAGE关键词等,所以大家见到“.text:{ } > 0×0080”这样的语句可千万不要奇怪。“run =运行地址”中的“ = ”可以用“>”其它的简化写法就没有了。大家不要乱用。
由此,一个简单的SECTIONS的代码就是:
SECTIONS
{
vectors:{}> PM PAGE 0
.text: {}> PM PAGE 0
.data: {}> PM PAGE 0
.bss: {}> BLK_B1 PAGE 1
}
这样的话,基本就可以理解CMD文件了。至于CMD的其他高级应用,暂时可以不用关心。
这里还需要补充说明的是:以2407A为例,0×40-0×43是加密位,一般编写CMD时最好错开此位,以免误加密。如果使用我这里的示例代码,那就应该在代码中以.word之类预留出加密位位置。否则一旦误加密,那。。。。本人不承担任何责任。。。呵呵。
|