【DSP学习笔记】CMD文件的讲解
一、前言
在笔者学习F28335的过程中,发现网上少有对于cmd文件的讲解,而学习DSP,肯定是要编写或修改cmd文件的。故笔者基于自己的学习经验,给出了自己对于cmd文件的理解。
在正式开始学习cmd文件之前,我们首先需要知道什么是cmd文件。cmd文件即链接命令文件(Linker Command Files),以后缀.cmd结尾。cmd文件用于DSP代码的定位,由于DSP编译器的编译结果是未定位的,因此需要用户自定义代码存放和加载位置。而普通的单片机,编译器自身会定位代码地址,故编写cmd文件,也是学习DSP的难点之一。
二、有关cmd文件的基础知识
由于cmd文件是用户用来分配DSP工程中ROM和RAM空间的,所以我们就不得不提一下存储器的相关知识了。而各类存储器基本都可以划分为以下两类:断电后仍然能够保存数据的叫做非易失性存储器(ROM类),断电后数据丢失的叫做易失性存储器(RAM类)。故我们分配存储空间时,就需要将需要永久保存的数据存储到ROM中去,如程序代码。而如果对读写速度要求更高或是暂存数据,则选用RAM,如程序运行时,为了提高速度,就必须在RAM中运行。
DSP的片内存储器很多,用户通过cmd文件来管理、分配系统里的存储器资源。值得一提的是,只要没有被TI占用的,用户都可以全权支配,TI声明的保留空间(Reserved或illegal)是芯片无法访问的,分配资源时,不能涉及这些区域。
cmd文件由MEMORY(即:内存)和SECTIONS(即:段)两部分组成。MEMERY用于定义每个存储器块的名字、起始地址和长度。SECTIONS主要用于描述哪个段映射到了哪段存储空间。MEMORY中又可分为PAGE0(程序存储空间)和PAGE1(数据存储空间),PAGE(即:帧)。
上文所提及的段,又可分为两大类:已初始化的段和未初始化的段。已初始化的段含有真实的指令和数据,存放于程序存储空间。未初始化的段只是保留变量的地址空间,未初始化的段并不具有真实的内容,在程序运行过程中才向变量内写数据进去,存放于数据存储空间。C语言中,有许多定义好的段,如“.text”,“.const”,“.system”。对于这些定义好的段,在网上有许多关于他们的讲解,故这里笔者不再赘述。本文接下来会给读者介绍作为用户,来自己定义段的方法。
三、MEMORY和SECTION
cmd文件中可以写上注释,用"/*"和“*/”,包围起来,但不允许使用“//”,这点与c语言不同。
编写cmd文件我们需要借助两条伪指令MEMORY和SECTIONS(必须大写)。
MEMORY和SECTION的语法可在自行网上查找,本文将结合具体例子对MEMORY和SECTION中的内容进行讲解。
结合笔者使用的F28335的cmd文件对MEMORY进行讲解。


可以看到MEMORY中通常包含PAGE0和PAGE1,PAGE0中的RAML0代表起始地址为0x008000,存储空间长度为0x001000的存储空间。同理可知其他存储空间名称所代表的含义。
对照TI28335芯片数据手册(仅截取了部分)可以看到,以上cmd文件的编写是基于TI28335芯片数据手册内存映射一节所编写的。我们也可参考芯片数据手册上的内存映射一节进行cmd文件的编写。

接下来,笔者对SECTION所包含的内容进行讲解,同样以F28335的cmd文件为例

可以看到SECTION中包含了各种段名。以“.text”为例 ,“.text” 为编译后生成的二进制指令代码段,可以看到,我们将“.text”中的内容分配到FLASHA中存储,而FLASHA位于MEMORY中的PAGE0。
SECTION中的ramfuncs与28335的启动有关,其本质就是上电运行时通过“引导程序”把用户代码从FLASH中读出,保存在RAM中并在RAM中运行,从而解决ROM读写速度慢,难以满足高速智能芯片和RAM掉电丢失数据的问题。
四、自定义段
而知道了段的这些信息对于我们用户来说有什么用呢?最直接的用处就是,当编译器提示存储器内存不足时,我们可以通过对应的段名,找到对应的存储空间,修改其存储空间的大小来满足我们程序的需要。甚至我们可以通过自定义段名来存放我们的代码和数据。
通过#pragma DATA_SECTION(函数名或全局变量名,"用户自定义在数据空间的段名")或#pragma CODE_SECTION(函数名或全局变量名,"用户自定义在程序空间的段名")可实现自定义段名,从而自由的分配存储空间。
#pragma DATA_SECTION(用于变量)
#pragma CODE_SECTION(用于函数)
但使用以上指令时需注意:不能在函数体内声明必须在定义和使用前声明,#pragma可以阻止对未调用的函数的优化。
下面结合实际使用例子来具体讲解:
#pragma DATA_SECTION(FFT_output, "FFT_buffer1");
float FFT_output[FFT_SIZE];
笔者声明了一个数据段,段名为FFT_buffer1,段的内容在变量FFT_ouput里。而声明后才定义变量FFT_output的大小。
我们如果想要使用这个自定义的段,接下来我们还要在CMD文件的SECTION中指定FFT_buffer1的存储空间。
FFT_buffer1 : > RAML4, PAGE = 1
通过以上几条语句,笔者实现了将变量的内容存放入指定的RAML4存储空间的操作。
从上可以得出,当全局变量所占内存过大时,我们可以通过自定义段选择有所余裕的存储空间的方式,从而来解决内存不足的问题。
至此,关于CMD文件的相关基础内容笔者介绍完毕。
由于笔者水平有限,如有不足与错误之处,欢迎指出。