FC2カウンター FPGAの部屋 ラプラシアンフィルタのソフトウェアとハードウェアの速度の比較3
FC2ブログ

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

FPGAの部屋

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

ラプラシアンフィルタのソフトウェアとハードウェアの速度の比較3

ラプラシアンフィルタのソフトウェアとハードウェアの速度の比較2”の続き。

今回はVivado HLS 2014.4 で最速の性能をたたき出しているCソースコードをZYBOのLinuxで動作するソフトウェアとして性能を測定してみた。

今回、試してみるVivado HLS 2014.4 のCソースコードの記事を示す。
Vivado HLS 2014.4 で合成したラプラシアンフィルタIPの高速化15(性能が最大になる設定を探る8、追加1)
Vivado HLS 2014.4 で合成したラプラシアンフィルタIPの高速化16(性能が最大になる設定を探る8、追加2)

そのCソースコードをZYBOのLinuxで動作するソフトウェアとして書き直した laplacian_filter4.c を下に示す。
2015/07/12:修正 時間計測にバグがあったので、修正しました。miyox さん、ありがとうございました)

// laplacian_filter4.c
// RGBをYに変換後にラプラシアンフィルタを掛ける。
// ピクセルのフォーマットは、{8'd0, R(8bits), G(8bits), B(8bits)}, 1pixel = 32bits
// 2013/09/16
// 2014/12/04 : ZYBO用Ubuntu Linux のUIO用に変更
// Vivado HLS 2014.4 のプロジェクト ZYBO/lap_filter_axim_tu2_2014_4を使用したソースコードと同じものを使用する。これは、Vivado HLSで最速のCソースコードだ

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

#define HORIZONTAL_PIXEL_WIDTH    800
#define VERTICAL_PIXEL_WIDTH    600
#define ALL_PIXEL_VALUE    (HORIZONTAL_PIXEL_WIDTH*VERTICAL_PIXEL_WIDTH)

#define CMA_START_ADDRESS           0x17800000
#define VIDEO_BUFFER_START_ADDRESS  0x18000000  // Limit 0x18800000, 800*600*4 = 2MBytes * 2
#define LAPLACIAN_FILTER_ADDRESS    0x18200000  // 800*600*4 = 0x1d4c00

int laplacian_fil(int x0y0, int x1y0, int x2y0, int x0y1, int x1y1, int x2y1, int x0y2, int x1y2, int x2y2);
int conv_rgb2y(int rgb);
int chkhex(char *str);

void filter_line(unsigned int* lap_buf, unsigned int* fl, unsigned int* sl, unsigned int* tl){
    int lap_fil_val;
    int prev[3],current[3],next[3];    // 0->1ライン目, 1->2ライン目, 2->3ライン目, prev->1pixel前, current->現在, next->次pixel
    int x;

    next[0] = conv_rgb2y(fl[0]);
    next[1] = conv_rgb2y(sl[0]);
    next[2] = conv_rgb2y(tl[0]);

    for (x = 0; x < HORIZONTAL_PIXEL_WIDTH; x++){
        if (x == 0 || x == HORIZONTAL_PIXEL_WIDTH-1){
            lap_fil_val = 0;

            current[0] = next[0];
            next[0] = conv_rgb2y(fl[1]);

            current[1] = next[1];
            next[1] = conv_rgb2y(sl[1]);

            current[2] = next[2];
            next[2] = conv_rgb2y(tl[1]);
        }else{
            prev[0] = current[0];
            current[0] = next[0];
            next[0] = conv_rgb2y(fl[x+1]);

            prev[1] = current[1];
            current[1] = next[1];
            next[1] = conv_rgb2y(sl[x+1]);

            prev[2] = current[2];
            current[2] = next[2];
            next[2] = conv_rgb2y(tl[x+1]);
            lap_fil_val = laplacian_fil(prev[0], current[0], next[0],
                                        prev[1], current[1], next[1],
                                        prev[2], current[2], next[2]);
        }
        lap_buf[x] = (lap_fil_val<<16)+(lap_fil_val<<8)+lap_fil_val; // RGB同じ値を入れる
    }
}

