SDSoC でのプログラミングの概要

SDSoC™ 環境には、ザイリンクス Zynq®-7000 SoC および Zynq® UltraScale+™ MPSoC 内のエンベデッド システム、またはザイリンクス デバイスの MicroBlaze™ エンベデッド プロセッサを開発するためのツールが含まれます。

含まれるツールは、次のとおりです。
  • Arm® および MicroBlaze プロセッサ用のコンパイラ、デバッガー、プロファイラーを含む Eclipse ベースの統合設計環境 (IDE)。
  • ハードウェア エミュレーター。
  • C/C++ 関数を最適化されたハードウェア関数に合成し、プログラマブル ロジック (PL) で使用できるようにするハードウェア コンパイラ。
  • C/C++ プログラミング言語で記述されたアプリケーション コードから、カスタム ハードウェアおよびデータ ムーバー ハードウェア ブロックを含む完全なハードウェア/ソフトウェア システムを生成するシステム コンパイラ。
sdscc/sds++ (sds++ と呼ぶ) システム コンパイラには、ハードウェア/ソフトウェア イベント トレースを実行するオプションが含まれ、ハードウェアで実行されるアクセラレータ タスク、アクセラレータとメモリ間のデータ転送、および CPU で実行されるアプリケーション コードなど詳細なタイムラインが表示できます。

ザイリンクス FPGA および SoC デバイスには、カスタム データパスをインプリメントするプログラマブル ハードウェア アーキテクチャ、マルチレベルの分散メモリ アーキテクチャ、カスタム入力/出力デバイスへのインターフェイスなどの多くの利点があり、ハードウェア/ソフトウェア インターフェイスを高パフォーマンスなエンベデッド CPU へカスタマイズできます。カスタム ハードウェア システムをビルドすると、エンベデッド アプリケーションのパフォーマンスをより向上し、消費電力をさらに削減できます。

SDSoC 環境には、クロス コンパイル、リンキング、プロファイリング、デバッグ、ターゲット ハードウェアおよびエミュレーターでのアプリケーション バイナリの実行などの馴染みのあるソフトウェア開発ワークフローを使用しつつ、ハードウェアおよびソフトウェアを作成できるという特徴があります。sds++ システム コンパイラを使用すると、アプリケーションのターゲット パーツをプロセッサで実行される最適済みコードよりも何倍も速く実行されるハードウェア アクセラレータとしてインプリメントできます。

プログラマにとってターゲット デバイスは異種計算で、通常は不均一なメモリ アーキテクチャと入力/出力デバイスへのカスタム インターフェイスを使用して、C/C++ で記述したコードがマルチコア Arm CPU およびカスタム ハードウェア アクセラレータで実行されます。コードを実行する箇所、メモリへのデータのマップ方法、ハードウェアおよびソフトウェアの相互関係に注意すると、アプリケーションのパフォーマンスをさらに向上できるようになります。

通常、アプリケーション コードはターゲット システムの異種性を反映している必要があります。ハードウェア アクセラレータにコンパイルされる C/C++ コードにはマイクロアーキテクチャの詳細を反映するプログラミング イディオムからの利点があり、CPU で実行されるコードには命令セット、キャッシュ、メモリ アーキテクチャを反映するイディオムからの利点があります。

SDSoC 環境では、CPU およびハードウェア アクセラレータ間のハードウェア/ソフトウェア インターフェイスが関数呼び出しおよび API (下位デバイスの場合) を使用して記述されます。コードのほとんどは、sds++ システム コンパイラがユーザーユーザー空間から効率の良いアクセスを生成して、システム コンパイラで提供されるカスタム ドライバーを介してキャッシュ管理などの下位レベルの考慮事項を自動的に管理した状態で、デバイス ドライバー API ではなく、関数呼び出しを介してアクセラレータにアクセスします。

SDSoC を使用したソフトウェア アクセラレーション

ザイリンクス デバイスのプログラマブル ロジック (PL) では、プロセッサ アーキテクチャよりも、アプリケーションの実行をより高い割合で並列処理可能です。アクセラレータのハードウェア関数用に sds++/sdscc (sds++ と表記) システム コンパイラで生成されるカスタム プロセッシング アーキテクチャは、CPU とは実行の枠組みが異なるので、パフォーマンスを大幅に向上できます。既存のエンベデッド プロセッサ アプリケーションを PL でアクセラレーションするよう変更することはできますが、ザイリンクス xfOpenCV ライブラリなどの既存のハードウェア関数のソース コード ライブラリを使用してアプリケーションを記述したり、PL デバイスのアーキテクチャがうまく使用されるようにコードを変更したりすることで、パフォーマンスを大幅に向上して、消費電力を削減できます。

