A course you build as you read

Build a Microprocessor

Start with a light switch and finish with a working CPU that runs programs you write yourself. Each part is built from the parts before it, so nothing has to be taken on trust.

No hardware background needed No maths beyond counting

How to use this page

Read a little, then play with the widget under it. Every box below is real: the adders really add, the flip-flops really remember, and the CPU at the end really executes the machine code that the assembler produces from your typed program. If a widget confuses you, click things until it doesn't. That is exactly how hardware engineers learn too.

Every step

Step 0

What a processor is

Open a laptop, find the biggest black square on the board, and that's it: the processor. Under the lid is a sliver of silicon the size of a fingernail holding tens of billions of microscopic switches. It has no idea what a photo is, what a song is, or what a game is. It knows how to do about a hundred childishly simple things, like "add these two numbers" and "fetch this number from memory."

What silicon has to do with any of this

Silicon is the main ingredient of ordinary sand, purified until a slice of it is very nearly one perfect crystal. It is used because it can be persuaded to carry electricity in some places and block it in others, which is exactly what a switch needs.

A chip is a thin wafer of that crystal with patterns printed into it. Step 2 is where those patterns start to matter.

No single trick explains it. What explains it is speed and obedience: the processor does billions of those tiny steps per second, forever, in exactly the order you specify. Everything else is built out of those steps stacked high: operating systems, browsers, neural networks.

Here is the plan. We build the tower from the bottom up:

The tower we're going to build
Try this firstNothing to click here: this is the map. Each block is built out of the blocks under it, and you build them in this order. Come back and look at it whenever you lose track of where you are.
Click any layer to jump there. Each one is made only from the layers below it. No new ingredients get added along the way.
The idea this all rests on

A computer is not one clever machine. It's a stack of stupid machines, where each level is built out of the level under it and hides that level's messiness. Nobody understands all of it at once. Engineers work one layer at a time. So will we.

"A stack of stupid machines", said another way

Think about reading. Ink makes letters, letters make words, words make sentences, sentences make an argument. While you follow the argument you are not thinking about ink, and you do not have to.

A computer is stacked the same way, and each step here adds one level. When we reach the top the bottom is still down there doing its work. We just stop looking at it.

Before we start: a processor does one simple step, like adding two numbers. How many times per second?
A few billion. A 3 GHz chip gets three billion ticks a second, and a modern core often finishes several instructions per tick. In the time light crosses this room, your processor has done tens of millions of things. It has no cleverness at all, only that. By Step 16 you'll have built a small one and watched it work, one tick at a time, slowly enough to read.
What GHz means

Hertz means "times per second". A kilohertz is a thousand of those, a megahertz a million, a gigahertz a billion. So a 3 GHz chip is being told "now" three billion times a second.

Step 10 builds the wire that does the telling. Until then, treat GHz as nothing more than a count of how often the machine takes its next step.

Step 1

Switches and the two-state wire

Before transistors, before chips: a battery, a bulb, and a switch. Electricity flows in a loop. Break the loop and the bulb goes dark. That's the entire physical basis of computing.

This next step is where computing begins. Stop thinking "the bulb is lit." Start thinking "the wire is carrying a 1." Voltage present = 1 = true = yes. No voltage = 0 = false = no. We just decided that electricity means information. Nothing in physics forced that choice. We made it up, and the whole industry runs on it.

What voltage actually is

Electricity in a wire behaves a little like water in a pipe. Voltage, measured in volts, is the push behind it. Current is how much actually flows. A battery supplies the push, and the wire gives it somewhere to go.

From here on we ask only one question about any wire: is there a push on it or not. That yes or no is the entire information.

Lab 1 · Switches in series and in parallel
Try this firstFlick Switch A and Switch B below, in all four combinations, and find which ones light the bulb. Then press Parallel (side by side) at the top to rewire the same two switches a different way, and try all four again. The answer changes.
Notice: two switches in a row (series) light the bulb only when both are on. Two switches side by side (parallel) light it when either is on. You have just built the logical ideas AND and OR out of nothing but wire.
Series and parallel, in plain words

Two switches are in series when the current has to get past the first one to reach the second, like two doors along a single corridor. Both have to be open for anything to get through.

They are in parallel when the current has a choice of two routes, like two doors into the same room. Either one being open is enough.

That's worth sitting with for a second. Nobody designed "AND" into the universe. It fell out of the shape of the circuit. Wiring switches one way gives you both; wiring them another way gives you either. Arrangement is logic.

Why two levels and not ten

A wire could carry ten different voltages and count from 0 to 9 directly, and a few early machines did work that way. The trouble is noise: every wire picks up small stray voltages from everything around it and the closer the levels sit, the more easily one is mistaken for its neighbour.

With only "clearly on" and "clearly off" there is a wide gap in the middle, so a signal can be knocked about quite badly and still be read correctly. Nothing later depends on this, so skip it happily.

You want a car that starts only when the key is turned AND the brake pedal is pressed. How do you wire the two sensors?
Series. Both switches must close to complete the single loop. Parallel would start the car if either one was active, which is how you get a car that starts by itself when someone leans on the brake.
Step 2

The transistor: a switch with no finger

A hand-operated switch is useless for computing: a computer has to flip its own switches, billions of times a second. So we need a switch that is flipped by electricity instead of by a finger. That device is the transistor, and it is the single most manufactured object in human history.

A transistor has three connections:

  • Gate: the control wire (the "finger")
  • Source and Drain: the two ends of the switch (the "contacts")

Put voltage on the gate and current can flow from source to drain. Remove it and the path closes off. No moving parts, no wear, and it switches in well under a billionth of a second.

A switch with nothing that moves

Picture a garden hose lying across a table with your foot resting on it. Press down and nothing flows. Lift your foot and water runs. Almost nothing moved, and your foot never touched the water.

In a transistor the gate is the foot. Voltage on the gate changes the silicon just underneath it, enough to open or close a path for current, while taking nothing from the current it controls.

How fast is a billionth of a second

A billionth of a second is a nanosecond. Light, the fastest thing there is, crosses about 30 centimetres in one. A transistor can switch several times in that span.

One comparison makes the rest of the course easier: a nanosecond is to a second roughly as a second is to thirty years.

Lab 2 · A transistor is an electric finger
Try this firstClick the gate input on the left to switch it between 0 and 1, and watch whether current gets through to the other side. A transistor is a switch that another wire flicks, instead of your finger.
The trick that matters: the gate is fed by a wire, and the output is a wire. So the output of one transistor can drive the gate of the next. Switches that control switches. That is what makes stacking possible, and stacking is what makes computers.
Scale check

A modern phone chip holds roughly 15-20 billion transistors. If each transistor were a grain of rice, that chip would be a pile weighing about 400 tonnes. The smallest features are only a few dozen atoms wide, and the wires between them a few hundred atoms apart. We won't design at that level, but everything above is built from exactly this one component.

How anyone makes something a few dozen atoms wide

Chips are printed rather than assembled. Light shines through a patterned mask onto a wafer coated in a chemical that changes wherever the light lands, and the exposed parts are then washed away. Repeat with a few dozen masks and the layers of a chip build up.

The whole wafer is patterned at once, however many transistors the pattern contains, which is why the cost of a chip depends on its area rather than on how much is squeezed into it. Nothing later depends on this.

You have a warehouse containing ten billion ordinary light switches and all the wire you could ever want. Why can you still not build a computer out of them?
Nothing can flip them. Speed isn't the blocker. The blocker is authority. A computer has to decide its own next move, which means the output of one decision must be able to operate the next one. A light switch answers only to a finger. A transistor answers to a wire, and that single difference is the whole of the digital revolution.
Step 2a

Floating outputs, and the pair that fixes them

One thing has been quietly missing. A voltage is never an amount on its own: it is always a difference between two points, the way a height is always a height above something. So every circuit picks one point, declares it to be zero, and measures everything else against it. That point is called ground, and the other end of the battery, held at the full supply voltage, is called the supply rail."This wire is carrying a 1" is short for "this wire has been joined to the supply rail", and "carrying a 0" is short for "joined to ground".

Why a difference, and not just an amount

A step is not two metres tall on its own. It is two metres above the floor. If you moved the floor, the number would change and the step would not. Voltage works the same way: a battery does not put 3 volts somewhere, it holds one terminal 3 volts above the other.

That is why every diagram from here on has two horizontal lines across it, one at the top marked +V and one at the bottom marked 0 V. They are the two things every other voltage is measured between, and every gate in this course is wired to both.

Which raises a question the last step stepped over. A single transistor can join the output wire to a rail. What is the output when the channel is closed and it is joined to nothing?

One transistor, and a reading you cannot trust
Try this firstAnswer the question under the switches before you touch anything else: with the gate at 0, what does the meter on the output read? Then press Take a reading four or five times and watch the answer. After that, use the two choosers to put the transistor's far end on the supply rail instead, and try the PMOS.
Notice: there are eight arrangements here and only four of them put a level on the wire at all. An NMOS joined to ground gives a solid 0 and nothing else; a PMOS joined to the rail gives a solid 1 and nothing else. Each type is also poor at the other job, which is why the readout says weak when you ask an NMOS to pull a wire up.
The word for it

A wire joined to neither rail is floating. It is not a 0, and it is not a secret third value the machine can use. It is the absence of an answer and a meter on it reports whatever charge happens to be left over there. Two readings of the same floating wire need not agree. Nothing downstream can be built on it.

So a real circuit has to drive its output both ways: something has to pull it down to make a 0, and something else has to pull it up to make a 1. One transistor cannot do both, because it has one channel and one gate and it either conducts or it does not. Two can, if they are arranged so that exactly one of them is conducting for any input.

Two transistors, so the output is always driven
Try this firstThe bench opens with a build that does not work. Flip Input A and read the table: both rows say weak. Now make transistor 2 an NMOS as well, so both are the same type, and watch the ammeter. Then fix it: transistor 1 a PMOS, transistor 2 an NMOS, with 1 to the rail, 2 to ground.
Notice: three different ways to be wrong, and the table names each one. Two transistors of the same type switch together rather than in opposition, so with one on each rail they are both open for one input and both shut for the other: a short, and then a float. A short is not a wrong answer. It is a wire from the supply straight to ground with current pouring down it. The one arrangement that works has each transistor doing only the job it is built for.
Could you not just use a resistor instead of the second transistor?

You can, and outside a chip people often do: a resistor from the wire up to the supply rail holds it at 1 until a transistor pulls it down. It is called a pull-up, and it is how a push-button gets wired to a real circuit board. Embedded Systems builds one.

Inside a chip it is a bad bargain. While the transistor is pulling down, the resistor is still pulling up, so current flows the whole time the output is 0, and a few billion of those adds up to a hot chip and a flat battery. A second transistor costs almost no area, switches fully off, and draws nothing while it sits there. That is the trade the whole industry made.

Two transistors, and you own your first gate outright. Feed the pair a signal and the output is always the opposite of it, always driven, and always at a full rail voltage. That is a NOT gate, also called an inverter. It is the thing nothing in the first two steps could do: turn a 1 into a 0.

Inverters in a row
Try this firstAnswer the question under the dial, then drag the dial from 0 up to 6 with Input A at 1 and read the run of numbers under the chain.
Notice: the number under each stage is worked out by the same transistor model as the labs above, one pair at a time, and not by counting flips. Each pair drives its own output from the rails, so the signal is rebuilt at full strength at every stage rather than being passed along and worn down. That is the property that lets a chip be billions of gates deep.
Why "complementary", and where the name CMOS comes from

Two things are complementary when each one is exactly what the other is not. The pair here is complementary in that sense. The PMOS conducts when its gate is low and the NMOS when its gate is high, so for any input one is open and the other is shut. There is never a moment when both are.

Almost every chip made today is built this way, and the technology is named after it: CMOS, for complementary metal-oxide-semiconductor. The metal-oxide-semiconductor part is the sandwich from the last step, a metal plate over an insulating oxide over silicon. The C is the pair.

A circuit board has a chip whose output pin is not connected to anything at all. Someone puts a voltmeter on that pin and writes down 0. They come back an hour later, measure the same pin again, and write down 1. Nothing has been changed. What happened?
It is floating. An unconnected wire is not a 0. It sits at whatever voltage the last thing to touch it left behind, and leakage, nearby wires and a fingertip can all move it. That is why the fix is never "assume it is 0": the fix is to drive it, which is what the second transistor in the pair is for.
You wire the pair with both transistors the same type by mistake, one still on the supply rail and one still on ground, and you hold the input at a value where both of them conduct. What does an ammeter on the supply read, and what does the output read?
Current, and no usable output. A path from the supply straight to ground is a short. The chip heats up, the supply voltage sags, and the output sits somewhere in the forbidden band in the middle where no gate downstream will agree with any other about what it says. The complementary pair exists precisely so that this cannot happen: exactly one of the two conducts, for every input.
Step 3

Logic gates

Nobody designs chips transistor-by-transistor any more than a novelist thinks about ink chemistry. We package a few transistors into a gate: a box with input wires, one output wire, and a fixed rule. From here on, transistors disappear from the story. This is our first act of hiding a layer.

"Gate" again? It meant something else a minute ago

It did, and the clash is unfortunate. On a transistor, the gate is the control wire. A logic gate is a different thing: a small circuit of a few transistors, drawn and used as one box, with a fixed rule from its inputs to its output.

From now on the box is what we draw, and the transistors inside it stop being mentioned. That is the bargain at every layer: fewer details, bigger ideas.

There are only a handful of gates worth knowing, and you already invented two of them with light switches.

