Skip to main content

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_en at a bit_tick.
  • A mod-8 synchronous counter (Counters) tracks how many data bits have been shifted in, cleared by the FSM's count_clear and asserting count_done once it reaches 7.

RX feeds both the Control FSM and the SIPO shift register; the FSM drives shift_en/count_clear into the shift register and counter, the mod-8 counter reports count_done back to the FSM, bit_tick drives the FSM, and the FSM outputs done/framing_error

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:

StateMeaningMoore output
IDLEWaiting for the line to fall (start of a frame)shift_en=0, count_clear=1
STARTAbsorbing the start bit for one bit-timeshift_en=0, count_clear=1
DATAShifting in data bits, one per bit_tick, until the counter says 8 are inshift_en=1, count_clear=0
STOPSampling the line for a valid stop bitshift_en=0, count_clear=0
DONEFrame received correctly — pulse done for one cycledone=1
ERRORStop bit missing — pulse framing_error for one cycleframing_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.

StateCode (Q2 Q1 Q0)
IDLE000
START001
DATA011
STOP010
DONE110
ERROR111

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.

Beyond the basics

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.