适用于 Vitis 内核流程的接口 - 2023.2 简体中文

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

Document ID
UG1399
Release Date
2023-12-18
Version
2023.2 简体中文

Vitis 内核流程为已编译的内核对象 (.xo) 提供支持,以便从主机应用和 Xilinx Runtime (XRT) 来执行软件控制。如 Vitis 统一软件平台文档中的 PL 内核属性 中所述,此流程具有非常具体的接口要求,Vitis HLS 必须满足这些要求。

Vitis HLS 支持多种存储器、串流和寄存器接口范例,其中每个范例都遵循某个接口协议并使用适配器来与外部世界进行通信。
  • 存储器范例 (m_axi):内核通过存储器(如 DDR、HBM、PLRAM/BRAM/URAM)来访问数据

  • 串流范例 (axis):数据从其他串流源(例如,视频处理器或其他内核)串流至内核中,也可从该内核流出。

  • 寄存器范例 (s_axilite):内核通过寄存器接口来访问数据,软件则通过寄存器读/写来访问数据。

提示: AXI 协议要求采用低电平有效复位。本工具将定义此复位并显示警告,前提是 syn.rtl.reset_level 设为高电平有效状态(默认设置)。

Vitis 内核流程默认会实现下列接口:

C 语言实参类型 范例 接口协议 (I/O/Inout)
标量(通过值来传递) 寄存器 AXI4‑Lite (s_axilite)
阵列 存储器 AXI4 存储器映射 (m_axi)
指向阵列的指针 存储器 m_axi
指向标量的指针 寄存器 s_axilite
参考 寄存器 s_axilite
hls::stream 串流 AXI4‑Stream (axis)

如上表所示,指向阵列的指针是作为 m_axi 接口(用于数据传输)来实现的,指向标量的指针则是使用 s_axilite 接口来实现的。作为常量来传递的标量值不需要读取权限,而指向标量值的指针则需要读取和写入权限。s_axilite 接口会根据 C 语言实参类型来实现一项额外的内部协议。此内部实现可使用 Vivado IP 流程的端口级协议 来控制。但如非必要,不应在 Vitis 内核流程中修改默认端口协议。

注释: 当元素需要不同接口类型时,Vitis HLS 将不会自动推断结构体/类的成员元素的默认接口。例如,当某个结构体的某个元素需要串流接口,而另一个成员元素需要 s_axilite 接口时,就是如此。您不能依靠默认接口分配,而必须为结构体的每个元素显式定义 INTERFACE 编译指示。如未定义任何 INTERFACE 编译指示或指令,则 Vitis HLS 将发出以下错误消息:
ERROR: [HLS 214-312] Vitis mode requires explicit INTERFACE 
pragmas for structs in the interface. Please add one INTERFACE pragma for each struct 
member field for argument 'd' of function 'dut(A&)' (example.cpp:19:0)

Vitis 内核流程的默认执行模式为流水打拍执行,即启用内核的重叠执行以改善吞吐量。这是通过 s_axilite 接口上的 ap_ctrl_chain 块控制协议来指定的。

提示: Vitis 环境支持含所有受支持的块控制协议的内核,如 块级控制协议 中所述。

以下代码中的 vadd 函数提供了接口综合的示例。

#define VDATA_SIZE 16

typedef struct v_datatype { unsigned int data[VDATA_SIZE]; } v_dt;

extern "C" {
void vadd(const v_dt* in1, // Read-Only Vector 1
          const v_dt* in2, // Read-Only Vector 2
          v_dt* out_r, // Output Result for Addition
          const unsigned int size // Size in integer 
) {

   unsigned int vSize = ((size - 1) / VDATA_SIZE) + 1;

   // Auto-pipeline is going to apply pipeline to this loop
   vadd1:
   for (int i = 0; i < vSize; i++) {
      vadd2:
      for (int k = 0; k < VDATA_SIZE; k++) {
         out_r[i].data[k] = in1[i].data[k] + in2[i].data[k];
      }
   }
}
}

vadd 函数包括:

  • 两个指针输入:in1in2
  • 指针输出:out_r,结果写入其中
  • 标量值 size

如果 Vitis HLSVitis 内核流程使用默认接口综合设置,那么设计将综合到含下图所示端口和接口的 RTL 块中。

图 1. 默认接口综合后的 RTL 端口

该工具会在 RTL 上创建三种类型的接口端口,以处理数据流和控制流。

  • 时钟、复位和中断端口:在内核中添加 ap_clkap_rst_ninterrupt
  • AXI4‑Lite 接口:s_axi_control 接口中包含标量实参(如 size)、管理 m_axi 接口的地址偏移并定义块控制协议。
  • AXI4 存储器映射接口:m_axi_gmem 接口,其中包含指针实参:in1in2out_r

Vitis 的 M_AXI 接口的详细信息

AXI4 存储器映射 (m_axi) 接口允许内核在全局存储器(DDR、HBM 和 PLRAM)中读取和写入数据,存储器映射接口便于跨加速应用的不同元素进行数据共享,例如,在主机与内核之间或者在加速器卡上的内核之间。以下列出了 m_axi 接口的主要优势:
  • 每个接口均有独立的读取通道和写入通道
  • 它支持基于突发的访问
  • 它可提供未完成传输事务队列
