APP下载

基于ADSP-TS101的嵌入式系统混合编程及优化方法

2010-05-13苏延川

现代电子技术 2009年20期
关键词:汇编语言

苏延川

摘 要:为了使DSP程序同时具备可读性好和效率高的优点,提出一种采用C/C++和汇编语言混合编程的方法。该方法兼顾了两种编程语言的优点,已成为嵌入式 DSP系统的重要编程方法。现以ADSP-TS101的嵌入式系统为基础,介绍混合编程的方法和函数调用的规则,然后阐述程序优化的几种方法和总体策略,最后给出一个混合编程的实例。采用这种方法编写的程序能够在保持高级语言自身优点的情况下,使执行效率提高5~10倍。

关键词:ADSP-TS101;C/C++;汇编语言;混合编程;程序优化

中图分类号:TP312文献标识码:A

文章编号:1004-373X(2009)20-036-04

Mixed Programming and Optimized Method in Embedded System Based on ADSP-TS101

SU Yanchuan

(China Airborne Missile Academy,Luoyang,471009,China)

Abstract:In order to make DSP program has advantages of readability and high efficiency,mixed programming method which adopts C/C++ and assembler language is proposed.This method has advantages of two kinds of programming languages and it has already become the important programming method of embedded DSP system.Embedded system based on ADSP-TS101,the mixed programming method and the rule that function calls in are introduced.Some methods of optimized program and general policy are proposed,an example of mixed programming is given.The efficiency of program which uses the programming method can be improved by 5 to 10 times.

Keywords:ADSP-TS101;C/C++;assembler language;mixed programming;program optimization

0 引 言

随着数据处理要求(如雷达、无线通信、图像处理)的不断提高,以及DSP的快速发展,目前许多嵌入式系统都以DSP为核心来构建。嵌入式DSP系统的软件设计就是为了充分利用DSP的资源,完成复杂任务算法和高速数据的实时处理,保证系统的可靠性,并提高工作效率。

ADSP-TS101S是ADI公司专为大信号处理任务和通信应用而推出的一款高性能DSP芯片,在嵌入式信号处理中得到广泛应用。DSP软件设计可以采用C/C++语言、汇编语言或者二者混合编程。汇编语言编写的程序代码执行效率高,但是程序开发难度大,而且可读性、可移植性差;C/C++编写的程序可读性、移植性较好,但是执行效率太低[1-3],在某些实时性要求高的场合这种方法所产生代码的效率无法满足设计要求。为了充分利用两种语言的优势而采用二者混合编程的方法,这种方法既可充分利用芯片内的硬件资源,又可缩短其开发时间,维护升级也很方便。因此,在很多情况下采用混合编程方法能更好地达到设计要求,二者相互结合,可极大地提高DSP任务处理的能力,并可在有限的时间内完成设计任务。

1 混合编程方法及规则

1.1 混合编程方法

汇编语言和C/C++混合编程方法[2-4]通常可分为以下两种:

(1)独立编写C/C++程序和汇编程序。分开编译或汇编形成各自的目标模块,再用链接器将各个目标模块链接起来,这是一种灵活性较大的方法。通常的做法是用C/C++编写主程序框架,用汇编语言编写调用次数较多或者执行时间较长的子程序,这样既能发挥C/C++程序可读性好的优点,又能充分体现汇编语言执行效率高的长处,但它必须了解程序运行时的环境,维护各汇编模块的入口和出口代码,计算传递参数在堆栈中的位置,工作量稍大,不过能满足软件设计结构化的要求。

(2) 在C/C++程序中直接内联汇编语句。这种方法适用于短小或者简单的算法。混合编程的规则[5,6]是在C/C++环境下,由TigerSHARC 定义了一套严格的寄存器规则,它分为三类[2,3]:

第一类是保留寄存器。有j16~j25,k16~k25,xr24~xr31,yr24~yr31共40个,要使用这些寄存器,需要将其保存在实时堆栈中,在C可调用的汇编子程序中也必须保存和恢复这些寄存器。

