FC2カウンター FPGAの部屋 2019年01月03日
FC2ブログ

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

FPGAの部屋

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

垂直方向が反転している画像のDMA(vflip_dma_write)

垂直方向、水平方向が反転している画像のDMA(vhflip_dma_write)”の続き。

前回は、垂直方向はフリップし、水平方向をミラーありにして、アドレスが減りながらDMAを行う方式をVivado HLS で実装して、検証した。その結果、性能が出ないことがわかった。今回は、垂直方向はフリップしているが、水平方向はミラーなしの場合のDMA を実装して性能を確認しよう。

まずは、ソースを貼っておく。
vflip_dma_write.h から貼っておく。

// vflip_dma_write.h
// 2019/01/01 by marsee
//

#ifndef __VFLIP_DMA_WRITE_H__
#define __VFLIP_DMA_WRITE_H__

#include "ap_axi_sdata.h"
#include "hls_video.h"

#define MAX_WIDTH 800
#define MAX_HEIGHT 600

typedef hls::stream<ap_axiu<32,1,1,1> > AXI_STREAM;
typedef ap_axiu<32,1,1,1> AP_AXIU32;
typedef hls::Scalar<3, unsigned char> RGB_PIXEL;
typedef hls::Mat<MAX_HEIGHT, MAX_WIDTH, HLS_8UC3> RGB_IMAGE;
typedef hls::Mat<MAX_HEIGHT, MAX_WIDTH, HLS_8UC1> GRAY_IMAGE;

#endif


vflip_dma_write.cpp を貼っておく。

// vflip_dma_write.cpp
// 2019/01/01 by marsee
//

#include <ap_int.h>
#include <hls_stream.h>
#include <ap_axi_sdata.h>

#include "vflip_dma_write.h"

int vflip_dma_write(AXI_STREAM & ins,
    volatile ap_int<32> *fb0, volatile ap_int<32> *fb1, volatile ap_int<32> *fb2,
    volatile ap_uint<2> &active_frame){
#pragma HLS INTERFACE s_axilite port=active_frame
#pragma HLS INTERFACE m_axi depth=480000 port=fb0 offset=slave
#pragma HLS INTERFACE m_axi depth=480000 port=fb1 offset=slave
#pragma HLS INTERFACE m_axi depth=480000 port=fb2 offset=slave
#pragma HLS INTERFACE axis register both port=ins
#pragma HLS INTERFACE s_axilite port=return

    AP_AXIU32 pix;
    int max_fb_chk;

    active_frame = 0;

    LOOP_WAIT0: do { // user が 1になった時にフレームがスタートする
#pragma HLS LOOP_TRIPCOUNT min=1 max=1 avg=1
        ins >> pix;
    } while(pix.user == 0);

    LOOP_Y0: for (int y=MAX_HEIGHT-1; y>=0; y--){ // vflip
        LOOP_X0: for (int x=0; x<MAX_WIDTH; x++){
#pragma HLS PIPELINE II=1
            if (!(x==0 && y==0))    // 最初の入力はすでに入力されている
                ins >> pix;    // AXI4-Stream からの入力

            fb0[(y*MAX_WIDTH)+x] = pix.data;
        }
    }

    active_frame = 1;
    LOOP_WAIT1: do { // user が 1になった時にフレームがスタートする
#pragma HLS LOOP_TRIPCOUNT min=1 max=1 avg=1
        ins >> pix;
    } while(pix.user == 0);

    LOOP_Y1: for (int y=MAX_HEIGHT-1; y>=0; y--){ // vflip
        LOOP_X1: for (int x=0; x<MAX_WIDTH; x++){
#pragma HLS PIPELINE II=1
            if (!(x==0 && y==0))    // 最初の入力はすでに入力されている
                ins >> pix;    // AXI4-Stream からの入力

            fb1[(y*MAX_WIDTH)+x] = pix.data;

        }
    }

    active_frame = 2;
    LOOP_WAIT2: do { // user が 1になった時にフレームがスタートする
#pragma HLS LOOP_TRIPCOUNT min=1 max=1 avg=1
        ins >> pix;
    } while(pix.user == 0);

    LOOP_Y2: for (int y=MAX_HEIGHT-1; y>=0; y--){ // vflip
        LOOP_X2: for (int x=0; x<MAX_WIDTH; x++){
#pragma HLS PIPELINE II=1
            if (!(x==0 && y==0))    // 最初の入力はすでに入力されている
                ins >> pix;    // AXI4-Stream からの入力

            fb2[(y*MAX_WIDTH)+x] = pix.data;
        }
    }
end:
    return(0);
}


vflip_dma_write_tb_tb.cpp を貼っておく。

// vflip_dma_write_tb_tb.cpp
// 2019/01/01 by marsee
//

#include <ap_int.h>
#include <hls_stream.h>
#include <iostream>
#include <fstream>
#include "hls_opencv.h"

#include "vflip_dma_write.h"

int vflip_dma_write(AXI_STREAM & ins,
    volatile ap_int<32> *fb0, volatile ap_int<32> *fb1, volatile ap_int<32> *fb2,
    volatile ap_uint<2> &active_frame);

