An Introduction to SystemVerilog Arrays

This post of the the first of two which talk about SystemVerilog arrays. In this post, we will talk about static arrays, array assignment, loops and packed vs unpacked arrays.

In SystemVerilog, we can write arrays which have either a fixed number of elements or a variable number of elements.

Fixed size arrays are also known as static arrays in SystemVerilog. When we declare a static array, a fixed amount of memory is allocated to the array at compile time. As a result of this, we can’t add new elements or resize the array during simulation.

In contrast, we can allocate extra memory or resize a dynamic array while a simulation is running.

Static arrays are generally simpler to use than dynamic arrays and are similar to verilog arrays. Therefore, we will discuss static arrays in more depth in the rest of this post.

In the next post in this series, we will talk about more advanced SystemVerilog arrays. This includes a discussion of dynamic arrays, queues and associative arrays.

Static Arrays in SystemVerilog

Static arrays are the simplest form of array to work with in SystemVerilog. We use static arrays in exactly the same way as we would use an array in verilog.

The code snippet below shows the general syntax which we use to declare a static array in SystemVerilog.

// General syntax to declare a static array
<type> <size> <variable_name> <elements>;

In this construct, we use the <elements> field to declare how many elements are in our array.

In contrast to verilog, we can use a single number in the <elements> field to determine how many elements are in the array.

When we use this approach, SystemVerilog creates a zero indexed array as this is the most commonly used type of array. This approach is also consistent with the way that arrays in C are declared and created.

However, we can also use the old verilog syntax to specify a value for the low and high indexes in our array. When we use this approach, the <elements> field takes the form [high_index : low_index].

We previously discussed the rest of the fields used in this declaration in the post on SystemVerilog Data Types.

To show how we would declare a SystemVerilog array using both approaches, let’s consider a simple example. In this example, we will create an array of 4 bit logic types and we want to have a total of 16 elements.

The SystemVerilog code below shows the two different methods we could use to create this array.

// New SystemVerilog array declaration method
logic [3:0] example [16];

// Verilog style array declaration method
logic [3:0] example [15:0];

Array Assignment and Literals

After we have created an array in our SystemVerilog code, we can access individual elements in the array using square brackets.

For example, if we wanted to assign some data to the first element in an array then we would use the syntax shown in the code snippet below.

example[0] = 4'hF;

We use this same syntax to access array elements in verilog.

However, we can also use array literals to assignment data to arrays in SystemVerilog. Array literals provide us with a quick and convenient method to assign values to multiple elements in the array at the same time.

The code snippet below shows the general syntax we use to assign data to an array using an array literal.

<array_name> = '{ <values> };

We use the <values> field in the above code snippet to assign a value to every element in the array.

We can use three different methods to assign data to the <values> field in this construct. The most common method is to use a comma separated list of values.

The SystemVerilog code below shows how we would assign elements to an array using this technique. We can also simulate this code on eda playground.

logic [3:0] example [4] = '{ 4'h1, 4'h2, 4'h3, 4'h4 };

In addition to this method, we can also use other techniques to assign the same value to multiple elements in the array.

We actually have two methods which we can use for this. Both of these methods are more concise than writing a comma separated list with duplicated values.

The first approach is to use another pair of curly braces inside the list which shows the value we want to assign. We can then put a number in front of the braces to indicate how many consecutive elements should be assigned this value.

As an example, we might want to create an array and assign all of the elements to 0. The code snippet below shows how we would do this using array literals. This code can also be simulated on eda playground.

// Assign all four elements in an array to 0
logic [3:0] example [4] = '{ 4{ 4'h0 } };

In addition to this, we can also use the SystemVerilog default keyword inside our array literal.

We more typically use the default keyword inside of SystemVerilog struct types which we will talk about in a future post.

However, we can also use the default keyword as a convenient way to assign the same value to every element in a static array. We can’t use this technique with dynamic arrays though.

As an example, we might want to create an array and assign all of the elements to 0. The code snippet below shows how we would do this using the default keyword. We can also simulate this code on eda playground.

// use the default keyword to assign all elements in an array
logic [3:0] example [4] = '{ default : 4'h0 };

When we work with arrays in SystemVerilog it is possible that we may try to assign data to an array element which is out of bounds. When we are working with static arrays, any attempt to do this is simply ignored.

