c语言汇编语言结合的代码,如何将汇编语言与C语言整合至DSP

本文将讨论如何将汇编语言程序代码整合到C语言中,以最大化性能以及程序设计人员生产力,内容涵盖了编译器惯例(convention)、内嵌(inlining)、内嵌函数(intrinsic)、缓存器连结(register binding)和除错策略。

随着DSP处理器性能的提升以及编译器最佳化技术的进步,曾经红极一时、仅用汇编语言编写DSP应用程序的作法已逐渐被淘汰。今天,几乎每个DSP应用程序都使用C语言程序代码和汇编程序码混合的方式。对于一些性能需求极高的关键功能,DSP工程师会继续使用高度最佳化的汇编程序码;而一些次要的功能现在也使用C语言编写,使程序代码更容易维护和移植。对于C语言和汇编程序码的结合,每位DSP工程师都需要掌握特殊的工具和方法。

众所皆知,汇编语言编码具有更高的性能优势,而用C语言编码则较容易且速度也更快。为了解其中原因,以下我们进一步比较汇编语言编码与C语言编码的优缺点:

汇编语言编码的优点:汇编语言编码可以充分利用处理器的独特指令以及各种专门的硬件资源。而C语言程序代码是通用型程序代码,必须支持各种硬件平台,因此很难支持特殊平台程序代码。

汇编程序设计人员通常对应用程序非常熟悉,可以作出编译器无法作出的假设。

汇编程序设计人员可以发挥人类的创造性;而再先进的编译器也只是一个自动化的程序。

汇编语言编码的缺点:汇编程序设计人员必须解决耗时的机器级问题,如缓存器分配和指令排程。若使用C语言程序代码,这些问题可以由编译器解决。

使用汇编语言编码的程序设计人员必须了解DSP架构及其指令集的专业知识;而C语言编码只需要掌握相当普及的C语言知识。

若使用汇编语言,将应用程序从一个平台移植到另一个平台非常困难也相当耗时;而C语言应用程序的移植相对而言非常容易。

图1显示了如何利用专用硬件机制来获得高度最佳化的汇编程序码。左边的C语言编码利用模块算法设计出一个循环缓冲区P1;右边高度最佳化的汇编程序码中,等效的缓冲区是利用CEVA-TeakLite-III DSP核心的模块运算机制(Modulo Mechanism)设计产生的。只要缓冲区指标(本例中的r0)有更新,模块运算机制便会自动执行模块运算。这种运算与指针更新在同一个周期发生,因此汇编程序码比C语言程序代码更加高效,可为模块运算产生独立的指令。

c0801445cab8f7037bdb2cdf3e0bf365.png

点击看原图

图1:右边的CEVA-TeakLite-III汇编程序码可以建置成左边的C语言程序代码。

为DSP应用选择C/汇编程序码

混合使用的问题就在于该如何划分C语言程序代码和汇编程序码的界限,而答案取决于剖析器提供的性能分析结果。然而在使用剖析器之前,DSP工程师需要为应用程序定义清晰的对象,一些典型的对象包括循环数、程序代码大小和数据大小。一旦这些对象确定后,所有应用程序都应该先以C语言编写和制作,随后使用剖析器来分析性能。

在一些极端情况下,如控制应用,用C语言层级的编码就足够了;但大多数情况下,原始C语言层级应用程序版本不会遵从一个或多个对象,这也意味着需要使用一些汇编程序码来完成。在求助于汇编语言编码之前,C语言编码可提供许多方法来提高性能,但这些方法不属于本文讨论的范畴。假设所有C语言级的方法全用完了,并且准备使用汇编语言编码,这时强烈建议将原始C语言程序代码保存起来。这样不仅方便除错,而且一旦条件许可(比如转移到更强大的平台)还可以回复原始的C语言。

程序代码中的汇编语言部份应尽可能维持在最少,这样便能分析从剖析器得到的性能结果,并定义应用程序中的关键函数。关键函数会占用大部份执行时间,必须用汇编语言重写才能满足性能对象。当两到三个最关键的函数重写后,需要重新进行性能测量,若应用程序仍然不能满足对象需求,那么必须使用汇编语言定义并重写额外的关键函数,这个过程需要不断地重复直到满足性能对象需求为止。

汇编语言设计师的编译器考虑

在编写会与C语言程序代码结合的汇编程序码时,汇编程序设计人员必须了解编译器的惯例和假设。其中有个重要的编译器惯例是函数呼叫惯例,也称为函数参数传递惯例。这个惯例描述了编译器如何在一个函数呼叫另一个函数时传递参数。为了使汇编语言函数能被C语言函数成功呼叫;反之亦然;汇编语言函数必须截取参数,然后将参数发送到由函数呼叫惯例定义的硬件资源上,通常为缓存器或堆栈内存。

