Building a UVM Testbench
A walkthrough of the pieces and how to keep growing
You will not write a full UVM environment in one sitting, and that is fine. The goal here is to see how the blocks from the last lesson turn into code, so that when you study a real environment it reads as familiar, not foreign.
A transaction - the unit of stimulus
class my_item extends uvm_sequence_item;
rand bit [7:0] addr;
rand bit [7:0] data;
`uvm_object_utils(my_item) // register with the factory
function new(string name = "my_item");
super.new(name);
endfunction
endclassIt is the same kind of transaction you built in lessons 4 and 5 (the Packet class with rand fields), now extending a UVM base class so the framework can build, copy, and randomize it for you.
A driver - turns items into pin activity
class my_driver extends uvm_driver #(my_item);
`uvm_component_utils(my_driver)
task run_phase(uvm_phase phase);
forever begin
seq_item_port.get_next_item(req); // ask for the next item
// drive req.addr / req.data onto the DUT pins here
seq_item_port.item_done(); // tell the sequencer we are done
end
endtask
endclassThe driver loops forever: get an item, drive it onto the DUT, signal done, repeat. The sequencer keeps it fed. (req is a built-in handle that every uvm_driver provides, already typed as my_item here - that is where it comes from, you do not declare it yourself.)
The phases - UVM runs in a fixed order
Every UVM component goes through the same phases, so building and connecting always happen in a predictable order.
- build_phase - create the components (top builds env, env builds agents).
- connect_phase - wire components together (driver to sequencer, monitor to scoreboard).
- run_phase - the actual simulation runs here; stimulus flows and checks fire.
- report_phase - print the final pass or fail summary.
The fastest way to learn UVM is to take one small, complete, working example (an adder or a simple FIFO environment) and trace one transaction end to end: sequence -> sequencer -> driver -> DUT -> monitor -> scoreboard. Once you can narrate that single journey, the whole methodology clicks. Build the example up, do not study it top-down.
A realistic learning order
- Solidify classes and randomization (lessons 4 and 5).
- Read and run a minimal UVM testbench for a tiny DUT.
- Trace one transaction through every component.
- Add a coverage model and an assertion to it.
- Only then take on a larger, multi-agent environment.
UVM is a means, not the goal. The goal is still: did we exercise the design thoroughly and prove it matches the spec? A beautiful UVM environment with a weak cover plan and no real checks verifies nothing. Keep coming back to stimulus, checking, and coverage - the methodology only exists to organize those three.