Vitis HLS の概要
Vitis™ HLS は、Vitis アプリケーション アクセラレーション開発フローで C/C++ 関数をハードウェア カーネルとしてインプリメンテーションするプロセスを簡単に実行できるようにするために開発されたツールで、C/C++ コードを FPGA デザイン用の RTL IP を開発するためにも使用できます。
Vitis アプリケーション アクセラレーション フローでは、C/C++ コードをプログラマブル ロジックにインプリメントして最適化するのに必要なコード変更の多くが Vitis HLS により自動化され、短いレイテンシ、高スループットを達成できます。ユーザーの関数引数用に正しいインターフェイスを生成し、コード内でループおよび関数をパイプライン処理するのに必要なプラグマを推論することが、アプリケーション アクセラレーション フローでの Vitis HLS の基本です。
Vitis HLS では、コードのカスタマイズもサポートされており、異なるインターフェイス規格をインプリメントしたり、デザイン目標を達成するために特定の最適化を適用したりできます。
Vitis HLS のデザイン フローは次のとおりです。
- C/C++ アルゴリズムをコンパイル、シミュレーション、およびデバッグ。
- レポートを確認して、デザインを解析および最適化。
- C アルゴリズムを RTL デザインに合成。
- RTL 協調シミュレーションを使用して RTL インプリメンテーションを検証。
- RTL インプリメンテーションをコンパイル済みオブジェクト ファイル (.xo) にコンパイルまたは RTL IP にエクスポート。
基本的な高位合成
ザイリンクス Vitis HLS は、プログラマブル ロジックでアクセラレーションするために、C または C++ 関数を RTL コードに合成するツールです。Vitis HLS は、Vitis コア開発キットとアプリケーション アクセラレーション デザイン フローに緊密に統合されています。
高位合成 (HLS) 設計手法を使用する利点は、次のとおりです。
- C レベルでアルゴリズムを開発および検証し、ハードウェア インプリメンテーションの詳細から抽象化されたレベルで設計。
- C シミュレーションを使用してデザインを検証し、通常の RTL デザインよりもすばやくイテレーション。
- 最適化プラグマを使用して C 合成プロセスを制御し、高パフォーマンスのインプリメンテーションを作成。
- C ソース コードおよびプラグマから複数のデザイン ソリューションを作成して、デザイン空間を試し、最適なソリューションを特定。
- C ソースをすばやく再コンパイルし、さまざまなプラットフォームおよびハードウェア デバイスをターゲット。
HLS には、次の段階が含まれます。
- スケジューリング: 各クロック サイクルでどの演算を実行するかを次に基づいて決定します。
- 演算の依存が満たされ、使用可能になるタイミング。
- クロック サイクルの長さまたはクロック周波数。
- 演算が終了するまでにかかる時間 (ターゲット デバイスで定義)。
- 使用可能なリソースの割り当て。
- ユーザー指定の最適化指示子。
ヒント: クロック周期が長ければ、1 つのクロック サイクルでより多くの演算を実行できます。高速なデバイスがターゲットの場合、すべての演算を 1 クロックで完了できることもあります。クロック周期が短い場合、または低速のデバイスがターゲットの場合は、HLS で演算が複数のクロック サイクルでスケジューリングされます。演算によっては、マルチサイクル リソースとしてインプリメントする必要のあるものもあります。 - バインディング: スケジューリングされた各演算をインプリメントするハードウェア リソースを決定し、加算、乗算、シフトなどの演算子を特定の RTL インプリメンテーションにマップします。たとえば乗算 (
mult) は、組み合わせまたはパイプライン RTL 乗算器としてインプリメントできます。 - 制御ロジックの抽出: 定義されたスケジュールに基づいて、RTL デザインで演算を順序付ける有限ステート マシン (FSM) を作成します。
スケジューリングおよびバインディングの例
次の図に、このコード例のスケジューリングおよびバインディングの例を示します。
int foo(char x, char a, char b, char c) {
char y;
y = x*a+b+c;
return y;
}
この例のスケジューリング段階では、次の演算が各クロック サイクルで発生するようにスケジューリングされます。
- 1 番目のクロック サイクル: 乗算と 1 回目の加算
- 2 番目のクロック サイクル: 2 番目のクロック サイクルで最初の加算の結果が使用可能な場合は、2 回目の加算を実行して出力を生成
x、a、b データ ポートが読み出され、2 番目のサイクルで c データ ポートが読み出されて y 出力が生成されます。最終的なハードウェア インプリメンテーションでは、最上位関数への引数が I/O (入力および出力) ポートとしてインプリメントされます。この例では、引数は単純なデータ ポートです。各入力変数は char 型なので、入力データ ポートはすべて 8 ビット幅です。関数 return は 32 ビットの int 型なので、出力データ ポートは 32 ビット幅です。
この例の初期バインディング段階では、乗算が組み合わせ乗算器 (Mul) を使用してインプリメントされ、両方の加算が組み合わせ加減算器 (AddSub) を使用してインプリメントされています。
ターゲット バインディング段階では、乗算器と加算演算の 1 つが DSP モジュール リソースを使用してインプリメントされています。アプリケーションには多数のバイナリ乗算器およびアキュムレータを使用するものもありますが、これらは専用の DSP リソースにインプリメントするのが最適です。DSP モジュールは、FPGA アーキテクチャで使用可能な計算ブロックで、高パフォーマンスと効率的なインプリメンテーションの理想的なバランスを達成します。
制御ロジックの抽出と I/O ポートのインプリメント例
次の図に、ここに示すコード例の制御ロジックの抽出と I/O ポートのインプリメンテーションを示します。
void foo(int in[3], char a, char b, char c, int out[3]) {
int x,y;
for(int i = 0; i < 3; i++) {
x = in[i];
y = a*x + b + c;
out[i] = y;
}
}
このコード例では前の例と同じ演算が実行されますが、演算が for ループ内で実行され、関数引数のうちの 2 つが配列である点が異なります。結果のデザインでは、コードがスケジューリングされたときに for ループ内のロジックが 3 回実行されます。高位合成では C コードから制御ロジックが自動的に抽出され、RTL デザインでこれらの演算を順序付ける有限ステート マシン (FSM) が作成されます。最終的な RTL デザインでは、最上位関数の引数がポートとしてインプリメントされます。char 型のスカラー変数は標準の 8 ビット データ バス ポートにマップされます。in および out などの配列引数には、データ コレクション全体が含まれます。
高位合成では、配列はデフォルトでブロック RAM に合成されますが、FIFO、分散 RAM、個別のレジスタなどに合成することも可能です。最上位関数で配列を引数として使用すると、ブロック RAM が最上位関数外にあると想定され、データ ポート、アドレス ポート、必要なチップ イネーブルまたはライト イネーブル信号など、デザイン外のブロック RAM にアクセスするためのポートが自動的に作成されます。
FSM では、データをいつレジスタに格納するかと、I/O 制御信号のステートが制御されます。FSM は C0 ステートで開始し、次のクロックで C1 ステートに遷移し、その後 C2 ステート、C3 ステートに遷移します。そして C1 (および C2、C3) を 3 回反復してから、C0 ステートに戻ります。
C0、{C1, C2,
C3}、{C1, C2, C3}、{C1,
C2, C3} の後、C0 に戻ります。b と c の加算が必要なのは 1 回だけなので、この演算は for ループ外に出され、C0 ステートに挿入されます。この加算結果は、C3 ステートに遷移するたびに再利用されます。
デザインでは in のデータが読み出され、x に格納されます。FSM の C1 で最初の要素のアドレスが生成されます。また、C1 ステートでは、C1、C2、C3 ステートを何回反復する必要があるのかを知るために、加算器がインクリメントされます。C2 ステートでは、ブロック RAM から in のデータが返され、変数 x として格納されます。
計算に必要なその他の値が a ポートから読み出され、最初の y 出力が生成されます。FSM で正しいアドレスと制御信号が生成され、この値がブロック外に格納されます。この後デザインは C1 ステートに戻り、配列/ブロック RAM の in から次の値が読み出されます。このプロセスは、すべての出力が書き込まれるまで継続されます。そして C0 ステートに戻り、b と c の次の値が読み出されて、プロセスが再度実行されます。
パフォーマンス メトリクスの例
次の図に、前の例のコードの実行 (各クロック サイクルのステート、読み出し、計算、書き込みなど) をサイクルごとに示します。
この例のパフォーマンス メトリクスは次のとおりです。
- レイテンシ: すべての値を出力するのに 9 クロック サイクルかかります。注記: 出力が配列の場合、レイテンシは配列の最後の出力値までで計測されます。
- 開始間隔 (II): II は 10 なので、関数が新しい入力データのセットの読み出しを開始してから次のセットのプロセスを開始するまでに 10 クロック サイクルかかります。注記: 1 つの関数の実行を完了するのにかかる時間は、1 トランザクションと呼ばれます。この例では、関数が次のトランザクション用のデータを受信できるまでに 11 クロック サイクルかかります。
- ループの反復レイテンシ: 各ループ反復のレイテンシは 3 クロック サイクルです。
- ループ II: 開始間隔は 3 です。
- ループ レイテンシ: レイテンシは 9 クロック サイクルです。
チュートリアルおよびサンプル
Vitis HLS の使用方法を短時間で習得するためのチュートリアルおよびサンプル アプリケーションが次の場所から提供されています。
- Vitis HLS チュートリアル (https://github.com/Xilinx/HLS-Tiny-Tutorials/tree/2020.1)
- 適切な設計プラクティス、コーディング ガイドライン、一般的なアプリケーションのデザイン パターンなどのコード例が多数含まれ、アプリケーションのパフォーマンスを最大限にするために重要な最適化手法が示されています。すべての例には、コード例を使用するための README.md ファイルおよび run_hls.tcl スクリプトが含まれています。
- Vitis アクセラレーション例リポジトリ (https://github.com/Xilinx/Vitis_Accel_Examples)
- Vitis ツールおよびプラットフォームのさまざまな機能を紹介する例が含まれます。Vitis アプリケーション アクセラレーション開発フロー用のホスト コードおよびカーネル プログラムに関する特定の状況が機能する小型の例で示されます。これらの例のカーネル コードは、Vitis HLS で直接コンパイルできます。
- Vitis アプリケーション アクセラレーション開発フロー チュートリアル (https://github.com/Xilinx/Vitis-Tutorials)
- Vitis HLS をスタンドアロン アプリケーションとして使用する方法や Vitis ボトムアップ デザイン フローで使用する方法を含め、ツール フローおよびアプリケーション開発に関する特定の概念を理解するためのチュートリアルが多数含まれます。