Kernel Interfaces - 2021.2 English

Vitis Unified Software Platform Documentation: Application Acceleration Development (UG1393)

Document ID
UG1393
Release Date
2022-03-29
Version
2021.2 English
void cnn( int *pixel, // Input pixel
  int *weights, // Input Weight Matrix
  int *out, // Output pixel
  ... // Other input or Output ports

In the example above, the kernel function has three pointer parameters: pixel, weights, and out. By default the Vitis compiler will map these three parameters to the same AXI4 interface (m_axi).

The default interface mapping inferred by the compiler is equivalent to the following INTERFACE pragmas:

#pragma HLS INTERFACE m_axi port=pixel   offset=slave bundle=gmem
#pragma HLS INTERFACE m_axi port=weights offset=slave bundle=gmem
#pragma HLS INTERFACE m_axi port=out     offset=slave bundle=gmem
Tip: The inferred pragma is not added to the code by the tool; it is shown here to represent the default settings assigned to the interface port.

The bundle keyword on the INTERFACE pragma defines the name of the port. The system compiler will create a port for each unique bundle name, resulting in a compiled kernel object (XO) file that has a single AXI interface, m_axi_gmem. When the same bundle name is used for different interfaces, this results in these interfaces being mapped to same port.

Tip: The gmem name is short for global memory; however, it is not a keyword and is just used for consistency. You can assign your own names for the bundles.

Sharing ports helps save FPGA resources by eliminating AXI interfaces, but it can limit the performance of the kernel because all the memory transfers have to go through a single port. The m_axi port has independent READ and WRITE channels, so with a single m_axi port, you can do reads and writes simultaneously. However, the bandwidth and throughput of the kernel can be increased by creating multiple ports, using different bundle names, to connect to multiple memory banks. There are many options for configuring the INTERFACE, as described in . Some reasons to manually define an INTERFACE pragma in your code could include:

  • Specifying the bundle for the INTERFACE pragma to separate AXI signals into separate bundles.
  • Specifying the interface width to deviate from default int = 64 bytes (512-bits).
  • Specifying AXI properties for burst transactions.
void cnn( int *pixel, // Input pixel
  int *weights, // Input Weight Matrix
  int *out, // Output pixel
  ... // Other input or Output ports
		   
#pragma HLS INTERFACE m_axi port=pixel   offset=slave bundle=gmem
#pragma HLS INTERFACE m_axi port=weights offset=slave bundle=gmem1
#pragma HLS INTERFACE m_axi port=out     offset=slave bundle=gmem

In the example above, two bundle names create two distinct ports: gmem and gmem1. The kernel will access pixel and out data through the gmem port, while weights data will be accessed through the gmem1 port. As a result, the kernel will be able to make parallel accesses to pixel and weights, potentially improving the throughput of the kernel.

Important: Specify bundle= names using all lowercase characters, so you can assign it to a specific memory bank using the connectivity.sp option.

The INTERFACE pragma is used during v++ compilation, resulting in a compiled kernel object (XO) file with two separate AXI interfaces, m_axi_gmem and m_axi_gmem1, that can be connected to global memory as needed. During system compiler linking, the separate interfaces can be mapped to different global memory banks using the connectivity.sp option in a configuration file, as described in Mapping Kernel Ports to Memory.