FC2カウンター FPGAの部屋
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

Vivado HLS 2019.2 で krnl_lap_filter を作成する2(IP化)

Vivado HLS 2019.2 で krnl_lap_filter を作成する1(ソースコードの表示)”の続き。

前回は、ラプラシアン・フィルタ・カーネル krnl_lap_filter を作成するということで、ソースコードを貼った。今回は、Vivado HLS 2019.2 のプロジェクトを作成して、C シミュレーション、C コードの合成、C/RTL 協調シミュレーション、Export RTL を行っていこう。

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

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

C シミュレーション後には、lap.bmp が生成されていた。
streaming_kernel_40_200127.png

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

Latency の min は 3089 クロックだった。総ピクセル数が 3072 ピクセルなので、ほとんど 1 クロック/ピクセルとなっている。

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

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

outs_TVALID や ins_TREADY がほとんど 1 になっていて、スループットが高いことが分かる。

xo ファイルを生成するために extern "C" { } のコメントを外した。
streaming_kernel_44_200127.png

もう一度 C コードの合成を行ってから、Export RTL を行った。結果を示す。
streaming_kernel_45_200127.png

CP achieved post-implementation も 3.344 ns で問題無さそうだ。
krnl_lap_filter.xo ファイルが生成された。
streaming_kernel_46_200127.png
  1. 2020年01月28日 04:38 |
  2. Vitis
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS 2019.2 で krnl_lap_filter を作成する1(ソースコードの表示)

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

ストーミング接続用のhls::stream の定義は、hls::stream > にする必要がある。こうするとサイドチャネルの信号は last と keep , strb だけになる。この内の last を使用して、フレームの最後をマークしよう。
ストーミング接続用ラプラシアン・フィルタ・カーネルの krnl_lap_filter.cpp を示す。
なお、今回から、絶対値を取って、マイナスのエッジも表示するようにソースコードを変更した。
前回までは、ラプラシアン・フィルタを書ける前にフレームに同期していたが、今回は user 信号が無くなっているため、ラプラシアン・フィルタ後に last 信号で同期している。こうすると、最初のラプラシアン・フィルタは不成功でも last で同期できるので、その次のフレームは成功するようになる。

// krnl_lap_filter.cpp
// 2020/01/26 by marsse

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

// RGBからYへの変換
// RGBのフォーマットは、{8'd0, R(8bits), G(8bits), B(8bits)}, 1pixel = 32bits
// 輝度信号Yのみに変換する。変換式は、Y =  0.299R + 0.587G + 0.114B
// "YUVフォーマット及び YUV<->RGB変換"を参考にした。http://vision.kuee.kyoto-u.ac.jp/~hiroaki/firewire/yuv.html
// 2013/09/27 : float を止めて、すべてint にした
int32_t conv_rgb2y(int32_t rgb){
    int32_t r, g, b, y_f;
    int32_t y;

    b = rgb & 0xff;
    g = (rgb>>8) & 0xff;
    r = (rgb>>16) & 0xff;

    y_f = 77*r + 150*g + 29*b; //y_f = 0.299*r + 0.587*g + 0.114*b;の係数に256倍した
    y = y_f >> 8; // 256で割る

    return(y);
}

// ラプラシアンフィルタ
// x0y0 x1y0 x2y0 -1 -1 -1
// x0y1 x1y1 x2y1 -1  8 -1
// x0y2 x1y2 x2y2 -1 -1 -1
int32_t laplacian_fil(int32_t x0y0, int32_t x1y0, int32_t x2y0, int32_t x0y1,
        int32_t x1y1, int32_t x2y1, int32_t x0y2, int32_t x1y2, int32_t x2y2)
{
    int32_t y;

    y = -x0y0 -x1y0 -x2y0 -x0y1 +8*x1y1 -x2y1 -x0y2 -x1y2 -x2y2;
    if (y<0)
        y = -y;
    else if (y>255)
        y = 255;
    return(y);
}

