Sunday, 30 April 2017

p_sequencer vs. m_sequencer

This is probably the most used and still most confusing for me in UVM.
In simple words, p_sequencer is just the dynamic casting of m_sequencer.

In testcase whenever we call start method to run the sequence,
 ------------------------------------------------------------------------------------------
class test extends uvm_test; 
 ...
  seq.start(sequencer);
...
endclass: test
 ------------------------------------------------------------------------------------------

This start method is the source code of UVM class uvm_sequence_base:
------------------------------------------------------------------------------------------
virtual task start (uvm_sequencer_base sequencer, 
                    uvm_sequence_base parent_sequence = null, 
                    int this_priority = -1, 
                    bit call_pre_post = 1);

...
  set_sequencer(sequnecer);
...
endtask
------------------------------------------------------------------------------------------

Inside uvm_sequence_item:
------------------------------------------------------------------------------------------
virtual function void set_sequencer(uvm_sequencer_base sequencer);
  m_sequencer = sequencer;
  m_set_p_sequencer();
endfunction

virtual function void m_set_p_sequencer();
  return; 
endfunction
------------------------------------------------------------------------------------------

So, as we can see whenever we call start method of sequence it's m_sequencer(a variable of uvm_sequencer) is getting set. This variable stores any object derived from uvm_sequencer. And it can access all the properties of uvm_sequencer class. 

Now what if user wants to access the property of it's own user defined sequencer, for that we have to have p_sequencer which points to the sequencer of our environment. 

p_sequencer only exists if we call uvm_decalre_p_sequencer macro, which is defined inside the UVM source code: 
------------------------------------------------------------------------------------------
`define uvm_declare_p_sequencer(SEQUENCER) \
SEQUENCER p_sequencer;\
virtual function void m_set_p_sequencer();\
   super.m_set_p_sequencer(); \
   if( !$cast(p_sequencer, m_sequencer)) \
   `uvm_fatal("DCLPSQ", \
   $sformatf("%m %s Error casting p_sequencer, please verify that   this sequence/sequence item is intended to execute on this type of sequencer", get_full_name())) \
endfunction
 ------------------------------------------------------------------------------------------

This macro is called inside the user defined sequence and the sequencer passed when we call start method (from the testcase) is casted to p_sequencer.

As we can see in the above code, p_sequencer is just the dynamic casting of m_sequencer with the user defined type class. So ultimately both points to the same thing with different type of class.

Properties of user defined sequencer can be accessed through p_sequencer and that is the reason we required p_sequencer.  














Friday, 28 April 2017

Polymorphism and $cast

Polymorphism 

In OOPS polymorphism means same operation to be performed in wide range. 

-------------------------------------------------------------------------------------
Example: 

class India; 

task my_print();
  $display("This is India");
endtask: my_print

endclass: India

class Kerala extends India; 

task my_print();
  $display("This is Kerala");
endtask: my_print

endclass: Kerala

module top();

India i_var;
Kerala k_var;

initial 
begin
  i_var = new();
  i_var.my_print();
  k_var = new();
  i_var = k_var;
  i_var.my_print();
end

endmodule

Output:
This is India
This is India
-------------------------------------------------------------------------------------

In OOPS, base class can also access the method of child class if the it has the same method defined it it's own class(Here, my_print) and when the base class variable has the child object (Here, i_var = k_var). For that, key is "virtual"

-------------------------------------------------------------------------------------
Example: 

class India; 

virtual task my_print();
  $display("This is India");
endtask: my_print

endclass: India

class Kerala extends India; 

virtual task my_print();
  $display("This is Kerala");
endtask: my_print

endclass: Kerala

module top();

India i_var;
Kerala k_var;

initial 
begin
  i_var = new();
  i_var.my_print();
  k_var = new();
  i_var = k_var;
  i_var.my_print();
end

endmodule

Output:
This is India
This is Kerala
-------------------------------------------------------------------------------------

If method is declared as virtual in the base class, it is still considered as virtual in all the child calss even it is not defined as virtual.

$cast

It is always legal in OOPS, that base class variable has child class object, but it is not true in reverse case. 

-------------------------------------------------------------------------------------
module top();

India i_var;
Kerala k_var;

initial 
begin
  i_var = new();
  k_var = new();
  k_var = i_var;  // Illegal
end

endmodule
-------------------------------------------------------------------------------------

However, base class object can still be assigned to child class, if that base class variable has the child class object. For that $cast is used.

In a simple way, if we want to use the method which is defined inside the child class but not in the parent class, we have to revert back to the variable of child class instead of parent class. 

-------------------------------------------------------------------------------------
Example: 

class India; 
endclass: India

class Kerala extends India; 

virtual task my_print();
  $display("This is Kerala");
endtask: my_print

endclass: Kerala

module top();

India i_var;
Kerala k_var;

initial 
begin
  k_var = new();
  i_var = k_var;
  perform_cast(i_var);
end

task perform_cast(India ind) ;
  Kerala ker;
  $cast(ker, ind);
  ker.my_print();
