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.