Lab 3 · Gate playground
Try this firstPress AND, then click the two input circles to try all four combinations of 0 and 1. Watch the output and the table fill in. Then press OR and do the same, and see which rows changed.
Flip the inputs and watch the output. The truth table on the right lists every possible input combination. With only two inputs there are just four, so a truth table is a complete, exhaustive description of what the gate does. Nothing about a gate is left unsaid.
What a truth table is, exactly

A truth table lists every combination the inputs can be in, one per row, and what the output does for each. Two inputs that are each 0 or 1 give four rows, and those four are every case there is.

That makes it the complete definition of a gate. Two circuits with the same truth table behave identically, however differently they are wired inside, and we lean on that fact all the way to Step 15.

Where the gate names come from

AND, OR and NOT mean roughly what the English words mean. An N on the front means "and then flip the answer", so NAND is not-and and NOR is not-or.

XOR is short for "exclusive or", and it means one or the other but not both. That is closer to how people usually use the word "or" in speech than plain OR is, which is why OR surprises beginners more often than XOR does.

A XOR gate outputs 1 when its inputs are different. Which everyday question does XOR answer?
"Do these two disagree?" That's why XOR is the heart of the adder you build in Step 5 (1+1 gives a sum of 0, with a 1 carried into the next column: the inputs agree, so no sum) and why it's used to compare data for equality.
Step 3a

NAND and NOR from four transistors

The last step built a gate with one input. Gates with two inputs are built the same way, and the whole design decision is a choice you already made in Step 1 with a bulb and two switches: series or parallel. Two transistors in series only conduct when both are open. Two in parallel conduct when either one is.

A gate has a pull-down side made of NMOS below the output and a pull-up side made of PMOS above it, and the two sides have to be opposites of each other. Where one needs both of its transistors, the other needs either of its own. Get that wrong and the output floats for some inputs and shorts for others, which is the failure you just met.

Why the pull-up side is opposite, and not the same

Take the two NMOS in series, below the output. They pull the output down only when A and B are both 1. For every other input the output must be pulled up instead, so the pull-up side has to conduct whenever A and B are not both 1, which is to say whenever either of them is 0.

A PMOS conducts when its gate is low. So "either input is 0" is two PMOS in parallel. The two sides say the same thing from opposite directions, and that is what makes exactly one of them conducting for every input.

NAND, NOR and AND, four transistors at a time
Try this firstRound 1 opens already correct, so start by breaking it: press in parallel for the NMOS side and read the table, then put both sides in parallel and watch the current badge. Put it back to series below and parallel above, then move on to round 2 and round 3. The inverter chooser is live in every round, and switching it on in round 1 turns your NAND into a gate the checker will name for you.
Notice: swapping series for parallel on both sides at once does not break the gate, it changes which gate it is. NAND becomes NOR with nothing added and nothing removed. Match the two sides instead of opposing them and the circuit stops being a gate at all: one row floats and another shorts, and the checker names the row rather than the shape.
The price list

NAND costs four transistors. NOR costs four. AND costs six, because the pair is naturally inverting. The cheapest thing four transistors can do is answer "not both", so to get "both" you bolt an inverter on the end and pay for two more. OR costs six for the same reason. NOT costs two.

That price list is the answer to something Step 3 asserted and could not show. A factory would rather be good at manufacturing one kind of gate, and the kind it picks is NAND, because NAND is what the silicon wants to make. The friendly-sounding gates, the ones named after English words, are the expensive ones.

Why not build a three-input gate the same way

You can, and chips do. Three NMOS in series below and three PMOS in parallel above gives a three-input NAND for six transistors, which is cheaper than two two-input NANDs plus an inverter.

It stops paying off quite quickly. Each transistor added to a series stack makes the pull-down weaker and slower, because the current has to get through all of them, so a stack of eight is unusable in practice even though it is correct on paper. Four is a common limit. Nothing later in this course depends on knowing that.

Where the little circle on a gate symbol comes from

Step 3 drew NAND and NOR with a small circle on the nose, and called it the answer being flipped. Now it has a physical cause. The circle marks an inversion, and the inversion is not something added to the gate: it is what a complementary pair does when you drive its gates and read its output.

Which is why the gates without a circle are the ones with more transistors in them. An AND symbol has no circle, and the reason is that a circle has been cancelled by a second one you cannot see, drawn inside the box as two more transistors.

A chip designer needs to compute A AND B, and can use any gates. Their colleague suggests building it as NAND followed by NOT rather than using an AND gate. How many transistors does each version take?
Six either way, because they are the same circuit. Every AND gate on every drawing in this course is a NAND with an inverter after it. That is worth carrying forward. When you count gates later on, a NAND and an AND look equally cheap on paper and are not, so a design that falls out naturally as NANDs is cheaper than the same design drawn with ANDs and ORs.
Someone builds a two-input gate with both NMOS in parallel below the output and both PMOS in parallel above it. What happens when A is 1 and B is 0?
A short. Both sides in parallel means each side conducts if either of its inputs suits it, so for a mixed input both sides conduct together. The two sides being opposites is not a stylistic rule about how to draw gates. It is the thing that stops a gate destroying itself.
Step 3b

From a truth table to a circuit

Here is the part that makes everything after it possible, and it is a recipe rather than an insight. Write down what you want as a truth table. Then: one AND for every row whose output is 1, with a NOT in front of each input that has to be 0 in that row, and finally one OR across all of them. That circuit computes that table. Always. For any table, of any size.

Each of those ANDs is called a term, and a term is 1 for exactly one row of the table and 0 for every other row. Since no two rows can happen at once, at most one term is ever 1, so an OR across the terms is 1 exactly when the table says 1. The whole construction is called a sum of products, because an OR was once written as a sum and an AND as a product.

Why an AND is a "product" and an OR is a "sum"

Treat 0 and 1 as numbers for a moment. Multiply them: the answer is 1 only when both are 1, which is exactly AND. Add them, and cap the answer at 1: the answer is 1 when at least one of them is, which is exactly OR. So an AND behaves like multiplication and an OR like addition, and the older books write them as AB and A + B.

You do not need that notation here, and this course keeps writing AND and OR in words. The name "sum of products" is worth knowing anyway, because every book and every chip designer uses it.

From a row of the table to a term, and back out again
Try this firstPress tab 1 · one row. Click NOT in the palette, then a dashed slot to drop it in, then click an output pin and an input pin to run a wire between them. The target table is on the right and every row is checked as you build. Work through the four tabs in order: the last one cannot be solved by following the recipe, which is the point of it.
Notice: the first two tabs are the same construction with the NOT moved, and the third is both of them with an OR on the end. Nothing was invented along the way. The fourth tab gives you five slots for a table the recipe would spend ten gates on, so the recipe gets you a correct circuit and then you go looking for the waste in it.
What "three-input AND" means on the palette

AND3 is an AND gate with three input pins instead of two, and it outputs 1 only when all three are 1. It is drawn with the same symbol, because the number of inputs is read off the wires rather than the shape. The last step built one out of transistors: three in series below, three in parallel above.

Two two-input ANDs chained together do the same job, so you never strictly need it. It is on the palette because a term over three inputs is one gate rather than two, and the recipe reads more clearly when a term is one gate.

The recipe leans on two rules for moving an inversion around, and both are worth having by name. The first is De Morgan's law: an inversion moves through a gate by swapping AND for OR, or OR for AND, and inverting both of the inputs. NOT(A AND B) is the same as NOT A OR NOT B. The second is double negation: two inversions on one signal cancel. The tempting mistake is to move the inversion and forget to swap the gate, and that gives a circuit that is not the one you started with.

Pushing an inversion about, legally and otherwise
Try this firstClick the outermost bracket in the expression, which selects the whole thing, then press the De Morgan rule. Watch the table stay identical. Then do it again on the NOT that is left, and try the tempting rule at least once to see what it costs.
Notice: the two columns of the table are what the expression said at the start and what it says now, and a legal rewrite never moves a single row. That is the whole test. Anything that changes a row is not a different way of writing the same circuit, it is a different circuit, and the eight rows are how you tell without having to be clever.
Is the recipe ever the best circuit?

Sometimes, and not often. For a table with a single 1 in it the recipe gives exactly one term and there is nothing to save. For most tables there is something to save. Finding the smallest circuit for a given table is a hard problem in general, and the tools chip designers use spend real computer time on it without always finding the best answer either.

Which is why the recipe matters more than the shortcuts. A correct circuit you can always get in a minute beats a small circuit you might get eventually, and every automatic tool starts from the recipe and improves it. Nothing later in this course needs the shortcuts.

A truth table with four inputs has exactly three rows whose output is 1, and in one of those rows all four inputs are 0. Following the recipe exactly, how many NOT gates does that one row's term need?
Four. The rule is per input, not per term and not per row. The term has to be 1 for this row and 0 for all fifteen others, so every input that is 0 in this row gets inverted on its way in. That is also the clue for spotting waste later, because a term with a NOT on everything is often a sign that the table would be better described the other way round.
Someone rewrites NOT(A OR B) as NOT A OR NOT B and says the two are the same because "the NOT just moves inside". How many of the four rows of the table do they get wrong?
Two. And that is exactly why the mistake survives testing: the expression agrees on the all-0 row and the all-1 row, which are the two rows anybody checks first. De Morgan says an inversion moving through a gate has to swap AND for OR as it goes, and the rule bench above will let you make the mistake and then show you the two rows it cost.
Step 3c

Everything from NAND

Step 3 promised that every other gate can be built from NAND gates alone, and asked you to take it. You can now work it out instead, in two moves. The recipe from the last step turns any truth table into a circuit made of nothing but ANDs, ORs and NOTs. So if each of those three can be built out of NANDs, then every truth table there is can be built out of NANDs, and there is nothing left to prove.

All three are short. A NAND with the same wire fed into both of its inputs is a NOT, because the only rows that can now happen are 0,0 and 1,1. A NAND followed by that NOT is an AND. And an OR is De Morgan's law wired up: invert both inputs, then NAND them.

Lab 4 · Everything from NAND
Try this firstSet A to 1 and leave B at 0. All three constructions are on screen at once, built from nothing but NANDs, and each prints the answer it works out. Check one of them by following your two values through its gates yourself, then predict what the third will say before you flip a switch.
Try: set the inputs so the OR construction outputs 1 while the AND construction outputs 0. Notice that the same three NANDs, wired differently, behave completely differently. Wiring is the design.
Getting a NOT gate out of a NAND, slowly

Feed the same wire into both inputs of a NAND. The only rows that can now happen are 0,0 and 1,1. NAND outputs 0 only when both inputs are 1, so 0,0 gives 1 and 1,1 gives 0.

The output is the opposite of the input, which is a NOT gate. Every other construction starts from that: build the pieces you need, then flip whatever came out the wrong way round.

Notice which direction the saving runs in. Built out of transistors, NOT costs two and NAND costs four, so a NOT out of a NAND costs four rather than two and is a waste on its own. What you buy for that waste is a factory that only has to be good at one thing. You also buy a design you can shave afterwards, because a NAND followed by a NAND often collapses into fewer gates than the recipe asked for.

Lab 5 · Logic lab: wire it yourself
Try this firstPress 1 · NOT. The job is written above the board. Click NAND in the palette, then click one of the numbered empty slots to drop a gate into it, then click a pin and another pin to join them with a wire. Undo and Reset are there for when it goes wrong, and Hint nudges you three times before Show me gives it away.
Build each construction yourself and the truth table below tells you whether it works. Any correct wiring is accepted, so there is more than one right answer.
Is NAND the only gate that can do this?

No. NOR can do it too, by the mirror image of the same three constructions, and for the same reason: it is a gate that inverts and it can be made to ignore one of its inputs. A gate with both of those properties is called universal, and NAND and NOR are the only two-input gates that are.

AND is not universal, and neither is OR, because nothing built from them can ever turn a 1 into a 0. That is the wall Step 1 ran into with switches, and it took a second kind of transistor to get over it. Nothing later depends on this.

Lab 6 · Gate scratchpad
Try this firstNothing is marked here. Pick any gate, wire up whatever you like, and watch the table. Somewhere to try an idea before you meet it for real.
No target and no marking here. Place gates, wire them up, and the panel names your circuit when it matches something known. Try to invent XOR without being told how.
What is now settled

Two transistor types make a pair. A pair makes NOT. Two pairs make NAND. NAND plus the recipe makes every truth table there is. From here the transistors really can drop out of the story, and when they come back it will be to answer a question about cost or about time rather than about whether something is possible.

"Truth table" and "term", in one place

A truth table lists every combination the inputs can be in, one row each, and what the output does for each. Two inputs give four rows, three give eight, and n inputs give 2 to the power of n. A table is the complete description of a gate or a circuit: two circuits with the same table behave identically, however differently they are wired inside.

A term is one AND from the recipe, covering one row of the table. A circuit built as an OR across terms is a sum of products. Those three words are the whole vocabulary needed for the rest of this course.

A chip factory can print only NAND gates. A designer hands them a circuit containing one XOR gate. What has to happen, and what does it cost compared with the XOR the designer drew?
Four NANDs. Universality is not a promise that a substitution is free, only that it is always possible. Every gate symbol in this course is a stand-in for some number of NANDs, and the count is a real cost paid in area, in power and in time. The next step is where the time part arrives.
Step 3d

How long a gate takes

A gate does not answer the instant its inputs change. Its output is a small store of charge, and the transistors underneath have to move that charge before the next gate can read it. The wait is called propagation delay, it is measured in picoseconds, and for the gates in this course it runs from about 12 ps for a NOT to about 45 ps for an XOR.

Delays add. If a signal has to pass through four gates to reach the output, the output cannot be right until all four have had their turn, one after another. So the speed of a circuit is set by its critical path: the longest run of gates between anything holding a value and anything else. Not the longest wire. The most gates.

