Support SystemVerilog style partial ANSI port declarations

In Verilog it is possible to declare multiple ports as part of the same
port declaration. Ports declared this way all have same direction, signal
kind and data type. E.g.

```
module M (input [3:0] a, b, c) ...
```

SystemVerilog extends this and allows to override on a per port basis
certain port attributes. E.g. redefine just the data type

```
module test (input [3:0] a, [1:0] b, int c) ...
```

Or to just redefine the port kind

```
module test(input [3:0] a, var b, wire c) ...
```

It is even possible to leave out the direction for the very first port. As
long as at least one other property of the port is specified. In that case
the direction will default to `inout`. E.g.

```
module test(integer a, b, c) ...
```

Furthermore it is possible to specify unpacked dimensions for each of the
ports. E.g.

```
module test(input integer a, b[1:0], c[3:0][1:0]) ...
```

If all port properties are omitted for the first port this indicates the
start of a non-ANSI port list.

Extend the parser to handle this.

If all three direction, port kind and data type are omitted they are
inherited from the previous port. Otherwise

 * If the direction is omitted it is inherited from the previous port.
 * If the data type is omitted it defaults to logic.
 * If the port kind is omitted the behavior depends on the direction.
   For output ports with an explicit data type it is a variable, for
   all others it is a net.

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
This commit is contained in:
Lars-Peter Clausen 2022-09-14 23:00:22 +02:00
parent 82a974a801
commit 664a611e16
1 changed files with 37 additions and 35 deletions

72
parse.y
View File

@ -352,6 +352,13 @@ static void current_function_set_statement(const YYLTYPE&loc, std::vector<Statem
current_function->set_statement(tmp);
}
static void port_declaration_context_init(void)
{
port_declaration_context.port_type = NetNet::PINOUT;
port_declaration_context.port_net_type = NetNet::IMPLICIT;
port_declaration_context.data_type = nullptr;
}
Module::port_t *module_declare_port(const YYLTYPE&loc, char *id,
NetNet::PortType port_type,
NetNet::Type net_type,
@ -705,6 +712,7 @@ Module::port_t *module_declare_port(const YYLTYPE&loc, char *id,
%type <decl_assignments> list_of_variable_decl_assignments
%type <data_type> data_type data_type_opt data_type_or_implicit data_type_or_implicit_or_void
%type <data_type> data_type_or_implicit_no_opt
%type <data_type> simple_type_or_string let_formal_type
%type <data_type> packed_array_data_type
%type <data_type> ps_type_identifier
@ -1337,7 +1345,11 @@ scalar_vector_opt /*IEEE1800-2005: optional support for packed array */
;
data_type_or_implicit /* IEEE1800-2005: A.2.2.1 */
: data_type_opt
: data_type_or_implicit_no_opt
| { $$ = nullptr; }
data_type_or_implicit_no_opt
: data_type
{ $$ = $1; }
| signing dimensions_opt
{ vector_type_t*tmp = new vector_type_t(IVL_VT_LOGIC, $1, $2);
@ -4466,39 +4478,16 @@ list_of_port_declarations
tmp->push_back($3);
$$ = tmp;
}
| list_of_port_declarations ',' IDENTIFIER initializer_opt
{ Module::port_t*ptmp;
perm_string name = lex_strings.make($3);
ptmp = pform_module_port_reference(@3, name);
std::vector<Module::port_t*>*tmp = $1;
tmp->push_back(ptmp);
| list_of_port_declarations ',' attribute_list_opt IDENTIFIER dimensions_opt initializer_opt
{ std::vector<Module::port_t*> *ports = $1;
if ($4) {
switch (port_declaration_context.port_type) {
case NetNet::PINOUT:
yyerror(@4, "error: Default port value not allowed for inout ports.");
break;
case NetNet::PINPUT:
pform_requires_sv(@4, "Default port value");
ptmp->default_value = $4;
break;
case NetNet::POUTPUT:
pform_make_var_init(@3, name, $4);
break;
default:
break;
}
}
/* Get the port declaration details, the port type
and what not, from context data stored by the
last port_declaration rule. */
pform_module_define_port(@3, name,
port_declaration_context.port_type,
port_declaration_context.port_net_type,
port_declaration_context.data_type,
nullptr, nullptr);
delete[]$3;
$$ = tmp;
Module::port_t* port;
port = module_declare_port(@4, $4, port_declaration_context.port_type,
port_declaration_context.port_net_type,
port_declaration_context.data_type,
$5, $6, $3);
ports->push_back(port);
$$ = ports;
}
| list_of_port_declarations ','
{ yyerror(@2, "error: Superfluous comma in port declaration list."); }
@ -4506,9 +4495,21 @@ list_of_port_declarations
{ yyerror(@2, "error: ';' is an invalid port declaration separator."); }
;
// All of port direction, port kind and data type are optional, but at least
// one has to be specified, so we need multiple rules.
port_declaration
: attribute_list_opt port_direction net_type_or_var_opt data_type_or_implicit IDENTIFIER dimensions_opt initializer_opt
{ $$ = module_declare_port(@2, $5, $2, $3, $4, $6, $7, $1);
{ $$ = module_declare_port(@5, $5, $2, $3, $4, $6, $7, $1);
}
| attribute_list_opt net_type_or_var data_type_or_implicit IDENTIFIER dimensions_opt initializer_opt
{ pform_requires_sv(@4, "Partial ANSI port declaration");
$$ = module_declare_port(@4, $4, port_declaration_context.port_type,
$2, $3, $5, $6, $1);
}
| attribute_list_opt data_type_or_implicit_no_opt IDENTIFIER dimensions_opt initializer_opt
{ pform_requires_sv(@3, "Partial ANSI port declaration");
$$ = module_declare_port(@3, $3, port_declaration_context.port_type,
NetNet::IMPLICIT, $2, $4, $5, $1);
}
| attribute_list_opt port_direction K_wreal IDENTIFIER
{ real_type_t*real_type = new real_type_t(real_type_t::REAL);
@ -4613,7 +4614,8 @@ cont_assign_list
module
: attribute_list_opt module_start lifetime_opt IDENTIFIER
{ pform_startmodule(@2, $4, $2==K_program, $2==K_interface, $3, $1); }
{ pform_startmodule(@2, $4, $2==K_program, $2==K_interface, $3, $1);
port_declaration_context_init(); }
module_package_import_list_opt
module_parameter_port_list_opt
module_port_list_opt