FC2カウンター FPGAの部屋 Vivado HLS 2019.2 で krnl_dma_write を作成する3
FC2ブログ

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

FPGAの部屋

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

Vivado HLS 2019.2 で krnl_dma_write を作成する3

Vivado HLS 2019.2 で krnl_dma_write を作成する2”の続き。

Vitis のカーネル間ストーミング接続をテストするために DMA Read カーネル ー ラプラシアン・フィルタ・カーネル ー DMA Write カーネルをストーミング接続するためのDMA Write カーネルを作成したが、リソースを消費すぎていた。リソース消費を抑えようと for ループ内のPIPELINE 指示子を削除した。そうすると C コードの合成では、リソース使用量が減った様にレポートされたが、Export RTL したらリソース使用量が急激に増えてしまった。結局うまく行かなかった。その後の解析で、どうやら除算をしているようで、それがリソース使用量の大半を占めていることが分かった。
考えてみれば、DMA 数を設定できるということは、バーストの数を数えて行く必要がある、ということで除算を使用しているのだと思う。(私ならば減算していくが。。。)そこで、予めDMA 数をコンパイラが数えられるように定数にすれば、コンパイラが静的に解析できるはずだ。今回は X_SIZE と Y_SIZE を定数として与えてみよう。

krnl_dma_write2.cpp を示す。

// krnl_dma_write2.cpp
// 2020/01/30 by marsee

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

#define X_SIZE  64
#define Y_SIZE  48

extern "C" {
void dma_write2(hls::stream<ap_axiu<32,0,0,0> >& ins, volatile int32_t *outm){
#pragma HLS INTERFACE m_axi depth=3072 port=outm bundle=gmem
#pragma HLS INTERFACE axis register both port=ins
#pragma HLS INTERFACE s_axilite port=return bundle=control
    ap_axiu<32,0,0,0> pix;

    LOOP_DWY: for(int y=0; y<Y_SIZE; y++){
        LOOP_DWX: for(int x=0; x<X_SIZE; x++){
#pragma HLS PIPELINE II=1
            ins >> pix;
            outm[X_SIZE*y+x] = pix.data;
        }
    }
}
}


テストベンチの krnl_dma_write2_tb.cpp を示す。

// krnl_dma_write2_tb.cpp
// 2020/01/30 by marsee

#include <stdio.h>
#include <stdint.h>
#include "hls_opencv.h"
#include <ap_int.h>
#include <hls_stream.h>
#include <ap_axi_sdata.h>

void dma_write2(hls::stream<ap_axiu<32,0,0,0> >& ins, volatile int32_t *outm);
void dma_write_soft(hls::stream<ap_axiu<32,0,0,0> >& ins, volatile int32_t *outm,
        int32_t x_size, int32_t y_size);

const char INPUT_BMP_FILE[] = "test.bmp";
const char OUTPUT_BMP_FILE[] = "dma_write.bmp";

