Chapter 6 of 6
Capstone: Assembling the Advanced Environment
Organize five chapters' worth of classes into one package, add a second scenario that never clears the interrupt -- proving COUNT keeps incrementing past the threshold while irq stays level and the monitor never double-reports -- and switch between it and chapter 5's scenario from the command line, the same factory-override trick uvm ch10 used, without touching run_phase.
Every piece of regfile_env already exists — axi_agent and irq_agent (chapters 2-3), the virtual sequencer coordinating them (chapter 4), the register model (chapter 5). Like uvm ch10, this chapter is closer to pure assembly than to new concepts: organize everything into one package, add a second scenario that genuinely exercises the environment differently, and switch between the two from the command line without rewriting anything that already works.
Organizing everything into a package
Same principle SV ch16 established and uvm ch10 reused: one class per file, `included into a single package, imported once by the top module.
`include "uvm_macros.svh"
package regfile_uvm_pkg;
import uvm_pkg::*;
`include "axil_regfile.sv"
`include "axi4lite_if.sv"
`include "irq_if.sv"
`include "axi_txn.sv"
`include "axi_driver.sv"
`include "axi_monitor.sv"
`include "axi_sequencer.sv"
`include "axi_agent.sv"
`include "axi_basic_seq.sv"
`include "irq_txn.sv"
`include "irq_monitor.sv"
`include "irq_agent.sv"
`include "irq_watcher.sv"
`include "ctrl_reg.sv"
`include "status_reg.sv"
`include "data_reg.sv"
`include "count_reg.sv"
`include "regfile_reg_block.sv"
`include "axi_reg_adapter.sv"
`include "regfile_vsqr.sv"
`include "regfile_env.sv"
`include "regfile_virtual_seq.sv"
`include "regfile_no_clear_seq.sv"
`include "axi_smoke_test.sv"
`include "regfile_no_clear_test.sv"
endpackageTwenty-two files, five chapters — and every single one of them is either unchanged from when it was first introduced, or has exactly the one addition each later chapter called out explicitly (irq_monitor gained one line in chapter 4, regfile_vsqr/regfile_env each grew by one field per chapter). Nothing here required going back and rewriting an earlier chapter's design.
A second scenario: never clearing the interrupt
Chapter 5's scenario always clears the interrupt right after confirming it asserts. This chapter's new sequence does the opposite on purpose — write past the threshold repeatedly, never clear, and check that both the register model and the interrupt behave exactly as they should under sustained assertion:
class regfile_no_clear_seq extends regfile_virtual_seq;
`uvm_object_utils(regfile_no_clear_seq)
function new(string name = "regfile_no_clear_seq");
super.new(name);
endfunction
task body();
uvm_event irq_event = uvm_event_pool::get_global("irq_event");
irq_txn ev_txn;
uvm_status_e status;
uvm_reg_data_t rdata;
regfile_reg_block regmodel = p_sequencer.regmodel;
write(8'h00, 32'h1); // CTRL: ENABLE=1
write(8'h08, 32'hAA); // #1
write(8'h08, 32'hBB); // #2
write(8'h08, 32'hCC); // #3
fork
write(8'h08, 32'hDD); // #4 -> crosses IRQ_THRESHOLD
irq_event.wait_trigger();
join
if ($cast(ev_txn, irq_event.get_trigger_data()) && ev_txn.level == 1'b1)
`uvm_info("VSEQ", "confirmed: irq asserted at write #4", UVM_LOW)
else
`uvm_error("VSEQ", "expected irq to assert at write #4")
// Two more writes past the threshold, no IRQ_CLR anywhere -- irq
// should stay asserted the whole time, and since irq_monitor only
// reports on a *change* (chapter 3), neither write should produce a
// second ASSERTED event.
write(8'h08, 32'hEE); // #5
write(8'h08, 32'hFF); // #6
regmodel.COUNT.read(status, rdata, UVM_FRONTDOOR, regmodel.default_map, this);
if (rdata == 32'd6)
`uvm_info("VSEQ", $sformatf("confirmed: COUNT kept incrementing past the threshold to %0d, while irq stayed asserted (and silent) the whole time", rdata), UVM_LOW)
else
`uvm_error("VSEQ", $sformatf("expected COUNT=6 after 6 enabled DATA writes with no clear, got %0d", rdata))
endtask
endclassextends regfile_virtual_seq — not uvm_sequence directly — the same reason chapter 10 of uvm had my_sequence_exhaustive extends my_sequence: the inherited write() helper task and `uvm_declare_p_sequencer are reused as-is, and only body() changes. Running this scenario against the actual DUT confirms both halves precisely: COUNT really does reach 6, and exactly one irq event fires for the entire sequence — writes #5 and #6 happen while irq is already asserted, so irq_monitor's vif.cb.irq !== last check (chapter 3) never sees a change to report.
A second test, without rewriting run_phase
axi_smoke_test (chapters 2-5) is completely unchanged. The new test swaps which sequence class actually runs, using exactly the mechanism uvm ch6/ch10 already taught:
class regfile_no_clear_test extends axi_smoke_test;
`uvm_component_utils(regfile_no_clear_test)
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
regfile_virtual_seq::type_id::set_type_override(regfile_no_clear_seq::get_type());
super.build_phase(phase);
endfunction
endclassaxi_smoke_test's run_phase creates a regfile_virtual_seq via type_id::create(...) and runs it — unchanged, inherited byte-for-byte. The factory override set in build_phase, before that create() call ever executes, is what makes it actually construct a regfile_no_clear_seq instead — the identical trick uvm ch10 used for exhaustive_test, just one level removed (overriding a virtual sequence's type instead of a plain sequence's).
Running both scenarios
module tb_top;
// ... same DUT, axi4lite_if, irq_if, config_db handoffs as chapters 1-5 ...
initial begin
uvm_config_db#(virtual axi4lite_if.tb_mp)::set(null, "*", "vif", axi_if);
uvm_config_db#(virtual irq_if.mon_mp)::set(null, "*", "vif", irq_intf);
run_test();
end
endmodulerun_test() with no argument (uvm ch10) reads which test to build from +UVM_TESTNAME:
+UVM_TESTNAME=axi_smoke_test // chapter 5's scenario: assert, then clear
+UVM_TESTNAME=regfile_no_clear_test // this chapter's scenario: assert, stay asserted, keep counting
Same compiled environment, same DUT, same package — which scenario actually runs is a command-line flag, not a source edit.
What this track didn't cover
This environment coordinates two agents, drives a real register map through RAL, and synchronizes across a passive observer — genuinely more than a single-agent environment could. It still only checks the two scenarios this track wrote by hand. Nothing here measures whether these two scenarios are enough — whether every reachable value of COUNT, every response code, every ordering of writes relative to reads has actually been exercised — and nothing here has a systematic way to find a bug neither scenario happens to trigger. That's exactly dv-methodology's stated scope: coverage-driven judgment (measuring and directing thoroughness, not just checking two hand-picked scenarios) and debug intuition (a systematic way to chase down a failure once one of these checks actually fires). Same handoff shape as every capstone before this one: everything here gets reused, nothing gets thrown away, and the next track standardizes what's still being decided by hand.
Summary
- Twenty-two files, one
`include-based package — the same organizing principle from SV ch16 anduvmch10, and every file is either unchanged since its introducing chapter or grew by exactly the one addition that chapter called out. regfile_no_clear_seq extends regfile_virtual_seqreuses the inheritedwrite()helper andp_sequencerdeclaration, changing onlybody()— the same shape asuvmch10'smy_sequence_exhaustive extends my_sequence.- Verified against the real DUT:
COUNTreaches6after six enabledDATAwrites with no clear, and exactly oneirqevent fires for the whole sequence —irq_monitor's change-only reporting (chapter 3) means writes past the threshold produce no duplicate events. regfile_no_clear_test extends axi_smoke_test, overriding onlybuild_phaseto install a factory override before the inheritedrun_phase'screate()call ever runs —run_phaseitself is never touched, the identical trickuvmch6/ch10 already taught.run_test()with+UVM_TESTNAMEswitches between chapter 5's scenario and this chapter's from the command line, same compiled package.- This track checked two hand-picked scenarios; it never measured whether that's enough, and has no systematic way to chase a failure neither one happens to trigger. That's
dv-methodology's job.
Why does regfile_no_clear_seq extend regfile_virtual_seq instead of uvm_sequence directly?
Running regfile_no_clear_seq against the real DUT, how many irq events does irq_monitor report in total, and why?
What system task/function, called with no argument, reads which test to construct from the +UVM_TESTNAME command-line plusarg?