FC2カウンター FPGAの部屋 2014年06月05日
FC2ブログ

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

FPGAの部屋

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

Vivado HLS 2014.1でラプラシアン・フィルタ関数をaxi masterモジュールにする5(Cソースのフィルタ処理をパイプライン化)

Vivado HLS 2014.1でラプラシアン・フィルタ関数をaxi masterモジュールにする4(ChipScope Proでデバック)”の続き。

twitter で tu1978 さんに、私のラプラシアンフィルタのCソースのフィルタ処理をパイプライン化して頂いたので、そのVivado HLS 2014.1合成結果と以前の合成結果を比べてみようと思う。

フィルタ処理をパイプライン化したC++のソースを、Vivado HLS 2014.1 のプロジェクトにして、合成を書けてみた結果を下に示す。
Vivado_HLS_2014_1_23_140605.png

Latency を見ると、min で 1943516 クロック、max で 3884624 クロックだった。1クロックは、100MHz, 10 ns なので、約 19.44 ms から、約 38.85 ms だった。

以前の合成結果を下に示す。
Vivado_HLS_2014_1_24_140605.png

Latency を見ると、min で 1450201 クロック、max で 6930730201 クロックだった。1クロックは、100MHz, 10 ns なので、約 14.50 ms から、約 69.31 s だった。実際にこのラプラシアンフィルタのAXI Master IPを使ってみると、実行時間は、984 ms だった。(”Vivado HLS 2014.1でラプラシアン・フィルタ関数をaxi masterモジュールにする2(実機でテスト)”参照)

下に、フィルタ処理をパイプライン化したC++ソースのAnalysis 結果の一部を示す。
Vivado_HLS_2014_1_25_140605.png

以前のラプラシアンフィルタのCソースのAnalysis 結果の一部を示す。
Vivado_HLS_2014_1_26_140605.png

フィルタ処理をパイプライン化したC++ソースを下に示す。(tu1978 さん、ありがとうございました。公開させていただきます)

// laplacian_filter.cpp
// lap_filter_axim()

#include <stdio.h>
#include <string.h>
#include <ap_int.h>

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

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

void conv_line(unsigned int* buf){
    for (int b=0; b<HORIZONTAL_PIXEL_WIDTH; b++){
#pragma HLS pipeline
        buf[b] = conv_rgb2y(buf[b]);    // カラーから白黒へ
    }
}

void filter_line(unsigned int* lap_buf, unsigned int* fl, unsigned int* sl, unsigned int* tl){
    int lap_fil_val;
    ap_uint<8> prev[3],current[3],next[3];    // 0->1ライン目, 1->2ライン目, 2->3ライン目, prev->1pixel前, current->現在, next->次pixel
#pragma HLS array_partition variable=prev complete dim=0
#pragma HLS array_partition variable=current complete dim=0
#pragma HLS array_partition variable=next complete dim=0

    next[0] = fl[0] & 0xFF;
    next[1] = sl[0] & 0xFF;
    next[2] = sl[0] & 0xFF;

    for (int x = 0; x < HORIZONTAL_PIXEL_WIDTH; x++){
#pragma HLS pipeline
        if (x == 0 || x == HORIZONTAL_PIXEL_WIDTH-1){
            lap_fil_val = 0;

            current[0] = next[0];
            next[0] = fl[1];

            current[1] = next[1];
            next[1] = sl[1];

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

            prev[1] = current[1];
            current[1] = next[1];
            next[1] = sl[x+1];

            prev[2] = current[2];
            current[2] = next[2];
            next[2] = 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 lap_filter_axim(int cam_addr, int lap_addr, volatile int *cam_fb, volatile int *lap_fb)
{
    #pragma HLS RESOURCE variable=cam_addr core=AXI4LiteS metadata="-bus_bundle LiteS"
    #pragma HLS RESOURCE variable=lap_addr core=AXI4LiteS metadata="-bus_bundle LiteS"
    #pragma HLS RESOURCE variable=return core=AXI4LiteS metadata="-bus_bundle LiteS"

    #pragma HLS INTERFACE ap_bus port=cam_fb depth=480000
    #pragma HLS INTERFACE ap_bus port=lap_fb depth=480000
    #pragma HLS RESOURCE variable=cam_fb core=AXI4M
    #pragma HLS RESOURCE variable=lap_fb core=AXI4M

    unsigned int line_buf[3][HORIZONTAL_PIXEL_WIDTH];
#pragma HLS array_partition variable line_buf block factor=3 dim=1
#pragma HLS resource variable=line_buf core=RAM_2P

    unsigned int lap_buf[HORIZONTAL_PIXEL_WIDTH];
    int x, y;
    int lap_fil_val;
    int a, b;
    int fl, sl, tl;
    unsigned int offset_cam_addr, offset_lap_addr;
    int *cam_fb_addr, *lap_fb_addr;

    offset_cam_addr = cam_addr/sizeof(int);
    offset_lap_addr = lap_addr/sizeof(int);

    // RGB値をY(輝度成分)のみに変換し、ラプラシアンフィルタを掛けた。
    for (y=1; y<VERTICAL_PIXEL_WIDTH-1; y++){
        fl = (y-1)%3;    // 最初のライン, y=1 012, y=2 120, y=3 201, y=4 012
        sl = y%3;        // 2番めのライン
        tl = (y+1)%3;    // 3番目のライン

        if (y == 1){
#ifndef __SYNTHESIS__
            printf("copy 3 lines\n");
#endif
            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));
                conv_line(line_buf[a]);
            }
        }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));
            conv_line(line_buf[tl]);
        }
        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)));
