再上一篇:6.2.3 Angel 的通信结构
上一篇:6.3 ADS 开发环境
主页
下一篇:6.5 嵌入式 Linux
再下一篇:6.5.2 Linux 的开发模式
文章列表

6.4.2 在 ARM 平台上使用 IPP 开发应用

嵌入式系统(修订本)——Intel XScale 结构与开发 陈章龙 著

一、IPP ARM 处理器的支持
到V1.11 为止,IPP 向ARM 处理器提供了超过2500 个函数调用,并且有Windows CE
和Linux 两个版本。具体涉及到DSP 和多媒体数据处理:

图6-10 IPP DSP功能

图6-11 IPP 多媒体数据处理支持

IPP 在ARM 支持上有其特殊之处,主要集中在内存分配和浮点数运算方面。

因为ARM和XScale处理器多用于手持移动设备等嵌入式系统中,所以在内存分配时常会遇 到分配失败等情况。通常,这些情况由操作系统的异常机制来处理,保证系统资源与用户信 息不被丢失。如释放进程的所有已经获得的资源,退出进程;或让进程进入等待队列。由于 IPP避免使用操作系统的特性,所以在设计支持StrongARM和XScale的调用时,在函数运行 过程不涉及内存分配,所有内存资源都由开发者先申请,再提交给IPP函数。而由此也会带 来新的问题,例如如何确定所需内存的大小等。这些方面在支持IA32和Itanium Architechture 的IPP中是不必太担心的,它们可能更关心并行处理。
例如二维FFT的初始化,IA32体系结构的IPP由函数

IppStatus ippiFFTInitAlloc_R_32s ( IppiFFTSpec_R_32s** pFFTSpec, int orderX, int orderY, int flag, IppHintAlgorithm hint );

完成,而StrongARM的IPP由函数

IppStatus ippiFFTInit_R_8u (IppiFFTSpec_R_8u* pCtx, int xOrder, int yOrder, int flag, IppHintAlgorithm hint );

完成。两者除了由于输入输出参数的数据类型不同导致的函数名后缀各为32s和8u外,关键
的差别在于前者的Alloc表示该函数会在运行过程中申请内存,并通过输出参数返回;后者 则不会在运行过程中申请内存,而需要应用程序事先申请。
有意思的是,后者所要事先申请的内存并不是一个固定大小的结构,因此IPP还提供了 另一个函数
IppStatus ippiFFTGetSpecSize_R_8u ( int xOrder, int yOrder, IppHintAlgorithm hint, int* pSize );来计算所需内存的大小。 这实际上是将一件事情分成三步来做。将内存分配这件与操作系统相关的棘手工作交由应用 程序自己处理,而IPP只做元语级的算法工作。由于嵌入式系统的软硬件环境各不相同,所 以只实现最基本的算法功能,不涉及I/O和操作系统,是IPP能够跨平台的重要因素。
另一个方面,由于StrongARM 和XScale 只有整数运算指令,而手持设备通常不另加浮 点运算协处理器,但是某些算法必须使用小数保证精度,如信号发生,滤波器的参数,各种 变换的结果等。通常,使用浮点数运算模拟库来用整数运算模拟浮点数运算,就像Linux, 但是单纯地用整数运算模拟浮点运算会使性能大幅下降。在一些需要浮点数的场合,IPP 采 用了一些技巧兼顾计算误差和运算性能。
首先在数据类型上,IPP 定义了 Q16 数据类型。Q16 为 4 字节,定义为小数点在 15,

16 位中间的定点小数。

此外,在某些需要使用浮点数的函数中,通过添加一个变量充当阶数,从而扩大有效位数, 降低误差,达到模拟浮点数运算的效果。

如:IppStatusippsFIR_Direct_16s_ISfs(Ipp16s*pSrcDst,intsampLen,const
Ipp16s*pTapsQ15,inttapsLen,Ipp16s*pDelayLine,int*pDelayLineIndex, int scaleFactor);使用这种方法的函数都有Sfs后缀,表示Saturated fixed scaling。凭借 这些技巧,在没有浮点运算协处理器和浮点运算模拟库的情况下,仅用整数运算指令就实现 了需要浮点数的多种算法。
二、实例
就浮点数运算举一个简单的例子:

1 #include <stdio.h>

2 #include <math.h>

3 #include "ippdefs.h"

4 #include "ippSP.h"

5 #define tapsLen 20

6 #define N 40

7 #define scaleFactor 4

8 int main()

9 {

10 int i;

11 Ipp16s pSrc[N], pDst[N];

12 Ipp32s pDelayLine[tapsLen*2];

13 int pDelayLineIndex;

14 Ipp16s pTaps[tapsLen];

15 float b[tapsLen] =

16 { 0.080000000, 0.104924069, 0.176995366, 0.288403847, 0.427076676,

17 0.577986499, 0.724779895, 0.851549523, 0.944557926, 0.993726200,

18 0.993726200, 0.944557926, 0.851549523, 0.724779895, 0.577986499,

19 0.427076676, 0.288403847, 0.176995366, 0.104924069, 0.080000000 };

20 /* scale the filter taps to Q15 */

21 for ( i = 0; i < tapsLen; i ++ )

22 {

23 b[i] *= (1<<14);

24 pTaps[i] = (b[i] > 32767)?32767 : b[i];

25 }

26 /* random input signal */

27 srand(200);

28 for ( i = 0; i < N; i ++ )

29 pSrc[i] = rand() - 32768/2;

30 pDelayLineIndex = 0;

31 for ( i = 0; i < tapsLen*2; i ++ )

32 pDelayLine[i] = 0;

33 ippsFIR_Direct_16s_Sfs(pSrc, pDst, N, pTaps, tapsLen, pDelayLine,

34 pDelayLineIndex, scaleFactor);

35 /* display out signal vector */

36 for ( i = 0; i < N; i ++ )

37 {

38 printf("%8d", pDst[i]);

39 if ( (i+1)%5 == 0 )

40 {

41 printf("\n");

42

}

43

}

44

return(0);

45

}

这个例子用随机函数生成输入序列,用ippsFIR_Direct_16s_Sfs 进行FIR 滤波,最后将
滤波结果输出。

ippsFIR_Direct_16s_Sfs 函数原型为:

IppStatus ippsFIR_Direct_16s_Sfs(const Ipp16s * pSrc, Ipp16s * pDst, int sampLen, const Ipp16s * pTapsQ15, int tapsLen, Ipp16s * pDelayLine, int * pDelayLineIndex, int scaleFactor);

滤波器参数必须保留足够小数位,以保证卷积后的误差。所以作为滤波器参数的第四个

参数采用类似 Q16 的 Q15 表示。它们的差别在于后者的最高位是符号位。第 23,24 行就 是把浮点数表示的滤波器参数用Q15 表示。对于超过表示范围得参数用32767 表示。 此外,这个函数以 Sfs 为后缀,使用了上面提到的第二个方法。第 34 行,最后一个参数 scaleFactor 值为4,说明滤波输出结果的结构为:
符号位(1) 整数(4) 小数点 小数(11)