FC2カウンター FPGAの部屋 Vivado HLS

FPGAやCPLDの話題やFPGA用のツールの話題などです。 マニアックです。 日記も書きます。

FPGAの部屋

FPGAの部屋の有用と思われるコンテンツのまとめサイトを作りました。Xilinx ISEの初心者の方には、FPGAリテラシーおよびチュートリアルのページをお勧めいたします。

「ゼロから作るDeep Learning」の2層ニューラルネットワークのハードウェア化1

「ゼロから作るDeep Learning」以前、すべての章をご紹介した。とっても良い本だ。

その5章 誤差逆伝播法の2層のニューラルネットワークをハードウェア化してFPGA に実装してみようと思う。
学習はdeep-learning-from-scratch/ch05/ のtrain_neuralnet.py のコードをそのまま使用する。
このままでは浮動小数点数なので、指定されたビット長の固定小数点に量子化するメソッドをtwo_layer_net.py、layers.py に追加してある。それが、two_layer_net_int.py、layers_int.py だ。
nn_fpga_ch5_1_170602.png

上の図のように Class Relu に def forward_int が追加されているのが分かると思う。つまり、固定小数点に量子化してforward 処理を実行するメソッドを追加したわけだ。ただし、Relu はforward と同じだ。
MAGNIFICATION が量子化のビット長を表す。つまり、全体が 9 ビットで、整数部が1ビットを表す。RANGE が内積を取った後の整数部に拡張されるビット長を表している。これらはいろいろとやってみて、精度があまり落ちない値にしてある。

今回、def forward_int を書き換えたのは、class Affine だけだ。
重みやバイアスにMAGNIFICATION を掛けて、0.5 を加算して(四捨五入のため)integer に変換した後で、飽和演算を行う。すると小数点以下が切れてるので、量子化できることになる。そして、float に戻して、MAGNIFICATION で割っている。
その後、量子化の目安のため値のmax と min を表示している。
nn_fpga_ch5_2_170602.png

先ほど書いたように、train_neuralnet.py の学習はそのまま使って、two_layer_net_int.py にも def loss_int や def predict_int、def accuracy_int を追加してあるので、固定小数点に量子化してforward 処理したときのaccuracy(精度)を確認することができる。
nn_fpga_ch5_3_170602.png
nn_fpga_ch5_4_170602.png

その出力を示す。

0.17975 0.1722
0.904483333333 0.9106
0.9247 0.9279
0.9357 0.9352
0.94515 0.9425
0.95155 0.9506
0.956733333333 0.9527
0.95995 0.9564
0.9649 0.9615
0.9663 0.9609
0.968883333333 0.9632
0.972766666667 0.9655
0.973883333333 0.9665
0.97535 0.968
0.97635 0.9678
0.9784 0.9696
0.979483333333 0.9692

(60000, 784)
np.max(x) = 1.0
np.min(x) = 0.0
(784, 50)
np.max(self.W) = 0.42578125
np.min(self.W) = -0.53515625
(50,)
np.max(self.b) = 0.28515625
np.min(self.b) = -0.22265625
(60000, 50)
np.max(out) = 10.925199185698148
np.min(out) = -12.404212626584922
np.max(out2) = 10.92578125
np.min(out2) = -12.3984375
(60000, 50)
np.max(x) = 10.92578125
np.min(x) = 0.0
(50, 10)
np.max(self.W) = 0.99609375
np.min(self.W) = -1.0
(10,)
np.max(self.b) = 0.421875
np.min(self.b) = -0.39453125
(60000, 10)
np.max(out) = 30.527862548828125
np.min(out) = -28.802261352539062
np.max(out2) = 15.99609375
np.min(out2) = -16.0
(10000, 784)
np.max(x) = 1.0
np.min(x) = 0.0
(784, 50)
np.max(self.W) = 0.42578125
np.min(self.W) = -0.53125
(50,)
np.max(self.b) = 0.28515625
np.min(self.b) = -0.21875
(10000, 50)
np.max(out) = 10.473299672572466
np.min(out) = -10.355300247856576
np.max(out2) = 10.47265625
np.min(out2) = -10.3515625
(10000, 50)
np.max(x) = 10.47265625
np.min(x) = 0.0
(50, 10)
np.max(self.W) = 0.99609375
np.min(self.W) = -0.99609375
(10,)
np.max(self.b) = 0.421875
np.min(self.b) = -0.390625
(10000, 10)
np.max(out) = 29.088897705078125
np.min(out) = -24.532501220703125
np.max(out2) = 15.99609375
np.min(out2) = -16.0
0.9774 0.9636


