总结自:
姚睿等老师 《DSP原理及应用技术》 人民邮电出版社
o.先
28335的片内总线包括存储器总线、外设总线和DMA总线。
存储器总线采用哈佛结构(将在下面讲到),外设总线采用TI统一标准激活片内外设的连接。F28335支持3种不同的外设:外设1支持16/32位地址访问,外设2支持16位访问,外设3可通过DMA总线支持16/32位DMA访问。
可见DSP控制器片内存储器总线有6组,地址总线和数据总线各3组。F28335具有独立的程序空间和数据空间(因具有独立的地址总线和数据总线)。
F28335片内存储器:
1).FLASH(512KB: 256K*16位):起始地址0x300000-0x33ffff,FLASH同时被映射到程序空间和数据空间,可以存放程序代码或掉电后需要保护的用户数据。其中,最高地址的8个字(0x33fff8~0x33ffff)为CSM(代码安全模块),可写入128位的密码,以保护产品的知识产权。
2).SRAM(68KB: 34K*16位):单口随机读写存储器,每个机器周期仅能访问一次。分为M0、M1和L0~L7.
3).OTP(2K*16位):前1K字保留,用于ADC校准和系统测试,后1K字提供给用户使用。OTP同时映射到数据空间和程序空间,并受CSM保护。
4).BOOT ROM(8K*16位):出厂时固化了引导程序等。
5).外设寄存器帧和EALLOW保护寄存器
一.汇编格式
见微机原理
二.C格式
见C语言及DSP各寄存器配置
三.编译器
1.段
DSP软件开发流程
TI公司采用COFF格式的目标文件,可以灵活地管理代码段和目标存储器,便于程序的编写和移植。COFF目标文件格式要求在编程时基于代码块和数据块,将之成为段,汇编器和连接器通过伪指令创建和管理这些段。段是目标文件的最小单位,一个段就是最终在存储器映象中占据连续空间的一段代码或数据,且每个段相互独立。
COFF目标文件一般包含三个默认的段:
.text可执行代码
.data已初始化数据
.bss为未初始化数据保留的空间
另外,用户还可以利用.sect(初始化段)和.usect(未初始化段)伪指令自定义段。
2.编译器产生的段
C编译器产生如下两类段:
1)初始化段:用于存放数据表和可执行代码。
.text:存放可执行代码和实型常量。
.cinit:存放初始化变量表和常量表。
.const/.econst:存放字符串常量、全局变量和静态变量的定义及其初始化内容。
.switch:存放switch语句建立的表格。
2)未初始化段:用于在内存(通常为RAM)中保留空间,以便在程序运行时创建和存储变量。
.bss/.ebss:为全局变量和静态变量保留空间。在程序运行时,C程序将.const段(可以在ROM中)内的数据复制到.bss段。或者由.econst段到.ebss段(大内存模式下)。
.stack:为C系统堆栈保留存储区,用于函数参数传递、为局部变量分配空间。
.sysmem/.esysmem:为动态存储器分配保留空间,供calloc、malloc和realloc函数使用(若程序为使用上述函数,则不建立.sysmem段)。
3.连接器对段的处理
编译器完成段的分配和定位,而连接器建立可执行的输出文件,并为各输出段分配至程序存储器和数据存储器。连接器使用MEMORY(定义目标存储器映射)和SECTIONS(指示连接器怎样组合输入段,并将输出段定位到存储器中)两条伪指令实现上述功能。
PAGE–标识存储器空间。通常PAGE 0为程序存储器,PAGE 1为数据存储器。若未规定,则按PAGE 0处理。
name–存储区名字。同一页上的存储区名不能相同,但不同页上的存储区名可以相同。
attr–规定存储区属性。 R-可读 、W-可写 、X-可包含可执行代码 、I-可被初始化
origin–存储区起始地址
length–存储区长度
以name开头的每个段的说明定义了一个输出段。跟在name后面的是定义段内容和段如何分配的属性列表。
load allocation定义在存储器中段装载的位置。
load=allocation
或load>allocation
或allocation
run allocation定义在存储器中段运行的位置。
run=allocation
或run>allocation
四.(接三扩大化)预编译指令(#pragma指令)
注:预编译时通与#ifdef条件编译联用
见
http://blog.sina.com.cn/s/blog_4b4b54da0100r2l6.html
在CCS编程中,如果我们不指定变量的存放位置,编译器会自动的给变量分配一个位置。但是,如果有的时候需要把变量放在一个特定的空间内,我们应该如何操作呢?CCS提供了如下的两个指令:
#pragma code_section(func,"section_name");
int func(int x)
{
return c;
}
#pragma data_section(bufferA,"section_name");
char bufferA[512];
然后在.cmd文件中建立对应的section
MEMORY
{
PAGE 1:
spacename : origin = 0x...., length 0x..
}
SECTIONS
{
section_name : >spacename PAGE 1
}
引自:
http://blog.sina.com.cn/s/blog_762cf5f80101aovp.html
或参考
http://www.cnblogs.com/layup/archive/2013/05/19/3087521.html
加:
在DSP28335工程文件里(不用BIOS产生CMD文件),手写CMD文件一般有两个:
在RAM里调试时用的两个CMD文件分别为DSP2833x_Headers_nonBIOS.cmd和28335_RAM_lnk.cmd,烧写到flash里时用的两个CMD文件分别为DSP2833x_Headers_nonBIOS.cmd和F28335.cmd.
其中DSP2833x_Headers_nonBIOS.cmd文件可以在所有工程文件中通用,主要作用是把外设寄存器产生的数据段映射到对应的存储空间,可以跟DSP2833x_GlobalVariableDefs.c文件对照一下看看。
下面通过一个简单例子,比如向CpuTimer0Regs. TIM.all写数据,来解读一下CMD文件是如何把寄存器里的值准确映射到所在存储器的位置的。
先在DSP2833x_GlobalVariableDefs.c文件里找到以下几行代码:
#ifdef __cplusplus
#pragma DATA_SECTION("CpuTimer0RegsFile")
#else
#pragma DATA_SECTION(CpuTimer0Regs,"CpuTimer0RegsFile");
#endif
volatile struct CPUTIMER_REGS CpuTimer0Regs;
由上可知CpuTimer0Regs是一个结构体变量名(其定义在DSP2833x_CpuTimers.c文件里),通过预处理命令#pragma 为这个结构体定义了一个名称为CpuTimer0RegsFile的数据段。
接着在DSP2833x_Headers_nonBIOS.cmd文件里找到如下代码:
SECTIONS
{
PieVectTableFile : > PIE_VECT, PAGE = 1
DevEmuRegsFile : > DEV_EMU, PAGE = 1
FlashRegsFile : > FLASH_REGS, PAGE = 1
CsmRegsFile : > CSM, PAGE = 1
AdcMirrorFile : > ADC_MIRROR, PAGE = 1
XintfRegsFile : > XINTF, PAGE = 1
CpuTimer0RegsFile : > CPU_TIMER0, PAGE = 1
......
}
红字体代码的作用就是,通过SECTIONS伪指令把CpuTimer0RegsFile数据段装载到名称为CPU_TIMER0的存储空间。
同样在DSP2833x_Headers_nonBIOS.cmd文件里找到如下代码:
MEMORY
{
PAGE 0:
PAGE 1:
DEV_EMU : origin = 0x000880, length = 0x000180
FLASH_REGS : origin = 0x000A80, length = 0x000060
CSM : origin = 0x000AE0, length = 0x000010
ADC_MIRROR : origin = 0x000B00, length = 0x000010
XINTF : origin = 0x000B20, length = 0x000020
CPU_TIMER0 : origin = 0x000C00, length = 0x000008
......
}
CPU_TIMER0存储空间通过MEMORY伪指令指示了其起始地址和长度,也就等于间接确定了结构体CpuTimer0Regs的具体位置,所以通过以上几层映射关系,当向CpuTimer0Regs. TIM.all写数据时就可以准确的写入DSP内部寄存器所在的存储器的位置。由此看见,CMD的作用就是为程序代码和数据分配存储空间。
五.函数调用(子程序调用)、堆栈和中断函数处理
见微机部分
六.CCS的使用
见mannual