Presentation is loading. Please wait.

Presentation is loading. Please wait.

DSP技术及应用 Digital Signal Processor 数字信号处理器.

Similar presentations


Presentation on theme: "DSP技术及应用 Digital Signal Processor 数字信号处理器."— Presentation transcript:

1 DSP技术及应用 Digital Signal Processor 数字信号处理器

2 第四章 DSP软件开发过程 第一节 汇编语言程序的编写方法 第二节 汇编语言程序的汇编 第三节 COFF的一般概念 第四节 目标文件的链接

3 第一节 汇编语言程序的编写方法 软件开发过程

4 汇编语言程序的编辑、汇编和链接过程

5 软件开发的目的,就是要产生一个可以由C54x目标系统执行的模块。
然后,可以用软件仿真器(Simulator)或可扩展开发系统硬件仿真器(XDS510)或评价模块(EVM)工具来修正或改进程序。

6 供本程序的其它部分或其它程序调用。标号是任选项,标号后面可以加也可以不加冒号“:”。
1.汇编语言源程序的句法格式 以.asm为扩展名 每一行由4个部分组成 要点 格式 [标号][:] 助记符 操作数 [;注释] [ ]中的内容为可选择部分 供本程序的其它部分或其它程序调用。标号是任选项,标号后面可以加也可以不加冒号“:”。

7 要 点 1.标号必须从第1列写起, 2.标号最多可达32个字符,可以是A~Z,a~z,0~9,_,以及$,但标号的第1个字符不能是数字。
3.引用标号时,标号的大小写必须一致。 4.标号的值就是段程序计数器SPC的值。 5.如果不用标号,则第一个字母必须为空格、分号 或星号(*)。

8 可以是助记符指令、汇编指令、宏指令和宏调用命令。
[标号][:] 助记符 操作数 [;注释] 可以是助记符指令、汇编指令、宏指令和宏调用命令。 要点 1.助记符指令,一般用大写; 2.汇编命令和宏命令,以句号“.” 开始,且为通常用小写。 建议

9 1.指令中的操作数或汇编命令中定义的内容 2.操作数之间必须用逗号“,”分开。
[标号][:] 助记符 操作数 [;注释] 1.指令中的操作数或汇编命令中定义的内容 2.操作数之间必须用逗号“,”分开。 要点 1.从分号“;”开始 2.可以放在指令或汇编命令后面, 也可以放在单独的一行或数行。 要点

10 2.汇编语言源程序的数据型式 二进制:如 b或 B; 八进制:226q或572Q; 十进制:1234或+1234或-1234(缺省型) 十六进制:0A40h或0A40H或0xA40 浮点数:1.623e-23(仅C语言程序中能用,汇编程序不能用) 字符:‘D’ 字符串:“this is a string”

11 3.汇编命令 汇编命令是用来为程序提供数据和控制汇编进程的。C54x汇编器共有64条汇编命令,根据它们的功能,可以将汇编命令分成8类: (1)对各种段进行定义的命令。 (2)对常数(数据和存储器)进行初始化的命令。 (3)调整SPC(段寄存器)的指令。 (4)对输出列表文件格式化的命令。 (5)引用其它文件的命令。 (6)控制条件汇编的命令。 (7)在汇编时定义符号的命令。 (8)执行其它功能的命令。

12 例4-1 编写计算y=a1*x1+a2*x2+a3*x3+a4*x4的汇编源程序
* * * * * * * * * * * * * * * * * * * * * * * * ** * * * * * * * * example.asm y=a1*x1+a2*x2+a3*x3+a4*x * .title “example.asm” ;为汇编源程序取名 .mmregs ;定义存储器映象寄存器 STACK .usect “STACK”,10h ;分配16个单元的堆栈空间 .bss a, ;为系数a分配4个单元的空间 .bss x, ;为变量x分配4个单元的空间 .bss y, ;为结果y 分配1个单元的空间 .def _c_int ;定义标号_c_int00 .data ;定义数据代码段 table: word 1,2,3, ;在标号table开始的8个单元中 .word 8,6,4, ;为这8个单元赋初值

13 .text ;定义文本代码段 _c_int00 :STM #0,SWWSR ;软件等待状态寄存器置0,不设等待 STM #STACK+10h,SP ;设置堆栈指针初值 STM #a,AR ;AR1 指向 a的地址 RPT # ;从程序存储器向数据存储器 MVPD table,*AR1+;重复传送 8个数据 CALL SUM ;调用 SUM 实现乘法累加和的子程序 end: B end ;循环等待 SUM:STM #a,AR3 ;将系数a的地址赋给AR3 STM #x,AR ;将变量x的地址赋给AR3 RPTZ A,# ;将A清0,并重复执行下条指令4次 MAC *AR3+,*AR4+,A ;执行乘法并累加,结果放在A中 STL ;将A的低字内容送结果单元y RET ;结束子程序 .end ;结束全部程序

14 4.宏定义和宏调用 (1)两者都可以被多次调用,但是把子程序汇编成目标代码的过程只进行一次,而在用到宏指令的每个地方都要对宏指令中的语句逐条地进行汇编。 (2)在调用前,由于子程序不使用参数,故子程序所需要的寄存器等都必须事先设置好;而对于宏指令来说,由于可以使用参数,调用时只要直接代入参数就行了。 (3)宏指令可以在源程序的任何位置上定义,但必须在用到它之前先定义好。宏定义可以嵌套。 宏指令与子程序的异 同

15 宏定义的格式 macname .macro[parameter 1][,…,parameter n]    助记符指令与宏指令    [.mexit]    .endm 宏调用的格式 [label][:]macname [parameter1][,…,parametern]

16 例4-2 宏定义、宏调用和宏展开的一个例子。 1 * 2 3 * add3 4 * 5 * ADDRP=P1+P2+P3 ;说明宏功能 6
例4-2 宏定义、宏调用和宏展开的一个例子。 * 2 * add3 * * ADDRP=P1+P2+P ;说明宏功能 6 add macro p1,p2,p3,ADDRP ;定义宏 8 LD p1,A ;将参数1赋给A ADD p2,A ;将参数2与A相加 ADD p3,A ;将参数3与A相加 STL A,ADDRP ;将结果A的低字存参数4 endm ;结束宏 14 15 global abc,def,ghi,adr ;定义全局符号 17 add abc,def,ghi,adr ;调用宏 1 ! LD abc,A ;宏展开 ! ADD def,A ! ADD ghi,A ! STL A,adr

17 第二节 汇编语言程序的汇编 汇编语言源程序要素 汇编程序(汇编器)功能 将源程序汇编成可重定位的目标文件(.obj文件);
第二节 汇编语言程序的汇编 汇编伪命令 助记符指令 宏命令 汇编语言源程序要素 汇编程序(汇编器)功能 将源程序汇编成可重定位的目标文件(.obj文件); 如果需要,可以生成一个列表文件(.lst文件); 将程序代码分段,每段的目标代码都有一个SPC管理; 定义和引用全局符号,可在列表后附加交叉引用表; 对条件程序块进行汇编; 支持宏功能,允许定义宏命令。

18 1.运行汇编程序 汇编器是名为asm500.exe的可执行程序 axm500 [input file[object file[listing file]]][-options] 例如 axm500 cjy.asm -l -s -x 源程序cjy.asm经汇编后将生成一个cjy. obj目标文件、列表文件、符号表(在目标文件中)以及交叉引用表(在列表文件中)。

19 2.列表文件 列表文件中包括源程序语句和目标代码
TMS320C54x COFF Assembler Version Tue Oct 19 12:42: Copyright (c) Texas Instruments Incorporated example.asm PAGE 1 * * * * * * * * * * * * * * * * * * * * * * * * ** * * * * * * * * example.asm y=a1*x1+a2*x2+a3*x3+a4*x4 * * * * * * * * * * * * * * * * * * * * * * * * * ** * * * * * * * 4 mmregs ;定义存储器映象寄存器 STACK .usect "STACK",10h ;分配10个单元的堆栈空间 bss a, ;为系数a分配4个单元的空间 bss x, ;为变量x分配4个单元的空间 bss y, ;为结果y 分配1个单元的空间 global _c_int ;定义标号

