FC2カウンター FPGAの部屋 2015年09月

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

FPGAの部屋

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

Vivado HLS によるアンシャープマスクキング・フィルタの作製5(Vivado プロジェクトの作製)

Vivado HLS によるアンシャープマスクキング・フィルタの作製4(固定小数点で実装してみた2)”の続き。

前回、アンシャープマスキング・フィルタ IP が作成できたので、Vivado 2015.2 でプロジェクトを作製して動作をチェックする。
プロジェクトは、”ZYBOのHDMI入力をVGA出力に出力する8(ラプラシアンフィルタIP付き)”のプロジェクトを使用する。ラプラシアンフィルタ IP の部分だけをアンシャープマスキング・フィルタ IP に交換する。

まずはZYBO 用のプロジェクトを作製した。( dvi2vga_usm )
unsharp_mask_25_150929.png

dvi2vga_usm フォルダに ラプラシアンフィルタのプロジェクトに使用するIP と dvi2vga.tcl をすべてコピーした。
unsharp_mask_26_150929.png

Flow Navigator -> Project Manager -> IP Catalog をクリックして、IP Catalog を開いた。

IP Catalog を右クリックして、右クリックメニューから IP Settings... を選択した。

Project Settings ダイアログの IP を選択して、 dvi2vga_usm フォルダに追加したすべてのIP を登録した。
unsharp_mask_27_150929.png

ラプラシアンフィルタIP プロジェクトのブロックデザインで Export -> Export Block Design... を選択して、できたTcl スクリプト dvi2vga.tcl を Tcl Console から source dvi2vga.tcl コマンドで起動する。(”Vivado プロジェクトを配布する方法”を参照)
unsharp_mask_28_150929.png

dvi2vga ブロックデザインがあっという間に完成した。
unsharp_mask_29_150929.png

Vivado HLS によるアンシャープマスクキング・フィルタの作製4(固定小数点で実装してみた2)”で出来上がったアンシャープマスキング・フィルタIP を dvi2vga_usm フォルダに unsharp_masking フォルダを作ってコピーした。
unsharp_mask_30_150929.png

IP Catalog を右クリックして、右クリックメニューから IP Settings... を選択した。

Project Settings ダイアログの IP を選択して、アンシャープマスキング・フィルタIP を登録した。
unsharp_mask_31_150929.png

ラプラシアンフィルタIP を削除し、unsharp_mask_axis_0 をAdd IP して配線を繋ぎ直した。
unsharp_mask_32_150929.png

これで、ブロックデザインは完成した。

トップのラッパー Verilog HDL ファイルを生成した。
unsharp_mask_33_150929.png
  1. 2015年09月30日 03:45 |
  2. ZYBO
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS によるアンシャープマスクキング・フィルタの作製4(固定小数点で実装してみた2)

Vivado HLS によるアンシャープマスクキング・フィルタの作製3(固定小数点で実装してみた)”の続き。

前回は、float で実装したアンシャープマスキング・フィルタを固定小数点で実装した。その結果、FPGAのLUT リソース使用量が 1/33 になり、FPGAに実装可能となった。
今回は、アンシャープマスキング・フィルタのC/RTLコシミュレーションを行って、IP化しようと思う。

前回はC からHDL へ合成を行ったが、ブロックのインターフェースが ap_ctrl_none だとC/RTLコシミュレーションを行えないので、ap_ctrl_hs に変更して、もう一度C からHDL への合成を行った。
unsharp_mask_18_150928.png

これで、C/RTLコシミュレーションを行った。
unsharp_mask_19_150928.png

Vivado 2015.2 を立ちあげて、Tcl Console に次のコマンドを入力した。

cd c:/Users/Masaaki/Documents/Vivado_HLS/ZYBO/unsharp_mask_14_4/solution1/sim/verilog
current_fileset
open_wave_database unsharp_mask_axis.wdb
open_wave_config unsharp_mask_axis.wcfg

でC/RTLコシミュレーションの波形が表示された。
unsharp_mask_20_150928.png

ins_TVALID, ins_TREADY, outs_TVALID, outs_TREADY がアンシャープマスキング・フィルタ処理中はずっと1のままなので、インターバルは 0 なのが分かった。
これで、AXI4-Stream のフィルタとして問題ないことが分かったので、(インターバルが 1 とかでも問題があるわけじゃないが、スループットを確保するためには、より高い動作周波数で動作される必要があるだろう) IP化を行う。

まずは、C/RTLコシミュレーションを行うために変更した。ブロック・インターフェースを ap_ctrl_none に戻した。

次に、unsharp_mask_axis.h の HORIZONTAL_PIXEL_WIDTH と VERTICAL_PIXEL_WIDTH を 720p 用に変更した。
unsharp_mask_21_150928.png

これで、C からHDL を合成した。当然ながら使用リソースも少し増えた。
unsharp_mask_22_150928.png

unsharp_mask_23_150928.png

Export RTL を行った。
unsharp_mask_24_150928.png

IP化が成功した。
  1. 2015年09月28日 04:06 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS によるアンシャープマスクキング・フィルタの作製3(固定小数点で実装してみた)

Vivado HLS によるアンシャープマスクキング・フィルタの作製2(floatで実装してみた)”の続き。