//extern "C" {
void krnl_lap_filter(hls::stream<ap_axiu<32,0,0,0> >& ins, hls::stream<ap_axiu<32,0,0,0> >& outs,
        int32_t x_size, int32_t y_size){
#pragma HLS INTERFACE s_axilite port=y_size bundle=control
#pragma HLS INTERFACE s_axilite port=x_size bundle=control
#pragma HLS INTERFACE axis register both port=outs
#pragma HLS INTERFACE axis register both port=ins
#pragma HLS INTERFACE s_axilite port=return bundle=control
    ap_axiu<32,0,0,0> pix;
    ap_axiu<32,0,0,0> lap;

    int32_t line_buf[2][1920]; // supported HD resolution
#pragma HLS array_partition variable=line_buf block factor=2 dim=1
#pragma HLS resource variable=line_buf core=RAM_2P

    int32_t pix_mat[3][3];
#pragma HLS array_partition variable=pix_mat complete

    int32_t lap_fil_val;

    LOOP_X : for (int y=0; y<y_size; y++){
#pragma HLS LOOP_TRIPCOUNT min=48 max=600
        LOOP_Y : for (int x=0; x<x_size; x++){
#pragma HLS LOOP_TRIPCOUNT min=64 max=800
#pragma HLS PIPELINE II=1
            ins >> pix; // AXI4-Stream からの入力

            Loop4 : for (int k=0; k<3; k++){
                Loop5 : for (int m=0; m<2; m++){
#pragma HLS UNROLL
                    pix_mat[k][m] = pix_mat[k][m+1];
                }
            }
            pix_mat[0][2] = line_buf[0][x];
            pix_mat[1][2] = line_buf[1][x];

            int32_t y_val = conv_rgb2y(pix.data);
            pix_mat[2][2] = y_val;

            line_buf[0][x] = line_buf[1][x];    // 行の入れ替え
            line_buf[1][x] = y_val;

            lap_fil_val = laplacian_fil(    pix_mat[0][0], pix_mat[0][1], pix_mat[0][2],
                                            pix_mat[1][0], pix_mat[1][1], pix_mat[1][2],
                                            pix_mat[2][0], pix_mat[2][1], pix_mat[2][2]);
            lap.data = (lap_fil_val<<16)+(lap_fil_val<<8)+lap_fil_val; // RGB同じ値を入れる

            if (x<2 || y<2) // 最初の2行とその他の行の最初の2列は無効データなので0とする
                lap.data = 0;

            if (x==(x_size-1) && y==(y_size-1)) // フレームの最後で TLAST をアサートする
                lap.last = 1;
            else
                lap.last = 0;

            outs << lap;    // ストリームへ出力
        }
    }

    LOOP_WAIT_LAST: while(pix.last == 0) { // last が 1 になるまで待つ
#pragma HLS PIPELINE II=1
#pragma HLS LOOP_TRIPCOUNT min=1 max=1 avg=1
        ins >> pix;
    };
}
//}


次に、テストベンチの krnl_lap_filter_tb.cpp を示す。
今回は、OpenCV を使用している。Linux を使用している場合で Vivado HLS の C シミュレーションの時に warning: libjpeg.so.62 が出てしまう方は、”Ubuntu 16.04 上のVivado HLS 2017.2 でOpenCV を使用したプロジェクトでエラー発生”を参照ください。

// krnl_lap_filter_tb.cpp
// 2020/01/26 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 krnl_lap_filter(hls::stream<ap_axiu<32,0,0,0> >& ins, hls::stream<ap_axiu<32,0,0,0> >& outs,
        int32_t x_size, int32_t y_size);

void krnl_lap_filter_soft(hls::stream<ap_axiu<32,0,0,0> >& ins, hls::stream<ap_axiu<32,0,0,0> >& outs,
        int32_t x_size, int32_t y_size);

const char INPUT_BMP_FILE[] = "test.bmp";
const char OUTPUT_BMP_FILE[] = "lap.bmp";