CPU のリソースは固定されており、タスクまたは演算を並列処理する機会は限られます。プロセッサは、そのタイプにかかわらず、プログラムをプロセッサのコンパイラ ツールで生成された命令順に実行します。コンパイラ ツールは、C/C++ で記述されたアルゴリズムをターゲット プロセッサにネイティブのアセンブリ言語の構文に変換します。2 つの値の乗算のような単純な演算ですら、複数のアセンブリ命令となり、複数のクロック サイクルで実行されます。

FPGA は本質的に並列処理デバイス ファブリックであり、プロセッサ上で実行可能な関数をどれでもインプリメントできます。ザイリンクス デバイスにはプログラムおよびコンフィギュレーション可能なリソースが大量に含まれており、カスタム アーキテクチャをインプリメントしてどんなレベルの並列処理でも達成できます。FPGA のプログラム ロジックは、すべての計算が同じ ALU を共有するプロセッサとは異なり、アクセラレーション関数を定義してインプリメント可能な空白のキャンバスのようなものです。FPGA コンパイラは、ALU 全体ではなくニューラル ネットの積和ハードウェアのみをインプリメントするなど、各アプリケーションまたはアルゴリズム用に最適化された独自の回路を作成します。

sds++ システム コンパイラを -c オプションを指定して実行すると、必要な関数定義に対して Vivado 高位合成 (HLS) ツールが起動されてファイルがハードウェア IP にコンパイルされます。HLS ツールを呼び出す前に、sds++ コンパイラで #pragma SDS が HLS ツールで解釈可能なプラグマに変換されます。HLS ツールは、同時処理を増やすように、スケジューリング、パイプライン、データデータフローを含むハードウェア指向の変換と最適化を実行します。

sds++ リンカーは、プログラム データフローを解析し、ハードウェア関数内への呼び出しおよびハードウェア関数間の呼び出しを含め、ハードウェアのデータ モーション ネットワークおよびソフトウェア制御コード (スタブと呼ぶ) をシステムにマップして、データ ムーバーを介したアクセラレータとデータ転送を調整します。次のセクションで説明するように、sds++ リンカーは、データ転送をスケジューリングして共有可能な演算を特定し、待機バリア API 呼び出しをスタブに挿入して、プログラム セマンティックが保持されるようにします。

SDSoC アプリケーションの実行モデル

SDSoC 環境アプリケーションの実行モデルは、プラットフォームのブート後にターゲット CPU で実行される C++ プログラムの通常実行を考えると理解できます。C++ バイナリの実行ファイルがハードウェアとどのようにインターフェイスするかを理解しておくと有益です。

プログラム内で宣言されたハードウェア関数はハードウェア アクセラレータにコンパイルされ、標準 C ランタイムでこれらの関数への呼び出しによりアクセスされます。各ハードウェア関数呼び出しがタスクとしてアクセラレータを起動し、関数への各引数が CPU とアクセラレータ間で転送されて、アクセラレータ タスクの完了後にプログラムでアクセスできるようになります。メモリとアクセラレータ間のデータ転送には、DMA エンジンなどのデータ ムーバーが使用され、zero_copy などのユーザー データ ムーバー プラグマを考慮して、sds++ システム コンパイラにより自動的に挿入されます。

図: SDSoC システムのアーキテクチャ

プログラムが正しく機能するようにするため、システム コンパイラはハードウェア関数への各呼び出しをインターセプトし、シグネチャが同じで派生した名前が付けられたスタブ関数への呼び出しに置き換えます。スタブ関数はすべてのデータ移動とアクセラレータの演算を制御し、ハードウェア関数呼び出しの終了時にソフトウェアとアクセラレータ ハードウェアを同期化します。スタブ内では、すべてのアクセラレータとデータ ムーバーは sds_lib ライブラリに含まれる送信および受信 API により制御されます。

