Verilog Always Block for RTL Modeling

This article is going to introduce the Verilog always block—one of the most basic constructs that has existed since the very beginning of Verilog (IEEE standard 1364-1995)—relate it to some other introductory constructs, and use them to write some simple hardware logic.

After a long hiatus, I’m picking up the proverbial pen again and writing some Verilog articles! I have a new goal to create a series of articles to help new engineers transition from “textbook knowledge” to real world knowledge needed to become a digital design engineer. You’ll see some new articles on basic concepts, as well as intermediate level concepts similar to my previous articles. Hope you find these new articles useful for your career! Let’s get started!

Verilog Always Block In a Nutshell

Verilog behaviour models (RTL design/model is a class of behavioural models) contain procedural statements that control the simulation, and manipulate variables to model hardware circuitry and data flow. The Verilog always block is a procedural statement that starts an activity flow. Each Verilog always block starts a separate activity flow. All of the activity flows are concurrent to model the inherent concurrence of hardware. Each Verilog always block repeats continuously throughout the duration of the simulation, executing the statements defined in its procedure. Its activity ceases only when the simulation is terminated.

An Infinite Loop?

The Verilog always block essentially describes an infinite loop. Without some form of timing control to avoid a zero-delay infinite loop, and allow simulation time to advance, a simulation deadlock condition can be created (read: a simulation hang). The following code, for example, creates such a zero-delay infinite loop.

always areg = ~areg;

If there is a Verilog always block with no timing control, your simulation will look as though it has hung, and will not advance in time. So let’s add a timing control to make the code more useful:

always #half_period clk = ~clk;

Now this becomes a potentially useful statement. It causes the clk signal to toggle its polarity at a delay of half_period. If you haven’t noticed already, it can be used to create a continuously toggling clock stimulus in a simulation.

The “#” is formally called a delay control. It causes the simulation to insert the specified delay (to that procedural block) where the “#” is written.

Timing Control with Event Expression (Sensitivity List)

Another way to provide a timing control is in the form of a Verilog event expression. The syntax of Verilog event expression is “@(event_expression)“. For the procedural block that contains the Verilog event expression, it causes the simulator to wait until the event_expression has occurred before continuing execution of that procedural block. When used together with a Verilog always block, it adds the necessary timing control to make the Verilog always block useful to model hardware. The SystemVerilog standards (IEEE 1800-2005 onwards) also describe the event expression, when used to trigger a Verilog always block, as a “sensitivity list”.

The event expression (or sensitivity list) can contain multiple signals. Each signal is separated by the keyword “or”. The following Verilog always block describes a simple OR gate with inputs A and B, and output C. This code tells the simulator to re-evaluate the value of output C, whenever the signal A or B changes. It is not entirely straightforward, but you can see how this essentially describes the behaviour of an OR gate in simulation.

always @(A or B)
    C = A | B;

Modelling Hardware Logic with Verilog Always Block

Combining these ideas brings us to the more common usage of Verilog always block—together with an event expression.

always @(event_expression)
    single_statement;

always @(event_expression)
begin
    multiple_statements;
end

For hardware modeling, the Verilog event expression is typically specified in one of two forms:

  • Sequential logic: execution triggered based on a clock event (and frequently a reset event)
  • Combinational logic: execution triggered based on the inputs to the logic (i.e. nets and variables on the right hand side of an assignment statement)

Modeling Sequential Logic

A typical event expression for a flip-flop based design is “@(posedge clock_name)”. This tells the simulator to only evaluate the Verilog always block when the clock_name signal makes a positive edge transition (0->1), which is how a flip-flop based design is constructed. Without the “posedge” keyword, the Verilog always block will trigger on any transition of the clock_name signal, both positive and negative.

The following code describes a flip-flop with input d and output q, clocked by positive edge of clk (with no reset):

always @(posedge clk)
    q <= d;

