---
title: The Whole Machine
module: Capstone
moduleNumber: 10
lessonNumber: 1
order: 1001
summary: >
  We take one line of C down the whole tower the
  course built — compiler to assembly, assembly to machine-code bytes, the bytes
  into the fetch–decode–execute datapath — then trace one load and
  one add through the pipelined, cached, translated, interruptible machine,
  each step cross-linked to the lesson that built it. We close with the map of
  the course as a stack of layers and an accounting of what we simplified:
  out-of-order execution, superscalar issue, and speculation past the branch
  predictor.
topics: [Capstone]
sources:
  - book: Bryant & O'Hallaron
    ref: "CS:APP — §4 Processor Architecture (synthesis); §5.7 Modern Processor Operation"
  - book: Bistriceanu
    ref: "Computer Architecture Notes — §2 Basic Organization / §5 CPU Implementation"
---

We have built every layer separately. We fixed the units —
[bits and bytes](/computer-architecture/foundations/bits-bytes-and-words), then
[the ALU](/computer-architecture/digital-logic/multiplexers-decoders-and-the-alu)
and [the register file](/computer-architecture/digital-logic/register-files-and-random-access-memory).
We fixed the [Y86-64 ISA](/computer-architecture/instruction-set-architecture/the-y86-64-instruction-set)
the machine speaks, wired the units into
[SEQ](/computer-architecture/processor-design/assembling-seq), watched it
[run a program](/computer-architecture/processor-design/tracing-a-program), then
[pipelined it](/computer-architecture/pipelining/the-complete-pipe-processor), gave
it [caches](/computer-architecture/memory-hierarchy/cache-memories-direct-mapped)
and [virtual memory](/computer-architecture/virtual-memory/address-spaces-and-translation),
connected it to [the outside world](/computer-architecture/exceptions-and-io/interrupts-and-the-kernel),
and finally [put a second one on the die](/computer-architecture/multithreading-and-multicore/processes-threads-and-parallelism).
What we have not yet done is stand back and see the layers as **one** thing. This
lesson makes two passes over the whole machine. First a **vertical** pass: a single
line of C followed straight down the tower, from source to the charge that moves in
DRAM. Then a **horizontal** pass, the grand tour: two instructions traced through
the full machine — pipeline stages, forwarding, TLBs, caches, a possible page fault,
a timer interrupt — with every stop cross-linked to the lesson that built it. No new
mechanism appears; the point is that every mechanism covered is one stop on a path a
value actually travels, and they compose.

## One line, all the way down

Here is the line. It is the smallest thing that touches every layer: a read from
memory, an arithmetic op, a write back.

```c [sum.c]
long s = a[i] + 1;
```

Nothing about it looks like hardware. `a` is an array, `i` an index, `s` a
variable; the `+ 1` is arithmetic the way a calculator does arithmetic. But there
is no array, no variable, and no addition in the machine — only bytes in memory and
a datapath pulling them in. Each layer below translates this line into the
vocabulary of the layer beneath it, until what is left is gates switching.

$$
% caption: One line of C descending through every layer of the course. Each band is
% caption: a stop on the path: the compiler lowers C to assembly, the assembler to
% caption: machine-code bytes, the datapath fetches and runs them on the ALU and
% caption: register file, and the result settles through cache and DRAM in silicon.
\begin{tikzpicture}[font=\footnotesize,>=stealth,
  layer/.style={draw, fill=acc!8, minimum width=64mm, minimum height=8mm,
                align=center, inner sep=3pt},
  tag/.style={anchor=west, text=acc!80, font=\footnotesize\ttfamily}]
  \definecolor{acc}{HTML}{2348F2}
  \foreach \y/\name/\tag in {
    0/{\textbf{C source}: \texttt{long s = a[i] + 1;}}/{foundations},
    -1.15/{compiler -> \texttt{x86-64 / Y86-64 assembly}}/{machine-level x86-64},
    -2.30/{\texttt{assembler} -> \texttt{machine-code} bytes}/{instruction set architecture},
    -3.45/{\texttt{fetch-decode-execute} datapath}/{processor design + pipelining},
    -4.60/{\textbf{ALU} + \texttt{register file} + control}/{digital logic},
    -5.75/{\texttt{cache} -> TLB / page table -> DRAM}/{memory + virtual memory},
    -6.90/{gates and transistors (silicon)}/{digital logic} } {
    \node[layer] at (0,\y) {\name};
    \node[tag] at (3.5,\y) {\tag};
  }
  % single descending spine on the left margin, crossing nothing
  \draw[->, acc] (-3.7,0.25) -- (-3.7,-7.15);
  \node[text=acc, anchor=south, rotate=90, font=\footnotesize\ttfamily] at (-3.95,-3.45)
        {lowering};
\end{tikzpicture}
$$

> **Takeaway.** Every layer of the course is a translation step. The line of C does
> not _run_ at any one level; it is rewritten, level by level, into the language of
> the level below, until the bottom level is logic gates — and the answer climbs
> back up the same ladder as a stored value.

## Compiler and assembler: C becomes bytes

The first translation is the **compiler**, which lowers the C line into assembly. It
must turn the named array access `a[i]` into an explicit address computation (a base
register plus an index scaled by the element size) and the abstract `+ 1` into a
concrete instruction with a size suffix. In x86-64 the line might compile to a load,
an add, and a store; in the Y86-64 subset the course implements, the same three
moves look like this.

