UVM Tutorial for Candy Lovers – 14. Field Macros

Last Updated: September 14, 2013

This post will explain how UVM field macros (`uvm_field_*) work. In Transactions and Sequences, we used the UVM field macros to automatically implement the standard data methods, such as copy(), compare(), and pack() for the jelly_bean_transaction.

1
2
3
4
5
6
7
`uvm_object_utils_begin(jelly_bean_transaction)
   `uvm_field_enum(flavor_e, flavor, UVM_ALL_ON)
   `uvm_field_enum(color_e,  color,  UVM_ALL_ON)
   `uvm_field_int (sugar_free,       UVM_ALL_ON)
   `uvm_field_int (sour,             UVM_ALL_ON)
   `uvm_field_enum(taste_e,  taste,  UVM_ALL_ON)
`uvm_object_utils_end

The following pseudo code shows how these field macros are expanded.

Click here to expand the macros.
// Assuming UVM_NO_DEPRECATED is defined.
// Consequently assuming UVM_NO_REGISTERED_CONVERTER is defined.
// Assuming UVM_OBJECT_MUST_HAVE_CONSTRUCTOR is defined.
 
class jelly_bean_transaction extends uvm_sequence_item;
 
   // `uvm_object_utils_begin(T) is expanded as follows
   //  |
   //  +--> `m_uvm_object_registry_internal(T,T)
   //  |
   //  V
 
   typedef uvm_object_registry#( jelly_bean_transaction,
                                "jelly_bean_transaction" ) type_id;
 
   static function type_id get_type();
     return type_id::get();
   endfunction
 
   virtual function uvm_object_wrapper get_object_type();
     return type_id::get();
   endfunction 
 
   //  |
   //  +--> `m_uvm_object_create_func(T)
   //  |
   //  V
 
   function uvm_object create( string name = "" );
      jelly_bean_transaction tmp;
      if ( name == "" ) tmp = new();
      else              tmp = new( name );
      return tmp;
   endfunction
 
   //  |
   //  +--> `m_uvm_get_type_name_func(T)
   //  |
   //  V
 
   const static string type_name = "jelly_bean_transaction";
 
   virtual function string get_type_name();
     return type_name;
   endfunction 
 
   //  |
   //  +--> `uvm_field_utils_begin(T) 
 
   function void __m_uvm_field_automation( uvm_object tmp_data__,                                           int what__,                                           string str__ );      begin
         jelly_bean_transaction local_data__; // Used for copy and compare
         typedef jelly_bean_transaction ___local_type____;
         string string_aa_key; // Used for associative array lookups
         uvm_object __current_scopes[$];
         if ( what__ inside { UVM_SETINT, UVM_SETSTR, UVM_SETOBJ } ) begin
            if ( __m_uvm_status_container.m_do_cycle_check( this ) ) begin
               return;
            end else
              __current_scopes=__m_uvm_status_container.m_uvm_cycle_scopes;
         end
         super.__m_uvm_field_automation( tmp_data__, what__, str__ );
 
         // Type is verified by uvm_object::compare()
 
         if ( tmp_data__ != null )
 
           // Allow objects in same hierarchy to be copied/compared
 
           if ( ! $cast( local_data__, tmp_data__ ) ) return;
 
         // `uvm_field_enum(flavor_e, flavor, UVM_ALL_ON) is expanded as follows
         //  |
         //  V
 
         begin
            case ( what__ )              UVM_CHECK_FIELDS:                __m_uvm_status_container.do_field_check( "flavor", this );              UVM_COPY: begin                 if ( local_data__ == null ) return;                 if ( ! ( UVM_ALL_ON & UVM_NOCOPY ) )                   flavor = local_data__.flavor;              end              UVM_COMPARE: begin                 if ( local_data__ == null ) return;                 if ( ! ( UVM_ALL_ON & UVM_NOCOMPARE ) ) begin                    if ( flavor !== local_data__.flavor ) begin                       __m_uvm_status_container.scope.set_arg( "flavor" );                       $swrite( __m_uvm_status_container.stringv,                                 "lhs = %0s : rhs = %0s",                                flavor.name(), local_data__.flavor.name() );                       __m_uvm_status_container.comparer.print_msg                         ( __m_uvm_status_container.stringv );                       if ( __m_uvm_status_container.comparer.result &&                            ( __m_uvm_status_container.comparer.show_max < =                              __m_uvm_status_container.comparer.result ) )                         return;                    end                 end              end              UVM_PACK:                if ( ! ( UVM_ALL_ON & UVM_NOPACK ) ) begin                   __m_uvm_status_container.packer.pack_field                     ( flavor, $bits( flavor ) );                end              UVM_UNPACK:                if ( ! ( UVM_ALL_ON & UVM_NOPACK ) ) begin                   flavor = flavor_e'                            ( __m_uvm_status_container.packer.unpack_field_int                              ( $bits( flavor ) ) );                end              UVM_RECORD:                `m_uvm_record_string( flavor, flavor.name(), UVM_ALL_ON )              UVM_PRINT:                if ( ! ( UVM_ALL_ON & UVM_NOPRINT ) ) begin                   __m_uvm_status_container.printer.print_generic                     ( "flavor", "flavor_e", $bits( flavor ), flavor.name() );                end              UVM_SETINT: begin                 __m_uvm_status_container.scope.set_arg( "flavor" );                 if ( uvm_is_match( str__,                                     __m_uvm_status_container.scope.get())) begin                    if ( UVM_ALL_ON & UVM_READONLY ) begin                       uvm_report_warning                         ( "RDONLY",                            $sformatf( "Readonly argument match %s is ignored",                                      __m_uvm_status_container.get_full_scope_arg()),                           UVM_NONE);                    end else begin                       if ( __m_uvm_status_container.print_matches )                         uvm_report_info                           ( "STRMTC",                              { "set_int()", ": Matched string ", str__,                               " to field ",                               __m_uvm_status_container.get_full_scope_arg() },                             UVM_LOW );                       flavor = flavor_e'                                ( uvm_object::__m_uvm_status_container.bitstream );                       __m_uvm_status_container.status = 1;                    end // else: !if( UVM_ALL_ON & UVM_READONLY )                 end // if ( uvm_is_match( str__,...              end // case: UVM_SETINT            endcase // case ( what__ )         end
 
         // `uvm_field_enum(color_e, color, UVM_ALL_ON) is expanded as follows
         //  |
         //  V
 
         begin
            case ( what__ )
              UVM_CHECK_FIELDS:
                __m_uvm_status_container.do_field_check( "color", this );
              UVM_COPY: begin
                 if ( local_data__ == null ) return;
                 if ( ! ( UVM_ALL_ON & UVM_NOCOPY ) )
                   color = local_data__.color;
              end
              UVM_COMPARE: begin
                 if ( local_data__ == null ) return;
                 if ( ! ( UVM_ALL_ON & UVM_NOCOMPARE ) ) begin
                    if ( color !== local_data__.color ) begin
                       __m_uvm_status_container.scope.set_arg( "color" );
                       $swrite( __m_uvm_status_container.stringv, 
                                "lhs = %0s : rhs = %0s",
                                color.name(), local_data__.color.name() );
                       __m_uvm_status_container.comparer.print_msg
                         ( __m_uvm_status_container.stringv );
                       if ( __m_uvm_status_container.comparer.result &&
                            ( __m_uvm_status_container.comparer.show_max <=
                              __m_uvm_status_container.comparer.result ) )
                         return;
                    end
                 end
              end
              UVM_PACK:
                if ( ! ( UVM_ALL_ON & UVM_NOPACK ) ) begin
                   __m_uvm_status_container.packer.pack_field
                     ( color, $bits( color ) );
                end
              UVM_UNPACK:
                if ( ! ( UVM_ALL_ON & UVM_NOPACK ) ) begin
                   color = color_e'
                           ( __m_uvm_status_container.packer.unpack_field_int
                             ( $bits( color ) ) );
                end
              UVM_RECORD:
                `m_uvm_record_string( color, color.name(), UVM_ALL_ON )
              UVM_PRINT:
                if ( ! ( UVM_ALL_ON & UVM_NOPRINT ) ) begin
                   __m_uvm_status_container.printer.print_generic
                     ( "color", "color_e", $bits( color ), color.name() );
                end
              UVM_SETINT: begin
                 __m_uvm_status_container.scope.set_arg( "color" );
                 if ( uvm_is_match( str__, 
                                    __m_uvm_status_container.scope.get())) begin
                    if ( UVM_ALL_ON & UVM_READONLY ) begin
                       uvm_report_warning
                         ( "RDONLY", 
                           $sformatf( "Readonly argument match %s is ignored",
                                      __m_uvm_status_container.get_full_scope_arg()),
                           UVM_NONE);
                    end else begin
                       if ( __m_uvm_status_container.print_matches )
                         uvm_report_info
                           ( "STRMTC", 
                             { "set_int()", ": Matched string ", str__,
                               " to field ",
                               __m_uvm_status_container.get_full_scope_arg() },
                             UVM_LOW );
                       color = color_e'
                               ( uvm_object::__m_uvm_status_container.bitstream );
                       __m_uvm_status_container.status = 1;
                    end // else: !if( UVM_ALL_ON & UVM_READONLY )
                 end // if ( uvm_is_match( str__,...
              end // case: UVM_SETINT
            endcase // case ( what__ )
         end
 
         // `uvm_field_int(sugar_free, UVM_ALL_ON) is expanded as follows
         //  |
         //  V
 
         begin
            case ( what__ )
              UVM_CHECK_FIELDS: begin
                 __m_uvm_status_container.do_field_check( "sugar_free", this );
              end
              UVM_COPY: begin
                 if ( local_data__ == null ) return;
                 if ( !( UVM_ALL_ON & UVM_NOCOPY ) ) 
                   sugar_free = local_data__.sugar_free;
              end
              UVM_COMPARE: begin
                 if ( local_data__ == null ) return;
                 if ( ! ( UVM_ALL_ON & UVM_NOCOMPARE ) ) begin
                    if ( sugar_free !== local_data__.sugar_free ) begin
                       void'( __m_uvm_status_container.comparer.compare_field
                              ( "sugar_free", sugar_free, 
                                local_data__.sugar_free, $bits( sugar_free )));
                       if ( __m_uvm_status_container.comparer.result &&
                            ( __m_uvm_status_container.comparer.show_max <=
                              __m_uvm_status_container.comparer.result ) )
                         return;
                    end
                 end // if ( ! ( UVM_ALL_ON & UVM_NOCOMPARE ) )
              end // case: UVM_COMPARE
              UVM_PACK:
                if ( !( UVM_ALL_ON & UVM_NOPACK ) ) begin
                   if ( $bits( sugar_free ) <= 64 )
                     __m_uvm_status_container.packer.pack_field_int
                       ( sugar_free, $bits( sugar_free ) );
                   else
                     __m_uvm_status_container.packer.pack_field
                       ( sugar_free, $bits( sugar_free ) );
                end
              UVM_UNPACK:
                if ( !( UVM_ALL_ON & UVM_NOPACK ) ) begin
                   if ( $bits( sugar_free ) <= 64 )
                     sugar_free = __m_uvm_status_container.packer.unpack_field_int
                                  ( $bits( sugar_free ) );
                   else
                     sugar_free = __m_uvm_status_container.packer.unpack_field
                                  ( $bits( sugar_free ) );
                end
              UVM_RECORD:
                `m_uvm_record_int( sugar_free, UVM_ALL_ON )
              UVM_PRINT:
                if ( !( UVM_ALL_ON & UVM_NOPRINT ) ) begin
                   __m_uvm_status_container.printer.print_int
                     ( "sugar_free", sugar_free, $bits( sugar_free ),
                       uvm_radix_enum'( UVM_ALL_ON & UVM_RADIX ) );
                end
              UVM_SETINT: begin
                 bit matched;
                 __m_uvm_status_container.scope.set_arg( "sugar_free" );
                 matched = uvm_is_match( str__, 
                                         __m_uvm_status_container.scope.get());
                 if ( matched ) begin
                    if ( UVM_ALL_ON & UVM_READONLY ) begin
                       uvm_report_warning
                         ( "RDONLY", 
                           $sformatf( "Readonly argument match %s is ignored",
                                      __m_uvm_status_container.get_full_scope_arg()),
                           UVM_NONE );
                    end else begin
                       if ( __m_uvm_status_container.print_matches )
                         uvm_report_info
                           ( "STRMTC",
                             { "set_int()", ": Matched string ", str__,
                               " to field ",
                               __m_uvm_status_container.get_full_scope_arg() },
                             UVM_LOW );
                       sugar_free = uvm_object::__m_uvm_status_container.bitstream;
                       uvm_object::__m_uvm_status_container.status = 1;
                    end // else: !if( UVM_ALL_ON & UVM_READONLY )
                 end // if ( matched )
                 __m_uvm_status_container.scope.unset_arg( "sugar_free" );
              end // case: UVM_SETINT
            endcase // case (what__)
         end
 
         // `uvm_field_int(sour, UVM_ALL_ON) is expanded as follows
         //  |
         //  V
 
         begin
            case ( what__ )
              UVM_CHECK_FIELDS: begin
                 __m_uvm_status_container.do_field_check( "sour", this );
              end
              UVM_COPY: begin
                 if ( local_data__ == null ) return;
                 if ( !( UVM_ALL_ON & UVM_NOCOPY ) ) 
                   sour = local_data__.sour;
              end
              UVM_COMPARE: begin
                 if ( local_data__ == null ) return;
                 if ( ! ( UVM_ALL_ON & UVM_NOCOMPARE ) ) begin
                    if ( sour !== local_data__.sour ) begin
                       void'( __m_uvm_status_container.comparer.compare_field
                              ( "sour", sour, 
                                local_data__.sour, $bits( sour )));
                       if ( __m_uvm_status_container.comparer.result &&
                            ( __m_uvm_status_container.comparer.show_max <=
                              __m_uvm_status_container.comparer.result ) )
                         return;
                    end
                 end // if ( ! ( UVM_ALL_ON & UVM_NOCOMPARE ) )
              end // case: UVM_COMPARE
              UVM_PACK:
                if ( !( UVM_ALL_ON & UVM_NOPACK ) ) begin
                   if ( $bits( sour ) <= 64 )
                     __m_uvm_status_container.packer.pack_field_int
                       ( sour, $bits( sour ) );
                   else
                     __m_uvm_status_container.packer.pack_field
                       ( sour, $bits( sour ) );
                end
              UVM_UNPACK:
                if ( !( UVM_ALL_ON & UVM_NOPACK ) ) begin
                   if ( $bits( sour ) <= 64 )
                     sour = __m_uvm_status_container.packer.unpack_field_int
                            ( $bits( sour ) );
                   else
                     sour = __m_uvm_status_container.packer.unpack_field
                            ( $bits( sour ) );
                end
              UVM_RECORD:
                `m_uvm_record_int( sour, UVM_ALL_ON )
              UVM_PRINT:
                if ( !( UVM_ALL_ON & UVM_NOPRINT ) ) begin
                   __m_uvm_status_container.printer.print_int
                     ( "sour", sour, $bits( sour ),
                       uvm_radix_enum'( UVM_ALL_ON & UVM_RADIX ) );
                end
              UVM_SETINT: begin
                 bit matched;
                 __m_uvm_status_container.scope.set_arg( "sour" );
                 matched = uvm_is_match( str__, 
                                         __m_uvm_status_container.scope.get());
                 if ( matched ) begin
                    if ( UVM_ALL_ON & UVM_READONLY ) begin
                       uvm_report_warning
                         ( "RDONLY", 
                           $sformatf( "Readonly argument match %s is ignored",
                                      __m_uvm_status_container.get_full_scope_arg()),
                           UVM_NONE );
                    end else begin
                       if ( __m_uvm_status_container.print_matches )
                         uvm_report_info
                           ( "STRMTC",
                             { "set_int()", ": Matched string ", str__,
                               " to field ",
                               __m_uvm_status_container.get_full_scope_arg() },
                             UVM_LOW );
                       sour = uvm_object::__m_uvm_status_container.bitstream;
                       uvm_object::__m_uvm_status_container.status = 1;
                    end // else: !if( UVM_ALL_ON & UVM_READONLY )
                 end // if ( matched )
                 __m_uvm_status_container.scope.unset_arg( "sour" );
              end // case: UVM_SETINT
            endcase // case (what__)
         end
 
         // `uvm_field_enum(taste_e, taste, UVM_ALL_ON) is expanded as follows
         //  |
         //  V
 
         begin
            case ( what__ )
              UVM_CHECK_FIELDS:
                __m_uvm_status_container.do_field_check( "taste", this );
              UVM_COPY: begin
                 if ( local_data__ == null ) return;
                 if ( ! ( UVM_ALL_ON & UVM_NOCOPY ) )
                   taste = local_data__.taste;
              end
              UVM_COMPARE: begin
                 if ( local_data__ == null ) return;
                 if ( ! ( UVM_ALL_ON & UVM_NOCOMPARE ) ) begin
                    if ( taste !== local_data__.taste ) begin
                       __m_uvm_status_container.scope.set_arg( "taste" );
                       $swrite( __m_uvm_status_container.stringv, 
                                "lhs = %0s : rhs = %0s",
                                taste.name(), local_data__.taste.name() );
                       __m_uvm_status_container.comparer.print_msg
                         ( __m_uvm_status_container.stringv );
                       if ( __m_uvm_status_container.comparer.result &&
                            ( __m_uvm_status_container.comparer.show_max <=
                              __m_uvm_status_container.comparer.result ) )
                         return;
                    end
                 end
              end
              UVM_PACK:
                if ( ! ( UVM_ALL_ON & UVM_NOPACK ) ) begin
                   __m_uvm_status_container.packer.pack_field
                     ( taste, $bits( taste ) );
                end
              UVM_UNPACK:
                if ( ! ( UVM_ALL_ON & UVM_NOPACK ) ) begin
                   taste = taste_e'
                           ( __m_uvm_status_container.packer.unpack_field_int
                             ( $bits( taste ) ) );
                end
              UVM_RECORD:
                `m_uvm_record_string( taste, taste.name(), UVM_ALL_ON )
              UVM_PRINT:
                if ( ! ( UVM_ALL_ON & UVM_NOPRINT ) ) begin
                   __m_uvm_status_container.printer.print_generic
                     ( "taste", "taste_e", $bits( taste ), taste.name() );
                end
              UVM_SETINT: begin
                 __m_uvm_status_container.scope.set_arg( "taste" );
                 if ( uvm_is_match( str__, 
                                    __m_uvm_status_container.scope.get())) begin
                    if ( UVM_ALL_ON & UVM_READONLY ) begin
                       uvm_report_warning
                         ( "RDONLY", 
                           $sformatf( "Readonly argument match %s is ignored",
                                      __m_uvm_status_container.get_full_scope_arg()),
                           UVM_NONE);
                    end else begin
                       if ( __m_uvm_status_container.print_matches )
                         uvm_report_info
                           ( "STRMTC", 
                             { "set_int()", ": Matched string ", str__,
                               " to field ",
                               __m_uvm_status_container.get_full_scope_arg() },
                             UVM_LOW );
                       taste = taste_e'
                               ( uvm_object::__m_uvm_status_container.bitstream );
                       __m_uvm_status_container.status = 1;
                    end // else: !if( UVM_ALL_ON & UVM_READONLY )
                 end // if ( uvm_is_match( str__,...
              end // case: UVM_SETINT
            endcase // case ( what__ )
         end
 
         // `uvm_object_utils_end is expanded as follows
         //  |
         //  V
 
      end
   endfunction // __m_uvm_field_automation
