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
endmoduleSEED = 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
endmoduleSEED = 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
| Property | Fibonacci | Galois |
|---|---|---|
| XOR placement | One gate outside the register | Gates distributed inside |
| Feedback path | Longer (multi-input XOR) | Shorter — often higher fmax |
| Shift direction (here) | Left, insert at LSB | Right, XOR polynomial in |
| Period (8-bit, 0xB8) | 255 | 255 |
| State order | Different sequence of the same 255 states | Different 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
endmodulefirst 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):
| Width | Taps | Max period |
|---|---|---|
| 4 | 4, 3 | 15 |
| 7 | 7, 6 | 127 |
| 8 | 8, 6, 5, 4 | 255 |
| 16 | 16, 15, 13, 4 | 65535 |
| 32 | 32, 22, 2, 1 | 4294967295 |
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 oflfsrwithin 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.
Share your question in comments or talk to our mentor team for batch guidance.
Open this design in the free in-browser simulator — edit, compile and see the waveform/log instantly.
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