Combinational logic has a superpower and a matching disability: it answers instantly, and it
remembers nothing. It cannot count to three, because counting means remembering where you got to.
Every interesting machine — a processor, a traffic light, your GPU-to-be — needs state,
and in synchronous digital design all state lives in one kind of place: the
A D flip-flop is a 1-bit camera: at the instant its clock ticks from 0 to 1 (the
rising edge), it photographs its input D and holds that photo on its
output Q until the next tick — ignoring everything D does in between. A
register is a row of flip-flops sharing one clock. In SystemVerilog, clocked state
has its own block:
Read @(posedge clk) as "at every rising clock edge". Between edges, nothing in this
block happens: the combinational cloud computing count + 1 settles at its leisure,
and only the edge commits the result into the register. That rhythm — settle, then
snap — is the entire discipline of synchronous design. It is why a chip with a billion
gates can be reasoned about at all: time is chopped into cycles, and each cycle asks one tractable
question, "what are the register values at the next edge, given the values at this one?"
Designers live in diagrams like this one — time flowing right, one row per signal:
Notice what the bottom row does not do: Q never reacts to D's
mid-cycle changes. The flip-flop turns messy, continuously-varying signals into a clean sequence of
per-cycle values — analogue chaos in, clockwork out.
The arrow <= is the non-blocking assignment, and it encodes the
deepest fact about registers: at a clock edge, every register in the chip samples its input
simultaneously, using the old values of everything else. The classic proof is the swap:
In software those two lines destroy a. In hardware they exchange two registers,
because <= means "sample now, commit at the edge": both right-hand sides are read
from the pre-edge world, and both photos develop at once. Two flip-flops with crossed wires — of
course they swap. The statement order is irrelevant, just as it was for assign.
Registers also need a way to start from a known value — a reset:
This is a synchronous reset (obeyed at a clock edge); the alternative, an asynchronous
reset (@(posedge clk or negedge rst_n)), yanks the register to zero the moment reset
asserts, clock or no clock. Chips use both; what matters now is that every register gets some
reset story, or it wakes up holding x — the unknown.
"Settle, then snap" is not just a slogan — it is an algorithm, and it is how we will simulate every design in this course. Represent the registers as a state object; write one function computing what the combinational logic feeds each register's input; stepping the clock means calling it and committing the result all at once. Here it is, running a 3-bit counter and a 4-bit shift register side by side:
Three behaviours, one mechanism. The counter climbs and wraps at 8; the shift register slides each
arriving bit along one place per tick; and a/b swap forever — the
non-blocking semantics falls out for free, because next reads only the old state and
the whole new state replaces it in one go. Keep this pattern in mind: the CPU in Module 6 and the
GPU capstone are this exact loop with a richer State.
always_ff @(posedge clk) describes clocked state; the cycle rhythm is
settle (combinational), then snap (edge);<= means all registers read old values and update
together — so a <= b; b <= a; swaps;<= in always_ff, = in
always_comb — never mix.Somewhere on every board sits a sliver of quartz, ringing like a microscopic tuning fork a few million times a second; on-chip circuits multiply that ring up to gigahertz. That one signal then fans out to every register on the die — hundreds of millions of flip-flops — through a distribution network so demanding it gets an entire lesson later in this course. A 3 GHz clock gives each cycle just 333 picoseconds; in that time light itself travels about ten centimetres, roughly the width of the laptop the chip sits in. Every combinational cloud on the chip must settle within that sliver of time, every cycle, billions of times a second, for years — which is why the question "how fast can the clock go?" (the critical path) governs so much of chip design.
Write the swap with ordinary = (the blocking assignment) and it breaks
exactly the way software does: a = b; b = a; executes in order during the edge, so both
end up holding b's value — and worse, the behaviour now depends on statement
order, which is poison in a language describing simultaneous hardware. The
community's iron rule: non-blocking <= in always_ff, blocking
= in always_comb, and never mix them in one block. Follow it
blindly now; you will meet the rare principled exceptions (and the simulation race conditions that
punish violators) in the verification module.