説明
次は、一般的な状況における具体的な例です。
C コードを合成して加速化しようとしています。
下の例では、質問を説明するためにコードを切り詰めて表示しています。
構造体を使用するときに配列パーティションをしっかりと制御することが主な目的です。
#define NPOINTS 1000
#define N_CONS 5
#define O_CONS 3
typedef struct _point_data {
double xx[O_CONS]; /* */
double consflux[N_CONS];
double dtvol;
double otherdouble;
} PointData;
void fpga_runge_kutta_step( PointData point_data[NPOINTS])
{
double coeff = 0.15;
double fac;
int pnt, eq;
L1: for(pnt = 0; pnt < NPOINTS; pnt++){
fac = coeff * point_data[pnt].dtvol;
L2: for(eq = 0; eq < N_CONS; eq++){
point_data[pnt].consflux[eq] *= fac;
}
}
}
L1 をパイプライン処理しましたが (L2 はその後アンロール)、consflux が配列であるためにパイプライン II は 5 です。
これにより、ポート point_data_consflux が生成されます。
この point_data_consflux を 5 (N_CONS) ポート (point_data_consflux_0、point_data_consflux_1...) に分割したいと思います。
この方法を教えてください。
ソリューション
これは、ほかの一般的な指示子の場合と同様、pragma または Tcl 指示子を使用して、コード構造を変更せずに実現できます。
ここでは、指示子を適用する対象のオブジェクトを逆参照する方法を見つけることが大切です。
基本的に入力配列は、分割させる配列を含む _point_data 型への単なるポインターです。
この事実に基づくと、'->' メンバー アクセス演算子を使用してポインターを逆参照できます。
'.' アクセスは機能しません。その理由は、point_data[]... の特定の要素を逆参照する必要があるためです。
方法 1:
1 つ目の方法は、指示子の pragma バージョンをソース コードに埋め込むことです。
void fpga_runge_kutta_step( PointData point_data[NPOINTS])
{
double coeff = 0.15;
double fac;
int pnt, eq;
#pragma AP array_partition complete variable=point_data->consflux
...
この方法の利点は、ほかの最適化を探求するために作成される可能性のあるすべてのソリューションに対して設定されることです。
方法 2:
2 つ目の方法は、特定のソリューションに対して次の Tcl コマンドを使用することです (バッチ モード スクリプトで、または GUI から directive.tcl ファイルに入力して)。
set_directive_array_partition -type complete fpga_runge_kutta_step point_data->consflux
(AutoESL / Vivado HLS 2012.1 では) これらの方法は両方とも機能し、正しいポート マッピング、つまり consflux に対しては 5 つの 1000 要素 RAM ポート、dtvol に対しては 1 つの 1000 要素 RAM ポートになります (例のほかのメンバーは未使用のため、ポートとして表示されない)。