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

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

FPGAの部屋

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

Vivado HLS 2017.4 で片方が定数の場合の乗算の検討8(畳み込み演算5)

Vivado HLS 2017.4 で片方が定数の場合の乗算の検討7(畳み込み演算4)”の続き。

前回は、2 個目の畳み込み演算の重みの配列を使用して違いを確認した。今回は、2 つの畳み込み演算を並列にやってみよう。

2 つの畳み込み演算を並列に行うためにC ソースコードを少し変更した。プロジェクトも新しく作成した。
multi_test4 プロジェクトを示す。
multi_test_60_180205.png

multi_test4.cpp を示す。

// multi_test4.cpp
// 2018/02/04 by marsee
//

#include <ap_fixed.h>
#include "multi_test4.h"
#include "conv1_weight.h"

int multi_test4(ap_ufixed_in in[25], ap_fixed_add &out0, ap_fixed_add &out1){
#pragma HLS ARRAY_PARTITION variable=in complete dim=1

    ap_fixed_madd out_temp = 0.0;

#pragma HLS RESOURCE variable=out_temp core=AddSub
#pragma HLS PIPELINE II=1

    conv0: for(int k=0; k<2; k++){
        conv1: for(int m=0; m<5; m++){
            conv2: for(int n=0; n<5; n++){
                out_temp += in[m*5+n] * conv1_weight[k][0][m][n];
            }
        }
        if(k==0)
            out0 = out_temp;
        else
            out1 = out_temp;
        out_temp = 0.0;
    }

    return(0);
}


これは前と同じだが、multi_test4.h を示す。

// multi_test4.h
// 2018/02/04 by marsee
//

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

typedef ap_ufixed<80, AP_TRN, AP_WRAP> ap_ufixed_in;
typedef ap_fixed<91, AP_TRN, AP_WRAP> ap_fixed_weight;
typedef ap_fixed<226, AP_TRN, AP_WRAP> ap_fixed_madd;
typedef ap_fixed<166, AP_TRN_ZERO, AP_SAT> ap_fixed_add;

#endif


muti_test4_tb.cpp を示す。

// multi_test4_tb.h
// 2018/02/04 by marsee
//

#include "multi_test4.h"

int multi_test4(ap_ufixed_in in[25], ap_fixed_add &out0, ap_fixed_add &out1);

int main(void){
    ap_ufixed_in in[25];
    ap_fixed_add out0, out1;
    ap_ufixed_in v = 0.5;

    for(int i=0; i<25; i=i++){
        in[i] = (ap_ufixed_in)v;
        v += (ap_ufixed_in)0.00390625;
        printf("in[%d] = %f\n", i, (float)v);
    }

    multi_test4(in, out0, out1);

    printf("out0 = %f\n", (float)out0);
    printf("out1 = %f\n", (float)out1);

    return(0);
}


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

出力値は正常だ。

C コードの合成を行った。結果を示す。
multi_test_62_180205.png

Latency は 7 クロックだった。2 つの畳み込み演算を並列にできているようだ。
なお、conv0 の for ループに UNROLL 指示子を入れても、入れなくても結果は変わらなかった。
BRAM_18K や DSP48E は使用していない。FF は 1,424 個、LUT は 2,767 個使用している。

次に、C/RTL 協調シミュレーションを行った。結果を示す。
multi_test_63_180205.png

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

2 つの畳み込み演算器がデータを受け取ってから、演算を行い、出力の値をその先の回路が受け取るまでに 7 クロックだった。

Export RTL を行った。結果を示す。
なお、Vivado synthesis, place and route にチェックを入れてある。
multi_test_65_180205.png

こちらは、1 個目の畳み込み演算器の影響を受けるだろうから、この値になっていると思う。少し、危なさそうな数値だ。
  1. 2018年02月05日 05:13 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS 2017.4 で片方が定数の場合の乗算の検討7(畳み込み演算4)

Vivado HLS 2017.4 で片方が定数の場合の乗算の検討6(畳み込み演算3)”の続き。

