Updated for UVM 1.0
From HDL Input and Output Ports to TLM Ports and Exports
UVM allows communication between components using ports, but things work a little differently as compared with Verilog and VHDL. UVM uses transaction-level communication, that is, transactions are passed between components using function calls. Consider the following example:
// Verilog module producer (my_port); output [31:0] my_port; reg [31:0] my_port; initial my_port = 99; endmodule module consumer (my_export); input [31:0] my_export; always @(my_export) $display("my_export = %0d", my_export); endmodule module top; wire [31:0] w; producer producer_inst( .my_port(w) ); consumer consumer_inst( .my_export(w) ); endmodule
// UVM class producer extends uvm_component; `uvm_component_utils(producer) uvm_blocking_put_port #(int) my_port; function new(string name, uvm_component parent); super.new(name, parent); endfunction function void build_phase(uvm_phase phase); super.build_phase(phase); my_port = new("my_port", this); endfunction task run_phase(uvm_phase phase); my_port.put(99); endtask endclass class consumer extends uvm_component; `uvm_component_utils(consumer) uvm_blocking_put_imp #(int, consumer) my_export; function new(string name, uvm_component parent); super.new(name, parent); endfunction function void build_phase(uvm_phase phase); super.build_phase(phase); my_export = new("my_export", this); endfunction task put(int arg); `uvm_info("", $sformatf("Called put(%0d)", arg), UVM_NONE) endtask endclass class top extends uvm_component; `uvm_component_utils(top) producer producer_h; consumer consumer_h; function new(string name, uvm_component parent); super.new(name, parent); endfunction function void build_phase(uvm_phase phase); super.build_phase(phase); producer_h = producer::type_id::create("producer_h", this); consumer_h = consumer::type_id::create("consumer_h", this); endfunction function void connect_phase(uvm_phase phase); producer_h.my_port.connect( consumer_h.my_export ); endfunction endclass
Whereas in Verilog a producer would make an assignment to an output port, in UVM a producer would make a call to the put method through a port. Whereas in Verilog a consumer would have a process sensitive to an input port and would then read the value of that input port when it changes, in UVM a consumer would provide an implementation of the put method and also an export that can be hooked to the corresponding port on the producer.
In the example above you can see the producer making the call
my_port.put(99);
and the consumer providing an implementation of put
function void put(int arg);.
In order to connect the two together, the producer declares a port
uvm_blocking_put_port #(int) my_port;
the consumer declares an export
uvm_blocking_put_imp #(int, consumer) my_export;
and the top-level component makes the connection between the two
producer_h.my_port.connect( consumer_h.my_export );.
Transaction-level communication using methods such as put does not involve wires (Verilog) or signals (VHDL), nor does it necessarily involve events or synchronization with the simulation kernel, but nonetheless it does have the effect of passing messages between components in the UVM test bench. As is the case for VHDL signals, the data type of the transaction passed as an argument to the put method can be user-defined; in fact, in UVM, the transaction is usually itself a class, as we will see next.
Next: From VHDL Records to UVM TransactionsPrevious: From HDL Processes to the UVM Run Phase
Back to Easier UVM - for VHDL and Verilog Users
Back to the full list of UVM Resources