FC2カウンター FPGAの部屋 2018年10月04日
FC2ブログ

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

FPGAの部屋

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

ZYBOt の白線間走行用CNNをVivado HLS 2018.2 でIP化1

ZYBOt の白線間走行用CNNをVivado HLS 2018.2 で試しに実装した”で、ZYBOt の白線間走行用CNNの精度が検証できたので、今度はIP化を行う。今回はC シミュレーションとC コードの合成を行う。

(2018/10/06 : 修正)画像を増やすスクリプトが間違っていたので、間違いを修正してやり直しました。

まずは、”Kerasで学習した重みとバイアスを使用した白線間走行用CNNをIPにする1”と”Kerasで学習した重みとバイアスを使用した白線間走行用CNNをIPにする2”を参照して、Vivado HLS 2018.2 の course_conv_nn2_axis3_k プロジェクトを作成した。
ZYBOt_Keras_32_181004

C シミュレーションを実行した。
ZYBOt_Keras_48_181006

レポートを示す。

INFO: [SIM 2] *************** CSIM start ***************
INFO: [SIM 4] CSIM will launch GCC as the compiler.
   Compiling ../../../course_conv_nn2_axis3_tb.cpp in debug mode
   Compiling ../../../course_conv_nn2_axis3.cpp in debug mode
   Generating csim.exe
test/straight_test/bmp_file231.bmp
test/straight_test/bmp_file185.bmp
test/straight_test/bmp_file238.bmp
test/straight_test/bmp_file184.bmp
test/straight_test/bmp_file101.bmp
test/straight_test/bmp_file198.bmp
test/straight_test/bmp_file72.bmp
test/straight_test/bmp_file115.bmp
test/straight_test/bmp_file65.bmp
test/straight_test/bmp_file100.bmp
test/straight_test/bmp_file102.bmp
test/straight_test/bmp_file117.bmp
test/straight_test/bmp_file74.bmp
test/straight_test/bmp_file113.bmp
test/straight_test/bmp_file73.bmp
test/straight_test/bmp_file76.bmp
test/straight_test/bmp_file103.bmp
test/straight_test/bmp_file68.bmp
test/straight_test/bmp_file75.bmp
test/straight_test/bmp_file240.bmp
test/straight_test/bmp_file232.bmp
test/straight_test/bmp_file235.bmp
test/straight_test/bmp_file104.bmp
test/straight_test/bmp_file187.bmp
test/straight_test/bmp_file201.bmp
test/straight_test/bmp_file233.bmp
test/straight_test/bmp_file197.bmp
test/straight_test/bmp_file199.bmp
test/straight_test/bmp_file116.bmp
test/straight_test/bmp_file239.bmp
test/straight_test/bmp_file186.bmp
test/straight_test/bmp_file188.bmp
test/straight_test/bmp_file234.bmp
test/straight_test/bmp_file67.bmp
test/straight_test/bmp_file114.bmp
Straight error is 0

test/left_test/bmp_file203.bmp
test/left_test/bmp_file94.bmp
correct data = 0, outs = 1
dot2[0] = -2.724609 dot2[1] = 3.486328 dot2[2] = -3.287109 
test/left_test/bmp_file89.bmp
test/left_test/bmp_file214.bmp
test/left_test/bmp_file84.bmp
test/left_test/bmp_file112.bmp
test/left_test/bmp_file105.bmp
test/left_test/bmp_file96.bmp
correct data = 0, outs = 2
dot2[0] = 1.197266 dot2[1] = -7.605469 dot2[2] = 9.322266 
test/left_test/bmp_file109.bmp
test/left_test/bmp_file97.bmp
test/left_test/bmp_file125.bmp
test/left_test/bmp_file123.bmp
test/left_test/bmp_file78.bmp
test/left_test/bmp_file82.bmp
test/left_test/bmp_file107.bmp
test/left_test/bmp_file85.bmp
test/left_test/bmp_file122.bmp
test/left_test/bmp_file124.bmp
test/left_test/bmp_file237.bmp
test/left_test/bmp_file108.bmp
test/left_test/bmp_file70.bmp
test/left_test/bmp_file95.bmp
test/left_test/bmp_file215.bmp
test/left_test/bmp_file88.bmp
test/left_test/bmp_file87.bmp
test/left_test/bmp_file79.bmp
test/left_test/bmp_file120.bmp
test/left_test/bmp_file111.bmp
test/left_test/bmp_file121.bmp
test/left_test/bmp_file80.bmp
test/left_test/bmp_file99.bmp
test/left_test/bmp_file86.bmp
test/left_test/bmp_file213.bmp
test/left_test/bmp_file118.bmp
test/left_test/bmp_file98.bmp
Left error is 2