#define NUM_FRAME_BUFFER 3

int main()
{
    using namespace cv;

    AXI_STREAM ins;
    AP_AXIU32 pix;

    ap_uint<2> active_frame;
    int *frame_buffer;

    // OpenCV で 画像を読み込む
    Mat src = imread("bmp_file0_vflip.bmp");
    AXI_STREAM src_axi, dst_axi;

    // Mat フォーマットから AXI4 Stream へ変換、3画面分
    for(int i=0; i<NUM_FRAME_BUFFER; i++){
        cvMat2AXIvideo(src, src_axi);
        for (int y=0; y<MAX_HEIGHT; y++){
            for (int x=0; x<MAX_WIDTH; x++){
                src_axi >> pix;
                ins << pix;
            }
        }
    }

    // frame buffer をアロケートする、3倍の領域を取ってそれを3つに分ける
    if ((frame_buffer =(int *)malloc(NUM_FRAME_BUFFER * sizeof(int) * (MAX_WIDTH * MAX_HEIGHT))) == NULL){
        fprintf(stderr, "Can't allocate frame_buffer0 ~ 2\n");
        exit(1);
    }

    vflip_dma_write(ins, (volatile ap_int<32> *)frame_buffer,
        (volatile ap_int<32> *)&frame_buffer[MAX_WIDTH * MAX_HEIGHT],
        (volatile ap_int<32> *)&frame_buffer[2 * (MAX_WIDTH * MAX_HEIGHT)],
        active_frame);

    // AXI4 Stream から Mat フォーマットへ変換
    // dst は宣言時にサイズとカラー・フォーマットを定義する必要がある
    Mat dst[3];
    for(int i=0; i<NUM_FRAME_BUFFER; i++){
        dst[i] = Mat(src.rows, src.cols, CV_8UC3);
    }

    // dst[i] にframe_bufferから画像データをロード
    for(int i=0; i<NUM_FRAME_BUFFER; i++){
        Mat_<Vec3b> dst_vec3b = Mat_<Vec3b>(dst[i]);
        for(int y=0; y<dst[i].rows; y++){
            for(int x=0; x<dst[i].cols; x++){
                Vec3b pixel;
                int rgb = frame_buffer[i*(MAX_WIDTH * MAX_HEIGHT)+y*MAX_WIDTH+x];
                pixel[0] = (rgb & 0xff); // blue
                pixel[1] = (rgb & 0xff00) >> 8; // green
                pixel[2] = (rgb & 0xff0000) >> 16; // red
                dst_vec3b(y,x) = pixel;
            }
        }
    }

    // DMAされたデータをBMPフィルに書き込む
    char output_file[] = "dma_result0.bmp";
    for (int i=0; i<NUM_FRAME_BUFFER; i++){
        switch (i){
            case 0:
                strcpy(output_file,"dma_result0.bmp");
                break;
            case 1:
                strcpy(output_file,"dma_result1.bmp");
                break;
            case 2:
                strcpy(output_file,"dma_result2.bmp");
                break;
        }
        // Mat フォーマットからファイルに書き込み
        imwrite(output_file, dst[i]);
    }

    //free(frame_buffer);
    return 0;
}


Vivado HLS 2018.3 で作成した vflip_dma_write プロジェクトを示す。
vflip_dma_write_11_190102.png

C シミュレーションを行った。結果を示す。
vflip_dma_write_12_190102.png

ZYBO-Z7-20/vflip_dma_write/soulution1/csim/build ディレクトリを示す。
vflip_dma_write_13_190102.png

bmp_file0_vflip.bmp を示す。垂直方向のみフリップされている。
vflip_dma_write_14_190102.jpg

bmp_file0_vflip.bmp をDMA した結果、元に戻ったBMP ファイルの dma_result0.bmp を示す。なお、dma_result1.bmp 、dma_result2.bmp もdma_result0.bmp と同様だ。
vflip_dma_write_15_190102.jpg

C コードの合成を行った。結果を示す。
vflip_dma_write_16_190102.png
vflip_dma_write_17_190102.png

レイテンシはは 1440093 クロックで、ほとんど 1 クロックで 1 ピクセル転送できているのが分かる。

C/RTL 協調シミュレーションを行った。結果を示す。
vflip_dma_write_18_190102.png

レイテンシは 1448260 クロックで合成時のレイテンシと大幅に変わってはいないことが分かる。

C/RTL 協調シミュレーションの波形を見てみよう。
vflip_dma_write_20_190102.png

AWLEN が 0f で 16 バースト転送だということが分かる。

波形を拡大してみよう。
vflip_dma_write_21_190102.png

きちんとバースト転送していることが分かる。これだと性能が出るはずだ。

最後にExport RTL を行った。結果を示す。
vflip_dma_write_19_190102.png

DMA 3個入っているので、リソースは多めに使用するようだ。CP achieved post-implementation は 8.965 ns で大丈夫そうだ。
  1. 2019年01月03日 06:51 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0