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?
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.
// 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()))
endfunctionstart(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 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().
// 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.
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.
With the types right, the scratch run from the previous lesson is the output (dma_vsqr_test, +UVM_VERBOSITY=UVM_MEDIUM):
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 mismatchedThe line numbers 567 and 618 point inside dma_vseq: dma_p_vseq only added the handle copy and reused the parent's body.
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:
// bad-example
psqr_vseq v = psqr_vseq::type_id::create("v");
v.start(env.apb.sqr); // wrong: an apb_sequencer is not a dma_vsqrUVM_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 : 1Read 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.
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.
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.