Why Learn D Latches? Most edge-triggered flip-flops are built from two latches, and the clock-gating cells that cut dynamic power in modern SoCs rely on a latch to stay glitch-free. Latches also appear in designs uninvited, whenever combinational code leaves an output unassigned. In this tutorial, you'll write a D latch, see what synthesis builds from it, and learn to recognize latches you never meant to create.

A D latch is a level-sensitive storage element. While its enable input is high, the output follows the data input; when the enable goes low, the output holds its last value. In Verilog, a D latch is described with an always block that assigns the output only when the enable is active.

10 min read | Beginner Level

What You'll Learn

  • How a D latch behaves in its transparent and hold states
  • Write a D latch with an asynchronous reset in Verilog
  • What synthesis builds from latch code, and how unintended latches get inferred
  • How a latch differs from a flip-flop, and how to verify one with a testbench

What is a D Latch?

A D latch has a data input (d), an enable input (en) and an output (q). The design in this article also has an active-low reset (rstn). The latch works in two states:

  • Transparent (en = 1): q follows d. Any change on d appears on q after a short propagation delay.
  • Hold (en = 0): q keeps the value d had at the moment en went low. Changes on d are ignored.
D latch block with inputs d, en and rstn and output q
rstnendq (next)State
0xx0Reset
1100Transparent
1111Transparent
10xq (no change)Hold

The key word is level-sensitive. A latch responds for the whole time the enable is high, not just at one instant. A flip-flop is edge-triggered and samples its input only at a clock edge.

Syntax

Verilog has no latch keyword. Synthesis infers a latch when an always block assigns a signal under some conditions but not others, and the block is not edge-triggered. The signal must then keep its old value in the unassigned cases, which requires storage.

Basic Form:


always @(*)        // Level-sensitive: no posedge or negedge
  if (en)          // Assign q only when en is high...
    q <= d;        // ...so q must hold its value when en is low

Components:

  • always @(*) - Level-sensitive block; must include d and en so q tracks d while transparent (required)
  • if (en) - The enable condition, with no else branch for q (required)
  • q - Declared as reg because it is assigned in a procedural block (required)

Variations:


// 1. With asynchronous active-low reset (used in this article)
always @(*)
  if (!rstn)    q <= 0;
  else if (en)  q <= d;

// 2. Negative-level latch: transparent while en is low
always @(*)
  if (!en)      q <= d;

Design

This D latch adds an active-low reset to the basic form. Reset is checked first, so it clears q regardless of en.


module d_latch (
  input      d,       // Data input
  input      en,      // Enable: 1 = transparent, 0 = hold
  input      rstn,    // Active-low asynchronous reset
  output reg q        // Latched output; reg because it is assigned in always
);

  // Level-sensitive block: re-evaluates whenever en, rstn or d changes
  always @(*)
    if (!rstn)        // Reset has top priority and works even when en = 0
      q <= 0;
    else if (en)      // Transparent: q follows d
      q <= d;
    // No final else: when rstn = 1 and en = 0, q is not assigned,
    // so it keeps its value. This missing branch is what creates the latch.

endmodule

Synthesis result: Synthesis sees that q is not assigned when rstn = 1 and en = 0, so it infers a level-sensitive latch with en as its gate and rstn as an asynchronous clear. On an ASIC, this maps to a D-latch standard cell with a clear input. On an FPGA, the storage elements in each logic slice can usually be configured as latches, and the tool reports the inferred latch in its log.

Synthesized schematic of a D latch with enable and active-low reset

The design uses non-blocking assignments (<=) because a latch is a storage element, like a flip-flop. This is the common coding guideline for latches; see Verilog Blocking & Non-Blocking.

Where is a D Latch Used?

Clock gating turns off the clock to idle blocks to save dynamic power. An integrated clock gating (ICG) cell uses a latch that is transparent while the clock is low to capture the enable signal, then ANDs the latched enable with the clock. Because the latch holds the enable steady while the clock is high, the gated clock cannot glitch. Synthesis tools insert thousands of these cells into a typical SoC.

