7.3 Latch: Level-Sensitive Storage and the Transparent Phase

Explain how a latch's level-sensitive enable creates a transparent phase, and identify why incomplete combinational RTL accidentally infers unwanted latches.

A latch is the simplest possible memory element in digital electronics, and it is the natural starting point before flip-flops because a flip-flop is, internally, usually built from a pair of latches (the master-slave structure covered in Topic 8.5). Unlike the edge-triggered devices that dominate modern synchronous design, a latch is level-sensitive: its behavior is controlled by whether an enable signal is currently high or low, not by an instantaneous edge. That difference sounds subtle, but it has real consequences for both circuit analysis and for correctness bugs that show up constantly in real hardware-description-language (HDL) code.

Core Concept: Enable-Controlled Transparency

A latch has a data input and an enable (or control) input. While the enable is asserted (level-sensitive "open"), the latch is transparent — its output tracks its input directly and continuously, exactly the way a piece of wire would. The moment the enable is deasserted, the latch closes and freezes whatever value was present at the input at that instant, holding it steady regardless of any further changes at the data input, until the enable is asserted again.

This transparent-then-latched behavior is the defining trait that separates a latch from a flip-flop. A flip-flop only samples its input at a single instantaneous clock edge and is otherwise completely deaf to input changes; a latch, by contrast, is "listening" continuously throughout the entire time its enable is high, and only becomes deaf once the enable drops.

• Enable high (transparent phase): output follows input in real time. • Enable low (latched phase): output freezes at whatever value was captured the instant enable fell. • Level-sensitive (whole duration of a level) is the key contrast with edge-sensitive flip-flops.

Step-by-Step: Tracing a Gated D-Latch Through a Waveform

To trace a D-latch's output by hand: scan the timeline and, whenever the enable (EN) is high, set Q equal to whatever D is doing at that moment (Q tracks D in real time during this window — if D wiggles multiple times while EN is still high, Q wiggles right along with it). The instant EN drops low, freeze Q at D's value at that exact transition instant and hold it there, ignoring any further changes to D, until EN goes high again and the next transparent window begins.

This is exactly why the transparent phase is the dangerous part of a latch-based design: because Q can glitch or change multiple times while EN is high, any downstream logic reading Q during that window can see intermediate, not-yet-settled values — which is one reason fully edge-triggered flip-flop-based design is the default choice in modern synchronous digital design, reserving latches for specific, deliberate uses.

Tricky Concept and Common Mistake: The Unintended Latch

In RTL design (Verilog/SystemVerilog), an unintended latch is one of the most common and most notorious beginner bugs. It happens inside a combinational always block (always @(*) or always_comb) when an output signal is not assigned a value on every possible path through an if or case statement. Synthesis tools interpret a missing assignment as "hold the previous value here" — and the only way to hold a previous value in hardware is to insert a latch, even though the designer almost certainly intended pure combinational logic with no memory at all.

This is precisely the tricky problem this module calls out: an unintended latch in RTL is a strong signal of incomplete combinational assignment, not a deliberate design choice. Fixing it means covering every branch explicitly — adding an else clause to every if, or a default case to every case statement — so the synthesized logic is guaranteed to be purely combinational whenever that was the actual intent.

Gated D-latch behavior

EN = 1  ->  Q = D            (transparent: Q follows D continuously)
EN = 0  ->  Q = Q_previous    (latched: Q holds last value from when EN fell)

Code Examples

At t=0..2, EN is high, so Q tracks D exactly (0, 1, 0). At t=3, EN falls — Q freezes at D's value at that instant (0) and stays 0 through t=4 and t=5 even though D changes to 1 during that window, because the latch is closed and no longer listening. At t=6, EN rises again, transparency resumes, and Q immediately jumps to match D's current value (1); by t=7, Q again tracks D directly (0).

While EN=0, Q holds its value from the instant EN fell, completely ignoring D — this is the frozen/latched phase.

Because q is never assigned when sel is 0, the synthesis tool must generate hardware that holds q's previous value on that path — which requires an actual latch, even though the designer intended a simple combinational mux. This is exactly the tricky problem this topic calls out: the presence of an inferred latch is a red flag pointing back at incomplete assignment coverage, not a deliberate design decision.

