Getting Configuration in Sequences with Null Sequencer - UVM Guide

When using virtual sequences or layered sequences in UVM, you often start upper-layer sequences on a null sequencer. This creates a challenge: how do you access configuration objects when there's no component hierarchy? This post covers the solutions.

The Problem

In a typical UVM environment:

  • Sequences run on sequencers (components in the hierarchy)
  • uvm_config_db uses the component hierarchy for context
  • Virtual sequences often run on null - no sequencer, no hierarchy!
flowchart TD
    subgraph NORMAL["Normal Sequence"]
        N1["Sequence"] --> N2["m_sequencer"]
        N2 --> N3["Component hierarchy"]
        N3 --> N4["config_db::get() works"]
    end
    
    subgraph VIRTUAL["Virtual Sequence"]
        V1["Virtual Sequence"] --> V2["null sequencer"]
        V2 --> V3["No hierarchy!"]
        V3 --> V4["config_db::get() fails"]
    end
    
    style N4 fill:#d1fae5,stroke:#10b981
    style V4 fill:#fee2e2,stroke:#ef4444
Key Issue: When m_sequencer is null, get_full_name() returns just the sequence name with no hierarchy path, breaking config_db lookups.

Example: The Failing Approach

class virtual_sequence extends uvm_sequence;

  `uvm_object_utils(virtual_sequence)

  my_config cfg;

  function new(string name = "virtual_sequence");
    super.new(name);
  endfunction

  virtual task body();
    // This FAILS when started on null sequencer!
    if (!uvm_config_db#(my_config)::get(m_sequencer, "", "cfg", cfg)) begin
      `uvm_fatal("CFG", "Failed to get config")
    end

    // Run sub-sequences...
  endtask

endclass

When started with vseq.start(null), m_sequencer is null and the config lookup fails.

Solution 1: Use uvm_root as Context (Recommended)

Use uvm_root::get() as the context for global configurations:

class virtual_sequence extends uvm_sequence;

  `uvm_object_utils(virtual_sequence)

  my_config cfg;

  virtual task body();
    // Use uvm_root for context - works with null sequencer!
    if (!uvm_config_db#(my_config)::get(uvm_root::get(), "", "cfg", cfg)) begin
      `uvm_fatal("CFG", "Failed to get config from uvm_root")
    end

    `uvm_info("VSEQ", $sformatf("Got config: %s", cfg.convert2string()), UVM_MEDIUM)
  endtask

endclass

Setting Config for This Approach

// In test or env build_phase:
uvm_config_db#(my_config)::set(uvm_root::get(), "", "cfg", my_cfg);

// Or with wildcard:
uvm_config_db#(my_config)::set(uvm_root::get(), "*", "cfg", my_cfg);

Solution 2: Use null with Wildcard Path

class virtual_sequence extends uvm_sequence;

  `uvm_object_utils(virtual_sequence)

  my_config cfg;

  virtual task body();
    // null context with empty path
    if (!uvm_config_db#(my_config)::get(null, "", "cfg", cfg)) begin
      `uvm_fatal("CFG", "Failed to get config")
    end
  endtask

endclass

Setting Config for This Approach

// Set with null context and empty path:
uvm_config_db#(my_config)::set(null, "", "cfg", my_cfg);

Solution 3: Pass Config Through Constructor/Property

Avoid config_db entirely by passing the config directly:

class virtual_sequence extends uvm_sequence;

  `uvm_object_utils(virtual_sequence)

  // Config passed directly - no config_db needed
  my_config cfg;

  function new(string name = "virtual_sequence");
    super.new(name);
  endfunction

  virtual task body();
    if (cfg == null) begin
      `uvm_fatal("CFG", "Config not set before starting sequence")
    end

    `uvm_info("VSEQ", $sformatf("Using config: %s", cfg.convert2string()), UVM_MEDIUM)
  endtask

endclass


// In test:
class my_test extends base_test;

  virtual_sequence vseq;

  virtual task run_phase(uvm_phase phase);
    phase.raise_objection(this);

    vseq = virtual_sequence::type_id::create("vseq");
    vseq.cfg = env.cfg;  // Direct assignment
    vseq.start(null);

    phase.drop_objection(this);
  endtask

endclass

Solution 4: Access Through p_sequencer

If starting on an actual sequencer, use the p_sequencer macro:

class my_sequence extends uvm_sequence #(my_seq_item);

  `uvm_object_utils(my_sequence)
  `uvm_declare_p_sequencer(my_sequencer)

  virtual task body();
    // Access config through typed sequencer
    my_config cfg = p_sequencer.cfg;

    // Or through sequencer's config_db:
    if (!uvm_config_db#(my_config)::get(p_sequencer, "", "cfg", cfg)) begin
      `uvm_fatal("CFG", "Failed to get config")
    end
  endtask

endclass

Comparison of Approaches

ApproachProsConsBest For
uvm_root::get()Always works, standard patternGlobal scope onlyTop-level virtual sequences
null contextSimple syntaxCan be confusingQuick prototyping
Direct assignmentNo config_db overhead, clearManual wiringTight coupling OK
p_sequencerType-safe accessRequires real sequencerRegular sequences

Complete Working Example

// Configuration class
class my_config extends uvm_object;

  `uvm_object_utils(my_config)

  rand int timeout;
  rand bit enable_coverage;
  string dut_name;

  function new(string name = "my_config");
    super.new(name);
    timeout = 1000;
    enable_coverage = 1;
    dut_name = "default";
  endfunction

  function string convert2string();
    return $sformatf("timeout=%0d, cov=%0b, dut=%s",
                     timeout, enable_coverage, dut_name);
  endfunction

endclass


// Virtual sequence using uvm_root
class my_virtual_sequence extends uvm_sequence;

  `uvm_object_utils(my_virtual_sequence)

  my_config cfg;

  function new(string name = "my_virtual_sequence");
    super.new(name);
  endfunction

  virtual task body();
    // Get config using uvm_root - works with null sequencer
    if (!uvm_config_db#(my_config)::get(uvm_root::get(), "", "my_cfg", cfg)) begin
      `uvm_fatal("CFG", "Cannot get my_cfg from config_db")
    end

    `uvm_info("VSEQ", $sformatf("Config: %s", cfg.convert2string()), UVM_LOW)

    // Start sub-sequences on actual sequencers
    // ...
  endtask

endclass


// Test that sets config and runs virtual sequence
class config_test extends uvm_test;

  `uvm_component_utils(config_test)

  my_config cfg;

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

  virtual function void build_phase(uvm_phase phase);
    super.build_phase(phase);

    // Create and configure
    cfg = my_config::type_id::create("cfg");
    cfg.timeout = 5000;
    cfg.dut_name = "test_dut";

    // Set in config_db with uvm_root context
    uvm_config_db#(my_config)::set(uvm_root::get(), "", "my_cfg", cfg);
  endfunction

  virtual task run_phase(uvm_phase phase);
    my_virtual_sequence vseq;

    phase.raise_objection(this);

    vseq = my_virtual_sequence::type_id::create("vseq");
    vseq.start(null);  // Started on null - still works!

    phase.drop_objection(this);
  endtask

endclass

Interview Questions

Q1: How do you get configuration in a sequence running on a null sequencer?

Answer: Use uvm_root::get() as the context in uvm_config_db::get(). This provides a valid component context even when m_sequencer is null. Alternatively, pass the config directly to the sequence before starting it.

Q2: Why does uvm_config_db::get fail with null sequencer?

Answer: uvm_config_db uses the component hierarchy for context matching. When m_sequencer is null, there's no hierarchy path, so the lookup fails. Using uvm_root::get() or null context with matching set() solves this.

Q3: What are virtual sequences and why use null sequencer?

Answer: Virtual sequences coordinate multiple sub-sequences across different sequencers. They don't generate transactions directly, so they don't need a real sequencer. Starting on null is cleaner and indicates the sequence is a coordinator, not a generator.

Key Takeaways

  • Null sequencer means no component hierarchy for config_db
  • Use uvm_root::get() as context for global configs
  • Match set/get contexts and paths carefully
  • Direct assignment is simpler when coupling is acceptable
  • p_sequencer only works with real sequencers
  • Virtual sequences typically coordinate, not generate transactions
Author
Mayur Kubavat
VLSI Design and Verification Engineer sharing knowledge about SystemVerilog, UVM, and hardware verification methodologies.

Comments (0)

Leave a Comment