Video 2 of 4 · ~10 minutes
Dr. Mike Borowczak · Electrical & Computer Engineering · CECS · UCF
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.
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.
“Shifting is a software bit operation. data = data << 1 is the same in hardware.”
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.
SB_DFF (~0.6% of the chip). Scales linearly with depth.
| Type | Input | Output | Use Case |
|---|---|---|---|
| SIPO | Serial (1 bit/cycle) | Parallel (N bits after N cycles) | UART RX, SPI MISO receive |
| PISO | Parallel (N bits loaded) | Serial (1 bit/cycle) | UART TX, SPI MOSI transmit |
| SISO | Serial | Serial (delayed) | Delay lines, FIFO stages |
| PIPO | Parallel | Parallel (shifted) | Barrel shifters, ALU shifts |
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
{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.
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
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.
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?
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.
~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.
$ 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!
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.
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.
① 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.
🔗 Transfer
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.