为 PYNQ 正确地将 float64 转换为 16bit 定点

问题描述

我需要将 float64 值转换为定点 (16 位,小数部分为 15 位,整数部分为 1)。

我已经阅读了很多解决方案:

  1. Convert floating point to fixed point
  2. Simple Fixed-Point Conversion in C
  3. fxpmath 图书馆

但是我还没有真正理解我在特定情况下需要的“类型”。

为了更好地解释这一点,我在 PYNQ(基于 Python 的 Xilinx 框架)中实现了一个生成简单正弦波的代码

import numpy as np
###
fs = 44100  
n_samples = 1024 
f_sig = 130 #selected to get almost exactly a single frequency bin (43.066 * 3)
A = 1
seconds = 1

t = np.arange(0,seconds,step=1/fs)

#resolution of fft = fs/n_samples -> 44100/1024 = 43.066
data_in = A * np.sin(2*np.pi*f_sig*t)
data_in = data_in[:1024]
###

生成一个简单的正弦波,然后我选择前 1024 个样本。此选择的目标是将这些样本发送到 FFT 逻辑核块 (FFT logicore documentation)。在我的特定情况下,FFT 已配置为接受实部和虚部的 16 位定点输入 (您可以在第 18 页的文档中找到更多信息)并执行 1024 点 FFT。所以基本上我需要将这个正弦曲线从 float64 转换为 ap_fixed<16,15>

但是,我不确定应该在转换中使用哪种 Python 数据类型。例如,在第二种方法中,它使用 uint16 来存储转换后的数据。在第三种方法中,库返回一个对象。

调用函数将输入复制到 DMA 缓冲区并计算 FFT 时,我有以下代码

from pynq imort Overlay
from pynq import allocate

dma = overlay.axi_dma_0
buff_in = allocate(1024,'ukNown type') # which type should I use here? 
buff_in[:] = data_in[:]
np.copyto(buff_in,data_in,casting="unsafe")
dma.sendchannel.transfer(buff_in)

我理解的基本算法应该是这样的:

import numpy as np
my_casting_int = int32(np.round(data_in * 2**15)))
my_casting_uint = uint32(np.round(data_in * 2**15)))

但同样,考虑到输入数据是带符号的 float64 正弦曲线,我不知道我是否需要有符号或无符号转换。

总结一下:

  1. 我的 FFT 期望每个输入样本有 2 个 ap_fixed<16,15> 数组,一个用于实部,一个用于虚部。作为一个真实的输入样本,我将有 16 位的实部和 16 位的虚部基本上全为零。
  2. 我需要将 Python 中的正弦曲线 float64 转换为适合我的 FFT 内核的格式(即 ap_fixed<16,15>)。
  3. 在从 float64 进行转换后,我不知道应该使用哪种 Python 类型。

解决方法

我想 FFT 需要 ap_fixed,其中 MSB 是符号位。在您的示例中,您对样本进行了签名(因为 -1.0 和 1.0 之间的正弦曲线),因此您的演员表必须是 int (签名整数)。但是,如果您需要一个带符号整数的两个补码表示,则使用 uint 进行强制转换是正确的。在这两种情况下,使用 16 位进行转换就足够了。

np.round(data_in*2**15).astype(np.int16) # returns -16384
np.round(data_in*2**15).astype(np.uint16) # returns 49152

如果您使用 float64 并且需要转换为 ap_fixed(fxp-s16/15S1.15 类型),请注意舍入和截断方法。

如果您使用 fxpmath,则您正在执行的转换会更加清楚。确实返回了一个对象,但您可以通过int 方法提取raw(原始)值,或通过uint 方法提取uraw

from fxpmath import Fxp

#...

data_in_fxp = Fxp(data_in,dtype='fxp-s16/15') # or dtype='S1.15'

raw_data_int = data_in_fxp.raw()
raw_data_uint = data_in_fxp.uraw() # two-complement

这些方法返回一个 numpy 数组。如果您需要 list 的 python int,只需使用 tolist 方法,例如:data_in_fxp.raw().tolist()

可以修改舍入和溢出行为,有关更多信息,请访问 fxpmath#behaviors