endclass: jelly_bean_transaction

Wow! What a long code! Each field macro was expanded to about 80 lines of code. You don’t need to fully understand the code, but basically the code does the followings:

  • The `uvm_object_utils_begin() macro creates a virtual function called __m_uvm_field_automation (the first highlighted block of code in yellow).
  • Each `uvm_field_* macro creates a case statement (the second highlighted block) and performs the functionality of copy, compare, and pack, depending on the value of the what__ argument passed to the __m_uvm_field_automation() function.

The __m_uvm_field_automation() is then used in uvm_object class. As you see the following diagram, the uvm_object::copy() calls the __m_uvm_field_automation() with UVM_COPY as the value of the what__. Similarly uvm_object::compare() calls the __m_uvm_field_automation() with UVM_COMPARE.

Some Standard Data Methods of the uvm_object Class
By now you might think that these field macros are convenient but not efficient. For more efficient and more flexible implementation, we can use user definable do_*() hooks. As shown above the uvm_object calls the do_*() hook after calling the __m_uvm_field_automation(). For example, the uvm_object::copy() calls the do_copy() after calling the __m_uvm_field_automation(), and the uvm_object::compare() calls the do_compare() after calling the __m_uvm_field_automation(). By default these do_*() methods are empty. I will explain more detail about the do_*() methods in the next post. If no field macros are used, the __m_uvm_field_automation() does almost nothing (only the code between the two highlighted blocks will remain).

