Overview

Classes are a fundamental concept that allows you to create complex data structures and encapsulate behavior within your code. Classes provide a way to define new types that can have properties (variables) and methods (functions or tasks). This object-oriented programming paradigm makes it easier to model and manage complex systems by promoting modularity, reusability, and organization.

Features of SystemVerilog OOPs:

Encapsulation
Inheritance
Polymorphism
Data Abstraction

Inheritance Code Example
class Dashrat;
    string name = "Dashrat";
    int weight = 80;
    int age = 30;

    function print_details();
        $display( "Dashrat Call : Name -> %s", name );
        $display( "Dashrat Call : Weight -> %d", weight );
        $display( "Dashrat Call : Age -> %d", age );
    endfunction
    
    function hello_print();
        $display( "Hello Dashrat" );
    endfunction
endclass

class Ram extends Dashrat;

    string name = "Ram";

    function print_parent();
        $display( "Ram Call : Name -> %s", name ); // Ram's Variable
        $display( "Ram Call : Weight -> %d", weight ); // Dashrat's Variable
        $display( "Ram Call : Age -> %d", age ); // Dashrat's Variable
    endfunction
    
    function hello_print();
        $display( "Hello Ram" );
    endfunction
    
    /*
        Inherited Code
        --------------
        Variable -> weight
                    age
        Methods ->  print_details

        Overridded Code ( Can access parent using "super."" keyword )
        -------------------------------------------------------------
        Variable -> name
        Methods ->  hello_print
    */

endclass

class Lava extends Ram;

    int age = 10;

    function print_parent();
        $display( "Lava Call : Name -> %s", name ); // Ram's variable
        $display( "Lava Call : Weight -> %d", weight ); // Dashrat's variable
        $display( "Lava Call : Age -> %d", age ); // Lava's variable
    endfunction
    
    function hello_print();
        $display( "Hello Lava" );
    endfunction
    
    /*
        Inherited Code
        --------------
        Variable -> weight ( from "Dashrat" class )
                    name ( from "Ram" class )
        Methods ->  print_details ( from "Dashrat" class )

        Overridded Code
        ---------------
        Variable -> age
        Methods ->  hello_print
    */

endclass
Encapsulation Code Example
class Parent;
    string name;
    protected int weight;
    local int age;

    function print_details();
        $display( "Parent Call : Name -> %s", name );
        $display( "Parent Call : Weight -> %d", weight );
        $display( "Parent Call : Age -> %d", age );
    endfunction
endclass

class Child extends Parent;
    function print_parent();
        $display( "Child Call : Name -> %s", name );
        $display( "Child Call : Weight -> %d", weight );
        $display( "Child Call : Age -> %d", age ); // Inaccessible
    endfunction
endclass

class GrandChild extends Child;
    function print_grand_parent();
        $display( "GrandChild Call : Name -> %s", name );
        $display( "GrandChild Call : Weight -> %d", weight ); // Inaccessible
        $display( "GrandChild Call : Age -> %d", age ); // Inaccessible
    endfunction