第二类是堆栈专用寄存器。有k26,k27和j26,j27。这些寄存器专门用于堆栈保护,在使用高级语言编程时,不需要使用这四个寄存器。

第三类是高速暂存寄存器。它包括除了以上两类寄存器以外的所有寄存器。用法和汇编中的普通寄存器是一样的,使用前不需要保存寄存器内容。

在默认情况下,cjmp寄存器用作存放被调用函数的返回地址,但在嵌套调用中,这个值会被修改。为了保证安全返回,一般把返回地址存放在堆栈顶部(偏移地址为0的地方)。函数调用时,有时需要参数传递,通常,如果参数少于5个,则通过寄存器传递,如表1所示。

表1 参数字与寄存器的对应关系

参数字

使用的寄存器

参数为int或者指针参数为float或者双字

参数字1j4xr4

参数字2j5xr5

参数字3j6xr6

参数字4j7xr7

如果在C/C++ 调用函数中做了正确的函数返回声明,则被调用的汇编函数可使用寄存器j8,xr8 和xr9 返回有效值。j8用于返回整数或地址;xr9:8可提供双字结果返回。若返回值大于2个字长,则必须为它们分配存储空间,令j8为返回值,指向该空间的首地址即可。

在C/C++中声明全局变量及函数,汇编中加“_”前缀才能使用;汇编中的对象必须用“_”前缀命名,并用.global 声明全局变量,才可在C/C++中访问到。

1.2 函数调用规则[7]

函数调用的标准运行模式为:

(1) 调用者将参数压入堆栈,压入时按照反序进行,即最右边的参数位于堆栈的顶部;

(2) 调用函数;

(3) 调用结束时,调用者将参数弹出堆栈并返回。整个过程离不开堆栈操作。

ADSP-S101的堆栈是一个先入后出存储区,用堆栈指针(j/k27) 和帧指针(j/k26) 来管理堆栈。调用函数时,编译器在运行栈中建立一个帧以存储信息,当前函数帧称为局部帧。j/k26指向当前函数局部帧的开始,即栈底。j/k27指向栈顶,工作方式是向低地址变化。每调用一次函数,就建立一个新帧。C环境利用局部帧来实现如下功能:

保护函数的返回地址及相关寄存器 把函数返回地址,保存在j27+0的位置(栈顶),同时设置j26为j27-0x40(栈底),得到长度为64的栈区,并在栈区内保护相关寄存器。

分配局部变量 在局部变量赋初值的时候,系统在堆栈内给它分配一个空间。

传递函数的参数 前四个参数传递给相应寄存器(见表1),后续参数按顺序装载到堆栈j27 + 0xC的起始空间中。注意,如果传递的参数是结构类型,则其所有元素都将入栈。例如第五个参数是两元素的结构体,则元素一放于j27+0xC,元素二放于j27+0xD,在使用参数时只需从对应的位置上读取汇编子程序即可。

C环境在调用C函数时自动管理这些操作,当汇编与C接口时,必须采用与C一样的方式进行操作。特别需要注意的是,由于C编译器不提供检查堆栈溢出的任何手段,因此必须保证有足够的空间用于堆栈,否则若发生溢出现象,将破坏程序的运行环境,从而导致程序的瘫痪。

2 软件设计优化策略

2.1 代码优

代码优化[8,9]包括优化C/C++语言的执行速度和代码长度。现代DSP的C/C++语言编译器可提供一定程度的代码优化,但是编译器不能同时做两种类型的优化,只能涉及执行速度和代码大小的平衡。相对编译器来说,手工改变代码的大小比较困难。ADI公司的VisualDSP++4.5集成开发环境支持C/C++语言编译和优化,同时编译器可以进行几个不同级别的优化。

2.1.1 优化代码的执行速度

对于某些有时间限制或频繁执行的代码段,执行速度是非常重要的,可以通过手工的办法做很多优化,以提高这些代码段的效率。中断服务程序、高优先级的任务、有实时限制的计算、计算密集型或者频繁调用的函数都是优化速度时的候选对象。在实际的嵌入式系统中,由于处理器的特点不同,所以优化手段会有所不同。主要有算法优化、指令优化、数据流优化三种[1]。

