This are my first successful Verilator experiments. I was looking for a counter example, but was not able to find one, so I decided to write one myself and publish it. The instructions are for Ubuntu.
First write a verilog RTL counter. This example has two registers, one running at clock posedge, the other on negedge. I just wished to check if using both clock edges still qualifies as synthesizable RTL.
module counter #(
parameter WIDTH = 8
)(
// system signals
input wire clk,
input wire rst,
// counter signas
input wire cen, // counter enable
input wire wen, // write enable
input wire [WIDTH-1:0] dat, // input data
output reg [WIDTH-1:0] o_p, // output value (posedge counter)
output reg [WIDTH-1:0] o_n // output value (negedge counter)
);
always @ (posedge clk, posedge rst)
if (rst) o_p <= {WIDTH{1'b0}};
else o_p <= wen ? dat : o_p + {{WIDTH-1{1'b0}}, cen};
always @ (negedge clk, posedge rst)
if (rst) o_n <= {WIDTH{1'b0}};
else o_n <= wen ? dat : o_n + {{WIDTH-1{1'b0}}, cen};
endmodule
Than write a C++ testbench. C++ is used for the bench since non synthesizable Verilog features would be needed to write a proper Verilog bench. Anoter option would be to use SystemC, but due to licensing issues it is very difficult to get a package for Linux distributions. The bench will toggle the clock and provide input values.
#include "Vcounter.h"
#include "verilated.h"
#include "verilated_vcd_c.h"
int main(int argc, char **argv, char **env) {
int i;
int clk;
Verilated::commandArgs(argc, argv);
// init top verilog instance
Vcounter* top = new Vcounter;
// init trace dump
Verilated::traceEverOn(true);
VerilatedVcdC* tfp = new VerilatedVcdC;
top->trace (tfp, 99);
tfp->open ("counter.vcd");
// initialize simulation inputs
top->clk = 1;
top->rst = 1;
top->cen = 0;
top->wen = 0;
top->dat = 0x55;
// run simulation for 100 clock periods
for (i=0; i<20; i++) {
top->rst = (i < 2);
// dump variables into VCD file and toggle clock
for (clk=0; clk<2; clk++) {
tfp->dump (2*i+clk);
top->clk = !top->clk;
top->eval ();
}
top->cen = (i > 5);
top->wen = (i == 10);
if (Verilated::gotFinish()) exit(0);
}
tfp->close();
exit(0);
}
Now run the next script or copy-paste line by line into the command line.
#!/bin/sh
# cleanup
rm -rf obj_dir
rm -f counter.vcd
# run Verilator to translate Verilog into C++, include C++ testbench
verilator -Wall --cc --trace counter.v --exe counter_tb.cpp
# build C++ project
make -j -C obj_dir/ -f Vcounter.mk Vcounter
# run executable simulation
obj_dir/Vcounter
# view waveforms
gtkwave counter.vcd counter.sav &
The script will automatically launch GTKWave and open the waveform.