A completed design specification and an exercised testbench are not the same thing. The specification states intended behavior, while the testbench must create stimulus that tests whether the implementation meets it. Bridging that gap takes more than translating text into code. It requires identifying the input sequences that reveal the behaviors the specification defines.
SystemVerilog constraint-random features provide the language for expressing that translation. The difficult part is ensuring the constraint blocks represent design intent instead of an engineer's assumptions about likely behavior. Over-constraining is the usual failure: transactions look valid, but the constraints exclude the corner cases where bugs tend to appear.
Intent to Constraint
A design specification defines the legal behavior space: valid inputs, expected responses, and permitted ordering. It seldom explains that space in terms of probability or coverage. An AXI specification permits burst lengths from 1 to 256 beats. It does not warn that using only 4-beat bursts leaves important behavior untested, although that often becomes the default when no length distribution is declared in the constraint block.
The result is a familiar translation mistake: the engineer sees "burst length 1 to 256" and writes:
constraint c_burst_len {
burst_len inside {[1:256]};
}
Relative to the specification, that constraint is valid. The simulator's constraint solver will, however, select lengths with an approximately uniform distribution from 1 to 256. The design may behave differently at length 1, near the maximum, and at cache line boundaries of 16, 32, and 64 beats. Uniform sampling reaches those values only occasionally. A deliberate distribution looks like this:
constraint c_burst_len {
burst_len dist {
1 := 20,
[2:15] := 20,
[16:63] := 30,
[64:255] := 20,
256 := 10
};
}
Both blocks satisfy the specification. The second also captures where the design is likely to show meaningful behavior. That perspective comes from examining the specification alongside the RTL, rather than relying on the specification in isolation.
Building Constraints from Protocol State Descriptions
A practical way to connect specification intent to constraint structure is to derive the constraints from a protocol state machine instead of reconstructing them from prose and memory.
Specifications for protocols such as AXI, PCIe, and CHI can be modeled as state machines. States represent protocol phases, and transitions describe valid event sequences. When BigEndian parses an interface specification, it creates a state machine that exposes and enumerates legal sequences rather than leaving them implicit in prose.
The state machine then serves as a precise reference for generation: a constraint block is correct when its stimulus follows a legal path through that machine. This provides more than matching specification prose, because ordering and co-occurrence rules are explicit in the state representation.
For AXI4 write channel traffic, the state machine can encode that AWVALID must precede or occur concurrently with WVALID for a transaction, that WLAST is asserted on the final data beat and nowhere else, and that BVALID waits until AWVALID and at least WVALID have been observed. Constraints generated from those states enforce the relationships in a UVM sequence object without requiring the engineer to recall every rule manually.
Undefined Behavior
Design specifications generally define behavior for legal inputs. Treatment of illegal inputs may be absent or vary considerably across specification types and organizations. For pre-silicon verification, that leaves a basic question: should stimulus include inputs outside the legal space?
The answer follows the verification objective. If the objective is correct DUT behavior for legal inputs, constraints should remain inside that space and maximize legal behavior coverage. If the plan also checks how the DUT handles illegal inputs without catastrophic failure, a portion of the stimulus should be intentionally invalid, with assertions checking for clean rejection rather than silent corruption or deadlock.
These are both valid objectives, but they need separate constraint structures and assertion sets. Combining them without stating the active objective makes the coverage model ambiguous about whether it measures correctness or behavior under invalid conditions. Each objective should have its own coverage groups and acceptance criteria.
For initial functional verification of a new block, we prioritize coverage of legal behavior. Testing illegal inputs remains useful, but it should be scheduled as a separate test plan phase with a distinct coverage model and acceptance criteria. A single constraint set for both purposes creates coverage data that does not clearly distinguish verified correctness from behavior that was merely stimulated.
Constraint Maintenance
Constraints built against an early specification can become incomplete or wrong as that specification evolves. Complex RTL projects routinely add features, clarify edge cases, and revise performance parameters. Any such update can invalidate constraints tied to the earlier intent.
The operational problem is knowing which constraints depend on which specification assumptions. A block based on a 64-byte cache line becomes wrong when the design supports configurable cache line sizes. If that dependency is undocumented, the block keeps running with no signal that it no longer matches current design intent.
A useful practice is to tag each constraint block with the specification section it implements. One comment naming the specification document and section number is enough. After an update, searching for that section identifies the constraints for review. Without tags, review depends on an engineer remembering what was written and why, which is unreliable six months into a project.
When Hand-Written Constraints Still Make Sense
Generating constraints from protocol state machines addresses the specification-level problem effectively. It does not cover micro-architectural rules based on design choices absent from the interface specification.
Custom arbitration, a particular priority scheme, or an internal power management handshake outside a standard interface protocol all require hand-written constraints because no standard specification defines them. Automated generation is not meant to eliminate manual constraints. It handles the standard, repeatable portion, leaving engineering judgment for the custom rules specific to the design.
Good specification-to-stimulus translation makes the testbench exercise intended behavior rather than an engineer's interpretation of it. Those interpretations may be similar, but the differences often expose the bugs that matter.