ARM处理器从cortex系列开始集成NEON处理单元,该单元可以简单理解为协处理器,专门为矩阵运算等算法设计,特别适用于图像、视频、音频处理等场景,应用也很广泛。

  本文先对NEON处理单元进行简要介绍,然后介绍如何在内核态下使用NEON,最后列举实例说明。

一.NEON简介

  其实最好的资料就是官方文档,Cortex-A Series Programmer’s Guide ,以下描述摘自该文档

1.1 SIMD

  NEON采用SIMD架构,single instruction multy data,一条指令处理多个数据,NEON中这多个数据可以很多,而且配置灵活(8bit、16bit、32bit为单位,可多个单位数据),这是优势所在。

  如下图,APU需要至少四条指令完成加操作,而NEON只需要1条,考虑到ld和st,节省的指令更多。

  上述特性,使NEON特别适合处理块数据、图像、视频、音频等。

1.2 NEON architecture overview

  NEON也是load/store架构,寄存器为64bit/128bit,可形成向量化数据,配合若干便于向量操作的指令。

1.2.1 commonality with VFP

1.2.2 data type

  

    指令中的数据类型表示,例如VMLAL.S8:

  

  1.2.3 registers 

  32个64bit寄存器,D0~D31;同时可组成16个128 bit寄存器,Q0~Q15。与VFP公用。

  

  寄存器内部的数据单位为8bit、16bit、32bit,可以根据需要灵活配置。

  

  

  NEON的指令有Normal,Long,Wide,Narrow和Saturating variants等几种后缀,是根据操作的源src和dst寄存器的类型确定的。

  

  

1.2.4 instruction set

  

   1.3 NEON 指令分类概述

    指令比较多, 详细可参考Cortex-A Series Programmer’s Guide。可大体分为:

  • NEON general data processing instructions  
  • NEON shift instructions 
  • NEON logical and compare operations 
  • NEON arithmetic instructions
  • NEON multiply instructions 
  • NEON load and store element and structure instructions B.8 NEON and VFP pseudo-instructions

  简单罗列一下各指令

  

    

    无循环左移,负数左移按右移处理。

    load和store指令不太好理解,说明一下。

    

 

1.4 NEON 使用方式

1.4.1 NEON使用方式

  NEON有若干种使用方式:

  •   C语言被编译器自动向量化,需要增加编译选项,且C语言编码时有若干注意事项。这种方式不确定性太大,没啥实用价值
  •   NEON汇编,可行,汇编稍微复杂一点,但是核心算法还是值得的
  •   intrinsics,gcc和armcc等编译器提供了若干与NEON对应的inline函数,可直接在C语言里调用,这些函数反汇编时会直接编程响应的NEON指令。这种方式比较实用与C语言环境,且相对简单。本文后续使用这种方式进行详细说明。

1.4.2  C语言NEON数据类型

    需包含arm_neon.h头文件,该头文件在gcc目录里。都是向量数据。