Let’s make it a more complete flip-flop design by also adding a reset, namely an asynchronous reset. An asynchronous reset means the reset will occur even without the presence of a clock. An asynchronous reset is modeled by adding the reset signal also to the sensitivity list. This will cause the simulator to re-evaluate this Verilog always block when the reset signal transitions, irrespective of whether the clk signal transitions (which is what an asynchronous reset means). The Verilog event expression “@(negedge rst_n)” makes the reset active low (trigger an evaluation when rst_n transitions 1->0), and the “if” statement specifies a reset value for the flip-flop. The following code describes the same flip-flop, now with an active-low asynchronous reset (rst_n) that will reset the flip-flop output q to 0 when the reset is asserted (when rst_n transitions 1->0).

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

To describe two flip flops, you can write them as two separate Verilog always block. Let’s make it more interesting and put them in series into a two-stage pipeline. First the diagram, then the code.

Two stage pipelined D flip flops
Two stage pipelined D flip flops
always @(posedge clk or negedge rst_n)
    if (!rst_n)
        q1 <= 1'b0;
    else
        q1 <= d1;

always @(posedge clk or negedge rst_n)
    if (!rst_n)
        q2 <= 1'b0;
    else
        q2 <= q1;

Or you can also write it as one Verilog always block.

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

When there are multiple Verilog always blocks, there is no implied order of execution between them. There is also no limit to the number of always constructs that can be defined in a module.

Modeling Combinational Logic

The Verilog always block can also model combinational logic, but it is a bit less straight forward to understand. A physical implementation of a combinational circuit obviously operates continuously, sampling the inputs and calculating the resulting outputs. A simulator, however, cannot execute a logical statement “continuously”, without causing the zero-delay infinite loop described at the beginning of the article. Therefore to simulate combinational circuit, we have to define specific events at which the simulator should execute procedures, while maintaining correct behaviour.

The simple answer is, to model combinational circuit, the sensitivity list needs to contain all the inputs to the circuit (all the variables on the right hand side of the assignment). The following code describes a combinational AND gate using a Verilog always block.

always @(A or B)
    C = A & B;

More on Event Expression with Verilog Always Block

Here are a few more tips with using Verilog event expression with Verilog always block.

Using Comma in Event Expression

Verilog-2001 standard introduced the use of comma “,” to separate items in the event expression. Prior to that with the original Verilog-1995 standard, the separate items in the event expression must be separated by the keyword “or”. I have used the Verilog-1995 syntax in all the code examples so far. But here is the same flip-flop code example written with the Verilog-2001 syntax.

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

Personally I prefer the Verilog-2001 syntax. That is how I write my code.

Implicit Event Expression @* or @(*)

Sensitivity list is a frequent source of bugs in a Verilog design/model. A deeper discussion of common pitfalls will be the subject of a future article. The standard writers wanted to simplify the usage of the the sensitivity list somewhat in Verilog-2001, so they added the “implicit event_expression list” syntax to simplify using Verilog always block, at least to describe combinational logic. Using an implicit event expression list, the AND gate combinational logic can be rewritten as follows.

always @*
    C = A & B;

always @(*)
    C = A & B;

More precisely, the “@*” and “@(*)” syntax will add all nets and variables that appear in the (right hand side of a) statement to the event expression, with some exceptions.

Verilog Always Block, Evolution to SystemVerilog always_comb and always_ff

SystemVerilog adds several new syntax in addition to the Verilog always block, primarily to address the exceptions noted above. You can read more about these constructs in my article SystemVerilog always_comb, always_ff.

Conclusion

Verilog always block is one of the four procedural statements in the original Verilog language. It can be used to model testbench stimulus as well as hardware design. The Verilog always block is essentially an infinite loop. However, when combined with a Verilog event expression, it can be used to model combinational and sequential logic. SystemVerilog adds several new versions of “always”, in addition to Verilog always block, to address some limitations and pitfalls of the original Verilog syntax.

References

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.