前回は、従来通りの畳み込み演算の重みの配列を使用して、LUT にマップされた畳み込み演算を行うことができた。今回は、畳み込みフィルタは 2 個ある。前回はそのうちの 1 個目を使用していたので、今回は、2 個目の畳み込み演算の重みの配列を使用して違いを見てみよう。

まずは、2 個目の畳み込み演算の重みの配列を使用するために、19 行目の

conv1_weight[0][0][m][n]

conv1_weight[1][0][m][n]

に変更して、2 個目の畳み込み演算の重みの配列を使用する。
multi_test_54_180204.png

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

出力 out = -0.18555 だった。これだとマイナスの値なので、ReLU で 0 になるはずだ。

C コードの合成を行った。左に今回の 2 個目の畳み込み演算の重みの配列を使用した場合、右に前回の 1 個目の畳み込み演算の重みの配列を使用した場合の合成結果を示す。
multi_test_56_180204.pngmulti_test_50_180203.png

Latency は前回は 7 クロックだったが、今回は 4 クロックに減っている。Interval は変わらずに 1 クロックのままだ。
リソース使用量の FF も前回は、1,286 個だったが、今回は 478 個で約 37 % に減っている。LUT も前回は、2,106 個だったが、今回は805 個で、約 38 % に減っていた。
2 個目の畳み込み演算の重みの配列を見ると、0 が多く入っていた。そして他の重みもたぶんリソース使用量が少なくなる値だったんだと思う。
このように、DSP48E を使用せずに片方が定数の場合は演算を簡単化することができるのだが、重みの値によって圧縮率が異なる。2つに畳み込みフィルタを使用する場合に、2つ独立にインスタンスするとLatency が異なると結果が出てくるクロックが異なるので、Latency を合わせる必要がある。

次に、C/RTL 協調シミュレーションを行った。結果を示す。
multi_test_57_180204.png

やはり、Latency は 4 クロックだった。
C/RTL 協調シミュレーションの波形を示す。
multi_test_58_180204.png

畳み込み演算器がデータを受け取ってから、演算を行い、出力の値をその先の回路が受け取るまでに 4 クロックだった。

Export RTL を行った。結果を示す。
なお、Vivado synthesis, place and route にチェックを入れてある。
multi_test_59_180204.png

LUT は247 個、SRL は0 個なので、LUT は247 個使用する。こちらのFinal Timing は問題なさそうだ。
  1. 2018年02月04日 04:55 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS 2017.4 で片方が定数の場合の乗算の検討6(畳み込み演算3)

Vivado HLS 2017.4 で片方が定数の場合の乗算の検討5(畳み込み演算2)”の続き。

前回は、乗算記号の * を使用して、畳み込みニューラルネットワークの畳み込み演算の重みを定数と置いたときの乗算で途中の演算の小数点以下の精度を下げてやってみた。今回は、従来通りの畳み込み演算の重みの配列を使用して、LUT にマップした演算ができないかどうか?を確かめてみた。

さて、Vivado HLS 2017.4 で multi_test3 プロジェクトを作成した。
multi_test_48_180203.png

multi_test3.cpp を貼っておく。

// multi_test3.cpp
// 2018/01/30 by marsee
//

#include <ap_fixed.h>
#include "multi_test3.h"
#include "conv1_weight.h"

int multi_test3(ap_ufixed_in in[25], ap_fixed_add &out){
#pragma HLS ARRAY_PARTITION variable=in complete dim=1
#pragma HLS PIPELINE II=1

    ap_fixed_madd out_temp = 0.0;
#pragma HLS RESOURCE variable=out_temp core=AddSub

    conv1: for(int m=0; m<5; m++){
#pragma HLS UNROLL
        conv2: for(int n=0; n<5; n++){
            out_temp += in[m*5+n] * conv1_weight[0][0][m][n];
        }
    }

    out = out_temp;

    return(0);
}


ご覧の様に、今までやってたように for ループを使用しているが、unroll 指示子でループを展開して、RESOURCE 指示子で AddSub を指定すれば、LUT を使用してくれるようだ。

次に、multi_test3.h を示す。

// multi_test3.h
// 2018/01/30 by marsee
//

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

