控制 AXI4 突发行为的选项 - 2023.2 简体中文

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

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

当设计在等待访问总线时,从不会在最优化 AXI4 接口上停滞,授予总线访问后,总线等待设计执行读/写时从不停滞。影响系统性能和突发传输的设计元素有很多,例如:

  • 时延
  • 端口宽度
  • 多个端口
  • 指定突发长度
  • 未完成的读/写操作数

时延

读取时延定义为从发送突发读取请求直到内核接收到来自突发中的首个读取请求之间所耗用的时间。同样,写入时延定义为发送突发中的最后一次写入操作的数据的时间与内核接收到写入响应的时间之间所耗用的时间。这些时延取决于诸如 DDR 存储器访问拥塞等系统特性,因此可采用非确定性值。有鉴于此,HLS 编译器无法准确判定综合期间的存储器读/写时延,故而使用 64 个内核周期的默认时延来调度请求和操作,如下所示。

  • 它会在调度读/写请求并等待数据的同时,并行执行与存储器无关的各项操作,例如,处理串流或计算
  • 等待调度新的读/写请求
提示: 默认工具时延可使用 LATENCY 编译指示或指令来进行更改。特定接口的默认时延可使用 INTERFACE 编译指示或指令的时延选项来更改。

为帮助您理解系统中可能存在的各种时延,下图显示了当 HLS 内核向 DDR 存储器发出突发时所发生的操作。

图 1. 突发传输事务图示

当设计发出读/写请求时,请求将通过多个专用帮助程序模块发送至 DDR 存储器。首先,m_axi 适配器会充当缓冲器,以供 HLS 内核所创建的请求使用。该适配器包含用于将大型突发剪切为小型突发的逻辑,目的是为了避免占用通道或者避免请求超过 4 KB 边界,请参阅 Vivado Design Suite:AXI 参考指南(UG1037)

默认情况下,适配器会暂停发送突发请求(根据未完成请求参数的最大值),直至所有数据变为可用为止,以便安全缓冲每个内核的完整数据。这样做的目的是减少因存储器子系统上的并发请求(读取或写入请求)而导致的死锁,但这可能导致写入时延有所增加。您可通过按 接口配置 中所述设置 syn.interface.m_axi_conservative_mode=false 来禁用此写入请求的保持时间,但会增大发生死锁的风险。

穿越此适配器需耗费数个时延周期,通常需 5 到 7 个周期。随后,请求会穿越 AXI Interconnect,后者将把内核请求路由至 MIG 并最终发送到 DDR 存储器。穿越 AXI Interconnect 需耗费大量时延,可能需约 30 个周期。最终到达 DDR,返回则需耗费 9 到 14 个周期。这些提供的数值并非精确测得的时延,仅为估算,用于展示这些专用模块的相对时延成本。如需获取更精确的测量值,您可使用特定于您的系统的“Application Timeline”(应用时间线)报告来测试并观察这些时延,如 AXI 性能案例研究 中所述。
提示: 如需了解有关“Application Timeline”报告的更多信息,请参阅 Vitis 统一软件平台文档中的时间线轨迹

以下提供了查看系统中的时延的另一种方法:互连接口的平均启动时间间隔 (II) 为 2,而 DDR 存储器控制器用于请求的平均 II 则为 4-5 周期(两者在数据上耗费的 II 均为 1)。互连接口的仲裁测量是根据读/写请求大小来进行仲裁的,因此所请求的数据的突发长度越长,则优先级越高(从而导致发生争用时,分配给较长的突发的通道带宽更大)。当然,大型突发请求的副作用是阻止其他任何请求访问 DDR 存储器,因此,在突发长度与减少 DDR 端口争用之间必须有所取舍。幸好,大延迟有助于防止出现某些此类端口争用问题,而对请求进行有效的流水打拍则可以显著提升系统中可用的带宽吞吐量。

时延不影响具有流水打拍的突发的循环/函数,因为突发在单次请求内即请求最大大小。

时延可能会在以下两方面影响具有顺序突发的循环/函数:

  • 如果系统读/写时延大于默认工具时延,则 Vitis HLS 必须等待数据。更改时延将无法改善系统性能。
  • 如果读/写时延小于工具默认时延,那么 Vitis HLS 会保持空闲 (Idle) 状态,并浪费剩余内核周期。由于在此空闲状态期间不执行任务,因此可能影响设计性能。如下图所示,系统时延与默认时延参数之间的差异会导致顺序请求被进一步延迟。这会导致吞吐量显著损失。
