AXI4 マスター インターフェイスは配列またはポインター/リファレンス引数で使用でき、Vitis HLS では次のいずれか
のモードでインプリメントされます。
• 個別のデータ転送
• バースト モードのデータ転送
個別のデータ転送では、Vitis HLS で各アドレスのデータの 1 つの要素が読み出されるか書き込まれます。次の例に、
1 つの読み出しおよび 1 つの書き込み操作を示します。Vitis HLS で AXI インターフェイスにアドレスが生成され、1 つのデータ値とアドレスが読み出され、1 つのデータ値が書き込まれます。インターフェイスは、アドレスごとに 1 つのデータ値を転送します。
void bus (int *d) { static int acc = 0;
acc += *d;
*d = acc;
}
バースト モード転送では、Vitis HLS で 1 つのベース アドレスを使用してデータが読み出しまたは書き込みされ、そ の後に複数のシーケンシャル データ サンプルが続くので、より高いデータ スループットを達成できます。バースト モードは、C のmemcpy関数を使用しテイル場合、またはforループをパイプライン処理している場合に使用できま す。
注記: C の memcpy 関数は、AXI4 マスター インターフェイスで指定された最上位関数の引数のデータ転送に使用され た場合にのみ、合成でサポートされます。
次の例に、memcpy 関数を使用したバースト モードのコピーを示します。最上位関数の引数 a は、AXI4 マスター イ ンターフェイスとして指定されています。
void example(volatile int *a){
#pragma HLS INTERFACE m_axi depth=50 port=a
#pragma HLS INTERFACE s_axilite port=return //Port a is assigned to an AXI4 master interface int i;
int buff[50];
//memcpy creates a burst access to memory memcpy(buff,(const int*)a,50*sizeof(int));
for(i=0; i < 50; i++){
buff[i] = buff[i] + 100;
}
memcpy((int *)a,buff,50*sizeof(int));
}
この例が合成されると、次の図のようなインターフェイスになります。
注記: この図では、AXI4 インターフェイスは展開されていません。
図 63: AXI4 インターフェイス
次の例は前の例と同じコードですが、データ出力をコピーするのに for ループを使用しています。
void example(volatile int *a){
#pragma HLS INTERFACE m_axi depth=50 port=a
#pragma HLS INTERFACE s_axilite port=return //Port a is assigned to an AXI4 master interface int i;
int buff[50];
//memcpy creates a burst access to memory memcpy(buff,(const int*)a,50*sizeof(int));
for(i=0; i < 50; i++){
buff[i] = buff[i] + 100;
}
for(i=0; i < 50; i++){
#pragma HLS PIPELINE a[i] = buff[i];
}}
バースト読み出しまたは書き込みをインプリメントするのに for ループを使用する場合は、次の要件に従ってさい。
• ループをパイプライン処理する
• 昇順でアドレスにアクセスする
• 条件文内にアクセスを記述しない
• 入れ子状態のループの場合、バースト動作が抑制されてしまわないよう、ループを平坦にしない
注記: ポートが別の AXI ポートにまとめられない場合、for ループ内で使用できるのは読み出し 1 つと書き込み 1 つ のみです。次の例では、別の AXI インターフェイスを使用して、2 つの読み出しをバースト モードで実行するところ を示します。
次の例では、Vitis HLS でポート読み出しがバースト転送としてインプリメントされます。ポート a は bundle オプ ションを使用せずに指定され、デフォルトの AXI インターフェイスにインプリメントされます。b ポートにはバンド ル名が指定され、d2_port という別の AXI インターフェイスにインプリメントされます。
void example(volatile int *a, int *b){
#pragma HLS INTERFACE s_axilite port=return
#pragma HLS INTERFACE m_axi depth=50 port=a
#pragma HLS INTERFACE m_axi depth=50 port=b bundle=d2_port
int i;
int buff[50];
//copy data in
for(i=0; i < 50; i++){
#pragma HLS PIPELINE buff[i] = a[i] + b[i];
}...
}
重要: 構造体は、パックされている場合 (Vitis HLS のデフォルト) に AXI4 マスター インターフェイスでのみサポート されます。
ポート幅の自動変更
Vitis HLS には、カーネル インターフェイス ポートのサイズを Vitis ツール フローでのバースト アクセス用に自動的 に 512 に変更する機能があります。これはつまり、バースト可能なアクセスは 512 ビット幅にしか変更されないと いうことです。バーストするには、バースト転送の最適化 で説明する前提条件にコードが従っている必要がありま
す。Vitis HLS では、次の 2 つのコマンドを使用してポート幅の自動変更を制御できます。
• config_interface -m_axi_max_widen_bitwidth <N>: M-AXI インターフェイスのバーストを 指定した 幅まで自動的に広げます。<N> の値には、0 ~ 1024 の 2 のべき乗を指定する必要があります。バースト幅を広 げる場合は、バーストだけでなく、強力なアライメント プロパティが必要となります。<N> のデフォルト値は 512 ビットです。
• config_interface -m_axi_alignment_byte_size <N>: M-AXI インターフェイスにマップされる最上位 関数ポインターが少なくとも指定したバイト幅 (2 のべき乗) に揃えられるとします。これにより、自動バースト拡 張が実行されやすくなります。デフォルトのアライメントは 64 バイトです。
自動最適化の場合、カーネルが次の前提条件を満たす必要があります。
• アクセスするメモリのロケーションおよび時間が単調に増加する必要があります。以前にアクセスされた 2 つの メモリ ロケーション間のメモリ ロケーションにアクセスすることはできません (オーバーラップなしというこ と)。
• バーストの前提条件は、すべてポート幅のサイズ変更にも適用されます。
• グローバル メモリからのアクセス パターンはシーケンシャル順で、次の追加要件に従っている必要があります。
○ シーケンシャル アクセスは、ベクター以外のタイプにする必要があります。
○ シーケンシャル アクセスの開始は、拡張されたワード サイズにアラインメントする必要があります。
○ シーケンシャルアクセスの長さはその拡張係数で割り切れるようにする必要があります。
次のコード例は、これを示しています。
for (int i = 0; i < iterations; i++) {$
┆ #pragma HLS LOOP_TRIPCOUNT min=c_len/c_n max=c_len/c_n$
$ // Pipelining loops that access only one variable is the ideal way to$
// increase the global memory bandwidth.$
read_a:$
┆ for (int x = 0; x < N; ++x) {$
┆ ┆#pragma HLS LOOP_TRIPCOUNT min=c_n max=c_n$
┆ ┆ #pragma HLS PIPELINE II=1$
// #pragma HLS unroll factor=8$
┆ ┆ result[x] = a[i * N + x];$
┆ }$
$ read_b:$
┆ for (int x = 0; x < N; ++x) {$
┆ ┆ #pragma HLS LOOP_TRIPCOUNT min=c_n max=c_n$
┆ ┆ #pragma HLS PIPELINE II=1$
┆ ┆ result[x] += b[i * N + x];$
┆ }$
$ write_c:$
┆ for (int x = 0; x < N; ++x) {$
┆ ┆ #pragma HLS LOOP_TRIPCOUNT min=c_n max=c_n$
┆ ┆ #pragma HLS PIPELINE II=1$
┆ ┆ c[i * N + x] = result[x];$
┆ }$
}$
}$ }$
"src/
注記: 上記のコード例は、Vitis HLS サンプルの cpp-kernels/loop pipeline からのものです。
上記のコードの自動最適化の幅は、次の 3 つの段階で実行されます。
1. まず、read_a ループのアクセス パターン数がチェックされます。1 つのループ反復中に 1 アクセスなので、最適
化ではインターフェイスのビット幅が 32= 32 *1 (int 変数のビット幅 * アクセス) と決定されます。
2. 次の式の項を使用して、config_interface m_axi_max_widen_bitwidth 512 で指定されたデフォルト の最大値を到達しようとします。
length = (ceil((loop-bound of index inner loops) * (loop-bound of index - outer loops)) * #(of access-patterns))
• 上記のコードでは外側のループが不完全なループなので、外側のループのバースト転送はなくなります。こ のため、長さには内側のループのみが含まれるので、計算式は次のように短縮できます。
length = (ceil((loop-bound of index inner loops)) * #(of access-patterns))
または、長さ = ceil(128) *32 = 4096 となります。
3. 最後に、計算された長さが 2 のべき乗かどうか確認され、2 のべき乗の場合は長さが m_axi_max_widen_bitwidth で指定された幅に制限されます。
ポート幅の自動変更機能を使用する前に、その利点と問題点について考慮してください。この機能は、データ型サイ ズではなく大きなベクターを読み出すので、DDR からの読み出しレイテンシを改善します。大きなベクターをバッフ ァリングして、データをデータパス サイズに合わせてシフトする必要があるので、リソースも増えます。ただし、自 動ポート幅変更では標準 C データ型のみがサポートされ、ap_int、ap_uint、構造体、配列などの集合体でないデータ 型はサポートされません。
AXI4 バースト動作の制御
最適な AXI4 インターフェイスとは、バスへのアクセス待機中にデザインが停止せず、バス アクセスが承認された後 の読み出し/書き込み待機中にバスが停止しないようなインターフェイスです。最適な AXI4 インターフェイスを作 成するには、INTERFACE プラグマまたは指示子に次のオプションを使用してバーストの動作を指定し、AXI4 インタ ーフェイスの効率を最適化します。バースト転送の詳細は、バースト転送の最適化 を参照してください。
これらのオプションの中には、内部ストレージを使用してデータをバッファリングするため、エリアおよびリソース への影響があるものもあります。
• latency: AXI4 インターフェイスのレイテンシを指定し、読み出しまたは書き込みの指定サイクル (レイテンシ) 前にバス要求を開始できるようにします。この値が小さすぎると、デザインが準備完了になるのが早すぎ、バス を待つために停止する可能性があります。この値が大きすぎると、バス アクセスが承認されても、デザインがア クセスを開始するのを待つためにバスが停止する可能性があります。
• max_read_burst_length: バースト転送中に読み出されるデータ値の最大数を指定します。
• num_read_outstanding: デザインが停止する前に、AXI4 バスに対して応答なしで送信できる読み出し要求の 数を指定します。これにより、デザインの内部ストレージである FIFO のサイズ
(num_read_outstanding*max_read_burst_length*word_size) が決まります。
• max_write_burst_length: バースト転送中に書き込まれるデータ値の最大数を指定します。
• num_write_outstanding: デザインが停止する前に、AXI4 バスに対して応答なしで送信できる書き込み要求の 数を指定します。これにより、デザインの内部ストレージである FIFO のサイズ
(num_read_outstanding*max_read_burst_length*word_size) が決まります。
次に、これらのオプションを使用した例を示します。
#pragma HLS interface m_axi port=input offset=slave bundle=gmem0 depth=1024*1024*16/(512/8)
latency=100
num_read_outstanding=32 num_write_outstanding=32 max_read_burst_length=16 max_write_burst_length=16
インターフェイスは、レイテンシ 100 になるように指定されています。Vitis HLS では、デザインが AXI4 バスにアク セスできるようになる 100 クロック サイクル前に、バースト アクセス要求をスケジューリングしようとします。バ スの効率をさらに向上するには、num_write_outstanding および num_read_outstanding オプションを使用 して、デザインに最大 32 個の読み出しおよび書き込みアクセスを格納できるバッファー容量が含まれるようにしま す。これで、バス要求が送信されるまでデザインの処理を続行できるようになります。最後に、
max_read_burst_length および max_write_burst_length オプションを使用することで、最大バースト サイ ズが 16 になり、AXI4 インターフェイスがそれを超える時間バスを保持しないようにします。
これらのオプションを使用すると、AXI4 インターフェイスの動作をそれが実行されるシステム用に最適化できます。
操作の効率は、これらの値が正しく設定されているかどうかによって異なります。
32 ビット アドレス機能のある AXI4 インターフェイスを作成
Vitis HLS では、デフォルトで 64 ビット バスの AXI4 ポートがインプリメントされますが、次のように
m_axi_addr64 インターフェイス コンフィギュレーション オプションをディスエーブルにすると、32 ビット アド レス バスの AXI4 インターフェイスをインプリメントできます。
1. [Solution] → [Solution Settings] をクリックします。
2. [Solution Settings] ダイアログ ボックスで [General] カテゴリをクリックし、[Edit] で既存の config_interface コマンドを変更するか、[Add] で追加します。
3. [Edit] または [Add] ダイアログ ボックスで [config_interface] を選択して [m_axi_addr64] をオフにします。
重要: [m_axi_addr64] を選択する場合、Vitis HLS は 32 ビットアドレスバスでデザインにすべての AXI4 インターフ
ェイスをインプリメントします。
AXI4 インターフェイスのアドレス オフセットの制御
デフォルトでは、AXI4 マスター インターフェイスはすべての 読み出しおよび書き込み操作をアドレス 0x00000000 から開始します。たとえば、次のコードの場合、50 個のアドレス値を表すアドレス 0x00000000 ~ 0x000000c7 (32 ビット ワード 50 個で 200 バイトを提供) からデータを読み出してから、同じアドレスにデータを再び書き込みます。
void example(volatile int *a){
#pragma HLS INTERFACE m_axi depth=50 port=a
#pragma HLS INTERFACE s_axilite port=return bundle=AXILiteS int i;
int buff[50];
memcpy(buff,(const int*)a,50*sizeof(int));