Quick Check

Q: The latch is transparent with d = 1. Then en goes low, and d changes to 0. What is q?

Show Answer

q = 1. When en goes low, the latch holds the value d had at that moment. Later changes on d are ignored until en goes high again.

D Latch vs D Flip-Flop

Both store one bit, but they capture data differently. A flip-flop is effectively two latches in series that are enabled on opposite clock levels, which is why it responds only at the edge between them. See D Flip-Flop Async Reset for the edge-triggered version.

D LatchD Flip-Flop
Triggered byLevel of enEdge of clk
Output changesAny time while en is highOnly at the active clock edge
Verilog formalways @(*) with incomplete assignmentalways @(posedge clk)
Glitches on dPass through to q while transparentIgnored between clock edges
AreaSmaller, roughly half a flip-flopLarger
Timing analysisMore complex (time borrowing)Straightforward

Latch-Based Pipelines

Some high-performance processor pipelines use latches instead of flip-flops between stages. Because a latch is transparent for half the clock period, a slow logic stage can borrow time from the next stage, a technique called time borrowing. This helps the design meet a higher clock frequency, at the cost of more complex timing analysis.

Unintended Latch Inference

The same rule that creates the D latch above also creates latches by accident. In a block meant to be combinational, if any path leaves an output unassigned, synthesis infers a latch to hold its value.


// Intended: combinational logic. Actual: a latch on y.
always @(*)
  if (sel)
    y = a;        // y is not assigned when sel = 0, so a latch holds it

// Fixed: every path assigns y, so this is a plain 2 to 1 mux
always @(*)
  if (sel)
    y = a;
  else
    y = b;

Unintended latches cause timing problems, because static timing analysis treats them differently from combinational paths, and they make the design hard to test. Most synthesis tools print a warning such as "Latch inferred for signal y". Treat that warning as an error unless the latch is intentional. Other causes are a case statement with no default and an output that is assigned in only some branches. See Combinational Logic with always for coding patterns that avoid them.

Assign a default value to every output at the top of a combinational always block, such as y = 0;. Every path then assigns the output, and no latch can be inferred.

Testbench

The testbench releases reset at 10 ns, then toggles en and changes d after random delays. This exercises both states: q should follow d while en is 1 and hold while en is 0. See Verilog Testbench for testbench structure.

Testbench connected to the D latch design

module tb_latch;
   // Testbench regs drive the design inputs; a wire collects the output
   reg d;
   reg en;
   reg rstn;
   reg [2:0] delay;     // Random delay before each d change (0 to 7)
   reg [1:0] delay2;    // Random delay before each en toggle (0 to 3)
   integer i;
   wire q;

   // Instantiate the design and connect its ports to testbench signals
   d_latch  dl0 ( .d (d),
                  .en (en),
                  .rstn (rstn),
                  .q (q));

   initial begin
      // Print a line whenever en, d or q changes
      $monitor ("[%0t] en=%0b d=%0b q=%0b", $time, en, d, q);

      // 1. Start with reset asserted and the latch closed
      d <= 0;
      en <= 0;
      rstn <= 0;

      // 2. Release reset after 10 time units
      #10 rstn <= 1;

      // 3. Toggle en and change d after random delays
      //    d is 1 bit wide, so d <= i keeps only bit 0 of i
      for (i = 0; i < 5; i=i+1) begin
         delay = $random;
         delay2 = $random;
         #(delay2) en <= ~en;
         #(delay) d <= i;
      end
   end
