If Statements and Case Statements in SystemVerilog

In this post we talk about two of the most commonly used constructs in SystemVerilog – the if statement and the case statement.

We have seen in a previous post how use procedural blocks such as the always block to write SystemVerilog code which executes sequentially.

We can also use a number of statements within procedural blocks which control the way that signals are assigned in our SystemVerilog designs. Collectively, these statements are known as sequential statements.

The case statement and the if statement are both examples of sequential statements in SystemVerilog.

In the rest of this post, we talk about how we use both of these statements in SystemVerilog. We then consider a short example for both of these constructs to show how we use them in practise.

SystemVerilog If Statement

The if statement is a conditional statement which uses boolean conditions to determine which blocks of SystemVerilog code to execute.

Whenever a condition evaluates as true, the code branch associated with that condition is executed.

This statement is similar to if statements used in other programming languages such as C.

The SystemVerilog code snippet below shows the basic syntax for the if statement.

if (<expression1>) begin 
  // Code to execute
end
else if (<expression2>) begin
  // Code to execute
end
else begin
  // Code to execute
end

We can exclude the else and else if branches from the statement if we don’t need them.

In fact, we have already seen this in the post on always blocks where we used the posedge macro to detect the rising edge of a clock signal.

We can include as many else if branches as necessary to properly model the underlying circuit.

The if statement uses boolean conditions to determine which lines of code to execute.

In the snippet above, these expressions are given by <expression1> and <expression2>.

These expressions are sequentially evaluated and the code associated with the expression is executed if it evaluates to true.

Only one branch of an if statement will ever execute. This is normally the first expression which evaluates as true.

The only exception to this occurs when none of the expressions are true. In this instance, the code in the else branch will execute.

When we omit the else branch in our if statement code then none of the branches will execute in this case.

The code associated with each branch can include any valid SystemVerilog code, including further if statements. This approach is known as nested if statements.

When using this type of code in SystemVerilog, we should take care to limit the number of nested statements as it can lead to difficulties in meeting timing.

If Statement Example

We have already seen a practical example of the if statement when modelling flip flops and latches in the post on the SystemVerilog always block.

To demonstrate this construct more thoroughly, let’s consider an example of a clocked multiplexor.

In this instance, we will use an asynchronously resettable D type flip flop to register the output of a multiplexor.

The circuit diagram below shows the circuit which we will use in this example.

A circuit diagram showing a two input multiplexor and a d type flip flop. The output of the multiplexor is the input to the flip flop.

The code snippet below shows how we implement this using a single SystemVerilog always_ff block and an if statement.

always_ff @(posedge clock, posedge reset) begin
  if (reset) begin
    Q <= 1'b0;
  end
  else begin
    if (addr) begin
      Q <= b;
    end
    else begin
      Q <= a;
    end
  end
end

In this example, we use the first if statement to set the output of the flip flop to 0b whenever reset is active.

When the reset is not active, then the rising edge of the clock signal has triggered the always_ff block. We use the else branch of the first if statement to capture this condition.

We use a second if statement to model the behaviour of the multiplexor circuit. This is an example of a nested if statement in SystemVerilog.

When the addr signal is 0b, we assign the output of the flip flop to input a. We use the first branch of the nested if statement to capture this condition.

We then use the else branch of the nested if statement to capture the case when the addr signal is 1b.

It is also possible for us to use an else-if type statement here but the else statement is more succinct. The behaviour is the same in both cases as the signal can only ever be 0b or 1b in a real circuit.

SystemVerilog Case Statement

We use the SystemVerilog case statement to select a block of code to execute based on the value of a given signal in our design.

When we write a case statement in SystemVerilog we specify an input signal to monitor and evaluate.

The value of this signal is then compared with the values specified in each branch of the case statement.

Once a match is found for the input signal value, the branch associated with that value will execute.

The verilog case statement performs the same function as the switch statement in the C programming language.

The code snippet below shows the general syntax for the case statement in SystemVerilog.

case (<variable>)
  <value1> : begin
    // This branch executes when <variable> = <value1> 
  end
  <value2> : begin
    // This branch executes when <variable> = <value2> 
  end
  default : begin
    // This branch executes in all other cases
  end
endcase

It is possible to exclude the default branch of the statement, although this is not advisable. If the default branch is excluded then all valid values of the <variable> must have it’s own branch.

As with the if statement, the code associated with each branch can include any valid SystemVerilog code.

This includes further sequential statements, such as if or case statements. Again, we should try to limit the number of nested statements as it makes it easier to meet our timing requirements.

Case Statement Example

To better demonstrate the way we use the case statement in SystemVerilog, let’s consider a basic example.

For this example we will look at a simple four to one multiplexor circuit.

We frequently use the case statement to model large multiplexors in SystemVerilog as it produces more readable code than continuous assignment based implementations.

The circuit diagram below shows the circuit which we will use in this example.

A four input multiplexor

The SystemVerilog code below shows how we would implement this circuit using a case statement and an always_comb block.

always_comb begin
  case (addr)
    2'b00 : begin
      q = a;
    end
    2'b01 : begin
      q = b;
    end
    3'b10 : begin
      q = c;
    end
    default : begin
      q = d;
    end
  endcase
end

This example shows how simple it is to model a multiplexor using the case statement in SystemVerilog. In fact, the case statement provides the most intuitive way of modelling a multiplexor in SystemVerilog.

Although this example is quite straight forward, there are a few important points which we should consider in more detail.

The first thing to note in this example is that we use blocking assignment. The reason for this is that we are modelling combinational logic and non-blocking assignment is not allowed inside of the always_comb block.

Another thing to note here is that we could remove the default keyword from this example. We would then explicitly list the value of addr required to output the value of d instead.

However, we have included the default keyword in this example to demonstrate how we use it in practise.

Exercises

Which blocks do we use to write sequential statements in a SystemVerilog design?

Sequential statements can only be written within a procedural block such as an always block.

Which keywords can we exclude from the if statement when they are not required?

We can exclude the else and else if keywords if they are not needed.

How many branches of the if statement can be executed at one time?

A maximum of one branch in an if statement can execute at any time.

When can we exclude the default branch from the case statement?

We can exclude the default branch if all valid values of the input signal are explicitly listed.

Use a case statement to write the code for a six to one multiplexor. Be sure to take advantage of the SystemVerilog always_comb block when you write this code.

always_comb begin
  case (addr)
    3'b000 : begin
      q = a;
    end
    3'b001 : begin
      q = b;
    end
    3'b010 : begin
      q = c;
    end
    3'b011 : begin
      q = d;
    end
    3'b100 : begin
      q = e;
    end
    default : begin
      q = f;
    end
  endcase
end

Rewrite the six to one multiplexor from the last exercise so that it uses an if statement.

always_comb begin
  if (addr == 3'b000) begin
    q = a;
  end
  else if (addr = 3'b001) begin
    q = b;
  end
  else if (addr = 3'b010) begin
    q = c;
  end
  else if (addr = 3'b011) begin
    q = d;
  end
  else if (addr = 3'b100) begin
    q = e;
  end    
  else begin
    q = f;
  end
end