Structure expressions

A structure expression (packed or unpacked) can be built from member expressions using braces and commas, with the members in declaration order. Replicate operators can be used to set the values for the exact number of members. Each member expression shall be evaluated in the context of an assignment to the type of the corresponding member in the structure. It can also be built with the names of the members.

module mod1;
typedef struct {
int x;
int y;
} st;
st s1;
int k = 1;
initial begin
#1 s1 = {1, 2+k}; // by position
#1 $display( s1.x, s1.y);
#1 s1 = {x:2, y:3+k); // by name
#1 $display( s1);
#1 $finish;

It can sometimes be useful to set structure members to a value without having to keep track of how many members there are, or what the names are. This can be done with the default keyword:

initial s1 = {default:2}; // sets x and y to 2

The {member:value} or {data_type: default_value} syntax can also be used:

ab abkey[1:0] = {{a:1, b:1.0}, {int:2, shortreal:2.0}};

Note that the default keyword applies to members in nested structures or elements in unpacked arrays in structures. It descends the nesting to a built-in type or a packed array of them.

struct {
int A;
struct {
int B, C;
} BC1, BC2;
ABC = {A:1, BC1:{B:2, C:3}, BC2:{B:4,C:5}};
DEF = {default:10};

To deal with the problem of members of different types, a type can be used as the key. This overrides the
default for members of that type:

typedef struct {
logic [7:0] a;
bit b;
bit signed [31:0] c;
string s;
} sa;
sa s2;
initial s2 = {int:1, default:0, string:""}; // set all to 0 except the array of bits to 1 and string to ""

Similarly, an individual member can be set to override the general default and the type default:

initial #10 s1 = {default:’1, s : ""}; // set all to 1 except s to ""

SystemVerilog determines the context of the braces when used in the context of an assignment. If used in the context of an assignment to an unpacked structure, the braces represent an unpacked structure literal or expression. Outside the context of an assignment to an aggregate type, an explicit cast must be used with the braces to distinguish it from a concatenation. When the braces include a label, type, or default key, the braces shall not be interpreted as a concatenation for both packed and unpacked structure types.

The matching rules are as follows:

  • A member:value: specifies an explicit value for a named member of the structure. The named member must be at the top level of the structure—a member with the same name in some level of substructure shall not be set. The value must be castable to the member type and is evaluated in the context of an assignment to the named member, otherwise, an error is generated.
  • The type:value specifies an explicit value for a field in the structure that is equivalent to the type and has not been set by a field name key above. If the same type key is mentioned more than once, the last value is used. The value is evaluated in the context of an assignment to the matching type.
  • The default:value applies to members that are not matched by either member name or type key and are not either structures or unpacked arrays. The value is evaluated in the context of each assignment to a member by default and must be castable to the member type, otherwise an error is generated. For unmatched structure members, the type and default specifiers are applied recursively according to the rules in this section to each member of the substructure. For unmatched unpacked array members, the type and default keys are applied to the array according to the rules for unpacked arrays. Every member must be covered by one of these rules. If the type key, default key, or replication operator is used on an expression with side effects, the number of times that expression evaluates is undefined.

<< Previous | Next >>

Structures and unions

Structure and union declarations follow the C syntax but without the optional structure tags before the ‘{‘.

struct { bit [7:0] opcode; bit [23:0] addr; } REG; // anonymous structure defines variable REG

REG.opcode = 1; // set field opcode in REG.

Some additional examples of declaring structure and unions are:

typedef struct {
bit [7:0] opcode;
bit [23:0] addr;
} REG ; // named structure type
REG REG1; // define variable

typedef union { int i; shortreal f; } num; // named union type
num n;
n.f = 0.0; // set n in floating point format

typedef struct {
bit isfloat;
union { int i; shortreal f; } n; // anonymous type
} tagged_st; // named structure
tagged_st a[9:0]; // array of structures

A structure can be assigned as a whole and passed to or from a function or task as a whole. A packed structure consists of bit fields, which are packed together in memory without gaps. This means that they are easily converted to and from bit vectors. An unpacked structure has an implementation-dependent packing, normally matching the C compiler.

Like a packed array, a packed structure can be used as a whole with arithmetic and logical operators. The first member specified is the most significant and subsequent members follow in decreasing significance. The structures are declared using the packed keyword, which can be followed by the signed or unsigned keywords, according to the desired arithmetic behavior. The default is unsigned:

struct packed signed {
int a;
shortint b;
byte c;
bit [7:0] d;
} pack1; // signed, 2-state

struct packed unsigned {
time a;
integer b;
logic [31:0] c;
} pack2; // unsigned, 4-state

If any data type within a packed structure is 4-state, the whole structure is treated as 4-state. Any 2-state members are converted as if cast. One or more bits of a packed structure can be selected as if it were a packed array, assuming an [n-1:0] numbering.

Non-integer data types, such as real and shortreal, are not allowed in packed structures or unions nor are in unpacked arrays.
A packed structure can be used with a typedef.

typedef struct packed { // default unsigned
bit [3:0] GFC;
bit [7:0] VPI;
bit [11:0] VCI;
bit CLP;
bit [3:0] PT ;
bit [7:0] HEC;
bit [47:0] [7:0] Payload;
bit [2:0] filler;
} s_atmcell;

A packed union shall contain members that must be packed structures or packed arrays or integer data types all of the same size (in contrast to an unpacked union, where the members can be of different sizes). This ensures that you can read back a union member that was written as another member. A packed union can also be used as a whole with arithmetic and logical operators, and its behavior is determined by the signed or unsigned keyword, the latter being the default. If a packed union contains a 2-state member and a 4-state member, the entire union is 4-state. There is an implicit conversion from 4-state to 2-state when reading and from 2-state to 4-state when writing the 2-state member.

For example, a union can be accessible with different access widths:

typedef union packed { // default unsigned
s_atmcell acell;
bit [423:0] bit_slice;
bit [52:0][7:0] byte_slice;
} u_atmcell;
u_atmcell u1;
byte b; bit [3:0] nib;
b = u1.bit_slice[415:408]; // same as b = u1.byte_slice[51];
nib = u1.bit_slice [423:420]; // same as nib = u1.acell.GFC;

Note that writing one member and reading another is independent of the byte ordering of the machine, unlike a normal union of normal structures, which are C-compatible and have members in ascending address order.

The signing of unpacked structures is not allowed. The following declaration would be considered illegal:

typedef struct signed {
int f1 ;
logic f2 ;
} IllegalSignedUnpackedStructType; // illegal declaration

<< Previous | Next >>