Equality (a == b): XNOR each bit pair, then AND all the results. Depth is log₂ N.
Ordering (a < b): needs information to flow from the most significant bit down, like a carry. It is a subtraction in disguise.
An unsigned a < b is exactly a borrow out of a − b. In terms of the adder flags from the earlier lesson, the carry out of a + ~b + 1 is 1 when there is no borrow, so
Signed ordering depends on both the sign bit of the difference and the overflow flag, because the subtraction itself can overflow:
Worked example with 4 bits. Take a = 0111 (7) and b = 1000 (8 unsigned, −8 signed).
Subtract: a − b = 0111 + ~1000 + 1 = 0111 + 0111 + 1 = 1111.
Borrow: the sign bit N is 1 and the carry out is 0, so there was a borrow.
Overflow: the true answer 7 − (−8) = 15 does not fit 4-bit signed, so V = 1.
Answers: the borrow gives unsigned less-than (lt_u = ~C = 1). N ⊕ V = 0 gives signed greater-than (7 > −8).
Watch lt_u against lt_s: they disagree wherever the top bit changes the meaning. The diagram shows a and b in hex, so 15 appears as f.
Slot 1: 7 versus 8 borrows, so lt_u is 1; N ⊕ V is 0, so lt_s is 0.
Slot 2: 8 versus 7 has no borrow, so lt_u is 0; N ⊕ V is 1, so lt_s is 1.
Slot 3: equal values give eq of 1 and both less-than outputs 0.
Slot 4: 15 (f) versus 1 has no borrow, so lt_u is 0; N ⊕ V is 1, so lt_s is 1.
Slot 5: 0 versus 15 (f) borrows, so lt_u is 1; N ⊕ V is 0, so lt_s is 0.
This table is the pattern to memorize. Two rows disagree:
a | b | Unsigned a < b | Signed a < b |
|---|---|---|---|
|
| false | true |
|
| true | false |
|
| true | true |
a = 11111, b = 00001. Is a < b true unsigned? Signed?Unsigned: false (31 > 1). Signed: true (−1 < 1). Same bits, different answer.
a = 01111, b = 10000. Is a < b true unsigned? Signed?Unsigned: true (15 < 16). Signed: false (15 > −16). The top bit flips the meaning.
if (offset > -1) on logic [3:0] offset is always false. Why?offset is unsigned and -1 is converted to unsigned too (all ones, 4294967295 for a 32-bit literal), so no 4-bit value exceeds it. Declare offset as signed or restate the condition.
A common interview question is how to compare two signed numbers using only an adder.
Subtract with the adder: a − b = a + ~b + 1.
Answer signed less-than as N ⊕ V, unsigned as ~C.
Declare both operands signed, or the comparison is unsigned.
The comparator plus a testbench that checks all 1024 pairs against the language operators.
module compare #(parameter int N = 5) (
input logic [N-1:0] a, b,
output logic eq, lt_u, lt_s
);
logic [N:0] diff;
logic [N-1:0] nb;
logic c, n, v;
always_comb begin
nb = ~b;
diff = {1'b0, a} + {1'b0, nb} + 1'b1; // a - b with carry out
c = diff[N]; // 1 means "no borrow" (a >= b unsigned)
n = diff[N-1];
v = (a[N-1] == nb[N-1]) && (diff[N-1] != a[N-1]);
eq = &(~(a ^ b)); // XNOR every bit, AND the results
lt_u = ~c;
lt_s = n ^ v;
end
endmodule
The test uses 1 ns time units and 1 ps precision. #1 is a settle interval for zero-delay combinational RTL, not a measured comparator delay. It checks known binary operands at N = 5; X/Z operand behaviour and mapped timing need separate checks. Failures identify both numeric interpretations and each expected and actual output, then $fatal gives a failing simulation verdict.