20 源文件的每一行都会在列表文件中生成一行。包括行号、段程序计数器SPC的数值、汇编后的目标代码、源程序语句。
data ;定义数据代码段 table: word 1,2,3, ;在标号table开始的8个单元中 word 8,6,4, ;为这8个单元赋初值 text ;定义文本代码段 _c_int00 STM #0,SWWSR ;软件等待状态寄存器置0,不设等待 STM #STACK+10h,SP ;设置堆栈指针初值 STM #a,AR ;AR1 指向 a的地址 EC RPT # ;从程序存储器向数据存储器 C MVPD table,*AR1+ ;重复传送 8个数据 " 源文件的每一行都会在列表文件中生成一行。包括行号、段程序计数器SPC的数值、汇编后的目标代码、源程序语句。 一条指令可以生成1或2个字的目标代码。 第2字单独列一行,列出了SPC的数值和目标代码 。

21 Field 2段程序计数器(SPC),用十六进制数表示 Field 1源程序语句的行号,用十进制数表示
F CALL SUM ;调用 SUM 实现乘法累加和的子程序 00000a 000D' b F end: B end ;循环等待 00000c 000B' d SUM: STM #a,AR3 ;将系数a的地址赋给AR3 00000e 0000- f STM #x,AR4 ;将变量x的地址赋给AR3 F RPTZ A,# ;将A清0,并重复执行下条指令4次 B09A MAC *AR3+,*AR4+,A ;执行乘法并累加,结果放在A中 STL ;将A的低字内容送结果单元y FC RET ;结束子程序 end ;结束全部程序 No Assembly Errors, No Assembly Warnings Field 4:源程序语句 Field 3目标代码 ! 未定义的外部引用。 ’ .text段重新定位。 ” .data段重新定位。 + .sect段重新定位。 - .bss和.usect段重新定位。 Field 1源程序语句的行号,用十进制数表示

22 在运行汇编程序时,还可产生交叉引用清单,清单包括符号、定义和引用的位置。
LABEL VALUE DEFN REF INT ISR REF proc REF 列出引用此符号的语句的行号。如果此栏是空格,表示此符号还没有被引用过。 列出汇编时定义和引用的每个符号 列出一个赋给符号的4位十六进制数值,或说明符号属性的字符或名称。 列出定义符号的语句编号。如果此符号未加定义,则此栏是空格。

23 交叉引用清单中符号的属性 REF :外部引用(.global) UNDF:未曾定义过 ’ :在.text段定义的符号。
” :在.data段定义的符号。 + :在.sect段定义的符号。 - :在.bss或.usect段定义的符号。

24 第三节 COFF的一般概念 汇编器 和 链接器 功能的异同 相同点 不同点 建立公共目标文件格式的目标文件
汇编器建立的是相对地址COFF文件,即.obj文件 链接器建立的是绝对地址COFF文件,即.out文件 不同点 汇编器根据汇编命令用适当的段将各部分程序代码和数据连在一起,构成目标文件; 链接器的一个任务就是分配存储单元,即把各个段重新定位到目标存储器中。

25 1.COFF文件中的段 COFF文件种类 不同种类的区别
标题格式不相同 数据部分是相同 不同种类的区别 C54x汇编器和C编译器建立的是COFF2文件。C54x能够读/写所有形式的COFF文件,缺省值下链接器生成的是COFF2文件,用链接器-vn选项可以选择不同形式的COFF文件。

26 段(sections)的概念 定义 特点 段是在存储器图中占据相邻空间的代码或数据块。 一个目标文件中的每一个段都是分开的和不相同的。
分段的优点:在目标文件中将放置程序、数据、变量的代码分开,便于在链接时作为一个单独的部分分配存储器。由于大多数系统都有好几种形式的存储器,通过对各个段重新定位,可以使目标存储器得到更为有效的利用。 目标文件中的段与目标存储器之间的关系

27 .text 段,此段通常包含可执行代码; .data 段,此段通常包含初始化数据; .bss 段,此段通常为未初始化变量保留存储空间

28 2.汇编器对段的处理 用于定义段的汇编命令 .bss 未初始化段 .usect 未初始化自定义段 .text 已初始化程序正文段
.data 已初始化程序数据段 .sect 已初始化自定义段 如果汇编语言程序中一个段命令都没有用,那么汇编器把程序中的内容都汇编到.text段。 注意

29 作用 为变量保留存储器空间 通常将它们定位到RAM区
(1)未初始化段 未初始化段由.bss和.usect命令建立 作用 对应于保留的存储空间第一个字的变量名称 为变量保留存储器空间 通常将它们定位到RAM区 位置 使用方法    .bss 符号 , 字数 符号 .usect “段名”,字数 程序员为自定义未初始化段起的名字

30 作用 包含有可执行代码或初始化数据 通常将它们定位到EPROM区
(2)已初始化段 已初始化段是由.text、.data的.sect命令建立 作用 包含有可执行代码或初始化数据 段程序计数器(SPC)定义的一个起始值。 通常将它们定位到EPROM区 位置 .text [段起点] .data [段起点] .sect “段名”[,段起点] 使用方法 程序员为自定义未初始化段起的名字

31 段的构成要经过一个反复过程。例如:当汇编器第一次遇到. data命令时,这个. data段是空的。接着将紧跟其后的语句汇编到
段的构成要经过一个反复过程。例如:当汇编器第一次遇到.data命令时,这个.data段是空的。接着将紧跟其后的语句汇编到.data段,直到汇编器遇到一条.text或.sect命令。 如果汇编器再遇到一个.data段,它就将紧跟这条命令的语句汇编后加到已经存在的.data中。这样,就建立了单一的.data段,段内数据都是连续地安排到存储器中的。

32 (3)子段 作用 子段结构,可以使存储器分配图更加紧密。 可单独分配空间或在基段位置之后 位置 基段名:子段名 .sect “.text:_func” 使用方法 子段也有两种:用.sect命令建立的是已初始化段,而用.usect命令建立的段是未初始化段。

33 汇编器为每个段都安排了一个单独的程序计数器,即段程序计数器(SPC)
表示一个程序代码段或数据段内的当前地址 作用 一开始,汇编器将每个SPC置0。当汇编器将程序代码或数据加到一个段内时,相应的SPC就增加。如果再继续对某个段汇编,则相应的SPC就在先前的数值上继续增加。链接器在链接时要对每个段进行重新定位。 编址过程

34 下例列出的是一个汇编语言程序经汇编后的.lst文件
例4-4 段命令应用举例: * * * * * * * * * * * * * * * * * * * * * * * * * * * Assemble an initialized table into .data * * * * * * * * * * * * * * * * * * * * * * * * * * * data coeff .word 011h,022h,033h     * * * * * * * * * * * * * * * * * * * * * * * * * * * * Reserve space in .bss for a variable * * * * * * * * * * * * * * * * * * * * * * * * * * * * bss buffer,10 * * * * * * * * * * * * * * * * * * * * * * * * * * * * still in .data * * * * * * * * * * * * * * * * * * * * * * * * * * * * ptr word 0123h

35 15 * * * * * * * * * * * * * * * * * * * * * * * * * *
* * Assemble code into the .text section * * * * * * * * * * * * * * * * * * * * * * * * * * * * text f add: LD 0Fh,A f010 aloop: SUB #1,A f BC aloop,AGEQ * * * * * * * * * * * * * * * * * * * * * * * * * * * * Another initialized table into .data * * * * * * * * * * * * * * * * * * * * * * * * * * * * data aa ivals .word 0Aah,0BBh,0CCh bb cc

36 30 0000 var2 .usect “newvars”,1 31 0001 inbuf .usect “newvars”,7
27 * * * * * * * * * * * * * * * * * * * * * * * * * * * * Define another section for more variables * * * * * * * * * * * * * * * * * * * * * * * * * * * * var2 .usect “newvars”,1 inbuf .usect “newvars”,7 32 * * * * * * * * * * * * * * * * * * * * * * * * * * 33 * * Assemble more code into .text * * 34 * * * * * * * * * * * * * * * * * * * * * * * * * * text a mpy: LD Ah,B 0006 f166 mloop: MPY #0Ah,B a 0008 f BC mloop,BNOV * * Define a named section for int. vectors * * sect “vectors” word h,033h

