FC2カウンター FPGAの部屋 HLSストリームの全結合層1(C ソースコード)
FC2ブログ

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

FPGAの部屋

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

HLSストリームの全結合層1(C ソースコード)

HLSストリームのマックス・プーリング層に続いて、HLSストリームの全結合層をやっていこう。
今回は、C ソースコードを貼っておく。

まずは、affine_layer1.h から貼っておく。

// affine_layer1.h
// 2018/04/27 by marsee (HLS stream)
//

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

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

static const size_t V_PRE_LAYER_HIGHT = 3;
static const size_t H_PRE_LAYER_WIDTH = 26;
static const size_t NUMBER_OF_MIDDLE_LAYER = 100;

static const size_t NUMBER_OF_KERNEL = 2;
static const size_t ARRAY_SIZE = 5;
static const size_t CW = 16;
static const size_t CI = 6;
static const size_t NW = 19;
static const size_t NI = 7;

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

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

typedef ap_fixed<NW,NI,AP_TRN,AP_WRAP> affine_type;
typedef ap_fixed<CW,CI,AP_TRN,AP_WRAP> conv_type;


#endif


次に、affine_layer.cpp を貼っておく。

// affine_layer1.cpp
// 2018/04/27 by marsee (HLS stream)
//

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

#include "layer_general.h"
#include "affine_layer1.h"

int affine_layer1(hls::stream<ap_fixed_axis<CW,CI,NUMBER_OF_KERNEL,1> >& ins,
        hls::stream<ap_fixed_axis<NW,NI,1,1> >& outs){
//#pragma HLS ARRAY_PARTITION variable=af1_weight complete dim=1
#pragma HLS DATA_PACK variable=outs
#pragma HLS DATA_PACK variable=ins

    ap_fixed_axis<CW,CI,NUMBER_OF_KERNEL,1> stdata;
    affine_type dot[NUMBER_OF_MIDDLE_LAYER];
//#pragma HLS ARRAY_PARTITION variable=dot complete dim=1
    ap_fixed_axis<NW,NI,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++){
//#pragma HLS PIPELINE II=1
            if (!(x==0 && y==0))    // 最初の入力はすでに入力されている
                ins >> stdata;    // AXI4-Stream からの入力

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

                affine_type dot_temp = (affine_type)0;
                for (int i=0; i<NUMBER_OF_KERNEL; i++){
                    dot_temp += stdata.data[i] * af1_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] += af1_bias[col];

                    outd.data[0] = 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);
}


テストベンチの affine_layer1_tb.cpp を貼っておく。

// affine_layer1_tb.cpp
// 2018/04/27 by marsee (HLS stream)
//

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <ap_int.h>
#include <hls_stream.h>
#include <iostream>
#include <fstream>
#include <iomanip>
#include <math.h>
#include <ap_axi_sdata.h>
#include <hls_video.h>

#include "layer_general.h"
#include "affine_layer1.h"
#include "max_pooling_output.h"

int affine_layer1(hls::stream<ap_fixed_axis<CW,CI,NUMBER_OF_KERNEL,1> >& ins,
        hls::stream<ap_fixed_axis<NW,NI,1,1> >& outs);

int affine_layer1_2(hls::stream<ap_fixed_axis<CW,CI,NUMBER_OF_KERNEL,1> >& ins,
        hls::stream<ap_fixed_axis<NW,NI,1,1> >& outs);

int affine_layer1_soft(hls::stream<float_axis<NUMBER_OF_KERNEL,1> >& ins,
        hls::stream<float_axis<1,1> >& outs);

