FC2カウンター FPGAの部屋 2018年03月04日

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

FPGAの部屋

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

AXI4-Stream インターフェースの全結合層4(C コードや指示子による性能差4)

前回は、af1_weight[][] と dot[] をBRAM にマップしたほうが良いかな?ということになったが、II = 2 クロックとなって、性能的に少し不満があったので、この辺りをチューニングしてみよう。

まずは、dot[] のポート数が問題なので、dot_temp という変数を使用した。

dot[col] += stdata.data.data0 * af1_fweight[y*H_PRE_LAYER_WIDTH+x][col];
dot[col] += stdata.data.data1 * af1_fweight[V_PRE_LAYER_HIGHT*H_PRE_LAYER_WIDTH+y*H_PRE_LAYER_WIDTH+x][col];

を dot_temp を使用して、こう書き直した。

affine_type dot_temp = stdata.data.data0 * af1_weight[y*H_PRE_LAYER_WIDTH+x][col] +
stdata.data.data1 * af1_weight[V_PRE_LAYER_HIGHT*H_PRE_LAYER_WIDTH+y*H_PRE_LAYER_WIDTH+x][col];
dot[col] += dot_temp;

affine_layer_29_180304.png

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

Loop4 のInitiation achieved は 2 クロックで変更がなかったが、Estmated は 9.92 ns から 8.67 ns に改善した。
リソース使用量はBRAM_18K 、DSP48E は変更がないが、FF と LUT は多少増えている。

次に、やはり出力をストリームにしてみよう。これで少なくとも100 クロックは速く最初のデータを出力することができる。
affine_layer_31_180304.png


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

なんと、出力をAXI4-Stream にすると、Loop4 の Initiation achieved が 1 になった。
Latency は 8,504 クロックになった。100 MHz クロックを使用した場合は、85.04 us で変換が終了する。半分になった。
ただし、Estmated は10.34 ns でタイミング制約違反になっている。
クロック間の遅延の見積もりは、ReLU のところのキャストが一番時間がかかっているようだ。

INFO: [SCHED 204-61] Pipelining result : Target II = 1, Final II = 1, Depth = 6.
WARNING: [SCHED 204-21] Estimated clock period (10.3415ns) exceeds the target (target clock period: 10ns, clock uncertainty: 1.25ns, effective delay budget: 8.75ns).
WARNING: [SCHED 204-21] The critical path consists of the following:
    'mul' operation ('__Val2__', affine_layer1/affine_layer1.cpp:44) (3.36 ns)
    'add' operation ('__Val2__', affine_layer1/affine_layer1.cpp:44) (3.02 ns)
    'add' operation ('tmp_21', affine_layer1/affine_layer1.cpp:44) (0 ns)
    'add' operation ('p_Val2_8_cast', affine_layer1/affine_layer1.cpp:49) (3.96 ns)


リソース使用量はBRAM_18K は 13 個から 12 個に減少した。DSP48E は変更なしの 2 個使用している。FF は 898 個、LUT は 1,263 個で、2つとも減少しているが、特に FF は 4,279 個から約 1/4.77 倍に.減少している。

それじゃ、ストリームにしたことだし、ReLU は後付けしようということで、ReLU の記述を削除した。
affine_layer_33_180304.png

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

Estmated は 10.71 ns と悪くなってしまった。
Latency は 8,426 クロックで良くなっている。
リソース使用量は FF と LUT がさらに減少している。

動作周波数をどうにかしようということで、Soution Settings のSynthesis のUncertainty を 3 ns に設定した。
affine_layer_35_180304.png

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

Estmated は 8.55 ns に改善した。これでタイミング制約違反は無くなった。
Latency は 8,504 クロックで、100 MHz クロックだと、85.04 us になる。
リソース使用量は、FF と LUT が多少増えている。

85.04 us かかってしまうが、リソース使用量もお手頃なので、これで行くことにする。
次は全結合層用のReLU を作ろう。テンプレートで配列を書きたいものである。@hiyuh さんに教えてもらったサンプル・コードがあるので、うまく行ったら、それで書き直したいと思っている。


現在のaffine_layer1.h を貼っておく。

// affine_layer1.h
// 2018/02/25 by marsee
// 2018/03/04 : ap_fixed1_axis と float1_axis を追加
//

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

template<int W, int I, int U, int TI, int TD>
    struct ap_fixed2_axis{
        struct data {
            ap_fixed<W,I,AP_TRN,AP_WRAP> data0;
            ap_fixed<W,I,AP_TRN,AP_WRAP> data1;
        } data;
        ap_uint<(W+7)/8> keep;
        ap_uint<(W+7)/8> strb;
        ap_uint<U>       user;
        ap_uint<1>       last;
        ap_uint<TI>      id;
        ap_uint<TD>      dest;
    };

