HiFi 5 概要
- Cadence HiFi :Cadence Tensilica HiFi DSP 架构,用于音频解码
- Tensilica:嵌入式处理器公司,已被芯片 EDA 巨头 Cadence 收购
- Xtensa:DSP 处理器核心,也是该系列的型号名称,也是该处理器中指令集架构的名称
- HiFi:特指 32 位音频/语音处理数字信号处理 (DSP) 核,基于 Xtensa® 架构
1.1 HiFi 5 DSP Features
HiFi 5 DSP 是single-instruction/multiple-data(SIMD)处理器,最多可并行处理 4 个 32-bit数据项或 8 个 16-bit数据元素。
HiFi 5 DSP采用 VLIW 架构,支持并行执行五个操作。
HiFi 5 DSP包括32个 64-bit AE_DR 寄存器和16个 32-bit AR 寄存器。每个 AE_DR 寄存器通常包含两个32-bit操作数或四个16-bit操作数或八个8-bit操作数。操作数可为整数、定点或浮点数值。
- Slot 0 操作包括标量操作、向量和标量读取、写入、比特流和哈夫曼操作。此外,0 号槽还提供一些 ALU、选择和移位操作。
- Slot 1 的操作与Slot 0 的操作类似,但数量较少。此外,Slot 1 不允许任何写入操作。
- Slot 2 和Slot 3 包含向量 MAC、ALU 和可选浮点运算。Slot 2 还包含神经网络扩展软件包中使用的特殊 MAC 运算。
- Slot 4 有一组有限的操作,可用于选择 16 位和 32 位内核。
1.1.1 AE_DR寄存器
HiFi 5 DSP 的一个32-entry,64-bit的寄存器,AE_DR。寄存器能够支持:
- 1或2个的24-bit或32-bit操作数
- 1或4个16-bit的操作数
- 1或8个8-bit的操作数
- 1个56-bit或64-bit的操作数
如下图所示,寄存器的单独一半或四分之一区域是单独的数据项。
当寄存器与内存进行操作时,寄存器的高半区(H)总是被写入到内存的低地址或从内存的低地址被读取。
访问 AE_DR 寄存器中单个 24-bit或 32-bit元素的操作,参阅助记符中带有选择器 L 和 H 的元素。
访问 16-bit单个寄存器元素的操作,参阅助记符中带有 3、2、1 和 0 扇区的元素。
访问单个 8-bit元素的操作,参阅助记符中带有 7、6、5、4、3、2、1 和 0 扇区的元素。
1.1.2 AE_EP寄存器
HiFi 5 DSP支持一个4-entry,8-bit额外精度寄存器,AE_EP。这个寄存器与一些乘法、加法、移位和饱和指令一起使用,以提供总共72-bit的精度。
1.1.3 AE_VALIGN寄存器
HiFi 5 DSP支持一个4-entry,128-bit对齐寄存器,AE_VALIGN。该寄存器可用 64-bit模式(ae_valign)或 128-bit模式(ae_valignx2)。在 128-bit模式下使用该寄存器,硬件可以每周期 128-bit的速率读取或写入未 128-bit对齐的 SIMD 数据流。在 64-bit模式下使用该寄存器,硬件可以以每周期 64-bit的速率读取或写入未 64-bit对齐的 SIMD 数据流。
对齐读取在Slot 0和Slot 1上,对齐写入仅在Slot 0上。
补充1:字节对齐
Ref: C语言字节对齐详解
1 字节 = 8 位
char: 1字节, short: 2字节, int: 4字节, long: 4字节, long long: 8字节, pointer: 8字节
基本概念
许多计算机系统对基本数据类型合法地址做出了一些限制,要求某种类型对象的地址必须是某个值K的倍数(K通常是2,4或8)。对齐跟数据在内存中的位置有关。如果一个变量的内存地址正好位于它**长度的整数倍**,他就被称做**自然对齐**。比如在32位cpu下,假设一个整型变量(4字节)的地址为0x00000004,那它就是自然对齐的。
为什么要进行字节对齐
需要字节对齐的原因在于CPU访问数据的效率问题。例如,**假设一个处理器总是从存储器中取出8字节,则地址必须为8的倍数**。如果我们能保证将所有的double类型数据(8字节)的地址对齐成8的倍数,那么就可以用一个存储器操作来读写值了。否则我们可能需要执行两次存储器访问,因为对象可能被分放在两个8字节存储块中。假设一个整型变量(4字节)的地址不是自然对齐,比如为0x00000002,则CPU如果取它的值的话需要访问两次内存,第一次取从0x00000002-0x00000003的一个short,第二次取从0x00000004-0x00000005的一个short然后组合得到所要的数据;如果变量在0x00000003地址上的话则要访问三次内存,第一次为char,第二次为short,第三次为char,然后组合得到整型数据。而如果变量在自然对齐位置上,则只要一次就可以取出数据。
如何进行字节对齐
编译器对齐原则:
- 数据类型自身的对齐值:为指定平台上基本类型的长度。对于char型数据,其自身对齐值为1,对于short型为2,对于int, float, double类型,其自身对齐值为4,单位字节。
- 结构体或者类的自身对齐值:其成员中自身对齐值最大的那个值。
- 指定对齐值:#pragma pack (value)时的指定对齐值value。
- 数据成员、结构体和类的有效对齐值:自身对齐值和指定对齐值中小的那个值。
结构体字节对齐
当数据类型为结构体时,编译器可能需要在结构体字段的分配中插入间隙,以保证每个结构元素都满足它的对齐要求。第一个数据变量的起始地址就是数据结构的起始地址。结构体的成员变量要对齐排放(对于非对齐成员需要在其前面填充一些字节,保证其在对齐位置上),结构体本身也要根据自身的有效对齐值圆整(就是结构体总长度需要是结构体有效对齐值的整数倍),此时可能需要在结构末尾填充一些空间,以满足结构体整体的对齐—-向结构体元素中最大的元素对齐。 对结构体进行字节对齐,有四个值:
- 指定对齐值:代码中指定的对齐值,记为packLen;
- 默认对齐值:结构体中每个数据成员及结构体本身都有默认对齐值,记为defaultLen;
- 成员偏移量:即相对于结构体起始位置的长度,记为offset;
以及两个规则:
- 成员长度:结构体中每个数据成员的长度(注结构体成员为补齐之后的长度),记为memberLen. 对齐规则:
offset % vaildLen = 0,其中vaildLen为有效对齐值vaildLen = min(packLen, defaultLen)- 填充规则: 如成员变量不遵守对齐规则,则需要对其补齐;在其前面填充一些字节保证该成员对齐。需填充的字节数记为pad。
适当地编排结构体成员地顺序,可以在保存相同信息地情况下尽可能节约内存空间。
什么时候进行字节对齐
在设计不同CPU下的通信协议时,或者编写硬件驱动程序时寄存器的结构这两个地方都需要按一字节对齐。即使看起来本来就自然对齐的也要使其对齐,以免不同的编译器生成的代码不一样。
1.2 操作助记符号
见 HiFi 5 DSP User's Guide Table 9.
1.3 定点数及定点算法
1.3.1 定点值表示
- Q31、Q15和Q7使用一个定点数来表示分数部分的位
- 如果 m=1 可以简写为 Qn,比如 Q1.15 可以简写为 Q15
一个定点数的数据类型 m.n 包含一个符号位,在小数点的左边有m-1位,在小数点的右边有n位。当表示为一个二进制值并保存在register file的时候,n位小数部分不重要,最重要的m位是以带符号位2的补码形式表示的整数部分。如果二进制值被解释为 2 的补码有符号整数,那么将二进制值转换为定点数需要将该 2 的补码有符号整数除以 。
Example1:
将数0.5以24-bit的fixed point data type=1.23形式为例,其被表示为定点化的
0x040 0000
计算由来
十进制数419+4304以十六进制表示为:0x040 0000
Example2:
将十进制数-1.5以64-bit的fixed point data type 17.47形式为例,其被表示为定点化的(-2 + 0.5 = 0xff 4000 0000 0000)
十进制-2 = 2的16-bit补码 1 0000 0000 0000 0010b(原码)= 1 1111 1111 1111 1101b(反码)= 1 1111 1111 1111 1110b(补码)= 0x1FFFE
十进制0.5 = = 70368744177664 = 0x4000 0000 0000
-2 + 0.5 = 1 0000 0000 0000 0010b(原码)+ 100 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000b(原码)
= 1 1111 1111 1111 1110b(补码)+ 100 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000b(补码)
= 1111 1111 1111 1111 0100 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000b
= 0xFFFF 4000 0000 0000
补充2:定点化基础知识
- 以一个32位的定点数为例,假设转换因子为Q,即32位中小数的位数为Q,整数位数则为31-Q(有符号数的情况),则定点数与浮点数的换算关系为:
例如浮点数-2.0转换成Q30的定点数:。由此可得32位有符号数的表示范围是:(-2147483648 ~ 2147483647)。如果我们把有符号定点数的最大值2147483647转换为Q为30对应的浮点数,则结果为
- 负数的补码是在其原码的基础上,符号位不变,其余各位取反,最后+1.(即在反码的基础上+1)
Ref1: DSP芯片的定点运算
Ref2: 原码、反码和补码
HiFi 5 DSP 小数指令使用 1.15、1.23、9.23、1.31、17.47 和 1.63 的小数运算。额外精度 72 位累加器仅支持整数运算。
1.15: 16-bit fixed point data type with 1 sign bit and 15 bits to the right of the decimal. The largest positive value 0x7fff is interpreted as . The smallest negative value 0x8000 is interpreted as -1.0. The value 0 is interpreted as 0.0.
最大正值
- 也可以这样理解:, 所以16位有符号数的表示范围是(-32,768 ~ 32,767),将有符号定点数最大值转换成浮点数有:
- , in Hex: 0x7FFF
最小负值
- [32768 in Hex: 0x8000]
1.23: 24-bit fixed point data type with 1 sign bit and 23 bits to the right of the decimal. The largest positive value 0x7fffff is interpreted as . The smallest negative value 0x800000 is interpreted as -1.0. The value 0 is interpreted as 0.0. Since register halves hold 32 bits, not 24 bits, typical 24-bit fractional variables are 9.23. However, 24-bit fixed point multiply instructions ignore the upper 8 bits, thereby treating them as 1.23.
最大正值
- , 表示范围为(-8,388,608 ~ 8,388,607)
- 8,388,607 in Hex: 0x7F FFFF
最小负值
- ,
- [8,388,608 in Hex: 0x80 0000]
9.23: 32-bit fixed point data type with a 9-bit signed integer and 23 bits to the right of the decimal. The largest positive value 0x7fffffff is interpreted as . The smallest negative value 0x80000000 is interpreted as -256.0. The value 0 is interpreted as 0.0.
最大正值
- , 表示范围为(-2,147,483,648 ~ 2,147,483,647)
- 2,147,483,647 in Hex=0x7FFF FFFF
最小负值
- [2,147,483,648 in Hex: 0x8000 0000]
1.3.2 定点数计算法则
有符号定点数运算 m.n0 * m.n1,其结果将为一个m.n型数据。其中n = n0 + n1, m = m0 + m1。例如将一个1.32型变量乘以1.32型变量将得到一个2.64型变量。HiFi 5 DSP同时支持1.63和17.47的数据类型。定点乘指令是将2.62的结果左移1位来生成1.63数据类型的,因而使结果饱和。乘积指令生成17.47位数据则是通过舍入底部的15位。
补充x: 数据的溢出、饱和与截断
溢出: 指给某个变量赋值时,超出了该类型变量所能表达的范围。unsigned char a; a = 260; // a的取值是(0 ~ 255), 此时数据溢出, a返回错误的值饱和: 指给某个变量赋值时,超出了该类型能表达的范围,如果采取饱和策略,就会让该变量等于一个最大值或者最小值。
unsigned char a; a = 260; // a溢出, 采取饱和策略则a的最终值为255截断: 指给某个变量赋值时,超出了该类型能表达的范围,如果采取截断策略,则变量只保留数据中低字节的数据,高字节的数据则会丢弃。
unsigned char a; a = 260; // a溢出, 采取截断策略, 260 = 0x0104, 保留低字节数据则a的最终值为4
1.3.3 高级语言: 从浮点到定点
- 加减运算: 浮点加/减法转化为定点加/减法时必须保证两个操作数的定标值一样。若两者不一样,则在加法/减法运算前先进行小数点的调整。需使Q值小的数调整为与另一个数的Q值一样大。如果加减法的结果超出16位(Q16)的表示范围,则必须保留32位结果,以保证运算的精度。
- 乘法运算: x的定标值为Qx, y的定标值为Qy,乘积z的定标值为Qz,
z = (temp y) >> (Qx+Qy-Qz)
- 除法运算: 被除数x的定标值为Qx, 除数y的定标值为Qy, 商z的定标值为Qz,
z = (temp << (Qz-Qx+Qy)) y
1.4 VLIW槽和格式
HiFi 5 DSP可以使用VLIW在一个128位的指令束中发出5个运算指令,或在一个64位的指令束中发出2个运算指令。HiFi 5 DSP支持几种不同的格式伴随不同的槽限制。每条指令都属于一种格式,但不同的格式可以在一条指令中打包不同数量的运算指令。
- 第一个槽支持标量与矢量的 读取与写入,以及 标量 Xtensa 操作
- 第二个槽支持标量与矢量的 读取,以及 标量 Xtensa 操作
- 最后三个槽主要支持 矢量逻辑运算 与 向量乘法运算
在进行HiFi 5 DSP优化代码时,了解slotting非常重要。通常情况下,一个循环受限于只能在一个或另一个槽中进行的操作。例如,每个cycle内不可能发出一个以上(可能的 SIMD)写入操作。如果一个循环受限于一个槽中的操作,就没有必要尝试优化另一个槽中的操作。
DSP编程
HiFi 5 DSP 提供了几种 C 语言数据类型,便于使用 C 和 C++ 内在指令和操作符重载在 DSP 上进行编程。
HiFi 5 DSP每个周期可以提供8个32位MAC或16个16位MAC。通过可选的神经网络扩展,HiFi 5还可提供每周期32个MAC的8x16位和8位支持。它对整数和分数算术提供同等支持。
C和C++语言支持32x32位或16x16位数据的整数运算。
因此,虽然标准应用程序可以有效利用 HiFi 5 DSP的资源,但应用程序如果需要进行小数运算、24位或者32x16位乘法运算时,必须进行修改才能表达这些语义。这些修改可以非常简单,只需声明相应自定义数据类型的变量,并依靠内置运算符重载,或使用显式本征来表达所需的精确运算。
HiFi 5 DSP 也是 2/4/8 路 SIMD(单指令/多数据)架构。不利用 SIMD 的应用程序的运行速度将比利用 SIMD 的应用程序慢八倍。
2.1 数据类型
下面的示例演示了如何使用 HiFi 5 DSP 在C语言中高效进行load、store和在各种数据类型之间进行convert。一般来说, 寄存器(局部)变量 与 内存中的数据(数组、结构字段等) 之间的转换应通过指针类型来转换,而 寄存器变量 之间的转换则应直接使用相应的 HiFi 5 DSP内部转换。
- 获取一个32-bit的数,将其复制为两个32-bit在AE_DR中
int mem32 = 123;
ae_int32x2 p = mem32;
- 加载2个32-bit的值在AE_DR中
&mem32 [i]必须是64-bit对齐
int *mem32 = ...;
ae_int32x2 p = *((ae_int32x2 *) &mem32[i]);
- 将 AR 中的两个32-bit数值移至AE_DR中的两个32-bit element上
int ah = ...;
int al = ...;
ae_int32x2 p = AE_MOVDA32X2(ah, al);
- 饱和并截断AE_DR中的两个9.55-bit值到AE_DR中的两个1.31-bit fraction elements
ae_int64 qh = ...;
ae_int64 ql = ...;
ae_int32x2 p = AE_TRUNCI32X2F64S(qh, ql, 8);
改变类型
- 指令来自
AE_MOV<dest_type>_FROM<SRC_TYPE>。下面的示例展示了如何将ae_f64变量强制转换为ae_int24x2
ae_f64 = ...;
ae_int24x2 p = AE_MOVINT24X2_FROMF64(d);
2.2 编程风格
通常代码可以被有效的运行在任意定点化DSP上。如果参考代码是浮点,除非使用了可选的浮点单元,否则必须将其转换为定点。参考代码通常是用基础定点内在库来编写的。
如果一个库的主体被移植到使用HiFi 5 DSP内部组件,但接口仍然是标准的C/C++,那么实现必须转换到HiFi 5 DSP数据类型。
Cadence 提供了语音编解码器中常用的 ITU-T/ETSI 的优化实现。该接口使用标准 C/C++ 数据类型,但其实现经过精心设计,允许编译器消除转换。
最常见的情况是,应用中重要功能直接针对 HiFi 5 DSP 进行优化,而不太重要的功能则使用原始库。
根据应用需要,有几种基本的编程方式可供选择,手动程度依次递增。它们是:
- Auto-vectorizing standard scalar C/C++ code
- Auto-vectorization code written on top of the ITU-T/ETSI intrinsics
- C/C++ code with HiFi 5 DSP data types and operator overloading
- Use of intrinsic functions for computation instruction along with HiFi 5 DSP data types and implicit loads and stores
- Use of intrinsic functions for both computation and loads and stores
对于这些方式,每个都能编写标量或者向量代码。一个通常的策略是一次移植单个函数,如果所需的语句符合标准C/C++ Code或者ITU-T/ETSI intrinsics,则从它开始automatic vectorization。
2.3 标准C/C++的Auto-Vectorization
标量C代码的Auto-vectorization可以在简单的loop上提供有效的结果,但也有限制。它的改进可以通过使用编译器pragmas和options,也能通过数据编组使数据的读/写规范与对齐,从而实现。 Xtensa C/C++编译器提供了几个选项和分析方法来帮助向量化。详细介绍在Xtensa C and C++ Application Programmer's Guide的SIMD Vectorization章节。以下为一些总结形式的指南:
2.4 ITU-T/ETSI Intrinsics
要使用ITU-T/ETSI内部文件,包含以下一个或两个头文件:
#include <hifi2/basic_op_xtensa.h
#include <hifi2/oper_32b_xtensa.h
标准的intrinsics可以使用或不使用Auto-vectorization,就像标准的C/C++代码一样。
Example1: A simple example that performs a 16-bit energy calculation:
int Energy (short a[], int n)
{
int i;
int s = 0;
for (i = 0; i < n; i++)
{
s = s + a[i]*a[i];
}
return s;
}
Example2: using the intrinsics:
int Energy (short a[], int n)
{
int i;
int s = 0;
for (i = 0; i < n; i++)
{
s = L_mac(s, a[i], a[i]);
}
return s;
}
2.5 操作符重载
Example3: A simple example that performs a energy calculation where the input data is stored in memory as a 1.31 fixed point value:
int Energy (short a[], int n)
{
int i;
int s = 0;
for (i = 0; i < n; i++)
{
s += ((long long) a[i]*a[i]) >> 31;
}
return s;
}
Example4: 转换成HiFi 5 DSP 编码。
int Energy (short a[], int n)
{
int i;
ae_f32 *ap = (ae_f32 *)a;
ae_f32 s = 0;
for (i = 0; i < n; i++)
{
s += ap[i]*ap[i];
}
return s;
}
主循环使用操作符重载来执行一个32位的定点乘法。累加器的类型为ae_f32。将结果赋值给int并不会改变位模式。因此,这个例程返回一个作为int存储的1.31值。
我们无法在每个累加器周期内发出多于1个的乘积累加操作,但我们可以改变代码来使用2个部分和:
Example5: 每个周期做两次读取和两次乘积累加。
int Energy (short a[], int n)
{
int i;
ae_f32 *ap = (ae_f32 *)a;
ae_f32 s = 0;
ae_f32 s2 = 0;
for (i = 0; i < n; i+=2)
{
s += ap[i]*ap[i];
s2 += ap[i+1]*ap[i+1];
}
return s + s2;
}
将ae_f32更改为ae_f32x2,循环次数减少一半,就可以将Example5更改成一个2-way SIMD例程。主循环将并行计算四个部分和。在循环之后,我们必须使用加法和intrinsic中的指令AE_ADD32_HL_LH将四个部分和相加为一个和。
Example6: SIMD指令
int Energy (short a[], int n)
{
int i;
ae_f32x2 *ap = (ae_f32x2 *)a;
ae_f32x2 s = 0;
ae_f32x2 s2 = 0;
for (i = 0; i < n>>1; i+=2)
{
s += ap[i]*ap[i];
s2 += ap[i+1]*ap[i+1];
}
s = s + s2;
return AE_ADD32_HL_LH(s, s);
}
生成的代码现在可以在每个周期中执行四个乘法。
现在考虑一下我们希望进行 32x16-bit乘法运算而不是 32x32-bit乘法运算的情况。
能量计算只有一个输入操作数,而 32x16-bit乘法需要两个输入操作数。 因此,我们将energy calculation example转换为点乘。由于一个寄存器可容纳 4 个 16-bit元素,因此我们将矢量化为 4,而不是 2。32-bit操作数的元素个数必须与 16-bit操作数的元素个数相同。
因此,HiFi 5 DSP 定义了 ae_f32x4(和 ae_int32x4)数据类型。
这些结构数据类型占用两个寄存器。在这些类型上定义的大多数操作都需要2条指令,因此它们并不比2-way SIMD 类型更快。不过,在使用操作符重载进行 32x16-bit乘法运算时,有必要使用它们。
在下面的示例中,请注意使用 AE_F32X4_RADD intrinsic将结果还原为单个 int。这个函数可转换为1个3-instruction序列。
Example7: 32x16 Dot product
int Energy (short a[], short b[], int n)
{
int i;
ae_f32x4 *ap = (ae_f32x4 *)a;
ae_f16x4 *bp = (ae_f16x4 *)b;
ae_f32x4 s, s2 = 0;
for (i = 0; i < n>>2; i+=2)
{
s += bp[i]*ap[i];
s2 += bp[i+1]*ap[i+1];
}
return AE_F32X4_RADD(s+s2);
}
2.5.1 操作符重载: 分数乘法
2.6 Cache Prefetch Operations
(To be continued...)
按Type划分的标准DSP操作
Load 是将内存里面的值读取到寄存器。
Store 是将寄存器里面的值写入到内存。
3.1 Load和Store操作
HiFi 5 DSP 支持读取和写入 8、16、24、32 和 64 位元素的标量、矢量或对偶矢量。
- 每个标量以 8、16、24、32 或 64 位形式读取/写入
- 每个矢量以 64 位或 48 位打包的 24 位数据形式
- 每个对偶矢量可以 128 位数据访问两个数据寄存器
对于矢量读取和写入,内存中高位地址的内容总是存储在寄存器的最小有效位。反向矢量读取内容并(?)反向写入寄存器中,这样内存中低位地址的内容就会存储在寄存器的最小有效位中。因此无论是以stride one还是stride negative one方式访问数据,最早被访问的数据始终位于寄存器中的相同位置。
HiFi 5 DSP 支持一次性读取或写入 64 位的数据向量流,即使数据未对齐到 64 位。请注意,虽然向量无需对齐到 64 位,但它们仍必须按照每个标量元素的要求进行对齐,如 32 位 int 型的向量。这样的读取与写入被称为对齐读取、写入。
一个特殊的启动指令,AE_LA64.PP,用于开启读取未对齐数据数组的过程。该指令从流的起始点读取数据到alignment寄存器。随后的对齐load指令将从内存中的下一个位置读取,并与alignment寄存器中已有的数据进行合并。对齐指令工作的具体细节与程序员无关。只需调用 AE_LA64_PP,加载第一个要读取的地址(无论是否对齐),然后继续使用适当的对齐读取,即可实现每条指令一次对齐读取的后续吞吐量。
3.2 MAC操作
HiFi 5 支持:
- 8个32x32-bit乘积累加操作每cycle。
- 16个32x16-bit乘积累加操作每cycle。
- 16个16x16-bit乘积累加操作每cycle。
运算中使用的累加器大多是32-bit和64-bit的累加器。
以下是所使用的指令描述约定:
-
Two-Way Single MUL acc_32:
This indicates the instruction results in two accumulator outputs, each containing a 32-bit integer value resulting from single multiply or multiply and accumulate operation. -
Two-Way Dual MUL acc_1.31:
This indicates the instruction results in two accumulator outputs, each containing a 1.31 fractional value resulting from dual multiply or dual multiply and accumulate operation. -
Two-Way Single CMUL acc_32 (or simply Two-Way CMUL acc_32):
This indicates the instruction results in two complex accumulator outputs, each containing two 32-bit integer values (real and imaginary part) resulting from a complex multiply or complex-multiply and accumulate operation. Therefore a single CMUL operation indicates there are 2 dual element multiply and sub/add operations to create real and imaginary outputs.
补充3:乘积累加运算
Ref: 乘积累加运算
3.3 加/减和比较操作
见 HiFi 5 DSP User's Guide Section 4.12.
3.4 Shift操作
所有的shift操作是以前缀AE_S开始。接着的字母是L或R代表shift的方向是左还是右。然后的字母是L或A代表shift是否是逻辑(logical)的(右移后填充0)还是算术(arithmetic)的(右移后符号位扩展)。下一个字母I表示immediate shift,A表示AR shift,V表示DR变量shift,S表示AE_SAR shift。紧接着是数字,表示被移元素的大小,可选的SYM表示右移对称的右移,还有一个可选的R表示右移位而不是截断的右移,以及一个可选的S表示饱和的左移。
3.5 归一化操作
见 HiFi 5 DSP User's Guide Section 4.14.
3.6 除法操作
HiFi 5 DSP提供2种divide step操作。
3.7 截断操作 & Round操作 & 饱和操作 & 转换操作
见 HiFi 5 DSP User's Guide Section 4.16 - 4.19.
3.8 Move操作
主要列出了在地址寄存器(AR)、DR、Core Boolean寄存器(BR)和EP寄存器之间移动数据的Move操作。
3.9 FFT操作
保持动态范围的特殊声明
固定字长寄存器和ALU, MACs在处理数据上有限制的动态范围。每经过一级 FFT/IFFT 算法,ALU 和 MAC 运算都会导致数据位宽度(bits width)增加。这意味着 FFT/IFFT 的总数据位宽与级数(number of stages)成比例增长。因此,重要的是通过适当缩放(scaling)输入数据,避免中间阶段的数据溢出,同时保持每级输出的最佳精度。
解决数据溢出/饱和有两种方法。
- 第一种方法是固定缩放,即每次通过算法后都将数据右移一个固定值,以保持足够的空间余量(sufficent headroom)
headroom: the space above a driver's or passenger's head in a vehicle。每次通过后进行固定值的移位可以保证数据不会饱和,但可能会丢掉底部的bit。但如果输入数据的magnitude较小,这将导致精度下降。 - 另一种方法是动态缩放或归一化,即在 FFT 的每个阶段检测最大输出值,并将中间输出归一化。这不仅提高了精度,还克服了固定缩放的限制。但是,除非 ISA 有专门的动态缩放指令,否则动态缩放操作会造成很大的周期开销。
HiFi 5 DSP ISA包括为高效动态缩放而设计的归一化指令,这在FFT/IFFT内核中特别有用。
HiFi 5 DSP 支持动态归一化,可根据需要进行归一化。在代码的每次传递中,intrinsics AE_S32X2X2RNG_XP 会存储传递结果,并计算所存储数据中可用的guard bit数量。在传递结束时,AE_CALCRNG32 指令会确定当前传递中生成的数据的最小空间余量(headroom),并使用最小headroom值设置 AE_SAR 寄存器,以确保至少有三个bit的headroom。随后,AE_ADDRNG32 和 AE_SUBRNG32 以及 AE_ADDANDSUBRNG32_ 指令将两个参数相加/减,并将结果根据 AE_SAR 中的值(在上一次循环中设置)右移,以确保本次循环不会溢出。
FFT 输入必须进行预缩放,从而至少有三个bit的headroom,以确保更好的精度和在第一次传递时不会溢出。(用于动态范围调整的 ISA 经过精心设计,可支持单声道或立体声 FFT 计算。它支持 Radix-2 和 Radix-4 FFT 变体所需的适当移位。
3.10 Bit Reversal操作
HiFi 5 DSP 提供辅助操作 AE_ADDBRBA32,可与索引读取和写入 (.X) 结合使用,在优化的 FFT 实现中执行位反转寻址。
3.11 Zero操作
AE_ZERO() 将 AE_DR 寄存器 d 的所有bit设置为零。通过 AE_MOVI 指令实现。该操作通过多个intrinsic函数支持各种数据类型。
3.12 可选浮点单元操作
HiFi 5 DSP 支持一对可选的 4-way SIMD IEEE 单精度浮点运算单元(SP FPU)和一个 8-way SIMD 半精度浮点运算单元(HP FPU)。
浮点单元与HiFi 5 DSP的其余部分共享AE_DR寄存器文件。因此,标准读取、写入和选择操作是共享的,并随intrinsic与浮点计算操作一起运行。
3.13 NaN传播
有些浮点运算将浮点数据作为输入操作数或输出操作数,但不能同时作为输入操作数和输出操作数。
还有一些浮点运算既有浮点输入操作数,也有浮点输出操作数。
根据 IEEE 754™ -2008,大多数浮点运算的输入和输出操作数都是浮点数据,如果输入为 NaN,则输出结果为 NaN。这种传播有助于程序员追溯数字异常或 NaN 的起源,通常是无效操作,如 inf - inf。
不过不要依赖 NaN 传播、payload或符号位,因为重新编译可能会导致传播改变或停止。