The pipeline
A pipeline splits each instruction into five stages: fetch, decode, execute, memory and write back. At every clock tick each instruction moves one stage on, so up to five overlap. These four LDIs never need each other's results.
Data hazards
A hazard stops the next instruction going on safely. ADD r0, r1 makes r0 = 2 + 3 = 5. In cycle 3, OUT r0 reads r0 in decode, but ADD writes 5 back only in cycle 5. OUT would read the old 2.
The simplest fix is a stall: hold OUT in decode and send empty bubbles down the pipeline instead. A bubble does nothing. In cycle 5, ADD writes r0 in the first half of the cycle and OUT reads 5 in the second half.
A pipeline chart has one row per instruction and one column per clock cycle. With the stall, OUT finishes in cycle 8 instead of 6. Two bubbles cost two cycles, every time a result is needed straight away.
The answer 5 already exists at the end of cycle 3, as ADD leaves execute. Forwarding adds a wire that carries it straight back to the execute input, so OUT uses 5 in cycle 4 without waiting for write back.
With forwarding the chart has no bubbles and OUT finishes in cycle 6. One case still waits a cycle: LD only has its value after the memory stage, so an instruction that uses it at once gets one bubble.
Control hazards
JNZ 12 jumps back to SUB at address 12 when the zero flag is 0. This pipeline checks the flag in execute, in cycle 4. By then it has already fetched the next two instructions, HALT at 16 and 18.
The jump is taken, so the two wrong instructions are flushed: turned into bubbles that do nothing. The PC becomes 12 and SUB is fetched again in cycle 5. Every taken JNZ costs two lost cycles.
Branch prediction guesses instead of waiting. Fetch already sees JNZ 12, and 12 is behind it, like a loop, which usually jumps back. So it guesses taken and fetches SUB next. This JNZ runs 3 times: right twice, wrong once.
Structural hazards
A structural hazard is two instructions needing the same hardware at once. TINY-8 keeps program and data in one memory. In cycle 4, ST writes it while fetch wants the next instruction, so fetch waits. Separate instruction and data memories fix it.
The pipeline, cycle by cycle
;; step 1/10 · five-stages ;; about: Fetch, Decode, Execute, Memory, Write back stage does IF read the instruction at PC ID decode it, read its registers EX ALU work; JNZ checks the flag MEM LD reads / ST writes memory WB write the result register cycle 1 2 3 4 LDI r0, 2 IF ID EX MEM LDI r1, 3 IF ID EX LDI r2, 3 IF ID LDI r3, 1 IF
The register file is written in the first half of a clock cycle and read in the second half, so without forwarding an instruction that needs the result of the one just before it waits two cycles (Patterson and Hennessy, section 4.8). In this lesson's pipeline JNZ checks the zero flag in execute, so a taken jump flushes the two instructions fetched behind it. Real pipelines often decide branches earlier and predict them; backward-taken, forward-not-taken is one simple static rule (Hennessy and Patterson, Computer Architecture: A Quantitative Approach, appendix C).