Library Configuration
向量对齐
本页面提供有关库中向量对齐的信息。
该库利用了XMOS架构的向量处理单元(VPU)。所有对XS3 VPU的加载和存储都要求加载/存储地址对齐到4字节边界(字对齐)。
在当前版本的API中,这意味着大多数API函数要求向量(或支持BFP向量的数据)以字对齐的地址开始。然而,向量的大小(以字节为单位)不要求是4的倍数。
编写对齐安全的代码
对齐要求适用于支持向量的数据。对于低级API,这指的是传递给函数本身的指针。对于高级API,这指的是在初始化BFP向量时指定的data字段(或在bfp_complex_s16_t情况下的real和imag字段)所指向的内存。
int32_t和complex_s32_t类型的数组通常由编译器保证字对齐。然而,如果用户手动指定int32_t数组的起始位置,如下例所示:
uint8_t byte_buffer[100];
int32_t* integer_array = (int32_t*) &byte_buffer[1];
则向量可能不是字对齐的。用户有责任确保数据的正确对齐。
对于int16_t数组,默认情况下编译器不保证数组从字对齐的地址开始。要强制对这种类型的数组进行字对齐,请在变量定义中使用__attribute__((aligned (4))),如下所示:
int16_t __attribute__((aligned (4))) data[100];
有时需要8字节(双字)对齐。在这种情况下,既不保证int32_t也不保证int16_t按要求对齐。与上述类似,可以如下示例向编译器提示:
int32_t __attribute__((aligned (8))) data[100];
该库还提供了宏WORD_ALIGNED和DWORD_ALIGNED,分别强制进行4字节和8字节对齐,如上所述。
对称饱和算术(Symmetrically Saturating Arithmetic)
在普通整数算术中,块浮点逻辑选择指数和操作数的移位,以防止整数溢出。然而,XS3 VPU使用对称饱和整数算术。
饱和算术是指应用操作的部分结果使用比输出位深更大的位深度,并将无法用输出位深度正确表示的值设置为最接近的可表示值。
例如,在普通的C整数算术中,一个将两个32 位整数相乘的函数可能在返回32位结果之前,内部计算完整的64位乘积,并将值夹在范围 (INT32_MIN, INT32_MAX) 内。
对称饱和算术还包括一个性质,即可表示范围的下界是可表示范围的上界的负数。
非饱和整数算术的一个主要问题是,在二进制补码编码中,存在一个非零整数(例如16位二进制补码算术中的INT16_MIN)的值x,使得。如果不考虑这种情况,可能导致严重的算术错误。
相反,对称饱和算术的一个结果是,在使用与非饱和算术相同的指数和移位逻辑时,对于特定的输入尾数组合,可能会发生饱和情况。对于不同的操作,这个特殊情况是不同的。
当发生这种特殊情况时,结果向量的最小值(并且是最大幅度值)比其理想值多1个最低有效位(例如,16位算术中的-0x4000变为-0x3FFF)。这个输出元素尾数的误差为1个最低有效位, 即,其中是结果BFP向量的指数。
当然,BFP算术的本质通常涉及这个量级的误差。
频谱打包(Spectrum Packing)
在一般情况下,N点离散傅里叶变换(DFT)是对复数N点信号进行的操作,产生复数频谱。任何N点DFT的频谱都具有性质。因此,完整表示的N点DFT需要N个复数元素。
复数DFT和逆DFT:
在这个库中,当执行复数DFT(例如使用fft_bfp_forward_complex())时,结果是一个直接的映射:
X[f] X[f] 对于 ,其中X是一个包含complex_s32_t类型的N个元素的数组,其中X[f]的实部在X[f].re中,虚部在X[f].im中。
同样地,在执行N点复数逆DFT时,也期望使用这种表示。
实数DFT和逆DFT:
通常情况下,我们希望计算实信号的DFT。除了周期性性质()之外,实信号的DFT还具有复共轭对称性,即 ,其中是的复共轭。这种对称性使得存储这样的对称元素对变得多余(因此不希望存储)。这样一来,我们只需要在 个复数元素中显式存储 的 X[f]。
不幸的是,使用这样的表示方式具有一个不希望的特性,即无法原位计算N点实信号的DFT,因为该表示方式所需的内存比原始信号更多。
然而,如果我们将周期性性质和复共轭对称性结合起来:
因为 和 都保证是实数,我们可以通过将 的实部打包到 的虚部中,来恢复原位计算的好处。
因此,这个库中用于产生实信号频谱的函数(例如fft_bfp_forward_mono()和fft_bfp_forward_stereo())将以稍微不那么直接的方式进行打包(与复数DFT相比):
X[f] X[f] 对于
X[0] X[0] + jX[N/2],其中X是一个包含complex_s32_t类型的N/2个元素的数组。
同样地,在计算N点逆DFT时,比如通过fft_bfp_inverse_mono()或fft_bfp_inverse_stereo(),也期望使用这种编码方式。
注意:当执行立体声DFT或逆DFT时,为了保持结果的原位计算,两个信号的频谱将编码到内存的相邻块中,其中第二个频谱(即关联到'channel b'的频谱)占据较高的内存地址。
库FFT长度支持(Library FFT Length Support)
在计算DFT时,该库依赖于一对或两对包含离散傅里叶变换矩阵部分的查找表。较长的FFT长度需要更大的查找表。在使用CMake构建时,可以将最大FFT长度指定为CMake选项,并在构建时自动生成这些表。
如果不使用CMake,可以使用库中附带的一个Python脚本手动生成这些文件。该脚本位于lib_xcore_math/scripts/gen_fft_table.py。如果手动生成,必须将生成的.c文件添加为源文件,并在编译库文件时将包含xmath_fft_lut.h的目录添加为包含目录。
注意,头文件必须命名为xmath_fft_lut.h,因为它是通过#include "xmath_fft_lut.h"包含的。
默认情况下,这些表包含执行最多1024点 的正向或逆向DFT所需的系数。如果需要更大的DFT,或者已知最大所需DFT大小小于1024点,可以修改MAX_FFT_LEN_LOG2 CMake选项,默认值为10。
这两个查找表对应于分治FFT算法和分频FFT算法,运行时的符号分别为xmath_dit_fft_lut和xmath_dif_fft_lut。每个表包含N-4个复数32位值,每个表的大小为8\cdot (N-4)字节。
要手动为最大FFT长度为16384(),仅支持分治FFT算法的情况下重新生成这些表,可以使用以下命令:
python lib_xcore_math/script/gen_fft_table.py --dit --max_fft_log2 14
对于gen_fft_table.py,使用--help标志以获得更详细的语法和参数说明。
数字滤波器转换
本文介绍了将数字滤波器系数转换为库中优化滤波器实现所需形式的过程。
该库支持16位和32位FIR滤波器的优化实现,以及级联的32位双二阶滤波器。这些滤波器实现都要求滤波器系数以兼容的形式表示。
为了辅助完成这一过程,库中附带了几个Python脚本。这些脚本可用于将现有的浮点型滤波器系数转换为易于从xCore应用程序中调用的形式。
每个脚本从文件中读取浮点型滤波器系数,并计算出具有尽可能高精度 且与lib_xcore_math滤波器API兼容的滤波器的新表示形式。
每个脚本的输出包括两个文件,可以包含在您自己的xCore应用程序中。第一个输出是一个C源代码文件(.c),其中包含计算出的滤波器参数以及用于初始化和执行生成的滤波器的几个函数定义。第二个输出是一个C头文件(.h),可以包含在您的应用程序中,以提供对这些函数的访问。
此外,每个脚本都接受用户提供的滤波器名称作为输入参数。输出文件和函数名包含滤波器名称,允许使用该机制生成和执行多个滤波器。
例如,要生成一个名为"MyFilter"的32位FIR滤波器,其系数来自文件filter_coefs.txt,您可以使用以下命令:
python lib_xcore_math/script/gen_fir_filter_s32.py MyFilter filter_coefs.txt
该命令将生成两个输出文件:MyFilter.c和MyFilter.h。包含MyFilter.h文件可提供对三个函数的访问:MyFilter_init()、MyFilter_add_sample()和MyFilter()。这些函数分别对应库函数filter_fir_s32_init()、filter_fir_s32_add_sample()和filter_fir_s32()。
您可以在脚本中使用--help标志获取有关输入和其他选项的更详细描述。
以下是每种滤波器类型的可用脚本:
| 滤波器类型 | 脚本 |
|---|---|
| 32位FIR | lib_xcore_math/script/gen_fir_filter_s32.py |
| 16位FIR | lib_xcore_math/script/gen_fir_filter_s16.py |
| 32位双二阶 | lib_xcore_math/script/gen_biquad_filter_s32.py |