FC2カウンター FPGAの部屋 Vivado HLS 2019.2 で krnl_dma_read を作成する3(DMA Read サイズを固定する)
FC2ブログ

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

FPGAの部屋

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

Vivado HLS 2019.2 で krnl_dma_read を作成する3(DMA Read サイズを固定する)

Vivado HLS 2019.2 で krnl_dma_read を作成する2(IP 化)”の続き。

Vivado HLS 2019.2 で krnl_dma_write を作成する3”でDMA Write のサイズを固定したところ、劇的にリソース使用量が減少した。そうしたら、krnl_dma_read も同様ではないか?と思って、DMA Read サイズを固定したバージョンを作成してみることにした。

まずは、 krnl_dma_read2.cpp を示す。

// krnl_dma_read2.cpp
// 2020/01/31 by marsee

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

#define X_SIZE  64
#define Y_SIZE  48

//extern "C" {
void dma_read2(volatile int32_t *inm, hls::stream<ap_axiu<32,0,0,0> >& outs){
#pragma HLS INTERFACE s_axilite port=return bundle=control
#pragma HLS INTERFACE axis register both port=outs
#pragma HLS INTERFACE m_axi depth=3072 port=inm offset=slave bundle=gmem

    ap_axiu<32,0,0,0> pix;

    LOOP_DRY: for(int y=0; y<Y_SIZE; y++){
        LOOP_DRX: for(int x=0; x<X_SIZE; x++){
#pragma HLS PIPELINE II=1
            pix.data = inm[X_SIZE*y+x];

            if(x==(X_SIZE-1) && y==(Y_SIZE-1))
                pix.last = 1;
            else
                pix.last = 0;
            outs << pix;
        }
    }
}
//}


krnl_dma_read2_tb.cpp を示す。

// krnl_dma_read2_tb.cpp
// 2020/01/31 by marsee

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <ap_int.h>
#include <hls_stream.h>
#include <iostream>
#include <fstream>
#include <ap_axi_sdata.h>
#include "bmp_header.h"

void dma_read2(volatile int32_t *inm, hls::stream<ap_axiu<32,0,0,0> >& outs);

