CMD文件介绍(后续再整理完善)
链接命令文件(Linker Command Files),以后缀.cmd结尾,简称为CMD文件。CMD文件的两大主要功能是指示存储空间和分配段到存储空间,CMD文件其实也就是由这两部分组成,下边分别进行介绍:
1、通过MEMORY伪指令来指示存储空间。
MEMORY伪指令的语法如下:
MEMORY
{
PAGE0:name0[(sttr)] :origin = constant,length = constant
PAGEn:namen[(sttr)] :origin = constant,length = constant
}
其中:PAGE 用来标识存储空间的关键字。PAGEn的最大值为PAGE255.F28335的DSP中用的是PAGE0、PAGE1,其中PAGE0为程序空间,PAGE为数据空间。
Name 代表某一属性或地址范围的存储空间名称。名称可以是1~8个字符,在同一个页内名称不能相同,不同页内名称能相同。
Attr 用来规定存储空间的属性。共有4个属性,分别用4个字母来表示,只读R,只写W,该空间可包含可执行代码X,该空间可以被初始化I。实际使用时,为了简化起见,通常会忽略此选项,表示存储空间具有所有属性。
Origin 用来定义存储空间的其实地址。
Length 用来定义存储空间的长度。

2、通过SECTIONS伪指令来分配存储空间。
SECTIONS指令的语法如下:
SECTIONS
{
Name : [property,property,property,…]
Name : [property,property,property,…]

}
其中:name为输出段的名称;property为输出段的属性,常用的属性如下:
①load:定义输出段将被装载到哪里的关键字,其语法如下:
load = allocation 或者 allocation 或者 >allocation
allocation可以是绝对地址,例如“load = 0x000400”;当然,更多的时候allocation是存储空间的名称,这也是最为通常的用法。
②run:定义输出段从哪里开始运行的关键字,器语法如下:
run = allocation 或者>allocation
CMD文件中规定,当只出现一个关键字load或者run时,表示load地址和run地址是重叠的。实际应用中,大部分的load地址和run地址都是重叠的。除了.const段。
③输入段。其语法如下:
{ input_sections }
花括号“{}”中是输入段名。这里对输入段和输出段做一个区分,每一个C语言文件经过编译都会生成若干个段,多个汇编语言或C语言文件生成的段大都是同名的,常见的的段有如.cinit、.ebss等,这些都属于输入段。这些归属于不同文件的输入段,在CMD文件的指示下,会被链接器链接在一起生成输出段。
④PAGE:定义段分配到存储空间的类型。其语法如下:
PAGE = 0或者PAGE = 1
当PAGE = 0,说明段分配到程序空间;而当PAGE = 1,说明段分配到数据空间。

项目中的CMD文件
项目中DSP28335的CMD文件共有两个,分别为“DSP2833x_Headers_nonBIOS.cmd”和“F28335_FLASH.cmd”。
DSP2833x_Headers_nonBIOS.cmd
“DSP2833x_Headers_nonBIOS.cmd”文件中主要是将代码中的寄存器变量与F28335芯片内部外设寄存器资源地址一一映射。
下面以外设ADC模块为例详细说明一下该CMD文件的功能作用,打开TI官方的CMD文件“DSP2833x_Headers_nonBIOS.cmd”,在里面找到关于ADC模块的代码,摘录如下:
MEMORY
{
PAGE 0: ADC : origin = 0x007100, length = 0x000020
}