最後の”0.9774 0.9636”が固定小数点で量子化した場合のaccuracy だ。浮動小数点で計算したときのaccuracy は”0.979483333333 0.9692”なので、少し下がっているのが分かると思う。
  1. 2017年06月02日 04:55 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS の浮動小数点型を使用した内積の計算

Vivado HLS の任意精度固定小数点型を使用した内積の計算”のおまけ。

任意精度固定小数点型の比較のために作った浮動小数点数の内積の計算だが、思ったより使用リソースが少なかったので、書いておくことにした。

まずはnn_test_float プロジェクトを示す。
ap_fixed_dot_8_170601.png

次にソースコードを示す。
まずは、nn_test_float.h から示す。

// nn_test_float.h
// 2017/05/31 by marsee
//

#ifndef __NN_TEST_H__
#define __NN_TEST_H__

#include <ap_fixed.h>

const float wt[3][4]={
    {-0.10.2, -0.30.4},
    {-0.50.6, -0.70.8},
    {-0.20.4, -0.50.6}
};

const float b[4] ={
    -0.10.4, -0.30.5
};

#endif


nn_test.cpp を示す。

// nn_test_float.cpp
// 2017/05/29 by marsee
//

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

int nn_test(float in[3], float out[4]){
    float dot[4];

    Loop1: for(int j=0; j<4; j++){
        dot[j] = 0;
        Loop2: for(int i=0; i<3; i++){
            dot[j] += in[i]*wt[i][j];
        }
        dot[j] += b[j];
        out[j] = dot[j];
    }

    return(0);
}


nn_test_tb_float.cpp を示す。

// nn_test_tb_float.cpp
// 2017/05/29 by marsee
//

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

int nn_test(float in[3], float out[4]);
int nn_test_soft(float in[3], float out[4]);

int main(){
    float in[3] = {0.3906250.58593750.78125};
    float out[4];
    float out_soft[4];

    nn_test(in, out);
    nn_test_soft(in, out_soft);

    for(int i=0; i<4; i++){
        if(out[i] != out_soft[i]){
            printf("ERROR HW and SW results mismatch i = %d, HW = %f, SW = %f\n", i, (float)out[i], (float)out_soft[i]);
        }else{
            printf("out[%d] = %f\n", i, (float)out[i]);
        }
    }

    return(0);
}

int nn_test_soft(float in[3], float out[4]){
    float dot[4];

    for(int j=0; j<4; j++){
        dot[j] = 0;
        for(int i=0; i<3; i++){
            dot[j] += in[i]*wt[i][j];
        }
        dot[j] += b[j];
        out[j] = dot[j];
    }

    return(0);
}


C シミュレーションは、前回の”Vivado HLS の任意精度固定小数点型を使用した内積の計算”を参照のこと。

C コードの合成を行った。
ap_fixed_dot_9_170601.png

DSPを5個使っている以外は案外、リソース使用量が少ないのではないだろうか?

Analysis 画面を開いた。DSP は浮動小数点数の乗算に3個、加算の2個使用されている。
ap_fixed_dot_10_170601.png

C/RTL協調シミュレーションを行った。やはり、前回の 109 クロックよりも完了までのクロック数は増えている。
ap_fixed_dot_11_170601.png

C/RTL協調シミュレーションの出力も問題なさそうだ。

out[0] = -0.588281
out[1] = 1.142187
out[2] = -1.217969
out[3] = 1.593750


C/RTL協調シミュレーション結果を示す。
ap_fixed_dot_12_170601.png

