@Vengineerの戯言 : Twitter
SystemVerilogの世界へようこそ、すべては、SystemC v0.9公開から始まった
はじめに
昨日の「Verilatorの中を調べる(その3)」の続き。
今回は、examples/make_hello_c と examples/make_tracing_c では見てこなかった下記の機能についてみていきます。
- timescale
- coverage
- trace
Timescale
Verilog HDL では `timescale directive 、SystemVerilog では timeunit/timeprecision にて timescale を決めることができます。timescale がどのように設定されているのかを examples/make_hello_c で生成されたC++コードをみていきます。
下記のように、Vtopクラスの__Vconfigure メソッドて、Timescale (timeunit と timeprecision)を設定しています。
void Vtop::__Vconfigure(Vtop__Syms* vlSymsp, bool first) { if (false && first) {} // Prevent unused this->__VlSymsp = vlSymsp; if (false && this->__VlSymsp) {} // Prevent unused vlSymsp->_vm_contextp__->timeunit(-12); vlSymsp->_vm_contextp__->timeprecision(-12); }
下記のように、Verilog HDLコード(top.v) に、`timescale 100ns/1ns を追加してみました。
`timescale 100ns/1ns module top; initial begin $display("Hello World!"); $finish; end endmodule
この時の、__Vconfigure メソッドは次のようになりました。timeuinit は -7 に、timeprecision は -9 になりました。ということは、デフォルトの -12 は、1ps ということになります。
void Vtop::__Vconfigure(Vtop__Syms* vlSymsp, bool first) { if (false && first) {} // Prevent unused this->__VlSymsp = vlSymsp; if (false && this->__VlSymsp) {} // Prevent unused vlSymsp->_vm_contextp__->timeunit(-7); vlSymsp->_vm_contextp__->timeprecision(-9); }
module 内で、timeunit と timeprecision にて再設定すると、__Vconfigure メソッドの設定値も、-8 と -10 に変わりました。
`timescale 100ns/1ns module top; timeunit 10ns; timeprecision100ps; initial begin $display("Hello World!"); $finish; end endmodule
void Vtop::__Vconfigure(Vtop__Syms* vlSymsp, bool first) { if (false && first) {} // Prevent unused this->__VlSymsp = vlSymsp; if (false && this->__VlSymsp) {} // Prevent unused vlSymsp->_vm_contextp__->timeunit(-8); vlSymsp->_vm_contextp__->timeprecision(-10); }
coverage
verilator コマンドで、--coverage オプションを指定した時には、Vtop_Syms クラスの内容が変わるようです。examples/make_hello_c の例題で、verilator コマンドに --coverage オプションを付けたときの __Vconfigure メソッドの部分です。_configura_coverage(vlSymsp, first); が追加されました。
void Vtop::__Vconfigure(Vtop__Syms* vlSymsp, bool first) { if (false && first) {} // Prevent unused this->__VlSymsp = vlSymsp; if (false && this->__VlSymsp) {} // Prevent unused _configure_coverage(vlSymsp, first); vlSymsp->_vm_contextp__->timeunit(-12); vlSymsp->_vm_contextp__->timeprecision(-12); }
_configure_coverage メソッドは、次のようになっています。examples/make_hello_c の場合は // Body 以降に何もありません。
void Vtop::_configure_coverage(Vtop__Syms* __restrict vlSymsp, bool first) { VL_DEBUG_IF(VL_DBG_MSGF("+ Vtop::_configure_coverage\n"); ); // Body if (false && vlSymsp && first) {} // Prevent unused }
exampls/make_tracing_c の場合はどうなるでしょうか?
Vtop__Symsの constructor が次のようになっていて、下記のメンバー変数が追加されています。
, __Vm_dumping(false) , __Vm_dumperp(nullptr) , __Vm_activity(false) , __Vm_baseCode(0)
Vtop__Syms::Vtop__Syms(VerilatedContext* contextp, Vtop* topp, const char* namep) // Setup locals : VerilatedSyms{contextp} , __Vm_namep(namep) , __Vm_dumping(false) , __Vm_dumperp(nullptr) , __Vm_activity(false) , __Vm_baseCode(0) , __Vm_didInit(false) // Setup submodule names { // Pointer to top level TOPp = topp; // Setup each module's pointers to their submodules // Setup each module's pointer back to symbol table (for public functions) TOPp->__Vconfigure(this, true); // Setup scopes __Vscope_top__sub.configure(this, name(), "top.sub", "sub", -12, VerilatedScope::SCOPE_OTHER); __Vscope_top__sub__AssertionExample.configure(this, name(), "top.sub.AssertionExample", "AssertionExample", -12, VerilatedScope::SCOPE_OTHER); }
また、Vtopクラスの_configura_coverage メソッドは下記のように、// Body のしあに、__vlCoverInsert がいっぱい追加されています。この__vlCoverInsert メソッドがカバレッジのポイントになっているっぽいです。vlSymsp->__Vcoverage[X] に、ファイル名(top.v) の行数およびラベルを設定しています。vlSymsp->__Vcoverage[X]がカバレッジポイントになるようですね。
void Vtop::_configure_coverage(Vtop__Syms* __restrict vlSymsp, bool first) { VL_DEBUG_IF(VL_DBG_MSGF("+ Vtop::_configure_coverage\n"); ); // Body if (false && vlSymsp && first) {} // Prevent unused __vlCoverInsert(&(vlSymsp->__Vcoverage[0]), first, "top.v", 14, 23, ".top", "v_toggle/top", "clk", ""); __vlCoverInsert(&(vlSymsp->__Vcoverage[1]), first, "top.v", 15, 23, ".top", "v_toggle/top", "reset_l", ""); __vlCoverInsert(&(vlSymsp->__Vcoverage[2]), first, "top.v", 17, 23, ".top", "v_toggle/top", "out_small[0]", ""); __vlCoverInsert(&(vlSymsp->__Vcoverage[3]), first, "top.v", 17, 23, ".top", "v_toggle/top", "out_small[1]", "");
__vlCoverInsertメソッドは次のようになっています。
// Coverage void Vtop::__vlCoverInsert(uint32_t* countp, bool enable, const char* filenamep, int lineno, int column, const char* hierp, const char* pagep, const char* commentp, const char* linescovp) { uint32_t* count32p = countp; static uint32_t fake_zero_count = 0; if (!enable) count32p = &fake_zero_count; *count32p = 0; VL_COVER_INSERT(__VlSymsp->_vm_contextp__->coveragep(), count32p, "filename",filenamep, "lineno",lineno, "column",column, "hier",std::string(name())+hierp, "page",pagep, "comment",commentp, (linescovp[0] ? "linescov" : ""), linescovp); }
trace
波形ダンプ関連も coverage と同様に、--trace オプションを指定した時のみ生成されるようです。examples/make_tracing_c の Vtop.h の中を見ると、下記のような traceXXX というメソッドが追加されています。
void _traceDump(); void _traceDumpOpen(); void _traceDumpClose(); private: static void traceChgSub0(void* userp, VerilatedVcd* tracep); static void traceChgTop0(void* userp, VerilatedVcd* tracep); static void traceCleanup(void* userp, VerilatedVcd* /*unused*/); static void traceFullSub0(void* userp, VerilatedVcd* tracep) VL_ATTR_COLD; static void traceFullTop0(void* userp, VerilatedVcd* tracep) VL_ATTR_COLD; static void traceInitSub0(void* userp, VerilatedVcd* tracep) VL_ATTR_COLD; static void traceInitTop(void* userp, VerilatedVcd* tracep) VL_ATTR_COLD; void traceRegister(VerilatedVcd* tracep) VL_ATTR_COLD; static void traceInit(void* userp, VerilatedVcd* tracep, uint32_t code) VL_ATTR_COLD;
Verilog HDL コード (top.v) の initial 文の中で、$dumpfile システムタスク と $dumpvars システムタスクを呼んでいます。
initial begin if ($test$plusargs("trace") != 0) begin $display("[%0t] Tracing to logs/vlt_dump.vcd...\n", $time); $dumpfile("logs/vlt_dump.vcd"); $dumpvars(); end $display("[%0t] Model running...\n", $time); end
生成されたC++コードにおいて、 initial 文の中で、$dumpfile システムタスク と $dumpvars システムタスクに対応する部分が_initial__TOP__1 メソッドの中にあります。
if (VL_UNLIKELY*1;; になります。__Vtemp1には、”logs/vlt_dump.vcd” が設定されています。$dumpvars();がvlSymsp->Topp->_traceDumpOpen(); に対応しているのでしょう!
ちなみに、++(vlSymsp->__Vcoverage[226]);のようなコードは、coverage のところで説明したカバレッジポイントでこの行を実行すると実行数をインクリメントする感じになっています。
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; // Variables WData/*159:0*/ __Vtemp1[5]; // Body if (VL_UNLIKELY((0U != VL_TESTPLUSARGS_I("trace")))) { __Vtemp1[0U] = 0x2e766364U; __Vtemp1[1U] = 0x64756d70U; __Vtemp1[2U] = 0x766c745fU; __Vtemp1[3U] = 0x6f67732fU; __Vtemp1[4U] = 0x6cU; vlSymsp->_vm_contextp__->dumpfile(VL_CVT_PACK_STR_NW(5, __Vtemp1)); vlSymsp->TOPp->_traceDumpOpen(); ++(vlSymsp->__Vcoverage[226]); VL_WRITEF("[%0t] Tracing to logs/vlt_dump.vcd...\n\n", 64,VL_TIME_UNITED_Q(1)); } else { ++(vlSymsp->__Vcoverage[227]); } VL_WRITEF("[%0t] Model running...\n\n",64,VL_TIME_UNITED_Q(1)); ++(vlSymsp->__Vcoverage[228]); }
_traceDumpOpen メソッドは、Vtop__Trace__Slow.cpp ファイル内で定義されています。この中で VCDファイルをオープンしています。
void Vtop::_traceDumpOpen() { const VerilatedLockGuard lock(__VlSymsp->__Vm_dumperMutex); if (VL_UNLIKELY(!__VlSymsp->__Vm_dumperp)) { __VlSymsp->__Vm_dumperp = new VerilatedVcdC(); trace(__VlSymsp->__Vm_dumperp, 0, 0); std::string dumpfile = __VlSymsp->_vm_contextp__->dumpfile(); __VlSymsp->__Vm_dumperp->open(dumpfile.c_str()); __VlSymsp->__Vm_dumping = true; } }
おわりに
今回は、
- timescle
- coverage
- trace
について、見てみました。
coverage と trace については、--coverage オプション、--trace オプション を付けて、verilator コマンドを実行しないと、対応するコードが生成されないんですね。
そうすることでシミュレーション速度の低下を防いでいるんですね。。。