Verilator で波形ファイルを出力する

Verilator は最速の Verilog シミュレータかつ lint ツールです。オープンソースのツールでありながら、機能が優れているので様々なプロジェクトで利用されています (Welcome to Verilator - Veripool)。

Verilator の大きな特徴として、VerilogC++コンパイルしてからシミュレーションすることが挙げられます。また、設計した回路のテストベンチを C++ で記述できるので、Verilog だけでは困難なシミュレーションを行えるという特徴もあります。

Verilator で波形ファイルを出力する方法ですが、これには以下の2通りの方法があります。

  1. Verilog ファイルに $dumpfile$dumpvars を追加し、iverilog などと同様な形式で波形ファイルを出力する方法
  2. C++ ファイルから直接波形ファイルを出力する方法

1つ目の方法は他の Verilog シミュレータを利用したことがある人にとっては親しみやすいと思います。2つ目の方法は C++ のみでテストベンチを記述するときに向いていると思います。これらの2通りの方法について、サンプルプロジェクトを例にそれぞれ説明します。

今回は C++ のテストベンチを利用する場合について紹介します。Verilog のファイルだけを使ってシミュレーションする場合は、verilator --binary --trace <verilog_file> などとし、後述する $dumpfile$dumpvars を追加すれば波形ファイルを出力することができます。


一次資料を読みたい方は以下がおすすめです。

動作環境

OS release        : Ubuntu 22.04.3 LTS
Verilator version : Verilator 5.019 devel rev v5.018-75-g39d9bd4d4

サンプルプロジェクト

サンプルプロジェクトのディレクトリ構成は以下のようになっています。

.
├── Makefile
├── sim_main.cpp
├── top.v
└── counter.v

sim_main.cpp が Verilator 用のテストベンチを記述した C++ ファイル、top.vVerilog のシミュレーション用の記述をした Verilog ファイル、counter.v がカウンタ回路を記述した Verilog ファイルです。Makefileソースコードをビルドして動かすために利用します。それぞれのファイルの内容を以下に示します。

Makefile

#===========================================================
# Sources
#-----------------------------------------------------------
SRCS                += $(wildcard *.v)
CXX_SRCS            += $(wildcard *.cpp)

#===========================================================
# Verilator
#-----------------------------------------------------------
VERILATOR           ?= verilator

VL_TOPNAME          := Vtop

VERILATOR_FLAGS     += --cc --exe --build
VERILATOR_FLAGS     += --prefix $(VL_TOPNAME)

VERILATOR_INPUT     += $(CXX_SRCS) $(SRCS)

#===========================================================
# Build rules
#-----------------------------------------------------------
.PHONY: default build run clean
default: build run

build:
  $(VERILATOR) $(VERILATOR_FLAGS) $(VERILATOR_INPUT)

run:
  obj_dir/$(VL_TOPNAME)

clean:
  rm -rf obj_dir
  rm -f *.vcd

sim_main.cpp

#include <verilated.h>
#include "Vtop.h"

int main (int argc, char **argv) {
    VerilatedContext *contextp = new VerilatedContext;
    contextp->commandArgs(argc, argv);

    Vtop *top = new Vtop{contextp};
    top->clk_i  = 0;
    top->rst_ni = 0;

    while (!contextp->gotFinish()) {
        contextp->timeInc(1);
        top->clk_i = !top->clk_i;
        if (!top->clk_i) {
            if (contextp->time()>5) {
                top->rst_ni = 1;
            }
        }
        top->eval();
    }

    top->final();
    delete top;
    return 0;
}

top.v

module top (
    input  wire clk_i ,
    input  wire rst_ni
);

    wire [31:0] cntr;
    counter counter0 (
        .clk_i (clk_i ),
        .rst_ni(rst_ni),
        .cntr_o(cntr  )
    );

    always @(negedge clk_i) begin
        $write("%10d\n", cntr);
        if (cntr==20) begin
            $finish;
        end
    end

endmodule

counter.v