浮動小数点数が出ているようだ。
  1. 2017年06月01日 21:17 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS の任意精度固定小数点型を使用した内積の計算

今回は、配列の内積をVivado HLS の任意精度固定小数点型を使用して計算してみようと思う。浮動小数点数の計算値と比較してみよう。

Vivado HLS の任意精度固定小数点型を使用した配列の内積を計算するVivado HLS 2017.1 のプロジェクトを示す。
ap_fixed_dot_1_170601.png

nn_test.h を示す。

// nn_test.h
// 2017/05/31 by marsee
//

#ifndef __NN_TEST_H__
#define __NN_TEST_H__

#include <ap_fixed.h>

const ap_fixed<91, AP_TRN_ZERO, AP_SAT> wt[3][4]={
    {-0.10.2, -0.30.4},
    {-0.50.6, -0.70.8},
    {-0.20.4, -0.50.6}
};

const ap_fixed<91, AP_TRN_ZERO, AP_SAT> b[4] ={
    -0.10.4, -0.30.5
};

#endif


nn_test.cpp を示す。

// nn_test.cpp
// 2017/05/29 by marsee
//

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

int nn_test(ap_ufixed<80, AP_TRN_ZERO, AP_SAT> in[3], ap_fixed<135, AP_TRN_ZERO, AP_SAT> out[4]){
    ap_fixed<135, AP_TRN_ZERO, AP_SAT> dot[4];

    Loop1: for(int j=0; j<4; j++){
        dot[j] = 0;
        Loop2: for(int i=0; i<3; i++){
            dot[j] += in[i]*wt[i][j];
        }
        dot[j] += b[j];
        out[j] = dot[j];
    }

    return(0);
}


nn_test_tb.cpp を示す。

// nn_test_tb.cpp
// 2017/05/29 by marsee
//

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

int nn_test(ap_ufixed<80, AP_TRN_ZERO, AP_SAT> in[3], ap_fixed<135, AP_TRN_ZERO, AP_SAT> out[4]);
int nn_test_soft(ap_ufixed<80, AP_TRN_ZERO, AP_SAT> in[3], ap_fixed<135, AP_TRN_ZERO, AP_SAT> out[4]);

int main(){
    ap_ufixed<80, AP_TRN_ZERO, AP_SAT> in[3] = {0.3906250.58593750.78125};
    ap_fixed<135, AP_TRN_ZERO, AP_SAT> out[4];
    ap_fixed<135, AP_TRN_ZERO, AP_SAT> out_soft[4];

    nn_test(in, out);
    nn_test_soft(in, out_soft);

    for(int i=0; i<4; i++){
        if(out[i] != out_soft[i]){
            printf("ERROR HW and SW results mismatch i = %d, HW = %f, SW = %f\n", i, (float)out[i], (float)out_soft[i]);
        }else{
            printf("out[%d] = %f\n", i, (float)out[i]);
        }
    }

    return(0);
}

int nn_test_soft(ap_ufixed<80, AP_TRN_ZERO, AP_SAT> in[3], ap_fixed<135, AP_TRN_ZERO, AP_SAT> out[4]){
    ap_fixed<135, AP_TRN_ZERO, AP_SAT> dot[4];

    for(int j=0; j<4; j++){
        dot[j] = 0;
        for(int i=0; i<3; i++){
            dot[j] += in[i]*wt[i][j];
        }
        dot[j] += b[j];
        out[j] = dot[j];
    }

    return(0);
}


C シミュレーションを行った。結果を示す。
ap_fixed_dot_2_170601.png

out[] の値を示す。
out[0] = -0.578125
out[1] = 1.128906
out[2] = -1.207031
out[3] = 1.582031

Vivado HLS の任意精度固定小数点型をそっくりそのまま float 型で実装したVivado HLS プロジェクトの結果を示す。
ap_fixed_dot_3_170601.png

out[] の値を示す。
out[0] = -0.588281
out[1] = 1.142187
out[2] = -1.217969
out[3] = 1.593750

このコードで本当に配列の内積が計算できているか?を調べるために、maxima で配列の内積を計算した。
ap_fixed_dot_4_170601.png