int main(){
    hls::stream<ap_axiu<32,0,0,0> > ins;
    hls::stream<ap_axiu<32,0,0,0> > ins_soft;
    ap_axiu<32,0,0,0> pix;

    // BMPファイルをMat に読み込む
    cv::Mat img = cv::imread(INPUT_BMP_FILE);

    // ピクセルを入れる領域の確保
    std::vector<int32_t> rd_bmp(sizeof(int32_t)*img.cols*img.rows);
    std::vector<int32_t> hw_dmaw(sizeof(int32_t)*(img.cols-2)*(img.rows-2));
    std::vector<int32_t> sw_dmaw(sizeof(int32_t)*(img.cols-2)*(img.rows-2));

    // rd_bmp にBMPのピクセルを代入
    cv::Mat_<cv::Vec3b> dst_vec3b = cv::Mat_<cv::Vec3b>(img);
    for (int y=0; y<img.rows; y++){
        for (int x=0; x<img.cols; x++){
            cv::Vec3b pixel;
            pixel = dst_vec3b(y,x);
            rd_bmp[y*img.cols+x] = (pixel[0] & 0xff) | ((pixel[1] & 0xff)<<8) | ((pixel[2] & 0xff)<<16);
            // blue - pixel[0]; green - pixel[1]; red - pixel[2];
        }
    }

    // ins に入力データを用意する
    for(int j=0; j < img.rows; j++){
        for(int i=0; i < img.cols; i++){
            pix.data = (ap_int<32>)rd_bmp[(j*img.cols)+i];

            if ((i==img.cols-1) && (j==img.rows-1)) // フレームの最後で last をアサートする
                pix.last = 1;
            else
                pix.last = 0;

            ins << pix;
            ins_soft << pix;
        }
    }

    dma_write2(ins, hw_dmaw.data());
    dma_write_soft(ins_soft, sw_dmaw.data(), img.cols, img.rows);

    // ハードウェアとソフトウェアの dma_write の値のチェック
    for (int y=0; y<img.rows; y++){
        for (int x=0; x<img.cols; x++){
            if (hw_dmaw[y*img.cols+x] != sw_dmaw[y*img.cols+x]){
                printf("ERROR HW and SW results mismatch x = %ld, y = %ld, HW = %x, SW = %x\n",
                        x, y, hw_dmaw[y*img.cols+x], sw_dmaw[y*img.cols+x]);
                return(1);
            }
        }
    }
    printf("Success HW and SW results match\n");

    const int dmaw_rows = img.rows;
    const int dmaw_cols = img.cols;
    cv::Mat wbmpf(dmaw_rows, dmaw_cols, CV_8UC3);
    // wbmpf にラプラシアンフィルタ処理後の画像を入力
    cv::Mat_<cv::Vec3b> lap_vec3b = cv::Mat_<cv::Vec3b>(wbmpf);
    for (int y=0; y<wbmpf.rows; y++){
        for (int x=0; x<wbmpf.cols; x++){
            cv::Vec3b pixel;
            pixel = lap_vec3b(y,x);
            int32_t rgb = hw_dmaw[y*wbmpf.cols+x];
            pixel[0] = (rgb & 0xff); // blue
            pixel[1] = (rgb & 0xff00) >> 8; // green
            pixel[2] = (rgb & 0xff0000) >> 16; // red
            lap_vec3b(y,x) = pixel;
        }
    }

    // ハードウェアのラプラシアンフィルタの結果を bmp ファイルへ出力する
    cv::imwrite(OUTPUT_BMP_FILE, wbmpf);

    return(0);
}

void dma_write_soft(hls::stream<ap_axiu<32,0,0,0> >& ins, volatile int32_t *outm,
        int32_t x_size, int32_t y_size){
    ap_axiu<32,0,0,0> pix;

    LOOP_DWY: for(int y=0; y<y_size; y++){
        LOOP_DWX: for(int x=0; x<x_size; x++){
            ins >> pix;
            outm[x_size*y+x] = pix.data;
        }
    }
}


Vivado HLS 2019.2 で krnl_dma_write2 プロジェクトを作成した。
streaming_kernel_67_200130.png

C シミュレーションを行った。
streaming_kernel_68_200130.png

dma_write.bmp が生成されている。成功だ。
streaming_kernel_69_200130.png

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

リソース使用量は FF で 1 % 以下で、LUT で 1% だった。

C/RTL 協調シミュレーションを行った。
streaming_kernel_71_200130.png

C/RTL 協調シミュレーションの波形を示す。
streaming_kernel_72_200130.png

WVALID も TREADY もほとんど 1 を保っている。スループットが高そうだ。

Export RTL を行った。
streaming_kernel_73_200130.png

LUT は 361 個, FF は 542 個と少ない。

dma_write2.xo ファイルも生成されている。
streaming_kernel_74_200130.png

AXI4 Master を使用する場合に DMA 数を動的に設定できると、設定された数がDMA バースト何個に相当するかを演算する必要がある。最後のバーストは数を変える必要があるかも知れない?それを動的にする必要があるため除算しているのだろう?(私は減算するが。。。)
一方、DMA 数をコードに記述してしまえば、コンパイラが解析できて、DMA 数をHDL コードの埋め込むことができるようになる。結果、回路が簡単になり、リソース使用量が減る。
Vivado HLS で AXI4 Master を使用する場合には、なるべく DMA 数を決め打ちにしておいたほうがリソース使用量が減って、スループットを上げやすくなる。DMA 数が変わったら、C ソースコードのDMA 数を変更してもう一度 Vivado HLS で合成すれば良いのである。。。

これが AXI4 Stream の場合は、データ転送数が可変でも、バーストという決まった数の転送を複数回する方法ではなく、バースト数の規定のない無限連続バーストであるため、データ転送数が可変でも問題ない。現にラプラシアン・フィルタはデータ転送数が可変だが、リソース使用量は少ない。
Vivado HLS 2019.2 で krnl_lap_filter を作成する1(ソースコードの表示)”、”Vivado HLS 2019.2 で krnl_lap_filter を作成する2(IP化)”を参照ください。
  1. 2020年01月31日 04:39 |
  2. Vitis
  3. | トラックバック:0
  4. | コメント:0

コメント

コメントの投稿


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

トラックバック URL
https://marsee101.blog.fc2.com/tb.php/4779-49743d62
この記事にトラックバックする(FC2ブログユーザー)