Continuous Assignment and Combinational Logic in SystemVerilog

By John
May 27, 2021

In this post, we primarily talk about the concept of continuous assignment in SystemVerilog. We also look at how we can use this in conjunction with the SystemVerilog operators to model basic combinational logic circuits.

However, continuous assignment is a feature which is entirely inherited from verilog so anyone who is already familiar with verilog can skip this post.

There are two main classes of digital circuit which we can model in SystemVerilog – combinational and sequential.

Combinational logic is the simplest of the two, consisting solely of basic logic gates, such as ANDs, ORs and NOTs. When the circuit input changes, the output changes almost immediately (there is a small delay as signals propagate through the circuit).

In contrast, sequential circuits use a clock and require storage elements such as flip flops. As a result, output changes are synchronized to the circuit clock and are not immediate.

In the rest of this post, we talk about the main techniques we can use to design combinational logic circuits in SystemVerilog.

In the next post, we will discuss the techniques we use to model basic sequential circuits.

Continuous Assignment in SystemVerilog

In verilog based designs, we use continuous assignment to drive data on verilog net types. As a result of this, we use continuous assignment to model combinational logic circuits.

In SystemVerilog, we often use the logic data type rather than the verilog net or reg types. This is because the behavior of the logic type is generally more intuitive than the reg and wire types.

Despite this, we still make use of continuous assignment in SystemVerilog as it provides a convenient way of modelling combinational logic circuits.

We can use continuous assignment with either the logic type or with net types such as wire.

In SystemVerilog, we can actually use two different methods to implement continuous assignment.

The first of these is known as explicit continuous assignment. This is the most commonly used method for continuous assignment in SystemVerilog.

In addition, we can also use implicit continuous assignment, or net declaration assignment as it is also known. This method is less common but it can allow us to write less code.

Let's look at both of these techniques in more detail.

Explicit Continuous Assignment

We normally use the assign keyword when we want to use continuous assignment in SystemVerilog. This approach is known as explicit continuous assignment.

The SystemVerilog code below shows the general syntax for continuous assignment using the assign keyword.

assign <variable> = <value>;

In this construct, we use the <variable> field to give the name of the signal which we are assigning data to. As we mentioned earlier, we can only use continuous assignment to assign data to net or logic type variables.

The <value> field can be a fixed value or we can create an expression using the SystemVerilog operators we discussed in a previous post.

When we use continuous assignment, the <variable> value changes whenever one of the signals in the <value> field changes state.

The code snippet below shows the most basic example of continuous assignment in SystemVerilog. In this case, whenever the b signal changes states, the value of a is updated so that it is equal to b.

assign a = b;

Net Declaration Assignment

We can also use implicit continuous assignment in our SystemVerilog designs. This approach is also commonly known as net declaration assignment in SystemVerilog.

When we use net declaration assignment, we place a continuous assignment in the statement which declares our signal. This can allow us to reduce the amount of code we have to write.

To use net declaration assignment in SystemVerilog, we use the = symbol to assign a value to a signal when we declare it.

The code snippet below shows the general syntax we use for net declaration assignment.

<type> <variable> = <value>;

The variable and value fields have the same function for both explicit continuous assignment and net declaration assignment.

As an example, the SystemVerilog code below shows how we would use net declaration assignment to assign the value of b to signal a.

logic a = b;

Modelling Combinational Logic Circuits in SystemVerilog

We use continuous assignment and the SystemVerilog operators to model basic combinational logic circuits in SystemVerilog.

In order to show we would do this, let's look at the very basic example of a three input and gate as shown below.

A circuit diagram showing a three input and gate

In order to model this circuit in SystemVerilog, we must use the assign keyword to drive the data on to the and_out output.

We can then use the bit wise and operator (&) to model the behavior of the and gate.

The code snippet below shows how we would model this three input and gate in SystemVerilog.

assign and_ut = a & b & c;

This example shows how simple it is to design basic combinational logic circuits in SystemVerilog. If we need to change the functionality of the logic gate, we can simply use a different SystemVerilog bit wise operator.

If we need to build a more complex combinational logic circuit, it is also possible for us to use a mixture of different bit wise operators.

To demonstrate this, let's consider the basic circuit shown below as an example.

A logic circuit with the function (a and b) or c.

In order to model this circuit in SystemVerilog, we need to use a mixture of the bit wise and (&) and or (|) operators. The code snippet below shows how we would implement this circuit in SystemVerilog.

assign logic_out = (a & b) | c;

Again, this code is relatively straight forward to understand as it makes use of the SystemVerilog bit wise operators which we discussed in the last post.

However, we need to make sure that we use brackets to model more complex logic circuit. Not only does this ensure that the circuit operates properly, it also makes our code easier to read and maintain.

Modelling Multiplexors in SystemVerilog

