Migrating to Vitis HLS

When migrating a kernel module implemented in one version of Vivado® HLS, it is essential to understand the difference between the versions of HLS, and the impact that these differences have on the design.

Key Considerations:

  • Behavioral Differences
  • Unsupported Features
  • Deprecated Commands

New more strict syntax checking is provided in Vitis HLS for both source code and pragma usage:

  • Syntax Checker: The tool will error out for any incorrect/bad code fed to csynth_design
  • Pragma Conflict Checker: New warnings and errors are reported for pragma conflicts

HLS Behavioral Differences

Vitis™ HLS brings some fundamental changes in the way HLS synthesizes the C code, supports language constructs, and supports existing commands, pragmas, and directives. For example, the std::complex<long double> data type is not supported in Vitis HLS, and should not be used. These changes have implications on the application QoR. Xilinx® recommends reviewing this section before using the tool.

TIP: Due to the behavioral differences between Vitis HLS and Vivado HLS, you might need to differentiate your code for use in the Vitis tool. To enable the same source code to be used in both tools, Vitis HLS supports the __VITIS_HLS__ predefined macro to encapsulate source code written specifically for use in that tool. Use #if defined( __VITIS_HLS__) type pre-processor declarations to encapsulate tool specific code.

Default Interfaces

As described in Defining Interfaces, the type of the interfaces that Vitis HLS creates depends on the data type of the C arguments or the top-level function, the target flow, the default interface mode, and any specified INTERFACE pragmas or directives.

You can specify the target flow as either Vivado IP flow or Vitis Kernel flow using the open_solution -flow_target [vitis | vivado] option, or when opening the project solution as explained in Vitis HLS Process Overview. The following sections describe the default interface for the target flows.

Argument type definitions include the following:

  • I: Input only (can only read from arg)
  • O: Output only (can only write to arg)
  • IO: Input & output (can read and write to arg)
  • Return: Return data output
  • Block: Block-level control
  • D: Default mode for each typed.
IMPORTANT: If an illegal interface is specified, Vitis HLS issues a warning message and implements the default interface mode.

Vitis Flow (Kernel Mode)

If HLS is used in the Vitis flow, the tool will automatically set the following configurations.

open_solution -flow_target vitis
Table 1. Argument Types
Argument Type Scalar Pointer to an Array Hls::stream
Interface Mode Input Return I I/O O I and O
ap_ctrl_none
ap_ctrl_hs
ap_ctrl_chain D
axis D
m_axi D D D
  1. D = Default Interface

The AXI4-Lite slave interface directive will change the behavior of the interface pragmas as shown below.

config_interface -default_slave_interface s_axilite
Table 2. Argument Types
Argument Type Scalar Pointer to an Array Hls::stream
Interface Mode Input Return I I/O O I and O
s_axi_lite D D D D D
  1. D = Default Interface
Note: These default interface pragma settings can be overridden by a user-specified interface pragma.

Vivado Flow (IP Mode)

Vitis HLS can be used in standalone mode to create IP. The tool will run this flow by default for which it sets the following global options:

  • open_solution -flow_target vivado
  • config_interface -default_slave_interface s_axilite

Structs

Structs in the code, for instance internal and global variables, are disaggregated by default and decomposed into their member elements, as described in Structs. The number and type of elements created are determined by the contents of the struct itself. Arrays of structs are implemented as multiple arrays, with a separate array for each member of the struct.

Structs in C/C++ are padded with extra bytes by the compiler for data alignment. In order to make kernel code in Vitis HLS compliant with gcc, structs in kernel code are padded with extra bytes. Padding and alignment can be changed as described in Struct Padding and Alignment.

Interface Bundle Rules

The INTERFACE pragma or directive contains a bundle option that groups function arguments into AXI interface ports. The following sections list the rules if there is a mix of user-defined/not used and default bundle option.

S_AXI Lite