```asm [sum.ys]
mrmovq (%rdi), %rax    # rax <- M[a + i*8]   (the load; %rdi holds the address)
irmovq $1, %r8         # r8  <- 1            (the constant)
addq   %r8, %rax       # rax <- rax + 1      (the + 1)
rmmovq %rax, (%rsi)    # M[&s] <- rax        (store back to s)
```

The **assembler** then performs the second translation, and it is purely
mechanical: each line becomes the exact bytes the
[ISA lesson](/computer-architecture/instruction-set-architecture/the-y86-64-instruction-set)
specified. `mrmovq (%rdi),%rax` is `icode:ifun = 5:0`, register byte `rA:rB = 0:7`
(`%rax`:`%rdi`), and an 8-byte displacement of zero: ten bytes,
`50 07 00 00 00 00 00 00 00 00`. `addq %r8,%rax` is `60 80`, two bytes. There is no
cleverness left at this level: the assembler is a lookup table from mnemonics to the
`icode:ifun` byte, the register nibbles, and the little-endian constant. After it
runs, the C line exists nowhere — what sits in memory is a run of bytes, and those
bytes _are_ the program.

The three notations are worth seeing side by side, because the machine only ever
sees the rightmost column, yet all three are descriptions of the same single
instruction.

$$
% caption: The same load shown three ways. The C subscript a[i] is one assembly
% caption: instruction mrmovq (%rdi),%rax, which the assembler encodes as the
% caption: ten machine-code bytes the datapath fetches. Reading left to right is compiling; the
% caption: machine only ever sees the right column.
\begin{tikzpicture}[font=\footnotesize,>=stealth,
  col/.style={draw, fill=acc!8, minimum width=33mm, minimum height=12mm,
              align=center, inner sep=3pt},
  lab/.style={anchor=south, text=acc, font=\footnotesize\ttfamily}]
  \definecolor{acc}{HTML}{2348F2}
  \node[lab] at (-4.2,0.85) {C source};
  \node[lab] at (0,0.85)    {Y86-64 assembly};
  \node[lab] at (4.4,0.85)  {machine-code bytes};
  \node[col] (c) at (-4.2,0) {\texttt{a[i]}};
  \node[col] (a) at (0,0)    {\texttt{mrmovq (\%rdi),}\\\texttt{\%rax}};
  \node[col] (b) at (4.4,0)  {\texttt{50 07 00 00}\\\texttt{00 00 00 00 00 00}};
  \draw[->, acc] (c.east) -- (a.west) node[midway,above,font=\scriptsize,text=acc]{compile};
  \draw[->, acc] (a.east) -- (b.west) node[midway,above,font=\scriptsize,text=acc]{assemble};
\end{tikzpicture}
$$

## The datapath: bytes become work

Now the bytes are in memory and the **PC** holds the address of the first one. From
here the machine drives itself, with no compiler and no assembler in sight, just the
[fetch–decode–execute loop](/computer-architecture/processor-design/assembling-seq)
turning bytes into state changes.

- **Fetch** reads the bytes at the PC. For our load it pulls `50 07 00 …`, splits the
  first byte into `icode:ifun = 5:0`, reads the register byte `0:7`, and reads the
  8-byte displacement. It also computes the next-instruction address
  $\texttt{valP} = \text{PC} + \text{len}$, where $\text{len}$ is this instruction's
  byte length (10 here).
- **Decode** uses the register nibbles as addresses into
  [the register file](/computer-architecture/digital-logic/register-files-and-random-access-memory):
  it reads `%rdi` (the array base address) out the combinational read port as
  `valB`. No clock, no waiting: the read port simply presents the register's
  contents.
- **Execute** sends the base and the displacement into
  [the ALU](/computer-architecture/digital-logic/multiplexers-decoders-and-the-alu),
  which adds them (the same ripple-carry adder from digital logic) to form the
  effective memory address `valE = R[%rdi] + 0`. For the later `addq` it is this same
  ALU that performs the actual `+ 1` and sets the condition codes.
- **Memory** presents `valE` as an address to data memory and reads the word there,
  `valM` — this is the `a[i]` access finally happening.
- **Write-back** clocks `valM` into `%rax` through the register file's write port.
- **PC update** loads `valP`, and the loop repeats with the next instruction.

Every one of those steps is a unit we built and a mux the
[control unit](/computer-architecture/processor-design/assembling-seq) selected. The
opcode `5:0` is what tells the control logic to route the ALU output to the memory
address port and the memory output to the register write port; a different opcode
would steer the same wires to a different shape. The datapath is fixed silicon; the
opcode is what configures it, cycle by cycle.

## Down to silicon, and the memory request path

Two layers remain below the datapath. The first is the **physical logic**: the ALU's
adder is full adders, each a 3-input XOR and a majority gate; the register file's
ports are decoders and multiplexers; the muxes the control unit drives are AND–OR
trees. The `+` in `a[i] + 1` is, at the bottom, a carry rippling through a row of
gates, which is a wave of transistors switching. There is no arithmetic down there:
only switches that, wired the way digital logic prescribed, _compute_ the sum as a
side effect of settling.

