Advanced UVM Patterns
Production-grade patterns — layered sequences, synchronization, response handlers, and test organization.
Advanced UVM Patterns
This chapter covers production-grade patterns used in real-world semiconductor verification environments.
1. Layered Sequences
Build complex scenarios by composing simple, reusable sequences:
systemverilog// Layer 1: Atomic operation class single_write extends uvm_sequence #(apb_txn); `uvm_object_utils(single_write) rand bit [31:0] addr; rand bit [31:0] data; function new(string name = "single_write"); super.new(name); endfunction task body(); apb_txn txn = apb_txn::type_id::create("txn"); start_item(txn); txn.randomize() with { write == 1; txn.addr == local::addr; txn.data == local::data; }; finish_item(txn); endtask endclass // Layer 2: Scenario (initialize all registers) class init_sequence extends uvm_sequence #(apb_txn); `uvm_object_utils(init_sequence) function new(string name = "init_sequence"); super.new(name); endfunction task body(); single_write wr; for (int i = 0; i < 8; i++) begin wr = single_write::type_id::create($sformatf("wr_%0d", i)); wr.addr = i * 4; wr.data = 32'h0; wr.start(m_sequencer); end endtask endclass // Layer 3: Full test flow class full_test_seq extends uvm_sequence #(apb_txn); `uvm_object_utils(full_test_seq) function new(string name = "full_test_seq"); super.new(name); endfunction task body(); init_sequence init; traffic_sequence traffic; cleanup_sequence cleanup; init = init_sequence::type_id::create("init"); traffic = traffic_sequence::type_id::create("traffic"); cleanup = cleanup_sequence::type_id::create("cleanup"); init.start(m_sequencer); // Phase 1 traffic.start(m_sequencer); // Phase 2 cleanup.start(m_sequencer); // Phase 3 endtask endclass
2. Event-Based Synchronization
Coordinate components using UVM events:
systemverilogclass my_env extends uvm_env; `uvm_component_utils(my_env) uvm_event reset_done; uvm_event config_done; function new(string name, uvm_component parent); super.new(name, parent); endfunction function void build_phase(uvm_phase phase); super.build_phase(phase); reset_done = new("reset_done"); config_done = new("config_done"); endfunction endclass // In the reset sequence: task body(); // ... perform reset ... `uvm_info("RST", "Reset complete", UVM_NONE) p_sequencer.env.reset_done.trigger(); endtask // In the main test sequence: task body(); // Wait for reset to finish before proceeding p_sequencer.env.reset_done.wait_trigger(); `uvm_info("MAIN", "Starting main traffic", UVM_NONE) // ... run test ... endtask
3. Response Handler Pattern
Process drive requests and responses in separate threads:
systemverilogclass my_driver extends uvm_driver #(my_txn); `uvm_component_utils(my_driver) virtual my_if vif; function new(string name, uvm_component parent); super.new(name, parent); endfunction task run_phase(uvm_phase phase); fork drive_requests(); collect_responses(); join endtask task drive_requests(); my_txn req; forever begin seq_item_port.get_next_item(req); // Drive request to DUT @(posedge vif.clk); vif.valid <= 1; vif.addr <= req.addr; vif.data <= req.data; @(posedge vif.clk); vif.valid <= 0; seq_item_port.item_done(); end endtask task collect_responses(); forever begin @(posedge vif.clk); if (vif.resp_valid) begin // Process async response from DUT `uvm_info("DRV", $sformatf( "Response: data=0x%h", vif.resp_data ), UVM_HIGH) end end endtask endclass
4. Test Library Pattern
Organize tests with a common base and specialized overrides:
systemverilog// Base test: common setup for all tests class base_test extends uvm_test; `uvm_component_utils(base_test) my_env env; function new(string name, uvm_component parent); super.new(name, parent); endfunction function void build_phase(uvm_phase phase); super.build_phase(phase); env = my_env::type_id::create("env", this); endfunction function void check_phase(uvm_phase phase); // Common end-of-test checks endfunction endclass // Smoke test: basic functionality class smoke_test extends base_test; `uvm_component_utils(smoke_test) function new(string name, uvm_component parent); super.new(name, parent); endfunction task run_phase(uvm_phase phase); smoke_vseq vseq; phase.raise_objection(this); vseq = smoke_vseq::type_id::create("vseq"); vseq.start(env.v_sqr); phase.drop_objection(this); endtask endclass // Stress test: high-volume traffic class stress_test extends base_test; `uvm_component_utils(stress_test) function new(string name, uvm_component parent); super.new(name, parent); endfunction task run_phase(uvm_phase phase); stress_vseq vseq; phase.raise_objection(this); vseq = stress_vseq::type_id::create("vseq"); vseq.start(env.v_sqr); phase.drop_objection(this); endtask endclass
Select from the command line:
bashvsim +UVM_TESTNAME=smoke_test vsim +UVM_TESTNAME=stress_test
5. UVM Heartbeat — Detect Hangs
systemverilogclass watchdog_env extends uvm_env; `uvm_component_utils(watchdog_env) uvm_heartbeat hb; uvm_event hb_event; function new(string name, uvm_component parent); super.new(name, parent); endfunction function void build_phase(uvm_phase phase); super.build_phase(phase); hb_event = new("hb_event"); hb = new("hb", this, hb_event); endfunction function void connect_phase(uvm_phase phase); uvm_component comps[$]; comps.push_back(agent.driver); comps.push_back(agent.monitor); hb.set_mode(UVM_ANY_ACTIVE); hb.add(comps); endfunction endclass
If no registered component raises an objection within the heartbeat window, UVM issues a fatal error — catching hangs early.
Key Takeaways
| Pattern | When to Use |
|---|---|
| Layered sequences | Complex multi-step test scenarios |
| Event synchronization | Cross-component coordination |
| Response handler | Async request/response protocols |
| Test library | Organized, maintainable test suite |
| Heartbeat | Long-running simulations that may hang |
🎉 Congratulations! You've completed the full UVM tutorial series — from basics to production-grade patterns!