typedef ap_ufixed<80, AP_TRN, AP_WRAP> ap_ufixed_in;
typedef ap_fixed<91, AP_TRN, AP_WRAP> ap_fixed_weight;
typedef ap_fixed<226, AP_TRN, AP_WRAP> ap_fixed_madd;
typedef ap_fixed<166, AP_TRN_ZERO, AP_SAT> ap_fixed_add;

#endif


conv_weight.h を示す。

// conv1_weight.h
// 2017/12/06 10:54:11 by marsee

const float conv1_fweight[2][1][5][5] = 
{
    {
        {
            {0.764403421227,0.658424746889,0.595604201652,0.554044871161,0.367767232883},
            {0.582414155838,0.413274869036,0.31659268154,0.3508390519,0.331194144626},
            {0.589182274309,0.462105790282,-0.241299390378,-0.10093021104,0.233291757594},
            {0.792411286764,0.315893121865,0.0397628864727,0.356726636694,0.426826537165},
            {0.634481192118,0.651475977113,0.688949928547,0.707285991358,0.681420943406}
        }
    }
    ,
    {
        {
            {0.00564732125401,-0.012955272371,-0.0231571581103,-0.00289983746176,0.0281080593816},
            {-0.0115360072012,0.00253310449813,-0.00860163957467,0.00112793810127,-0.01455040341},
            {-0.00881717612899,-0.00902248113722,0.0004194288468,0.00110240651437,-0.0140454059394},
            {0.00271556513713,-0.00307791921855,0.000117170379207,-0.00891721414879,0.0173026634286},
            {0.000808453898046,0.000116327205532,-0.00275343050716,-0.00683461392689,-0.0169130858704}
        }
    }
};

const ap_fixed<91, AP_TRN, AP_WRAP> conv1_weight[2][1][5][5] =
{
    {
        {
            {0.765625,0.66015625,0.59375,0.5546875,0.3671875},
            {0.58203125,0.4140625,0.31640625,0.3515625,0.33203125},
            {0.58984375,0.4609375,-0.23828125,-0.09765625,0.234375},
            {0.79296875,0.31640625,0.0390625,0.35546875,0.42578125},
            {0.6328125,0.65234375,0.6875,0.70703125,0.6796875}
        }
    }
    ,
    {
        {
            {0.00390625,-0.0078125,-0.01953125,0.0,0.02734375},
            {-0.0078125,0.00390625,-0.00390625,0.0,-0.01171875},
            {-0.00390625,-0.00390625,0.0,0.0,-0.01171875},
            {0.00390625,0.0,0.0,-0.00390625,0.015625},
            {0.0,0.0,0.0,-0.00390625,-0.01171875}
        }
    }
};


multi_test3_tb.cpp を示す。

// multi_test3_tb.h
// 2018/01/30 by marsee
//

#include "multi_test3.h"

int multi_test3(ap_ufixed_in in[25], ap_fixed_add &out);

int main(void){
    ap_ufixed_in in[25];
    ap_fixed_add out;
    ap_ufixed_in v = 0.5;

    for(int i=0; i<25; i=i++){
        in[i] = (ap_ufixed_in)v;
        v += (ap_ufixed_in)0.00390625;
        printf("in[%d] = %f\n", i, (float)v);
    }

    multi_test3(in, out);

    printf("out = %f\n", (float)out);

    return(0);
}


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

C コードの合成を行った。結果を示す。
multi_test_50_180203.png

Latency は 7 クロックだった。Interval は 1 で 1 クロックごとに次のデータを入れることができる。
リソース使用量は、BRAM_18K が 0 個、DSP48E も 0 個で、演算を LUT にマップできていることが分かった。FF は 1,286 個、LUT は 2,106 個使用している。うまく行っている。

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

Latency は 7 クロックだった。

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

畳み込み演算器がデータを受け取ってから、演算を行い、出力の値をその先の回路が受け取るまでに 7 クロックだった。

Export RTL を行った。結果を示す。
なお、Vivado synthesis, place and route にチェックを入れてある。
multi_test_53_180203.png

