|
摘 要:首先介绍并比较了 TI 5000 系列DSP的三种主要软件开发方法的优缺点,提出了C/C++与汇编语言混合编程方法的优越性所在。之后在比较了几种典型混合编程方法的基础上,提出了一种结构化的混合编程方法并详细阐述了其函数调用规则和寄存器规则。最后给出了一个经过验证的混合编程程序,该程序对DSP软件开发有较大参考价值。 关键词:DSP;混合编程;函数调用规则;寄存器规则
A Method of Mixed Programming of C/C++ and Asse mbly Language on TI 5000 Series of DSP
WANG Xia1,FENG Liying2,ZHAO Xiaoqun1
(1.The Information Institute,Hebei University of Technology ,Tianjin, 300130, China; 2.Tianjin University of Technology and Education,Tia njin,300222,China)
Abstract:Have introduced and compared the pluses and minuses of three kinds of software development approaches on TI 5000 series of DSP at first, then we proposed that the method of interfacing C/C++ with assemble r language is better than the others programming methods Later on the basis of comparedding several kinds of method of interfacing C/C++ with Assembly language, we present a structured mixed programming method and explain in detai l transfer rules in function calling and the rules of registers At last ,we pres ented a program tested by reality which has a better directive impact on those d evelopers who can acquire the essence of the technique soon Keywords:DSP;mixed programming;rules in function calling; rules of registers
1引言 TI 公司的5000系列低功耗16 b定点DSP,因其良好的性价比,在国内获得了很大的普及。如何对5000系列DSP进行软件开发也一直是业界关注的热点。5000系列DSP的软件设计通常有3种方法。 1.1用C/C++语言开发 TI公司提供了用于C/C++语言开发的CCS平台。该平台包括优化ANSI C/C++ 编译器,从而可以在源程序级进行开发调试。这种方法大大提高了软件的开发速度和可读性,方便了软件的修改和移植。但是,C/C++代码的效率还是无法与手工编写的汇编代码效率相比,如FFT程序。因为即使是最佳的C/C++编译器,也无法在所有的情况下都能合理的利用DSP芯片提供的各种资源。此外,用C/C++语言实现DSP芯片某些硬件控制也不如汇编方便,有些甚至无法用C/C++语言实现。 1.2全汇编语言开发 TI公司提供了用于汇编语言开发的针对5000系列DSP的汇编语言。用户可以用他进行软件开发。这种方式可以更为合理的利用芯片提供的硬件资源,其代码效率高,程序执行速度快。但是用汇编语言编写程序是比较复杂的,一般来说,不同公司的芯片汇编语言是不同的,即使是同一公司的芯片,由于芯片的类型不同(如定点和浮点)、芯片的升级换代,其汇编语言也不同。因此,用汇编语言开发基于某种芯片的产品周期较长,并且软件的修改和升级较困难。而且汇编语言的可读性和可移植性较差。 1.3C/C++语言和汇编语言混合编程开发 为了充分利用DSP芯片的硬件资源,更好发挥C/C++语言和汇编语言进行软件开发的各自优点,可以将两者有机的结合起来,兼顾两者优点,避免其弊端。因此,在很多情况下,采用混合编程方法能更好地达到设计要求,完成设计任务。
2 C/C++语言和汇编语言混合编程方法讨论 C/C++语言和汇编语言混合编程的具体方法有以下几种: (1)独立编写C/C++程序和汇编程序,分开编译或汇编形成各自的目标模块,再用链接器将C/C++模块和汇编模块链接起来,这是一种灵活性较大的方法。但用户必须自己维护各汇编模块的入口和出口代码,自己计算传递参数在堆栈中的偏移量,工作量稍大,但能做到对程序的绝对控制,也能满足软件设计结构化的要求。这是本文主要讲述的方法。 (2)在C/C++程序中使用汇编程序中定义的变量和常量。 (3)在C/C++程序中直接内嵌汇编语句。这种方法可以在C/C++程序中实现C/C++语言无法实现的硬件控制功能,如修改中断控制寄存器、中断标志寄存器等。 (4)在C/C++源程序中使用内部函数直接调用汇编语言语句。 后3种方法由于在C/C++语言中直接嵌入了汇编语言的成分,容易造成程序混乱,C/C++环境被破坏,甚至导致程序崩溃,而编程者又很难对不良结果进行预期和有效控制。而如果采用第一种方法,只要遵循有关C/C++语言函数调用规则和寄存器规则,就能预见到程序运行的结果,保证程序正确。下面分别讲述函数调用规则和寄存器规则,最后给出编程实例。
3函数调用规则 C/C++编译器对函数调用强加了一组严格的原则。除了特殊的运行时间支持库函数外,任何调用函数和被C/C++函数调用的函数都必须遵守这些原则。不遵守这些原则可能破坏C/C++环境并导致程序失败。 图1说明了典型的函数调用。在这个例子中,参数被传递到堆栈中调用者的参数块,函数再使用这些参数调用被调用函数。注意,第一个参数是在A累加器中传递的。这个例子还说明了汇编器对被调用函数的局部帧的分配。局部帧包括局部变量块和局部参数块两部分,其中局部参数块是局部帧中用来传递参数到其他函数的部分。如果被调用函数没有局部变量并且不再调用其他函数或需要调用的函数没有参数,则不分配局部帧。对于混合编程而言,由于被调用函数是手工编写的汇编程序,则局部帧由编程者自己完成分配,也不需要在堆栈中进行,而编译器分配局部帧。
(1)函数如何调用 函数(调用者)在调用被调用函数时执行以下任务。 ①调用者将第一个(最左边)的参数值放进累加器A。调用者将剩下的参数按相反的顺序传进参数块,剩下的最左边的参数在最低的地址。 ②若函数返回一个结构,则调用者为该结构分配空间,然后用累加器A传递返回空间的地址给调用的函数。 ③调用者调用函数。 (2)被调用函数如何响应。 被调用函数执行以下任务: 注意:如果被调用函数是C/C++函数,则下面步骤都是由汇编器自动完成。如果是混合编程,则如下步骤都是由编程者在被调用的汇编函数中完成的。 ①若被调用函数修改AR1,AR2或AR7,则将他们压入堆栈。 ②被调用函数通过从SP减去一个常数,为局部变量块和局部参数块分配存储器。该常数按以下公式计算,即: 局部变量块的大小+局部参数块的大小+padding padding值是为了保证SP对准偶数边界而可能要求补充的一个字。之所以SP要对准偶数边界,是因为5000系列DSP指令可一次读写存储器的32 b,例如DLD,DADD等。这样,编译器必须保证所有32 b的目标都驻留在偶数边界。 对于混合编程而言可以在汇编函数中,按本步骤的方法在堆栈中分配局部帧,但本方法相对比较麻烦,尤其该汇编函数还要调用其他函数时,所以,一般而言编程者通常用其他方法分配局部帧,比如用bss伪指令定义局部变量供函数使用。 ③被调用函数为调用函数执行代码。 ④若函数返回一个值,则被调用函数将该值放在累加器A中;若函数返回一个结构,则被调用函数将该结构复制到累加器A指到的存储器块;若调用者不返回函数值,则A被置0。 ⑤被调用函数给SP上加上第二步计算的常数,释放为局部变量和局部参数分配的存储空间。对混合编程而言,如果编程者没有在堆栈中分配局部帧,则本步骤省略。 ⑥被调用函数恢复所有保存的寄存器。 ⑦被调用函数执行返回。
4寄存器规则 (1)必须保存任何被函数修正的专用寄存器。专用寄存器包括: ①AR1,AR6,AR7 ②堆栈指针(SP) 若对SP正常使用,不需要明显的保存。换句话说,只要任何压入堆栈的东西在函数返回之 前被弹回(因而保存了SP),汇编函数就可以自由的使用堆栈。任何非专用的寄存器都可以自由地使用而无需将他们保存。 (2)中断函数必须保存他使用的所有寄存器。 (3)ARP在函数进入和返回时,必须为0,即当前辅助寄存器为AR0。函数执行时可以为其 他值。 (4)在默认的情况下,编译器总是认为OVM为0。因此,若在汇编程序中将OVM置为1,则返回C/C++环境时,必须将其恢复为0。 (5)在默认的情况下,编译器总是认为CPL为1。因此,若在汇编程序中将CPL清0,则在返回C/C++环境时,必须将其恢复为1。 (6)长整数和浮点数存储在存储器中的方法是最高有效字在低位地址。 (7)函数必须按前面有关被调用函数响应中所述的方法返回值。 (8)除了全局变量的初始化外,汇编语言模块不能以任何目的使用cinit段。在boot asm中的C/C++启动程序假定cinit段完全由初始化表组成。将其他的信息放入cin it中将使初始化表产生混乱,并将产生不可预期的结果。 (9)在汇编语言模块中,对可以从C/C++中访问的变量和函数名需加上前缀“_”。对于仅用于汇编语言模块中的标识符,应不得用下划线开始。 (10)任何在汇编语言模块中声明的将要从C/C++访问或调用的对象或函数,都必须在汇编语言中用global伪指令声明为全局变量。
5编程实例 以32 b乘法运算为例。虽然用C/C++语言表达32 b乘法运算较为方便和明了,但由于C/C++语言无法很好利用DSP汇编语言为实现各种乘法运算而提供的指令,而使得C/C++程序效率低下。所以这里用汇编语言完成32 b乘法运算,再用C/C++程序调用他。 5.1算法简介 由于16 b定点DSP中没有32 b乘法指令,所以一定要用几种16 b乘法指令结合一定算法来进行32 b乘法运算。一个32 b数在存储器中是分开存储的。高16位存放在低地址,他在进行乘法运算是可以看作一个16 b有符号数;低16位存放在相邻的低地址,他进行乘法运算时可以看作一个16 b无符号数。于是算式如下:

