1. Home
  2. Computer Organization & Architecture
  3. Instruction Pipelining

Instruction Pipelining

How a CPU works on five instructions at once, like an assembly line. Watch instructions flow through IF, ID, EX, MEM and WB in 3D, and see stalls and forwarding fix data hazards.

Interactive 3DIntermediate14 min readCOAUpdated

Drag to rotate · Right-drag to pan · Click, then scroll to zoom · Space play · ←→ step

What's happening

Pseudocode

    Try this in the 3D model

    • Run No pipelining, then Pipeline + forwarding. Compare the total cycles.
    • In Pipeline (stalls only) mode, find the red stall cells. Which register is each instruction waiting for?
    • With forwarding on, one stall is left after the lw. Why can't forwarding remove it?
    • Switch to Independent instructions. How close does it get to the ideal n + 4 cycles?

    The assembly-line idea

    Doing a load of laundry has stages: wash, dry, fold. With one load at a time, the washer sits idle while you fold. A smarter way: start washing load 2 while load 1 is drying. Pipelining does the same thing with instructions.

    A classic RISC CPU splits every instruction into 5 stages:

    Stage Name What happens
    IF Instruction fetch read the instruction from memory (at address PC)
    ID Instruction decode work out what it is, read source registers
    EX Execute the ALU computes (add, compare, address)
    MEM Memory access loads read memory, stores write it
    WB Write back write the result into the destination register

    Each stage takes one clock cycle. Without pipelining, an instruction finishes every 5 cycles. With pipelining, once the pipe is full, one instruction finishes every cycle.

    In the 3D model, the five coloured blocks at the top are the pipeline hardware and the spheres are instructions flowing through it. Below is the space-time diagram: one row per instruction, one column per clock cycle.

    Speed-up

    For n instructions and k stages:

    • Non-pipelined: n × k cycles
    • Pipelined (ideal): k + (n − 1) cycles
    • Speed-up = nk / (k + n − 1) → approaches k for large n

    With 6 instructions: 30 cycles versus 10 cycles — 3× faster. Pipelining doesn’t make a single instruction faster (latency stays 5 cycles); it increases throughput.

    Hazards: when the pipeline must wait

    1. Structural hazards — two instructions need the same hardware in the same cycle (e.g. a single memory port for both IF and MEM). Fixed with more hardware (separate instruction and data caches).

    2. Data hazards — an instruction needs a result that isn’t ready yet:

      lw  r1, 0(r2)
      add r3, r1, r4    ← needs r1, which lw is still loading
    3. Control hazards — after a branch, the CPU doesn’t yet know which instruction comes next. Fixed with branch prediction.

    Fixing data hazards

    Stalling — hold the dependent instruction in ID and insert bubbles (the red cells) until the value is written back. Simple but slow: in the demo, 6 instructions take 18 cycles.

    Forwarding (bypassing) — the result actually exists at the end of EX, two stages before WB. Extra wires carry it straight from the EX/MEM or MEM/WB pipeline latch to the ALU input (the green arrows). The same program now takes 11 cycles.

    The load-use hazard — a load only has its data at the end of MEM. The next instruction needs it at the start of EX in the same cycle, so even forwarding needs one stall. Compilers avoid it by reordering an independent instruction into that slot.

    Code: a pipeline scheduler

    def pipeline(instrs, forwarding=True):
        """instrs: list of (op, dest, [sources]). Returns the cycle of each stage."""
        times = []
        for i, (op, dest, srcs) in enumerate(instrs):
            if i == 0:
                IF, ID, EX = 0, 1, 2
            else:
                p = times[-1]
                IF = p["ID"]
                ID = max(IF + 1, p["EX"])
                EX = max(ID + 1, p["MEM"])
            for j in range(i - 1, -1, -1):            # RAW dependencies
                if instrs[j][1] in srcs:
                    t = times[j]
                    if not forwarding:
                        ready = t["WB"] + 1
                    elif instrs[j][0] == "lw":
                        ready = t["MEM"] + 1
                    else:
                        ready = t["EX"] + 1
                    EX = max(EX, ready)
            times.append({"IF": IF, "ID": ID, "EX": EX, "MEM": EX + 1, "WB": EX + 2})
        return times
    
    prog = [("lw", "r1", ["r2"]), ("add", "r3", ["r1", "r4"]), ("sub", "r5", ["r3", "r6"]),
            ("and", "r7", ["r8", "r9"]), ("or", "r2", ["r5", "r7"]), ("sw", None, ["r2", "r1"])]
    print(pipeline(prog)[-1]["WB"] + 1)          # 11 cycles with forwarding
    print(pipeline(prog, False)[-1]["WB"] + 1)   # 18 cycles with stalls only

    Common mistakes

    • Thinking pipelining makes each instruction faster — it makes the stream faster.
    • Counting cycles as n × k for a pipeline. The formula is k + n − 1 (plus stalls).
    • Forgetting that register files write in the first half of a cycle and read in the second, so WB and ID can share a cycle.

    Complexity at a glance

    Case / operationTimeWhy
    Non-pipelined (n instructions, k stages)n · k cycles
    Ideal pipelinek + (n − 1) cyclesAfter the pipeline fills, one instruction finishes every cycle.
    Ideal speed-up≈ k (as n grows)

    Quick check

    Test yourself — pick an answer to see if you got it.

    1. In an ideal 5-stage pipeline, how many cycles do 6 instructions take?

    2. What is a data hazard?

    3. What does forwarding (bypassing) do?

    4. Why does a load followed by an instruction that uses the loaded value still need one stall with forwarding?

    Saved only in this browser — no account needed.
    Spotted a mistake or a bug in the 3D model?

    Report a mistake

    in Instruction Pipelining. Thank you — every report makes the lesson better for the next reader.

    We'll also include a link to the step of the 3D model you're on and your browser type, so we can reproduce it.