FC2カウンター FPGAの部屋 AXI4 Stream 入力にAXI4 Stream スイッチ付きのDMA Write IP 1
FC2ブログ

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

FPGAの部屋

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

AXI4 Stream 入力にAXI4 Stream スイッチ付きのDMA Write IP 1

AX4 Stream 入力したデータをDMA Write するIP を作成する。ただし、AXI4 Stream 入力は、2 つあり、それらを切り替えてデータを取得することとする。つまり、AXI4 Stream スイッチ付きのDMA Write IP を作る。
これは、”AXI4 Stream 出力にAXI4 Stream スイッチ付きのDMA Read IP 1”と”AXI4 Stream 出力にAXI4 Stream スイッチ付きのDMA Read IP 2”とセットになるDMA Write IP である。1つのAXI4 Stream は画像そのまま、もう1つの間にラプラシアン・フィルタを挿入する予定だ。

最初に作成した Vivado HLS 2019.2 のプロジェクトの axis2DMA2st を示す。
bmp_header_file_20_190612.png

ソースコードの axis2DMA2st.cpp を示す。

// axis2DMA2st.cpp
// 2019/06/08 by marsee
//

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

int axis2DMA2st(hls::stream<ap_axis<32,1,1,1> >& ins0, hls::stream<ap_axis<32,1,1,1> >& ins1,
    int sel,  int x_size, int y_size, volatile ap_int<32> *out){
#pragma HLS INTERFACE s_axilite port=return
#pragma HLS INTERFACE m_axi depth=480000 port=out offset=slave
#pragma HLS INTERFACE axis register both port=ins1
#pragma HLS INTERFACE axis register both port=ins0
    ap_axis<32,1,1,1> val;

    Loop1 : do {    // user が 1 になった時にスタート
#pragma HLS LOOP_TRIPCOUNT min=1 max=1 avg=1
        if(sel == 0)
            ins0 >> val;
        else
            ins1 >> val;
    } while(val.user == 0);
    
    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
            if(!(y==0 && x==0)){
                if(sel == 0)
                    ins0 >> val;
                else
                    ins1 >> val;
            }
            out[y*x_size+x] = val.data;
        }
    }
    
    return(0);
}


x のループの内側のPIPELINE 指示子は入れるとリソースを食いすぎるため除いてある。

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

// axis2DMA2st_tb.cpp
// 2019/06/09 by marsee
//

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

#include "bmp_data.h"

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

int axis2DMA2st(hls::stream<ap_axis<32,1,1,1> >& ins0, hls::stream<ap_axis<32,1,1,1> >& ins1,
    int sel,  int x_size, int y_size, volatile ap_int<32> *out);
    
int main(){
    hls::stream<ap_axis<32,1,1,1> > ins0;
    hls::stream<ap_axis<32,1,1,1> > ins1;
    ap_axis<32,1,1,1> axisp;
    ap_int<32> *wr_bmp0, *wr_bmp1;

    for(int y=0; y<Y_SIZE; y++){
        for(int x=0; x<X_SIZE; x++){
            axisp.data = (ap_int<32>)bmp_file_array[y][x][0] | ((ap_int<32>)bmp_file_array[y][x][1])<<8 | ((ap_int<32>)bmp_file_array[y][x][2])<<16;
            
            if(y==0 && x==0)
                axisp.user = 1;
            else
                axisp.user = 0;
                
            if(x == X_SIZE-1)
                axisp.last = 1;
            else
                axisp.last = 0;
            
            ins0 << axisp;
            ins1 << axisp;
        }
    }
    
    if((wr_bmp0 =(ap_int<32> *)malloc(sizeof(ap_int<32>) * (X_SIZE * Y_SIZE))) == NULL){
        fprintf(stderr, "Can't allocate wr_bmp0 memory\n");
        exit(1);
    }

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

    axis2DMA2st(ins0, ins1, 0, X_SIZE, Y_SIZE, wr_bmp0);

    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++){
            ap_int<32> data = wr_bmp0[y*X_SIZE+x];
            cv::Vec3b pixel;
            pixel[0] = data & 0xff; // blue
            pixel[1] = (data >> 8) & 0xff; // green
            pixel[2] = (data >> 16) & 0xff; // red
            dst_vec3b(y,x) = pixel;
        }
    }
    
    cv::imwrite(OUTPUT_BMP_FILE0, img);

    axis2DMA2st(ins0, ins1, 1, X_SIZE, Y_SIZE, wr_bmp1);
    
    for (int y=0; y<Y_SIZE; y++){
        for (int x=0; x<X_SIZE; x++){
            ap_int<32> data = wr_bmp1[y*X_SIZE+x];
            cv::Vec3b pixel;
            pixel[0] = data & 0xff; // blue
            pixel[1] = (data >> 8) & 0xff; // green
            pixel[2] = (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

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

axis2DMA2st/solution1/csim/build/ ディレクトリを見ると、test0.bmp, test1.bmp ができている。成功だ。
bmp_header_file_26_190615.png

C コードの合成を行った。
bmp_header_file_27_190615.png

Estimated は 8.75 ns で問題ない。
Latency は、 962402 クロックで、 962402 / 480000 ≒ 2.0-1 クロック/処理ピクセルので、予定通りだ。
リソース使用量もBRAM_18K が 1 % 、FF が 3%、 LUT が 7 % なので、問題ない。

Analysis 画面を見てみよう。
bmp_header_file_28_190615.png

gmem_addr_write_ln36(write) は x のループ (Loop 2.1) の 2 ステート目でWrite するのは 1 クロックのようだが、下の gmem_addr_wr_resp(writeresp) は 5 ステート必要なようだ。

(2019/06/20:修正)
axis2DMA2st.cpp の sel, x_size, y_size がAXI4 Lite Slave として、レジスタにマップされていなかったので修正する。

// axis2DMA2st.cpp
// 2019/06/08 by marsee
// 2019/06/20 bug fix
//

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

int axis2DMA2st(hls::stream<ap_axis<32,1,1,1> >& ins0, hls::stream<ap_axis<32,1,1,1> >& ins1,
    int sel,  int x_size, int y_size, volatile ap_int<32> *out){
#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 m_axi depth=480000 port=out offset=slave
#pragma HLS INTERFACE axis register both port=ins1
#pragma HLS INTERFACE axis register both port=ins0
    ap_axis<32,1,1,1> val;

    Loop1 : do {    // user が 1 になった時にスタート
#pragma HLS LOOP_TRIPCOUNT min=1 max=1 avg=1
        if(sel == 0)
            ins0 >> val;
        else
            ins1 >> val;
    } while(val.user == 0);

    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
            if(!(y==0 && x==0)){
                if(sel == 0)
                    ins0 >> val;
                else
                    ins1 >> val;
            }
            out[y*x_size+x] = val.data;
        }
    }

    return(0);
}

  1. 2019年06月15日 08:39 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

コメント

コメントの投稿


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

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