Hamming Code Verilog Code: (7,4) Encoder and Decoder

A single bit flipped by noise or a weak storage cell can silently corrupt a message. The Hamming (7,4) code is the classic answer: it adds three parity bits to four data bits so that any single-bit error can be detected and located, then corrected. This tutorial builds a complete hamming code verilog code example in plain Verilog-2005: a (7,4) encoder, a decoder that computes a syndrome, and a self-checking testbench that deliberately injects a bit flip and watches the decoder repair it.

Everything here is synthesizable combinational logic (apart from the testbench) and runs under any simulator. If you have Icarus Verilog, you can reproduce each result with iverilog -g2005 -o sim file.v && vvp sim. The simulation logs shown below are the real output of exactly that command.

How the (7,4) code is laid out

Number the seven codeword positions 1 to 7. Positions that are powers of two (1, 2, 4) hold parity bits; the rest (3, 5, 6, 7) hold the four data bits. Each parity bit covers the positions whose index has that power-of-two set in binary:

  • p1 (position 1) covers positions 1, 3, 5, 7
  • p2 (position 2) covers positions 2, 3, 6, 7
  • p4 (position 4) covers positions 4, 5, 6, 7

Each parity bit is the XOR of the data bits it covers, chosen so the covered set has even parity. In our code the message data[3:0] maps to positions 7, 6, 5, 3, and the parity bits fill positions 4, 2, 1.

The encoder

The encoder is pure combinational XOR. There is no clock and no state: give it four data bits, it returns the seven-bit codeword. The testbench prints the codeword for all sixteen messages so you can see the mapping.

module hamming74_enc (
    input  wire [3:0] data,   // message bits d1 d2 d3 d4 = data[0..3]
    output wire [6:0] code     // code[0]=c1 (pos 1) ... code[6]=c7 (pos 7)
);
    wire d1 = data[0];
    wire d2 = data[1];
    wire d3 = data[2];
    wire d4 = data[3];

    wire p1 = d1 ^ d2 ^ d4;    // parity over positions 1,3,5,7
    wire p2 = d1 ^ d3 ^ d4;    // parity over positions 2,3,6,7
    wire p4 = d2 ^ d3 ^ d4;    // parity over positions 4,5,6,7

    // positions: 7=d4 6=d3 5=d2 4=p4 3=d1 2=p2 1=p1
    assign code = {d4, d3, d2, p4, d1, p2, p1};
endmodule

module tb_enc;
    reg  [3:0] data;
    wire [6:0] code;
    integer i;
    hamming74_enc dut (.data(data), .code(code));
    initial begin
        $display("message | codeword(c7..c1)");
        for (i = 0; i < 16; i = i + 1) begin
            data = i[3:0];
            #1;
            $display("  %b   |    %b", data, code);
        end
        $finish;
    end
endmodule
message | codeword(c7..c1)
  0000   |    0000000
  0001   |    0000111
  0010   |    0011001
  0011   |    0011110
  0100   |    0101010
  0101   |    0101101
  0110   |    0110011
  0111   |    0110100
  1000   |    1001011
  1001   |    1001100
  1010   |    1010010
  1011   |    1010101
  1100   |    1100001
  1101   |    1100110
  1110   |    1111000
  1111   |    1111111
/var/folders/l0/gs5hnkm56g54bmxg32r6nnwr0000gn/T/tmp0v8xf7rc/hamming74_enc.v:30: $finish called at 16 (1s)

The decoder and syndrome

The decoder recomputes the same three parity checks on the received word. Each check bit is called a syndrome bit:

  • s1 = c1 ^ c3 ^ c5 ^ c7
  • s2 = c2 ^ c3 ^ c6 ^ c7
  • s4 = c4 ^ c5 ^ c6 ^ c7

The brilliant part of the Hamming layout is that the syndrome, read as the binary number {s4, s2, s1}, equals the position of the flipped bit. A syndrome of 000 means the word is clean; 011 means position 3 is wrong; 101 means position 5, and so on. Correction is then a single XOR: flip the bit at that position. After correcting, the decoder pulls the four data bits back out of positions 3, 5, 6, 7.

The testbench below encodes an arbitrary message, flips one bit to simulate a channel error, and feeds the corrupted word to the decoder. It compares the recovered message against the original using === so an unknown x would also fail the check.

