Section 3.2

LRM-125

Change in Syntax 3-1 (changes in red and blue):

enum_base_type ::=

  integer_atom_type [ signing ]

| integer_vector_type [ signing ] [ packed_dimension ]

 

enum_name_declaration ::=

enum_identifier [ [ integral_number ] | [ integral_number : integral_number ] ]

[ = constant_expression ]

LRM-122

Change in Syntax 3-1 (changes in red and blue):

struct_union_member ::=

  { attribute_instance } data_type { packed_dimension }

      variable_identifier variable_dimension { , variable_identifier variable_dimension } list_of_variable_identifiers ;

| { attribute_instance } void list_of_variable_identifiers ;

 

struct_union ::= struct | union [ tagged ]

Section 3.10

LRM-125

Add to Syntax 3-3 (changes in red and blue):

enum_base_type ::=

  integer_atom_type [ signing ]

| integer_vector_type [ signing ] [ packed_dimension ]

 

enum_name_declaration ::=

enum_identifier [ [ integral_number ] | [ integral_number : integral_number ] ]

[ = constant_expression ]

Section 3.11

LRM-122

Change in Syntax 3-4 (changes in red and blue):

struct_union_member ::=

  { attribute_instance } data_type { packed_dimension }

      variable_identifier variable_dimension { , variable_identifier variable_dimension } list_of_variable_identifiers ;

| { attribute_instance } void list_of_variable_identifiers ;

 

struct_union ::= struct | union [ tagged ]

LRM-122

Changes (changes in red and blue):

typedef struct signed {

int f1 ;

logic f2 ;

} sIllegalSignedUnpackedStructType; // illegal declaration

 

The qualifier tagged in a union declares it as a tagged union, which is a type-checked union. An ordinary (untagged) union can be updated using a value of one member type and read as a value of another member type, which is a potential type loophole. A tagged union stores both the member value and a tag, i.e., additional bits representing the current member name. The tag and value can only be updated together consistently, using a statically type-checked tagged union expression (Section 7.15). The member value can only be read with a type that is consistent with the current tag value (i.e., member name). Thus, it is impossible to store a value of one type and (mis)interpret the bits as another type.

 

In addition to type safety, the use of member names as tags also makes code simpler and smaller than code that has to track unions with explicit tags. Tagged unions can also be used with pattern matching (Section 8.4), which improves readability even further.

 

In tagged unions, members can be declared with type void, when all the information is in the tag itself, as in the following example of an integer together with a valid bit:

 

typedef union tagged {

void Invalid;

int Valid;

} VInt;

 

A value of VInt type is either Invalid and contains nothing, or is Valid and contains an int. Section 7.15 describes how to construct values of this type, and also describes how it is impossible to read an integer out of a VInt value that currently has the Invalid tag.

 

Example:

 

typedef union tagged {

struct {

bit [4:0] reg1, reg2, regd;

} Add;

union tagged {

bit [9:0] JmpU;

struct {

bit [1:0] cc,

bit [9:0] addr;

} JmpC;

} Jmp;

} Instr;

 

A value of Instr type is either an Add instruction, in which case it contains three 5-bit register fields, or it is a Jmp instruction. In the latter case, it is either an unconditional jump, in which case it contains a 10-bit destination address, or it is a conditional jump, in which case it contains a 2-bit condition-code register field and a 10-bit destination address. Section 7.15 describes how to construct values of Instr type, and describes how, in order to read the cc field, for example, the instruction must have opcode Jmp and sub-opcode JmpC.

 

When the packed qualifier is used on a tagged union, all the members must have packed types, but they do not have to be of the same size. The (standard) representation for a packed tagged union is the following.

 

      The size is always equal to the number of bits needed to represent the tag plus the maximum of the sizes of the members.

 

      The size of the tag is the minimum number of bits needed to code for all the member names (e.g., 5 to 8 members would need 3 tag bits).

 

      The tag bits are always left-justified (i.e., towards the most-significant bits).

 

      For each member, the member bits are always right-justified (i.e., towards the least significant bits).

 

      The bits between the tag bits and the member bits are undefined. In the extreme case of a void member, only the tag is significant and all the remaining bits are undefined.

 

The representation scheme is applied recursively to any nested tagged unions.

 

Example: If the VInt type definition had the packed qualifier, Invalid and Valid values will have the following layouts, respectively:

 

1

32

0

x x x x x x x ... ... ... x x x x x x x x x

 

1

32

1

... an int value ...

tag is 0 for Invalid, 1 for Valid

           

 

Example: If the Instr type had the packed qualifier, its values will have the following layouts:

 

1

5

5

5

 

 

0

reg1

reg2

regd

Add Instrs

 

 

 

 

 

 

 

 

1

2

1

2

10

 

 

1

xx

0

xx

 

Jmp/JmpU Instrs

 

 

 

 

 

 

 

 

1

2

1

2

10

 

 

1

xx

1

cc

addr

Jmp/JmpC Instr

 

 

 

0 for JmpU, 1 for JmpC

 

 

0 for Add, 1 for Jmp

 

Section 3.14

LRM-124

Changes (changes in red and blue):

A data type can be changed by using a cast ( ) operation. The expression to be cast must be enclosed in parenthesis or within concatenation or replication braces and is self-determined.

 

int(2.0 * 3.0)

shortint’{8’hFA,8’hCE}

 

A positive decimal number as a data type means a number of bits to change the size.

 

17’(x - 2)

 

The signedness can also be changed.

 

signed’(x)

 

The expression inside the cast must be an integral value when changing the size or signing. When changing the size, the signing passes through unchanged. When changing the signing, the size passes through unchanged.