SECTIONS
{
AdcRegsFile : > ADC, PAGE = 1
}
·MENORY伪指令指出ADC是一个起始地址为0x007100、长度为0x000020的存储空间;SECTIONS伪指令,将段AdcRegsFile装载到名字为ADC的空间,即起始地址为0x007100、长度为0x000020的地址空间。而AdcRegsFile段在TI库内“DSP2833x_GlobalVariableDefs.c”文件找到相关定义,如下:
#pragma DATA_SECTION(AdcRegs,“AdcRegsFile”);
Volatile struct ADC_REGS AdcRegs;
结构体ADC_REGS根据F28335的硬件手册定义所有ADC模块的寄存器,#pragma伪指令将ADC寄存器结构体变量AdcRegs分配到段"AdcRegsFile"内。
总结上述结论:C代码根据F28335的数据手册,将其所有外设寄存器(如:ADC)以结构体(如:ADC_REGS)的形式打包并以C语言的形式定义;并将相关模块寄存器结构体变量(如:AdcRegs)分配到具体的段内(如:ADC),最后通过CMD将段映射到F28335内部该外设模块的实际地址上。

F28335_FLASH.cmd
“F28335_FLASH.cmd”文件主要是将程序的代码段及数据段映射到F28335的RAM或Flsh等存储空间中。MEMORY伪指令指示的存储空间分两大类,分别为代码段和程序段。
PAGE0为存放代码段的存储空间,主要有RAMM0、RAML0、FLASHE_B、BEGIN、CSM_PWL等存储空间。
PAGE1为存放数据段的存储空间,主要有BOOT_RSVD、RAMM1、RAML1_7、ZONE6、ZONE7。
SECTIONS伪指令将编译器生成的段以及C和汇编编译的段分配到相应的存储空间。涉及到段的存储特性如表x.x所示。
表 6 7 段的存储属性
段 存储类型 分配的存储空间
.text ROM/RAM(Flash) PAGE0
.cinit ROM/RAM(Flash) PAGE0
.const ROM/RAM(Flash) PAGE1
.econst ROM/RAM(Flash) PAGE1
.pinit ROM/RAM(Flash) PAGE0
.switch ROM/RAM(Flash) PAGE0/ PAGE1
.bss RAM PAGE1
.ebss RAM PAGE1
.stack RAM PAGE1
.sysmem RAM PAGE1
.esysmem RAM PAGE1
通过#pragma CODE_SECTION定义的段 ROM/RAM(Flash) PAGE0
通过#pragma DATA_SECTION定义的段 RAM PAGE1
C语言生成的段
为了更好的理解上表中段的意义,下边简要介绍一下C语言生成的段。C语言生成的段可以分为两大类:已初始化的段和未初始化的段。已初始化的段含有真实的指令和数据,存放在程序的存储空间。未初始化的段只是保留变量的地址空间,在DSP上电调用_c_int0初始化库前,未初始化的段并没有真实的内容。未初始化的段存放在数据存储空间。
1、已初始化的段
.text 编译C语言中的语句时,生成汇编指令代码存放于此。
.cinit 存放用来对全局和静态变量初始化的常数。
.const 包含字符串常量和全局变量、静态变量(由const声明)的初始化及说明。
.econst 包含字符串常量和全局变量、静态变量(由far const声明)的初始化及说明。
.pinit 全局构造器(C++)程序列表。
.switch 存放switch语句产生的常数表格。
2、未初始化的段
.bss 为全局变量和局部变量保留的空间。在程序上电时,.cinit空间中的数据复制出来并存储在.bss空间。
.ebss 为使用大寄存器莫实时的全局变量和静态变量保留的空间。在程序上电时,.cinit空间中的数据复制出来并存储在.ebss空间。
.stack 为系统堆栈保留的空间,主要用于与函数传递变量或为局部变量分配空间。
.system 为动态存储分配包保留的空间。如果有宏函数,此空间被宏函数占用;如果没有此空间保留0。
.esystem 为动态存储分配包保留的空间。如果有far函数,此空间被宏函数占用;如果没有此空间保留0。
上边介绍的段都是C语言预先定义好的段,而作为开发人员如果要定义段,需要用C语言中保留的预处理命令#pragma。#pragma的语法格式如下:
#pragma CODE_SECTION(symbol,“section name”);
#pragma DATA_SECTION(symbol,“section name”);

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