Skip to content

Inside Operator

Set membership testing, ranges, arrays, X/Z wildcard behavior, constraint usage.

Module 4 · Page 4.8

The Operator That Makes Constraint Ranges Readable

Without inside, checking whether an 8-bit value belongs to a set of legal opcodes looks like this: (op == 8'h10) || (op == 8'h20) || (op == 8'h30) || (op inside {[8'h40:8'h5F]}). With inside: op inside {8'h10, 8'h20, 8'h30, [8'h40:8'h5F]}. Same semantics, a quarter of the characters, zero chance of missing a parenthesis.

The real power shows up in constraints. The constraint solver treats inside as a first-class construct — it can draw uniformly from a set that mixes individual values and ranges without you building a custom distribution. Trying to replicate that with OR-chained equalities gives you correct coverage but usually a biased distribution.

In procedural simulation code, inside returns a 1-bit result: 1 if the value is in the set, 0 if not. The comparison under the hood uses the wildcard equality operator ==?, which means X and Z bits in the set members act as don't-cares. That is useful when intentional, and surprising when it isn't.

Set Membership — What It Actually Tests

val inside {set} asks: "does val match at least one member of the set?" The set can contain three kinds of members:

  • Single value — inside {8'hFF} — tests equality with one specific value. Equivalent to val == 8'hFF (using ==? internally).
  • Range [lo:hi] — inside {[0:15]} — tests whether val falls within the closed range [lo, hi] inclusive. Both bounds are included.
  • Array/queue variable — inside {arr} — tests membership against all elements of a packed or unpacked array. Each element is tested with ==?.
  • Mixed set — inside {0, [4:7], 15, arr} — all member types can appear in one set. The result is 1 if any single test passes.

The operator evaluates each member test in order and short-circuits to 1 as soon as any match is found. In simulation this is purely a combinational expression — it has no state, no side effects. In a constraint block it tells the solver which values are legal for that random variable.

Syntax and Evaluation Rules

SystemVerilog — Inside Operator Syntax
// General syntax
// result = expression inside { member_list };
// result is 1-bit: 1 = match found, 0 = no match
 
logic [7:0] val;
 
// ── Single values ────────────────────────────────────────────────
val inside {8'h00, 8'hFF}           // 1 if val == 0 or val == 255
 
// ── Range [lo:hi] — closed, inclusive ────────────────────────────
val inside {[8'h10:8'h1F]}           // 1 if 0x10 ≤ val ≤ 0x1F
 
// ── Mixed: values + ranges ────────────────────────────────────────
val inside {8'h00, [8'h10:8'h1F], 8'hFF}
 
// ── Array membership ──────────────────────────────────────────────
logic [7:0] legal_ops[4] = '{8'h10, 8'h20, 8'h30, 8'h40};
val inside {legal_ops}                // tests val against all 4 elements
 
// ── Negation: NOT inside ──────────────────────────────────────────
!(val inside {[8'h10:8'h1F]})         // 1 if val is outside the range
 
// ── In an if statement ────────────────────────────────────────────
if (opcode inside {8'h10, 8'h20, 8'h30})
$display("Legal opcode");
 
// ── In an assign (procedural equivalent) ─────────────────────────
logic is_legal;
assign is_legal = val inside {[8'h00:8'h0F], [8'hF0:8'hFF]};
 
// ── In a constraint block ─────────────────────────────────────────
// constraint legal_addr { addr inside {[32'h0000:32'h0FFF],
//                                      [32'hF000:32'hFFFF]}; }
 
// ── In an SVA assertion ───────────────────────────────────────────
// assert property (@(posedge clk)
//   cmd_valid |-> cmd_opcode inside {4'h1, 4'h2, 4'h4, 4'h8});
Member formWhat it testsExample
valueval ==? valueinside {8'h10}
[lo:hi]val >= lo && val <= hiinside {[0:255]}
Array variableval ==? arr[0] || val ==? arr[1] || ...inside {my_arr}
MixedOR of all individual testsinside {0, [2:5], 8}

Visual Evaluation — What Matches and What Doesn't

Set Membership Evaluation Table

Expression: val inside {8'h00, [8'h10:8'h13], 8'hFF}

valMatches 8'h00?Matches [10:13]?Matches 8'hFF?inside result
8'h00YesNoNo1
8'h0FNoNoNo0
8'h10NoYes (lo bound)No1
8'h12NoYes (in range)No1
8'h13NoYes (hi bound)No1
8'h14NoNo (just outside)No0
8'hFENoNoNo0
8'hFFNoNoYes1

X/Z Wildcard Behavior in Set Members

Because inside uses ==? internally, X and Z bits in set members act as don't-cares. A set member of 4'b1X1X matches any 4-bit value where bit 3 = 1 and bit 1 = 1, regardless of bits 2 and 0.

valSet memberBits comparedMatch?Reason
4'b10104'b1X1Xbits 3,1 only (X=don't-care)Yesbit3=1✓ bit1=1✓
4'b11104'b1X1Xbits 3,1 onlyYesbit3=1✓ bit1=1✓
4'b00104'b1X1Xbits 3,1 onlyNobit3=0 ≠ 1
4'b10014'b1X1Xbits 3,1 onlyNobit1=0 ≠ 1
4'bXX104'b1X1Xbits 3,1 — val has XX (unknown)val bit3=X: result unknown

Code Examples — From Basic Checks to Constraint Solving

Example 1 — Beginner: Procedural Set Membership

Example 1 — Inside Operator Basics
module tb_inside_basic;
 
logic [7:0] val;
 
initial begin
 
// ── Single values ─────────────────────────────────────────────
val = 8'hFF;
$display("FF in {00,FF}   = %0b", val inside {8'h00, 8'hFF});    // 1
val = 8'hAB;
$display("AB in {00,FF}   = %0b", val inside {8'h00, 8'hFF});    // 0
 
// ── Range ─────────────────────────────────────────────────────
val = 8'h12;
$display("12 in [10:1F]   = %0b", val inside {[8'h10:8'h1F]});    // 1
val = 8'h20;
$display("20 in [10:1F]   = %0b", val inside {[8'h10:8'h1F]});    // 0
 
// ── Mixed set ─────────────────────────────────────────────────
val = 8'h30;
$display("30 in {00,[10:1F],30,FF} = %0b",
val inside {8'h00, [8'h10:8'h1F], 8'h30, 8'hFF});    // 1
val = 8'h31;
$display("31 in {00,[10:1F],30,FF} = %0b",
val inside {8'h00, [8'h10:8'h1F], 8'h30, 8'hFF});    // 0
 
// ── Negation ──────────────────────────────────────────────────
val = 8'h50;
$display("50 NOT in [10:1F] = %0b", !(val inside {[8'h10:8'h1F]})); // 1
 
// ── Array membership ──────────────────────────────────────────
logic [7:0] opcodes[3] = '{8'h10, 8'h20, 8'h30};
val = 8'h20;
$display("20 in opcodes[] = %0b", val inside {opcodes});           // 1
val = 8'h40;
$display("40 in opcodes[] = %0b", val inside {opcodes});           // 0
 
$finish;
end
 
endmodule

Expected output:

Simulation Output
FF in {00,FF}   = 1
AB in {00,FF}   = 0
12 in [10:1F]   = 1
20 in [10:1F]   = 0
30 in {00,[10:1F],30,FF} = 1
31 in {00,[10:1F],30,FF} = 0
50 NOT in [10:1F] = 1
20 in opcodes[] = 1
40 in opcodes[] = 0

Example 2 — Intermediate: Opcode Decoder Using Inside

Example 2 — Opcode Class Decoder
// Instruction set: opcodes are grouped by class
// ALU ops:    8'h10 – 8'h1F
// Load/Store: 8'h20 – 8'h2F
// Branch:     8'h30, 8'h31, 8'h32
// NOP:        8'h00
 
module tb_opcode_decoder;
 
logic [7:0] opcode;
 
function automatic void decode(input logic [7:0] op);
if      (op inside {[8'h10:8'h1F]})              $display("0x%02h → ALU operation",   op);
else if (op inside {[8'h20:8'h2F]})              $display("0x%02h → Load/Store",      op);
else if (op inside {8'h30, 8'h31, 8'h32})       $display("0x%02h → Branch",          op);
else if (op == 8'h00)                            $display("0x%02h → NOP",            op);
else                                              $display("0x%02h → ILLEGAL opcode",  op);
endfunction
 
initial begin
decode(8'h00);   // NOP
decode(8'h15);   // ALU
decode(8'h1F);   // ALU (hi bound)
decode(8'h20);   // Load/Store (lo bound)
decode(8'h31);   // Branch
decode(8'hAB);   // ILLEGAL
$finish;
end
 
endmodule

Expected output:

Simulation Output
0x00 → NOP
0x15 → ALU operation
0x1F → ALU operation
0x20 → Load/Store
0x31 → Branch
0xAB → ILLEGAL opcode

Example 3 — Verification: Constrained-Random with Inside

Example 3 — Inside in Constraints and Assertions
// AXI transaction: constrain address to valid memory map regions
 
class axi_transaction;
 
rand logic [31:0] addr;
rand logic [7:0]  burst_len;
rand logic [1:0]  burst_type;
 
// Only generate addresses within valid memory map regions
constraint valid_addr {
addr inside {
[32'h0000_0000:32'h0000_FFFF],   // BOOT ROM
[32'h1000_0000:32'h1FFF_FFFF],   // SRAM
[32'hC000_0000:32'hCFFF_FFFF]    // Peripheral APB
};
}
 
// Burst length: short (1-4) or page-aligned (8, 16)
constraint valid_burst {
burst_len inside {[8'd1:8'd4], 8'd8, 8'd16};
}
 
// Burst type: FIXED or INCR only (not WRAP for this test)
constraint burst_type_c {
burst_type inside {2'b00, 2'b01};    // FIXED=00, INCR=01
}
 
// Scoreboard checker: validate received transaction
function void check_legal();
if (!(addr inside {[32'h0000_0000:32'h0000_FFFF],
[32'h1000_0000:32'h1FFF_FFFF],
[32'hC000_0000:32'hCFFF_FFFF]}))
$error("ILLEGAL addr = 0x%08h", addr);
if (!(burst_len inside {[8'd1:8'd4], 8'd8, 8'd16}))
$error("ILLEGAL burst_len = %0d", burst_len);
endfunction
 
endclass
 
module tb_constraint_inside;
initial begin
axi_transaction txn = new();
repeat(5) begin
void'(txn.randomize());
$display("addr=0x%08h burst_len=%0d burst_type=%02b",
txn.addr, txn.burst_len, txn.burst_type);
txn.check_legal();
end
$finish;
end
endmodule

Example 4 — Corner Case: X/Z in Value and Wildcard Members

Example 4 — X/Z Wildcard Behavior
module tb_inside_xz;
 
logic [3:0] val;
 
initial begin
 
// ── Wildcard member: X bits are don't-cares ────────────────────
// 4'b1X1X matches any value where bit3=1 and bit1=1
val = 4'b1010;
$display("1010 inside {1X1X} = %0b", val inside {4'b1X1X});  // 1
val = 4'b1110;
$display("1110 inside {1X1X} = %0b", val inside {4'b1X1X});  // 1
val = 4'b0010;
$display("0010 inside {1X1X} = %0b", val inside {4'b1X1X});  // 0 (bit3=0)
val = 4'b1001;
$display("1001 inside {1X1X} = %0b", val inside {4'b1X1X});  // 0 (bit1=0)
 
// ── X in the tested value ─────────────────────────────────────
val = 4'bXX10;
$display("XX10 inside {1X1X} = %0b", val inside {4'b1X1X});  // X (bit3 unknown)
 
// ── Clean value, wildcard member covers all possibilities ─────
// 4'bXXXX as a member matches EVERYTHING — acts as a wildcard
val = 4'hA;
$display("0xA inside {XXXX}  = %0b", val inside {4'bXXXX});   // 1
 
// ── Range endpoint with X: undefined behavior — avoid ─────────
// inside {[4'bXX00:4'b1111]} — bounds contain X, result is X
// Best practice: always use clean (non-X) range bounds
 
$finish;
end
 
endmodule

Expected output:

Simulation Output
1010 inside {1X1X} = 1
1110 inside {1X1X} = 1
0010 inside {1X1X} = 0
1001 inside {1X1X} = 0
XX10 inside {1X1X} = x
0xA inside {XXXX}  = 1

Simulation Behavior and Synthesis Considerations

What the Simulator Actually Does

In simulation, val inside {set} is evaluated left-to-right. For each set member, it computes val ==? member. As soon as one test returns 1, the result is 1 and evaluation stops. If all tests return 0, the result is 0. If any test returns X (due to X/Z in val or members) and no earlier test returned 1, the result may be X.

ScenarioResultReason
val is clean, member is clean, val matches1Normal equality match
val is clean, no member matches0All tests failed
val has X bits, no member has XXWildcard rule: X in val propagates
member has X bits (don't-care), val's non-X bits match1X in member = don't-care, non-X bits agree
val has X bits, and a member with matching X pattern exists1==? returns 1 when X bits align as don't-cares
Range bounds are XXRange comparison with X bound is undefined

Synthesis Support

ContextSynthesis supportNotes
assign / combinational RTLTool-dependent — not all tools support itExpand to explicit comparisons for safety: (val >= lo && val <= hi) || ...
SVA assertionFully supportedStandard use case — inside reads naturally in property expressions
Constraint blockFully supported (verification-only)Native to the constraint solver — preferred form for range membership
Procedural if / caseSimulation onlyFine for testbench, not synthesized

Where You'll Use This in Real Projects

Real Verification Patterns
// ── 1. CONSTRAINT: address alignment + region filter ─────────────
constraint addr_map {
addr inside {[32'h0000_0000:32'h0FFF_FFFF],  // DDR
[32'hA000_0000:32'hA000_FFFF]};  // MMIO
addr[1:0] == 2'b00;    // 4-byte aligned
}
 
// ── 2. SCOREBOARD: validate DUT response code ─────────────────────
function void check_resp(input logic [1:0] resp);
if (!(resp inside {2'b00, 2'b01}))   // OKAY or EXOKAY only
$error("Unexpected AXI response: %02b", resp);
endfunction
 
// ── 3. SVA ASSERTION: legal state transitions ─────────────────────
// assert property (@(posedge clk) disable iff (rst)
//   state_valid |-> next_state inside {ST_IDLE, ST_ACTIVE, ST_DRAIN});
 
// ── 4. COVERAGE: conditional sample based on membership ───────────
// covergroup cg_burst;
//   cp_len: coverpoint burst_len {
//     bins short  = {[1:4]};
//     bins medium = {[5:15]};
//     bins long   = {[16:255]};
//   }
// endgroup
 
// ── 5. DRIVER: select stimulus type based on address region ───────
function string get_region(input logic [31:0] a);
if      (a inside {[32'h0000_0000:32'h0FFF_FFFF]}) return "DDR";
else if (a inside {[32'hA000_0000:32'hAFFF_FFFF]}) return "MMIO";
else                                                return "UNMAPPED";
endfunction
 
// ── 6. PROTOCOL CHECKER: illegal burst type for region ────────────
// AXI rule: WRAP bursts only allowed in cacheable region
if (burst_type == 2'b10 && !(addr inside {[32'h1000_0000:32'h1FFF_FFFF]}))
$error("WRAP burst to non-cacheable address 0x%08h", addr);

Bugs Engineers Actually Hit

Bug 1 — Range Bounds Reversed: Empty Range

Bug 1 — Swapped Range Bounds
logic [7:0] val = 8'h15;
 
// BUGGY: lo > hi — the range [8'h1F:8'h10] is empty in most tools
// No value can satisfy hi ≥ val ≥ lo when lo > hi
if (val inside {[8'h1F:8'h10]})   // silently returns 0 always
$display("Inside");
// Waveform: 'Inside' never prints, even for val=8'h15
 
// FIXED: low bound first
if (val inside {[8'h10:8'h1F]})   // correct: [lo:hi]
$display("Inside");              // now prints for 0x15

Bug 2 — Unsigned Range With Signed Variable

Bug 2 — Signed Variable, Unsigned Range Bounds
logic signed [7:0] sval = -1;   // 8'hFF in two's complement
 
// Engineer expects: -1 is NOT inside [0:127]
// But the range bounds 0 and 127 are treated as unsigned in inside{}
// -1 = 8'hFF = 255 unsigned
// 255 is NOT in [0:127] → result is 0 — happens to be correct here
if (sval inside {[8'sh00:8'sh7F]})
$display("In positive range");
 
// TRAP: what about -1 inside [-128:0] (negative range)?
// -128 = 8'b1000_0000 = 8'h80 unsigned (128)
// -1   = 8'b1111_1111 = 8'hFF unsigned (255)
// Range [8'sh80:8'sh00] interpreted as [128:0] unsigned — EMPTY! (128 > 0)
if (sval inside {[8'sh80:8'sh00]})   // BUGGY: empty range
$display("In negative range");       // never prints!
 
// CORRECT: for signed ranges, ensure comparison context is signed
// Use explicit comparisons when mixing signed values with inside ranges
if ($signed(sval) >= -128 && $signed(sval) <= -1)
$display("Negative value confirmed");   // correct approach for signed

Bug 3 — Inside in Constraint vs. Procedural: Different Semantics

Bug 3 — Inside in Constraint Uses == Not ==?
// In a CONSTRAINT block, inside uses == (exact equality, no wildcard)
// In PROCEDURAL code, inside uses ==? (wildcard equality)
// This is a subtle but important difference
 
class pkt;
rand logic [3:0] val;
// In constraint: inside uses == — X in member is NOT a don't-care
// Writing {4'b1X10} in a constraint means the literal value with X bits
// The solver cannot randomize to X/Z, so this effectively matches nothing
constraint bad_c { val inside {4'b1X10}; }  // ISSUE: no valid 2-state value matches
endclass
 
// Procedural context — X member IS a wildcard
logic [3:0] check_val = 4'b1010;
$display("proc: 1010 inside {1X10} = %0b",
check_val inside {4'b1X10});  // 1 — wildcard: bit2=X is don't-care
 
// CORRECT for constraint: list all explicit values or use a range
class pkt_fixed;
rand logic [3:0] val;
// Want: all 4-bit values where bit3=1 and bit1=1 (i.e., 4'b1X1X pattern)
// Enumerate them explicitly: 1010, 1011, 1110, 1111
constraint good_c { val inside {4'b1010, 4'b1011, 4'b1110, 4'b1111}; }
endclass

Bug 4 — Expecting inside to Work as Synthesizable RTL

Bug 4 — Inside in RTL: Synthesizability Issue
// BUGGY: using inside in an assign — synthesis may fail or warn
module decoder (
input  logic [7:0] opcode,
output logic        is_alu
);
// May synthesize with Synopsys DC 2019+ but not older tools
assign is_alu = opcode inside {[8'h10:8'h1F]};   // RISKY
 
endmodule
 
// CORRECT: expand to explicit comparison — portable across all tools
module decoder_safe (
input  logic [7:0] opcode,
output logic        is_alu
);
assign is_alu = (opcode >= 8'h10) && (opcode <= 8'h1F);  // always synthesizable
 
endmodule

Bug 5 — Constraint Contradiction: Inside + Other Constraint

Bug 5 — Constraint Contradiction Causes Randomize Failure
class bad_txn;
rand logic [7:0] burst_len;
 
// Constraint A: burst length in {1,2,3,4,8,16}
constraint len_set  { burst_len inside {[8'd1:8'd4], 8'd8, 8'd16}; }
 
// Constraint B: burst length must be > 16 (added later, contradicts A)
constraint len_min  { burst_len > 16; }
// randomize() will FAIL — no value satisfies both constraints
// $error: "Randomization failed"
endclass
 
module tb_constraint_debug;
initial begin
bad_txn t = new();
if (!t.randomize())
$error("Randomize failed — check for constraint contradiction");
 
// DEBUG: disable one constraint to isolate the conflict
t.randomize() with { burst_len inside {[8'd1:8'd4]}; };
// Works → confirms len_min was the conflicting constraint
$finish;
end
endmodule

Interview Questions

Beginner Level

Q1: What does val inside {[4:7]} return for val=4, val=7, val=8? val=4 → 1 (lower bound is inclusive). val=7 → 1 (upper bound is inclusive). val=8 → 0 (just outside the range). Both bounds are always included. Q2: How do you test that a value is NOT in a set? Wrap the expression in !(): !(val inside {[0:15]}). There is no !inside keyword in SystemVerilog — the negation is applied to the result of the inside expression.

Intermediate Level

Q3: What comparison operator does inside use internally for each set member? The wildcard equality operator ==?. This means X and Z bits in set members act as don't-cares — a member of 4'b1X1X matches any value where bits 3 and 1 are 1, regardless of bits 2 and 0. This is different from the standard equality == used in constraint blocks. Q4: Can you use a variable as a range bound in an inside expression? Yes, range bounds can be runtime expressions: val inside {[lo_bound:hi_bound]} where lo_bound and hi_bound are variables is legal in simulation and constraints. If the bounds are X, the result is X. If lo > hi at runtime, the range is empty — the member never matches.

Experienced Engineer Level

Q5: Why might a constraint using inside produce a different distribution than an equivalent OR chain of comparisons? The constraint solver treats inside as a first-class set membership constraint, allowing it to compute the total legal value set and distribute uniformly across it. An OR chain of comparisons requires the solver to reason about a compound boolean — some solvers may weight clauses differently or use heuristics that bias the distribution. For example, inside {[0:3], [252:255]} gives uniform probability across all 8 values. An equivalent OR comparison may over-weight whichever clause the solver processes first. Use inside in constraints when uniform distribution over the set is required. Q6: A signed 8-bit variable holds -1. Does it match inside {[8'sh80:8'sh7F]}? No — and the reason is subtle. 8'sh80 = -128 in signed, but its bit pattern is 8'hFF - 0x7F = 8'h80 = 128 unsigned. 8'sh7F = +127 unsigned. The range comparison is performed on the unsigned bit patterns: [128:127]. Since 128 > 127, this is an empty range — nothing ever matches. This is the signed/unsigned range trap. Use explicit $signed(val) >= -128 && $signed(val) <= 127 for signed range checks.

Best Practices & Coding Guidelines

  • Always put lo ≤ hi in ranges — Reversed bounds [hi:lo] produce an empty range with no error — the inside expression quietly returns 0. Always verify bound order in code review.
  • Use inside in constraints, not OR chains — The constraint solver distributes uniformly across the inside set. Equivalent OR-comparison constraints may have biased distributions depending on the solver implementation.
  • Avoid inside in synthesizable RTL — Expand to explicit comparisons for portability. The synthesized hardware is identical — just a comparator chain — but the RTL compiles cleanly across all tool versions.
  • Avoid signed variables with inside ranges — Inside range comparisons use unsigned arithmetic on bit patterns. Negative values have large unsigned patterns. Use explicit $signed() comparisons for signed range checks.
TaskPreferred approachAvoid
Constrain rand var to specific values + rangesrand_var inside {v1, [lo:hi], v2}Long OR chain of equality constraints
Check opcode class in testbenchif (op inside {[8'h10:8'h1F]})Chained if(op==10||op==11||...)
Check opcode class in RTL assign(op >= 8'h10) && (op <= 8'h1F)op inside {[8'h10:8'h1F]} — synthesis risk
Check signed range$signed(val) >= lo && $signed(val) <= hival inside {[lo:hi]} — unsigned comparison
Constraint for multiple non-contiguous rangesinside {[r1lo:r1hi], [r2lo:r2hi]}Separate constraints per range — harder to maintain

Summary

The inside operator is where SystemVerilog's verification heritage shows. It exists because writing exhaustive value-range checks as OR chains is error-prone and biases constraint distributions. A single inside {[0:15], 32, 64} is cleaner, solver-friendly, and far easier to maintain when the legal set changes.

  • Range bounds are inclusive, low-first. [lo:hi] includes both endpoints. Reversed bounds give an empty range with no error.
  • Set members use ==? internally. X/Z in members are don't-cares. In procedural code, X/Z in the tested value may produce an X result. In constraint blocks, inside uses == — X members match nothing randomizable.
  • Signed variables need special care. Inside range comparisons are unsigned on bit patterns. Use explicit $signed() comparisons for signed arithmetic ranges.
  • Not reliably synthesizable. Use in assertions, constraints, and testbench procedural code. Expand to comparator chains in RTL assign statements.
  • Constraint solver treats it as a first-class construct. Prefer it over OR-equality chains in constraints for uniform distribution and solver performance.