FC2カウンター FPGAの部屋 Vivado HLS 2014.4 を使ったAXI4 Master版のラプラシアンフィルタ

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

FPGAの部屋

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

Vivado HLS 2014.4 を使ったAXI4 Master版のラプラシアンフィルタ

Vivado HLS 2014.4 で合成したラプラシアンフィルタIPの高速化15(性能が最大になる設定を探る8、追加1)”で、tu1978さんとおるさんの協力(というか、コードを書いてもらちゃいましたが)の元に実現した最速のAXI4 Master版ラプラシアンフィルタだが、なかなかシミュレーションすることができなかった。

今回は、”Vivado HLSの INTERFACE m_axi ディレクティブのオプション”でわかったことを使って、CシミュレーションもC/RTL協調シミュレーションもできるようにした。
C++ソースコードは多少修正しているのだが、filter_line()関数をコールしていると、どうしてもC/RTL協調シミュレーションでハードウェアの値が 0 になってしまうという事態が発生してしまった。Cシミュレーションはソフトウェアとハードウェアの値が同一と言っているのだが、C/RTL協調シミュレーションには違うと言われてしまう。
解決方法としては、filter_line()関数の中身をトップのlap_filter_axim の中に統合した。こうするとC/RTL協調シミュレーション時にもハードウェアとソフトウェアの値は同一となる。
この状態でCからHDLへ合成すると、タイミング・エラーになってしまった。なので、PIPELINEディレクティブの位置を少し移動した。
次に、”Vivado HLSの INTERFACE m_axi ディレクティブのオプション”での Vivado HLS の AXI4 Master インターフェース生成用のディレクティブ INTERFACE m_axi ディレクティブの offset=slave も使用した。

テストベンチでラプラシアンフィルタに入力するデータは A という文字でそれがラプラシアンフィルタを通ってエッジになればOKというものだ。下に概要を示す。これは、Vivado HLS勉強会資料4(AXI Master編)の一部だ。
HLS_AXI4M_slave_1_150827.png

Vivado HLS 2014.4 の画面を示す。
HLS_AXI4M_slave_2_150827.png

Cシミュレーション結果を示す。
HLS_AXI4M_slave_5_150827.png

CからHDLへの合成を行った。結果を示す。
HLS_AXI4M_slave_3_150827.png

HLS_AXI4M_slave_4_150827.png

C/RTL Co-Simulation を行った。Dump Trace を all にして実行した。成功した。
HLS_AXI4M_slave_6_150827.png

VivadoでC/RTL Co-Simulation を見た。64 ピクセル x 48 行のラプラシアンフィルタ処理は約 112 us で終了した。
HLS_AXI4M_slave_7_150827.png

1行Read する間隔は 2.33 us だった。ARLEN, AWLEN の値は0x3f なので、64 バースト転送となる。
HLS_AXI4M_slave_8_150827.png

AXI4 Lite Slaveのアドレスマップを示す。

// AXILiteS
// 0x00 : Control signals
//        bit 0  - ap_start (Read/Write/COH)
//        bit 1  - ap_done (Read/COR)
//        bit 2  - ap_idle (Read)
//        bit 3  - ap_ready (Read)
//        bit 7  - auto_restart (Read/Write)
//        others - reserved
// 0x04 : Global Interrupt Enable Register
//        bit 0  - Global Interrupt Enable (Read/Write)
//        others - reserved
// 0x08 : IP Interrupt Enable Register (Read/Write)
//        bit 0  - Channel 0 (ap_done)
//        bit 1  - Channel 1 (ap_ready)
//        others - reserved
// 0x0c : IP Interrupt Status Register (Read/TOW)
//        bit 0  - Channel 0 (ap_done)
//        bit 1  - Channel 1 (ap_ready)
//        others - reserved
// 0x10 : Data signal of ap_return
//        bit 31~0 - ap_return[31:0] (Read)
// 0x18 : Data signal of cam_fb_offset
//        bit 31~0 - cam_fb_offset[31:0] (Read/Write)
// 0x1c : reserved
// 0x20 : Data signal of lap_fb_offset
//        bit 31~0 - lap_fb_offset[31:0] (Read/Write)
// 0x24 : reserved
// (SC = Self Clear, COR = Clear on Read, TOW = Toggle on Write, COH = Clear on Handshake)


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

// laplacian_filter3.c
// m_axi offset=slave version
// 2015/08/26
//

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

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

int lap_filter_axim(volatile int *cam_fb, volatile int *lap_fb)
{
    #pragma HLS INTERFACE s_axilite port=return

#pragma HLS INTERFACE m_axi depth=3072 port=cam_fb offset=slave bundle=cam_fb
#pragma HLS INTERFACE m_axi depth=3072 port=lap_fb offset=slave bundle=lap_fb

    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

    int lap_buf[HORIZONTAL_PIXEL_WIDTH];
    int x, y;
    int lap_fil_val;
    int a, b;
    int fl, sl, tl;
    int line_sel;
    int 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


    // RGB値をY(輝度成分)のみに変換し、ラプラシアンフィルタを掛けた。
    for (y=0, 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){
#ifndef __SYNTHESIS__
            printf("copy 3 lines\n");
#endif
            for (a=0; a<3; a++){
 // 3ライン分
                memcpy(line_buf[a], (const int*)&cam_fb[a*(HORIZONTAL_PIXEL_WIDTH)], HORIZONTAL_PIXEL_WIDTH*sizeof(int));
            }
        }else// 最初のラインではないので、1ラインだけ読み込む。すでに他の2ラインは読み込まれている
            memcpy(line_buf[tl], (const int*)&cam_fb[(y+1)*(HORIZONTAL_PIXEL_WIDTH)], HORIZONTAL_PIXEL_WIDTH*sizeof(int));
        }
        if (y==0 || y==VERTICAL_PIXEL_WIDTH-1){
            for(b=0; b<HORIZONTAL_PIXEL_WIDTH; b++){
                lap_buf[b] = 0;
            }
        } else {
            next[0] = conv_rgb2y(line_buf[fl][0]);
            next[1] = conv_rgb2y(line_buf[sl][0]);
            next[2] = conv_rgb2y(line_buf[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(line_buf[fl][1]);

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

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

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

                    prev[2] = current[2];
                    current[2] = next[2];
                    next[2] = conv_rgb2y(line_buf[tl][x+1]);
#pragma HLS pipeline
                    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同じ値を入れる
            }
        }
#ifndef __SYNTHESIS__
        printf("write back:%d\n", y);
#endif
        memcpy((int*)&lap_fb[y*(HORIZONTAL_PIXEL_WIDTH)], (const int*)lap_buf, HORIZONTAL_PIXEL_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 : line[sl]oat を止めて、すべて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月27日 06:39 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

コメント

コメントの投稿


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

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