typedef __builtin_neon_qi int8x8_t    __attribute__ ((__vector_size__ ()));
typedef __builtin_neon_hi int16x4_t __attribute__ ((__vector_size__ ()));
typedef __builtin_neon_si int32x2_t __attribute__ ((__vector_size__ ()));
typedef __builtin_neon_di int64x1_t;
typedef __builtin_neon_sf float32x2_t __attribute__ ((__vector_size__ ()));
typedef __builtin_neon_poly8 poly8x8_t __attribute__ ((__vector_size__ ()));
typedef __builtin_neon_poly16 poly16x4_t __attribute__ ((__vector_size__ ()));
typedef __builtin_neon_uqi uint8x8_t __attribute__ ((__vector_size__ ()));
typedef __builtin_neon_uhi uint16x4_t __attribute__ ((__vector_size__ ()));
typedef __builtin_neon_usi uint32x2_t __attribute__ ((__vector_size__ ()));
typedef __builtin_neon_udi uint64x1_t;
typedef __builtin_neon_qi int8x16_t __attribute__ ((__vector_size__ ()));
typedef __builtin_neon_hi int16x8_t __attribute__ ((__vector_size__ ()));
typedef __builtin_neon_si int32x4_t __attribute__ ((__vector_size__ ()));
typedef __builtin_neon_di int64x2_t __attribute__ ((__vector_size__ ()));
typedef __builtin_neon_sf float32x4_t __attribute__ ((__vector_size__ ()));
typedef __builtin_neon_poly8 poly8x16_t __attribute__ ((__vector_size__ ()));
typedef __builtin_neon_poly16 poly16x8_t __attribute__ ((__vector_size__ ()));
typedef __builtin_neon_uqi uint8x16_t __attribute__ ((__vector_size__ ()));
typedef __builtin_neon_uhi uint16x8_t __attribute__ ((__vector_size__ ()));
typedef __builtin_neon_usi uint32x4_t __attribute__ ((__vector_size__ ()));
typedef __builtin_neon_udi uint64x2_t __attribute__ ((__vector_size__ ())); typedef float float32_t;
typedef __builtin_neon_poly8 poly8_t;
typedef __builtin_neon_poly16 poly16_t; typedef struct int8x8x2_t
{
int8x8_t val[];
} int8x8x2_t; typedef struct int8x16x2_t
{
int8x16_t val[];
} int8x16x2_t; typedef struct int16x4x2_t
{
int16x4_t val[];
} int16x4x2_t; typedef struct int16x8x2_t
{
int16x8_t val[];
} int16x8x2_t; typedef struct int32x2x2_t
{
int32x2_t val[];
} int32x2x2_t; typedef struct int32x4x2_t
{
int32x4_t val[];
} int32x4x2_t; typedef struct int64x1x2_t
{
int64x1_t val[];
} int64x1x2_t; typedef struct int64x2x2_t
{
int64x2_t val[];
} int64x2x2_t; typedef struct uint8x8x2_t
{
uint8x8_t val[];
} uint8x8x2_t; typedef struct uint8x16x2_t
{
uint8x16_t val[];
} uint8x16x2_t; typedef struct uint16x4x2_t
{
uint16x4_t val[];
} uint16x4x2_t; typedef struct uint16x8x2_t
{
uint16x8_t val[];
} uint16x8x2_t; typedef struct uint32x2x2_t
{
uint32x2_t val[];
} uint32x2x2_t; typedef struct uint32x4x2_t
{
uint32x4_t val[];
} uint32x4x2_t; typedef struct uint64x1x2_t
{
uint64x1_t val[];
} uint64x1x2_t; typedef struct uint64x2x2_t
{
uint64x2_t val[];
} uint64x2x2_t; typedef struct float32x2x2_t
{
float32x2_t val[];
} float32x2x2_t; typedef struct float32x4x2_t
{
float32x4_t val[];
} float32x4x2_t; typedef struct poly8x8x2_t
{
poly8x8_t val[];
} poly8x8x2_t; typedef struct poly8x16x2_t
{
poly8x16_t val[];
} poly8x16x2_t; typedef struct poly16x4x2_t
{
poly16x4_t val[];
} poly16x4x2_t; typedef struct poly16x8x2_t
{
poly16x8_t val[];
} poly16x8x2_t; typedef struct int8x8x3_t
{
int8x8_t val[];
} int8x8x3_t; typedef struct int8x16x3_t
{
int8x16_t val[];
} int8x16x3_t; typedef struct int16x4x3_t
{
int16x4_t val[];
} int16x4x3_t; typedef struct int16x8x3_t
{
int16x8_t val[];
} int16x8x3_t; typedef struct int32x2x3_t
{
int32x2_t val[];
} int32x2x3_t; typedef struct int32x4x3_t
{
int32x4_t val[];
} int32x4x3_t; typedef struct int64x1x3_t
{
int64x1_t val[];
} int64x1x3_t; typedef struct int64x2x3_t
{
int64x2_t val[];
} int64x2x3_t; typedef struct uint8x8x3_t
{
uint8x8_t val[];
} uint8x8x3_t; typedef struct uint8x16x3_t
{
uint8x16_t val[];
} uint8x16x3_t; typedef struct uint16x4x3_t
{
uint16x4_t val[];
} uint16x4x3_t; typedef struct uint16x8x3_t
{
uint16x8_t val[];
} uint16x8x3_t; typedef struct uint32x2x3_t
{
uint32x2_t val[];
} uint32x2x3_t; typedef struct uint32x4x3_t
{
uint32x4_t val[];
} uint32x4x3_t; typedef struct uint64x1x3_t
{
uint64x1_t val[];
} uint64x1x3_t; typedef struct uint64x2x3_t
{
uint64x2_t val[];
} uint64x2x3_t; typedef struct float32x2x3_t
{
float32x2_t val[];
} float32x2x3_t; typedef struct float32x4x3_t
{
float32x4_t val[];
} float32x4x3_t; typedef struct poly8x8x3_t
{
poly8x8_t val[];
} poly8x8x3_t; typedef struct poly8x16x3_t
{
poly8x16_t val[];
} poly8x16x3_t; typedef struct poly16x4x3_t
{
poly16x4_t val[];
} poly16x4x3_t; typedef struct poly16x8x3_t
{
poly16x8_t val[];
} poly16x8x3_t; typedef struct int8x8x4_t
{
int8x8_t val[];
} int8x8x4_t; typedef struct int8x16x4_t
{
int8x16_t val[];
} int8x16x4_t; typedef struct int16x4x4_t
{
int16x4_t val[];
} int16x4x4_t; typedef struct int16x8x4_t
{
int16x8_t val[];
} int16x8x4_t; typedef struct int32x2x4_t
{
int32x2_t val[];
} int32x2x4_t; typedef struct int32x4x4_t
{
int32x4_t val[];
} int32x4x4_t; typedef struct int64x1x4_t
{
int64x1_t val[];
} int64x1x4_t; typedef struct int64x2x4_t
{
int64x2_t val[];
} int64x2x4_t; typedef struct uint8x8x4_t
{
uint8x8_t val[];
} uint8x8x4_t; typedef struct uint8x16x4_t
{
uint8x16_t val[];
} uint8x16x4_t; typedef struct uint16x4x4_t
{
uint16x4_t val[];
} uint16x4x4_t; typedef struct uint16x8x4_t
{
uint16x8_t val[];
} uint16x8x4_t; typedef struct uint32x2x4_t
{
uint32x2_t val[];
} uint32x2x4_t; typedef struct uint32x4x4_t
{
uint32x4_t val[];
} uint32x4x4_t; typedef struct uint64x1x4_t
{
uint64x1_t val[];
} uint64x1x4_t; typedef struct uint64x2x4_t
{
uint64x2_t val[];
} uint64x2x4_t; typedef struct float32x2x4_t
{
float32x2_t val[];
} float32x2x4_t; typedef struct float32x4x4_t
{
float32x4_t val[];
} float32x4x4_t; typedef struct poly8x8x4_t
{
poly8x8_t val[];
} poly8x8x4_t; typedef struct poly8x16x4_t
{
poly8x16_t val[];
} poly8x16x4_t; typedef struct poly16x4x4_t
{
poly16x4_t val[];
} poly16x4x4_t; typedef struct poly16x8x4_t
{
poly16x8_t val[];
} poly16x8x4_t;

  1.4.3  gcc的NEON函数

  跟NEON指令对应,详见gcc手册。

