FC2カウンター FPGAの部屋 SDSoC 2015.2 でハードウェアとソフトウェアのラプラシアンフィルタの性能を比較した7(ハードウェア化4)

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

FPGAの部屋

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

SDSoC 2015.2 でハードウェアとソフトウェアのラプラシアンフィルタの性能を比較した7(ハードウェア化4)

SDSoC 2015.2 でハードウェアとソフトウェアのラプラシアンフィルタの性能を比較した6(ハードウェア化3)”の続き。

前回は、ハードウェア化する関数をAXI4 Master ポート入出力にする方法を発見して、ラプラシアンフィルタのハードウェア、ソフトウェアで実装した時の性能を確認した。その結果は、約4.5倍、ハードウェアの方が遅くなってしまった。

今回はハードウェア化する関数 lap_filter_axim() の書き方をVivado HLS でやったように memcpy() で書き換えてどのくらいハードウェアが速くなるのかやってみた。

前回と同じワークスペースに lap_filter1 プロジェクトを作製した。
SDSoC_110_150810.png

bmp_header.h, lap_filter_tb.c をコピーした。

laplacian_filter1.c を作ってコピーした。

初めに、バグ取りのために、すべてソフトウェアでビルドしてみた。実機のZYBOに入れてテストした時のTera Termを示す。
SDSoC_106_150810.png
ソフトウェアとしても、今回のラプラシアンフィルタの方が速い。ハードウェア用だが、ソフトウェアでのラプラシアンフィルタ処理時間は 254 us, ソフトウェアでのラプラシアンフィルタ処理時間は 302 us だった。ハードウェア用のラプラシアンフィルタ処理時間はソフトウェアのラプラシアンフィルタ処理時間の 254 us / 302 us ≒ 0.841 倍になった。よって、性能は 1.19 倍になった。

次に、lap_filter_axim() 関数をハードウェアとして登録してビルドした。実機のZYBOに入れてテストした時のTera Termを示す。
SDSoC_107_150810.png
ハードウェアでのラプラシアンフィルタ処理時間は 410 us, ソフトウェアでのラプラシアンフィルタ処理時間は 299 us だった。ハードウェア用のラプラシアンフィルタ処理時間はソフトウェアのラプラシアンフィルタ処理時間の 410 us / 299 us ≒ 1.37 倍になった。よって、性能は 0.72 倍になった。まだハードウェアの方が遅いが性能差はずいぶんと縮まった。
ハードウェア同士の性能を比較してみよう。前回のハードウェアのラプラシアンフィルタ処理時間は 1.35 ms なので、1350 us / 410 us ≒ 3.29 倍速くなった。但し、今回の lap_filter_axim() は、memcpy() と一部 PIPELINE ディレクティブを使用している。
D:\SDSoC\Examples\ZYBO\lap_filter1\SDRelease\_sds\p0\ipi の Vivado のプロジェクトを開いて見てみた。
Summary を見ると、前回よりもBRAM リソースが増えている。
SDSoC_109_150810.png

ブロックデザインは相変わらず、Vivado HLSのAXI4 Master AXI4 Interconnect を通って、PSのAXI ACPポートに入っているが見えす。
SDSoC_108_150810.png

最後に laplacian_filter1.c を貼っておく。

// laplacian_filter1.c
// lap_filter_axim()

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

#define HORIZONTAL_PIXEL_WIDTH    64
#define VERTICAL_PIXEL_WIDTH    48
//#define 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);

