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

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

FPGAの部屋

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

VGA画像をXGA画像やHD画像に変換するdisp_dmar_axis_vga IP 3(2 種類の実装を試した)

VGA画像をXGA画像やHD画像に変換するdisp_dmar_axis_vga IP 2(性能が足りない)”の続き。

前回は、disp_dmar_axis IP を作って、C シミュレーション、C コードの合成、C/RTL 協調シミュレーションを行ったが、C/RTL 協調シミュレーション波形から性能が足りないということが分かった。今回は、ソースコードを 2 種類書いて試してみたが、うまく行かなかった。

まずは、”2つのHLSストリームを同時に入力して演算してHLSストリーム出力2”を参考にして、DMA 部分とAXI4 Stream 部分を分けた。DMA部分では、VGA画像をDMA してHLS Stream で出力する。AXI4 Stream 部分では、DMA 部分からのAXI4 Stream を受けて、周りに 0 のデータを挿入して、解像度を大きくする。DMA 部分とAXI4 Stream 部分を別関数にして、DATAFLOW 指示子で並列動作を狙った。
ソースコード disp_dmar_axis_vga.cpp を示す。

// disp_dmar_axis_vga.cpp
// 2019/07/11 by marsee
//

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

#include "disp_dmar_axis_vga.h"

int disp_dmar_stream(AXI_STREAM &ins, AXI_STREAM &outs, int max_width, int max_height);
int fb0_hls_stream(volatile ap_int<32> *fb, AXI_STREAM &outs);
int fb1_hls_stream(volatile ap_int<32> *fb, AXI_STREAM &outs);
int fb2_hls_stream(volatile ap_int<32> *fb, AXI_STREAM &outs);

int disp_dmar_axis_vga(volatile ap_int<32> *fb0, volatile ap_int<32> *fb1, volatile ap_int<32> *fb2,
        AXI_STREAM &outs, ap_uint<16> row, ap_uint<16> col, ap_uint<2> active_frame){
#pragma HLS INTERFACE s_axilite port=col
#pragma HLS INTERFACE s_axilite port=row
#pragma HLS DATAFLOW
#pragma HLS INTERFACE ap_ctrl_hs port=return
#pragma HLS INTERFACE ap_none port=active_frame
#pragma HLS INTERFACE axis register both port=outs
#pragma HLS INTERFACE m_axi depth=307200 port=fb2 offset=slave
#pragma HLS INTERFACE m_axi depth=307200 port=fb1 offset=slave
#pragma HLS INTERFACE m_axi depth=307200 port=fb0 offset=slave

    AP_AXIU32 pix;
    AXI_STREAM indata0;
    AXI_STREAM indata1;
    AXI_STREAM indata2;
#pragma HLS STREAM variable=indata0 depth=64 dim=1
#pragma HLS STREAM variable=indata1 depth=64 dim=1
#pragma HLS STREAM variable=indata2 depth=64 dim=1

    if (active_frame == (ap_uint<2>)0){
        fb2_hls_stream(fb2, indata0);
        disp_dmar_stream(indata0, outs, col, row);
    }else if (active_frame == (ap_uint<2>)1){
        fb0_hls_stream(fb0, indata1);
        disp_dmar_stream(indata1, outs, col, row);
    }else{
        fb1_hls_stream(fb1, indata2);
        disp_dmar_stream(indata2, outs, col, row);
    }

    return(0);
}

int disp_dmar_stream(AXI_STREAM &ins, AXI_STREAM &outs, int max_width, int max_height){
    AP_AXIU32 pix;
    AP_AXIU32 dmad;

    int x_padding = (max_width - VGA_WIDTH)/2;
    int y_padding = (max_height - VGA_HEIGHT)/2;

    LOOP_Y0: for (int y=0; y<max_height; y++){
#pragma HLS LOOP_TRIPCOUNT min=600 max=1080 avg=768
        LOOP_X0: for (int x=0; x<max_width; x++){
#pragma HLS LOOP_TRIPCOUNT min=800 max=1920 avg=1024
#pragma HLS PIPELINE II=1
            if (y < y_padding || y >= y_padding+VGA_HEIGHT)
                pix.data = 0;
            else if (x < x_padding || x >= x_padding+VGA_WIDTH)
                pix.data = 0;
            else{
                ins >> dmad;
                pix.data = dmad.data;
            }

            if (x==0 && y==0)
                pix.user = 1;
            else
                pix.user = 0;

            if (x == max_width-1)
                pix.last = 1;
            else
                pix.last = 0;

            outs << pix;
        }
    }
    return(0);
}

