Even if it sounds like something quite simple, handling reset in an UVM agent is not that trivial when it comes to actual implementation.
In this post I will present a generic mechanism for handling reset which can be reused in any UVM agent.
Let’s consider that we have an UVM agent with the following architecture:
Step #1: Handling Reset in the Agent Component
Because the agent is the top component we can implement in it some logic which detects when the reset becomes active and then informs all its children that they should reset their logic.
class cfs_agent extends uvm_agent; ... virtual task run_phase(uvm_phase phase); forever begin wait_reset_start(); if(agent_config.get_should_handle_reset() == 1) begin handle_reset(phase, "HARD"); end wait_reset_end(); end endtask ... endclass
Informing all its children that they should reset their logic is pretty straight forward:
class cfs_agent extends uvm_agent; ... virtual function void handle_reset(uvm_phase phase, string kind = "HARD"); monitor.handle_reset(kind); if(driver != null) begin driver.handle_reset(kind); end if(sequencer != null) begin sequencer.handle_reset(phase, kind); end if(coverage != null) begin coverage.handle_reset(kind); end //add here any other component which might need to be reset endfunction ... endclass
In the agent configuration class we declare our switch for controlling reset handling:
class cfs_agent_config extends uvm_component; ... protected bit should_handle_reset; function new(string name = ""); super.new(name); should_handle_reset = 1; endfunction virtual function bit get_should_handle_reset(); return should_handle_reset(); endfunction virtual function void set_should_handle_reset(bit should_handle_reset); this.should_handle_reset = should_handle_reset; endfunction endclass
Step #2: Handling Reset In The Monitor Component
At its most basics, the monitor logic is a continuous loop which watches over some physical bus and collects all transfers.
Based on this we can say that the reset logic should stop this loop (regardless of its state when reset comes), clear any temporary information and restart the monitoring loop once the reset is finished.
Of course, this must be done regardless of the physical protocol handled by the agent.
class cfs_monitor extends uvm_monitor; ... //process for collect_transactions() task protected process process_collect_transactions; //task for collecting all transactions virtual task collect_transactions(); fork begin process_collect_transactions = process::self(); forever begin collect_transaction(); end end join endtask virtual task run_phase(uvm_phase phase); forever begin fork begin wait_reset_end(); collect_transactions(); disable fork; end join end endtask //function for handling reset virtual function void handle_reset(string kind = "HARD"); if(process_collect_transactions != null) begin process_collect_transactions.kill(); end //clear here any temporary information from the monitor endfunction ... endclass
Step #3: Handling Reset In The Driver Component
The same logic that we implemented in the monitor can be adapted for the driver. In the end the driver’s logic is just a loop waiting for items from the sequencer and put them on a physical bus.
class cfs_driver extends uvm_driver; ... //process for drive_transactions() task protected process process_drive_transactions; //task for driving all transactions virtual task drive_transactions(); fork begin process_drive_transactions = process::self(); forever begin cfs_item_drv_master transaction; seq_item_port.get_next_item(transaction); drive_transaction(transaction); seq_item_port.item_done(); end end join endtask task run_phase(uvm_phase phase); forever begin fork begin wait_reset_end(); drive_transactions(); disable fork; end join end endtask //function for handling reset virtual function void handle_reset(string kind = "HARD"); if(process_drive_transactions != null) begin process_drive_transactions.kill(); end //clear here any temporary information, initialize some interface signals etc endfunction ... endclass
Step #4: Handling Reset In The Sequencer Component
Resetting the sequencer is pretty simple. We just have to make sure that any pending items are thrown away nicely so that the sequencer can handle new items after reset:
class cfs_sequencer extends uvm_sequencer; ... virtual function void handle_reset(uvm_phase phase, string kind = "HARD"); uvm_objection objection = phase.get_objection(); int objections_count; stop_sequences(); objections_count = objection.get_objection_count(this); if(objections_count > 0) begin objection.drop_objection(this, $sformatf("Dropping %0d objections at reset", objections_count), objections_count); end start_phase_sequence(phase); endfunction ... endclass
Step #5: Handling Reset In Coverage Component
Resetting the coverage component depends more on the physical protocol as there is no “standard” logic that it must do as in the components described above.
So you can use the handle_reset() function to do some coverage sampling during reset and clear any temporary information:
class cfs_coverage extends uvm_component; ... virtual function void handle_reset(string kind = "HARD"); //sample coverage groups related to reset //clear some temporary information endfunction ... endclass
And that’s it!
This technique is quite generic so it can be used for any agent.
If you want to read more about how this technique can be integrated in a generic agent Hamilton Carter did two articles about such a generic agent I wrote back in 2015:
- The Ecstasy and the Agony of UVM Abstraction and Encapsulation Featuring the AMIQ APB VIP: Part I
- Part II: The Ecstasy and the Agony of UVM Abstraction and Encapsulation Featuring the AMIQ APB VIP
Hope this helps π
Later edit: you can read about how to handle the reset in the verification environment in part 2 of this article: SystemVerilog: How To Handle Reset In UVM (part 2)

Nice Article
thanks
HI,
It’s good article
thanks
Hi, Cristian.
How are you doing?
Keep up the good work with posting technical rich articles.
I have a dilemma about the “disable fork” construct from inside run_phase of each component. Why do you need that?
Cheers,
Aurelian
Hi Aurelian,
Nice to hear from you!
That “disable fork” comes in handy when somebody else extends the component and starts some other fork inside wait_reset_end() or collect_transactions() or …
I want to terminate as much processes as I can at reset!
Cristi
Good article. I used to use the very same technique for handling reset in my testbenches.
Great Article.
Thanks
You don’t talk about how the reset in the middle could be handled by the scoreboard. Isn’t that necessary? Any inputs on that?
That is a very good point.
Essentially I am doing something similar in my scoreboards: I have a handle_reset() function in which I kill all scoreboard processes and initialize the class fields. I just need to save a process handler for every process of the scoreboard.
I will try to update the article with few examples for this case!
Yes, that’ll be really helpful.
Hi,
I just published an article about how to handle the reset in the environment:
http://cfs-vision.com/2017/06/14/systemverilog-how-to-handle-reset-in-uvm-part-2/
Thanks for the idea! π
Hi ,
As per your example ,
What happens when reset triggered in middle of ” collect_transactions();” in driver.
As you are abondoning the item_done there by sequencer and driver communicaiton will locked up ,is in’t
Ignore previous post.
What happens when reset triggered in middle of β drive_transaction(transaction);β in driver.
As you are abandoning the item_done there by causing the sequencer and driver communication will lock up ,is inβt
Hi,
This locking is prevented by also resetting the sequencer – see Step #4: Handling Reset In The Sequencer Component
I’m really curious on how would you implement the method: “wait_reset_start()”
I think I shouldn’t look into the interface object, so how would you do it?
Thanks.