37 在此例中,一共建立了5个段: .text 段内有10个字的程序 代码。 .data 段内有7个字的数据。 vectors 是一个用.sect建 立的自定义段,段内 有2个字的已初始化数 据。 .bss 在存储器中为变量保 留10个存储单元。 newvars是一个用.usect命令 建立的自定义段,它 在存储器中为变量保 留8个存储单元。

38 在一个模块中定义,可在另一个模块中引用的符号。 外部符号
3.COFF文件中的符号 在一个模块中定义,可在另一个模块中引用的符号。 外部符号 .def:在当前模块中定义,并可在别的模块中使用的符号。 .ref:在当前模块中使用,但在别的模块中定义的符号。 .global:可以是上面的随便哪一种情况。 用于处理符号的汇编命令 38

39 x: ADD #56h,A ;定义x B y ;引用y .def x ;x在此模块中定义, ;可被别的模块引用
       ;可被别的模块引用 .ref y ;y在这里引用, ;它在别的模块中定义 汇编时,汇编器把x和y都放在目标文件的符号表中。链接器必须使所引用的符号与相应的定义相匹配。如果链接器不能找到某个符号的定义,就给出不能辨认所引用符号的出错信息。 39

40 第四节 目标文件的链接 根据链接命令或链接命令文件(.cmd文件),将一个或多个COFF目标文件链接起来,生成存储器映象文件(.map)和可执行的输出文件(.out)(COFF目标模块) 链接器 主要 功能

41 在连接过程中,连接器将各个目标文件合并起来,将各个文件的各个段配置到目标系统的存储器中;
对各个符号和段进行重定位,并给它们指定一个最终的地址;解决输入文件之间未定义的外部引用。

42 1.运行链接程序 链接器是名为lnk500.exe 的可执行程序 (1)键入命令 lnk500 (2)键入命令 lnk500 file1.obj file2.obj –o (3)键入命令 lnk500 linker.cmd 应包含如下内容: file1.obj file2.obj -o lind.out 建立一个名为a.out(默认情况)的可重新定位的输出模块。

43 2.链接器选项 在连接时,一般通过连接器选项控制连接操作,在选项前,必须加一短划”-”。选项之间,用空格分开。 除-l(小写L)和-i选项外,其他选项的先后顺序并不重要。 例如: -a:生成一个绝对地址的、可执行的输出模块。

44 3.链接器命令文件 (1)将有多个选项的命令,写成一个链接器命令文件.cmd。
(2)运行链接器命令文件.cmd,生成一个映象文件.map和一个可执行的输出文件.out (3)进行存储器分配 使用方法  lnk500 链接器命令文件名.cmd 44

45 PAGE0: ROM: origin=1000h, length=0100h
例4-6 链接器命令文件举例。 a.obj b.obj    /* 输入文件名 */ -o prog.out /* 选项 */ -m prog.map /* 选项 */ MEMORY /* 目标存储器配置命令 */ { PAGE0: ROM: origin=1000h, length=0100h PAGE1: RAM: origin=0100h, length=0100h } SECTIONS /* 段定位命令 */   .text: >ROM   .data: >ROM   .bss: >RAM 如果链接器认定一个文件为目标文件,就对它链接;否则就假定它是一个命令文件,并从中读出命令和进行处理。 45

46 (2)链接器选项。这些选项既可以用在链接器命令行,也可以编在命令文件中。
(1)输入文件名,就是要链接的目标文件和文档库文件,或者是其它的命令文件。如果要调用另一个命令文件作为输入文件,此句一定要放在本命令文件的最后,因为链接器不能从新调用的命令文件返回。 (2)链接器选项。这些选项既可以用在链接器命令行,也可以编在命令文件中。 (3)MEMORY和SECTIONS都是链接器命令。如果链接命令文件中没有MEMORY和SECTIONS命令(默认情况),则链接器就从地址0080h一个段接着一个段进行配置。 (4)注释的内容应当用/*和*/符号括起来。 注意 46

47 4.链接器对段的处理 链接器在对段进行处理时,主要完成 (1)把一个或多个COFF目标文件中的各种段作为链接器的输入段,经链接后在一个可执行的COFF输出模块中建立各个输出段。 (2)为各个输出段选定存储器地址。 汇编器在需要引用重新定位的符号处都留了一个重定位入口。链接器对符号重定位时,利用这些入口修正对符号的引用值。

48 链接器将输入段组合成一个可执行的目标模块

49 程序重定位 (1)将各个段定位到存储器分配图中,这样一来每个段都从一个恰当的地址开始。 (2)将符号的数值调整到相对于新的段地址的数值。
(3)调整对重新定位后符号的引用。 汇编器在需要引用重新定位的符号处都留了一个重定位入口。链接器对符号重定位时,利用这些入口修正对符号的引用值。

50 例4-5 列表文件中,汇编器为需要重新定位的符号所留的重定位入口。 1 0100 X .set 0100h ;给X赋值
例4-5 列表文件中,汇编器为需要重新定位的符号所留的重定位入口。 X .set 0100h ;给X赋值 text F B Y ;生成一个重定位入口 F LD #X,A ;生成一个重定位入口 ! F7E0 Y: RESET 另一个模块中定义的X。这里赋初值 .text段内对Y的引用是一次内部引用 (’) 在.text段对X的引用是一次外部引用(!) .text段内定义Y

51 假设链接时X重新定位在地址7100h,.text段重新定位到从地址7200h开始,那么Y的重定位值为7204h。链接器利用两个重定位入口,对目标文件中的两次引用进行修正:
F B Y   变成 F073 0004’      F LD #X,A 变成 F020 0000!    在COFF目标文件中有一张重定位入口表。链接器在处理完之后就将重定位入口消去,以防止在重新链接或加载时再次重新定位。 一个没有重定位入口的文件称为绝对文件,它的所有地址都是绝对地址。

52 5.两条链接器命令的使用方法 (1)MEMORY命令

53 MEMORY命令的一般句法 MEMEORY { ┆ }
PAGE 0: name 1 [(attr)]: origin=constant, length=constant PAGE n: name n [(attr)]: origin=constant, length=constant } 对一个存储空间加以标记,每一个PAGE代表一个完全独立的地址空间。页号n最多可规定为255,取决于目标存储器的配置。通常PAGE 0定为程序存储器,PAGE 1定为数据存储器。如果没有规定PAGE,则链接器就目标存储器配置在PAGE 0。

54 MEMORY命令的一般句法 MEMORY { ┆ }
PAGE 0: name 1 [(attr)]: origin=constant, length=constant PAGE n: name n [(attr)]: origin=constant, length=constant } 名字可以包含8个字符,A~Z、a~z、$、.、_均可。名字并没有特殊的含义,用来标记存储器的区间而已;名字都是内部记号,不需要保留在输出文件或者符号表中。不同PAGE上的存储器区间可以取相同的名字,但在同一PAGE内的名字不能相同,且不许重叠配置。

55 MEMORY命令的一般句法 MEMEORY { ┆ }
PAGE 0: name 1 [(attr)]: origin=constant, length=constant PAGE n: name n [(attr)]: origin=constant, length=constant } 规定一个存储区的长度,键入length、len或l都可以。 规定一个存储区的起始地址。键入origin、org或o都可以。这个值是一个16位二进制常数,可以用十进制数、八进制数或十六进制数表示。 任选项,为命名区规定1~4个属性。如果有选项,应写在括号内。 当输出段定位到存储器时,可利用属性加以限制。

56 属性选项一共有4项  R 规定可以对存储器执行读操作。  W 规定可以对存储器执行写操作。  X 规定存储器可以装入可执行的程序代码。  I 规定可以对存储器进行初始化。  如果一项属性都没有选,就可以将输出段不受限制地定位到任何一个存储器位置。任何一个没有规定属性的存储器都默认有全部4项属性。 Fill:任选项,不常用,在句法中未列出,为没有定位输出段的存储器空单元填充一个数,键入fill或f均可。这是2个字节的整型常数,可以是十进制数、八进制数或十六进制数表示。如fill=0FFFFh。