(2015/09/29 : unsharp_masking() が間違っていたので全面的に書き換えました。PRECISION を書き換えることで小数点以下の精度を変更できるようにしました。
2015/10/04:もう一度、
unsharp_masking() を修正しました

前回はfloat でアンシャープマスキング・フィルタを実装してみたが、リソース使用量が 100% を超えてしまって、ZYBOに実装できなかった。今回は float を止めて固定小数点で実装した。
誤差が出るので、エラーになってもリターンしないようにテストベンチを変更した。

鮮鋭化の強さが k = 3.0 だと9を割った商が整数なので、float のアンシャープマスキング・フィルタの結果と固定小数点の結果は同一だ。
unsharp_mask_11_150926.png

鮮鋭化の強さが k = 2.5 だと9を割った商が小数なので、float のアンシャープマスキング・フィルタの結果と固定小数点の結果は誤差が出てしまう。
unsharp_mask_12_150926.png

鮮鋭化の強さが k = 2.5 の時の出力BMPファイル temp_usm.bmp も問題無さそうだ。
unsharp_mask_17_150926.png

なお、小数点以下が 4  だと鮮鋭化の強さが k = 2.5 の時の出力BMPファイル temp_usm.bmp の背景がわずかに灰色に近くなってしまったので、6 を小数点以下の2進数の数とした。

これで、C から HDL へ合成した時の結果を下に示す。
unsharp_mask_13_150926.png

unsharp_mask_14_150926.png

DSP は 22 %、LUT 使用率は 10 % だった。float の時の LUT 使用率は 330 % だったので、1/33 になった。

Anaylsis を見てみた。
unsharp_mask_15_150926.png

unsharp_mask_9_150926.png

こちらも float の時の 114 ステートが 31 ステートになった。

最後に 変更した unsharp_mask_axis.cpp の unsharp_masking() を貼っておく。

// アンシャープマスキング・フィルタ
// x0y0 x1y0 x2y0 -k   -j  -k
// x0y1 x1y1 x2y1 -k  9+8k -k x 1/9
// x0y2 x1y2 x2y2 -k   -k  -k
//
// k : 鮮鋭化の強さ(固定小数点) , k != 0
// num_adec_k : Kの小数点の位置
// 2015/09/27 : 演算の小数部は num_adec_k*2 ビットとする。
//

#define PRECISION    6    // 小数点以下の桁数、精度(1以上)

int unsharp_masking(int pix_mat[3][3], int k, int num_adec_k)
{
    int y;
    int xy[3][3];
    int result=0;
    int z;

    for (int i=0; i<=16; i += 8){
        for (int j=0; j<3; j++){
            for (int k=0; k<3; k++){
                xy[j][k] = (pix_mat[j][k] >> i) & 0xff; // RGBのいずれかを抽出
            }
        }
        int x1y1 = (9<<(PRECISION+num_adec_k))/k + (8<<PRECISION);

        y = -(xy[0][0]<<PRECISION) -(xy[0][1]<<PRECISION) -(xy[0][2]<<PRECISION)
            -(xy[1][0]<<PRECISION) +x1y1*xy[1][1]         -(xy[1][2]<<PRECISION)
            -(xy[2][0]<<PRECISION) -(xy[2][1]<<PRECISION) -(xy[2][2]<<PRECISION);

        y = ((k * y)/9) >> num_adec_k; // k は num_adc_k だけ左シフトされているので戻す

        z = y + (1<<(PRECISION-1)); // 四捨五入 +0.5
        z = z >> PRECISION; // 小数点以下切り捨て

        if (z<0// 飽和演算
            z = 0;
        else if (z>255)
            z = 255;

        result += z<<i; // i=0 : blue, i=8 : green, i=16 : red
    }

    return(result);
}

  1. 2015年09月27日 10:13 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

「進撃の巨人 ATTACK ON TITAN エンド オブ ザ ワールド」を見てきました

今日は奥さんと「進撃の巨人 ATTACK ON TITAN エンド オブ ザ ワールド」を見てきました。
なかなか良いと思いましたが、また続編があるんでしょうか?
今回、行動している間は普通の巨人には攻撃されないという、ご都合主義的な展開に疑問が残りました。
  1. 2015年09月26日 21:04 |
  2. 日記
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS によるアンシャープマスクキング・フィルタの作製2(floatで実装してみた)

”Vivado HLS によるアンシャープマスクキング・フィルタの作製1(準備編)”の続き。

今回はZYBO 用のアンシャープマスキング・フィルタのプロジェクト unsharp_mask_14_4 をVivado HLS 2014.4 で作製した。
Source には、unsharp_mask_axis.cpp、unsharp_mask_axis.h を入れた。
Test Bench には、bmp_header.h、test.bmp、unsharp_mask_axis_tb.cpp を入れた。
unsharp_mask_2_150926.png

Cシミュレーションを行った。その際に、C Simulation Dialog で Clean Build にチェックを入れておいたほうが良さそうだ。
特に、バグありの状態からデバックしている際には、TCLエラーか抜け出せない時があったが、 Clean Build にチェックを入れたらビルドが成功した。
unsharp_mask_3_150926.png

Cシミュレーションが成功した。
unsharp_mask_4_150926.png

A の文字のBMPファイル(test.bmp) をアンシャープマスキング・フィルタを通した結果が test_usm.bmp ファイルに出力される。
unsharp_mask_5_150926.png

下に2つのファイルを示す。鮮鋭化の強さ k は、3.0 に設定してある。
unsharp_mask_6_150926.png

C から HDL へ合成を行った。
Estmated が 8.62 ns なので成功しているのだが、
unsharp_mask_7_150926.png

リソースのDSP が 153 %、LUT が 330 % になってしまってZYBO のZynq-7010 に入らない。
unsharp_mask_8_150926.png

失敗だ。

Anaylsis を見てみた。
unsharp_mask_9_150926.png

ステートはC114 まである。とても長いステートなので、LUT も相当増えてしまったのだろう?
unsharp_mask_10_150926.png

最後にソースだが、bmp_header.h は”SDSoC 2015.2 でハードウェアとソフトウェアのラプラシアンフィルタの性能を比較した1(ソースの公開)”を参照のこと。

unsharp_mask_axis.h を下に示す。

// unsharp_mask_axis.h
// 2015/09/26 by marsee

//#define HORIZONTAL_PIXEL_WIDTH 1280
//#define VERTICAL_PIXEL_WIDTH 720

#define HORIZONTAL_PIXEL_WIDTH 64
#define VERTICAL_PIXEL_WIDTH 48

#define ALL_PIXEL_VALUE (HORIZONTAL_PIXEL_WIDTH*VERTICAL_PIXEL_WIDTH)


unsharp_mask_axis.cpp を下に示す。

// unsharp_mask_axis.cpp
// 2015/09/24 by marsee
//

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

#include "unsharp_mask_axis.h"

int unsharp_masking(int pix_mat[3][3], int k, int num_adec_k);

int unsharp_mask_axis(ap_uint<1> usm_fil_enable, ap_uint<4> usm_fil_k, hls::stream<ap_axis<32,1,1,1> >& ins, hls::stream<ap_axis<32,1,1,1> >& outs){
#pragma HLS INTERFACE ap_none port=usm_fil_enable
#pragma HLS INTERFACE ap_none port=usm_fil_k
#pragma HLS INTERFACE axis port=ins
#pragma HLS INTERFACE axis port=outs
#pragma HLS INTERFACE ap_ctrl_none port=return

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

    int line_buf[2][HORIZONTAL_PIXEL_WIDTH];
#pragma HLS array_partition variable=line_buf block factor=2 dim=1
#pragma HLS resource variable=line_buf core=RAM_2P

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

    int usm_fil_val;

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

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

            for (int i=0; i<3; i++){
                for (int j=0; j<2; j++){
#pragma HLS UNROLL
                    pix_mat[i][j] = pix_mat[i][j+1];
                }
            }
            pix_mat[0][2] = line_buf[0][x];
            pix_mat[1][2] = line_buf[1][x];

            pix_mat[2][2] = pix.data;

            line_buf[0][x] = line_buf[1][x];    // 行の入れ替え
            line_buf[1][x] = pix.data;

            usm.data = unsharp_masking(pix_mat, (int)usm_fil_k, 2);

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

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

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

            if (usm_fil_enable)
                outs << usm;    // AXI4-Stream へ出力
            else
                outs << pix;    // 入力画像をそのまま出力
        }
    }

    return 0;
}

