Constraints in SystemVerilog

Constraints are rules that guide the random solver to generate legal and meaningful values. Without constraints, random values would be meaningless - you'd get addresses outside your memory range or invalid packet sizes. Let's learn how to write effective constraints.

Why Do We Need Constraints?

Unconstrained randomization generates any possible value. For a 32-bit address, that's 4 billion possibilities! Most of those might be invalid for your DUT. Constraints let you focus on legal and interesting values.

Think of constraints like a game's rules. In cricket, you can't just throw the ball anywhere - there are rules about where it should bounce. Constraints are the "rules" for what random values are acceptable.

Basic Constraint Syntax

Constraint Block Syntax
class Packet;
    rand bit [31:0] addr;
    rand bit [7:0]  length;
    rand bit        read_write;
    
    // Constraint block
    constraint addr_range {
        addr >= 32'h1000;
        addr < 32'h2000;
    }
    
    constraint valid_length {
        length inside {[1:64]};  // 1 to 64 bytes
    }
endclass

Types of Constraints

Simple Relational Constraints

Relational Constraints
constraint simple_constraints {
    addr > 0;                    // Greater than
    addr < 32'hFFFF;             // Less than
    length <= 128;               // Less than or equal
    length >= 4;                 // Greater than or equal
    length == 64;                // Exact value
    length != 0;                 // Not equal
}

Inside Constraints

Use inside to specify a set or range of valid values:

Inside Constraints
constraint inside_examples {
    // Specific values
    length inside {4, 8, 16, 32, 64};
    
    // Range
    addr inside {[32'h1000:32'h1FFF]};
    
    // Mix of values and ranges
    size inside {1, 2, [4:8], 16, 32};
    
    // Exclude values (NOT inside)
    !(addr inside {[32'hDEAD:32'hDEAF]});
}

Implication Constraints

Use -> for "if-then" conditions:

Implication Constraints
constraint if_then_rules {
    // If read, then length must be 4
    (read_write == 0) -> (length == 4);
    
    // If write, then data shouldn't be zero
    (read_write == 1) -> (data != 0);
    
    // Burst mode requires aligned address
    (burst_mode == 1) -> (addr[1:0] == 0);
}

Implication vs if-else

Implication a -> b means "if a is true, then b must be true". If a is false, b can be anything. It's not the same as if-else!

Distribution Constraints

Want some values to appear more often than others? Use dist to control the probability distribution.

Distribution Constraints
constraint probability_examples {
    // :=  means weight applies to each value
    // :/  means weight is divided among range
    
    // 80% reads, 20% writes
    read_write dist {0 := 80, 1 := 20};
    
    // Small packets are more common
    length dist {
        [1:16]   := 60,    // 60% chance for 1-16
        [17:32]  := 30,    // 30% chance for 17-32
        [33:64]  := 10     // 10% chance for 33-64
    };
    
    // Using :/ - weight divided among range
    addr dist {
        [0:99]   :/ 50,    // Each value gets 50/100 = 0.5
        [100:109] :/ 50    // Each value gets 50/10 = 5
    };                     // So 100-109 are 10x more likely!
}

:= vs :/ Confusion

:= assigns weight to each value in range. :/ divides weight among values. This is a common interview question and source of bugs!

Soft Constraints

soft constraints are "preferred but not required". They can be overridden by inline constraints. Regular constraints cannot be overridden - they must always be satisfied.

Soft Constraints
class Transaction;
    rand bit [7:0] data;
    rand bit [3:0] size;
    
    // Soft constraint - can be overridden
    constraint default_size {
        soft size == 4;  // Prefer size 4, but can be changed
    }
    
    // Hard constraint - must always be true
    constraint valid_size {
        size inside {1, 2, 4, 8, 16};
    }
endclass

// Usage:
Transaction t = new();
t.randomize();                    // size = 4 (default)
t.randomize() with {size == 8};   // size = 8 (overrides soft)
t.randomize() with {size == 3};   // ERROR! Violates hard constraint

When to Use Soft?

Use soft for default values that tests might want to override. Common in base transaction classes.

Solve-Before

solve...before controls the order in which variables are solved. This affects the probability distribution when variables depend on each other.

Solve-Before Example
class Packet;
    rand bit mode;           // 0 or 1
    rand bit [7:0] length;
    
    constraint length_rule {
        mode == 0 -> length inside {[1:10]};    // 10 values
        mode == 1 -> length inside {[1:100]};   // 100 values
    }
    
    // Without solve-before:
    // All 110 solutions equally likely
    // mode=0 has 10/110 = 9% chance
    // mode=1 has 100/110 = 91% chance
    
    // With solve-before:
    constraint solve_mode {
        solve mode before length;
    }
    // Now mode is solved first (50-50)
    // Then length is solved based on mode
    // mode=0 has 50% chance, mode=1 has 50% chance
endclass

Interview Favorite!

solve-before is a very common interview topic. Remember: it affects probability distribution, not the legal solution space.

Constraint Control

You can enable/disable constraints at runtime:

Enabling/Disabling Constraints
class Transaction;
    rand bit [31:0] addr;
    
    constraint aligned_addr {
        addr[1:0] == 0;  // Word aligned
    }
    
    constraint high_addr {
        addr >= 32'h8000_0000;
    }
endclass

Transaction t = new();

// Disable a constraint
t.aligned_addr.constraint_mode(0);  // Turn off alignment
t.randomize();                       // addr can be unaligned

// Re-enable
t.aligned_addr.constraint_mode(1);

// Check if constraint is active
if (t.aligned_addr.constraint_mode())
    $display("Constraint is ON");

Quick Summary

  • Basic: Use relational operators (<,>, ==, !=)
  • inside: Specify sets or ranges of values
  • ->: Implication for conditional constraints
  • dist: Control probability distribution (:= vs :/)
  • soft: Default values that can be overridden
  • solve-before: Control solving order for probability
  • constraint_mode(): Enable/disable at runtime

What's Next?

Learn more advanced randomization techniques: