Parity Generator and Checker in Verilog (XOR Reduction)

Parity is the smallest error-detecting code in digital design: a single extra bit appended to a data word so the receiver can tell whether the word arrived intact. It costs one wire and one gate, which is why you still find it on memories, UART frames, and low-speed buses. This tutorial walks through complete, runnable parity generator verilog code using the XOR-reduction operator, a matching checker, and a self-checking testbench. Every block below is Verilog-2005 that compiles and simulates with iverilog.

Two conventions exist. With even parity the parity bit is chosen so the total number of 1s in the word (data plus parity) is even. With odd parity the total is forced odd. The core computation is identical for both; odd parity is just the inverted even-parity bit.

The idea: one XOR reduction

The expression ^data_in is Verilog’s unary (reduction) XOR. It XORs every bit of the bus together and returns a single bit that is 1 when the word contains an odd number of 1s. That value is exactly the even-parity bit, because appending it makes the running count of 1s even. For odd parity you invert it. No loops, no manual bit-indexing, and it scales to any width.

Parameterized parity generator

The generator below takes a WIDTH parameter and an ODD parameter so one module serves every bus size and both conventions. The testbench instantiates it twice (even and odd) and prints the 1s count next to each result.

// parity_gen.v  -- parameterized even/odd parity generator
module parity_gen #(
    parameter WIDTH = 8,   // number of data bits
    parameter ODD   = 0    // 0 = even parity, 1 = odd parity
) (
    input  wire [WIDTH-1:0] data_in,
    output wire             parity
);
    // XOR-reduction of all data bits.
    // ^data_in is 1 when data_in holds an ODD number of 1s.
    // That value is exactly the EVEN-parity bit: appending it makes
    // the total number of 1s even. Invert it for odd parity.
    wire xor_all = ^data_in;
    assign parity = ODD ? ~xor_all : xor_all;
endmodule

// ---- testbench (self-contained, module last) ----
module tb_parity_gen;
    localparam WIDTH = 8;

    reg  [WIDTH-1:0] data;
    wire             even_p, odd_p;

    parity_gen #(.WIDTH(WIDTH), .ODD(0)) u_even (.data_in(data), .parity(even_p));
    parity_gen #(.WIDTH(WIDTH), .ODD(1)) u_odd  (.data_in(data), .parity(odd_p));

    // count 1s so the log is easy to read (synthesizable-free, TB only)
    function integer ones;
        input [WIDTH-1:0] v;
        integer k;
        begin
            ones = 0;
            for (k = 0; k < WIDTH; k = k + 1) ones = ones + v[k];
        end
    endfunction

    integer i;
    reg [WIDTH-1:0] vec [0:4];
    initial begin
        vec[0] = 8'b0000_0000;
        vec[1] = 8'b0000_0001;
        vec[2] = 8'b0000_0011;
        vec[3] = 8'b1011_0100;
        vec[4] = 8'b1111_1111;

        $display("  data_in    ones  even_p  odd_p");
        $display("  --------   ----  ------  -----");
        for (i = 0; i < 5; i = i + 1) begin
            data = vec[i];
            #5;
            $display("  %b   %0d      %b       %b", data, ones(data), even_p, odd_p);
        end
        $finish;
    end
endmodule
data_in    ones  even_p  odd_p
  --------   ----  ------  -----
  00000000   0      0       1
  00000001   1      1       0
  00000011   2      0       1
  10110100   4      0       1
  11111111   8      0       1
/var/folders/l0/gs5hnkm56g54bmxg32r6nnwr0000gn/T/tmpjmt8mtoh/parity_gen.v:53: $finish called at 25 (1s)

Read the log row by row. When the 1s count is already even (for example 00000011 with two 1s), the even-parity bit is 0 and the odd-parity bit is 1. When the count is odd (00000001), the values swap. The odd column is always the complement of the even column, confirming that ~xor_all is all it takes to switch conventions.

Parity checker

The checker does the reverse. It re-derives the parity it expects from the received data and compares it against the received parity bit. Because XOR doubles as a “not equal” test, the whole comparison is one gate. The output error is high whenever the recomputed value disagrees with what arrived.

