Monday, February 29, 2016

Virtual sequence and virtual seqeuencer

Why we need to use virtual sequence as well virtual sequencer.

Lets take an hypothetical example.

If in your system you want to drive USB and AHB transaction together (parallel) as your DUT support both interface.

From test you will start a virtual sequence (here virtual means not a virtual class like of polymorphism)

This virtual sequence will in turn start USB and AHB sequence in its body.
Now USB sequence will need usb_sequencer and ABH will need AHB_sequencer.

So n virtual sequence we will take handle of both sequencer and assign the handle with respective sequencer in test case like

virtual_seq.usb_sequencer = env.usb_agent.usb_sequencer
virtual_seq.ahb_sequencer = env.ahb_agent.ahb_sequencer

We will start virtual sequence as below
virtual_seq.start( .sequencer(null))

This start task will call body of virtual sequence and it will call start for USB and AHB sequence.

Here virtual sequence is not started on any sequencer. Because we are not going to connect any sequencer for virtual sequence to driver. As we do not need it. Alternatively we can start it on virtual_sequencer and that virtual_sequencer will have handle of usb_sequencer and ahb_sequencer, but it will add up with more code unnecessarily.

So better not to use virtual_sequencer.

Sunday, February 14, 2016

type_id::create simplified.

With UVM we use type_id::create to create object of any class. We do not use new. It is because we can override that class without changing single line of your code by help of uvm_factory.

How type_id::create works, that I will try to show you by very simple example.

class u_object; // Think of it as uvm_object
endclass
class u_registry #(type T=u_object); // Think of it as uvm_object_registry #(type T=uvm_object, string Tname="<unknown>")
  static function T create();
    u_object obj;
    u_registry#(T) me;
    me = new();
    obj = me.create_object();
    $cast(create,obj);
  endfunction
  function u_object create_object();
    T obj;
    obj = new();
    return obj;
  endfunction
endclass
class transaction extends u_object;
  int i;
  typedef u_registry #(transaction) type_id; // Assume this line will be added when we write `uvm_object_utils(transaction)
  virtual function display();
    i = 25;
    $display("i is %0d",i);
  endfunction
endclass
module test();
transaction pkt;
initial
begin
  pkt = transaction::type_id::create(); // transaction::type_id is 
                                        // u_registry #(transaction)
                                        // u_registry #(transaction)::create()
                                        // so we called static function create which will
                                        // return object of type transaction.
  pkt.display();
end
endmodule
Output :
i is 25 (run here)

1. pkt = transaction::type_id::create() will do pkt=u_registry #(transaction)::create()
2. create is a static method. In this method we have created object of own class type "me"
3. "me.create_object" will return handle of "transaction" object of type u_object
4. As return type of create_object is u_object, we will downcast it with "transaction" type by $cast in "create" method.
5. Retrun type of "create" is T ("transaction"), so finally object of type "transaction" returned to "pkt"

Now, how can we override transaction class with another class (say transaction_1) by factory.

UVM Factory method is
transaction::type_id::set_type_override(transaction_1::get_type());

Without going in to the details of factory, I will just give an essence that how factory will override pkt to contain handle of transaction_1 instead of transaction.

Check out below code. I have just changed (keeping rest of the code same)
pkt = transaction::type_id::create(); to
pkt = transaction_1::type_id::create();
(Above change will be done by factory when we call set_type_override, 
 this change is just for explanation purpose)

class u_object;
endclass
class u_registry #(type T=u_object);
  static function T create();
    u_object obj;
    u_registry#(T) me;
    me = new();
    obj = me.create_object();
 //create = me.create_object();
 $cast(create,obj);
  endfunction
  function u_object create_object();
 T obj;
    obj = new();
    return obj;
  endfunction
endclass
class transaction extends u_object;
  int i;
  typedef u_registry #(transaction) type_id;
  virtual function display();
    i = 25;
    $display("i is %0d",i);
  endfunction
endclass
class transaction_1 extends transaction; // derived transaction class
  typedef u_registry #(transaction_1) type_id;
  function display();
    i = 50;
    $display("i is %0d",i);
  endfunction
endclass
module test();
transaction pkt;
initial
begin
  pkt = transaction_1::type_id::create(); // Check this change
  pkt.display();
end
endmodule
Output
i is 50 (run here) 
Now create will return object of type transaction_1 instead of transaction.
Check the output.

Monday, December 7, 2015

What is difference between copy and clone method in UVM

There are two methods we use to copy data of one uvm_object (transaction or sequence item) to another.
Copy and
Clone.

Here I will explain you difference between copy and clone.

See below code.
========================================================================
// do_copy method:
function void do_copy(uvm_object rhs);
  bus_item rhs_;
  if(!$cast(rhs_, rhs)) begin
  uvm_report_error("do_copy:", "Cast failed");
  return;
  end
  super.do_copy(rhs); // Chain the copy with parent classes
  delay = rhs_.delay;
  addr = rhs_.addr;
  op_code = rhs_.op_code;
  slave_name = rhs_.slave_name;
  data = rhs_.data;
  response = rhs_.response;