int main(){
    using namespace std;

    hls::stream<ap_fixed_axis<CW,CI,NUMBER_OF_KERNEL,1> > ins;
    hls::stream<ap_fixed_axis<CW,CI,NUMBER_OF_KERNEL,1> > ins2;
    hls::stream<ap_fixed_axis<NW,NI,1,1> > outs;
    hls::stream<ap_fixed_axis<NW,NI,1,1> > outs2;
    hls::stream<float_axis<NUMBER_OF_KERNEL,1> > ins_soft;
    hls::stream<float_axis<1,1> > outs_soft;

    mdata_type dot;
    mdata_type dot2;
    fmdata_type fdot;

    ap_fixed_axis<CW,CI,NUMBER_OF_KERNEL,1> pix;
    ap_fixed_axis<CW,CI,NUMBER_OF_KERNEL,1> pix2;
    float_axis<NUMBER_OF_KERNEL,1> fpix;
    ap_fixed_axis<NW,NI,1,1> pdata;
    ap_fixed_axis<NW,NI,1,1> pdata2;
    float_axis<1,1> fpdata;

    // ins に入力データを用意する
    for(int i=0; i<5; i++){    // dummy data
        pix.user = 0;
        for(int k=0; k<NUMBER_OF_KERNEL; k++){
            pix.data[k] = (affine_type)i;
        }
        ins << pix;
        ins2 << pix;

        fpix.user = 0;
        for(int k=0; k<NUMBER_OF_KERNEL; k++){
            fpix.data[k] = (float)i;
        }
        ins_soft << fpix;
    }

    // 1 画面分のデータを ins、ins_soft に入力する
    for(int j=0; j < V_PRE_LAYER_HIGHT; j++){
        for(int i=0; i < H_PRE_LAYER_WIDTH; i++){
            for(int k=0; k<NUMBER_OF_KERNEL; k++){
                pix.data[k] = mp_out[j*H_PRE_LAYER_WIDTH+i][k];
                fpix.data[k] = mp_fout[j*H_PRE_LAYER_WIDTH+i][k];
            }

            if (j==0 && i==0){    // 最初のデータの時に TUSER を 1 にする
                pix.user = 1;
                fpix.user = 1;
            } else {
                pix.user = 0;
                fpix.user = 0;
            }

            if (i == H_PRE_LAYER_WIDTH-1){ // 行の最後でTLASTをアサートする
                pix.last = 1;
                fpix.last = 1;
            } else {
                pix.last = 0;
                fpix.last = 0;
            }

            ins << pix;
            ins2 << pix;
            ins_soft << fpix;
        }
    }

    affine_layer1(ins, outs);
    affine_layer1_2(ins2, outs2);
    affine_layer1_soft(ins_soft, outs_soft);

    // outs, outs2 を dot[] と dot2[] に代入して比較する
    int errcnt = 0;
    for(int i=0; i<NUMBER_OF_MIDDLE_LAYER; i++){
        outs >> pdata;
        outs2 >> pdata2;
        outs_soft >> fpdata;

        dot.data[i] = pdata.data[0];
        dot2.data[i] = pdata2.data[0];
        fdot.data[i] = fpdata.data[0];

        printf("i = %d, HW = %f, HW2 = %f, SW = %f\n", i, (float)dot.data[i], (float)dot2.data[i], fdot.data[i]);
        if(dot.data[i] != dot2.data[i]){ // 2つの実装の値が合わない
            printf("ERROR HW and SW results mismatch i = %d, HW = %f, HW2 = %f, SW = %f\n", i, (float)dot.data[i], (float)dot2.data[i], fdot.data[i]);
            errcnt++;
            //return(1);
        }
    }
    cout << "Error Count = " << errcnt << endl;
    cout << endl;

    // max_pooling の結果をヘッダファイルに出力
    ofstream OH("affine_layer1_output.h");
    OH << "// affine_layer1_output.h" << endl;
    time_t now = time(0);
    struct tm* localNow = localtime(&now);
    OH << "// " << localNow->tm_year+1900 << "/" << localNow->tm_mon+1 << "/" << localNow->tm_mday;
    OH << " " << setw(2) << setfill('0') << localNow->tm_hour << ":" << localNow->tm_min << ":" << localNow->tm_sec << " by marsee" << endl;
    OH << "//" << endl;
    OH << endl;
    OH << "#ifndef __AFFINE_LAYER1_OUTPUT_H__" << endl;
    OH << "#define __AFFINE_LAYER1_OUTPUT_H__" << endl;
    OH << endl;
    OH << "const float affine1_fout[" << NUMBER_OF_MIDDLE_LAYER  << "] = {" << endl;
    for (int i=0; i<NUMBER_OF_MIDDLE_LAYER ; i++){
        OH << "    " << fixed << setprecision(14) << fdot.data[i];
        if (i == NUMBER_OF_MIDDLE_LAYER-1)
            OH << endl;
        else
            OH << "," << endl;
    }
    OH << "};" << endl << endl;

    OH << "const ap_fixed<19,7,AP_TRN,AP_WRAP> affine1_out[" << NUMBER_OF_MIDDLE_LAYER << "] = {" << endl;
    for (int i=0; i<NUMBER_OF_MIDDLE_LAYER ; i++){
        OH << "    " << fixed << setprecision(14) << (float)dot.data[i];
        if (i == NUMBER_OF_MIDDLE_LAYER-1)
            OH << endl;
        else
            OH << "," << endl;
    }
    OH << "};" << endl << endl;
    OH << "#endif" << endl;

    return(0);
}

