Day 5 · Counters, Shifters & Sync

Shift Registers

Video 2 of 4 · ~10 minutes

Dr. Mike Borowczak · Electrical & Computer Engineering · CECS · UCF

Counter VariationsShift RegistersMetastabilityDebouncing

🌍 Where This Lives

In Industry

Every serial link in a modern SoC — UART, SPI, I2C, JTAG, PCIe serdes, Ethernet PHY — has shift registers at its heart. Even high-speed SerDes (28 Gbps+) runs on parallel-in-serial-out / serial-in-parallel-out architectures internally. Mastering SIPO and PISO means mastering the innards of every serial protocol you'll ever touch.

In This Course

Day 11 UART transmitter is PISO. Day 12 UART receiver is SIPO + oversampling. Day 12 SPI shifts both in and out simultaneously. Your capstone uses these patterns. Today's video is the foundation.

⚠️ A Shift Register Is a Pipeline of Flops

❌ Wrong Model

“Shifting is a software bit operation. data = data << 1 is the same in hardware.”

✓ Right Model

A shift register is an N-deep pipeline of flip-flops, each one's Q feeding the next one's D. On each clock edge, every flop loads its predecessor's value. The “shift” is physical — bits march through flops, one stage per clock cycle.

The receipt: An 8-bit shift register costs 8 flops. Not one shifter unit — 8 independent flops. On iCE40, 8× SB_DFF (~0.6% of the chip). Scales linearly with depth.

The Four Shift Register Types

TypeInputOutputUse Case
SIPOSerial (1 bit/cycle)Parallel (N bits after N cycles)UART RX, SPI MISO receive
PISOParallel (N bits loaded)Serial (1 bit/cycle)UART TX, SPI MOSI transmit
SISOSerialSerial (delayed)Delay lines, FIFO stages
PIPOParallelParallel (shifted)Barrel shifters, ALU shifts
SIPO and PISO are the workhorses — every serial protocol pairs one of each at the two endpoints.

👁️ I Do — SIPO (Serial-In, Parallel-Out)

module sipo_8bit (
    input  wire       i_clk, i_reset, i_serial_in, i_shift_en,
    output reg  [7:0] o_parallel
);
    always @(posedge i_clk) begin
        if (i_reset)        o_parallel <= 8'b0;
        else if (i_shift_en) o_parallel <= {o_parallel[6:0], i_serial_in};
        //                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        //                                  shift left, new bit at LSB
    end
endmodule
My thinking: Concatenation {old_bits_shifted, new_bit} does the shift. Every cycle the enable is high, the oldest bit falls off the MSB and the new serial bit enters at LSB. After 8 enable-active cycles, the parallel output holds the received byte.

🤝 We Do — PISO (Parallel-In, Serial-Out)

module piso_8bit (
    input  wire       i_clk, i_reset, i_load, i_shift_en,
    input  wire [7:0] i_parallel,
    output wire       o_serial
);
    reg [7:0] r_shift;
    always @(posedge i_clk) begin
        if (i_reset)         r_shift <= 8'hFF;       // idle=1 for UART
        else if (i_load)     r_shift <= i_parallel;   // load parallel byte
        else if (i_shift_en) r_shift <= {1'b1, r_shift[7:1]};  // shift right
    end
    assign o_serial = r_shift[0];                     // LSB first
endmodule
Together: Load has priority over shift (you can't load and shift same cycle). r_shift[0] is exposed as the serial output — LSB first, matching UART convention. Shift-in bit is 1'b1 (idle state) so after N shifts, the register returns to idle.

🧪 You Do — Trace the SIPO

Starting o_parallel = 8'b00000000. Serial input stream: 1, 0, 1, 1, 0, 0, 1, 0 (one bit per cycle, enable=1 every cycle).

What is o_parallel after 8 cycles?

Answer: 8'b10110010 = 8'hB2. The first serial bit (1) ends up as MSB after 8 shifts. Last received bit (0) is LSB. Reading the result MSB-first gives the input stream in transmission order.
▶ LIVE DEMO

Shift Register in GTKWave

~4 minutes

▸ COMMANDS

cd labs/week2_day05/ex2_sipo/
make sim
make wave
make stat   # 8× SB_DFF

▸ EXPECTED STDOUT

Cycle 1: in=1, par=00000001
Cycle 2: in=0, par=00000010
Cycle 3: in=1, par=00000101
...
Cycle 8: par=10110010
PASS: assembled 0xB2

▸ GTKWAVE — THE SIGNATURE SHAPE

Add all 8 bits of o_parallel separately (not as a bus). Watch them “walk” to the left each clock edge — new bits enter at the right (LSB), old bits climb toward the MSB and fall off. That marching pattern is what a shift register looks like.

🔧 What Did the Tool Build?

$ yosys -p "read_verilog sipo_8bit.v; synth_ice40 -top sipo_8bit; stat" -q

=== sipo_8bit ===
   Number of wires:                 14
   Number of cells:                  8
     SB_CARRY                        0
     SB_DFFESR                       8    ← 8 D-flops with enable + sync reset
     SB_LUT4                         0    ← zero combinational logic!
Zero LUTs. The shift is pure wire routing — o_parallel[6:0] feeds back into bits [7:1], i_serial_in feeds bit [0]. The iCE40 DFFESR has enable and sync reset built in, so even the shift enable logic fits inside the flops. Peak efficiency for this pattern.

🤖 Check the Machine

Ask AI: “Write a parameterized N-bit SIPO shift register that can also shift right, with a direction select input. Predict the LUT count on iCE40.”

TASK

Bidirectional parameterized SIPO.

BEFORE

Predict: N flops, ~N LUTs for direction mux (1 LUT per bit selecting neighbor).

AFTER

Good AI handles parameterization. Watch for wrong concatenation direction.

TAKEAWAY

Bidirectional adds a per-bit mux → doubles LUT count compared to unidirectional.

Key Takeaways

 Shift registers are pipelines of flops, one per bit.

 SIPO = serial receive. PISO = serial transmit. Every protocol pairs them.

 Concatenation {old[N-2:0], new} = shift left by one.

 N-bit shift register = N flops, zero LUTs on iCE40.

Serial protocols are SIPO at one end and PISO at the other. Build both well.

🔗 Transfer

Metastability & Synchronizers

Video 3 of 4 · ~10 minutes

▸ WHY THIS MATTERS NEXT

Serial data arrives from outside your chip — from a keyboard, sensor, or another SoC — with no relationship to your clock. Video 3 covers metastability: what happens when asynchronous signals hit a flip-flop at the wrong moment, and the 2-flop synchronizer pattern that fixes it. This is the most-ignored, highest-stakes concept in synchronous design.