Skip to content
DenseChip
Learn
PracticeCode labInterview

DenseChip

Navigate

Menu

LearnPracticeCode labInterview

Tools

All toolsSV editorCode repoResume
DenseChip

Practice, courses, and chip-design tools in one focused workspace.

LearnPracticeCode labInterviewToolsLearning paths

© 2026 DenseChip

AboutRoadmapCareersInternshipsFAQContactPrivacyTerms
Skip to lesson

Course contents

  • Start Here6
  • SystemVerilog Language Foundations28
  • Object-Oriented SystemVerilog for Verification30
    • Classes, Objects & Handles
    • Inheritance and Polymorphism
    • Static, Encapsulation & Parameterized Classes
      • Static Properties & Methods
      • local, protected & Encapsulation
      • Parameterized Classes
      • Constants & Enums in Classes
      • extern Methods & Class File Organization
    • Virtual Interfaces: the Class-to-Pins Bridge
    • Events, Semaphores, Mailboxes and Processes
    • OOP Patterns for Testbenches
  • Constraint Randomization — Interview-Critical Deep Dive36
  • SystemVerilog Assertions — Interview-Critical Deep Dive40
  • Functional Coverage in SystemVerilog22
  • Testbench Architecture in SystemVerilog23
  • Advanced Language & Integration25
  • Senior & Interview Preparation21
  • Principal-Level Verification28
Loading lesson

‹Static, Encapsulation & Parameterized Classes
  1. Topics
  2. SystemVerilog
  3. Object-Oriented SystemVerilog for Verification
  4. Static, Encapsulation & Parameterized Classes
  5. Static Properties & Methods
