摘要:提出并分析了在考虑程序代码效率、执行效率和程序的可读性及可移植性的情况下,对于TMS320C54X系列,采用C语言和汇编语言混合编程的优点;详细阐述了混合编程方法的特点、应遵循的规则和详细接口规范;给出了程序设计实例。
关键词:数字信号处理(DSP) TMS320C54X 混合编程
数字信号处理技术是一门涉及许多学科的新技术,广泛应用于军事、工业、航空、航天等诸多领域。数字信号处理技术由于其运行量大和实现算法复杂,通常采用专用的DSP芯片来实现。美国TI公司的TMS320C54X系列芯片是为实现低功耗和高性能而专门设计的定点DSP芯片。
TMS320C54X的主要特点包括:高运算速度、优化的CPU结构、低功耗方式和智能外设等。
使用专用DSP芯片进行设计与开发包括硬件和软件两个方面。通常有以下三种软件设计方式:
(1)完全用C语言开发。TI公司提供了用于C语言开发的CCS(CODE COMPOSER STUDIO)平台。该平台包括了优化ANSI C编译器,从而可以在C源程序级进行开发调方式。这种方式大大提高了软件的开发速度和可读性,方便了软件的修改和移植。但是,在某些情况下,C代码的效率还是无法与手工编写的汇编代码的效率相比,如FFT编程。这是因为即使最佳的C编译器,也无法在所有的飞速下都能够最合理地利用DSP芯片所提供的各种资源。此外,用C语言实现DSP芯片的某些硬件控制也不如汇编程序方便,有些甚至无法用C语言实现。
(2)完全有汇编语言开发,TI公司提供了用于汇编语言开发的针对TMS320C54X的汇编语言。用户可以用它进行软件开发。此种方式可以更为合理地充分利用DSP芯片提供的硬件资源,其代码效率高,程序执行速度快。但是用DSP芯片的汇编语言编写程序是比较繁杂的。一般来说,不同公司的芯片汇编语言是不同的,即使是同一公司的芯片,由于片类型的不同(如定点和浮点),芯片的升级换代,其汇编语言也不同。因此,用汇编语言开发基于某种DSP芯片的产品周期较长,并且软件的修改和升级较困难,这些都是因为汇编语言的可读性和可移植性较差所致。
(3)用C语言和汇编语言混合编程开发。为了充分利用DSP芯片的资源,更好地发挥C语言和汇编语言进行软件开发的各自的优点,可以将两者有机结合起来,兼顾两者的优点,避免其弊端。因此,在很多情况下,采用混合编程方法能更好地达到设计要求,完成设计功能。但是,采用C语言和汇编语言混合编程必须遵循一些有关的规则,否则会遇到一些意想不到的问题,给开发设计带来许多麻烦。
本文提出了基于DSP(TMS320C54X)的C语言和汇编语言混合编程的程序设计方法,并给出了混合编程应遵循的规则和需要注意的一些问题。
1 TMS320C54X的C语言和汇编语言混合编程方法
C语言和汇编语言的混合编程有以下几种方法:
(1)独立编写编编程序和C程序,分开编译或汇编形成各自的目标代码模块,用链接器将C模块和汇编模块链接起来,这是一种灵活性较大的方法。采用这种方法,C程序可以调用汇编程序,并且可以访问汇编程序中定义的变量。同样,汇编程序也可以调用C程序或访问C程序中定义的变量。但用户必须自己维护各汇编模块的入口和出口代码,自己计算传递的参数在堆栈中的偏移量,工作量稍大,但能做到对程序的绝对控制。
(2)在C程序中直接内嵌汇编语句。此种方法可以在C程序中实现C语言无法实现的一些硬件控制功能,如修改中断控制寄存器、中断标志寄存器等。嵌入汇编语句的方法比较简单,只需在汇编语句的两边加上括号和双引号,并且在括号前加上asm标识符即可,即asm(“汇编语句”)。
但是,采用此种方法必须注意以下几点:
①括号中的汇编语句必须以标号、空格、tab、分号开头,这和通常的汇编编程的语法一样。
②不要破坏C环境,因为C编译器并不检查和分析嵌入的汇编语句。
③插入跳转语句和标号会产生不可预测的结果。
④汇编语句不要改变C程序中变量的值。
⑤不要在汇编语句中加入汇编器选项而改变汇编环境。
(3)将C程序编译生成相应的汇编程序,手工修改和优化C编译器生成的汇编代码。采用此种方法可以控制C编程器从而产生个有交叉列表的汇编程序,而且程序员可能对其中的汇编语句进行修改。之后,对汇编程序进行汇编可产生目标文件。注意,修改汇编语句时切勿破坏C环境。
2 混合编程应遵循的规则和详细的接口规范
2.1 寄存器规则
在C环境中,定义了严格的寄存器规则。寄存器规则明确了编译器如何使用寄存器以及在函数调用过程中如何保护寄存器。调用函数时,被调用函数负责保护某些寄存器,这些寄存器不必由调用者来保护。如果调用者需要使用没有保护的寄存器,则调用者在调用函数前必须予以保护。下面具体说明寄存器规则:
(1)辅助寄存器AR1、AR6、AR7由被调用函数保护,即可以在函数执行过程中修改,但在函数返回时必须恢复。在TMS320C54X中,编译器将AR1和AR6用作寄存器变量。其中,AR1被用作第一个寄存器变量,AR6被有作第二个寄存器变量,其顺序不能改变。
AR0、AR2、AR3、AR4、AR5可以自由使用,即在函数执行过程中可以修改,而且不必恢复。
(2)堆栈指针SP在函数调用时必须予以保护,但其是自动保护的,即在返回时,压入椎栈的内容都将被全部弹出。
(3)ARP在函数进入和返回时,必须为0,即当前辅助寄存器为AR0。函数执行时可以是其它值。
(4)在缺省的情况下,编译器总是认为OVM为0。因此,若在汇编程序中将OVM置为1,则在返回C环境时,必须将其恢复为0。
(5)其它状态位和寄存器在子程序中可以任意使用,不必恢复。
2.2 标识符合名规则
C编译器将C程序定义的所有标识符前都加一下划线(-)。因此,必须将在C程序中要引用的汇编变量和汇编模块子程序的名字前加上下划线(-)。如果变量仅在汇编模块中使用,则不加下划线(-)的变量名可以任意使用,而不会与C标识符发生冲突。
2.3 函数调用规则
C编译器规定了一组严格的函数调用规则。除了特殊的运行支持函数外,任何调用C函数或被C函数所调用的函数都必须遵循这些规则,否则就会破坏C环境,造成不可预测的结果。
2.3.1 参数传递
函数调用前,将参数以逆序压入运行堆栈,即最右边的参数最先入栈,然后自右向左将参数依次入栈。但是,对于TMS320C54X,在函数调用时,第一个参数放入累加器A中进行传递。若参数是长整型和浮点数时,则低位字先压栈,高位字后压栈。若参数中有结构形式,则调用函数给结构分配空间,其地址通过累加器A传递给被调用函数。
2.3.2 结果返回
函数调用结束后,将返回值置于累加器A中。整数和指针在累加器A的低16位中返回。浮点数和长整型数在累加器A的32位中返回。
2.3.3 函数调用时需注意的一些问题
参数不是由被调用函数弹出椎栈,而是由调用函数弹出。因此调用函数可以传递任意数目的参数至函数,而且函数不必知道有多少个参数传递。
在汇编程序中,除了自动初始化全局变量外,不要将.cinit段用作其它用途。C程序在boot.asm中的启动程序认为.cinit段中放置的全部是初始化表,因此将其它一些信息放入.cinit段将产生不可预料的结果。
如果要定义在C程序中访问的汇编变量或调用的汇编子程序,则必须在汇编程序中用.global说明为外部;同样,如果要定义在汇编程序中要调用的C函数或访问变量,也必须在C程序中将其以exterm说明为外部。
下面给出具体例子。
C程序:
Extern int asmfunc ( ); /*声明外部的汇编子程序*/
/*注意函数名前不要加下划线*/
int gvar; /*定义全局变量*/
main( )
{
int I=3;
I=asmfunc(i); /*进行函数调用*/
}
汇编程序:
_asmfunc: ;函数名胶一定要有下划线
ADD *(-gvar),A ;I的值在累加器A中
STL A,*(-gvar) ;返回结果在累加器A中
RETD ;子程序返回
3 C语言和汇编语言混合编程软件设计要点和具体设计实例
在智能测试仪表的软件设计中,要完成对振动信号进行数据采集,从而进行频谱分析的主要功能。在这个具体实例中,主程序要完成系统的实始化,进行用户操作界面显示,并且进行键值查询,根据按键值决定程序的流程,从而完成仪器设计要求的各项功能。主程序对运行速度和代码效率要求不高,但要求可读性强且修改维护容易,因此采和C语言实现。另外一些子程序如FFT算法,对运行速度要求较高,可用汇编语言实现。而DSP与PC机的通讯与硬件串口有关,采用汇编语言易于实现编程,因此也采用汇编语言钭其做成子程序,而在C语言编写的主程序中调用它。
在实际的系统软件设计中,可以根据具体情况来选择将某一模块或某一子程序用C语言或汇编语言来实现,从而更充分地发挥两者的优势,将DSP技术更加充分地利用于各种系统设计中。
在此,仅以A/D数据采集为例来具体说明C语言和汇编语言的相互调用问题。由于篇幅有限,仅列出与混合编程及相互调用相关的部分程序以供分析和参考。
C程序:
Extern void ad1247( ); /*定义外部的汇编函数*/
/*函数名前不必加下划线*/
main( ) /*主程序*/
{
int adlength=2048; /*数据的采样长度*/
int adfreq=40; /*数据采样频率*/
.
.
}
汇编程序:
.mmregs
FP .set AR7
.sect".text"
.global_ad1247 ;定义汇编子程序,以global说明要被外部的C程序调用,函数名前要有划线
.bss len,1 ;定义局部变量len
.bss freq,1 ;定义局站变量freq,变量名前不必加下划线
.
.
.
_ad1247 ;程序入口
PSHM AR6 ;保护AR6
PSHM AR7 ;保护AR6
PSHM AR1 ;保护AR1
FRAME #-16 ;为建立的局部帧分配空间函数体
STL A,*(len) ;将置于累加器A的第一个参数传给变量len
SSBX CPL ;用SP的直接寻址方式
LD @17,A ;第二个参数的偏移地址
STL A,*(freq) ;将置于堆栈中的第二个参数传给变量freq
.
.
.
FRAME #16 ;释放局部帧的空间
POPM AR1 ;恢复 AR1
POPM AR7 ;恢复 AR7
POPM AR6 ;恢复 AR6
RET ;返回
其中有关函数调用中堆栈的使用和分配,详见图1.
由以上的例子可以看出,采用C语言和汇编语言混合谐和的方法,可以大大提高编程的效率,使程序设计者可以把精力更多地集中到算法的实现上,而不必关心寄存器的使用和安排;在一些要求代码执行效率高的地方可以用汇编语言来实现,从而可以编写出高效的处理程序,将DSP技术更加充分地利用于各种系统设计中。