二.内核状态下使用NEON的规则

  在linux里,应用态可以比较方便使用NEON instrinsic,增加头arm_neon.h头文件后直接使用。但是内核态下使用NEON有较多限制,在linux内核文档  /Documentation/arm/kernel_mode_neon.txt对此有详细说明。要点为:

  

   还有一点特别关键:

  

  CC [M]  /work/platform-zynq/drivers/zynq_fpga_driver/mmi_neon/lcd_hw_fs8812_neon.o
In file included from /home/liuwanpeng/lin/lib/gcc/arm-xilinx-linux-gnueabi/4.8./include/arm_neon.h::,
from /work/platform-zynq/drivers/zynq_fpga_driver/mmi_neon/lcd_hw_fs8812_neon.c::
/home/liuwanpeng/lin/lib/gcc/arm-xilinx-linux-gnueabi/4.8./include/stdint.h::: error: no include path in which to search for stdint.h
# include_next <stdint.h>
没有使用-ffreestanding编译选项时,在内核态下使用出现此编译错误。

三.实例

  NEON一般在图像等领域,最小处理单位就是8bit,而不是1bit,这方便的例子非常多,本文就不说明了。在实际项目中,我需要对液晶的一组数据按位操作,变换,形成新的数据,如果用传统ARM指令,掩码、移位、循环,想想效率就非常低。于是决定使用NEON的位相关指令完成上述任务。

 3.1 任务说明

  如下图,需要对各个bit进行转换,组成新的数据。

 

