Thursday, 26 September 2013

UVM-Multi Language solution from Cadence

Today's complex SoC level verification environment are based on advanced verification methodology standards. But these standards usually meant for single language domain. But fact of the life is that the these SoC level testbenches are made of mix of language domains like legacy Specman/e environment along with newly developed UVM-SV and SystemC or C++. Usually, when these scenario arrives, verification teams typically comes up with their own solutions which is mainly short term solution keeping existing needs in to the consideration. To develop this kind of inter operable solutions, there are quite a few challenges involved. Few are listed below.

1) Connecting different components of different domains
2) Transferring data/events from different domains
3) Synchronizations of major phases


Having said that, industry standard solution is the need of an hour. Cadence's Multi Language solution also known as UVM-ML addresses the above mentioned multi language integration challenges. UVM-ML is in existence for quite a sometime but least known. It was initially developed to take care of e-SV and e-SC integrations. UVM-ML is based on a back-plane library responsible for connecting two or more domains. Different language domain connect to back-plane library via language specific adapter layer. Currenty this solution supports integration of SystemC, UVM-SV, UVM-e.

Here is an example of how UVM-SV based producer can communicate to SystemC based consumer. You can refere more examples in the Incisiv installation area at $CDNS_HOME/tools/uvm/uvm_lib/uvm_ml/examples/sc/ and $CDNS_HOME/tools/uvm/uvm_lib/additions/uvm_ml_examples/ex_e_sv_sc_tlm/


//-----------------------------------------
//File : producer.sv
//System Verilog Producer
//-----------------------------------------
module topmodule;
import uvm_pkg::*;
`include "uvm_macros.svh"

//Declare basic packet which will be passed to SC consumer
class packet extends uvm_object;
   rand int data;
endclass

//Declare SV producer which will communicate with SC consumer through out port
class producer extends uvm_component;
   //Declare out port. This port will be connected to SC consumer
   uvm_analysis_port #(packet) out;

   //constructor
   function new(string name"producer", uvm_component parent=null);

      super.new(name, parent);
      out = new("out", this);

      //Register out port with UVM-ML
      ml_uvm::extnernal_if(out, "packet");

   endfunction : new

   //Generate dummy packet and pass it to out port
   task run_phase (uvm_phase phase);
      packet pkt = packet::type_id::create("pkt");

      phase.raise_objection(this);
      for (int i=0; i<5 br="" i="">      begin
         bit res = pkt.randomize();
         `uvm_info("TEST", $sformatf("producer.pkt.data='h%h", pkt.data), UVM_LOW)
         //Send the generated packet to out port
         out.write(pkt);
         #10;
      end

      phase.drop_objection(this);
   endtask : run_phase
endclass : producer
endmodule

//-----------------------------------------
//File : consumer.cpp
//SystemC Consumer
//-----------------------------------------
#include "ml_uvm.h";
#include "uvm.h";

using namespace tlm;
using namespace uvm;

class packet : public uvm_object
{
   UVM_OBJECT_UTILS(packet)
   public:
      int data;
};

UVM_OBJECT_REGISTER(packet)

//System C Consumer with templated with data type T
template <typename T>
class consumer : public uvm_compnent, public tlm_analysis_if
{
   public:
      sc_export > in;

   //constructor
   consumer(sc_module_name nm) : uvm_component(nm), in("in")
   {
      in(*this);
      //Register the export for mixed language communication
      ml_uvm::ml_uvm_register(&in);
   }

   UVM_COMPONENT_UTILS(consumer)

   //export implementation
   virtual void write( const T& t )
   {
      cout << sc_time_stamp() << " SystemC consumer receive a packet with data " << t.data << endl;
   }
};

UVM_COMPONENT_REGISTER_T(consumer, packet)

//System C testbench
SC_MODULE(TB)
{
   consumer c;
   SC_CTOR(TB) : c("consumer")
   {
      //Connect multi language port and export
      ml_uvm::ml_uvm_connect("producer.out", cons.in.name());
   }
};

Output Log:

UVM_INFO producer.sv(55) @ 0: producer [TEST] producer.pkt.data='h00000008
0 s SC consumer got a packet with data 8
UVM_INFO producer.sv(55) @ 10: producer [TEST] producer.pkt.data='h00000008
10 ns SC consumer got a packet with data 8
UVM_INFO producer.sv(55) @ 20: producer [TEST] producer.pkt.data='h00000003
20 ns SC consumer got a packet with data 3
UVM_INFO producer.sv(55) @ 30: producer [TEST] producer.pkt.data='h00000005
30 ns SC consumer got a packet with data 5
UVM_INFO producer.sv(55) @ 40: producer [TEST] producer.pkt.data='h00000006
40 ns SC consumer got a packet with data 6

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.