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.
__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.
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
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 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 singleThis rule explicitly groups all interface ports with the same
s_axilite
interface. Creating multiple bundles in this flow, either explicitly or implicitly, will result in an error during synthesis.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 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 compatiblem_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 singlem_axi
interface port and names the RTL port the value specified bym_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 interfacegmem0
.#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 portm_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 allm_axi
interface ports without a bundle name into individual interface ports and names the RTL port sequentially asm_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 them_axi
interface, this rule explicitly groups all them_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 them_axi
interface, this rule explicitly groups all them_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 }
- Rule 1: User-specified SAXI Lite:
When the INTERFACE pragma specifies an
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 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.
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_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 |