We have detected your current browser version is not the latest one. Xilinx.com uses the latest web technologies to bring you the best online experience possible. Please upgrade to a Xilinx.com supported browser:Chrome, Firefox, Internet Explorer 11, Safari. Thank you!

Showing results for 
Search instead for 
Did you mean: 

Adam Taylor’s MicroZed Chronicles Part 145: Cracking Open HLS part 2

Xilinx Employee
Xilinx Employee
0 0 43.8K


By Adam Taylor



Having introduced the flow we use for Vivado HLS in my previous blog post, I now want to spend a little time looking at some of the more basic functions that we skipped over last week.


When we used SDSoC in the previous blog post about synthesis, we did not discuss the rules we must follow when writing the module to be synthesized by Vivado HLS. These rules are not surprising really because SDSoC uses the Vivado HLS engine. The rules are:


  • The function must be self-contained; that is it must contain the entire design to be implemented.
  • You cannot use system calls or dynamic memory allocations.
  • You must use fixed or bounded constructs
  • The function must be unambiguous in its use of the bounds.


With the ground rules set for creating the function, I now want to look at how we define the interface to our function in the final RTL module. Generally speaking, there are two interface types to consider:



  • Block-Level Interface – This is the default for a generic interface that provides clock and reset inputs along with start, ready, idle, and done handshaking signals.


  • Port-Level Interface – This interface implements a more complete I/O protocol on the data ports such as AXI, AXI streaming, or AXI Lite.



We can use the simple Block-Level interface if we want, without the need for a Port-Level interface. If this is the case, we will need to ensure that the data on the block’s inputs is stable for the I/O operation.


Looking at the example from last week, the synthesised results produced a very simple entity that implemented a Block-Level Interface. 






Block-Level Interface from the example in the MicroZed Chronicles, Part 144



There are three options we can apply when we use the Block-Level Interface. The first, AP_NONE, will generate no interfaces beyond the ones defined within the module. You can use this option for very simple modules. However, should you choose this option, you will not be able to use Co-Simulation to verify your design.







HLS results with AP_NONE set



The default option for the Block-Level interface is AP_CNTRL_HS, which provides the required clock, reset, and handshaking ports needed to create a handshake for the block, as discussed above. The final option, AP_CNTRL_CHAIN, is very similar in that it provides the same handshaking signals as AP_CNTRL_HS but adds the ability to chain HLS blocks together by adding a continue signal, which can be used to stall an upstream block if necessary.


Depending on the structure we create with our HLS module, there are a number of other options we can apply to the Block-Level interface such as “MEMORY” or “FIFO,” for example.


We can control these options using pragmas to define the block’s I/O architecture or we can use the directives window, which opens a dialog allowing us to select the I/O architecture that we want to use.


By highlighting the top-level function in the directives window, we can select the Block-Level interface or the AXI interface. We can also do this for the inputs by selecting the required standards.






Directives Window – Set to use AXI Lite





Directives Insertion Window for Input A



If we decide to use a pragma-based approach to define out interfaces, we can do so as shown below which creates AXI Streaming and AXI Lite interfaces.






Defining an Interface using pragmas



Setting our simple example from last week to provide an AXI Lite interface using the directives window, you can see the resultant interface below:





AXI Slave Interface for example from the MicroZed Chronicles, Part 144



Depending upon the type of AXI interface (indeed any interface) we wish to implement, we need to consider if we are passing the arguments to the function by either value or reference. Here’s a figure that summarizes the argument-passing options for different argument types:





Interface type and Function passing style for different interfaces




Having covered how we can control our resultant module interface, next time we will look at the libraries available to help us accelerate our HLS developments.



Code is available on Github as always.


If you want E book or hardback versions of previous MicroZed chronicle blogs, you can get them below.




  • First Year E Book here
  • First Year Hardback here.




MicroZed Chronicles hardcopy.jpg 



  • Second Year E Book here
  • Second Year Hardback here




MicroZed Chronicles Second Year.jpg 




All of Adam Taylor’s MicroZed Chronicles are cataloged here.




Tags (2)