HomeSystemVerilog VerificationDay 9
DAY 9 · OOP PHASE

SystemVerilog Parameterized Classes & Factory Pattern

By EcrioniX · Updated Jun 13, 2026

Classes can be parameterized just like Verilog modules, allowing you to create reusable templates with configurable data widths, depths, and types. The factory pattern centralizes object creation: a factory method creates different transaction types based on a string or enum, eliminating scattered new() calls throughout the testbench.

Parameterized classes

A parameterized class accepts parameters specified at instantiation:

Syntax

class fifo #(int WIDTH=32, int DEPTH=16);
  logic [WIDTH-1:0] data [DEPTH];
  int wr_ptr, rd_ptr;

  function new();
    wr_ptr = 0; rd_ptr = 0;
  endfunction

  function void put(logic [WIDTH-1:0] item);
    data[wr_ptr] = item;
    wr_ptr = (wr_ptr + 1) % DEPTH;
  endfunction
endclass

// Instantiation with overrides:
fifo #(.WIDTH(64), .DEPTH(32)) f32 = new();
fifo #(.WIDTH(8))              f8 = new();

The factory pattern

A factory method creates objects based on input parameters, centralizing object creation logic. Instead of scattered conditionals throughout the testbench, all creation logic lives in one place:

// WITHOUT factory (scattered):
if (type == "read") tr = new read_transaction();
else if (type == "write") tr = new write_transaction();
else if (type == "burst") tr = new burst_transaction();

// WITH factory (centralized):
tr = transaction_factory::create(type);

Complete factory example

transaction_factory.sv
// Base transaction class
class transaction;
  rand logic [15:0] addr;
  rand logic [7:0]  data;
  virtual function void display();
    $display("  addr=%h data=%h", addr, data);
  endfunction
endclass

class read_transaction extends transaction;
  logic [7:0] read_data;
  virtual function void display();
    super.display();
    $display("  + read_data=%h", read_data);
  endfunction
endclass

class write_transaction extends transaction;
  logic [3:0] byte_enable;
  virtual function void display();
    super.display();
    $display("  + byte_enable=%b", byte_enable);
  endfunction
endclass

class burst_transaction extends transaction;
  int unsigned burst_len;
  virtual function void display();
    super.display();
    $display("  + burst_len=%0d", burst_len);
  endfunction
endclass

// ---- FACTORY CLASS ----
class transaction_factory;
  // Factory method: creates objects based on type string
  static function transaction create(string tx_type);
    case (tx_type)
      "read": begin
        read_transaction rd = new();
        return rd;
      end
      "write": begin
        write_transaction wr = new();
        return wr;
      end
      "burst": begin
        burst_transaction br = new();
        br.burst_len = 4;
        return br;
      end
      default: begin
        $fatal(1, "Unknown transaction type: %s", tx_type);
      end
    endcase
  endfunction
endclass

// ---- TESTBENCH USING FACTORY ----
module factory_demo;
  initial begin
    transaction q[$];

    // Create different types using factory
    q.push_back(transaction_factory::create("read"));
    q.push_back(transaction_factory::create("write"));
    q.push_back(transaction_factory::create("burst"));
    q.push_back(transaction_factory::create("read"));

    // All stored in base class queue, factory handled creation
    foreach (q[i]) begin
      $display("Transaction %0d:", i);
      q[i].display();
    end

    $finish;
  end
endmodule

Why use the factory pattern?

Parameterized classes with factories

Combine both for maximum flexibility:

class fifo #(int WIDTH=32, int DEPTH=16);
  // ... FIFO implementation
endclass

class fifo_factory;
  static function fifo create_fifo(string config);
    case (config)
      "small":  return new fifo#(.WIDTH(8),  .DEPTH(16));
      "medium": return new fifo#(.WIDTH(32), .DEPTH(64));
      "large":  return new fifo#(.WIDTH(64), .DEPTH(256));
    endcase
  endfunction
endclass

Day 9 takeaways

Frequently Asked Questions

What are parameterized classes?

Classes can have type or value parameters like Verilog modules. Example: class fifo #(int WIDTH=32) specifies a parameter with a default value. At instantiation: fifo #(.WIDTH(64)) f = new() overrides the default.

What is the factory pattern?

A static method that creates and returns objects based on input parameters. Instead of scattered conditionals, the factory centralizes all object creation logic in one place.

When should I use a factory?

When many different transaction types or object configurations are created throughout the testbench. The factory eliminates scattered new() calls and makes adding new types easier.

Previous
← Day 8: Inheritance

← Full course roadmap