AXI4 主接口 - 2021.2 Chinese

Vitis 高层次综合用户指南 (UG1399)

Document ID
UG1399
Release Date
2021-12-15
Version
2021.2 Chinese

AXI4 存储器映射 (m_axi) 接口允许内核在全局存储器(DDR、HBM 和 PLRAM)内读写数据。存储器映射接口便于跨加速应用的不同元素共享数据,例如,在主机与内核之间或者在加速器卡上的内核之间共享数据。请参阅 Github 上的 Vitis-HLS-Introductory-Examples/Interface/Memory,以获取这些概念的部分示例。

以下列出了 m_axi 接口的主要优势:
  • 此接口有独立的读取通道和写入通道。
  • 它支持基于突发的访问,潜在性能可达 ~17 GB/s
  • 它可为未完成传输事务提供支持

Vitis 内核流程中,默认情况下,会将 m_axi 接口分配到指针和实参。在此流程中,它支持以下默认功能:

  • 将指针和阵列实参自动映射到 m_axi 接口
  • Vitis 中,默认操作模式是 offset=slave,并且不应更改此模式
  • 所有指针和阵列实参都映射到单个接口捆绑,以节省器件资源,并且各端口在活动期间共享读取和写入访问
  • Vitis 流程中的默认对齐设置为 64 个字节
  • 默认情况下,最大读/写突发长度设置为 16
虽然 Vivado IP 流程默认不使用 m_axi 接口,但指定该接口时,它具有下列默认功能特性:
  • 默认操作模式为 offset=off ,但可对此进行更改,如 偏移和操作模式 中所述
  • 已分配的指针和阵列实参都映射到单个接口捆绑,以节省器件资源,并且在活动期间共享该接口
  • Vivado IP 流程中的默认对齐设置为 1 个字节
  • 默认情况下,最大读/写突发长度设置为 16

Vivado IP 流程和 Vitis 内核流程中,INTERFACE 编译指示或指令可用于按需修改默认值。有些自定义设置有助于提升设计性能,如 最优化 AXI 系统性能 中所述。

您可在阵列或指针/参考实参上使用 AXI4 主接口,Vitis HLS 通过以下任一模式来实现该接口:

  • 单独数据传输
  • 突发模式数据传输

对于单独数据传输,Vitis HLS 会为每个地址读取或写入单个数据元素。以下示例显示了单一读取操作和单一写入操作。在此示例中,Vitis HLS 可在 AXI 接口上生成 2 个地址,1 个用于读取单个数据值,另 1 个用于写入单个数据值。该接口会为每个地址传输 1 个数据值。

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

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

对于突发模式传输,Vitis HLS 会使用单一基址读取或写入数据,后接多次顺序数据采样,因此,该模式支持更高的数据吞吐量。使用 C memcpy 函数或经流水打拍的 for 循环时,即可执行突发模式操作。如需了解更多信息,请参阅 AXI 突发传输

重要: 仅当 C memcpy 函数用于在使用 AXI4 主接口指定的顶层函数实参上执行双向数据出入传输时,才支持对其进行综合。

以下示例显示了使用 memcpy 函数的突发模式副本。顶层函数实参 a 指定为 AXI4 主接口。

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));
}

对此示例进行综合时,会生成如下图所示接口。

注释: 在此图中,AXI4 接口处于折叠状态。
图 1. AXI4 接口

以下示例显示的代码与先前示例相同,但使用 for 循环来复制出数据:

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];
 }
}

使用 for 循环实现突发读取或写入时,请遵循如下要求:

  • 对循环进行流水打拍
  • 按升序访问地址
  • 请勿将访问置于条件语句内
  • 对于嵌套循环,请勿将循环平铺,因为这样有碍突发操作
注释: 在任一 for 循环内只允许 1 次读取和 1 次写入,除非端口捆绑在不同 AXI 端口内。以下示例显示了如何使用不同 AXI 接口以突发模式执行 2 次读取。

在以下示例中,Vitis HLS 将端口读取作为突发传输来实现。指定的端口 a 未使用 bundle 选项,并在默认 AXI 接口中实现。端口 b 是使用已命名的捆绑指定的,在名为 d2_port 的独立 AXI 接口中实现。

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];
 }
...
 }