Structures and Unions in system verilog
Structures and unions are special data types which are used to group variables having different data types. We have seen that arrays also provide the grouping of various variables or elements. But in arrays, the data type of each element is the same, whereas structure and unions can have elements having different data types. Let’s explore structure and union in detail in this article. In the end, we will also know the difference between structure and union.
Structures
A structure is a collection of elements having different data types. These elements can be accessed as a whole or individually. Like Enums these can be used to make user-defined data types. When a structure is declared, the memory is allocated for all the elements.
Syntax
Struct keyword is used to declare a structure. If a user-defined data type needs to be created using the structure, the typedef keyword is also used. Please note that each element is seperated using ;
and not ,
.
<typedef> struct { <element1>; <element2>; ... } <name>;
// syntax example
struct {
int roll;
string name;
} student;
Example
module structure1();
typedef struct {
int roll;
string name;
int mark;
} student;
student s1, s2;
initial begin
s1 = '{ 23, "Amit", 78 };
$display("s1 = %p", s1);
$display("s2 = %p", s2);
// chaning one of the element
s1.name = "Anita";
$display("s1 = %p", s1);
// copyind one struct to another
s2 = s1;
$display("s2 = %p", s2);
end
endmodule
Output
# s1 = '{roll:23, name:"Amit", mark:78}
# s2 = '{roll:0, name:"", mark:0}
# s1 = '{roll:23, name:"Anita", mark:78}
# s2 = '{roll:23, name:"Anita", mark:78}
Try this code in EDA PlaygroundLike the arrays, a structure can also be packed or unpacked. This concept of a packed and unpacked structure is exclusive to System Verilog. All structures are by default unpacked in nature.
Packed structure
A packed structure like a packed array can be thought of as a single dimension memory or a vector. In a packed array, this vector is divided into equal size partitions which can sometimes be inefficient as often data would be of different lengths. Packed structure on the other hand divides the vector into unequal size partitions providing more efficient packing of data.
Syntax
<typedef> struct packed { <element1>; <element2>; ... } <name>;
Example
module structure2();
typedef struct packed{
int roll;
bit [10:0] mark;
} student;
student s1, s2;
initial begin
s1 = { 10'd23, 10'd78 };
$display("s1 = %p", s1);
// In this initialization the data will be
// stored correctly
s2 = { 32'd23, 11'd98};
$display("s2 = %p", s2);
end
endmodule
Output
In the below output it can be observed that in first initialization is not correct as the size of the assigned value was not correct.
# s1 = '{roll:11, mark:1102}
# s2 = '{roll:23, mark:98}
Try this code in EDA PlaygroundPacked vs unpacked structure
Packed Structure | Unpacked Structure |
---|---|
members can be of packed data type only | Members can be of any data type |
Initialization can be done for several members at once using the concatenation operator as well. This is because the packed structure is treated as a vector. | Initialization can be done for several members at once but only with the assignment operator, i.e., '{}. The concatenation operator cannot be used. |
In a packed structure, we can only use a packed data type. For example, a string cannot be used inside a packed structure. Also, all array types except a packed array cannot be used inside the packed address.
Unions
Unions are like structures, but in a union, we can only access one element at a time. When unions are declared the memory is allocated only for the largest data type.
Syntax
<typedef> union { <element1>; <element2>; ... } <name>;
// syntax example
union {
int a;
byte b;
} test;
Not all simulators support unions. Check the simulator manual before using unions.
Example
module union1 ();
union {
int n;
byte d;
} test;
initial begin
test.n = 'hFFFF;
$display("n = %0h", test.n );
$display("d = %0h", test.d );
test.d = 'h48;
$display("n = %0h", test.n );
$display("d = %0h", test.d );
$displayh("test = %p", test);
end
endmodule
Output
# n = ffff
# d = ff
# n = ff48
# d = 48
# test = '{n:0000ff48, d:48}
Structure vs Union
Structure | Union |
---|---|
Struct keyword is used for declaration | Union keyword is used for declaration |
The size of a structure is the sum of all the members of the structure | The size of a union is the size of the largest member of the union |
All members are assigned different memory location | The same memory location is shared by all the members |
Individual members can be accessed at a time | Only one member can be accessed at a time |
Several members can be initialized at once | Only the first member can be initialized |
Unions can also be declared as packed, but in the packed union, all the elements should be of the same size. I do not find packed union useful as all members have the same size and also store the same value at a given time. Thus, a packed union can be replaced with a simple data type variable.
Tagged Unions
We have seen that the unions in System Verilog is not very useful as the value changed for one variable is reflected in other variables defined inside union. System Verilog introduced a new concept of tagged unions. In tagged union, we can assign a tag to a particular variable of any data type. Using this tag we can know that union is currently used to store which variable. This is useful when we want to have a packet which can store different value at different times.
Example
Below is an example of tagged union. In this example we have defined a tagged union which can store cpu instruction. We know at a particular time a instruction packet can hold any one type of instruction. Thus, using tagged union can save memory location and also instruction packet can be generated easily.
Below typdef command defines a new union data type named Instr. This is a tagged union having 2 outer tags, i.e, Add
and Jmp
. Jmp
tag has inner tag which have 2 tags, i.e, JmpU
and JmpC
. When Add tag is used we can store 3 values each of 5 bits. Similarly if JmpU
is used, we can store a 10 bit address and for JmpC
we can have 2 bit condition along with the address.
Also note, if we have assigned a particular tag to the union, we can’t use any other tag to access the union. It will lead to a run time error.
module tagged_union();
typedef union tagged {
struct {
bit [4:0] a, b, c;
} Add;
union tagged {
bit [9:0] JmpU;
struct {
bit [1:0] cc;
bit [9:0] addr;
} JmpC;
} Jmp;
} Instr;
Instr i1;
reg e;
reg [4:0] e1, ed;
initial begin
e= 1'b1;
e1 = $urandom();
ed = $urandom();
i1 = ( e
? tagged Add { e1, 4, ed } // struct members by position
: tagged Add { b:45, a:3, c:19 });
$display("i1 = %p", i1.Add);
$display("i1 = %p", i1);
$display("i1 = %p", i1.Jmp.JmpC); // This will show a run time error as Add is assigned as tag
i1 = tagged Jmp (tagged JmpU 129); // struct members by position
$display("\ni1 = %p", i1.Jmp);
$display("i1 = %p", i1);
end
endmodule
Output
# i1 = '{a:24, b:4, c:6}
# i1 = '{Add:Add:'{a:24, b:4, c:6}}
# ** Error (suppressible): Union is tagged 'Add', but is referenced as 'Jmp'.
# Time: 0 ns Iteration: 0 Instance: /tagged_union
# ** Error (suppressible): Union is tagged 'ëþð', but is referenced as 'JmpC'.
# Time: 0 ns Iteration: 0 Instance: /tagged_union
# i1 = '{cc:0, addr:4}
#
# i1 = '{JmpU:JmpU:129}
# i1 = '{Jmp:Jmp:'{JmpU:JmpU:129}}
Eda playground link is not given for this example as the free simulator available in Eda playground does not support tagged Unions as of now. It can be simulated in simulators like Questasim, VCS, etc.