VHDL · Chapter 7.7 · Sequential Logic Design
Shift Registers
A shift register is a chain of flip-flops where, on every clock, each bit takes the value of its neighbour while a new bit enters one end. That simple sideways motion does a surprising amount of work: serialising a word onto a single wire, deserialising a serial stream back into a word, building delay lines, and, with feedback, generating pseudo-random sequences. This lesson models shifting cleanly with concatenation and covers the serial-in and parallel-in, serial-out and parallel-out variants. It maps each form to its underlying flip-flop chain and connects shifting back to the one-clock latency of a single flip-flop, since a length-N register is also an N-clock delay. It builds the intuition to reach for shift registers with confidence.
Foundation14 min readVHDLShift RegisterSerialiseDelay LineLFSRRTL
1. Engineering intuition — data marching down a chain
A shift register is a bucket brigade: a row of flip-flops, and on every clock each one passes its value to the next while a fresh value enters at the input end. After N clocks, a value that entered the front has marched to the back. Because each stage is a flip-flop (one clock of latency, lesson 7.3), a length-N shift register is also an N-clock delay line. The same structure, read at the end serially or across all stages in parallel, gives you serialisation, deserialisation, and delay — three of the most common jobs in digital design.
2. Formal explanation — shifting with concatenation
library ieee; use ieee.std_logic_1164.all;
signal sreg : std_logic_vector(7 downto 0);
-- SHIFT LEFT: drop the top bit, bring 'din' in at the bottom (concatenation).
sl : process (clk)
begin
if rising_edge(clk) then
sreg <= sreg(6 downto 0) & din; -- {old bits 6..0, new din} → shifts toward the MSB
end if;
end process;
-- SHIFT RIGHT would be: sreg <= din & sreg(7 downto 1);
serial_out <= sreg(7); -- the bit that has shifted all the way throughA shift register is a vector whose next value is the old vector concatenated with the incoming bit:
sreg <= sreg(n-2 downto 0) & din shifts toward the MSB (left), dropping the top bit and inserting din
at the LSB. Reading one end (sreg(7)) gives the serial output; reading the whole vector gives the
parallel output. The concatenation & is the clean, idiomatic way to express the shift.
3. Production RTL — serialiser (PISO) and deserialiser (SIPO)
library ieee; use ieee.std_logic_1164.all;
-- PISO: parallel-load a word, then shift it out one bit per clock.
piso : process (clk)
begin
if rising_edge(clk) then
if load = '1' then sreg <= parallel_in; -- load all 8 bits
else sreg <= sreg(6 downto 0) & '0'; -- shift out MSB-first
end if;
end if;
end process;
ser_out <= sreg(7);
-- SIPO: shift a serial stream in; read the assembled word in parallel.
sipo : process (clk)
begin
if rising_edge(clk) then
rxreg <= rxreg(6 downto 0) & ser_in; -- assemble 8 bits over 8 clocks
end if;
end process;
word_out <= rxreg;What hardware does this become? Two flip-flop chains. The PISO has a load mux on each stage (parallel load vs shift) and reads the top bit serially — a serialiser (UART transmit, SPI shift-out). The SIPO shifts the serial input through and reads all stages in parallel — a deserialiser (UART receive, SPI shift-in). Both are the same shift chain with different input/output ports.
4. Hardware interpretation — a chain of flip-flops
5. Simulation interpretation — a bit marches through
A single '1' shifts left through an 8-bit register, one stage per clock
8 cycles6. Debugging example — wrong shift direction or off-by-one length
Expected: a serial stream serialised/deserialised correctly. Observed: the bits come out reversed,
or one bit short/extra, or misaligned with a framing signal. Root cause: the concatenation order
shifted the wrong way (MSB-first vs LSB-first) relative to the protocol, or the register length did not
match the word size (off-by-one), or the load/shift control was timed one clock off. Fix: match the
concatenation direction to the protocol's bit order (sreg(n-2 downto 0) & din for MSB-first out, or the
reverse), size the register to the word, and align the load/shift enable with the framing. Engineering
takeaway: a shift register's correctness is direction + length + timing — pin down bit order, word
length, and exactly when load versus shift happens.
-- MSB-first serial OUT (shift left, read the top bit):
sreg <= sreg(6 downto 0) & '0'; ser_out <= sreg(7);
-- LSB-first serial OUT (shift right, read the bottom bit):
-- sreg <= '0' & sreg(7 downto 1); ser_out <= sreg(0);7. Common mistakes & what to watch for
- Wrong shift direction / bit order. Match
&concatenation to the protocol (MSB-first vs LSB- first); getting it backwards reverses the bits. - Length mismatch. Size the register to the word; an off-by-one length drops or adds a bit.
- Load/shift timing. Align the load (parallel-in) and shift enable with the framing/valid signals; one-cycle slips misalign the data.
- Forgetting the N-clock latency. A length-N shift register delays by N clocks; account for it in surrounding logic.
- Unintended feedback. Feeding the output back into the input with XOR taps makes an LFSR — powerful, but only when intended; otherwise keep the input a real data source.
8. Engineering insight & continuity
The shift register is motion through flip-flops: one stage per clock, which simultaneously serialises, deserialises, and delays depending on which ports you read. Express it with concatenation, size it to the word, and get the direction and timing right, and it underlies every serial interface (UART, SPI), every delay line, and — with feedback taps — LFSRs for pseudo-random sequences and CRCs. With registers, enables, counters, and shift registers covered, the module's core structures are complete. The remaining two lessons address pitfalls and subtleties: Gated Clocks and Why to Avoid Them (why clock-enables, not clock gating, are the right tool) and Signals versus Variables in Clocked Processes (how the two behave when registered).