UPGRADE YOUR BROWSER

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!

cancel
Showing results for 
Search instead for 
Did you mean: 

MicroZed Chronicles: Understanding High Level Synthesis Interfacing

Xilinx Employee
Xilinx Employee
0 0 755

This content is republished from the MicroZed Chronicles, with permission from the author and Hackster.io.

In my recent audio processing project on Hackster.io on Hackster.io I created a simple High Level Synthesis (HLS) IP Block to which filtering, and effects could be added. To ensure this IP core would interface with the I2S TX and RX IP Cores from Xilinx I needed to create AXI Streaming interfaces on the HLS IP Block.

 

Fig1 Audio Processing Block Diagram.jpg

Doing this got me thinking a little about how we perform interfacing using Vivado HLS. So in this blog I am going to explain how we control what interface our HLS IP Block uses.

We are going to start right at the beginning with a simple HLS IP Block which performs simple addition. For all these examples we are going to be targeting the Zedboard.

In HLS the interfaces on the synthesized block are defined by the arguments we pass to our C/C++ function along with any return parameters. The direction depends upon how they are used in the function, however if we use pointers or arrays these can be used as IO. 

In addition to the inputs and outputs, an ap_cntrl interface will also be created in the synthesised HLS IP Block. This control interface provides clock, reset and handshaking for example enabling the block to be started (ap_start), reporting when a result is ready (ap_done), when the block is ready to take a new input (ap_ready) and if the block is idle (ap_idle). 

However, even the behaviour of the ap_cntrl interface depends upon our solution setting and the code itself.Fig2 New Vivado HLS Project.png

For the first example we are going to use a slow clock, at 100 ns. The first stage of the HLS process is scheduling, that is when the HLS tool assigns operations to clock cycles. If the clock period is long enough, then it may not even need registers, resulting in a combinatorial design.

The clock period defined in the solution settings will therefore have an impact on the synthesis and the interfacing. The very simple code below when synthesized with a clock period of 100 ns will result in a combinatorial implementation.Fig3 example_1.png

As such there is no need for the handshaking signals, if you examine the output VHDL or Verilog you will see while the signals exist, they simply assign the inputs and constants to the outputs. Another indication the synthesised block is combinatorial is there is no clock or reset input.Fig4 RTL Ports.png

If we change the clock period to one of 5 ns, we will see the reported latency increase as the HLS tool inserts a register stage. This is also reflected in the interfaces with the clock and reset now being present and the behaviour of the handshaking signals being updated to correctly implement the required handshaking.Fig5 RTL Ports 2.png

This simple ap_cntrl port therefore gives us the ability to work with the HLS IP block in our design and for that reason is called the block level protocol.

Of course, for many applications we want to be able to interface our HLS IP block with designs which use AXI or Memory interfaces. 

This is where the port level protocols come in and for these the type of C construct used for the variable becomes important. Port level protocols enable us to define for each port on the HLS block a specific interface protocol such as AXI Lite, AXI, FIFO, BRAM etc.

Updating the simple example above to use arrays we can modify the interfaces to use FIFO type interfaces. We define this interface type using the pragma

#pragma HLS interface ap_fifo depth = <depth> port =<port>

To define the interface type we can either type these in by hand as above or use the directives window on the right-handside of the HLS window.

Fig6 Directive Editor window.png

As this is a port level interface, we need to define this for each of the interfaces in the HLS block.Fig7 example_1 2.pngFig8 RTL Ports 3.png

When we run this through HLS, we will see the synthesis report generated which defines the interfaces implemented. As you can see the FIFO interfaces have been implemented.

FIFO interfaces are interesting when we want to stream data between HLS modules.

Often however, we want to implement interfaces which use a flavour of AXI (s_axilite, axis, s_axi, m_axi) we can use the same approach to implement AXIS and AXI Master and Slave Interfaces.Fig9 AXIS.pngFig10 AXIS RTL Ports.png

If we are working with a heterogeneous SoC such as the Zynq or Zynq MPSoC we may want to use a AXI lite for control of the HLS IP block and not the discrete block interface. We can do this by using the pragma as below 

#pragma s_axilite port=return bundle=cmd

This will create not only a AXI lite interface which allows us to control the HLS IP core, it will also generate the software drivers necessary to work with the HLS IP core in SDK.Fig11 Function Prototypes.png

If there is no need to stop and start the HLS IP core then we can use the pragma 

#pragma HLS interface ap_ctrl_none port=return

Understanding, how we can control the interfaces on our HLS IP blocks means when we are working with HLS we can ensure our IP block easily integrates with the rest of our design saving time and effort in creation of conversion blocks.

 

See My FPGA / SoC Projects: Adam Taylor on Hackster.io

Get the Code: ATaylorCEngFIET (Adam Taylor)

Access the MicroZed Chronicles Archives with over 280 articles on the Zynq / Zynq MpSoC updated weekly at MicroZed Chronicles.