Reputation: 127
I am trying run zynq book tutorials lab 4 and c part in vivado hls (hls included vitis in new version) but when I right click in the step of adding directive as described in the book, the add directive window does not open. I tried this separately in 2015.1, 2018.3 and 2021.2 versions of vivado, the result is the same in all of them.The step I'm having trouble with is as follows.Tutorial Book Link is here. Although I researched the problem a lot on the internet, there was not much result, but someone who encountered this problem in xilinx's forum explained in this link He mentioned that this is a bug and that the same operation can be added to the c file with the #pragma command line.Since I am new to these issues, I would appreciate if you could help me how to add the directive by using the #pragma command mentioned in the step (m) in the tutorial.
The c code mentioned is as follows.
void nco (ap_fixed<16,2> *sine_sample, ap_ufixed<16,12> step_size){
ap_ufixed<12,12> address;
temp+=step_size; // Accumulator. Values will wrap around on overflow.
address = ap_ufixed<12,12>(temp); // Cast address to a 12-bit integer value.
*sine_sample = sine_lut[(int)address]; // Assign sign sample from LUT based on current address
}
Upvotes: 1
Views: 723
Reputation: 1729
Yes, it happens at times that Vivado (now Vitis) HLS GUI does not show the directive. However, it's a blessing in disguise since it compels you to manually add the pragmas in your code and as a result you may actually understand the pragmas and their effect on the generated RTL. From the example above it seems you will be using INTERFACE
and RESOURCE
pragmas.
As a HLS enthusiast, I would like to briefly start with the basic understanding and syntax of both pragmas and then you can check my solution to your question in the end.
pragma HLS interface :
The INTERFACE
pragma specifies how RTL ports are created from the function definition during interface synthesis .
The ports in the RTL implementation are derived from:
Below is the syntax of the INTERFACE
pragma:
Syntax:
#pragma HLS interface <mode> port=<name> bundle=<string> \
register register_mode=<mode> depth=<int> offset=<string> \
clock=<string> name=<string> \
num_read_outstanding=<int> num_write_outstanding=<int> \
max_read_burst_length=<int> max_write_burst_length=<int>
In order to explicitly understand each parameter in the syntax please do read the details in following references:
page 102 to 107 of Vivado HLS Optimization Methodology Guide for your better understanding.
From page 438 on wards of Vivado Design Suite User Guide High-Level Synthesis about set_directive_interface for your better understanding.
pragma HLS resource:
Specify that a specific library resource (core) is used to implement a variable (array, arithmetic operation or function argument) in the RTL. If the RESOURCE
pragma is not specified, Vivado HLS determines the resource to use.
#pragma HLS resource variable=<variable> core=<core>\
latency=<int>
In order to explicitly understand each parameter in the syntax please do read the details in following references:
page 120 to 121 of Vivado HLS Optimization Methodology Guide for your better understanding about pragma HLS resource.
On page 452 and page 453 of Vivado Design Suite User Guide High-Level Synthesis about set_directive_interface for your better understanding.
Your solution:
Updated: Now I again post this solution after running the code myself on my Vivado HLS and hence more clarification:
void nco (ap_fixed<16,2> *sine_sample, ap_ufixed<16,12> step_size) {
#pragma HLS RESOURCE variable=sine_sample core=AXI4LiteS
#pragma HLS RESOURCE variable=step_size core=AXI4LiteS
#pragma HLS INTERFACE ap_ctrl_none port=return
#pragma HLS RESOURCE variable=return core=AXI4LiteS
// Define the pcore interface and group into AXI4 slave "slv0"
/* Value to hold the current address value of the sine LUT
* 12-bit unsigned fixed-point, all integer bits.
* Overflow is set to "wrap around" by default. */
ap_ufixed<12,12> address;
temp+=step_size; // Accumulator. Values will wrap around on overflow.
address = ap_ufixed<12,12>(temp); // Cast address to a 12-bit integer value.
*sine_sample = sine_lut[(int)address]; // Assign sign sample from LUT based on current address
}
On some Vivado HLS versions the above solution may give warning like following:
WARNING: [HLS 200-41] Resource core 'AXI4LiteS' on port '&sine_sample' is deprecated. Please use the interface directive to specify the AXI interface.
Hence, we use INTERFACE
directive for all variables as following that will give no warnings:
void nco (ap_fixed<16,2> *sine_sample, ap_ufixed<16,12> step_size) {
#pragma HLS INTERFACE s_axilite port=sine_sample
#pragma HLS INTERFACE s_axilite port=step_size
#pragma HLS INTERFACE s_axilite port=return
#pragma HLS INTERFACE ap_ctrl_none port=return
// Define the pcore interface and group into AXI4 slave "slv0"
/* Value to hold the current address value of the sine LUT
* 12-bit unsigned fixed-point, all integer bits.
* Overflow is set to "wrap around" by default. */
ap_ufixed<12,12> address;
temp+=step_size; // Accumulator. Values will wrap around on overflow.
address = ap_ufixed<12,12>(temp); // Cast address to a 12-bit integer value.
*sine_sample = sine_lut[(int)address]; // Assign sign sample from LUT based on current address
}
Upvotes: 2
Reputation: 359
I'll try to give you a more pedagogical answer, rather than a specific one to your problem.
The idea of Xilinx HLS-specific pragmas is to guide the HLS "compiler" in generating a hardware component close to the designer intentions. As such, there exist a lot of those HLS pragmas and the Xilinx Vitis-HLS User Guide is full of references to look up to.
In practice, according to the pragma you want to use, you need to place it accordingly in your code (after all, pragmas are just as any other line of code). For instance, in your code snippet:
void nco (ap_fixed<16,2> *sine_sample, ap_ufixed<16,12> step_size){
#pragma HLS INTERFACE port=sine_sample ...
#pragma HLS INTERFACE port=step_size ...
ap_ufixed<12,12> address;
static ap_ufixed<12,12> temp = 0;
#pragma HLS RESOURCE variable=temp ...
temp += step_size; // Accumulator. Values will wrap around on overflow.
address = ap_ufixed<12,12>(temp); // Cast address to a 12-bit integer value.
*sine_sample = sine_lut[(int)address]; // Assign sign sample from LUT based on current
}
The pragma INTERFACE
can be placed anywhere within your function since it's referring to the function arguments and they're available everywhere in the function scope. On the other side instead, the pragma RESOURCE
must be placed after defining the variable is bounded to, i.e. temp
.
Another example where pragma order matters even more is in loops and nested loops. For example, imagine placing the pragma PIPELINE
in these three different positions:
#pragma HLS PIPELINE II=1 // It will appy the pipeline to the whole function, fully unrolls both N-loop and M-loop
for (int i = 0; i < N; ++i) {
#pragma HLS PIPELINE II=1 // It will appy the pipeline to the N-loop and fully unroll the M loop
for (int j = 0; j < M; ++j) {
#pragma HLS PIPELINE II=1 // It will appy the pipeline to M-loop only, keeping both the N-loop and M-loop rolled, but pipelined
// ...
}
}
Note, in the example, imagine only one of the pragmas commented out at the time. I'm putting them together just for the sake of the example (all active in this way won't make much sense and I believe the function-level pragma will simply take over the other ones).
In general, I would recommend you to carefully read the documentation of each pragma you intend to use and how and where to place it in your code.
Good luck!
Upvotes: 1