5. LegUp Pragmas Manual¶
LegUp accepts pragma directives in the source code to guide the hardware generation. This reference section explains all of the pragmas available for LegUp HLS.
The pragmas follow the following syntax:
#pragma LEGUP <category> <feature> <parameter>(<value>)
The category refers to the general usage class of the pragma. Each pragma has one of the following categories:
function
: configure a hardware function.loop
: configure loop optimizations.interface
: configure hardware interfaces (arguments / global variables).memory
: configure hardware memory implementation.
Each category can have different configurable features. Some categories / features have parameters to passed to the pragma. A parameter can be optional with a default behaviour if not specified.
The value of a parameter can be either integer, boolean (true|false
),
name (variable / argument), or a set of pre-specified values.
Note
For integer parameters, the user is allowed to use constants (or expressions of constants) defined using #define directive. For example, this is allowed:
#define N 10
void fun() {
#pragma LEGUP loop unroll factor(N+1)
for (int i = 0; i < 100; i++)
...
}
The pragma position is not arbitrary and placing the pragma in an incorrect position will cause an error. Each pragma can has one of the following positions:
- At the beginning of function definition block before any other statements.
- Before global / local variable declaration.
- Before loop block.
5.1. Set Custom Top-Level Function¶
Syntax
#pragma LEGUP function top
Description
This pragma specifies the top-level C/C++ function. The top-level function and all of its descendant functions will be compiled to hardware.
Position
At the beginning of the function definition block.
Examples
int sum(int *a) {
#pragma LEGUP function top
...
}
5.2. Pipeline Function¶
Syntax
#pragma LEGUP function pipeline II(<int>)
Description
This pragma enables pipelining for a given function in the code. Function pipelining allows a new invocation of a function to begin before the current one has completed, achieving higher throughput. Optional arguments:
Parameters
Parameter | Value | Optional | Default | Description |
---|---|---|---|---|
II |
Integer | Yes | 1 | Pipeline initiation interval |
Position
At the beginning of the function definition block.
Examples
int sum(int *a) {
#pragma LEGUP function pipeline
...
}
int conv(int a[], int b[]) {
#pragma LEGUP function pipeline II(3)
...
}
5.3. Inline Function¶
Syntax
#pragma LEGUP function inline
Description
This pragma forces a given function to be inlined.
Position
At the beginning of the function definition block.
Examples
int sum(int *a) {
#pragma LEGUP function inline
...
}
5.4. Noinline Function¶
Syntax
#pragma LEGUP function noinline
Description
This pragma prevents a given function from being inlined.
Position
At the beginning of the function definition block.
Examples
int sum(int *a) {
#pragma LEGUP function noinline
...
}
5.5. Flatten Function¶
Syntax
#pragma LEGUP function flatten
Description
This pragma unrolls all loops and inlines all subfunctions for a given function.
Position
At the beginning of the function definition block.
Examples
int sum(int *a) {
#pragma LEGUP function flatten
...
}
5.6. Replicate Function¶
Syntax
#pragma LEGUP function replicate
Description
This pragma specifies a function to be replicated every time it is called. By default, when the circuit is not pipelined, LegUp creates a single instance for each function which is shared across multiple calls to the function. When using this pragma on the function, LegUp will create a new instance of the function for every function call.
Position
At the beginning of the function definition block.
Examples
int sum(int *a) {
#pragma LEGUP function replicate
...
}
5.7. Pipeline Loop¶
Syntax
#pragma LEGUP loop pipeline II(<int>)
Description
This pragma enables pipelining for a given loop in the code. Loop pipelining allows a new iteration of the loop to begin before the current one has completed, achieving higher throughput. It can be specified to pipeline a single loop or a nested loop. If specified on a single loop or on a inner loop of a nested loop, that loop will be pipelined. If specified on the outer loop of a nested loop, the outer loop will be pipelined and all of its inner loops will be automatically unrolled.
Parameters
Parameter | Value | Optional | Default | Description |
---|---|---|---|---|
II |
Integer | Yes | 1 | Pipeline initiation interval |
Position
Before the beginning of the loop. If there is a loop label, the pragma should be placed after the label.
Examples
#pragma LEGUP loop pipeline II(2)
for (int i = 0; i < 10; i++) {
...
}
LOOP_LABEL:
#pragma LEGUP loop pipeline
while (i < 10) {
...
}
5.8. Unroll Loop¶
Syntax
#pragma LEGUP loop unroll factor(<int>)
Description
Specifies a loop to be unrolled.
Parameters
The factor indicates how many times to unroll the loop. If it is not specified, or specified as N (the total number of loop iterations), the loop will be fully unrolled. If it is specified as 2, the loop will be unrolled 2 times, where the number of loop iterations will be halved and the loop body will be replicated twice. If it is specified as 1, the loop will NOT be unrolled.
Parameter | Value | Optional | Default | Description |
---|---|---|---|---|
factor |
Integer | Yes | N (fully unroll) | Unroll count |
Position
Before the beginning of the loop.
Note
If there is a loop label, the pragma should be placed after the label.
Examples
Fully unroll a loop.
#pragma LEGUP loop unroll
for (int i = 0; i < 10; i++) {
...
}
Unroll the loop by 2 times only.
LOOP_LABEL:
#pragma LEGUP loop unroll factor(2)
while (i < 10) {
...
}
Small loops may be unrolled even without the unroll pragma. Make sure the loop is not unrolled.
#pragma LEGUP loop unroll factor(1)
for (int i = 0; i < 10; i++) {
...
}
5.9. Configure Scalar Argument Interface¶
Syntax
#pragma LEGUP interface argument(<arg_name>) type(simple) stable(<false|true>)
Description
This pragma configures the scalar input port.
The stable
option informs LegUp that the port can be held valid and unchanged throughout the whole
iteration of the execution and potentially can save register usage in the
generated module.
Note that the type
option is not configurable in the current release but
still needs to be specified if you wish to add the stable option.
If ‘stable’ is false, the pragma is not necessary because LegUp assumes not stable by default.
More details in Top-Level RTL Interface section.
Parameters
Parameter | Type | Optional | Default | Description |
---|---|---|---|---|
argument |
String | No | Argument name | |
type |
simple |
No | Interface type | |
stable |
true|false |
Yes | false |
true if the argument is stable |
Position
At the beginning of the function definition block.
Examples
int fun(int a) {
#pragma LEGUP interface argument(a) type(simple) stable(true)
...
}
5.10. Configure Argument as Memory Interface¶
Syntax
#pragma LEGUP interface argument(<arg_name>) type(memory) num_elements(<int>)
Description
This pragma specifies the memory interface type for an array/struct argument.
The array size can be specified or overridden (over the declared size in C++) by specifying the num_elements
option.
More details in Top-Level RTL Interface section.
Parameters
Parameter | Type | Optional | Default | Description |
---|---|---|---|---|
argument |
String | No | Argument name | |
type |
memory |
No | Interface type | |
num_elements |
Integer | Yes | Specifies the number of elements of the argument array. Can override the array size in the argument. |
Position
At the beginning of the function definition block.
Examples
int fun(int a[], int b[]) {
#pragma LEGUP interface argument(a) type(memory) num_elements(100)
#pragma LEGUP interface argument(b) type(memory)
...
}
5.11. Configure Global Variable as Memory Interface¶
Syntax
#pragma LEGUP interface variable(<var_name>) type(memory) num_elements(<int>)
Description
This pragma specifies the memory interface type for a shared array/struct global variable.
The array size can be specified or overridden (over the declared size in C++) by specifying the num_elements
option.
More details in Top-Level RTL Interface section.
Parameters
Parameter | Type | Optional | Default | Description |
---|---|---|---|---|
varaible |
String | No | Variable name | |
type |
memory |
No | Interface type | |
num_elements |
Integer | Yes | Specifies the number of elements of the variable array. Can override the array size of the variable. |
Position
Before the global variable declaration.
Examples
#pragma LEGUP interface variable(b) type(memory) num_elements(100)
int b[100];
int fun() {
...
}
5.12. Configure Argument as Scalar Memory Interface¶
Syntax
#pragma LEGUP interface argument(<arg_name>) type(memory) num_elements(<int>)
Description
This pragma specifies the scalar memory interface for an argument. The scalar memory interface is used by LegUp module to access external memory that has only one element. More details in Top-Level RTL Interface section.
Parameters
Parameter | Type | Optional | Default | Description |
---|---|---|---|---|
argument |
String | No | Argument name | |
type |
scalar_memory |
No | Interface type | |
num_elements |
Integer | Yes | Specifies the number of elements of the argument array. Can override the array size in the argument. |
Position
At the beginning of the function definition block.
Examples
int fun(int a[]) {
#pragma LEGUP interface argument(b) type(scalar_memory) num_elements(100)
...
}
5.13. Configure Global Variable as Scalar Memory Interface¶
Syntax
#pragma LEGUP interface variable(<var_name>) type(scalar_memory)
Description
This pragma specifies the scalar memory interface for a shared global variable. The scalar memory interface is used by LegUp module to access external memory that has only one element. More details in Top-Level RTL Interface section.
Parameters
Parameter | Type | Optional | Default | Description |
---|---|---|---|---|
variable |
String | No | Variable name | |
type |
scalar_memory |
No | Interface type |
Position
Before the global variable declaration.
Examples
#pragma LEGUP interface variable(b) type(scalar_memory)
int b[SIZE];
5.14. Configure Global as AXI4 Interface¶
Syntax
#pragma LEGUP interface variable(<var_name>) type(axi_slave) concurrent_access(true|false)
Description
This pragma specifies an AXI4 slave interface. When the concurrent_access option is set to true (default to false), the external logic can read/write the AXI4 slave interface while the LegUp module is running. The concurrent access will however reduce the LegUp module’s throughput to access the memory. More details in Top-Level RTL Interface section.
Parameters
Parameter | Value | Optional | Default | Description |
---|---|---|---|---|
variable |
String | No | Variable name | |
type |
axi_slave |
No | Interface type | |
concurrent_access |
true|false |
Yes | false |
Enable/disable concurrent access |
Position
Before the global variable declaration.
Examples
#pragma LEGUP interface variable(b) type(axi_slave) concurrent_access(true)
int b[SIZE];
5.15. Partition Memory¶
Syntax
#pragma LEGUP memory partition variable`(<arg_name>) type(complete|none) dim(<int>)
Description
This pragma specifies a variable to be partitioned. Dimension 0 corresponds to the right-most dimension of an array and higher dimensions correspond to leftward dimensions.
Parameters
Parameter | Value | Optional | Default | Description |
---|---|---|---|---|
variable |
String | No | Variable name | |
type |
complete|none |
Yes | complete |
Partition type |
dim |
Integer | Yes | 0 | Partition dimension |
Position
Before the global / local variable declaration.
Examples
#pragma LEGUP memory partition variable(b) type(none)
int b[100];
int fun(int *a) {
...
#pragma LEGUP memory partition variable(c) dim(1)
int c[100][100];
...
}
5.16. Partition Top-Level Interface¶
Syntax
#pragma LEGUP memory partition argument(<arg_name>) type(complete|none) dim(<int>)
Description
This pragma specifies a top-level argument to be partitioned. Dimension 0 corresponds to the right-most dimension of an array and higher dimensions correspond to leftward dimensions. Note that this only applies to top level functions.
Parameters
Parameter | Value | Optional | Default | Description |
---|---|---|---|---|
argument |
String | No | Argument name | |
type |
complete|none |
Yes | complete |
Partition type |
dim |
Integer | Yes | 0 | Partition dimension |
Position
At the beginning of the function definition block.
Examples
int sum(int *a, int *b) {
#pragma LEGUP function top
#pragma LEGUP memory partition argument(a) type(none)
#pragma LEGUP memory partition argument(b)
}
5.17. Contention-Free Memory Access¶
Syntax
#pragma LEGUP memory impl variable(<arg_name>) contention_free(true|false)
Description
The pragma is to be used for variables accessed by parallel functions (legup::thread
)
so that LegUp does not create arbiters for the specified variable.
The specified variable can still be accessed by multiple concurrently running functions, but without contention.
It will be the users’ responsibility to ensure at most one function may access the shared
variable in a clock cycle. If not specified, by default,
LegUp creates arbiters for variables that are accessed by parallel functions.
Parameters
Parameter | Value | Optional | Default | Description |
---|---|---|---|---|
variable |
String | No | Variable name | |
contention_free |
true - false |
Yes | false |
true for contention-free access |
Position
Before the global / local variable declaration.
Examples
#pragma LEGUP memory impl variable(b) contention_free(true)
int b[100];