Handling of SystemVerilog-2012 port types.
Here are a few key grammar rules within SystemVerilog ports:
net_port_type ::= [ net_type ] data_type_or_implicit | identifier | 'interconnect' implicit_data_type variable_port_type ::= var_data_type var_data_type ::= data_type | 'var' data_type_or_implicit data_type_or_implicit ::= data_type | implicit_data_type implicit_data_type ::= [ signing ] { packed_dimension }
These are used in two places. The first is in non-ANSI style SystemVerilog port declarations, where we have:
inout_declaration ::= 'inout' net_port_type list_of_port_identifiers input_declaration ::= 'input' net_port_type list_of_port_identifiers | 'input' variable_port_type list_of_variable_identifiers output_declaration ::= 'output' net_port_type list_of_port_identifiers | 'output' variable_port_type list_of_variable_port_identifiers ref_declaration ::= 'ref' variable_port_type list_of_variable_identifiers
The other is in ANSI-style SystemVerilog port declarations, where we have some trivial wrappers:
net_port_header ::= [port_direction] net_port_type variable_port_header ::= [port_direction] variable_port_type interface_port_header ::= identifier [ '.' identifier ] | 'interface' [ '.' identifier ]
And then the main port declaration syntax:
ansi_port_declaration ::= [ net_port_header | interface_port_header ] identifier {unpacked_dimension} [ '=' expression ] | [ variable_port_header ] identifier {variable_dimension} [ '=' expression ] | [ port_direction ] '.' identifier '(' [expression] ')'
Determining the type of a port is tricky. Consider the above rules and suppose we're past any port direction stuff. Then,
In either case, the port type is followed by an identifier (the port name), which may have then be followed by unpacked/variable dimensions. So to handle the trickier ANSI case, we basically want to parse:
my_port_type ::= net_port_type | variable_port_type | interface_port_type net_port_type ::= [net_type] data_type | [net_type] [signing] {packed_dimension} | identifier | 'interconnect' [signing] {packed_dimension} variable_port_type ::= data_type | 'var' data_type | 'var' [signing] {packed_dimension} interface_port_header ::= identifier [ '.' identifier ] | 'interface' [ '.' identifier ]
Where
The tricky case is what to do if we find an identifier. A leading identifier might be:
Both NCVerilog and VCS appear to require uses of data type names to come earlier in the parse order. However, they allow interface names to be used even before the interface is defined.
I believe it is the case that, whenever we see
module tricky; typedef logic [2:0] foo_t; endmodule module m (tricky.foo_t a); ... endmodule
See in particular the
The other tricky possibility is that we have a port such as