module hamming74_enc (
    input  wire [3:0] data,
    output wire [6:0] code
);
    wire d1 = data[0];
    wire d2 = data[1];
    wire d3 = data[2];
    wire d4 = data[3];
    wire p1 = d1 ^ d2 ^ d4;
    wire p2 = d1 ^ d3 ^ d4;
    wire p4 = d2 ^ d3 ^ d4;
    assign code = {d4, d3, d2, p4, d1, p2, p1};
endmodule

module hamming74_dec (
    input  wire [6:0] rx,        // received word, rx[0]=c1 ... rx[6]=c7
    output wire [2:0] syndrome,  // binary error position, 0 = clean
    output wire [6:0] corrected, // word after correction
    output wire [3:0] data       // recovered 4-bit message
);
    wire c1 = rx[0];
    wire c2 = rx[1];
    wire c3 = rx[2];
    wire c4 = rx[3];
    wire c5 = rx[4];
    wire c6 = rx[5];
    wire c7 = rx[6];

    wire s1 = c1 ^ c3 ^ c5 ^ c7;
    wire s2 = c2 ^ c3 ^ c6 ^ c7;
    wire s4 = c4 ^ c5 ^ c6 ^ c7;
    assign syndrome = {s4, s2, s1};

    // syndrome gives the 1-based position of the flipped bit
    assign corrected = (syndrome == 3'd0) ? rx
                                          : (rx ^ (7'd1 << (syndrome - 3'd1)));

    // data bits live at positions 3,5,6,7 -> corrected[2],[4],[5],[6]
    assign data = {corrected[6], corrected[5], corrected[4], corrected[2]};
endmodule