int main(){
    hls::stream<ap_axiu<32,0,0,0> > ins;
    hls::stream<ap_axiu<32,0,0,0> > ins_soft;
    hls::stream<ap_axiu<32,0,0,0> > outs;
    hls::stream<ap_axiu<32,0,0,0> > outs_soft;
    ap_axiu<32,0,0,0> pix;
    ap_axiu<32,0,0,0> vals, vals_soft;

    // 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_lap(sizeof(int32_t)*(img.cols-2)*(img.rows-2));
    std::vector<int32_t> sw_lap(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;
        }
    }

    krnl_lap_filter(ins, outs,img.cols, img.rows);  // ハードウェアのラプラシアンフィルタ
    krnl_lap_filter_soft(ins_soft, outs_soft,img.cols, img.rows);   // ソフトウェアのラプラシアンフィルタ

    // ハードウェアとソフトウェアのラプラシアンフィルタの値のチェック
    for (int y=0; y<img.rows; y++){
        for (int x=0; x<img.cols; x++){
            outs >> vals;
            ap_int<32> val = vals.data;
            hw_lap[y*img.cols+x] = (int32_t)val;
            outs_soft >> vals_soft;
            ap_int<32> val_soft = vals_soft.data;
            if (val != val_soft){
                printf("ERROR HW and SW results mismatch x = %ld, y = %ld, HW = %x, SW = %x\n",
                        x, y, val, val_soft);
                return(1);
            }
        }
    }
    printf("Success HW and SW results match\n");

    const int lap_rows = img.rows;
    const int lap_cols = img.cols;
    cv::Mat wbmpf(lap_rows, lap_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_lap[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);
}

// RGBからYへの変換
// RGBのフォーマットは、{8'd0, R(8bits), G(8bits), B(8bits)}, 1pixel = 32bits
// 輝度信号Yのみに変換する。変換式は、Y =  0.299R + 0.587G + 0.114B
// "YUVフォーマット及び YUV<->RGB変換"を参考にした。http://vision.kuee.kyoto-u.ac.jp/~hiroaki/firewire/yuv.html
// 2013/09/27 : float を止めて、すべてint にした
int32_t conv_rgb2y_soft(int32_t rgb){
    int32_t r, g, b, y_f;
    int32_t y;

    b = rgb & 0xff;
    g = (rgb>>8) & 0xff;
    r = (rgb>>16) & 0xff;

    y_f = 77*r + 150*g + 29*b; //y_f = 0.299*r + 0.587*g + 0.114*b;の係数に256倍した
    y = y_f >> 8; // 256で割る

    return(y);
}

// ラプラシアンフィルタ
// x0y0 x1y0 x2y0 -1 -1 -1
// x0y1 x1y1 x2y1 -1  8 -1
// x0y2 x1y2 x2y2 -1 -1 -1
int32_t laplacian_fil_soft(int32_t x0y0, int32_t x1y0, int32_t x2y0, int32_t x0y1,
        int32_t x1y1, int32_t x2y1, int32_t x0y2, int32_t x1y2, int32_t x2y2)
{
    int32_t y;

    y = -x0y0 -x1y0 -x2y0 -x0y1 +8*x1y1 -x2y1 -x0y2 -x1y2 -x2y2;
    if (y<0)
        y = -y;
    else if (y>255)
        y = 255;
    return(y);
}

void krnl_lap_filter_soft(hls::stream<ap_axiu<32,0,0,0> >& ins, hls::stream<ap_axiu<32,0,0,0> >& outs,
        int32_t x_size, int32_t y_size){
    ap_axiu<32,0,0,0> pix;
    ap_axiu<32,0,0,0> lap;

    int32_t line_buf[2][1920]; // supported HD resolution

    int32_t pix_mat[3][3];
    int32_t lap_fil_val;

    LOOP_X : for (int y=0; y<y_size; y++){
        LOOP_Y : for (int x=0; x<x_size; x++){
            ins >> pix; // AXI4-Stream からの入力

            Loop4 : for (int k=0; k<3; k++){
                Loop5 : for (int m=0; m<2; m++){
                    pix_mat[k][m] = pix_mat[k][m+1];
                }
            }
            pix_mat[0][2] = line_buf[0][x];
            pix_mat[1][2] = line_buf[1][x];

            int32_t y_val = conv_rgb2y_soft(pix.data);
            pix_mat[2][2] = y_val;

            line_buf[0][x] = line_buf[1][x];    // 行の入れ替え
            line_buf[1][x] = y_val;

            lap_fil_val = laplacian_fil_soft(   pix_mat[0][0], pix_mat[0][1], pix_mat[0][2],
                                                pix_mat[1][0], pix_mat[1][1], pix_mat[1][2],
                                                pix_mat[2][0], pix_mat[2][1], pix_mat[2][2]);
            lap.data = (lap_fil_val<<16)+(lap_fil_val<<8)+lap_fil_val; // RGB同じ値を入れる

            if (x<2 || y<2) // 最初の2行とその他の行の最初の2列は無効データなので0とする
                lap.data = 0;

            if (x==(x_size-1) && y==(y_size-1)) // フレームの最後で TLAST をアサートする
                lap.last = 1;
            else
                lap.last = 0;

            outs << lap;    // ストリームへ出力
        }
    }

    LOOP_WAIT_LAST: while(pix.last == 0) { // last が 1 になるまで待つ
        ins >> pix;
    };
}

  1. 2020年01月27日 05:11 |
  2. Vitis
  3. | トラックバック:0
  4. | コメント:0

