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:
Shift in (load low): {q[6:0], din}: the vector slides left,
the new serial bit enters at the bottom. Eight clocks turn a serial
bitstream into a parallel byte. This concatenation idiom is one of the
most-used lines in all of Verilog.
Parallel load (load high): the whole register is written at once
, the starting point for shifting a byte out serially (watch dout,
the MSB, as it plays the loaded byte one bit per clock).
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;
`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.
Try it live
Open this lesson in a playground, edit the code, and re-run it.