Using the Always Block to Model Sequential Logic in Verilog

By John
July 16, 2020

In this post, we discuss one of the most important constructs in verilog - the always block.

As we discussed in the post on verilog operators, there are two main classes of digital circuit which we can model in verilog – combinational and sequential.

In contrast to combinational logic, sequential circuits use a clock and require storage elements such as flip flops.

As a result, the output signals are synchronised to the circuit clock and changes do not occur immediately. 

We use the always block to write code which executes sequentially in verilog. This is crucial when describing sequential logic circuits in verilog.

As always, there are a number of exercises at the end of this post.

However, it is worth reading the blog on writing a basic testbench in verilog before tackling these exercises. This will allow us to simulate some of the circuits which we design in these exercises.

The Always Block in Verilog

When we write verilog, we use procedural blocks to create statements which are executed sequentially. Procedural blocks are particularly important for the modelling of sequential digital circuits.

In contrast, verilog continuous assignment statements execute concurrently (i.e. in parallel) in our designs. This matches the nature of the underlying circuits, which consist of a number of separate logic gates.

The always block is one of the most commonly used procedural blocks in verilog. Whenever one of the signals in the sensitivity list changes state, all of the statements in the always block execute in sequence.

The verilog code below shows the general syntax for the always block. We talk about the sensitivity list in more depth in the next section.

always @(<sensitivity_list>) begin 
  // Code to be executed goes here 
end

We need to be careful when using this construct as there are some features which are unique to verilog.

In particular, beginners often find it difficult to understand the way that signals are updated in an always block.

When we use always blocks, we can update the value of our signals either in parallel or sequentially. This depends on whether we use blocking or non-blocking assignment, which we discuss in more depth later in this post.

In order to be an effective verilog designer, it is important that we have a good understanding of the always block.

Let's look at some of the key features of the always block in more detail.

Sensitivity Lists

Any code which we write within an always block runs continuously. This means that the statements within the code block are executed in sequence until we reach the last line.

Once the last line in the sequence has been executed, the program then loops back to the first line. All of the statements in our always block are then executed in sequence again.

However, this behaviour is not representative of a real circuit which will remain in a steady state until one of the input signals changes state.

We use the sensitivity list in the alway block to emulate this behaviour.

To do this, the code within the always block will only execute after one of the signals in the sensitivity list changes state.

Flip Flop Example

Let’s consider how we would model a basic D type flip flop using the always block as an example.

As with all clocked flip flops, the output of a D type flip flop only changes state when there is a positive clock edge.

As a result of this, we include the clock signal in the sensitivity list so that the always block only executes when there is a rising edge on the clock signal.

The verilog code below shows how we would model a D type flip flop using the always block.

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

In this code example, we use the posedge macro to determine when there is a transition from 0 to 1.

The single line of code within the always block is executed when this macro evaluates as true. This line of code assigns the value of D to the output signal (Q).

When we use the posedge macro in verilog, all other changes of state are simply ignored. This is exactly what we would expect from a D type flip flop.

Verilog also has a negedge macro which has the opposite functionality. When we use this macro, the always block will execute whenever the clock changes from 1 to 0.

We can also omit this macro altogether. In this case, the code executes whenever a signal in the sensitivity list changes state.

We should only ever use the posedge macro for clock signals in our verilog design. This is because synthesis tools will attempt to utilize clock resources within the FPGA to implement it.

Multiple Signals in a Sensitivity List

There are instances when we will want to include more than one signal in the sensitivity list.

A common example of this is when we write code to model the behaviour of flip flops with asynchronous resets.

When this is the case, we need the flip flop model to perform an action whenever the reset or clock signals change state.

To do this we simply list both of the signals inside the sensitivity list and separate them with a comma.

The code snippet below shows how we write such a flip flop.

always @(posedge clock, posedge reset) begin 
  if (reset) begin  
    q <= 1'b0; 
  end 
  else begin 
    q <= d; 
  end
end

As this example uses an active high reset, we again use the posedge macro in the sensitivity list.

An active high reset means that the reset is only active when it is equal to one.

We then use a construct known as an if statement to determine whether the always block triggered by the reset signal or the clock signal.

We will discuss the verilog if statement in a future blog post, although it’s functionality is fairly self explanatory.

When working with code which is verilog 1995 compatible we must separate the signals in the sensitivity list using the or keyword instead or a comma.

The code snippet below shows how we would model an asynchronously resettable flip flop using verilog 1995.