How long is a picosecond

A picosecond is a thousandth of a nanosecond, and a nanosecond is a thousandth of a millionth of a second. Light crosses about a third of a millimetre in one picosecond, which is roughly the width of three human hairs.

Two numbers to hold on to. A 3 GHz clock ticks once every 333 picoseconds, so a gate at 20 ps fits about sixteen deep inside one tick. And a signal crossing a centimetre of wire on a chip takes around 60 ps, which is more than one gate, which is why long wires eventually matter too.

Which route decides the speed
Try this firstLook at the drawing, pick one of the three routes in the question above it, and commit. Every arrival time on the diagram is hidden until you do. Then drag the dial at the bottom from 1 bit up to 8.
Notice: the route with the longest wire on the drawing is the fastest one on the clock, because it passes through one gate. Wire length on a diagram carries no information at all about time: schematics are drawn for legibility, not to scale. Count gates, and add up their delays.
The number the second half of this course runs on

Every timing question from here on is the same question: how long is the critical path. A clock that ticks faster than the critical path asks a circuit for an answer it has not finished working out. What it gets back is not a slightly wrong answer. It is whatever the wires happened to be carrying at that instant.

Which brings the awkward part. While a circuit is settling, its wires carry values that are simply wrong: some signals have arrived and others have not, so the gates in the middle are working on a mixture of old and new. An output can move to a value it is not going to keep, and then move again. That is a glitch. It is not a fault to be fixed. It is what settling looks like.

The instant the answer becomes true
Try this firstCommit to an answer in the question above, and the waveforms appear. Then drag the cursor slowly from 0 ps to the right and read the OUT row: it goes up, and then it comes back down again, and the circuit's answer never changed at all.
Notice: the shaded window is the glitch, and the two inverters are the cause. Both inputs changed at the same instant, but NOT B is 12 ps behind B, so for 12 ps the circuit believes A is already 1 while B is still 0, which is a row where XOR really is 1. It answers that question instead, briefly, and then corrects itself.
Why not just look at the output when it is right

Because nothing in the circuit knows when that is. A gate has no way to tell a settled input from an unsettled one: it sees a voltage and it responds. If a glitch reaches something that remembers, the wrong value gets stored and no amount of settling afterwards will take it back out.

So the answer is to wait, on purpose, for longer than the critical path, every time, whether the circuit needed it or not. Step 10 gives that waiting a name and a wire, and Step 17 is where a design is chopped up so that the waiting can be made shorter.

Where a gate's delay number comes from

It is measured, not derived. A factory characterises each gate in its library over temperature, supply voltage and how much load is hung on the output, and ships a table. The numbers in this course are one plausible set, chosen so that the arithmetic the page does out loud is the arithmetic the model does. NAND is 18 and NOT is 12, so AND is 30, because an AND really is a NAND with a NOT after it.

Real tables are messier. A gate is slower when it is driving more gates, slower when the chip is hot, and slower when the supply voltage sags, and a design has to work at the worst combination of all three. Nothing later depends on this.

Two circuits compute the same table. The first is 3 gates deep and uses 40 gates in total. The second is 8 gates deep and uses 12 gates in total. Which one can be clocked faster, and which one is cheaper to make?
Depth is time, count is cost. You will meet this trade again in Step 5, where a wider adder is a longer carry chain, and in Step 17, where a deep circuit is cut into stages so the clock only has to outlast the longest piece. Two circuits with the same truth table can differ by a factor of ten in both directions at once.
A circuit's critical path is 200 ps. Someone clocks it at one tick every 150 ps and reports that it "mostly works". What is happening on the ticks where it does not?
It stores the mid-settle value."Mostly works" is the worst possible symptom, because the failures depend on which inputs arrive, so the circuit passes every test you happen to write and fails in the field. The critical path is the slowest route over all inputs, and that is the number the clock has to respect, not the route the case in front of you happened to take.
Step 4

Counting in binary

Our wires only hold 0 or 1. To handle numbers bigger than one, we use more wires, the same way you handle numbers bigger than nine by using more digits.

One wire's answer, a single 0 or 1, has a name: it is a bit. The word is short for binary digit, and a bit is the smallest piece of information there is, because nothing is smaller than a plain yes or no. Eight wires side by side carry eight bits, and from here on we count bits the way you count digits.

In the decimal number 407, each position is worth ten times the one to its right: 4 hundreds, 0 tens, 7 ones. Binary works identically, except each position is worth two times the one to its right: 128, 64, 32, 16, 8, 4, 2, 1. There is no deep difference. Ten fingers gave us base 10; two voltage levels give us base 2.

Reading a binary number by hand

Write the column values above the bits, biggest on the left: 128, 64, 32, 16, 8, 4, 2, 1. Then add up the columns that have a 1 underneath. So 00001011 is 8 + 2 + 1 = 11.

Going the other way, take the biggest column value that fits and keep going with what is left. For 100: 64 fits, leaving 36; 32 fits, leaving 4; 4 fits, leaving 0. Bits on for 64, 32 and 4 gives 01100100.

Lab 7 · Bit flipper
Try this firstClick the eight bits to turn them on and off, and watch the number at the end change. Turn on only the rightmost bit, then only the one next to it, and compare what each is worth.
Try: make 100. Then notice that turning on one more bit on the left doubles the value, and that all-ones is always one less than the value of the next column along: 1111 is 15, and the next column is worth 16. Eight bits (a byte) covers 0 to 255.

Hexadecimal: binary for humans

0110110110110101 is unreadable and easy to miscopy. So engineers chunk bits into groups of four and give each group a single symbol: 0-9 then A-F. That's hex. One hex digit = exactly 4 bits, which is why you'll see hex everywhere in this page: 0x6DB5 is the same 16 bits, but you can actually read it aloud.

Why sixteen symbols, and why letters

Four bits have sixteen patterns, 0000 up to 1111, which is 0 to 15. Ten of those already have digits, so six more symbols were needed and the alphabet was to hand: A is 10, B is 11, and so on to F for 15.

The 0x in front is only a label meaning "read what follows as hex", so that 0x20 is not mistaken for twenty.

Lab 8 · Conversion drill
Try this firstRead the question, type your answer in the box, and press Check. Get it wrong and it shows you the working. Press New question for another. Do about ten.
Get five in a row and you're fluent enough for everything that follows. Real engineers don't compute these in their head either. They recognise the common ones by sight.
You're a lookout on a hilltop in 1805 with five torches. Each one is either lit or unlit, and your friend on the next hill can see them all. How many different messages can you send?
32. Each torch doubles the number of possible arrangements rather than adding one: 2 × 2 × 2 × 2 × 2 = 25 = 32, counting "all five dark" as a message. Those torches are bits, that hilltop is a bus, and this is the only arithmetic behind every "how much can this hold?" question in computing: 8 bits gives 256, 16 gives 65,536.
What that little raised 5 means

25 is five 2s multiplied together: 2 × 2 × 2 × 2 × 2 = 32. The small raised number counts how many 2s there are, and says nothing about what to multiply by.

These are the powers of two, and they are worth half-knowing by sight: 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024. They come up constantly, because each extra bit doubles how many different things a group of wires can say.

A game shows your gold as 0x1F. You pick up one more coin. What does it show?
0x20. Hex digits run out at F, not at 9. F is fifteen, so 0x1F is 31, and one more is 32, written 0x20. There is no such digit as G. If you have ever seen a game cap an item count at 255 or 65,535, you have watched a hex number run out of room.
Step 5

Building an adder

Time to make the machine actually do arithmetic. Add two bits and there are exactly four cases:

0 + 0 = 0
0 + 1 = 1
1 + 0 = 1
1 + 1 = 2  ← doesn't fit in one bit! Write 0, carry 1.

Look at the two output columns. The sum bit is 1 when the inputs differ, which is XOR. The carry bit is 1 only when both are 1, which is AND. Two gates, and we can add. This is called a half adder.

Carrying, the way you already do it

Adding 7 + 5 on paper, you write 2 and carry 1, because 12 will not fit in a single digit column. Binary uses the same rule with a smaller column: 1 + 1 is 2, which will not fit in one bit, so you write 0 and carry 1.

The adder's two output wires are exactly the two things you write down: the digit that stays in this column, and the one that moves to the left.

Lab 9 · Half adder
Try this firstClick the two inputs A and B to try all four combinations. Two outputs come out: sum and carry. Watch what happens on the one row where both inputs are 1.
Read the outputs as a 2-bit number (Carry then Sum): 0+0 = 00 = 0, 1+0 = 01 = 1, 1+1 = 10 = 2. Two gates, doing arithmetic.

The full adder

The half adder has a flaw: to add multi-bit numbers, each column must also accept a carry coming in from the column to its right. So we need three inputs: A, B, and Carry-in. Chain two half adders and OR their carries, and you have a full adder, the workhorse of all computer arithmetic.

Why three inputs and not two

Do a column addition on paper and look at any column except the rightmost. It has three things coming into it: the two digits, plus whatever was carried in from the right.

A half adder can only cope with two, so it can only ever do the rightmost column. A full adder handles the general case, which is why the machine we chain together in a moment is built from full adders.

Lab 10 · Full adder
Try this firstSame as before, but now there are three inputs, because a real addition has a carry coming in from the column to its right. Click all three and find the two rows where the carry goes out.
Try 1+1+1: sum 1, carry 1, which reads as binary 11 = 3. Correct. The OR at the end is right because a carry can never be generated by both half adders at once.

Chaining them: the ripple-carry adder

Now the payoff. Line up eight full adders, one per bit column, and wire each carry-out into the next adder's carry-in. That's a machine that adds two 8-bit numbers, exactly like the column addition you learned in primary school, done in wire.

