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 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
.
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.
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.
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 thedo_*
hooks such asdo_copy
anddo_compare
like you did in themy_transaction
.(1) Why use
do_copy()
?Let’s assume you have two instances of the
my_transaction
.Then you copy the
tr0
to thetr1
as follows:The
copy()
function internally calls thedo_copy()
, which is empty by default. That means if you don’t define thedo_copy()
, the properties (cmd
,addr
, anddata
) of themy_transaction
are not copied. For example:still displays
tr1.cmd=1
, nottr1.cmd=0
.On the other hand, if you define the
do_copy()
like what you did, the properties are copied. For example:displays
tr1.cmd=0
.(2) Why
$cast
is necessary?The type of
rhs
isuvm_object
. Theuvm_object
does not know the properties such ascmd
andaddr
. The$cast
assigns therhs
to themy_transaction
type so that you can access thecmd
andaddr
.(3) What does
cmd = tx.cmd
do?It copies the value of
tx.cmd
tothis.cmd
.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
The
uvm_comparer
is a so-called policy class that you can delegate the comparison task to. For example, Surya’sdo_compare
function can be rewritten using theuvm_comparer
as follows:However, because of the overhead the
uvm_comparer
class adds, it is not recommended to use this class.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.)
1) The
compare_field
function I used above executes the following code:This is the overhead I mentioned. You can do the equivalent comparison without using the
uvm_comparer
like this:Accellera did not comment on this, though.
2) The
compare
function calls both__m_uvm_field_automation
anddo_compare
functions internally. The__m_uvm_field_automation
does not compare the field if theUVM_NOCOMPARE
flag is set. But if you have your owndo_compare
(and it compares the field), the field is still compared because thedo_compare
is not aware of theUVM_NOCOMPARE
flag.Also what are we comparing ??
The
compare()
function internally callsdo_compare()
, which is empty by default. Thedo_compare()
defines the strategy of comparison. In yourmy_transaction
, you compare thecmd
,addr
, anddata
. By doing this, you defined two objects being equal if the values ofcmd
,addr
, anddata
are equal in the two objects.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..!
Thank you for your kind comment. Let me know if you have any further questions or requests.
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
The pack methods are used to convert object’s properties into an array of
bit
s,byte
s, orint
s. 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 owndo_pack
anddo_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 owndo_pack
anddo_unpack
.When you pack a property that requires more than one bit to represent itself, the
big_endian
property of theuvm_packer
determines the order of bits. Please see Pack in little endian and in big endian in this article.Hi Shimizu,
Please can you tell me how to use do_record ,record, begin_tr and end_tr
Regards,
Ch.Siva
I finally wrote a new article about recording.