Vivado HLSのテストベンチでEigenを使う(失敗談)

# はじめに
最近，Xilinx社のVivado HLSで高位合成の勉強をチマチマとやっています．
今回はVivado HLSでテストベンチにC++の行列演算ライブラリEigen[^1]を使おうとした時の記録です．
[^1]: [Eigen](http://eigen.tuxfamily.org/index.php?title=Main_Page)

ですが・・・これは失敗談です．頑張ったけど，C/RTL cosmulationでコケてうまくいきませんでした・・・(泣)

追記(2019/06/02):あれこれ原因を探して，最終的にはうまく動くようになりました．[こちら](https://qiita.com/HoriThe3rd/items/7d9025342cfc55454046)にまとめましたので，合わせてどうぞ．

# シミュレーションとは
Vivado HLSには作成した(作成される)回路の動作を検証するために，シミュレーション機能が備わっています．大きく分けて以下の二つがあります．

- C Simulation
- C/RTL Cosimulation

前者は高位合成する前に，C/C++で記述された合成予定の関数をソフトウェアで正常に動作するか確認します．これはそもそも書いたコードが論理的に間違っていないかどうかを確認するためのもので，最も初期段階のテストと言えます．

後者は高位合成後に行われるシミュレーションです．詳細は勉強中ですが，高位合成された関数に，C/C++のテストベンチから入力を与え，検証対象の関数(RTL)から返ってきた結果をC/C++のテストベンチで正解と合致しているかを検証します．名前の通り，C/C++テストベンチとRTLの合成結果が連携して実行されるシミュレーションです．論理合成された回路が用いられるので，タイミングチャートなどで実際の動作を確認できます．

# 目指すこと
行列演算を行う回路を検証するとき，回路の出力と比較するために，正しい計算結果=正解を用意する必要があります．そこで，Vivado HLSの設定を行い，正解を用意するためにEigenをテストベンチのコードで使用できるようにします．

Eigenのはじき出した結果なら，正解として十分信用できるはずという算段です．

もちろん，Eigenを高位合成することは難しい(というか無理？)ので，あくまでソフトウェアとして動作するテストベンチのみの使用となります．

なお，Vivado HLSには"HLS線形代数ライブラリ"なるものが存在し，行列の演算などはすでに用意されているみたいです．これを使えば，行列演算を手作業で作成する手間はある程度減らせるかと思います．

## コード
次のようなコードを高位合成で回路化します．行列とベクトルの積o=wvを求めます．

```cpp:dot_multi.cpp
#define SIZE_ROW 10
#define SIZE_COL 20

void dot_multi(int w[SIZE_ROW][SIZE_COL], int v[SIZE_COL], int o[SIZE_ROW]){
    int tmp = 0;
    dot_multi_label1:for(int i=0; i<SIZE_ROW; i++){
        dot_multi_label2:for(int j=0; j<SIZE_COL; j++){
            tmp += w[i][j] * v[j];
        }
        o[i] = tmp;
        tmp = 0;
    }
}
```

テストベンチは次のようにしました．

```cpp:dot_multi_tb.cpp
#include <iostream>
#include <random>
#include "Eigen/Core"
#include "Eigen/Geometry"

using namespace std;

#define SIZE_ROW 10
#define SIZE_COL 20

void dot_multi(int w[SIZE_ROW][SIZE_COL], int v[SIZE_COL], int o[SIZE_ROW]);

int main()
{
    cout << "start" << endl;
    random_device rng;
    mt19937_64 mt(rng());
    uniform_int_distribution<> rng10(0, 10);

    int w[SIZE_ROW][SIZE_COL];
    int v[SIZE_COL];
    int result[SIZE_ROW];
    
    Eigen::MatrixXi ew = Eigen::MatrixXi::Zero(SIZE_ROW, SIZE_COL);
    Eigen::VectorXi ev = Eigen::VectorXi::Zero(SIZE_COL);
    Eigen::VectorXi eresult = Eigen::VectorXi::Zero(SIZE_ROW);

    // Initialize
    for(int i=0; i<SIZE_ROW; i++){
        for(int j=0; j<SIZE_COL; j++){
            int num = rng10(mt);
            w[i][j] = num;
            ew(i,j) = num;
        }
    }
    for(int i=0; i<SIZE_COL; i++){
        int num = rng10(mt);
        v[i] = num;
        ev(i) = num;
    }

    dot_multi(w, v, result);
    eresult = ew * ev;

    // Display arguments
    cout << "--- w ---" << endl;
    for(int i=0; i<SIZE_ROW; i++){
        for(int j=0; j<SIZE_COL; j++){
            cout << w[i][j] << " ";
        }
        cout << endl;
    }
    cout << "--- v --- " << endl;
    for(int i=0; i<SIZE_COL; i++){
        cout << v[i] << endl;
    }

    // Display the result
    cout << " --- Result --- " << endl;
    for(int i=0; i<SIZE_ROW; i++){
        cout << result[i] << endl;
    }
    cout << " --- Eigen Result --- " << endl;
    cout << eresult << endl;

    // Auto check
    for(int i=0; i<SIZE_ROW; i++){
        if(result[i] != eresult(i)){
            cout << "**** Test failed... :( ****" << endl;
            return 1;
        }
    }
    cout << "*** Test passed ***" << endl;
    return 0;
}
```


## 設定
次の設定を行いました．
- Include pathsの設定
- gccのオプション設定

ほぼ手探りですが，XilinxのユーザーガイドUG902[^2]なども参考になるかもしれません．

[^2]: [Vivado Design Suite ユーザーガイド　高位合成](https://japan.xilinx.com/support/documentation/sw_manuals_j/xilinx2018_3/ug902-vivado-high-level-synthesis.pdf)

## Include pathsの設定
以下の設定を行うことで，Vivado HLS内でコーディングする時に，自動補完などが効くようになります．

まず，テストベンチ用のコード(今回は，"dot_multi_tb.cpp")を右クリックして，Propertiesを開きます．
![スクリーンショット 2019-05-28 18.45.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/357417/2b4df58b-51a5-9ad9-668a-a00d76ce2d77.png)

C/C++ Build > Settingsを開き，GCC C++ CompilerのIncludesのところでAdd...ボタンを押して，include pathsにEigenのパスを追加します．

![スクリーンショット 2019-05-28 18.46.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/357417/1e38108f-75af-cdbb-02ae-fc66cbc403d1.png)

最後に，"Apply and Close"をクリックして終了です．

# gccのオプション設定
Include pathsの設定だけ行って，C Simulationを行うと，"そのようなファイルやディレクトリはありません"といってエラーとなります．gccがEigenのファイルを見つけられていないようです．

これは，gccのコマンドに与えるオプションを設定し，includeファイルを見つけられるようにします．

歯車マークのボタンを押して，Project Settingsを開きます．
![スクリーンショット 2019-05-28 18.51.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/357417/6fe148b1-256b-8a40-3444-c477546aacfb.png)

Simulation SettingsのTestBench Filesで対象のファイルをクリックし，"Edit CFLAGS..."を押します．
![スクリーンショット 2019-05-28 18.48.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/357417/797fb2cd-e308-5f08-1d79-f1417db1de39.png)

設定画面が開くので，ここに，-IオプションでEigenのパスを追加します．
![スクリーンショット 2019-05-28 18.49.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/357417/6fa943ef-e268-5fc8-6bce-cbdb37f82914.png)

## シミュレーション実行・・・
C Simulationを実行すると，ちゃんとEigenの行列の計算も実行でき，普通のC/C++のプログラムを走らせた時と同じ感覚で動いてくれました．

一方，論理合成した後，C/RTL Cosimulationを実行すると，以下のように，エラーとなってダメでした．Eigen関係のコードとincludeの記述をテストベンチコードから削除すると動きます．

```txt
INFO: [COSIM-47] Using XSIM for RTL simulation.
INFO: [COSIM-14] Instrumenting C test bench ...
ERROR: [COSIM-44] Input file: /home/hoge/hls_project/tutorial_dot_multi/tutorial_dot_multi/solution1/./sim/autowrap/testbench/dot_multi_tb.cpp_pre.cpp.line.cpp
Output file: /home/hoge/hls_project/tutorial_dot_multi/tutorial_dot_multi/solution1/sim/autowrap/testbench/dot_multi_tb.cpp_pre.cpp.tb.cpp.line
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 1
	at org.eclipse.cdt.internal.core.dom.parser.cpp.CPPTemplateDefinition.updateTemplateParameterBindings(CPPTemplateDefinition.java:366)

*** 中略 ***

org.eclipse.cdt.internal.core.dom.parser.AbstractGNUSourceCodeParser.parse(AbstractGNUSourceCodeParser.java:409)
	at autopilot.tbprocessor.CParser.parse(CParser.java:263)
	at autopilot.tbprocessor.CParser.main(CParser.java:169)
   Build using "/opt/Xilinx/Vivado/2018.3/tps/lnx64/gcc-6.2.0/bin/g++"
   Compiling dot_multi_tb.cpp_pre.cpp
   Compiling dot_multi.cpp_pre.cpp.tb.cpp
   Compiling apatb_dot_multi.cpp
   Generating cosim.tv.exe
INFO: [COSIM-302] Starting C TB testing ... 
ERROR: [COSIM-330] Aborting co-simulation: top function 'dot_multi' is not invoked in the test bench.
```

結局目論見は失敗に終わりました・・・

# 終わりに
今回，Vivado HLSのテストベンチでEigenを読み込もうとしましたが，Cシミュレーションはうまくいくものの，C/RTL Cosimulationはうまくいきませんでした．
テストベンチのソースコードはソフトウェアとして実行されるのだろうから，Eigenも動いてくれるかと思ったのですが，そう簡単にもいかないようです．

まだ，Vivado HLSは使い始めたばかりで勉強中なので，何かまだ不足していることがあるのかもしれません．今後うまくいくようなことがあれば追記したいです・・・

とりあえず，今回は失敗して終了です．