A computer’s main chip is called the . It does one simple thing, very fast. It reads a command, does it, and moves on to the next one.
Each command is tiny. “Add these two numbers.” “Fetch a number from memory.” “Jump back to step 3.” A program is a long list of these commands, often billions of them.
Every kind of chip has its own list of commands it understands. This list is called the . It works like a deal. Programs promise to use only these commands. Chips promise to do every one of them exactly right.1
Here is the surprise. As long as a chip keeps the deal, it can be built any way at all inside. A cheap chip in a toy and a giant chip in a laptop can run the same program.
A runs a program by carrying out its instructions one after another: arithmetic on numbers held in a few fast storage slots called registers, loads and stores that move data between registers and memory, and branches that choose which instruction comes next.
Which instructions exist, and exactly what each one does, is set by the (ISA). The ISA is the contract between software and hardware.1 A compiler turns a program into instructions of one ISA, and any chip that implements that ISA runs the result. How a particular chip carries the instructions out, its , is hidden behind the contract and can change from one design to the next. Three ISAs dominate today: x86 in most laptops and servers, Arm in most phones and a growing share of everything else, and , an open ISA anyone can implement. This chapter uses RISC-V for its examples because its specification is free to read.2
The chapter covers the ideas every CPU builds on:
- The instruction set: what an instruction is, how it is encoded, and the RISC and CISC styles.
- Fetch, decode, execute: the loop driven by the .
- Pipelining: the classic five-stage pipeline, which works on five instructions at once.
- Hazards: what stops a pipeline, and the fixes: forwarding, stalls and handling branches.
- Measuring it: cycles per instruction and the iron law of performance.
The next three chapters build on this one: out-of-order execution, caches and multicore.
You know a program is a list of instructions. This chapter is about the interface those instructions define and the simplest machine that runs them well. Amdahl, Blaauw and Brooks defined the of the IBM System/360 in 1964 as the structure a machine-language programmer must understand to write a correct, timing-independent program; the 360 was the first family to separate that from its implementations.1 Everything below the line, the , is free to change as long as results match.
Three threads run through the chapter:
- ISA design shapes the pipeline. Fixed-length, load-store encodings with registers in fixed fields make decode cheap and pipelining natural; variable-length CISC encodings push work into a decoder that cracks instructions into simpler internal operations.5
- Pipelining trades latency for throughput. Five stages cut the cycle time roughly fivefold at best, while dependences and branches add stall cycles. The pipeline’s quality is its .11
- Performance is a product. The , time = instructions × CPI × cycle time, is the frame for every trade-off here and in the rest of the CPU chapters.
We use as the worked ISA because the ratified specification is open2, and set it beside x86-64 and Arm A64 where they differ. Depth versus clock frequency is treated in the Design Flow guide’s Architecture stage; here the pipeline has five stages.
A small core runs the instructions one after another through a short pipeline. Tap a layer.
A chip doesn’t understand words. Each command is stored as a pattern of 1s and 0s, called bits. On a RISC-V chip, most commands are 32 bits long. Some bits say what to do, like “add”. Others say which numbers to use.3
The chip keeps a few numbers in very fast slots right next to its math part. These slots are called registers. A RISC-V chip has 32 of them. One always holds zero, which turns out to be handy.3
Big chip families made different choices. Some use only small, simple commands. This style is called . Arm chips in most phones work this way, and so does RISC-V.8
Others also have big commands that do several jobs at once, like “fetch a number, add 13, and put it back”. That style is called CISC. The x86 chips in most laptops work this way.7 Inside, a modern x86 chip breaks those big commands into small steps anyway.15
Which style is better? People argued about it for forty years. Today, careful tests find it hardly matters for speed or battery life. How the chip is built inside matters much more.15
An instruction is a fixed pattern of bits. In the RISC-V base set, every instruction is exactly 32 bits (4 bytes), and there are four core formats (R, I, S and U) plus two variants (B and J) that differ only in how they pack a constant.3 Fields have fixed places: the 7-bit opcode at the bottom says which kind of instruction it is; 5-bit fields name up to two source registers and one destination; the remaining bits hold an extra opcode field (funct3 or funct7) or an immediate, a constant stored inside the instruction. Tap an instruction in the figure below to see its fields.
The state a program can see is small: 32 integer registers of 32 or 64 bits, with x0 wired to zero, plus the , which holds the address of the current instruction.3 RISC-V is a load-store architecture: only load and store instructions touch memory, and arithmetic works on registers alone.3 Its base set is deliberately small, 47 instructions in Waterman’s count, with optional standard extensions for multiplication (M), atomics (A), floating point (F, D) and compressed 16-bit instructions (C).52
RISC and CISC
In 1980 David Patterson coined the term for a design built from a smaller set of simple, general instructions. Berkeley’s RISC-I of 1982 outperformed a conventional design that used more than twice as many transistors.6 The contrast is with complex instruction set computers (CISC), whose instructions vary in length and can combine memory access with arithmetic.
- x86 is CISC. It stays backward compatible with Intel’s 8086 of 1978, and its manuals run to about 5,000 pages.7 An instruction can be anywhere from 1 to 15 bytes long. The 64-bit version, x86-64, was AMD’s extension and doubled the integer registers to 16.5
- Arm’s 64-bit instruction set, A64, is RISC: every instruction is 32 bits, there are 31 general-purpose registers plus a zero register, and only loads and stores access memory.98
- RISC-V is RISC too, with an optional compressed extension that encodes common instructions in 16 bits and cuts code size by about a quarter.5
The figure shows one small job, adding 13 to a number in memory, in all three. RISC-V and Arm need three instructions (load, add, store); x86 does it in one. That doesn’t make x86 three times faster: inside, modern x86 processors split complex instructions into simpler RISC-like operations, called micro-ops, before executing them.155
RV32I has 32 registers of XLEN bits with x0 hardwired to zero, a , and six formats, all 32 bits: R (register–register), I (12-bit immediate, loads, JALR), S (stores), B (branches), U (20-bit upper immediate) and J (JAL).3 The design keeps rs1, rs2 and rd in the same bit positions in every format, so the register file can be read before decode finishes, and the S format splits its immediate (imm[11:5] high, imm[4:0] where rd would be) precisely to keep that true; the sign bit is always bit 31.5 Waterman counts 47 base instructions, 40 of them mandatory user-level hardware.5 The base is frozen, and everything else comes as extensions (M, A, F, D, C, and later V and others).2
Encoding and decode cost
- x86-64: variable length up to 15 bytes, with optional prefixes, an opcode of one to three bytes, ModRM and SIB bytes for addressing, a displacement and an immediate. Most integer instructions are destructive two-operand forms and can take a memory operand; Waterman counts about 1,300 instructions as of 2015.5 Since AMD’s K5 and Intel’s Pentium Pro, x86 cores have broken complex instructions into sequences of simpler internal operations.5
- Arm A64: fixed 32-bit encodings with 5-bit register fields, 31 general registers (X0–X30) and a zero register that shares encoding 31 with the stack pointer.985 ARMv8 dropped the 16-bit Thumb encodings of the 32-bit architecture, so it is less dense than variable-length code.5
- RISC-V C extension: 16-bit forms that each expand to one 32-bit instruction; many reach only x8–x15 through 3-bit register fields. IALIGN drops to 16 bits.2 Waterman reports RVC code about 25–30% smaller, enough to make RV64C denser than x86-64 and A64 on SPEC CPU2006.5
In terms, CISC aims to lower the instruction count and RISC to lower CPI and cycle time. Blem et al. measured Cortex-A8 and A9 against Atom and Sandy Bridge and found instruction counts similar across the ISAs (compilers mostly pick x86’s RISC-like instructions), micro-op to instruction ratios usually below 1.3, and no intrinsic performance or energy advantage for either style: “the ISA being RISC or CISC seems irrelevant.”15 The costs that remain are in the front end: finding instruction boundaries in a variable-length stream, and decoders or micro-op caches to feed a wide core.
3 instructions, 12 bytes. Tap an instruction.
How does the chip know which command is next? It keeps a bookmark called the . It holds the place in memory where the next command is stored.3
Then it repeats the same loop, billions of times a second:
- Fetch: read the command the bookmark points to.
- Decode: work out what it means, and get the numbers it needs from the registers.
- Execute: do the math.
- Memory: if the command says so, read from or write to memory.
- Write back: put the answer in a register.
Then the bookmark moves on to the next command. A “jump” command moves it somewhere else instead. That is how a program repeats things or makes choices.
The simplest chip does all five steps in one tick of its . That works, but each tick must be long enough for the slowest command to finish.11
Every CPU runs the same loop. The (PC) holds the address of the current instruction.3 The processor fetches the instruction at that address, decodes it, reads its source registers from the , executes it in the arithmetic logic unit (ALU), accesses data memory if it is a load or store, and writes any result back to a register. Then the PC advances by 4 (the length of a base instruction), or, for a taken branch or a jump, is loaded with a new address.11
Instructions use different parts of that path. An add skips data memory; a store writes memory but no register; a branch only compares and maybe updates the PC.11 Step through them in the figure.
The single-cycle design
The simplest implementation does all of this in one clock cycle. The datapath is a chain of logic from the PC through instruction memory, register file, ALU and data memory back to the register file, with only in the PC and the registers. That makes , the average number of clock cycles per instruction, exactly 1. But the clock period must fit the longest instruction, a load, which uses every unit in turn, so every add pays for the load’s path.11
The opposite extreme is a multi-cycle design that spends one short cycle per step and only as many steps as each instruction needs. Its cycle is short, but its CPI is well above 1. In Cornell’s worked example, a multi-cycle processor has CPI 6.7 with a 36-unit cycle against the single-cycle design’s CPI 1 with a 74-unit cycle, and ends up slower overall.11 Pipelining gets a short cycle and a CPI near 1 at the same time.
The architectural contract is sequential: each instruction appears to complete before the next begins, and the PC names the next one (PC + 4, or a target for taken branches and jumps; PC + 2 steps exist once the C extension is present).32 Every microarchitecture in this guide preserves that illusion.
Cornell’s ECE 4750 notes derive three implementations of the same RISC-V subset and compare them with the iron law: single-cycle (CPI 1, long cycle), FSM or multi-cycle (CPI greater than 1, short cycle), and pipelined (CPI near 1, short cycle).11 With their component delays (in units of τ), the single-cycle machine runs at 74τ and the FSM machine at 36τ with CPI 6.7 on a vector-add loop, so the FSM loses despite a clock twice as fast.11 The single-cycle critical path is the load: PC → instruction memory → register read → ALU (address) → data memory → write-back mux → register-file setup.
Two details carry into the pipeline. The register file is written on one edge and read combinationally, so a read in the same cycle as a write can see the new value if the write happens in the first half of the cycle. And a taken branch needs the comparison result before the next PC can be chosen, which in a single-cycle machine just lengthens the cycle, and in a pipeline becomes a hazard.
Fetch. The program counter holds 0x100. Instruction memory returns the 32-bit instruction at that address: lw x1, 0(x10).
Doing a whole command in one long tick wastes time. While the chip does the math, the parts that fetch and decode just sit there.
So fast chips use an assembly line, called . They cut the work into five steps. Each step has its own part of the chip. On every tick, each command moves one step along, and a new command enters at the start.11
Each command still needs five ticks from start to finish. But the ticks are about five times shorter, and a new command finishes on every one. So the chip gets through programs up to five times faster.
There’s a catch. The line can only tick as fast as its slowest step. If one step is twice as slow as the others, everyone waits for it.11
cuts the single-cycle datapath into stages separated by pipeline registers, banks of flip-flops that hold each instruction’s values between stages. The classic version has five:1112
| Stage | What it does |
|---|---|
| IF (F): instruction fetch | Read the instruction at the PC; compute PC + 4 |
| ID (D): decode | Decode the fields; read the source registers |
| EX (X): execute | ALU operation, address calculation or branch comparison |
| MEM (M): memory | Load from or store to data memory |
| WB (W): write back | Write the result to the register file |
Each clock cycle, every instruction moves one stage on and a new one is fetched, so up to five are in flight. The time to finish one instruction, its latency, doesn’t improve; it actually grows a little, because each stage adds register delay. What improves is throughput: one instruction completes per cycle, and the cycle is short.11
Three things keep the speedup below five:
- Unbalanced stages. The clock must fit the slowest stage. With the delays in the figure, the clock drops from 800 ps to 200 ps, a 4× gain, not 5×.11
- Filling and draining. The first instruction takes five cycles to come out, and stages sit idle while the pipeline fills: a . Over billions of instructions it hardly matters.11
- Hazards. An instruction sometimes can’t move on because it needs something that isn’t ready. That is the next section.
The five stages are F, D, X, M and W, with pipeline registers F/D, D/X, X/M and M/W carrying each instruction’s operands, destination, control signals and PC.11 Control is decoded once in D and travels with the instruction, which keeps every later stage’s control a function of its own pipeline register. A valid bit per stage marks bubbles.
With stage delays and per-stage register overhead , the cycle is , and instructions with no hazards take cycles on a -stage pipeline. The asymptotic speedup over single-cycle is therefore
reached only with perfectly balanced stages and no overhead. For the figure’s illustrative delays (200, 100, 200, 200 and 100 ps, overhead ignored) . Cornell lists the same lessons: pipelining helps throughput, not latency; speedup is limited by the slowest stage and reduced by fill time.11
The memory stages assume a one-cycle access, which in practice means separate instruction and data that hit; misses are the subject of Caches and coherence. The optimum depth, and the studies that put it at a handful of gate delays per stage, are in the Architecture stage; where the registers go in RTL, and why retiming can’t add them for you, in RTL design.
Single-cycle: the clock period must fit the slowest instruction’s whole path, 800 ps. Four instructions take 3,200 ps.
An assembly line works best when every step is ready on time. Sometimes one isn’t. That’s called a , and there are three kinds.11
- Two steps want the same part. If the chip had only one memory, fetching a command and loading a number would clash. Chips avoid this by giving each job its own part.
- An answer isn’t ready yet. Say one command works out a, and the very next one needs a. The first hasn’t finished, so the second must wait.
- The chip doesn’t know which way to go. After a “jump if” command, the next command depends on the answer. But the line has already started loading the next ones.
When a command has to wait, the chip holds it in place. The empty slot it leaves moves down the line like a bubble, and no command finishes on that tick.
A is anything that stops the next instruction from moving on in the next cycle. There are three kinds.11
- Structural hazards: two instructions need the same hardware in the same cycle. With a single memory, a load in M and a fetch in F would collide every time. The five-stage design avoids them by construction: separate instruction and data memories, and a register file with two read ports and one write port.11
- Data hazards: an instruction needs a value that an earlier, still-in-flight instruction has computed but not yet written back.12 In the figure, sub reads x1 in D two cycles before add writes it in W. The register file writes in the first half of a cycle and reads in the second, so an instruction three behind is safe; one or two behind is not.
- Control hazards: instructions that change the PC.12 A branch’s direction is known only once it has been compared in X. By then the two instructions after it have already been fetched.
The simplest response to any hazard is to stall: hold the waiting instruction and everything behind it in place, and let the instructions ahead move on. The empty slot that travels down the pipeline is a bubble, and each one adds a cycle. The next section covers the cheaper fixes.
Cornell’s notes list five ways to resolve a hazard: expose it in the ISA (software must schedule around it), stall in hardware, bypass, speculate, or schedule dynamically.11 The five-stage pipeline uses several at once.
- Structural: resolved by duplication (split I- and D-memories, enough register ports) or by stalling. Cornell’s example creates one on purpose by letting ALU instructions skip M, so two instructions reach the single write port in the same cycle, and then fixes it with a second write port.11
- Data (RAW): only read-after-write hazards occur in this in-order pipeline, since every instruction reads in D and writes in W, in program order. Write-after-read and write-after-write hazards appear once instructions can complete out of order, the case that handles with renaming.11
- Control: Cornell defines a resolution latency, the number of cycles the next fetch must wait to avoid any control hazard: two cycles for jumps, whose target is known in D, and three for branches, decided in X.11 Exceptions are a control hazard too: flags travel with the instruction to a commit point, so that the oldest faulting instruction is the one reported and younger ones are squashed.11
Data hazard: sub and and read x1 in D before add writes it in W. The or is safe, because the register file writes in the first half of a cycle and reads in the second.
Waiting costs time, so chips use a trick called . As soon as the math part has an answer, a shortcut wire sends it straight back to the math part for the next command. It doesn’t wait for the answer to be written down first.12
Numbers loaded from memory are harder. They show up one step later than a sum does. So a command that uses a loaded number right away still waits one tick. This is called a wait.12
Here’s a neat fix. The translator program that prepares the commands can move another command into the gap. The chip then does useful work instead of waiting. You can try this in the simulation below.13
For “jump if” commands, the chip keeps loading the next commands in a row and hopes there is no jump. If there is a jump, it throws two commands away. Fast chips make smart guesses instead, which is the subject of the next chapter.
, also called bypassing, removes most data stalls. The result of an add exists at the end of its X stage, long before W. Extra wires carry it from the X/M pipeline register back to a in front of the ALU, and comparators check whether the instruction now in X needs it.1211 A second path from the M/W register covers results two instructions back. With both, an ALU instruction can use the result of the one right before it with no stall at all.
The load-use stall
Loads are the exception. A load’s data arrive only at the end of M, one cycle too late for an instruction that follows directly. Forwarding can’t send a value back in time, so the pipeline must stall for one cycle: a .12 In Cornell’s terms, ALU-use latency is one cycle but load-use latency is two.11
Compilers hide it by instruction scheduling: they move an independent instruction between the load and its use. GCC’s scheduler exists for exactly this, to “reorder instructions to eliminate execution stalls due to required data being unavailable.”13 In the simulation below you can do the compiler’s job by hand.
The branch penalty
The simplest way through a control hazard is to assume each branch is not taken and keep fetching the next instructions in order. If the branch turns out taken, the two instructions fetched behind it are flushed (turned into bubbles) and fetch restarts at the target: a penalty of two cycles per taken branch when branches resolve in X.1112 Not-taken branches cost nothing. Some designs resolve branches in D instead, which halves the penalty but needs the comparison operands a cycle earlier.
Loops branch backward and are usually taken, so fixed rules help: the RISC-V specification tells software to assume backward branches are predicted taken and forward ones not taken, at least the first time.3 Real CPUs go much further with that learns each branch’s behavior, which Branch prediction and out-of-order execution covers.
The forwarding unit compares the source registers of the instruction in X with the destinations in X/M and M/W, giving priority to X/M (the newer value) and ignoring x0; the hazard unit in D stalls when the instruction in X is a load whose destination matches either source of the instruction in D. A stall holds the PC and F/D and injects a bubble into D/X.11 Under the hood shows the logic.
Distances at which a consumer of a value stalls, with full bypassing:
| Producer → consumer | Distance 1 | Distance 2 | No forwarding (dist. 1 / 2) |
|---|---|---|---|
| ALU → ALU, address or store data | 0 | 0 | 2 / 1 |
| Load → ALU | 1 | 0 | 2 / 1 |
| ALU → branch resolved in D | 1 | 0 | 2 / 1 |
| Load → branch resolved in D | 2 | 1 | 2 / 1 |
These follow from the stage timings, with write-first-half, read-second-half register-file timing; they are what the simulation implements. A load feeding a store’s data can also be forwarded M/W → M with no stall in designs that add that path; the simulation treats store data as an X-stage operand.
For control hazards, predict-not-taken with resolution in X costs two cycles per taken branch, and resolution in D one, at the price of a comparator and target adder in D and the extra branch-operand stalls in the table.11 Small real cores show the same arithmetic: lowRISC’s two-stage Ibex documents taken branches at two cycles, or one with an extra branch-target adder, and loads that stall at least one cycle for their data.16 Dynamic prediction (a plus history-based direction predictors) moves the redirect into F; the five-stage Rocket core has a configurable BTB, branch history table and return-address stack.17 The mechanisms belong to the next chapter.
Without forwarding, the consumer waits in decode until the producer’s result is written to the register file: 2 stall cycles.
This is the assembly line, tick by tick. Each row is one step of a program. Each column is one tick of the clock. The letters show where each step is: F fetch, D decode, X do the math, M memory, W write back.
Start with “Load, then use” and look for the waiting box. Then use the arrows to move another step into the gap until the waiting is gone. Try the loop too, and turn the shortcut off to see how much it helps.
A cycle-by-cycle diagram of the five-stage pipeline. Pick a program, toggle forwarding, and reorder the instructions. The strip on top marks bubbles in X; the readouts give CPI, ignoring the four cycles it takes to fill the pipeline. Things to try:
- Dependent chain: what is the CPI with forwarding off, and with it on? Where do the arrows go?
- Load then use: with forwarding on, reorder until CPI is 1.00. Which orders work, and which change the program?
- Loop with a branch: which cycles are lost to the load and which to the taken branch? Can you remove the load stall?
The model is the textbook in-order pipeline: one instruction per stage, write-first-half register file, bypasses X/M → X and M/W → X, predict-not-taken with a taken branch flushing the instructions fetched behind it. The Expert view adds a toggle to resolve branches in D (one-cycle penalty, operands needed a cycle earlier) and shows IPC. Try:
- In the loop, resolve branches in D. The flush cost halves, but a new stall appears in front of the branch. Reorder to remove it.
- With forwarding off, compare the dependent chain (CPI 2.60) with the load-use program. Why does reordering help less without bypassing?
Reordering is checked against read-after-write, write-after-read and write-after-write dependences in the original order; an order that breaks one is flagged, since a compiler may not use it.
How do you know if a chip is fast? Counting ticks per second isn’t enough. Three numbers matter, and you multiply them.1
- How many commands the program needs.
- How many ticks each command takes, on average.
- How long one tick lasts.
This rule is called the .14 It explains why tricks often help one number and hurt another. Big CISC commands mean fewer commands, but each may take more ticks. A longer assembly line makes ticks shorter, but waits cost more ticks.
A perfect five-step line finishes one command per tick. Waiting and wrong guesses push that up. Fast modern chips beat it by working on several commands side by side.15
The splits run time into three factors:1
- Instructions per program depend on the source code, the compiler and the ISA.
- Cycles per instruction () depend on the ISA and the microarchitecture.
- Time per cycle (the clock period, the inverse of frequency) depends on the microarchitecture and the circuits.1
The name is credited to Douglas Clark, from his work with Joel Emer measuring the VAX-11/780 in the 1980s.14 Its lesson is that no single number decides speed. A higher clock frequency means nothing if CPI rises as much, and fewer instructions mean nothing if each takes longer.
For a pipeline, CPI = 1 + stall cycles per instruction. Its inverse, (instructions per cycle), is the more common figure for modern chips, which issue several instructions per cycle and so reach IPC above 1, or CPI below 1. In Blem et al.’s measurements, a four-wide out-of-order x86 core averaged CPI 0.7, while two-wide in-order Arm and x86 cores were near 2–3.4 once memory stalls were included.15
With dynamic instructions, average CPI and clock period :
where is the ideal (1 for a scalar pipeline), the frequency of stall event per instruction and its penalty in cycles. The factors are not independent: the ISA moves and CPI in opposite directions; pipeline depth moves down and every up; a bypass network removes stall terms but can lengthen . Cornell frames the single-cycle, multi-cycle and pipelined designs exactly this way.11 The name comes from Clark, after his and Emer’s VAX-11/780 characterization.14
Two cautions when using it. Compare designs on for the same program and compiler, never on MIPS or GHz, since differs across ISAs (though Blem et al. found dynamic instruction counts close between Arm and x86 for compiled code).15 And CPI measured on a real core is dominated by events this chapter leaves out: and branch mispredictions in the CPI stacks of every modern core.
1.20 × 10⁹ instructions × CPI 1.00 ÷ 3.0 GHz = 0.40 s (about the same as the first design).
- Stages in the classic RISC pipeline
- 5
- Load-use stall with forwarding
- 1 cycle
- RISC-V instruction: base / compressed
- 4 / 2 bytes
- Longest x86 instruction
- 15 bytes
What these numbers mean:
- 5 is the number of steps on the classic assembly line: fetch, decode, math, memory, write back.11
- 1 tick is how long a command waits when it needs a number that is still coming from memory, even with the shortcut.12
- 4 or 2 bytes: most RISC-V commands take 4 bytes. A short form packs common ones into 2.2
- 15 bytes: an x86 command can be anywhere from 1 to 15 bytes long. That makes it harder to tell where one command ends and the next begins.5
Two readings of the tables. First, the same ISA spans very different machines: two to six stages among open RISC-V cores alone, and both in-order and out-of-order cores for Arm and x86. Second, CPI tracks the microarchitecture, not the ISA: the two in-order cores land near each other whichever ISA they run, and the wide out-of-order core does several times better than either. Blem et al. conclude that ARM and x86 processors are “simply engineering design points optimized for different levels of performance.”15
Blem et al.’s CPIs are measured at each part’s own frequency (0.6 GHz A8 to 3.4 GHz i7) with different cache hierarchies, so they fold memory stalls into the comparison; the authors attribute the gaps to issue width, out-of-order capability and caches rather than to the ISA, and report x86 micro-op to instruction ratios usually below 1.3.15 Intel’s APX data point illustrates the instruction-count side of the iron law from within one ISA: doubling the architectural registers to 32 removes about 10% of loads and more than 20% of stores in compiled code.10
Every fix has a price. The shortcut wires add a little switch right in front of the math part. That spot sets how fast the clock can tick, so the shortcut can slow every tick a bit.
Moving steps around is free for the chip. But the translator needs other useful steps to put in the gap, and there aren’t always any.13
Simple commands make the line easy to build. But a program needs more of them, and that is more to fetch and store.5
Long ago, some chips made programs deal with the waits themselves. Those chips’ rules were tied to one design, and later chips were stuck with them.5
| You get | You give up |
|---|---|
| Pipelining: a cycle several times shorter | Register overhead per stage, hazards, and more hardware and verification |
| Forwarding: no stalls between ALU instructions | Multiplexers and comparators on the execute stage’s critical path |
| Compiler scheduling: hidden load-use stalls, no hardware | Needs independent instructions and spare registers; tuned to one pipeline |
| Resolving branches early: smaller taken-branch penalty | A longer decode stage and new stalls when a branch needs a fresh result |
| Simple fixed-length instructions: easy decode | More instructions and larger code than dense variable-length encodings |
| Hazards exposed in the ISA: simpler hardware | Every future design must honor one pipeline’s timing |
Ways designs go wrong
- Building the pipeline into the ISA. Early MIPS exposed a load delay slot and a branch delay slot, rules that suited a five-stage pipeline exactly. Later revisions dropped the load slot because hardware interlocks were simpler and faster; the branch slot could never be removed without breaking old programs.5 RISC-V has no delay slots.3
- Optimizing one factor of the iron law. A faster clock bought with a deeper pipeline can lose more in CPI than it gains; fewer, more complex instructions can raise CPI just as much.11
- Forgetting x0 and priorities in the bypass logic. Forwarding a value “written” to x0, or the older of two matching results, gives silently wrong answers.11
- Bypass cost scales badly. Each extra stage that holds a result adds a mux input per operand; a core, which issues several instructions per cycle, multiplies sources and destinations. The bypass mux sits on the X-stage loop (ALU output → mux → ALU input), which is often the that sets the clock.
- Static scheduling is brittle. Compiler schedules assume a pipeline; code tuned for one core can stall on another, and a cache miss turns a one-cycle load-use slot into hundreds of cycles that no static schedule covers. GCC’s first scheduling pass is conventionally on at -O2, but targets override it: off at every level on x86, only at -O3 on AArch64.13 Hiding variable latency dynamically is what out-of-order execution is for.
- Exposed hazards age poorly. MIPS-I exposed load, multiply and divide hazards and branch delay slots; interlocks later replaced the data hazards, but the branch delay slot stayed for compatibility, and for superscalar and deep pipelines it is pure cost. Waterman notes that even for a five-stage pipeline, dropping the slot and adding a small branch target buffer typically gives better performance and performance per area.5
- Encoding constraints are permanent. MIPS’s 16-bit immediates left too little opcode space for a compressed encoding, forcing a mode switch; RISC-V reserved space for 16-bit instructions from the start.5
No fixes: CPI 1.98. Turn on a fix to see what it saves and what it costs.
This part goes deeper, into the math, models and algorithms behind the chapter. It’s written for the Expert level.
1. Hazard detection and forwarding
The control for a fully bypassed five-stage pipeline is a few comparators. Forwarding selects, per ALU operand, the newest in-flight value for that register; the hazard unit handles the one case forwarding can’t, a load at distance 1. Cornell’s notes derive the same signals (their ostall_load_use and bypass_waddr terms), including the valid bits and the x0 check.11
// Operand A of the instruction in X (operand B is the same with rs2).
always_comb begin
if (xm_wen && xm_rd != 5'd0 && xm_rd == dx_rs1) fwd_a = FWD_XM; // ALU result, 1 back
else if (mw_wen && mw_rd != 5'd0 && mw_rd == dx_rs1) fwd_a = FWD_MW; // load data or result, 2 back
else fwd_a = FWD_RF; // value from the register file
end
// Load-use: the instruction in D needs what the load now in X hasn't read yet.
assign stall_d = dx_valid && dx_is_load && dx_rd != 5'd0 &&
((fd_uses_rs1 && dx_rd == fd_rs1) || (fd_uses_rs2 && dx_rd == fd_rs2));
// Stall: hold PC and F/D, insert a bubble into D/X.
assign pc_en = !stall_d;
assign fd_en = !stall_d;
assign dx_valid_next = fd_valid && !stall_d && !squash;
// Taken branch resolved in X: squash the two younger instructions (in F and D).
assign squash = xm_br_taken_now;- 1L3X/M checked first: if both match, the X/M value is newer and correct.
- 2L3Writes to x0 must never be forwarded; x0 always reads as zero.
- 3L9Only loads stall with full bypassing; ALU producers are covered by the muxes above.
- 4L9fd_uses_rs*: an instruction that doesn’t read rs2 (an I-type) must not stall on it.
- 5L15A bubble is an instruction with its valid bit cleared, so no later stage writes anything.
- 6L18Squashes win over stalls: a flushed instruction can’t be the reason to hold the pipeline.
Two refinements are common. Branches resolved in D need their own bypass into the comparator and a stall rule for ALU (1) and load (2) producers. And the register file’s write-before-read timing removes the need for a third bypass from W into D.
2. A CPI model for the in-order pipeline
For a scalar in-order pipeline with full bypassing and predict-not-taken branches resolved in X:
where is the fraction of loads, the fraction whose first use is the very next instruction, the fraction of branches and the fraction taken; the last term collects cache misses per instruction times their penalties, which this chapter leaves to Caches and coherence. The Trade-offs figure is this model with illustrative and . The simulation measures the same quantity directly as , excluding the fill cycles, which vanish as grows.
3. Scheduling to fill the load-use slot
Compilers typically hide the slot with list scheduling on each basic block. Build the dependence graph (edges for read-after-write, write-after-read and write-after-write, weighted by latency: 2 after a load, 1 after an ALU instruction in this pipeline); give each node a priority, usually its longest latency-weighted path to the end of the block; then cycle by cycle issue the highest-priority instruction whose predecessors have completed. GCC has a pass before register allocation (-fschedule-insns) and one after (-fschedule-insns2), which its manual calls especially useful on machines with few registers and loads that take more than one cycle; it also has -fdelayed-branch to fill branch delay slots on targets that have them.13
before: lw x1, 0(x10) cycle 1
add x2, x1, x3 cycle 3 <- stall, x1 not ready
lw x4, 4(x10) cycle 4
add x5, x4, x6 cycle 6 <- stall
sub x7, x8, x9 cycle 7 CPI 1.40
priorities (longest path to end): lw x1 = 3, lw x4 = 3, add x2 = 1, add x5 = 1, sub = 1
after: lw x1, 0(x10) cycle 1
lw x4, 4(x10) cycle 2
add x2, x1, x3 cycle 3 x1 ready via M/W bypass
add x5, x4, x6 cycle 4
sub x7, x8, x9 cycle 5 CPI 1.00- 1L2Cycle numbers are when each instruction enters X.
- 2L7Loads head the longest paths, so the scheduler issues both first.
- 3L11Moving lw x4 up is legal: it neither reads nor writes x1 or x2.
4. Exposing hazards in the ISA
The alternative to interlocks is to make the hazard part of the contract: a delay slot after each load or branch that the compiler must fill (with a no-op if nothing fits). Cornell lists this first among the ways to resolve data and control hazards: it removes the stall logic but makes software scheduling necessary for correctness.11 MIPS-I did it for both; later MIPS revisions replaced the load slot with interlocks because that was simpler for software and could perform better, while the branch delay slot had to stay for compatibility, even though dropping it and adding a small branch target buffer usually gives better performance and performance per area, even in a five-stage pipeline.5 RISC-V’s control transfers have no architecturally visible delay slots.3
5. Precise exceptions in the pipeline
A load can fault in M while a younger instruction is already in X and an older one in W. The pipeline must report the oldest fault and make the architectural state look as if every older instruction completed and no younger one started. Cornell’s design records exception flags as the instruction flows, acts on them at a commit point after the last stage that can raise one and before any state is updated, squashes younger instructions, saves the PC of the faulting instruction and redirects fetch to the handler; asynchronous interrupts are injected at the same point.11 The same idea, scaled up with a reorder buffer, is how out-of-order cores keep exceptions precise.
You now know the basic plan of every CPU: a list of commands, and an assembly line that runs them. Real chips add clever tricks. The next chapter shows how a chip guesses which way a jump will go, and how it does commands out of order when some have to wait.
Two simplifications in this chapter matter most. The pipeline stalls on every taken branch and every load-use pair, and it never runs more than one instruction per stage. Branch prediction and out-of-order execution removes both: predictors guess branches before they resolve, and runs whichever instructions are ready, several at a time. After that, Caches and coherence drops the assumption that memory answers in one cycle, and Multicore and vector units covers what chips did when deeper pipelines stopped paying. For a very different way to keep math units busy, see SIMT and GPUs.
Every limit of the scalar in-order pipeline is a lever pulled in the next chapters: the two-cycle taken branch (branch target buffers and direction predictors), the load-use slot and every longer latency (dynamic scheduling, register renaming and a reorder buffer in Branch prediction and out-of-order execution), the one-cycle memory stage (Caches and coherence), and the single instruction stream per core (Multicore and vector units). The depth–frequency optimum stays in the Architecture stage.
1. Pipelines: This chapter: the instruction set, the five-stage pipeline, hazards and CPI.
Q1What does an instruction set architecture (ISA) define?
Q2With forwarding on, why does lw x1, 0(x10) followed directly by add x2, x1, x3 still stall for one cycle?
Q3A single-cycle CPU has an 800 ps clock. Its stages take 200, 100, 200, 200 and 100 ps. Ignoring register overhead, how much faster can a five-stage pipeline run a long program?
Q4Chip A runs a program in 1.0 billion instructions at CPI 1.5 and 3 GHz. Chip B needs 1.2 billion at CPI 1.0 and 3 GHz. Which finishes first?
Sources
Show Hide 18 sources
- ECE 4750 Computer Architecture, Topic 1: Processor ConceptsThe ISA as the contract between software and hardware; the IBM 360 as the first line of machines to separate ISA from microarchitecture, with Amdahl, Blaauw and Brooks’s 1964 definition; time = instructions × CPI × cycle time; single-cycle CPI 1 with a long cycle, pipelined CPI ≈ 1 with a short one.
- The RISC-V Instruction Set Manual, Volume I: Unprivileged Architecture — IntroductionA completely open ISA, freely available; avoids over-architecting for a particular microarchitecture; base integer ISA ‘I’ plus standard extensions M, A, F, D and C; fixed-length 32-bit base instructions and 16-bit compressed ones; the spec is the software-visible interface to many implementations.
- The RISC-V Instruction Set Manual, Volume I — RV32I Base Integer Instruction Set, Version 2.132 x registers with x0 hardwired to zero; the pc holds the address of the current instruction; four core formats (R/I/S/U) plus B/J variants, all a fixed 32 bits; load-store architecture; no architecturally visible delay slots; software should assume backward branches are predicted taken; NOP is ADDI x0, x0, 0.
- About RISC-V InternationalAn open, royalty-free ISA governed by the non-profit RISC-V International (founded 2015, incorporated in Switzerland in March 2020); started in May 2010 at UC Berkeley by Krste Asanović, Yunsup Lee and Andrew Waterman.
- Design of the RISC-V Instruction Set Architecture (PhD thesis, UCB/EECS-2016-1)x86 instructions of any length up to 15 bytes, about 1,300 instructions in 2015, x86-64 doubling integer registers to 16, out-of-order x86 cores translating instructions into RISC-like internal operations; ARMv8 announced in 2011 with a fixed-width encoding; architectural licenses; MIPS-I exposed load and branch delay slots, the load ones later removed; RV32I has 47 instructions in six formats; the compressed extension cuts code size by about 25–30%.
- David Patterson: A winning RISCPatterson coined ‘reduced instruction set computer’ in 1980; a smaller set of simple, general instructions needing fewer transistors; the 1982 RISC-I outperformed a conventional design that used more than twice as many transistors.
- Machine-Level Programming I: Basics (15-213/18-213: Introduction to Computer Systems)x86 is backward compatible with the 8086 of 1978 and documented in about 5,000 pages; a complex instruction set computer (CISC) with many instructions and formats; x86-64 has 16 integer registers; compared with RISC designs such as Arm and RISC-V.
- Assembly Language: Part 1 (COS 217: Introduction to Programming Systems)AArch64 general-purpose registers X0–X30 plus a zero register (XZR), the PC; arithmetic instructions only access registers, a load/store architecture characteristic of RISC, versus CISC such as x86.
- Machine Language (COS 217: Introduction to Programming Systems)All AArch64 instructions are 32 bits long and 4-byte aligned; some bits give the opcode, others the registers (5-bit fields), an immediate or an offset.
- Introducing Intel Advanced Performance Extensions (Intel APX)APX doubles x86-64’s general-purpose registers from 16 to 32 and adds three-operand forms of legacy integer instructions; code compiled with APX has about 10% fewer loads and more than 20% fewer stores.
- ECE 4750 Computer Architecture, Topic 2: Processor MicroarchitectureSingle-cycle, FSM and five-stage pipelined RISC-V processors (stages F, D, X, M, W); pipelining helps throughput, not latency, and its speedup is limited by the slowest stage and by fill time; worked example (single-cycle CPI 1.0 at 74τ, FSM CPI 6.7 at 36τ); RAW hazards resolved by exposing them in the ISA, stalling, bypassing or speculation; ALU-use latency one cycle, load-use two; jump and branch resolution latencies of two and three cycles; structural hazards by stalling or duplication; exceptions held to a commit point.
- Processor Pipeline Hazards (CS 315 Computer Architecture lecture notes)The five RISC-V pipeline stages; a data hazard is an instruction needing a value an in-flight instruction hasn’t written back; forwarding routes the result to EX through multiplexers; a load-use hazard can’t be forwarded in time and needs a one-cycle stall; control hazards turn wrongly fetched instructions into bubbles.
- Options That Control Optimization (GCC manual)-fschedule-insns reorders instructions to eliminate execution stalls due to required data being unavailable, helping machines with slow floating-point or memory loads; -fschedule-insns2 schedules again after register allocation.
- Iron law of processor performanceTime per program = instructions per program × cycles per instruction × time per cycle; the name is credited to Douglas Clark, from his work with Joel Emer characterizing the VAX-11/780 (1984).
- Power Struggles: Revisiting the RISC vs. CISC Debate on Contemporary ARM and x86 ArchitecturesCortex-A8, Cortex-A9, Atom N450 and Sandy Bridge i7 measured on mobile, desktop and server workloads; x86 CISC instructions split into RISC-like micro-ops; instruction counts similar across ISAs; geometric-mean CPI 3.4 (A8), 2.2 (A9), 2.1 (Atom) and 0.7 (i7); ‘the ISA being RISC or CISC seems irrelevant.’
- Pipeline Details (Ibex RISC-V core documentation)A 2-stage pipeline (instruction fetch; decode and execute), configurable with a third write-back stage; loads stall at least one cycle for their response; taken branches take 2 cycles, or 1 with the branch-target ALU.
- The Rocket Chip Generator (Technical Report UCB/EECS-2016-17)Rocket is a 5-stage in-order scalar core generator for RV32G and RV64G, with a branch target buffer, branch history table and return address stack; BOOM is its out-of-order sibling.
- CVA6 User Manual: IntroductionCVA6 (formerly Ariane) is a 6-stage, single-issue 64-bit RISC-V CPU implementing the I, M and C extensions.