Skip to content

Verilog · Chapter 10.6 · Operators & Operands

Relational Operators in Verilog — Comparison & the Signed/Unsigned Trap

The relational operators, less-than, less-or-equal, greater-than, and greater-or-equal, compare the magnitude of two values and return a single bit, true or false. They build comparators, the hardware behind range checks, address decoding, thresholds, and arbiters, anywhere a design decides which of two numbers is bigger. They look simple, with one sharp edge that catches every engineer: signedness. The same bit pattern compares differently depending on whether the operands are signed or unsigned, and just as in arithmetic, if either operand is unsigned the whole comparison goes unsigned, so a value meant to be negative reads as a large positive and gives the wrong answer. An unsigned value is also never less than zero, which quietly defeats many a negative-threshold check. This lesson drills the four operators, the comparator they build, the signed-versus-unsigned rule, and the range-check idiom.

Foundation18 min readVerilogRelational OperatorsComparisonSignednessRTL Design

Chapter 10 · Section 10.6 · Operators & Operands

1. The Engineering Problem

A controller is meant to act when a signed sensor reading drops below a threshold. The reading can be negative, but it was declared as a plain reg:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
reg [7:0] temp;                  // plain reg — UNSIGNED
// ...
if (temp < THRESHOLD)            // intent: act when temp is below threshold
    cool = 1;

