SystemVerilog queue is very powerful data structures that allow you to store elements in a First In, First Out (FIFO) order. The unique feature of a queue is its ability to dynamically change its size, allowing it to store a varying number of elements. Queues in SystemVerilog are highly versatile and can store any type of data. This guide will provide an in-depth understanding of how queues work in SystemVerilog, their types, and their methods.
What is a SystemVerilog Queue?
A SystemVerilog queue is similar to an unpacked array, but with a major difference: its size can grow and shrink automatically. A queue allows you to manipulate its elements using various operations, including indexing, concatenation, and slicing. You can also pass queues to functions or tasks, either as reference (ref
) or non-reference (non-ref
) arguments.
Types of SystemVerilog Queues
There are two types of queues in SystemVerilog: bounded queues and unbounded queues. Each type has its specific behavior.
Bounded Queue
A bounded queue has a fixed size, meaning you can define the maximum number of elements it can store. Once the queue reaches its maximum size, it cannot accept more elements.
Example of a Bounded Queue:
int bounded_queue [$:10]; // Depth 10
Here, bounded_queue
can hold a maximum of 10 elements. It cannot grow beyond this size.
Unbounded Queue
An unbounded queue has no size limit and can store an unlimited number of elements. The size of an unbounded queue grows dynamically as you add more items.
Example of an Unbounded Queue:
int unbounded_queue [$]; // Unlimited entries
The unbounded_queue
can grow indefinitely and holds as many elements as required.
Basic Queue Operations
SystemVerilog queues allow you to perform various operations on their elements, such as insertion, deletion, and access. Let’s explore how to use these operations effectively.
Initializing Queues
You can initialize queues with elements, as shown in the following examples:
int q1 [$] = {1, 2, 3, 4, 5}; // Integer queue with initial values
int q2 [$]; // Empty integer queue
int tmp; // Temporary variable to store values
Accessing Elements
To access queue elements, you can use indexing. SystemVerilog supports both regular and reverse indexing for queues.
tmp = q1[0]; // Get the first item of q1 (index 0)
tmp = q1[$]; // Get the last item of q1 (index 4)
Modifying Queue Elements
You can modify elements in a queue as follows:
q2 = q1; // Copy all elements from q1 to q2
q1 = {}; // Empty the queue (delete all items)
q2[2] = 15; // Replace element at index 2 with 15
q2.insert(2, 15); // Insert value 15 at index 2
q2 = {q2, 22}; // Append 22 to the end of q2
q2 = {99, q2}; // Add 99 at the beginning of q2
q2 = q2[1:$]; // Delete the first item
q2 = q2[0:$-1]; // Delete the last item
q2 = q2[1:$-1]; // Delete both the first and last items
Using a Queue in a SystemVerilog Module
Here’s an example of how to use a queue in a SystemVerilog module to display and manipulate queue elements:
module tb;
// Create a queue that can store "string" values
string fruits [$] = {"orange", "apple", "kiwi"};
initial begin
// Iterate through each queue element
foreach (fruits[i])
$display("fruits[%0d] = %s", i, fruits[i]);
// Display all elements in the queue
$display("fruits = %p", fruits);
// Delete all elements in the queue
fruits = {};
$display("After deletion, fruits = %p", fruits);
end
endmodule
Queue Slicing
You can use slice expressions to select a subset of elements in a queue. This is useful when you want to access a range of elements.
$display("citrus fruits = %p", fruits[1:2]);
$display("fruits = %p", fruits[1:$]);
SystemVerilog Queue Methods
SystemVerilog provides several built-in methods to manipulate queues. Below is a table listing the most commonly used methods:
Method | Description |
---|---|
int size() | Returns the number of items in the queue. Returns 0 if empty. |
void insert(int index, item) | Inserts the item at the specified index position. |
void delete(int index) | Deletes the element at the specified index. Deletes all if index is not provided. |
element_t pop_front() | Removes and returns the first element of the queue. |
element_t pop_back() | Removes and returns the last element of the queue. |
void push_front(item) | Inserts an item at the front of the queue. |
void push_back(item) | Inserts an item at the end of the queue. |
Example of Queue Operations:
module tb;
string fruits [$] = {"apple", "pear", "mango", "banana"};
initial begin
// size() - Get the size of the queue
$display("Number of fruits=%0d fruits=%p", fruits.size(), fruits);
// insert() - Insert an element at the specified index
fruits.insert(1, "peach");
$display("Insert peach, size=%0d fruits=%p", fruits.size(), fruits);
// delete() - Delete element at the given index
fruits.delete(3);
$display("Delete mango, size=%0d fruits=%p", fruits.size(), fruits);
// pop_front() - Pop the first element
$display("Pop %s, size=%0d fruits=%p", fruits.pop_front(), fruits.size(), fruits);
// push_front() - Push a new element at the front
fruits.push_front("apricot");
$display("Push apricot, size=%0d fruits=%p", fruits.size(), fruits);
// pop_back() - Pop the last element
$display("Pop %s, size=%0d fruits=%p", fruits.pop_back(), fruits.size(), fruits);
// push_back() - Push an element to the back
fruits.push_back("plum");
$display("Push plum, size=%0d fruits=%p", fruits.size(), fruits);
end
endmodule
Class-Based Queues
SystemVerilog allows you to create queues that hold objects of a class. For example, let’s create a queue of Fruit
objects:
class Fruit;
string name;
function new(string name="Unknown");
this.name = name;
endfunction
endclass
module tb;
// Create a queue of Fruit objects
Fruit list [$];
initial begin
// Create a new class object for "Apple" and add it to the queue
Fruit f = new("Apple");
list.push_back(f);
// Create another "Banana" object and add it to the queue
f = new("Banana");
list.push_back(f);
// Iterate through the queue and display each fruit's name
foreach (list[i])
$display("list[%0d] = %s", i, list[i].name);
end
endmodule
Queue of Dynamic Arrays
You can also create a queue of dynamic arrays in SystemVerilog. Here’s an example:
typedef string str_da [];
module tb;
// Create a queue of dynamic arrays
str_da list [$];
initial begin
// Initialize dynamic arrays
str_da marvel = {"Spiderman", "Hulk", "Captain America"};
str_da dcWorld = {"Batman", "Superman"};
// Add dynamic arrays to the queue
list.push_back(marvel);
list.push_back(dcWorld);
// Access elements from the dynamic arrays inside the queue
foreach (list[i])
foreach (list[i][j])
$display("list[%0d][%0d] = %s", i, j, list[i][j]);
// Print the entire queue
$display("list = %p", list);
end
endmodule
Simulation Output
After running the simulations, you will see the following output for various operations:
Number of fruits=4 fruits='{"apple", "pear", "mango", "banana"}
Insert peach, size=5 fruits='{"apple", "peach", "pear", "mango", "banana"}'
Delete mango, size=4 fruits='{"apple", "peach", "pear", "banana"}'
Pop apple, size=3 fruits='{"peach", "pear", "banana"}'
Push apricot, size=4 fruits='{"apricot", "peach", "pear", "banana"}'
Pop banana, size=3 fruits='{"apricot", "peach", "pear"}'
Push plum, size=4 fruits='{"apricot", "peach", "pear", "plum"}'
Conclusion
SystemVerilog queues are powerful, dynamic data structures that offer flexibility in handling a variety of data types. Understanding how to use bounded and unbounded queues, along with the built-in methods, can significantly improve your designs. Whether you’re managing simple data types or complex class objects, SystemVerilog queues provide the tools you need for efficient data storage and manipulation.