endclass
Theory Questions
    /*
    Shallow copy refers to a direct way of copying a class without taking its dynamic elements into account. All the elements of a class which have do not deal with pointers and whic are basically access by value
    are copied as it is. The execption of shallow copy is that it treats pointers as variable values and hence does not really create a copy of the pointer memory, but rather
    points to the same memory. An example is given below:
    */
    
    class Occupation;
        string occ = "Unemployed";
      
      function new(string oc = "Unemployed");
      	  this.occ = oc;
      endfunction
    endclass
    
    class Original;
    
        int age = 10;
        string name = "Sonu";
        Occupation o;
    
        function new(string occ = "Unemployed");
            o = new(occ);
        endfunction
    
        function void print();
            $display( "Name : %s", name );
            $display( "Age  : %d", age );
            $display( "Occu : %s", o.occ );
        endfunction
    
    endclass
    
    module top;
    
        Original a, b;
    
        initial 
        begin
            a = new("Pilot");
            a.print();
            b = new;
            b.print();
            b = new a;
            b.print();
            b.name = "Rinzler";
            b.print();
            a.o.occ = "Fireman";
    
            a.print();
            b.print();
        end
    
    endmodule
    
    In SystemVerilog, a **deep copy** is the process of creating a new object that is an independent replica of the original, including all its internal data and dynamically allocated structures. Unlike a shallow copy, which only duplicates the references or handles to objects, a deep copy ensures that any changes made to the new copy do not affect the original object. This is especially important when the object contains dynamic arrays, queues, or other objects, as a deep copy ensures that each element or sub-object is also copied, maintaining complete independence between the original and the copied version.
    A singleton class in SystemVerilog (and in general object-oriented programming) is a class that restricts the instantiation of its objects to just one instance. This design pattern ensures that only one instance of the class exists throughout the program, which is useful when exactly one object is needed to coordinate actions across the system, such as a central controller or resource manager.
    Function overloading in SystemVerilog refers to the ability to define multiple functions with the same name but different argument lists within the same scope. The arguments can differ in number, types, or both. Based on the arguments provided during a function call, the appropriate version of the function is selected by the compiler. This feature allows for more intuitive and flexible function usage, where the same function name can be reused for different types of inputs.
    class P;
        int age;
    endclass
    
    class C extends P;
        int age;
    endclass
    
    module top;
    
        P p;
        C c;
    
        initial
        begin
            p = new;
            c = new;
            p.age = 100;
            c.age = 50;
    
            $display( "Age : %0d", p );
            $display( "Age : %0d", c );
            p = c;
            $display( "Age : %0d", p );
        end
    
    endmodule
    
    /*
    
        100
        50
        0
    
        0 is printed because after casting "p" now has the pointer to the base class memory
        of "c", which if you see was never initialized and hence age will have the datatype's
        default value
    
    */
    
    In SystemVerilog, a virtual class serves as a blueprint for creating other classes without being instantiated itself. The main purpose of a virtual class is to provide a base class with common methods, properties, and functionality that other derived classes can inherit and implement or extend. A virtual class is typically used in object-oriented verification environments, such as UVM, to define abstract behavior, leaving the details of that behavior to be specified by concrete derived classes.
    super keyword essentially has the pointer to the parent class stored in it. Using the "super" keyword is as good as using the pointer to your parent class.
    class A;
    
        string name = "Arjun";
    
        virtual function void print_name();
            $display( "Name : %s", name );        
        endfunction
    
    endclass
    
    class B extends A;
    
        string name = "Buddy";
    
        function void print_name();
            $display( "Name : %s", name );        
        endfunction
    
    endclass
    
    module top;
        A a;
        B b;
    
        initial
        begin
            a = new;
            b = new;
    
            a.print_name;
            b.print_name;
            b.name = "Big Brother";
            a.name = "Arya";
            a = b;
            a.print_name;
            a.name = "Bro";
            b.print_name;
            $cast( a, b );
            b.print_name;
        end
    endmodule
    
    /*
    
        Arya
        Buddy
        Arjun
        Big Brother
        Big Brother
    
    */
    
    module top;
    
        class A;
            int age = 10;
        endclass
    
    endmodule
    
    Yes, this is allowed
    class A;
    
        module top;
            logic M;
        endmodule
    
    endclass
    
    No, this is not allowed since during elaboration the top module will be treated as a static block but since its inside a class, there is a contradiction becuase class is a dynamic datatype.
    class A;
    
        class B;
            int age = 20;
        endclass
    
    endclass
    
    Locked Solution
    Quasi-Static simply refers to a block which exists on a need to basis. The best example for this is a class, since it only exists as long as the scope exists or unless null is assigned to the object variable. Calling them dynamic will also be understandable.
    The primary reason for introducing classes to SV is that they offer great reusability and improve memory/system resource usage from a simulation stand point.
