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

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

FPGAの部屋

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

Vivado HLSで#define と プラグマ指示子の使用

今日はVivado HLS の小ネタをご紹介したい。

Vivado HLS でFIFO を作ってみた3(hiyuhさんに教えてもらった回路)”でFIFO の C コードが書いてあるが、hiyuh さんに教えてもらったコードが興味深いので、試してみよう。

Vivado HLS 勉強会の乗算回路で、static const size_t を使用して PIPELINE指示子の II の値を指定する方法と、#define を使用してPIPELINE指示子(プラグマ指示子)を使用する方法だ。

参考にするのは、”Vivado Design Suite ユーザー ガイド 高位合成 UG902 (v2017.3) 2017 年 10 月 04 日”の 54 ページの”#define と プラグマ指示子の使用”だ。

まずは、”#define と プラグマ指示子の使用”を参考に、プラグマを入れてみた。なお、multi_apuint() 関数の ap_uint 引数の幅は、static const size_t 宣言した値で記述することができた。

// multi_apuint.cpp

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

#define PRAGMA_SUB(x) _Pragma (#x)
#define PRAGMA_HLS(x) PRAGMA_SUB(x)
#define II_VAL_D 1


static const size_t INW = 8;
static const size_t OUTW = INW * 2;


void multi_apuint(ap_uint<INW> multi_in0, ap_uint<INW> multi_in1,
        ap_uint<OUTW> *multi_out){

PRAGMA_HLS(HLS PIPELINE II=II_VAL_D)
#pragma HLS INTERFACE s_axilite register port=multi_in1 bundle=AXI4LS
#pragma HLS INTERFACE s_axilite register port=multi_in0 bundle=AXI4LS
#pragma HLS INTERFACE s_axilite register port=multi_out bundle=AXI4LS
#pragma HLS INTERFACE s_axilite port=return bundle=AXI4LS
    *multi_out = multi_in0 * multi_in1;
}


これで C コードの合成を行った。結果を示す。
Vivado_HLS _1_171106

Interval は 1 になっていて、PIPELINE指示子が効いている。

次に、PIPELINE指示子の II の値を static const size_t で与えた。

// multi_apuint.cpp

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

static const size_t INW = 8;
static const size_t OUTW = INW * 2;
static const size_t II_VAL = 2;

void multi_apuint(ap_uint<INW> multi_in0, ap_uint<INW> multi_in1,
        ap_uint<OUTW> *multi_out){
#pragma HLS PIPELINE II=II_VAL
#pragma HLS INTERFACE s_axilite register port=multi_in1 bundle=AXI4LS
#pragma HLS INTERFACE s_axilite register port=multi_in0 bundle=AXI4LS
#pragma HLS INTERFACE s_axilite register port=multi_out bundle=AXI4LS
#pragma HLS INTERFACE s_axilite port=return bundle=AXI4LS
    *multi_out = multi_in0 * multi_in1;
}


こちらは、#pragma を使用して、Vivado HLS のコードそのものだが、PIPELINE 指示子のとことでワーニングが出てしまっている。
Vivado_HLS _3_171106

このワーニングを気にしなければ、C コードの合成結果は満足できる。
Vivado_HLS _2_171106

Interval が 2 になっていて、PIPELINE指示子が効いているのわかる。
  1. 2017年11月06日 05:09 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS 2017.3.1 における識別子の違いによる任意精度固定小数点データ型の動作の違い2(合成編)

Vivado HLS 2017.3.1 における識別子の違いによる任意精度固定小数点データ型の動作の違い1”の続き。

前回は、任意精度固定小数点データ型を使用した時に、量子化モードやオーバーフロー・モードの違いによるCシミュレーションの演算結果の違いについて検討した。今回は、任意精度固定小数点データ型を使用した時に量子化モードやオーバーフロー・モードの違いによる C コードの合成結果の違いについて検討する。なお使用するVivado HLS はUbuntu 16.04 上のVivado HLS 2017.3.1 を使用した。

まずは、

typedef ap_fixed<4, 3, AP_TRN_ZERO, AP_SAT> ap_fixed_def;

の時にC コードを合成してみよう。
結果を示す。
ap_fixed_test_3_171103.png

Latency は 1 クロックで、Interval は 2 クロックとなった。
FF は 11 個、LUT は 220 個使用されている。
Analysis 結果を示す。
ap_fixed_test_5_171103.png

次に、

typedef ap_fixed<4, 3, AP_TRN_ZERO, AP_WRAP> ap_fixed_def;

の時にC コードを合成してみよう。
結果を示す。
ap_fixed_test_4_171103.png