Array Loops

When we work with arrays in any programming language, one common operation is iterating over every element in the array.

There are actually two different methods which we can use to do this in SystemVerilog.

In both case, we make use of a SystemVerilog loops. There are several types of loop which we can use in SystemVerilog and we discuss this in more detail in a later post.

However, let’s take a look at both of the methods we can use to iterate over an array in more detail.

The for loop

The first method makes use of the SystemVerilog for loop which we discuss in more depth in a later post.

The code snippet below shows the general syntax which we use to implement a for loop in SystemVerilog. This syntax is exactly the same as we use in verilog for loops.

for (<initial_condition>; <stop_condition>; <increment>) begin
  // Code to execute
end

We use the <initial_condition> field to set the initial value of our loop variable.

The <stop_condition> field is a conditional statement which determines how many times the loop runs. The for loop will continue to execute until this field evaluates as false.

The <increment> field determines how the loop variable is updated in every iteration of the loop.

We can use the for loop to generate the index for every element in an array. We can then use the loop variable to access each of the elements in the array in turn.

The code snippet below shows how we would do this in SystemVerilog. This example can also be simulated on eda playground.

// Create an array to loop over
logic [3:0] example [8];

// Loop to access every element in the array
for (int i =0; i < $size(example); i++) begin
  // Use the loop variable to access an element
  example[i];  
end

In this example, we initialize the loop variable (i) to 0 in order to get the lower index of the array. We then use the $size macro to get the index of the final element in our array.

The $size macro is a built in function in SystemVerilog which returns the number of elements in an array.

Finally, we use the SystemVerilog increment operator to increment the value of the loop variable by one on every iteration. As we can see for this example, we can use C style increment and decrement operators in our SystemVerilog code.

From this we can see how the loop variable i is increased fro 0 to 7 in the for loop. This allows us to access each of the array elements in turn by using the loop variable as an index.

The foreach Loop

The second method which we can use to loop over an array in SystemVerilog is the foreach loop.

This is a new type of loop which was introduced as a part of the SystemVerilog language. The foreach loop is designed to provide a more concise way of looping over an array.

The code snippet below shows the general syntax for the foreach loop.

foreach ( <array_name>[<iterator>] ) begin
  // Code to execute
end

In this construct, we use the <array_name> field is select the array which we want to loop over.

The <iterator> field is then automatically incremented from the low index to the high index of the selected array.

We can use the <iterator> field in the body of our foreach loop in the same way as we used the loop variable in the body of the for loop.

As we can see, we don’t need to manage the array index when we use the foreach loop. As a result of this, we generally find that the foreach loop is easier to use than the for loop.

To demonstrate how we would use the foreach loop, let’s rewrite the example from the section on for loops.

In this example, we created an array of 8 elements and then looped over each element in turn.

The code snippet below shows how we would implement this functionality with a foreach loop. We can also simulate this example on eda playground.

// Create an array to loop over
logic [3:0] example [8];

// Loop to access every element in the array
foreach ( example[i] ) begin
  // Use the loop variable to access an element
  example[i];  
end

As we can see from this example, when we use a foreach loop we generally have to write less code to achieve the same result.

Multi Dimensional Arrays in SystemVerilog

So far, all of the arrays which we have discussed in this post have been one dimensional.

However, we can also create SystemVerilog arrays which have more than dimension. To do this, we simply add another field to the end of the array declaration.

The code snippet below shows the general syntax we would use to create a 2D array in SystemVerilog.

// General syntax to declare an array type
<type> <size> <variable_name> <elements> <elements>;

Both of the <elements> fields in this construct take the same form as the <element> field which we previously discussed in the section on static arrays.

As a result, we can either use a single number to determine the number of elements or we can specify a value for the low and high indexes. When we use the second approach, the <elements> field takes the form [high_index : low_index].

We can add as many <element> fields as needed to create a multidimensional array. For example, if we need a 3D array, then we would use a total of 3 <element> fields in our declaration.

In order to better demonstrate how we use multidimensional arrays in SystemVerilog, let’s consider a basic example.

For this example, let’s say that we want to create an array which has 2 elements both of which contain 4 8-bit wide logic types.