square root を Vivado HLS で実装する3

square root を Vivado HLS で実装する2”の続き。

前回は、square root を Vivado HLS で実装しようということで、逐次比較型ADコンバータのやり方を真似したsquare root を求めるソースコードを作って、C コードの合成とExport RTL を行った。しかし、使い物にならないということが分かった。前回までの square root ソースコードは、square root するビット長を可変にしていた。しかし、実際は、Sobel フィルタで使用するので、高々、結果が 8 ビット長なので、ビット長を 8 ビットに決め打ちにした。Vivado HLS もループ長を固定値にしたほうが最適化しやすいので、これでやってみよう。

ビット長を 8 ビットにした square_root8.cpp を示す。

// square_root8.cpp
// 2020/01/22 by marsee

#include <stdint.h>

int square_root8(int32_t val, int32_t *result){
#pragma HLS PIPELINE II=1
    int32_t temp = 0;
    int32_t square;

    for(int i=7; i>=0; --i){
        temp += (1 << i);
        square = temp * temp;

        if(square > val){
            temp -= (1 << i);
        }
    }
    *result = int32_t(temp);

    return(0);
}


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

// square_root8_tb.cpp
// 2020/01/22 by marsee

#include <stdio.h>
#include <stdint.h>

int square_root8(int32_t val, int32_t *result);

int main(){
    int64_t val;
    int32_t result;
    int32_t bit_len;

    for(int i=0; i<256; i++){
        square_root8(i, &result);
        printf("i = %d, result = %d\n", i, result);
    }

    int i = 255*255+1;
    square_root8(i, &result);
    printf("i = %d, result = %d\n", i, result);

    return(0);
}


Vivado HLS 2019.2 の square_root8 プロジェクトを作成した。
square_root_7_200124.png

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

問題ない。前回と同じだ。

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

Interval を見ると 1 クロックになっている。パイプラインはこうじゃないと。。。

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

C/RTL 協調シミュレーションの波形を示す。まずは全体の波形から。。。
square_root_11_200124.png

result_ap_vld が途中からずっと 1 なのが良いですね。切れ目なくデータ出ている。

最初の部分を拡大してみたが、result の値も問題ない。C シミュレーションで確認しているが。。。
square_root_12_200124.png

Export RTL を行った。タイミング的にも問題なかった。
square_root_13_200124.png

このように、結果のビット長を 8 ビット決め打ちにすれば、問題なくなった。このように、Vivado HLS でソースコードを作る時にいろいろなパラメータを可変にしていると、Vivado HLS のコンパイラの制約が厳しくなって、性能が取れないことがある。その場合は、決め打ちにできるパラメータを決定してコンパイラの制約をゆるくしてみると結果が改善することがある。

パラメータを変更したくなったら、またVivado HLS でコンパイルすれば良いじゃない。。。

(追加)
ソースコードをもう一例書いてみましたが、結果は上のソースコードと全く一緒でした。

// square_root8.cpp
// 2020/01/22 by marsee

#include <stdint.h>

