Memory Elements: Latches, Flip-Flops, and Clocking
A combinational circuit holds no state; feeding a circuit's output back to its input creates memory. We build the SR latch from cross-coupled gates, the level-sensitive D latch, and the master/slave edge-triggered D flip-flop, then introduce the clock and the synchronous design discipline, the setup/hold timing window, clock skew, metastability, and the register as n flip-flops sharing one clock.
╌╌╌╌
Every block in the previous lesson was combinational: its output was a function of its current inputs, and the instant the inputs changed the output followed. Such a circuit cannot remember anything — and a computer is mostly memory. This lesson breaks the one rule that kept circuits combinational, the ban on feedback, and discovers that feedback is what stores a bit. From a single cross-coupled pair we build up to the edge-triggered flip-flop and the clocked register that holds a processor's state.
Feedback creates memory
Combinational logic forbade cycles because a path from a gate's output back to its own input creates a loop with no fixed answer — or rather, with a remembered answer. Connect two inverters in a ring and the loop is bistable: it has two stable states, output feeding output feeding back , and the other way around. Left alone it stays in whichever state it is in. That is one bit of storage. The trouble is there is no way to set it; the useful versions add inputs that can flip the loop on demand.
The SR latch
The SR latch is the simplest controllable memory: two cross-coupled NOR gates with inputs S (set) and R (reset). Each gate's output feeds the other's input, closing the loop.
Walk the four input cases. With : the bottom NOR sees the on and outputs , so ; the top NOR then sees two s ( and ) and outputs . : the latch is set, and the loop holds that state even after drops back to . With the mirror argument runs through the top gate and becomes (reset). With : each NOR just passes the loop's current state, so the latch holds whatever it last stored: this is the memory. The fourth case is disallowed: it drives both outputs to , violating the complement, and worse, when both inputs then fall the loop's next state depends on which gate happens to win the race.
Because the output depends on the stored state, an SR latch has a characteristic table — next state as a function of the inputs and the current state — not the plain truth table a combinational gate has:
| action | ||||
|---|---|---|---|---|
| hold | ||||
| hold | ||||
| reset | ||||
| set | ||||
| — | disallowed |
The two hold rows are the whole point: with the next state equals the current one, which is memory written as a table. Set and reset override the stored value regardless of (hence the ), and the last row is the forbidden input. This dependence on — a variable that is also an output — is the formal signature of sequential logic, the very thing the acyclic rule of the previous lesson ruled out. The SR latch stores a bit but with an awkward two-wire, illegal-combination interface; the D latch fixes that.
The D latch: level-sensitive storage
We want a cleaner interface: one data input giving the bit to store, and an enable input saying when to store it. The D latch wraps the SR latch with a little logic so that, while , the output simply follows , and while , the latch holds. Internally and are gated by into the latch's set and reset, so the illegal combination can never arise.
The D latch is level-sensitive (or transparent): it is a window that is open the whole time is high. That transparency is a problem in a feedback system. If a latch's output feeds combinational logic that feeds back to the same latch's input while the window is open, the value can race around the loop more than once in a single enable pulse. We want to capture a value at one instant, not during a whole interval. That is the flip-flop.
The edge-triggered D flip-flop
A D flip-flop samples its data input only at the edge of the clock — the
instant the clock transitions — and holds that value for the entire cycle until the
next edge. It is built from two D latches in series (a master
and a slave
)
driven by opposite clock phases, so at most one is ever transparent: the value
passes through on one phase and is captured on the other. The net effect is that
changes only at, say, the rising edge of the clock, taking on whatever
was at that edge, and is otherwise frozen.
Follow one cycle around. While the clock is low the master is transparent (its output tracks every change of ) but the slave is opaque, so none of that reaches . When the clock rises, the two swap roles in the same instant: the master goes opaque, freezing whatever was at that moment, and the slave goes transparent, passing the frozen value out. Any change of after the edge finds the master closed and goes nowhere. The window of the level-sensitive latch has been squeezed to a point.
The difference is easiest to see on the same waveforms. Feed one D latch (enable clk) and one D flip-flop the same data:
Compare the two output rows. Whenever changes during an open window (its
fall in the second high phase, its rise in the third) the latch passes the
change straight through, mid-cycle; the flip-flop holds the single value it
sampled at the last edge until the next one. The latch
leaks every wiggle of a busy input through to its output; the flip-flop reduces
a whole cycle of activity to one decision per edge. That one-decision-per-edge
behavior is what makes machine state countable: the register's value in cycle
is a well-defined phrase for a flip-flop and a blurry one for a transparent
latch.
The clock and the synchronous discipline
A clock is a square wave alternating and at a fixed frequency; its rising edges mark when every flip-flop updates. The synchronous design discipline organizes a whole machine around one clock: all state is held in edge-triggered flip-flops driven by the same clock, and between edges, combinational logic computes the next state from the current state. On each rising edge, every flip-flop simultaneously latches its next-state input, state advances by one step, and the cycle repeats.
The discipline buys a clean mental model: think one cycle at a time, the state
now
and the combinational logic deciding the state next.
It also imposes a
timing constraint. The data at a flip-flop's input must be stable for a
short window around the clock edge: it must arrive at least a setup time
before the edge and stay at least a hold time after it. If the combinational
logic feeding the flip-flop is too slow to settle within one clock period minus the
setup time, the flip-flop samples a half-formed value. This is why the clock
period (and therefore the clock frequency) is bounded by the slowest
combinational path between flip-flops, the critical path.
The window also completes the clock-period arithmetic the critical path began. For the flip-flop to capture good data every cycle, the clock period must cover everything between one edge and the next input's arrival: the flip-flop's own clock-to-output delay, then the critical path of the combinational logic between registers, then the setup time. With a 20 ps clock-to-output, an 800 ps critical path, and a 40 ps setup, ps: a hair under 1.2 GHz, and every extra gate on the worst path shaves that headroom.
Work the two timing checks a static timing tool actually runs, on a launch flip-flop feeding a capture flip-flop through combinational logic. The setup check (can the data arrive in time for the next edge?) is the slow-path constraint:
where is clock-to-output, the longest combinational path, and the capture clock's lateness (which helps setup — a late capture edge gives the data more time). Plug in , , , and a ps late capture edge: ps. Skew bought ps of margin here.
But the same skew endangers the hold check (does the fast path hold its value long enough at the current edge?):
The new data launched at this edge must not race through the shortest path and
reach the capture flip-flop before its (now late) edge plus hold time. With a fast
path ps, , , and the same ps late
edge: is ? No — the hold check fails by 20 ps,
and the design is broken at any clock frequency, because hold has nothing to do
with . Fixing it means padding the fast path with buffer delay or reducing skew,
not slowing the clock. This asymmetry — skew helps setup on the slow path but hurts
hold on the fast path — is why clock trees are engineered so obsessively, and why
just clock it slower
cannot rescue a hold violation.
Clock skew and metastability
One clock
is a useful fiction. The clock is itself a signal on wires, and it
reaches different flip-flops at slightly different times; the difference between
arrival times at two communicating flip-flops is clock skew. Skew eats the
timing budget from both ends. If the receiving flip-flop's clock arrives
early, the effective period shrinks and a slow path that met setup on paper
now misses it. If it arrives late, a fast path becomes the hazard: the
sender's new value can race through thin logic and reach the receiver before its
delayed edge plus hold time has passed, overwriting the value it was still
trying to capture. Designers attack skew physically, distributing the clock
through balanced trees so every leaf sees the edge within a few picoseconds, and
guard the fast-path case with minimum-delay constraints; a synchronous design's
single instant
is really a window a few picoseconds wide that the clock
network keeps narrow.
Setup and hold can also be violated by something no design discipline controls: an input from outside the clock domain — a keypress, a network signal, another chip on a different clock. Sample such a signal at just the wrong moment and the flip-flop's internal loop is knocked to the unstable balance point between its two stable states, where it can linger, output at neither nor , for an unbounded time before falling to one side arbitrarily. This is metastability. It cannot be prevented, only made improbable: the probability of remaining metastable decays exponentially with waiting time, so designers pass asynchronous inputs through a chain of two (or more) flip-flops — a synchronizer — giving the first stage a full cycle to resolve before the next stage samples it. The cost is a cycle of latency; the alternative is a corrupted bit propagating through the machine.
A register is flip-flops sharing a clock
A single flip-flop stores one bit. An -bit register is just flip-flops side by side, all driven by the same clock, storing an -bit word. On each rising edge the whole word updates at once from the register's data input; between edges it holds, presenting the stored word at its output.
This clocked register is the unit of processor state: the program counter, the condition-code register, and — many of them at once — the register file of the next lesson are all built from exactly this structure. Y86-64's pipeline registers between stages are nothing more than wide D flip-flops that snapshot the whole datapath on each clock edge.
Clocking a real chip
CS:APP presents one storage element (the edge-triggered D flip-flop) and one timing rule. The public design literature adds the parts that make a real chip's clocking work.
Clock distribution dominates power. A modern processor spends a large fraction of
its energy just distributing the clock — the balanced tree mentioned above
toggles every cycle across the whole die. Clock gating is the standard defense:
insert an AND (with a latch to avoid glitches) so a block's flip-flops see clock
edges only when their state can actually change, and idle blocks fall silent. At
the extreme, whole regions are power-gated off. None of this changes the flip-flop;
it changes when the flip-flop is allowed to see an edge, and it is why typical
and peak
power differ so much.
Metastability has a number. The claim that metastability decays exponentially
is quantitative: the failure rate is governed by the mean time
between failures, , where
is the resolution time allowed, a device time constant, and the s the
clock and asynchronous-event rates (Kleeman and Cantoni's analyses in the 1980s
formalized this). A two-flip-flop synchronizer works because giving the first stage
a full cycle makes large enough that the MTBF stretches to years; safety-
critical crossings add a third stage. Getting clock-domain crossing right —
never letting a multi-bit bus cross unsynchronized, using handshakes or Gray-coded
FIFO pointers so at most one bit changes per edge — is a whole sub-discipline, and
the Gray code of lesson 1 reappears in it.
Latches did not disappear. CS:APP prefers flip-flops for their clean
one-decision-per-edge model, but high-performance designs still use
level-sensitive latches deliberately. Time borrowing lets a slow combinational
stage push its lateness past a transparent latch into the next stage's slack,
something a hard flip-flop edge forbids; and pulsed latches shrink the storage
overhead on the critical path. The FPGA world, meanwhile, does not build
flip-flops from gates at all — each logic cell ships with a dedicated D flip-flop
next to its lookup table, so the register
is a fixed silicon resource the mapper
assigns, not something synthesized from NORs. The cross-coupled-pair story of this
lesson is the physics underneath all of them.
The next lesson assembles many registers into a register file and contrasts it with the SRAM and DRAM cells that make up main memory.
╌╌ END ╌╌