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 acase
statement (the second highlighted block) and performs the functionality of copy, compare, and pack, depending on the value of thewhat__
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).