// parity_check.v  -- parameterized even/odd parity checker
module parity_check #(
    parameter WIDTH = 8,
    parameter ODD   = 0    // must match the generator's setting
) (
    input  wire [WIDTH-1:0] data_in,
    input  wire             parity_in,   // received parity bit
    output wire             error        // 1 = parity mismatch detected
);
    // Re-derive what the parity bit SHOULD be, then compare.
    wire xor_all  = ^data_in;
    wire expected = ODD ? ~xor_all : xor_all;
    assign error  = (expected ^ parity_in);  // XOR == "not equal"
endmodule

// ---- testbench (self-contained, module last) ----
module tb_parity_check;
    localparam WIDTH = 8;

    reg  [WIDTH-1:0] data;
    reg              rx_parity;
    wire             error;

    parity_check #(.WIDTH(WIDTH), .ODD(0)) dut (
        .data_in(data), .parity_in(rx_parity), .error(error)
    );

    // helper: correct EVEN parity for a value
    function automatic even_par;
        input [WIDTH-1:0] v;
        begin even_par = ^v; end
    endfunction

    initial begin
        $display("  data_in    rx_parity  error  note");
        $display("  --------   ---------  -----  ----");

        // 1) clean frame: parity matches the data
        data = 8'b1011_0100; rx_parity = even_par(data); #5;
        $display("  %b      %b       %b    clean", data, rx_parity, error);

        // 2) same data, parity bit flipped in transit
        data = 8'b1011_0100; rx_parity = ~even_par(data); #5;
        $display("  %b      %b       %b    parity flipped", data, rx_parity, error);

        // 3) one data bit flipped, parity unchanged
        data = 8'b1011_0101; rx_parity = even_par(8'b1011_0100); #5;
        $display("  %b      %b       %b    data bit flipped", data, rx_parity, error);

        // 4) another clean frame
        data = 8'b0000_0001; rx_parity = even_par(data); #5;
        $display("  %b      %b       %b    clean", data, rx_parity, error);

        $finish;
    end
endmodule
data_in    rx_parity  error  note
  --------   ---------  -----  ----
  10110100      0       0    clean
  10110100      1       1    parity flipped
  10110101      0       1    data bit flipped
  00000001      1       0    clean
/var/folders/l0/gs5hnkm56g54bmxg32r6nnwr0000gn/T/tmpafeujr33/parity_check.v:54: $finish called at 20 (1s)

The two clean frames report error = 0. Flipping the parity bit alone, or flipping a single data bit while leaving the parity bit as originally transmitted, both raise error = 1. That is the whole promise of a parity bit: it catches any odd number of flipped bits, whether the corruption lands in the data field or on the parity wire itself.

Putting it together: a self-checking link

A generator and a checker only matter as a pair. This final example wires a transmitter to a receiver through a model of a noisy channel, clocks a few frames through it, and injects faults so you can watch detection succeed and fail. The fault vector XORs the 9-bit transmitted word (parity bit plus eight data bits), flipping exactly the bits set in it.

// parity_link.v  -- transmitter + noisy channel + receiver
module parity_gen #(parameter WIDTH = 8) (
    input  wire [WIDTH-1:0] data_in,
    output wire             parity        // even parity
);
    assign parity = ^data_in;
endmodule

module parity_check #(parameter WIDTH = 8) (
    input  wire [WIDTH-1:0] data_in,
    input  wire             parity_in,
    output wire             error         // even parity
);
    assign error = ^{parity_in, data_in};  // 0 when word has even # of 1s
endmodule