test/right_test/bmp_file210.bmp
correct data = 2, outs = 0
dot2[0] = 3.501953 dot2[1] = 3.076172 dot2[2] = -9.761719 
test/right_test/bmp_file93.bmp
test/right_test/bmp_file196.bmp
test/right_test/bmp_file192.bmp
test/right_test/bmp_file208.bmp
test/right_test/bmp_file207.bmp
test/right_test/bmp_file219.bmp
test/right_test/bmp_file204.bmp
test/right_test/bmp_file77.bmp
test/right_test/bmp_file119.bmp
correct data = 2, outs = 1
dot2[0] = -6.904297 dot2[1] = 4.335938 dot2[2] = -1.699219 
test/right_test/bmp_file236.bmp
test/right_test/bmp_file221.bmp
test/right_test/bmp_file229.bmp
test/right_test/bmp_file71.bmp
correct data = 2, outs = 1
dot2[0] = -4.986328 dot2[1] = 2.332031 dot2[2] = 0.953125 
test/right_test/bmp_file205.bmp
test/right_test/bmp_file242.bmp
test/right_test/bmp_file206.bmp
test/right_test/bmp_file225.bmp
test/right_test/bmp_file195.bmp
test/right_test/bmp_file226.bmp
test/right_test/bmp_file228.bmp
test/right_test/bmp_file209.bmp
test/right_test/bmp_file211.bmp
correct data = 2, outs = 1
dot2[0] = -1.001953 dot2[1] = 2.556641 dot2[2] = -2.494141 
test/right_test/bmp_file216.bmp
test/right_test/bmp_file202.bmp
test/right_test/bmp_file217.bmp
test/right_test/bmp_file106.bmp
test/right_test/bmp_file246.bmp
test/right_test/bmp_file193.bmp
test/right_test/bmp_file194.bmp
test/right_test/bmp_file223.bmp
test/right_test/bmp_file230.bmp
test/right_test/bmp_file191.bmp
test/right_test/bmp_file218.bmp
test/right_test/bmp_file241.bmp
Right error is 4

INFO: [SIM 1] CSim done with 0 errors.
INFO: [SIM 3] *************** CSIM finish ***************


2 つ同じファイル名が続いているところがある。どうもこのフォルダのファイルをリストするコードが良くないのかもしれない?
とりあえず、精度を確認してみると、(105 - 6) / 105 * 100 = 94.3 % だった。

C コードの合成を行った。レポートを示す。
ZYBOt_Keras_49_181006
ZYBOt_Keras_50_181006

Estimated は 8.313 ns でLatency は 178833 クロックだった。100 MHz クロックだと、約 1.79 ms となる。
リソース使用量は BRAM_18K が 15 個、DSP48E が 8 個、FF が 970 個、LUT が 2805 個となった。

curve_conv_nn2_axis3.cpp を貼っておく。

// curve_conv_nn2_axis3.cpp
// 2017/09/09 by marsee
// 畳み込み層のカーネル数 2
// AXI4 Stream入力 番号出力
// 2017/09/18 : dot2[3]の出力も追加
// 2017/12/13 : 直線に加えてカーブのデータも使用して学習した
// 2018/10/03 : 家のZYBOtコースでKerasを使用して学習した
//

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

#include "conv1_weight.h"
#include "conv1_bias.h"
#include "af1_weight.h"
#include "af1_bias.h"
#include "af2_weight.h"
#include "af2_bias.h"

#define REDUSED_ROW     45
#define REDUSED_COULMN  60
#define NUM_OF_KERNELS  2
#define COULMN_PIXELS   56
#define ROW_PIXELS      10
#define ALL_PIXELS      560
#define NUM_OF_OUTPUT   3

int max_ap_fixed(ap_fixed<16, 7, AP_TRN_ZERO, AP_SAT> out[NUM_OF_OUTPUT], ap_uint<2> &out_num);

