インテル FPGA SDK for OpenCLプロ・エディション: プログラミング・ガイド
インテル FPGA SDK for OpenCL の概要
インテル FPGA SDK for OpenCL プロ・エディション・プログラミング・ガイドのご利用における前提条件
インテル® FPGA SDK for OpenCL™ または、OpenCL向けのインテルFPGAランタイム環境 (RTE) を使用しデバイスをプログラムする前に、それぞれのスタートガイドを十分に理解してから作業を開始してください。なお、このプログラミング・ガイドは次の作業をすでに実行していることを前提としています。
- OpenCLカーネルを開発および展開するためのtarファイルのダウンロード。また、インストーラーを実行しSDK、 インテル® Quartus® Prime開発ソフトウェア・プロ・エディション、デバイスサポートのインストール。
- OpenCLカーネルを展開するためのRTEのダウンロードおよびインストール。
- SDKもしくはRTEを使用しインテルSoC FPGAをプログラムする場合は、 インテル® SoC FPGAエンベデッド開発スイート (EDS) プロ・エディションのダウンロードとインストール。
- FPGAボードのインストールと設定。
- ボードのインストールが正常に終了し、正確に機能するかの確認。
上記作業を実行していない場合は、SDKのスタートガイドを参照し詳細を確認ください。
インテル FPGA SDK for OpenCLのFPGAプログラミング・フロー
次のSDKコンポーネントと連携し、インテルFPGAのプログラムを作成します。
- ホスト・アプリケーションとホスト・コンパイラー
- OpenCLカーネルとオフライン・コンパイラー
- カスタム・プラットフォーム
カスタム・プラットフォームは、ボード・サポート・パッケージを提供します。ボードメーカーは通常、特定のOpenCLボードをサポートするカスタム・プラットフォームを開発します。オフライン・コンパイラーは、OpenCLカーネルをコンパイルしハードウェア・プログラミング・イメージを生成する際に、カスタム・プラットフォームをターゲットにします。ホストはその後ホスト・アプリケーションを実行し、通常、ハードウェア・イメージをFPGAにプログラムし実行します。
例えば、従来のプロセッサーなどでのシーケンシャルなプログラムの実装では、プログラムカウンターが、ハードウェアで実行される命令のシーケンスと、ハードウェアで順次実行される命令を制御します。 インテル® FPGA SDK for OpenCL™ などでの空間的なプログラムの実装では、前提条件となるデータが利用可能になるとすぐに命令が実行されます。プログラムは、データの依存関係を表す連続した流れとして解釈されます。
インテル FPGA SDK for OpenCLオフライン・コンパイラーのカーネル・コンパイル・フロー
OpenCLカーネル・ソースファイル (.cl) は、FPGAで動作するOpenCLカーネルのソースコードを含みます。オフライン・コンパイラーは、1つもしくは複数のカーネルを一時ファイルにグループ化した後、この一時ファイルをコンパイルして以下のファイルとフォルダーを生成します。
- 中間オブジェクト・ファイルである.aoco オブジェクト・ファイル。このファイルには、コンパイルの後半段階に向けての情報が含まれます。
- ハードウェア・コンフィグレーション・ファイルである.aocxイメージファイル。このファイルは、ランタイムにFPGAをプログラミングするために必要な情報を含みます。
- .aocxファイルの作成に必要なデータを含む作業フォルダーまたはサブディレクトリー。デフォルトの状態で作業ディレクトリーの名前は、ご自身の.clファイル名です。複数のカーネル・ソースファイルをコンパイルする場合、作業ディレクトリーの名前は、 aocコマンドラインの最後にリストした.clファイル名です。
.aocxファイルは、ホスト・アプリケーションがターゲットFPGAのプログラム・オブジェクトを作成するために使用するデータを含みます。プログラム・オブジェクトは、OpenCLランタイムAPIの概念です。ホスト・アプリケーションはまず、これらのプログラム・オブジェクトをメモリーにロードします。ホストランタイムはその後、これらのプログラム・オブジェクトを使用し、ホストプログラムのカーネル起動動作の要求に基づきターゲットFPGAをプログラムします。
シンプルなカーネルに向けたワンステップ・コンパイル
次の図は、ワンステップでコンパイルを行うOpenCLカーネルのデザインフローを表しています。
コンパイルが正常に終了すると、次のファイルとレポートが作成されます。
- .aocoファイル
- .aocrファイル
- .aocxファイル
- <your_kernel_filename>/reports/report.htmlファイルの概算リソース使用率の要約では、暫定的に評価されたエリア使用率が提供されます。単一ワークアイテムのカーネルの場合は、最適化に向けたレポートでボトルネックが特定されます。
複数のステップによる インテル FPGA SDK for OpenCL プロ・エディション・デザイン・フロー
次の図は、SDKのデザインフローを段階で表しています。デザインフローの各ステップは、機能的なエラーとパフォーマンスのボトルネックを特定するためのチェックポイントとしての役割を果たします。これにより、イタレーションごとにフルコンパイルを実行することなく、OpenCLのカーネルコードを修正することができるようになります。コンパイルステップは一部、もしくはすべてを実行することのどちらも可能です。
SDKのデザインフローには次のステップがあります。
- エミュレーション
x86-64ホストシステムの1つもしくは複数のエミュレーション・デバイスでOpenCLカーネルを実行し、その機能性を評価します。Linuxシステムでは、エミュレーターはシンボリック・デバッグのサポートを提供します。シンボリック・デバッグを使用すると、カーネルコードの機能的なエラーの原因を特定できます。
- 中間コンパイル
中間コンパイルを行う方法は2つあります。デザインフローにはこの2つの方法のうち1つ、もしくは両方を含むことが可能です。
- -cフラグを使用し1つもしくは複数の.clカーネル・ソース・ファイルをコンパイルする方法。これにより、OpenCLパーサーからの出力を含む.aocoオブジェクト・ファイルを生成するよう、オフライン・コンパイラーに命令が出されます。
-
-rtlフラグを使用し1つもしくは複数の.clカーネル・ソース・ファイルまたは.aocoファイルをコンパイルする方法。ただし、コンパイルはどちらか一方のファイルになります。これにより、オフライン・コンパイラーに以下のタスクを実行するよう命令が出されます。
- 入力ファイルが.clファイルの場合、オフライン・コンパイラーは.aocoファイルを各カーネル・ソース・ファイルに生成し、それらをリンクし.aocrファイルを生成します。
- 入力ファイルが.aocoファイルの場合、オフライン・コンパイラーはそれらをリンクし.aocrファイルを生成します。
- <your_kernel_filename> ディレクトリーを作成します。
オフライン・コパイラーは、.aocrファイルを使用し最終的な.aocxハードウェア・コンフィグレーション・ファイルを生成します。注: デフォルトのボードがXの環境において-cフラグでカーネルをコンパイルし、デフォルトのボードがYの環境において.aocoファイルを-rtlフラグでコンパイルする場合、オフライン・コンパイラーは.aocoファイルからボードXを読み出し、それを続くコンパイルの段階へ渡します。
- HTMLレポートのレビュー
OpenCLアプリケーションの <your_kernel_filename>/reports/report.htmlファイルをレビューし、概算のカーネルのパフォーマンス・データが許容できるかを判断します。HTMLレポートでは、パフォーマンスを向上させるためのカーネルの修正案も提供されます。
- シミュレーション (プレビュー)
OpenCLカーネルをシミュレーションで実行し、その機能性を評価します。シミュレーションでは、コンパイルに長時間を費やすことなくカーネルの機能の正確性および動的なパフォーマンスの評価を行うことができます。カーネルのデバッグに向け、カーネルの波形をキャプチャーし表示することが可能です。
- 高速コンパイル
ハードウェアでのOpenCLカーネルの機能性を評価します。高速コンパイルステップは、フルコンパイルに比べわずかな時間で.aocxファイルを生成します。 インテル® FPGA SDK for OpenCL™オフライン・コンパイラーは簡単な最適化のみを実行し、コンパイル時間を削減します。
- インクリメンタル・コンパイル
ハードウェアでのOpenCLカーネルの機能性を評価します。インクリメンタル・コンパイルでは、修正したカーネルのみをコンパイルし.aocxファイルを生成します。 インテル® FPGA SDK for OpenCL™オフライン・コンパイラーは、デザイン全体のサイズではなく、変更したデザインのサイズによってコンパイル時間をスケーリングし、生産性を向上させます。
- プロファイリング
FPGAプログラミング・イメージにパフォーマンス・カウンターを挿入するようインテル FPGA SDK for OpenCL オフライン・コンパイラーに命令します。実行中、カウンターは性能情報を集めます。その情報は、 インテル® FPGA Dynamic Profiler for OpenCL™ GUIで確認することが可能です。
- フル・デプロイメント
デザインフロー全体におけるOpenCLカーネルのパフォーマンスに納得したら、フルコンパイルを実行します。実行後、デプロイメントに適した.aocxファイルが生成されます。
HTMLレポートとカーネルのプロファイリングについては、 インテル® FPGA SDK for OpenCL™ : ベスト・プラクティス・ガイドを参照ください。
ソフトウェア、コンパイラー、カスタム・プラットフォームに関する一般的な情報の取得
- インテル® FPGA SDK for OpenCL™オフライン・コンパイラー・コマンド・オプション ( aoc <command_option> ) は現在、二重ダッシュ (--) ではなく単一ダッシュ (-) を使用します。二重ダッシュの規則は17.1のリリースにて非推奨となり、今後のリリースで削除される予定です。
- インテル® FPGA SDK for OpenCL™オフライン・コンパイラー・コマンド・オプションは現在、 <command_option>=<value> の規則に従っています。この規則においてvalueは、ユーザー入力値をコンマで区切ったリストとすることが可能です。 -option value1 -option value2の使用は17.1リリースで非推奨となり、今後のリリースでは削除される予定です。
- ソフトウェア・バージョンの表示 (version)
インテル® FPGA SDK for OpenCL™ のバージョンを表示するには、versionユーティリティー・コマンドを呼び出します。 - コンパイラー・バージョンの表示 (-version)
インテル® FPGA SDK for OpenCL™オフライン・コンパイラーのバージョンを表示するには、-versionコンパイラー・コマンドを呼び出します。 - インテル FPGA SDK for OpenCL ユーティリティー・コマンド・オプション・リストの表示 (help)
インテル® FPGA SDK for OpenCL™ ユーティリティー・コマンド・オプションに関する情報を表示するには、helpユーティリティー・コマンドを呼び出します。 - インテル FPGA SDK for OpenCL オフライン・コンパイラー・コマンド・オプション・リストの表示 (引数なし、-help、-h)
インテル® FPGA SDK for OpenCL™オフライン・コンパイラー・コマンド・オプションに関する情報を表示するには、引数なしでコンパイラー・コマンドを呼び出すか、-helpまたは -hコマンドオプションでコンパイラー・コマンドを呼び出します。 - 利用可能なFPGAボードとカスタム・プラットフォームのリスト表示 (-list-boardsおよび-list-board-packages)
カスタム・プラットフォームで利用可能なFPGAボードのリストを表示するには、-list-boardsオプションをaocコマンドに含めます。 - OpenCLバイナリーのコンパイル環境の表示 (env)
インテル® FPGA SDK for OpenCL™オフライン・コンパイラーの入力引数およびコンパイルされたOpenCLデザインの環境を表示するには、envユーティリティー・コマンドを呼び出します。
ソフトウェア・バージョンの表示 (version)
aocl <version>.<build> (Intel(R) FPGA SDK for OpenCL(TM), Version <version> Build <build>, Copyright (C) <year> Intel Corporation)
コンパイラー・バージョンの表示 (-version)
Intel(R) FPGA SDK for OpenCL(TM), 64-Bit Offline Compiler Version <version> Build <build> Copyright (C) <year> Intel Corporation
インテル FPGA SDK for OpenCL ユーティリティー・コマンド・オプション・リストの表示 (help)
インテル FPGA SDK for OpenCL ユーティリティー・コマンド・オプション情報の表示 (help <command_option>)
aocl install - Installs a board onto your host system. Usage: aocl install Description: This command installs a board's drivers and other necessary software for the host operating system to communicate with the board. For example this might install PCIe drivers.
インテル FPGA SDK for OpenCL オフライン・コンパイラー・コマンド・オプション・リストの表示 (引数なし、-help、-h)
- aoc
- aoc -help
- aoc -h
利用可能なFPGAボードとカスタム・プラットフォームのリスト表示 (-list-boardsおよび-list-board-packages)
-
コマンドプロンプトで、
aoc
-list-boards
コマンドを呼び出します。
インテル® FPGA SDK for OpenCL™オフライン・コンパイラーは、以下のような出力を生成します。
Board list: <board_name_1> <board_name_2> ...
この <board_name_N> は、特定のFPGAボードをターゲットにするためにaocコマンドで使用するボード名です。
-
複数のカスタム・プラットフォームがインストールされている場合に、特定のカスタム・プラットフォームで利用可能なFPGAボードの一覧を表示するには、-board-package=<custom_platform_path> オプションをaocコマンドに含めます。コマンドプロンプトで、次のようにコマンドを呼び出します。
aoc -board-package=<custom_platform_path> -list-boards
インテル® FPGA SDK for OpenCL™オフライン・コンパイラーは、カスタム・プラットフォームで利用可能なボードの一覧を表示します。 -
システムで利用可能なカスタム・プラットフォームを一覧表示するには、-list-board-packagesオプションをaocコマンドに含めます。コマンドプロンプトで、
aoc
-list-board-packages
コマンドを呼び出します。
インテル® FPGA SDK for OpenCL™オフライン・コンパイラーは、次のような出力を生成します。
Installed board packages: <board_package_1> <board_package_2> ... Board packages shipped with Intel(R) FPGA SDK for OpenCL(TM): <board_package_3> <board_package_4> ...
この <board_package_N> は、ご自身のシステムにインストールされているカスタム・プラットフォームのボードパッケージ、または、 インテル® FPGA SDK for OpenCL™ に同梱されているボードパッケージです。
OpenCLバイナリーのコンパイル環境の表示 (env)
INPUT_ARGS=-march=emulator -v device/vector_add.cl -o bin/vector_add.aocx BUILD_NUMBER=90 ACL_VERSION=16.1.0 OPERATING_SYSTEM=linux PLATFORM_TYPE=s5_net
FPGAボードの管理
aocl installユーティリティーを使用し、同じシステムに複数のカスタム・プラットフォームを同時にインストール可能です。カスタム・プラットフォームのサブディレクトリーには、board_env.xmlファイルが含まれます。
複数のカスタム・プラットフォームがあるシステムでは、カスタム・プラットフォームのメモリーマップド・デバイス (MMD) ライブラリー・ディレクトリーに直接リンクするのではなく、ホストプログラムがFPGAクライアント・ドライバー (FCD) を使用しボードを検出するようにしてください。FPGAクライアント・ドライバーの旧称はアルテラ・クライアント・ドライバー (ACD) です。
aocl installユーティリティーを実行するとFCDが設定されます。インストールされたBSPはシステムにレジスターされるため、ランタイムおよびSDKユーティリティーは必要なBSPファイルを見つけることができます。
- aocl uninstallユーティリティーを使用し、現在の位置からBSPをアンインストールします。
- BSPディレクトリーを変更します。
- aocl installユーティリティーを使用し、新しい位置にBSPを再インストールします。
- FPGAボードのインストール (install)
aocl install <path_to_customplatform> ユーティリティー・コマンドを呼び出し、ご自身のボードをホストシステムにインストールしてください。 - FPGAボードのアンインストール (uninstall)
FPGAボードをアンインストールするには、uninstallユーティリティー・コマンドを呼び出しカスタム・プラットフォームのアンインストール後に、関連する環境変数の設定を解除します。 - FPGAボードのデバイス名の照会 (diagnose)
アクセラレーター・ボードのリストを照会すると、OpenCLソフトウェアは、使用するマシンにインストールされているデバイスのリストをデバイス名の順に生成します。 - ボード診断テストの実行 (diagnose <device_name>)
特定のFPGAボードで詳細な診断を実行するには、diagnose ユーティリティー・コマンドの引数として <device_name> を含みます。 - オフラインまたはホストなしでのFPGAのプログラミング (program <device_name>)
オフラインまたはホストなしでFPGAデバイスをプログラミングするには、programユーティリティー・コマンドを呼び出します。 - フラッシュメモリーのプログラミング (flash <device_name>)
カスタム・プラットフォームでサポートされている場合は、flashユーティリティー・コマンドを呼び出し、指定されたスタートアップ・コンフィグレーションでFPGAを初期化します。
FPGAボードのインストール (install)
- ボードメーカーが案内している手順に従い、FPGAボードをシステムに接続します。
-
ボードメーカーのWebサイトから、ご自身のFPGAボード用のカスタム・プラットフォームをダウンロードします。
インテル®
FPGA SDK for OpenCLリファレンス・プラットフォームのダウンロードついては、
インテル®
FPGA SDK for OpenCLプラットフォームのページを参照ください。
ヒント: インテル® FPGA SDK for OpenCL™ とともに提供されているBSP (a10_ref、 s10_ref、a10socなど) をインストールする場合は、カスタム・プラットフォームのインストールは不要です。BSPファイルは、 INTELFPGAOCLSDKROOT/hld/boardsにあります。手順4 に進んでください。
-
システムフォルダー以外の任意のフォルダーにカスタム・プラットフォームをインストールします。
複数のカスタム・プラットフォームでのaocl diagnoseなど、SDKユーティリティーを使用し、同じシステムに複数のカスタム・プラットフォームを同時にインストールできます。カスタム・プラットフォームのサブディレクトリーは、board_env.xmlファイルを含みます。
複数のカスタム・プラットフォームがあるシステムでは、カスタム・プラットフォームのメモリーマップド・デバイス (MMD) ライブラリー・ディレクトリーに直接リンクするのではなく、ホストプログラムがFPGAクライアント・ドライバー (FCD) を使用しボードを検出するようにしてください。FCDがカスタム・プラットフォームに対し正しく設定されている限り、FCDはインストールされているボードすべてをランタイムに検出します。
- QUARTUS_ROOTDIR_OVERRIDEユーザー環境変数が、 インテル® Quartus® Prime開発ソフトウェア・プロ・エディションのソフトウェアのインストール・ディレクトリーを指定するように設定します。
-
カスタム・プラットフォーム・ライブラリーへのパス (ボード・サポート・パッケージのMMDライブリーへのパスであれば、<path_to_customplatform>/windows64/binなど) を、PATH (Windows) もしくはLD_LIBRARY_PATH (Linux) 環境変数設定に追加します。
インテル® FPGA SDK for OpenCL™ スタート・ガイドは、init_openclスクリプトに関するより詳細な情報を提供しています。ユーザー環境変数の設定およびinit_openclスクリプトの実行に関しては、 インテル® FPGA SDK for OpenCL™ ユーザー環境変数の設定 の章を参照ください。
-
コマンドプロンプトで、
aocl
install
<path_to_customplatform>
を呼び出します。
aocl install <path_to_customplatform> を呼び出すと、FCDとボードドライバーがインストールされ、ホスト・アプリケーションとハードウェアのカーネルプログラムの通信が可能になります。要確認:
- ボードのインストールには管理者権限が必要です。Windowsコマンドプロンプトを管理者として実行するには、Start > All Programs > Accessoriesをクリックします。AccessoriesのCommand Promptを右クリックし、右クリックメニューにあるRun as Administratorをクリックします。
Windows 8.1またはWindows 10のシステムでは、署名付きドライバーの検証を無効にしなければならない場合があります。詳細については以下の内容を参照してください。
- Windows 8: https://www.intel.com/content/altera-www/global/en_us/index/support/support-resources/knowledge-base/solutions/fb321729.html
- Windows 10: https://www.intel.com/content/altera-www/global/en_us/index/support/support-resources/knowledge-base/embedded/2017/Why-does-aocl-diagnose-fail-while-using-Windows-10.html
- システムにドライバーがすでにインストールされており、管理者権限なしでFCDをインストールする必要がある場合、以下のようにaocl installコマンドを-fcd-onlyフラグとともに呼び出し、FCDインストールのプロンプトに従ってください。
aocl install <path_to_customplatform> -fcd-only
- ボードのインストールには管理者権限が必要です。Windowsコマンドプロンプトを管理者として実行するには、Start > All Programs > Accessoriesをクリックします。AccessoriesのCommand Promptを右クリックし、右クリックメニューにあるRun as Administratorをクリックします。
-
ご自身のマシンにインストールされたFPGA デバイスのリストを照会するには、
aocl
diagnose
コマンドを呼び出します。
ソフトウェアは、<device_name> を含む出力を生成します。<device_name> は、acl0からacl127のacl番号です。重要: aocl diagnoseユーティリティーを実装した後に発生する可能性があるエラーに関しては、Intel Arria 10 GX FPGA Development Kit Reference Platform Porting Guideの、Possible Errors After Running the diagnose Utilityの章を参照ください。ご自身のアクセラレーター・ボードの <device_name> の照会に関しては、FPGAボードのデバイス名の照会の章を参照ください。
- FPGAボードが正常にインストールされていることを確認するには、 aocl diagnose <device_name> コマンドを呼び出し、ボードメーカーが推奨する診断テストを実行します。
FPGAボードのアンインストール (uninstall)
FPGAボードをアンインストールするには、次の手順を行います。
- ボードメーカーから提供されている手順に従い、マシンからボードを外します。
-
aocl
uninstall
<path_to_customplatform>
ユーティリティー・コマンドを呼び出し、現在のホスト・コンピューター・ドライバーを削除します (PCIe®ドライバーなど)。
インテル® FPGA SDK for OpenCL™
は、FPGAボードと通信するためにそれらのドライバーを使用します。
要確認:
- カスタム・プラットフォームのアンインストールには、root権限が必要です。ドライバーを維持しながらインストールされているFCDを削除するには、以下のように
aocl
uninstall
コマンドを-fcd-onlyフラグとともに呼び出し、FCDのアンインストールのプロンプトに従ってください。
aocl uninstall <path_to_customplatform> -fcd-only
- Linuxシステムにおいて、FCDをデフォルト・ディレクトリーである/ opt / Intel / OpenCL / Boards /以外の特定のディレクトリーにインストールした場合、アンインストールを実行する前に、環境変数ACL_BOARD_VENDOR_PATHが、その特定のFCDのインストール・ディレクトリーを指すよう設定されていることを確認してください。
- カスタム・プラットフォームのアンインストールには、root権限が必要です。ドライバーを維持しながらインストールされているFCDを削除するには、以下のように
aocl
uninstall
コマンドを-fcd-onlyフラグとともに呼び出し、FCDのアンインストールのプロンプトに従ってください。
- カスタム・プラットフォームをアンインストールします。
- LD_LIBRARY_PATH環境変数 (Linuxの場合) またはPATH環境変数 (Windowsの場合) の設定を解除します。
FPGAボードのデバイス名の照会 (diagnose)
aocl diagnose: Running diagnostic from ALTERAOCLSDKROOT/board/<board_name>/<platform>/libexec Verified that the kernel mode driver is installed on the host machine. Using board package from vendor: <board_vendor_name> Querying information for all supported devices that are installed on the host machine ... device_name Status Information acl0 Passed <descriptive_board_name> PCIe dev_id = <device_ID>, bus:slot.func = 02:00.00, at Gen 2 with 8 lanes. FPGA temperature=43.0 degrees C. acl1 Passed <descriptive_board_name> PCIe dev_id = <device_ID>, bus:slot.func = 03:00.00, at Gen 2 with 8 lanes. FPGA temperature = 35.0 degrees C. Found 2 active device(s) installed on the host machine, to perform a full diagnostic on a specific device, please run aocl diagnose <device_name> DIAGNOSTIC_PASSED
ボード診断テストの実行 (diagnose <device_name>)
オフラインまたはホストなしでのFPGAのプログラミング (program <device_name>)
<device_name> は、FPGAデバイスに対応するacl番号 (acl0からacl127) を指します。
<your_kernel_filename>.aocxは、ハードウェアをプログラミングするために使用する実行可能ファイルです。
フラッシュメモリーのプログラミング (flash <device_name>)
インテル® Arria® 10 SoC開発キットなどの、SoCボードのマイクロSDフラッシュカードのプログラミング例に関しては、Intel FPGA SDK for OpenCL Intel Arria 10 SoC Development Kit Reference Platform Porting Guideの、Building the SD Card Imageの章を参照ください。
<device_name> は、FPGAデバイスに対応するacl番号 (acl0からacl127) を指します。
<your_kernel_filename>.aocxは、ハードウェアをプログラミングするために使用する実行可能ファイルです。
OpenCLカーネルの構築
- カーネルの命名ガイドライン
インテルでは、ファイル名に英数字のみを含めることを推奨しています。 - データの処理効率を最適化するためのプログラミング手法
カーネルのデータ処理効率は、ループ展開、ワークグループ・サイズの設定、計算ユニットとワークアイテムの指定などの手法を実行し最適化してください。 - ローカル・メモリー・サイズへのポインターの最適化に向けたプログラミング手法
clSetKernelArgでランタイムに要求するサイズを指定することで、メモリーサイズを最適化できます。 - インテル FPGA SDK for OpenCL チャネル拡張の実装
インテル® FPGA SDK for OpenCL™ のチャネル拡張は、カーネル間のデータの受け渡し、およびカーネルの同期を高効率かつ低レイテンシーで行うメカニズムを提供します。 - OpenCLパイプの実装
インテル® FPGA SDK for OpenCL™ は、OpenCLパイプ機能に対する暫定サポートを提供します。 - 任意精度での整数の実装
インテル® FPGA SDK for OpenCL™ の任意精度での整数拡張を使用し、カスタムビット幅で整数を定義します。整数のカスタムビット幅は、最大64ビットまで定義できます。 - 条件付きコンパイルにおける定義済みプリプロセッサー・マクロの使用
定義済みプリプロセッサー・マクロを活用すると、カーネルコードの一部を条件付きでコンパイルできるようになります。 - __constantアドレス空間修飾子の宣言
__constantアドレス空間修飾子をカーネルに含める際には、いくつかの考慮すべき制限と回避策があります。 - 構造体データ型をOpenCLカーネルに引数として含める
OpenCLカーネルの構造体パラメーター (struct) は、値もしくは構造体へのポインターとして渡します。 - レジスターの推論
変数へのアクセスが固定されており、動的インデックスを必要としない場合、オフライン・コンパイラーは通常レジスターを選択します。 - 倍精度浮動小数点演算の有効化
インテル® FPGA SDK for OpenCL™ では、すべての倍精度浮動小数点関数に対し暫定的なサポートを提供します。 - 単一ワークアイテム・カーネルに向けた単一サイクル浮動小数点アキュムレーター
ループ内で累算を実行する単一ワークアイテム・カーネルは、 インテル® FPGA SDK for OpenCL™オフライン・コンパイラーの単一サイクル浮動小数点アキュムレーターの機能を活用することができます。 - 整数のプロモーション規則
intX_tのデータ型を使用する際に適用される整数のプロモーション規則は、C/C++の標準規則とは異なります。以下の異なる規則を考慮し、カーネルをデザインしてください。
カーネルの命名ガイドライン
-
ファイル名は英数字で始めてください。
OpenCL™アプリケーションのファイル名が英数字以外で始まっている場合、以下のコンパイルエラーが発生します。
Error: Quartus compilation FAILED See quartus_sh_compile.log for the output log.
-
英数字以外の文字を使用しファイル名を区別しないでください。
インテル® FPGA SDK for OpenCL™オフライン・コンパイラーは、英数字以外の文字をアンダースコア ( _ ) に変換します。ファイル名の末尾のみを英数字以外の異なる文字にし、2つのファイルを区別すると (例えば、myKernel#.clとmyKernel&.clなど)、オフライン・コンパイラーはその2つのファイル名をどちらも、<your_kernel_filename> _.cl (前述の例の場合、myKernel_.cl) に変換します。
-
Windowsシステムの場合、カーネルファイル名とそのファイルパスの合計文字数が260文字を超えないようにしてください。
64ビットのWindows 7およびWindows 8.1には、ファイルパスの長さに260文字の制限があります。カーネルファイル名とそのファイルパスの合計文字数が260文字を超えると、オフライン・コンパイラーは次のエラーメッセージを生成します。
The filename or extension is too long. The system cannot find the path specified.
コンパイラーのエラーメッセージに加え、次のメッセージが <your_kernel_filename>/quartus_sh_compile.log ファイルに表示されます。
Error: Can’t copy <file_type> files: Can’t open <your_kernel_filename> for write: No such file or directory
Windows 10では、260文字の制限を除外することができます。詳しくは、Windows 10の資料を参照してください。
-
OpenCLのカーネル・ソースファイル (.cl) に、予約されているキーワードである「kernel」、「Verilog」、または「VHDL」の名前を付けないでください。
ソースファイルにkernel.cl、Verilog.cl、またはVHDL.clの名前を付けると、オフライン・コンラパイラーは特定の内部ファイルと同じ名前の中間デザインファイルを生成するため、コンパイルエラーが発生します。
データの処理効率を最適化するためのプログラミング手法
- ループ展開 (unroll Pragma)
ループを展開するよう、または明示的にループを展開しないようオフライン・コンパイラーに指示するには、カーネルコードの展開するループの前に、unrollカーネルプラグマを挿入します。 - ネスト化されたループの結合
loop_coalesceプラグマを使用し、ループの機能に影響を与えずにネスト化されたループを1つのループに結合するよう、インテル FPGA SDK for OpenCLオフライン・コンパイラーに指示します。ループの結合は、コンパイラーにループ制御に必要なオーバーヘッドを削減するよう指示することにより、カーネルの使用領域を削減することができます。 - ループ開始間隔の指定 (II)
iiプラグマを使用し、プラグマ宣言に続くループにIIの設定を試みるよう、 インテル® FPGA SDK for OpenCL™オフライン・コンパイラー に指示します。オフライン・コンパイラーがそのループに対して指定されたIIを実現できない場合、コンパイルエラーが発生します。 - ループの並列性 (max_concurrency Pragma)
max_concurrencyプラグマを使用し、コンポーネントのループの並列性を制限できます。 - ループの投機的実行 (speculated_iterations Pragma)
speculated_iterationsプラグマを使用し、 インテル® FPGA SDK for OpenCL™オフライン・コンパイラーに、パイプライン化されたループのパフォーマンス向上を指示します。 - ワークグループ・サイズの指定
最大の、または必要なワークグループ・サイズを可能な限り指定してください。 - 計算ユニット数の指定
OpenCL™カーネルのデータ処理効率を向上させるため、カーネルの計算ユニットを複数生成するよう、 インテル® FPGA SDK for OpenCL™オフライン・コンパイラーに指示することができます。各計算ユニットは、複数のワークグループを同時に実行することができます。 - SIMDワークアイテム数の指定
インテル® FPGA SDK for OpenCL™オフライン・コンパイラーに、SIMDまたはベクトル化された状態で実行するワークグループのワークアイテム数を指定してください。 - メモリー属性の指定
OpenCLカーネルの配列宣言に、メモリー属性を適用することができます。
ループ展開 (unroll Pragma)
ループ展開は、ループ本体を複数回複製し、ループのトリップカウントを減らすことで行われます。ループの展開は、FPGAでのループ制御オーバーヘッドの削減、または防止のために行います。ループに依存関係がなく、オフライン・コンパイラーがループの反復処理を並列して実行できる場合は、ループを展開することでFPGAのレイテンシーとオーバーヘッドを低減させることもできます。
インテル® FPGA SDK for OpenCL™オフライン・コンパイラーは、プラグマでアノテートされていない場合でも、単純なループを展開する場合があります。-
可能な限り展開係数を指定してください。展開係数Nを指定するには、#pragma unroll <N>
ディレクティブをカーネルコードのループの前に挿入します。
オフライン・コンパイラーは、ループ展開を最大 <N> 回試みます。コードの一部を下記に示します。展開係数に2の値を割り当てることにより、ループを2回展開するよう、オフライン・コンパイラーに指示しています。
#pragma unroll 2 for(size_t k = 0; k < 4; k++) { mac += data_in[(gid * 4) + k] * coeff[k]; }
-
ループを完全に展開する場合、カーネルコードのループの前に#pragma unrollディレクティブのみを挿入し、展開係数を省略することができます。
オフライン・コンパイラーは、トリップカウントを認識すると完全なループ展開を試みます。展開要求が実行できない場合、オフライン・コンパイラーは警告を発行します。
- ループが展開されないようにするには、展開係数に1を指定します (#pragma unroll 1)。
ネスト化されたループの結合
ネスト化されたループを結合すると、コンポーネントのレイテンシーも低減されるため、カーネルの使用領域をさらに削減することも可能です。ただし場合によっては、ループを結合させることで重要なループを開始させるパスの間隔が長くなる場合があるため、ループの結合がカーネルすべてに適しているとは限りません。
NDRangeカーネルでは、loop_coalesceプラグマによりアノテートされていない場合でも、コンパイラーは自動的にループ結合を試みます。NDRangeカーネルでループを結合することにより、スループットが向上するとともに、カーネルの使用領域が低減します。loop_coalesceプラグマを使用すると、NDRangeカーネルにおけるループの自動結合を防ぐことができます。
#pragma loop_coalesce <loop_nesting_level>
<loop_nesting_level> パラメーターは、コンパイラーが結合を試みるネスト化されたループのレベル数をオプションで指定する整数です。<loop_nesting_level> パラメーターを指定しない場合、コンパイラーはネスト化されたループすべてを結合しようと試みます。
for (A) for (B) for (C) for (D) for (E)
- ループ (A) のループ・ネスト・レベルは1
- ループ (B) のループ・ネスト・レベルは2
- ループ (C) のループ・ネスト・レベルは3
- ループ (D) のループ・ネスト・レベルは4
- ループ (E) のループ・ネスト・レベルは3
- #pragma loop_coalesce 1をループ (A) に指定した場合、コンパイラーはこのネスト化されたループの結合を試みません。
- #pragma loop_coalesce 2をループ (A) に指定した場合、コンパイラーはループ (A) と (B) の結合を試みます。
- #pragma loop_coalesce 3を (A) に指定した場合、コンパイラーはループ (A)、(B)、(C)、(E)の結合を試みます。
- #pragma loop_coalesce 4をループ (A) に指定した場合、コンパイラーはループ (A) からループ (E) すべてのループの結合を試みます。
例
以下は、コンパイラーが2つのループを1つのループに結合する方法を簡単な例にて示しています。
#pragma loop_coalesce for (int i = 0; i < N; i++) for (int j = 0; j < M; j++) sum[i][j] += i+j;
int i = 0; int j = 0; while(i < N){ sum[i][j] += i+j; j++; if (j == M){ j = 0; i++; } }
ループ開始間隔の指定 (II)
iiプラグマは、ループがパイプライン化されている単一のワークアイテム・カーネル (シングルスレッド・カーネル) に適用されます。ループのパイプライン化および、カーネルをシングルスレッドとして処理するかどうかの、オフライン・コンパイラーの決定を左右するカーネル・プロパティーについての詳細は、 インテル® FPGA SDK for OpenCL™ : ベスト・プラクティス・ガイドの、シングル・ワーク・アイテム・カーネル対NDRangeカーネルの章を参照してください。
IIの値が高いほど、後続するループ反復の実行開始までの待機時間が長くなります。IIおよび、特定のループにおけるIIのパフォーマンスへの影響についての詳細を提供するコンパイラー・レポートに関しては、 インテル® FPGA SDK for OpenCL™ : ベスト・プラクティス・ガイドのカーネルのreport.htmlファイルのレビューの章を参照ください。
カーネルのループによっては、デフォルトでコンパイラーが選択する値よりも高いIIの値をiiプラグマで指定すると、スループットを低下させることなくカーネルの最大動作周波数 (fMAX) を向上させることができます。
- カーネルがシングルスレッドであるため、ループがパイプライン化されている。
- カーネルのスループットに重要ではないループである。
- ループの実行時間が、それに含まれる可能性のある他のループと比較して短い。
#pragma ii <desired_initiation_interval><desired_initiation_interval> パラメーターは、後続のループ反復の開始を実行するまでに待機するクロックサイクル数を指定する整数であり、必須です。
例
カーネルに、パイプライン化が可能な異なるループが2つある場合を考えます。1つはループに依存関係を持つ実行時間が短い初期化ループ、もう1つは処理の大部分を実行する実行時間が長いループです。この場合、初期化ループがデザインの全体的なスループットに与える影響が、もう一方のループに比べはるかに小さいことをコンパイラーは認識しません。可能な場合、コンパイラーは両方のループのIIを1でパイプライン化しようと試みます。
初期化ループにはループに依存関係があるため、生成されるハードウェアはフィードバック・パスを持つようになります。このようなフィードバック・パスでIIを実現すると、クロック周波数の一部が犠牲になる可能性があります。メインループのフィードバック・パスによっては、残りのデザインがより高い動作周波数で動作できる場合があります。
初期化ループに#pragma ii 2を指定すると、このループに対するIIの最適化をそれほど積極的に行う必要がないことをコンパイラーに通知します。最適化をあまり積極的に行わないことで、fmaxを制限しているパスをコンパイラーがパイプライン化できるようになり、カーネルのデザイン全体でより高いfmaxを実現できるようになります。
新しく指定されるIIにより、初期化ループの実行時間は長くなります。しかし、fmaxが高くなることでもう一方の長いループの実行時間が短縮されるため、初期化ループの実行時間の増加は補償されます。
ループの並列性 (max_concurrency Pragma)
ループの並列性とは、そのループの反復を同時に何回進行することができるかということです。デフォルトにおいてインテル FPGA SDK for OpenCLは、ループの並列性を最大化し、コンポーネントが最大のスループットで動作するように試みます。
max_concurrencyプラグマは、ループがパイプライン化されている単一のワークアイテム・カーネル (つまりシングルスレッド・カーネル) に適用されます。ループのパイプライン化および、カーネルをシングルスレッドとして処理するかどうかの、オフライン・コンパイラーの決定を左右するカーネルのプロパティーについては、 インテル® FPGA SDK for OpenCL™ : ベスト・プラクティス・ガイドのシングル・ワーク・アイテム・カーネル対NDRangeカーネルの章を参照ください。
max_concurrencyプラグマを使用すると、ループのパイプライン化に必要なオンチップ・メモリー・リソースを制御できるようになります。ループ反復の同時実行を実現するため、オフライン・コンパイラーは単一の反復に専用のメモリーコピーを作成する必要があります。このコピーはプライベート・コピーと呼ばれます。許可される並列性が高いほど、より多くのプライベート・コピーをコンパイラーは作成しなければなりません。
カーネルのHTMLレポート (report.html) は、ループの並列性に関する以下の情報を提供します。
- オフライン・コンパイラーが選択した最大の並列性
この情報は、ループ解析レポートとカーネル・メモリー・ビューアーで確認できます。
- ループ解析レポートのDetailsペインのメッセージは、同時実行の最大数はNに制限されていると報告します。注: 符号なしのNは、0以上の値になることが可能です。N = 0は、並列性が無制限であることを意味します。
- カーネル・メモリー・ビューアーのローカルメモリーのバンクビューは、プライベート・コピーの数を図式化し表示します。
- ループ解析レポートのDetailsペインのメッセージは、同時実行の最大数はNに制限されていると報告します。
- メモリー使用量への影響
この情報は、エリア解析レポートで確認可能です。Detailsペインのメッセージは、オフライン・コンパイラーがN個のループ反復を同時実行するため、N個の独立したメモリーコピーを作成したことを報告します。
パフォーマンスよりも物理メモリーの低減を優先する場合、以下のように#pragma max_concurrency <N> をループに適用します。このプラグマを適用すると、オフライン・コンパイラーは同時に実行するループの反復回数をNに制限します。ループのメモリーのプライベート・コピー数もNに減少します。
#pragma max_concurrency 1 for (int i = 0; i < N; i++) { int arr[M]; // Doing work on arr }
ローカルメモリーに作成され、ループでアクセスされるプライベート・コピーの数も、__attribute__((max_concurrency(N))) を使用し制御することができます。属性に関する詳細は、カーネルのメモリーシステムをコンフィグレーションするメモリー属性 を参照ください。__attribute__((max_concurrency(N))) を持つローカルメモリーに、#pragma max_concurency Mをともなうループでアクセスすると、オフライン・コンパイラーは同時に実行されるループの反復数をmin(M,N) に制限します。
ループの投機的実行 (speculated_iterations Pragma)
speculated_iterationsプラグマはループに適用されるため、(他のループプラグマの位置と同様に) ループの直前に配置する必要があります。以下に例を示します。
#pragma speculated_iterations k // where k >= 0
インテル® FPGA SDK for OpenCL™オフライン・コンパイラーは、他に影響を与えることなく、k回の反復を追加し実行するハードウェアを生成します。これにより、ループのIIの短縮、またはfmaxの増加どちらかが可能になります。決定を左右するのは、ループの終了条件を計算する速度です。計算に多くのサイクルを費やす場合は、speculated_iterationsを大きくすることを推奨します。
ワークグループ・サイズの指定
- カーネルにバリアーが含まれる場合、オフライン・コンパイラーは、デフォルトの最大ワークグループ・サイズである256ワークアイテムを設定します。
- カーネルが、異なるスレッドの異なる動作を可能にするOpenCL組み込み関数 (ローカルまたはグローバルスレッドID、ワークグループID) の問い合わせを行わない場合、オフライン・コンパイラーはシングルスレッド実行モードを想定し、最大ワークグループ・サイズを (1,1,1) に設定します。この場合OpenCLランタイムもまた、グローバル・エンキュー・サイズを (1,1,1) に強制するため、ループのパイプライン化の最適化がオフライン・コンパイラーで有効になります。
ワークグループ・サイズを指定するには、次のようにカーネルコードを修正します。
-
カーネルのワークグループに向けてオフライン・コンパイラーが規定するワークアイテムの最大数を指定するには、max_work_group_size(X, Y, Z) 属性をカーネルのソースコードに挿入します。
例:
__attribute__((max_work_group_size(512,1,1))) __kernel void sum (__global const float * restrict a, __global const float * restrict b, __global float * restrict answer) { size_t gid = get_global_id(0); answer[gid] = a[gid] + b[gid]; }
-
カーネルのワークグループにオフライン・コンパイラーが規定するワークアイテムの必要数を指定するには、reqd_work_group_size(X, Y, Z) 属性をカーネルのソースコードに挿入します。
例:
__attribute__((reqd_work_group_size(64,1,1))) __kernel void sum (__global const float * restrict a, __global const float * restrict b, __global float * restrict answer) { size_t gid = get_global_id(0); answer[gid] = a[gid] + b[gid]; }
計算ユニット数の指定
__attribute__((num_compute_units(2))) __kernel void test(__global const float * restrict a, __global const float * restrict b, __global float * restrict answer) { size_t gid = get_global_id(0); answer[gid] = a[gid] + b[gid]; }
SIMDワークアイテム数の指定
__attribute__((num_simd_work_items(4))) __attribute__((reqd_work_group_size(64,1,1))) __kernel void test(__global const float * restrict a, __global const float * restrict b, __global float * restrict answer) { size_t gid = get_global_id(0); answer[gid] = a[gid] + b[gid]; }
メモリー属性の指定
int __attribute__((max_concurrency(k)) local_A[M];
このkは符号なしであり、0以上の値を取ることができます。kの値は配列への最大同時アクセス数です。 インテル® FPGA SDK for OpenCL™オフライン・コンパイラーは、配列のコピーをk個作成します。k=0は、スループットを最大化するために、オフライン・コンパイラーが配列への最大同時アクセス数を最小16までの範囲で選択することを示します。これは、メモリー属性を適用しないデフォルトの動作です。
次の例は、外側のループが4つのローカル配列を宣言しています。
for (int i = 0; i < N; i++) { int local_A[M]; int local_B[M]; int local_C[M]; int local_D[M]; // Step 1 for (int j = 0; j < M; j++) { local_A[j ] = initA(); } // Step 2 for (int j = 0; j < M; j++) { local_B[j] = initB(local_A[j]); } // Step 3 for (int j = 0; j < M; j++) { local_C[j] = initC(local_B[j]); } // Step 4 for (int j = 0; j < M; j++) { local_D[j] = initD(local_C[j]); } }
この例において、外側のループは4つのステップを含み、各ステップは内側のループに対応しています。ステップ1では、最初のローカル配列local_Aが初期化されます。ステップ2では、local_Aの読み取りが行われますが、書き込みはされません。これは、外側のループでのlocal_Aの最後の使用になります。同様にlocal_Bは、ステップ2で最初に使用され初期化されます。ステップ3ではlocal_Bを読み取りますが、書き込みはされません。そしてこれが最後のlocal_Bの使用になります。同様にlocal_Cは、ステップ3と4でのみで使用されます。 インテル® FPGA SDK for OpenCL™オフライン・コンパイラーは、16個のコピーを作成し各配列をプライベート化します。このコピーは、外側のループでの16の並行処理をサポートするのに十分ですが、これらのローカル配列の実際の範囲は外側のループ全体に及ばないため、外側のループのスループットを最大化するために16のコピーすべては必要ありません。これは、コピーを作成するために消費される領域が必要以上に大きいことを意味します。この場合、max_concurrency属性を適用し、ローカル配列のコピー数を制御すると、外側のループのスループットを維持しながら、使用される領域を減らすことができます。
ローカル・メモリー・サイズへのポインターの最適化に向けたプログラミング手法
__kernel void myLocalMemoryPointer( __local float * A, __attribute__((local_mem_size(1024))) __local float * B, __attribute__((local_mem_size(32768))) __local float * C) { //statements }
myLocalMemoryPointerカーネルにおいて、ローカルメモリーの16 KB (デフォルト) はポインターAに、1 KBはポインターBに、そして32 KBはポインターCに割り当てられています。
インテル FPGA SDK for OpenCL チャネル拡張の実装
インテル FPGA SDK for OpenCL チャネル拡張の概要
チャネルの実装は、並行して実行されているカーネル間のデータ移動をホスト・プロセッサーから切り離します。
チャネルにおけるデータの動作
OpenCLの実装において、デバイスの再プログラミング動作を回避するよう最適化を実行した場合でも、チャネルのデータは、コンテキスト、プログラム、デバイス、カーネル、またはプラットフォームのリリース間では持続しません。例えば、同じ.aocxファイルを使用しホストプログラムを2回実行する場合や、ホストプログラムがコンテキストをリリースし再取得した場合、チャネルのデータは動作全体にわたって持続したりしなかったりする可能性があります。FPGAデバイスのリセット動作が、チャネルのデータを消去するオブジェクト・リリースの裏で発生する可能性があります。
次のコードを例に示します。
channel int c0; __kernel void producer() { for (int i = 0; i < 10; i++) { write_channel_intel (c0, i); } } __kernel void consumer (__global uint * restrict dst) { for (int i = 0; i < 5; i++) { dst[i] = read_channel_intel(c0); } }
カーネルのproducerは、10の要素 ([0, 9]) をチャネルに書き込みます。カーネルのコンシューマーには、ワークアイテムの識別子の問い合わせが含まれていません。よって、暗黙的なreqd_work_group_size属性 (1,1,1) を受け取ります。この暗黙的な属性reqd_work_group_size(1,1,1) は、単一のワークアイテム・カーネルとしてコンシューマーを起動させる必要があることを意味します。上記例においてconsumerは、1回の呼び出しに5つの要素をチャネルから読み取ります。最初の呼び出し中にカーネルのconsumerは、0から4の値をチャネルより読み取ります。データはNDRangeの呼び出し間で持続するため、2回目にカーネルconsumerを実行する際は、5から9の値が読み取られます。
この例の場合、デッドロックの発生を回避するため、カーネルproducerの呼び出しごとにカーネルconsumerを2回呼び出す必要があります。consumerの呼び出しが2回未満になると、チャネルの容量がフルになるためproducerがストールします。consumerの呼び出しが2回より多い場合は、チャネルに十分なデータがないためconsumerがストールします。
チャネルに対する複数のワークアイテムの順序付け
複数のワークアイテムのチャネルへのアクセスは、いくつかのシナリオにおいて有効です。例えば、チャネルのデータワードが独立している場合や、チャネルがコントロール・ロジックに向けて実装されている場合などです。複数のワークアイテムがチャネルにアクセスする際の主な注意事項は、カーネルがチャネルにデータを書き込み、チャネルからデータを読み取る順序です。SDKのチャネル拡張は、チャネルへのワークアイテムの読み書き動作を可能な限り決定論的な順序で処理します。これにより、読み取りおよび書き込み動作はカーネル呼び出し間で一貫します。
複数のワークアイテムの決定論的な順序付けにおける要件
決定論的な順序付けの保証にあたり、SDKは以下の特性に基づき、チャネルのアクセスがワークアイテムに対し不変であることを確認します。
- カーネルを通るすべてのパスが必ずチャネルアクセスを実行しているか。
- 上記要件が満たされない場合、チャネル呼び出しに到達する分岐条件はいずれも、ワークアイテムに依存しない方法で実行されているか。
- カーネルが単一のワークアイテム・カーネルとして推測されていないか。
複数のワークアイテムのチャネルへのアクセスにおいて、決定論的な順序付けを保証できない場合、SDKは、チャネルの順序付けが適切に定義されない可能性があり、非決定論的な実行が行われる可能性があることを警告します。SDKは主に、チャネル呼び出しをともなうループの実行にワークアイテムに依存するコードが存在する場合、決定論的な順序付けを提供することができません。次に例を示します。
__kernel void ordering (__global int * restrict check, __global int * restrict data) { int condition = check[get_global_id(0)]; if (condition) { for (int i = 0; i < N, i++) { process(data); write_channel_intel (req, data[i]); } } else { process(data); } }
チャネルのワークアイテムのシリアル実行
カーネルにチャネルを実装すると、 インテル® FPGA SDK for OpenCL™オフライン・コンパイラーは、カーネルの動作は計算ユニットに同時に最大1つのインフライトのワークグループを持っているのと同等であるということを適用します。コンパイラーもまた、カーネルがチャネルをシリアル実行することを保証します。よってカーネルは、IDの小さいワークアイテムから実行します。ワークアイテムは識別子 (x、y、z、group) を持ちます。このx、y、zはローカルの3D識別子であり、groupはワークグループの識別子です。
ワークアイテムID (x0、y0、z0、group0) は、次の条件のいずれかが当てはまる場合、ID (x1、y1、z1、group1) よりも小さいとみなされます。
- group0 < group1の場合
- group0 = group1、 z0 < z1の場合
- group0 = group1、 z0 = z1、 y0 < y1の場合
- group0 = group1、 z0 = z1、 y0 = y1、 x0 < x1の場合
インクリメンタルIDをともなうワークアイテムは、シーケンシャルに実行されます。例えば、ID (x0、y0、z0、group0) をともなうワークアイテムは、チャネル書き込み呼び出しをまず実行します。その後、ID (x1、y0、z0、group0) をともなうワークアイテムが呼び出しを実行します。この順序を定義することで、システムが外部モデルと検証可能になるよう保証しています。
複数のワークアイテムをともなうループのチャネル実行
次のように、複数のワークアイテムがあるループの本体にチャネルが存在する場合、各ループの反復は後続の反復の前に実行されます。これは、ワークグループの各ワークアイテムのループ反復0が、ワークグループの各ワークアイテムの反復1の前に実行されることを意味します。
__kernel void ordering (__global int * data, int X) { int n = 0; while (n < X) { write_channel_intel (req, data[get_global_id(0)]); n++; } }
インテル FPGA SDK for OpenCL チャネル拡張の実装における制約
複数のチャネル呼び出しサイト
__kernel void k1() { read_channel_intel (channel1); read_channel_intel (channel1); read_channel_intel (channel1); }
__kernel void k1(){ write_channel_intel (channel1, 1); } __kernel void k2() { write_channel_intel (channel1, 2); }
フィードバック・チャネルとフィードフォワード・チャネル
カーネルのチャネルは、read_onlyまたはwrite_onlyのどちらかになります。同じチャネルに対して読み書きを行うカーネルのパフォーマンスは低下する可能性があります。
静的なインデックス化
インテル® FPGA SDK for OpenCL™ のチャネル拡張は、チャネルIDの配列へのインデックス化をサポートしますが、非効率なハードウェアにつながります。
次に例を示します。
channel int ch[WORKGROUP_SIZE]; __kernel void consumer() { int gid = get_global_id(0); int value = read_channel_intel(ch[gid]); //statements }
この例をコンパイルすると、次の警告メッセージが表示されます。
Compiler Warning: Dynamic access into channel array ch was expanded into predicated static accesses on every channel of the array.
アクセスが動的で、配列のチャネルのサブセットのみにアクセスできることが明確な場合は、switchステートメントを使用し、わずかに効率的なハードウェアを生成することができます。
channel int ch[WORKGROUP_SIZE]; __kernel void consumer() { int gid = get_global_id(0); int value; switch(gid) { case 0: value = read_channel_intel(ch[0]); break; case 2: value = read_channel_intel(ch[2]); break; case 3: value = read_channel_intel(ch[3]); break; //statements case WORKGROUP_SIZE-1:read_channel_intel(ch[WORKGROUP_SIZE-1]); break; } //statements }
カーネルのベクトル化に対するサポート
チャネルを使用するカーネルをベクトル化することはできません。したがって、カーネルコードにnum_simd_work_itemsカーネル属性を含めないでください。チャネルを使用するカーネルをベクトル化すると、同じカーネルで複数のチャネルアクセスが発生し、調停が必要になります。これはベクトル化の利点を無効にします。よってSDKのチャネル拡張はカーネルのベクトル化をサポートしません。
read_channel_intelおよびwrite_channel_intel呼び出しにおける命令レベルの並列処理
データの依存性がread_channel_intelとwrite_channel_intel呼び出し間に存在しない場合、オフライン・コンパイラーはそれらの命令を並行して実行しようと試みます。その結果、オフライン・コンパイラーはこのread_channel_intelとwrite_channel_intel呼び出しを、OpenCLカーネルコードに表現されているシーケンスとは異なる順序で実行する場合があります。
次のコードのシーケンスを例に示します。
in_data1 = read_channel_intel(channel1); in_data2 = read_channel_intel(channel2); in_data3 = read_channel_intel(channel3);
read_channel_intel呼び出し間にデータの依存性がないため、オフライン・コンパイラーはこれらを任意の順序で実行できます。
OpenCLカーネルに向けた インテル FPGA SDK for OpenCL チャネルの有効化
チャネル拡張を有効にするには、次のプラグマを使用します。
#pragma OPENCL EXTENSION cl_intel_channels : enableチャネル宣言は、特定のOpenCLカーネルプログラムに固有のものです。チャネルのインスタンスもまた、各OpenCLカーネルプログラムのデバイスペアに固有です。ランタイムに単一の OpenCLカーネルプログラムを複数のデバイスにロードする場合、各デバイスはチャネルのコピーを1つ保有するようになります。ただし、このチャネルのコピーは独立しており、デバイス間でデータを共有しません。
チャネルのハンドル宣言
チャネルから読み書きするには、カーネルはチャネル変数を対応する各API呼び出しに渡す必要があります。
-
以下の規則を使用し、カーネルのソースコードにファイルスコープ変数としてチャネルのハンドルを宣言します。channel <type>
<variable_name>
例: channel int c;
-
インテル® FPGA SDK for OpenCL™
のチャネル拡張は、データ構造で宣言された複数の変数による同時チャネルアクセスをサポートします。次の方法で、structデータ構造をチャネルに宣言します。
typedef struct type_ { int a; int b; } type_t; channel type_t foo;
ブロッキングのチャネル書き込みの実装
以下に詳細を説明します。
channel_idは、チャネルが接続するバッファーを識別します。また、対応する読み取りチャネル (read_channel_intel) のchannel_idと一致しなければなりません。
dataは、チャネル書き込み動作がチャネルに書き込むデータです。
<Type> は、チャネルのデータ幅を定義します。OpenCL™変換規則に従い、カーネルがチャネルに書き込むデータが、<type> に変換できるようにしてください。
//Defines chan, a kernel file-scope channel variable. channel long chan; /*Defines the kernel which reads eight bytes (size of long) from global memory, and passes this data to the channel.*/ __kernel void kernel_write_channel( __global const long * src ) { for (int i = 0; i < N; i++) { //Writes the eight bytes to the channel. write_channel_intel(chan, src[i]); } }
ノンブロッキングのチャネル書き込みの実装
アプリケーションにデータ・プロデューサーが1つと、同一のワーカーが2つあるシナリオを例とします。各ワーカーがメッセージの処理に要する時間は、データの内容によって異ると想定します。この場合、一方のワーカーはビジー状態で、もう一方のワーカーはフリーの状態になる可能性があります。ノンブロッキングの書き込みは、両方のワーカーがビジー状態になるワークの分配を促進します。
channel long worker0, worker1; __kernel void producer( __global const long * src ) { for(int i = 0; i < N; i++) { bool success = false; do { success = write_channel_nb_intel(worker0, src[i]); if(!success) { success = write_channel_nb_intel(worker1, src[i]); } } while(!success); } }
ブロッキングのチャネル読み取りの実装
以下に詳細を説明します。
channel_idは、チャネルが接続するバッファーを識別します。これは、対応する書き込みチャネル (write_channel_intel) のchannel_idと一致している必要があります。
<type> は、チャネルのデータ幅を定義します。チャネルデータを読み取るためにカーネルが割り当てる変数が、<type> から変換可能であることを確認してください。
//Defines chan, a kernel file-scope channel variable. channel long chan; /*Defines the kernel, which reads eight bytes (size of long) from the channel and writes it back to global memory.*/ __kernel void kernel_read_channel (__global long * dst); { for (int i = 0; i < N; i++) { //Reads the eight bytes from the channel. dst[i] = read_channel_intel(chan); } }
ノンブロッキングのチャネル読み取りの実装
読み取りが正常に行われた場合 (validがtrueに設定される)、チャネルから読み取った値はread_channel_nb_intel関数により返されます。読み取りが失敗した場合 (validがfalseに設定される)、read_channel_nb_intel関数の戻り値は定義されません。
channel long chan; __kernel void kernel_read_channel (__global long * dst) { int i = 0; while (i < N) { bool valid0, valid1; long data0 = read_channel_nb_intel(chan, &valid0); long data1 = read_channel_nb_intel(chan, &valid1); if (valid0) { process(data0); } if (valid1) { process(data1); } } }
ioチャネル属性を使用したI/Oチャネルの実装
io("chan_id") 属性は、チャネルが接続するアクセラレーター・ボードのI/Oフィーチャーを指定します。このchan_idは、カスタム・プラットフォームのboard_spec.xmlにリストされているI/Oインターフェイス名です。
ペリフェラル・インターフェイスの使用方法はデバイスの種類によって異なるため、I/Oチャネルをカーネルプログラムに実装する際は、ボードメーカーの資料を参照してください。ご自身のOpenCL™カーネルコードは、ペリフェラル・インターフェイスが生成するデータと互換性がある必要があります。
- ボードに直接接続し、I/Oチャネルを介してペリフェラル・デバイスと通信するチャネルには、暗黙的なデータの依存関係が存在する可能性があります。 インテル® FPGA SDK for OpenCL™オフライン・コンパイラーは、これらの依存関係に対する可視性を持たないため、この暗黙的なデータの依存関係は予期しない動作を引き起こす可能性があります。
- 同じペリフェラルと通信する外部I/Oチャネルは、シーケンシャルな順序に従いません。予期しない動作が発生する可能性があるため、外部デバイスがシーケンシャルな順序を必要としないようにしてください。
-
カスタム・プラットフォームのboard_spec.xmlファイルを参照し、ご自身のFPGAボードで使用可能な入力および出力フィーチャーを特定してください。
例えば、board_spec.xmlファイルには次のようなI/Oフィーチャー情報が含まれています。
<channels> <interface name="udp_0" port="udp0_out" type="streamsource" width="256" chan_id="eth0_in"/> <interface name="udp_0" port="udp0_in" type="streamsink" width="256" chan_id="eth0_out"/> <interface name="udp_0" port="udp1_out" type="streamsource" width="256" chan_id="eth1_in"/> <interface name="udp_0" port="udp1_in" type="streamsink" width="256" chan_id="eth1_out"/> </channels>
interface要素のwidth属性は、そのチャネルで使用されるデータタイプの幅をビットで指定します。上の例の場合、uintとfloatのデータタイプはどちらも32ビット幅です。他のより大きいデータタイプやベクトル化されたデータタイプは、board_spec.xmlファイルで指定されている適切なビット幅と一致している必要があります。
-
次のコード例で示されているようにioチャネル属性を実装します。ioチャネル属性の名前は、board_spec.xmlファイルで指定されているI/Oチャネル名 (chan_id) と一致していなければなりません。
channel QUDPWord udp_in_IO __attribute__((depth(0))) __attribute__((io("eth0_in"))); channel QUDPWord udp_out_IO __attribute__((depth(0))) __attribute__((io("eth0_out"))); __kernel void io_in_kernel (__global ulong4 *mem_read, uchar read_from, int size) { int index = 0; ulong4 data; int half_size = size >> 1; while (index < half_size) { if (read_from & 0x01) { data = read_channel_intel(udp_in_IO); } else { data = mem_read[index]; } write_channel_intel(udp_in, data); index++; } } __kernel void io_out_kernel (__global ulong2 *mem_write, uchar write_to, int size) { int index = 0; ulong4 data; int half_size = size >> 1; while (index < half_size) { ulong4 data = read_channel_intel(udp_out); if (write_to & 0x01) { write_channel_intel(udp_out_IO, data); } else { //only write data portion ulong2 udp_data; udp_data.s0 = data.s0; udp_data.s1 = data.s1; mem_write[index] = udp_data; } index++; } }
重要: board_spec.xmlファイルの、XML (eXtensible Markup Language) チャネル要素で指定されている各I/Oチャネルに、固有のio("chan_id") ハンドルを宣言してください。
I/Oチャネルのエミュレーション
io属性で宣言されたチャネルをともなうカーネルをエミュレーションすると、I/Oチャネル入力はファイルからの読み取りによってエミュレーションされ、チャネル出力はファイルへの書き込みによってエミュレーションされます。
channel uint chanA __attribute__((io("myIOChannel")));
channel uint readChannel __attribute__((io("myIOChannel"))); channel uint writeChannel __attribute__((io("myIOChannel")));
I/Oチャネル読み取りのエミュレーション
- Non-blocking read
- ファイルが存在しない場合やデータが不十分な場合、読み取りの試みが失敗したことを示すメッセージを返します。
- Blocking read
- ファイルが存在しない場合やデータが不十分な場合、ディスクにファイルが作成されるか、ファイルに十分なデータが含まれるまでプログラムをブロックします。
I/Oチャネル書き込みのエミュレーション
- Non-blocking write
- 書き込みの試みが失敗した場合、エラーが返されます。
- Blocking write
- 書き込みの試みが失敗した場合、書き込みの試みがさらに行われます。
インテル FPGA SDK for OpenCL チャネル実装を活用したモデル例
次に示すモデルは、安全かつ効率的な同時実行の活用方法の概要を示しています。
フィードフォワードのデザインモデル
フィードフォワードのデザイン・モデルを実装し、サイクルを作成せずに1つのカーネルから次のカーネルにデータを送信します。次のコードを参照ください。
__kernel void producer (__global const uint * src, const uint iterations) { for (int i = 0; i < iterations; i++) { write_channel_intel(c0, src[2*i]); write_channel_intel(c1, src[2*i+1]); } } __kernel void consumer (__global uint * dst, const uint iterations) { for (int i = 0; i < iterations; i++) { dst[2*i] = read_channel_intel(c0); dst[2*i+1] = read_channel_intel(c1); } }
producerカーネルは、データをチャネルc0とc1に書き込みます。consumerカーネルは、c0とc1からデータを読み取ります。下の図は、2つのカーネル間のフィードフォワードのデータフローを表しています。
バッファー管理
フィードフォワードのデザインモデルでは、データはproducerとconsumerのカーネル間を一度に1ワードずつ移動します。複数のワードから構成される大きなデータメッセージの転送を容易にするため、通信用アプリケーションで一般的に見られるデザインパターンである、ピンポンバッファーを実装することができます。次の図は、カーネルとピンポンバッファー間の通信を表しています。
managerカーネルは、producerカーネルとconsumerカーネル間における循環バッファーの割り当ておよび割り当て解除を管理します。consumerカーネルがデータを処理した後、managerはconsumerが解放したメモリー領域を受け取り、再度使用するためにproducerに送信します。managerはまた、使用されていない位置の初期セット (トークンの初期セット) をproducerカーネルに送信し、そこにproducerがデータを書き込めるようにします。
次の図は、バッファー管理の際に発生するイベントのシーケンスを表しています。
- Managerカーネルは、トークンのセットをproducerカーネルに送信し、メモリー内のどの領域が現在使用されておらず、producerが利用できるかを示します。
- managerがメモリー領域を割り当てた後、producerはピンポンバッファーのその領域にデータを書き込みます。
-
producerは書き込み動作完了後、consumerカーネルに同期トークンを送信し、処理するデータが含まれるメモリー領域を示します。次にconsumerカーネルは、ピンポンバッファーの該当領域からデータを読み取ります。注: producer、consumer、managerカーネルは並行して実行されるため、consumerが読み取り動作を実行している間、producerは他の使用されていないメモリー位置へ処理を行うデータを書き込むことができます。
- consumerは読み出し動作が完了後、メモリー領域を解放しトークンをmanagerに送り返します。次にmanagerカーネルはその領域をリサイクルし、producerが使用できるようにします。
OpenCLカーネルへのバッファー管理の実装
SDKが適切なバッファー管理を実行するためには、チャネルの読み取りおよび書き込みの順序が重要です。次のカーネル例を参照ください。
__kernel void producer (__global const uint * restrict src, __global volatile uint * restrict shared_mem, const uint iterations) { int base_offset; for (uint gID = 0; gID < iterations; gID++) { // Assume each block of memory is 256 words uint lID = 0x0ff & gID; if (lID == 0) { base_offset = read_channel_intel(req); } shared_mem[base_offset + lID] = src[gID]; // Make sure all memory operations are committed before // sending token to the consumer mem_fence(CLK_GLOBAL_MEM_FENCE | CLK_CHANNEL_MEM_FENCE); if (lID == 255) { write_channel_intel(c, base_offset); } } }
このカーネルにおいて以下のコード行は独立しているため、 インテル® FPGA SDK for OpenCL™オフライン・コンパイラーは、これらを同時に実行するようスケジュールすることができます。
shared_mem[base_offset + lID] = src[gID];
および
write_channel_intel(c, base_offset);
base_offsetにデータを書き込み、base_offsetをチャネルへ書き込むことは、グローバルメモリーへデータを書き込むよりもはるかに早い可能性があります。consumerカーネルは次に、チャネルからbase_offsetを読み取り、それをグローバルメモリーから読み取るためのインデックスとして使用します。同期がなければ、shared_mem[base_offset + lID] = src[gID];の実行が終了する前に、consumerがproducerからデータを読み取る可能性があります。その結果、consumerは無効なデータを読み取ることになります。このシナリオを回避するには、producerカーネルがデータをメモリーに収納した後に、同期トークンが発生しなければなりません。つまりconsumerカーネルは、producerがグローバルメモリーにデータを正常に格納するまでproducerカーネルからデータを消費することはできません。
この順序を維持するには、OpenCL mem_fenceトークンをカーネルに含めます。mem_fence構造は、CLK_GLOBAL_MEM_FENCEとCLK_CHANNEL_MEM_FENCEの2つのフラグを持ちます。mem_fenceは、mem_fence呼び出し前後に発生する動作間に、制御フローの依存性を効率的に作成します。CLK_GLOBAL_MEM_FENCEフラグは、グローバルメモリー動作が制御フローに従う必要があることを示します。CLK_CHANNEL_MEM_FENCEは、チャネル動作が制御フローに従う必要があることを示します。そのため、この例にあるwrite_channel_intel呼び出しは、グローバルメモリー動作が共有メモリーバッファーに格納されるまで開始できません。
depth属性を使用するバッファーされたチャネルの実装
バッファーされたチャネルは、スループットの制限や、共有メモリーへのアクセスの同期化といった、データ・トラフィックの制御に使用することが可能です。バッファーされていないチャネルでは、読み取り動作がデータの値を読み取るまで書き込み動作を開始できません。バッファーされたチャネルでは、データの値がバッファーにコピーされるまで書き込み動作を開始できません。バッファーがフルの場合は、読み取り動作がデータの一部を読み取り、それをチャネルから削除するまで動作を開始することができません。
channel int c __attribute__((depth(10))); __kernel void producer (__global int * in_data) { for (int i = 0; i < N; i++) { if (in_data[i]) { write_channel_intel(c, in_data[i]); } } } __kernel void consumer (__global int * restrict check_data, __global int * restrict out_data) { int last_val = 0; for (int i = 0; i < N, i++) { if (check_data[i]) { last_val = read_channel_intel(c); } out_data[i] = last_val; } }
この例において書き込み動作は、ブロッキングをすることなく10個のデータ値をチャネルに書き込むことができます。チャネルがフルになると、関連する読み取り動作がチャネルに発生するまで、書き込み動作を進めることはできません。
チャネルの読み取りと書き込みの呼び出しは条件付きステートメントのため、チャネルの読み取りと書き込みの呼び出しが不均衡になる可能性があります。チャネルにバッファー容量を追加することで、producerカーネルとconsumerカーネルを確実に分離することができます。この手順は、consumerカーネルがチャネルからデータを読み取っていないときに、producerカーネルがデータを書き込んでいる場合に特に重要です。
チャネルの呼び出し順序の強制
計算ユニットを生成する際、 インテル® FPGA SDK for OpenCL™オフライン・コンパイラーは、それぞれが独立している命令のすべてに、命令レベルの並列性を必ず作成するわけではありません。そのため、チャネル読み取りと書き込み動作の間に制御やデータの依存性がない場合でも、それぞれが独立して実行されない場合があります。チャネル呼び出しが相互に通信する際、またはチャネルが外部デバイスにデータを書き込む際に、デッドロックが発生する可能性があります。
次のコード例は、producerカーネルとconsumerカーネルで構成されています。チャネルc0とc1はバッファーされていないチャネルです。c0とc1からのチャネル読み出し動作のスケジュールは、c0とc1へのチャネル書き込み動作とは逆の順序で発生する可能性があります。つまり、producerカーネルはまずc0に書き込みますが、consumerカーネルはc1を最初に読み取る場合があります。このチャネル呼び出しのスケジューリングの変更は、consumerカーネルが空のチャネルから読み取っているため、デッドロックを引き起こす可能性があります。
__kernel void producer (__global const uint * src, const uint iterations) { for (int i = 0; i < iterations; i++) { write_channel_intel(c0, src[2*i]); write_channel_intel(c1, src[2*i+1]); } } __kernel void consumer (__global uint * dst, const uint iterations) { for (int i = 0; i < iterations; i++) { /*During compilation, the AOC might reorder the way the consumer kernel writes to memory to optimize memory access. Therefore, c1 might be read before c0, which is the reverse of what appears in code.*/ dst[2*i+1] = read_channel_intel(c0); dst[2*i] = read_channel_intel(c1); } }
channel uint c0 __attribute__((depth(0))); channel uint c1 __attribute__((depth(0))); __kernel void producer (__global const uint * src, const uint iterations) { for (int i = 0; i < iterations; i++) { write_channel_intel(c0, src[2*i]); mem_fence(CLK_CHANNEL_MEM_FENCE); write_channel_intel(c1, src[2*i+1]); } } __kernel void consumer (__global uint * dst; const uint iterations) { for (int i = 0; i < iterations; i++) { dst[2*i+1] = read_channel_intel(c0); mem_fence(CLK_CHANNEL_MEM_FENCE); dst[2*i] = read_channel_intel(c1); } }
この例でproducerカーネルのmem_fenceは、c0へのチャネル書き込み動作がc1よりも前に発生するようにしています。同様にconsumerカーネルのmem_fenceは、c0のチャネル読み取り動作がc1よりも前に発生するようにしています。
チャネル使用時のカーネル間におけるメモリーの一貫性の定義
__kernel void producer( __global const uint * src, const uint iterations ) { for(int i=0; i < iterations; i++) { write_channel_intel(c0, src[2*i]); mem_fence(CLK_CHANNEL_MEM_FENCE | CLK_GLOBAL_MEM_FENCE); write_channel_intel(c1, src[2*i+1]); } }
このカーネルにおいてmem_fence関数は、c0への書き込み動作とsrc[2*i]へのメモリーアクセスが、c1への書き込み動作とsrc[2*i+1]へのメモリーアクセスよりも先に実行されるようにしています。これにより、データがc1に書き込まれる前に、c0に書き込まれたデータが読み取りチャネルから見えるようになります。
OpenCLパイプの実装
OpenCLカーネルが他のSDKと互換性があることが重要な場合にパイプを実装してください。
OpenCL Cプログラミング言語の仕様および、パイプに関する概要については、OpenCL Specification version 2.0を参照ください。
インテル® FPGA SDK for OpenCL™ のパイプの実装は、パイプの仕様全体を網羅するものではありません。そのため、OpenCL Specification version 2.0に完全には準拠していません。SDKのパイプ実装の目的は、OpenCL 2.0に準拠する異なるデバイスにおいて、一貫して機能するソリューションを提供することです。 インテル® FPGA製品に向けてパイプを有効にするには、ご自身のデザインが特定の追加要件を満たしている必要があります。
OpenCLパイプ機能の概要
パイプを実装すると、カーネルの実行がホスト・プロセッサーから切り離されます。 インテル® FPGA SDK for OpenCL™ パイプサポートの基本は、SDKのチャネル拡張です。ただし、パイプの関数の構文はチャネルの構文とは異なります。
ブロッキングおよびノンブロッキング機能に関しては、チャネルに関する次の章をそれぞれ参照ください。
パイプデータの動作
次のコードを例に示します。
__kernel void producer (write_only pipe uint __attribute__((blocking)) c0) { for (uint i = 0; i < 10; i++) { write_pipe (c0, &i); } } __kernel void consumer (__global uint * restrict dst, read_only pipe uint __attribute__((blocking)) __attribute__((depth(10))) c0) { for (int i = 0; i < 5; i++) { read_pipe (c0, &dst[i]); } }
パイプへの読み取り操作では、最初 にパイプに書き込まれたデータがまず読み出されます。パイプデータは、パイプ内のFIFOの順序を維持します。
カーネルproducerは、10個の要素 ([0, 9]) をパイプに書き込みます。カーネルconsumerはNDRangeの呼び出しごとに、パイプから5個の要素を読み出します。最初の呼び出しにおいてカーネルconsumerは、0から4の値をパイプから読み出します。データはNDRange呼び出しにわたり維持されるため、2回目のカーネルconsumer実行時には、5から9の値が読み出されます。
この例の場合、デッドロックの発生を防ぐため、カーネルproducerの1回の呼び出しに対しカーネルconsumerを2回呼び出す必要があります。consumerの呼び出しが2回未満になると、パイプ容量がフルになるためproducerがストールします。consumerの呼び出しが2回を超えると、パイプ内のデータが不足するためconsumerがストールします。
パイプにおける複数のワークアイテムの順序付け
複数のワークアイテムによるパイプへのアクセスは、いくつかのシナリオでは有効です。例えばパイプのデータワードが独立している場合や、パイプが制御ロジックに向け実装されている場合に効果があります。複数のワークアイテムがパイプにアクセスにする際に最も注意しなければならないのは、カーネルがデータをパイプへ書き込み、パイプからデータを読み出す順序です。OpenCLパイプは可能な限り、ワークアイテムのパイプへの読み書き動作を決定論的な順序で処理します。そのため、読み出し動作と書き込み動作はカーネル呼び出し間において一貫します。
複数のワークアイテムの決定論的な順序付けにおける要件
決定論的な順序付けの保証にあたり、SDKはパイプ呼び出しがワークアイテムに対し不変であることを次の点に基づき確認します。
- カーネルを通るすべてのパスがパイプ呼び出しを実行しているか
- 上の要件が満たされない場合、パイプ呼び出しに到達する分岐条件はいずれも、ワークアイテムに依存しない方法で実行されているか
SDKは、複数のワークアイテムのパイプへのアクセスにおいて決定論的な順序を保証できない場合、パイプが非決定論的な実行をともない適切な順序に定義されない可能性があることを警告します。SDKは通常、パイプ呼び出しをともなうループの実行に、ワークアイテムに依存するコードが存在する場合に、決定的な順序付けを行うことができません。
__kernel void ordering (__global int * check, global int * data, write_only pipe int __attribute__((blocking)) req) { int condition = check[get_global_id(0)]; if (condition) { for (int i = 0; i < N; i++) { process(data); write_pipe (req, &data[i]); } } else { process(data); } }
パイプにおけるワークアイテムのシリアル実行
カーネルにパイプを実装すると インテル® FPGA SDK for OpenCL™オフライン・コンパイラーは、カーネルの動作は、最大1つのインフライトのワークグループを保有しているのと同等であるということを強制します。オフライン・コンパイラーはまた、カーネルがワークアイテムのシリアル実行でパイプを実行するよう保証します。ここでカーネルは、小さなIDを有するワークアイテムをまず実行します。ワークアイテムは、(x、y、z、group)の識別子を持ち、x、y、zはローカルの3D識別子であり、groupはワークグループの識別子です。
次のいずれかの条件が当てはまる場合、ワークアイテムID (x0、y0、z0、group0) は、ID (x1、y1、z1、group1) よりも小さいとみなされます。
- group0 < group1の場合
- group0 = group1、z0 < z1の場合
- group0 = group1、z0 = z1、y0 < y1の場合
- group0 = group1、z0 = z1、y0 = y1、x0 < x1の場合
インクリメンタルIDを持つワークアイテムは、シーケンシャルに実行されます。例えば、ID (x0、y0、z0、group0) を持つワークアイテムは、書き込みチャネル呼び出しを最初に実行します。次に、ID (x1、y0、z0、group0) を持つワークアイテムが呼び出しを実行します。この順序を定義することで、システムを外部モデルと検証することが可能になります。
複数のワークアイテムを持つループでのパイプの実行
次に示すように、複数のワークアイテムを持つループの本体にパイプが存在する場合、各ループの反復は後続の反復の前に実行されます。これは、ワークグループの各ワークアイテムのループ反復0が、ワークグループの各ワークアイテムの反復1の前に実行されることを意味します。
__kernel void ordering (__global int * data, write_only pipe int __attribute__((blocking)) req) { write_pipe (req, &data[get_global_id(0)]); }
OpenCLのパイプ実装における制約
デフォルト動作
エミュレーションのサポート
インテル® FPGA SDK for OpenCL™ Emulatorは、パイプを含むカーネルのエミュレーションをサポートしています。エミュレーターのサポート範囲は、FPGAハードウェアに実装されるOpenCLパイプサポートのサブセットと一致します。
パイプAPIのサポート
現在SDKのパイプの実装は、OpenCL Specification version 2.0の組み込みパイプ関数のすべてをサポートしているわけではありません。パイプAPIにおけるサポートの有無については、OpenCL 2.0 Cプログラミング言語のパイプにおける制約の一覧を参照ください。
シングル・コール・サイト
パイプの読み取りおよび書き込み動作は決定論的に機能しないため、カーネルにはパイプIDごとに1つの呼び出しサイトしか割り当ることができません。例えば、 インテル® FPGA SDK for OpenCL™オフライン・コンパイラーは次のコード例をコンパイルすることができません。
read_pipe(pipe1, &in_data1); read_pipe(pipe2, &in_data2); read_pipe(pipe1, &in_data3);
pipe1への2回目のread_pipe呼び出しは、pipe1への2つ目の呼び出しサイトを作成するため、コンパイルは失敗します。
特定のパイプから複数のデータを収集するには、以下に示すようにパイプを複数のパイプに分割します。
read_pipe(pipe1, &in_data1); read_pipe(pipe2, &in_data2); read_pipe(pipe3, &in_data3);
1つのパイプIDに対し1つの呼び出しサイトしか割り当てることができないため、パイプを含むループを展開することはできません。次のコードを参照ください。
#pragma unroll 4 for (int i = 0; i < 4; i++) { read_pipe (pipe1, &in_data1); }
オフライン・コンパイラーは、コンパイル中に次の警告メッセージを発行します。
Compiler Warning: Unroll is required but the loop cannot be unrolled.
フィードバック・パイプとフィードフォワード・パイプ
カーネルのパイプは、read_onlyまたはwrite_onlyのどちらかです。同じパイプを読み書きするカーネルのパフォーマンスは低下します。
カーネルのベクトル化に対するサポート
パイプを使用するカーネルをベクトル化することはできません。つまり、カーネルコードにnum_simd_work_itemsのカーネル属性を含めることはできません。パイプを使用するカーネルをベクトル化すると、複数のパイプマスターが作成され、調停が必要になります。これはOpenCLパイプの仕様ではサポートされていません。
read_pipeとwrite_pipe呼び出しにおける命令レベルの並列性
read_pipeとwrite_pipe呼び出し間にデータの依存性がない場合、オフライン・コンパイラーはこれらの命令を並行して実行しようと試みます。その結果、オフライン・コンパイラーは、このread_pipeとwrite_pipe呼び出しを、OpenCLのカーネルコードで表現されているシーケンスに従わない順序で実行する可能性があります。
次のコードのシーケンスを例に示します。
in_data1 = read_pipe(pipe1); in_data2 = read_pipe(pipe2); in_data3 = read_pipe(pipe3);
read_pipe呼び出し間にデータの依存性がないため、オフライン・コンパイラーは任意の順序でそれらを実行することができます。
カーネルに向けたOpenCLパイプの有効化
パイプ宣言は、特定のOpenCLカーネルプログラムに固有のものです。また、パイプのインスタンスは、OpenCLカーネルのプログラムとデバイスのペアごとに一意のものです。ランタイムに単一のOpenCLカーネルプログラムを複数のデバイスにロードすると、各デバイスは各パイプのコピーを1つ持つようになります。ただし、これらのパイプのコピーは独立しており、デバイス間でデータを共有しません。
他のOpenCL SDKとの互換性の確保
ホストコードの変更
以下は、変更されたホスト・アプリケーションの例です。
#include <stdio.h> #include <stdlib.h> #include <string.h> #include "CL/opencl.h" #define SIZE 1000 const char *kernel_source = "__kernel void pipe_writer(__global int *in," " write_only pipe int p_in)\n" "{\n" " int gid = get_global_id(0);\n" " write_pipe(p_in, &in[gid]);\n" "}\n" "__kernel void pipe_reader(__global int *out," " read_only pipe int p_out)\n" "{\n" " int gid = get_global_id(0);\n" " read_pipe(p_out, &out[gid]);\n" "}\n"; int main() { int *input = (int *)malloc(sizeof(int) * SIZE); int *output = (int *)malloc(sizeof(int) * SIZE); memset(output, 0, sizeof(int) * SIZE); for (int i = 0; i != SIZE; ++i) { input[i] = rand(); } cl_int status; cl_platform_id platform; cl_uint num_platforms; status = clGetPlatformIDs(1, &platform, &num_platforms); cl_device_id device; cl_uint num_devices; status = clGetDeviceIDs(platform, CL_DEVICE_TYPE_ALL, 1, &device, &num_devices); cl_context context = clCreateContext(0, 1, &device, NULL, NULL, &status); cl_command_queue queue = clCreateCommandQueue(context, device, 0, &status); size_t len = strlen(kernel_source); cl_program program = clCreateProgramWithSource(context, 1, (const char **)&kernel_source, &len, &status); status = clBuildProgram(program, num_devices, &device, "", NULL, NULL); cl_kernel pipe_writer = clCreateKernel(program, "pipe_writer", &status); cl_kernel pipe_reader = clCreateKernel(program, "pipe_reader", &status); cl_mem in_buffer = clCreateBuffer(context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, sizeof(int) * SIZE, input, &status); cl_mem out_buffer = clCreateBuffer(context, CL_MEM_WRITE_ONLY, sizeof(int) * SIZE, NULL, &status); cl_mem pipe = clCreatePipe(context, 0, sizeof(cl_int), SIZE, NULL, &status); status = clSetKernelArg(pipe_writer, 0, sizeof(cl_mem), &in_buffer); status = clSetKernelArg(pipe_writer, 1, sizeof(cl_mem), &pipe); status = clSetKernelArg(pipe_reader, 0, sizeof(cl_mem), &out_buffer); status = clSetKernelArg(pipe_reader, 1, sizeof(cl_mem), &pipe); size_t size = SIZE; cl_event sync; status = clEnqueueNDRangeKernel(queue, pipe_writer, 1, NULL, &size, &size, 0, NULL, &sync); status = clEnqueueNDRangeKernel(queue, pipe_reader, 1, NULL, &size, &size, 1, &sync, NULL); status = clFinish(queue); status = clEnqueueReadBuffer(queue, out_buffer, CL_TRUE, 0, sizeof(int) * SIZE, output, 0, NULL, NULL); int golden = 0, result = 0; for (int i = 0; i != SIZE; ++i) { golden += input[i]; result += output[i]; } int ret = 0; if (golden != result) { printf("FAILED!"); ret = 1; } else { printf("PASSED!"); } printf("\n"); return ret; }
カーネルコードの変更
カーネルコードが、OpenCL Specification version 2.0に準拠するOpenCL SDKで実行される場合、 インテル® FPGA SDK for OpenCL™ で実行する前にこのコードを変更する必要があります。次のようにカーネルコードを変更してください。
- パイプ引数の名前が両方のカーネルにおいて同一になるように変更します。例えば、p_inとp_outをpに変更します。
- パイプ引数にdepth属性を指定します。ホストで保持するためにパイプが作成する最大パケット数に等しい値を、depth属性に割り当てます。
- インテル® FPGA SDK for OpenCL™ にはオフライン・コンパイラーがあるため、オフライン・コンパイル・モードでカーネルプログラムを実行します。
変更後のカーネルコードは次のようになります。
#define SIZE 1000 __kernel void pipe_writer(__global int *in, write_only pipe int __attribute__((depth(SIZE))) p) { int gid = get_global_id(0); write_pipe(p, &in[gid]); } __kernel void pipe_reader(__global int *out, read_only pipe int __attribute__((depth(SIZE))) p) { int gid = get_global_id(0); read_pipe(p, &out[gid]); }
パイプハンドルの宣言
パイプの読み書きを行うにあたり、カーネルはパイプ変数を対応する各API呼び出しに渡す必要があります。
パイプの <type> は、スカラーサイズが1024ビット以下の任意のOpenCL™組み込みスカラーまたはベクトルデータ型にすることができます。また、スカラーサイズで1024ビット以下のスカラーまたはベクトルデータ型で構成される任意のユーザー定義の型にすることも可能です。
次のパイプハンドル宣言を参照ください。
__kernel void first (pipe int c)
__kernel void second (write_only pipe int c)
最初の例では、int型の読み出し専用パイプハンドルをカーネルfirstで宣言しています。2番目の例では、書き込み専用パイプをカーネルsecondで宣言しています。カーネルfirstはパイプcからの読み取りのみを行うことができ、カーネルsecondはパイプcへの書き込みのみを行うことができます。
インテル® OpenCLシステムでは、1つのカーネルのみがパイプを読み取ることができます。同様に、1つのカーネルのみがパイプに書き込むことができます。I/O以外のパイプに、対応する読み取り動作と書き込み動作が少なくとも1つずつない場合、オフライン・コンパイラーはエラーを発行します。
インテル® FPGA SDK for OpenCL™ I/Oパイプの実装については、io属性を使用したI/Oパイプの実装を参照してください。
パイプ書き込みの実装
インテルでは、write_pipe関数の簡易版のみをサポートしています。デフォルトでwrite_pipe呼び出しはノンブロッキングです。受信するパケットを収容する容量がパイプにある場合にのみ、パイプ書き込み動作は成功します。
以下に詳細を説明します。
pipe_idはパイプが接続するバッファーを識別します。これは、対応する読み出しパイプ (read_pipe) のpipe_idと一致する必要があります。
dataはパイプ書き込み動作がパイプに書き込むデータです。これは、パイプのパケットタイプへのポインターになります。パイプへの書き込みは、データポインターのソースアドレス空間によっては、グローバルまたはローカルメモリーのロードにつながる可能性があることに注意してください。
<type> はパイプのデータ幅を定義します。戻り値はパイプ書き込み動作が成功したかどうかを示します。成功した場合の戻り値は0となり、パイプの書き込みが失敗した場合の戻り値は-1です。
/*Declares the writable nonblocking pipe, p, which contains packets of type int*/ __kernel void kernel_write_pipe (__global const long *src, write_only pipe int p) { for (int i = 0; i < N; i++) { //Performs the actual writing //Emulates blocking behavior via the use of a while loop while (write_pipe(p, &src[i]) < 0) { } } }
blocking属性を指定する場合、whileループは不要です。より良いハードウェアの実装を実現するためにインテルでは、カーネルのパイプ引数宣言にブロッキング属性 (__attribute__((blocking))) を指定することで、ブロッキングのwrite_pipe呼び出し機能を提供します。ブロッキングのwrite_pipe呼び出しは必ず成功を返します。
パイプ読み出しの実装
インテルでは、read_pipe関数の簡易版のみをサポートしています。read_pipe呼び出しは、デフォルトではノンブロッキングです。
以下に詳細を説明します。
pipe_idはパイプが接続するバッファーを識別します。これは対応するパイプ書き込み動作 (write_pipe) のpipe_idと一致する必要があります。
dataはパイプ読み出し動作がパイプから読み取るデータです。これはデータ位置へのポインターになります。read_pipe呼び出しは、データポインターのソースアドレス空間によっては、グローバルまたはローカルメモリーのロードにつながる可能性があることに注意してください。
<type> はデータのパケットサイズを定義します。
/*Declares the read_only_pipe that contains packets of type long.*/ /*Declares that read_pipe calls within the kernel will exhibit blocking behavior*/ __kernel void kernel_read_pipe(__global long *dst, read_only pipe long __attribute__((blocking)) p) { for (int i = 0; i < N; i++) { /*Reads from a long from the pipe and stores it into global memory at the specified location*/ read_pipe(p, &dst[i]); } }
より良いハードウェア実装を実現するためにインテルでは、カーネルのパイプ引数宣言にプロッキング属性 (__attribute__((blocking))) を指定することで、ブロッキングのread_pipe呼び出し機能を提供します。ブロッキングのread_pipe呼び出しは必ず成功が返されます。
depth属性を使用するバッファーされたパイプの実装
バッファーされたパイプを使用し、スループットの制限や共有メモリーのアクセスの同期化といったデータのトラフィックを制御できます。バッファーされていないパイプにおいて書き込み動作は、読み出し動作がデータの読み取りをしようとしている場合にのみ開始できます。バッファーされていないパイプは、並行して実行されるカーネルでのブロッキングの読み書き動作と組み合わせて使用してください。バッファーされていないパイプは、自己同期型のデータ転送を効率的に提供します。
バッファーされたパイプにおいて書き込み動作は、受信するパケットを収容する容量がパイプにある場合にのみ進めることが可能です。読み出し動作は、少なくとも1つのパケットがパイプになければ実行することができません。
パイプ呼び出しが書き込みカーネルと読み出しカーネルで異なって表されている場合にバッファーされたパイプを使用すると、カーネルは並行して実行されません。
__kernel void producer (__global int *in_data, write_only pipe int __attribute__((blocking)) __attribute__((depth(10))) c) { for (i = 0; i < N; i++) { if (in_data[i]) { write_pipe( c, &in_data[i] ); } } } __kernel void consumer (__global int *check_data, __global int *out_data, read_only pipe int __attribute__((blocking)) c ) { int last_val = 0; for (i = 0; i < N; i++) { if (check_data[i]) { read_pipe( c, &last_val ); } out_data[i] = last_val; } }
この例において書き込み動作は、10個のデータ値をパイプに正常に書き込むことができます。パイプがフルの状態になると書き込みカーネルは、読み出しカーネルがパイプのデータの一部を消費するまで失敗を返します。
パイプの読み出しと書き込みの呼び出しは条件付きステートメントのため、パイプの読み出しおよび書き込みの呼び出しには不均衡が発生する可能性があります。パイプにバッファー容量を追加すると、producerとconsumerカーネルを切り離すことができます。この方法は、consumerカーネルがパイプからデータを読み取っていない際に、producerカーネルがパイプにデータを書き込んでいる場合に特に重要です。
io属性を使用したI/Oパイプの実装
インテル® FPGA SDK for OpenCL™ チャネル拡張のio("chan_id") 属性は、チャネルが接続するアクセラレーター・ボードのI/Oフィーチャーを指定します。chan_id引数は、カスタム・プラットフォームのboard_spec.xmlファイルにあるI/Oインターフェイスの名前です。同じI/Oフィーチャーを使用し、I/Oパイプを識別することができます。
ペリフェラル・インターフェイスの使用法は各デバイスのタイプによって異なる可能性があるため、I/Oパイプをカーネルプログラムに実装する際は、ボードメーカーの資料を参照してください。OpenCL™カーネルのコードは、ペリフェラル・インターフェイスが生成するデータの型と互換性がなければなりません。外部I/Oパイプとカーネルのバイトの順序が異なる場合、 インテル® FPGA SDK for OpenCL™オフライン・コンパイラーは、バイトの順序を入出時にシームレスに変換します。
- ボードに直接接続しており、I/Oパイプを介してペリフェラル・デバイスと通信するパイプには、暗黙的なデータの依存性が存在する可能性があります。オフライン・コンパイラーはそれらの依存関係を認識できないため、この暗黙的なデータの依存性はコンパイルの問題を引き起こす可能性があります。
- 同一のペリフェラルと通信する外部I/Oパイプは、シーケンシャルな順序に従いません。予期しない動作が発生する可能性があるため、外部デバイスがシーケンシャルな順序を必要としないようにしてください。
-
カスタム・プラットフォームのboard_spec.xmlファイルを確認し、FPGAボードで使用可能な入力および出力フィーチャーを特定ください。
例えば、board_spec.xmlファイルには、I/Oフィーチャーに関する次のような情報が含まれています。
<channels> <interface name="udp_0" port="udp0_out" type="streamsource" width="256" chan_id="eth0_in"/> <interface name="udp_0" port="udp0_in" type="streamsink" width="256" chan_id="eth0_out"/> <interface name="udp_0" port="udp1_out" type="streamsource" width="256" chan_id="eth1_in"/> <interface name="udp_0" port="udp1_in" type="streamsink" width="256" chan_id="eth1_out"/> </channels>
interface要素のwidth属性は、そのパイプで使用されるデータの型の幅をビット単位で指定します。上記例の場合、uintとfloatのデータ型はどちらも32ビット幅です。その他のより大きなデータ型や、ベクトル化されたデータ型は、board_spec.xmlファイルで指定されている適切なビット幅に一致している必要があります。
-
次のコード例で示されているようにio属性を実装します。io属性名は、board_spec.xmlファイルで指定されているI/Oチャネル名 (chan_id) に一致しなければなりません。
__kernel void test (pipe uint pkt __attribute__((io(“enet”))),; pipe float data __attribute__((io(“pcie”))));
重要: board_spec.xmlファイル内のチャネルのXML要素で指定されている各I/Oパイプに、固有のio("chan_id") ハンドルを宣言してください。
パイプ呼び出し順序の強制
インテル® FPGA SDK for OpenCL™オフライン・コンパイラーが計算ユニットを生成する際、それぞれが独立している命令のすべてに対し、命令レベルの並列性を構築するわけではありません。そのため、パイプの読み出しおよび書き込み動作は、それらに制御やデータの依存性がない場合でも、互いに独立して実行されない可能性があります。パイプ呼び出しが相互に通信する場合や、パイプが外部デバイスにデータを書き込む場合に、デッドロックが発生する可能性があります。
次のコード例は、producerカーネルとconsumerカーネルで構成されています。パイプc0とc1はバッファーされていないパイプです。c0とc1からのパイプ読み出し動作のスケジュールは、c0とc1へのパイプ書き込み動作と逆の順序になる可能性があります。つまり、producerカーネルが最初にc0に書き込む一方で、consumerカーネルはc1から読み出す可能性があるということです。consumerカーネルが空のパイプから読み出しているため、このパイプ呼び出しにおけるスケジューリングの変更は、デッドロックを発生させる可能性があります。
__kernel void producer (__global const uint * restrict src, const uint iterations, write_only pipe uint __attribute__((blocking)) c0, write_only pipe uint __attribute__((blocking)) c1) { for (int i = 0; i < iterations; i++) { write_pipe (c0, &src[2*i ]); write_pipe (c1, &src[2*i+1]); } } __kernel void consumer (__global uint * restrict dst, const uint iterations, read_only pipe uint __attribute__((blocking)) c0, read_only pipe uint __attribute__((blocking)) c1) { for (int i = 0; i < iterations; i++) { read_pipe (c0, &dst[2*i+1]); read_pipe( c1, &dst[2*i]); } }
__kernel void producer (__global const uint * src, const uint iterations, write_only_pipe uint __attribute__((blocking)) c0, write_only_pipe uint __attribute__((blocking)) c1) { for (int i = 0; i < iterations; i++) { write_pipe(c0, &src[2*i ]); mem_fence(CLK_CHANNEL_MEM_FENCE); write_pipe(c1, &src[2*i+1]); } } __kernel void consumer (__global uint * dst; const uint iterations, read_only_pipe uint __attribute__((blocking)) c0, read_only_pipe uint __attribute__((blocking)) c1) { for(int i = 0; i < iterations; i++) { read_pipe(c0, &dst[2*i ]); mem_fence(CLK_CHANNEL_MEM_FENCE); read_pipe(c1, &dst[2*i+1]); } }
この例では、producerカーネルのmem_fenceは、c0へのパイプ書き込み動作がc1より先に発生するようにしています。同様にconsumerカーネルのmem_fenceは、c0からの読み取り動作がc1より先に行われるようにしています
パイプ使用時におけるカーネル間のメモリーの一貫性の定義
__kernel void producer (__global const uint * restrict src, const uint iterations, write_only pipe uint __attribute__((blocking)) c0, write_only pipe uint __attribute__((blocking)) c1) { for (int i = 0; i < iterations; i++) { write_pipe(c0, &src[2*i]); mem_fence(CLK_CHANNEL_MEM_FENCE | CLK_GLOBAL_MEM_FENCE); write_pipe(c1, &src[2*i+1]); } }
このカーネルでmem_fence関数は、c0への書き込み動作とsrc[2*i] へのメモリーアクセスが、c1への書き込み動作とsrc[2*i+1] へのメモリーアクセスの前に必ず実行されるようにしています。これにより、c0に書き込まれたデータは、c1にデータが書き込まれる前に読み出しパイプから見えるようになります。
ホストパイプを介したカーネルとの直接通信
この拡張機能は、clCreatePipeのflags引数に2つの新しい値を認証し、パイプホストをアクセス可能にします。また、4つの新しいAPI関数を追加し (clReadPipeIntelFPGA、clWritePipeIntelFPGA、clMapHostPipeIntelFPGA、clUnmapHostPipeIntelFPGA)、ホストのアクセスを有効にすることで作成したパイプをホストが読み書きできるようにします。新しいカーネル引数属性のオプションが追加され、カーネル言語においてパイプカーネル引数の反対側の端をホストプログラムに指定することで、パイプが別のカーネルに接続されなくなります。カーネルの定義においてパイプカーネル引数は、ホストパイプまたは他のカーネルのどちらかへの接続に限定されており、ランタイムにこの2つを動的に切り替えることはできません。
パイプカーネル引数にホストアクセス可能のマークが付けられている場合、カーネル言語のパイプアクセサーは、2.x関数のサブセットに制限され (予約はサポートされていません)、メモリーの一貫性または可視性の保証は、OpenCL同期ポイントを超えて行われます。
- このホストパイプの実装における制限は、プラットフォームが2つのホストパイプしかサポートしていないことです。1つは読み出し用、もう一つは書き出し用です。さらに、コパイラーは32ビット幅のパイプのみを許容します。よって、 cl_intel_fpga_host_pipe拡張機能の使用例の章で、ulong4が使用されています。
- cl_intel_fpga_host_pipe拡張を使用しているホストプログラムは、OpenCL 2.0の機能のサポート状況 に記されているように、CL/cl_ext_intelfpga.hヘッダーファイルを含み、OpenCL 2.0のサポートを有効にする必要があります。
intel_host_accessibleカーネル引数属性オプション
__attribute__((intel_host_accessible))
ホストがアクセス可能なパイプカーネル引数にバインドされたcl_memパイプ・オブジェクトと通信するためのAPI関数
- clReadPipeIntelFPGAおよびclWritePipeIntelFPGA関数は、パイプ幅のシングルワードで動作します。
- clMapHostPipeIntelFPGA関数は、ホストパイプで多数のワードの読み取りまたは書き込みを実行する際のレイテンシーとオーバーヘッドを低減する高度なメカニズムです。
- clUnmapHostPipeIntelFPGA関数を使用すると、ホストプログラムは、先にclMapHostPipeIntelFPGA関数呼び出しが作成したマップ領域の一部、または全体に、書き込みもしくは読み出しを行ったことをOpenCLランタイムに通知することができるようになります。
関数 | 説明 |
---|---|
cl_int clReadPipeIntelFPGA (cl_mem pipe, gentype *ptr); |
以下の特性を持つパイプからデータパケットを読み取ります。
各clReadPipeIntelFPGA関数呼び出しは、1パケットをパイプから読み取ります。動作はノンブロッキングであり、データがパイプ内で正常に読み取れるようになるまで待機しません。 |
cl_int clWritePipeIntelFPGA (cl_mem pipe, gentype *ptr); |
次の特性を持つパイプにデータパケットを書き込みます。
各clWritePipeIntelFPGA関数呼び出しは、1パケットをパイプに書き込みます。動作はノンブロッキングであり、正常に書き込める容量がパイプにできるまで待機しません。返されるステータスのCL_SUCCESSは、カーネルの読み取りにデータが利用可能なことを意味するわけではありません。ホストパイプに以前にマップされたバッファーのマップが解除されると、データは最終的にカーネルの読み取りに利用可能になります。 |
void * clMapHostPipeIntelFPGA (cl_mem pipe, cl_map_flags map_flags, size_t requested_size, size_t * mapped_size, cl_int * errcode_ret); |
ホストアドレス空間に、void *を返します。CL_MEM_HOST_WRITE_ONLYフラグで作成されている場合、パイプはこのアドレス空間にデータを書き込むことができます。また、CL_MEM_HOST_READ_ONLYフラグで作成されている場合、パイプはこのアドレス空間からデータを読み取ることができます。 mapped_size引数は、メモリーのランタイムによって決定する、ホストがアクセス可能な最大バイト数を指定します。mapped_sizeで指定される値は、呼び出し側が指定するrequested_size引数以下の値になります。 返されたvoid *に対する読み出しまたは書き込み後、ホストは1回以上のclUnmapHostPipeIntelFPGA関数呼び出しを実行し、デバイスに転送するデータが準備できていること (書き込み時) および、メモリーを再利用できること (読み込みおよび書き込みに時) をランタイムに通知する必要があります。前回のclMapHostPipeIntelFPGA関数呼び出しでマッピングされたメモリーを、clUnmapHostPipeIntelFPGA関数が解除する前に新たにclMapHostPipeIntelFPGA関数が呼び出された場合、2回目のclMapHostPipeIntelFPGA関数呼び出しで返されるバッファーは、最初の呼び出しで返されたバッファーと重複しません。 |
cl_int clUnmapHostPipeIntelFPGA ( cl_mem pipe, void * mapped_ptr, size_t size_to_unmap, size_t * unmapped_size ); |
clMapHostPipeIntelFPGA関数によって返された、ホストがアドレス指定可能なバッファーのsize_to_unmapバイトを、ホストがすでに使用していないことをランライムに通知します。書き込み可能なホストパイプの場合、clUnmapHostPipeIntelFPGAを呼び出すと、マッピングが解除されたデータをカーネルが利用できるようになります。size_to_unmapの値がclMapHostPipeIntelFPGA関数で指定されたmapped_sizeの値より小さい場合、clUnmapHostPipeIntelFPGA関数を複数回呼び出し、バッファー全容量のマッピングを解除する必要があります。clUnmapHostPipeIntelFPGA関数呼び出しを複数回含め、clMapHostPipeIntelFPGA関数呼び出しで返されたバッファーの一連のバイトのマッピングを、clMapHostPipeIntelFPGA呼び出しで定義されたmapped_sizeの値まで解除することができます。 |
ホストがアクセス可能なパイプの作成
ホストのパイプへのアクセス (読み取りまたは書き込み) を有効にするため、cl_intel_fpga_host_pipe拡張機能は、次の2つのflags値をclCreatePipeに対し正当なものにします。
- CL_MEM_HOST_READ_ONLY
- CL_MEM_HOST_WRITE_ONLY
このflagsのうち1つがclCreatePipe関数に渡されると、対応するcl_memオブジェクトは、最初の引数としてclReadPipeIntelFPGAとclWritePipeIntelFPGA関数に渡されます。cl_intel_fpga_host_pipe拡張の残りの部分において、このようなパイプはホストパイプとして表されます。
cl_intel_fpga_host_pipe拡張機能の使用例
カーネルコード
#pragma OPENCL EXTENSION cl_intel_fpga_host_pipe : enable kernel void reader(__attribute__((intel_host_accessible)) __read_only pipe ulong4 host_in) { ulong4 val; if (read_pipe(host_in, &val)) { .... } .... } kernel void writer(__attribute__((intel_host_accessible)) __write_only pipe ulong4 device_out) { ulong4 val; .... if (write_pipe(device_out, &val)) { .... } }
ホストコード
.... cl_kernel read_kern = clCreateKernel(program, "reader", NULL); cl_kernel write_kern = clCreateKernel(program, "writer", NULL); cl_mem read_pipe = clCreatePipe(context, CL_MEM_HOST_READ_ONLY, sizeof( cl_ulong4 ), 128, // Number of packets that can be buffered NULL, &error); cl_mem write_pipe = clCreatePipe(context, CL_MEM_HOST_WRITE_ONLY, sizeof( cl_ulong4 ), 64, // Number of packets that can be buffered NULL, &error); // Bind pipes to kernels clSetKernelArg(read_kern, 0, sizeof(cl_mem), (void *)&write_pipe); clSetKernelArg(write_kern, 0, sizeof(cl_mem), (void *)&read_pipe); // Enqueue kernels .... cl_ulong4 val if (!clReadPipeIntelFPGA (read_pipe, &val)) { cl_int result = clWritePipeIntelFPGA (write_pipe, &val); // Check write success/failure and handle .... } ....
任意精度での整数の実装
インテル® FPGA SDK for OpenCL™ の任意精度での整数拡張を使用し、カスタムビット幅で整数を定義します。整数のカスタムビット幅は、最大64ビットまで定義できます。
#include "ihc_apint.h"
aoc <other command options> -I $INTELFPGAOCLSDKROOT/include/kernel_headers <my_kernel_file>
#define ap_int<d> intd_t #define ap_uint<d> uintd_t
int10_t x_signed; uint10_t x_unsigned;
任意精度の整数は、最大64ビット幅まで宣言することが可能です。
#pragma OPENCL EXTENSION cl_intel_arbitrary_precision_integers : enable
ap_int<d> intd_t my_signed_integer ap_uint<d> uintd_t my_unsigned_integer
結果のビット幅が引数のビット幅より大きい演算を行う場合は、引数の1つを結果のビット幅に明示的にキャストする必要があります。
int10_t a; int10_t b; int20_t res; res = a * b;
この例においてコンパイラーは、2つの10ビットの整数を乗算する乗数をインスタンス化し、結果を別の10ビットの整数に納めようと試みます。結果は符号拡張されるか、20ビットまでゼロ拡張されます。
res = ((int20_t)a) * b
x86-64のプラットフォームに向けてプログラムをコンパイルすると、任意の精度での整数のビット幅は32ビットまたは64ビットに切り上げられます。FPGAプラットフォームにカーネルをコンパイルすると、ビット幅は切り上げられず、任意の精度の整数は宣言されたビット幅のまま維持されます。
その結果、x86-64のプログラムで正しく動作するように見える演算をFPGAカーネルにコンパイルすると、オーバーフローし精度を失う可能性があります。x86-64プラットフォームにおいて、ビット幅を切り上げることで与えられる追加精度は、FPGAカーネルのコンパイル時に発生する可能性があるオーバーフローと、精度損失の問題をマスクします。
条件付きコンパイルにおける定義済みプリプロセッサー・マクロの使用
-
デバイス固有のコード (例えばFPGA_board_1) をカーネルプログラムに含めるには、カーネルプログラムを次のように構成します。
#if defined(AOCL_BOARD_FPGA_board_1) //FPGA_board_1-specific statements #else //FPGA_board_2-specific statements #endif
カーネルのコンパイルを特定のボードに対して行うと、定義済みプリプロセッサー・マクロAOCL_BOARD_<board_name> が1に設定されます。<board_name> がFPGA_board_1の場合、 インテル® FPGA SDK for OpenCL™オフライン・コンパイラーは、FPGA_board_1に固有のパラメーターとフィーチャーをコンパイルします。 -
インテル® FPGA SDK for OpenCL™オフライン・コンパイラー固有のコンパイラー・フィーチャーと最適化を導入するには、カーネルプログラムを次のように構成します。
#if defined(INTELFPGA_CL) //statements #else //statements #endif
このINTELFPGA_CLは、 インテル® にて定義済みのオフライン・コンパイラー用のプリプロセッサー・マクロです。
__constantアドレス空間修飾子の宣言
関数スコープ__constant変数
インテル® FPGA SDK for OpenCL™オフライン・コンパイラーは、関数スコープの__constant変数をサポートしません。関数スコープの__constant変数は、ファイルスコープのconstant変数に置き換えてください。また、関数スコープの__constant変数を、ホストがカーネルに渡す__constantバッファーに置き換えることも可能です。
ファイルスコープ__constant変数
ホストが常に同じ定数データをカーネルに渡す場合、そのデータを初期化されたconstantファイルスコープ配列としてカーネルファイルで宣言することを検討ください。初期化されたconstantファイルスコープ配列宣言は、データを格納するためのROMをハードウェアに直接作成します。このROMは、NDRangeのすべてのワークアイテムが利用可能です。
以下に例を示します。
__constant int my_array[8] = {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7};
__kernel void my_kernel (__global int * my_buffer)
{
size_t gid = get_global_id(0);
my_buffer[gid] += my_array[gid % 8];
}
この場合、ファイルスコープの定数データはカーネルの呼び出しにわたって変化しないため、オフライン・コンパイラーはmy_arrayの値をROMに設定します。
ホストから__constantパラメーターへのポインター
カーネルの呼び出しにわたってデータが固定されていない場合、ファイルスコープの定数データを、カーネルコードの__constantパラメーターへのポインターに置き換えることが可能です。その後、次の方法でホスト・アプリケーションを修正する必要があります。
- グローバルメモリーのポインターに関連付けられたcl_memメモリー・オブジェクトを作成します。
- カーネルを実行する前に、clEnqueueWriteBufferで定数データをcl_memオブジェクトにロードします。
- clSetKernelArg関数で、cl_memオブジェクトをカーネルに引数として渡します。
constant変数が複合型の場合は、次の表で示されているようにtypedef引数を使用し簡潔にします。
元のソースコードの構成 | 以下のような構文に書き換え |
---|---|
__constant int Payoff[2][2] = {{ 1, 3}, {5, 3}}; __kernel void original(__global int * A) { *A = Payoff[1][2]; // and so on } |
__kernel void modified(__global int * A, __constant Payoff_type * PayoffPtr ) { *A = (PayoffPtr)[1][2]; // and so on } |
構造体データ型をOpenCLカーネルに引数として含める
ホストとカーネルにおける構造体データ型のデータレイアウトの一致
メンバーのデータ型を一致させるには、カーネルコードのデータ型に対応するデータ型のcl_バージョンをホスト・アプリケーションで使用します。データ型のcl_バージョンは、opencl.hヘッダーファイルにあります。例えばカーネルコードにfloat4型のデータメンバーがある場合、ホスト・アプリケーションにおいて宣言する対応するデータメンバーは、cl_float4です。
ホストとカーネル・アプリケーションにおける構造と、structデータメンバーのアライメントを揃えます。異なるホスト・コンパイラー間においては変動性があるため、アライメントは慎重に管理してください。
例えば構造体に、float4のOpenCLデータ型がある場合、これらのデータアイテムのアライメントはOpenCL Specificationを満たす必要があります (float4に対しては16バイトのアライメント)。
インテル® FPGA SDK for OpenCL™オフライン・コンパイラーがOpenCLカーネルをコンパイルする際は、次の規則が適用されます。
- 組み込みのスカラー型とベクトル型のアラインメントは、OpenCL Specification version 1.0のSection 6.1.5で説明されている規則に従います。
オフライン・コンパイラーは通常、サイズに基づいてデータ型のアライメントを行います。ただし、コンパイラーは3要素ベクトルの値を、4要素ベクトルと同じ方法でアライメントします。
- 配列はその要素の1つと同じアラインメントを有します。
-
struct (またはunion) は、そのデータメンバーのいずれかに必要な最大アライメントと同じアライメントを有します。
以下に例を示します。
struct my_struct { char data[3]; float4 f4; int index; };
データ型がfloat4であるため、オフライン・コンパイラーは上記のstruct要素を16バイトの境界でアライメントします。その結果、dataとindexもまた16バイトのアライメント境界を有します。
- オフライン・コンパイラーはstructのデータメンバーを並び替えません。
- オフライン・コンパイラーは通常、各データメンバーのアライメント要件を満たすため、structのデータメンバー間に最小限のデータ構造パディングを挿入します。
- OpenCLカーネルコードでは、packed属性をstruct宣言に適用することで、データのパッキングを指定することができます (すなわち、データ構造パディングを挿入しない)。データのパッキングを課す場合は、データメンバーのアラインメントがOpenCLのアラインメント要件を満たしていることを確認してください。 インテル® FPGA SDK for OpenCL™ は、このアライメント要件を強制しません。ホスト・コンパイラーがカーネル属性に配慮し、適切なアライメントを設定していることを確認してください。
- OpenCLカーネルコードでは、aligned(N) 属性をデータメンバーに適用し、データ構造パディングの量を指定することができます。ここでNは、パディングの量を表します。SDKはこのアライメント要件を強制しません。ホスト・コンパイラーがカーネル属性に配慮し、適切なアライメントを設定していることを確認してください。
Windowsシステムにおいて、Microsoft Visual Studioコンパイラーの一部のバージョンでは、デフォルトで構造体データ型をパッキングします。データのパッキングが不要な場合は、データ構造パディングの量を次のように指定してください。
struct my_struct { __declspec(align(16)) char data[3]; /*Note that cl_float4 is the only known float4 definition on the host*/ __declspec(align(16)) cl_float4 f4; __declspec(align(16)) int index; };
ヒント: データ構造パディングを追加することに代わり、char型またはchar配列のダミーのstructメンバーを挿入する方法もあります。
データ構造パディング挿入の無効化
struct __attribute__((packed)) Context { float param1; float param2; int param3; uint param4; }; __kernel void algorithm(__global float * restrict A, __global struct Context * restrict c) { if ( c->param3 ) { // Dereference through a pointer and so on } }
構造体のアライメントの指定
struct __attribute__((aligned(2))) Context { float param1; float param2; int param3; uint param4; }; __kernel void algorithm(__global float * A, __global struct Context * restrict c) { if ( c->param3 ) { // Dereference through a pointer and so on } }
レジスターの推論
オフライン・コンパイラーは、プライベート配列を単一の値のレジスター、または区分的なレジスターとして推論します。区分的な実装は非常に効率的なハードウェアをもたらしますが、オフライン・コンパイラーがデータのアクセスを静的に決定できなければなりません。区分的な実装を容易にするために、アクセスポイントを配列にハードコードします。また、配列にアクセスするループを展開することで、レジスターの推論を容易にすることもできます。
配列のアクセスを静的に推論できない場合、オフライン・コンパイラーは配列をレジスターとして推論する場合があります。ただしオフライン・コンパイラーは、単一のワークアイテム・カーネルに対し、それらの配列のサイズを64バイトに制限します。複数のワークアイテムを有するカーネルには実質的にサイズ制限はありません。
次のコードを例に示します。
int array[SIZE]; for (int j = 0; j < N; ++j) { for (int i = 0; i < SIZE - 1; ++i) { array[i] = array[i + 1]; } }
array[i]へのインデックスは、ループが展開されていないため静的に推論することができません。array[SIZE]のサイズが、単一ワークアイテム・カーネルにおいて64バイト以下である場合、オフライン・コンパイラーはarray[SIZE]を単一の値としてレジスターに実装します。array[SIZE]のサイズが、単一ワークアイテム・カーネルにおいて64バイトより大きい場合、オフライン・コンパイラーは配列全体をブロックRAMに実装します。複数のワークアイテム・カーネルの場合、サイズが1キロバイト (KB) 未満である限り、オフライン・コンパイラーは単一の値としてarray[SIZE]をレジスターに実装します。
シフトレジスターの推論
次のコードを例に示します。
channel int in, out; #define SIZE 512 //Shift register size must be statically determinable __kernel void foo() { int shift_reg[SIZE]; //The key is that the array size is a compile time constant // Initialization loop #pragma unroll for (int i=0; i < SIZE; i++) { //All elements of the array should be initialized to the same value shift_reg[i] = 0; } while(1) { // Fully unrolling the shifting loop produces constant accesses #pragma unroll for (int j=0; j < SIZE–1; j++) { shift_reg[j] = shift_reg[j + 1]; } shift_reg[SIZE – 1] = read_channel_intel(in); // Using fixed access points of the shift register int res = (shift_reg[0] + shift_reg[1]) / 2; // ‘out’ channel will have running average of the input channel write_channel_intel(out, res); } }
各クロックサイクルで、カーネルは新しい値を配列にシフトします。このシフトレジスターをブロックRAMに配置することにより、 インテル® FPGA SDK for OpenCL™オフライン・コンパイラーは、配列への複数のアクセスポイントを効率的に処理できます。シフトレジスターのデザインパターンは、フィルター (例えば、Sobelフィルターなどのイメージフィルターや、有限インパルス応答 (FIR) フィルターなどの時間遅延フィルター) を実装する際に理想的な方法です。
カーネルコードにシフトレジスターを実装する際は、次の点に注意してください。
- シフトループを展開し、配列のすべての要素にアクセスできるようにします。
- すべてのアクセスポイントは、一定のデータアクセスを持つ必要があります。例えば、複数のアクセスポイントを使用しネスト化されたループに計算を書き込む場合は、これらのループを展開し、一定のアクセスポイントを確立します。
- 配列の要素すべてを同じ値に初期化します。特定の初期値が不要な場合は、要素を初期化せずに維持することも可能です。
- 大規模な配列へのアクセスが静的に推論できない場合、オフライン・コンパイラーは非効率なハードウェアを作成することになります。それらのアクセスが必要な場合は、__privateメモリーの代わりに__localメモリーを使用してください。
- 大規模なシフトレジスターを条件付きでシフトしないでください。非効率なハードウェアの作成を防ぐため、シフトはシフトコードを含むループの反復で必ず行わなければなりません。
倍精度浮動小数点演算の有効化
OpenCLカーネルで倍精度浮動小数点のデータ型を宣言する前に、次のOPENCL EXTENSIONプラグマをカーネルコードに含めます。
#pragma OPENCL EXTENSION cl_khr_fp64 : enable
単一ワークアイテム・カーネルに向けた単一サイクル浮動小数点アキュムレーター
オフライン・コンパイラーは、値を加算または減算するアキュムレーターをサポートします。この機能を活用するには、オフライン・コンパイラーがアキュムレーターを推論できるように累算を記述します。
- アキュムレーターは、Arria® 10デバイスでのみ利用可能です。
- アキュムレーターはループの一部である必要があります。
- アキュムレーターは初期値の0を持つ必要があります。
- アキュムレーターを条件付きにすることはできません。
次は、オフライン・コンパイラーによる正しいアキュムレーターの推論をもたらす記述例です。
channel float4 RANDOM_STREAM; __kernel void acc_test(__global float *a, int k) { // Simplest example of an accumulator. // In this loop, the accumulator acc is incremented by 5. int i; float acc = 0.0f; for (i = 0; i < k; i++) { acc+=5; } a[0] = acc; } __kernel void acc_test2(__global float *a, int k) { // Extended example showing that an accumulator can be // conditionally incremented. The key here is to describe the increment // as conditional, not the accumulation itself. int i; float acc = 0.0f; for (i = 0; i < k; i++) { acc += ((i < 30) ? 5 : 0); } a[0] = acc; } __kernel void acc_test3(__global float *a, int k) { // A more complex case where the accumulator is fed // by a dot product. int i; float acc = 0.0f; for (i = 0; i < k; i++ ){ float4 v = read_channel_intel(RANDOM_STREAM); float x1 = v.x; float x2 = v.y; float y1 = v.z; float y2 = v.w; acc += (x1*y1+x2*y2); } a[0] = acc; } __kernel void loader(__global float *a, int k) { int i; float4 my_val = 0; for(i = 0; i < k; i++) { if ((i%4) == 0) write_channel_intel(RANDOM_STREAM, my_val); if ((i%4) == 0) my_val.x = a[i]; if ((i%4) == 1) my_val.y = a[i]; if ((i%4) == 2) my_val.z = a[i]; if ((i%4) == 3) my_val.w = a[i]; } }
アキュムレーターを推論するためのプログラミング手法
複数のループを使用するアキュムレーターの記述
複数のループを使用し、一部のループを展開した状態でアキュムレーターを記述する場合を以下に示します。
float acc = 0.0f; for (i = 0; i < k; i++) { #pragma unroll for(j=0;j < 16; j++) acc += (x[i+j]*y[i+j]); }
この場合、-fp-relaxedの インテル® FPGA SDK for OpenCL™オフライン・コンパイラー・コマンド・オプションでカーネルをコンパイルし、累積が公開されるようにオフライン・コンパイラーが操作を再配置できるようにすることが重要です。-fp-relaxedでカーネルをコンパイルしない場合、アキュムレーターの構成は高いイニシエーション・インターバル (II) をともなうことになります。イニシエーション・インターバルは、連続するループ反復の開始から開始までのサイクル数です。イニシエーション・インターバルの値が大きいほど、アキュムレーター構造体が次のループ反復を処理するまでの時間が長くなります。
複数ループのアキュムレーターの記述変更
-fp-relaxedオフライン・コンパイラーのコマンドオプションでアキュムレーターの記述をコンパイルできない場合は、コードを書き換え累積を公開します。
上記コード例の場合は、次のように書き換えます。
float acc = 0.0f; for (i = 0; i < k; i++) { float my_dot = 0.0f; #pragma unroll for(j=0;j < 16; j++) my_dot += (x[i+j]*y[i+j]); acc += my_dot; }
変数またはゼロ以外の初期値を含むアキュムレーターの記述変更
ゼロ以外の値で始まるアキュムレーターの記述にオフセットを適用する場合を下に示します。
float acc = array[0]; for (i = 0; i < k; i++) { acc += x[i]; }
アキュムレーター・ハードウェアは、記述に変数またはゼロ以外の初期値をサポートしないため、記述を書き換える必要があります。
float acc = 0.0f; for (i = 0; i < k; i++) { acc += x[i]; } acc += array[0];
上記のように記述を書き換えると、カーネルはループでアキュムレーターを使用できるようになります。その後のループ構造には、array[0]のインクリメントが続きます。
整数のプロモーション規則
- 両方のオペランドが標準の整数型 (charやshortなど) の場合、整数はC/C++標準に従ってプロモートされます。つまり演算は、32ビット以上の最大オペランドのデータ型とサイズで実行されます。この式は、より大きい方のデータ型で結果を返します。
- オペランドがどちらもintX_tデータ型の場合、データ型が32ビットより小さい場合でも、最大のintX_tデータ型で演算は実行されます。この式は、そのデータ型で結果を返します。
- 式に標準データ型1つとintX_tデータ型1つが含まれる場合、intX_tデータ型のプロモーション規則が適用されます。この式の型は常にintX_tデータ型になります。例えば最大のデータ型が標準整数型のshortの場合、結果で返されるデータ型はint16_tです。
- リテラルはC/C++のデフォルトにおいてintデータ型のため、キャストなしでリテラルを使用すると、式の型は常に少なくとも32ビットになります。例えば次のようなコードの場合、比較は32ビットで行われます。
int5_t ap; ... if (ap < 4) { ...
- オペランドの符号が異なり、符号なしの型が少なくとももう一方の型と同じサイズである場合、演算は符号なしで実行されます。それ以外の場合は、符号なしオペランドは符号付きの値に変換されます。
例えば次のようなコードの場合、-1は32ビットの負の値 (0xffffffff) に拡張され、uint3_tは32ビットの正の値7 (0x00000007) となり、等しくなりません。
uint3_t x = 7; if (x != -1) { // FAIL }
ホスト・アプリケーションの設計
- ホストのプログラミング要件
インテル® FPGA SDK for OpenCL™ で使用するためにOpenCLホスト・アプリケーションをデザインする際は、アプリケーションが次のホスト・プログラミング要件を満たしていることを確認ください。 - グローバルメモリーの手動分割におけるOpenCLバッファーの割り当て
グローバル・メモリー・バッファーを手動で分割すると、バッファー間のメモリーアクセスを制御し、メモリー帯域幅を最大化することができます。バッファーの分割は、同じメモリータイプのインターフェイス間、または異なるメモリータイプのインターフェイス間で行うことが可能です。 - カーネル実行中におけるプロファイル・データの収集
カーネルの実行がホスト・アプリケーションの完了後に終了する場合、カーネルの実行中にプロファイル・データを収集するよう、FPGAに対し明示的に要求することができます。 - カスタム・プラットフォーム固有の関数へのアクセス
FCDとリンク中に、カスタム・プラットフォーム固有のユーザーがアクセス可能な関数を参照するには、ホスト・アプリケーションにclGetBoardExtensionFunctionAddressIntelFPGA拡張を含めます。 - 構造パラメーター変換に向けたホストプログラムの変更
OpenCL™カーネルにおいて、構造パラメーターを定数へのポインター構造に変換する場合、ホスト・アプリケーションを適宜変更する必要があります。 - ホスト・アプリケーションの管理
インテル® FPGA SDK for OpenCL™ には、ホスト・アプリケーションのコンパイルとリンクに必要なフラグおよびライブラリーに関する情報を取得するために呼び出すことができる、ユーティリティー・コマンドが含まれています。 - SoCをターゲットとするOpenCL カーネルへの共有メモリーの割り当て
インテルでは、 インテル® SoCで動作するOpenCL™カーネルは、FPGA DDRメモリーではなく共有メモリーにアクセスすることを推奨しています。 - 徐々に速度低下するOpenCLシステムのデバッグ
ホスト・アプリケーションのループがイベントを解放しないまま作成を続けると、OpenCLシステムが実行中に徐々に遅くなることがあります。
ホストのプログラミング要件
ホストマシンのメモリー要件
ホストマシンは、次のコンポーネントをサポートする必要があります。
- ホスト・アプリケーションおよびオペレーティング・システム
- ホスト・アプリケーションのワーキングセット
- 一度に割り当てることのできるOpenCL™メモリーバッファーの最大量。デバイス側のcl_memバッファーはすべて、ホストプロセスの対応するストレージエリアに関連付けられます。したがって、ホストメモリーに必要な容量は、FPGAがサポートしている外部メモリーの量と同等になる可能性があります。
ホストバイナリーの要件
複数のホストスレッド
clSetKernelArg関数を除くOpenCL APIはすべてスレッドセーフです。
clSetKernelArgの同時呼び出しが異なるcl_kernelオブジェクトで実行される限り、任意のホストスレッドからclSetKernelArgを呼び出しても、再入可能な方法で呼び出しても安全です。
順不同のコマンドキュー
カーネルを同時に実行するための複数のコマンドキューにおける要件
グローバルメモリーの手動分割におけるOpenCLバッファーの割り当て
グローバル・メモリー・バッファーを手動で分割すると、バッファー間のメモリーアクセスを制御し、メモリー帯域幅を最大化することができます。バッファーの分割は、同じメモリータイプのインターフェイス間、または異なるメモリータイプのインターフェイス間で行うことが可能です。
同じメモリータイプの複数のインターフェイスでのバッファーの分割
次の図は、バーストインターリーブでのメモリー分割と、インターリーブ以外のメモリー分割における違いを表しています。
使用可能なグローバル・メモリー・タイプの一部またはすべてを手動で分割するには、次の作業を実行します。
- OpenCLカーネルを -no-interleaving=<global_memory_type> フラグでコンパイルし、指定されたメモリータイプのメモリーバンクを個別のアドレスでコンフィグレーションします。 -no-interleaving=<global_memory_type> フラグの使用方法に関しては、グローバルメモリーのバーストインターリーブの無効化 (-no-interleaving=<global_memory_type>) の章を参照ください。
-
ホスト・アプリケーションにOpenCLバッファーを作成し、CL_CHANNELフラグでバッファーをいずれかのバンクへ割り当てます。
- CL_CHANNEL_1_INTELFPGAを指定し、バッファーを使用可能な最下位メモリー領域に割り当てます。
- CL_CHANNEL_2_INTELFPGAを指定し、メモリーを2番目のバンクに割り当てます (利用可能な場合)。
重要: それぞれのバッファーは、単一メモリーバンクにのみ割り当ててください。ランタイムに2番目のバンクが利用できない場合、メモリーは1番目のバンクに割り当てられます。グローバルメモリーが利用できない場合は、clCreateBuffer呼び出しは失敗し、エラーメッセージCL_MEM_OBJECT_ALLOCATION_FAILUREが表示されます。
異なるメモリータイプ (異種メモリー) 間でのバッファーの分割
異種メモリーを使用するには、.clファイルのコードを次のように変更します。
-
次のいずれかの方法で、FPGAボードで使用可能なグローバル・メモリー・タイプの名前を確認してください。
- ボードメーカーの資料を参照する。
- ボードのカスタム・プラットフォームのboard_spec.xmlファイルで名前を検索する。各グローバル・メモリー・タイプの名前は、global_mem要素のname属性に割り当てられた一意の文字列です。
-
特定のグローバル・メモリー・タイプにバッファーを割り当てるようにホストに指示するには、buffer_location("<memory_type>") 属性を挿入します。ここで <memory_type> は、ボードメーカーより提供されているグローバル・メモリー・タイプの名前です。
例
__kernel void foo(__global __attribute__((buffer_location("DDR"))) int *x, __global __attribute__((buffer_location("QDR"))) int *y)
buffer_location属性を指定しない場合、ホストはバッファーをデフォルトのメモリータイプに自動的に割り当てます。デフォルトのメモリータイプを確認するには、ボードメーカーから提供されている資料を参照してください。または、カスタム・プラットフォームのboard_spec.xmlファイルで、最初に定義されているメモリータイプもしくは、属性default=1が割り当てられたメモリータイプを検索します。インテルでは、次のようにbuffer_location属性をプリプロセッサー・マクロに定義し、再利用を容易にすることを推奨しています。
#define QDR\ __global __attribute__((buffer_location("QDR"))) #define DDR\ __global __attribute__((buffer_location("DDR"))) __kernel void foo (QDR uint * data, DDR uint * lup) { //statements }
重要: カーネル引数をデフォルト以外のメモリーに割り当てる場合 (上記コードであればQDR uint * dataおよびDDR uint * lup)、その引数をconstantキーワードで宣言することはできません。さらに、その引数から派生するポインターでアトミック操作を実行することはできません。
clCreateProgramWithBinary関数を使用しOpenCLランタイムにカーネルをロードすると、デフォルトで、ホストはバッファーをメインメモリーに割り当てます。カーネルの呼び出し中にホストは、カーネル引数にバインドされている異種メモリーバッファーを、メインメモリーに自動的に再配置します。
-
異種メモリーバッファーが最初にメインメモリーに割り当てられるのを防ぐには、clCreateBuffer関数を呼び出す際に、CL_MEM_HETEROGENEOUS_INTELFPGAフラグを含めます。また、 clSetKernelArgを使用しcl_memバッファーをbuffer_location属性を使用した引数にまずバインドした後に、そのバッファーに対する読み取りまたは書き込みを実行してください。次に例を示します。
mem = clCreateBuffer(context, flags|CL_MEM_HETEROGENEOUS_INTELFPGA, memSize, NULL, &errNum); clSetKernelArg(kernel, 0, sizeof(cl_mem), &mem); clEnqueueWriteBuffer(queue, mem, CL_FALSE, 0, N, 0, NULL, &write_event); clEnqueueNDRangeKernel(queue, kernel, 1, NULL, global_work_size, NULL, 0, NULL, &kernel_event);
例えば次のclCreateBuffer呼び出しは、デフォルト以外のメモリーバンクの、使用可能な最下位メモリー領域にメモリーを割り当てます。
mem = clCreateBuffer(context, (CL_MEM_HETEROGENEOUS_INTELFPGA|CL_CHANNEL_1_INTELFPGA), memSize, NULL, &errNum);
clCreateBuffer呼び出しは、カーネル引数で指定した内容に基づき、メモリーを特定のグローバル・メモリー・タイプに割り当てます。メモリータイプにあるメモリー・オブジェクト (cl_mem) が、異なるメモリー・テクノロジーに対応するカーネル引数として設定されている場合、ホストはカーネルをキューする際にそのメモリー・オブジェクトを自動的に移動します。バッファーは複数のメモリー・テクノロジーに関連付けるカーネル引数として渡さないでください。
異種グローバル・メモリー・アクセスの最適化に関する詳細は、 インテル® FPGA SDK for OpenCL™ : ベスト・プラクティス・ガイドの異種メモリーバッファーとグローバルメモリーの手動分割の章を参照ください。
ホスト・アプリケーションでのパイプ・オブジェクトの作成
SDK固有のパイプ・オブジェクトは、OpenCL Specification version 2.0で説明されているように、真のOpenCLパイプ・オブジェクトではありません。この実装は、準拠したソリューションによるインテルFPGA製品からの移行を可能にします。SDK固有のパイプ・オブジェクトはメモリー・オブジェクト (cl_mem) ですが、ホストはパイプ自体にメモリーを割り当てません。
次のclCreatePipeホストAPIは、パイプ・オブジェクトを作成します。
cl_mem clCreatePipe(cl_context context, cl_mem_flags flags, cl_uint pipe_packet_size, cl_uint pipe_max_packets, const cl_pipe_properties *properties, cl_int *errcode_ret)
clCreatePipeホストAPI関数についての詳細は、OpenCL Specification version 2.0のSection 5.4.1を参照ください。
以下は、clCreatePipeホストAPI関数の構文例です。
cl_int status; cl_mem c0_pipe = clCreatePipe(context, 0, sizeof(int), 1, NULL, &status); status = clSetKernelArg(kernel, 1, sizeof(cl_mem), &c0_pipe);
カーネル実行中におけるプロファイル・データの収集
extern CL_API_ENTRY cl_int CL_API_CALL clGetProfileInfoIntelFPGA(cl_event);
上記cl_eventは、カーネルのイベントです。このホスト・ライブラリー呼び出しに渡すカーネルイベントは、clEnqueueNDRangeKernel呼び出しに渡すものと同じである必要があります。
- clGetProfileInfoIntelFPGAの呼び出し前にカーネルの実行が完了した場合、この関数はイベント・エラー・メッセージを返します。
- clGetProfileInfoIntelFPGAおよびclGetProfileDataDeviceIntelFPGA関数呼び出しを使用しているホストプログラムには、CL/cl_ext_intelfpga.hヘッダーファイルを含む必要があります。
int main() { ... clEnqueueNDRangeKernel(queue, kernel, ..., NULL); ... clEnqueueNDRangeKernel(queue, kernel, .. , NULL); ... }
このホスト・アプリケーションは、カーネルが2回起動した後に完了するという前提で動作します。カーネルの呼び出しごとに1組のプロファイル・データが生成されるため、profile.monファイルには合計2組のプロファイル・データが生成されます。カーネルの実行中にプロファイル・データを収集するには、ホストコードを次のように変更します。
int main() { ... clEnqueueNDRangeKernel(queue, kernel, ..., &event); //Get the profile data before the kernel completes clGetProfileInfoIntelFPGA(event); //Wait until the kernel completes clFinish(queue); ... clEnqueueNDRangeKernel(queue, kernel, ..., NULL); ... }
clGetProfileInfoIntelFPGAへの呼び出しにより、profile.monファイルに新しいエントリーが追加されます。 インテル® FPGA Dynamic Profiler for OpenCL™ GUIはその後、レポートにこのエントリーを解析します。
- インテル® FPGA SDK for OpenCL™ : ベスト・プラクティス・ガイドのパフォーマンスのボトルネックを特定するためのカーネルのプロファイリング
- OpenCLカーネルのプロファイリング
エンキューされたカーネルおよび自動実行カーネルのプロファイリング
次は、clGetProfileDataDeviceIntelFPGAホスト・ライブラリー呼び出しのコード例です。
cl_int clGetProfileDataDeviceIntelFPGA (cl_device_id device_id, cl_program program, cl_bool read_enqueue_kernels, cl_bool read_auto_enqueued, cl_bool clear_counters_after_readback, size_t param_value_size, void *param_value, size_t *param_value_size_ret, cl_int *errcode_ret);
以下が詳細です。
- read_enqueue_kernelsパラメーターは、エンキューされたカーネルをプロファイリングします。このリリースにおいてこのパラメーターは無効です。
- read_auto_enqueuedパラメーターは、自動実行カーネルをプロファイリングします。
- 以下は今後のリリースにおけるプレースホルダー・パラメーターです。
- clear_counters_after_readback
- param_value_size
- param_value
- param_value_size_ret
- errcode_ret
clGetProfileDataDeviceIntelFPGAホスト・ライブラリー呼び出しは、成功時にCL_SUCCESSを返します。それ以外の場合は、次のエラーのいずれかを返します。
- デバイスが有効なデバイスではない場合はCL_INVALID_DEVICE
- プログラムが有効なプログラムではない場合はCL_INVALID_PROGRAM
read_auto_enqueued | |
---|---|
エンキューされたカーネルのみをプロファイリング 注: 実行が完了すると、自動的にプロファイル情報を出力します。
|
False |
自動実行カーネルのみをプロファイリング | True |
エンキューされたカーネルと自動実行カーネルの両方をプロファイリング | True |
プロファイル・データの取得
データ取得の一時停止は、すべてのカーネル間において完全には同期されません。カーネル間におけるプロファイル・データの取得停止のスキューは、デバイスとの通信リンク、ドライバーのオーバーヘッドおよび通信バス上の輻輳によって異なります。カーネル間におけるプロファイル・データの正確な同期スナップショットに依存するべきではありません。
複数の自動実行プロファイリング呼び出し
カスタム・プラットフォーム固有の関数へのアクセス
clGetBoardExtensionFunctionAddressIntelFPGA拡張は、カスタム・プラットフォームからユーザーがアクセス可能な関数へのポインターを取得するAPIを指定します。
拡張インターフェイスの定義は、 INTELFPGAOCLSDKROOT/host/include/CL/cl_ext.hファイルで確認できます。
void* clGetBoardExtensionFunctionAddressIntelFPGA ( const char* function_name, cl_device_id device );
以下に詳細を説明します。
function_nameは、カスタム・プラットフォームのメーカーが提供するユーザーがアクセス可能な関数の名前を指します。
また、
deviceは、clGetDeviceIDs関数によって返されるデバイスIDです。
インストーラブル・クライアント・ドライバー (ICD) を介しclGetBoardExtensionFunctionAddressIntelFPGA APIにアクセスする際は、ICD拡張APIのclGetExtensionFunctionAddressIntelFPGAが、最初にclGetBoardExtensionFunctionAddressIntelFPGA APIへのポインターを取得していることを確認してください。
次のコード例は、ICDを介したカスタム・プラットフォーム固有の関数へのアクセス方法を示しています。
clGetBoardExtensionFunctionAddressIntelFPGA_fn clGetBoardExtensionFunctionAddressIntelFPGA = (clGetBoardExtensionFunctionAddressIntelFPGA_fn) clGetExtensionFunctionAddressForPlatform (platform, "clGetBoardExtensionFunctionAddressIntelFPGA"); if (clGetBoardExtensionFunctionAddressIntelFPGA == NULL){ printf ("Failed to get clGetBoardExtensionFunctionAddressIntelFPGA\n"); } void * board_extension_function_ptr = clGetBoardExtensionFunctionAddressIntelFPGA("function_name",device_id);
構造パラメーター変換に向けたホストプログラムの変更
ホスト・アプリケーションに次の変更を加えます。
-
構造体のコンテンツを格納するためのcl_memバッファーを割り当てます。
重要: 異なる構造体の値を使用するカーネルごとに個別のcl_memバッファーが必要です。
- 構造体のカーネル引数を、構造体のコンテンツへのポインターではなく、構造体のバッファーへのポインターで設定します。
-
カーネルをキューする前に構造体のバッファーのコンテンツを設定します。次のいずれかの手順を実行し、カーネルが起動する前に構造体のバッファーが設定されるようにします。
- カーネルキューと同じコマンドキューに構造体バッファーをキューします。
- 個別のカーネルキューと構造体のバッファーキューをイベントと同期させます。
- アプリケーションが構造体バッファーを使用するカーネルを呼び出す必要がなくなった際には、cl_memバッファーを解放します。
ホスト・アプリケーションの管理
Linuxシステムにおいて、GNU Project Debugger (GDB) を使用しホスト・アプリケーションをデバッグする場合は、ホスト・アプリケーションを実行する前に次のコマンドを呼び出します。
handle SIG44 nostop
このコマンドを呼び出していない場合、GDBのデバッグプロセスは次のエラーメッセージとともに終了します。
Program received signal SIG44, Real-time event 44.
Makefileフラグメント例の表示 (example-makefileまたはmakefile)
The following are example Makefile fragments for compiling and linking a host program against the host runtime libraries included with the Intel FPGA SDK for OpenCL. Example GNU makefile on Linux, with GCC toolchain: AOCL_COMPILE_CONFIG=$(shell aocl compile-config) AOCL_LINK_CONFIG=$(shell aocl link-config) host_prog : host_prog.o g++ -o host_prog host_prog.o $(AOCL_LINK_CONFIG) host_prog.o : host_prog.cpp g++ -c host_prog.cpp $(AOCL_COMPILE_CONFIG) Example GNU makefile on Windows, with Microsoft Visual C++ command line compiler: AOCL_COMPILE_CONFIG=$(shell aocl compile-config) AOCL_LINK_CONFIG=$(shell aocl link-config) host_prog.exe : host_prog.obj link -nologo /OUT:host_prog.exe host_prog.obj $(AOCL_LINK_CONFIG) host_prog.obj : host_prog.cpp cl /MD /Fohost_prog.obj -c host_prog.cpp $(AOCL_COMPILE_CONFIG) Example GNU makefile cross-compiling to ARM SoC from Linux or Windows, with Linaro GCC cross-compiler toolchain: CROSS-COMPILER=arm-linux-gnueabihf- AOCL_COMPILE_CONFIG=$(shell aocl compile-config --arm) AOCL_LINK_CONFIG=$(shell aocl link-config --arm) host_prog : host_prog.o $(CROSS-COMPILER)g++ -o host_prog host_prog.o $(AOCL_LINK_CONFIG) host_prog.o : host_prog.cpp $(CROSS-COMPILER)g++ -c host_prog.cpp $(AOCL_COMPILE_CONFIG)
ホスト・アプリケーションのコンパイルとリンク
- ホスト・アプリケーションとKhronos ICD Loader Libraryのリンク
インテル® FPGA SDK for OpenCL™ は、Khronos Group™のOpenCL ICD拡張をサポートしています。 - ホスト・アプリケーションをコンパイルするためのフラグの表示 (compile-config)
ホスト・アプリケーションのコンパイルに必要なフラグのリストを表示するには、compile-configユーティリティー・コマンドを呼び出します。 - OpenCLホスト・ランタイム・ライブラリーおよびMMDライブラリーへのパスの表示 (ldflags)
ホスト・アプリケーションを、OpenCLホスト・ランタイム・ライブラリーおよびMMDライブラリーにリンクするために必要なパスを表示するには、ldflagsユーティリティー・コマンドを呼び出します。 - OpenCLホスト・ランタイム・ライブラリーおよびMMDライブラリーのリスト表示 (ldlibs)
ホスト・アプリケーションをリンクするために必要なOpenCLホスト・ランタイム・ライブラリー名およびMMDライブラリー名を表示するには、ldlibsユーティリティー・コマンドを呼び出します。 - OpenCLホスト・ランタイム・ライブラリーおよびMMDライブラリーに関する情報の表示 (link-config または linkflags)
ホスト・アプリケーションを、OpenCLホスト・ランタイム・ライブラリーおよびMMDライブラリーにリンクするために必要なフラグの一覧を表示するには、link-configまたはlinkflagsユーティリティー・コマンドを呼び出します。
ホスト・アプリケーションとKhronos ICD Loader Libraryのリンク
SDKのホスト・ランタイム・ライブラリーに加えインテルは、OpenCL Specification version 1.0および、OpenCL Specification versions 1.1、1.2、2.0に実装されているAPIをサポートするICD Loader Libraryのバージョンを提供します。他のメーカーが提供するICDライブラリーを使用する場合、該当メーカーが発行している資料を参照し、ICDライブラリーのリンク方法を確認ください。
OpenCLホスト・アプリケーションをICD Loader Libraryにリンクする前に、ボードMMDライブラリーをロードするためのFCDを設定する必要があります。FCDをまだ設定していない場合、FPGAボードの管理 にて詳細を確認ください。
ICDとFCDがどちらも正しくセットアップされていることを確認してください。これは、 aocl diagnose –icd-only ユーティリティーを使用し確認することができます。このユーティリティーは対応するICDまたはFCDを作成し、ライブラリーがシステムにレジスターされているかどうかを確認します。
- aocl diagnoseユーティリティーの出力にICD diagnostics PASSEDが表示されると、ホスト・アプリケーションを作成する際に、ホスト・アプリケーションは自動的にICD Loader Libraryに接続します。
-
aocl diagnoseユーティリティーがICDを検出しなかった場合、次の手順でICDの設定を確認してください。
- Windowsシステムにおいては、regeditを管理者権限で開き、Windowsレジストリー・キーのHKEY_LOCAL_MACHINE\SOFTWARE\Khronos\OpenCL\Vendorsに移動します。Nameの値はaltera_icd.dllになります。この動的ライブラリー・ファイルは、
<INTELFPGAOCLSDKROOT>/host/windows64/binにあります。TypeはDWORDであり、Dataは00000000になります。レジストリー・キーは次の例ようになります。
HKEY_LOCAL_MACHINE\SOFTWARE\Khronos\OpenCL\Vendors] "alteracl_icd.dll"=dword:00000000
- Linuxシステムにおいては、/etc/OpenCL/vendors/Altera.icdファイルがシステム内に存在し、libalteracl.soのテキストが含まれていることを確認してください。
- Windowsシステムにおいては、regeditを管理者権限で開き、Windowsレジストリー・キーのHKEY_LOCAL_MACHINE\SOFTWARE\Khronos\OpenCL\Vendorsに移動します。Nameの値はaltera_icd.dllになります。この動的ライブラリー・ファイルは、
<INTELFPGAOCLSDKROOT>/host/windows64/binにあります。TypeはDWORDであり、Dataは00000000になります。レジストリー・キーは次の例ようになります。
-
aocl diagnoseユーティリティーがFCDを検出しなかった場合、次の手順でFCDの設定を確認してください。
- Windowsシステムにおいては、HKEY_LOCAL_MACHINE\SOFTWARE\Intel\OpenCL\Boardsレジストリー・キーのライブラリーで、すべてのユーザーのインストールが選択されていることを確認してください。もしくは、 HKEY_CURRENT_USER\SOFTWARE\Intel\OpenCL\Boardsレジストリー・キーを確認してください。Nameの値は <path_to_the_mmd_library> になり、Dataは0に設定されたDWORDです。レジストリー・キーは、次の例のようになります。
[HKEY_LOCAL_MACHINE\SOFTWARE\Intel\OpenCL\Boards] "c:\board_vendor a\my_board_mmd.dll"=dword:00000000
- Linuxシステムの場合は、/opt/Intel/OpenCL/Boards/my_board.fcdファイルがシステムに存在し、メーカー固有のライブラリー名が含まれていることを確認してください (/data/board_vendor_a/libmy_board_mmd.soなど)。重要:
- ボードメーカーが複数のライブラリーを提供している場合、それらが特定の順序になっているかを再度確認してください。ライブラリーをロードする正しい順序に関してはボードメーカーにお問い合わせください。ライブラリーは、ロードする順序通りにレジストリーに並べる必要があります。
- インテル® Arria® 10 SoCボードにおいて、カスタム・プラットフォームにSDフラッシュ・カード・イメージをビルドする際は、libalteracl.soのテキストを含むAltera.icdファイルを作成ください。Altera.icdファイルを、カスタム・プラットフォームの/etc/OpenCL/vendorsディレクトリーに保存します。詳細は、Building the Software and SD Card Image for the Intel Arria 10 SoC Development Kit Reference Platformを参照ください。
- Windowsシステムにおいては、HKEY_LOCAL_MACHINE\SOFTWARE\Intel\OpenCL\Boardsレジストリー・キーのライブラリーで、すべてのユーザーのインストールが選択されていることを確認してください。もしくは、 HKEY_CURRENT_USER\SOFTWARE\Intel\OpenCL\Boardsレジストリー・キーを確認してください。Nameの値は <path_to_the_mmd_library> になり、Dataは0に設定されたDWORDです。レジストリー・キーは、次の例のようになります。
ホスト・アプリケーションをコンパイルするためのフラグの表示 (compile-config)
-
コマンドプロンプトで
aocl
compile-config
ユーティリティー・コマンドを呼び出します。
ソフトウェアは、OpenCL™ APIヘッダーファイルが存在するフォルダーまたはディレクトリーへのパスを表示します。例えば、
- Windowsシステムの場合、パスは-I%INTELFPGAOCLSDKROOT%/host/includeになります。
- Linuxシステムの場合、パスは-I$INTELFPGAOCLSDKROOT/host/includeになります。
このINTELFPGAOCLSDKROOTは、ソフトウェアのインストール位置を指します。
- このパスを、Cプリプロセッサーに追加します。
OpenCLホスト・ランタイム・ライブラリーおよびMMDライブラリーへのパスの表示 (ldflags)
OpenCLホスト・ランタイム・ライブラリーおよびMMDライブラリーのリスト表示 (ldlibs)
ソフトウェアは、 INTELFPGAOCLSDKROOT/host/<OS_platform>/libディレクトリーにあるホスト・ランタイム・ライブラリーのリストを表示します。また、カスタム・プラットフォームの/<board_family_name>/<OS_platform>/libディレクトリー にあるカスタム・プラットフォーム固有のMMDライブラリーのリストも表示します。
FCDが正しく設定されていると、ソフトウェアはMMDライブラリーのリストを表示しません。
- Windowsシステムの場合、出力はOpenCL.libになります。
- Linuxシステムの場合、出力は-lOpenCLになります。
OpenCLホスト・ランタイム・ライブラリーおよびMMDライブラリーに関する情報の表示 (link-config または linkflags)
- Windowsシステムの場合、出力は/libpath:%INTELFPGAOCLSDKROOT%/host/windows64/lib OpenCL.libになります。
- Linuxシステムの場合、出力は-L/$INTELFPGAOCLSDKROOT/host/[linux64|arm32]/lib -lOpenCLになります。
OpenCL ICD拡張APIの使用
次のコード例を参照ください。
extern CL_API_ENTRY cl_int CL_API_CALL clGetProfileDataDeviceIntelFPGA( cl_device_id /*device_id*/, cl_program /*program*/, cl_bool /*read_enqueue_kernels*/, cl_bool /*read_auto_enqueued*/, cl_bool /*clear_counters_after_readback*/, size_t /*param_value_size*/, void * /*param_value*/, size_t * /*param_value_size_ret*/, cl_int * /*errcode_ret*/ );
以下の関数呼び出しを
cl_int status = clGetProfileDataDeviceIntelFPGA (device, program, false, true, false, 0, NULL, NULL, NULL);
以下の構文を使用するコードと置き換え、関数ポインターの定義およびロードを行います。
typedef cl_int (*clGetProfileDataDevice_fn) (cl_device_id, cl_program, cl_bool, cl_bool, cl_bool, size_t, void *, size_t *, cl_int *); clGetProfileDataDevice_fn get_profile_data_ptr = (clGetProfileDataDevice_fn) clGetExtensionFunctionAddressForPlatform ("clGetProfileDataDeviceIntelFPGA");
その後、関数呼び出しとして関数ポインターを使用します。
cl_int status = (get_profile_data_ptr) (device, program, false, true, false, 0, NULL, NULL, NULL);
ホストを経由したFPGAのプログラミング
- オフライン・コンパイラーでOpenCLカーネルをコンパイルし.aocxファイルを作成します。
- ホスト・アプリケーションにclCreateProgramWithBinary関数を含め、.aocxファイルからcl_program OpenCLプログラム・オブジェクトを作成します。
-
ホスト・アプリケーションにclBuildProgram関数を含め、指定したデバイスで実行可能なプログラムファイルを作成します。
以下は、clCreateProgramWithBinaryを使用しFPGAデバイスをプログラムするホストコード例です。
size_t lengths[1]; unsigned char* binaries[1] ={NULL}; cl_int status[1]; cl_int error; cl_program program; const char options[] = ""; FILE *fp = fopen("program.aocx","rb"); fseek(fp,0,SEEK_END); lengths[0] = ftell(fp); binaries[0] = (unsigned char*)malloc(sizeof(unsigned char)*lengths[0]); rewind(fp); fread(binaries[0],lengths[0],1,fp); fclose(fp); program = clCreateProgramWithBinary(context, 1, device_list, lengths, (const unsigned char **)binaries, status, &error); clBuildProgram(program,1,device_list,options,NULL,NULL);
clBuildProgram関数が正常に実行されると、CL_SUCCESSが返されます。 - clCreateKernelsInProgramまたはclCreateKernel関数を使用し、プログラム実行可能ファイルからカーネル・オブジェクトを作成します。
-
カーネルを実行するための関数を含め、スケジュールされたカーネルをFPGAで実行するようホストランタイムに指示します。
- NDRangeカーネルを実行するためのコマンドをエンキューするには、clEnqueueNDRangeKernelを使用します。
- 単一ワークアイテムのカーネルをエンキューするには、clEnqueueTaskを使用します。
重要:インテルでは、使用されていないイベント・オブジェクトを解放することを推奨しています。SDKは、明示的な指示があるまでイベント・オブジェクトを解放せずに保持し続けます。使用していないイベント・オブジェクトを保持し続けると、不必要にメモリーが消費されます。
イベント・オブジェクトの解放には、clReleaseEvent関数を呼び出します。
複数のFPGAのプログラムをメモリーにロードすることができます。ホストはそれらを使用し、必要に応じてFPGAを再プログラムします。
複数のFPGAデバイスのプログラミング
ホスト・アプリケーションをFCDにリンクすると、異なるカスタム・プラットフォームから複数のFPGAデバイスをターゲットにすることができます。ただし、16.1よりも前のバージョンのSDKと互換性のあるカスタム・プラットフォームにおいては、この機能に対するサポートが制限されています。
次のように、最大128のFPGAデバイスをシステムに提示できます。
- それぞれが単一のFPGAで構成されている複数のFPGAアクセラレーター・ボード
- PCIe®スイッチを介しホストシステムに接続する、単一アクセラレーター・ボード上の複数のFPGA
- 上記の組み合わせ
ホストランタイムは、それぞれのFPGAデバイスすべてにカーネルをロードすることができます。また、FPGAデバイスは並列に動作できます。
OpenCL FPGAデバイスの診断
- ご自身のマシンにインストールされたFPGA デバイスのリストを照会するには、 aocl diagnose コマンドを呼び出します。
-
ホスト・アプリケーションに次のコード行を追加し、ホストにOpen CL FPGAのデバイス数を特定させます。
//Get the platform ciErrNum = clGetPlatformID(&cpPlatform); //Get the devices ciErrNum = clGetDeviceIDs(cpPlatform, CL_DEVICE_TYPE_ALL, 0, NULL, &ciDeviceCount); cdDevices = (cl_device_id * )malloc(ciDeviceCount * sizeof(cl_device_id)); ciErrNum = clGetDeviceIDs(cpPlatform, CL_DEVICE_TYPE_ALL, ciDeviceCount, cdDevices, NULL);
デバイス情報の照会
char buf[1024]; for (unsigned i = 0; i < ciDeviceCount; i++); { clGetDeviceInfo(cdDevices[i], CL_DEVICE_NAME, 1023, buf, 0); printf("Device %d: '%s'\n", i, buf); }
Device <N>: <board_name>: <name_of_FPGA_board>
以下が詳細になります。
- <N> はデバイスの番号です。
- <board_name> は、aocコマンドを呼び出す際にFPGAデバイスをターゲットにするボードの指定です。
- <name_of_FPGA_board> は、FPGAボードに公示されている名前です。
例えばシステムに同一のFPGAボードが2つある場合、ホストは次のような出力を生成します。
Device 0: board_1: Stratix V FPGA Board Device 1: board_1: Stratix V FPGA Board
複数のFPGAデバイスへのカーネルのロード
次のホストコードは、複数のFPGAデバイスをプログラムするためのclCreateProgramWithBinaryと、createMultiDeviceProgram関数の使用方法を示しています。
cl_program createMultiDeviceProgram(cl_context context, const cl_device_id *device_list, cl_uint num_devices, const char *aocx_name); // Utility function for loading file into Binary String // unsigned char* load_file(const char* filename, size_t *size_ret) { FILE *fp = fopen(aocx_name,"rb"); fseek(fp,0,SEEK_END); size_t len = ftell(fp); char *result = (unsigned char*)malloc(sizeof(unsigned char)*len); rewind(fp); fread(result,len,1,fp); fclose(fp); *size_ret = len; return result; } //Create a Program that is compiled for the devices in the "device_list" // cl_program createMultiDeviceProgram(cl_context context, const cl_device_id *device_list, cl_uint num_devices, const char *aocx_name) { printf("creating multi device program %s for %d devices\n", aocx_name, num_devices); const unsigned char **binaries = (const unsigned char**)malloc(num_devices*sizeof(unsigned char*)); size_t *lengths=(size_t*)malloc(num_devices*sizeof(size_t)); cl_int err; for(cl_uint i=0; i<num_devices; i++) { binaries[i] = load_file(aocx_name,&lengths[i]); if (!binaries[i]) { printf("couldn't load %s\n", aocx_name); exit(-1); } } cl_program p = clCreateProgramWithBinary(context, num_devices, device_list, lengths, binaries, NULL, &err); free(lengths); free(binaries); if (err != CL_SUCCESS) { printf("Program Create Error\n"); } return p; } // main program main () { // Normal OpenCL setup } program = createMultiDeviceProgram(context, device_list, num_devices, "program.aocx"); clBuildProgram(program,num_devices,device_list,options,NULL,NULL);
ランタイム環境の終了とエラー回復
ランタイム環境は、ホスト・アプリケーションの一部としてコンパイルされるライブラリーです。ホスト・アプリケーションが終了すると、ランタイム環境もまた、実行する追跡アクティビティーとともに終了します。ホスト・アプリケーションを再起動すると、新しいランタイム環境および関連する追跡アクティビティーが再度初期化されます。初期化関数は、カーネルのハードウェア状態をリセットします。
同様に、ホスト・アプリケーションが予期せず終了した場合、特定のハードウェア (PCIe® ハードIPなど) のコンフィグレーションが不完全に終わります。これらのハードウェアのコンフィグレーションを復元するために、ホストはFPGAを再プログラムする必要があります。
カスタマイズされたハードウェア・ブロックを実装するカスタム・プラットフォームを使用している場合、ホスト・アプリケーションを再起動しそれらのブロックをリセットすることで、デザインに影響を与える可能性があることに注意してください。
- ホスト・アプリケーションがclGetPlatformIDs関数を呼び出すと、利用可能なすべてのデバイスに対してカーネルとチャネルはすべてリセットされます。
- ホスト・アプリケーションがclGetPlatformIDs関数を呼び出すと、デバイスのリセット時にFIFOバッファーとチャネルがリセットされます。
- ホスト・アプリケーションは、clCreateBufferおよびclEnqueueWriteBuffer関数呼び出しを介しメモリーバッファーを初期化します。新しいホスト実行において、前回のホスト実行時のバッファーの内容へはアクセスすることができません。
SoCをターゲットとするOpenCL カーネルへの共有メモリーの割り当て
- カーネル間の共有バッファーは揮発性としてマークし、あるカーネルによるバッファーの変更が他のカーネルも認識できるようにしてください。
- 共有メモリーにアクセスするにはホストコードの変更のみが必要です。カーネルコードの変更は必要ありません。
- ライブラリー関数mallocまたはnew演算子で、物理的に共有されたメモリーを割り当てることはできません。また、CL_MEM_USE_HOST_PTRフラグは共有メモリーでは機能しません。
DDRメモリーでは、共有メモリーは物理的に連続している必要があります。FPGAは、SG-DMAコントローラー・コアなしでは実質的に連続したメモリーを消費することができません。malloc関数およびnew演算子は、実質的に連続したメモリーへアクセスするためのものです。
- 共有メモリーのCPUキャッシュは無効になっています。
- 共有メモリーを使用する際は、データのコピー1つがホストとカーネルの両方で使用されます。このメモリーを使用すると、OpenCLメモリーの呼び出しは、バッファー読み取り、バッファー書き込み、マッピングおよびアンマッピングに対し、ゼロコピー転送で実行されます。
-
共有メモリーの割り当てとアクセスには、次の例のようなホストコードを構築します。
cl_mem src = clCreateBuffer(…, CL_MEM_ALLOC_HOST_PTR, size, …); int *src_ptr = (int*)clEnqueueMapBuffer (…, src, size, …); *src_ptr = input_value; //host writes to ptr directly clSetKernelArg (…, src); clEnqueueNDRangeKernel(…); clFinish(); printf (“Result = %d\n”, *dst_ptr); //result is available immediately clEnqueueUnmapMemObject(…, src, src_ptr, …); clReleaseMemObject(src); // actually frees physical memory
CONFIG_CMA_SIZE_MBYTESカーネル・コンフィグレーション・オプションを含め、割り当てに有効な共有メモリーの最大総量を制御することができます。実際には、割り当てられた共有メモリーの総量は、CONFIG_CMA_SIZE_MBYTESの値よりも小さくなります。重要:- ターゲットボードに複数のDDRメモリーバンクがある場合、clCreateBuffer(..., CL_MEM_READ_WRITE, ...)関数は、メモリーを非共有DDRメモリーバンクに割り当てます。ただし、FPGAが共有メモリーである単一DDRバンクへアクセスできる場合、clCreateBuffer(..., CL_MEM_READ_WRITE, ...) は、CL_MEM_ALLOC_HOST_PTRフラグを使用するのと同様に、メモリーを共有メモリーに割り当てます。
- clCreateBuffer(..., CL_MEM_ALLOC_HOST_PTR, size, ...)関数で要求する共有メモリーは、Linux OpenCLカーネルドライバーに割り当てられ、Linuxカーネルの連続したメモリー割り当て (CMA) 機能に依存します。CMAを有効にしコンフィグレーションする方法に関しては、Intel FPGA SDK for OpenCL Intel Arria 10 SoC Development Kit Reference Platform Porting GuideのRecompiling the Linux Kernel for the Intel Arria 10 SoC Development Kit およびCompiling and Installing the OpenCL Linux Kernel Driverの章を参照ください。
-
共有ハード・プロセッサー・システム (HPS) DDRからFPGA DDRへの効率的なデータ転送に向け、memcpy関数を実行するカーネルを次のように含めます。
__attribute__((num_simd_work_items(8))) mem_stream(__global uint * src, __global uint * dst) { size_t gid = get_global_id(0); dst[gid] = src[gid]; }
重要: CL_MEM_ALLOC_HOST_PTRフラグを使用し、srcポインターをHPS DDRに共有メモリーとして割り当てます。 -
ホストがコンスタント・メモリーを共有HPS DDRシステムに割り当て、カーネル実行後にそれを変更すると、変更が有効にならない可能性があります。その結果、続くカーネル実行に古いデータが使用される可能性があります。カーネルの実行に古いコンスタント・メモリーが使用されないようにするため、次のいずれかを実行してください。
- コンスタント・メモリーの初期化後は、それを変更しない
- 複数の__constantデータセットが必要な場合、複数のコンスタント・メモリー・バッファーを作成する
- 可能であれば、アクセラレーター・ボードのFPGA DDRにコンスタント・メモリーを割り当てる
徐々に速度低下するOpenCLシステムのデバッグ
void oclContextCallback (const char *errinfo, const void *, size_t, void *) { printf ("Context callback: %s\n", errinfo); } int main(){ … // Create the context. context = clCreateContext (NULL, num_devices, device, &oclContextCallback, NULL, &status); … }
[Runtime Warning]: Too many 'event' objects in the host. This causes deterioration in runtime performance.
OpenCL カーネルのコンパイル
OpenCL™カーネルをコンパイルする前に、 QUARTUS_ROOTDIR_OVERRIDE環境変数が、 インテル® Quartus® Prime開発ソフトウェア・プロ・エディション・ソフトウェアを指定していることを確認してください。
ハードウェアのコンフィグレーション・ファイルを作成するためのカーネルのコンパイル
インテルでは、次のような状況においてこの1ステップ・コンパイル手法を使用することを推奨しています。
- インテル® FPGA SDK for OpenCL™ デザインフローでカーネルを最適化し、FPGAに展開する.aocxファイルの作成準備が整っている場合
- 最適化を必要としない単純なカーネルが1つ以上ある場合
カーネルのコンパイルおよび.aocxファイルの生成を1ステップで行うには、 aoc <your_kernel_filename1>.cl [<your_kernel_filename2>.cl ...]コマンドを呼び出します。
この [ <your_kernel_filename2>.cl ...] は、カーネルのファイル名をスペースで区切ったもので、オプションで <your_kernel_filename1>.clに加えコンパイル可能です。
インテル® FPGA SDK for OpenCL™オフライン・コンパイラーは、.clファイルを一時ファイルにグループ化します。その後このファイルを使用し.aocxファイルを生成します。
ハードウェアを構築せずに行うカーネルのコンパイル (-c)
- 各.clカーネル・ソースファイルの.aocoファイル。オフライン・コンパイラーは、.aocoファイルを数秒から数分で作成します。
ハードウェアを構築せずに行うカーネルまたはオブジェクト・ファイルのコンパイルおよびリンク (-rtl)
-
1つもしくは複数のカーネル・ソースファイルをコンパイルするには、コマンドプロンプトで
aoc
-rtl
<your_kernel_filename1>.cl [<your_kernel_filename2>.cl ...]コマンドを呼び出します。
この [ <your_kernel_filename2>.cl ...] は、カーネルのファイル名をスペースで区切ったもので、 <your_kernel_filename1>.clに加えオプションでコンパイル可能です。aocコマンドを-rtlフラグとともに呼び出すと、オフライン・コンパイラーはカーネルをコンパイルし、次のファイルとディレクトリーを作成します。
- 各.clカーネル・ソースファイルの.aocoファイル。その後オフライン・コンパイラーはそれらをリンクし.aocrファイルを生成します。.aocoファイルまたは.aocrファイルの生成は、数秒から数分で行われます。
- <your_kernel_filename> フォルダーまたはサブディレクトリー。これには、FPGAのプログラミングに必要なハードウェア・コンフィグレーション・ファイルを構築するためにSDKが使用する中間ファイルが含まれます。
-
1つ以上の.aocoオブジェクト・ファイルをコンパイルするには、
aoc
-rtl
<your_kernel_filename>.aoco [<your_kernel_filename2>.aoco ...]コマンドをコマンドプロンプトで呼び出します。
この [ <your_kernel_filename2>.aoco ...] は、オブジェクト・ファイルのファイル名をスペースで区切ったもので、 <your_kernel_filename1>.aocoに加えオプションでコンパイル可能です。aocコマンドを-rtlフラグとともに呼び出すと、オフライン・コンパイラーは次のファイルとディレクトリーを作成します。
- オフライン・コンパイラーはすべての.aocoファイルをリンクし、.aocrファイルを生成します。
- <your_kernel_filename> フォルダーまたはサブディレクトリー。これには、FPGAのプログラミングに必要なハードウェア・コンフィグレーション・ファイルを構築するためにSDKが使用する中間ファイルが含まれます。
ヘッダーファイル位置の指定 (-I=<directory>)
ヘッダーファイルがカーネルと同じディレクトリーにある場合は、 -I=<directory> オプションをaocコマンドに含める必要はありません。オフライン・コンパイラーは現在のフォルダーまたはディレクトリーでヘッダーファイルを自動的に検索します。
Windowsシステムの場合、インクルード・パスの末尾にスラッシュが含まれていないことを確認してください。オフライン・コンパイラーは、末尾のスラッシュ (/) またはバックスラッシュ (\) を不正と認識します。
次のような方法でaocコマンドを呼び出すと、オフライン・コンパイラーはエラーメッセージを生成します。
aoc -I=<drive>\<folder>\<subfolder>\ <your_kernel_filename>.cl
または
aoc -I=<drive>/<folder>/<subfolder>/ <your_kernel_filename>.cl
インクルード・パスの正しい指定方法は以下のようになります。
aoc -I=<drive>\<folder>\<subfolder> <your_kernel_filename>.cl
または
aoc -I=<drive>/<folder>/<subfolder> <your_kernel_filename>.cl
インテル FPGA SDK for OpenCLオフライン・コンパイラーの出力ファイル名の指定 (-o <filename>)
-
複数ステップでのコンパイルフローを実行する場合は、出力ファイル名を次のように指定します。
- 中間コンパイルの段階でオフライン・コンパイラーが作成する.aocoファイルの名前を指定するには、 aoc -rtl -o <your_object_filename>.aocr <your kernel_filename>.clコマンドを呼び出します。
- 最終コンパイルの段階でオフライン・コンパイラーが作成する.aocxファイルの名前を指定するには、 aoc -o <your_executable_filename>.aocx <your_object_filename>.aocrコマンドを呼び出します。
- 1ステップでのコンパイルフローを実行する場合は、 aoc -o <your_executable_filename>.aocx <your_kernel_filename>.clコマンドを呼び出し、.aocxファイル名を指定します。
特定のFPGAボードとカスタム・プラットフォームに対するカーネルのコンパイル (-board=<board_name>) および (-board-package=<board_package_path>)
-board=<board_name> オプションをaocコマンドに含めてカーネルをコンパイルすると、 インテル® FPGA SDK for OpenCL™オフライン・コンパイラーは、プリプロセッサー・マクロAOCL_BOARD_<board_name> を1に定義します。これにより、デバイスに最適化されたコードをカーネルにコンパイルすることができます。
-
カスタム・プラットフォームで利用可能な FPGAボードの名前を取得するには、
aoc
-list-boards
コマンドを呼び出します。
オフライン・コンパイラーは以下のような出力を生成します。
Board List: FPGA_board_1
このFPGA_board_1は、<board_name> です。
特定のカスタム・プラットフォームの利用可能なFPGAボードをすべて一覧表示することも可能です。-board-package=<custom_platform_path> オプションをaocコマンドに含めてください。コマンドプロンプトで次のコマンドを呼び出します。
aoc –board-package=<custom_platform_path> -list-boards=<board_name>
インテル® FPGA SDK for OpenCL™オフライン・コンパイラーは、特定のカスタム・プラットフォームで利用可能なボードの一覧を表示します。
-
OpenCLカーネルをFPGA_board_1にコンパイルするには、
aoc
-board=FPGA_board_1 <your_kernel_filename>.clコマンドを呼び出します。
オフライン・コンパイラーは、プリプロセッサー・マクロAOCL_BOARD_FPGA_board_1を1に定義し、FPGA_board_1をターゲットとするカーネルコードをコンパイルします。
-
複数のカスタム・プラットフォーム (ボードパッケージ) がインストールされている場合、特定のカスタム・プラットフォームのボードバリアントでカーネルをコンパイルできます。-board-package=<custom_platform_path> オプションを -board=<board_name> とともに含めてください。コマンドプロンプトで次のコマンドを呼び出します。
aoc -board-package=<custom_platform_path> -board=<board_name>
インテル® FPGA SDK for OpenCL™オフライン・コンパイラーは、<custom_platform_path> で指定されているボードでカーネルをコンパイルします。
-
システムで利用可能なカスタム・プラットフォームのリストを表示するには、-list-board-packagesオプションをaocコマンドに含めます。コマンドプロンプトで
aoc
-list-board-packages
コマンドを呼び出します。
インテル® FPGA SDK for OpenCL™オフライン・コンパイラーは、次の例のような出力を生成します。
Installed board packages: <board_package_1> ... Board packages shipped with Intel(R) FPGA SDK for OpenCL(TM): <board_package_2> ...
この <board_package_N> は、システムにインストールされているカスタム・プラットフォームのボードパッケージ、または インテル® FPGA SDK for OpenCL™ に同梱されているボードパッケージです。
特定のFPGAボードをターゲットとするコンパイル済みのカーネルファイルを容易に識別するため、インテルでは、-oオプションをaocコマンドに含め、カーネルバイナリーの名前を変更することを推奨しています。
- 1ステップのコンパイルフローでカーネルのターゲットをFPGA_board_1にするには、次のコマンドを呼び出します。
aoc -board=FPGA_board_1 <your_kernel_filename>.cl -o <your_executable_filename>_FPGA_board_1.aocx
-
複数ステップのコンパイルフローでカーネルのターゲットをFPGA_board_1にするには、次の作業を実行します。
- 以下のコマンドを呼び出し.aocoファイルを生成します。
aoc -rtl -board=FPGA_board_1 <your_kernel_filename>.cl -o <my_object_filename>_FPGA_board_1.aocr
- 以下のコマンドを呼び出し.aocxファイルを生成します。
aoc -board=FPGA_board_1 <your_object_filename>_FPGA_board_1.aocr -o <your_executable_filename>_FPGA_board_1.aocx
- 以下のコマンドを呼び出し.aocoファイルを生成します。
- 2つのFPGAで構成されるアクセラレーター・ボードの場合、各FPGAデバイスには同等の「ボード」名が付けられます (例えばboard_fpga_1、board_fpga_2など)。kernel_1.clのターゲットをboard_fpga_1、kernel_2.clのターゲットをboard_fpga_2にするには、次のコマンドを呼び出します。
aoc -board=board_fpga1 kernel_1.cl aoc -board=board_fpga2 kernel_2.cl
カーネルコンパイル時のハードウェア生成フィッティング・エラーの解決 (-high-effort)
フィッティング制約の問題によってカーネルのコンパイルが失敗すると、 インテル® FPGA SDK for OpenCL™オフライン・コンパイラーは次のエラーメッセージを表示します。
Error: Kernel fit error, recommend using -high-effort. Error: Cannot fit kernel(s) on device
コマンドを呼び出すと、オフライン・コンパイラーは次のメッセージを表示します。
High-effort hardware generation selected, compile time may increase significantly.
オフライン・コンパイラーは、カーネルの再コンパイルとハードウェアの生成を3回試みます。-high-effortを試みた後もコンパイルが失敗する場合は、カーネルを変更してください。
カーネルのFmaxターゲットのスケジュール指定 (-fmax=<fmax target in MHz>)
次のオプションの一方または両方を使用し、カーネル固有のfmaxターゲットを指定できます。
- __attribute__((scheduler_target_fmax_mhz(__x))) ソースレベル属性を使用する
- aocコマンドの-fmax=<fmax target in Mhz> で、すべてのカーネルをグローバルにコンパイルするよう、インテル FPGA SDK for OpenCL オフライン・コンパイラーに指示する
kernel void k1(){ ... } __attribute__((scheduler_target_fmax_mhz(200))) kernel void k2(){ ... }
オフライン・コンパイラーにaocコマンドの-fmax=300をコンパイルするよう指示すると、コンパイラーはカーネルk1を300 MHz、カーネルk2を200 MHzでスケジュールします。
カーネル・パラメーターを指定するためのプリプロセッサー・マクロの定義 (-D<macro_name>)
- プリプロセッサー・マクロの定義をオフライン・コンパイラーに渡すには、 aoc -D <macro_name> <kernel_filename>.clコマンドを呼び出します。
-
定義済みのプリプロセッサー・マクロの既存値を上書きするには、
aoc
-D
<macro_name>=<value>
<kernel_filename>.clコマンドを呼び出します。
次のカーネルsumのコードを例にします。
#ifndef UNROLL_FACTOR #define UNROLL_FACTOR 1 #endif __kernel void sum (__global const int * restrict x, __global int * restrict sum) { int accum = 0; #pragma unroll UNROLL_FACTOR for(size_t i = 0; i < 4; i++) { accum += x[i + get_global_id(0) * 4]; } sum[get_global_id(0)] = accum; }
UNROLL_FACTORの1を4に上書きし設定するには、 aoc -DUNROLL_FACTOR=4 sum.clコマンドを呼び出します。このコマンドを呼び出すことは、sumカーネル・ソースコードの#define UNROLL_FACTOR 1の行を#define UNROLL_FACTOR 4に置き換えることと同等です。
-
プリプロセッサー・マクロを使用し、カーネルのソースコードを変更することなくオフライン・コンパイラーによるカーネルの最適化を制御するには、
aoc
-o
<hardware_filename>.aocx -D
<macro_name>=<value>
<kernel_filename>.clを呼び出します。
以下に詳細を説明します。
-oは、オフライン・コンパイラーが生成する.aocxファイル名を指定するために使用するオフライン・コンパイラーのオプションです。
<hardware_filename> は、指定したプリプロセッサー・マクロの値を使用しオフライン・コンパイラーが生成する.aocxファイルの名前です。
ヒント: コンパイル結果をどちらもファイルシステムに保存するには、aocコマンドの-oフラグを使用し、カーネルを個別のバイナリーとしてコンパイルします。例えば、同じカーネルを必要なワークグループ・サイズの64および128で複数回コンパイルする場合、次のようにWORK_GROUP_SIZEプリプロセッサー・マクロをカーネル属性reqd_work_group_sizeに定義できます。__attribute__((reqd_work_group_size(WORK_GROUP_SIZE,1,1))) __kernel void myKernel(...) for (size_t i = 0; i < 1024; i++) { // statements }
次のコマンドを入力し、カーネルを複数回コンパイルします。
aoc –o myKernel_64.aocx –DWORK_GROUP_SIZE=64 myKernel.cl
aoc –o myKernel_128.aocx –DWORK_GROUP_SIZE=128 myKernel.cl
コンパイル進捗レポートの生成 (-v)
-
フルコンパイルの進捗をオフライン・コンパイラーに報告させるには、
aoc
-v
<your_kernel_filename>.clコマンドを呼び出します。
オフライン・コンパイラーは、次のようなコンパイル進捗レポートを生成します。
aoc: Environment checks are completed successfully. You are now compiling the full flow!! aoc: Selected target board a10gx aoc: Running OpenCL parser.... aoc: OpenCL parser completed successfully. aoc: Compiling.... aoc: Linking with IP library ... aoc: First stage compilation completed successfully. aoc: Setting up project for CvP revision flow.... aoc: Hardware generation completed successfully.
-
ハードウェアを構築しない中間コンパイルの進捗をオフライン・コンパイラーに報告させるには、
aoc
-rtl
-v
<your_kernel_filename>.clコマンドを呼び出します。
オフライン・コンパイラーは、次のようなコンパイル進捗レポートを生成します。
aoc: Environment checks are completed successfully. aoc: Selected target board a10gx aoc: Running OpenCL parser.... aoc: OpenCL parser completed successfully. aoc: Compiling.... aoc: Linking with IP library ... aoc: First stage compilation completed successfully. aoc: To compile this project, run "aoc <your_kernel_filename>.aoco"
-
エミュレーションに向けたコンパイルの進捗を報告するようオフライン・コンパイラーに指示するには、
aoc
-march=emulator
-v
<your_kernel_filename>.clコマンドを呼び出します。
オフライン・コンパイラーは、次のようなコンパイル進捗レポートを生成します。
aoc: Environment checks are completed successfully. You are now compiling the full flow!! aoc: Selected target board a10gx aoc: Running OpenCL parser....ex aoc: OpenCL parser completed successfully. aoc: Compiling for Emulation .... aoc: Emulator Compilation completed successfully. Emulator flow is successful.
リソース推定使用率要約の画面表示 (-report)
推定リソース使用率の要約は、フルコンパイルを実行しなくても確認することができます。ハードウェアのコンフィグレーション・ファイルを生成する前に、画面上で使用率の要約を確認するには、-rtlオプションをaocコマンドに含めてください。
+--------------------------------------------------------------------+ ; Estimated Resource Usage Summary ; +----------------------------------------+---------------------------+ ; Resource + Usage ; +----------------------------------------+---------------------------+ ; Logic utilization ; 35% ; ; ALUTs ; 22% ; ; Dedicated logic registers ; 15% ; ; Memory blocks ; 29% ; ; DSP blocks ; 0% ; +----------------------------------------+---------------------------;
インテル FPGA SDK for OpenCLオフライン・コンパイラーの警告メッセージの抑制 (-W)
インテル FPGA SDK for OpenCLオフライン・コンパイラーの警告メッセージのエラーメッセージへの変換 (-Werror)
コンパイラー・レポートのデバッグデータの削除および.aocxファイルのソースコードの削除 (-g0)
グローバルメモリーのバーストインターリーブの無効化 (-no-interleaving=<global_memory_type>)
-
デフォルトのグローバルメモリーのバーストインターリーブを無効にするようオフライン・コンパイラーに指示するには、
aoc
<your_kernel_filename>.cl -no-interleaving=default コマンドを呼び出します。
アクセラレーター・ボードには、複数のグローバル・メモリー・タイプが含まれる場合があります。デフォルトのグローバル・メモリー・タイプを特定するには、ご利用のカスタム・プラットフォームのボードメーカーより提供されている資料を確認ください。
-
異種メモリーのシステムにおいて、特定のグローバル・メモリー・タイプのバーストインターリーブを無効にするようオフライン・コンパイラーに指示するには、次の作業を実行します。
- カスタム・プラットフォームのBoard_spec.xmlファイルで、利用可能なグローバル・メモリー・タイプ名を確認します (DDR、クアッド・データ・レート (QDR) など)。
-
例えば、DDRのメモリータイプのバーストインターリーブを無効にするには、
aoc
<your_kernel_filename>.cl
-no-interleaving=DDRコマンドを呼び出します。
オフライン・コンパイラーは、DDRメモリーバンクの手動分割を有効にし、他のメモリーバンクをバーストインターリーブ方式でコンフィグレーションします。
-
複数のタイプのグローバル・メモリー・バッファーのバーストインターリーブを無効にするには、
-no-interleaving=<global_memory_type>
オプションを各グローバル・メモリー・タイプに含めます。
例えば、DDRとQDRのバーストインターリーブをどちらとも無効にするには、 aoc <your_kernel_filename>.cl -no-interleaving=DDR -no-interleaving=QDRコマンドを呼び出します。
グローバルメモリーのリング型相互接続の強制 (-global-ring)
コンパイラーの選択を無効にし、リング型トポロジーを強制するには、 -global-ring オプションをaocコマンドで使用します。このオプションは、ツリー型トポロジーの使用によってfmaxが制限されている場合などにカーネルのfmaxを向上させることができます。特に、4バンク以上のグローバルメモリーをともなうボード・サポート・パッケージをターゲットとするデザインのfmaxに、このオプションは有効に働きます。
例: aoc -global-ring <your_kernel_filename>.cl
グローバルメモリーへの書き込みスループット向上に向けたストアリングの複製 (-duplicate-ring)
このオプションは相互接続の幅を広げるため、より多くの書き込みが並行して発生するようになります。したがって、より多くの領域を消費します。
例: aoc -duplicate-ring <your_kernel_filename>.cl
コンスタント・メモリー・キャッシュ・サイズのコンフィグレーション (-const-cache-bytes=<N>)
デフォルトのコンスタント・キャッシュ・サイズは16 kBです。
浮動小数点演算処理順序の緩和 (-fp-relaxed)
バランスの取れたツリー構造の実装は、結果における数値の変動性と引き換えに、より効率的なハードウェアをもたらします。
浮動小数点演算における丸め処理の削減 (-fpc)
この最適化に向けた制御を実装すると、丸め処理のモードも変更されます。このモードでは、一連の浮動小数点演算処理 (乗算、加算、減算) の最後でのみ、ゼロに丸める処理が行われます。
OpenCLコンパイルの高速化 (-fast-compile)
-fast-compileフィーチャーは、最適化の作業を減らすことによりコンパイル時間を大幅に削減します。
コマンドプロンプトで aoc -rtl <your_kernel_filename1>.cl -fast-compile コマンドを呼び出します。
-fast-compileフィーチャーを有効にすると、次のようなパフォーマンス上の問題が発生する可能性があります。
- リソース使用率の増加
- fmaxの低下および、それにより発生するアプリケーション・パフォーマンスの低下
- 電力効率の低下
インテルでは、-fast-compileオプションを内部での開発目的にのみ使用することを推奨しています。
- -fast-compileコンパイラー・オプションは、 インテル® Arria® 10以降のデバイスをターゲットにするOpenCLデザインをコンパイルする場合にのみ使用することができます。
- デザインの完成後、 OpenCL* カーネルを-fast-compileオプションを指定せずに複数のシードでコンパイルし、最高のパフォーマンスを確立します。
-
-fast-compileフィーチャーが有効になっているか否かにかかわらず、OpenCLシステムの初期コンパイルを新しいボードおよび、新しいバージョンのインテルFPGA SDK for OpenCL プロ・エディションで行う場合、45分から60分の時間が追加で必要です。この追加される時間では、今後のコンパイルに備えコンパイルの一部をキャッシュしています (この動作はカーネルのパフォーマンスに影響しません)。このキャッシュを作成するには、環境変数$AOCL_TMP_DIRを共有できる書き込み可能なディレクトリーに定義します。デフォルトでこのキャッシュは、Linuxの場合、/var/tmp/aocl/$USERに、Windowsの場合、%USERPROFILE%\AppData\Local\aoclに格納されます。この書き込み可能なディレクトリーは、共有ネットワーク位置に設定することで共有できます。
キャッシュを作成後は、現在のバージョンのインテル FPGA SDK for OpenCLおよび現在のターゲットボードに対し、再度キャッシュを作成する必要はありません。
カーネルのインクリメンタル・コンパイル (-incremental)
複数のカーネルを持つ大規模なシステムにおいて単一のカーネルのみ変更する場合、 インテル® FPGA SDK for OpenCL™オフライン・コンパイラーでは、前回のコンパイル結果を再利用し、変更したカーネルのみを合成、配置、ルーティングすることができます。このインクリメンタル・コンパイル・フィーチャーは、コンパイル時間の大幅な短縮につながります。
インクリメンタル・コンパイル・フロー例
aoc-incremental<your_kernel_filename>.cl /*****Update kernels in your OpenCL design*****/ aoc-incremental-fast-compile<your_kernel_filename>.cl
-
aoc
-incremental
<your_kernel_filename>.clコマンドを呼び出し、インクリメンタル・モードが有効になっている状態で、初期セットアップのコンパイルをクリーンなディレクトリーに行います。
注: セットアップをコンパイルする際は、-incrementalフラグを必ず有効にしてください。
このセットアップのコンパイルは、以前のコンパイル結果を再利用しません。セットアップをコンパイルする際は、-fast-compileオフライン・コンパイラー・コマンド・オプションをaocコマンドに含めないでください。このオプションを含めると、今後のインクリメンタル・コンパイル時にエラーが発生する可能性が高くなります。
ヒント: インテルでは、コンパイルの時間が問題にならない場合は必ず、セットアップのコンパイルを新しく実行し、今後のインクリメンタル・コンパイル時にコンパイルが失敗する可能性を低下させることを推奨しています。連続して多くのインクリメンタル・コンパイルを実行すると、コンパイルが失敗する可能性が高くなります。また、ハードウェアのパフォーマンスおよび、生成された.aocxファイルの効率も低下します。 -
ご自身のOpenCLデザインのカーネルを修正します。
デザインには複数の.clファイルを含めることが可能です。
-
デザインのインクリメンタル・コンパイルを実行します。最適なコンパイル速度を得るために、-fast-compileフラグもaocコマンドに含めます。
aoc -incremental -fast-compile <your_kernel_filename>.cl
-
report.htmlファイルのIncremental compileセクションで、オフライン・コンパイラーが検出した変更を確認してください。
report.htmlファイルは、 <your_kernel_filename>/reportsディレクトリーにあります。
インクリメンタル・コンパイル・レポート
インクリメンタル・コンパイル・レポートは、OpenCLのデザインに関する次のようなメトリクスを提供します。
-
レポートの下部にある <%> of design not preservedメトリクスは、デザインの全体的な変更の簡単な概要を示します。これはコンパイル時間を予測する最も良い判断材料です。
注:インクリメンタル・コンパイル・レポートに表示されるFPGAリソースは、 インテル® FPGA SDK for OpenCL™オフライン・コンパイラーが作成する推定エリアモデルに基づき計算されます。エリアの数字は、標準コンパイル (インクリメンタルではないコンパイル) 時の推定エリア使用量を表します。この数字をもとに、標準コンパイル時にデザインが消費するエリアを見積もることが可能です。
FPGAのリソース情報は、 インテル® Quartus® Prime開発ソフトウェア・プロ・エディションのソフトウェア・コンパイル・レポートの最終的なエリアと完全に一致しない場合があります。


インクリメンタル・コンパイルのその他のコマンドオプション
複数のカーネルのパーティションへのグループ化 (-incremental-grouping=<filename>)
デフォルトにおいて、 インテル® FPGA SDK for OpenCL™オフライン・コンパイラーは、インクリメンタル・コンパイル時にデザイン内の各カーネルを個別のパーティションに配置します。 -incremental-grouping=<partition_filename> コマンドオプションをaocコマンドに含め、複数のカーネルを単一のパーティションにまとめることができます。通常、デザインに含まれるパーティションが少ないほどコンパイル速度は早くなります。
グループ化されたカーネルが多数のロードおよびストア動作を実行する場合、-incremental=aggressiveオプションもaocコマンドに含め、コンパイルスピードをさらに加速させることが可能です。
-Incremental-groupingオプションに渡すパーティション・ファイルは、プレーン・テキスト・ファイルです。ファイル内の各行は新しいパーティションを指定しており、カーネル名をセミコロン (;) で区切ったリストを含んでいます。例えば、パーティション・ファイルの以下の行は、3つのパーティションを指定し、それぞれに4つのカーネルを含んでいます。
reader0;reader1;reader2;reader3 accum0;accum1;accum2;accum3 writer0;writer1;writer2;writer3
アグレッシブ・モードによるデザインのコンパイル (-incremental=aggressive)
エリア使用量とスループットを引き換えに、インクリメンタル・コンパイルを加速するには、 -incremental=aggressiveコマンドオプションをaocコマンドに含めます。
このフィーチャーは、デザイン内のカーネルが多数のバッファーに対してロードおよびストア動作を行う場合や、-incremental-groupingコマンドオプションで複数のカーネルをグループ化した場合に特に有効です。
例: aoc -incremental=aggressive -incremental-grouping=<partition_filename> <your_kernel_filename>.cl
- アグレッシブ・モードを有効にすると、Fmaxの低下が示すよりも大きいスループットの低下が生じる可能性があります。
- 各OpenCLデザインのインクリメンタル・コンパイルを行っている間は、コンパイルモードを変更しないでください。デザインをアグレッシブ・モードでコンパイルする場合、そのデザインに対して続けて実行するインクリメンタル・コンパイルでは、すべてアグレッシブ・モードを有効にします。インクリメンタル・コンパイルのモードを切り替えるたびに、コンパイルが完了するまでの時間が長くなります。
カスタム入力ディレクトリーの指定 (-incremental-input-dir=<path_to_directory>)
オフライン・コンパイラーはインクリメンタル・コンパイル中に、デフォルトの <your_kernel_filename> プロジェクト・ディレクトリーを現在の作業ディレクトリーに作成し、中間コンパイルファイルを保存します。インクリメンタル・コンパイルをデフォルトのプロジェクト・ディレクトリー以外に配置するには、 -incremental-input-dir=<path_to_directory> コマンドオプションをaocコマンドに含めます。
次のいずれかまたは両方のシナリオでデザインをコンパイルする場合、-incremental-input-dirオプションを含める必要があります。
- aocコマンドを前回のコンパイルとは違う作業ディレクトリーから実行する場合
- -o <filename> コマンドオプションを前回のコンパイルに含めた場合
mykernel.clファイルが初期の作業ディレクトリーにあり、同じmykernel.clファイルの別のリビジョンがnew_revサブディレクトリーにある以下のシナリオを例にします。
aoc -incremental mykernel.cl cd new_rev aoc -incremental -fast-compile mykernel.cl -incremental-input-dir=../mykernel
このシナリオにおいてオフライン・コンパイラーは、mykernelプロジェクト・ディレクトリーにある初期コンパイル時のファイルを再利用し、それをもとに2回目のコンパイルを行います。オフライン・コンパイラーは、元のmykernelディレクトリー内のファイルを変更することなく、new_rev/mykernelプロジェクト・ディレクトリーを2回目のコンパイルに作成します。
-incremental-input-dirコマンドオプションは、複数のデベロッパーが同じインクリメンタル・セットアップ・コンパイルを共有する場合に有効です。各デベロッパーはその後のインクリメンタル・コンパイルを、他のデベロッパーのコンパイル結果を上書きすることなく自分の作業スペースで実行できます。
自動再試行の無効化 (-incremental-flow=no-retry)
インクリメンタル・コンパイルが失敗した場合、デフォルトでオフライン・コンパイラーはコンパイルの再試行を自動的に行います。その場合、パーティションは保存されずに2回目のコンパイルが実行されます。この2回目のコンパイルは、デザイン全体を再コンパイルするため完了までに時間がかかります。
オフライン・コンパイラーの自動再試行メカニズムを無効にするには、-incremental-flow=no-retryコマンドオプションをaocコマンドに含めます。このフィーチャーを有効にすると、最初の試みが失敗に終わった際に、オフライン・コンパイラーは2回目のインクリメンタル・コンパイルを実行しません。また、オフライン・コンパイラーは、.aocxファイルを生成しません。
このフィーチャーを有効にすることにより、次のような独自の失敗軽減ストラテジーを実行することが可能になります。
- 複数のシードを並行してコンパイルすることにより、最低1つのコンパイルが再試行なしで成功する可能性を高める
- インクリメンタル高速コンパイルに代わり、非インクリメンタル高速コンパイル ( aoc -fast-compile <your_kernel_filename>.cl) を実行する。
インクリメンタル・コンパイル・フィーチャーの制限
デバイスのサポートのほかに、インクリメンタル・コンパイルには次の制限があります。
- インクリメンタル・コンパイル・フィーチャー (-incremental) または高速コンパイルフィーチャー (-fast-compile)、もしくはその両方を有効にすると、エリア、Fmax、電力の低下が発生します。
- 輻輳したデザインのインクリメンタル・コンパイルでは、初期セットアップのコンパイルと比較しFmaxが大幅に低下することがあります (25%以上)。Fmaxの低下を許容できない場合、非インクリメンタル高速コンパイルを実行し、一定のコンパイル時間の短縮を維持しながらFmaxの低下量を軽減します。
-
オフライン・コンパイラーは、 -l <library_name>.aoclibオフライン・コンパイラー・コマンド・オプションを呼び出すことで行ったRTLライブラリーの変更を検出しません。RTLライブラリーを変更した後は、セットアップ・コンパイルを再度行う必要があります。
オフライン・コンパイラーは、セットアップ・コンパイルを再度実行するよう警告メッセージを表示し注意喚起します。
メモリーの誤り訂正符号をともなうカーネルのコンパイル (-ecc)
ECCの実装には、各32ビットワードに対しシングルエラー訂正機能とダブルエラー検出機能があります。
ハードウェアのカーネル呼び出しキューの無効化 (-no-hardware-kernel-invocation-queue)
例: aoc -no-hardware-kernel-invocation-queue <your_kernel_filename>.cl
このオプションを使用することでカーネルの実行時間が長くなる場合があります。カーネルの呼び出しキューは、OpenCLランタイム環境において、カーネルの起動をアクセラレーターにキューすることを可能にします。そのためアクセラレーターは、カーネルの呼び出し終了後すぐに同じカーネルの次の実行をスタートできます。
カーネル呼び出しキューの使用方法に関しては、Intel FPGA SDK for OpenCL Pro Edition: Best Practices Guideのトピック、Utilizing Hardware Kernel Invocation Queueを参照ください。
ハンドシェイク・プロトコルの変更 (-hyper-optimized-handshaking)
-hyper-optimized-handshaking オプションには、次のいずれかの値を設定できます。
- auto
- オプションを指定しないデフォルトの動作です。可能な場合、コンパイラーは最適化を有効にします。そうでない場合はoffに設定されます。
- より高いfmaxを要求する場合にこの値を使用します。このオプションを有効にすると、 インテル® FPGA SDK for OpenCL™オフライン・コンパイラーは、ストールを許容するノードのハンドシェイク・パスに、パイプライン・レジスターを追加します。これにより、エリアとレイテンシーが増加する代わりにfmaxが高くなります。
-
例: aoc -hyper-optimized-handshaking <your_kernel_filename>.cl
- off
- コンパイラーは、fmaxが低下する潜在的な可能性と引き換えに、低レイテンシーに向けた最適化を試みます。このハンドシェイクの高度な最適化を無効にすると、エリアの縮小につながる可能性もあります。これは、fmaxをあまり必要としない小規模なデザインにおいて、レイテンシーとエリアを低減できるため効果的です。
-
例: aoc -hyper-optimized-handshaking=off <your_kernel_filename>.cl
OpenCLカーネルのエミュレーションとデバッグ
インテル® FPGA SDK for OpenCL™ Emulatorは、x86-64 Windows または Linuxホストで実行する.aocxファイルを生成します。この機能により、カーネルの機能性をエミュレーションし、毎回実際のFPGAで実行することなくデザインを反復することが可能になります。Linuxのプラットフォームでは、エミュレーターを使用し機能のデバッグを実行することも可能です。
エミュレーターは、64ビットのWindowsおよびLinuxのオペレーティング・システムをサポートしています。Linuxシステムでは、GNU C Library (glibc) version 2.15以降のバージョンが必要です。お使いのLinuxシステムがこの要件を満たさない場合、レガシー・エミュレーターを利用できる可能性があります。詳細は、レガシー・エミュレーターの使用 を確認ください。
- 高速エミュレーターの設定
インテル® FPGA SDK for OpenCL™ プロ・エディションを管理者権限でインストールしている場合、次の手順を実行する必要はありません。 インテル® FPGA SDK for OpenCL™ を管理者権限でインストールしていない場合は、高速エミュレーターを有効にするための作業を行う必要があります。 - エミュレーションに向けたチャネル・カーネル・コードの変更
I/Oチャネルの読み書きを行うチャネルを持つアプリケーションをエミュレーションするには、カーネルを変更し、I/Oチャネルに代わる読み取りまたは書き込みチャネルを追加します。また、そのチャネルを使用するソースコードを条件付きにします。 - エミュレーションに向けたカーネルのコンパイル (-march=emulator -fast-emulator)
エミュレーションに向けてOpenCL™カーネルをコンパイルするには、-march=emulatorおよび-fast-emulatorオプションをaocコマンドに含めます。 - OpenCLカーネルのエミュレーション
OpenCL™カーネルをエミュレーションするには、カーネルを構築したプラットフォームで.aocxエミュレーション・ファイルを実行します。OpenCL高速エミュレーターは、FPGAハードウェアをターゲットとする際とは異なるOpenCLプラットフォームを使用します。 - LinuxにおけるOpenCLカーネルのデバッグ
Linux システムでは、 インテル® FPGA SDK for OpenCL™ Emulatorに、デバッガーでOpenCLカーネルを実行し、ホスト・アプリケーションの一部として機能的にデバッグするよう指示することができます。 - インテル FPGA SDK for OpenCL Emulatorの制限
インテル® FPGA SDK for OpenCL™ Emulatorのフィーチャーには、いくつかの制限があります。 - ハードウェアとエミュレーターの結果の不一致
カーネルをエミュレーションすると、OpenCLシステムは、ハードウェアにコンパイルされたカーネルとは異なる結果を生成することがあります。シミュレーションでカーネルを実行することで、ハードウェアにカーネルをコンパイルする前にさらにカーネルをデバッグすることが可能です。 - 高速エミュレーターの環境変数
高速エミュレーターの動作を変更する環境変数をいくつか利用することが可能です。 - 高速エミュレーターでサポートされている拡張機能
高速エミュレーターは、さまざまなOpenCL拡張機能に対し、さまざまなレベルのサポートを提供しています。 - 高速エミュレーターの既知の問題
高速エミュレーターの使用は、いくつかの既知の問題に影響を受ける可能性があります。起こりうる問題を回避するため、高速エミュレーターを使用する際はこれらの問題を考慮ください。 - レガシー・エミュレーターの使用
インテル® FPGA SDK for OpenCL™ バージョン18.1および、それ以前のバージョンにおけるデフォルトのエミュレーターは現在も入手可能であり、利用することができます。 ただし、インテルFPGA OpenCLシステムのエミュレーションには、高速エミュレーターの利用を推奨しています。
高速エミュレーターの設定
インテル® FPGA SDK for OpenCL™ プロ・エディションを管理者権限でインストールしている場合、次の手順を実行する必要はありません。 インテル® FPGA SDK for OpenCL™ を管理者権限でインストールしていない場合は、高速エミュレーターを有効にするための作業を行う必要があります。
インテル® FPGA SDK for OpenCL™ を管理者権限でインストールしていない場合、高速エミュレーターを使用する前に次の手順を実行してください。
- 高速エミュレーターをインストール可能なクライアント・ドライバー (ICD) エントリーを手動で設定します。
-
Linuxの場合、/etc/OpenCL/vendors/Intel_FPGA_SSG_Emulator.icdファイルが、環境変数INTELFPGAOCLSDKROOTが指定するディレクトリーにあるファイルと一致することを確認します。INTELFPGAOCLSDKROOT環境変数は、SDKをインストールする位置を指しています。
ファイルが一致しない場合や、/etc/OpenCL/vendorsにファイルが存在しない場合は、Intel_FPGA_SSG_Emulator.icdファイルをINTELFPGAOCLSDKROOT環境変数で指定されている位置から/etc/OpenCL/vendorsディレクトリーへコピーします。
- Windowsの場合、レジストリー・キーHKEY_LOCAL_MACHINE\SOFTWARE\Khronos\OpenCL\Vendorsが次の値を含んでいることを確認してください。[HKEY_LOCAL_MACHINE\SOFTWARE\Khronos\OpenCL\Vendors] "intelocl64_emu.dll"=dword:00000000
-
Linuxの場合、/etc/OpenCL/vendors/Intel_FPGA_SSG_Emulator.icdファイルが、環境変数INTELFPGAOCLSDKROOTが指定するディレクトリーにあるファイルと一致することを確認します。INTELFPGAOCLSDKROOT環境変数は、SDKをインストールする位置を指しています。
- インテルFPGAサポートプレビューで、
インテル® FPGA SDK for OpenCL™
を手動でインストール (または再インストール) します。
この手順により、64ビットの インテル® SDK for OpenCL™オフライン・コンパイラー・コマンド・ライン・インターフェイス (ioc64) がシステムにインストールされます。
エミュレーションに向けたチャネル・カーネル・コードの変更
channel unlong4 inchannel __attribute__((io("eth0_in"))); __kernel void send (int size) { for (unsigned i = 0; i < size; i++) { ulong4 data = read_channel_intel(inchannel); //statements } }
I/Oチャネルに接続するチャネルを持つカーネルをエミュレーションするエミュレーターを有効にするには、次の手順を実行します。
-
次のいずれかの方法でカーネルコードを変更します。
- 次のように、一致するwrite_channel_intel呼び出しを追加する
#ifdef EMULATOR __kernel void io_in (__global char * restrict arr, int size) { for (unsigned i = 0; i < size; i++) { ulong4 data = arr[i]; //arr[i] being an alternate data source write_channel_intel(inchannel, data); } } #endif
- I/Oチャネルアクセスを次のようにメモリーアクセスに置き換える
__kernel void send (int size) { for (unsigned i = 0; i < size; i++) { #ifndef EMULATOR ulong4 data = read_channel_intel(inchannel); #else ulong4 data = arr[i]; //arr[i] being an alternate data source #endif //statements } }
- 次のように、一致するwrite_channel_intel呼び出しを追加する
- エミュレーション中にこの条件付きカーネルを作成および起動するよう、ホスト・アプリケーションを変更します。
値によってチャネルまたはパイプを渡すカーネルのエミュレーション
値によってチャネルまたはパイプを渡すカーネルは、次のようにエミュレーションできます。
channel uint my_ch; void my_function (channel uint ch, __global uint * dst, int i) { dst[i] = read_channel_intel(ch); } __kernel void consumer (__global uint * restrict dst) { for (int i=0;i<5;i++) { my_function(my_ch, dst, i ); } }
チャネル深度のエミュレーション
エミュレーションに向けて OpenCL* カーネルをコンパイルする際のデフォルトのチャネル深度は、カーネルがハードウェアにコンパイルされる際に生成されるデフォルトのチャネル深度と異なります。この動作は、CL_CONFIG_CHANNEL_DEPTH_EMULATION_MODE環境変数を使用しエミュレーションに向けてカーネルをコンパイルすることで変更できます。
- ignoredepth
- カーネルのエミュレーション実行時間が最短になるよう選択されたチャネル深度がすべてのチャネルに与えられます。明示的に設定されたチャネル深度属性は無視されます。
CL_CONFIG_CHANNEL_DEPTH_EMULATION_MODE環境変数が設定されていない場合はこの値がデフォルトで使用されます。
- default
- 明示的に深度属性が指定されているチャネルは、その深度を持ちます。深度が指定されていないチャネルには、カーネルのエミュレーション実行時間が最短になるよう選択されたデフォルトのチャネル深度が与えられます。
- strict
- エミュレーションのチャネル深度はすべて、FPGAのコンパイルに指定されている深度に一致するように与えられます。
エミュレーションに向けたカーネルのコンパイル (-march=emulator -fast-emulator)
- カーネルのエミュレーションを実行する前に、次の作業を実行します。
- 環境変数QUARTUS_ROOTDIR_OVERRIDEが、 インテル® Quartus® Prime開発ソフトウェア・プロ・エディションのソフトウェアをインストールするフォルダーを指していることを確認してください。
- LD_LIBRARY_PATH環境変数の設定に、 インテル® FPGA SDK for OpenCL™ スタート・ガイド の インテル® FPGA SDK for OpenCL™ ユーザー環境変数の設定の章で説明されているパスがすべて含まれていることを確認します。
- x86-64のホストシステムで実行可能なカーネルプログラムを作成するため、 aoc -march=emulator -fast-emulator <your_kernel_filename>.clコマンドを呼び出します。
-
Linuxシステムに向けてインテル FPGA SDK for OpenCL オフライン・コンパイラーは、シンボリック・デバッガーのデバッグサポートを提供しています。
オフライン・コンパイラーのデバッグサポートでは、カーネル・ソースコードの機能的なエラーの原因を特定できます。
OpenCLカーネルのエミュレーション
カーネルをエミュレーションするには、次のステップを実行します。
-
ホストプログラムを変更し、エミュレーターのOpenCLプラットフォームを選択します。
次のプラットフォーム名を選択し、ホストプログラムのエミュレーション用OpenCLプラットフォームを選択します。
Intel(R) FPGA Emulation Platform for OpenCL(TM)
- ホスト・アプリケーションを構築し、ホスト・アプリケーションをKhronos ICD Loader Libraryにリンクします。詳細については、ホスト・アプリケーションとKhronos ICD Loader Libraryのリンクを確認ください。
- 必要な場合は、 <your_kernel_filename>.aocxファイルを、ホストが容易に検索できる位置へ動かします。現在の作業ディレクトリーが推奨されます。
-
エミュレーションに向けてホスト・アプリケーションを次のように実行します。
- Windowsの場合
- set CL_CONFIG_CPU_EMULATE_DEVICES=<number_of_devices> コマンドを呼び出し、エミュレーションされたデバイス数を定義します。
- ホスト・アプリケーションを実行します。
- set CL_CONFIG_CPU_EMULATE_DEVICES=を呼び出し、変数の設定を解除します。
- Linuxの場合は、 env CL_CONFIG_CPU_EMULATE_DEVICES=<number_of_devices> <host_application_filename> コマンドを呼び出します。
このコマンドは、エミュレーターが提供する必要のある同一のエミュレーション・デバイス数を指定します。要確認: エミュレーション用OpenCLプラットフォーム (Intel(R) FPGA Emulation Platform for OpenCL(TM)) は、物理的なボードへのアクセスを提供しません。エミュレーションされたデバイスのみ利用可能です。ヒント: エミュレーター・デバイスを1つだけ使用する場合は、CL_CONFIG_CPU_EMULATE_DEVICES環境変数を設定する必要はありません。 - Windowsの場合
- ホストまたはカーネルプログラムの変更後にテストを行う場合、変更したホストまたはカーネルプログラムのみを再コンパイルし、エミュレーションを再度実行します。
LinuxにおけるOpenCLカーネルのデバッグ
デバッグに向けてOpenCLカーネルをコンパイルするには、次のステップを実行します。
- 特定のアクセラレーター・ボードをターゲットとするデバッグの.aocxファイルを生成するため、 aoc -march=emulator -fast-emulator -g <your_kernel_filename>.clコマンドを呼び出します。
- ホスト・アプリケーションを構築し、ホスト・アプリケーションをKhronos ICD Loader Libraryにリンクします。詳細は、ホスト・アプリケーションとKhronos ICD Loader Libraryのリンク を確認ください。
- <your_kernel_filename>.aocxファイルが、ホストが容易に検索できる位置にあることを確認してください。現在の作業ディレクトリーが推奨されます。
- アプリケーションの実行には、 env CL_CONFIG_CPU_EMULATE_DEVICES=<number_of_devices> gdb --args <your_host_program_name> [<host_program_arguments>] コマンドを呼び出します。この <number_of_devices> は、エミュレーターが提供する必要のある同一のエミュレーション・デバイス数です。
- ホストまたはカーネルプログラムの変更後にテストを行う場合は、変更したホストまたはカーネルプログラムのみを再コンパイルし、デバッガーを再度実行します。
- プログラムの実行中、デバッガーはホストコードからカーネルコードにステップすることはできません。次の行を追加し、実際にカーネルを呼び出す前にブレークポイントを設定する必要があります。
-
break <your_kernel>
この行はカーネルの前にブレークポイントを設定します。
-
continue
ホストのデバッグを開始していない場合は、代わりにstartを入力します。
-
break <your_kernel>
- カーネルは、ホストがカーネルをロードする直前に共有ライブラリーとしてロードされます。ホストが実際にカーネル関数をロードするまで、デバッガーはカーネル名を認識しません。そのためデバッガーは、最初のカーネルの実行前に設定したブレークポイントに対する次の警告を生成します。
Function "<your_kernel>" not defined.
Make breakpoint pending on future shared library load? (y or [n])
yと返答します。最初のプログラム実行後にデバッガーは、セッション中の関数名と変数名および、行番号の参照を認識します。
インテル FPGA SDK for OpenCL Emulatorの制限
- 実行モデル
エミュレーターは、FPGAバリアントと同じコンパイルモードをサポートします。そのため、clCreateProgramBinary関数を呼び出し、エミュレーションのためのcl_programオブジェクトを作成する必要があります。
- 並列実行
並行するカーネルの実行のモデリングには制限があります。実行中にエミュレーターは、通信しているワークアイテムを実際には並行して実行しません。よって、異なるカーネルが同期化のためにバリアーなしでグローバルメモリーにアクセスしている場合など、並行して実行されている動作によっては、実行ごとに一貫性のないエミュレーション結果が生成される可能性があります。
- エミュレーターは、ホストランタイムとカーネルを同じアドレス空間で実行します。ホスト・アプリケーションで特定のポインターや配列を使用することにより、カーネルプログラムが失敗する場合があります。またその逆も同じです。使用例としては、外部に割り当てられたメモリーのインデックス化や、ランダムなポインターへの書き込みなどがあります。Valgrindなどのメモリーリーク検出ツールを使用し、プログラムを分析することができます。ただし、カーネルでの範囲を超える書き込み動作によってホストに致命的なエラーが発生する可能性があります。また、その逆も同様です。
- チャネル動作のエミュレーションには制限があります。特に、カーネルがループの反復ごとにチャネル動作を呼び出さない条件付きのチャネル動作の場合に当てはまります。このような場合、エミュレーターはハードウェアとは異なる順序でチャネル動作を実行することがあります。
ハードウェアとエミュレーターの結果の不一致
カーネルをエミュレーションすると、OpenCLシステムは、ハードウェアにコンパイルされたカーネルとは異なる結果を生成することがあります。シミュレーションでカーネルを実行することで、ハードウェアにカーネルをコンパイルする前にさらにカーネルをデバッグすることが可能です。
エミュレーターとハードウェアの結果の不一致を引き起こす最も一般的な理由は次のとおりです。
- OpenCLカーネルコードが#pragma ivdepディレクティブを使用している。エミュレーターは、真の依存関係がpragma ivdepディレクティブによって壊されている場合、OpenCLシステムをモデル化しません。ハードウェアをフルコンパイルする際に、これは誤った結果として考えられます。
- OpenCLカーネルコードが、初期化されていないデータに依存している。初期化されていないデータとは、初期化されていない変数、初期化されていない、または部分的に初期化されたグローバルバッファー、ローカル配列、プライベート配列などです。
- OpenCLカーネルコードの動作が、正確な浮動小数点演算の結果に依存している。エミュレーターがCPUの浮動小数点計算ハードウェアを使用する一方で、ハードウェアの実行にはFPGAコアとして実装されている浮動小数点コアが使用されます。-fp-relaxedのaocオプションをOpenCLカーネルコードに使用すると、演算の順序が変わり、浮動小数点の計算結果がさらに変動する可能性があります。注: OpenCLの標準は、各プラットフォームの浮動小数点計算の最下位ビットが1つ以上異なることを許容します。一方でそれは、どのプラットフォームでも正しいと見なされます。
- OpenCLカーネルコードの動作が、異なるカーネルのチャネルアクセスの順序に依存している。チャネル動作のエミュレーションには制限があります。特に、カーネルがループの反復ごとにチャネル動作を呼び出さない条件付きのチャネル動作の場合に当てはまります。このような場合にエミュレーターは、ハードウェアとは異なる順序でチャネル動作を実行することがあります。
- OpenCLカーネルまたはホストコードが、範囲外のグローバルメモリーにアクセスしている。重要:
- 初期化されていないメモリーの読み取りおよび書き込み動作は、プラットフォームに依存します。カーネル内のすべてのアドレスを使用している場合、clCreateBuffer関数呼び出しを割り当てている場合、clEnqueueReadBufferとclEnqueueWriteBuffer関数呼び出しを転送している場合は、グローバルメモリーのバッファーサイズを確認してください。
- Valgrindなどのソフトウェア・メモリー・リーク検出ツールをエミュレーションされたバージョンのOpenCLシステムで使用すると、メモリーに関する問題を分析できます。これらのツールからの警告がないということは、問題がないということを意味するわけではありません。これは単に、ツールが問題を検出できなかったことを意味するだけです。インテルではこのような状況において、OpenCLカーネルまたはホストコードの手動検証を推奨しています。
- OpenCLカーネルコードが、範囲外のローカルまたはプライベート変数にアクセスしている。例えば、範囲外のローカルまたはプライベート配列にアクセスしている場合や、スコープ外になったプライベート変数にアクセスしている場合です。重要: 範囲外の変数へのアクセスは、通常ソフトウェアのスタックにおいて、アクセスされている変数付近の無関係な変数に影響を及ぼすため、これらの問題はソフトウェア用語でスタックの破損問題と呼ばれています。エミュレーションされたOpenCLカーネルは、通常のCPU機能として実装されており、破損する可能性のあるスタックを実際に持っています。ハードウェアをターゲットにしている場合はスタックが存在しないため、スタックの破損問題は必ず別の形で現れます。スタックの破損が疑われる場合は、Valgrindなどのメモリーリーク検証ツールを使用することが可能ですが、スタック関連の問題の特定は通常困難です。インテルでは、スタック関連の問題をデバッグするため、OpenCLカーネルコードを手動で検証することを推奨しています。
- OpenCLカーネルコードが、シフトされる型よりも大きいシフトを使用している。例えば64ビットの整数を65ビットでシフトしている場合などです。OpenCL specification version 1.0によると、このようなシフトの動作は未定義です。
- エミュレーションに向けてOpenCLカーネルをコンパイルする際のデフォルトのチャネル深度が、カーネルがハードウェアにコンパイルされる際に生成されるデフォルトのチャネル深度と異なる。このチャネル深度の相違は、カーネルのエミュレーションは問題なく機能する一方で、ハードウェアでの実行はハングアップするという状況を引き起こす可能性があります。チャネル深度の相違を修正する方法については、チャネル深度のエミュレーション を参照してください。
- 出力されるラインの順序に関しては、printf関数で出力される順序がエミュレーターとハードウェアで異なる場合があります。これは、ハードウェアにおいてprintfデータはグローバル・メモリー・バッファーに格納され、カーネルの実行が完了した際、またはバッファーがフルになった際にのみバッファーからフラッシュされるためです。エミュレーターのprintf関数は、x86 stdoutを使用しています。
- 型のアップキャストでアライメントされていないロードやストアを実行すると、FPGAとエミュレーターでは異なる結果が生じる可能性があります。この型のロードおよびストアは、C99の仕様では定義されていません。次例のような演算では、予期しない結果になる可能性があります。
int tmp = *((int *) (my_ptr + 5));
高速エミュレーターの環境変数
高速エミュレーターの動作を変更する環境変数をいくつか利用することが可能です。
OCL_TBB_NUM_WORKERS
エミュレーターが使用できる最大スレッド数を示します。デフォルト値は32、最大値は255です。各スレッドは単一のカーネルを実行できます。
アプリケーションで複数のカーネルを同時に実行する必要がある場合、OCL_TBB_NUM_WORKERSを適切に設定する必要があります (使用されているカーネルの数またはそれ以上の値に設定してください)。
CL_CONFIG_CPU_FORCE_LOCAL_MEM_SIZE
利用可能なOpenCLローカルメモリーの容量を単位で設定します。例えば8MB、256KB、1024Bなどです。
CL_CONFIG_CPU_FORCE_PRIVATE_MEM_SIZE
利用可能なOpenCLプライベート・メモリーの容量を単位で設定します。例えば8MB、256KB、1024Bなどです。
CL_CONFIG_CHANNEL_DEPTH_EMULATION_MODE
エミュレーションに向けてOpenCLカーネルをコンパイルすると、カーネルをハードウェアに向けてコンパイルする際に生成されるチャネル深度とは異なるチャネル深度になります。この動作は、CL_CONFIG_CHANNEL_DEPTH_EMULATION_MODE環境変数で変更できます。詳細は、チャネル深度のエミュレーション を参照ください。
高速エミュレーターでサポートされている拡張機能
高速エミュレーターは、さまざまなOpenCL拡張機能に対し、さまざまなレベルのサポートを提供しています。
- cl_intel_fpga_host_pipe
- cl_khr_byte_addressable_store
- cl_khr_icd
- cles_khr_int64
- cl_intel_channels
- cl_khr_local_int32_base_atomics
- cl_khr_local_int32_extended_atomics
- cl_khr_global_int32_base_atomics
- cl_khr_global_int32_extended_atomics
- cl_khr_fp64
- cl_khr_fp16
高速エミュレーターの既知の問題
高速エミュレーターの使用は、いくつかの既知の問題に影響を受ける可能性があります。起こりうる問題を回避するため、高速エミュレーターを使用する際はこれらの問題を考慮ください。
自動実行カーネル
自動実行カーネルは、clReleaseProgram() 呼び出しの後ではなく、ホストプログラムの終了後にのみシャットダウンします。
コンパイラー診断
一部のコンパイラー診断は高速エミュレーターに未実装です。
clEnqueueNDRangeKernel() が返すCL_OUT_OF_RESOURCESエラー
高速エミュレーターがデフォルトでサポートしているよりも多くの__privateまたは__localメモリーをカーネルが使用している場合に発生する可能性があります。
CL_CONFIG_CPU_FORCE_PRIVATE_MEM_SIZEまたはCL_CONFIG_CPU_FORCE_LOCAL_MEM_SIZE環境変数を設定してください。詳細は高速エミュレーターの環境変数 を参照ください。
clCreateKernel() が返すCL_INVALID_VALUEエラー
clBuildProgram() の呼び出しが行われていない可能性があります。
プログラムがバイナリーから作成されている場合でも、この呼び出しはOpenCL仕様に必要です。詳細は、OpenCL Specification version 1.0のSection 5.4.2を参照してください。
uses_global_work_offsetカーネル属性は認識されない属性であるという警告
OpenCL高速エミュレーターは、uses_global_work_offsetカーネル属性をサポートしていません。属性が無視されることを通知する警告がコンパイル時に出されることがありますが、この警告は高速エミュレーターにおけるカーネルの実行には影響しないため、無視しても問題ありません。
レガシー・エミュレーターの使用
OpenCLカーネルのエミュレーションとデバッグ で紹介されている推奨事項の多くは、どちらのエミュレーターにも適用されます。ただし、レガシー・エミュレーターには、次の章以降で説明されている違いがいくつかあります。
レガシー・エミュレーターでのエミュレーションに向けたカーネルのコンパイル (-march=emulator)
- カーネルのエミュレーションを実行する前に、以下の作業を実行してください。
- FPGAアクセラレーター・ボードに対しボードメーカーから提供されているカスタム・プラットフォームをインストールします。
- 環境変数QUARTUS_ROOTDIR_OVERRIDEが、 インテル® Quartus® Prime開発ソフトウェア・プロ・エディションのソフトウェアをインストールするフォルダーを指していることを確認します。
- Windowsシステムでカーネルをエミュレーションするには、Microsoftのリンカーおよび、コンパイル用のライブラリーが別途必要になります。PATH環境変数設定に、
インテル® FPGA SDK for OpenCL™
スタート・ガイドの
インテル® FPGA SDK for OpenCL™
ユーザー環境変数の設定の章で説明されているパスがすべて含まれていることを確認してください。
PATH環境変数設定は、Microsoft Visual StudioのLINK.EXEファイルへのパスを含んでいる必要があります。
-
LIB環境変数設定に、Microsoftのコンパイル時のライブラリーへのパスが含まれていることを確認してください。
コンパイル時のライブラリーは、Microsoft Visual Studioで利用可能です。
- LD_LIBRARY_PATH環境変数設が、 インテル® FPGA SDK for OpenCL™ スタート・ガイドの インテル® FPGA SDK for OpenCL™ ユーザー環境変数の設定の章で説明されているパスをすべて含んでいることを確認してください。
- x86-64ホストシステムで実行可能なカーネルプログラムを作成するため、 aoc -march=emulator <your_kernel_filename>.clコマンドを呼び出します。
- 特定のボードをターゲットとするエミュレーションに向けてカーネルをコンパイルするには、 aoc -march=emulator -board=<board_name> <your_kernel_filename>.clコマンドを呼び出します。
-
Linuxシステムに向け、
インテル® FPGA SDK for OpenCL™オフライン・コンパイラーは、シンボリック・デバッガーのデバッグサポートを提供しています。
オフライン・コンパイラーのデバッグサポートにより、カーネル・ソースコードの機能的なエラーの原因を特定できるようになります。
レガシー・エミュレーターでのチャネル深度のエミュレーション
エミュレーションに向けて OpenCL* カーネルをコンパイルする際のデフォルトのチャネル深度は、カーネルをハードウェアに向けてコンパイルする際に生成されるデフォルトのチャネル深度とは異なります。この動作は、カーネルをエミュレーションに向けてコンパイルする際に-emulator-channel-depth-modelオプションを使用し変更することが可能です。
- default
- 明示的な深度属性を持つチャネルは、指定された深度持ちます。深度が指定されていないチャネルには、カーネルのエミュレーション実行時間が最短になるように選択されたデフォルトのチャネル深度が与えられます。
- strict
- FPGAのコンパイルに指定されている深度と一致するチャネル深度が、エミュレーションのすべてのチャネル深度に与えられます。
- ignore-depth
- カーネルのエミュレーション実行時間が最短になるように選択されたチャネル深度が、すべてのチャネルに与えられます。明示的に設定されたチャネル深度属性はいずれも無視されます。
レガシー・エミュレーターでのOpenCLカーネルのエミュレーション
以下の手順でカーネルをエミュレーションしてください。
- ユーティリティー・コマンド aocl linkflags を実行し、ホスト・アプリケーションを構築するために必要なライブラリーを検索します。ソフトウェアは、エミュレーションおよび通常のカーネルのコンパイルフローに必要なライブラリーをどちらも表示します。
-
ホスト・アプリケーションを構築し、手順1で検索したライブラリーにリンクします。
重要: 複数のデバイスを他のOpenCL SDKと共にエミュレーションするには、ホスト・アプリケーションをホストランタイムのライブラリーに接続する前 に、Khronos ICD Loader Libraryに接続します。ホスト・アプリケーションとICD Loader Libraryの接続は、ホスト・アプリケーションのMakefileを変更して行います。詳細は、ホスト・アプリケーションとKhronos ICD Loader Libraryのリンク を参照ください。
- 必要な場合は、 <your_kernel_filename>.aocxファイルをホストが容易に検索できる位置に移動します。現在の作業ディレクトリーが推奨されます。
-
エミュレーションに向けてホスト・アプリケーションを実行するには
- Windowsの場合、
set CL_CONTEXT_EMULATOR_DEVICE_INTELFPGA=<number_of_devices>
コマンドを呼び出し、エミュレーションされたデバイス数を最初に定義し、その後ホスト・アプリケーションを実行します。
ホスト・アプリケーションを実行した後、 set CL_CONTEXT_EMULATOR_DEVICE_INTELFPGA=を呼び出し変数の設定を解除します。
- Linuxの場合、 env CL_CONTEXT_EMULATOR_DEVICE_INTELFPGA=<number_of_devices> <host_application_filename> コマンドを呼び出します。
このコマンドは、エミュレーターが提供する必要がある同一のエミュレーション・デバイス数を指定します。要確認: 環境変数CL_CONTEXT_EMULATOR_DEVICE_INTELFPGAを設定すると、エミュレーションされたデバイスのみが利用可能になります。つまり、物理的なボードへのアクセスはすべて無効になります。 - Windowsの場合、
set CL_CONTEXT_EMULATOR_DEVICE_INTELFPGA=<number_of_devices>
コマンドを呼び出し、エミュレーションされたデバイス数を最初に定義し、その後ホスト・アプリケーションを実行します。
- ホストまたはカーネルプログラムを変更後にテストを行う場合は、変更したホストまたはカーネルプログラムのみを再コンパイルし、エミュレーションを再度実行してください。
エミュレーションされたカーネルを呼び出すたびに、共有ライブラリーのコピーである <process_ID>-libkernel.soが、デフォルトの一時ディレクトリーに作成されます。この<process_ID> は、各エミュレーションの実行に割り当てられた固有の数値です。Windowsでは、TMPまたはTEMP環境変数を設定し、デフォルトのディレクトリーを変更することができます。LinuxではTMPDIRの設定です。
Linuxにおけるレガシー・エミュレーターでのOpenCLカーネルのデバッグ
レガシー・エミュレーションでのデバッグに向けてOpenCLカーネルをコンパイルするには以下を行います。
- レガシー・エミュレーターでのエミュレーションに向けたカーネルのコンパイル (-march=emulator) の説明に従い、カーネルをコンパイルしてください。
- ホスト・アプリケーションを構築し、Khronos ICD Loader Libraryに接続します。詳細は、ホスト・アプリケーションとKhronos ICD Loader Libraryのリンク を参照ください。
- <your_kernel_filename>.aocxファイルが、ホストが特定できる位置にあることを確認します。現在の作業ディレクトリーが推奨されます。
- env CL_CONTEXT_EMULATOR_DEVICE_INTELFPGA=<number_of_devices> gdb --args <your_host_program_name> [<host_program_arguments>]コマンドを呼び出し、アプリケーションを実行します。ここで <number_of_devices> は、エミュレーターが提供する必要のある同一のエミュレーション・デバイス数です。
- ホストまたはカーネルプログラムを変更した後にテストを行う場合は、変更したホストまたはカーネルプログラムのみを再コンパイルし、デバッガーを再度実行してください。
- プログラムの実行中、デバッガーはホストコードからカーネルコードにステップすることはできません。次の行を追加し、実際にカーネルが呼び出される前にブレークポイントを設定する必要があります。
-
break <your_kernel>
この行はカーネルの前にブレークポイントを設定します。
-
continue
ホストのデバッグをまだ開始していない場合は、代わりにstartを入力します。
-
break <your_kernel>
- カーネルは、ホストがカーネルをロードする直前に共有ライブラリーとしてロードされます。ホストが実際にカーネル関数をロードするまで、デバッガーはカーネル名を認識しません。そのためデバッガーは、最初のカーネルの実行の前に設定したブレークポイントに対する次の警告を生成します。
Function "<your_kernel>" not defined.
Make breakpoint pending on future shared library load? (y or [n])
yを返答します。最初のプログラムの実行後にデバッガーは、セッション中の関数名と変数名、および行番号の参照を認識します。
カーネルのreport.htmlファイルのレビュー
analyze-area インテル® FPGA SDK for OpenCL™ ユーティリティー・オプションは廃止されました。カーネルの推定エリア使用率を確認するには、report.htmlファイルを参照してください。
廃止されたエリアレポートに関する参考情報は、Altera SDK for OpenCL Best Practices Guide version 16.0のReview Your Kernel's Area Report to Identify Inefficiencies in Resource Usageの章を参照ください。
OpenCLカーネルのプロファイリング
- パフォーマンス・カウンターを使用したカーネル・パイプラインの測定 (-profile)
パフォーマンス・カウンターでOpenCLカーネルのパイプラインを測定するには、aocコマンドの-profile=(all|autorun|enqueued)オプションをカーネルをコンパイルする際に含めます。 - インテル FPGA Dynamic Profiler for OpenCL GUIの起動 (report)
インテル® FPGA Dynamic Profiler for OpenCL™ のreportユーティリティー・コマンドを使用し、プロファイラーGUIを起動することができます。 - 自動実行カーネルのプロファイリング
自動実行カーネルのプロファイル機能は、自動実行カーネルのプロファイルを可能にします。
パフォーマンス・カウンターを使用したカーネル・パイプラインの測定 (-profile)
-
<your_kernel_filename>.aocxファイルのVerilogコードをパフォーマンス・カウンターで測定するには、
aoc
-profile=(all|autorun|enqueued)
<your_kernel_filename>.clコマンドを呼び出します。以下に詳細を説明します。
- all引数は、 <your_kernel_filename>.clファイルのすべてのカーネルをパフォーマンス・カウンターで測定します。これは引数が指定されていない場合のデフォルトのオプションです。
- autorun引数は、自動実行のカーネルのみをパフォーマンス・カウンターで測定します。
- enqueued引数は、自動実行以外のカーネルのみをパフォーマンス・カウンターで測定します。
重要:- 複数の異なるカーネルをプロファイリングする際は、異なる.aocxファイルに同じカーネル名を使用しないでください。同じカーネル名が付けられている場合、それらのカーネルのプロファイル・データは正確なものになりません。
- clGetProfileDataDeviceIntelFPGAホスト・ライブラリー呼び出しへの入力にかかわらず、 インテル® FPGA Dynamic Profiler for OpenCL™ は、コンパイル中に示したカーネルタイプのみをプロファイルします。
- すべてのカーネルまたはエンキューされたカーネルのOpenCLカーネル・パイプラインをパフォーマンス・カウンターで測定すると、OpenCLランタイム環境でのハードウェアのカーネル呼び出しキューの使用が無効になります。そのため、プロファイルの時間が異なる場合があります。
注意:自動実行カーネルをプロファイルすると、カウンターに対するハードウェアのオーバーヘッドが発生します。大規模なデザインの場合、オーバーヘッドによってfmaxおよびデザイン周波数が低下する可能性があります。また、 インテル® FPGA Dynamic Profiler for OpenCL™ がすべてのカーネルをプロファイルすると、デザインがチップに収まらなくなる可能性もあります。 -
ローカルディスクからホスト・アプリケーションを実行し、
<your_kernel_filename>.aocxファイルをFPGAで実行します。カーネルの実行中、カーネル・パイプライン全体のパフォーマンス・カウンターはプロファイル情報を収集します。ホストは現在の作業ディレクトリーのprofile.monモニター記述ファイルにその情報を保存します。
注意:ネットワーク・ディスクのアクセスが遅いため、ネットワークのディレクトリーからホスト・アプリケーションを実行すると、各カーネルの実行に遅れをもたらす可能性があります。この遅れにより、ホスト・アプリケーションの全体的な実行時間が長くなる可能性があります。さらに、ランタイムがプロファイル出力データをディスクに格納している間に、カーネルの起動に遅れが発生する可能性もあります。
インテル FPGA Dynamic Profiler for OpenCL GUIの起動 (report)
インテル® FPGA Dynamic Profiler for OpenCL™ は、パフォーマンス・データを現在の作業ディレクトリーのprofile.monファイルに格納します。
自動実行カーネルのプロファイリング
autorun属性でマークされたカーネルは、自動実行カーネルと呼ばれます。したがって自動実行カーネルは、ホストが明示的に他のカーネルを起動する前に自動的に開始され、完了すると自動的に再起動します。autorun属性についての詳細は、ホストとカーネル間の通信ハードウェアの省略を参照ください。
自動実行カーネルは終了することがないため、ホスト・ライブラリー呼び出しのclGetProfileDataDeviceIntelFPGAを呼び出し、自動実行のプロファイル・データをキャプチャーする必要があります。ホスト・アプリケーションに指示することで、この呼び出しは実行中の任意のタイミングで行うことができます。
インテル Code Builder for OpenCL を使用するOpenCLアプリケーションの開発
インテル® Code Builder for OpenCL™ は、OpenCLによって加速されたWindowsおよびLinuxアプリケーションの作成、構築、デバッグ、分析を可能にする一連のMicrosoft Visual StudioおよびEclipseのプラグインを提供します。
Microsoft Visual Studioに向けた インテル Code Builder for OpenCL オフライン・コンパイラー・プラグインのコンフィグレーション
Microsoft Visual Studioに向けてインテルCode Builder for OpenCLプラグインを有効にするには、次の手順を実行します。
- Visual Studioソフトウェアで、Project > Propertiesを選択します。
- Project > Properties > Code Builderのページで、Deviceを目的のFPGAデバイスに変更します。
- C/C++ > Generalプロパティー・ページのAdditional Include Directoriesに、OpenCLコードのヘッダーファイルが配置されているディレクトリーへのフルパスを入力します。 ($(INTELFPGAOCLSDKROOT)\include).
- Linker > Generalプロパティー・ページのAdditional Library Directoriesに、OpenCLコードのランタイム・インポート・ライブラリー・ファイルが配置されているディレクトリーへのフルパスを入力します。例えば64ビットのアプリケーションには、$(INTELFPGAOCLSDKROOT)\lib\x64を追加します。
- Linker > Inputプロパティー・ページ、Additional DependenciesのOpenCL ICDのインポート・ライブラリー・ファイル名にOpenCL.libを入力します。
Eclipseに向けた インテル Code Builder for OpenCL オフライン・コンパイラー・プラグインのコンフィグレーション
Eclipse IDEに向けてインテルCode Builder for OpenCLオフライン・コンパイラー・プラグインを有効にするには、次の手順を実行します。
-
CodeBuilder_<version>.jarプラグインファイルを$INTELFPGAOCLSDKROOT/eclipse-plug-inから
<ECLIPSE_ROOT_FOLDER>/dropinsへコピーします。
重要: Linuxでは、$INTELFPGAOCLSDKROOT\binをLD_LIBRARY_PATH環境変数に追加する必要があります。
- Eclipse IDEを実行します。
- Windows > Preferencesを選択します。
- インテル® OpenCLダイアログに切り替えます。
-
OpenCLバイナリーのディレクトリーを$INTELFPGAOCLSDKROOT/binに設定します。
オフライン・コンパイラーのコンフィグレーションが完了すると、Code-Builderメニューを使用し、次の基本的な操作を実行できます。
- 新しいセッションの作成
- 既存のセッションを開く
- セッションの保存
- セッションの構築
- セッションのコンパイル
- セッションのコンフィグレーション
インテル® Code Builder for OpenCL™ に関する詳細は、Developer Guide for Intel SDK for OpenCL Applicationsを参照ください。Microsoft Visual Studioに向けたインテルCode Builder for OpenCLのコンフィグレーション方法については、Intel Code Builder for OpenCL API for Microsoft Visual Studioを参照ください。Eclipseに向けたインテルCode Builder for OpenCLのコンフィグレーション方法については、Intel Code Builder for OpenCL API for Eclipseを参照ください。
インテル Code Builder for OpenCL でのセッションの作成
インテル® Code Builder for OpenCL™ でセッションを作成するには、次の手順を実行します。
- Code-Builder > OpenCL Kernel Development > New Sessionを選択します。
- セッション名、セッションファイルを格納するフォルダーへのパス、およびセッションのコンテンツを指定します (空のセッションまたは事前に定義されたOpenCLコードを持つセッションのどちらか)。
- Doneをクリックします。
セッションが作成されると、新しいセッションがCode Builder Sessions Explorer ビューに表示されます。

セッションのコンフィグレーション
Code Builder Session Explorerのセッションを右クリックしSession Optionsを選択すると、セッションをコンフィグレーションできます。あるいは、Code-Builder > OpenCL Kernel Development > Session Optionsを選択すると、Session Settingsダイアログボックスを開くことができます。
Session Settingsダイアログボックスでは、次の内容をコンフィグレーションすることができます。
- ターゲットマシン、OpenCLプラットフォーム、OpenCLデバイスなどのデバイスオプション
- オフライン・コンパイラー・フラグやビルド・アーキテクチャーなどのビルドオプション
- .aocxおよび.aocoファイルなどのビルド・アーティファクトや静的レポート
- ジョブ・アーキテクチャーやネットワーク設定などの一般的なオプション
Device OptionsタブのOpenCL platformドロップダウン・リストから、 インテル® FPGA SDK for OpenCL™ を選択していることを確認してください。
Build OptionsタブのOpenCL Build Optionsセクションに、 インテル® FPGA SDK for OpenCL™オフライン・コンパイラー・フラグを手動で入力します。
セッションのコンフィグレーションと変数管理については、Developer Guide for Intel SDK for OpenCL Applicationsを参照ください。
インテル FPGA SDK for OpenCL のアドバンスト機能
OpenCLライブラリー
以前に作成したライブラリーを使用することも、独自のライブラリーを作成することも可能です。OpenCLライブラリーの使用にあたり、ハードウェア・デザインやライブラリー・コンポーネントの実装に関する詳細な知識は必要ありません。OpenCLライブラリーを作成するには、次のファイルとコンポーネントを作成する必要があります。
ファイルまたはコンポーネント | 説明 |
---|---|
RTLコンポーネント | |
RTLソースファイル | RTLコンポーネントを定義するVerilog、System Verilog、またはVHDLファイルです。 インテル® Quartus® Prime IPファイル (.qip)、SDCファイル (.sdc)、Tcl Scriptファイル (.tcl) などの付加的なファイルは使用することができません。 |
eXtensible Markup Languageファイル (.xml) | RTLコンポーネントのプロパティーを記述します。 インテル® FPGA SDK for OpenCL™オフライン・コンパイラーは、これらのプロパティーを使用しRTLコンポーネントをOpenCLパイプラインに統合します。 |
ヘッダーファイル (.h) | RTLコンポーネントにより実装される関数のシグニチャーを宣言する、Cスタイルのヘッダーファイルです。 |
OpenCLエミュレーション・モデル・ファイル (.cl) | エミュレーションでのみ使用されるRTLコンポーネントのCモデルを提供します。ハードウェアのフルコンパイルにはRTLソースファイルを使用します。 |
OpenCLの機能 | |
OpenCLソースファイル (.cl) | OpenCL関数の定義を含んでいます。これらの関数は、エミュレーションおよびハードウェアのフルコンパイルの際に使用されます。 |
ヘッダーファイル (.h) | OpenCLソースファイルで定義される関数のシグネチャーを宣言するCスタイルのヘッダーファイルです。 |
- RTLモジュールとOpenCLパイプラインの理解
この章では、 インテル® FPGA SDK for OpenCL™オフライン・コンパイラーが、RTLモジュールをインテル FPGA SDK for OpenCLパイプライン・アーキテクチャーに統合する方法の概要を説明します。 - OpenCLライブラリーに向けたOpenCLヘルパー関数ファイルのパッケージ化
OpenCL™ライブラリー・ファイルを作成する前に、ヘルパー関数を持つ各OpenCLソースファイルを.aocoファイルにパッケージ化します。 - OpenCLライブラリーに向けたRTLコンポーネントのパッケージ化
OpenCL™ライブラリー・ファイルを作成する前に、各RTLコンポーネントを.aocoファイルにパッケージ化します。 - RTLモジュールの検証
OpenCL™ライブラリーの作成者は、ライブラリーのRTLモジュールを独立したエンティティーとして、またOpenCLシステムの一部として検証する責任があります。 - 複数のオブジェクト・ファイルのライブラリー・ファイルへのパッケージ化
OpenCL™ライブラリーに含める.aocoファイルを作成後、 インテル® FPGA SDK for OpenCL™ libraryユーティリティー・コマンド・オプションを呼び出し、それらをライブラリー・ファイルにパッケージ化します。 - OpenCLカーネルコンパイル時のOpenCLライブラリーの指定
OpenCLカーネルでOpenCL™ライブラリーを使用するには、カーネルをコンパイルする際にライブラリー・ファイル名とディレクトリーを指定します。 - シミュレーションによるOpenCLライブラリーのデバッグ (プレビュー)
インテル® FPGA SDK for OpenCL™ シミュレーターは、 OpenCL* ライブラリーの機能性を評価します。 - シンプルな関数で動作するOpenCLライブラリーの使用 (例1)
インテルでは、倍精度関数を3つ実装した (sqrt、rsqrt、divide) RTLを含むライブラリーを使用する、シンプルなカーネルのOpenCL™ライブラリー・デザイン例を提供しています。 - 外部メモリーと動作するOpenCLライブラリーの使用 (例2)
インテルでは、グローバルメモリーと通信する2つのRTLモジュールを含むライブラリーを使用する、シンプルなカーネルのOpenCL™ライブラリー・デザイン例を提供しています。 - OpenCLライブラリーのコマンドライン・オプション
インテル® FPGA SDK for OpenCL™オフライン・コンパイラーの一連のコマンドおよび、SDKユーティリティーは、OpenCLライブラリーに関連するタスクを実行するために呼び出すことができるオプションを含んでいます。
RTLモジュールとOpenCLパイプラインの理解
次の状況においてRTLモジュールを使用してください。
- 最適化および検証がされたRTLモジュールを、OpenCL関数として書き直すことなくOpenCLカーネルで使用する場合
- OpenCLで効果的に表現できないOpenCLカーネル機能を実装する必要がある場合
インテルFPGA SDK for OpenCLパイプラインのアプローチの概要
各レベルの動作が、パイプラインの1ステージであると仮定します。各ステージにおいてインテル FPGA SDK for OpenCLは、そのステージに存在するスレッドに基づきすべての動作を並列で実行します。例えばスレッド2ではLoad A、Load Bを実行し、現在のグローバルIDを (gidを介し) 次のパイプライン・ステージにコピーします。RISC (縮小命令セット・コンピューター) プロセッサーでのパイプライン化された命令の実行と同様、SDKのパイプライン・ステージもまた、並列して実行を行います。ステージの実行がすべて完了しなければ、スレッドは次のパイプライン・ステージに進みません。
一部の動作では、インテルFPGA SDK for OpenCL パイプラインをストールさせることができます。例えば、メモリーのロードやストア動作のような可変レイテンシー動作などがそれにあたります。ストールをサポートするには、ready信号とvalid信号をパイプライン全体に伝播し、オフライン・コンパイラーがパイプライン・ステージをスケジュールできるようにします。ただし、すべての動作のレイテンシーが固定されている場合、ready信号は必要ありません。このような場合オフライン・コンパイラーはパイプラインを最適化し静的に動作をスケジュールするため、パイプラインの実装に必要なロジックが大幅に削減されます。
RTLモジュールのインテル FPGA SDK for OpenCLパイプラインへの統合
示されているRTLモジュールはバランスの取れたレイテンシーを有し、RTLモジュールのスレッドはパイプラインのステージ数と一致しています。レイテンシーのバランスが取れているため、RTLモジュールのスレッドはSDKのパイプラインをストールさせることなく実行することができます。
RTLの仕様ファイルでRTLモジュールのレイテンシーを設定すると、オフライン・コンパイラーはパイプラインのレイテンシーのバランスを取ることができるようになります。 RTLは、 Avalon™ Streaming (Avalon™ -ST) インターフェイスをサポートしているため、RTLモジュールのレイテンシシーは可変です (つまり固定ではありません)。ただし、性能を最大限に上げるためには、レイテンシーの変動は小さくなければなりません。さらにレイテンシーを <RTL module description file name>.xml仕様ファイルで指定すると、RTLモジュールは安定状態で実際のレイテンシーの適切な概算を得られるようになります。
ストールのないRTL
- 可能な場合にRTLモジュール周囲のストールロジックを削除するようオフライン・コンパイラーに指示するには、FUNCTION要素のIS_STALL_FREE属性を"yes"に設定します。この変更は、RTLモジュールがEXPECTED_LATENCYサイクルごとに有効なデータを生成することをオフライン・コンパイラーに通知します。注: EXPECTED_LATENCYは、FUNCTION要素の.xmlファイルで指定する属性です。
- レイテンシーがモジュールのパイプライン・ステージ数に等しくなるようEXPECTED_LATENCYの値を指定します。注意:不正確なEXPECTED_LATENCYの値を指定すると、RTLモジュールがパイプラインの他の部分と同期しなくなります。
ストールのないRTLモジュールは、無効な (ivalidがLowの) 入力信号を受け取る可能性があります。この場合モジュールは入力を無視し、出力に無効なデータを生成します。ストールのないRTLモジュールに内部状態がない場合、モジュールを通して無効な入力を伝播するほうが容易な場合があります。ただし内部状態を持つRTLモジュールでは、ivalid=0入力の処理に注意する必要があります。
RTLモジュール・インターフェイス
RTLモジュールが、他のコンパイラーによって生成された動作と適切に通信するには、RTLモジュールの入力および出力の両方において、単純化されたAvalon-STインターフェイスをサポートする必要があります。
次の図は、図 19 で示されているRTLモジュールmyModの最終的なインターフェイスを表しています。
この図においてmyModは、A、Bのデータ信号やivalid (入力) およびoready (出力) のコントロール信号を介し、アップストリームのモジュールと通信しています。ivalidコントロール信号は、データ信号Aおよびデータ信号Bが有効なデータを含む場合のみ、1に等しくなります (ivalid = 1)。コントロール信号oreadyが1に等しくなると (oready = 1)、データ信号AとBが有効であれば (ivalid = 1)、myMod RTLモジュールはそれらの信号の処理を行うことができることを示します。ivalid = 1でoready = 0の場合、アップストリームのモジュールは、次のクロックサイクルでivalid、AおよびBの値を保持すると考えられます。
myModは、データ信号Cおよびコントロール信号のovalid (出力) とiready (入力) を介しダウンストリームのモジュールと通信します。ovalidコントロール信号は、データ信号Cが有効なデータを含む場合にのみ、1に等しくなります (ovalid = 1)。ireadyコントロール信号が1に等しくなると (ivalid = 1)、データ信号Cが有効であればダウンストリームのモジュールは処理が可能なことを示します。ovalid = 1でiready = 0の場合、myMod RLTモジュールは次のクロックサイクルでovalidとC信号の有効性を保持すると考えられます。
myModモジュールは、oreadyを1クロックサイクル間アサートし、アクティブサイクルに向け準備が整っていることを示します。myModモジュールのデータの準備ができているサイクルを、レディーサイクルと呼びます。レディーサイクル中、myModモジュールの上にあるモジュールは、ivalidをアサートしmyModにデータを送信することができます。
バックプレッシャー下でのデータ転送に関する詳細な説明は、Avalon Interface SpecificationsのData Transfer with Backpressureを参照ください。レディー・レイテンシーに関する情報は参考にしないでください。
Avalon Streaming (Avalon-ST) インターフェイス
オフライン・コンパイラーは、入力と出力の両方においてRTLモジュールが、readyLatency = 0のAvalon-STインターフェイスをサポートすることを想定しています。
- Avalon-STインターフェイス入力のivalidとiready
- Avalon-STインターフェイス出力のovalidとoready

固定レイテンシーを持つRTLモジュールの場合、出力信号 (ovalidとoready) は一定の高い値を持つことができ、入力レディー信号 (iready) は無視することができます。
ストールのないRTLモジュールは、無効な (ivalidがLow の) 入力信号を受け取る可能性があります。この場合モジュールは入力を無視し、出力に無効なデータを生成します。ストールのないRTLモジュールに内部状態がない場合、モジュールを介し無効な入力を伝播するほうが容易な場合があります。ただし、内部状態を持つRTLモジュールにおいては、ivalid = 0入力の処理に注意する必要があります。
RTLリセットおよびクロック信号
共通のクロックドライバーとリセットドライバーであるため、RTLモジュールはOpenCLカーネルと同じクロックドメインで動作します。モジュールは、 インテル® FPGA SDK for OpenCL™ のprogramユーティリティーまたはclCreateProgramwithBinaryホスト関数で、OpenCLカーネルが最初にFPGAにロードされた際にのみリセットされます。つまり、ホストがカーネルを連続するclEnqueueNDRangeKernelまたはclEnqueueTask呼び出しで再起動した場合、関連するRTLモジュールはこの再起動時にリセットされません。
カーネルのクロック周波数を設定する過程を次に説明します。
- インテル® Quartus® Primeソフトウェアのフィッターは、カーネルクロックに積極的な制約を適用します。
- インテル® Quartus® Primeソフトウェアのタイミング・アナライザーは、静的なタイミング解析を実行し、フィッターが実際に達成する周波数を決定します。
- カーネルのクロックを駆動するフェーズ・ロック・ループ (PLL) は、上記2で決定された周波数をカーネルのクロック周波数に設定します。
オプションによりRTLモジュールは、OpenCL™カーネルクロックの2倍の周波数で動作するシステム全体のクロックにアクセスできます。このシステム全体のクロックをRTLモジュールの入力信号に接続するには、AVALON要素のタイプclock2xを含めます。この2つのクロックは、定義された位相関係を持ちません。
ストールのないRTLモジュールおよびストールをサポートするRTLモジュールの インテル Stratix 10デザイン固有のリセット要件
ストールのないRTLモジュールのリセット要件
ストールのないRTLモジュールはレイテンシーが固定されたモジュールであり、 インテル® FPGA SDK for OpenCL™オフライン・コンパイラーは、ストールロジックを削除し最適化することができます。
- インテル® Stratix® 10のデザインに向けストールのないRTLモジュールを作成する際は、同期クリアー信号のみを使用してください。
- ストールのないRTLモジュールに対するリセットシグナルのディアサート後、モジュールは15クロックサイクル以内に動作可能でなければなりません。リセット信号がモジュール内でパイプライン化されている場合、この要件はリセットのパイプライン化を15ステージ以下に制限します。
ストールをサポートするRTLモジュールのリセット要件
ストールをサポートするRTLモジュールは可変レイテンシーを持ち、正確に機能するためにバックプレッシャー入力および出力インターフェイスに依存します。
- インテル® Stratix® 10デザインにストールをサポートするRTLモジュールを作成する際は、同期クリアー信号のみを使用してください。
- ストールをサポートするRTLモジュールにリセット信号をアサートした後、モジュールはoreadyおよびovalidインターフェイス信号を40クロックサイクル内にディアサートしなければなりません。
- ストールをサポートするRTLモジュールにリセット信号をディアサートした後、モジュールは40クロックサイクル以内に完全に動作可能になる必要があります。モジュールは、準備が完了したことをoreadyインターフェイス信号をアサートし知らせます。
RTLモジュールのXML構文
次のXML仕様ファイルは、OpenCL™のヘルパー関数my_sqrtfdを実装するRTLモジュールmy_fp_sqrt_doubleのものです (どちらの名前もライン2にあります)。
1: <RTL_SPEC> 2: <FUNCTION name="my_sqrtfd" module="my_fp_sqrt_double"> 3: <ATTRIBUTES> 4: <IS_STALL_FREE value="yes"/> 5: <IS_FIXED_LATENCY value="yes"/> 6: <EXPECTED_LATENCY value="31"/> 7: <CAPACITY value="1"/> 8: <HAS_SIDE_EFFECTS value="no"/> 9: <ALLOW_MERGING value="yes"/> 10: </ATTRIBUTES> 11: <INTERFACE> 12: <AVALON port="clock" type="clock"/> 13: <AVALON port="resetn" type="resetn"/> 14: <AVALON port="ivalid" type="ivalid"/> 15: <AVALON port="iready" type="iready"/> 16: <AVALON port="ovalid" type="ovalid"/> 17: <AVALON port="oready" type="oready"/> 18: <INPUT port="datain" width="64"/> 19: <OUTPUT port="dataout" width="64"/> 20: </INTERFACE> 21: <C_MODEL> 22: <FILE name="c_model.cl" /> 23: </C_MODEL> 24: <REQUIREMENTS> 25: <FILE name="my_fp_sqrt_double_s5.v" /> 26: <FILE name="fp_sqrt_double_s5.vhd" /> 27: </REQUIREMENTS> 28: <RESOURCES> 29: <ALUTS value="2057"/> 30: <FFS value="3098"/> 31: <RAMS value="15"/> 32: <MLABS value="43"/> 33: <DSPS value="1.5"/> 34: </RESOURCES> 35: </FUNCTION> 36: </RTL_SPEC>
XML要素 | 説明 |
---|---|
RTL_SPEC | XML仕様ファイルにおける最上位の要素です。このような最上位の要素はファイルに1つしか存在できません。 |
FUNCTION |
RTLモジュールが実装するOpenCL関数を定義する要素です。FUNCTION要素のname属性は、関数の名前を指定します。 OpenCLカーネルから呼び出すことのできる、異なる関数を宣言する複数のFUNCTION要素を持つことが可能です。異なるパラメーターを指定することで、同じRTLモジュールに複数の関数を実装できます。 |
ATTRIBUTES | RTLモジュールのさまざまな特性 (レイテンシーなど) を記述する他のXML要素を含む要素です。例にあるRTLモジュールは、32の値を持つWIDTHというPARAMETER設定を1つ取ります。その他のATTRIBUTES固有の要素に関しては、表 6 を参照ください。 注: 異なるモジュールに複数のOpenCLヘルパー関数を作成する場合や、異なるPARAMETER設定で同じRTLモジュールを使用する場合は、関数ごとに個別のFUNCTION要素を作成する必要があります。
|
INTERFACE | RTLモジュールのインターフェイスを記述する他のXML要素を含む要素です。例にあるXML仕様ファイルでは、すべてのRTLモジュールが提供する必要のあるAvalon®-STインターフェイス信号 (clock、resetn、ivalid、iready、ovalid、oready) を示しています。この信号名は.xmlファイルで指定する名前と一致している必要があります。信号名が一致しない場合、ライブラリー作成時にエラーが発生します。 |
C_MODEL | 関数にOpenCL Cモデルを実装する1つ以上のファイルを指定する要素です。モデルはエミュレーション時にのみ使用されます。ただし、C_MODEL要素と関連するファイルは、ライブラリー・ファイル作成時に存在している必要があります。 |
REQUIREMENTS | 1つ以上のRTLリソースファイル (.v、.sv、.vhd、.hex、.mif) を指定する要素です。これらのファイルに指定されたパスは、XML仕様ファイルの位置に対応します。各RTLリソースファイルは、OpenCLシステム全体に対応する、関連するプラットフォーム・デザイナー・コンポーネントの一部になります。 注:
インテル® FPGA SDK for OpenCL™
ライブラリー機能は、.qipファイルをサポートしません。サポートされていないリソース・ファイル・タイプを含むライブラリーを使用しOpenCLカーネルをコンパイルすると、
インテル® FPGA SDK for OpenCL™オフライン・コンパイラーのエラーが発生します。
|
RESOURCES | RTLモジュールが使用するFPGAリソースを指定するオプションの要素です。この要素を指定しない場合、RTLモジュールが使用するFPGAリソースはデフォルトのゼロになります。 |
ATTRIBUTESを指定するXML要素
XML要素 | 説明 |
---|---|
IS_STALL_FREE |
インテル® FPGA SDK for OpenCL™オフライン・コンパイラーに、RTLモジュール周囲のすべてのストールロジックを削除するように指示します。 IS_STALL_FREEを"yes"に設定し、モジュールでストールを生成しないこと、および受信ストールの適切な処理ができないことを示します。モジュールはストールの入力を単純に無視します。IS_STALL_FREEを"no"に設定する場合、モジュールはストールおよび有効な信号をすべて適切に処理する必要があります。 注:
IS_STALL_FREEを"yes"に設定する際は、IS_FIXED_LATENCYも"yes"に設定する必要があります。また、RTLモジュールが内部状態がを持つ場合、ivalid=0入力を適切に処理する必要があります。
不正なIS_STALL_FREEを設定すると、ハードウェに不正確な結果をもたらします。 |
IS_FIXED_LATENCY |
RTLモジュールのレイテンシーが固定されているかどうかを示します。 RTLモジュールが常に既知のクロックサイクル数で出力を計算する場合は、IS_FIXED_LATENCYを"yes”に設定します。EXPECTED_LATENCY要素に指定する値は、クロックサイクル数を指定します。 IS_FIXED_LATENCYの安全な値は"no”です。IS_FIXED_LATENCY="no"を設定する際に、EXPECTED_LATENCYの値は少なくとも1でなければなりません。 注: 特定のモジュールでは、IS_FIXED_LATENCYを"yes"にし、IS_STALL_FREEを"no"に設定することができます。このようなモジュールは、固定のクロックサイクル数で出力を生成し、ストール信号を適切に処理します。
|
EXPECTED_LATENCY |
RTLモジュールの予想されるレイテンシーを指定します。 IS_FIXED_LATENCYを"yes"に設定すると、EXPECTED_LATENCYの値は、モジュールにあるパイプラインのステージ数を示します。この場合、この値はモジュールの正確なレイテンシーに設定する必要があります。正確に設定されていない場合、オフライン・コンパイラーは不正確なハードウェアを生成します。 レイテンシーが可変のモジュールにおいては、オフライン・コンパイラーはこのモジュール周辺のパイプラインを、指定したEXPECTED_LATENCYの値でバランスを取ります。ストールをサポートし、ireadyなどの信号を必要とするモジュールにおいては、EXPECTED_LATENCYの値は最低でも1に設定する必要があります。指定した値と実際のレイテンシーは異なることがあり、パイプラインのストール数に影響を与える場合があります。 ただし、生成されるハードウェアは正しいものです。 |
CAPACITY |
このモジュールが同時に処理できる複数の入力の数を指定します。IS_STALL_FREE="no"およびIS_FIXED_LATENCY="no"も設定する場合は、CAPACITYの値を指定する必要があります。それ以外の場合はCAPACITYの値を指定する必要はありません。 CAPACITYがEXPECTED_LATENCYよりもわずかでも小さい場合、オフライン・コンパイラーは必要に応じて、容量のバランスをとるFIFOバッファーをこのモジュールの後に自動的に挿入します。 CAPACITYの安全な値は1です。 |
HAS_SIDE_EFFECTS | RTLモジュールに副作用があるかどうかを示します。内部状態のあるモジュールや外部メモリーと通信するモジュールは、副作用をともなうモジュールの例になります。 HAS_SIDE_EFFECTSを"yes"に設定し、モジュールに副作用があることを示します。HAS_SIDE_EFFECTSを"yes"に指定すると、最適化を行う際に副作用のあるモジュールへの呼び出しは削除されません。 副作用があり、ストールのないモジュール (IS_STALL_FREE="yes"でありHAS_SIDE_EFFECTS="yes"のモジュール) は、無効なデータを受け取る可能性があるため、ivalid=0の入力を適切に処理する必要があります。 HAS_SIDE_EFFECTSの安全な値は"yes"です。 |
ALLOW_MERGING | オフライン・コンパイラーに、RTLモジュールの複数のインスタンスをマージするよう指示します。 ALLOW_MERGINGを"yes"に設定し、モジュールの複数のインスタンスのマージを可能にします。インテルでは、ALLOW_MERGINGを"yes"に設定することを推奨しています。 ALLOW_MERGINGの安全な値は、"no"です。 注:
HAS_SIDE_EFFECTS="yes"でモジュールをマークしてもマージを防ぐことはできません。
|
PARAMETER |
RTLモジュールのパラメーター値を指定します。 PARAMETERの属性
注: RTLモジュールのパラメーター値は、value属性またはtype属性を使用し指定できます。
|
INTERFACEを指定するXML要素
XML要素 | 説明 |
---|---|
INPUT |
RTLモジュールの入力パラメーターを指定します。 INPUT属性
入力パラメーターは連結されて入力ストリームを形成します。 構造体や配列などの集約データ構造は、入力パラメーターとしてサポートされていません。 |
出力 |
RTLモジュールの出力パラメーターを指定します。 OUTPUT属性
入力ストリームからの戻り値は、出力ストリームの出力パラメーターを介し送信されます。 構造体や配列などの集約データ構造は、入力パラメーターとしてサポートされていません。 |
RTLモジュールが外部メモリーと通信する場合は、XML要素を追加し含める必要があります。
<MEM_INPUT port="m_input_A" access="readonly"/> <MEM_INPUT port="m_input_sum" access ="readwrite"/> <AVALON_MEM port="avm_port0" width="512" burstwidth="5" optype="read" buffer_location=""/>
XML要素 | 説明 |
---|---|
MEM_INPUT |
RTLモジュールへのポインター入力を記述します。 MEM_INPUT属性
外部メモリーへのポインターはすべて64ビットである必要があるため、MEM_INPUTに関連付けられるwidth属性はありません。 |
AVALON_MEM |
RTLモジュールのAvalon-MMインターフェイスを宣言します。 AVALON_MEM属性
|
上のコード例で定義されたAVALON_MEM要素に対応するRTLモジュールポートは次のようになります。
output avm_port0_enable, input [511:0] avm_port0_readdata, input avm_port0_readdatavalid, input avm_port0_waitrequest, output [31:0] avm_port0_address, output avm_port0_read, output avm_port0_write, input avm_port0_writeack, output [511:0] avm_port0_writedata, output [63:0] avm_port0_byteenable, output [4:0] avm_port0_burstcount,
MEM_INPUTで指定するポインターとAVALON_MEMで指定するAvalon-MMインターフェイス間に、想定の関係はありません。RTLモジュールは1つのポインターを使用し、0から複数のAvalon-MMインターフェイスのアドレスを指定することができます。
リソースを指定するXML要素
XML要素 | 説明 |
---|---|
ALUTS | モジュールが使用する、組み合わせ対応可能なルックアップ・テーブル (ALUT) の数を指定します。 |
FFS | モジュールが使用する専用のロジックレジスター数を指定します。 |
RAMS | モジュールが使用するブロックRAMの数を指定します。 |
DSPS | モジュールが使用するデジタル信号処理 (DSP) ブロックの数を指定します。 |
MLABS | モジュールが使用するメモリー・ロジック・アレイ (MLAB) の数を指定します。各MLABは10のALMを消費するため、この値はメモリーに使用されるALM (アダプティブ・ロジック・モード) の数を10で割った値に等しくなります。 |
RTLモジュールと外部メモリーの通信
RTLモジュールと外部メモリーの通信は、通信が必要かつ避けられない場合にのみ行うようにしてください。
次の例は、OpenCLライブラリーへの容易な統合を行うためのRTLモジュールのコード構築方法を示しています。
複雑なRTLモジュール | 簡潔化されたRTLモジュール |
---|---|
// my_rtl_fn does: // out_ptr[idx] = fn(in_ptr[idx]) my_rtl_fn (in_ptr, out_ptr,idx); |
int in_value = in_ptr[idx]; // my_rtl_fn now does: out = fn(in) int out_value = my_rtl_fn (in_value); out_ptr[idx] = out_value; |
左側の複雑なRTLモジュールは、外部メモリーから値を読み出し、その値にスカラー関数を実行し値をグローバルメモリーに書き戻します。このようなRTLモジュールは、OpenCLライブラリーに統合する際の記述が困難です。さらに、このRTLモジュールの検証は難しく、 インテル® FPGA SDK for OpenCL™オフライン・コンパイラーにおいて非常に控えめなポインター分析が行われます。
右側の簡潔化されたRTLモジュールは、複雑なRTLモジュールと全体的には同じ機能を提供します。ただし、簡潔化されたRTLモジュールは、グローバルメモリーに接続せずにスカラーからスカラーの計算のみを行います。この簡潔なRTLモジュールをOpenCLライブラリーに統合することで、オフライン・コンパイラーは作成されるOpenCLカーネルをより容易に分析できるようになります。
RTLモジュールは、外部メモリーと通信するためのAvalon®-MMポートを必要とする場合があります。このAvalon-MMポートは、OpenCLカーネルの他のグローバルロードおよびストアユニットの接続先と同じアービトレーション・ネットワークに接続します。
RTLモジュールがメモリーポインターを引数として受け取ると、オフライン・コンパイラーは次のメモリーモデルを強制します。
- RTLモジュールがポインターへ書き込む場合、OpenCLカーネルの他の部分はこのポインターに対する読み書きはできません。
- RTLモジュールがポインターから読み取る場合、OpenCLカーネルの他の部分および他のRTLモジュールもまた、このポインターから読み取ることができます。
- MEM_INPUT属性のaccessフィールドを設定することで、RTLモジュールのメモリーポインターの使用方法を指定できます。値を検証する方法がないため、アクセスを行うための値が正しく設定されていることを確認してください。
RTLモジュールに入るスレッドの順序
RTLモジュールのOpenCL Cモデル
平方根関数のOpenCL Cモデルファイル例
double my_sqrtfd (double a) { return sqrt(a); }
インテルでは、OpenCLシステムをエミュレーションすることを推奨しています。OpenCLシステムをエミュレーションしない場合は、XML仕様ファイルで指定した関数名と一致する名前の空の関数を作成してください。
RTLモジュールとパーシャル・リコンフィグレーションにおける潜在的な非互換性
パーシャル・リコンフィグレーション (PR) をサポートしていないデバイスでライブラリーを作成し検証した後に、ライブラリーのユーザーが、ライブラリーのRTLモジュールをPR領域で使用すると、PR後にモジュールが正しく機能しない可能性があります。
- RTLモジュールは、初期化されたコンテンツのメモリー・ロジック・アレイ・ブロック (MLAB) を使用していないこと。
- RTLモジュールは、ロジックのパワーアップ値に関していかなる仮定もしていないこと。
PRコーディングの完全なガイドラインに関しては、インテル Quartus Prime プロ・エディション ユーザーガイド:パーシャル・リコンフィグレーションの、パーシャル・リコンフィグレーション・デザインの作成を参照ください。
OpenCLライブラリーに向けたOpenCLヘルパー関数ファイルのパッケージ化
OpenCLで書かれたヘルパー関数を共有するためのライブラリーは通常作成する必要はありません。例えば <shared_file>.clなどのソース形式でヘルパー関数を配布し、#include "<shared_file>.cl"行をOpenCLカーネルのソースコードに挿入することができます。
次のような状況において、ライブラリーを作成することを検討ください。
- ヘルパー関数が複数のファイルにあり、配布を簡素化する必要がある。
- ヘルパー関数のソースコードの公開を望まない。
ヘルパー関数は、アセンブリー言語のようなLLVM IRとして、関連するライブラリーにコメントなしで格納されます。
.aocoファイルの作成にハードウェアの生成は必要ありません。-cオフライン・コンパイラー・コマンド・オプションを使用し、OpenCLソースファイルをコンパイルします。
OpenCLライブラリーに向けたRTLコンポーネントのパッケージ化
.aocoファイルの作成においてハードウェアの生成は必要ありません。-c インテル® FPGA SDK for OpenCL™オフライン・コンパイラー・コマンド・オプションを使用し、OpenCLソースファイルをコンパイルします。
インテル FPGA SDK for OpenCL ライブラリー機能に対するRTLサポートの制約と制限
RTLモジュールを作成する際は、次の制約内で動作させることを確認してください。
- RTLモジュールは、単一入力のAvalon®-STインターフェイスを使用する必要があります。つまり、readyとvalidロジックの単一ペアが、すべての入力を制御しなければなりません。
必要なAvalon-STポートを提供し、RTLモジュールをストールのないモジュールとして宣言するオプションもあります。この場合、 インテル® FPGA SDK for OpenCL™オフライン・コンパイラーはモジュールに向けてラッパーを作成するため、適切なストール動作を実装する必要はありません。構文と使用法に関する詳細は、RTLモジュールのXML構文、およびシンプルな関数で動作するOpenCLライブラリーの使用 (例1) を参照ください。
注: RTLモジュールが内部状態を持つ場合は、ivalid信号を適切に処理する必要があります。詳しくは、ストールのないRTLを参照ください。 - RTLモジュールは、カーネルのクロック周波数にかかわらず正確に動作する必要があります。
- データの入力および出力サイズは、charの8ビットからlong16の1024ビットまでの有効なOpenCLデータ型と一致する必要があります。
例えばRTLモジュールで24ビットの値を使用する場合、入力が32ビットになるよう宣言し、SDKライブラリーのヘッダー・ファイルで関数シグニチャーを宣言しuintデータ型を受け取るようにします。その後、RTLモジュールで32ビットの入力を受け入れ、上位8ビットを破棄します。
- RTLモジュールは、外部I/O信号に接続することができません。入出力信号はすべて、OpenCL カーネルから発行される必要があります。
- RTLモジュールには、clockポート、resetnポート、Avalon-ST入力および出力ポート (ivalid、ovalid、iready、oready) が必要です。ここで指定されている通りに名前を付けてください。
- 外部メモリーと通信する RTLモジュールには、対応するカスタム・プラットフォーム・パラメーターに一致するAvalonメモリーマップド (Avalon-MM) ポートのパラメーターが必要です。オフライン・コンパイラーは、幅またはバーストの調整を行いません。
- 外部メモリーと通信するRTLモジュールは、次のように動作する必要があります。
- バースト境界を越えてバーストしないこと。
- クロックサイクルごとに要求を行い、アービトレーション・ロジックを独占しハードウェアをストールさせないこと。RTLモジュールは定期的に要求を一時停止し、他のロードまたはストアユニットが動作を実行できるようにする必要があります。
- RTLモジュールは、独立したOpenCLカーネルとして動作することはできません。RTLモジュールはヘルパー関数にしかなれず、カーネルのコンパイル時にOpenCLカーネルに統合されます。
- RTLモジュールのインスタンス化に対応する関数呼び出しはすべて、他のインスタンス化から完全に独立しています。ハードウェアの共有はありません。
- カーネルコード (kernelとしてマークされた関数) は、.aoclibライブラリー・ファイルに組み込まないでください。カーネルコードをライブラリー・ファイルに組み込むと、オフライン・コンパイラーはエラーメッセージを発行します。ヘルパー関数をライブラリー・ファイルに組み込むことは可能です。
- RTLコンポーネントは、すべての入力を同時に受け取る必要があります。単一のivalid入力は、すべての入力が有効なデータを含んでいることを意味します。
- SDKはI/O RTLモジュールをサポートしません。
- RTLモジュールのパラメーターは、OpenCLカーネル・ソース・ファイルではなく、 <RTL module description file name>.xml仕様ファイルでのみ設定できます。同じRTLモジュールを複数のパラメーターで使用するには、個別のFUNCTIONタグを各パラメーターの組み合わせに作成します。
SDKのRTLモジュールのライブラリー機能に対するサポートには現在、次の制限があります。
- RTLモジュールへのデータ入力は、OpenCLカーネルコードを介した値でのみ渡すことができます。参照渡し、構造体、またはチャネルを介しRTLモジュールにデータ入力を渡さないでください。チャネルデータの場合は、まずチャネルからデータを抽出し、抽出したスカラーデータを RTLモジュールに渡します。注: 参照渡しまたは構造体によってデータ入力をRTLモジュールに渡すと、オフライン・コンパイラーで致命的なエラーが発生します。
- デバッガー (LinuxのGDB、など) は、エミュレーション中にライブラリー関数にステップインできません。さらに、最適化レポートおよびエリアレポートは、ライブラリー関数の横にコード行番号を含みません。
- RTLモジュールのソースファイル名は、 インテル® FPGA SDK for OpenCL™オフライン・コンパイラー IPファイル名と競合することはできません。RTLモジュールのソースファイルとオフライン・コンパイラーのIPファイルはどちらも、 <kernel file name>/system/synthesis/submodulesディレクトリーに格納されます。名前が競合すると、ディレクトリーにある既存のオフライン・コンパイラーIPファイルが、RTLモジュールのソースファイルに上書きされます。
- SDKは.qipファイルをサポートしません。ネスト化された.qipファイルを手動で解析し、RTLファイルのフラットリストを作成する必要があります。
- 単独では正しく動作する一方で、OpenCLカーネルの一部としては正しく動作しない RTLモジュールをデバッグすることは非常に困難です。 <RTL module description file name>.xmlファイルのATTRIBUTES要素にあるパラメーターをすべて再度確認してください。
- オフライン・コンパイラーのエリア見積りツールはすべて、RTLモジュールのエリアが0であると仮定しています。SDKは現在、RTLモジュールのエリアモデルを指定する機能をサポートしていません。
- RTLモジュールは、カーネルクロックと同位相でありカーネルクロック周波数の2倍の、2xクロックにアクセスできません。
RTLモジュールの検証
- 各RTLモジュールを、標準のハードウェア検証方法にて検証します。
-
インテル® FPGA SDK for OpenCL™
ライブラリー・デザイン例の1つを変更し、OpenCLシステム全体においてRTLモジュールをテストします。
この手順は、ライブラリーのユーザーがハードウェアの問題に直面しないようにするために重要です。
XML仕様ファイルのATTRIBUTES要素の値を正しく設定していることが重要です。OpenCLシステム全体をシミュレーションすることはできないため、インターフェイス・レベルのエラーによって発生する問題は、ハードウェアを動作させるまで検出されない可能性があります。
-
注: インテル® FPGA SDK for OpenCL™ libraryユーティリティーは、XML仕様ファイルとソースファイルの整合性を確認しますが、それにはいくつかの制限があります。aocl library [<command option>]コマンドを呼び出します。
- サポートされている <command options> リストは、 aocl library コマンドで呼び出します。
- libraryユーティリティーは、XML仕様ファイルのATTRIBUTES、MEM_INPUT、AVALON_MEM要素内の要素に割り当てられている値のエラーを検出しません。
- library ユーティリティーは、RTL構文のエラーを検出しません。RTL構文のエラーについては、 <your_kernel_filename>/quartus_sh_compile.logファイルを確認する必要があります。ただし、エラーの解析には時間を要する可能性があります。
複数のオブジェクト・ファイルのライブラリー・ファイルへのパッケージ化
OpenCLカーネルコンパイル時のOpenCLライブラリーの指定
-l <library file name> および-L <library directory> の複数のインスタンスを、オフライン・コンパイラー・コマンドに含めることができます。
例えば、関数my_div_fd()、my_sqrtfd()、myrsqrtfd()を含むライブラリーを作成する場合、OpenCLのカーネルコードは次のようになります。
#include “lib_header.h” kernel void test_lib ( global double * restrict in, global double * restrict out, int N) { int i = get_global_id(0); for (int k =0; k < N; k++) { double x = in[i*N + k]; out[i*N + k] = my_divfd (my_rsqrtfd(x), my_sqrtfd(my_rsqrtfd (x))); } }
対応するlib_header.hファイルは、次のようになります。
double my_sqrtfd (double x); double my_rsqrtfd(double x); double my_divfd(double a, double b);
シミュレーションによるOpenCLライブラリーのデバッグ (プレビュー)
インテル® FPGA SDK for OpenCL™ シミュレーターは、x86-64 WindowsまたはLinuxホストで動作する.aocxファイルを生成します。この機能により、毎回ハードウェアにライブラリーをコンパイルしたりFPGAで動作させたりすることなく、カーネルの機能性のシミュレーションやデザインのイタレーションができるようになります。
OpenCL* ライブラリーの動的なパフォーマンスをより深く理解することが必要な場合や、 OpenCL* ライブラリー機能の正確性に関し、エミュレーションやOpenCLのレポートツールが提供する内容以上の情報が必要な場合は、シミュレーターを使用してください。
シミュレーターはサイクル精度が高く、ハードウェアを生成するのと同じネットリストを持ち、デバッグに向けた完全な波形を提供することが可能です。波形は、 Mentor Graphics* ModelSim* ソフトウェアで確認してください。
シミュレーションに向けたライブラリーのコンパイル (-march=emulator)
- ライブラリーのシミュレーションを実行する前に、次の作業を実行してください。
- FPGAアクセラレーター・ボードにボードメーカーが提供しているカスタム・プラットフォームをインストールします。
- 環境変数QUARTUS_ROOTDIR_OVERRIDEが、 インテル® Quartus® Prime開発ソフトウェア・プロ・エディション・ソフトウェアのインストール・フォルダーを指していることを確認します。
- Windowsシステムでライブラリーをシミュレーションするには、Microsoftのリンカーおよび、コンパイル時のライブラリーが追加で必要になります。PATH環境変数設定に、
インテル® FPGA SDK for OpenCL™
スタート・ガイドの、
インテル® FPGA SDK for OpenCL™
・ユーザー環境変数の設定の章で説明されているパスがすべて含まれていることを確認してください。
PATH環境変数設定は、Microsoft Visual StudioのLINK.EXEファイルへのパスを含んでいる必要があります。
-
LIB環境変数設定に、Microsoftのコンパイル時のライブラリーへのパスが含まれていることを確認してください。
コンパイル時のライブラリーは、Microsoft Visual Studioで利用可能です。
- LD_LIBRARY_PATH環境変数設定が、 インテル® FPGA SDK for OpenCL™ スタート・ガイドの、 インテル® FPGA SDK for OpenCL™ ユーザー環境変数の設定の章で説明されているパスをすべて含んでいることを確認してください。
- 特定のボードをターゲットとするシミュレーションをコンパイルするには、 aoc -march=simulator -ghdl -board=<board_name> <your_kernel_filename>.clコマンドを呼び出します。
-
Linuxシステムの場合、
インテル® FPGA SDK for OpenCL™オフライン・コンパイラーは、シンボリック・デバッガーのデバッグサポートを提供しています。
オフライン・コンパイラーのデバッグサポートにより、カーネル・ソースコードにおける機能的なエラーの原因を特定できるようになります。
OpenCL ライブラリーのシミュレーション
シミュレーション中に生成された波形を確認するには、 Mentor Graphics* ModelSim* ソフトウェアをインストールし、コンフィグレーションする必要があります。
エミュレーターとシミュレーターを同じ端末またはコマンド・プロンプト・セッションから実行する場合、シミュレーターを実行する前にエミュレーターの環境変数設定 (CL_CONTEXT_EMULATOR_DEVICE_INTELFPGA) を解除します。エミュレーター環境変数の設定が解除されていないと、シミュレーションはエラーにより失敗します。
エミュレーターとシミュレーターは、別々の端末セッションまたはコマンド・プロンプト・セッションから実行することもできます。
OpenCL* ライブラリーをシミュレーターで実行するには以下を行います。
- ユーティリティー・コマンド aocl linkflags を実行し、ホスト・アプリケーションの構築に必要なライブラリーを検索します。ソフトウェアは、エミュレーションおよび通常のカーネルのコンパイルフローに必要なライブラリーの一覧を表示します。
-
ホスト・アプリケーションを構築し、手順1のライブラリーにリンクします。
ヒント: 他のOpenCL SDKとともに複数のデバイスをエミュレーションするには、ホストランタイムのライブラリーに接続する前 に、ホスト・アプリケーションをKhronos ICD Loader Libraryに接続します。ホスト・アプリケーションをICD Loader Libraryに接続するには、ホスト・アプリケーションのMakefileを変更します。詳細は、ホスト・アプリケーションとKhronos ICD Loader Libraryのリンクを確認ください。
- 必要であれば、.aocxファイルをホストが容易に見つけることのできる位置に動かします。現在の作業ディレクトリーが推奨されます。
-
CL_CONTEXT_MPSIM_DEVICE_INTELFPGA環境変数を設定し、シミュレーション・デバイスを有効にします。
- Windowsの場合
set CL_CONTEXT_MPSIM_DEVICE_INTELFPGA=1
- Linuxの場合
env CL_CONTEXT_MPSIM_DEVICE_INTELFPGA=1
要確認: 環境変数CL_CONTEXT_MPSIM_DEVICE_INTELFPGAを設定すると、シミュレーション・デバイスのみが利用可能になります。すなわち、物理的なボードおよびエミュレーション・デバイスへのアクセスは無効になります。ホストプログラムがシミュレーション・デバイスを識別できない場合、CL_CONTEXT_COMPILER_MODE_INTELFPGA=3を設定する必要があります。
- Windowsの場合
-
ホストプログラムを実行します。
ホストコードとデバイスをデバッグするには、gdbまたはEclipseでホストコードを実行します。
ホストプログラムを実行すると、波形ファイルvsim.wifが作成されます。この波形ファイルは、ホストコードを実行時に ModelSim* ソフトウェアで確認することができます。vsim.wifファイルは、ホストプログラムを実行しているディレクトリーと同じディレクトリーに書き込まれます。
- ホストまたはカーネルプログラムを変更しテストを行う場合は、変更したホストまたはカーネルプログラムのみを再コンパイルし、シミュレーションを再度実行してください。
シミュレーターの制限
シミュレーターの制限を確認し、シミュレーションを試みる際に発生する可能性がある問題をトラブルシューティングしてください。
Windowsのコンパイルが失敗しホストプログラムが.aocxファイルの破損を報告している場合
device.clファイルをコンパイルする際のディレクトリー・パスが長すぎる可能性があります。-oコンパイラー・オプションを使用し、短いパスでコンパイル結果を出力してください。
socket=-11エラーがtranscript.logに記録される場合
Message: "src/hls_cosim_ipc_socket.cpp:202: void IPCSocketMaster::connect(): Assertion `sockfd != -1 && "IPCSocketMaster::connect() call to accept() failed"' failed."
ModelSim* リソースの混同例には、 ModelSim* SEでデバイスをコンパイルし、ホストプログラムを ModelSim* - Intel® FPGA Editionで実行している場合などがあります。
ホストプログラムを実行するとセグメンテーション違反が発生する場合
ホストプログラムを実行した際にセグメンテーション違反が発生する場合は、同じ端末またはコマンド・プロンプト・セッションからエミュレーターとシミュレーターを実行している可能性があります。シミュレーターを実行する前に、エミュレーターの環境変数設定を必ず解除してください。
同じ端末セッションまたはコマンド・プロンプト・セッションで、デバイスとホストプログラムをコンパイルしないようにしてください。別々のセッションを使用することで、起こりうる環境変数の競合を回避できます。
シンプルな関数で動作するOpenCLライブラリーの使用 (例1)
Example1.tgzターボールには、ライブラリー、カーネル、およびホストシステムが含まれています。example1.clカーネル・ソース・ファイルには、2つのカーネルが含まれます。カーネルのtest_libはライブラリー関数を使用します。すなわち、カーネルのtest_builtinは組み込み関数を使用します。ホストは両方のカーネルを実行し、それらの出力とランタイムを比較します。インテルでは、同じ方法でご自身がデザインしたライブラリー関数を検証することを推奨しています。
このデザイン例をコンパイルするには、次の作業を実行します。
- OpenCLデザイン例のウェブページから、example1.tgzを入手します。
- 入手したデザイン例をローカル・ディレクトリーに解凍します。
-
解凍したデザイン例のトップレベルに位置するREADME.htmlファイルの指示に従います。
コンパイルされたホストプログラムを実行すると、次の出力が生成されます。
Loading example1.aocx ... Create buffers Generate random data for conversion... Enqueuing both library and builtin in kernels 4 times with global size 65536 Kernel computation using library function took 5.35333 seconds Kernel computation using built-in function took 5.39949 seconds Reading results to buffers... Checking results... Library function throughput is within 5% of builtin throughput. PASSED
外部メモリーと動作するOpenCLライブラリーの使用 (例2)
Example2.tgzターボールには、ライブラリー、カーネル、そしてホストシステムが含まれています。この例において、グローバルメモリーと通信するRTLコードは、カスタム・プラットフォームまたはリファレンス・プラットフォームに依存します。コンパイルが、Stratix® V Network Reference Platformに対応するボードをターゲットとしていることを確認してください。
インテルでは、RTLモジュールのcopyElement()およびsumOfElements() を、 インテル® FPGA SDK for OpenCL™オフライン・コンパイラーを使用し生成しました。 インテル® FPGA SDK for OpenCL™オフライン・コンパイラーはコードの追加入力を示します。
Example2.clカーネル・ソース・ファイルは2つのカーネルを含みます。カーネルtest6は、copyElement() RTL関数を呼び出すNDRangeカーネルであり、データを B[]からA[]へコピーしglobal_id+100をC[]に格納します。カーネルtest11は、RTL関数を使用する単一ワークアイテムのカーネルです。sumOfElements() RTL関数は、範囲[i, N]のA[]の要素の合計を確定し、残りをC[i]に追加します。
このデザイン例をコンパイルするには次の作業を実行します。
- OpenCLデザイン例のウェブページからexample2.tgzを入手します。
- 入手したデザイン例をローカル・ディレクトリーに解凍します。
-
解凍したデザイン例のトップレベルに位置するREADME.htmlファイルの指示に従います。
コンパイルされたホストプログラムを実行すると、次の出力が生成されます。
Loading example2.aocx ... Running test6 Launching the kernel test6 with globalsize=128 localSize=16 Loading example2.aocx ... Running test11 Launching the kernel test11 with globalsize=1 localSize=1 PASSED
OpenCLライブラリーのコマンドライン・オプション
コマンドオプション | 説明 |
---|---|
-shared |
-rtlコマンドオプションとともに、OpenCLソースファイルをオブジェクト・ファイル (.aoco) へコンパイルします。その後、それをライブラリーに含めることができます。 aoc -rtl -shared <OpenCL source file name>.cl -o <OpenCL object file name>.aoco |
-I=<library_directory> |
<library directory> をヘッダーファイルの検索パスに追加します。 aocl -I <library_header_file_directory> -l <library_file_name>.aoclib <kernel_file_name>.cl |
-L=<library directory> |
<library directory> をOpenCLライブラリーの検索パスに追加します。 「-L」の後のスペースはオプションです。 aoc -l=<library_file_name>.aoclib [-L=<library directory>] <kernel file name>.cl |
-l=<library_file_name>.aoclib | OpenCLライブラリー・ファイル (
<library_file_name>.aoclib) を指定します。 「 -l 」の後のスペースはオプションです。 aoc -l=<library_file_name>.aoclib [-L=<library directory>] <kernel file name>.cl |
-library-debug | ライブラリーに関連のあるデバッグ出力を生成します。追加の出力の一部はstdoutに表示され、その他の部分は
<kernel_file_name>/<kernel_file_name>.log ファイルに表示されます。 aoc -l=<library_file_name>.aoclib -library-debug <kernel_file_name>.cl |
コマンドオプション | 説明 |
---|---|
hdl-comp-pkg <XML_specification_ file>.xml |
単一のHDLコンポーネントを.aocoファイルにパッケージ化します。それをその後、ライブラリーに含みます。このコマンドの呼び出しは、 aoc -rtl <XML_specification_file>.xmlを呼び出すことに類似しています。ただし、aoclは環境チェックを行わないため、処理時間は短くなります。 aocl library hdl-comp-pkg <XML_specification_ file>.xml -o <output_file>.aoco |
-rtl <XML_specification_ file>.xml |
hdl-comp-pkg <XML_specification_ file>.xmlと同じ機能です。 aocl library -rtl <XML_specification_ file>.xml |
create |
hdl-comp-pkgユーティリティー・オプションまたは aoc -shared コマンドを呼び出すことによって作成した.aocoファイルからライブラリー・ファイル、そしてその他の.aoclibライブラリーを作成します。 aocl library create [-name <library_name>] [-vendor <library_vendor>] [-version <library_version>] [-o <output_file>.aoclib] [.aoco...] [.aoclib...] この-name、-vendor、-versionは、オプションの情報文字列で、それらを指定しライブラリーに加えることができます。 |
list <library_name> |
ライブラリーのRTLコンポーネントをすべてリスト表示します。現在このオプションは、OpenCL関数のリスト化には利用できません。 aocl library list <library_name> |
help |
インテル® FPGA SDK for OpenCL™
ライブラリー・ユーティリティー・オプションのリストとその説明を画面上に表示します。 aocl library help |
カーネルのメモリーシステムをコンフィグレーションするメモリー属性
属性 | 説明 |
---|---|
register | ローカル変数が、レジスターのパイプラインを介して伝達される必要があることを指定します。レジスター変数の実装は、FFのみで行うことも、FFとRAMベースのFIFOを組み合わせて行うことも可能です。 |
memory("impl_type") | ローカル変数をメモリーシステムに実装する必要があることを指定します。メモリーカーネル属性を含めることは、ローカル変数を__local修飾子で宣言することと同等です。 オプションで文字列引数を渡し、メモリーの実装タイプを指定することが可能です。impl_typeにBLOCK_RAMまたはMLABを指定すると、メモリーはそれぞれ、メモリーブロック (M20Kなど) または、メモリー・ロジック・アレイ・ブロック (MLAB) を使用し実装されます。 |
numbanks(N)
Nは整数値です。 |
ローカル変数を実装しているメモリーシステムが、N個のバンクを有する必要があることを指定します。Nは0より大きい2のべき乗の整数値です。 |
bankwidth(N)
Nは整数値です。 |
ローカル変数を実装しているメモリーシステムが、Nバイト幅のバンクを有する必要があることを指定します。Nは0より大きい2のべき乗の整数値です。 |
singlepump | ローカル変数を実装しているメモリーシステムをシングルポンピングする必要があることを指定します。 |
doublepump | ローカル変数を実装しているメモリーシステムをダブルポンピングする必要があることを指定します。 |
numreadports(N)
Nは整数値です。 |
ローカル変数を実装しているメモリーシステムが、N個の読み出しポートを有する必要があることを指定します。Nは0より大きい整数値です。 |
numwriteports(N)
Nは整数値です。 |
ローカル変数を実装しているメモリーシステムが、N個の書き込みポートを有する必要があることを指定します。Nは0より大きい整数値です。 |
merge("label", "direction") | 2つ以上の変数を同じメモリーシステムに実装するように強制します。 labelは任意の文字列です。結合しようとしている変数に同じラベルを割り当てます。 directionにwidthもしくはdepthを指定し、メモリーをそれぞれ幅方向または深さ方向のどちらに結合するかを特定します。 |
bank_bits(b 0 , b 1 , ..., b n ) | メモリーシステムを、2nのバンクに分割するよう強制します。b
0
, b
1
, ..., b
n
は、バンク選択ビットを形成しています。 重要:
b
0
、b
1
、…、b
n
はかならず連続する正の整数にしてください。
bank_bits属性なしでnumbanks(n)属性を指定すると、バンク選択ビットはデフォルトで最下位ビット (0、1、…、log2(numbanks)-1) になります。 |
max_concurrency(N) |
メモリーが最大N個のプライベート・コピーを保有し、N回のループの同時反復を常に実行することを指定します。ここでNは、2の累乗に切り上げられます。 (宣言またはアクセスパターンによって) 変数の範囲がループに限定されている場合、この属性を適用します。ループが#pragma max_concurrency M も持っている場合、作成されるプライベート・コピーの数は、min(M,N)になります。 |
ユースケース例 | 構文 |
---|---|
レジスターに変数を実装する |
int __attribute__((register)) a[12]; |
それぞれ8バイト幅の8つのバンクを持つメモリーシステムを実装する |
int __attribute__((memory, numbanks(8), bankwidth(8)) b[16]; |
ダブルポンピングのメモリーシステムを、128バイト幅のバンク1つ、書き込みポート1つ、読み出しポート4つとともに実装する |
int __attribute__((memory, numbanks(1), bankwidth(128), doublepump, numwriteports(1), numreadports(4)) c[32]; |
structのデータメンバーにメモリー属性を適用することも可能です。struct 宣言のstructデータメンバーに属性を指定してください。structのオブジェクトのインスタンス化に属性を適用すると、この属性はstructデータメンバーの宣言で指定された属性を上書きします。以下を例として示します。
struct State { int array[100] __attribute__((__memory__)); int reg[4] __attribute__((__register__)); }; __kernel void sum(...) { struct State S1; struct State S2 __attribute__((__memory__)); // some uses }
オフライン・コンパイラーはS1を、メモリーに実装されるS1.array[100]と、レジスターに実装されるS1.reg[4]の2つの変数に分割します。ただし、S2にはmemory属性が適用されているため、コンパイラーはstruct宣言でオブジェクトS2に適用された属性を無視し、それを分割しません。
変数固有の属性を使用する際の制約
コンパイルエラーの原因となる変数固有の属性のサポートされていない使い方
- カーネル属性を、コンスタント、ローカルまたはプライベート変数の宣言以外の宣言で使用している (例えば、関数パラメーターの宣言、グローバル変数宣言、関数宣言など)
- register属性を、他の変数固有の属性と組み合わせて使用している
- singlepumpとdoublepump属性をどちらも同じ変数宣言に含んでいる
- singlepumpおよびdoublepump属性を、numreadportsまたはnumwriteports属性を含まずに指定している
- numreadports属性を指定する際にnumwriteports属性を同じ変数宣言に含んでいない、またはその逆
- 同じ変数宣言にnumbanksとbankwidth属性を含まずに、次の属性のいずれかを指定している
- numreadports
- numwriteports
- singlepump
- doublepump
コンパイル時にオフライン・コンパイラーが警告を発行する原因となる不正なメモリー構成
- 変数固有の属性で定義されているメモリー構成が、利用可能なストレージサイズを超えている (例えば、8バンクのローカルメモリーを整数変数に指定するなど)
コンパイルエラーの原因となる不正なメモリー構成
- バンク幅がデータのストレージサイズよりも小さい (例えば4バイト整数の配列の場合バンク幅は2 バイトです)
- 変数にメモリーのコンフィグレーションを指定している。コンパイラーの制約またはコーディング・スタイルのため、オフライン・コンパイラーはメモリーを分割するのではなく、同じメモリーに変数を実装します。
- register属性を変数に指定している。コンパイラーの制約またはコーディング・スタイルにより、オフライン・コンパイラーは変数をレジスターに実装できません。
ハードウェア使用量のオーバーヘッド低減に向けたカーネル属性
カーネル・インターフェイスに向けたハードウェア
カーネル・パイプライン周辺のハードウェアは、次のような機能において必要です。
- ワークアイテムおよびワークグループIDの振り分け
- カーネル引数とワークグループのサイズに関するホストとの通信
図 20 は、オフライン・コンパイラーが次のカーネルをコンパイルする際に生成するハードウェアを表しています。
__kernel void my_kernel(global int* arg) { … int sum = 0; for(unsigned i = 0; i < n; i++) { if(sum < m) sum += val; } *arg = sum; … }
カーネルIDを生成し振り分けるハードウェアの省略
意味的にmax_global_work_dim(0)カーネル属性は、カーネルのグローバル・ワーク・サイズがゼロであることを指定します。このカーネル属性を設定すると、カーネルがグローバル、ローカル、またはグループIDを使用しないことを意味します。カーネルコード内のこの属性の存在は、カーネルが単一のワークアイテム・カーネルであるということをオフライン・コンパイラーに対し保証しています。
次のカーネルをコンパイルすると、オフライン・コンパイラーは図 21 で表されているインターフェイス・ハードウェアを生成します。
channel int chan_in; channel int chan_out; __attribute__((max_global_work_dim(0))) __kernel void plusK (int N, int k) { for (int i = 0; i < N; ++i) { int data_in = read_channel_intel(chan_in); write_channel_intel(chan_out, data_in + k); } }
現在のカーネル実装に複数のワークアイテムがあり、グローバル、ローカル、またはグループIDを使用していない場合、次のようにカーネルコードを変更すると、max_global_work_dim(0)カーネル属性を使用することができます。
- ワークアイテムと同じ数だけ反復するforループのカーネル本体をラップする
- 変更したカーネルを1つのワークアイテムのみで起動する
ホストとカーネル間の通信ハードウェアの省略
autorunカーネル属性はオフライン・コンパイラーに対し、カーネルが独立して実行され、どのホストからもエンキューされないことを通知します。
autorun属性を活用するには、カーネルが次の基準をすべて満たしている必要があります。
- I/Oチャネルを使用しないこと注: カーネルからカーネルへのチャネルはサポートされています。
- 引数を持たないこと
-
max_global_work_dim(0)属性またはreqd_work_group_size(X、Y、Z) 属性のどちらかを持つこと注: reqd_work_group_size(X、Y、Z) 属性のパラメーターは 232の除数である必要があります。
前述のように、autorun属性のあるカーネルは引数を持つことができず、ホストが明示的に起動することなく実行を開始します。そのためオフライン・コンパイラーは、ホストとカーネルの通信のためのロジックを生成する必要がありません。このロジックを省略するとロジック使用率が低下し、オフライン・コンパイラーがパフォーマンスの最適化をさらに適用することが可能になります。
autorun属性の一般的な使用例は、カーネル間の1つ以上のチャネルからデータを読み取り、データを処理し、その結果を1つ以上のチャネルに書き込むカーネルです。カーネルをコンパイルすると、オフライン・コンパイラーは、図 22 で表されているようにハードウェアを生成します。
channel int chan_in; channel int chan_out; __attribute__((max_global_work_dim(0))) __attribute__((autorun)) __kernel void plusOne () { while(1) { int data_in = read_channel_intel(chan_in); write_channel_intel(chan_out, data_in + 1); } }
clEnqueueNDRangeKernel APIのglobal_work_offset引数をサポートするハードウェアの省略
このカーネル属性は、0もしくはNULLのglobal_work_offset引数で必ずエンキューされるカーネルすべてに推奨されます。このカーネル属性が設定されると、インテルFPGAホストランタイムは、0以外もしくはNULL以外のglobal_work_offset引数でカーネルがエンキューされた場合に、CL_INVALID_GLOBAL_OFFSETエラーコードを返します。
num_compute_units(X,Y,Z) 属性を使用したカーネルの複製
計算ユニット数の指定 で示されているように、num_compute_units(N) カーネル属性をカーネルに含めることで、 インテル® FPGA SDK for OpenCL™オフライン・コンパイラーに対し、複数の計算ユニットを生成しデータを処理するよう指示します。num_compute_unit(N) 属性はオフライン・コンパイラーに、カーネルの同一のコピーをN個ハードウェアに生成するよう指示します。
get_compute_id() 関数を使用した複製カーネルのカスタマイズ
計算IDの取得は、カーネルをソースコードに複写し、各カーネルのコピーに特定のコードを追加することに代わる有用な方法です。カーネルがnum_compute_units(X,Y,Z) 属性を使用し、get_compute_id() 関数を呼び出すと、 インテル® FPGA SDK for OpenCL™オフライン・コンパイラーは固有の計算IDを各計算ユニットに割り当てます。その後get_compute_id() 関数は、それらの固有の計算IDを取得します。計算IDを使用することで、関連付けられている計算ユニットの動作を指定し、同じカーネルのソースコードから派生した他の計算ユニットと異なる動作をさせることが可能です。例えば、get_compute_id() の戻り値を使用しチャネルの配列にインデックスを付け、各計算ユニットが読み書きするチャネルを指定することができます。
num_compute_units属性は最大3つの引数を受け取ります (num_compute_units(X,Y,Z))。get_compute_id() 関数とともにこの属性を使用すると、1次元、2次元および3次元のロジック配列の計算ユニットを作成することが可能になります。計算ユニットの1D配列の使用例には、線形パイプラインのカーネル (カーネルのデイジーチェーンとも呼ばれる) があります。計算ユニットの2D配列の使用例は、カーネルのシストリック・アレイです。
__attribute__((max_global_work_dim(0))) __attribute__((autorun)) __attribute__((num_compute_units(4,4))) __kernel void PE() { row = get_compute_id(0); col = get_compute_id(1); … }
3D配列の計算ユニットにおいては、論理計算ユニット配列の計算ユニットのX、Y、Z座標を、get_compute_id(0)、get_compute_id(1)、get_compute_id(2)をそれぞれ使用することで取得できます。この場合のAPIは、ワークアイテムの組み込み関数API (get_global_id()、get_local_id()、get_group_id()) と非常に類似しています。
グローバルID、ローカルID、およびグループIDは、ホストがカーネルを呼び出す方法によってランタイムで変化します。ただし、計算IDはコンパイル時に認識されるため、オフライン・コンパイラーは各計算ユニットに最適化されたハードウェアを生成できます。
カーネルコピーでのチャネル使用
次のコード例は、複数の計算ユニットにチャネルを実装しています。
#define N 4 channel int chain_channels[N+1]; __attribute__((max_global_work_dim(0))) __kernel void reader(global int *data_in, int size) { for (int i = 0; i < size; ++i) { write_channel_intel(chain_channels[0], data_in[i]); } } __attribute__((max_global_work_dim(0))) __attribute__((autorun)) __attribute__((num_compute_units(N))) __kernel void plusOne() { int compute_id = get_compute_id(0); int input = read_channel_intel(chain_channels[compute_id]); write_channel_intel(chain_channels[compute_id+1], input + 1); } __attribute__((max_global_work_dim(0))) __kernel void writer(global int *data_out, int size) { for (int i = 0; i < size; ++i) { data_out[i] = read_channel_intel(chain_channels[N]);; } }
カーネル内に登録される割り当ての組み込み関数
通常、望まれるパフォーマンスを達成するために__fpga_reg() 関数をカーネルコードに含める必要はありません。
__fpga_reg()組み込み関数のプロトタイプ
T __fpga_reg(T op)
このTは、標準のOpenCLデバイスのデータ型や、OpenCL型を含むユーザー定義の構造体など、任意のサイズの型にすることができます。
__fpga_reg() 関数は次のような目的で使用します。
- 大規模なストリックアレイを処理する要素間など、空間的に離れたデータパスの間のクリティカル・パスの切断
- 空間的に異なるカーネル実装によって発生する配置とルーティング・エフォートの負荷の軽減
__fpga_reg() 関数は、 インテル® FPGA SDK for OpenCL™オフライン・コンパイラーに対し、オペランドを戻り値に割り当てる信号パスに、ハードウェアをパイプライン化するレジスターを最低1つ挿入することを指示します。この組み込み関数は、OpenCLプログラミング言語で割り当てとして機能し、ここでオペランドは戻り値に割り当てられます。この割り当てには、標準のC代入を超える暗黙的な意味や機能的な意味はありません。機能的に__fpga_reg() 関数は、オフライン・コンパイラーの最適化によって常に削除されていると考えることができます。
ネスト化された__fpga_reg() 関数呼び出しをカーネルコードに導入し、オフライン・コンパイラーが割り当てパスに挿入する最小レジスター数を増やすことができます。各関数呼び出しは、少なくとも1つのレジスター・ステージの挿入を保証しているため、呼び出し数はレジスター数に下限を与えます。
次に例を示します。
int out=__fpga_reg(__fpga_reg(in));
このコード行はオフライン・コンパイラーに対し、割り当てパスに少なくとも2つのレジスターを挿入するよう指示します。オフライン・コンパイラーは、2つより多くのレジスターをパスに挿入する場合があります。
OpenCL機能のサポート状況
OpenCL1.0の機能のサポート状況
以降の章では、OpenCL Specification version 1.0に記載されているOpenCL™機能のサポート状況の概要を説明します。
OpenCL1.0 Cプログラミング言語の実装
サポート状況の表の見方
シンボル | 説明 |
---|---|
● | この機能はサポートされており、備考欄にてサポートされている内容を明確にしています。 |
○ | この機能は備考欄で特定されている内容を除きサポートされています。 |
X | この機能はサポートされていません。 |
セクション | 機能 | サポート状況 | 備考 |
---|---|---|---|
6.1.1 | 組み込みスカラーデータ型 | ||
倍精度浮動小数点 | ○ | 倍精度浮動小数点の組み込みスカラーデータ型すべてに対する暫定サポートです。この機能は、OpenCL Specification version 1.0に準拠していない可能性があります。 現在、次の倍精度浮動小数点関数は、OpenCL Specification version 1.0に準拠すると考えられています。 add / subtract / multiply / divide / ceil / floor / rint / trunc / fabs / fmax / fmin / sqrt / rsqrt / exp / exp2 / exp10 / log / log2 / log10 / sin / cos / asin / acos / sinh / cosh / tanh / asinh / acosh / atanh / pow / pown / powr / tanh / atan / atan2 / ldexp / log1p / sincos |
|
半精度浮動小数点 | ○ | スカラーの加算、減算、乗算に対するサポートです。単精度浮動小数点との変換をサポートします。この機能は、OpenCL Specification version 1.0に準拠していない可能性があります。 この機能はエミュレーターでサポートされています。 |
|
6.1.2 | 組み込みベクトルデータ型 | ○ |
3要素のベクトルに対する暫定サポートです。3要素ベクトルのサポートは、OpenCL Specification version 1.0を補うものです。 |
6.1.3 | その他の組み込みデータ型 | ○ | SDKはイメージをサポートしていないため、イメージ型またはサンプラー型をサポートしていません。 |
6.2.1 | 暗黙的な変換 | ● | スカラー型とベクトル型の暗黙的な変換に関する重要な説明については、OpenCL Specification version 1.2のSection 6.2.6、Usual Arithmetic Conversionsを参照してください。 |
6.2.2 | 明示的なキャスト | ● | SDKでは、スカラーを異なる要素型のベクトルにキャストできます。 |
6.5 | アドレス空間修飾子 | ○ | 関数スコープ__constant変数はサポートされていません。 |
6.6 | イメージアクセス修飾子 | X | SDKはイメージをサポートしていません。 |
6.7 | 関数修飾子 | ||
6.7.2 | オプションの属性修飾子 | ● | カーネルのパフォーマンス向上に向けたreqd_work_group_sizeの使用方法に関するヒントは、
インテル® FPGA SDK for OpenCL™
ベスト・プラクティス・ガイドを参照ください。 SDKはvec_type_hintとwork_group_size_hint属性修飾子の解析は行いますが、それらを無視します。 |
6.9 | プリプロセッサー・ディレクティブとマクロ | ||
#pragmaディレクティブ (#pragma unroll) | ● |
インテル® FPGA SDK for OpenCL™オフライン・コンパイラーは#pragma unrollのみサポートしています。整数引数をアンロール・ディレクティブに割り当て、ループ展開の範囲を制御することができます。 例えば#pragma unroll 4は、ループの4つの反復を展開します。 展開係数のないアンロール・ディレクティブの場合、デフォルトでオフライン・コンパイラーはループを完全に展開しようと試みます。 カーネルのパフォーマンス向上に向けた#pragma unrollの使用方法に関するヒントは、 インテル® FPGA SDK for OpenCL™ ベスト・プラクティス・ガイドを参照ください。 |
|
値1に定義された__ENDIAN_LITTLE__ | ● | ターゲットFPGAはリトル・エンディアンです。 | |
__IMAGE_SUPPORT__ | X | __IMAGE_SUPPORT__は未定義です。SDKはイメージをサポートしていません。 | |
6.10 | Attribute Qualifiers—オフライン・コンパイラーは以下ように属性修飾子を解析します。 | ||
6.10.3 | 変数の属性を指定する (endian) | X | |
6.10.4 | ブロック属性と制御フロー・ステートメント属性の指定 | X | |
6.10.5 | 属性修飾子の拡張 | ● | オフライン・コンパイラーは、さまざまな構文構造の属性を解析することができます。また、内部使用のためにいくつかの属性名を予約しています。 これらのカーネル属性を使用し、カーネルのパフォーマンスを最適化する方法については、 インテル® FPGA SDK for OpenCL™ ベスト・プラクティス・ガイドを参照ください。 |
6.11.2 | 数学関数 | ||
組み込み数学関数 | ○ | 倍精度浮動小数点の組み込み数学関数に対する暫定サポートです。OpenCL Specification version 1.0に準拠していない可能性があります。 | |
組み込み数学関数のhalf_とnative_ | ○ | 倍精度浮動小数点の組み込み数学関数half_およびnative_に対する暫定サポートです。OpenCL Specification version 1.0に準拠していない可能性があります。 | |
6.11.5 | ジオメトリック関数 | ○ | 倍精度浮動小数点の組み込みジオメトリック関数に対する暫定サポートです。この関数はOpenCL Specification version 1.0に準拠していない可能性があります。 SDKでサポートされる組み込みのジオメトリック関数のリストについては、組み込みジオメトリック関数の引数型 を参照してください。 |
6.11.8 | イメージ読み取りおよび書き込み機能 | X | SDKはイメージをサポートしていません。 |
6.11.9 | Synchronization Functions— バリアー同期機能 | ● | 内容の明確化と例外
ワークアイテムの制限は、カーネルにサポートされているワークグループの最大サイズであり、この制限はランタイムに強制されます。 |
6.11.11 | グローバルメモリーからローカルメモリー、ローカルメモリーからグローバルメモリーの非同期コピーおよびプリフェッチ | ● | 実装は単純なものです。 ワークアイテム (0,0,0) はコピーを実行し、wait_group_eventsがバリアーとして実装されます。
|
OpenCL Cプログラミング言語の制約
インテル® FPGA SDK for OpenCL™オフライン・コンパイラーは、許可されていない特定のプログラミング言語機能に対する制約を強制しません。使用しているカーネルコードに、OpenCL Specification version 1.0がサポートしていない機能が含まれていないようにしてください。
次の表は、 インテル® FPGA SDK for OpenCL™ が強制するOpenCL Cプログラミング言語の制約に関する補足説明のみを記載しています。他の制約はすべて、OpenCL Specification version 1.0のSection 6.8の内容と完全に一致します。
機能 | サポート状況 | 備考 |
---|---|---|
構造体型のカーネル引数 | X | 構造体引数をグローバルメモリーの構造体へのポインターに変換します。 |
既約の制御フロー | X | オフライン・コンパイラーはこの制約に関しエラーは返しませんが、この機能はサポートされていません。 |
32ビット未満の組み込み型メモリーへの書き込み | ○ | サイズが32ビット未満のストア操作はメモリーの性能が低下する可能性がありますが、サポートされています。 |
異なるアドレス空間に属するstructまたはunion要素 | X | オフライン・コンパイラーはこの制約を強制しません。 警告:
structまたはunion要素を異なるアドレス空間に割り当てると、致命的なエラーが発生する可能性があります。
|
シンボル | 説明 |
---|---|
● |