算法优化 根据任务的具体情况,寻找最优算法,有效提高代码的效率,同时保证结果的正确性,如FFT和DFT的快速算法、查表法等。同时也可以根据处理器的结构特点,利用处理器的并行处理结构,实现SIMD和MIMD,甚至多DSP的并行处理,有效进行算法优化。必须说明的是,算法的优化和数据类型以及数据在内存空间的分布也有很大的关系。例如在图像处理时,ADSP-TS101可以把一副图像分为上下两部分进行同时处理,或者几幅图像同时进行多片处理。

常用的指令优化主要有用移位来实现除法运算;最好用的索引方式访问数组;使用inline函数;使用全局变量减少函数调用参数;复杂运算模块采用手工汇编,局部利用嵌入式汇编;减少条件判断转移;Switch语句中根据发生频率来进行case排序,大的switch语句转为嵌套switch语句;重点优化循环体;避免使用C++的昂贵特性。

数据流优化主要有以下几个方面:

(1) 选用合适的数据类型;

(2) 访问时保证从数据对齐地址开始;

(3) 将数据存储在不同的存储空间;

(4) 频繁访问的数据放在内部存储器内;

(5) 数据的输入/输出及DSP间的大量数据交换,采用DMA方式进行。

2.1.2 优化代码的长度

优化代码的长度与优化代码的执行速度往往是互相矛盾的。因此前面提到的优化代码的执行速度方法,如果反其道而行之,则会起到优化代码长度的效果。除了编译器自动优化代码长度外,还有一些手工的方法可减少代码的大小:

(1) 避免使用大的标准化程序库例程。很多库例程由于设法处理很多可能情况,所以形成庞大代码。避免使用大的标准库例程,用简洁代码实现子功能。

(2) 使用本地字长,减少附加指令。

(3) 使用Goto语句。Goto语句的使用与优化代码速度相矛盾,但是可以去除复杂控制结构以及共享重复代码。

除了以上技术,手工编写汇编能够最大限度地减少代码长度。

2.2 系统优化[9,10]

除了以上优化手段,系统的优化还需要保证系统的实时性和可靠性。

2.2.1 系统实时性保证

对于实时性系统,通过优化代码的执行效率来减少任务的完成时间,可以提高系统的实时性。但对于复杂的多DSP系统,任务的分配和调度需要嵌入式实时操作系统RTOS来完成,以保证系统的可靠性及实时性,而开发者只需关注应用程序算法的研究。RTOS的高效任务管理、快速灵活的任务间通信、高度的可裁减性、动态的内存管理等优点都有效提高了嵌入式系统的资源利用率,同时快速有效的中断及异常时间处理能力保证了系统的安全性。常见的RTOS有Linux,WinCE,μCOS/Ⅱ,VxWorks等。对TS101,可用的嵌入式操作系统有OSE,VDK等。

另外,在多处理器系统中,较少通信开销对提高系统的效率非常重要。

2.2.2 系统可靠性保证

系统可靠性保证是指通过软件设计上的一些措施来实现对系统故障的检测、隔离以及实时监控系统遇到的异常信息,并尽可能保证在异常状态或带故障状态下,系统能完成正常的任务,避免系统死机。提高系统的可靠性是软件设计优化的一个重要方面。

在实时环境下,由于外部异常导致系统中数据传输或者通信被打断,需要重新建立通信,保证数据传输的畅通。算法出现的异常,如除法溢出、数值超出范围等,需要建立相应的处理机制。例如,在总线共享结构的多DSP处理系统中,软件设计上加入实时的系统状态查询操作,多片工作时当一片出现问题,主控处理器会根据具体的出错状态来自动将其隔离,并通知其他的处理器,重新进行任务分配,利用它们完成剩余的工作。

