Part 10 · Advanced Topics · Intermediate

build_phase Parsing Discipline: Timing, Ownership, and Safety

Ensuring CLI values are parsed early, propagated correctly, and consumed deterministically across UVM phases.

Why build_phase is the right parsing window

Most runtime knobs affect component construction and configuration. Therefore parsing should happen before or during build-phase, not deep in run-phase after behavior has already diverged.

Late parsing creates split-brain configuration: some components use defaults from build_phase while others consume CLI values later, producing inconsistent and hard-to-replay behavior.

diagram
Legend: [UVM] [ADV]

  [UVM] phase ordering relevant to CLI knobs

  start_of_simulation
          ▲
  build_phase       <- parse + validate + publish config
  connect_phase     <- consume wiring knobs if needed
  end_of_elaboration
  run_phase         <- use resolved values only (no primary parsing)
  • Primary parsing belongs in a deterministic early lifecycle point.

  • Run-phase should consume final values, not decide them.

  • Treat parse location as architecture, not convenience.


Centralized parsing ownership model

Assign one component (usually test or env root) as parsing owner. That owner parses once, validates once, and publishes typed config to all consumers.

systemverilog
class base_test extends uvm_test;
  `uvm_component_utils(base_test)
  cli_cfg cfg;

  function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    cfg = cli_cfg::type_id::create("cfg");

    parse_cli(cfg);
    validate_cfg(cfg);
    log_cfg(cfg);

    // Publish to env descendants
    uvm_config_db#(cli_cfg)::set(this, "env*", "cli_cfg", cfg);
  endfunction
endclass
diagram
[ADV] ownership matrix

  Responsibility              Preferred owner
  --------------------------------------------------------
  parse raw command line      test/env root
  validate ranges/enums       test/env root utility
  publish typed config        config_db at root scope
  consume operational values  env/agent/components
  log resolved configuration  root + optional local echo
  • Avoid parsing inside many leaf components.

  • Use typed config objects instead of many scalar config_db keys when possible.

  • Keep parser and validator reusable across tests.


Consumption patterns and defensive checks

Consumers should assume config might be missing if ownership contracts are violated, and fail with actionable diagnostics rather than continuing silently.

systemverilog
class traffic_agent extends uvm_agent;
  `uvm_component_utils(traffic_agent)
  cli_cfg cfg;

  function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    if (!uvm_config_db#(cli_cfg)::get(this, "", "cli_cfg", cfg))
      `uvm_fatal("CFG", "cli_cfg not found; parsing owner not configured?")

    `uvm_info("CFG", $sformatf(
      "agent sees NUM_TXN=%0d BURST_MAX=%0d MODE=%s",
      cfg.num_txn, cfg.burst_max, cfg.traffic_mode), UVM_LOW)
  endfunction
endclass
diagram
[UVM] defensive consumption checklist

  [ ] get() return value checked
  [ ] fatal on missing critical configuration
  [ ] local echo logging for high-impact knobs
  [ ] no fallback to hidden defaults without warning
  [ ] build-phase only, not deferred to run-phase
  1. Fail fast for missing mandatory config objects.

  2. Allow defaults only for explicitly optional knobs.

  3. Echo resolved values near the point of use for easier triage.


Late-parse failure modes and recovery

If a project already parses late, migrate incrementally: introduce early parsing while preserving old hooks behind warnings, then remove late hooks once consumers are converted.

diagram
[ADV] common late-parse failure modes

  Failure mode                         Observable symptom
  --------------------------------------------------------------------------
  mixed defaults and CLI values        inconsistent behavior across components
  replay mismatch                      same command does not reproduce result
  phase-order race                     intermittent config visibility
  hidden override in run_phase         hard-to-explain seed-dependent drift
diagram
[UVM] phased recovery plan

  Step 1: add root build-phase parser + logger
  Step 2: publish typed config object in config_db
  Step 3: migrate one consumer at a time to typed get()
  Step 4: warn on legacy late parsing path
  Step 5: remove legacy path after two stable release cycles

Key takeaways

  • Parse CLI in build-phase under a single owner for determinism.

  • Publish typed configuration and require explicit consumer checks.

  • Late parsing creates replay and consistency failures; retire it gradually with warnings.

  • Configuration lifecycle discipline is as important as parser syntax.

Common pitfalls

  • Parsing custom args independently in multiple components.

  • Allowing run-phase parsing to mutate behavior after build decisions.

  • Using silent defaults for missing critical command-line configuration.