みねっちょのマイコン関係ブログ

組込開発系フリーソフトやハードの情報発信ブログ

WSL の Verilator で トレース波形をダンプする

サイト内 Google 検索:


最終更新: 2021-05-03

Verilator は サイクルベース方式で高速な無償の Verilog HDL シミュレータです。本記事では、WSL 上 Ubuntu-20.04 の標準パッケージだけを使用して、GUI でシミュレーション波形を観測する手順を紹介します。Ubuntu-20.04 標準パッケージ版 V4.0 と最新版 の Verilator V4.2 との差異も解説します。

Verilator は RISC-V の開発に使用され、SiFive 社より既に製品が発売されています。しかし、Verilator は未だ開発途上のソフトウェアな為、バージョンによっては機能に制限が生じます。

目次:



前提条件:

公式ドキュメントと Ubuntu-20.04 版の違い:

2021年5月現在、Verilator の公式サイトのドキュメントは V4.202 版です。一方、Ubuntu-20.04 の標準レポジトリからインストールした物は V4.028 版です。この部分で、公式ドキュメントと若干の差異が有ります。

波形をダンプするに当たっては、次の 公式ドキュメント FAQ の How do I generate waveforms (traces) in C++? 及び How do I generate waveforms (traces) in SystemC? のセクションを参照します。

veripool.org

このドキュメントの V4.202版と Ubuntu-20.04 の V4.028 版の違いについて、次項以降で具体的に説明します。尚、現在使用している Verilator のバージョンを確認したい場合は、次の通りコマンドを打ちます。

verilator --version


最上位が C++ の場合のテストベンチの変更:

上述の通り版数の違いにより、
A. Pass the --trace option to Verilator, and in your top level C code, call Verilated::traceEverOn(true). 
により、Verilog HDL 内の $dumpfile$dumpvars が動作するというのは Ubuntu-20.04 の版 V4.028 では使えない様です。そこで、次の説明に従う事にします。
B. Or, for finer-grained control, or C++ files with multiple Verilated modules you may also create the trace purely from C++.
C++ でテストベンチを記述している場合は、次の様にします。
cat から EOF までを ubuntu のターミナル上にコピー&ペーストすると、cframe.cpp というファイルが出来ます。
cat << "EOF" > cframe.cpp
#pragma onece
#include <iostream>
#include "verilated.h"
#include "verilated_fst_c.h"
#include "obj_dir/Vbench.h"

#define PERIOD 1000

int main(int argc, char* argv[]) {

  /* Command Line Arguments */
  // Verilated::commandArgs(argc, argv);

  /* Testbench */
  Vbench* u_bench = new Vbench();

  /* FST Dump */
  Verilated::traceEverOn(true);
  VerilatedFstC* tfp = new VerilatedFstC;
  u_bench->trace(tfp, 3);  // Trace 3 levels of hierarchy
  tfp->open("u_bench.dump");

  /* Clock and DumpTrigger */
  while (!Verilated::gotFinish()) {
    u_bench->clk = 1;
    u_bench->eval();
    u_bench->dumptrig = 0;
    // tfp->dump(u_bench->count * PERIOD - PERIOD/2);
    // u_bench->eval();
    u_bench->clk = 0;
    u_bench->eval();
    u_bench->dumptrig = 1;
    tfp->dump(u_bench->count * PERIOD);
    u_bench->eval();
  }

  /* Closing Operation */
  tfp->close();
  delete tfp;
  u_bench->final();
  // Never comes here.
  delete u_bench;
  return 0;
}
EOF

2021-05-02: delete 追加。tfp->dump の位置修正。count を下位から持ち上げ。
2021-05-03: delete tfp; の位置を有効に変更。


while 文の中で、クロック信号 clk の立ち上がり直前で波形をダンプしています。タイムスタンプにクロックサイクルカウント count の1000倍を使用しています。1クロックサイクルに1回の波形ダンプを観測出来れば良い方にはこれで問題無いのですが、クロック信号の立下り直前の状態も見たい方は、while 文中のコメント2行の // 外してください。



最上位が C++ の場合のコンパイルスクリプト

