Shift Registers

A shift register is a chain of flip-flops where each one feeds the next: every clock, the whole pattern moves one position. It's the workhorse behind serializers (UART, SPI), deserializers, delay lines and, in a pinch, the classic dev-board LED chaser.

The design shows the two fundamental operations on one 8-bit register:

In the waveform: after the load of 8'hA5 you can read the byte marching out of dout MSB-first, 1,0,1,0,0,1,0,1, while zeros (from din) fill in behind it.

Experiment: wire din = dout to make a ring that rotates forever, or tap two bits into an XOR feeding din, congratulations, you've built the LFSR from our generator.

The design

Verilog, design.v
// 8-bit shift register with parallel load and serial in/out.
module shifter (
    input  wire       clk,
    input  wire       rst,
    input  wire       load,     // 1: parallel load, 0: shift
    input  wire [7:0] pdata,    // parallel load value
    input  wire       din,      // serial in (enters at bit 0)
    output wire       dout,     // serial out (MSB first)
    output reg  [7:0] q
);
    always @(posedge clk) begin
        if (rst)
            q <= 8'd0;
        else if (load)
            q <= pdata;
        else
            q <= {q[6:0], din};   // shift left, din enters LSB
    end

    assign dout = q[7];
endmodule
Show the VHDL version
VHDL, design.vhd
-- 8-bit shift register with parallel load and serial in/out.
library ieee;
use ieee.std_logic_1164.all;

entity shifter is
    port (
        clk   : in  std_logic;
        rst   : in  std_logic;
        load  : in  std_logic;
        pdata : in  std_logic_vector(7 downto 0);
        din   : in  std_logic;
        dout  : out std_logic;
        q     : out std_logic_vector(7 downto 0)
    );
end entity;

architecture rtl of shifter is
    signal r : std_logic_vector(7 downto 0) := (others => '0');
begin
    process (clk) begin
        if rising_edge(clk) then
            if rst = '1' then
                r <= (others => '0');
            elsif load = '1' then
                r <= pdata;
            else
                r <= r(6 downto 0) & din;
            end if;
        end if;
    end process;
    q    <= r;
    dout <= r(7);
end architecture;
Show the MyHDL (Python) version
MyHDL, design.py
from myhdl import block, Signal, intbv, concat, always, always_comb, instance, delay, StopSimulation

@block
def shifter(clk, rst, load, pdata, din, dout, q):
    r = Signal(intbv(0)[8:])

    @always(clk.posedge)
    def logic():
        if rst:
            r.next = 0
        elif load:
            r.next = pdata
        else:
            r.next = concat(r[7:0], din)   # shift left, din enters LSB

    @always_comb
    def outs():
        q.next = r
        dout.next = r[7]
    return logic, outs

@block
def tb():
    clk = Signal(bool(0))
    rst = Signal(bool(1))
    load, din = Signal(bool(0)), Signal(bool(0))
    pdata = Signal(intbv(0xA5)[8:])
    dout = Signal(bool(0))
    q = Signal(intbv(0)[8:])
    dut = shifter(clk, rst, load, pdata, din, dout, q)

    @always(delay(5))
    def clkgen():
        clk.next = not clk

    @instance
    def stim():
        yield delay(12); rst.next = 0
        yield delay(10); load.next = 1
        yield delay(10); load.next = 0
        yield delay(80); din.next = 1
        yield delay(40); raise StopSimulation
    return dut, clkgen, stim

inst = tb()
inst.config_sim(trace=True)
inst.run_sim()

The testbench

Verilog, tb.v
`timescale 1ns/1ns
module tb;
    reg clk = 0, rst = 1, load = 0, din = 0;
    reg [7:0] pdata = 8'hA5;
    wire dout;
    wire [7:0] q;

    shifter dut (.clk(clk), .rst(rst), .load(load), .pdata(pdata),
                 .din(din), .dout(dout), .q(q));

    always #5 clk = ~clk;

    initial begin
        $dumpfile("wave.vcd"); $dumpvars(0, tb);
        #12 rst = 0;
        #10 load = 1;      // load 0xA5
        #10 load = 0;      // now shift it out MSB-first on dout
        #80                // 8 shifts: dout plays 1,0,1,0,0,1,0,1
        din = 1;           // then shift ones in for contrast
        #40 $finish;
    end
endmodule
Show the VHDL testbench
VHDL, tb.vhd
library ieee;
use ieee.std_logic_1164.all;
entity tb is end entity;
architecture sim of tb is
  signal clk : std_logic := '0';
  signal rst : std_logic := '1';
  signal load, din : std_logic := '0';
  signal pdata : std_logic_vector(7 downto 0) := x"A5";
  signal dout : std_logic;
  signal q : std_logic_vector(7 downto 0);
begin
  dut : entity work.shifter port map (clk=>clk, rst=>rst, load=>load,
        pdata=>pdata, din=>din, dout=>dout, q=>q);
  clk <= not clk after 5 ns;
  process begin
    wait for 12 ns; rst  <= '0';
    wait for 10 ns; load <= '1';         -- load 0xA5
    wait for 10 ns; load <= '0';         -- shift it out MSB-first
    wait for 80 ns; din  <= '1';         -- then shift ones in
    wait for 40 ns; std.env.stop;
  end process;
end architecture;

The MyHDL version keeps the design and its testbench in one design.py.

Simulated waveform

This trace was produced by actually simulating the code above with Icarus Verilog.

15 30 45 60 75 90 105 120 135 150 t (ns) q[7:0] x 0 A5 4A 94 28 50 A0 40 80 0 1 3 7 F dout clk din load pdata[7:0] A5 rst

Try it live

Open this lesson in a playground, edit the code, and re-run it.

Verilog → VHDL → MyHDL →

Put it to work

Tools that apply what this lesson covers.