This example shows how to implement the conversion routines in UVM-style transaction in the virtual do_pack and do_unpack functions inherited from the uvm_object base class.
Most SV transactions extend uvm_sequence_item, which extends uvm_object, which defines virtual do_pack and do_unpack methods for override in user-defined transaction types. The UVMC’s default converter for SV works for these types of transactions. Defining SV-side transasctions in this way minimizes the extra code needed to make a cross-language connection.
UVMC Converter Example - SV In-Transaction | |
This example shows how to implement the conversion routines in UVM-style transaction in the virtual do_pack and do_unpack functions inherited from the uvm_object base class. | |
User Library | This section defines a transaction class, packet, that indirectly extends uvm_object. |
Conversion code | This section is empty because our conversion functionality is built into the transaction proper. |
Testbench code | This section defines our testbench environment. |
This section defines a transaction class, packet, that indirectly extends uvm_object. It also defines a generic producer model via an `include. All transactions and components in the user library should be written to be independent of context, i.e. not assume a UVMC or any other outside connetion.
The `uvm_pack_* and `uvm_unpack_* macros expand into two or so lines of code that are more efficient than using the packer’s API directly. These macros are part of the UVM standard and are documented under the Macros heading in the UVM Reference Manual.
package user_pkg; `include "uvm_macros.svh" import uvm_pkg::*; class packet_base extends uvm_sequence_item; `uvm_object_utils(packet_base) typedef enum { NOOP, READ, WRITE, RW } cmd_t; rand cmd_t cmd; rand int addr; rand byte data[$]; function new(string name=""); super.new(name); endfunction constraint c_data_size { data.size() inside { [1:10] }; } virtual function void do_pack(uvm_packer packer); `uvm_pack_enum(cmd) `uvm_pack_int(addr) `uvm_pack_queue(data) endfunction virtual function void do_unpack(uvm_packer packer); `uvm_unpack_enum(cmd,cmd_t) `uvm_unpack_int(addr) `uvm_unpack_queue(data) endfunction virtual function string convert2string(); return $sformatf("cmd:%s addr:%h data:%p",cmd,addr,data); endfunction endclass class packet extends packet_base; `uvm_object_utils(packet) rand int extra_int; function new(string name=""); super.new(name); endfunction virtual function void do_pack(uvm_packer packer); super.do_pack(packer); `uvm_pack_int(extra_int) endfunction virtual function void do_unpack(uvm_packer packer); super.do_unpack(packer); `uvm_unpack_int(extra_int) endfunction virtual function string convert2string(); return $sformatf("%s extra_int:%h",super.convert2string(),extra_int); endfunction endclass `include "producer.sv" `include "consumer.sv" endpackage : user_pkg
This section is empty because our conversion functionality is built into the transaction proper.
/*** No external conversion code needed ***/
This section defines our testbench environment. In the env’s build function, we instantiate the generic producer model. In the connect method, we register the producer’s out port for UVMC connection using the lookup string ‘stimulus’. The SC-side will register its consumer’s in port with the same lookup string. UVMC will match these two strings and complete the cross- language connection, i.e. the SV producer’s out port will be bound to the SC consumer’s in export.
Because our packet class implements the requisite do_pack and do_unpack methods, we can leverage UVMC’s default converter, which delegates to these methods. When making the uvmc_tlm::connect call, we do not need to specify a custom converter type--only the transaction type.
module sv_main; `include "uvm_macros.svh" import uvm_pkg::*; import uvmc_pkg::*; import user_pkg::*; // Define env with connection specifying custom converter class sv_env extends uvm_env; producer #(packet) prod; consumer #(packet) cons; `uvm_component_utils(sv_env) function new(string name, uvm_component parent=null); super.new(name,parent); endfunction function void build_phase(uvm_phase phase); prod = new("prod", this); cons = new("cons", this); endfunction function void connect_phase(uvm_phase phase); uvmc_tlm #(packet)::connect(prod.out, "stimulus"); uvmc_tlm #(packet)::connect(cons.in, "sc_stimulus"); endfunction endclass sv_env env; initial begin env = new("env"); run_test(); end endmodule