LUT 使用数は 852 個だった。でもSRL の 36 もLUTとしての数に含めるべきかもしれない?
そうすると、852 + 36 = 888 個だった。
  1. 2018年02月03日 05:44 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS 2017.4 で片方が定数の場合の乗算の検討5(畳み込み演算2)

”Vivado HLS 2017.4 で片方が定数の場合の乗算の検討4(畳み込み演算1)”の続き。

前回は、乗算記号の * を使用して、畳み込みニューラルネットワークの畳み込み演算の重みを定数と置いたときの乗算を検討した。今回は、同様に演算をするのだが、途中の演算の小数点以下の精度を下げてみようと思う。

multi_test2.cpp、multi_test2_tb.cpp はそのままで、multi_test2.h だけ変更した。

typedef ap_fixed<9, 1, AP_TRN, AP_WRAP> ap_fixed_multi;


multi_test_42_180202.png

つまり、17 ビットだった乗算のビット幅を 9 ビットに変更した。減らした 8 ビットはすべて小数部のビット数となる。これで、飽和は関係なく、量子化モードだけが効くことになる。
これで C シミュレーションを行った。結果を示す。
multi_test_43_180202.png
出力は、out = 6.070313 となった。前回の結果が out = 6.113281 だったため、減ってしまっている。

C コードの合成を行った。結果を示す。
multi_test_44_180202.pngmulti_test_39_180131.png
左が今回の結果、右が前回の結果だ。
Latency が 8 クロックだったのが、6 クロックになっている。FF が 1305 個から 1193 個に減少した。 LUT は 2251 個から 2022 個に減少した。

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

C シミュレーションの波形を示す。
multi_test_46_180202.png

Export RTL を行った。その際に、Vivado synthesis, place and route にチェックを入れた。
結果を示す。
multi_test_47_180202.png

実際にVivado で論理合成、インプリメントしたときのリソース使用量は、だいぶ少なくなっている。FF は 1193 個だったのが、639 個に、LUT は 2022 個だったのが、853 個に減少した。
Final Timing を見ると、これでVivado にIP として持っていくとタイミングエラーが出そうだが、とりあえずの仮のIP 化なので、そのままとする。
  1. 2018年02月02日 05:22 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS 2017.4 で片方が定数の場合の乗算の検討4(畳み込み演算1)

Vivado HLS 2017.4 で片方が定数の場合の乗算の検討3(C コードの合成2)”の続き。

前回は、片方が定数のときの乗算は、片方が定数と定義して、乗算記号の * を使ったほうがリソース使用量点から言っても効率が良いということになった。今回は、乗算記号の * を使用して、畳み込みニューラルネットワークの畳み込み演算の重みを定数と置いたときの乗算を検討してみよう。

Vivado HLS 2017.4 で、multi_test2 プロジェクトを作成した。
multi_test_30_180131.png

multi_test2.h を貼っておく。

// multi_test2.h
// 2018/01/30 by marsee
//

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

typedef ap_ufixed<80, AP_TRN, AP_WRAP> ap_ufixed_in;
typedef ap_fixed<91, AP_TRN, AP_WRAP> ap_fixed_weight;
typedef ap_fixed<171, AP_TRN, AP_WRAP> ap_fixed_multi;
typedef ap_fixed<166, AP_TRN_ZERO, AP_SAT> ap_fixed_add;

#endif


multi_test2.cpp を貼っておく。

// multi_test2.cpp
// 2018/01/30 by marsee
//

#include "multi_test2.h"

