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.