はじめに
最近,Xilinx社のVivado HLSで高位合成の勉強をチマチマとやっています.
今回はVivado HLSでテストベンチにC++の行列演算ライブラリEigen1を使おうとした時の記録です.
ですが・・・これは失敗談です.頑張ったけど,C/RTL cosmulationでコケてうまくいきませんでした・・・(泣)
追記(2019/06/02):あれこれ原因を探して,最終的にはうまく動くようになりました.こちらにまとめましたので,合わせてどうぞ.
シミュレーションとは
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を求めます.
# 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;
}
}
テストベンチは次のようにしました.
# 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のユーザーガイドUG9022なども参考になるかもしれません.
Include pathsの設定
以下の設定を行うことで,Vivado HLS内でコーディングする時に,自動補完などが効くようになります.
まず,テストベンチ用のコード(今回は,"dot_multi_tb.cpp")を右クリックして,Propertiesを開きます.
C/C++ Build > Settingsを開き,GCC C++ CompilerのIncludesのところでAdd...ボタンを押して,include pathsにEigenのパスを追加します.
最後に,"Apply and Close"をクリックして終了です.
gccのオプション設定
Include pathsの設定だけ行って,C Simulationを行うと,"そのようなファイルやディレクトリはありません"といってエラーとなります.gccがEigenのファイルを見つけられていないようです.
これは,gccのコマンドに与えるオプションを設定し,includeファイルを見つけられるようにします.
歯車マークのボタンを押して,Project Settingsを開きます.
Simulation SettingsのTestBench Filesで対象のファイルをクリックし,"Edit CFLAGS..."を押します.
設定画面が開くので,ここに,-IオプションでEigenのパスを追加します.
シミュレーション実行・・・
C Simulationを実行すると,ちゃんとEigenの行列の計算も実行でき,普通のC/C++のプログラムを走らせた時と同じ感覚で動いてくれました.
一方,論理合成した後,C/RTL Cosimulationを実行すると,以下のように,エラーとなってダメでした.Eigen関係のコードとincludeの記述をテストベンチコードから削除すると動きます.
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は使い始めたばかりで勉強中なので,何かまだ不足していることがあるのかもしれません.今後うまくいくようなことがあれば追記したいです・・・
とりあえず,今回は失敗して終了です.