Latency は 0 クロックで、Interval は 1 クロックとなった。組み合わせ回路だ。
リソース使用量もFF が 0 になって、LUT は 33 個だった。やはり、ap_fixed<4, 3, AP_TRN_ZERO, AP_SAT>の時の方がリソース使用量が多い。
Analysis 画面を示す。
ap_fixed_test_6_171103.png

ap_fixed<4, 3, AP_RND, AP_SAT>

を使用した演算を合成した。結果を示す。
ap_fixed_test_7_171103.png

ap_fixed<4, 3, AP_TRN_ZERO, AP_SAT>と比較して、FF は変化ないが、LUT は 220 個から 212 個に減っている。

ap_fixed<4, 3, AP_TRN, AP_SAT>

を使用した演算を合成した。結果を示す。
ap_fixed_test_8_171103.png

なんと、Latency は 0 クロックで、Interval は 1 クロックとなった。飽和演算がついていても組み合わせ回路となった。
リソース使用量は、FF が 0 になった。LUT も 116 個と減少した。

最後に、最小のLUT を求めて、

ap_fixed<4, 3, AP_TRN, AP_WRAP>

でやってみた。
ap_fixed_test_9_171103.png

リソース使用量が LUT が 13 個になった。

このように、量子化モードやオーバーフロー・モードによって、リソース使用量が変化するのがわかった。
  1. 2017年11月04日 05:12 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS 2017.3.1 における識別子の違いによる任意精度固定小数点データ型の動作の違い1

Zynq+Vivado HLS 勉強会で使用する予定のVivado HLS 2017.3 における識別子の違いによる任意精度固定小数点データ型の動作の違いをブログに書いて置く。

任意精度固定小数点データ型とは、任意のビット長の固定小数点のデータ型のことだ。整数+小数のビット幅と整数のビット幅を選ぶことができる。更に量子化モードとオーバーフローモードを選ぶことができる。つまり、自動的に飽和演算してくれるわけだ。とっても便利な任意精度固定小数点データ型を調査していこう。

なお、任意精度固定小数点データ型については、”Vivado Design Suite ユーザー ガイド 高位合成 UG902 UG902 (v2017.3) 2017 年 10 月 04 日”の189ページからの”任意精度固定小数点デー タ型”を参照のこと。

まずは、ap_fixed_test.h から貼っておく。

// ap_fixed_test.h
// 2017/11/03 by marsee
//

#ifndef __ap_fixed_test_H__
#define __ap_fixed_test_H__

typedef ap_fixed<4, 3, AP_TRN_ZERO, AP_SAT> ap_fixed_def;
// for simulation test
// ap_fixed<4, 3, AP_TRN_ZERO, AP_SAT>
// ap_fixed<4, 3, AP_TRN_ZERO, AP_WRAP>
// ap_fixed<4, 3, AP_RND, AP_SAT>
// ap_fixed<4, 3, AP_TRN, AP_SAT>

// for synthesis test
// ap_fixed<4, 3, AP_TRN_ZERO, AP_SAT>
// ap_fixed<4, 3, AP_TRN_ZERO, AP_WRAP>

#endif


ap_fixed_test.cpp を貼っておく。

// ap_fixed_test.cpp
// 2017/11/03 by marsee
//

#include <ap_fixed.h>

#include "ap_fixed_test.h"

int ap_fixed_test(ap_fixed_def in0, ap_fixed_def in1,

	
	
ap_fixed_def &out){

	
out = in0 * in1;

	
return(0);
}


ap_fixed_test_tb.cpp を貼っておく。

// ap_fixed_test_tb.cpp
// 2017/11/03 by marsee
//

#include <stdio.h>
#include <ap_fixed.h>

#include "ap_fixed_test.h"

int ap_fixed_test(ap_fixed_def in0, ap_fixed_def in1,

	
	
ap_fixed_def &out);

int main(){
	
ap_fixed_def out;

	
ap_fixed_test(1.5, 1.5, out);
	
printf("1.5 x 1.5 = %f\n", (float)out);

	
ap_fixed_test(-1.5, 1.5, out);
	
printf("-1.5 x 1.5 = %f\n", (float)out);

	
ap_fixed_test(2, 2, out);
	
printf("2 x 2 = %f\n", (float)out);

	
ap_fixed_test(-2, 2, out);
	
printf("-2 x 2 = %f\n", (float)out);

	
ap_fixed_test(3, 3, out);
	
printf("3 x 3 = %f\n", (float)out);

	
ap_fixed_test(-3, 3, out);
	
printf("-3 x 3 = %f\n", (float)out);

	
return(0);

}


ap_fixed_test プロジェクトを示す。
ap_fixed_test_1_171103.png

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

