Vivado HLS 最適化手法ガイドUG1270 (v2018.1) 2018 年 04 月 04 日
この資料は表記のバージョンの英語版を翻訳したもので、内容に相違が生じる場合には原文を優先します。資料によっては英語版の更新に対応していないものがあります。日本語版は参考用としてご使用の上、最新情報につきましては、必ず最新英語版をご参照ください。
改訂履歴次の表に、この文書の改訂履歴を示します。
セクション 改訂内容2018 年 4 月 4 日 バージョン 2018.1
資料全体 マイナーな編集上のアップデート。
Vivado HLS 最適化手法ガイド 3UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
目次改訂履歴......................................................................................................................................... 3
第 1 章: 概要................................................................................................................................. 8HLS プラグマ................................................................................................................................ 8OpenCL 属性...............................................................................................................................10指示子..........................................................................................................................................11
第 2 章: ハードウェア関数の最適化................................................................................14ハードウェア関数の最適化手法.................................................................................................15ベースラインのハードウェア関数............................................................................................. 17メトリクスのための最適化........................................................................................................ 18パフォーマンスのためのパイプライン処理.............................................................................. 19
第 3 章: パフォーマンスのための構造最適化............................................................. 26レイテンシの削減....................................................................................................................... 29エリアの削減...............................................................................................................................30デザイン最適化のワークフロー.................................................................................................32
第 4 章: データ アクセス パターン...................................................................................34不適切なデータ アクセス パターンを使用したアルゴリズム...................................................34最適なデータ アクセス パターンを使用したアルゴリズム...................................................... 43
第 5 章: 標準水平たたみ込み.............................................................................................. 46最適な水平たたみ込み................................................................................................................50最適な垂直たたみ込み................................................................................................................51最適な境界ピクセルたたみ込み.................................................................................................53最適なデータ アクセス パターン............................................................................................... 55
付録 A: OpenCL 属性............................................................................................................. 56always_inline.............................................................................................................................. 57opencl_unroll_hint..................................................................................................................... 58
Vivado HLS 最適化手法ガイド 4UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
reqd_work_group_size.............................................................................................................. 60vec_type_hint..............................................................................................................................61work_group_size_hint................................................................................................................63xcl_array_partition..................................................................................................................... 64xcl_array_reshape...................................................................................................................... 67xcl_data_pack............................................................................................................................. 69xcl_dataflow................................................................................................................................71xcl_dependence......................................................................................................................... 72xcl_max_work_group_size.........................................................................................................75xcl_pipeline_loop........................................................................................................................76xcl_pipeline_workitems.............................................................................................................78xcl_reqd_pipe_depth..................................................................................................................79xcl_zero_global_work_offset.....................................................................................................81
付録 B: HLS プラグマ.............................................................................................................84pragma HLS allocation..............................................................................................................85pragma HLS array_map............................................................................................................ 88pragma HLS array_partition.....................................................................................................90pragma HLS array_reshape......................................................................................................92pragma HLS clock......................................................................................................................95pragma HLS data_pack............................................................................................................. 96pragma HLS dataflow............................................................................................................... 99pragma HLS dependence.......................................................................................................102pragma HLS expression_balance.......................................................................................... 104pragma HLS function_instantiate..........................................................................................105pragma HLS inline...................................................................................................................107pragma HLS interface............................................................................................................. 110pragma HLS latency................................................................................................................ 116pragma HLS loop_flatten........................................................................................................117pragma HLS loop_merge........................................................................................................119pragma HLS loop_tripcount................................................................................................... 121pragma HLS occurrence......................................................................................................... 122pragma HLS pipeline.............................................................................................................. 124pragma HLS protocol..............................................................................................................126pragma HLS reset....................................................................................................................127pragma HLS resource............................................................................................................. 129pragma HLS stream................................................................................................................ 131pragma HLS top.......................................................................................................................133
Vivado HLS 最適化手法ガイド 5UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
pragma HLS unroll.................................................................................................................. 134
付録 C: その他のリソースおよび法的通知.................................................................138参考資料.................................................................................................................................... 138お読みください: 重要な法的通知.............................................................................................139
Vivado HLS 最適化手法ガイド 6UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
Vivado HLS 最適化手法ガイド 7UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
第 1 章
概要このガイドでは、Vivado HLS を使用して最適化を実行する方法を詳細に説明します。実行する最適化を指定する指示子と、最適化を確実に効率的に適用する手法を示します。
HLS プラグマVivado HLS での最適化SDAccel および SDSoC プロジェクトでは、ハードウェア カーネルを OpenCL、C、または C++言語から RTL に合成し、ザイリンクス デバイスのプログラマブル ロジックにインプリメントできるようにする必要があります。Vivado HLS は、OpenCL、C、および C++ 言語記述から RTLを合成します。Vivado HLS はユーザーの操作なしで SDAccel または SDSoC 開発環境プロジェクトを処理できるよう設計されていますが、生成される RTL コードのレイテンシの削減、スループット パフォーマンスの向上、エリアおよびデバイス リソース使用率の削減など、デザインを最適化するためのプラグマが提供されています。これらのプラグマは、カーネルのソース コードに直接追加できます。重要:SDSoC 環境では HLS プラグマの使用がサポートされますが、関数インターフェイスの引数に適用されるプラグマ (インターフェイス、配列分割、または data_pack プラグマ) はサポートされません。詳細は、『SDSoC 環境プロファイリングおよび最適化ガイド』 (UG1235) の「ハードウェア関数の最適化」を参照してください。Vivado HLS プラグマには、次の最適化タイプがあります。
1: 概要
Vivado HLS 最適化手法ガイド 8UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
表 1: Vivado HLS プラグマ (タイプ別)
タイプ 属性カーネル最適化 • pragma HLS allocation
• pragma HLS clock
• pragma HLS expression_balance
• pragma HLS latency
• pragma HLS reset
• pragma HLS resource
• pragma HLS top
関数のインライン展開 • pragma HLS inline
• pragma HLS function_instantiate
インターフェイス合成 • pragma HLS interface
• pragma HLS protocol
タスク レベルのパイプライン処理 • pragma HLS dataflow
• pragma HLS stream
パイプライン処理 • pragma HLS pipeline
• pragma HLS occurrence
ループ展開 • pragma HLS unroll
• pragma HLS dependence
ループ最適化 • pragma HLS loop_flatten
• pragma HLS loop_merge
• pragma HLS loop_tripcount
配列最適化 • pragma HLS array_map
• pragma HLS array_partition
• pragma HLS array_reshape
構造体のパック • pragma HLS data_pack
1: 概要
Vivado HLS 最適化手法ガイド 9UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
OpenCL 属性OpenCL での最適化このセクションでは、SDAccel コンパイラ xocc、SDSoC システム コンパイラ sdscc と sds++、および Vivado HLS 合成でのシステム最適化のためにソース コードに追加可能な OpenCL 属性について説明します。SDx では、コードをデータの動きとカーネル パフォーマンスで最適化する OpenCL 属性が提供されています。データの動きの最適化は、インターフェイス帯域幅および DDR 帯域幅の最大限に活用することにより、システム レベルのデータ スループットを最大にすることを目的として実行されます。カーネル計算最適化は、カーネル インターフェイスにデータが到達したらすぐにすべてのデータを消費できるプロセッシング ロジックを作成することを目的として実行されます。これは通常、関数のインライン展開とパイプライン処理、ループ展開、配列分割、データフローなどの手法を使用してデータパスを一致させるようにプロセッシング コードを展開することによって達成されます。OpenCL 属性には、次のタイプがあります。表 2: OpenCL 属性 (タイプ別)
タイプ 属性カーネル サイズ • reqd_work_group_size
• vec_type_hint
• work_group_size_hint
• xcl_max_work_group_size
• xcl_zero_global_work_offset
関数のインライン展開 • always_inline
タスク レベルのパイプライン処理 • xcl_dataflow
• xcl_reqd_pipe_depth
パイプライン処理 • xcl_pipeline_loop
• xcl_pipeline_workitems
ループ展開 • opencl_unroll_hint
配列最適化 • xcl_array_partition
• xcl_array_reshape
注記: 配列変数では、使用できる配列最適化属性は 1つのみです。
1: 概要
Vivado HLS 最適化手法ガイド 10UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
ヒント: SDAccel および SDSoC コンパイラでは、always_inline、noinline、unroll、nounroll など、gcc でサポートされる多くの標準属性もサポートされます。
指示子次の表に示される属性の詳細は、『Vivado Design Suite ユーザー ガイド: 高位合成』 (UG902) の「コマンド リファレンス」を参照してください。表 3: Vivado HLS プラグマ (タイプ別)
タイプ 属性カーネル最適化 • set_directive_allocation
• set_directive_clock
• set_directive_expression_balance
• set_directive_latency
• set_directive_reset
• set_directive_resource
• set_directive_top
関数のインライン展開 • set_directive_inline
• set_directive_function_instantiate
インターフェイス合成 • set_directive_interface
• set_directive_protocol
タスク レベルのパイプライン処理 • set_directive_dataflow
• set_directive_stream
パイプライン処理 • set_directive_pipeline
• set_directive_occurrence
ループ展開 • set_directive_unroll
• set_directive_dependence
ループ最適化 • set_directive_loop_flatten
• set_directive_loop_merge
• set_directive_loop_tripcount
1: 概要
Vivado HLS 最適化手法ガイド 11UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
表 3: Vivado HLS プラグマ (タイプ別) (続き)
タイプ 属性配列最適化 • set_directive_array_map
• set_directive_array_partition
• set_directive_array_reshape
構造体のパック • set_directive_data_pack
1: 概要
Vivado HLS 最適化手法ガイド 12UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
1: 概要
Vivado HLS 最適化手法ガイド 13UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
第 2 章
ハードウェア関数の最適化SDSoC 環境ではヘテロジニアス クロス コンパイルが導入されており、Zynq-7000 SoC およびZynq UltraScale+ MPSoC CPU の ARM CPU 用クロス コンパイラと、ハードウェア関数用の PLクロス コンパイラとして Vivado HLS が含まれています。このセクションでは、デフォルトの動作と、Vivado HLS クロス コンパイラに関連する最適化指示子について説明します。Vivado HLS のデフォルトの動作では、ハードウェアが C/C++ コードを正確に反映するように、関数およびループが順次実行されます。最適化指示子を使用すると、パイプライン処理を使用することにより、ハードウェア関数のパフォーマンスが大幅に向上します。この章では、高パフォーマンスを達成するようデザインを最適化するための一般的な手法を示します。Vivado HLS を使用してデザインを最適化する際には、さまざまな目標が考えられます。この設計手法では、クロック サイクルごとに新しい入力データ 1 サンプルを処理するパフォーマンスができるだけ高いデザインを作成することを目標としていると想定されるので、そのための最適化がレイテンシまたはリソースを削減する最適化の前に実行されます。ここで説明する最適化の詳細は、『Vivado Design Suite ユーザー ガイド: 高位合成』 (UG902) を参照してください。特定の最適化の詳細を確認する前に、設計手法を確認し、ハードウェア関数の最適化をグローバルな観点で理解しておくことをお勧めします。
2: ハードウェア関数の最適化
Vivado HLS 最適化手法ガイド 14UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
ハードウェア関数の最適化手法ハードウェア関数は Vivado HLS コンパイラでプログラマブル ロジック (PL) のハードウェアに合成されます。このコンパイラでは、C/C++ コードが FPGA ハードウェア インプリメンテーションに自動的に変換されますが、ほかのコンパイラと同様、コンパイラ デフォルトが使用されます。Vivado HLS には、コンパイラ デフォルトだけでなく、コード内にプラグマを使用して C/C++ コードに適用できる最適化が多数あります。この章では、適用可能な最適化と、推奨される適用手法を説明します。ハードウェア関数を最適化するには、次の 2 つのフローがあります。• トップダウン フロー: SDSoC 環境内でプログラムがトップダウンでハードウェア関数に分解され、自動的にデータフロー モードで動作する関数のパイプラインがシステム コンパイラで作成されるようにします。各ハードウェア関数のマイクロアーキテクチャは、Vivado HLS を使用して最適化します。
• ボトムアップ フロー: Vivado Design Suite に含まれる Vivado HLS コンパイラを使用してハードウェア関数をシステムとは別に最適化します。ハードウェア関数を解析して、最適化指示子を使用してデフォルトとは異なるインプリメンテーションを作成し、その結果のハードウェア関数を SDSoC 環境に組み込みます。
ボトムアップ フローは、ソフトウェアとハードウェアが別のチームによって最適化される場合や、ソフトウェア プログラマが自社またはパートナーからの既存のハードウェア インプリメンテーションを利用する場合などによく使用されます。どちらのフローもサポートされており、同じ最適化手法を使用できます。どちらのワークフローでも同じ高パフォーマンスのシステムを作成できます。この選択は各チームおよび組織によるワークフロー決定事項であり、ザイリンクスではどちらのフローを使用するかは特に推奨しません。両方のフローの例が『SDAccel 環境プロファイリングおよび最適化ガイド』 (UG1207) に示されています。次の図は、ハードウェア関数の最適化手法を示しています。
2: ハードウェア関数の最適化
Vivado HLS 最適化手法ガイド 15UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
デザインのシミュレーション - C 関数の検証
デザインの合成 - デザインのベースライン制約の作成
1: 初期最適化 - インターフェイス (およびデータ パック) の定義 - ループ トリップ カウントの定義
2: パフォーマンスのためのパイプライン処理 - パイプラインおよびデータフロー
3: パフォーマンスのための構造最適化 - メモリおよびポートの分割 - 偽依存性の削除
4: レイテンシの削減 - レイテンシ要件の指定 (オプション)
5: エリアの縮小 - 共有によるリソースの復元 (オプション)
X15638-011618
この図は、この手法のすべての手順を示しています。次のセクションで、最適化の詳細を説明します。重要: デザインは、手順 3 の後に最適なパフォーマンスになります。手順 4 はデザイン全体のレイテンシを最小限に抑えるか、特定の制御をするために使用し、レイテンシが問題となるようなアプリケーションでのみ必要です。手順 5 では、ハードウェア インプリメンテーションに必要なリソースを削減します。これは、通常大型のハードウェア関数が使用可能なリソースにインプリメントできなかった場合にのみ使用します。FPGA のリソース数は決まっているので、パフォーマンス目標が達成されていれば、より小型のインプリメンテーションを作成する利点は通常ありません。
2: ハードウェア関数の最適化
Vivado HLS 最適化手法ガイド 16UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
ベースラインのハードウェア関数ハードウェア関数の最適化の前に、既存コードとコンパイラのデフォルトを使用して達成されるパフォーマンスを理解し、パフォーマンスがどのように測定されるか把握することが重要です。これは、ハードウェアにインプリメントする関数を選択して、プロジェクトを構築することにより達成できます。プロジェクトを構築すると、レポート (<project name>/<build_config>/_sds/vhls/<hw_function>/solution/syn/report/<hw_function>.rpt) を IDE のレポート セクションに表示できるようになります。このレポートには、パフォーマンスの見積もりと使用率の見積もりが表示されます。パフォーマンス見積もりでの重要なのは、順にタイミング、間隔、レイテンシです。• タイミング サマリには、ターゲットとクロック周波数の見積もりが表示されます。クロック周波数の見積もりがターゲットよりも大きい場合、ハードウェアはこのクロック周波数では機能しません。 この場合、クロック周波数を [Project Settings] の [Data Motion NetworkClock Frequency] オプションを使用して削減する必要があります。ただし、これはフローのこの段階ではあくまでも見積もりにすぎないので、見積もりがターゲットを 20% しか超えていない場合などは、残りのフローを進めることが可能なこともあります。ビットストリームが生成されるときにさらに最適化が適用されるので、タイミング要件を満たす可能性はまだありますが、これはハードウェア関数でタイミングが満たされない可能性があることを示しています。
• 開始間隔 (II) は、関数で新しい入力を受信できるようになるまでのクロック サイクル数で、通常システムの最も重要なパフォーマンス メトリクスです。理想的なハードウェア関数では、ハードウェアでクロック サイクルごとに 1 サンプルのレートでデータが処理されます。ハードウェアに渡される最大容量のデータ セットがサイズ N (例: my_array[N]) の場合、最適な II は N + 1 になります。つまり、ハードウェア関数では N 個のデータ サンプルが N クロック サイクルで処理され、N 個のサンプルすべてが処理された 1 クロック サイクル後に新しいデータを受信できます。II が N 未満のハードウェア関数を作成することはできますが、利点はほとんどなく、必要な PL のリソースが増加します。ハードウェア関数は残りのシステムよりも速いレートでデータを生成して消費するので、ほとんどの場合理想的なものになります。
• ループ開始間隔とは、ループの次の繰り返しでデータの処理が開始するまでのクロック サイクル数です。このメトリクスは、詳細な解析でパフォーマンスのボトルネックを発見して削除する際に重要となります。
• レイテンシとは、関数がすべての出力値を計算するのに必要なクロック サイクル数で、データが適用されてから使用可能になるまでの時間です。ほとんどのアプリケーションでは、ハードウェア関数のレイテンシがソフトウェア関数またはシステム関数 (DMA など) のレイテンシを大きく上回る場合などは特に、これが問題となることはほとんどありませんが、アプリケーションで問題とならないことを確認しておく必要があります。
2: ハードウェア関数の最適化
Vivado HLS 最適化手法ガイド 17UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
• ループ繰り返しレイテンシはループ 1 回分を終了するのにかかるクロック サイクル数、ループ レイテンシはループのすべての繰り返しを実行するのにかかるサイクル数です。
レポートの「Area Estimates」セクションには、ハードウェア関数をインプリメントするのに PLで必要となるリソース数、およびデバイスで使用可能なリソース数が示されます。重要なメトリクスは使用率 (Utilization (%)) で、100% を超えないようにする必要があります。100% を超えている場合は、そのハードウェア関数をインプリメントするのに十分なリソースがないことを示しており、より大型の FPGA デバイスに移行することが必要な可能性があります。これは、タイミングと同様、フローのこの段階ではあくまでも見積もりです。使用率が 100% を少しだけ超えている場合は、ビットストリーム作成中にハードウェアが最適化される可能性があります。ハードウェア関数から、システムに必要なパフォーマンスとメトリクスはわかるはずです。また、クロック サイクルなどのハードウェアの概念を熟知していなくても、高パフォーマンスのハードウェア関数では II = N + 1 (N はその関数で処理される最大データ セット) であることがわかります。現在のデザイン パフォーマンスと基本的なパフォーマンス メトリクスについて理解したら、次はハードウェア関数に最適化指示子を適用します。
メトリクスのための最適化次の表に、デザインに追加するかどうかを最初に考慮する必要のある指示子をリストします。表 4: 最適化ストラテジの手順 1: メトリクスのための最適化
指示子およびコンフィギュレーション 説明LOOP_TRIPCOUNT 可変範囲のループに使用されます。ループの繰り返し回数の見積もりを指定します。これは合成には影響がなく、レポートにのみ影響します。
ハードウェア関数を最初にコンパイルすると、レポート ファイルにレイテンシと開始間隔 (II) が数値ではなくクエスチョン マーク (?) として表示されることがよくあります。デザインに可変範囲のループがある場合は、コンパイラでレイテンシまたは II を判断できず、この状況を示すためにクエスチョン マーク (?) が使用されます。可変範囲のループでは、ループの繰り返し回数の上限が可変の高さ、幅、深さのパラメーターなどのようにハードウェア関数への入力引数であるため、ループの繰り返し回数の上限をコンパイル時に決定できません。この状況を解消するには、ハードウェア関数のレポートで数値が示されていない最下位ループを見つけ、LOOP_TRIPCOUNT 指示子を使用して tripcount の見積もり値を指定します。tripcountは、見積もられる繰り返し回数の最小値、平均値、最大値です。これにより、レイテンシと II の値がレポートされるようになり、さまざまな最適化を適用したインプリメンテーションを比較できるようになります。
2: ハードウェア関数の最適化
Vivado HLS 最適化手法ガイド 18UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
LOOP_TRIPCOUNT 値はレポートにしか使用されず、結果のハードウェア インプリメンテーションには影響がないので、任意の値を使用できますが、より正確な値を使用した方が有益なレポートが得られます。
パフォーマンスのためのパイプライン処理高パフォーマンス デザインを作成する次の段階では、関数、ループ、および演算をパイプライン処理します。パイプライン処理により、同時処理が最大限に実行されるようになり、最高のパフォーマンスを得ることができます。次の表に、パイプライン処理のために使用する指示子を示します。表 5: 最適化ストラテジの手順 2: パフォーマンスのためのパイプライン処理
指示子およびコンフィギュレーション 説明PIPELINE ループまたは関数内で演算を同時に実行できるようにして開始間隔を削減します。DATAFLOW タスク レベルのパイプライン処理を有効にし、関数およびルー
プが同時に実行されるようにします。開始間隔を最小にするために使用します。RESOURCE 変数 (配列、算術演算) をインプリメントするために使用される
ハードウェア リソースのパイプライン処理を指定します。Config Compile ボトムアップ フローを使用する場合に、繰り返し回数に基づい
てループが自動的にパイプライン処理されるようにします。
最適化プロセスのこの段階では、できるだけ多くの同時処理演算が作成されます。PIPELINE 指示子は関数およびループに適用できます。DATAFLOW 指示子を関数およびループを含むレベルで使用すると、それらを並列実行できます。RESOURCE 指示子が必要なことはまれですが、最高レベルのパフォーマンスを達成できるようにするために使用可能です。推奨されるのはボトムアップ方式で、次の点に注意する必要があります。• 関数およびループの中には、サブ関数が含まれるものがあります。サブ関数がパイプライン処理されていないと、それより上位の関数がパイプライン処理されたときにあまり改善が見られないことがあります。これは、サブ関数がパイプライン処理されていないことが原因です。
• 関数およびループの中には、下位ループが含まれるものがあります。PIPELINE 指示子を使用すると、それより下の階層のループすべてが自動的に展開され、かなり多くのロジックが作成される可能性があります。このため、下位階層のループをパイプライン処理することを推奨します。
2: ハードウェア関数の最適化
Vivado HLS 最適化手法ガイド 19UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
• 上位階層をパイプライン処理してその階層より下のループを展開した方が良い場合、可変範囲のループは展開できず、その上の階層のループおよび関数はパイプライン処理できません。この問題を回避するには、可変範囲のループをパイプライン処理し、DATAFLOW 最適化を使用してパイプライン処理されたループが同時に実行されるようにして、ループを含む関数のパフォーマンスを向上します。または、可変範囲を削除するようループを記述し直します。条件付きブレークを使用して最大の上限を適用します。
最適化プロセスのこの段階での基本的なストラテジは、タスク (関数およびループ) をできるだけパイプライン処理することです。どの関数およびループをパイプライン処理するかについての詳細は、ハードウェア関数のパイプライン ストラテジを参照してください。あまり一般的ではありませんが、演算子レベルでパイプライン処理を適用することもできます。たとえば、FPGA のワイヤ配線により予期しない大きな遅延が発生し、デザインを必要なクロック周波数でインプリメントすることが困難な場合があります。このような場合、RESOURCE 指示子を使用して乗算器、加算器、およびブロック RAM などの特定の演算をパイプライン処理してロジック レベルにパイプライン レジスタ段を追加し、ハードウェア関数でデータが再帰の必要なしでできるだけ高いパフォーマンス レベルで処理されるようにします。注記: Config コマンドを使用すると、最適化のデフォルト設定を変更できます。これらのコマンドは、ボトムアップ フローを使用した場合に Vivado HLS 内からのみ使用できます。詳細は、『Vivado Design Suiteユーザー ガイド: 高位合成』 (UG902) を参照してください。
ハードウェア関数のパイプライン ストラテジ高パフォーマンスのデザインを得るのに重要な最適化指示子は、PIPELINE および DATAFLOW指示子です。このセクションでは、さまざまな C コード アーキテクチャにこれらの指示子を適用する方法を説明します。C/C++ 関数には、フレーム ベースとサンプル ベースの 2 種類のコード形式があります。どちらのコーディング スタイルを使用しても、ハードウェア関数は同じパフォーマンスでインプリメントできます。違いは、最適化指示子の適用方法のみです。
2: ハードウェア関数の最適化
Vivado HLS 最適化手法ガイド 20UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
フレーム ベースの C コードフレーム ベースのコーディング スタイルの主な特徴は、各トランザクションで関数により複数のデータ サンプル (1 データ フレーム) が処理されることです。データ フレームは、ポインター演算を使用してアクセスされる、データを含む配列またはポインターとして供給されます。トランザクションは、C 関数の完全な実行 1 回と考えられます。このコーディング スタイルでは、データは通常連続したループまたは入れ子のループで処理されます。次に、フレーム ベースの C コード例を示します。void foo( data_t in1[HEIGHT][WIDTH], data_t in2[HEIGHT][WIDTH], data_t out[HEIGHT][WIDTH] { Loop1: for(int i = 0; i < HEIGHT; i++) { Loop2: for(int j = 0; j < WIDTH; j++) { out[i][j] = in1[i][j] * in2[i][j]; Loop3: for(int k = 0; k < NUM_BITS; k++) { . . . . } } }
C/C++ コードをパイプライン処理してハードウェアでのパフォーマンスを向上する場合は、データのサンプルが処理されるレベルに PIPELINE 最適化指示子を配置します。上記の例は、画像またはビデオ フレームを処理するのに使用されるコードで、ハードウェア関数を効率的にパイプライン処理する方法を示しています。2 つの入力セットがデータ フレームとして関数に供給され、出力もデータ フレームです。この関数をパイプライン処理できる箇所は、次のように複数あります。• foo 関数のレベル• Loop1 ループのレベル• Loop2 ループのレベル• Loop3 ループのレベル
2: ハードウェア関数の最適化
Vivado HLS 最適化手法ガイド 21UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
これらの各箇所に PIPELINE 指示子を配置した場合の長所と短所を考慮することにより、コードのどこにパイプライン指示子を配置するのが最適なのかを判断するのに役立ちます。関数レベル: 関数は、入力としてデータ フレーム (in1 および in2) を受信します。関数が II=1 (各クロック サイクルごとに新しい入力セットを読み込み) でパイプライン処理されると、in1 と in2のすべての HEIGHT*WIDTH 値が 1 つのクロック サイクルで読み込まれるようコンパイルされます。これが意図するデザインであるという可能性は低いはずです。PIPELINE 指示子を関数 foo に適用する場合、この階層の下位にあるループすべてを展開する必要があります。これは、パイプライン内に順序ロジックを存在させることはできないというパイプライン処理の要件です。このため、ロジックのコピーが HEIGHT*WIDTH*NUM_ELEMENT 個作成され、デザインが大きくなります。データは順番にアクセスされるので、ハードウェア関数へのインターフェイスの配列は、次のような複数のハードウェア インターフェイスのタイプとしてインプリメントできます。• ブロック RAM インターフェイス• AXI4 インターフェイス• AXI4-Lite インターフェイス• AXI4 Stream インターフェイス• FIFO インターフェイスブロック RAM インターフェイスは、クロックごとに 2 サンプルを供給可能なデュアル ポートインターフェイスとしてインプリメントできます。その他のインターフェイス タイプでは、クロックごとに 1 サンプルしか供給できないので、これがボトルネックになり、高度に並列化された大型ハードウェア デザインですべてのデータを並列処理できず、ハードウェア リソースが無駄に使用される結果となります。Loop1 レベル: Loop1 内のロジックでは、2 次元行列の行全体が処理されます。ここに PIPELINE指示子を配置すると、クロック サイクルごとに 1 行を処理するデザインが作成されます。これにより、これより下のループは展開されるので、追加のロジックが作成されます。ただし、この追加ロジックを利用するには、クロック サイクルごとにデータの行全体 (各ワードがWIDTH*<number of bits in data_t> ビット幅になる HEIGHT データ ワードの配列) を転送するのが唯一の方法です。PS で実行されるホスト コードがこのような大型データ ワードを処理できることはまれなので、この場合も高度に並列化されたハードウェア リソースで、帯域幅の制限のため並列実行できないという結果になる可能性があります。
2: ハードウェア関数の最適化
Vivado HLS 最適化手法ガイド 22UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
Loop2 レベル: Loop2 内のロジックでは、配列からの 1 サンプルが処理されます。画像アルゴリズムの場合は、これが 1 つのピクセルのレベルです。デザインでクロック サイクルごとに 1 サンプル処理される場合は、これがパイプライン処理を実行するレベルです。これは、インターフェイスが PS に入出力されるデータを消費して生成するレートでもあります。これにより Loop3 が完全に展開されますが、クロックごとに 1 サンプル処理されるようになります。Loop3 の演算がすべて並列処理されることが要件です。典型的なデザインでは、Loop3 のロジックはシフト レジスタで、1 ワード内のビットを処理します。クロックごとに 1 サンプル処理されるようにするには、ループを展開してこれらの処理が並列実行されるようにしてください。Loop2 をパイプライン処理することにより作成されるハードウェア関数では、クロックごとに 1 データ サンプル処理され、必要なデータ スループットを達成するのに必要な場合にのみ並列ロジックが作成されます。Loop3 レベル: 前述のように Loop2 が各データ サンプルまたはピクセルを演算をする場合、Loop3 のロジックでは通常ビット レベル タスクまたはデータ シフト タスクが実行されるので、このレベルではピクセルごとに複数の演算が実行されます。このレベルでパイプライン処理を実行すると、クロックごとにこのループの各演算が 1 回実行されるので、ピクセルごとにNUM_BITS クロックとなり、各ピクセルまたはデータ サンプルが複数クロックのレートで処理されます。たとえば、Loop3 にウィンドウィングまたはたたみ込みアルゴリズム用に前のピクセルを保持するシフト レジスタが含まれているとします。このレベルに PIPELINE 指示子を追加すると、各クロック サイクルでデータ値が 1 つシフトされるようになります。デザインは Loop2 のロジックに戻って NUM_BITS 回の繰り返し後に次の入力を読み込むので、データ処理レートはかなり遅くなります。この例の場合、パイプライン処理する理想的な箇所は Loop2 です。フレーム ベースのコードの場合、ループ レベルでパイプライン処理し、通常はサンプルのレベルで演算を実行するループをパイプライン処理することをお勧めします。確信がない場合は、Cコードに print コマンドを記述し、これが各クロック サイクルで実行するレベルであるかどうかを確認してください。上記の例では入れ子のループのセットが 1 つしかありませんが、階層の同レベルに複数のループがある場合は、ループごとに PIPELINE 指示子を配置するのに最適な場所を決定してから、DATAFLOW 指示子を関数に適用して、各ループが同時に実行されるようにします。
2: ハードウェア関数の最適化
Vivado HLS 最適化手法ガイド 23UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
サンプル ベースの C コード次にサンプル ベースの C コードの例を示します。このコーディング スタイルの主な特徴は、トランザクションごとに関数で 1 つのデータ サンプルが処理されることです。void foo (data_t *in, data_t *out) { static data_t acc; Loop1: for (int i=N-1;i>=0;i--) { acc+= ..some calculation..; } *out=acc>>N;}
サンプル ベースのコーディング スタイルでは、関数にスタティック変数が含まれることがよくあります。スタティック変数の値は、アキュムレータやサンプル カウンターなど、関数呼び出し間で保持される必要があります。サンプル ベースのコードを使用すると、PIPELINE 指示子の位置が明確になり、II = 1 を達成して、クロック サイクルごとに 1 つのデータ値が処理されるようにできます。このためには、関数のパイプライン処理が必要です。パイプライン処理により関数内のループがすべて展開され、追加ロジックが作成されますが、これを回避する方法はありません。Loop1 がパイプライン処理されない場合、完了するのに最低でも N クロック サイクルかかります。この後にのみ、関数は次の x 入力値を読み込むことができます。サンプル レベルで動作する C コードを使用する際は、常に関数をパイプライン処理するようにします。このタイプのコーディング スタイルでは、ループは通常配列に対して実行され、シフト レジスタまたはライン バッファー関数が実行されます。3: パフォーマンスのための構造最適化に示すように、これらの配列を個別のエレメントに分割し、すべてのサンプルが 1 クロック サイクルでシフトされるようにするのが一般的な方法です。配列がブロック RAM にインプリメントされる場合、各クロック サイクルで読み込みまたは書き出しできるのは最大 2 サンプルだけなので、これがデータ処理のボトルネックとなります。この例でのソリューションは、関数 foo をパイプライン処理することです。これにより、クロックごとに 1 サンプル処理するデザインが得られます。
2: ハードウェア関数の最適化
Vivado HLS 最適化手法ガイド 24UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
2: ハードウェア関数の最適化
Vivado HLS 最適化手法ガイド 25UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
第 3 章
パフォーマンスのための構造最適化C コードに、必要なパフォーマンスを達成するための関数またはループのパイプライン処理を妨げるような記述が含まれていることがあります。これは通常、C コードの構造または PL ロジックをインプリメントするのに使用されるデフォルトのロジック構造からわかります。この場合、コードを変更する必要のあることもありますが、ほとんどの場合は追加の最適化指示子を使用することによりこれらの問題を解決できます。次に、最適化指示子を使用してインプリメンテーションの構造およびパイプライン処理のパフォーマンスを改善する例を示します。最初の例では、ループに PIPELINE 指示子を追加して、ループのパフォーマンスを改善しています。このコード例は、関数内で使用されているループを示しています。#include "bottleneck.h"dout_t bottleneck(...) { ... SUM_LOOP: for(i=3;i<N;i=i+4) {#pragma HLS PIPELINE sum += mem[i] + mem[i-1] + mem[i-2] + mem[i-3]; } ...}
上記のコードがハードウェアにコンパイルされると、次のメッセージが表示されます。INFO: [SCHED 61] Pipelining loop 'SUM_LOOP'.WARNING: [SCHED 69] Unable to schedule 'load' operation ('mem_load_2', bottleneck.c:62) on array 'mem' due to limited memory ports.INFO: [SCHED 61] Pipelining result: Target II: 1, Final II: 2, Depth: 3.I
この例の問題は、配列が PL ファブリックの効率的なブロック RAM リソースを使用してインプリメントされていることです。これにより、小型でコスト パフォーマンスの高い高速デザインが得られます。ただし、ブロック RAM では、DDR や SRAM などのメモリと同様、データ ポート数が通常 2 までに制限されます。上記のコードでは、sum の値を計算するのに mem からの 4 つのデータ値が必要です。mem は配列であり、データ ポート数が 2 つのみのブロック RAM にインプリメントされるので、各クロック サイクルで読み出しまたは書き込みできるのは 2 つの値のみです。この構成では、sum の値を 1 クロック サイクルで計算することは不可能であり、データの消費または生成の開始間隔(II) は 1 です (クロックごとに 1 つのデータ サンプルを処理)。
3: パフォーマンスのための構造最適化
Vivado HLS 最適化手法ガイド 26UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
メモリ ポートの制限による問題は、配列 mem に ARRAY_PARTITION 指示子を使用すると解決できます。この指示子を使用すると、配列が複数の小型の配列に分割され、データ ポート数が増加し、高パフォーマンスのパイプライン処理が可能になります。次に示すように指示子を追加すると、配列 mem が 2 つのデュアル ポート メモリに分割され、4つの読み出しすべてを 1 クロック サイクルで実行できるようになります。配列を分割するには、複数のオプションがあります。この例では、係数 2 での循環分割により、最初のパーティションに元の配列からの要素 0、2、4 などが含まれ、2 つ目のパーティションに要素 1、3、5 などが含まれます。分割することにより 2 つのデュアル ポート ブロック RAM (合計 4 つのデータ ポート) が使用されるようになるので、要素 0、1、2、および 3 を 1 つのクロック サイクルで読み出すことができます。注記: アクセラレータとして選択された関数の引数である配列に対しては、ARRAY_PARTITION 指示子は使用できません。#include "bottleneck.h"dout_t bottleneck(...) {#pragma HLS ARRAY_PARTITION variable=mem cyclic factor=2 dim=1 ... SUM_LOOP: for(i=3;i<N;i=i+4) {#pragma HLS PIPELINE sum += mem[i] + mem[i-1] + mem[i-2] + mem[i-3]; } ...}
ループおよび関数をパイプライン処理する際には、ほかにも問題が発生する可能性があります。次の表に、これらの問題に対処するのに有益な、データ構造のボトルネックを削減する指示子をリストします。表 6: 最適化ストラテジの手順 3: パフォーマンスのための構造最適化
指示子およびコンフィギュレーション 説明ARRAY_PARTITION 大型の配列を複数の配列または個別のレジスタに分割し、データへのアクセスを改善してブロック RAM のボトルネックを削
除します。DEPENDENCE ループ キャリー依存性を克服し、ループをパイプライン処理で
きるようにする (またはより短い間隔でパイプラインできるようにする) 追加情報を提供します。
INLINE 関数をインライン化し、関数の階層をすべて削除します。関数の境界を超えたロジック最適化をイネーブルにし、関数呼び出しのオーバーヘッドを削減することによりレイテンシ/間隔を改善します。
UNROLL for ループを展開し、複数の演算を 1 つにまとめたものではなく、複数の個別の演算を作成して、ハードウェアの並列化を向上します。これにより、ループの部分展開が可能になります。
Config Array Partition グローバル配列を含めた配列の分割方法と、分割が配列ポートに影響するかどうかを指定します。Config Compile 自動ループ パイプラインおよび浮動小数点の math 最適化な
ど、合成特有の最適化を制御します。
3: パフォーマンスのための構造最適化
Vivado HLS 最適化手法ガイド 27UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
表 6: 最適化ストラテジの手順 3: パフォーマンスのための構造最適化 (続き)
指示子およびコンフィギュレーション 説明Config Schedule 合成のスケジューリング段階で使用するエフォート レベル、出
力メッセージの詳細度、およびタイミングを満たすためにパイプライン処理されたタスクの開始間隔 (II) を緩和するかどうかを指定します。
Config Unroll 指定したループ繰り返し数以下のすべてのループを展開します。
配列の自動分割には、ARRAY_PARTITION 指示子だけでなく、配列分割のコンフィギュレーション (Config Array Partition) も使用できます。ループをパイプライン処理する際に暗示される依存性を削除するため、DEPENDENCE 指示子が必要な場合があります。このような依存性は、次のように SCHED-68 メッセージでレポートされます。@W [SCHED-68] Target II not met due to carried dependence(s)
INLINE 指示子を使用すると、関数の境界が削除されます。これは、ロジックまたはループを 1レベル上の階層に移動するために使用できます。ロジックをその上の関数に含め、ループを上の階層に統合すると、上の階層では中間サブ関数呼び出しのオーバーヘッドなしですべてのループを同時実行する DATAFLOW 最適化を使用できるので、関数内のロジックをより効率的にパイプライン処理できるようになる場合があります。これにより、デザインのパフォーマンスを向上できる可能性があります。ループを必要な開始間隔 (II) でパイプライン処理できない場合は、UNROLL 指示子が必要である可能性があります。ループをパイプライン処理しても II=4 しか達成できない場合、システム内のその他のループおよび関数も II=4 に制約されます。ループを展開するとさらにロジックが作成されますが、ボトルネックは削除されるので、場合によってはループを展開または部分展開すると有益です。ループをパイプライン処理しても II=4 しか達成できない場合、係数 4 を使用してループを展開すると、ループの 4 つの繰り返しを並列処理するロジックが作成され、II=1 を達成できます。Config コマンドを使用すると、最適化のデフォルト設定を変更できます。これらのコマンドは、ボトムアップ フローを使用した場合に Vivado HLS 内からのみ使用できます。詳細は、『VivadoDesign Suite ユーザー ガイド: 高位合成』 (UG902) を参照してください。開始間隔 (II) を改善するために最適化指示子を使用できない場合、コードの変更が必要となる可能性があります。この例が『Vivado Design Suite ユーザー ガイド: 高位合成』 (UG902) に示されています。
3: パフォーマンスのための構造最適化
Vivado HLS 最適化手法ガイド 28UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
レイテンシの削減コンパイラで開始間隔 (II) を最小限に抑える処理が終了すると、レイテンシを最小限に抑えるための処理が実行されます。次の表にリストされる最適化指示子を使用すると、特定のレイテンシを指定したり、生成されたレイテンシよりも短いレイテンシを達成するように指定して、結果のII が大きくなってもレイテンシ指示子を満たすようにされるようにできます。これにより、デザインのパフォーマンスが低下する可能性があります。ほとんどのアプリケーションにはスループット要件はありますが、レイテンシ要件はないので、レイテンシ指示子は通常必要ありません。ハードウェア関数がプロセッサと統合される場合、プロセッサのレイテンシが通常システムの制限要因となります。ループおよび関数がパイプライン処理されない場合、現在のタスクが完了するまで次の入力セットを読み込むことはできないので、スループットがレイテンシにより制限されます。表 7: 最適化ストラテジの手順 4: レイテンシの削減
指示子 説明LATENCY 最小および最大レイテンシ制約を指定します。LOOP_FLATTEN 入れ子のループを 1 つのループに展開して、ループ遷移のオーバ
ーヘッドを削減し、レイテンシを改善します。入れ子のループは、PIPELINE 指示子を適用すると、自動的にフラットになります。
LOOP_MERGE 連続するループを結合して、全体的なレイテンシを削減し、ロジック リソースの共有を増やして最適化を向上します。
ループ最適化指示子は、ループ階層をフラットにしたり、連続するループを結合するために使用できます。レイテンシを向上できるのは、通常ループで作成されたロジックに入って出るまでに制御ロジックで 1 クロック サイクル費やされるからです。ループ間の遷移数が少ないほど、デザインが完了するまでにかかるクロック数も少なくなります。
3: パフォーマンスのための構造最適化
Vivado HLS 最適化手法ガイド 29UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
エリアの削減ハードウェアでは、ロジック関数をインプリメントするのに必要なリソース数はデザイン エリアと呼ばれます。デザイン エリアは、決まったサイズの PL ファブリックでどれだけのリソースが使用されるかも意味します。エリアは、ハードウェアがターゲット デバイスにインプリメントするには大きすぎる場合、およびハードウェア関数が使用可能なエリアをかなり高い割合 (>90%) で消費している場合などに重要となります。この場合、ハードウェア ロジックどうしをワイヤ接続しようとすると、ワイヤ自体がリソースを必要とするので、接続が困難になります。必要なパフォーマンス ターゲット (または開始間隔 (II)) が満たされたら、次は同じパフォーマンスを維持しながらエリアを削減します。ただし、ハードウェア関数が必要なパフォーマンスで動作していて、残りの PL のスペースにインプリメントされるハードウェア関数がほかにない場合は、エリアを削減しても意味がないので、この段階はオプションです。最もよく使用されるエリア最適化は、データフロー メモリ チャネルの最適化で、ハードウェア関数をインプリメントするのに必要なブロック RAM リソース数を削減できます。各デバイスのブロック RAM リソースの数には制限があります。DATAFLOW 最適化を使用している場合に、デザインのタスクがストリーミング データであるかどうかをコンパイラで判断できない場合は、ピンポン バッファーを使用してデータフロー タスク間にメモリ チャネルがインプリメントされます。これには、それぞれサイズ N (N はタスク間を転送されるサンプル数で、通常はタスク間で渡される配列のサイズ) の 2 つのブロックが必要です。デザインがパイプライン処理されて、データが 1 つのタスクから次のタスクにストリーミングされて値がシーケンシャルに生成および消費される場合は、STREAM 指示子を使用して配列がストリーミング方式 (深さを指定可能な単純な FIFO を使用) でインプリメントされるように指定すると、エリアを大幅に削減できます。レジスタを使用して浅い FIFO がインプリメントされます。PL ファブリックには、多数のレジスタが含まれています。ほとんどのアプリケーションでは、深さを 1 に指定すると、メモリ チャネルが単純なレジスタとしてインプリメントされます。ただし、アルゴリズムでデータ圧縮または外挿がインプリメントされ、生成されるよりも多くのデータが消費されたり、消費されるよりも多くのデータが生成されるようなタスクがある場合は、一部の配列の深さを大きくする必要があります。• 同じレートでデータを生成および消費するタスクでは、それらの間に配列を指定して深さ 1でストリーミングされるようにします。
• データ レートを X:1 で削減するタスクでは、タスクの入力に配列を指定して、深さ X でストリーミングされるようにします。関数内のこれより前の配列の深さもすべて X にして、FIFOがフルになったためにハードウェア関数が停止することがないようにします。
• データ レートを 1:Y で増加するタスクでは、タスクの出力に配列を指定して、深さ Y でストリーミングされるようにします。関数内のこれより後の配列の深さもすべて Y にして、FIFOがフルになったためにハードウェア関数が停止することがないようにします。
3: パフォーマンスのための構造最適化
Vivado HLS 最適化手法ガイド 30UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
注記: 設定した深さが小さすぎると、FIFO がフルになって残りのシステムが待機状態になるため、ハードウェア エミュレーション中にハードウェア関数が停止してパフォーマンスが低下したり、場合によってはデッドロック状態になることがあります。次の表に、デザインをインプリメントするために使用されるリソースを最小限に抑える場合に考慮すべきその他の指示子を示します。表 8: 最適化ストラテジの手順 5: エリアの削減
指示子およびコンフィギュレーション 説明ALLOCATION 使用される演算、ハードウェア リソース、または関数の数を制
限します。これによりハードウェア リソースが強制的に共有されますが、レイテンシは増加する可能性があります。
ARRAY_MAP 複数の小型の配列を 1 つの大型の配列に結合し、ブロック RAMリソース数を削減します。
ARRAY_RESHAPE 配列を多数の要素を含むものからワード幅の広いものに変更します。ブロック RAM 数を増やさずにブロック RAM アクセスを向上するのに有益です。
DATA_PACK 内部構造体のデータ フィールドをワード幅の広い 1 つのスカラーにパックして、1 つの制御信号ですべてのフィールドを制御できるようにします。
LOOP_MERGE 連続するループを結合して、全体的なレイテンシを削減し、共有を増やして最適化を向上します。OCCURRENCE 関数またはループをパイプライン処理する際に、あるロケーションのコードがそれを含む関数またはループのコードよりも低速で実行されるように指定します。RESOURCE 変数 (配列、算術演算) をインプリメントするために使用される
特定のハードウェア リソース (コア) を指定します。STREAM 特定のメモリ チャネルを FIFO (オプションで深さを指定) とし
てインプリメントするよう指定します。Config Bind 合成のバインド段階で使用するエフォート レベルを指定しま
す。使用される演算数をグローバルに最小限に抑えるために使用します。Config Dataflow DATAFLOW 最適化でのデフォルトのメモリ チャネルと FIFO
の深さを指定します。
演算数を制限し、演算をインプリメントするのに使用するコア (ハードウェア リソース) を選択するには、ALLOCATION と RESOURCE 指示子を使用します。たとえば、関数またはループに乗算器が 1 つだけ使用されるように制限し、パイプライン乗算器を使用してインプリメントされるよう指定できます。開始間隔を向上するために ARRAY_PARITION 指示子を使用する場合は、その代わりにARRAY_RESHAPE 指示子を使用することも考慮してみてください。ARRAY_RESHAPE 最適化では、配列の分割と同様のタスクが実行されますが、分割により作成された要素がより幅の広いデータ ポートを持つ 1 つのブロック RAM に再結合され、必要な RAM リソース数が増加しないようにすることができる可能性があります。
3: パフォーマンスのための構造最適化
Vivado HLS 最適化手法ガイド 31UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
C コードに類似のインデックスを持つ一連のループが含まれる場合、LOOP_MERGE 指示子を使用してループを結合することにより実行できるようになる最適化もあります。最後に、パイプライン領域のコードの一部が、領域の残りの部分よりも小さい開始間隔で動作する場合は、OCCURENCE 指示子を使用して、このロジックが低いレートで実行されるよう最適化できます。注記: Config コマンドを使用すると、最適化のデフォルト設定を変更できます。これらのコマンドは、ボトムアップ フローを使用した場合に Vivado HLS 内からのみ使用できます。詳細は、『Vivado Design Suiteユーザー ガイド: 高位合成』 (UG902) を参照してください。
デザイン最適化のワークフロー最適化を実行する前に、プロジェクト内に新しいビルド コンフィギュレーションを作成することをお勧めします。別のビルド コンフィギュレーションを使用すると、結果のセットを異なる結果のセットと比較できます。[Manage Build Configurations for the Project] ツールバー ボタンを使用すると、標準の Debug および Release コンフィギュレーションに加えて、より便利な名前 (Opt_ver1 および UnOpt_ver など) のカスタム コンフィギュレーションを [Project Settings]ダイアログ ボックスに作成できます。異なるビルド コンフィギュレーションを使用すると、結果だけでなく、ログ ファイルや FPGAのインプリメントに使用される出力 RTL ファイルも比較できます (RTL ファイルはハードウェア デザインを熟知したユーザーにのみ推奨)。高パフォーマンス デザインを得るための基本的な最適化ストラテジは、次のとおりです。• 初期またはベースライン デザインを作成します。• ループおよび関数をパイプライン処理します。DATAFLOW 最適化を適用してループおよび関数が同時に実行されるようにします。
• 配列のボトルネックやループの依存性など、パイプラインを制限する問題を解決します(ARRAY_PARTITION および DEPENDENCE 指示子を使用)。
• 特定のレイテンシを指定するか、データフロー メモリ チャネルのサイズを削減し、ALLOCATION および RESOUCES 指示子を使用してさらにエリアを削減します。
注記: パフォーマンスを満たすため、必要に応じてコードを変更します。エリアを削減するよりも前に、まずパフォーマンスを満たすようにします。できるだけ少ないリソースでデザインを作成することがストラテジである場合は、パフォーマンスを改善する手順を実行しないようにします。ただし、ベースラインの結果は最小のデザインに近いものとなります。
3: パフォーマンスのための構造最適化
Vivado HLS 最適化手法ガイド 32UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
最適化プロセス中は、コンパイル後にコンソールへの出力 (またはログ ファイル) を確認することをお勧めします。コンパイラで指定した最適化のパフォーマンス目標が達成できなかった場合、目標が自動的に緩和され (クロック周波数は例外)、達成できる目標でデザインが作成されます。このため、コンパイルのログ ファイルとレポートを確認して、どのような最適化が実行されたのか理解しておくことが重要です。プログラム機能の特有は、『Vivado Design Suite ユーザー ガイド: 高位合成』 (UG902) を参照してください。
3: パフォーマンスのための構造最適化
Vivado HLS 最適化手法ガイド 33UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
第 4 章
データ アクセス パターンFPGA では大量の並列アーキテクチャによりプロセッサのシーケンシャル演算よりもかなり高速に演算を実行できるので、そのパフォーマンスを活用するため、C コードのインプリメントにFPGA が選択されます。ここでは、C コードのアクセス パターンが結果にどう影響するかについて説明します。最も注意が必要なアクセス パターンはハードウェア関数への入力および出力のパターンですが、ハードウェア関数内にボトルネックがあると、関数への入力および出力の転送レートに悪影響を及ぼすので、関数内のアクセス パターンを考慮することをお勧めします。このセクションでは画像のたたみ込みアルゴリズムについて説明し、データ アクセス パターンがパフォーマンスに悪影響を与え、適切なパターンを使用することにより FPGA の並列化およびパフォーマンス機能を活用できることを示します。• まずアルゴリズムを確認し、FPGA でのパフォーマンスの制限となるデータ アクセスについて説明します。
• 次に、最高のパフォーマンスを達成するためのアルゴリズムの記述方法について説明します。
不適切なデータ アクセス パターンを使用したアルゴリズムここでは画像に適用される標準的なたたみ込み関数を使用して、FPGA で可能なパフォーマンスが C コードによりどのように劣化するかを説明します。この例では、データに対してまず水平たたみ込みが実行された後、垂直たたみ込みが実行されます。画像のエッジのデータはたたみ込み範囲外にあるので、最後に境界周辺のデータが処理されます。アルゴリズムでの処理は、次の順で実行されます。• 水平たたみ込み• 垂直たたみ込み
4: データ アクセス パターン
Vivado HLS 最適化手法ガイド 34UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
• 境界ピクセル処理static void convolution_orig( int width, int height, const T *src, T *dst, const T *hcoeff, const T *vcoeff) { T local[MAX_IMG_ROWS*MAX_IMG_COLS];
// Horizontal convolution HconvH:for(int row = 0; row < height; row++){ HconvWfor(int col = border_width; col < width - border_width; col++){ Hconv:for(int i = - border_width; i <= border_width; i++){ ... } } }
// Vertical convolution VconvH:for(int row = border_width; row < height - border_width; row++){ VconvW:for(int col = 0; col < width; col++){ Vconv:for(int i = - border_width; i <= border_width; i++){ } } }
// Border pixels Top_Border:for(int col = 0; col < border_width; col++){ } Side_Border:for(int col = border_width; col < height - border_width; col++){ } Bottom_Border:for(int col = height - border_width; col < height; col++){ }}
標準水平たたみ込み最初に、次の図のように水平方向のたたみ込みを実行します。
4: データ アクセス パターン
Vivado HLS 最適化手法ガイド 35UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
First Output Second Output Final Output
src
Hsamp
local
Hcoeff
Hsamp
Hcoeff
Hsamp
Hcoeff
X14296-121417
たたみ込みは、K 個のデータ サンプルと K 個のたたみ込み係数を使用して実行されます。上の図では K の値は 5 ですが、この値はコードで定義されます。たたみ込みを実行するには、K 個以上のデータ サンプルが必要です。たたみ込みウィンドウは、画像外にあるピクセルを含む必要があるため、最初のピクセルでは開始できません。対称たたみ込みを実行すると、src 入力からの最初の K 個のデータ サンプルが水平係数でたたみ込まれ、最初の出力が計算されます。2 つ目の出力を計算するには、次の K 個のデータ サンプルが使用されます。この計算は、最後の出力が書き込まれるまで各行に対して実行されます。この操作を実行する C コードは次のとおりです。const int conv_size = K;
const int border_width = int(conv_size / 2);
#ifndef __SYNTHESIS__
4: データ アクセス パターン
Vivado HLS 最適化手法ガイド 36UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
T * const local = new T[MAX_IMG_ROWS*MAX_IMG_COLS];
#else // Static storage allocation for HLS, dynamic otherwise
T local[MAX_IMG_ROWS*MAX_IMG_COLS];
#endif
Clear_Local:for(int i = 0; i < height * width; i++){
local[i]=0;
}
// Horizontal convolution
HconvH:for(int col = 0; col < height; col++){
HconvWfor(int row = border_width; row < width - border_width; row++){
int pixel = col * width + row;
Hconv:for(int i = - border_width; i <= border_width; i++){
local[pixel] += src[pixel + i] * hcoeff[i + border_width];
}
}
}
このコードは簡単でわかりやすいものですが、ハードウェアの結果の質に悪影響を及ぼす問題がいくつかあります。1 つ目の問題は、C コンパイル中に大型ストレージが必要であるということです。アルゴリズムの中間結果は内部 local 配列に格納されます。HEIGHT*WIDTH の配列が必要で、1920*1080 の標準ビデオ画像では 2,073,600 の値が保持されます。• Zynq®-7000 All Programmable SoC または Zynq UltraScale+™ MPSoC をターゲットとするクロス コンパイラおよび多くのホスト システムでは、この量のローカル ストレージのためランタイムでスタック オーバーフローが発生します (ターゲット デバイス上での実行、VivadoHLS 内での協調シミュレーションの実行など)。local 配列のデータはスタックに配置され、OS で管理されるヒープには含まれません。arm-linux-gnueabihf-g++ でクロス コンパイルする際は、-Wl,"-z stacksize=4194304" リンカー オプションを使用して十分なスタック空間を割り当てます。このオプションの構文は、リンカーによって異なります。関数がハードウェアでのみ実行される場合は、__SYNTHESIS__ マクロを使用するとこのような問題を回避できます。このマクロは、ハードウェア関数がハードウェアに合成されるときにシ
4: データ アクセス パターン
Vivado HLS 最適化手法ガイド 37UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
ステム コンパイラにより自動的に定義されます。上記のコードでは、C シミュレーション中にダイナミック メモリ割り当てを使用してコンパイルの問題を回避しており、合成中はスタティック ストレージのみが使用されます。このマクロを使用する短所は、C シミュレーションで検証されたコードが合成されるコードとは異なるものになることです。この例の場合はコードは複雑ではないので、動作は同じになります。
• この local 配列の主な問題は、FPGA インプリメンテーションの質です。これは配列なので、内部 FPGA ブロック RAM を使用してインプリメントされます。これは、FPGA 内にインプリメントするにはかなり大きいメモリであり、より大型でコストのかかる FPGA デバイスが必要となる可能性があります。DATAFLOW 最適化を使用して、小型で効率的な FIFO を介してデータをストリーミングすると、ブロック RAM の使用を最小限に抑えることはできますが、データがストリーミングでシーケンシャルに使用されるようにする必要があります。現在のところ、そのような要件はありません。
2 つ目の問題は、パフォーマンスと local 配列の初期化に関するものです。Clear_Local ループはlocal 配列の値を 0 に設定するために使用されます。高パフォーマンスで実行するためにこのループをハードウェアでパイプライン処理しても、この操作をインプリメントするのに約 2 百万クロック サイクル必要です。このメモリの初期化中、システムで画像処理を実行することはできません。同じデータの初期化は、HConv ループ内の一時的な変数を使用して、書き出し前に累積を初期化することにより実行できます。最後の問題は、データのスループット、つまりシステム パフォーマンスがデータ アクセス パターンにより制限されることです。• 最初のたたみ込み出力を作成するため、最初の K 個の値が入力から読み出されます。• 2 つ目の出力の計算には、新しい値が読み出され、その後同じ K-1 個の値が再度読み出されます。
高パフォーマンスの FPGA を得るには、PS へのアクセスを最小限に抑えることが重要です。前に既にフェッチされたデータに再度アクセスすることは、システムのパフォーマンスに悪影響を与えます。FPGA では多数の計算を同時に実行して高パフォーマンスを達成することが可能ですが、データのフローが値を再読み出しするために頻繁に中断されると高パフォーマンスは得られません。注記: 高パフォーマンスを達成するには、PS からのデータにアクセスするのは 1 回のみにし、小型のローカル ストレージ (小型から中型のサイズの配列) に格納して再利用する必要があります。上記のコードでは、データを何度も読み出す必要があるので、DMA 操作を使用してプロセッサから直接ストリーミングできません。
4: データ アクセス パターン
Vivado HLS 最適化手法ガイド 38UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
標準垂直たたみ込み次の段階では、次の図に示す垂直たたみ込みを実行します。
First Output Second Output Final Output
local
Vsamp
dst
Vcoeff
Vsamp
Vcoeff
Vsamp
Vconv
X14299-110617
垂直たたみ込みのプロセスは、水平たたみ込みと似ています。たたみ込み係数 (この場合はVcoeff) を使用したたたみ込みには、K 個のデータ サンプルが必要です。垂直方向の最初の K 個のサンプルを使用して最初の出力が作成された後、次の K 個の値を使用して 2 つ目の出力が作成されます。この処理は、最後の出力が作成されるまで各列に対して実行されます。水平および垂直の境界効果により、垂直たたみ込み後の画像はソース画像 src よりも小さくなります。これらの処理を実行するコードは、次のとおりです。Clear_Dst:for(int i = 0; i < height * width; i++){ dst[i]=0;}// Vertical convolution
4: データ アクセス パターン
Vivado HLS 最適化手法ガイド 39UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
VconvH:for(int col = border_width; col < height - border_width; col++){ VconvW:for(int row = 0; row < width; row++){ int pixel = col * width + row; Vconv:for(int i = - border_width; i <= border_width; i++){ int offset = i * width; dst[pixel] += local[pixel + offset] * vcoeff[i + border_width]; } }}
このコードには、水平たたみ込みコードを使用して既に説明した問題と同様の問題があります。• 出力画像 dst の値 を 0 に設定するのに、多数のクロック サイクルが費やされます。この場合、1920*1080 画像サイズに対してさらに約 2 百万サイクル必要です。
• local 配列に格納されたデータを再度読み出すために、各ピクセルが複数回アクセスされます。
• 出力配列/ポート dst に対しても、各ピクセルが複数回書き込まれます。上記のコードのアクセス パターンでは、大型の local 配列が必要となります。アルゴリズムでは、最初の計算を実行するために行 K のデータが使用可能になっていることが必要です。次の列に進む前に各行のデータを処理するため、画像全体がローカルに格納されている必要があります。そのためすべての値が格納されるので、FPGA に大型のローカル ストレージが作成されます。さらに、local 配列からデータがストリーミング出力されないので、コンパイラ指示子を使用してハードウェア関数のパフォーマンスを最適化する段階に達したときに、水平および垂直ループ間のデータのフローを FIFO (高パフォーマンスで低リソースのユニット) を使用して制御することができません。FIFO は、シーケンシャル アクセス パターンでのみ使用可能です。このコードでは任意/ランダム アクセスが必要なため、パフォーマンスを向上するにはピンポン ブロックRAM が必要です。ローカル配列のインプリメンテーションに必要なメモリが 2 倍の約 4 百万個のデータ サンプルになり、1 つの FPGA には大きすぎます。
4: データ アクセス パターン
Vivado HLS 最適化手法ガイド 40UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
標準境界ピクセルたたみ込みたたみ込みの最後の段階では、境界周辺のデータを作成します。これらのピクセルは、たたみ込み出力の最も近いピクセルを再利用することにより作成されます。次の図に、これをどのように達成するかを示します。
Top Left Top Row Top Right
Left and Right Edges Bottom Left and Bottom Row Bottom Right
dst
dst
X14294-121417
境界領域は、最も近い有効な値を使用して作成されます。図に示す操作は、次のコードで実行されます。int border_width_offset = border_width * width;int border_height_offset = (height - border_width - 1) * width;
// Border pixels
Top_Border:for(int col = 0; col < border_width; col++){ int offset = col * width; for(int row = 0; row < border_width; row++){ int pixel = offset + row; dst[pixel] = dst[border_width_offset + border_width]; } for(int row = border_width; row < width - border_width; row++){ int pixel = offset + row; dst[pixel] = dst[border_width_offset + row]; }
4: データ アクセス パターン
Vivado HLS 最適化手法ガイド 41UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
for(int row = width - border_width; row < width; row++){ int pixel = offset + row; dst[pixel] = dst[border_width_offset + width - border_width - 1]; }}
Side_Border:for(int col = border_width; col < height - border_width; col++){ int offset = col * width; for(int row = 0; row < border_width; row++){ int pixel = offset + row; dst[pixel] = dst[offset + border_width]; } for(int row = width - border_width; row < width; row++){ int pixel = offset + row; dst[pixel] = dst[offset + width - border_width - 1]; }}
Bottom_Border:for(int col = height - border_width; col < height; col++){ int offset = col * width; for(int row = 0; row < border_width; row++){ int pixel = offset + row; dst[pixel] = dst[border_height_offset + border_width]; } for(int row = border_width; row < width - border_width; row++){ int pixel = offset + row; dst[pixel] = dst[border_height_offset + row]; } for(int row = width - border_width; row < width; row++){ int pixel = offset + row; dst[pixel] = dst[border_height_offset + width - border_width - 1]; }}
このコードには、データに繰り返しアクセスするという同じ問題があります。FPGA 外の dst配列に格納されたデータは、入力データとして複数回読み出されることが可能になっている必要があります。最初のループでも、dst[border_width_offset + border_width] が複数回読み出されますが、border_width_offset および border_width の値は変更されません。このコードは、読み出しと書き込みのどちらも非常にわかりやすいものです。SDSoC 環境でインプリメントすると約 120M クロック サイクルであり、CPU のパフォーマンスよりも少し長くなります。ただし、次のセクションで示すように最適なデータ アクセス パターンを使用すると、同じアルゴリズムをクロック サイクルごとに 1 ピクセルのレート (約 2M クロック サイクル) でFPGA にインプリメントできます。このように、次のような不適切なデータ アクセス パターンを使用すると、パフォーマンスが低下し、FPGA インプリメンテーションのサイズが大きくなります。• データを繰り返し読み出すために複数回アクセスする。可能な場合は、ローカル ストレージを使用してください。
• データに任意またはランダムにアクセスする。データをローカルに配列として格納する必要があり、リソースが多く必要になります。
4: データ アクセス パターン
Vivado HLS 最適化手法ガイド 42UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
• 配列のデフォルト値を設定する。クロック サイクル数が多くなり、パフォーマンスが低下します。
最適なデータ アクセス パターンを使用したアルゴリズム前のセクションで説明したたたみ込みの例 (リソース使用量が最小の高パフォーマンス デザイン) をインプリメントするには、次の点が重要です。• システム中のデータフローを最大限にします。データフローが途切れてしまうようなコーディング手法やアルゴリズム処理は避けてください。
• データの再利用を最大限にします。ローカル キャッシュを使用して、同じデータを何回も読み出す必要がないようにし、入力データのフローが途切れないようにします。
• 条件分岐を含めます。これは CPU、GPU、または DSP ではコストがかかりますが、FPGA では最適な方法です。
最初の段階では、システムから FPGA、FPGA からシステムにデータがどのように流れるかを理解します。たたみ込みアルゴリズムは、画像に対して実行されます。画像からのデータは、次の図に示すような標準のラスター走査順序で転送されます。
Width
Height
X14298-121417
4: データ アクセス パターン
Vivado HLS 最適化手法ガイド 43UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
データが FPGA にストリーミング方式で転送された場合、FPGA ではそれをストリーミング方式で処理し、同じ方式で転送し戻す必要があります。次に示すたたみ込みアルゴリズムでは、このコード形式を使用しています。この抽象レベルでは、コードが簡潔に示されますが、各ループ間に中間バッファー hconv および vconv があります。これらはストリーミング方式でアクセスされるので、最終的なインプリメンテーションでは1 つのレジスタに最適化されます。template<typename T, int K>static void convolution_strm(int width,int height,T src[TEST_IMG_ROWS][TEST_IMG_COLS],T dst[TEST_IMG_ROWS][TEST_IMG_COLS],const T *hcoeff,const T *vcoeff){
T hconv_buffer[MAX_IMG_COLS*MAX_IMG_ROWS];T vconv_buffer[MAX_IMG_COLS*MAX_IMG_ROWS];T *phconv, *pvconv;
// These assertions let HLS know the upper bounds of loopsassert(height < MAX_IMG_ROWS);assert(width < MAX_IMG_COLS);assert(vconv_xlim < MAX_IMG_COLS - (K - 1));// Horizontal convolutionHConvH:for(int col = 0; col < height; col++) { HConvW:for(int row = 0; row < width; row++) { HConv:for(int i = 0; i < K; i++) { } }}// Vertical convolutionVConvH:for(int col = 0; col < height; col++) { VConvW:for(int row = 0; row < vconv_xlim; row++) { VConv:for(int i = 0; i < K; i++) { } }}Border:for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { }}
これで 3 つの処理ループに条件分岐が含まれ、データが連続処理されるようになります。
4: データ アクセス パターン
Vivado HLS 最適化手法ガイド 44UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
4: データ アクセス パターン
Vivado HLS 最適化手法ガイド 45UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
第 5 章
標準水平たたみ込み最初に、次の図のように水平方向のたたみ込みを実行します。
5: 標準水平たたみ込み
Vivado HLS 最適化手法ガイド 46UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
First Output Second Output Final Output
src
Hsamp
local
Hcoeff
Hsamp
Hcoeff
Hsamp
Hcoeff
X14296-121417
たたみ込みは、K 個のデータ サンプルと K 個のたたみ込み係数を使用して実行されます。上の図では K の値は 5 ですが、この値はコードで定義されます。たたみ込みを実行するには、K 個以上のデータ サンプルが必要です。たたみ込みウィンドウは、画像外にあるピクセルを含む必要があるため、最初のピクセルでは開始できません。対称たたみ込みを実行すると、src 入力からの最初の K 個のデータ サンプルが水平係数でたたみ込まれ、最初の出力が計算されます。2 つ目の出力を計算するには、次の K 個のデータ サンプルが使用されます。この計算は、最後の出力が書き込まれるまで各行に対して実行されます。この操作を実行する C コードは次のとおりです。const int conv_size = K;
const int border_width = int(conv_size / 2);
#ifndef __SYNTHESIS__
5: 標準水平たたみ込み
Vivado HLS 最適化手法ガイド 47UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
T * const local = new T[MAX_IMG_ROWS*MAX_IMG_COLS];
#else // Static storage allocation for HLS, dynamic otherwise
T local[MAX_IMG_ROWS*MAX_IMG_COLS];
#endif
Clear_Local:for(int i = 0; i < height * width; i++){
local[i]=0;
}
// Horizontal convolution
HconvH:for(int col = 0; col < height; col++){
HconvWfor(int row = border_width; row < width - border_width; row++){
int pixel = col * width + row;
Hconv:for(int i = - border_width; i <= border_width; i++){
local[pixel] += src[pixel + i] * hcoeff[i + border_width];
}
}
}
このコードは簡単でわかりやすいものですが、ハードウェアの結果の質に悪影響を及ぼす問題がいくつかあります。1 つ目の問題は、C コンパイル中に大型ストレージが必要であるということです。アルゴリズムの中間結果は内部 local 配列に格納されます。HEIGHT*WIDTH の配列が必要で、1920*1080 の標準ビデオ画像では 2,073,600 の値が保持されます。• Zynq®-7000 All Programmable SoC または Zynq UltraScale+™ MPSoC をターゲットとするクロス コンパイラおよび多くのホスト システムでは、この量のローカル ストレージのためランタイムでスタック オーバーフローが発生します (ターゲット デバイス上での実行、VivadoHLS 内での協調シミュレーションの実行など)。local 配列のデータはスタックに配置され、OS で管理されるヒープには含まれません。arm-linux-gnueabihf-g++ でクロス コンパイルする際は、-Wl,"-z stacksize=4194304" リンカー オプションを使用して十分なスタック空間を割り当てます。このオプションの構文は、リンカーによって異なります。関数がハードウェアでのみ実行される場合は、__SYNTHESIS__ マクロを使用するとこのような問題を回避できます。このマクロは、ハードウェア関数がハードウェアに合成されるときにシ
5: 標準水平たたみ込み
Vivado HLS 最適化手法ガイド 48UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
ステム コンパイラにより自動的に定義されます。上記のコードでは、C シミュレーション中にダイナミック メモリ割り当てを使用してコンパイルの問題を回避しており、合成中はスタティック ストレージのみが使用されます。このマクロを使用する短所は、C シミュレーションで検証されたコードが合成されるコードとは異なるものになることです。この例の場合はコードは複雑ではないので、動作は同じになります。
• この local 配列の主な問題は、FPGA インプリメンテーションの質です。これは配列なので、内部 FPGA ブロック RAM を使用してインプリメントされます。これは、FPGA 内にインプリメントするにはかなり大きいメモリであり、より大型でコストのかかる FPGA デバイスが必要となる可能性があります。DATAFLOW 最適化を使用して、小型で効率的な FIFO を介してデータをストリーミングすると、ブロック RAM の使用を最小限に抑えることはできますが、データがストリーミングでシーケンシャルに使用されるようにする必要があります。現在のところ、そのような要件はありません。
2 つ目の問題は、パフォーマンスと local 配列の初期化に関するものです。Clear_Local ループはlocal 配列の値を 0 に設定するために使用されます。高パフォーマンスで実行するためにこのループをハードウェアでパイプライン処理しても、この操作をインプリメントするのに約 2 百万クロック サイクル必要です。このメモリの初期化中、システムで画像処理を実行することはできません。同じデータの初期化は、HConv ループ内の一時的な変数を使用して、書き出し前に累積を初期化することにより実行できます。最後の問題は、データのスループット、つまりシステム パフォーマンスがデータ アクセス パターンにより制限されることです。• 最初のたたみ込み出力を作成するため、最初の K 個の値が入力から読み出されます。• 2 つ目の出力の計算には、新しい値が読み出され、その後同じ K-1 個の値が再度読み出されます。
高パフォーマンスの FPGA を得るには、PS へのアクセスを最小限に抑えることが重要です。前に既にフェッチされたデータに再度アクセスすることは、システムのパフォーマンスに悪影響を与えます。FPGA では多数の計算を同時に実行して高パフォーマンスを達成することが可能ですが、データのフローが値を再読み出しするために頻繁に中断されると高パフォーマンスは得られません。注記: 高パフォーマンスを達成するには、PS からのデータにアクセスするのは 1 回のみにし、小型のローカル ストレージ (小型から中型のサイズの配列) に格納して再利用する必要があります。上記のコードでは、データを何度も読み出す必要があるので、DMA 操作を使用してプロセッサから直接ストリーミングできません。
5: 標準水平たたみ込み
Vivado HLS 最適化手法ガイド 49UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
最適な水平たたみ込みFPGA インプリメンテーションで効率的な方法で計算を実行するため、水平たたみ込みは次の図に示すように計算されます。
First Calculation First Output Final Output
src
Hwin
hconv
Hconv
Hsamp
Hconv
Hsamp
Hconv
X14297-110617
前の K 個のサンプルを使用してたたみ込み結果を計算する必要があるので、サンプルが一時キャッシュ hwin にコピーされます。このようにローカル ストレージを使用すると、PS から値を読み込み直す必要はなく、データのフローは途切れません。最初の計算では、hwin に結果を計算するのに十分な値が含まれていないので、条件により出力値は書き出されません。アルゴリズムは入力サンプルを読み込み続け、hwin キャッシュに格納します。新しいサンプルが読み込まれるたびに、不要なサンプルが hwin から排出されます。K 番目の入力が読み込まれると、最初の出力値を書き込むことができるようになります。このように、最後のサンプルが読み込まれるまで行ごとに処理されます。この段階で hwin に格納されているのは最後の K 個のサンプルだけであり、そのすべてがたたみ込み計算に必要となります。
5: 標準水平たたみ込み
Vivado HLS 最適化手法ガイド 50UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
次に示すように、この操作を実行するコードでは、ローカル ストレージを使用することによりPL からの再読み込みを回避して最終インプリメンテーションでローカル ストレージからの読み出しを並列に実行できるようにし、さらに条件分岐を多用して各新しいデータ サンプルを異なる方法で処理できるようにしています。// Horizontal convolution phconv=hconv_buffer; // set / reset pointer to start of buffer
// These assertions let HLS know the upper bounds of loopsassert(height < MAX_IMG_ROWS);assert(width < MAX_IMG_COLS);assert(vconv_xlim < MAX_IMG_COLS - (K - 1));HConvH:for(int col = 0; col < height; col++) { HConvW:for(int row = 0; row < width; row++) {#pragma HLS PIPELINE T in_val = *src++; // Reset pixel value on-the-fly - eliminates an O(height*width) loop T out_val = 0; HConv:for(int i = 0; i < K; i++) { hwin[i] = i < K - 1 ? hwin[i + 1] : in_val; out_val += hwin[i] * hcoeff[i]; } if (row >= K - 1) { *phconv++=out_val; } }}
上記のコードでは、一時変数 out_val を使用してたたみ込み計算が実行されています。この変数は、計算の実行前に 0 に設定されるので、前の例で示したように、値をリセットするために 2百万クロック サイクルを費やす必要はありません。プロセス全体を通して、src 入力のサンプルはラスター ストリーミング方式で処理されます。すべてのサンプルが順番に読み込まれます。タスクからの出力は破棄または使用されますが、タスクは常に計算を実行し続けます。この点が、CPU で実行するために記述されたコードと異なります。
最適な垂直たたみ込み垂直たたみ込みでは、FPGA 向けのストリーミング データ モデルを記述するのが困難です。データには列ごとにアクセスする必要がありますが、画像全体を格納するのは望ましくありません。ソリューションは、次の図に示すようにライン バッファーを使用することです。
5: 標準水平たたみ込み
Vivado HLS 最適化手法ガイド 51UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
First Calculation First Output Final Output
hconv
vconv
Vconv Vconv Vconv
X14300-110617
先ほどと同様、サンプルはストリーミング方式で読み込まれますが、この場合はローカル バッファーの hconv から読み込まれます。このアルゴリズムでは、最初のサンプルを処理するのに少なくとも K-1 行のデータが必要です。この前に実行される計算はすべて、条件を使用することにより破棄します。ライン バッファーには、K-1 行のデータを格納できます。新しいサンプルが読み込まれるたびに、別のサンプルがライン バッファーから排出されます。つまり、最新のサンプルが計算に使用されると、そのサンプルがライン バッファーに格納され、古いサンプルが排出されます。これにより、K-1 行のみをキャッシュすればよく、ローカル ストレージの使用が最小限に抑えられます。ライン バッファーにはローカルで格納するために複数行が必要ですが、たたみ込みのカーネル サイズ K はフル ビデオ画像の 1080 行よりもかなり小さくなります。
5: 標準水平たたみ込み
Vivado HLS 最適化手法ガイド 52UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
最初の計算は、K 行目にある最初のサンプルが読み込まれると実行されます。その後、最後のピクセルが読み込まれるまで値が出力されます。// Vertical convolution phconv=hconv_buffer; // set/reset pointer to start of bufferpvconv=vconv_buffer; // set/reset pointer to start of bufferVConvH:for(int col = 0; col < height; col++) { VConvW:for(int row = 0; row < vconv_xlim; row++) {#pragma HLS DEPENDENCE variable=linebuf inter false#pragma HLS PIPELINE T in_val = *phconv++; // Reset pixel value on-the-fly - eliminates an O(height*width) loop T out_val = 0; VConv:for(int i = 0; i < K; i++) { T vwin_val = i < K - 1 ? linebuf[i][row] : in_val; out_val += vwin_val * vcoeff[i]; if (i > 0) linebuf[i - 1][row] = vwin_val; } if (col >= K - 1) { *pvconv++ = out_val; }}
上記のコードでは、デザインのサンプルがすべてストリーミング方式で処理されます。タスクは、継続して実行されます。再読み出し (または再書き込み) の回数を最小限に抑えるコーディング スタイルに従う場合、データをローカルでキャッシュする必要があります。これは、FPGAをターゲットにする場合の理想的なストラテジです。
最適な境界ピクセルたたみ込みこのアルゴリズムの最後には、エッジ ピクセルを境界領域に複製します。一定したデータフローおよびデータ再利用を実現するため、ローカル キャッシュが使用されます。次の図に、境界サンプルがどのように画像に組み込まれるかを示します。• 各サンプルが垂直たたみ込みからの vconv 出力から読み込まれます。• サンプルが 4 つのピクセル タイプのいずれかとしてキャッシュに格納されます。• サンプルが出力ストリームに書き出されます。
5: 標準水平たたみ込み
Vivado HLS 最適化手法ガイド 53UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
First Output Middle Output Final Output
vconv
Left Edge Border
dst
Right Edge Raw Pixel
Left Edge Border
Right Edge Raw Pixel
Left Edge Border
Right Edge Raw Pixel
Border
Raw Pixel
Border
Right Edge
Left Edge
X14295-110617
次に、境界ピクセルの位置を決定するコードを示します。// Border pixelspvconv=vconv_buffer; // set/reset pointer to start of bufferBorder:for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { T pix_in, l_edge_pix, r_edge_pix, pix_out;#pragma HLS PIPELINE if (i == 0 || (i > border_width && i < height - border_width)) { // read a pixel out of the video stream and cache it for // immediate use and later replication purposes if (j < width - (K - 1)) { pix_in = *pvconv++; borderbuf[j] = pix_in; } if (j == 0) { l_edge_pix = pix_in; } if (j == width - K) { r_edge_pix = pix_in; } } // Select output value from the appropriate cache resource if (j <= border_width) {
5: 標準水平たたみ込み
Vivado HLS 最適化手法ガイド 54UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
pix_out = l_edge_pix; } else if (j >= width - border_width - 1) { pix_out = r_edge_pix; } else { pix_out = borderbuf[j - border_width]; } *dst++=pix_out; }}
このコードの明らかな違いは、タスク内に条件文が多く使用されている点です。これにより、タスクがパイプライン処理された後、データが継続的に処理されるようになります。条件文の結果はパイプラインの実行には影響しません。結果は出力値に影響しますが、入力サンプルが使用可能である限り、パイプラインは継続的に処理されます。
最適なデータ アクセス パターンFPGA で最適なパフォーマンスを得るためのデータ アクセス パターンは、次のとおりです。• データ入力の読み込みを最小限にします。データがブロックに読み込まれると、多くの並列パスに簡単に供給できますが、ハードウェア関数への入力がパフォーマンスのボトルネックになることがあります。データを読み込んだ後、再利用する必要がある場合は、ローカル キャッシュを使用します。
• 配列、特に大型の配列へのアクセスを最小限に抑えます。配列はブロック RAM にインプリメントされますが、ブロック RAM では I/O ポートと同様ポート数が限られるので、パフォーマンスのボトルネックになることがあります。配列は小型の配列および個別のレジスタに分割できますが、大型の配列を分割すると使用されるレジスタ数が多くなります。小型のローカル キャッシュを使用して累積などの結果を保持してから、最終結果を配列に書き出すようにします。
• パイプライン処理されたタスクであっても、タスクを条件で実行するのではなく、パイプライン処理されたタスク内で条件分岐を実行するようにします。条件文は、パイプラインで個別のパスとしてインプリメントされます。データが 1 つのタスクから次のタスク内で実行される条件に流れるようにすると、システムのパフォーマンスが向上します。
• 入力の読み込みと同様にポートはボトルネックになるので、出力の書き出しを最小限にします。追加のアクセスを複製すると、問題がシステム内に先送りされるだけです。
データをストリーミング方式で処理する C コードでは、関数引数に対する読み出し/書き込みを一度のみにすると、FPGA に効率的にインプリメントできるようになります。FPGA が必要なパフォーマンスで動作しない理由をデバッグするよりも、高パフォーマンスの FPGA インプリメンテーションが得られる C のアルゴリズムを設計する方が生産的です。
5: 標準水平たたみ込み
Vivado HLS 最適化手法ガイド 55UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
付録 A
OpenCL 属性OpenCL での最適化このセクションでは、SDAccel コンパイラ xocc、SDSoC システム コンパイラ sdscc と sds++、および Vivado HLS 合成でのシステム最適化のためにソース コードに追加可能な OpenCL 属性について説明します。SDx では、コードをデータの動きとカーネル パフォーマンスで最適化する OpenCL 属性が提供されています。データの動きの最適化は、インターフェイス帯域幅および DDR 帯域幅の最大限に活用することにより、システム レベルのデータ スループットを最大にすることを目的として実行されます。カーネル計算最適化は、カーネル インターフェイスにデータが到達したらすぐにすべてのデータを消費できるプロセッシング ロジックを作成することを目的として実行されます。これは通常、関数のインライン展開とパイプライン処理、ループ展開、配列分割、データフローなどの手法を使用してデータパスを一致させるようにプロセッシング コードを展開することによって達成されます。OpenCL 属性には、次のタイプがあります。表 9: OpenCL 属性 (タイプ別)
タイプ 属性カーネル サイズ • reqd_work_group_size
• vec_type_hint
• work_group_size_hint
• xcl_max_work_group_size
• xcl_zero_global_work_offset
関数のインライン展開 • always_inline
タスク レベルのパイプライン処理 • xcl_dataflow
• xcl_reqd_pipe_depth
パイプライン処理 • xcl_pipeline_loop
• xcl_pipeline_workitems
ループ展開 • opencl_unroll_hint
付録 A: OpenCL 属性
Vivado HLS 最適化手法ガイド 56UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
表 9: OpenCL 属性 (タイプ別) (続き)
タイプ 属性配列最適化 • xcl_array_partition
• xcl_array_reshape
注記: 配列変数では、使用できる配列最適化属性は 1つのみです。
ヒント: SDAccel および SDSoC コンパイラでは、always_inline、noinline、unroll、nounroll など、gcc でサポートされる多くの標準属性もサポートされます。
always_inline説明always_inline 属性は、関数をインライン展開する必要があることを示します。この属性はGCC の標準機能であり、SDx コンパイラの標準機能でもあります。ヒント: noinline 属性は GCC の標準機能であり、SDx コンパイラでもサポートされます。
この属性は、関数を呼び出し関数内でインライン展開するコンパイラ最適化をイネーブルにします。インライン展開された関数は、RTL で別の階層としては表示されなくなります。関数をインライン展開すると、関数内の演算が共有され、呼び出し関数の周辺の演算と効率よく最適化されることがあります。ただし、インライン展開された関数はほかの関数と共有できなくなるので、インライン展開された関数とその関数の別のインスタンス (より広く共有可能) でロジックが複製される可能性があります。これによりパフォーマンスを向上できますが、RTL をインプリメントするのに必要なエリアが増加します。OpenCL カーネルの場合、SDx コンパイラでは独自の規則を使用して、関数がインライン化または非インライン化されます。インライン関数を直接制御するには、always_inline またはnoinline 属性を使用ます。デフォルトでは、インライン展開は関数階層のすぐ下の階層でのみ実行され、サブ関数では実行されません。重要: xcl_dataflow 属性を使用すると、コンパイラで always_inline 属性が無視され、関数がインライン化されません。
付録 A: OpenCL 属性
Vivado HLS 最適化手法ガイド 57UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
構文OpenCL ソースで関数の前に記述し、その関数が呼び出されたときに常にインライン展開されるようにします。__attribute__((always_inline))
例次の例では、関数 foo に always_inline 属性を追加しています。__attribute__((always_inline)) void foo ( a, b, c, d ) { ...}
次の例では、関数 foo のインライン化がされないようになります。__attribute__((noinline)) void foo ( a, b, c, d ) { ...}
関連項目• https://gcc.gnu.org
• 『SDAccel 環境プロファイリングおよび最適化ガイド』 (UG1207)
opencl_unroll_hint説明重要: これはコンパイラ ヒントであり、コンパイラにより無視される可能性があります。ループ展開は、SDAccel で使用可能な最初の最適化手法です。ループ展開最適化は、コンパイラで同時処理が認識されるようにするために実行されます。認識された新たな同時処理により、レイテンシが削減され、パフォーマンスが向上しますが、より多くの FPGA ファブリック リソースを使用します。opencl_unroll_hint 属性は OpenCL 言語仕様の一部であり、OpenCL コンパイラでループ(for、while、do) が展開されるよう指定します。詳細は、『SDAccel 環境最適化ガイド』(UG1207) の「ループ展開」を参照してください。opencl_unroll_hint 属性修飾子は、適用するループの直前に記述する必要があります。この属性では、ループの完全な展開、指定した量の部分展開、またはループ展開のディスエーブルを指定できます。
付録 A: OpenCL 属性
Vivado HLS 最適化手法ガイド 58UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
構文OpenCL ソースのループ定義の前に配置します。__attribute__((opencl_unroll_hint(n)))
説明:
• n: オプションのループ展開係数。正の整数またはコンパイル時定数表現で指定します。1 にすると、ループ展開がディスエーブルになります。
ヒント: n を指定しない場合、ループの展開係数はコンパイラにより自動的に決定されます。
例 1
次の例では、for ループを係数 2 で展開しています。この結果、計算ユニットの 4 つの順次反復ではなく、2 つの並列ループ反復が生成されます。__attribute__((opencl_unroll_hint(2)))for(int i = 0; i < LENGTH; i++) {bufc[i] = bufa[i] * bufb[i];}
上記のループは、コンパイラにより次のコードに変換されます。for(int i = 0; i < LENGTH; i+=2) {bufc[i] = bufa[i] * bufb[i];bufc[i+1] = bufa[i+1] * bufb[i+1];}
関連項目• 『SDAccel 環境最適化ガイド』 (UG1207)
• https://www.khronos.org/
• OpenCL C 仕様
付録 A: OpenCL 属性
Vivado HLS 最適化手法ガイド 59UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
reqd_work_group_size説明OpenCL™ カーネルが OpenCL デバイスでの実行用に投稿されると、ND 範囲 (1、2、または 3次元) と呼ばれるインデックス空間内で実行されます。これは、OpenCL API ではグローバル サイズと呼ばれます。ワーク グループ サイズは、カーネル計算ユニットの 1 回の起動で処理可能な ND 範囲の量を定義します。ワーク グループ サイズは、OpenCL API ではローカル サイズとも呼ばれます。OpenCL コンパイラでは、カーネルおよび選択されたデバイスのプロパティに基づいてワーク グループ サイズを決定できます。ワーク グループ サイズ (ローカル サイズ) が決定されたら、ND 範囲 (グローバル サイズ) が自動的にワーク グループに分割され、デバイス上で実行するためにワーク グループがスケジューリングされます。OpenCL コンパイラでワーク グループ サイズを定義できますが、カーネルの FPGA インプリメンテーションでは、カーネルの reqd_work_group_size 属性でワーク グループ サイズを定義することをお勧めします。この属性は、カーネルのカスタム ロジックの生成中にパフォーマンスを最適化するのに推奨されます。詳細は、『SDAccel 環境プロファイリングおよび最適化ガイド』 (UG1207) の「OpenCL 実行モデル」を参照してください。ヒント: FPGA インプリメンテーションの場合は、reqd_work_group_size 属性をカーネルのカスタム ロジックの生成中にパフォーマンスを最適化するために使用できるので、この属性を指定することをお勧めします。OpenCL カーネル関数は、ND 範囲インデックス空間の各点に対して 1 回のみ実行されます。ND 範囲の各点に対するこの処理ユニットは、ワーク アイテムと呼ばれます。ワーク アイテムは、計算ユニットにスケジューリングされるワーク ユニットであるワーク グループにまとめられます。オプションの reqd_work_group_size は、clEnqueueNDRangeKernel のlocal_work_size 引数として使用される必要のある計算ユニットのワーク グループ サイズを定義します。これにより、生成されたコードがこのカーネル用に適切に最適化されます。構文カーネル定義の前、またはそのカーネル用に指定されたプライマリ関数の前に配置します。__attribute__((reqd_work_group_size(X, Y, Z)))
説明:
• X、Y、Z: カーネルの ND 範囲を指定します。カーネルのワーク グループのサイズを指定する3 次元行列の各次元を表します。
付録 A: OpenCL 属性
Vivado HLS 最適化手法ガイド 60UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
例次の OpenCL API C カーネル コードはベクトル加法デザインを記述したもので、2 つの配列のデータの和が 3 つ目の配列に挿入されます。ワーク グループに必要なサイズは 16x1x1 です。このカーネルは 16 回実行され、有効な結果が生成されます。#include <clc.h>// For VHLS OpenCL C kernels, the full work group is synthesized__attribute__ ((reqd_work_group_size(16, 1, 1)))__kernel void vadd(__global int* a,__global int* b,__global int* c){int idx = get_global_id(0);c[idx] = a[idx] + b[idx];}
関連項目• 『SDAccel 環境プロファイリングおよび最適化ガイド』 (UG1207)
• https://www.khronos.org/
• OpenCL C 仕様
vec_type_hint説明重要: これはコンパイラ ヒントであり、コンパイラにより無視される可能性があります。オプションの __attribute__((vec_type_hint(<type>))) は OpenCL 言語仕様の一部であり、カーネルの計算幅を表す OpenCL コンパイラへのヒントです。この設定は、コンパイラでコードを自動ベクター化しようとするときにプロセッサ帯域幅の使用率を計算する際のベースとなります。デフォルトでは、カーネルに __attribute__((vec_type_hint(int))) 修飾子があるとして処理されます。これにより、異なるベクター化タイプを指定できます。自動ベクター化では、コンパイラでワーク アイテムが結合または分離される状況に対応するため、カーネルから呼び出されたライブラリはランタイム時に再コンパイル可能であると想定されます。つまり、ライブラリはハード コードされたバイナリではないか、ハード コードされたバイナリの場合はソースまたはリターゲット可能な中間表記も提供されていることが必要です。これが、コード セキュリティの問題となる可能性もあります。
付録 A: OpenCL 属性
Vivado HLS 最適化手法ガイド 61UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
構文カーネル定義の前、またはそのカーネル用に指定されたプライマリ関数の前に配置します。__attribute__((vec_type_hint(<type>)))
説明:
• <type>: 次の表にリストされたビルトイン ベクターのいずれか、または構成スカラー要素のデータ型。
注記: 指定しない場合は、INT 型であると想定されます。表 10: ベクター型
タイプ 説明charn n 個の 8 ビット符号付き 2 の補数整数値のベクター。ucharn n 個の 8 ビット符号なし整数値のベクター。shortn n 個の 16 ビット符号付き 2 の補数整数値のベクター。ushortn n 個の 16 ビット符号なし整数値のベクター。intn n 個の 32 ビット符号付き 2 の補数整数値のベクター。uintn n 個の 32 ビット符号なし整数値のベクター。longn n 個の 64 ビット符号付き 2 の補数整数値のベクター。ulongn n 個の 64 ビット符号なし整数値のベクター。floatn n 個の 32 ビット浮動小数点値のベクター。doublen n 個の 64 ビット浮動小数点値のベクター。
注記: n を指定しない場合は、1 と想定されます。上記のベクター データ型名で n が 2、3、4、8、および16 以外の値のものも、予約されています。つまり、n は 2、3、4、8、および 16 のみに指定できます。
例次の例では、基本計算幅が 2 倍幅整数であると想定して自動ベクター化を実行しています。#include <clc.h>// For VHLS OpenCL C kernels, the full work group is synthesized__attribute__((vec_type_hint(double)))__attribute__ ((reqd_work_group_size(16, 1, 1)))__kernel void ...
関連項目• 『SDAccel 環境最適化ガイド』 (UG1207)
• https://www.khronos.org/
付録 A: OpenCL 属性
Vivado HLS 最適化手法ガイド 62UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
• OpenCL C 仕様
work_group_size_hint説明重要: これはコンパイラ ヒントであり、コンパイラにより無視される可能性があります。OpenCL 規格のワーク グループ サイズは、カーネル計算ユニットの 1 回の起動で処理可能なND 範囲のサイズを定義します。OpenCL カーネルが OpenCL デバイスでの実行用に投稿されると、ND 範囲 (1、2、または 3 次元) と呼ばれるインデックス空間内で実行されます。詳細は、『SDAccel 環境最適化ガイド』 (UG1207) の「OpenCL 実行モデル」を参照してください。OpenCL カーネル関数は、ND 範囲インデックス空間の各点に対して 1 回のみ実行されます。ND 範囲の各点に対するこの処理ユニットは、ワーク アイテムと呼ばれます。ループ反復が順番どおりに順次実行される C の for ループとは異なり、OpenCL ランタイムおよびデバイスではワーク アイテムを並列に任意の順序で実行できます。ワーク アイテムは、計算ユニットにスケジューリングされるワーク ユニットであるワーク グループにまとめられます。オプションの work_group_size_hint 属性は OpenCL 言語仕様の一部であり、ほとんどの場合に clEnqueueNDRangeKernel への local_work_size 引数により指定されるワーク グループ サイズ値を示す OpenCL コンパイラへのヒントです。これにより、生成されたコードが指定した値に従って最適化されます。ヒント: FPGA インプリメンテーションの場合、work_group_size_hint の代わりにreqd_work_group_size 属性をカーネルのカスタム ロジックの生成中にパフォーマンスを最適化するために使用できるので、この属性を指定することをお勧めします。構文カーネル定義の前、またはそのカーネル用に指定されたプライマリ関数の前に配置します。__attribute__((work_group_size_hint(X, Y, Z)))
説明:
• X、Y、Z: カーネルの ND 範囲を指定します。カーネルのワーク グループのサイズを指定する3 次元行列の各次元を表します。
付録 A: OpenCL 属性
Vivado HLS 最適化手法ガイド 63UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
例次の例では、カーネルがほとんどの場合にワーク グループ サイズ 1 で実行されることをコンパイラにヒントとして指定しています。__attribute__((work_group_size_hint(1, 1, 1)))__kernel void...
関連項目• 『SDAccel 環境最適化ガイド』 (UG1207)
• https://www.khronos.org/
• OpenCL C 仕様
xcl_array_partition説明重要: 配列変数では、使用できる属性は 1 つのみです。xcl_array_partition では多次元配列がサポートされますが、1 つの属性で再形成できるのは 1 つの次元のみです。OpenCL プログラマのほかの計算デバイスと比較した場合の FPGA の利点は、システム全体のメモリ アーキテクチャをカスタマイズし、計算ユニットに挿入できることです。デフォルトでは、SDAccel コンパイラにより、カーネル コードのスタティック コード解析に基づいてローカルおよびプライベート メモリの帯域幅を最大限にする計算ユニット内にメモリ アーキテクチャが生成されます。これらのメモリはカーネル ソース コードの属性に基づいてさらに最適化することが可能であり、ローカルおよびプライベート メモリの物理的なレイアウトおよびインプリメンテーションを指定するのに使用できます。計算ユニットのメモリの物理的なレイアウトを制御する SDAccel コンパイラの属性は array_partition です。1 次元配列の場合、array_partition 属性により、カーネル コード内で宣言された配列が 1つの物理メモリではなく複数の物理メモリにインプリメントされます。どの分割方法を使用するかは、アプリケーションおよびパフォーマンス要件によって異なります。SDAccel コンパイラで使用可能な配列の分割方法は、cyclic、block、および complete です。
付録 A: OpenCL 属性
Vivado HLS 最適化手法ガイド 64UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
構文この属性は、配列変数の定義に配置します。__attribute__((xcl_array_partition(<type>, <factor>, <dimension>)))
説明:
• <type>: 次のいずれかの分割タイプを指定します。○ cyclic: サイクリック分割では、配列が計算ユニットのロジックにより同時にアクセス可能な複数の小型の物理メモリとしてインプリメントされます。各メモリに要素が 1 つずつ配置され、すべての配列に配置されたら最初の配列に戻って、配列が完全に分割されるまでそれが繰り返されます。
○ block: ブロック分割では、配列が計算ユニットのロジックにより同時にアクセス可能な複数の小型メモリとしてインプリメントされます。メモリ ブロックに配列からの要素がフルになるまで配置され、次のメモリ ブロックに移動してそれが繰り返されます。
○ complete: 完全分割では、配列を個々の要素に分割します。1 次元配列の場合は、メモリが個々のレジスタに分割されます。
○ デフォルトの type は complete です。• <factor>: サイクリック分割では、factor でカーネル コードの元の配列をいくつの物理メモリに分割するかを指定します。ブロック分割では、factor で元の配列から各物理メモリに配置する要素の数を指定します。
重要: 完全分割 (complete) では factor は指定しません。• <dimension>: 分割する次元を指定します。1 ~ N の整数値を指定します。SDAccel では、N次元の配列がサポートされ、配列をどの 1 つの次元でも分割できます。
例 1
次のような配列宣言があるとします。int buffer[16];
buffer という名前の整数配列には、32 ビット幅の値が 16 個格納されています。この配列に、次の宣言を使用してサイクリック分割を適用します。int buffer[16] __attribute__((xcl_array_partition(cyclic,4,1)));
この例では、<partition_type> に cyclic により、SDAccel で配列の内容が 4 つの物理メモリに分割されます。この属性により、配列 buffer へのアクセスに使用可能なメモリ帯域幅が 4 倍になります。
付録 A: OpenCL 属性
Vivado HLS 最適化手法ガイド 65UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
SDAccel では、計算ユニット内のすべての配列で最高 2 つの同時アクセスを保持できます。コードの元の配列を 4 つの物理メモリに分割することにより、計算ユニットで配列 buffer に対する最高 8 つの同時アクセスを保持できます。例 2
例 1 と同じ整数配列に、次の宣言を使用してブロック分割を適用します。int buffer[16] __attribute__((xcl_array_partition(block,4,1)));
ブロックのサイズは 4 なので、SDAccel で 4 つの物理メモリが生成され、各メモリに配列からのデータが順に配置されます。例 3
例 1 と同じ整数配列に、次の宣言を使用して完全分割を適用します。int buffer[16] __attribute__((xcl_array_partition(complete, 1)));
この例では、配列がカーネルのプログラマブル ロジックの分散 RAM または 16 個のレジスタに完全に分割されます。完全分割がデフォルトなので、次の宣言を使用しても同じ結果が得られます。int buffer[16] __attribute__((xcl_array_partition));
これによりメモリ帯域幅が最大のインプリメンテーションが作成されますが、すべてのアプリケーションに適しているわけではありません。カーネル コードによる定数またはデータ依存インデックスを介したデータへのアクセス方法によって、元のコードと同等の機能を確実にするために SDx で各レジスタの周辺に構築する必要のあるサポート ロジックの量が異なります。SDxでの一般的なベスト プラクティス ガイドラインとして、完全分割は、少なくとも 1 つの次元が定数インデックスを介してアクセスされる配列に適しています。関連項目• xcl_array_reshape
• pragma HLS array_partition
• 『SDAccel 環境最適化ガイド』 (UG1207)
• 『Vivado Design Suite ユーザー ガイド: 高位合成』 (UG902)
付録 A: OpenCL 属性
Vivado HLS 最適化手法ガイド 66UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
xcl_array_reshape説明重要: 配列変数では、使用できる属性は 1 つのみです。xcl_array_reshape では多次元配列がサポートされますが、1 つの属性で再形成できるのは 1 つの次元のみです。配列分割と垂直配列マップを組み合わせます。ARRAY_RESHAPE 属性は、配列を複数の小型の配列に分割する ARRAY_PARTITION の効果と、ビット幅を増やして配列の要素を連結する操作を組み合わせたものです。これにより、使用されるブロック RAM の数を削減すると共に、データへの並列アクセスを実現できます。この属性では元の配列よりも要素数が少なくビット幅の広い配列が作成され、1 クロック サイクルでアクセスできるデータ量が増加します。次のようなコードがあるとします。void foo (...) {int array1[N] __attribute__((xcl_array_reshape(block, 2, 1)));int array2[N] __attribute__((xcl_array_reshape(cycle, 2, 1)));int array3[N] __attribute__((xcl_array_reshape(complete, 1)));...}
ARRAY_RESHAPE 属性を使用すると、配列が次の図に示すように変換されます。図 1: ARRAY_RESHAPE
0 1 2 ... N-3 N-2 N-1
N/2 ... N-2 N-10 1 ... (N/2-1)
1 ... N-3 N-10 2 ... N-2
block
cyclic
complete
X14307-110217
0 1 2 ... N-3 N-2 N-1
0 1 2 ... N-3 N-2 N-1
array1[N]
array2[N]
array3[N] N-1N-2...10
MSBLSB
MSBLSB
MSB
LSB
array4[N/2]
array5[N/2]
array6[1]
付録 A: OpenCL 属性
Vivado HLS 最適化手法ガイド 67UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
構文この属性は、配列変数の定義に配置します。__attribute__((xcl_array_reshape(<type>,<factor>, <dimension>)))
説明:
• <type>: 次のいずれかの分割タイプを指定します。○ cyclic: サイクリック分割では、配列が計算ユニットのロジックにより同時にアクセス可能な複数の小型の物理メモリとしてインプリメントされます。各メモリに要素が 1 つずつ配置され、すべての配列に配置されたら最初の配列に戻って、配列が完全に分割されるまでそれが繰り返されます。
○ block: ブロック分割では、配列が計算ユニットのロジックにより同時にアクセス可能な複数の小型メモリとしてインプリメントされます。メモリ ブロックに配列からの要素がフルになるまで配置され、次のメモリ ブロックに移動してそれが繰り返されます。
○ complete: 完全分割では、配列を個々の要素に分割します。1 次元配列の場合は、メモリが個々のレジスタに分割されます。
○ デフォルトの type は complete です。• <factor>: サイクリック分割では、factor でカーネル コードの元の配列をいくつの物理メモリに分割するかを指定します。ブロック分割では、factor で元の配列から各物理メモリに配置する要素の数を指定します。
重要: 完全分割 (complete) では factor は指定しません。• <dimension>: 分割する次元を指定します。1 ~ N の整数値を指定します。SDAccel では、N次元の配列がサポートされ、配列をどの 1 つの次元でも分割できます。
例 1
次の例では、17 個の要素を含む 8 ビット配列 AB[17] を、ブロック マップを使用して 5 つの要素を含む 32 ビット配列に再形成 (分割およびマップ) しています。int AB[17] __attribute__((xcl_array_reshape(block,4,1)));
ヒント: factor を 4 に指定すると、配列は 4 つに分割されます。つまり、17 個の要素がビット幅が 4 倍の 5 つの要素を含む配列に再形成されます。この場合、最後の要素 AB[17] は、5 番目の要素の下位 8 ビットにマップされ、5 番目の要素の残りは空になります。
付録 A: OpenCL 属性
Vivado HLS 最適化手法ガイド 68UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
例 2
次の例では、2 次元配列 AB[6][4] を次元 [6][2] の配列 1 つに再形成しています。この次元 2 のビット幅は 2 倍です。int AB[6][4] __attribute__((xcl_array_reshape(block,2,2)));
例 3
次の例では、関数 foo の 3 次元の 8 ビット配列 AB[4][2][2] を 128 ビット幅は (4*2*2*8) の 1 要素配列 (1 つのレジスタ) に再形成しています。int AB[4][2][2] __attribute__((xcl_array_reshape(complete,0)));
ヒント: dimension を 0 に指定すると、配列のすべての次元が再形成されます。
関連項目• xcl_array_partition
• pragma HLS array_reshape
• 『SDAccel 環境最適化ガイド』 (UG1207)
• 『Vivado Design Suite ユーザー ガイド: 高位合成』 (UG902)
xcl_data_pack説明構造体 (struct) のデータ フィールドをワード幅が広い 1 つのスカラーにパックします。xcl_data_pack 属性は、struct のすべての要素を 1 つの幅の広いベクターにパックして、変数に必要なメモリを削減します。これにより、struct のすべてのメンバーを同時に読み出しおよび書き込みできます。ワード数のビット アライメントは、struct フィールドの宣言から自動推論されます。最初のフィールドはベクターの LSB で、struct の最後の要素がベクターのMSB に揃えられます。ヒント: struct 内で宣言されている配列はすべて完全に分割されて幅の広いスカラーにまとめられ、ほかのスカラー フィールドと共にパックされます。
付録 A: OpenCL 属性
Vivado HLS 最適化手法ガイド 69UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
struct に配列が含まれる場合は、これらの配列を xcl_array_partition 属性を使用して分割することにより最適化できます。xcl_data_pack 属性は xcl_array_partition 属性の完全分割と同様の処理を実行し、struct の要素を 1 つの幅の広いベクターに再形成します。struct は、xcl_data_pack を使用して最適化すると同時に分割することはできません。xcl_data_pack および xcl_array_partition 属性を同時に使用することはできません。大きな配列を含む構造体に xcl_data_pack 最適化を使用する際には注意が必要です。配列にint 型の要素が 4096 個含まれる場合、ベクター (およびポート) の幅は 4096*32=131072 ビットになります。SDx でこの RTL デザインは作成できますが、FPGA インプリメンテーション中に論理合成でこれが配線できることはほとんどありません。構文struct 変数が定義されている領域内に配置します。__attribute__((xcl_data_pack(<variable>, <name>)))
説明:
• <variable>: パックする変数を指定します。• <name>: パック後の変数名を指定します。<name> を指定しない場合は、<variable> の名前が使用されます。
例 1
次の例では、関数 foo の 8 ビット フィールド 3 つを持つ構造体 (typedef struct {unsigned charR, G, B;} pixel) の配列 AB[17] を 24 ビットの 17 要素の配列 1 つにパックしています。typedef struct{unsigned char R, G, B;} pixel;
pixel AB[17] __attribute__((xcl_data_pack(AB)));
関連項目• pragma HLS data_pack
• 『SDAccel 環境プロファイリングおよび最適化ガイド』 (UG1207)
付録 A: OpenCL 属性
Vivado HLS 最適化手法ガイド 70UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
xcl_dataflow説明xcl_dataflow 属性は、タスク レベルのパイプライン処理をイネーブルにして関数およびループを重複できるようにし、RTL インプリメンテーションでの同時実行性を増加してデザイン全体のスループットを向上します。C 記述では、すべての演算が順次に実行されます。pragma HLS allocation などのリソースを制限する指示子を指定しない場合は、Vivado HLS ではレイテンシを最小限に抑え、同時実行性を向上するように処理されます。ただし、データ依存性のためにこれが制限されることがあります。たとえば、配列にアクセスする関数またはループは、完了する前に配列への読み出し/書き込みアクセスをすべて終了する必要があります。そのため、そのデータを消費する次の関数またはループの演算を開始できません。データフロー最適化を使用すると、前の関数またはループがすべての演算を完了する前に、次の関数またはループの演算を開始できるようになります。データフロー最適化を指定した場合、Vivado HLS で順次関数またはループ間のデータフローが解析され、プロデューサー関数またはループが完了する前にコンシューマー関数またはループの演算を開始できるように、ピンポン RAM または FIFO に基づいてチャネルが作成されます。これにより関数またはループを並列実行でき、レイテンシが削減されて RTL のスループットが向上します。開始間隔 (II) (関数またはループの開始から次の関数またはループの開始までのサイクル数) が指定されていない場合は、Vivado HLS で開始間隔が最小になるようにし、データが使用可能になったらすぐに演算を開始できるようにすることが試みられます。ヒント: Vivado HLS には、データフロー コンフィギュレーション設定があります。config_dataflow コマンドは、データフロー最適化で使用されるデフォルトのメモリ チャネルと FIFO の深さを指定します。詳細は、『Vivado Design Suite ユーザー ガイド: 高位合成』(UG902) を参照してください。DATAFLOW 最適化が機能するようにするには、デザイン内でデータが 1 つのタスクから次のタスクに流れる必要があります。次のコーディング スタイルを使用すると、Vivado HLS でDATAFLOW 最適化が実行されなくなります。詳細は、『Vivado Design Suite ユーザー ガイド: 高位合成』 (UG902) を参照してください。• シングル プロデューサー コンシューマー違反• タスクのバイパス• タスク間のフィードバック• タスクの条件付き実行
付録 A: OpenCL 属性
Vivado HLS 最適化手法ガイド 71UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
• 複数の exit 条件を持つループ重要: これらのコーディング スタイルのいずれかが使用されている場合、Vivado HLS でメッセージが表示され、DATAFLOW 最適化は実行されません。最後に、DATAFLOW 最適化には階層インプリメンテーションはありません。サブ関数またはループに DATAFLOW 最適化が有益な可能性のあるタスクが含まれる場合、DATAFLOW 最適化をそのループまたはサブ関数に適用するか、サブ関数をインライン展開する必要があります。構文dataflow 属性は、関数定義またはループ定義の前に指定します。__attribute__((xcl_dataflow))
例次の例では、関数 foo 内にデータフロー最適化を指定しています。#pragma HLS dataflow
関連項目• pragma HLS dataflow
• 『SDAccel 環境最適化ガイド』 (UG1207)
• 『Vivado Design Suite ユーザー ガイド: 高位合成』 (UG902)
xcl_dependence説明xcl_dependence 属性は、ループ運搬依存を克服し、ループをパイプライン処理できるようにする (またはより短い間隔でパイプラインできるようにする) ための追加情報を提供します。Vivado HLS では、次の依存性が自動的に検出されます。• ループ内 (ループ独立依存)。• 同じループの反復間 (ループ運搬依存)。
付録 A: OpenCL 属性
Vivado HLS 最適化手法ガイド 72UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
こうした依存は、演算をスケジューリングするタイミング、特に関数およびループのパイプライン処理に影響します。• ループ独立依存: 同じ要素が同じループ反復でアクセスされます。
for (i=0;i<N;i++) { A[i]=x; y=A[i];}
• ループ運搬依存: 同じ要素が異なるループ反復でアクセスされます。for (i=0;i<N;i++) { A[i]=A[i-1]*2;}
複雑な条件下では、自動依存性解析が保守的すぎ、偽依存性が除去されないことがあります。変数依存の配列インデックスや、外部要件を満たす必要があるような状況 (2 つの入力が同じインデックスにならない場合など) では、依存解析が保守的すぎることがあります。xcl_dependence 属性を使用すると、依存を明示的に指定し、偽依存を解決できます。重要: 偽依存でないものを偽依存と指定すると、ハードウェアが正しく機能しなくなる可能性があります。依存性 (true または false) が正しいことを確認してから指定してください。
構文変数の宣言で設定します。__attribute__((xcl_dependence(<class> <type> <direction> distance=<int> <dependent>)))
説明:
• <class>: 依存を明確にする必要がある変数のクラスを指定します。有効な値は array またはpointer です。
ヒント: <class> と variable= を同時に指定することはできません。変数または変数のクラスのいずれかのみを指定できます。• <type>: 有効な値は intra または inter です。依存のタイプを指定します。
○ intra: 同じループ反復内の依存。<type> を intra、<dependent> を false に設定すると、Vivado HLS によりループ内で演算を自由に移動でき、パフォーマンスまたはエリアを向上できる可能性が高くなります。<dependent> を true に設定すると、演算を指定の順序で実行する必要があります。
○ inter: 異なるループ反復間の依存。これがデフォルトの <type> です。<type> をinter、<dependent> を false に設定すると、関数またはループがパイプライン処理されているか、ループが展開されていない場合または部分的に展開されている場合に、並列実行が可能になり、<dependent> を true に設定すると並列実行が不可能になります。
付録 A: OpenCL 属性
Vivado HLS 最適化手法ガイド 73UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
• <direction>: 有効な値は RAW、WAR または WAW です。これはループ運搬依存にのみ関連し、依存の方向を指定します。○ RAW (Read-After-Write - 真の依存): 書き込み命令により書き込まれた値が読み出し命令で使用されます。
○ WAR (Write-After-Read - 非依存): 読み出し命令により値が取得され、その値が書き込み命令で上書きされます。
○ WAW (Write-After-Write - 出力依存): 2 つの書き込み命令により、特定の順序で同じロケーションに書き込みが実行されます。
• distance=<int>: 配列アクセスの反復間隔を指定します。これは、依存が true に設定されているループ運搬依存でのみ使用されます。
• <dependent>: 依存を適用する必要があるか (true)、削除するか (false) を指定します。デフォルトは true です。
例 1
次の例では、Vivado HLS では cols の値を認識する機能がないので、buff_A[1][col] への書き込みと buff_A[1][col] の読み出し間に常に依存性があると想定されます。このようなアルゴリズムでは、cols が 0 になることはほとんどありませんが、Vivado HLS ではデータ依存を想定できません。この依存を解決するため、xcl_dependence 属性を使用して、ループ反復間 (この例の場合は buff_A と buff_B の両方) に依存がないことを指定できます。void foo(int rows, int cols, ...) for (row = 0; row < rows + 1; row++) { for (col = 0; col < cols + 1; col++) __attribute__((xcl_pipeline_loop(II=1))) { if (col < cols) { buff_A[2][col] = buff_A[1][col] __attribute__((xcl_dependence(inter false))); // read from buff_A buff_A[1][col] = buff_A[0][col]; // write to buff_A buff_B[1][col] = buff_B[0][col] __attribute__((xcl_dependence(inter false))); temp = buff_A[0][col];}
例 2
次の例では、関数 foo の loop_1 の同じ反復での Var1 の依存を削除しています。__attribute__((xcl_dependence(intra false)));
付録 A: OpenCL 属性
Vivado HLS 最適化手法ガイド 74UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
例 3
次の例では、関数 foo の loop_2 にあるすべての配列の依存を定義し、同じループ反復ではすべての読み出しが書き込みの後に実行される (RAW) ように設定しています。__attribute__((xcl_dependence(array intra RAW true)));
関連項目• pragma HLS dependence
• 『SDAccel 環境プロファイリングおよび最適化ガイド』 (UG1207)
• 『Vivado Design Suite ユーザー ガイド: 高位合成』 (UG902)
xcl_max_work_group_size説明4K サイズよりも大型のカーネルを指定する必要がある場合は、reqd_work_group_size ではなくこの属性を使用してください。この属性は、SDx で reqd_work_group_size 属性によりサポートされるデフォルトの最大ワーク グループ サイズを拡大します。ザイリンクス属性 xcl_max_work_group_size を使用すると、SDx で 4096 より大型のワーク サイズをサポートできます。注記: 実際のワーク グループ サイズの制限は、プラットフォームに選択したザイリンクス デバイスによって異なります。
構文カーネル定義の前、またはそのカーネル用に指定されたプライマリ関数の前に配置します。__attribute__((xcl_max_work_group_size(X, Y, Z)))
説明:
• X、Y、Z: カーネルの ND 範囲を指定します。カーネルのワーク グループのサイズを指定する3 次元行列の各次元を表します。
付録 A: OpenCL 属性
Vivado HLS 最適化手法ガイド 75UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
例 1
次は、最適化されていない加算器のカーネル ソース コード例です。このデザインでは、ワークサイズを行列のサイズ (64x64) に設定している以外は、属性は指定されていません。つまり、ワーク グループ全体を実行すると、入力行列 a および b が完全に加算され、output に結果が出力されます。3 つはすべてグローバル整数ポインターであり、行列の各値は 4 バイトで、オフチップの DDR グローバル メモリに格納されます。#define RANK 64__kernel __attribute__ ((reqd_work_group_size(RANK, RANK, 1)))void madd(__global int* a, __global int* b, __global int* output) {int index = get_local_id(1)*get_local_size(0) + get_local_id(0);output[index] = a[index] + b[index];}
このローカル ワーク サイズ (64, 64, 1) は、グローバル ワーク サイズと同じです。この設定により、合計ワーク サイズ 4096 が作成されます。注記: これは、標準の OpenCL 属性 reqd_work_group_size を使用した場合に SDAccel でサポートされる最大ワーク サイズです。ザイリンクス属性 xcl_max_work_group_size を使用すると、SDAccel で 4096 より大型のワーク サイズをサポートできます。64x64 より大型の行列では、ワーク サイズを定義するのに 1 次元のみを使用する必要があります。つまり、128x128 行列はワーク サイズ (128, 1, 1) のカーネルで演算できます (各実行でデータの行全体または列全体を演算)。関連項目• 『SDAccel 環境最適化ガイド』 (UG1207)
• https://www.khronos.org/
• OpenCL C 仕様
xcl_pipeline_loop説明ループをパイプライン処理して、レイテンシを向上し、カーネル スループットおよびパフォーマンスを最大限にします。ループを展開することにより同時実行性は増加しますが、カーネル データパスのすべての要素を常にビジー状態に保持する問題は解決されません。ループ展開されていても、ループ制御依存により順次実行となることがあります。演算が順次実行されると、ハードウェアがアイドル状態となり、パフォーマンスが低下します。
付録 A: OpenCL 属性
Vivado HLS 最適化手法ガイド 76UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
ザイリンクスでは、この問題に対処するために、ループ パイプライン処理の OpenCL 2.0 仕様にベンダー拡張、xcl_pipeline_loop を導入しました。デフォルトでは、XOCC コンパイラにより自動的に、トリップカウントが 64 を超える場合はループがパイプライン処理され、トリップカウントが 64 以下の場合はループが展開されます。これにより、良い結果が得られるはずです。ループの前に nounroll 属性とxcl_pipeline_loop 属性を指定すると、ループをパイプライン処理するよう指定できます。構文OpenCL ソースのループ定義の前に配置します。__attribute__((xcl_pipeline_loop))
例次の例では、関数 LOOP_1 の vaccum をパイプライン処理し、パフォーマンスを向上しています。__kernel __attribute__ ((reqd_work_group_size(1, 1, 1)))void vaccum(__global const int* a, __global const int* b, __global int*result){int tmp = 0;__attribute__((xcl_pipeline_loop))LOOP_1: for (int i=0; i < 32; i++) {tmp += a[i] * b[i];}result[0] = tmp;}
関連項目• pragma HLS pipeline
• 『SDAccel 環境最適化ガイド』 (UG1207)
• 『Vivado Design Suite ユーザー ガイド: 高位合成』 (UG902)
付録 A: OpenCL 属性
Vivado HLS 最適化手法ガイド 77UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
xcl_pipeline_workitems説明ワーク アイテムをパイプライン処理してレイテンシおよびスループットを向上します。ワークアイテムのパイプライン処理は、カーネルのワーク グループに対するループのパイプライン処理の拡張です。これは、カーネルのスループットおよびパフォーマンスを最大にするために必要です。構文OpenCL ソースのパイプライン処理する要素の前に配置します。__attribute__((xcl_pipeline_workitems))
例 1
次の例の reqd_work_group_size 属性を処理するため、SDAccel により ND 範囲 (3,1,1) の 3次元特性を処理するループ ネストが自動的に挿入されます。この追加のループ ネストのため、このカーネルの実行プロファイルはパイプライン処理されていないループのようになります。xcl_pipeline_workitems 属性を追加すると、同時実行性が追加され、コードのスループットが向上します。kernel__attribute__ ((reqd_work_group_size(3,1,1)))void foo(...){...__attribute__((xcl_pipeline_workitems)) {int tid = get_global_id(0);op_Read(tid);op_Compute(tid);op_Write(tid);}...}
例 2
次の例では、カーネルの適切な要素にワーク アイテム パイプライン処理を追加しています。__kernel __attribute__ ((reqd_work_group_size(8, 8, 1)))void madd(__global int* a, __global int* b, __global int* output){int rank = get_local_size(0);__local unsigned int bufa[64];__local unsigned int bufb[64];__attribute__((xcl_pipeline_workitems)) {
付録 A: OpenCL 属性
Vivado HLS 最適化手法ガイド 78UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
int x = get_local_id(0);int y = get_local_id(1);bufa[x*rank + y] = a[x*rank + y];bufb[x*rank + y] = b[x*rank + y];}barrier(CLK_LOCAL_MEM_FENCE);__attribute__((xcl_pipeline_workitems)) {int index = get_local_id(1)*rank + get_local_id(0);output[index] = bufa[index] + bufb[index];}}
関連項目• pragma HLS pipeline
• 『SDAccel 環境最適化ガイド』 (UG1207)
• 『Vivado Design Suite ユーザー ガイド: 高位合成』 (UG902)
xcl_reqd_pipe_depth説明重要: パイプの宣言には、小文字と数字を使用する必要があります。また、パイプで使用される変数では printf() はサポートされません。OpenCL 2.0 仕様には、パイプと呼ばれる新しいメモリ オブジェクトが導入されています。パイプには、FIFO として構成されたデータが格納されます。パイプを使用すると、FPGA デバイス内の 1 つのカーネルから別のカーネルにデータを外部メモリを使用せずにストリーミングでき、全体的なシステム レイテンシを大幅に向上できます。SDAccel 開発環境では、パイプはすべてのカーネル関数の外部にスタティックに定義する必要があります。パイプの深さは、パイプ宣言内で xcl_reqd_pipe_depth 属性を使用して指定する必要があります。pipe int p0 __attribute__((xcl_reqd_pipe_depth(512)));
パイプは、ノンブロッキング モードの標準 OpenCL read_pipe() および write_pipe() ビルトイン関数、またはブロッキング モードのザイリンクスの拡張 read_pipe_block() およびwrite_pipe_block() 関数を使用してのみアクセス可能です。重要: 1 つのパイプは、異なるカーネル内に 1 つのプロデューサーおよびコンシューマーのみを持つことができます。
付録 A: OpenCL 属性
Vivado HLS 最適化手法ガイド 79UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
パイプ オブジェクトはホスト CPU からはアクセスできません。パイプのステータスは、OpenCL get_pipe_num_packets() および get_pipe_max_packets() ビルトイン関数を使用してクエリできます。これらのビルトイン関数の詳細は、Khronos OpenCL Working Groupからの『The OpenCL C Specification』を参照してください。構文パイプ オブジェクトの宣言で設定します。pipe int id __attribute__((xcl_reqd_pipe_depth(n)));
説明:
• id: パイプの ID を指定します。小文字と数字のみで指定する必要があります。たとえば、inFifo1 ではなく infifo1のように指定します。
• n: パイプの深さを指定します。有効な値は 16、32、64、128、256、512、1024、2048、4096、8192、16384、32768 です。
例次はザイリンクス GitHub からの dataflow_pipes_ocl 例で、パイプを使用してブロッキングread_pipe_block() および write_pipe_block() 関数によりデータを 1 つの処理段階から次の処理段階に渡しています。pipe int p0 __attribute__((xcl_reqd_pipe_depth(32)));pipe int p1 __attribute__((xcl_reqd_pipe_depth(32)));// Input Stage Kernel : Read Data from Global Memory and write into Pipe P0kernel __attribute__ ((reqd_work_group_size(1, 1, 1)))void input_stage(__global int *input, int size){__attribute__((xcl_pipeline_loop))mem_rd: for (int i = 0 ; i < size ; i++){//blocking Write command to pipe P0write_pipe_block(p0, &input[i]);}}// Adder Stage Kernel: Read Input data from Pipe P0 and write the result// into Pipe P1kernel __attribute__ ((reqd_work_group_size(1, 1, 1)))void adder_stage(int inc, int size){__attribute__((xcl_pipeline_loop))execute: for(int i = 0 ; i < size ; i++){int input_data, output_data;//blocking read command to Pipe P0read_pipe_block(p0, &input_data);output_data = input_data + inc;//blocking write command to Pipe P1write_pipe_block(p1, &output_data);}
付録 A: OpenCL 属性
Vivado HLS 最適化手法ガイド 80UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
}// Output Stage Kernel: Read result from Pipe P1 and write the result to // Global Memorykernel __attribute__ ((reqd_work_group_size(1, 1, 1)))void output_stage(__global int *output, int size){__attribute__((xcl_pipeline_loop))mem_wr: for (int i = 0 ; i < size ; i++){//blocking read command to Pipe P1read_pipe_block(p1, &output[i]);}}
関連項目• 『SDAccel 環境最適化ガイド』 (UG1207)
• https://www.khronos.org/
• OpenCL C 仕様
xcl_zero_global_work_offset説明clEnqueueNDRangeKernel を global_work_offset を NULL またはすべて 0 に設定して使用する場合、この属性を使用すると global_work_offset を常に 0 に指定できます。この属性は、次のようなメモリ アクセスがある場合にメモリのパフォーマンスを向上できます。A[get_global_id(x)] = ...;
注記: reqd_work_group_size、vec_type_hint および xcl_zero_global_work_offsetを一緒に指定すると、パフォーマンスを最大限にできます。構文カーネル定義の前、またはそのカーネル用に指定されたプライマリ関数の前に配置します。__kernel __attribute__((xcl_zero_global_work_offset))void test (__global short *input, __global short *output, __constant short *constants) { }
関連項目• reqd_work_group_size
• vec_type_hint
• clEnqueueNDRangeKernel
付録 A: OpenCL 属性
Vivado HLS 最適化手法ガイド 81UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
• 『SDAccel 環境最適化ガイド』 (UG1207)
付録 A: OpenCL 属性
Vivado HLS 最適化手法ガイド 82UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
付録 A: OpenCL 属性
Vivado HLS 最適化手法ガイド 83UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
付録 B
HLS プラグマVivado HLS での最適化SDAccel および SDSoC プロジェクトでは、ハードウェア カーネルを OpenCL、C、または C++言語から RTL に合成し、ザイリンクス デバイスのプログラマブル ロジックにインプリメントできるようにする必要があります。Vivado HLS は、OpenCL、C、および C++ 言語記述から RTLを合成します。Vivado HLS はユーザーの操作なしで SDAccel または SDSoC 開発環境プロジェクトを処理できるよう設計されていますが、生成される RTL コードのレイテンシの削減、スループット パフォーマンスの向上、エリアおよびデバイス リソース使用率の削減など、デザインを最適化するためのプラグマが提供されています。これらのプラグマは、カーネルのソース コードに直接追加できます。重要:SDSoC 環境では HLS プラグマの使用がサポートされますが、関数インターフェイスの引数に適用されるプラグマ (インターフェイス、配列分割、または data_pack プラグマ) はサポートされません。詳細は、『SDSoC 環境プロファイリングおよび最適化ガイド』 (UG1235) の「ハードウェア関数の最適化」を参照してください。Vivado HLS プラグマには、次の最適化タイプがあります。表 11: Vivado HLS プラグマ (タイプ別)
タイプ 属性カーネル最適化 • pragma HLS allocation
• pragma HLS clock
• pragma HLS expression_balance
• pragma HLS latency
• pragma HLS reset
• pragma HLS resource
• pragma HLS top
関数のインライン展開 • pragma HLS inline
• pragma HLS function_instantiate
付録 B: HLS プラグマ
Vivado HLS 最適化手法ガイド 84UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
表 11: Vivado HLS プラグマ (タイプ別) (続き)
タイプ 属性インターフェイス合成 • pragma HLS interface
• pragma HLS protocol
タスク レベルのパイプライン処理 • pragma HLS dataflow
• pragma HLS stream
パイプライン処理 • pragma HLS pipeline
• pragma HLS occurrence
ループ展開 • pragma HLS unroll
• pragma HLS dependence
ループ最適化 • pragma HLS loop_flatten
• pragma HLS loop_merge
• pragma HLS loop_tripcount
配列最適化 • pragma HLS array_map
• pragma HLS array_partition
• pragma HLS array_reshape
構造体のパック • pragma HLS data_pack
pragma HLS allocation説明インプリメントされたカーネルでのリソースの割り当てを制限するインスタンス制限を指定します。特定の関数、ループ、演算、またはコアをインプリメントするのに使用される RTL インスタンスおよびハードウェア リソースの数を定義して制限できます。ALLOCATION プラグマは関数の本体、ループ、またはコード領域内に指定します。たとえば、C ソース コードに foo_sub という関数のインスタンスが 4 つある場合、ALLOCATION プラグマを使用して最終 RTL で foo_sub のインスタンスを 1 つだけにできます。C 関数の 4 つのインスタンスすべてが、同じ RTL ブロックを使用してインプリメントされます。これにより関数で使用されるリソースは削減されますが、パフォーマンスが低下することがあります。
付録 B: HLS プラグマ
Vivado HLS 最適化手法ガイド 85UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
加算、乗算、配列読み出し、書き込みなどの C コードでの演算は、ALLOCATION プラグマを使用して制限できます。合成中に演算子がマップされるコアは、それらの演算子と同じ方法で制限できます。乗算演算子の総数を制限する代わりに、組み合わせ乗算器コアの数を制限して、残りの乗算がパイプライン乗算器を使用して実行されるようにすることもできます (逆も可能)。ALLOCATION プラグマは、指定された関数、ループ、またはコード領域内に適用されますが、config_bind コマンドの -min_op 引数を使用して、デザイン全体で演算子の数を最小限に抑えることもできます。ヒント: 詳細は、『Vivado Design Suite ユーザー ガイド: 高位合成』 (UG902) の「ハードウェアリソースの制御」および「config_bind」を参照してください。
構文このプラグラマは、適用する関数の本体、ループ、または領域内に配置します。#pragma HLS allocation instances=<list> \limit=<value> <type>
説明:
• instances=<list>: 関数、演算子、またはコアの名前を指定します。• limit=<value>: カーネルで使用されるインスタンスの制限を指定します (オプション)。• <type>: ALLOCATION プラグマが、関数、演算、またはコア (デザインを作成するのに使用される加算器、乗算器、パイプライン乗算器、ブロック RAM などのハードウェア コンポーネント) に適用されることを指定します。次のいずれかを指定します。○ function: ALLOCATION プラグマが instances= にリストされたに適用されることを指定します。関数には、次の条件を満たす元の C または C++ コードの関数を指定できます。- pragma HLS inline または set_directive_inline コマンドによりインライン展開されていない。
- Vivado HLS により自動的にインライン展開されていない。○ operation: ALLOCATION プラグマが instances= にリストされた演算に適用されることを指定します。ALLOCATION プラグマを使用して制限可能な演算のリストは、『Vivado Design Suite ユーザー ガイド: 高位合成』 (UG902) を参照してください。
○ core: ALLOCATION がコア (加算器、乗算器、パイプライン乗算器、ブロック RAM などのデザイン作成のために使用される特定ハードウェア コンポーネント) に適用されるよう指定します。使用するコアは、instances= オプションで指定します。コアを指定する場合、ツールで使用される必要のあるコアを指定するか、または指定したコアの制限を定義できます。
付録 B: HLS プラグマ
Vivado HLS 最適化手法ガイド 86UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
例 1
デザインに関数 foo のインスタンスが複数含まれているとします。この例では、ハードウェアカーネルの RTL における foo のインスタンス数を 2 に制限しています。#pragma HLS allocation instances=foo limit=2 function
例 2
次の例では、関数 my_func のインプリメンテーションに使用される乗算演算の数を 1 に制限しています。この制限は、my_func 外にある乗算器、または my_func のサブ関数に含まれる乗算器には適用されません。ヒント: サブ関数のインプリメンテーションに使用される乗算器の数を制限するには、そのサブ関数に ALLOCATION プラグマを指定するか、サブ関数を my_func にインライン展開します。void my_func(data_t angle) {#pragma HLS allocation instances=mul limit=1 operation...}
関連項目• pragma HLS function_instantiate
• pragma HLS inline
• 『Vivado Design Suite ユーザー ガイド: 高位合成』 (UG902)
付録 B: HLS プラグマ
Vivado HLS 最適化手法ガイド 87UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
pragma HLS array_map説明複数の小型の配列を 1 つの大型の配列にまとめ、ブロック RAM リソースを削減します。通常は、pragma HLS array_map コマンド (同じ instance= ターゲット) を使用して、複数の小型の配列を 1 つの大型の配列に組み合わせします。この大型の配列は、1 つの大型メモリ(RAM または FIFO) リソースに配置できます。各配列は、ブロック RAM または UltraRAM (デバイスでサポートされる場合) にマップされます。FPGA で提供される基本ブロック RAM の単位は 18K です。小型の配列の多くで 18K がフルに使用されない場合、小型の配列を 1 つの大型の配列にマップすることでブロック RAM リソースを効率的に使用できます。ヒント: ブロック RAM が 18K を超える場合は、それらが複数の 18K 単位に自動的にマップされます。ARRAY_MAP プラグマでは、複数の小型の配列を 1 つの大型の配列にマップするのに次の 2 つの方法がサポートされています。• 水平マップ: 元の配列を連結して新しい配列を作成します。物理的には、要素数の多い 1 つの配列にインプリメントされます。
• 垂直マップ: 元の配列のワードを連結して、新しい配列を作成します。物理的には、ビット幅の広い 1 つの配列にインプリメントされます。
配列はプラグマが指定された順に、次のものから連結されます。• 水平マップの場合はターゲット要素 0。• 垂直マップの場合はビット 0。構文C ソースの配列変数が定義されている関数内に配置します。#pragma HLS array_map variable=<name> instance=<instance> \<mode> offset=<int>
説明:
• variable=<name>: 必須の引数で、新しいターゲット配列 <instance> にマップする配列変数を指定します。
• instance=<instance>: 配列の結合先である新しい配列の名前を指定します。
付録 B: HLS プラグマ
Vivado HLS 最適化手法ガイド 88UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
• <mode>: 配列マップを horizontal または vertical に指定します (オプション)。○ デフォルトの <mode> は水平マップ (holizontal) で、配列は要素の多い新しい配列に連結されます。
○ 垂直マップ (vertical) では、ワード数が多い新しい配列に連結されます。• offset=<int>: 水平マップにのみ適用されます。配列を新しい配列 <instance> にマップする前に適用するオフセット値を整数で指定します。次に例を示します。○ 配列変数の要素 0 は、新しいターゲットの要素 <int> にマップされます。○ ほかの要素は新しいターゲットの <int+1>、<int+2> にマップされます。
重要: オフセット値が指定されていない場合は、配列要素の重複を避けるため、Vivado HLS で必要なオフセットが自動的に計算されます。例 1
次の例では、関数 foo の配列 array1 および array2 が array3 として指定した 1 つの配列にマップされます。void foo (...) {int8 array1[M];int12 array2[N];#pragma HLS ARRAY_MAP variable=array1 instance=array3 horizontal#pragma HLS ARRAY_MAP variable=array2 instance=array3 horizontal...loop_1: for(i=0;i<M;i++) {array1[i] = ...;array2[i] = ...;...}...}
例 2
次の例では、関数 foo の配列 A[10] および B[15] を、新しい 1 つの配列 AB[25] にマップしています。• 要素 AB[0] は A[0] と同じになります。• 要素 AB[10] は B[0] と同じになります (offset= オプションが使用されていないため)。• 配列 AB[25] のビット幅は A[10] または B[15] の最大ビット幅になります。#pragma HLS array_map variable=A instance=AB horizontal#pragma HLS array_map variable=B instance=AB horizontal
付録 B: HLS プラグマ
Vivado HLS 最適化手法ガイド 89UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
例 3
次の例では、配列 C と D を垂直マップで連結し、C と B を結合したビット幅の新しい配列 CDにまとめています。CD の要素数は、元の配列 C または D の最大数になります。#pragma HLS array_map variable=C instance=CD vertical#pragma HLS array_map variable=D instance=CD vertical
関連項目• pragma HLS array_partition
• pragma HLS array_reshape
• 『Vivado Design Suite ユーザー ガイド: 高位合成』 (UG902)
pragma HLS array_partition説明配列をより小型の配列または個々の要素に分割します。この分割により、次のようになります。• 1 つの大型メモリではなく、複数の小型メモリまたは複数のレジスタを含む RTL が生成されます。
• ストレージの読み出しおよび書き込みポートの数が増加します。• デザインのスループットが向上する可能性があります。• より多くのメモリ インスタンスまたはレジスタが必要となります。構文C ソースの配列変数が定義されている関数内に配置します。#pragma HLS array_partition variable=<name> \<type> factor=<int> dim=<int>
説明:
• variable=<name>: 必須の引数で、パーティションする配列変数を指定します。• <type>: 分割タイプを指定します (オプション)。デフォルトは complete です。次のタイプがサポートされます。
付録 B: HLS プラグマ
Vivado HLS 最適化手法ガイド 90UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
○ cyclic: サイクリック分割では、元の配列の要素をインターリーブすることにより小型の配列を作成します。新しい各配列に要素が 1 つずつ配置され、すべての配列に配置されたら最初の配列に戻って、配列が完全に分割されるまでそれが繰り返されます。たとえばfactor=3 の場合、要素は次のように割り当てられます。- 要素 0 は 1 番目の新しい配列。- 要素 1 は 2 番目の新しい配列。- 要素 2 は 3 番目の新しい配列。- 要素 3 は再び 1 番目の新しい配列。
○ block: ブロック分割では、元の配列の連続したブロックから小型の配列を作成します。Nが factor= 引数で定義される整数だとすると、1 つの配列が N 個のブロックに分割されます。
○ complete: 完全分割では、配列を個々の要素に分割します。1 次元配列の場合は、メモリが個々のレジスタに分割されます。これがデフォルトの <type> です。
• factor=<int>: 作成する小型配列の数を指定します。重要: 完全分割では指定しません。ブロック分割およびサイクリック分割では、factor= は必須です。• dim=<int>: 多次元配列のどの次元を分割するかを指定します。配列が N 次元の場合、0 ~
N の整数を指定します。○ 0 を指定すると、多次元配列のすべての次元が指定したタイプおよび係数オプションで分割されます。
○ 0 以外の値を指定すると、指定した次元のみが分割されます。たとえば、1 を指定した場合、最初の次元のみが分割されます。
例 1
次の例では、13 要素の配列 AB[13] をブロック分割を使用して 4 つの配列に分割しています。#pragma HLS array_partition variable=AB block factor=4
ヒント:4 は 13 の因数ではないので、次のように分割されます。• 3 つの配列には要素が 3 個ずつ含まれます。• 1 つの配列に 4 つのエレメント (AB[9:12])
付録 B: HLS プラグマ
Vivado HLS 最適化手法ガイド 91UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
例 2
次の例では、2 次元配列 AB[6][4] の 2 番目の次元を次元 [6][2] の配列 2 つに分割しています。#pragma HLS array_partition variable=AB block factor=2 dim=2
例 3
次の例では、2 次元配列 in_local の 2 番目の次元を個々の要素に分割しています。int in_local[MAX_SIZE][MAX_DIM]; #pragma HLS ARRAY_PARTITION variable=in_local complete dim=2
関連項目• pragma HLS array_map
• pragma HLS array_reshape
• 『Vivado Design Suite ユーザー ガイド: 高位合成』 (UG902)
• xcl_array_partition
• 『SDAccel 環境最適化ガイド』 (UG1207)
pragma HLS array_reshape説明配列分割と垂直配列マップを組み合わせます。ARRAY_RESHAPE プラグマは、配列を複数の小型の配列に分割する ARRAY_PARTITION と、ビット幅を増やして配列の要素を連結する ARRAY_MAP の垂直タイプを組み合わせたものです。これにより、使用されるブロック RAM の数を削減すると共に、分割の主な利点であるデータへの並列アクセスを実現できます。このプラグマでは元の配列よりも要素数が少なくビット幅の広い配列が作成され、1 クロック サイクルでアクセスできるデータ量が増加します。
付録 B: HLS プラグマ
Vivado HLS 最適化手法ガイド 92UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
次のようなコードがあるとします。void foo (...) {int array1[N];int array2[N];int array3[N];#pragma HLS ARRAY_RESHAPE variable=array1 block factor=2 dim=1#pragma HLS ARRAY_RESHAPE variable=array2 cycle factor=2 dim=1#pragma HLS ARRAY_RESHAPE variable=array3 complete dim=1...}
ARRAY_RESHAPE プラグマを使用すると、配列が次の図に示すように変換されます。図 2: ARRAY_RESHAPE プラグマ
0 1 2 ... N-3 N-2 N-1
N/2 ... N-2 N-10 1 ... (N/2-1)
1 ... N-3 N-10 2 ... N-2
block
cyclic
complete
X14307-110217
0 1 2 ... N-3 N-2 N-1
0 1 2 ... N-3 N-2 N-1
array1[N]
array2[N]
array3[N] N-1N-2...10
MSBLSB
MSBLSB
MSB
LSB
array4[N/2]
array5[N/2]
array6[1]
構文C ソースの配列変数が定義されている関数の領域内に配置します。
#pragma HLS array_reshape variable=<name> \<type> factor=<int> dim=<int>
説明:
• <name>: 必須の引数で、再形成する配列変数を指定します。• <type>: 分割タイプを指定します (オプション)。デフォルトは complete です。次のタイプがサポートされます。○ cyclic タイプでは、元の配列の要素をインターリーブすることにより小型配列が作成されます。たとえば、factor=3 の場合、要素 0 は新しく作成される 1 番目の配列に割り当てられ、要素 1 は 2 番目、要素 2 は 3 番目、要素 3 は 1 番目の配列に割り当てられます。最終的な配列は、新しい配列を 1 つの配列に垂直連結 (ワードを連結してワード数が大きいものを作成) したものになります。
付録 B: HLS プラグマ
Vivado HLS 最適化手法ガイド 93UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
○ block: ブロック タイプの再形成では、元の配列の連続したブロックから小型配列が作成されます。これにより、配列が N 個 (N は factor= オプションで定義されている数) のブロックに分割され、その N 個のブロックが word-width*N で 1 つの配列にまとめられます。
○ complete タイプでは、配列を一時的に個々の要素に分割してから、ワード数の大きい 1つの配列にまとめます。1 次元配列の場合、これはワード数が非常に大きいレジスタを 1つ作成するのと同じです (元の配列が N 個の M ビット要素を含む場合、N*M ビットのレジスタとなる)。これが配列再形成のデフォルト タイプです。
• factor=<int>: 現在の配列を分割する量 (一時的に作成する配列数) を指定します。2 に設定すると、配列が 2 分割され、ビット幅は倍になります。3 に設定すると、配列が 3 分割され、ビット幅は 3 倍になります。
重要: 完全分割では指定しません。ブロック分割およびサイクリック分割では、factor= は必須です。• dim=<int>: 多次元配列のどの次元を分割するかを指定します。配列が N 次元の場合、0 ~
N の整数を指定します。○ 0 を指定すると、多次元配列のすべての次元が指定したタイプおよび係数オプションで分割されます。
○ 0 以外の値を指定すると、指定した次元のみが分割されます。たとえば、1 を指定した場合、最初の次元のみが分割されます。
• object: コンテナー配列のみに関連します。このキーワードを指定すると、ARRAY_RESHAPE プラグマがコンテナー内のオブジェクトに適用され、コンテナー内のオブジェクトのすべての次元が再形成されますが、コンテナー自体のすべての次元は保持されます。このキーワードを指定しない場合、プラグマはコンテナー配列に適用され、オブジェクトには適用されません。
例 1
次の例では、17 個の要素を含む 8 ビット配列 AB[17] を、ブロック マップを使用して 5 つの要素を含む 32 ビット配列に再形成 (分割およびマップ) しています。#pragma HLS array_reshape variable=AB block factor=4
ヒント: factor=4 は、配列を 4 つに分割することを指定します。つまり、17 個の要素がビット幅が 4 倍の 5 つの要素を含む配列に再形成されます。この場合、最後の要素 AB[17] は、5 番目の要素の下位 8 ビットにマップされ、5 番目の要素の残りは空になります。
付録 B: HLS プラグマ
Vivado HLS 最適化手法ガイド 94UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
例 2
次の例では、2 次元配列 AB[6][4] を次元 [6][2] の配列 1 つに再形成しています。この次元 2 のビット幅は 2 倍です。#pragma HLS array_reshape variable=AB block factor=2 dim=2
例 3
次の例では、関数 foo の 3 次元の 8 ビット配列 AB[4][2][2] を 128 ビット幅は (4*2*2*8) の 1 要素配列 (1 つのレジスタ) に再形成しています。#pragma HLS array_reshape variable=AB complete dim=0
ヒント: dim=0 は、配列のすべての次元を再形成することを指定します。
関連項目• pragma HLS array_map
• pragma HLS array_partition
• 『Vivado Design Suite ユーザー ガイド: 高位合成』 (UG902)
• 『SDAccel 環境プロファイリングおよび最適化ガイド』 (UG1207)
pragma HLS clock説明指定したクロックを指定した関数に適用します。C および C++ デザインでは、クロック 1 つのみがサポートされます。create_clock で指定したクロック周期が、デザイン内のすべての関数に適用されます。SystemC デザインでは、複数のクロックがサポートされます。create_clock コマンドを使用して複数のクロックを指定し、pragma HLS clock を使用して個々の SC_MODULE に適用できます。各 SC_MODULE は、1 つのクロックを使用して合成されます。構文C ソースの関数本体内に配置します。#pragma HLS clock domain=<clock>
付録 B: HLS プラグマ
Vivado HLS 最適化手法ガイド 95UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
説明:
• domain=<clock>: クロック名を指定します。重要: create_clock コマンドを使用して既に作成されているクロックを指定する必要があります。create_clock と同等のプラグマはありません。詳細は、『Vivado Design Suite ユーザー ガイド: 高位合成』 (UG902) を参照してください。
例 1
最上位 foo_top にクロック ポート fast_clock および slow_clock を含む SystemC デザインがあるとします。foo_top ではその関数内で fast_clock のみが使用され、サブブロックfoo_sub では slow_clock のみが使用されます。この例では、Vivado HLS ツールを起動したときに指定された script.tcl ファイルで次のcreate_clock コマンドが指定されています。create_clock -period 15 fast_clkcreate_clock -period 60 slow_clk
C ソース ファイルで次のプラグマを指定し、クロックを指定の関数 foo_sub および foo_topに割り当てています。foo_sub (p, q) { #pragma HLS clock domain=slow_clock ...}void foo_top { a, b, c, d} { #pragma HLS clock domain=fast_clock ...
関連項目• 『Vivado Design Suite ユーザー ガイド: 高位合成』 (UG902)
pragma HLS data_pack説明構造体 (struct) のデータ フィールドをワード幅が広い 1 つのスカラーにパックします。
付録 B: HLS プラグマ
Vivado HLS 最適化手法ガイド 96UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
DATA_PACK プラグマは、struct のすべての要素を 1 つの幅の広いベクターにパックし、変数に必要なメモリを削減すると同時に、struct のすべてのメンバーを同時に読み出しおよび書き込みできるようにします。ワード数のビット アライメントは、struct フィールドの宣言から自動推論されます。最初のフィールドはベクターの LSB で、struct の最後の要素がベクターのMSB に揃えられます。struct に配列が含まれる場合、DATA_PACK プラグマにより ARRAY_RESHAPE プラグマと同様の処理が実行され、再形成された配列が struct 内のほかの要素とまとめられます。struct 内で宣言されている配列はすべて完全に分割されて幅の広いスカラーにまとめられ、ほかのスカラー フィールドと共にパックされます。ただし、struct を DATA_PACK と ARRAY_PARTITIONまたは ARRAY_RESHAPE を同時に使用して最適化することはできません。これらのプラグマは同時に使用できません。重要: 大きな配列を含む struct に DATA_PACK 最適化を使用する際には注意が必要です。配列に int 型の要素が 4096 個含まれる場合、ベクター (およびポート) の幅は 4096*32=131072 ビットになります。Vivado HLS でこの RTL デザインは作成できますが、FPGA インプリメンテーション中に論理合成でこれが配線できることはほとんどありません。通常は、任意精度 (またはビット精度) データ型を使用することをお勧めします。標準 C 型は 8ビット境界 (8、16、32、64 ビット) に基づいていますが、デザインで任意精度型を使用すると、合成前に C コードでビット サイズを指定できます。ビット精度幅の方が、小型で高速なハードウェア演算子が得られます。これにより、より多くのロジックが FPGA に配置できるようになり、ロジックがより高速のクロック周波数で実行できるようになります。DATA_PACK プラグマでは、必要に応じて、パック型 struct のデータを 8 ビット境界に揃えることもできます。struct ポートを AXI4 インターフェイスを使用してインプリメントする場合は、DATA_PACK<byte_pad> オプションを使用して struct のメンバー要素が 8 ビット境界に自動的に揃えられるようにすることを考慮してください。AXI4-Stream プロトコルでは、IP の TDATA ポートの幅を 8 の倍数にする必要があります。TDATA ポートの幅が 8 の倍数でない AXI4-Stream IP を定義することは仕様違反となるので、TDATA の幅をバイトの倍数に繰り上げる必要があります。詳細は、『Vivado Design Suite ユーザー ガイド: 高位合成』 (UG902) の「インターフェイス合成および構造体」を参照してください。構文パックする struct 変数の定義の近くに配置します。#pragma HLS data_pack variable=<variable> \instance=<name> <byte_pad>
説明:
• variable=<variable>: パックする変数を指定します。
付録 B: HLS プラグマ
Vivado HLS 最適化手法ガイド 97UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
• instance=<name>: パック後の変数の名前を指定します。<name> を指定しない場合は、<variable> の名前が使用されます。
• <byte_pad>: データを 8 ビット境界 (8 ビット、16 ビット、24 ビット...) 上にパックするかを指定します (オプション)。次の 2 つの値がサポートされます。○ struct_level: まず struct 全体をパックし、次の 8 ビット境界までパディングします。○ field_level: struct の各要素 (フィールド) をそれぞれ 8 ビット境界までパディングしてから、struct をパックします。
ヒント: データの複数のフィールドをバイト境界に揃える前 (field_level) または後(struct_level) に連結するかは、通常データがどれだけアトミックであるかによって決定されます。アトミック情報はそれのみで解釈可能なデータであり、非アトミック情報はデータの解釈には不完全です。たとえば、アトミック データには浮動小数点値のすべてのビットが含まれます。浮動小数点値の指数ビットのみでは、アトミックではありません。TDATA に情報をパックする際は通常、ビット幅にかかわらず、データの非アトミック ビットはアトミック ユニットを形成するまで連結されます。その後、アトミック ユニットが必要に応じてパディング ビットを使用してバイト境界にそろえられます。例 1
次の例では、3 つの 8 ビット フィールド (R、G、B) を含む struct 配列 AB[17] を、17 個の 24ビット要素の配列 1 つにパックしています。typedef struct{unsigned char R, G, B;} pixel;
pixel AB[17];#pragma HLS data_pack variable=AB
例 2
次の例では、関数 foo にある 3 つの 8 ビットフィールドを含む struct ポインター AB (typedefstruct {unsigned char R, G, B;} pixel) を 1 つの 24 ビット ポインターにパックしています。typedef struct{unsigned char R, G, B;} pixel;
pixel AB;#pragma HLS data_pack variable=AB
付録 B: HLS プラグマ
Vivado HLS 最適化手法ガイド 98UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
例 3
次の例では、rgb_to_hsv 関数の in および out 引数に DATA_PACK プラグマを指定して構造体を 8 ビット境界にパックし、メモリ アクセスを向上しています。void rgb_to_hsv(RGBcolor* in, // Access global memory as RGBcolor struct-wise HSVcolor* out, // Access Global Memory as HSVcolor struct-wise int size) {#pragma HLS data_pack variable=in struct_level#pragma HLS data_pack variable=out struct_level...}
関連項目• pragma HLS array_partition
• pragma HLS array_reshape
• 『Vivado Design Suite ユーザー ガイド: 高位合成』 (UG902)
pragma HLS dataflow説明DATAFLOW プラグマは、タスク レベルのパイプライン処理をイネーブルにして関数およびループを重複できるようにし、RTL インプリメンテーションでの同時実行性を増加してデザイン全体のスループットを向上します。C 記述では、すべての演算が順次に実行されます。pragma HLS allocation などのリソースを制限する指示子を指定しない場合は、Vivado HLS ではレイテンシを最小限に抑え、同時実行性を向上するように処理されます。ただし、データ依存性のためにこれが制限されることがあります。たとえば、配列にアクセスする関数またはループは、完了する前に配列への読み出し/書き込みアクセスをすべて終了する必要があります。そのため、そのデータを消費する次の関数またはループの演算を開始できません。DATAFLOW データ最適化を使用すると、前の関数またはループがすべての演算を完了する前に、次の関数またはループの演算を開始できるようになります。
付録 B: HLS プラグマ
Vivado HLS 最適化手法ガイド 99UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
図 3: DATAFLOW プラグマvoid top (a,b,c,d) { ... func_A(a,b,i1); func_B(c,i1,i2); func_C(i2,d)
return d;}
func_Afunc_Bfunc_C
8 cycles
func_A func_B func_C
8 cycles
3 cycles
func_Afunc_B
func_C
func_Afunc_B
func_C
5 cycles
(A) Without Dataflow Pipelining (B) With Dataflow Pipelining
X14266-110217
DATAFLOW プラグマを指定した場合、Vivado HLS で順次関数またはループ間のデータフローが解析され、プロデューサー関数またはループが完了する前にコンシューマー関数またはループの演算を開始できるように、ピンポン RAM または FIFO に基づいてチャネルが作成されます。これにより関数またはループを並列実行でき、レイテンシが削減されて RTL のスループットが向上します。開始間隔 (II) (関数またはループの開始から次の関数またはループの開始までのサイクル数) が指定されていない場合は、Vivado HLS で開始間隔が最小になるようにし、データが使用可能になったらすぐに演算を開始できるようにすることが試みられます。ヒント: config_dataflow コマンドは、データフロー最適化で使用されるデフォルトのメモリチャネルと FIFO の深さを指定します。詳細は、『Vivado Design Suite ユーザー ガイド: 高位合成』 (UG902) の「config_dataflow」を参照してください。DATAFLOW 最適化が機能するようにするには、デザイン内でデータが 1 つのタスクから次のタスクに流れる必要があります。次のコーディング スタイルを使用すると、Vivado HLS でDATAFLOW 最適化が実行されなくなります。• シングル プロデューサー コンシューマー違反• タスクのバイパス• タスク間のフィードバック• タスクの条件付き実行
付録 B: HLS プラグマ
Vivado HLS 最適化手法ガイド 100UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
• 複数の exit 条件を持つループ重要: これらのコーディング スタイルのいずれかが使用されている場合、Vivado HLS でメッセージが表示され、DATAFLOW 最適化は実行されません。最後に、DATAFLOW 最適化には階層インプリメンテーションはありません。サブ関数またはループに DATAFLOW 最適化が有益な可能性のあるタスクが含まれる場合、DATAFLOW 最適化をそのループまたはサブ関数に適用するか、サブ関数をインライン展開する必要があります。構文C ソースの領域、関数、またはループ内に配置します。#pragma HLS dataflow
例 1
次の例では、ループ wr_loop_j 内で DATAFLOW 最適化を指定しています。 wr_loop_j: for (int j = 0; j < TILE_PER_ROW; ++j) {#pragma HLS DATAFLOW wr_buf_loop_m: for (int m = 0; m < TILE_HEIGHT; ++m) { wr_buf_loop_n: for (int n = 0; n < TILE_WIDTH; ++n) {#pragma HLS PIPELINE // should burst TILE_WIDTH in WORD beat outFifo >> tile[m][n]; } } wr_loop_m: for (int m = 0; m < TILE_HEIGHT; ++m) { wr_loop_n: for (int n = 0; n < TILE_WIDTH; ++n) {#pragma HLS PIPELINE outx[TILE_HEIGHT*TILE_PER_ROW*TILE_WIDTH*i+TILE_PER_ROW*TILE_WIDTH*m+TILE_WIDTH*j+n] = tile[m][n]; } }
関連項目• pragma HLS allocation
• 『Vivado Design Suite ユーザー ガイド: 高位合成』 (UG902)
• xcl_dataflow
• 『SDAccel 環境最適化ガイド』 (UG1207)
付録 B: HLS プラグマ
Vivado HLS 最適化手法ガイド 101UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
pragma HLS dependence説明DEPENDENCE プラグマは、ループ運搬依存を克服し、ループをパイプライン処理できるようにする (またはより短い間隔でパイプラインできるようにする) ための追加情報を提供します。Vivado HLS では、次の依存性が自動的に検出されます。• ループ内 (ループ独立依存)。• 同じループの反復間 (ループ運搬依存)。こうした依存は、演算をスケジューリングするタイミング、特に関数およびループのパイプライン処理に影響します。• ループ独立依存: 同じ要素が同じループ反復でアクセスされます。
for (i=0;i<N;i++) { A[i]=x; y=A[i];}
• ループ運搬依存: 同じ要素が異なるループ反復でアクセスされます。for (i=0;i<N;i++) { A[i]=A[i-1]*2;}
複雑な条件下では、自動依存性解析が保守的すぎ、偽依存性が除去されないことがあります。変数依存の配列インデックスや、外部要件を満たす必要があるような状況 (2 つの入力が同じインデックスにならない場合など) では、依存解析が保守的すぎることがあります。DEPENDENCE プラグマを使用すると、依存を明示的に指定し、偽依存を解決できます。重要: 偽依存でないものを偽依存と指定すると、ハードウェアが正しく機能しなくなる可能性があります。依存性 (true または false) が正しいことを確認してから指定してください。
構文依存が定義されている関数内に配置します。#pragma HLS dependence variable=<variable> <class> \<type> <direction> distance=<int> <dependent>
説明:
• variable=<variable>: 依存を考慮する変数を指定します (オプション)。
付録 B: HLS プラグマ
Vivado HLS 最適化手法ガイド 102UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
• <class>: 依存を明確にする必要がある変数のクラスを指定します (オプション)。有効な値はarray または pointer です。
ヒント: 関数内で変数または変数のクラスのいずれかを指定していれば、<class> およびvariable= の両方を指定する必要はありません。• <type>: 有効な値は intra または inter です。依存のタイプを指定します。
○ intra: 同じループ反復内の依存。<type> を intra、<dependent> を false に設定すると、Vivado HLS によりループ内で演算を自由に移動でき、パフォーマンスまたはエリアを向上できる可能性が高くなります。<dependent> を true に設定すると、演算を指定の順序で実行する必要があります。
○ inter: 異なるループ反復間の依存。これがデフォルトの <type> です。<type> をinter、<dependent> を false に設定すると、関数またはループがパイプライン処理されているか、ループが展開されていない場合または部分的に展開されている場合に、並列実行が可能になり、<dependent> を true に設定すると並列実行が不可能になります。
• <direction>: 有効な値は RAW、WAR または WAW です。これはループ運搬依存にのみ関連し、依存の方向を指定します。○ RAW (Read-After-Write - 真の依存): 書き込み命令により書き込まれた値が読み出し命令で使用されます。
○ WAR (Write-After-Read - 非依存): 読み出し命令により値が取得され、その値が書き込み命令で上書きされます。
○ WAW (Write-After-Write - 出力依存): 2 つの書き込み命令により、特定の順序で同じロケーションに書き込みが実行されます。
• distance=<int>: 配列アクセスの反復間隔を指定します。これは、依存が true に設定されているループ運搬依存でのみ使用されます。
• <dependent>: 依存を適用する必要があるか (true)、削除するか (false) を指定します。デフォルトは true です。
例 1
次の例では、Vivado HLS では cols の値を認識する機能がないので、buff_A[1][col] への書き込みと buff_A[1][col] の読み出し間に常に依存性があると想定されます。このようなアルゴリズムでは、cols が 0 になることはほとんどありませんが、Vivado HLS ではデータ依存を想定できません。この依存を解決するため、DEPENDENCE プラグマを使用して、ループ反復間 (この例の場合は buff_A と buff_B の両方) に依存がないことを指定できます。void foo(int rows, int cols, ...) for (row = 0; row < rows + 1; row++) { for (col = 0; col < cols + 1; col++) { #pragma HLS PIPELINE II=1 #pragma HLS dependence variable=buff_A inter false #pragma HLS dependence variable=buff_B inter false if (col < cols) {
付録 B: HLS プラグマ
Vivado HLS 最適化手法ガイド 103UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
buff_A[2][col] = buff_A[1][col]; // read from buff_A[1][col] buff_A[1][col] = buff_A[0][col]; // write to buff_A[1][col] buff_B[1][col] = buff_B[0][col]; temp = buff_A[0][col];}
例 2
次の例では、関数 foo の loop_1 の同じ反復での Var1 の依存を削除しています。#pragma HLS dependence variable=Var1 intra false
例 3
次の例では、関数 foo の loop_2 にあるすべての配列の依存を定義し、同じループ反復ではすべての読み出しが書き込みの後に実行される (RAW) ように設定しています。#pragma HLS dependence array intra RAW true
関連項目• pragma HLS pipeline
• 『Vivado Design Suite ユーザー ガイド: 高位合成』 (UG902)
• xcl_pipeline_loop
• 『SDAccel 環境最適化ガイド』 (UG1207)
pragma HLS expression_balance説明C ベースの仕様が演算のシーケンスで記述され、RTL で長い演算チェーンとなることがあります。クロック周期が短い場合、これによりデザイン レイテンシが増加する可能性あります。デフォルトでは、Vivado HLS で演算の関連性および接続性を考慮して、演算が並べ替えられます。これにより、ツリーのバランスが取られてチェーンが短くなるので、ハードウェア リソースは増加しますが、デザインのレイテンシを削減できます。EXPRESSION_BALANCE プラグマを使用すると、指定のスコープ内でこの演算調整をディスエーブルまたは明示的にイネーブルにできます。
付録 B: HLS プラグマ
Vivado HLS 最適化手法ガイド 104UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
構文C ソースの必要なロケーションの境界内に配置します。#pragma HLS expression_balance off
説明:
• off: 演算調整を指定の場所でオフにします。ヒント: プラグマでこのオプションを使用しないと、デフォルトで演算調整がオンになります。
例 1
次の例では、関数 my_Func 内で演算調整を明示的にオンにしています。void my_func(char inval, char incr) { #pragma HLS expression_balance
例 2
次の例では、関数 my_Func 内で演算調整をオフにしています。void my_func(char inval, char incr) { #pragma HLS expression_balance off
関連項目• 『Vivado Design Suite ユーザー ガイド: 高位合成』 (UG902)
pragma HLS function_instantiate説明FUNCTION_INSTANTIATE プラグマは、関数の階層を保持するエリアの利点を維持しながら、関数の特定のインスタンスに対してローカル最適化を実行する優れたオプションを提供する最適化手法です。これにより関数呼び出し周辺の制御ロジックが単純になり、レイテンシおよびスループットが向上することがあります。デフォルトでは次のようになります。• 関数は RTL で個別の階層ブロックのままになります。
付録 B: HLS プラグマ
Vivado HLS 最適化手法ガイド 105UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
• 同じ階層レベルにある関数のすべてのインスタンスは、同じ RTL インプリメンテーション (ブロック) を使用します。
FUNCTION_INSTANTIATE プラグマは、関数の各インスタンスに固有の RTL インプリメンテーションを作成し、関数呼び出しに応じて各インスタンスをローカルで最適化できるようにします。このプラグマを使用すると、関数が呼び出されたときに関数の一部の入力が定数であることがあるという事実を利用して、周辺の制御構造が単純化され、小型で最適化された関数ブロックが作成されます。FUNCTION_INSTANTIATE プラグマを使用しない場合、次のコードから、foo 内の関数の 3 つのインスタンスすべてに対して関数 foo_sub の RTL インプリメンテーションが 1 つ作成されます。関数 foo_sub の各インスタンスはまったく同じようにインプリメントされます。これは、関数を再利用して、関数の各インスタンス呼び出しに必要なエリアを削減するには良いですが、foo_sub の各呼び出しのバリエーションを考慮するため、関数内の制御ロジックがより複雑なものとなります。char foo_sub(char inval, char incr) {#pragma HLS function_instantiate variable=incr return inval + incr;}void foo(char inval1, char inval2, char inval3, char *outval1, char *outval2, char * outval3){ *outval1 = foo_sub(inval1, 1); *outval2 = foo_sub(inval2, 2); *outval3 = foo_sub(inval3, 3);}
上記のコード例では、FUNCTION_INSTANTIATE プラグマにより関数 foo_sub の 3 つのインプリメンテーションが生成され、それぞれを個別に incr 引数で最適化されるので、エリアが削減されて関数のパフォーマンスが向上します。FUNCTION_INSTANTIATE 最適化の後、foo_sub は 3 つの個別の関数に変換され、それぞれ incr に指定された値によって最適化されます。構文C ソースの必要なロケーションの境界内に配置します。#pragma HLS function_instantiate variable=<variable>
説明:
• variable=<variable>: 必須の引数で、定数として使用する関数引数を定義します。
付録 B: HLS プラグマ
Vivado HLS 最適化手法ガイド 106UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
例 1
次の例では、関数 swInt に FUNCTION_INSTANTIATE プラグマを配置し、関数 swInt の各インスタンスが maxv 関数引数に対して個別に最適化されるようにしています。void swInt(unsigned int *readRefPacked, short *maxr, short *maxc, short *maxv){#pragma HLS function_instantiate variable=maxv uint2_t d2bit[MAXCOL]; uint2_t q2bit[MAXROW];#pragma HLS array partition variable=d2bit,q2bit cyclic factor=FACTOR
intTo2bit<MAXCOL/16>((readRefPacked + MAXROW/16), d2bit); intTo2bit<MAXROW/16>(readRefPacked, q2bit); sw(d2bit, q2bit, maxr, maxc, maxv);}
関連項目• pragma HLS allocation
• pragma HLS inline
• 『Vivado Design Suite ユーザー ガイド: 高位合成』 (UG902)
pragma HLS inline説明関数を階層の別エンティティとして削除します。インライン展開された関数は呼び出し関数に分解され、RTL で別の階層として表示されなくなります。関数をインライン展開すると、関数内の演算が共有され、周辺の演算と効率よく最適化されるようになることがあります。ただし、インライン展開された関数は共有できないので、RTL をインプリメントするのに必要なエリアが増加する可能性があります。INLINE プラグマは、その指定方法によって、定義されているスコープに異なる方法で適用されます。• INLINE: 引数を指定しない場合、関数は呼び出し関数または領域に上向きにインライン展開されます。
• INLINE OFF: プラグマを指定した関数を呼び出し関数または領域にインライン展開しないよう指定します。これにより、自動的にインライン展開、あるいは領域または再帰の一部としてインライン展開されるはずであった関数のインライン展開がディスエーブルになります。
• INLINE REGION: プラグマを領域または関数の本体に適用します。領域または関数の内容が下向きにインライン展開されますが、階層全体に再帰的にインライン展開されることはありません。
付録 B: HLS プラグマ
Vivado HLS 最適化手法ガイド 107UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
• INLINE RECURSIVE: プラグマを領域または関数の本体に適用します。領域または関数の内容が下向きに再帰的にインライン展開されます。
デフォルトでは、インライン展開は関数階層のすぐ下の階層でのみ実行され、サブ関数では実行されません。recursive オプションを使用すると、階層全体でインライン展開が指定されます。構文C ソースの関数本体内またはコードの領域内に配置します。#pragma HLS inline <region | recursive | off>
説明:
• region: 指定した領域内の (または関数の本体に含まれる) すべての関数をインライン展開するよう指定し、領域スコープに適用します。
• recursive: デフォルトでは、関数のインライン展開は 1 つの階層レベルでのみ実行され、指定の関数内の関数はインライン展開されません。recursive オプションを使用すると、指定の関数または領域内のすべての関数が再帰的にインライン展開されます。
• off: 関数のインライン展開をオフにし、指定の関数がインライン展開されないようにします。たとえば、関数に recursive オプションが指定されている場合、ほかの関数はインライン展開されても、特定の呼び出された関数がインライン展開されないようにできます。
ヒント: Vivado HLS では小型の関数が自動的にインライン展開されるので、INLINE プラグマとoff オプションを使用してこの自動インライン展開が実行されないようにできます。
例 1
次の例では、指定した領域内 (foo_top の本体) のすべての関数をインライン展開しています。この関数内の下位関数はインライン展開されません。void foo_top { a, b, c, d} { #pragma HLS inline region ...
付録 B: HLS プラグマ
Vivado HLS 最適化手法ガイド 108UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
例 2
次の例では、foo_top の本体内のすべての関数がインライン展開され、この関数に含まれる階層すべてで再帰的にインライン展開が実行されます。ただし、関数 foo_sub はインライン展開されません。recursive プラグマは関数 foo_top に配置されています。関数 foo_sub にはインライン展開をディスエーブルにするプラグマが配置されています。foo_sub (p, q) {#pragma HLS inline offint q1 = q + 10;foo(p1,q);// foo_3...}void foo_top { a, b, c, d} { #pragma HLS inline region recursive ... foo(a,b);//foo_1 foo(a,c);//foo_2 foo_sub(a,d); ...}
注記: この例では、INLINE は関数 foo_top の内容に下向きに適用されますが、foo_sub を呼び出すコードには上向きに適用されます。例 3
次の例では、copy_output 関数が copy_output を呼び出す関数または領域にインライン展開されます。void copy_output(int *out, int out_lcl[OSize * OSize], int output) {#pragma HLS INLINE // Calculate each work_item's result update location int stride = output * OSize * OSize; // Work_item updates output filter/image in DDR writeOut: for(int itr = 0; itr < OSize * OSize; itr++) { #pragma HLS PIPELINE out[stride + itr] = out_lcl[itr]; }
関連項目• pragma HLS allocation
• pragma HLS function_instantiate
• 『Vivado Design Suite ユーザー ガイド: 高位合成』 (UG902)
付録 B: HLS プラグマ
Vivado HLS 最適化手法ガイド 109UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
pragma HLS interface説明C ベース デザインでは、すべての入力および出力操作がフォーマル関数引数を使用して 0 時間で実行されます。RTL デザインでは、同じ入力および出力操作をデザイン インターフェイスのポートを介して実行する必要があり、通常は特定の I/O (入力/出力) プロトコルを使用して実行されます。詳細は、『Vivado Design Suite ユーザー ガイド: 高位合成』 (UG902) の「インターフェイスの管理」を参照してください。INTERFACE プラグマは、インターフェイス合成で関数定義からどのように RTL ポートが作成されるかを指定します。RTL インプリメンテーションのポートは次のものから導出されます。• 指定されている任意の関数レベルのプロトコル。• 関数引数。• 最上位関数によりアクセスされ、スコープ外部で定義されるグローバル変数。関数レベルのプロトコルは、ブロック レベルの I/O プロトコルとも呼ばれ、関数の演算が開始したときに制御する信号と、関数の演算が終了したこと、アイドル状態であること、新しい入力を受信できる状態であることを通知する信号を提供します。関数レベルのプロトコルのインプリメンテーションは次のようになります。• <mode> 値により ap_ctrl_none、ap_ctrl_hs、または ap_ctrl_chain のいずれかに指定されます。デフォルトは ap_ctrl_hs ブロック レベル I/O プロトコルです。
• 関数名に関連付けられています。各関数引数は、有効ハンドシェイク (ap_vld) や肯定応答ハンドシェイク (ap_ack) などの独自のポート レベル (I/O) インターフェイス プロトコルを持つように指定できます。ポート レベルインターフェイス プロトコルは、最上位関数の各引数および関数の戻り値 (関数が値を返す場合) に対して作成されます。作成されるデフォルトの I/O プロトコルは、C 引数のタイプによって異なります。ブロック レベルのプロトコルが使用されてブロックの演算が開始したら、ポート レベルの I/O プロトコルを使用して、データをブロック内外に順に送信できます。グローバル変数がアクセスされても、すべての読み出しおよび書き込みがデザインのローカルである場合は、リソースはデザイン内に作成され、RTL に I/O ポートは必要ありません。グローバル変数が外部ソースまたはデスティネーションである場合は、インターフェイスは標準関数引数と同様の方法で指定します。例を参照してください。
付録 B: HLS プラグマ
Vivado HLS 最適化手法ガイド 110UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
INTERFACE プラグマがサブ関数に使用されている場合は、register オプションのみを使用できます。サブ関数では、<mode> オプションはサポートされません。ヒント: サブ関数で使用される I/O プロトコルは Vivado HLS により自動的に決定されます。ポートにレジスタを付けるかどうかを指定することを除き、これらのポートを制御することはできません。構文関数内に配置します。#pragma HLS interface <mode> port=<name> bundle=<string> \register register_mode=<mode> depth=<int> offset=<string> \clock=<string> name=<string> \num_read_outstanding=<int> num_write_outstanding=<int> \max_read_burst_length=<int> max_write_burst_length=<int>
説明:
• <mode>: 関数引数、関数で使用されるグローバル変数、またはブロック レベルの制御プロトコルのインターフェイス プロトコル モードを指定します。これらのモードの詳細な説明は、『Vivado Design Suite ユーザー ガイド: 高位合成』 (UG902) の「インターフェイス合成リファレンス」を参照してください。モードは次のいずれかに指定できます。○ ap_none: プロトコルなし。インターフェイスはデータ ポートです。○ ap_stable: プロトコルなし。インターフェイスはデータ ポートです。Vivado HLS では、リセット後はデータ ポートが常に安定していると想定され、内部最適化により不要なレジスタが削除されます。
○ ap_vld: データ ポートと、データが読み出しまたは書き込みに対して有効になったことを示す valid ポートがインプリメントされます。
○ ap_ack: データ ポートと、データが読み出された/書き込まれたことを肯定応答するacknowledge ポートがインプリメントされます。
○ ap_hs: データ ポートと、データが読み出しおよび書き込みに対して有効になったことを示す valid ポートおよびデータが読み出された/書き込まれたことを肯定応答するacknowledge ポートがインプリメントされます。
○ ap_ovld: 出力データ ポートと、データが読み出しまたは書き込みに対して有効になったことを示す valid ポートがインプリメントされます。
重要: 入力引数または読み出し/書き込み引数の入力部分は ap_none モードを使用してインプリメントされます。
○ ap_fifo: 標準 FIFO インターフェイスのポートが、アクティブ Low FIFO の empty および full ポートが関連付けられたデータ入力および出力ポートを使用してインプリメントされます。
注記: このインターフェイスは、読み出し引数または書き込み引数のみに使用できます。ap_fifo モードでは双方向の読み出し/書き込み引数はサポートされません。
付録 B: HLS プラグマ
Vivado HLS 最適化手法ガイド 111UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
○ ap_bus: ポインターおよび参照渡しポートがバス インターフェイスとしてインプリメントされます。
○ ap_memory: 配列引数が標準 RAM インターフェイスとしてインプリメントされます。Vivado IP インテグレーターで RTL デザインを使用する場合は、メモリ インターフェイスは個別のポートととして表示されます。
○ bram: 配列引数が標準 RAM インターフェイスとしてインプリメントされます。Vivado IPインテグレーターで RTL デザインを使用する場合は、メモリ インターフェイスはシングルポートとして表示されます。
• ○ axis: すべてのポートが AXI4-Stream インターフェイスとしてインプリメントされます。○ s_axilite: すべてのポートが AXI4-Lite インターフェイスとしてインプリメントされます。Vivado HLS では、RTL のエクスポート中に C ドライバー ファイルの関連セットが生成されます。
○ m_axi: すべてのポートが AXI4 インターフェイスとしてインプリメントされます。32 ビット (デフォルト) または 64 ビットのアドレス ポートを指定し、アドレス オフセットを制御するには、config_interface コマンドを使用できます。
○ ap_ctrl_none: ブロック レベル I/O プロトコルなし。注記: ap_ctrl_none を使用すると、C/RTL の協調シミュレーション機能を使用してデザインを検証できなくなることがあります。
○ ap_ctrl_hs: デザインの演算を start し、デザインが idle、done、および新しい入力データに対して ready になっていることを示すブロック レベルの制御ポート セットをインプリメントします。
注記: ap_ctrl_hs モードはデフォルトのブロック レベル I/O プロトコルです。○ ap_ctrl_chain: デザインの演算を start および continue し、デザインが idle、
done、および新しい入力データに対して ready になっていることを示すブロック レベルの制御ポート セットをインプリメントします。
注記: ap_ctrl_chain インターフェイス モードは ap_ctrl_hs と似ていますが、バック プレッシャーを適用するために入力信号 ap_continue が追加されている点が異なります。Vivado HLS ブロックをチェーン接続する場合は、ap_ctrl_chain ブロックレベル I/O プロトコルを使用することをお勧めします。• port=<name>: INTERFACE プラグマを適用する関数引数、関数戻り値、またはグローバル変数の名前を指定します。
ヒント: 関数の return 値のポートには、ブロックレベル I/O プロトコル (ap_ctrl_none、ap_ctrl_hs、または ap_ctrl_chain) を割り当てることができます。
付録 B: HLS プラグマ
Vivado HLS 最適化手法ガイド 112UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
• bundle=<string>: 関数引数を AXI ポートにまとめます。デフォルトでは、Vivado HLS でAXI4-Lite (s_axilite) インターフェイスとして指定されたすべての関数引数は 1 つのAXI4-Lite ポートにまとめられます。同様に、AXI4 (m_axi) インターフェイスとして指定されたすべての関数引数は 1 つの AXI4 ポートにまとめられます。このオプションでは、同じbundle=<string> が指定されたすべてのインターフェイス ポートが 1 つの AXI インターフェイス ポートにまとめられ、RTL ポートの名前が <string> になります。
• register: 信号および関連プロトコル信号にレジスタを付けるオプションのキーワードで、信号が少なくとも関数実行の最終サイクルまで保持されます。このオプションは、次のインターフェイス モードに適用されます。○ ap_none
○ ap_ack
○ ap_vld
○ ap_ovld
○ ap_hs
○ ap_stable
○ axis
○ s_axilite
ヒント: config_interface コマンドの -register_io オプションは、最上位関数のすべての入力/出力にレジスタを付けるかどうかをグローバルに制御します。詳細は、『Vivado DesignSuite ユーザー ガイド: 高位合成』 (UG902) を参照してください。• register_mode= <forward|reverse|both|off>: register キーワードと共に使用し、forward パス (TDATA および TVALID)、reverse パス (TREADY)、またはその両方(both) のパス (TDATA、TVALID、および TREADY) にレジスタを配置するか、どのポート信号にもレジスタを配置しないか (off) を指定します。デフォルトの register_mode はboth です。AXI-Stream (axis) サイドチャネル信号はデータ信号と考慮され、TDATA にレジスタが付けられるとレジスタが付けられます。
• depth=<int>: テストベンチで処理されるサンプルの最大数を指定します。この設定は、Vivado HLS で RTL 協調シミュレーション用に作成される検証アダプターに必要な FIFO の最大サイズを示します。
ヒント: depth は通常はオプションですが、m_axi インターフェイスでは必須です。• offset=<string>: AXI4-Lite (s_axilite) および AXI4 (m_axi) インターフェイスのアドレス オフセットを指定します。○ s_axilite インターフェイスでは、<string> はレジスタ マップのアドレスを指定します。○ m_axi インターフェイスでは、<string> は次の値を指定します。
付録 B: HLS プラグマ
Vivado HLS 最適化手法ガイド 113UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
- direct: スカラー入力ポートを生成します。- slave: オフセット ポートを生成し、AXI4-Lite スレーブ インターフェイスに自動的にマップします。
- off: オフセット ポートは生成しません。ヒント: config_interface コマンドの -m_axi_offset オプションは、デザインのすべてのM_AXI インターフェイスのオフセット ポートをグローバルに制御します。• clock=<name>: s_axilite インターフェイス モードのみでオプションで指定します。インターフェイスで使用するクロック信号を定義します。デフォルトでは、AXI4-Lite インターフェイス クロックはシステム クロックと同じです。このオプションを使用すると、AXI4-Liteインターフェイス (s_axilite) に別のクロックを指定できます。
ヒント: bundle オプションを使用して複数の最上位関数引数を 1 つの AXI4-Lite インターフェイスにている場合は、clock オプションはバンドル メンバーの 1 つにのみ指定します。• num_read_outstanding=<int>: AXI4 (m_axi) インターフェイスで、デザインが停止するまでに、AXI4 バスに対して応答なしで読み込み要求を送信できる回数を指定します。これにより、デザインの内部ストレージである FIFO のサイズ(num_read_outstanding*max_read_burst_length*word_size) が決まります。
• num_write_outstanding=<int>: AXI4 (m_axi) インターフェイスで、デザインが停止するまでに、AXI4 バスに対して応答なしで書き込み要求を送信できる回数を指定します。これにより、デザインの内部ストレージである FIFO のサイズ(num_write_outstanding*max_write_burst_length*word_size) が決まります。
• max_read_burst_length=<int>: AXI4 (m_axi) インターフェイスでのバースト転送で読み出されるデータの最大数を指定します。
• max_write_burst_length=<int>: AXI4 (m_axi) インターフェイスでのバースト転送で書き込まれるデータの最大数を指定します。
• name=<string>: ポートの名前を変更します。生成された RTL ポートでこの名前が使用されます。
例 1
次の例では、両方の関数引数が AXI4-Stream インターフェイスを使用してインプリメントされます。void example(int A[50], int B[50]) { //Set the HLS native interface types #pragma HLS INTERFACE axis port=A #pragma HLS INTERFACE axis port=B int i; for(i = 0; i < 50; i++){ B[i] = A[i] + 5; }}
付録 B: HLS プラグマ
Vivado HLS 最適化手法ガイド 114UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
例 2
次の例では、ブロック レベル I/O プロトコルをオフにし、関数戻り値に割り当てています。#pragma HLS interface ap_ctrl_none port=return
関数引数 InData は ap_vld インターフェイスを使用し、入力にレジスタが付けられます。#pragma HLS interface ap_vld register port=InData
これにより、グローバル変数 lookup_table が RTL デザインでポートとして処理され、インターフェイスは ap_memory に指定されます。pragma HLS interface ap_memory port=lookup_table
例 3
次の例では、最上位 transpose 関数のポートに INTERFACE 規格を定義しています。bundle=オプションを使用して信号をグループ化しています。// TOP LEVEL - TRANSPOSEvoid transpose(int* input, int* output) { #pragma HLS INTERFACE m_axi port=input offset=slave bundle=gmem0 #pragma HLS INTERFACE m_axi port=output offset=slave bundle=gmem1
#pragma HLS INTERFACE s_axilite port=input bundle=control #pragma HLS INTERFACE s_axilite port=output bundle=control #pragma HLS INTERFACE s_axilite port=return bundle=control
#pragma HLS dataflow
関連項目• pragma HLS protocol
• 『Vivado Design Suite ユーザー ガイド: 高位合成』 (UG902)
付録 B: HLS プラグマ
Vivado HLS 最適化手法ガイド 115UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
pragma HLS latency説明関数、ループ、および領域の完了するまでの最小レイテンシまたは最大レイテンシ、あるいはその両方を指定します。レイテンシは、出力を生成するのに必要なクロック サイクル数として定義されます。関数レイテンシは、関数がすべての出力値を計算して戻るまでに必要なクロックサイクル数です。ループ レイテンシは、ループのすべての反復を実行するのにかかるサイクル数です。『Vivado Design Suite ユーザー ガイド: 高位合成』 (UG902) の「パフォーマンス メトリクスの例」を参照してください。Vivado HLS では常に、デザインのレイテンシを最短にするよう試みられます。LATENCY プラグマを指定すると、ツールで次のように処理されます。• レイテンシが最小値より大きく、最大値未満: 制約は満たされています。これ以上の最適化は実行されません。
• レイテンシが最小値未満: レイテンシが指定値まで拡張されます。リソースがさらに共有される可能性があります。
• レイテンシが最大値を超える: 指定された制約を満たすことができるようエフォート レベルが上げられます。それでも最大レイテンシを満たすことができない場合は、警告が表示され、達成可能な最短のレイテンシでデザインが作成されます。
ヒント: LATENCY プラグマを使用すると、ツールで最良のソリューションを探すエフォートを制限することもできます。コード内のループ、関数、または領域にレイテンシ制約を指定すると、そのスコープ内で可能なソリューションが削減され、ツールの実行時間が短縮されます。詳細は、『Vivado Design Suite ユーザー ガイド: 高位合成』 (UG902) の「ランタイムおよび容量の改善」を参照してください。構文レイテンシを制御する関数、ループ、または領域内に配置します。#pragma HLS latency min=<int> max=<int>
説明:
• min=<int>: 関数、ループ、またはコードの領域の最小レイテンシを指定します (オプション)。
• max=<int>: 関数、ループ、またはコードの領域の最大レイテンシを指定します (オプション)。
注記: min および max はオプションですが、いずれかを指定する必要があります。
付録 B: HLS プラグマ
Vivado HLS 最適化手法ガイド 116UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
例 1
次の例では、関数 foo の最小レイテンシを 4、最大レイテンシを 8 に指定しています。int foo(char x, char a, char b, char c) { #pragma HLS latency min=4 max=8 char y; y = x*a+b+c; return y}
例 2
次の例では、loop_1 の最大レイテンシを 12 に指定しています。プラグマはループ本体内に記述します。void foo (num_samples, ...) { int i; ... loop_1: for(i=0;i< num_samples;i++) { #pragma HLS latency max=12 ... result = a + b; }}
例 3
次の例では、コード領域を作成し、同じクロック サイクルで変化する必要のある信号を 0 レイテンシを指定してグループ化しています。// create a region { } with a latency = 0{ #pragma HLS LATENCY max=0 min=0 *data = 0xFF; *data_vld = 1;}
関連項目• 『Vivado Design Suite ユーザー ガイド: 高位合成』 (UG902)
pragma HLS loop_flatten説明入れ子のループを 1 つのループ階層にフラット化し、レイテンシを改善します。
付録 B: HLS プラグマ
Vivado HLS 最適化手法ガイド 117UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
RTL インプリメンテーションでは、外側のループから内側のループに、内側のループから外側のループに移動するのに 1 クロック サイクルかかります。入れ子のループをフラットにすると、それらを 1 つのループとして最適化できるので、クロック サイクル数を削減でき、ループ本文のロジックをさらに最適化することが可能です。ループ階層内の一番内側にあるループの本体に LOOP_FLATTEN プラグマを適用します。完全または半完全ループのみをこの方法でフラットにできます。• 完全ループの入れ子:
○ 最内ループのみにループ本体の内容が含まれます。○ ループ文の間に指定されるロジックはありません。○ すべてのループ範囲は定数です。
• 半完全ループの入れ子:
○ 最内ループのみにループ本体の内容が含まれます。○ ループ文の間に指定されるロジックはありません。○ 最外ループの範囲は変数にできます。
• 不完全ループの入れ子: 内側のループの範囲が変数であったり、ループ本体が内側のループにのみ含まれているとは限らない場合、コードの構造を変更するか、ループ本体内のループを展開して、完全ループの入れ子を作成してみてください。
構文C ソースの入れ子のループ内に配置します。#pragma HLS loop_flatten off
説明:
• off: オプションのキーワードで、フラットされないようにします。指定ロケーション内の一部のループはフラットにせず、それ以外のループをフラットにできます。
注記: LOOP_FLATTEN プラグマを使用すると、最適化がイネーブルになります。
付録 B: HLS プラグマ
Vivado HLS 最適化手法ガイド 118UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
例 1
関数 foo 内の loop_1 と、ループ階層でこれより上にあるすべてのループ (完全または半完全)を 1 つのループにフラット化します。プラグマを loop_1 の本体に記述します。void foo (num_samples, ...) { int i; ... loop_1: for(i=0;i< num_samples;i++) { #pragma HLS loop_flatten ... result = a + b; }}
例 2
loop_1 でループがフラット化されないようにします。loop_1: for(i=0;i< num_samples;i++) { #pragma HLS loop_flatten off ...
関連項目• pragma HLS loop_merge
• pragma HLS loop_tripcount
• pragma HLS unroll
• 『Vivado Design Suite ユーザー ガイド: 高位合成』 (UG902)
pragma HLS loop_merge説明連続するループを 1 つのループに結合して、全体的なレイテンシを削減し、共有を増やして最適化を向上します。ループを結合すると、次が可能になります。• RTL でループ本文のインプリメンテーション間の移行に必要なクロック サイクル数を削減できます。
• ループを並列にインプリメントできます (可能な場合)。
付録 B: HLS プラグマ
Vivado HLS 最適化手法ガイド 119UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
LOOP_MERGE プラグマでは、適用されたスコープ内のループすべてを結合するよう試みられます。たとえば、LOOP_MERGE プラグマをループの本体に適用した場合、Vivado HLS ではプラグマがそのループ自体ではなく、ループ内の下位ループに適用されます。ループ結合の規則は、次のとおりです。• ループの境界が変数の場合、同じ値 (反復回数) である必要があります。• ループの境界が定数の場合、最大定数値が結合されたループの境界として使用されます。• 境界が変数のループと定数のループを結合することはできません。• 結合するループ間のコードが、結合により悪影響を受けないようにします。コードを複数回実行しても常に同じ結果になるようにする必要があります。たとえば、a=b は許容されますが a=a+1 は許容されません。
• ループに FIFO 読み出しが含まれる場合は、ループは結合できません。結合により読み出しの順序が変更されてしまうためです。FIFO または FIFO インターフェイスからの読み出しは、常に順序どおりに実行される必要があります。
構文C ソースの必要なスコープまたは領域内に配置します。#pragma HLS loop_merge force
説明:
• force: Vivado HLS で警告が出力されてもループが結合されるようにするオプションのキーワードです。
重要: この場合、結合したループが問題なく動作するかどうかをユーザーが確認する必要があります。例次の例では、関数 foo の連続するすべてのループを 1 つのループに結合しています。void foo (num_samples, ...) { #pragma HLS loop_merge int i; ... loop_1: for(i=0;i< num_samples;i++) { ...
付録 B: HLS プラグマ
Vivado HLS 最適化手法ガイド 120UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
次の例では、loop_2 内の loop_2 を除くすべてのループを force オプションを使用して結合しています。プラグマを loop_2 の本体に記述します。loop_2: for(i=0;i< num_samples;i++) {#pragma HLS loop_merge force...
関連項目• pragma HLS loop_flatten
• pragma HLS loop_tripcount
• pragma HLS unroll
• 『Vivado Design Suite ユーザー ガイド: 高位合成』 (UG902)
pragma HLS loop_tripcount説明TRIPCOUNT プラグマをループに適用すると、ループで実行される反復回数の合計を手動で指定できます。重要: TRIPCOUNT プラグマは解析専用で、合成結果には影響しません。
Vivado HLS により、各ループの合計レイテンシ、つまりループのすべての反復を実行するためのクロック サイクル数がレポートされます。ループ レイテンシは、ループ反復数 (トリップカウント) に依存します。トリップカウントは、定数値であることもあり、ループ式 (x<y など) で使用される変数の値やループ内の制御文によって異なる場合もあります。Vivado HLS でトリップカウントを決定できないこともあり、その場合はレイテンシは不明になります。これは、トリップカウントの決定に使用される変数が次のいずれかの場合です。• 入力引数。• ダイナミック演算により算出される変数。ループのレイテンシが不明または算出できない場合、TRIPCOUNT プラグマを使用してループの反復回数の最小値および最大値を指定できます。これにより、ループのレイテンシがデザインの総レイテンシのどの程度を占めているのかがツールで解析されてレポートされるので、デザインに適切な最適化を判断するのに役立ちます。
付録 B: HLS プラグマ
Vivado HLS 最適化手法ガイド 121UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
構文C ソースのループ本体内に配置します。#pragma HLS loop_tripcount min=<int> max=<int> avg=<int>
説明:
• max=<int>: ループの反復回数の最大値を指定します。• min=<int>: ループの反復回数の最小値を指定します。• avg=<int>: ループの反復回数の平均値を指定します。例次の例では、関数 foo の loop_1 の最小トリップカウントを 12、最大トリップカウントを 16に指定しています。void foo (num_samples, ...) { int i; ... loop_1: for(i=0;i< num_samples;i++) { #pragma HLS loop_tripcount min=12 max=16 ... result = a + b; }}
関連項目• 『Vivado Design Suite ユーザー ガイド: 高位合成』 (UG902)
pragma HLS occurrence説明関数またはループをパイプライン処理するとき、OCCURRENCE プラグマを使用して、ある領域のコードがそれを含む関数またはループのコードよりも低頻度で実行されるように指定します。これにより、実行速度が遅いコード部分が低頻度でパイプライン処理されるようにでき、最上位パイプライン内で共有できる可能性があります。OCCURRENCE (実行頻度) は次を基に判断します。• ループは N 回反復する。• ループ本体の一部が条件文で有効になっており、M 回しか実行されない (N は M の整数倍)。
付録 B: HLS プラグマ
Vivado HLS 最適化手法ガイド 122UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
• 条件コードの実行頻度は、ループ本体の残りの部分の N/M 倍。たとえば、10 回実行されるループで、ループ内の条件文が 2 回だけ実行される場合、実行頻度は 5 (10/2) となります。領域に OCCURRENCE プラグマを使用すると、その領域内の関数およびループがそれを含む関数またはループよりも遅い開始間隔 (II) でパイプライン処理されます。構文C ソースのコードの領域内に配置します。#pragma HLS occurrence cycle=<int>
説明:
• cycle=<int>: N/M の実行頻度を指定します。○ N は領域を含む関数/ループの実行回数です。○ M は条件領域の実行回数です。
重要: N は M の整数倍である必要があります。
例次の例では、領域 Cond_Region の実行頻度を 4 に指定しています。この領域は、それを含むコードの 1/4 の速度で実行されます。Cond_Region: {#pragma HLS occurrence cycle=4...}
関連項目• pragma HLS pipeline
• 『Vivado Design Suite ユーザー ガイド: 高位合成』 (UG902)
付録 B: HLS プラグマ
Vivado HLS 最適化手法ガイド 123UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
pragma HLS pipeline説明PIPELINE プラグマは、演算を同時実行できるようにすることにより関数またはループの開始間隔 (II) を短縮します。パイプライン処理された関数またはループは、N クロック サイクル (N は関数またはループの開始間隔) ごとに新しい入力を処理できます。PIPELINE プラグマのデフォルトの開始間隔は 1で、クロック サイクルごとに 1 つの入力を処理します。開始間隔は、プラグマの II オプションを使用して指定できます。ループをパイプライン処理すると、次の図に示すように、ループ内の演算が同時実行されるようインプリメントできます。この図の (A) はデフォルトの順次演算を示しています。各入力は 3クロック サイクルごとに処理され (II=3)、最後の出力が書き出されるまでに 8 クロック サイクルかかっています。
図 4: ループのパイプラインvoid func(m,n,o) { for (i=2;i>=0;i--) { op_Read; op_Compute; op_Write;
}}
4 cycles
RD
3 cycles
8 cycles
1 cycleRD CMP WR
RD CMP WR
RD CMP WR
(A) Without Loop Pipelining (B) With Loop PipeliningX14277-110217
CMP WR RD CMP WR RD CMP WR
重要: ループのパイプラインは、ループ運搬依存を使用することにより回避できます。DEPENDENCE プラグマを使用して、ループ運搬依存を克服し、ループをパイプライン処理できるようにする (またはより短い間隔でパイプラインできるようにする) ための追加情報を提供します。Vivado HLS で指定した開始間隔でデザインを作成できない場合は、次が実行されます。• 警告メッセージが表示されます。
付録 B: HLS プラグマ
Vivado HLS 最適化手法ガイド 124UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
• 達成可能な最短の開始間隔でデザインが作成されます。警告メッセージを参考にデザインを解析し、必要な開始間隔を満たしてデザインを作成するためにどの手順が必要なのかを判断します。構文C ソースの関数の本体またはループ内に配置します。#pragma HLS pipeline II=<int> enable_flush rewind
説明:
• II=<int>: パイプラインの開始間隔を指定します。Vivado HLS では、この指定を満たすことが試みられます。データの依存性によって、実際の開始間隔はこの指定より大きくなることがあります。デフォルトは 1 です。
• enable_flush: オプションのキーワードで、パイプラインの入力で有効であったデータが非アクティブになった場合にデータをフラッシュして空にするパイプラインをインプリメントします。
ヒント: この機能は、パイプライン処理された関数でのみサポートされ、パイプライン処理されたループではサポートされません。• rewind: オプションのキーワードで、1 つのループ反復の終了と次の反復の開始の間に一時停止のない連続ループ パイプライン処理 (巻き戻し) をイネーブルにします。巻き戻しは、最上位関数内に 1 つのループしかない (完全なループ ネスト) 場合にのみ効果的です。ループ前のコード部分は、次のようになります。○ 初期化と認識されます。○ パイプラインで一度だけ実行されます。○ 条件演算 (if-else) を含むことはできません。
ヒント: この機能は、パイプライン処理されたループでのみサポートされ、パイプライン処理された関数ではサポートされません。例 1
この例では、関数 foo が開始間隔 1 でパイプライン処理されます。void foo { a, b, c, d} { #pragma HLS pipeline II=1 ...}
注記: II のデフォルト値は 1 なので、II=1 を指定する必要はありません。
付録 B: HLS プラグマ
Vivado HLS 最適化手法ガイド 125UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
関連項目• pragma HLS dependence
• 『Vivado Design Suite ユーザー ガイド: 高位合成』 (UG902)
• xcl_pipeline_loop
• 『SDAccel 環境最適化ガイド』 (UG1207)
pragma HLS protocol説明PROTOCOL プラグマは、コードの領域をプロトコル領域として指定します。この領域には、コードで明確に指定していない限り、Vivado HLS によりクロック動作は挿入されません。プロトコル領域は、最終的なデザインを特定の I/O プロトコルを使用するハードウェア ブロックに接続できるようにするため、インターフェイス プロトコルを手動で指定するために使用できます。注記: 詳細は、『Vivado Design Suite ユーザー ガイド: 高位合成』 (UG902) の「インターフェイスの手動指定」を参照してください。Vivado HLS では、関数引数からの読み出しおよび関数引数への書き込みも含め、操作の間にクロックは挿入されません。このため、読み出しおよび書き込みの順序は RTL で保持されます。クロック動作は、次のように指定できます。• C の場合: ap_wait() 文 (ap_utils.h を含む) を使用します。• C++ および SystemC の場合: wait() 文 (systemc.h を含む) を使用します。ap_wait および wait 文は、C および C++ デザインのシミュレーションには影響せず、VivadoHLS でのみ解釈されます。C コードの領域を作成するには、次の手順に従います。1. 領域を波かっこ ({ }) で囲みます。2. オプションで識別子を指定します。次の例では、io_section という領域を定義しています。io_section:{...}
付録 B: HLS プラグマ
Vivado HLS 最適化手法ガイド 126UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
構文プロトコルを定義する領域内に配置します。#pragma HLS protocol <floating | fixed>
説明:
• floating: プロトコル領域外の文を最終的な RTL のプロトコル領域内の文と重複できるようにします。プロトコル領域内のコードはサイクル精度ですが、その他の操作は同時に実行できます。これがデフォルトのプロトコル モードです。
• fixed: プロトコル領域内の文とプロトコル領域外の文が重複しないようにします。重要: プロトコル モードを使用しない場合は、floating に設定されます。
例 1
この例では、領域 io_section を fixed プロトコル領域として定義しています。プラグマは領域内に配置します。io_section:{ #pragma HLS protocol fixed ...}
関連項目• pragma HLS array_map
• pragma HLS array_reshape
• 『Vivado Design Suite ユーザー ガイド: 高位合成』 (UG902)
• xcl_array_partition
• 『SDAccel 環境最適化ガイド』 (UG1207)
pragma HLS reset説明特定のステート変数 (グローバルまたはスタティック) のリセットを追加または削除します。
付録 B: HLS プラグマ
Vivado HLS 最適化手法ガイド 127UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
リセット ポートは、リセット信号が適用されたときにリセット ポートに接続されているレジスタおよびブロック RAM を初期値に戻すために FPGA で使用されます。RTL リセット ポートの存在と動作は、config_rtl コンフィギュレーション ファイルで制御されます。リセット設定ではリセットの極性および同期か非同期かを設定できますが、重要なのは、[reset] オプションを使用してリセット信号を適用したときにリセットするレジスタを指定できるということです。詳細は、『Vivado Design Suite ユーザー ガイド: 高位合成』 (UG902) の「クロック、リセット、および RTL 出力」を参照してください。RESET プラグマを使用すると、リセットを詳細に制御できます。変数がスタティックまたはグローバルの場合、RESET プラグマを使用してリセットを明示的に追加したり、off に設定して変数からリセットを削除したりできます。これはスタティック配列またはグローバル配列がデザインに含まれる場合に特に便利なことがあります。構文C ソース コードの変数のライフ サイクルの境界内に配置します。#pragma HLS reset variable=<a> off
説明:
• variable=<a>: プラグマを適用する変数を指定します。• off: 指定した変数にリセットを生成しないようにします。例 1
次の例では、グローバル リセット設定が none または control の場合でも、関数 foo の変数a にリセットが追加されます。void foo(int in[3], char a, char b, char c, int out[3]) {#pragma HLS reset variable=a
例 2
次の例では、グローバル リセット設定が state または all の場合でも、関数 foo の変数 a からリセットが削除されます。void foo(int in[3], char a, char b, char c, int out[3]) {#pragma HLS reset variable=a off
関連項目• 『Vivado Design Suite ユーザー ガイド: 高位合成』 (UG902)
付録 B: HLS プラグマ
Vivado HLS 最適化手法ガイド 128UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
pragma HLS resource説明変数 (配列、算術演算、関数引数) を RTL にインプリメントするのに使用するライブラリ リソース (コア) を指定します。RESOURCE プラグマを指定しない場合、Vivado HLS により使用するリソースが自動的に判断されます。Vivado HLS では、ハードウェア コアを使用して演算がインプリメントされます。演算をインプリメントできるコアがライブラリに複数ある場合、RESOURCE プラグマを使用して使用するコアを指定できます。使用可能なコアのリストを生成するには、list_core コマンドを使用します。ヒント: list_core コマンドを使用すると、ライブラリで使用可能なコアの詳細を表示できます。list_core は Vivado HLS の Tcl コマンド インターフェイスでのみ使用でき、set_partコマンドを使用してザイリンクス デバイスを指定する必要があります。デバイスを指定しない場合、list_coreコマンドは実行されません。たとえば、配列をインプリメントするのに使用するライブラリのメモリ エレメントをRESOURCE プラグマを使用して指定し、配列をシングル ポート RAM とデュアル ポート RAM のどちらとしてインプリメントするかを制御できます。配列に関連付けられているメモリ タイプによって RTL で必要なポートが決まるので、これは最上位関数インターフェイスの配列には重要な方法です。latency= オプションを使用すると、コアのレイテンシを指定できます。インターフェイスのブロック RAM の場合、latency= オプションを指定すると、たとえばレイテンシ 2 または 3 のSRAM をサポートするなど、インターフェイスにオフチップの標準でない SRAM を記述できます。詳細は、『Vivado Design Suite ユーザー ガイド: 高位合成』 (UG902) の「インターフェイスの配列」を参照してください。内部演算の場合、latency= オプションを使用すると、演算をより多くのパイプライン段を使用してインプリメントできます。これらの追加のパイプライン段により、RTL 合成中にタイミング問題を解決しやすくなります。重要: latency= オプションを使用するには、使用可能な複数段のコアを演算に含める必要があります。Vivado HLS には、基本的な算術演算 (加算、減算、乗算、除算)、すべての浮動小数点演算、およびすべてのブロック RAM 用に複数段コアが含まれています。より良い結果を得るため、C の場合は -std=c99 を、C および C++ の場合は -fno-builtinを使用することをお勧めします。-std=c99 などの C コンパイル オプションを指定するには、Tcl コマンドの add_files に -cflags オプションを使用します。または、[Project Settings] ダイアログ ボックスの [Edit CFLAGs] ボタンを使用します。詳細は、『Vivado Design Suite ユーザー ガイド: 高位合成』 (UG902) の「新規合成プロジェクトの作成」を参照してください。
付録 B: HLS プラグマ
Vivado HLS 最適化手法ガイド 129UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
構文C ソースの配列変数が定義されている関数の本体内に配置します。#pragma HLS resource variable=<variable> core=<core>\latency=<int>
説明:
• variable=<variable>: RESOURCE プラグマを設定する配列、算術演算、または関数引数を指定します (必須)。
• core=<core>: テクノロジ ライブラリで定義されているようにコアの名前を指定します。• latency=<int>: コアのレイテンシを指定します。例 1
次の例では、関数 foo の変数 c の乗算をインプリメントするのに 2 段のパイプライン乗算器を指定しています。変数 d に使用するコアは、Vivado HLS により決定されます。int foo (int a, int b) {int c, d;#pragma HLS RESOURCE variable=c latency=2c = a*b;d = a*c;return d;}
例 2
次の例では、変数 coeffs[128] が最上位関数の foo_top に対する引数で、coeffs がライブラリの RAM_1P コアを使用してインプリメントされるように指定しています。#pragma HLS resource variable=coeffs core=RAM_1P
ヒント: coeffs の値にアクセスするために RTL で作成されるポートは、RAM_1P コアで定義されます。関連項目• 『Vivado Design Suite ユーザー ガイド: 高位合成』 (UG902)
付録 B: HLS プラグマ
Vivado HLS 最適化手法ガイド 130UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
pragma HLS stream説明デフォルトでは、配列変数は RAM としてインプリメントされます。• 最上位関数の配列パラメーターは、RAM インターフェイスのポートとしてインプリメントされます。
• 一般配列は、読み出しおよび書き込みアクセス用に RAM としてインプリメントされます。• DATAFLOW 最適化に関連するサブ関数では、配列引数は RAM のピンポン バッファー チャネルを使用してインプリメントされます。
• ループ ベースの DATAFLOW 最適化に関連する配列は、RAM のピンポン バッファー チャネルを使用してインプリメントされます。
配列に格納されているデータが順次に消費または生成される場合は、STREAM プラグマを指定して RAM ではなく FIFO を使用し、ストリーミング データを使用する方が効率的です。重要: 最上位関数の引数の INTERFACE タイプが ap_fifo に指定されている場合は、配列は自動的にストリーミングとしてインプリメントされます。構文C ソースの必要なロケーションの境界内に配置します。#pragma HLS stream variable=<variable> depth=<int> dim=<int> off
説明:
• variable=<variable>: ストリーミング インターフェイスとしてインプリメントする配列の名前を指定します。
• depth=<int>: DATAFLOW チャネルの配列ストリーミングにのみ適用されます。RTL にインプリメントされる FIFO の深さは、デフォルトでは C コードで指定した配列と同じサイズになります。このオプションを使用すると、FIFO に異なる深さを指定できます。配列が DATAFLOW 領域にインプリメントされる場合は、depth= オプションで FIFO のサイズを削減する方法がよく使用されます。たとえば、DATAFLOW 領域ですべてのループおよび関数がデータを II=2 のレートで処理する場合、データはクロック サイクルごとに生成および消費されるので、大型 FIFO は必要ありません。この場合、depth= オプションを使用してFIFO サイズを 1 に削減すると、RTL デザインのエリアを大幅に削減できます。
付録 B: HLS プラグマ
Vivado HLS 最適化手法ガイド 131UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
ヒント: config_dataflow -depth コマンドを使用すると、DATAFLOW 領域のすべての配列をストリーミングできます。depth= オプションを指定すると、その variable に対してconfig_dataflow コマンドを無効にできます。• dim=<int>: ストリーミングする配列の次元を指定します。デフォルトは次元 1 です。N 次元の配列の場合、0 ~ N の整数を指定します。
• off: ストリーミング データをディスエーブルにします。DATAFLOW チャネルの配列ストリーミングにのみ適用されます。
ヒント: config_dataflow -default_channel fifo コマンドを使用すると、STREAM をデザインのすべての配列に適用するのと同じになります。off オプションを指定すると、そのvariable に対して config_dataflow コマンドを無効し、RAM ピンポン バッファー ベースのチャネルを使用したデフォルトでインプリメントできます。例 1
次の例では、配列 A[10] をストリーミングにし、FIFO としてインプリメントするよう指定しています。#pragma HLS STREAM variable=A
例 2
次の例では、配列 B が深さ 12 の FIFO でストリーミングされるように設定しています。#pragma HLS STREAM variable=B depth=12
例 3
次の例では、配列 C のストリーミングをディスエーブルにしています。ストリーミングはconfig_dataflow によりイネーブルになっていると想定しています。#pragma HLS STREAM variable=C off
関連項目• 『Vivado Design Suite ユーザー ガイド: 高位合成』 (UG902)
付録 B: HLS プラグマ
Vivado HLS 最適化手法ガイド 132UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
pragma HLS top説明関数に名前を付けます。この名前を set_top コマンドで使用し、名前を付けた関数および指定した最上位から呼び出される関数を合成できます。これは通常 C/C++ のクラスのメンバー関数を合成するために使用されます。このプラグマはアクティブ ソリューションで指定し、その新しい名前を set_top コマンドで使用します。構文C ソースの必要なロケーションの境界内に配置します。#pragma HLS top name=<string>
説明:
• name=<string>: set_top コマンドで使用する名前を指定します。例次の例では、関数 foo_long_name を最上位関数に指定し、名前を DESIGN_TOP に変更しています。コードにプラグマを含めた後、Tcl コマンド ラインから、または GUI プロジェクト設定で指定した最上位で set_top コマンドを実行する必要があります。void foo_long_name () { #pragma HLS top name=DESIGN_TOP ...}
set_top DESIGN_TOP
関連項目• 『Vivado Design Suite ユーザー ガイド: 高位合成』 (UG902)
付録 B: HLS プラグマ
Vivado HLS 最適化手法ガイド 133UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
pragma HLS unroll説明ループを展開し、複数の演算を 1 つにまとめたものではなく、複数の個別の演算を作成します。UNROLL プラグマを使用すると、RTL デザインにループ本体のコピーを複数作成することにより、一部またはすべてのループ反復を並列実行できるようになります。C/C++ 関数のループは、デフォルトでは展開されません。ループが展開されていない場合、合成ではそのループの 1 反復に対してロジックが作成され、RTL デザインでこのロジックがループの反復ごとに順に実行されます。ループは、ループ帰納変数で指定されている反復回数実行されます。反復の回数は、break 条件やループ exit 変数の変更など、ループ本体内のロジックにも影響されます。UNROLL プラグマを使用すると、データのアクセスおよびスループットを向上するためにループを展開できます。UNROLL プラグマでは、ループを完全にまたは部分的に展開できます。ループを完全に展開すると、RTL に各ループ反復対してループ本体のコピーが作成され、ループ全体を同時に実行できるようになります。ループの部分展開では、係数 N を指定してループのコピーを N 個作成し、ループ反復数を削減します。ループを完全に展開するには、ループの境界がコンパイル時に認識される必要があります。これは部分展開には必要ありません。ループを部分展開する場合、N は最大反復回数の整数因数である必要はありません。Vivado HLSでは、部分展開されたループが元のループと同じように動作することを確認する終了チェックが追加されます。たとえば、次のようなコードがあるとします。for(int i = 0; i < X; i++) { pragma HLS unroll factor=2 a[i] = b[i] + c[i];}
ループを係数 2 で展開すると、コードが次のように変換されます。このコードでは、break コンストラクトにより機能が同じになり、ループが適切なポイントで終了します。for(int i = 0; i < X; i += 2) { a[i] = b[i] + c[i]; if (i+1 >= X) break; a[i+1] = b[i+1] + c[i+1];}
最大反復回数 X は変数であり、Vivado HLS でその値を判断できないので、部分展開されるループに終了チェックと制御ロジックが追加されます。ただし、展開係数 (この例では 2) が最大反復回数 X の整数因数であるとわかっている場合は、skip_exit_check オプションを使用して終了チェックとその関連ロジックを削除できます。これによりエリアが最小限に抑えられ、制御ロジックが単純になります。
付録 B: HLS プラグマ
Vivado HLS 最適化手法ガイド 134UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
ヒント: DATA_PACK、ARRAY_PARTITION、ARRAY_RESHAPE などのプラグマの使用により 1クロック サイクルでより多くのデータにアクセスできるようになっている場合、このデータを消費するループを展開することによりスループットが向上するのであれば、Vivado HLS でこれらのループが自動的に展開されます。ループを完全または部分展開すると、1 クロック サイクルでこれらの追加データを消費するのに十分なハードウェアを作成できます。この機能は、config_unroll コマンドを使用して制御します。詳細は、『Vivado Design Suite ユーザー ガイド: 高位合成』 (UG902) の「config_unroll」を参照してください。
構文C/C++ ソースの展開するループの本体内に配置します。#pragma HLS unroll factor=<N> region skip_exit_check
説明:
• factor=<N>: 0 以外の整数値を指定して、部分展開が実行されるようにします。ループの本体が指定した回数繰り返され、反復情報がそれに合わせて調整されます。factor= を指定しない場合、ループは完全に展開されます。
• region: オプションのキーワードで、指定したループ自体は展開せずに、そのループの本体(領域) 内に含まれるすべてのループを展開します。
• skip_exit_check: factor= を使用して部分展開を指定している場合にのみ適用されるオプションのキーワードです。ループの反復回数が既知であるかどうかによって、終了チェックが削除されます。○ 固定 (既知の) 範囲: 反復回数が係数の倍数である場合、終了条件チェックは実行されません。反復回数が係数の整数倍でない場合は、次のようになります。1. 展開は実行されません。2. 処理を続行するには終了チェックを実行する必要があることを示す警告メッセージが表示されます。
○ 変数 (不明の) 範囲: 指定したとおりに終了条件チェックが削除されます。次を確認してください。1. 可変境界が指定した展開係数の整数倍数である。2. 終了チェックが不要である。
付録 B: HLS プラグマ
Vivado HLS 最適化手法ガイド 135UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
例 1
次の例では、関数 foo 内の loop_1 を完全に展開しています。プラグマを loop_1 の本体に記述します。loop_1: for(int i = 0; i < N; i++) { #pragma HLS unroll a[i] = b[i] + c[i];}
例 2
次の例では、関数 foo の loop_2 を係数 4 で部分展開し、終了チェックを削除しています。void foo (...) { int8 array1[M]; int12 array2[N]; ... loop_2: for(i=0;i<M;i++) { #pragma HLS unroll skip_exit_check factor=4 array1[i] = ...; array2[i] = ...; ... } ...}
例 3
次の例では、関数 foo の loop_1 内に含まれるすべてのループを完全に展開しますが、regionキーワードを使用して loop_1 自体は展開しないようにしています。void foo(int data_in[N], int scale, int data_out1[N], int data_out2[N]) { int temp1[N]; loop_1: for(int i = 0; i < N; i++) { #pragma HLS unroll region temp1[i] = data_in[i] * scale; loop_2: for(int j = 0; j < N; j++) { data_out1[j] = temp1[j] * 123; } loop_3: for(int k = 0; k < N; k++) { data_out2[k] = temp1[k] * 456; } }}
関連項目• pragma HLS loop_flatten
• pragma HLS loop_merge
• pragma HLS loop_tripcount
• 『Vivado Design Suite ユーザー ガイド: 高位合成』 (UG902)
付録 B: HLS プラグマ
Vivado HLS 最適化手法ガイド 136UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
• opencl_unroll_hint
• 『SDAccel 環境最適化ガイド』 (UG1207)
付録 B: HLS プラグマ
Vivado HLS 最適化手法ガイド 137UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
付録 C
その他のリソースおよび法的通知ザイリンクス リソースアンサー、資料、ダウンロード、フォーラムなどのサポート リソースは、ザイリンクス サポート サイトを参照してください。ソリューション センターデバイス、ツール、IP のサポートについては、ザイリンクス ソリューション センターを参照してください。デザイン アシスタント、デザイン アドバイザリ、トラブルシューティングのヒントなどが含まれます。
参考資料このガイドの補足情報は、次の資料を参照してください。日本語版のバージョンは、英語版より古い場合があります。1. 『SDx 環境リリース ノート、インストールおよびライセンス ガイド』 (UG1238)
2. 『SDSoC 環境ユーザー ガイド』 (UG1027)
3. 『SDSoC 環境プロファイリングおよび最適化ガイド』 (UG1235)
4. 『SDSoC 環境チュートリアル: 入門』 (UG1028)
5. 『SDSoC 環境プラットフォーム開発ガイド』 (UG1146)
6. SDSoC 開発環境ウェブ ページ7. 『UltraFast エンベデッド デザイン設計手法ガイド』 (UG1046: 英語版、日本語版)
8. 『Zynq-7000 All Programmable SoC ソフトウェア開発者向けガイド』 (UG821: 英語版、日本語版)
9. 『Zynq UltraScale+ MPSoC ソフトウェア開発者向けガイド』 (UG1137: 英語版、日本語版)
10.『ZC702 評価ボード (Zynq-7000 XC7Z020 All Programmable SoC 用) ユーザー ガイド』(UG850)
付録 C: その他のリソースおよび法的通知
Vivado HLS 最適化手法ガイド 138UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
11.『ZCU102 評価ボード ユーザー ガイド』 (UG1182)
12.『PetaLinux ツール資料: ワークフロー チュートリアル』 (UG1156)
13.『Vivado Design Suite ユーザー ガイド: 高位合成』 (UG902)
14.『Vivado Design Suite ユーザー ガイド: カスタム IP の作成とパッケージ』 (UG1118)
15. Vivado® Design Suite の資料
お読みください: 重要な法的通知本通知に基づいて貴殿または貴社 (本通知の被通知者が個人の場合には「貴殿」、法人その他の団体の場合には「貴社」。以下同じ) に開示される情報 (以下「本情報」といいます) は、ザイリンクスの製品を選択および使用することのためにのみ提供されます。適用される法律が許容する最大限の範囲で、(1) 本情報は「現状有姿」、およびすべて受領者の責任で (with all faults) という状態で提供され、ザイリンクスは、本通知をもって、明示、黙示、法定を問わず (商品性、非侵害、特定目的適合性の保証を含みますがこれらに限られません)、すべての保証および条件を負わない (否認する) ものとします。また、(2) ザイリンクスは、本情報 (貴殿または貴社による本情報の使用を含む) に関係し、起因し、関連する、いかなる種類・性質の損失または損害についても、責任を負わない (契約上、不法行為上 (過失の場合を含む)、その他のいかなる責任の法理によるかを問わない) ものとし、当該損失または損害には、直接、間接、特別、付随的、結果的な損失または損害 (第三者が起こした行為の結果被った、データ、利益、業務上の信用の損失、その他あらゆる種類の損失や損害を含みます) が含まれるものとし、それは、たとえ当該損害や損失が合理的に予見可能であったり、ザイリンクスがそれらの可能性について助言を受けていた場合であったとしても同様です。ザイリンクスは、本情報に含まれるいかなる誤りも訂正する義務を負わず、本情報または製品仕様のアップデートを貴殿または貴社に知らせる義務も負いません。事前の書面による同意のない限り、貴殿または貴社は本情報を再生産、変更、頒布、または公に展示してはなりません。一定の製品は、ザイリンクスの限定的保証の諸条件に従うこととなるので、https://japan.xilinx.com/legal.htm#tos で見られるザイリンクスの販売条件を参照してください。IP コアは、ザイリンクスが貴殿または貴社に付与したライセンスに含まれる保証と補助的条件に従うことになります。ザイリンクスの製品は、フェイルセーフとして、または、フェイルセーフの動作を要求するアプリケーションに使用するために、設計されたり意図されたりしていません。そのような重大なアプリケーションにザイリンクスの製品を使用する場合のリスクと責任は、貴殿または貴社が単独で負うものです。https://japan.xilinx.com/legal.htm#tos で見られるザイリンクスの販売条件を参照してください。
付録 C: その他のリソースおよび法的通知
Vivado HLS 最適化手法ガイド 139UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
自動車用のアプリケーションの免責条項オートモーティブ製品 (製品番号に「XA」が含まれる) は、ISO 26262 自動車用機能安全規格に従った安全コンセプトまたは余剰性の機能 (「セーフティ設計」) がない限り、エアバッグの展開における使用または車両の制御に影響するアプリケーション (「セーフティ アプリケーション」) における使用は保証されていません。顧客は、製品を組み込むすべてのシステムについて、その使用前または提供前に安全を目的として十分なテストを行うものとします。セーフティ設計なしにセーフティ アプリケーションで製品を使用するリスクはすべて顧客が負い、製品の責任の制限を規定する適用法令および規則にのみ従うものとします。Copyright
© Copyright 2018 Xilinx, Inc. Xilinx、Xilinx のロゴ、Artix、ISE、Kintex、Spartan、Virtex、Vivado、Zynq、およびこの文書に含まれるその他の指定されたブランドは、米国およびその他各国のザイリンクス社の商標です。OpenCL および OpenCL のロゴは Apple Inc. の商標であり、Khronosによる許可を受けて使用されています。すべてのその他の商標は、それぞれの保有者に帰属します。
この資料に関するフィードバックおよびリンクなどの問題につきましては、[email protected] まで、または各ページの右下にある [フィードバック送信] ボタンをクリックすると表示されるフォームからお知らせください。フィードバックは日本語で入力可能です。いただきましたご意見を参考に早急に対応させていただきます。なお、このメールアドレスへのお問い合わせは受け付けておりません。あらかじめご了承ください。
付録 C: その他のリソースおよび法的通知
Vivado HLS 最適化手法ガイド 140UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
付録 C: その他のリソースおよび法的通知
Vivado HLS 最適化手法ガイド 141UG1270 (v2018.1) 2018 年 04 月 04 日 japan.xilinx.com [placeholder text]
Top Related