int fb0_hls_stream(volatile ap_int<32> *fb0, AXI_STREAM &outs){
    AP_AXIU32 dmad;

    LOOP_Y1: for(int y=0; y<VGA_HEIGHT; y++){
        LOOP_X1: for(int x=0; x<VGA_WIDTH; x++){
#pragma HLS PIPELINE II=1
            dmad.data = fb0[(y*VGA_WIDTH)+x];
            outs << dmad;
        }
    }
    return(0);
}

int fb1_hls_stream(volatile ap_int<32> *fb1, AXI_STREAM &outs){
    AP_AXIU32 dmad;

    LOOP_Y1: for(int y=0; y<VGA_HEIGHT; y++){
        LOOP_X1: for(int x=0; x<VGA_WIDTH; x++){
#pragma HLS PIPELINE II=1
            dmad.data = fb1[(y*VGA_WIDTH)+x];
            outs << dmad;
        }
    }
    return(0);
}

int fb2_hls_stream(volatile ap_int<32> *fb2, AXI_STREAM &outs){
    AP_AXIU32 dmad;

    LOOP_Y1: for(int y=0; y<VGA_HEIGHT; y++){
        LOOP_X1: for(int x=0; x<VGA_WIDTH; x++){
#pragma HLS PIPELINE II=1
            dmad.data = fb2[(y*VGA_WIDTH)+x];
            outs << dmad;
        }
    }
    return(0);
}


この実装でC コードの合成はうまく行くのだが、C/RTL 協調シミュレーションで出力が出ないで、シミュレーションがいつまで経っても終わらなかった。
いずれの問題も、DMA エンジンを 3 個入れているところがまずいのだと思うが、DMA アドレスのオフセットを毎回変えずに、トリプル・バッファリングを実現するのには、必要となる。


次の実装は、if 文での実装に問題が合って単発転送になっているのじゃないか?という推測のもとに、for 文を細かく区切って実装したら、性能的にはどうだろうか?ということでやってみた。
ソースコード disp_dmar_axis_vga.cpp を示す。

// disp_dmar_axis_vga.cpp
// 2019/07/11 by marsee
//

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

#include "disp_dmar_axis_vga.h"

int disp_dmar_fb0(volatile ap_int<32> *fb0, AXI_STREAM &outs, int max_width, int max_height);
int disp_dmar_fb1(volatile ap_int<32> *fb1, AXI_STREAM &outs, int max_width, int max_height);
int disp_dmar_fb2(volatile ap_int<32> *fb2, AXI_STREAM &outs, int max_width, int max_height);
void pix_in_zero(AP_AXIU32 &pix, int x, int y, int max_width);

int disp_dmar_axis_vga(volatile ap_int<32> *fb0, volatile ap_int<32> *fb1, volatile ap_int<32> *fb2,
        AXI_STREAM &outs, ap_uint<32> &resolution, ap_uint<2> &active_frame){
#pragma HLS INTERFACE ap_ctrl_hs port=return
#pragma HLS INTERFACE ap_none port=active_frame
#pragma HLS INTERFACE s_axilite port=resolution
#pragma HLS INTERFACE axis register both port=outs
#pragma HLS INTERFACE m_axi depth=307200 port=fb2 offset=slave
#pragma HLS INTERFACE m_axi depth=307200 port=fb1 offset=slave
#pragma HLS INTERFACE m_axi depth=307200 port=fb0 offset=slave

    AP_AXIU32 pix;
    int max_width, max_height;

    switch((int)resolution){
        case 0: // SVGA
            max_width = SVGA_WIDTH;
            max_height = SVGA_HEIGHT;
            break;
        case 1: // XGA
            max_width = XGA_WIDTH;
            max_height = XGA_HEIGHT;
            break;
        default: // HD
            max_width = HD_WIDTH;
            max_height = HD_HEIGHT;
            break;
    }

    if (active_frame == (ap_uint<2>)0)
        disp_dmar_fb2(fb2, outs, max_width, max_height);
    else if (active_frame == (ap_uint<2>)1)
        disp_dmar_fb0(fb0, outs, max_width, max_height);
    else
        disp_dmar_fb1(fb1, outs, max_width, max_height);
    
    return(0);
}