For a temp that should read −1 (bit pattern 8'hFF), the comparison treats it as 255 — because a plain reg is unsigned, and an unsigned comparison reads 8'hFF as the large positive 255, not −1. So a freezing reading compares as the hottest possible value, and the controller does the opposite of what it should. Worse, a check like if (temp < 0) is always false — an unsigned value can never be less than zero — so a negative-detect that looks reasonable never fires.

The fix is to make the operand's type match its meaning:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
reg signed [7:0] temp;           // signed → 8'hFF means -1
// now  (temp < THRESHOLD)  and  (temp < 0)  behave as intended

A relational operator compares signed or unsigned depending on its operands' types — and if either operand is unsigned, the whole comparison is unsigned, so a negative value reads as a large positive. The comparison compiles and runs; it just answers the wrong question on negative or large values.

Comparison is where signedness, introduced for arithmetic in 10.2, bites again — and it produces corner-case bugs that pass every positive-value test. This page teaches the operators, the comparator hardware, and above all the signedness rule that decides every comparison.

2. Mental Model — Relational Operators Are Magnitude Comparators

Visual A — a relational operator builds a comparator

Relational operator as a comparatoraoperandboperandmagnitude comparatorsigned or unsigned context1-bit resultrelation holds? 1/012
A relational operator builds a magnitude comparator: two operands in, a single truth bit out (the relation holds or it does not). The comparator's behaviour depends on the sign context — signed if both operands are signed, unsigned if either is unsigned — which decides how the most-significant bit is read.

3. The Four Operators

relational-operators.v
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   a <  b      // less than            → 1 if a is strictly less than b
   a <= b      // less than or equal   → 1 if a is less than or equal to b
   a >  b      // greater than         → 1 if a is strictly greater than b
   a >= b      // greater than or equal→ 1 if a is greater than or equal to b

All four are binary, take operands of any width, and return a single bit. They are the operators of decisions about order: which value is bigger, whether a value is within a range, whether an address falls in a region. Their natural home is a condition — an if test, an FSM guard, an address decode — combined with the logical operators (10.3) when several comparisons join into one decision.

Note on the less-or-equal symbol: the `<=` token is also the non-blocking assignment operator (Chapter 14). Verilog tells them apart by context — inside an expression it is the relational comparison; as a statement with a signal on the left (`q <= d;`) it is non-blocking assignment (§6).

4. Signed vs Unsigned — The Comparison That Changes Answer

This is the heart of the page. The same bits compare differently by sign context:

signed-vs-unsigned.v
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   reg signed [7:0] s = 8'hFF;     // signed   → value -1
   reg        [7:0] u = 8'hFF;     // unsigned → value 255
 
   (s <  0)     // = 1  (-1 is less than 0)            signed compare
   (u <  0)     // = 0  (255 is not less than 0)       unsigned — never < 0
   (s <  8'd10) // = 1  (-1 < 10)                       signed
   (u <  8'd10) // = 0  (255 < 10 is false)             unsigned
 
   // MIXING types forces unsigned:
   reg signed [7:0] a = -1;        // signed -1
   reg        [7:0] b = 8'd10;     // unsigned 10
   (a < b)      // = 0  ⚠ a is treated as UNSIGNED 255, so 255 < 10 is FALSE

The rules, identical to the arithmetic signedness of 10.2:

  • The comparison is signed only if BOTH operands are signed. Then the sign bit is read as a sign, and negatives compare as less than positives.
  • If EITHER operand is unsigned, the whole comparison is unsigned. The signed operand's bit pattern is reinterpreted as an unsigned magnitude — so −1 becomes the largest value, and the comparison flips on negatives. This is the §1 bug.
  • An unsigned value is never less than zero and always greater-than-or-equal to zero — so `u < 0` is constant-false and `u >= 0` is constant-true (synthesis often warns and optimizes them away).

The discipline: type each operand to match the value's real meaning (signed sensor data as reg signed), keep both operands consistent, and use $signed(...) / $unsigned(...) to force interpretation where a port can't be re-typed. Comparison signedness is a top source of corner-case bugs that pass every non-negative test.

Visual B — same bits, different comparison

Signed versus unsigned comparison of the same bits8'hFF vs 0same bitssigned: -1 less-than0= 1 (true)unsigned: 255less-than 0= 0 (false)12
The bit pattern 8'hFF compared against 0. As a signed value it is -1, so it is less than 0 (true). As an unsigned value it is 255, so it is not less than 0 (false). The same operator and operands give opposite answers depending on the sign context — and a mixed-signedness comparison goes unsigned, taking the false branch on what was meant to be a negative.

5. The Range-Check Idiom

The most common use of relational operators is a range / window check — is a value within bounds? It pairs two comparisons with a logical AND (10.3):

range-check.v
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   // "addr is in [BASE, LIMIT)" — common address-decode window
   assign in_region = (addr >= BASE) && (addr < LIMIT);
 
   // "value is within [LO, HI]" — inclusive bounds
   assign in_range  = (value >= LO) && (value <= HI);

Two things to get right:

  • Inclusive vs exclusive bounds — pick `<` vs `<=` (and `>` vs `>=`) deliberately. The classic off-by-one is using `<=` where `<` was meant (or vice versa), including or excluding the boundary value by one (DebugLab 3). For a half-open window [BASE, LIMIT), it is `>= BASE` and `< LIMIT`.
  • Consistent signedness across both comparisons — if the value is signed, BASE/LIMIT/LO/HI and the value must all be signed, or the §4 trap reappears inside the range check.

Address decoding, FIFO occupancy thresholds, and saturating-arithmetic clamps are all this idiom: two relationals joined by a logical operator.

6. The Less-Or-Equal Symbol Clash

Worth its own note because it confuses beginners reading code: the `<=` token has two meanings in Verilog, disambiguated by context:

le-symbol.v
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   // RELATIONAL (less-or-equal) — inside an expression, returns a truth bit:
   assign le = (a <= b);            // 'le' is 1 if a is less than or equal to b
 
   // NON-BLOCKING ASSIGNMENT — a statement in a procedural block (Chapter 14):
   always @(posedge clk)
       q <= d;                      // 'q gets d' on the clock edge — NOT a comparison
  • Inside an expression (a right-hand side, a condition), `<=` is the relational less-or-equal.
  • As a statement in a procedural block, with a signal on the left, `<=` is non-blocking assignment — a completely different operation (assigning, not comparing), drilled in Chapter 14.

Verilog never confuses them — the parser decides by context — but a reader must. When you see `<=`, check whether it is producing a value (relational) or driving a signal in an always block (non-blocking). This page is about the relational meaning; the assignment meaning is a Chapter 14 topic flagged here only so the shared symbol does not surprise you.

7. X Behaviour

If any bit of either operand is x or z, the comparison generally cannot be decided, so the result is x:

relational-x.v
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   (4'b1x10 < 4'b1010)   // = 1'bx   — the x bit could change the ordering
   (4'b001x < 4'b1000)   // = 1'b1   — high bits already decide it, x irrelevant

Unlike the bitwise operators (which resolve per bit), a relational result is a single ordering decision — so an unknown bit that could affect the ordering makes the whole result x. When the known bits already determine the order (a clearly larger high part), some tools resolve it; treat any x in a compared operand as a risk of an x result. This is another reason an unreset register feeding a comparator yields x decisions in simulation.

8. Worked Examples

8.1 Example 1 — an address-decode window

addr-decode.v
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
module region_decode #(
    parameter [15:0] BASE  = 16'h1000,
    parameter [15:0] LIMIT = 16'h2000
)(
    input  [15:0] addr,
    output        in_region
);
    // half-open window [BASE, LIMIT): includes BASE, excludes LIMIT
    assign in_region = (addr >= BASE) && (addr < LIMIT);
endmodule

The canonical range check: `>= BASE` (inclusive low bound) AND `< LIMIT` (exclusive high bound). All operands are unsigned addresses, consistently typed, so no signedness trap — and the half-open form [BASE, LIMIT) makes adjacent regions tile without overlap.

8.2 Example 2 — a signed threshold

signed-threshold.v
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
module below_threshold (
    input  signed [9:0] temp,        // signed — can be negative
    input  signed [9:0] threshold,
    output              cool
);
    assign cool = (temp < threshold);    // signed comparison, correct for negatives
endmodule

Because both temp and threshold are signed, the comparison reads negative temperatures correctly — the §1 fix. A negative temp compares as less than a positive threshold, as intended. Re-type either operand to unsigned and the bug returns.

8.3 Example 3 — priority via comparison

priority.v
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   // pick the larger of two requests' counts (a tiny arbiter helper)
   assign a_wins = (count_a >= count_b);    // ties go to A (>=)

A single relational expresses a priority/ordering decision. The choice of `>=` versus `>` decides who wins ties (here, A) — a deliberate design choice, not an accident. Comparison is the basis of arbiters, max/min selection, and saturating logic.

9. Industry Perspective

  • Address decoding is range checks. Every memory-map decoder is (addr >= BASE) && (addr < LIMIT) per region — relational operators with consistent unsigned addresses and deliberate half-open windows so regions tile cleanly.
  • Signedness discipline prevents threshold bugs. Signed sensor data, error terms, and DSP values must be compared signed; mixing a signed value with an unsigned bound is a classic silent defect that passes positive-value testing and fails on negatives. Teams type operands consistently and lint mixed-sign comparisons.
  • Constant-fold warnings catch dead comparisons. unsigned < 0 (always false) and unsigned >= 0 (always true) are flagged by lint and optimized away by synthesis — usually a sign that an operand should have been signed.
  • The off-by-one boundary is a review focus. Whether a range is inclusive or exclusive (`<` vs `<=`) is a frequent bug and a standard code-review check, especially for thresholds and FIFO full/empty levels.

10. Common Mistakes

  1. Comparing an unsigned value against 0 for negativity`u < 0` is always false; an unsigned value is never negative (§1, DebugLab 1).
  2. Mixed signed/unsigned comparison — any unsigned operand forces unsigned, so a negative reads as a large positive and the comparison flips (§4, DebugLab 2).
  3. Off-by-one bounds`<=` where `<` was meant (or vice versa), including/excluding the boundary by one (§5, DebugLab 3).
  4. Confusing relational `<=` with non-blocking `<=` — same symbol, different meaning by context (§6).
  5. Ignoring x in a compared operand — an unknown bit can make the whole comparison x (§7).

11. Debugging Lab

Three relational-operator debug post-mortems

12. Interview Q&A

13. Exercises

Exercise 1 — Predict signed vs unsigned

For a = 8'hFF and b = 8'd5, give the result of (a < b) when: (a) both are unsigned; (b) both are signed; (c) a is signed and b is unsigned. Explain the difference.

Exercise 2 — Write the range check

Write the expression for each window on an 8-bit addr: (a) inclusive [0, 63]; (b) half-open [64, 128); (c) "strictly above 200"; (d) "at most 15".

Exercise 3 — Fix the comparisons

Each line states an intent. Identify the bug and fix it.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
y = (count < 0);                 // intent: detect underflow on unsigned count
y = (sval < ulimit);             // intent: signed compare (sval is signed, ulimit unsigned)
y = (addr >= LO) && (addr <= HI);// intent: half-open [LO, HI)

Exercise 4 — Reason about boundaries

A memory map has region A at [0x000, 0x100) and region B at [0x100, 0x200). (a) Write each region's decode. (b) Which address would be wrongly claimed by both if region A used <= on its high bound? (c) Why is the half-open convention preferred for tiling regions?

14. Summary

The relational operators — less-than, less-or-equal, greater-than, greater-or-equal — compare two magnitudes and return a single truth bit, building the comparators behind range checks, address decoding, and thresholds.

The core ideas:

  • Magnitude comparison, one-bit result — true/false (or x), regardless of operand width; the hardware is a comparator.
  • Signedness decides the answer — signed only if both operands are signed; if either is unsigned the whole comparison is unsigned, so a negative value reads as a large positive (the same rule as arithmetic, 10.2).
  • An unsigned value is never less than zerounsigned < 0 is constant-false; detecting negativity requires a signed operand.
  • The range-check idiom pairs two comparisons with a logical AND; choose `<` vs `<=` deliberately for inclusive/exclusive bounds, and keep signedness consistent.
  • The less-or-equal symbol is shared with non-blocking assignment (Chapter 14); context disambiguates — expression vs procedural statement.

The discipline this page instils:

  • Type operands to match the value's meaning — signed data as signed, consistent across both operands; force with $signed/$unsigned when needed.
  • Pick boundary operators deliberately`<` vs `<=` shifts a range by one; use half-open windows for tiling regions.
  • Treat unsigned < 0 / unsigned >= 0 warnings as a signedness mistake, not noise.

You can now build correct comparisons. The chapter turns next to moving bits rather than comparing them: Chapter 10.7 Shift Operator drills <<, >>, <<<, >>> — logical and arithmetic shifts — which reposition bits (often as free wiring) and interact with signedness in their own way for the arithmetic right shift.

  • Arithmetic Operators — Chapter 10.2; the same signedness rule, applied to addition and multiplication.
  • Logical Operators — Chapter 10.3; the AND that joins two comparisons into a range check.
  • integer — Chapter 5.2.2; the signed type whose sign governs comparison.
  • Operator Precedence — Chapter 10.1; relational operators bind tighter than equality and looser than shift.