Skip to content
DenseChip
Learn
PracticeCode labInterview

DenseChip

Navigate

Menu

LearnPracticeCode labInterview

Tools

All toolsSV editorCode repoResume
DenseChip

Practice, courses, and chip-design tools in one focused workspace.

LearnPracticeCode labInterviewToolsLearning paths

© 2026 DenseChip

AboutRoadmapCareersInternshipsFAQContactPrivacyTerms
Skip to lesson

Course contents

  • RTL Foundations36
    • Digital Logic for RTL Engineers
    • Number Systems for RTL Engineers
    • Verilog Fundamentals
    • Verilog Operators and Types
    • SystemVerilog for RTL
      • Why SystemVerilog Improves RTL Quality
      • The `logic` Type and Assignment Model
      • always_ff and always_comb
      • typedef, enum and Robust FSM Coding
      • Interfaces and Modports
      • SystemVerilog RTL Cheat Sheet
    • Simulation and Testbenches
  • Combinational Design25
  • Sequential Design27
  • FSM & Control27
  • Timing & Clocking39
  • Reset & CDC34
  • Memory & Buses57
  • Advanced RTL28
  • Senior & Interview76
  • Glossary and Notation
Loading lesson

←SystemVerilog for RTL
  1. Topics
  2. RTL Design
  3. RTL Foundations
  4. SystemVerilog for RTL
  5. always_ff and always_comb
On this page

    Part 1 of 9, lesson 27 of 36Part 1 of 9/Foundations/Lesson 27 of 36Beginner

    always_ff and always_comb

    Process types that state design intent, what the tools check, and when a latch is intended.

    A plain always @(*) with a missing branch is accepted, and synthesis builds a latch. The same code in always_comb contradicts the declared intent, so tools can reject it.

    After this lesson you can: use always_ff, always_comb and always_latch to state intent, avoid latches, and use unique and priority case correctly.

    Three process types, three promises

    Each process type states what the block implements. The tool then checks that the code keeps the promise.

    Block

    Promise you make

    What the tool checks

    always_ff @(posedge clk ...)

    This is edge-triggered storage.

    Only edge events in the list; assignments should be non-blocking; one block per variable.

    always_comb

    This is combinational logic.

    Sensitivity is automatic; the variable gets a value on every path; no latch; no second writer.

    always_latch

    This is a level-sensitive latch (on purpose).

    A latch really is intended and is inferred.

    seq_comb_demo.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    module seq_comb_demo (
      input  logic       clk, rst_n,
      input  logic [1:0] sel,
      input  logic [7:0] a, b, c,
      output logic [7:0] y,
      output logic [7:0] y_q
    );
      // Pure logic: every path assigns y, so no latch is possible
      always_comb begin
        y = '0;                           // default
        unique case (sel)
          2'd0: y = a;
          2'd1: y = b;
          2'd2: y = c;
          default: y = '0;
        endcase
      end
    
      // Registered copy of the result
      always_ff @(posedge clk or negedge rst_n)
        if (!rst_n) y_q <= '0;
        else        y_q <= y;
    endmodule

    Incomplete assignments infer latches

    latch_bug.v
    1
    2
    3
    4
    5
    6
    // bad-example: y is not assigned when en is 0 -> a latch is inferred
    module latch_bug (input logic en, a, output logic y);
      always_comb begin
        if (en) y = a;          // no else branch
      end
    endmodule

    Many tools can warn that y has no value on every path, which implies a latch. Enable the checks and review each report. With a plain always @(*) the language accepts the latch, and lint and synthesis only warn, because nothing said the block was meant to be combinational. With always_comb the same latch contradicts declared intent, so tools can treat it as an error.

    The fix is a default assignment or an else branch:

    latch_fixed.sv
    1
    2
    3
    4
    5
    6
    module latch_fixed (input logic en, a, output logic y);
      always_comb begin
        y = 1'b0;               // default makes every path assign y
        if (en) y = a;
      end
    endmodule

    Do not assign the same variable from more than one always_ff or always_comb block. The tools reject it, and in older styles it produced simulation races. One signal, one process.

    “What is a latch and how do you avoid it?” A latch is storage inferred when a combinational block fails to assign an output on some path. Avoid it with default assignments or complete branches, and enable the always_comb checks.

    Check yourself When is always_latch the right choice?

    Only when you actually want a transparent latch, for example in a hand-designed clock-gating cell or time-borrowing structure. In ordinary synchronous RTL a latch is almost always a bug, so always_latch should be rare and commented.

    Unique and priority

    These modifiers state how case branches relate. They replace the old full_case parallel_case pragmas. Synthesis tools honoured those pragmas but simulators ignored them, so simulation and hardware could disagree, and a wrong pragma went unnoticed.

    • unique case promises exactly one matching branch. Simulation can warn on overlap or no match, so enable the checks and review each report. A default branch suppresses the no-match report, while a pre-case default assignment still leaves it reported. Both can give a deterministic value.

    • priority case and priority if describe ordered branches, of which at least one matches. Synthesis can use that assumption to simplify the logic, so keep the promise exact.

    • Each block type is a promise the tool checks: clocked, combinational or latch.

    • A latch comes from an output not assigned on some path. Fix it with a default assignment or an else branch.

    • unique promises exactly one match and priority promises at least one. Synthesis uses the promise, so keep it exact.

    Rule of thumb: one signal, one process.

    • Practice: Level-sensitive latch: why it is risky and how to code it safely

    • Practice: FSM incomplete case and unique0: simulation vs synthesis

    • Interview: run a mock interview on this topic

    Related topics

    • Latch inference in depth

    Previous

    27 / 350

    Next: typedef, enum and Robust FSM Coding

    Up next

    Up nexttypedef, enum and Robust FSM Coding

    Sign in to save progress

    Practice this lessonPrevious lessonReport an error

    See also

    • Why SystemVerilog Improves RTL Quality
    • Interfaces and Modports
    • SystemVerilog RTL Cheat Sheet