To do this, we simply need to add an extra field to the end of our normal array declaration. The code snippet below shows how we would do this.

// Array declaration using newer SystemVerilog syntax
logic [7:0] example [2][4];
// Array declaration using the older verilog syntax
logic [7:0] example [1:0][3:0];

Assigning Data to Multidimensional Arrays

We use the same method to assign a multidimensional array as we would for a 1D array. However, we now use a pair of square brackets to define the element in both dimensions of the array.

As an example, suppose we want to assign the value of AAh to the the last element in both dimensions of the example array we declared in the previous section. The SystemVerilog code below shows how we would do this.

example [0][3] = 8'haa;
example [1][3] = 8'haa;

We can also use arrays literals to assign data to multidimensional arrays in SystemVerilog.

However, we now have to create a literal which itself contains a separate array literal for each dimension of the array.

For example, if we have a 2D array then we would create an array literal which is made up of 2 further array literals.

As an example, let’s consider the case where we want to populate both elements of the 2D array example we created in the previous section.

However, we want all elements of the first dimension to be set to 0xFF and all elements in the second dimension to be set to 0.

The code snippet below shows how we would create an array literal to populate the entire 2D array with the required data. This code can also be simulated on eda playground.

// Assigning data using array literals
example = '{ '{4{8'hFF}}, '{default : 8'h0} };

Packed and Unpacked Arrays in SystemVerilog

In all of the examples which we have considered so far, we have used unpacked arrays.

This means that each of the elements in the array is stored in a different memory location during simulation.

For example, suppose we have an array which consists of 4 8-bit elements. The code snippet below shows how we would create this is SystemVerilog.

logic [7:0] example [4];

As most simulators use 32 bit wide memory locations, we would end up with the memory contents shown below during simulation.

An array of 4 bytes arranged in 4 32 bit words.

As we can see from this example, unpacked arrays can result in inefficient usage of memory as parts of the memory are unused.

In SystemVerilog, we can also use packed arrays to store our data in. Packed arrays will result in more efficient usage of memory when we run our simulations in some instances.

In contrast to unpacked arrays, all elements of a packed array are stored continuously in memory. This means that more than one element can be stored in a single memory location.

The code snippet below shows the general sytnax we use to declare a packed array in SystemVerilog.

// General syntax to declare a packed array
<type> <size> <elements> <variable_name>;

All of the fields in the packed array declaration are the same as in the unpacked declaration. In fact, the only difference is that the <elements> field now comes before the <variable_name> field rather than after it.

To demonstrate how packed arrays are different to unpacked arrays, let’s consider a simple example.

For this example, we will again create an array of 4 8-bit elements. However, this time we will declare the array as a packed type array.

The SystemVerilog code below shows would we declare this packed array.

logic [7:0] [3:0] example;

This example would result in the memory allocation shown below.

An array of 4 bytes arranged in a single 32 bit word

We can see from this that the packed array has used a single 32-bit wide memory location to store the entire contents of the array.

In comparison to the unpacked array example, we can see how the packed array uses less memory by packing all of the data into a single memory location in this instance.

Exercises

What is the difference between static arrays and dynamic arrays in SystemVerilog?

Static arrays have a fixed number of elements which are determined at compile time. In contrast, dynamic arrays don’t have a fixed sized and we can add or remove elements during simulation.

Write the code to create an array which consists of 4 8-bit elements. Use an array literal to assign all of the elements in the array to 0.

// Array declaration
logic [7:0] example [4];
// Assign all elements using a list
example = '{ 0, 0, 0, 0 };
// Assign all elements using a pattern
example = '{ 4{0} };
// Assign all elements using the default keyword
example = '{ default:0 };

Write a foreach loop for the array which you created in the last exercise

// Array declaration
logic [7:0] example [4];

// foreach loop 
foreach ( example[i] ) begin
  example[i];
end;

Write the code to declare a 2D array of int types. The array should have 2 elements in the first dimension and 8 elements in the second dimension.

// Declaration using SystemVerilog syntax
int example [2] [8];
// Declaration using verilog style syntax
int example [1:0] [7:0];

What is the difference between an unpacked array and a packed array?

Each element of an unpacked array is stored in an individual memory address. In contrast, packed arrays are stored continuously in memory meaning that several elements can be stored in a single memory address.