FC2カウンター FPGAの部屋 Vivado HLS

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

FPGAの部屋

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

HLS ビデオライブラリの hls::Window のVivado HLS 2015.4 以前とその後での変更点3(ソースコード)

HLS ビデオライブラリの hls::LineBuffer のVivado HLS 2015.4 以前とその後での変更点1(メモリ・ライン・バッファー)
HLS ビデオライブラリの hls::Window のVivado HLS 2015.4 以前とその後での変更点2(メモリ・ウインドウ・バッファー)
でメモリ・ライン・バッファーとメモリ・ウインドウ・バッファーのVivado HLS 2015.4 以前と以後の変更点を説明してきた。
今回は、ソースコードを貼っておこうと思う。Vivado HLSのソースコードはVivado HLS 2016.1 以降のメモリ・ライン・バッファーとメモリ・ウインドウ・バッファーのメソッドを使用している。それに対して、テストベンチはVivado HLS 2015.4 以前のメソッドを使用しているので、比べてみてほしい。
なお、これらのコードはガボール・フィルタのプロジェクトを使用している。

まずは、CソースコードのGabor_fiter_lh.cpp から貼っておく。

// Gabor_fiter_lh.cpp
// 2016/07/23 by marsee
// 2016/07/25 : 右白線検出用のGabor Filterを追加して、右左の白線を指定するRorL 引数を追加
// 2016/07/27 : 右白線検出用配列と左白線検出用配列を統合
//

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

#include "Gabor_filter_lh.h"

int conv_rgb2y(int rgb);

