TMS320F2833x片上Flash和SARAM存储器在逻辑上既可以映射到程序空间,也可以映射到数据空间。到底映射到哪个空间,这要由CMD文件来指定。
CCS生成的可执行文件(.out)格式采用COFF格式,这种格式的突出优点是便于模块化编程,程序员能够自由地决定把由源程序文件生成的不同代码及数据定位到哪种物理存储器及地址空间指定段。
CMD的专业名称叫链接器配置文件,是存放链接器的配置信息的,我们简称为命令文件。从其名称可以看出,该文件的作用是指明如何链接程序的。在编写TI DSP程序时,是可以将程序分为很多段,比如text、bss等,各段的作用均不相同。实际在片中运行时,所处的位置也不相同。比如text代码一般应该放在Flash内,而bss的变量应该放在RAM内等。但是对于不同的芯片,其各存储器的起止地址都是不一样的,而且用户希望将某一段,尤其是自定义段,放在什么存储器的什么位置,这也是链接器不知道的。为了告诉链接器,即将使用的芯片其内部存储空间的分配和程序各段的具体存放位置,这就需要编写一个配置文件,即CMD文件了。
所以,CMD文件里面最重要的就是两段,即由MEMORY和SECTIONS两个伪指令指定的两段配置。简单地说,MEMORY指令就是用来建立目标存储器的模型,而SECTIONS指令就是根据这个模型来安排各个段的位置。
MEMORY伪指令用来表示实际存在目标系统中的可以使用的存储器范围,在这里每个存储器都有自己的名字、起始地址和长度;它告诉链接程序怎样计算和分配空间,根据芯片不同存储空间大小不同。格式如下:
H代表十六进制数,0~F。例如想定义一段Flash存储空间,其长度为64K字,开始位置为0x300000,其格式如下:
需要说明:这里描述存储空间大小时,length=0x020000→64×(2^10)=64K字,即64K×16bit。
DSP的CMD采用分页制(PAGE),其中PAGE0用于存放程序空间、PAGE1用于存放数据空间,PAGE里包含的区间名字与其后面的参数反映了该区间的起始地址和长度。例如,“PAGE0:DARAM0:o=0x00080,l=0x01F80”,o=origin起始地址,l=length长度。
SECTIONS伪指令是用来描述输入端是如何组合到输出端内的。SECTIONS部分,在程序里添加段名.XXXX(如.vectors.)用来指定该段名以下,另一个段名以上的程序(属于PAGE0)或数据(属于PAGE1)放到“>”符号后的空间名字所在的地方。
SECTIONS部分的段可分为初始化段和非初始化段。初始化段包含代码和常数等必须在DSP上电之后有效的数,故初始化块必须保存在如片内flash等非易失性存储器中;非初始化段中在程序运行过程中才向变量内写数据,所以非初始化段必须链接到易失性存储器中,如RAM。
CMD文件中各段含义见表2-2。
表2-2 CMD文件中各段的含义
.vectors,.text,.cinit,.const,.econst,.pinit和.switch。
.vectors:存放中断向量表。
.text:所有可以执行的程序代码和常量。
.cinit:存放程序中的变量初值和常量。
.const:包含字符串常量、浮点常量以及初始化的全局变量和静态变量(由const声明)的初始化和说明。
.econst:包含字符串常量和初始化的全局变量和静态变量(由far const声明)的初始化和说明。
.pinit:全局构造器(C++)程序列表。
.switch:包含switch声明的跳转地址列表。
.bss,.ebss,.stack,.sysmem,和.esysmem
在执行过程中可以被操作和改变,只有在它们被程序调用时才会分配响应的值。
.bss:为程序中全局变量和静态变量保留的空间,在程序上电时,.cinit空间中的数据复制出来并存储在.bss空间中。
.ebss:为使用大寄存器模式时的全局变量和静态变量(由far声明)而预留的空间,在程序上电时,.cinit空间中的数据复制出来并存储在.ebss中。
.stack:为系统堆栈保留的空间,用于和函数传递变量或为局部变量分配空间。
.sysmem:为动态存储分配保留的空间。如果有宏函数,此空间被宏函数占用,如果没有的话,此空间保留为0。
.esysmem:为动态存储分配保留的空间。如果有far函数,此空间被相应地占用,如果没有,此空间保留为0。
存储器映射需要3个步骤:
1)定义存储单元的不同区域(片上SRAM、Flash或者外部存储器),需要注意,使用外部存储器时,需要使用外部存储器接口XINTF。
2)定义段与存储器区域的关联关系。
3)进行.obj文件与.cmd文件的链接。
链接器将输入文件中的每一个段链接起来,然后根据.cmd文件中MEMORY与SECTIONS命令的参数,将相应位置、长度的存储空间分配给段,如图2-1所示。
如果某文件例如.text很大时,编译显示超过内存范围,可以查看.map文件确定每个存储单元的使用情况(或者每个变量的地址及占用的存储空间);解决方法是:可以将其放入两个存储段,使用“>>”及“|”将两个存储段合并。
例如
图2-1 链接代码与存储单元及链接示意
下面是一个简单的CMD文件:
在该CMD文件中,采用MEMORY伪指令建立目标存储器的模型(列出存储器资源清单)。PAGE关键词用于对独立的存储区进行标记。通常的应用中分为两页,PAGE0为程序存储区、PAGE1为数据存储区。RAML1和RAML4为定义的存储区名字,同一个PAGE内不允许有相同的存储区名,但不同的PAGE上可以出现相同的名字。
“o”和“l”分别是origin和length的缩写。origin标识该段存储区的起始地址,length标识该段存储区的长度。有了存储器模型,就可以定义各个段在不同存储区的具体位置了。这要使用SECTIONS伪指令。每个输出段的说明都是从段名开始,段名之后是给段分配存储器的参数说明。
CMD文件可以自己编写,也可由集成开发环境CCS自动生成。