always @(posedge clock or posedge reset) begin 
  if (reset) begin  
    q <= 1'b0; 
  end 
  else begin 
    q <= d; 
  end
end

Blocking and Non-Blocking Assignment in Verilog

In the code examples we have seen so far in this series of posts, we have used two different types of assignment operators.

This is because verilog has two different types of assignment – blocking and non-blocking.

When we write code with non-blocking assignments we use the <= symbol whilst blocking code uses the = symbol.

When we use continuous assignment in verilog, we can only use blocking assignment.

However, we can use both types of assignment in procedural block.

Blocking assignment typically results in our synthesis tools implementing combinational logic circuits. In contrast, non-blocking assignment normally results in sequential circuits after synthesis.

Blocking assignment is the simplest of the two techniques to understand. When we assign signals using blocking assignment in verilog, our signals update their value as soon as the line of code is executed.

We commonly use this type of assignment to write combinational logic in verilog. However, in some circumstances we can use it to create sequential circuits.

In contrast, signals which use the non-blocking technique are not updated immediately after assignment. Instead, verilog uses assignment scheduling to update the values.

This is a little tricky to understand, so let’s consider it in a bit more depth.

Assignment Scheduling

When we write verilog code using non-blocking assignment, our code still executes sequentially. However, the signals which we are assigning do not get updated in this way.

To demonstrate why this is the case, let’s consider the twisted ring counter circuit below.

A simple twisted ring style counter circuit
always @(posedge clock) begin 
  q_dff1 <= ~q_dff2; 
  q_dff2 <= q_dff1; 
end

First, let’s look at the behaviour if the signals did update immediately.

If we assume that the output of both flip flops is 0b when a clock edge occurs, then the second line in the code will set the output of DFF1 to 1b.

We can then see that the line of code immediately beneath this would set the output of DFF2 to 1. This is clearly not the intended behaviour of this circuit.

To overcome this issue, non blocking assignment in verilog uses scheduled assignment.

As a result, changes to our signals don’t occur immediately after assignment but are instead scheduled to occur at some point in the future.

Normally, the signals update their value at the end of a simulation cycle. This refers to the time it takes the simulator to execute all of the code for a given time step.

To better demonstrate the way scheduled assignment works, let’s again consider the simple dual flip flop circuit.

When a rising edge is detected, the simulator firstly executes the statement to update DFF1. Once this line has been executed, an update to the output of DFF1 is scheduled to happen.

The simulator then runs the second line of code, this time using the original value of the DFF1 flip flop and schedules the update of DFF2. 

As there are only two statements in this design, the simulation cycle is now complete. At this point, all of the scheduled changes are applied and the values are updated for both flip flops.

Synthesis Example

To further demonstrate the difference between blocking and non blocking assignments in verilog, we will again model a basic two flip flop twisted ring counter circuit. The code snippet below shows he implementation of this circuit.

always @(posedge clock) begin 
  q_dff1 <= ~q_dff2;
  q_dff2 <= q_dff1; 
end

However, we can also look at the output of a synthesis tool such as vivado to see a diagram of the resulting circuit. The circuit diagram below shows this circuit.

We can see that there are two flip flops in the circuit whilst the not gate is implemented using LUT1.

Circuit diagram showing a 2 bit twisted ring circuit.

Now let’s take at look at the circuit we would get if we used blocking assignment in the code.

The verilog code below shows how we could (incorrectly) attempt to model this circuit using blocking assignment.

always @(posedge clock) begin 
  q_dff1 = ~q_dff2; 
  q_dff2 = q_dff1; 
end

This results in the circuit shown below after synthesis.

A circuit diagram showing a single flip flop with the output being passed through a look up table and back into the input.

We can see from this that using non blocking has resulted in the removal of the second flip flop from our circuit.

The reason for this should be fairly obvious, given what we have learnt about blocking assignment so far.

As the value of the q_dff2 is immediately assigned to the same value as q_dff1, our circuit model does not imply that there should be a flip flop in this signal path.

This example actually shows us one of the most important differences between blocking and non blocking assignment in verilog.

When we use non-blocking assignment, the synthesis tool will always place a flip flop in the circuit. This means that we can only use non blocking assignment to model sequential logic.

In contrast, we can use blocking assignment to create either sequential or combinational circuits.

However, we should only use blocking assignment to model combinational logic circuits in verilog. The main reason for this is that our code will be much easier to understand and maintain.

Combinational Logic in Always Blocks

Up to this point, we have only considered the modelling of sequential circuits using always block.

