FC2カウンター FPGAの部屋 SDx 2016.3 のプラグマによるハードウェアと性能の違い4

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

FPGAの部屋

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

SDx 2016.3 のプラグマによるハードウェアと性能の違い4

SDx 2016.3 のプラグマによるハードウェアと性能の違い3”の続き。

前回は、SDS data access_pattern にRANDOM を指定したときと、以前 SDS data zero_copy を指定したら sds_alloc() を使用する必要があるとのことなので、やってみた。今回は、ハードウェア化する関数 lap_filter_axim() の書き方をVivado HLS でやったように memcpy() で書き換えてどのくらいハードウェアが速くなるのかやってみた。

この記事は、”SDSoC 2015.2 でハードウェアとソフトウェアのラプラシアンフィルタの性能を比較した7(ハードウェア化4)”の焼き直しなのだが、SDSoC の新しいバージョンでやってみることと、画像の大きさが大きくなっているという違いがある。さらにVivado HLS の結果を観察してみた。

まずは、lap_filter2.c を”SDSoC 2015.2 でハードウェアとソフトウェアのラプラシアンフィルタの性能を比較した7(ハードウェア化4)”のmemcpy() を使用したバージョンに変更した。ただ単にコピー&ペーストしただけではなく、扱っている画像が違うので、そのあたりを修正しながらコピー&ペーストした。
SDx_v2016_3_95_170106.png

SDRelease でビルド後に生成された workspace\lap_filter2\SDRelease\_sds\p0\ipi のVivado 2016.3 プロジェクトを開いて結果を見た。
SDx_v2016_3_96_170106.png

BRAM の使用率が高く 68 % になっている。

ブロックデザインを示す。この辺りは変更がない。
SDx_v2016_3_97_170106.png

workspace\lap_filter2\SDRelease\_sds\vhls\lap_filter_axim にVivado HLS のプロジェクトがあるのでVivado HLS 2016.3 を起動して観察した。
SDx_v2016_3_98_170106.png

その結果、Source には lap_filter2.c がそのまま入っていた。これなら、Vivado HLS のプラグマ(指示子)が使える。

Synthesis Report を示す。
SDx_v2016_3_99_170106.png

レイテンシは?ばかりでTRIPCOUNT 指示子を指定する必要があるようだ。
BRAM を見ると、65 % を使用していて、Vivado HLS で全体で使用しているBRAM のほとんどを使用しているのがわかる。

次に、実際にZYBO で確かめてみよう。
workspace\lap_filter2\SDRelease\sd_card の内容をMicro SD カードにコピーした。
ZYBO に挿入して電源ONした。
Linux が立ち上がった。
cd /mnt./lap_filter2.elf を実行した。
SDx_v2016_3_100_170106.png

その結果、ソフトウェアの実行時間は最初は943 us で少し時間がかかったがキャッシュがフィルされた2回目からは速くなっている。2回目からの5回の平均は 824 us だった。
ハードウェアの実行時間はソフトウェアの実行時間との実行時間の差がほとんどなくなった。2回目からの5回の平均は 942 us だった。
ハードウェアの実行時間/ソフトウェアの実行時間 = 約 1.14 倍、つまり、ハードウェアの性能はソフトウェアの約 0.87 倍ということになる。だいぶ差が縮まった。


さて、今のlap_filter2.c には、PIPELINE指示子が付加されているが、これを取ってしまうとどのくらいの性能低下があるかをやってみた。
PIPELINE指示子をコメントアウトした。
SDx_v2016_3_101_170106.png

リソース使用量に違いは無いみたいだ。パーセンテージでなく、絶対数で比べれば違いがあると思う。

Vivado HLS のSynthesis Report を示す。
SDx_v2016_3_103_170106.png

Loop 1.1.1.2、Loop1.1.3 のPipelined が no になっているのがわかる。

workspace\lap_filter2\SDRelease\sd_card の内容をMicro SD カードにコピーした。
ZYBO に挿入して電源ONした。
Linux が立ち上がった。
cd /mnt./lap_filter2.elf を実行した。
SDx_v2016_3_104_170106.png

2回目からの5回のハードウェアの実行時間の平均は 1091 us だった。PIPELINE 指示子を入れたときの平均が 942 us なので、約 1.16 倍になった。

memcpy() を使用するために変更した lap_filter2.c を示す。

// laplacian_filter2.c
// lap_filter_axim()

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

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

#pragma SDS data zero_copy(cam_fb)
#pragma SDS data zero_copy(lap_fb)
//#pragma SDS data access_pattern(cam_fb:RANDOM, lap_fb:RANDOM)
//#pragma SDS data access_pattern(cam_fb:SEQUENTIAL, lap_fb:SEQUENTIAL)
//#pragma SDS data sys_port(cam_fb:AFI, lap_fb:AFI)

int lap_filter_axim(int cam_fb[ALL_PIXEL_VALUE], int lap_fb[ALL_PIXEL_VALUE], int width, int height)
{
    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. 2017年01月07日 06:44 |
  2. SDSoC
  3. | トラックバック:0
  4. | コメント:0

コメント

コメントの投稿


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

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