57 file1.obj file2.obj /* Input files */ -o prog.out /* Options */ MEMORY
/* Example command file with MEMORY directive */ file1.obj file2.obj /* Input files */ -o prog.out /* Options   */ MEMORY { PAGE0: ROM: origin=c00h, length=1000h PAGE1: SCRATCH: origin=60h, length=20h ONCHIP: origin=80h, length=200h } 名为ROM的程序存储器:4K字ROM,起始地址C00h。 名为SCRATCH的数据存储器:32字RAM,起始地址60h。 名为ONCHIP的数据存储器:512字RAM,起始地址为80h。

58 (2)SECTIONS命令 告诉链接器如何将输入段合成输出段 在可执行程序中定义输出段 功 规定输出段在存储器中的存放位置 能
允许重新命名输出项。 在链接器命令文件中,SECTIONS命令用大写字母,紧随其后并用大括号括起来的是关于输出段的详细说明。每一个输出段的说明都从段名开始。段名后面是一行说明段的内容和如何给段分配存储单元的性能参数。 注意

59 SECTIONS命令的一般句法 SECTIONS 性能参数 { name:[property,property,property,…] }
1)load allocation 定义将输出段加载到存储器中的 什么位置。 句法:load=allocation >allocation (或者用大于号代替“load=”) allocation (或者省掉“load=”) 其中allocation是关于输出段地址的说明,即给输出段分配存储单元。

60 例如: .text:load=0x1000 将输出段.text定位到一个特定地址。 .text:>ROM 将输出段.text定位到名为ROM的存储区。 .bss:>(RW)将.bss段定位到属性为R、W的存储区。 .text:align=0x80 将.text定位到0x80开始的存储区。 xn :align(n) { } 将xn定位到一个n字存储器块的任 何一个位置(n为 2的幂次)。 .text: PAGE 0 将输出段.text定位到PAGE 0。 如果要用到一个以上参数,可以将它们排成一行,例如: .text:>ROM align (16) PAGE(2) 或者为方便阅读,可用括号括起来: .text: load=(ROM align (16) PAGE(2))

61 2)Run allocation 用来定义输出段在存储器的
什么位置上开始运行。 句法:run=allocation run>allocation 或者用大于号代替等号 链接器为每个输出段在目标存储器中分配两个地址:一个是加载的地址,另一个是执行程序的地址。通常,这两个地址是相同的,可以认为每个输出段只有一个地址。有时要想把程序的加载和运行区分开(先将程序加载到ROM,然后在RAM中以较快的速度运行),只要用 SECTIONS命令让链接器对这个段定位两次就行了。一次是设置加载地址,另一次是设置运行地址。例如: fir: load=ROM, run=RAM

62 3) Input sections 用来定义由哪些输入段组
成输出段。 句法:{input_sections} 大多数情况下,在SECTIONS命令中是不列出每个输入文件的输入段的段名的: SECTIONS { .text: .data: .bss: } 这样,在链接时,链接器就将所有输入文件的.text段链接成.text输出段,其它段也一样。

63 用文件名和段名来明确地规定输入段 SECTIONS { .text: /* 建立 .text 输出段 */ f1.obj(.text)/* 链接源于f1.obj的.text段*/ f2.obj(sec1) /*链接源于f2.obj的sec1段*/ f3.obj /*链接源于f3.obj的所有段*/ f4.obj(.text,sec2)/*链接源于f4.obj的.text 段和sec2段 */ }

64 (3) MEMORY和SECTIONS命令的默认算法
{ PAGE 0: PROG: origin=0x0080, length=0xFF00 PAGE 1: DATA: origin=0x0080, length=0xFF80 } SECTIONS .text: PAGE=0 .data: PAGE=0 .cinit: PAGE=0 .bss: PAGE=1 .text和.data段定位到配置为PAGE 0上的存储器,即程序存储空间。 所有的.text输入段,链接成一个.text输出段,它是可执行的输出文件; 所有的.data输入段组合成.data输出段 所有的.bss输入段则组合成一个.bss输出段

65 6.多个文件的链接 例4-8 编写复位向量文件vectors.asm。
* * * * * * * * * * * * * * * * * * * * * * * * Reset vector for example.asm * .title “vectors.asm” .ref _c_int00 .sect “.vectors” B _c_int00 .end 引用example.asm中.def _c_int00定义的标号 编写example.asm见例4-1。

66 例4-9 根据例4-1和例4-8编写链接器命令文件example.cmd。 vectors.obj example.obj
假设目标存储器的配置如下: 程序存储器: EPROM E000h~FFFFh(片内) 数据存储器: SPRAM h~007Fh(片内) DARAM h~017Fh(片内) 例4-9 根据例4-1和例4-8编写链接器命令文件example.cmd。 vectors.obj example.obj -o example.out -m example.map -e _c_int /*软件仿真器的入口地址*/ 汇编生成目标文件example.obj和vectors.obj 生成可执行的输出文件example.out 生成一个映象文件example.map

67 .vectors: >VECS PAGE 0
MEMORY { PAGE 0: EPROM: org=0E000h, len=100h VECS: org=0FF80h, len=04h PAGE 1: SPRAM: org=0060h, len=20h DARAM: org=0080h, len=100h } SECTIONS .text: >EPROM PAGE 0 .data: >EPROM PAGE 0 .bss: >SPRAM PAGE 1 STACK: >DARAM PAGE 1 .vectors: >VECS PAGE 0

68 链接后生成一个可执行的输出文件example.out和映象文件 example.map。
映象文件中给出了存储器的配置情况、程序文本段、数据段、堆栈段、向量段在存储器中的定位表,以及全局符号在存储器中的位置。 可执行输出文件example.out装入目标系统后就可以运行了。系统复位后,PC首先指向0FF80h,这是复位向量地址。在这个地址上,有一条B _c_int00指令,程序马上跳转到_c_int00语句标号,从程序起始地址0E000h开始执行主程序。

69 例4-10 由例4-9得到的映象文件example.map。
************************************************************************** TMS320C54x COFF Linker PC Version 3.70 >> Linked Thu Oct 21 18:32: OUTPUT FILE NAME: < example.out> ENTRY POINT SYMBOL: "_c_int00" address: 0000e000 MEMORY CONFIGURATION name origin length used attr fill PAGE 0: EPROM e e RWIX VECS ff RWIX PAGE 1: SPRAM RWIX DARAM RWIX SECTION ALLOCATION MAP output attributes/ section page origin length input sections .text e 0000e example.obj (.text) 0000e vectors.obj (.text)

70 .data e 0000e example.obj (.data) 0000e01e vectors.obj (.data) .bss UNINITIALIZED example.obj (.bss) vectors.obj (.bss) STACK UNINITIALIZED example.obj (STACK) .vectors ff 0000ff vectors.obj (.vectors) GLOBAL SYMBOLS: SORTED ALPHABETICALLY BY Name address name address name bss bss 0000e data end 0000e text e000 _c_int00 0000e000 _c_int e text 0000e01e edata e016 etext end e data 0000e016 etext e01e edata [7 symbols]

71 C语言程序设计与混合编程 1 C语言程序设计 2 用C语言和汇编语言混合编程

72 C54x DSP软件设计的方法通常有三种: (1) 用汇编语言开发。此方式代码效率高,程序执行速度快,可以充分合理地利用芯片提供的硬件资源。但程序编写比较烦琐,可读性较差,可移植性较差,软件的修改和升级困难。 (2) 用C语言开发。CCS平台包括优化ANSI C编译器,从而可以在C源程序级进行开发调试,增强软件的可读性,提高了软件的开发速度,方便软件的修改和移植。然而,C编译器无法实现在任何情况下都能够合理地利用DSP芯片的各种资源。 (3) C语言和汇编语言混合编程开发。采用混合编程的方法能更好地达到设计要求,完成设计任务。