异常维护操作必然会使软件运行效率下降,这就需要根据系统具体的运行环境进行取舍。为此它通常用于对可靠性维护的操作,以模块子函数的形式存放在软件相应的故障对策库中,以便根据故障的具体情况来选择应用中应采取的相应措施。

2.3 程序优化的总体策略

根据不同的处理器及实际应用,嵌入式DSP系统的优化情况有所不同。同时应当指出,编译器的优化是速度优化与代码长度优化的折中,多数情况下代码速度优化与长度优化是互相矛盾的,应当根据实际情况有所取舍。在优化系统的过程中,要兼顾以下几点:

(1) 不要一味地追求程序的效率,应当在满足正确性、可靠性、健壮性、可读性等质量因素的前提下,设法提高程序的效率;

(2) 以提高程序的全局效率为主,提高局部效率为辅;

(3) 在优化程序的效率时,应当先找出限制效率的“瓶颈”,不要在无关紧要之处优化;

(4) 先优化数据结构和算法,再优化执行代码;

(5) 不要追求紧凑的代码,因为紧凑的代码并不能产生高效的机器码。

同时,充分了解系统硬件结构,特别是处理器内部结构,与硬件工程师有效协调配合,设计出高效率代码,同时保证系统的安全性及实时性。

3 混合编程在程序中的应用

下面以一个嵌入式DSP程序为例,其系统由两片DSP构成,主DSP0 负责通过IRQ0接收控制台从RS 232串口传来的控制字、译码,并通过控flag3产生下降沿触发IRQ3 中断来启动另一片DSP。这里用C搭建框架,汇编控制底层硬件,效率高,可读性强。由于篇幅限制,这里略去串口初始化、串口数据接收函数以及其他芯片的处理程序。以下是系统管理中的混合编程:

Main.c 文件:

# include < signal.h >

# include < sysreg.h >

# include " def TS101.h"

int code[ 2 ] ;

volatile int inruptflag = 0,IRQ3flag = 0 ;

extern void INITIAL_DSP ( ) ;

extern int decode (int ( 3 p) [ ]) ;

extern void IRQ0_int () ;

# pragma interrupt //中断服务程序标识(第2 种方法)

void IRQ3_int (void) { IRQ3flag = 1 ;}

main ()

{

int temp,r ;

INITIAL_DSP() ;// 一些初始化设置

temp = __builtin_sysreg_read (__IMASKH) ;

// 读取IMASKH

temp | = (1 < < IN T_IRQ0_P) | (1 < < INT_IRQ3_P) ;

__builtin_sysreg_write (__IMASKH,temp ) ;

// 打开IRQ0 、IRQ3 中断屏蔽位

interrupt (SIGIRQ0,IRQ0_int) ;

// 赋IRQ0 中断入口地址

while(inruptflag = = 0) { } //等待接收IRQ0传命令字

Rev8byte ( &code;) ;

// 接收串口命令字并存放于code (略)

r = decode ( &code;) ;/ / 调用asm 译码程序

if ( 3 (int 3 ) (r + 1) = = 2)

{// 判断命令码第2 个字是否为2

__builtin_sysreg_write (__IVIRQ0,(int) IRQ3_int) ;

// 第2 种方法设置中断矢量表

__builtin_sysreg_write ( __SQCTLCL,0xf0ffffff) ;

// 拉低FLAG3,产生IRQ3 中断

while( IRQ3flag = = 0) { } // 等中断启动各片DSP

…}

… }

INITIAL.asm 文件:

# include " def TS101.h"

# define mPUSHQ(arg) Q[ k27 + = - 4 ] = arg;

// 宏定义保存寄存器

# define mPOPQ(arg) k27 = k27 + 4;arg = Q[ k27 + = 0 ];// 宏定义释放寄存器

# define mENTER j26 = j27 - 0x40 ;k26 = k27 - 0x40;

// 宏定义实现调用进入

[ j27 + = 0xFFFFFFF4 ] = cJMP ;k27 = k27 - 0x04;

# define mRETURN cjmp = [j26 + 0x40 ];