ここで使用している任意精度固定小数点データ型は全ビット数が4ビット、整数部3ビット、小数部1ビットだ。つまり、2の補数表示をすると、-4 〜 +3.5 までの 0.5 刻みの小数ということになる。
今回の量子化モードは AP_TRN_ZERO : 0 への切 り捨て (デフォル ト )
オーバーフロー・モードは AP_SAT : 飽和

1.5 x 1.5 = 2.000000
-1.5 x 1.5 = -2.000000
2 x 2 = 3.500000
-2 x 2 = -4.000000
3 x 3 = 3.500000
-3 x 3 = -4.000000


1.5 x 1.5 = 2.25 だが、0.5 刻みなので、0 へ切り捨てされて 2.0 になった。
-1.5 x 1.5 = -2.25 だが、やはり 0 方向に切り捨てるので、切り上がって -2.0 になった。
2 x 2 = 3.5 は整数部が負の値を含めて3ビットなので、実質2ビットで整数の絶対値を表すため、+3.5 〜 -4.0 なので、3.5が正の値の最大値であるため飽和演算されている。
-2 x 2 = -4.000000 は正しい。
3 x 3 = 3.500000 は正の値の飽和演算のテスト、正の最大値。
-3 x 3 = -4.000000 は負の値の飽和演算のテスト、負の最大値。

次にオーバーフローモードを AP_WRAP : 折り返し (デフォル ト )にする。
typedef ap_fixed<4, 3, AP_TRN_ZERO, AP_WRAP> ap_fixed_def;
結果を示す。

1.5 x 1.5 = 2.000000
-1.5 x 1.5 = -2.000000
2 x 2 = -4.000000
-2 x 2 = -4.000000
3 x 3 = 1.000000
-3 x 3 = -1.000000


飽和演算が無くなったので、飽和演算をしていた下の演算の値が桁上げが無いためおかしくなっている。
2 x 2 = -4.000000
3 x 3 = 1.000000
-3 x 3 = -1.000000
AP_WRAP は演算してもオーバーフローしない時の演算に使用すると良い。飽和演算をするよりも使用リソースが少なくなる。

オーバーフロー・モードを AP_SAT に戻して、
量子化モードを AP_RND : 正の無限大への丸め にしてみよう。
typedef ap_fixed<4, 3, AP_RND, AP_SAT> ap_fixed_def;
結果を示す。

1.5 x 1.5 = 2.500000
-1.5 x 1.5 = -2.000000
2 x 2 = 3.500000
-2 x 2 = -4.000000
3 x 3 = 3.500000
-3 x 3 = -4.000000


今回は、1.5 x 1.5 = 2.500000 が変わっている。正の無限大への丸めなので、2.25 が 2.5 に丸められている。

量子化モードを AP_TRN : 負の無限大への切り捨て にしてみよう。
typedef ap_fixed<4, 3, AP_TRN, AP_SAT> ap_fixed_def;
結果を示す。

1.5 x 1.5 = 2.000000
-1.5 x 1.5 = -2.500000
2 x 2 = 3.500000
-2 x 2 = -4.000000
3 x 3 = 3.500000
-3 x 3 = -4.000000


今度は、-1.5 x 1.5 = -2.500000 となって、負の無限大への切り捨て となっているのがわかる。

このように量子化モードやオーバーフローモードで結果が変わってくる。
C コードを合成してもリソース使用量が変わるので次回やってみよう。
  1. 2017年11月03日 06:29 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS 2015.4と2017.2の合成時の違い

Linux 版のVivado 2017.2 で乗算回路を作った。FPGAマガジンNo.14 にも書いた乗算回路のC 記述だ。
Vivado_HLS_1_171027.png

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

Target が 10 ns ではLUT が 62 個使用されている。

それでは、Vivado HLS 2015.4 ではどうだったかというと、Target 10 ns の時、DSP を 1 個使用していた。FPGAマガジンNo.14 の時のVivado HLS 2016.1 も同様だった。
Vivado hls勉強会1(基礎編)の41ページと42ページを示す。
https://image.slidesharecdn.com/vivadohls1-151210121350/95/vivado-hls1-41-638.jpg?cb=1463176003
Vivado_HLS_3_171027.png

https://image.slidesharecdn.com/vivadohls1-151210121350/95/vivado-hls1-42-638.jpg?cb=1463176003
Vivado_HLS_4_171027.png

Vivado HLS 2015.4、Vivado HLS 2016.1 の時はDSP を使用して、Vivado HLS 2017.2 の時はLUT を使用するようになったようだ。

