描述
在 FPGA 设计中,对片外 DDR 存储器进行非突发访问的成本很高,而且会使性能劣化。为了缓解该问题,Vitis HLS 在 M_AXI 适配器中支持一种高速缓存机制,通过利用引用的局部性(时间性和空间性)来减少平均访问时延。访问某个存储器区域时,很可能很快会再次访问该区域,并且还可能访问附近的位置。
这种高速缓存机制既支持单端口高速缓存,也支持多端口层级:
- 单端口高速缓存:直接映射、只读高速缓存,连接到单个 m_axi 端口。这样在内核重复访问同一行时可以减少对 DRAM 的重复读取。
- 多端口高速缓存:一种分层高速缓存,每个端口都有 L1 高速缓存,并共享 L2 高速缓存。它支持在单个时钟周期内对同一个顶层 m_axi 指针进行最多 N 次读取访问,而无需更改源代码,适用于类似 stencil 的访问模式以及编译器无法推断窗口缓冲器的其他动态访问模式。相比于编译器推断的窗口缓冲器,多端口高速缓存更灵活(处理动态模式),但通常会耗用更多的资源。
多端口高速缓存架构
- L2 高速缓存(共享):
-
所有端口共享一个直接映射的高速缓存。
-
通过突发传输从片外 DRAM 加载。
-
将各行广播到发出请求的 L1 高速缓存,通过避免冗余 DRAM 加载来优化存储器带宽。
-
- L1 高速缓存(各端口):
-
每个高速缓存端口一个直接映射的 L1 高速缓存。
-
每个 L1 均可在每个周期内提供一个数据项,允许每个周期对同一个 m_axi 指针进行最多 N 次读取访问。
-
从共享的 L2 高速缓存加载 L1 行。
-
提示: 您可使用
syn.interface.m_axi_cache_impl 命令控制高速缓存实现资源。语法
pragma HLS cache port=<name> lines=<value> depth=<value>
其中:
-
port=<name> - 指定高速缓存要添加到的只读端口(顶层 m_axi 指针)。
-
lines=<value> - 每个 L1 高速缓存中的高速缓存行数(针对单端口的情况:唯一的一个高速缓存)。针对单行指定 1,针对多行指定大于 1 的 2 的幂值。可选;如不指定,则默认值为 1。
-
depth=<value> - 每个高速缓存行的大小,以字数为单位,必须为 2 的幂值。适用于指针的元素类型(例如,深度以 int 或 float 为单位)。可选;默认值为最大突发长度。最大突发长度默认为 16,但可通过 syn.interface.m_axi_max_read_burst_length 来全局指定,或通过 syn.directive.interface 来按接口指定。注释: 在多端口高速缓存中,L1 和 L2 缓存共享相同的行深度;深度同时适用于两个级别。
-
ports=<N> - 启用多端口高速缓存功能特性。定义在一个时钟周期内可以针对对应顶层 m_axi 指针调度执行的读取访问次数。仅适用于映射到其 m_axi 捆绑的只读指针;当端口数量大于 1 时,只能将一个只读阵列映射到该捆绑(请参阅“限制”)。
-
l2_lines=<M> - 可选;默认情况下,L1 和 L2 高速缓存的行数由深度选项来选择。在共享 L2 高速缓存中显式设置行数。每个 L2 行的大小与 L1 行大小相等,并受深度控制。必须大于行数;L2 高速缓存行数必须大于 L1 高速缓存行数。
不含 L2 高速缓存的单端口高速缓存示例:
#pragma HLS cache port=A lines=4 depth=32
启用每个周期最多 3 次读取的多端口高速缓存,显式设置共享 L2 的大小:
#pragma HLS cache port=A lines=4 depth=32 ports=3 l2_lines=16
行为与性能注释
- 多端口高速缓存是专为类似 stencil 的访问模式以及无法推断窗口缓冲器的动态访问模式而设计的。它可以在每个周期内允许多个读取请求,同时减少通过共享 L2 的冗余 DRAM 突发,从而提高吞吐量。
- L1 与 L2 高速缓存均为直接映射。命中/未命中行为由局部性决定;增大行数并选择合适的深度可以提高命中率,但会增加资源消耗。
- 共享 L2 高速缓存每次未命中时会加载一个 DRAM 行,并将该行广播到需要它的任意 L1 高速缓存,从而优化与窗口缓冲器类似的带宽,但支持动态模式。
限制
- 仅限只读端口才支持高速缓存。写端口可以与缓存的只读指针共享一个捆绑,因为它们使用的是独立通道,但不支持 inout 输入输出端口。
- 在多端口模式(端口数大于 1)下:
- 缓存的顶层 m_axi 指针必须是映射到其捆绑的唯一读端口。
- 写端口也可以映射到同一个捆绑,但 inout 端口对此不予支持。
- 高速缓存在 L1 和 L2 级处均为直接映射(单路)。
- 指定的 l2_lines 值必须大于行数。
- 只能将一个只读阵列映射到多端口缓存指针的 m_axi 捆绑。
其他控制
-
syn.interface.m_axi_cache_impl可用于控制高速缓存实现资源。 -
syn.interface.m_axi_max_read_burst_length和syn.directive.interface可通过最大突发长度来控制默认深度。
示例
以下示例显示的设计中,重叠访问会导致突发失败。使用 CACHE 编译指示或指令会改善设计性能。
extern "C" {
void dut(
const double *in, // Read-Only Vector 1
double *out, // Output Result
int size // Size in integer
)
#pragma HLS INTERFACE m_axi port=in bundle=aximm depth = 1026
#pragma HLS INTERFACE m_axi port=out bundle=aximm depth = 1024
#pragma HLS cache port=in lines=8 depth=128
for(int i = 0; i < size; i++)
{
out[i] = in[i] + in[i + 1];
}
}