int multi_test2(ap_ufixed_in in[25], ap_fixed_add &out){
#pragma HLS PIPELINE II=1

    out = (ap_fixed_multi)(in[0]*(const ap_fixed_weight)0.765625) +
            (ap_fixed_multi)(in[1]*(const ap_fixed_weight)0.66015625) +
            (ap_fixed_multi)(in[2]*(const ap_fixed_weight)0.59375) +
            (ap_fixed_multi)(in[3]*(const ap_fixed_weight)0.5546875) +
            (ap_fixed_multi)(in[4]*(const ap_fixed_weight)0.3671875) +
            (ap_fixed_multi)(in[5]*(const ap_fixed_weight)0.58203125) +
            (ap_fixed_multi)(in[6]*(const ap_fixed_weight)0.4140625) +
            (ap_fixed_multi)(in[7]*(const ap_fixed_weight)0.31640625) +
            (ap_fixed_multi)(in[8]*(const ap_fixed_weight)0.3515625) +
            (ap_fixed_multi)(in[9]*(const ap_fixed_weight)0.33203125) +
            (ap_fixed_multi)(in[10]*(const ap_fixed_weight)0.58984375) +
            (ap_fixed_multi)(in[11]*(const ap_fixed_weight)0.4609375) +
            (ap_fixed_multi)(in[12]*(const ap_fixed_weight)(-0.23828125))+
            (ap_fixed_multi)(in[13]*(const ap_fixed_weight)(-0.09765625)) +
            (ap_fixed_multi)(in[14]*(const ap_fixed_weight)0.234375) +
            (ap_fixed_multi)(in[15]*(const ap_fixed_weight)0.79296875) +
            (ap_fixed_multi)(in[16]*(const ap_fixed_weight)0.31640625) +
            (ap_fixed_multi)(in[17]*(const ap_fixed_weight)0.0390625) +
            (ap_fixed_multi)(in[18]*(const ap_fixed_weight)0.35546875) +
            (ap_fixed_multi)(in[19]*(const ap_fixed_weight)0.42578125) +
            (ap_fixed_multi)(in[20]*(const ap_fixed_weight)0.6328125) +
            (ap_fixed_multi)(in[21]*(const ap_fixed_weight)0.65234375) +
            (ap_fixed_multi)(in[22]*(const ap_fixed_weight)0.6875) +
            (ap_fixed_multi)(in[23]*(const ap_fixed_weight)0.70703125) +
            (ap_fixed_multi)(in[24]*(const ap_fixed_weight)0.6796875);

    return(0);
}


multi_test2_tb.cpp を貼っておく。

// multi_test2_tb.h
// 2018/01/30 by marsee
//

#include "multi_test2.h"

int multi_test2(ap_ufixed_in in[25], ap_fixed_add &out);

int main(void){
    ap_ufixed_in in[25];
    ap_fixed_add out;
    ap_ufixed_in v = 0.5;

    for(int i=0; i<25; i=i++){
        in[i] = (ap_ufixed_in)v;
        v += (ap_ufixed_in)0.00390625;
        printf("in[%d] = %f\n", i, (float)v);
    }

    multi_test2(in, out);

    printf("out = %f\n", (float)out);

    return(0);
}


C シミュレーションを行った。
multi_test_31_180131.png

INFO: [SIM 2] *************** CSIM start ***************
INFO: [SIM 4] CSIM will launch GCC as the compiler.
Compiling ../../../multi_test2_tb.cpp in debug mode
Generating csim.exe
in[0] = 0.503906
in[1] = 0.507813
in[2] = 0.511719
in[3] = 0.515625
in[4] = 0.519531
in[5] = 0.523438
in[6] = 0.527344
in[7] = 0.531250
in[8] = 0.535156
in[9] = 0.539063
in[10] = 0.542969
in[11] = 0.546875
in[12] = 0.550781
in[13] = 0.554688
in[14] = 0.558594
in[15] = 0.562500
in[16] = 0.566406
in[17] = 0.570313
in[18] = 0.574219
in[19] = 0.578125
in[20] = 0.582031
in[21] = 0.585938
in[22] = 0.589844
in[23] = 0.593750
in[24] = 0.597656
out = 6.113281
INFO: [SIM 1] CSim done with 0 errors.
INFO: [SIM 3] *************** CSIM finish ***************


C コードの合成を行った。結果を示す。
multi_test_32_180131.png

Latency が 15 クロックは良いのだが、Interval が 13 クロックになっている。
リソース使用量は、DSP48E が 22 個、FF が 295 個、LUT が 581 個となった。

Analysis 画面を示す。
multi_test_33_180131.png

