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

Properly converting float64 to 16bit fixed point for PYNQ

我需要将一个float64值转换成一个定点数<16,15>(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]
###

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

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

当调用函数将输入复制到 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<16,15>,其中 MSB 是符号位。在您的示例中,您已经签署了样本(因为正弦曲线在 -1.0 和 1.0 之间),因此您的转换必须是 int(signed int)。但是,如果您需要 signed 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<16,15>(fxp-s16/15S1.15 类型),请注意舍入和截断方法。

如果你使用fxpmath,你在做什么转换就更清楚了。确实返回了一个对象,但是你可以通过raw方法提取int(原始)值,或者通过uraw方法提取uint

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 数组。如果需要int的pythonlist,就用tolist的方法,例如:data_in_fxp.raw().tolist()

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