説明
ループを展開すると、複数の演算を 1 つにまとめたものではなく、複数の個別の演算を作成できます。UNROLL プラグマを使用すると、RTL デザインにループ本体のコピーを複数作成することにより、一部またはすべてのループ反復を並列実行できるようになります。
C/C++ 関数のループは、デフォルトでは展開されません。ループが展開されない場合、合成でそのループの 1 反復に対してロジックが作成され、RTL デザインでループの各反復に対してこのロジックが順に実行されます。ループは、ループ帰納変数で指定されている反復回数実行されます。反復の回数は、break
条件やループ exit 変数の変更など、ループ本体内のロジックにも影響されます。UNROLL プラグマを使用すると、データのアクセスおよびスループットを向上するためにループを展開できます。
UNROLL プラグマでは、ループを完全にまたは部分的に展開できます。ループを完全に展開すると、RTL に各ループ反復対してループ本体のコピーが作成され、ループ全体を同時に実行できるようになります。ループの部分展開では、係数 N を指定してループのコピーを N 個作成し、ループ反復数を削減します。
ループを部分展開する場合、N は最大反復回数の整数因数である必要はありません。Vitis HLS ツールでは、部分展開されたループが元のループと同じように動作することを確認する終了チェックが追加されます。たとえば、次のようなコードがあるとします。
for(int i = 0; i < X; i++) {
pragma HLS unroll factor=2
a[i] = b[i] + c[i];
}
break
コンストラクトにより機能が同じになり、ループが適切なポイントで終了します。for(int i = 0; i < X; i += 2) {
a[i] = b[i] + c[i];
if (i+1 >= X) break;
a[i+1] = b[i+1] + c[i+1];
}
上記の例では最大反復回数 X
は変数なので、HLS ツールでその値を判断できず、部分展開されるループに終了チェックと制御ロジックが追加されます。ただし、展開係数 (この例では 2) が最大反復回数 X
の整数因数であるとわかっている場合は、skip_exit_check
オプションを使用して終了チェックとその関連ロジックを削除できます。これによりエリアが最小限に抑えられ、制御ロジックが単純になります。
config_unroll
コマンドを使用して制御します。構文
C ソースの展開するループの本体内に配置します。
#pragma HLS unroll factor=<N> region skip_exit_check
説明:
-
factor=<N>
- 以外の整数値を指定して、部分展開が実行されるようにします。ループの本体が指定した回数繰り返され、反復情報がそれに合わせて調整されます。
factor=
を指定しない場合、ループは完全に展開されます。 -
skip_exit_check
-
factor=
を使用して部分展開を指定している場合にのみ適用されるオプションのキーワードです。ループの反復回数が既知であるかどうかによって、終了チェックが削除されます。-
Fixed bounds
反復回数が係数の倍数である場合は、終了条件はチェックされません。
反復回数が係数の整数倍でない場合は、次のようになります。
- 展開は実行されません。
- 処理を続行するには終了チェックを実行する必要があることを示す警告メッセージが表示されます。
-
Variable bounds
終了条件チェックが削除されます。次を確認してください。
- 可変境界が係数の整数倍である。
- 終了チェックが不要である。
-
Fixed bounds
例 1
次の例では、関数 foo
内の loop_1
を完全に展開しています。プラグマを loop_1
の本体に記述します。
loop_1: for(int i = 0; i < N; i++) {
#pragma HLS unroll
a[i] = b[i] + c[i];
}
例 2
次の例では、関数 foo
の loop_2
を係数 4 で部分展開し、終了チェックを削除しています。
void foo (...) {
int8 array1[M];
int12 array2[N];
...
loop_2: for(i=0;i<M;i++) {
#pragma HLS unroll skip_exit_check factor=4
array1[i] = ...;
array2[i] = ...;
...
}
...
}
例 3
次の例では、関数 foo
の loop_1
内に含まれるすべてのループを完全に展開しますが、region
キーワードを使用して loop_1
自体は展開しないようにしています。
void foo(int data_in[N], int scale, int data_out1[N], int data_out2[N]) {
int temp1[N];
loop_1: for(int i = 0; i < N; i++) {
#pragma HLS unroll region
temp1[i] = data_in[i] * scale;
loop_2: for(int j = 0; j < N; j++) {
data_out1[j] = temp1[j] * 123;
}
loop_3: for(int k = 0; k < N; k++) {
data_out2[k] = temp1[k] * 456;
}
}
}