展开循环 - 2023.2 简体中文

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

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

循环的迭代执行次数由循环归纳变量来指定。迭代次数也可能受到循环主体内的逻辑影响(例如,对循环出口变量条件应用 break 条件或修改)。您可展开循环,在 RTL 设计中创建循环主体的多个副本,从而允许并行执行部分或全部循环迭代。您可使用 UNROLL 编译指示展开循环以便增加数据访问和吞吐量。

默认情况下,HLS 循环保持处于收起状态。这意味着循环的每次迭代都使用相同硬件。展开循环意味着,循环的每次迭代都有其自己的硬件用于执行循环函数。这意味着,展开的循环的性能显著高于收起的循环。但提升性能的代价是增加面积和资源利用率。

请参阅来自 GitHub 的 basic_loops_primer 示例,如下所示:

#include "test.h"
 
dout_t test(din_t A[N]) { 
  dout_t out_accum=0;
  dsel_t x;
   
  LOOP_1:for (x=0; x<N; x++) {
      out_accum += A[x];
  }
  return out_accum;
}

下图中的“Synthesis Summary”(综合汇总)报告显示,实现以顺序方式执行,且无最优化。这可通过观察 LOOP_1 的循环次数来确认,报告称迭代次数为 10,“Latency”(时延)为 200。时延是循环可接受新的输入值之前的时间。

图 1. Performance & Resource Estimates

为了获得最优吞吐量,时延需尽可能短。为了提升性能,假定循环边界为静态,可使用 UNROLL 编译指示来完全展开循环,以创建循环主体的并行实现。在完全展开 LOOP_1 后,可见时延显著缩短 (50 ns),如下图所示。展开循环暗示提升性能的代价是使用额外资源(如下所示,FF 和 LUT 增加)。完全展开循环还将导致循环本身消失,并被循环主体的并行实现所替代,后者将用尽额外资源,如下所示。

图 2. Performance & Resource Estimates

当然,在某些情况下,由于资源增加以及平台可用资源所限,导致无法完全展开循环。在此情况下,首选解决方案是部分展开循环,这样通常可以提升部分性能,同时无需耗用过多资源。要部分展开循环,您将需要为编译指示或指令定义展开因数。鉴于此处案例受到约束,可以接受以 2 为因数来展开相同循环(这暗示复制循环主体,循环次数减半至 5)的解决方案,如下所示。

图 3. Performance & Resource Estimates

此外,部分展开循环时,HLS 工具将在循环中实现出口检查,以防该循环次数无法被展开因数整除。如果循环次数能被展开因数整除,则跳过出口检查。