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

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

FPGAの部屋

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

Vivado HLS で xfOpenCV を使用する3(Harris コーナー検出)

Vivado HLS で xfOpenCV を使用する2(Vivado HLS 2018.3 のGUI を使用する)”の続き。

前回は、xfopencv/HLS_Use_Model/ ディレクトリにあるもう一つのサンプル・デザインの AXI_Sample をVivado HLS GUI を起動してやってみた。今回は、examples ディレクトリの harris をVivado HLS GUI でやってみようと思う。

まずは、examples ディレクトリの harris ディレクトリを ~/xfopencv/HLS_Use_Model/examples ディレクトリの下にコピーした。
xfOpenCV_Vivado_HLS_22_190522.png

harris/data ディレクトリには、 im0.jpg がある。
xfOpenCV_Vivado_HLS_23_190522.png

さて、Vivado HLS 2018.3 で harris_project プロジェクトを作っていこう。
xfOpenCV_Vivado_HLS_24_190522.png

次に、ソースを登録する。
xf_harris_accel.cpp を CFLAG = "-D__XFCV_HLS_MODE__ -I../../../include --std=c++0x"を付けて登録した。
xfOpenCV_Vivado_HLS_25_190522.png

次にテストベンチを登録する。
xf_harris_tb.cpp を CFLAG = "-D__XFCV_HLS_MODE__ -I../../../include --std=c++0x"を付けて登録した。(ここで、im0.jpg を登録するのを忘れてしまった。。。)
xfOpenCV_Vivado_HLS_26_190522.png

Ultra96 の使用を仮定して、 xczu3eg-sbva484-1-e を選択して、Finish をクリックした。
xfOpenCV_Vivado_HLS_27_190522.png

harris_project が生成された。
xfOpenCV_Vivado_HLS_28_190522.png

ここで、テストベンチに im0.jpg が入っていないのに気がついたので、Add File... で im0.jpg を追加した。
xfOpenCV_Vivado_HLS_29_190522.png

Project メニューから Project Settings... を選択した。
Project Settings ダイアログが表示された。左のペインから Simulation を選択した。
右のペインで、Options のClear Build にチェックを入れて、Input Arguments に im0.jpg を入力した。
xfOpenCV_Vivado_HLS_30_190522.png

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

C シミュレーションの結果を示す。
xfOpenCV_Vivado_HLS_32_190522.png

harris/harris_project/solution1/csim/build の内容を示す。
xfOpenCV_Vivado_HLS_33_190522.png

output_hls.png を見るとコーナー検出されているのが分かる。
xfOpenCV_Vivado_HLS_34_190522.png

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

Latency -> Detail -> Instance の grp_cornerHarris_fu_72 をクリックして、下の階層のレポートを見る。
Latency -> Detail -> Instance には、xFCornerHarrisDetect_1_U0, cornerHarris_Loop2_U0, cornerHarris_Loop1_U0 があった。
xfOpenCV_Vivado_HLS_36_190522.png

xFCornerHarrisDetect_1_U0 をクリックした。
Latency -> Detail -> Instance には、grp_xFCornerHarrisDetect_fu_56 があった。
xfOpenCV_Vivado_HLS_37_190522.png

grp_xFCornerHarrisDetect_fu_56 をクリックした。
今度は、Latency -> Detail -> Instance には、沢山のインスタンスがあった。もう良く分からない???
xfOpenCV_Vivado_HLS_38_190522.png

C/RTL 協調シミュレーションを行ったところ、Fail してしまった。
xfOpenCV_Vivado_HLS_39_190522.png

Export RTL を行った。ただし、Vivado synthesis, place and route にチェックを入れてある。
結果を示す。
xfOpenCV_Vivado_HLS_40_190522.png
  1. 2019年05月22日 21:05 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS で xfOpenCV を使用する2(Vivado HLS 2018.3 のGUI を使用する)

Vivado HLS で xfOpenCV を使用する1”の続き。

前回は、xfopencv/HLS_Use_Model/ ディレクトリにある Standalone_HLS_Example をTCL スクリプトを起動してやってみた。今回は、、xfopencv/HLS_Use_Model/ ディレクトリにあるもう一つのサンプル・デザインの AXI_Sample をVivado HLS GUI を起動してやってみようと思う。
なお、HLS_UseModel_Usage_Doc.pdf を参考にしている。

