Associative arrays

When the size of the collection is unknown or the data space is sparse, an associative array is a better option. Associative arrays do not have any storage allocated until it is used, and the index expression is not restricted to integral expressions but can be of any type.
An associative array implements a lookup table of the elements of its declared type. The data type to be used as an index serves as the lookup key, and imposes an ordering.

The syntax to declare an associative array is:

data_type array_id [ index_type ];

where:

  • data_type is the data type of the array elements. Can be any type allowed for fixed-size arrays.
  • array_id is the name of the array being declared.
  • index_type is the data type to be used as an index, or *. If * is specified, then the array is indexed by any

integral expression of arbitrary size. An index type restricts the indexing expressions to a particular type.

Examples of associative array declarations are:

integer i_array[*]; // associative array of integer (unspecified index)
bit [20:0] array_b[string]; // associative array of 21-bit vector, indexed by string
event ev_array[myClass]; // associative array of event indexed by class myClass

Array elements in associative arrays are allocated dynamically; an entry is created the first time it is written. The associative array maintains the entries that have been assigned values and their relative order according to the index data type. Associative array elements are unpacked, meaning that other than copying or comparing arrays, you must select an individual element out of the array before using it in most expressions.

Wildcard index type

Example: int array_name [*];

Associative arrays that specify a wildcard index type have the following properties:

  • The array can be indexed by any integral data type. Since the indices can be of different sizes, the same numerical value can have multiple representations, each of a different size. SystemVerilog resolves this ambiguity by detecting the number of leading zeros and computing a unique length and representation for every value.
  • Non-integral index types are illegal and result in a type check error.
  • A 4-state Index containing X or Z is invalid.
  • Indices are unsigned.
  • Indexing expressions are self-determined; signed indices are not sign extended.
  • A string literal index is auto-cast to a bit-vector of equivalent size.
  • The ordering is numerical (smallest to largest).

String index

Example: int array_name [ string ];

Associative arrays that specify a string index have the following properties:

  • Indices can be strings or string literals of any length. Other types are illegal and shall result in a type check error.
  • An empty string “” index is valid.
  • The ordering is lexicographical (lesser to greater).

Class index

Example: int array_name [ some_Class ];

Associative arrays that specify a class index have the following properties:

  • Indices can be objects of that particular type or derived from that type. Any other type is illegal and shall result in a type check error.
  • A null index is valid.
  • The ordering is deterministic but arbitrary.

Integer (or int) index

Example: int array_name [ integer ];

Associative arrays that specify an integer index have the following properties:

  • Indices can be any integral expression.
  • Indices are signed.
  • A 4-state index containing X or Z is invalid.
  • Indices smaller than integer are sign extended to 32 bits.
  • Indices larger than integer are truncated to 32 bits.
  • The ordering is signed numerical.

Signed packed array

Example: typedef bit signed [4:1] Nibble;

int array_name [ Nibble ];

Associative arrays that specify a signed packed array index have the following properties:

  • Indices can be any integral expression.
  • Indices are signed.
  • Indices smaller than the size of the index type are sign extended.
  • Indices larger than the size of the index type are truncated to the size of the index type.
  • The ordering is signed numerical.

Unsigned packed array or packed struct

Example: typedef bit [4:1] Nibble;

int array_name [ Nibble ];

Associative arrays that specify an unsigned packed array index have the following properties:

  • Indices can be any integral expression.
  • Indices are unsigned.
  • A 4-state Index containing X or Z is invalid.
  • Indices smaller than the size of the index type are zero filled.
  • Indices larger than the size of the index type are truncated to the size of the index type.
  • The ordering is numerical

Associative array methods

In addition to the indexing operators, several built-in methods are provided that allow users to analyze and manipulate associative arrays, as well as iterate over its indices or keys.

num()

The syntax for the num() method is:
function int num();

The num() method returns the number of entries in the associative array. If the array is empty, it returns 0.

