Module 5: Behavioral Verilog for Synthesis ยท 5.2 Sensitivity Lists and Combinational always Blocks

The Sensitivity List

Section 5.1 took the @(...) after always for granted. This section is about it. An always block does not run continuously; it sleeps until something in that list changes. The list is the block’s statement of what kind of hardware it is, and the synthesiser reads it that way: get it right and you get the gates or the flip-flops you meant; get it wrong and you get a latch, or a simulation that quietly disagrees with the silicon.

The list

always @(a or b or c) begin
    y = a & b & c;
end

After each pass through the body the block suspends, and it resumes only when a, b or c changes. Clocks ticking, other signals toggling, none of it is visible while it sleeps.

Four forms

Any change, @(signal): fires on either edge. Level-sensitive.

always @(a) begin
    y = ~a;
end

Rising edge, @(posedge clk): fires on 0 to 1 only. Edge-sensitive, and the synthesiser builds a positive-edge flip-flop.

always @(posedge clk) begin
    q <= d;
end

Falling edge, @(negedge clk): a negative-edge flip-flop. Rare in ordinary synchronous design; used at DDR interfaces and in a few reset schemes.

Several events, or between them in Verilog-95, a comma in Verilog-2001. This is the asynchronous-reset flip-flop:

always @(posedge clk or negedge rst_n) begin
    if (!rst_n)
        q <= 1'b0;
    else
        q <= d;
end

Both spellings are accepted by every current tool. The code in this course uses or in clocked lists; use whichever your team uses and be consistent.

What the form declares

No edge qualifier means combinational logic, or a latch if the body leaves an output unassigned on some path. An edge qualifier means a flip-flop (Figure 1). When you write @(posedge clk) you are not saying “wake on the edge”; you are saying “this is a register”. When you write @(a or b) you are saying “this is a function of the present inputs, no storage”, and the synthesiser holds you to that.

What the sensitivity list infers
Figure 1: Edge-sensitive means a flip-flop; level-sensitive means gates, or a latch if the body is incomplete

A correct combinational block

`timescale 1ns / 1ps
`default_nettype none

module and_gate (
    input  wire a,
    input  wire b,
    output reg  y
);

    always @(a or b) begin
        y = a & b;
    end

endmodule

Both inputs are in the list, the body assigns y in every case, and the result is one AND gate. Now leave b out:

// broken
always @(a) begin
    y = a & b;
end

The block runs only when a changes. If b changes while a holds, nothing runs and y keeps its old value. The simulation is now modelling storage that the synthesiser will not build. The third article of this section traces that in detail; for now, every signal read anywhere in a combinational block must be in its list.

The opposite mistake is an edge qualifier on a data signal. always @(posedge a) begin y = a & b; end is not combinational; it is a flip-flop clocked by a, and if a is data that is nonsense hardware. Tools warn about a clock driven from a non-clock net.

Practice Exercise

For each header, what form is it and what hardware does it infer, assuming the body suits it?

always @(posedge clk) begin ... end                  // A
always @(a or b or c or d) begin ... end              // B
always @(negedge rst_n or posedge clk) begin ... end  // C
always @(*) begin ... end                             // D
always @(posedge a) begin ... end                     // E, a is a data bus
Solution

A: edge-sensitive; a positive-edge flip-flop. B: level-sensitive on four signals; combinational logic if every output is assigned on every path, a latch otherwise. C: a flip-flop with asynchronous active-low reset. D: the automatic list, combinational, with the same caveat as B. E: a bug; a flip-flop clocked by a data bus.

The list in B has to be maintained by hand, and the next article is about the form that maintains itself.

โ† The Scheduler always @(*) โ†’