FC2カウンター FPGAの部屋 Vivado HLS でFIFO を作ってみた1(指示子を入れない場合)

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

FPGAの部屋

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

Vivado HLS でFIFO を作ってみた1(指示子を入れない場合)

Vivado HLS でFIFO を作ってみようと思う。
DATAFLOW 指示子が for 文にも有効ということが分かったので、それを試してみよう。最初はPIPELINE や DATAFLOW 指示子を入れないでどうなるのかを試してから入れてみようと思う。

さて、作成する FIFO は、画像表示の 1 行分を保持する FIFO を作ろうと思う。この FIFO は 1 行分の配列に入力ピクセルを入れて、配列から出力するものとする。画像の 1 行は、ピクセルデータが出力される画像表示部分と画像が表示されないフロント・ポーチ、水平同期信号、バック・ポーチがあるので、次の行のデータ処理のことは考えなくても済むので、それを考慮した実装にした。

最初に、fifo_test.h を示す。

// fifo_test.h
// 2016/09/26 by marsee
//

#ifndef __FIFO_TEST_H__
#define __FIFO_TEST_H__

//#define HORIZONTAL_PIXEL_WIDTH 800
//#define VERTICAL_PIXEL_WIDTH 600

#define HORIZONTAL_PIXEL_WIDTH 640
#define VERTICAL_PIXEL_WIDTH 480

#endif


fifo_test.cpp を示す。まだ、指示子を入れていない状態だ。

// fifo_test.cpp
// 2016/09/26 by marsee
//

#include <stdio.h>
#include <string.h>
#include <ap_int.h>
#include <hls_stream.h>

#include "fifo_test.h"

int fifo_test(hls::stream<ap_uint<32> >& ins, hls::stream<ap_uint<32> >& outs){
    ap_uint<32> buf[HORIZONTAL_PIXEL_WIDTH];
    ap_uint<32> ind, outd;
    int i, j;

    input : for(i=0; i<HORIZONTAL_PIXEL_WIDTH; i++){
        ins >> buf[i];
    }

    output : for(j=0; j<HORIZONTAL_PIXEL_WIDTH; j++){
        while(i <= j) ; // i よりも j が等しいか大きい場合はwait
        outs << buf[j];
    }

    return 0;
}


テストベンチは、BMPファイルを読み込んで、fifo_test() を通したデータをもう一度、result_copy.bmp に書き込む。
テストベンチの fifo_test_tb.cpp を示す。

// fifo_test_tb.cpp
// 2016/09/26 by marsee
//

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ap_int.h>
#include <hls_stream.h>
#include <iostream>
#include <fstream>

#include "fifo_test.h"
#include "bmp_header.h"

int fifo_test(hls::stream<ap_uint<32> >& ins, hls::stream<ap_uint<32> >& outs);

#define BMP_FILE_NAME   "road_1.bmp"

int main()
{
    using namespace std;

    hls::stream<ap_uint<32> > ins;
    hls::stream<ap_uint<32> > outs;
    ap_uint<32> pix, result;

    BITMAPFILEHEADER bmpfhr; // BMPファイルのファイルヘッダ(for Read)
    BITMAPINFOHEADER bmpihr; // BMPファイルのINFOヘッダ(for Read)
    FILE *fbmpr, *fbmpw;
    ap_uint<32> *rd_bmp, *result_copy;
    int blue, green, red;

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

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

    if ((result_copy =(ap_uint<32> *)malloc(sizeof(int) * (bmpihr.biWidth * bmpihr.biHeight))) == NULL){
        fprintf(stderr, "Can't allocate result_copy 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] = (blue & 0xff) | ((green & 0xff)<<8) | ((red & 0xff)<<16);
        }
    }
    fclose(fbmpr);

    for(int j=0; j < bmpihr.biHeight; j++){
        for(int i=0; i < bmpihr.biWidth; i++){
            pix = rd_bmp[(j*bmpihr.biWidth)+i];
            ins << pix;
        }
    }

    // 1行容量の fifo_test を呼び出しながら、malloc 領域に書き込む
    for(int j=0; j < bmpihr.biHeight; j++){
        fifo_test(ins, outs);

        for(int i=0; i < bmpihr.biWidth; i++){
            outs >> result;
            result_copy[(j*bmpihr.biWidth)+i] = result;
        }
    }

    // ハードウェアのラプラシアンフィルタの結果を result_copy.bmp へ出力する
    if ((fbmpw=fopen("result_copy.bmp""wb")) == NULL){
        fprintf(stderr, "Can't open result_copy.bmp by binary write mode\n");
        exit(1);
    }
    // BMPファイルヘッダの書き込み
    fwrite(&bmpfhr.bfType, sizeof(char), 2, fbmpw);
    fwrite(&bmpfhr.bfSize, sizeof(long), 1, fbmpw);
    fwrite(&bmpfhr.bfReserved1, sizeof(short), 1, fbmpw);
    fwrite(&bmpfhr.bfReserved2, sizeof(short), 1, fbmpw);
    fwrite(&bmpfhr.bfOffBits, sizeof(long), 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 = result_copy[((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x] & 0xff;
            green = (result_copy[((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x] >> 8) & 0xff;
            red = (result_copy[((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x]>>16) & 0xff;

            fputc(blue, fbmpw);
            fputc(green, fbmpw);
            fputc(red, fbmpw);
        }
    }
    fclose(fbmpw);

    free(rd_bmp);
    free(result_copy);
}


Vivado HLS 2016.2 の fifo_test プロジェクトを示す。
fifo_test_1_160927.png

C シミュレーションは成功した。result_copy.bmp も road_1.bmp と同じだった。

次に、C コードの合成を行った。
fifo_test_2_160927.png

loop を見ると、input は指示子を入れて無くてもIteration Latency が 1 だが、output はIteration Latency が 2 になっている。

Analysis を見てみた。input と output の処理がよくわかる。
fifo_test_3_160927.png

Analysis でResource 画面にしてみよう。buf_V へのトランザクションがよくわかる。
fifo_test_4_160927.png

C/RTL協調シミュレーションを行った。1922 クロックだった。これは1行分だ。
fifo_test_5_160927.png

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

HLSストリームのデフォルトのIOレベルのプロトコルは ap_fifo になっているようだ。

波形を拡大して、1行分の画像のFIFO 処理を表示した。
fifo_test_7_160927.png

ins_V_V_read は1 になったら、1 のままで、1クロックごとに処理ができているのがわかる。
outs_V_V_write は1 になっても 0 になることがあるようだ。

outs_V_V_write がどうなっているのか?を見るために、波形を拡大した。
fifo_test_8_160927.png

outs_V_V_write は、合成レポートにもあったように、1クロックごとに 1 と 0 を繰り返している。つまり、2クロックで出力しているということになる。
  1. 2016年09月28日 04:41 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

コメント

コメントの投稿


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

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