@Vengineerの戯言 : Twitter
SystemVerilogの世界へようこそ、すべては、SystemC v0.9公開から始まった
はじめに
昨日の「Verilatorの中を調べる(その1)」の続き。
Verilatorの最新版 v4.200 では、その前の v4.110 とちょこっと変わったようです。。。それは、後程。
準備
最新版の v4.200 をインストール。あたしは Windows 10 上の WSL2/Ubuntu 20.04 + Visual Studio を使っています。
インストール先は、/usr/local/verilator/v4.200 です。
SystemCを使うので、v2.3.3 をビルドし、/usr/local/systemc/2.3.3 にインストールしています。
とりあえず、-V オプションにて確認。環境変数 PATH に /usr/local/verilator/v4.200/bin を追加し、SYSTEMC_INCLUDE に /usr/local/systemc/2.3.3/inculde を設定しています。
下記のログから、VERILATOR_ROOT に /usr/local/verilator/v4.200/share/verilator、SYSTMEC_INCLUDE に /usr/local/systemc/2.3.3/inculde が設定されているのがわかります。
$ verilator -V
Verilator 4.200 2021-03-12 rev v4.200
Copyright 2003-2021 by Wilson Snyder. Verilator is free software; you can
redistribute it and/or modify the Verilator internals under the terms of
either the GNU Lesser General Public License Version 3 or the Perl Artistic
License Version 2.0.
See https://verilator.org for documentation
Summary of configuration:
Compiled in defaults if not in environment:
SYSTEMC =
SYSTEMC_ARCH =
SYSTEMC_INCLUDE =
SYSTEMC_LIBDIR =
VERILATOR_ROOT = /usr/local/verilator/v4.200/share/verilator
SystemC system-wide = 1
Environment:
MAKE =
PERL =
SYSTEMC =
SYSTEMC_ARCH =
SYSTEMC_INCLUDE = /usr/local/systemc/2.3.3/include
SYSTEMC_LIBDIR =
VERILATOR_ROOT =
VERILATOR_BIN =
Features (based on environment or compiled-in support):
SystemC found = 1
examples/make_hello_c
examples/make_hello_c を見てみます。
Verilog HDLコードは、top.v 、テストベンチ側は sim_main.cpp (C++コード)です。あとは、Makefile です。
make コマンドを実行してみましょう。
以下のように、最初に verilator -cc --exe --build -j top.v sim_main.cpp が実行されています。
$ cd examples/make_hello_c
$ ls
Makefile sim_main.cpp top.v
$ make
- Verilator hello-world simple example
-- VERILATE & BUILD --------
verilator -cc --exe --build -j top.v sim_main.cpp
make[1]: Entering directory '/mnt/c/Users/haray/home/verilator/verilator/examples/make_hello_c/obj_dir'
g++ -I. -MMD -I/usr/local/verilator/v4.200/share/verilator/include -I/usr/local/verilator/v4.200/share/verilator/include/vltstd -DVM_COVERAGE=0 -DVM_SC=0 -DVM_TRACE=0 -DVM_TRACE_FST=0 -faligned-new -fcf-protection=none -Wno-bool-operation -Wno-sign-compare -Wno-uninitialized -Wno-unused-but-set-variable -Wno-unused-parameter -Wno-unused-variable -Wno-shadow -std=gnu++14 -Os -c -o sim_main.o ../sim_main.cpp
g++ -I. -MMD -I/usr/local/verilator/v4.200/share/verilator/include -I/usr/local/verilator/v4.200/share/verilator/include/vltstd -DVM_COVERAGE=0 -DVM_SC=0 -DVM_TRACE=0 -DVM_TRACE_FST=0 -faligned-new -fcf-protection=none -Wno-bool-operation -Wno-sign-compare -Wno-uninitialized -Wno-unused-but-set-variable -Wno-unused-parameter -Wno-unused-variable -Wno-shadow -std=gnu++14 -Os -c -o verilated.o /usr/local/verilator/v4.200/share/verilator/include/verilated.cpp
/usr/bin/perl /usr/local/verilator/v4.200/share/verilator/bin/verilator_includer -DVL_INCLUDE_OPT=include Vtop.cpp Vtop__Slow.cpp Vtop__Syms.cpp > Vtop__ALL.cpp
g++ -I. -MMD -I/usr/local/verilator/v4.200/share/verilator/include -I/usr/local/verilator/v4.200/share/verilator/include/vltstd -DVM_COVERAGE=0 -DVM_SC=0 -DVM_TRACE=0 -DVM_TRACE_FST=0 -faligned-new -fcf-protection=none -Wno-bool-operation -Wno-sign-compare -Wno-uninitialized -Wno-unused-but-set-variable -Wno-unused-parameter -Wno-unused-variable -Wno-shadow -std=gnu++14 -Os -c -o Vtop__ALL.o Vtop__ALL.cpp
Archive ar -cr Vtop__ALL.a Vtop__ALL.o
g++ sim_main.o verilated.o Vtop__ALL.a -o Vtop
make[1]: Leaving directory '/mnt/c/Users/haray/home/verilator/verilator/examples/make_hello_c/obj_dir'
-- RUN ---------------------
obj_dir/Vtop
Hello World!
- top.v:11: Verilog $finish
final
-- DONE --------------------
Note: Once this example is understood, see examples/make_tracing_c.
Note: Also see the EXAMPLE section in the verilator manpage/document.
cc オプションは、テストベンチ側に C++ を使う時に指定します。生成されるものが C++ コードになります。
exe オプションは、実行プログラムを生成します。
build オプションは、コードを生成して、実行プログラムを生成します。
ls コマンドにて生成されたコードを確認してみましょう。obj_dir ディレクトリが生成され、その下にいろいろなファイルが生成されています。
$ ls
Makefile obj_dir sim_main.cpp top.v
$ ls obj_dir
Vtop Vtop.h Vtop__ALL.a Vtop__ALL.d Vtop__Slow.cpp Vtop__Syms.h Vtop__verFiles.dat sim_main.d verilated.d
Vtop.cpp Vtop.mk Vtop__ALL.cpp Vtop__ALL.o Vtop__Syms.cpp Vtop__ver.d Vtop_classes.mk sim_main.o verilated.o
obj_dir はデフォルト値で、-Mdir directory_name にて変更できます。
$ make clean
$ verilator --c --exe --build -j top.v sim_main.cpp -Mdir xxx
$ ls
Makefile xxx sim_main.cpp top.v
build オプションを付けないで実行するとどうなるでしょうか?*.d や *.o および、Vtop_ALL.a や Vtop などのオブジェクトは生成されていません。--build オプションをしていすることでこれらオブジェクトを生成していることになります。
$ make clean
Makefile sim_main.cpp top.v
$ verilator --cc --exe top.v sim_main.cpp
$ ls obj_dir
Vtop.cpp Vtop.h Vtop.mk Vtop__Slow.cpp Vtop__Syms.cpp Vtop__Syms.h Vtop__ver.d Vtop__verFiles.dat Vtop_classes.mk
最初のログファイルを見てみると、obj_dir ディレクトリ内で、g++ コマンドにて生成されたC++コードをコンパイルしています。
コンパイルしているファイルは、../sim_main.cpp 、 /usr/local/verilator/v4.200/share/verilator/include/verilated.cpp、Vtop__ALL.cpp です。ただし、Vtop_ALL.cpp は、Vtop.cpp,
Vtop_Slow.cpp Vtop__Syms.cpp を リダイレクトしたファイルです。Vtop__ALL.o は、ar コマンドに Vtop_ALL.a にして、最後に、sim_main.o verilated.o Vtop_ALL.a から Vtop を生成しています。
make[1]: Entering directory '/mnt/c/Users/haray/home/verilator/verilator/examples/make_hello_c/obj_dir'
g++ -I. -MMD -I/usr/local/verilator/v4.200/share/verilator/include -I/usr/local/verilator/v4.200/share/verilator/include/vltstd -DVM_COVERAGE=0 -DVM_SC=0 -DVM_TRACE=0 -DVM_TRACE_FST=0 -faligned-new -fcf-protection=none -Wno-bool-operation -Wno-sign-compare -Wno-uninitialized -Wno-unused-but-set-variable -Wno-unused-parameter -Wno-unused-variable -Wno-shadow -std=gnu++14 -Os -c -o sim_main.o ../sim_main.cpp
g++ -I. -MMD -I/usr/local/verilator/v4.200/share/verilator/include -I/usr/local/verilator/v4.200/share/verilator/include/vltstd -DVM_COVERAGE=0 -DVM_SC=0 -DVM_TRACE=0 -DVM_TRACE_FST=0 -faligned-new -fcf-protection=none -Wno-bool-operation -Wno-sign-compare -Wno-uninitialized -Wno-unused-but-set-variable -Wno-unused-parameter -Wno-unused-variable -Wno-shadow -std=gnu++14 -Os -c -o verilated.o /usr/local/verilator/v4.200/share/verilator/include/verilated.cpp
/usr/bin/perl /usr/local/verilator/v4.200/share/verilator/bin/verilator_includer -DVL_INCLUDE_OPT=include Vtop.cpp Vtop__Slow.cpp Vtop__Syms.cpp > Vtop__ALL.cpp
g++ -I. -MMD -I/usr/local/verilator/v4.200/share/verilator/include -I/usr/local/verilator/v4.200/share/verilator/include/vltstd -DVM_COVERAGE=0 -DVM_SC=0 -DVM_TRACE=0 -DVM_TRACE_FST=0 -faligned-new -fcf-protection=none -Wno-bool-operation -Wno-sign-compare -Wno-uninitialized -Wno-unused-but-set-variable -Wno-unused-parameter -Wno-unused-variable -Wno-shadow -std=gnu++14 -Os -c -o Vtop__ALL.o Vtop__ALL.cpp
Archive ar -cr Vtop__ALL.a Vtop__ALL.o
g++ sim_main.o verilated.o Vtop__ALL.a -o Vtop
make[1]: Leaving directory '/mnt/c/Users/haray/home/verilator/verilator/examples/make_hello_c/obj_dir'
生成された Vtop.h を見てみる
まずは、top.v の中を見てみます。initial 文内で、$display システムタスクと $finish システムタスクを実行しているだけです。
module top;
initial begin
$display("Hello World!");
$finish;
end
endmodule
生成された Vtop.h を見てみましょう。
#ifndef VERILATED_VTOP_H_
#define VERILATED_VTOP_H_
#include "verilated_heavy.h"
class Vtop__Syms;
VL_MODULE(Vtop) {
public:
Vtop__Syms* __VlSymsp;
private:
VL_UNCOPYABLE(Vtop);
public:
Vtop(VerilatedContext* contextp, const char* name = "TOP");
Vtop(const char* name = "TOP")
: Vtop(nullptr, name) {}
~Vtop();
VerilatedContext* contextp();
void eval() { eval_step(); }
void eval_step();
void eval_end_step() {}
void final();
static void _eval_initial_loop(Vtop__Syms* __restrict vlSymsp);
void __Vconfigure(Vtop__Syms* symsp, bool first);
private:
static QData _change_request(Vtop__Syms* __restrict vlSymsp);
static QData _change_request_1(Vtop__Syms* __restrict vlSymsp);
void _ctor_var_reset() VL_ATTR_COLD;
public:
static void _eval(Vtop__Syms* __restrict vlSymsp);
private:
#ifdef VL_DEBUG
void _eval_debug_assertions();
#endif
public:
static void _eval_initial(Vtop__Syms* __restrict vlSymsp) VL_ATTR_COLD;
static void _eval_settle(Vtop__Syms* __restrict vlSymsp) VL_ATTR_COLD;
static void _final_TOP(Vtop__Syms* __restrict vlSymsp) VL_ATTR_COLD;
static void _initial__TOP__1(Vtop__Syms* __restrict vlSymsp) VL_ATTR_COLD;
} VL_ATTR_ALIGNED(VL_CACHE_LINE_BYTES);
#endif
VL_MODULE(Vtop) というマクロを使ったクラス定義になっています。VL_MODULEは、下記のように、include/verilated.h の中でマクロ定義されています。VerilatedModule クラスを継承した Vtop というクラスになります。
include/verilated.h:#define VL_MODULE(modname) class modname VL_NOT_FINAL : public VerilatedModule
テストベンチの sim_main.cpp を見てみる
テストベンチの sim_main.cpp を見てみます。#include にて、 と "Vtop.h" を 取り込んでいます。verilated.h は、上記の Vtop.h の中で VL_MODULE マクロを定義しているファイルです。main の中では、Verilated::randReset(1)、Verilated::goFinish() を使っていますが、これらは verilated.h の中で宣言されています。
Vtop* top = new Vtop; で top.v から生成された Vtop クラスのインスタンスを生成しています。生成したインスタンス top のメソッドである eval と final を呼んでいます。
#include <verilated.h>
#include "Vtop.h"
int main(int argc, char** argv, char** env) {
if (false && argc && argv && env) {}
Verilated::randReset(1);
Vtop* top = new Vtop;
while (!Verilated::gotFinish()) {
top->eval();
}
top->final();
delete top;
return 0;
}
eval および final メソッド の中を見てみる
eval は、Vtop.hおよびVtop.cpp 、final は Vtop_Slow.cpp の中で以下のようにメソッドが定義されています。
eval メソッドの中で、eval_step メソッドが呼ばれています。eval_step メソッドは、Vtop.cpp の中で下記のように定義されています。__Vchange が 1 の間、do - while loop を実行しています。
void eval() { eval_step(); }
void Vtop::eval_step() {
VL_DEBUG_IF(VL_DBG_MSGF("+++++TOP Evaluate Vtop::eval\n"); );
Vtop__Syms* __restrict vlSymsp = this->__VlSymsp;
Vtop* const __restrict vlTOPp VL_ATTR_UNUSED = vlSymsp->TOPp;
#ifdef VL_DEBUG
_eval_debug_assertions();
#endif
if (VL_UNLIKELY(!vlSymsp->__Vm_didInit)) _eval_initial_loop(vlSymsp);
int __VclockLoop = 0;
QData __Vchange = 1;
do {
VL_DEBUG_IF(VL_DBG_MSGF("+ Clock loop\n"););
_eval(vlSymsp);
if (VL_UNLIKELY(++__VclockLoop > 100)) {
int __Vsaved_debug = Verilated::debug();
Verilated::debug(1);
__Vchange = _change_request(vlSymsp);
Verilated::debug(__Vsaved_debug);
VL_FATAL_MT("top.v", 8, "",
"Verilated model didn't converge\n"
"- See DIDNOTCONVERGE in the Verilator manual");
} else {
__Vchange = _change_request(vlSymsp);
}
} while (VL_UNLIKELY(__Vchange));
}
一方、final メソッドは、Vtop_Slow.cpp で下記のように定義されています。_final_TOP メソッドが呼び出されています。
void Vtop::final() {
VL_DEBUG_IF(VL_DBG_MSGF("+ Vtop::final\n"); );
Vtop__Syms* __restrict vlSymsp = this->__VlSymsp;
Vtop* const __restrict vlTOPp VL_ATTR_UNUSED = vlSymsp->TOPp;
vlTOPp->_final_TOP(vlSymsp);
}
_final_TOP メソッドも Vtop_Slow.cpp の中で次のように定義されています。特に何もしていません。
void Vtop::_final_TOP(Vtop__Syms* __restrict vlSymsp) {
VL_DEBUG_IF(VL_DBG_MSGF("+ Vtop::_final_TOP\n"); );
Vtop* const __restrict vlTOPp VL_ATTR_UNUSED = vlSymsp->TOPp;
}
Vtop を実行してみる
生成された Vtop を実行してみましょう。Hello World! は、$display システムタスクの出力、- top.v:11:Verilog $finish は、$finish システムタスクの出力になります。
$ obj_dir/Vtop
Hello World!
- top.v:11: Verilog $finish
make コマンドの最後の部分と同じです。
-- RUN ---------------------
obj_dir/Vtop
Hello World!
- top.v:11: Verilog $finish
-- DONE --------------------
Verilog HDL の initial 文はどうなっているのか?
Verilog HDL の initial 文は、生成された Vtop クラスの中でどのように実装されているのでしょうか?
Vtop__Slow.cpp の initial__TOP__1 メソッドが Verilog HDL の initial 文に対応しているようです。下記のように // Body のコメントの後に、VL_WRITE("Hello World!\n"); と VL_FINISH_MT("top.v, 11, ""); が実行されています。これは、obj_dir/Vtop の実行結果に一致します。
void Vtop::_initial__TOP__1(Vtop__Syms* __restrict vlSymsp) {
VL_DEBUG_IF(VL_DBG_MSGF("+ Vtop::_initial__TOP__1\n"); );
Vtop* const __restrict vlTOPp VL_ATTR_UNUSED = vlSymsp->TOPp;
VL_WRITEF("Hello World!\n");
VL_FINISH_MT("top.v", 11, "");
}
initial__TOP__1 メソッドは、以下のように、Vtop__Slow.cpp の eval_initial メソッドから呼ばれています。
void Vtop::_eval_initial(Vtop__Syms* __restrict vlSymsp) {
VL_DEBUG_IF(VL_DBG_MSGF("+ Vtop::_eval_initial\n"); );
Vtop* const __restrict vlTOPp VL_ATTR_UNUSED = vlSymsp->TOPp;
vlTOPp->_initial__TOP__1(vlSymsp);
}
_eval_initial メソッドは、Vtop.cpp の eval_initial_loop メソッドの最初に呼ばれています。
void Vtop::_eval_initial_loop(Vtop__Syms* __restrict vlSymsp) {
vlSymsp->__Vm_didInit = true;
_eval_initial(vlSymsp);
int __VclockLoop = 0;
QData __Vchange = 1;
eval_initial_loop メソッドは、Vtop.cpp の eval_step メソッドの最初で呼ばれています。
void Vtop::eval_step() {
VL_DEBUG_IF(VL_DBG_MSGF("+++++TOP Evaluate Vtop::eval\n"); );
Vtop__Syms* __restrict vlSymsp = this->__VlSymsp;
Vtop* const __restrict vlTOPp VL_ATTR_UNUSED = vlSymsp->TOPp;
#ifdef VL_DEBUG
_eval_debug_assertions();
#endif
if (VL_UNLIKELY(!vlSymsp->__Vm_didInit)) _eval_initial_loop(vlSymsp);
int __VclockLoop = 0;
QData __Vchange = 1;
C++モデルの eval と Verilog HDL の initial 文の関係
以上のことからまとめると、
C++モデルの eval => eval_step => _eval_initial_loop => _eval_initial => initial__TOP__1 になり、initial_TOP__1 が Verilog HDL の initial 文に対応することになります。
initial 文を複数書くと?
initial 文を下記のように追加してみました。
module top;
initial begin
$display("Hello World!");
$finish;
end
initial begin
$display("Hello World!-2");
end
endmodule
make コマンドでの実行結果は、下記のようになりました。
-- RUN ---------------------
obj_dir/Vtop
Hello World!
- top.v:11: Verilog $finish
Hello World!-2
-- DONE --------------------
書いた順番に、initial 文が実行されているっぽいです。
生成されたコードの initial__TOP__1 メソッドは、下記のようになっています。
void Vtop::_initial__TOP__1(Vtop__Syms* __restrict vlSymsp) {
VL_DEBUG_IF(VL_DBG_MSGF("+ Vtop::_initial__TOP__1\n"); );
Vtop* const __restrict vlTOPp VL_ATTR_UNUSED = vlSymsp->TOPp;
VL_WRITEF("Hello World!\n");
VL_FINISH_MT("top.v", 11, "");
VL_WRITEF("Hello World!-2\n");
}
initial 文をもう一つ追加しました。今度は、最初の initial 文の前に追加しました。
module top;
initial begin
$display("Hello World!-0");
end
initial begin
$display("Hello World!");
$finish;
end
initial begin
$display("Hello World!-2");
end
endmodule
実行結果は以下の通り。
-- RUN ---------------------
obj_dir/Vtop
Hello World!-0
Hello World!
- top.v:14: Verilog $finish
Hello World!-2
-- DONE --------------------
* おわりに
生成されたコードは、下記のように、最初の initial 文の Hellow World!-0 と 2番目の Hello World! が 1つの LV_WRITEF 文でまとまっていますね。
void Vtop::_initial__TOP__1(Vtop__Syms* __restrict vlSymsp) {
VL_DEBUG_IF(VL_DBG_MSGF("+ Vtop::_initial__TOP__1\n"); );
Vtop* const __restrict vlTOPp VL_ATTR_UNUSED = vlSymsp->TOPp;
VL_WRITEF("Hello World!-0\nHello World!\n");
VL_FINISH_MT("top.v", 14, "");
VL_WRITEF("Hello World!-2\n");
}
final メソッドは何をする?
Vtop クラスの final メソッドは、何をするのだろうか?と思ったのですが、SystemVerilog には、final 文なるものがあります。どうやら、この final 文が final メソッドになるっぽです。
top.v に final 文を追加してみました。
module top;
initial begin
$display("Hello World!");
$finish;
end
final begin
$display("final");
end
endmodule
実行結果の最後に、final が表示されますね。
-- RUN ---------------------
obj_dir/Vtop
Hello World!
- top.v:16: Verilog $finish
final
-- DONE --------------------
生成されたコードを見てみると、// Body の後に、VL_WRITEF("final\n"); が追加されています。
void Vtop::_final_TOP(Vtop__Syms* __restrict vlSymsp) {
VL_DEBUG_IF(VL_DBG_MSGF("+ Vtop::_final_TOP\n"); );
Vtop* const __restrict vlTOPp VL_ATTR_UNUSED = vlSymsp->TOPp;
VL_WRITEF("final\n");
}
Verilated::gotFinish() メソッドとは?
テストベンチ sim_main.cppの中の Verilated::gotFinish() メソッドって、何だろうか?
while (!Verilated::gotFinish()) {
top->eval();
}
Verilated::gotFinishメソッドは、include/verilated.h に以下のように定義されています。Verilated::threadContexp()->gotFinish() の戻り値を返しています。
static bool gotFinish() VL_MT_SAFE { return Verilated::threadContextp()->gotFinish(); }
こっちの gotFinish() は、include/verilated.h に次のように定義されています。m_s.m_gotFinish の値を返しているだけですね。
bool gotFinish() const VL_MT_SAFE { return m_s.m_gotFinish; }
m_s.m_gotFinish は、include/verilated.cpp の中で次のように gotFinish メソッドで設定されています。
void VerilatedContext::gotFinish(bool flag) VL_MT_SAFE {
const VerilatedLockGuard lock(m_mutex);
m_s.m_gotFinish = flag;
}
gotFinish(bool flag) メソッドは、include/verilated.cpp の中の vl_finish、vl_stop、vl_fatal メソッドの中で呼ばれています。
#ifndef VL_USER_FINISH
void vl_finish(const char* filename, int linenum, const char* hier) VL_MT_UNSAFE {
if (false && hier) {}
VL_PRINTF(
"- %s:%d: Verilog $finish\n", filename, linenum);
if (Verilated::threadContextp()->gotFinish()) {
VL_PRINTF(
"- %s:%d: Second verilog $finish, exiting\n", filename, linenum);
Verilated::runFlushCallbacks();
Verilated::runExitCallbacks();
exit(0);
}
Verilated::threadContextp()->gotFinish(true);
}
#endif
#ifndef VL_USER_STOP
void vl_stop(const char* filename, int linenum, const char* hier) VL_MT_UNSAFE {
const char* const msg = "Verilog $stop";
Verilated::threadContextp()->gotError(true);
Verilated::threadContextp()->gotFinish(true);
if (Verilated::threadContextp()->fatalOnError()) {
vl_fatal(filename, linenum, hier, msg);
} else {
if (filename && filename[0]) {
VL_PRINTF("%%Error: %s:%d: %s\n", filename, linenum, msg);
} else {
VL_PRINTF("%%Error: %s\n", msg);
}
Verilated::runFlushCallbacks();
}
}
#endif
#ifndef VL_USER_FATAL
void vl_fatal(const char* filename, int linenum, const char* hier, const char* msg) VL_MT_UNSAFE {
if (false && hier) {}
Verilated::threadContextp()->gotError(true);
Verilated::threadContextp()->gotFinish(true);
if (filename && filename[0]) {
VL_PRINTF("%%Error: %s:%d: %s\n", filename, linenum, msg);
} else {
VL_PRINTF("%%Error: %s\n", msg);
}
Verilated::runFlushCallbacks();
VL_PRINTF("Aborting...\n");
Verilated::runFlushCallbacks();
Verilated::runExitCallbacks();
abort();
}
#endif
vl_finish、vl_stop、vl_fatal メソッドは、Verilog HDLの $finish、$stop、$fatal(これは、SystemVerilogかな?) システムタスクに対応していて、include/verilated.cpp で次のように定義されています。
void VL_FINISH_MT(const char* filename, int linenum, const char* hier) VL_MT_SAFE {
#ifdef VL_THREADED
VerilatedThreadMsgQueue::post(VerilatedMsg([=]() { //
vl_finish(filename, linenum, hier);
}));
#else
vl_finish(filename, linenum, hier);
#endif
}
void VL_STOP_MT(const char* filename, int linenum, const char* hier, bool maybe) VL_MT_SAFE {
#ifdef VL_THREADED
VerilatedThreadMsgQueue::post(VerilatedMsg([=]() { //
vl_stop_maybe(filename, linenum, hier, maybe);
}));
#else
vl_stop_maybe(filename, linenum, hier, maybe);
#endif
}
void VL_FATAL_MT(const char* filename, int linenum, const char* hier, const char* msg) VL_MT_SAFE {
#ifdef VL_THREADED
VerilatedThreadMsgQueue::post(VerilatedMsg([=]() { //
vl_fatal(filename, linenum, hier, msg);
}));
#else
vl_fatal(filename, linenum, hier, msg);
#endif
}
ということは、下記のように、$finish; をコメントアウトすると、シミュレーションは終わらないのか?
module top;
initial begin
$display("Hello World!");
end
endmodule
はい、終わりませんでした。
$ make
-- RUN ---------------------
obj_dir/Vtop
Hello World!
$stop システムタスクを追加したみたら、どうなるでしょうか?
module top;
initial begin
$display("Hello World!");
$stop();
end
endmodule
シミュレーションは終わりますが、%Error および Aborting ... になりますね。
-- RUN ---------------------
obj_dir/Vtop
Hello World!
%Error: top.v:16: Verilog $stop
Aborting...
make: *** [Makefile:40: default] Aborted
$fatal システムタスクにしたみたら、ちょっと違うメッセージですが、%Error および Aborting ... になりますね。
module top;
initial begin
$display("Hello World!");
$fatal();
end
endmodule
-- RUN ---------------------
obj_dir/Vtop
Hello World!
[0] %Error: top.v:16: Assertion failed in TOP.top
%Error: top.v:16: Verilog $stop
Aborting...
make: *** [Makefile:40: default] Aborted
$stop システムタスクの場合は、fatalなエラーが発生した場合(fatalOnError()が0出ない時)は、vl_fatal ($fatalシステムタスクを呼んだ場合と同じ)を呼んでいますね。
#ifndef VL_USER_STOP
void vl_stop(const char* filename, int linenum, const char* hier) VL_MT_UNSAFE {
const char* const msg = "Verilog $stop";
Verilated::threadContextp()->gotError(true);
Verilated::threadContextp()->gotFinish(true);
if (Verilated::threadContextp()->fatalOnError()) {
vl_fatal(filename, linenum, hier, msg);
} else {
if (filename && filename[0]) {
VL_PRINTF("%%Error: %s:%d: %s\n", filename, linenum, msg);
} else {
VL_PRINTF("%%Error: %s\n", msg);
}
Verilated::runFlushCallbacks();
}
}
#endif
$fatal システムタスクでは、vl_fatal メソッドの最後に abort() 関数を呼んでいますね。
#ifndef VL_USER_FATAL
void vl_fatal(const char* filename, int linenum, const char* hier, const char* msg) VL_MT_UNSAFE {
if (false && hier) {}
Verilated::threadContextp()->gotError(true);
Verilated::threadContextp()->gotFinish(true);
if (filename && filename[0]) {
VL_PRINTF("%%Error: %s:%d: %s\n", filename, linenum, msg);
} else {
VL_PRINTF("%%Error: %s\n", msg);
}
Verilated::runFlushCallbacks();
VL_PRINTF("Aborting...\n");
Verilated::runFlushCallbacks();
Verilated::runExitCallbacks();
abort();
}
#endif
おわりに
Verilatror の中を調べるということで、例題の examples/make_hello_c で生成される C++ コードとその中で呼ばれる メソッドを調べてみました。
make_hello_c の top.v には、initial 文しかないですが、それだけでも、いろいろなことが行われていることがわかって、凄くためになりました。
次回は、examples/make_tracing_c を見てみます。
Verilatorの最新版 v4.200 では、その前の v4.110 とちょこっと変わった部分の例題が examples/make_trace_c にありましたので。
こうご期待。。。