← tonia.ai
Computer Architecture

Beyond the basic pipeline

The hardware and compiler techniques that remove pipeline stalls, the two classic ways to get more than one instruction per cycle's worth of work (deeper pipelines and very long instruction words), and how to judge which strategy actually wins. Three tools let you test each idea.

Part A

Resolving hazards

Forwarding

An ALU result exists at the end of EX, but a simple pipeline waits until WB to write it to the register file, two cycles later. Forwarding (also called bypassing) adds multiplexers in front of the ALU that can take an operand straight from the EX/MEM or MEM/WB pipeline register instead of the register file. With it, back-to-back dependent ALU instructions run with no stalls at all.

A forwarding unit compares register numbers carried in the pipeline registers. For the first ALU operand, in the textbook five-stage RISC-V design:

if (EX/MEM.RegWrite and EX/MEM.rd != 0 and EX/MEM.rd == ID/EX.rs1)
    ForwardA = from EX/MEM
else if (MEM/WB.RegWrite and MEM/WB.rd != 0 and MEM/WB.rd == ID/EX.rs1)
    ForwardA = from MEM/WB
else
    ForwardA = from register file

The same logic, with rs2, drives the second operand. EX/MEM is checked first because it holds the more recent value. The check for rd != 0 keeps writes to the hardwired zero register from being forwarded.

Hazard detection

Forwarding cannot send a value backward in time. A load's data arrives at the end of MEM, but an instruction right behind it needs that value at the start of EX in the same cycle. This load-use hazard needs one stall. A hazard detection unit in ID spots it:

if (ID/EX.MemRead and (ID/EX.rd == IF/ID.rs1 or ID/EX.rd == IF/ID.rs2))
    hold the PC and the IF/ID register
    clear the control signals entering ID/EX

Holding PC and IF/ID makes the two younger instructions repeat their stages; clearing the control signals turns the slot behind the load into a bubble that writes nothing. One cycle later the forwarding unit supplies the loaded value from MEM/WB.

Hazard detection predates RISC. The CDC 6600 (1964) used a scoreboard to track which functional unit would write each register and to hold instructions until their operands were ready. Tomasulo's algorithm for the IBM 360/91 went further, letting later independent instructions proceed past a stalled one, the root of today's out-of-order processors.

Pipeline simulator

Edit the RISC-V code or load an example, then switch forwarding on and off. With forwarding on, the only remaining stalls are load-use hazards caught by the hazard detection unit. Supported: add sub and or xor sll srl sra slt sltu mul div rem, the matching -i forms, and loads and stores.