~/xfopencv/HLS_Use_Mode/AXI_Sample ディレクトリの下に dilation_axi プロジェクトを作成する。
xfOpenCV_Vivado_HLS_9_190520.png

Add/remove C-based source files では、 xf_dilation_accel.cpp と xf_ip_accel_app.cpp を追加した。
xfOpenCV_Vivado_HLS_10_190520.png

Add/remove C-based testbench files では、testcase55.jpg と xf_dilation_tb.cpp を追加した。
xfOpenCV_Vivado_HLS_11_190520.png

Solution Configuration では、 Part に xczu3cg-sbva484-1-e を指定した。(Ultra96 に使用されている xczu3eg-sbva484-1-e と間違ってしまった)
xfOpenCV_Vivado_HLS_12_190520.png

dillation_axi プロジェクトが生成された。
xfOpenCV_Vivado_HLS_13_190520.png

Project メニューから Project Settings... を選択する。
左のペインで、Simulation を選択する。 xf_dilation_tb.cpp を選択して、Edit CFLAGS... ボタンをクリックする。
xfOpenCV_Vivado_HLS_14_190520.png

”-D__XFCV_HLS_MODE__ -I../../include --std=c++0x”を入力した。
xfOpenCV_Vivado_HLS_15_190520.png

TestBench Files のCFLAG に先程入力した値が入っているのが分かる。
Options の Clear Build にチェックを入れて、Impt Arguments に testcase55.jpg を入力する。
これでSimulation の設定は完了したので、左のペインのSynthesis をクリックする。
xfOpenCV_Vivado_HLS_16_190520.png

Synthesis の xf_dilation_accel.cpp と xf_ip_accel_app.cpp にも、CFLAG に”-D__XFCV_HLS_MODE__ -I../../include --std=c++0x”を入力した。
xfOpenCV_Vivado_HLS_17_190520.png

C Simulation を行う前に、エラーの原因を修正しよう。
xf_dilation_config.h の画像の解像度を実際の写真に合わせて変更した。
xfOpenCV_Vivado_HLS_19_190520.png

C Simulation を行う。
xfOpenCV_Vivado_HLS_18_190520.png

C Simulation の結果を示す。成功した。
xfOpenCV_Vivado_HLS_20_190520.png

最後に合成結果を示す。
xfOpenCV_Vivado_HLS_21_190520.png
  1. 2019年05月20日 21:23 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS で xfOpenCV を使用する1

久しぶりにUG902 見たところ、Vivado HLS 2018.3 では、OpenCV 対応が変更され、xfOpenCV を使用するようになっていた。これはやってみないとということでやってみたいと思う。そして他にもVivado HLS が拡張されているところがあるので、それもいずれやってみたい。

なお、やりかけの”Fixstars Tech Blogの「Ultra96 Linux で MIPI カメラから画像を取得する」をやってみる2”の続きについては、今日、明日の休日にゆっくりやって見る予定だ。

まずは、xfOpenCV を git clone しよう。
git clone https://github.com/Xilinx/xfopencv.git
なのだが、私は以前 git clone してあったので、git pull した。

さて、xfopencv/HLS_Use_Model/ ディレクトリに Standalone_HLS_Example と AXI_Sample ディレクトリと HLS_UseModel_Usage_Doc.pdf がある。
HLS_UseModel_Usage_Doc.pdf には xfOpenCV をVivado HLS で使用する方法が書いてある。HLS_UseModel_Usage_Doc.pdf によると、xfOpenCV を Vivado HLS で使用するには、

1. テストベンチでの xf::Mat オブジェクトには、 static を定義する。
Ex: static xf::MatimgInput(in_img.rows,in_img.cols);

2. コンパイル時に __XFCV_HLS_MODE__, -std=c++0x オプションを使用する。

3. インタフェースレベルの関数の引数にインタフェースのプラグマを指定する。
例えば関数の引数にポインタや参照渡しがあったら、m_axi のインターフェース・プラグマを指定する。ストリームがあったら axis を指定するということでしょう?