The second is the **memory request path**. When Memory asks for the word at `valE`,
that address is a **virtual** address, and reaching the real bits takes several
steps. The cache is checked first; on a miss the virtual address is translated to a
physical one (the **TLB** answers fast if it has seen the page; otherwise the **page
table** in memory is walked), and only then does the request reach physical **DRAM**,
where reading the word means sensing charge on a row of one-transistor cells and
restoring it after the destructive read. The observed access time is the hit time
plus the amortized miss penalty,

$$
T_{\text{access}} = T_{\text{hit}} + m \cdot T_{\text{miss}},
$$

with miss rate $m$ and $T_{\text{miss}}$ the cost of descending toward DRAM; the
whole hierarchy exists to drive $m$ toward zero so $T_{\text{access}} \to
T_{\text{hit}}$. Two side exits complicate the picture, and
the course built both. If the walk finds the page absent, the access does not
complete at all: it becomes a
[page fault](/computer-architecture/virtual-memory/page-tables-and-page-faults), the
kernel pages the data in from disk, and the instruction re-executes. And on the
multicore die of
[module 9](/computer-architecture/multithreading-and-multicore/multicore-organization),
a miss consults more than DRAM: the freshest copy of the line may be sitting
modified in a **peer core's cache**, and the
[coherence protocol](/computer-architecture/multithreading-and-multicore/cache-coherence)
must fetch it from there, because DRAM's copy is stale.