其中:S代表符号数;U代表无符号数。 由上算式可见,在32 b乘法运算中,实际上包含了3种乘法运算:U*U,S*U和S*S 。一般的乘法运算指令都是两个带符号数相乘,即S*S。所以在编程时,还要用到以下两条乘法指令:
5.2C语言主程序

在主程序中进行MPY32函数调用时,函数传递情形如图2所示。

从图2可以看出,函数MPY32的第一参数存放在A累加器中,第二个参数在堆栈中,高16位在堆栈中的低地址,低16位在堆栈中的高地址。由于MPY32是汇编语言函数,所以编译器不为其分配局部帧,局部帧的分配在汇编程序中进行。 5.3汇编程序 可以看出,在汇编程序中至少要为局部帧分配8个单元,其中4个单元用来存放参数值,4个单元用来存放运算结果,如图3所示。 汇编函数:   
6结语 本文介绍的混合编程方法不但适用于TI 5000系列DSP,同样也适用于TI其他系列的DSP,如2000系列、6000系列,甚至对其他芯片,如51系列单片机,实现混合编程也有很大参考价值。值得注意的是,为了使混合编程不破坏C语言的结构性,在汇编语言中不要设置除函数名之外的任何全局变量。
参考文献
[1] Brian Kernighan W,Dennis Ritchie M,The C programming language(second edition) .PrenticeHall, Englewood Cliffs, NewJersey, 1988. [2] The TMS320C54x optimizing C compiler user′s guide[Z].Texas Instruments Co,1999. [3] TMS320C54x DSP Reference Set, (Volume 3): Algebraic Instruction[Z].Texas Inst ruments Co,1999.
|