Tuesday, December 17, 2013

how the sequence is run on sequencer

Nothing new in this post but just wanted to put together what i was going through yday about the sequences/sequencers and how they are started:

so we will have a sequence written something like

class random_seq extends uvm_sequence;
  `uvm_object_utils(random_seq)
::::
::::
endclass:random_seq

and in the test we will start the sequence by using uvm_config_db set method, something like:

configuration by type:
uvm_config_db#(uvm_object_wrapper)::set(this, "m_tb.m_agent.m_sequencer.main_phase", "default_sequence", random_seq::get_type());

or something like:

Configuration by instance:
random_seq m_rand_seq_inst;
m_rand_seq_inst = new("m_rand_seq_inst");
uvm_config_db#(uvm_sequence_base)::set(this, "m_tb.m_agent.m_sequencer.main_phase", "default_sequence", m_rand_seq_inst);

above setting will call the following task from the uvm_sequencer_base class and it is called in " protected virtual function void execute" function of uvm_task_phase class.

function void uvm_sequencer_base::start_phase_sequence(uvm_phase phase);
    uvm_object_wrapper wrapper;
  .......
   seq.start(this);
  ......
endfunction

protected virtual function void execute(uvm_component comp,
                                          uvm_phase phase);

...............
    if ($cast(seqr,comp))   // Only if the component is sequencer then the start_phase_sequence is called 
                                       // otherwise its ignored. 
          seqr.start_phase_sequence(phase);
...............
endfunction

the execute function is called inside
 function void m_traverse(uvm_component comp,
                           uvm_phase phase,
                           uvm_phase_state state);

 case (state)
 ....
   UVM_PHASE_EXECUTING: begin
....
   ph.execute(comp, phase);
....
 end
 ....
 endcase

endfunction

m_tranverse is called in traverse function of uvm_task_phase

  virtual function void traverse(uvm_component comp,
                                 uvm_phase phase,
                                 uvm_phase_state state);
    phase.m_num_procs_not_yet_returned = 0;
    m_traverse(comp, phase, state);
  endfunction


which is called in uvm_phase s task

task uvm_phase::execute_phase();
......
  m_state = UVM_PHASE_STARTED;
    m_imp.traverse(top,this,UVM_PHASE_STARTED);
    #0; // LET ANY WAITERS WAKE UP
.......
endtask

and the execute+phase is called as a part of m_run_phases task of uvm_phase

 task uvm_phase::m_run_phases();
  uvm_root top = uvm_root::get();
 .....
  fork
      begin
        phase.execute_phase();
      end
    join_none
.....
endtask

this m_run_phases task is called in run_test task of uvm_root from which all the tests are run and all the components are instantiated.

task uvm_root::run_test(string test_name="");
................
 // phase runner, isolated from calling process
  fork begin
    // spawn the phase runner task
    phase_runner_proc = process::self();
    uvm_phase::m_run_phases();
  end
  join_none
...............
endtask

and the run_test with which the testcase is run

the explanation for run_test:
  // Phases all components through all registered phases. If the optional
  // test_name argument is provided, or if a command-line plusarg,
  // +UVM_TESTNAME=TEST_NAME, is found, then the specified component is created
  // just prior to phasing. The test may contain new verification components or
  // the entire testbench, in which case the test and testbench can be chosen from
  // the command line without forcing recompilation. If the global (package)
  // variable, finish_on_completion, is set, then $finish is called after
  // phasing completes.

other notes:
uvm_object --> uvm_phase --> uvm_task_phase --> all the  uvm_runtime_phases (reset_phase,configure_phase ....)

uvm_phase has the prototype for all the tasks required while executing a phase and uvm_task_phase is derived from it which has some functionality required for executing the phases (things which are common across the phases) 



Monday, December 9, 2013

why its not good to use defines in uvm code in general -2

Continuing from the previous post

found the following paper written by Adam Erickson of Mentor Graphics

This papers gives an idea about what are the macros that can be used and what are the ones which should be avoided

http://verificationhorizons.verificationacademy.com/volume-7_issue-2/articles/stream/are-ovm-and-uvm-macros-evil-a-cost-benefit-analysis_vh-v7-i2.pdf

Tuesday, November 19, 2013

Second level constraints

Ok let me start with this open-secret i usually forget while doing randomization:

Writing the constraints won't apply them on the random variable, at some point in time ".randomize" must be called for the class instance

There was some confusion about randomization for a class instance declared inside another class, the scenarios was given as below.

class lower;
   rand int y;
endclass : lower

The random variable y will get random value if lower is randomized at some point in time (i.e., .randomize is called for its instance at some time) and it will get some random value as there are no constraints present in lower class.Now consider another class upper  where i have created a handle for lower class and added constraints for it, what should happen when i do "lower_inst.randomize()" in upper class will the constraints be applied? the answer is NO as the constraint is in upper class and it is never randomized, for this to happen you can add a line where u have "this.randomize()" which calls randomization for upper class and the constraint gets applied.

The upper class code looks like below:

class upper;

   rand lower x;
   constraint x_y { x.y == 5; }

   function new();
      x = new();
   endfunction : new

   function void randomize_x();
      this.randomize(x);                   // Randomize called for upper class (with the key word "this")
   endfunction : randomize_x

    function void randomize_xx();
      x.randomize();                       // Randomization called for lower class 
   endfunction : randomize_xx

endclass : upper

program automatic test();
   initial begin

      upper u = new();

      $display("u.x.y=%0d", u.x.y);
      u.randomize_x();
      $display("u.x.y=%0d", u.x.y);
      u.randomize_xx();
      $display("u.x.y=%0d", u.x.y);
      u.x.y = 10;
      $display("u.x.y=%0d", u.x.y);
      u.randomize();
      $display("u.x.y=%0d", u.x.y);

   end
endprogram

Output for the above test is:

u.x.y=0
u.x.y=5                      // output with u.randomize_x()  randomizing the upper class with constraints
u.x.y=-2118898666  // output with u.randomize_xx() randomizing the lower class no constraints
u.x.y=10                    
u.x.y=5                     // output with u.randomize()  randomizing the upper class with constraints





Monday, November 18, 2013

Issue with randc variable declared in a sequence_item.

I ran into an issue some time back and thought it would be good to put it here:

i have declared a randc variable inside a seq_item and used `uvm_do for sending the sequence item across but later i realized the randc wont work the way it should and the reason for the same is, `uvm_do calls for a `uvm_create every time, ans each time new item is getting created and that doesn't have any history of the previous values for the randc variable.