int Gabor_filter_lh(hls::stream<ap_axis<32,1,1,1> >& ins,
        hls::stream<ap_axis<32,1,1,1> >& outs, ap_uint<1> & RorL){
#pragma HLS INTERFACE ap_none port=RorL
#pragma HLS INTERFACE axis port=ins
#pragma HLS INTERFACE axis port=outs
#pragma HLS INTERFACE s_axilite port=return

    ap_axis<32,1,1,1> pix;
    ap_axis<32,1,1,1> gabor;

    hls::LineBuffer<ARRAY_SIZE-1, HORIZONTAL_PIXEL_WIDTH, int> linebuf;
    hls::Window<ARRAY_SIZE, ARRAY_SIZE, int> mbuf;

    int gray_pix, val, i, j, x, y;

    do {
#pragma HLS LOOP_TRIPCOUNT min=1 max=1 avg=1
    // user が 1になった時にフレームがスタートする
        ins >> pix;
    } while(pix.user == 0);

    for (y=0; y<VERTICAL_PIXEL_WIDTH; y++){
        for (x=0; x<HORIZONTAL_PIXEL_WIDTH; x++){
#pragma HLS PIPELINE II=1
            if (!(x==0 && y==0))    // 最初の入力はすでに入力されている
                ins >> pix;    // AXI4-Stream からの入力

            mbuf.shift_pixels_left();    // mbuf の列を1ビット左シフト
            for(i=0; i<ARRAY_SIZE-1; i++){
                mbuf.insert_pixel(linebuf.getval(i,x), i, ARRAY_SIZE-1);
            }
            gray_pix = conv_rgb2y(pix.data);
            mbuf.insert_pixel(gray_pix, ARRAY_SIZE-1, ARRAY_SIZE-1);

            // LineBuffer の更新
            linebuf.shift_pixels_up(x);
            linebuf.insert_bottom_row(gray_pix, x);

            // Gabor filter の演算
            for (j=0, val=0; j<ARRAY_SIZE-1; j++){
                for (i=0; i<ARRAY_SIZE-1; i++){
                    val += gabor_weight[(int)RorL][j][i] * mbuf(j,i);
                }
            }
            val = val/256// 256倍してあるので、1/256して戻す
            if (val<0)
                //val = -val; // 絶対値
                val = 0// マイナスの値を0に丸める
            else if (val>255)
                val = 255;

            // Gabor filter・データの書き込み
            gabor.data = (val<<16)+(val<<8)+val;
            // 最初のARRAY_SIZE-1行とその他の行の最初のARRAY_SIZE-1列は無効データなので0とする
            if (x<(ARRAY_SIZE-1) || y<(ARRAY_SIZE-1))
                gabor.data = 0;

            if (x==0 && y==0// 最初のデータでは、TUSERをアサートする
                gabor.user = 1;
            else
                gabor.user = 0;

            if (x == (HORIZONTAL_PIXEL_WIDTH-1))    // 行の最後で TLAST をアサートする
                gabor.last = 1;
            else
                gabor.last = 0;

            outs << gabor;    // AXI4-Stream へ出力
         }
     }
     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 にした
int conv_rgb2y(int rgb){
    int r, g, b, y_f;
    int 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);
}


次に、テストベンチのGabor_filter_lh_tb.cpp を貼っておく。なお、メモリ・ライン・バッファーとメモリ・ウインドウ・バッファーのVivado HLS 2015.4 以前のコードが書いてある関数は、Gabor_filter_lh_soft() だ。

// Gabor_filter_lh_tb.cpp
// 2016/07/24 by marsee
// 2016/07/25 : 右白線検出用のGabor Filterを追加して、右左の白線を指定するRorL 引数を追加
//

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

#include "Gabor_filter_lh.h"
#include "bmp_header.h"

int Gabor_filter_lh(hls::stream<ap_axis<32,1,1,1> >& ins, hls::stream<ap_axis<32,1,1,1> >& outs, ap_uint<1> & RorL);

int conv_rgb2y_soft(int rgb);
int Gabor_filter_lh_soft(hls::stream<ap_axis<32,1,1,1> >& ins, hls::stream<ap_axis<32,1,1,1> >& outs, ap_uint<1> & RorL);

#define CLOCK_PERIOD 10
#define RIGHT_OR_LEFT  LEFT_WEIGHT
#define BMP_FILE_NAME   "bmp_f_0823_0.bmp"

int main()
{
    using namespace std;

    hls::stream<ap_axis<32,1,1,1> > ins;
    hls::stream<ap_axis<32,1,1,1> > ins_soft;
    hls::stream<ap_axis<32,1,1,1> > outs;
    hls::stream<ap_axis<32,1,1,1> > outs_soft;
    ap_axis<32,1,1,1> pix;
    ap_axis<32,1,1,1> vals;
    ap_axis<32,1,1,1> vals_soft;

    int m_seq = 1// M系列の値
    int i;
    int xor_shift;

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

    if ((fbmpr = fopen(BMP_FILE_NAME, "rb")) == NULL){ // test.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 =(int *)malloc(sizeof(int) * (bmpihr.biWidth * bmpihr.biHeight))) == NULL){
        fprintf(stderr, "Can't allocate rd_bmp memory\n");
        exit(1);
    }
    if ((hw_gabor =(int *)malloc(sizeof(int) * (bmpihr.biWidth * bmpihr.biHeight))) == NULL){
        fprintf(stderr, "Can't allocate hw_gabor memory\n");
        exit(1);
    }
    if ((sw_gabor =(int *)malloc(sizeof(int) * (bmpihr.biWidth * bmpihr.biHeight))) == NULL){
        fprintf(stderr, "Can't allocate hw_gabor 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);

    // ins に入力データを用意する
    for(int i=0; i<5; i++){    // dummy data
        pix.user = 0;
        pix.data = i;
        ins << pix;
    }

    for(int j=0; j < bmpihr.biHeight; j++){
        for(i=0; i < bmpihr.biWidth; i++){
            pix.data = (ap_int<32>)rd_bmp[(j*bmpihr.biWidth)+i];

            if (j==0 && i==0)    // 最初のデータの時に TUSER を 1 にする
                pix.user = 1;
            else
                pix.user = 0;

            if (i == bmpihr.biWidth-1// 行の最後でTLASTをアサートする
                pix.last = 1;
            else
                pix.last = 0;

            ins << pix;
            ins_soft << pix;
        }
    }

    r_l = (ap_uint<1>)RIGHT_OR_LEFT;
    Gabor_filter_lh(ins, outs, r_l);
    Gabor_filter_lh_soft(ins_soft, outs_soft, r_l);

    // ハードウェアとソフトウェアのラプラシアン・フィルタの値のチェック
    cout << endl;
    cout << "outs" << endl;
    for(int j=0; j < bmpihr.biHeight; j++){
        for(i=0; i < bmpihr.biWidth; i++){
            outs >> vals;
            outs_soft >> vals_soft;
            ap_int<32> val = vals.data;
            ap_int<32> val_soft = vals_soft.data;

            hw_gabor[(j*bmpihr.biWidth)+i] = (int)val;
            sw_gabor[(j*bmpihr.biWidth)+i] = (int)val_soft;

            if ((double)pow((double)(val&0xff)-(val_soft&0xff),(double)2) > 4){ // 2乗誤差が4よりも大きい
                printf("ERROR HW and SW results mismatch i = %ld, j = %ld, HW = %08x, SW = %08x\n", i, j, (int)val, (int)val_soft);
                //return(1);
            }
            //if (vals.last)
                //cout << "AXI-Stream is end" << endl;
        }
    }
    cout << "Success HW and SW results match" << endl;
    cout << endl;

    // ハードウェアのラプラシアンフィルタの結果を temp_gabor.bmp へ出力する
    if ((fbmpw=fopen("temp_gabor.bmp""wb")) == NULL){
        fprintf(stderr, "Can't open temp_gabor.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 = hw_gabor[((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x] & 0xff;
            green = (hw_gabor[((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x] >> 8) & 0xff;
            red = (hw_gabor[((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x]>>16) & 0xff;

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

    // ソフトウェアのラプラシアンフィルタの結果を temp_gabor_float.bmp へ出力する
    if ((fbmpwf=fopen("temp_gabor_float.bmp""wb")) == NULL){
        fprintf(stderr, "Can't open temp_gabor_float.bmp by binary write mode\n");
        exit(1);
    }
    // BMPファイルヘッダの書き込み
    fwrite(&bmpfhr.bfType, sizeof(char), 2, fbmpwf);
    fwrite(&bmpfhr.bfSize, sizeof(long), 1, fbmpwf);
    fwrite(&bmpfhr.bfReserved1, sizeof(short), 1, fbmpwf);
    fwrite(&bmpfhr.bfReserved2, sizeof(short), 1, fbmpwf);
    fwrite(&bmpfhr.bfOffBits, sizeof(long), 1, fbmpwf);
    fwrite(&bmpihr, sizeof(BITMAPINFOHEADER), 1, fbmpwf);
    // RGB データの書き込み、逆順にする
    for (int y=0; y<bmpihr.biHeight; y++){
        for (int x=0; x<bmpihr.biWidth; x++){
            blue = sw_gabor[((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x] & 0xff;
            green = (sw_gabor[((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x] >> 8) & 0xff;
            red = (sw_gabor[((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x]>>16) & 0xff;

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

    free(rd_bmp);
    free(hw_gabor);

    return 0;
}

int Gabor_filter_lh_soft(hls::stream<ap_axis<32,1,1,1> >& ins, hls::stream<ap_axis<32,1,1,1> >& outs, ap_uint<1> & RorL){
    ap_axis<32,1,1,1> pix;
    ap_axis<32,1,1,1> gabor;

    hls::LineBuffer<ARRAY_SIZE-1, HORIZONTAL_PIXEL_WIDTH, int> linebuf;
    hls::Window<ARRAY_SIZE, ARRAY_SIZE, int> mbuf;

    int gray_pix, val, i, j, x, y;
    float valf;

    do {    // user が 1になった時にフレームがスタートする
        ins >> pix;
    } while(pix.user == 0);

    for (y=0; y<VERTICAL_PIXEL_WIDTH; y++){
        for (x=0; x<HORIZONTAL_PIXEL_WIDTH; x++){
            if (!(x==0 && y==0))    // 最初の入力はすでに入力されている
                ins >> pix;    // AXI4-Stream からの入力

            mbuf.shift_left();    // mbuf の列を1ビット左シフト
            for(i=ARRAY_SIZE-2; i>=0; --i){
                mbuf.insert(linebuf(i,x), i+1, ARRAY_SIZE-1);
            }
            gray_pix = conv_rgb2y_soft(pix.data);
            mbuf.insert(gray_pix, 0, ARRAY_SIZE-1);

            // LineBuffer の更新
            linebuf.shift_down(x);
            linebuf.insert_bottom(gray_pix, x);

            // Gabor filter の演算
            for (j=0, valf=0; j<ARRAY_SIZE-1; j++){
                for (i=0; i<ARRAY_SIZE-1; i++){
                    valf += gabor_fweight[(int)RorL][j][i] * (float)mbuf(ARRAY_SIZE-1-j,i);
                }
            }

            val = (int)valf;
            if (val<0)
                //val = -val; // 絶対値
                val = 0// マイナスの値を0に丸める
            else if (val>255)
                val = 255;

            // Gabor filter・データの書き込み
            gabor.data = (val<<16)+(val<<8)+val;
            // 最初のARRAY_SIZE-1行とその他の行の最初のARRAY_SIZE-1列は無効データなので0とする
            if (x<(ARRAY_SIZE-1) || y<(ARRAY_SIZE-1))
                gabor.data = 0;

            if (x==0 && y==0// 最初のデータでは、TUSERをアサートする
                gabor.user = 1;
            else
                gabor.user = 0;

            if (x == (HORIZONTAL_PIXEL_WIDTH-1))    // 行の最後で TLAST をアサートする
                gabor.last = 1;
            else
                gabor.last = 0;

            outs << gabor;    // AXI4-Stream へ出力
         }
     }
     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 にした
int conv_rgb2y_soft(int rgb){
    int r, g, b, y_f;
    int 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);
}

  1. 2017年07月27日 05:34 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

HLS ビデオライブラリの hls::Window のVivado HLS 2015.4 以前とその後での変更点2(メモリ・ウインドウ・バッファー)

HLS ビデオライブラリの hls::LineBuffer のVivado HLS 2015.4 以前とその後での変更点”の続き。

hls::LineBuffer と同様に、hls::Window も行のインデックスとクラス・メソッドが変更になっているので紹介する。
hls::LineBuffer と同様に、hls::Window もガボール・フィルタでカーネルとして、重みとの演算に使用されている。

Vivado HLS 2015.4 以前のメモリ・ウインドウのデータ セットを引用する。”Vivado Design Suite ユーザー ガイド 高位合成 UG902 (v2015.4) 2015 年 11 月 24 日”の”メモリ・ウインドウ・バッファー”の 194 ページの”表 2‐10 : メモリ ウ ィ ン ド ウ例のデー タ セッ ト”を引用する。
gabor_filter_lh2_3_170725.png

やはり、行が行2 から始まって、行1 、行0 とインデックスが減っているのが分かるだろうか?列は列0 から始まって増えていっている。メモリ・ウインドウを更新する際には、shift_left() メソッドでメモリ・ウインドウを1ビット左シフトし、insert()メソッドで、インデック0 以外にメモリ・ライン・バッファーの値を入力し、インデック0 には、現在の画像ピクセルの値を入力した。つまり、通常と逆のインデックスになっている。ソースコードを示す。

            mbuf.shift_left();    // mbuf の列を1ビット左シフト
            for(i=ARRAY_SIZE-2; i>=0; --i){
                mbuf.insert(linebuf(i,x), i+1, ARRAY_SIZE-1);
            }
            gray_pix = conv_rgb2y_soft(pix.data);
            mbuf.insert(gray_pix, 0, ARRAY_SIZE-1);


前回同様に、Vivado HLS 2016.1 からは、この行のインデックスが通常と同様にインデックス 0 からとなっている。
下に、”Vivado Design Suite ユーザー ガイド 高位合成 UG902 (v2017.1) 2017 年 4 月 5 日”の”HLS ビデオライブラリ”の”メモリ ウィンドウ バッファー ”の221 ページの”2‐9: メモリ ウ ィ ン ド ウ例のデー タ セット”を引用する。
gabor_filter_lh2_4_170725.png

行のインデックスが列のインデックス同様に 0 から始まっているのが分かると思う。メモリ・ウインドウ・バッファーのメソッドも更新されている。メモリ・ウインドウ・バッファーのメソッドを示す。

• shift_pixels_up()
• shift_pixels_down()
• shift_pixels_left()
• shift_pixels_right()
• insert_pixel(value,row,colum)
• insert_row()
• insert_bottom_row()
• insert_top_row()
• insert_col()
• insert_left_col()
• insert_right_col()
• getval(row, column)


やはり、今まで使用していたshifft_left(), insert() が無くなっている。前回同様、互換性確保のためにshifft_left(), insert() メソッドなどの、Vivado HLS 2015.4 以前のコードも使用することができた。Vivado HLS 2016.1 以降の新しいメソッドを使用して、同様にメモリ・ウインドウの処理をする場合には、shift_pixels_left() と insert_pixel() を使用する。
新しいメソッドを使用した上と同様のコードを示す。

            mbuf.shift_pixels_left();    // mbuf の列を1ビット左シフト
            for(i=0; i<ARRAY_SIZE-1; i++){
                mbuf.insert_pixel(linebuf.getval(i,x), i, ARRAY_SIZE-1);
            }
            gray_pix = conv_rgb2y(pix.data);
            mbuf.insert_pixel(gray_pix, ARRAY_SIZE-1, ARRAY_SIZE-1);

  1. 2017年07月26日 05:00 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

HLS ビデオライブラリの hls::LineBuffer のVivado HLS 2015.4 以前とその後での変更点1(メモリ・ライン・バッファー)

ガボール・フィルタ (Gabor Filter) による白線検出5(hls::LineBuffer と hls::Window を使用2)”などで、Vivado HLS のHLS ビデオライブラリのhls::LineBuffer と hls::Window を使用してガボール・フィルタを実装した。

この時は知らなかったが、Vivado HLS 2016.1 から hls::LineBuffer と hls::Window のクラス・メソッドが大きく変更になった。Vivado HLS 2015.4 までは、 hls::LineBuffer と hls::Window の配列の並びは、列はインデックス 0 からなのだが、行はインデックス「行の配列の最大数」かになっていた。”Vivado Design Suite ユーザー ガイド 高位合成 UG902 (v2015.4) 2015 年 11 月 24 日”の”メモリ・ライン・バッファー”の193 ページの”表 2‐7 : LineBuffer 例のデー タ セッ ト”を引用する。
gabor_filter_lh2_1_170725.png

行が行2 から始まって、行1 、行0 とインデックスが減っているのが分かるだろうか?列は列0 から始まって増えていっている。
メモリ・ライン・バッファーの値を更新するためにメモリ・ライン・バッファーの現在処理している列を上方向にシフトするのだが、Vivado HLS 2015.4 まででは、shift_down() メソッドを使用する。とっても紛らわしい。その後、現在のピクセル値を挿入するには、insert_bottom() メソッドを使用する。

Vivado HLS 2016.1 からは、この行のインデックスが通常と同様にインデックス 0 からとなっている。
下に、”Vivado Design Suite ユーザー ガイド 高位合成 UG902 (v2017.1) 2017 年 4 月 5 日”の”HLS ビデオライブラリ”の ” 2‐6: LineBuffer 例のデー タ セッ ト”を引用する。
gabor_filter_lh2_2_170725.png

行のインデックスが列のインデックス同様に 0 から始まっているのが分かると思う。
メモリ・ライン・バッファーのメソッドも更新されている。下に、メモリ・ライン・バッファーのメソッドを示す。

• shift_pixels_up()
• shift_pixels_down()
• insert_bottom_row()
• insert_top_row()
• getval(row,column)


上に示すように、今まで使用していた shift_down() メソッドが無くなっている。ただし、互換性確保のためにshift_down() メソッドというか、Vivado HLS 2015.4 以前のコードも使用することができた。
Vivado HLS 2015.4 以前のコードをVivado HLS 2016.1 以降のコードに書き直すと、shift_pixels_up() メソッドで現在処理している列を上方向にシフトし、insert_bottom_row() で現在のピクセル値を挿入する。
  1. 2017年07月25日 04:47 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Bash on Windows 10にVivado WebPACK 2017.2をインストールした3(Vivado HLS)

Bash on Windows 10にVivado WebPACK 2017.2をインストールした2(日本語表示)”の続き。

前回は、Bash on Windows 10 の日本語表示について書いた。今回は、Bash on Windows 10 でVivado_HLSを使ってみよう。

PowerShell に vivado_hls & と入力して、Vivado HLS 2017.2 を立ち上げた。

Vivado HLS 2017.2 が立ち上がった。
Win10_Ubuntu_Vivado_24_170720.png

mnist_conv_nn10 プロジェクトを開いた。
Win10_Ubuntu_Vivado_20_170720.png

表示されているのは、Window 10 上でC コードの合成を行ったときの合成結果だ。

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

out of memory allocating エラーで止まってしまった。これはWindow 10 上でVivado HLS を使用したときと同様の結果だ。

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

エラーが発生した。
詳しく見てみよう。
Win10_Ubuntu_Vivado_23_170720.png

/opt/Xilinx/Vivado_HLS/2017.2/lnx64/tools/gcc/lib/gcc/x86_64-unknown-linux-gnu/4.6.3/../../../../include/c++/4.6.3/x86_64-unknown-linux-gnu/bits/os_defines.h で'features.h' file not found だそうだ。
自分のところのコードの問題ではなくXilinx 社のコードでの問題のようだ。

Bash on Windows 10 のVivado HLS はうまく動かいないし、C シミュレーションのコンパイラのWindow 10 と同じようなので、メリットが無い。また、C コードの合成はエラーになってしまうことがあるようだ。(ap_fixed を使用していないC ソースコードでは合成できるか?は確かめていない)
  1. 2017年07月20日 05:03 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS の浮動小数点型を使用した内積の計算

Vivado HLS の任意精度固定小数点型を使用した内積の計算”のおまけ。

任意精度固定小数点型の比較のために作った浮動小数点数の内積の計算だが、思ったより使用リソースが少なかったので、書いておくことにした。

まずはnn_test_float プロジェクトを示す。
ap_fixed_dot_8_170601.png

次にソースコードを示す。
まずは、nn_test_float.h から示す。

// nn_test_float.h
// 2017/05/31 by marsee
//

#ifndef __NN_TEST_H__
#define __NN_TEST_H__

#include <ap_fixed.h>

const float wt[3][4]={
    {-0.10.2, -0.30.4},
    {-0.50.6, -0.70.8},
    {-0.20.4, -0.50.6}
};

const float b[4] ={
    -0.10.4, -0.30.5
};

#endif


nn_test.cpp を示す。

// nn_test_float.cpp
// 2017/05/29 by marsee
//

#include <stdio.h>
#include "nn_test_float.h"

int nn_test(float in[3], float out[4]){
    float dot[4];

    Loop1: for(int j=0; j<4; j++){
        dot[j] = 0;
        Loop2: for(int i=0; i<3; i++){
            dot[j] += in[i]*wt[i][j];
        }
        dot[j] += b[j];
        out[j] = dot[j];
    }

    return(0);
}


nn_test_tb_float.cpp を示す。

// nn_test_tb_float.cpp
// 2017/05/29 by marsee
//

#include <stdio.h>
#include "nn_test_float.h"

int nn_test(float in[3], float out[4]);
int nn_test_soft(float in[3], float out[4]);

int main(){
    float in[3] = {0.3906250.58593750.78125};
    float out[4];
    float out_soft[4];

    nn_test(in, out);
    nn_test_soft(in, out_soft);

    for(int i=0; i<4; i++){
        if(out[i] != out_soft[i]){
            printf("ERROR HW and SW results mismatch i = %d, HW = %f, SW = %f\n", i, (float)out[i], (float)out_soft[i]);
        }else{
            printf("out[%d] = %f\n", i, (float)out[i]);
        }
    }

    return(0);
}

int nn_test_soft(float in[3], float out[4]){
    float dot[4];

    for(int j=0; j<4; j++){
        dot[j] = 0;
        for(int i=0; i<3; i++){
            dot[j] += in[i]*wt[i][j];
        }
        dot[j] += b[j];
        out[j] = dot[j];
    }

    return(0);
}


C シミュレーションは、前回の”Vivado HLS の任意精度固定小数点型を使用した内積の計算”を参照のこと。

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

DSPを5個使っている以外は案外、リソース使用量が少ないのではないだろうか?

Analysis 画面を開いた。DSP は浮動小数点数の乗算に3個、加算の2個使用されている。
ap_fixed_dot_10_170601.png

C/RTL協調シミュレーションを行った。やはり、前回の 109 クロックよりも完了までのクロック数は増えている。
ap_fixed_dot_11_170601.png

C/RTL協調シミュレーションの出力も問題なさそうだ。

out[0] = -0.588281
out[1] = 1.142187
out[2] = -1.217969
out[3] = 1.593750


C/RTL協調シミュレーション結果を示す。
ap_fixed_dot_12_170601.png

浮動小数点数が出ているようだ。
  1. 2017年06月01日 21:17 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS の任意精度固定小数点型を使用した内積の計算

今回は、配列の内積をVivado HLS の任意精度固定小数点型を使用して計算してみようと思う。浮動小数点数の計算値と比較してみよう。

Vivado HLS の任意精度固定小数点型を使用した配列の内積を計算するVivado HLS 2017.1 のプロジェクトを示す。
ap_fixed_dot_1_170601.png

nn_test.h を示す。

// nn_test.h
// 2017/05/31 by marsee
//

#ifndef __NN_TEST_H__
#define __NN_TEST_H__

#include <ap_fixed.h>

const ap_fixed<91, AP_TRN_ZERO, AP_SAT> wt[3][4]={
    {-0.10.2, -0.30.4},
    {-0.50.6, -0.70.8},
    {-0.20.4, -0.50.6}
};

const ap_fixed<91, AP_TRN_ZERO, AP_SAT> b[4] ={
    -0.10.4, -0.30.5
};

#endif


nn_test.cpp を示す。

// nn_test.cpp
// 2017/05/29 by marsee
//

#include <stdio.h>
#include "nn_test.h"

int nn_test(ap_ufixed<80, AP_TRN_ZERO, AP_SAT> in[3], ap_fixed<135, AP_TRN_ZERO, AP_SAT> out[4]){
    ap_fixed<135, AP_TRN_ZERO, AP_SAT> dot[4];

    Loop1: for(int j=0; j<4; j++){
        dot[j] = 0;
        Loop2: for(int i=0; i<3; i++){
            dot[j] += in[i]*wt[i][j];
        }
        dot[j] += b[j];
        out[j] = dot[j];
    }

    return(0);
}


nn_test_tb.cpp を示す。

// nn_test_tb.cpp
// 2017/05/29 by marsee
//

#include <stdio.h>
#include "nn_test.h"

int nn_test(ap_ufixed<80, AP_TRN_ZERO, AP_SAT> in[3], ap_fixed<135, AP_TRN_ZERO, AP_SAT> out[4]);
int nn_test_soft(ap_ufixed<80, AP_TRN_ZERO, AP_SAT> in[3], ap_fixed<135, AP_TRN_ZERO, AP_SAT> out[4]);

int main(){
    ap_ufixed<80, AP_TRN_ZERO, AP_SAT> in[3] = {0.3906250.58593750.78125};
    ap_fixed<135, AP_TRN_ZERO, AP_SAT> out[4];
    ap_fixed<135, AP_TRN_ZERO, AP_SAT> out_soft[4];

    nn_test(in, out);
    nn_test_soft(in, out_soft);

    for(int i=0; i<4; i++){
        if(out[i] != out_soft[i]){
            printf("ERROR HW and SW results mismatch i = %d, HW = %f, SW = %f\n", i, (float)out[i], (float)out_soft[i]);
        }else{
            printf("out[%d] = %f\n", i, (float)out[i]);
        }
    }

    return(0);
}

int nn_test_soft(ap_ufixed<80, AP_TRN_ZERO, AP_SAT> in[3], ap_fixed<135, AP_TRN_ZERO, AP_SAT> out[4]){
    ap_fixed<135, AP_TRN_ZERO, AP_SAT> dot[4];

    for(int j=0; j<4; j++){
        dot[j] = 0;
        for(int i=0; i<3; i++){
            dot[j] += in[i]*wt[i][j];
        }
        dot[j] += b[j];
        out[j] = dot[j];
    }

    return(0);
}


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

out[] の値を示す。
out[0] = -0.578125
out[1] = 1.128906
out[2] = -1.207031
out[3] = 1.582031

Vivado HLS の任意精度固定小数点型をそっくりそのまま float 型で実装したVivado HLS プロジェクトの結果を示す。
ap_fixed_dot_3_170601.png

out[] の値を示す。
out[0] = -0.588281
out[1] = 1.142187
out[2] = -1.217969
out[3] = 1.593750

このコードで本当に配列の内積が計算できているか?を調べるために、maxima で配列の内積を計算した。
ap_fixed_dot_4_170601.png

その結果 float 型で計算したVivado HLS のプロジェクトの値は、maxima で計算した値にほぼ等しい。
Vivado HLS の任意精度固定小数点型はやはり固定小数点なので、誤差があるが、おおむね値は合っていそうだ。

Vivado HLS の任意精度固定小数点型のVivado HLS プロジェクトで、C コードの合成を行った。
ap_fixed_dot_5_170601.png

全く指示子を与えていないので、デフォルトの状態になっている。

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

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

このように、Vivado HLS の任意精度固定小数点型で書くと、固定小数点数のビット長や固定小数点位置が違っても、うまく計算してくれる。とっても便利だ。
うまく計算してくれるのは、以前のやった結果からわかってはいたのだが、もう一度、確認するためにやってみた。
  1. 2017年06月01日 04:05 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLSのCoSim がNorton Endpointの検疫に引っかかる

Vivado HLSのCoSim がNorton Endpointの検疫に引っかかってしまった。
Vivado HLS のバージョンは2017.1 だが、2016.4 も同様に引っかかっている。

あるVivado HLS のプロジェクトを作成した。
C-Simulation を行ったら通った。
Endpoint_Cosim_3_170531.png

けれどもC コードの合成を行って、CoSim を行ったらエラー発生。
Endpoint_Cosim_4_170531.png

同時にNorton Endpoint の検疫に引っかかっていた。
Endpoint_Cosim_1_170531.png

詳しいダイアログを表示した。
Endpoint_Cosim_2_170531.png

CoSim 時に自動生成されたcosim.pc.exe が検疫されているではないか。。。

このファイルはCoSim 時に自動生成されているので、これがウィルスということはないと思うのだが。。。怪しい動きをしていたのだろうか?
ちなみに検出結果は”SONAR.SuspScript!g3”ということだった。

Norton Endpoint を立ち上げて、「設定の変更」ボタンをクリックし、
「ウイルス対策とスパイウェアの設定」ダイアログの「グローバル設定(G)」タブで「例外:」の「リスト表示(L)」ボタンをクリックし、
「例外」ダイアログの「追加(A)...」ボタンをクリックして、「SONAR例外」→「フォルダ」を選択して、Vivado HLS のプロジェクトのあるフォルダを指定したところ、CoSim が通った。
Endpoint_Cosim_5_170531.png


  1. 2017年05月31日 15:00 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0
»