Previous journal: | Next journal: |
---|---|
0050-2020-08-03.md | 0052-2020-08-05.md |
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 thatq
will become set to something other than 255, but that hasn't yet taken effect so the firstif
detectsq==255
and displays its value. - Once the
always
block ends, the assignment toq
takes effect, but sincereset
is also asserted (given it uses positive logic, is unassigned, and our default state is1
) then our counter gets reset. - For each subsequent iteration of the test bench,
reset
remains unassigned (hence1
), thus thealways
block starts endlessly displayingCounted 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 withq
all 0, which matches the firstif
condition during thereset()
call, hence displaysCounted 0
. It then starts thetick()
loop which again displaysCounted 0
, counts up to displayCounted 100
, then hits$finish
atq==110
. - Example
1
starts withq
all 1, doing the same as above but for the255
condition. - Example
2
starts withq
in a random state, but obviously not 0, 100, 110, or 255, so the very firstreset()
call doesn't lead to anything being displayed. It then starts the loop withCounted 0
, and stops atCounted 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.
- 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.