// 宏定义实现调用返回

cjmp(ABS) (NP) ;j27:24 =Q[j26 + 0x44] ;k27:24 =Q[k26 + 0x44];

.global _INITIAL_DSP,_IRQ0_int,_decode,_Rev8byte ;// 声明为全局变量

.extern _inruptflag,_code ;// 外部变量

.section flags ;

.var dsp_code[8 ] ;

.section program ;

_INITIAL_DSP:mEN TER

xr5 = 0x08800000;sqctlst = xr5;

// 令flag3 为输出高电平

xr5 = 0xfff6ffff;sqctlcl = xr5;

// 设置IRQ3、IRQ0 为边沿触发

xr0 = 0xD0010000;IMASKH = xr0;//打开异常中断

yr17 = j27;yr16 = j26;yr15 = k27;yr14 = k26 ;

// 嵌套调用,保护堆栈指针寄存器

. align_code 4 ;call InitialSPA(np) (abs);

// 调用UART 芯片初始化程序(略)

j27 = yr17;j26 = yr16;k27 = yr15;k26 = yr14;

// 释放堆栈指针寄存器

_INITIAL_DSP.END:mRETURN

_decode:mEN TER

xr3 = [ j4 + 0 ];j2 = dsp_code;[ j2 + = 1 ] = xr3;

// j4 传递参数,译码结果存于dsp_code

j8 = dsp_code;// j8作为返回值返dsp_code的首地址

_decode.end:mRETURN

_IRQ0_int:mPUSHQ(xr3:0) mPUSHQ(j3:0)

// 保存所有寄存器

j1 = _inruptflag;xr0 = 1;[ j1 + 0 ] = xr0;

mPOPQ(j3:0) mPOPQ(xr3:0) // 释放所有寄存器

_IRQ0_int .end:rti (ABS) (NP) ;nop ;nop ;nop;

4 结 语

汇编语言与高级语言的混合编程方法能够最大限度地发挥两种编程方法的优势,让开发者写出可移植性好,执行效率高的DSP应用程序。设计良好的混合编程软件既能有效满足嵌入式系统对功能与性能的需求,也可以为程序的扩展和移植预留足够的空间。该方法对ADI公司其他系列的DSP编程也有很大的参考价值。

参考文献

[1]Michael Barr.Programming Embedded Systems in C and C++ [M].First Edition.O′Reilly,1999.

[2]王霞,冯立营,赵晓群.一种TI5000系列DSP C/C++语言和汇编语言混合编程的方法[J].现代电子技术,2004,27(15):71-74.

[3]齐崇英.TMS320C3x DSP的C汇编语言混合编程及中断的C语言实现[J].国外电子元器件,2002(4):23-25.

[4]Analog Devices INC.ADSP-TS101 TigerSHARC Processor Software Reference[Z].2003.

[5]张永.C语言与汇编语言混合编程的研究与实现 [J].计算机与数字工程,2006(5):120-122.

[6]刘书明.TigerSHARC DSP 应用系统设计[M].北京:电子工业出版社,2004.

[7]陈丽安,魏宏伟.C54x DSP混合编程及中断的C语言实现[J].微处理机,2005(6):52-56.

[8]任志考.用C语言进行DSP软件设计的优化方法[J].青岛科技大学学报,2003(9):102-104.

[9]刘朝晖,郑玉墙.用C语言进行DSP软件设计的优化考虑[J].空军雷达学院学报,2001(2):49-52.

[10]Andrew Sloss.ARM System Developer's Guide:Designing and Optimizing System Software[M].Elsevier,2005.

猜你喜欢

汇编语言
高等学校计算机专业课程教学改革实践——以汇编语言与接口技术课程为例
汇编语言与C语言的混合程序设计技术研究
提高《汇编语言程序设计》教学效率的思考与实践
计算机中几个语言的理解
汇编语言在C语言学习中的应用
汇编语言在大学生能力培养中的研究与探索
试论汇编语言与C语言的混合程序设计技术
教改,不能只做减法了事