FC2カウンター FPGAの部屋 AXI4-Stream インターフェースのMax Pooling 1(ソースコード)
FC2ブログ

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

FPGAの部屋

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

AXI4-Stream インターフェースのMax Pooling 1(ソースコード)

AXI4-Stream インターフェースのReLU に続いて、AXI4-Stream インターフェースの Max Pooling を作っていこう。

52 x 6 を入力して 2 x 2 のウインドウでMax Pooling を行う。ストライドは 2 だ。よって、出力は半分の 26 x 3 となる。

C++ のソースコードを貼っていく。

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

// max_pooling.h
// 2018/02/19 by marsee
//

#ifndef __MAX_POOLING_H__
#define __MAX_POOLING_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 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;
    };

#define H_PIXEL_WIDTH_IN    52
#define V_PIXEL_WIDTH_IN    6
#define H_PIXEL_WIDTH_OUT    26
#define V_PIXEL_WIDTH_OUT    3

#define ARRAY_SIZE                2

#define NUMBER_OF_KERNEL        2

#define X_STRIDE                2
#define Y_STRIDE                2

typedef ap_fixed<166, AP_TRN, AP_WRAP> conv_type;

#endif


max_pooling.cpp を貼っておく。
(2018/04/21 : 修正、バグフィックス)
(2018/04/25 : 修正 Loop10 バグフィックス

// max_pooling.cpp
// 2018/02/20 by marsee
// 2018/04/20 : bug fix
// 2018/04/25 : Loop10 bug fix
//

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

#include "max_pooling.h"

int max_pooling(hls::stream<ap_fixed2_axis<16,6,1,1,1> >& ins,
        hls::stream<ap_fixed2_axis<16,6,1,1,1> >& outs){
#pragma HLS INTERFACE axis port=ins
#pragma HLS INTERFACE axis port=outs
#pragma HLS INTERFACE s_axilite port=return

    ap_fixed2_axis<16,6,1,1,1> pix;
    ap_fixed2_axis<16,6,1,1,1> mp_out;

    conv_type line_buf[NUMBER_OF_KERNEL][ARRAY_SIZE-1][H_PIXEL_WIDTH_IN];
#pragma HLS ARRAY_PARTITION variable=line_buf block factor=2 dim=1
#pragma HLS ARRAY_PARTITION variable=line_buf block factor=1 dim=2

    conv_type pix_mat[NUMBER_OF_KERNEL][ARRAY_SIZE][ARRAY_SIZE];
#pragma HLS array_partition variable=pix_mat complete

    conv_type val[NUMBER_OF_KERNEL], conv_data;

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

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

            Loop4: for (int n=0; n<NUMBER_OF_KERNEL; n++){
#pragma HLS UNROLL
                if (n == 0)
                    conv_data = pix.data.data0;
                else
                    conv_data = pix.data.data1;

                // 2次元配列のデータを左シフト
                Loop5 : for (int k=0; k<ARRAY_SIZE; k++){
#pragma HLS UNROLL
                    Loop6 : for (int m=0; m<ARRAY_SIZE-1; m++){
                        pix_mat[n][k][m] = pix_mat[n][k][m+1];
                    }
                }

                Loop7: for (int i=0; i<ARRAY_SIZE-1; i++){ // 以前の行のデータを line_buf から入力
                    pix_mat[n][i][ARRAY_SIZE-1] = line_buf[n][i][x];
                }
                pix_mat[n][ARRAY_SIZE-1][ARRAY_SIZE-1] = conv_data; // pix_mat の最後に新しいデータを入力

                Loop8: for (int i=0; i<ARRAY_SIZE-2; i++){ // 行の入れ替え
                    line_buf[n][i][x] = line_buf[n][i+1][x];
                }
                line_buf[n][ARRAY_SIZE-2][x] = conv_data;

                // max pooling の検索
                Loop9 : for (int k=0; k<ARRAY_SIZE; k++){
#pragma HLS UNROLL
                    Loop10 : for (int m=0; m<ARRAY_SIZE; m++){
                        if (k==0 && m==0){
                            val[n] = pix_mat[n][k][m];
                        } else if (val[n] < pix_mat[n][k][m]){
                            val[n] = pix_mat[n][k][m];
                        }
                    }
                }
                if (n == 0)
                    mp_out.data.data0 = val[0];
                else
                    mp_out.data.data1 = val[1];

                if (x==X_STRIDE-1 && y==Y_STRIDE-1){ // 最初のデータでは、TUSERをアサートする
                    mp_out.user = 1;
                } else {
                    mp_out.user = 0;
                }

                if (x == H_PIXEL_WIDTH_IN-1){    // 行の最後で TLAST をアサートする
                    mp_out.last = 1;
                } else {
                    mp_out.last = 0;
                }
            }
            if (x%X_STRIDE==X_STRIDE-1 && y%Y_STRIDE==Y_STRIDE-1){ // ストライド
                outs << mp_out;
            }
        }
    }
    return(0);
}