图 2. 默认工具时延

但当您使用 LATENCY 编译指示或指令减少工具时延时,本工具将紧缩请求以供单一顺序突发使用,如下图所示。

图 3. 调整后的工具时延

端口宽度

通过最大限度提升传输的字节数,可以进一步改善加载 - 存储函数的吞吐量。Vitis Unified IDE 和 v++ 支持最大位宽为 1024 位的内核,这意味着内核每个端口每个时钟周期可以读取或写入多达 128 字节。

Vitis HLS 还支持通过分析源代码的存储器访问模式来进行自动端口宽度最优化。如果代码满足突发方法的前置条件和限制,那么它将在 Vitis 内核流程中将端口大小自动调整为 512 位。

重要: 如果在编译时,迭代大小和次数可变,那么该工具将不会自动拓宽端口宽度。

如果该工具无法自动拓宽此端口,那么您可通过使用 矢量数据类型任意精度 (AP) 数据类型 作为该端口的数据类型来手动更改端口宽度。

多个端口和通道

通过最大限度提升并发读/写,可以进一步改善加载 - 存储函数的吞吐量。在 Vitis HLS 中,默认情况下函数实参捆绑/映射/组合到单个端口。将多个端口捆绑到单个端口有助于节省资源。但是,单一端口可能会限制内核性能,因为所有存储器传输都必须穿越单个端口。m_axi 接口具有独立的 READ 和 WRITE 通道,因此单个端口可同时执行读取和写入。m_axi 接口还在单一端口中提供了多个通道(如 M_AXI 通道 中所述)以增加该端口内的读取和写入通道数。

使用多个端口可以创建多个接口以连接到不同存储体(如多 DDR 教程中所示)或者可执行顺序访问,因而可以增加内核带宽和吞吐量。当有多个实参访问同一个存储器端口或存储体时,仲裁器将把对同一个存储器端口或存储体执行的并发访问转变为顺序访问。将多个端口连接到不同的存储体可能增加加载和存储函数的吞吐量,从而导致计算块也会一并伸缩调整以满足加载和存储函数的吞吐量需求,否则将在加载存储函数上放置反压或停滞。

未完成的读/写操作数

通过允许系统隐藏部分存储器时延,可以进一步改善加载 - 存储函数的吞吐量。config_interface 命令或 INTERFACE 编译指示或指令的 m_axi_num_read_outstandingm_axi_num_write_outstanding 选项允许内核控制流水打拍的存储器请求数量,这些请求无需等待上一个请求完成即可发送到全局存储器。

增加流水打拍请求的数量会增大读/写请求的流水线深度,这将耗用额外的 BRAM/URAM 资源。

注释: 大部分情况下,当突发长度 >=16 时,未完成的读/写操作数应足矣。对于大小小于 16 的突发,AMD 建议将未完成的操作数量从默认值 16 加倍。

利用 INTERFACE 编译指示定义突发属性

为创建最优化 AXI4 接口,在 INTERFACE 指令中提供了以下命令选项以指定突发的行为并最优化 AXI4 接口的效率。

请注意,其中部分选项可使用内部存储空间来缓冲数据,这可能影响面积和资源:

latency
指定期望的 AXI4 接口时延,允许设计发起总线请求的时间比执行期望的读取或写入操作早数个周期(时延)。如果该值太低,设计将过早达成就绪状态,可能停滞并等待总线;如果该值太高,则可能授予总线访问权时,总线仍处于停滞状态并等待设计发起访问。Vitis HLS 中的默认时延为 64。
max_read_burst_length
指定突发传输期间读取的数据值的最大数量。默认值:16。
num_read_outstanding
指定在设计停滞前可对 AXI4 总线发出的读取请求的数量(无响应)。此操作暗示设计中的内部存储空间:即 FIFO 大小为 num_read_outstanding*max_read_burst_length*word_size。默认值:16。
max_write_burst_length
指定突发传输期间写入的数据值的最大数量。默认值:16。
num_write_outstanding
指定在设计停滞前可对 AXI4 总线发出的写入请求的数量(无响应)。此操作暗示设计中的内部存储空间:即 FIFO 大小为 num_write_outstanding*max_write_burst_length*word_size。默认值:16。
以下 INTERFACE 编译指示示例可用于帮助解释这些选项:
#pragma HLS interface mode=m_axi port=input offset=slave bundle=gmem0         
depth=1024*1024*16/(512/8) latency=100 num_read_outstanding=32 num_write_outstanding=32 
max_read_burst_length=16 max_write_burst_length=16
  • 接口时延指定为 100。HLS 编译器会尝试将突发访问请求调度为比设计准备好访问 AXI4 总线早 100 个时钟周期。
  • 为进一步提升总线效率,num_write_outstanding 选项和 num_read_outstanding 选项可确保设计包含的缓冲足以存储多达 32 次读取和/或写入突发。每个请求都将需要自己的缓冲器。这样即可允许设计持续处理,直至处理总线请求为止。
  • 最后,max_read_burst_lengthmax_write_burst_length 选项可确保最大突发量为 16,并且 AXI4 接口暂存总线的时间不超过该设置。HLS 工具将根据指定的总线长度来对较长的突发进行分区,并通过如下消息来报告此状况:
    Multiple burst reads of length 192 and bit width 128 in loop 'VITIS_LOOP_2'(./src/filter.cpp:247:21)has been inferred on port 'mm_read'.
    These burst requests might be further partitioned into multiple requests during RTL generation based on the max_read_burst_length settings.