User-Defined and Default Bundle Names
Rule 1: User-specified Bundle Name
IMPORTANT: The Vitis Kernel flow only supports a single s_axilite interface. Creating multiple bundles in this flow, either explicitly or implicitly, will result in an error during synthesis.
This rule explicitly groups all interface ports with the same bundle=<string> into the same AXI4-Lite interface port and names the RTL port the value specified by s_axi_<string>.
void top(char *a, char *b, char *c, char *d) { 
#pragma HLS INTERFACE s_axilite port=a bundle=terry 
#pragma HLS INTERFACE s_axilite port=b bundle=terry 
#pragma HLS INTERFACE s_axilite port=c bundle=stephen 
#pragma HLS INTERFACE s_axilite port=d bundle=jim 
}
INFO: [RTGEN 206-100] Bundling port 'd' to AXI-Lite port jim. 
INFO: [RTGEN 206-100] Bundling port 'c' to AXI-Lite port stephen. 
INFO: [RTGEN 206-100] Bundling port 'a' and 'b' to AXI-Lite port terry. 
INFO: [RTGEN 206-100] Finished creating RTL model for 'example'
Rule 2: Default Bundle Name
This rule explicitly groups all interface ports with no bundle name into the same AXI4-Lite interface port, and uses tool default bundle=<deafult>, and names the RTL port s_axi_<default>.
void top(char *a, char *b, char *c, char *d) { 
#pragma HLS INTERFACE s_axilite port=a 
#pragma HLS INTERFACE s_axilite port=b 
#pragma HLS INTERFACE s_axilite port=c 
#pragma HLS INTERFACE s_axilite port=d 
}
Log fileINFO: [RTGEN 206-100] Bundling port 'a', 'b', 'c' to AXI-Lite port control. 
INFO: [RTGEN 206-100] Finished creating RTL model for 'example'.
Rule 3: Partially Specified Bundle Name
If the bundle names are partially specified, then the tool will create more than one s_axi lite interface port. See the following bundle rules:
  • This rule explicitly groups all interface ports with which the bundle name "control" (default name) is specified into the same AXI4-Lite Interface port.
  • This rule also explicitly groups all the remaining un-specified bundle names to the new default name which does not conflict with any user names.
void top(char *a, char *b, char *c, char *d) { 
#pragma HLS INTERFACE s_axilite port=a 
#pragma HLS INTERFACE s_axilite port=b 
#pragma HLS INTERFACE s_axilite port=c bundle=control 
#pragma HLS INTERFACE s_axilite port=d bundle=control 
}
INFO: [RTGEN 206-100] Bundling port 'c' and 'd' to AXI-Lite port control. 
INFO: [RTGEN 206-100] Bundling port 'a' and 'b' to AXI-Lite port control_r. 
INFO: [RTGEN 206-100] Finished creating RTL model for 'example'.

MAXI

  • No Global bundle configuration: The default global config option config_interface -m_axi_auto_max_ports false automatically bundles all compatible m_axi interfaces into a single interface, and impacts the bundle rules as follows:
    • Rule 1: User-specified Bundle Name:
    • This rule explicitly groups all interface ports with the same bundle=<string> into a single m_axi interface port and names the RTL port the value specified by m_axi_<string>.
      #pragma HLS INTERFACE m_axi port=a depth=50 bundle=newport
      #pragma HLS INTERFACE m_axi port=b depth=50 bundle=newport
      #pragma HLS INTERFACE m_axi port=c depth=50 bundle=newport
      Log file 
      INFO: [RTGEN 206-500] Setting interface mode on port 'example/newport' to 'm_axi'.
      INFO: [RTGEN 206-500] Setting interface mode on port 'example/newport' to 'm_axi'.
      INFO: [RTGEN 206-500] Setting interface mode on port 'example/newport' to 'm_axi'.
      

      Any ports without the bundle specified are grouped into the default interface gmem0.

      #pragma HLS INTERFACE m_axi port=a depth=50 bundle=newport
      #pragma HLS INTERFACE m_axi port=b depth=50
      #pragma HLS INTERFACE m_axi port=c depth=50
      Log file 
      INFO: [RTGEN 206-500] Setting interface mode on port 'example/newport' to 'm_axi'.
      INFO: [RTGEN 206-500] Setting interface mode on port 'example/gmem0' to 'm_axi'.
      INFO: [RTGEN 206-500] Setting interface mode on port 'example/gmem0' to 'm_axi'.
    • Rule 2: Default Bundle Name:
    • This rule explicitly groups all interface ports with no bundle name specified into a single m_axi interface port and names the port m_axi_gmem0.
      #pragma HLS INTERFACE m_axi port=a depth=50 
      #pragma HLS INTERFACE m_axi port=b depth=50
      #pragma HLS INTERFACE m_axi port=c depth=50 
      Log file 
      INFO: [RTGEN 206-500] Setting interface mode on port 'example/gmem0' to 'm_axi'.
      INFO: [RTGEN 206-500] Setting interface mode on port 'example/gmem0' to 'm_axi'.
      INFO: [RTGEN 206-500] Setting interface mode on port 'example/gmem0' to 'm_axi'.
      
  • Global Bundle Configuration: The global config option config_interface -m_axi_auto_max_ports true explicitly maps all m_axi interface ports without a bundle name into individual interface ports and names the RTL port sequentially as m_axi_gmem0, m_axi_gmem1, m_axi_gmem2...