Standalone_HLS_Example と AXI_Sample ディレクトリの xf::dilate のサンプルでは、 2. のコンパイルオプションは tcl スクリプトに書いてあります。
Standalone_HLS_Example/script.tcl を引用する。
Copyright (c) 2018, Xilinx, Inc.
All rights reserved.

############################################################
## Copyright (C) 1986-2018 Xilinx, Inc. All Rights Reserved.
############################################################
open_project dilation_project
set_top dilation_accel
add_files ./xf_dilation_accel.cpp -cflags "-D__XFCV_HLS_MODE__ -I../../include --std=c++0x"
add_files -tb ./testcase55.jpg
add_files -tb ./xf_dilation_tb.cpp -cflags "-D__XFCV_HLS_MODE__ -I../../include --std=c++0x"
open_solution "solution1"
set_part {xczu9eg-ffvb1156-2-i} -tool vivado
create_clock -period 10 -name default
#source "./dilation_color/solution1/directives.tcl"
csim_design -argv {testcase55.jpg} -clean -compiler gcc
csynth_design
cosim_design -argv {testcase55.jpg}
#export_design -format ip_catalog


さて、このscript.tcl をVivado HLS 2018.3 で動作させることにしよう。

OpenCV をVivado HLS で使用する場合は、PNGやJPEG用のライブラリをインストールする必要がある。
エラーが出た場合は、
Ubuntu 18.04 上でVivado HLS 2018.2 でOpenCV を使用したとき?のエラー
Ubuntu 16.04 上のVivado HLS 2017.2 でOpenCV を使用したプロジェクトでエラー発生”を参照のこと。
私は、libjpeg.so.62 が無くて、sudo apt-get install libjpeg62-dev を実行した。

まずは、Vivado 2018.3 の実行ファイルにパスを通す。
source /tools/Xilinx/Viavdo/2018.3/settings64.sh

xfopencv/HLS_Use_Model/Standalone_HLS_Example ディレクトリに行く。
cd ~/xfopencv/HLS_Use_Model/Standalone_HLS_Example/

Vivado HLS 2018.3 に tcl スクリプトを実行させる。(Ubuntu 18.04 を使用している)
vivado_hls -f script.tcl
xfOpenCV_Vivado_HLS_1_190518.png

xfopencv/HLS_Use_Model/Standalone_HLS_Example ディレクトリに、dilation_project ディレクトリが生成された。
xfOpenCV_Vivado_HLS_2_190518.png

xfopencv/HLS_Use_Model/Standalone_HLS_Example/dilation_project/solution1/csim/build ディレクトリには、C シミュレーション結果の hls_out.jpg, out_error.jpg, out_ocv.jpg も表示されている。
xfOpenCV_Vivado_HLS_3_190518.png

Vivado HLS のバッチモードから抜けるために exit を入力した。
GUI モードのVivado HLS 2018.3 を起動した。
xfOpenCV_Vivado_HLS_4_190518.png

合成結果を見てみた。
xfOpenCV_Vivado_HLS_5_190518.png

C/RTL 協調シミュレーションをDump all で実行した。
xfOpenCV_Vivado_HLS_6_190518.png

波形を表示した。
xfOpenCV_Vivado_HLS_7_190518.png

波形を拡大した。
xfOpenCV_Vivado_HLS_8_190518.png
  1. 2019年05月18日 05:04 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

(目標)Vivado HLSで1クロック毎に結果を出力できるNNを作る5(チューンナップ2)

(目標)Vivado HLSで1クロック毎に結果を出力できるNNを作る4(チューンナップ1)”の続き。

MNIST データセットを使った2層の全結合層を持ったニューラル・ネットワークについて、目標の1クロック毎に結果を出力できるようにチューンナップしようとしたが、large runtime and excessive memory usage エラーになってしまった。今回は、そのエラーを回避するために 1/4 の入力数に変更して高位合成してみよう。

入力数を 784 から 196 つまり 14 x 14 に変更してみよう。
mnist_nn.cpp に一部を示す。

