Part 5 · Sequences · Intermediate
Constraints & randomize() with Blocks
Inline constraint blocks, per-call randomize() with, conflict handling, and directed corners inside randomized sequences.
Why constraints live in items
Randomization without constraints produces illegal transactions — misaligned addresses, out-of-range burst lengths, reserved opcodes. Constraints in the item class define the legal protocol space : every randomize() call produces a valid transaction unless the caller adds conflicting directed constraints. This keeps sequences thin — they call randomize(), not hand-pick legal values.
Separating legal space (inline constraints) from test intent (randomize() with blocks) is a key UVM pattern. Inline constraints always apply; with blocks add temporary constraints for one call — perfect for hitting corners inside a randomized stream.
When constraints conflict, randomize() returns 0. Unchecked failures are a top cause of 'mystery' stimulus bugs — the sequence proceeds with stale or null data.
[ITEM] constraint layers
INLINE (in class) → always active — defines legal protocol space
randomize() → explores full legal space
randomize() with {} → adds TEMPORARY constraints for one call
CONFLICT → returns 0 — MUST checkInline constraints — the legal space
Inline constraint blocks in the class body define what randomize() may produce. They encode protocol rules from the spec — alignment, supported burst lengths, valid opcodes.
class apb_item extends uvm_sequence_item;
rand bit [31:0] addr, data;
rand bit write;
constraint word_aligned {
addr[1:0] == 2'b00;
}
constraint addr_map {
addr inside {[32'h0000:32'hFFFF]};
}
// Empty constraint block — placeholder for factory override
constraint c_data {}
endclassword_aligned encodes hardware rule — APB word accesses only.
addr inside {...} restricts to memory map range.
Empty blocks (c_data) allow factory/set_type_override to replace constraints.
randomize() with — directed corners in randomized streams
Inside a sequence body(), randomize() with { ... } adds temporary constraints for a single call. Use this for directed setup beats inside a mostly-random stress stream.
task body();
apb_item req;
req = apb_item::type_id::create("req");
// Beat 1: fully random legal transaction
start_item(req);
if (!req.randomize())
`uvm_fatal("RAND", "randomize failed")
finish_item(req);
// Beat 2: directed corner — fixed addr, random data
start_item(req);
if (!req.randomize() with { addr == 32'hDEAD0000; write == 1; })
`uvm_fatal("RAND", $sformatf("with-block failed: %s", req.sprint()))
finish_item(req);
// Beat 3: read at same address
start_item(req);
if (!req.randomize() with { addr == 32'hDEAD0000; write == 0; })
`uvm_fatal("RAND", "read corner failed")
finish_item(req);
`uvm_info("READ", $sformatf("rdata=0x%08x", req.rdata), UVM_MEDIUM)
endtask[SEQ] randomize() with in stimulus flow
[STIM] test wants: random traffic + one directed poke at 0xDEAD0000
[SEQ] apb_mixed_seq:
repeat(50) randomize(); ← explores legal space
randomize() with { addr==DEAD; }; ← directed corner
repeat(50) randomize(); ← back to random
[ITEM] inline constraints still apply — cannot randomize misaligned addrFailure handling — never ignore return value
When inline and with constraints conflict, randomize() returns 0. Common conflicts: directed addr outside addr_map, write==1 with a read-only constraint in a derived class, over-constrained id uniqueness.
// Always check — fatal in sequences, error in functions
if (!req.randomize() with { addr == 32'h1000; write == 1; })
`uvm_fatal("RAND_FAIL", $sformatf(
"constraint conflict on %s", req.sprint()))
// Debug aid: print constraint solver state (simulator-dependent)
void'(req.randomize() with { addr == bad_addr; }); // returns 0
req.print(); // shows last successful randomize state — misleading![ITEM] common randomize() failures
addr == 32'h1001 + word_aligned (addr[1:0]==0) → FAIL
write == 0 + wr_only { write==1; } → FAIL (derived item)
id unique array full → FAIL (soft constraint issue)
Symptom: sequence hangs or drives stale data from previous randomizeFatal in sequences — bad randomize means broken test, not soft warning.
Log req.sprint() on failure — shows field state at failure time.
Use rand_mode(OFF) + direct assignment for fully directed beats (no randomize).
Soft constraints and distribution
Soft constraints express preferences, not requirements — the solver may violate them if hard constraints demand it. dist constraints weight value distributions for stress.
class axi_item extends uvm_sequence_item;
rand bit [31:0] addr;
rand bit [3:0] id;
constraint c_addr {
addr[1:0] == 2'b00;
soft addr inside {[32'h0000:32'h0FFF]}; // prefer low mem, not required
}
constraint c_id_dist {
id dist { [0:3] := 80, [4:15] := 20 }; // weight low IDs
}
endclassWhen to use soft vs with-block
Soft — default bias across all randomize calls (e.g., prefer aligned bursts).
with-block — explicit directed corner for one beat in one sequence.
Hard inline — protocol legality that must never be violated.
APB register programming pattern
Register sequences combine directed with-blocks for setup and random traffic for stress — the most common constraint pattern in block-level verification:
task program_reg(bit [31:0] reg_addr, bit [31:0] val);
apb_item req = apb_item::type_id::create("req");
start_item(req);
assert(req.randomize() with {
write == 1;
addr == reg_addr;
data == val;
});
finish_item(req);
endtask
task body();
program_reg(CTRL_REG, 32'h1); // directed setup
repeat (100) begin // random stress
start_item(req);
assert(req.randomize() with { write inside {0,1}; });
finish_item(req);
end
endtaskKey takeaways
Inline constraints = legal protocol space; with-blocks = per-call directed corners.
Always check randomize() return — silent failure produces stale or illegal data.
Combine random beats and directed with-blocks in one sequence for realistic tests.
Common pitfalls
Ignoring randomize() return value — #1 subtle stimulus bug.
Over-constraining with blocks that fight inline constraints — solver returns 0.
Using randomize for fully directed setup — use rand_mode(OFF) and assign instead.
Assuming last randomize state after failure — fields may be unchanged or partial.