Part 10 · Advanced Topics · Intermediate

Simulator Seed Control

Tool-specific random seed controls, deterministic launch patterns, and policy boundaries between test logic and regression launcher.

Why control belongs to the launcher

A test should define scenario intent ; the launcher should define seed identity. Keeping these concerns separate avoids hard-coded seeds in test code and allows matrix runs without recompiling.

Most teams standardize on a launcher contract such as --seed <int>|random and map it to vendor-specific simulator arguments. The contract should be stable even when simulator flavor changes.

diagram
[REG] launcher contract -> tool mapping

  User intent:
    --seed 12345
    --seed random

  Mapping:
    VCS      -> +ntb_random_seed=<value>
    Xcelium  -> -svseed <value>
    Questa   -> -sv_seed <value>
    Riviera  -> -sv_seed <value>

  Rule:
    random means simulator generates a seed
    but launcher must still capture the chosen value

Portable command snippets

bash
# VCS
simv +UVM_TESTNAME=axi_random_test +ntb_random_seed=821734 \
     +UVM_VERBOSITY=UVM_LOW

# Xcelium
xrun -R -uvm -svseed 821734 \
     +UVM_TESTNAME=axi_random_test +UVM_VERBOSITY=UVM_LOW

# Questa
vsim -c work.top -sv_seed 821734 \
     +UVM_TESTNAME=axi_random_test -do "run -all; quit -f"
  • Do not combine multiple seed options in one command.

  • Do not rely on simulator default seed behavior.

  • Record the final chosen numeric seed, even in random mode.


Seed propagation inside UVM

The simulator seed initializes the random stream, then object-level randomization proceeds through sequence items and classes. Debug often needs both top-level seed and object-local random state snapshots.

systemverilog
class seed_probe_test extends uvm_test;
  `uvm_component_utils(seed_probe_test)

  function void start_of_simulation_phase(uvm_phase phase);
    super.start_of_simulation_phase(phase);
    // Print enough metadata for deterministic replay.
    `uvm_info(
      "SEED",
      $sformatf(
        "test=%s sim_seed=%0d build=%s",
        get_type_name(),
        $get_initial_random_seed(),
        `GIT_SHA
      ),
      UVM_NONE
    )
  endfunction
endclass

If your simulator does not support $get_initial_random_seed(), pull the seed from the launch manifest and print that value via plusarg parsing.

Plusarg fallback pattern

systemverilog
function int get_seed_from_plusarg();
  string s;
  if ($value$plusargs("DC_SEED=%s", s))
    return s.atoi();
  return -1; // unknown
endfunction

Key takeaways

  • Keep seed policy in launcher, not in test source code.

  • Map one stable interface to simulator-specific options.

  • Always emit the effective numeric seed in runtime logs.

  • Treat random mode as random selection plus deterministic recording.

Common pitfalls

  • Hard-coding seed constants in sequences.

  • Using tool defaults and expecting consistency across versions.

  • Forgetting to persist selected seed when random mode is used.


Regression-friendly launch wrappers

A thin wrapper script can normalize seed handling and reduce command drift across teams.

bash
#!/usr/bin/env bash
set -euo pipefail

SIM="${SIM:-vcs}"
TEST="${1:-axi_random_test}"
SEED="${2:-random}"

case "$SIM" in
  vcs)
    if [[ "$SEED" == "random" ]]; then
      CMD=(simv +UVM_TESTNAME="$TEST" +ntb_random_seed=random)
    else
      CMD=(simv +UVM_TESTNAME="$TEST" +ntb_random_seed="$SEED")
    fi
    ;;
  xrun)
    if [[ "$SEED" == "random" ]]; then
      CMD=(xrun -R -uvm +UVM_TESTNAME="$TEST" -svseed random)
    else
      CMD=(xrun -R -uvm +UVM_TESTNAME="$TEST" -svseed "$SEED")
    fi
    ;;
  *)
    echo "unsupported SIM=$SIM" >&2
    exit 2
    ;;
esac

echo "[REG] launch sim=$SIM test=$TEST seed=$SEED"
"${CMD[@]}"
diagram
[REG] wrapper responsibilities

  must:
    - accept explicit seed or random mode
    - print launch tuple (sim, test, seed mode)
    - write manifest json row per run
    - preserve additional plusargs

  should:
    - detect duplicate run-id collisions
    - include tool version and compile hash