---
title: Register Files and Random-Access Memory
module: Digital Logic
moduleNumber: 3
lessonNumber: 5
order: 305
summary: >
  Storage organized for access by address. We build the register file (a small
  bank of registers with addressed read ports and clocked write ports, the exact
  structure Y86-64's decode and write-back stages use), then descend to the SRAM
  and DRAM cells of main memory, why one is fast and dear and the other dense and
  slow, and how a row decoder picks a word out of a memory array.
topics: [Digital Logic]
sources:
  - book: Bryant & O'Hallaron
    ref: "CS:APP — §4.2 Logic Design and the Hardware Control Language"
  - book: Bistriceanu
    ref: "Computer Architecture Notes — §9 Main Memory"
---

The [last lesson](/computer-architecture/digital-logic/memory-elements-latches-flip-flops-and-clocking)
built a register: $n$ flip-flops on a shared clock holding one word. A processor
has many such registers and a vast main memory, and the interesting engineering is
how you reach **one** word out of many by its **address**. This final Digital Logic
lesson builds the two storage structures a machine actually uses, the **register
file** and **random-access memory**, and the addressing logic, built from the
[decoders](/computer-architecture/digital-logic/multiplexers-decoders-and-the-alu)
and registers of earlier lessons, that selects a word from each.

## The register file

A **register file** is a small bank of registers (Y86-64 has fifteen 64-bit
program registers) addressed by a **register identifier** (a small number naming
which register). What makes it more than an array of registers is its **port**
structure: it serves multiple reads and accepts a write at once, the way an
instruction needs.

Y86-64's register file has **two read ports** and **two write ports**. Each read
port takes a register id (`srcA`, `srcB`) and combinationally drives out that
register's current contents (`valA`, `valB`): no clock needed, the data is just
there. Each write port takes a register id and a data word and is **clocked**: on
the rising edge, the addressed register loads the data. The write ports are named
for what they carry — port E takes a computed result (`dstE`, `valE`), port M a
value read from memory (`dstM`, `valM`). Why two? One instruction can produce
both kinds of result at once: `popq %rax` must update `%rsp` with the incremented
pointer (through port E) _and_ load `%rax` from memory (through port M) in the
same cycle.

$$
% caption: The Y86-64 register file: two combinational read ports and two clocked
% caption: write ports. The read ports select valA, valB from the register ids
% caption: srcA, srcB; on the clock edge, port E loads valE into register dstE and
% caption: port M loads valM into register dstM.
\begin{tikzpicture}[font=\footnotesize,>=stealth]
  \definecolor{acc}{HTML}{2348F2}
  \node[draw, fill=acc!8, minimum width=34mm, minimum height=40mm,
        align=center] (rf) at (0,0) {register\\f\/ile};
  % read port A (top-left in, top-right out)
  \node[anchor=east] at (-3.2,1.55) {srcA};
  \draw (-3.2,1.55) -- (rf.west |- 0,1.55);
  \draw (rf.east |- 0,1.55) -- (3.2,1.55) node[anchor=west,text=acc] {valA};
  % read port B
  \node[anchor=east] at (-3.2,1.0) {srcB};
  \draw (-3.2,1.0) -- (rf.west |- 0,1.0);
  \draw (rf.east |- 0,1.0) -- (3.2,1.0) node[anchor=west,text=acc] {valB};
  % write port E: address and data in
  \node[anchor=east] at (-3.2,0.05) {dstE};
  \draw (-3.2,0.05) -- (rf.west |- 0,0.05);
  \node[anchor=east] at (-3.2,-0.45) {valE};
  \draw (-3.2,-0.45) -- (rf.west |- 0,-0.45);
  % write port M: address and data in
  \node[anchor=east] at (-3.2,-1.1) {dstM};
  \draw (-3.2,-1.1) -- (rf.west |- 0,-1.1);
  \node[anchor=east] at (-3.2,-1.6) {valM};
  \draw (-3.2,-1.6) -- (rf.west |- 0,-1.6);
  % clock input from below with edge wedge
  \draw (0,-3.3) node[anchor=north] {clk} -- (rf.south);
  \draw (-0.2,-2.15) -- (0,-2.0) -- (0.2,-2.15);
  \node[anchor=west, align=left] at (3.2,-1.1)
    {\footnotesize reads: combinational\\writes: on clock edge};
\end{tikzpicture}
$$

This is the register file Y86-64 uses. In the **decode** stage the
processor reads the two source operands by their ids; in the **write-back** stage
it writes results on the clock edge — an ALU result through port E, a loaded
value through port M, and for `popq` both at once. Two subtleties make it work
cleanly. First, the read is **combinational** so an instruction can fetch its
operands within the same cycle, while the writes are **clocked** so state
advances exactly once per cycle. Second, when the same cycle reads and writes the
same register, the read returns the **old** value (the write takes effect at the
edge, after the read has been used) — a timing detail the pipeline must respect.
A third rule covers the corner where **both** write ports name the same register:
port M wins, and the instruction that needs the rule is `popq %rsp`, whose port-E
write (the incremented pointer) and port-M write (the old stack top) collide on
`%rsp` — the ISA says the memory value prevails, as the
[SEQ stage tables](/computer-architecture/processor-design/the-seq-stages) will
rely on.

> **Definition (Register file).** A small, fast, multi-ported memory addressed by
> register id. Read ports are **combinational** (apply an id, the data appears);
> write ports are **clocked** (the addressed register loads its data on the
> active clock edge). It is built from registers plus a decoder on each write
> address and multiplexers on the read addresses.

Open the box and there is nothing new inside: registers, decoders, and muxes
from the last three lessons, assembled into an addressed memory. Each **read
port** is a word-wide multiplexer: the register id `srcA` is its select code,
steering the chosen register's output onto `valA`. Each **write port** is the
[decoder](/computer-architecture/digital-logic/multiplexers-decoders-and-the-alu)
put to work: `dstE` drives it, exactly one of its one-hot outputs goes high, and
that line, ANDed with the clock, becomes the load-enable of the one register
allowed to capture `valE` at the edge. Every other register sees its enable low
and holds. Port M repeats the structure — a second decoder on `dstM` whose
one-hot line ORs into the same enables and steers a small mux in front of each
register, so that when both decoders select one register, `valM` is the value
captured.

$$
% caption: Inside the register file (one read port and one write port shown; the
% caption: second read port is an identical mux, the second write port an identical
% caption: decoder). The write decoder turns dstE into a one-hot enable, gated by
% caption: clk, so exactly one register loads valE at the edge; the read mux steers
% caption: the register named by srcA onto valA, no clock involved.
\begin{tikzpicture}[font=\footnotesize,>=stealth,
  reg/.style={draw, fill=acc!8, minimum width=16mm, minimum height=8mm,
              inner sep=1pt},
  tall/.style={draw, minimum width=14mm, minimum height=44mm, inner sep=2pt,
               align=center}]
  \definecolor{acc}{HTML}{2348F2}
  \node[reg] (r0) at (0,3.3) {$R_0$};
  \node[reg] (r1) at (0,2.2) {$R_1$};
  \node[reg] (r2) at (0,1.1) {$R_2$};
  \node[reg] (r3) at (0,0.0) {$R_3$};
  \node[tall] (dec) at (-3.2,1.65) {write\\decoder};
  \node[tall, fill=acc!8, minimum width=11mm] (m) at (3.7,1.65) {read\\mux};
  % enable lines from the decoder into each register (upper entry)
  \foreach \y in {3.3, 2.2, 1.1, 0.0}{
    \draw (-2.5,\y+0.15) -- (-0.8,\y+0.15);
  }
  % valE bus down the left, tapping each register (lower entry)
  \node[anchor=south] at (-1.65,4.35) {valE};
  \draw (-1.65,4.35) -- (-1.65,-0.15);
  \foreach \y in {3.3, 2.2, 1.1}{
    \draw (-1.65,\y-0.15) -- (-0.8,\y-0.15);
    \fill (-1.65,\y-0.15) circle (1.1pt);
  }
  \draw (-1.65,-0.15) -- (-0.8,-0.15);
  % dstE and clk into the decoder
  \draw (-5.2,1.65) node[anchor=east] {dstE} -- (-3.9,1.65);
  \draw (-3.2,-1.35) node[anchor=north] {clk} -- (-3.2,-0.55);
  % register outputs straight into the read mux
  \foreach \y in {3.3, 2.2, 1.1, 0.0}{
    \draw (0.8,\y) -- (3.15,\y);
  }
  % select and output of the read mux
  \draw (3.7,-1.35) node[anchor=north] {srcA} -- (3.7,-0.55);
  \draw (4.25,1.65) -- (5.4,1.65) node[anchor=west, text=acc] {valA};
\end{tikzpicture}
$$

The two halves obey different clocks because reads and writes have different
semantics. The contents of $R_2$ are defined at every instant, so the read mux is
pure combinational logic and `valA` follows `srcA` within a propagation delay.
Writing $R_2$ changes state, and state changes only at clock edges — so the
enable is gated with the clock, and between edges the decoder's output can
wiggle freely without corrupting anything. Y86-64's register file scales this
picture to fifteen 64-bit registers: 4-bit ids, two 4-to-16 write decoders (one
code, `0xF`, means "no register"), and two read muxes, each fifteen words wide.

Trace one cycle of `addq %rax, %rbx` (compute `%rbx = %rbx + %rax`, storing
through port E). Say `%rax` is id $0$ holding
$5$ and `%rbx` is id $3$ holding $8$, and the ALU result is $13$.

- **Decode (combinational read).** `srcA` $= 0$ and `srcB` $= 3$ drive the two
  read muxes. Mux A selects register $R_0$'s output onto `valA` $= 5$; mux B
  selects $R_3$ onto `valB` $= 8$. No clock — the values are on the wires as soon
  as the ids settle.
- **Execute.** The ALU adds `valA` and `valB` to $13$, which becomes `valE`.
- **Write-back (clocked).** `dstE` $= 3$ drives the write decoder; its one-hot
  output raises **only** line $3$. That line ANDed with the clock forms $R_3$'s
  load enable; every other register sees enable $= 0$. On the rising edge $R_3$
  captures `valE` $= 13$ and the other fourteen hold. Port M is idle this cycle
  (`dstM` $=$ `0xF`, no register), so its decoder raises no line.

Notice the read used the **old** `%rbx` $= 8$, correctly: the write to $R_3$ lands
at the edge, after the mux already delivered the operand. Had this been
`popq %rax` instead, both ports would fire — port E writing the incremented
`%rsp`, port M writing the loaded `%rax` — and the "port M wins on a collision"
rule would decide any tie. One decoder, one mux per port, and the timing
discipline of the previous lesson: nothing more.

## SRAM and DRAM: two ways to store a bit

The register file is small and fast because each bit is a full flip-flop. Main
memory needs **billions** of bits, and a flip-flop per bit would be impossibly
large and power-hungry. Memory technology offers two cheaper cells, trading speed
for density.

- A **static RAM (SRAM)** cell stores a bit in a small bistable latch, typically
  **six transistors** (6T): a cross-coupled inverter pair holding the bit, plus two
  access transistors connecting it to the bit lines. It holds its value as long as
  power is applied, with no refresh, and is **fast**. But six transistors per bit
  is expensive in area, so SRAM is used where speed matters most: registers and
  caches.
- A **dynamic RAM (DRAM)** cell stores a bit as **charge on a tiny capacitor**,
  read and written through a **single** access transistor: **one transistor plus
  one capacitor** (1T1C). One transistor per bit makes DRAM enormously **dense and
  cheap**, which is why main memory is DRAM. The cost is that the capacitor
  **leaks**: the charge decays in milliseconds, so every cell must be **refreshed**
  (read and rewritten) periodically, and a read is **destructive** (it drains the
  capacitor and must be restored). DRAM is therefore slower than SRAM.

$$
% caption: The two cells, transistor by transistor. SRAM (left) holds its bit in a
% caption: cross-coupled inverter loop (4 transistors) reached through two access
% caption: transistors gated by the word line: six in all, stable while powered.
% caption: DRAM (right) is one access transistor and one capacitor: a fraction of
% caption: the area, but the charge leaks away and must be refreshed.
\begin{tikzpicture}[font=\footnotesize,>=stealth]
  \definecolor{acc}{HTML}{2348F2}
  % ---- SRAM cell (left) ----
  % cross-coupled inverter loop between storage nodes A (-0.7,0) and B (1.5,0)
  \draw (-0.7,0) -- (-0.7,0.55) -- (-0.15,0.55);
  \draw (-0.15,0.3) -- (-0.15,0.8) -- (0.45,0.55) -- cycle;
  \draw (0.55,0.55) circle (0.09);
  \draw (0.64,0.55) -- (1.5,0.55) -- (1.5,0);
  \draw (1.5,0) -- (1.5,-0.55) -- (0.95,-0.55);
  \draw (0.95,-0.3) -- (0.95,-0.8) -- (0.35,-0.55) -- cycle;
  \draw (0.25,-0.55) circle (0.09);
  \draw (0.16,-0.55) -- (-0.7,-0.55) -- (-0.7,0);
  \fill (-0.7,0) circle (1.1pt);
  \fill (1.5,0) circle (1.1pt);
  % access transistors: wire through, gate plate above, gate wire to word line
  \draw (-2.3,0) -- (-0.7,0);
  \draw (-1.72,0.16) -- (-1.28,0.16);
  \draw (-1.5,0.16) -- (-1.5,1.5);
  \draw (1.5,0) -- (3.1,0);
  \draw (2.08,0.16) -- (2.52,0.16);
  \draw (2.3,0.16) -- (2.3,1.5);
  % word line across the top
  \draw (-2.9,1.5) node[anchor=east] {word line} -- (2.3,1.5);
  \fill (-1.5,1.5) circle (1.1pt);
  % bit lines
  \draw (-2.3,1.0) -- (-2.3,-1.1);
  \node[anchor=north] at (-2.3,-1.15) {bit line $BL$};
  \draw (3.1,1.0) -- (3.1,-1.1);
  \node[anchor=north] at (3.1,-1.15) {bit line $\overline{BL}$};
  \node[anchor=north, text=acc] at (0.4,-1.75) {SRAM: 6 transistors};
  % ---- DRAM cell (right) ----
  \draw (6.6,1.0) -- (6.6,-1.1);
  \node[anchor=north] at (6.6,-1.15) {bit line};
  \draw (6.6,0) -- (8.0,0);
  \fill (6.6,0) circle (1.1pt);
  \draw (6.98,0.16) -- (7.42,0.16);
  \draw (7.2,0.16) -- (7.2,1.5);
  \draw (5.9,1.5) -- (8.6,1.5) node[anchor=west] {word line};
  \fill (7.2,1.5) circle (1.1pt);
  % capacitor plates and ground
  \draw (8.0,0.3) -- (8.0,-0.3);
  \draw (8.2,0.3) -- (8.2,-0.3);
  \draw (8.2,0) -- (8.5,0) -- (8.5,-0.45);
  \draw (8.3,-0.45) -- (8.7,-0.45);
  \draw (8.37,-0.57) -- (8.63,-0.57);
  \draw (8.44,-0.69) -- (8.56,-0.69);
  \node[anchor=north, text=acc] at (7.5,-1.75) {DRAM: 1 transistor + 1 capacitor};
\end{tikzpicture}
$$

Reading each cell explains its speed. Assert the SRAM cell's word line and its
two access transistors connect the inverter loop, which is actively **driving**
both storage nodes, straight onto the bit-line pair; the lines split apart
quickly and cleanly, and the cell's state is untouched. Assert the DRAM cell's
word line and all that reaches the bit line is the charge of one tiny capacitor,
tens of femtofarads dumped onto a long wire with many times its capacitance. The
resulting voltage nudge is millivolts, so a **sense amplifier** must recover the
value and then — because dumping the charge destroyed it — write it back.
Every DRAM read is really a read-plus-restore, and that restore is part of why
DRAM access takes tens of nanoseconds while on-chip SRAM answers in a fraction
of one.

Put numbers on the DRAM read to see why the sense amplifier is unavoidable. A
storage capacitor holds around $C_s = 25$ fF; the bit line it must drive is a long
wire with $C_{BL} \approx 250$ fF, ten times larger. When the access transistor
opens, the cell's charge redistributes by **charge sharing** across the combined
capacitance. If the bit line is pre-charged to the midpoint $V_{DD}/2$ and the cell
holds a full $V_{DD}$ (a stored $1$), the new bit-line voltage settles to roughly

$$
V_{BL} = \frac{C_{BL}\,(V_{DD}/2) + C_s\,V_{DD}}{C_{BL} + C_s}
       = \frac{V_{DD}}{2} + \frac{C_s}{C_{BL}+C_s}\cdot\frac{V_{DD}}{2},
$$

a nudge of only $\tfrac{25}{275}\cdot\tfrac{V_{DD}}{2} \approx 0.045\,V_{DD}$ above
the midpoint — tens of millivolts on a $1$ V supply. That tiny swing is why a
**differential sense amplifier**, comparing the bit line against an untouched
reference at exactly $V_{DD}/2$, is what actually resolves the bit; the cell alone
cannot swing the line to a logic level. And because the charge sharing left the
cell sitting near $V_{DD}/2$ too, its stored $1$ is now half-gone: the amplifier
must **write the row back** as part of the read. The SRAM cell, by contrast,
_actively drives_ both bit lines from its inverter loop, splitting them apart in
tens of picoseconds and leaving its own state untouched — the whole speed-versus-
density trade in two numbers.

The leak sets the other clock. A DRAM capacitor loses its charge in tens of
milliseconds, so every cell must be refreshed before its bit fades. The standard contract is that every row survives 64 ms, so a chip
with $8192$ rows per bank refreshes one row about every $7.8\ \mu s$, an
obligation the memory controller schedules forever in the background. Each
refresh occupies the bank for a few tens of nanoseconds, so the tax is small,
around a percent of the memory's time, but it is one more reason "dynamic"
memory is cheap per bit and never quite as responsive as static.

The two form a hierarchy: SRAM's speed serves the processor's
hottest storage (registers, on-chip caches), and DRAM's density serves the bulk of
main memory. The whole **memory hierarchy** is a later topic, but its physical root
is right here — the 6T cell is fast and dear, the 1T1C cell is slow and cheap.

> **Definition (SRAM vs DRAM).** **SRAM** stores each bit in a 6-transistor latch:
> fast, persistent while powered, low density — used for registers and caches.
> **DRAM** stores each bit as charge on one capacitor behind one access transistor:
> high density, low cost, but it leaks and so must be refreshed and restored after
> each (destructive) read — used for main memory.

## A memory array and address decoding

Memory cells are laid out in a two-dimensional **array** of rows and columns; each
**row** is one addressable word. To read or write a word you must select its row,
and that is the job of a **row decoder** — the same $n$-to-$2^n$ decoder from the
adder lesson. The address feeds the decoder, which asserts exactly one of its
output lines, the **word line** for that row, turning on the access transistors
of every cell in that row and connecting them to the column **bit lines** that
carry the data in or out.

$$
% caption: A memory array with a row decoder. The address selects one word line via
% caption: the decoder; that row's cells drive (read) or accept (write) data on the
% caption: column bit lines, which carry the selected word out.
\begin{tikzpicture}[font=\footnotesize,>=stealth,
  cellbox/.style={draw, minimum width=8mm, minimum height=7mm, inner sep=0pt}]
  \definecolor{acc}{HTML}{2348F2}
  % row decoder on the left
  \node[draw, fill=acc!8, minimum width=15mm, minimum height=30mm,
        align=center] (dec) at (-2.8,0) {row\\decoder};
  \node[anchor=south] at (-2.8,1.7) {address};
  \draw (-2.8,1.65) -- (dec.north);
  % a 4x4 array of cells
  \foreach \r in {0,1,2,3}{
    \foreach \c in {0,1,2,3}{
      \node[cellbox] (m\r\c) at (\c*0.95, 1.2-\r*0.85) {};
    }
    % word line from decoder into this row (one per row, no crossings)
    \draw (dec.east |- 0,1.2-\r*0.85) -- (m\r0.west);
  }
  % highlight the selected row (row 1) word line
  \draw[acc, thick] (dec.east |- 0,0.35) -- (m10.west);
  % bit lines down each column out the bottom
  \foreach \c in {0,1,2,3}{
    \draw (\c*0.95,-1.7) -- (\c*0.95,-2.6);
  }
  \node[anchor=north, text=acc] at (1.4,-2.8) {bit lines (selected word out)};
\end{tikzpicture}
$$

Reads are **combinational** in spirit (assert an address, and after the decoder
and bit lines settle the word appears) while writes are gated by a write-enable and
the clock so a word changes only when intended, exactly mirroring the register
file's read/write asymmetry but at the scale of a whole memory.

At real sizes the geometry has to bend. A one-gigabit chip holds $2^{30}$ cells,
and a single decoder with $2^{30}$ outputs is not buildable — nor is routing a
billion word lines. So the array is kept roughly **square**: $2^{15}$ rows of
$2^{15}$ cells, a 15-bit row decoder down one side, and a 15-bit **column**
selection (a wide mux) along the bottom that picks the wanted bits out of the
selected row. The address splits in half, and DRAM chips push the split onto the
pins: the same address wires carry the row half and the column half in two
beats, which is why DRAM protocol speaks of a row address strobe and a column
address strobe. Better still, the sense amplifiers that read the selected row
form a **row buffer** holding all $2^{15}$ bits of it; further accesses that hit
the same row skip the slow array access entirely and pay only the column mux.
$$
% caption: How a gigabit DRAM spends its 30 address bits. The row half drives the
% caption: row decoder, copying one full row into the row buffer through the sense
% caption: amplifiers; the column half muxes the wanted word out of the buffer.
% caption: Accesses that hit the already-open row skip the slow array step.
\begin{tikzpicture}[font=\footnotesize,>=stealth]
  \definecolor{acc}{HTML}{2348F2}
  \node[draw, fill=acc!8, minimum width=13mm, minimum height=30mm,
        align=center] (dec) at (0,2.0) {row\\decoder};
  \node[draw, minimum width=36mm, minimum height=30mm, align=center]
    (arr) at (3.6,2.0) {cell array\\$2^{15}$ rows of\\$2^{15}$ cells};
  \foreach \y in {2.8, 2.0, 1.2}{
    \draw (0.65,\y) -- (1.8,\y);
  }
  \node[draw, fill=acc!8, minimum width=36mm, minimum height=8mm]
    (buf) at (3.6,-0.2) {row buf\/fer};
  \node[draw, minimum width=22mm, minimum height=8mm]
    (cm) at (3.6,-1.7) {column mux};
  \draw[->] (3.6,0.5) -- (3.6,0.2);
  \draw[->] (3.6,-0.6) -- (3.6,-1.3);
  \draw (-2.4,2.0) node[anchor=east] {row bits (15)} -- (-0.65,2.0);
  \draw (0.4,-1.7) node[anchor=east] {column bits (15)} -- (2.5,-1.7);
  \draw[->] (4.7,-1.7) -- (5.8,-1.7) node[anchor=west, text=acc] {data};
  \node[anchor=west, text=acc] at (5.6,-0.2) {stays open for row hits};
\end{tikzpicture}
$$

Locality in the address stream becomes speed for free — a theme the
[memory-hierarchy module](/computer-architecture/memory-hierarchy/locality)
builds an entire architecture on. Every refinement keeps the core idea:
a decoder turns an address into a one-hot selection, and everything else is
engineering around wire length and charge.

## Ports, error correction, and real DRAM

The 6T cell, the 1T1C cell, and the row decoder are the enduring core; the public
memory literature wraps three layers of engineering around them.

**Ports cost area, quadratically.** The register file above shows one read mux and
one write decoder per port, which understates the price. Each _additional_ port
adds a word line and bit line pair to **every** cell, so an $r$-read, $w$-write
SRAM cell grows roughly with $(r + w)^2$ in area — the wires dominate. This is why
a wide superscalar core, whose several ALUs may each need two operands per cycle,
cannot simply keep adding ports: real designs **bank** the register file, replicate
it (two identical copies, each serving half the read ports), or clock it at double
rate so one physical port serves two logical reads. The clean two-read/two-write
picture is Y86-64's teaching simplification.

**Error-correcting memory.** A stored bit can flip from a cosmic ray or a leaky
cell, so server memory adds **error-correcting codes**: a 64-bit word carries 8
extra bits computed as a **SEC-DED** Hamming code (Hamming, 1950), enough to
_correct_ any single-bit error and _detect_ any double-bit one. The extra bits ride
alongside the data through the same array and decoder; the correction logic is a
parity tree — the XOR-and-AND net of
[lesson 2](/computer-architecture/digital-logic/combinational-logic-and-hcl) — on
the read path. Caches use the same trick, and the "beat" structure of DRAM makes
room for the check bits almost for free.

**DRAM is also a protocol.** The row-buffer split above is the seed of
modern DRAM. A chip is divided into **banks**, each with its own row buffer, so one
bank can be servicing a column read while another is opening a fresh row —
**bank-level parallelism** that hides the slow array access. **DDR** ("double data
rate") transfers on both clock edges, and the memory controller schedules the
row-activate / column-read / precharge commands to keep the banks busy and honor the
refresh contract — a small scheduler whose job is turning the address stream's
locality into open-row hits. Jacob, Ng, and Wang's _Memory Systems_ (2007) is the
public reference. Every layer still bottoms out in this lesson: a decoder turns an
address into a one-hot word-line, and a cell either drives its bit or dribbles a
capacitor's charge onto a sense amplifier.

> **Takeaway.** A register file is a small multi-ported memory: combinational read
> ports (a mux on the register id) and clocked write ports (a decoder + clock enable
> each, port M winning when both name one register) — the structure Y86-64's decode
> and write-back stages use. Main memory trades the
> flip-flop for cheaper cells: the 6T SRAM cell drives its bit and answers fast;
> the 1T1C DRAM cell packs six times denser but leaks, reads destructively, and
> must be refreshed on a 64 ms contract. Both organize cells into an array whose
> row decoder turns an address into one selected word, with big DRAMs splitting
> the address into row and column halves and holding the open row in a buffer.

This closes the Digital Logic module: from a single transistor switch, through
gates, combinational function units, and clocked memory, to the addressed storage a
processor reads and writes. The next module puts these pieces together into a
datapath that fetches and executes instructions.