Interface Offset

The interface option contains an offset option that controls the address offset in the AXI4-Lite (s_axilite) and AXI4 (m_axi) interfaces. The following list the rules the HLS tool uses when default offsets are mixed with user-defined offsets.

SAXI Lite

Rule 1: Fully Specified
This rule explicitly groups all the scalar and offsets into the user-specified AXI4-Lite ports.
Rule 2: Default Specified
This rule explicitly groups all the scalar and offsets without offset settings updating the default AXI4-Lite ports.
Rule 3: Partially Specified
If the offsets are partially specified, then the tool will create more than one s_axi lite interface ports.

MAXI Offset

  • Fully Specified Offset: This rule adheres to the user-specified offset settings in the INTERFACE pragma.
  • No-offset Specified: If m_axi offset is not specified in the INTERFACE pragma, the global config option: config_interface -m_axi_offset <off/direct/slave> will impact the offset rules.
    • Rule 1: User-specified SAXI Lite: When the INTERFACE pragma specifies an s_axilite interface associated with the m_axi interface, this rule explicitly groups all the m_axi offsets into an AXI4-Lite interface adapter, for which the -m_axi_offset=slave and the -default_slave_interface=s_axilite.
      void top(int *a) {
      #pragma HLS interface mode=m_axi port=a
      #pragma HLS interface mode=s_axilite port=a
      }
    • Rule 2: No SAXI LITE: When the INTERFACE pragma does not associate an s_axilite interface with the m_axi interface, this rule explicitly groups all the m_axi offsets into the tool default offset: -m_axi_offset=slave
      void top(int *a, int *b) {
      #pragma HLS interface mode=m_axi port=a bundle=M0
      #pragma HLS interface mode=m_axi port=b bundle=M0
      }

Memory Property on Interface

The storage_type option on the interface pragma or directive lets the user explicitly define which type of RAM is used, and which RAM ports are created (single-port or dual-port). If no storage_type is specified, Vitis HLS uses:
  • A single-port RAM by default.
  • A dual-port RAM if it reduces the initiation interval or latency.

For the Vivado flow, the user can specify a RAM storage type on the specified interface, replacing the old resource pragma with the storage_type.

#pragma HLS INTERFACE bram port = in1 storage_type=RAM_2P
#pragma HLS INTERFACE bram port = out storage_type=RAM_1P latency=3

AXI4-Stream Interfaces with Side-Channels

Side-channels are optional signals which are part of the AXI4-Stream standard. The side-channel signals can be directly referenced and controlled in the C/C++ code using a struct, provided the member elements of the struct match the names of the AXI4-Stream side-channel signals. The AXI4-Stream side-channel signals are considered data signals and are registered whenever TDATA is registered.

Behavior Changes to Module Names and Module Prefix

In Vivado HLS, when config_rtl -module_auto_prefix was enabled the top RTL module would have its name prefixed with its own name. In 2020.1 Vitis HLS, this auto prefix will only be applied to sub-modules.

