Waveforms

Write VCD or FST waveforms from a simulation and open them in a viewer.

There are two ways to get a waveform file:

Example design

Save the counter as counter.sv:

`timescale 1ns/1ps module counter ( input logic clk, input logic rst, input logic en, output logic [3:0] count ); always_ff @(posedge clk) if (rst) count <= '0; else if (en) count <= count + 1'b1; endmodule

and its testbench as tb.sv:

`timescale 1ns/1ps module tb; logic clk = 0, rst = 1, en = 0; logic [3:0] count; counter dut (.clk, .rst, .en, .count); always #5 clk = ~clk; initial begin @(negedge clk) rst = 0; en = 1; repeat (3) @(negedge clk); $display("count=%0d", count); $finish; end endmodule

VCD

Compile with --trace-vcd and run the model:

ryusim compile --top tb --trace-vcd tb.sv counter.sv
obj_dir/build/tb_sim

The run writes trace.vcd in the current directory. Its header lists every scope and signal:

sed -n '/^\$timescale/,/^\$enddefinitions/p' trace.vcd
$timescale 1ps $end $scope module tb $end $var wire 1 ! clk $end $var wire 4 " count $end $scope module dut $end $var wire 1 # clk $end $var wire 4 $ count $end $var wire 1 % en $end $var wire 1 & rst $end $upscope $end $var wire 1 ' en $end $var wire 1 ( rst $end $upscope $end $enddefinitions $end

Times in the file are in the design's global time precision, here 1ps, so the first clock edge at 5ns is #5000.

FST

FST is a compressed binary format that GTKWave and Surfer read. Compile with --trace-fst. This example also sets the file name:

ryusim compile --top tb --trace-fst --trace-fst-file counter.fst tb.sv counter.sv obj_dir/build/tb_sim ls counter.fst

Give both --trace-vcd and --trace-fst to write both files from one run.

Trace options

The options are compiled into the model. Change them and compile again.

OptionEffect
--trace-vcdWrite a VCD file.
--trace-vcd-file <path>VCD file name. Default trace.vcd, relative to the directory the simulation runs in.
--trace-fstWrite an FST file.
--trace-fst-file <path>FST file name. Default trace.fst.
--trace-depth <n>Record only the top n levels of hierarchy. 1 keeps the top module's signals and drops dut. 0 (the default) records every level.
--trace-max-width <n>Leave out signals wider than n bits. 0 (the default) keeps all.

ryusim compile --help gives the default file names as <top_module>.vcd and <top_module>.fst. The files are written as trace.vcd and trace.fst.

$dumpfile and $dumpvars

The testbench can choose what to dump and when. This version of the testbench dumps the whole tb hierarchy to counter.vcd. Save it as tb_dump.sv:

`timescale 1ns/1ps module tb; logic clk = 0, rst = 1, en = 0; logic [3:0] count; counter dut (.clk, .rst, .en, .count); always #5 clk = ~clk; initial begin $dumpfile("counter.vcd"); $dumpvars(0, tb); @(negedge clk) rst = 0; en = 1; repeat (3) @(negedge clk); $finish; end endmodule

Compile it without any trace option and run it:

ryusim compile --top tb -o obj_dump tb_dump.sv counter.sv obj_dump/build/tb_sim grep '^\$scope' counter.vcd
$scope module tb $end $scope module dut $end

The first argument of $dumpvars is the depth: $dumpvars(1, tb) dumps the signals of tb and none of dut. $dumpoff writes every signal as x and pauses the dump; $dumpon writes the current values and resumes. Without $dumpfile the file is named dump.vcd. $dumpfile always writes VCD, whatever the file extension.

With cocotb

WAVES=1 adds --trace-vcd. The cocotb guide covers it for each flow, including where trace.vcd lands. For FST, add COMPILE_ARGS += --trace-fst to the Makefile, or add it to the Runner's build_args.

Viewing

Open the file in GTKWave or Surfer. Both read VCD and FST. On Ubuntu and Debian, GTKWave is the gtkwave package:

sudo apt install gtkwave
gtkwave trace.vcd

These two blocks are not run in CI: GTKWave needs a display.