int course_conv_nn2_axis3(hls::stream<ap_axiu<32,1,1,1> >& ins, ap_uint<2> &outs,
        ap_fixed<16, 7, AP_TRN_ZERO, AP_SAT> dot2[NUM_OF_OUTPUT]){
#pragma HLS INTERFACE s_axilite port=dot2
#pragma HLS INTERFACE s_axilite port=return
#pragma HLS INTERFACE s_axilite port=outs
#pragma HLS INTERFACE axis register both port=ins
    ap_ufixed<8, 0, AP_TRN_ZERO, AP_SAT> buf[ROW_PIXELS][COULMN_PIXELS];
    ap_fixed<13, 6, AP_TRN_ZERO, AP_SAT> conv_out[NUM_OF_KERNELS][ROW_PIXELS-4][COULMN_PIXELS-4];
    ap_fixed<13, 6, AP_TRN_ZERO, AP_SAT> pool_out[NUM_OF_KERNELS][(ROW_PIXELS-4)/2][(COULMN_PIXELS-4)/2];
    ap_fixed<16, 7, AP_TRN_ZERO, AP_SAT> dot1[100];
    ap_axiu<32,1,1,1> pix;

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

    // 10 x 56 に整形
    buf_copy1: for(int i=0; i<REDUSED_ROW; i++){
        buf_copy2: for(int j=0; j<REDUSED_COULMN; j++){
            if (!(i==0 && j==0))    // 最初の入力はすでに入力されている
                ins >> pix; // AXI4-Stream からの入力

            if((i>=33 && i<33+ROW_PIXELS) && (j>=2 && j<2+COULMN_PIXELS)){
                buf[i-33][j-2] = (ap_ufixed<8, 0, AP_TRN_ZERO, AP_SAT>)((ap_ufixed<16, 8, AP_TRN_ZERO, AP_SAT>)(pix.data & 0xff) / 256);
            }
        }
    }

    // Convolutional Neural Network 5x5 kernel, Stride = 1, Padding = 0
    // + ReLU
    CONV1: for(int i=0; i<NUM_OF_KERNELS; i++){ // カーネルの個数
        CONV2: for(int j=0; j<ROW_PIXELS-4; j++){
            CONV3: for(int k=0; k<COULMN_PIXELS-4; k++){
                conv_out[i][j][k] = 0;
                CONV4: for(int m=0; m<5; m++){
                    CONV5: for(int n=0; n<5; n++){
                        conv_out[i][j][k] += buf[j+m][k+n] * conv1_weight[i][0][m][n];
                    }
                }
                conv_out[i][j][k] += conv1_bias[i];

                if(conv_out[i][j][k]<0) // ReLU
                    conv_out[i][j][k] = 0;
            }
        }
    }

    // Pooling Kernel = 2 x 2, Stride = 2
    POOL1: for(int i=0; i<NUM_OF_KERNELS; i++){
        POOL2: for(int j=0; j<ROW_PIXELS-4; j += 2){
            POOL3: for(int k=0; k<COULMN_PIXELS-4; k += 2){
                POOL4: for(int m=0; m<2; m++){
                    POOL5: for(int n=0; n<2; n++){
                        if(m==0 && n==0){
                            pool_out[i][j/2][k/2] = conv_out[i][j][k];
                        } else if(pool_out[i][j/2][k/2] < conv_out[i][j+m][k+n]){
                            pool_out[i][j/2][k/2] = conv_out[i][j+m][k+n];
                        }
                    }
                }
            }
        }
    }

    af1_dot1: for(int col=0; col<100; col++){
        dot1[col] = 0;
        af1_dot2: for(int i=0; i<NUM_OF_KERNELS; i++){
            af1_dot3: for(int j=0; j<(ROW_PIXELS-4)/2; j++){
                af1_dot4: for(int k=0; k<(COULMN_PIXELS-4)/2; k++){
                    dot1[col] += pool_out[i][j][k]*af1_weight[i*((ROW_PIXELS-4)/2)*((COULMN_PIXELS-4)/2)+j*((COULMN_PIXELS-4)/2)+k][col];
                }
            }
        }
        dot1[col] += af1_bias[col];

        if(dot1[col] < 0)   // ReLU
            dot1[col] = 0;
    }

    af2_dot1: for(int col=0; col<NUM_OF_OUTPUT; col++){
        dot2[col] = 0;
        af2_dot2: for(int row=0; row<100; row++){
            dot2[col] += dot1[row]*af2_weight[row][col];
        }
        dot2[col] += af2_bias[col];
    }

    max_ap_fixed(dot2, outs);

    return(0);
}

int max_ap_fixed(ap_fixed<16, 7, AP_TRN_ZERO, AP_SAT> out[NUM_OF_OUTPUT], ap_uint<2> &out_num){
    int max_id;
    ap_fixed<16, 7, AP_TRN_ZERO, AP_SAT> max;

    for(int i=0; i<NUM_OF_OUTPUT; i++){
        if(i == 0){
            max = out[0];
            max_id = 0;
        }else if(out[i]>max){
            max = out[i];
            max_id = i;
        }
    }
    out_num = (ap_uint<2>)max_id;

    return(0);
}



course_conv_nn2_axis3_tb.cpp を貼っておく。(2018/10/06:変更、バグフィックス)

// course_conv_nn2_axis3_tb.cpp
// 2018/10/03 by marsee
// Keras を使用して学習した
//

#include <iostream>
#include "hls_opencv.h"
#include "ap_axi_sdata.h"
#include "hls_video.h"
#include <dirent.h>

#define MAX_HEIGHT  600
#define MAX_WIDTH   800