用于配置突发的命令

这些命令可为工具配置全局设置,对 AXI4 接口进行最优化,以便运行该工具的系统使用此接口。操作效率取决于能否正确设置这些值。提供的默认值均为保守值,根据您的设计的存储器访问剖析,可能需要进行更改。

表 1. Vitis HLS 控制
Vitis HLS 命令 描述
syn.interface.m_axi_conservative_mode bool

默认值 = true

延迟 M-AXI 的每个写入请求,直至关联写入数据变为完全可用(通常,缓冲至适配器内或者已发射)。这样可能导致写入时延有所增加,但是可以解决由于存储器子系统上的并发请求(读取或写入请求)而导致的死锁。
syn.interface.m_axi_latency uint

0 表示自动

对于 Vivado IP 流程,默认值为 0

对于 Vitis 内核流程,默认值为 64

为调度器提供期望的 M-AXI 访问时延值。时延即读取请求与首次读取数据之间的延迟或者最后一次写入数据与写入响应之间的延迟。请注意,该数值并不需要精确,估算过低会导致调度的时延更低,但存在更长的动态停滞。调度器将考虑其他适配器的时延并增加几个周期。
syn.interface.m_axi_min_bitwidth uint

默认值为 8

M-AXI 接口数据通道的最大位宽。必须为 2 的幂(介于 8 到 1024 之间)。请注意,如果实际访问小于所需接口,那么这样并不一定会增加吞吐量。
syn.interface.m_axi_max_bitwidth uint

默认值为 1024

M-AXI 接口数据通道的最大位宽。必须为 2 的幂(介于 8 到 1024 之间)。请注意,如果实际访问大于所需接口,那么这样会降低吞吐量,因为访问将被拆分为多周期访问突发。
syn.interface.m_axi_max_widen_bitwidth uint

对于 Vivado IP 流程,默认值为 0

对于 Vitis 内核流程,默认值为 512

允许该工具将 M-AXI 接口上的突发自动拓宽至所选位宽。必须为 2 的幂(介于 8 到 1024 之间)。请注意,突发拓宽需要强大的对齐属性(不只限于突发)。
syn.interface.m_axi_auto_max_ports bool

默认值为 false

如果该选项为 false,那么未显式捆绑的所有 M-AXI 接口都将捆绑到单一通用接口上,从而最大限度降低资源利用率(单一适配器)。如果该选项为 true,那么未显式捆绑的所有 M-AXI 接口都将映射到独立接口,从而增大资源利用率(多个适配器)。
syn.interface.m_axi_alignment_byte_size uint

对于 Vivado IP 流程,默认值为 1

对于 Vitis 内核流程,默认值为 64

假定映射到 M-AXI 接口的主要函数指针至少对齐到所提供的宽度(以字节为单位,值为 2 的幂)。这样有助于自动进行突发拓宽。警告:如果指针在运行时未能准确对齐,则会发生错误行为。
syn.interface.m_axi_num_read_outstanding uint

默认值为 16

M-AXI num_read_outstanding 接口参数的默认值。
syn.interface.m_axi_num_write_outstanding uint

默认值为 16

M-AXI num_write_outstanding 接口参数的默认值。
syn.interface.m_axi_max_read_burst_length uint

默认值为 16

M-AXI max_read_burst_length 接口参数的默认值。
syn.interface.m_axi_max_write_burst_length uint

默认值为 16

M-AXI max_write_burst_length 接口参数的默认值。