in_V の read が横にずらっと並んでしまっている。これでは要求仕様を満たさない。Interval は 1 クロックにしたい。

multi_test2.v を見てみよう。
multi_test_34_180131.png

in_V はメモリ・インターフェースになっている。

それでは、入力 in の配列をバラバラにするにはどうするといえば、array partition 指示子で complete オプションを付ければ良い。

#pragma HLS ARRAY_PARTITION variable=in complete dim=1


multi_test_35_180131.png

これで、C コードの合成を行った。結果を示す。
multi_test_36_180131.png

Latency は 12 クロックで 15 クロックより少なくなった。Interval は 1 クロックで 1 クロックごとに処理することができる。これは希望通り。
リソース使用量は、DSP48E は 22 個で同じだが、 FF が約 5.7 倍の 1685 個、LUT が約 1.7 倍の 988 個だった。

multi_test.v を見ると、in の配列が array partition complete でバラバラになり、in_0_V から in_24_V までの入力ポートがインスタンスされているのが分かる。
multi_test_37_180131.png

さて、次は、DSP48E を使用しないようにしたい。それは、すべての畳み込み演算をDSP48E を使用して行うとDSP48E が足りなくなるからだ。以前はLUTに演算を割り振る方法は分からなかったのだが、今回、いろいろとトライするうちに、その方法を見つけることができた。それは、RESOURCE 指示子で core = AddSub を指定することだ。
早速、試してみよう。

#pragma HLS RESOURCE variable=out core=AddSub


multi_test_38_180131.png

C コードの合成を行った。結果を示す。
multi_test_39_180131.png

Latency は 8 クロックで、Interval は 1 クロックだった。Latency がさらに縮まった。
リソース使用量は、DSP48E は使用しなかったが、その代わり FF が 1305 個、LUT が 2251 個使用されている。

Analysis 画面を示す。
multi_test_40_180131.png

read が縦に並んで 1 クロックで行っているのが分かる。

multi_test2.v を示す。前回同様に、in_0_V から in_24_V までの入力ポートがインスタンスされているのが分かる。
multi_test_41_180131.png
  1. 2018年01月31日 07:05 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS 2017.4 で片方が定数の場合の乗算の検討3(C コードの合成2)

Vivado HLS 2017.4 で片方が定数の場合の乗算の検討2(C コードの合成1)”の続き。

前回は、乗数が定数の場合の乗算のC コードの合成を行って、2 つの量子化モードとオーバーフローモードでの演算の比較を行った。今回は、片方が定数なので、乗算をシフト+加算に置き換えた演算で、2 つの量子化モードとオーバーフローモードでの演算の比較を行う。

まずは、任意精度固定小数点データ型の ap_fixed の量子化モードは 0 への切り捨て(AP_TRN_ZERO)で、オーバーフローモードは飽和(AP_SAT)に設定した。つまり、multi_test.h で上の 2 行のコメントを外して、下の 2 行をコメントアウトした。
multi_test_9_180128.png

そして、multi_test.cpp はout1 を out2 からコピーして、本来の out1 をコメントアウトした。これは、out2 のリソース使用量を見るためだ。
multi_test_17_180128.png

これで C コードの合成を行った。結果を示す。
multi_test_18_180129.png

Latency が 2 クロックになって、更にレイテンシが増えている。これだと、入力したクロックの 2 クロック後が出力のクロックになる。
リソース使用量は、FF が 25 個、LUT が 269 個となっている。今までで一番リソース使用量が多い。

Analysis 画面を示す。
multi_test_19_180129.png

3 個のステートがある。

次に、multi_test.h の下の 2 行を生かして、任意精度固定小数点データ型の ap_fixed の量子化モードが負の無限大への切り捨て(AP_TRN)で、オーバーフローモードが折り返し(AP_WRAP)に変更してみよう。
multi_test_5_180127.png

これで、C コードの合成を行った。結果を示す。
multi_test_20_180129.png

Latency は 0 クロックになったので、組み合わせ回路になっている。
リソース使用量は、LUT が 53 個だ。

Analysis 画面を示す。
multi_test_21_180129.png