Lab 11 · 8-bit ripple-carry adder
Try this firstPress Ripple the carry and watch the answer appear one bit at a time, right to left. It is deliberately slow. Notice that each column has to wait for the one before it to finish.
Press "Ripple" and watch the carry crawl left. This is the first time timing appears in our story: bit 7 cannot be correct until the carry has travelled through all seven adders below it. That delay is a hard limit on how fast the chip can be clocked, and defeating it is a whole branch of chip design (look up "carry-lookahead adder" when you're curious).
Where "ripple" comes from, and how it gets beaten

The carry has to travel one column at a time, like a rumour going down a line of people, so the leftmost answer is not trustworthy until the message has reached the end. Eight columns means eight delays stacked up.

A carry-lookahead adder works out separately, and all at once, whether each column will create a carry or merely pass one along, then computes every carry in one go. It costs many more gates and is much faster. Nothing later in the course needs it.

Your 8-bit adder computes 200 + 100. What comes out?
44, plus a carry-out. 300 needs 9 bits; only 8 come out, so the ninth bit is pushed out of the row and into a flag called carry-out, and what is left reads 300 − 256 = 44. Keep that name: engineers save the word overflow for the signed version of the same accident, which you meet in Step 6. The hardware can't fix either one, so it raises a flag and lets the program decide what to do. This is the same class of bug that has crashed real rockets.
Step 6

Negative numbers (and why subtraction is free)

We can add. Can we subtract? Building a whole second machine for subtraction would be wasteful, and it turns out to be unnecessary, because of a trick called two's complement.

Think of a 3-digit odometer. Roll it back one click from 000 and you get 999. So on that odometer, 999 behaves like −1: add 1 to it and you're back at 000. Binary works the same way. In 8 bits, 11111111 behaves exactly like −1, because adding 1 rolls it over to 00000000.

The odometer, done with real bits

Start at 00000000 and roll back one click. Every wheel turns over and you land on 11111111. Add 1 to that and the carries run all the way off the left-hand end, leaving 00000000 again.

So in every sum, 11111111 and −1 do the same thing. The hardware never decides which of the two it "really" is. That reading is ours to choose.

The rule: to negate a number, flip every bit and add 1. Then throw your subtractor away: A − B is just A + (−B), computed by the adder you already built.

"Flip every bit and add 1", worked through once

Take 5 in eight bits: 00000101. Flip every bit and you get 11111010. Add 1 and you get 11111011. That pattern is −5.

Check it by adding 5 back: 11111011 + 00000101 rolls over to 00000000, with the last carry falling off the end. Which is what −5 + 5 ought to give.

Lab 12 · Two's complement explorer
Try this firstStart with a positive number, then press Flip bits only, then Add 1, and watch the value at the bottom. Those two steps together are what Negate does, and they are how a computer makes a number negative.
Notice: the top bit doubles as the sign, so 1 means negative. And the range is lopsided: −128 to +127, because zero uses up one of the "positive" slots. That asymmetry bites for real: negating the most negative number gives you back the same number, because its positive twin doesn't exist. Press Negate on −128 and watch nothing happen. In 32-bit code the same trap sits at −2,147,483,648.
Why this matters

Two's complement is a design decision that removes hardware. One adder now handles addition, subtraction, comparison, and negation. Great engineering usually looks like this: not a cleverer machine, but a representation that makes the machine unnecessary.

Signed and unsigned: the same bits, two answers

11111011 is 251 if you read it as a plain positive number, and −5 if you read it as two's complement. Both readings are correct. The bits carry no hint about which was meant.

That is why programming languages make you say which kind you want, and why mixing the two up produces bugs the machine cannot notice on your behalf.

Lab 13 · Number scratchpad
Try this firstNothing is marked here. Type a number in any of the boxes and the others update to show the same value written differently. Try 255, then 256, then -1.
How to read this. The row of squares is one 8-bit number, and clicking a square flips that bit. The four boxes under it are the same number written four ways at once, so changing any one of them changes the rest. Put a second number in B, then press an operation.

Two of the buttons use an idea we have not built yet. << 1 slides every bit one place to the left, which doubles the number, the same way sliding a decimal number left multiplies it by ten. >> 1 slides them one place right, which halves it. Sliding right leaves a gap at the top, and the two RIGHT SHIFT buttons decide what gets fed into it: a copy of the sign bit, so a negative number stays negative, or a plain 0.

FLAGS are four one-bit answers the machine keeps about the result it has just produced. ZERO means the answer was 0. NEGATIVE means the top bit came out 1. CARRY-OUT means the unsigned answer needed one more bit than there was room for. OVERFLOW means the signed answer went out of range, the way 127 + 1 does in a signed byte, which is the first of the presets above. Step 8 is where flags start doing real work. Here they are something to watch.
A game stores your score in one signed byte. You're on 127 and you score one more point. What does the scoreboard read?
−128. 127 is 01111111. Add one and it becomes 10000000, and in two's complement a leading 1 means negative, so the score reads −128. Nothing broke and nothing warned you: the adder did exactly its job, and the interpretation fell off a cliff. This is the shape of a whole family of real bugs, where a health bar, an altitude or a follower count suddenly goes hugely negative.
In the widget above, put in −128, then press Negate. You get −128 back. Is the widget broken?
It's correct, and that's the unsettling part. Eight bits reach from −128 to +127, so −128 is the one number whose positive twin is missing. Flip-the-bits-and-add-one lands right back where it started. Every fixed-width signed type has this one damaged number at its floor; in 32-bit code it is −2,147,483,648.
Step 7

Compute everything, then pick one

A program is a list of orders for the machine, and one of the orders people write most often is "if this is true then do that, otherwise do the other thing". Hardware has no way of doing that. Every circuit on the chip is powered all the time and is computing all the time. A wire cannot decide to sit this one out. So how does a processor ever make a choice?

It cheats: it computes everything at once and then picks one answer. The picker is called a multiplexer, or mux: a data selector with several inputs, one output, and control wires that say which input wins. It's the traffic switch of the whole chip.

A multiplexer as a railway junction

Several tracks come in, one track leaves, and a lever decides which incoming train carries on. The trains on the other tracks are real and still moving. They simply do not get the junction.

Swap the trains for numbers and the lever for a couple of control wires, and that is a multiplexer.

Lab 14 · Multiplexer
Try this firstPress 2-to-1 mux, then click the select wire to switch it between 0 and 1, and watch which input reaches the output. A mux is a points lever on a railway: one control decides which track the data takes.
Notice: the unselected inputs are still there, still valid, still burning power. They are just ignored. With 2 select bits you can choose between 4 inputs; with n select bits, 2n inputs.
Why n select wires choose between 2n inputs

Each select wire is a yes or a no, so two wires have four combinations: 00, 01, 10, 11. One combination per input. Three wires give eight combinations, and so on.

It is the torch counting from Step 4 doing a job. A group of wires can name one thing out of two to the power of however many wires there are.

Remember this shape

In the picture above, a mux is the tall wedge marked MUX. A diagram of a whole chip has no room for that shape. There it shrinks to a small narrow box wedged between the bigger blocks, with several wires arriving and one leaving. You will find three of them in the finished CPU, and each one is a place where the machine makes a decision: which number to feed the ALU, where to take the result from, which register to write. Muxes are where a processor's choices live.

A vending machine has 8 slots. Press a button and a mux routes power to exactly one of them. How many select wires does that mux need?
3. Three wires make 23 = 8 patterns, one per slot. This is the torch problem from Step 4 in a new form. Running one wire per slot would work too, but it wastes wires badly, and wires are the expensive part of a chip. This is exactly why our CPU needs 3 bits to name one of 8 registers.
What is a register?

A register is a small box inside the processor that holds one number, ready to be used right now. Picture the two or three sheets of scrap paper on the desk in front of you: not much room, but everything on them is within arm's reach. Our processor will have eight of these boxes, named R0 to R7.

We build one properly in Step 11, out of the remembering circuit from Step 9. Until then, whenever a sentence says "register", read it as "one of the eight little boxes the processor keeps its working numbers in".

The same idea run backwards

A mux takes many inputs and passes one along. Run the idea the other way and you get a demultiplexer: one input, many outputs, and control wires deciding which output the value reaches. The vending machine in the question above really wants one of those.

Both are the same thought about naming one thing out of many, so you can leave the second name behind safely. The processor we build uses only muxes.

Step 8

The ALU

Now we can build the first component a computer engineer would recognise by name. The Arithmetic Logic Unit takes two numbers and an operation code, and produces a result. Internally it's exactly the trick from Step 7: run an adder, an AND, an OR, an XOR, and a comparator all at the same time, then let a mux pick the one the op-code asked for.

AND took one bit on each input. How can it take two whole numbers?

It does not, quite. An 8-bit AND is eight separate AND gates standing side by side, one for each column. The first gate is fed bit 0 of A and bit 0 of B, the second gets bit 1 of each, and so on up the row. Every gate answers only for its own column, and nothing travels sideways.

That is the same widening trick as Step 5, where eight full adders side by side made an 8-bit adder. The one difference is that adding has to pass a carry along the row, and AND, OR and XOR do not, which is why they are the quickest things in the ALU.

What an op-code is

A number that names an operation. Say 0 means add, 1 means subtract, 2 means AND, and now a few wires carrying a number are enough to tell the ALU which job you want done.

Which numbers mean what is decided by whoever designs the machine. Step 12 is where we choose ours and write the list down properly.

It also reports flags, little status bits describing the result. Zero is the important one. Every time a program asks "are these two numbers the same?", the machine answers by subtracting one from the other and looking at the Zero flag. A 1 there means they matched.

How subtracting answers "are these equal?"

To ask whether 42 and 42 are equal, the machine subtracts one from the other and looks at the answer. 42 − 42 is 0, the Zero flag comes on, and the answer is yes.

Try 42 − 41. The result is 1, the Zero flag stays off, and the two were not equal. One subtraction settles every equality question the machine will ever be asked.

Lab 15 · Build and drive an 8-bit ALU
Try this firstSet the two input numbers, then press ADD. Now press SUB without changing the inputs, and then AND. Same inputs, same box, different answer, because one control wire changed.
Try: set A = B and choose SUB, and the Zero flag lights up. That single wire is how every if statement you've ever written eventually gets decided. Then try SLT (set-if-less-than), which outputs 1 or 0. That's < in hardware.
The ALU computes AND, OR, XOR and the sum simultaneously, then discards all but one. Isn't that wasteful?
It's a deliberate trade. Deciding first and then computing would take two steps; computing everything and selecting takes one. Hardware is rich in space and poor in time, so it spends transistors to buy nanoseconds. (Power is a real cost, which is why modern chips add "clock gating" to switch off blocks that aren't needed.)
The other flags a real ALU reports

Alongside Zero, most processors keep Carry (the answer needed one more bit than there was room for), Negative (the top bit came out 1) and Overflow (the signed answer went out of range, like the 127 + 1 from Step 6).

Programs read them to catch exactly the accidents Step 6 showed. TOY-16 only needs Zero, so treat the other three as interest rather than homework.

Step 9

Memory: making a circuit remember

Everything so far is combinational: outputs depend only on the inputs right now. Remove the inputs and the answer evaporates. But a computer must hold values while it works on them: a calculator that forgot the first number would be useless.

What "combinational" means, and why it needs a name

A combinational circuit is one whose output depends only on what its inputs are doing right now. Every gate, adder and mux so far has been one. Take the inputs away and nothing is left behind.

The word for the other sort, where the answer also depends on what happened earlier, is sequential. This step is where our machine grows its first sequential part.

The trick is almost absurd. Take the output of a gate and feed it back into its own input. The circuit now has a history: it can sit in one of two stable states forever, holding a bit. A circuit with exactly two states it can rest in is called bistable:"bi" for two,"stable" for sits still. The plainest one there is has a name of its own, the SR latch, after its two inputs SET and RESET.

Why a loop of gates can hold a value

Take two NOT gates in a ring. If the first outputs 1, the second outputs 0, and that 0 goes back into the first, which keeps it outputting 1. The ring agrees with itself, so it sits still.

Start it the other way round and it agrees with itself just as contentedly at 0. Two arrangements that both hold, which is one bit remembered, with nothing anywhere that "saves" anything.

Lab 16 · The bistable loop (SR latch)
Try this firstClick Set and watch the output go to 1. Now let go: it stays at 1. Click Reset and it goes to 0 and stays there. This is the first circuit in the whole course that remembers anything.
Try: press SET, then release it. The output stays at 1. That's a stored bit. Now press RESET and release, and it stays at 0. The circuit remembers which button you pressed last, with no battery and nothing that "saves" anything. Feedback is memory.
The catch

Press SET and RESET together and the latch goes into a nonsense state: both outputs equal, and when you release, it settles unpredictably. Circuits that can enter unpredictable states are unacceptable in a processor, which is why the raw SR latch is never used directly. The fix is the next widget.

The D flip-flop: memory you can trust

We want a box with one data input, one output, and one rule: copy D to Q only at the instant the clock ticks; ignore D completely the rest of the time. That's a D flip-flop. It is the single most important component in synchronous digital design. Almost everything in the rest of this course that remembers a value is built from these.

SET, RESET, D and Q: what the letters stand for

SET means "make it 1" and RESET means "make it 0". D is the data input, the value waiting to go in, and Q is the output, the value being held at the moment.

Q is the conventional label for the stored output; it is not an abbreviation used in the circuit. "Synchronous" in the paragraph above means the state changes on the shared clock edge.

Lab 17 · D flip-flop
Try this firstChange the D input and notice that the output does not move. Now press Clock tick. It moves. This one only listens at the moment of the tick, which is what makes a whole machine able to march in step.
How to read the picture. That is a timing diagram, a chart of what some wires did over time. Time runs left to right. Each of the three rows is one wire: D on top, the clock in the middle, Q at the bottom. A line drawn high means that wire is at 1, a line drawn low means 0, and a vertical jump is the moment it changed. The dashed lines mark the clock's rising edges, the instants when the clock goes from 0 up to 1, and those are the only moments Q is allowed to move.

Try: wiggle D as much as you like and Q won't move. Now hit the clock, and Q snaps to whatever D was at that moment. Then read the bottom row from the left: Q is a copy of whatever D happened to be at each dashed line, and nothing else. That discipline ("change only on the edge") is what keeps a billion-transistor chip from descending into chaos.
Feedback made memory possible. So take one NOT gate and wire its output straight back to its own input. What does it do?
It oscillates: a 0 makes it output 1, which makes it output 0, for as long as the power is on. That is a radio transmitter, not a memory. The fix is the reason the latch above uses two gates: go around an even number of inversions and the loop agrees with itself and sits still. Odd number, oscillator; even number, memory.
Step 10

The clock

A processor's clock is a wire that alternates 0,1,0,1,… forever, driven by a quartz crystal. It carries no data. Its only job is to tell every flip-flop on the chip "now".

This turns a tangle of gates with messy, unequal delays into a machine that advances in clean discrete steps. Between two ticks, signals scramble through the logic and settle down; on the tick, the flip-flops capture whatever has settled. The rule that sets the speed limit for the whole chip:

Ticks per second, and how long one tick lasts

Frequency is how many ticks happen per second. The period is how long one tick lasts, and each is one divided by the other. At 3 GHz there are three billion ticks a second, so one tick lasts a three-billionth of a second, which works out at about 333 picoseconds.

A picosecond is a thousandth of a nanosecond. Light gets about a third of a millimetre in one.

The fundamental constraint

The clock period must be longer than the slowest path between any two flip-flops. One sluggish circuit anywhere on the chip slows down everything. This single sentence explains most of what chip designers spend their careers doing.

Lab 18 · Clock and the critical path
Try this firstPress ▶ Start clock. Now drag the Clock period slider to the left, towards 40 ps. The period is how long one tick lasts, so a shorter period means a faster clock. Keep going and there is a point past which the counter's carry chain has not finished before the next tick arrives. Then widen the counter and watch its calculated critical path grow.
The stepped line at the top is the clock drawn the same way as in Step 9: high is 1, low is 0, and time runs left to right. The green or red bar underneath it is how much of one tick the logic needs before its answer is trustworthy.

Try: push the clock faster than the logic can settle and watch the register capture a mixture of arrived and still-old bits. Real chips can also enter a temporarily undecided, metastable state. This is the same timing mistake that makes an overclocked PC crash. Run in reverse, it also explains why “undervolting” is safest at lower speeds.
How a lump of quartz keeps time

A slice of quartz cut to a particular size flexes at one particular rate, the way a tuning fork sounds one note. Squeeze quartz and it produces a small voltage; apply a voltage and it flexes. Wire those two facts into a loop and it hums at its own rate, millions of times a second, and drifts very little.

The same component keeps a wristwatch honest. On a chip, its steady hum is multiplied up to the gigahertz the processor runs at.

Two chips run the same program. Chip A: 3 GHz clock, slowest logic path 300 ps. Chip B: 2 GHz clock, slowest logic path 200 ps. Which one is closer to producing garbage?
Chip A, and it's close. 3 GHz means a period of 1/3 ns ≈ 333 ps, and the logic needs 300, leaving 33 ps of slack. Chip B has 500 ps to play with and needs 200, so it has 300 ps of slack. The faster chip is the more fragile one. This is the trade every chip company lives inside: raise the clock and you eat slack, until one hot afternoon a laptop starts crashing.
Why the clock is the most awkward wire on a chip

Every flip-flop needs the tick, so the clock has to reach millions of places, and it has to arrive at all of them within a whisker of the same moment. Arriving late in one corner is called clock skew. It eats into the same slack the question above worked out.