int square_root8(int32_t val, int32_t *result){
#pragma HLS PIPELINE II=1
    int32_t temp = 0;
    int32_t tempn = 0;
    int32_t square;

    for(int i=7; i>=0; --i){
        tempn = temp + (1 << i);
        square = tempn * tempn;

        if(square <= val){
            temp = tempn ;
        }
    }
    *result = int32_t(temp);

    return(0);
}

  1. 2020年01月25日 04:31 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

square root を Vivado HLS で実装する2

square root を Vivado HLS で実装する1”の続き。

square root を Vivado HLS で実装しようということで、逐次比較型ADコンバータのやり方を真似したsquare root を求めるソースコードを作って、前回貼った。そして、テストベンチのソースコードも貼って、C シミュレーションを行った。
今回は、C コードの合成を行って、Export RTL で Vivado のインプリメンテーションの結果を見てみよう。

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

Latency が大きいので、for 文に PIPELINE 指示子を入れてみよう。
square_root_4_200123.png

これでもう一度、C コードの合成を行った。結果をもう一度示す。
square_root_5_200123.png

PIPELINE 指示子を入れているのに Interval が min が 4 クロックで、max が 19 クロックだった。これでは 1 クロック毎にパイプラインしているとは言えない。。。
次回で何とか解決方法を考える。

Export RTL を行った。
square_root_6_200123.png

タイミングがメットしていない。
  1. 2020年01月24日 04:44 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

square root を Vivado HLS で実装する1

square root を Vivado HLS で実装しようと思う。厳密な square root ではなく、結果が 8 ビットの整数で表されればよい。つまり、 Sobel フィルタを実装したいので、x と y を 2 乗して足して、square Root したいので、RGB の値の範囲で square root できれば十分なのだ。

アルゴリズムを考えていたら、ちょうど逐次比較型ADコンバータが思い浮かんだ。これは、MSB からビットを立てながら、入力値と比較して、大きい場合はビットをクリアし、まだ小さい値ならばビットをそのままにする。そして、その下のビットを立てながら同じことをやっていく。
square root もそれが使えるんじゃないだろうか?ということで、逐次比較型ADコンバータと同様に square root を計算するように作ったソースコードの square_root.cpp を示す。

// square_root.cpp
// 2020/01/22 by marsee

#include <stdint.h>

int square_root(int64_t val, int32_t *result, int32_t bit_len){
    int64_t temp = 0;
    int64_t square;

    for(int i=(bit_len-1); i>=0; --i){
#pragma HLS LOOP_TRIPCOUNT min=1 max=16 avg=7
        temp += (1 << i);
        square = temp * temp;

        if(square > val){
            temp -= (1 << i);
        }
    }
    *result = int32_t(temp);

    return(0);
}


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

// square_root_tb.cpp
// 2020/01/22 by marsee

#include <stdio.h>
#include <stdint.h>

int square_root(int64_t val, int32_t *result, int32_t bit_len);

int main(){
    int64_t val;
    int32_t result;
    int32_t bit_len;

    for(int i=0; i<256; i++){
        square_root(i, &result, 8);
        printf("i = %d, result = %d\n", i, result);
    }

    int i=255*255+1;
    square_root(i, &result, 8);
    printf("i = %d, result = %d\n", i, result);

    return(0);
}


Vivado HLS 2019.2 で ZYBO Z7-10 用に square_root プロジェクトを作成した。
square_root_1_200123.png

C シミュレーションを行った。うまく行っているようだ。
square_root_2_200123.png

すべての結果を示す。

INFO: [SIM 2] *************** CSIM start ***************
INFO: [SIM 4] CSIM will launch GCC as the compiler.
   Compiling ../../../square_root_tb.cpp in debug mode
   Generating csim.exe