int imem[*];
imem[ 2’b3 ] = 1;
imem[ 16’hffff ] = 2;
imem[ 4b’1000 ] = 3;
$display( "%0d entries\n", imem.num );
// prints “3 entries”

delete()

The syntax for the delete() method is:
function void delete( [input index] );

Where index is an optional index of the appropriate type for the array in question. If the index is specified, then the delete() method removes the entry at the specified index. If the entry to be deleted does not exist, the method issues no warning.

If the index is not specified, then the delete() method removes all the elements in the array.

int map[ string ];
map[ "hello" ] = 1;
map[ "sad" ] = 2;
map[ "world" ] = 3;

map.delete( "sad" ); // remove entry whose index is “sad” from “map”
map.delete; // remove all entries from the associative array “map”

exists()

The syntax for the exists() method is:
function int exists( input index );

Where index is an index of the appropriate type for the array in question. The exists() function checks if an element exists at the specified index within the given array. It returns 1 if the element exists, otherwise it returns 0.

if ( map.exists( "hello" ))
map[ "hello" ] += 1;
else
map[ "hello" ] = 0;

first()

The syntax for the first() method is:
function int first( ref index );

Where index is an index of the appropriate type for the array in question. The first() method assigns to the given index variable the value of the first (smallest) index in the associative array. It returns 0 if the array is empty, and 1 otherwise.

string s;
if ( map.first( s ) )
$display( "First entry is : map[ %s ] = %0d\n", s, map[s] );

last()

The syntax for the last() method is:
function int last( ref index );

Where index is an index of the appropriate type for the array in question. The last() method assigns to the given index variable the value of the last (largest) index in the associative array. It returns 0 if the array is empty, and 1 otherwise.

string s;
if ( map.last( s ) )
$display( "Last entry is : map[ %s ] = %0d\n", s, map[s] );

next()

The syntax for the next() method is:
function int next( ref index );

Where index is an index of the appropriate type for the array in question. The next() method finds the entry whose index is greater than the given index. If there is a next entry, the index variable is assigned the index of the next entry, and the function returns 1. Otherwise, the index is unchanged, and the function returns 0.

string s;
if ( map.first( s ) )
do
$display( "%s : %d\n", s, map[ s ] );
while ( map.next( s ) );

prev()

The syntax for the prev() method is:
function int prev( ref index );

Where index is an index of the appropriate type for the array in question. The prev() function finds the entry whose index is smaller than the given index. If there is a previous entry, the index variable is assigned the index of the previous entry, and the function returns 1. Otherwise, the index is unchanged, and the function returns 0.

string s;
if ( map.last( s ) )
do
$display( "%s : %d\n", s, map[ s ] );
while ( map.prev( s ) );

If the argument passed to any of the four associative array traversal methods first, last, next, and prev are smaller than the size of the corresponding index, then the function returns –1 and shall copy only as much data as can fit into the argument. For example:

string aa[*];
byte ix;
int status;
aa[ 1000 ] = "a";
status = aa.first( ix );

// status is –1
// ix is 232 (least significant 8 bits of 1000)

<< Previous | Next >>

Arrays

Introduction

An array is a collection of variables, all of the same type, and accessed using the same name plus one or more indices.

In Verilog-2001, arrays are indexed from left-bound to right-bound. If they are vectors, they can be assigned as a single unit, but not if they are arrays. Verilog-2001 allows multiple dimensions.

In Verilog-2001, all data types can be declared as arrays. The reg, wire and all other net types can also have a vector width declared. A dimension declared before the object name is referred to as the “vector width” dimension.

The dimensions declared after the object name are referred to as the “array” dimensions.

reg [7:0] r1 [1:256]; // [7:0] is the vector width, [1:256] is the array size

SystemVerilog uses the term “packed array” to refer to the dimensions declared before the object name (what Verilog-2001 refers to as the vector width). The term “unpacked array” is used to refer to the dimensions declared after the object name.

bit [7:0] c1; // packed array

real u [7:0]; // unpacked array

