×
嵌入式 > 嵌入式开发 > 详情

ARM处理器NEON编程及优化技巧—左移右移等移位操作

发布时间:2020-08-24 发布时间:
|
ARM的NEON协处理器技术是一个64/128-bit的混合SIMD架构,用于加速包括视频编码解码、音频解码编码、3D图像、语音和图像等多媒体和信号处理应用。本文主要介绍如何使用NEON的汇编程序来写SIMD的代码,包括如何开始NEON的开发,如何高效的利用NEON。首先会关注内存操作,即如何变更指令来灵活有效的加载和存储数据。接下来是由于SIMD指令的应用而导致剩下的若干个单元的处理,然后是用一个矩阵乘法的例子来说明用NEON来进行SIMD优化,最后关注如何用NEON来优化各种各样的移位操作,左移或者右移以及双向移位等。本节详述NEON提供的各种各样的移位操作,左右移位,移位插入以及移位限定符如饱和舍入等以及这些移位操作如何能有效的处理图像中的色深。

移位向量

NEON中的移位指令和ARM指令中的标量移位,把向量中的各个元素左移或者右移若干比特。那些移到临近元素的比特会被丢弃掉,不会影响到邻近元素的移位结果。移位操作的移位数可以直接编码到指令里,或者用 一个指定的移位比特向量,如果使用移位向量,每一个元素的移位比特值将取决于对应的移位向量里存储的值,移位向量里保存的移位值是有符号的,所以可能进行左移,右移或者不移位的操作。

图1. 带移位向量的左移操作,可以左移、右移或者不移位

有符号数据的右移操作的类型可以根据指令来制定,如是否进行符号扩展(算术右移还是逻辑右移),这对应于ARM指令里的移位操作。对于无符号的右移而言,就不用进行符号扩展了。

移位并右侧插入

NEON还支持带插入的移位,即进行两个向量的比特位域的组合。比如VLSI指令左移并插入,会把向量进行左移,然后用 目标向量的右侧数据来填充。如下图所示:

arm.com/index.php?app=core&module=attach&section=attach&attach_rel_module=blogentry&attach_id=511" rel="nofollow" >

图2. 向量移位并填充结果向量

移位并累加

NEON支持把向量的各个元素右移然后累加结果到另外一个向量。这对于那些中间结果需要更高精度的情况非常适用,然后才把 结果保存到一个低精度的累加器里。

指令修改符

每个移位指令都能包含一个或者多个修改符,这些修改符不会改变移位操作本身,但是输入和输出结果会去除基准或者饱和到一个有效范围,有5种移位限定符:

  • 舍入(round),使用R前缀,用于纠正右移截断导致的基准;
  • 变窄(narrow),使用N前缀,表示向量中所有元素的位宽变窄为一半,即源是128-bit的Q寄存器,而结果是64-bit的D寄存器;
  • 变长(long),使用L前缀,表示向量中所有元素的位宽变宽为两倍,即源是64-bit的D寄存器,而结果是128-bit的Q寄存器;
  • 饱和(saturate),使用Q前缀,把结果元素变成其能表示的最大和最小值范围内,位宽比特数和符号类型来表明该元素的有效范围;
  • 无符号的饱和(Unsigned Saturating),使用Q前缀,U后缀,类似于饱和限定符,但结果会饱和到无符号数据范围,不管输入是有符号还是无符号的;

这些限定符的有些组合起来不能描述有用的操作,因而NEON并不包含这些指令。比如类似VQSHR的饱和右移并不需要,因为右移会让数据变小,不会超过有效范围。

有效的移位操作表格

所有NEON支持的移位指令如下表所示,他们按照上面提到的限定符排列。

图3. NEON支持的移位操作,用立即数表示的移位数和用寄存器表示的

色深转换的例子

色深转换是图像处理中经常用到的。通常输入数据是RGB565 16-bit色度格式,需要转换成RGB888格式才更适合于NEON这种并行处理。 然而NEON还是能处理RGB565的数据的,这就需要用到前面提到的移位指令了。

图4. RGB888和RGB565的色度格式

从RGB565到RGB888

首先看如何从RGB565转换成RGB888,假设输入的8个16-bit的像素保存到寄存器Q0,我们想把分量分离成R通道,G通道和B通道,保存到d2到d4寄存器。

vshr.u8      q1, q0, #3     @ 把R通道右移3比特,丢弃G通道比特 

vshrn.i16    d2, q1, #5     @ 右移并变窄,取得R分量数据到d2寄存器 

vshrn.i16    d3, q0, #5     @ 右移并变窄取得G分量数据 

vshl.i8      d3, d3, #2     @ 左移G分量2个比特,丢弃R分量部分,同时把G分量保存到正确的位置; 

vshl.i16 q0, q0, #3     @ 把B分量左移到最重要的8-bit数据 

vmovn.i16    d4, q0         @ 丢地仍然有的R和G分量,保存B分量为8-bit 

这些指令的含义可以参考注释处,基本上完成的操作就是去除临近通道的不用的色度数据,然后继续移位把色度分量的值到最高位。

一个小问题

你可能注意到,这样转换成RGB888格式后,原来的白就不是完全的白色了,这是因为R和B分量是左移3bit,而G分量则只左移两bit,因而如RGB565值(0x1F, 0x3F, 0x1F)变成RGB888 (0xF8, 0xFC, 0xF8),并不跟以前的表示颜色一致。

从RGB888到RGB565

从RGB888转换成RGB565,假设RGB888的输入是用上面代码表示的形式,单独通道的分量保存在从寄存器d0到d2,结果保存到16-bit的RGB565格式到q2寄存器。

vshll.u8 q2, d0, #8     @ 左移红色分量到16bit结果中的最重要的5bit 

vshll.u8 q3, d1, #8     @ 左移绿色分量数据到16bit最重要的8比特 

vsri.16      q2, q3, #5    @ 移位绿色分量,并插入到红色元素寄存器里。 

vshll.u8 q3, d2, #8     @ 左移红色分量到16bit结果中的最重要的8-bit 

vsri.16      q2, q3, #11   @ 把蓝色分量插入到红色和绿色分量后面 

基本操作是把分量扩展成16-bit,然后右移插入指令把分量放到合适的位置;

总结

NEON提供了功能强大的移位指令,能完成:

  • 快速的把数据乘以或者除以2,并带有舍入和饱和操作;
  • 移位并拷贝若干比特从一个 向量到另一个向量;
  • 中间计算结果在高精度,而把结果累加到低精度。


『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
USB接口定义图