FC2カウンター FPGAの部屋 Vivado HLSでラプラシアン・フィルタ関数をaxi masterモジュールにする1
FC2ブログ

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

FPGAの部屋

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

Vivado HLSでラプラシアン・フィルタ関数をaxi masterモジュールにする1

今回は、ラプラシアン・フィルタ関数自体をVivado HLSで、AXI4 Master を使用したIPとして生成して見ようと思う。

まずは、laplacian_filter.c を Vivado HLS でコンパイルできるように書き換えた。
次に、シミュレーション用のテストベンチを作製した。

Vivado HLSでCのシミュレーションを行ったところ、CPUが100%になったきり、なかなか終わらなかったので、DebugモードでCのシミュレーションを行った。
HLS_lap_fil_axim_2_131011.png

Debugモードでブレークポイントを入れながらCのシミュレーションを行うと、うまく行った。なぜだろうか?とっても大きな規模のシミュレーションだとは思うのだが。。。
(2013/10/13: フォルダをC:\Users\Masaaki\Documents\Vivado_HLS\lap_filter_aximに変更しました)
HLS_lap_fil_axim_1_131011.png

HLS_lap_fil_axim_3_131011.png

C Synthesis もやってみた。
min Latency が 1450801 クロック、クロックが10nsec だと14.5msec だ。
max Latency が 6932170801 クロックで 69.3sec だ。
かなり開きがある?これはなぜだろうか?
HLS_lap_fil_axim_4_131011.png

Interface を見ると、指定した ap_bus もあるが、ap_ctl_hs もある。AXI4 Masterバスだけでなく、アドレスや開始を設定する AXI4 Lite Slave バスも必要なのだが、ソレも作ってくれるのだろうか? pragma での指定が必要だろうか?
HLS_lap_fil_axim_5_131011.png

Export RTL で EDK用の pcore を作ってみたが、カメラ画像フレーム・バッファ読み込み用アドレスとラプラシアン・フィルタの結果を書き出すフレーム・バッファのアドレスは、parameter 指定の固定アドレスのようだ。これをAXI4 Lite Slave でAXI4 Master のRead/Writeするアドレスを渡す必要がある。次回修正を試みる。
HLS_lap_fil_axim_6_131011.png

現在の、Vivado HLS合成用の laplacian_filter.c とテストベンチの lap_filter_tb.c を貼っておく。
まずは、laplacian_filter.c から。(2013/11/30:修正)

// laplacian_filter.c
//

#include <stdio.h>
#include <string.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 laplacian_filter(volatile int *cam_fb, volatile int *lap_fb)
{
    #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];
    unsigned int lap_buf[HORIZONTAL_PIXEL_WIDTH];
    int x, y;
    int lap_fil_val;
    int a, b;
    int fl, sl, tl;
    
    // RGB値をY(輝度成分)のみに変換し、ラプラシアンフィルタを掛けた。
    for (y=0; y<VERTICAL_PIXEL_WIDTH; y++){
        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ライン分
                            memcpy(line_buf[a], (const int*)(cam_fb+a*HORIZONTAL_PIXEL_WIDTH), HORIZONTAL_PIXEL_WIDTH*sizeof(int));
                            for (b=0; b<HORIZONTAL_PIXEL_WIDTH; b++){ // ライン
                                line_buf[a][b] = conv_rgb2y(line_buf[a][b]);    // カラーから白黒へ
                            }
                        }
                    }
                } else { // 最初のラインではないので、1ラインだけ読み込む。すでに他の2ラインは読み込まれている
                    memcpy(line_buf[(y+1)%3], (const int*)(cam_fb+((y+1)*HORIZONTAL_PIXEL_WIDTH)), HORIZONTAL_PIXEL_WIDTH*sizeof(int));
                    for (b=0; b<HORIZONTAL_PIXEL_WIDTH; b++){ // ライン
                        line_buf[(y+1)%3][b] = conv_rgb2y(line_buf[(y+1)%3][b]);    // カラーから白黒へ
                    }
                }
                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番目のライン
                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同じ値を入れる
        }
        memcpy((const *)(lap_fb+(y*HORIZONTAL_PIXEL_WIDTH)), lap_buf, HORIZONTAL_PIXEL_WIDTH*sizeof(int));
    }
}

// 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)
{
    // return(abs(-x0y0 -x1y0 -x2y0 -x0y1 +8*x1y1 -x2y1 -x0y2 -x1y2 -x2y2)); // 元の実装
    y = -x0y0 -x1y0 -x2y0 -x0y1 +8*x1y1 -x2y1 -x0y2 -x1y2 -x2y2;
    if (y<0)
        y = 0;
    else if (y>255)
        y = 255;
    return(y);
}


次に、テストベンチの lap_filter_tb.c を貼る。(2013/11/30:修正)(2015/07/26:修正)

