阵列可分区到块中或者分区到其各元素中。在某些情况下,Vitis HLS 会将阵列分区到各独立元素中。这可使用自动分区配置设置来加以控制。将阵列分区到多个块中时,单一阵列将作为多个 RTL RAM 块来实现。分区到元素中时,每个元素都作为 RTL 中的 1 个寄存器来实现。在这 2 种情况下,分区允许并行访问更多元素,并且有助于提升性能;设计将在性能与实现性能所需的 RAM 或寄存器数量之间加以权衡取舍。
以下消息显示了函数流水打拍时常见的问题:
INFO: [SCHED 204-61] Pipelining loop 'SUM_LOOP'.
WARNING: [SCHED 204-69] Unable to schedule 'load' operation ('mem_load_2',
bottleneck.c:62) on array 'mem' due to limited memory ports.
WARNING: [SCHED 204-69] The resource limit of core:RAM:mem:p0 is 1, current
assignments:
WARNING: [SCHED 204-69] 'load' operation ('mem_load', bottleneck.c:62) on array
'mem',
WARNING: [SCHED 204-69] The resource limit of core:RAM:mem:p1 is 1, current
assignments:
WARNING: [SCHED 204-69] 'load' operation ('mem_load_1', bottleneck.c:62) on array
'mem',
INFO: [SCHED 204-61] Pipelining result: Target II: 1, Final II: 2, Depth: 3.
在此示例中,Vitis HLS 声明它无法达成指定的启动时间间隔 (II) 值 1,因为它受存储器端口所限,无法在存储器上调度 load
(读取)操作 (mem_load_2
)。以上消息指出了第 62 行上的 mem_load
操作所使用的“core:RAM:mem:p0 is 1
”的资源限制。块 RAM 的第 2 个端口同样仅含 1 项资源,该资源同样供 mem_load_1
操作使用。由于存在此存储器端口争用,Vitis HLS 报告的最终 II 为 2,而不是所期望的值 1。
此问题通常是由阵列所导致的。如果阵列不属于顶层函数的接口,则会作为包含最多 2 个数据端口块 RAM 来实现。这可能限制读写(或加载/存储)密集型算法的吞吐量。通过将该阵列(单一块 RAM 资源)拆分为多个更小的阵列(多个块 RAM)从而有效增加端口数量,即可改善带宽。
阵列可使用 ARRAY_PARTITION 指令来进行分区。Vitis HLS 可提供 3 种类型的阵列分区,如下图所示。这 3 种分区样式分别是:
-
block
- 原始阵列分割为原始阵列的连续元素块(大小相同)。
-
cyclic
- 原始阵列分割多个大小相同的块,这些块交织成原始阵列的元素。
-
complete
- 默认操作是将阵列按其独立元素进行拆分。这对应于将存储器解析为寄存器。
对于 block
和 cyclic
分区,factor
选项可指定要创建的阵列数量。在前图中,使用因子 2,即将阵列分割为 2 个更小的阵列。如果阵列的元素数量并非该因子的整数倍,那么最后一个阵列所含元素数量较少。
对多维阵列进行分区时,dimension
选项可用于指定对哪个维度进行分区。下图显示了使用 dimension
选项对以下代码示例进行分区的方式:
void foo (...) {
int my_array[10][6][4];
...
}
此图中的示例演示了如何通过对 dimension
3 进行分区来生成 4 个独立阵列,以及如何对 dimension
1 进行分区以生成 10 个独立分区。如果针对 dimension
指定 0,则将对所有维度进行分区。