Why Learn Shift Registers? Almost every serial interface on a chip, including SPI, UART, I2C and the high-speed links between chips, moves data one bit at a time. Shift registers convert between those serial bit streams and the parallel words the rest of the design works with. The same structure appears in CRC generators, pseudo-random number generators and delay lines. In this tutorial, you'll build a parameterized shift register that can shift in either direction, and see what hardware it becomes.

A shift register is a chain of flip-flops in which the output of each flip-flop drives the data input of the next. All flip-flops share the same clock, so on every clock edge the stored bits move one position along the chain, and a new bit enters at one end. A bidirectional shift register can move its bits either left or right, selected by a control input.

12 min read | Beginner Level

What You'll Learn

  • How a shift register moves bits one position per clock, and the four basic shift register types
  • Write an n-bit bidirectional shift register with enable and reset in Verilog
  • What synthesis builds for each bit, and how concatenation sets the shift direction
  • Verify the design with a testbench and avoid common shift register mistakes

How a Shift Register Works

Consider a 5-bit register holding 10110 that shifts right, with its serial input tied to 0. On each clock edge, every bit moves one position to the right, the rightmost bit is shifted out, and a 0 enters on the left:

Start     : 10110
1st clock : 01011
2nd clock : 00101
5-bit shift register

Shift registers are grouped by how data enters and leaves them:

TypeData inData outTypical use
SISO (serial in, serial out)1 bit per clock1 bit per clockDelay lines
SIPO (serial in, parallel out)1 bit per clockAll bits at onceReceiving serial data
PISO (parallel in, serial out)All bits at once1 bit per clockTransmitting serial data
PIPO (parallel in, parallel out)All bits at onceAll bits at onceStorage registers, bit manipulation

The design in this article takes one serial input bit and makes all of its bits available as a parallel output, so it is a SIPO shift register, with the added choice of shift direction.

Design

The module has five inputs and one output:

  • d - Serial data bit that enters the register on each shift
  • clk - Clock shared by all flip-flops
  • en - Enable: shift when 1, hold the current value when 0
  • dir - Direction: 0 shifts left (d enters at bit 0), 1 shifts right (d enters at the MSB)
  • rstn - Active-low synchronous reset that clears the register
  • out - The current value of all flip-flops

The parameter MSB sets the number of bits in the register, for example 4, 8 or 16. Despite its name, it is the register width, so out is declared [MSB-1:0].


module shift_reg  #(parameter MSB=8) (  input d,                      // Declare input for data to the first flop in the shift register
                                        input clk,                    // Declare input for clock to all flops in the shift register
                                        input en,                     // Declare input for enable to switch the shift register on/off
                                        input dir,                    // Declare input to shift in either left or right direction
                                        input rstn,                   // Declare input to reset the register to a default value
                                        output reg [MSB-1:0] out);    // Declare output to read out the current value of all flops in this register

   // This always block will "always" be triggered on the rising edge of clock
   // Once it enters the block, it will first check to see if reset is 0 and if yes then reset register
   // If no, then check to see if the shift register is enabled
   // If no => maintain previous output. If yes, then shift based on the requested direction
   always @ (posedge clk)
      if (!rstn)
         out <= 0;
      else begin
         if (en)
            case (dir)
               0 :  out <= {out[MSB-2:0], d};    // Shift left: drop the MSB, d enters at bit 0
               1 :  out <= {d, out[MSB-1:1]};    // Shift right: drop bit 0, d enters at the MSB
            endcase
         else
            out <= out;                          // Hold
      end
endmodule

Each shift is written as a single concatenation. For a left shift, {out[MSB-2:0], d} takes every bit except the MSB, moves it one position up, and places d in bit 0. For a right shift, {d, out[MSB-1:1]} takes every bit except bit 0, moves it one position down, and places d in the MSB.

Synthesis result: The tool builds MSB flip-flops. In front of each one is a small mux that selects its next value: its left neighbor, its right neighbor (or d at the ends), or its own current value when en is 0. The hold path is often implemented as a clock enable on the flip-flops instead of a mux input. Reset gating forces each D input to 0 when rstn is low. There is no arithmetic, so each flip-flop's input depends on only a few signals and the register runs at high clock speeds.

Quick Check

Q: An 8-bit shift_reg is reset, then enabled with dir = 0. On three consecutive clock edges, d is 1, 0 and 1. What is out?

Show Answer

8'b0000_0101. With a left shift, each new bit enters at bit 0 and pushes the earlier bits up, so the first bit received ends up highest: 1, then 10, then 101.

Where are Shift Registers Used?

An SPI or UART receiver is a SIPO shift register: it shifts in one bit from the serial line on each bit time, and after 8 bits the parallel output holds a full byte for the rest of the chip. The transmitter is the reverse, a PISO register that is loaded with a byte and shifts it out one bit at a time. The direction setting decides whether the most or the least significant bit travels first, which differs between protocols.

Hardware Schematic

Synthesized schematic of the 8-bit bidirectional shift register: eight flip-flops, each with a mux selecting the left neighbor, the right neighbor or the held value

