HLS プラグマ
Vivado HLS での最適化
SDAccel™ および SDSoC™ 開発環境では、ハードウェア カーネルを OpenCL™、C、または C++ 言語からレジスタ トランスファー レベル (RTL) に合成し、ザイリンクス デバイスのプログラマブル ロジックにインプリメントできるようにする必要があります。Vivado® 高位合成 (HLS) ツールは、OpenCL、C、および C++ 言語記述から RTL を合成します。
HLS ツールはユーザーの操作なしで SDAccel または SDSoC 開発環境プロジェクトを処理できるよう設計されていますが、HLS ツールでは、生成される RTL コードのレイテンシの削減、スループット パフォーマンスの向上、エリアおよびデバイス リソース使用率の削減など、デザインを最適化するためのプラグマが提供されています。これらのプラグマは、カーネルのソース コードに直接追加できます。
HLS プラグマには、次の最適化タイプがあります。
タイプ | 属性 |
---|---|
カーネル最適化 | |
関数のインライン展開 | |
インターフェイス合成 | |
タスク レベルのパイプライン処理 | |
パイプライン処理 | |
ループ展開 | |
ループ最適化 | |
配列最適化 | |
構造体のパック |
pragma HLS allocation
説明
インプリメントされたカーネルでのリソースの割り当てを制限するインスタンス制限を指定します。特定の関数、ループ、演算、またはコアをインプリメントするのに使用されるレジスタ トランスファー レベル (RTL) インスタンスおよびハードウェア リソースの数を定義して制限できます。ALLOCATION プラグマは関数の本体、ループ、またはコード領域内に指定します。
たとえば、C ソース コードに foo_sub
という関数のインスタンスが 4 つある場合、ALLOCATION プラグマを使用して最終 RTL で foo_sub
のインスタンスを 1 つだけにできます。C 関数の 4 つのインスタンスすべてが、同じ RTL ブロックを使用してインプリメントされます。これにより関数で使用されるリソースは削減されますが、パフォーマンスが低下することがあります。
加算、乗算、配列読み出し、書き込みなどの C コードでの演算は、ALLOCATION プラグマを使用して制限できます。合成中に演算子がマップされるコアは、それらの演算子と同じ方法で制限できます。乗算演算子の総数を制限する代わりに、組み合わせ乗算器コアの数を制限して、残りの乗算がパイプライン乗算器を使用して実行されるようにすることもできます (逆も可能)。
ALLOCATION プラグマは、指定された関数、ループ、またはコード領域内に適用されますが、config_bind
コマンドの -min_op
引数を使用して、デザイン全体で演算子の数を最小限に抑えることもできます。
構文
このプラグラマは、適用する関数の本体、ループ、または領域内に配置します。
#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=
オプションで指定します。コアを指定する場合、ツールで使用される必要のあるコアを指定するか、または指定したコアの制限を定義できます。
例 1
デザインに関数 foo
のインスタンスが複数含まれているとします。この例では、ハードウェア カーネルの RTL における foo
のインスタンス数を 2 に制限しています。
#pragma HLS allocation instances=foo limit=2 function
例 2
次の例では、関数 my_func
のインプリメンテーションに使用される乗算演算の数を 1 に制限しています。この制限は、my_func
外にある乗算器、または my_func
のサブ関数に含まれる乗算器には適用されません。
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)
pragma HLS array_map
説明
複数の小型の配列を 1 つの大型の配列にまとめ、ブロック RAM リソースを削減します。
通常は、pragma HLS
array_map
コマンド (同じ instance=
ターゲット) を使用して、複数の小型の配列を 1 つの大型の配列に組み合わせします。この大型の配列は、1 つの大型メモリ (RAM または FIFO) リソースに配置できます。
各配列は、ブロック RAM または UltraRAM (デバイスでサポートされる場合) にマップされます。FPGA で提供される基本ブロック RAM の単位は 18K です。小型の配列の多くで 18K がフルに使用されない場合、小型の配列を 1 つの大型の配列にマップすることでブロック RAM リソースを効率的に使用できます。
- 水平マップ: 元の配列を連結して新しい配列を作成します。物理的には、要素数の多い 1 つの配列にインプリメントされます。
- 垂直マップ: 元の配列のワードを連結して、新しい配列を作成します。物理的には、ビット幅の広い 1 つの配列にインプリメントされます。
配列はプラグマが指定された順に、次のものから連結されます。
- 水平マップの場合はターゲット要素 0。
- 垂直マップの場合はビット 0。
構文
C ソースの配列変数が定義されている関数内に配置します。
#pragma HLS array_map variable=<name> instance=<instance> \
<mode> offset=<int>
説明:
variable=<name>
: 必須の引数で、新しいターゲット配列 <instance
> にマップする配列変数を指定します。instance=<
: 配列の結合先である新しい配列の名前を指定します。instance
>- <
mode
>: 配列マップをhorizontal
またはvertical
に指定します (オプション)。- デフォルトの <
mode
> は水平マップ (holizontal) で、配列は要素の多い新しい配列に連結されます。元の N 配列のリマップには、1 ポートのブロック RAM で N サイクルまたは 2 ポートのブロック RAM でシーリング (N/2) サイクル必要です。 - 垂直マップ (vertical) では、ワード数が多い新しい配列に連結されます。元の N 配列のリマップは、上記の水平マップに似ていますが、同じインデックスが使用された場合は 1 サイクルのみ必要です。
- デフォルトの <
offset=<int>
: 水平マップにのみ適用されます。配列を新しい配列 <instance
> にマップする前に適用するオフセット値を整数で指定します。次に例を示します。- 配列変数の要素 0 は、新しいターゲットの要素 <
int
> にマップされます。 - ほかの要素は新しいターゲットの <
int+1
>、<int+2
> などにマップされます。
重要: オフセット値が指定されていない場合は、配列要素の重複を避けるため、Vivado 高位合成 (HLS) tu-ru で必要なオフセットが自動的に計算されます。- 配列変数の要素 0 は、新しいターゲットの要素 <
例 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
例 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
です。次のタイプがサポートされます。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]) が含まれます。
例 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 環境プロファイリングおよび最適化ガイド』
pragma HLS array_reshape
説明
配列分割と垂直配列マップを組み合わせます。
ARRAY_RESHAPE プラグマは、配列を複数の小型の配列に分割する ARRAY_PARTITION と、ビット幅を増やして配列の要素を連結する ARRAY_MAP の垂直タイプを組み合わせたものです。これにより、使用されるブロック RAM の数を削減すると共に、分割の主な利点であるデータへの並列アクセスを実現できます。このプラグマでは元の配列よりも要素数が少なくビット幅の広い配列が作成され、1 クロック サイクルでアクセスできるデータ量が増加します。
次のようなコードがあるとします。
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 プラグマを使用すると、配列が次の図に示すように変換されます。
図: ARRAY_RESHAPE プラグマ
構文
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 つの配列に垂直連結 (ワードを連結してワード数が大きいものを作成) したものになります。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
例 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
関連項目
- pragma HLS array_map
- pragma HLS array_partition
- 『Vivado Design Suite ユーザー ガイド: 高位合成』 (UG902)
- 『SDAccel 環境プロファイリングおよび最適化ガイド』
pragma HLS data_pack
説明
構造体 (struct
) のデータ フィールドをワード幅が広い 1 つのスカラーにパックします。
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 <bype_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>
: パックする変数を指定します。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 (typedef struct {unsigned char R, G, B;} pixel
) を 1 つの 24 ビット ポインターにパックしています。
typedef struct{
unsigned char R, G, B;
} pixel;
pixel AB;
#pragma HLS data_pack variable=AB
例 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 最適化を使用すると、前の関数またはループがすべての演算を完了する前に、次の関数またはループの演算を開始できるようになります。
図: DATAFLOW プラグマ
DATAFLOW プラグマを指定した場合、HLS ツールで順次関数またはループ間のデータフローが解析され、プロデューサー関数またはループが完了する前にコンシューマー関数またはループの演算を開始できるように、ピンポン RAM または FIFO に基づいてチャネルが作成されます。これにより関数またはループを並列実行でき、レイテンシが削減されて RTL のスループットが向上します。
開始間隔 (II) (関数またはループの開始から次の関数またはループの開始までのサイクル数) が指定されていない場合は、HLS ツールで開始間隔が最小になるようにし、データが使用可能になったらすぐに演算を開始できるようにすることが試みられます。
config_dataflow
コマンドは、データフロー最適化で使用されるデフォルトのメモリ チャネルと FIFO の深さを指定します。詳細は、『Vivado Design Suite ユーザー ガイド: 高位合成』 (UG902) の「config_dataflow
」を参照してください。- シングル プロデューサー コンシューマー違反
- タスクのバイパス
- タスク間のフィードバック
- タスクの条件付き実行
- 複数の exit 条件を持つループ
最後に、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
- xcl_dataflow
- 『Vivado Design Suite ユーザー ガイド: 高位合成』 (UG902)
- 『SDAccel 環境プロファイリングおよび最適化ガイド』
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 プラグマを使用すると、依存を明示的に指定し、偽依存を解決できます。
構文
依存が定義されている関数内に配置します。
#pragma HLS dependence variable=<variable> <class> \
<type> <direction> distance=<int> <dependent>
説明:
variable=<variable>
: 依存を考慮する変数を指定します (オプション)。- <class>: 依存を明確にする必要がある変数のクラスを指定します (オプション)。有効な値は
array
またはpointer
です。ヒント: 関数内で変数または変数のクラスのいずれかを指定していれば、<class> およびvariable=
の両方を指定する必要はありません。 - <type>: 有効な値は
intra
またはinter
です。依存のタイプを指定します。intra
: 同じループ反復内の依存。<type> をintra
、<dependent> を false に設定すると、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
次の例では、HLS ツールでは cols
の値を認識する機能がないので、buff_A[1][col]
への書き込みと buff_A[1][col]
の読み出し間に常に依存性があると想定されます。このようなアルゴリズムでは、cols
が 0 になることはほとんどありませんが、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) {
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 環境プロファイリングおよび最適化ガイド』
pragma HLS expression_balance
説明
C ベースの仕様が演算のシーケンスで記述され、RTL で長い演算チェーンとなることがあります。クロック周期が短い場合、これによりデザイン レイテンシが増加する可能性あります。デフォルトでは、Vivado 高位合成 (HLS) ツールで演算の関連性および接続性を考慮して、演算が並べ替えられます。これにより、ツリーのバランスが取られてチェーンが短くなるので、ハードウェア リソースは増加しますが、デザインのレイテンシを削減できます。
EXPRESSION_BALANCE プラグマを使用すると、指定のスコープ内でこの演算調整をディスエーブルまたは明示的にイネーブルにできます。
構文
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) で個別の階層ブロックのままになります)。
- 同じ階層レベルにある関数のすべてのインスタンスは、同じ 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>
: 必須の引数で、定数として使用する関数引数を定義します。
例 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
- プラグマを領域または関数の本体に適用します。領域または関数の内容が下向きにインライン展開されますが、階層全体に再帰的にインライン展開されることはありません。
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
...
例 2
次の例では、foo_top
の本体内のすべての関数がインライン展開され、この関数に含まれる階層すべてで再帰的にインライン展開が実行されます。ただし、関数 foo_sub
はインライン展開されません。recursive プラグマは関数 foo_top
に配置されています。関数 foo_sub
にはインライン展開をディスエーブルにするプラグマが配置されています。
foo_sub (p, q) {
#pragma HLS inline off
int 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);
...
}
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)
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 プロトコルです。 - 関数名に関連付けられています。
- <mode> 値により
- 関数引数: 各関数引数は、有効ハンドシェイク (
ap_vld
) や肯定応答ハンドシェイク (ap_ack
) などの独自のポート レベル (I/O) インターフェイス プロトコルを持つように指定できます。ポート レベル インターフェイス プロトコルは、最上位関数の各引数および関数の戻り値 (関数が値を返す場合) に対して作成されます。作成されるデフォルトの I/O プロトコルは、C 引数のタイプによって異なります。ブロック レベルのプロトコルが使用されてブロックの演算が開始したら、ポート レベルの I/O プロトコルを使用して、データをブロック内外に順に送信できます。 - 最上位関数によりアクセスされ、スコープ外部で定義されるグローバル変数:
- グローバル変数がアクセスされても、すべての読み出しおよび書き込みが関数のローカルである場合は、リソースは RTL 内に作成されます。RTL に I/O ポートは必要ありません。グローバル変数が外部ソースまたはデスティネーションである場合は、インターフェイスは標準関数引数と同様の方法で指定します。例を参照してください。
INTERFACE プラグマがサブ関数に使用されている場合は、register
オプションのみを使用できます。サブ関数では、<mode> オプションはサポートされません。
バースト モードの指定
構文セクションに説明されているように max_read_burst_length
または max_write_burst_length
オプションを使用してインターフェイスのバースト モードを指定する場合は、AXI 規格に関連する制限と考慮事項があります。
- ARLEN および AWLEN は 8 ビットで、実際のバースト長は AxLEN+1 なので、トランザクションごとのバースト長は 256 ワード以下にする必要があります。
- バースト トランザクションごとに転送される量は合計 4 KB 未満です。
- 4 KB アドレス境界を超えないようにしてください。
- バス幅は、32 ~ 512 ビットの間の 2 のべき乗数 (32、64、128、256、512 ビット) または 4、8、16、32、64 バイトに指定します。
4 KB の制限があるので、各バス幅での最大バースト長は次のようになります。
- 32 ビット幅では、1 つのバースト トランザクションで 256 ワード転送されます。この場合、トランザクションごとに転送される合計バイト数は 1024 です。
- 64 ビット幅では、1 つのバースト トランザクションで 256 ワード転送されます。トランザクションごとに転送される合計バイト数は 2048 です。
- 128 ビット幅では、1 つのバースト トランザクションで 256 ワード転送されます。トランザクションごとに転送される合計バイト数は 4096 です。
- 256 ビット幅では、1 つのバースト トランザクションで 128 ワード転送されます。トランザクションごとに転送される合計バイト数は 4096 です。
- 512 ビット幅では、1 つのバースト トランザクションで 64 ワード転送されます。トランザクションごとに転送される合計バイト数は 4096 です。
max_read_burst_length
または max_write_burst_length
を 128 に設定している場合、100 回反復される for ループからのパイプライン アクセスでは最大バースト長は達成されません。ただし、指定されたバースト長よりも長いアクセスがソース コード内で実行される場合は、アクセスは小さいバーストに分割されます。たとえば、100 回アクセスされるパイプライン for ループで max_read_burst_length
または max_write_burst_length
を 64 に設定すると、最大バースト長 (64) のトランザクションと残りのデータのトランザクション (36 ワードのバースト) に分割されます。
構文
関数内に配置します。
#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
: プロトコルなし。インターフェイスはデータ ポートです。HLS ツールでは、リセット後はデータ ポートが常に安定していると想定され、内部最適化により不要なレジスタが削除されます。ap_vld
: データ ポートと、データが読み出しまたは書き込みに対して有効になったことを示すvalid
ポートがインプリメントされます。ap_ack
: データ ポートと、データが読み出された/書き込まれたことを肯定応答するacknowledge
ポートがインプリメントされます。ap_hs
: データ ポートと、データが読み出しおよび書き込みに対して有効になったことを示すvalid
ポートおよびデータが読み出された/書き込まれたことを肯定応答するacknowledge
ポートがインプリメントされます。ap_ovld
: 出力データ ポートと、データが読み出しまたは書き込みに対して有効になったことを示すvalid
ポートがインプリメントされます。重要: HLS ツールでは、入力引数または読み出し/書き込み引数の入力部分はap_none
モードを使用してインプリメントされます。ap_fifo
: 標準 FIFO インターフェイスのポートが、アクティブ Low FIFO のempty
およびfull
ポートが関連付けられたデータ入力および出力ポートを使用してインプリメントされます。注記: このインターフェイスは、読み出し引数または書き込み引数のみに使用できます。ap_fifo
モードでは双方向の読み出し/書き込み引数はサポートされません。ap_bus
: ポインターおよび参照渡しポートがバス インターフェイスとしてインプリメントされます。ap_memory
: 配列引数が標準 RAM インターフェイスとしてインプリメントされます。Vivado IP インテグレーターで RTL デザインを使用する場合は、メモリ インターフェイスは個別のポートととして表示されます。bram
: 配列引数が標準 RAM インターフェイスとしてインプリメントされます。IP インテグレーターで RTL デザインを使用する場合は、メモリ インターフェイスはシングル ポートとして表示されます。axis
: すべてのポートが AXI4-Stream インターフェイスとしてインプリメントされます。s_axilite
: すべてのポートが AXI4-Lite インターフェイスとしてインプリメントされます。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
が追加されている点が異なります。ザイリンクスでは、HLS ツール ブロックをチェーン接続する場合はap_ctrl_chain
ブロックレベル I/O プロトコルを使用することをお勧めします。
port=<name>
:INTERFACE
プラグマを適用する関数引数、関数戻り値、またはグローバル変数の名前を指定します。ヒント: 関数のreturn
値のポートには、ブロックレベル I/O プロトコル (ap_ctrl_none
、ap_ctrl_hs
、またはap_ctrl_chain
) を割り当てることができます。bundle=<string>
: 関数引数を AXI ポートにまとめます。デフォルトでは、HLS ツールで AXI4-Lite (s_axilite
) インターフェイスとして指定されたすべての関数引数は 1 つの AXI4-Lite ポートにまとめられます。同様に、AXI4 (m_axi
) インターフェイスとして指定されたすべての関数引数は 1 つの AXI4 ポートにまとめられます。このオプションでは、同じbundle=<string>
が指定されたすべてのインターフェイス ポートが 1 つの AXI インターフェイス ポートにまとめられ、RTL ポートの名前が <string> になります。重要:bundle=
で名前を指定する場合は、すべて小文字にする必要があります。register
: 信号および関連プロトコル信号にレジスタを付けるオプションのキーワードで、信号が少なくとも関数実行の最終サイクルまで保持されます。このオプションは、次のインターフェイス モードに適用されます。ap_none
ap_ack
ap_vld
ap_ovld
ap_hs
ap_stable
axis
s_axilite
ヒント:config_interface
コマンドの-register_io
オプションは、最上位関数のすべての入力/出力にレジスタを付けるかどうかをグローバルに制御します。詳細は、『Vivado Design Suite ユーザー ガイド: 高位合成』 (UG902) を参照してください。register_mode= <forward|reverse|both|off>
:register
キーワードと共に使用し、forward
パス (TDATA および TVALID)、reverse
パス (TREADY)、またはその両方 (both
) のパス (TDATA、TVALID、および TREADY) にレジスタを配置するか、どのポート信号にもレジスタを配置しないか (off
) を指定します。デフォルトのregister_mode
はboth
です。AXI4-Stream (axis
) サイドチャネル信号はデータ信号と考慮され、TDATA にレジスタが付けられるとレジスタが付けられます。depth=<int>
: テストベンチで処理されるサンプルの最大数を指定します。この設定は、HLS ツールで RTL 協調シミュレーション用に作成される検証アダプターに必要な FIFO の最大サイズを示します。ヒント:depth
は通常はオプションですが、m_axi
インターフェイスでは必須です。offset=<string>
: AXI4-Lite (s_axilite
) および AXI4 (m_axi
) インターフェイスのアドレス オフセットを指定します。s_axilite
インターフェイスでは、<string> はレジスタ マップのアドレスを指定します。m_axi
インターフェイスでは、<string> は次の値を指定します。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 つにのみ指定します。latency=<value>
: mode がm_axi
の場合、AXI4 インターフェイスのレイテンシを指定し、読み出しまたは書き込みの指定サイクル (レイテンシ) 前にバス要求を開始できるようにします。このレイテンシ値が小さすぎると、デザインが準備完了になるのが早すぎ、バスを待つために停止する可能性があります。レイテンシ値が大きすぎると、バス アクセスは承認されても、デザインがアクセスを開始するのを待つためにバスが停止する可能性があります。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
) インターフェイスでのバースト転送で書き込まれるデータの最大数を指定します。ヒント: ポートが読み出し専用ポートの場合は、メモリ リソースを節約するためnum_write_outstanding=1
およびmax_write_burst_length=2
を設定してください。ポートが書き込み専用の場合は、num_read_outstanding=1
およびmax_read_burst_length=2
を設定します。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;
}
}
例 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 - TRANSPOSE
void 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
関連項目
- 『Vivado Design Suite ユーザー ガイド: 高位合成』 (UG902)
pragma HLS latency
説明
関数、ループ、および領域の完了するまでの最小レイテンシまたは最大レイテンシ、あるいはその両方を指定します。
- レイテンシ
- 出力を生成するのに必要なクロック サイクル数。
- 関数レイテンシ
- 関数がすべての出力値を計算して戻るまでに必要なクロック サイクル数。
- ループ レイテンシ
- ループのすべての反復を実行するのにかかるサイクル数。
『Vivado Design Suite ユーザー ガイド: 高位合成』 (UG902) の「パフォーマンス メトリクスの例」を参照してください。
Vivado 高位合成 (HLS) ツールでは常に、デザインのレイテンシを最短にするよう試みられます。LATENCY プラグマを指定すると、ツールで次のように処理されます。
- レイテンシが最小値より大きく、最大値未満: 制約は満たされています。これ以上の最適化は実行されません。
- レイテンシが最小値未満: HLS ツールで指定の最小レイテンシ未満を達成できる場合は、レイテンシが指定値まで拡張されます。リソース共有が増加する可能性があります。
- レイテンシが最大値を超える: HLS ツールで最大値以下でスケジューリングできない場合は、指定された制約を満たすことができるようエフォート レベルが上げられます。それでも最大レイテンシを満たすことができない場合は、警告が表示され、達成可能な最短のレイテンシでデザインが作成されます。
構文
レイテンシを制御する関数、ループ、または領域内に配置します。
#pragma HLS latency min=<int> max=<int>
説明:
min=<int>
: 関数、ループ、またはコードの領域の最小レイテンシを指定します (オプション)。max=<int>
: 関数、ループ、またはコードの領域の最大レイテンシを指定します (オプション)。注記: min および max はオプションですが、いずれかを指定する必要があります。
例 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 つのループ階層にフラット化し、レイテンシを改善します。
レジスタ トランスファー レベル (RTL) インプリメンテーションでは、外側のループから内側のループに、内側のループから外側のループに移動するのに 1 クロック サイクルかかります。入れ子のループをフラットにすると、それらを 1 つのループとして最適化できるので、クロック サイクル数を削減でき、ループ本文のロジックをさらに最適化することが可能です。
ループ階層内の一番内側にあるループの本体に LOOP_FLATTEN プラグマを適用します。完全または半完全ループのみをこの方法でフラットにできます。
- 完全ループの入れ子:
- 最内ループのみにループ本体の内容が含まれます。
- ループ文の間に指定されるロジックはありません。
- すべてのループ範囲は定数です。
- 半完全ループの入れ子:
- 最内ループのみにループ本体の内容が含まれます。
- ループ文の間に指定されるロジックはありません。
- 最外ループの範囲は変数にできます。
- 不完全ループの入れ子: 内側のループの範囲が変数であったり、ループ本体が内側のループにのみ含まれているとは限らない場合、コードの構造を変更するか、ループ本体内のループを展開して、完全ループの入れ子を作成してみてください。
構文
C ソースの入れ子のループ内に配置します。
#pragma HLS loop_flatten off
説明:
off
: オプションのキーワードで、フラットされないようにします。指定ロケーション内の一部のループはフラットにせず、それ以外のループをフラットにできます。注記: LOOP_FLATTEN プラグマを使用すると、最適化がイネーブルになります。
例 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) でループ本文のインプリメンテーション間の移行に必要なクロック サイクル数を削減できます。
- ループを並列にインプリメントできます (可能な場合)。
LOOP_MERGE プラグマは、適用されたスコープ内のループすべてを結合しようとします。たとえば、LOOP_MERGE プラグマをループの本体に適用した場合、Vivado 高位合成 (HLS) ツールではプラグマがそのループ自体ではなく、ループ内の下位ループに適用されます。
ループ結合の規則は、次のとおりです。
- ループの境界が変数の場合、同じ値 (反復回数) である必要があります。
- ループの境界が定数の場合、最大定数値が結合されたループの境界として使用されます。
- 境界が変数のループと定数のループを結合することはできません。
- 結合するループ間のコードが、結合により悪影響を受けないようにします。コードを複数回実行しても常に同じ結果になるようにする必要があります。たとえば、a=b は許容されますが a=a+1 は許容されません。
- ループに FIFO 読み出しが含まれる場合は、ループは結合できません。結合により読み出しの順序が変更されてしまうためです。FIFO または FIFO インターフェイスからの読み出しは、常に順序どおりに実行される必要があります。
構文
C ソースの必要なスコープまたは領域内に配置します。
#pragma HLS loop_merge force
説明:
force
: HLS ツールで警告が出力されてもループが結合されるようにするオプションのキーワードです。重要: この場合、結合したループが問題なく動作するかどうかをユーザーが確認する必要があります。
例
次の例では、関数 foo
の連続するすべてのループを 1 つのループに結合しています。
void foo (num_samples, ...) {
#pragma HLS loop_merge
int i;
...
loop_1: for(i=0;i< num_samples;i++) {
...
次の例では、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
説明
LOOP_TRIPCOUNT プラグマをループに適用すると、ループで実行される反復回数の合計を手動で指定できます。
Vivado 高位合成 (HLS) ツールにより、各ループの合計レイテンシ、つまりループのすべての反復を実行するためのクロック サイクル数がレポートされます。ループ レイテンシは、ループ反復数 (トリップカウント) に依存します。
トリップカウントは、定数値であることもあり、ループ式 (x < y など) で使用される変数の値やループ内の制御文によって異なる場合もあります。HLS ツールでトリップカウントを決定できないこともあり、その場合はレイテンシは不明になります。これは、トリップカウントの決定に使用される変数が次のいずれかの場合です。
- 入力引数。
- ダイナミック演算により算出される変数。
ループのレイテンシが不明または算出できない場合、LOOP_TRIPCOUNT プラグマを使用してループの反復回数の最小値および最大値を指定できます。これにより、ループのレイテンシがデザインの総レイテンシのどの程度を占めているのかがツールで解析されてレポートされるので、デザインに適切な最適化を判断するのに役立ちます。
構文
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> の整数倍)。
- 条件コードの実行は、ループ本体の残りの部分の 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)
pragma HLS pipeline
説明
PIPELINE プラグマは、演算を同時実行できるようにすることにより関数またはループの開始間隔 (II) を短縮します。
パイプライン処理された関数またはループは、<N> クロック サイクル (<N> は関数またはループの II) ごとに新しい入力を処理できます。PIPELINE プラグマのデフォルトの II は 1 で、クロック サイクルごとに 1 つの入力を処理します。開始間隔は、プラグマの II オプションを使用して指定できます。
ループをパイプライン処理すると、次の図に示すように、ループ内の演算が同時実行されるようインプリメントできます。次の図の (A) はデフォルトの順次演算を示しています。各入力は 3 クロック サイクルごとに処理され (II=3)、最後の出力が書き出されるまでに 8 クロック サイクルかかっています。
図: ループのパイプライン
Vivado 高位合成 (HLS) ツールで指定した II でデザインを作成できない場合は、警告メッセージが表示されて、可能な最短の開始間隔でデザインが作成されます。
警告メッセージを参考にデザインを解析し、必要な開始間隔を満たしてデザインを作成するためにどの手順が必要なのかを判断します。
構文
C ソースの関数の本体またはループ内に配置します。
#pragma HLS pipeline II=<int> enable_flush rewind
説明:
II=
<int>: パイプラインの開始間隔を指定します。HLS ツールでは、この指定を満たすことが試みられます。データの依存性によって、実際の開始間隔はこの指定より大きくなることがあります。デフォルトは 1 です。enable_flush
: オプションのキーワードで、パイプラインの入力で有効であったデータが非アクティブになった場合にデータをフラッシュして空にするパイプラインをインプリメントします。ヒント: この機能は、パイプライン処理された関数でのみサポートされ、パイプライン処理されたループではサポートされません。rewind
: オプションのキーワードで、1 つのループ反復の終了と次の反復の開始の間に一時停止のない連続ループ パイプライン処理 (巻き戻し) をイネーブルにします。巻き戻しは、最上位関数内に 1 つのループしかない (完全なループ ネスト) 場合にのみ効果的です。ループ前のコード部分は、次のようになります。- 初期化と認識されます。
- パイプラインで一度だけ実行されます。
- 条件演算 (if-else) を含むことはできません。
ヒント: この機能は、パイプライン処理されたループでのみサポートされ、パイプライン処理された関数ではサポートされません。
例 1
この例では、関数 foo
が開始間隔 1 でパイプライン処理されます。
void foo { a, b, c, d} {
#pragma HLS pipeline II=1
...
}
関連項目
- pragma HLS dependence
- xcl_pipeline_loop
- 『Vivado Design Suite ユーザー ガイド: 高位合成』 (UG902)
- 『SDAccel 環境プロファイリングおよび最適化ガイド』
pragma HLS reset
説明
RESET プラグマは、特定のステート変数 (グローバルまたはスタティック) のリセットを追加または削除します。
リセット ポートは、リセット信号が適用されたときにリセット ポートに接続されているレジスタおよびブロック 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)pragma HLS resource
説明
変数 (配列、算術演算、関数引数) をレジスタ トランスファー レベル (RTL) にインプリメントするのに使用するライブラリ リソース (コア) を指定します。RESOURCE プラグマを指定しない場合、Vivado 高位合成 (HLS) ツールにより使用するリソースが自動的に判断されます。HLS ツールでは、ハードウェア コアを使用して演算がツールインプリメントされます。演算をインプリメントできるコアがライブラリに複数ある場合、RESOURCE プラグマを使用して使用するコアを指定できます。使用可能なコアのリストを生成するには、list_core
コマンドを使用します。
list_core
コマンドを使用すると、ライブラリで使用可能なコアの詳細を表示できます。list_core
は HLS ツールの Tcl コマンド インターフェイスでのみ使用でき、set_part
コマンドを使用してザイリンクス デバイスを指定する必要があります。デバイスを指定しない場合、list_core
コマンドは実行されません。たとえば、配列をインプリメントするのに使用するライブラリのメモリ エレメントを RESOURCE プラグマを使用して指定し、配列をシングル ポート RAM とデュアル ポート RAM のどちらとしてインプリメントするかを制御できます。配列に関連付けられているメモリ タイプによって RTL で必要なポートが決まるので、これは最上位関数インターフェイスの配列には重要な方法です。
latency=
オプションを使用すると、コアのレイテンシを指定できます。インターフェイスのブロック RAM の場合、latency=
オプションを指定すると、たとえばレイテンシ 2 または 3 の SRAM をサポートするなど、インターフェイスにオフチップの標準でない SRAM を記述できます。内部演算の場合、latency=
オプションを使用すると、演算をより多くのパイプライン段を使用してインプリメントできます。これらの追加のパイプライン段により、RTL 合成中にタイミング問題を解決しやすくなります。
詳細は、『Vivado Design Suite ユーザー ガイド: 高位合成』 (UG902) の「インターフェイスの配列」を参照してください。
latency=
オプションを使用するには、使用可能な複数段のコアを演算に含める必要があります。HLS ツールには、基本的な算術演算 (加算、減算、乗算、除算)、すべての浮動小数点演算、およびすべてのブロック RAM 用に複数段コアが含まれています。より良い結果を得るため、ザイリンクスでは C の場合は -std=c99
を、C および C++ の場合は -fno-builtin
を使用することをお勧めします。-std=c99
などの C コンパイル オプションを指定するには、Tcl コマンドの add_files
に -cflags
オプションを使用します。または、[Project Settings] ダイアログ ボックスの [Edit CFLAGs] ボタンをクリックします。詳細は、『Vivado Design Suite ユーザー ガイド: 高位合成』 (UG902) の「新規合成プロジェクトの作成」を参照してください。
構文
C ソースの配列変数が定義されている関数の本体内に配置します。
#pragma HLS resource variable=<variable> core=<core>\
latency=<int>
説明:
variable=
<variable>: RESOURCE プラグマを設定する配列、算術演算、または関数引数を指定します (必須)。core=
<core>: テクノロジ ライブラリで定義されているようにコアの名前を指定します。latency=
<int>: コアのレイテンシを指定します。
例 1
次の例では、関数 foo
の変数 <c> の乗算をインプリメントするのに 2 段のパイプライン乗算器を指定しています。変数 <d> に使用するコアは、HLS ツールにより決定されます。
int foo (int a, int b) {
int c, d;
#pragma HLS RESOURCE variable=c latency=2
c = 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)pragma HLS stream
説明
デフォルトでは、配列変数は RAM としてインプリメントされます。
- 最上位関数の配列パラメーターは、RAM インターフェイスのポートとしてインプリメントされます。
- 一般配列は、読み出しおよび書き込みアクセス用に RAM としてインプリメントされます。
- DATAFLOW 最適化に関連するサブ関数では、配列引数は RAM のピンポン バッファー チャネルを使用してインプリメントされます。
- ループ ベースの DATAFLOW 最適化に関連する配列は、RAM のピンポン バッファー チャネルを使用してインプリメントされます。
配列に格納されているデータが順次に消費または生成される場合は、ストリーミング プラグマを指定して RAM ではなく 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 デザインのエリアを大幅に削減できます。ヒント: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)
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)
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];
}
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> は変数であり、HLS ツールでその値を判断できないので、部分展開されるループに終了チェックと制御ロジックが追加されます。ただし、展開係数 (この例では 2) が最大反復回数 <X> の整数因数であるとわかっている場合は、skip_exit_check
オプションを使用して終了チェックとその関連ロジックを削除できます。これによりエリアが最小限に抑えられ、制御ロジックが単純になります。
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
次の例では、関数 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)
- opencl_unroll_hint
- 『SDAccel 環境プロファイリングおよび最適化ガイド』