Although this is the most common use case, we can also model combinational logic using this approach.

As an example, the code below shows how we can use an always block to model the AND-OR circuit which we discussed in the post on continuous assignment in verilog.

// Using verilog 2001 style coding
always @(a, b, c) begin 
  logic_out = (a & b) | c; 
end

// Using verilog 1995 style coding
always @(a or b or c) begin 
  logic_out = (a & b) | c; 
end

We see that this code is almost identical to the example we looked at in the post on continuous assignment.

The only major difference here is the fact that we have encased it within an always block. We also remove the verilog assign keyword from the statement as we no longer need it.

We can also see from this example how the sensitivity list is more complex for combinational circuits than sequential circuits.

There are actually two methods which we can use to write the sensitivity list when modelling combinational logic circuits.

The first method we can use is to list each of the inputs to the circuit separated by either by the or keyword or by a comma. This is the method we have used in the example code above.

In addition to this, we can also use the * character to tell our verilog tools to automatically decide which signals to include in the sensitivity list.

This technique is preferable as it has the advantage of being easier to maintain. However, this method was introduced as part of the verilog 2001 standard meaning it can't be used with verilog 1995 code.

The code snippet below shows how we would use both of these methods.

// Sensitivity list with all signals listed 
always @ (a, b, c)
 
// Sensitivity list using the special character 
always @ (*)

Generally speaking, using the always block to model combinational logic adds boiler plate code to our design.

Therefore, we only use the always block to model combinational logic circuits in a few circumstances where it can simplify the modelling of complex combinational logic.

Multiplexors

One instance where it can be useful to use an always block to model combinational logic is when we want to model a multiplexor.

In this case, we can use a construct known as the case statement to model the multiplexor. In comparison to the methods we discussed in the post on modelling combinational logic in verilog, this provides a simpler and more intuitive way of modelling large multiplexors.

We talk about the verilog case statement in more detailed in a future blog post. However, the code snippet below shows how we would use the case statement to model a simple four to one multiplexor.

always @(*) 
  case (addr) begin 
    0 : begin 
      // This branch executes when addr = 0
      mux_out = a; 
    end 1 : begin 
        // This branch executres when addr = 1
        mux_out = b; 
    end 
    2 : begin
      // This branch executes when addr = 2
      mux_out = c; 
    end 
    3 : begin 
      // This branch executes when addr = 3
      mux_out = d; 
    end 
  endcase 
end

The case statement is fairly simple to understand, as it uses a variable to select one of a number of branches to execute.

We can include as many different branches as we require in the case statement.

In addition, we use the default branch to catch any values which we haven’t explicitly listed.

In order to use this as a multiplexor, we use the variable as if it were the address pins.

We can then assign the output of the multiplexor to the required value based on which branch we are executing.

Exercises

What is the difference between continuous assignment and procedural blocks (such as the always block) in verilog?

We use procedural blocks such as the always block to execute code sequentially in verilog. In contrast, continuous assignment is executed in parallel.

Why do we use sensitivity lists in the verilog always block?

They define the list of signals that an always will wait on before resuming the execution of code.

What is the difference between blocking and non-blocking assignment in verilog?

When we use blocking assignment all signal assignments take effect immediately. In contrast, when we use non-blocking assignment our signals are updated using assignment scheduling.

Which type of assignment can we use in continuous assignment? What about in procedural blocks?

When we write code using continuous assignment, we can only use blocking assignment.

We can use both types of assignment within a verilog procedural block. However, non-blocking assignment normally results in a sequential implementation after synthesis. In contrast to this, blocking assignment normally results in a combinational implementation.

Write the code for a 4 input NAND gate using an always block

always @(*) begin
  nand_out = a ~& b ~& c ~& d;
end

Write the code for the circuit shown below.

Circuit diagram showing 3 d type flip flops in a chain. The input to the first flip is an or gate whose inputs are the outputs of the other two flip flops.
always @(posedge clock) begin 
  q_dff1 <= q_dff2 | q_dff3; 
  q_dff2 <= q_dff1;
  q_dff3 <= q_dff2;
end
Enjoyed this post? Why not share it with others.

Subscribe

Join our mailing list and be the first to hear about our latest FPGA tutorials
Sign Up to our Mailing List
© 2024 FPGA Tutorial

Sign up free for exclusive content.

Don't Miss Out

We are about to launch exclusive video content. Sign up to hear about it first.

Close
The fpgatutorial.com site logo

Don't Miss Out

We are about to launch exclusive video content. Sign up to hear about it first.

Close