変数ループ境界の使用 - 2023.2 日本語

Vitis 高位合成ユーザー ガイド (UG1399)

Document ID
UG1399
Release Date
2023-12-18
Version
2023.2 日本語

Vitis HLS で適用できる最適化の中には、ループに可変境界があると実行されないものがあります。次のコード例では (GitHub の variable_bound_loops)、ループ境界が最上位入力から駆動される変数 width により決定されます。この例の場合、Vitis HLS ではループがいつ終了するのか判断できないので、ループには可変境界があると考えられます。

#include "ap_int.h"
#define N 32
 
typedef ap_int<8> din_t;
typedef ap_int<13> dout_t;
typedef ap_uint<5> dsel_t;
 
dout_t code028(din_t A[N], dsel_t width) { 
 
 dout_t out_accum=0;
 dsel_t x;
 
 LOOP_X:for (x=0;x<width; x++) {
 out_accum += A[x];
 }
 
 return out_accum;
}

上記の例で設計を最適化しようとすると、変数ループ境界により発生する問題が明らかになります。可変ループ境界に関する最初の問題は、Vitis HLS でループのレイテンシが決定できなくなることです。Vitis HLS はループを 1 回実行するレイテンシは決定できますが、正確な可変 width をスタティックに決定できないので、何回繰り返されるのかは判断できず、ループ レイテンシ (ループの繰り返しすべてを完全に実行するまでのサイクル数) はレポートできません。

ループ境界が可変の場合、Vitis HLS ではそのレイテンシが正確な値ではなく、クエスチョン マーク (?) でレポートされます。次に、前の例の合成結果を示します。

+ Summary of overall latency (clock cycles):
 * Best-case latency:    ?
 * Worst-case latency:   ?
+ Summary of loop latency (clock cycles):
 + LOOP_X:
 * Trip count: ?
 * Latency:    ?

この問題を解決するには、LOOP_TRIPCOUNT プラグマまたは指示子を使用して、ループの最小および/または最大反復回数を指定します。tripcount はループの繰り返し回数を意味します。最初の例のように最大の tripcount の 32 が LOOP_X に適用されると、レポートは次のようにアップデートされます。

+ Summary of overall latency (clock cycles):
 * Best-case latency:    2
 * Worst-case latency:   34
+ Summary of loop latency (clock cycles):
 + LOOP_X:
 * Trip count: 0 ~ 32
 * Latency:    0 ~ 32

LOOP_TRIPCOUNT 指令子に対してユーザーが指定した値は、レポート、あるいは PERFORMANCE プラグマまたは指示子のサポートの目的にのみ使用されます。指定した tripcount の値により、Vitis HLS でレイテンシ値が決定されるので、さまざまなソリューションからの値を比較できます。合成に使用される同じループ境界情報を得るには、アサートを使用して C/C++ コードを更新する必要があるので、合成に影響します (ただし、アサート条件が真であると想定されるため、アサート文は慎重に使用する必要があります)。

次に、最初の例をより短い開始間隔で最適化する手順を示します。

  • ループを展開し、並列累算が実行されるようにします。
  • 配列入力を分割しないと並列累算が 1 つのメモリ ポートに制限されます。

これらのコード変更が適用されると、Vitis HLS で可変境界ループに関する最大の問題を示すメッセージが表示されます。

WARNING: [HLS 200-936] Cannot unroll loop 'LOOP_X' (loop_var.cpp:22) in 
function 'loop_var': cannot completely unroll a loop with a variable trip count.

可変境界ループは完全に展開できないので、unroll 指示子が適用できないだけでなく、そのループの上のレベルのパイプライン処理もできません。

重要: ループまたは関数がパイプライン処理されると、Vitis HLS はその関数またはループの下の階層ですべてのループを展開します。この階層に可変境界を含むループがあると、パイプライン処理はできなくなります。

この問題は、ループ内で条件付き実行を使用し、ループの繰り返し回数を固定値にすると回避できます。可変ループ境界のコードは、次のコード例に示すように書き直すことができます。この例では、ループ境界は可変 width の最大値に設定され、ループ本体は条件付きで実行されます。

#include "ap_int.h"
#define N 32
 
typedef ap_int<8> din_t;
typedef ap_int<13> dout_t;
typedef ap_uint<5> dsel_t;
 
dout_t loop_max_bounds(din_t A[N], dsel_t width) { 
 
 dout_t out_accum=0;
 dsel_t x;
 
 LOOP_X:for (x=0; x<N; x++) {
 if (x<width) {
  out_accum += A[x];
 }
 }
 
 return out_accum;
}

上記の例の for ループ (LOOP_X) は、完全に展開できます。これは、ループに上位境界があり、Vitis HLS でハードウェアがどれくらい作成されるか認識されるからです。RTL デザインのループ本体には、N(32) 個のコピーがあります。このループ本体の各コピーには、それに関する条件付きロジックが含まれ、可変の width 値によって実行されます。例は、GitHub の Vitis-HLS-Introductory-Examples/Modeling/variable_bound_loops を参照してください。