SystemVerilog enhances packed arrays by allowing multiple dimensions. SystemVerilog adds the ability to procedurally change the size of one of the dimensions of an unpacked array. Fixed-size unpacked arrays can be multi-dimensional and have fixed storage allocated for all the elements of the array. Each dimension of an unpacked array can be declared as having a fixed or un-fixed size.

A dynamic array allocates storage for elements at runtime along with the option of changing the size of one of its dimensions.

An associative array allocates storage for elements individually as they are written. Associative arrays can be indexed using arbitrary data types.

A queue type of array grows or shrinks to accommodate the number of elements written to the array at runtime.

Packed and unpacked arrays

A packed array is a mechanism for subdividing a vector into subfields which can be conveniently accessed as array elements. Consequently, a packed array is guaranteed to be represented as a contiguous set of bits.

An unpacked array may or may not be so represented. A packed array differs from an unpacked array in that when a packed array appears as a primary, it is treated as a single vector.

If a packed array is declared as signed, then the array viewed as a single vector shall be signed. The individual elements of the array are unsigned unless they are of a named type declared as signed. A part-select of a packed array shall be unsigned.

Packed arrays allow arbitrary length integer types, so a 48 bits integer can be made up of 48 bits. These integers can then be used for 48 bits arithmetic. The maximum size of a packed array can be limited but shall be at least 65536 (216) bits.

Packed arrays can only be made of the single bit types (bit, logic, reg, wire, and the other net types) and recursively other packed arrays and packed structures.

Integer types with predefined widths cannot have packed array dimensions declared. These types are byte, shortint, int, longint, and integer. An integer type with a predefined width can be treated as a single-dimension packed array. The packed dimensions of these integer types shall be numbered down to 0, such that the right-most index is 0.

byte c2; // same as bit [7:0] c2;

integer i1; // same as logic signed [31:0] i1;

Unpacked arrays can be made of any type. System Verilog enhances fixed-size unpacked arrays in that in addition to all other variable types, unpacked arrays can also be made of object handles and events.

System Verilog accepts a single number, as an alternative to a range, to specify the size of an unpacked array, like C. That is, [size] becomes the same as [0:size-1]. For example:

int Array[8][32]; is the same as: int Array[0:7][0:31];

The following operations can be performed on all arrays, packed or unpacked. The examples provided with these rules assume that A and B are arrays of the same shape and type.

  • Reading and writing the array, e.g., A = B
  • Reading and writing a slice of the array, e.g., A[i:j] = B[i:j]
  • Reading and writing a variable slice of the array, e.g., A[x+:c] = B[y+:c]
  • Reading and writing an element of the array, e.g., A[i] = B[i]
  • Equality operations on the array or slice of the array, e.g. A==B, A[i:j] != B[i:j]

The following operations can be performed on packed arrays, but not on unpacked arrays. The examples provided with these rules assume that A is an array.

  • Assignment from an integer, e.g., A = 8’b11111111;
  • Treatment as an integer in an expression, e.g., (A + 3)

If an unpacked array is declared as signed, then this applies to the individual elements of the array, since the whole array cannot be viewed as a single vector.

When assigning to an unpacked array, the source and target must be arrays with the same number of unpacked dimensions, and the length of each dimension must be the same. Assignment to an unpacked array is done by assigning each element of the source unpacked array to the corresponding element of the target unpacked array. Note that an element of an unpacked array can be a packed array.

For the purposes of assignment, a packed array is treated as a vector. Any vector expression can be assigned to any packed array. The packed array bounds of the target packed array do not affect the assignment. A packed array cannot be directly assigned to an unpacked array without an explicit cast.

Multiple dimensions

Like Verilog memories, the dimensions following the type set the packed size. The dimensions following the instance set the unpacked size.

bit [3:0] [7:0] john [1:10]; // 10 entries of 4 bytes (packed into 32 bits)

can be used as follows:

john[9] = john[8] + 1; // 4 byte add
john[7][3:2] = john[6][1:0]; // 2 byte copy

<< Previous | Next >>