module tb_hamming;
    reg  [3:0] data;
    wire [6:0] code;
    reg  [6:0] rx;
    wire [2:0] syn;
    wire [6:0] corr;
    wire [3:0] rdata;
    integer pos;

    hamming74_enc enc (.data(data), .code(code));
    hamming74_dec dec (.rx(rx), .syndrome(syn), .corrected(corr), .data(rdata));

    initial begin
        data = 4'b1011;
        #1;
        $display("message       = %b", data);
        $display("encoded word  = %b", code);

        pos = 3;                        // corrupt the bit at position 3
        rx  = code ^ (7'd1 << (pos - 1));
        #1;
        $display("flip position = %0d", pos);
        $display("received word = %b", rx);
        $display("syndrome      = %b  (decimal %0d)", syn, syn);
        $display("corrected word= %b", corr);
        $display("recovered msg = %b", rdata);
        if (rdata === data)
            $display("RESULT: single-bit error corrected, message recovered");
        else
            $display("RESULT: MISMATCH");
        $finish;
    end
endmodule
message       = 1011
encoded word  = 1010101
flip position = 3
received word = 1010001
syndrome      = 011  (decimal 3)
corrected word= 1010101
recovered msg = 1011
RESULT: single-bit error corrected, message recovered
/var/folders/l0/gs5hnkm56g54bmxg32r6nnwr0000gn/T/tmpcghgr_zg/hamming74.v:72: $finish called at 2 (1s)

Notice how the received word differs from the codeword in exactly one place, the syndrome reports decimal 3, and the corrected word is identical to the original codeword again. The recovered message matches the one we sent.

Proving it works for every error position

One passing case is not a proof. A stronger test sweeps the error across all seven positions (plus the no-error case) for a fixed message and checks that the syndrome always equals the error position and the message always survives.

module hamming74_enc (
    input  wire [3:0] data,
    output wire [6:0] code
);
    wire d1 = data[0];
    wire d2 = data[1];
    wire d3 = data[2];
    wire d4 = data[3];
    wire p1 = d1 ^ d2 ^ d4;
    wire p2 = d1 ^ d3 ^ d4;
    wire p4 = d2 ^ d3 ^ d4;
    assign code = {d4, d3, d2, p4, d1, p2, p1};
endmodule

module hamming74_dec (
    input  wire [6:0] rx,
    output wire [2:0] syndrome,
    output wire [6:0] corrected,
    output wire [3:0] data
);
    wire s1 = rx[0] ^ rx[2] ^ rx[4] ^ rx[6];
    wire s2 = rx[1] ^ rx[2] ^ rx[5] ^ rx[6];
    wire s4 = rx[3] ^ rx[4] ^ rx[5] ^ rx[6];
    assign syndrome  = {s4, s2, s1};
    assign corrected = (syndrome == 3'd0) ? rx
                                          : (rx ^ (7'd1 << (syndrome - 3'd1)));
    assign data = {corrected[6], corrected[5], corrected[4], corrected[2]};
endmodule

module tb_sweep;
    reg  [3:0] data;
    wire [6:0] code;
    reg  [6:0] rx;
    wire [2:0] syn;
    wire [6:0] corr;
    wire [3:0] rdata;
    integer p;

    hamming74_enc enc (.data(data), .code(code));
    hamming74_dec dec (.rx(rx), .syndrome(syn), .corrected(corr), .data(rdata));

    initial begin
        data = 4'b0110;
        #1;
        $display("message = %b, codeword = %b", data, code);
        $display("errpos | received | syndrome | recovered | ok");
        for (p = 0; p <= 7; p = p + 1) begin
            if (p == 0) rx = code;
            else        rx = code ^ (7'd1 << (p - 1));
            #1;
            $display("   %0d   | %b |   %b    |   %b    | %s",
                     p, rx, syn, rdata, (rdata === data) ? "yes" : "no");
        end
        $finish;
    end
endmodule
message = 0110, codeword = 0110011
errpos | received | syndrome | recovered | ok
   0   | 0110011 |   000    |   0110    | yes
   1   | 0110010 |   001    |   0110    | yes
   2   | 0110001 |   010    |   0110    | yes
   3   | 0110111 |   011    |   0110    | yes
   4   | 0111011 |   100    |   0110    | yes
   5   | 0100011 |   101    |   0110    | yes
   6   | 0010011 |   110    |   0110    | yes
   7   | 1110011 |   111    |   0110    | yes
/var/folders/l0/gs5hnkm56g54bmxg32r6nnwr0000gn/T/tmpwcrzcjjh/hamming74_sweep.v:54: $finish called at 9 (1s)

Every row reports yes, and the syndrome column counts cleanly from 0 through 7, confirming the decoder locates and repairs a flip wherever it lands, parity bits included.

Common mistakes

  • Mixing up bit ordering. The syndrome equals the 1-based position. Because Verilog vectors are 0-based, position n lives at index n-1, which is why the correction shifts by syndrome - 1. Forgetting the offset corrects the wrong bit.
  • Wrong parity coverage. If a syndrome equation omits a position or includes an extra one, the error location decodes incorrectly. Always derive coverage from the binary index of each position, not from memory.
  • Assuming it handles two errors. The (7,4) code corrects exactly one error. Two flipped bits produce a non-zero syndrome that points at a different single position, so the decoder “corrects” a bit that was fine and the message is lost. It cannot even reliably detect a double error without an extra overall parity bit (the extended (8,4) code).
  • Using = instead of === in the check. A regular equality with an x operand returns x, which Verilog treats as false in an if but hides the real cause. The === operator compares x and z literally and makes a bad decode obvious.
  • Clocking combinational logic unnecessarily. Both encoder and decoder are purely combinational. Wrapping them in an always @(posedge clk) block adds latency and risks latches if you forget a branch; keep them as assign statements unless you specifically want a pipelined register stage.

Quick check

Why are the parity bits placed at positions 1, 2, and 4 rather than at the end?

Those positions are powers of two. With that layout each position index, written in binary, directly tells you which parity checks cover it, so the syndrome read as a binary number equals the position of a single-bit error. Grouping parity at the end would lose this neat position-equals-syndrome property.

A received word gives syndrome s4 s2 s1 = 101. What does the decoder do?

Binary 101 is decimal 5, so the decoder concludes position 5 is wrong and flips that single bit (index 4 in the 0-based vector) before extracting the data bits. A syndrome of 000 would instead mean no error.

What happens if two bits are flipped in the same codeword?

The (7,4) code can only correct one error. Two errors yield a non-zero syndrome pointing at some single position, so the decoder flips an unrelated bit and returns a wrong message. Reliable double-error detection needs an extra parity bit (the extended (8,4) SECDED code).

Why does the correction expression shift by syndrome - 1?

The syndrome is a 1-based position (1 to 7), but Verilog vector indices start at 0. Subtracting 1 converts the position into the matching bit index, so 7'd1 << (syndrome - 1) builds a mask with a single 1 at the faulty bit, and XOR-ing flips it back.

Have questions about this topic?
Share your question in comments or talk to our mentor team for batch guidance.
▶ Try it in the ChipXpert Lab

Open this design in the free in-browser simulator — edit, compile and see the waveform/log instantly.

Run it in the Lab

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