第四章 DSP的软件系统结构与设计.docx
《第四章 DSP的软件系统结构与设计.docx》由会员分享,可在线阅读,更多相关《第四章 DSP的软件系统结构与设计.docx(48页珍藏版)》请在冰豆网上搜索。
第四章DSP的软件系统结构与设计
第四章DSP的软件系统结构与设计
主要内容:
●概述
●C54XDSP指令系统
●公共目标文件格式(COFF)
●汇编语言程序设计
●C语言程序设计
●汇编和C语言混和编程
§4.1概述
C54x软件设计有3种方法:
⏹用C语言开发
⏹全汇编语言开发
⏹C和汇编语言混合编程开发(主流)
采用C和汇编语言混合编程必须遵循一些有关的规则。
§4.1.1汇编软件开发过程
§4.1.2C语言软件开发过程
§4.2指令系统及汇编程序编写
●汇编语言软件结构
●指令系统
●汇编语言程序编写实例
§4.2.1汇编语言软件结构
若用户要建立一个汇编项目,项目必须包括以下文件(以建一个项目example为例):
vectors.asm;定义复位、中断矢量源文件
example.asm;主体源程序。
c5402.cmd;根据用户选用的C54X具体型号定义存储空间,并为用户段分配空间。
c5402.h;定义C549内部控制及状态寄存器。
汇编源程序结构
由各种段组成:
.bss未初始化段,为变量保留空间
.text已初始化段,用户程序代码
.data已初始化段,为某段存储空间初始化数据
自定义段:
用.usect或.sect建立的用户自定义段
例4.1:
计算
STM#x,AR1
ST#1,*AR1+
ST#2,*AR1+
ST#3,*AR1+
ST#4,*AR1+
ST#5,*AR1
STM#x,AR1
STM#4,AR2
LD#0,A
LOOP:
ADD*AR1+,A
BANZLOOP,*AR2-
STLA,y
herebhere
.end
§4.2.2指令系统
汇编指令语法格式:
标号:
助记符源操作数,目的操作数;注释
例:
Start:
LD#9,DP;9→DP
§4.2.2.1寻址方式
⏹立即寻址
⏹绝对寻址
⏹累加器寻址
⏹直接寻址
⏹间接寻址
⏹存储器映象寄存器寻址
⏹堆栈寻址
指令部分寻址缩略语说明
见书P74表3.1.1
立即寻址
就是在指令字中包括立即操作数。
⏹短立即数寻址(单字指令)
就是包含的立即数是3、5、8或9位。
例如:
RPT#99;将下一条指令循环100次
LD#0h,DP;将0装入数据页指针
ADD#0ffh,A;将0ffh加给AccA
编码为单字:
⏹长立即数寻址(双字指令)
就是包含的立即数是16位。
例:
ADD#1234h,A;将#1234h加给AccA
绝对寻址
就是在指令中包含所要寻址的存储单
元的16位地址,16位地址可以用其所
在单元的地址标号或16位符号常数来
表示。
绝对寻址
例如:
数据存储器地址寻址:
MVKDsample,*AR5;将数据存储器
Sample地址单元中的数据传送到由AR5寄
存器所指向的数据存储器单元中。
这里的sample是一个符号常数,代表一个
数据存储单元的地址。
绝对寻址
程序存储器地址寻址:
MVPDtable,*AR5;将程序存储器标
号为table地址单元中的数据传送到由AR5
所指向的数据存储器单元中。
这里的table是一个地址标号,代表一个程
序存储器单元的地址。
绝对寻址
端口地址寻址:
PORTRPA,Smem;从PA端口读入一个
数据,将其存放在数据存储器Smem单元。
PORTWSmem,PA;将数据存储器Smem
单元中的数据写到PA端口。
这里的PA是I/O端口地址的标号。
绝对寻址
*(lk)寻址:
用一个符号或一个常数来确定数据存储器
中的一个地址。
LD*(BUFFER),A
把地址为BUFFER的数据单元中的数据装
到AccA中,BUFFER是个符号常数。
累加器寻址
就是利用累加器的数值作为地址来读写程序存储器。
只有两条指令:
READASmem
WRITASmem
READA,把累加器A所确定的程序存储器单元中的一个字,传送到数据存储器单元Smem中。
WRITA,把数据单元Smem中的一个字,传送到累加器A确定的程序存储器单元。
直接寻址
就是指令中包含数据存储器地址的低7位。
这7位地址作为偏移地址值,与基地址值
(数据页指针DP或堆栈指针SP)一起构成
16位数据存储器地址。
优点:
可以在不改变DP或SP的情况下,随
机地寻址128个存储单元。
直接寻址:
DP指针直接寻址
TMS320C54的数据存储器分为512页,每页128
字。
设置一个数据页指针DP(DataPointer),
用9-bit指向一个数据页,再加上一个7-bit的页内
偏移地址,形成16-bit的数据地址。
例如:
LD#4,DP;指向页4(0200h-027Fh)
ADD9h,A;将数据页4中地址9h的
数据加给AccA
直接寻址:
SP指针直接寻址
就是选择SP的16位地址加上指令中的7位地址,形成16位的数据存储单元的地址。
例如:
(SP)=1000H,执行
SSBXCPL;设置SP指针直接寻址方式
LD1H,A;将数据存储器1001h单元数据加载到AccA
ADD2H,A;将数据存储器1002h单元数据加到AccA
间接寻址
就是按照存放在某个辅助寄存器中的16位地址寻
址的。
8个辅助寄存器(AR0--AR7),由一个辅助寄存器指针(ARP3-bit)来指定。
辅助寄存器算术单元(ARAU)作16-bit无符号数运算,决定一个新的地址,装入辅助寄存器中的一个。
AR0--AR7的内容相当灵活,可以装入立即数,加上立即数,减去立即数;也可以从数据存储器装入地址;还可以作以下的变址寻址:
间接寻址
将该AR的内容加1或减1,再寻址(循环常用)
将该AR的内容加上或减去AR0的内容,再寻址。
将该AR的内容逆向进位加上或减去AR0的内容,再寻址
具体寻址形式见书78页表3.1.5。
间接寻址
例如:
ADD*,8,A;将当前辅助寄存器所指的地址里的数据,左移8-bit后加给AccA
ADD*+,8,A,AR4;数据左移,加给AccA后,当前辅助寄存器中的地址加1,然后选择辅助寄存器AR4作为当前辅助寄存器
间接寻址
ADD*0+,8,A;执行加法后,将AR0的值加给当前辅助寄存器
ADD*BR0+,8,A;执行加法后,将AR0的值加给当前辅助寄存器,但反向进位
间接寻址:
位倒序寻址功能
正向进位与反向进位:
间接寻址:
位倒序寻址功能
位倒序寻址:
AR0=100
AR1=000
按AR1寻址后,将AR0加给AR1,反
向进位规则。
间接寻址:
循环寻址功能
许多算法,如卷积,相关和FIR滤波等,都需要在存储器中实现一个循环缓冲器。
在这些算法中,一个循环缓冲器就是一个包含了最近的数据的滑动窗口。
当新的数据来到时,缓冲器就会覆盖最早的数据。
循环缓冲器实现的关键是循环寻址的实现。
C54x间接寻址中提供了循环寻址的方式,以%表示。
间接寻址
定义循环缓冲器大小寄存器(BK)来确定循环缓冲器的大小。
用循环缓冲器的有效基地址(EFB)和尾地址(EOB)来组成循环地址。
step是加到辅助寄存器,或从辅助寄存器中减去的值。
存储器映射寄存器(MMR)寻址
存储器映射寄存器寻址用来修改存储
器映射寄存器而不影响当前数据页指
针(DP)或堆栈指针(SP)的值。
存
储器映射寄存器寻址既可以在直接寻
址中使用,又可以在间接寻址中使用
存储器映射寄存器(MMR)寻址
有两种产生MMR地址方法:
采用直接寻址方式时,高9位数据存储器地址被置0(但不改变DP、SP值),利用指令中的低7位地址访问数据存储器。
采用间接寻址方式时,高9位数据存储器地址被置0,按照当前辅助寄存器中的低7位地址访问数据存储器。
即:
MMR寻址只能访问数据存储器0页资源,不考虑DP、SP的直接寻址方式。
存储器映射寄存器(MMR)寻址
只有8条指令:
LDMMMR,dst
MVDMdmad,MMR
MVMDMMR,dmad
MVMMMMRx,MMRy
POPMMMR
PSHMMMR
STLMsrc,MMR
STM#1k,MMR
MMR寻址特点:
①寻址速度快,对MMR执行写操作开销小;
②可直接利用MMR的名称快速访问数据存储空间的0页资源;
3只能寻址数据空间的0页单元。
用途:
主要用于不改变DP、SP的情况下,修改MMR中的内容。
堆栈寻址
系统堆栈用来在中断和子程序期间自动存放程序计数器。
它也能用来存放额外的数据项或传递数据值。
处理器使用一个16-bit的存储器映射寄存器—堆栈指针来对堆栈寻址,它总是指向存放在堆栈中的最后一个元素。
堆栈寻址
SP:
是16位的专用寄存器,指示出栈顶在数据存储空间的位置,系统复位后,SP初始化为0h,使得堆栈由0000h开始,程序设计时可以重设SP值。
注意:
C54x的堆栈是从高地址向低地址方向生长,因此,在压入操作时,先减小SP后将数据压入堆栈;在弹出操作时,先从堆栈弹出数据后增加SP值。
堆栈寻址
例如:
SP=3FFH,CPU执行一条调用子程序指令,程序计数器PC进栈,这时,PC值压入数据存储器3FEH中。
4条指令:
PSHDsmem
PSHMMMR
POPDsmem
POPMMMR
关于指令的几点说明
1、只能使用绝对寻址和累加器A寻址程序空间。
2、只能使用绝对寻址寻址I/O空间
3、寻址数据空间可以使用多种寻址方式,指令中的Smem、xmem、ymem、lmem用来寻址数据空间。
§4.2.2.2C54x指令系统:
具体指令
C54x指令系统共有129条指令,由于操作数的寻址方式不同,派生至205条。
主要有以下几类:
●数据传送指令
●算术运算指令
●逻辑运算指令
●程序控制指令
●并行操作类指令
●重复操作指令
具体指令请参见书上P84,大家可以自学汇编指令。
关于移位的说明
⏹主要是为了完成数据存储器和累加器中数据的定标。
⏹指令中的移位数表示:
二进制补码,正值为左移,负值为右移,有三种形式:
1、立即数:
shift(5位,-16~+15)
shft(4位,0~15)
2、ST1中的ASM位:
5位,-16~+15
3、暂存器T的低六位TS:
-16~+31
注意:
关于条件操作数cond的说明
数据传送指令(装载与存储)
1、装载指令(LD)
目的操作数:
A、B、T、DP、ARP、ASM
源操作数:
数据存储器,立即数
(AR1)=200H,(200H)=FFEDH,SXM=0,(A)=000000FFFFH
LD*AR1A;(A)=000000FFEDH
LD*AR1,8,A;(A)=0000FFED00H
LD*AR1,-4,A;(A)=0000000FFEH
思考:
上例中,若SXM=1时,结果各是多少
课堂练习:
若AR1)=200H,(200H)=FFEDH,(201H)=0FEEH,SXM=0,CPL=0,
(IMR)=FFFFH,(A)=000000FFFFH,请问执行下列每条语句时A各是多少?
2、装载指令(ST)
目的操作数:
数据存储器单元,MMR,程序存储器单元,I/O空间
源操作数:
A,B,T,TRN,立即数
题目:
(AR1)=200H,(200H)=FFEDH,(A)=000000FFFFH
解答:
STHA,8,*AR1;(200H)=00FFH
STLA,*AR1;(200H)=FFFFH
ST#65,*AR1;(200H)=65
STM#201H,AR1;(AR1)=201H
ST#0,*AR1;(201H)=0
LD#1,DP;(DP)=1
ST#F000H,20H;(A0H)=F000H
§4.2.3汇编语言程序的编写
常用的伪指令:
.title:
允许汇编器在每页顶部打印标题。
.text:
指定.text后面的为可执行代码即程序。
.bss:
指定.bss后面的变量为未初始化段的内容。
.data:
指定.data后面的代码为初始化数据段。
.sect:
定义初始化段,可包含可执行代码或数据
.usect:
为未初始化的命名段保留空间,与.bss类似。
.word:
指定.word后面的数据为16位宽。
.byte:
指定.byte后面的数据为8位宽。
.set:
赋值,将值赋给符号。
.equ:
同.set。
.end:
终止汇编。
.def:
指定定义在当前模块中,但可以被其他模块使用。
.ref:
指明在当前模块使用,但在其他模块中定义。
.include:
告诉汇编器开始从其他文件读取源代码语句。
.global:
声明符号为全局符号。
.mmregs:
为片内CPU及外设映射寄存器定义符号名。
练习4.1
请计算:
§4.3公共目标文件格式(COFF)
通过汇编器和链接器建立的可执行目标文件的格式称为公共目标文件格式,即COFF(commonobjectfileformat)。
COFF文件有3种形式:
COFF0、COFF1、COFF2。
C54x汇编器和编译器默认建立的是COFF2文件。
§4.2.1COFF文件中的段
段(sections)是COFF文件中最重要的概念。
段——就是在存储器中占据相邻空间的代
码或数据块。
每个目标文件都分成若干个段。
且每一个
段都是分开和各不相同的。
所有的COFF
文件都包含以下3种形式的段:
.text段——该段通常包含可执行代码
.data段——该段通常包含初始化数据
.bss段——该段通常为未初始化变量保留存储空间
用户还可以自己定义段——自定义段。
§4.2.2段的类型
●已初始化段:
包含的是数据或代,.text和.data段都是已初始化段,用户可以通过.sect建立已初始化的自定义段。
●未初始化段:
它为未初始化数据保留空间,.bss段是未初始化段,用户也可以通过.usect建立未初始化的自定义段。
§4.2.3目标文件中的段与目标存储器之间的关系
⏹汇编器:
把指令译成代码,并根据汇编命令用适当的段将各部分程序代码和数据连在一起,构成目标文件。
⏹链接器:
分配存储单元,将各个段分配定位到相应的目标存储器中。
那么,各个段到底在存储器中的什么位置,对我们很重要,要弄清楚。
自定义段也可以通过定义分配到数据存储
器或是程序存储器。
§4.2.4汇编器对段的处理
汇编器通过5条命令识别汇编语言程序的段,这5条命令是:
.bss未初始化段,为变量保留空间
.usect未初始化段,建立用户的自定义段
.text已初始化段,用户程序代码
.data已初始化段,为某段存储空间初始化数据
.sect已初始化段,建立用户的自定义段
未初始化段命令语法:
.bss符号,字数
符号.usect“段名”,字数
符号:
对应于保留空间的第一个字的变量名称。
字数:
表示保留多少个存储单元。
例:
.bssx,2;为x变量保留2个字的空间
var1.usect“.newvars”,10;为.newvars段保留10个字的空间,var1为该空间第一个字
初始化段命令语法:
.text[段起点]
.data[段起点]
.sect“段名”,[段起点]
[段起点]是可选项,它是用段程序计数器SPC定义的一个起始值,也可以缺省,缺省值为0,一般都是省略。
例4.1:
.data
.word011h,022h,033h
.bssvar1,1
ptr.word0123h
.text
add:
LD#4,DP;EA04H
LD#1234H,A;F020H,1234H
ADD9H,A;0089H
.data
vals.word0aah,0bbh
var2.usect“.newvars”,1
buff.usect“.newvars”,7
.text
LDA,B;F540H
汇编结果(形成的段):
练习4.1请具体写出汇编器对下面一段程序进行汇编后建立的段。
§4.2.5链接器对段的处理
链接器处理段,主要是完成2个任务:
●把一个或多个COFF目标文件(.obj)中的各个段作为链接器的输入段,经链接以后在一个可执行的COFF输出模块中建立各个输出段。
●为各个输出段选定存储器地址。
链接器的任务通过链接命令文件(.cmd)完成,链
接命令文件则主要是通过2条命令实现:
●MEMORY此命令用来定义目标系统的存储器配置图,包括对存储器各部分命名,以及规定它们的起始地址和长度
●SECTIONS此命令告诉链接器如何将输入段组合成输出段,以及将输出段放在存储器的什么位置
例4.2:
两个文件file1.asm和
file2.asm经汇编后形
成两个目标文件file1.obj
和file2.obj,形成的各个
段如右图所示:
§4.2.6链接命令文件编写(CMD文件)
.cmd文件要包括以下内容:
●要链接的文件名
●链接的选项
●存储器的配置
这些内容主要是由MEMORY和SECTIONS两命令
和其他的一些链接命令及选项实现。
一、MEMORY命令
此命令用来定义目标系统的存储器配置图,
包括对存储器各部分命名,以及规定它们的
起始地址和长度。
语法:
MEMORY
{PAGE0:
name0[(attri)]:
origin=常数,length=常数
PAGE1:
name1[(attri)]:
origin=常数,length=常数
………
PAGEn:
namen[(attri)]:
origin=常数,length=常数
}
PAGE:
指定存储器空间页面,最多255页。
通常:
PAGE0:
程序存储器
PAGE1:
数据存储器
PAGE2:
I/O空间
Name:
存储器区间名。
一页里可以有多个区间,但同一页的区间名不能相同,且地址不能重叠,不同页里的区间名可以相同,但地址也不能重叠。
attri:
属性(选项)。
R:
可读;W:
可写;X:
可装入执行程序代码;I:
可进行初始化。
若不写,则具有以上所有属性。
origin:
区间起始地址。
length:
区间的长度。
fill(选项):
可简写为f,指定存储器区间的填充字符。
该值为两个字节的整型常数。
例4.3:
C5402的存储器配置。
MEMORY
{PAGE0:
PROG:
origin=0x0100,length=0x2000vect:
origin=0x0080,length=0x80
PAGE1:
DARAM:
origin=0x2100,length=0x1000
PAGE2:
IO:
origin=0x0000,length=0xffff
}
注意:
语法中大写的词在实际编写文件时也一定要大
写,如PAGE,MEMORY。
练习4.2
若5402工作在MP模式,OVLY=0,外部扩展一个64K的程序存储器flash,要求片内DARAM全部配置为数据空间,将64K的flash全部配置为程序空间,且要把128字的中断向量地址范围单独分个区间,请用MEMORY写出存储器配置文件。
MEMORY
{PAGE0:
PROG:
origin=0x0000,length=0xff80vect:
origin=0xff80,length=0x80
PAGE1:
DARAM:
origin=0x2100,length=0x1000
}
二、SECTIONS命令
任务:
说明如何将输入段组合成输出段;
在可执行程序中定义输出段;
规定输出段在存储器中的存放位置;
允许重新命名输出段。
语法:
SECTIONS
{.name:
[property,property……]
.name:
[property,property……]
………
.name:
[property,property……]
}
Name:
输出段名称
Property:
性质,包括以下内容:
●装载位置(loadallocation)
load=allocationor
allocationor
>allocation
如:
{.text:
load=0x1000}or
{.text:
0x1000}or
{.text:
>0x1000}
●运行位置(runallocation)
run=allocationor
allocationor
>allocation
●输入段(inputsections)
{input_section1,input_section2······}
如:
{file1.obj(.text),file2.obj(.text)}
{file1.obj(.data),file2.obj(.data)}
●段类型(sectiontype)
type=COPYor
type=DSECTor
type=NOLOAD
●填充值(fillvalue)
fill=value
name:
······{······}=value
理论上讲,连接器可对每个输出段在目标存储器中赋予两个地址:
装入地址和运行地址。
但大多数情况下,这两个地址是相同的。
这种将输出段定位在目标存储器中并赋予地址的过程称为段的定位(allocation)。
定位还可以通过指定一个或多个参数来控制。
•Binding:
将段定位到指定的地址。
如:
.text:
load=0x1000
•Memory:
将段定位到由MEMORY伪指令定义的具有指定名称(如PROG,DARAM)或属性的地址空间。
如:
.text:
load>PROG
•Alignment:
指定段的起始地址边界。
如:
.text:
align=0x80
•Page:
指定要使用的存储器页面。
如:
.text:
PAGE0
例4.4:
SECTIONS
{
.text:
>progPAGE0
.vectors:
>vectsPAGE0
.data:
>progPAGE0
.bss:
>dramPAGE1
}
注意:
这里段加载位置一定是在MEM