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

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

FPGAの部屋

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

アベンジャーズ/インフィニティ・ウォー(映画)を見てきました

今日は、「アベンジャーズ/インフィニティ・ウォー|映画|マーベル公式」を見てきました。私としてはアベンジャーズ・シリーズはわざと見に行かないで避けていたのですが、奥さんが見たいというので仕方なく行ってしまいました。最初はアメコミ・ヒーローで詰まらなかったのですが、だんだんと引き込まれてしまいました。
奥さんは好きな映画じゃなかったそうです。初めからわかってたんじゃないか?と思いますが。。。
  1. 2018年05月03日 22:21 |
  2. 日記
  3. | トラックバック:0
  4. | コメント:0

テンプレートで書いた全結合層1(テンプレートのソースコード)

今まで書いてきたHLSストリームでもストリームのデータの配列は書けたのだが、1つのコードですべての全結合層をインスタンスすることはできなかった。そこで、ツィッターで聞いたのだが、@ciniml さんがテンプレートを使ってはどうか?と提案してくれた。それに、Xilinxの BNN-PYNQ がそのように作られているということを教えてもらった。
早速、BNN-PYNQを調べると、教えてもらった通りにテンプレートを使ってありました。ここで、Vivado HLSにおけるテンプレートの使い方を勉強して自分で全結合層のテンプレート実装を書いてみた。
PIPELINE指示子のII の値は、@hiyuh さんに教えてもらったサンプルコードの書き方でテンプレートから入力することができたが、PIPELINE指示子自体は無くしたり、入れたりはテンプレートからはできなかった。その代わりに #ifdef を使用した。
これを何とかする方法は無いものだろうか?つまり指示子を書いたユニークなディレクティブ・ファイルを各インスタンスごとに連結できないだろうか?それは、各層ごとにチューニングが違うという状況があるからなのだが、同じ層でもFPGAの余裕によってもう少しチューニングしたいとか要求があると思うので、その場合にチューニングを変えるために指示子を書き換えたいという欲求があるためだ。
テンプレートを使用した affine_layer_template.h を示す。

// affine_layer_template.h
// 2018/05/02 by marsee
// テンプレートを使用して汎用化した affine_layer
// #define LOOP3_PIPELINE_ENABLE を書くとLoop3にPIPELINE指示子が入る
//

#ifndef __AFFINE_LAYER_TEMPLATE_H__
#define __AFFINE_LAYER_TEMPLATE_H__

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

#include "layer_general.h"

#define TO_LITERAL(x) #x
#define PRAGMA_HLS(tok) _Pragma(TO_LITERAL(HLS tok)) // @hiyuhさんから

template<
    const size_t IN_W,      // 入力のビット幅
    const size_t IN_I,        // 入力の小数点位置
    const size_t OUT_W,  // 出力のビット長
    const size_t OUT_I,    // 出力の小数点位置
    const size_t WB_W,    // 重みとバイアスのビット長
    const size_t WB_I,      // 重みとバイアスの小数点位置
    const size_t V_PRE_LAYER_HIGHT,
    const size_t H_PRE_LAYER_WIDTH,
    const size_t NUMBER_OF_CHANNELS,
    const size_t NUMBER_OF_OUTPUT,
    const size_t H_PIPELINE_II
>int affine_layer_template(hls::stream<ap_fixed_axis<IN_W,IN_I,NUMBER_OF_CHANNELS,1> >& ins,
        hls::stream<ap_fixed_axis<OUT_W,OUT_I,1,1> >& outs,
        const ap_fixed<WB_W,WB_I,AP_TRN,AP_WRAP> af_weight[V_PRE_LAYER_HIGHT*H_PRE_LAYER_WIDTH*NUMBER_OF_CHANNELS][NUMBER_OF_OUTPUT],
        const ap_fixed<WB_W,WB_I,AP_TRN,AP_WRAP> af_bias[NUMBER_OF_OUTPUT]
){
    //#pragma HLS ARRAY_PARTITION variable=af_weight complete dim=1

    ap_fixed_axis<IN_W,IN_I,NUMBER_OF_CHANNELS,1> stdata;
    ap_fixed<OUT_W,OUT_I,AP_TRN,AP_WRAP> dot[NUMBER_OF_OUTPUT];
//#pragma HLS ARRAY_PARTITION variable=dot complete dim=1
    ap_fixed_axis<OUT_W,OUT_I,1,1> outd;

    Loop1: do {
#pragma HLS PIPELINE II=1
#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++){
#ifdef LOOP3_PIPELINE_ENABLE
    PRAGMA_HLS(pipeline II=H_PIPELINE_II)
#endif
            if (!(x==0 && y==0))    // 最初の入力はすでに入力されている
                ins >> stdata;    // AXI4-Stream からの入力

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

                ap_fixed<OUT_W,OUT_I,AP_TRN,AP_WRAP> dot_temp = ap_fixed<OUT_W,OUT_I,AP_TRN,AP_WRAP>(0);
                for (int i=0; i<NUMBER_OF_CHANNELS; i++){
                    dot_temp += stdata.data[i] * af_weight[V_PRE_LAYER_HIGHT*H_PRE_LAYER_WIDTH*i+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] += af_bias[col];

                    outd.data[0] = dot[col];

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

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

                    outs << outd;
                }
            }
        }
    }

    return(0);
}

#endif


affine_layer_template.h をインスタンスする全結合層 1 層目の affine_layer1.cpp を示す。
affine_layer1.cpp は Loop3 のPIPELINE指示子は必要ないというか、それを入れてしまうとリソース使用量が増えすぎるので、LOOP3_PIPELINE_ENABLE を定義していない。(”HLSストリームの全結合層1(C ソースコード)”、”HLSストリームの全結合層2(Export RTLまで)”参照)

// affine_layer1.cpp
// 2018/05/02 by marsee
// affine layer1 by template
//

#include "affine_layer_template.h"
#include "af1_weight.h"
#include "af1_bias.h"

int affine_layer1(hls::stream<ap_fixed_axis<16,6,2,1> >& ins,
        hls::stream<ap_fixed_axis<19,7,1,1> >& outs){
#pragma HLS DATA_PACK variable=outs
#pragma HLS DATA_PACK variable=ins
    return(affine_layer_template<16,6,19,7,11,1,3,26,2,100,0>(ins, outs, af1_weight, af1_bias));
}


次に、affine_layer2.cpp を示す。これは、全結合層の 2 層目で、Loop3 の PIPELINE 指示子の II は 3 を指定している。つまり、LOOP3_PIPELINE_ENABLE を定義して、テンプレートの最後に 3 を入れている。(”AXI4-Stream インターフェースの全結合層2層目1(指示子による性能差)”参照)

// affine_layer2.cpp
// 2018/05/03 by marsee
// affine layer2 by template
//

#define LOOP3_PIPELINE_ENABLE

#include "affine_layer_template.h"
#include "af2_weight.h"
#include "af2_bias.h"

int affine_layer2(hls::stream<ap_fixed_axis<19,7,1,1> >& ins,
        hls::stream<ap_fixed_axis<12,7,1,1> >& outs){
#pragma HLS DATA_PACK variable=outs
#pragma HLS DATA_PACK variable=ins
    return(affine_layer_template<19,7,12,7,11,1,1,100,1,3,3>(ins, outs, af2_weight, af2_bias));
}

  1. 2018年05月03日 22:07 |
  2. DNN
  3. | トラックバック:0
  4. | コメント:0