Why Constrained Random?
On Day 11, you learned how to generate test scenarios with fork/join. But there's a problem: manually writing every test case doesn't scale. A real chip may have 64-bit addresses, multiple protocol variations, and thousands of edge cases. You can't hand-code them all.
Constrained random generation solves this:
- ✅ Generate millions of stimulus combinations automatically
- ✅ Declare constraints, not test cases (e.g., "addr must be 4KB-aligned and in DRAM range")
- ✅ Solver finds valid values that satisfy all rules
- ✅ Explore corner cases you wouldn't think to write
Result: 99% fewer lines of testbench code, and far better coverage.
The rand & randc Keywords
In SystemVerilog, declare random fields like this:
class Packet;
rand bit [31:0] addr; // Random 32-bit address
rand int data; // Random integer
randc bit [3:0] cmd; // Random cyclic (0..15, all used before repeat)
int fixed_id; // NOT random (static)
endclass
Packet pkt = new();
pkt.randomize(); // Solver fills in random values
$display("Addr: %0h, Cmd: %0d", pkt.addr, pkt.cmd);
rand vs randc
| Keyword | Behavior | Use Case |
|---|---|---|
rand | Independent random each call | Addresses, payload, timing (any value any time) |
randc | Random cyclic: all values in type used before repeating | IDs, opcodes (want full coverage of enum) |
Example: If cmd is randc bit [1:0], it will generate: 0, 3, 1, 2, 0, 1, 3, 2, ... (permutations). Guaranteed to hit all 4 values before repeating.
Constraint Blocks & Rules
A constraint block is a set of Boolean expressions that the randomizer must satisfy. Declare them with the constraint keyword:
class AXI_Request;
rand bit [31:0] addr;
rand bit [7:0] len;
rand bit [2:0] size;
// Constraint block: address must be aligned to size
constraint align {
(size == 3'b000) -> addr[0:0] == 0; // 1-byte: any addr
(size == 3'b001) -> addr[1:0] == 0; // 2-byte: 2-aligned
(size == 3'b010) -> addr[3:0] == 0; // 4-byte: 4-aligned
(size == 3'b011) -> addr[7:0] == 0; // 8-byte: 8-aligned
}
// Another constraint: length must be 1-256
constraint len_range {
len >= 1; len <= 256;
}
// Yet another: forbidden region
constraint no_bootrom {
addr < 32'h0000_0000 || addr >= 32'h0001_0000;
}
endclass
Key points:
- ✅ Multiple constraint blocks are ANDed (all must be true)
- ✅ Use
->(implication) to write conditional constraints - ✅ Constraints apply to all randomization calls
- ✅ If no solution exists,
randomize()returns0
Weighted & Guided Distributions
Sometimes you don't want uniform random. You want weighted random: hit certain values more often. Use dist or inside:
class Traffic;
rand bit [7:0] opcode;
rand bit [31:0] burst_len;
// Weighted distribution: READ 50%, WRITE 30%, IDLE 20%
constraint opcode_dist {
opcode dist { 8'h01 := 50, 8'h02 := 30, 8'h00 := 20 };
}
// Use inside for range: stress long bursts (80%) vs short (20%)
constraint burst_stress {
burst_len inside { [1024:65535] := 80, [1:1023] := 20 };
}
endclass
Syntax: dist { value1 := weight1, value2 := weight2, ... }
- Weights are proportional (don't need to sum to 100)
- Use
[min:max]for ranges (all values in range get equal weight) - Common for stress testing protocol edge cases
Implication Constraints (->)
The -> operator is logical implication: if the left side is true, the right side must be true.
class Memory_Access;
rand bit is_write;
rand bit [31:0] addr;
rand bit [31:0] data;
rand bit [3:0] mask;
// If write, data and mask are random. If read, ignore them.
constraint write_deps {
is_write -> (mask != 0); // Write: mask must not be all zeros
!is_write -> (mask == 0); // Read: mask must be zero (ignored)
}
// Another: unaligned reads not allowed, aligned writes okay
constraint alignment {
(addr[1:0] != 0) -> is_write; // If misaligned, MUST be write
}
endclass
Rules:
A -> Bmeans: IF A, THEN B (equivalent to!A || B)- If A is false, B is unconstrained
- Use for conditional logic and dependencies
Full Testbench Example: AXI Protocol
Let's build a constrained-random AXI write request generator:
class AXI_Write_Req;
rand bit [11:0] awid;
rand bit [31:0] awaddr;
rand bit [7:0] awlen;
rand bit [2:0] awsize;
rand bit [1:0] awburst;
// ID in range 0-4095
constraint id_c { awid inside { [0:4095] }; }
// Address 4-byte aligned (size = 2, so 4-byte transfers)
constraint addr_c { awsize == 3'b010; awaddr[1:0] == 0; }
// Length 1-256 beats
constraint len_c { awlen >= 0; awlen <= 255; }
// Burst type: 0=FIXED, 1=INCR, 2=WRAP (no 3)
constraint burst_c { awburst inside { [0:2] }; }
// WRAP length: if WRAP, len must be power-of-2 aligned
constraint wrap_c {
(awburst == 2) -> (awlen inside { [0,1,3,7,15,31,63,127,255] });
}
function void print();
$display("[AXI_W] ID=%0d, Addr=%08h, Len=%0d, Size=%0d, Burst=%0d",
awid, awaddr, awlen, awsize, awburst);
endfunction
endclass
module axi_gen_tb();
AXI_Write_Req req;
initial begin
req = new();
repeat(1000) begin
if(req.randomize()) begin
req.print();
end else begin
$display("ERROR: Failed to randomize!");
end
end
$finish;
end
endmodule
Run this and 1,000 different valid AXI requests are generated, all satisfying alignment, range, and protocol rules. No manual test case writing!
Controlling Randomization
You can enable/disable constraints and reseed the random number generator:
class Packet;
rand bit [7:0] payload;
constraint size_c { payload inside { [0:127] }; }
endclass
Packet p = new();
// Normal randomization
p.randomize(); // Constraints applied
// Disable a specific constraint
p.size_c.constraint_mode(0);
p.randomize(); // payload can be 0-255 now
// Re-enable
p.size_c.constraint_mode(1);
// Seed for reproducibility
srandom(12345);
p.randomize(); // Same sequence every run
FAQ & Takeaways
A:
randomize() returns 0 (false). Check your constraints are logically consistent. Use $display to verify a few values generate correctly.
A: SystemVerilog uses a SAT (satisfiability) solver behind the scenes. It finds variable assignments that make all constraints true. You don't write the solver—just declare the rules.
A: Not after class definition, but you can inherit and add new constraints in child classes (see Day 8: inheritance).
🎯 Takeaways:
- ✅
rand= independent random;randc= cyclic (all values before repeat) - ✅ Constraints are ANDed—all must be satisfied
- ✅ Use
distfor weighted random (stress testing) - ✅ Use
->for conditional constraints (implication) - ✅
randomize()returns 0 if unsatisfiable - ✅ **One constraint block can reduce 10,000 lines of hand-written tests to 50**
Tomorrow (Day 13): Functional coverage — measure what you've tested, not just how many tests you ran.