i = 0, result = 0
i = 1, result = 1
i = 2, result = 1
i = 3, result = 1
i = 4, result = 2
i = 5, result = 2
i = 6, result = 2
i = 7, result = 2
i = 8, result = 2
i = 9, result = 3
i = 10, result = 3
i = 11, result = 3
i = 12, result = 3
i = 13, result = 3
i = 14, result = 3
i = 15, result = 3
i = 16, result = 4
i = 17, result = 4
i = 18, result = 4
i = 19, result = 4
i = 20, result = 4
i = 21, result = 4
i = 22, result = 4
i = 23, result = 4
i = 24, result = 4
i = 25, result = 5
i = 26, result = 5
i = 27, result = 5
i = 28, result = 5
i = 29, result = 5
i = 30, result = 5
i = 31, result = 5
i = 32, result = 5
i = 33, result = 5
i = 34, result = 5
i = 35, result = 5
i = 36, result = 6
i = 37, result = 6
i = 38, result = 6
i = 39, result = 6
i = 40, result = 6
i = 41, result = 6
i = 42, result = 6
i = 43, result = 6
i = 44, result = 6
i = 45, result = 6
i = 46, result = 6
i = 47, result = 6
i = 48, result = 6
i = 49, result = 7
i = 50, result = 7
i = 51, result = 7
i = 52, result = 7
i = 53, result = 7
i = 54, result = 7
i = 55, result = 7
i = 56, result = 7
i = 57, result = 7
i = 58, result = 7
i = 59, result = 7
i = 60, result = 7
i = 61, result = 7
i = 62, result = 7
i = 63, result = 7
i = 64, result = 8
i = 65, result = 8
i = 66, result = 8
i = 67, result = 8
i = 68, result = 8
i = 69, result = 8
i = 70, result = 8
i = 71, result = 8
i = 72, result = 8
i = 73, result = 8
i = 74, result = 8
i = 75, result = 8
i = 76, result = 8
i = 77, result = 8
i = 78, result = 8
i = 79, result = 8
i = 80, result = 8
i = 81, result = 9
i = 82, result = 9
i = 83, result = 9
i = 84, result = 9
i = 85, result = 9
i = 86, result = 9
i = 87, result = 9
i = 88, result = 9
i = 89, result = 9
i = 90, result = 9
i = 91, result = 9
i = 92, result = 9
i = 93, result = 9
i = 94, result = 9
i = 95, result = 9
i = 96, result = 9
i = 97, result = 9
i = 98, result = 9
i = 99, result = 9
i = 100, result = 10
i = 101, result = 10
i = 102, result = 10
i = 103, result = 10
i = 104, result = 10
i = 105, result = 10
i = 106, result = 10
i = 107, result = 10
i = 108, result = 10
i = 109, result = 10
i = 110, result = 10
i = 111, result = 10
i = 112, result = 10
i = 113, result = 10
i = 114, result = 10
i = 115, result = 10
i = 116, result = 10
i = 117, result = 10
i = 118, result = 10
i = 119, result = 10
i = 120, result = 10
i = 121, result = 11
i = 122, result = 11
i = 123, result = 11
i = 124, result = 11
i = 125, result = 11
i = 126, result = 11
i = 127, result = 11
i = 128, result = 11
i = 129, result = 11
i = 130, result = 11
i = 131, result = 11
i = 132, result = 11
i = 133, result = 11
i = 134, result = 11
i = 135, result = 11
i = 136, result = 11
i = 137, result = 11
i = 138, result = 11
i = 139, result = 11
i = 140, result = 11
i = 141, result = 11
i = 142, result = 11
i = 143, result = 11
i = 144, result = 12
i = 145, result = 12
i = 146, result = 12
i = 147, result = 12
i = 148, result = 12
i = 149, result = 12
i = 150, result = 12
i = 151, result = 12
i = 152, result = 12
i = 153, result = 12
i = 154, result = 12
i = 155, result = 12
i = 156, result = 12
i = 157, result = 12
i = 158, result = 12
i = 159, result = 12
i = 160, result = 12
i = 161, result = 12
i = 162, result = 12
i = 163, result = 12
i = 164, result = 12
i = 165, result = 12
i = 166, result = 12
i = 167, result = 12
i = 168, result = 12
i = 169, result = 13
i = 170, result = 13
i = 171, result = 13
i = 172, result = 13
i = 173, result = 13
i = 174, result = 13
i = 175, result = 13
i = 176, result = 13
i = 177, result = 13
i = 178, result = 13
i = 179, result = 13
i = 180, result = 13
i = 181, result = 13
i = 182, result = 13
i = 183, result = 13
i = 184, result = 13
i = 185, result = 13
i = 186, result = 13
i = 187, result = 13
i = 188, result = 13
i = 189, result = 13
i = 190, result = 13
i = 191, result = 13
i = 192, result = 13
i = 193, result = 13
i = 194, result = 13
i = 195, result = 13
i = 196, result = 14
i = 197, result = 14
i = 198, result = 14
i = 199, result = 14
i = 200, result = 14
i = 201, result = 14
i = 202, result = 14
i = 203, result = 14
i = 204, result = 14
i = 205, result = 14
i = 206, result = 14
i = 207, result = 14
i = 208, result = 14
i = 209, result = 14
i = 210, result = 14
i = 211, result = 14
i = 212, result = 14
i = 213, result = 14
i = 214, result = 14
i = 215, result = 14
i = 216, result = 14
i = 217, result = 14
i = 218, result = 14
i = 219, result = 14
i = 220, result = 14
i = 221, result = 14
i = 222, result = 14
i = 223, result = 14
i = 224, result = 14
i = 225, result = 15
i = 226, result = 15
i = 227, result = 15
i = 228, result = 15
i = 229, result = 15
i = 230, result = 15
i = 231, result = 15
i = 232, result = 15
i = 233, result = 15
i = 234, result = 15
i = 235, result = 15
i = 236, result = 15
i = 237, result = 15
i = 238, result = 15
i = 239, result = 15
i = 240, result = 15
i = 241, result = 15
i = 242, result = 15
i = 243, result = 15
i = 244, result = 15
i = 245, result = 15
i = 246, result = 15
i = 247, result = 15
i = 248, result = 15
i = 249, result = 15
i = 250, result = 15
i = 251, result = 15
i = 252, result = 15
i = 253, result = 15
i = 254, result = 15
i = 255, result = 15
i = 65026, result = 255
INFO: [SIM 1] CSim done with 0 errors.
INFO: [SIM 3] *************** CSIM finish ***************

  1. 2020年01月23日 04:58 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS 2019.2 で krnl_dma_read を作成する2(IP 化)