int main()
{
    volatile unsigned int *cam_fb = 0;
    volatile unsigned int *lap_fb = 0;
    volatile unsigned int *cam_addr;
    volatile unsigned int *lap_addr;
    int lap_fil_val;
    int x, y;
    struct timeval start_time, temp1, temp2, end_time;
    unsigned int line_buf[3][HORIZONTAL_PIXEL_WIDTH];
    int a, b;
    int fl, sl, tl;
    int fd0, fd3;
      unsigned int offset_cam_addr, offset_lap_addr;
    unsigned int lap_buf[HORIZONTAL_PIXEL_WIDTH];
    volatile unsigned int *cam_fb_addr, *lap_fb_addr;
    int line_sel;
    volatile unsigned int *bmdc_axi_lites;
    volatile unsigned int *frame_buffer;

    // gettimeofday(&start_time, NULL);    // プログラム起動時の時刻を記録

    // frame_buffer にマップする
    fd3 = open("/dev/uio3", O_RDWR); // Frame Buffer
    if (fd3 < 1){
        fprintf(stderr, "/dev/uio3 open error\n");
        exit(-1);
    }
    frame_buffer = (volatile unsigned int *)mmap(NULL, 0x1000000, PROT_READ|PROT_WRITE, MAP_SHARED, fd3, 0);
    if (!frame_buffer){
        fprintf(stderr, "frame_buffer mmap error\n");
        exit(-1);
    }
    cam_addr = (volatile unsigned int *)((unsigned int)frame_buffer + (unsigned int)(VIDEO_BUFFER_START_ADDRESS-CMA_START_ADDRESS));

    // ラプラシアンフィルタの結果を入れておくフレーム・バッファ
    lap_addr = (volatile unsigned int *)((unsigned int)frame_buffer + (unsigned int)(LAPLACIAN_FILTER_ADDRESS-CMA_START_ADDRESS));

    offset_cam_addr = (volatile unsigned int)((unsigned int)cam_addr/sizeof(int));
    offset_lap_addr = (volatile unsigned int)((unsigned int)lap_addr/sizeof(int));
    
    gettimeofday(&start_time, NULL);
    
     // ラプラシアンフィルタ処理開始
    // RGB値をY(輝度成分)のみに変換し、ラプラシアンフィルタを掛けた。
     for (y=1, line_sel=0; y<VERTICAL_PIXEL_WIDTH-1; y++){
        // 最初のライン, y=1 012, y=2 120, y=3 201, y=4 012
        switch(line_sel){
            case 1 :
                fl = 0; sl = 1; tl = 2;
                break;
            case 2 :
                fl = 1; sl = 2; tl = 0;
                break;
            case 3 :
                fl = 2; sl = 0; tl = 1;
                break;
            default :
                fl = 0; sl = 1; tl = 2;
        }

        if (y == 1){
            for (a=0; a<3; a++){
 // 3ライン分
                cam_fb_addr = (int*)(cam_fb+offset_cam_addr+(a*(HORIZONTAL_PIXEL_WIDTH)));
                memcpy(line_buf[a], (unsigned int*)cam_fb_addr, HORIZONTAL_PIXEL_WIDTH*sizeof(int));
            }
        }else// 最初のラインではないので、1ラインだけ読み込む。すでに他の2ラインは読み込まれている
            cam_fb_addr = (int*)(cam_fb+offset_cam_addr+((y+1)*(HORIZONTAL_PIXEL_WIDTH)));
            memcpy(line_buf[tl], (unsigned int*)cam_fb_addr, HORIZONTAL_PIXEL_WIDTH*sizeof(int));
        }
        filter_line(lap_buf, line_buf[fl], line_buf[sl], line_buf[tl]);
        lap_fb_addr = (int *)(lap_fb+offset_lap_addr+(y*(HORIZONTAL_PIXEL_WIDTH)));
        memcpy((unsigned int*)lap_fb_addr, (unsigned int*)lap_buf, HORIZONTAL_PIXEL_WIDTH*sizeof(int));

        line_sel++;
        if (line_sel > 3){
            line_sel = 1;
        }
    }

    // 最初と最後のラインは0にする
    for (x = 0; x < HORIZONTAL_PIXEL_WIDTH; x++)
        lap_buf[x] = 0;
    lap_fb_addr = (int *)(lap_fb+offset_lap_addr+(0*(HORIZONTAL_PIXEL_WIDTH)));
    memcpy((unsigned int*)lap_fb_addr, (unsigned int*)lap_buf, HORIZONTAL_PIXEL_WIDTH*sizeof(int));
    lap_fb_addr = (int *)(lap_fb+offset_lap_addr+(VERTICAL_PIXEL_WIDTH-1)*HORIZONTAL_PIXEL_WIDTH);
    memcpy((unsigned int*)lap_fb_addr, (unsigned int*)lap_buf, HORIZONTAL_PIXEL_WIDTH*sizeof(int));
    // ラプラシアンフィルタ処理終了
    
    gettimeofday(&end_time, NULL);
    
    munmap((void *)frame_buffer, 0x1000000);
 
   // ラプラシアンフィルタ表示画面に切り替え
    // Bitmap Display Controller AXI4 Lite Slave (UIO0)
    fd0 = open("/dev/uio0", O_RDWR); // bitmap_display_controller axi4 lite
    if (fd0 < 1){
        fprintf(stderr, "/dev/uio0 open error\n");
        exit(-1);
    }
    bmdc_axi_lites = (volatile unsigned *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd0, 0);
    if (!bmdc_axi_lites){
        fprintf(stderr, "bmdc_axi_lites mmap error\n");
        exit(-1);
    }
    bmdc_axi_lites[0] = (unsigned int)LAPLACIAN_FILTER_ADDRESS; // Bitmap Display Controller start (ラプラシアンフィルタ表示画面のアドレス)
    munmap((void *)bmdc_axi_lites, 0x10000);
    
    //gettimeofday(&end_time, NULL);
    if (end_time.tv_usec < start_time.tv_usec) {
        printf("total time = %ld.%06ld sec\n", end_time.tv_sec - start_time.tv_sec - 11000000 + end_time.tv_usec - start_time.tv_usec);
    }
    else {
        printf("total time = %ld.%06ld sec\n", end_time.tv_sec - start_time.tv_sec, end_time.tv_usec - start_time.tv_usec);
    }
    return(0);
}