その結果 float 型で計算したVivado HLS のプロジェクトの値は、maxima で計算した値にほぼ等しい。
Vivado HLS の任意精度固定小数点型はやはり固定小数点なので、誤差があるが、おおむね値は合っていそうだ。

Vivado HLS の任意精度固定小数点型のVivado HLS プロジェクトで、C コードの合成を行った。
ap_fixed_dot_5_170601.png

全く指示子を与えていないので、デフォルトの状態になっている。

C/RTL協調シミュレーションを行った。
ap_fixed_dot_6_170601.png

C/RTL協調シミュレーションの波形を示す。
ap_fixed_dot_7_170601.png

このように、Vivado HLS の任意精度固定小数点型で書くと、固定小数点数のビット長や固定小数点位置が違っても、うまく計算してくれる。とっても便利だ。
うまく計算してくれるのは、以前のやった結果からわかってはいたのだが、もう一度、確認するためにやってみた。
  1. 2017年06月01日 04:05 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLSのCoSim がNorton Endpointの検疫に引っかかる

Vivado HLSのCoSim がNorton Endpointの検疫に引っかかってしまった。
Vivado HLS のバージョンは2017.1 だが、2016.4 も同様に引っかかっている。

あるVivado HLS のプロジェクトを作成した。
C-Simulation を行ったら通った。
Endpoint_Cosim_3_170531.png

けれどもC コードの合成を行って、CoSim を行ったらエラー発生。
Endpoint_Cosim_4_170531.png

同時にNorton Endpoint の検疫に引っかかっていた。
Endpoint_Cosim_1_170531.png

詳しいダイアログを表示した。
Endpoint_Cosim_2_170531.png

CoSim 時に自動生成されたcosim.pc.exe が検疫されているではないか。。。

このファイルはCoSim 時に自動生成されているので、これがウィルスということはないと思うのだが。。。怪しい動きをしていたのだろうか?
ちなみに検出結果は”SONAR.SuspScript!g3”ということだった。

Norton Endpoint を立ち上げて、「設定の変更」ボタンをクリックし、
「ウイルス対策とスパイウェアの設定」ダイアログの「グローバル設定(G)」タブで「例外:」の「リスト表示(L)」ボタンをクリックし、
「例外」ダイアログの「追加(A)...」ボタンをクリックして、「SONAR例外」→「フォルダ」を選択して、Vivado HLS のプロジェクトのあるフォルダを指定したところ、CoSim が通った。
Endpoint_Cosim_5_170531.png


  1. 2017年05月31日 15:00 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS の符号付 C++の任意精度固定小数点型について

Vivado HLS の符号付 C++の任意精度固定小数点型について疑問があったので調べてみた。
Vivado HLS の C++の任意精度固定小数点型は、C++のテンプレートを使用して、ap_[u]fixed で宣言される。(Xilinx社のUser Guide 高位合成 UG902 (v2016.4) 2016年11月30日 192 ページ「C++ の任意精度固定小数点型」参照)

W :ワード長(ビット数)
I :整数部のビット長
Q :量子化モード
O :オーバーフローモード
N :折り返しモードでの飽和ビット数


符号なしC++の任意精度固定小数点型 ap_ufixed<> は問題ないが、符号付C++の任意精度固定小数点型 ap_fixed<>で整数部を1ビットにした場合は符号ビットを含むのか含まないのか?が疑問だった。
そこで、Vivado HLS 2017.1 で ap_fixed_test プロジェクトを作成して、テストしてみた。
ap_fixed_test プロジェクトを示す。
ap_fixed_test_1_170529.png

テストベンチの ap_fixed_test_tb.cpp を示す。

// ap_fixed_test_tb.cpp
// 2017/04/29
//

#include <stdio.h>
#include <ap_fixed.h>

