Optimizing the HLS Project
After analysis, you will most likely need or want to optimize the performance of your function. Even if it is performing well there may be opportunities for improvement. This section discusses the mechanisms for applying optimizations to your project. Refer to Optimization Techniques in Vitis HLS for a discussion of the various types of optimizations you can perform.
You can add optimization directives directly into the source code as
compiler pragmas, using various HLS PRAGMAS, or you can use Tcl set_directive
commands to apply optimization directives in a Tcl script to
be used by a solution.
In addition to optimization pragmas and directives, Vitis HLS provides a number of configuration settings to let you manage the default results of simulation and synthesis. These configuration settings are accessed using the menu command, and clicking the Add command to add configuration settings. Refer to Configuration Commands for more information on applying specific configuration settings.
Creating Additional Solutions
The most typical use of Vitis HLS is to create an initial design, analyze the results, and then perform optimizations to meet the desired area and performance goals. This is often an iterative process, requiring multiple steps and multiple optimizations to achieve the desired results. Solutions offer a convenient way to configure the tool, add directives to your function to improve the results, and preserve those results to compare with other solutions.
To create an additional solution for your , use the New Solution toolbar button . This opens the Solution Wizard as shown in the following figure.
menu command, or theThe Solution Wizard has the same options as described in Creating a New Vitis HLS Project, with an additional option to let you Copy directives and constraints from solution. In the case where there are already multiple solutions, you can specify which solution to copy from. After the new solution has been created, optimization directives can be added (or modified if they were copied from the previous solution).
When your project has multiple solutions, the commands are generally directed at the current active solution. You can specify the active solution by right-clicking on a solution in the Explorer view, and use the Set Active Solution command. By default, synthesis and simulation commands build the active solution, directives are applied to the active solution, and reports are opened for the active solution. You want to ensure you are working in the correct solution when your project has multiple solutions.
Adding Pragmas and Directives
Vitis HLS pragmas and directives let you configure the synthesis results for your code.
- HLS Pragmas are added to the source code to enable the optimization or change in the original source code. Every time the code is synthesized, it is implemented according to the specified pragmas.
- Optimization Directives, or the
set_directive
commands, can be specified as Tcl commands that are associated with a specific solution, or set of solutions. Allowing you to customize the synthesis results for the same code base across different solutions.
ARRAY_PARTITION
and ARRAY_RESHAPE
pragmas
to the same array variable will result in an error.To add pragmas or directives to your project:
- In the Explorer view of the Vitis HLS IDE, double-click the code file under the Source folder to open the Code Editor dialog box, the Outline view, and the Directive view.
- Use the Directive view to add pragmas to your source code. This
view helps you add and manage pragmas and directives for your project, and it
ensures that the pragmas are correct and applied in the proper location. To use this
view:
- With your source code open, select the Directive view tab to locate the function, loop, or feature
of the code to add a pragma or directive to.
Vitis HLS applies directives to the appropriate scope for the object currently selected in the Directive view.
- Right-click an object in the Directive view to use the Insert Directive command. The
Vitis HLS Directive
Editor opens, as shown in the following
figure:
- Review the Vitis HLS
Directive Editor dialog box. It includes the
following sections:
- Directive
- Specifies the directive or pragma to apply. This is a drop-down menu that lets you choose from the list of available directives.
- Destination
- Specifies that a pragma should be added to the source file, or that a
set_directive
command should be added to a Tcl script, the directive file, associated with the active solution.TIP: If your project only has one solution then it is always active. However, if you have multiple solutions you will need to ensure the desired solution is active in the project. Right-click the solution in the Explorer view of the project and click the Set Active Solution command. Refer to Creating Additional Solutions for details on adding solutions. - Options
- Lists various configurable options associated with the currently selected directive.
- Click OK to apply the pragma or directive.
Note: To view information related to a selected directive, click Help. - With your source code open, select the Directive view tab to locate the function, loop, or feature
of the code to add a pragma or directive to.
Using Directives in Scripts vs. Pragmas in Code
In the Vitis HLS Directive Editor dialog box, you can specify either of the following Destination settings:
- Directive File
- Vitis HLS inserts the directive as a Tcl command into the file directives.tcl in the solution directory.
- Source File
- Vitis HLS inserts the directive directly into the C source file as a pragma.
The following table describes the advantages and disadvantages of both approaches.
Directive Format | Advantages | Disadvantages |
---|---|---|
Directives file (Tcl Script) |
Each solution has independent directives. This approach is ideal for design exploration. If any solution is re-synthesized, only the directives specified in that solution are applied. |
If the C source files are transferred to a third-party or archived, the directives.tcl file must be included. The |
Source Code (Pragma) |
The optimization directives are embedded into the C source code. Ideal when the C sources files are shipped to a third-party as C IP. No other files are required to recreate the same results. Useful approach for directives that are unlikely to change, such as TRIPCOUNT and INTERFACE. |
If the optimization directives are embedded in the code, they are automatically applied to every solution when re-synthesized. |
When specifying values for pragma arguments, you can use literal values (for example, 1, 55, 3.14), or pass a macro using #define. The following example shows a pragma with literal values:
#pragma HLS ARRAY_PARTITION variable=k_matrix_val type=cyclic factor=5
This example uses defined macros:
#define E 5
#pragma HLS ARRAY_PARTITION variable=k_matrix_val type=cyclic factor=E
Applying Directives to the Proper Scope
Although the Vitis HLS GUI lets you
apply directives to specific code objects, the directives are added to the scope that contains
the object. For example, you can apply the INTERFACE
pragma
to an interface object in the Vitis HLS GUI, but the
directive is applied to the top-level function (scope). The interface port (object) is
identified in the directive.
You can apply optimization directives to the following objects and scopes:
- Functions
- When you apply directives to functions, Vitis
HLS applies the directive to all objects within the scope of that function. The effect of
any directive stops at the next level of the function hierarchy, and does not apply to
sub-functions.TIP: Directives that include a recursive option, such as the
PIPELINE
directive, can be applied recursively through the hierarchy. - Interfaces
- Vitis HLS applies the directive to the top-level function, which is the scope that contains the interface.
- Loops
- Directives apply to all objects within the scope of the loop.
For example, if you apply the
LOOP_MERGE
directive to a loop, Vitis HLS applies the directive to any sub-loops within the loop, but not to the loop itself. The loop to which the directive is applied is not merged with siblings at the same level of hierarchy. - Arrays
- Directives are applied to the scope that contains the array.
Applying Optimization Directives to Global Variables
- With the code open in the Code Editor, select the scope (function, loop or region) where the global variable is used in the Directive view.
- Right-click and use the Insert Directive command to open the Vitis HLS Directives Editor.
- Select and configure the required directive, and click OK to add it.
- Locate the added directive in the Directive view, and manually edit the
variable
name to assign it to the global variable.
Applying Optimization Directives to Class Objects
Optimization directives can be also applied to objects or scopes defined in a class. The difference is typically that classes are defined in a header file. Use one of the following actions to open the header file:
- From the Explorer view in the Vitis HLS GUI,
open the
Includes
folder, double-click the header file to open it in the Code Editor. - From within an open source code file, place the cursor over the
#include
statement for the header file, hold down the Ctrl key, and click the header file to open it in the Code Editor.
The Directives tab is populated with the objects in the header file, and directives can be applied.
Applying Optimization Directives to Templates
To apply optimization directives manually on templates when using Tcl commands, specify the template arguments and class when referring to class methods. For example, given the following C++ code:
template <uint32 SIZE, uint32 RATE>
void DES10<SIZE,RATE>::calcRUN() {
}
The following Tcl command is used to specify the INLINE directive on the function:
set_directive_inline DES10<SIZE,RATE>::calcRUN
Using #define with Pragma Directives
Pragma directives support the use of values specified by #define
statements.
For example, the following code seeks to specify the depth of a stream
using the #define
statement:
#include <hls_stream.h>
using namespace hls;
#define STREAM_IN_DEPTH 8
void foo (stream<int> &InStream, stream<int> &OutStream) {
// pragma using #DEFINE
#pragma HLS stream depth=STREAM_IN_DEPTH variable=InStream
// pragma using value
#pragma HLS stream depth=8 variable=OutStream
}
You can use a constant such as const int
, or
constexpr
. For example:
const int MY_DEPTH=1024;
#pragma HLS stream variable=my_var depth=MY_DEPTH
You can also use macros in the C code to implement this functionality. The key to using macros is to use a level of hierarchy in the macro. This allows the expansion to be correctly performed. The code can be made to compile as follows:
#include <hls_stream.h>
using namespace hls;
#define PRAGMA_SUB(x) _Pragma (#x)
#define PRAGMA_HLS(x) PRAGMA_SUB(x)
#define STREAM_IN_DEPTH 8
void foo (stream<int> &InStream, stream<int> &OutStream) {
// Legal pragmas
PRAGMA_HLS(HLS stream depth=STREAM_IN_DEPTH variable=InStream)
#pragma HLS stream depth=8 variable=OutStream
}
Failure to Satisfy Optimization Directives
When optimization directives are applied, Vitis HLS outputs information to the console (and log file) detailing the progress. In the following example the PIPELINE directives was applied to the C function with an II=1 (iteration interval of 1) but synthesis failed to satisfy this objective.
INFO: [SCHED 11] Starting scheduling ...
INFO: [SCHED 61] Pipelining function 'array_RAM'.
WARNING: [SCHED 63] Unable to schedule the whole 2 cycles 'load' operation
('d_i_load', array_RAM.c:98) on array 'd_i' within the first cycle (II = 1).
WARNING: [SCHED 63] Please consider increasing the target initiation interval of the
pipeline.
WARNING: [SCHED 69] Unable to schedule 'load' operation ('idx_load_2',
array_RAM.c:98) on array 'idx' due to limited memory ports.
INFO: [SCHED 61] Pipelining result: Target II: 1, Final II: 4, Depth: 6.
INFO: [SCHED 11] Finished scheduling.
By seeking to create a design which satisfies a lower optimization target, Vitis HLS is able to provide three important types of information:
- What target performance can be achieved with the current C code and optimization directives.
- A list of the reasons why it was unable to satisfy the higher performance target.
- A design which can be analyzed to provide more insight and help understand the reason for the failure.
In message SCHED-69
, the reason given for failing
to reach the target II is due to limited ports. The design must access
a block RAM, and a block RAM only has a maximum of two ports.
The next step after a failure such as this is to analyze what the issue is. In this example, analyze line 52 of the code and/or use the Analysis perspective to determine the bottleneck and if the requirement for more than two ports can be reduced or determine how the number of ports can be increased.
After the design is optimized and the desired performance achieved, the RTL can be verified and the results of synthesis packaged as IP.