Vivado HLS 2017.2 でTarget を 2.5 ns にしてやってみたが、やはりDSP は使われずに、今度はFF が入ったようで、FF が 107 個に LUT が 110 個になった。それでも Estimated は 5.42 ns だった。
Vivado_HLS_6_171027.png

Export RTL をやってみた。Vivado で論理合成とプレース・アンド・ルートするようにチェックを入れた。結果を示す。
Vivado_HLS_7_171027.png

CP achieved post-implementation は 5.535 ns だった。

次に、Vivado 2017.2 でDSP を使うように出来た。それは、入力と出力のビット幅を増やしたことだ。入力のビット幅を 16 ビットに、出力のビット幅を 32 に増やしたらDSP を使うようになった。
Vivado_HLS_5_171027.png

C コードの合成結果を示す。
Vivado_HLS_8_171027.png

Export RTL の結果を示す。Target 2.5 ns でも満足している。
Vivado_HLS_9_171027.png

Vivado HLS 2017.2 で高速な乗算回路が欲しくてDSP を使いたいときは、入力のビット幅と出力のビット幅をある程度増やせば良いようだ。
ちなみに、Vivado HLS 2017.3 でなのだが、いろいろな指示子(ディレクティブ)を使用してDSP 使わせようとしたが、頑として使ってくれなかった。唯一、ビット幅を拡張した時だけ、DSPを使ってくれた。やはり、少ないビット幅だとDSP を使うのがもったいないということで使わなくなったのだろうか?
  1. 2017年10月27日 05:31 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

2017年度Zynq+Vivado HLS勉強会概要

今度、筑波大学でやる予定の「2017年度Zynq+Vivado HLS勉強会概要」です。

2017年度Zynq+Vivado HLS勉強会概要

2017年度にZynqとVivado HLSの使い方を勉強する勉強会を開きます。回数は半日7回とします。なお資料は
(とりあえず秘密です。。。)
から見ることができます。このURLや資料はお友達やWebに拡散しないようにお願いします。
Windows版のVivado 2017.3(必ずVivado 2017.3をインストールしてください。他のバージョンでは結果が異なります)をインストールし、ZYBOのボードファイルをインストールしてください。ZYBOのボードファイルのインストール方法については、FPGAの部屋のブログの“ZYBO Z7-20 のボードファイルをVivado 2017.2 にインストールする”をご覧ください。この操作を行うと、ZYBOのボードファイルもインストールすることができます。ただし、Vivado 2017.3にインストールしてください。
http://marsee101.blog19.fc2.com/blog-entry-3926.html

第7回ではLinuxを使用してデバイスツリー・ソースをコンパイルします。よって、VirtualBoxかVMwareでUbuntu 16.04をインストールしてください。その際に
sudo apt-get install device-tree-compiler
でデバイスツリー・コンパイラをインストールしてください。

1. 第1回目(2時間程度)No1のフォルダに資料があります。実習はありません
(ア) Zynqの概要
(イ) AXIインターフェース

2. 第2回目(半日程度)資料-No2フォルダ
(ア) Vivado HLS勉強会1(基礎編)(ZYBO実機を使用します)
① Vivado HLSの基本的に使い方を学習します。簡単な乗算回路のIPを作ります。組み合わせ回路を作ります。
② Vivado HLSで作ったIPを使用して、VivadoでIPIを使って乗算回路にしてZYBOで実際に動作させます。

3. 第3回目(半日程度)資料-No3フォルダ
(ア) Vivado HLS勉強会2(レジスタの挿入とPIPELINEディテクティブ)
① 第2回目で作成した乗算回路にレジスタを挿入します。
② パイプライン処理の方法を学習します。
③ Vivado HLSでディスプレイ・コントローラを作ってみます。

4. 第4回(半日程度)資料-No4フォルダ(ZYBO実機を使用します)
(ア) Vivado HLS勉強会3(AXI4 Lite Slave)
① 乗算回路をAXI4 Lite Slaveインターフェース対応にします。
② Vivado HLSで作ったIPを使用して、VivadoでIPIを使って乗算回路にしてZYBOで実際に動作させます。

5. 第5回(半日程度)資料-No5フォルダ
(ア) Vivado HLS勉強会4(AXI4 Master)
① ラプラシアンフィルタを題材にAXI4 Masterの実装方法を学習します。
② ディテクティブやCの構造による性能の違いを学習します。

6. 第6回(半日程度)資料-No6フォルダ
(ア) Vivado HLS勉強会5(AXI4 Stream)+任意精度型固定小数点データ型+HLSビデオライブラリ
① 第5回目のラプラシアンフィルタをAXI4 Streamで実装します。AXI4 Streamが一番性能が出やすくお得です。
② 任意精度型固定小数点データ型+HLSビデオライブラリについて説明します。資料が作れて実習できれば良いのですが、講義だけになってしまうかも?です。