The schematic shows the 8-bit version. Each flip-flop's mux connects to its two neighbors, and the end flip-flops connect to d instead of a missing neighbor.

Testbench

The testbench instantiates a 16-bit version of the design and uses a 20 ns clock period (50 MHz). It applies and releases reset, enables the register, then:

  1. Toggles data on 7 clock edges while shifting left (dir = 0)
  2. Switches to dir = 1 and toggles data on 7 more edges while shifting right
  3. Stops changing data for 7 edges, so the same value keeps shifting in
n-bit shift register TB

module tb_sr;
   parameter MSB = 16;        // [Optional] Declare a parameter to represent number of bits in shift register

   reg data;                  // Declare a variable to drive d-input of design
   reg clk;                   // Declare a variable to drive clock to the design
   reg en;                    // Declare a variable to drive enable to the design
   reg dir;                   // Declare a variable to drive direction of shift register
   reg rstn;                  // Declare a variable to drive reset to the design
   wire [MSB-1:0] out;        // Declare a wire to capture output from the design

   // Instantiate design (16-bit shift register) by passing MSB and connect with TB signals
   shift_reg  #(MSB) sr0  (  .d (data),
                             .clk (clk),
                             .en (en),
                             .dir (dir),
                             .rstn (rstn),
                             .out (out));

   // Generate clock time period = 20ns, freq => 50MHz
   always #10 clk = ~clk;

   // Initialize variables to default values at time 0
   initial begin
      clk <= 0;
      en <= 0;
      dir <= 0;
      rstn <= 0;
      data <= 'h1;
   end

   // Drive main stimulus to the design to verify if this works
   initial begin

      // 1. Apply reset and deassert reset after some time
      rstn <= 0;
      #20 rstn <= 1;
          en <= 1;

	  // 2. For 7 clocks, drive alternate values to data pin
      repeat (7) @ (posedge clk)
         data <= ~data;

     // 3. Shift direction and drive alternate value to data pin for another 7 clocks
      #10 dir <= 1;
      repeat (7) @ (posedge clk)
         data <= ~data;

      // 4. Drive nothing for next 7 clocks, allow shift register to simply shift based on dir
      repeat (7) @ (posedge clk);

      // 5. Finish the simulation
      $finish;
   end

   // Monitor values of these variables and print them into the logfile for debug
   initial
      $monitor ("rstn=%0b data=%b, en=%0b, dir=%0b, out=%b", rstn, data, en, dir, out);
endmodule
Output
ncsim> run
rstn=0 data=1, en=0, dir=0, out=xxxxxxxxxxxxxxxx
rstn=0 data=1, en=0, dir=0, out=0000000000000000
rstn=1 data=1, en=1, dir=0, out=0000000000000000
rstn=1 data=0, en=1, dir=0, out=0000000000000001
rstn=1 data=1, en=1, dir=0, out=0000000000000010
rstn=1 data=0, en=1, dir=0, out=0000000000000101
rstn=1 data=1, en=1, dir=0, out=0000000000001010
rstn=1 data=0, en=1, dir=0, out=0000000000010101
rstn=1 data=1, en=1, dir=0, out=0000000000101010
rstn=1 data=0, en=1, dir=0, out=0000000001010101
rstn=1 data=0, en=1, dir=1, out=0000000001010101
rstn=1 data=1, en=1, dir=1, out=0000000000101010
rstn=1 data=0, en=1, dir=1, out=1000000000010101
rstn=1 data=1, en=1, dir=1, out=0100000000001010
rstn=1 data=0, en=1, dir=1, out=1010000000000101
rstn=1 data=1, en=1, dir=1, out=0101000000000010
rstn=1 data=0, en=1, dir=1, out=1010100000000001
rstn=1 data=1, en=1, dir=1, out=0101010000000000
rstn=1 data=1, en=1, dir=1, out=1010101000000000
rstn=1 data=1, en=1, dir=1, out=1101010100000000
rstn=1 data=1, en=1, dir=1, out=1110101010000000
rstn=1 data=1, en=1, dir=1, out=1111010101000000
rstn=1 data=1, en=1, dir=1, out=1111101010100000
rstn=1 data=1, en=1, dir=1, out=1111110101010000
Simulation complete via $finish(1) at time 430 NS + 0

Reading the log in three parts:

  • Left shift (dir=0): each new data bit enters at bit 0, and the alternating pattern grows from the right: ...0001, ...0010, ...0101, up to ...01010101.
  • Right shift (dir=1): the existing pattern moves back toward bit 0 and drops off the right end, while new bits enter at bit 15. The pattern now grows from the left: 1000..., 0100..., 1010....
  • Constant data: data stays 1, so a 1 enters at bit 15 on every edge and a run of 1s fills the register from the left: 1101..., 1110..., 1111....
n-bit shift register waveform

CRC and Pseudo-Random Generators

