如上图所示,xilinx hls的fft ip核不仅可以计算固定长度的FFT变换,还可以在运行时动态配置fft变换长度,但其可配置的长度仅限于小于等于最大长度的所有可能的2的幂,即若该fft ip可支持的最大长度为32,那么配置选项为32,16,8… 运行时实时参数的配置可通过如下方式实现
config_t fft_config;
fft_config.setDir(0);
fft_config.setSch(0x57B);
fft_config.setNfft(nfft);
以下是整个工程的代码,分为fft_top.h,fft_top.cpp,fft_tb.cpp,前两者为源文件(设计文件),最后者为测试平台。在本工程示例中,fft ip可以支持的最大长度为32,我们通过运行时fft变换长度的动态配置,实现了16点fft的计算。 fft_top.h:
#include "ap_fixed.h"
#include "hls_fft.h"
const char FFT_INPUT_WIDTH = 16;
const char FFT_OUTPUT_WIDTH = FFT_INPUT_WIDTH;
const char FFT_CONFIG_WIDTH = 16;
const char FFT_NFFT_MAX = 5;
const int FFT_LENGTH = 1 << FFT_NFFT_MAX;
#include <complex>
using namespace std;
struct config1 : hls::ip_fft::params_t {
static const unsigned ordering_opt = hls::ip_fft::natural_order;
static const unsigned config_width = FFT_CONFIG_WIDTH;
static const bool has_nfft = true;
static const unsigned max_nfft = FFT_NFFT_MAX;
};
typedef hls::ip_fft::config_t<config1> config_t;
typedef hls::ip_fft::status_t<config1> status_t;
typedef ap_fixed<FFT_INPUT_WIDTH,1> data_in_t;
typedef ap_fixed<FFT_OUTPUT_WIDTH,FFT_OUTPUT_WIDTH-FFT_INPUT_WIDTH+1> data_out_t;
typedef std::complex<data_in_t> cmpxDataIn;
typedef std::complex<data_out_t> cmpxDataOut;
void dummy_proc_fe(
bool direction,
config_t* config,
cmpxDataIn in[FFT_LENGTH],
cmpxDataIn out[FFT_LENGTH]);
void dummy_proc_be(
status_t* status_in,
bool* ovflo,
cmpxDataOut in[FFT_LENGTH],
cmpxDataOut out[FFT_LENGTH]);
void fft_top(
bool direction,
cmpxDataIn in[FFT_LENGTH],
cmpxDataOut out[FFT_LENGTH],
bool* ovflo);
这里通过static const bool has_nfft = true语句激活了运行时fft变换长度动态可配置的功能。 fft_top.cpp:
#include "fft_top.h"
void dummy_proc_fe(
bool direction,
config_t* config,
cmpxDataIn in[FFT_LENGTH],
cmpxDataIn out[FFT_LENGTH])
{
int i;
config->setDir(direction);
config->setSch(0xA);
config->setNfft(4);
for (i=0; i< FFT_LENGTH; i++)
out[i] = in[i];
}
void dummy_proc_be(
status_t* status_in,
bool* ovflo,
cmpxDataOut in[FFT_LENGTH],
cmpxDataOut out[FFT_LENGTH])
{
int i;
for (i=0; i< FFT_LENGTH; i++)
out[i] = in[i];
*ovflo = status_in->getOvflo() & 0x1;
}
void fft_top(
bool direction,
complex<data_in_t> in[FFT_LENGTH],
complex<data_out_t> out[FFT_LENGTH],
bool* ovflo)
{
#pragma HLS interface ap_hs port=direction
#pragma HLS interface ap_fifo depth=1 port=ovflo
#pragma HLS interface ap_fifo depth=FFT_LENGTH port=in,out
#pragma HLS data_pack variable=in
#pragma HLS data_pack variable=out
#pragma HLS dataflow
complex<data_in_t> xn[FFT_LENGTH];
complex<data_out_t> xk[FFT_LENGTH];
config_t fft_config;
status_t fft_status;
dummy_proc_fe(direction, &fft_config, in, xn);
hls::fft<config1>(xn, xk, &fft_status, &fft_config);
dummy_proc_be(&fft_status, ovflo, xk, out);
}
在该代码中,片段
config->setDir(direction);
config->setSch(0xA);
config->setNfft(4);
实现了运行时变换方向、缩放因子以及变换长度的配置,这里设置的变换长度为
2
4
=
16
2^4=16
24=16 fft_tb.cpp:
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <sstream>
#include "fft_top.h"
#include <stdio.h>
#include <fstream>
#include <string>
#include <sstream>
using namespace std;
int main()
{
cmpxDataIn xn_input[FFT_LENGTH];
cmpxDataOut xk_output[FFT_LENGTH];
for(int i=0;i<FFT_LENGTH;i++){
xn_input[i].real((data_in_t)((float)i/(float)FFT_LENGTH));
xn_input[i].imag((data_in_t)((float)(-i)/(float)FFT_LENGTH));
}
bool ovflo;
int FWD_INV=1;
fft_top(FWD_INV, xn_input, xk_output, &ovflo);
for(int i=0;i<FFT_LENGTH;i++)
cout<<"i="<<i<<": "<<xk_output[i].real()<<"+"<<xk_output[i].imag()<<"j"<<endl;
cout<<ovflo<<endl;
return 0;
}
这里虽然数组长度为32,但是其实只有前16个数参与了16点FFT变换。可通过实验验证: HLS仿真结果:
i=0: 0.234375+-0.234375j
i=1: 0.0628967+0.0941467j
i=2: 0.0220947+0.0533447j
i=3: 0.00775146+0.0390015j
i=4: 0+0.03125j
i=5: -0.00518799+0.026062j
i=6: -0.00915527+0.0220947j
i=7: -0.0125122+0.0187378j
i=8: -0.015625+0.015625j
i=9: -0.0187378+0.0125122j
i=10: -0.0220947+0.00915527j
i=11: -0.026062+0.00518799j
i=12: -0.03125+0j
i=13: -0.039032+-0.00778198j
i=14: -0.0533447+-0.0220947j
i=15: -0.0941772+-0.0629272j
i=16: -0.0454102+0.00408936j
i=17: 0+0j
i=18: -0.262939+0.00195313j
i=19: 0+0j
i=20: -0.0424805+0.00408936j
i=21: 0+0j
i=22: -0.0351563+0.00408936j
i=23: 0+0j
i=24: -0.0439453+0.00408936j
i=25: 0+0j
i=26: -0.787781+0.00195313j
i=27: 0+0j
i=28: -0.0439453+0.00408936j
i=29: 0+0j
i=30: -0.348389+0.00195313j
i=31: 0+0j
而作为baseline的python代码如下:
if __name__=='__main__':
x=torch.from_numpy(np.array(x))
y=torch.from_numpy(np.array(y))
print(x)
print(y)
z=torch.complex(x,y)
print(torch.fft.fft(z)/16)
运行结果为:
tensor([ 0.2344-0.2344j, 0.0629+0.0942j, 0.0221+0.0533j, 0.0078+0.0390j,
0.0000+0.0312j, -0.0052+0.0261j, -0.0092+0.0221j, -0.0125+0.0187j,
-0.0156+0.0156j, -0.0187+0.0125j, -0.0221+0.0092j, -0.0261+0.0052j,
-0.0312+0.0000j, -0.0390-0.0078j, -0.0533-0.0221j, -0.0942-0.0629j],
dtype=torch.complex128)
可以看到,baseline的输出和HLS输出的前16个数在误差允许范围内可以认为是相同的。
|