8.3 D Flip-Flop: The Universal Data-Register Element
State the D flip-flop's characteristic equation and excitation table, and explain why it is the default flip-flop type in modern synchronous RTL design.
The D (Data, or Delay) flip-flop is the simplest possible edge-triggered storage element: it has exactly one data input, D, and its entire behavior is captured in a single sentence — whatever value D holds at the active clock edge becomes the new Q. There is no forbidden state to worry about (unlike SR), no toggle ambiguity to reason through (unlike JK or T) — just a direct, unconditional copy at the sampling instant. That simplicity is precisely why the D flip-flop is the default, near-universal choice in modern digital design, and why every register, pipeline stage, and finite-state-machine state element you will meet for the rest of this course is built from D flip-flops.
Core Concept: Q(next) = D at the Active Edge
The D flip-flop's entire behavior is the equation Q(next) = D, applied at the active clock edge: at the rising (or falling, depending on the specific device) edge, the flip-flop samples whatever value is currently present on D and copies it directly to Q, holding that value steady until the next active edge. There is no dependency on the current value of Q at all in the next-state equation — unlike JK or T, D's next state is entirely determined by the input, with zero influence from the flip-flop's own current state.
This directly matches the module's example: if D=1 at the sampling edge, the next stored state becomes 1 — full stop, no other input, no conditional behavior, no exceptions. That directness is exactly what makes a D flip-flop feel like a single bit of "memory with a clock-controlled write enable": whatever you present at D right before the edge is exactly what gets written to Q.
• Characteristic equation: Q(next) = D. • No dependency on the previous value of Q — unlike JK (toggle) or SR (hold/forbidden logic). • No forbidden input combination exists — D can only ever be 0 or 1, and both produce a fully well-defined result.
Step-by-Step: Sample-and-Hold Across Multiple Clock Cycles
To trace Q across several clock cycles, simply read off the value of D at each active edge in turn — Q takes on that exact value and holds it steady until the next active edge, completely ignoring anything D does in between edges (exactly the general edge-triggered behavior established in Topic 7.4, now specialized to the D flip-flop's specific rule). Because there is no feedback dependency on Q in the equation itself, tracing a D flip-flop is the simplest possible flip-flop-tracing exercise: no need to track "toggle" state or worry about forbidden combinations — just read D at each edge.
Practical Application: Why D Dominates Modern RTL Design
In hardware description languages like Verilog and SystemVerilog, sequential logic is almost always written using D-flip-flop semantics directly: an always_ff @(posedge clk) block with a nonblocking assignment (q <= d;) describes exactly the D flip-flop's Q(next) = D behavior, and standard-cell libraries used in ASIC synthesis overwhelmingly provide D flip-flops as the base sequential primitive — other behaviors (toggle, set/reset conditioning) are built by wrapping combinational logic around a D flip-flop's input rather than by using a native JK or T cell, which is precisely the technique formalized in Topic 8.6 (Flip-Flop Conversion).
Nonblocking assignment (<=) matters specifically because it models all the right-hand sides of every always_ff block as being evaluated simultaneously using the OLD values of every signal, then updating all the left-hand sides together — exactly mirroring how real parallel hardware flip-flops all sample their D inputs at the same instant using the state that existed just before the edge, rather than a value some other statement in the same block might have already updated moments earlier in simulation order.
D flip-flop characteristic equation and excitation table
Characteristic equation: Q(next) = D (evaluated at the active clock edge)
Excitation table (given Q -> Q(next), required D):
Q | Q+ | D
--|----|---
0 | 0 | 0
0 | 1 | 1
1 | 0 | 0
1 | 1 | 1
(Note: D always simply equals the desired Q+ — no don't-cares, unlike SR/JK.)Code Examples
Apply the characteristic equation directly: Q(next) = D = 1. The flip-flop's next stored state becomes 1, with no dependency on whatever Q happened to be beforehand — this is exactly the module's own example of D flip-flop behavior. Expected result: Q(next) = 1
Q(next) = D = 1 — the next state is simply a direct copy of D at the edge.
At each edge, Q simply copies whatever D equals at that instant — there is no hold logic, no toggle logic, and no forbidden case to check. This one-to-one mapping (Q always equals the most recently sampled D) is exactly why D flip-flops feel like the most predictable and easiest-to-reason-about flip-flop type. Expected result: Q sequence: 0, 1, 1, 0, 1
Every Q value in the trace is a direct copy of the corresponding D value at that edge — no other rule applies.
Given: D = 1 at the active clock edge
Find: the next stored state, Q(next)edge: 1 2 3 4 5
D: 0 1 1 0 1
Q: 0 1 1 0 1D flip-flop timing: Q mirrors D at every active edge
Every rising edge produces a fresh, direct copy of D into Q — D's mid-cycle wiggles (visible around the 4th edge) have no effect until the next actual edge samples whatever D equals at that exact moment.
clk __|‾|_____|‾|_____|‾|_____|‾|_____|‾|__
D __0___1__1___0__1________0_1_0________
Q __0___1__1___0__1__________1__________
^each edge: Q <- D, ignoring D's activity between edgesImportant Rules
Characteristic equation: Q(next) = D, sampled only at the active clock edge.
No forbidden input combination exists for a D flip-flop.
The next state has zero dependency on the flip-flop's own previous Q — it depends only on D.
In RTL, always_ff @(posedge clk) q <= d; models exactly this behavior, and <= (nonblocking assignment) is required to correctly model simultaneous hardware sampling.
D flip-flops as the default cell in every standard-cell library
Virtually every ASIC standard-cell library and every FPGA's native logic fabric is built around the D flip-flop as its one and only native sequential primitive — there usually isn't a dedicated JK or T flip-flop cell available at all. Any other flip-flop behavior needed in a design (toggle-based counters, JK-style control logic inherited from an older design) is implemented by wrapping a small amount of combinational logic around a D flip-flop's D input, exactly the technique this course formalizes in Topic 8.6.
Circuit Symbol
IEEE block symbol for the D flip-flop: a single D data input on the left, CLK with its clock-edge indicator, Q and Q' complementary outputs on the right.
Using blocking assignment (q = d;) instead of nonblocking assignment (q <= d;) inside a sequential always_ff block.
Always use nonblocking assignment (<=) for sequential logic inside always_ff blocks, reserving blocking assignment (=) for combinational always_comb/always @(*) blocks.
Blocking assignment updates its left-hand side immediately within simulation order, which can make one flip-flop's model 'see' another flip-flop's already-updated new value within the same clock edge — a simulation/synthesis mismatch that does not reflect how real parallel hardware flip-flops behave, since real flip-flops all sample the OLD state simultaneously. This maps directly to interview-checklist question #41: why is <= commonly used in always_ff?
A D flip-flop samples D=1 exactly at the active clock edge. What is Q(next)? A. Q(next) = the complement of the previous Q, regardless of D B. Q(next) = 1, directly copying D — the previous value of Q has no effect on the result C. Q(next) = 0, because D flip-flops invert their input D. Q(next) is undefined without knowing the previous Q
B. Q(next) = 1, directly copying D — the previous value of Q has no effect on the result — This is exactly the module's own example: the D flip-flop's characteristic equation is Q(next) = D, so if D=1 at the sampling edge, the next stored state simply becomes 1 — unconditionally, with zero dependency on whatever Q was beforehand.
Why is nonblocking assignment (<=) the standard choice inside a Verilog always_ff block modeling D flip-flops, rather than blocking assignment (=)? A. Nonblocking assignment runs faster in simulation B. Nonblocking assignment schedules all right-hand-side evaluations using OLD signal values and updates every left-hand side together, correctly modeling how real hardware flip-flops all sample simultaneously; blocking assignment can cause one flip-flop's model to incorrectly see another's already-updated value within the same edge C. Blocking assignment is illegal syntax inside always_ff blocks D. There is no real difference; the choice is purely stylistic
B. Nonblocking assignment schedules all right-hand-side evaluations using OLD signal values and updates every left-hand side together, correctly modeling how real hardware flip-flops all sample simultaneously; blocking assignment can cause one flip-flop's model to incorrectly see another's already-updated value within the same edge — This is interview-checklist question #41. Real D flip-flops physically sample their D inputs simultaneously using whatever state existed just before the clock edge. Nonblocking assignment (<=) models this correctly by deferring all updates until every right-hand side has been evaluated against the old values; blocking assignment (=) can create simulation/synthesis mismatches by letting statements within the same always block see each other's already-updated values out of the intended hardware order.
A D flip-flop currently holds Q=0. At the next three active clock edges, D equals 1, 1, and 0 respectively (in that order). What is Q after each of those three edges, and why does the flip-flop's starting value of Q=0 have no bearing on any of the three results?
Q after each edge: 1, 1, 0 — directly matching the D sequence 1, 1, 0. The starting Q=0 is irrelevant to every result because the D flip-flop's characteristic equation Q(next) = D contains no Q term at all; unlike JK or T, the previous state never influences the next state.
Apply Q(next) = D directly at each edge — check whether Q(next) depends on the previous Q anywhere in the equation.