Verilator で波形ファイルを出力する - /var/log/shmknrk.log

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 を実行すればよい。

参考文献