imx6ull cortex-A7 armv7架构,已经引入了neon技术。ubuntu版本是16.04。Ne10则是引用了该技术,对数学计算进行优化,例如傅里叶变换、滤波、矩阵计算等。Ne10库在github上有源码,我用的版本是截至目前最新的。在一块老的A9平台编译,使用最新的版本编译,出现了很多问题,最后重新下载了最低的Ne10的版本,编译通过了。
编译Ne10需要用到cmake,如果环境中没有,需要安装下。Ne10下载包的doc里有building.md,讲述了如何编译。首先要使用cmake配置编译选项,涉及两个文件,第一个文件是GUNlinux_config.cmake,另一个是CMakeList.txt。
一步一步来,根据building.md里边的提示敲命令行:
//
cd $NE10_PATH
mkdir build && cd build
export NE10_LINUX_TARGET_ARCH=armv7 # Can also be “aarch64”
cmake -DCMAKE_TOOLCHAIN_FILE=…/GNUlinux_config.cmake …
make
//
当执行到cmake这句时,出现问题:
该错误和编译链有关,在GUNlinux_config.cmake文件中指定下编译链,再编译。在GUNlinux_config.cmake文件中添加的内容如下:
set(CMAKE_C_COMPILER /opt/imx6ull-yocto-L4.1.15-2.1.0/toolchain-qt5.6.2/sysroots/x86_64-pokysdk-linux/usr/bin/arm-poky-linux-gnueabi/arm-poky-linux-gnueabi-gcc)
set(CMAKE_CXX_COMPILER /opt/imx6ull-yocto-L4.1.15-2.1.0/toolchain-qt5.6.2/sysroots/x86_64-pokysdk-linux/usr/bin/arm-poky-linux-gnueabi/arm-poky-linux-gnueabi-g++)
set(CMAKE_ASM_COMPILER /opt/imx6ull-yocto-L4.1.15-2.1.0/toolchain-qt5.6.2/sysroots/x86_64-pokysdk-linux/usr/bin/arm-poky-linux-gnueabi/arm-poky-linux-gnueabi-as)
再次编译,又一次出现问题:
编译链是找到了,但编译过程中需要搜索的路径不对。mmp. 百度了下,GUNlinux_config.cmake文件中指定了下CMAKE_SYSROOT参数:set(CMAKE_SYSROOT “/opt/imx6ull-yocto-L4.1.15-2.1.0/toolchain-qt5.6.2/sysroots/cortexa7hf-neon-poky-linux-gnueabi”);再次编译,依然出错:
百度呗。意思是检测我的编译链不行,太low。mmp,就非要用这个链编译。于是在cmake又多加俩参数,配置成功了。 cmake -DCMAKE_TOOLCHAIN_FILE=…/GNUlinux_config.cmake -DCMAKE_C_COMPILER_WORKS=ON -DCMAKE_CXX_COMPILER_FORCED=ON …
接下来就是执行make。开始编译库。刚执行,又报错,fk. 如下:
又没有头文件,不都指定了吗,继续百度吧。是编译选项不对,没指定-mfloat-abi编译选项,加吧。把build文件夹删除,然后在GUNlinux_config.cmake文件中添加如下语句:
set(CMAKE_C_FLAGS “-mfloat-abi=hard”)
从头再来!
//
mkdir build && cd build
export NE10_LINUX_TARGET_ARCH=armv7 # Can also be “aarch64”
cmake -DCMAKE_TOOLCHAIN_FILE=…/GNUlinux_config.cmake …
make
//
呦吼!!!编译过了。如图:
我编译的是静态库,如果想编译动态库,可以在CMakeList.txt中修改。
//
option(NE10_BUILD_SHARED “Build NE10 shared libraries” OFF)
option(NE10_BUILD_STATIC “Build NE10 static libraries” ON)
//
默认的是编译静态库。如果想编译动态库,就把第一行OFF改成ON,把第二行ON改为OFF。没测试编译动态库。
至此,Ne10的库就编译完成了。编译好的静态库在build/modules文件夹里libNE10.a。注意引用的时候还需要引用头文件,在inc目录里。build目录里还有个samples文件夹,里边有个demo,NE10_test_static,这个拷贝到开发板上,可以直接运行,计算fft啥的。
再说一下使用neon和不使用的对比。我所运行的代码包括fft、滤波、比特转符号等操作,对算法不熟悉,其实是在同事那拷贝过来的,然后替换部分函数使用neon,做对比。imx6ull开发板cpu运行在528M,不开启优化等级的时候:
当不使用neon时,运行一次所用时间大约是10.101ms。使用了neon后,运行一次大约是7.544ms。
使用neon分为两种情况:
第一种情况,使用Ne10库。原始代码中fft是自行编写,优化后是使用Ne10库中的fft计算。ne10_fft_c2c_1d_float32(dst, src, cfg, 0);如何使用请看demo。
这种情况运行一次所用时间大约是7.742ms。
第二种情况,使用编译链中提供的neon函数,在第一种情况基础上再次优化。编译链中提供的函数在arm_neon.h里,是内联函数。
使用这些函数计算编译后对应就是使用neon指令进行操作,在汇编文件已经查看:
指令前带v的,应该都是neon指令。说一下函数怎么用。先粘贴正常的代码:
for(i = 0; i < 2304; i++)
{
data= data1[i] + data2[i];
data = Modtopi(data);
data3[i] = data ;
}
变量都是int32类型的。
再粘一下使用arm_neon.h里函数的:
int32x4_add_neon(&data[0], (int32_t *)&data2[0], data3);
static void int32x4_add_neon(int32_t * sum, int32_t* sum2, int32_t* sum3)
{
int32_t sum4[2304];
int32x4_t sum1_v, sum2_v, sum_v;
uint16_t i, j;
for(i = 0; i < 576; i++)
{
sum1_v = vld1q_s32(&sum[4*i]);
sum2_v = vld1q_s32(&sum2[4*i]);
sum_v = vaddq_s32 (sum1_v, sum2_v);
vst1q_s32 (&sum4[4*i], sum_v) ;
Mod2Pi_x(&sum4[4*i], &sum3[4*i]);
}
}
能够看出来,变量全是矢量了,所以分别把数组转换成矢量,然后加法,然后再把矢量转为数组。矢量类型int32x4_t 的含义是4个int32的数(个人理解,不一定对)。然后vld1q_s32转换时一次能转换4个int32数据到int32x4_t 里,所以长度是2304的数据需要转换576次。替换的代码不难,但是确有提升。arm_neon.h里有很多函数,需要慢慢查看。
个人纪录,如有错误,还望指正。
与君共勉