A shift register is a chain of flip-flops that share one clock, where each flop passes its value to the next on every active clock edge. In Verilog it takes one line of real logic: a concatenation inside a clocked always block, such as shift_reg <= {shift_reg[2:0], data_in};. This post gives a 4-bit serial-in, serial-out (SISO) shift register with a clock, synchronous reset and data input, a testbench for it, and then a parameterised version with a self-checking testbench. All the code below was compiled and simulated with Icarus Verilog.
Key takeaways
- Use non-blocking assignments (
<=) in the clocked block, so every flop samples the old value of its neighbour. - In a pure 4-bit SISO register, a bit entering
data_inappears at the serial output after 4 clock edges. - Registering
data_outas a separate flop, as the first design below does, adds a fifth stage and one more cycle of latency. - Drive testbench inputs away from the active clock edge (for example on the falling edge) to avoid race conditions.
- A self-checking testbench with a reference model is worth more than a page of
$monitoroutput.
Types of shift register
| Type | Input | Output | Typical use |
|---|---|---|---|
| SISO (serial in, serial out) | 1 bit per clock | 1 bit per clock | Delay lines, scan chains |
| SIPO (serial in, parallel out) | 1 bit per clock | All bits at once | Serial receivers, SPI, deserialisers |
| PISO (parallel in, serial out) | All bits at once (load) | 1 bit per clock | Serial transmitters, serialisers |
| PIPO (parallel in, parallel out) | All bits | All bits | Pipeline registers |
| Universal | Serial or parallel, left or right | Serial and parallel | General-purpose blocks such as the 74HC194 |
The design here is a SISO register that also exposes its contents in parallel, so it can be used as a SIPO as well.
The 4-bit shift register module
A Verilog module for a 4-bit shift register with a testbench. The shift register is a simple serial-in, serial-out design with a clock, reset and data input. A `timescale line has been added at the top so that the testbench delays are in nanoseconds.
`timescale 1ns/1ns
module shift_register (
input wire clk, // Clock input
input wire reset, // Synchronous reset
input wire data_in, // Serial data input
output reg data_out, // Serial data output
output reg [3:0] reg_content // Current register content
);
// 4-bit shift register
reg [3:0] shift_reg;
always @(posedge clk) begin
if (reset) begin
shift_reg <= 4'b0000;
data_out <= 1'b0;
end
else begin
// Shift left and insert new data
shift_reg <= {shift_reg[2:0], data_in};
data_out <= shift_reg[3];
end
end
// Assign register content to output
always @(*) begin
reg_content = shift_reg;
end
endmodule
How it works
On every rising edge of clk, when reset is low, {shift_reg[2:0], data_in} builds a new 4-bit value: the lower three bits move up one place and data_in enters at bit 0. Bit 3, the oldest bit, drops out of shift_reg, but on the same edge it is copied into data_out. Because the assignments are non-blocking, data_out gets the value bit 3 had before the edge, which is exactly what we want.
The cost of registering data_out is one extra stage. A bit applied at data_in reaches reg_content[0] on the first edge, reg_content[3] on the fourth and data_out on the fifth. If you want a strict 4-cycle delay, drive data_out straight from shift_reg[3], as the parameterised version further down does. Both are valid; just know which one you have.
The always @(*) block that copies shift_reg to reg_content works, but a continuous assign with reg_content declared as a wire is the more usual style.
The testbench
`timescale 1ns/1ns
module shift_register_tb;
// Testbench signals
reg clk;
reg reset;
reg data_in;
wire data_out;
wire [3:0] reg_content;
// Instantiate the shift register
shift_register uut (
.clk(clk),
.reset(reset),
.data_in(data_in),
.data_out(data_out),
.reg_content(reg_content)
);
// Clock generation
initial begin
clk = 0;
forever #5 clk = ~clk; // 100MHz clock (10ns period)
end
// Test stimulus
initial begin
// Initialize signals
reset = 1;
data_in = 0;
#20;
// Release reset
reset = 0;
#10;
// Test case 1: Shift in 1011
data_in = 1; #10;
data_in = 0; #10;
data_in = 1; #10;
data_in = 1; #10;
// Test case 2: Shift in 0101
data_in = 0; #10;
data_in = 1; #10;
data_in = 0; #10;
data_in = 1; #10;
// Apply reset
reset = 1; #10;
reset = 0; #10;
// Test case 3: Shift in 1111
data_in = 1; #40;
// End simulation
#20;
$finish;
end
// Monitor outputs
initial begin
$monitor("Time=%0t reset=%b data_in=%b data_out=%b reg_content=%b",
$time, reset, data_in, data_out, reg_content);
end
endmodule
Simulating with Icarus Verilog
Save the module as shift_register.v and the testbench as shift_register_tb.v, then run:
iverilog -g2005 -Wall -o sr_sim shift_register.v shift_register_tb.v
vvp sr_sim
The same files also run in ModelSim, Questa, Vivado or any other Verilog simulator. The first part of the output from Icarus Verilog 12.0 is:
Time=0 reset=1 data_in=0 data_out=x reg_content=xxxx
Time=5 reset=1 data_in=0 data_out=0 reg_content=0000
Time=20 reset=0 data_in=0 data_out=0 reg_content=0000
Time=30 reset=0 data_in=1 data_out=0 reg_content=0000
Time=35 reset=0 data_in=1 data_out=0 reg_content=0001
Time=40 reset=0 data_in=0 data_out=0 reg_content=0001
Time=45 reset=0 data_in=0 data_out=0 reg_content=0010
Time=50 reset=0 data_in=1 data_out=0 reg_content=0010
Time=55 reset=0 data_in=1 data_out=0 reg_content=0101
Time=65 reset=0 data_in=1 data_out=0 reg_content=1011
Time=70 reset=0 data_in=0 data_out=0 reg_content=1011
Time=75 reset=0 data_in=0 data_out=1 reg_content=0110
Reading the log:
- At 5 ns the first rising edge with reset high clears the register; before that every output is X because nothing has been initialised.
- Reset is released at 20 ns, and the first bit of 1011 is applied at 30 ns. It is sampled at the 35 ns edge and
reg_contentbecomes 0001. - After four edges (65 ns)
reg_contentholds 1011, the full first pattern. - The first 1 appears on
data_outat 75 ns, on the fifth edge, as explained above. - The reset applied at 110 ns clears everything at the 115 ns edge, and then four 1s fill the register to 1111.
The clock edges fall at 5, 15, 25 ns and so on, while the stimulus changes at multiples of 10 ns, so inputs never change on an active edge. If you change the delays, keep that property, otherwise the result depends on the order in which the simulator runs the two processes.
This implementation includes:
- A 4-bit shift register module (
shift_register.v) that:- Shifts data left on each positive clock edge
- Has a synchronous reset
- Outputs both the serial data and the entire register content
- A testbench (
shift_register_tb.v) that:- Generates a 100 MHz clock (10 ns period)
- Tests several input sequences (1011, 0101, 1111)
- Includes reset testing
- Monitors and displays signal values
The testbench exercises the shift register by applying reset, shifting in different bit patterns, testing reset in mid-stream and showing the serial output and register content. What it does not do is check anything: you have to read the log and decide whether it is right. The next section fixes that.
A parameterised design with a self-checking testbench
The version below takes the width as a parameter, drives data_out directly from the last flop (so latency equals WIDTH cycles), and uses a continuous assignment for reg_content.
`timescale 1ns/1ps
// Parameterised serial-in, serial-out shift register
module shift_register_p #(
parameter WIDTH = 4
) (
input wire clk,
input wire reset, // synchronous, active high
input wire data_in, // serial input
output wire data_out, // serial output (oldest bit)
output wire [WIDTH-1:0] reg_content
);
reg [WIDTH-1:0] shift_reg;
always @(posedge clk) begin
if (reset)
shift_reg <= {WIDTH{1'b0}};
else
shift_reg <= {shift_reg[WIDTH-2:0], data_in};
end
assign data_out = shift_reg[WIDTH-1];
assign reg_content = shift_reg;
endmodule
The testbench keeps a simple reference model of the register, compares the design against it on every falling edge, drives inputs on the falling edge so they are stable at the next rising edge, and finishes with a single PASS or FAIL line. After the fixed patterns it sends 100 random bits.
`timescale 1ns/1ps
// Self-checking testbench for shift_register_p
module shift_register_p_tb;
localparam WIDTH = 4;
reg clk = 1'b0;
reg reset;
reg data_in;
wire data_out;
wire [WIDTH-1:0] reg_content;
reg [WIDTH-1:0] model; // reference model
integer errors = 0;
integer i;
shift_register_p #(.WIDTH(WIDTH)) dut (
.clk(clk), .reset(reset), .data_in(data_in),
.data_out(data_out), .reg_content(reg_content)
);
always #5 clk = ~clk; // 10 ns period, 100 MHz
// Reference model updates on the same edge as the DUT
always @(posedge clk) begin
if (reset) model <= {WIDTH{1'b0}};
else model <= {model[WIDTH-2:0], data_in};
end
// Compare on the falling edge, when everything has settled
always @(negedge clk) begin
if (reg_content !== model || data_out !== model[WIDTH-1]) begin
errors = errors + 1;
$display("FAIL t=%0t reg_content=%b expected=%b data_out=%b",
$time, reg_content, model, data_out);
end
end
// Drive a bit on the falling edge so it is stable at the next rising edge
task shift_in(input b);
begin
@(negedge clk) data_in = b;
end
endtask
initial begin
reset = 1'b1; data_in = 1'b0;
repeat (2) @(negedge clk);
reset = 1'b0;
// 1011, then 0101 (first bit sent first)
shift_in(1); shift_in(0); shift_in(1); shift_in(1);
@(posedge clk) #1 if (reg_content !== 4'b1011) begin
errors = errors + 1; $display("FAIL: expected 1011, got %b", reg_content);
end
shift_in(0); shift_in(1); shift_in(0); shift_in(1);
// Reset in mid-stream
@(negedge clk) reset = 1'b1;
@(negedge clk) reset = 1'b0;
// 1111, then 100 random bits
for (i = 0; i < 4; i = i + 1) shift_in(1);
for (i = 0; i < 100; i = i + 1) shift_in($random);
repeat (2) @(negedge clk);
if (errors == 0) $display("PASS: all checks matched");
else $display("FAILED with %0d errors", errors);
$finish;
end
endmodule
Run it with:
iverilog -g2005 -Wall -o srp_sim shift_register_p.v shift_register_p_tb.v
vvp srp_sim
Expected output:
PASS: all checks matched
To make sure the checker can actually fail, the design was also run with a deliberate bug (the input inverted as it enters the register). The testbench then reported FAILED with 116 errors. Changing WIDTH to 8 in the testbench also passes, which confirms the parameterisation.
Common mistakes
- Blocking assignments in the shift. Writing
q1 = q0; q2 = q1;with=copies the new value straight through, so the register collapses into one stage. Use<=. - Changing inputs on the active edge. If the testbench changes
data_inat the same time as the rising edge, the result depends on simulator scheduling and can differ between tools. - Off-by-one latency. Count the stages. A registered output adds one.
- Shifting the wrong way.
{shift_reg[2:0], data_in}shifts towards the MSB;{data_in, shift_reg[3:1]}shifts towards the LSB. Pick the one that matches the protocol (SPI usually sends MSB first). - No
`timescale. Without it a delay of#5uses the tool's default unit, so comments such as "100 MHz" may not be true.
FAQ
Is this shift register synthesizable?
Yes. Both modules use only a clocked always block, a synchronous reset and continuous assignments, which every synthesis tool supports. The testbenches are for simulation only.
How do I make the reset asynchronous?
Add the reset to the sensitivity list, for example always @(posedge clk or posedge reset), and keep the if (reset) test first. Check which style your target library and design rules prefer.
How would I turn this into a PISO register?
Add a load input and a parallel data bus. When load is high, assign the bus to shift_reg; otherwise shift as before and take the serial output from the end of the chain.
Why compare on the falling edge in the testbench?
By the falling edge every non-blocking update from the rising edge has completed, so the comparison sees settled values and is free of races.

Your comments will be moderated before it appears here.