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

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

FPGAの部屋

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

AXI4 Stream 出力にAXI4 Stream スイッチ付きのDMA Read IP 1

AXI4 Stream 出力にAXI4 Stream スイッチ付きのDMA Read IP を作るの第1回目。

DMA Read したデータをAXI4 Stream 出力するIP を作りたいのだが、AXI4 Stream 出力は 2 つにして、そのどちらかにAXI4 Stream を出力するIP を作る。これは、Xilinx のAXI Stream スイッチを使わずに、通常の画像出力とフィルタを通った画像を切り替えるために使用する。
このIP は、Vivado HLS セミナの第8回目のAXI4 Stream 応用編の例として使用する予定だ。

まずは、ソースコードの DMA2axis2st.cpp を示す。

// DMA2axis2st.cpp
// 2019/06/07 by marsee
//

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

int DMA2axis(volatile ap_int<32> *in, int sel, int x_size, int y_size,
    hls::stream<ap_axis<32,1,1,1> >& outs0, hls::stream<ap_axis<32,1,1,1> >& outs1){
#pragma HLS INTERFACE s_axilite port=y_size
#pragma HLS INTERFACE s_axilite port=x_size
#pragma HLS INTERFACE s_axilite port=sel
#pragma HLS INTERFACE s_axilite port=return
#pragma HLS INTERFACE axis register both port=outs0
#pragma HLS INTERFACE axis register both port=outs1
#pragma HLS INTERFACE m_axi depth=480000 port=in offset=slave
    ap_axis<32,1,1,1> out_val;

    for(int y=0; y<y_size; y++){
#pragma HLS LOOP_TRIPCOUNT min=600 max=600 avg=600
        for(int x=0; x<x_size; x++){
#pragma HLS LOOP_TRIPCOUNT min=800 max=800 avg=800
#pragma HLS PIPELINE II=1
            out_val.data = in[y*x_size+x];
            if(x==0 && y==0)
                out_val.user = 1;
            else
                out_val.user = 0;
            if(x == x_size-1)
                out_val.last = 1;
            else
                out_val.last = 0;
            if(sel == 0)
                outs0 << out_val;
            else
                outs1 << out_val;
        }
    }
    return(0);
}


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

// DMA2axis2st_tb.cpp
// 2019/06/07 by marsee
//

#include <iostream>
#include "hls_opencv.h"

#include "bmp_data.h"

#define Y_SIZE  600
#define X_SIZE  800

char OUTPUT_BMP_FILE0[] = "test0.bmp";
char OUTPUT_BMP_FILE1[] = "test1.bmp";

int DMA2axis(volatile ap_int<32> *in, int sel, int x_size, int y_size,
    hls::stream<ap_axis<32,1,1,1> >& outs0, hls::stream<ap_axis<32,1,1,1> >& outs1);

int main(){
    hls::stream<ap_axis<32,1,1,1> > outs0;
    hls::stream<ap_axis<32,1,1,1> > outs1;
    ap_axis<32,1,1,1> axisp;
    ap_int<32> *rd_bmp;

    if((rd_bmp =(ap_int<32> *)malloc(sizeof(ap_int<32>) * (X_SIZE * Y_SIZE))) == NULL){
        fprintf(stderr, "Can't allocate rd_bmp memory\n");
        exit(1);
    }

    for(int y=0; y<Y_SIZE; y++){
        for(int x=0; x<X_SIZE; x++){
            rd_bmp[y*X_SIZE+x] = (int)(bmp_file_array[y][x][0]) | ((int)(bmp_file_array[y][x][1])<<8) | ((int)(bmp_file_array[y][x][2]) <<16);
        }
    }

    DMA2axis(rd_bmp, 0, X_SIZE, Y_SIZE, outs0, outs1);

    cv::Mat img(Y_SIZE, X_SIZE, CV_8UC3);
    cv::Mat_<cv::Vec3b> dst_vec3b = cv::Mat_<cv::Vec3b>(img);
    for (int y=0; y<Y_SIZE; y++){
        for (int x=0; x<X_SIZE; x++){
            outs0 >> axisp;
            cv::Vec3b pixel;
            pixel[0] = axisp.data & 0xff; // blue
            pixel[1] = (axisp.data >> 8) & 0xff; // green
            pixel[2] = (axisp.data >> 16) & 0xff; // red
            dst_vec3b(y,x) = pixel;
        }
    }

    cv::imwrite(OUTPUT_BMP_FILE0, img);

    DMA2axis(rd_bmp, 1, X_SIZE, Y_SIZE, outs0, outs1);

    for (int y=0; y<Y_SIZE; y++){
        for (int x=0; x<X_SIZE; x++){
            outs1 >> axisp;
            cv::Vec3b pixel;
            pixel[0] = axisp.data & 0xff; // blue
            pixel[1] = (axisp.data >> 8) & 0xff; // green
            pixel[2] = (axisp.data >> 16) & 0xff; // red
            dst_vec3b(y,x) = pixel;
        }
    }

    cv::imwrite(OUTPUT_BMP_FILE1, img);

    return(0);
}


なお、bmp_data.h はこの画像を”Vivado HLS 2019.1 を使用してBMPファイルをC のヘッダファイルに変換する”を使用して、ヘッダファイルに変換してある。
bmp_header_file_11_190612.jpg

Vivado HLS 2019.1 のプロジェクトの DMA2axis2st を示す。
bmp_header_file_8_190612.png

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

DMA2axis2st/solution1/csim/build/ ディレクトリを見てみよう。
bmp_header_file_12_190612.png

test0.bmp と test1.bmp ができている。成功だ。

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

Estimated は 8.750 ns で大丈夫そうだ。
Latency は 480078 クロックで、ほとんど 1 クロックで 1 ピクセルを処理できている。問題ない。
リソースは、FF が 29 % で LUT が 50 % 使用されているので、リソース使い過ぎか。。。

それでは、 x の for ループの下のPIPELINE 指示子を取ってみよう。
bmp_header_file_13_190612.png

このソースコードでの合成結果を示す。
bmp_header_file_14_190612.png

Estimated は 8.750 ns だった。

Latency は 965401 クロックで、約 2 クロックで 1 出力となった。倍の時間がかかるようになったが、FF は 3 % で、LUT は 9 % の使用率で相当低減されている。bmp_data.h をメモリに展開して送るので、これで全く問題ない。よって、遅いけど、リソースを使用しない方を採用する。

指示子(プラグマ)や設定によって、リソース使用量を取るか、スループットや動作周波数を取るかを選べるのが、Vivado HLS の最大の利点ではないか?と思っている。
  1. 2019年06月12日 00:54 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0