ハードウェア関数呼び出し間のプログラム データフローに、プログラム内で関数呼び出し (デストラクターまたは free() 呼び出し以外) が実行された後にアクセスされない配列引数が関係する場合、およびハードウェア アクセラレータをストリームを使用して接続できる場合、メモリへの往復がインプリメントされるのではなく、1 つのハードウェア アクセラレータから直接ハードウェア ストリーム接続を介して次のハードウェア アクセラレータにデータが転送されます。この最適化により、パフォーマンスが大幅に向上し、ハードウェア リソースを削減できます。

SDSoC プログラム実行モデルの手順は次のとおりです。
  1. プログラムのコンストラクターが main() に入る前に sds_lib ライブラリが初期化されます。
  2. プログラム内で、ハードウェア関数への各呼び出しが、元の関数と関数シグネチャが同じ (名前は異なる) のスタブ関数への関数呼び出しによりインターセプトされます。スタブ関数内では、次の手順が実行されます。
    1. 同期アクセラレータ タスク制御コマンドがハードウェアに送信されます。
    2. ハードウェア関数への各引数に対して、非同期データ転送要求が関連付けられた wait() ハンドルと共に適切なデータ ムーバーに送信されます。void 以外の戻り値は、出力スカラー引数として扱われます。
    3. 各転送要求に対して、バリア wait() が発行されます。アクセラレータ間のデータ転送がダイレクト ハードウェア ストリームとしてインプリメントされる場合、この転送のバリア wait() は、この引数のアクセラレータ関数チェーンの最後のアクセラレータ関数に対するスタブ関数で発生します。
  3. プログラムのデストラクターが main() を終了するときに、sds_lib ライブラリがクリーンアップされます。
ヒント: 手順 2a ~ 2c は、アクセラレータ パイプラインの開始および終了時にプログラムを正しく保ち、それと同時にパイプライン内での同時実行を可能にします。

場合によっては、システム コンパイラで自動的には推論できないアクセラレータ タスクの同時実行の可能性を、プログラマが認識していることもあります。sds++ システム コンパイラでは #pragma SDS async(ID) プラグマがサポートされており、このような場合にハードウェア関数への呼び出しの直前に挿入できます。このプラグマを指定すると、データ転送に対してバリア wait() 呼び出しなしでスタブ関数が生成されます。その結果、すべての転送要求を発行した後、制御がプログラムに戻り、アクセラレータの実行中にプログラムの同時実行が可能になります。この場合、プログラム内の適切な同期ポイントに #pragma SDS wait(ID) を挿入してください。このプラグマは sds_wait(ID) API 呼び出しとなり、ハードウェア アクセラレータ、その暗示的なデータ ムーバー、および CPU が正しく同期化されます。

重要:async(ID) プラグマには、対応する wait(ID) プラグマが必要です。

SDSoC のビルド プロセス

SDSoC のビルド プロセスでは、標準のコンパイルおよびリンク プロセスが使用されます。g++ と同様に、sds++ システム コンパイラはサブプロセスを呼び出してコンパイルおよびリンクを実行します。

次の図に示すように、コンパイル段階では、CPU で実行されるオブジェクト コードのコンパイルだけでなく、Vivado 高位合成 (HLS) ツールを使用したハードウェア関数の IP ブロックへのコンパイルおよびリンク、およびターゲット CPU ツールチェーンを使用した標準オブジェクト ファイル (.o) の作成も実行されます。システムのリンク段階では、すべてのハードウェア関数における呼び出し元/呼び出し先の関係のプログラム解析、各ハードウェア関数呼び出しをインプリメントするアプリケーション特定のハードウェア/ソフトウェア ネットワークの生成が実行されます。sds++ システム コンパイラは、Vivado HLS (関数コンパイラ)、生成されたハードウェア システムをインプリメントする Vivado Design Suite、CPU で実行されるアプリケーション バリアを作成する Arm コンパイラおよび sds++ リンカーなど必要なすべてのツールを呼び出して、SD カード用の完全なブート可能イメージを出力することにより、各ハードウェア関数用のアクセラレータ (スタブ) を呼び出します。

図: SDSoC のビルド プロセス

コンパイル プロセスには、次のタスクが含まれます。

  1. コードが解析され、Arm コアの main アプリケーションがコンパイルされ、各ハードウェア アクセラレータに対して個別にコンパイルが実行されます。
  2. 標準 GNU Arm コンパイル ツールでアプリケーションがコンパイルされ、最終出力としてオブジェクト ファイル (.o) が生成されます。
  3. ハードウェアでアクセラレーションされる関数が HLS ツールで実行され、カスタム ハードウェア作成プロセスが開始し、出力としてオブジェクト ファイル (.o) が生成されます。

