Using FFT Function with Streaming Interface - 2022.2 English

Vitis High-Level Synthesis User Guide (UG1399)

Document ID
UG1399
Release Date
2022-12-07
Version
2022.2 English

The FFT function with streaming interfaces is defined in the HLS namespace similarly to this:

  template <typename PARAM_T>
  void fft(hls::stream<complex<float or ap_fixed>> &xn_s,
           hls::stream<complex<float or ap_fixed >> &xk_s,
           hls::stream<ip_fft::status_t<CONFIG_T>> &status_s,
           hls::stream<ip_fft::config_t<CONFIG_T>> &config_s);

and can be called as follows:

hls::fft<STATIC_PARAM> (
INPUT_DATA_STREAM,
OUTPUT_DATA_STREAM,
OUTPUT_STATUS_STREAM,
INPUT_RUN_TIME_CONFIGURATION_STREAM);

The STATIC_PARAM is the static parameterization struct that defines the static parameters for the FFT.

All input and outputs are supplied to the function as a hls::stream<>. In the final implementation, the ports on the FFT RTL block will be implemented as AXI4-Stream ports. Xilinx recommends always using the FFT function in a dataflow region using set_directive_dataflow or #pragma HLS dataflow.

Important: The FFT cannot be used in a region which is pipelined. If high-performance operation is required, pipeline the loops or functions before and after the FFT then use dataflow optimization on all loops and functions in the region.

The data types for input data and output data streams can be float or ap_fixed.

void fft_top(
    bool direction,
    complex<data_in_t> in[FFT_LENGTH],
    complex<data_out_t> out[FFT_LENGTH],
    bool &ovflo)
{
    #pragma HLS dataflow
    hls::stream<complex<data_in_t>> in_fft; 
    hls::stream<complex<data_out_t>> out_fft; 
    hls::stream<config_t> fft_config;
    hls::stream<status_t> fft_status;

    // convert inputs to hls::stream<> and generates fft_config stream based on input arguments   
    proc_fe(direction, fft_config, in, in_fft);
    // FFT IP
    hls::fft<config1>(in_fft, out_fft, fft_status, fft_config);
    // convert fft result to outputs data type and sets output ovflo according to fft_status stream
    proc_be(fft_status, ovflo, out_fft, out);
}

To use fixed-point data types, the Vitis HLS arbitrary precision type ap_fixed should be used.

#include "ap_fixed.h"
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;
#include <complex>
typedef complex<data_in_t> cmpxData;
typedef complex<data_out_t> cmpxDataOut;

In both cases, the FFT should be parameterized with the same correct data sizes. In the case of floating point data, the data widths will always be 32-bit and any other specified size will be considered invalid.

Comparing Stream and Array

The following table shows the array example and stream example usage of the FFT IP.

Tip: The top functions in each example are using array interfaces for consistency, but could be changed to use hls::stream<> or use the STREAM pragma or directive to the same effect.

Table 1. FFT Arguments
FFT with Array Arguments FFT with hls::stream<> Arguments
#include "fft_top.h"
 
void proc_fe(
 bool direction,
 config_t* config, 
 cmpxDataIn in[FFT_LENGTH], 
 cmpxDataIn out[FFT_LENGTH])
{
 int i; 
 config->setDir(direction);
 config->setSch(0x2AB);
 for (i=0; i< FFT_LENGTH; i++)
 out[i] = in[i];
}
 
void 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;
}
 
// TOP function
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;
 
 // convert inputs to arrays and generates fft_config 
 // based on input arguments 
 proc_fe(direction, &fft_config, in, xn);
 // FFT IP
 hls::fft<config1>(xn, xk, &fft_status, &fft_config);
 // convert fft results to outputs data type and 
 // sets output ovflo according to fft_status
 proc_be(&fft_status, ovflo, xk, out);
}
#include "fft_top.h"
 
void proc_fe(
 bool direction,
 hls::stream<config_t> &config_s, 
 cmpxDataIn in[FFT_LENGTH],
 hls::stream<cmpxDataIn> &out_s)
{
 int i; 
 config_t config;
 config.setDir(direction);
 config.setSch(0x2AB);
 config_s.write(config);
 for (i=0; i< FFT_LENGTH; i++) {
 #pragma HLS pipeline II=1 rewind
 out_s.write(in[i]);
 }
}
 
void proc_be(
 hls::stream<status_t> &status_in_s, 
 bool &ovflo,
 hls::stream<cmpxDataOut> &in_s,
 cmpxDataOut out[FFT_LENGTH])
{
 int i; 
 for (i=0; i< FFT_LENGTH; i++) {
 #pragma HLS pipeline II=1 rewind
 out[i] = in_s.read();
 }
 status_t status_in = status_in_s.read();
 ovflo = status_in.getOvflo() & 0x1;
}
 
// TOP function
void fft_top(
 bool direction,
 complex<data_in_t> in[FFT_LENGTH],
 complex<data_out_t> out[FFT_LENGTH],
 bool &ovflo)
{
#pragma HLS dataflow
 hls::stream<complex<data_in_t>> xn;
 hls::stream<complex<data_out_t>> xk;
 hls::stream<config_t> fft_config;
 hls::stream<status_t> fft_status;
 // convert inputs to hls::stream<> 
 // and generates fft_config stream based on input arguments 
 proc_fe(direction, fft_config, in, xn);
 // FFT IP
 hls::fft<config1>(xn, xk, fft_status, fft_config);
 // convert fft result to outputs data type 
 // and sets output ovflo according to fft_status stream
 proc_be(fft_status, ovflo, xk, out);
}