今度は、乗数が定数の乗算で C コードの合成結果の in1 を 2 倍して、in1 の 1/4 の数を引く方法をやってみようと思う。
multi_test_22_180129.png

この時に C シミュレーションを行ったところ、小数部のビット幅を 1 ビットずつ増やす必要があった。
multi_test.h を示す。
multi_test_23_180129.png

C コードの合成を行った。結果を示す。
multi_test_24_180129.png

Latency が 1 クロックで、FF は 12 個、LUT は 227 個使用している。
これは、”Vivado HLS 2017.4 で片方が定数の場合の乗算の検討2(C コードの合成1)”の時の乗数が定数の時と同じ合成レポートになった。

Analysis 画面を示す。
multi_test_25_180129.png

multi_test.h の下の 2 行を生かして、任意精度固定小数点データ型の ap_fixed の量子化モードが負の無限大への切り捨て(AP_TRN)で、オーバーフローモードが折り返し(AP_WRAP)に変更した。
multi_test_26_180129.png

C コードの合成を行った。結果を示す。
multi_test_28_180129.png

これも、”Vivado HLS 2017.4 で片方が定数の場合の乗算の検討2(C コードの合成1)”の時の乗数が定数の時と同じ合成レポートになった。

Analysis 画面を示す。
multi_test_29_180129.png

結論としては、片方が定数の乗算を使用して、任意精度固定小数点データ型の ap_fixed の量子化モードが負の無限大への切り捨て(AP_TRN)で、オーバーフローモードが折り返し(AP_WRAP)を使用したほうが良さそうだ。
  1. 2018年01月29日 05:02 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS 2017.4 で片方が定数の場合の乗算の検討2(C コードの合成1)

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

前回は、Vivado HLS 2017.4 で片方が定数の場合の乗算を乗算記号を使用した演算と 2 の n 乗の数に分解し、それを足し合わせることで乗算する演算との比較を C シミュレーションで行った。今回は、C コードの合成を行って、2 つの演算の比較を行う。

まずは、multi_test.h を元に戻す。つまり、任意精度固定小数点データ型の ap_fixed の量子化モードは 0 への切り捨て(AP_TRN_ZERO)で、オーバーフローモードは飽和(AP_SAT)にするということだ。
multi_test_9_180128.png

次に、multi_test.cpp を out1 の通常の乗算記号を使った out1 の演算だけにする。out2 は out1 の出力をコピーする。
multi_test_10_180128.png

これで C コードの合成を行った。なお、使用したFPGAは、PYNQ やZYBO Z7-20 を想定した xc7z020clg400-1 だ。
結果を示す。
multi_test_11_180128.png

Estmated は 8.30 ns だった。
Latency が 1 クロックなので 2 クロックで 1 出力だが、PIPELINE指示子があるので、Interval は 1 クロックになっている。FF は 12 個、LUT は 227 個使用している。飽和演算をしているので、組み合わせ回路では回路的にダメの様で 2 クロックに分けているのではないだろうか?

Analysis 画面を示す。
multi_test_12_180128.png

出力された Verilog HDL コードを見てみよう。
multi_test_13_180128.png

演算は、in1 を 3 ビット左シフトする。下2ビットは小数点部なので、3 ビットシフトするということは 2 を乗算するのに等しい。そこから in1 そのものを引いている。つまり、in1 の 1/4 (0.25 )を引いている。 2 - 0.25 = 1.75 倍ということになる。思ったより賢い。乗算をシフト+引き算で実現している。

それでは、量子化モードが負の無限大への切り捨て(AP_TRN)で、オーバーフローモードが折り返し(AP_WRAP)に変更する。
multi_test.h の下の 2 行のコメントを外して、上の 2 行をコメントアウトする。
multi_test_5_180127.png

これで C コードの合成を行った。結果を示す。
multi_test_14_180128.png