endmodule
Output
ncsim> run
[0] en=0 d=0 q=0
[11] en=1 d=0 q=0
[18] en=0 d=0 q=0
[19] en=0 d=1 q=0
[20] en=1 d=1 q=1
[25] en=1 d=0 q=0
[27] en=0 d=0 q=0
[32] en=0 d=1 q=0
[33] en=1 d=1 q=1
[34] en=1 d=0 q=0
ncsim: *W,RNQUIE: Simulation is complete.
Waveform of the D latch showing q following d while en is high and holding while en is low

Reading the log, both states are visible:

  • Hold: At 19 ns, d changes to 1 while en is 0, and q stays 0. The same happens at 32 ns.
  • Transparent: At 20 ns, en goes high and q immediately takes the value of d (1). At 25 ns, d drops to 0 while en is still 1, and q follows it in the same time step. A flip-flop would not respond until its next clock edge.

Latch-Based Register Files

Low-power and area-sensitive designs, such as small microcontrollers and sensor-node processors, sometimes build register files from latches instead of flip-flops. Since a latch is roughly half the size of a flip-flop, this can noticeably reduce the area and power of a register file that holds dozens of words.

Common Beginner Mistakes

Mistake #1: Adding an edge to the sensitivity list

Wrong: always @(posedge en) if (en) q <= d;
Right: always @(*) if (en) q <= d;

Why? posedge makes the block edge-triggered, so synthesis builds a flip-flop clocked by en. q captures d only at the rising edge of en and ignores later changes, which removes the transparent behavior.

Mistake #2: Leaving d out of the sensitivity list

Wrong: always @(en or rstn)
Right: always @(*) or always @(en or rstn or d)

Why? In simulation, the block runs only when en or rstn changes, so q does not follow d while the latch is transparent. Synthesis ignores the sensitivity list and builds a correct latch, so the RTL simulation and the gate-level netlist disagree.

Mistake #3: Checking enable before reset

Wrong: if (en) q <= d; else if (!rstn) q <= 0;
Right: if (!rstn) q <= 0; else if (en) q <= d;

Why? The first true condition wins. With enable checked first, reset is ignored while en is high, so the latch cannot be cleared during transparent operation. Reset must come first to work at all times.

Mistake #4: Creating a latch by accident in combinational code

Wrong: always @(*) if (sel) y = a; in logic meant to be combinational
Right: always @(*) begin y = b; if (sel) y = a; end

Why? Any path that leaves y unassigned makes synthesis add a latch. Check the synthesis log for "latch inferred" warnings after every run, and make sure each reported latch is one you wrote on purpose.

Practice Exercise

Challenge: Negative-Level D Latch

Write a module d_latch_low that is transparent while en is low and holds while en is high. Keep the active-low asynchronous reset. This is the type of latch used inside a clock gating cell, where it captures the enable while the clock is low.

Hint

Only the enable condition changes. Keep reset as the first check, then invert the enable test.

Solution

module d_latch_low (
  input      d,       // Data input
  input      en,      // Enable: 0 = transparent, 1 = hold
  input      rstn,    // Active-low asynchronous reset
  output reg q
);

  always @(*)
    if (!rstn)        // Reset still has top priority
      q <= 0;
    else if (!en)     // Transparent while en is low
      q <= d;
    // No else: q holds while en is high

endmodule

Synthesis infers a latch with an active-low gate. You can verify it with the same testbench: q should now follow d in the intervals where the log shows en=0.

Summary

In this tutorial, you learned:

  • A D latch is level-sensitive: transparent while enable is high, holding while enable is low
  • A latch is inferred when a non-edge-triggered always block leaves an output unassigned on some path
  • Reset should be checked before enable so it works in every state
  • Synthesis maps the design to a latch cell with an asynchronous clear
  • A flip-flop is edge-triggered and is built from two latches enabled on opposite clock levels
  • Latches are used intentionally in clock gating cells, latch-based pipelines and compact register files
  • Unintended latches come from missing else or default branches; assign defaults to prevent them
Did you find what you were looking for?

Quiz

Test Your Knowledge

1 of 5 Score: 0 / 5