3.2 算法说明

 使用vmsk、vshl、vadd等位操作完成。

3.3 kernel配置

  必须配置内核支持NEON,否则kernel_neon_begin()和kernel_neon_end()等函数不会编辑进去。

  make menuconfig:Floating point emulation,如下图。

  

未使能“Support for NEON in kernel mode”时会报错:
mmi_module_amp: Unknown symbol kernel_neon_begin (err )
mmi_module_amp: Unknown symbol kernel_neon_end (err )

3.4 模块代码

 由于NEON代码需要单独设置编译选项,所以单独建立了一个内核模块,makefile如下:

CFLAGS_MODULE += -O3 -mfpu=neon -mfloat-abi=softfp -ffreestanding 

 核心代码:

#include <linux/module.h>
#include <linux/printk.h>
#include <arm_neon.h>
  // 来自GCC的头文件,必须用-ffreestanding编译选徐昂

  #define LCD_8812_ROW_BYTES 16
  #define LCD_8812_PAGE_ROWS 8

  #define LCD_PAGE_BYTES (LCD_8812_ROW_BYTES*LCD_8812_PAGE_ROWS)

int fs8812_cvt_buf( uint8 * dst, uint8 * src )
{ uint8x16_t V_src[];
uint8x16_t V_tmp[];
uint8x16_t V_dst[];
uint8x16_t V_msk;
int8x16_t V_shift;
int8 RSHL_bits[] = {,,,,,,,};
int8 row,bit;
uint8 page;
uint8 * fb_page_x = NULL; // convert the frame_buf for fs8812
for( page=;page<;page++ ){
fb_page_x = src + page*LCD_PAGE_BYTES;
for( row=;row<LCD_8812_PAGE_ROWS;row++ )
V_src[row] = vld1q_u8( fb_page_x + row*LCD_8812_ROW_BYTES );
       for( bit=;bit<;bit++){
  V_msk = vdupq_n_u8(<<bit);
  for( row=;row<LCD_8812_PAGE_ROWS;row++){
  V_tmp[row] = vandq_u8(V_src[row],V_msk); // only process the desire bit
  V_shift = vdupq_n_s8( RSHL_bits[row]-bit );
  V_tmp[row] = vshlq_u8( V_tmp[row],V_shift );
  }
  V_dst[bit] = vorrq_u8(V_tmp[],V_tmp[]); // all bit_x convert to one row
  V_dst[bit] |= vorrq_u8(V_tmp[],V_tmp[]);
  V_dst[bit] |= vorrq_u8(V_tmp[],V_tmp[]);
  V_dst[bit] |= vorrq_u8(V_tmp[],V_tmp[]);
  }
  // store to ram
  fb_page_x = dst + page*LCD_PAGE_BYTES;
  for( row=;row<LCD_8812_PAGE_ROWS;row++ ){
   vst1q_u8(fb_page_x,V_dst[row]);
  fb_page_x += LCD_8812_ROW_BYTES;
  }
}
  return ;
} EXPORT_SYMBOL_GPL(fs8812_cvt_buf);

调用模块,务必没有“-mfpu=neon -mfloat-abi=softfp ”选项

    // convert the frame_buf for fs8812
kernel_neon_begin();
fs8812_cvt_buf( g_tmp_buf, frame_buf );
kernel_neon_end();

  

  

