Skip to content

Latest commit

 

History

History
106 lines (79 loc) · 5.54 KB

0051-2020-08-04.md

File metadata and controls

106 lines (79 loc) · 5.54 KB

4 Aug 2020

Previous journal: Next journal:
0050-2020-08-03.md 0052-2020-08-05.md

More Verilator

Loosely following this tutorial, I did the following, in [t01b]...

I created counter.v which includes a $display statement and $finish condition. Probably not necessary to do it this way (i.e. testing-related stuff should probably not be in the module under test), but I'm just learning for now and this will do. Later we'll try printf statements in the C++ code.

I made a TESTBENCH template header file by copying the example. I fixed it to declare everything public and updated it to use my signal names of clk and reset.

I created sim_main.cpp which is just a simple endless clocking testbench.

Compiled it and ran it all with:

verilator -Wall --cc counter.v --exe --build sim_main.cpp && obj_dir/Vcounter

Running it works as expected. Note that it definitely seems to start with all register values at 0 (hence count starting at 0, and not getting up to 255):

Counted   0
Counted 100
- counter.v:10: Verilog $finish

Actually, with respect to initial values, I learned that the verilator command has a default --x-initial unique switch which defines that it should determine the value of otherwise unset registers/inputs using a "unique" function (i.e. dynamically-generated). That function in turn is controlled by a command-line switch to the actual executable test bench (obj_dir/Vcounter in this case) called +verilator+rand+reset+value.

It looks like this reset value:

  • Defaults to 0;
  • Can be set to 1 to set all bits;
  • Can be set to 2 to randomise all bits.

For example, this command will start with all unassigned values being set to 0:

obj_dir/Vcounter

...while this will start with them all set to 1:

obj_dir/Vcounter +verilator+rand+reset+1

This causes the testbench to run endlessly. It outputs:

Counted 255
Counted   0
Counted   0
Counted   0
...

This makes sense because:

  • The initial value of everything is set to 1, so the counter's initial value is 8'b11111111 (i.e. 255).
  • On the positive edge of clk, we know that q will become set to something other than 255, but that hasn't yet taken effect so the first if detects q==255 and displays its value.
  • Once the always block ends, the assignment to q takes effect, but since reset is also asserted (given it uses positive logic, is unassigned, and our default state is 1) then our counter gets reset.
  • For each subsequent iteration of the test bench, reset remains unassigned (hence 1), thus the always block starts endlessly displaying Counted 0.

I changed sim_main.cpp to add an initial reset (which includes an internal tick call inside the reset cycle to ensure it is synchronously clocked, properly). After rebuilding with verilator, I can now do this:

$ obj_dir/Vcounter +verilator+rand+reset+0
Counted   0
Counted   0
Counted 100
- counter.v:10: Verilog $finish

$ obj_dir/Vcounter +verilator+rand+reset+1
Counted 255
Counted   0
Counted 100
- counter.v:10: Verilog $finish

$ obj_dir/Vcounter +verilator+rand+reset+2
Counted   0
Counted 100
- counter.v:10: Verilog $finish

To explain:

  • Example 0 starts with q all 0, which matches the first if condition during the reset() call, hence displays Counted 0. It then starts the tick() loop which again displays Counted 0, counts up to display Counted 100, then hits $finish at q==110.
  • Example 1 starts with q all 1, doing the same as above but for the 255 condition.
  • Example 2 starts with q in a random state, but obviously not 0, 100, 110, or 255, so the very first reset() call doesn't lead to anything being displayed. It then starts the loop with Counted 0, and stops at Counted 100.

We could probably put a check for reset==1 in the first if condition to always display what was loaded into q at the start.

NOTE: I believe there are command-line switches for setting the random seed value. Without this, it possibly picks the same random value(s) each time it is run.

Other things learned

  • An example of a similar TESTBENCH template, which includes capturing signal traces as VCD: https://github.com/ZipCPU/zipcpu/blob/master/sim/verilator/testb.h
  • Delays (#nnn) are ignored:
    %Warning-STMTDLY: counter.v:12:4: Unsupported: Ignoring delay on this delayed statement.
                                    : ... In instance counter
       12 |   #100;
          |    ^~~
                      ... Use "/* verilator lint_off STMTDLY */" and lint_on around source to disable this message.
    
  • $monitor in Verilog is a nifty way to report any time a parameter changes value, during simulation.