LFSR Verilog Code: Galois and Fibonacci (With PRBS)

A linear-feedback shift register (LFSR) is a shift register whose next input bit is a linear function — an XOR — of selected bits of its current state. Choose the feedback taps correctly and an n-bit register marches through all 2n−1 non-zero states before repeating. That makes the LFSR the cheapest structure in digital hardware for pseudo-random bit streams, scramblers, CRC engines, counters, and built-in self-test (BIST) stimulus. This tutorial gives you working, runnable LFSR Verilog code in both the Fibonacci and Galois forms, each parameterized by width and polynomial, plus a PRBS generator. Every testbench prints the sequence and measures the period so you can see the maximal-length property with your own eyes.

Fibonacci vs Galois: two ways to wire the feedback

There are two standard arrangements. In a Fibonacci (external-XOR) LFSR, several tapped bits feed a single XOR gate, and that one feedback bit is shifted into the end of the register. In a Galois (internal-XOR) LFSR, the single bit shifted out is XORed into each tapped position inside the register. Both use the same characteristic polynomial and reach the same maximal period, but they visit the states in a different order and have different timing. The Galois form distributes its XOR gates, so the feedback path is shorter — it usually runs faster at wide bit-widths.

Fibonacci LFSR Verilog code

The design below is width-parameterized. TAPS is a bit mask: a 1 marks a tapped position. The value 8'hB8 encodes the polynomial x8+x6+x5+x4+1, a maximal-length polynomial for 8 bits. The feedback is just the reduction-XOR of the tapped bits, written compactly as ^(lfsr & TAPS).

// Fibonacci (external-XOR) LFSR, parameterized width + tap mask
module lfsr_fibonacci #(
    parameter WIDTH = 8,
    parameter [WIDTH-1:0] TAPS = 8'hB8,   // x^8+x^6+x^5+x^4+1 -> maximal length
    parameter [WIDTH-1:0] SEED = 8'h01
)(
    input  wire              clk,
    input  wire              rst_n,
    output wire [WIDTH-1:0]  state,
    output wire              prbs      // serial output bit
);
    reg [WIDTH-1:0] lfsr;
    wire feedback = ^(lfsr & TAPS);     // reduction-XOR of the tapped bits

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n)
            lfsr <= SEED;               // seed must be non-zero
        else
            lfsr <= {lfsr[WIDTH-2:0], feedback};
    end

    assign state = lfsr;
    assign prbs  = lfsr[WIDTH-1];
endmodule

module tb_lfsr_fibonacci;
    localparam WIDTH = 8;
    reg clk = 0, rst_n = 0;
    wire [WIDTH-1:0] state;
    wire prbs;
    integer count;
    reg [WIDTH-1:0] seed_val;

    lfsr_fibonacci #(.WIDTH(WIDTH)) dut (
        .clk(clk), .rst_n(rst_n), .state(state), .prbs(prbs)
    );

    always #5 clk = ~clk;

    initial begin
        count = 0;
        @(negedge clk) rst_n = 1;       // release reset
        @(posedge clk);                 // register now holds SEED
        seed_val = state;
        $display("SEED      = 0x%02h", seed_val);
        $display("step  state  prbs");
        // walk the sequence until the seed recurs
        begin : walk
            integer i;
            for (i = 0; i < (1<<WIDTH); i = i + 1) begin
                if (i < 8)
                    $display(" %0d    0x%02h    %b", i, state, prbs);
                @(posedge clk);
                count = count + 1;
                if (state == seed_val) disable walk;
            end
        end
        $display("PERIOD    = %0d (max possible = %0d)", count, (1<<WIDTH)-1);
        $finish;
    end
endmodule
SEED      = 0x01
step  state  prbs
 0    0x01    0
 1    0x02    0
 2    0x04    0
 3    0x08    0
 4    0x11    0
 5    0x23    0
 6    0x47    0
 7    0x8e    1
PERIOD    = 255 (max possible = 255)
/var/folders/l0/gs5hnkm56g54bmxg32r6nnwr0000gn/T/tmpowxere2r/lfsr_fibonacci.v:59: $finish called at 2565 (1s)

How the Fibonacci version works