7. 第7回(半日程度)資料-No7フォルダ(ZYBO実機を使用します)
(ア) Vivado HLSのIPをLinuxから使用する(資料は未作成です)
① 第4回目で作成した乗算回路をLinuxから使用する方法を学習します。
② SDKでBOOT.binを作成し、devicetreeやuImageとともにSDカードに書き込みます。
③ SDカードの第2パーティションには私のUbuntu 14.04のRootFSを入れておいて、ZYBO上でUbuntuを起動します。
④ Vivado HLSのドライバを使って乗算回路IPをソフトで制御するためにMakefileを作ってコンパイル実行します。
⑤ LinuxのCMA領域を使用するudmabufについて学習します。


参加者の方には後で、PDFファイルを送ります。
  1. 2017年10月20日 10:12 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

a と b の信号を切り替えるVivado HLSのセレクタ

a と b の信号を切り替えるVivado HLSのセレクタを作った。
これだけならば、Verilog HDL で書いて、なひたふさんの記事”VivadoのIPインテグレータでRTLソースをIP化せずに、モジュールとして追加する”を参考にしてRTL のIP としてIP Integrator にインスタンスして使えばよいのだが、選択信号(select)をARM プロセッサから選択したいということでVivado HLS 2017.2 でプロジェクトを作ってやってみた。

まずはVivado HLS で ZYBO Z7-20 用の select_one プロジェクトを作った。
select_one_1_171014.png

select_one.cpp を貼っておく。

// select_one.cpp
// 2017/10/14 by marsee
//

#include <ap_int.h>

int select_one(
    ap_uint<1> a,
    ap_uint<1> b,
    ap_uint<1> select,
    ap_uint<1> &out
){
#pragma HLS INTERFACE ap_none port=out
#pragma HLS INTERFACE s_axilite port=select
#pragma HLS INTERFACE ap_none port=b
#pragma HLS INTERFACE ap_none port=a
#pragma HLS INTERFACE s_axilite port=return
    switch(select){
        case 0:
            out = a;
            break;
        default:
            out = b;
            break;
    }
    return(0);
}



a と b は入力ポートとして合成、select はAXI4 Lite Slave インターフェース、out は出力ポートとした。

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

Latency が 0 なので組み合わせ回路だ。

出力されたVerilog HDL コードを見てみよう。
select_one.v を見てみると 120 行目にそのままの記述が。。。

