AXI4 Master Interface - 2022.1 English

Vitis High-Level Synthesis User Guide (UG1399)

Document ID
UG1399
Release Date
2022-06-07
Version
2022.1 English

AXI4 memory-mapped (m_axi) interfaces allow kernels to read and write data in global memory (DDR, HBM, PLRAM). Memory-mapped interfaces are a convenient way of sharing data across different elements of the accelerated application, such as between the host and kernel, or between kernels on the accelerator card. Refer to Vitis-HLS-Introductory-Examples/Interface/Memory on Github for examples of some of these concepts.

The main advantages for m_axi interfaces are listed below:
  • The interface has a separate and independent read and write channels
  • It supports burst-based accesses with potential performance of ~17 GB/s
  • It provides support for outstanding transactions

In the Vitis Kernel flow the m_axi interface is assigned by default to pointer and array arguments. In this flow it supports the following default features:

  • Pointer and array arguments are automatically mapped to the m_axi interface
  • The default mode of operation is offset=slave in the Vitis flow and should not be changed
  • All pointer and array arguments are mapped to a single interface bundle to conserve device resources, and ports share read and write access across the time it is active
  • The default alignment in the Vitis flow is set to 64 bytes
  • The maximum read/write burst length is set to 16 by default
While not used by default in the Vivado IP flow, when the m_axi interface is specified it has the following default features:
  • The default operation mode is offset=off but you can change it as described in Offset and Modes of Operation
  • Assigned pointer and array arguments are mapped to a single interface bundle to conserve device resources, and share the interface across the time it is active
  • The default alignment in Vivado IP flow is set to 1 byte
  • The maximum read/write burst length is set to 16 by default

In both the Vivado IP flow and Vitis kernel flow, the INTERFACE pragma or directive can be used to modify default values as needed. Some customization can help improve design performance as described in Optimizing AXI System Performance.

You can use an AXI4 master interface on array or pointer/reference arguments, which Vitis HLS implements in one of the following modes:

  • Individual data transfers
  • Burst mode data transfers

With individual data transfers, Vitis HLS reads or writes a single element of data for each address. The following example shows a single read and single write operation. In this example, Vitis HLS generates an address on the AXI interface to read a single data value and an address to write a single data value. The interface transfers one data value per address.

void bus (int *d) {
 static int acc = 0;

 acc += *d;
 *d  = acc;
}

With burst mode transfers, Vitis HLS reads or writes data using a single base address followed by multiple sequential data samples, which makes this mode capable of higher data throughput. Burst mode of operation is possible when you use the C memcpy function or a pipelined for loop. Refer to AXI Burst Transfers for more information.

Important: The C memcpy function is only supported for synthesis when used to transfer data to or from a top-level function argument specified with an AXI4 master interface.

The following example shows a copy of burst mode using the memcpy function. The top-level function argument a is specified as an AXI4 master interface.

void example(volatile int *a){

//Port a is assigned to an AXI4 master interface
#pragma HLS INTERFACE mode=m_axi depth=50 port=a
#pragma HLS INTERFACE mode=s_axilite port=return

 int i;
 int buff[50];

//memcpy creates a burst access to memory
 memcpy(buff,(const int*)a,50*sizeof(int));

 for(i=0; i < 50; i++){
 buff[i] = buff[i] + 100;
 }

 memcpy((int *)a,buff,50*sizeof(int));
}

When this example is synthesized, it results in the interface shown in the following figure.

Note: In this figure, the AXI4 interfaces are collapsed.
Figure 1. AXI4 Interface

The following example shows the same code as the preceding example but uses a for loop to copy the data out:

void example(volatile int *a){

#pragma HLS INTERFACE mode=m_axi depth=50 port=a
#pragma HLS INTERFACE mode=s_axilite port=return

//Port a is assigned to an AXI4 master interface

 int i;
 int buff[50];

//memcpy creates a burst access to memory
 memcpy(buff,(const int*)a,50*sizeof(int));

 for(i=0; i < 50; i++){
 buff[i] = buff[i] + 100;
 }

 for(i=0; i < 50; i++){
#pragma HLS PIPELINE
 a[i] = buff[i];
 }
}

When using a for loop to implement burst reads or writes, follow these requirements:

  • Pipeline the loop
  • Access addresses in increasing order
  • Do not place accesses inside a conditional statement
  • For nested loops, do not flatten loops, because this inhibits the burst operation
Note: Only one read and one write is allowed in a for loop unless the ports are bundled in different AXI ports. The following example shows how to perform two reads in burst mode using different AXI interfaces.

In the following example, Vitis HLS implements the port reads as burst transfers. Port a is specified without using the bundle option and is implemented in the default AXI interface. Port b is specified using a named bundle and is implemented in a separate AXI interface called d2_port.

void example(volatile int *a, int *b){

#pragma HLS INTERFACE s_axilite port=return 
#pragma HLS INTERFACE mode=m_axi depth=50 port=a
#pragma HLS INTERFACE mode=m_axi depth=50 port=b bundle=d2_port


 int i;
 int buff[50];

//copy data in
 for(i=0; i < 50; i++){
#pragma HLS PIPELINE
 buff[i] = a[i] + b[i];
 }
...
 }