5.1.4 · D2Instruction Set Architecture (ISA)

Visual walkthrough — Register file organization

2,435 words11 min readBack to topic

We only need three plain-word ideas to start, so let us name them once:


Step 1 — One storage cell (the atom)

WHAT. We start with the smallest possible piece: a single box that remembers one bit. In hardware this box is a D flip-flop. It has one data-in wire, one data-out wire, and one clock tick that tells it "now, capture whatever is on data-in and hold it."

WHY start here? Because a register file is nothing but a huge grid of these boxes. If you understand one box and how to (a) look inside it and (b) change it, you understand the whole file — everything else is just doing that for many boxes at once.

PICTURE. The figure shows one cell. The green arrow is data flowing in on a clock edge; the orange arrow is the value flowing out to be read. The little triangle marks the clock input — think of it as the box's heartbeat.

Figure — Register file organization

Step 2 — Stack cells into one register (width )

WHAT. One box holds one bit, but we compute on whole numbers. So we lay boxes side by side and hand them one shared clock and one shared write-enable line. That bundle is a single register: bits that update together.

WHY. A -bit number is bits that must be stored and fetched as a unit — you never want half of a number updated. Sharing one enable line guarantees all bits capture on the same tick, or none do.

PICTURE. Below, the cells of Step 1 are drawn in a row. The single teal wire on top is the shared write-enable; when it is at a clock tick, all boxes latch their inputs at once.

Figure — Register file organization

Each symbol above: = the width (how many bits in one number); "cell" = the single box from Step 1; the product means "a register is copies of that box wired to one enable".


Step 3 — Stack registers into the file ( rows)

WHAT. Now stack of those registers vertically. That grid is the register file: rows, each bits wide.

WHY the number ? Because we will name each row with a -bit address, and a -bit binary number can point at exactly different rows — no more, no fewer. Choosing to be a power of two means no address goes to waste. If , then rows, which is exactly the common case.

PICTURE. A grid: rows are registers (labelled R0, R1, ... R), columns are bit positions. The total number of storage boxes is the whole grid.

Figure — Register file organization

= how many rows; = how many bits in the address that picks a row; = every box in the grid. For that is boxes of state.


Step 4 — Reading: pick ONE row onto a wire (the MUX)

WHAT. We have rows but the ALU can only look at one at a time on a read wire. We need a gadget that takes an address and selects the matching row's output onto that wire. That gadget is a multiplexer (MUX): a many-in, one-out switch steered by the address.

WHY a MUX and not something else? The very shape of the read problem is "given candidates and a chosen index, put the chosen one on a single output wire." That sentence is the definition of a MUX. We reach for a MUX because the problem is literally "select one of many onto a wire" — no other block does exactly that. A -to- MUX steered by the -bit address does the whole job.

PICTURE. The figure shows all row-outputs entering a big trapezoid (the MUX). A plum-coloured -bit address enters the narrow side and steers exactly one input straight through to the single read port wire on the right. Follow the highlighted path: only the addressed row reaches the output.

Figure — Register file organization

Step 5 — Writing: route ONE wire back into ONE row (the decoder)

WHAT. Writing is reading in reverse: one incoming value must land in exactly one chosen row. We take the destination address, feed it into a decoder, and the decoder lights up one of its output lines. We AND that line with a global WriteEnable; only the row whose line is high captures the data on the next clock tick.

WHY a decoder + enable? The write problem is "one wire → one of many rows", the mirror of the MUX. A decoder turns a -bit address into lines with exactly one line hot — precisely the "pick which row gets written" signal. The AND-with-WriteEnable is the safety catch: no row changes unless we deliberately say "write now."

PICTURE. The -bit write address enters a decoder (left). Of its output lines, exactly one (highlighted orange) is . That line is ANDed with the teal WriteEnable and becomes the shared enable of that one register's cells from Step 2. The green data bus reaches all rows, but only the enabled row latches it.

Figure — Register file organization

Step 6 — Two read ports at once (replicate the MUX)