Designers grow a carefully balanced tree of clock wires so every branch is about the same length. Nothing later depends on this.

Step 11

Registers and the register file

One flip-flop stores one bit. Line up 16 side by side, wired to the same clock, and you have a 16-bit register, a box holding one number, updated all at once.

A CPU needs a small pile of these for scratch work. Ours will have eight registers named R0-R7. They're the fastest storage in the machine: no address decoding, no waiting, right next to the ALU. This is what people mean when they say a variable "lives in a register".

The pile is packaged as a register file with a very specific set of ports, chosen to match what one instruction needs: read two values, write one value, all in the same clock cycle.

What a "port" is

A port is a bundle of wires with one job, together with the wires that say which register it should be looking at. A read port carries a register number in and that register's value out.

Two read ports means two of those bundles side by side, so two different registers can be looked at in the same instant. It is not one bundle being used twice in quick succession.

Lab 19 · Register file (2 read ports, 1 write port)
Try this firstChoose a register to write to, set a value, and press Clock tick. Then read two registers at once using the two read selectors. Two things can be read at the same moment, and only one written.
Notice: reading is instant and needs no clock: the selected register's wires are just steered to the output by a mux. Writing needs both a clock tick and the switch marked RegWrite to be on. RegWrite is the enable signal that decides whether the selected register changes on the clock edge. Reads remain combinational in this design.

Main memory (RAM)

Eight registers isn't much. Bulk storage lives in RAM: a big array of cells. An array is a row of boxes all the same size, numbered from 0 upwards, so you can name any one of them by giving its number. Here you supply that number, called the address, and get back the data stored there. RAM is slower than a register (further away, and it needs address decoding), which is precisely why registers exist.

What "address decoding" means

A memory with 1024 cells has to turn a 10-bit address into "wake up exactly one of these 1024 cells, and leave the other 1023 alone". The circuit that does it is a tree of gates called a decoder, and like all logic it takes time to settle.

The register file barely does any of this, because it is choosing between eight boxes rather than thousands. Small and near is the whole of its advantage.

Lab 20 · Addressable memory
Try this firstDrag the address slider to pick a slot, set a value, and press Write. Move the slider somewhere else, then come back and press Read. The value is still there.
Memory is just a very long street of houses. The address is the house number; the data is what's inside. Load = go read that house. Store = go write to it.
Registers and RAM, as a desk and a filing cabinet

Registers are the few sheets of paper on the desk in front of you. One glance and they are in your hands. RAM is the filing cabinet across the room: far more room in it, but you have to stand up, find the right drawer, and walk back.

Nobody works out of the cabinet directly. You fetch what you need onto the desk, do the work there, and put the results back. Load and store instructions are the walking.

Why does a register file have two read ports rather than one?
Two operands. ADD R1, R2, R3 must fetch R2 and R3 in the same cycle. The hardware's shape is dictated by the instruction set's shape. Design decisions at the top ripple all the way down to the wires.
Step 12

Designing an instruction set

We have parts. Now we need a plan: what should this machine be able to be told to do? The list of commands a processor understands, and their exact binary encoding, is the instruction set architecture (ISA). It's the contract between hardware and software: the only thing programmers see, and the thing the hardware must implement exactly.

We'll design a small RISC machine. RISC means Reduced Instruction Set Computer: few instructions, all the same size, all simple, memory touched only by dedicated load and store instructions. It's the philosophy behind MIPS, ARM and RISC-V, which is to say behind essentially every phone and, increasingly, every laptop on Earth.

Reduced from what, exactly?

Before RISC, the fashion ran the other way: hundreds of instructions, of different lengths, some of them doing quite elaborate jobs in a single go. That style got the name CISC afterwards, for Complex Instruction Set Computer, and the x86 chips in most desktops are its descendants.

Fewer, simpler, same-sized instructions turned out to be easier to make fast, and modern x86 chips quietly chop their complicated instructions into simple ones inside. Safe to skip.

The complete instruction set

Fifteen instructions. That's the entire vocabulary of the machine, and it is enough to compute anything computable, given enough memory and patience.

"Anything computable" sounds like a very big claim

It is, and it holds. In the 1930s Alan Turing worked out what the smallest possible complete machine has to be able to do and the list is startlingly short: add, compare two things, read and write memory, and change what it does next based on a comparison. TOY-16 can do all four, so anything any computer anywhere can work out, TOY-16 can work out too.

Slowly, and only if you hand it enough memory. Speed and room are what separate it from the chip in your phone, not ability. Nothing later in the course depends on this, so it is safe to enjoy and move on.

Lab 21 · The instruction set (click a row)
Try this firstClick any row in the table. The panel below shows what that instruction does, in ordinary words and as the bit pattern the machine actually sees. Start with ADD, then LW, then BEQ.
Notice what the bit pattern is doing. The first four bits say which instruction it is, and the rest are split into fields whose meaning depends on that. That is why the same 16 bits can mean "add these two registers" or "jump somewhere": nothing in the bits themselves says which, only the agreement you are reading in this table. The next step is about how those fields are laid out.
I have not seen != before

!= is read "is not equal to". It is the ≠ sign from maths, typed on a keyboard that has no ≠ key, so an exclamation mark stands in for "not". Rs != Rt means "Rs and Rt are holding different numbers".

Its partner == means "is equal to". It is doubled up so it cannot be confused with a single =, which in most programming languages means "put this value in here" rather than "these two are the same". Both turn up in the table above: BEQ jumps when two registers are equal, BNE jumps when they are not.

Rs, Rt, Rd: which register is which?

Rs and Rt are the two registers an instruction reads. The s is for source; the t is just the letter after it, for the second one. Rd is the register it writes, d for destination. So ADD Rd, Rs, Rt reads two registers and writes a third.

Some instructions have no room for three register fields. ADDI Rt, Rs, imm spends part of the word on the constant, so it has only two, and the write goes to Rt instead. The rule that never changes is the order on the line: whatever an instruction writes to is named first.

Read the list again and notice what is not in it: there is no multiply. You need 7 × 6. What do you do?
Add it up six times. Multiplying is repeated adding, so a short loop does it with instructions this machine already has: keep a total in one register, keep a countdown in another, add and branch until the countdown reaches zero. It is slower than a multiply circuit, and that is the whole difference. Every instruction a designer leaves out is work handed back to the programmer, and every instruction they put in is silicon that has to be paid for, powered and tested. Real chips argue about this line for years.

That is what the machine can be told to do. Next comes the harder half of the contract: turning each of these lines into a number the hardware can actually read.

Step 13

Packing an instruction into sixteen bits

One more piece of vocabulary first. A word is what a machine calls its natural bite of bits, and every machine picks its own size. TOY-16's word is sixteen bits, which is why a register holds sixteen bits, one slot of memory holds sixteen bits and one instruction is exactly one word long.

Meet TOY-16

Our CPU: 16-bit words, 8 registers, 16-bit instructions. Every instruction is exactly one word. The top 4 bits are the opcode (which operation), and the remaining 12 bits are laid out in one of three formats:

Lab 22 · Instruction formats
Try this firstNothing to click: this is the shape of an instruction, drawn to scale. Every instruction in this machine is 16 bits laid out in one of these patterns. Come back to it when a bit pattern stops making sense.
Only three shapes, and the register fields sit in the same bit positions in all of them. That's not an accident. It means the hardware can start reading registers before it has even finished working out what the instruction is. Regular encodings make fast chips.
What a "field" inside an instruction is

A field is a fixed group of bits inside the instruction word with a fixed meaning: these three bits name the destination register, those six carry a constant, and so on. One word, chopped into labelled pieces.

Because the pieces sit in the same places in every format, the wires that read a field can be attached in one position and never move. That is what the caption above is getting at.

Assemble one by hand

Machine code is just numbers. Build an instruction below and watch its 16 bits appear. This is precisely what an assembler does, and what the CPU will decode in Step 15.

What an assembler is

Machine code is numbers, and writing programs as numbers is miserable work. So we write ADD R1, R2, R3 instead, and a small program looks up the opcode, packs the register numbers into their fields, and hands back the 16 bits.

That program is an assembler, and there is one waiting for you in Step 16. It does no thinking on your behalf: one line of text becomes one instruction, every time.

Lab 23 · Instruction encoder
Try this firstPick an instruction and its registers from the dropdowns, and watch the 16 bits assemble underneath, field by field. This is exactly what an assembler does, and now you can watch it happen.
Try: change the destination register and watch only three bits move. Every program you have ever run is a river of numbers exactly like this one.
Lab 24 · Decode it yourself
Try this firstPress New instruction to get 16 bits with no explanation, then work out what they mean by splitting them into fields yourself. The buttons above give you a hint of which instruction it is.
This is the skill that makes debuggers and disassemblers feel like tricks you know the secret to.
What an "immediate" is

An immediate is a constant written into the instruction itself, like the 5 in "add 5 to R1". It arrives with the instruction, instead of being fetched from a register or from memory first.

The name comes from the value being there immediately, with nothing to look up. It is also why the size of the instruction word puts a hard limit on how big such a constant can be, which is what the question below is about.

Why those numbers and not others

Four opcode bits, three register bits, six immediate bits. Those look arbitrary until you try to choose them yourself, so now that every word in the sentence means something, here is the choice as a machine you can operate.

Lab 25 · The bit budget
Try this firstPress TOY-16 in the row marked "real designs", and read the paragraph that appears underneath. Then press One byte and watch the same machine fall apart.
How to read this. An instruction is one 16-bit word and every part of it wants room. The opcode says which operation this is. The register fields say which of the eight boxes to work on. The immediate is a plain number written into the instruction itself. Three sliders divide the same sixteen bits between them, so pushing one up always pushes another down, and the panel underneath tells you which one paid for it.

The four challenges at the bottom are the interesting part, and challenge 1 cannot be solved at all, which is the point of it.
TOY-16 immediates are 6 bits: −32 to +31. You want to put the number 1000 into a register. What are your options?
Build it up. Watch the distinction carefully, because it's the one people trip over: a register is 16 bits wide and holds up to 65,535 quite happily. It's the instruction that has no room: after the opcode and the register fields, only 6 bits are left to carry a constant. So you load 25, then add 25 five more times, or load 31 and multiply up by adding. Real ISAs hit this exact wall and answer it the same way: RISC-V has LUI to load the upper bits, and ARM stitches constants together from pieces. The instruction word is a budget, and constants are what you sacrifice first.
Step 14

One datapath, every instruction

Now we connect everything into one circuit that can execute any instruction in the set. Every instruction goes through the same five phases:

  1. Fetch: read the instruction at the address in the Program Counter
  2. Decode: split it into fields, read the register operands
  3. Execute: put the ALU to work
  4. Memory: access RAM, if this instruction needs it
  5. Write back: store the result into a register

The program counter (PC) is one more register, holding the address of the current instruction. Add 1 to it each cycle and the machine walks through your program by itself. Load something else into it and you have jumped. That's all a jump, a loop, a function call, or an if ever is.

The program counter as a finger on a list

Keep your finger on the line of a recipe you are working through. Finish the line, move the finger down one. That is the PC, and adding 1 to it is the whole of "and then do the next instruction".

Putting the finger somewhere else instead is a jump. Moving it back up is a loop, and deciding whether to move it based on a flag is a branch.

Lab 26 · The complete TOY-16 datapath
Try this firstPress ADD. The wires that carry data for that one instruction light up, and the ones that do nothing stay grey. Now press LW and watch a different set light. Same hardware, different path through it.
Pick an instruction type and watch which wires carry live data. The circuit never changes. Only which paths matter changes. Unused blocks still compute; their results are simply not selected. Orange = data flowing, purple dashes = control signals. The three small rounded boxes squeezed between the larger blocks are the muxes promised in Step 7. The purple names, on the diagram and in the row of badges underneath it, are the control unit's twelve signals. You are not meant to know them yet: Step 15 is entirely about where they come from.
Read the picture this way

Data flows left to right: PC → instruction memory → registers → ALU → data memory → back to registers. Two loops break that flow: the write-back line curving along the bottom, and the next-PC line returning along the top. Those two loops are what makes it a computer rather than a calculator.

Now pick BEQ. Two more lines light up, and both of them run backwards. One carries the ALU's Zero flag to the Next-PC logic, so the machine finds out whether the two numbers matched. The other carries the sign-extended offset, which is how far to jump. That is the whole mechanism: a comparison at one end of the chip deciding which instruction runs next. Every if and every loop you will ever write comes down to those two wires.

What "sign-extended" means

A branch offset is stored in 6 bits, but it has to be added to a 16-bit PC. Padding it with zeros on the left would be fine for positive offsets and would turn every negative one into a large positive number, so every backward jump would fly off in the wrong direction.

Sign extension copies the offset's top bit into all the new bits instead. 111011 is −5 in 6 bits, and 1111111111111011 is −5 in 16 bits. Same value, more bits.