// Testbench of laplacian_filter.c
// M系列データをハードウェアとソフトウェアで、ラプラシアン・フィルタを掛けて、それを比較する
//

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

#define HORIZONTAL_PIXEL_WIDTH    40
#define VERTICAL_PIXEL_WIDTH    20
//#define HORIZONTAL_PIXEL_WIDTH    800
//#define VERTICAL_PIXEL_WIDTH    600
#define ALL_PIXEL_VALUE    (HORIZONTAL_PIXEL_WIDTH*VERTICAL_PIXEL_WIDTH)

int laplacian_fil_soft(int x0y0, int x1y0, int x2y0, int x0y1, int x1y1, int x2y1, int x0y2, int x1y2, int x2y2);
int conv_rgb2y_soft(int rgb);
int lap_filter_axim(int cam_addr, int lap_addr, volatile int *cam_fb, volatile int *lap_fb);    // hardware
void laplacian_filter_soft(volatile int *cam_fb, volatile int *lap_fb); // software

int mseq_po[ALL_PIXEL_VALUE];
int hw_lap_po[ALL_PIXEL_VALUE];
int sf_lap_po[ALL_PIXEL_VALUE];

int main()
{
//    int *mseq_po, *hw_lap_po, *sf_lap_po;
    int *s, *h;
    int x, y;
    unsigned int lfsr = 1;
    int cam_addr, lap_addr;
    
    // ピクセルデータ領域にM系列データを入力
    for (y=0, s=mseq_po; y<VERTICAL_PIXEL_WIDTH; y++){
        for (x=0; x<HORIZONTAL_PIXEL_WIDTH; x++){
            lfsr = (lfsr >> 1) ^ (-(lfsr & 1u) & 0xd0000001u); /* taps 32 31 29 1 */
            // ”線形帰還シフトレジスタ ”参照 : http://ja.wikipedia.org/wiki/%E7%B7%9A%E5%BD%A2%E5%B8%B0%E9%82%84%E3%82%B7%E3%83%95%E3%83%88%E3%83%AC%E3%82%B8%E3%82%B9%E3%82%BF
            *s = lfsr;
            s++;
        }
    }
    
    cam_addr = (int)mseq_po;
    lap_addr = (int)hw_lap_po;
    lap_filter_axim(cam_addr, lap_addr, (volatile int *)0, (volatile int *)0);    // ハードウェアのラプラシアン・フィルタ
    laplacian_filter_soft(mseq_po, sf_lap_po);    // ソフトウェアのラプラシアン・フィルタ
    
    // ハードウェアとソフトウェアのラプラシアン・フィルタの値のチェック
    for (y=0, h=hw_lap_po, s=sf_lap_po; y<VERTICAL_PIXEL_WIDTH; y++){
        for (x=0; x<HORIZONTAL_PIXEL_WIDTH; x++){
            if (*h != *s){
                printf("ERROR HW and SW results mismatch x = %d, y = %d, HW = %d, SW = %d\n", x, y, *h, *s);
                return(1);
            } else {
                h++;
                s++;
            }
        }
    }
    printf("Success HW and SW results match\n");
}

void laplacian_filter_soft(volatile int *cam_fb, volatile int *lap_fb)
{
    unsigned int line_buf[3][HORIZONTAL_PIXEL_WIDTH];
    unsigned int lap_buf[HORIZONTAL_PIXEL_WIDTH];
    int x, y;
    int lap_fil_val;
    int a, b;
    int fl, sl, tl;

    // RGB値をY(輝度成分)のみに変換し、ラプラシアンフィルタを掛けた。
    for (y=0; y<VERTICAL_PIXEL_WIDTH; 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番目のライン
        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ライン分
                            memcpy(&line_buf[a][0], (const int*)&cam_fb[a*(HORIZONTAL_PIXEL_WIDTH)], HORIZONTAL_PIXEL_WIDTH*sizeof(int));
                            for (b=0; b<HORIZONTAL_PIXEL_WIDTH; b++){ // ライン
                                line_buf[a][b] = conv_rgb2y(line_buf[a][b]);    // カラーから白黒へ
                            }
                        }
                    } else { // 最初のラインではないので、1ラインだけ読み込む。すでに他の2ラインは読み込まれている
                         memcpy(line_buf[(y+1)%3], (const int*)&cam_fb[(y+1)*(HORIZONTAL_PIXEL_WIDTH)], HORIZONTAL_PIXEL_WIDTH*sizeof(int));
                        for (b=0; b<HORIZONTAL_PIXEL_WIDTH; b++){ // ライン
                            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同じ値を入れる
        }
        memcpy(&lap_fb[y*(HORIZONTAL_PIXEL_WIDTH)], (const int*)lap_buf, HORIZONTAL_PIXEL_WIDTH*sizeof(int));
    }
}

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

  1. 2013年10月11日 04:48 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

コメント

コメントの投稿


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

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