Part 7 · Environment & Tests · Intermediate

Base Test Pattern: One Contract for Every Scenario

Core base_test skeleton that centralizes env creation, default configuration, and lifecycle policy for all derived tests.

Canonical base_test contract

Base tests should define constructor, build_phase, configure hook, and run policy once, then expose narrow extension points.

diagram
[TEST][UVM] base_test responsibilities

build_phase:
  create cfg
  apply defaults
  push cfg to config_db
  create env

run_phase:
  open objection
  execute scenario flow
  enforce end criteria
  close objection
systemverilog
class base_test extends uvm_test;
  `uvm_component_utils(base_test)
  my_env env;
  env_cfg cfg;

  function new(string name, uvm_component parent);
    super.new(name, parent);
  endfunction

  virtual function void apply_defaults();
    cfg.enable_scoreboard = 1;
    cfg.enable_coverage   = 1;
    cfg.default_timeout_ns = 2_000_000;
  endfunction

  function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    cfg = env_cfg::type_id::create("cfg");
    apply_defaults();
    uvm_config_db#(env_cfg)::set(this, "env*", "cfg", cfg);
    env = my_env::type_id::create("env", this);
  endfunction
endclass

Key takeaways

  • A minimal stable contract beats many ad-hoc convenience methods.

  • apply_defaults() should express policy, not scenario specifics.

  • Every derived test should compile if only configure hooks change.

Common pitfalls

  • Creating env before pushing cfg into config_db.

  • Using plusarg parsing directly in every child test.

  • Hardcoding sequence types in base_test without extension hooks.


Implementation pattern kit

The snippets below form a reusable scaffold for teams that maintain large test libraries.

Hook structure

systemverilog
virtual function void configure_scenario();
  // child tests override this method to set cfg knobs
endfunction

virtual task pre_main();
  // optional setup common to all tests
endtask

virtual task post_main();
  // optional teardown common to all tests
endtask
diagram
[TEST][ENV] hook order

build_phase:
  apply_defaults()
  configure_from_plusargs()
  create env

run_phase:
  pre_main()
  configure_scenario()
  run_main_sequence()
  post_main()

Review checklist

  • No direct hierarchical signal peek/poke in test classes.

  • No duplicated timeout logic outside base_test.

  • Factory override calls are centralized and auditable.

  • All child tests override only documented hooks.

diagram
[TEST] quick smoke

1) run base_test directly
2) run one child scenario
3) confirm both share same startup logs
4) verify only scenario knobs differ

Common pitfalls

  • Adding too many hooks without clear ownership semantics.

  • Allowing configure methods to create components dynamically.

  • Hiding mandatory lifecycle in optional hooks.