20 February, 2011

Experimenting with Verilator (counter)

Verilator is a open source Verilog HDL simulator. It is very fast since it translates Verilog code into optimized C++. But it as also quiet different from other Verilog simulators like Icarus Verilog and commercial ones, it only supports synthesizable RTL language constructs.

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.







4 comments:

ali said...

Hi,

I am trying to run this, and I am getting the following error:

counter.v:2: syntax error, unexpected $end, expecting IDENTIFIER or parameter

Any ideas. I am using latest verilator from head:

3.811

Thanks!

Andrewli2003 said...

Great work. Thanks!
I works for me. I use cygwin.

Manoel Gomes de Andrade said...

I wonder if there is a way to construct a C++ testebench, in a graphical front end (WxWidgets, Tk, etc.), to interact with verilog model by buttons, dialog box, message box, and so on. In other words build a graphical simulator for an specific architecture, starting with your "counter.v". Think of this.
Good contribution !

Unknown said...

Thank you so much! Very easy.