Latches vs. Flip-Flops

Combinational logic forgets its inputs instantly. To remember, hardware needs feedback, and it comes in two flavors:

The design contains one of each; the waveform makes the difference vivid. While le (latch enable) is high, q_latch wiggles whenever d wiggles. q_ff changes only at the rising clock edges, no matter what d does in between.

The classic accident. In Verilog, an always @* block whose output isn't assigned on every path infers a latch — the tool has to remember the old value somehow. If you ever see the synthesis warning inferring latch for variable 'y', you forgot an else or a default. It's rarely what you wanted. The fix is mechanical: assign a default at the top of the block, or complete the branches.

Experiment: in the playground, delete the else from the latch process — the code still simulates identically. That's what makes this bug sneaky: simulation looks fine, and the surprise arrives in synthesis.

Schematic

dDclkqQD flip-flop: Q samples D on the rising edge

The design

Verilog — design.v
// A D-latch (level-sensitive) and a D flip-flop (edge-sensitive).
module storage (
    input  wire clk,
    input  wire le,       // latch enable
    input  wire d,
    output reg  q_latch,
    output reg  q_ff
);
    // Latch: no edge in the sensitivity - "while le, follow d".
    // (This is a DELIBERATE latch. Accidental ones look the same.)
    always @* begin
        if (le)
            q_latch = d;
        // no else: when le is low, q_latch holds -> latch inferred
    end

    // Flip-flop: the @(posedge clk) edge is what makes it a register.
    always @(posedge clk)
        q_ff <= d;
endmodule
Show the VHDL version
VHDL — design.vhd
-- A D-latch and a D flip-flop.
library ieee;
use ieee.std_logic_1164.all;

entity storage is
    port (
        clk     : in  std_logic;
        le      : in  std_logic;
        d       : in  std_logic;
        q_latch : out std_logic;
        q_ff    : out std_logic
    );
end entity;

architecture rtl of storage is
begin
    -- Latch: assignment guarded by a level, no edge.
    process (all) begin
        if le = '1' then
            q_latch <= d;
        end if;  -- no else: holds -> latch
    end process;

    -- Flip-flop: rising_edge() makes it a register.
    process (clk) begin
        if rising_edge(clk) then
            q_ff <= d;
        end if;
    end process;
end architecture;

The testbench

Verilog — tb.v
`timescale 1ns/1ns
module tb;
    reg clk = 0, le = 0, d = 0;
    wire q_latch, q_ff;

    storage dut (.clk(clk), .le(le), .d(d), .q_latch(q_latch), .q_ff(q_ff));

    always #10 clk = ~clk;      // slow clock so the difference is visible

    initial begin
        $dumpfile("wave.vcd"); $dumpvars(0, tb);
        #4  d = 1;              // FF ignores this until the next edge
        #8  le = 1;             // latch opens: q_latch follows d...
        #5  d = 0;
        #5  d = 1;              // ...wiggle for wiggle
        #6  le = 0;             // latch closes, holds last value
        #6  d = 0;
        #16 $finish;
    end
endmodule

Simulated waveform

This trace was produced by actually simulating the code above with Icarus Verilog.

5 10 15 20 25 30 35 40 45 t (ns) q_latch q_ff clk d le

Open this lesson in the playground →