4-Bit Comparator Verilog Code: Behavioral & Structural
A magnitude comparator takes two binary numbers and tells you whether the first is greater than, equal to, or less than the second. This tutorial walks through working comparator Verilog code for a 4-bit comparator, first as a compact behavioral description and then as an explicit gate-level structural build, each paired with a simulation testbench you can run yourself.
Every listing below compiles and runs with Icarus Verilog using iverilog -g2005 -o sim FILES && vvp sim. Copy a file, simulate it, and compare the output against the log shown under each block.
Behavioral 4-bit comparator
The behavioral style lets the synthesis tool infer the comparison hardware from the relational operators >, ==, and <. A combinational always @(*) block drives three one-bit outputs. This is the version you will write 95% of the time in real designs.
// comparator_4bit_behavioral.v
// 4-bit magnitude comparator described behaviorally.
module comparator_4bit (
input [3:0] a,
input [3:0] b,
output reg a_gt_b, // high when a > b
output reg a_eq_b, // high when a == b
output reg a_lt_b // high when a < b
);
always @(*) begin
a_gt_b = (a > b);
a_eq_b = (a == b);
a_lt_b = (a < b);
end
endmodule
// Testbench
module comparator_4bit_tb;
reg [3:0] a, b;
wire a_gt_b, a_eq_b, a_lt_b;
integer i, j;
comparator_4bit dut (
.a(a), .b(b),
.a_gt_b(a_gt_b), .a_eq_b(a_eq_b), .a_lt_b(a_lt_b)
);
initial begin
$display(" a b | gt eq lt");
$display("------------------");
for (i = 0; i < 16; i = i + 5) begin
for (j = 0; j < 16; j = j + 6) begin
a = i[3:0];
b = j[3:0];
#1;
$display("%2d %2d | %b %b %b", a, b, a_gt_b, a_eq_b, a_lt_b);
end
end
$display("Done: behavioral comparator verified.");
$finish;
end
endmodule
a b | gt eq lt ------------------ 0 0 | 0 1 0 0 6 | 0 0 1 0 12 | 0 0 1 5 0 | 1 0 0 5 6 | 0 0 1 5 12 | 0 0 1 10 0 | 1 0 0 10 6 | 1 0 0 10 12 | 0 0 1 15 0 | 1 0 0 15 6 | 1 0 0 15 12 | 1 0 0 Done: behavioral comparator verified. /var/folders/l0/gs5hnkm56g54bmxg32r6nnwr0000gn/T/tmpnxv6xgw6/comparator_4bit_behavioral.v:40: $finish called at 12 (1s)
Structural 4-bit comparator
The structural style spells out the logic gate by gate. It is verbose, but it shows exactly how a comparator decides an ordering: compare the most significant bit first, and only look at a lower bit when all the higher bits are equal. Each bit contributes an equality term (an XNOR) and a “this bit makes a larger” term (a[i] & ~b[i]).
// comparator_4bit_structural.v
// 4-bit magnitude comparator built from gate primitives.
module comparator_4bit_struct (
input [3:0] a,
input [3:0] b,
output a_gt_b,
output a_eq_b,
output a_lt_b
);
wire [3:0] eq; // eq[i] = 1 when a[i] == b[i]
wire [3:0] nb; // nb[i] = ~b[i]
wire [3:0] hi; // hi[i] = a[i] & ~b[i] (a wins this bit)
wire g2, g1, g0;
// Per-bit equality using XNOR.
xnor xn3 (eq[3], a[3], b[3]);
xnor xn2 (eq[2], a[2], b[2]);
xnor xn1 (eq[1], a[1], b[1]);
xnor xn0 (eq[0], a[0], b[0]);
// Per-bit "a is higher here" = a[i] AND NOT b[i].
not i3 (nb[3], b[3]);
not i2 (nb[2], b[2]);
not i1 (nb[1], b[1]);
not i0 (nb[0], b[0]);
and h3 (hi[3], a[3], nb[3]);
and h2 (hi[2], a[2], nb[2]);
and h1 (hi[1], a[1], nb[1]);
and h0 (hi[0], a[0], nb[0]);
// Greater-than, resolved from the MSB downward.
and t2 (g2, eq[3], hi[2]);
and t1 (g1, eq[3], eq[2], hi[1]);
and t0 (g0, eq[3], eq[2], eq[1], hi[0]);
or og (a_gt_b, hi[3], g2, g1, g0);
// Equal when every bit matches.
and ae (a_eq_b, eq[3], eq[2], eq[1], eq[0]);
// Less-than is "not greater and not equal".
nor nl (a_lt_b, a_gt_b, a_eq_b);
endmodule
// Testbench: exhaustive self-check against Verilog's own operators.
module comparator_4bit_struct_tb;
reg [3:0] a, b;
wire a_gt_b, a_eq_b, a_lt_b;
integer i, j, errors;
comparator_4bit_struct dut (
.a(a), .b(b),
.a_gt_b(a_gt_b), .a_eq_b(a_eq_b), .a_lt_b(a_lt_b)
);
initial begin
errors = 0;
for (i = 0; i < 16; i = i + 1) begin
for (j = 0; j < 16; j = j + 1) begin
a = i[3:0];
b = j[3:0];
#1;
if ((a_gt_b !== (a > b)) ||
(a_eq_b !== (a == b)) ||
(a_lt_b !== (a < b))) begin
errors = errors + 1;
$display("MISMATCH a=%0d b=%0d gt=%b eq=%b lt=%b", a, b, a_gt_b, a_eq_b, a_lt_b);
end
end
end
$display("Exhaustive check of 256 vectors complete; errors = %0d", errors);
$display("Sample results:");
a = 4'd9; b = 4'd4; #1 $display(" a=%0d b=%0d -> gt=%b eq=%b lt=%b", a, b, a_gt_b, a_eq_b, a_lt_b);
a = 4'd7; b = 4'd7; #1 $display(" a=%0d b=%0d -> gt=%b eq=%b lt=%b", a, b, a_gt_b, a_eq_b, a_lt_b);
a = 4'd2; b = 4'd11; #1 $display(" a=%0d b=%0d -> gt=%b eq=%b lt=%b", a, b, a_gt_b, a_eq_b, a_lt_b);
$finish;
end
endmodule
Exhaustive check of 256 vectors complete; errors = 0 Sample results: a=9 b=4 -> gt=1 eq=0 lt=0 a=7 b=7 -> gt=0 eq=1 lt=0 a=2 b=11 -> gt=0 eq=0 lt=1 /var/folders/l0/gs5hnkm56g54bmxg32r6nnwr0000gn/T/tmph79bpo29/comparator_4bit_structural.v:75: $finish called at 259 (1s)
How the code works
Both modules expose the same three-output interface, which is the standard convention for a comparator and matches the classic 7485 comparator IC.
- Greater-than (a_gt_b): high when
ais strictly larger thanb. - Equal (a_eq_b): high only when all four bit pairs match.
- Less-than (a_lt_b): high when
ais strictly smaller.
Exactly one output is high for any input pair, so the three signals are mutually exclusive. In the behavioral module the tool synthesizes an efficient comparator automatically. In the structural module the greater-than output follows a priority chain: hi[3] alone decides the result when the top bits differ; otherwise eq[3] gates the next term down, and so on. The equal output is one big AND of all four equality bits, and less-than is simply a NOR of greater and equal, which guarantees the three outputs stay one-hot.
Notice how the two testbenches differ in intent. The behavioral testbench uses two nested for loops with coarse step sizes to print a readable sample table of a few representative cases. The structural testbench instead sweeps all 256 possible input combinations and checks each one against Verilog’s own relational operators with the case-equality operator !==, incrementing an error counter on any disagreement. That exhaustive self-check is the single most useful habit when writing combinational Verilog: because the input space of a 4-bit comparator is tiny, you can prove the gate-level logic matches a trusted reference for every input rather than eyeballing a handful of rows.
Behavioral versus structural: which to use
Reach for the behavioral version for production RTL. It is shorter, easier to read, easier to parameterize to other widths, and modern synthesis tools produce excellent carry-chain comparators from it. The structural version earns its place in two situations: a lab assignment that explicitly asks for a gate-level description, and a case where you need precise control over the gate structure for timing or area reasons. Both describe the same function and both simulate identically, so you can develop against the readable behavioral model and swap in the structural one when a requirement demands it.
Reusable 1-bit building block
If you prefer a hierarchical structural design, the smallest unit is a single-bit comparator. This fragment is the cell you would instantiate four times and chain together.
// Single-bit comparison cell (building block, not standalone).
module comparator_1bit (
input a,
input b,
output gt, // a > b
output eq, // a == b
output lt // a < b
);
assign gt = a & ~b;
assign eq = ~(a ^ b);
assign lt = ~a & b;
endmodule
Common mistakes
- Using
=in a combinational block incorrectly. Insidealways @(*)use blocking assignments (=), not non-blocking (<=). Non-blocking belongs to clocked sequential logic; mixing it into combinational comparator logic causes simulation-versus-synthesis mismatches. - Declaring outputs as
wirewith analwaysblock. Any signal assigned inside analwaysblock must be areg. If you drive the outputs withassigninstead, keep them aswire. Pick one style per signal. - Treating signed and unsigned the same. Plain
[3:0]vectors are unsigned, so 4’b1000 is 8, not -8. For signed comparison declare the portssignedand the operators reinterpret the magnitudes accordingly. - Forgetting the equal case in cascading. When you widen the comparator by chaining stages, a lower stage must only break a tie, so its result has to be gated by the equality of all higher bits. Dropping that gate lets a low bit override a decided high bit.
- Comparing different widths. If
ais 4 bits andbis 8 bits, Verilog zero-extends the narrower operand, which is easy to misread. Keep both operands the same width or extend them explicitly.
Quick check
Why should comparator outputs inside an always block be declared as reg?
In Verilog, any signal assigned a value inside an always block must be of type reg. It does not create a physical flip-flop here; for a combinational always @(*) it still synthesizes to pure logic. The reg keyword only means “assigned procedurally.”
In the structural design, why is the greater-than term for bit 2 gated by eq[3]?
Because the most significant bit has priority. Bit 2 can only decide the comparison when bit 3 of both numbers is equal. The eq[3] gate ensures a lower bit is consulted only after the higher bit has tied.
How many outputs are high at once for any valid input pair?
Exactly one. The three results are mutually exclusive and collectively exhaustive: a number is either greater than, equal to, or less than another, never two of those at the same time.
What does 4’b1000 represent in an unsigned 4-bit comparator?
It represents decimal 8. Without the signed keyword, 4-bit vectors are unsigned, so the most significant bit carries a weight of 8 rather than acting as a sign bit.
Share your question in comments or talk to our mentor team for batch guidance.
Learn Verilog with mentors, labs and placement support
This free series is maintained by the ChipXpert training team in Hyderabad & Bengaluru. Go deeper with instructor-led courses on real EDA tools.
Explore VLSI Courses