FC2カウンター FPGAの部屋 手書き数字認識用畳み込みニューラルネットワーク回路の製作9(Linuxでの動作2)
FC2ブログ

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

FPGAの部屋

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

手書き数字認識用畳み込みニューラルネットワーク回路の製作9(Linuxでの動作2)

手書き数字認識用畳み込みニューラルネットワーク回路の製作8(Linuxでの動作1)”の続き。

前回はPYNQボードのDebian 上でカメラ画像を表示するアプリケーションソフトを作成して、カメラ画像を表示した。今回は、PYNQボードのDebian 上でピンクの四角枠を表示して、その中の手書き数字を認識した。

最初に、デバイスツリー・オーバーレイ、クロックの設定、FPGAのコンフィギュレーションを行う。all_settings_bat ファイルを作成した。all_settings_bat ファイルの中身を示す。

./devtov pynq_mnist_cnn
./clock_settings.sh
./fpgamag pynq_fastx_wrapper.bit


まずは、su になる必要があるので、
su コマンドを入力して、ルートのパスワード admin を入れてから、./all_settings_bat を実行して、su から exitする。
hand_draw_num_109_170721.png

./cam_disp を実行して、カメラ画像を表示してから、build ディレクトリに移動して、./pynq_mnist_cnn を実行する。
hand_draw_num_110_170721.png

手書き数字の 4 を認識した。
hand_draw_num_108_170721.jpg

hand_draw_num_107_170720.png

gcc で pynq_mnist_cnn.c をコンパイルすると浮動小数点数演算でのmnist_cnn は約 60.8 ms かかった。
次に、gcc の -O2 オプションを付けたところ、最初は 7 ms 程度だったが、キャッシュがフィルしたであろう 2 回目以降は約 6.13 ms で実行できているので、ハードウェアの2倍速くなった。
hand_draw_num_106_170720.png

ハードウェアの方が遅くなって残念な結果になったが、DSP をほとんど使用していないので、仕方ないのかもしれない?
本格的にリソースを使用するようにチューニングすると、PYNQボードには入らなくなってしまうと思うので、とりあえずは、今のままやっていくことにする。

build ディレクトリの構成を示す。重みやバイアス、Vivado HLS のドライバ・ファイルと pynq_mnist_cnn.c がある。
hand_draw_num_111_170721.png

Makefile を示す。

#Makefile
# Referred to http://www.ie.u-ryukyu.ac.jp/~e085739/c.makefile.tuts.html

PROGRAM = pynq_mnist_cnn
OBJS = pynq_mnist_cnn.o xmnist_conv_nn.o xmnist_conv_nn_linux.o xsquare_frame_gen.o xsquare_frame_gen_linux.o

CC = gcc
CFLAGS = -Wall -O2

.SUFFIXES: .c .o

.PHONY: all

all: pynq_mnist_cnn

pynq_mnist_cnn: $(OBJS)
    $(CC) -Wall -o $@ $(OBJS)
    
.c.o:
    $(CC) $(CFLAGS) -c $<

    
.PHONY: clean
clean:
    $(RM) $(PROGRAM) $(OBJS)


pynq_mnist_cnn.c を貼っておく。

//
// pynq_mnist_cnn.c
// 2017/07/17 by marsee
//

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <termios.h>
#include <sys/time.h>

#include "xmnist_conv_nn.h"
#include "xsquare_frame_gen.h"
#include "af1_bias_float.h"
#include "af1_weight_float.h"
#include "af2_bias_float.h"
#include "af2_weight_float.h"
#include "conv1_bias_float.h"
#include "conv1_weight_float.h"

#define HORIZONTAL_PIXELS   800
#define VERTICAL_LINES      600
#define PIXEL_NUM_OF_BYTES  4
#define ALL_DISP_ADDRESS    (HORIZONTAL_PIXELS*VERTICAL_LINES*PIXEL_NUM_OF_BYTES)

int max_int(int out[10]);
int max_float(float out[10]);
int mnist_conv_nn_float(int in[22400], int addr_offset, float out[10]);
float conv_rgb2y_soft(int rgb);

