09Addressable Memory (RAM)
Chapter 8 ended on a small triumph and a big limitation. The triumph: we built a register — eight flip-flops on one clock — that snapshots a whole word at a single edge and holds it rock-steady. The limitation is that it holds one word, and only one. That's fine for a running total. But it falls apart the moment a computation needs several numbers alive at the same time. Almost every computation does. So here is the question this chapter turns on: how do you build not one register but a thousand of them, and hand yourself the power to say "give me slot number 617" and get exactly that one back? The answer has a name you already half-know: RAM, random-access memory. Open it up and there is no new physics inside. It's a bank of registers — the very ones we just built — plus one genuinely new circuit. That circuit does something we haven't needed until now. It takes an address, a plain binary number, and lights up exactly one of the registers, leaving every other one dark. That circuit is the decoder, and it's the whole trick. Around it, reading turns out to be an old friend in disguise: AND to mask, OR to merge. That's nothing but the multiplexer from Chapter 3 grown wide. And writing turns out to hide a genuine trap — the trap that motivates the clean design real chips actually use. No magic. Just registers we own, a decoder that picks one, and the honest hazard that comes with letting a machine write to itself.
01One register can't hold a computation
Let's start by holding on to what we already have, so the gap we're about to hit is unmistakable. Here is a single register from last chapter: eight D flip-flops sharing one clock, capturing a whole byte on one rising edge. It's a solid one-word memory. Load a byte into it below, and remember its one hard limit: whatever you put in replaces whatever was there. It has room for exactly one number.
Now watch that one-number limit bite. Take a computation only a hair more ambitious than a running total — say (a + b) − (c + d). Trace it and count how many values have to be alive at once. You need a, b, c, and d all sitting somewhere, plus room for partial results as you go. A single register can hold exactly one of them. The moment you load b, your a is gone. Follow the values below and watch them pile up with nowhere to live.
So the need is plain. We want many registers, and a way to talk to a specific one without a separate wire running to each. Here's the mental model to hold for the rest of the chapter, and it's deliberately mundane: a numbered table. Every row is one word of storage. Every row has an address — a number that names it, 0 at the top, counting down. And the whole device offers just two verbs: read (name an address, get its word back) and write (name an address, drop a new word in). That's it. That table, made real in silicon, is RAM. Browse it below: pick an address, see the one word that lives there.
But how does a row actually get picked? That word address is hiding a step, and it's worth stopping on. Start with the dumbest thing that could work. Give every register its own select line — a private wire running from you straight to that register's door, with nothing else on it. Raise line 5 and register 5 wakes up. Every other register stays dark. No code, no cleverness, no new circuit. And it works perfectly. Now scale it and count. A thousand registers means a thousand select lines leaving the chip. A thousand pins. A thousand traces on the board, one per slot, forever. Sit with that number until you recoil, because the recoil is the point. The dumb selector is completely correct and completely unbuildable, and both halves of that will matter.
The word "random-access" in the name is worth demystifying right now, because it sounds fancier than it is. It doesn't mean the memory is random. It means you can jump to any address in one step. The 900th slot is no harder to reach than the 3rd. Compare that to a tape, which you'd have to spool through in order. Random access = direct access: any slot, same cost. That single promise is exactly what the decoder we're about to build delivers.
02A table with a shape — depth × width
Before we build anything, let's give the table its two honest dimensions. Every real memory chip is spec'd by exactly these two numbers. Down its height it has some number of slots — that's its depth, how many words it can hold. Across each slot it has some number of bits — that's its width, the size of one word. A memory that is 16 slots deep and 8 bits wide holds sixteen bytes. Multiply the two and you get its total capacity. Depth and width are independent knobs. Grab both below and watch the grid grow.
Now the piece that makes the whole thing tick. The address is itself just a binary number, so its bit-count is what caps the depth. This is the Chapter 4 place-value idea wearing a job. With k address lines you can write 2ᵏ distinct patterns — 00, 01, 10, 11 for two lines — and each pattern names one slot. So two address wires reach four slots. Four wires reach sixteen. Ten wires reach a thousand and change. Run it backward: to address N slots you need ⌈log₂N⌉ lines. Address width and depth are locked together. Slide the number of address lines below and watch how many slots it can name leap upward.
Notice what that costs us and what it buys us. It buys the "random-access" promise cheaply: naming any of a thousand slots takes just ten wires, not a thousand. But it hands us a job we haven't done before. Those ten wires arrive as a coded number, and the storage array underneath speaks in one wire per slot. Something has to translate the compact binary address into a single "this one" signal aimed at the right register. That translator is the decoder, and it's next.
Before we build it, say the objection out loud, because it's the right one to have. We just squeezed a thousand select lines down to ten address wires. Now the decoder's whole job is to turn those ten back into a thousand. So what did we actually buy? Here's the answer, and it's the reason the scheme is worth anything at all. The ten wires are the ones that travel. They cross the chip, ride inside an instruction, sit written down in a program you'll write later. The thousand never leave the memory's own doorstep. They're local fan-out — short hops from the decoder to the registers standing right beside it. We compressed the expensive part and decompressed the cheap part. That's the whole trade, and it's a bargain.
03The decoder — pick exactly one
Here's the problem stated at the metal. We have, say, four registers and a 2-bit address on two wires, A₁ and A₀. We need four output lines, one per register. The address 00 should light only line 0, 01 should light only line 1, and so on. First, one piece of wiring that shapes everything you're about to see. Each address bit runs past the gates on two rails, not one: the bit itself, and its inverse through a NOT gate. We mark the inverted rail with a bar over it, so A̅₁ is the wire that is high exactly when A₁ is 0. Now the building block, and it's one you already trust: the AND gate, which goes high only when all its inputs are high. Wire line 0's AND to fire on "A₁ is 0 and A₀ is 0" — feed it both address bits inverted, which is to say the rails A̅₁ and A̅₀. Wire line 1's AND to fire on "A₁ is 0 and A₀ is 1," and so on down. Each register gets an AND gate watching for its own binary address, tapping the two rails that are high for its code. Before you look: which two rails must line 3's gate tap? Then check yourself against the 2-to-4 decoder below: four AND gates, each matching one code.
Before you touch it, predict. Set any address in your head. How many of the four output lines light? Here's the answer, and it's the one worth carving into stone. For any address you set, exactly one output line is high and all the others are low. Not two, not zero — precisely one. This is called the one-hot property, and it isn't luck. It falls straight out of the four gates you just wired, in two strokes. Never two: each AND gate demands a different combination of the address bits, so pick any two gates and no address can satisfy both. Never zero: four gates, four possible addresses, one each — no address is an orphan. Exclusive plus exhaustive forces exactly one — at four lines, at sixteen, at any width, including the widths you could never sit and check by hand. So the decoder converts a compact number into a single unambiguous pointer. And it answers the promise we made earlier. Every AND gate is watching the address at the same instant. The address is broadcast to all of them at once. It never travels to the slot. That's why no slot is nearer than another. Set the address below and watch it: only one line ever lights.
And nothing about "four" was special, so let's state the general machine. An n-to-2ⁿ decoder takes n address bits in and produces 2ⁿ output lines out, always exactly one hot. Three bits fan out to eight lines. Four bits fan out to sixteen. The k lines that named 2ᵏ slots a moment ago are exactly the k inputs of a k-to-2ᵏ decoder. The address width, the slot count, and the decoder's fan-out are one and the same number. That's the through-line of the whole chapter. Widen the decoder below and watch the address bits and the output lines grow in lockstep, one-hot the whole way.
04Reading — mask, then merge
We can point at one register now. But pointing isn't reading. Every register is still sitting there with its stored bits, and we need only the pointed-one's bits to actually come out. The first half of the answer is a gate you met as plumbing back in Chapter 3: the AND gate as a valve. Feed a register's data bit into one leg of an AND and its decoder line into the other. When the decoder line is 0, this register isn't selected: the valve is shut and the AND outputs a flat 0, no matter what the register holds. When the line is 1, the valve opens and the data bit walks straight through. Re-meet the AND-as-valve below: the enable leg decides whether the data survives.
Hold on, though — why do we need a valve at all? The instinct is to just tie all four registers' bit-0 outputs onto one wire and let the selected one drive it. Try it, and you learn a rule this book leans on from here to the end. A gate output always drives. It never abstains and never lets go. Every moment it's powered, it is either pushing high or pulling low. Tie four of them together and three of them are still shoving. You haven't selected a register. You've started an argument on a wire, between outputs that cannot back down. So the order is forced. You silence first and merge second, and the AND valve is how you silence.
So the decoder's one hot line arms exactly one register's valves and shuts everyone else's. Every unselected register is now masked to a clean 0, and only the selected register's real bits are alive. Now we just need to collect them onto one output. That's the other Chapter 3 friend: the OR gate as a merge, where several inlets join into one pipe. OR together the corresponding bit from all four registers. Three of them are forced to 0 by their shut valves, so they contribute nothing. The survivor's bit — a 0 or a 1 — passes through the merge untouched. Re-meet the OR-as-merge below: any live inlet reaches the tap, and here only one inlet is ever live.
Put the two halves together and you have the whole read path, one bit-column at a time. The decoder lights one line. That line opens one register's AND valves and slams the rest shut. The deselected registers all mask to 0. Then a big OR merges the column, so the lone survivor emerges on the output wire. Do that for every bit-column in parallel and the selected word appears whole. Trace one read below: set an address, watch one line go hot, the others mask to zero, and the OR carry the survivor out.
Now step back and look at what we just built, because it isn't new. It's an old device grown up. "Steer one of several inputs to a single output, chosen by a control code" is the exact definition of the multiplexer from Chapter 3. A 2-to-1 mux picked between two inputs with one select bit. This read multiplexer picks between many registers with a whole address. And here's the honest part, because there are two different ways to scale a selector up wide. You can stack 2-to-1 muxes into a tree, each layer halving the field until one input survives. Or you can go flat: one decoder, an AND-mask, an OR-merge — which is exactly what we just built. The two roads compute the identical function. Memory takes the flat one for a plain reason: the decoder is already sitting there, because the write path is going to need it anyway. See the tree road below, as three stacked 2-to-1 muxes: same answer, different shape.
So there's the reader, complete and honest: an address in, one word out, any slot reachable in a single step. Try it end to end below. Type an address and watch that one register's stored word flow out onto the data lines, while every other register stays silently masked. This is "random access" made physical: slot 12 is no more work to fetch than slot 1.
05Writing — the enable, and the hazard
Reading was the gentle half: it disturbs nothing. Writing is where memory earns its keep, and where the real subtlety lives, so let's do it carefully. Recall first the ritual by which a single register accepts a new value. You present the data on D and it waits there at the door. The register captures it on the next rising clock edge, and only if its enable says so at that moment. Everywhere else in time, the register just holds. Re-watch that one-register write ritual below: data waits at the door until the clock edge comes and the enable lets it in.
Before we scale that to a whole bank, two words have to come apart, because everything ahead hangs on the difference. A clock edge says when. It's the drumbeat, and it alone decides the instant a register samples its input. An enable says whether. It's a yes-or-no vote about that instant, and it has no power to create one. Chapter 8's register captures on an edge and at no other time. It is not a door that hangs open while you feed it. Hold that line hard: when versus whether. Every knot left in this chapter is those two words quietly collapsing back into one.
Now scale it to the bank, and the decoder pays off a second time. We put the same data word on a shared set of input wires running past every register at once. But we only want one of them to actually swallow it. So we take each register's write-enable and gate it with that register's decoder line ANDed with a global write signal. When you assert write, only the addressed register — the one whose decoder line is hot — sees its enable go high and latches the data. All the others see a low enable and hold their old contents, untouched. Write one slot below and watch the other three bystanders sit perfectly still.
Here's a fork in the design worth naming. The naive version has a bug, and the clean version is what real chips do. The tempting shortcut is to gate the clock: feed each register a clock that only ticks when it's selected. It seems to work, but it's fragile. You're now manufacturing clock edges out of AND gates, and any glitch on the address or write line can forge a stray edge. The cleaner discipline keeps the true clock global — every register ticks on the same real edge, always. Instead it gates a separate write-enable input, which decides whether the register loads new data or reloads its own output on that edge. Same clock for all; only the enable is steered. Compare the two schemes below: gated clock versus global clock with a steered enable.
That phrase — reloads its own output — earns more than a clause, because it's the strangest move in the chapter. Every rising edge, the register captures something. It has no choice: the clock never stops and never asks permission. The write-enable only picks what gets captured. Either the new data on D, or the register's own Q, looped back around into its own input. So holding a value isn't the absence of writing. Holding is writing, over and over, with yourself as the source. And look at what does the picking: one of two inputs, chosen by a control bit. That's a 2-to-1 mux, the thing you rebuilt one section ago, now wired with its own output as one of its inputs. That's the whole fix in one sentence: the enable changes what is captured, never when.
And now the trap the gated-clock scheme sets, because feeling it is the best argument for the clean one. Suppose write is held high and you change the address while it's still asserted. As the address bits flip, the decoder's hot line jumps from the old register to the new one. In that jump, a line that was low goes high. To a register whose write is gated by that rising line, a low-to-high transition looks exactly like a clock edge. It latches — even though you never meant to write to it. That's the spurious-write hazard: a rogue rising edge, born from moving the address mid-write, corrupting a slot you never addressed on purpose. Trigger it below: hold write high, slide the address, and watch a register you didn't choose quietly capture garbage. One honest note, since the figure below says real chips gate the clock and this chapter says don't. Both are true, and the gap between them is the whole discipline. A real SRAM does gate a line into its cells. But the edge it gates with belongs to a write pulse, generated by the chip's own timing chain, fired only once the address has been latched and allowed to settle. The address never gets to make the edge. So what we're banning isn't the AND gate. It's letting a signal that moves whenever it likes decide when a register samples. When the pulse owns the edge, gating is safe. When the address owns it, you get the hazard you just triggered.
Last, lift your eyes from the wires to the shape of what you've made, because it's a landmark. Every input we've been toggling by hand — the address lines, the write line, the data going in — is a control signal. Its job is not to carry a number but to steer the machine: to say which slot and whether to write. And the registers, the decoder, the read multiplexer, the data wires threading them — that's a datapath, the roads the numbers travel. Right now you are the thing setting those control signals. Hold that thought: a human punching address-and-write is a job a circuit could do instead, and that circuit is the seed of a control unit a few chapters out. See the whole RAM as one block below, its control lines named.
And that's Chapter 9 — the chapter where one word became a thousand, addressable. We started stuck: a lone register holds exactly one number, and any real computation needs several alive at once. The fix was to picture a numbered table — RAM — with just two verbs, read and write, and two honest dimensions, depth and width. Its address is a plain binary number whose bit-count caps the depth (k lines name 2ᵏ slots). The one new circuit was the decoder: n address bits in, 2ⁿ lines out, always exactly one hot. That's the one-hot property, guaranteed because no two AND terms can be satisfied together (never two) and every address owns a gate (never zero). Reading turned out to be an old friend: AND to mask the deselected registers to zero, OR to merge the survivor. That's the Chapter 3 multiplexer grown wide into a read multiplexer. And writing hid a real trap — the spurious-write hazard of a gated clock — which is exactly why real memory keeps the clock global and steers a clean write-enable instead. Underneath it all we spotted the two things every computer is made of: a datapath the numbers ride, and the control signals that steer them. But look at how we wired it. Every register's output runs to the read multiplexer, and its input runs from the data lines. Inside this RAM that count stays honest: add a register, and you add one output and one input. The wires grow in step with the parts, no faster. The squeeze starts one level up. A computer isn't one bank. It's a CPU, a RAM, an I/O port and more, and each of them needs to reach every other. Wire that mesh point-to-point and N parts need N(N−1)/2 links, growing as the square of the components. There's a trick that collapses that rat's nest to a single shared connection per part. It's called a bus — and it comes with a way to physically short-circuit your own hardware. That's next.