I hope this post helped you to understand the field macros.

14 thoughts on “UVM Tutorial for Candy Lovers – 14. Field Macros”

  1. Hi I have started working on UVM a fresh.
    I would like to know why do we require this feild macros. I mean, when we say copy, what are we copying ? If you see the following example, cmd, addr, data are the members of class my_transaction. Then what is the need to declare an object of my_transaction ‘tx’ and cast it to the already exixting uvm object and then copy them to members of the class ?
    Basically I want to understand:
    1) Why do we use this macros like copy, compare ? What if we not using them ?
    2) Is casting operation doing a cast with UVM_OBJECT ? what ar we ensuring from this ?
    3) When we use cmd = tx.cmd, are we actually copying the handles to members of my_transaction class ?

    I thank you in advance.

      class my_transaction extends uvm_sequence_item;
      
        `uvm_object_utils(my_transaction)
      
        rand bit cmd;
        rand int addr;
        rand int data;
      
        constraint c_addr { addr >= 0; addr = 0; data < 256; }
        
        function new (string name = "");
          super.new(name);
        endfunction
        
        function string convert2string;
          return $sformatf("cmd=%b, addr=%0d, data=%0d", cmd, addr, data);
        endfunction
    
        function void do_copy(uvm_object rhs);
          my_transaction tx;
          $cast(tx, rhs);
          cmd  = tx.cmd;
          addr = tx.addr;
          data = tx.data;
        endfunction
        
        function bit do_compare(uvm_object rhs, uvm_comparer comparer);
          my_transaction tx;
          bit status = 1;
          $cast(tx, rhs);
          status &= (cmd  == tx.cmd);
          status &= (addr == tx.addr);
          status &= (data == tx.data);
          return status;
        endfunction
    
      endclass: my_transaction
    
    1. First of all, using the field macros such as `uvm_field_enum and `uvm_field_int is not recommended because of the overhead I mentioned in the article. It is recommended to use the do_* hooks such as do_copy and do_compare like you did in the my_transaction.

      (1) Why use do_copy() ?
      Let’s assume you have two instances of the my_transaction.

      my_transaction tr0 = new( "tr0" );
      my_transaction tr1 = new( "tr1" );
       
      tr0.cmd  = 0;
      tr0.addr = 0;
      tr0.data = 0;
       
      tr1.cmd  = 1;
      tr1.addr = 1;
      tr1.data = 1;

      Then you copy the tr0 to the tr1 as follows:

      tr1.copy( tr0 );

      The copy() function internally calls the do_copy(), which is empty by default. That means if you don’t define the do_copy(), the properties (cmd, addr, and data) of the my_transaction are not copied. For example:

      $display( "tr1.cmd=%b", tr1.cmd );

      still displays tr1.cmd=1, not tr1.cmd=0.
      On the other hand, if you define the do_copy() like what you did, the properties are copied. For example:

      $display( "tr1.cmd=%b", tr1.cmd );

      displays tr1.cmd=0.

      (2) Why $cast is necessary?

      function void do_copy(uvm_object rhs);
         my_transaction tx;
         $cast(tx, rhs);
         cmd  = tx.cmd;
         addr = tx.addr;
         data = tx.data;
      endfunction

      The type of rhs is uvm_object. The uvm_object does not know the properties such as cmd and addr. The $cast assigns the rhs to the my_transaction type so that you can access the cmd and addr.

      (3) What does cmd = tx.cmd do?
      It copies the value of tx.cmd to this.cmd.

      1. Hi Keisuke Shimizu,

        I found your answers very helpful. I have a question regarding uvm_comparer, in the above code pasted by surya it says function bit do_compare(uvm_object rhs, uvm_comparer comparer);
        -> my question is what does uvm_comparer do as a second argument?

        Regards,
        Imran

        1. The uvm_comparer is a so-called policy class that you can delegate the comparison task to. For example, Surya’s do_compare function can be rewritten using the uvm_comparer as follows:

          virtual function bit do_compare( uvm_object rhs, uvm_comparer comparer );
             my_transaction tx;
             bit status = 1;
             $cast( tx, rhs );
             status &= comparer.compare_field( .name("cmd" ), .lhs(cmd ), .rhs(tx.cmd ), .size( 1) );
             status &= comparer.compare_field( .name("addr"), .lhs(addr), .rhs(tx.addr), .size(32) );
             status &= comparer.compare_field( .name("data"), .lhs(data), .rhs(tx.data), .size(32) );
             return status
          endfunction: do_compare

          However, because of the overhead the uvm_comparer class adds, it is not recommended to use this class.

          1. 1) Can you describe what overhead the uvm_comparer class adds? Did accellera recommend not to use this?

            2) Does .compare() also respect the UVM_NOCOMPARE flag on a `uvm_field_()? (Im trying this now and it doesnt appear to be working.)

          2. 1) The compare_field function I used above executes the following code:

            1
            2
            3
            4
            5
            6
            7
            8
            9
            10
            11
            12
            13
            14
            15
            16
            17
            18
            19
            20
            21
            22
            23
            24
            25
            26
            27
            28
            29
            30
            31
            32
            33
            34
            35
            36
            37
            38
            39
            40
            41
            42
            43
            44
            45
            46
            47
            48
            49
            50
            51
            
              virtual function bit compare_field (string name, 
                                                  uvm_bitstream_t lhs, 
                                                  uvm_bitstream_t rhs, 
                                                  int size,
                                                  uvm_radix_enum radix=UVM_NORADIX); 
                uvm_bitstream_t mask;
                string msg;
             
                if(size < = 64)
                  return compare_field_int(name, lhs, rhs, size, radix);
             
                mask = -1;
                mask >>= (UVM_STREAMBITS-size);
                if((lhs & mask) !== (rhs & mask)) begin
                  uvm_object::__m_uvm_status_container.scope.set_arg(name);
                  case (radix)
                    UVM_BIN: begin
                          $swrite(msg, "lhs = 'b%0b : rhs = 'b%0b", 
                                   lhs&mask, rhs&mask);
                         end
                    UVM_OCT: begin
                          $swrite(msg, "lhs = 'o%0o : rhs = 'o%0o", 
                                   lhs&mask, rhs&mask);
                         end
                    UVM_DEC: begin
                          $swrite(msg, "lhs = %0d : rhs = %0d", 
                                   lhs&mask, rhs&mask);
                         end
                    UVM_TIME: begin
                        $swrite(msg, "lhs = %0t : rhs = %0t", 
                           lhs&mask, rhs&mask);
                    end
                    UVM_STRING: begin
                          $swrite(msg, "lhs = %0s : rhs = %0s", 
                                   lhs&mask, rhs&mask);
                         end
                    UVM_ENUM: begin
                          //Printed as decimal, user should cuse compare string for enum val
                          $swrite(msg, "lhs = %0d : rhs = %0d", 
                                   lhs&mask, rhs&mask);
                          end
                    default: begin
                          $swrite(msg, "lhs = 'h%0x : rhs = 'h%0x", 
                                   lhs&mask, rhs&mask);
                         end
                  endcase
                  print_msg(msg);
                  return 0;
                end
                return 1;
              endfunction

            This is the overhead I mentioned. You can do the equivalent comparison without using the uvm_comparer like this:

            status &= ( cmd === tx.cmd );

            Accellera did not comment on this, though.

            2) The compare function calls both __m_uvm_field_automation and do_compare functions internally. The __m_uvm_field_automation does not compare the field if the UVM_NOCOMPARE flag is set. But if you have your own do_compare (and it compares the field), the field is still compared because the do_compare is not aware of the UVM_NOCOMPARE flag.

    1. The compare() function internally calls do_compare(), which is empty by default. The do_compare() defines the strategy of comparison. In your my_transaction, you compare the cmd, addr, and data. By doing this, you defined two objects being equal if the values of cmd, addr, and data are equal in the two objects.

  2. Shimizu,

    I am very much happy to receive your answers. Thank You Very much…!
    Your website is fabulous and very informative and I am trying all sort of nonsence in UVM 🙂

    All the best..!

  3. Hello Keisuke,

    Your articles are really helpful,

    Can u explain the exact usage of pack and unpack macros how they work and when do we need to write our own do_pack and do_unpack?

    Also can u explain the concept of big_endian?

    I will be really thankful to you!!

    Regards
    Shreemant

    1. The pack methods are used to convert object’s properties into an array of bits, bytes, or ints. The unpack methods convert back the array into the properties. You can use the pack method to convert a SystemVerilog object into an array of bits and pass it to a C++ program, for example. Or, you can use the pack method to convert an Ethernet packet into a bit-stream representation.
      To pack/unpack an object, you need to specify which properties of the object to pack/unpack. You can either use `uvm_field macros (which are not recommended due to an overhead) or write your own do_pack and do_unpack methods. If the bit-stream representation is crucial (for example, if you are creating an Ethernet bit-stream), it is almost certain that you create your own do_pack and do_unpack.
      When you pack a property that requires more than one bit to represent itself, the big_endian property of the uvm_packer determines the order of bits. Please see Pack in little endian and in big endian in this article.

Leave a Reply