Common mistake: Leaving any branch of an if/case without an explicit assignment inside a block meant to be purely combinational.

time:   0   1   2   3   4   5   6   7
EN:     1   1   1   0   0   0   1   1
D:      0   1   0   0   1   1   1   0
Q:      0   1   0   0   0   0   1   0
// BUGGY: no else branch -> synthesizer infers a latch for q
always @(*) begin
  if (sel)
    q = a;
  // missing else: what is q when sel == 0?
end

D-latch symbol and transparent-window timing

Q tracks D only while EN is high (transparent window); while EN is low, Q is frozen at whatever D was at the instant EN fell.

______
  D -->|      |
       |  D   |--> Q
  EN-->|  Q   |
       |______|

EN:  __|‾‾‾‾‾‾‾|______|‾‾‾‾
D:   __|‾|_____________|‾‾
Q:   __|‾|_____[frozen]_|‾‾
        (transparent)  (transparent)

Important Rules

Latches are level-sensitive: transparent while enable is asserted, frozen while it is not.

During the transparent phase, Q can change multiple times if D changes multiple times.

Once EN falls, Q holds the value captured at that exact falling instant, regardless of later D changes.

In synchronous RTL, every branch of a combinational always block must assign every output, or a latch is unintentionally inferred.

Latches are avoided by default, but not banned

Standard synchronous digital design practice avoids latches almost entirely in favor of edge-triggered flip-flops, precisely because a level-sensitive element that can change output multiple times per clock cycle is much harder to reason about with static timing analysis. That said, latches are not useless — they are smaller and lower-power than flip-flops, which is why deliberate latch-based techniques (such as integrated clock gating cells, and level-sensitive scan design in some ASIC test methodologies) intentionally use latches where their transparency is well understood and controlled, rather than accidentally inferred.

always @(*) if (sel) q = a; // no else — leaves q unassigned when sel == 0 always @(*) if (sel) q = a; else q = b; // every branch assigns q

An if without a covering else (or a case without a default) inside a combinational always block leaves a path where the tool must infer memory to "remember" the last value — producing an unintended latch instead of the intended pure combinational logic.

[tricky-problem] A designer finds that their synthesis tool reports an inferred latch inside a block they intended to be purely combinational. What does this almost always indicate? A. The tool made an error and can be safely ignored B. The design intentionally needs memory and no fix is required C. An output is not assigned on every path through an if/case statement, so the tool infers a latch to hold the missing branch's previous value D. The clock signal is missing from the sensitivity list

C. An output is not assigned on every path through an if/case statement, so the tool infers a latch to hold the missing branch's previous value — An unintended latch in RTL is the classic symptom of incomplete combinational assignment — some branch through the conditional logic never assigns the output, forcing the synthesizer to add memory (a latch) to hold the last known value on that path. The fix is to cover every branch explicitly with an else/default.

What is the single most important behavioral difference between a latch and a flip-flop? A. A latch can only store a 0; a flip-flop can store a 0 or a 1 B. A latch is level-sensitive (transparent for an entire enable duration); a flip-flop is edge-sensitive (samples only at one clock instant) C. A latch requires a clock; a flip-flop does not D. There is no meaningful difference — the terms are interchangeable

B. A latch is level-sensitive (transparent for an entire enable duration); a flip-flop is edge-sensitive (samples only at one clock instant) — This is a classic interview question. A latch is transparent for as long as its enable/control is asserted, so its output can change any number of times during that window. A flip-flop samples its input only at a single instantaneous clock edge and is otherwise unresponsive to input changes — a fundamentally different (and, for synchronous design, generally preferred) timing discipline.

Given EN = 1,1,0,0,1 and D = 0,1,1,0,0 sampled at five successive time steps, trace Q at each step, assuming Q starts at 0.

t1: EN=1,D=0 → Q=0. t2: EN=1,D=1 → Q=1. t3: EN=0 → Q holds = 1. t4: EN=0 → Q holds = 1. t5: EN=1,D=0 → Q=0.

Apply Q = D whenever EN = 1 at that step; otherwise Q holds its previous value.