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
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 突发传输。
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));
}
对此示例进行综合时,会生成如下图所示接口。
以下示例显示的代码与先前示例相同,但使用 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];
}
...
}