Object-Oriented Programming & Interfaces
Master classes, inheritance, polymorphism, and interface design for scalable SystemVerilog testbenches.
🏗️ Core OOP Concepts
Classes & Objects
FundamentalFoundation of object-oriented programming in SystemVerilog for testbench development.
Key Topics:
- •Class declaration and instantiation
- •Constructor and destructor methods
- •Properties and methods
- •Access modifiers (local, protected)
- •Static members and methods
Syntax:
// Basic class structure
class packet;
// Properties
rand bit [7:0] data;
rand bit [3:0] addr;
local bit [15:0] checksum;
// Constructor
function new();
data = 8'h00;
addr = 4'h0;
endfunction
// Methods
function void display();
$display("Packet: addr=%0h, data=%0h", addr, data);
endfunction
function bit [15:0] calc_checksum();
return data + addr;
endfunction
// Static method
static function packet create_default();
packet p = new();
return p;
endfunction
endclassComplete Example:
// Complete packet class example
class ethernet_packet;
// Random properties
rand bit [47:0] dest_addr;
rand bit [47:0] src_addr;
rand bit [15:0] ethertype;
rand byte payload[];
// Constraints
constraint payload_size {
payload.size() inside {[64:1500]};
}
constraint valid_ethertype {
ethertype inside {16'h0800, 16'h0806, 16'h86DD};
}
// Constructor
function new(string name = "ethernet_packet");
this.name = name;
endfunction
// Methods
virtual function void post_randomize();
$display("[%s] Generated packet: DA=%h, SA=%h",
name, dest_addr, src_addr);
endfunction
virtual function bit compare(ethernet_packet pkt);
return (this.dest_addr == pkt.dest_addr &&
this.src_addr == pkt.src_addr &&
this.ethertype == pkt.ethertype);
endfunction
virtual function ethernet_packet clone();
ethernet_packet cloned = new();
cloned.copy(this);
return cloned;
endfunction
endclassInheritance & Polymorphism
IntermediateExtend base classes and implement polymorphic behavior for scalable testbench architectures.
Key Topics:
- •Class inheritance with extends
- •Method overriding and virtual functions
- •Abstract classes and pure virtual methods
- •Polymorphism and dynamic binding
- •Super keyword for parent access
Syntax:
// Inheritance syntax
class base_packet;
rand bit [7:0] length;
virtual function void display();
$display("Base packet: length=%0d", length);
endfunction
pure virtual function bit [15:0] calc_crc();
endclass
class tcp_packet extends base_packet;
rand bit [15:0] src_port;
rand bit [15:0] dest_port;
// Override parent method
virtual function void display();
super.display(); // Call parent
$display("TCP: src_port=%0d, dest_port=%0d",
src_port, dest_port);
endfunction
// Implement pure virtual
virtual function bit [15:0] calc_crc();
return src_port ^ dest_port ^ length;
endfunction
endclassComplete Example:
// Complete inheritance example
virtual class base_transaction;
protected int transaction_id;
protected time timestamp;
static protected int next_id = 0;
function new();
transaction_id = next_id++;
timestamp = $time;
endfunction
pure virtual function void print();
pure virtual function base_transaction clone();
virtual function int get_id();
return transaction_id;
endfunction
endclass
class cpu_transaction extends base_transaction;
rand bit [31:0] addr;
rand bit [31:0] data;
rand bit write;
constraint addr_align {
addr[1:0] == 2'b00; // Word aligned
}
function new();
super.new();
endfunction
virtual function void print();
$display("[%0t] CPU Transaction ID=%0d: %s addr=%h data=%h",
timestamp, transaction_id,
write ? "WRITE" : "READ", addr, data);
endfunction
virtual function base_transaction clone();
cpu_transaction cloned = new();
cloned.addr = this.addr;
cloned.data = this.data;
cloned.write = this.write;
return cloned;
endfunction
endclass
class dma_transaction extends base_transaction;
rand bit [31:0] src_addr;
rand bit [31:0] dest_addr;
rand int transfer_size;
constraint size_limit {
transfer_size inside {[1:1024]};
transfer_size % 4 == 0; // Word transfers
}
virtual function void print();
$display("[%0t] DMA Transaction ID=%0d: src=%h dest=%h size=%0d",
timestamp, transaction_id, src_addr, dest_addr, transfer_size);
endfunction
virtual function base_transaction clone();
dma_transaction cloned = new();
cloned.src_addr = this.src_addr;
cloned.dest_addr = this.dest_addr;
cloned.transfer_size = this.transfer_size;
return cloned;
endfunction
endclassInterfaces & Modports
AdvancedCreate clean, reusable interface definitions with modports for different perspectives.
Key Topics:
- •Interface declaration and instantiation
- •Modport definitions for different views
- •Clocking blocks for synchronous designs
- •Interface methods and tasks
- •Parameterized interfaces
Syntax:
// Basic interface structure
interface cpu_bus_if (input logic clk);
logic [31:0] addr;
logic [31:0] data;
logic write;
logic valid;
logic ready;
// Clocking block
clocking cb @(posedge clk);
output addr, data, write, valid;
input ready;
endclocking
// Modports
modport master (clocking cb, output valid);
modport slave (input addr, data, write, valid,
output ready);
modport monitor (input addr, data, write, valid, ready);
endinterfaceComplete Example:
// Complete interface example with methods
interface axi4_lite_if #(
parameter ADDR_WIDTH = 32,
parameter DATA_WIDTH = 32
)(input logic aclk, input logic aresetn);
// Write address channel
logic [ADDR_WIDTH-1:0] awaddr;
logic [2:0] awprot;
logic awvalid;
logic awready;
// Write data channel
logic [DATA_WIDTH-1:0] wdata;
logic [DATA_WIDTH/8-1:0] wstrb;
logic wvalid;
logic wready;
// Write response channel
logic [1:0] bresp;
logic bvalid;
logic bready;
// Read address channel
logic [ADDR_WIDTH-1:0] araddr;
logic [2:0] arprot;
logic arvalid;
logic arready;
// Read data channel
logic [DATA_WIDTH-1:0] rdata;
logic [1:0] rresp;
logic rvalid;
logic rready;
// Clocking blocks
clocking master_cb @(posedge aclk);
output awaddr, awprot, awvalid, wdata, wstrb, wvalid;
output bready, araddr, arprot, arvalid, rready;
input awready, wready, bresp, bvalid;
input arready, rdata, rresp, rvalid;
endclocking
clocking slave_cb @(posedge aclk);
input awaddr, awprot, awvalid, wdata, wstrb, wvalid;
input bready, araddr, arprot, arvalid, rready;
output awready, wready, bresp, bvalid;
output arready, rdata, rresp, rvalid;
endclocking
clocking monitor_cb @(posedge aclk);
input awaddr, awprot, awvalid, awready;
input wdata, wstrb, wvalid, wready;
input bresp, bvalid, bready;
input araddr, arprot, arvalid, arready;
input rdata, rresp, rvalid, rready;
endclocking
// Modports
modport master (clocking master_cb);
modport slave (clocking slave_cb);
modport monitor (clocking monitor_cb);
// Interface methods
task automatic write_transaction(
input [ADDR_WIDTH-1:0] addr,
input [DATA_WIDTH-1:0] data,
input [DATA_WIDTH/8-1:0] strb = '1
);
// Write address phase
master_cb.awaddr <= addr;
master_cb.awprot <= 3'b000;
master_cb.awvalid <= 1'b1;
// Write data phase
master_cb.wdata <= data;
master_cb.wstrb <= strb;
master_cb.wvalid <= 1'b1;
// Wait for address ready
do @(master_cb); while (!master_cb.awready);
master_cb.awvalid <= 1'b0;
// Wait for data ready
do @(master_cb); while (!master_cb.wready);
master_cb.wvalid <= 1'b0;
// Wait for response
master_cb.bready <= 1'b1;
do @(master_cb); while (!master_cb.bvalid);
master_cb.bready <= 1'b0;
if (master_cb.bresp != 2'b00)
$error("Write transaction failed with response: %0d", master_cb.bresp);
endtask
task automatic read_transaction(
input [ADDR_WIDTH-1:0] addr,
output [DATA_WIDTH-1:0] data,
output [1:0] resp
);
// Read address phase
master_cb.araddr <= addr;
master_cb.arprot <= 3'b000;
master_cb.arvalid <= 1'b1;
master_cb.rready <= 1'b1;
// Wait for address ready
do @(master_cb); while (!master_cb.arready);
master_cb.arvalid <= 1'b0;
// Wait for read data
do @(master_cb); while (!master_cb.rvalid);
data = master_cb.rdata;
resp = master_cb.rresp;
master_cb.rready <= 1'b0;
if (resp != 2'b00)
$error("Read transaction failed with response: %0d", resp);
endtask
// Assertions
property write_addr_stable;
@(posedge aclk) disable iff (!aresetn)
awvalid && !awready |=> $stable(awaddr) && $stable(awprot);
endproperty
property write_data_stable;
@(posedge aclk) disable iff (!aresetn)
wvalid && !wready |=> $stable(wdata) && $stable(wstrb);
endproperty
assert_write_addr_stable: assert property(write_addr_stable);
assert_write_data_stable: assert property(write_data_stable);
endinterface🎨 Common Design Patterns
Factory Pattern
Create objects without specifying exact classes
Use Case:
Dynamic test generation
Benefit:
Flexible object creation
Example:
class transaction_factory;
static function base_transaction create(string type);
case (type)
"CPU": return new cpu_transaction();
"DMA": return new dma_transaction();
default: return null;
endcase
endfunction
endclassObserver Pattern
Notify multiple objects about state changes
Use Case:
Coverage and monitoring
Benefit:
Loose coupling
Example:
class scoreboard;
mailbox #(transaction) mb;
task run();
transaction tr;
forever begin
mb.get(tr);
check_transaction(tr);
end
endtask
endclassStrategy Pattern
Encapsulate algorithms and make them interchangeable
Use Case:
Different test scenarios
Benefit:
Runtime algorithm selection
Example:
virtual class test_strategy;
pure virtual task execute();
endclass
class random_test extends test_strategy;
virtual task execute();
// Random stimulus generation
endtask
endclassCommand Pattern
Encapsulate requests as objects
Use Case:
Test sequencing
Benefit:
Undo/redo, queuing
Example:
virtual class command;
pure virtual task execute();
pure virtual task undo();
endclass
class write_command extends command;
bit [31:0] addr, data;
virtual task execute();
// Perform write
endtask
endclass🔧 Interface Best Practices
Use Modports
Define clear directional views for different components
Implementation:
modport master (output req, input ack); modport slave (input req, output ack);
Clocking Blocks
Synchronize interface signals to clock edges
Implementation:
clocking cb @(posedge clk); output data, valid; input ready; endclocking
Interface Methods
Encapsulate common operations within interfaces
Implementation:
task write(input [31:0] addr, data); @(cb) cb.addr <= addr; cb.data <= data; cb.valid <= 1; wait(cb.ready); endtask
Parameterization
Make interfaces reusable with parameters
Implementation:
interface bus_if #( parameter ADDR_WIDTH = 32, parameter DATA_WIDTH = 32 )(input logic clk);
⚡ Benefits of OOP in Verification
Code Reusability
Inherit and extend existing classes
Modularity
Encapsulate related data and methods
Polymorphism
Single interface, multiple implementations
Abstraction
Hide implementation details
Ready to Build Advanced Testbenches?
Apply OOP principles and interface design to create scalable, maintainable verification environments.