int main(){
    ap_fixed<91, AP_TRN_ZERO, AP_SAT> a;
    float f;

    a = 0.5;
    f = a;
    printf("0.5: %f\n", f);
    a = 1.0;
    f = a;
    printf("1.0: %f\n", f);
    a = -1.0;
    f = a;
    printf("-1.0: %f\n", f);
    a = -0.5;
    f = a;
    printf("-0.5: %f\n", f);
    a = 2.0;
    f = a;
    printf("2.0: %f\n", f);
    a = -1.5;
    f = a;
    printf("-1.5: %f\n", f);
    a = -0.9;
    f = a;
    printf("-0.9: %f\n", f);
}


このテストベンチのC シミュレーション結果はすでにVivado HLS のプロジェクトの図に表示されているが、下に示す。

0.5: 0.500000
1.0: 0.996094
-1.0: -1.000000
-0.5: -0.500000
2.0: 0.996094
-1.5: -1.000000
-0.9: -0.898438


この結果から、整数部のビット長を 1 とすると、1.0 - 量子化ステップ値 ~ -1.0 までの値を取れるようだ。つまり整数部は符号を表すビットのみの 1 ビットだけということになる。
  1. 2017年05月29日 15:05 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLSで stdint.h を使用する

FPGAマガジンNo16 のAXI4 Master のソースコードと、FPGAマガジン No.17 のAXI4-Stream のソースコードを公開しているが、taksei さんから「ソースコードを64bit Linux版VivadoでC-Sim実行してみたのですが、long型が8バイトとして確保されるため、BMPファイルのリード・ライトに失敗するようです。」との報告があった。long型で4バイトのところ、8バイトになっていたようだ。
そこで、taksei さんに教えて頂いたように、stdint.h を使用して、uintX_t や intX_t を使用することにした。taksei さん、ありがとうございました。

私もVirtualBox 上に64ビット版のUbuntu 16.04 をインストールしてあって、その上にVivado 2016.4 をインストールしてある。よって、それ上のVivado HLS 2016.4 で確かめてみよう。

まずは、64ビット版Ubuntu 16.04 の Vivado HLS 2016.4 を起動した。
まずは、以前のままの bitmap_header.h でやってみよう。
stdint_1_170505.png

これで C シミュレーションを行った。
stdint_2_170505.png

”Can't allocate rd_bmp memory”エラーになってしまった。
taksei さんによると、”stdint.h” を使えば良いらしい。
”stdint.h”は、少なくとも 8, 16, 32, 64 ビット長の intN_t, uintN_t という型を宣言してるようだ。つまり、ビット長が明確に定義されている。
これを使用して、BMPのフォーマットに関しての資料を参考に、 intN_t, uintN_t で bitmap_header.h を書き直してみよう。実際には、taksei さんのコードを検証しながら、コピー&ペーストを行った。
更に、lap_filter_tb.c にもBMPのフォーマットがあるので、それも同様に、 intN_t, uintN_t に修正した。
stdint_3_170505.png

stdint_4_170505.png

修正後に C シミュレーションを行ったところ、今度は成功した。
stdint_5_170505.png

C コードの合成結果を示す。Windows 10 での結果と同じレイテンシだった。
stdint_6_170505.png

C/RTL協調シミュレーション結果を示す。これも、Windows 10 での結果と同じだった。
stdint_7_170505.png

lap_filter0_axim/solution1/sim/wrap_pc ディレクトリを見ると、test_lap.bmp が出来て、正常にラプラシアンフィルタ処理が行われているのが分かる。
stdint_8_170505.png

Export RTL も正常に行うことができた。
stdint_9_170505.png

更新した bmp_header.h, lap_filter_tb.c を Windows 10 のVivado HLS 2017.1 に持って行っても正常にシミュレーション、合成を行うことができた。
stdint_10_170505.png

OS によって、int, long の型のビット長は変化するので、ビット長が決まっている変数を宣言する場合は、stdint.h の intN_t, uintN_t を使用して書いたほうが良い。
  1. 2017年05月05日 16:25 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

AXI4マスタ版ラプラシアン・フィルタ最速の条件

この記事はFPGAマガジンNo.17 に載るはずだったけど、ページが足りなくて載せることができなかったコラムです。

今回、「高位合成ツールVivado HLS 特設記事」第2章で説明した第5段階のAXI4マスタ版のラプラシアン・フィルタですが、その最速の条件を探すためにCソースコードを少しずつ変更して最速の条件を確かめてみました。

