FC2カウンター FPGAの部屋 Vivado HLS 2017.4 で片方が定数の場合の乗算の検討1(C シミュレーション)
FC2ブログ

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

FPGAの部屋

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

Vivado HLS 2017.4 で片方が定数の場合の乗算の検討1(C シミュレーション)

畳み込みニューラルネットワークの乗算を展開するため、片方が定数の乗算のリソース使用量を少なくできる方法を探っていく。ニューラルネットワークの重みは定数なので、変数にしておく必要はない。つまり、片方が定数ということで、乗算回路を簡単化することができるということだ。ASICでは重みが固定されてしまうので、決まりきった回路になって単一機能になってしまうが、FPGAは重みが変わったら、再コンフィギュレーションすればよいだけなので、デメリットにはならない。もし動作中に重みを変えたいときは、パーシャル・リコンフィギュレーションすれば良いだけである。ZynqやZynqMP でARMプロセッサからパーシャル・リコンフィギュレーションできれば自分で重みを変えられるので更に良いだろう。

さて最初のVivado HLS による乗数が定数の乗算の C サンプルコード(multi_test.cpp)を示す。

// multi_test.cpp
// 2018/01/23 by marsee
//

#include "multi_test.h"

int multi_test(ap_fixed_def in1,
        ap_fixed_def &out1, ap_fixed_def &out2){
#pragma HLS PIPELINE II=1
    out1 = in1 * (ap_fixed_def)1.75;
    //out2 = out1;

    out2 = in1/4 + in1/2 + in1;
    //out2 = (ap_fixed_def2)in1/4 + (ap_fixed_def2)in1/2 + (ap_fixed_def2)in1;
    //out1 = out2;

    return(0);
}


いろいろとコメントアウトしてある行は後で使用する。

つまり、乗数が整数値の乗算と、乗算をシフト+加算に直した演算だ。

ヘッダ・ファイルの multi_test.h を示す。

// multi_test.h
// 2018/01/23 by marsee
//

#ifndef __multi_test_H__
#define __multi_test_H__
#include <ap_fixed.h>

typedef ap_fixed<53, AP_TRN_ZERO, AP_SAT> ap_fixed_def;
typedef ap_fixed<63, AP_TRN_ZERO, AP_SAT> ap_fixed_def2;
//typedef ap_fixed<6, 4, AP_TRN, AP_WRAP> ap_fixed_def;
//typedef ap_fixed<7, 4, AP_TRN, AP_WRAP> ap_fixed_def2;

#endif



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

// multi_test.h
// 2018/01/23 by marsee
//

#include "multi_test.h"

int multi_test(ap_fixed_def in1,
        ap_fixed_def &out1, ap_fixed_def &out2);

int main(void){
    ap_fixed_def in1, in2, out1, out2;

    for(float v=0.25; v<=3.75; v+=0.25){
        in1 = (ap_fixed_def)v;
        multi_test(in1, out1, out2);
        printf("v = %f, out1 = %f, out2 = %f, float = %f", v, (float)out1, (float)out2, v*1.75);
        if(out1 != out2)
            printf(" error\n");
        else
            printf("\n");
    }

    return(0);
}


つまり、0.25 刻みに 0.25 ~ 3.75 までの量子化した数と 1.75 を乗算するということだ。
multi_out.cpp の out1 はin1 と 1.75 をそのまま乗算しているが、out2 は in1 に 1/4 を乗じた数(つまり 0.25乗)+ in1 に 1/2 を乗じた数(つまり 0.5 乗)+ in1 の演算となる。
out1 と out2 は同じ結果になるはずだ。つまりそのまま乗算してるか筆算の方式で乗算しているかの違いだ。
Vivado HLS 2017.4 のプロジェクトの multi_test を下に示す。
multi_test_1_180127.png

このままのソースコードで C シミュレーションを行ってみよう。結果を示す。
multi_test_2_180127.png