so solved the above issue  with using another macro `uvm_rand_send which is same as uvm_do but without uvm_create (which basically wont create the seq_item every time and history of randc variable will be stored).

Found the following code snippet from synopsys UVM training material later:

class  trans extends uvm_sequence_item;
    randc  bit[3:0]  value;
endclass

virtual  task body()
     `uvm_create_on(req, m_sequencer);
     repeat (n)  begin
        `uvm_rand_send(req);  // Same as uvm_do except no `uvm_create_on
                                                        // randomizes the same object to enable randc
    end
endtask


Monday, November 11, 2013

Why its not good to use defines in uvm code in general

The defines which makes our job much easier which are a part of UVM, they are evil if we consider the lines of code it generates when we expand the macro and the simulation time it takes.

Say for a data class which has 2 variables, m_a,m_b we usually write the data class as below:

class data extends uvm_data_item;
    rand bit m_a;
    rand int m_b;
  
   `uvm_object_utils_begin(data)
       `uvm_field_int(m_a)
       `uvm_field_int(m_b)
   `uvm_object_utils_end

endclass:data

The following two macros do defining of functions like create, and registering with factory (as given below)
   `uvm_object_utils_begin(data)
   `uvm_object_utils_end

And the field macros “uvm_field_*” implements all the functions of data operations such as check_fields,copy, compare, pack, unpack, print, setint. All these functions we may not use always and also if we need to modify any of these functions we will write the custom code in the methods do_copy,do_compare… so these macros just creates a whole lot of code (each time a macro is called it creates a case statement as shown below) which may not be required always (I have used copy, compare most of the times and pack, unpack few times and whenever I have used compare its mostly custom method)