typedef hls::stream<ap_axiu<32,1,1,1> > AXI_STREAM;
typedef hls::Mat<MAX_HEIGHT, MAX_WIDTH, HLS_8UC3> RGB_IMAGE;
typedef hls::Mat<MAX_HEIGHT, MAX_WIDTH, HLS_8UC1> GRAY_IMAGE;

using namespace cv;

#define NUM_OF_OUTPUT   3

//#define LOOP_COUNT        35  // test_images
#define LOOP_COUNT    1    // for C/RTL Co-Simulation

//#define STRAIGHT_IMAGE_NAME     "train/straight"
//#define LEFT_IMAGE_NAME    "train/left"
//#define RIGHT_IMAGE_NAME   "train/right"
#define STRAIGHT_IMAGE_NAME     "test/straight_test"
#define LEFT_IMAGE_NAME    "test/left_test"
#define RIGHT_IMAGE_NAME   "test/right_test"

const char CUR_DIR[] = ".";
const char PAR_DIR[] = "..";

int course_conv_nn2_axis3(hls::stream<ap_axiu<32,1,1,1> >& ins, ap_uint<2> &outs,
        ap_fixed<16, 7, AP_TRN_ZERO, AP_SAT> dot2[NUM_OF_OUTPUT]);
int resize_gray(AXI_STREAM& ins, AXI_STREAM& outs);
int main_output_loop(char *buf, int loop_count, int correct_data);

int main () {
    char buf[200];

    sprintf(buf, "%s", STRAIGHT_IMAGE_NAME);
    main_output_loop(buf, LOOP_COUNT, 1);

    sprintf(buf, "%s", LEFT_IMAGE_NAME);
    main_output_loop(buf, LOOP_COUNT, 0);

    sprintf(buf, "%s", RIGHT_IMAGE_NAME);
    main_output_loop(buf, LOOP_COUNT, 2);

    return(0);
}

int main_output_loop(char *buf, int loop_count, int correct_data){
    char bmp_file_name[200];
    ap_uint<2> outs;
    AXI_STREAM src_axi, dst_axi;
    Mat src;
    ap_fixed<16, 7, AP_TRN_ZERO, AP_SAT> dot2[NUM_OF_OUTPUT];
    int err_num = 0;
    DIR *dir;
    struct dirent *dp;

    if((dir=opendir(buf)) == NULL){
        fprintf(stderr, "%s Open Error\n", buf);
        exit(1);
    }

    for(int i=0; i<loop_count;){
        if((dp=readdir(dir)) == NULL){
            fprintf(stderr, "readdir(dir) == NULL\n");
            exit(1);
        }

        if(strcmp(CUR_DIR, dp->d_name)!=0 && strcmp(PAR_DIR, dp->d_name)!=0){
            sprintf(bmp_file_name, "%s/%s", buf, dp->d_name);
        }else{
            continue;
        }
        printf("%s\n", bmp_file_name);

        // OpenCV で 画像を読み込む
        src = imread(bmp_file_name);

        // BGR から RGBへ変換
        Mat src_rgb;
        cvtColor(src, src_rgb, CV_BGR2RGB);

        // Mat フォーマットから AXI4 Stream へ変換
        cvMat2AXIvideo(src_rgb, src_axi);

        // resize_gray() 関数をコール
        resize_gray(src_axi, dst_axi);

        course_conv_nn2_axis3(dst_axi, outs, dot2);

        if((int)outs != correct_data){
            //printf("*%s\n", bmp_file_name);
            printf("correct data = %d, outs = %d\n", correct_data, (int)outs);
            for(int i=0; i<NUM_OF_OUTPUT; i++)
                printf("dot2[%d] = %f ", i, (float)dot2[i]);
            printf("\n");
            err_num++;
        }
        i++;
    }
    if(correct_data == 1)
        printf("Straight error is %d\n\n", err_num);
    else if(correct_data == 0)
        printf("Left error is %d\n\n", err_num);
    else // if(correct_data == 2)
        printf("Right error is %d\n\n", err_num);

    closedir(dir);
    return(0);
}

int resize_gray(AXI_STREAM& ins, AXI_STREAM& outs){

    RGB_IMAGE org_img(600, 800);
    GRAY_IMAGE org_img_g(600, 800);
    GRAY_IMAGE resize_img_g(45, 60);
    RGB_IMAGE resize_img(45, 60);

    hls::AXIvideo2Mat(ins, org_img);
    hls::CvtColor<HLS_RGB2GRAY>(org_img, org_img_g);
    hls::Resize(org_img_g, resize_img_g);
    hls::CvtColor<HLS_GRAY2RGB>(resize_img_g, resize_img);
    hls::Mat2AXIvideo(resize_img, outs);

    return(0);
}

  1. 2018年10月04日 05:33 |
  2. Zybot
  3. | トラックバック:0
  4. | コメント:0