On each clock edge the register shifts left by one position: {lfsr[WIDTH-2:0], feedback} drops the top bit and inserts the computed feedback bit at the bottom. The asynchronous active-low reset loads a non-zero SEED. The testbench records the seed, then clocks the register until the seed value recurs and reports that count as the period. The log shows the period equals 255 — the full 28−1 — confirming these taps are maximal.

Galois LFSR Verilog code

The Galois version uses the same polynomial, now held in POLY. The bit shifted out (lfsr[0]) decides whether to XOR the polynomial back in. Notice the body is a single conditional XOR with no multi-input reduction gate — that is the structural difference that shortens the critical path.

// Galois (internal-XOR) LFSR, parameterized width + polynomial mask
module lfsr_galois #(
    parameter WIDTH = 8,
    parameter [WIDTH-1:0] POLY = 8'hB8,   // same maximal polynomial, Galois form
    parameter [WIDTH-1:0] SEED = 8'h01
)(
    input  wire              clk,
    input  wire              rst_n,
    output wire [WIDTH-1:0]  state,
    output wire              prbs
);
    reg [WIDTH-1:0] lfsr;
    wire out_bit = lfsr[0];               // bit shifted out drives the taps

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n)
            lfsr <= SEED;
        else if (out_bit)
            lfsr <= (lfsr >> 1) ^ POLY;   // XOR polynomial into the register
        else
            lfsr <= (lfsr >> 1);
    end

    assign state = lfsr;
    assign prbs  = out_bit;
endmodule

module tb_lfsr_galois;
    localparam WIDTH = 8;
    reg clk = 0, rst_n = 0;
    wire [WIDTH-1:0] state;
    wire prbs;
    integer count;
    reg [WIDTH-1:0] seed_val;

    lfsr_galois #(.WIDTH(WIDTH)) dut (
        .clk(clk), .rst_n(rst_n), .state(state), .prbs(prbs)
    );

    always #5 clk = ~clk;

    initial begin
        count = 0;
        @(negedge clk) rst_n = 1;
        @(posedge clk);
        seed_val = state;
        $display("SEED      = 0x%02h, POLY = 0xB8", seed_val);
        $display("step  state  prbs");
        begin : walk
            integer i;
            for (i = 0; i < (1<<WIDTH); i = i + 1) begin
                if (i < 8)
                    $display(" %0d    0x%02h    %b", i, state, prbs);
                @(posedge clk);
                count = count + 1;
                if (state == seed_val) disable walk;
            end
        end
        $display("PERIOD    = %0d (max possible = %0d)", count, (1<<WIDTH)-1);
        $finish;
    end
endmodule
SEED      = 0x01, POLY = 0xB8
step  state  prbs
 0    0x01    1
 1    0xb8    0
 2    0x5c    0
 3    0x2e    0
 4    0x17    1
 5    0xb3    1
 6    0xe1    1
 7    0xc8    0
PERIOD    = 255 (max possible = 255)
/var/folders/l0/gs5hnkm56g54bmxg32r6nnwr0000gn/T/tmpfp4p45ns/lfsr_galois.v:60: $finish called at 2565 (1s)

Fibonacci and Galois side by side

PropertyFibonacciGalois
XOR placementOne gate outside the registerGates distributed inside
Feedback pathLonger (multi-input XOR)Shorter — often higher fmax
Shift direction (here)Left, insert at LSBRight, XOR polynomial in
Period (8-bit, 0xB8)255255
State orderDifferent sequence of the same 255 statesDifferent sequence of the same 255 states

PRBS output from an LFSR

A pseudo-random bit sequence (PRBS) is simply the serial output of a maximal-length LFSR, one bit per clock. The example uses the telecom-standard PRBS7 polynomial x7+x6+1. Its period is 27−1 = 127, and a maximal-length sequence is balanced: the count of ones exceeds the count of zeros by exactly one per period. The testbench checks both facts.

// PRBS7 serial generator: x^7 + x^6 + 1 (ITU-T O.150), maximal length 127
module prbs7_gen (
    input  wire       clk,
    input  wire       rst_n,
    output wire       prbs      // one pseudo-random bit per clock
);
    reg [6:0] lfsr;
    wire feedback = lfsr[6] ^ lfsr[5];   // taps on bits 7 and 6

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n)
            lfsr <= 7'h7F;               // any non-zero seed
        else
            lfsr <= {lfsr[5:0], feedback};
    end

    assign prbs = lfsr[6];