Vivado HLS 2019.2 で krnl_dma_read を作成する1(ソースコードの表示)”の続き。

前回は、Vitis のカーネル間ストーミング接続をテストするために DMA Read カーネル ー ラプラシアン・フィルタ・カーネル ー DMA Write カーネルをストーミング接続してみようということで、最初にDMA Read を作ることにした。そして、ソースコードを貼った。今回は、それらのソースコードを使用して、Vivado HLS 2019.2 で C シミュレーション、C コードの合成、C/RTL 協調シミュレーション、Export RTL を行った。

Vivado HLS 2019.2 で Krnl_dma_read プロジェクトを作成した。その際に New Vivado HLS Project のダイアログで Vitis Bottom Up Flow にチェックを入れた。
streaming_kernel_34_200122.png

Source に krnl_dma_read.cpp を入れて、Test Bench に bmp_header.h, krnl_dma_read_tb.cpp, test.bmp を入れた。
streaming_kernel_35_200122.png

最初に C シミュレーションを行った。
streaming_kernel_27_200121.png

dma_read.bmp が生成されていた。成功だ。
streaming_kernel_30_200121.png

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

Latency の min の 3153 クロック / 3072 ピクセル ≒ 1.03 クロック/ピクセルだった。

C/RTL 協調シミュレーションを行った。Latency は 3488 クロックだった。
streaming_kernel_29_200121.png

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

outs_TVALID の波形が途中で切れ気味だが、だいたいOKだろう。

ここで、krnl_dma_read.cpp の extern "C" { } のコメントアウトを外して C コードの合成を行った。
引き続きExport RTL を行った。結果を示す。
streaming_kernel_32_200121.png

dma_read.xo もできた。
streaming_kernel_36_200122.png

とりあえず、krnl_dma_read.cpp を使用して、Vitis のカーネルのストーミング接続をテストするが、その後で、 xo ファイルを使用して、Vitis のカーネルのストーミング接続をテストしてみよう。
  1. 2020年01月22日 05:01 |
  2. Vitis
  3. | トラックバック:0
  4. | コメント:0
»