Essentially I was trying to figure out a way that I don't have to use this clumsy workaround in my RTL to get it to compile cleanly. After trying several fruitless ways of searching for the answer, I decided to try leaving the question here for the experts.
Consider the following simplified example of what I had to do to get a clean compile of my synthesizable RTL. (please forgive any possible minor syntax errors as I manually obfuscated this from my real design and didn't compile it afterward)
typedef enum logic [1:0]
{ENUM_A,
ENUM_B,
ENUM_C,
ENUM_D
} t_gen_enum;
module example
(input t_gen_enum enum_in,
output t_gen_enum enum_out
);
logic [$bits(t_gen_enum)-1:0] enum_out_temp;
STANDARD_CELL_BUF enum_out_buffer [$bits(t_gen_enum)-1:0]
(.i(enum_in),
.o(enum_out_temp));
assign enum_out = t_gen_enum'(enum_out_temp);
endmodule
If I try to connect the enumerated output in the same way I connect to the input, I get a warning like the following from vcs.
Warning-[ENUMASSIGN] Illegal assignment to enum variable
<filename>, <line_number>
example, "STANDARD_CELL_BUF enum_out_buffer[($bits(t_gen_enum) - 1):0]( .i (enum_in), .o (enum_out));"
Only expressions of the enum type can be assigned to an enum variable.
The type wire is incompatible with the enum 't_gen_enum'
Expression: o
Use the static cast operator to convert the expression to enum type.
Specifically it seems to be able to handle the enum being assigned to the inputs of the standard cell (which is an old school wire type) but not assigning the multiple wire type outputs back to the enumerated type.
I tried the obviously incorrect way (just in case) of putting the type cast directly in the output connection, but that naturally doesn't work.
module example
(input t_gen_enum enum_in,
output t_gen_enum enum_out
);
STANDARD_CELL_BUF enum_out_buffer [$bits(t_gen_enum)-1:0]
(.i(enum_in),
.o(t_gen_enum'(enum_out)}));
endmodule
I also tried one suggestion from this thread that does get rid of the compile warnings from vcs, but I'm not completely clear if the enum_out remains officially enumerated or not. The example code from trying this would be as follows.
module example
(input t_gen_enum enum_in,
output t_gen_enum enum_out
);
STANDARD_CELL_BUF enum_out_buffer [$bits(t_gen_enum)-1:0]
(.i(enum_in),
.o({enum_out}));
endmodule
My workaround does compile cleanly, but feels clumsy. The other workaround is fairly clean but I'm not sure if the signal actually remains enumerated after connecting using the {} syntax.
I might be stuck with this, but hoping that someone here might know a better way.
The intermediate assignment and curly bracket approaches are both acceptable. The intermediate value is more common because it is straight forward for others to understand. Using the curly bracket to cast the enum as a packed vector is more nuanced, so when you do see it it often has a comment next to it.
The enum_out
is still treated as a enum outside of the {enum_out}
. The easiest way to prove it is to probe the signal in a waveform viewer that supports displaying the names of enums or by put temporary code to output enum_out.name()
as it changes.
The curly bracts concatenate the variable and treats it as an unsigned packed array per IEEE1800-2017 § 11.4.12 Concatenation operators
The concatenation is treated as a packed vector of bits that section
What is interesting is that concatenation requires only one expression as stated in IEEE1800-2017 § A.8.1 Concatenations:
concatenation ::=
{
expression {,
expression }}
Then in consider IEEE1800-2017 § 23.3.3.2 Port connection rules for variables**** it states:
— An output port can be connected to a variable (or a concatenation) of a compatible data type. A continuous assignment shall be implied when a variable is connected to the output port of an instance. Procedural or continuous assignments to a variable connected to the output port of an instance shall be illegal.
An emum of the same bit-width is technically a compatible data type, but some tools do not see it as a directly compatible data type so they give a warning. By adding the curly brackets around the enum, it casts it into a packed vector which is a directly compatible data type to the arrayed module.
A third approaches would be using the square brackets to get the range of the desired mapped bits. Like the curly bracket concatenation, the square bracket range is also treated as packed vector.
module example
(input t_gen_enum enum_in,
output t_gen_enum enum_out
);
STANDARD_CELL_BUF enum_out_buffer [$bits(t_gen_enum)-1:0]
(.i(enum_in),
.o(enum_out[$bits(t_gen_enum)-1:0]));
endmodule