SystemVerilog constructs and operations require a certain level of type compatibility for their operands to be legal. There are four levels of type compatibility: Equivalent, Assignment Compatible, Cast Compatible, and Non-Equivalent.
Equivalent Types
Two data types shall be defined as equivalent data types using the following inductive definition. If the two data types are not defined as equivalent using the following definition, then they shall be defined to be non-equivalent.
- Any built-in type is equivalent to every other occurrence of itself, in every scope.
- A simple typedef or type parameter override that renames a built-in or user-defined type is equivalent to that built-in or user-defined type within the scope of the type identifier.
typedef bit node; // ’bit’ and ’node’ are equivalent types
typedef type1 type2; // ’type1’ and ’type2’ are equivalent types
- An anonymous enum, struct, or union type is equivalent to itself among variables declared within the same declaration statement and no other types.
struct {int A; int B;} AB1, AB2; // AB1, AB2 have equivalent types
struct {int A; int B;} AB3; // AB3 is not type equivalent to AB1
- A typedef for an enum, unpacked struct, or unpacked union, or a class is equivalent to itself and variables are declared using that type within the scope of the type identifier.
typedef struct {int A; int B;} AB_t;
AB_t AB1; AB_t AB2; // AB1 and AB2 have equivalent types
typedef struct {int A; int B;} otherAB_t;
otherAB_t AB3; // AB3 is not type equivalent to AB1 or AB2
- Packed arrays, packed structures, and built-in integral types are equivalent if they contain the same number of total bits, are either all 2-state or all 4-state, and are either all signed or all unsigned. Note that if any bit of a packed structure or union is 4-state, the entire structure or union is considered 4-state.
typedef bit signed [7:0] BYTE; // equivalent to the byte type
typedef struct packed signed {bit[3:0] a, b;} uint8; // equivalent to the byte type
- Unpacked array types are equivalent by having equivalent element types and identical shapes. Shape is defined as the number of dimensions and the number of elements in each dimension, not the actual range of the dimension.
bit [9:0] A[0:5];
bit [1:10] B[6];
typedef bit [10:1] uint10;
uint10 C[6:1]; // A, B and C have equivalent types
typedef int anint[0:0]; // anint is not type equivalent to int
- Explicitly adding signed or unsigned modifiers to a type that does not change its default signing, does not create a non-equivalent type. Otherwise, the signing must match to have equivalence.
typedef bit unsigned ubit; // type equivalent to bit
- A typedef for an enum, unpacked struct, or unpacked union, or a class type declared in a package is always equivalent to itself, regardless of the scope where the type is imported.
The scope of a type identifier includes the hierarchical instance scope. This means that each instance with user-defined types declared inside the instance creates a unique type. To have type equivalence among multiple instances of the same module, interface, or program, a type must be declared at higher level in the compilation unit scope than the declaration of the module, interface or program, or imported from a package.
The following example is assumed to be within one compilation unit, although the package declaration need not be in the same unit:
package p1;
typedef struct {int A;} t_1;
endpackage
typedef struct {int A;} t_2;
module sub();
import p1:t_1;
parameter type t_3 = int;
parameter type t_4 = int;
typedef struct {int A;} t_5;
t_1 v1; t_2 v2; t_3 v3; t_4 v4; t_5 v5;
endmodule
module top();
typedef struct {int A;} t_6;
sub #(.t_3(t_6)) s1 ();
sub #(.t_3(t_6)) s2 ();
initial begin
s1.v1 = s2.v1; // legal - both types from package p1
s1.v2 = s2.v2; // legal - both types from $unit
s1.v3 = s2.v3; // legal - both types from top
s1.v4 = s2.v4; // legal - both types are int
s1.v5 = s2.v5; // illegal - types from s1 and s2
end
endmodule
Assignment Compatible
All equivalent types and all non-equivalent types that have implicit casting rules defined between them are assignment-compatible types. For example, all integral types are assignment-compatible. Conversion between assignment-compatible types can involve loss of data by truncation or rounding.
Compatibility can be in one direction only. For example, an enum can be converted to an integral type without a cast, but not the other way around.
Cast Compatible
All assignment-compatible types, plus all non-equivalent types that have defined explicit casting rules are cast-compatible types. For example, an integral type requires a cast to be assigned to an enum.
Type Incompatible
These are all the remaining non-equivalent types that have no defined implicit or explicit casting rules. Class handles and chandles are type incompatible with all other types.