int disp_dmar_fb0(volatile ap_int<32> *fb0, AXI_STREAM &outs, int max_width, int max_height){
    AP_AXIU32 pix;
    int x_padding = (max_width - VGA_WIDTH)/2;
    int y_padding = (max_height - VGA_HEIGHT)/2;

    Y00: for (int y=0; y<y_padding; y++){ // 最初の空白行
#pragma HLS LOOP_TRIPCOUNT min=60 max=300 avg=144
        X00: for (int x=0; x<max_width; x++){
#pragma HLS LOOP_TRIPCOUNT min=800 max=1920 avg=1024
#pragma HLS PIPELINE II=1
            pix_in_zero(pix, x, y, max_width);
            outs << pix;
        }
    }

    Y01: for (int y=y_padding; y<y_padding+VGA_HEIGHT; y++){
#pragma HLS LOOP_TRIPCOUNT min=480 max=480 avg=480
        X010: for (int x=0; x<x_padding; x++){ // 最初の空白列
#pragma HLS LOOP_TRIPCOUNT min=80 max=640 avg=192
#pragma HLS PIPELINE II=1
            pix_in_zero(pix, x, y, max_width);
            outs << pix;
        }
        X011: for (int x=x_padding; x<x_padding+VGA_WIDTH; x++){
#pragma HLS LOOP_TRIPCOUNT min=640 max=640 avg=640
#pragma HLS PIPELINE II=1
            pix.data = fb0[((y-y_padding)*VGA_WIDTH)+x-x_padding];
            
            if (x==0 && y==0)
                pix.user = 1;
            else
                pix.user = 0;

            if (x == max_width-1)
                pix.last = 1;
            else
                pix.last = 0;

            outs << pix;
        }
        X012: for (int x=x_padding+VGA_WIDTH; x<max_width; x++){ // 最後の空白列
#pragma HLS LOOP_TRIPCOUNT min=80 max=640 avg=192
#pragma HLS PIPELINE II=1
            pix_in_zero(pix, x, y, max_width);
            outs << pix;
        }
    }

    Y02: for (int y=y_padding+VGA_HEIGHT; y<max_height; y++){ // 最後の空白行
#pragma HLS LOOP_TRIPCOUNT min=60 max=300 avg=144
        X02: for (int x=0; x<max_width; x++){
#pragma HLS LOOP_TRIPCOUNT min=800 max=1920 avg=1024
#pragma HLS PIPELINE II=1
            pix_in_zero(pix, x, y, max_width);
            outs << pix;
        }
    }
    
    return(0);
}

int disp_dmar_fb1(volatile ap_int<32> *fb1, AXI_STREAM &outs, int max_width, int max_height){
    AP_AXIU32 pix;
    int x_padding = (max_width - VGA_WIDTH)/2;
    int y_padding = (max_height - VGA_HEIGHT)/2;

    Y10: for (int y=0; y<y_padding; y++){ // 最初の空白行
#pragma HLS LOOP_TRIPCOUNT min=60 max=300 avg=144
        X10: for (int x=0; x<max_width; x++){
#pragma HLS LOOP_TRIPCOUNT min=800 max=1920 avg=1024
#pragma HLS PIPELINE II=1
            pix_in_zero(pix, x, y, max_width);
            outs << pix;
        }
    }

    Y11: for (int y=y_padding; y<y_padding+VGA_HEIGHT; y++){
#pragma HLS LOOP_TRIPCOUNT min=480 max=480 avg=480
        X100: for (int x=0; x<x_padding; x++){ // 最初の空白列
#pragma HLS LOOP_TRIPCOUNT min=80 max=640 avg=192
#pragma HLS PIPELINE II=1
            pix_in_zero(pix, x, y, max_width);
            outs << pix;
        }
        X101: for (int x=x_padding; x<x_padding+VGA_WIDTH; x++){
#pragma HLS LOOP_TRIPCOUNT min=640 max=640 avg=640
#pragma HLS PIPELINE II=1
            pix.data = fb1[((y-y_padding)*VGA_WIDTH)+x-x_padding];
            
            if (x==0 && y==0)
                pix.user = 1;
            else
                pix.user = 0;

            if (x == max_width-1)
                pix.last = 1;
            else
                pix.last = 0;

            outs << pix;
        }
        X102: for (int x=x_padding+VGA_WIDTH; x<max_width; x++){ // 最後の空白列
#pragma HLS LOOP_TRIPCOUNT min=80 max=640 avg=192
#pragma HLS PIPELINE II=1
            pix_in_zero(pix, x, y, max_width);
            outs << pix;
        }
    }

    Y12: for (int y=y_padding+VGA_HEIGHT; y<max_height; y++){ // 最後の空白行
#pragma HLS LOOP_TRIPCOUNT min=60 max=300 avg=144
        X12: for (int x=0; x<max_width; x++){
#pragma HLS LOOP_TRIPCOUNT min=800 max=1920 avg=1024
#pragma HLS PIPELINE II=1
            pix_in_zero(pix, x, y, max_width);
            outs << pix;
        }
    }
    
    return(0);
}