//int lap_filter_axim(int cam_fb[ALL_PIXEL_VALUE], int lap_fb[ALL_PIXEL_VALUE], int width, int height)
int lap_filter_axim(int *cam_fb, int *lap_fb, int width, int height)
{
#pragma HLS INTERFACE m_axi port=cam_fb depth=800 offset=slave
#pragma HLS INTERFACE m_axi port=lap_fb depth=800 offset=slave
#pragma HLS INTERFACE s_axilite port=return

    unsigned int line_buf[3][ALL_PIXEL_VALUE];
    unsigned int lap_buf[ALL_PIXEL_VALUE];
    int x, y, i;
    int lap_fil_val;
    int a, b;
    int fl, sl, tl;
    int line_sel;

    // RGB値をY(輝度成分)のみに変換し、ラプラシアンフィルタを掛けた。
    for (y=0, line_sel=0; y<height; 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;
        }
        for (x=0; x<width; x++){
            if (y==0 || y==height-1){ // 縦の境界の時の値は0とする
                lap_fil_val = 0;
            }else if (x==0 || x==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*(width)]), width*sizeof(int));
                            for (b=0; b<width; b++){
#pragma HLS PIPELINE
 // ライン
                                line_buf[a][b] = conv_rgb2y(line_buf[a][b]);    // カラーから白黒へ
                            }
                        }
                    } else { // 最初のラインではないので、1ラインだけ読み込む。すでに他の2ラインは読み込まれている
                         memcpy(line_buf[tl], (const int*)(&cam_fb[(y+1)*(width)]), width*sizeof(int));
                        for (b=0; b<width; b++){
#pragma HLS PIPELINE
 // ライン
                            line_buf[tl][b] = conv_rgb2y(line_buf[tl][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*width]), (const int*)(&lap_buf[0]), width*sizeof(int));

        line_sel++;
        if (line_sel > 3){
            line_sel = 1;
        }
    }
    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);
}


  1. 2015年08月11日 04:34 |
  2. SDSoC
  3. | トラックバック:0
  4. | コメント:3

コメント

こんにちは。
個人的には「銀の弾丸などない」と思うのですが、世間的にはSDSoCに対して何かとても過剰な期待が寄せられているように感じます。まあ、この手のツールは上手く使えば「多目的汎用弾」ぐらいにはなると思うのですが、残念ながら「超高速徹甲弾」には絶対にならないかと。でも、Xilinx自体が「RTLを記述できるエンジニアの1000倍くらい、C/C++のエンジニアが居る」とか言ってますからね~。「超音速戦闘機を操縦できるパイロットの1000倍くらい、歩兵科の兵隊が居る」と言っているのと同じかと^^;)。
  1. 2015/08/12(水) 17:40:48 |
  2. URL |
  3. くり #195Lvy4Y
  4. [ 編集 ]

こんにちは。

くりさんお久しぶりです。
上手いこと言いますね。。。
確かにそうとも思いますが、「超高速徹甲弾」の部分は自分でか、既存のIPを使えば良いのではないでしょうか?そして、ARMからの制御はSDSoCで。。。それが必要無い場合はそもそもSDSoCが必要なくてVivadoだけで。

ともかく、ハード化、ドライバ作製、アプリも作製してくれるのは便利ですね。速くなるソースの書き換え方を教えてくれるとか?そのうち進歩して行くのを待てば良いのじゃないかな?と思います。

今のところ、ハードエンジニアというか?FPGAの内部構造をわかっている人にはチャンスなんじゃないでしょうか?処理をスピードアップするストラテジーを描けますからね。。。そして、楽にハードウェア・オフロードできますよ。

私ももう爺さんなので、HDLを集中して書くのが難しくなりました。できるだけVivado HLSで書こうと思っています。その勉強をしていたので、成果をまとめて大学内でVivado HLSの勉強会をしようと思っています。若い人にもその便利さを伝えていきたいですね。そして、性能向上の秘訣(今のところのですが。。。)も。
  1. 2015/08/12(水) 21:21:35 |
  2. URL |
  3. marsee #f1oWVgn2
  4. [ 編集 ]

こんばんわ。
昔からやっている人にとっては、便利なツールがまた1つ増えた、これで手抜きができる部分は楽に手抜きをして徹甲弾の部分に集中できる…、といった感じですが、メーカー自身が「これでただの歩兵がステルス戦闘機のパイロットになれる!」的な幻想を振り撒いているのを見ると…。「過ってHDLの普及期に似たようなこと言ってませんでした?」と思います。まあ「歴史は繰り返す」とよく言われますが^^)。
  1. 2015/08/13(木) 00:03:16 |
  2. URL |
  3. くり #195Lvy4Y
  4. [ 編集 ]

コメントの投稿


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

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