データフローのパイプライン処理
これまでに説明した最適化手法はすべて、乗算、加算、メモリのロード/ストアなどの演算子レベルでの細粒度の並列処理最適化でした。これらの最適化では、これらの演算子間が並列処理されます。一方データフローのパイプライン処理では、関数およびループのレベルで粗粒度の並列処理が実行されます。データフロー パイプラン処理により、関数およびループの同時処理が増加します。
関数のデータフローのパイプライン処理
図: 関数のデータフローのパイプライン処理
上記の図の (B) は、データフロー パイプライン処理を使用した例を示しています。func_A の実行に 3 サイクルかかるとすると、func_A はこの 3 つの関数すべてが完了するまで待たずに、3 クロック サイクルごとに新しい入力の処理を開始できるので、スループットが増加します。最終的な値は 5 サイクルで出力されるようになり、全体的なレイテンシが短くなります。
- 関数パラメーター (プロデューサーまたはコンシューマー) が配列の場合は、該当するチャネルがマルチバッファーとして標準メモリ アクセス (関連のアドレスおよび制御信号を使用) を使用してインプリメントされます。
- スカラー、ポインター、参照パラメーター、および関数の戻り値の場合は、チャネルは FIFO としてインプリメンテーションされます。この場合、アドレス生成は不要なので使用されるハードウェア リソースは少なくなりますが、データに順次アクセスする必要があります。
#pragma HLS dataflow を挿入します。次に、コード例を示します。void top(a, b, c, d) {
#pragma HLS dataflow
func_A(a, b, i1);
func_B(c, i1, i2);
func_C(i2, d);
}ループのデータフローのパイプライン処理
データフロー パイプライン処理は、関数に適用するのと同様の方法でループにも適用できます。これにより、ループのシーケンス (通常は順次処理) がイネーブルになり、同時処理されるようになります。データフロー パイプライン処理は、関数、ループ、またはすべて関数かすべてループを含む領域に適用する必要があります。ループと関数が混合したスコープに適用しないでください。
データフロー パイプライン処理をループに適用した場合の利点については、次の図を参照してください。データフロー パイプライン処理を実行しない場合、ループ M を開始する前にループ N のすべての繰り返しを実行し、完了する必要があります。ループ M とループ P にも同様の関係があります。この例では、ループ N で新しい値を処理できるようになるまでに 8 サイクルかかり、出力が書き込まれる (ループ P が終了したときに出力が書き込まれると想定) までに 8 サイクルかかります。
図: ループのデータフローのパイプライン処理
データフロー パイプラインを使用すると、これらのループが同時に処理されるようにできます。上記の図の (B) は、データフロー パイプライン処理を使用した例を示しています。ループ M の実行に 3 サイクルかかるとすると、このコードでは 3 サイクルごとに新しい入力を受信できます。同様に、同じハードウェア リソースを使用して 5 サイクルごとに出力値を生成できます。Vivado HLS ではループ間に自動的にチャネルが挿入され、データが 1 つのループから次のループに非同期に流れるようになります。データフロー パイプラインを使用した場合と同様、ループ間のチャネルはマルチバッファーか FIFO のいずれかとしてインプリメントされます。
ループのデータフロー パイプライン処理を使用するには、データフロー最適化が必要な部分に #pragma HLS dataflow を挿入します。