cat から EOF までを ubuntu のターミナル上にコピー&ペーストすると、veri.sh というファイルが出来ます。
cat << "EOF" > veri.sh
#!/bin/dash -x

if [ -e obj_dir/Vbench ]; then
  /bin/rm obj_dir/Vbench
fi
if [ -e u_bench.dump ]; then
  /bin/rm u_bench.dump
fi

verilator -Wall -Wno-SYNCASYNCNET --trace-fst --cc bench.v dut.v --exe cframe.cpp

if [ $? -eq 0 ]; then
  cd obj_dir

  export CXX='g++'
  export CXXFLAGS='-Og -g -gdwarf-2 -fPIC'
  export LINK='g++'
  export LDFLAGS=''
  make -e -j --output-sync -f Vbench.mk

  if [ $? -eq 0 ]; then
    (cd ..; time ./obj_dir/Vbench)
  fi

  cd ..
fi
EOF

次のコマンドでコンパイルとシミュレーションの実行が出来ます。

chmod +x veri.sh
./veri.sh

シミュレーションのみの再実行、または ROM コード (main.v) のみの変更の場合には、次のコマンドを実行します。

./obj_dir/Vbench


最上位が SystemC の場合のテストベンチの変更:

SystemC の場合にもやはり版数の違いによる問題が有る様で、Ubuntu-20.04 では FST ダンプは行えず、VCD ダンプのみとなります。 C++ の場合との違いは、VerilatedVcdSc インスタンス (末尾が Sc ) を使用すると、明示しなくとも sc_start 間のダンプが自動的に行われる事です。尚、VerilatedVcdC インスタンス (末尾が C ) を使用して明示的にダンプする事も可能です。

SystemC でテストベンチを記述した場合は、次の通り書き換えます。

cat から EOF までを ubuntu のターミナル上にコピー&ペーストすると、scframe.cpp というファイルが出来ます。
cat << "EOF" > scframe.cpp
#pragma onece
#include <iostream>
#include <systemc.h>
#include "verilated_sc.h"
#include "verilated_vcd_sc.h"
#include "obj_dir/Vbench.h"

#define PERIOD 1000

int sc_main(int argc, char* argv[]) {

  /* Command Line Arguments */
  // Verilated::commandArgs(argc, argv);

  /* Signal Definition */
  sc_signal<bool>       clk;
  sc_signal<bool>       dumptrig;
  sc_signal<uint64_t>   count;

  /* Testbench */
  Vbench* u_bench = new Vbench("bench");

  /* Port Connection */
  u_bench->clk(clk);
  u_bench->dumptrig(dumptrig);
  u_bench->count(count);

  /* VCD Dump */
  #ifdef VCD_CXX
    VerilatedVcdC* tfp = new VerilatedVcdC;    
  #else  
    VerilatedVcdSc* tfp = new VerilatedVcdSc;
  #endif
  Verilated::traceEverOn(true);
  u_bench->trace(tfp, 3);  // Trace 3 levels of hierarchy
  tfp->open("u_bench.dump");

  /* Clock and DumpTrigger */
  sc_start();

  while (!Verilated::gotFinish()) {
    clk = 1;
    sc_start(PERIOD/2-1, SC_NS);
    dumptrig = 0;
    #ifdef VCD_CXX
      tfp->dump(u_bench->count * PERIOD - PERIOD/2);
    #endif
    sc_start(1, SC_NS);
    clk = 0;
    sc_start(PERIOD/2-1, SC_NS);
    dumptrig = 1;
    #ifdef VCD_CXX
      tfp->dump(u_bench->count * PERIOD);
    #endif
    sc_start(1, SC_NS);
  }

  /* Closing Operation */
  tfp->close();
  delete tfp;
  u_bench->final();
  // Never comes here.
  delete u_bench;
  return 0;
}
EOF

2021-05-02: delete 追加。tfp->dump を追加。count を下位から持ち上げ。
2021-05-03: "verilated.h" -> "verilated_sc.h"。while の前に sc_start(); 追加。delete tfp; の位置を有効に変更。VcdC から VcdSc に変更。


