分类:
2012-10-28 13:53:46
原文地址:DSP集成开发环境中的混合编程及FFT算法的实现 作者:tiny_xd
2 C语言和汇编语言的混合编程
TMS320 C5000系列的软件设计通常有三种方法:
(1) 用C语言开发;
(2) 用汇编语言开发;
(3) C和汇编的混合开发。
其中用C语言开发具有兼容性和可移植的优点,有利于缩短开发周期和减少开发难度,但是在运算量较大的情况下,C代码的效率还是无法和手工编写的汇编代码的效率相比,比如FFT运算,用汇编语言开发的效率高,程序执行速度快,而且可以合理利用芯片的硬件资源,但是开发难度较大,开发周期长,而且可读性和可移植性差。C和汇编的混合编程则可以充分利用前两者的优点,以达到最佳利用DSP资源的目的。但是,采用C和汇编语言混合编程必须遵循相关函数调用规则和寄存器调用规则,否则会给程序的开发带来意想不到的问题。
2.1 C语言和汇编语言混合编程的四种方法
(1) 独立编写汇编程序和C程序,分开编译或汇编成各自的目标代码模块,再用链接器将二者链接起来。这种方法比较灵活,但是设计者必须自己维护各汇编模块的入口和出口代码,自己计算传递的参数在堆栈中的偏移量,工作量较大,但是能做到对程序的绝对控制。
(2) 在C程序中使用汇编程序中定义的变量和常数。
(3) 在C程序中内嵌汇编语句。这种方法可以实现C语言无法实现的一些硬件控制功能,如修改中断控制寄存器。
(4) 将C语言编译生成相应的汇编代码,手工修改和优化C编译器生成的汇编代码。采用这种方法可以控制C编译器,从而产生具有交叉列表的汇编程序,而设计者可以对其中的汇编语句进行修改,然后对汇编程序进行编译,产生目标文件。
后3种方法由于在C中直接嵌入了汇编语言,易造成程序混乱,破坏C环境,甚至导致程序崩溃,而开发者又很难对不良结果进行预期和有效控制。而如果采用第一种方法,只要遵循有关C语言函数调用规则和寄存器规则,就能预见到程序运行的结果,保证程序正确。
2.2 编程注意事项
C编译器对函数调用制定了一组严格的规则。除了特殊的运行时间支持库函数外,任何调用函数和被C函数调用的函数都必须遵守这些规则。结合作者在编程中的实际情况和切身体会,提出在编程时要注意以下几点:
(1) 必须保护任何被函数修正的专用寄存器。这些专用寄存器包括:AR1,AR6,AR72和堆栈指针(SP)。其中,如果对SP正常使用,则不必明显的保存。换句话说,只要汇编函数在调用返回时弹出压入的对象,实际上就已经保护了SP。
(2) 中断函数必须保存其使用的所有寄存器。
(3) 从汇编函数中调用C函数时,第一个参数(最左边的)必须放入累加器A中,剩下的参数按照自右向左的顺序压入堆栈。
(4) 如果函数有返回值,则返回值存放在累加器A中。
(5) 调用C函数时,注意C函数只保护了几个特定的寄存器,对于其他寄存器C函数是可以自由使用的。
(6) 长整数和浮点数存储在存储器中的方法是最高有效字在低位地址。
(7) 汇编语言模块不能改变由C模块产生的.cinit段,如果改变其中的内容将会引起不可预测的后果。
(8) 在汇编语言模块中,对可以从C中访问的变量和函数名需加上前缀“_”。对于仅用于汇编语言模块中的标识符,不用加下划线。而且如果仅在汇编中使用,只要不加下划线,即使与C程序中定义的对象名相同,也不会造成冲突。
(9) 任何在汇编语言模块中声明的将要从C访问或调用的对象或函数,都必须在汇编语言中用.global伪指令声明为全局变量。同样,任何在C程序中定义而将在汇编中访问或调用的对象或函数,在汇编中也必须用.global声明。
(10) 在默认的情况下,编译器总是认为CPL为1。因此,若在汇编程序中将CPL清0,则在返回C环境时,必须将其恢复为1;在默认的情况下,编译器总是认为 OVM为0。因此,若在汇编程序中将OVM置为1,则返回C环境时,必须将其恢复为0;ARP在函数进入和返回时,必须为0,即当前辅助寄存器为AR0。函数执行时可以为其他值。
3 编程实例
3.1 FFT算法简介
FFT是一种高效实现离散傅立叶变换的算法,在数字信号处理系统中,FFT作为一个非常重要的工具,甚至成为DSP运算能力的一个考核因素。如何将FFT算法很好的应用到DSP系统中对于DSP系统的设计具有重要的意义。
一个优化的实数FFT算法是一个组合以后的算法。该算法主要分为以下几步,首先将输入的2N点实序列进行位倒序组合成一个N点的复序列,之后对复序列进行N 点的FFT运算,最后再由N点的复数输出拆散成2N点的复数序列,这2N点的复数序列与原始的2N点的实数输入序列的DFT输出一致。(详细的算法介绍可参考相关信号处理书籍)。
3.2 C主程序
#include "stdlib.h"
extern void fft(); // FFT运算函数
int DisData[256]; // 输出结果
int SimData[256]={
0,6270,11585,15137, 16384, 15137, 11585,6270,
0, -6270, -11585,-15137,-16384,-15137,-11585,-6270,
……
0,6270,11585,15137,16384,15137,11585,6270,
0,-6270,-11585,-15137,-16384,-15137,-11585,-6270
};
// 输入数据
int main()
{
rfft();
// 调用FFT函数
while(1) ;
}
本程序中FFT运算所用到的数据是通过matlab仿真产生的,然后通过全局数组进行传值,这种方式的优点是数据的通用性强,方便对数据进行其他相关处理; 也可通过其他C程序产生然后保存到一个文本文件中,再由汇编程序将该数据文件拷到数据存储器中参与FFT运算。这种方式的优点是程序的可读性强,缺点是当输入数据修改后,必须进行重新编译、汇编和链接。
3.3 汇编程序
.mmregs
.global sav_sin,sav_idx,sav_grp,_rFFT,_DisData
.data
DATA .space 1024 ;数据缓冲区
INPUT .set 0x9000 ;输入数据起始地址
N .set 128 ;复数点数
LOGN .set 7 ;碟形运算级数
sav_grp .usect "tempv",3 ;定义组变量值
sav_sin .set sav_grp+1 ;定义旋转因子表
sav_idx .set sav_grp+2
.copy "twiddle1.inc" ;正弦表系数
.copy "twiddle2.inc" ;余弦表系数
.text
_rfft:
SSBX TC
SSBX C
RSBX OVA
RSBX OVB
LD #0,DP
RSBX CPL
SSBX XF
SSBX SXM
LD #00,ASM
SSBX FRCT
……
本程序主要是对输入的256点实数作FFT运算,通过修改N和LOGN可以完成16-1024点的FFT运算。在编写汇编程序的时候要注意以下几个方面:
(1) 由于采用循环寻址来对旋转因子表寻址,因此每张表的开始地址必须是10个LSB位为0的地址。如:正弦表系数可以放在0400h开始的地址,余弦表系数可以放在0800h开始的地址;
(2) 在汇编程序的入口处要注意设置好汇编程序所需要的运行环境(见前述程序),如果此处按照编译器默认的环境将得不到正确的结果。详细的汇编程序可参考TI公司提供的例程。
3.4 运算结果
输入正弦信号的频率为f=16Hz,幅值为1(单位),采样长度为2N=256点,采样频率为fs=256Hz。CCS提供了很多方法将程序产生的数据画图显示,包括时域/频域波形显示等。可通过查看相关图像来检验FFT运算的正确性。由链接命令文件可以知道输入信号存储在程序存储区0900h开始的256 个单元中,经程序计算得到的信号幅值谱存放在数据存储区0200h开始的256个单元中。在CCS中选择View——>Graph——> Time/Frequency命令,相关设置可参考图1:
图1 FFT运算结果查看选项设置参考
输入信号的时域波形见图2。
点击看原图
图2 输入信号的时域波形
点击看原图
图3 信号幅值谱图
将输入信号通过Matlab仿真进行验证。输入信号的时域波形见图4。
点击看原图
图4 输入信号的时域波形
经FFT运算后得到的信号幅值谱图见图5。
点击看原图
图5 信号幅值谱图
通过比较CCS中的输出图形和Matlab中的仿真输出图形, 可以看到二者是一致的,说明本程序的结果是正确的。
4 结束语
本文通过实例,说明了TMS320 C5000系列DSP芯片的混合编程方法,利用混合编程达到了提高程序的可读性与编程效率的目的,是开发DSP软件的常用方法。本文介绍的混合编程方法不但适用于TI C5000系列DSP芯片,同样也适用于TI其他系列的DSP芯片,如C2000系列、C6000系列,甚至对其他芯片,如51系列单片机等,实现混合编程也有很大参考价值。