73 1 C语言程序设计 1.1 C语言特点及语法 1. C语言特点 C语言是国际上广泛流行的、很有发展前途的计算机高级语言。它适合于作为系统描述语言,既可以用来编写系统软件,也可以用来编写应用软件。 汇编语言依赖于计算机硬件,程序的可读性和可移植性比较差。而高级语言具有很好的可移植性,但是难以实现汇编语言的某些功能。因为C语言仍然需要通过编译、连接才能得到可执行目标程序,所以是一种高级语言。但C语言允许访问物理地址,能进行位操作,能实现汇编语言的大部分功能,能直接对硬件进行操作。C语言正是一种既可以访问物理地址又可以进行位操作的高级语言,这也是其与别的高级语言不同的地方。

74 C语言具有如下基本特点: (1) 语言简洁、紧凑,使用方便、灵活。 (2) 运算符丰富,表达式类型多样化。 (3) 数据结构类型丰富,具有现代化语言的各种数据结构。 (4) 具有结构化的控制语句。 (5) 语法限制不太严格,程序设计自由度大。 (6) C语言允许访问物理地址,能进行位操作,能实现汇编语言的大部分功能,能直接对硬件进行操作。

75 2. C语言基本语法概述 (1) 限定词 可由字母、数字和下划线组成。限定词必须以字母或下划线开头。区分大小写。 (2) 常量
常量包括整型常量(八进制、十进制、十六进制、长整型),字符常量,实型常量(小数形式、指数形式),字符串常量,表达式,算术表达式(整型表达式、实型表达式),逻辑表达式,字位表达式,强制类型转换表达式,逗号表达式(顺序表达式),赋值表达式,条件表达式,指针表达式。 (3) 数据定义 数据类型(int、short、long、unsigned、char、float、double、struct、union、enum)用ty-pedef定义的类型名,存储类型(auto、static、register、extern)变量的定义形式为: 存储类别 数据类型 变量列表; 例如:static float a,b,c;

76 (4) 函数定义 函数的定义形式为: 存储类别 数据类型 函数名(形参列表 ) 函数体 (5) 语句 语句包括表达式语句、函数调用语句、控制语句、复合语句、空语句。其中控制语句包括if语句、while语句、for语句和switch语句以及break语句、continue语句、return语句、goto语句。 (6) 预处理命令 预处理命令主要是指#define和#include语句。

77 1.2 系统堆栈的使用 C系统的堆栈可以完成的主要功能如下: * 分配局部变量; * 传递函数参数; * 保存所调用函数的返回地址; * 保存临时结果。 运行堆栈的增长方向是从高地址到低地址, 即入栈则地址减少,出栈则地址增加。堆栈的管理者是堆栈指针SP。堆栈的容量由链接器(Linker)设定。 如:使用C语言,在链接命令文件(.cmd文件)中加入选项 -stack 0x1000 则堆栈的容量被设为1000H个字。

78 1.3 存储器模式及分配 1. 存储器模式 C54x DSP定点处理器有两种类型的存储器模式:程序存储器和数据存储器。前者主要用于装载可执行代码,后者主要用于装载外部变量、静态变量、系统堆栈以及一些中间运算结果。 C54x DSP的程序代码或数据以段的形式装载于存储器中。C语言程序经C编译器编译后,生成七个可重定位的段,其中四个被称为已初始化段,三个被称为未初始化段。 四个已初始化段分别是: * .text段 包括可执行代码、字符串和编译器产生的常量。 * .cinit段 包括初始化变量和常量表。 * .const段 包括字符串常量和以const关键字定义的常量。 * .switch段 为.const语句建立的表格。

79 三个未初始化段分别是: * .bss段 保留全局和静态变量空间。在程序开始运行时,C的引导(boot)程序将数据从.cinit段拷贝到.bss段。 * .stack段 为C的系统堆栈分配存储空间,用于变量的传递。 * .sysmem段 为动态存储器函数malloc、calloc、realloc分配存储器空间。若C程序未用到此类函数,则C编译器不产生该段。 在编写链接命令文件(.cmd文件)时, .text、.cinit、.switch段通常可以链接到系统的ROM或者RAM中去,但是必须放在程序段(page 0);.const段通常可以链接到系统的ROM或者RAM中去,但是必须放在数据段(page l);而.bss、.stack和.sysmem段必须链接到系统的RAM中去,并且必须放在数据段(page l)。由实验程序所建的某工程的链接命令文件(.cmd文件),如例1所示。

80 【例1】某工程的链接命令文件(.cmd文件)
MEMORY /* TMS320C54x DSP存储器分配 */ { PAGE 0 : HPIRAM: origin = 0x100, length = 0x200 PROG: origin = 0x2000, length = 0x1000 PAGE 1 : DARAM1: origin = 0x03000, length = 0x1000 PAGE 2 : FLASHRAM: origin = 0x8000, length = 0x7fff }

81 SECTIONS { /* 由C 定义 */ .vectors : load = PROG page 0 /*中断向量表*/ .text : load = PROG page 0 /*可执行代码*/ .cinit : load = PROG page 0 /*初始化变量和常数表*/ .switch : load = PROG page 0 /*为.constant语句建立的表格*/ .stack : load = DARAM1 page 1 /*C 系统堆栈*/ .const : load = DARAM1 page 1 /*字符串常量和以const关键字定义的常量*/ .bss : load = DARAM1 page 1 /*全局和静态变量空间*/ .dbuffer1024 : {} > DARAM1 page 1, align (1024) .coeffs1024 : {} > DARAM1 page 1, align (1024) .hpibuffer : load = HPIRAM page 0 /*由汇编定义*/ .data : >DARAM page 1 /*汇编定义的数据段*/ }

82 2.存储器分配 C编译器提供的运行支持函数中包含有几个允许在运行时为变量分配存储器的函数,如malloc、calloc和recalloc。动态分配不是C语言本身的标准,而是由运行支持函数所提供的。 为全局pool和heap分配的存储器空间定义在.sysmem块中。.sysmem段的大小可由链接器选项中的-heap项来设定,其方法是在-heap项后加一个常数。与堆栈类似,连接器也创建一个全局符号_SYSMEM_SIZE。.sysmem段的大小由_SYSMEM_SIZE的数值来确定,默认值为l K字。为了在.bss段中保留空间,对于大的数据,可以用heap为其分配空间,而不将它们说明为全局或静态的。

83 (1) 静态和全局变量的存储器分配 在C程序中,静态变量被分配一个惟一的连续空间,该空间的地址由链接器决定。编译器安排这些变量的空间被分配在若干个字的长度中,以保证每个变量按字边界对准。全局变量分配到数据空间,在同一模块中定义的变量分配到同一个连续的存储空间。 (2) 域/结构的对准 C编译器在为结构分配存储空间时,它分配足够的字以包含所有的结构成员。一组结构中,每个结构开始于字边界。所有的非域类型对准于字的边界。对域应分配足够多的比特。相邻域应装入一个字的相邻比特,不能跨越两个字,否则整个域会被分配到下一个字中。

84 1.4 TMS320C54x DSP的C语言规则 1.寄存器规则 在C环境中,定义了严格的寄存器规则。寄存器规则明确了编译器如何使用寄存器,以及在函数的调用过程中如何保护寄存器。调用函数时,某些寄存器不必由调用者来保护,而由被调用函数负责保护。如果调用者需要使用没有保护的寄存器,则调用者在调用函数前必须对这些寄存器予以保护。在编写汇编语言和C语言的接口程序时,这些规则非常重要。如果编写时不遵守寄存器的使用规则,则C环境将会被破坏。

85 寄存器规则概括如下: (1) 辅助寄存器 ARl、AR6、AR7由被调用函数保护,即可以在函数执行过程中修改,但在函数返回时必须恢复。在C54x DSP中,编译器将ARl和AR6用作寄存器变量。其中,AR1被用作第一个寄存器变量,AR6被用作第二个寄存器变量,其顺序不能改变。另外五个辅助寄存器AR0、AR2、AR3、AR4、AR5则可以自由使用,即在函数执行过程中可以对它们进行修改,不必恢复。 (2) 栈指针SP 堆栈指针SP在函数调用时必须予以保护,但这种保护是自动的,即在返回时,压入堆栈的内容都将被弹出。

