1. OOP in SystemVerilog - Polymorphism
Polymorphism is a fundamental OOP concept that allows objects of different classes to be treated through a common base class interface. It's essential for UVM and enables flexible, reusable verification code.
What is Polymorphism?
Polymorphism means "many forms." In SystemVerilog:
- A base class handle can point to a derived class object
- When calling a virtual method, the actual method executed depends on the object type, not the handle type
- Enables writing generic code that works with any derived class
Key Insight: Polymorphism requires the virtual keyword. Without it, the method called depends on the handle type, not the object type.
Virtual vs Non-Virtual Methods
flowchart LR
subgraph VIRTUAL["Virtual Method"]
V1["Base handle"] --> V2["Looks at OBJECT type"]
V2 --> V3["Calls derived method"]
end
subgraph NONVIRTUAL["Non-Virtual Method"]
N1["Base handle"] --> N2["Looks at HANDLE type"]
N2 --> N3["Calls base method"]
end
style V3 fill:#d1fae5,stroke:#10b981
style N3 fill:#fee2e2,stroke:#ef4444
| Aspect | Virtual Method | Non-Virtual Method |
|---|---|---|
| Keyword | virtual function | function |
| Resolution | Runtime (object type) | Compile-time (handle type) |
| Polymorphism | Yes | No |
| Override | Derived method called | Base method called |
Complete Example
class Animal;
string name;
function new(string name);
this.name = name;
endfunction
// Virtual method - enables polymorphism
virtual function void speak();
$display("%s makes a sound", name);
endfunction
// Non-virtual method - no polymorphism
function void info();
$display("Animal: %s", name);
endfunction
endclass
class Dog extends Animal;
function new(string name);
super.new(name);
endfunction
// Override virtual method
virtual function void speak();
$display("%s says: Woof!", name);
endfunction
// Override non-virtual method
function void info();
$display("Dog: %s", name);
endfunction
endclass
class Cat extends Animal;
function new(string name);
super.new(name);
endfunction
virtual function void speak();
$display("%s says: Meow!", name);
endfunction
function void info();
$display("Cat: %s", name);
endfunction
endclass
Testbench Demonstrating Polymorphism
module tb;
initial begin
Animal animal;
Dog dog;
Cat cat;
dog = new("Buddy");
cat = new("Whiskers");
$display("=== Direct Access ===");
dog.speak(); // Dog::speak()
dog.info(); // Dog::info()
cat.speak(); // Cat::speak()
cat.info(); // Cat::info()
$display("\n=== Polymorphism via Base Handle ===");
// Base handle pointing to Dog object
animal = dog;
animal.speak(); // Dog::speak() - VIRTUAL: uses object type
animal.info(); // Animal::info() - NON-VIRTUAL: uses handle type
// Base handle pointing to Cat object
animal = cat;
animal.speak(); // Cat::speak() - VIRTUAL
animal.info(); // Animal::info() - NON-VIRTUAL
$display("\n=== Array of Mixed Types ===");
process_animals();
end
// Generic function that works with any Animal
function void process_animals();
Animal zoo[3];
zoo[0] = new("Generic");
zoo[1] = Dog::new("Rex");
zoo[2] = Cat::new("Tom");
foreach (zoo[i]) begin
zoo[i].speak(); // Polymorphic call
end
endfunction
endmodule
Output
=== Direct Access === Buddy says: Woof! Dog: Buddy Whiskers says: Meow! Cat: Whiskers === Polymorphism via Base Handle === Buddy says: Woof! Animal: Buddy Whiskers says: Meow! Animal: Whiskers === Array of Mixed Types === Generic makes a sound Rex says: Woof! Tom says: Meow!
Inheritance Chain Example
classDiagram
class A {
+virtual display()
+message()
}
class B {
+display()
+message()
}
class C {
+display()
+message()
}
A <|-- B : extends
B <|-- C : extends
note for A "virtual keyword here"
note for B "virtual inherited"
note for C "virtual inherited"
class A;
// Virtual in base class - polymorphism enabled for ALL derived classes
virtual function void display();
$display("Display: Class A");
endfunction
// Non-virtual - no polymorphism
function void message();
$display("Message: Class A");
endfunction
endclass
class B extends A;
// No need to redeclare 'virtual' - inherited from A
function void display();
$display("Display: Class B");
endfunction
function void message();
$display("Message: Class B");
endfunction
endclass
class C extends B;
// Still virtual (inherited through A -> B)
function void display();
$display("Display: Class C");
endfunction
function void message();
$display("Message: Class C");
endfunction
endclass
Test Results
initial begin
A a_h;
B b_h;
C c_h;
b_h = new();
c_h = new();
// A handle -> B object
a_h = b_h;
$display("A handle -> B object:");
a_h.display(); // "Display: Class B" (virtual)
a_h.message(); // "Message: Class A" (non-virtual)
// B handle -> C object
b_h = c_h;
$display("\nB handle -> C object:");
b_h.display(); // "Display: Class C" (virtual)
b_h.message(); // "Message: Class B" (non-virtual)
end
Polymorphism in UVM
Polymorphism is fundamental to UVM's flexibility:
| UVM Feature | Uses Polymorphism For |
|---|---|
| Factory | Override components/transactions at runtime |
| Sequences | Base sequence handle runs different sequence types |
| Callbacks | Insert custom behavior without modifying base code |
| Phases | Call appropriate phase methods in derived components |
// UVM Factory override example
class base_test extends uvm_test;
`uvm_component_utils(base_test)
my_env env;
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
// Factory creates the registered type (or its override)
env = my_env::type_id::create("env", this);
endfunction
endclass
class error_test extends base_test;
`uvm_component_utils(error_test)
virtual function void build_phase(uvm_phase phase);
// Override transaction type for this test
my_seq_item::type_id::set_type_override(error_seq_item::get_type());
super.build_phase(phase);
endfunction
endclass
Common Mistakes
| Mistake | Result | Fix |
|---|---|---|
Forgetting virtual | Base method called instead of derived | Add virtual to base class method |
| Wrong signature in derived | New method, not override | Match return type and arguments exactly |
Declaring virtual only in derived | Partial polymorphism | Declare virtual in the topmost base class |
| Assigning base to derived handle | Compile error | Use $cast() for downcasting |
// WRONG - virtual only in derived class
class Base;
function void foo(); // Not virtual
$display("Base");
endfunction
endclass
class Derived extends Base;
virtual function void foo(); // Too late!
$display("Derived");
endfunction
endclass
// Result: Base handle calling foo() always prints "Base"
$cast for Downcasting
class Animal;
virtual function void speak();
$display("Animal sound");
endfunction
endclass
class Dog extends Animal;
function void fetch();
$display("Fetching ball!");
endfunction
virtual function void speak();
$display("Woof!");
endfunction
endclass
module tb;
initial begin
Animal a;
Dog d, d2;
d = new();
a = d; // Upcast: OK (implicit)
// a.fetch(); // ERROR: Animal doesn't have fetch()
// Downcast: requires $cast
if ($cast(d2, a)) begin
d2.fetch(); // OK: d2 is Dog handle
end else begin
$error("Cast failed");
end
end
endmodule
Interview Questions
Q1: What is polymorphism in SystemVerilog?
Answer: Polymorphism allows a base class handle to reference derived class objects and call overridden methods. The actual method executed is determined at runtime based on the object type, not the handle type.
Q2: What keyword enables polymorphism?
Answer: The virtual keyword. It must be declared in the base class; derived classes inherit it automatically.
Q3: What happens without the virtual keyword?
Answer: The method call is resolved at compile-time based on the handle type. The base class method is called even if the object is of a derived type.
Q4: How does UVM use polymorphism?
Answer: UVM uses polymorphism extensively in the factory (type overrides), sequences, callbacks, and phase execution. It allows flexible, configurable testbenches without modifying base code.
Key Takeaways
virtualkeyword enables polymorphism - declare in base class- Virtual methods: object type determines which method runs
- Non-virtual methods: handle type determines which method runs
virtualis inherited - no need to redeclare in derived classes- Use
$cast()for downcasting (base handle to derived handle) - UVM relies heavily on polymorphism for flexibility and reuse
Comments (0)
Leave a Comment