I run onto a rather confusing situation where I was writing a code for a UVM monitor. This module performs these operations in order:
- Listen to channel (virtual interface) until intercepting one or many events on the pin-level.
- Fill in a data structure with any values that can be read on the input ports of the interface.
- Spawn a child thread to wait for the response and go back to step 1.
Spawning is using the SystemVerilog Fork.. JOIN_NONE construct and it was meant to start a task that only waits for the response at the interface and then keep going with the parent task - WITHOUT having to wait for the spawned task to finish. Inside the FORK JOIN, there is a SystemVerilog Task with input arguments that are passed by value. When the program starts, index=0. When the get_output(data_struct, index) is executed inside the FORK..JOIN, index is still 0. However it changes once inside the spawned task! Please look carefully to where I put my debug prints of the variable index. The first one prints index = 0 and the second one prints index = 1. It is not possible in my opinion that this can happen, because first, variables are passed by value to the task and second index increments after the task is called assuming instructions take place in a sequential order inside the task run_phase. I want to know why does index change inside the spawned task by itself. The tweak I found to maintain the passed value is to add a wait statement of 1ns after the spawned task has been called.
Here is the code:
task run_phase(uvm_phase phase);
int unsigned index;
dpu_input_str data_struct;
//monitoring
index = 0;
//wait until NOT reset
@(posedge vif.rst_n);
forever begin
//listen to channel: wait for any change in the IF signals
fork
@(vif.ctrl_0);
@(vif.ctrl_1);
...
join_any
disable fork; //kill outstanding waiting processes
//create intermediate struct
data_struct.ctrl_0 = vif.ctrl_0;
data_struct.ctrl_1 = vif.ctrl_1;
..
//wait for the output
//spawn a task to perform that and get back to channel listening
//again
fork
get_output(data_struct, index);
join_none
$display("Value of index after calling the task is: %d", index);
//#1;
index++;
end
endtask: run_phase
task get_output(input dpu_input_str data_struct,
input int unsigned index);
dpu_simple_transaction dpu_tr;
int unsigned delay_cnt;
int i;
//create analysis transaction
dpu_tr = dpu_simple_transaction::type_id::create
(.name("dpu_tr_dut_mon"),
.contxt(get_full_name));
$display("Value of index inside the task is: %d", index);
// fill in data back into analysis transaction
// pay attention to cast when writing numeric values to enum datatypes
dpu_tr.ctrl_0 = dpu_control_type'(data_struct.ctrl_0);
dpu_tr.ctrl_1 = dpu_control_type'(data_struct.ctrl_1);
...
// decide about the delay
...
endtask