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

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

FPGAの部屋

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

Vivado HLS 2019.2 で krnl_dma_write を作成する1

Vitis のカーネル間ストーミング接続をテストするために DMA Read カーネル ー ラプラシアン・フィルタ・カーネル ー DMA Write カーネルをストーミング接続してみよう。
今回は、DMA Write カーネルを作成しよう。

ストーミング接続用のhls::stream の定義は、hls::stream > にする必要がある。こうするとサイドチャネルの信号は last と keep , strb だけになる。この内の last を使用して、フレームの最後をマークしよう。
ストーミング接続用 DMA Write カーネルの krnl_dma_write.cpp を示す。
なお、C シミュレーションできないので、とりあえず extern "C" { } は外してある。

// krnl_dma_write.cpp
// 2020/01/28 by marsee

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

//extern "C" {
void dma_write(hls::stream<ap_axiu<32,0,0,0> >& ins, volatile int32_t *outm,
        int32_t x_size, int32_t y_size){
#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=y_size bundle=control
#pragma HLS INTERFACE s_axilite port=x_size bundle=control
#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++){
#pragma HLS LOOP_TRIPCOUNT min=48 max=600
        LOOP_DWX: for(int x=0; x<x_size; x++){
#pragma HLS LOOP_TRIPCOUNT min=64 max=800
#pragma HLS PIPELINE II=1
            ins >> pix;
            outm[x_size*y+x] = pix.data;
        }
    }
}
//}


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

// krnl_dma_write_tb.cpp
// 2020/01/28 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_write(hls::stream<ap_axiu<32,0,0,0> >& ins, volatile int32_t *outm,
        int32_t x_size, int32_t y_size);
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_write(ins, hw_dmaw.data(), img.cols, img.rows);
    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_write プロジェクトを作成した。
streaming_kernel_48_200129.png

C シミュレーションを実行した。
streaming_kernel_48_200129.png

dma_write.bmp が生成された。問題無さそうだ。
streaming_kernel_63_200129.png

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

LUT が 20 % も消費されている。リソースを消費すぎているのではないだろうか?
  1. 2020年01月29日 05:02 |
  2. Vitis
  3. | トラックバック:0
  4. | コメント:0