// RGBからYへの変換
// RGBのフォーマットは、{8'd0, R(8bits), G(8bits), B(8bits)}, 1pixel = 32bits
// 輝度信号Yのみに変換する。変換式は、Y =  0.299R + 0.587G + 0.114B
// "YUVフォーマット及び YUV<->RGB変換"を参考にした。http://vision.kuee.kyoto-u.ac.jp/~hiroaki/firewire/yuv.html
// 2013/09/27 : float を止めて、すべてint にした
int conv_rgb2y(int rgb){
    int r, g, b, y_f;
    int y;

    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;の係数に256倍した
    y = y_f >> 8// 256で割る

    return(y);
}

// ラプラシアンフィルタ
// x0y0 x1y0 x2y0 -1 -1 -1
// x0y1 x1y1 x2y1 -1  8 -1
// x0y2 x1y2 x2y2 -1 -1 -1
int laplacian_fil(int x0y0, int x1y0, int x2y0, int x0y1, int x1y1, int x2y1, int x0y2, int x1y2, int x2y2)
{
    int y;

    y = -x0y0 -x1y0 -x2y0 -x0y1 +8*x1y1 -x2y1 -x0y2 -x1y2 -x2y2;
    if (y<0)
        y = 0;
    else if (y>255)
        y = 255;
    return(y);
}

// 文字列が16進数かを調べる
int chkhex(char *str){
    while (*str != '\0'){
        if (!isxdigit(*str))
            return 0;
        str++;
    }
    return 1;
}


このCソースコードを gcc laplacian_filter4.c -o laplacian_filter4 コマンドでコンパイルして、./laplacian_filter4 で実行した。
その結果を下に示す。
soft_hard_comp_16_150704.png

ラプラシアンフィルタ処理のみの経過時間は、228 ms だった。以外にもソフトウェアのラプラシアンフィルタ処理で最速の結果になった。キャッシュに入っている確率が高いからだろうか?
完全ソフトウェアとして作った laplacian_fiter1 のラプラシアンフィルタ処理のみの経過時間は 447 ms だったので、laplacian_filter4 の性能は 447 ms / 228 ms ≒ 1.96 倍となった。

今回のCソースコードでのソフトウェアと高位合成されたハードウェアのラプラシアンフィルタ処理のみの経過時間の性能差は、228 ms / 15.3 ms ≒ 14.9 倍だった。

完全にソフトウェアとして作ったCソースコードと今回のCソースコードで高位合成されたハードウェアのラプラシアンフィルタ処理のみの経過時間の性能差は447 ms / 15.3 ms ≒ 29.2 倍となった。

今回のCソースコードで高位合成されたハードウェアで測定したラプラシアンフィルタ処理のみの経過時間を下に示す。./lap_fil_hls_1shot
soft_hard_comp_17_150704.png
  1. 2015年07月04日 05:23 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

コメント

コメントの投稿


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

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