// アンシャープマスキング・フィルタ
// x0y0 x1y0 x2y0 -k   -j  -k
// x0y1 x1y1 x2y1 -k  9+8k -k x 1/9
// x0y2 x1y2 x2y2 -k   -k  -k
//
// k : 鮮鋭化の強さ(固定小数点)
// num_adec_k : Kの小数点の位置
//
int unsharp_masking(int pix_mat[3][3], int k, int num_adec_k)
{
    float y;
    int xy[3][3];
    float rgb[3];
    int result=0;

    float fk = (float)k;
    for (int m=0; m<num_adec_k; m++){ // 小数点の位置を正しい位置にする
        fk /= 2.0;
    }

    for (int i=0; i<=16; i += 8){
        for (int j=0; j<3; j++){
            for (int k=0; k<3; k++){
                xy[j][k] = (pix_mat[j][k] >> i) & 0xff; // RGBのいずれかを抽出
            }
        }
        y = -(float)xy[0][0]         -(float)xy[0][1]      -(float)xy[0][2]
            -(float)xy[1][0] +(9.0/fk+8.0)*(float)xy[1][1] -(float)xy[1][2]
            -(float)xy[2][0]         -(float)xy[2][1]      -(float)xy[2][2];
        y = (fk * y)/9.0;

        int z = (int)(y + 0.5);    // 四捨五入

        if (z<0// 飽和演算
            z = 0;
        else if (z>255)
            z = 255;

        result += z<<i; // i=0 : blue, i=8 : green, i=16 : red
    }

    return(result);
}


unsharp_mask_axis_tb.cpp を下に示す。

// unsharp_mask_axis_tb.cpp
// 2015/09/26 by marsee
//

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

#include "unsharp_mask_axis.h"
#include "bmp_header.h"

int unsharp_mask_axis(ap_uint<1> usm_fil_enable, ap_uint<4> usm_fil_k, hls::stream<ap_axis<32,1,1,1> >& ins, hls::stream<ap_axis<32,1,1,1> >& outs);

int unsharp_masking_soft(int pix_mat[3][3], int k, int num_adec_k);
int unsharp_mask_axis_soft(ap_uint<1> usm_fil_enable, ap_uint<4> usm_fil_k, hls::stream<ap_axis<32,1,1,1> >& ins, hls::stream<ap_axis<32,1,1,1> >& outs, int width, int height);

#define CLOCK_PERIOD 10

#define K 3.0 // 鮮鋭化の強さ
#define NUM_ADEC_K 2 // Kの小数点の位置

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;
    int *rd_bmp, *hw_usmd;
    int blue, green, red;

    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(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_usmd =(int *)malloc(sizeof(int) * (bmpihr.biWidth * bmpihr.biHeight))) == NULL){
        fprintf(stderr, "Can't allocate hw_usmd 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;
        }
    }

    int usm_k = (int)(K * pow(2.0, (double)NUM_ADEC_K));
    unsharp_mask_axis(1, usm_k, ins, outs); // k = 2
    unsharp_mask_axis_soft(1, usm_k, ins_soft, outs_soft, bmpihr.biWidth, bmpihr.biHeight); // k = 2;

    // ハードウェアとソフトウェアのラプラシアン・フィルタの値のチェック
    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_usmd[(j*bmpihr.biWidth)+i] = (int)val;

            if (val != val_soft){
                printf("ERROR HW and SW results mismatch i = %ld, j = %ld, HW = %d, SW = %d\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_usm.bmp へ出力する
    if ((fbmpw=fopen("temp_usm.bmp""wb")) == NULL){
        fprintf(stderr, "Can't open temp_usm.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_usmd[((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x] & 0xff;
            green = (hw_usmd[((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x] >> 8) & 0xff;
            red = (hw_usmd[((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x]>>16) & 0xff;

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

    return 0;
}

int unsharp_mask_axis_soft(ap_uint<1> usm_fil_enable, ap_uint<4> usm_fil_k, hls::stream<ap_axis<32,1,1,1> >& ins, hls::stream<ap_axis<32,1,1,1> >& outs, int width, int height){
    ap_axis<32,1,1,1> pix;
    ap_axis<32,1,1,1> usm;
    int **line_buf;
    int pix_mat[3][3];
    int usm_fil_val;
    int i;

    // line_buf の1次元目の配列をアロケートする
    if ((line_buf =(int **)malloc(sizeof(int *) * 2)) == NULL){
        fprintf(stderr, "Can't allocate line_buf[3][]\n");
        exit(1);
    }

    // メモリをアロケートする
    for (i=0; i<2; i++){
        if ((line_buf[i]=(int *)malloc(sizeof(int) * width)) == NULL){
            fprintf(stderr, "Can't allocate line_buf[%d]\n", i);
            exit(1);
        }
    }

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

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

            for (int k=0; k<3; k++){
                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];

            pix_mat[2][2] = pix.data;

            line_buf[0][x] = line_buf[1][x];    // 行の入れ替え
            line_buf[1][x] = pix.data;

            usm.data = unsharp_masking_soft(pix_mat, (int)usm_fil_k, 2);

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

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

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

            if (usm_fil_enable)
                outs << usm;    // AXI4-Stream へ出力
            else
                outs << pix;    // 入力画像をそのまま出力
        }
    }

    for (i=0; i<2; i++)
        free(line_buf[i]);
    free(line_buf);

    return 0;
}

// アンシャープマスキング・フィルタ
// x0y0 x1y0 x2y0 -k   -j  -k
// x0y1 x1y1 x2y1 -k  9+8k -k x 1/9
// x0y2 x1y2 x2y2 -k   -k  -k
//
// k : 鮮鋭化の強さ(固定小数点)
// num_adec_k : Kの小数点の位置
//
int unsharp_masking_soft(int pix_mat[3][3], int k, int num_adec_k)
{
    float y;
    int xy[3][3];
    float rgb[3];
    int result=0;

    float fk = (float)k;
    for (int m=0; m<num_adec_k; m++){ // 小数点の位置を正しい位置にする
        fk /= 2.0;
    }

    for (int i=0; i<=16; i += 8){
        for (int j=0; j<3; j++){
            for (int k=0; k<3; k++){
                xy[j][k] = (pix_mat[j][k] >> i) & 0xff; // RGBのいずれかを抽出
            }
        }
        y = -(float)xy[0][0]         -(float)xy[0][1]      -(float)xy[0][2]
            -(float)xy[1][0] +(9.0/fk+8.0)*(float)xy[1][1] -(float)xy[1][2]
            -(float)xy[2][0]         -(float)xy[2][1]      -(float)xy[2][2];
        y = (fk * y)/9.0;

        int z = (int)(y + 0.5); // 四捨五入

        if (z<0// 飽和演算
            z = 0;
        else if (z>255)
            z = 255;

        result += z<<i; // i=0 : blue, i=8 : green, i=16 : red
    }

    return(result);
}

  1. 2015年09月26日 20:22 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado や Vivado HLS が Visual Studio 2012 Visual C++ Runtimeのインストールダイアログが出て起動できない

Vivado 2014.4 WebPACK と Vivado HLS 2014.4 1年間評価版がインストールしてあるノートパソコンが、Vivado や Vivado HLS を起動しても、 Visual Studio 2012 Visual C++ Runtime のインストールダイアログが出て起動できなくなりました。

Xilinx Forum を調べると、ちょうど”Vivado/Xilinx SDK Error Incorrect Visual C++ Version”の状況です。

解決策は2ページ目の”Re: Vivado/Xilinx SDK Error Incorrect Visual C++ Version”で直りました。

コントロールパネルの”プログラムと機能”を見ると、Visual Studio 2012 Visual C++ Runtime がたくさんインストールされていたので、すべて削除して、パソコンをリブートしました。

そのうえで、Windows 8.1 64 ビット版だったので、Xilinx\Vivado\2014.4\tps\win64 フォルダの vcredist_x64.exe を起動して、 Visual Studio 2012 Visual C++ Runtime をインストールしました。
Visual_Studio_2012_1_150925.png

パソコンをリブートしてから Vivado や Vivado HLS を起動すると、動作するようになりました。
  1. 2015年09月25日 14:55 |
  2. Vivado
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS によるアンシャープマスクキング・フィルタの作製1(準備編)

今までラプラシアンフィルタを作ってきたが、違うフィルタをVivado HLS 2014.4 で作ってみることにした。
アンシャープマスキング・フィルタを作って見ようと思う。インターフェースはAXI4-Stream とする。
アンシャープマスキング処理については、イメージングソリューションのアンシャープマスキングを参考にさせて頂いた。
そのサイトによると、アンシャープマスキングと言うのは、平滑化フィルタの結果を元画像から引いて、それに元画像に足すという処理だそうだ。

イメージングソリューションのアンシャープマスキングで示されたアンシャープマスキング・フィルタは 3x3 のピクセルをそれぞれ 1/9 しているが、演算が多くなるので、1/9 は外に出そうと思う。その演算式を下に示す。
unsharp_mask_1_150925.png

プラットフォームは、ラプラシアンフィルタで使っていたHDMI入力のVGA出力の回路を使って、ラプラシアンフィルタをアンシャープマスキング・フィルタに変更しようと思う。

演算 はとりあえず、浮動小数点数で実装してみよう。
k の値の与え方だが、ZYBO の 4 つのスライド・スイッチを利用しようと思う。
SW3, SW2 の 2 つのスイッチは整数部で 0 ~ 3 を表し、SW1, SW0 の 2 つのスイッチは小数部で、0.25 刻みに 0 ~ 0.75 を表すことにしよう。
  1. 2015年09月25日 05:16 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

ピクセル(映画)を見てました

ピクセル(映画)(音注意)を見てました。
パックマンやドンキーコングなどが出てくるということで見に行ったのですが、B級映画の典型的な例でした。懐かしくはあったのですが。。。他の映画見たほうが良かったかも?
  1. 2015年09月23日 20:53 |
  2. 日記
  3. | トラックバック:0
  4. | コメント:0

筑波山の麓までサイクリングに行ってきました

今日は筑波山の麓までサイクリングに行ってきました。自転車は電動アシスト自転車です。登りは楽ですが、急な下りが重量が重いので怖かったです。

今日はサイクリングにいきたいよいてんきということで、つくば道を電動アシスト自転車で行けるところまで行ってみようと思って行ってきました。

自宅を出発してから、北条を目指します。昔々、中学時代に自転車で北条に行った道をトレースしました。懐かしい。記憶にあるお店はシャッターが閉まっていました。しょうがないですね。。。

国道125号に出てもうすぐ北条の町中です。
国道125号から見る景色がとっても良かったので、写真を取りました。場所はここですね
Cycling_1_150923.jpg

Cycling_2_150923.jpg

北条の町中を走って、体育館の坂を上がって、平沢官衙遺跡に行きました。
Cycling_3_150923.jpg

少し進んでから、良い景色があったので取りました。
Cycling_4_150923.jpg

つくば田井郵便局を過ぎて、町中を過ぎて、この辺りで写真を取りました。ここは筑波山がよく見えて写真スポットです。
Cycling_5_150923.jpg

ここからは筑波山を登っていきます。いつもだったらふうふう歩きで登って行くのを電動アシスト自転車だからスイスイとまでは行かなかったけど、楽でした。アシスト量のパーセンテージが決まっているようでしたね。
お蕎麦屋さんのえだまで行った所で、そろそろ止めようということで戻りました。ここで、残りの電池の容量は70%でした。
Cycling_6_150923.jpg

今度は下りでしたが、下りがやばかったです。重さは普通の自転車よりも重いし、ブレーキは普通の自転車と同じということで、とっても下りがヤバイ感じでした。神社まで登って行ったら、ブレーキがすり減ってなくなるんじゃない?という心配がありますね。

帰りは流石に疲れていて、アシスト量フルパワーで帰って来ました。また、向かい風なんですが、電動アシスト自転車だと楽ちんですね。とは言っても、最大アシスト量のパーセンテージは決まっているので、相対的に人力が増えるので疲れます。
楽しかったです。また、何処かに行きたいですね。下にデータを示します。ガーミンのGPS時計でデータ取ってました。

往復距離: 29.32km
時間:    1: 50 : 44
移動時間: 1 : 49 : 21
経過時間: 2 : 02 : 16
平均速度: 15.9 kph
高度上昇: 160 m
  1. 2015年09月22日 20:18 |
  2. 日記
  3. | トラックバック:0
  4. | コメント:0

ZYBOのHDMI入力をVGA出力に出力する9(プロジェクトの公開)

ZYBOのHDMI入力をVGA出力に出力する8(ラプラシアンフィルタIP付き)”で出来上がったプロジェクトをGitHubで公開します。

GitHub の marsee101/dvi2vga_lap にファイルがあります。

GitHub には、div2vga_lap.tcl と config_files フォルダがあります。
dvi2lap2vga_47_150920.png

config_files フォルダの内容です。
dvi2lap2vga_48_150920.png

DigilentInc/vivado-library を Download ZIP します。
dvi2lap2vga_49_150920.png

ダウンロードされてきた vivado-library-master.zip を展開します。

vivado-library-master フォルダの下の ipフォルダの下の rgb2vga_v1_0 と dvi2rgg_v1_5 を
dvi2lap2vga_50_150920.png

config_files フォルダに追加します。
dvi2lap2vga_51_150920.png

config_files フォルダの下の dvi2rgb.vhd を config_files\dvi2rgb_v1_5\src の dvi2rgb.vhd に上書きします。
dvi2lap2vga_52_150920.png

Help With A Zybo Video Design”の dvi2rgb.xdc のコードをコピーして、
dvi2lap2vga_53_150920.png

config_files\dvi2rgb_v1_5\src の dvi2rgb.xdc をエディタで開き、すべてのコードを選択して、ペーストします。
dvi2lap2vga_58_150920.png

config_files\dvi2rgb_v1_5\src の dvi2rgb.xdc をセーブします。

Vivado 2015.2 を立ちあげて、GitHub の div2vga_lap.tcl と config_files フォルダをダウンロードしたフォルダに Tcl Console から cd コマンドで移動します。
dvi2lap2vga_54_150920.png
cd z:/ などの最初のパスを入れると自動的にその下のフォルダを表示してくれるので、選択していくと目的のフォルダに行くことができます。

source dvi2vga_lap.tcl コマンドで div2vga_lap プロジェクトを生成します。
dvi2lap2vga_56_150920.png

div2vga_lap プロジェクトが生成できたので、Flow Navigator の Program and Debug から Generate Bitstream をクリックしてビットストリームを生成します。
dvi2lap2vga_57_150920.png

ビットストリームの生成が成功しました。

ZYBOを使用したテスト手順を示します。

・ノートパソコンのHDMI端子からZYBOのHDMI端子に接続します。

・ZYBOのVGA端子からディスプレイに接続します。

・ZYBOの電源を入れます。

・Vivado でZYBOにビットストリームをコンフィグレーションします。

ノートパソコンのディスクトップ画面がZYBOに接続された画面に表示されます。

ZYBOのSW0を1にすると、ラプラシアンフィルタ処理が行われます。
  1. 2015年09月20日 07:27 |
  2. ZYBO
  3. | トラックバック:0
  4. | コメント:0

DSF 2015で発表します

Design Solution Forum 2015 で発表します。
Xilinx社のFPGAにおける高位合成ツールVivado HLSの効果と性能”という題です。
まずは自己紹介で、次にVivado HLSを使ってみたいと思った理由を説明します。
Vivado HLSの特徴について簡単に説明した後で、性能を評価します。性能を評価するのは、なかなか難しですが、ソフトウェアの最適化オプションの違いやSIMDエンジンのNEONをGCCの自動ベクトル化オプションで使った時の性能も交えて、ハードウェアとの性能を比較します。
AXI4 Masterインターフェースを生成する簡単な例を説明して、まとめます。

こんな感じで発表を予定しています。
受けを取るつもりですが、あまりやり過ぎないようにします。。。
  1. 2015年09月19日 12:14 |
  2. その他のFPGAの話題
  3. | トラックバック:0
  4. | コメント:0

SDSoC 2015.2で motion detect demo をやってみる

HDMI入力は一通りできたので、今度はSDSoC に戻って、使い込んでみたいと思う。
まずは、HDMI への入出力をどうするか?というのが疑問だったので、D:\Xilinx\SDSoC\2015.2\docs フォルダにある zc702_hdm.pdf に従ってやってみることにした。
SDSoC_motion_detect_demo_7_150918.png

まずはWorkspace をD:\SDSoC\Examples2 に指定した。

次に、新規プロジェクトを作製した。motion detect demo という名前をつけた。
SDSoC_motion_detect_demo_1_150918.png

SDSoC_motion_detect_demo_2_150918.png

motion detect demo プロジェクトが作成された。
SDSoC_motion_detect_demo_3_150918.png

Hardware Functions の+ ボタンをクリックして、combo_image, diff_image, median_char_filter, rgb_pad2ycbcr, sobel_filter, sobel_filter_pass, ycbcr2rgb_pad をハードウェア化のリストに加えた。
SDSoC_motion_detect_demo_4_150918.png

Build アイコンのプルダウンメニューから SDRelease を選択するとビルドが始まった。
SDSoC_motion_detect_demo_6_150918.png

結局ビルドの途中で進まず完了はしなかった。
しかし、ビルドは終わっているようで、SD Card イメージも生成されていた。
SDSoC_motion_detect_demo_9_150919.png
  1. 2015年09月19日 04:55 |
  2. SDSoC
  3. | トラックバック:0
  4. | コメント:0

Vivado HLSでAXI4 Master IPを書いた時のバースト転送の条件

Vivado HLS で AXI4 Master IP を書いた時のバースト転送の条件は、ユーザーズガイドによると memcpy() を使った時にバースト転送しますと書いてあるが、どうやらそうでもないようだ。(Vivado Design Suite ユーザー ガイド 高位合成 UG902 (v2015.1) 2015 年 4 月 1 日の158ページの”AXI4 マスター インターフェイス”を参照のこと)

ラプラシアンフィルタの時は、シングル転送で読んで、シングル転送で書いていたが、もっと簡単な、if 文とかもない実装では、バースト転送するようだ。2014.4 と 2015.2 でもそのようだ。

RGBの値のピクセルを輝度信号Y に変換する conv_rgb2y() を複数処理できる conv_rgb2ym() にして単独で AXI4 Master IP にしてみた。
下に、conv_rgb2ym.cpp を示す。

// 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 にした
// 2015/09/15 : RGB-y変換AXI Masterハージョン by marsee

#include "conv_rgb2ym.h"

int conv_rgb2ym(int *rgb, int *y){
#pragma HLS INTERFACE s_axilite port=return

#pragma HLS INTERFACE m_axi depth=4096 port=rgb offset=slave
#pragma HLS INTERFACE m_axi depth=4096 port=y offset=slave

    int r, g, b, y_f;
    int rgb_val;

    for (int i=0; i<DATA_SIZE; i++){
#pragma HLS PIPELINE II=1
        rgb_val = *rgb++;
         b = rgb_val & 0xff;
        g = (rgb_val>>8) & 0xff;
        r = (rgb_val>>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(0);
}


conv_rgb2ym.h を貼っておく。

// conv_rgb2ym.h
// 2015/09/15 : by marsee
//

#define DATA_SIZE 4096


下にC++のテストベンチ conv_rgb2ym_tb.cpp を貼っておく。

// conv_rgb2ym_tb.cpp
// 2015/09/15 by marsee
//

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "conv_rgb2ym.h"

int conv_rgb2ym(int *rgb, int *y);
int conv_rgb2ym_soft(int *rgb, int *y);

int main(){
    int *rgb, *hw_y, *sw_y;
    int *h, *s;

    // メモリをアロケートする
    if ((rgb =(int *)malloc(sizeof(int) * (DATA_SIZE))) == NULL){
        fprintf(stderr, "Can't allocate rd_bmp memory\n");
        exit(1);
    }
    if ((hw_y =(int *)malloc(sizeof(int) * (DATA_SIZE))) == NULL){
        fprintf(stderr, "Can't allocate hw_lapd memory\n");
        exit(1);
    }
    if ((sw_y =(int *)malloc(sizeof(int) * (DATA_SIZE))) == NULL){
        fprintf(stderr, "Can't allocate sw_lapd memory\n");
        exit(1);
    }

    for (int i=0; i<DATA_SIZE; i++)
        rgb[i] = i<<16 + i<<8 + i;

    conv_rgb2ym(rgb, hw_y);
    conv_rgb2ym_soft(rgb, sw_y);

   for (int i=0; i<DATA_SIZE; i++){
       if (hw_y[i] != sw_y[i]){
           printf("ERROR HW and SW results mismatch i = %d, HW = %d, SW = %d\n", i, hw_y[i], sw_y[i]);
           return(1);
       }
   }
   printf("Success HW and SW results match\n");

   free(rgb);
   free(hw_y);
   free(sw_y);
}

int conv_rgb2ym_soft(int *rgb, int *y){
    int r, g, b, y_f;
    int rgb_val;

    for (int i=0; i<DATA_SIZE; i++){
        rgb_val = *rgb++;
         b = rgb_val & 0xff;
        g = (rgb_val>>8) & 0xff;
        r = (rgb_val>>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(0);
}


Vivado HLS 2014.4 でプロジェクトを作った。
HLS_AXIM_Burst_1_150917.png

Cシミュレーションを行ってから、HDLへ合成を行った。
その後、C/RTLコシミュレーションを行った。
Vivado 2015.2 で波形を確認した。その波形を下に示す。
HLS_AXIM_Burst_2_150917.png

AWLEN やARLEN は0xff つまり 256バーストとなっている。
WVALID ,WREADY は転送中は 1 だし、RVALID とRREADY も転送中は 1 なので、WAIT無しでバーストしているようだ。
このように単純なコードならば、memcpy() を使わなくてもバースト転送するようだ。
  1. 2015年09月17日 04:25 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:4

Vivado HLS勉強会第2日目

Vivado HLS勉強会第1日目に続き、2日目です。
今日はAXI4 バスを生成するというお題でした。AXI Lite Slave, AXI Master, AXI Stream。

Vivado HLS勉強会3 (AXI4-Lite Slave)
  • Vivado HLS勉強会1、2で使用した掛け算のプロジェクトをAXI4-Lite Slaveインターフェースにする
    • Vivado HLS 2014.4
      • Vivado HLS 2014.4のプロジェクトを新規作成
      • C++ソースコードとテストベンチは勉強会2の物を使用する
      • INTERFACEディレクティブ(AXI4 Lite Slave mode)
      • CからHDLを生成
      • C/RTLコシミュレーション
      • VivadoでC/RTLコシミュレーションの波形を確認
      • IPとしてパッケージ
    • Vivado 2014.4
      • Vivadoでプロジェクトを作製
      • AXI4 Lite Slave (multi_apuint) IPをIP Catalogに登録
      • ブロックデザインにIPを追加し、回路を構成
      • 制約ファイルを追加
      • 論理合成、インプリメント、ビットストリームの生成
      • ハードウェアをエクスポートして、SDK起動
      • Vivado HLSが作ったドライバを使用し、ソフトウェアを作製
      • 実機確認

Vivado HLS勉強会4 (AXI4 Master)
  • ソフトウェアとして作製したCソースを使用する
    • Vivado HLS 2014.4
      • Vivado HLS 2014.4のプロジェクトを新規作成
      • ソースコードとテストベンチをコピー
      • Cシミュレーション
      • CからHDLを生成
      • C/RTLコシミュレーション
      • VivadoでC/RTLコシミュレーションの波形を確認
      • IPとしてパッケージ
  • memcpy()関数を使用してバースト転送を行う
    • Vivado HLS 2014.4
      • Vivado HLS 2014.4のプロジェクトを新規作成
      • ソースコードとテストベンチをコピー
      • Cシミュレーション
      • CからHDLを生成
      • C/RTLコシミュレーション
      • VivadoでC/RTLコシミュレーションの波形を確認
      • IPとしてパッケージ
  • 更にディレクティブやソースの変更でHLSに最適化
  • @tu1978さんが書き換えてくれたコードを使用しています
    • Vivado HLS 2014.4
      • Vivado HLS 2014.4のプロジェクトを新規作成
      • ソースコードとテストベンチをコピー
      • Cシミュレーション
      • CからHDLを生成
      • C/RTLコシミュレーション
      • VivadoでC/RTLコシミュレーションの波形を確認
      • IPとしてパッケージ

Vivado HLS勉強会5 (AXI4 Stream)
  • Vivado HLS 2014.4
    • AXI4 Streamのラプラシアンフィルタ用プロジェクト作製
    • ソースコードなどをコピーして、Add Sources
    • Cシミュレーション
    • CからHDLを生成
    • C/RTLコシミュレーション
    • VivadoでC/RTLコシミュレーションの波形を確認
    • IPとしてパッケージ
勉強会全体で実機確認は2回です。パソコンなどの備え付けのセミナルームではなく、会議室みたいなところで、ノートパソコン持ち込んでもらってやっているので、機材の都合で難しいです。

疲れました。いろいろと課題も見えてきました。このような内容なので、ZynqやAXIバスを知らないとメリットが良くわからないかもしれません。AXIバスは簡単にかけて、AXIバスをHDLで自分で書いていた人から見れば感動モノなんですが、やったこと無い人にはどうなのか?
またZynqは一番AXIバスの恩恵を受けますが、Zynqをわからない人にはわからないという懸念が有ります。
つまり今のところ、多少、AXIバス、Zynqの説明はしますが、両方わかっている人向けになっています。
実機はZYBOを使用しました。

なお、勉強会4 では、3つのラプラシアンフィルタの実装を示して、実装の違いによる速度の違いを実感してもらいました。
  1. 2015年09月16日 04:35 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:3

Vivado HLS勉強会第1日目

昨日は、Vivado HLS勉強会の第1日目でした。いろいろと問題点が浮かび上がってきました。

Vivado HLS勉強会第1日目の項目です。

Vivado HLS勉強会1(基礎編)
  • Vivado HLS 2014.4
    • 掛け算のプロジェクト、Cソースコードの作製
    • Cのテストベンチを作製してCシミュレーション
    • CからHDLを生成
    • C/RTLコシミュレーション
    • VivadoでC/RTLコシミュレーションの波形を確認
    • ディレクティブを追加して再度CからHDLを生成
    • IPとしてパッケージ
  • Vivado 2014.4
    • Vivadoでプロジェクトを作製
    • Vivado HLSプロジェクト(multi_apuint)のIPをIP Catalogに登録
    • ブロックデザインにIPを追加し、回路を構成
    • 制約ファイルを追加
    • 論理合成、インプリメント、ビットストリームの生成
    • 実機確認

Vivdo HLS勉強会2(レジスタの挿入とPIPELINEディレクティブ)
  • 掛け算回路の出力にレジスタを挿入する
    • ディレクティブを変更し、高位合成
    • C/RTLコシミュレーションを行い波形を観察する
  • 掛け算回路の入力にレジスタを挿入する
    • ディレクティブを変更し、高位合成
    • C/RTLコシミュレーションを行い波形を観察する
  • PIPELINEディレクティブを挿入する
    • ディレクティブを変更し、高位合成
    • C/RTLコシミュレーションを行い波形を観察する
    • 新規Solutionを作製(400MHzクロック制約でのHDL合成)
  • 補足(応用例)
    • ディスプレイ・コントローラーの作製

  1. 2015年09月15日 03:40 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado プロジェクトを配布する方法

Vivado プロジェクトを配布する方法には2つあると思う。

1つ目は、File メニュー -> Archive Project.... を選択して、プロジェクトをアーカイブする方法だ。
Vivado_Archives_1_150914.png

すると、Archive Project ダイアログが表示され、OKボタンをクリックするとプロジェクトがアーカイブされる。
Vivado_Archives_2_150914.png

dvi2vga_lap.xpr.zip が作られた。
Vivado_Archives_3_150914.png

これを解凍すると、今まで他のフォルダに有るIPライブラリもプロジェクト内にまとめられて、プロジェクト内で完結するようになるのだが、XilinxのIPまでアーカイブされている。
Vivado_Archives_4_150914.png

そのバージョンのIPでしか動かないこともあるのだと思うし、当然の処置だと思うが、これでは、XilinxのIPソースコードが入っているので、パブリックにダウンロードさせることが難しそうだ。
この方法は自分用、もしくは身内に配るのに良さそうだ。


パブリックにダウンロードさせる方法としては、2つ目の tcl スクリプトでプロジェクトやブロックデザインを生成する方法がある。

まずはブロックデザインの生成を tcl スクリプトにエクスポートする。

ブロックデザインを開いて、File メニューから Export -> Export Block Design... を選択する。
Vivado_Archives_5_150914.png

Export Block Design ダイアログが開く。
Tcl file を指定して OK ボタンをクリックする。
Vivado_Archives_6_150914.png

tcl ファイルが生成された。
Vivado_Archives_7_150914.png

tcl ファイルを開けると、ブロックデザインを生成する tcl スクリプトが書かれている。
Vivado_Archives_8_150914.png

元々、Vivado のGUI も tcl スクリプトが実行されているのが、tcl console を見ていると分かる。Vivado は tcl スクリプトで動いているのだろう。

次に、プロジェクトの生成を行う tcl スクリプトを生成する。

File メニュー -> Write Project Tcl... を選択した。
Vivado_Archives_9_150914.png

Write Project Tcl ダウンロードが表示された。
dvi2vga_lap.tcl と Output File に入力して、OKボタンをクリックする。
Vivado_Archives_10_150914.png

成功したというダイアログが出た。
Vivado_Archives_11_150914.png

dvi2vga_lap.tcl ができた。
Vivado_Archives_12_150914.png

dvi2vga_lap.tcl を開いた。プロジェクトを生成する tcl スクリプトが書かれている。
Vivado_Archives_13_150914.png

これだと、無いファイルも要求されているので、自分で tcl スクリプトを書き換えた。ブロックデザインも読んでくるようにした。sim は無いので、削除した。
dvi2vga_lap.tcl を示す。
Vivado_Archives_14_150914.png

# dvi2vga_lap.tcl
# I have to modify the output file of "write project tcl ...". 
# by marsee
# 2015/09/13

# Set the reference directory for source file relative paths (by default the value is script directory path)
set origin_dir "."

# Create project
create_project dvi2vga_lap ./dvi2vga_lap

# Set the directory path for the new project
set proj_dir [get_property directory [current_project]]

# Set project properties
set obj [get_projects dvi2vga_lap]
set_property "default_lib" "xil_defaultlib" $obj
set_property "part" "xc7z010clg400-1" $obj
set_property "simulator_language" "Mixed" $obj

# Create 'sources_1' fileset (if not found)
if {[string equal [get_filesets -quiet sources_1] ""]} {
  create_fileset -srcset sources_1
}

# Set IP repository paths
set obj [get_filesets sources_1]
set_property "ip_repo_paths" "[file normalize "$origin_dir/config_files/lap_fil_axis_cnone"] [file normalize "$origin_dir/config_files/synchro/synchro.srcs/sources_1/new"] [file normalize "$origin_dir/config_files/dvi2rgb_v1_5"] [file normalize "$origin_dir/config_files/rgb2vga_v1_0"]" $obj

# Rebuild user ip_repo's index before adding any source files
update_ip_catalog -rebuild

source "$origin_dir/config_files/dvi2vga.tcl"

regenerate_bd_layout

save_bd_design

make_wrapper -files [get_files "$origin_dir/dvi2vga_lap/dvi2vga_lap.srcs/sources_1/bd/dvi2vga/dvi2vga.bd"] -top
add_files -norecurse "$origin_dir/dvi2vga_lap/dvi2vga_lap.srcs/sources_1/bd/dvi2vga/hdl/dvi2vga_wrapper.v"

add_files -fileset constrs_1 -norecurse "$origin_dir/config_files/dvi2vga.xdc"

update_compile_order -fileset sources_1
update_compile_order -fileset sim_1


config_files フォルダの下に、必要なファイルを置くようにしている。

この dvi2vga_lap.tcl と config_files フォルダを一緒にフォルダに置いておく。
Vivado_Archives_15_150914.png

config_files フォルダ。
Vivado_Archives_16_150914.png

Vivado を起動して(バージョンは2015.2)、dvi2vga_lap.tcl と config_files フォルダが置いてあるフォルダまで cd する。

tcl console に source dvi2vga_lap.tcl と入力して、tcl スクリプトを実行する。
Vivado_Archives_17_150914.png

ブロックデザインを生成している途中を示す。
Vivado_Archives_18_150914.png

プロジェクトの生成が終了した。
Vivado_Archives_19_150914.png

元のフォルダを見ると、dvi2vga_lap フォルダが生成されている。
Vivado_Archives_20_150914.png

こちらの方法は、Xilinx のIP は同じバージョンにする必要があるので、Vivado のバージョンを合わせる必要がある。
  1. 2015年09月14日 04:41 |
  2. Vivado
  3. | トラックバック:0
  4. | コメント:0

ZYBOのHDMI入力をVGA出力に出力する8(ラプラシアンフィルタIP付き)

ZYBOのHDMI入力をVGA出力に出力する7(ラプラシアンフィルタIPを除いたシステム)”の続き。

前回、ラプラシアンフィルタを除いたVideo In to AXI4-Stream IP と AXI4-Stream to Video Out IP、Video Timing Controller (VTC)を接続したが、正常に画像が表示できた。
今回は、Video In to AXI4-Stream IP と AXI4-Stream to Video Out IPの間のAXI4-Stream インターフェースの間にラプラシアンフィルタIP を挿入する。

その前に、720p 用にラプラシアンフィルタIP を変更した。
最初に、lap_filter_axis.h に 1280 x 720 画素を定義した。lap_filter_axis.h を下に示す。

// lap_filter_axis.h
// 2015/05/01

#define HORIZONTAL_PIXEL_WIDTH    1280
#define VERTICAL_PIXEL_WIDTH    720

//#define HORIZONTAL_PIXEL_WIDTH    64
//#define VERTICAL_PIXEL_WIDTH    48

#define ALL_PIXEL_VALUE    (HORIZONTAL_PIXEL_WIDTH*VERTICAL_PIXEL_WIDTH)


C++ からHDL へ合成した。
dvi2lap2vga_40_150910.png

dvi2lap2vga_41_150910.png

IP化を行って、Vivado 2015.2 のプロジェクトにIP をコピーした。(Vivado HLS のバージョンは 2014.4)

Vivado のブロックデザインの最終形を示す。(実は下の図にはデバック・マークが入っている)
dvi2lap2vga_42_150910.png

これで、ビットストリームの生成までを行い、ZYBOにコンフィグレーションしてやってみたが、画像が表示されないので、デバックを行うことにする。

その後、Vivado Analyzer でデバックしてみたが、信号は問題なかった。画像も映っていた。どうやら、HDMI端子をノートパソコンに入れた状態でコンフィグレーションする必要があるようだ。一度、画像が出ると安定している。HDMI端子をノートパソコンに入れてコンフィグしても画像が出ないようだったら、もう一度、コンフィグしてほしい。

その後、いろいろとFFを2つ入れてそれぞれのクロックにdvi2rgb_0 の aPixelClkLckd を同期させて見たが、HDMI端子を一度抜くと、再び挿入してもVGA画像が表示されなかった。なにか気づいたことがあったら教えて下さい。

最終的なブロックデザインを下に示す。
dvi2lap2vga_43_150912.png

どのような表示になるかというと、ノートパソコンのこんな壁紙が
dvi2lap2vga_44_150912.jpg

SW0 をON するとこのようになる。
dvi2lap2vga_45_150912.jpg

Chrome を立ちあげてラプラシアンフィルタ処理すると、リアルタイムにこのような表示が見える。とっても面白いです。。。
dvi2lap2vga_46_150912.jpg

後で、Digilent フォーラムのProject Vaultでプロジェクトを公開しようと思う。

プロジェクトを公開しようと思ったのですが、”Digilent Vivado library”のIPのライセンスが分からないので、とりあえず止めることにしました。
  1. 2015年09月11日 05:25 |
  2. ZYBO
  3. | トラックバック:0
  4. | コメント:0

ZYBOのHDMI入力をVGA出力に出力する7(ラプラシアンフィルタIPを除いたシステム)

ZYBOのHDMI入力をVGA出力に出力する6(dvi2rgb IPの信頼性2)”の続き

現在のdvi2rgb IP はDigilent フォーラムの”Help With A Zybo Video Design”の mwingerson さんの書き込みに従って"dvi2rgb.xdc"を修正してある。それに、dvi2rgb IP に hdmi_hpd と hdmi_out_en 端子を追加した。

ブロックデザインは下図を参照のこと。
dvi2vga_40_150910.png
なお、dvi2rgb IP の設定は 720p (60 MHz) に設定してある。

これで、ビットストリームの生成まで行って、ノートパソコンをHDMIに接続して解像度を変えて試してみた。結果を示す。

1920 x 1080 ◯
1280 x 768 ◯
1280 x 720 ◯
1152 x 864 ◯
1024 x 768 ◯
1024 x 600 ◯
800 x 600 X

つまり、800 x 600 だけ画像が出力されなかった。やはり、800 x 600 は映らない。
800 x 600 解像度は諦めて、1280 x 720 60p でやってみることにした。
まずは、ラプラシアンフィルタIP は 800 x 600 固定になっているので、ラプラシアンフィルタを抜いたシステムを構築してみた。dvi2vga_lap_t だ。
dvi2lap2vga_37_150910.png

ブロックデザインを示す。
dvi2lap2vga_38_150910.png
VTCの設定は 720p に設定した。

これでビットストリームの生成まで行って、ノートパソコンからHDMI 端子をZYBOのHDMI 端子に接続して、ZYBOにコンフィグレーションした。
するとノートパソコンの画像が出力された。成功した。。。やった~~~。。。
dvi2lap2vga_39_150910.jpg

入力はノートパソコンで行くことにして、ラプラシアンフィルタを 720p 用に修正することにする。
  1. 2015年09月10日 05:14 |
  2. ZYBO
  3. | トラックバック:0
  4. | コメント:4

ZYBOのHDMI入力をVGA出力に出力する6(dvi2rgb IPの信頼性2)

ZYBOのHDMI入力をVGA出力に出力する5(dvi2rgb IPの信頼性)”の続き。

自作のカメラ画像出力の800x600 解像度のHDMI 信号がまずいのかも知れない?ということで、Digilent 社のZYBO ページのZYBO Base System Design で画像を出力してみることにした。

やはり、800x600 60p ではslave 側にVGA画像が出力されない。

しかし、dvi2rgb IP を 60MHz 720p に設定して、ZYBO Base System Design で 1280x720 60p を指定すると、コンフィグレーションの順番に関係なく、100発100中でVGA端子から画像が出力された。但し、9秒ほど表示するまでの時間がかかっている。

dvi2rgb IP のマニュアルには、800x600 以上に対応していると書いてあるが、実際は720p 位からなのかもしれない?

ノートパソコンをつないで1024x768 も確かめてみようと思う。
  1. 2015年09月09日 05:04 |
  2. ZYBO
  3. | トラックバック:0
  4. | コメント:0

ZYBOのHDMI入力をVGA出力に出力する5(dvi2vga IPの信頼性)

ビデオ信号にAXI4 Stream版のラプラシアンフィルタを通して画像出力3(Vivadoプロジェクト)”で、画像が表示されなかったので、”ZYBOのHDMI入力をVGA出力に出力する4(実機テスト)”にまで戻って見たが画像が表示されない。
昨日、1日いろいろとパラメータを変えながらやってみたが、どうやら、たまたまHDMIで画像を転送できる時もあるが、転送できない時のほうが圧倒的に多いようだ。昨日は1回しか転送できなかった。

今日は、HDMI経由で画像が転送できた時に、同様に2回同じことをしたが転送できなかった。
なお、Slave側ZYBO をコンフィグしてから、Master側ZYBO の電源を入れて、Ubuntuを起動し、画像を出力しないとHDMI画像は転送できないようだ。dvi2vga IP が不完全なのか?Master側ZYBO のHDMI出力が良くないのか?でも、ディスプレイにMaster側ZYBO のHDMI出力をつないで映らなかったことはないし、どうしてだろう?
  1. 2015年09月07日 05:16 |
  2. ZYBO
  3. | トラックバック:0
  4. | コメント:2

水信玄餅を作って食べました

ある方のFaceBook で見た水信玄餅がとっても美味しそうだったので、通販で買おうとしたところ売っていないことが判明した。それじゃ自分で作ろうということで、レシピを探すと”ぶるるん水信玄餅★材料2つマフィン型”を発見しました。
作り方は簡単そうだったが、アガーって何?ということになった。アガーは寒天と同じように固める材料ということが分かった。
アガーを買いにスーパーに行ったが、スーパーには売っていなかった。それじゃ通販だということで、富澤商店の通販でぷるるんアガーを2袋購入しました。
更に、レシピに書いてあった”貝印 マフィン型 6個取り DL-5580”も購入したのですが、説明書をよく見てみると水菓子とか作るにに使うなと書いてありました。orz でも使っちゃいました。なんで使ったらダメなんだろう?

これで材料が揃ったので、とりあえず黒蜜から制作しました。”簡単おいし~黒蜜の作り方”があったので、その通りに作りましたが、煮詰める時間は8分じゃなくて、12~13分くらいでした。とろっとしてなかったからです。それでも少し水っぽいからな?と思いましたが、冷やすと、とろみが出るんですね?

まずは、砂糖と黒砂糖と水を鍋に入れて、溶かしながら煮ました。
mizushingenmochi_1_150906.jpg

だいたい溶けた所。
mizushingenmochi_2_150906.jpg

だいぶ煮詰まりました。この辺で終了。
mizushingenmochi_3_150906.jpg

次にいよいよ水信玄餅を作ります。
水とアガーだけです。最初は水、450cc に対して、アガー 14 g でしたが、これだと結構ゆるいので、2回目は 16 g 入れました。こっちのほうが好みです。

アガーをキッチン秤で計量します。
mizushingenmochi_8_150906.jpg

水を沸騰させて、アガーを投入した所。
mizushingenmochi_4_150906.jpg

アガーが白くなって焦りましたが、泡立て器でよくかき混ぜると溶けていきました。全部溶けきったら完成。
mizushingenmochi_5_150906.jpg

型に流し込みました。
mizushingenmochi_6_150906.jpg

冷めたら、冷蔵庫に入れました。2時間から3時間入れると完成です。
器にとって、黒蜜ときなこをかけました。
mizushingenmochi_7_150906.jpg

凄く美味しかったです。また作ろっと。。。
  1. 2015年09月06日 20:44 |
  2. 日記
  3. | トラックバック:0
  4. | コメント:0

ビデオ信号にAXI4 Stream版のラプラシアンフィルタを通して画像出力3(Vivadoプロジェクト)

ビデオ信号にAXI4 Stream版のラプラシアンフィルタを通して画像出力2(Vivado HLS)”の続き。

Video In to AXI4-Stream IP と AXI4-Stream to Video Out IP”と”Video Timing Controller (VTC)”でそれらのIPの使い方を学んだ。これらのIPと”ビデオ信号にAXI4 Stream版のラプラシアンフィルタを通して画像出力2(Vivado HLS)”を使って、Vivado 2015.2 のブロックデザインで接続していこう。

まずは”ZYBOのHDMI入力をVGA出力に出力する4(実機テスト)”でHDMI同士をつないで転送が成功した dvi2vga_test フォルダをコピー&ペーストして、dvi2vga_lap フォルダと名前を変更した。
dvi2lap2vga_25_150904.png

次に、”ビデオ信号にAXI4 Stream版のラプラシアンフィルタを通して画像出力2(Vivado HLS)”のC:\Users\Masaaki\Documents\Vivado_HLS\ZYBO\lap_fil_aixs_cnone_14_4\solution1\impl\ip フォルダのxilinx_com_hls_lap_filter_axis_1_0.zip の中のファイルを dvi2vga_lap フォルダにlap_fil_axis_cnone フォルダを作製して、その中にコピーした。
dvi2lap2vga_26_150904.png

左端のProject Manager -> IP Catalog をクリックして、IP Catalog で右クリックして、IP Settings... を選択し、vdin_lap_vdout_15_2 プロジェクトのフォルダにlap_fil_axis_cnone フォルダを指定した。
dvi2lap2vga_27_150904.png

dvi2vga ブロックデザインに Video In to AXI4-Stream IP、Lap_fiter_axis IP、Video Timing Controller IP、AXI4-Stream to Video Out IP の4つを入れた。
dvi2lap2vga_28_150904.png

Video Timing Controller IP をダブルクリックして、設定を行った。
最初の画面で、Include AXI4-Lite Interface にチェックを外した。
dvi2lap2vga_29_150904.png

Default/Constant タブで Video Mode を 800x600p に設定した。
dvi2lap2vga_30_150904.png

clk_wiz をダブルクリックして設定した。
100 MHz の clk_out2 を追加した。
dvi2lap2vga_31_150904.png

ブロックデザインが完成した。バリデーションも通った。
dvi2lap2vga_33_150905.png

Sourcesウインドウで dvi2vga ブロックデザインを右クリックし、右クリックメニューから、Create HDL Wapper... を選択して、Wapper HDLファイルを作成し直した。

ラプラシアンフィルタ処理を行う(1)か行わない(0)か選択するスイッチ sw0 を成約する必要があるので、論理合成を行った。

Synthesized Design をオープンして、sw0 を G15 つまりスライドスイッチの 0 番に割り当てた。IOレベルはLVCMOS33 に設定した。
dvi2lap2vga_34_150905.png

Synthesized Design を閉じて、Flow Navigator の Generate Bitstream をクリックして、ビットストリーム生成まで行った。

ビットストリーム生成は成功した。

Project Summary を示す。
dvi2lap2vga_35_150905.png

これでZYBO をコンフィグレーションしてもVGAに画像は出なかった。失敗だ。
  1. 2015年09月04日 05:23 |
  2. ZYBO
  3. | トラックバック:0
  4. | コメント:0

Video Timing Controller (VTC)

Video In to AXI4-Stream IP と AXI4-Stream to Video Out IP”で、Video In to AXI4-Stream IP と AXI4-Stream to Video Out IPを使うということになったが、Video Timing Controller (VTC) も必要ということだった。
ブロック図が”LogiCORE IP Video In to AXI4-Stream v3.0 Product Guide Vivado Design Suite PG043 April 2, 2014”の19ページの”Figure 3-2: Without VDMA - Slave Timing Mode”に載っていたので引用する。
dvi2lap2vga_24_150903.png

多分、AXI-S to Video Out から VTC に行っている clock enable は、AXI-S to Video Out の vtg_ce を VTC の gen_clken につなげば良いのじゃないか?と思っている。

なお、VTC はAXI4 Lite Slave インターフェースを除いて使用する。

Xilinx のビデオ関係のIP の情報については、次の情報が役に立つと思う。
AXI4-Stream Video IP and System Design Guide UG934 April2, 2014
Application Notes, Reference Designs, Video IP and Development Kits
  1. 2015年09月03日 05:27 |
  2. IP
  3. | トラックバック:0
  4. | コメント:0

Video In to AXI4-Stream IP と AXI4-Stream to Video Out IP

ビデオ入力-AXI4 Stream 変換、AXI4 Stream-ビデオ出力 を作ろうと思ったが、すでにXilinx のIP としてあるので、それを学習して使おうと思う。

LogiCORE IP Video In to AXI4-Stream v3.0 Product Guide Vivado Design Suite PG043 April 2, 2014
LogiCORE IP AXI4-Stream to Video Out v3.0 Product Guide Vivado Design Suite PG044 April 1, 2014

少し読んだら、AXI4-Stream to Video Out IP はAXI4 Stream からビデオ制御信号を生成するようだ。私は変換しようとしていたので、こっちのほうが賢い。この2つのIP の使い方を学習しようと思う。
  1. 2015年09月02日 05:07 |
  2. IP
  3. | トラックバック:0
  4. | コメント:0

ビデオ信号にAXI4 Stream版のラプラシアンフィルタを通して画像出力2(Vivado HLS)

ビデオ信号にAXI4 Stream版のラプラシアンフィルタを通して画像出力1(準備編)”の続き。

今回は、Vivado HLS でAXI4 Stream 版のラプラシアンフィルタIP をAXI4 Lite Slave を使わない形にする。更に、1ビットの lap_fil_enable を作って、lap_fil_enable = 1 の時だけラプラシアンフィルタ処理を行い、それ以外の時はビデオ信号をそのまま出力するように変更する。lap_fil_enable はスイッチに割り当てる予定である。

早速、lap_fil_axis_cnone_14_4 プロジェクトを作製した。今のところ、C/RLT コシミュレーションを行うために

#pragma HLS INTERFACE ap_ctrl_hs port=return

に設定してある。C/RLT コシミュレーションが終了したら

#pragma HLS INTERFACE ap_ctrl_none port=return

に戻す予定だ。
dvi2lap2vga_15_150901.png

Cシミュレーションを行った。最初に lap_fil_enable = 1 に設定し、ラプラシアンフィルタ処理を行った。ラプラシアンフィルタ処理が行われて、ラプラシアンフィルタ処理画像が temp_lap.bmp に保存されていた。成功だ。
次に、lap_fil_enable = 0 に設定して、ラプラシアンフィルタ処理を行った。ラプラシアンフィルタ処理が行われずに原画像が temp_lap.bmp に保存された。
dvi2lap2vga_16_150901.png

C から HDL への合成を行った。
dvi2lap2vga_17_150901.png

dvi2lap2vga_18_150901.png
BRAM_18K が使われていない。64ピクセル x 2 行のラインバッファなので、LUTのRAMが使われているんだろう?と思う。LUTのRAMは 16 bit なので、4 x 32 x 2 = 256 個使われているはずだ。

Dump Trace を all にして C/RTLコシミュレーションを行った。
最初に、 lap_fil_enable = 1 に設定し、ラプラシアンフィルタ処理を行った。ラプラシアンフィルタ処理が行われて、ラプラシアンフィルタ処理画像が temp_lap.bmp に保存されていた。成功だ。
dvi2lap2vga_19_150901.png

Vivado を立ちあげて、C/RTLコシミュレーションの結果を見るために、次のコマンドをTCL Console に入力した。

cd C:/Users/Masaaki/Documents/Vivado_HLS/ZYBO/lap_fil_aixs_cnone_14_4/solution1/sim/verilog
current_fileset
open_wave_database lap_filter_axis.wdb
open_wave_config lap_filter_axis.wcfg

C/RTLコシミュレーションの結果の波形が表示された。
dvi2lap2vga_20_150901.png

最初の部分を拡大して、入力ストリームと出力ストリームのクロック遅延がどうのくらいあるか確認したところ、7クロックだった。
dvi2lap2vga_21_150901.png

次に、lap_fil_enable = 0 に設定して、ラプラシアンフィルタ処理を行った。ラプラシアンフィルタ処理が行われずに原画像が temp_lap.bmp に保存された。
同様にVivado を立ちあげて、C/RTLコシミュレーション結果の波形を表示した。
dvi2lap2vga_22_150901.png

最初を拡大して、入力ストリームと出力ストリームのクロック遅延がどうのくらいあるか確認したところ、7クロックだった。
つまり、、lap_fil_enable = 1 の時の入力ストリームと出力ストリームのクロック遅延は 7 クロックで、
lap_fil_enable = 0 の時の入力ストリームと出力ストリームのクロック遅延は 2 クロックだった。

#pragma HLS INTERFACE ap_ctrl_none port=return

に設定してから、もう一度、C からRTL への合成を行って、IP化した。

lap_fil_axis_cnone.cpp を貼っておく。

//
// lap_filter_axis.cpp
// 2015/05/01
// 2015/06/25 : 修正、ラプラシアンフィルタの値が青だけになっていたので、RGBに拡張した
// 2015/08/31 : lap_fil_enable を追加。lap_fil_enable が 1 の時にラプラシアンフィルタ処理、0 の時は、入力画像をそのまま出力する
//

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

#include "lap_filter_axis.h"

int laplacian_fil(int x0y0, int x1y0, int x2y0, int x0y1, int x1y1, int x2y1, int x0y2, int x1y2, int x2y2);
int conv_rgb2y(int rgb);

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

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

    int line_buf[2][HORIZONTAL_PIXEL_WIDTH];
#pragma HLS array_partition variable=line_buf block factor=2 dim=1
#pragma HLS resource variable=line_buf core=RAM_2P

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

    int lap_fil_val;

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

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

            for (int k=0; k<3; k++){
                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];

            int 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==0 && y==0// 最初のデータでは、TUSERをアサートする
                lap.user = 1;
            else
                lap.user = 0;

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

            if (lap_fil_enable)
                outs << lap;    // AXI4-Stream へ出力
            else
                outs << pix;    // 入力画像をそのまま出力
        }
    }

    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);
}

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

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


次に、テストベンチの lap_filter_axis_tb.cpp を貼っておく。

// lap_filter_axis_tb.cpp
// 2015/05/01
// 2015/08/17 : BMPファイルを読み書きするように変更した
// 2015/08/31 : lap_fil_enable を追加した
//

#include <stdio.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 "lap_filter_axis.h"
#include "bmp_header.h"

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

int laplacian_fil_soft(int x0y0, int x1y0, int x2y0, int x0y1, int x1y1, int x2y1, int x0y2, int x1y2, int x2y2);
int conv_rgb2y_soft(int rgb);
int lap_filter_axis_soft(ap_uint<1> lap_fil_enable, hls::stream<ap_axis<32,1,1,1> >& ins, hls::stream<ap_axis<32,1,1,1> >& outs, int width, int height);

#define CLOCK_PERIOD 10

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;
    int *rd_bmp, *hw_lapd;
    int blue, green, red;

    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(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_lapd =(int *)malloc(sizeof(int) * (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] = (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;
        }
    }

    lap_filter_axis(0, ins, outs);
    lap_filter_axis_soft(0, ins_soft, outs_soft, bmpihr.biWidth, bmpihr.biHeight);

    // ハードウェアとソフトウェアのラプラシアン・フィルタの値のチェック
    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_lapd[(j*bmpihr.biWidth)+i] = (int)val;

            if (val != val_soft){
                printf("ERROR HW and SW results mismatch i = %ld, j = %ld, HW = %d, SW = %d\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_lap.bmp へ出力する
    if ((fbmpw=fopen("temp_lap.bmp""wb")) == NULL){
        fprintf(stderr, "Can't open temp_lap.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_lapd[((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x] & 0xff;
            green = (hw_lapd[((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x] >> 8) & 0xff;
            red = (hw_lapd[((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x]>>16) & 0xff;

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

    return 0;
}

int lap_filter_axis_soft(ap_uint<1> lap_fil_enable, hls::stream<ap_axis<32,1,1,1> >& ins, hls::stream<ap_axis<32,1,1,1> >& outs, int width, int height){
    ap_axis<32,1,1,1> pix;
    ap_axis<32,1,1,1> lap;
    int **line_buf;
    int pix_mat[3][3];
    int lap_fil_val;
    int i;

    // line_buf の1次元目の配列をアロケートする
    if ((line_buf =(int **)malloc(sizeof(int *) * 2)) == NULL){
        fprintf(stderr, "Can't allocate line_buf[3][]\n");
        exit(1);
    }

    // メモリをアロケートする
    for (i=0; i<2; i++){
        if ((line_buf[i]=(int *)malloc(sizeof(int) * width)) == NULL){
            fprintf(stderr, "Can't allocate line_buf[%d]\n", i);
            exit(1);
        }
    }

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

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

            for (int k=0; k<3; k++){
                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];

            int 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==0 && y==0// 最初のデータでは、TUSERをアサートする
                lap.user = 1;
            else
                lap.user = 0;
            
            if (x == (HORIZONTAL_PIXEL_WIDTH-1))    // 行の最後で TLAST をアサートする
                lap.last = 1;
            else
                lap.last = 0;

            if (lap_fil_enable)
                outs << lap;    // AXI4-Stream へ出力
            else
                outs << pix;    // 入力画像をそのまま出力
        }
    }

    for (i=0; i<2; i++)
        free(line_buf[i]);
    free(line_buf);

    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);
}

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

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

  1. 2015年09月01日 04:58 |
  2. ZYBO
  3. | トラックバック:0
  4. | コメント:0