From 664a611e16a3a0306147127c08aa4855aa3f06f0 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 14 Sep 2022 23:00:22 +0200 Subject: [PATCH] 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 --- parse.y | 72 +++++++++++++++++++++++++++++---------------------------- 1 file changed, 37 insertions(+), 35 deletions(-) diff --git a/parse.y b/parse.y index 950910368..b7f880b8c 100644 --- a/parse.y +++ b/parse.y @@ -352,6 +352,13 @@ static void current_function_set_statement(const YYLTYPE&loc, std::vectorset_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 list_of_variable_decl_assignments %type data_type data_type_opt data_type_or_implicit data_type_or_implicit_or_void +%type data_type_or_implicit_no_opt %type simple_type_or_string let_formal_type %type packed_array_data_type %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*tmp = $1; - tmp->push_back(ptmp); + | list_of_port_declarations ',' attribute_list_opt IDENTIFIER dimensions_opt initializer_opt + { std::vector *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