On this page

    Part 2 of 10, lesson 10 of 30Part 2 of 10/OOP for Verification/Lesson 10 of 30Intermediate

    Static Properties & Methods

    One shared counter per class, class-scope access and the limits of static methods.

    Two environments in one simulation checked each other's packets. Their expected queue was static, so it was one queue shared by both.

    After this lesson you can: use static properties and methods, read them through the class scope, and tell class-wide state from per-object state.

    One copy for the whole class

    An instance property gets a fresh copy in every object made by new(). A static property gets exactly one copy owned by the class, shared by every object.

    The static copy is initialized once at its declaration and lives for the whole simulation. It never waits for the first new().

    A static lives for the whole run; an instance property lives with its object.

    Member

    Copies

    Lifetime

    Typical use

    Instance property

    One per object

    From new() until the object is reclaimed

    Address, data, per-object id

    Static property

    One per class

    Whole simulation, set once at declaration

    Population count, next unique id

    The transaction ID counter

    The standard idiom stamps each transaction with a unique id by incrementing a shared static counter inside new(). The id makes every log line traceable to one object.

    Read statics through the class scope, as bus_txn::count. Handle access such as a.count is legal but reads like instance state.

    Static method limits

    A static method belongs to the class and runs with no object behind the call. It therefore has no this handle, reaches instance members only through an explicit object handle, and can never be virtual.

    Virtual dispatch needs an object whose type selects the override. A static call has no object, so the combination is illegal.

    Testbench

    Predict first The bench builds a, b and c in that order. What are c.id and bus_txn::count afterwards?

    2 and 3. Each new() stamps the current count as the id, then advances the one shared counter.

    The counter, the class-scope reads and the static-method rules in one self-checking file.

    top.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    class bus_txn;
      static int count = 0;   // one copy for the whole class
      int id;                 // one copy per object
      int unsigned data;
    
      function new(int unsigned d);
        id = count;           // stamp unique id
        count++;              // shared counter advances
        data = d;
      endfunction
    
      // Legal: no 'this'; reads static state directly
      static function int how_many();
        return count;
      endfunction
    
      // ILLEGAL: static methods have no 'this', so these never compile:
      // static function int get_id();
      //   return id;          // no instance 'id' to read
      // endfunction
      // static virtual function void note();  // static cannot be virtual
    endclass
    
    module top;
      bus_txn a;
      bus_txn b;
      bus_txn c;
      int errors = 0;
      initial begin
        a = new(32'hA1);   // a.id = 0, count = 1
        b = new(32'hB2);   // b.id = 1, count = 2
        c = new(32'hC3);   // c.id = 2, count = 3
        if (a.id !== 0) errors++;
        if (b.id !== 1) errors++;
        if (c.id !== 2) errors++;
        if (bus_txn::count !== 3) errors++;
        if (bus_txn::how_many() !== 3) errors++;
        if (a.count !== bus_txn::count) errors++;   // handle read sees the same variable
        if (errors == 0) $display("PASS: three txns stamped 0,1,2 with one shared count of 3");
        else $fatal(1, "FAIL: id or shared-count values differ from 0,1,2 and 3");
        $finish;
      end
    endmodule
    Terminal
    PASS: three txns stamped 0,1,2 with one shared count of 3

    Common mistakes

    Static state is shared across the entire simulation, not per environment instance. Two agents sharing a static queue silently couple environments that must stay independent.

    The id counter is safe because ids only need global uniqueness. A static expected queue or static configuration knob is a bug of the same shape.

    Per-environment state made static corrupts every extra instance. Stale statics also leak across tests in multi-test flows, so reset or account for counters at each test start.

    Likely question: what breaks if a checker's expected queue is static?

    • Every environment instance shares one queue.

    • Expected items from one instance match checks from another.

    • The fix is an instance queue, with statics kept for globally unique ids.

    Check yourself Handle a is declared but no object exists yet. Which read is legal: bus_txn::count or a.count?

    Both. A static is reached through the class, and even through a null handle of that class. Prefer bus_txn::count, because a.count reads like instance state.

    Check yourself A static method tries to return an instance field. What does the compiler say?

    It reports an error. The static method has no this handle, so there is no instance field to read.

    Give pkt_txn a packet number: a static next_id and an instance id stamped in new(). Every log line can then name the exact packet it is about.

    One way to write it:

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    typedef enum bit [1:0] {DATA = 2'd0, CTRL = 2'd1, ERR = 2'd2} kind_e;
    
    class pkt_txn;
      static int next_id = 0;   // one counter for every packet in the run
      int        id;            // this packet's own number
      kind_e     kind;
      bit [1:0]  dest;
      bit [3:0]  len;
    
      function new(kind_e k = DATA, bit [1:0] d = 0, bit [3:0] n = 1);
        id = next_id++;
        kind = k; dest = d; len = n;
      endfunction
    
      function string sprint();
        return $sformatf("pkt#%0d %s dest=%0d len=%0d", id, kind.name(), dest, len);
      endfunction
    endclass
    
    module tb_pkt_id;
      pkt_txn q[$];
      pkt_txn p;
      int errors = 0;
      initial begin
        p = new(DATA, 2, 2); q.push_back(p);
        p = new(CTRL, 1, 1); q.push_back(p);
        p = new(DATA, 0, 8); q.push_back(p);
        foreach (q[i]) $display("%s", q[i].sprint());
        foreach (q[i]) if (q[i].id != i) errors++;
        if (pkt_txn::next_id != 3) errors++;
        if (errors == 0) $display("PASS: three packets numbered 0, 1, 2 from one static counter");
        else $fatal(1, "FAIL: %0d id checks failed", errors);
        $finish;
      end
    endmodule
    Terminal
    pkt#0 DATA dest=2 len=2
    pkt#1 CTRL dest=1 len=1
    pkt#2 DATA dest=0 len=8
    PASS: three packets numbered 0, 1, 2 from one static counter
    • A static property has one copy per class, for the whole run.

    • Static methods have no this and can never be virtual.

    • Only truly global facts, like a next-id counter, belong in statics.

    Rule of thumb: static means one for everybody: use it only when that is true.

    How this was checked

    Run with Verilator 5.048 (--binary --timing) on 2026-10-18; every bench printed the PASS line shown, with no warnings. These class benches were run on Verilator only.

    Related topics

    • Next: local, protected & Encapsulation

    Previous

    44 / 259

    Next: local, protected & Encapsulation

    Up next

    Up nextlocal, protected & Encapsulation

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Parameterized Classes
    • Constants & Enums in Classes
    • extern Methods & Class File Organization