Coding Questions
    class A;
        local int age = 10;
    
        function show_age();
            $display( "Age : %0d", age );
        endfunction
    
    endclass
    
    class B;
        int age = 30;
        
        /* Add modificaitons here */
    
    endclass
    
    // The code can be re-written like this
    
    
    class A;
        local int age = 10;
    
        function show_age();
            $display( "Age : %0d", age );
        endfunction
    
    endclass
    
    class B;
        int age = 30;
        A a;
        /* Accessing parent class */
    
        function new();
            a = super;
        endfunction
    endclass
    
    module top;
        B b;
    
        initial
        begin
            b = new;
            $display( "B Age : %0d", b.age );
            b.a.show_age(); // Accessing parent class indirectly
            b.a.age = 100;
            b.a.show_age(); // Accessing parent class indirectly
        end
    endmodule
    
    class A;
        static int CLASS_INST;
    
        function new();
            $display("Class Created : %0d", CLASS_INST);
            CLASS_INST++;
        endfunction
    endclass
    
    module top;
    
        A a1,a2;
    
        initial
        begin
            a1 = new;
            a2 = new;            
        end
    
    endmodule
    
    class A;
        static A CLASS_INST[$];
        static int INST_NUM;
        int LOCAL_INST_NUM;
        
        static int S_TRACKER; // Only used to track the original creation and deletion
        int TRACKER; // Same as above
    
        function new();
            $display("Class Created : %0d", INST_NUM);
            CLASS_INST.push_back(this);
            LOCAL_INST_NUM = INST_NUM;
            INST_NUM++;
    
            TRACKER = S_TRACKER++;
        endfunction
    
        function destroy();
            CLASS_INST.delete(LOCAL_INST_NUM);
            $display("Instance %0d Destroyed", LOCAL_INST_NUM);
            foreach(CLASS_INST[i])
            begin
                if( i >= LOCAL_INST_NUM )
                begin
                    CLASS_INST[i].LOCAL_INST_NUM--;
                end
            end
            INST_NUM--;
    
            LOCAL_INST_NUM = -1; // Basically means its destroyed
        endfunction
    
        function printInst();
            $display("Instance Number : %0d ( Tracker Details : %0d )", LOCAL_INST_NUM, TRACKER);
        endfunction
    endclass
    
    module top;
    
        A p0,p1,p2,p3,p4;
    
        initial
        begin
            p1 = new;
            p1.printInst;
            p2 = new;            
            p2.printInst;
            p3 = new;
            p3.printInst;
            p4 = new;            
            p4.printInst;
    
            p2.destroy;
            p1.printInst;
            p2.printInst;
            p3.printInst;
            p4.printInst;
        end
    
    endmodule
    
    class EVEN;
    
        rand int num;
    
        virtual function int genEvenNum();
            // Generate an EVEN number >0 & <1000
            return ( ( ( $urandom % 1001 ) >> 1 ) << 1 );
        endfunction
    
    endclass
    
    module top;
    
        EVEN e;
    
        initial
        begin
            e = new;
    
            repeat(5)
            begin
                $display( "Generated Number : %0d", e.genEvenNum );
            end
        end
    
    endmodule
    
    class EVEN;
    
        rand int num;
    
        virtual function int genEvenNum();
            // Generate an EVEN number >0 & <1000
            return ( ( ( $urandom % 1001 ) >> 1 ) << 1 );
        endfunction
    
    endclass
    
    module top;
    
        EVEN e;
    
        initial
        begin
            e = new;
    
            repeat(5)
            begin
                $display( "Generated Number : %0d", (e.genEvenNum>>2)<<2 ); // Modificaiton made here
            end
        end
    
    endmodule
    
    Locked Solution
    Locked Solution
    Locked Solution
    Locked Solution