endmodule

module tb_prbs7_gen;
    reg clk = 0, rst_n = 0;
    wire prbs;
    integer count, ones;
    reg [6:0] seed_state;

    prbs7_gen dut (.clk(clk), .rst_n(rst_n), .prbs(prbs));

    always #5 clk = ~clk;

    initial begin
        count = 0; ones = 0;
        @(negedge clk) rst_n = 1;
        @(posedge clk);
        seed_state = dut.lfsr;
        $write("first 20 PRBS bits: ");
        begin : walk
            integer i;
            for (i = 0; i < 200; i = i + 1) begin
                if (i < 20) $write("%b", prbs);
                if (prbs) ones = ones + 1;
                @(posedge clk);
                count = count + 1;
                if (dut.lfsr == seed_state) disable walk;
            end
        end
        $display("");
        $display("PERIOD = %0d (expected 127)", count);
        $display("ONES   = %0d, ZEROS = %0d (balanced +1)", ones, count-ones);
        $finish;
    end
endmodule
first 20 PRBS bits: 11111110000001000001
PERIOD = 127 (expected 127)
ONES   = 64, ZEROS = 63 (balanced +1)
/var/folders/l0/gs5hnkm56g54bmxg32r6nnwr0000gn/T/tmpvqwtto53/prbs7_gen.v:49: $finish called at 1285 (1s)

Choosing maximal-length taps

The period is maximal only when the feedback polynomial is primitive for that width. You do not derive these by hand — you look them up. A few trusted tap sets (1-indexed positions, bit n is the MSB):

WidthTapsMax period
44, 315
77, 6127
88, 6, 5, 4255
1616, 15, 13, 465535
3232, 22, 2, 14294967295

Common mistakes

  • Seeding with zero. The all-zero state is a fixed point — XOR of zeros is zero, so the register locks up forever. Always load a non-zero seed, and gate any load logic so zero can never be written.
  • Expecting 2n states. A maximal LFSR cycles through 2n−1 states; the all-zero state is deliberately excluded. If you truly need 2n, add a de Bruijn correction, not more taps.
  • Copying a tap mask across forms. A Fibonacci tap mask and a Galois polynomial mask are bit-reversed images of each other. Pasting one into the other silently gives a non-maximal, short sequence.
  • Using blocking assignments. In the clocked block the shift must use non-blocking <=. Blocking = can reorder the read and write of lfsr within the step and corrupt the shift.
  • Off-by-one on indexing. Polynomial term x8 maps to register bit index 7 in a zero-based [7:0] vector. Mixing 1-indexed polynomial notation with 0-indexed Verilog bits is the most common tap bug.
  • Trusting a guessed polynomial. Not every tap set is primitive. Verify the period in simulation — exactly what the testbenches above do — before trusting a new width.

Build and run

Each file is self-contained. Compile and simulate with Icarus Verilog using iverilog -g2005 -o sim file.v followed by vvp sim. The printed period is your proof that the taps are maximal.

Quick check

Why must an LFSR never be seeded with all zeros?

The feedback is an XOR of state bits. With every bit zero, the feedback is zero and the shifted-in bit is zero, so the register stays at all-zero forever. It is a fixed point outside the maximal cycle, so the seed must be non-zero.

What is the maximum period of a maximal-length n-bit LFSR, and why is it not 2^n?

It is 2^n−1. The all-zero state cannot be part of the cycle (it is a lock-up state), so exactly one of the 2^n possible states is excluded, leaving 2^n−1.

Structurally, how does a Galois LFSR differ from a Fibonacci LFSR?

Fibonacci uses one external XOR gate that combines several tapped bits and feeds the shift input. Galois places XOR gates inside the register, XORing the single output bit into each tapped position. Galois distributes the logic, giving a shorter feedback path and typically a higher clock frequency.

A maximal 7-bit LFSR runs one full period. How many ones versus zeros does its PRBS output contain?

64 ones and 63 zeros. A maximal-length sequence is balanced with exactly one more one than zero per period, so over 127 bits you get 64 ones and 63 zeros.

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