#ifndef __SYNTHESIS__
        printf("write back:%d\n", y);
#endif
        memcpy((unsigned int*)lap_fb_addr, (unsigned int*)lap_buf, HORIZONTAL_PIXEL_WIDTH*sizeof(int));
    }

    // 最初と最後のラインは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));

#if 0
        for (x=0; x<HORIZONTAL_PIXEL_WIDTH; x++){
            if (y==0 || y==VERTICAL_PIXEL_WIDTH-1){ // 縦の境界の時の値は0とする
                lap_fil_val = 0;
            }else if (x==0 || x==HORIZONTAL_PIXEL_WIDTH-1){ // 横の境界の時も値は0とする
                lap_fil_val = 0;
            }else{
                 if (x == 1){ // ラインの最初でラインの画素を読み出す
                    if (y == 1){ // 最初のラインでは3ライン分の画素を読み出す
                        for (a=0; a<3; a++){ // 3ライン分
                            cam_fb_addr = (int*)(cam_fb+offset_cam_addr+(a*(HORIZONTAL_PIXEL_WIDTH)));
                            memcpy(&line_buf[a][0], (const int*)cam_fb_addr, HORIZONTAL_PIXEL_WIDTH*sizeof(int));
                            for (b=0; b<HORIZONTAL_PIXEL_WIDTH; b++){ // ライン
#pragma HLS pipeline
                                line_buf[a][b] = conv_rgb2y(line_buf[a][b]);    // カラーから白黒へ
                            }
                        }
                    } else {
                        cam_fb_addr = (int*)(cam_fb+offset_cam_addr+((y+1)*(HORIZONTAL_PIXEL_WIDTH)));
                        memcpy(line_buf[(y+1)%3], (const int*)cam_fb_addr, HORIZONTAL_PIXEL_WIDTH*sizeof(int));
                        for (b=0; b<HORIZONTAL_PIXEL_WIDTH; b++){ // ライン
#pragma HLS pipeline
                            line_buf[(y+1)%3][b] = conv_rgb2y(line_buf[(y+1)%3][b]);    // カラーから白黒へ
                        }
                    }
                }
                lap_fil_val = laplacian_fil(line_buf[fl][x-1], line_buf[fl][x], line_buf[fl][x+1], line_buf[sl][x-1], line_buf[sl][x], line_buf[sl][x+1], line_buf[tl][x-1], line_buf[tl][x], line_buf[tl][x+1]);
            }
            lap_buf[x] = (lap_fil_val<<16)+(lap_fil_val<<8)+lap_fil_val; // RGB同じ値を入れる
        }
        lap_fb_addr = (int *)(lap_fb+offset_lap_addr+(y*(HORIZONTAL_PIXEL_WIDTH)));
        memcpy(lap_fb_addr, (const int*)lap_buf, HORIZONTAL_PIXEL_WIDTH*sizeof(int));
    }
#endif
    return(7);
}

// 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);
}


やはり、ラプラシアンフィルタに値を入力するためにラインバッファの該当領域を参照するのではなく、明示的にFFを作るように書く必要がある様だ。これを余りやってしまうと、HDLを書くのと変わらなくなってしまう気がする。。。
  1. 2014年06月05日 05:37 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0