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 acasestatement (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.

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.
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_transactionFirst of all, using the field macros such as
`uvm_field_enumand`uvm_field_intis not recommended because of the overhead I mentioned in the article. It is recommended to use thedo_*hooks such asdo_copyanddo_comparelike 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
tr0to thetr1as 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_transactionare 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
$castis necessary?The type of
rhsisuvm_object. Theuvm_objectdoes not know the properties such ascmdandaddr. The$castassigns therhsto themy_transactiontype so that you can access thecmdandaddr.(3) What does
cmd = tx.cmddo?It copies the value of
tx.cmdtothis.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_compareris a so-called policy class that you can delegate the comparison task to. For example, Surya’sdo_comparefunction can be rewritten using theuvm_compareras follows:However, because of the overhead the
uvm_comparerclass 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_fieldfunction I used above executes the following code:This is the overhead I mentioned. You can do the equivalent comparison without using the
uvm_comparerlike this:Accellera did not comment on this, though.
2) The
comparefunction calls both__m_uvm_field_automationanddo_comparefunctions internally. The__m_uvm_field_automationdoes not compare the field if theUVM_NOCOMPAREflag is set. But if you have your owndo_compare(and it compares the field), the field is still compared because thedo_compareis not aware of theUVM_NOCOMPAREflag.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, anddataare 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
bits,bytes, orints. 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_fieldmacros (which are not recommended due to an overhead) or write your owndo_packanddo_unpackmethods. 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_packanddo_unpack.When you pack a property that requires more than one bit to represent itself, the
big_endianproperty of theuvm_packerdetermines 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.