template<int W, int I, int U, int TI, int TD>
    struct ap_fixed1_axis{
        struct data {
            ap_fixed<W,I,AP_TRN,AP_WRAP> data0;
        } data;
        ap_uint<(W+7)/8> keep;
        ap_uint<(W+7)/8> strb;
        ap_uint<U>       user;
        ap_uint<1>       last;
        ap_uint<TI>      id;
        ap_uint<TD>      dest;
    };

template<int U, int TI, int TD>
    struct float2_axis{
        struct data {
            float data0;
            float data1;
        } data;
        ap_uint<1> keep;
        ap_uint<1> strb;
        ap_uint<U>       user;
        ap_uint<1>       last;
        ap_uint<TI>      id;
        ap_uint<TD>      dest;
    };

template<int U, int TI, int TD>
    struct float1_axis{
        struct data {
            float data0;
        } data;
        ap_uint<1> keep;
        ap_uint<1> strb;
        ap_uint<U>       user;
        ap_uint<1>       last;
        ap_uint<TI>      id;
        ap_uint<TD>      dest;
    };

#define NUMBER_OF_MIDDLE_LAYER    100

typedef struct {
    ap_fixed<19,7,AP_TRN,AP_WRAP> data [NUMBER_OF_MIDDLE_LAYER];
} mdata_type;

typedef struct {
    float data [NUMBER_OF_MIDDLE_LAYER];
} fmdata_type;

typedef ap_fixed<19,7,AP_TRN,AP_WRAP> affine_type;

#define V_PRE_LAYER_HIGHT    3
#define H_PRE_LAYER_WIDTH    26

#endif


affine_layer1.cpp を貼っておく。

// affine_layer1.cpp
// 2018/02/26 by marsee
//

#include <ap_int.h>
#include <hls_stream.h>
#include <ap_axi_sdata.h>
#include <hls_video.h>

#include "affine_layer1.h"
#include "af1_weight.h"
#include "af1_bias.h"

int affine_layer1(hls::stream<ap_fixed2_axis<16,6,1,1,1> >& ins,
        hls::stream<ap_fixed1_axis<19,7,1,1,1> >& outs){
//#pragma HLS ARRAY_PARTITION variable=af1_weight complete dim=1
#pragma HLS INTERFACE axis register both port=outs
#pragma HLS INTERFACE s_axilite port=return
#pragma HLS INTERFACE axis register both port=ins

    ap_fixed2_axis<16,6,1,1,1> stdata;
    affine_type dot[100];
//#pragma HLS ARRAY_PARTITION variable=dot complete dim=1
    ap_fixed1_axis<19,7,1,1,1> outd;

    Loop1: do {
#pragma HLS LOOP_TRIPCOUNT min=1 max=1 avg=1
    // user が 1になった時にフレームがスタートする
        ins >> stdata;
    } while(stdata.user == 0);

    Loop2: for (int y=0; y<V_PRE_LAYER_HIGHT; y++){
        Loop3: for (int x=0; x<H_PRE_LAYER_WIDTH; x++){
//#pragma HLS PIPELINE II=1
            if (!(x==0 && y==0))    // 最初の入力はすでに入力されている
                ins >> stdata;    // AXI4-Stream からの入力

            Loop4: for (int col=0; col<100; col++){
#pragma HLS PIPELINE II=1
                if (x==0 && y==0// 最初は 0 にクリアする
                    dot[col] = 0;

                affine_type dot_temp = stdata.data.data0 * af1_weight[y*H_PRE_LAYER_WIDTH+x][col] +
                        stdata.data.data1 * af1_weight[V_PRE_LAYER_HIGHT*H_PRE_LAYER_WIDTH+y*H_PRE_LAYER_WIDTH+x][col];
                dot[col] += dot_temp;

                if (y==V_PRE_LAYER_HIGHT-1 && x==H_PRE_LAYER_WIDTH-1){ // 最後はバイアスを加算する
                    dot[col] += af1_bias[col];

                    outd.data.data0 = dot[col];

                    if(col == 0)
                        outd.user = 1;
                    else
                        outd.user = 0;

                    if(col == NUMBER_OF_MIDDLE_LAYER-1)
                        outd.last = 1;
                    else
                        outd.last = 0;

                    outs << outd;
                }
            }
        }
    }

    return(0);
}

  1. 2018年03月04日 09:14 |
  2. DNN
  3. | トラックバック:0
  4. | コメント:0