int affine_layer1_soft(hls::stream<float_axis<NUMBER_OF_KERNEL,1> >& ins,
        hls::stream<float_axis<1,1> >& outs){

    float_axis<NUMBER_OF_KERNEL,1> stdata;
    float dot[100];
    float_axis<1,1> outd;

    Loop1: do {
    // 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++){
            if (!(x==0 && y==0))    // 最初の入力はすでに入力されている
                ins >> stdata;    // AXI4-Stream からの入力

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

                for (int i=0; i<NUMBER_OF_KERNEL; i++){
                    dot[col] += stdata.data[i] * af1_fweight[V_PRE_LAYER_HIGHT*H_PRE_LAYER_WIDTH*i+y*H_PRE_LAYER_WIDTH+x][col];
                }

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

                    outd.data[0] = 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);
}

// 検証用 affine_layer1_2()
// 検証用に affine_layer1() とは異なる実装でコーディング
int affine_layer1_2(hls::stream<ap_fixed_axis<CW,CI,NUMBER_OF_KERNEL,1> >& ins,
        hls::stream<ap_fixed_axis<NW,NI,1,1> >& outs){

    ap_fixed_axis<CW,CI,NUMBER_OF_KERNEL,1> stdata;
    conv_type aff_in[NUMBER_OF_KERNEL][V_PRE_LAYER_HIGHT][H_PRE_LAYER_WIDTH];
    affine_type dot1[NUMBER_OF_MIDDLE_LAYER];
    ap_fixed_axis<NW,NI,1,1> outd;

    do {
    // user が 1になった時にフレームがスタートする
        ins >> stdata;
    } while(stdata.user == 0);

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

            for (int i=0; i<NUMBER_OF_KERNEL; i++){
                aff_in[i][y][x] = stdata.data[i];
            }
        }
    }

    for(int col=0; col<NUMBER_OF_MIDDLE_LAYER; col++){
        dot1[col] = 0;
        for(int i=0; i<NUMBER_OF_KERNEL; i++){
            for(int j=0; j<V_PRE_LAYER_HIGHT; j++){
                for(int k=0; k<H_PRE_LAYER_WIDTH; k++){
                    dot1[col] += aff_in[i][j][k]*af1_weight[i*V_PRE_LAYER_HIGHT*H_PRE_LAYER_WIDTH+j*H_PRE_LAYER_WIDTH+k][col];
                }
            }
        }
        dot1[col] += af1_bias[col];

        outd.data[0] = dot1[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);
}


なお、layer_general.h はここにある
  1. 2018年04月28日 05:37 |
  2. DNN
  3. | トラックバック:0
  4. | コメント:0

コメント

コメントの投稿


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

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