Network and storage interfaces compute CRC checksums with a shift register whose input is the XOR of the incoming data bit and selected register bits. Built-in self-test logic and scramblers use a similar structure, a linear feedback shift register (LFSR), to generate long pseudo-random bit sequences from a handful of flip-flops and XOR gates.

Quick Check

Q: A 16-bit shift_reg is used as a serial receiver. How many enabled clock edges does it take before every bit of out holds received data?

Show Answer

16. Each enabled edge shifts in one bit, so the first bit received reaches the far end of the register after 16 edges. Before that, the bits that have not yet been filled still hold their reset value.

Delay Lines and Pipeline Alignment

When two signals take different numbers of pipeline stages to reach the same point, such as a data word and its valid flag, the shorter path is delayed with a shift register so both arrive in the same cycle. A shift register of length N delays its input by exactly N clock cycles.

Common Beginner Mistakes

Mistake #1: Mixing up left and right in the concatenation

Wrong: Labeling {out[MSB-2:0], d} as a right shift
Right: {out[MSB-2:0], d} shifts left; {d, out[MSB-1:1]} shifts right

Why? In a concatenation, the leftmost item becomes the most significant bits. Putting out[MSB-2:0] first moves every bit up by one position, which is a left shift. Check the direction by tracing where d ends up: bit 0 for a left shift, the MSB for a right shift.

Mistake #2: Using the shift operator and losing the serial input

Wrong: out <= out << 1;
Right: out <= {out[MSB-2:0], d};

Why? The << operator always fills the vacated bit with 0, so d never enters the register. It is correct for multiplying by 2, but a shift register needs the concatenation, or (out << 1) | d.

Mistake #3: Choosing the wrong part-select

Wrong: out <= {out[MSB-1:1], d};
Right: out <= {out[MSB-2:0], d};

Why? out[MSB-1:1] keeps the upper bits in their current positions and only replaces bit 0 with d. Nothing actually shifts. The width is still correct, so the compiler gives no warning.

Mistake #4: Writing the shift bit by bit with blocking assignments

Wrong: for (i = 1; i < MSB; i = i + 1) out[i] = out[i-1]; out[0] = d;
Right: Use non-blocking assignments, or the single concatenation shown in the design

Why? With blocking assignments, each bit is updated before the next iteration reads it. Counting upward, out[1] gets out[0], then out[2] gets the new out[1], which is also the old out[0], and so on, so the old bit 0 is copied into every position in one clock. Non-blocking assignments make every bit read its neighbor's value from before the clock edge, regardless of order. See Verilog Blocking & Non-Blocking.

Practice Exercise

Challenge: Universal Shift Register

Extend the design into a module univ_shift_reg with a parameter N for the width and a 2-bit mode input instead of en and dir:

  • 2'b00 - Hold
  • 2'b01 - Shift right, with serial input sr_in entering at the MSB
  • 2'b10 - Shift left, with serial input sl_in entering at bit 0
  • 2'b11 - Parallel load from an N-bit input pin

Keep the active-low synchronous reset. This register can act as any of the four types from the table above.

Hint

Use a case statement on mode with one item per operation. The shift items are the same concatenations as in shift_reg. See Verilog case statement.

Solution

module univ_shift_reg #(parameter N = 8) (
  input              clk,
  input              rstn,     // Active-low synchronous reset
  input      [1:0]   mode,     // 00 hold, 01 right, 10 left, 11 load
  input              sr_in,    // Serial input for right shifts (enters at MSB)
  input              sl_in,    // Serial input for left shifts (enters at bit 0)
  input      [N-1:0] pin,      // Parallel load input
  output reg [N-1:0] q
);

  always @(posedge clk) begin
    if (!rstn)
      q <= {N{1'b0}};
    else
      case (mode)
        2'b00 : q <= q;                      // Hold
        2'b01 : q <= {sr_in, q[N-1:1]};      // Shift right
        2'b10 : q <= {q[N-2:0], sl_in};      // Shift left
        2'b11 : q <= pin;                    // Parallel load
      endcase
  end

endmodule

Synthesis builds N flip-flops, each preceded by a 4 to 1 mux selected by mode, with inputs from its own output, its two neighbors and its pin bit. Loading a word and then shifting right turns it into a PISO transmitter; shifting in serial bits and reading q makes it a SIPO receiver. This is the structure of classic 4-bit universal shift register chips.

Summary

In this tutorial, you learned:

  • A shift register is a chain of flip-flops on a common clock that moves its bits one position per clock edge
  • Shift registers are classified as SISO, SIPO, PISO or PIPO by how data enters and leaves
  • {out[MSB-2:0], d} shifts left with d entering at bit 0; {d, out[MSB-1:1]} shifts right with d entering at the MSB
  • Synthesis builds one flip-flop per bit with a small mux choosing between neighbors and the held value
  • The << and >> operators fill with 0 and do not insert a serial input
  • Use non-blocking assignments, or a single concatenation, so every bit reads its neighbor's old value
  • Shift registers are the core of serial interfaces, CRC generators and delay lines
Did you find what you were looking for?

Quiz

Test Your Knowledge

1 of 5 Score: 0 / 5