86 (3) ARP 在函数进入和返回时,必须为0,即当前辅助寄存器必须为AR0,而函数执行时则可以是其他值。
(4) OVM 在默认情况下,编译器总认为OVM是0。因此,若在汇编程序中将OVM置为1,则在返回C环境时,必须将其恢复为0。 (5) 其他状态位和寄存器可以任意使用,不必恢复。

87 2.函数调用规则 (1) 参数传递 在函数调用前,将参数以逆序压入运行堆栈。所谓逆序,即最右边的参数最先压入栈,然后自右向左将参数依次压入栈,直至第二个参数入栈完毕。对第一个参数,则不需压入堆栈,而是放入累加器A中,由A进行传递。若参数是长整型和浮点数时,则低位字先压入栈,高位字后压入栈。若参数中有结构,则调用函数先给结构分配空间,而该空间的地址则通过累加器A传递给被调用函数。 参数传递到函数,同时该函数使用了局部变量并调用另一个函数。第一个参数不由堆栈传递,而是放入累加器A中传递。

88 (2) 被调用函数的执行过程 被调用函数依次执行以下几项任务:
函数调用时局部帧的产生过程:函数调用时,编译器在运行堆栈中建立一个帧用以存储信息。当前函数帧成为局部帧,C环境利用局部帧来保护调用者的有关信息、传递参数和为局部变量分配存储空间。每调用一个函数,就建立一个新的局部帧。局部帧空间的一部分用于分配参数区(局部参数区),被传递的参数放入局部参数区,即压入堆栈,再传递到其他被调用的函数中。 (2) 被调用函数的执行过程 被调用函数依次执行以下几项任务: * 如果被调用函数修改了寄存器(如AR1、AR6、AR7),则必须将它们压栈保护。 * 当被调用函数需分配内存来建立局部变量及参数区时,SP向低地址移动一个常数(即SP减去一个常数),该常数的计算方法如下: 常数=局部变量长度+参数区中调用其他函数的参数长度

89 * SP向高地址移动一个常数(即SP加上一个常数),该常数即为图1(b)所确定的常数,这样就又恢复了帧和参数区。
* 被调用函数执行程序。 * 如果被调用函数修改了寄存器ARl、AR6和AR7,则必须予以恢复。将函数的返回值放入累加器A中。整数和指针在累加器A的低16位中返回,浮点数和长整型数在累加器A的32位中返回。如果函数返回一个结构体,则被调用函数将结构体的内容拷贝到累加器A所指向的存储器空间。如果函数没有返回值,则将累加器A置0,撤销为局部帧开辟的存储空间。ARP在从函数返回时,必须为0,即当前辅助寄存器为AR0。参数不是由被调用函数弹出堆栈的,而是由调用函数弹出的。 * SP向高地址移动一个常数(即SP加上一个常数),该常数即为图1(b)所确定的常数,这样就又恢复了帧和参数区。 * 被调用函数恢复所有保存的寄存器。 * 函数返回。 当C程序编译成汇编后, 上述过程如例2所示。

90 【例2】 be_called: ;函数入口 PSHM AR ;保存AR6 PSHM AR ;保存AR7 FRAME # ;分配帧和参数区 ;函数主体 FRAME # ;恢复原来的帧和参数区 PSHM AR ;恢复AR7 PSHM AR ;恢复AR6 RET ;函数返回

91 (3) 入参数区和局部变量区 当编译器采用CPL=1的编译模式时,采用直接寻址即可很容易寻址到参数区和局部变量区。例如:
LD *SP(6), A ;将SP+6所指单元的内容送累加器A 以上直接寻址方式的最大偏移量为127,所以当寻址超过127时,可以将SP值复制到辅助寄存器中(如AR7),以此代替SP进行长偏移寻址。例如: MVMM SP, AR ;将SP的值送AR7 ... LD *AR7(128), A ;AR7加128后所指向的单元内容送A

92 (4) 分配帧及使用32位内存读/写指令。 * 一些C54x DSP指令提供了一次读/写32位的操作(如DLD和DADD),因此必须保证32位对象存放在偶地址开始的内存中。为了保证这一点,C编译器需要初始化SP,使其为偶数值。 * 由于CALL指令使SP减1,因此SP在函数入口设置为奇数;而长调用FCALL指令使SP减2,故SP在函数入口设定为偶数。 * 使用CALL指令时,应确保PSHM指令的数目加上FRAME指令分配字的数目为奇数,这样SP就指向一个偶地址;同样,使用长调用FCALL指令时,应保证PSHM指令的数目与FRAME指令分配字的数目和为偶数,以保证SP指向偶地址。 * 为了确保32位对象在偶地址,可通过设置SP的相对地址来实现。 * 由于中断调用时不能确保SP为奇数还是偶数,因此,中断分配SP指向偶数地址。

93 3.中断函数 C函数可以直接处理中断。但是在用C语言编写中断程序时,应注意以下几点: (1) 中断的使能和屏蔽由程序员自己来设置。这一点可以通过内嵌汇编语句来控制中断的使能和屏蔽,即通过内嵌汇编语句来设置中断屏蔽寄存器IMR及INTM,也可通过调用汇编程序函数来实现。 (2) 中断程序不能有入口参数,即使声明,也会被忽略。 (3) 中断子程序即使被普通的C程序调用,也是无效的,因为所有的寄存器都已经被保护了。

94 (4) 将一个程序与某个中断进行关联时,必须在相应的中断矢量处放置一条跳转指令。采用
(4) 将一个程序与某个中断进行关联时,必须在相应的中断矢量处放置一条跳转指令。采用.sect汇编指令可以建立这样一个跳转指令表以实现该功能。 (5) 在汇编语言中,必须在中断程序名前加上一个下划线_。 (6) 用C语言编写的中断程序必须用关键字interrupt说明。 (7) 中断程序用到的所有寄存器,包括状态寄存器都必须保护。 (8) 如果中断程序中调用了其他的程序,则所有的寄存器都必须保护。

95 4.表达式分析 当C程序中需要计算整型表达式时,必须注意到以下几点: (1) 算术上溢和下溢。TMS320C54x DSP采用16位操作数,产生40位结果,算术溢出是不能以一种可预测的方式进行处理的。 (2) 整除和取模。TMS320C54x DSP没有直接提供整除指令,因此,所有的整除和取模运算都需要调用库函数来实现。这些函数将运算表达式的右操作数压入堆栈,将左操作数放入累加器的低16位。函数的计算结果在累加器中返回。

96 (3) 32位表达式分析。一些运算在函数调用时并不遵循标准的C调用规则,其目的在于提高程序运行速度和减少程序代码空间。这些运算包括通过变量的左移、右移、除法、取模和乘法。

97 有符号结果: int n1,n2,result; result=((long)n1*(long)n2)>>16; 无符号结果: unsigned n1,n2,result; result=((unsigned long)n1*(unsigned long)n2)>>16;

98 5 C语言程序实例 C语言程序编写过程步骤: (1) 编辑器编辑C程序readdata.c; (2) 编译程序将C程序编译汇编成目标文件readdata.obj; (3) 编辑一个链接命令文件(.cmd文件); (4) 链接生成.out文件,用硬件仿真器进行调试。

99 【例3】用C语言编写C54x DSP的I/O口的读程序,实现从I/O口地址8000H连续读入1000个数据并存入数组中。
【例3】 C程序readdata.c: #include“portio.h” /*包含头文件portio.h*/ #define RD_PORT 0x /*定义输入I/O口*/ static int indata[1000]; /*定义全局数组*/ main() { int I; for(I=0;I<1000;I++) portRead(RD_PORT); /*从I/O口读数据*/ }

