So … you noticed that the final exercise failed, didn’t you ?
This is due to the fact that we are simulating hardware in software. More precisely, we’re simulating hardware that runs everything in parallel. The simulation is done in software, which typically is sequential.
Now how to fix this ?
To achieve support for parallel pieces of software, threads can be used. In Verilog2001 the fork…join construct was added. SystemVerilog adds two additional variants: fork…join_any and fork…join_none. How this multi-threaded approach works, can be explained easiest with a drawing.
Let’s assume there are four “tasks” in software: a, b, c, and d. Note that this can also be a function or method call, or a simple one-line statement. Simply writing a; b; c; d;
will start task a. When this is done, task b will be started, and so on.
To start every task in its own dedicated thread, they should be wrapped in a fork-statement. The three available options for a fork-statement are:
Adding disable fork and wait fork to the mix, gives you a lot of control.
The disable fork statement stops all active threads that were spawned from the current thread. The problem is that this may accidentally stop threads that you did not intended to.
With all that tackled, the environment can be fixed.
When the run() method is called, 2 threads need to be started in parallel: 1 for the driver, and 1 for the monitor. This allows to run both instances in parallel.
The fork is closed with a join_any.
After giving 10 clock cycles of spin-up, the downstream threads are started in a join_any. If you remember, the Monitor has a forever loop, so the this.mon.run() will never end. This implies that the fork is ended when the driver ends.
To illustrate this, the Transcript windows of both simulations (with and without fork) are shown below.
class environment;
virtual gbprocessor_iface ifc;
driver drv;
monitor mon;
function new(virtual gbprocessor_iface ifc);
this.drv = new(ifc);
this.mon = new(ifc);
endfunction : new
task run();
fork
this.drv.run_addition();
this.mon.run();
join_any;
$display("[ENV]: end of run()");
endtask : run
endclass : environment
Take a close look at the timestamp of the message that stated the Monitor will start working.