// ---- testbench: clocked link with fault injection (module last) ----
module tb_parity_link;
    localparam WIDTH = 8;

    reg              clk = 0;
    reg  [WIDTH-1:0] tx_data;
    reg  [WIDTH:0]   fault;        // bit 8 = parity, bits 7:0 = data
    wire             tx_parity;

    wire [WIDTH:0]   tx_word = {tx_parity, tx_data};
    wire [WIDTH:0]   rx_word = tx_word ^ fault;   // channel flips bits in 'fault'
    wire [WIDTH-1:0] rx_data   = rx_word[WIDTH-1:0];
    wire             rx_parity = rx_word[WIDTH];
    wire             error;

    parity_gen   #(.WIDTH(WIDTH)) tx (.data_in(tx_data), .parity(tx_parity));
    parity_check #(.WIDTH(WIDTH)) rx (.data_in(rx_data), .parity_in(rx_parity), .error(error));

    always #5 clk = ~clk;

    task send;
        input [WIDTH-1:0] d;
        input [WIDTH:0]   f;
        begin
            @(negedge clk);
            tx_data = d;
            fault   = f;
            @(posedge clk);  // receiver samples here
            #1 $display("  %b       %b     %b     %b", d, f, error,
                        (|f) ? ((error) ? 1'b0 : 1'b1) : 1'b0);
        end
    endtask

    initial begin
        $display("  tx_data    fault(p,d)  error  undetected?");
        $display("  --------   ----------  -----  -----------");
        send(8'b1011_0100, 9'b0_0000_0000); // clean frame
        send(8'b1011_0100, 9'b0_0000_0001); // 1 data bit flipped
        send(8'b1011_0100, 9'b1_0000_0000); // parity bit flipped
        send(8'b0010_1101, 9'b0_0000_0011); // 2 data bits flipped
        send(8'b0010_1101, 9'b0_0000_0000); // clean frame
        $finish;
    end
endmodule
tx_data    fault(p,d)  error  undetected?
  --------   ----------  -----  -----------
  10110100       000000000     0     0
  10110100       000000001     1     0
  10110100       100000000     1     0
  00101101       000000011     0     1
  00101101       000000000     0     0
/var/folders/l0/gs5hnkm56g54bmxg32r6nnwr0000gn/T/tmpre62x97r/parity_link.v:58: $finish called at 56 (1s)

The log shows the headline limitation of parity. The clean frames pass, the single-bit flips are caught, but the frame with two flipped data bits sails through with error = 0 even though the data is corrupt. The undetected? column marks that row. A single parity bit detects any odd number of bit errors and misses any even number; it never locates or corrects an error. When you need to survive multiple flips or repair data, you move up to CRC or an error-correcting code such as Hamming.

Common mistakes

  • Mixing up the convention. Even parity makes the total count of 1s even; the bit itself is ^data. Odd parity is ~(^data). Decide once and document it.
  • Generator and checker disagreeing. If the transmitter uses even parity and the receiver is parameterized for odd, every valid frame is flagged as an error. Keep the ODD parameter identical on both ends.
  • Expecting too much. Parity cannot detect two-bit (or any even-count) errors, and it cannot correct anything. Do not use it where multi-bit upsets are likely.
  • Width or bit-position errors. An off-by-one on the WIDTH parameter, or slicing the parity bit from the wrong index of the transmitted word, silently breaks detection. Let the parameter drive every range.
  • X-propagation. Reduction XOR over a vector that contains an x or z yields x, so an uninitialized or floating bit makes parity meaningless. Reset or drive the full bus before you sample.
  • Sampling before data settles. In a registered link, latch the parity in the same clock domain and after the data is stable, or the checker compares a stale bit against fresh data.

Quick check

For the byte 8’b1100_1011, what is the even-parity bit?

The byte has five 1s, an odd count, so the even-parity bit is 1 (appending it makes the total of six 1s even). The odd-parity bit would be 0.

Which single Verilog operator computes the parity of an N-bit bus in one line?

The unary reduction XOR, written ^bus. It XORs every bit together and returns 1 when the bus holds an odd number of 1s, which is the even-parity bit.

Can a single parity bit detect two bit flips in the same word?

No. Any even number of flipped bits leaves the overall parity unchanged, so a two-bit error passes as if the word were clean. Parity reliably catches only an odd number of errors.

A generator uses even parity but the checker is set to odd. What happens to a clean frame?

The checker recomputes the opposite expected value, so it reports error = 1 on every correctly transmitted word. The two ends must share the same even/odd convention.

Have questions about this topic?
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
Next batch starts 18th November 2026
Start your VLSI career with ChipXpert

Live online and classroom batches in Hyderabad & Bengaluru. Fill in your details and a counsellor will call you back.

  • Real EDA tools in your browser: industry-standard EDAReal EDA tools in your browser
  • Recorded sessions on the elearn portalRecorded sessions
  • Placement assistance: resume, mock interviewsPlacement assistance
  • Merit scholarship up to 60% · EMI optionsScholarship up to 60%
Hi! Ask me about courses, fees, batches or discounts. ×
BENGALURU
HYDERABAD