FC2カウンター FPGAの部屋 Vivado HLS 2014.4 でHLSストリームを使用してビデオ信号を入出力する

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

FPGAの部屋

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

Vivado HLS 2014.4 でHLSストリームを使用してビデオ信号を入出力する

ZYBOのHDMI入力画像にラプラシアンフィルタ処理を行う2(高位合成)”で lap_filter_rgb.cpp を作ったが、このファイルのシミュレーションをどうやってやるのか考えていた。
lap_filter_rgb.cpp には、HSYNCやVSYNC、VDEやpDATAなどのスティミュラスを与える必要がある。与えるためにはテストベンチに制御を残しておく必要があるが、lap_filter_rgb.cpp をコールしてからどうやって、それらのスティミュラスを作ろうか?スレッド立てるのも方法だろうし、スティミュラスをすべて配列に保存しておいて、lap_filter_rgb.cpp に読んでもらっても良いかも?

調べてみるとVivado HLS にHLSストリームというライブラリがあって、ストリームとして扱えるようになっている。この例はAXI4 Streamで使っている。今回は、独自のtemplate を作って、ビデオ信号の入出力を試みることにした。なお、HLSストリームライブラリについては、”Vivado Design Suite ユーザー ガイド 高位合成 UG902 (v2014.3) 2014 年 10 月 1 日”の 221 ページのHLSストリームライブラリを参照下さい。

最初に、lap_filter_rgb.h に書いた template を紹介するために、lap_filter_rgb.h を貼っておく。
(2015/08/26:修正)

// lap_filter_rgb.h
// 2015/08/22

//#define HORIZONTAL_PIXEL_WIDTH    800
//#define VERTICAL_PIXEL_WIDTH    600

#ifndef __LAP_FILTER_RGB__
#define __LAP_FILTER_RGB__

#include "ap_int.h"

#define HORIZONTAL_PIXEL_WIDTH    64
#define VERTICAL_PIXEL_WIDTH    48

#define ALL_PIXEL_VALUE    (HORIZONTAL_PIXEL_WIDTH*VERTICAL_PIXEL_WIDTH)

template<int Data>
    struct vid_in{
        ap_uint<Data>    rgb_pData;
        ap_uint<1>        rgb_pHSync;
        ap_uint<1>        rgb_pVSync;
        ap_uint<1>        rgb_pVDE;
    };

template<int Data>
    struct RGB{
        ap_uint<Data>    vid_pData;
        ap_uint<1>        vid_pHSync;
        ap_uint<1>        vid_pVSync;
        ap_uint<1>        vid_pVDE;
};

#endif


vid_in と RGB の2つのtemplate を定義している。これは、Vivado のブロックデザインの信号そのままだ。
dvi2lap2vga_1_150821.png

これに合わせてHLSストリームで lap_filter_rgb.cpp を書きなおした。
Vivado HLS 2014.4 で高位合成を行ったところエラーになった。
dvi2lap2vga_5_150823.png

エラー内容を示す。

@E [XFORM-801] Stream port 'video_in.V.rgb_pData.V' (lap_filter_rgb_14_4/lap_filter_rgb.cpp:16) has invalid interface mode 'ap_none' (lap_filter_rgb_14_4/lap_filter_rgb.cpp:19:1). Stream port only supports ap_hs, ap_fifo and axis modes.
@E [XFORM-801] Stream port 'video_in.V.rgb_pHSync.V' (lap_filter_rgb_14_4/lap_filter_rgb.cpp:16) has invalid interface mode 'ap_none' (lap_filter_rgb_14_4/lap_filter_rgb.cpp:19:1). Stream port only supports ap_hs, ap_fifo and axis modes.
@E [XFORM-801] Stream port 'video_in.V.rgb_pVSync.V' (lap_filter_rgb_14_4/lap_filter_rgb.cpp:16) has invalid interface mode 'ap_none' (lap_filter_rgb_14_4/lap_filter_rgb.cpp:19:1). Stream port only supports ap_hs, ap_fifo and axis modes.
@E [XFORM-801] Stream port 'video_in.V.rgb_pVDE.V' (lap_filter_rgb_14_4/lap_filter_rgb.cpp:16) has invalid interface mode 'ap_none' (lap_filter_rgb_14_4/lap_filter_rgb.cpp:19:1). Stream port only supports ap_hs, ap_fifo and axis modes.
@E [XFORM-801] Stream port 'video_out.V.vid_pData.V' (lap_filter_rgb_14_4/lap_filter_rgb.cpp:16) has invalid interface mode 'ap_none' (lap_filter_rgb_14_4/lap_filter_rgb.cpp:18:1). Stream port only supports ap_hs, ap_fifo and axis modes.
@E [XFORM-801] Stream port 'video_out.V.vid_pHSync.V' (lap_filter_rgb_14_4/lap_filter_rgb.cpp:16) has invalid interface mode 'ap_none' (lap_filter_rgb_14_4/lap_filter_rgb.cpp:18:1). Stream port only supports ap_hs, ap_fifo and axis modes.
@E [XFORM-801] Stream port 'video_out.V.vid_pVSync.V' (lap_filter_rgb_14_4/lap_filter_rgb.cpp:16) has invalid interface mode 'ap_none' (lap_filter_rgb_14_4/lap_filter_rgb.cpp:18:1). Stream port only supports ap_hs, ap_fifo and axis modes.
@E [XFORM-801] Stream port 'video_out.V.vid_pVDE.V' (lap_filter_rgb_14_4/lap_filter_rgb.cpp:16) has invalid interface mode 'ap_none' (lap_filter_rgb_14_4/lap_filter_rgb.cpp:18:1). Stream port only supports ap_hs, ap_fifo and axis modes.
@E [HLS-70] Synthesizability check failed.


