Vengineerの戯言

人生は短いけど、長いです。人生を楽しみましょう!

Verilatorの中を調べる(その4)

@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 コマンドを実行しないと、対応するコードが生成されないんですね。
そうすることでシミュレーション速度の低下を防いでいるんですね。。。

*1:0U != VL_TESTPLUSARGS_I("trace")))) {の部分が、 if ($test$plusargs("trace") != 0) begin に対応しています。$dumpfile("logs/vlt_dump.vcd");に対応するのがvlSymsp->_vm_contexp__->dumpfile(VL_CVT_PACK_STR_NW(5,__Vtemp1