Skip to content
DenseChip
Learn
PracticeCode labInterview

DenseChip

Navigate

Menu

LearnPracticeCode labInterview

Tools

All toolsSV editorCode repoResume
DenseChip

Practice, courses, and chip-design tools in one focused workspace.

LearnPracticeCode labInterviewToolsLearning paths

© 2026 DenseChip

AboutContactPrivacyTerms
Skip to lesson

Course contents

  • Start Here4
  • UVM Foundations14
  • UVM Phases & Simulation Lifecycle26
  • Factory & Configuration15
  • Transaction-Level Modeling (TLM)13
  • UVM Sequences & Stimulus21
    • Sequence Items and the Driver Handshake
    • Sequence Mechanics: Lifecycle, Sequencer, Responses and Control
    • Virtual Sequences and Coordination
      • The Coordination Problem
      • Virtual Sequencer Wiring
      • Declaring p_sequencer
      • Scenario Patterns and Placement
      • Sub-sequences and Sequence Libraries
      • Virtual Sequences Without a Virtual Sequencer
      • lock vs grab: Who Gets the Sequencer
      • Protocol Layering with Translator Sequences
      • Reactive (Slave) Sequences
  • UVM Agents & Protocol VIP21
  • Environment & Test Classes11
  • Checking & Coverage in UVM21
  • UVM Register Abstraction Layer (RAL)22
  • Advanced UVM in Practice13
  • Debug & Regression10
  • Coding Practice47
  • Verification in Industry Practice10
  • Senior Prep: Architecture, Leadership and Interviews32
Loading lesson

‹Virtual Sequences and Coordination
  1. Topics
  2. UVM Verification
  3. UVM Sequences & Stimulus
  4. Virtual Sequences and Coordination
  5. Declaring p_sequencer

Part 5 · Sequences · Intermediate

Declaring p_sequencer

What uvm_declare_p_sequencer expands to, m_sequencer versus p_sequencer, the DCLPSQ fatal when a sequence starts on the wrong sequencer type, and what the parent argument controls (locks and priority).