コンパイル後のリンク プロセスには、次のタスクが含まれます。

  1. デザイン内のデータ移動が解析され、ハードウェア プラットフォームがアクセラレータを受け入れるよう変更されます。
  2. Vivado Design Suite で合成およびインプリメンテーションが実行されてハードウェア アクセラレータがプログラマブル ロジック (PL) 領域にインプリメントされ、デバイス用のビットストリームが生成されます。
  3. エンベデッド プロセッサ アプリケーションからハードウェア関数を呼び出すため、ソフトウェア イメージがハードウェア アクセス API でアップデートされます。
  4. ボードを ELF (Executable and Linkable Format) ファイルのアプリケーションでブートする統合 SD カード イメージが生成されます。

SDSoC プログラミング フローの概要

エンベデッド システムの開発には、コードの開発、プラットフォーム/デバイスのコンパイルおよびリンク、システム パフォーマンスのプロファイリング、および実際のパフォーマンスの計測などの段階があります。

SDSoC 環境はこの標準的なソフトウェア中心フローに従いますが、まずハードウェア関数を定義してからエンベデッド アプリケーションに統合するハードウェア中心フローもサポートします。これら 2 つのフローはヘテロジニアス プログラミング モデルであるところが特有であり、システムの CPU 側のコード記述はプログラマブル ロジック用のコード記述とは異なります。

ソフトウェア中心手法では、エンベデッド プロセッサ アプリケーション、およびザイリンクス デバイスのプログラマブル ロジック (PL) 領域で実行されるハードウェア関数への特定ソフトウェア関数のアクセラレーションに重点を置きます。これには、ソフトウェア関数の C または C++ コードを Vivado HLS を使用してプログラマブル ロジックにコンパイル可能なハードウェア記述言語 (HDL) に変換する必要があります。

典型的なアクセラレータ コードは、プロセッサ負荷の高く (時間のかかる複雑な計算など) で、多量のデータを処理します。このコードは、データ転送がアクセラレータを行き来するストリーミング データに制限されるように記述し、命令レベルの並列処理とタスク レベルの並列処理を使用して、デバイスのプログラマブル ロジック領域にある並列処理の多いアーキテクチャの利点が生かせるようにしておく必要があります。目的は、並列処理を使用してアクセラレーションされた関数で必要なパフォーマンスを達成することにあります。アクセラレータの目的は、できるだけ速く入力データを送信し、入力データを消費して、処理し、データを出力することです。

プロセッサ およびアクセラレータ コードが記述されたら、エミュレーション用またはハードウェア プラットフォームへのコンパイル/リンク用にコンパイルできます。エミュレーション用の場合、すばやくデザイン イテレーションできるようにコードがより速くコンパイルされ、パフォーマンスの見積もりやデータ インテグリティに使用できますが、実際のハードウェアよりも実行速度は遅くなります。エミュレーションは、同じ CPU コードが Quick Emulator (QEMU) とターゲット デバイスの両方で実行されるので、ハードウェア上で実行されるものにかなり近くなります。

ハードウェア プラットフォームへのビルドでは、プロセッサおよびハードウェア アクセラレータの記述どおりに実行されます。ハードウェア上で実行する利点は、実際の実行時間を正確に計測できる点と、インサーキット デバッグまたはパフォーマンス解析用に後でビルドを調整できる点にあります。

ハードウェア中心の手法は、FPGA 用のデザイン開発に精通した開発者向きです。この手法では、アクセラレータにどの機能を含めるか、ロジックと CPU 間でデータ/コマンドをどのように転送するかをユーザーが制御できます。このフローでは、Vivado Design Suite を使用してプログラマブル ロジック (PL) 領域とプロセッシング システム (PS) 間の通信に使用される AXI インターフェイスを含むカスタム IP を作成します。この IP を sdx_pack コマンドでパッケージして IP の AXI インターフェイスをヘッダー ファイルにマップし、スタティック ライブラリを作成します。この作成されたインクルード ファイルおよびスタティックライブラリは、ライブラリ関数を呼び出すだけで使用できるようになります。この際、ヘッダー ファイルのデータ幅と IP で必要なデータ幅を一致させることが重要です。C 呼び出し可能 IP を作成および使用する詳細は、SDSoC 環境ユーザー ガイド を参照してください。