While classic Verilog was limited to basic fixed-size arrays, SystemVerilog introduces a powerful and versatile set of storage types. Whether you need to model a massive sparse memory or build a flexible testbench scoreboard, choosing the right data structure will make your code faster, more efficient, and easier to write.
Here is a breakdown of the primary array and storage types in SystemVerilog and when to use them.
1. Fixed-Size Arrays
SystemVerilog brings several enhancements to the traditional fixed-size array. You can now use compact C-style declarations, such as int c_style;.
Initializing these arrays is significantly easier using array literals (an apostrophe followed by curly braces), allowing you to set multiple elements at once, such as '{0, 1, 2, 3}. To iterate through them, SystemVerilog provides the foreach loop, which automatically steps through all elements without you having to manually manage the index bounds.
SystemVerilog also introduces packed arrays. A packed array is stored as a contiguous set of bits with no unused space. This allows the variable to be treated as both an array (where you can access individual bits or bytes) and a single scalar value.
2. Dynamic Arrays
What if you are generating variable-length network packets and don’t know the required array size at compile time? Instead of declaring a massive fixed-size array that wastes memory, you can use a dynamic array.
Dynamic arrays are declared using empty brackets, like int dyn[];. At run-time, you use the new[] operator to allocate the exact amount of space you need, such as dyn = new;. You can even expand a dynamic array while preserving its existing contents by passing the old array into the constructor: dyn = new(dyn);.
3. Queues
A queue is an incredibly flexible data structure that combines the speed of a fixed-size array with the versatility of a linked list. Declared with a dollar sign subscript, like int q[$];, queues can grow and shrink automatically without needing the new[] operator.
Queues are highly optimized for adding and removing elements from the front or back. They come with powerful built-in methods like push_front(), push_back(), pop_front(), and pop_back(). This makes them the perfect data structure for building testbench scoreboards, where expected transactions are constantly being pushed in and popped out during simulation.
4. Associative Arrays
If you need to model a processor with a multi-gigabyte address space, allocating a standard array would instantly crash your simulator by exhausting all available memory.
The solution is an associative array, which stores data in a sparse matrix. SystemVerilog only allocates memory for an element when you actually write to it. Declared with wildcard syntax (logic [63:0] assoc[*];), associative arrays can be indexed with widely scattered values or even non-standard indices like strings (similar to Perl hash arrays).
5. Linked Lists
SystemVerilog does offer a parameterized linked list container, similar to the Standard Template Library (STL) List in C++.
However, the best advice for using linked lists in SystemVerilog is simple: avoid them. SystemVerilog’s built-in queues provide the same ability to add and remove elements dynamically, but they are significantly more efficient and much easier to use.
Summary: Choosing the Right Storage Type
- Fixed-size or Dynamic Arrays: Best when data is accessed sequentially with positive integer indices. Use fixed if the size is known at compile time, and dynamic if it is decided at run-time.
- Queues: The best choice for data sets that frequently grow and shrink, such as testbench scoreboards.
- Associative Arrays: Ideal for modeling massive sparse memories, or when you need to look up data using strings or widely scattered addresses.
