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
      • inside, Ranges & Set Membership
      • dist: Weighted Distributions
      • Implication, if/else and solve...before
      • soft Constraints
    • Dynamic Constraint Control
    • 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

‹Constraint Operators & Expressions
  1. Topics
  2. SystemVerilog
  3. Constraint Randomization — Interview-Critical Deep Dive
  4. Constraint Operators & Expressions
  5. Implication, if/else and solve...before

Part 3 · Constraint Randomization · Intermediate

Implication, if/else and solve...before

Implication truth tables, bidirectional solving and solve-before shaping.

On this page

    Why this matters

    Error packets never appeared in 10,000 runs. The error flag implied one magic address, so almost every legal solution had the flag clear, and nothing in the code looked wrong.

    After this lesson you can: read implication and if/else as truth tables, predict what the solver forces in both directions, and say what solve...before changes and what it cannot.

    Implication is not-a or b

    The expression a -> b reads as follows: when a holds, b must hold. Formally it is (!a) || b, so three of the four truth-table rows satisfy it.

    When a is false, the implication says nothing about b. The false branch stays fully open unless another constraint pins it.

    a

    b

    a -> b holds?

    false

    false

    Yes: b is free when a is false

    false

    true

    Yes: b is free when a is false

    true

    false

    No: the only forbidden row

    true

    true

    Yes

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    // snippet: checked in the bench below
    class dma_txn;
      rand bit       small_mode;
      rand bit [7:0] len;
    
      constraint len_c { small_mode -> len <= 8; }
    endclass
    // small_mode==1 implies len in 0..8.
    // small_mode==0 leaves len in 0..255: full range, NOT len above 8.

    The classic wrong answer claims the false branch means the opposite. It does not: the opposite needs an else arm or a second implication.

    Solving runs both ways

    Constraints have no left-to-right direction. The solver treats a -> b as one relation over every variable on both sides and solves them together.

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // snippet: checked in the bench below
    class bi_demo;
      rand bit       flag;
      rand bit [3:0] val;
    
      constraint c1 { flag -> val == 9; }
      constraint c2 { val inside {[0:7]}; }   // 9 is impossible here
    endclass
    // flag==1 needs val==9, but c2 forbids 9, so every solution has flag==0.
    // randomize still SUCCEEDS here: solutions exist, with flag forced to 0.

    Implication also skews probability: with c1 alone, one legal pair has the flag set and sixteen do not. Bidirectional Solving works through those numbers; this lesson is about what is legal.

    if/else pins both branches

    An if/else constraint is sugar for a pair of implications. The statement if (a) b; else c; means (a -> b) && (!a -> c) exactly.

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    // snippet: checked in the bench below
    class pkt;
      rand bit        jumbo;
      rand bit [13:0] len;
    
      // Both branches pinned down, with no open hole:
      constraint len_c {
        if (jumbo) len inside {[1501:9000]};
        else       len inside {[64:1500]};
      }
    
      // Identical meaning written as two implications:
      constraint len_c2 {
        jumbo  -> len inside {[1501:9000]};
        !jumbo -> len inside {[64:1500]};
      }
    endclass

    Both forms stay bidirectional. A later inline constraint such as len == 5000 forces the solver to pick jumbo==1.

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    // snippet: a richer shape, same rules
    class mem_txn;
      typedef enum {READ, WRITE, ATOMIC} op_e;
      rand op_e      op;
      rand bit [3:0] burst;
      rand bit       locked;
    
      constraint shape_c {
        // Atomic ops are single-beat and locked
        op == ATOMIC -> (burst == 1 && locked == 1);
        // Writes longer than 8 beats stay unlocked
        (op == WRITE && burst > 8) -> locked == 0;
      }
    endclass
    // (op==WRITE && burst>8) -> locked==0 is the flattened form of
    // op==WRITE -> (burst>8 -> locked==0): identical solution set.

    Form

    Constrained rows

    Open rows

    a -> b

    a true requires b true

    a false leaves b free

    if (a) b; else c;

    Both branches pinned

    No open hole

    (a && b) -> c

    Flattened chain, one forbidden combo

    All rows with !(a && b)

    solve before reshapes probability

    The statement solve a before b changes probability only, never the solution set. The solver picks a first over its own legal values, then picks b among values consistent with that choice. With flag -> val == 9, the standard gives the flag about a 1 in 17 chance without the ordering and about 1 in 2 with it; Bidirectional Solving has the counting.

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // snippet: the two classes, run in the bench below
    class skew_demo;
      rand bit       flag;
      rand bit [3:0] val;
      constraint c { flag -> val == 9; }
    endclass
    
    class shaped_demo;
      rand bit       flag;
      rand bit [3:0] val;
      constraint c { flag -> val == 9; }
      constraint order_c { solve flag before val; }
    endclass
    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    // snippet: errors only at one address, made likely by ordering
    class bus_txn;
      rand bit        inject_err;
      rand bit [31:0] addr;
      constraint err_c {
        inject_err -> addr == 32'hDEAD_BEEF;
        solve inject_err before addr;
      }
    endclass

    Ordering and dist compose: dist sets the flag ratio while solve...before stops the partner variable from drowning it. The ordering rules are strict and worth memorizing.

    Rule

    Consequence

    Only rand variables may be ordered

    randc is implicitly solved first and cannot appear in the list.

    Cycles are illegal

    solve a before b plus solve b before a is rejected.

    Lists give a partial order

    solve a, b before c, d orders the groups, not every pair.

    Independent variables ignore it

    With no shared constraint, ordering changes nothing.

    Failures stay failures

    An empty solution set cannot be fixed by any ordering.

    Common pitfall

    Ordering cannot fix a contradiction, and it cannot produce an illegal value. A failing randomize call is a set problem, not an order problem.

    Interview

    Count the pairs out loud: 1 pair with the flag set against 16 without gives about 1/17, and solving the flag first restores about 1/2. Lead with the sentence that legality never changes.

    Check yourself Does solve flag before val change which (flag, val) pairs are legal?

    No. The reachable pairs are bit-for-bit identical with and without it. Only the probability weights move.

    Check yourself Why is the flag so unlikely without the hint when the partner is 32 bits wide?

    Pair counting: one pair carries the flag set against about four billion without it. The interview model gives about 1 in 2^32, so error traffic effectively never appears.

    Testbench

    Predict first The bench solves skew_demo and shaped_demo 1,700 times each. Can either class ever produce flag == 1 with val != 9?

    No. Ordering never changes what is legal, so every solve of both classes is one of the 17 legal pairs; only how often each pair appears can change.

    Three classes from this lesson solved 2,000 times, checking that each implication binds only its true branch and that solving runs both ways; then the two ordering classes, checking legality and printing the measured flag rates.

    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
    39
    40
    41
    42
    43
    44
    class dma_txn;
      rand bit       small_mode;
      rand bit [7:0] len;
      constraint len_c { small_mode -> len <= 8; }
    endclass
    
    class bi_demo;
      rand bit       flag;
      rand bit [3:0] val;
      constraint c1 { flag -> val == 9; }
      constraint c2 { val inside {[0:7]}; }   // 9 is impossible here
    endclass
    
    class pkt;
      rand bit        jumbo;
      rand bit [13:0] len;
      constraint len_c {
        if (jumbo) len inside {[1501:9000]};
        else       len inside {[64:1500]};
      }
    endclass
    
    module tb_implication;
      dma_txn d = new();
      bi_demo b = new();
      pkt     p = new();
      int big_when_off = 0, flag_set = 0;
      initial begin
        repeat (600) begin
          if (!d.randomize()) $fatal(1, "FAIL: dma randomize failed");
          if (d.small_mode && d.len > 8) $fatal(1, "FAIL: small_mode with len %0d", d.len);
          if (!d.small_mode && d.len > 8) big_when_off++;      // the false branch is open
          if (!b.randomize()) $fatal(1, "FAIL: bi_demo randomize failed");
          if (b.flag) flag_set++;                               // c2 forces flag to 0
          if (!p.randomize()) $fatal(1, "FAIL: pkt randomize failed");
          if (p.jumbo != (p.len > 1500)) $fatal(1, "FAIL: jumbo %0d with len %0d", p.jumbo, p.len);
        end
        if (!p.randomize() with { len == 5000; }) $fatal(1, "FAIL: len == 5000 failed");
        $display("small_mode == 0 with len > 8: %0d of 600; bi_demo flag set: %0d; len 5000 chose jumbo = %0d", big_when_off, flag_set, p.jumbo);
        if (big_when_off == 0 || flag_set != 0 || p.jumbo != 1) $fatal(1, "FAIL: the solver did not solve both ways");
        $display("PASS: the implication bound only its true branch, c2 forced flag to 0, and len 5000 forced jumbo");
        $finish;
      end
    endmodule
    Terminal
    small_mode == 0 with len > 8: 538 of 600; bi_demo flag set: 0; len 5000 chose jumbo = 1
    PASS: the implication bound only its true branch, c2 forced flag to 0, and len 5000 forced jumbo
    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
    class skew_demo;
      rand bit       flag;
      rand bit [3:0] val;
      constraint c { flag -> val == 9; }
    endclass
    
    class shaped_demo;
      rand bit       flag;
      rand bit [3:0] val;
      constraint c { flag -> val == 9; }
      constraint order_c { solve flag before val; }
    endclass
    
    module tb_solve_order;
      skew_demo   s = new();
      shaped_demo h = new();
      int n = 1700;
      int sf = 0, hf = 0;
      int pairs_s[int], pairs_h[int];
      initial begin
        repeat (n) begin
          if (!s.randomize() || !h.randomize()) $fatal(1, "FAIL: randomize failed");
          if ((s.flag && s.val != 9) || (h.flag && h.val != 9)) $fatal(1, "FAIL: an illegal pair");
          sf += s.flag;
          hf += h.flag;
          pairs_s[{s.flag, s.val}]++;
          pairs_h[{h.flag, h.val}]++;
        end
        $display("legal pairs reached: without solve-before %0d, with it %0d (17 exist)", pairs_s.num(), pairs_h.num());
        $display("flag == 1: without solve-before %0d of %0d (%0.1f%%), with it %0d (%0.1f%%)", sf, n, 100.0 * sf / n, hf, 100.0 * hf / n);
        $display("standard: about 5.9%% without solve-before, about 50%% with it");
        $display("PASS: all %0d solves of both classes were legal pairs", 2 * n);
        $finish;
      end
    endmodule
    Terminal
    legal pairs reached: without solve-before 17, with it 16 (17 exist)
    flag == 1: without solve-before 54 of 1700 (3.2%), with it 0 (0.0%)
    standard: about 5.9% without solve-before, about 50% with it
    PASS: all 3400 solves of both classes were legal pairs

    The printed rates are this simulator's, not the standard's: it does not follow the solve...before probability rule (in this run the flag was never set with the ordering), while every pair it produced was legal. Interview answers use the standard's numbers.

    Remember

    • a -> b forbids only a true with b false; b is free when a is false.

    • if/else pins both branches; the solver solves every constraint in both directions.

    • solve...before moves probability only; it never changes what is legal.

    Rule of thumb: legality first, probability second, and never mix them up in an answer.

    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.

    Verilator 5.048 does not follow the standard's solve...before probabilities: in this lesson's bench the flag was never set with the ordering (the standard gives about 50%), and in another probe it reached about 30%. Every solve was legal, which is all the bench asserts.

    Related topics

    • Next: soft Constraints

    Previous

    71 / 259

    Next: soft Constraints

    Up next

    Up nextsoft Constraints

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • inside, Ranges & Set Membership