第 2 章
Vitis HLS のプログラム
注意: __SYNTHESIS__はコードやコンパイラ オプションで定義または定義解除すると、コンパイル エラーになる ことがあります。
次に、サブ関数からの中間結果をハード ドライブのファイルに保存するコード例を示します。__SYNTHESIS__ マク ロを使用すると、合成不可能なファイルの書き込みが合成中に無視されるようになります。
#include "hier_func4.h"
int sumsub_func(din_t *in1, din_t *in2, dint_t *outSum, dint_t *outSub) { *outSum = *in1 + *in2;
*outSub = *in1 - *in2;
}
int shift_func(dint_t *in1, dint_t *in2, dout_t *outA, dout_t *outB) { *outA = *in1 >> 1;
*outB = *in2 >> 2;
}
void hier_func4(din_t A, din_t B, dout_t *C, dout_t *D) { dint_t apb, amb;
sumsub_func(&A,&B,&apb,&amb);
#ifndef __SYNTHESIS__
FILE *fp1; // The following code is ignored for synthesis char filename[255];
sprintf(filename,Out_apb_%03d.dat,apb);
fp1=fopen(filename,w);
fprintf(fp1, %d \n, apb);
fclose(fp1);
#endif
shift_func(&apb,&amb,C,D);
}
__SYNTHESIS__ マクロを使用すると、C 関数からコード自体を削除せずに、合成不可能なコードを削除できます。
ただし、このマクロを使用すると、シミュレーション用の C コードと合成用の C コードが異なることになります。
注意: __SYNTHESIS__ マクロを C コードの機能を変更するために使用すると、C シミュレーションと C 合成の結 果が異なります。このようなコードのエラーはデバッグしにくいので、機能を変更するために__SYNTHESIS__マ クロを使用するのはお勧めできません。
ダイナミック メモリの使用
システム内のメモリ割り当てを管理するシステム コール、たとえば malloc()、alloc()、および free() は、OS のメモリにあるリソースを使用し、ランタイム中に作成およびリリースされます。ハードウェア インプリメンテーシ ョンを合成できるようにするには、デザインに必要なリソースがすべて 指定され、含まれている必要があります。
メモリ割り当てのシステム コールは、合成前にデザインから削除する必要がありますが、ダイナミック メモリ操作 がデザインの機能を定義するために使用されているので、同等の範囲が制限された表現に変換する必要があります。
次に、malloc()を使用するデザインを合成可能なバージョンに変換するコード例を示します。次の 2 つの便利なコ ーディング スタイル手法が含まれます。
• このデザインでは __SYNTHESIS__ マクロは使用しません。
合成可能なバージョンまたは合成不可能なバージョンの選択には、ユーザー定義の NO_SYNTH マクロを使用しま す。これにより、同じコードが C でシミュレーションされ、Vitis HLS で合成されます。
• malloc()を使用する元のデザインのポインターは、固定サイズのエレメント用に書き直す必要はありません。
固定サイズのリソースは作成でき、既存ポインターは単に固定サイズのリソースを指定するようにできます。こ の方法により、既存デザインを手動で書き直す必要はなくなります。
#include "malloc_removed.h"
#include <stdlib.h>
//#define NO_SYNTH
dout_t malloc_removed(din_t din[N], dsel_t width) {
#ifdef NO_SYNTH
long long *out_accum = malloc (sizeof(long long));
int* array_local = malloc (64 * sizeof(int));
#else
long long _out_accum;
long long *out_accum = &_out_accum;
int _array_local[64];
int* array_local = &_array_local[0];
#endif int i,j;
LOOP_SHIFT:for (i=0;i<N-1; i++) { if (i<width)
*(array_local+i)=din[i];
else
*(array_local+i)=din[i]>>2;
}
*out_accum=0;
LOOP_ACCUM:for (j=0;j<N-1; j++) { *out_accum += *(array_local+j);
}
return *out_accum;
}
コード変更はデザインの機能に影響するので、ザイリンクスでは __SYNTHESIS__ マクロの使用はお勧めしません。
ザイリンクスでは、次の手順をお勧めします。
1. ユーザー定義のマクロ NO_SYNTH をコードに追加して、コードを修正します。
2. NO_SYNTH マクロをイネーブルにし、C シミュレーションを実行して結果を保存します。
3. NO_SYNTH マクロをディスエーブルにし、C シミュレーションを実行して結果が同じになるかどうかを検証しま す。
4. このユーザー定義のマクロをディスエーブルにして 合成を実行します。
この方法を使用すると、アップデートされたコードが C シミュレーションで検証され、同じコードが合成されるよう にできます。C でのダイナミック メモリ使用に関する制限と同様、Vitis HLS では、ダイナミックに作成/削除される C++ オブジェクトも合成でサポートされません。
次のコードは、ランタイムで新しい関数を作成するので、合成できません。
Class A { public:
virtual void bar() {â¦};
};
void fun(A* a) { a->bar();
}A* a = 0;
if (base) a = new A();
else
a = new B();
foo(a);
ポインターの制限
一般的なポインターの型変換
Vitis HLS では、一般的なポインターの型変換はサポートされませんが、ネイティブ C 型間ではサポートされます。
ポインター配列
Vitis HLS では、各ポインターがスカラーまたはスカラーの配列を指定する場合に、ポインター配列が合成でサポート
されます。ポインター配列では、別のポインターを指定することはできません。
関数ポインター
関数ポインターはサポートされていません。
再帰関数
再帰関数は合成できません。これは、次のような再帰が恒久的に繰り返される可能性のある関数に適用されます。
unsigned foo (unsigned n)
{ if (n == 0 || n == 1) return 1;
return (foo(n-2) + foo(n-1));
}
Vitis HLS では、有限数の関数呼び出しがある末尾再帰もサポートされません。
unsigned foo (unsigned m, unsigned n) { if (m == 0) return n;
if (n == 0) return m;
return foo(n, m%n);
}
C++ では、テンプレートで末尾再帰をインプリメントできます。C++ テンプレートについては、次で説明します。
標準テンプレート ライブラリ
C++ 標準テンプレート ライブラリ (STL) には、再帰関数が含まれており、ダイナミック メモリ割り当てが使用されま す。そのため、STL は Vitis HLS で合成できません。STL を使用する場合は、再帰、ダイナミック メモリ割り当て、
ダイナミックなオブジェクトの作成/削除などの特性を持たない同じ機能のローカル関数を作成します。
注記: 合成では std::complex のような標準データ型がサポートされます。
関数
最上位関数は合成後に最上位 RTL デザインになり、サブ関数は RTL デザインのブロックに合成されます。
重要: 最上位関数は、static 関数にはできません。
合成後、デザインの各関数に対して独自の合成レポートと HDL ファイル (Verilog および VHDL) が作成されます。
関数のインライン展開
サブ関数をオプションでインライン展開し、そのロジックと周囲の関数のロジックとを統合できます。関数をインラ イン展開することにより最適化が改善されることはありますが、メモリに維持して解析する必要のあるロジックが増 えるので、ランタイムは増加する可能性があります。
ヒント: Vitis HLS では、小型の関数のインライン展開は自動的に実行されます。小型の関数の自動インライン展開を にするには、その関数に対して inline 指示子を off に設定します。
関数がインライン展開されると、その関数に対するレポートおよび別の RTL ファイルは作成されません。サブ関数の ロジックおよびループは上の階層の関数と統合されます。
コーディング スタイルの影響
関数のコーディング スタイルは、主に関数の引数およびインターフェイスに影響します。
関数への引数サイズが正確に記述されていれば、Vitis HLS でこの情報がデザインに伝搬されます。すべての変数に対 して任意精度型を作成する必要はありません。次の例では、2 つの整数が乗算されますが、下位 24 ビットのみが結 果に使用されます。
#include "ap_cint.h"
int24 foo(int x, int y) { int tmp;
tmp = (x * y);
return tmp }
このコードが合成されると、出力が 24 ビットに切り捨てられた 32 ビット乗算器になります。
次のコード例 のように、入力のサイズが正しく 12 ビット型 (int12) になっていれば、最終 RTL では 24 ビット乗算器 が使用されます。
#include "ap_cint.h"
typedef int12 din_t;
typedef int24 dout_t;
dout_t func_sized(din_t x, din_t y) { int tmp;
tmp = (x * y);
return tmp }
2 つの関数入力に任意精度型を使用するだけで、Vitis HLS で 24 ビット乗算器を使用するデザインが作成され、12 ビ ット型がデザインに伝搬されます。ザイリンクスでは、階層内のすべての関数の引数のサイズを正しく記述すること を勧めしています。
通常は、変数が関数インターフェイスから、特に最上位関数インターフェイスから直接駆動されると、最適化の中に 実行されないものがでてくることがあります。典型的な例は、入力がループ インデックスの上位制限として使用され る場合です。