An AND instruction is executing. It has nothing to do with memory. So what is data memory doing during that instruction?
Reading, pointlessly, and being ignored. There is no "skip" in hardware. Every block is powered, every wire carries something, and every unit computes on whatever arrives, so the real question is never "what runs?" but "whose answer gets selected?" The muxes and RegWrite decide what counts; everything else is discarded work. (Modern chips do add "clock gating" to switch off idle blocks, but that's a power optimisation bolted on afterwards, not how the logic thinks.)
Our five phases are fetch, decode, execute, memory, write-back. How many clock ticks does one instruction take on this single-cycle machine?
One tick, always. This is the single most common misreading of a datapath diagram. The five phases describe a signal's journey through space, left to right across the silicon, and the whole journey has to finish inside one clock period, which is why the clock has to be slow enough for the longest instruction. Step 17 is where we finally turn those five places into five separate moments, and get most of the speed back.
Why real machines add 4 to the PC and not 1

Our instruction memory holds one whole instruction per numbered slot, so the next instruction is at PC + 1. Most real machines number memory in bytes, and one of their instructions occupies four bytes, so the next one sits at PC + 4.

Same idea, different unit of counting. Worth knowing only so that the 4 does not puzzle you the first time you read about a real processor.

Step 15

Control signals from a truth table

The datapath is a city of roads with the traffic lights missing. The control unit supplies them. It's a small block of pure logic that takes the 4-bit opcode and outputs a handful of yes/no signals telling every mux which way to point and every memory whether to write.

And here is the punchline of the whole course: the control unit is just a truth table. Same device as Step 3, only wider. Opcode in, control signals out. Nothing more mysterious than the AND gate you started with.

Designing a control unit on a sheet of paper

Write the sixteen possible opcodes down the left of a page and put one column across the top for each control signal. Fill in each row with the 0s and 1s that instruction needs. You have now designed the control unit.

Turning that page into gates is mechanical work, and tools do it without being clever. The thinking was the table.

Lab 27 · The control unit truth table
Try this firstEach row is one instruction and each column is one control wire. Click any row to see, in words, why each wire is 0 or 1 for that instruction. Start with ADD, and read the explanation under the table before trying to read the whole grid.
This table is the CPU's personality. Change one bit in it and you've designed a different processor. Real chips build exactly this from a few hundred gates (or, in a microcoded CPU, store it in a tiny internal ROM).
What a ROM is

ROM is read-only memory: a grid of cells whose contents are fixed when the chip is made. Give it an address and it hands back the word stored there. It never forgets, because nothing is being remembered so much as wired in.

A truth table with the opcode as the address is exactly the shape a ROM suits, which is why some processors keep their control table in one instead of building it out of gates.

Lab 28 · Rewire the control unit
Try this firstPress ADD subtracts. That flips one control wire, and the program below now gives the wrong answer. Find which wire changed by comparing with Correct. Then try the other broken versions.
A processor with a damaged table can run forever, so the run is cut off after 3,000 cycles and reported as "never stopped". Note also that MemToReg without MemRead writes a 0 into a register rather than a word from memory, because nothing is driving the memory output.
What microcode is

Some processors go a step further and let one instruction walk through several rows of an internal table, so a single instruction becomes a short program written in a private language that nobody outside the chip ever sees. That is microcode.

It lets a manufacturer repair a mistake in a chip that has already shipped, by sending out a new table. Ours is a plain table with one row per instruction, and nothing later needs the idea.

For SW (store word), RegWrite is 0. Why does that matter so much?
It prevents corruption. The ALU still computes the address and that value still arrives at the register file's write port. It's a shared datapath, so wires can't be "unplugged". The only thing stopping a wrong write is RegWrite = 0. In hardware,"do nothing" must be actively commanded.
Step 16

Your CPU, running

Everything is built. Below is the finished TOY-16: an assembler, the datapath from Step 14, the register file from Step 11, the ALU from Step 8, wired together and running.

Write assembly on the left. Press Assemble to turn it into machine code. Then Step through one instruction at a time and watch the wires light up, or press Run and let it fly.

How to read a line of TOY-16 assembly

Each line is one instruction: the name of the operation first, then what it works on. In ADD R1, R2, R3 the destination comes first, so it means "put R2 plus R3 into R1". Anything after a # is a note to yourself and is ignored.

A word followed by a colon, like loop:, is a label. It marks that spot in the program so a branch can say "go to loop" and nobody has to count addresses by hand.

Lab 29 · TOY-16: the complete processor
Try this firstPress ⚙ Assemble, then ▶| Step over and over, slowly. Watch the program counter move, the registers change, and the datapath light up. This is a whole processor running, one tick at a time, slow enough to read.
Start here: load the "Count to 10" example, press Assemble, then hit Step about fifteen times and read the narration under the diagram each time. Then try writing your own. The instruction list from Step 12 is your entire vocabulary.
What pressing Assemble actually did

Assemble turned each line into one 16-bit number and laid those numbers out in instruction memory. Nothing has run yet. The machine code you can now see is the same sort of thing you built by hand in Step 13.

Step runs one instruction: the PC picks out a word, the control unit reads its opcode, the wires that matter light up, a result lands somewhere, and the PC moves on.

Lab 30 · Programming challenges
Try this firstPress 1 and read the challenge, then finish the program in the editor and press ✓ Test my program. It runs what you wrote and tells you whether the answer came out right. Start with 1: the whole answer is four lines.
Ten problems, checked by running your program. The instruction list from Step 12 is the whole vocabulary you have.
My program runs forever and I do not know why

Nearly always, the branch that should end the loop is never true. Check that the counter really changes on every trip round, and check which way the comparison points, since a loop that tests the wrong direction never finishes.

Stepping is the cure. Run five or six instructions by hand and watch the one register that ought to be moving. If it is not moving, you have found the bug.

What you just did

You went from a bulb and a switch to a stored-program computer. Every layer is present and none of it was hand-waved: the ALU inside that simulator is the same adder logic you rippled carries through in Step 5, and the loop in your program works because of the flip-flop feedback trick from Step 9. A real CPU is this, with more instructions, more registers, and vastly more engineering for speed.

You single-step a BNE R1, R2, loop and the PC does not jump to loop. What must have been true at that moment?
They were equal. A branch is a subtraction you throw away, keeping only the Zero flag. BNE means "branch if not equal", so it takes the jump when Zero is 0. Here it didn't jump, so Zero must have been 1 and the registers matched. That inverting bit is the BrNotZero signal from Step 15, and it is the only difference between BNE and BEQ anywhere in the machine.
In the simulator, a SW instruction runs. Watch the registers: none of them change. But the ALU definitely produced a number, and that number definitely arrived at the register file's write port. Why is no register damaged?
RegWrite is 0. Wires can't be unplugged: the address the ALU computed really is sitting on the write-data port, aimed at a real register. One control bit being low is the entire reason your variables survive. In hardware,"do nothing" is not the absence of an instruction; it is something you have to actively command.
Step 17

Pipelining

Our CPU does one instruction per clock tick, and the tick has to be long enough for the slowest instruction to crawl all the way from PC to write-back. Meanwhile the instruction memory sits idle for 80% of that time, having finished its job in the first phase.

Think of a laundromat. Wash, dry, fold. Doing one whole load before starting the next wastes two of three machines at all times. Instead, start washing load 2 while load 1 is drying. Each load still takes the same time, but loads come out three times as often.

Two different meanings of "faster"

Latency is how long one thing takes from start to finish. Throughput is how many things finish per hour. A load of washing still takes its full three hours; the laundromat simply finishes more loads in a day.

Almost every argument about computer speed turns out to be about which of those two the two people meant.

That's pipelining. Split the datapath into five stages with registers between them, and keep five instructions in flight at once.

Lab 31 · Pipeline visualiser
Try this firstPress One at a time, then ▶ Play, and count how long five instructions take. Then press Pipelined and play it again. Same instructions, same work, much less waiting.
How to read this. Each row is one instruction and each column is one clock tick. A cell shows which of the five phases that instruction is in during that tick, written with the two-letter names engineers use: IF fetch, ID decode, EX execute, ME memory, WB write back. They are the same five phases as Step 14, shortened so they fit in a table cell.

Press ▶ Play and watch the blocks form a diagonal staircase. Any column holding more than one block is a moment when the machine is working on several instructions at once. Then press One at a time and watch the staircase stretch into a slow single line, which is what the processor in Step 16 was doing.

Watch the throughput number. Latency per instruction doesn't improve at all: each one still takes 5 stages. What improves is how many finish per second. Nearly every "performance" trick in computing is this same distinction.
Why cutting the work up lets the clock speed up

From Step 10: the tick has to be long enough for the slowest stretch of logic between two flip-flops. In our single-cycle machine that stretch runs the whole width of the datapath.

Put flip-flops in four places along the way and the longest stretch is now about a fifth as long, so the tick can be about a fifth as long too. No piece of logic got quicker. The work was cut into smaller pieces, and smaller pieces let "now" be called out more often.

A pipelined chip runs at 4 GHz. The same design without pipelining would have to run at 1 GHz, because one clock tick must cover all five phases. Roughly how much faster does the pipelined chip finish a long program?
About 4×, and the reason matters. Both machines retire roughly one instruction per clock tick in the steady state. Pipelining doesn't do more work per tick. What it buys is a shorter tick: each stage only has to settle its own slice of logic, so the clock can go up. The "5 stages = 5× faster" shortcut gets the right ballpark for the wrong reason, and the wrong reason will mislead you the moment stalls enter the picture.
And now the bill

Overlapping instructions like this creates a brand new class of problem, one that could not exist while the machine did one thing at a time. The next step is nothing but that problem and its fixes.

Step 18

Pipeline hazards

Here are two ordinary instructions, one after the other:

ADD R1, R2, R3     # work out R2 + R3 and put it in R1
SUB R4, R1, R5     # take R5 away from R1

On the single-cycle machine this was safe, because the first instruction was completely finished before the second one started. In a pipeline it is not. The second instruction reads its registers in ID, its second tick. The first instruction does not file its answer into R1 until WB, its fifth. The reader gets there first and picks up whatever R1 held before. That is a data hazard: the right instructions in the right order, giving the wrong answer, purely because they now overlap.

Remind me what IF, ID, EX, ME and WB are

They are the five phases from Step 14, shortened to two letters each so they fit in a table cell. IF is fetch the instruction. ID is decode it and read its registers. EX is put the ALU to work. ME is touch memory, if this instruction needs to. WB is write the result back into a register.

In a pipeline each of those is a separate tick, and five different instructions can be sitting in the five phases at the same moment. The tables in this step are one row per instruction and one column per tick.

Fix one: forwarding

The answer exists earlier than the register file admits. The ALU has finished computing R1 at the end of EX, three ticks before it is written back. So run an extra wire from the ALU's output straight back to the ALU's input, and let the next instruction take the value from there. That wire is called forwarding, and the lab below lets you switch it off and on.

Lab 32 · Watch a data hazard, and fix it
Try this firstLook at the table: instructions 2 and 3 sit waiting (••) for a value instruction 1 has not written yet. Now press Forwarding ON and watch the waiting disappear.
How to read this. Three instructions, and the second one needs R1, which the first has not finished working out. Each row is an instruction, each column is one clock tick, and the cells use the same five stage names as the lab in Step 17. •• is a bubble: the instruction is stuck, doing nothing, waiting.

Press Forwarding OFF and count the bubbles and the total. Then press Forwarding ON and watch them vanish, because a wire now carries the answer straight out of the first instruction's EX stage into the second one's, without waiting for it to be filed away in the register file. Nine cycles becomes seven, and that is one extra wire doing it.

Fix two: stalling, for when forwarding cannot reach

Forwarding can only hand a value backwards in space, never backwards in time. If the value does not exist yet at the moment the next instruction needs it, there is nothing to forward, and the only remaining move is to make the pipeline wait: freeze the instructions behind for a tick and let a bubble travel through instead. That is stalling. It is the fix of last resort, because a stalled tick is a tick in which the machine did nothing.

Which of these pairs of instructions has a hazard that forwarding cannot fully hide, so the pipeline must stall anyway?
The load. Forwarding works by grabbing a result the instant it exists, and an ALU result exists at the end of execute, in good time for the next instruction's execute. But a loaded value doesn't exist until the end of memory, one stage later, and no wire can carry it backwards in time. This is the famous load-use hazard, and it's why compilers work so hard to slip an unrelated instruction in right after a load.
What is a compiler?

So far you have written TOY-16 assembly by hand, one line for one instruction. Almost nobody writes a whole program that way. People write in a language that reads more like maths and English, and a program called a compiler translates what they wrote into instructions like the ones you have been writing.

A compiler is an assembler that thinks. It decides which registers to use, works out the addresses, and, as the next lab shows, shuffles the instructions into an order that leaves the processor with less waiting to do. What comes out the other end is a list of ordinary instructions, no cleverer than yours, just chosen carefully.

Lab 33 · Reorder to remove the stalls
Try this firstPress 1 · Load and add, then use the and buttons beside each line to move it, and watch the stall count at the bottom. The machine gives the same answer either way. One order is faster.
Same instructions, different order, fewer wasted cycles. A compiler does this before your program runs, and an out-of-order processor does it while it runs.

The other hazard: not knowing where to go next

Data hazards are about values. The second kind is about addresses. When the machine fetches a BEQ, it will not know whether the branch is taken until the ALU has done the comparison, three ticks later, and by then it has already fetched two or three more instructions from the wrong place. That is a control hazard.

The fix is to guess. The machine picks the more likely of the two paths, carries on fetching down it, and if the guess turns out wrong it throws that work away and starts again from the right address. This is branch prediction, and it is not the desperate measure it sounds: a loop goes round many times and exits once, so "assume the branch behaves the way it behaved last time" is right well over 95% of the time on real programs. Speculation of this kind is a core part of modern processor speed rather than a niche optimisation.

How guessing turned into a security hole

A processor that guesses runs ahead on work it may have to throw away. Throwing away the results is easy. Throwing away the traces is not, because data dragged into the cache during a wrong guess stays sitting there afterwards. That is where the Spectre and Meltdown security flaws of 2018 came from.

Spectre and Meltdown timed those leftovers to work out secrets the program was never allowed to read directly. Interesting, and entirely safe to skip.

There is a third kind of hazard, and it is the dull one

A structural hazard is two stages wanting the same piece of hardware in the same tick. Fetch wants to read memory while an older load is also reading memory, and there is only one memory.

The fix is to build more hardware, which is why a real chip keeps its instructions and its data in two separate caches instead of one. Nothing later in the course depends on this.

A branch is predicted wrongly. What has the machine lost?
A few ticks of wasted fetching. The speculative instructions are discarded before they are allowed to write anything, so no register and no memory location is damaged. What is gone is the time: the pipeline has to be refilled from the correct address, which costs about as many ticks as the pipeline is deep. That is why deep pipelines and bad predictors are a painful combination, and why an enormous amount of engineering goes into guessing well.
Step 19

Caches and the memory wall

One inconvenient fact dominates modern computer design: processors got fast much quicker than memory did. A modern core can execute a few instructions per nanosecond. Fetching a value from main memory takes around 80 nanoseconds. That's hundreds of instructions' worth of standing still.

The fix is a cache: a small, very fast memory near the core holding recently used data. It works because real programs are predictable: they reuse the same variables (temporal locality) and walk through neighbouring addresses (spatial locality).

What a cache line is

Memory does not hand over one number at a time. It hands over a block of neighbouring bytes, usually 64 of them, called a cache line, because once the trip is being made the neighbours cost almost nothing extra.

So asking for one value quietly brings in the values sitting next to it. That is the whole reason walking through an array in order is cheap, and the question below turns on it.

Lab 34 · Cache hits and misses
Try this firstPress Sequential, then ▶ Run 40 accesses, and note the hit rate. Now press Big stride and run it again. Same number of reads, wildly different speed, and nothing about the program changed except which addresses it asked for.
Try the "random access" pattern versus "sequential". Same amount of work, wildly different time. This is why looping over an array in order can be many times faster than jumping around it, and that is the single most useful performance fact a programmer can know.
Rough cost of reaching data

Register: effectively free, since it's part of the instruction. L1 cache: ~4 cycles. L2: ~14. L3: ~50. Main memory: ~250. An SSD, the flash storage a laptop keeps its files on: ~200,000. Scale it up: if an L1 hit took one second, main memory would take about a minute, and reading an SSD would take over half a day.

L1, L2, L3: what the numbers mean

Caches come in a few sizes, stacked. L1 is tiny and sits right beside the core. L2 is bigger and a little further away. L3 is bigger again and usually shared between all the cores. A miss in one asks the next one out, and a miss in the last one asks main memory.

Each level trades size against speed, for the same reason your desk is smaller than the filing cabinet in Step 11.

You add up every number in a 1000×1000 grid. Version A walks along each row in turn. Version B walks down each column in turn. Identical additions, identical answer. In memory the grid is stored row by row. Which is faster?
A wins, sometimes by a lot. Memory doesn't hand out single numbers; it hands out whole cache lines. Walking a row, one miss drags in the next several values you were about to want anyway, and they arrive free. Walking a column, every step lands in a different line, so you pay the full price every time, and each new line shoves out one you were going to want later. The same number of additions either way, and the row version still finishes several times sooner on a stopwatch. Counting operations is not the same as predicting speed, and this is the cheapest performance trick a programmer can own.
I have not seen "big-O" before

Big-O is shorthand for how the amount of work grows as the problem gets bigger, ignoring constants. Both versions of that grid loop do a million additions, so both have the same big-O.

The point of the question is that the same big-O can still hide a difference of several times in real seconds, because counting operations says nothing about where the numbers were sitting when you asked for them.

Step 20

Building it for real

Nobody draws these diagrams to make a chip. Engineers write the hardware down as text, in a hardware description language: Verilog/SystemVerilog or VHDL. It looks like programming, but it isn't: you are describing structure and simultaneous behaviour, not a sequence of steps. Everything happens at once, all the time.

"Describing" hardware, as opposed to programming it

In a program, one line runs and then the next one runs. In Verilog, every line you write is a piece of circuit that exists all the time. Two lines are two pieces of hardware, both live, both doing their job on every signal that arrives.

That is why the code below looks familiar and behaves oddly if you read it as a list of steps. Read it as a wiring diagram written out in words.

Here is our register file and ALU as real, synthesisable code. Compare it against the ALU you drove in Step 8 and the register file you clocked in Step 11. It is the same machine.

The punctuation in this code means nothing to me

A short key, so you can read it rather than stare at it. moduleendmodule wraps up one block of hardware and names its wires, the way we drew a box round a gate in Step 3. [15:0] after a name means "this is not one wire, it is sixteen, numbered 15 down to 0". 3'd0 is a literal: three bits wide, written in decimal, value 0.

&, | and ^ are AND, OR and XOR applied one bit per column, exactly as in Step 8. assign means "wire this permanently to that". <= inside a clocked block means "load this on the next tick" rather than "less than or equal". case picks one line out of many by matching a value, which is a mux written in words.

Lab 35 · The same hardware, written down
Try this firstPress Verilog. This is the same ALU you built by clicking, written down as text. You do not need to be able to write this yet: look for the parts you recognise, like the operation codes and the word case.
Spot the difference between always @(*) and always @(posedge clk). The first describes combinational logic: gates, no memory. The second describes flip-flops. That one distinction is the practical form of everything in Steps 9-10.
What "synthesisable" means

You can write plenty of Verilog that no factory could ever build: descriptions with no possible circuit behind them, or checks written purely to poke at the design while testing it. Code is synthesisable when a tool can turn it into real gates.

The unbuildable rest is still useful. It is what a testbench is made of, and testing on a laptop is where nearly all the bugs get caught.

From text to silicon

  1. Simulate: run the design against a testbench on your laptop. Catch bugs here; they get exponentially more expensive later.
  2. Synthesise: a tool converts your text into a specific netlist of gates.
  3. Place & route: decide where each gate physically sits and how wires run between them.
  4. Timing closure: prove no path is slower than the clock period. This is where the real work is.
  5. FPGA or ASIC: either load it onto a reconfigurable chip for about $50, or spend millions on photomasks and have a fab print it in silicon.
FPGA and ASIC, in plain words

An FPGA is a chip full of blank logic and switches that can be configured, over and over, to behave like whichever circuit you have described. Slower and dearer per chip, but your design is running the same afternoon, and a mistake costs nothing.

An ASIC is a chip printed to do one thing only. The masks that print it cost a fortune, so it pays only in large numbers, and a mistake means printing the whole thing again.

You can actually do this

A $50 FPGA board plus free tools is enough to run a CPU like TOY-16 as real hardware on your desk, blinking real LEDs, within a weekend. That's not a metaphor or a simulation. It's your processor design, physically executing your program.

Step 21

Interrupts, exceptions and privilege

TOY-16 runs one program until it reaches HALT. A useful computer cannot wait that long. A keyboard may receive a key, a disk may finish a transfer, and a timer must let the operating system share the processor. An interrupt is a request from outside the current instruction stream to handle an event.

An exception starts inside the instruction being executed: divide by zero, an unknown opcode, a forbidden memory access or a page that is not present. Both events use a trap. The processor saves enough state to resume, records a cause, switches to a trusted handler address and enters a more privileged mode.

Privilege protects the whole machine from ordinary programs. User mode cannot change page tables, disable interrupts or talk straight to devices. Kernel mode can. A system call is an intentional exception that asks the kernel to perform one checked service, then returns to the instruction after the request.

What state has to be saved?

At minimum, save the program counter for the return point, the previous privilege mode and the cause. The handler also saves any registers it will overwrite. Architectures divide that work differently, but the software contract must say who owns each part.

An interrupt normally resumes after the interrupted instruction. A fault may retry the same instruction after the handler repairs the cause, as a page fault does. The saved program counter must match that rule.

Can an interrupt arrive in the middle of an instruction?

The electrical request can arrive at any time, but the architecture normally presents a precise boundary. All older instructions appear finished, and younger ones appear not to have happened. The handler sees a state that belongs between two instructions.

That promise becomes harder in the out-of-order core from Step 23. The reorder buffer exists partly so completed work can become visible in order and a precise trap point can be recovered.

What about interrupt priority and nesting?

A controller may mask low-priority sources while a handler runs and allow a more urgent source to interrupt it. Each nested trap needs its own saved state. Unbounded nesting can exhaust the kernel stack.

Real-time systems also measure the longest interrupt-disabled interval and handler time. Continue with Real-Time Systems for latency accounting and priority interference.

Lab 36 · Take a trap and return precisely
Try this firstPress Run one instruction twice, then inject a timer interrupt. Step through save, vector, handler and return. Repeat with a page fault and compare the saved return address.
Compare the saved addresses before returning. The timer finishes the current instruction first, while the page fault saves its address so the handler can repair the mapping and retry that instruction.
Why can an ordinary program not write the register that selects the page table?
That register controls protection. If user code could replace the page table, it could map the kernel or another program's memory. The instruction is restricted to kernel mode.
Step 22

Virtual memory and the MMU

A program uses virtual addresses. The memory chips use physical addresses. Between them, the memory management unit, or MMU, translates one fixed-size page at a time. Each program can therefore use the same convenient address range while its pages live in different physical frames.

A page-table entry stores the physical frame number plus permission bits such as valid, readable, writable, executable and user-accessible. The page offset is copied unchanged. If a translation is absent or the requested access breaks a permission, the MMU raises a page-fault exception instead of touching memory.

Walking a page table for every load would be slow, so the processor keeps recent translations in a translation lookaside buffer, or TLB. A TLB hit translates quickly. A miss walks the table and fills the TLB. A page fault means the table itself says the access cannot proceed yet or at all.

Why not give each program a different physical range?

Relocating every pointer would complicate loading and sharing. Virtual memory lets each process start from a consistent map, place guard pages around stacks, share selected pages and leave unused ranges unmapped.

It also permits demand paging, where a page is loaded only when first touched. That can save memory and startup work, but a storage-backed fault is thousands or millions of cycles slower than a TLB hit.

Does virtual memory make more RAM?

It creates a larger address space, not faster physical storage. Inactive pages may be moved to storage, but using them again causes slow faults. If active pages do not fit in RAM, repeated eviction and reload can make the system spend most of its time moving pages. That is thrashing.

Protection and flexible mapping remain useful even on systems that never swap a page to storage.

Why are page tables arranged in levels?

A flat entry for every possible virtual page would consume memory even for unused address ranges. A multi-level table allocates lower levels only where mappings exist. The virtual page number is divided into indexes, one per level.

Larger pages need fewer entries and cover more memory per TLB slot, but waste more space and provide coarser protection. Operating systems may mix page sizes.

Lab 37 · Translate an address through a TLB and page table
Try this firstChoose Process A, enter virtual address 0x12A and press Read. Repeat it to turn the TLB miss into a hit. Then try a write to a read-only page and switch to Process B without flushing the process tag.
The MMU releases a physical address only after the process tag, virtual page and requested access match an allowed entry. A TLB hit makes that check faster; it does not bypass it.
A TLB lookup misses. Does that mean the program has a page fault?
A TLB miss is a cache miss for translations. The page table is the next place to look. Only an absent or disallowed page-table entry produces the fault.
Step 23

Execute ready instructions out of order

A superscalar core can start more than one instruction per tick. An out-of-order core can start a younger instruction while an older one waits, provided its inputs are ready and its result cannot become visible in the wrong order. Fetch and decode still follow the program. Execution uses the available data and units.

Register renaming gives each new result a temporary physical register. This removes false dependencies caused only by reusing an architectural name such as R1. True dependencies remain: an ADD that needs a load's value must wait for that load.

Results enter a reorder buffer. They retire, or commit, in program order. If an exception or wrong branch occurs, younger entries are discarded and the last committed state remains precise. The program sees the same architectural results, in the same order, that a simple core would produce.

What are false and true dependencies?

Read-after-write is true: the reader needs the older result. Write-after-read and write-after-write can be false when the instructions merely reuse a name. Renaming sends the later write to a fresh physical register, so both values can coexist.

Memory dependencies are harder because two addresses may be equal only after calculation. A load-store queue checks addresses and can replay a load when an earlier store turns out to overlap it.

Is out-of-order execution the same as a compiler reordering code?

No. A compiler changes the saved instruction order under the language's rules. The processor keeps the original instruction order but schedules internal work dynamically from the values and delays it sees during this run.

Both must preserve observable behaviour. The processor also has to recover from branch guesses and precise exceptions while several instructions are in flight.

What limits instructions per cycle?

Decode width, rename width, queue size, execution units, register ports, cache bandwidth, dependency chains and branch accuracy all matter. A four-wide front end does not promise four retired instructions every tick.

The measured average is instructions per cycle, or IPC. It belongs to a particular program, input and machine state. A wider core often spends more power searching for independent work.

Lab 38 · Schedule around a slow load
Try this firstPress Run in order, then Run out of order. Step the second run and watch independent instructions execute while the load waits. Turn renaming off to expose a false dependency.
Use the cycle rows to separate execution from retirement. The reorder buffer keeps completed values private until every older instruction is safe to commit.
Why can a completed younger instruction wait in the reorder buffer?
Retirement preserves precise state. If the older instruction faults, all younger work must disappear as though it never happened.
Step 24

Keep private caches coherent across cores

Put two cores beside each other and each wants a private fast cache. If both cache address X, then Core 0 writes X, Core 1 must not keep reading its old copy. Cache coherence is the agreement about writes to one memory location.

A simplified MESI protocol labels each cache line Modified, Exclusive, Shared or Invalid. A core may read a Shared line. Before writing it must gain exclusive ownership, which invalidates other copies. A Modified line contains a newer value than memory and must eventually be written back or transferred.

Coherence does not give every operation one global order. Store buffers and speculative loads can make different addresses appear in surprising orders. The architecture's memory model states the allowed observations. Fences and atomic read-modify-write instructions add the ordering needed by locks and queues.

How do caches learn that another core wants a line?

Small systems may broadcast requests on a shared interconnect, called snooping. Larger systems use a directory that records which caches hold each line and sends messages only to them.

The exact protocol has more transient states than the four stable MESI names. Messages take time, cross and retry, so verification checks every allowed interleaving.

Does coherence prevent data races?

No. It eventually makes writes to one location visible according to protocol rules. It does not decide which thread should write first or make a group of ordinary operations atomic.

Software still uses locks, atomics or message passing. Continue with Concurrent Data Structures for compare-and-swap, memory ordering and safe reclamation.

What is false sharing?

Coherence works on whole cache lines. Two cores can update different variables that happen to share one line, causing ownership to bounce even though the variables do not logically interact. That is false sharing.

Padding or reorganising data can separate hot per-core variables. Measure first, because padding also increases memory footprint and can harm useful locality.

Lab 39 · Pass ownership between two caches
Try this firstHave Core 0 read X, then Core 1 read X. Both should be Shared. Now make Core 0 write X and watch Core 1 become Invalid. Turn on adjacent counters to produce false sharing.
The simulator transfers ownership for a complete cache line. That is why adjacent counters can invalidate each other even though the source program treats them as separate variables.
Core 0 and Core 1 update different counters, but performance collapses when they run together. Both counters occupy one cache line. What is the likely cause?
The protocol sees one line. It cannot tell that the two words are unrelated. Each write invalidates the other core's copy of the whole line.
Step 25

Do the same operation across many values

A scalar instruction adds one pair of numbers. A vector instruction can add several pairs at once. The processor divides a wide register into lanes, gives each lane the same operation, and stores several results. This is useful for pixels, audio samples, simulation cells and machine-learning tensors.

SIMD means single instruction, multiple data. Fixed-width SIMD instructions name a particular register width. A vector-length architecture instead tells the processor how many elements remain and lets each implementation handle as many as its hardware supports. A mask can disable lanes whose elements should not change.

A GPU applies this idea to many groups of threads. It keeps thousands of small jobs available, then runs another group when one waits for memory. That helps regular, data-parallel work. A branch that sends nearby lanes down different paths wastes part of the machine until those paths join again.

Why is memory layout part of vector performance?

Adjacent lanes are easiest to feed when their elements occupy adjacent addresses. One wide load can then fetch useful data for every lane. Scattered addresses may require several cache-line requests.

Changing an array of objects into separate arrays of positions, speeds and colours can improve access for one calculation. It can make other calculations worse, so the right layout follows the work.

Does eight lanes always mean eight times faster?

No. The loop needs enough independent elements, the data must arrive quickly enough, and most lanes must be active. Setup, tail elements and branches reduce utilisation. Some operations also share a smaller number of execution units behind the lanes.

Measure useful results per second, not lane count. The explorer below reports both cycle count and lane utilisation so a partly filled final group is visible.

What is the difference between a CPU vector unit and a GPU?

A CPU spends silicon on low latency, large caches, branch prediction and a few capable cores. A GPU spends more of it on arithmetic lanes and tolerates latency by switching among many ready groups.

Neither is universally faster. Parsing irregular pointer structures often suits a CPU. Applying the same matrix operation to a large batch often suits a GPU or another accelerator.

Lab 40 · Pack work into vector lanes
Try this firstSet 17 items and four lanes. Step through the groups and watch the final group leave three lanes idle. Then compare one, four and eight lanes, and turn on a mask to see why active-lane count matters.
Compare vector cycles with useful lane slots. Adding lanes helps when the program supplies enough independent data, but partly filled groups and masks leave some capacity idle.
A loop has eight SIMD lanes, but every branch enables only two of them. What is the immediate problem?
The inactive lanes still belong to the issued instruction. Divergent control flow lowers utilisation even when the arithmetic itself is parallel.
Step 26

Represent a wide range of real numbers

Fixed-point numbers keep the binary point in one agreed place. Floating point stores a sign, a scaled exponent and a fraction called the significand. That lets the same 32 bits describe tiny and huge values, but the gaps between neighbouring representable values grow as the magnitude grows.

IEEE 754 binary32 uses 1 sign bit, 8 exponent bits and 23 stored fraction bits. Ordinary values also have an implied leading 1. Exponent patterns at the ends represent zero, subnormal values, infinity and NaN, which means “not a number.” Those special cases make overflow and invalid operations explicit.

Most decimal fractions, including 0.1, do not end in binary. An operation computes an exact mathematical result, then rounds it to a nearby representable value. Addition is therefore not always associative: rounding (a+b)+c after each operation can differ from a+(b+c).

How does the hardware add two floating-point numbers?

First compare exponents and shift the smaller significand so the binary points line up. Add or subtract the significands, normalise the result, then round it using extra guard information.

The shifts, leading-bit search and rounding logic make a floating-point adder larger than the integer ripple adder from Step 6. Pipelining lets it start new work before earlier additions finish.

Is floating point inaccurate?

It is a precisely specified approximation. Every finite format has gaps. The useful questions are how large the rounding error can be, whether the calculation magnifies it, and whether a different scale or algorithm would behave better.

Money is often stored as integer cents or decimal arithmetic because binary rounding is awkward for exact decimal rules. Scientific calculations often prefer floating point because their scales vary.

Why do machine-learning chips use several number formats?

Smaller formats move more values through the same memory path and fit more multipliers on a chip. Training may multiply low-precision values but accumulate into a wider result to limit error.

Format choice is an engineering trade-off among range, precision, bandwidth, energy and model quality. A test must compare the final task result, not just the bit width.

Lab 41 · Open a 32-bit floating-point value
Try this firstEnter 0.1 and inspect its sign, exponent and fraction. Add 0.1 several times, then try a large number and compare the gap to its next representable neighbour.
Try values near 1 and near 16,777,216. Binary32 keeps roughly the same number of significant binary digits, so the absolute gap between neighbouring values grows with magnitude.
Why can repeatedly adding 0.1 produce a nearby value instead of an exact decimal result?
The rule is deterministic. The approximation comes from finite binary spacing, not random behaviour in the arithmetic unit.
Step 27

Meet the power, heat and timing limits

Each CMOS gate charges and discharges capacitance when it switches. A useful first model of dynamic power is P = αCV²f: activity times capacitance times voltage squared times frequency. Raising the clock can increase work per second, but raising voltage to keep that clock stable has a squared power cost.

Transistors also leak current while idle. Leakage depends strongly on process and temperature. Electrical power becomes heat; the package and cooling system must carry that heat to the surrounding air. If the die gets too hot, firmware lowers frequency or voltage. That is thermal throttling.

A clock period still has to exceed the slowest register-to-register path, including clock uncertainty and setup time. Lower voltage makes gates switch more slowly. Dynamic voltage and frequency scaling, or DVFS, therefore changes energy, performance and timing margin together.

Why can turning off unused blocks save more than slowing the clock?

Clock gating stops unnecessary register switching, which cuts dynamic power. Power gating disconnects a block from its supply and can also reduce leakage, but the block loses state unless retention circuits preserve it.

Waking a gated block takes time and energy. A controller should gate only when the expected idle period is long enough to repay that cost.

Does lower frequency always save energy?

Lower frequency reduces dynamic power, but the task then runs longer. Leakage and the rest of the system continue consuming energy during that time. Finishing quickly and entering a deep idle state can use less total energy in some systems.

Separate power, measured in watts, from energy, measured in joules. Energy equals the area under the power-versus-time curve.

How does a real chip find safe settings?

Design tools analyse paths across manufacturing, voltage and temperature corners. On-chip sensors watch temperature and sometimes timing margin. Firmware selects tested voltage-frequency pairs rather than choosing arbitrary values.

The model below is for reasoning, not sign-off. Physical design also includes voltage drop, local hot spots, wire delay, clock distribution and variation between transistors.

Lab 42 · Balance voltage, clock and temperature
Try this firstRaise frequency until timing fails. Increase voltage just enough to recover margin and observe the power change. Then lower activity and compare power with temperature after the model settles.
If timing fails, lower the clock or select a tested higher-voltage operating point. Then check the power and temperature estimates, because recovering timing changes both.
Why can a small voltage increase cause a noticeable dynamic-power increase?
Dynamic power grows approximately with V². The exact chip is more complicated, but the squared term explains why voltage is an expensive way to reach a higher clock.
Step 28

Verify the design and defend hidden state

A processor must work for far more cases than a person can click. A testbench generates instructions, interrupts and memory responses, then compares the design with a reference model. Assertions watch rules such as “a register changes only on a clock edge” and “two caches never own the same writable line.”

Random tests explore many combinations. Coverage records which instructions, states and transitions were exercised. Formal verification asks a solver to prove a property for every allowed input sequence within a model. None of these replaces the others: the reference model, property and environment assumptions can each be wrong.

Speculation adds a security problem. A wrong-path instruction may be cancelled architecturally yet still change a cache, predictor or timing state. An attacker can measure that leftover effect. Mitigations include barriers, data-independent access patterns, predictor isolation and hardware that prevents secret-dependent transient work from changing observable state.

What should be checked at each layer?

Unit tests check an ALU or decoder. Integration tests check the datapath and controller together. Instruction-set tests compare complete programs with the architectural specification. FPGA prototypes add real clocks and device traffic before manufacturing.

Post-silicon tests then measure fabricated parts, including speed, temperature and rare interactions. Finding a logic error before fabrication is far cheaper, so verification often uses more project time than writing the initial RTL.

Can testing prove that there are no bugs?

A passing test proves only that the tested run matched its oracle. Exhaustive testing can cover a small finite block, such as the four-bit adder below. It cannot enumerate every state of a modern processor.

A formal proof covers the stated property under stated assumptions. Review still asks whether that property describes the requirement and whether the model omitted a physical or security behaviour.

Why are timing effects part of security?

Two executions can produce the same architectural registers and memory but take different amounts of time because their cache histories differ. If that difference depends on a secret, repeated measurements can reveal information without permission to read the secret address.

Security review therefore includes microarchitectural state, shared resources and speculative paths. Continue with Memory Exploits for practical attack and defence methods, including control-flow and memory-safety boundaries.

Lab 43 · Find a carry bug and a transient leak
Try this firstRun random four-bit adder tests, then run every input. Inject the carry bug and compare how quickly each method finds it. In the second panel, execute a mispredicted secret-dependent load with and without the barrier.
The adder panel checks an architectural property. The transient panel checks a different boundary: a cancelled instruction must not leave a secret-dependent cache or timing signal.
A formal tool proves an assertion. What must an engineer still review?
A proof is only as useful as its statement and model. A missing property or unrealistic assumption can leave a real failure outside the proof.
Step 29

Where to go next

You have followed a processor from transistor switches through instructions, pipelines, memory systems and multicore execution. You have also seen where timing, power, numerical error and security enter the design. The next useful move is to connect one of those layers to a larger system.

What RISC-V is, and why "free" matters here

An instruction set is a specification, and most of them are owned. Building a chip that runs ARM instructions means an agreement with ARM. RISC-V is published for anyone to implement, without permission or fee.

That is why it turns up in university courses, research chips and hobby projects. Its base set is close in spirit to TOY-16, with 32 registers and a few more instructions.

Build things

  • Nand2Tetris: a free course that builds a computer from NAND gates up to a working OS and a Tetris game. The natural next step after this page.
  • Turing Complete or Digital Logic Sim: puzzle games where you wire real gates. Solid teaching, disguised as play.
  • An FPGA board (iCEBreaker, TinyFPGA, Arty A7) with the open-source Yosys/nextpnr toolchain, or the vendor's. Write your CPU in Verilog and run it.
  • RISC-V: a real, free, open instruction set, small enough to implement yourself. The RV32I base is only about 40 instructions. People do implement it in a weekend.

Read things

  • Patterson & Hennessy, Computer Organization and Design: the classic. The single-cycle MIPS datapath you just built is a simplified cousin of theirs.
  • Charles Petzold, Code: from telegraph relays to computers, no diagrams needed. Clear prose, zero prerequisites.
  • Hennessy & Patterson, Computer Architecture: A Quantitative Approach, the graduate sequel: superscalar, out-of-order, multicore, GPUs.

Follow the connections

  • Bits and Memory: connect encodings, addresses and object layout to the machine that stores them.
  • Operating Systems: use traps, privilege and page tables to understand processes, devices and protection.
  • Performance Engineering: measure pipelines, caches, branch misses, vectors and memory traffic instead of guessing what is slow.
  • Parallel Algorithms and Concurrent Data Structures: turn cores, coherence and atomics into correct parallel programs.
  • Real-Time Systems: put interrupt latency, timing bounds and hardware interfaces into a system with deadlines.
  • Build a Language: translate source programs into the instructions, registers and calling rules a processor actually executes.
What "out-of-order" actually reorders

Start with one concrete extension: add an interrupt input and two control registers to TOY-16, or add a second pipeline issue slot to the simulator. Write the architectural rule first, then test the edge cases before changing the datapath.

For a larger implementation, choose the RV32I instruction set and reuse the same sequence as this course: reference model, assembler tests, single-cycle core, pipeline, caches, then advanced features.

Lab 44 · The tower, from memory
Try this firstEach row names a piece you built. Click the button beside it, over and over, until it shows the part of the course that built that piece, then press Check my answers. It marks each row with a tick or a cross and tells you where the piece really came from. No looking back at the earlier steps until you have tried.
Each piece was built in one part of the course. Anything you misplace is a step worth half an hour again, and the check tells you which one to open.
What to keep

A processor is a stack of contracts. Gates implement state, the datapath implements instructions, and the architecture tells software what it may rely on. When a result surprises you, locate the contract, build a small test, and trace the state across that boundary.