Every time `uvm_field_int is written the entire case statement at <3> is called which is about 100 lines of code, for the two variables m_a, m_b we have in the above data class the macro adds 200 lines of redundant code if the macros are expanded (assuming you are not using any of the built in functions and you are writing custom functions where ever required)  and say if you have 100 variables then it becomes 10000 lines of code if the macro is expanded, which severely affects the simulation performance.


Defines and functions I have used in writing above comment

=============================================================
`define uvm_object_utils_begin(T) \
   `m_uvm_register_converter(T) \
   `m_uvm_object_registry_internal(T,T)  \
   `m_uvm_object_create_func(T) \
   `m_uvm_get_type_name_func(T) \
   `uvm_field_utils_begin(T)     ----------------------------------------------      1
=============================================================
`define uvm_field_utils_begin(T) \ ----------------------------------------------------   1
   function void __m_uvm_field_automation (uvm_object tmp_data__, \
                                     int what__, \
                                     string str__); \ -------------------------------------------------   2
   begin \
     T local_data__; /* Used for copy and compare */ \
     typedef T ___local_type____; \
     string string_aa_key; /* Used for associative array lookups */ \
     /* Type is verified by uvm_object::compare() */ \
     super.__m_uvm_field_automation(tmp_data__, what__, str__); \
     if(tmp_data__ != null) \
       /* Allow objects in same hierarchy to be copied/compared */ \
       if(!$cast(local_data__, tmp_data__)) return;
=============================================================
`define uvm_field_int(ARG,FLAG) \ -----------------------------------------------  3
  case (what__) \
      UVM_CHECK_FIELDS: \
      UVM_COPY: \
        begin \
          if(local_data__ == null) return; \
          if(!((FLAG)&UVM_NOCOPY)) ARG = local_data__.ARG; \
        end \
  UVM_COMPARE: \
.....
  UVM_PACK: \
.......
  UVM_UNPACK: \
   ...... 
  UVM_RECORD: \
...........
  UVM_PRINT: \
...........
  UVM_SETINT: \
.......
=============================================================
`define uvm_object_utils_end \
     end \
   endfunction \
=============================================================
// copy function as in uvm_object.sv
// ----
function void uvm_object::copy (uvm_object rhs);
  //For cycle checking
  static int depth;
  if((rhs !=null)  && (uvm_global_copy_map.get(rhs) != null)) begin
    return;
  end
  if(rhs==null) begin
    uvm_report_warning("NULLCP", "A null object was supplied to copy; copy is ignored", UVM_NONE);
    return;
  end
  uvm_global_copy_map.set(rhs, this); 
  ++depth;
  __m_uvm_field_automation(rhs, UVM_COPY, "");  ---------------------------------- 2
  do_copy(rhs); --------------------------------------------------------------------------------  3
  --depth;
  if(depth==0) begin
    uvm_global_copy_map.clear();
  end
endfunction
=============================================================
// do_copy
// -------
function void uvm_object::do_copy (uvm_object rhs);
  return;
endfunction
=============================================================

1 is where the uvm_field_utils_egin is called as a part of uvm_object_utils_begin
2 is where the function “__m_uvm_field_automation” is called inside copy function.

3 is where user function do_copy is called as a part of copy function after the function call “__m_uvm_field_automation” inside copy function.

NOTE : if you are sure that you are going to use most of the functions as is then using macros reduces a lot of code user needs to write but we need to use these with caution if we are concerned about simulation performance.

Side note: the do_copy method is called in copy function after __m_uvm_field_automation  is called, so if no_copy is not provided as a argument then that function is called initially followed by do_copy, if you implement custom do_copy method we should use NO_COPY argument so that the function __m_uvm_field_automation is not called and only do_copy is called.