Understanding SystemVerilog Unions: A Beginner's Guide
If you're new to circuit design, you might have heard of unions but don't know what they are. In this post, we'll explain what unions are, their types, and how they can be used in SystemVerilog.
What are Unions in SystemVerilog?
Unions are a data type in SystemVerilog that represent a single piece of storage that can be accessed using one of the named member data types. They are useful when you want to interpret a single memory location as two or more data types. Only one of the data types in the union can be used at a time.
Types of Unions in SystemVerilog
There are three types of unions in SystemVerilog: unpacked unions, packed unions, and tagged unions.
Unpacked Unions
Unpacked unions are the default type of union in SystemVerilog. There is no required representation for how members of the union are stored. Let's take a look at an example:
typedef union {
int value;
byte byteVal;
shortreal shortVal;
} MyUnion;
In this example, we define a MyUnion
that contains three members: value
, byteVal
, and shortVal
. The three members have the same memory location.
Packed Unions
Packed unions are a specialized type of union that can hold only integral data types of the same size. In a packed union, all members share the same memory location and are treated as a single vector. This enables performing arithmetic and logical operations on the entire union as a single value, with its behavior determined by its signedness.
A member of a packed union that has been written as another member can be read back to provide a different view of the same data. To define a packed union, the packed
keyword is used. Let's see an example below:
typedef union packed {
bit [31:0] wordView;
struct packed {
bit [23:0] fraction;
bit [7:0] exponent;
} floatView;
} MyUnion;
module Testbench;
MyUnion myData;
initial begin
// Writing data to the union member "wordView"
myData.wordView = 1067030938;
// Reading the same data using a different view
$display("Fraction: %h, Exponent: %h", myData.floatView.fraction, myData.floatView.exponent);
end
endmodule
In this example, we define a union called MyUnion
that has two members: wordView
, which is a 32-bit bitvector, and floatView
, which is a struct that represents a floating-point number in terms of its fraction and exponent. We then create an instance of this union called myData
.
In the initial block of the Testbench
module, we write the value 1067030938 to the wordView
member of myData
. We then use the floatView
member to read the same data in a different view, printing out the fraction and exponent of the floating-point number using $display
.
Tagged Unions
Tagged unions are a special type of union that supports type checking. They store both the member value and a tag, which indicates the current member name. To update a tagged union, both the tag and value must be updated together using a statically type-checked tagged union expression. Trying to read or assign a value whose type is inconsistent with the tag leads to a run-time error.
Using member names as tags simplifies code and makes it more concise, providing additional type safety. Tagged unions can also be used with pattern matching, improving code readability. To declare a union as tagged, the tagged
qualifier is used. In tagged unions, void
can be used as a data type, where the tag itself holds all the information.
Let's take a look at an example:
typedef union tagged {
void Invalid;
int Valid;
} VInt;
VInt vi1, vi2;
vi1 = tagged Valid (23+34); // Create Valid int
vi2 = tagged Invalid; // Create an Invalid value
This code declares a tagged union called VInt
, which can have two different values: Invalid
and Valid
. Invalid
is of type void
and represents an invalid state, while Valid
is of type int
and holds an integer value.
The code then creates two instances of the VInt
union, vi1
and vi2
. The first instance, vi1
, is assigned the value Valid
with an integer value of 23+34, effectively creating a Valid
integer. The second instance, vi2
, is assigned the Invalid
value, indicating that it is in an invalid state. By using a tagged union, the code ensures that only the correct member values are accessed at any given time, providing additional type safety and simplifying the code by using member names as tags.
Conclusion
Unions are a powerful tool in SystemVerilog for interpreting memory locations as two or more data types. Understanding the different types of unions, including unpacked unions, packed unions, and tagged unions, can help you write more efficient and type-safe code. By using unions in your circuit designs, you can unlock a new level of flexibility and control over your data.