将 FFT 函数与串口接口搭配使用 - 2023.2 简体中文

Vitis 高层次综合用户指南 (UG1399)

Document ID
UG1399
Release Date
2023-12-18
Version
2023.2 简体中文

含串流接口的 FFT 函数在 HLS 名称空间内的定义方式如下:

  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);

调用方式如下:

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

STATIC_PARAM 属于静态参数化结构体,用于为 FFT 定义静态参数。

所有输入和输出都作为 hls::stream<> 提供给函数。在最终实现中,FFT RTL 块上的端口将作为 AXI4‑Stream 端口来实现。

重要: 在使用 set_directive_dataflow#pragma HLS dataflow 的数据流区域内,您必须使用串流 FFT 函数。FFT 无法在经流水打拍的区域内使用。为了避免发生诸如在时钟周期内 FFT 停滞且不处理任何数据之类的情况,就需要高性能运算,为此:
  • 整个设计层级(包括 FFT 层级及其上层层级)都必须是数据流区域或仅包含循环数据流的函数。
  • 所有 FIFO 的深度必须足以避免在协同仿真中出现阻塞,如 协同仿真死锁检测 中所述
  • 整个层级内的所有数据流进程都必须仅包含:
    • 带有回绕选项的流水线
    • 或者带有回绕选项的流水打拍循环

输入数据和输出数据串流的数据类型可采用 floatap_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);
}

要使用定点数据类型,应使用 Vitis HLS 任意精度类型 ap_fixed

#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;

使用这 2 种类型时,都应使用相同且正确的数据大小来将 FFT 参数化。对于浮点数据,数据宽度始终为 32 位,指定任何其他大小都将被视为无效。

串流与阵列的比较

下表显示了阵列和串流中 FFT IP 的使用示例。在这两种情况下,数据流层级一直延伸至顶层,不含任何顺序代码(数据流区域的代码和循环数据流函数的代码都不包含在内),并且复制输入和复制输出的循环均通过回绕方式进行流水打拍。

提示: 每个示例中顶层函数都使用阵列接口以保持一致性,但可改为使用 hls::stream<> 或者使用 STREAM 编译指示或指令以达成相同效果。
表 1. FFT 实参
含阵列实参的 FFT hls::stream<> 实参的 FFT
#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++)
 #pragma HLS pipeline II=1 rewind
 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++)
 #pragma HLS pipeline II=1 rewind
 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);
}