认识突发访问
AXI4 存储器映射接口支持高达 4K 字节的高吞吐量突发,且只需单一地址阶段即可。对于突发模式传输,Vitis HLS 会使用单一基址读取或写入数据,后接多次顺序数据采样,因此,该模式支持更高的数据吞吐量。使用流水打拍的 for 循环来复制存储器时,可使用操作的突发模式。如需了解更多信息,请参阅 控制 AXI4 突发行为AXI 突发传输
自动端口拓宽和端口宽度对齐

端口宽度自动调整 中所述,Vitis HLS 能够自动拓宽端口宽度以便执行数据传输并改善突发访问,前提是该工具可发现突发访问。因此,突发所需的所有前置条件(如 AXI 突发传输 中所述)都必须得到满足后才能调整端口大小。

Vitis 内核流程中,默认启用端口宽度自动调整,可采用以下配置命令来执行此操作(请注意,其中一条命令指定为位,另一条命令则指定为字节):
syn.interface.m_axi_max_widen_bitwidth=512
syn.interface.m_axi_alignment_byte_size=64
偏移规则
重要:Vitis 内核流程中,操作的默认模式为 syn.interface.m_axi_offset=directsyn.interface.default_slave_interface=s_axilite,并且不应更改。

正确指定偏移才能将 HLS 内核正确集成到 Vitis 系统中。如需了解更多信息,请参阅 偏移和操作模式

捆绑接口 - 性能对比资源使用情况

默认情况下,Vitis HLS 将具有兼容选项的函数实参组合到单个 m_axi 接口适配器中,如 M_AXI 捆绑 中所述。将端口捆绑到单个接口中可消除 AXI4 逻辑,因而有助于节省器件资源,在拥塞的设计内工作时可能需要这些资源。

但是,单一接口捆绑可能会限制内核性能,因为所有存储器传输都必须穿越单个接口。m_axi 接口具有独立的读取 (READ) 和写入 (WRITE) 通道,因此单个接口即可同时执行读取和写入,但仅限单一位置。使用多个捆绑即可创建多个接口以连接到存储体,从而能够增大内核带宽和吞吐量。

Vitis 的 S_AXILITE 接口的详细信息

在 C++ 语言中,从父函数调用函数时,该函数就会开始处理数据。函数调用将在调用时推送到栈上,处理完成时则会从栈中移除以便将控制权返还给调用函数。此进程可确保父函数明确知晓子函数的状态。

由于主机和内核在 Vitis 内核流程中占用两个独立的计算空间,因此“栈”由 Xilinx Runtime (XRT) 来管理,通信则通过 s_axilite 接口来管理。此内核由软件通过 XRT 来控制,方法是读取和写入 s_axilite 接口的控制寄存器,如 S_AXILITE 控制寄存器映射 中所述。此接口具有下列特性:

控制协议
块控制协议可定义 s_axilite 接口中的控制寄存器,以便您设置控制信号来管理内核的执行和操作。
标量实参
内核上的标量数据为典型输入,可视作为编程常量或参数。主机应用通过 s_axilite 接口来传输这些值。
指向标量实参的指针
如果指向标量值的指针分配给 s_axilite 接口,那么 Vitis HLS 允许您在该指针上执行读取或写入。指针默认分配给 m_axi 接口,因此您必须使用 INTERFACE 编译指示或指令将该指针手动分配给 s_axilite
int top(int *a, int *b) {
#pragma HLS interface s_axilite port=a
偏移规则
注释: Vitis 内核流程会判定所需的偏移。请勿在该流程中指定 offset 选项。
捆绑规则
Vitis 内核流程仅支持单个 s_axilite 接口,这意味着所有 s_axilite 接口都必须捆绑在一起。
  • 如不指定捆绑,那么该工具会自动创建名为 Control 的默认捆绑。
  • 如果您出于某些原因想要手动指定捆绑名称,则必须将此捆绑应用于所有 s_axilite 接口以创建单一捆绑。

Vitis 的 AXIS 接口的详细信息

AXI4‑Stream 协议 (AXIS) 可定义单向通道,用于按顺序执行串流。AXI4‑Stream 接口可突发的数据量是无限的,故而能显著提升性能。不同于需要地址才能读/写存储器的 AXI4 存储器映射接口,AXIS 接口可直接将数据传递至另一个 AXIS 接口,无需地址,因此使用的器件资源更少。通过将这些功能特性相结合,即可使串流接口成为轻量级高性能接口。

AXI4‑Stream 可用于生产者与使用者之间的业界标准 ready/valid 握手,如下图所示。当生产者发送 TVALID 信号,并且使用者通过发送 TREADY 信号来响应时,即可立即开始数据传输。此数据和控制握手应持续进行,直至 TREADYTVALID 设为低位或者生产者断言 TLAST 信号有效(表示这是传输的最后一个数据包)为止。

图 2. AXI4‑Stream 握手
重要: AXIS 接口只能分配给内核或 IP 的顶层实参(端口),不能分配给设计内部函数实参。HLS 设计内部使用的串流通道应使用 hls::stream,而不能使用 AXIS 接口。

您应使用 hls::stream<T_data_type> 来定义串流的数据类型,使用 ap_axis 结构体类型来实现 AXIS 接口。如 AXI4-Stream 接口 中所述,ap_axis 结构体支持您选择接口实现(含旁路或不含旁路):

提示: 您不应自行定义结构体来对 AXIS 信号(旁路、TLAST、TVALID)进行建模。您可改为重载 TDATA 信号来实现自己的数据类型。