Skip to article

Operators and Expressions

Reduction Operators in Verilog

How the unary reduction operators (&, |, ^, and their negations) collapse a whole vector into one bit, and the ~| idiom used to build a zero flag.

4 min read · Updated


A reduction operator takes a single multi-bit operand and collapses it to one bit by applying the operator across every bit of the vector. Written as a prefix before one operand — &result, not result & something — the AND, OR, and XOR symbols switch from their bitwise role (comparing two vectors) to their reduction role (folding one vector). Hardware it becomes: a tree of AND gates that combines all bits of the input vector into one output bit. This differs from bitwise AND, which performs independent AND operations at each bit position and preserves a vector result.

OperatorResult is 1 whenCommon name
&vecEvery bit of vec is 1AND-reduce
~&vecAt least one bit of vec is 0NAND-reduce
|vecAt least one bit of vec is 1OR-reduce
~|vecEvery bit of vec is 0NOR-reduce (the zero flag idiom)
^vecAn odd number of bits in vec are 1XOR-reduce (parity)

The NOR-reduce is worth memorizing on its own: NOR-reducing a value is the standard idiom for a zero flag. "Every bit is 0" is exactly the definition of "this value equals zero," and the reduction computes it in one operator instead of a width-specific comparison like result == 4'b0000.

If any bit of vec is x, whether the reduction resolves depends on the same controlling-value rule as ordinary bitwise operators — an AND-reduce with even one known 0 bit still resolves to 0 regardless of what the other bits are, but an AND-reduce of all-1-or-x bits with no 0 present resolves to x. Four-State Logic covers the controlling-value rule directly.

Predict before running

Before running it, predict the AND-reduce, OR-reduce, and XOR-reduce of 4'b0000, 4'b1111, and 4'b0110 — and which of the three inputs the zero flag should catch.

module zero_flag (
  input  wire [3:0] result,
  output wire        zero
);
  assign zero = ~|result;
endmodule

module tb;
  reg [3:0] result;
  wire zero;

  zero_flag u_zero (.result(result), .zero(zero));

  initial begin
    result = 4'b0000;
    #1;
    $display("result=%b  and-reduce=%b or-reduce=%b xor-reduce=%b zero_flag=%b",
      result, &result, |result, ^result, zero);

    result = 4'b1111;
    #1;
    $display("result=%b  and-reduce=%b or-reduce=%b xor-reduce=%b zero_flag=%b",
      result, &result, |result, ^result, zero);

    result = 4'b0110;
    #1;
    $display("result=%b  and-reduce=%b or-reduce=%b xor-reduce=%b zero_flag=%b",
      result, &result, |result, ^result, zero);

    $display("PASS");
    $finish;
  end
endmodule
Expected output — reveal after you predict
result=0000  and-reduce=0 or-reduce=0 xor-reduce=0 zero_flag=1
result=1111  and-reduce=1 or-reduce=1 xor-reduce=0 zero_flag=0
result=0110  and-reduce=0 or-reduce=1 xor-reduce=0 zero_flag=0
PASS

Only 4'b0000 sets zero_flag, exactly matching its definition. The XOR-reduce column doubles as a parity check: it reads 1 only when an odd number of bits are set — 4'b1111 (four set bits, even) and 4'b0110 (two set bits, even) both reduce to 0, even though neither is zero.

Common mistakes

  • Confusing reduction with bitwise. &result (one operand, prefix position) is reduction; a & b (two operands, infix position) is bitwise. The symbol is identical — only the position tells you which one is meant.
  • Writing a width-specific zero check like result == 4'b0000 when ~|result says the same thing without hard-coding the width, and keeps working if result is later widened.
  • Expecting XOR-reduce to detect "nonzero." It detects odd parity, not nonzero — an even number of set bits (including zero set bits) reduces to 0 either way, as 4'b1111 above shows.

Sources and verification

Example provenance: SkillLift Labs authored the example for this tutorial. tests/scripts/tutorial-verified-examples.test.ts extracts the complete marked example from this Markdown file, compiles it in Verilog-2005 mode with Icarus Verilog, runs it, and requires the simulator output to match the Expected output block exactly. The same test also carries an independent, code-owned semantic oracle, and exercises the article-to-runner source-splitting path. Automation verifies those stated properties; human technical review for indexing remains a separate gate.