Previous journal: | Next journal: |
---|---|
0052-2020-08-05.md | 0054-2020-08-07.md |
I made t01d
:
- Uses a
Makefile
to build/run the thing now. Just a simple example so far.make clean
will delete any existingobj_dir
.make
is the same asmake all
which doesclean
, then builds the project.make run
will just do the general (initial 0) run, building if needed.make run1
andmake run2
will run, respectively, with: (1) all unassigned bits set; (2) unassigned bits randomised. .gitignore
excludesobj_dir
now, since it is platform-specific, and all of its contents (for now) can just be rebuilt by Verilator.- Actual project is a sawtooth counter (i.e. given a clock, it counts up and then down again). It supports a
SIZE
parameter (defaults to 8-bit output), but I'm not yet sure how to override this in Verilator. - Test bench inspects the high bit of
saw_count__DOT__counter
to look inside the module and see whether it's currently counting up or down.
This example:
- Shows the state of all signals as it runs.
- Runs an initial 5 clock cycles from cold start (which might include unassigned/random signal states).
- Does a synchronous reset.
- Runs for 600 clock cycles.
Run make
initially, which will delete obj_dir
and then build it again. Then we can do:
$ make run | head -15
Before first eval()...
[00000000] clk=0 reset=0 q=0 (counter)=00000000 (up)
After first eval()...
[00000000] clk=0 reset=0 q=0 (counter)=00000000 (up)
Cold start...
[00000001] clk=0 reset=0 q=1 (counter)=00000001 (up)
[00000002] clk=0 reset=0 q=2 (counter)=00000002 (up)
[00000003] clk=0 reset=0 q=3 (counter)=00000003 (up)
[00000004] clk=0 reset=0 q=4 (counter)=00000004 (up)
[00000005] clk=0 reset=0 q=5 (counter)=00000005 (up)
Synchronous reset...
[00000006] clk=0 reset=1 q=0 (counter)=00000000 (up)
Main loop...
[00000007] clk=0 reset=0 q=1 (counter)=00000001 (up)
[00000008] clk=0 reset=0 q=2 (counter)=00000002 (up)
This starts all unassigned signals as cleared.
Likewise, start with all unassigned signals set:
$ make run1 | head -15
Before first eval()...
[00000000] clk=1 reset=1 q=255 (counter)=000001FF (down)
After first eval()...
[00000000] clk=1 reset=1 q=0 (counter)=000001FF (down)
Cold start...
[00000001] clk=0 reset=1 q=0 (counter)=00000000 (up)
[00000002] clk=0 reset=1 q=0 (counter)=00000000 (up)
[00000003] clk=0 reset=1 q=0 (counter)=00000000 (up)
[00000004] clk=0 reset=1 q=0 (counter)=00000000 (up)
[00000005] clk=0 reset=1 q=0 (counter)=00000000 (up)
Synchronous reset...
[00000006] clk=0 reset=1 q=0 (counter)=00000000 (up)
Main loop...
[00000007] clk=0 reset=0 q=1 (counter)=00000001 (up)
[00000008] clk=0 reset=0 q=2 (counter)=00000002 (up)
Then this is how it looks with random values. Note how q
needs the first eval()
in order to "settle", even though it is an assign
ed (rather than reg
istered) value:
$ make run2 SEED=991 | head -15
Before first eval()...
[00000000] clk=0 reset=0 q=121 (counter)=000001B1 (down)
After first eval()...
[00000000] clk=0 reset=0 q=78 (counter)=000001B1 (down)
Cold start...
[00000001] clk=0 reset=0 q=77 (counter)=000001B2 (down)
[00000002] clk=0 reset=0 q=76 (counter)=000001B3 (down)
[00000003] clk=0 reset=0 q=75 (counter)=000001B4 (down)
[00000004] clk=0 reset=0 q=74 (counter)=000001B5 (down)
[00000005] clk=0 reset=0 q=73 (counter)=000001B6 (down)
Synchronous reset...
[00000006] clk=0 reset=1 q=0 (counter)=00000000 (up)
Main loop...
[00000007] clk=0 reset=0 q=1 (counter)=00000001 (up)
[00000008] clk=0 reset=0 q=2 (counter)=00000002 (up)
When doing something like this:
reg [SIZE:0] counter /* verilator public */;
...noting placement of the semicolon relative to the comment... it exposes counter
as a variable you can directly access in the C++ code. Just note that whichever .h
file defines it, that header must be included in your main .cpp
file, so you can do this, for example:
printf("counter=%08X\n", m_core->saw_count->counter);
(For more info on the headers and how this all works, see this but note that I think where it mentions ->v
, this only applies for SystemC stuff, which we're not using).
In my example I made this function, to get the status of the internal up/down bit:
`ifdef verilator
function get_up_down;
// verilator public
get_up_down = counter[SIZE];
endfunction // get_up_down
`endif // verilator
Note that the placement of the // verilator public
comment is vital. If you want, a function like this can also be declared as a bit range such as function [4:0] whatever;
. In any case, I can easily call the function above in my C++ code like this:
printf("direction=%s\n", (m_core->saw_count->get_up_down()) ? "down" : "up");
Examples of Verilator functions in Verilog code can be found here.
Verilog define
s can be set on the verilator
command-line using +define+myvar=somevalue
. Note that the =somevalue
part is optional.
I modified my makefile so it can be used like this:
make DEF='myvar=somevalue whatever'
...which in this case is equivalent to including the following in the Verilog code:
`define myvar somevalue
`define whatever
- How to override Verilog
parameter
s in Verilator? - Need to try out GTKWave for Windows and macOS. At the time of writing, win64 binary is only up to 3.3.100, and macOS only up to 3.3.103, and can also be installed via
brew install gtkwave
? Check out the list of all releases here. - VCD file format seems pretty straightforward.
- FST (Fast Signal Trace) is an alternative to VCD, used by GTKWave. Maybe it has more features? It definitely has compression.
- CVC is an alternative to Verilator and Icarus?
- Comments on Accessing Signals in Verilator Models (see also; its subheadings 6.2.x).
- Learn Makefiles and produce one for use in Verilator, XISE projects, etc. This looks like a great tutorial but I recall a similar site that might have been even better.