DSP个人心得(1)

时间:2024.4.27

DSP, 一般来说是有两个意思,一个是数字信号处理(Digital Signal Processing),另一个则是(Digital Signal Processor).今天学习了一部分DSP的硬件结构,软件操作的话其实之前就尝试操作过,学过51,或者说接触过一点微处理器方面的同学都知道,这些东西大多都是按照买的开发板所给的教程(一般来说都会有一个新手教程,无论是视频或者PDF)一步一步的把光盘里所给的代码烧录进开发板里头去,然后你就可以看到你人生中第一个实验效果,基本上都是流水灯吧,我学51的时候第一个实验是流水灯,接触FPGA实验的时候也是流水灯,到现在买的DSP的开发板还是流水灯。当然,如果你用DSP仅仅只做流水灯的话确实很浪费,它的功能远远比这强大很多很多。扯多了,咱回到硬件结构吧,码字也和辛苦。

介绍DSP的硬件结构之前,我先说一下微处理器的发展的三个方向,一个是GPP,另有一个是MCU,还有一个则是DSP。DSP和其他通用处理器一样,都有CPU,存储器,总线,外设,接口,时钟等部分,但是,DSP有其自己的鲜明特点。

DSP采用了哈佛结构

计算机发展以来就有两大基本结构,一个是冯.诺依曼结构,还有一个就是哈佛结构,为了更直观的表示,在这里我就画一个直观图吧。

冯.诺依曼结构

DSP个人心得1

DSP个人心得1

哈佛结构

DSP采用哈佛结构,程序和数据分开,各有各自的地址总线和数据总线,取指和读数可以并行操作,这样极大的提高了DSP的处理速度。

DSP采用了流水操作

取指→译码→寻址→取数→运算→存储

上面这六个操作可以认为是一个流程,假设一个操作需要一个机器周期,那么通用CPU则需要六个机器周期,但是DSP的流水操作模式如下

取指→译码→寻址→取数→运算→存储

取指→译码→寻址→取数→运算→存储

取指→译码→寻址→取数→运算→存储

即是在第一条指令开始译码时,第二条指令就开始取指,当第一条指令开始寻址时,第二条指令就开始译码,第三条指令就开始取指,这样的操作方式称为流水操作。

我们考虑有一个指令快,即包含了很多指令,那么通用CPU平均执行一条指令需要六个机器周期,但是DSP平均执行一条指令的时间为:T=(6+n-1)/n = 1+5/n ;n表示该指令快含有的指令的数目,可见,limn?????1??

?5???1。相信这个简单的极限大家应该都?n?

知道,所以DSP平均下来一个指令只需要1个机器周期。可见速度有大大的提升。

DSP有独立的硬件乘法器

学过信号与系统的孩纸应该都知道,在我们进行卷积,傅里叶变换时,我们会重复的进行乘法,累加求和的操作,据我所知,除非有单独的函数库,如果用通用CPU的话你只能靠软件编程来实现这乘法累加的功能,并且需要若干个周期,但是DSP的硬件乘法器,用MAC指令(取数,乘法,累加)可在单周期内完成。

独立的DMA总线和控制器

DMA即直接存储器的访问,不经过CPU。通用CPU也有DMA 只不过不独立,即取数过程需要占用总线,总线被占用了,那么你的CPU也就相当于处于挂机状态了。而DSP则有独立的DMA总线,该总线为独立的,且有控制器,这样取数存储就不影响CPU的程序与数据总线。

DSP的CPU

通用的CPU由ALU和CU,ALU为算术逻辑单元,CU为控制单元。通用CPU算术逻辑运算是通过软件实现的,DSP有硬件乘法器,且可在单周期内完成操作。

DSP的数据溢出

通用CPU是溢出发生后,设立溢出标志位,举个简单的例子,我们在做C语言的时候,如果定义a,b,c都是int型的,如果c=a+b;且c的计算结果超出了具体多少,好像是3开头的吧,你会发现它给出的结果应该是一个负数,如果我没记错的话应该就是正确计算结果的补

码来着(上述内容全凭记忆,请自行脑补)而DSP则有一个最高位的检测位,不断的在检测,这样的话它将是在将要发生还未发生的时候通知你要溢出了,这是一个很大的特点。

今天还学了的是它的一个硬件乘法器的内部大致工作原理,这里我就不说了因为还要找图,太麻烦了,随便找一本DSP原理的书应该都能讲到这些东西。

附:本文仅仅是读者学习的第一篇心得或者说总结,请勿太当真,另外这也算是督促自己在暑假里要完成自己该完成的任务吧,希望能看到第二篇学习总结。


第二篇:DSP 优化心得


DSP 优化心得

C6XX优化经验总结
一、c6x的编译的常用选项
(一)c6x的编译程序为“cl6x.exe”使用的方法

Cl6x [options] [filenames]

Cl6x:   编译程序
Options: 编译选项
Filenames: C或汇编源文件

说明:
编译选项是一个字母或者两个字母,对大小写不敏感。
编译选项的前面需要有一个“-”符号。
一个字母的选项可以合并在一起。比如“-sgq”与“-s -g -q”相同。
两个字母的选项如果第一个字母相同也可以合并在一起。比如“-mgt”与“-mg -mt”相同。