int main()
{
    int fd0, fd3, fd4;
    int fd_udmabuf, fd_paddr;
    volatile unsigned int *axis_switch_0, *axis_switch_1;
    volatile unsigned int *axi_gpio_0;
    volatile unsigned int *frame_buffer;
    unsigned char  attr[1024];
    unsigned long  phys_addr;
    struct termios save_settings;
    struct termios settings;
    int xval, yval;
    int inbyte_in;
    int result_disp = 0;
    unsigned int conv_addr;
    int max_id_float;
    struct timeval start_time, end_time;
    XMnist_conv_nn mcnn;
    XSquare_frame_gen sf_gen;
    int i, res;
    int max_id;
    int result[10];
    float result_float[10];

    // Reffered to http://d.hatena.ne.jp/mFumi/20101002/1286003738
    tcgetattr(0,&save_settings);
    settings = save_settings;

    settings.c_lflag &= ~(ECHO|ICANON);
    settings.c_cc[VTIME] = 0;
    settings.c_cc[VMIN] = 1;
    tcsetattr(0,TCSANOW,&settings);
    fcntl(0,F_SETFL,O_NONBLOCK);

    // axi_gpio_0 (uio0)
    fd0 = open("/dev/uio0", O_RDWR); // axi_iic_0
    if (fd0 < 1){
        fprintf(stderr, "/dev/uio0 (axi_gpio_0) open errorn");
        exit(-1);
    }
    axi_gpio_0 = (volatile unsigned int *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd0, 0);
    if (axi_gpio_0 == MAP_FAILED){
        fprintf(stderr, "axi_gpio_0 mmap errorn");
        exit(-1);
    }
    
    // axis_switch_0 (uio3)
    fd3 = open("/dev/uio3", O_RDWR); // axis_switch_0
    if (fd3 < 1){
        fprintf(stderr, "/dev/uio3 (axis_switch_0) open errorn");
        exit(-1);
    }
    axis_switch_0 = (volatile unsigned int *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd3, 0);
    if (axis_switch_0 == MAP_FAILED){
        fprintf(stderr, "axis_switch_0 mmap errorn");
        exit(-1);
    }
    
    // axis_switch_1 (uio4)
    fd4 = open("/dev/uio4", O_RDWR); // axis_switch_1
    if (fd4 < 1){
        fprintf(stderr, "/dev/uio4 (axis_switch_1) open errorn");
        exit(-1);
    }
    axis_switch_1 = (volatile unsigned int *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd4, 0);
    if (axis_switch_1 == MAP_FAILED){
        fprintf(stderr, "axis_switch_1 mmap errorn");
        exit(-1);
    }
       
    // udmabuf4
    fd_udmabuf = open("/dev/udmabuf4", O_RDWR | O_SYNC); // frame_buffer, The chache is disabled. 
    if (fd_udmabuf == -1){
        fprintf(stderr, "/dev/udmabuf4 open errorn");
        exit(-1);
    }
    frame_buffer = (volatile unsigned int *)mmap(NULL, (ALL_DISP_ADDRESS*3), PROT_READ|PROT_WRITE, MAP_SHARED, fd_udmabuf, 0);
    if (frame_buffer == MAP_FAILED){
        fprintf(stderr, "frame_buffer mmap errorn");
        exit(-1);
    }

    // phys_addr of udmabuf4
    fd_paddr = open("/sys/devices/soc0/amba/amba:udmabuf4/udmabuf/udmabuf4/phys_addr", O_RDONLY);
    if (fd_paddr == -1){
        fprintf(stderr, "/sys/devices/soc0/amba/amba:udmabuf4/udmabuf/udmabuf4/phys_addr open errorn");
        exit(-1);
    }
    read(fd_paddr, attr, 1024);
    sscanf((const char *)attr, "%lx", &phys_addr);  
    close(fd_paddr);
    printf("phys_addr = %x\n", (unsigned int)phys_addr);

    // Mnist_conv_nn, Square_frame_gen Initialize
    if (XMnist_conv_nn_Initialize(&mcnn, "mnist_conv_nn_0") != XST_SUCCESS){
        fprintf(stderr,"mnist_conv_nn_0 open error\n");
        exit(-1);
    }
    if (XSquare_frame_gen_Initialize(&sf_gen, "square_frame_gen_0") != XST_SUCCESS){
        fprintf(stderr,"square_frame_gen_0 open error\n");
        exit(-1);
    }

    // square_frame_gen initialize
    XSquare_frame_gen_Set_x_pos(&sf_gen, HORIZONTAL_PIXELS/2);
    xval = HORIZONTAL_PIXELS/2;
    XSquare_frame_gen_Set_y_pos(&sf_gen, VERTICAL_LINES/2);
    yval = VERTICAL_LINES/2;
    XSquare_frame_gen_Set_width(&sf_gen, 28);
    XSquare_frame_gen_Set_height(&sf_gen, 28);
    XSquare_frame_gen_Set_off_on(&sf_gen, 1); // on

    // XSquare_frame_gen start
    XSquare_frame_gen_DisableAutoRestart(&sf_gen);
    while(!XSquare_frame_gen_IsIdle(&sf_gen)) ;
    XSquare_frame_gen_Start(&sf_gen);
    XSquare_frame_gen_EnableAutoRestart(&sf_gen);

    // mnist_conv_nn initialize
    XMnist_conv_nn_Set_addr_offset(&mcnn, HORIZONTAL_PIXELS/2);
    XMnist_conv_nn_Set_in_r(&mcnn, (unsigned int)phys_addr+HORIZONTAL_PIXELS*(VERTICAL_LINES/2)*sizeof(int));
 
    // axis_switch_1, 1to2 ,Select M00_AXIS
    // Refer to http://marsee101.blog19.fc2.com/blog-entry-3177.html
    axis_switch_1[16] = 0x80000000// 0x40 = 0x80000000, disable
    axis_switch_1[17] = 0x0// 0x44 = 0
    axis_switch_1[0] = 0x2// Comit registers
    
    // axis_switch_0, 2to1, Select S00_AXIS
    // Refer to http://marsee101.blog19.fc2.com/blog-entry-3177.html
    axis_switch_0[16] = 0x1// 0x40 = 1;
    axis_switch_0[0] = 0x2// Comit registers
  
    axi_gpio_0[0] = 0// LED 0 clear

    printf("mnist_conv_nn_test, <h> : left, <k> : up, <j> : down, <l> : right, <r> : result, <d> : help, <q> : exit\n");
    
    while(1){   // main loop
        inbyte_in = getchar(); fflush(stdin);
        if (inbyte_in != EOF){
            if (inbyte_in == 'q')
                break;
            else if (inbyte_in < 'A' || inbyte_in > 'z')
                usleep(10000);
        }
        switch(inbyte_in) {
            case 'h' : // left
            case 'H' : // left -5
                if(inbyte_in == 'h' && xval > 0)
                    --xval;
                else if(inbyte_in == 'H' && xval >= 5)
                    xval -= 5;
                XSquare_frame_gen_Set_x_pos(&sf_gen, xval);
                XMnist_conv_nn_Set_addr_offset(&mcnn, xval);
                printf("X_POS = %d, Y_POS = %d\n", xval, yval);
                break;
            case 'l' : // right
            case 'L' : // right +5
                if(inbyte_in == 'l' && xval < HORIZONTAL_PIXELS-28)
                    xval++;
                else if(inbyte_in == 'L' && xval <= HORIZONTAL_PIXELS-28-5)
                    xval += 5;
                XSquare_frame_gen_Set_x_pos(&sf_gen, xval);
                XMnist_conv_nn_Set_addr_offset(&mcnn, xval);
                printf("X_POS = %d, Y_POS = %d\n", xval, yval);
                break;
            case 'k' : // up
            case 'K' : // up -5
                if(inbyte_in == 'k' && yval > 0)
                    --yval;
                else if(inbyte_in == 'K' && yval >= 5)
                    yval -= 5;
                XSquare_frame_gen_Set_y_pos(&sf_gen, yval);
                XMnist_conv_nn_Set_in_r(&mcnn, (unsigned int)phys_addr+HORIZONTAL_PIXELS*yval*sizeof(int));
                printf("X_POS = %d, Y_POS = %d\n", xval, yval);
                break;
            case 'j' : // down
            case 'J' : // down +5
                if(inbyte_in == 'j' && yval < VERTICAL_LINES-28)
                    yval++;
                else if(inbyte_in == 'J' && yval <= VERTICAL_LINES-28-5)
                    yval += 5;
                XSquare_frame_gen_Set_y_pos(&sf_gen, yval);
                XMnist_conv_nn_Set_in_r(&mcnn, (unsigned int)phys_addr+HORIZONTAL_PIXELS*yval*sizeof(int));
                printf("X_POS = %d, Y_POS = %d\n", xval, yval);
                break;
            case 'r' : // result check
                result_disp = 1;
                break;
            case 'd' : // help
            case 'D' :
                printf("mnist_conv_nn_test, <h> : left, <k> : up, <j> : down, <l> : right, <r> : result, <d> : help, <q> : exit\n");
                break;
            case 'q' : // exit
                return(0);
        }

        if(result_disp){
            printf("\nHardware\n");
            // XMnist_conv_nn start
            XMnist_conv_nn_DisableAutoRestart(&mcnn);
            while(!XMnist_conv_nn_IsIdle(&mcnn));
            gettimeofday(&start_time, NULL);
            XMnist_conv_nn_Start(&mcnn);
            while(!XMnist_conv_nn_IsIdle(&mcnn));
            gettimeofday(&end_time, NULL);
            if (end_time.tv_usec < start_time.tv_usec) {
                printf("conv_time = %ld.%06ld sec\n", end_time.tv_sec - start_time.tv_sec - 11000000 + end_time.tv_usec - start_time.tv_usec);
            } else {
                printf("conv_time = %ld.%06ld sec\n", end_time.tv_sec - start_time.tv_sec, end_time.tv_usec - start_time.tv_usec);
            }

            // mnist cnn result check
            for(i=0; i<5; i++){
                XMnist_conv_nn_Read_out_V_Words(&mcnn, i, &res, 1);
                result[i*2] = res & 0x0fff;
                if(result[i*2] & 0x800// minus
                    result[i*2] = 0xfffff000 | result[i*2]; // Sign extension

                result[i*2+1] = (res & 0x0fff0000) >> 16;
                if(result[i*2+1] & 0x800// minus
                    result[i*2+1] = 0xfffff000 | result[i*2+1]; // Sign extension
            }

            max_id = max_int(result);
            axi_gpio_0[0] = max_id;

            for(i=0; i<10; i++){
                printf("result[%d] = %x\n", i, result[i]);
            }
            printf("max_id = %d\n", max_id);

            printf("\nSoftware\n");
            conv_addr = (unsigned int)frame_buffer+HORIZONTAL_PIXELS*yval*sizeof(int);
            gettimeofday(&start_time, NULL);
            mnist_conv_nn_float((int *)conv_addr, xval, result_float);
            gettimeofday(&end_time, NULL);
            max_id_float = max_float(result_float);

           if (end_time.tv_usec < start_time.tv_usec) {
                printf("conv_time = %ld.%06ld sec\n", end_time.tv_sec - start_time.tv_sec - 11000000 + end_time.tv_usec - start_time.tv_usec);
            } else {
                printf("conv_time = %ld.%06ld sec\n", end_time.tv_sec - start_time.tv_sec, end_time.tv_usec - start_time.tv_usec);
            }

            for(i=0; i<10; i++){
                printf("result_float[%d] = %f\n", i, result_float[i]);
            }
            printf("max_id_float = %d\n", max_id_float);
            printf("\n");

            result_disp = 0;
        }
    }
            
    munmap((void *)axi_gpio_0, 0x10000);
    munmap((void *)axis_switch_0, 0x10000);
    munmap((void *)axis_switch_1, 0x10000);
    munmap((void *)frame_buffer, (ALL_DISP_ADDRESS*3));
    
    close(fd0);
    close(fd3);
    close(fd4);
    close(fd_udmabuf);
    
    // Reffered to http://d.hatena.ne.jp/mFumi/20101002/1286003738
    tcsetattr(0,TCSANOW,&save_settings);
    
    return(0);
}

int max_int(int out[10]){
    int max_id;
    int max = 0, i;

    for(i=0; i<10; i++){
        if(i == 0){
            max = out[0];
            max_id = 0;
        }else if(out[i]>max){
            max = out[i];
            max_id = i;
        }
    }
    return(max_id);
}

int mnist_conv_nn_float(int in[22400], int addr_offset, float out[10]){
    
    int i, j, k, m, n, col, row;
    float buf[28][28];
    float conv_out[10][24][24];
    float pool_out[10][12][12];
    float dot1[100];
    float dot2[10];
   
    // 
    /*for (i=0; i<28; i++){        for (j=0; j<800; j++){            if (j>=addr_offset && j<addr_offset+28)                printf("%2x, ", (int)(conv_rgb2y_soft(in[i*800+j])*256.0));        }        printf("\n");    } */
    for(i=0; i<28; i++){
        for(j=0; j<800; j++){
            if (j>=addr_offset && j<addr_offset+28){
                buf[i][j-addr_offset] = (float)0.99609375 - (float)conv_rgb2y_soft(in[i*800+j]);
            }
        }
    }

    // Convolutional Neural Network 5x5 kernel, Stride = 1, Padding = 0
    // + ReLU
    for(i=0; i<10; i++){    //
        for(j=0; j<24; j++){
            for(k=0; k<24; k++){
                conv_out[i][j][k] = 0;
                for(m=0; m<5; m++){
                    for(n=0; n<5; n++){
                        conv_out[i][j][k] += buf[j+m][k+n] * conv1_fweight[i][0][m][n];
                    }
                }
                conv_out[i][j][k] += conv1_fbias[i];

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

    // Pooling Kernel = 2 x 2, Stride = 2
    for(i=0; i<10; i++){
        for(j=0; j<24; j += 2){
            for(k=0; k<24; k += 2){
                for(m=0; m<2; m++){
                    for(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];
                        }
                    }
                }
            }
        }
    }

    for(col=0; col<100; col++){
        dot1[col] = 0;
        for(i=0; i<10; i++){
            for(j=0; j<12; j++){
                for(k=0; k<12; k++){
                    dot1[col] += pool_out[i][j][k]*af1_fweight[i*12*12+j*12+k][col];
                }
            }
        }
        dot1[col] += af1_fbias[col];

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

    for(col=0; col<10; col++){
        dot2[col] = 0;
        for(row=0; row<100; row++){
            dot2[col] += dot1[row]*af2_fweight[row][col];
        }
        dot2[col] += af2_fbias[col];

        out[col] = dot2[col];
    }

    return(0);
}

int max_float(float out[10]){
    int max_id, i;
    float max = 0;

    for(i=0; i<10; i++){
        if(i == 0){
            max = out[0];
            max_id = 0;
        }else if(out[i]>max){
            max = out[i];
            max_id = i;
        }
    }
    return(max_id);
}

float conv_rgb2y_soft(int rgb){
    int r, g, b, y_f;
    int y;
    float y_float;

    b = rgb & 0xff;
    g = (rgb>>8) & 0xff;
    r = (rgb>>16) & 0xff;

    y_f = 77*r + 150*g + 29*b; //y_f = 0.299*r + 0.587*g + 0.114*b;
    y = y_f >> 8// /256

    if (y >= 256)
        y = 255;

    y_float = (float)y/256.0;

    return(y_float);
}

  1. 2017年07月21日 04:20 |
  2. DNN
  3. | トラックバック:0
  4. | コメント:0

コメント

コメントの投稿


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

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