DSP之CMD文件
用户希望将某一段,尤其是自定义段,放在什么存储器的什么位置,这也是链接器不知道的。为了告诉链接器,即将使用的芯片其内部存储空间的分配和程序各段的具体存放位置,这就需要编写一个配置文件,即CMD文件了。
CMD是链接器配置文件,指明连接器如何链接obj、lib等文件,生成可执行文件(.out)。
cmd文件是使用TI芯片很重要的一步,了解它,能对整个软件系统的分布,对芯片本身资源的使用有一个更清晰的了解,从而达到优化系统的目的。
CMD文件一句话来表达:定义芯片内部硬件资源和分配管理软件代码的一个配置文件。因此从cmd文件的组织上你能看到两个部分:1定义硬件资源,2管理软件代码,用户通过编写CMD 文件,来管理、分配系统中的所有物理存储器和地址空间。
CMD文件里面最重要的就是两段,即由MEMORY和SECTIONS两个伪指令指定的两段配置。简单的说,MEMORY就是用来建立目标存储器的模型,而SECTIONS指令就是根据这个模型来安排各个段的位置。
功能:
指示存储空间
分配段到存储空间
修改链接配置,如:
① -l test.lib链接文件
② -o test.out重定义生成可执行文件名称
③ -m test.map 重定义生成map文件名称
④ -stack 0x1000 栈大小0x1000字节
⑤ -heap 0x10000 堆大小为0x10000字节
注:在CMD文件中修改配置会覆盖工程配置
CMD文件其实就是用户的“声明” ,包括两方面的内容:
1、用户声明的整个系统里的存储器资源。无论是DSP 芯片自带的,还是用户外扩的,凡是可以 使用的、需要用到的存储器和空间,用户都要一一声明出来:有哪些存储器,它们的位置和大小。如果有些资源根本用不到,可以视为不存在,不必列出来;列出来也无所谓。
2、用户如何分配这些存储器资源,即关于资源分配情况的声明。用户根据自己的需要,结合芯片的要求,把各 种数据分配到适当种类、适当特点、适当长度的存储器区域
MEMORY /* #2 声明存储空间 */{PAGE 0 ://PAGE 0程序存储空间VECS : origin = 0000h , length =0040h /* 中断向量 */ /* #3 */PROG : origin = 0100h , length =7F00h /* 片上 FLASH */ /* #4 */PAGE 1 ://PAGE1 数据存储空间B2 : origin = 0060h , length =0020h /* DARAM B2 块 */ /* #5 */B0B1 : origin = 0200h , length =0200h /* DARAM B0 块 */ /* #6 */SARAM : origin = 0800h , length = 0800h /* SARAM 块 */ /* #7 */ExtSRAM : origin = 8000h , length = 8000h /* 外部存储器 */ /* #8 */}/**********************************************************************************************/SECTIONS /* #9 分配段到存储空间 */{.vectors : > VECS PAGE 0 /* 中断向量表 */ /* #10 */.vectors代表输出段, >VECS同样可以写成load=VECS或VECS,和load平级的还有run,run定义输出段将会在哪里运行,语法run=VECS或run>VECS,当只出现一个run或load时表示地址相同.text : > PROG PAGE 0 /* 代码 */ /* #11 */.cinit : > PROG PAGE 0 /* #12 */.bss : > SARAM PAGE 1 /* #13*/这个其实是.bss load = SARAM PAGE1{*.(bss)}的简写。表示输入端和输出段同名.stack : > B0B1 PAGE 1 /* #14 */.extdata : > ExtSRAM PAGE 1 /* #15 */ }/**********************************************************************************************/
1 资源清单
如上文所述,CMD 文件包含两大内容,首先就是存储器的资源清单,或者说,系统中 (电路板上)可用的存储器资源。TI 规定,CMD文件的资源清单用关键字“MEMORY”作为标识,具体内容写在后面的大括号{ } 里面。如下面的形式:
MEMORY{PAGE 0:xxx : org = 0x1234 , length = 0x5678 /*This is my house.*/PAGE 1:aaa : org = 0x1357 , length = 0x2468 /*My home here.*/}
其中,MEMORY,PAGE n,org,length,包括冒号、等于号、花括号,都是关键字符,必不可少。PAGE n表示把可用的资源空间再划分成几个大块,最多允许分256块,从PAGE0到PAGE 255。很多关键字,还允许有别的写法,比 如“org”可以写为“o” ,“length”可以写为“len” 。这些规定和其他细节,可以去查阅TI 的pdf 文档。
2 资源的分配
首 先,SECTIONS,PAGE,包括花括号、冒号,都是关键字符。注意:SECTIONS字符是复数形式。在花括号内,每一行最左侧的“.vectors” 、“.text” 、“.cinit” 、“.bss” 、“.stack”这些名称,包括小数点,都是TI 默认的关键字符,只有“.extdata”是用户自己定义的名称。另外,“VECS”、“PROG” 、“SARAM” 、“B0B1” 、“ExtSRAM”必须是在MEMORY 里声明过的资源名称。
段分为两类:已初始化段 (Initialized Sections)和未初始化段(Uninitialized Sections)。以下是常用的一些段(不全):
“.vectors” , 表示“中断向量段”
“.text” , 编译后生成的二进制指令代码段
“.cinit”段,“对全局变量和静态变量初始化的常数” 。
“.bss” ,“保存全局变量和静态变量”,它属于“未初始化的”段,
“.stack”,堆栈,它属于“未初始化的”段,定位在数据空间。
“.extdata”,用户自定义的段, 属于“未初始化的”
注意:
1.主要有PAGE0和PAGE1,PAGE0上的memory可以overlay到PAGE1 and so on
2. origin和length都是22bit的常数,在以前的一些dsp由于它的地址总线是16bit的,所以相应的origin和length只能为16bit的常数。
3. Sections以name开始,name就是定义的输出段。
4. load的格式有: load=allocation or allocation or >allocation
5. run的格式有:run = allocation or run>allocation
比如text代码一般应该放在flash内,而bss的变量应该放在ram内。
//###########################################################################
// 文件: F28035.cmd
// 说明: F28035连接命令文件
//###########################################################################
/*
定义F28035内存块的起始地址及长度
PAGE 0 为代码段
PAGE 1 为数据段
注意:
F2803x的内存块对于PAGE 0和PAGE 1是共用的。
同一个内存块不能同时定义为PAGE 0和PAGE 1。会造成程序的混乱。
L0内存块是被镜像的:可以在高地址内存或都低地址内存被访问。
在这里只使用低地址内存。当需要一片很大的的内存块时,SARAM内存块
或者FLASH段可以连接在一起。
*/
MEMORY
{
PAGE 0: /* 程序内存块 */RAML0 : origin = 0x008000, length = 0x000800 /* 片上 RAM block L0 */RAML1 : origin = 0x008800, length = 0x000400 /* 片上 RAM block L1 */OTP : origin = 0x3D7800, length = 0x000400 /* 片上 OTP */FLASHH : origin = 0x3E8000, length = 0x002000 /* 片上 FLASH */FLASHG : origin = 0x3EA000, length = 0x002000 /* 片上 FLASH */FLASHF : origin = 0x3EC000, length = 0x002000 /* 片上 FLASH */FLASHE : origin = 0x3EE000, length = 0x002000 /* 片上 FLASH */FLASHD : origin = 0x3F0000, length = 0x002000 /* 片上 FLASH */FLASHC : origin = 0x3F2000, length = 0x002000 /* 片上 FLASH */FLASHA : origin = 0x3F6000, length = 0x001F80 /* 片上 FLASH */CSM_RSVD : origin = 0x3F7F80, length = 0x000076 /* FLASHA的一部分. Program with all 0x0000 when CSM is in use. */BEGIN : origin = 0x3F7FF6, length = 0x000002/* FLASHA的一部分. 使用了启动引导FLASH模式 */CSM_PWL_P0 : origin = 0x3F7FF8, length = 0x000008 /* FLASHA的一部分. CSM password locations in FLASHA */
/*
说明
CSM_RSVD是Code Security Module_reserved的意思,
是指当使用代码 安全模块时,origin = 0x3F7F80, length = 0x000076
这个内存块是受保留的reserved。不能放程序代码或者数据块。且必须初始化为0x0000.
CSM_PWL是password locations的意思,即存放密码的地方
BEGIN是程序启动的开始地址。
*/IQTABLES : origin = 0x3FE000, length = 0x000B50 /* 在引导ROM中的IQ数学表 */IQTABLES2 : origin = 0x3FEB50, length = 0x00008C /* 在引导ROM中的IQ数学表 */IQTABLES3 : origin = 0x3FEBDC, length = 0x0000AA /* 在引导ROM中的IQ数学表 */ROM : origin = 0x3FF27C, length = 0x000D44 /* 启动引导 ROM */RESET : origin = 0x3FFFC0, length = 0x000002 /* 启动引导 ROM的一部分 复位地址BROM矢量 */VECTORS : origin = 0x3FFFC2, length = 0x00003E/* 启动引导 ROM的一部分 BROM矢量*/PAGE 1 : /* 数据内存块 */BOOT_RSVD : origin = 0x000000, length = 0x000050 /* M0的一部分, 预留给启动引导 ROM时的栈空间 */RAMM0 : origin = 0x000050, length = 0x0003B0 /* 片上 RAM M0 */RAMM1 : origin = 0x000400, length = 0x000400 /* 片上 RAM M1 */RAML2 : origin = 0x008C00, length = 0x000400 /* 片上 RAM L2 */RAML3 : origin = 0x009000, length = 0x001000 /* 片上 RAM L3 */FLASHB : origin = 0x3F4000, length = 0x002000 /* 片上 FLASH */
}
/*
将各个段分配到内存块.
注意: codestart段在 DSP28_CodeStartBranch.asm中被定义,用于引导FLASH时,重定向代码。
ramfuncs段内的函数会从FLASH被转移到RAM中运行。
*/
SECTIONS
{.cinit : > FLASHA PAGE = 0 /* 初始化的全局变量和static变量表*/.pinit : > FLASHA PAGE = 0 /* 全局对象的构造函数表 C++范畴*/.text : > FLASHA PAGE = 0 /* 可执行代码和常数段 */codestart : > BEGIN PAGE = 0 /* 代码启动段 */ramfuncs : LOAD = FLASHD, /* 将定义到段ramfuncs上的代码,载入到FLASHD */RUN = RAML0, /* 定义到ramfuncs上的代码,复制到RAML0上运行 */LOAD_START(_RamfuncsLoadStart), /* 所要加载程序在Flash里的初始地址 */LOAD_END(_RamfuncsLoadEnd), /* 所要加载程序在Flash里的结束地址 */RUN_START(_RamfuncsRunStart), /* 程序运行的起始地址 */PAGE = 0csmpasswds: > CSM_PWL_P0 PAGE = 0 /* 密码段 */csm_rsvd : > CSM_RSVD PAGE = 0/* 使用代码安全模块时,需要预留的段 *//* 未初始化数据段: */.stack : > RAMM0 PAGE = 1 /* 栈空间*/.ebss : > RAML2 | RAML3 PAGE = 1/* 长调用的全局或static变量,初始化和未初始化变量*/.esysmem : > RAML2 | RAML3 PAGE = 1/* 已初始化的段 */.econst : > FLASHA PAGE = 0 /* 字符串常量和far const定义的全局和静态变量(static const)*/.switch : > FLASHA PAGE = 0/* 存放switch语句产生的常数表格*//* 分配 IQ 数学表区域 : */IQmath : > FLASHA PAGE = 0 /* Math Code */IQmathTables : > IQTABLES, PAGE = 0, TYPE = NOLOAD /* 不载入*/
/*
.reset是由编译器使用的。它包含了C代码的开始地址_c_int00。
使用启动引导ROM的这部分和CPU向量表是不需要的。默认的类型被设置为dsect
*/.reset : > RESET, PAGE = 0, TYPE = DSECT /* DSECT 说明这一块地址并不会真的加载数据,只是链接一下symbol。*/vectors : > VECTORS PAGE = 0, TYPE = DSECT
}
/*说明
编译器生成的包含代码和数据的多个部分,称为段。这个段被分为两个不同的组:初始化了的和没被初始化的。
初始化的部分是由所有的代码,常量和初始化表组成的。下面列出了由编译器产生的初始化段。初始化段
段名 内容 限制
.cinit 初始化的全局变量和static变量表 代码
.const 初始化的全局const变量和static const变量和字符串常量 不超过64K长度
.econst 长调用的常量 数据中的任何地方
.pinit 全局对象的构造函数表 代码
.switch switch语句产生的表 代码或者数据
.text 可执行代码和常数 代码
没初始化的段是由未初始化的变量,堆栈和malloc产生的内存。下表列出了由编译器产生的没初始化段。没初始化段
段名 内容 限制
.bss 全局变量和static变量 不超过64K长度
.ebss 长调用的全局变量和static变量 数据中的任何地方
.stack 栈空间 不超过64K长度
.sysmem malloc函数产生的内存 不超过64K长度
.esysmem far_malloc函数产生的内存 数据中的任何地方已初始化的段:.text,.cinit,.const,.econst,.pinit和.switch..
.text:所有可以执行的代码和常量
.cinit:全局变量和static变量的C初始化记录,
包含未用const声明的外部(extern)或静态(static)数据表
.const:包含字符串常量和const定义的全局和static变量
.econst:包含字符串常量和初始化的全局变量和static变量(由far const)
的初始化和说明,与.const不同的是.const分配范围被限制在低64K
16位数据区,而.econst的分配范围是4M 22位数据区
.pinit:全局构造器(C++)程序列表,注意是全局的构造器。
.switch:包含大switch段声明的列表。Jump tables for large switch statements。
非初始化的段:.bss,.ebss,.stack,.sysmem,和esysmem.
.bss: 为全局变量和局部变量保留的空间,在程序上电时.cinit空间中的数据复制出来并存储在.bss空间中。
.ebss:为使用大寄存器模式时的全局变量和静态变量预留的空间,在程序上电时,
cinit空间中的数据复制出来并存储在.ebss中,与.ebss不同的是.bss分配范围被限制在低64K 16位数据区,
而.ebss的分配范围是4M 22位数据区
.stack:为系统堆栈保留的空间,用于和函数传递变量或为局部变量分配空间。
.sysmem:为动态存储分配保留的空间。如果有宏函数,此空间被宏函数占用,如果没有的话,此空间保留为0
.esysmem:为动态存储分配保留的空间。如果有far函数,此空间被相应的占用,如果没有的化,此空间保留为0.
ramfuncs: LOAD = FLASHD,表示ramfuncs段的装载在PAGA0的FLASHD中。
RUN = RAML0,表示ramfuncs段的运行地址在PAGE0的RAML0中
LOAD_START(_RamfuncsLoadStart),
令编译器创建了一个变量RamfuncsLoadStart,该变量指向段
ramfuncs的装载地址的首地址。
LOAD_END(_RamfuncsLoadEnd),
令编译器创建了一个变量RamfuncsLoadEnd,该变量指向段ramfuncs的装载地址的末地址。
RUN_START(_RamfuncsRunStart),
令编译器创建了一个变量RamfuncsRunStart,该变量指向段ramfuncs的运行地址的首地址。
解释一下什么叫长调用,指的是程序指令的寻指范围。
上面说了绝对调用的寻指范围是16位数据区,长调用的寻指范围是22位数据区 。
*/