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 指示子が適用できないだけでなく、そのループの上のレベルのパイプライン処理もできません。
この問題は、ループ内で条件付き実行を使用し、ループの繰り返し回数を固定値にすると回避できます。可変ループ境界のコードは、次のコード例に示すように書き直すことができます。この例では、ループ境界は可変 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 を参照してください。