Verilog Always Block

The always block is the heart of behavioral modeling in Verilog. It describes both combinational and sequential logic based on its sensitivity list.

Combinational Always Block

For pure combinational logic, use always @(*) or list all inputs:

Combinational Logic
module mux4to1 (
    input  wire [3:0] in,
    input  wire [1:0] sel,
    output reg        out  // 'reg' because assigned in always
);
    // always @(*) = trigger on ANY change to RHS signals
    always @(*) begin
        case (sel)
            2'b00: out = in[0];
            2'b01: out = in[1];
            2'b10: out = in[2];
            2'b11: out = in[3];
        endcase
    end
endmodule

Key Points for Combinational Logic

  • Use always @(*) – auto-includes all RHS signals
  • Use blocking assignment (=)
  • Assign ALL outputs in ALL branches to avoid latches

Sequential Always Block

For flip-flops and registers, use clock edge sensitivity:

Sequential Logic (Flip-Flop)
module dff (
    input  wire clk,
    input  wire rst_n,    // Active-low async reset
    input  wire d,
    output reg  q
);
    // Positive edge clock, negative edge async reset
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n)
            q <= 1'b0;       // Async reset
        else
            q <= d;          // Clock edge: capture D
    end
endmodule

Key Points for Sequential Logic

  • Use posedge clk for rising-edge triggered FF
  • Use non-blocking assignment (<=)
  • Reset goes in sensitivity list if asynchronous

Blocking vs Non-Blocking

This is the most critical concept in Verilog!

Blocking (=) Non-Blocking (<=)
Execution Sequential (one after another) Parallel (all at once)
Use for Combinational logic Sequential logic
RHS evaluation Immediate Deferred to end of time step
Blocking vs Non-Blocking Example
// WRONG: Using blocking in sequential logic
always @(posedge clk) begin
    a = b;    // a gets b immediately
    c = a;    // c gets NEW a (same as b) - WRONG!
end
// Result: c = b (unintended combinational behavior)
// CORRECT: Using non-blocking in sequential logic
always @(posedge clk) begin
    a <= b;   // Schedule: a will get OLD b
    c <= a;   // Schedule: c will get OLD a
end
// Result: Proper pipeline stage (a→c shift register)
// CORRECT: Using blocking in combinational logic
always @(*) begin
    temp = a & b;      // temp assigned immediately
    result = temp | c; // result uses new temp value
end

Golden Rule

Use = for combinational, <= for sequential. Never mix them in the same always block!

Complete Counter Example

8-bit Counter with Enable
module counter_8bit (
    input  wire        clk,
    input  wire        rst_n,
    input  wire        enable,
    input  wire        load,
    input  wire [7:0]  load_val,
    output reg  [7:0]  count,
    output wire        overflow
);
    // Sequential logic for counter register
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n)
            count <= 8'd0;
        else if (load)
            count <= load_val;
        else if (enable)
            count <= count + 1'b1;
        // else: hold current value (implicit)
    end
    // Combinational logic for overflow flag
    assign overflow = (count == 8'hFF) && enable;
endmodule

Common Interview Questions

  1. When do you get a latch instead of a flip-flop?

    When not all outputs are assigned in all branches of a combinational always block (incomplete if/case).

  2. What's the difference between @(*) and @(a, b, c)?

    @(*) automatically includes all RHS signals; explicit lists can miss signals causing simulation mismatches.

  3. Why use non-blocking for sequential logic?

    Non-blocking ensures all flip-flops sample their inputs simultaneously at the clock edge, modeling real hardware behavior.