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.

No comments:

Post a Comment