There is no change to the -module_prefix behavior. If this option is used, the specified prefix value will be prepended to all modules including the top module. The -module_prefix option also still takes precedence over -module_auto_prefix.

# vivado HLS 2020.1 generated module names (top module is "top")
top_top.v      
top_submodule1.v
top_submodule2.v
 
# Vitis HLS 2020.1 generated module names
top.v             <-- top module no longer has prefix
top_submodule1.v
top_submodule2.v

Updated memory module name and RTL file name

The new naming of the module is appended with the type of memory as shown below.

OLD Module and RTL File Names NEW Module and RTL File Names
ncp_encoder_func_parbits_memcore_ram ncp_encoder_func_parbits_RAM_1P_LUTRAM_1R1W

Dataflow

Support of std::complex:

In Vivado HLS, std::complex data type could not be used directly inside the DATAFLOW, because of multiple readers and writer issue. This multiple reader and writer issue is coming from the std class constructor being called to initialize the value. When this variable is also used inside the dataflow as a channel, it leads to the above issue. However, Vitis supports the use of std::complex with support of an attribute no_ctor as shown below.

// Nothing to do here.
void proc_1(std::complex<float> (&buffer)[50], const std::complex<float> *in);
void proc_2(hls::stream<std::complex<float>> &fifo, const std::complex<float> (&buffer)[50], std::complex<float> &acc);
void proc_3(std::complex<float> *out, hls::stream<std::complex<float>> &fifo, const std::complex<float> acc);
 
void top(std::complex<float> *out, const std::complex<float> *in) {
#pragma HLS DATAFLOW
  std::complex<float> acc __attribute((no_ctor)); // here
  std::complex<float> buffer[50] __attribute__((no_ctor)); // here
  hls::stream<std::complex<float>, 5> fifo; // not here! (hls::stream has it internally)
 
  proc_1(buffer, in);
  proc_2(fifo, buffer, acc);
  porc_3(out, fifo, acc);
}

Default User Control Settings

The default global option configures the solution for either Vitis application acceleration development flow or Vivado IP development flow.

open_solution -flow_target [vitis | vivado]

This global option is replacing the old config option (config_sdx).

Vivado Flow:

Configures the solution to run in support of the Vivado IP generation flow, requiring strict use of pragmas and directives, and exporting the results as Vivado IP. The command to set up the project solution for the Vivado IP flow is:

open_solution -flow_target vivado

The table below shows the original default settings of command options in the Vivado HLS tool, and the new defaults found in the Vitis HLS tool.

Table 3. Default Control Settings
Default Control Settings Vivado HLS Vitis HLS
config_compile -pipeline_loops 0 64
config_export -vivado_optimization_level 2 0
set_clock_uncertainty 12.5 27%
config_interface -m_axi_alignment_byte_size N/A 0
config_interface -m_axi_max_widen_bitwidth N/A 0
config_export -vivado_phys_opt place none
config_interface -m_axi_addr64 false true
config_schedule -enable_dsp_full_reg false true
config_rtl -module_auto_prefix false true
interface pragma defaults ip mode ip mode

Vitis Flow (Kernel Mode):

Configures the solution for use in the Vitis application acceleration development flow. This configures the Vitis HLS tool to properly infer interfaces for the function arguments without the need to specify the INTERFACE pragma or directive, and to output the synthesized RTL code as a Vitis kernel object file (.xo). The command to set up the project solution for the Vitis kernel flow is:

open_solution -flow_target vitis

The table below shows the original default settings of command options in the Vivado HLS tool, and the new defaults found in the Vitis HLS tool.

Table 4. Default Control Settings
Default Control Settings Vivado HLS Vitis HLS
interface pragma defaults ip mode kernel mode (check default interfaces)
config_interface -m_axi_offset N/A direct
config_interface -m_axi_alignment_byte_size N/A 64
config_interface -m_axi_max_widen_bitwidth N/A 512
config_compile -name_max_length 256 255
config_compile -pipeline_loops 64 64
set_clock_uncertainty 27% 27%
config_rtl -register_reset_num 3 3
config_interface -m_axi_latency 0 64