MicroZed Chronicles: High-Level Synthesis Interfacing
High-Level Synthesis can provide us a significant advantage when we are working in the data plane (e.g., signal / image processing and algorithmic implementations). One of the most powerful and flexible elements of HLS implementations is in its interfacing. We can control this via pragmas and compiler directives and it enables us to use the same core / algorithm across several different implementations.
When we develop Vitis HLS solutions, we can generate IP blocks for either Vitis or Vivado. If we are intending to create a Vitis Kernel, the output will be a XO file. If we are compiling for Vivado, we will be creating an IP XACT description. The flow we are using with Vitis or Vivado determines the interfaces we will be able to use for that design.
Vitis HLS blocks have two classes of interfaces. These can be defined as follows:
1. Port-Level I/O Protocols – These define how the data is passed and communicated to the HLS block, including when data is valid and can be read or written.
2. Block-Level I/O Protocols – These define the operation, status, and synchronization of the HLS block.
One of the ways that interfacing is controlled in Vitis HLS is by the type of argument used. These will define the Port-Level I/O Protocols which can be used.
These arguments can be implemented in a range of different interface protocols. When
we are targeting a Vitis kernel development, the interfaces must be AXI based. As a result, the port-level interfaces will be either AXI Lite, AXI MM, or AXIS.
Targeting Vivado designs provides several more interface solutions along with AXI which can be used to interface with different logical structures within RTL or IP Integrator designs. These different interfaces include the following:
Of course, when AXI interfaces are used, software drivers will be provided for software applications which use the processing systems of SoC or MicroBlaze solutions.
While we are all familiar with AXI interfaces, we might not be so familiar with the AP_x interfaces defined above. Let’s take a quick look at them.
ap_none - The simplest interface with no other signals associated with the argument. This requires that producer blocks provide the data at the right time when the HLS IP module executes and that they are held for the appropriate time. Consumer blocks must also read the output port at the right time.
ap_vld – Input and output data which has an associated valid signal.
Ap_ovld - INOUT data which has ap_none on input and ap_vld on output interfaces.
ap_ack – Handshaking signal which is used with ap_vld to acknowledge the valid signal. The core stalls on outputs until the ack signal is received.
ap_memory – Implements a standard Block RAM interface with data, address, CE and WE Ports.
ap_fifo – Implements a FIFO interface as either input or output.
So far, the interfaces we have examined are those which we can use to get data into and out of the module using the Port-Level IO Protocols.
We also need to be able to control the HLS block and ensure it can be started and stopped, and that status of the block is provided. These three interfaces are called Block-Level Control protocols:
ap_ctrl_chan – Pipelined execution allows processing of data as soon as the kernel is ready.
ap_ctrl_he – Sequential execution allows the kernel to execute once before starting the next.
ap_ctrl_none – Data drive execution allows the core to process data when available and stall when it is not.
The default for Vitis Kernels is ap_ctrl_chain versus the Vivado default of ap_ctrl_hs. These interfaces can be directly synthesized as ports on the HLS IP block. They can also be included in a AXI Lite control interface which is, of course, the default for the Vitis flow.
Now that we understand a little more about HLS IP and interfacing, we can optimize and ensure that we select the most appropriate interface for our next HLS development.