Global training solutions for engineers creating the world's electronics

Easier UVM - for VHDL and Verilog Users: Ports

John Aynsley, Doulos, March 2011
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 Transactions
Previous:  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