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 randcase is and how it differs from an ordinary case statement
  • 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

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

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

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:

  • randcase randomly 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
  • randcase is a standalone statement — it doesn't need a rand variable or a call to randomize()

Continuing the Randomization Chapter

Continue with SystemVerilog Array Randomization in Class to see how rand and randc apply to arrays, queues, and dynamic arrays.

Did you find what you were looking for?

Quiz

Test Your Knowledge

1 of 5 Score: 0 / 5
Cron Job Starts