endtask: perform_cast

endmodule

Output:
This is Kerala
-------------------------------------------------------------------------------------

If parent class variable is not having the object of  child class,
 -------------------------------------------------------------------------------------
Example: 

class India; 
endclass: India

class Kerala extends India; 

virtual task my_print();
  $display("This is Kerala");
endtask: my_print

endclass: Kerala

module top();

India i_var;
Kerala k_var;

initial 
begin
  i_var = new();
  perform_cast(i_var);
end

task perform_cast(India ind) ;
  Kerala ker;
  if (!$cast(ker, ind))
  begin
      $display("This casting is not legal");
  end
  else
  begin
    ker.my_print();
  end
endtask: perform_cast

endmodule

Output:
This casting is not legal
-------------------------------------------------------------------------------------

Monday, 10 April 2017

Static Property and Methods

Static Property

Usually class properties don't get created until its object get constructed. But the exception is "Static"


class packet;
  int id;
  static int pkt_id;
endclass

...
packet pkt1, pk2;
pkt1 = new();
pkt2 = new();

pkt1.id = packet::pkt_id++;
pkt2.id = packet::pkt_id++;
...

Output:
pkt1.id = 1
pkt2.id = 2


To access the static property, we have to use "class_name::property".
Static keyword added before the property becomes global through out the class type.


Static Method

class packet;
  int id;
  static int pkt_id;

  static function int unique_id;
     return pkt_id ++;
  endfunction
endclass

...
packet pkt1, pkt2;
pkt1 = new();
pkt2 = new();
pkt1.id = packet::unique_id;
pkt2.id = packet::unique_id;
...

Output:
pkt1.id = 1
pkt2.id = 2

Static method can not access to non static class property. Also there is no "this" for accessing static property or method.

Shalllow copy Deep copy

Shallow copy

class A;
  int j = 5;
endclass

class B;
  int i = 1;
  A a = new;
endclass

B b1, b2;
  b1 = new;
  b2 = new b1;
  b2.i = 10;
  b2.a.j = 7;
...

Output:
b1.i = 1
b2.i = 10
b1.a.j = 7
b2.a.j = 7


When we copy a class variable we are just copying the handle(pointer) not the object(memory). So here both the variables have same handle(pointer). This works well if the fields are values, but may not be what you want for fields that point to dynamically allocated memory. The pointer will be copied. but the memory it points to will not be copied -- the field in both the original object and the copy will then point to the same dynamically allocated memory.

When you do shallow copy all properties of the class will be duplicated(all properties are copied to new memory locations) in new memory except for objects. Shallow copy copies only the object handles.

Deep copy

class A;
  int j = 5;
endclass

class B;
  int i = 1;
  A a = new;

  function copy(B source);
    this.i = source.i;
    this.a = new source.a
   endfunction : copy
endclass

...
B b1, b2;
  b2.copy(b1);
  b2.i = 10;
  b2.a.j = 7;
...

Output:
b1.i = 1
b2.i = 10
b1.a.j = 5
b2.a.j = 7

For Deep copy we have to explicitly create logic to copy all the property of one class to another and if it has object as a property of other class we have to allocate memory for it and then copy the same.







Basic OOP: Constructor, Class handle and class object

Every class has in built method called: new (known as constructor).

Let's see an example,

class packet;
  int val;
endclass: packet

When we take the instance of this calss,

packet pkt; // This is just the variable of the data type packet.

We can not directly use this pkt to access the packet class property. We have to construct,

pkt = new; // This class variable is now class object.

Object can only be created if we call new of the class variable. Ultimately, class object is the class variable with it's dedicated memory. So whenever we call constructor of the class variable, memory is getting allocated for that class variable and it's memory pointer (known as class handle) is stored inside the class variable.

User can also overwrite this constructor,

class packet;
...
function new (int _val_);
  val = _val_;
endfunction
endclass

packet pkt = new(5);


Example:

Packet pkt1, pkt2;

pkt1 = new();


 _____________
|___0x80______|
|   int val             |
|_____________|

pkt2 = new();
 _____________
|___0x84______|
|   int val             |
|_____________|

pkt1 = new();
 _____________
|___0x88______|
|   int val             |
|_____________|
 _____________
|___0x80______|
|   Unused           |
|_____________|

pkt2 = pkt1;
 _____________
|___0x88______|
|   int val             |
|_____________|
 _____________
|___0x84______|
|   Unused           |
|_____________|

Now both pkt2 and pkt1 both are variables are pointing to the same memory (0x84). Because
pkt2 = pkt1 copies memory pointer only.

pkt1 = new();

 _____________
|___0x90______|
|   int val             |
|_____________|   // For pkt1
 _____________
|___0x88______|
|   int val             |
|_____________|  // For pkt2

pkt2 = null;
 _____________
|___0x90______|
|   int val             |
|_____________|   // For pkt1
 _____________
|___0x88______|
|   Unused           |
|_____________|