Thursday, 29 June 2017

Default Sequence

There are two usual ways to run the sequence:
  • Using default_sequence
  • Using start method

How default_sequence works?

Inside uvm_sequencer_base.svh
---------------------------------------------------------------------------------
function void uvm_sequencer_base::start_phase_sequence(uvm_phase phase);
...
(uvm_config_db #(uvm_object_wrapper)::get(
               this, {phase.get_name(),"_phase"}, "default_sequence", wrapper) && wrapper != null)
...
--------------------------------------------------------------------------------------

This API will do the following:
  • get default_sequence 
  • cast this uvm_object_wrapper into uvm_sequence_base
  • start the sequence

Who calls this above method?

Inside uvm_task_phase.svh:
--------------------------------------------------------------------------------------
  virtual function void execute(uvm_component comp,
                                                 uvm_phase phase);
...
      if ($cast(seqr,comp))
          seqr.start_phase_sequence(phase);
...
--------------------------------------------------------------------------------------
execute method is called for all phases of component.

And if the component is Sequencer it will call start_phase_sequence() method, to check if the default_sequence has been set for this method.

If we run sequence using both the approach:
Registering default sequence in main phase of sequencer and starting another sequence in main phase of testcase, default sequence will be executed first as main phase is called in bottom-up hierarchy.

Tuesday, 27 June 2017

Do we need p_sequencer?

You need p_sequencer only if you want to access the property of user defined sequencer.

Let us understand this with example.
User defined sequencer:
--------------------------------------------------------------------------
class user_seqr extends uvm_sequencer;
  int agent_id = 1;
 
endclass: user_seqr
-------------------------------------------------------------------------

Trying to access the property of user_seqr using m_sequencer:
--------------------------------------------------------------------------
class user_seq extends uvm_sequence

  $display(“Agent ID: %0d”, m_sequnencer.agent_id);

endcalss: user_seq
--------------------------------------------------------------------------
Output:
Error-[MFNF] Member not found
my_seq.sv, 20
"this.m_sequencer."
  Could not find member 'agent_id' in class 'uvm_sequencer_base'


Trying same with p_sequencer:
--------------------------------------------------------------------------
class user_seq extends uvm_sequence

`uvm_declare_p_sequencer(user_seqr)

  $display(“Agent ID: %0d”, p_sequnencer.agent_id);

endcalss: user_seq
--------------------------------------------------------------------------
Output:
Agent ID: 1

Why this difference?


So this difference can be explained with OOP example:
--------------------------------------------------------------------------
class parent;
endclass: parent
class child extends parent;

  virtual function info();
   $display("Child::info");
  endfunction: info

endclass: child

module tb_top();
  parent p;
  child  c1;
  child  c2;

  initial
  begin
    c1 = new();
    p = c1;
    p.info();
  end
endmodule: top
--------------------------------------------------------------------------
Output:
Error-[MFNF] Member not found
tb_top.sv, 39
"p."
  Could not find member 'info' in class 'parent'


[We can access the property of child class if parent class handle is pointing to child class,ut only condition is that property definition must be there inside parent class.]

Same case is here for m_sequencer and p_sequencer, where m_seuqencer is handle of uvm_sequencer_base class and p_sequecner is handle of user_sequencer.

So when we call,

  `uvm_declare_p_sequecner(user_seqeucner)

UVM takes the handle of user_sequencer called p_sequencer and do the casting of m_sequencer into p_sequencer.

Same way we can do in our polymorphism example, we will just modify our tb_top file
--------------------------------------------------------------------------

module tb_top();
  parent p;
  child  c1;
  child  c2;

 
initial
  begin
    c1 = new();
    p = c1;
    $cast(c2, p);
    c2.info();

  end
endmodule: top

--------------------------------------------------------------------------
Output:
Child::info


So difference between m_sequencer and p_sequencer is the difference between p and c2 in our example.

If we map our example p_sequencer and m_sequencer:
parent is uvm_sequencer_base and its handle p is m_sequencer.
child is user_sequencer and its handle c2 is p_sequencer.

Wednesday, 14 June 2017

run_test() and UVM_TESTNAME

Three are two ways to provide testcase name:
  1. In top module, run_test(<testcase_name>)
  2. Command line argument, UVM_TESTNAME=<testcase_name>
So here we're calling run_test inside top module. Now this run_test is implemented inside the uvm_globals.svh. This method is not defined in any class, this is a method defined in package.

Inside uvm_globals.svh :
--------------------------------------------------------------------------------
task run_test (string test_name="");
  uvm_root top;
  top = uvm_root::get();
  top.run_test(test_name);
endtask
--------------------------------------------------------------------------------
This run_test() is calling run_test of uvm_root and here name of testcase is getting stored inside "test_name" variable.

Implementation of command-line UVM_TESTNAME is also done inside run_test() of uvm_root.

Inside uvm_root file:
--------------------------------------------------------------------------------
task uvm_root::run_test(string test_name="");
...
  test_name_count = clp.get_arg_values("+UVM_TESTNAME=", test_names);

  // If at least one, use first in queue.
  if (test_name_count > 0) begin
    test_name = test_names[0];
    testname_plusarg = 1;
  end
...
endtask
--------------------------------------------------------------------------------

So whenever we pass UVM_TESTNAME it will be stored inside the "test_name" variable of uvm_root, which means if we have passed testcase name using run_test(), it will be overwritten by testcase name passed through UVM_TESTNAME.

Example:
--------------------------------------------------------------------------------
module top();
...
initial begin
run_test("my_test");
end
endmodule: top
--------------------------------------------------------------------------------

Testname passed through command line argument:
--------------------------------------------------------------------------------
./simv +UVM_TESTNAME=user_test
--------------------------------------------------------------------------------

If we have passed testcase name using both the above methods, user_test will be run as it is passed through command line argument.

PS:: Not only run_test() but global_stop_request(), set_global_timeout() [and many more] are also implemented inside uvm_globals.svh