module counter (
    input  wire        clk_i ,
    input  wire        rst_ni,
    output wire [31:0] cntr_o
);

    reg [31:0] cntr;
    assign cntr_o = cntr;

    always @(posedge clk_i) begin
        if (!rst_ni) begin
            cntr <= 0;
        end else begin
            cntr <= cntr+1;
        end
    end

endmodule

1. Verilog ファイルに $dumpfile$dumpvars を追加して波形ファイルを出力する方法

まずは1つ目の方法について説明します。変更を加えるファイルは sim_main.cpptop.vMakefile の3つです。

sim_main.cpp に波形の出力を有効化するための処理を追加します。以下のソースコードにおいて、コメントで <- Added と記述した行が新たに追加したコードです。

// sim_main.cpp
#include <verilated.h>
#include "Vtop.h"

int main (int argc, char **argv) {
    VerilatedContext *contextp = new VerilatedContext;
    contextp->commandArgs(argc, argv);
    contextp->traceEverOn(true); // <- Added

top.v$dumpfile$dumpvars を追加します。$dumpfile は波形ファイルの出力先を指定するものであり、$dumpvars は波形の出力を行うモジュールの階層を指定するものです。

// top.v
module top (
    input  wire clk_i ,
    input  wire rst_ni
);

    initial begin              // <- Added
        $dumpfile("dump.vcd"); // <- Added
        $dumpvars(0);          // <- Added
    end                        // <- Added

Makefile に波形ファイルを出力するための Verilator オプション --trace を追加します。--trace は VCD (Value Change Dump) 形式の波形ファイルを出力するために使用されます。

# Makefile
VERILATOR_FLAGS     += --cc --exe --build
VERILATOR_FLAGS     += --prefix $(VL_TOPNAME)
VERILATOR_FLAGS     += --trace # <- Added

VERILATOR_INPUT     += $(CXX_SRCS) $(SRCS)

あとは make を実行すればシミュレーションが行われ、波形ファイル dump.vcd が出力されます。波形を見るには gtkwave dump.vcd を実行すればいいです。

2. C++ ファイルから直接波形を出力する方法

次に、2つ目の方法について説明します。変更を加えるファイルは sim_main.cppMakefile の2つです。

sim_main.cpp に波形ファイルを出力するために必要なヘッダーファイル、波形の出力を有効化するための処理、波形ファイルのオープン、書き込み、クローズの処理を追加します。

// sim_main.cpp
#include <verilated.h>
#include <verilated_vcd_c.h> // <- Added
#include "Vtop.h"

int main (int argc, char **argv) {
    VerilatedContext *contextp = new VerilatedContext;
    contextp->commandArgs(argc, argv);
    contextp->traceEverOn(true); // <- Added

    Vtop *top = new Vtop{contextp};
    top->clk_i  = 0;
    top->rst_ni = 0;

    VerilatedVcdC *tfp = new VerilatedVcdC; // <- Added
    top->trace(tfp, 0);                     // <- Added
    tfp->open("dump.vcd");                  // <- Added

    while (!contextp->gotFinish()) {
        contextp->timeInc(1);
        top->clk_i = !top->clk_i;
        if (!top->clk_i) {
            if (contextp->time()>5) {
                top->rst_ni = 1;
            }
        }
        top->eval();
        tfp->dump(contextp->time()); // <- Added
    }

    tfp->close(); // <- Added
    top->final();
    delete tfp;   // <- Added
    delete top;
    return 0;
}

Makefile に波形ファイルを出力するための Verilator オプション --trace を追加します。

# Makefile
VERILATOR_FLAGS     += --cc --exe --build
VERILATOR_FLAGS     += --prefix $(VL_TOPNAME)
VERILATOR_FLAGS     += --trace # <- Added

VERILATOR_INPUT     += $(CXX_SRCS) $(SRCS)

あとは1つ目の方法と同様に make を実行すればシミュレーションが行われ、波形ファイル dump.vcd が出力されます。波形を見るには gtkwave dump.vcd を実行すればいいです。

参考文献