On this page

    A colleague's new test fails at time 0 with a fatal you have never seen: [DCLPSQ] ... Error casting p_sequencer, please verify that this sequence/sequence item is intended to execute on this type of sequencer. The virtual sequence compiles, the env builds, and nothing has run yet. What is being cast, by whom, and why does it fail before body() has executed a single line?

    The idea: a typed name badge

    Every sequence already knows the sequencer it runs on. The handle is m_sequencer, and its type is uvm_sequencer_base: the most general sequencer type, which knows how to arbitrate but nothing about the members your virtual sequencer added. Writing m_sequencer.apb_sqr does not compile, because the base class has no apb_sqr.

    ` uvm_declare_p_sequencer(dma_vsqr2) ` gives the sequence a second, **typed** handle to the same object, called p_sequencer. Think of a conference badge: m_sequencer says *attendee*; p_sequencer says *speaker, room 4*, which tells you what the person can actually do. Without the macro there is no p_sequencer` member at all. The macro also writes the code that fills it in, and that code is where the fatal comes from.

    design.sv
    1
    2
    3
    4
    5
    6
    7
    // snippet: what the macro expands to (UVM 1.2 and IEEE 1800.2)
    dma_vsqr2 p_sequencer;
    virtual function void m_set_p_sequencer();
      super.m_set_p_sequencer();
      if (!$cast(p_sequencer, m_sequencer))
        `uvm_fatal("DCLPSQ", $sformatf("%m %s Error casting p_sequencer, please verify that this sequence/sequence item is intended to execute on this type of sequencer", get_full_name()))
    endfunction

    start(sqr) sets m_sequencer and immediately calls m_set_p_sequencer(), before pre_start and long before body. If the object is not a dma_vsqr2 (or a class derived from it), the $cast fails and the run stops right there.

    The picture: what start() does with the sequencer

    Flowchart: start sets m_sequencer, casts it to the p_sequencer type, and either continues to body or stops with a DCLPSQ fatal.
    start() records the sequencer and the parent, casts the sequencer to the declared p_sequencer type, and only then runs the hooks and body.

    Build it: a p_sequencer flavour of dma_vseq

    The bench's dma_vseq keeps its sequencer handles in its own fields, set by the test. A virtual-sequencer flavour can reuse every line of it: declare p_sequencer, copy the handles out of the virtual sequencer at the top of body, and call super.body().

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    // snippet
    class dma_p_vseq extends dma_vseq;
      `uvm_object_utils(dma_p_vseq)
      `uvm_declare_p_sequencer(dma_vsqr2)
    
      function new(string name = "dma_p_vseq");
        super.new(name);
      endfunction
    
      task body();
        apb_sqr  = p_sequencer.apb_sqr;          // copy the handles, then run the job
        axis_sqr = p_sequencer.axis_sqr;
        irq_vif  = p_sequencer.irq_vif;
        super.body();
      endtask
    endclass
    
    // In the test's run_phase
    dma_vsqr_env venv;
    if (!$cast(venv, env)) `uvm_fatal("CAST", "env is not a dma_vsqr_env")
    vseq.start(venv.v_sqr);

    Two details are worth saying out loud in an interview. First, the test needs a $cast of its own: env is declared as dma_env, and only the derived class has v_sqr. Second, the sub-sequences inside dma_vseq still start on real sequencers (s.start(apb_sqr, this)). Starting an APB sequence on p_sequencer itself would put APB items on a sequencer with no driver, and they would wait forever.

    The parent argument does more than naming

    start(sequencer, parent_sequence, this_priority, call_pre_post) takes the parent as its second argument, and dma_vseq always passes this. Three things depend on it:

    • Names and reporting. The child's full name becomes vseq.isr, which is how you find it in a log and how config_db paths for sequences are formed.

    • Lock inheritance. A sequencer that is locked or grabbed lets through the lock holder and its descendants, found by walking the parent chain. A child started without a parent is a stranger to the lock, and waits. lock vs grab shows the deadlock this causes, with a real log.

    • Priority inheritance. this_priority defaults to -1, which means use the parent's priority, or 100 if there is no parent. Priority matters only in the weighted and strict arbitration modes.

    Watch it run

    With the types right, the scratch run from the previous lesson is the output (dma_vsqr_test, +UVM_VERBOSITY=UVM_MEDIUM):

    Diagram
    UVM_INFO dma_tb.sv(572) @ 365000: uvm_test_top.env.apb.sqr@@vseq.isr [IRQ] handler has the APB sequencer
    UVM_INFO dma_tb.sv(623) @ 545000: uvm_test_top.env.v_sqr@@vseq [VSEQ] job done: STATUS=00000002 after 4 polls
    UVM_INFO dma_tb.sv(411) @ 645000: uvm_test_top.env.sb [SB] 6 words matched, 0 mismatched

    The line numbers 567 and 618 point inside dma_vseq: dma_p_vseq only added the handle copy and reused the parent's body.

    What breaks: started on the wrong sequencer

    Here is the opening fatal, reproduced in a scratch run of the bench. A test declares a sequence with ` uvm_declare_p_sequencer(dma_vsqr) `` and, by mistake, starts it on the APB agent's sequencer:

    design.sv
    // bad-example
    psqr_vseq v = psqr_vseq::type_id::create("v");
    v.start(env.apb.sqr);          // wrong: an apb_sequencer is not a dma_vsqr
    Diagram
    UVM_FATAL dma_tb_scratch.sv(982) @ 0: uvm_test_top.env.apb.sqr@@v [DCLPSQ] dma_pkg.psqr_vseq.m_set_p_sequencer.unnamedblk1 uvm_test_top.env.apb.sqr.v Error casting p_sequencer, please verify that this sequence/sequence item is intended to execute on this type of sequencer
    UVM_FATAL :    1

    Read it left to right: the time is 0, the reporter is the sequencer it was started on (env.apb.sqr), and body never printed its first message. The usual causes are starting a virtual sequence on an agent sequencer, a factory override that replaced the virtual sequencer with a class not derived from the declared type, or a sequence copied from another bench with a different virtual sequencer class. Notice the fatal makes a p_sequencer == null check inside body pointless: when the cast fails, the run never reaches it.

    Note

    In IEEE 1800.2 the macro, the DCLPSQ id and the parent and priority rules are the same as in UVM 1.2.

    Interview

    "What's the difference between m_sequencer and p_sequencer?"

    Weak: "p_sequencer is the parent sequencer and m_sequencer is the main one." Both halves are wrong.

    Strong: "m_sequencer always exists; it's the sequencer the sequence was started on, typed uvm_sequencer_base. p_sequencer exists only if you use uvm_declare_p_sequencer(T): the macro declares a T handle and overrides m_set_p_sequencer to $cast m_sequencer into it when start() is called. If the sequencer isn't a T, you get a DCLPSQ fatal before body runs. I use p_sequencer to reach members of a virtual sequencer, like the agent sequencer handles, and I always pass this as the parent when starting children so locks and priorities are inherited."

    Follow-ups: What happens if a child is started without the parent while the vseq holds a lock? (it is blocked by the lock and the scenario deadlocks), and Can you use p_sequencer with start(null)? (there is nothing to reach through; use handles set by the test instead).

    Check yourself A factory override replaces dma_vsqr2 with dma_vsqr3 extends dma_vsqr2, which adds a register-model handle. Do sequences declared with ` uvm_declare_p_sequencer(dma_vsqr2) `` still work?

    Yes. A dma_vsqr3 object is a dma_vsqr2, so the cast succeeds. The sequences cannot see the new member through p_sequencer unless they are declared with the derived type, which is a reason to declare the most general type that has what the sequence needs.

    Check yourself Why does a DCLPSQ fatal always appear at the time the sequence starts, never in the middle of body?

    The cast runs in m_set_p_sequencer, which start() calls as soon as it records the sequencer, before pre_start, pre_body and body. A wrong type is caught at the first possible moment.

    Next: with handles in hand, the real design work is the shape of the scenario: what runs in sequence, what runs in parallel, and how a background branch is told to stop. That is Scenario Patterns and Placement.

    Related topics

    • Configuring sequences

    • lock vs grab

    • Virtual sequencer wiring

    Previous

    87 / 280

    Next: Scenario Patterns and Placement

    Up next

    Up nextScenario Patterns and Placement

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • The Coordination Problem
    • Sub-sequences and Sequence Libraries
    • Virtual Sequences Without a Virtual Sequencer