INFO: [SIM 2] *************** CSIM start ***************
INFO: [SIM 4] CSIM will launch GCC as the compiler.
Compiling ../../../multi_test_tb.cpp in debug mode
Compiling ../../../multi_test.cpp in debug mode
Generating csim.exe
v = 0.250000, out1 = 0.250000, out2 = 0.250000, float = 0.437500
v = 0.500000, out1 = 0.750000, out2 = 0.750000, float = 0.875000
v = 0.750000, out1 = 1.250000, out2 = 1.000000, float = 1.312500 error
v = 1.000000, out1 = 1.750000, out2 = 1.750000, float = 1.750000
v = 1.250000, out1 = 2.000000, out2 = 2.000000, float = 2.187500
v = 1.500000, out1 = 2.500000, out2 = 2.500000, float = 2.625000
v = 1.750000, out1 = 3.000000, out2 = 2.750000, float = 3.062500 error
v = 2.000000, out1 = 3.500000, out2 = 3.500000, float = 3.500000
v = 2.250000, out1 = 3.750000, out2 = 3.750000, float = 3.937500
v = 2.500000, out1 = 3.750000, out2 = 3.750000, float = 4.375000
v = 2.750000, out1 = 3.750000, out2 = 3.750000, float = 4.812500
v = 3.000000, out1 = 3.750000, out2 = 3.750000, float = 5.250000
v = 3.250000, out1 = 3.750000, out2 = 3.750000, float = 5.687500
v = 3.500000, out1 = 3.750000, out2 = 3.750000, float = 6.125000
v = 3.750000, out1 = 3.750000, out2 = 3.750000, float = 6.562500
INFO: [SIM 1] CSim done with 0 errors.
INFO: [SIM 3] *************** CSIM finish ***************


v = 0.75 と v = 1.75 のところで、out1 と out2 の値が違っている。これは、out2 では、演算ごとに飽和演算や丸めが行われるので、演算精度が足りないのではないか?と思われる。out1 では一発で乗算をしてしまうので、演算による誤差の蓄積は無いと考えられる。
それでも、浮動小数点数の演算と違って、整数部が3ビットなので、整数は3 ~ -4 までしか表せないので、飽和演算で飽和はしても値は差が大きくなる。これは元来、整数部のビットが足りないので仕方がないことだ。

さて、次に、out2 の途中の演算でもう1ビット小数部のビットを追加してみてはどうだろうか?
小数部を1ビット増やして 3 ビットとした ap_fixed_def2 で乗算をキャストしてみよう。
multi_test_3_180127.png

これで C シミュレーションを行った。結果を示す。
multi_test_4_180127.png

INFO: [SIM 2] *************** CSIM start ***************
INFO: [SIM 4] CSIM will launch GCC as the compiler.
Compiling ../../../multi_test.cpp in debug mode
Generating csim.exe
v = 0.250000, out1 = 0.250000, out2 = 0.250000, float = 0.437500
v = 0.500000, out1 = 0.750000, out2 = 0.750000, float = 0.875000
v = 0.750000, out1 = 1.250000, out2 = 1.250000, float = 1.312500
v = 1.000000, out1 = 1.750000, out2 = 1.750000, float = 1.750000
v = 1.250000, out1 = 2.000000, out2 = 2.000000, float = 2.187500
v = 1.500000, out1 = 2.500000, out2 = 2.500000, float = 2.625000
v = 1.750000, out1 = 3.000000, out2 = 3.000000, float = 3.062500
v = 2.000000, out1 = 3.500000, out2 = 3.500000, float = 3.500000
v = 2.250000, out1 = 3.750000, out2 = 3.750000, float = 3.937500
v = 2.500000, out1 = 3.750000, out2 = 3.750000, float = 4.375000
v = 2.750000, out1 = 3.750000, out2 = 3.750000, float = 4.812500
v = 3.000000, out1 = 3.750000, out2 = 3.750000, float = 5.250000
v = 3.250000, out1 = 3.750000, out2 = 3.750000, float = 5.687500
v = 3.500000, out1 = 3.750000, out2 = 3.750000, float = 6.125000
v = 3.750000, out1 = 3.750000, out2 = 3.750000, float = 6.562500
INFO: [SIM 1] CSim done with 0 errors.
INFO: [SIM 3] *************** CSIM finish ***************


今度は、out1 と out2 の値はすべて合っている。やはり桁落ちしていたようだ。

次に、現在は任意精度固定小数点データ型の ap_fixed の量子化モードは 0 への切り捨て(AP_TRN_ZERO)で、オーバーフローモードは飽和(AP_SAT)だが、これだと回路も大きくなるし、レイテンシも増える。回路が小さくなるのは、量子化モードが負の無限大への切り捨て(AP_TRN)で、オーバーフローモードが折り返し(AP_WRAP)だ。これでやってみよう。
この場合は、折り返してしまうので、ビット幅が足りないと演算結果が不正になってしまう。よって整数部を 1 ビット増やして、4 ビットにしてみよう。小数部は 2 ビットのままとする。
multi_test.h の下の 2 行のコメントを外して、上の 2 行をコメントアウトする。
multi_test_5_180127.png

