ループは、ループ帰納変数で指定されている反復回数実行されます。反復の回数は、break
条件やループ exit 変数の変更など、ループ本体内のロジックにも影響されます。ループを展開すると、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;
}
最適化しない場合、次の図に示す合成サマリレポートでは、インプリメンテーションが順番どおりに実行されていることがわかります。これは LOOP_1 のトリップカウントで確認でき、反復回数が 10 およびレイテンシが 200 とレポートされています。レイテンシは、ループが新しい入力データを受信できるようになるまでの時間です。
最適なスループットを得るためには、レイテンシをできるだけ短くする必要があります。パフォーマンスを向上させるには、ループの境界が静的であるとし、UNROLL プラグマを使用してループを完全に展開し、ループ本体を並行してインプリメントします。LOOP_1 を完全に展開すると、次の図に示すようにレイテンシが大幅に短縮されます (50ns)。ループを展開することで、パフォーマンスは向上しますが、追加のリソースが使用されることになります (次の図に示すように FF および LUT が増加)。また、ループを完全に展開すると、ループ自体が消滅し、ループ本体が並行してインプリメントされ、次に示すようにさらに多くのリソースが消費されます。
もちろん、リソースの増加および利用可能なプラットフォーム リソースの関係で、ループを完全に展開できないケースもあります。このような場合、部分的にループ展開をする方法が、パフォーマンスをある程度向上させながら、多くのリソースを必要としないため、推奨されることがあります。ループを部分的に展開するには、プラグマまたは指示子の展開係数を定義します。このような制約のある場合、検討可能なソリューションとして同じループを係数 2 で展開します (つまり、ループ本体が複製され、トリップカウントは半分の 5 となる)。
さらに、ループを部分的に展開すると、トリップカウントが展開係数で完全に割り切れない場合に備えて、HLS ツールによって終了チェックがループにインプリメントされます。トリップカウントが展開係数で完全に割り切れる場合は、終了チェックはスキップされます。