diff --git a/ivtest/ivltests/sv_module_port1.v b/ivtest/ivltests/sv_module_port1.v new file mode 100644 index 000000000..f80b4e554 --- /dev/null +++ b/ivtest/ivltests/sv_module_port1.v @@ -0,0 +1,33 @@ +// Check that partial module ANSI port declarations are supported. Check that it +// is possible to redefine the data type only. + +module test (input [3:0] a, [1:0] b, integer c, wire d); + + bit failed = 1'b0; + +`define check(val, exp) do begin \ + if ((val) !== (exp)) begin \ + $display("FAILED(%0d): Expected `%d`, got `%d`.", `__LINE__, \ + (exp), (val)); \ + failed = 1'b1; \ + end \ + end while (0) + + initial begin + `check($bits(a), 4); + `check($bits(b), 2); + `check($bits(c), $bits(integer)); + `check($bits(d), 1); + + // They should all be wires + `check(a, 'z); + `check(b, 'z); + `check(c, 'z); + `check(d, 'z); + + if (!failed) begin + $display("PASSED"); + end + end + +endmodule diff --git a/ivtest/ivltests/sv_module_port2.v b/ivtest/ivltests/sv_module_port2.v new file mode 100644 index 000000000..8537a29a6 --- /dev/null +++ b/ivtest/ivltests/sv_module_port2.v @@ -0,0 +1,29 @@ +// Check that partial module ANSI port declarations are supported. Check that it +// is possible to redefine the unpacked dimensions only. + +module test (input integer a, b[1:0], c[2:0][3:0]); + + bit failed = 1'b0; + +`define check(val, exp) do begin \ + if ((val) !== (exp)) begin \ + $display("FAILED(%0d): Expected `%d`, got `%d`.", `__LINE__, \ + (exp), (val)); \ + failed = 1'b1; \ + end \ + end while (0) + + initial begin + `check($dimensions(a), 1); + `check($dimensions(b), 2); + `check($dimensions(c), 3); + `check($bits(a), $bits(integer)); + `check($bits(b), $bits(integer) * 2); + `check($bits(c), $bits(integer) * 3 * 4); + + if (!failed) begin + $display("PASSED"); + end + end + +endmodule diff --git a/ivtest/ivltests/sv_module_port3.v b/ivtest/ivltests/sv_module_port3.v new file mode 100644 index 000000000..3611b5e7c --- /dev/null +++ b/ivtest/ivltests/sv_module_port3.v @@ -0,0 +1,49 @@ +// Check that it is possible to declare module ports without specifing the +// direction. + +bit failed = 1'b0; + +`define check(val, exp) do begin \ + if ((val) !== (exp)) begin \ + $display("FAILED(%0d): Expected `%d`, got `%d`.", `__LINE__, \ + (exp), (val)); \ + failed = 1'b1; \ + end \ + end while (0) + +// All ports should be inout if no direction is specified +module M (wire [31:0] a, b, c); + + assign c = a ^ b; + + initial begin + `check($bits(a), 32); + `check($bits(b), 32); + `check($bits(c), 32); + end + +endmodule + +module test; + + wire [31:0] a, b, c; + + M i_m (a, b, c); + + `define A 'h01234567 + `define B 'hfedcba98 + + assign a = `A; + assign b = `B; + + initial begin + #1 + + `check(c, `A ^ `B); + + if (!failed) begin + $display("PASSED"); + end + end + +endmodule diff --git a/ivtest/ivltests/sv_module_port4.v b/ivtest/ivltests/sv_module_port4.v new file mode 100644 index 000000000..3459847e4 --- /dev/null +++ b/ivtest/ivltests/sv_module_port4.v @@ -0,0 +1,38 @@ +// Check that partial module ANSI port declarations are supported. Check that it +// is possible to redefine the port kind only. + + + +module test( + output [3:0] a, + var b, c, + wire d); + + bit failed = 1'b0; + +`define check(val, exp) do begin \ + if ((val) !== (exp)) begin \ + $display("FAILED(%0d): Expected `%d`, got `%d`.", `__LINE__, \ + (exp), (val)); \ + failed = 1'b1; \ + end \ + end while (0) + + initial begin + `check($bits(a), 4); + `check($bits(b), 1); + `check($bits(c), 1); + `check($bits(d), 1); + + // a and d are wires, b and c are variables + `check(a, 4'bzzzz); + `check(b, 1'bx); + `check(c, 1'bx); + `check(d, 1'bz); + + if (!failed) begin + $display("PASSED"); + end + end + +endmodule diff --git a/ivtest/regress-vvp.list b/ivtest/regress-vvp.list index 281262924..fdacfbc4d 100644 --- a/ivtest/regress-vvp.list +++ b/ivtest/regress-vvp.list @@ -62,6 +62,10 @@ sv_array_cassign6 vvp_tests/sv_array_cassign6.json sv_array_cassign7 vvp_tests/sv_array_cassign7.json sv_foreach9 vvp_tests/sv_foreach9.json sv_foreach10 vvp_tests/sv_foreach10.json +sv_module_port1 vvp_tests/sv_module_port1.json +sv_module_port2 vvp_tests/sv_module_port2.json +sv_module_port3 vvp_tests/sv_module_port3.json +sv_module_port4 vvp_tests/sv_module_port4.json sv_wildcard_import8 vvp_tests/sv_wildcard_import8.json sdf_header vvp_tests/sdf_header.json task_return1 vvp_tests/task_return1.json diff --git a/ivtest/vvp_tests/sv_module_port1.json b/ivtest/vvp_tests/sv_module_port1.json new file mode 100644 index 000000000..c4b6a2312 --- /dev/null +++ b/ivtest/vvp_tests/sv_module_port1.json @@ -0,0 +1,5 @@ +{ + "type" : "normal", + "source" : "sv_module_port1.v", + "iverilog-args" : [ "-g2005-sv" ] +} diff --git a/ivtest/vvp_tests/sv_module_port2.json b/ivtest/vvp_tests/sv_module_port2.json new file mode 100644 index 000000000..da7bef140 --- /dev/null +++ b/ivtest/vvp_tests/sv_module_port2.json @@ -0,0 +1,5 @@ +{ + "type" : "normal", + "source" : "sv_module_port2.v", + "iverilog-args" : [ "-g2005-sv" ] +} diff --git a/ivtest/vvp_tests/sv_module_port3.json b/ivtest/vvp_tests/sv_module_port3.json new file mode 100644 index 000000000..5fcf35c55 --- /dev/null +++ b/ivtest/vvp_tests/sv_module_port3.json @@ -0,0 +1,5 @@ +{ + "type" : "normal", + "source" : "sv_module_port3.v", + "iverilog-args" : [ "-g2005-sv" ] +} diff --git a/ivtest/vvp_tests/sv_module_port4.json b/ivtest/vvp_tests/sv_module_port4.json new file mode 100644 index 000000000..ab33a3785 --- /dev/null +++ b/ivtest/vvp_tests/sv_module_port4.json @@ -0,0 +1,5 @@ +{ + "type" : "normal", + "source" : "sv_module_port4.v", + "iverilog-args" : [ "-g2005-sv" ] +} diff --git a/parse.y b/parse.y index 0178a3131..b7f880b8c 100644 --- a/parse.y +++ b/parse.y @@ -352,6 +352,72 @@ 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, + data_type_t *data_type, + std::list *unpacked_dims, + PExpr *default_value, + std::list *attributes) +{ + perm_string name = lex_strings.make(id); + delete[] id; + + Module::port_t *port = pform_module_port_reference(loc, name); + + switch (port_type) { + case NetNet::PINOUT: + if (default_value) + yyerror(loc, "error: Default port value not allowed for inout ports."); + if (unpacked_dims) { + yyerror(loc, "sorry: Inout ports with unpacked dimensions are not supported."); + delete unpacked_dims; + unpacked_dims = nullptr; + } + break; + case NetNet::PINPUT: + if (default_value) { + pform_requires_sv(loc, "Input port default value"); + port->default_value = default_value; + } + break; + case NetNet::POUTPUT: + if (default_value) + pform_make_var_init(loc, name, default_value); + + // Output types without an implicit net type but with a data type + // are variables. Unlike the other port types, which are nets in + // that case. + if (net_type == NetNet::IMPLICIT) { + if (vector_type_t*dtype = dynamic_cast (data_type)) { + if (!dtype->implicit_flag) + net_type = NetNet::IMPLICIT_REG; + } else if (data_type) { + net_type = NetNet::IMPLICIT_REG; + } + } + break; + default: + break; + } + + pform_module_define_port(loc, name, port_type, net_type, data_type, + unpacked_dims, attributes); + + port_declaration_context.port_type = port_type; + port_declaration_context.port_net_type = net_type; + port_declaration_context.data_type = data_type; + + return port; +} + %} %union { @@ -646,6 +712,7 @@ static void current_function_set_statement(const YYLTYPE&loc, std::vector 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 @@ -1278,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); @@ -4407,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."); } @@ -4447,112 +4495,27 @@ 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 K_input net_type_or_var_opt data_type_or_implicit IDENTIFIER dimensions_opt initializer_opt - { if ($7) - pform_requires_sv(@7, "Input port default value"); - Module::port_t*ptmp; - perm_string name = lex_strings.make($5); - ptmp = pform_module_port_reference(@2, name); - ptmp->default_value = $7; - pform_module_define_port(@2, name, NetNet::PINPUT, $3, $4, $6, nullptr, - $1); - port_declaration_context.port_type = NetNet::PINPUT; - port_declaration_context.port_net_type = $3; - port_declaration_context.data_type = $4; - delete[]$5; - $$ = ptmp; + : attribute_list_opt port_direction net_type_or_var_opt data_type_or_implicit IDENTIFIER dimensions_opt initializer_opt + { $$ = module_declare_port(@5, $5, $2, $3, $4, $6, $7, $1); } - | attribute_list_opt - K_input K_wreal IDENTIFIER - { Module::port_t*ptmp; - perm_string name = lex_strings.make($4); - ptmp = pform_module_port_reference(@2, name); - real_type_t*real_type = new real_type_t(real_type_t::REAL); + | 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); FILE_NAME(real_type, @3); - pform_module_define_port(@2, name, NetNet::PINPUT, NetNet::WIRE, - real_type, nullptr, $1); - port_declaration_context.port_type = NetNet::PINPUT; - port_declaration_context.port_net_type = NetNet::WIRE; - port_declaration_context.data_type = real_type; - delete[]$4; - $$ = ptmp; - } - | attribute_list_opt K_inout net_type_opt data_type_or_implicit IDENTIFIER dimensions_opt - { Module::port_t*ptmp; - perm_string name = lex_strings.make($5); - ptmp = pform_module_port_reference(@2, name); - pform_module_define_port(@2, name, NetNet::PINOUT, $3, $4, nullptr, - $1); - port_declaration_context.port_type = NetNet::PINOUT; - port_declaration_context.port_net_type = $3; - port_declaration_context.data_type = $4; - delete[]$5; - if ($6) { - yyerror(@6, "sorry: Inout ports with unpacked dimensions not supported."); - delete $6; - } - $$ = ptmp; - } - | attribute_list_opt - K_inout K_wreal IDENTIFIER - { Module::port_t*ptmp; - perm_string name = lex_strings.make($4); - ptmp = pform_module_port_reference(@2, name); - real_type_t*real_type = new real_type_t(real_type_t::REAL); - FILE_NAME(real_type, @3); - pform_module_define_port(@2, name, NetNet::PINOUT, NetNet::WIRE, - real_type, nullptr, $1); - port_declaration_context.port_type = NetNet::PINOUT; - port_declaration_context.port_net_type = NetNet::WIRE; - port_declaration_context.data_type = real_type; - delete[]$4; - $$ = ptmp; - } - | attribute_list_opt K_output net_type_or_var_opt data_type_or_implicit IDENTIFIER dimensions_opt initializer_opt - { Module::port_t*ptmp; - perm_string name = lex_strings.make($5); - NetNet::Type use_type = $3; - if (use_type == NetNet::IMPLICIT) { - if (vector_type_t*dtype = dynamic_cast ($4)) { - if (dtype->implicit_flag) - use_type = NetNet::IMPLICIT; - else - use_type = NetNet::IMPLICIT_REG; - - // The SystemVerilog types that can show up as - // output ports are implicitly (on the inside) - // variables because "reg" is not valid syntax - // here. - } else if ($4) { - use_type = NetNet::IMPLICIT_REG; - } - } - ptmp = pform_module_port_reference(@2, name); - pform_module_define_port(@2, name, NetNet::POUTPUT, use_type, $4, $6, $1); - port_declaration_context.port_type = NetNet::POUTPUT; - port_declaration_context.port_net_type = use_type; - port_declaration_context.data_type = $4; - - if ($7) - pform_make_var_init(@5, name, $7); - delete[]$5; - $$ = ptmp; - } - | attribute_list_opt - K_output K_wreal IDENTIFIER - { Module::port_t*ptmp; - perm_string name = lex_strings.make($4); - ptmp = pform_module_port_reference(@2, name); - real_type_t*real_type = new real_type_t(real_type_t::REAL); - FILE_NAME(real_type, @3); - pform_module_define_port(@2, name, NetNet::POUTPUT, NetNet::WIRE, - real_type, nullptr, $1); - port_declaration_context.port_type = NetNet::POUTPUT; - port_declaration_context.port_net_type = NetNet::WIRE; - port_declaration_context.data_type = real_type; - delete[]$4; - $$ = ptmp; + $$ = module_declare_port(@4, $4, $2, NetNet::WIRE, + real_type, nullptr, nullptr, $1); } ; @@ -4651,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