linux kernel态下使用NEON对算法进行加速的更多相关文章

  1. linux应用态下的时间

    1.时间值 1.1 日历时间(UTC) 该值是自1 9 7 0年1月1日0 0 : 0 0 : 0 0以来国际标准时间( U T C)所经过的秒数累计值(早期的手册称 U T C为格林尼治标准时间) ...

  2. Linux kernel 4.9及以上开启TCP BBR拥塞算法

    Linux kernel 4.9及以上开启TCP BBR拥塞算法 BBR 目的是要尽量跑满带宽, 并且尽量不要有排队的情况, 效果并不比速锐差 Linux kernel 4.9+ 已支持 tcp_bb ...

  3. Linux kernel 2.6下的modules编译与KBuild

    转载:http://blog.sina.com.cn/s/blog_602f87700100dq1u.html Sam之前在Linux kernel 2.4下写过一些driver.但自从转到kerne ...

  4. Linux Kernel中所應用的數據結構及演算法

    Linux Kernel中所應用的數據結構及演算法 Basic Data Structures and Algorithms in the Linux kernel Links are to the  ...

  5. Linux Kernel之flush_cache_all在ARM平台下是如何实现的【转】

    转自:http://blog.csdn.net/u011461299/article/details/10199989 版权声明:本文为博主原创文章,未经博主允许不得转载. 在驱动程序的设计中,我们可 ...

  6. Ubantu下编译Linux Kernel

    wget https://www.kernel.org/pub/linux/kernel/v3.x/linux-3.9.3.tar.gztar -xzf linux-3.9.3.tar.gzcd li ...

  7. Linux Kernel(Android) 加密算法汇总(四)-应用程序调用OpenSSL加密演算法

    Linux Kernel(Android) 加密算法总结(三)-应用程序调用内核加密算法接口 讲到了怎样调用内核中的接口的方法. 本节主要是介绍怎样Android C/C++应用程序调用Openssl ...

  8. Linux kernel pwn notes(内核漏洞利用学习)

    前言 对这段时间学习的 linux 内核中的一些简单的利用技术做一个记录,如有差错,请见谅. 相关的文件 https://gitee.com/hac425/kernel_ctf 相关引用已在文中进行了 ...

  9. linux 用户态和内核态以及进程上下文、中断上下文 内核空间用户空间理解

    1.特权级         Intel x86架构的cpu一共有0-4四个特权级,0级最高,3级最低,ARM架构也有不同的特权级,硬件上在执行每条指令时都会对指令所具有的特权级做相应的检查.硬件已经提 ...

随机推荐

  1. 基于FPGA的肤色识别算法实现

    大家好,给大家介绍一下,这是基于FPGA的肤色识别算法实现. 我们今天这篇文章有两个内容一是实现基于FPGA的彩色图片转灰度实现,然后在这个基础上实现基于FPGA的肤色检测算法实现. 将彩色图像转化为 ...

  2. Echarts数据可视化visualMap,开发全解+完美注释

    全栈工程师开发手册 (作者:栾鹏) Echarts数据可视化开发代码注释全解 Echarts数据可视化开发参数配置全解 6大公共组件详解(点击进入): title详解. tooltip详解.toolb ...

  3. cocos2dx - 伤害实现

    接上一节内容:cocos2dx - 生成怪物及AI 本节主要讲如何通过创建简单的矩形区域来造成伤害 在小游戏中简单的碰撞需求应用box2d等引擎会显得过于臃肿复杂,且功能不是根据需求定制,还要封装,为 ...

  4. PHP windowns安装扩展包

    1.  php_msgpack.dll php.ini 添加  extension=php_msgpack.dll 下载dll: http://pecl.php.net/package/msgpack ...

  5. 深入浅出Diffie–Hellman

    一.作者 这个密钥交换方法,由惠特菲尔德·迪菲(Bailey Whitfield Diffie).马丁·赫尔曼(Martin Edward Hellman)于1976年发表. 二.说明 它是一种安全协 ...

  6. 声明数组变量/// 计算所有元素的总和/打印所有元素总和/输出/foreach循环/数组作为函数的参数/调用printArray方法打印

    实例 下面是这两种语法的代码示例: double[] myList; // 首选的方法 或 double myList[]; // 效果相同,但不是首选方法 创建数组 Java语言使用new操作符来创 ...

  7. eclipse环境下,java操作MySQL的简单演示

    首先先通过power shell 进入MySQL 查看现在数据库的状态(博主是win10系统) 右键开始,选择Windows powershell ,输入MySQL -u用户名 -p密码 选择数据库( ...

  8. JavaScript责任链模式

    介绍 责任链模式(Chain of responsibility)是使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系.将对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理 ...

  9. winform中执行任务,解决未响应界面

    private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)        {            var coun ...

  10. 如何内网搭建NuGet服务器

    NuGet 是.NET程序员熟知的给.NET项目自动配置安装library的工具,它可以直接安装开源社区中的各个公用组件,可以说是非常方便.不过,有些时候,公司内部的公用的基础类库,各个项目都有引用, ...