Vitis HLS ライブラリ リファレンス

Vitis HLS には次の基本的な C ライブラリが含まれます。
  • 任意精度型ライブラリ: 任意精度型は、ハードウェアのパフォーマンスおよびエリアを向上するため、C コードでビット幅の狭い変数を使用できるようにします。
  • Vitis HLS math ライブラリ: ザイリンクス デバイスに合成するための標準数学演算を指定します。
  • HLS ストリーム ライブラリ: ストリーミング データ構造をモデリングおよびコンパイルするためのライブラリ。
  • HLS IP ライブラリ: FFT (Fast Fourier Transform) および FIR (Finite Impulse Response) を含む IP ファンクション

これらの C ライブラリをデザインでしようするには、こーどのライブラリ ヘッダー ファイルを含めます。これらのヘッダー ファイルは、Vitis HLS のインストール ディレクトリ内の include ディレクトリにあります。

重要: Vitis HLS C ライブラリのヘッダー ファイルは、デザインが Vitis HLS で使用される場合はインクルード パスに含める必要はありません。ライブラリ ヘッダー ファイルへのパスは、自動的に追加されます。

任意精度型ライブラリ

ネイティブ C データ型は、8 ビット境界 (8、16、32、64 ビット) にあります。RTL バス (ハードウェアに対応) では、任意の幅がサポートされます。HLS では、任意精度のビット幅の仕様を使用できるようにし、ネイティブ C データ型の制限に依存ないようにするメカニズムが必要です。たとえば、17 ビット乗算器が必要な場合に、32 ビット乗算器にインプリメントしなければならないような制限はあるべきではありません。

Vitis HLS では、C++ の整数および固定小数点の任意精度型が提供されています。任意精度型の利点は、C コードをビット幅の狭い変数を使用するようアップデートしてから、C シミュレーションを再実行して機能が同一または使用可能であることを検証できる点です。

任意精度型の使用

Vitis HLS では、次の表に示すように任意精度の整数型が提供されており、指定した幅の境界内で整数値を管理できるようになっています。

表 1. 任意精度型
言語 整数型 必要なヘッダー
C++

ap_[u]int<W> (1024 ビット)

32K ビット幅までに拡張可能。

#include "ap_int.h"
C++ ap_[u]fixed<W,I,Q,O,N> #include "ap_fixed.h"

Vitis HLS には、任意精度型を定義するヘッダー ファイルもスタンドアロン パッケージとして含まれており、ソース コードで使用できるようになっています。xilinx_hls_lib_<release_number>.tgz パッケージは、Vitis HLS インストール ディレクトリの include ディレクトリに含まれます。

C++ 言語での整数任意精度型

ヘッダー ファイル ap_int.h では、C++ の任意精度整数型の ap_[u]int 型が定義されます。C++ 関数で任意精度の整数型を使用するには、次の手順に従います。

  • ソース コードにヘッダー ファイル ap_int.h を追加します。
  • ビット型を、符号付きの型の場合は ap_int<N>、符号なしの型の場合は ap_uint<N> (N はビット サイズを表す 1 ~ 1024 の値) に変更します。

次の例に、ヘッダー ファイルの追加方法と、2 つの変数を 9 ビット整数型および 10 ビットの符号なし整数型を使用してインプリメントする方法を示します。


#include "ap_int.h"

