VerilatorとはVerilogコードをシミュレーション可能なC++コードに変換するツールです。Windowsにインストールしてみましょう。
MSYS2上でインストール
MSYSを起動して、以下をタイプ。32bit板を入れたい人はmingw-w64-i686-verilator。
pacman -S mingw-w64-x86_64-verilator
う~ん、ただ、折角入れるなら、最新バージョン入れたいな。(2018年1/31現在、pacmanのだと、バージョン3.914。最新は3.918。3.918のSupport > 64 bit decimal $display.が気になった。)
というわけで、MSYS2上で野良ビルドしてみましょう。野良ビルド必要ない人は上記のコマンドでOKです。次のシミュレーションしてみた、にどうぞお進みください。
まずは、字句解析器を作ってくれるflexが必要なので、入っていない場合はflexを入れておきましょう。flexは使ったことないけど、jflexにはよくお世話になっております。
pacman -S flex
私の場合、足りないのはflexだけでしたが、他に足りない物がある場合は各自で入れてください。
必要な物をインストールしたら、適当な作業用ディレクトリを作成し、そこに移動してから、以下のコマンドを実行します。何が足りないか分からない場合でも、configureを動かした段階で分かるのでひとまず実行しても良いでしょう。
git clone http://git.veripool.org/git/verilator cd verilator # もし特定のバージョンにしたい人は以下の様にしてください。(以下はversion3.904の場合) # git branch refs/tags/verilator_3_904 export CPPFLAGS=-I/usr/include autoconf ./configure make
/usr/includeの下にあるFlexLexer.hが見つからないとエラーが出るので、環境変数CPPFLAGSでインクルーディレクトリを追加しています。
特に問題なければ、そのままビルド完了するので、make installでインストール。
make install
ちなみに、make testを実行すると何か、「mkdir」でエラー起こしました。どうもパーミッションを渡そうとして、引数超過となっているらしい。う~ん、大丈夫かな?
まぁ、この程度なら実際にビルドする時にエラー起きても手動で簡単に直せそうだしいっか。
適当にシミュレーションしてみる
とりあえず、シミュレーションをしてみましょう。てきとーに、こんな状態遷移をする謎のステートマシンmoをVerilogで用意し、このステートマシンmoを動かすテストベンチをC++で用意しました。
mo.v
module mo ( input CLK, input RST, input I, output [1:0] STATE, output [1:0] NEXT_STATE, output DONE ); parameter STATE_START =2'd0; parameter STATE_A =2'd1; parameter STATE_B =2'd2; parameter STATE_END =2'd3; logic [1:0] state; logic [1:0] next_state; always @(posedge CLK,negedge RST) begin if(~RST) state <= STATE_START; else state <= next_state; end always @(*) begin case (state) STATE_START: next_state = I ? STATE_A : STATE_START; STATE_A : next_state = I ? STATE_B : STATE_A; STATE_B : next_state = I ? STATE_A : STATE_END; STATE_END : next_state = STATE_END; default : next_state = 2'bxx; endcase end assign STATE = state; assign NEXT_STATE=next_state; assign DONE = state == STATE_END; endmodule
tb_top.cpp
#include <iostream> #include "Vmo.h" #include "verilated.h" vluint64_t main_time = 0; //Current simulation time double sc_time_stamp() { //Called by $time in Verilog return main_time; } template<typename T> class Sim{ T* instance; public: template<typename... ARGS> Sim(ARGS... args):instance(new T(args...)){} ~Sim(){delete instance;} operator vluint64_t()const{return main_time;} void operator()(){ instance->eval(); main_time++; } bool isDone(vluint64_t max_sim_time)const{ return Verilated::gotFinish() || max_sim_time <= main_time; } T* operator ->()const noexcept{return instance;} }; int main(int argc, char *argv[]) { Verilated::commandArgs(argc,argv); Sim<Vmo> sim; sim->CLK = 0; sim->RST = 0; sim->I = 0; std::cout << "Simulation Start!"<<sim<<std::endl; while(!sim.isDone(100)){ if(sim == 10){ std::cout << "reset release" << std::endl; sim->RST = 1; } if((sim % 5) == 0) sim->CLK = !sim->CLK; if(sim == 10 || sim == 60){ sim->I = 1; std::cout << "sim->I=1"<<std::endl; }else if(sim == 40 || sim == 70){ sim->I = 0; std::cout << "sim->I=0"<<std::endl; } sim(); printf("Time %d :clk = %d,rst=%d,i=%d, state = %d,next_state = %d, done = %d\n", (vluint64_t)sim,sim->CLK,sim->RST,sim->I,sim->STATE,sim->NEXT_STATE,sim->DONE); } std::cout << "Simulation Done!"<<std::endl; sim->final(); return 0; }
Simクラスは、使い回そうと思って作っただけだから、大した意味は無いです。記憶力悪いから、main_timeをインクリメントしたり、Verilated::gotFinish() とかなんて一々覚えてられる訳がないのだ。
後、Vmoをnewしないといけないのは、理由はよく分からないけど、物によってはスタックに乗らない程度にデカいオブジェクトになることがあるからかな?mo程度の小さい物なら、別にnewしなくても普通に動いた。
で、これを次の様にしてビルドします。lintが邪魔な場合は、-Wallの代わりに、-Wno-lintを指定してください。
verilator --cc -Wall mo.v --exe tb_top.cpp make -j -C obj_dir -f Vmo.mk Vmo
ビルドに成功すると、obj_dirにVmo.exeが出来ているので、それを実行する。
obj_dir/Vmo.exe
正常に動いて、シミュレーション時間が81の段階でSTATEが3(STATE_END)に移行し、DONEが1になっているのが確認出来ました。
Time 80 :clk = 0,rst=1,i=0, state = 2,next_state = 3, done = 0 Time 81 :clk = 1,rst=1,i=0, state = 3,next_state = 3, done = 1 Time 82 :clk = 1,rst=1,i=0, state = 3,next_state = 3, done = 1
やったね ( ^ω^)