assign out_V = ((select_V[0:0] === 1'b1) ? b_V : a_V);

select_one_4_171014.png

組み合わせ回路なので、ap_start をアサートしなくても 0x18 番地に書けば select の値が変わるようだ。

select_one_AXILiteS_s_axi.v のアドレス・マップを貼っておく。

//------------------------Address Info-------------------
// 0x00 : Control signals
//        bit 0  - ap_start (Read/Write/SC)
//        bit 1  - ap_done (Read/COR)
//        bit 2  - ap_idle (Read)
//        bit 3  - ap_ready (Read)
//        bit 7  - auto_restart (Read/Write)
//        others - reserved
// 0x04 : Global Interrupt Enable Register
//        bit 0  - Global Interrupt Enable (Read/Write)
//        others - reserved
// 0x08 : IP Interrupt Enable Register (Read/Write)
//        bit 0  - Channel 0 (ap_done)
//        others - reserved
// 0x0c : IP Interrupt Status Register (Read/TOW)
//        bit 0  - Channel 0 (ap_done)
//        others - reserved
// 0x10 : Data signal of ap_return
//        bit 31~0 - ap_return[31:0] (Read)
// 0x18 : Data signal of select_V
//        bit 0  - select_V[0] (Read/Write)
//        others - reserved
// 0x1c : reserved
// (SC = Self Clear, COR = Clear on Read, TOW = Toggle on Write, COH = Clear on Handshake)


Export RTL を行って、IP にした。
select_one_3_171014.png

本来であればシミュレーションを行うのだが、Verilog HDL のコードがとってもわかりやすいので、その必要もないだろう。
10 分でAXI4 Lite Slave インタフェースのセレクタIP ができた。。。
  1. 2017年10月14日 08:17 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

AXI4 Stream版白線追従走行用畳み込みニューラルネットワークIPその1(C シミュレーション)

白線追従走行用畳み込みニューラルネットワークの製作18(Vivado HLSでCシミュレーション)
白線追従走行用畳み込みニューラルネットワークの製作19(Cコードの合成、IP化)
で作成した白線追従走行用畳み込みニューラルネットワークをAXI4 Streamで入力するように変更した。これは、”Vivado HLS で画像のサイズを縮小して白黒変換2(resize_gray)”のAXI4 Stream 入力を受けて、白線追従走行用畳み込みニューラルネットワークにそのAXI4 Stream データを入力して処理する。

straight_conv_nn2_axis プロジェクトを Ubuntu 16.04 上のVivado HLS 2016.4 で作成した。
wlt_cnn_139_170908.png

C シミュレーションを行った。結果を示す。
wlt_cnn_140_170908.png
出力結果を貼っておく。

INFO: [SIM 2] *************** CSIM start ***************
INFO: [SIM 4] CSIM will launch GCC as the compiler.
Compiling ../../../straight_conv_nn2_axis_tb.cpp in debug mode
Generating csim.exe
*straight0.bmp
outs[0] = -6.218750 outs[1] = 3.812500 outs[2] = -3.078125
*straight1.bmp
outs[0] = -5.625000 outs[1] = 2.500000 outs[2] = -1.796875
*straight2.bmp
outs[0] = -3.796875 outs[1] = 4.015625 outs[2] = -5.890625
*straight3.bmp
outs[0] = -3.484375 outs[1] = 0.875000 outs[2] = -2.140625
*straight4.bmp
outs[0] = -0.765625 outs[1] = 2.218750 outs[2] = -6.156250
*straight5.bmp
outs[0] = -5.468750 outs[1] = 3.046875 outs[2] = -2.828125
*straight6.bmp
outs[0] = -6.500000 outs[1] = 5.062500 outs[2] = -4.234375
*straight7.bmp
outs[0] = -5.140625 outs[1] = 2.187500 outs[2] = -1.312500
*straight8.bmp
outs[0] = 1.359375 outs[1] = 1.546875 outs[2] = -8.156250
*straight9.bmp
outs[0] = -5.390625 outs[1] = 2.515625 outs[2] = -1.375000
*straight10.bmp
outs[0] = 1.718750 outs[1] = 1.640625 outs[2] = -8.796875
*left_turn0.bmp
outs[0] = 5.671875 outs[1] = -0.515625 outs[2] = -9.937500
*left_turn1.bmp
outs[0] = 5.093750 outs[1] = -3.734375 outs[2] = -3.078125
*left_turn2.bmp
outs[0] = 6.500000 outs[1] = -6.875000 outs[2] = -0.593750
*left_turn3.bmp
outs[0] = 6.078125 outs[1] = -6.546875 outs[2] = -0.515625
*left_turn4.bmp
outs[0] = 6.984375 outs[1] = -9.109375 outs[2] = 1.859375
*left_turn5.bmp
outs[0] = 7.250000 outs[1] = -9.593750 outs[2] = 2.109375
*left_turn6.bmp
outs[0] = 6.359375 outs[1] = -3.046875 outs[2] = -5.593750
*left_turn7.bmp
outs[0] = 8.843750 outs[1] = -8.890625 outs[2] = -1.203125
*left_turn8.bmp
outs[0] = 8.453125 outs[1] = -8.781250 outs[2] = -0.656250
*left_turn9.bmp
outs[0] = 5.343750 outs[1] = -1.062500 outs[2] = -7.953125
*left_turn10.bmp
outs[0] = 7.609375 outs[1] = -6.062500 outs[2] = -3.421875
*right_turn0.bmp
outs[0] = -1.578125 outs[1] = -0.890625 outs[2] = 0.843750
*right_turn1.bmp
outs[0] = 3.828125 outs[1] = -6.937500 outs[2] = 3.937500
*right_turn2.bmp
outs[0] = 3.296875 outs[1] = -5.375000 outs[2] = 2.562500
*right_turn3.bmp
outs[0] = 2.796875 outs[1] = -4.234375 outs[2] = 1.421875
*right_turn4.bmp
outs[0] = 3.562500 outs[1] = -4.625000 outs[2] = 0.843750
*right_turn5.bmp
outs[0] = 2.890625 outs[1] = -3.906250 outs[2] = 0.328125
*right_turn6.bmp
outs[0] = -2.109375 outs[1] = -0.312500 outs[2] = -1.296875
*right_turn7.bmp
outs[0] = 0.281250 outs[1] = -2.843750 outs[2] = 2.375000
*right_turn8.bmp
outs[0] = 0.671875 outs[1] = -5.812500 outs[2] = 6.656250
*right_turn9.bmp
outs[0] = 1.562500 outs[1] = -4.765625 outs[2] = 3.437500
*right_turn10.bmp
outs[0] = 3.515625 outs[1] = -7.093750 outs[2] = 4.437500
INFO: [SIM 1] CSim done with 0 errors.
INFO: [SIM 3] *************** CSIM finish ***************


right_turn2.bmp、right_turn3.bmp、right_turn4.bmp が左旋回にミスっているが、下の図を見ると左旋回とも取れる画像になっているので仕方ないかもしれない。右旋回のミスは多いかもしれない?
wlt_cnn_141_170908.jpg

right_turn2.bmp、right_turn3.bmp、right_turn4.bmp はどのようなシチュエーションなのかを示す。

2: 角度 -5 度、左車輪逸脱車体1/4
3: 角度 -10 度、左車輪逸脱車体1/4
4: 角度 0 度、左車輪逸脱車体1/2


staight_conv_nn2_axis.cpp を貼っておく。

// straight_conv_nn2_axis.cpp
// 2017/09/05 by marsee
// 畳み込み層のカーネル数 2
// AXI4 Stream入力
//

#include <ap_fixed.h>
#include <hls_stream.h>
#include <ap_axi_sdata.h>

#include "conv1_weight.h"
#include "conv1_bias.h"
#include "af1_weight.h"
#include "af1_bias.h"
#include "af2_weight.h"
#include "af2_bias.h"

#define REDUSED_ROW        45
#define REDUSED_COULMN    60
#define NUM_OF_KERNELS    2
#define COULMN_PIXELS    56
#define ROW_PIXELS        10
#define ALL_PIXELS        560
#define NUM_OF_OUTPUT    3

int straight_conv_nn2_axis(hls::stream<ap_axiu<32,1,1,1> >& ins, ap_fixed<137, AP_TRN_ZERO, AP_SAT> outs[NUM_OF_OUTPUT]){
#pragma HLS INTERFACE s_axilite port=return
#pragma HLS INTERFACE s_axilite port=outs
#pragma HLS INTERFACE axis register both port=ins
    ap_ufixed<80, AP_TRN_ZERO, AP_SAT> buf[ROW_PIXELS][COULMN_PIXELS];
    ap_fixed<106, AP_TRN_ZERO, AP_SAT> conv_out[NUM_OF_KERNELS][ROW_PIXELS-4][COULMN_PIXELS-4];
    ap_fixed<106, AP_TRN_ZERO, AP_SAT> pool_out[NUM_OF_KERNELS][(ROW_PIXELS-4)/2][(COULMN_PIXELS-4)/2];
    ap_fixed<137, AP_TRN_ZERO, AP_SAT> dot1[100];
    ap_fixed<137, AP_TRN_ZERO, AP_SAT> dot2[NUM_OF_OUTPUT];
    ap_axiu<32,1,1,1> pix;

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

    // 10 x 56 に整形
    buf_copy1: for(int i=0; i<REDUSED_ROW; i++){
        buf_copy2: for(int j=0; j<REDUSED_COULMN; j++){
            if (!(i==0 && j==0))    // 最初の入力はすでに入力されている
                ins >> pix;    // AXI4-Stream からの入力

            if((i>=33 && i<33+ROW_PIXELS) && (j>=2 && j<2+COULMN_PIXELS)){
                buf[i-33][j-2] = (ap_ufixed<80, AP_TRN_ZERO, AP_SAT>)((ap_ufixed<168, AP_TRN_ZERO, AP_SAT>)(pix.data & 0xff) / 256);
                //printf("%1x", (((unsigned int)pix.data&0xff)+8)/16);
                //if(j==2+COULMN_PIXELS-1)
                //    printf("\n");
            }
        }
    }

    // Convolutional Neural Network 5x5 kernel, Stride = 1, Padding = 0
    // + ReLU
    CONV1: for(int i=0; i<NUM_OF_KERNELS; i++){    // カーネルの個数
        CONV2: for(int j=0; j<ROW_PIXELS-4; j++){
            CONV3: for(int k=0; k<COULMN_PIXELS-4; k++){
                conv_out[i][j][k] = 0;
                CONV4: for(int m=0; m<5; m++){
                    CONV5: for(int n=0; n<5; n++){
                        conv_out[i][j][k] += buf[j+m][k+n] * conv1_weight[i][0][m][n];
                    }
                }
                conv_out[i][j][k] += conv1_bias[i];

                if(conv_out[i][j][k]<0)    // ReLU
                    conv_out[i][j][k] = 0;
            }
        }
    }

    // Pooling Kernel = 2 x 2, Stride = 2
    POOL1: for(int i=0; i<NUM_OF_KERNELS; i++){
        POOL2: for(int j=0; j<ROW_PIXELS-4; j += 2){
            POOL3: for(int k=0; k<COULMN_PIXELS-4; k += 2){
                POOL4: for(int m=0; m<2; m++){
                    POOL5: for(int n=0; n<2; n++){
                        if(m==0 && n==0){
                            pool_out[i][j/2][k/2] = conv_out[i][j][k];
                        } else if(pool_out[i][j/2][k/2] < conv_out[i][j+m][k+n]){
                            pool_out[i][j/2][k/2] = conv_out[i][j+m][k+n];
                        }
                    }
                }
            }
        }
    }

    af1_dot1: for(int col=0; col<100; col++){
        dot1[col] = 0;
        af1_dot2: for(int i=0; i<NUM_OF_KERNELS; i++){
            af1_dot3: for(int j=0; j<(ROW_PIXELS-4)/2; j++){
                af1_dot4: for(int k=0; k<(COULMN_PIXELS-4)/2; k++){
                    dot1[col] += pool_out[i][j][k]*af1_weight[i*((ROW_PIXELS-4)/2)*((COULMN_PIXELS-4)/2)+j*((COULMN_PIXELS-4)/2)+k][col];
                }
            }
        }
        dot1[col] += af1_bias[col];

        if(dot1[col] < 0)    // ReLU
            dot1[col] = 0;
    }

    af2_dot1: for(int col=0; col<NUM_OF_OUTPUT; col++){
        dot2[col] = 0;
        af2_dot2: for(int row=0; row<100; row++){
            dot2[col] += dot1[row]*af2_weight[row][col];
        }
        dot2[col] += af2_bias[col];

        outs[col] = dot2[col];
    }

    return(0);
}


staight_conv_nn2_axis_tb.cpp を貼っておく。

// straight_conv_nn2_axis_tb.cpp
// 2017/09/06 by marsee
//

#include <iostream>
#include "hls_opencv.h"
#include "ap_axi_sdata.h"
#include "hls_video.h"

#define MAX_HEIGHT  600
#define MAX_WIDTH   800

typedef hls::stream<ap_axiu<32,1,1,1> > AXI_STREAM;
typedef hls::Mat<MAX_HEIGHT, MAX_WIDTH, HLS_8UC3> RGB_IMAGE;
typedef hls::Mat<MAX_HEIGHT, MAX_WIDTH, HLS_8UC1> GRAY_IMAGE;

using namespace cv;

#define NUM_OF_OUTPUT   3

#define MAX_LOOP_COUNT    11
//#define MAX_LOOP_COUNT  1   // for C/RTL Co-Simulation

int straight_conv_nn2_axis(AXI_STREAM& ins, ap_fixed<137, AP_TRN_ZERO, AP_SAT> outs[NUM_OF_OUTPUT]);
int resize_gray(AXI_STREAM& ins, AXI_STREAM& outs);
int main_output_loop(char *buf);

int main () {
    char buf[200];

    sprintf(buf, "%s""straight");
    main_output_loop(buf);

    sprintf(buf, "%s""left_turn");
    main_output_loop(buf);

    sprintf(buf, "%s""right_turn");
    main_output_loop(buf);

    return(0);
}

int main_output_loop(char *buf){
    char bmp_file_name[200];
    ap_fixed<137, AP_TRN_ZERO, AP_SAT> outs[NUM_OF_OUTPUT];
    AXI_STREAM src_axi, dst_axi;
    Mat src;

    for(int i=0; i<MAX_LOOP_COUNT; i++){
        sprintf(bmp_file_name, "%s%d.bmp", buf, i);

        // OpenCV で 画像を読み込む
        src = imread(bmp_file_name);

        // Mat フォーマットから AXI4 Stream へ変換
        cvMat2AXIvideo(src, src_axi);

        // resize_gray() 関数をコール
        resize_gray(src_axi, dst_axi);

        straight_conv_nn2_axis(dst_axi, outs);

        printf("*%s\n", bmp_file_name);
        for(int i=0; i<NUM_OF_OUTPUT; i++)
            printf("outs[%d] = %f ", i, (float)outs[i]);
        printf("\n");
    }

    return(0);
}

int resize_gray(AXI_STREAM& ins, AXI_STREAM& outs){

    RGB_IMAGE org_img(600800);
    GRAY_IMAGE org_img_g(600800);
    GRAY_IMAGE resize_img_g(4560);
    RGB_IMAGE resize_img(4560);

    hls::AXIvideo2Mat(ins, org_img);
    hls::CvtColor<HLS_BGR2GRAY>(org_img, org_img_g);
    hls::Resize(org_img_g, resize_img_g);
    hls::CvtColor<HLS_GRAY2BGR>(resize_img_g, resize_img);
    hls::Mat2AXIvideo(resize_img, outs);

    return(0);
}

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