This tutorial shows the steps of using Xilinx FFT IP with DMA and stream interface. The FFT IP is used to calculate the FFT of a given signal. The FFT IP is implemented in Vivado HLS, and the Vivado block design is created to connect the IP with DMA and stream interface. The overlay is created and used in python environment.
To read more about the Xilinx FFT library see Xilinx FFT Library.
This work contain code from https://github.com/DYGV/HLS_FFT
This design will include the C reference design into the testbench. It will read in input from a file, then process for FFT results. See testbench for more details.
The interface of this design will take in a stream interface for the input signal then return the FFT value from stream. In addition, the size of FFT problem can be controlled from a register write using s_axilite
interface.
void hls_fft(fft_stream &stream_input, fft_stream &stream_output,
unsigned size) {
#pragma HLS INTERFACE axis port = stream_input
#pragma HLS INTERFACE axis port = stream_output
#pragma HLS INTERFACE s_axilite port = size
#pragma HLS INTERFACE s_axilite port = return
The design use complex number buffer to contain the input and output data. The complex number is defined as
std::complex<float> fft_in[FFT_LENGTH_MAX] __attribute__((no_ctor));
The FFT library can be called from
hls::fft<fft_param>(fft_input, fft_output, &fft_status, &fft_config);
Note, we need to specify the parameter to configure the IP, read more detailed parameters from FFT Static Parameters. We defined them in https://github.com/aproposorg/KV260-PYNQ-tutorial/blob/main/HW_HLS/X_FFT/hls_fft.h
Now we can move on to the testbench
It calls the HLS design first and saves the output, then it calls the C reference design and compare the results. Note here we are rounding the results to 3 digits after the decimal, since the precision of the C reference design is slightlly different with the HLS.
Similar with the previous tutorial, we need to create a Vivado block design to connect the IP with DMA and stream interface.
We need manually enable some interface of the Zynq block such as master interface S_AXI_HP0
. The DMA memory map width also need to increased to 64 to match with the stream interface of your IP.
The block design is shown below.
This step similar with the previous tutorial.
Note here we are using DMA for data transfer, so we use a API to call for transfer and receive. We also need to pass a scarlar into the IP which control the size of FFT problem.
dma_ip.sendchannel.transfer(in_buffer)
dma_ip.recvchannel.transfer(out_buffer)
fft_ip.write(0x00, 0x01)