$$
% caption: The request path of one memory access. The datapath issues a virtual
% caption: address; the cache answers a hit at once; on a miss the TLB, or failing
% caption: that the page table, translates virtual to physical, and the request
% caption: reaches DRAM. On a multicore, coherence may redirect the miss to a peer
% caption: cache; if the page is absent, the access becomes a page fault instead.
\begin{tikzpicture}[font=\footnotesize,>=stealth,
  u/.style={draw, fill=acc!8, minimum width=20mm, minimum height=10mm,
            align=center, inner sep=2pt},
  g/.style={draw=black, dashed, minimum width=20mm, minimum height=10mm,
            align=center, text=black, inner sep=2pt}]
  \definecolor{acc}{HTML}{2348F2}
  \node[u] (dp)  at (0,0)     {datapath\\(Memory)};
  \node[u] (ca)  at (4.3,0)   {\texttt{cache}};
  \node[u] (tlb) at (8.2,0)   {TLB};
  \node[u] (pt)  at (8.2,-2.2) {page\\table};
  \node[u] (dr)  at (12.0,0)  {DRAM};
  % main request chain along the row
  \draw[->] (dp.east) -- (ca.west) node[midway,above,font=\scriptsize]{virtual addr};
  \draw[->] (ca.east) -- (tlb.west) node[midway,above,font=\scriptsize]{miss};
  \draw[->] (tlb.east) -- (dr.west) node[midway,above,font=\footnotesize]{\texttt{phys} addr};
  % hit short-circuit returns over the top margin, crossing nothing
  \draw[->, acc] (ca.north) -- ++(0,0.9) -| (dp.north);
  \node[font=\scriptsize, text=acc, anchor=south] at (2.15,1.45) {hit: the word returns};
  % TLB miss drops to the page-table walk, result rejoins at DRAM
  \draw[->] (tlb.south) -- (pt.north) node[midway,right,font=\scriptsize]{TLB miss: walk};
  \draw[->, acc] (pt.east) -| (dr.south);
  \node[font=\footnotesize, text=acc, anchor=north] at (10.5,-2.3) {\texttt{phys} addr from walk};
  % coherence side exit (module 9)
  \node[g] (peer) at (4.3,-2.2) {peer core's\\\texttt{cache}};
  \draw[<->, black, dashed] (ca.south) -- (peer.north);
  \node[font=\footnotesize, text=black, anchor=west] at (4.5,-1.1) {coherence (\texttt{module 9})};
  % page-fault side exit (module 8)
  \node[font=\scriptsize, text=black, align=center, anchor=north] at (8.2,-3.0)
        {page absent: page fault\\(\texttt{modules 7-8})};
\end{tikzpicture}
$$

The word comes back the way it went out — DRAM to cache to the datapath's Memory
stage — and rises through Write-back into `%rax`, where the `addq` adds one and the
`rmmovq` sends it down the same path again to store `s`. The whole round trip, from a
C subscript to charge in a capacitor and back, is the course read end to end.

> **Takeaway.** `long s = a[i] + 1;` is not one operation but a stack of
> translations: the compiler turns it into assembly, the assembler into bytes, the
> datapath fetches and decodes those bytes, the ALU and register file do the work,
> and the memory access threads through cache, TLB or page table, and DRAM before
> the answer climbs back up. Every module of this course names one rung of that
> ladder, and the rungs are continuous — a value really does travel the whole length.

## The grand tour: two instructions on the full machine

The walk above ran on bare SEQ: ideal one-cycle memories, one instruction at a
time, nothing else on the machine. The later modules removed every one of those
simplifications. [Pipelining](/computer-architecture/pipelining/from-seq-to-pipe)
put five instructions in flight at once;
[caches](/computer-architecture/memory-hierarchy/cache-memories-direct-mapped) made
memory fast only where
[locality](/computer-architecture/memory-hierarchy/locality) holds;
[virtual memory](/computer-architecture/virtual-memory/address-spaces-and-translation)
inserted a translation between every address the program names and every address
the hardware touches;
[interrupts](/computer-architecture/exceptions-and-io/interrupts-and-the-kernel)
let the outside world preempt the program between any two instructions; and
[module 9](/computer-architecture/multithreading-and-multicore/multicore-organization)
put a second core on the die and required the caches to stay coherent. Here is the
machine as the course actually left it.

$$
% caption: The full machine. Core 0 is the five-stage PIPE datapath; fetch and
% caption: memory each translate through a TLB and then probe an L1 cache; the L1s
% caption: share a unified L2, which meets the rest of the die at the shared L3 and
% caption: interconnect. Below sit the memory controller with DRAM and the I/O
% caption: devices whose interrupts re-enter the core between instructions. Core 1,
% caption: dashed, is module 9's addition — a full copy kept honest by coherence.
\begin{tikzpicture}[font=\footnotesize,>=stealth,
  u/.style={draw, fill=acc!8, align=center, inner sep=2pt},
  stage/.style={draw, fill=acc!8, minimum width=9mm, minimum height=7mm},
  ghost/.style={draw=black, dashed, align=center, text=black, inner sep=2pt},
  tag/.style={text=acc!75, font=\footnotesize\ttfamily},
  wl/.style={font=\scriptsize, text=black}]
  \definecolor{acc}{HTML}{2348F2}
  % ===== Core 0 container and pipeline =====
  \draw (-0.4,5.1) rectangle (7.8,7.5);
  \node[anchor=north west, font=\scriptsize\bfseries, text=acc] at (-0.3,7.42) {Core 0: the PIPE datapath};
  \node[tag, anchor=north east] at (7.7,7.42) {modules 4-5};
  \node[stage] (f) at (0.6,5.9) {\texttt{F}};
  \node[stage] (d) at (2.2,5.9) {\texttt{D}};
  \node[stage] (e) at (3.8,5.9) {\texttt{E}};
  \node[stage] (m) at (5.4,5.9) {\texttt{M}};
  \node[stage] (w) at (7.0,5.9) {\texttt{W}};
  \draw[->] (f) -- (d); \draw[->] (d) -- (e);
  \draw[->] (e) -- (m); \draw[->] (m) -- (w);
  % ===== translation, then L1s =====
  \node[u, minimum width=17mm, minimum height=8mm] (itlb) at (0.6,4.0) {i-TLB};
  \node[u, minimum width=17mm, minimum height=8mm] (l1i) at (0.6,2.6) {\texttt{L1 i-cache}};
  \node[u, minimum width=17mm, minimum height=8mm] (dtlb) at (5.4,4.0) {d-TLB};
  \node[u, minimum width=17mm, minimum height=8mm] (l1d) at (5.4,2.6) {\texttt{L1 d-cache}};
  \draw[->] (f.south) -- (itlb.north);
  \node[wl, anchor=west] at (0.78,4.78) {virtual addr};
  \draw[->] (itlb.south) -- (l1i.north);
  \node[wl, anchor=west] at (0.78,3.3) {\texttt{phys} addr};
  \draw[->] (m.south) -- (dtlb.north);
  \node[wl, anchor=west] at (5.58,4.78) {virtual addr};
  \draw[->] (dtlb.south) -- (l1d.north);
  \node[wl, anchor=west] at (5.58,3.3) {\texttt{phys} addr};
  \node[tag, anchor=west] at (6.5,4.0) {module 7};
  \node[tag, anchor=west] at (6.5,2.6) {module 6};
  % ===== unified L2 =====
  \node[u, minimum width=68mm, minimum height=8mm] (l2) at (3.0,1.2) {\texttt{unified L2 cache}};
  \draw[->] (l1i.south) -- (0.6,1.6);
  \draw[->] (l1d.south) -- (5.4,1.6);
  % ===== shared L3 + interconnect bar =====
  \node[u, minimum width=120mm, minimum height=8mm, fill=acc!4] (bar) at (5.0,-0.2)
        {shared \texttt{L3} + ring \texttt{interconnect}};
  \draw[->] (3.0,0.8) -- (3.0,0.2);
  \node[tag, anchor=east] at (11.0,-0.78) {module 9};
  % ===== memory and I/O below the bar =====
  \node[u, minimum width=34mm, minimum height=9mm] (dram) at (1.2,-1.9) {memory controller\\+ DRAM};
  \node[u, minimum width=34mm, minimum height=9mm] (io) at (6.9,-1.9) {I/O: disk, NIC, \texttt{timer}};
  \draw[->] (1.2,-0.6) -- (dram.north);
  \draw[->] (6.9,-0.6) -- (io.north);
  \node[wl, anchor=west] at (7.05,-1.05) {DMA};
  \node[tag, anchor=west] at (3.05,-1.9) {modules 6-7};
  % ===== interrupt path up the right margin =====
  \draw[->, acc, dashed] (io.east) -- (11.6,-1.9) -- (11.6,5.9) -- (7.8,5.9);
  \node[font=\footnotesize\ttfamily, rotate=-90, anchor=center, text=acc!80] at (11.9,2.0) {interrupts (module 8)};
  % ===== ghost second core =====
  \node[ghost, minimum width=30mm, minimum height=26mm] (c1) at (9.6,4.0)
        {Core 1\\(own \texttt{pipeline},\\\texttt{L1}s, TLBs, \texttt{L2})};
  \node[tag, anchor=south] at (9.6,5.4) {module 9};
  \draw[->, black, dashed] (9.6,2.7) -- (9.6,0.2);
  \node[wl, anchor=west] at (9.75,1.3) {coherence};
\end{tikzpicture}
$$

Now run two instructions from the compiled line — the load `mrmovq (%rdi),%rax`
and, right behind it, `addq %r8,%rax` — across this machine, stage by stage. This
is the course's grand tour: every clause below is a lesson.

**Fetch.** The pipeline does not wait to be sure what to fetch next; the
[PC prediction logic](/computer-architecture/pipelining/control-hazards-and-branch-prediction)
guesses (for straight-line code, correctly) that the next instruction follows the
current one, and fetch proceeds. The PC it presents is a **virtual** address,
so it is translated first: the **i-TLB** answers in the same cycle if the page is
warm ([the TLB lesson](/computer-architecture/virtual-memory/the-tlb-and-multi-level-page-tables)).
The physical address then indexes the **L1 i-cache**, whose
[set-index/tag-compare machinery](/computer-architecture/memory-hierarchy/cache-memories-direct-mapped)
returns the ten instruction bytes on a hit. The
[fetch stage](/computer-architecture/processor-design/the-seq-stages) splits them:
`icode:ifun = 5:0`, registers `0:7`, displacement `0`, and `valP`.

**Decode.** The register nibbles address the
[register file](/computer-architecture/digital-logic/register-files-and-random-access-memory),
which presents `%rdi` combinationally. But in a pipeline the register file can be
stale: an older instruction that writes `%rdi` may still be in flight ahead of us.
So decode does not use the file's value blindly: the
[forwarding logic](/computer-architecture/pipelining/data-hazards-stalling-and-forwarding)
compares our source registers against the destination fields of everything in
Execute, Memory, and Write-back, and takes the youngest match off the bypass wires
instead. For this load there is no conflict; `valB = R[%rdi]` stands.

**Execute.** The [ALU](/computer-architecture/digital-logic/multiplexers-decoders-and-the-alu)
adds base and displacement to form the effective address, exactly as in SEQ. A
`mrmovq` leaves the
[condition codes](/computer-architecture/digital-logic/memory-elements-latches-flip-flops-and-clocking)
alone; only the `OPq` instructions set them.

**Memory.** The effective address is again virtual: the **d-TLB** translates it. A
TLB miss costs a
[page-table walk](/computer-architecture/virtual-memory/the-tlb-and-multi-level-page-tables):
a few dependent memory reads, tens of cycles. And if the walk finds the page not
present, the access does not merely slow down; it **cannot complete**. The machine
raises a [page fault](/computer-architecture/virtual-memory/page-tables-and-page-faults):
the `mrmovq` and everything younger is cancelled, the
[exception machinery](/computer-architecture/exceptions-and-io/exceptional-control-flow)
transfers to the kernel with the faulting address in hand, the kernel pages the
data in from disk, and the same `mrmovq` re-executes as if nothing happened:
precise exceptions, doing exactly the job
[PIPE's special cases](/computer-architecture/pipelining/the-complete-pipe-processor)
prepared for. On the ordinary path the physical address probes the **L1 d-cache**;
a hit returns `valM` in a few cycles, and a miss descends the
[hierarchy](/computer-architecture/memory-hierarchy/storage-technologies-and-the-latency-gap)
toward DRAM, two hundred cycles away. On the multicore die, that miss carries one
more obligation: the [coherence protocol](/computer-architecture/multithreading-and-multicore/cache-coherence)
checks whether the other core holds the line modified, and if so the data comes
from the peer cache, not from DRAM, whose copy is stale.

**Write-back.** `valM` reaches the register file's write port and clocks into
`%rax` at the
[rising edge](/computer-architecture/digital-logic/memory-elements-latches-flip-flops-and-clocking).
One instruction done.

Meanwhile the `addq %r8,%rax` has been one stage behind the whole time, and it
needs `%rax` — the very register the load is still fetching from memory. This is
the [load-use hazard](/computer-architecture/pipelining/data-hazards-stalling-and-forwarding):
forwarding alone cannot fix it, because when `addq` sits in Decode the load's data
does not exist anywhere in the pipeline yet. The control logic holds `addq` in
Decode for one extra cycle and injects a bubble into Execute; one cycle later the
load is in Memory, its `valM` appears on the bypass wire, and forwarding hands it
straight to `addq`'s Decode: the register file itself is skipped.

$$
% caption: The load-use pair on PIPE. The mrmovq flows F,D,E,M,W; the dependent
% caption: addq stalls one extra cycle in Decode (shaded) while a bubble drains
% caption: through Execute, then picks up valM forwarded from the Memory stage in
% caption: cycle 4 and proceeds. Total cost of the hazard: one cycle.
\begin{tikzpicture}[font=\footnotesize,>=stealth,
  cell/.style={draw, fill=acc!8, minimum width=10mm, minimum height=7mm},
  bub/.style={draw=black, dashed, minimum width=10mm, minimum height=7mm, text=black},
  hd/.style={text=acc, font=\scriptsize\bfseries},
  il/.style={anchor=east, font=\footnotesize\ttfamily}]
  \definecolor{acc}{HTML}{2348F2}
  \node[hd, anchor=east] at (1.3,0.75) {cycle};
  \foreach \c in {1,...,7} {
    \node[hd] at (\c*1.35+0.65, 0.75) {\c};
  }
  % row 1: the load
  \node[il] at (1.3,0) {mrmovq (\%rdi),\%rax};
  \node[cell] at (2.0,0) {\texttt{F}};
  \node[cell] at (3.35,0) {\texttt{D}};
  \node[cell] at (4.7,0) {\texttt{E}};
  \node[cell] (m1) at (6.05,0) {\texttt{M}};
  \node[cell] at (7.4,0) {\texttt{W}};
  % row 2: the dependent add
  \node[il] at (1.3,-1.3) {addq \%r8,\%rax};
  \node[cell] at (3.35,-1.3) {\texttt{F}};
  \node[cell] at (4.7,-1.3) {\texttt{D}};
  \node[cell, fill=acc!25] (d2) at (6.05,-1.3) {\texttt{D}};
  \node[cell] at (7.4,-1.3) {\texttt{E}};
  \node[cell] at (8.75,-1.3) {\texttt{M}};
  \node[cell] at (10.1,-1.3) {\texttt{W}};
  % row 3: the injected bubble
  \node[il, text=black] at (1.3,-2.6) {(bubble)};
  \node[bub] at (6.05,-2.6) {\texttt{E}};
  \node[bub] at (7.4,-2.6) {\texttt{M}};
  \node[bub] at (8.75,-2.6) {\texttt{W}};
  % forwarding arrow, straight down one column
  \draw[->, acc, thick] (m1.south) -- (d2.north);
  \node[font=\footnotesize\ttfamily, text=acc, anchor=west] at (6.25,-0.65) {valM forwarded};
  \node[font=\scriptsize, text=black, anchor=west] at (10.8,-1.3) {one-cycle stall in D};
\end{tikzpicture}
$$

The outside world can also cut in at any instruction boundary. Suppose
the millisecond **timer** fires while the load is in Execute. Nothing halts
mid-gate. The
[interrupt machinery](/computer-architecture/exceptions-and-io/interrupts-and-the-kernel)
picks a clean boundary: instructions ahead of the boundary complete, instructions
behind it are cancelled, the address of the next unexecuted instruction is saved,
and control vectors through the interrupt table into the kernel. The kernel may
only tick its clock and return, or it may decide this thread's quantum is spent
and [hand the core to another thread](/computer-architecture/multithreading-and-multicore/processes-threads-and-parallelism),
loading that thread's saved registers and page-table base. Either way, when our
program next runs, the saved PC is restored and the trace resumes exactly where it
stopped. The program cannot tell it was ever off the CPU; that invisibility is the
whole design goal of
[exceptional control flow](/computer-architecture/exceptions-and-io/exceptional-control-flow).

$$
% caption: A timer interrupt lands between two instructions. Everything before the
% caption: boundary completes; the PC of the next instruction is saved; the kernel
% caption: handler runs (and may switch threads); iret restores the saved PC and
% caption: the program resumes, unable to tell it was ever preempted.
\begin{tikzpicture}[font=\footnotesize,>=stealth,
  ib/.style={draw, fill=acc!8, minimum width=16mm, minimum height=8mm, align=center,
             font=\footnotesize\ttfamily},
  kb/.style={draw, fill=acc!15, minimum height=9mm, align=center, font=\scriptsize},
  note/.style={font=\scriptsize, text=black, align=center}]
  \definecolor{acc}{HTML}{2348F2}
  \node[ib] (i1) at (-0.3,0) {addq \%r10,\%rax};
  \node[kb, minimum width=34mm] (k) at (4.3,0) {kernel handler runs\\\texttt{(may switch threads)}};
  \node[ib] (i2) at (8.3,0) {addq \%r8,\%rdi};
  \node[font=\footnotesize\ttfamily, text=black] at (10.2,0) {...};
  % boundaries
  \draw[acc, dashed] (1.45,-0.6) -- (1.45,0.55);
  \draw[acc, dashed] (6.8,-0.6) -- (6.8,0.55);
  % the interrupt arrives
  \draw[->, acc] (1.45,1.7) -- (1.45,0.65);
  \node[font=\footnotesize\ttfamily, text=acc, anchor=west] at (1.6,1.35) {timer interrupt};
  % annotations
  \node[note, anchor=north] at (1.45,-0.8) {PC of next\\instruction saved};
  \node[note, anchor=north] at (6.8,-0.8) {\texttt{iret}: saved\\PC restored};
  % time axis
  \draw[->, black] (-1.6,-2.1) -- (10.8,-2.1)
        node[anchor=west, font=\scriptsize, text=black] {time};
\end{tikzpicture}
$$

> **Takeaway.** One load, traced honestly, touches every module of the course: PC
> prediction (pipelining), the i-TLB and d-TLB (virtual memory), two L1 caches and
> the hierarchy below them (memory hierarchy), the register file and ALU (digital
> logic), forwarding and a load-use stall (pipelining), a possible page fault
> (virtual memory meeting exceptions), a possible interrupt (exceptions and I/O),
> and, on a multicore, the coherence protocol (module 9). The subsystems are
> stations on one path.

## The map of the course

Three strands of the course ran in parallel and
met in the middle. The **program strand** (foundations, machine-level x86-64, the
ISA) settled what must be computed, ending in an exact contract: bytes with
defined meanings. The **processor strand** (digital logic, processor design,
pipelining) built the thing that honors the contract, ending in PIPE. And the
**memory-and-world strand** (the memory hierarchy, virtual memory, exceptions and
I/O, multicore) surrounded that processor with everything a real machine needs:
storage that keeps up, address spaces that protect, a connection to devices, and
more cores than one. The capstone is where the strands tie.

$$
% caption: The course as three strands meeting in the capstone. The program strand
% caption: fixes the contract (bytes with meanings); the processor strand builds the
% caption: machine that honors it; the memory-and-world strand surrounds the core
% caption: with storage, protection, devices, and more cores. Arrows mark the two
% caption: hand-offs: the ISA gives the processor its bytes, and the processor gives
% caption: the memory system its addresses.
\begin{tikzpicture}[font=\footnotesize,>=stealth,
  mm/.style={draw, fill=acc!8, minimum width=52mm, minimum height=8mm, align=center,
             font=\footnotesize\ttfamily},
  ghd/.style={text=acc, font=\footnotesize\ttfamily}]
  \definecolor{acc}{HTML}{2348F2}
  % group headers
  \node[ghd] at (0,0.8)    {the program};
  \node[ghd] at (6.0,0.8)  {the processor};
  \node[ghd] at (12.0,0.8) {memory + the world};
  % column A: the program
  \node[mm] (a0) at (0,0)     {00 foundations};
  \node[mm] (a1) at (0,-1.05) {01 machine-level x86-64};
  \node[mm] (a2) at (0,-2.10) {02 instruction set arch.};
  \draw[->] (a0.south) -- (a1.north);
  \draw[->] (a1.south) -- (a2.north);
  % column B: the processor
  \node[mm] (b0) at (6.0,0)     {03 digital logic};
  \node[mm] (b1) at (6.0,-1.05) {04 processor design};
  \node[mm] (b2) at (6.0,-2.10) {05 pipelining};
  \draw[->] (b0.south) -- (b1.north);
  \draw[->] (b1.south) -- (b2.north);
  % column C: memory and the world
  \node[mm] (c0) at (12.0,0)     {06 memory hierarchy};
  \node[mm] (c1) at (12.0,-1.05) {07 virtual memory};
  \node[mm] (c2) at (12.0,-2.10) {08 exceptions + i/o};
  \node[mm] (c3) at (12.0,-3.15) {09 multicore};
  \draw[->] (c0.south) -- (c1.north);
  \draw[->] (c1.south) -- (c2.north);
  \draw[->] (c2.south) -- (c3.north);
  % hand-offs between strands
  \draw[->, acc] (a2.east) -- (3.0,-2.10) |- (b1.west);
  \node[font=\scriptsize, text=acc, rotate=90, anchor=center] at (2.75,-1.58) {bytes};
  \draw[->, acc] (b2.east) -- (9.0,-2.10) |- (c0.west);
  \node[font=\scriptsize, text=acc, rotate=90, anchor=center] at (8.75,-1.05) {addresses};
  % everything feeds the capstone
  \node[mm, minimum width=54mm] (cap) at (6.0,-4.6) {10 capstone:\\the whole machine};
  \draw[->, acc] (a2.south) |- (cap.west);
  \draw[->, acc] (b2.south) -- (cap.north);
  \draw[->, acc] (c3.south) |- (cap.east);
\end{tikzpicture}
$$

Read the map against the grand tour and the two agree: the program strand wrote the
bytes the tour fetched, the processor strand executed them, and the
memory-and-world strand served every address, fault, and interrupt along the way.

## What we simplified

The machine above is real — it is, structurally, the machine in your laptop. But a
modern x86-64 core is bigger than PIPE in three specific ways this course chose to
leave out, and honesty requires naming them. Each gets a paragraph and a pointer;
all three are beyond this course's scope, sketched in CS:APP §5.7.

**Out-of-order execution.** PIPE keeps instructions in program order from fetch to
write-back; one long stall holds up everything behind it. A modern core does not
wait. It **renames** registers to strip false dependences, parks decoded
instructions in an issue queue, and lets each one run the moment _its own_
operands are ready, often with hundreds of instructions in flight, finishing in an
order the program never wrote. A **reorder buffer** then retires results strictly
in program order, so the architectural state advances as if execution had been
sequential and
[exceptions stay precise](/computer-architecture/exceptions-and-io/exceptional-control-flow).
The effect is a machine that extracts the dataflow graph from the instruction
stream at runtime and executes the graph, not the listing.

**Superscalar issue.** PIPE launches at most one instruction per cycle, so
$\text{CPI} \ge 1.0$ always. A superscalar core of width $w$ issues up to $w$
instructions per cycle, admitting $\text{CPI} \ge 1/w$ — with $w = 4$–$8$
instructions dispatched into multiple ALUs, load/store ports, and branch units. The
cost is roughly quadratic growth in the machinery we
built once: forwarding networks between every producer and every consumer, hazard
checks across every pair of in-flight instructions, register files with a dozen
ports instead of
[our three](/computer-architecture/digital-logic/register-files-and-random-access-memory).
Nothing conceptually new appears (the same hazards, the same bypasses), but the
bookkeeping multiplies until it dominates the die.

**Speculation beyond branches.** We speculated on exactly one thing:
[branch direction](/computer-architecture/pipelining/control-hazards-and-branch-prediction),
with a squash-and-refetch when wrong. Real cores speculate wholesale: that a load
will not conflict with an older store whose address is still unknown, that a value
will match last time's, that the next cache lines the program wants are the ones a
prefetcher guesses. Every guess needs recovery machinery when it misses, and the
guesses leave footprints: a speculatively loaded line sits in the cache even after
the speculation is squashed, a residue that timing can observe and that modern
security work spends real effort containing.

None of these change the contract. An out-of-order, superscalar, deeply
speculative core still presents the ISA's illusion: instructions appear to execute
one at a time, in order, exactly as
[SEQ](/computer-architecture/processor-design/assembling-seq) actually does it.
That is why the simple machine is worth building: it is the specification the
complicated one must imitate.

> **Takeaway.** What this course omitted is acceleration, not architecture:
> out-of-order execution reorders work under an in-order facade, superscalar issue
> multiplies the datapath's width, and speculation generalizes the branch
> predictor's bet to loads, values, and prefetches. All of it exists to feed the
> same fetch–decode–execute contract we built — and all of it retires, in the end,
> in program order, pretending to be SEQ.

## Where the three simplifications came from

CS:APP §5.7 sketches the modern out-of-order core; the ideas have names and origins
worth attaching, because each answers a specific question the simple machine leaves
open, and each is still live.

**Out-of-order issue is a 1967 idea.** The mechanism that lets a modern core run
instructions as their operands become ready, rather than in program order, is
**Tomasulo's algorithm**, designed for the IBM System/360 Model 91's floating-point
unit.[^tomasulo] Its two moving parts are the ones the "what we simplified"
section named: reservation stations that hold waiting instructions and a
result-broadcast bus that wakes them, which together perform register renaming
implicitly. Nearly every high-performance core since is a descendant. The **reorder
buffer** that retires results back in program order — keeping exceptions precise — was
added later, and the canonical treatment of precise interrupts on such a machine is
Smith and Pleszkun's.[^precise]

**Branch prediction is what makes deep pipelines viable.** The course predicted branch
_direction_ with a simple scheme; real cores use two-level and correlating predictors
that learn per-branch and per-history patterns, the line of work opened by Smith and
extended by Yeh and Patt.[^branchpred] The deeper the pipeline and the wider the
issue, the more a misprediction costs, so prediction accuracy — now well above 95% on
typical code — is what keeps a fifteen-stage superscalar core from losing most of
its work to squashes.

**Speculation left a security hole.** The lesson noted that a speculatively loaded
cache line "sits in the cache even after the speculation is squashed, a residue that
timing can observe." That residue is not hypothetical: it is the basis of **Spectre**
and **Meltdown** (2018), which trick a core into speculatively accessing data across a
protection boundary and then read the secret back out through cache timing, even
though the architectural state was correctly rolled back.[^spectre] The
architecture/microarchitecture split this course leaned on — the promise that
microarchitecture is invisible to correctness — turned out to leak: the timing of the
implementation is observable in a way the ISA never accounted for. It is a
reminder that "invisible to results" is not the same as "invisible."

> **Beyond-the-book takeaway.** The three accelerations are named, old, and
> consequential: Tomasulo's algorithm (1967) for out-of-order issue with a reorder
> buffer for precise exceptions, decades of branch-prediction research to feed deep
> pipelines, and speculation whose microarchitectural residue became the Spectre/
> Meltdown class of attacks. The simple machine is the specification; the fast
> machine is these ideas layered on it — and the last one shows the layers are not
> perfectly sealed.

[^tomasulo]: **R. M. Tomasulo**, "An Efficient Algorithm for Exploiting Multiple
    Arithmetic Units," _IBM Journal of Research and Development_ 11(1), 1967 — the
    reservation-station and common-data-bus scheme, with implicit register renaming,
    underlying modern out-of-order execution.
[^precise]: **J. E. Smith and A. R. Pleszkun**, "Implementation of Precise Interrupts
    in Pipelined Processors," ISCA 1985 — the reorder buffer and related mechanisms
    that let an out-of-order machine retire in program order and keep exceptions
    precise.
[^branchpred]: **J. E. Smith**, "A Study of Branch Prediction Strategies," ISCA 1981,
    and **T.-Y. Yeh and Y. N. Patt**, "Two-Level Adaptive Training Branch Prediction,"
    MICRO 1991 — foundational dynamic branch-prediction schemes.
[^spectre]: **P. Kocher et al.**, "Spectre Attacks: Exploiting Speculative Execution,"
    IEEE S&P 2019, and **M. Lipp et al.**, "Meltdown: Reading Kernel Memory from User
    Space," USENIX Security 2018 — attacks that read secrets through the cache-timing
    residue of squashed speculative execution.

That is the synthesis in the abstract. The
[final lesson](/computer-architecture/capstone/assembling-a-complete-cpu) makes it
physical: it bolts the named parts (PC, instruction memory, register file, ALU,
data memory, control unit) into one block diagram, powers the machine on from
reset, and runs a complete compiled program (an array sum with a real `call` and
`ret`) across it, cycle by cycle, until the answer lands in a register, and then asks
what it would take to put two of these cores on one die.
