From c631ff24831fbfe0a8c8c7c205cf6dec3a71a130 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 4 Jan 2022 16:16:08 +0100 Subject: [PATCH 1/4] Support SystemVerilog types for non-ansi task port declarations Tasks and functions support two types of port declarations. Either ANSI style, in parenthesis after the task name, or non-ANSI style, as declaration statements in the task body. In the current implementation SystemVerilog types are only accept for ANSI style port declarations, while non-ANSI style only accept Verilog types (reg, integer, time, real). Add support for SystemVerilog data types for non-ansi style ports. This also makes the parsing rules simpler since we can use `data_type` to match all data types and don't need a explicit rule for each supported data type. Signed-off-by: Lars-Peter Clausen --- parse.y | 55 +++---------------------------------------------------- pform.cc | 23 ++++++++++++++--------- pform.h | 13 ++----------- 3 files changed, 19 insertions(+), 72 deletions(-) diff --git a/parse.y b/parse.y index 4ddf96132..eef078c8f 100644 --- a/parse.y +++ b/parse.y @@ -590,7 +590,7 @@ static void current_function_set_statement(const YYLTYPE&loc, std::vector number pos_neg_number %type signing unsigned_signed_opt signed_unsigned_opt %type import_export -%type K_genvar_opt K_reg_opt K_static_opt K_virtual_opt +%type K_genvar_opt K_static_opt K_virtual_opt %type udp_reg_opt edge_operator %type drive_strength drive_strength_opt dr_strength0 dr_strength1 %type udp_input_sym udp_output_sym @@ -2104,13 +2104,6 @@ random_qualifier /* IEEE1800-2005 A.1.8 */ | K_randc { $$ = property_qualifier_t::make_randc(); } ; - /* real and realtime are exactly the same so save some code - * with a common matching rule. */ -real_or_realtime - : K_real - | K_realtime - ; - signing /* IEEE1800-2005: A.2.2.1 */ : K_signed { $$ = true; } | K_unsigned { $$ = false; } @@ -2353,50 +2346,9 @@ task_declaration /* IEEE1800-2005: A.2.7 */ tf_port_declaration /* IEEE1800-2005: A.2.7 */ - : port_direction K_reg_opt unsigned_signed_opt dimensions_opt list_of_identifiers ';' - { std::vector*tmp = pform_make_task_ports(@1, $1, - $2 ? IVL_VT_LOGIC : - IVL_VT_NO_TYPE, - $3, $4, $5); - $$ = tmp; + : port_direction data_type_or_implicit list_of_identifiers ';' + { $$ = pform_make_task_ports(@1, $1, $2, $3, true); } - - /* When the port is an integer, infer a signed vector of the integer - shape. Generate a range ([31:0]) to make it work. */ - - | port_direction K_integer list_of_identifiers ';' - { std::list*range_stub = make_range_from_width(integer_width); - vector*tmp = pform_make_task_ports(@1, $1, IVL_VT_LOGIC, true, - range_stub, $3, true); - $$ = tmp; - } - - /* Ports can be time with a width of [63:0] (unsigned). */ - - | port_direction K_time list_of_identifiers ';' - { std::list*range_stub = make_range_from_width(64); - vector*tmp = pform_make_task_ports(@1, $1, IVL_VT_LOGIC, false, - range_stub, $3); - $$ = tmp; - } - - /* Ports can be real or realtime. */ - - | port_direction real_or_realtime list_of_identifiers ';' - { std::vector*tmp = pform_make_task_ports(@1, $1, IVL_VT_REAL, true, - 0, $3); - $$ = tmp; - } - - - /* Ports can be string. */ - - | port_direction K_string list_of_identifiers ';' - { std::vector*tmp = pform_make_task_ports(@1, $1, IVL_VT_STRING, true, - 0, $3); - $$ = tmp; - } - ; @@ -7244,6 +7196,5 @@ unique_priority collect those rules here. */ K_genvar_opt : K_genvar { $$ = true; } | { $$ = false; } ; -K_reg_opt : K_reg { $$ = true; } | { $$ = false; } ; K_static_opt : K_static { $$ = true; } | { $$ = false; } ; K_virtual_opt : K_virtual { $$ = true; } | { $$ = false; } ; diff --git a/pform.cc b/pform.cc index f9f7a3237..8184c7802 100644 --- a/pform.cc +++ b/pform.cc @@ -2923,13 +2923,13 @@ void pform_makewire(const struct vlltype&li, * constraints as those of tasks, so this works fine. Functions have * no output or inout ports. */ -vector*pform_make_task_ports(const struct vlltype&loc, - NetNet::PortType pt, - ivl_variable_type_t vtype, - bool signed_flag, - list*range, - list*names, - bool isint) +static vector*pform_make_task_ports(const struct vlltype&loc, + NetNet::PortType pt, + ivl_variable_type_t vtype, + bool signed_flag, + list*range, + list*names, + bool isint = false) { assert(pt != NetNet::PIMPLICIT && pt != NetNet::NOT_A_PORT); assert(names); @@ -2999,7 +2999,8 @@ static vector*do_make_task_ports(const struct vlltype&loc, vector*pform_make_task_ports(const struct vlltype&loc, NetNet::PortType pt, data_type_t*vtype, - list*names) + list*names, + bool allow_implicit) { vector*ret = NULL; std::list*unpacked_dims = NULL; @@ -3017,7 +3018,11 @@ vector*pform_make_task_ports(const struct vlltype&loc, } if (vector_type_t*vec_type = dynamic_cast (vtype)) { - ret = pform_make_task_ports(loc, pt, vec_type->base_type, + ivl_variable_type_t base_type = vec_type->base_type; + if (allow_implicit && vec_type->implicit_flag) + base_type = IVL_VT_NO_TYPE; + + ret = pform_make_task_ports(loc, pt, base_type, vec_type->signed_flag, copy_range(vec_type->pdims.get()), names, vec_type->integer_flag); diff --git a/pform.h b/pform.h index 687dee9bd..02c22f32f 100644 --- a/pform.h +++ b/pform.h @@ -492,20 +492,11 @@ extern void pform_make_pgassign_list(std::list*alist, struct str_pair_t str, const char* fn, unsigned lineno); -/* Given a port type and a list of names, make a list of wires that - can be used as task port information. */ -extern std::vector*pform_make_task_ports(const struct vlltype&loc, - NetNet::PortType pt, - ivl_variable_type_t vtype, - bool signed_flag, - std::list*range, - std::list*names, - bool isint = false); - extern std::vector*pform_make_task_ports(const struct vlltype&loc, NetNet::PortType pt, data_type_t*vtype, - std::list*names); + std::list*names, + bool allow_implicit = false); /* * The parser uses this function to convert a unary From b0c386182aa6e72c1f4301df7d5094779cc3c86d Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 19 Feb 2022 22:44:02 +0100 Subject: [PATCH 2/4] Support unpacked array dimensions on non-ansi style task ports SystemVerilog allows unpacked array dimensions on non-ANSI style task and function ports. To support this refactor pform_make_task_ports() to accept a of pform_port_t, which in addition to the identifier name also allows to specify per port unpacked dimensions. Signed-off-by: Lars-Peter Clausen --- parse.y | 14 ++++--------- pform.cc | 54 +++++++++++++++++++++++++++++-------------------- pform.h | 2 +- pform_pclass.cc | 4 ++-- 4 files changed, 39 insertions(+), 35 deletions(-) diff --git a/parse.y b/parse.y index eef078c8f..dad645bef 100644 --- a/parse.y +++ b/parse.y @@ -2346,7 +2346,7 @@ task_declaration /* IEEE1800-2005: A.2.7 */ tf_port_declaration /* IEEE1800-2005: A.2.7 */ - : port_direction data_type_or_implicit list_of_identifiers ';' + : port_direction data_type_or_implicit list_of_port_identifiers ';' { $$ = pform_make_task_ports(@1, $1, $2, $3, true); } ; @@ -2367,8 +2367,7 @@ tf_port_item /* IEEE1800-2005: A.2.7 */ NetNet::PortType use_port_type = $1; if ((use_port_type == NetNet::PIMPLICIT) && (gn_system_verilog() || ($2 == 0))) use_port_type = port_declaration_context.port_type; - perm_string name = lex_strings.make($3); - list* ilist = list_from_identifier($3); + list* port_list = make_port_list($3, $4, 0); if (use_port_type == NetNet::PIMPLICIT) { yyerror(@1, "error: missing task/function port direction."); @@ -2383,7 +2382,7 @@ tf_port_item /* IEEE1800-2005: A.2.7 */ } tmp = pform_make_task_ports(@3, use_port_type, port_declaration_context.data_type, - ilist); + port_list); } else { // Otherwise, the decorations for this identifier @@ -2395,12 +2394,7 @@ tf_port_item /* IEEE1800-2005: A.2.7 */ FILE_NAME($2, @3); } port_declaration_context.data_type = $2; - tmp = pform_make_task_ports(@3, use_port_type, $2, ilist); - } - if ($4 != 0) { - if (pform_requires_sv(@4, "Task/function port with unpacked dimensions")) { - pform_set_reg_idx(name, $4); - } + tmp = pform_make_task_ports(@3, use_port_type, $2, port_list); } $$ = tmp; diff --git a/pform.cc b/pform.cc index 8184c7802..e81efd3ca 100644 --- a/pform.cc +++ b/pform.cc @@ -2928,16 +2928,15 @@ static vector*pform_make_task_ports(const struct vlltype&loc, ivl_variable_type_t vtype, bool signed_flag, list*range, - list*names, + list*ports, bool isint = false) { assert(pt != NetNet::PIMPLICIT && pt != NetNet::NOT_A_PORT); - assert(names); + assert(ports); vector*res = new vector(0); - for (list::iterator cur = names->begin() - ; cur != names->end() ; ++ cur ) { - - perm_string name = *cur; + for (list::iterator cur = ports->begin() + ; cur != ports->end() ; ++ cur ) { + perm_string &name = cur->name; /* Look for a preexisting wire. If it exists, set the port direction. If not, create it. */ @@ -2961,6 +2960,11 @@ static vector*pform_make_task_ports(const struct vlltype&loc, curw->set_range(*range, SR_PORT); } + if (cur->udims) { + if (pform_requires_sv(loc, "Task/function port with unpacked dimensions")) + curw->set_unpacked_idx(*cur->udims); + } + res->push_back(pform_tf_port_t(curw)); } @@ -2972,15 +2976,16 @@ static vector*do_make_task_ports(const struct vlltype&loc, NetNet::PortType pt, ivl_variable_type_t var_type, data_type_t*data_type, - list*names) + list*ports) { assert(pt != NetNet::PIMPLICIT && pt != NetNet::NOT_A_PORT); - assert(names); + assert(ports); vector*res = new vector(0); - for (list::iterator cur = names->begin() - ; cur != names->end() ; ++cur) { - perm_string name = *cur; + for (list::iterator cur = ports->begin() + ; cur != ports->end() ; ++cur) { + perm_string &name = cur->name; + PWire*curw = pform_get_wire_in_scope(name); if (curw) { curw->set_port_type(pt); @@ -2991,6 +2996,11 @@ static vector*do_make_task_ports(const struct vlltype&loc, pform_put_wire_in_scope(name, curw); } + if (cur->udims) { + if (pform_requires_sv(loc, "Task/function port with unpacked dimensions")) + curw->set_unpacked_idx(*cur->udims); + } + res->push_back(pform_tf_port_t(curw)); } return res; @@ -2999,7 +3009,7 @@ static vector*do_make_task_ports(const struct vlltype&loc, vector*pform_make_task_ports(const struct vlltype&loc, NetNet::PortType pt, data_type_t*vtype, - list*names, + list*ports, bool allow_implicit) { vector*ret = NULL; @@ -3014,7 +3024,7 @@ vector*pform_make_task_ports(const struct vlltype&loc, list*range_tmp = make_range_from_width(atype->type_code); ret = pform_make_task_ports(loc, pt, IVL_VT_BOOL, atype->signed_flag, - range_tmp, names); + range_tmp, ports); } if (vector_type_t*vec_type = dynamic_cast (vtype)) { @@ -3025,36 +3035,36 @@ vector*pform_make_task_ports(const struct vlltype&loc, ret = pform_make_task_ports(loc, pt, base_type, vec_type->signed_flag, copy_range(vec_type->pdims.get()), - names, vec_type->integer_flag); + ports, vec_type->integer_flag); } if (/*real_type_t*real_type = */ dynamic_cast (vtype)) { ret = pform_make_task_ports(loc, pt, IVL_VT_REAL, - true, 0, names); + true, 0, ports); } if (dynamic_cast (vtype)) { ret = pform_make_task_ports(loc, pt, IVL_VT_STRING, - false, 0, names); + false, 0, ports); } if (class_type_t*class_type = dynamic_cast (vtype)) { - ret = do_make_task_ports(loc, pt, IVL_VT_CLASS, class_type, names); + ret = do_make_task_ports(loc, pt, IVL_VT_CLASS, class_type, ports); } if (! ret) { - ret = do_make_task_ports(loc, pt, IVL_VT_NO_TYPE, vtype, names); + ret = do_make_task_ports(loc, pt, IVL_VT_NO_TYPE, vtype, ports); } if (unpacked_dims) { - for (list::iterator cur = names->begin() - ; cur != names->end() ; ++ cur ) { - PWire*wire = pform_get_wire_in_scope(*cur); + for (list::iterator cur = ports->begin() + ; cur != ports->end() ; ++ cur ) { + PWire*wire = pform_get_wire_in_scope(cur->name); wire->set_unpacked_idx(*unpacked_dims); } } - delete names; + delete ports; return ret; } diff --git a/pform.h b/pform.h index 02c22f32f..b942c7e26 100644 --- a/pform.h +++ b/pform.h @@ -495,7 +495,7 @@ extern void pform_make_pgassign_list(std::list*alist, extern std::vector*pform_make_task_ports(const struct vlltype&loc, NetNet::PortType pt, data_type_t*vtype, - std::list*names, + std::list*ports, bool allow_implicit = false); /* diff --git a/pform_pclass.cc b/pform_pclass.cc index e9facc013..c077521a8 100644 --- a/pform_pclass.cc +++ b/pform_pclass.cc @@ -106,8 +106,8 @@ void pform_set_this_class(const struct vlltype&loc, PTaskFunc*net) if (pform_cur_class == 0) return; - list*this_name = new list; - this_name->push_back(perm_string::literal(THIS_TOKEN)); + list*this_name = new list; + this_name->push_back(pform_port_t(perm_string::literal(THIS_TOKEN), 0, 0)); vector*this_port = pform_make_task_ports(loc, NetNet::PINPUT, pform_cur_class->type, From 372e3eae25d4b9cc527d6c5ec88771f22e7e5bec Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 23 Feb 2022 11:37:15 +0100 Subject: [PATCH 3/4] Add regression test for SystemVerilog task port types Check that it is possible to declare task ports with SystemVerilog types. Both ANSI style and one for non-ANSI style. Signed-off-by: Lars-Peter Clausen --- ivtest/ivltests/task_port_types1.v | 35 ++++++++++++++++++++++++++++++ ivtest/ivltests/task_port_types2.v | 35 ++++++++++++++++++++++++++++++ ivtest/regress-sv.list | 2 ++ ivtest/regress-vlog95.list | 2 ++ 4 files changed, 74 insertions(+) create mode 100644 ivtest/ivltests/task_port_types1.v create mode 100644 ivtest/ivltests/task_port_types2.v diff --git a/ivtest/ivltests/task_port_types1.v b/ivtest/ivltests/task_port_types1.v new file mode 100644 index 000000000..4bab99a9f --- /dev/null +++ b/ivtest/ivltests/task_port_types1.v @@ -0,0 +1,35 @@ +// Check that it is possible to use SV data types for ANSI style task ports + +module test; + + typedef logic [7:0] T1; + typedef struct packed { int i; } T2; + typedef enum { A } T3; + + task t(input reg a, + input logic b, + input bit c, + input logic [3:0] d, + input bit [3:0][3:0] e, + input byte f, + input int g, + input T1 h, + input T2 i, + input T3 j, + input real k, + input shortreal l, + input string m, + input int n[], + input int o[$], + input x, + input [3:0] y, + input signed z + ); + $display("PASSED"); + endtask + + initial begin + t('0, '0, '0, '0, '0, '0, '0, '0, '0, A, 0.0, 0.0, "", '{0}, '{0}, '0, '0, '0); + end + +endmodule diff --git a/ivtest/ivltests/task_port_types2.v b/ivtest/ivltests/task_port_types2.v new file mode 100644 index 000000000..1a14f3cb0 --- /dev/null +++ b/ivtest/ivltests/task_port_types2.v @@ -0,0 +1,35 @@ +// Check that it is possible to use SV data types for non-ANSI style task ports + +module test; + + typedef logic [7:0] T1; + typedef struct packed { int i; } T2; + typedef enum { A } T3; + + task t; + input reg a; + input logic b; + input bit c; + input logic [3:0] d; + input bit [3:0][3:0] e; + input byte f; + input int g; + input T1 h; + input T2 i; + input T3 j; + input real k; + input shortreal l; + input string m; + input int n[]; + input int o[$]; + input x; + input [3:0] y; + input signed z; + $display("PASSED"); + endtask + + initial begin + t('0, '0, '0, '0, '0, '0, '0, '0, '0, A, 0.0, 0.0, "", '{0}, '{0}, '0, '0, '0); + end + +endmodule diff --git a/ivtest/regress-sv.list b/ivtest/regress-sv.list index c4b8b163c..2cac285ad 100644 --- a/ivtest/regress-sv.list +++ b/ivtest/regress-sv.list @@ -580,6 +580,8 @@ task_init_assign normal,-g2009 ivltests task_init_var1 normal,-g2009 ivltests task_init_var2 normal,-g2009 ivltests task_init_var3 normal,-g2009 ivltests +task_port_types1 normal,-g2009 ivltests +task_port_types2 normal,-g2009 ivltests task_scope2 normal,-g2009 ivltests test_inc_dec normal,-g2009 ivltests test_tliteral normal,-g2009 ivltests diff --git a/ivtest/regress-vlog95.list b/ivtest/regress-vlog95.list index 1c66d40f6..30baccb0a 100644 --- a/ivtest/regress-vlog95.list +++ b/ivtest/regress-vlog95.list @@ -95,6 +95,8 @@ recursive_task CE ivltests task_init_var1 CE,-pallowsigned=1 ivltests task_init_var2 CE,-pallowsigned=1 ivltests task_init_var3 CE,-pallowsigned=1 ivltests +task_port_types1 CE,-pallowsigned=1 ivltests +task_port_types2 CE,-pallowsigned=1 ivltests test_work14 CE ivltests vhdl_elab_range CE ivltests vhdl_notfunc_stdlogic CE ivltests From 5f71307ab48cecc795abeea6f86a80363c09f712 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 9 Jan 2022 21:04:06 +0100 Subject: [PATCH 4/4] Use data_type_t to pass `string` type information to signal elaboration For signals that are declared in a block string_type_t is already used to pass the type information to the signal elaboration. But for task ports it is passed as IVL_VT_STRING. Switch this over to also passing the type information as a data_type_t. This allows to remove the special handling for IVL_VT_STRING in the signal elaboration. Signed-off-by: Lars-Peter Clausen --- elab_sig.cc | 19 ++++--------------- pform.cc | 3 +-- 2 files changed, 5 insertions(+), 17 deletions(-) diff --git a/elab_sig.cc b/elab_sig.cc index d0cb720b4..ceb4256c4 100644 --- a/elab_sig.cc +++ b/elab_sig.cc @@ -1254,21 +1254,6 @@ NetNet* PWire::elaborate_sig(Design*des, NetScope*scope) const sig = new NetNet(scope, name_, wtype, unpacked_dimensions, &netstring_t::type_string); - } else if (set_data_type_==0 && data_type_==IVL_VT_STRING) { - - // Signal declared as: string foo; - if (debug_elaborate) { - cerr << get_fileline() << ": PWire::elaborate_sig: " - << "Create signal " << wtype - << " string " - << name_ << " in scope " << scope_path(scope) - << " without set_data_type_" - << endl; - } - - sig = new NetNet(scope, name_, wtype, unpacked_dimensions, - &netstring_t::type_string); - } else if (parray_type_t*parray_type = dynamic_cast(set_data_type_)) { // The pform gives us a parray_type_t for packed arrays // that show up in type definitions. This can be handled @@ -1315,6 +1300,10 @@ NetNet* PWire::elaborate_sig(Design*des, NetScope*scope) const } } + ivl_assert(*this, use_data_type == IVL_VT_LOGIC || + use_data_type == IVL_VT_BOOL || + use_data_type == IVL_VT_REAL); + netvector_t*vec = new netvector_t(packed_dimensions, use_data_type); vec->set_signed(get_signed()); vec->set_isint(get_isint()); diff --git a/pform.cc b/pform.cc index e81efd3ca..cac63013f 100644 --- a/pform.cc +++ b/pform.cc @@ -3044,8 +3044,7 @@ vector*pform_make_task_ports(const struct vlltype&loc, } if (dynamic_cast (vtype)) { - ret = pform_make_task_ports(loc, pt, IVL_VT_STRING, - false, 0, ports); + ret = do_make_task_ports(loc, pt, IVL_VT_STRING, vtype, ports); } if (class_type_t*class_type = dynamic_cast (vtype)) {