SystemVerilog's randcase statement is a case-like construct that randomly picks exactly one of its branches to execute, weighted by positive integer values you assign to each branch. Unlike an ordinary case statement, where the branch that runs depends on comparing an expression against each item, no expression decides which randcase branch runs — the solver does, based purely on the weights you give it.
6 min read | Intermediate Level
What You'll Learn
- What
randcaseis and how it differs from an ordinarycasestatement - How branch weights convert into selection probabilities
- What happens with a zero-weight branch, and with every branch weighted zero
- How SystemVerilog picks a branch internally from a single random draw
Syntax: randcase
Each branch is a positive integer weight followed by a colon and a statement to execute if that branch is chosen:
randcase
item : statement;
...
endcase
Parameters:
item— a non-negative integer expression representing that branch's weight (zero is legal, and simply means the branch is never taken)statement— the statement executed if this branch is selected
The probability of any one branch being selected is that branch's weight divided by the sum of all the weights in the block.
Weighted Random Branch Selection
In the example below, the weights are 1, 5, and 3, which sum to 9. That makes the first branch's probability 1/9 (11.11%), the second branch's probability 5/9 (55.56%), and the third branch's probability 3/9 (33.33%):
module tb;
initial begin
for (int i = 0; i < 10; i++)
randcase
1 : $display ("Wt 1");
5 : $display ("Wt 5");
3 : $display ("Wt 3");
endcase
end
endmodule
Output
ncsim> run Wt 5 Wt 5 Wt 3 Wt 5 Wt 1 Wt 3 Wt 5 Wt 3 Wt 3 Wt 5 ncsim: *W,RNQUIE: Simulation is complete.
Note that Wt 5 appeared the most (the highest-probability branch), Wt 1 appeared the least (the lowest-probability branch), and Wt 3 landed somewhere in between — exactly what the weights predict over enough iterations.
Quick Check
Q: If the three weights above were changed to 2, 2, and 2 instead of 1, 5, and 3, what would the probability of each branch be?
Show Answer
An equal 1/3 (33.33%) each. Probability only depends on each weight relative to the sum — 2/6, 2/6, and 2/6 all reduce to the same 1/3, just like 3/9, 3/9, 3/9 would.
Zero-Weight Branches Are Skipped
If a branch specifies a weight of zero, that branch is simply never taken:
module tb;
initial begin
for (int i = 0; i < 10; i++)
randcase
0 : $display ("Wt 0");
5 : $display ("Wt 5");
3 : $display ("Wt 3");
endcase
end
endmodule
Output
ncsim> run Wt 5 Wt 5 Wt 3 Wt 5 Wt 5 Wt 3 Wt 5 Wt 3 Wt 3 Wt 5 ncsim: *W,RNQUIE: Simulation is complete.
Wt 0 never appears in the output — with a weight of 0, that branch contributes nothing to the probability calculation, so it can never be the one selected.
What Happens When All Weights Are Zero
Weighting every branch at zero doesn't make sense in practice, but SystemVerilog defines what happens if it occurs: no branch is taken, and the simulator issues a run-time warning instead of a compile-time error.
module tb;
initial begin
for (int i = 0; i < 10; i++)
randcase
0 : $display ("Wt 0.1");
0 : $display ("Wt 0.2");
0 : $display ("Wt 0.3");
endcase
end
endmodule
Output
ncsim> run
ncsim: *W,RANDNOB: The sum of the weight expressions in the randcase statement is 0.
No randcase branch was taken.
File: ./testbench.sv, line = 4, pos = 14
Scope: tb.unmblk1
Time: 0 FS + 0
ncsim: *W,RANDNOB: The sum of the weight expressions in the randcase statement is 0.
No randcase branch was taken.
File: ./testbench.sv, line = 4, pos = 14
Scope: tb.unmblk1
Time: 0 FS + 0
ncsim: *W,RANDNOB: The sum of the weight expressions in the randcase statement is 0.
No randcase branch was taken.
File: ./testbench.sv, line = 4, pos = 14
Scope: tb.unmblk1
Time: 0 FS + 0
...
This warning is easy to miss in a large regression log, especially if the weights come from variables rather than literal numbers — if a weight variable unexpectedly evaluates to 0 for every branch, the intended randomized behavior silently doesn't happen at all.
How the Solver Picks a Branch
Each call to randcase draws one random number in the range 0 up to the sum of the weights. The branches are then checked in declaration order — the first (top) branch's weight covers the smallest random numbers, the next branch covers the following range, and so on — so whichever branch's range contains the drawn number is the one that executes.
Where Is randcase Used?
randcase shows up whenever you want weighted, probabilistic branching without wrapping a variable in a class just to constrain its distribution — for example, mostly generating normal read/write transactions but occasionally injecting an error scenario, or picking between a handful of delay profiles with different likelihoods.
Common Beginner Mistakes
Mistake #1: Assuming weights must add up to 100
randcase weights are not percentages. Why? Probability is always that branch's weight divided by the sum of all weights, so 1, 5, 3 works exactly the same as 10, 50, 30 or 100, 500, 300 — only the ratios between weights matter, not their absolute values.
Mistake #2: Expecting a compile-time error when all weights are zero
An all-zero randcase is legal SystemVerilog — it just does nothing at run time and prints a warning. Why? Because it's a run-time condition (the weights could be variables that only evaluate to zero under certain circumstances), the tool can't catch it at compile time, so nothing stops your simulation from silently skipping that branch selection every single time.
Mistake #3: Confusing randcase with a dist constraint
randcase is a standalone procedural statement that doesn't need a rand variable or a call to randomize() at all — it just runs and picks a branch. Why? A weighted dist constraint, by contrast, only takes effect the next time randomize() is called on the class that contains it. If you need weighted branching outside of a class, randcase is usually the simpler tool; see SystemVerilog Constraints for dist.
Practice Exercise
Challenge: Weighted Transaction Type Selector
Write a randcase block that picks between three transaction types — "READ", "WRITE", and "ERROR" — so that READ and WRITE are equally likely and ERROR is much rarer, roughly a 1-in-20 chance.
Hint
Give READ and WRITE the same weight, and give ERROR a weight that's about 1/20th of that when all three weights are added together.
Solution
module tb;
initial begin
repeat (10) begin
randcase
10 : $display ("READ");
10 : $display ("WRITE");
1 : $display ("ERROR");
endcase
end
end
endmodule
The weights sum to 21, so READ and WRITE are each 10/21 (about 47.6%) — equally likely as each other — while ERROR is only 1/21 (about 4.8%), close to the roughly 1-in-20 target.
Summary
In this tutorial, you learned:
randcaserandomly executes exactly one of its branches, weighted by positive integer values- A branch's selection probability is its weight divided by the sum of all the weights
- A zero-weight branch is never selected; if every branch is weighted zero, none of them run and the simulator issues a run-time warning
- SystemVerilog draws one random number per call and matches it against the branches in declaration order to pick one
randcaseis a standalone statement — it doesn't need arandvariable or a call torandomize()
Continuing the Randomization Chapter
Continue with SystemVerilog Array Randomization in Class to see how rand and randc apply to arrays, queues, and dynamic arrays.