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

AboutContactPrivacyTerms
Skip to lesson

Course contents

  • RTL Foundations36
    • Digital Logic for RTL Engineers
    • Number Systems for RTL Engineers
    • Verilog Fundamentals
      • Modules and Ports
      • Wire vs reg
      • assign vs always
      • The Two Process Templates
      • Blocking vs Non-blocking
      • From Code to Hardware
    • Verilog Operators and Types
    • SystemVerilog for RTL
    • Simulation and Testbenches
  • Combinational Design25
  • Sequential Design27
  • FSM & Control27
  • Timing & Clocking39
  • Reset & CDC34
  • Memory & Buses57
  • Advanced RTL28
  • Senior & Interview76
  • Glossary and Notation
Loading lesson

‹Verilog Fundamentals
  1. Topics
  2. RTL Design
  3. RTL Foundations
  4. Verilog Fundamentals
  5. The Two Process Templates

Part 1 · Foundations · Beginner

The Two Process Templates

Two templates cover most RTL: default-first combinational blocks and resettable clocked blocks.

On this page

    Why this matters

    Missing else means different things in the two templates: in a combinational block it infers a latch, and in a clocked block it correctly holds the value. Mixing the habits gives a latch, or a register that clears when it should hold.

    After this lesson you can: write the combinational and sequential templates, and compare synchronous and asynchronous reset.

    Almost every block of RTL is one of two shapes: a combinational process that recomputes outputs from inputs, or a clocked process that updates registers at an edge. Write each shape the same way every time and simulation, synthesis and the reader all agree on what the hardware is.

    Template

    Assignments

    Becomes

    always @(*), defaults first

    Blocking =

    Gates, no storage

    always @(posedge clk ...), reset first

    Non-blocking <=

    Flip-flops

    Combinational template

    Assign a default value to every output first, then override it conditionally. Every path then assigns the output, so no latch is inferred.

    comb_template.v
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    module comb_template (
      input  wire [1:0] op,
      input  wire [7:0] a, b,
      output reg  [7:0] y,
      output reg        zero
    );
      always @(*) begin
        // 1) defaults for every output
        y    = 8'd0;
        zero = 1'b0;
    
        // 2) override on specific conditions
        case (op)
          2'd0: y = a + b;
          2'd1: y = a - b;
          2'd2: y = a & b;
          2'd3: y = a | b;
        endcase
    
        zero = (y == 8'd0);       // derived from the final value of y
      end
    endmodule

    Blocking assignments (=) fit here. Statements read the newly computed value of earlier ones, so zero sees the final y.

    Check yourself A combinational always @(*) block assigns y only inside if (en) y = a;. What happens in hardware?

    y keeps its old value when en is 0, so synthesis infers a latch. Fix it with a default assignment (y = 0; before the if) or an else branch.

    Sequential template

    A clocked block uses non-blocking assignments (<=) and checks reset first. Pick one reset style and keep it across the design.

    seq_sync.v
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    // Synchronous reset: reset is only sampled on the clock edge
    module seq_sync (input wire clk, rst_n, en, input wire [7:0] d, output reg [7:0] q);
      always @(posedge clk) begin
        if (!rst_n)      q <= 8'd0;
        else if (en)     q <= d;
      end
    endmodule
    
    // Asynchronous assert and asynchronous release unless rst_n is already synchronised outside the module: reset acts immediately
    module seq_async (input wire clk, rst_n, en, input wire [7:0] d, output reg [7:0] q);
      always @(posedge clk or negedge rst_n) begin
        if (!rst_n)      q <= 8'd0;
        else if (en)     q <= d;
      end
    endmodule

    No else: two different meanings

    The same if without else is a bug in one template and a feature in the other.

    Template

    When en is 0

    Hardware meaning

    always @(*) if (en) y = d;

    y must retain a prior value between input changes

    An unintended latch unless storage was specified

    always @(posedge clk) if (en) q <= d;

    q retains its previous value at that edge

    An intentional flip-flop clock enable

    Check yourself q is A5, en is 0 and d changes to 3C before the next rising edge. What does the sequential template produce?

    q remains A5. No enabled assignment occurs at that edge. Do not add a combinational-style q=0 default: that would change hold behavior into clearing.

    Synchronous or asynchronous reset

    The only difference between seq_sync and seq_async is the sensitivity list, and it decides when reset acts.

    Synchronous reset

    Asynchronous reset

    Sensitivity list

    @(posedge clk)

    @(posedge clk or negedge rst_n)

    Takes effect

    At the next clock edge

    Immediately, without a clock

    Needs a running clock to reset

    Yes

    No

    Extra hardware

    Reset is an extra input to the D logic

    Needs flip-flops with an async reset pin

    Main risk

    Reset is ignored if the clock is stopped

    Asynchronous assert and asynchronous release: release near a clock edge can violate recovery time unless rst_n is synchronised outside the module

    Each clock period is drawn as four slots, two while clk is high and two while it is low, and rst_n changes only while clk is low. Read the two q lanes against the clock.

    • Slots 0 to 2: both flops hold A5 after the load at the first rising edge.

    • Slot 3: rst_n has fallen and there has been no clock edge. q_async is already 00, q_sync still shows A5.

    • Slot 4: the next rising edge samples rst_n low, so q_sync clears now. q_async had already cleared in slot 3, with no clock edge.

    • Slot 7: rst_n rose while clk was low, so both flops still hold 00. They load A5 at the next rising edge in slot 8.

    The sensitivity list does not provide a release synchroniser: seq_async releases asynchronously too. Prefer an externally synchronised reset, as described in Reset strategies, so that release meets recovery time.

    Common mistakes

    Common pitfall

    A reset initialised low in its declaration (logic rst_n = 0;) never makes a falling edge, so an @(negedge rst_n) block may not fire until the first clock edge. Drive a real pulse instead: rst_n = 1; #1 rst_n = 0;.

    Tip

    In SystemVerilog the same templates read always_comb and always_ff @(posedge clk ...). The tool then checks your intent: it warns if an always_comb infers a latch or an always_ff is not clocked.

    Testbench

    Both reset styles with a testbench that checks their reset behavior.

    seq_sync.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    module tb_templates;
      logic clk = 0, rst_n = 1, en = 0;
      logic [7:0] d = 8'hA5;
      wire  [7:0] q_sync, q_async;
      int errors = 0;
    
      seq_sync  u_sync  (.clk, .rst_n, .en, .d, .q(q_sync));
      seq_async u_async (.clk, .rst_n, .en, .d, .q(q_async));
    
      always #5 clk = ~clk;
    
      initial begin
        #1 rst_n = 0;                       // a real 1 -> 0 transition creates the negedge event
        #1;
        // async reset acts immediately; the sync one has not seen a clock edge yet
        if (q_async !== 8'h00) errors++;
        if (q_sync  === 8'h00) errors++;    // still unknown before the first edge
    
        @(posedge clk); #1;
        if (q_sync !== 8'h00) errors++;     // now the synchronous reset has taken effect
    
        rst_n = 1; en = 1;
        @(posedge clk); #1;
        if (q_sync !== 8'hA5 || q_async !== 8'hA5) errors++;
    
        if (errors == 0) $display("PASS: reset behaviors as described");
        $finish;
      end
    endmodule
    
    module seq_sync (input wire clk, rst_n, en, input wire [7:0] d, output reg [7:0] q);
      always @(posedge clk) begin
        if (!rst_n)      q <= 8'd0;
        else if (en)     q <= d;
      end
    endmodule
    
    module seq_async (input wire clk, rst_n, en, input wire [7:0] d, output reg [7:0] q);
      always @(posedge clk or negedge rst_n) begin
        if (!rst_n)      q <= 8'd0;
        else if (en)     q <= d;
      end
    endmodule

    Remember

    • Combinational: assign a default to every output first, use blocking assignments.

    • Sequential: use non-blocking assignments and one reset style across the design.

    • An asynchronous reset in the sensitivity list still releases asynchronously, so synchronise the release outside.

    Rule of thumb: default first in combinational code, hold by omission in clocked code.

    Keep going

    • Practice: Synchronous D flip-flop with reset

    • Practice: D flip-flop with asynchronous reset

    • Code lab: Build a D flip-flop with asynchronous reset

    • Interview: run a mock interview on this topic

    Related topics

    • Blocking vs non-blocking

    • Reset and CDC

    • From code to hardware

    Previous

    16 / 350

    Next: Blocking vs Non-blocking

    Up next

    Up nextBlocking vs Non-blocking

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Modules and Ports
    • Wire vs reg
    • From Code to Hardware
    clkrst_nq_synca50a5q_asynca50a5
    Both templates with q loaded to A5: rst_n falls between clock edges and clears the asynchronous flop at once, while the synchronous flop clears only at the next rising edge