汇编程序设计人员还必须了解编译器的缓存器使用惯例。这些惯例将硬件缓存器分成被呼叫者保存(callee-saved;或呼叫者使用,caller-used);以及被呼叫者使用(callee-used;或呼叫者保存,caller-saved)缓存器。编译器假设被呼叫者保存缓存器在函数呼叫过程中保持不变的值,若汇编程序设计人员希望使用这种缓存器,他们必须先将缓存器的值备份,然后在返回到C语言程序代码之前恢复这些缓存器的内容;相反的,被呼叫者使用缓存器被认为在函式呼叫过程中不会保持一定的值。这意味着汇编程序设计人员使用这些缓存器之前无需进行备份,不过他们必须记住,当汇编语言函数呼叫C语言函数时,被呼叫者可以对这些缓存器进行覆写。

图2为一个从CEVA-X1641 DSP核心FFT实作中截取的汇编程序码案例。其中以黄色标示的add指令遵循CEVA-X1641编译器的呼叫惯例,在r0地址缓存器中传递指针参数。标为蓝色的pushd指令用于备份,随后函数会使用的被呼叫者保存缓存器。

1165af30db94c279f029dc295f692915.png

图2:从CEVA-X1641手写FFT算法汇编语言实作摘录的一段程序代码。

几个c程序,希望有用 include <math.h> /* In the VMS compiler, M_PI is not defined in math.h */ #ifdef vms #define M_PI 3.14159265358979323846 #endif #ifndef KR #define M_PI 3.14159265358979323846 #endif #define FALSE 0 #define TRUE 1 #define BIG 1e10 #define SMALL 1e-10 #define ORDER5 1e-5 #define ORDER4 1e-4 #define ABS(x) ((x) >= 0 ? (x) : -(x)) #define MIN(a,b) ((a) <= (b) ? (a) : (b)) #define MAX(a,b) ((a) >= (b) ? (a) : (b)) /* Table of constant values */ static long neg_i1 = -1; static long pos_i1 = 1; static long pos_i2 = 2; static long pos_i4 = 4; static long pos_i5 = 5; static float neg_f1 = -1.0; static float pos_f2 = 2.0; static double pos_d10 = 10.0; typedef struct { float r, i; } complex; typedef struct { double r, i; } doublecomplex; /* .................End of include file.................*/ /* SPBFCT 11/14/85 */ /* GENERATES (I1)!/(I1-I2)!=I1*(I1-1)*...*(I1-I2+1). */ /* NOTE: 0!=1 AND SPBFCT(I,I)=SPBFCT(I,I-1)=I!. */ #ifndef KR double spbfct(long *i1, long *i2) #else double spbfct(i1, i2) long *i1, *i2; #endif { /* Local variables */ long i; double ret_val; ret_val = 0.0; if (*i1 < 0 || *i2 < 0 || *i2 > *i1) { return(ret_val); } ret_val = 1.0; for (i = *i1 ; i >= (*i1 - *i2 + 1) ; --i) { ret_val *= (double) i; } return(ret_val); } /* spbfct */ /* SPBILN 11/13/85 */ /* CONVERTS ANALOG H(S) TO DIGITAL H(Z) VIA BILINEAR TRANSFORM */ /* ANALOG TRANSFER FUNCTION DIGITAL TRANSFER FUNCTION */ /* D(L)*S**L+.....+D(0) B(0)+......+B(L)*Z**-L */ /* H(S)=--------------------- H(Z)=---------------------- */ /* C(L)*S**L+.....+C(0) 1+.......+A(L)*Z**-L */ /* H(S) IS ASSUMED TO BE PRE-SCALED AND PRE-WARPED */ /* LN SPECIFIES THE LENGTH OF THE COEFFICIENT ARRAYS */ /* FILTER ORDER L IS COMPUTED INTERNALLY */ /* WORK IS AN INTERNAL ARRAY (2D) SIZED TO MATCH COEF ARRAYS */ /* IERROR=0 NO ERRORS DETECTED IN TRANSFORMATION */ /* 1 ALL ZERO TRANSFER FUNCTION */ /* 2 INVALID TRANSFER FUNCTION; Y(K) COEF=0 */ #ifndef KR void spbiln(float *d, float *c, long *ln, float *b, float *a, float *work, long *error) #else void spbiln(d, c, ln, b, a, work, error) long *ln, *error; float *d, *c, *b, *a, *work; #endif { /* Local variables */ long i, j, l, zero_func, work_dim1; float scale, tmp; double atmp; zero_func = TRUE; for (i = *ln ; i >= 0 && zero_func ; --i) { if (c[i] != 0.0 || d[i] != 0.0) { zero_func = FALSE; } } if ( zero_func ) { *error = 1; return; } work_dim1 = *ln + 1; l = i + 1; for (j = 0 ; j <= l ; ++j) { work[j * work_dim1] = 1.0; } tmp = 1.0; for (i = 1 ; i <= l ; ++i) { tmp = tmp * (float) (l - i + 1) / (float) i; work[i] = tmp; } for (i = 1 ; i <= l ; ++i) { for (j = 1 ; j <= l ; ++j) { work[i + j * work_dim1] = work[i + (j - 1) * work_dim1] - work[i - 1 + j * work_dim1] - work[i - 1 + (j - 1) * work_dim1]; } } for (i = l ; i >= 0 ; --i) { b[i] = 0.0; atmp = 0.0; for (j = 0 ; j <= l ; ++j) { b[i] += work[i + j * work_dim1] * d[j]; atmp += (double) work[i + j * work_dim1] * c[j]; } scale = (double) atmp; if (i != 0) { a[i - 1] = (double) atmp; } } if (scale == 0.0) { *error = 2; return; } b[0] /= scale; for (i = 1 ; i <= l ; ++i) { b[i] /= scale; a[i - 1] /= scale; } if (l < *ln) { for (i = l + 1 ; i <= *ln ; ++i) { b[i] = 0.0; a[i - 1] = 0.0; } } *error = 0; return; } /* spbiln */ /* SPBSSL 11/13/85 */ /* GENERATES ANALOG FILTER COEFFICIENTS FOR LTH ORDER */ /* NORMALIZED LOWPASS BESSEL FILTER */ /* COEFFICIENTS ARE RETURNED IN ARRAYS D AND C */ /* LN SPECIFIES ARRAY SIZE (LN>=L) */ /* WSCL CONTROLS FREQUENCY SCALING SUCH THAT RESPONSE AT 1 RAD/SEC */ /* IS EQUAL TO THAT OF UNSCALED H(S) AT WSCL RAD/SEC */ /* IERROR=0 NO ERRORS DETECTED */ /* 1 INVALID FILTER ORDER (L<=0 OR L>=LN) */ /* 2 INVALID SCALE PARAMETER (WSCL<=0) */ #ifndef KR void spbssl(long *l, float *wscl, long *ln, float *d, float *c, long *error) #else void spbssl(l, wscl, ln, d, c, error) long *l, *ln, *error; float *d, *c, *wscl; #endif { /* Builtin functions */ long pow_ii(); double pow_ri(); /* Local variables */ double spbfct(); long k, ll, tmp_int1, tmp_int2; if (*l <= 0 || *l > *ln) { *error = 1; return; } if (*wscl <= 0.0) { *error = 2; return; } for (ll = 0 ; ll <= *ln ; ++ll) { d[ll] = 0.0; c[ll] = 0.0; } for (k = 0 ; k <= *l ; ++k) { tmp_int1 = (*l * 2) - k; tmp_int2 = *l - k; c[k] = pow_ri(wscl, &k) * spbfct(&tmp_int1,&tmp_int1) / (pow_ii(&pos_i2, &tmp_int2) * spbfct(&k,&k) * spbfct(&tmp_int2,&tmp_int2)); } d[0] = c[0]; *error = 0; return; } /* spbssl */ /* SPBWCF 11/13/85 */ /* GENERATES KTH SECTION COEFFICIENTS FOR LTH ORDER NORMALIZED */ /* LOWPASS BUTTERWORTH FILTER */ /* SECOND ORDER SECTIONS: K<=(L+1)/2 */ /* ODD ORDER L: FINAL SECTION WILL CONTAIN 1ST ORDER POLE */ /* LN DEFINES COEFFICIENT ARRAY SIZE */ /* ANALOG COEFFICIENTS ARE RETURNED IN D AND C */ /* IERROR=0 NO ERRORS DETECTED */ /* 1 INVALID FILTER ORDER L */ /* 2 INVALID SECTION NUMBER K */ #ifndef KR void spbwcf(long *l, long *k, long *ln, float *d, float *c, long *error) #else void spbwcf(l, k, ln, d, c, error) long *l, *k, *ln, *error; float *d, *c; #endif { /* Local variables */ long i; float tmp; if (*l <= 0) { *error = 1; return; } if (*k <= 0 || *k > ((*l + 1) / 2)) { *error = 2; return; } d[0] = 1.0; c[0] = 1.0; for (i = 1 ; i <= *ln ; ++i) { d[i] = 0.0; c[i] = 0.0; } tmp = (float) *k - (*l + 1.0) / 2.0; if (tmp == 0.0) { c[1] = 1.0; } else { c[1] = (float) (-2.0 * cos((double) ((*k * 2) + *l - 1) * M_PI / (double) (*l * 2))); c[2] = 1.0; } *error = 0; return; } /* spbwcf */ 还有其它的几个例子
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值