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

AboutContactPrivacyTerms
Skip to lesson

Course contents

  • Start Here6
  • SystemVerilog Language Foundations28
  • Object-Oriented SystemVerilog for Verification30
  • Constraint Randomization — Interview-Critical Deep Dive36
    • Randomization Basics: rand, randc & randomize()
    • Constraint Operators & Expressions
    • Dynamic Constraint Control
      • rand_mode(): Freezing Fields
      • constraint_mode(): Toggling Blocks
      • Inline Constraints: randomize() with
      • Constraint Override via Inheritance
      • State Variables and Cross-Object
    • Randomizing Arrays and Queues
    • How the Constraint Solver Works
    • Debugging Randomization Failures
    • Interview Drills — Constraints and Randomization Q&A
  • 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

‹Dynamic Constraint Control
  1. Topics
  2. SystemVerilog
  3. Constraint Randomization — Interview-Critical Deep Dive
  4. Dynamic Constraint Control
  5. Constraint Override via Inheritance

Part 3 · Constraint Randomization · Intermediate

Constraint Override via Inheritance

Same-name replacement, test personalities, factory substitution, and override or toggle.

On this page

    Why this matters

    A jumbo-frame test still produced short frames. Its subclass gave the length rule a new name, which added to the parent's rule instead of replacing it.

    After this lesson you can: replace or extend inherited constraint blocks, build test personalities as subclasses, and install them for every sequence with a factory.

    Same name replaces, new name adds

    Constraint blocks inherit by name. A subclass block with the parent's name fully replaces that block for subclass objects, while a new name adds another block that intersects with everything inherited.

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    // snippet: the classes, run in the bench below
    class base_txn;
      rand bit [7:0] len;
      rand bit [3:0] prio;
      constraint len_c  { len inside {[1:16]}; }
      constraint prio_c { prio < 8; }
    endclass
    class jumbo_txn extends base_txn;
      // Same name: REPLACES the parent block, so relaxation works
      constraint len_c { len inside {[1:255]}; }
    endclass
    class urgent_txn extends base_txn;
      // New name: ADDS, so it meets the inherited blocks
      constraint urgent_c { prio inside {[6:7]}; }
    endclass
    // jumbo_txn:  len in [1:255] (parent [1:16] is gone), prio < 8
    // urgent_txn: len in [1:16], prio in {6, 7} from prio<8 met with [6:7]

    Replacement is total, so an override must restate any parent expression it still wants. Small single-purpose blocks keep that restatement cheap.

    Test personalities

    The methodology pattern: the base carries protocol legality only, and each test family adds a thin subclass that reshapes distributions or drops one rule. The subclass is named, reviewable, and reusable.

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    // snippet: two personalities of one base class
    class axi_txn;
      rand bit [31:0] addr;
      rand bit [7:0]  len;
      rand bit        write;
      constraint legal_c { len inside {[1:16]}; addr[1:0] == 0; }
      constraint shape_c { soft write dist { 1 :/ 1, 0 :/ 1 }; }
    endclass
    // Personality 1: write-heavy small bursts near a hotspot
    class hotspot_txn extends axi_txn;
      constraint shape_c {   // replaces the base shape_c
        write dist { 1 :/ 9, 0 :/ 1 };
        len inside {[1:4]};
        addr inside {[32'h8000:32'h80FF]};
      }
    endclass
    // Personality 2: out-of-spec lengths for error checking
    class badlen_txn extends axi_txn;
      constraint legal_c { addr[1:0] == 0; }   // replaces: the len rule is dropped
      constraint bad_c   { len inside {[17:64]}; }
    endclass

    Factory substitution

    Override scales when a factory builds the objects: sequences keep creating the base type while the test installs a subclass. Every randomize() in every reused sequence then solves the subclass set with zero sequence edits.

    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
    36
    37
    38
    class axi_txn;
      rand bit [31:0] addr;
      rand bit [7:0]  len;
      constraint legal_c { len inside {[1:16]}; addr[1:0] == 0; }
      virtual function axi_txn clone_empty();
        axi_txn t = new();
        return t;
      endfunction
    endclass
    class hotspot_txn extends axi_txn;
      constraint legal_c { len inside {[1:4]}; addr[1:0] == 0; }
      virtual function axi_txn clone_empty();
        hotspot_txn t = new();
        return t;   // subclass object behind a base-typed handle
      endfunction
    endclass
    // The same idea without UVM: a virtual create hook
    class txn_factory;
      static axi_txn proto = null;   // the test installs one prototype
      static function axi_txn create();
        if (proto == null) begin axi_txn t = new(); return t; end
        return proto.clone_empty();  // virtual call returns the subclass instance
      endfunction
    endclass
    module tb_factory;
      initial begin
        hotspot_txn proto;            // declarations first, statements after
        axi_txn t;
        proto = new();
        txn_factory::proto = proto;   // one line in the test reshapes every sequence
        t = txn_factory::create();
        void'(t.randomize());         // solves the hotspot_txn block set
        if (!(t.len inside {[1:4]}))
          $fatal(1, "FAIL: len=%0d, the base legal_c was solved", t.len);
        $display("PASS: the factory returned a hotspot_txn, len=%0d", t.len);
        $finish;
      end
    endmodule
    Terminal
    PASS: the factory returned a hotspot_txn, len=3
    1. The test installs the override: build hotspot_txn wherever axi_txn is asked for.

    2. Every reusable sequence still asks the factory for an axi_txn, unchanged.

    3. It receives a hotspot_txn, so each randomize() solves the overridden legal_c.

    4. One line in the test reshapes every transaction in every sequence.

    Testbench

    Predict first jumbo_txn redeclares len_c as len inside {[1:255]}. urgent_txn adds a new block urgent_c { prio inside {[6:7]}; }. Can a jumbo_txn have len == 200? Can an urgent_txn have prio == 7 and len == 200?

    Yes for jumbo: the same name replaced the parent's 1 to 16. No for urgent: a new name adds to the inherited blocks, so len is still 1 to 16 (and prio == 7 is fine).

    The base class and three subclasses from this lesson, 1,000 solves each.

    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
    36
    37
    38
    class base_txn;
      rand bit [7:0] len;
      rand bit [3:0] prio;
      constraint len_c  { len inside {[1:16]}; }
      constraint prio_c { prio < 8; }
    endclass
    
    class jumbo_txn extends base_txn;
      constraint len_c { len inside {[1:255]}; }      // same name: REPLACES the parent block
    endclass
    
    class urgent_txn extends base_txn;
      constraint urgent_c { prio inside {[6:7]}; }    // new name: ADDS to the inherited blocks
    endclass
    
    class badlen_txn extends base_txn;
      constraint len_c { len inside {[17:64]}; }      // replaces: out-of-spec lengths for error tests
    endclass
    
    module tb_override;
      base_txn b; jumbo_txn j; urgent_txn u; badlen_txn x;
      int jumbo_big = 0;
      initial begin
        b = new(); j = new(); u = new(); x = new();
        repeat (300) begin
          if (!b.randomize() || !j.randomize() || !u.randomize() || !x.randomize()) $fatal(1, "FAIL: randomize failed");
          if (b.len < 1 || b.len > 16 || b.prio >= 8) $fatal(1, "FAIL: base len %0d prio %0d", b.len, b.prio);
          if (j.len == 0 || j.prio >= 8) $fatal(1, "FAIL: jumbo len %0d prio %0d", j.len, j.prio);
          if (j.len > 16) jumbo_big++;
          if (u.len < 1 || u.len > 16 || !(u.prio inside {6, 7})) $fatal(1, "FAIL: urgent len %0d prio %0d", u.len, u.prio);
          if (x.len < 17 || x.len > 64) $fatal(1, "FAIL: badlen len %0d", x.len);
        end
        $display("jumbo lengths above 16: %0d of 300", jumbo_big);
        if (jumbo_big == 0) $fatal(1, "FAIL: the jumbo override did not widen len");
        $display("PASS: same-name blocks replaced the parent rule, a new block added to it, over 300 solves each");
        $finish;
      end
    endmodule
    Terminal
    jumbo lengths above 16: 258 of 300
    PASS: same-name blocks replaced the parent rule, a new block added to it, over 300 solves each

    Common mistakes

    Common pitfall

    Same-named parent and child blocks do not merge: replacement is total, so an override that forgets a parent expression silently drops a rule. The override link is also just the block name, so renaming a parent block breaks every subclass bound to it.

    Interview

    Likely questions: what a same-named subclass block does (full replacement, enabling relaxation), how one test reshapes every sequence (subclass plus factory override), and override or toggle (standing type policy or temporary instance switch).

    Check yourself A subclass redeclares the parent block len_c with a wider range. What happens to the parent expressions?

    They are gone for subclass objects. Replacement is total, so the override must restate anything it still wants, which is why blocks stay small and single-purpose.

    Check yourself When does override beat constraint_mode?

    When the change is a standing policy for a whole test rather than a temporary mid-sequence toggle. Override is per-type with nothing to restore, while mode is per-instance sticky state.

    Choice

    Scope

    Restore needed

    Subclass override

    Every instance of the subtype

    None: the type carries the policy.

    constraint_mode toggle

    One object, until changed

    Yes: re-enable what was disabled.

    Factory substitution

    Every sequence creating the base

    None: the test owns the install.

    In the layer map, inheritance answers what standing personality a test type carries. Use it for per-test policy, never for one-off directed values.

    Remember

    • Same block name replaces the parent block completely; a new name adds to it.

    • A replacing block must restate any parent rule it still wants.

    • A factory override reshapes every sequence without editing any of them.

    Rule of thumb: name the block you mean to replace exactly as the parent named it.

    How this was checked

    Run with Verilator 5.048 (--binary --timing) on 2026-10-18; every bench printed the PASS line shown. Verilator prints a harmless WIDTHTRUNC warning for if (!obj.randomize()), because randomize() returns an int, and an UNSATCONSTR warning when a solve fails on purpose. Icarus 13 does not support constraints.

    Related topics

    • Next: State Variables and Cross-Object

    Previous

    76 / 259

    Next: State Variables and Cross-Object

    Up next

    Up nextState Variables and Cross-Object

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • rand_mode(): Freezing Fields
    • constraint_mode(): Toggling Blocks