Part 5 · Sequences · Intermediate

p_sequencer Declare: uvm_declare_p_sequencer & dma_xfer_vseq

Strongly typed p_sequencer access, macro usage, full dma_xfer_vseq walkthrough, and sub-sequence delegation.

Why uvm_declare_p_sequencer

Inside a sequence body(), p_sequencer is the handle to the sequencer the sequence was started on. For virtual sequences, that must be the soc_virtual_sequencer type — not generic uvm_sequencer_base — so you can access p_sequencer.apb_sqr without casting.

systemverilog
class dma_xfer_vseq extends uvm_sequence;
  // [VSEQ] Declares p_sequencer as soc_virtual_sequencer type
  `uvm_declare_p_sequencer(soc_virtual_sequencer)

  rand bit [31:0] src_addr, dst_addr;
  rand int unsigned len;

  `uvm_object_utils(dma_xfer_vseq)

  function new(string name = "dma_xfer_vseq");
    super.new(name);
  endfunction
endclass
diagram
[SEQ] without vs with declare macro

  WITHOUT `uvm_declare_p_sequencer:
    p_sequencer is uvm_sequencer_base
    p_sequencer.apb_sqr   compile error (no such member)

  WITH `uvm_declare_p_sequencer(soc_virtual_sequencer):
    p_sequencer typed as soc_virtual_sequencer
    p_sequencer.apb_sqr   apb_sequencer handle  

Full dma_xfer_vseq body — step by step

The virtual sequence creates sub-sequences, passes scenario knobs, and starts them on the appropriate handles from p_sequencer:

systemverilog
task body();
  prog_dma_seq  prog = prog_dma_seq::type_id::create("prog");
  axi_wr_seq    wr   = axi_wr_seq::type_id::create("wr");
  axi_rd_seq    rd   = axi_rd_seq::type_id::create("rd");

  // Step 1: [APB] program DMA registers — sequential
  prog.src_addr = src_addr;
  prog.dst_addr = dst_addr;
  prog.len      = len;
  prog.start(p_sequencer.apb_sqr);

  // Step 2: [AXI] parallel read/write while DMA active
  fork
    begin
      wr.addr = src_addr;
      wr.num_beats = len;
      wr.start(p_sequencer.axi_sqr);
    end
    begin
      rd.addr = dst_addr;
      rd.num_beats = len;
      rd.start(p_sequencer.axi_sqr);
    end
  join

  // Step 3: [VSEQ] virtual seq never calls start_item — only sub-sequences do
endtask

Note: prog.start uses p_sequencer.apb_sqr — the real APB sequencer — not p_sequencer itself. The virtual sequencer is the launch point; sub-sequences run on protocol sequencers.


Delegation flow diagram

diagram
[VSEQ] start() delegation chain

  TEST
    vseq.start(env.v_sqr)
         │
         ▼
  dma_xfer_vseq running ON soc_virtual_sequencer
    p_sequencer == env.v_sqr
         │
         ├── prog.start(p_sequencer.apb_sqr)
         │        │
         │        ▼
         │   prog_dma_seq ON apb_agent.sequencer
         │        start_item/finish_item  apb_driver  DUT
         │
         └── wr.start(p_sequencer.axi_sqr)
                  │
                  ▼
             axi_wr_seq ON axi_agent.sequencer
                  start_item/finish_item  axi_driver  DUT

Rules for p_sequencer usage

  • Virtual sequence starts on v_sqr — test calls vseq.start(env.v_sqr).

  • Sub-sequences start on p_sequencer.<agent>_sqr — real sequencers.

  • Virtual sequence body() never calls start_item — no driver on v_sqr.

  • Sub-sequences may use their own p_sequencer (typed to apb_sequencer etc.).


Sub-sequence vs virtual sequence p_sequencer

Each sub-sequence has its own p_sequencer type. When prog_dma_seq runs on apb_sqr, its p_sequencer is the APB sequencer — declared with `uvm_declare_p_sequencer(apb_sequencer). The virtual sequence's p_sequencer and the sub-sequence's p_sequencer are different objects — no conflict.

diagram
[SEQ] two p_sequencer contexts — no collision

  dma_xfer_vseq.p_sequencer      soc_virtual_sequencer (env.v_sqr)
  prog_dma_seq.p_sequencer       apb_sequencer (apb_agent.sqr)
                                   (set when prog.start(apb_sqr) runs)

  Each sequence sees the sequencer it was started on

Key takeaways

  • uvm_declare_p_sequencer gives strongly typed p_sequencer in body().

  • Virtual seq starts on v_sqr; sub-sequences start on p_sequencer.<agent>_sqr.

  • Never start_item in a virtual sequence — delegate to sub-sequences.

Common pitfalls

  • Missing macro — p_sequencer.apb_sqr compile error or wrong cast.

  • sub_seq.start(p_sequencer) — wrong; start on p_sequencer.apb_sqr.

  • Virtual seq calling start_item — fatal; no driver on virtual sequencer.