Multiplexors are another component which are commonly used in combinational logic circuits.

In SystemVerilog, there are a number of ways we can model these components.

One of these methods uses a construct known as an always block which we will discuss in detail in the next post. Therefore, we will not discuss this approach to modelling multiplexors in this post.

However, we will look at the other methods we can use to model multiplexors in the rest of this post.

SystemVerilog Conditional Operator

As we talked about in a previous post, there is a conditional operator in SystemVerilog. This functions in the same way as the conditional operator in the C programming language.

To use the conditional operator, we write a logical expression before the ? operator which is then evaluated to see if it is true or false.

The output is assigned to one of two values depending on whether the expression is true or false.

The SystemVerilog code below shows the general syntax which the conditional operator uses.

output = <expression> ? <value if true> : <value if false>;

From this example, it is clear how we can create a basic two to one multiplexor using this operator.

However, let's look at the example of a simple 2 to 1 multiplexor as shown in the circuit diagram below.

A 2 input multiplexor circuit

The code snippet below shows how we would use the conditional operator to model this multiplexor in SystemVerilog.

assign q = addr ? b : a;

Nested Conditional Operators

Although this is not common, we can also write code to build larger multiplexors by nesting conditional operators.

To show how this is done, let's consider a basic 4 to 1 multiplexor as shown in the circuit below.

A four input multiplexor

In order to model this in SystemVerilog using the conditional operator, we treat the multiplexor circuit as if it were a pair of two input multiplexors.

This means one multiplexor will select between inputs A and B whilst the other selects between C and D. Both of these multiplexors use the LSB of the address signal as the address pin.

The SystemVerilog code shown below demonstrates how we would implement this.

assign mux1 = addr[0] ? b : a;
assign mux2 = addr[0] ? d : c;

To create the full four input multiplexor, we would then need another multiplexor.

This multiplexor then takes the output of the other two multiplexors and uses the MSB of the address signal to select between.

The code snippet below shows the simplest way to do this. This code uses the signals mux1 and mux2 which we defined in the last example.

assign q = addr[1] ? mux2 : mux1;

However, we could easily remove the mux1 and mux2 signals from this code and instead use nested conditional operators.

This reduces the amount of code that we would have to write without affecting the functionality.

The code snippet below shows how we would do this.

assign q = addr[1] ? (addr[0] ? d : c) : (addr[0] ? b : a);

As we can see from this example, when we use conditional operators to model multiplexors in verilog, the code can quickly become difficult to understand. Therefore, we should only use this method to model small multiplexors.

Arrays as Multiplexors

It is also possible for us to use basic SystemVerilog arrays to build simple multiplexors.

In order to do this, we combine all of the multiplexor inputs into a single array type and use the address to point at an element in the array.

In order to get a better idea of how this works in practise, let's consider a basic four to one multiplexor as an example.

The first thing we must do is combine our input signals into an array. There are two ways in which we can do this.

Firstly, we can declare an array and then assign all of the individual bits, as shown in the SystemVerilog code below.

// Assigning individual bits in the vector
assign in_vec[0] = a;
assign in_vec[1] = b;
assign in_vec[2] = c;
assign in_vec[3] = d;

Alternatively we can use the SystemVerilog concatenation operator, which allows us to assign the entire array in one line of code.

In order to do this, we use a pair of curly braces - { } - and list the elements we wish to include in the array inside of them.

When we use the concatenation operator we can also declare and assign the variable in one statement.

The SystemVerilog code below shows how we can use the concatenation operator to populate an array.

// Using vector assignment
assign in_vec = {d, c, b, a};

// Declare and assign the vector in one line
logic [3:0] in_vec = {d, c, b, a};

As SystemVerilog is a loosely typed language, we can use the two bit addr signal as if it were an integer type. This signal then acts as a pointer that determines which of the four elements to select.

The code snippet below demonstrates this method in practise.

assign mux_out = in_vec[addr];

Exercises

What is the difference between implicit and explicit continuous assignment?

When we use implicit continuous assignment we assign the variable a value when we declare. In contrast, when we use explicit continuous assignment we use the assign keyword to assign a value.

Write the code for a 2 to 1 multiplexor using any of the methods discussed in this post.

// Using the conditional operator
assign mux_out = addr ? b : a;
 
// Using an array
logic in_arr [1:0] = {a, b} 
assign mux_out = in_arr[addr];

Write the code for circuit below using both implicit and explicit continuous assignment.

A logic circuit with the function (a or b) and c.
// Using explicit continuous assignment
logic logic_out;
assign logic_out = (a | b) & c;
 
// Using implicit continuous assignment
lgoic logic_out = (a | b) & c;
Enjoyed this post? Why not share it with others.

Leave a Reply

Your email address will not be published. Required fields are marked *

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