Sequential Logic
Sequential logic: from computing to remembering
Everything so far is combinational: change an input and the output follows immediately, and nothing is remembered. But a CPU must keep a result in a register for the next instruction. What this layer adds is when to update.
01Where memory comes from: two gates locked together
The gates so far are combinational: change an input and the output follows. A number computed by the adder exists on that wire for an instant and is stored nowhere.
Wire two NOTs nose to tail: left output to right input, right output back to left input. In steady state Q = NOT(Q′) and Q′ = NOT(Q), each holding the other — as long as power stays on the state persists. That is bistability, the physical origin of all memory (one bit of SRAM is exactly this).
Why an even number? With an odd number (three NOTs in a ring) every stage inverts and the signal never settles — that is a ring oscillator, not a memory. Only an even number is stable: remembering costs a loop that agrees with itself.
02DFF: the clock as a sluice gate
A bistable remembers, but you cannot control when it changes: nudge it and it flips at once, outside your control. To make writing happen only at a defined instant you need edge triggering — that is the DFF.
DFF (D flip-flop) is the smallest storage element: on each clock edge it latches its input to its output; the rest of the time the output holds, whatever the input does.
The clock is a sluice gate: the water may slosh violently inside the pool (gate delays, glitches), but at the click of the rising edge only the settled level is let through. Discretising time buys the digital system its absolute certainty.
On the left is the symbol: D is the data input, and the small triangle on clk means rising-edge triggered. On the right is the timing: Q takes D only at the rising edge of clk and holds it in between.
CHIP DFF { IN in; OUT out; // latch in to out every clock cycle; hold it otherwise }
A primitive: no PARTS, implemented directly in hardware. The course does not ask you to build it from gates; doing so would be two latches making the “edge”, and the timing only gets subtler.
03Bit: adding a “write or not” switch
With only a DFF it would be written every cycle, but we often want it to hold — so a MUX goes in front: with load=1 it takes the new value, and with load=0 it feeds its own output back, which means no change.
CHIP Bit { IN in, load; OUT out; PARTS: // outv is an internal net: the DFF output drives both out and outv Mux(a=outv, b=in, sel=load, out=x); DFF(in=x, out=out, out=outv); }
With load=0 the DFF output is fed back (outv), which holds the value; load=1 writes in. Note that out is the chip’s output pin and cannot be used as an input.
Note Mux(a=outv, ...): it reads outv, a copy of the DFF output, not the chip’s out pin — “An output pin cannot be an input” is a hard HDL rule. Feeding an output back through an internal net is common in sequential circuits — that is what makes state.
04Register: sixteen Bit chips side by side
A 16-bit register is sixteen Bit chips sharing one load — either all 16 bits are written or none move.
CHIP Register { IN in[16], load; OUT out[16]; PARTS: Bit(in=in[0], load=load, out=out[0]); Bit(in=in[1], load=load, out=out[1]); Bit(in=in[2], load=load, out=out[2]); Bit(in=in[3], load=load, out=out[3]); Bit(in=in[4], load=load, out=out[4]); Bit(in=in[5], load=load, out=out[5]); Bit(in=in[6], load=load, out=out[6]); Bit(in=in[7], load=load, out=out[7]); Bit(in=in[8], load=load, out=out[8]); Bit(in=in[9], load=load, out=out[9]); Bit(in=in[10], load=load, out=out[10]); Bit(in=in[11], load=load, out=out[11]); Bit(in=in[12], load=load, out=out[12]); Bit(in=in[13], load=load, out=out[13]); Bit(in=in[14], load=load, out=out[14]); Bit(in=in[15], load=load, out=out[15]); }
Sixteen Bit chips share one load: either all 16 bits are written, or none move.
05PC: the register that walks forward on its own
A program counter must do three things: +1 (fall through), load a new value (jump) and reset to zero. Three controls form a chain, later ones winning: reset > load > inc.
CHIP PC { IN in[16], load, inc, reset; OUT out[16]; PARTS: // outv is an internal copy of out: an output pin cannot be used as an input Inc16(in=outv, out=incout); // +1 Mux16(a=outv, b=incout, sel=inc, out=o1); Mux16(a=o1, b=in, sel=load, out=o2); Mux16(a=o2, b=false, sel=reset, out=o3); Register(in=o3, load=true, out=out, out=outv); }
Three MUXes in series, the later one winning: reset overrides load, load overrides inc. A copy of the output (outv) feeds Inc16, which gives the increment.
Reading PARTS once explains the priority: three MUXes in series, the later one winning. reset is last, so it overrides everything.
06The clock: one beat, two phases
Every sequential element shares one clock line. Two things happen in a cycle:
- Edge: the clock flips and every DFF and register updates at once — the new values are photographed at that instant.
- Settling: between edges the combinational logic (ALU, adders) computes from the new values, waiting for the next edge to store the result.
So how many cycles an instruction takes depends on how long the combinational path is — that is the physical meaning of clock frequency: however fast the clock, it must wait for the slowest path to finish.