100 2  用C语言和汇编语言混合编程 1 C54x DSP混合编程方法 C语言和汇编语言的混合编程有以下几种方法: (1) 独立编写汇编程序和C程序,分开编译或汇编,形成各自的目标代码模块,再用链接器将C模块和汇编模块链接起来。这种方法灵活性较大,但用户必须自己维护各汇编模块的入口和出口代码,自己计算传递的参数在堆栈中的偏移量,工作量较大,但能做到对程序的绝对控制。 (2) 在C程序中使用汇编程序中定义的变量和常量。 (3) 在C程序中直接内嵌汇编语句。用此种方法可以在C程序中实现C语言无法实现的一些硬件控制功能,如修改中断控制寄存器,中断标志寄存器等。

101 (4) 将C程序编译生成相应的汇编程序,手工修改和优化C编译器生成的汇编代码。采用此种方法时,可以控制C编译器,使之产生具有交叉列表的C程序和与之对应的汇编程序,而程序员可以对其中的汇编语句进行修改。优化之后,对汇编程序进行汇编,产生目标文件。根据经验,只要程序员对C和汇编均很熟悉,这种混合汇编方法的效率可以做得很高。但是,由交叉列表产生的C程序对应的汇编程序往往读起来颇费劲,因此对一般程序员不提倡使用这种方法。

102 1. 独立的C和汇编模块接口 独立的C和汇编模块接口是一种常用的C和汇编语言接口方法。采用此方法在编写C程序和汇编程序时,必须遵循有关的调用规则和寄存器规则。调用规则和寄存器规则已在前面作了详述。如果遵循了这些规则,那么C和汇编语言之间的接口是非常方便的。 C程序可以直接引用汇编程序中定义的变量和子程序; 汇编程序也可以引用C程序中定义的变量和子程序。

103 【例5】 C程序调用汇编子程序: extern int asmfunc( ); /*声明外部的汇编子程序*/ /*注意函数名前不要加下划线*/ int gvar; /*定义全局变量*/ main( ) { int i=5; i =asmfunc(i); /*进行函数调用*/ }

104 汇编程序: _asmfunc: ;函数名前一定要有下划线 STL A,*(_gvar) ;i的值在累加器A中 ADD*(_gvar),A ;返回结果在累加器A中 RET ;子程序返回

105 2. C程序中访问汇编程序变量 从C程序中访问汇编程序中定义的变量或常数时,根据变量和常数定义的位置和方法的不同,可分为三种情况。 (1) 访问在.bss段中定义的变量,方法如下: * 采用.bss命令定义变量; * 用.global将变量说明为外部变量; * 在汇编变量名前加下划线“_”; * 在C程序中将变量说明为外部变量,然后就可以像访问普通变量一样访问它。

106 例如: 汇编程序: /*注意变量名前都有下划线*/ .bss _var,1 .global _var; ;声明为外部变量 C程序: extern int var; /*外部变量*/ var =1;

107 (2) 访问未在.bss段定义的变量,如当C程序访问在汇编程序中定义的常数表时,则方法更复杂一些。此时,定义一个指向该变量的指针,然后在C程序中间接访问它。在汇编程序中定义此常数表时,最好定义一个单独的段。然后,定义一个指向该表起始地址的全局标号,可以在链接时将它分配至任意可用的存储器空间。如果要在C程序中访问它,则必须在C程序中以extern方式予以声明,并且变量名前不必加下划线“_”。这样就可以像访问其他普通变量一样进行访问。C程序中访问汇编常数表如例6所示。

108 【例6】 C程序中访问汇编常数表 汇编程序: .global _sine ;定义外部变量
.sect "sine_tab" ;定义一个独立的块装常数表 _sine︰ ;常数表首址 .word 0 .word 50 .word 100 .word 200 C程序: extern int sine[]; /*定义外部变量*/ int *sine_ptr=sine; /*定义一个C指针*/ f=sine_ptr[2]; /*访问sine_ptr*/

109 (3) 对于那些在汇编中以.set和.global定义的全局常数,也可以在C程序中访问,不过要用到一些特殊的方法。一般来说,在C程序中和汇编程序中定义的变量,其符号表包含的是变量的地址。而对于汇编程序中定义的常数,符号表包含的是常数值。编译器并不能区分哪些符号表包含的是变量的地址,哪些是变量的值。因此,如果要在C程序中访问汇编程序中的常数,则不能直接用常数的符号名,而应在常数符号名前加一个地址操作符&,以示与变量的区别,这样才能得到常数值。

110 例如: 汇编程序: _tab_size .set 1000 .global _tab_size C程序: extern int tab_size; #define TAB_SIZE ((int)(&tab_size)); for(i=0; i< TAB_SIZE; ++i)

111 3. C程序中直接嵌入汇编语句 在C程序中直接嵌入汇编语句是一种直接的C和汇编的接口方法。此种方法可以在C程序中实现C语言无法实现的一些硬件控制功能,如修改中断控制寄存器、中断标志寄存器等。 嵌入汇编语句的方法比较简单,只需在汇编语句的两边加上双引号和括号,并且在括号前加上asm标识符即可。即: asm(“ 汇编语句 ”); 如:asm (" RSBX INTM "); /*开中断*/ asm (" SSBX XF "); /*XF置高电平*/ asm (" NOP ");

112 注意:括号中引号内的汇编语句的语法和通常的汇编编程的语法一样。不要破坏C环境,因为C编译器并不检查和分析嵌入的汇编语句。插入跳转语句和标号会产生不可预测的结果。不要让汇编语句改变C程序中变量的值。不要在汇编语句中加入汇编器选项而改变汇编环境。 修改编译器的输出可以控制C编译器,从而产生具有交叉列表的汇编程序。而程序员可以对其中的汇编语句进行修改,之后再对汇编程序进行汇编,可产生最终的目标文件。注意,修改汇编语句时切勿破坏C环境。

113 【例7】 用混合编程的设计方法实现四个数码管同时循环显示0~9十个数,每次显示的数以1递增。
2.2 混合编程实例 【例7】 用混合编程的设计方法实现四个数码管同时循环显示0~9十个数,每次显示的数以1递增。 C语言设计的主程序如下: ioport unsigned port0; //控制数码管选通的控制接口地址为0 ioport unsigned port1; //向数码管送显示内容的数据接口地址为1 /*发光二极管的显示代码*/ char leddisp[] ={0xf6,0x77,0x14,0xb3,0xb6,0xd4,0xe6,0xe7,0x34,0xf7}; void main() {

114 char ledcnt=0 ; c54_init(); /*调用5402芯片初始化函数*/ for (;;) { ledcnt = (ledcnt+1)%10 ; /*模10循环递增*/ port0 = 0xf ; /*向地址为0的口送1111b:四个数码管均选通*/ port1 = leddisp[ledcnt]; /*向地址为1的口送欲显示之数*/ delay3() ; /*调用延时函数,停顿片刻*/ }

115 用汇编程序设计对5402芯片初始化的函数如下: .title "C54_INIT.ASM" .mmregs .def _c54_init .text _c54_init: STM 0,ST ;ARP=0、DP=0 STM B,ST1 ;CPL=0 DP直接寻址、中断屏蔽、溢出保护、符号扩展、FRCT有效、ARP无效、 ;ASM=-1 STM B,PMST ;中断定位2000H STM 0x7FFF,SWWSR STM B,CLKMD ;PLL 10倍频 RET .end

116 用汇编程序设计的实现延时的函数如下: .title "delay3.asm" .mmregs .def _delay3 .text _delay3: STM #0X2FF,AR0 delay30: STM #0X2FF,AR2 delay31: BANZ delay31,*AR2- BANZ delay30,*AR0- RET .end

117 链接命令文件如下: -O DELAY1.OUT MEMORY { PAGE 0 : HPIRAM: origin = 0x100, length = 0x200 PROG: origin = 0x2000, length = 0x1000 PAGE 1 : DARAM1: origin = 0x03000, length = 0x1000 PAGE 2 : FLASHRAM: origin = 0x8000, length = 0xffff }

118 .text : load = PROG page 0 /*可执行代码*/
SECTIONS { .text : load = PROG page 0 /*可执行代码*/ .cinit : load = PROG page 0 /*初始化变量与常数表*/ .stack : load = DARAM1 page 1 /*C 系统堆栈*/ .const : load = DARAM1 page 1 /*常数*/ .bss : load = DARAM1 page 1 /*全局与静态变量*/ }