endfunction: do_copy

// Example of how do_copy would be used:
// Directly:
bus_item A, B;
A.copy(B); // A becomes a deep copy of B
// Indirectly:
$cast(A, B.clone()); // Clone returns an uvm_object which needs
// to be cast to the actual type
========================================================================
A.copy(B); is deep copy of  B. 
While B.clone() returns a new object of type uvm_object which we cast to type "bus_items". So in a way A becomes deep copy of B.

Below here I am mimicking behavior of copy and clone method of UVM

typedef base_class;
typedef derived_class;
function base_class create();
  derived_class tmp;
  tmp = new();
  return tmp;
endfunction
class base_class;
  int A = 7;
  virtual function do_copy(base_class rhs);
  endfunction
  virtual function copy(base_class rhs);
    do_copy(rhs);
  endfunction
  virtual function base_class clone();
    base_class base;
    base = create();
    base.copy(this);
    return (base);
  endfunction
endclass
class derived_class extends base_class;
  int A = 5;
  function do_copy(base_class rhs);
    derived_class derived;
    $cast(derived,rhs);
    super.do_copy(rhs);
    A = derived.A;
  endfunction
endclass

module test();
  derived_class d1,d2,d3;
  initial
  begin
    d1 = new();
    d1.A = 10;
    d3 = new();
    d3.copy(d1);
    $cast(d2,d1.clone());
    $display("A in d1 is %0d",d1.A);
    $display("A in d2 is %0d",d2.A);
    $display("A in d3 is %0d",d3.A);
    d1.A = 20;
    $display("A in d1 is %0d",d1.A);
    $display("A in d2 is %0d",d2.A);
    $display("A in d3 is %0d",d3.A);
  end 
endmodule

Output : 
A in d1 is 10
A in d2 is 10
A in d3 is 10
A in d1 is 20
A in d2 is 10
A in d3 is 10

There are two function used here copy and clone.
copy : d3.copy(d1); which is simply deep copy of d1 to d3. It works as below.
1. d3 calls copy function which is defined in base_class.
2. copy function calls do_copy, as do_copy is virtual method, always latest definition of function will be executed. There is only one implementation of a virtual method per class hierarchy, and it is always the one in the latest derived class. So do_copy from "derived class" will be called.

clone : $cast(d2,d1.clone()); This method works as below.
1. d1.clone() calls definition of clone from base class.
2. clone calls create method which, returns handle of type derived_class.
3. A child class handle can be directly assign to base class.
4. base.copy(this); This is basically deep copy of this(here d1) to base.
5. So clone method return class variable of type "base_class" which we need to cast in derived_class, then only we can access member of class.

If you have noticed, here there is one dilemma. 

base.copy(this); calls copy function from base class , which call do_copy of derived class. So in turn do_copy is called from handle of object of type base_class. So how in do_copy it can access variable A, since it is not defined in base_class.
Reason is that "A" is being used by do_copy function which is in derived class. So context of A will be derived_class. 
If I have written base.A then it would not work

Sunday, October 25, 2015

TLM (Transaction Level Modeling)

What is TLM?

TLM provide API. API is application programming interface. Let me explain API in some simple terms. In software, say in movie ticket booking application, the application gives you information about theater and seats and when you select it and go for payment, API connects it with payment gateway which deals with your credit card information. For Mobile app developer that payment gateway is independent software or module, which is connected through API. So seat selecting module and payment gateway both are independent module, don't know or don't dependent on each other, connected through API.

In testbench driver and sequencer are two individual block, which is independent to each other, but can communicate through API, which is TLM.

TLM works on some terminology like,

Producer : who produces transaction. In our case Sequencer.
Consumer : Who consumes transaction. Here Driver
Port : It is actually a TLM port which either put the transaction in producer or get the transaction in consumer.
Export : It is a TLM port which either implement put method in consumer or get method in producer
Put : It is blocking task which put transaction(seq_item) from producer.
Get : It is blocking task which get seq_item from consumer.
Try_put : Same as put but its non blocking (a function basically) with return type as bit. It return TRUE if process is successful else return FALSE.
Try_Get : Same as Get. But non blocking (function)

TLM connection would be as below

m_driver.seq_item_port.connect(m_sequencer.seq_item_export);

seq_item_port from driver is connected to seq_item_export of sequencer.
Driver will call seq_item_port.get_next_item(req_item).

Due to above connection made in environment, it will actually call m_sequencer.seq_item_export.get_next_item which finally calls get_next_item of sequencer.


seq_item_port.get_next_item(req) and seq_item_port.item_done() is the handshake mechanism between driver and sequencer. Once this handshake done, only then driver can ask for next seq_item.

Symbolic Notation :
Square : Indicate Port
Circle : Indicate Export
Diamond : Indicate Analysis Port

 












In Figure 1 Put method is implemented is in consumer, So export (Circle) will be at consumer.
Figure 2 get method is implemented in produce , so Port (Sqiare) will be producer.
Figure 3 , write method is implemented in subscriber, so export is always subscriber and here consumer will be analysis port.