void cnn( int *pixel, // Input pixel
int *weights, // Input Weight Matrix
int *out, // Output pixel
... // Other input or Output ports
在以上示例中,内核函数具有 3 个指针参数:pixel
、weights
和 out
。默认情况下,Vitis 编译器将把这 3 个参数映射到同一个 AXI4 接口 (m_axi
)。
编译器推断的默认接口映射等同于以下 INTERFACE 编译指示:
#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
INTERFACE 编译指示上的 bundle
关键字用于定义端口的名称。Vitis 编译器将为每个独立的捆绑包 (bundle) 名称创建一个端口,从而生成已编译的内核对象 (XO) 文件,此文件中包含单一 AXI 接口 m_axi_gmem
。当针对不同接口使用相同 bundle
名称时,会导致将这些接口映射到同一个端口。
gmem
名称表示全局存储器,但它并非关键字,仅用于保持一致性。您可以为捆绑包分配自己的名称。共享端口有助于通过消除 AXI 接口来节省 FPGA 资源,但它可能限制内核性能,因为所有存储器传输都必须经过同一个端口。m_axi
端口具有独立的 READ 通道和 WRITE 通道,因此通过单一 m_axi
端口即可同时执行读写操作。但通过创建多个端口、使用不同捆绑包名称连接到多个存储体,可能导致内核带宽和吞吐量增加。有许多选项可用于配置 INTERFACE,如
pragma HLS interface
中所述。在代码中手动定义 INTERFACE 编译指示的原因包括但不限于:
- 为 INTERFACE 编译指示指定捆绑包,用于将 AXI 信号拆分为多个独立的捆绑包。
- 指定接口宽度不同于默认 int = 64 字节(512 位)。
- 为突发传输事务指定 AXI 属性。
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
在以上示例中,2 个 bundle
名称会创建 2 个不同端口:gmem
和 gmem1
。内核将通过 gmem
端口访问 pixel
和 out
数据,而 weights
数据则将通过 gmem1
端口来访问。因此,内核将能够并行访问 pixel
和 weights
,从而提升内核吞吐量。
bundle=
名称,以便您使用 connectivity.sp 选项将其分配给特定存储体。在 v++
编译期间使用 INTERFACE 编译指示可生成编译后的内核对象 (XO) 文件,其中包含 2 个独立的 AXI 接口:m_axi_gmem
和 m_axi_gmem1
,这 2 个接口可按需连接到全局存储器。链接期间,可在配置文件中使用 connectivity.sp 选项将不同接口映射到不同的全局存储体,如 将内核端口映射到存储器 中所述。