119 基础完整程序编写实例 初学者编写的第一个程序通常是控制XF引脚的变化,然 后用示波器测量XF脚波形或观察与相接的LED。这个程
序也常常用来测试一下DSP能否正常工作。 例1. 最简单的程序:控制XF引脚周期性变化        实验目的:通过简单的程序了解DSP程序的结构, 熟悉CCS开发环境。 ************************************************************ *最简单的程序:TestXF1.asm *循环对XF位置1和清0,用示波器可以在XF脚检测到电 *平高低周期性变化 *常用于检测DSP是否工作。

120 .mmregs             ;预定义的寄存器             .def    CodeStart   ;定义程序入口标记                         .text               ;程序区 CodeStart:                      ;程序入口             SSBX    XF          ;XF置1             RPT     #999        ;重复执行1000次空指 ;令产生延时             NOP             RSBX    XF          ;XF清0             NOP                         B       CodeStart   ;跳转到程序开头循环 ;执行             .end   

121 NOP指令执行时间为一个时钟周期,设DSP工作频率是50MHz,可以估算出XF引脚电平的变化频率约为:50M/2000=25kHz
在没有示波器的情况下,就要将程序1稍作改进,增加延时,用一个延时子程序将XF脚电平变化频率降到肉眼可分辨的程度,就可以用LED来显示电平的变化,程序如下:

122 例2 子程序调用 实验目的:学习子程序的调用 ************************************************************* *TestXF2.asm *对TestXF1.asm稍作改进,用延时子程序设置 *较长的延时 *可以用试验板上的LED看到XF引脚电平的变化

123 .mmregs             ;预定义的寄存器             .def    CodeStart  ;定义程序入口标记                        .text               ;程序区 CodeStart:                      ;程序入口             SSBX    XF          ;XF置1             CALL    Delay     ;调用延时程序             RSBX    XF          ;XF清0             CALL    Delay       ;调用延时程序             B       CodeStart   ;跳转到程序开头循环执行

124 **************************************************************
*延时子程序:Delay *用两级减一计数器来延时。调整AR1和AR2的大小LED *闪烁的频率不同 Delay:                  STM     #999,AR1    ;循环次数1000 LOOP1:      STM     #4999, AR2  ;循环次数5000 LOOP2:      BANZ    LOOP2,*AR2- ;如果AR2不等 ;于0,AR2减1,跳转到LOOP2             BANZ    LOOP1,*AR1- ;如果AR1不等 ;于0,AR1减1,跳转到LOOP1             RET             .end

125 注意这种延时方法并不精确,需要精确定时必须
用定时器。按此法延时的近似公式为: 4*(AR2+1)*(AR1+1)*时钟周期 当DSP工作在50MHz(时钟周期20ns),AR1=999, AR2=4999时,延时约为400ms,则LED闪烁的 周期为800ms,频率1.25Hz

126 链接配置文件 一个完整的DSP程序至少包含三个部分:程序代码、中断向量表、链接配置文件(*.cmd)。这里介绍一下链接配置文件文件,对本次实验影响不大的中断向量表将在后文介绍。 连接配置文件的确定了程序链接成最终可执行代码时的选项,其中有很多条目,实现不同方面的选项,其中最常用的也是必须的有两条: 1.存贮器的分配 2.标明程序入口。 以本次实验为例,下面的简单的链接配置文件就够用了:

127 /* TestXF.cmd */ -e CodeStart        /*程序入口,必须在程序中定义相应的标号*/ MEMORY  {        PAGE 0:PRAM: org=0100h len=0F00h   /*定义程序存贮区,起始0100H,长度0F00H*/         }  SECTIONS {         .text:>PRAM PAGE 0              /*将.text段映射到page0的param区*/         }

128 由于每个程序都需要一个链接配置文件,可以编写一个满足通常需要的链接配置文件。作为本手册通用的链接配置文件如下,可以满足本书大部分程序的需要。在未特别指明的情况下使用这个通用的链接配置文件:

129 /* 5402.cmd */ -e  CodeStart           /*程序入口,必须在程序中定义相应的标号*/ -m  map.map           /*生成存储器映射报告文件 */ MEMORY  {     PAGE 0:         VECT:  org=0080h    len=0010h   /*中断向量表*/         PARAM: org=100h     len=0F00h   /*代码区*/     PAGE 1:         DARAM: org=1000h    len=1000h   /*数据区*/ } SECTIONS    {     .text       :> PARAM    PAGE 0      /*代码段*/     .vectors    :> VECT     PAGE 0      /*中断向量表*/     STACK   :> DARAM    PAGE 1      /*堆栈*/     .bss        :> DARAM    PAGE 1      /*未命名段*/     .data       :> DARAM    PAGE 1      /*数据段*/     }

130 中断向量表 中断向量表是DSP程序的重要组成部分,当有中断发生并且处于允许状态时,程序指针跳转到中断向量表中对应的中断地址。由于中断服务程序一般较长,通常中断向量表存放的是一个跳转指令,指向实际的中断服务程序。下面是5402中断向量表的一个范例,可以作为模板,使用时稍作修改就行:

131 ******************************************************
5402Vectors.asm /*完整的5402中断向量表示例*/ 5402共有30个中断向量,每个向量占4个字 的空间。 使用向量一般用一条跳转指令转到相应中断 服务子程序,其余空位用NOP填充未使用的向量 直接用RETE返回,是为了防止意外进入未用中 断。

132 .sect ".vectors" ;开始命名段.vecotrs
.global CodeStart ;引用程序入口的全局符 号定义 ;…引用其它中断程序入口的全局符 .align 0x80 ; 中断向量表必须对齐128字的页边界 RESET: B CodeStart ; Reset中断向量,跳转到程序入口 NOP ;用NOP填充表中其余空字 NOP ;B指令占了两个字,所以要填两个NOP NMI: RETE ;不可屏蔽中断 NOP NOP NOP

133 ; 软件中断 SINT17 .space 4*16 ;软件中断使用较少,简单起见用0填充 SINT18 .space 4*16 SINT19 .space 4*16 SINT20 .space 4*16 SINT21 .space 4*16 SINT22 .space 4*16 SINT23 .space 4*16 SINT24 .space 4*16 SINT25 .space 4*16 SINT26 .space 4*16 SINT27 .space 4*16 SINT28 .space 4*16 SINT29 .space 4*16 SINT30 .space 4*16 中断向量表.doc

134 本章小结 C54x DSP软件设计的方法通常有三种: (1) 用汇编语言开发。用汇编语言开发产品,周期长,软件的修改和升级困难。 (2) 用C语言开发。这种方式的优点是可以增强软件的可读性,提高了软件的开发速度,方便软件的修改和移植。但无法实现在任何情况下都能够合理地利用DSP芯片的各种资源。 (3) 采用C和汇编语言混合编程开发,能更好地达到设计要求,完成设计任务。 C语言和汇编语言的混合编程有以下几种方法: (1) 独立编写汇编程序和C程序,分开编译或汇编,形成各自的目标代码模块,再用链接器将C模块和汇编模块链接起来。 (2) 在C程序中使用汇编程序中定义的变量和常量。 (3) 在C程序中直接内嵌汇编语句。 (4) 将C程序编译生成相应的汇编程序,手工修改和优化C编译器生成的汇编代码。

135 习 题 1、 汇编语句格式包含哪几个部分? 2、为什么通常需要采用C语言和汇编语言的混合编程方法?
习 题 1、 汇编语句格式包含哪几个部分? 2、为什么通常需要采用C语言和汇编语言的混合编程方法? 3 、C语言和汇编语言的混合编程方法主要有几种?各有何特点? 4 、试解释下段指令: STM #10, AR3 LOOP:ADD *AR2+,A BANZ LOOP,*AR3- STM #10,AR3 5、试解释下段指令: RPT #4 MVPD table,*AR2+ HERE: B HERE


Download ppt "DSP技术及应用 Digital Signal Processor 数字信号处理器."

Similar presentations


Ads by Google