void foo_top (…) {
  
 ap_int<9>  var1;           // 9-bit
 ap_uint<10>  var2;         // 10-bit unsigned

任意精度固定小数点型

Vitis HLS では、固定小数点型を使用することが重要です。これは、固定小数点型を使用して実行された C++ シミュレーションの動作が、合成で作成したハードウェアの結果と同じになるからです。これにより、ビット精度、量子化、およびオーバーフローの影響を高速の C レベル シミュレーションで解析できるようになります。

これらのデータ型では、次の図に示すように、指定の合計幅および整数幅の境界内で実数 (整数以外) の値が管理されます。

1: 固定小数点型


固定小数点の識別子のまとめ

次の表に、固定小数点型でサポートされる演算の概要を示します。

表 2. 固定小数点の識別子のまとめ
識別子 説明

W

ワード長のビット数

I

整数値のビット数 (整数部のビット数)
Q 量子化モード: 結果の保存に使用される変数の最小の小数ビットで定義できるよりも高い精度が生成された場合の動作を指定します。
ap_fixed 型 説明
AP_RND 正の無限大への丸め
AP_RND_ZERO 0 への丸め
AP_RND_MIN_INF 負の無限大への丸め
AP_RND_INF 無限大への丸め
AP_RND_CONV 収束丸め
AP_TRN 負の無限大への切り捨て (デフォルト)
AP_TRN_ZERO 0 への切り捨て
O

Overflow mode: 演算結果が結果を格納するのに使用される変数に格納可能な最大値 (負の値の場合は最小値) を超えた場合の動作を指定します。

ap_fixed 型 説明
AP_SAT 飽和
AP_SAT_ZERO 0 への飽和
AP_SAT_SYM 対称飽和
AP_WRAP 折り返し (デフォルト)
AP_WRAP_SM 符号絶対値のラップ
N オーバーフロー折り返しモードの場合の飽和ビット数を定義します。
ap_fixed を使用した例

次の例では、Vitis HLS の ap_fixed 型を使用して、18 ビット変数 (6 ビットが整数部、12 ビットが小数部を表す) を定義しています。変数は符号付きで指定されており、量子化モードは正の無限大へ丸められるように設定され、オーバーフローにはデフォルトの折り返しモードが使用されます。

#include <ap_fixed.h>
...
ap_fixed<18,6,AP_RND > my_type;
...

C++ の任意精度整数型

C++ のネイティブ データ型は、8 ビット境界 (8、16、32、64 ビット) にあります。RTL 信号および演算では、任意のビット長がサポートされます。

Vitis HLS では C++ の任意精度型が提供されており、C コードの変数および演算を任意のビット幅 (6 ビット、17 ビット、234 ビットなど、最大 1024 ビットまで) で指定できます。

ヒント: デフォルトの幅の最大許容幅は 1024 ビットです。このデフォルトは、ap_int.h ヘッダー ファイルを含める前に、マクロ AP_INT_MAX_W に 32768 以下の正の整数値を定義すると上書きできます。

任意精度型には、ネイティブ C++ データ型よりも次のような利点があります。

  • ハードウェアの質の改善: たとえば 17 ビットの乗算器が必要な場合、計算で調度 17 ビットが使用されるように指定するために任意精度型を使用できます。

    任意精度型を使用しない場合、このような乗算 (17 ビット) は 32 ビットの整数型を使用してインプリメントする必要があるので、複数の DSP モジュールを使用して乗算がインプリメントされることになります。

  • 正確な C++ シミュレーション/解析: C++ コードの任意精度型を使用すると、正確なビット幅を使用して C++ シミュレーションを実行でき、合成前にアルゴリズムの機能 (および精度) を検証できます。

C++ の任意精度型には、C の任意精度型で発生するような問題はありません。

  • C++ 任意精度型は、標準 C++ コンパイラでコンパイルできます。apcc と同等のものは C++ にはありません。
  • C++ 任意精度型には、整数拡張問題はありません。

ファイル拡張子が .c から .cpp に変更されることはよくあるので、これらの問題がない場合はファイルを C++ としてコンパイルできます。

C++ 言語では、ヘッダー ファイル ap_int.h で任意精度の整数型 ap_(u)int<W> が定義されます。たとえば、ap_int<8> は 8 ビットの符号付き整数型を表し、ap_uint<234> は 234 ビットの符号なし整数型を表します。

ap_int.h ファイルは、$HLS_ROOT/include ディレクトリ ($HLS_ROOTVitis HLS のインストール ディレクトリ) にあります。

次のコード例は、基本演算例 (標準データ型) のコードを繰り返したものです。この例で合成される最上位関数のデータ型は dinA_tdinB_t などです。


#include "cpp_ap_int_arith.h"

void cpp_ap_int_arith(din_A  inA, din_B  inB, din_C  inC, din_D  inD,
 dout_1 *out1, dout_2 *out2, dout_3 *out3, dout_4 *out4
) {

 // Basic arithmetic operations
 *out1 = inA * inB;
 *out2 = inB + inA;
 *out3 = inC / inA;
 *out4 = inD % inA;

}

前の例をアップデートしたこの例では、C++ 任意精度型が使用されています。

  • ソース コードにヘッダー ファイル ap_int.h を追加します。
  • ネイティブ C++ 型を任意精度型 ap_int<N> または ap_uint<N> に変更します。N は 1 ~ 1024 のビット サイズです。前述のとおり、これは必要に応じて 32 ビットに拡張できます。

データ型はヘッダー ファイル cpp_ap_int_arith.h で定義されます。

標準データ型 の基本演算の例と比較すると、入力のデータ型が実際の入力データの最大サイズを表すよう削減されています (8 ビット入力 inA が 6 ビット入力に削減されているなど)。出力はより正確になるよう調整されています。たとえば inAinB の合計である out2 に必要なのは、32 ビットではなく 13 ビットのみです。

次は、C++ 任意精度型を使用した基本演算を示す例です。


#ifndef _CPP_AP_INT_ARITH_H_
#define _CPP_AP_INT_ARITH_H_

#include <stdio.h>
#include "ap_int.h"

#define N 9

// Old data types
//typedef char dinA_t;
//typedef short dinB_t;
//typedef int dinC_t;
//typedef long long dinD_t;
//typedef int dout1_t;
//typedef unsigned int dout2_t;
//typedef int32_t dout3_t;
//typedef int64_t dout4_t;

typedef ap_int<6> dinA_t;
typedef ap_int<12> dinB_t;
typedef ap_int<22> dinC_t;
typedef ap_int<33> dinD_t;

typedef ap_int<18> dout1_t;
typedef ap_uint<13> dout2_t;
typedef ap_int<22> dout3_t;
typedef ap_int<6> dout4_t;

void cpp_ap_int_arith(dinA_t inA,dinB_t inB,dinC_t inC,dinD_t inD,dout1_t 
*out1,dout2_t *out2,dout3_t *out3,dout4_t *out4);

#endif

C++ の任意精度整数型 が合成されると、標準データ型 と同じ機能を持つデザインになります。cout 演算子を使用して結果をファイルに出力するのではなく、ビルトインの ap_int メソッド .to_int() を使用して、ap_int の結果を標準 fprintf 関数で使用される整数型に変換します。


fprintf(fp, %d*%d=%d; %d+%d=%d; %d/%d=%d; %d mod %d=%d;\n, 
 inA.to_int(), inB.to_int(), out1.to_int(), 
 inB.to_int(), inA.to_int(), out2.to_int(), 
 inC.to_int(), inA.to_int(), out3.to_int(), 
 inD.to_int(), inA.to_int(), out4.to_int());

C++ の任意精度整数型: リファレンス情報

メソッド、合成動作、ap_(u)int<N> 任意精度小数点型を使用する場合の詳細は、C++ の任意精度型 を参照してください。このセクションには、次の項目が含まれます。

  • 任意精度の整数に (1024 ビットを超える値も含めて) 定数および初期値を代入する方法。
  • 表示、連結、ビット スライス、範囲選択などの Vitis HLS のヘルパー メソッドの詳細。
  • シフト演算 (シフト値が負の場合は反対方向にシフト) の記述も含めた演算子の動作の詳細。
C++ の任意精度型

Vitis HLS では、ソフトウェアとハードウェア モデリング間での一貫した、ビット精度の動作で、任意精度 (またはビット精度) の整数型をインプリメントする ap_[u]int<> という C++ のテンプレート クラスが提供されています。

このクラスは、ネイティブ C 整数型で使用可能なすべての四則演算、ビット単位、論理、関係演算子を提供しています。さらに、64 ビットよりも幅が広い変数の初期化および変換を可能にするハードウェア演算の一部を処理するためのメソッドも、このクラスで提供しています。演算子およびクラス メソッドの詳細は次を参照してください。

ap_[u]int<> 型のコンパイル

ap_[u]int<> クラスを使用するには、ap_[u]int<> を参照するソース ファイルすべてに ap_int.h ヘッダー ファイルを含める必要があります。

これらのクラスを使用するソフトウェア モデルをコンパイルする際は、g++ コンパイルに -I/<HLS_HOME>/include オプションを追加するなど、Vitis HLS のヘッダー ファイルの場所を指定する必要がある場合があります。

ap_[u] 変数の宣言/定義

次のようにそれぞれ符号付きおよび符号なしのクラスが別々にあります。

  • ap_int<int_W> (符号付き)
  • ap_uint<int_W> (符号なし)

テンプレート パラメーターの int_W は宣言されている変数の合計幅を指定します。

次の例にあるように、C/C++ の typedef 文を使用してユーザー定義型を作成できます。


include "ap_int.h"// use ap_[u]fixed<> types

typedef ap_uint<128> uint128_t; // 128-bit user defined type
ap_int<96> my_wide_var; // a global variable declaration

デフォルトの幅の最大許容幅は 1024 ビットです。このデフォルトは、ap_int.h ヘッダー ファイルを含める前に、32768 以下の正の整数値でマクロ AP_INT_MAX_W を定義すると上書きできます。

注意: AP_INT_MAX_W の値をあまり高く設定すると、ソフトウェアのコンパイルおよび実行に時間がかかる可能性があります。

次は AP_INT_MAX_W を上書きする例です。


#define AP_INT_MAX_W 4096 // Must be defined before next line
#include "ap_int.h"

ap_int<4096> very_wide_var;
定数 (リテラル) からの初期化および代入

クラス コンストラクターおよび代入演算子のオーバーロードを利用し、標準 C/C++ の整数リテラルを使用して ap_[u]fixed<> 変数を初期化および代入できます。

ただし、この ap_[u]fixed<> 変数への値の代入方法は、C++ およびソフトウェアが実行されるシステムの制限の対象となるので、通常、整数リテラルの 64 ビット制限 (LL または ULL 接頭辞など) の影響を受けます。

ap_[u]fixed<> クラスでは、任意の長さ (変数の幅以下のもの) の文字列から初期化が可能なコンストラクターが提供されています。

デフォルトでは、文字列に有効な 16 進数 (0 から 9 までの数字および a から f の文字) のみが含まれている限り、文字列は 16 進数値として処理されます。そのような文字列から値を代入するには、文字列を該当する型へと、明示的に C++ 形式の型変換をする必要があります。

64 ビットよりも大きな値を含む初期化および代入の例は次のとおりです。


ap_int<42> a_42b_var(-1424692392255LL); // long long decimal format
a_42b_var = 0x14BB648B13FLL; // hexadecimal format

a_42b_var = -1; // negative int literal sign-extended to full width

ap_uint<96> wide_var(“76543210fedcba9876543210”, 16); // Greater than 64-bit
wide_var = ap_int<96>(“0123456789abcdef01234567”, 16);
注記: 協調シミュレーション中に予期しない結果にならないようにするには、ap_uint<N> a ={0} は初期化しないようにしてください。

ap_[u]<> コンストラクターは、基数 2、8、10、または 16 で数値を表わして文字を処理するように明示的に指定できます。コンストラクターの呼び出しに 2 番目のパラメーターとして該当する基数値を追加すると、この設定ができます。

指定されている基数に対して無効な文字が文字リテラルに含まれていると、コンパイル エラーが発生します。

それぞれの基数フォーマットの例は次のとおりです。


ap_int<6> a_6bit_var(“101010”, 2); // 42d in binary format
a_6bit_var = ap_int<6>(“40”, 8); // 32d in octal format
a_6bit_var = ap_int<6>(“55”, 10); // decimal format
a_6bit_var = ap_int<6>(“2A”, 16); // 42d in hexadecimal format

a_6bit_var = ap_int<6>(“42”, 2);   // COMPILE-TIME ERROR! “42” is not binary

文字列でエンコードされている基数は、「0」の後に「b」、「o」、または「x」を続けた接頭辞を付けると、コンストラクターで自動推論することもできます。「0b」、「0o」、および「0x」という接頭辞はそれぞれ 2 進数、8 進数、16 進数のフォーマットに対応しています。

この初期化文字列フォーマットを使用した例は次のとおりです。


ap_int<6> a_6bit_var(“0b101010”, 2); // 42d in binary format
a_6bit_var = ap_int<6>(“0o40”, 8); // 32d in octal format
a_6bit_var = ap_int<6>(“0x2A”, 16); // 42d in hexidecimal format

a_6bit_var = ap_int<6>(“0b42”, 2); // COMPILE-TIME ERROR! “42” is not binary

ビット幅が 53 ビットを超える場合、ap_[u]fixed 値は次のように文字列で初期化する必要があります。


       ap_ufixed<72,10> Val(“2460508560057040035.375”);
コンソール I/O (出力) のサポート

Vitis HLS には、ap_[u]fixed<> 変数の初期化および代入の場合と同様に、64 ビットよりも大きな値の出力をサポートするための機能が提供されています。

C++ の標準出力ストリームの使用

ap_[u]int 変数に格納されている値を出力するのに最も簡単な方法は、次の C++ の標準出力ストリームのいずれかを使用する方法です。

std::cout (#include <iostream> または <iostream.h>)

ストリーム挿入演算子 << は、任意の ap_[u]fixed 変数に対する範囲全体に含まれる値が正しく出力されるように、オーバーロードされます。次のストリーム マニピュレーターもサポートされています。

  • dec (10 進数)
  • hex (16 進数)
  • oct (8 進数)

これにより、値を指定どおりにフォーマットできます。

次の例では、値を出力するのに cout が使用されています。


#include <iostream.h>
// Alternative: #include <iostream>

ap_ufixed<72> Val(“10fedcba9876543210”);

cout << Val << endl; // Yields: “313512663723845890576”
cout << hex << val << endl; // Yields: “10fedcba9876543210”
cout << oct << val << endl; // Yields: “41773345651416625031020”
標準 C ライブラリの使用

標準 C ライブラリ (#include <stdio.h>) を使用して、64 ビットよりも大きな値を出力することもできます。

  1. ap_[u]fixed クラスの to_string() メソッドを使用し、値を C++ の std::string に変換します。
  2. その結果を std::string クラスの c_str() メソッドを使用して、ヌルで終了する C 文字に変換します。
オプションの引数 1 (基数の指定)

ap[u]int::to_string() メソッドでは目的の基数を指定するオプションの引数を渡すことができます。有効な基数の引数値は、次のとおりです。

  • 2 (2 進数) (デフォルト)
  • 8 (8 進数)
  • 10 (10 進数)
  • 16 (16 進数)
オプションの引数 2 (符号付きの値として出力)

ap_[u]int::to_string() の 2 つ目のオプションの引数は、10 進数以外のフォーマットで符号付きの値を出力するかどうかを指定します。この引数はブール形式です。デフォルト値は false で、10 進数以外のフォーマットで符号なしの値が出力されます。

次の例では、値を出力するのに printf が使用されています。


ap_int<72> Val(“80fedcba9876543210”);

printf(“%s\n”, Val.to_string().c_str()); // => “80FEDCBA9876543210”
printf(“%s\n”, Val.to_string(10).c_str()); // => “-2342818482890329542128”
printf(“%s\n”, Val.to_string(8).c_str()); // => “401773345651416625031020” 
printf(“%s\n”, Val.to_string(16, true).c_str()); // => “-7F0123456789ABCDF0”
ap_[u]<> 型を使用した式

ap_[u]<> 型の変数は、通常 C/C++ 演算子を使用した式では自由に使用できます。ただし、一部には予期しない動作が見られることがあります。これについては、次を参照してください。

ビット幅が小さい値を幅が大きいものへ代入する場合のゼロまたは符号拡張

ビット幅が小さい符号付き変数 (ap_int<>) の値を幅の大きなものへ代入すると、符号にかかわらず、値はデスティネーション変数の幅 (大きいほうの幅) に符号拡張されます。

同様に、符号なしのビット幅の小さい変数は代入前にゼロ拡張されます。

代入で予期動作を得るには、ソース変数の明示的な型変換が必要になることがあります。次に例を示します。


ap_uint<10> Result;

ap_int<7> Val1 = 0x7f;
ap_uint<6> Val2 = 0x3f;

Result = Val1; // Yields: 0x3ff (sign-extended)
Result = Val2; // Yields: 0x03f (zero-padded)

Result = ap_uint<7>(Val1); // Yields: 0x07f (zero-padded)
Result = ap_int<6>(Val2); // Yields: 0x3ff (sign-extended)
ビット幅が大きい値を幅が小さいものへ代入する場合の切り捨て

ビット幅が大きいソース変数値を幅の小さなものへ代入すると、値が切り捨てられ、デスティネーション値 (幅の小さいほうの値) の最上位ビット (MSB) を超えたビットはすべて失われます。

この切り捨てが実行されるとき、符号情報が特別に処理されるわけではないので、予期しない動作が見られる可能性があります。予期しない動作を防ぐには、明示的な型変換を利用します。

クラス メソッドおよび演算子

ap_[u]int 型では、wide ap_[u]int (> 64 ビット) からビルトイン C/C++ 整数型への暗示的な変換はサポートされません。たとえば次のコード例の場合、if 文内で ap_int[65] から bool 型への暗示的な型変換により 0 が返されるので、戻り値は 1 になります。

   bool nonzero(ap_uint<65> data) {
      return data; // This leads to implicit truncation to 64b int
    }

   int main() {
     if (nonzero((ap_uint<65>)1 << 64)) {
        return 0;
     }
     printf(FAIL\n);
     return 1;
   }

幅の広い ap_[u]int 型をビルトインの整数に変換するには、ap_[u]int に含まれる次の明示的な変換関数を使用します。

  • to_int()
  • to_long()
  • to_bool()

通常、ネイティブの C/C++ 整数型で実行可能な演算は、ap_[u]int 型で演算子をオーバーロードするとサポートされます。

オーバーロードされた演算子に加え、一部の特定演算子およびメソッドを使用してビット レベルの演算を簡単に実行できます。

2 進数の演算子

任意精度演算を実行するため、標準 2 進数整数演算子がオーバーロードされます。次の演算子では次のいずれかが使用されます。

  • ap_[u]int の 2 つのオペランド
  • ap_[u]int 型 1 つと C/C++ 整数型 1 つ

次に例を示します。

  • char
  • short
  • int

結果値の幅および符号は、デスティネーション変数 (または式) の幅に基づいて符号拡張、ゼロの追加、または切り捨てが実行される前の、オペランドの幅および符号で決まります。戻り値の詳細は、各演算子のセクションで説明します。

式に ap_[u]int 型および C/C++ 整数型の両方が含まれている場合、C++ 型では次の幅が使用されます。

  • char (8 ビット)
  • short (16 ビット)
  • int (32 ビット)
  • long (32 ビット)
  • long long (64 ビット)
加算
ap_(u)int::RType ap_(u)int::operator + (ap_(u)int op)

次の和が返されます。

  • 2 つの ap_[u]int、または
  • 1 つの ap_[u]int および C/C++ 整数型

和の幅は、次のようになります。

  • 2 つのオペランドの幅の広い方に 1 ビット追加。
  • 幅の広い方が符号なしで幅の狭い方が符号付きの場合は 2 ビット追加。

オペランドのいずれか (または両方) が符号付きの場合、和は符号付きとして処理。

減算
ap_(u)int::RType ap_(u)int::operator - (ap_(u)int op)

2 つの整数の差を計算する式です。

差の値の幅は、次のようになります。

  • 2 つのオペランドの幅の広い方に 1 ビット追加。
  • 幅の広い方が符号なしで幅の狭い方が符号付きの場合は 2 ビット追加。

代入前に、デスティネーション変数の幅に基づいて、符号拡張、ゼロの追加、または切り捨てが実行されます。

オペランドの符号にかかわらず、差は符号付きとして処理されます。

乗算
ap_(u)int::RType ap_(u)int::operator * (ap_(u)int op)

2 つの整数値の積を返します。

積の幅はオペランドの幅の合計です。

オペランドのいずれかが符号付きであれば、積は符号付きとして処理されます。

除算
ap_(u)int::RType ap_(u)int::operator / (ap_(u)int op)

2 つの整数値の商を計算します。

除数が符号なしである場合、商の幅は被除数の幅となり、そうでない場合は、商の幅は被除数の幅に 1 を足したものの幅となります。

オペランドのいずれかが符号付きであれば、商は符号付きとして処理されます。

剰余
ap_(u)int::RType ap_(u)int::operator % (ap_(u)int op)

2 つの整数値の整数除算の余りを返します。

剰余とオペランド両方に同じ符号が付いている場合、剰余の幅は、オペランドの幅の最小値です。

オペランドがどちらも同じ符号である場合は、剰余の幅はオペランドの幅の最小値となります。

除数が符号なし、被除数が符号付きの場合は、剰余の幅は除数の幅に 1 を足したものとなります。

重要: 剰余 (%) 演算子を Vitis HLS で合成すると、生成された RTL で適宜パラメーター設定されたザイリンクス LogiCORE 除算コアがインスタンシエートされます。

次は、演算子の例です。


ap_uint<71> Rslt;

ap_uint<42> Val1 = 5;
ap_int<23> Val2 = -8;

Rslt = Val1 + Val2; // Yields: -3 (43 bits) sign-extended to 71 bits
Rslt = Val1 - Val2; // Yields: +3 sign extended to 71 bits
Rslt = Val1 * Val2; // Yields: -40 (65 bits) sign extended to 71 bits
Rslt = 50 / Val2; // Yields: -6 (33 bits) sign extended to 71 bits
Rslt = 50 % Val2; // Yields: +2 (23 bits) sign extended to 71 bits
ビット単位の論理演算子

ビット単位の論理演算子はすべて、2 つのオペランドの幅の最大値となる幅の値を返し、両方のオペランドが符号なしの場合にのみ符号なしとして処理されます。そうでない場合は、符号付きとして処理されます。

符号拡張 (またはゼロの追加) は、デスティネーション変数ではなく、式の符号に基づいて実行されます。

ビット単位 OR
ap_(u)int::RType ap_(u)int::operator | (ap_(u)int op)

2 つのオペランドのビット単位の OR 値を返します。

ビット単位 AND (bitwise_and)
ap_(u)int::RType ap_(u)int::operator & (ap_(u)int op)

2 つのオペランドのビット単位の AND 値を返します。

ビット単位 XOR
ap_(u)int::RType ap_(u)int::operator ^ (ap_(u)int op)

2 つのオペランドのビット単位の XOR 値を返します。

単項演算子
加算
ap_(u)int ap_(u)int::operator + ()

ap_[u]int オペランドのセルフ コピーを返します。

減算
ap_(u)int::RType ap_(u)int::operator - ()

次が返されます。

  • 符号付きの場合は同じ幅でオペランドのネゲートされた値。
  • 符号なしの場合はその幅に 1 を足した値。

戻り値は常に符号付きです。

ビット単位の逆
ap_(u)int::RType ap_(u)int::operator ~ ()

同じ幅および符号の、オペランドのビット単位の NOT を返します。

論理の反転
bool ap_(u)int::operator ! ()

オペランドが 0 ではない場合にブール値 false を返します。

オペランドが 0 の場合は true を返します。

三項演算子

標準 C int 型で三項演算子を使用する場合は、1 つのデータ型から別のデータ型に明示的に変換して、両方の結果が同じデータ型になるようにします。次に例を示します。

// Integer type is cast to ap_int type
ap_int<32> testc3(int a, ap_int<32> b, ap_int<32> c, bool d) {
 return d?ap_int<32>(a):b;
}
// ap_int type is cast to an integer type
ap_int<32> testc4(int a, ap_int<32> b, ap_int<32> c, bool d) {
 return d?a+1:(int)b;
}
// Integer type is cast to ap_int type
ap_int<32> testc5(int a, ap_int<32> b, ap_int<32> c, bool d) {
 return d?ap_int<33>(a):b+1;
}
シフト演算子

各シフト演算子には次の 2 つのバージョンがあります。

  • 符号なしの右辺 (RHS) オペランド
  • 符号付きの右辺 (RHS) オペランド

符号付き RHS に負の値が与えられるとシフト演算の方向が逆になります。たとえば、RHS オペランドの絶対値によるシフトが逆の方向で発生します。

シフト演算子は、左辺 (LHS) オペランドと同じ幅の値を返します。C/C++ の場合と同じように、右シフトの LHS オペランドが符号付きの場合、符号ビットは、LHS オペランドの符号を維持しつつ、最上位ビットにコピーされます。

符号なし整数右シフト
ap_(u)int ap_(u)int::operator << (ap_uint<int_W2> op)
整数右シフト
ap_(u)int ap_(u)int::operator << (ap_int<int_W2> op)
符号なし整数左シフト
ap_(u)int ap_(u)int::operator >> (ap_uint<int_W2> op)
整数左シフト
ap_(u)int ap_(u)int::operator >> (ap_int<int_W2> op)

注意: 左シフト演算子の結果を幅が広い方のデスティネーション変数に代入すると、情報の一部またはすべてが失われる場合がありますので注意してください。ザイリンクスでは、予期しない動作を防ぐため、シフト式を代入される方の型に明示的に型変換することをお勧めしています。

次は、シフト演算の例です。

ap_uint<13> Rslt;

ap_uint<7> Val1 = 0x41;

Rslt = Val1 << 6;  // Yields: 0x0040, i.e. msb of Val1 is lost
Rslt = ap_uint<13>(Val1) << 6;  // Yields: 0x1040, no info lost

ap_int<7> Val2 = -63;
Rslt = Val2 >> 4;  //Yields: 0x1ffc, sign is maintained and extended
複合代入演算子

Vitis HLS では、次の複合演算子がサポートされています。

  • *=
  • /=
  • %=
  • +=
  • -=
  • <<=
  • >>=
  • &=
  • ^=
  • |=

RHS 式はまず評価されてから RHS オペランドとして基本演算子に供給され、結果が LHS 変数に代入されます。式の長さ、符号、符号拡張か切り捨てかのルールは、関連する演算に対し上記で説明されているように、適用されます。

ap_uint<10> Val1 = 630;
ap_int<3> Val2 = -3;
ap_uint<5> Val3 = 27;

Val1 += Val2 - Val3; // Yields: 600 and is equivalent to:

// Val1 = ap_uint<10>(ap_int<11>(Val1) +
// ap_int<11>((ap_int<6>(Val2) -
// ap_int<6>(Val3))));
インクリメントおよびデクリメント演算子

インクリメントおよびデクリメントの演算子が提供されています。オペランドと同じ幅の値を返しますが、両方のオペランドが符号なしの場合のみ値は符号なしで、そうでない場合は、符号付きです。

プレインクリメント
ap_(u)int& ap_(u)int::operator ++ ()

オペランドのインクリメントされた値を返します。

そのインクリメントされた値をオペランドに代入します。

ポストインクリメント
const ap_(u)int ap_(u)int::operator ++ (int)

インクリメントされた値をオペランド変数に代入する前に、オペランドの値を返します。

プレデクリメント
ap_(u)int& ap_(u)int::operator -- ()

オペランドのデクリメントされた値を返し、その値をオペランドに代入します。

ポストデクリメント
const ap_(u)int ap_(u)int::operator -- (int)

デクリメントされた値をオペランド変数に代入する前に、オペランドの値を返します。

関係演算子

Vitis HLS では、すべての関係演算子がサポートされており、比較結果に基づいてブール値が返されます。ap_[u]int 型の変数は、これらの演算子を使用して C/C++ 基本整数型と比較できます。

等号
bool ap_(u)int::operator == (ap_(u)int op)
等号否定
bool ap_(u)int::operator != (ap_(u)int op)
小なり (<)
bool ap_(u)int::operator < (ap_(u)int op)
大なり (>)
bool ap_(u)int::operator > (ap_(u)int op)
以下
bool ap_(u)int::operator <= (ap_(u)int op)
以上
bool ap_(u)int::operator >= (ap_(u)int op)
その他のクラス メソッド、演算子、データ メンバー

次のセクションでは、その他のクラス メソッド、演算子、およびデータ メンバーについて説明します。

ビット レベル演算

ap_[u]int 型変数に格納されている値に基づいて一般的なビット レベル演算を実行するために、次のメソッドが提供されています。

長さ

int ap_(u)int::length ()

ap_[u]int 変数の合計ビット数を表す整数値を返します。

連結

ap_concat_ref ap_(u)int::concat (ap_(u)int low)  
ap_concat_ref ap_(u)int::operator , (ap_(u)int high, ap_(u)int low)

2 つの ap_[u]int 変数を連結します。返された値の幅はオペランドの幅の和です。

high および low の引数にはそれぞれ結果値の高位ビットまたは下位ビットが出力されます。concat() メソッドの場合は下位ビットが出力されます。

オーバーロードされたカンマ演算子を使用するときはかっこが必要です。カンマ演算子バージョンが代入の LHS に現れることもあります。

注記: 整数リテラルを含むネイティブ C 型は、予期しない結果を避けるため、連結前に該当する ap_[u]int 型に明示的に型変換します。

ap_uint<10> Rslt;

ap_int<3> Val1 = -3;
ap_int<7> Val2 = 54;

Rslt = (Val2, Val1); // Yields: 0x1B5
Rslt = Val1.concat(Val2); // Yields: 0x2B6
(Val1, Val2) = 0xAB; // Yields: Val1 == 1, Val2 == 43
ビット選択

ap_bit_ref ap_(u)int::operator [] (int bit)

任意精度の整数値から 1 ビットを選択し、それを返します。

戻り値は、ap_[u]int 変数の対応するビットをセットまたはクリアできる参照値です。

ビット引数は int 値にする必要があります。選択するビットの指数を指定します。最下位ビットの指数は 0 です。最大指数はこの ap_[u]int のビット幅から 1 を引いた値になります。

ap_bit_ref はビットで指定されているこの ap_[u]int インスタンスの 1 ビットへの参照を表しています。

範囲選択

ap_range_ref ap_(u)int::range (unsigned Hi, unsigned Lo)
ap_range_ref ap_(u)int::operator () (unsigned Hi, unsigned Lo)

引数で指定されるビットの範囲で表される値を返します。

引数 Hi は、範囲内の最上位ビット (MSB) を、Lo は最下位ビット (LSB) を指定します。

ソース変数の LSB は 0 の位置にあります。引数 Hi の値が Lo の値より小さい場合、ビットは逆順で返されます。


ap_uint<4> Rslt;

ap_uint<8> Val1 = 0x5f;
ap_uint<8> Val2 = 0xaa;

Rslt = Val1.range(3, 0); // Yields: 0xF
Val1(3,0) = Val2(3, 0); // Yields: 0x5A
Val1(3,0) = Val2(4, 1); // Yields: 0x55
Rslt = Val1.range(4, 7); // Yields: 0xA; bit-reversed!
AND を使用した低減

bool ap_(u)int::and_reduce ()
  • この ap_(u)int のビットすべてに AND 演算を適用します。
  • 結果の単一ビットを返します。
  • -1 (すべて 1) に対してこの値を比較することと同じで、一致すれば true が返され、一致しない場合は false が返されます。
OR を使用した低減

bool ap_(u)int::or_reduce ()
  • この ap_(u)int のビットすべてに OR 演算を適用します。
  • 結果の単一ビットを返します。
  • 0 (すべて 0) に対してこの値を比較することと同じで、一致すれば false が返され、一致しない場合は true が返されます。
XOR を使用した低減

bool ap_(u)int::xor_reduce ()
  • この ap_int のビットすべてに XOR 演算を適用します。
  • 結果の単一ビットを返します。
  • この値の 1 のビット数を数えて、偶数の場合は false、奇数の場合は true を返すことと同じです。
NAND を使用した低減

bool ap_(u)int::nand_reduce ()
  • この ap_int のビットすべてに NAND 演算を適用します。
  • 結果の単一ビットを返します。
  • -1 (すべて 1) に対してこの値を比較することと同じで、一致すれば false が返され、一致しない場合は true が返されます。
NOR を使用した低減

bool ap_int::nor_reduce ()
  • この ap_int のビットすべてに NOR 演算を適用します。
  • 結果の単一ビットを返します。
  • 0 (すべて 0) に対してこの値を比較することと同じで、一致すれば true が返され、一致しない場合は false が返されます。
XNOR を使用した低減

bool ap_(u)int::xnor_reduce ()
  • この ap_(u)int のビットすべてに XNOR 演算を適用します。
  • 結果の単一ビットを返します。
  • この値の 1 のビット数を数えて、偶数の場合は true、奇数の場合は false を返すことと同じです。
ビット低減メソッドの例

ap_uint<8> Val = 0xaa;

bool t = Val.and_reduce(); // Yields: false
t = Val.or_reduce();       // Yields: true
t = Val.xor_reduce();      // Yields: false
t = Val.nand_reduce();     // Yields: true
t = Val.nor_reduce();      // Yields: false
t = Val.xnor_reduce();     // Yields: true
ビットのリバース

void ap_(u)int::reverse ()

ap_[u]int インスタンスの内容を反転します。

  • LSB は MSB になります。
  • MSB は LSB になります。
リバース メソッドの例

ap_uint<8> Val = 0x12;

Val.reverse(); // Yields: 0x48
ビット値のテスト

bool ap_(u)int::test (unsigned i)

ap_(u)int インスタンスの指定ビットが 1 であるかどうかをチェックします。

1 の場合は true、それ以外の場合は false を返します。

テスト メソッドの例

ap_uint<8> Val = 0x12;
bool t = Val.test(5); // Yields: true
ビット値の設定

void ap_(u)int::set (unsigned i, bool v)                              
void ap_(u)int::set_bit (unsigned i, bool v)

ap_(u)int インスタンスの指定ビットを整数 V の値に設定します。

ビットの設定 (1)

void ap_(u)int::set (unsigned i)

ap_(u)int インスタンスの指定ビットを 1 に設定します。

ビットのクリア (0)

void ap_(u)int:: clear(unsigned i)

ap_(u)int インスタンスの指定ビットを 0 に設定します。

ビットの反転

void ap_(u)int:: invert(unsigned i)

ap_(u)int インスタンスの関数引数で指定したビットを反転します。指定ビットは、元の値が 0の場合 1 となり、0 の場合は 1 になります。

ビットの設定、クリア、反転メソッドの例:


ap_uint<8> Val = 0x12;
Val.set(0, 1); // Yields: 0x13
Val.set_bit(4, false); // Yields: 0x03
Val.set(7); // Yields: 0x83
Val.clear(1); // Yields: 0x81
Val.invert(4); // Yields: 0x91 
右に移動

void ap_(u)int:: rrotate(unsigned n)

ap_(u)int インスタンスを n 桁右に移動します。

左に移動

void ap_(u)int:: lrotate(unsigned n)

ap_(u)int インスタンスを n 桁左に移動します。


ap_uint<8> Val = 0x12;

Val.rrotate(3); // Yields: 0x42
Val.lrotate(6); // Yields: 0x90
ビット単位 NOT (bitwise_not)

void ap_(u)int:: b_not()
  • ap_(u)int インスタンスのすべてのビットを補足します。

ap_uint<8> Val = 0x12;

Val.b_not(); // Yields: 0xED

ビット単位 NOT の例

符号のテスト

bool ap_int:: sign()
  • ap_(u)int インスタンスが負の値なのかどうかをチェックします。
  • 負の値の場合は true が返されます。
  • 正の値の場合は false が返されます。
明示的な変換メソッド
C/C++ の (u)int 型

int ap_(u)int::to_int ()
unsigned ap_(u)int::to_uint ()
  • ap_[u]int に含まれる値のネイティブ C/C++ (ほとんどのシステムで 32 ビット) 整数を返します。
  • 値が [unsigned] int で表示されるよりも大きい場合は、切り捨てが実行されます。
C/C++ 64 ビット (u)int 型

long long ap_(u)int::to_int64 ()
unsigned long long ap_(u)int::to_uint64 ()
  • ap_[u]int に含まれる値のネイティブ C/C++ の 64 ビットの整数を返します。
  • 値が [unsigned] int で表示されるよりも大きい場合は、切り捨てが実行されます。
C/C++ の double 型

double ap_(u)int::to_double ()
  • ap_[u]int に含まれるネイティブ C/C++ の double の 64 ビット浮動小数点表記を返します。
  • ap_[u]int が 53 ビット (double の仮数のビット数) より大きい場合、結果の double に正確な値が含まれないことがあります。
注記: ザイリンクスでは、ap_[u]int をほかのデータ型に変換するのに、C 形式の型変換を使用する代わりに、メンバー関数を明示的に呼び出すことをお勧めしています。
Sizeof

標準 C++ sizeof() 関数は、ap_[u]int やその他のクラスまたはオブジェクトのインスタンスとは一緒に使用できません。ap_int<> 型はクラスで、sizeof からはそのクラスまたはインスタンス オブジェクトで使用されたストレージが返されます。sizeof(ap_int<N>) からは、常に使用されたバイト数が返されます。次に例を示します。


 sizeof(ap_int<127>)=16
 sizeof(ap_int<128>)=16
 sizeof(ap_int<129>)=24
 sizeof(ap_int<130>)=24
コンパイル時のデータ型属性へのアクセス

ap_[u]int<> 型には、変数のサイズをコンパイル時に決定できるようにするスタティック メンバーが含まれます。このデータ型に含まれる static const メンバーの width により、自動的にデータ型の幅が割り当てられます。


static const int width = _AP_W;

既存の ap_[u]int<> 型を抽出するのに width データ メンバーを使用すると、コンパイル時に別の ap_[u]int<> 型を作成できます。次の例は、変数 Res のサイズを変数 Val1Val2 変数よりも 1 ビット大きく定義する方法を示しています。


// Definition of basic data type
#define INPUT_DATA_WIDTH 8
typedef ap_int<INPUT_DATA_WIDTH> data_t;
// Definition of variables 
data_t Val1, Val2;
// Res is automatically sized at compile-time to be 1-bit greater than data type 
data_t
ap_int<data_t::width+1> Res = Val1 + Val2;

これにより、Vitis HLS で data_t の INPUT_DATA_WIDTH の値をアップデートした場合ですら、加算によるビット増加が正しく認識されるようになります。

C++ の任意精度固定小数点型

C++ 関数を使用すると、Vitis HLS に含まれる任意精度の固定小数点型の利点を活かすことができます。次の図は、これらの固定小数点型の基本的な機能についてまとめています。

  • ワードは符号付き (ap_fixed) または符号なし (ap_ufixed) にできます。
  • 任意サイズのワード幅 W を定義できます。
  • 整数部の桁数 I を指定すると、ワードの小数部 W-I (次の図の B) も定義されます。
  • 丸めまたは量子化 (Q) のタイプを選択できます。
  • オーバーフロー動作 (O および N) を選択できます。
2: 任意精度固定小数点型


ヒント: 任意精度の固定小数点型は、コードにヘッダー ファイル ap_fixed.h が含まれていると使用できます。

任意精度の固定小数点型の方が C シミュレーション中により多くのメモリを必要とします。ap_[u]fixed 型のかなり大きな配列を使用する場合は、配列の C シミュレーションの説明を参照してください。

固定小数点型を使用すると、次のような利点があります。

  • 小数を簡単に記述できます。
  • 整数部および小数部のビット数が異なる変数の場合、小数点のアライメントが処理されます。
  • 結果を精度を示すのに小数部ビットが足らない場合など、多くの丸めの実行を処理するオプションがあります。
  • 結果が整数部ビットよりも大きい場合のため、多くの変数のオーバーフローを処理するオプションもあります。

これらの属性は、次のコード例に含まれています。まず、ヘッダー ファイル ap_fixed.h が含まれ、ap_fixed 型が typedef 文により定義されます。

  • 10 ビット入力: 8 ビット整数値 + 2 小数部。
  • 6 ビット入力: 3 ビット整数値 + 3 小数部。
  • 累算用の 22 ビット変数: 22 ビット入力: 17 ビット整数値 + 5 小数部。
  • 結果用の 36 ビット変数: 22 ビット入力: 30 ビット整数値 + 6 小数部。

関数には、演算実行後の小数点のアライメントを管理するコードは含まれません。これは、自動的に実行されます。

次のコードのサンプルは、ap_fixed 型を示しています。


#include "ap_fixed.h"

typedef ap_ufixed<10,8, AP_RND, AP_SAT> din1_t;
typedef ap_fixed<6,3, AP_RND, AP_WRAP> din2_t;
typedef ap_fixed<22,17, AP_TRN, AP_SAT> dint_t;
typedef ap_fixed<36,30> dout_t;

dout_t cpp_ap_fixed(din1_t d_in1, din2_t d_in2) {

 static dint_t sum;
 sum += d_in1; 
 return sum * d_in2;
}

ap_(u)fixed 型を使用すると、C++ シミュレーションがビット精度になります。高速シミュレーションを使用すると、アルゴリズムとその精度を検証できます。合成後、RTL では同じビット精度の動作になります。

任意精度の固定小数点型を使用すると、コードにリテラル値を自由に代入できます。上記の例で使用されたテストベンチに示すように、in1 および in2 の値は宣言済みで、定数値が代入されます (次の例を参照)。

演算子が関係するリテラル値を代入する際は、まずリテラル値を ap_(u)fixed 型に変換する必要があります。このようにしないと、C コンパイラおよび Vitis HLS でこのリテラル値が整数または float/double 型として認識され、最適な演算子が検索されなくなります。次の例に示すように、in1 = in1 + din1_t(0.25) の代入では、リテラル値 0.25 が ap_fixed 型に変換されます。


#include <cmath>
#include <fstream>
#include <iostream>
#include <iomanip>
#include <cstdlib>
using namespace std;
#include "ap_fixed.h"

typedef ap_ufixed<10,8, AP_RND, AP_SAT> din1_t;
typedef ap_fixed<6,3, AP_RND, AP_WRAP> din2_t;
typedef ap_fixed<22,17, AP_TRN, AP_SAT> dint_t;
typedef ap_fixed<36,30> dout_t;

dout_t cpp_ap_fixed(din1_t d_in1, din2_t d_in2);
int main()
 {
 ofstream result;
 din1_t in1 = 0.25;
 din2_t in2 = 2.125;
 dout_t output;
 int retval=0;


 result.open(result.dat);
 // Persistent manipulators
 result << right << fixed << setbase(10) << setprecision(15);

 for (int i = 0; i <= 250; i++)
 {
 output = cpp_ap_fixed(in1,in2);

 result << setw(10) << i;
 result << setw(20) << in1;
 result << setw(20) << in2;
 result << setw(20) << output;
 result << endl;

 in1 = in1 + din1_t(0.25);
 in2 = in2 - din2_t(0.125);
 }
 result.close();

 // Compare the results file with the golden results
 retval = system(diff --brief -w result.dat result.golden.dat);
 if (retval != 0) {
 printf(Test failed  !!!\n); 
 retval=1;
 } else {
 printf(Test passed !\n);
 }

 // Return 0 if the test passes
 return retval;
}

固定小数点の識別子のまとめ

次の表は、量子化モードとオーバーフロー モードを示しています。

ヒント: 標準ハードウェア演算 (折り返しおよび切り捨て) のデフォルト ビヘイビアー以上を実行する量子化モードおよびオーバーフロー モードを使用すると、さらに多くの関連ハードウェアを含む演算子になります。負の無限大への丸めや対称的な飽和などのさらにアドバンスなモードをインプリメントするために、ロジック (LUT) が必要になります。
表 3. 固定小数点の識別子のまとめ
識別子 説明
W ワード長のビット数
I 整数値のビット数 (整数部のビット数)
Q 量子化モード: 結果の保存に使用される変数の最小の小数ビットで定義できるよりも高い精度が生成された場合の動作を指定します。
モード 説明
AP_RND 正の無限大への丸め
AP_RND_ZERO 0 への丸め
AP_RND_MIN_INF 負の無限大への丸め
AP_RND_INF 無限大への丸め
AP_RND_CONV 収束丸め
AP_TRN 負の無限大への切り捨て (デフォルト)
AP_TRN_ZERO 0 への切り捨て
O オーバーフロー モード: 演算結果が結果を格納するのに使用される変数の最大値を超えた場合の動作を指定します。
モード 説明
AP_SAT 飽和
AP_SAT_ZERO 0 への飽和
AP_SAT_SYM 対称飽和
AP_WRAP 折り返し (デフォルト)
AP_WRAP_SM 符号絶対値のラップ
N 折り返しモードでの飽和ビット数。

C++ の任意精度固定小数点型: リファレンス情報

メソッド、合成動作、ap_(u)fixed<N> 固定小数点型を使用する場合の詳細は、C++ の任意精度固定小数点型 を参照してください。このセクションには、次の項目が含まれます。

  • 任意精度の整数に (1024 ビットを超える値も含めて) 定数および初期値を代入する方法。
  • オーバーフロー モードおよび飽和モードの詳細。
  • 表示、連結、ビット スライス、範囲選択などの Vitis HLS のヘルパー メソッドの詳細。
  • シフト演算 (シフト値が負の場合は反対方向にシフト) の記述も含めた演算子の動作の詳細。
重要: 処理するコンパイラには、その言語の適切なヘッダー ファイルを使用する必要があります。
C++ の任意精度固定小数点型

Vitis HLS では、小数部分の計算を簡単に処理できるよう、固定小数点型がサポートされています。次の例に、固定小数点の演算の利点を示します。


ap_fixed<11, 6> Var1 = 22.96875; // 11-bit signed word, 5 fractional bits
ap_ufixed<12,11> Var2 = 512.5; // 12-bit word, 1 fractional bit
ap_fixed<16,11> Res1; // 16-bit signed word, 5 fractional bits

Res1 = Var1 + Var2; // Result is 535.46875

Var1 および Var2 の精度は異なりますが、固定小数点型を使用すると、演算 (この場合は加算) が実行される前に小数点が揃えられます。C コードでは、小数点を揃えるために演算を実行する必要はありません。

固定小数点演算の結果値を格納するのに使用されるデータ型は、整数ビットおよび小数ビットの両方を完全に格納するのに十分な大きさにする必要があります。

そうでない場合、ap_fixed 型で次が実行されます。

  • オーバーフロー処理 (指定されたデータ型でサポートされるよりも MSB が多い場合)
  • 量子化 (指定されたデータ型でサポートされるよりも LSB が少ない場合)

ap_[u]fixed 型では、オーバーフローおよび量子化にさまざまなオプションを使用できます。次のセクションで、その詳細を説明します。

ap_[u]fixed 表現

ap[u]fixed 型では、固定小数値は 2 進小数点の位置を指定した、ビット シーケンスで表現されます。

  • 2 進小数点の左側にあるビットは値の整数部を示します。
  • 右側にあるビットは値の小数部を示します。

ap_[u]fixed type は、次のように定義されています。


ap_[u]fixed<int W, 
 int I, 
 ap_q_mode Q, 
 ap_o_mode O,
 ap_sat_bits N>;

量子化モード
正の無限大への丸め AP_RND
0 への丸め AP_RND_ZERO
負の無限大への丸め AP_RND_MIN_INF
無限大への丸め AP_RND_INF
収束丸め AP_RND_CONV
切り捨て AP_TRN
0 への切り捨て AP_TRN_ZERO
AP_RND
  • 特定の ap_[u]fixed 型の近似値に値が丸められます。
    ap_fixed<3, 2, AP_RND, AP_SAT> UAPFixed4 = 1.25; // Yields: 1.5
    ap_fixed<3, 2, AP_RND, AP_SAT> UAPFixed4 = -1.25; // Yields: -1.0
AP_RND_ZERO
  • 表示可能なの近似値に値が丸められます。
  • 0 の方向へ丸められます。
    • 正の値に対しては重複ビットが削除されます。
    • 負の値に対しては LSB が追加され、最も近い表示可能な値が表示されます。
    ap_fixed<3, 2, AP_RND_ZERO, AP_SAT> UAPFixed4 = 1.25; // Yields: 1.0
    ap_fixed<3, 2, AP_RND_ZERO, AP_SAT> UAPFixed4 = -1.25; // Yields: -1.0
AP_RND_MIN_INF
  • 表示可能なの近似値に値が丸められます。
  • 負の無限大の方向へ丸められます。
    • 正の値に対しては重複ビットが削除されます。
    • 負の値に対しては LSB が追加されます。
    ap_fixed<3, 2, AP_RND_MIN_INF, AP_SAT> UAPFixed4 = 1.25; // Yields: 1.0
    ap_fixed<3, 2, AP_RND_MIN_INF, AP_SAT> UAPFixed4 = -1.25; // Yields: -1.5
AP_RND_INF
  • 表示可能なの近似値に値が丸められます。
  • 丸めは LSB によって変わります。
    • 正の値に対しては、LSB が設定されている場合は正の無限大の方向に丸められます。そうでない場合は負の無限大の方向に丸められます。
    • 負の値に対しては、LSB が設定されている場合は負の無限大の方向に丸められます。そうでない場合は正の無限大の方向に丸められます。
    ap_fixed<3, 2, AP_RND_INF, AP_SAT> UAPFixed4 = 1.25; // Yields: 1.5
    ap_fixed<3, 2, AP_RND_INF, AP_SAT> UAPFixed4 = -1.25; // Yields: -1.5
AP_RND_CONV
  • 表示可能なの近似値に値が丸められます。
  • 丸めは LSB によって変わります。
    • LSB が設定されている場合は正の無限大の方向に丸められます。
    • そうでない場合は負の無限大の方向に丸められます。
    ap_fixed<3, 2, AP_RND_CONV, AP_SAT> UAPFixed4 = 0.75; // Yields: 1.0
    ap_fixed<3, 2, AP_RND_CONV, AP_SAT> UAPFixed4 = -1.25; // Yields: -1.0
AP_TRN
  • 常に負の無限大に向かって値が丸められます。
    ap_fixed<3, 2, AP_TRN, AP_SAT> UAPFixed4 = 1.25; // Yields: 1.0
    ap_fixed<3, 2, AP_TRN, AP_SAT> UAPFixed4 = -1.25; // Yields: -1.5
AP_TRN_ZERO

値を次のように丸めます。

  • 正の値の場合、AP_TRN モードと同じ丸めになります。
  • 負の値の場合はゼロの方向へ丸められます。
    ap_fixed<3, 2, AP_TRN_ZERO, AP_SAT> UAPFixed4 = 1.25; // Yields: 1.0
    ap_fixed<3, 2, AP_TRN_ZERO, AP_SAT> UAPFixed4 = -1.25; // Yields: -1.0
オーバーフロー モード
飽和 AP_SAT
0 への飽和 AP_SAT_ZERO
対称飽和 AP_SAT_SYM
折り返し AP_WRAP
符号絶対値の折り返し AP_WRAP_SM
AP_SAT

値が次のように飽和されます。

  • オーバーフローが発生したときは値が最大値に飽和されます。
  • 負のオーバーフローのときは最小値に飽和されます。
    ap_fixed<4, 4, AP_RND, AP_SAT> UAPFixed4 = 19.0; // Yields: 7.0
    ap_fixed<4, 4, AP_RND, AP_SAT> UAPFixed4 = -19.0; // Yields: -8.0
    ap_ufixed<4, 4, AP_RND, AP_SAT> UAPFixed4 = 19.0; // Yields: 15.0
    ap_ufixed<4, 4, AP_RND, AP_SAT> UAPFixed4 = -19.0; // Yields: 0.0
AP_SAT_ZERO

オーバーフローまたは負のオーバーフローの場合に値 を 0 にします。

ap_fixed<4, 4, AP_RND, AP_SAT_ZERO> UAPFixed4 = 19.0; // Yields: 0.0
ap_fixed<4, 4, AP_RND, AP_SAT_ZERO> UAPFixed4 = -19.0; // Yields: 0.0
ap_ufixed<4, 4, AP_RND, AP_SAT_ZERO> UAPFixed4 = 19.0; // Yields: 0.0
ap_ufixed<4, 4, AP_RND, AP_SAT_ZERO> UAPFixed4 = -19.0; // Yields: 0.0
AP_SAT_SYM

値が次のように飽和されます。

  • オーバーフローが発生したときは値が最大値に飽和されます。
  • 負のオーバーフローのときは最小値に飽和されます。
    • 符号付き ap_fixed 型の場合は負の最大値
    • 符号なし ap_ufixed 型の場合は 0
    ap_fixed<4, 4, AP_RND, AP_SAT_SYM> UAPFixed4 = 19.0; // Yields: 7.0
    ap_fixed<4, 4, AP_RND, AP_SAT_SYM> UAPFixed4 = -19.0; // Yields: -7.0
    ap_ufixed<4, 4, AP_RND, AP_SAT_SYM> UAPFixed4 = 19.0; // Yields: 15.0
    ap_ufixed<4, 4, AP_RND, AP_SAT_SYM> UAPFixed4 = -19.0; // Yields: 0.0
AP_WRAP

オーバーフローが発生したときは値が折り返されます。

ap_fixed<4, 4, AP_RND, AP_WRAP> UAPFixed4 = 31.0; // Yields: -1.0
ap_fixed<4, 4, AP_RND, AP_WRAP> UAPFixed4 = -19.0; // Yields: -3.0
ap_ufixed<4, 4, AP_RND, AP_WRAP> UAPFixed4 = 19.0; // Yields: 3.0
ap_ufixed<4, 4, AP_RND, AP_WRAP> UAPFixed4 = -19.0; // Yields: 13.0

N の値が 0 の場合 (デフォルトのオーバーフロー モード):

  • 範囲外の MSB はすべて削除されます。
  • 符号なしの場合は、最大値に達すると 0 に戻ります。
  • 符号付きの場合は、最大値に達すると最小値に戻ります。

N が 0 より大きい場合:

  • N が 0 より大きい場合、MSB は飽和するか、または 1 に設定されます。
  • 符号ビットは保持されるため、正の値は正のまま、負の値は負のままになります。
  • 飽和していないビットは LSB 側からコピーされます。
AP_WRAP_SM

値が符号絶対値で折り返されます。

ap_fixed<4, 4, AP_RND, AP_WRAP_SM> UAPFixed4 = 19.0; // Yields: -4.0
ap_fixed<4, 4, AP_RND, AP_WRAP_SM> UAPFixed4 = -19.0; // Yields: 2.0

N の値が 0 の場合 (デフォルトのオーバーフロー モード):

  • このモードでは符号絶対値ラップが使用されます。
  • 符号ビットは最下位の削除されたビットの値に設定されます。
  • 最上位の残りのビットが元の MSB とは異なる場合、残っているビットすべてが反転されます。
  • MSB が同じである場合は、ほかのビットがコピーされます。
    1. 重複している MSB が削除されます。
    2. 新しい符号ビットは削除されたビットの最下位ビットです。この場合は 0 です。
    3. 新しい符号ビットが新しい値の符号と比較されます。
  • 異なる場合は、すべての数値が反転されます。この場合は数値は異なります。

N が 0 より大きい場合:

  • 符号絶対値の飽和が使用されます。
  • N 個の MSB が 1 に飽和します。
  • N =0 のケースと同様の動作になりますが、正の数値は正のまま、負の数値は負のままになる点が異なります。
ap_[u]fixed<> 型のコンパイル

ap_[u]fixed<> クラスを使用するには、ap_[u]fixed<> variables を参照するソース ファイルすべてに ap_fixed.h ヘッダー ファイルを含める必要があります。

これらのクラスを使用するソフトウェア モデルをコンパイルするとき、Vitis HLS のヘッダー ファイルの場所を指定する必要がある場合があります。たとえば、g++ コンパイルには “-I/<HLS_HOME>/include” オプションを追加するなどします。

ap_[u]fixed<> 変数の宣言と定義

次のようにそれぞれ符号付きおよび符号なしのクラスが別々にあります。

  • ap_fixed<W,I> (符号付き)
  • ap_ufixed<W,I> (符号なし)

C/C++ の typedef 文を使用すると、ユーザー定義型を作成できます。


#include "ap_fixed.h" // use ap_[u]fixed<> types

typedef ap_ufixed<128,32> uint128_t; // 128-bit user defined type, 
 //  32 integer bits

ユーザー定義型の例

定数 (リテラル) からの初期化および代入

ap_[u]fixed 変数は、次の一般的な C/C++ 幅の標準浮動小数点定数で初期化されます。

  • float 型の場合は 32 ビット
  • double 型の場合は 64 ビット

つまり、通常は、単精度または倍精度の浮動小数点です。

固定小数点変数に割り当てられる値は、定数の精度によって制限されます。上記の定数 (リテラル) からの初期化および代入の基準に従って、固定小数点変数のすべてのビットがストリングで記述された精度に従って生成されるようにしてください。


#include <ap_fixed.h>

ap_ufixed<30, 15> my15BitInt = 3.1415;
ap_fixed<42, 23> my42BitInt = -1158.987;
ap_ufixed<99, 40> = 287432.0382911;
ap_fixed<36,30> = -0x123.456p-1;

ap_[u]fixed 型では、それらが std::complex 型の配列で使用される場合は、初期化がサポートされません。


typedef ap_fixed<DIN_W, 1, AP_TRN, AP_SAT> coeff_t; // MUST have IW >= 1
std::complex<coeff_t> twid_rom[REAL_SZ/2] = {{ 1, -0 },{ 0.9,-0.006 }, etc.}

初期化値は、まず std::complex に変換する必要があります。


typedef ap_fixed<DIN_W, 1, AP_TRN, AP_SAT> coeff_t; // MUST have IW >= 1
std::complex<coeff_t> twid_rom[REAL_SZ/2] = {std::complex<coeff_t>( 1, -0 ), 
std::complex<coeff_t>(0.9,-0.006 ),etc.}
コンソール I/O (出力) のサポート

Vitis HLS には、ap_[u]fixed<> 変数の初期化および代入の場合と同様に、64 ビットよりも大きな値の出力をサポートするための機能が提供されています。

ap_[u]fixed 変数に格納されている値を出力するのに最も簡単な方法は、次の C++ の標準出力ストリーム std::cout (#include <iostream> or <iostream.h>) を使用する方法です。ストリーム挿入演算子 << は、任意の ap_[u]fixed 変数に対する範囲全体に含まれる値が正しく出力されるように、オーバーロードされます。次のストリーム マニピュレーターもサポートされているので、それぞれの進数値をフォーマットできます。

  • dec (10 進数)
  • hex (16 進数)
  • oct (8 進数)
    #include <iostream.h>
    // Alternative: #include <iostream>
    
    ap_fixed<6,3, AP_RND, AP_WRAP> Val = 3.25;
    
    cout << Val << endl;     // Yields: 3.25
標準 C ライブラリの使用

標準 C ライブラリ (#include <stdio.h>) を使用して、64 ビットよりも大きな値を出力することもできます。

  1. ap_[u]fixed クラスの to_string() メソッドを使用し、値を C++ の std::string に変換します。
  2. その結果を std::string クラスの c_str() メソッドを使用して、ヌルで終了する C 文字に変換します。
オプションの引数 1 (基数の指定)

ap[u]int::to_string() メソッドでは目的の基数を指定するオプションの引数を渡すことができます。有効な基数の引数値は、次のとおりです。

  • 2 (2 進数)
  • 8 (8 進数)
  • 10 (10 進数)
  • 16 (16 進数) (デフォルト)
オプションの引数 2 (符号付きの値として出力)

ap_[u]int::to_string() の 2 つ目のオプションの引数は、10 進数以外のフォーマットで符号付きの値を出力するかどうかを指定します。この引数はブール形式です。デフォルト値は false で、10 進数以外のフォーマットで符号なしの値が出力されます。

ap_fixed<6,3, AP_RND, AP_WRAP> Val = 3.25;

printf("%s \n", in2.to_string().c_str()); // Yields: 0b011.010
printf("%s \n", in2.to_string(10).c_str()); //Yields: 3.25

ap_[u]fixed 型は、次の C++ マニピュレーター関数でサポートされます。

  • setprecision
  • setw
  • setfill

setprecision マニピュレーターでは、使用する小数点以下の精度を設定します。1 つのパラメーター f を小数点以下の精度の値として使用します。n は表示する合計有効桁数の最大値 (小数点の前と後の両方をカウント) を指定します。

f のデフォルト値は 6 で、ネイティブ C の float 型と一貫しています。

ap_fixed<64, 32> f =3.14159;
cout << setprecision (5) << f << endl;
cout << setprecision (9) << f << endl;
f = 123456;
cout << setprecision (5) << f << endl;

上記を実行すると、実際の精度が指定した精度を超える場合に丸められ、次の結果が表示されます。

   3.1416
   3.14159
   1.2346e+05

setw マニピュレーターは、次を実行します。

  • フィールド幅に使用される文字数を設定します。
  • 1 つのパラメーター w を幅の値として使用します。

    説明:

    • w: 出力表示で使用される最小文字数を指定します。

表示の標準幅がフィールド幅よりも短い場合は、埋め字で補足されます。埋め字は setfill マニピュレーターで制御され、1 つのパラメーター f が補足文字として使用されます。

次に例を示します。

    ap_fixed<65,32> aa = 123456;
    int precision = 5;
    cout<<setprecision(precision)<<setw(13)<<setfill('T')<<a<<endl;

この出力は、次のようになります。

     TTT1.2346e+05
ap_[u]fixed<> 型を使用した式

任意精度の固定小数点値は、C/C++ でサポートされている演算子を使用する式で使用できます。任意精度の固定小数点型または変数を定義し終えたら、その使用方法は C/C++ 言語のほかの浮動小数点型または変数と同じです。

次に注意してください。

  • ゼロおよび符号拡張

    ソース値の符号により、ビット幅が小さいほうの値はすべてゼロが追加されるかまたは符号拡張されます。ビット幅が小さい方の値を大きな方の値に代入するとき、符号を得るには型変換 (キャスト) を挿入する必要がある場合があります。

  • 切り捨て

    デスティネーション変数よりもビット幅が大きな値の任意精度の固定小数点の代入をする合は、切り捨てが実行されます。

クラス メソッド、演算子、データ メンバー

通常、ネイティブの C/C++ 整数型で実行可能な有効な演算は、ap_[u]fixed 型で演算子のオーバーロードを使用するとサポートされます。オーバーロードされた演算子に加え、ビット レベルの演算を実行しやすくする演算子およびメソッドが含まれています。

2 進数の演算子
加算
ap_[u]fixed::RType ap_[u]fixed::operator + (ap_[u]fixed op)

任意精度の固定小数点と指定したオペランド op を加算します。

オペランドには、次のいずれかの整数型を使用できます。

  • ap_[u]fixed
  • ap_[u]int
  • C/C++

結果のデータ型 ap_[u]fixed::RType は、2 つのオペランドのデータ型によります。

ap_fixed<76, 63> Result;

ap_fixed<5, 2> Val1 = 1.125;
ap_fixed<75, 62> Val2 = 6721.35595703125;

Result = Val1 + Val2; //Yields 6722.480957

Val2 の方が整数部および小数部のビット幅が広いため、結果値をすべて格納できるようにするため、結果のデータ型の幅はこのビット幅に 1 を足したものになります。

データの幅を指定すると、次に示すように、べき関数を使用してリソースが制御されます。同様に、ザイリンクスでは、固定小数点演算の幅を指定する代わりに、格納された結果の幅を指定することをお勧めします。

ap_ufixed<16,6> x=5; 
ap_ufixed<16,7>y=hl::rsqrt<16,6>(x+x); 
減算
ap_[u]fixed::RType ap_[u]fixed::operator - (ap_[u]fixed op)

任意精度の固定小数点値から指定したオペランド op を減算します。

結果のデータ型 ap_[u]fixed::RType は、2 つのオペランドのデータ型によります。

ap_fixed<76, 63> Result;

ap_fixed<5, 2> Val1 = 1625.153;
ap_fixed<75, 62> Val2 = 6721.355992351;

Result = Val2 - Val1; // Yields 6720.23057

Val2 の方が整数部および小数部のビット幅が広いため、結果値をすべて格納できるようにするため、結果のデータ型の幅はこのビット幅に 1 を足したものになります。

乗算
ap_[u]fixed::RType ap_[u]fixed::operator * (ap_[u]fixed op)

任意精度の固定小数点値を指定したオペランド op と乗算します。

ap_fixed<80, 64> Result;

ap_fixed<5, 2> Val1 = 1625.153;
ap_fixed<75, 62> Val2 = 6721.355992351;

Result = Val1 * Val2; // Yields 7561.525452

この例では、Val1Val2 を乗算しています。この結果のデータ型は、整数部のビット幅と小数部のビット幅の和になります。

除算
ap_[u]fixed::RType ap_[u]fixed::operator / (ap_[u]fixed op)

任意精度の固定小数点値を指定したオペランド op で除算します。

ap_fixed<84, 66> Result;

ap_fixed<5, 2> Val1 = 1625.153;
ap_fixed<75, 62> Val2 = 6721.355992351;

Val2 / Val1; // Yields 5974.538628

この例では、Val1Val2 を除算しています。十分な精度を保持するため、次のようになります。

  • 結果のデータ型の整数ビット幅は、Val2 の整数ビット幅と、Val1 の小数ビット幅の和になります。
  • 結果のデータ型の小数ビット幅は、Val2 の小数ビット幅と等しくなります。
ビット単位の論理演算子
ビット単位 OR
ap_[u]fixed::RType ap_[u]fixed::operator | (ap_[u]fixed op)

任意精度の固定小数点と任意のオペランド op にビット単位演算を適用します。

ap_fixed<75, 62> Result;

ap_fixed<5, 2> Val1 = 1625.153;
ap_fixed<75, 62> Val2 = 6721.355992351;

Result = Val1 | Val2; // Yields 6271.480957
ビット単位 AND
ap_[u]fixed::RType ap_[u]fixed::operator & (ap_[u]fixed op)

任意精度の固定小数点と任意のオペランド op にビット単位演算を適用します。

ap_fixed<75, 62> Result;

ap_fixed<5, 2> Val1 = 1625.153;
ap_fixed<75, 62> Val2 = 6721.355992351;

Result = Val1 & Val2;  // Yields 1.00000
ビット単位 XOR
ap_[u]fixed::RType ap_[u]fixed::operator ^ (ap_[u]fixed op)

任意精度の固定小数点と任意のオペランド opxor ビット単位演算を適用します。

ap_fixed<75, 62> Result;

ap_fixed<5, 2> Val1 = 1625.153;
ap_fixed<75, 62> Val2 = 6721.355992351;

Result = Val1 ^ Val2; // Yields 6720.480957
インクリメントおよびデクリメント演算子
前置インクリメント
ap_[u]fixed ap_[u]fixed::operator ++ ()

任意精度の固定小数点変数を 1 インクリメントします。

ap_fixed<25, 8> Result;
ap_fixed<8, 5> Val1 = 5.125;

Result = ++Val1; // Yields 6.125000
後置インクリメント
ap_[u]fixed ap_[u]fixed::operator ++ (int)

この演算関数は、次を実行します。

  • 任意精度の固定小数点変数を 1 インクリメント。
  • この任意精度の固定小数点の元の値を返す。
    ap_fixed<25, 8> Result;
    ap_fixed<8, 5> Val1 = 5.125;
    
    Result = Val1++; // Yields 5.125000
前置デクリメント
ap_[u]fixed ap_[u]fixed::operator -- ()

任意精度の固定小数点変数を 1 デクリメントします。

ap_fixed<25, 8> Result;
ap_fixed<8, 5> Val1 = 5.125;

Result = --Val1; // Yields 4.125000
後置デクリメント
ap_[u]fixed ap_[u]fixed::operator -- (int)

この演算関数は、次を実行します。

  • 任意精度の固定小数点変数を 1 デクリメント。
  • この任意精度の固定小数点の元の値を返す。
    ap_fixed<25, 8> Result;
    ap_fixed<8, 5> Val1 = 5.125;
    
    Result = Val1--; // Yields 5.125000
単項演算子
加算
ap_[u]fixed ap_[u]fixed::operator + ()

任意精度の固定小数点変数のセルフ コピーを返します。

ap_fixed<25, 8> Result;
ap_fixed<8, 5> Val1 = 5.125;

Result = +Val1;  // Yields 5.125000
減算
ap_[u]fixed::RType ap_[u]fixed::operator - ()

任意精度の固定小数点変数の負の値を返します。

ap_fixed<25, 8> Result;
ap_fixed<8, 5> Val1 = 5.125;

Result = -Val1; // Yields -5.125000
0 との等価比較
bool ap_[u]fixed::operator ! ()

この演算関数は、次を実行します。

  • 任意精度の固定小数点変数を 0 と比較。
  • 結果を返す。
    bool  Result;
    ap_fixed<8, 5> Val1 = 5.125;
    
    Result = !Val1; // Yields false
ビット単位の反転
ap_[u]fixed::RType ap_[u]fixed::operator ~ ()

任意精度の固定小数点変数のビット単位の補数を返します。

ap_fixed<25, 15> Result;
ap_fixed<8, 5> Val1 = 5.125;

Result = ~Val1; // Yields -5.25
シフト演算子
符号なし左シフト
ap_[u]fixed ap_[u]fixed::operator << (ap_uint<_W2> op) 

この演算関数は、次を実行します。

  • 指定した整数オペランド分左にシフト。
  • 結果を返す。

オペランドには次の C/C++ の整数型を使用できます。

  • char
  • short
  • int
  • long

左シフト演算で返されるデータ型は、シフトされたデータ型と同じ幅になります。

注記: シフトではオーバーフローまたは量子化モードはサポートされていません。
ap_fixed<25, 15> Result;
ap_fixed<8, 5> Val = 5.375;

ap_uint<4> sh = 2;

Result = Val << sh; // Yields -10.5

結果のビット幅は (W = 25I = 15) です。左シフト演算の結果のデータ型は Val と同じであるため、次のようになります。

  • Val の上位 2 ビットがシフト アウトされます。
  • 結果は -10.5 になります。

結果が 21.5 になるようにするには、ap_ufixed<10, 7>(Val) のように、まず Valap_fixed<10, 7> にキャストする必要があります。

符号付き左シフト
ap_[u]fixed ap_[u]fixed::operator << (ap_int<_W2> op)

この演算子は、次を実行します。

  • 指定した整数オペランド分左にシフト。
  • 結果を返す。

オペランドが正か負かによって、シフトの方向が変わります。

  • オペランドが正の場合は右シフトが実行されます。
  • オペランドが負の場合は左シフト (逆方向) が実行されます。

オペランドには次の C/C++ の整数型を使用できます。

  • char
  • short
  • int
  • long

右シフト演算で返される型は、シフトされた型と同じ幅になります。

ap_fixed<25, 15,  false> Result;
ap_uint<8, 5> Val = 5.375;

ap_int<4> Sh = 2;
Result = Val << sh; // Shift left, yields -10.25

Sh = -2;
Result = Val << sh; // Shift right, yields 1.25
符号なし右シフト
ap_[u]fixed ap_[u]fixed::operator >> (ap_uint<_W2> op) 

この operator 関数では、次が実行されます。

  • 任意の整数オペランドで右にシフト。
  • 結果を返す。

オペランドには次の C/C++ の整数型を使用できます。

  • char
  • short
  • int
  • long

右シフト演算で返されるデータ型は、シフトされたデータ型と同じ幅になります。

ap_fixed<25, 15> Result;
ap_fixed<8, 5> Val = 5.375;

ap_uint<4> sh = 2;

Result = Val >> sh; // Yields 1.25

すべての上位ビットを保持する必要がある場合は、ap_fixed<10, 5>(Val) のように、まず Val の小数部のビット幅を拡張します。

符号付き右シフト
ap_[u]fixed ap_[u]fixed::operator >> (ap_int<_W2> op) 

この演算子は、次を実行します。

  • 指定した整数オペランド分右にシフト。
  • 結果を返す。

オペランドが正か負かによって、シフトの方向が変わります。

  • オペランドが正の場合は右シフトが実行されます。
  • オペランドが負の場合は左シフト (逆方向) が実行されます。

オペランドには C/C++ の整数型 (charshortint、または long) を使用できます。

右シフト演算で返されるデータ型は、シフトされるデータ型と同じ幅になります。次に例を示します。

ap_fixed<25, 15,  false> Result;
ap_uint<8, 5> Val = 5.375;

ap_int<4> Sh = 2;
Result = Val >> sh; // Shift right, yields 1.25

Sh = -2;
Result = Val >> sh; // Shift left,  yields -10.5

1.25
比較演算子
等価比較
bool ap_[u]fixed::operator == (ap_[u]fixed op)

任意精度の固定小数点変数と指定したオペランドを比較します。

等しい場合は true、等しくない場合は false を返します。

オペランド op のデータ型には、ap_[u]fixedap_int または C/C++ の整数型を使用できます。次に例を示します。

bool Result;

ap_ufixed<8, 5> Val1 = 1.25;
ap_fixed<9, 4> Val2 = 17.25;
ap_fixed<10, 5> Val3 = 3.25;

Result = Val1 == Val2; // Yields  true
Result = Val1 == Val3; // Yields  false
不等価比較
bool ap_[u]fixed::operator != (ap_[u]fixed op)

任意精度の固定小数点変数を指定したオペランドと比較します。

等しくない場合は true、等しい場合は false を返します。

オペランド op のデータ型には、次を使用できます。

  • ap_[u]fixed
  • ap_int
  • C または C++ の整数型

次に例を示します。

bool Result;

ap_ufixed<8, 5> Val1 = 1.25;
ap_fixed<9, 4> Val2 = 17.25;
ap_fixed<10, 5> Val3 = 3.25;

Result = Val1 != Val2; // Yields false
Result = Val1 != Val3; // Yields true
以上
bool ap_[u]fixed::operator >= (ap_[u]fixed op)

変数を指定のオペランドと比較します。

変数がオペランド以上の場合は true、そうでない場合は false を返します。

オペランド op のデータ型には、ap_[u]fixedap_int または C/C++ の整数型を使用できます。

次に例を示します。

bool Result;

ap_ufixed<8, 5> Val1 = 1.25;
ap_fixed<9, 4> Val2 = 17.25;
ap_fixed<10, 5> Val3 = 3.25;

Result = Val1 >= Val2; // Yields true
Result = Val1 >= Val3; // Yields false
以下
bool ap_[u]fixed::operator <= (ap_[u]fixed op)

変数と指定のオペランドを比較し、変数がオペランド以下の場合は true、そうでない場合は false を返します。

オペランド op のデータ型には、ap_[u]fixedap_int または C/C++ の整数型を使用できます。

次に例を示します。

bool Result;

ap_ufixed<8, 5> Val1 = 1.25;
ap_fixed<9, 4> Val2 = 17.25;
ap_fixed<10, 5> Val3 = 3.25;

Result = Val1 <= Val2; // Yields true
Result = Val1 <= Val3; // Yields true
大なり (>)
bool ap_[u]fixed::operator > (ap_[u]fixed op)

変数を指定のオペランドと比較し、変数がオペランドより大きい場合は true、そうでない場合は false を返します。

オペランド op のデータ型には、ap_[u]fixedap_int または C/C++ の整数型を使用できます。

次に例を示します。

bool Result;

ap_ufixed<8, 5> Val1 = 1.25;
ap_fixed<9, 4> Val2 = 17.25;
ap_fixed<10, 5> Val3 = 3.25;

Result = Val1 > Val2; // Yields false
Result = Val1 > Val3; // Yields false
小なり (<)
bool ap_[u]fixed::operator < (ap_[u]fixed op)

変数と指定のオペランドを比較し、変数がオペランド未満の場合は true、そうでない場合は false を返します。

オペランド op のデータ型には、ap_[u]fixedap_int または C/C++ の整数型を使用できます。次に例を示します。

bool Result;

ap_ufixed<8, 5> Val1 = 1.25;
ap_fixed<9, 4> Val2 = 17.25;
ap_fixed<10, 5> Val3 = 3.25;

Result = Val1 < Val2; // Yields false
Result = Val1 < Val3; // Yields true
ビット演算子
ビットの選択と設定
af_bit_ref ap_[u]fixed::operator [] (int bit) 

任意精度の固定小数点値から 1 ビット選択し、それを返します。

戻り値は、ap_[u]fixed 変数の対応するビットをセットまたはクリアできる参照値です。ビット引数は整数値である必要があり、選択するビットの指数を指定します。最下位ビットの指数は 0 です。最大指数はこの ap_[u]fixed 変数のビット幅から 1 を引いた値になります。

結果のデータ型は af_bit_ref で、値は 0 または 1 です。次に例を示します。

ap_int<8, 5> Value = 1.375;

Value[3]; // Yields  1
Value[4]; // Yields  0

Value[2] = 1; // Yields 1.875
Value[3] = 0; // Yields 0.875
ビット範囲
af_range_ref af_(u)fixed::range (unsigned Hi, unsigned Lo)
af_range_ref af_(u)fixed::operator [] (unsigned Hi, unsigned Lo) 

この演算は、ビット セレクト演算子 [] に似ていますが、1 ビットではなくビットの範囲を指定し、

任意精度の固定小数点変数からビットのグループを選択します。引数 Hi は範囲の上限のビットを指定し、引数 Lo は下限のビットを指定します。LoHi よりも大きい場合は、選択されたビットが逆順で返されます。

戻り値のデータ型 af_range_ref は、Hi および Lo で指定される ap_[u]fixed 変数の範囲の参照です。次に例を示します。

ap_uint<4> Result = 0;
ap_ufixed<4, 2> Value = 1.25;
ap_uint<8> Repl = 0xAA;

Result = Value.range(3, 0); // Yields: 0x5
Value(3, 0) = Repl(3, 0); // Yields: -1.5

// when Lo > Hi, return the reverse bits string
Result = Value.range(0, 3); // Yields: 0xA
範囲選択
af_range_ref af_(u)fixed::range ()
af_range_ref af_(u)fixed::operator [] 

これは、範囲選択演算子 [] の特殊ケースで、任意精度の固定小数点値からすべてのビットを標準の順序で選択します。

戻り値のデータ型 af_range_ref は、Hi = W - 1 および Lo = 0 で指定される範囲の参照です。次に例を示します。

ap_uint<4> Result = 0;

ap_ufixed<4, 2> Value = 1.25;
ap_uint<8> Repl = 0xAA;

Result = Value.range(); // Yields: 0x5
Value() = Repl(3, 0); // Yields: -1.5
長さ
int ap_[u]fixed::length ()

ビット数を表す整数値を任意精度の整数値で返します。データ型または値に使用できます。次に例を示します。

ap_ufixed<128, 64> My128APFixed;

int bitwidth = My128APFixed.length(); // Yields 128
明示的な変換メソッド
固定小数点から double 型
double ap_[u]fixed::to_double ()

このメンバー関数は、固定小数点値を IEEE の倍精度フォーマットに変換します。次に例を示します。

ap_ufixed<256, 77> MyAPFixed = 333.789;
double Result;

Result = MyAPFixed.to_double(); // Yields 333.789
固定小数点から浮動小数点
float ap_[u]fixed::to_float()

このメンバー関数は、固定小数点値を IEEE の浮動精度フォーマットに変換します。次に例を示します。

ap_ufixed<256, 77> MyAPFixed = 333.789;
float Result;

Result = MyAPFixed.to_float();  // Yields 333.789
固定小数点から半精度浮動小数点
half ap_[u]fixed::to_half()

このメンバー関数は、固定小数点値を HLS の半精度 (16 ビット) 浮動精度フォーマットに変換します。次に例を示します。

ap_ufixed<256, 77> MyAPFixed = 333.789;
half Result;

Result = MyAPFixed.to_half();  // Yields 333.789
固定小数点から ap_int 型
ap_int ap_[u]fixed::to_ap_int ()

このメンバー関数は、この固定小数点値を、小数ビットを切り捨てた整数ビットのみを含む ap_int 型に変換します。次に例を示します。

ap_ufixed<256, 77> MyAPFixed = 333.789;
ap_uint<77> Result;

Result = MyAPFixed.to_ap_int(); //Yields 333
固定小数点から整数型
int ap_[u]fixed::to_int ()
unsigned ap_[u]fixed::to_uint ()
ap_slong ap_[u]fixed::to_int64 ()
ap_ulong ap_[u]fixed::to_uint64 ()

このメンバー関数は、固定小数点値を C のビルトイン整数型に変換します。次に例を示します。

ap_ufixed<256, 77> MyAPFixed = 333.789;
unsigned int  Result;

Result = MyAPFixed.to_uint(); //Yields 333

unsigned long long Result;
Result = MyAPFixed.to_uint64(); //Yields 333
注記: ap_[u]fixed をほかのデータ型に変換するには、ザイリンクスでは C 形式のキャストを使用する代わりに、メンバー関数を明示的に呼び出すことをお勧めします。
コンパイル時のデータ型属性へのアクセス

ap_[u]fixed<> 型には、データ型のサイズと設定がコンパイル時に決定されるようにする複数のスタティック メンバーがあります。このデータ型には、static const メンバーである widthiwidthqmode、および omode があります。

static const int width = _AP_W;
static const int iwidth = _AP_I;
static const ap_q_mode qmode = _AP_Q;
static const ap_o_mode omode = _AP_O;

これらのデータ メンバーは、既存の ap_[u]fixed<> 型から次の情報を抽出するために使用できます。

  • width: データ型の幅。
  • iwidth: データ型の整数部分の幅。
  • qmode: データ型の量子化モード。
  • omode: データ型のオーバーフロー モード。

これらのデータ メンバーを使用すると、たとえば既存の ap_[u]fixed<> 型のデータ幅を抽出して、コンパイル時に別の ap_[u]fixed<> 型を作成できます。

次の例は、変数 Res のサイズを、変数 Val1 および Val2 よりも 1 ビット大きくし、同じ量子化モードで定義します。

// Definition of basic data type
#define INPUT_DATA_WIDTH 12
#define IN_INTG_WIDTH 6
#define IN_QMODE AP_RND_ZERO
#define IN_OMODE AP_WRAP
typedef ap_fixed<INPUT_DATA_WIDTH, IN_INTG_WIDTH, IN_QMODE, IN_OMODE> data_t;
// Definition of variables 
data_t Val1, Val2;
// Res is automatically sized at run-time to be 1-bit greater than INPUT_DATA_WIDTH 
// The bit growth in Res will be in the integer bits
ap_int<data_t::width+1, data_t::iwidth+1, data_t::qmode, data_t::omode> Res = Val1 + 
Val2;

これにより、INPUT_DATA_WIDTH、IN_INTG_WIDTH、または data_t の量子化モードの値をアップデートした場合でも、Vitis HLS で加算によるビット増加が正しくモデリングされるようになります。

Vitis HLS math ライブラリ

Vitis HLS の math ライブラリ (hls_math.h) は、標準 C (math.h) および C++ (cmath.h) ライブラリの合成をサポートするために提供されており、合成中の算術演算を指定するために自動的に使用されます。すべての関数の浮動小数点と一部の関数の固定小数点がサポートされます。

hls_math.h ライブラリは、オプションで標準 C++ math ライブラリ (cmath.h) の代わりに C++ ソース コードで使用できますが、C ソース コードでは使用できません。Vitis HLS では、適切なシミュレーション インプリメンテーションを使用して、C シミュレーションと C/RTL 協調シミュレーション間で精度に違いがでないようにされます。

HLS math ライブラリの精度

HLS 数学関数は、hls_math.h から合成可能なビット概算関数としてインプリメントされます。ビット概算の HLS math ライブラリ関数の精度は、標準 C 関数の精度と同じにはなりません。ビット概算インプリメンテーションでは、標準 C math ライブラリ のバージョンとは異なる下位アルゴリズムを使用して結果が達成されることがあります。関数の精度は ULP (Unit of Least Precision) で指定されます。この精度の違いは、C シミュレーションと C/RTL 協調シミュレーションの両方に影響します。

ULP の違いは、通常 1-4 ULP の範囲です。

  • 標準 C math ライブラリが C ソース コードで使用されると、関数の中に標準 C math ライブラリとは ULP の異なるものがあるため、C シミュレーションと C/RTL 協調シミュレーション間で違いがあることがあります。
  • HLS math ライブラリが C ソース コードで使用される場合は、C シミュレーションと C/RTL 協調シミュレーション間で違いはありませんが、HLS math ファイルを使用した C シミュレーションは、標準 C math ライブラリを使用した C シミュレーションとは異なることがあります。

また、次の 7 つの関数の場合、コンパイルおよび C シミュレーションの実行に使用する C 標準によって精度が異なることがあります。

  • copysign
  • fpclassify
  • isinf
  • isfinite
  • isnan
  • isnormal
  • signbit

C90 モード

通常 isinfisnan、および copysign だけがシステム ヘッダー ファイルで提供され、倍精度で動作します。特に copysign は常に倍精度の結果を返します。これにより、浮動小数点を返さなければならない場合に、倍制度から浮動小数点への変換ブロックがハードウェアに導入されるので、合成後に予期しない結果になることがあります。

C99 モード (-std=c99)

7 つの関数はすべて、システム ヘッダー ファイルにより __isnan(double) および __isnan(float) にリダイレクトされるという予測のを元に提供されています。通常の GCC ファイルは isnormal をリダイレクトはしませんが、それを fpclassify を使用してインプリメントします。

math.h を使用した C++

7 つの関数はすべて、通常システム ヘッダー ファイルで提供され、倍精度で動作します。

copysign は常に倍精度の結果を返します。これにより、浮動小数点を返さなければならない場合に、倍制度から浮動小数点への変換ブロックがハードウェアに導入されるので、合成後に予期しない結果になることがあります。

cmath を使用した C++

C99 モード (mode(-std=c99)) と同様ですが、次の点が異なります。

  • システム ヘッダー ファイルが通常異なります。
  • 関数は、次の場合適切にオーバーロードされます。
    • float(). snan(double)
    • isinf(double)

copysign および copysignfnamespace std; を使用した場合でもビルトイン として処理されます。

cmath および namespace std を使用した C++

問題なし。最適な結果となるので、ザイリンクスでは次の使用をお勧めします。

  • C の場合は -std=c99
  • C および C++ の場合は -fno-builtin
注記: -std=c99 などの C コンパイル オプションを指定するには、Tcl コマンドの add_files-cflags オプションを使用します。または、[Project Settings] ダイアログ ボックスの Edit CFLAGs ボタンを使用します。

HLS math ライブラリ

HLS ビデオ ライブラリには、次の関数が含まれます。すべての関数で半精度型 (half)、単精度型 (float)、および倍精度型 (double) がサポートされます。

重要: 次にリストする関数 func には、half_func という半精度のみの関数と funcf という単精度のみの関数もライブラリに含まれます。

半精度型、単精度型、倍精度型を混合して使用する場合は、最終的な FPGA インプリメンテーションで型変換ハードウェアが使用されないように、よくある合成エラーを確認してください。

三角関数

acos acospi asin asinpi
atan atan2 atan2pi cos
cospi sin sincos sinpi
tan tanpi

双曲線関数

acosh asinh atanh cosh
sinh tanh

指数関数

exp exp10 exp2 expm1
frexp ldexp modf

対数関数

ilogb log log10 log1p

べき関数

cbrt hypot pow rsqrt
sqrt

誤差関数

erf erfc

丸め関数

ceil floor llrint llround
lrint lround nearbyint rint
round trunc

剰余関数

fmod remainder remquo

浮動小数点

copysign nan nextafter nexttoward

差分関数

fdim fmax fmin maxmag
minmag

その他の関数

abs divide fabs fma
fract mad recip

分類関数

fpclassify isfinite isinf isnan
isnormal signbit

比較関数

isgreater isgreaterequal isless islessequal
islessgreater isunordered

関係関数

all any bitselect isequal
isnotequal isordered select

固定小数点の数学関数

固定小数点のインプリメンテーションは、次の数学関数用にも提供されています。

固定小数点の数学関数すべてで、次のビット幅仕様の ap_[u]fixed および ap_[u]int 型がサポートされます。

  1. ap_fixed<W,I> (I<=33 および W-I<=32)
  2. ap_ufixed<W,I> (I<=32 および W-I<=32)
  3. ap_int<I> (I> (I<=33)
  4. ap_uint<I> (I> (I<=32)

三角関数

cos sin tan acos asin atan atan2 sincos
cospi sinpi

双曲線関数

cosh sinh tanh acosh asinh atanh

指数関数

exp frexp modf exp2 expm1

対数関数

log log10 ilogb log1p

べき関数

pow sqrt rsqrt cbrt hypot

誤差関数

erf erfc

丸め関数

ceil floor trunc round rint nearbyint

固定小数点

nextafter nexttoward

差分関数

erf erfc fdim fmax fmin maxmag minmag

その他の関数

fabs recip abs fract divide

分類関数

signbit

比較関数

isgreater isgreaterequal isless islessequal islessgreater

関係関数

isequal isnotequal any all bitselect

固定小数点型の場合、関数値の精度は少し落ちますが、RTL インプリメンテーションはより小さく高速になります。

固定小数点型の数学関数をインプリメントするには、次の手順に従います。

  1. 固定小数点のインプリメンテーションがサポートされるかどうかを判断します。
  2. 数学関数を ap_fixed 型を使用してアップデートします。
  3. C シミュレーションを実行して、デザインが必要な精度でまだ動作するかどうかを検証します。C シミュレーションは、RTL インプリメンテーションと同じビット精度型を使用して実行されます。
  4. デザインを合成します。

たとえば、sin 関数の固定小数点インプリメンテーションは、次のように数学関数と共に固定小数点型を使用して指定されます。

#include "hls_math.h"
#include "ap_fixed.h"

ap_fixed<32,2> my_input, my_output;

my_input = 24.675;
my_output = sin(my_input);

固定小数点の数学関数を使用する場合、結果の型が入力と同じ幅と整数ビットである必要があります。

検証および数学関数

標準 C math ライブラリが C ソースで使用されると、C シミュレーション結果と C/RTL 協調シミュレーション結果が異なることがあります。ソース コードの数学関数のいずれかに標準 C math ライブラリと異なる ULP がある場合、RTL がシミュレーションされたときの結果が異なってしまうことがあります。

hls_math.h ライブラリが C コードに含まれると、C シミュレーションと RTL 協調シミュレーションの結果は同じになります。ただし、hls_math.h を使用した C シミュレーションの結果は、標準 C ライブラリを使用した結果と同じにはなりません。hls_math.h ライブラリを使用すると、単に C シミュレーションが C/RTL 協調シミュレーション結果と一致するようになります。どちらの場合も同じ RTL インプリメンテーションが作成されます。次に、数学関数を使用した場合に検証を実行するために使用する可能性のあるオプションについて説明します。

検証オプション 1: 標準 math ライブラリおよび差の検証

このオプションでは、ソース コードで標準 C math ライブラリが使用されます。合成されるいずれかの関数に正確な精度がある場合は、C/RTL 協調シミュレーションは C シミュレーションと異なります。次にこの方法の例を示します。

#include <cmath>
#include <fstream>
#include <iostream>
#include <iomanip>
#include <cstdlib>
using namespace std;

typedef float data_t;

data_t cpp_math(data_t angle) {
     data_t s = sinf(angle);
     data_t c = cosf(angle);
     return sqrtf(s*s+c*c);
}

この場合、C シミュレーションと C/RTL 協調シミュレーションの結果は異なります。テストベンチで生成される結果は、シミュレーションを実行した作業ディレクトリに保存されます。

  • C シミュレーション: <project>/<solution>/csim/build フォルダー
  • C/RTL 協調シミュレーション: <project>/<solution>/sim/<RTL> フォルダー

<project> はプロジェクト フォルダー、<solution> はソリューション フォルダーの名前、<RTL> は検証される RTL のタイプ (verilog または vhdl) です。次の図に、合成前の結果ファイル (左側) と合成後の RTL 結果ファイル (右側) の典型的な比較を示します。出力は 3 列目に表示されています。

3: 合成前後のシミュレーションと合成後のシミュレーションの違い


合成前のシミュレーションと合成後のシミュレーションの結果の差はわずかです。この差が最終 RTL インプリメンテーションで許容できるものであるかどうかは、ユーザーが判断する必要があります。

これらの差を処理するには、結果をチェックするテストベンチを使用し、エラーの許容範囲内に収まっているかどうかを確認するのが推奨されるフローです。これには、同じ関数を 2 バージョン (合成用に 1 つ、参照用に 1 つ) 作成します。次の例では、cpp_math 関数のみが合成されます。


#include <cmath>
#include <fstream>
#include <iostream>
#include <iomanip>
#include <cstdlib>
using namespace std;

typedef float data_t;

data_t cpp_math(data_t angle) {
 data_t s = sinf(angle);
 data_t c = cosf(angle);
 return sqrtf(s*s+c*c);
}

data_t cpp_math_sw(data_t angle) {
 data_t s = sinf(angle);
 data_t c = cosf(angle);
 return sqrtf(s*s+c*c);
}

デザインを検証するテストベンチでは、次の例のように diff 変数を使用し、両方の関数の出力を比較してその差が判断されます。C シミュレーションでは、両方の関数の出力は同じになります。C/RTL 協調シミュレーションでは、cpp_math 関数で異なる結果が出力され、その差がチェックされます。


int main() {
 data_t angle = 0.01;
 data_t output, exp_output, diff;
 int retval=0;

 for (data_t i = 0; i <= 250; i++) {
 output = cpp_math(angle);
 exp_output = cpp_math_sw(angle);

 // Check for differences
 diff = ( (exp_output > output) ? exp_output - output : output - exp_output);
 if (diff > 0.0000005) {
 printf("Difference %.10f exceeds tolerance at angle %.10f \n", diff, angle);
 retval=1;
 }

 angle = angle + .1;
 }

 if (retval != 0) {
 printf("Test failed  !!!\n"); 
 retval=1;
 } else {
 printf("Test passed !\n");
  }
 // Return 0 if the test passes
  return retval;
}

差のマージンを 0.00000005 に下げると、このテストベンチで C/RTL 協調シミュレーション中の差が示されます。


Difference 0.0000000596 at angle 1.1100001335
Difference 0.0000000596 at angle 1.2100001574
Difference 0.0000000596 at angle 1.5100002289
Difference 0.0000000596 at angle 1.6100002527
etc..

標準 C math ライブラリ (math.h および cmath.h) を使用する場合、精度の差が許容可能かどうかを検証するためのスマート テストベンチを作成します。

検証オプション 2: HLS math ライブラリおよび差の検証

別の検証オプションは、ソース コードを HLS math ライブラリを使用するように変換する方法です。このオプションを使用する場合、C シミュレーションと C/RTL 協調シミュレーション結果に差はなくなります。次の例では、上記のコードを hls_math.h ライブラリを使用するように変更しています。

注記: このオプションは、C++ でのみ使用可能です。
  • hls_math.h ヘッダー ファイルを含めます。
  • 数学関数を同等の hls:: 関数に置き換えます。
    #include <cmath>
    #include "hls_math.h"
    #include <fstream>
    #include <iostream>
    #include <iomanip>
    #include <cstdlib>
    using namespace std;
    
    typedef float data_t;
    
    data_t cpp_math(data_t angle) {
     data_t s = hls::sinf(angle);
     data_t c = hls::cosf(angle);
     return hls::sqrtf(s*s+c*c);
    }

検証オプション 3: HLS math ライブラリ ファイルおよび違差の検証

HLS math ライブラリ ファイル lib_hlsm.cpp をデザイン ファイルとして含めると、Vitis HLS で C シミュレーションに HLS math ライブラリが使用されます。このオプションはオプション 2 と同じですが、C コードを変更する必要はありません。

HLS math ライブラリ ファイルは、Vitis HLS インストール ディレクトリの src ディレクトリに含まれます。ファイルをローカル フォルダーにコピーして、ファイルを標準のデザイン ファイルとして追加します。

注記: このオプションは、C++ でのみ使用可能です。

このオプションを使用すると、オプション 2 を使用した場合と同様に HLS math ファイルを使用した C シミュレーション結果と、このファイルを追加しない前に取得したシミュレーション結果が異なるものになります。これらの差は、オプション 1 と同様のスマート テストベンチを使用して C シミュレーションで検証する必要があります。

よくある合成エラー

次は数学関数を合成する際によく発生する使用エラーです。これらは数学関数の合成の利点を生かすために、C 関数を C++ 関数へ変換した場合によく発生します。

C++ の cmath.h

C++ の cmath.h ヘッダー ファイルが使用されると、浮動小数点関数 (sinf および cosf など) が使用できます。これらはハードウェアで 32 ビット演算になります。cmath.h ヘッダー ファイルは標準関数 (sincos など) もオーバーロードするので、float および double 型にも使用できます。

C の math.h

C の math.h ライブラリが使用される場合は、32 ビットの浮動小数点演算を合成するために、単精度関数 (sinfcosf など) が必要です。すべての標準関数呼び出し (sincos など) は合成されると倍精度および 64 ビットの倍精度演算になります。

注意

math.h サポートの利点を生かすために C 関数を C++ に変換する場合は、Vitis HLS で合成する前に新しい C++ コードが正しくコンパイルされるようにする必要があります。たとえば、sqrtf()math.h と一緒にコードで使用される場合は、それをサポートするために次のコードを C++ コードに追加する必要があります。


#include <math.h>
extern “C” float sqrtf(float);

「float および double 型」で説明したように、float および double 型の混合に関する警告メッセージに従って、型変換によって不必要なハードウェアが作成されないようにします。

HLS ストリーム ライブラリ

ストリーミング データとは、データ サンプルが最初のサンプルからシーケンシャル順に送信されるタイプのデータ転送のことです。ストリーミングには、アドレス管理は必要ありません。

ストリーミング データを使用するデザインは C で記述するのが困難な場合があります。複数の読み出しおよび書き込みを実行するためにポインターを使用すると、型修飾子とテストベンチの構築方法が記述されるので、問題が発生する可能性があります。

Vitis HLS には、ストリーミング データ構造を記述するための C++ テンプレート クラスの hls::stream<> が含まれます。hls::stream<> クラスを使用してインプリメントしたストリームには、次の属性があります。

  • C コードでは、hls::stream<> が無限深さの FIFO のように動作します。hls::stream<> のサイズを定義する必要はありません。
  • これらは、シーケンシャルに読み出されて書き込まれます。つまり、データが hls::stream<> から読み出されると、それが再び読み出されることはありません。
  • 最上位インターフェイスの hls::stream<>ap_fifo インターフェイスを使用してデフォルトでインプリメントされます。
  • hls::stream<> は、深さ 2 の FIFO としてインプリメントされます。このデフォルト サイズを変更するには、最適化指示子の STREAM を使用します。

このセクションでは、ストリーミング データを使用したデザインを hls::stream<> クラスを使用してより簡単に記述する方法を示します。このセクションには、次のトピックが含まれます。

  • ストリームを使用した記述とストリームの RTL インプリメンテーションの概要。
  • グローバル ストリーム変数の規則。
  • ストリームの使用方法。
  • ブロッキング読み出しおよび書き込み。
  • ノンブロッキング読み出しおよび書き込み。
  • FIFO の深さの制御。
注記: hls::stream クラスは常に C++ 参照引数として関数間で使用されます。たとえば、&my_stream がその例です。
重要: hls::stream クラスは、C++ デザインでのみ使用されます。stream 配列はサポートされません。

C 記述と RTL インプリメンテーション

ストリームは、ソフトウェア (および RTL 協調シミュレーション中のテストベンチ) では無限大のキューとして記述されます。ストリームを C++ でシミュレーションするために、深さを指定する必要はありません。ストリームは関数内または関数へのインターフェイスに使用できます。内部ストリームは関数パラメーターとして渡すことができます。

ストリームは C++ ベースのデザインでのみ使用できます。各 hls::stream<> オブジェクトは 1 プロセスで書き込まれ、1 プロセスで読み出されるようにする必要があります。

hls::stream が最上位インターフェイスに使用されると、RTL ではデフォルトで FIFO インターフェイス (ap_fifo) としてインプリメントされますが、ハンドシェイク インターフェイス (ap_hs) または AXI-Stream インターフェイス (axis) としてもインプリメントできます。

hls::stream がデザイン関数内で使用され、ハードウェアに合成される場合は、デフォルトで深さ 2 の FIFO としてインプリメントされます。補間が使用される場合など、ハードウェアで生成されるエレメントすべてが保持できるように FIFO の深さを増加する必要のあることもあります。ハードウェアで生成されるデータ サンプルすべてを保持するのに十分な大きさがないと、FIFO が停止する可能性があります (C/RTL 協調シミュレーションおよびハードウェア インプリメンテーションで確認できます)。FIFO の深さは、STREAM 指示子に depth オプションを使用して調整できます。この例は、デザイン例 hls_stream に示されています。

重要: デフォルトの DATAFLOW 以外の領域で hls::stream 変数を使用する場合は、正しくサイズ指定するようにしてください。

hls::stream をタスク (サブ関数またはループ) 間のデータ転送に使用する場合は、データが 1 つのタスクから次のタスクに流れる DATAFLOW 領域にタスクをインプリメントすることを考慮してみてください。デフォルト (DATAFLOW 以外) では、各タスクが次のタスクの開始前に完了するので、hls::stream 変数をインプリメントするのに使用した FIFO のサイズをプロデューサー タスクで生成されるデータ サンプルすべてを保持すのに十分な大きさにする必要があります。hls::stream 変数のサイズを増加しないと、次のようなエラー メッセージが表示されます。


ERROR: [XFORM 203-733] An internal stream xxxx.xxxx.V.user.V' with default size is 
used in a non-dataflow region, which may result in deadlock. Please consider to 
resize the stream using the directive 'set_directive_stream' or the 'HLS stream' 
pragma.

このエラー メッセージは、DATAFLOW でない領域 (デフォルトの FIFO の深さ 2) がプロデューサー タスクにより FIFO に書き込まれるデータ サンプルすべてを保持するのに十分な大きさではない可能性があることを示しています。

グローバルおよびローカル ストリーム

ストリームは、ローカルまたはグローバルのいずれかに定義できます。ローカル ストリームは常に内部 FIFO としてインプリメントされます。グローバル ストリームは内部 FIFO またはポートとしてインプリメントされる可能性があります。

  • 読み出し専用または書き込み専用のグローバルに定義されたストリームは、最上位 RTL ブロックの外部ポートとして推論されます。
  • 最上位関数より下位の階層での読み出しおよび書き込み両方用のグローバルに定義されたストリームは、内部 FIFO としてインプリメントされます。

グローバル スコープで定義されるストリームは、その他のグローバル変数と同じ規則に従います。

HLS ストリームの使用

hls::stream<> オブジェクトを使用するには、hls_stream.h ヘッダー ファイルを含めます。ストリーミング データ オブジェクトは、型と変数名を指定して定義します。次の例では、128 ビットの符号なし整数型が定義されており、my_wide_stream というストリーム変数を作成するために使用されています。


#include "ap_int.h"
#include "hls_stream.h"

typedef ap_uint<128> uint128_t;  // 128-bit user defined type
hls::stream<uint128_t> my_wide_stream;  // A stream declaration

ストリームでは、スコープ付きの命名規則を使用する必要があるので、ザイリンクスでは、上記の例のようにスコープ付き (hls::) 命名規則を使用することを勧めしますが、hls 名前空間を使用する場合は、前述の例を次のように書き直してください。


#include <ap_int.h>
#include <hls_stream.h>
using namespace hls;

typedef ap_uint<128> uint128_t;  // 128-bit user defined type
stream<uint128_t> my_wide_stream;  // hls:: no longer required

ストリームを hls::stream<T> として指定すると、T のデータ型は次のいずれかになります。

  • C++ ネイティブ データ型
  • Vitis HLS 任意精度型 (例: ap_int<>、ap_ufixed<>)
  • 上記のいずれかの型を含んだユーザー定義の構造体
注記: メソッド (メンバー関数) を含む通常のユーザー定義のクラス (または構造) は、ストリーム変数の型 (T) として使用されるべきではありません。

ストリームは hls::stream<Type, Depth> を使用して指定することもできます。ここで、Depth は HLS ツールで RTL 協調シミュレーション用に作成される検証アダプターに必要な FIFO の深さを示します。

ストリームには、オプションで名前を付けることができます。ストリームに名前を定義すると、その名前がレポートで使用されるようになります。たとえば、Vitis HLS では入力ストリームからのすべてのエレメントがシミュレーション中に読み出されるようになっているかどうかが自動的に確認されます。次の 2 つのストリームがあるとします。


stream<uint8_t> bytestr_in1;
stream<uint8_t> bytestr_in2("input_stream2");

WARNING: Hls::stream 'hls::stream<unsigned char>.1' contains leftover data, which 
may result in RTL simulation hanging.
WARNING: Hls::stream 'input_stream2' contains leftover data, which may result in RTL 
simulation hanging.
Any warning on elements left in the streams are reported as follows, where
         it is clear which message relates to bytetr_in2:

ストリームが関数を出入りする際は、次の例のようにリファレンスごとに渡される必要があります。


   void stream_function (
         hls::stream<uint8_t> &strm_out,
         hls::stream<uint8_t> &strm_in,
        uint16_t strm_len
       )

Vitis HLS では、ブロッキングおよびノンブロッキング アクセスのメソッド両方がサポートされます。

  • ノンブロッキング アクセスは、FIFO インターフェイスとしてのみインプリメントできます。
  • ap_fifo ポートとしてインプリメントされるストリーミング ポートと AXI4-Stream リソースを使用して定義されるストリーミング ポートには、ノンブロッキング アクセスを使用しないでください。

ストリームを使用したデザイン全体の例は、Vitis HLS のサンプル デザイン例に含まれます。GUI の Welcome ページからデザイン例に含まれる hls_stream の例を参照してください。

読み出しおよび書き込みのブロッキング

hls::stream<> オブジェクトへの基本的なアクセスは読み出しおよび書き込みのブロッキングで、これはクラス メソッドを使用して達成できます。これらのメソッドは、空のストリーム FIFO からの読み出しや、フル ストリーム FIFO への書き込みが実行されようとした場合、または ap_hs インターフェイス プロトコルにマップされたストリームに対してフル ハンドシェイクが達成されるまで、実行を停止 (ブロック) します。

トランザクションの進捗なしにシミュレータの実行が続くと、C/RTL 協調シミュレーションが停止することがあります。次は、停止をする典型的な例で、RTL シミュレーション時間が増加しても内部トランザクションの進捗がありません。


// RTL Simulation : "Inter-Transaction Progress" ["Intra-Transaction Progress"] @ 
"Simulation Time"
///////////////////////////////////////////////////////////////////////////////////
// RTL Simulation : 0 / 1 [0.00%] @ "110000"
// RTL Simulation : 0 / 1 [0.00%] @ "202000"
// RTL Simulation : 0 / 1 [0.00%] @ "404000"
書き込みメソッドのブロッキング

次の例では、src_var 変数がストリームに含まれています。


// Usage of void write(const T & wdata)

hls::stream<int> my_stream;
int src_var = 42;

my_stream.write(src_var);

<< 演算子はオーバーロードされているので、C++ ストリームのストリーム挿入演算子 (例: iostreams、filestreams など) と同様の方法で使用できます。書き込まれる hls::stream<> オブジェクトは、左側に引数として、書き込まれる値は右側に記述されます。


// Usage of void operator << (T & wdata)

hls::stream<int> my_stream;
int src_var = 42;

my_stream << src_var;

読み出しメソッドのブロッキング

このメソッドでは、ストリームの冒頭から読み出し、値を dst_var 変数に代入します。


// Usage of void read(T &rdata)

hls::stream<int> my_stream;
int dst_var;

my_stream.read(dst_var);

または、ストリームの次のオブジェクトは、そのストリームを左側のオブジェクトに代入 (=、+=、などを使用) すると読み出すことができます。


// Usage of T read(void)

hls::stream<int> my_stream;

int dst_var = my_stream.read();

>> 演算子はオーバーロードされ、C++ ストリームのストリーム抽出演算子 (例: iostreams、filestreams など) のように使用できます。hls::stream は LHS 引数、およびデスティネーション変数の RHS として提供されています。


// Usage of void operator >> (T & rdata)

hls::stream<int> my_stream;
int dst_var;

my_stream >> dst_var;

ノンブロッキング読み出しおよび書き込み

読み出しおよび書き込みのノンブロッキング メソッドも提供されています。これにより、空のストリームからの読み出しやフル ストリームへの書き込みがあっても、実行を続行させることができます。

これらのメソッドは、アクセスのステータスを示すブール値を返します (問題ない場合は true、問題がある場合は false)。hls::stream<> ストリームのステータスをテストするために、別のメソッドも提供されています。

重要: ノンブロッキング ビヘイビアーは、ap_fifo プロトコルを使用したインターフェイスでのみサポートされます。つまり、AXI-Stream 規格とザイリンクスap_hs IO プロトコルでは、ノンブロッキング アクセスはサポートされません。

C シミュレーション中、ストリームには無限サイズが含まれます。このため、ストリームがフルの場合は C シミュレーションで検証はできません。これらのメソッドは、FIFO サイズ (デフォルト サイズの 1 か STREAM 指示子で定義された任意のサイズ) が定義された場合の RTL シミュレーション中にのみ検証できます。

重要: ブロック レベルの I/O プロトコルの ap_ctrl_none を使用するように指定されていて、デザインにブロッキング以外の動作を使用する hls::stream が含まれる場合は、C/RTL 協調シミュレーションが必ず終了するとは限りません。
ノンブロッキング書き込み

このメソッドは、src_var 変数を my_stream ストリームに挿入し、正しく挿入された場合はブール値 true が返されます。それ以外の場合は false が返され、キューは変更されません。


// Usage of void write_nb(const T & wdata)

hls::stream<int> my_stream;
int src_var = 42;

if (my_stream.write_nb(src_var)) {
 // Perform standard operations
 ...
} else {
 // Write did not occur
 return;
}

フルかどうかのテスト
bool full(void)

このメソッドは、hls::stream<> オブジェクトがフルの場合にのみ true を返します。

// Usage of bool full(void)

hls::stream<int> my_stream;
int src_var = 42;
bool stream_full;

stream_full = my_stream.full();
ノンブロッキング読み出し
bool read_nb(T & rdata)

このメソッドはストリームから値を読み出し、正しく読み出せた場合は true が返されます。それ以外の場合は false が返され、キューは変更されません。

// Usage of void read_nb(const T & wdata)

hls::stream<int> my_stream;
int dst_var;

if (my_stream.read_nb(dst_var)) {
 // Perform standard operations
 ...
} else {
 // Read did not occur
 return;
}
空かどうかのテスト
bool empty(void)

hls::stream<> が空の場合に true を返します。

// Usage of bool empty(void)

hls::stream<int> my_stream;
int dst_var;
bool stream_empty;

stream_empty = my_stream.empty();

次の例は、ノンブロッキング アクセスとフル/空のテストを組み合わせて、RTL FIFO がフルまたは空の場合にエラーをどのように処理するかを示しています。

#include "hls_stream.h"
using namespace hls;

typedef struct {
   short    data;
   bool     valid;
   bool     invert;
} input_interface;

bool invert(stream<input_interface>& in_data_1,
            stream<input_interface>& in_data_2,
            stream<short>& output
  ) {
  input_interface in;
  bool full_n;

// Read an input value or return
  if (!in_data_1.read_nb(in))
      if (!in_data_2.read_nb(in))
          return false;

// If the valid data is written, return not-full (full_n) as true
  if (in.valid) {
    if (in.invert)
      full_n = output.write_nb(~in.data);
    else
      full_n = output.write_nb(in.data);
  }
  return full_n;
}

RTL の FIFO の深さの制御

ストリーミング データを使用するほとんどのデザインでは、デフォルトの RTL FIFO の深さ 2 は十分な深さです。これは、ストリーミング データが通常 一度に 1 サンプルを処理するからです。

インプリメンテーションで FIFO の深さが 2 より多く必要なマルチレート デザインの場合は、STREAM 指示子を使用して RTL シミュレーションが終了するために必要な深さを設定する必要があります。FIFO の深さが十分でない場合、RTL 協調シミュレーションは停止します。

このため、ストリーム オブジェクトは GUI の [Directive] タブには表示されないので、STREAM 指示子を [Directive] タブからは直接適用できません。

hls::stream<> オブジェクトが宣言されている (または引数リストに使用されているか存在している) 関数で右クリックします。

  • STREAM 指示子を選択します。
  • variable フィールドには、そのストリーム変数名を入力します。

または、次のいずれかを実行します。

  • directives.tcl ファイルで STREAM 指示子を手動で指定します。
  • source に pragma として追加します。

C/RTL 協調シミュレーションのサポート

Vitis HLS の C/RTL 協調シミュレーション機能では、最上位インターフェイスに hls::stream<> メンバーを含む構造またはクラスはサポートされません。Vitis HLS ではこれらの構造およびクラスは合成でサポートされます。


typedef struct {
   hls::stream<uint8_t> a;
   hls::stream<uint16_t> b;
} strm_strct_t;

void dut_top(strm_strct_t indata, strm_strct_t outdata) { … }

これらの制限は、最上位関数の引数とグローバルに宣言されたオブジェクトの両方に適用されます。ストリームの構造体が合成に使用される場合は、外部の RTL シミュレータとユーザーの作成した HDL テストベンチを使用してデザインを検証する必要があります。内部リンケージのみの hls::stream<> オブジェクトには、このような制限はありません。

HLS IP ライブラリ

Vitis HLS には、多くのザイリンクス IP ブロックをインプリメントするための C ライブラリが提供されています。この C ライブラリを使用すると、次のザイリンクス IP ブロックが C ソース コードから直接推論でき、FPGA で高品質のインプリメンテーションになるようになります。

表 4. HLS IP ライブラリ
ライブラリ ヘッダー ファイル 説明
hls_fft.h ザイリンクス LogiCORE IP FFT を C でシミュレーション、ザイリンクス LogiCORE ブロックを使用してインプリメント可能。
hls_fir.h ザイリンクス LogiCORE IP FIR を C でシミュレーション、ザイリンクス LogiCORE ブロックを使用してインプリメント可能。
hls_dds.h ザイリンクス LogiCORE IP DDS を C でシミュレーション、ザイリンクス LogiCORE ブロックを使用してインプリメント可能。
ap_shift_reg.h ザイリンクス SRL プリミティブを使用して直接インプリメントされるシフト レジスタをインプリメントするための C++ クラスを提供。

FFT IP ライブラリ

ザイリンクス FFT ブロックは、hls_fft.h ライブラリを使用すると C++ デザイン内で呼び出すことができます。このセクションでは、FFT を C++ コードで設定する方法を説明します。

注記: ザイリンクスでは、この IP の機能のインプリメント方法および機能の使用方法については、『Fast Fourier Transform LogiCORE IP 製品ガイド』 (PG109) を参照することをお勧めしています。

C++ コードで FFT を使用するには、次の手順に従います。

  1. コードに hls_fft.h ライブラリを含めます
  2. 定義済み構造体 hls::ip_fft::params_t を使用してデフォルト パラメーターを設定します。
  3. ランタイム コンフィギュレーションを定義します。
  4. FFT 関数を呼び出します。
  5. ランタイム ステータスをチェックします (オプション)。

次のコード例に、これらの各手順の実行方法を示します。各手順の詳細は、次のとおりです。

まず、ソース コードに FFT ライブラリを含めます。このヘッダー ファイルは、Vitis HLS のインストール ディレクトリの include ディレクトリに含まれています。このディレクトリは、Vitis HLS が実行されると自動的に検索されます。

#include "hls_fft.h"

FFT のスタティック パラメーターを定義します。これには、動的に変化しない入力幅、チャネル数、アーキテクチャ タイプなどが含まれます。FFT ライブラリにはパラメーター指定構造体 hls::ip_fft::params_t が含まれ、すべてのスタティック パラメーターをデフォルト値で初期化できます。

この例では、出力順序、コンフィギュレーション ポートとステータス ポートの幅のデフォルト値を、定義済み構造体に基づくユーザー定義の構造体 param1 を使用して変更しています。

struct param1 : hls::ip_fft::params_t {
    static const unsigned ordering_opt = hls::ip_fft::natural_order;
    static const unsigned config_width = FFT_CONFIG_WIDTH;
    static const unsigned status_width = FFT_STATUS_WIDTH;
};

ランタイム コンフィギュレーションとランタイム ステータスのデータ型と変数を定義します。これらの値は動的に変化する可能性があるので、変更可能で API からアクセス可能な C コードの変数として定義します。

typedef hls::ip_fft::config_t<param1> config_t;
typedef hls::ip_fft::status_t<param1> status_t;
config_t fft_config1;
status_t fft_status1;

次に、ランタイム コンフィギュレーションを設定します。この例では、direction 変数の値に基づいて FFT の方向 (前方向または逆方向) を設定し、スケーリング スケジュールの値も設定しています。

fft_config1.setDir(direction);
fft_config1.setSch(0x2AB);

HLS 名前空間と定義済みスタティック コンフィギュレーション (この例では param1) を使用して FFT 関数を呼び出します。関数のパラメーターは、順に入力データ、出力データ、出力ステータス、入力コンフィギュレーションを示します。

hls::fft<param1> (xn1, xk1, &fft_status1, &fft_config1);

最後に出力ステータスをチェックします。この例では、オーバーフロー フラグがチェックされて、結果が ovflo 変数に格納されます。

    *ovflo = fft_status1->getOvflo();

FFT C ライブラリを使用したデザイン例は、HelpWelcomeOpen Example ProjectDesign ExamplesFFT をクリックして表示される Vitis HLS のサンプルに含まれます。

FFT のスタティック パラメーター

FFT のスタティック パラメーターでは、FFT のコンフィギュレーション方法、FFT のサイズ、サイズが動的に変更可能かどうか、インプリメンテーションがパイプライン処理されるか radix_4_burst_io が使用されるかなどの固定パラメーターが指定されます。

hls_fft.h ヘッダー ファイルでは、スタティック パラメーターのデフォルト値を設定するのに使用可能な hls::ip_fft::params_t という構造体が定義されています。デフォルト値を使用する場合は、FFT 関数と共にパラメーター指定構造体を直接使用できます。


 hls::fft<hls::ip_fft::params_t >  
     (xn1, xk1, &fft_status1, &fft_config1);

より一般的には、パラメーターの一部がデフォルト値以外の値に変更されます。これには、デフォルトのパラメーター指定構造体に基づく新しいユーザー定義のパラメーター指定構造体を作成し、一部のデフォルト値を変更します。

次の例では、新しいユーザー構造体 my_fft_config を定義し、出力順序を新しい値 (natural_order に変更) に変更しています。FFT のその他すべてのスタティック パラメーターにはデフォルト値が使用されます。


struct my_fft_config : hls::ip_fft::params_t {
    static const unsigned ordering_opt = hls::ip_fft::natural_order;
};

hls::fft<my_fft_config >  
     (xn1, xk1, &fft_status1, &fft_config1);

パラメーター指定構造体 hls::ip_fft::params_t については、FTT の構造体パラメーターを参照してください。パラメーターのデフォルト値および使用可能な値のリストは、FFT の構造体パラメーターの値 を参照してください。

注記: ザイリンクスでは、パラメーターの詳細およびその設定の影響については、『Fast Fourier Transform LogiCORE IP 製品ガイド』 (PG109) を参照することをお勧めしています。
FTT の構造体パラメーター
表 5. FTT の構造体パラメーター
パラメーター 説明
input_width データ入力ポート幅。
output_width データ出力ポート幅。
status_width 出力ステータス ポート幅。
config_width 入力コンフィギュレーション ポート幅。
max_nfft FFT データ セットのサイズを 1 << max_nfft に指定します。
has_nfft FFT のサイズをランタイムに設定できるようにするかどうかを指定します。
channels チャネル数。
arch_opt インプリメンテーション アーキテクチャ。
phase_factor_width 内部位相係数の精度を設定します。
ordering_opt 出力順序モード。
ovflo オーバーフロー モードをイネーブルにします。
scaling_opt スケーリング オプションを定義します。
rounding_opt 丸めモードを定義します。
mem_data データ メモリにブロック RAM を使用するか分散 RAM を使用するかを指定します。
mem_phase_factors 位相係数メモリにブロック RAM を使用するか分散 RAM を使用するかを指定します。
mem_reorder 出力順序並べ替えメモリにブロック RAM を使用するか分散 RAM を使用するかを指定します。
stages_block_ram インプリメンテーションで使用されるブロック RAM の段数を定義します。
mem_hybrid ブロック RAM がデータ、位相係数、または順序並べ替えバッファーに指定されている場合に、ブロック RAM と分散 RAM のハイブリッドを使用して特定のコンフィギュレーションでのブロック RAM 数を削減するかどうかを指定します。
complex_mult_type 複素乗算器に使用する乗算器のタイプを定義します。
butterfly_type FFT バタフライに使用されるインプリメンテーションを定義します。

整数またはブール型ではないパラメーター値を指定する際は、HLS FFT 名前空間を使用する必要があります。

たとえば、次の表の butterfly_type パラメーターに使用可能な値は use_luts および use_xtremedsp_slices です。C プログラムでは butterfly_type = hls::ip_fft::use_luts および butterfly_type = hls::ip_fft::use_xtremedsp_slices を使用する必要があります。

FFT の構造体パラメーターの値

次の表に、FFT IP のすべての機能を示します。この表に示されていない機能は、Vitis HLS のインプリメンテーションではサポートされていません。

表 6. FFT の構造体パラメーターの値
パラメーター C データ型 デフォルト値 有効値
input_width unsigned 16 8-34
output_width unsigned 16 input_width ~ (input_width + max_nfft + 1)
status_width unsigned 8 FFT コンフィギュレーションによって異なる
config_width unsigned 16 FFT コンフィギュレーションによって異なる
max_nfft unsigned 10 3-16
has_nfft bool false True、False
channels unsigned 1 1-12
arch_opt unsigned pipelined_streaming_io

automatically_select

pipelined_streaming_io

radix_4_burst_io

radix_2_burst_io

radix_2_lite_burst_io

phase_factor_width unsigned 16 8-34
ordering_opt unsigned bit_reversed_order

bit_reversed_order

natural_order

ovflo bool true

false

true

scaling_opt unsigned scaled

scaled

unscaled

block_floating_point

rounding_opt unsigned truncation

truncation

convergent_rounding

mem_data unsigned block_ram

block_ram

distributed_ram

mem_phase_factors unsigned block_ram

block_ram

distributed_ram

mem_reorder unsigned block_ram

block_ram

distributed_ram

stages_block_ram unsigned

(max_nfft < 10) ? 0:

(max_nfft - 9)

0-11
mem_hybrid bool false

false

true

complex_mult_type unsigned use_mults_resources

use_luts

use_mults_resources

use_mults_performance

butterfly_type unsigned use_luts

use_luts

use_xtremedsp_slices

FFT ランタイム コンフィギュレーションとランタイム ステータス

FFT では、コンフィギュレーション ポートとステータス ポートを介したランタイム コンフィギュレーションおよびランタイム ステータスの監視がサポートされます。これらのポートは FFT 関数への引数として定義され、次の例では fft_status1 および fft_config1 変数として記述されています。


hls::fft<param1> (xn1, xk1, &fft_status1, &fft_config1);

ランタイム コンフィギュレーションとランタイム ステータスには、FFT の C ライブラリから定義済み構造体を使用してアクセスできます。

  • hls::ip_fft::config_t<param1>
  • hls::ip_fft::status_t<param1>
注記: どちらの場合も、構造体にはスタティック パラメーター指定構造体が必要で、例では param1 と記述されています。スタティック パラメーター指定構造体の詳細は、前のセクションを参照してください。

ランタイム コンフィギュレーション構造体を使用すると、C コードで次を実行できます。

  • ランタイム コンフィギュレーションがイネーブルの場合は、FFT 長を設定
  • FFT の方向を前方向または逆方向に設定
  • スケーリング スケジュールを設定

FFT 長は、次のように設定できます。

typedef hls::ip_fft::config_t<param1> config_t;
config_t fft_config1;
// Set FFT length to 512 => log2(512) =>9
fft_config1-> setNfft(9);
重要: ランタイム中に指定する長さは、スタティック コンフィギュレーションの max_nfft で定義されているサイズを超えることはできません。

FFT の方向は、次のように設定できます。


typedef hls::ip_fft::config_t<param1> config_t;
config_t fft_config1;
// Forward FFT
fft_config1->setDir(1);
// Inverse FFT 
fft_config1->setDir(0);

FFT のスケーリング スケジュールは、次のように設定できます。


typedef hls::ip_fft::config_t<param1> config_t;
config_t fft_config1;
fft_config1->setSch(0x2AB);

出力ステータス ポートには、次を判断するために、定義済み構造体を使用してアクセスできます。

  • FFT 中にオーバーフローが発生したかどうか
  • ブロックの指数値

FFT オーバーフロー モードは、次のようにチェックできます。


typedef hls::ip_fft::status_t<param1> status_t;
status_t fft_status1;
// Check the overflow flag
bool *ovflo = fft_status1->getOvflo();

重要: 各トランザクションが完了した後、オーバーフロー ステータスを確認して FFT が正しく動作していることを確認してください。

ブロックの指数値は、次を使用すると取得できます。


typedef hls::ip_fft::status_t<param1> status_t;
status_t fft_status1;
// Obtain the block exponent
unsigned int *blk_exp = fft_status1-> getBlkExp();

FFT 関数の使用

FFT 関数は、HLS 名前空間で定義され、次のように呼び出すことができます。


hls::fft<STATIC_PARAM> (
INPUT_DATA_ARRAY,
OUTPUT_DATA_ARRAY, 
OUTPUT_STATUS, 
INPUT_RUN_TIME_CONFIGURATION);

STATIC_PARAM は、FFT のスタティック パラメーターを定義するスタティック パラメーター指定構造体です。

入力データおよび出力データは、配列 (INPUT_DATA_ARRAY および OUTPUT_DATA_ARRAY) として関数に供給されます。最終的なインプリメンテーションでは、FFT RTL ブロックのポートは AXI4-Stream ポートとしてインプリメントされます。データフロー最適化 (set_directive_dataflow) を使用した領域では、配列がストリーミング配列としてインプリメントされるので、ザイリンクスでは常に FFT 関数を使用することを勧めします。または、両方の配列を set_directive_stream コマンドを使用してストリーミングとして指定します。

重要: FFT は、パイプラインされた領域では使用できません。高度なパフォーマンスの演算が必要な場合、FFT の前後でループまたは関数をパイプラン処理して、その領域のループおよび関数すべてでデータフロー最適化を使用します。

配列のデータ型は、float または ap_fixed のいずれかにできます。


typedef float data_t;
complex<data_t> xn[FFT_LENGTH];
complex<data_t> xk[FFT_LENGTH];

固定小数点型を使用する場合は、Vitis HLS 任意精度型 ap_fixed を使用する必要があります。


#include "ap_fixed.h"
typedef ap_fixed<FFT_INPUT_WIDTH,1> data_in_t;
typedef ap_fixed<FFT_OUTPUT_WIDTH,FFT_OUTPUT_WIDTH-FFT_INPUT_WIDTH+1> data_out_t;
#include <complex>
typedef hls::x_complex<data_in_t> cmpxData;
typedef hls::x_complex<data_out_t> cmpxDataOut;

どちらの場合でも、FFT のパラメーターを同じ正しいデータ サイズに指定する必要があります。浮動小数点型の場合、データ幅は常に 32 ビットで、その他のサイズを指定しても無効と判断されます。

重要: FFT の入力および出力幅は、サポートされる範囲内の任意の値に設定できます。入力および出力パラメーターに接続される変数は、8 ビット単位で定義する必要があります。たとえば出力幅を 33 ビットに設定する場合は、出力変数は 40 ビット変数として定義する必要があります。

FFT のマルチチャネル機能は、入力データと出力データの 2 次元配列を使用すると使用できます。この場合、最初の次元が各チャネル、2 つ目の次元が FFT データを表すように配列データを設定する必要があります。


typedef float data_t;
static complex<data_t> xn[CHANNEL][FFT_LENGTH];
static complex<data_t> xk[CHANELL][FFT_LENGTH];

FFT コアは、データをインターリーブされたチャネル (たとえば、ch0-data0、ch1-data0、ch2-data0 など、ch0-data1、ch1-data1、ch2-data2、など) として生成および消費します。このため、FFT の入力配列または出力配列をデータを読み出しおよび書き込みするのと同じ順序でストリーミングするには、次の例に示すように、まずチャネル インデックスを順に処理することにより、複数のチャネルの 2 次元配列を充填または空にする必要があります。


cmpxData   in_fft[FFT_CHANNELS][FFT_LENGTH];
cmpxData  out_fft[FFT_CHANNELS][FFT_LENGTH];
 
// Write to FFT Input Array
for (unsigned i = 0; i < FFT_LENGTH; i++) {
 for (unsigned j = 0; j < FFT_CHANNELS; ++j) {
 in_fft[j][i] = in.read().data;
 }
}
   
// Read from FFT Output Array
for (unsigned i = 0; i < FFT_LENGTH; i++) {
 for (unsigned j = 0; j < FFT_CHANNELS; ++j) {
 out.data = out_fft[j][i];
 
 }
}

FFT C ライブラリを使用したデザイン例は、HelpWelcomeOpen Example ProjectDesign ExamplesFFT をクリックして表示される Vitis HLS のサンプルに含まれます。

FIR フィルター IP ライブラリ

ザイリンクス FIR ブロックは、hls_fir.h ライブラリを使用すると C++ デザイン内で呼び出すことができます。このセクションでは、FIR を C++ コードで設定する方法を説明します。

注記: ザイリンクスでは、この IP の機能のインプリメント方法および機能の使用方法について、『FIR Compiler LogiCORE IP 製品ガイド』 (PG149) を確認することをお勧めしています。

C++ コードで FIR を使用するには、次の手順に従います。

  1. コードに hls_fir.h ライブラリを含めます。
  2. 定義済み構造体 hls::ip_fir::params_t を使用してスタティック パラメーターを設定します。
  3. FIR 関数を呼び出します。
  4. ランタイム入力コンフィギュレーションを定義してパラメーターの一部を動的に変更します (オプション)。

次のコード例に、これらの各手順の実行方法を示します。各手順の詳細は、次のとおりです。

まず、ソース コードに FIR ライブラリを含めます。このヘッダー ファイルは、Vitis HLS インストール ディレクトリの include ディレクトリにあります。このファイルは、Vitis HLS を実行すると自動的に検索されます。Vitis HLS 内でコンパイルする場合は、このディレクトリへのパスを指定する必要はありません。

#include "hls_fir.h"

FIR のスタティック パラメーターを定義します。これには、入力幅、係数、フィルター レート (singledecimationhilbert) などのスタティック属性が含まれます。FIR ライブラリにはパラメーター指定構造体 hls::ip_fir::params_t が含まれ、すべてのスタティック パラメーターをデフォルト値で初期化できます。

次の例では、係数を coeff_vec 配列として定義し、定義済み構造体に基づくユーザー定義構造体 myconfig を使用して、係数値、入力幅、および量子化モードのデフォルト値を変更しています。

struct myconfig : hls::ip_fir::params_t {
static const double coeff_vec[sg_fir_srrc_coeffs_len];
    static const unsigned num_coeffs = sg_fir_srrc_coeffs_len;
    static const unsigned input_width = INPUT_WIDTH; 
    static const unsigned quantization = hls::ip_fir::quantize_only;
};

HLS 名前空間と定義済みスタティック パラメーター (この例では myconfig) を使用して FIR 関数のインスタンスを作成し、run メソッドで関数を呼び出して関数を実行します。関数引数は、順に入力データ、出力データです。

static hls::FIR<param1> fir1;
fir1.run(fir_in, fir_out);

オプションで、ランタイム入力コンフィギュレーションを使用できます。FIR のモードには、インターリーブされたチャネルで係数がどのように使用されるか、または係数の再読み込みがいつ必要かが、この入力のデータによって決定されるものがあります。これは動的に設定できるので、変数として定義されます。この入力コンフィギュレーションが必要なモードの詳細は、『FIR Compiler LogiCORE IP 製品ガイド』 (PG149) を参照してください。

ランタイム入力コンフィギュレーションを使用する場合、FIR 関数を入力データ、出力データ、および入力コンフィギュレーションの 3 つの引数を使用して呼び出します。

// Define the configuration type
typedef ap_uint<8> config_t;
// Define the configuration variable
config_t fir_config = 8;
// Use the configuration in the FFT
static hls::FIR<param1> fir1;
fir1.run(fir_in, fir_out, &fir_config);

FIR C ライブラリを使用したデザイン例は、 HelpWelcomeOpen Example ProjectDesign ExamplesFIR をクリックして表示される Vitis HLS のサンプルに含まれます。

FIR のスタティック パラメーター

FIR のスタティック パラメーターは、入力幅と出力幅、小数ビット数、係数値、補間レートおよび間引きレートなどの変化しない値を指定します。これらのほとんどにデフォルト値がありますが、係数にはデフォルト値はありません。

hls_fir.h ヘッダー ファイルでは、スタティック パラメーターのデフォルト値を設定するのに使用可能な struct hls::ip_fir::params_t が定義されます。

重要: 係数にはデフォルト値は定義されないので、ザイリンクスでは、FIR を直接初期化するために定義済みの構造体を使用することはお勧めしていません。スタティック パラメーターを指定するには、係数を指定する新しいユーザー定義構造体を使用する必要があります。

次の例では、新しい係数値を指定する新しいユーザーの struct my_config を定義しています。係数は配列 coeff_vec として指定されています。FIR のその他すべてのパラメーターにはデフォルト値が使用されます。

struct myconfig : hls::ip_fir::params_t {
    static const double coeff_vec[sg_fir_srrc_coeffs_len];
};
static hls::FIR<myconfig> fir1;
fir1.run(fir_in, fir_out);

FIR のスタティック パラメーターにパラメーター指定構造体 hls::ip_fir::params_t に使用されるパラメーターを示します。パラメーターのデフォルト値および使用可能な値のリストは、FIR の構造体パラメーターの値 を参照してください。

注記: ザイリンクスでは、パラメーターの詳細およびその設定の影響について、『FIR Compiler LogiCORE IP 製品ガイド』 (PG149) を参照することをお勧めします。
FIR の構造体パラメーター
表 7. FIR の構造体パラメーター
パラメーター 説明
input_width データ入力ポート幅
input_fractional_bits 入力ポートの小数ビットの数
output_width データ出力ポート幅
output_fractional_bits 出力ポートの小数ビットの数
coeff_width 係数のビット幅
coeff_fractional_bits 係数の小数ビットの数
num_coeffs 係数の数
coeff_sets 係数セットの数
input_length 入力データのサンプル数
output_length 出力データのサンプル数
num_channels 処理されるデータのチャネル数を指定
total_num_coeff 係数の総数
coeff_vec[total_num_coeff] 係数配列
filter_type フィルターに使用されるフィルター タイプをインプリメント
rate_change 整数または小数レートの変更を指定
interp_rate 補間レート
decim_rate 間引きレート
zero_pack_factor 補間で使用される係数 0 の数
rate_specification レートを周波数または周期として指定
hardware_oversampling_rate オーバーサンプリングのレートを指定
sample_period ハードウェアのオーバーサンプリング周期
sample_frequency ハードウェアのオーバーサンプリング周波数
quantization 使用される量子化方法
best_precision 最適な精度をイネーブルまたはディスエーブル
coeff_structure 使用する係数構造のタイプ
output_rounding_mode 出力で使用される丸めのタイプ
filter_arch シストリックまたは転置型アーキテクチャを選択
optimization_goal 最適化目標を速度またはエリアに指定
inter_column_pipe_length DSP 列間に必要なパイプライン長
column_config DSP モジュールの列数を指定
config_method DSP モジュール列のコンフィギュレーション方法を指定
coeff_padding フィルターの先頭に追加される 0 パディングの数

整数またはブール型ではないパラメーター値を指定する際は、HLS FIR 名前空間を使用する必要があります。

たとえば、rate_change に使用可能な値は、次の表で integer および fixed_fractional と示されており、C プログラムでは rate_change = hls::ip_fir::integer および rate_change = hls::ip_fir::fixed_fractional を使用する必要があります。

FIR の構造体パラメーターの値

次の表に、FIR IP のすべての機能を示します。この表に示されていない機能は、Vitis HLS のインプリメンテーションではサポートされていません。

表 8. FIR の構造体パラメーターの値
パラメーター C データ型 デフォルト値 有効値
input_width unsigned 16 制限なし
input_fractional_bits unsigned 0 input_width のサイズにより制限
output_width unsigned 24 制限なし
output_fractional_bits unsigned 0 output_width のサイズにより制限
coeff_width unsigned 16 制限なし
coeff_fractional_bits unsigned 0 coeff_width のサイズにより制限
num_coeffs bool 21 フル
coeff_sets unsigned 1 1 ~ 1024
input_length unsigned 21 制限なし
output_length unsigned 21 制限なし
num_channels unsigned 1 1 ~ 1024
total_num_coeff unsigned 21 num_coeffs * coeff_sets
coeff_vec[total_num_coeff] double array なし 該当なし
filter_type unsigned single_rate single_rate、interpolation、decimation、hilbert_filter、interpolated
rate_change unsigned integer integer、fixed_fractional
interp_rate unsigned 1 1 ~ 1024
decim_rate unsigned 1 1 ~ 1024
zero_pack_factor unsigned 1 1-8
rate_specification unsigned period frequency、period
hardware_oversampling_rate unsigned 1 制限なし
sample_period bool 1 制限なし
sample_frequency unsigned 0.001 制限なし
quantization unsigned integer_coefficients integer_coefficients、quantize_only、maximize_dynamic_range
best_precision unsigned false

false

true

coeff_structure unsigned non_symmetric inferred、non_symmetric、symmetric、negative_symmetric、half_band、hilbert
output_rounding_mode unsigned full_precision full_precision、truncate_lsbs、non_symmetric_rounding_down、non_symmetric_rounding_up、symmetric_rounding_to_zero、symmetric_rounding_to_infinity、convergent_rounding_to_even、convergent_rounding_to_odd
filter_arch unsigned systolic_multiply_accumulate systolic_multiply_accumulate、transpose_multiply_accumulate
optimization_goal unsigned area area、speed
inter_column_pipe_length unsigned 4 1-16
column_config unsigned 1 使用される DSP48 の数によって制限
config_method unsigned single single、by_channel
coeff_padding bool false

false

true

FIR 関数の使用

FIR 関数は、HLS 名前空間で定義され、次のように呼び出すことができます。


// Create an instance of the FIR 
static hls::FIR<STATIC_PARAM> fir1;
// Execute the FIR instance fir1
fir1.run(INPUT_DATA_ARRAY, OUTPUT_DATA_ARRAY);

STATIC_PARAM は、FIR の最もスタティックなパラメーターを定義するスタティック パラメーター指定構造体で、

入力データおよび出力データは、配列 (INPUT_DATA_ARRAY および OUTPUT_DATA_ARRAY) として関数に供給されます。最終的なインプリメンテーションでは、FIRT IP のこれらのポートは AXI4-Stream ポートとしてインプリメントされます。データフロー最適化 (set_directive_dataflow) を使用した領域では、配列がストリーミング配列としてインプリメントされるので、ザイリンクスでは常に FFT 関数を使用することを勧めします。または、両方の配列を set_directive_stream コマンドを使用してストリーミングとして指定します。

重要: FIR は、パイプラインされた領域では使用できません。高パフォーマンスの演算が必要な場合、FIR の前後でループまたは関数をパイプラン処理し、その領域のすべてのループおよび関数でデータフロー最適化を使用します。

FIR のマルチチャネル機能は、シングル入力およびシングル出力配列のデータをインターリーブすることでサポートされます。

  • 入力配列のサイズは、すべてのサンプル (num_channels * input_length) に十分な大きさである必要があります。
  • 出力配列のサイズは、すべての出力サンプル (num_channels * output_length) を含めるように指定する必要があります。

次のコード例は、2 チャネルで、データがどのようにインターリーブされるかを示しています。最上位関数に入力データのチャネルが 2 つ (din_idin_q) および出力データのチャネルが 2 つ (dout_idout_q) 含まれています。フロントエンド (fe) とバックエンド (be) の 2 つの関数を使用して、FIR 入力配列のデータを正しく並べ、FIR 出力配列からそれを抽出しています。


void dummy_fe(din_t din_i[LENGTH], din_t din_q[LENGTH], din_t out[FIR_LENGTH]) {
    for (unsigned i = 0; i < LENGTH; ++i) {
        out[2*i] = din_i[i];
        out[2*i + 1] = din_q[i];
    }
}
void dummy_be(dout_t in[FIR_LENGTH], dout_t dout_i[LENGTH], dout_t dout_q[LENGTH]) {   
    for(unsigned i = 0; i < LENGTH; ++i) {
        dout_i[i] = in[2*i];
        dout_q[i] = in[2*i+1];
    }
}
void fir_top(din_t din_i[LENGTH], din_t din_q[LENGTH],
             dout_t dout_i[LENGTH], dout_t dout_q[LENGTH]) {   

 din_t fir_in[FIR_LENGTH];
    dout_t fir_out[FIR_LENGTH];
    static hls::FIR<myconfig> fir1;

    dummy_fe(din_i, din_q, fir_in);
    fir1.run(fir_in, fir_out);
    dummy_be(fir_out, dout_i, dout_q);
}

オプションの FIR ランタイム コンフィギュレーション

演算モードによっては、係数の使用方法を設定するため、FIR に別の入力が必要なこともあります。この入力コンフィギュレーションが必要なモードの詳細は、『FIR Compiler LogiCORE IP 製品ガイド』 (PG149) を参照してください。

この入力コンフィギュレーションは、標準 8 ビット データ型の ap_int.h を使用して C コードで実行できます。この例では、fir_top.h ヘッダー ファイルで FIR と ap_fixed ライブラリの使用が指定され、多くのデザイン パラメーター値が定義された後、それらに基づいて固定小数点型がいくつか定義されています。


#include "ap_fixed.h"
#include "hls_fir.h"

const unsigned FIR_LENGTH   = 21;
const unsigned INPUT_WIDTH = 16;
const unsigned INPUT_FRACTIONAL_BITS = 0;
const unsigned OUTPUT_WIDTH = 24;
const unsigned OUTPUT_FRACTIONAL_BITS = 0;
const unsigned COEFF_WIDTH = 16;
const unsigned COEFF_FRACTIONAL_BITS = 0;
const unsigned COEFF_NUM = 7;
const unsigned COEFF_SETS = 3;
const unsigned INPUT_LENGTH = FIR_LENGTH;
const unsigned OUTPUT_LENGTH = FIR_LENGTH;
const unsigned CHAN_NUM = 1;
typedef ap_fixed<INPUT_WIDTH, INPUT_WIDTH - INPUT_FRACTIONAL_BITS> s_data_t;
typedef ap_fixed<OUTPUT_WIDTH, OUTPUT_WIDTH - OUTPUT_FRACTIONAL_BITS> m_data_t;
typedef ap_uint<8> config_t;

最上位コードには、ヘッダー ファイルの情報が含まれ、ビット幅を指定するのに使用したのと同じ定数値を使用してスタティックでパラメーター指定された構造体が作成されるので、C コードと FIR コンフィギュレーションが一致するようになり、係数が指定されます。最上位では、ヘッダー ファイルで 8 ビット データとして定義された入力コンフィギュレーションが FIR に渡されます。


#include "fir_top.h"

struct param1 : hls::ip_fir::params_t {
    static const double coeff_vec[total_num_coeff];
    static const unsigned input_length = INPUT_LENGTH;
    static const unsigned output_length = OUTPUT_LENGTH;
    static const unsigned num_coeffs = COEFF_NUM;
    static const unsigned coeff_sets = COEFF_SETS;
};
const double param1::coeff_vec[total_num_coeff] = 
    {6,0,-4,-3,5,6,-6,-13,7,44,64,44,7,-13,-6,6,5,-3,-4,0,6};

void dummy_fe(s_data_t in[INPUT_LENGTH], s_data_t out[INPUT_LENGTH], 
                config_t* config_in, config_t* config_out)
{
    *config_out = *config_in;
    for(unsigned i = 0; i < INPUT_LENGTH; ++i)
        out[i] = in[i];
}

void dummy_be(m_data_t in[OUTPUT_LENGTH], m_data_t out[OUTPUT_LENGTH])
{
    for(unsigned i = 0; i < OUTPUT_LENGTH; ++i)
        out[i] = in[i];
}

// DUT
void fir_top(s_data_t in[INPUT_LENGTH],
             m_data_t out[OUTPUT_LENGTH],
             config_t* config)
{

    s_data_t fir_in[INPUT_LENGTH];
    m_data_t fir_out[OUTPUT_LENGTH];
    config_t fir_config;
    // Create struct for config
    static hls::FIR<param1> fir1;
    
    //==================================================
// Dataflow process
    dummy_fe(in, fir_in, config, &fir_config);
    fir1.run(fir_in, fir_out, &fir_config);
    dummy_be(fir_out, out);
    //==================================================
}

FIR C ライブラリを使用したデザイン例は、HelpWelcomeOpen Example ProjectDesign ExamplesFIR をクリックして表示される Vitis HLS のサンプルに含まれます。

DDS IP ライブラリ

hls_dds.h ライブラリを使用し、C++ デザイン内でザイリンクス DDS (Direct Digital Synthesizer) IP ブロックを使用できます。このセクションでは、DDS IP を C++ コードで設定する方法を説明します。

注記: ザイリンクスでは、この IP の機能のインプリメント方法および機能の使用方法について、『DDS Compiler LogiCORE IP 製品ガイド』 (PG141) を参照することをお勧めしています。
重要: DDS IP コアの C IP インプリメンテーションでは、Phase_Increment および Phase_Offset パラメーターに fixed モードがサポートされ、Phase_Offset には none モードもサポートされますが、programmable および streaming モードはサポートされません。

C++ コードで DDS を使用するには、次の手順に従います。

  1. コードに hls_dds.h ライブラリを含めます。
  2. 定義済み構造体 hls::ip_dds::params_t を使用してデフォルト パラメーターを設定します。
  3. DDS 関数を呼び出します。

まず、ソース コードに DDS ライブラリを含めます。このヘッダー ファイルは、Vitis HLS のインストール ディレクトリの include ディレクトリに含まれています。このディレクトリは、Vitis HLS が実行されると自動的に検索されます。


#include "hls_dds.h"

DDS のスタティック パラメーターを定義します。たとえば、位相幅、クロック レート、位相およびインクリメントのオフセットを定義します。DDS C ライブラリにはパラメーター指定構造体 hls::ip_dds::params_t が含まれ、すべてのスタティック パラメーターをデフォルト値で初期化できます。この構造体の値のいずれかを再定義することで、インプリメンテーションをカスタマイズできます。

次の例では、既存の定義済み構造体 hls::ip_dds::params_t に基づくユーザー定義の構造体 param1 を使用して、位相幅、クロック レート、位相オフセット、チャネル数のデフォルト値を変更する方法を示しています。


struct param1 : hls::ip_dds::params_t {
 static const unsigned Phase_Width = PHASEWIDTH;
 static const double   DDS_Clock_Rate = 25.0;
 static const double PINC[16];
 static const double POFF[16];
}; 

HLS 名前空間と定義済みのスタティック パラメーターを使用して、DDS 関数のインスタンス (param1 など) を作成します。その後、run メソッドを使用して関数呼び出して実行します。次の例では、データおよび位相関数引数が順に示されています。


static hls::DDS<config1> dds1;
dds1.run(data_channel, phase_channel);

DDS の C ライブラリを使用するサンプル デザインにアクセスするには、HelpWelcomeOpen Example ProjectDesign ExamplesDDS をクリックします。

DDS のスタティック パラメーター

DDS のスタティック パラメーターは、クロック レート、位相間隔、モードなど、DDS を設定する方法を定義します。hls_dds.h ヘッダー ファイルは、スタティック パラメーターのデフォルト値を設定する hls::ip_dds::params_t 構造体を定義します。デフォルト値を使用するには、パラメーター設定構造体を DDS 関数で直接使用できます。

static hls::DDS< hls::ip_dds::params_t > dds1;
dds1.run(data_channel, phase_channel);

次の表に、パラメーター指定構造体 hls::ip_dds::params_t のパラメーターを示します。

注記: ザイリンクスでは、パラメーターおよび値の詳細について、『DDS Compiler LogiCORE IP 製品ガイド』 (PG141) を参照することをお勧めします。
表 9. DDS の構造体パラメーター
パラメーター 説明
DDS_Clock_Rate DDS 出力のクロック レートを指定します。
Channels チャネル数を指定します。DDS および位相ジェネレーターは最大 16 チャネルまでサポート可能です。チャネルは時分割されるので、チャネルごとの実効クロック周波数を低減します。
Mode_of_Operation

次の操作モードの 1 つを指定します。

標準モード: SIN/COS LUT へのアクセスに使用する前に累算された位相を切り捨てることができる場合に使用します。

ラスタライズ モード: 希望の周波数とシステム クロックが有理分数で関連している場合に使用します。

Modulus

システム クロック周波数と希望の周波数との関係を定義します。

ラスタライズ モードでのみ使用します。

Spurious_Free_Dynamic_Range DDS で生成されるトーンのターゲット純度を指定します。
Frequency_Resolution Hz で最小周波数分解能を指定し、また、関連付けられている位相インクリメント (PINC) および位相オフセット (POFF) 値を含む、位相アキュムレータで使用される位相幅を決定します。
Noise_Shaping 位相切り捨て、ディザリング、またはテイラー級数訂正を使用するかどうかを決定します。
Phase_Width

次の幅を設定します。

m_axis_phase_tdata 内の PHASE_OUT フィールド

DDS が SIN/COS LUT のみに設定される場合の s_axis_phase_tdata 内の Phase フィールド

位相アキュムレータ

関連付けられている位相インクリメントおよびオフセット レジスタ

s_axis_config_tdata 内の Phase フィールド

ラスタライズ モードの場合、位相幅は有効な入力範囲 [0, Modulus-1]、つまり log2 (Modulus-1) を切り上げた値を記述するのに必要なビット数として固定されています。

Output_Width m_axis_data_tdata 内の SINE および COSINE フィールドの幅を設定します。このパラメーターで提供される SFDR は、選択されている Noise Shaping オプションによって異なります。
Phase_Increment 位相インクリメント値を選択します。
Phase_Offset 位相オフセット値を選択します。
Output_Selection m_axis_data_tdata バスの SINECOSINE、またはその両方への出力を選択します。
Negative_Sine ランタイム時に SINE フィールドをネゲートします。
Negative_Cosine ランタイム時に COSINE フィールドをネゲートします。
Amplitude_Mode 振幅をフル レンジまたは単位円に設定します。
Memory_Type SIN/COS LUT のインプリメンテーションを制御します。
Optimization_Goal インプリメンテーションで最高速を狙うか、リソース使用率をできる限り抑えるかを決定します。
DSP48_Use 位相アキュムレータおよび位相オフセット、ディザ ノイズ追加、またはその両方をインプリメントします。
Latency_Configuration 最適化目標に基づいた最適値にコアのレイテンシを設定します。
Latency レイテンシ値を指定します。
Output_Form 出力形式を 2 の補数または符号絶対値に設定します。一般的には SINE および COSINE の出力形式は 2 の補数です。ただし、象限対称が使用されている場合は、出力形式を符号絶対値に変えることができます。
PINC[XIP_DDS_CHANNELS_MAX] 各出力チャネルの位相インクリメントの値を設定します。
POFF[XIP_DDS_CHANNELS_MAX] 各出力チャネルの位相オフセットの値を設定します。
DDS の構造体パラメーター値

次の表に、パラメーター設定構造体 hls::ip_dds::params_t のパラメーター値を示します。

表 10. DDS の構造体パラメーター値
パラメーター C データ型 デフォルト値 有効値
DDS_Clock_Rate double 20.0 任意の倍精度値
Channels unsigned 1 1 ~ 16
Mode_of_Operation unsigned XIP_DDS_MOO_CONVENTIONAL

XIP_DDS_MOO_CONVENTIONAL は累計位相を切り捨てます。

XIP_DDS_MOO_RASTERIZED はラスタライズ モードを選択します。

Modulus unsigned 200 129 ~ 256
Spurious_Free_Dynamic_Range double 20.0 18.0 ~ 150.0
Frequency_Resolution double 10.0 0.000000001 ~ 125000000
Noise_Shaping unsigned XIP_DDS_NS_NONE

XIP_DDS_NS_NONE は位相切り捨て DDS を生成します。

XIP_DDS_NS_DITHER は、SFDR を改善するために位相ディザーを使用しますが、ノイズ レベルが増加します。

XIP_DDS_NS_TAYLOR は、位相切り捨てにより破棄されるビットを使用してサイン/コサイン値を補間します。

XIP_DDS_NS_AUTO は自動的にノイズ シェーピングを決定します。

Phase_Width unsigned 16 8 の整数倍数である必要があります。
Output_Width unsigned 16 8 の整数倍数である必要があります。
Phase_Increment unsigned XIP_DDS_PINCPOFF_FIXED

XIP_DDS_PINCPOFF_FIXED は、生成時に PINC を固定します。PINC をランタイム時に変更することはできません。

この値しかサポートされていません。

Phase_Offset unsigned XIP_DDS_PINCPOFF_NONE

XIP_DDS_PINCPOFF_NONE は位相オフセットを生成しません。

XIP_DDS_PINCPOFF_FIXED は、生成時に POFF を固定します。POFF をランタイム時に変更することはできません。

Output_Selection unsigned XIP_DDS_OUT_SIN_AND_COS

XIP_DDS_OUT_SIN_ONLY はサイン出力のみを生成します。

XIP_DDS_OUT_COS_ONLY はコサイン出力のみを生成します。

XIP_DDS_OUT_SIN_AND_COS はサインおよびコサイン出力の両方を出力します。

Negative_Sine unsigned XIP_DDS_ABSENT

XIP_DDS_ABSENT は標準正弦波を生成します。

XIP_DDS_PRESENT は正弦波をネゲートします。

Negative_Cosine bool XIP_DDS_ABSENT

XIP_DDS_ABSENT は標準正弦波を生成します。

XIP_DDS_PRESENT は正弦波をネゲートします。

Amplitude_Mode unsigned XIP_DDS_FULL_RANGE

XIP_DDS_FULL_RANGE はまず 2 進小数点の出力幅に振幅を正規化します。たとえば、8 ビット出力のバイナリ振幅は 100000000 - 10 で、01111110 から 11111110 までの値になります。つまり、わずかに 1 に満たない値からわずかに -1 よりも大きい値までということになります。

XIP_DDS_UNIT_CIRCLE はハーフ フル レンジに振幅を正規化します。つまり 01000 . (+0.5) から 110000 .. (-0.5) までの値になります。

Memory_Type unsigned XIP_DDS_MEM_AUTO

XIP_DDS_MEM_AUTO は、テーブルが 1 層のメモリに含められる場合に分散 ROM を選択し、そうでない場合はブロック RAM を選択します。

XIP_DDS_MEM_BLOCK は常にブロック RAM を使用します。

XIP_DDS_MEM_DIST は常に分散 RAM を選択します。

Optimization_Goal unsigned XIP_DDS_OPTGOAL_AUTO

XIP_DDS_OPTGOAL_AUTO は自動的に最適化目標を選択します。

XIP_DDS_OPTGOAL_AREA はエリアを目標に最適化を実行します。

XIP_DDS_OPTGOAL_SPEED はパフォーマンスを目標に最適化を実行します。

DSP48_Use unsigned XIP_DDS_DSP_MIN

XIP_DDS_DSP_MIN は、位相アキュムレータおよび位相オフセット、ディザ ノイズ追加、またはその両方を FPGA ロジックにインプリメントします。

XIP_DDS_DSP_MAX は、位相アキュムレータおよび位相オフセット、ディザ ノイズ追加、またはその両方を DSP スライスを使用してインプリメントします。シングル チャネルの場合は、プログラマブルな位相インクリメント、位相オフセット、またはその両方を格納するレジスタを DSP スライスは提供することもできるため、デバイス リソースを節約できます。

Latency_Configuration unsigned XIP_DDS_LATENCY_AUTO

XIP_DDS_LATENCY_AUTO は自動的にレイテンシを決定します。

XIP_DDS_LATENCY_MANUAL は、レイテンシ オプションを使用してレイテンシを手動で指定します。

Latency unsigned 5 任意の値
Output_Form unsigned XIP_DDS_OUTPUT_TWOS

XIP_DDS_OUTPUT_TWOS は 2 の補数を出力します。

XIP_DDS_OUTPUT_SIGN_MAG は符号絶対値を出力します。

PINC[XIP_DDS_CHANNELS_MAX] unsigned array {0} 各出力チャネルの位相インクリメントの値
POFF[XIP_DDS_CHANNELS_MAX] unsigned array {0} 各出力チャネルの位相オフセットの値

SRL IP ライブラリ

C コードは、再利用、読みやすさ、パフォーマンスなどの複数の要件を満たすように記述されます。現時点では、C コードは高位合成後に最適なハードウェアが得られるように記述されていません。

ただし、再利用、読みやすさ、パフォーマンス要件と同様に、特定のコード記述方法またはあらかじめ定義されたコンストラクトを使用することにより、合成結果がより適切なハードウェアになるように、またはアルゴリズムをより簡単に検証して C でハードウェアをより適切に記述できるようできます。

SRL リソースへの直接マップ

多くの C アルゴリズムでは、データを配列内で順次シフトします。配列の開始に新しい値が追加され、既存のデータが配列内でシフトされ、最も古いデータ値が破棄されます。この操作は、ハードウェアではシフト レジスタとしてインプリメントされます。

C からシフト レジスタをハードウェアにインプリメントする場合、配列を個々の要素に完全に分割し、RTL 内の要素間のデータ依存性からシフト レジスタを暗示させるのが最も一般的です。

論理合成では、通常 RTL シフト レジスタがシフト レジスタを効率的にインプリメントするザイリンクス SRL リソースにインプリメントされます。問題は、論理合成で RTL シフト レジスタが SRL コンポーネントを使用してインプリメントされない場合があることです。

  • シフト レジスタの中央にあるデータにアクセスする場合、論理合成では SRL を直接推論できません。
  • SRL が最適であっても、ほかの要因により論理合成でシフト レジスタがフリップフロップにインプリメントされることがあります。論理合成は、複雑なプロセスです。

Vitis HLS では、C++ クラス (ap_shift_reg) が提供されており、C コードで定義されたシフト レジスタが SRL リソースを使用して常にインプリメントされるようになっています。ap_shift_reg クラスでは、SRL コンポーネントでサポートされるさまざまな読み出しおよび書き込みアクセスを実行する方法が 2 つあります。

シフト レジスタからの読み出し

読み出しメソッドでは、シフト レジスタの指定した位置から読み出しできます。

Vitis HLS には、ap_shift_reg クラスを定義する ap_shift_reg.h ヘッダー ファイルがスタンドアロン パッケージとして含まれており、ユーザーのソース コードで使用できるようになっています。xilinx_hls_lib_<release_number>.tgz パッケージは、Vitis HLS インストール ディレクトリの include ディレクトリに含まれます。

// Include the Class
#include "ap_shift_reg.h"

// Define a variable of type ap_shift_reg<type, depth>
// - Sreg must use the static qualifier
// - Sreg will hold integer data types
// - Sreg will hold 4 data values
static ap_shift_reg<int, 4> Sreg;
int var1;

// Read location 2 of Sreg into var1
var1 = Sreg.read(2);

データの読み出しおよび書き込みして、シフト

shift メソッドでは、読み出し、書き込みおよびシフト演算を実行できます。


// Include the Class
#include "ap_shift_reg.h"

// Define a variable of type ap_shift_reg<type, depth>
// - Sreg must use the static qualifier
// - Sreg will hold integer data types
// - Sreg will hold 4 data values
static ap_shift_reg<int, 4> Sreg;
int var1;

// Read location 3 of Sreg into var1
// THEN shift all values up one and load In1 into location 0
var1 = Sreg.shift(In1,3);

読み出しおよび書き込みして、シフトをイネーブル制御

shift メソッドではイネーブル入力もサポートされており、シフト演算を変数で制御およびイネーブルにできます。


// Include the Class
#include "ap_shift_reg.h"

// Define a variable of type ap_shift_reg<type, depth>
// - Sreg must use the static qualifier
// - Sreg will hold integer data types
// - Sreg will hold 4 data values
static ap_shift_reg<int, 4> Sreg;
int var1, In1;
bool En;

// Read location 3 of Sreg into var1
// THEN if En=1 
// Shift all values up one and load In1 into location 0
var1 = Sreg.shift(In1,3,En);

ap_shift_reg クラスを使用すると、Vitis HLS で各シフト レジスタに対して固有の RTL コンポーネントが作成されます。論理合成が実行されると、このコンポーネントが SRL リソースに合成されます。