|
本帖最后由 BOB_Sun 于 2015-7-28 15:33 编辑
[ARM开发] 【Altera SOC体验之旅 】Soc FPGA之DSP进阶---NEON高性能媒体引擎(二)
本文转自 http://bbs.eeworld.com.cn/thread-462935-1-1.html
Neon函数在头文件arm_neon.h中已经被定义,头文件既定义内嵌函数,也定义一组向量类型,编译器将NEON内嵌函数视为普通函数调用。
基本的DSP处理模块
例程1 实现从0-99的加法运算作为使用NEON函数的基本DSP处理模块。
----------------------------------------------------------------------------------------------
#include<stdio.h>
#include<arm_neon.h>
void fill_array(int16_t *array,int size)
{
int i;
for(i=0;i<size;i++)
{
array=i;
}
}
int sum_array(int16_t *array,int size)
{
int16_t *i;
int16x4_t acc=vdup_n_s16(0);
int32x2_t acc1;
int64x1_t acc2;
for(i=array;i<array+size;i+=4)
{
int16x4_t vec;
vec=vld1_s16(i);
acc=vadd_s16(acc,vec);
}
acc1=vpaddl_s16(acc);
acc2=vpaddl_s32(acc1);
return (int)vget_lane_s64(acc2,0);
}
int main()
{
int16_t my_array[100];
fill_array(my_array,100);
printf("sum was %d/n",sum_array(my_array,100));
return 0;
}
例程2 浮点向量运算,进行简单的浮点向量计算。VFP除了对浮点数基本加减乘除,开方,比较,取反提供支持之外,其向量功能能够使它同时支持最多8组单精度、4组双精度浮点运算。
------------------------------------------------------------------------------------------------------
#include<stdio.h>
#include<arm_neon.h>
int main()
{
float a[4]={2,4,8,16};
float b[4]={2,4,8,16};
float c[4]={0,0,0,0};
float32x4_t sample1,sample2,result;
sample1=vldlq_f32(a);
sample2=vldlq_f32(b);
result=vmulq_f32(sample1,sample2);
vst1q_f32(c,result);
printf("Result:%f,%f,%f,%f",c[0],c[1],c[2],c[3]);
return 0;
}
例程3 矩阵运算,用NEON优化矩阵乘法的实例,介绍如何用NEON有效地处理一个4X4的矩阵乘法运算,经常用于3D图形,我们认为这些矩阵在内存中是以列为主排列的。假设每列元素在NEON寄存器中表示为一个向量,那么矩阵乘法就是向量乘以标量的运算,而后续的累加也同样可以用向量乘以标量的累加指令实现。
--------------------------------------------------------------------------------------------------------------
#include<stdio.h>
#include<arm_neon.h>
int main()
{
int i;
uint16x4_t vectorT1,vectorT2,vectorT3,vectorT4;
uint16x4_t A[4][4]={ 1,2,3,4,
5,6,7,8,
9,10,11,12,
13,14,15,16};
uint16x4_t B[4][4]={16,15,14,13,
12,11,10,9,
8,7,6,5,
4,3,2,1};
uint16_t C[4][4];
uint16x4_t vectorB1,vectorB2,vectorB3,vectorB4;
vectorB1=vld1_u16(b[0]);
vectorB2=vld1_u16(b[1]);搜索
复制
vectorB3=vld1_u16(b[2]);
vectorB4=vld1_u16(b[3]);
for(i=0;i<4;i++)
{
vectorT1=vmul_n_u16( vectorB1,A[0]);
vectorT2=vmul_n_u16( vectorB2,A[1]);
vectorT3=vmul_n_u16( vectorB3,A[2]);
vectorT4=vmul_n_u16( vectorB4,A[3]);
vectorT1=vadd_u16( vectorT1, vectorT2);
vectorT1=vadd_u16( vectorT1, vectorT3);
vectorT1=vadd_u16( vectorT1, vectorT4);
vst1_u16(C,vectorT1);
printf("C[%d][0]=%ld\n",i,C[0]);
printf("C[%d][0]=%ld\n",i,C[1]);
printf("C[%d][0]=%ld\n",i,C[2]);
printf("C[%d][0]=%ld\n",i,C[3]);
}
return 0;
}
附录: ARM和NEON指令
在移动平台上进行一些复杂算法的开发,一般需要用到指令集来进行加速。目前在移动上使用最多的是ARM芯片。
ARM是微处理器行业的一家知名企业,其芯片结构有:armv5、armv6、armv7和armv8系列。芯片类型有:arm7、arm9、arm11、cortex系列。指令集有:armv5、armv6和neon指令。关于ARM到知识参考:http://baike.baidu.com/view/11200.htm
最初的ARM指令集为通用计算型指令集,指令集都是针对单个数据进行计算,没有并行计算到功能。随着版本的更新,后面逐渐加入了一些复杂到指令以及并行计算到指令。而NEON指令是专门针对大规模到并行运算而设计的。
NEON 技术可加速多媒体和信号处理算法(如视频编码/解码、2D/3D 图形、游戏、音频和语音处理、图像处理技术、电话和声音合成),其性能至少为ARMv5 性能的3倍,为 ARMv6 SIMD性能的2倍。
关于SIMD和SISD:Single Instruction Multiple Data,单指令多数据流。反之SISD是单指令单数据。以加法指令为例,单指令单数据(SISD)的CPU对加法指令译码后,执行部件先访问内存,取得第一个操作数;之后再一次访问内存,取得第二个操作数;随后才能进行求和运算。而在SIMD型的CPU中,指令译码后几个执行部件同时访问内存,一次性获得所有操作数进行运算。这个特点使SIMD特别适合于多媒体应用等数据密集型运算。如下图所示:
如何才能快速到写出高效的指令代码?这就需要对各个指令比较熟悉,知道各个指令的使用规范和使用场合。
ARM指令有16个32位通用寄存器,为r0-r15,其中r13为堆栈指针寄存器,r15为指令计算寄存器。实际可以使用的寄存器只有14个。r0-r3一般作为函数参数使用,函数返回值放在r0中。若函数参数超过4个,超过到参数压入堆栈。
有效立即数的概念:每个立即数采用一个8位的常数(bit[7:0])循环右移偶数位而间接得到,其中循环右移的位数由一个4位二进制(bit[11:8] )的两倍表示。如果立即数记作<immediate> , 8位常数记作immed_8 , 4位的循环右移值记作rotate_imm ,有效的立即数是由一个8位的立即数循环右移偶数位得到,可以表示成:
<immediate>=immed_8循环右移( 2×rotate_imm)
如:mov r4 , #0x8000 000A #0x8000 000A 由0xA8循环右移0x2位得到。
下面介绍一些比较常用到一些指令。
内存访问指令:
LDR和STR,有三种方式,比较容易搞混
LDR r0, [r1, #4] r0 := mem[r1+4] ,#4是直接偏移量,这时候只能在正负4Kb到范围内。也可以是寄存器偏移,用+/-表示。记住r1不进行偏移。
LDR r0, [r1, #4]! r0 :=mem[r1+4],r1 := r1 + 4,取值是取偏移量到值,并且r1进行偏移。
LDR r0, [r1], #4 r0 :=mem[r1] ,r1 := r1 +4,取值是取r1地方到值,取值后进行偏移。运算后自动加4,后变址。
另外:LDRB是无符号字节,SB是有符号字节,H无符号半字,SH有符号半字。
存储器和寄存器数据交换:SWP,SWPB
如SWP r0, r1, [r2] r0 := mem[r2],mem[r2] := r1
多寄存器数据传输:
LDMIA r1, {r0,r2,r5} r0 = mem[r1], r2 = mem[r1+4], r5=mem[r1+8]
通用数据处理指令
第二操作数,常用到有LSR,LSL等,如mov r1, r2, lsl #2 将r2左移2位然后赋值到r1中。
常用到操作有ADD、SUB、AND、ORR、EOR、BIC、ORN,如果加上了S则会更新条件标记。
MOV移动,MVN取反移动。MOV可以是R寄存器,立即数以及接第二操作数。
REV:在字或半字内反转字节或位到顺序
MUL、MLA和MLS,乘法、乘加和乘减。MLA R1,R2,R3,R4表示R1=R2*R3+R4,还有有符号和无符号乘法等。
跳转指令
B:无条件跳转,BL:带链接到跳转,BX跳转并交换指令集等。
重点介绍一下NEON指令,目前使用较多。而且使用难度也较大,很多文档上都没有比较详细到介绍,也没有给出相应到例子或者图示。
(未完,跟帖中)
|
|