(二)有关优化的选项
-mt:表示在程序中没有使用alaising技术,这使得编译器可以进行比较好的优化。
-o3:对文件级别进行最强的优化,一般在编译时应该使用这个选项。但是在个别情况下使用这个选项优化程序可能会出现
错误(-o2有相同现象,-o0和-o1不会出现错误)。可能是在优化循环,组织流水线的时候发生错误。如果有这种现象出现可以同时
使用-g选项,程序优化就不会出现错误,但是优化效果会下降。另外可以调整程序的表达方式,可能会避免编译器发生错误。
-pm:在程序级别进行优化。可以将所以文件联合在一起进行优化,主要有去掉没有被调用的函数、总是常数的变量以及没有使用的
函数返回值。建议由程序员自己进行这种优化工作。使用这个选项在win98下编译可能会出现找不到编译程序的情况。
-ms0:不使用冗余循环进行优化,减小程序的大小。一般情况下这个选项对程序大小的优化作用不明显。
-mh[n]:去掉流水线的epilog,减小程序的大小。这个选项的作用比较明显。但是有可能出现读取地址超出有效范围的问题,
所以要在数据段的开始和结尾处增加一些pading,或者在分配内存时保证数组的前面和后面一段范围内都是有效的地址。
可选的参数n给出这种pading的长度字节数。

(三)保留编译和优化信息的选项
-k:保留优化后生成汇编语言文件。
-s:汇编语言文件中加入优化信息,如果没有则加入C语言源程序作为注释。
-mw:在汇编语言文件加入软件流水线信息。

(四)有关调试和剖析的选项
-g:允许符号调试,在“out”文件中包含符号信息和行号信息,可以在c语言级别进行调试和剖析。使用联合使用-g、-mt和-o3可以保
证能够进行符号调试的情况下最大限度的优化。
-mg:允许profile优化后的程序。 在“out”文件中包含符号信息和很少的行号信息。允许在c语言的函数基本进行剖析。
如果联合使用这两个选项,-g选项可能被忽略,结果与只用-mg相同。

(五)其它类型
-mln:生成大内存模式的程序。
-ml0:缺省情况下将集合变量(数组和结构)作为far型。
-ml1:缺省情况下将全部函数作为far型
-ml2: 等于-ml0加-ml1
-ml3: 缺省情况下将全部数据和函数作为far型

(六)建议使用的编译方式
Cl6x -gk -mt -o3 -mw -ss “filename”
方式1用于程序的调试,这种方式具有比较强的优化能力,并且支持符号调试。在编译的过程中不会发生错误。
由于生成的“out”文件中包含了符号信息和行号信息,所以比较大。
Cl6x -k -mgt -o3 -mw -ss “filename”
方式2用于程序的剖析(profile),这种方式的优化能力几乎最强(绝大多数情况下与方式3相同),
并且支持对程序进行profile。文件中只包含了符号信息和很少的行号信息,所以“out”文件比较小。
Cl6x -k -mt -o3 -mw -ss “filename”
方式3用于最终的发行版本程序,可以对程序进行最强的优化,并且去掉了全部的符号和行号信息,所以“out”文件比较小。
由多个文件组成的程序应该编写makefile,将编译参数放在该文件中,并在其中说明使用的编译器的版本号。

(七)连接参数
-heap:指定堆的大小
-stack: 指定栈的大小
连接的各种选项应该统一放在“cmd”文件中
二、双重循环和多重循环的优化总结

双重循环多重循环看起来比较复杂,但实际上多重循环优化方法比较简单,就在于一个字:“拆”,一旦完成这一步之后,
多重循环就成为单层循环,优化就可以按照普通的单层循环来做了。
多重循环的特点是在优化器优化时只在最内层循环中形成一个pipeline,这样循环语句就不能充分利用C6的软件流水线,
而且对于内部循环的次数较少的情况,消耗在prolog和eplog上的cycle数也是不可忽视的。
针对这种状况可以考虑将多重循环拆开形成一个单层循环,可以拆外层循环也可以拆内层循环,
一般视具体情况而定。这样就可以充分利用优化器构成的Pipeline。如下例:

void fir2(const short input[], const short coefs[], short out[])
{
int i, j;
int sum = 0;
for (i = 0; i < 40; i++)
{
for (j = 0; j < 16; j++)
    sum += coefs[j] * input[i + 15 - j];
out[i] = (sum >> 15);
}

内层循环循环次数较少,运算量也不大,资源方面只占用了一个乘法器,一个cycle只使用一次乘法器,
而事实上我们可以在一个cycle内使用两个乘法器,所以还可以充分利用另外的一个乘法器。因此考虑将内层循环拆开来执行,如下:

void fir2_u(const short input[], const short coefs[], short out[])
{
int i, j;
int sum;
    for (i = 0; i < 40; i++)
      {
    sum = coefs[0] * input[i + 15];
    sum += coefs[1] * input[i + 14];
    sum += coefs[2] * input[i + 13];
    sum += coefs[3] * input[i + 12];
    sum += coefs[4] * input[i + 11];
    sum += coefs[5] * input[i + 10];
    sum += coefs[6] * input[i + 9];
    sum += coefs[7] * input[i + 8];
    sum += coefs[8] * input[i + 7];
    sum += coefs[9] * input[i + 6];
    sum += coefs[10] * input[i + 5];
    sum += coefs[11] * input[i + 4];
    sum += coefs[12] * input[i + 3];
    sum += coefs[13] * input[i + 2];
    sum += coefs[14] * input[i + 1];
    sum += coefs[15] * input[i + 0];
    out[i] = (sum >> 15);
}

这样虽然代码长度增加了,可变成了单循环,所有的运算都参加到pipeline中来,在Piped loop kernal
中产生每一个cycle内都使用了两个乘法器,充分利用了DSP内部的资源,提高了运行效率。又如下例:


tot = 4;
for (k = 0; k < 4; k++)
{
    max = 0;
    for (i = k; i < 44; i += STEP)
    {
      s = 0;
      for (j = i; j < 44; j++)
      s = L_mac(s, x[j], h[j - i]);
      y32[i] = s;
      s = L_abs(s);
      if (L_sub(s, max) > (Word32) 0)
      max = s;
    }
    tot = L_add(tot, L_shr(max, 1));
}
在这个多层循环中一共有三层循环,而最内层的循环的运算量很小,只有一次乘累加操作,
而我们知道C6中一个packet中可以做两个乘累加运算,所以为了增加内部循环的运算,减少外部循环的层数,
我们可以将第一层循环的操作拆开,其负责的运算加入到内部循环中,也就是在内层循环中一次做四次的乘累加运算,
这样将多次操作形成pipeline,提高了运行效率,优化后的C代码如下:  
tot = 4;
   max0=0;
   max1=0;
   max2=0;
   max3=0;
for (i = 0; i <44; i += STEP) //STEP=4, 11 times cirs
   {
//code
   for (j=0;j<=40-i;j++)
{s0=(Word32)(_sadd(s0,_smpy(hh[j],xx[j+i])));
s1=(Word32)(_sadd(s1,_smpy(hh[j],xx[j+i+1])));
s2=(Word32)(_sadd(s2,_smpy(hh[j],xx[j+i+2])));
s3=(Word32)(_sadd(s3,_smpy(hh[j],xx[j+i+3])));
}
}
//code

CCS的优化:
三、16位变为32位操作,使用intrinsic函数,用const等。

1、源代码:
Word32 L_mpy_ll(Word32 L_var1, Word32 L_var2)
{
double aReg;
Word32 lvar;
/* (unsigned)low1 * (unsigned)low1 */
aReg = (double)(0xffff & L_var1) * (double)(0xffff & L_var2) * 2.0;
/* >> 16 */
aReg = (aReg / 65536);
aReg = floor(aReg);
/* (unsigned)low1 * (signed)high2 */
aReg += (double)(0xffff & L_var1) * ((double)L_shr(L_var2,16)) * 2.0;
/* (unsigned)low2 * (signed)high1 */
aReg += (double)(0xffff & L_var2) * ((double)L_shr(L_var1,16)) * 2.0;
/* >> 16 */
aReg = (aReg / 65536);
aReg = floor(aReg);
/* (signed)high1 * (signed)high2 */
aReg += (double)(L_shr(L_var1,16)) * (double)(L_shr(L_var2,16)) * 2.0;
/* saturate result.. */
lvar = L_saturate(aReg);
return(lvar);
}

2、改编后的代码:
static inline Word32 L_mpy_ll(Word32 L_var1, Word32 L_var2)
{
Word32 aReg_hh;
Word40 aReg,aReg_ll,aReg_lh,aReg_hl;

aReg_ll = (Word40)_mpyu(L_var1, L_var2)>>16;
aReg_lh = (Word40)_mpyluhs(L_var1, L_var2);
aReg_hl = (Word40)_mpyhslu(L_var1, L_var2);
aReg_hh = _smpyh(L_var1, L_var2);
aReg = _lsadd(aReg_ll, _lsadd(aReg_lh, aReg_hl));
aReg = _lsadd(aReg>>15, aReg_hh);

return(_sat(aReg));
}

3、优化方法说明:
C6000编译器提供的intrinsic 可快速优化C代码,intrinsic用前下划线表示同调用函数一样可以调用它,即直接内联为C6000的函数。
例如,在上例的源代码中没有使用intrinsics,每一行C代码需多个指令周期,在改编后的代码中,每一行代码仅需一个指令周期。
例如,
“aReg_ll = (Word40)_mpyu(L_var1, L_var2)>>16”中“_mpyu”就是一个intrinsics函数,它表示两个无符号数的高16位相乘,
结果返回。C6000支持的所有intrinsics指令及其功能参见《TMS320C6000系列DSP的原理与应用》一书的第265、266页,
该书还提供了另外的例子。这些内联函数定义在CCS所在的C6000\CGTOOLS\Include目录下的C6X.h文件中。
下面这个例子是C6000的“Programmer's Guide”上提取的使用intrinsics优化C代码的例子。
源代码:
int dotprod(const short *a, const short *b, unsigned int N)
{
int i, sum = 0;

for (i = 0; i < N; i++)
sum += a[i] * b[i];
return sum;
}

改编后代码:
int dotprod(const int *a, const int *b, unsigned int N)
{
int i, sum1 = 0, sum2 = 0;

for (i = 0; i < (N >> 1); i++)
{
sum1 += _mpy (a[i], b[i]);
sum2 += _mpyh(a[i], b[i]);
}
return sum1 + sum2;
}

技巧:
在C语言的调试全部通过以后,可以尝试将尽可能多的语句使用intrinsics函数加以改编,
尤其在循环体内,这种改编可以大幅度减少执行时间。

四、
1、源代码:
void fir_fxd1(short input[], short coefs[], short out[])
{
int i, j;
for (i = 0; i < 40; i++)
{
for (j = 0; j < 16; j++)
out[i*16+j]= coefs[j] * input[i + 15 - j];
}
}

2、改编后的代码:
void fir_fxd2(const short input[], const short coefs[], short out[])
{
int i, j;

for (i = 0; i < 40; i++)
{
for (j = 0; j < 16; j++)
out[i*16+j]= coefs[j] * input[i + 15 - j];
}

3、优化方法说明:
C6000编译器如果确定两条指令是不相关的,则安排它们并行执行。 关键字const可以指定一个变量或者一个变量的存储单元保持不变。
这有助于帮助编译器确定指令的不相关性。例如上例中,源代码不能并行执行,而结果改编后的代码可以并行执行。

4、技巧:
使用const可以限定目标,确定存在于循环迭代中的存储器的不相关性。

五、
1、源代码:
void vecsum(short *sum, short *in1, short *in2, unsigned int N)
{
int i;

for (i = 0; i < N; i++)
sum[i] = in1[i] + in2[i];
}

2、改编后的代码:
void vecsum6(int *sum, const int *in1, const int *in2, unsigned int N)
{
int i;
int sz = N >> 2;

_nassert(N >= 20);

for (i = 0; i < sz; i += 2)
{
sum[i] = _add2(in1[i] , in2[i]);
sum[i+1] = _add2(in1[i+1], in2[i+1]);
}
}

3、优化方法说明:
源代码中,函数变量的定义是 short *sum, short *in1, short *in2, 改编后的代码函数变量是
int *sum, const int *in1, const int *in2, 整数类型由16位改编成32位,这时使用内联指令“_add2”一次可以完成两组16位整数的
加法, 效率提高一倍。注意这里还使用了关键字const和内联指令_nassert优化源代码。

4、技巧:
用内联指令_add2、_mpyhl、_mpylh完成两组16位数的加法和乘法,效率比单纯16位数的加法和乘法提高一倍。

六、if...else...语句的优化
(一)
1、源代码:
if (sub (ltpg, LTP_GAIN_THR1) <= 0)
{
adapt = 0;
}
else
{
if (sub (ltpg, LTP_GAIN_THR2) <= 0)
{
adapt = 1;
}
else
{
adapt = 2;
}
}

2、改编后的代码:
adapt = (ltpg>LTP_GAIN_THR1) + (ltpg>LTP_GAIN_THR2);

(二)
1、源代码:
if (adapt == 0)
{
if (filt>5443)
{
result = 0;
}
else
{
if (filt < 0)
{
result = 16384;
}
else
{
filt = _sshl (filt, 18)>>16; // Q15
result = _ssub (16384, _smpy(24660, filt)>>16);
}
}
}
else
{
result = 0;
}

2、改编后的代码:
filt1 = _sshl (filt, 18)>>16;
tmp = _smpy(24660, filt1)>>16;
result = _ssub(16384, tmp * (filt>=0));
result = result * (!((adapt!=0)||(filt>5443)));

(三)
1、源代码:
static Word16 saturate(Word32 L_var1)
{
Word16 swOut;

if (L_var1 > SW_MAX)
{
    swOut = SW_MAX;
    giOverflow = 1;
}
else if (L_var1 < SW_MIN)
{
    swOut = SW_MIN;
    giOverflow = 1;
}
else
    swOut = (Word16) L_var1;    /* automatic type conversion */
return (swOut);
}

2、改编后的代码:
static inline Word32 L_shl(Word32 a,Word16 b)
{
return ((Word32)((b) < 0 ? (Word32)(a) >> (-(b)) : _sshl((a),(b)))) ;
}

3、优化方法说明:
如果在循环中出现if...else...语句,由于if...else...语句中有跳转指令,而每个跳转指令有5个延迟间隙,
因此程序执行时间延长;另外,循环内跳转也使软件流水受到阻塞。直接使用逻辑判断语句可以去除不必要的跳转。
例如在例1的源代码最多有两次跳转,而改编后不存在跳转。例2和例3同样也去掉了跳转。

4、技巧:
尽可能地用逻辑判断语句替代if...else...语句,减少跳转语句。

七、
1、源程序
dm = 0x7FFF;
for (j = 0; j < nsiz[m]; j = add(j, 1))
{
    if (d[j] <= dm)
    {
      dm = d[j];
      jj = j;
    }
}
index[m] = jj;
2、优化后的程序
dm0 = dm1 = 0x7fff;
d0 = (Word16 *)&d[0];
d1 = (Word16 *)&d[1];
# pragma MUST_ITERATE(32,256,64);
for (j = 0; j < Nsiz; j+=2)
{
    n0 = *d0;
    d0 += 2;
    n1 = *d1;
    d1 += 2;
    if (n0 <= dm0)
    {
      dm0 = n0;
      jj0 = j;
    }
    if (n1 <= dm1)
    {
      dm1 = n1;
      jj1 = j+1;
    }
}
if (dm1 != dm0)
{
    index[m] = (dm1 < dm0)? jj1:jj0;
}
else
{
    index[m] = (jj1 > jj0)? jj1:jj0;
}
3、优化说明
求数组的最小值程序,优化时为了提高程序效率在一个循环之内计算N=1,3,5..和n=2,4,6...的最小值,
然后在比较二者的大小以求得整个数组的最小值。

八、
1、源程序
for (k = 0; k < NB_PULSE; k++)
{
    i = codvec[k];
    j = sign[i];
    index = mult(i, Q15_1_5);
    track = sub(i, extract_l(L_shr(L_mult(index, 5), 1)));
    if (j > 0)
    {
      if (i < l_subfr) code[i] = add(code[i], 4096);
      codvec[k] += (2 * L_SUBFR);
    }
    else
    {
      if (i < l_subfr) code[i] = sub(code[i], 4096);
      index = add(index, 16);
    }
    if (indx[track] < 0)
    {
      indx[track] = index;
    }
    else
    {
      if (((index ^ indx[track]) & 16) == 0)
      {
        if (sub(indx[track], index) <= 0)
        {
          indx[track] = shl((indx[track] & 16), 3)
             + shr(extract_l(L_mult((indx[track] &                15), NB_POS)), 1) + (index & 15);
        }
        else
        {
          indx[track] = shl((index & 16), 3)
             + shr(extract_l(L_mult((index & 15),                  NB_POS)), 1) + (indx[track] & 15);
        }
      }
      else
      {
        if (sub((indx[track] & 15), (index & 15)) <= 0)
        {
          indx[track] = shl((index & 16), 3)
             + shr(extract_l(L_mult((index & 15),                  NB_POS)), 1) + (indx[track] & 15);
        }
        else
        {
          indx[track] = shl((indx[track] & 16), 3)
             + shr(extract_l(L_mult((indx[track] & 15),              NB_POS)), 1) + (index & 15);
        }
      }
    }
}
2、优化后的程序
for (k = 0; k < 8; k++)
{
    i    = codvec[k];
    j    = sign[i];
    index = _smpy(i, 6554)>>16;
    track    = i - index*5;
    con = (j > 0);
    codvec[k] = codvec[k] + 110*con;
index = index + (!con)*16;
    conn = (i < l_subfr);
    cono = (j > 0)? 1:-1;
    code[i] = code[i] + 4096*conn*cono;
n0   = index;
t0 = indx[track];
n1   = n0&16;
t1   = t0&16;
n2   = n0&15;
t2   = t0&15;
    tmp0   = (_sshl(n1,19)>>16) + n2*NB_POS + t2;
    tmp1   = (_sshl(t1,19)>>16) + t2*NB_POS + n2;
    conp   = (((n1 == t1)&&(t0 > n0))||((n1 != t1)&&(t2 <= n2)));
tmp   = conp*tmp0 + (!conp)*tmp1;
    if (t0 < 0)
      indx[track] = n0;
    else
      indx[track] = tmp;
}
3、优化说明
源程序中在循环中含有许多的if结构,在优化时对if结构首先进行化简,
再将化简后的if结构用条件运算表达式进行改写,最后使循环可以Pipeline。
九、
1、源程序
for (i = 0; i < n; i++)
{
   max = -32767;
   for (j = 0; j < n; j++)
   {
    if (sub (tmp2[j], max) >= 0)
    {
      max = tmp2[j];
      ix = j;
    }
   }
   tmp2[ix] = -32768;
   tmp[i] = ix;
}
2、优化后的程序
if (n0>n1) {temp=n0;n0=n1;n1=temp;}
if (n1>n2) {temp=n1;n1=n2;n2=temp;}
   if (n2>n3) {temp=n2;n2=n3;n3=temp;}
   if (n3>n4) {temp=n3;n3=n4;n4=temp;}
   if (n0>n1) {temp=n0;n0=n1;n1=temp;}
   if (n1>n2) {temp=n1;n1=n2;n2=temp;}
   if (n2>n3) {temp=n2;n2=n3;n3=temp;}
   if (n0>n1) {temp=n0;n0=n1;n1=temp;}
   if (n1>n2) {return n1;}
3、优化说明
源程序也为一个求中值的问题,由于已知循环次数固定为5,因此将循环展开使用if语句直接求取中值。
十、
1、源程序
static Word16 Bin2int (Word16 no_of_bits, Word16 *bitstream)
{
Word16 value, i, bit;

value = 0;
for (i = 0; i < no_of_bits; i++)
{
value = shl (value, 1);
bit = *bitstream++;
if (sub (bit, BIT_1) == 0)
value = add (value, 1);
}
return (value);
}

for (i = 0; i < prmno[mode]; i++)
{
prm[i] = Bin2int (bitno[mode][i], bits);
bits += bitno[mode][i];
}
2、优化后的程序
value = 0;
bitsp = bits;
bitnop= &bitno[mode][0];
j = *bitnop++;
j1 = *bitnop++;
j2 = *bitnop++;
j3 = *bitnop++;
j4 = *bitnop++;
_nassert(loop[mode]>=35);
for (i = 0; i < loop[mode]; i++)
{
value = value*2 + *bitsp++;
j--;
if (j == 0)
{
*prm++ = value;
value = 0;
j = j1;
j1 = j2;
j2 = j3;
j3 = j4;
j4 = *bitnop++;
}
}
3、优化说明
源程序按照数据位流定义取出参数,为双重循环结构,优化中采用重新根据位流的bit长度定义循环次数,
化简为单重循环,然后优化循环,去除boundary,使pipeline的数目最小。

十一、copy程序的优化
1、源代码:
Word16 i;
for (i = 0; i < L; i++)
{
y[i] = x[i];
}
2、改编代码:
(1)要求数组长度能被2整除
Word32 i;
Word32   temp;
int *p1 = (int *)&x[0];
int *q1 = (int *)&y[0];
for (i = 0; i < L/2; i++)
{
temp = *p1++;
*q1++ = temp;
}
(2)要求数组长度能被4整除
Word32 i;
Word32   temp1, temp2;
Word32   *pin1, *pin2, *pout1, *pout2;
pin1 = (Word32 *)&x[0];
pin2 = (Word32 *)&x[2];
pout1= (Word32 *)&y[0];
pout2= (Word32 *)&y[2];
for (i = 0; i < L/4; i++)
{
temp1 = *pin1;
temp2 = *pin2;
pin1+=2;
pin2+=2;
*pout1= temp1;
*pout2= temp2;
pout1+=2;
pout2+=2;
}
3、优化方法说明:
把一次循环拷贝一个word16的数改为一次循环拷贝2个word16或4个word16的数。
4、技巧:
充分利用c6xx一次读取32位数的特性,并利用一个指令周期能读取两个数据的特点。
十二、set_zero程序的优化
1、源代码:
Word16 i;
for (i = 0; i < L; i++)
{
x[i] = 0;
}
2、改编代码:
(1)数组长度能被2整除
Word32 i;
int *x1 = (int *)&x[0];
for (i = 0; i < L/2; i++)
{
*x1++ = 0;
}
(2)数组长度能被4整除
Word32 i;
int *x1 = (int *)&x[0];
int *x2 = (int *)&x[2];
for (i = 0; i < L/4; i++)
{
*x1 = 0;
*x2 = 0;
x1++;
x2++;
x1++;
x2++;
}
3、优化方法说明:
把一次循环为一个word16的数赋值改为一次为2个或4个word16的数赋值。
4、技巧:
充分利用C6XX一次读取32位数的特点,并利用一个指令周期能读取两个数据的特点。
十三、32bit数与16bit数相乘
1、源代码:
L_tmp0 = Mac_32_16(L_32, hi1, lo1, lo2);
2、改编代码:
L_tmp0=_sadd(_sadd(_smpyhl(hl32, lo2),
(_mpyus(hl32, lo2)>>16)<<1), L_32);
3、优化方法说明:
hl32是32bit的数,hi1和lo1是16bit的数,且 hl32 = hi 1<<16 + lo1 << 1 ,即hi1和lo1分别是hl32的高16位数和低16位数。
函数Mac_32_16(L_32, hi1, lo1, lo2)实现
    L_32 = L_32 + (hi1*lo2)<<1 + ((lo1*lo2)>>15)<<1
源代码是把一个32位的数拆成两个16位的数与一个16位的数相乘,优化后的代码不拆开32位的数,
直接用32位的数与16位的数相乘。运用这种方法必须保证hl32的最低一位数必须为0,否则应用指令_clr(hl32, 0, 0)把
最低位清零。
4、技巧:
源代码中的低16位数lo1是hl32的低16位右移一位得到的(留出一位符号位)。在与lo2相乘时又右移了15位,
所以在改编代码中右移16位,并且是以无符号数与lo2相乘。
十四、32bit数与32bit数相乘
1、源代码:
L_tmp = Mac_32 (L_32, hi1, lo1, hi2, lo2);
2、改编代码:
L_tmp = _sadd(_sadd(_smpyh(hl1_32, hl2_32),
      ((_mpyhslu(hl1_32, hl2_32)>>16)<<1)+
      ((_mpyhslu(hl2_32, hl1_32)>>16)<<1)), L_32);
3、优化方法说明:
两个32位的数相乘,不必分成四个16位的数相乘,直接用32位相乘。其中:
    hl1_32 = hi1<<16 + lo1<<1, hl2_32 = hi2 <<16 + lo2 <<1 。
源代码实现: L_32 = L_32 + (hi1*hi2)<<1 + ( (hi1*lo2)>>15 + (lo1*hi2)>>15 )<<1
4、技巧:
低16位与高16位相乘时,低16位使用的是无符号数。
十五、16位除法的优化
1、源代码:
Word16 div_s (Word16 var1, Word16 var2) //实现 var1/var2
{
Word16 var_out = 0;
Word16 iteration;
Word32 L_num = (Word32)var1;
Word32 L_denom = (Word32)var2;
for (iteration = 0; iteration < 15; iteration++)
{
var_out <<= 1;
L_num <<= 1;
if (L_num >= L_denom)
{
L_num = L_sub (L_num, L_denom);
var_out = add (var_out, 1);
}
}
return (var_out);
}
2、改编代码:
Word16 div_s1 (Word16 var1, Word16 var2)
{
Word32 var1int;
Word32 var2int;
var1int = var1 << 16;
var2int = var2 << 15;
var1int = _subc(var1int,var2int);
var1int = _subc(var1int,var2int);
var1int = _subc(var1int,var2int);
var1int = _subc(var1int,var2int);
var1int = _subc(var1int,var2int);
var1int = _subc(var1int,var2int);
var1int = _subc(var1int,var2int);
var1int = _subc(var1int,var2int);
var1int = _subc(var1int,var2int);
var1int = _subc(var1int,var2int);
var1int = _subc(var1int,var2int);
var1int = _subc(var1int,var2int);
var1int = _subc(var1int,var2int);
var1int = _subc(var1int,var2int);
var1int = _subc(var1int,var2int);
return (var1int & 0xffff);
}
3、优化方法说明:
实现16位的除法,要求被除数var1和除数var2都是整数,且var1<=var2。利用C6XX特有的指令subc,实现除法的循环移位相减操作。
4、技巧:
把被除数和除数都转换成32位数来操作,返回时取低16位数。
十六、C6X优化inline举例:

1、原程序:
for (i = LO_CHAN; i <= HI_CHAN; i++)
{

    norm_shift = norm_l(st->ch_noise[i]);
    Ltmp = L_shl(st->ch_noise[i], norm_shift);

    norm_shift1 = norm_l(st->ch_enrg[i]);
    Ltmp3 = L_shl1(st->ch_enrg[i], norm_shift1 - 1);

    Ltmp2 = L_divide(Ltmp3, Ltmp);
    Ltmp2 = L_shr(Ltmp2, 27 - 1 + norm_shift1 - norm_shift); // * scaled as 27,4 *

    if (Ltmp2 == 0)
      Ltmp2 = 1;

    Ltmp1 = fnLog10(Ltmp2);
    Ltmp3 = L_add(Ltmp1, LOG_OFFSET - 80807124); // * -round(log10(2^4)*2^26 *
    Ltmp2 = L_mult(TEN_S5_10, extract_h(Ltmp3));
    if (Ltmp2 < 0)
      Ltmp2 = 0;
    // * 0.1875 scaled as 10,21 *
    Ltmp1 = L_add(Ltmp2, CONST_0_1875_S10_21);
    // * tmp / 0.375 2.667 scaled as 5,10, Ltmp is scaled 15,16 *
    Ltmp = L_mult(extract_h(Ltmp1), CONST_2_667_S5_10);
    ch_snr[i] = extract_h(Ltmp);
}
*/



2、优化后程序:
//因循环体太大,拆成两个循环并把相应的函数内嵌以使程序能pipeline,
//用L_div_tmp[]保存因拆分而产生的中间变量。
for (i = LO_CHAN; i <= HI_CHAN; i++)
{
    //norm_shift = norm_l(st->ch_noise[i]);
    norm_shift = _norm(st->ch_noise[i]);
    Ltmp = _sshl(st->ch_noise[i], norm_shift);

    //norm_shift1 = norm_l(st->ch_enrg[i]);
    norm_shift1 = _norm(st->ch_enrg[i]);   
    //Ltmp3 = L_shl1(st->ch_enrg[i], norm_shift1 - 1);
    LLtmp1 = st->ch_enrg[i];   
    LLtmp1 = LLtmp1 << (norm_shift1 + 7);
    Ltmp3 = (Word32)(LLtmp1 >> 8);

    Ltmp2 = IL_divide(Ltmp3, Ltmp);
    //Ltmp2 = L_shr(Ltmp2, 27 - 1 + norm_shift1 - norm_shift);  
    Ltmp2 = (Ltmp2 >> (27 - 1 + norm_shift1 - norm_shift));

    if (Ltmp2 == 0)
      Ltmp2 = 1;
    L_div_tmp[i] = Ltmp2;
}
for (i = LO_CHAN; i <= HI_CHAN; i++)
{
    Ltmp2 = L_div_tmp[i];
    Ltmp1 = IfnLog10(Ltmp2);
    //Ltmp3 = L_add(Ltmp1, LOG_OFFSET - 80807124);
    Ltmp3 = _sadd(Ltmp1, LOG_OFFSET - 80807124);
    //Ltmp2 = L_mult(TEN_S5_10, extract_h(Ltmp3));
    Ltmp2 = _smpy(TEN_S5_10, (Ltmp3 >> 16));
    if (Ltmp2 < 0)
      Ltmp2 = 0;
   
    Ltmp1 = _sadd(Ltmp2, CONST_0_1875_S10_21);
   
    //Ltmp = L_mult(extract_h(Ltmp1), CONST_2_667_S5_10);
    Ltmp = _smpy((Ltmp1 >> 16), CONST_2_667_S5_10);   
    //ch_snr[i] = extract_h(Ltmp);
    ch_snr[i] = (Ltmp >> 16);
}

3、优化说明
观察上面这个循环,循环体本身比较大,且含有两个函数L_divide()和
fnLog10(),而C62内部只有32个寄存器,且有些寄存器是系统用的,如B14、B15这样循环体太大将会导致寄存器不够分配,
从而导致系统编译器无法实现循环的pipeline。

为了实现循环的pipeline。我们需要把循环体进行拆分,拆分时要考虑以下几点:
(1)、拆分成几个循环比较合适?在各个循环能pipeline的前提下,拆开的循环个数越少越好。这就要求尽可能让各个
循环的运算量接近。
(2)考虑在什么地方把程序拆开比较合适?循环体里的数据流往往并不是单一的,在拆开的断点处势必要用中间变量保
存上次的循环运算结果,供以后的循环用。适当的拆开循环体,使所需的中间变量越少越好。
(3)循环体中的函数调用必须定义成内嵌形式,含有函数调用的循环系统是无法使之pipeline的;各个循环体中的判断分支
机构不可太多,否则系统也无法使之pipeline,为此应近可能把可以确定下来的分支确定下来,并尽可能用内嵌指令。

针对上面这个例子,考虑:
(1)为让各个循环的运算量大致相当,应把L_divide()和fnLog10()分到两个循环中去,从循环体大小上考虑,
估计拆成两个循环比较合适。
(2)考虑在什么地方把程序拆开比较合适?在
    if (Ltmp2 == 0)
      Ltmp2 = 1;
后拆开,因为后面用到的数据只有Ltmp2,故只需用一个数组保存每次循环的Ltmp2值即可。
(3)循环体中的两处函数调用L_divide()和fnLog10()都定义了其内嵌形式,IL_divide()和IfnLog10()。
当把可以确定下来的分支作确定处理,并尽可能用内嵌指令后,该循环体中所剩的分支结构已很少,循环体可以pipeline。
优化前程序用2676 cycle,优化后用400 cycle。优化后两个子循环的MII分别为14和6cycle。

内存地址形式: 奔腾,C6000都是32位计算机,字长32,但内存地址都是按字节组织的 一个字4字节(查看内存时候各个字
时候:例如两个连续字ox1000 ox1004) 写汇编程序时候,下一个字也需要+4,但写 C语言时候,int 型,+1就是加4

但是,在Tiger SHARC中,虽然也是32位机,但内存是地址是按字组织的,查看内存时,连续的字地址相差1

//////////////////////////////////////////////////////////////////////////////////自己写的一段性能很高的代码///////////////////////////
#include <stdio.h>
#define INTRINSIC

short add(short var1,short var2)
{
   short var_out;
   int L_somme;

   L_somme = (int) var1 + var2;
   return(var_out);
}

int main()
{
int i,result;
#ifdef INTRINSIC
for(i=0; i<1000;i++)
{
result=_sadd(100000,20);
result>0X00007fff?result=0x7fff:(result<0x8000?result=0x8000:0);
}
#else
for(i=0;i<1000;i++)
add(10,20);
#endif

return 0;
}

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/guanchanghui/archive/2006/09/05/1181851.aspx

更多相关推荐:
个人工作心得体会

个人工作心得体会时间飞逝转眼20xx年底已到我于20xx年9月1日来我院工作至今已快四个月了在这四个月的时间里因工作需要我曾经在个内科住院部内科门诊疼痛科理疗科综合治疗室等处工作现在为医院创二级医院准备相关资料...

个人工作心得

运城市欣达房地产开发有限公司个人工作总结工作总结时光飞逝,岁月流转,转眼间我到公司工作已有半个多月,回顾这半个多月来的工作,在公司领导及同事们的关心与帮助下,我对公司的工作环境、管理模式、组织机构、规章制度有了…

个人心得体会

个人心得体会郭店镇八府村主任助理王定杰从08年8月份成为大学生村官到农村工作不觉间快一年了在忙碌和快乐的工作中我时刻牢记自己的责任和使命以满腔的热情投入到农村基层工作中同时我也深深的感到了领导的关心和爱护以及对...

个人工作心得体会1

个人工作心得体会XXXX年XX月XX日我怀着激动的心情来到高新生态花园项目部就在那一天我的职业生涯真正意义上拉开了帷幕初到单位这里的一切让我觉得陌生而新鲜因此内心不免有些忐忑和激动当看到前辈们脸上的笑容时那种亲...

个人心得体会

个人心得体会有一句话一直在鼓励我,那就是“要做好一件事,能出十分力,那么决不能出九分”。作为一名大三的学生,从青协进入学生会,或许在这之前我还认为我了解的已经不少了,可真当我步入学生会这个集体的时候,我才明白我…

个人工作心得体会

个人工作心得体会个人工作gt心得体会随着一年的结束我们即将迈入新的一年转眼之间我参加工作已两年有余回顾我在20xx年里有耕耘有收获有失落有欢喜收获最多的是我一生中最难忘的情谊和工作经验上的财富在以往的工作中由于...

个人读书心得体会

书中自有黄金屋,书中自有颜如玉,自古人们就推崇读书,到了现代社会科技高速发展,电视、网络各种媒体平台纷纷出现,可这并未减少人们对于读书的热情,读书就是思想的源泉,读书与健康成长紧紧相联,读书促进成长,成长离不开…

买房个人心得

五证一个是建设用地规划许可证第二是建设工程规划许可证第三是建设工程开工证第四是国有土地使用证第五是商品房预售许可证简称叫五证其中前两个证是由市规划委员会核发的开工市是由市建委核发的国有土地使用证和商品房预售许可...

个人工作心得体会

人生的感悟常吉高速公路管理处乌云界收费站丁掌20xx年4月27日天波易谢寸暑难留转眼我已经在乌云界收费站愉快的工作了快2个月了非常幸运是通过2个月的工作学习我在站长和同事们的关心指导下已从最初的不安和紧张到现在...

情绪管理个人心得

情绪自我管理培训之心得体会情绪是对一系列主观认知经验的通称是多种感觉思想和行为综合产生的心理和生理状态最普遍通俗的情绪有喜怒哀惊恐爱等也有一些细腻微妙的情绪如嫉妒惭愧羞耻自豪等情绪常和心情性格脾气目的等因素互相...

20xx年小继教个人学习总结

20xx年小继教个人学习总结白沙小学王小会一年以来认真学习增强自身的科学文化素质和教育教学能力同时增强自己的业务能力这样才能适应新时期教育教学的需要为此我将小继教的学习总结如下一基本功训练以省市及县教科局一系列...

LOL个人心得

1低分段和高分段玩家最大的差别就是意识的差距对线技巧方面其实不会相差很多也见过段位比较低的线上杀段位比较高的玩家好几次提高意识首先你要从本质上去了解这个游戏其实我建议大家不要去开盒子看到对面段位并不能带给你什么...

个人心得(43篇)