今回は、Latency が 0 クロックなので、組み合わせ回路となっている。Estmated は 1.92 ns なので、500 MHz でも動作可能な数値だ。
組み合わせ回路なので、FF は 0 個、LUT はわずか 15 個となっている。ap_fixed の量子化モードは 0 への切り捨て(AP_TRN_ZERO)で、オーバーフローモードは飽和(AP_SAT)のモードの時と比べて、LUT は 15 / 227 ≒ 0.066 倍となり、7 % 弱となっている。
つまり、演算器のビット幅は増えても、量子化モードやオーバーフローモードの回路負担が大きいので、飽和演算をしたほうが回路規模は大きいようだ。この結果からではまだ何とも言えないだろうが、途中の計算では、無理にビット幅を落として飽和演算をするよりも、演算器のビット幅を増やして、桁あふれの無い演算をしたほうがリソース使用量は少ないという結論になった。

Analysis 画面を見てみよう。
multi_test_15_180128.png

組み合わせ回路なので、C0 ステートのみとなっているようだ。演算も1つだけでとってもシンプルだ。

出力された Verilog HDL コードを見てみよう。
multi_test_16_180128.png

ポート宣言を見ても組み合わせ回路なので、クロックとリセットが無い。
演算は、in1 を 3 ビット左シフトし、そこから in1 自体を引いているだけだ。
multi_test.v を貼っておく。

// ==============================================================
// RTL generated by Vivado(TM) HLS - High-Level Synthesis from C, C++ and SystemC
// Version: 2017.4
// Copyright (C) 1986-2017 Xilinx, Inc. All Rights Reserved.
// 
// ===========================================================

`timescale 1 ns / 1 ps 

(* CORE_GENERATION_INFO="multi_test,hls_ip_2017_4,{HLS_INPUT_TYPE=cxx,HLS_INPUT_FLOAT=0,HLS_INPUT_FIXED=1,HLS_INPUT_PART=xc7z020clg400-1,HLS_INPUT_CLOCK=10.000000,HLS_INPUT_ARCH=pipeline,HLS_SYN_CLOCK=1.915000,HLS_SYN_LAT=0,HLS_SYN_TPT=1,HLS_SYN_MEM=0,HLS_SYN_DSP=0,HLS_SYN_FF=0,HLS_SYN_LUT=15}" *)

module multi_test (
        ap_start,
        ap_done,
        ap_idle,
        ap_ready,
        in1_V,
        out1_V,
        out1_V_ap_vld,
        out2_V,
        out2_V_ap_vld,
        ap_return
);


input   ap_start;
output   ap_done;
output   ap_idle;
output   ap_ready;
input  [5:0] in1_V;
output  [5:0] out1_V;
output   out1_V_ap_vld;
output  [5:0] out2_V;
output   out2_V_ap_vld;
output  [31:0] ap_return;

reg out1_V_ap_vld;
reg out2_V_ap_vld;

wire   [4:0] tmp_fu_58_p1;
wire   [7:0] p_shl_fu_62_p3;
wire  signed [7:0] OP1_V_cast_fu_54_p1;
wire   [7:0] p_Val2_s_fu_70_p2;

always @ (*) begin
    if ((ap_start == 1'b1)) begin
        out1_V_ap_vld = 1'b1;
    end else begin
        out1_V_ap_vld = 1'b0;
    end
end

always @ (*) begin
    if ((ap_start == 1'b1)) begin
        out2_V_ap_vld = 1'b1;
    end else begin
        out2_V_ap_vld = 1'b0;
    end
end

assign OP1_V_cast_fu_54_p1 = $signed(in1_V);

assign ap_done = ap_start;

assign ap_idle = 1'b1;

assign ap_ready = ap_start;

assign ap_return = 32'd0;

assign out1_V = {{p_Val2_s_fu_70_p2[7:2]}};

assign out2_V = {{p_Val2_s_fu_70_p2[7:2]}};

assign p_Val2_s_fu_70_p2 = ($signed(p_shl_fu_62_p3) - $signed(OP1_V_cast_fu_54_p1));

assign p_shl_fu_62_p3 = {{tmp_fu_58_p1}, {3'd0}};

assign tmp_fu_58_p1 = in1_V[4:0];

endmodule //multi_test

  1. 2018年01月28日 08:26 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0