int mnist_nn(ap_ufixed<8, 0, AP_TRN, AP_WRAP> in[196], ap_fixed<13, 7, AP_TRN, AP_WRAP> out[10]){
#pragma HLS PIPELINE II=1
#pragma HLS ARRAY_PARTITION variable=af2_bias complete dim=0
#pragma HLS ARRAY_PARTITION variable=af2_weight complete dim=0
#pragma HLS ARRAY_PARTITION variable=af1_bias complete dim=0
#pragma HLS ARRAY_PARTITION variable=af1_weight complete dim=0
#pragma HLS ARRAY_PARTITION variable=out complete dim=0
#pragma HLS ARRAY_PARTITION variable=in complete dim=0

    ap_fixed<13, 7, AP_TRN, AP_WRAP> dot1[20];
#pragma HLS ARRAY_PARTITION variable=dot1 complete dim=1
    ap_fixed<13, 7, AP_TRN, AP_WRAP> dot2[10];
#pragma HLS ARRAY_PARTITION variable=dot2 complete dim=1

    af1_dot1: for(int col=0; col<20; col++){
        dot1[col] = 0;
        af1_dot2: for(int row=0; row<196; row++){
            dot1[col] += in[row]*af1_weight[row][col];
        }
        dot1[col] += af1_bias[col];

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

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

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

    return(0);
}


これで合成した。結果を示す。
all_deploy_afnet4mnist_21_190401.png

Latency は 33 クロックだが、Interval は 1 クロックになった。目標達成だ。
DSP48E は 607 個で 168 % 、LUTが 137589 個で 194 % でオーバーしているので、Ultra96 には実装できない。

さて、次に 10 回連続でニューラル・ネットワークの推論をさせてみよう。
mnist_nn.cpp の一部を示す。

int mnist_nn(ap_ufixed<8, 0, AP_TRN, AP_WRAP> in[1960], ap_fixed<13, 7, AP_TRN, AP_WRAP> out[100]){
#pragma HLS ARRAY_PARTITION variable=af2_bias complete dim=0
#pragma HLS ARRAY_PARTITION variable=af2_weight complete dim=0
#pragma HLS ARRAY_PARTITION variable=af1_bias complete dim=0
#pragma HLS ARRAY_PARTITION variable=af1_weight complete dim=0
#pragma HLS ARRAY_PARTITION variable=out complete dim=0
#pragma HLS ARRAY_PARTITION variable=in complete dim=0

    ap_fixed<13, 7, AP_TRN, AP_WRAP> dot1[20];
#pragma HLS ARRAY_PARTITION variable=dot1 complete dim=1
    ap_fixed<13, 7, AP_TRN, AP_WRAP> dot2[10];
#pragma HLS ARRAY_PARTITION variable=dot2 complete dim=1

    itre_10: for(int i=0; i<10; i++){
#pragma HLS PIPELINE II=1
        af1_dot1: for(int col=0; col<20; col++){
            dot1[col] = 0;
            af1_dot2: for(int row=0; row<196; row++){
                dot1[col] += in[row+i*196]*af1_weight[row][col];
            }
            dot1[col] += af1_bias[col];

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

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

            if(dot2[col] < 0)    // ReLU
                dot2[col] = 0;
            out[col+i*10] = dot2[col];
        }
    }

    return(0);
}


このコードで合成した。結果を示す。
all_deploy_afnet4mnist_22_190401.png

Latency の Detail -> Loop を見るとInitiation Interval の Target が 1 クリックで、 achieved も 1 クロックであるのが分かる。
リソース使用量は DSP48E は 607 個 168 % で変わらないが、 LUT が 147368 個で 208 % と増えてしまっている。

これで、Vivado HLSで1クロック毎に結果を出力できるNNを作るという目標は達成できたのだが、通常のMNIST のニューラル・ネットワークだと、Vivado HLS が large runtime and excessive memory usage エラーになって音を上げてしまう。ランタイムが大きくてもやってくれれば良いのだが。。。

なお、このmnist_nn.cpp を合成する時に”ARRAY_PARTITION COMPLETE has exceeded the threshold (1024)”のエラーが出てしまう。
” ARRAY_PARTITION COMPLETE has exceeded the threshold (1024)”の解決策”を参照しながら、maximum_size に 2000 を入れたら問題なく合成ができた。
all_deploy_afnet4mnist_23_190330.png

all_deploy_afnet4mnist_24_190330.png

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

(目標)Vivado HLSで1クロック毎に結果を出力できるNNを作る4(チューンナップ1)

(目標)Vivado HLSで1クロック毎に結果を出力できるNNを作る3(Vivado HLS)”の続き。

前回は、MNIST データセットを使った2層の全結合層を持ったニューラル・ネットワークについて、Vivado HLS でC シミュレーション、C コードの合成を行った。今回は目標の1クロック毎に結果を出力できるようにチューンナップしていこう。

今度はニューラルネットワークの回路全体を開始間隔 (II) = 1 のパイプライン実行をさせるということで、mnist_nn() 関数の下にパイプライン指示子を挿入した。

#pragma HLS PIPELINE II=1


これで合成を行ったところ、コードサイズが大きくなって、ランタイムが長くなって、メモリ使用量が過剰になる可能性があるというエラーが出てしまった。とことんやってくれれば良いのに。。。
all_deploy_afnet4mnist_18_190331.png

ERROR: [XFORM 203-504] Stop unrolling loop 'af1_dot1' (all_deploy_afnet4mnist2/mnist_nn.cpp:840) in function 'mnist_nn' because it may cause large runtime and excessive memory usage due to increase in code size. Please avoid unrolling the loop or form sub-functions for code in the loop body.
ERROR: [HLS 200-70] Pre-synthesis failed.


UG902 (v2018.3) 2018 年 12 月 20 日 高位合成 の 340 ページを見ると、ソリューションのコンフィギュレーション設定に config_compile があって、そのオプションに -ignore_long_run_time があるので、これをやってみようと思ったが、Vivado HLS のSolution Settings... のGeneral の Configuration Settings の config_compile には、 -ignore_long_run_time のオプションがない。
all_deploy_afnet4mnist_19_190331.png

そこで、Vivado HLS をTCLスクリプトから実行することにした。
参考にさせていただいたのは、msyksphinz/fpga_designs の fpga_designs/vivado_hls/edge_filter/script.tcl だった。
これを参考に ignore_long_run_time.tcl を書いた。
all_deploy_afnet4mnist_20_190331.png
ignore_long_run_time.tcl を示す。

open_project all_deploy_afnet4mnist3
set_top mnist_nn
add_files mnist_nn.cpp
open_solution solution1
create_clock -period 10 -name default

config_compile -ignore_long_run_time
set_part {xczu3eg-sbva484-1-e}

csynth_design


ignore_long_run_time.tcl を vivado_hls ignore_long_run_time.tcl で実行したが、やはり同様のエラーだった。ログを示す。

masaaki@masaaki-H110M4-M01:~/Vivado_HLS/Ultra96/NN/all_deploy_afnet4mnist3$ vivado_hls ignore_long_run_time.tcl 

****** Vivado(TM) HLS - High-Level Synthesis from C, C++ and SystemC v2018.3 (64-bit)
  **** SW Build 2405991 on Thu Dec  6 23:36:41 MST 2018
  **** IP Build 2404404 on Fri Dec  7 01:43:56 MST 2018
    ** Copyright 1986-2018 Xilinx, Inc. All Rights Reserved.

source /tools/Xilinx/Vivado/2018.3/scripts/vivado_hls/hls.tcl -notrace
INFO: [HLS 200-10] Running '/tools/Xilinx/Vivado/2018.3/bin/unwrapped/lnx64.o/vivado_hls'
INFO: [HLS 200-10] For user 'masaaki' on host 'masaaki-H110M4-M01' (Linux_x86_64 version 4.15.0-46-generic) on Sat Mar 30 12:31:54 JST 2019
INFO: [HLS 200-10] On os Ubuntu 18.04.2 LTS
INFO: [HLS 200-10] In directory '/home/masaaki/Vivado_HLS/Ultra96/NN/all_deploy_afnet4mnist3'
WARNING: [HLS 200-40] Environment variable 'C_INCLUDE_PATH' is set to :/usr/local/cuda/include.
INFO: [HLS 200-10] Opening project '/home/masaaki/Vivado_HLS/Ultra96/NN/all_deploy_afnet4mnist3/all_deploy_afnet4mnist3'.
INFO: [HLS 200-10] Adding design file 'mnist_nn.cpp' to the project
INFO: [HLS 200-10] Opening solution '/home/masaaki/Vivado_HLS/Ultra96/NN/all_deploy_afnet4mnist3/all_deploy_afnet4mnist3/solution1'.
INFO: [SYN 201-201] Setting up clock 'default' with a period of 10ns.
INFO: [HLS 200-10] Setting target device to 'xczu3eg-sbva484-1-e'
WARNING: [ANALYSIS 214-1] Skip long-run-time warning caused by lots of load/store instructions.
WARNING: [ANALYSIS 214-1] Skip long-run-time warning caused by lots of load/store instructions.
INFO: [SCHED 204-61] Option 'relax_ii_for_timing' is enabled, will increase II to preserve clock frequency constraints.
INFO: [HLS 200-10] Analyzing design file 'mnist_nn.cpp' ... 
INFO: [HLS 200-111] Finished Linking Time (s): cpu = 00:00:19 ; elapsed = 00:00:20 . Memory (MB): peak = 452.242 ; gain = 13.375 ; free physical = 25266 ; free virtual = 43474
INFO: [HLS 200-111] Finished Checking Pragmas Time (s): cpu = 00:00:19 ; elapsed = 00:00:20 . Memory (MB): peak = 452.242 ; gain = 13.375 ; free physical = 25266 ; free virtual = 43474
INFO: [HLS 200-10] Starting code transformations ...
INFO: [HLS 200-111] Finished Standard Transforms Time (s): cpu = 00:00:27 ; elapsed = 00:00:28 . Memory (MB): peak = 581.059 ; gain = 142.191 ; free physical = 25221 ; free virtual = 43429
INFO: [HLS 200-10] Checking synthesizability ...
INFO: [HLS 200-111] Finished Checking Synthesizability Time (s): cpu = 00:00:27 ; elapsed = 00:00:28 . Memory (MB): peak = 581.059 ; gain = 142.191 ; free physical = 25221 ; free virtual = 43429
INFO: [XFORM 203-502] Unrolling all sub-loops inside loop 'num_of_iter' (mnist_nn.cpp:839) in function 'mnist_nn' for pipelining.
INFO: [HLS 200-489] Unrolling loop 'af1_dot1' (mnist_nn.cpp:841) in function 'mnist_nn' completely with a factor of 20.
ERROR: [XFORM 203-504] Stop unrolling loop 'af1_dot1' (mnist_nn.cpp:841) in function 'mnist_nn' because it may cause large runtime and excessive memory usage due to increase in code size. Please avoid unrolling the loop or form sub-functions for code in the loop body.
ERROR: [HLS 200-70] Pre-synthesis failed.
command 'ap_source' returned error code
    while executing
"source ignore_long_run_time.tcl"
    invoked from within
"hls::main ignore_long_run_time.tcl"
    ("uplevel" body line 1)
    invoked from within
"uplevel 1 hls::main {*}$args"
    (procedure "hls_proc" line 5)
    invoked from within
"hls_proc $argv"
INFO: [Common 17-206] Exiting vivado_hls at Sat Mar 30 12:32:22 2019...


Viavdo HLS 2018.3 の能力の限界なのか?古いVivado HLS でやればもしかしたら行けるのかもしれない?
  1. 2019年03月31日 05:45 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

(目標)Vivado HLSで1クロック毎に結果を出力できるNNを作る3(Vivado HLS)

(目標)Vivado HLSで1クロック毎に結果を出力できるNNを作る2(ソースコード)”の続き。

前回は、MNIST データセットを使った2層の全結合層を持ったニューラル・ネットワークを構成し、そのソースコードを貼った。今回は、Vivado HLS でC シミュレーション、C コードの合成を行っていく。

まずは、Vivado HLS のプロジェクトを示す。
all_deploy_afnet4mnist_7_190329.png

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

ソフトウェアもハードウェアも100 個のデータの内の 2 個を間違っているので、精度は 98 % ということになる。

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

Latency は 301 クロック、Interval は 282 クロックだった。クロックは 10 ns なので、推論時間は 3.01 us となった。
一方でリソースの消費は激しい。DSP48E は 803 個で 803 / 360 x 100 = 223 % でオーバーしている。LUT も 75377 個で5000 固程度オーバーしてしまっている。
BRAM 使用量は 0 で array_partition complete が有効に効いているのが分かる。

Instance の Loop_af1_dot_proc_UO をクリックして、全結合層1層目の合成結果を見てみよう。
all_deploy_afnet4mnist_10_190329.png

DSP48E が 783 個使用されている。ここでもう 217 % 使用している。

Loop_af2_dot1_proc を表示してみよう。
all_deploy_afnet4mnist_11_190329.png

全結合層2層目でのリソース使用量はそれほど多くない。やはり、1層目で相当リソースを消費してしまっている。

Analyze 画面を表示する。
all_deploy_afnet4mnist_12_190329.png

Operation\Control Step の mnist_nn_entry21(function) を見てみよう。
all_deploy_afnet4mnist_13_190329.png

次に、Operation\Control Step の Loop_af1_dot1_proc(function) を見てみる。
all_deploy_afnet4mnist_14_190329.png

all_deploy_afnet4mnist_15_190329.png

785 ステートまである。

Operation\Control Step の Loop_af2_dot1_proc(function) を見てみる。
all_deploy_afnet4mnist_16_190329.png

all_deploy_afnet4mnist_17_190329.png

Loop_af2_dot1_proc(function) の Resource Viewer を見てみよう。
all_deploy_afnet4mnist_18_190329.png

最初のステートにRead が全て並んでいて、順番に乗算累算されているのが分かる。ここには表示されていないが、最後のステートでWrite が並んでいる。
  1. 2019年03月30日 06:24 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

(目標)Vivado HLSで1クロック毎に結果を出力できるNNを作る2(ソースコード)

(目標)Vivado HLSで1クロック毎に結果を出力できるNNを作る1(学習編)”の続き。

前回は、Jupyter Notebook を使用して MNIST データセットを使った2層の全結合層を持ったニューラル・ネットワークの学習を行って、重みとバイアスをC のヘッダファイルとして出力した。今回は、その重みとバイアスのヘッダファイルを使用して、Vivado HLS でニューラル・ネットワークを構成していこう。それで、今回は完成したソースコードを貼っておく。

Vivado HLS のソースコードとしては、”「ゼロから作るDeep Learning」の2層ニューラルネットワークのハードウェア化4(Vivado HLS)”の mnist_nn.c をベースにし、そこに、固定小数点の重みやバイアスをコピペして、更に指示子を追加した。主に、ARRAY_PARTITION complete を追加した。これは重みやバイアスが BRAM にアサインされるとポートが 2 つになってしまうので、性能向上ができないからだ。その代わり、膨大なLUT が必要となる。
mnist_nn.c のメインのソースコードを示す。

int mnist_nn(ap_ufixed<8, 0, AP_TRN, AP_WRAP> in[784], ap_fixed<13, 7, AP_TRN, AP_WRAP> out[10]){
#pragma HLS DATAFLOW
#pragma HLS ARRAY_PARTITION variable=af2_bias complete dim=0
#pragma HLS ARRAY_PARTITION variable=af2_weight complete dim=0
#pragma HLS ARRAY_PARTITION variable=af1_bias complete dim=0
#pragma HLS ARRAY_PARTITION variable=af1_weight complete dim=0
#pragma HLS ARRAY_PARTITION variable=out complete dim=0
#pragma HLS ARRAY_PARTITION variable=in complete dim=0

    ap_fixed<13, 7, AP_TRN, AP_WRAP> dot1[50];
#pragma HLS ARRAY_PARTITION variable=dot1 complete dim=1
    ap_fixed<13, 7, AP_TRN, AP_WRAP> dot2[10];
#pragma HLS ARRAY_PARTITION variable=dot2 complete dim=1

    af1_dot1: for(int col=0; col<20; col++){
#pragma HLS PIPELINE II=1
        dot1[col] = 0;
        af1_dot2: for(int row=0; row<784; row++){
            dot1[col] += in[row]*af1_weight[row][col];
        }
        dot1[col] += af1_bias[col];

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

    af2_dot1: for(int col=0; col<10; col++){
#pragma HLS PIPELINE II=1
        dot2[col] = 0;
        af2_dot2: for(int row=0; row<20; row++){
            dot2[col] += dot1[row]*af2_weight[row][col];
        }
        dot2[col] += af2_bias[col];

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

    return(0);
}


テストベンチの mnist_nn_tb.c を示す。

// mnist_nn_tb.cpp
// 2017/06/02 by marsee
//

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

#include "af1_weight.h"
#include "af1_bias.h"
#include "af2_weight.h"
#include "af2_bias.h"
#include "mnist_data.h"

int mnist_nn(ap_ufixed<8, 0, AP_TRN, AP_WRAP> in[784], ap_fixed<13, 7, AP_TRN, AP_WRAP> out[10]);
int mnist_nn_float(float in[784], float out[10]);
int max_ap_fixed(ap_fixed<13, 7, AP_TRN, AP_WRAP> out[10]);
int max_float(float out[10]);

#define NUM_ITERATIONS    100 // C Simulation
// #define NUM_ITERATIONS    2 // C/RTL CoSimulation

int main(){
    float x_test_float[NUM_ITERATIONS][784];
    ap_fixed<13, 7, AP_TRN, AP_WRAP> result_ap_fixed[NUM_ITERATIONS][10];
    float result_float[NUM_ITERATIONS][10];
    int max_id_hw, max_id_sw, max_id_ref;

    for(int i=0; i<NUM_ITERATIONS; i++)
        for(int j=0; j<784; j++)
            x_test_float[i][j] = (float)x_test[i][j];

    for(int i=0; i<NUM_ITERATIONS; i++){
        mnist_nn(&x_test[i][0], &result_ap_fixed[i][0]);
        mnist_nn_float(&x_test_float[i][0], &result_float[i][0]);
    }

    int errflag=0;
    for(int i=0; i<NUM_ITERATIONS; i++){
        max_id_hw = max_ap_fixed(&result_ap_fixed[i][0]);
        max_id_sw = max_float(&result_float[i][0]);
        max_id_ref = y_test[i];

        if(max_id_ref != max_id_hw){
            printf("id = %d, max_id_ref = %d, max_id_hw = %d\n", i, max_id_ref, max_id_hw);
            errflag = 1;
        }
        if(max_id_ref != max_id_sw){
            printf("id = %d, max_id_ref = %d, max_id_sw = %d\n", i, max_id_ref, max_id_sw);
            errflag = 1;
        }
    }
    if(errflag == 0)
        printf("No Error\n");

    return(0);
}

int mnist_nn_float(float in[784], float out[10]){
    float dot1[50];
    float dot2[10];

    af1_dot1: for(int col=0; col<20; col++){
        dot1[col] = 0;
        af1_dot2: for(int row=0; row<784; row++){
            dot1[col] += in[row]*af1_fweight[row][col];
        }
        dot1[col] += af1_fbias[col];

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

    af2_dot1: for(int col=0; col<10; col++){
        dot2[col] = 0;
        af2_dot2: for(int row=0; row<20; row++){
            dot2[col] += dot1[row]*af2_fweight[row][col];
        }
        dot2[col] += af2_fbias[col];

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

    return(0);
}

int max_ap_fixed(ap_fixed<13, 7, AP_TRN, AP_WRAP> out[10]){
    int max_id;
    ap_fixed<13, 5, AP_TRN, AP_WRAP> max;

    for(int i=0; i<10; i++){
        if(i == 0){
            max = out[0];
            max_id = 0;
        }else if(out[i]>max){
            max = out[i];
            max_id = i;
        }
    }
    return(max_id);
}

int max_float(float out[10]){
    int max_id;
    float max;

    for(int i=0; i<10; i++){
        if(i == 0){
            max = out[0];
            max_id = 0;
        }else if(out[i]>max){
            max = out[i];
            max_id = i;
        }
    }
    return(max_id);
}


MNIST のデータ・セットをC のヘッダファイルに変換した mnist_data.h は 1000 個のデータでは、GCC がセグメンテーション・フォールトで落ちてしまうので、100 のデータに変更した。
  1. 2019年03月29日 04:28 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0
»