Wednesday, 25 September 2013

UVM bidirectional port example using uvm_tlm_b_initiator_socket and uvm_tlm_b_target_socket ports

In your System Verilog UVM environment, sometimes you want few components to communicate to each other in both ways. For example, a master component can request a memory component to pass over the memory content of specific address. So, how can you impelent such a communication channel between two components? Well, there are multiple ways. You can have two one-directional ports like uvm_get_ports and uvm_put_ports. Initator can send the address through output port and receives the data from another input port. Or it can be implemented through mailbox. The other way to implement is to use uvm_tlm_b_initiator_socket and uvm_tlm_b_target_socket ports. Using this port, you initiator can send a packet with specific address. Same packet can be updated with data by the memory element and later on can be read by initiator. Thus, by using single port and packet, two elements can communicate with each other in bidirectional way. Since this is a blocking port, request information and response information should come in a single shot. Here is an example to illustrate it. You can download the full examples by clicking uvm_bidirectional_port_example.tar.gz


//Memory cell definition
class mem_cell extend umv_sequence_item;
   rand bit [1:0]  addr;
   rand bit [31:0] data;
endclass: mem_cell


//Memory component definition
class memory extends uvm_component;
   //Define local memory
   local bit [31:0] mem [3:0];

   function new(string name = "memory", uvm_component parent = null);
      super.new(name, parent);
      socket = new("socket", this);
      //Initialize the local memory
      mem[0] = 32'h00000000;
      mem[1] = 32'h11111111;
      mem[2] = 32'h22222222;
      mem[3] = 32'h33333333;
   endfunction : new

   //Declare blocking target socket port
   uvm_tlm_b_target_socket #(memory, mem_cell) socket;

   //Need to write implementation of b_transport() task
   task b_transport(mem_cell req, uvm_tlm_time delay);
      //Based on the req.addr, update the req.data which will be later read back by initiator
      case (req.addr)
         2'h0 : begin req.data = mem[0]; end
         2'h1 : begin req.data = mem[0]; end
         2'h2 : begin req.data = mem[0]; end
         2'h3 : begin req.data = mem[0]; end
      endcase

   endtask : b_transport

endclasss : memory


class initiator extend uvm_component'
  
   //Declare blocking transport socket port
   uvm_tlm_b_transport_socket #(mem_cell) socket;

   virtual task run_phase(uvm_phase phase);
     
      //instantiate a mem_cell
      mem_cell req;

      uvm_tlm_time delay = new;

      phase.raise_objection(this);

      //Create mem_cell
      req = mem_cell::type_id::create("req", , get_full_name());

      //set address and sent it across to memory component
      req.addr = 2'h0;
      socket.b_transport(req, delay);
      //memory element has now updated the req.data accordingly. it can be used now
      `uvm_info("TEST", $sformat(f("For req.add='h%h, received req.data='h%h", req.addr, req.data), UVM_LOW);

      //same object can be reused.
      //set address and sent it across to memory component
      req.addr = 2'h1;
      socket.b_transport(req, delay);
      //memory element has now updated the req.data accordingly. it can be used now
      `uvm_info("TEST", $sformatf("For req.add='h%h, received req.data='h%h", req.addr, req.data), UVM_LOW);

      phase.drop_objection(this);
   endtask : run_phase
  
endclass : initator


class env extends uvm_component;
   initiator init;
   memory   mem;

   //Connect two ports
   function void connect_phase(uvm_phase phase)
      init.socket.connect(mem.socket);
   endfunction

endclass : env


Log Output:

UVM_INFO initiator.sv(22) @ 10: env.init [TESTCASE] For req.add='h0 received  req.data='h00000000
UVM_INFO initiator.sv(26) @ 20: env.init [TESTCASE] For req.add='h1 received  req.data='h11111111



You can refer UVM User Guide 1.1 from http://www.accellera.org/community/uvm for more details about these ports.

1 comment:

David & Carolynne Larson said...

It looks to me that you are getting "bidirectional" behavior because you are passing the object reference around. That doesn't require a special TLM type. For a true bidirectional TLM connection try the uvm_blocking_transport:

https://verificationacademy.com/verification-methodology-reference/uvm/docs_1.1a/html/files/tlm1/uvm_ports-svh.html#uvm_*_port#(REQ,RSP)