int disp_dmar_fb2(volatile ap_int<32> *fb2, AXI_STREAM &outs, int max_width, int max_height){
    AP_AXIU32 pix;
    int x_padding = (max_width - VGA_WIDTH)/2;
    int y_padding = (max_height - VGA_HEIGHT)/2;

    Y20: for (int y=0; y<y_padding; y++){ // 最初の空白行
#pragma HLS LOOP_TRIPCOUNT min=60 max=300 avg=144
        X20: for (int x=0; x<max_width; x++){
#pragma HLS LOOP_TRIPCOUNT min=800 max=1920 avg=1024
#pragma HLS PIPELINE II=1
            pix_in_zero(pix, x, y, max_width);
            outs << pix;
        }
    }

    Y21: for (int y=y_padding; y<y_padding+VGA_HEIGHT; y++){
#pragma HLS LOOP_TRIPCOUNT min=480 max=480 avg=480
        X210: for (int x=0; x<x_padding; x++){ // 最初の空白列
#pragma HLS LOOP_TRIPCOUNT min=80 max=640 avg=192
#pragma HLS PIPELINE II=1
            pix_in_zero(pix, x, y, max_width);
            outs << pix;
        }
        X211: for (int x=x_padding; x<x_padding+VGA_WIDTH; x++){
#pragma HLS LOOP_TRIPCOUNT min=640 max=640 avg=640
#pragma HLS PIPELINE II=1
            pix.data = fb2[((y-y_padding)*VGA_WIDTH)+x-x_padding];
            
            if (x==0 && y==0)
                pix.user = 1;
            else
                pix.user = 0;

            if (x == max_width-1)
                pix.last = 1;
            else
                pix.last = 0;

            outs << pix;
        }
        X212: for (int x=x_padding+VGA_WIDTH; x<max_width; x++){ // 最後の空白列
#pragma HLS LOOP_TRIPCOUNT min=80 max=640 avg=192
#pragma HLS PIPELINE II=1
            pix_in_zero(pix, x, y, max_width);
            outs << pix;
        }
    }

    Y22: for (int y=y_padding+VGA_HEIGHT; y<max_height; y++){ // 最後の空白行
#pragma HLS LOOP_TRIPCOUNT min=60 max=300 avg=144
        X22: for (int x=0; x<max_width; x++){
#pragma HLS LOOP_TRIPCOUNT min=800 max=1920 avg=1024
#pragma HLS PIPELINE II=1
            pix_in_zero(pix, x, y, max_width);
            outs << pix;
        }
    }
    
    return(0);
}

void pix_in_zero(AP_AXIU32 &pix, int x, int y, int max_width){
    pix.data = 0;
    
    if (x==0 && y==0)
        pix.user = 1;
    else
        pix.user = 0;

    if (x == max_width-1)
        pix.last = 1;
    else
        pix.last = 0;
}


これも C/RTL 協調シミュレーションをやってみたところ、”VGA画像をXGA画像やHD画像に変換するdisp_dmar_axis_vga IP 2(性能が足りない)”と同様に単発転送になってしまった。ここまで書くことは無いようだ。
次回は、うまく行った実装を紹介する。
  1. 2019年07月14日 05:06 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0