out2 の記述はap_fixed_def2 で乗算をキャストしていない文に戻した。
multi_test_6_180127.png

これで、C シミュレーションを行った。結果を示す。
multi_test_7_180127.png

INFO: [SIM 2] *************** CSIM start ***************
INFO: [SIM 4] CSIM will launch GCC as the compiler.
Compiling ../../../multi_test_tb.cpp in debug mode
Compiling ../../../multi_test.cpp in debug mode
Generating csim.exe
v = 0.250000, out1 = 0.250000, out2 = 0.250000, float = 0.437500
v = 0.500000, out1 = 0.750000, out2 = 0.750000, float = 0.875000
v = 0.750000, out1 = 1.250000, out2 = 1.000000, float = 1.312500 error
v = 1.000000, out1 = 1.750000, out2 = 1.750000, float = 1.750000
v = 1.250000, out1 = 2.000000, out2 = 2.000000, float = 2.187500
v = 1.500000, out1 = 2.500000, out2 = 2.500000, float = 2.625000
v = 1.750000, out1 = 3.000000, out2 = 2.750000, float = 3.062500 error
v = 2.000000, out1 = 3.500000, out2 = 3.500000, float = 3.500000
v = 2.250000, out1 = 3.750000, out2 = 3.750000, float = 3.937500
v = 2.500000, out1 = 4.250000, out2 = 4.250000, float = 4.375000
v = 2.750000, out1 = 4.750000, out2 = 4.500000, float = 4.812500 error
v = 3.000000, out1 = 5.250000, out2 = 5.250000, float = 5.250000
v = 3.250000, out1 = 5.500000, out2 = 5.500000, float = 5.687500
v = 3.500000, out1 = 6.000000, out2 = 6.000000, float = 6.125000
v = 3.750000, out1 = 6.500000, out2 = 6.250000, float = 6.562500 error
INFO: [SIM 1] CSim done with 0 errors.
INFO: [SIM 3] *************** CSIM finish ***************


やはり、out1 と out2 が違ってい部分があるので、先ほど同様、小数部を1ビット増やして 3 ビットとした ap_fixed_def2 で乗算をキャストしてみよう。
multi_test_3_180127.png

これで、C シミュレーションをやり直した。結果を示す。
multi_test_8_180127.png

INFO: [SIM 2] *************** CSIM start ***************
INFO: [SIM 4] CSIM will launch GCC as the compiler.
Compiling ../../../multi_test.cpp in debug mode
Generating csim.exe
v = 0.250000, out1 = 0.250000, out2 = 0.250000, float = 0.437500
v = 0.500000, out1 = 0.750000, out2 = 0.750000, float = 0.875000
v = 0.750000, out1 = 1.250000, out2 = 1.250000, float = 1.312500
v = 1.000000, out1 = 1.750000, out2 = 1.750000, float = 1.750000
v = 1.250000, out1 = 2.000000, out2 = 2.000000, float = 2.187500
v = 1.500000, out1 = 2.500000, out2 = 2.500000, float = 2.625000
v = 1.750000, out1 = 3.000000, out2 = 3.000000, float = 3.062500
v = 2.000000, out1 = 3.500000, out2 = 3.500000, float = 3.500000
v = 2.250000, out1 = 3.750000, out2 = 3.750000, float = 3.937500
v = 2.500000, out1 = 4.250000, out2 = 4.250000, float = 4.375000
v = 2.750000, out1 = 4.750000, out2 = 4.750000, float = 4.812500
v = 3.000000, out1 = 5.250000, out2 = 5.250000, float = 5.250000
v = 3.250000, out1 = 5.500000, out2 = 5.500000, float = 5.687500
v = 3.500000, out1 = 6.000000, out2 = 6.000000, float = 6.125000
v = 3.750000, out1 = 6.500000, out2 = 6.500000, float = 6.562500
INFO: [SIM 1] CSim done with 0 errors.
INFO: [SIM 3] *************** CSIM finish ***************


これで、out1 と out2 の値が合った。
  1. 2018年01月28日 04:00 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

コメント

コメントの投稿


管理者にだけ表示を許可する

トラックバック URL
http://marsee101.blog.fc2.com/tb.php/4058-e40dbdd5
この記事にトラックバックする(FC2ブログユーザー)