int main(){
    BITMAPFILEHEADER bmpfhr; // BMPファイルのファイルヘッダ(for Read)
    BITMAPINFOHEADER bmpihr; // BMPファイルのINFOヘッダ(for Read)
    FILE *fbmpr, *fbmpw;
    int32_t *rd_bmp, *dmar;
    int32_t blue, green, red;
    hls::stream<ap_axiu<32,0,0,0> > outs;
    ap_axiu<32,0,0,0> vals;

    if ((fbmpr = fopen("test.bmp", "rb")) == NULL){ // test.bmp をオープン
        fprintf(stderr, "Can't open test.bmp by binary read mode\n");
        exit(1);
    }
    // bmpヘッダの読み出し
    fread(&bmpfhr.bfType, sizeof(uint16_t), 1, fbmpr);
    fread(&bmpfhr.bfSize, sizeof(uint32_t), 1, fbmpr);
    fread(&bmpfhr.bfReserved1, sizeof(uint16_t), 1, fbmpr);
    fread(&bmpfhr.bfReserved2, sizeof(uint16_t), 1, fbmpr);
    fread(&bmpfhr.bfOffBits, sizeof(uint32_t), 1, fbmpr);
    fread(&bmpihr, sizeof(BITMAPINFOHEADER), 1, fbmpr);

    // ピクセルを入れるメモリをアロケートする
    if ((rd_bmp =(int32_t *)malloc(sizeof(int32_t) * (bmpihr.biWidth * bmpihr.biHeight))) == NULL){
        fprintf(stderr, "Can't allocate rd_bmp memory\n");
        exit(1);
    }
    if ((dmar =(int32_t *)malloc(sizeof(int32_t) * (bmpihr.biWidth * bmpihr.biHeight))) == NULL){
        fprintf(stderr, "Can't allocate hw_lapd memory\n");
        exit(1);
    }

    // rd_bmp にBMPのピクセルを代入。その際に、行を逆転する必要がある
    for(int y=0; y<bmpihr.biHeight; y++){
        for(int x=0; x<bmpihr.biWidth; x++){
            blue = fgetc(fbmpr);
            green = fgetc(fbmpr);
            red = fgetc(fbmpr);
            rd_bmp[((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x] = (int32_t)((blue & 0xff) | ((green & 0xff)<<8) | ((red & 0xff)<<16));
        }
    }
    fclose(fbmpr);

    dma_read2(rd_bmp, outs);

    // DMAされた値のチェック
    for(int y=0; y<bmpihr.biHeight; y++){
        for(int x=0; x<bmpihr.biWidth; x++){
            outs >> vals;
            dmar[(y*bmpihr.biWidth)+x] = (int32_t)vals.data;
            if ((int32_t)vals.data != rd_bmp[(y*bmpihr.biWidth)+x]){
                printf("ERROR HW and SW results mismatch x = %ld, y = %ld, DMAR = %d, ORG = %d\n", x, y, (int)vals.data, (int)rd_bmp[(y*bmpihr.biWidth)+x]);
                return(1);
            }
        }
    }
    std::cout << "Success DMA READ results match" << std::endl;
    std::cout << std::endl;


    // DMA_Read の結果を dma_read.bmp へ出力する
    if ((fbmpw=fopen("dma_read.bmp", "wb")) == NULL){
        fprintf(stderr, "Can't open temp_lap.bmp by binary write mode\n");
        exit(1);
    }
    // BMPファイルヘッダの書き込み
    fwrite(&bmpfhr.bfType, sizeof(uint16_t), 1, fbmpw);
    fwrite(&bmpfhr.bfSize, sizeof(uint32_t), 1, fbmpw);
    fwrite(&bmpfhr.bfReserved1, sizeof(uint16_t), 1, fbmpw);
    fwrite(&bmpfhr.bfReserved2, sizeof(uint16_t), 1, fbmpw);
    fwrite(&bmpfhr.bfOffBits, sizeof(uint32_t), 1, fbmpw);
    fwrite(&bmpihr, sizeof(BITMAPINFOHEADER), 1, fbmpw);

    // RGB データの書き込み、逆順にする
    for (int y=0; y<bmpihr.biHeight; y++){
        for (int x=0; x<bmpihr.biWidth; x++){
            blue = dmar[((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x] & 0xff;
            green = (dmar[((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x] >> 8) & 0xff;
            red = (dmar[((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x]>>16) & 0xff;

            fputc(blue, fbmpw);
            fputc(green, fbmpw);
            fputc(red, fbmpw);
        }
    }
    fclose(fbmpw);
    free(rd_bmp);
    free(dmar);

    return(0);
}


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

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

dma_read.bmp が生成されていた。
streaming_kernel_75_200131.png

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

FF は 724 個、LUT は 1007 個使用している。DMA Read サイズを可変にしている時は、FF が 10754 個、LUT が 8515 個使用しているので、FF は 10 倍以上リソース使用量が多い。
Latency も 3082 クロックで、ほとんど 1 クロック/ピクセルに近い。

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

3123 クロックだった。

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

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

拡大してみると RVALID の 0 の部分は、クロックの立ち上がっている部分のみなので、0 と判定されていないことが分かった。よって、RVALID はずーと 1 と同じということが分かった。
TVALID もほとんど 1 になっているので、スループットが高いことが分かる。

Export RTL を行った。
streaming_kernel_81_200131.png

LUT も FF も DMA Read サイズを可変にした場合よりも劇的少なくなっているのが分かる。

dma_read2.xo ファイルが生成された。
streaming_kernel_82_200131.png
  1. 2020年02月01日 05:08 |
  2. Vitis
  3. | トラックバック:0
  4. | コメント:0

コメント

コメントの投稿


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

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