最上位が SystemC の場合のコンパイルスクリプト

cat から EOF までを ubuntu のターミナル上にコピー&ペーストすると、scveri.sh というファイルが出来ます。
cat << "EOF" > scveri.sh
#!/bin/dash -x

if [ -e obj_dir/Vbench ]; then
  /bin/rm obj_dir/Vbench
fi
if [ -e u_bench.dump ]; then
  /bin/rm u_bench.dump
fi

verilator -Wall -Wno-SYNCASYNCNET --trace --sc bench.v dut.v --exe scframe.cpp

if [ $? -eq 0 ]; then
  cd obj_dir

  export CXX='g++'
  export CXXFLAGS='-Og -g -gdwarf-2 -fPIC'
  export LINK='g++'
  export LDFLAGS=''
  make -e -j --output-sync -f Vbench.mk

  if [ $? -eq 0 ]; then
    (cd ..; time ./obj_dir/Vbench)
  fi

  cd ..
fi
EOF

次のコマンドでコンパイルとシミュレーションの実行が出来ます。

chmod +x scveri.sh
./scveri.sh

シミュレーションのみの再実行、または ROM コード (main.v) のみの変更の場合には、次のコマンドを実行します。

./obj_dir/Vbench


最上位を Verilog HDL で記述する場合のテストベンチ (比較用):

上記と同じ処理を Verilog HDL で記述する場合は次の通りとなります。

尚、Verilator には $dumplimit に対応する機能は有りません。

cat から EOF までを ubuntu のターミナル上にコピー&ペーストすると、frame.v というファイルが出来ます。
cat << "EOF" > frame.v
`default_nettype none
`timescale 1ns/1ps

`define PERIOD 1000

module frame();

  /* Command Line Arguments */
  // parameter argv = 1'bz;

  /* Signal Definition */
  reg   clk;
  reg   dumptrig;

  /* Testbench and Port Connection */
  bench u_bench(
    .clk(clk),
    .dumptrig(dumptrig),
    .count()
  );

  /* VCD or FST Dump */
  initial begin
    $dumpfile("u_bench.dump");
    $dumplimit(10000000); // 10M bytes
    $dumpvars(3, u_bench); // Trace 3 levels of hierarchy
  end

  /* Clock and DumpTrigger */
  always begin
    clk = 1;
    #(`PERIOD/2-1);
    dumptrig = 0;
    #1;
    clk = 0;
    #(`PERIOD/2-1);
    dumptrig = 1;
    #1;
  end

endmodule
EOF

2021-05-02: count を下位から持ち上げ。


最上位が Verilog HDL の場合の Icarus Verilogコンパイル スクリプト

cat から EOF までを ubuntu のターミナル上にコピー&ペーストすると、ivlog.sh というファイルが出来ます。
cat << "EOF" > ivlog.sh
#!/bin/dash -x

if [ -e a.out ]; then
  /bin/rm a.out
fi
if [ -e u_bench.dump ]; then
  /bin/rm u_bench.dump
fi

export IVERILOG_DUMPER=fst

iverilog frame.v bench.v dut.v

if [ $? -eq 0 ]; then
  time vvp ./a.out -fst
fi
EOF

次のコマンドでコンパイルとシミュレーションの実行が出来ます。

chmod +x ivlog.sh
./ivlog.sh

シミュレーションのみの再実行、または ROM コード (main.v) のみの変更の場合には、次のコマンドを実行します。

vvp ./a.out -fst

尚、Icarus Verilog の波形ダンプファイルを常に FST 形式 (バイナリ) で保存したい場合には、.bashrc に次の1行を追加します。これにより、./a.out だけで常に FST 形式 (バイナリ) となります。環境変数を設定しない場合には、デフォルトの波形ファイルは VCD 形式 (アスキー形式) となります。

export IVERILOG_DUMPER=fst


波形の観測:

ダンプした波形は、GTKWave を使用して GUI 表示します。前準備として、X Window サーバを起動しておきます。次のコマンドで GTKWave を起動します。

gtkwave u_bench.dump




【論理回路設計の目次へ戻る】   【WSL 関係の目次へ戻る】