Kerim Turak
Kerim Turak

Reputation: 127

The Zynq Book Tutorials Lab 4-C part adding directive problem

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.

enter image description here

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

Answers (2)

BZKN
BZKN

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:

  • Any function-level protocol that is specified.
  • Function arguments.
  • Global variables accessed by the top-level function and defined outside its scope.

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:


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:


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

Stefano Ribes
Stefano Ribes

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

Related Questions