FIR Filter IP Library - 2025.2 English - UG1399

Vitis High-Level Synthesis User Guide (UG1399)

Document ID
UG1399
Release Date
2025-11-20
Version
2025.2 English

The AMD FIR IP block can be called within a C++ design using the library hls_fir.h. This section explains how the FIR can be configured in your C++ code.

Important: AMD highly recommends that you review the FIR Compiler LogiCORE IP Product Guide (PG149) for information on how to implement and use the features of the IP.

To use the FIR in your C++ code:

  1. Include the hls_fir.h library in the code.
  2. Set the default parameters using the library predefined struct hls::ip_fir::params_t, or copy it as your own configuration struct.
    Important: The Vivado LogiCORE IP through its dialog wizard detects and prevents incompatibilities in settings. Check your own configuration struct parameters into these Vivado IP dialogs to avoid unsupported or faulty configurations.
  3. Call the FIR function.
  4. Optionally, define a runtime input configuration to modify some parameters dynamically.

The following code examples provide a summary of how each of these steps is performed. Each step is discussed in more detail below.

First, include the FIR library in the source code. This header file resides in the include directory in the Vitis HLS installation area. This directory is automatically searched when Vitis HLS executes. There is no need to specify the path to this directory if compiling inside Vitis HLS.

#include "hls_fir.h"

Define the static parameters of the FIR. This includes such static attributes such as the input width, the coefficients, the filter rate (single, decimation, hilbert). The FIR library includes a parameterization struct hls::ip_fir::params_t to initialize all static parameters with default values.

In this example, the coefficients are defined as residing in array coeff_vec and the default values for the number of coefficients, the input width and the quantization mode are over-ridden using a user-defined struct myconfig based on the predefined struct.

struct myconfig : hls::ip_fir::params_t {
    coeff_vec[sg_fir_srrc_coeffs_len] = { ... };
    num_coeffs = sg_fir_srrc_coeffs_len;
    input_width = INPUT_WIDTH;
    quantization = hls::ip_fir::quantize_only;
    OVERRIDE(coefficient_width) = COEFF_WIDTH; // errors out and suggests correct spelling: "no member named 'coefficient_width' in 'hls::ip_fir::params_t'; did you mean 'coeff_fract_width'?"
    num_coefficients = sg_fir_srrc_coeffs_len; // errors out with confusing message: "a type specifier is required for all declarations"
    
Tip: Do not declare the configuration parameters again, but only assign them a value (or use the OVERRIDE macro to do so), to ensure that compilation errors out when you misspell one of the parameter names.

Create an instance of the FIR function using the HLS namespace with the defined static parameters (myconfig in this example) and then call the function with the run method to execute the function. The function arguments are, in order, input data and output data.

static hls::FIR<param1> fir1;
fir1.run(fir_in, fir_out);

Optionally, a runtime input configuration can be used. In some modes of the FIR, the data on this input determines how the coefficients are used during interleaved channels or when coefficient reloading is required. This configuration can be dynamic and is therefore defined as a variable. For a complete description of which modes require this input configuration, refer to the FIR Compiler LogiCORE IP Product Guide (PG149).

When the runtime input configuration is used, the FIR function is called with three arguments: input data, output data, and input configuration.

// Define the configuration type
typedef ap_uint<8> config_t;
// Define the configuration variable
config_t fir_config = 8;
// Use the configuration in the FIR
static hls::FIR<param1> fir1;
// Define input and output data types
const int axis_data_width = ((myconfig::data_width+7)>>3<<3); // AXIS data width rounded up to byte boundary
const int axis_output_width = ((myconfig::output_width+7)>>3<<3); // AXIS data width rounded up to byte boundary
typedef ap_fixed<axis_data_width, axis_data_width - myconfig::data_fract_width> in_data_t;
typedef ap_fixed<axis_output_width, axis_output_width - myconfig::output_fract_width> out_data_t;
// input and output data should be declared locally or as arguments as follows:
// in_data_t fir_in[_CONFIG_T::data_length * _CONFIG_T::num_channels];
// out_data_t fir_out[_CONFIG_T::output_length * _CONFIG_T::num_channels];
fir1.run(fir_in, fir_out, &fir_config);
Tip: The example above shows the use of scalar values and arrays, but the FIR function also supports the use of hls::stream for arguments. Refer to Vitis HLS Introductory Examples for more information.
  • Observe that for SSR==1 and interpolation_rate==1, the fir_in and fir_out formal arguments can either have an array data type. In this case they can be passed as actual arguments:
    • Local arrays with #pragma HLS stream
    • Top arguments with #pragma HLS interface ap_fifo
  • hls::stream Data type, in this case they can be passed as actual arguments:
    • Local hls::streams
    • Top hls::stream arguments #pragma HLS interface ap_fifo
  • Observe that for SSR>1 or interpolation_rate>1. the fir_in and fir_out arguments must only satisfy the usual C++ type constraints and can have any interface type for arrays and hls::streams.