max_pooling_tb.cpp を貼っておく。

// max_pooling_tb.cpp
// 2018/02/23 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 "max_pooling.h"
#include "relu_output.h"

int max_pooling(hls::stream<ap_fixed2_axis<16,6,1,1,1> >& ins,
        hls::stream<ap_fixed2_axis<16,6,1,1,1> >& outs);

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

int main(){
    using namespace std;

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

    float mp_fout[H_PIXEL_WIDTH_OUT*V_PIXEL_WIDTH_OUT][2];
    conv_type mp_out[H_PIXEL_WIDTH_OUT*V_PIXEL_WIDTH_OUT][2];

    ap_fixed2_axis<16,6,1,1,1> pix;
    float2_axis<1,1,1> fpix;

    // ins に入力データを用意する
    for(int i=0; i<5; i++){    // dummy data
        pix.user = 0;
        pix.data.data0 = (conv_type)i;
        pix.data.data1 = (conv_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_PIXEL_WIDTH_IN; j++){
        for(int i=0; i < H_PIXEL_WIDTH_IN; i++){
            pix.data.data0 = relu_out[j*H_PIXEL_WIDTH_IN+i][0];
            pix.data.data1 = relu_out[j*H_PIXEL_WIDTH_IN+i][1];
            fpix.data.data0 = relu_fout[j*H_PIXEL_WIDTH_IN+i][0];
            fpix.data.data1 = relu_fout[j*H_PIXEL_WIDTH_IN+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_PIXEL_WIDTH_IN-1){ // 行の最後でTLASTをアサートする
                pix.last = 1;
                fpix.last = 1;
            } else {
                pix.last = 0;
                fpix.last = 0;
            }

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

    max_pooling(ins, outs);
    max_pooling_soft(ins_soft, outs_soft);

    // outs, outs_soft を mp_out[][], relu_fout[][] に出力する
    for(int j=0; j < V_PIXEL_WIDTH_OUT; j++){
        for(int i=0; i < H_PIXEL_WIDTH_OUT; i++){
            outs >> pix;
            outs_soft >> fpix;

            mp_out[j*H_PIXEL_WIDTH_OUT+i][0] = pix.data.data0;
            mp_out[j*H_PIXEL_WIDTH_OUT+i][1] = pix.data.data1;
            mp_fout[j*H_PIXEL_WIDTH_OUT+i][0] = fpix.data.data0;
            mp_fout[j*H_PIXEL_WIDTH_OUT+i][1] = fpix.data.data1;
            printf("%d, %d, data0 = %f, data1 = %f, fdata0 = %f, fdata1 = %f\n", j, i, (float)pix.data.data0, (float)pix.data.data1, fpix.data.data0, fpix.data.data1);
            if ((double)pow((double)pix.data.data0-(double)fpix.data.data0,(double)2) > 4 ||
                    (double)pow((double)pix.data.data1-(double)fpix.data.data1,(double)2) > 4){ // 2乗誤差が4よりも大きい
                printf("ERROR HW and SW results mismatch i = %ld, j = %ld, HW = %f, %f, SW = %f, %f\n", i, j, (float)pix.data.data0, (float)pix.data.data1, fpix.data.data0, fpix.data.data1);
                //return(1);
            }
        }
    }
    cout << "Success HW and SW results match" << endl;
    cout << endl;

    // max_pooling の結果をヘッダファイルに出力
    ofstream OH("max_pooling_output.h");
    OH << "// max_pooling_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 __MAX_POOLING_OUTPUT_H__" << endl;
    OH << "#define __MAX_POOLING_OUTPUT_H__" << endl;
    OH << endl;
    OH << "const float mp_fout[" << V_PIXEL_WIDTH_OUT*H_PIXEL_WIDTH_OUT  << "][" << NUMBER_OF_KERNEL << "] = {" << endl;
    for (int y=0; y<V_PIXEL_WIDTH_OUT ; y++){
        for (int x=0; x<H_PIXEL_WIDTH_OUT ; x++){
            OH << "    {" << fixed << setprecision(12) << mp_fout[H_PIXEL_WIDTH_OUT*y+x][0] << ", "
                    << mp_fout[H_PIXEL_WIDTH_OUT*y+x][1] << "}";
            if (y==V_PIXEL_WIDTH_OUT-1 && x==H_PIXEL_WIDTH_OUT-1)
                OH << endl;
            else
                OH << "," << endl;
        }
    }
    OH << "};" << endl << endl;

    OH << "const ap_fixed<16, 6, AP_TRN, AP_WRAP> mp_out[" << V_PIXEL_WIDTH_OUT*H_PIXEL_WIDTH_OUT  << "][" << NUMBER_OF_KERNEL << "] = {" << endl;
    for (int y=0; y<V_PIXEL_WIDTH_OUT ; y++){
        for (int x=0; x<H_PIXEL_WIDTH_OUT ; x++){
            OH << "    {" << fixed << setprecision(12) << (float)mp_out[H_PIXEL_WIDTH_OUT*y+x][0] << ", "
                    <<  (float)mp_out[H_PIXEL_WIDTH_OUT*y+x][1] << "}";
            if (y==V_PIXEL_WIDTH_OUT -1 && x==H_PIXEL_WIDTH_OUT -1)
                OH << endl;
            else
                OH << "," << endl;
        }
    }
    OH << "};" << endl << endl;
    OH << "#endif" << endl;

    return(0);
}    


int max_pooling_soft(hls::stream<float2_axis<1,1,1> >& ins,
        hls::stream<float2_axis<1,1,1> >& outs){

    float2_axis<1,1,1> fpix;
    float fpixd_ary[NUMBER_OF_KERNEL][V_PIXEL_WIDTH_IN][H_PIXEL_WIDTH_IN];
    float fval[NUMBER_OF_KERNEL];

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

    for (int y=0; y<V_PIXEL_WIDTH_IN; y++){
        for (int x=0; x<H_PIXEL_WIDTH_IN; x++){
            if (!(x==0 && y==0))    // 最初の入力はすでに入力されている
                ins >> fpix;

            fpixd_ary[0][y][x] = fpix.data.data0;
            fpixd_ary[1][y][x] = fpix.data.data1;
        }
    }

    for (int y=0; y<V_PIXEL_WIDTH_IN-1; y+=2){
        for (int x=0; x<H_PIXEL_WIDTH_IN-1; x+=2){
            for(int p=0; p<2; p++){
                for(int m=0; m<2; m++){
                    for(int n=0; n<2; n++){
                        if(m==0 && n==0){
                            fval[p] = fpixd_ary[p][y][x];
                        } else if(fval[p] < fpixd_ary[p][y+m][x+n]){
                            fval[p] = fpixd_ary[p][y+m][x+n];
                        }
                    }
                }
            }
            fpix.data.data0 = fval[0];
            fpix.data.data1 = fval[1];

            if(x==0 && y==0)
                fpix.user = 1;
            else
                fpix.user = 0;

            if(x==V_PIXEL_WIDTH_OUT-2)
                fpix.last = 1;
            else
                fpix.last = 0;

            outs << fpix;
        }
    }

    return(0);
}

  1. 2018年02月24日 21:08 |
  2. DNN
  3. | トラックバック:0
  4. | コメント:0

コメント

コメントの投稿


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

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