AXI4マスタ・ライトのみにif文を使用した場合
まずは、出力の画像フォーマットを出力画像の回り1ビットが0のフォーマットに戻してみましょう。つまり、「高位合成ツールVivado HLS 特設記事」第1章、図23の左の出力画像フォーマットにしてみようと思います。特設記事第2章のリスト1のlap変数をlap_fb[]にライトする部分を少し変更します。特設記事第2章のリスト1では、lap_fp[]にライトするときにifなどの条件文は使用していませんでしたが、今回はif文を使用します。具体的には、変数yが0のとき(つまり最初の行)は、lapの値は0に設定しているので、lap_fb[]の最後の行に書き込むことにします。変数yが1以降のとき(2番目以降の行)では、lap_fb[]の最初の行から順に書き込みます。この部分を抜き出してリスト1 に示します。

             if (y == 0)
                lap_fb[(VERTICAL_PIXEL_WIDTH-1)*HORIZONTAL_PIXEL_WIDTH+x] = lap;
            else
                lap_fb[(y-1)*HORIZONTAL_PIXEL_WIDTH+x] = lap;

リスト 1:  lap_fb[]のライト部分の変更点


Cコードの合成のLatencyは480019クロックでした。なお、今回の画像は800ピクセルx 600行の画像を使用しています。つまり、1ピクセル1クロックとして全てのピクセルを処理すると480000クロック必要です。それと比較するとCコード合成時のLatencyは効率が良いと言えると思います。次にC/RTL協調シミュレーションを行うと、Latencyは1120051クロックでした。Cコードの合成のときのLatencyからすると2倍以上にレイテンシが増えています。C/RTL協調シミュレーション波形を見ると、リードは16バーストなのですが、ライトはシングル転送になっていました。このためC/RTL協調シミュレーション時のレイテンシが増えてしまったようです。詳しくは、「AXI4-Stream向きのコードで書いたラプラシアンフィルタをVivado HLSでテスト2」をご覧ください。

AXI4マスタのリードとライト両方にif文を使用した場合
次に、出力の画像フォーマットを出力画像の回り1ビットが0のフォーマットに戻すのは同じですが、1行余計にループを回すことにします。最初の行はlap_fb[]に書き込まないようにし、最後の行(つまり、1行多いので、リードするピクセルはもう残っていないため)cam_fb[]から読み込まないようにします。つまり、リードもライトもif文が入ることになります。リード部分の変更点をリスト2 にライト部分の変更点をリスト3 に示します。

    for (int y=0; y<=VERTICAL_PIXEL_WIDTH; y++){
        for (int x=0; x<HORIZONTAL_PIXEL_WIDTH; x++){
#pragma HLS PIPELINE
            if (y < VERTICAL_PIXEL_WIDTH)
                pix = cam_fb[y*HORIZONTAL_PIXEL_WIDTH+x];

リスト 2:  cam_fb[]のリード部分の変更点


            if (y != 0)
                lap_fb[(y-1)*HORIZONTAL_PIXEL_WIDTH+x] = lap;

リスト 3:  lap_fb[]のライト部分の変更点


この実装では、Cコードの合成時のLatencyは480821クロックでした。この実装でも、画像は800ピクセルx 600行の画像を使用しています。次にC/RTL協調シミュレーションを行ってみたところ、1121430クロックでした。この実装でも、Cコードの合成のときのLatencyからすると2倍以上にレイテンシが増えています。しかも、「AXI4マスタ・ライトのみにif文を使用した場合」よりも遅くなってしまいました。C/RTL協調シミュレーション波形を見ると、リードもライトもシングル転送でした。詳しくは、「AXI4-Stream向きのコードで書いたラプラシアンフィルタをVivado HLSでテスト3」をご覧ください。

2つの実装の結果から言えることは、メモリにリード、ライトする記述にif文があるとシングル転送になるのではないか?ということです。つまり、AXI4マスタ版で最速実装ができる幅は狭いということが言えると思います。
  1. 2017年04月26日 20:30 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0
»