13 cycles

    Assumes separate instruction and data memories, straight-line code, register writes in the first half of a cycle and reads in the second, and store data forwarded into MEM.

    Compiler scheduling

    The "Unscheduled" example computes a = b + e; c = b + f; in source order and hits two load-use stalls: 13 cycles even with forwarding. In "Scheduled", the compiler moves the third load up so that an independent instruction sits between each load and its use: 11 cycles, no stalls, same result. The hardware still checks for hazards; the compiler simply arranges the code so the checks rarely fire.

    Handling branches

    TechniqueHow it worksCost per branch
    StallStop fetching until the branch resolves in EX2 cycles, always
    Resolve in IDAdd a comparator and target adder to the decode stage1 cycle, but needs forwarding into ID and may lengthen the cycle
    Predict not takenKeep fetching sequentially; flush the wrong instructions if the branch is takenPenalty only on taken branches
    Delayed branchThe ISA defines the instruction after a branch as always executing; the compiler fills that slot0 if the slot is filled usefully; ties the ISA to one pipeline
    Dynamic predictionHardware predicts direction and target from past behaviorPenalty only on mispredictions
    Control CPI = Branch frequency × Penalty rate × Penalty cycles

    With 15% branches, 60% of them taken, and a 2-cycle penalty: stalling costs 0.15 × 2 = 0.30 cycles per instruction; predict not taken costs 0.15 × 0.6 × 2 = 0.18; a predictor that is 92% accurate costs 0.15 × 0.08 × 2 = 0.024.

    Dynamic prediction

    A branch history table, indexed by low bits of the branch address, holds a 2-bit saturating counter for each entry. Two wrong guesses in a row are needed to change the prediction, so a loop branch that is taken many times and falls through once costs one misprediction per loop rather than two. A branch target buffer supplies the target address during fetch, so a predicted-taken branch costs nothing when the prediction is right.

    Strong NT00 · predict N Weak NT01 · predict N Weak T10 · predict T Strong T11 · predict T TTT NNN

    Later designs use history. Two-level predictors (Yeh and Patt) index counters by the recent outcomes of a branch or of all branches, capturing patterns and correlations a single counter cannot. Current high-end cores use descendants of TAGE (Seznec) and perceptron predictors (Jiménez and Lin), with accuracies well above 95% on typical code.

    Predictor simulator

    Type a sequence of branch outcomes (T for taken, N for not taken) or pick a pattern. Filled cells were correct predictions; outlined cells were mispredictions.

    The two-level predictor keeps this branch's last 4 outcomes and uses them to pick one of 16 two-bit counters. Counters start in Weak NT.

    Part B

    Superpipelining and VLIW

    Superpipelining

    A superpipelined processor splits the slowest stages, usually instruction fetch and data memory access, into several shorter ones so the clock can run faster. The MIPS R4000 (1991) is the classic example: its eight stages split both cache accesses in two.

    StageIFISRFEXDFDSTCWB
    WorkFetch, first halfFetch, second halfDecode, read registers, tag checkExecuteData fetch, first halfData fetch, second halfTag checkWrite back

    The price is longer hazard penalties measured in cycles. On the R4000 a load's value arrives two cycles later than in the five-stage pipeline, so a load-use hazard costs 2 stalls instead of 1, and a branch costs 3 cycles instead of 2. Intel's Pentium 4 pushed further, to 20 stages and later 31, before its successors cut the pipeline back as mispredictions and clock power grew.

    Studies of optimal depth (Kunkel and Smith; Hartstein and Puzak; Sprangle and Carmean; Hrishikesh and colleagues) agree on the shape of the trade-off. Performance rises as stages are added, peaks, then falls as register overhead and hazard penalties dominate. Where the peak lies depends on the workload, the predictor and the circuit overhead.

    VLIW

    A very long instruction word processor issues one wide instruction per cycle, often called a bundle. Each bundle holds several independent operations, one slot per functional unit. The compiler, not the hardware, decides which operations can run together and fills unused slots with no-ops. With no dependence checking between slots and no dynamic scheduling, the issue logic stays simple even at high width.

    VLIW came out of Fisher's trace-scheduling research and machines such as the Multiflow TRACE in the 1980s. Intel and HP's Itanium (EPIC) added predication, speculation support and bundle templates. Itanium was discontinued, but VLIW thrives where code is regular and power is tight: digital signal processors such as TI's C6000 family and Qualcomm's Hexagon, and many AI accelerators.

    A VLIW schedule

    The loop for (i = 0; i < n; i++) x[i] = x[i] + s; on doubles, unrolled four times, on a VLIW with two memory slots, two floating-point slots and one integer/branch slot. The latencies are 2 cycles from a load to its use and 3 cycles from an FP add to its use:

    CycleMemory 1Memory 2FP 1FP 2Integer / branch
    1fld f0, 0(x1)fld f2, 8(x1)nopnopnop
    2fld f4, 16(x1)fld f6, 24(x1)nopnopaddi x1, x1, 32
    3nopnopfadd.d f0, f0, f8fadd.d f2, f2, f8nop
    4nopnopfadd.d f4, f4, f8fadd.d f6, f6, f8nop
    5nopnopnopnopnop
    6fsd f0, -32(x1)fsd f2, -24(x1)nopnopnop
    7fsd f4, -16(x1)fsd f6, -8(x1)nopnopbne x1, x2, loop
    VersionCycles per elementNotes
    Scalar pipeline, one element per iteration85 instructions plus 3 stall cycles
    Scalar pipeline, unrolled ×4 and scheduled3.514 instructions, no stalls, loop overhead shared
    VLIW, unrolled ×41.757 bundles; 14 of 35 slots used (40%)

    Operations in the same bundle read their registers before any of them write, so the loads in cycle 2 see the old x1. Unrolling further fills more slots and approaches one element per cycle, at the cost of code size and registers.

    Compilers for VLIW

    TechniqueWhat it doesCost
    Loop unrollingCopies the loop body so there are more independent operations to packCode size, registers
    Software pipeliningOverlaps iterations: each bundle holds parts of several iterations at different stages (Lam)Prologue and epilogue code; register pressure
    Trace schedulingSchedules the most likely path through several basic blocks as one unit, with fix-up code off the path (Fisher)Compensation code; depends on profile accuracy
    PredicationTurns short branches into operations guarded by a predicate register, removing control hazards (the hyperblock of Mahlke and colleagues)Executes both paths' operations

    VLIW's weaknesses are the flip side of its strength. Schedules are fixed at compile time, so an unexpected cache miss stalls the whole bundle. Binaries encode the slot layout, so a wider implementation needs recompiled code. Empty slots waste instruction memory unless the encoding compresses no-ops, which most modern VLIWs do.

    Part C

    Evaluating strategies

    The CPI stack

    Time per instruction = (1 + Structural + Data + Control stalls) × Cycle time

    Every technique on this page attacks one term of this product, and many make another term worse. Forwarding shrinks data stalls but adds multiplexers to the critical path. Resolving branches early cuts the control penalty but may lengthen the cycle. Superpipelining shortens the cycle but multiplies stall penalties. The only fair comparison is the product, on a realistic workload.

    Strategy evaluator

    Set a workload and the hazard-handling options. The evaluator compares a 5-stage pipeline, an 8-stage superpipeline like the R4000's, and a 10-stage design that also splits the ALU.

    Base CPIData stallsControl stalls

    Model assumptions

    All designs share 5,000 ps of logic, divided evenly among stages, plus the register overhead per stage. With forwarding, dependent ALU instructions stall 0, 0 and 1 cycles in the 5-, 8- and 10-stage designs; load-use stalls are 1, 2 and 3 cycles; branch penalties are 2, 3 and 5 cycles. Without forwarding, an instruction that uses the previous result stalls 2, 4 or 6 cycles (one fewer for a result two back). Structural hazards are assumed to be designed out. Real processors differ in many details; the model shows the shape of the trade-off.

    Things to try. With the defaults, the 8-stage design is fastest: its shorter cycle more than pays for its longer penalties, but at 10 stages the penalties win. Turn forwarding off or switch to "Stall" and the 5-stage design wins instead, because deeper pipelines magnify every unhandled hazard. Raise the register overhead and the advantage of depth shrinks.

    Comparing approaches

    ApproachGains fromWho finds parallelismMain costsWhere it is used
    Scalar pipeline with forwarding and predictionFewer stallsHardware, adjacent instructionsForwarding paths, predictor tablesMicrocontrollers, small embedded cores
    SuperpipeliningShorter cycleHardwareLonger penalties, more registers and clock powerNearly every high-performance core, to a moderate depth
    Superscalar (in or out of order)Several instructions per cycleHardware, at run timeIssue logic grows quickly with widthPhone, laptop and server CPUs
    VLIWSeveral operations per cycleCompiler, ahead of timeCode size, compatibility, sensitivity to unpredictable latencyDSPs, accelerators

    Jouppi and Wall compared superpipelined and superscalar machines on the same code in 1989 and found that, for the same degree of parallelism, the two delivered roughly the same performance, because both were limited by the parallelism in the programs. Modern processors combine all of these techniques: a moderately deep pipeline, forwarding everywhere, accurate prediction, and several instructions issued per cycle.

    Speed is only one axis. Power, area, design and verification effort, code size, and the ability to run existing binaries all weigh in, and they usually decide which strategy a product uses.

    Go further

    Resources

    Interactive tools and references

    Peer-reviewed reading

    • Thornton, J. E. (1964). Parallel operation in the Control Data 6600. Proc. AFIPS Fall Joint Computer Conference, 33–40.The scoreboard: hardware hazard detection across multiple units.doi.org/10.1145/1464039.1464045
    • Tomasulo, R. M. (1967). An efficient algorithm for exploiting multiple arithmetic units. IBM Journal of Research and Development, 11(1), 25–33.Dynamic scheduling that lets independent instructions pass stalled ones.doi.org/10.1147/rd.111.0025
    • Yeh, T.-Y., & Patt, Y. N. (1991). Two-level adaptive training branch prediction. Proc. MICRO-24, 51–61.History-based prediction, the two-level row in the simulator.doi.org/10.1145/123465.123475
    • Seznec, A. (2005). Analysis of the O-GEometric history length branch predictor. Proc. ISCA, 394–405.Geometric history lengths, the idea behind TAGE.doi.org/10.1109/ISCA.2005.13
    • Jouppi, N. P., & Wall, D. W. (1989). Available instruction-level parallelism for superscalar and superpipelined machines. Proc. ASPLOS III, 272–282.Superpipelining and superscalar issue compared on the same code.doi.org/10.1145/70082.68207
    • Mirapuri, S., Woodacre, M., & Vasseghi, N. (1992). The MIPS R4000 processor. IEEE Micro, 12(2), 10–22.The classic superpipelined design.doi.org/10.1109/40.127580
    • Hartstein, A., & Puzak, T. R. (2002). The optimum pipeline depth for a microprocessor. Proc. ISCA, 7–13.An analytical model of the depth trade-off.doi.org/10.1109/ISCA.2002.1003557
    • Sprangle, E., & Carmean, D. (2002). Increasing processor performance by implementing deeper pipelines. Proc. ISCA, 25–34.Intel's case for deep pipelines, from the Pentium 4 era.doi.org/10.1109/ISCA.2002.1003559
    • Hrishikesh, M. S., et al. (2002). The optimal logic depth per pipeline stage is 6 to 8 FO4 inverter delays. Proc. ISCA, 14–24.Where register overhead stops deeper pipelines from paying off.doi.org/10.1109/ISCA.2002.1003558
    • Fisher, J. A. (1981). Trace scheduling: A technique for global microcode compaction. IEEE Transactions on Computers, C-30(7), 478–490.The compiler technique that made VLIW practical.doi.org/10.1109/TC.1981.1675827
    • Fisher, J. A. (1983). Very long instruction word architectures and the ELI-512. Proc. ISCA, 140–150.The founding VLIW paper.doi.org/10.1145/800046.801649
    • Colwell, R. P., et al. (1988). A VLIW architecture for a trace scheduling compiler. IEEE Transactions on Computers, 37(8), 967–979.The Multiflow TRACE machines.doi.org/10.1109/12.2247
    • Lam, M. (1988). Software pipelining: An effective scheduling technique for VLIW machines. Proc. ACM PLDI, 318–328.Overlapping loop iterations in the compiler.doi.org/10.1145/53990.54022
    • Mahlke, S. A., et al. (1992). Effective compiler support for predicated execution using the hyperblock. Proc. MICRO-25, 45–54.Removing branches with predication.doi.org/10.1109/MICRO.1992.696999
    • Rau, B. R., & Fisher, J. A. (1993). Instruction-level parallel processing: History, overview, and perspective. The Journal of Supercomputing, 7(1–2), 9–50.A survey by two of VLIW's originators.doi.org/10.1007/BF01205181
    • Schlansker, M. S., & Rau, B. R. (2000). EPIC: Explicitly parallel instruction computing. IEEE Computer, 33(2), 37–45.The philosophy behind Itanium.doi.org/10.1109/2.820037
    • Codrescu, L., et al. (2014). Hexagon DSP: An architecture optimized for mobile multimedia and communications. IEEE Micro, 34(2), 34–43.A VLIW in billions of phones.doi.org/10.1109/MM.2014.12