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
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.
__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 Managing Interface Synthesis, 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.
Vitis Flow (Accelerator Mode)
If HLS is used in the Vitis flow, the tool will automatically set the following configurations.
open_solution -flow_target vitis
| 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 | |||
|
||||||
The AXI4-Lite slave interface directive will change the behavior of the interface pragmas as shown below.
config_interface -default_slave_interface s_axilite
| 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 | |
|
||||||
Vivado Design Flow
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 vivadoconfig_interface -default_slave_interface s_axilite
In this case, the following default interfaces are applied.
| Argument Type | Scalar | Array | Pointer or Reference | Hls::stream | |||||
|---|---|---|---|---|---|---|---|---|---|
| Interface Mode | Input | Return | I | I/O | O | I | I/O | O | I and O |
| ap_ctrl_none | 3 | 1 | 3 | 3 | 3 | 3 | 3 | 3 | 3 |
| ap_ctrl_hs | 3 | D1 | 3 | 3 | 3 | 3 | 3 | 3 | 3 |
| ap_ctrl_chain | 3 | 1 | 3 | 3 | 3 | 3 | 3 | 3 | 3 |
| axis | 1 | 3 | 1 | 3 | 1 | 1 | 3 | 1 | 1 |
| s_axilite | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 3 |
| m_axi | 3 | 3 | 1 | 1 | 1 | 1 | 1 | 1 | 3 |
| ap_none | D1 | 3 | 3 | 3 | 3 | D1 | 1 | 1 | 3 |
| ap_stable | 1 | 3 | 3 | 3 | 3 | 1 | 3 | 3 | 3 |
| ap_ack | 1 | 3 | 3 | 3 | 3 | 1 | 1 | 1 | 3 |
| ap_vld | 1 | 3 | 3 | 3 | 3 | 1 | 1 | D1 | 3 |
| ap_ovld | 3 | 3 | 3 | 3 | 3 | 3 | D1 | 1 | 3 |
| ap_hs | 1 | 3 | 1 | 3 | 1 | 1 | 1 | 1 | 1 |
| ap_memory | 3 | 3 | D1 | D1 | D1 | 3 | 3 | 3 | 3 |
| bram | 3 | 3 | 1 | 1 | 1 | 3 | 3 | 3 | 3 |
| ap_fifo | 3 | 3 | 1 | 3 | 1 | 1 | 3 | 1 | D1 |
|
|||||||||
Structs
Structs in the code, for instance internal and global variables, are disaggregated by default. They are decomposed into their member elements. 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.
Interface Bundle Rules
The interface pragma contains a bundle option that groups function arguments to the respective individual 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
- 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 bys_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 ports_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 liteinterface 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
global config option
config_interface -m_axi_auto_max_ports falsewill impact the bundle rules as follows:- Rule 1: User-specified Bundle Name:
- This rule explicitly groups all interface ports with
the same
bundle=<string>into the same AXI MAXI interface port and names the RTL port the value specified bym_axi_<string>.#pragma HLS INTERFACE m_axi port=a depth=50 bundle=terry #pragma HLS INTERFACE m_axi port=a depth=50 #pragma HLS INTERFACE m_axi port=a depth=50 Log file INFO: [RTGEN 206-500] Setting interface mode on port 'example/terry' 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/gmem1' to 'm_axi'.#pragma HLS INTERFACE m_axi port=a depth=50 bundle=terry #pragma HLS INTERFACE m_axi port=a depth=50 bundle=terry #pragma HLS INTERFACE m_axi port=a depth=50 bundle=terry Log file INFO: [RTGEN 206-500] Setting interface mode on port 'example/terry' to 'm_axi'. INFO: [RTGEN 206-500] Setting interface mode on port 'example/terry' to 'm_axi'. INFO: [RTGEN 206-500] Setting interface mode on port 'example/terry' to 'm_axi'. - Rule 2: Default Bundle Name:
- This rule explicitly groups all interface ports with no
bundle name into the same AXI interface port, and uses tool default
bundle=<default>, and names the RTL port<default>_m_axi.#pragma HLS INTERFACE m_axi port=a depth=50 #pragma HLS INTERFACE m_axi port=a depth=50 #pragma HLS INTERFACE m_axi port=a 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'.#pragma HLS INTERFACE m_axi port=a depth=50 bundle=terry #pragma HLS INTERFACE m_axi port=a depth=50 #pragma HLS INTERFACE m_axi port=a depth=50 Log file INFO: [RTGEN 206-500] Setting interface mode on port 'example/terry' 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 truewill impact the bundle rules as follows.- This rule explicitly maps all the unspecified bundle
interface ports into individual different AXI MAXI interface ports and
names the RTL port sequentially as
gmem_0,geme_1,geme_2.
- This rule explicitly maps all the unspecified bundle
interface ports into individual different AXI MAXI interface ports and
names the RTL port sequentially as
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 liteinterface ports.
MAXI Offset
- Fully Specified Offset: This rule adheres to the user-specified offset settings in the pragma.
- No-offset Specified: If maxi offset is not specified
in the pragma, the global config option:
config_interface -m_axi_offset <off/direct/slave>will impact the offset rules.- Rule 1: User-specified SAXI Lite: This rule
explicitly groups all the maxi offsets into user-specified AXI_lite
port, for which the MAXI
offset=<slave>.
void top(int *a) { #pragma HLS interface m_axi port=a #pragma HLS interface s_axilite port=a } - Rule 2: No SAXI LITE: This rule explicitly groups
all the maxi offsets into the tool default offset.
- For Vitis:
offset = slave - For Vivado:
offset = off - User Specified:
config_interface -m_axi_offset direct
void top(int *a, int *b) { #pragma HLS interface m_axi port=a bundle=M0 #pragma HLS interface m_axi port=b bundle=M0 } - For Vitis:
- Rule 1: User-specified SAXI Lite: This rule
explicitly groups all the maxi offsets into user-specified AXI_lite
port, for which the MAXI
offset=<slave>.
Memory Property on Interface
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
TDATA is
registered. Behavior Changes to config_rtl -module_auto_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
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.
| 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.
| Default Control Settings | Vivado HLS | Vitis HLS |
|---|---|---|
| interface pragma defaults | ip mode | kernel mode (check default interfaces) |
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 |