Example Walkthrough: A Serial Frame Receiver Controller
Every prior page in this topic introduced one building block in isolation. This page is different: it's one complete design — the control logic for a serial frame receiver, the kind of circuit that sits behind a UART — built entirely from pieces already covered, applying the six-step FSM design procedure from the previous page start to finish.
The system to build
The receiver watches a serial line RX for a standard framing pattern: a start bit (RX falls to 0), followed by 8 data bits, followed by a stop bit (RX should return to 1). It assumes a bit_tick input pulses once per bit period, aligned to the middle of each bit — generating that pulse from a raw clock is a baud-rate-generation problem outside this page's scope, so bit_tick is treated as an already-available input, the same way earlier pages treated the system clock itself as a given.
Rather than build one FSM state per data bit (which would need 8 nearly-identical states, wasteful and error-prone to keep in sync), the design reuses two Section D building blocks to carry that repetition, leaving the FSM itself to track only the coarse phase of the frame:
- An 8-bit SIPO shift register (Registers & Shift Registers) captures the incoming data bits, shifting one bit in whenever the FSM asserts
shift_enat abit_tick. - A mod-8 synchronous counter (Counters) tracks how many data bits have been shifted in, cleared by the FSM's
count_clearand assertingcount_doneonce it reaches 7.
The FSM is the "brain" — it never touches the data bits directly, only orchestrates when the shift register and counter act, and reports the frame's outcome on done/framing_error. That division of labor (FSM decides when, datapath does the what) is the standard shape of nearly every real control-plus-datapath digital design.
Step 1–2: state diagram and table
Six states cover the whole frame, chosen as a Moore machine — done and framing_error are exactly the kind of registered, glitch-free outputs an external block consuming them would want, per the Moore/Mealy tradeoff from FSM Fundamentals:
| State | Meaning | Moore output |
|---|---|---|
| IDLE | Waiting for the line to fall (start of a frame) | shift_en=0, count_clear=1 |
| START | Absorbing the start bit for one bit-time | shift_en=0, count_clear=1 |
| DATA | Shifting in data bits, one per bit_tick, until the counter says 8 are in | shift_en=1, count_clear=0 |
| STOP | Sampling the line for a valid stop bit | shift_en=0, count_clear=0 |
| DONE | Frame received correctly — pulse done for one cycle | done=1 |
| ERROR | Stop bit missing — pulse framing_error for one cycle | framing_error=1 |
Current state | Condition | Next state
IDLE | RX=1 | IDLE (still idle)
IDLE | RX=0 | START (start bit seen)
START | bit_tick | DATA (start bit absorbed)
DATA | bit_tick, !count_done | DATA (still shifting)
DATA | bit_tick, count_done | STOP (8 bits captured)
STOP | bit_tick, RX=1 | DONE (valid stop bit)
STOP | bit_tick, RX=0 | ERROR (missing stop bit)
DONE | (unconditional) | IDLE (one-cycle pulse, then reset)
ERROR | (unconditional) | IDLE (one-cycle pulse, then reset)
Every row that isn't listed (e.g. START with no bit_tick yet) implicitly holds in the current state — the same "stay put until the trigger condition arrives" pattern used throughout the state diagrams two pages back.
Step 3: minimization and assignment
No two states in the table above share both the same output and the same transition behavior, so all six are already distinct — there's nothing to merge; the minimization step of the six-step procedure is a no-op here, which is itself worth checking explicitly rather than assuming, since skipping straight past it on a larger design is exactly how a redundant state slips through unnoticed.
Six states need 3 bits (⌈log₂6⌉ = 3). Given the FSM's structure — a mostly linear walk IDLE → START → DATA → STOP → {DONE|ERROR} → IDLE — a Gray-code assignment is a natural fit, per the tradeoffs from State Minimization & Assignment: most transitions are between adjacency-ordered neighbors, so most state changes flip only a single bit.
| State | Code (Q2 Q1 Q0) |
|---|---|
| IDLE | 000 |
| START | 001 |
| DATA | 011 |
| STOP | 010 |
| DONE | 110 |
| ERROR | 111 |
Steps 4–5: excitation equations
Each next-state flip-flop input is derived directly from the state table and codes above, then minimized exactly as in earlier pages — for example, Q0's next value is 1 in the START, DATA (while looping), and ERROR rows:
Q0(next) = (IDLE · RX') + (START) + (DATA · bit_tick · count_done')
+ (DATA · bit_tick' ) + (STOP · bit_tick · RX') + ERROR
Substituting each state name for its code and simplifying with a K-map (six variables here — three state bits plus RX, bit_tick, count_done — handled the same way as any other multi-variable minimization from Logic Minimization, just with more grouping work) yields the final gate-level expression for Q0's D-input. Q1 and Q2 follow the same derivation from their own rows, and shift_en, count_clear, done, and framing_error follow directly from their state table column, since as Moore outputs they depend on the state bits alone.
How it all connects
Tracing one full frame: the FSM sits in IDLE (Q=000) with count_clear=1, holding the counter at zero, until RX falls — moving to START (001). One bit_tick later (having absorbed the start bit), it advances to DATA (011) and asserts shift_en, so every subsequent bit_tick shifts one more bit into the SIPO register and increments the counter. After the eighth bit_tick, count_done goes high, moving the FSM to STOP (010), where it samples RX on the next bit_tick: a 1 sends it to DONE (110), pulsing done for one cycle before an unconditional return to IDLE; a 0 sends it to ERROR (111) instead, pulsing framing_error. Every arrow in that walk is a mechanism from an earlier page: the state register and its clocking discipline come from Latches & Flip-Flops and Timing, Setup/Hold & Metastability, the SIPO shift register and mod-8 counter are exactly the blocks built in Registers & Shift Registers and Counters, and the FSM itself is nothing more than the state-diagram-to-gates procedure from the previous two pages, applied once, end to end.
This topic covers the foundation of digital logic design — number systems and codes, Boolean algebra and minimization, combinational building blocks, sequential storage and timing, and finite state machines, enough to design and reason about real synchronous circuits like the one above. A few things it deliberately doesn't cover, each substantial enough to be its own topic: memory and programmable logic (ROM/RAM/PLA/FPGA fabric), logic family electrical characteristics (TTL/CMOS), asynchronous sequential circuit design, and static/dynamic hazards. Once the fundamentals here feel solid, those are the natural next steps — and the HDL topics elsewhere on this site (Verilog, SystemVerilog, UVM) are exactly where these same concepts get expressed in code and verified.