HLSストリームは、ap_none じゃダメで、ap_hs, ap_fifo, axis のどれかにしろとのことだ。ap_none が入出力数も少ないし、ノンブロッキングなんで良いと思ったのだが。。。HLSストリームを ap_hs に変更して、高位合成を行った。
今度は高位合成が成功した。
dvi2lap2vga_6_150824.png

dvi2lap2vga_7_150824.png

生成された Verilog HDL ファイルを見ると、ストリームの入出力の信号すべてに ap_vld と ap_ack が付いていた。
dvi2lap2vga_8_150824.png

ap_vld には常に 1 を、ap_ack は無視すればビデオ信号の入出力ができると思うが、ブロックデザインでつなぐのが面倒だ。どうしようか?考え中。

lap_filter_rgb.cpp を貼っておく。なお、間違っているかもしれない。まだシミュレーションしていないし。
(2015/08/26:修正) (2015/08/28:修正)

//
// lap_filter_rgb.cpp
// 2015/08/21 by marsee
//

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

#include "lap_filter_rgb.h"

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 lap_filter_rgb(ap_uint<1> lap_fil_enable, hls::stream<vid_in<24> >& video_in, hls::stream<RGB<24> >& video_out){
#pragma HLS INTERFACE ap_ctrl_hs port=return
#pragma HLS INTERFACE ap_hs register port=video_out
#pragma HLS INTERFACE ap_hs register port=video_in
#pragma HLS INTERFACE ap_none register port=lap_fil_enable

    enum  {idle, vsync_assert, v_back_porch, h_video, h_front_porch, hsync_assert, h_back_porch, v_front_porch, vsync_assert2};
    int cstate = idle;
    int cstateb = idle;

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

    int pix_mat[3][3];
#pragma HLS array_partition variable=pix_mat complete

    int lap_fil_val;
    int x=0, y=0;
    int y_val;
    int val;
    int first_h_video;

    vid_in<24> pix;
    RGB<24> lap;

    while(cstate != vsync_assert2){
#pragma HLS PIPELINE rewind
        if(!(cstate==h_video && (cstateb==v_back_porch || cstateb==h_back_porch))){ // v_back_porch と h_back_porch から h_videoになった時はストリームをReadしない
            video_in >> pix;
            first_h_video = 0;
        } else
            first_h_video = 1;
        cstateb = cstate;
        switch (cstate){
        case idle :
            if (pix.rgb_pVSync)
                cstate = vsync_assert;
            x = 0; y = 0;
            lap_fil_val = pix.rgb_pData;
            break;
        case vsync_assert :
            if (!pix.rgb_pVSync)
                cstate = v_back_porch;
            lap_fil_val = pix.rgb_pData;
            break;
        case v_back_porch :
            if (pix.rgb_pVDE){
                cstate = h_video;
                lap_fil_val = 0;
            } else
                lap_fil_val = pix.rgb_pData;
            break;
        case h_video :
            if (!pix.rgb_pVDE){
                cstate = h_front_porch;
                lap_fil_val = pix.rgb_pData;
            } else {
                for (int k=0; k<3; k++){
                    for (int m=0; m<2; m++){
#pragma HLS UNROLL
                        pix_mat[k][m] = pix_mat[k][m+1];
                    }
                }
                pix_mat[0][2] = line_buf[0][x];
                pix_mat[1][2] = line_buf[1][x];

                y_val = conv_rgb2y((unsigned int)pix.rgb_pData);
                pix_mat[2][2] = y_val;

                line_buf[0][x] = line_buf[1][x];    // 行の入れ替え
                line_buf[1][x] = y_val;

                lap_fil_val = laplacian_fil(    pix_mat[0][0], pix_mat[0][1], pix_mat[0][2],
                                            pix_mat[1][0], pix_mat[1][1], pix_mat[1][2],
                                            pix_mat[2][0], pix_mat[2][1], pix_mat[2][2]);
                lap_fil_val = (lap_fil_val<<16)+(lap_fil_val<<8)+lap_fil_val; // RGB蜷後§蛟、繧貞?・繧後k

                if (x<2 || y<2// 最初の2行とその他の行の最初の2列は無効データなので0とする
                    lap_fil_val = 0;
                x++;
                if (first_h_video)
                    continue;
            }
            break;
        case h_front_porch:
            if (pix.rgb_pHSync)
                cstate = hsync_assert;
            lap_fil_val = pix.rgb_pData;
            break;
        case hsync_assert :
            if (!pix.rgb_pHSync)
                cstate = h_back_porch;
            lap_fil_val = pix.rgb_pData;
            break;
        case h_back_porch :
            if (pix.rgb_pHSync) // rgb_pVDE が来ないうちに rgb_pHSync が来たので表示期間終了
                cstate = v_front_porch;
            else if (pix.rgb_pVDE){
                cstate = h_video;
                y++;
                x = 0;
                lap_fil_val = 0;
            } else
                lap_fil_val = pix.rgb_pData;
            break;
        case v_front_porch :
            if (pix.rgb_pVSync)
                cstate = vsync_assert2;
            break;
        }
        if (cstate == h_video){
            if (lap_fil_enable)
                lap.vid_pData = (ap_uint<24>)lap_fil_val;
            else
                lap.vid_pData = pix.rgb_pData;
        }else
            lap.vid_pData = pix.rgb_pData;
        lap.vid_pHSync = pix.rgb_pHSync;
        lap.vid_pVDE = pix.rgb_pVDE;
        lap.vid_pVSync = pix.rgb_pVSync;

        video_out << lap;
    }
}

// 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月24日 05:28 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

コメント

コメントの投稿


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

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