A while back I wrote anarticle about how to handle the reset in an UVM agent component. Someone asked a very good question: How about handling the reset in the scoreboard? In this article I will share with you my way of handling the reset in a verification environment.
DUT with an APB interface for accessing some registers
Simple overview of the verification environment architecture
Step #1: Handle the reset in the model
class cfs_dut_model extends uvm_component;
...
//function for handling reset inside the model
virtual function void handle_reset(string kind = "HARD");
//kill and restart any ongoing threads
reg_block.reset(kind);
//clear here any other elements part of the model class
endfunction
...
endclass
Step #2: Handle the reset in the scoreboard
class cfs_dut_scoreboard extends uvm_component;
...
//function for handling reset inside the scoreboard
virtual function void handle_reset(string kind = "HARD");
//kill and restart any ongoing threads
model.reset(kind);
//clear here any other elements part of the scoreboard class
endfunction
...
endclass
Step #3: Handle the reset in the coverage
class cfs_dut_coverage extends uvm_component;
...
//function for handling reset inside the coverage
virtual function void handle_reset(string kind = "HARD");
//Sample the state of the DUT at reset
//Clear here any other elements part of the coverage class
endfunction
...
endclass
Step #4: Handle the reset in the environment
class cfs_dut_env extends uvm_component;
...
//Function for handling reset inside the environment
virtual function void handle_reset(uvm_phase phase, string kind = "HARD");
coverage.handle_reset(kind);
scoreboard.handle_reset(kind);
//Clear here any other elements part of the environment class
endfunction
...
endclass
class cfs_dut_env extends uvm_component;
...
virtual task run_phase(uvm_phase phase);
forever begin
wait_reset_start();
handle_reset(phase, "HARD");
wait_reset_end();
end
endtask
...
endclass
In most of the cases this logic will work quite fine but there is a corner case where this logic might break down.
Relevant threads in the verification environment
thread #1 – APB monitor agent: task for collecting APB transactions. This thread usually affects the register block by updating and checking the registers values.
thread #2 – APB agent: task for detecting reset. This thread will affect only the internal logic of the APB agent and one of the actions which will do is to stop thread #1 and restart it. For details see Step 1 from part 1 of this article.
thread #3 – Environment: task for detecting reset. This thread will affect the model of the DUT (e.g. reset of the register block), reset the coverage class and so on.
If the reset is asynchronous and never becomes active in the same time with the positive edge of the clock everything is fine but we can not guarantee this.
The problem is when all threads want to take some action exactly in the same simulation time:
reset becomes active on the rising edge of the clock
on APB there is an register access
All threads want to take some action at the same time
Because these are parallel threads their execution order is more or less random from the verification engineer point of view.
So if for example the threads are executed in the following order #3 -> #1 -> #2 then: Model will be cleared by thread #3 so this means that the register block will be reset Data will be written in some internal register like CTRL by thread #1 as it collects the APB accessThread #2 will reset the APB agent and restart thread #1 but it will be too late as false data already ended up in our CTRL register model There is an easy fix for this:we get rid of the thread for handling reset in the agent (#2) and we handle agent reset from thread #3. Handling reset from one thread
First, we need to stop the agent from handling the reset on its own:
class cfs_dut_env extends uvm_component;
...
virtual function void end_of_elaboration_phase(uvm_phase phase);
apb_agent.agent_config.set_should_handle_reset(0);
//do the same for any other agent part of the environment
endtask
...
endclass
class cfs_dut_env extends uvm_component;
...
//function for handling reset inside the environment
virtual function void handle_reset(uvm_phase phase, string kind = "HARD");
apb_agent.handle_reset(phase, kind);
//do the same for any other agent part of the environment
coverage.handle_reset(kind);
scoreboard.handle_reset(kind);
//clear here any other elements part of the environment class
endfunction
...
endclass
In this way the verification environment will always be reset correctly because:
if first it happens thread #3 and then thread #1 – thread #3 will restart thread #1 so no APB transaction will reach the model
if first it happens thread #1 and then thread #3 – data will be pushed in the register block model by thread #1 but thread #3 will immediately clear it.
The same logic can be applied for any number of agents part of the verification environment.
The basic idea is this: handle the reset for the entire environment from one single thread so you can control the order in which the components are reset.
Hope this helps.
If you drive reset based on the clock's posedge, doesn't this mean that the change on reset will be scheduledafter all processes that are sensitive to the clock, meaning that the scoreboard gets reset after all of the otherthreads have done their stuff?
You are right, although I think this will work only for non blocking assignment of the reset signal.But usually I am driving the reset asynchronous in which case you might get into such scenarios.
If the explicit prediction used for RAL, then should I have to use set_check_on_read(o) or use onlyset_check_on_read(1)?I can declared as the below, but I get the mismatched error between DUT and mirrored value after read().
example_register.default_map.set_auto_predict(1'b0);
example_register.default_map.set_check_on_read(1);
virtual task body();
uvm_status_e status;
my_register.write(status, 32'hFFFF_FFFF);
my_register.read(status, rdata); //->>> make the Error(mismatched).