Part 3 · Factory & Configuration · Intermediate

Overrides Debug Patterns: Print, Paths, and Conflict Triage

Deterministic override debug workflow, factory.print interpretation, path mismatch isolation, and regression checks for override policy.

Override debug boundary order

Triage override failures in order: timing, requested base type, scope selection, path match, precedence, then Liskov/connectivity.

diagram
[FACTORY][UVM] override triage matrix

override seems ignored:
  -> registered before create?
  -> env still requests correct base type?
  -> instance path exactly matches get_full_name()?

wrong agent got override:
  -> type override too broad?
  -> path points to sibling agent?

connect_phase fails after override:
  -> derivative broke port/parameter contract?
systemverilog
function void report_overrides();
  uvm_factory f = uvm_factory::get();
  f.print(1);
endfunction

function void end_of_elaboration_phase(uvm_phase phase);
  super.end_of_elaboration_phase(phase);
  report_overrides();
  `uvm_info("OVERRIDE_CHK",
    $sformatf("tx=%s rx=%s",
      env.agt_tx.drv.get_type_name(),
      env.agt_rx.drv.get_type_name()), UVM_NONE)
endfunction

Key takeaways

  • factory.print() plus get_type_name() resolves most override mysteries.

  • Path mismatch is the leading root cause for instance override misses.

  • Connect failures after override often indicate Liskov structural breakage.

Common pitfalls

  • Changing both override policy and env hierarchy in one debug iteration.

  • Assuming print presence guarantees effective substitution.

  • Skipping seed replay after override policy changes.


Deterministic override debug toolkit

Keep lightweight override diagnostics in base_test for every scenario regression.

Instrumentation primitives

systemverilog
virtual function void apply_factory_overrides();
  override_apply_count++;
  `uvm_info("OVERRIDE_APPLY",
    $sformatf("test=%s count=%0d", get_type_name(), override_apply_count),
    UVM_LOW)
endfunction

function void log_override_targets();
  if (!uvm_config_db#(bit)::get(this, "", "override_debug", override_debug))
    override_debug = 0;
  if (override_debug)
    factory.print(1);
endfunction
diagram
[FACTORY] recommended override counters

- override_apply_count
- type_override_count
- inst_override_count
- override_path_mismatch_suspects
- elab_type_mismatch_count

Reproduction checklist

  1. Capture factory.print() after apply_factory_overrides().

  2. Run one deterministic seed twice; compare get_type_name() outputs.

  3. For partial misses, print full names of all sibling targets.

  4. If connect fails, diff base vs derivative port members.

  5. Fix one scope issue at a time (type OR instance, not both).

diagram
[FACTORY][UVM] override debug pass criteria

expected override entries in factory.print
AND
all target nodes report expected derivative type
AND
connect_phase/run_phase pass without topology edits

Conflict patterns

  • base_test and child_test both apply type overrides — document winner.

  • Multiple instance overrides on same path without replace=1.

  • Debug test leaves global type override active for later tests in same sim (if not isolated).

diagram
[UVM] common conflict fix

centralize all overrides in apply_factory_overrides()
define order:
  1) base_test policy
  2) child scenario deltas
log final effective policy once

Key takeaways

  • Override debug should be boring and repeatable — checklist driven.

  • Isolate one variable per iteration: timing, scope, path, precedence.

  • Persistent counters/logs in base_test reduce regression triage time.

Common pitfalls

  • Removing override debug hooks once one scenario passes.

  • Fixing override bugs by hardcoding derivatives in env.

  • Ignoring factory debug because behavior 'looks close enough'.