UVM Tutorial for Candy Lovers – 14. Field Macros

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.

`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.

// 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).

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页