SystemVerilog constraints are an important part of functional verification because they allow engineers to generate legal, varied stimulus without manually specifying every transaction. In verification interviews, candidates are often asked to write a constraint for a packet, generate unique array values, control random distributions, or identify why randomize() fails.
Interviewers are evaluating more than syntax. They want to know whether you can translate a specification into mathematical conditions, understand how constraints interact, and debug cases where the solver cannot find a valid solution.
This guide covers practical SystemVerilog constraint coding problems for verification interviews, with examples, explanations, and common mistakes. The examples use class-based randomization, which is commonly used to generate transactions in SystemVerilog testbenches.
Generate a Random Number Within a Range
Interview problem: Write a constraint that generates an 8-bit address between 32 and 127, inclusive.
class address_item;
rand bit [7:0] addr;
constraint addr_range_c {
addr inside {[8'd32:8'd127]};
}
endclass
Explanation: The inside operator restricts addr to the specified inclusive range. Because addr is an 8-bit unsigned variable, its normal representable values are 0 through 255, but the constraint narrows the legal set to 32 through 127.
To test the constraint:
address_item item = new();
repeat (10) begin
if (!item.randomize())
$error("Randomization failed");
else
$display("Address = %0d", item.addr);
end
The example checks the return value of randomize() rather than assuming every call succeeds.
Follow-up question: How would you allow only even addresses in the same range? Add addr[0] == 1'b0; to the constraint block. The least significant bit of an even unsigned integer is zero.
Generate Only Even or Odd Random Numbers
Interview problem: Generate a random 8-bit number that is always even.
class number_item;
rand bit [7:0] value;
constraint even_c {
value[0] == 1'b0;
}
endclass
For odd values, change the condition to:
constraint odd_c {
value[0] == 1'b1;
}
Why this works: In binary representation, the least significant bit determines whether an unsigned integer is even or odd. An even number ends in zero, while an odd number ends in one.
Practical use: This type of constraint can model aligned addresses, where a bus or memory protocol requires addresses to be aligned to a particular boundary.
For example, a four-byte-aligned address must have its two least significant bits equal to zero:
constraint align_4_c {
addr[1:0] == 2'b00;
}
Make sure the alignment condition matches the actual protocol specification. Not every interface requires the same alignment.
Generate Unique Values in an Array
Interview problem: Generate four distinct random values, each between 1 and 15.
class unique_array_item;
rand bit [3:0] data[4];
constraint data_range_c {
foreach (data[i])
data[i] inside {[1:15]};
}
constraint data_unique_c {
unique {data};
}
endclass
Explanation: The foreach constraint applies the range restriction to every array element. The unique constraint requires all listed elements to have different values.
Since there are four elements and fifteen possible values, a solution exists.
Common mistake: Trying to generate more unique elements than the number of legal values. For example, requiring sixteen distinct 4-bit values while excluding zero leaves only fifteen legal values, so randomization cannot satisfy the constraint.
This problem tests whether you understand both syntax and the number of available solutions.
Constrain One Random Variable Based on Another
Interview problem: A transaction has a write flag and an 8-bit address. Require write transactions to use addresses from 0x80 to 0xFF, and read transactions to use addresses from 0x00 to 0x7F.
class bus_transaction;
rand bit write;
rand bit [7:0] addr;
constraint addr_by_type_c {
if (write)
addr inside {[8'h80:8'hFF]};
else
addr inside {[8'h00:8'h7F]};
}
endclass
Explanation: The if-else constraint expresses two mutually exclusive conditions. When write is one, the upper half of the address range is legal. When write is zero, the lower half is legal.
An equivalent logical relationship can be written with implication:
constraint addr_by_type_c {
(write == 1'b1) -> (addr inside {[8'h80:8'hFF]});
(write == 1'b0) -> (addr inside {[8'h00:8'h7F]});
}
Interview follow-up: What if both branches accidentally restrict addr to non-overlapping values while another constraint forces it into a third range? The combined constraints may become unsatisfiable. Always consider all active constraints together.
Constrain Packet Length and Payload Size
Interview problem: A packet has a randomized length and a payload array. Generate packets containing between 1 and 16 payload bytes, with the array size matching the length.
class packet_item;
rand bit [4:0] length;
rand byte unsigned payload[];
constraint length_c {
length inside {[1:16]};
payload.size() == length;
}
endclass
Explanation: The length is limited to the range 1 through 16. The dynamic array size must equal the randomized length, so the generated transaction has a consistent number of payload bytes.
This is a useful example because real verification environments often need relationships between transaction metadata and data structures.
Follow-up questions:
- How would you restrict the length to powers of two?
- How would you constrain a particular header field based on the packet type?
- What happens if another constraint requires the payload array to contain 20 elements?
For the first question, one option is to enumerate the legal values with inside, such as {1, 2, 4, 8, 16}. If a protocol has special length rules, encode those rules explicitly rather than assuming all packet sizes are valid.
Generate Values With a Weighted Distribution
Interview problem: Generate a two-bit transaction type where type 0 should receive weight 1, type 1 weight 3, and type 2 weight 6.
class transaction_type_item;
rand bit [1:0] kind;
constraint kind_dist_c {
kind inside {[0:2]};
kind dist {
0 := 1,
1 := 3,
2 := 6
};
}
endclass
Explanation: The inside constraint limits the legal values to 0, 1, and 2. The dist constraint assigns relative weights to these values.
These weights express a desired relative distribution, not a guarantee that every small batch will contain exactly 10%, 30%, and 60% of each value. Observed frequencies vary across random samples and may depend on the simulator's constraint-solving behavior.
Interview follow-up: What is the difference between := and :/ in a distribution constraint? They specify different ways to apply weights to ranges: := assigns the specified weight to each value in the range, while :/ distributes the specified weight across the range. Be careful when weighting a single value against a range containing many values.
For a real verification plan, check the observed distribution over an adequate number of trials and use functional coverage to confirm that required scenarios are exercised.
Generate a Number Sequence Using an Array Constraint
Interview problem: Generate five 8-bit values in strictly increasing order.
class increasing_array_item;
rand bit [7:0] data[5];
constraint increasing_c {
foreach (data[i]) {
if (i < 4)
data[i] < data[i+1];
}
}
endclass
Explanation: For every element except the last, the constraint requires the next element to be larger. The if condition avoids accessing an element beyond the end of the array.
Follow-up question: How would you generate a non-decreasing sequence, where equal adjacent values are allowed? Replace < with <=.
Common mistake: Writing an array constraint without checking index boundaries. Array constraints should cover the intended elements while avoiding invalid index references.
This exercise tests array iteration, relational operators, and whether you understand how foreach expands across the array.
Constrain the Sum of Array Elements
Interview problem: Generate four 4-bit unsigned values whose sum is exactly 20.
class array_sum_item;
rand bit [3:0] data[4];
constraint sum_c {
data.sum() with (int'(item)) == 20;
}
endclass
Explanation: The array reduction method sum() adds the elements. The with clause casts each element to an integer before summation, avoiding a narrow intermediate element type from unintentionally limiting the arithmetic result.
Each element can range from 0 to 15, so a sum of 20 is possible.
Follow-up question: Can the sum be 100? No. The maximum sum is (4 \times 15 = 60), so a sum of 100 would be impossible for this array.
This is a good interview problem because you must check whether the requested sum is feasible before blaming the solver.
Control a Variable's Distribution With solve before
Interview problem: A one-bit mode selects between two address ranges. How can you influence the distribution of the mode values when one branch allows many more address values than the other?
class mode_item;
rand bit mode;
rand bit [7:0] addr;
constraint mode_addr_c {
if (mode == 1'b0)
addr inside {[8'h00:8'h03]};
else
addr inside {[8'h00:8'hFF]};
}
constraint mode_order_c {
solve mode before addr;
}
endclass
Explanation: The constraint solve mode before addr affects how the solver distributes solutions across the variables. Here, mode zero has only four legal addresses, while mode one has 256. Without considering solution distribution, it is easy to assume the mode values will appear equally often when they may not.
The ordering constraint can help make the distribution of mode more representative of its own legal choices before resolving the dependent address. It does not change which combinations are legal, and it does not replace the need to check observed frequencies.
Interview tip: Explain the difference between legality and distribution. Constraints determine which combinations are permitted; distribution controls influence how frequently legal choices are selected. solve before is not a procedural assignment sequence.
Debug a Randomization Failure
Interview problem: Find why this class cannot randomize successfully.
class bad_item;
rand bit [7:0] value;
constraint c1 {
value inside {[0:10]};
}
constraint c2 {
value > 20;
}
endclass
The first constraint requires a value from 0 through 10, while the second requires a value greater than 20. No value satisfies both conditions.
A safe way to detect the problem is:
bad_item item = new();
if (!item.randomize())
$error("No legal solution for bad_item");
How to debug it:
- Check the return value of randomize().
- Review all active class constraints.
- Check any inline constraints passed to randomize().
- Look for incompatible ranges, equality conditions, or array sizes.
- Temporarily isolate constraints to identify the conflict, then restore the intended specification.
Do not simply disable a constraint to make randomization pass. First determine which condition is incorrect or whether the specification itself is inconsistent.
Use Inline Constraints for a Specific Test
Interview problem: A class normally generates addresses from 0x00 through 0xFF, but one test needs an address from 0xF0 through 0xFF.
class address_generator;
rand bit [7:0] addr;
constraint default_addr_c {
addr inside {[8'h00:8'hFF]};
}
endclass
A test can add an inline constraint:
address_generator gen = new();
if (!gen.randomize() with {
addr inside {[8'hF0:8'hFF]};
})
$error("Address randomization failed");
Explanation: The inline constraint applies to that randomization call. It is combined with the class's active constraints, so the resulting address must satisfy both conditions.
This technique is useful when a general transaction class supports many tests but an individual test needs to focus on a specific range or corner case.
Common mistake: Assuming inline constraints override conflicting class constraints automatically. If the constraints have no common solution, randomization fails.
How Should You Prepare for Constraint Coding Interviews?
Practise writing and explaining each constraint rather than memorizing code. For every problem, ask:
- What are the legal values?
- Which variables depend on other variables?
- Can all the constraints be satisfied simultaneously?
- Are the array sizes and index boundaries valid?
- Does the specification require a particular distribution?
- How will I detect a randomization failure?
- How will I confirm the generated stimulus reaches the required scenarios?
Start with ranges and parity, then move to conditional constraints, arrays, uniqueness, distributions, and packet relationships. Finally, practise debugging contradictory constraints and explaining how constraint solving differs from procedural programming.
For more practice, see VLSIGuru’s UVM Interview Questions and Answers — Set 1 and UVM Interview Questions and Answers — Set 2. InSkill also offers Functional Verification Interview Preparation, which includes constraint theory, array randomization, pattern generation, and related verification exercises.
Conclusion
SystemVerilog constraint coding interviews test whether you can express verification requirements accurately and reason about the resulting legal solution space. Freshers should be comfortable with inside, foreach, unique, dist, conditional constraints, array reductions, inline constraints, and randomization-failure debugging.
The strongest answers explain not only how to write a constraint, but also why it works, when it becomes unsatisfiable, and how you would verify the generated results. Practise these problems in a SystemVerilog simulator, inspect the output over multiple randomization calls, and connect each exercise to a realistic verification requirement.