From VHDL Records to UVM Transactions
Although VHDL supports record types (structs), it is more common to use types like Std_logic and Std_logic_vector when describing hardware. With UVM, on the other hand, all communcation amongst verification components makes use of transactions. UVM represents transactions using classes, just as UVM represents components using classes. In object-oriented programming, classes are a very general and powerful mechanism that can be used to create an abstract description of anything. In UVM, components and transactions are distinguished by extending different base classes and by defining different methods.
-- VHDL package my_pkg is type my_transaction is record data: integer; end record; end package;
// UVM package my_pkg; class my_transaction extends uvm_sequence_item; `uvm_object_utils(my_transaction) rand int data; // Other attributes of the transaction go here... function new (string name = ""); super.new(name); endfunction endclass: my_transaction endpackage
Here you can see the second of the three main code templates used in Easier UVM, namely the transaction or sequence item. (The first code template was the component, and the third will be the sequence.) There are a number of differences between the transaction and the component, as shown in the following table:
Component | Transaction |
---|---|
extends uvm_component | extends uvm_sequence_item |
contains `uvm_component_utils | contains `uvm_object_utils |
may define ports and exports | (no ports or exports) |
contains function new(string name, uvm_component parent); super.new(name, parent); endfunction |
contains function new (string name = ""); super.new(name); endfunction |
may define phase methods build, connect, run, ... | may define utility methods convert2string, do_copy, do_compare |
Having defined a transaction data type, let's now use it. In VHDL, design entities are not permitted to communicate using procedure calls, so a producer might generate transactions by making signal assignments, as shown here:
-- VHDL entity producer is port (my_port: out work.my_pkg.my_transaction); end entity; architecture v1 of producer is begin process begin my_port <= (data => 99); wait; end process; end architecture;
whereas in UVM a producer would call put, as was shown above. We will just highlight the key lines here. The producer instantiates a UVM port and passes the transaction type as a parameter:
// UVM uvm_blocking_put_port #(my_transaction) my_port;
The producer then creates transactions and sends them out through the port by calling put:
my_transaction tx = my_transaction::type_id::create("tx"); my_port.put(tx);
In VHDL, a consumer might have a process wake up whenever it receives an incoming transaction on a signal, as follows:
-- VHDL use ... entity consumer is port (my_export: in work.my_pkg.my_transaction); end entity; architecture v1 of consumer is begin process begin wait on my_export'TRANSACTION; report "Received transaction, .data = " & to_string(my_export.data); end process; end architecture;
whereas as we have already seen a UVM consumer would implement the put method.
// UVM uvm_blocking_put_imp #(my_transaction, consumer) my_export;
task put(my_transaction arg); `uvm_info("", $sformatf("Received transaction, .data = %0d", arg.data), UVM_NONE) endtask
Once again we can see that the communication mechanism is rather different when comparing VHDL (or Verilog) with UVM: VHDL sends transactions my making signal assignments, whereas UVM makes method calls. The HDL approach is closer to hardware, whereas the UVM approach is faster in terms of simulation speed and much more flexible when we want to customize the behavior of a component for a specific test.
Next: From VHDL Generics and Verilog Parameters to UVM ConfigurationsPrevious: From HDL Input and Output Ports to TLM Ports and Exports
Back to Easier UVM - for VHDL and Verilog Users
Back to the full list of UVM Resources