1.
COFF格式
1>
通用目标文件格式(Common Object File Format)是一种流行的二进制可执行文件格式,二进制可执行文件包括库文件(lib),目标文件(obj)最终可执行文件(out)。,现今PC机上的Windows95和NT4.0以后的操作系统的二进制文件格式(PE)就是在COFF格式基础上的进一步扩充。
2>
COFF格式:详细的COFF文件格式包括段头,可执行代码和初始化数据,可重定位信息,行号入口,符号表,字符串表等,这些属于编写操作系统和编译器人员关心范畴。而对于C只需要了解定义段和给段分配空间就可以了。
3>
采用COFF更有利于模块化编程,程序员可以自由决定愿意把哪些代码归属到哪些段,然后加以不同的处理。
2.
Section目标文件中最小单位称为块。一个块就是最终在存储器映象中占据连续空间的一段代码或数据。
1>
COFF目标文件包含三个默认的块:
.text可执行代码
.data已初始化数据
.bss为未初始化数据保留的空间
2>
汇编器对块的处理
未初始化块
.bss
变量存放空间
.usect
用户自定义的未初始化段
初始化块
.text
汇编指令代码
.data
常数数据(比如对变量的初始化数据)
.sect
用户自定义的已初始化段
.asect
通.sect,多了绝对地址定位功能,一般不用
3>C语言的段
未初始化块(data)
.bss
存放全局和静态变量
.ebss
长调用的.bss(超过了64K地址限制)
.stack
存放C语言的栈
.sysmem 存放C语言的堆
.esysmem
长调用的.sysmem(超过了64K地址限制)
初始化块
.text
可执行代码和常数(program)
.switch
switch语句产生的常数表格(program/低64K数据空间)
.pinit
Tables for global constructors (C++)(program)
.cinit
用来存放对全局和静态变量的初始化常数值(program)
.const
全局和静态的const变量初始化值和字符串常数,(data)
.econst 长.const(可定位到任何地方)(data)
3>
自定义段(C语言)
在CCS编程中,如果我们不指定变量的存放位置,编译器会自动的给变量分配一个位置,但是如果有的时候需要把变量放在一个特定的空间内,我们应该如何操作呢,CCS提供了如下的两个指令
#pragma CODE_SECTION
#pragma DATA_SECTION
其中data_section是针对数据空间的,code_section是针对程序空间的,具体的使用办法是
#pragma DATA_SECTION(bufferB, ”my_sect”)
char bufferB[512];
在.cmd文件中建立对应的section就可以使用了。
#pragma DATA_SECTION(函数名或全局变量名,"用户自定义在数据空间的段名");
#pragma CODE_SECTION(函数名或全局变量名,"用户自定义在程序空间的段名");
注意
不能在函数体内声明。
必须在定义和使用前声明
#pragma可以阻止对未调用的函数的优化
3.
连接命令文件(CMD)
它是用来分配rom和ram空间用的,告诉链接程序怎样计算地址和分配空间.所以不同的芯片就有不同大小的rom和ram.放用户程序的地方也不尽相同.所以要根据芯片进行修改.分两部分.MEMORY和SECTIONS.
(1):存储器(MEMORY)伪指令,用来定义目标系统的存储器空间。MEMORY可以定义存储器的区域,并指定起始地址和长度。
(2):段(SECTOINS)伪指令,告诉链接器如何将输入段结合成输出段并告诉链接器将输出段放在存储器的何处。
MEMORY伪指令的一般语法:
MEMORY
{
PAGE 0: name1[(attr)]
origin=constant, length=constant;
PAGE n: name1[(attr)]
origin=constant, length=constant;
}
PAGEn中的页号n最大为255。每个PAGE代表一个完全独立的地址空间。通常PAGE0为程序存储器,PAGE1为数据存储器。
Name1:存储器区间名。可包含8个字符。不同PAGE可以取同样的name1,但在同一个PAGE内区间名不可以相同。
Attr:可选项。规定存储器属性。
R,可以对存储器执行读操作
W,可以对存储器执行写操作
X,破除可以装入可执行的程序代码
I,规定可以对存储器进行初始化
Origin:起始地址。
Length:区间长度。
初始化段用SECTIONS可定位两次:装入和运行。如:一些关键的执行代码必须装在系统的ROM中,但希望在较快的RAM中运行。
未初始化段只可被定位一次。
1、MEMORY是用来指定芯片的rom和ram的大小和划分出几个区间.
例如:
MEMORY
{
VECS:
o = 0x10800000
l = 0x00000200
BOOT:
o = 0x10800200
l = 0x00000200
IRAM:
o = 0x10800400
l = 0x0001FA00
DDR2:
o = 0x80000000
l = 0x10000000
}
其中o和l参数反映了该区间的起始地址和长度。
2、SECTIONS:
在程序里添加下面的段名如.vectors.用来指定该段名以下,另一个段名以上的程序放到“>”符号后的空间名字所在的地方。
例如
SECTIONS
{
.bss
>
IRAM
.cinit
>
IRAM
.cio
>
IRAM
.const
>
DDR2
.data
>
IRAM
.far
>
IRAM
.stack
>
IRAM
.switch
>
IRAM
.sysmem
>
IRAM
.text
>
IRAM
.ddr2
>
DDR2
}
3>
直接写编译命令
-l rts2800_ml.lib
连接系统文件rts2800_ml.lib
-o filename.out
最终生成的二进制文件命名为filename.out
-m filename.map
生成映射文件filename.map
-stack 0x200
堆栈为512字
4. .const段:
由关键字const限定的全局变量(const限定的局部变量不产生)初始化值,和出现在表达式(做指针使用,而用来初始化字符串数组变量不产生)中的字符串常数,另外数组和结构体是局部变量时,其初始值会产生.const段,而全局时不产生。
下面是一个例子
在CMD 文件中:
MEMORY
{
PAGE 0 :
BEGIN
: origin = 0x000000, length = 0x000002
BOOT_RSVD
: origin = 0x000002, length = 0x00004E
RAMM0
: origin = 0x000050, length = 0x0003B0
RAML0
: origin = 0x008000, length = 0x001000
RAML1
: origin = 0x009000, length = 0x001000
RAML2
: origin = 0x00A000, length = 0x001000
RAML3
: origin = 0x00B000, length = 0x001000
ZONE7A
: origin = 0x200000, length = 0x00FC00
CSM_RSVD
: origin = 0x33FF80, length = 0x000076
CSM_PWL
: origin = 0x33FFF8, length = 0x000008
ADC_CAL
: origin = 0x380080, length = 0x000009
RESET
: origin = 0x3FFFC0, length = 0x000002
IQTABLES
: origin = 0x3FE000, length = 0x000b50
IQTABLES2
: origin = 0x3FEB50, length = 0x00008c
FPUTABLES
: origin = 0x3FEBDC, length = 0x0006A0
BOOTROM
: origin = 0x3FF27C, length = 0x000D44
PAGE 1 :
RAMM1
: origin = 0x000400, length = 0x000400
RAML4
: origin = 0x00C000, length = 0x001000
RAML5
: origin = 0x00D000, length = 0x001000
RAML6
: origin = 0x00E000, length = 0x001000
RAML7
: origin = 0x00F000, length = 0x001000
ZONE7B
: origin = 0x20FC00, length = 0x000400
}
SECTIONS
{
codestart
: > BEGIN,
PAGE = 0
ramfuncs
: > RAML0,
PAGE = 0
.text
: > RAML1,
PAGE = 0
.cinit
: > RAML0,
PAGE = 0
.pinit
: > RAML0,
PAGE = 0
.switch
: > RAML0,
PAGE = 0
.stack
: > RAMM1,
PAGE = 1
.ebss
: > RAML4,
PAGE = 1
.econst
: > RAML5,
PAGE = 1
.esysmem
: > RAMM1,
PAGE = 1
IQmath
: > RAML1,
PAGE = 0
IQmathTables
: > IQTABLES,
PAGE = 0, TYPE = NOLOAD
FPUmathTables
: > FPUTABLES, PAGE = 0, TYPE = NOLOAD
DMARAML4
: > RAML4,
PAGE = 1
DMARAML5
: > RAML5,
PAGE = 1
DMARAML6
: > RAML6,
PAGE = 1
DMARAML7
: > RAML7,
PAGE = 1
ZONE7DATA
: > ZONE7B,
PAGE = 1
.reset
: > RESET,
PAGE = 0, TYPE = DSECT
csm_rsvd
: > CSM_RSVD
PAGE = 0, TYPE = DSECT
csmpasswds
: > CSM_PWL
PAGE = 0, TYPE = DSECT
.adc_cal
: load = ADC_CAL,
PAGE = 0, TYPE = NOLOAD
}
在C语言中
#define BUF_SIZE
1024
#pragma DATA_SECTION(DMABuf1,"DMARAML4");
#pragma DATA_SECTION(DMABuf2,"ZONE7DATA");
volatile Uint16 DMABuf1[BUF_SIZE];
volatile Uint16 DMABuf2[BUF_SIZE];
再比如,配置28335从内部flash启动时,
我们预定义
#pragma CODE_SECTION(epwm1_timer_isr, "ramfuncs");
#pragma CODE_SECTION(epwm2_timer_isr, "ramfuncs");
程序中调用一个函数
// The RamfuncsLoadStart, RamfuncsLoadEnd, and RamfuncsRunStart
// symbols are created by the linker. Refer to the cmd file.
MemCopy(&RamfuncsLoadStart, &RamfuncsLoadEnd, &RamfuncsRunStart);
定义如下
void MemCopy(Uint16 *SourceAddr, Uint16* SourceEndAddr, Uint16* DestAddr)
{
while(SourceAddr < SourceEndAddr)
{
*DestAddr++ = *SourceAddr++;
}
return;
}
系统自带的段名:(选择部分)(常见于DSP28x_CodeStartBranch.asm)
.reset
只包含一个32位的中断矢量,指向实时支持库rts2800_ml.lib中的C编译器导引函数,即_c_int00子程序。通常我们不用此块,而是另外创建分支指令指向开始代码。
用法如下:
SECTIONS
{
.reset
:>FLASH,
PAGE=0.
TYPE=DSECT
}
语句中的“TYPE=DSECT”用来提示编译器编译时将.reset段忽略掉。
codestart
包含一条长跳转指令,指向实时支持库rts2800_ml.lib中的C编译器导引函数,即_c_int00子程序。不同的是:如果系统的程序(.text)放在系统内部RAM中仿真运行,则本块应放入片内RAM中,如地址单元0x3F8000;如果是固化程序进FLASH,则.codestart应定位与FLASH中的其实地址为0x3F7FF6中。(一条长跳转指令占2个字)。
ramfuncs
此程序块用于对FLASH的控制寄存器进行初始化设置。
FLASH的控制寄存器受代码安全模块CSM保护,因此如果DSP是安全的(烧写后的FLASH往往处于这种状态),则必须从受保护的RAM中执行FLASH寄存器初始化代码。
InitFlash()位于DSP281x_SysCtril.c文件中,为FLASH寄存器初始化用,将次程序放进.ramfuncs块中。
在系统的cmd文件中有:
SECTIONS
{
……
Ramfuncs:
LOAD = FLASHD, PAGE = 0
//定义输出段将会被装载到哪里
RUN = RAMM1, PAGE = 0
//定义输出段将会在哪里运行
LOAD_START(_RamfuncsLoadStart),
LOAD_END(_RamfuncsLoadEnd),
RUN_START(_RamfuncsRunStart),
……
}
上例中SECTIONS段的语法如下:
SECTIONS
{
name : [property,property,……]
}
name:输出段的名称
property:输出段的属性:
load=allocation(强制地址或存储空间名称)同>allocation:定义输出段将会被装载到哪里。
run= allocation(强制地址或存储空间名称)同>allocation:定义输出段将会在哪里运行。
另:CMD文件中只出现一个关键字load或run时,表示两者的地址时表示两者的地址时重合的。
LOAD_ START(_RamfuncsLoadStart)令编译器创建了一个变量RamfuncsLoadStart,该变量指向段ramfuncs的装载地址的首地址(LOAD_ START为编译伪指令,请见CCS的帮助文档);
LOAD_ START(_RamfuncsLoadEnd)令编译器创建了一个变量RamfuncsLoadEnd,该变量指向段ramfuncs的装载地址的末地址(LOAD_ END为编译伪指令,请见CCS的帮助文档);
LOAD_ START(_RamfuncsRunStart)令编译器创建了一个变量RamfuncsRunStart,该变量指向段ramfuncs的运行地址的首地址(LOAD_ START为编译伪指令,请见CCS的帮助文档);
在C函数中,为了使用变量RamfuncsLoadStart、RamfuncsLoadEnd和RamfuncsRunStart,必须先声明。MemCpy()位于DSP281x_MemCopy.c文件中,在对FLASH寄存器初始化时,用于将指定的FLASH中的程序段复制到指定的RAM区中。如例:
extern Uint16 RamfuncsLoadStart;
extern Uint16 RamfuncsLoadEnd;
extern Uint16 RamfuncsRunStart;
……
Void main(main)
{
……
MemCopy(&RamfuncsLoadStart, &RamfuncsLoadEnd, &RamfuncsRunStart);
InitFlash();
……
}
根据自己的经验,codestart块与reset块并没有什么用处。
F2812的DSP芯片内部有一个BOOTROM,存储着出厂前已经编制好的导引加载程序和标准数学表,同时也包含复位矢量和CPU矢量表(仅用于测试)。当DSP上电后,导引加载程序会自动配置所需要的设置,例如配置好EMIF以存取flash,配置GPIO口为把程序从外部RAM或ROM下载到内部RAM中做准备。
————————类似于PC的加电硬件检测
在运行C程序(main函数)之前,必须创建C运行环境。这个任务由C引导程序(BOOTRAM中固化的导引加载程序)使用名为c_int00()的子程序来执行。此函数位于rts.lib文件中。
在系统开始运行时,c_int00()可以被调用,通常由硬件复位来调用。必须将c_int00()与其他的目标模块连接起来,当使用C连接器并且包含rts28xx.lib作为连接器输入文件时,以上调用自动实现。当连接C程序时,连接器将可执行输出模块中的入口点的值赋给c_int00()函数,这些可执行输出模块不尽相同,但最为人关心的则是由c_int00()来启动main()函数运行自己的C程序。
而在
.codestart
和
.reset代码块的编写的汇编程序的功能也仅仅是引导BOOTROM中的导引加载程序与c_int00()函数的衔接。
欢迎光临 电子技术论坛_中国专业的电子工程师学习交流社区-中电网技术论坛 (http://bbs.eccn.com/) | Powered by Discuz! 7.0.0 |