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

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

FPGAの部屋

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

AXI4-Stream インターフェースの全結合層5(C シミュレーション、C/RTL 協調シミュレーション、Export RTL )

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

前回で、ハードウェア化する C ソースコードは確定した。今回は、テストベンチを作成して、C シミュレーション、C/RTL 協調シミュレーション、Export RTL を実行した。

まずはテストベンチ・ファイルの affine_layer1_tb.cpp を作成した。
そして、C シミュレーションを行った。結果を示す。
affine_layer_37_180305.png

C シミュレーションでは、次の ReLU 層への出力として、affine_layer1_output.h を出力している。
affine_layer1_output.h の一部を示す。
affine_layer_39_180307.png 

affine_layer_40_180307.png 

次に、C コードの合成は既に実行されているので、C/RTL 協調シミュレーションを行った。
C/RTL 協調シミュレーションがまだ終了しないので、後で追加します。
結局、職場から帰ってきても終わってませんでした。キャンセルしました。


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

LUT は 311 個、FF が 395 個で、DSP も 2 個、BRAM は 12 個使用されていた。

affine_layer1_tb.cpp を貼っておく。

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

#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 "affine_layer1.h"
#include "max_pooling_output.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);

int affine_layer1_soft(hls::stream<float2_axis<1,1,1> >& ins,
        hls::stream<float1_axis<1,1,1> >& outs);

int main(){
    using namespace std;

    hls::stream<ap_fixed2_axis<16,6,1,1,1> > ins;
    hls::stream<ap_fixed1_axis<19,7,1,1,1> > outs;
    hls::stream<float2_axis<1,1,1> > ins_soft;
    hls::stream<float1_axis<1,1,1> > outs_soft;

    mdata_type dot;
    fmdata_type fdot;

    ap_fixed2_axis<16,6,1,1,1> pix;
    float2_axis<1,1,1> fpix;
    ap_fixed1_axis<19,7,1,1,1> pdata;
    float1_axis<1,1,1> fpdata;

    // ins に入力データを用意する
    for(int i=0; i<5; i++){    // dummy data
        pix.user = 0;
        pix.data.data0 = (affine_type)i;
        pix.data.data1 = (affine_type)i;
        ins << pix;
        fpix.user = 0;
        fpix.data.data0 = (float)i;
        fpix.data.data1 = (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++){
            pix.data.data0 = mp_out[j*H_PRE_LAYER_WIDTH+i][0];
            pix.data.data1 = mp_out[j*H_PRE_LAYER_WIDTH+i][1];
            fpix.data.data0 = mp_fout[j*H_PRE_LAYER_WIDTH+i][0];
            fpix.data.data1 = mp_fout[j*H_PRE_LAYER_WIDTH+i][1];

            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;
            ins_soft << fpix;
        }
    }

    affine_layer1(ins, outs);
    affine_layer1_soft(ins_soft, outs_soft);

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

        dot.data[i] = pdata.data.data0;
        fdot.data[i] = fpdata.data.data0;

        printf("i = %d, HW = %f, SW = %f\n", i, (float)dot.data[i], fdot.data[i]);
        if((double)pow((double)dot.data[i]-(double)fdot.data[i], (double)2) > 4){ // 2乗誤差が4よりも大きい
            printf("ERROR HW and SW results mismatch i = %d, HW = %f, SW = %f\n", i, (float)dot.data[i], fdot.data[i]);
            //return(1);
        }
    }

    // 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<float2_axis<1,1,1> >& ins,
        hls::stream<float1_axis<1,1,1> >& outs){

    float2_axis<1,1,1> stdata;
    float dot[100];
    float1_axis<1,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;

                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];

                if (y==V_PRE_LAYER_HIGHT-1 && x==H_PRE_LAYER_WIDTH-1){ // 最後はバイアスを加算する
                    dot[col] += af1_fbias[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月05日 04:55 |
  2. DNN
  3. | トラックバック:0
  4. | コメント:0