Executing Instructions
Fetch, decode, execute, write back — then the pipeline that overlaps them, the hazards that break the overlap, and the forwarding and stalls that patch it up.
Every introduction to CPUs teaches a tidy four-step cycle. It is genuinely useful and it has not described a shipping processor since the early 1990s. Both halves of that sentence matter: learn the model, then learn precisely which parts of it modern hardware abandoned.
Before a CPU can do anything with an instruction it has to load it from memory, through a cache, at an address it may have had to guess. The front end is a supply chain, and a starved front end leaves the most sophisticated execution engine in the world with nothing to do.
A fetched block is just bytes. Decode is where the CPU works out where one instruction ends and the next begins, what operation is requested, and which registers it touches — and how hard that is depends enormously on the instruction encoding the ISA chose.
The execute phase is where the work happens, in a set of specialised functional units. Two things surprise people: different operations take very different numbers of cycles, and an operation's latency and its throughput are separate numbers that can differ by an order of magnitude.
Almost every ISA makes you bring data into a register before you can compute with it, and write it back explicitly. That looks like bureaucracy until you notice that a load is the one instruction whose cost varies by two orders of magnitude depending on where the data happens to be.
A pipelined CPU does not execute any single instruction more quickly than an unpipelined one. It overlaps them, so instructions complete more often. Understanding that pipelining buys throughput and not latency explains most of what modern CPUs do and why they do it.
Pipelining assumes the next instruction can always start. Three situations break that assumption — a needed value is not ready, the next address is not known, or two instructions want the same hardware — and every hardware performance problem is a variation on one of them.
When one instruction needs another's result, the hardware has two options: route the value directly to where it is needed, or wait. Forwarding covers most cases at no cost. The case it cannot cover — a load feeding the very next instruction — is the shape of every serious memory performance problem.