WHAT. A real instruction add rd, rs1, rs2 needs both source rows in the same cycle. One MUX gives us one row per cycle, so we build a second independent MUX reading the same grid of cell-outputs, steered by a second address. Now rs1 and rs2 come out together.

WHY replicate, why not reuse? A single MUX can only put one row on its output per cycle — reusing it would serialise the two reads into two cycles, killing single-cycle execution. Two ports means two separate selection paths off the same stored bits. The stored boxes are shared; only the read machinery is duplicated. This is what " read ports" physically means.

PICTURE. The same grid, now with two trapezoid MUXes hanging off it — the burnt-orange one steered by address A gives read port 1, the teal one steered by address B gives read port 2. Both draw from the identical cell outputs at once.

Figure — Register file organization

= number of independent read MUXes; = number of independent write decoders. The 2-and-1 combo is exactly the shape of a 2-operand ALU op.


Step 7 — Why ports cost the SQUARE (the wiring picture)

WHAT. Now the punchline. Each read port lays wires across every cell (to reach whichever row it selects), and each write port does too. So the wires crossing one cell grow with . But adding a port also fattens each cell — it needs more word-lines (horizontal) and more bit-lines (vertical) — so both the height and the width of every cell grow with . Area is height width, so it grows as .

WHY this matters. This square law is the reason register files stay small and few-ported. It is not a scary formula — it is just "both the height and the width of a cell stretch with ports."

PICTURE. One cell drawn twice: left with (thin), right with (fat in both directions). Horizontal word-lines and vertical bit-lines are counted so you can see height and width .

Figure — Register file organization

Step 8 — The degenerate row: R0 hardwired to zero

WHAT. For one special row — register — we build no flip-flops at all. Its read-MUX input is simply tied to constant , and its decoder write-line is left disconnected so writes vanish.

WHY this edge case? It costs nothing (a wire tied low is free) yet gives every program a permanent source of the constant . That collapses several instructions into the ALU you already have: mov rd, rs becomes add rd, rs, r0; nop becomes add r0, r0, r0. The degenerate row is a feature, not a gap.

PICTURE. The R0 row drawn with its boxes greyed/removed; its MUX line pinned to , its write-enable line cut. Compare to a normal row beside it.

Figure — Register file organization

The one-picture summary

Everything above, compressed: the shared grid of cells in the middle; two read MUXes (steered by source addresses A, B) pulling operands out to the ALU; one decoder + WriteEnable routing the ALU result back into the destination row; R0 tied low. Trace green = write path, orange/teal = read paths, plum = address/steering.

Figure — Register file organization
Recall Feynman retelling — the whole walkthrough in plain words

Start with one little box that remembers a or a but only changes on a clock beat (Step 1). Line up of them and they remember a whole number together (Step 2). Stack of those rows and you have a grid — a filing cabinet of numbers (Step 3). To look at one drawer you use a selector switch called a MUX; you dial its address and out comes that one row (Step 4). To change one drawer you use a decoder that lights up exactly one drawer's "listen now" line, and a master WriteEnable that says "actually save it" — so only the chosen drawer latches the incoming value (Step 5). Because a normal add needs two sources at once, you build two selector switches reading the same cabinet, plus one writer (Step 6). Every extra port stretches every drawer in both directions, so cost grows as the square of the ports — that's why we stay stingy (Step 7). Finally, drawer has no boxes at all: it's just wired to forever, giving free mov, nop, and a constant (Step 8). The final picture (Step 9) shows the two read paths and one write path meeting the ALU — that is a register file.


Connections


Which hardware block reads one register onto a bus, and why that block?
A -to-1 MUX, because "select one of many onto a wire" is the definition of a MUX.
Which block routes a value into one register on write?
A -to- decoder ANDed with WriteEnable, because "one wire to one of many" is a decoder + enable.
Why does register-file area scale as ?
Adding a port grows both the height and width of every cell, and area = height × width.
Why does R0 hardwired to 0 cost nothing?
No flip-flops are built for it; the read line is tied low and writes are dropped.