From 5ef847ea87ea9222c5644805f261163086178643 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 20 Dec 2021 17:00:24 +0100 Subject: [PATCH] Support type parameters SystemVerilog supports type parameters. These are similar to value parameters, but they allow to pass a type to a module or similar when instantiating it. E.g. ``` module A #(parameter type T = int); endmodule module B; A #(.T(real)) i_a; endmodule ``` Add support for handling type parameters. For the vlog95 and vhdl backends type parameters, similar to typedefs, get replaced with their actual value. For modules with non-local type parameters for each module instance a unique module or architecture is generated with the actual type. Querying type parameters through VPI is not yet supported. Signed-off-by: Lars-Peter Clausen --- PScope.h | 2 ++ elab_scope.cc | 4 +--- elab_type.cc | 13 ++++++++++++ ivl.def | 1 + ivl_target.h | 4 ++++ net_design.cc | 48 ++++++++++++++++++++++++++++++++++----------- net_scope.cc | 32 +++++++++++++++++++----------- netlist.h | 23 +++++++++++++++------- parse.y | 22 +++++++++++++++++++-- pform.cc | 21 ++++++++++++++++++-- pform.h | 2 +- pform_types.h | 7 +++++++ t-dll-api.cc | 6 ++++++ t-dll.cc | 28 +++++++++++++++----------- t-dll.h | 1 + tgt-vhdl/scope.cc | 8 +++++++- tgt-vhdl/state.cc | 8 ++++++++ tgt-vlog95/scope.c | 7 +++++++ tgt-vvp/vvp_scope.c | 7 +++++++ 19 files changed, 195 insertions(+), 49 deletions(-) diff --git a/PScope.h b/PScope.h index d76b1c819..faee69fac 100644 --- a/PScope.h +++ b/PScope.h @@ -113,6 +113,8 @@ class LexicalScope { bool local_flag; // Whether the parameter can be overridden bool overridable; + // Whether the parameter is a type parameter + bool type_flag = false; SymbolType symbol_type() const; }; diff --git a/elab_scope.cc b/elab_scope.cc index 716c3bed4..4f545cf73 100644 --- a/elab_scope.cc +++ b/elab_scope.cc @@ -128,9 +128,7 @@ static void collect_parm_item(Design*des, NetScope*scope, perm_string name, // not yet known, don't try to guess here, put the type guess off. Also // don't try to elaborate it here, because there may be references to // other parameters still being located during scope elaboration. - scope->set_parameter(name, is_annotatable, cur.expr, cur.data_type, - cur.local_flag, cur.overridable, range_list, cur); - + scope->set_parameter(name, is_annotatable, cur, range_list); } static void collect_scope_parameters(Design*des, NetScope*scope, diff --git a/elab_type.cc b/elab_type.cc index 695ed5cf6..45407f00e 100644 --- a/elab_type.cc +++ b/elab_type.cc @@ -476,3 +476,16 @@ ivl_type_t typedef_t::elaborate_type(Design *des, NetScope *scope) return elab_type; } + +ivl_type_t type_parameter_t::elaborate_type_raw(Design *des, NetScope*scope) const +{ + ivl_type_t type; + + scope->get_parameter(des, name, type); + + // Recover + if (!type) + return netvector_t::integer_type(); + + return type; +} diff --git a/ivl.def b/ivl.def index 81d9ae853..a226a22a0 100644 --- a/ivl.def +++ b/ivl.def @@ -172,6 +172,7 @@ ivl_nexus_ptr_switch ivl_parameter_basename ivl_parameter_expr ivl_parameter_file +ivl_parameter_is_type ivl_parameter_lineno ivl_parameter_local ivl_parameter_lsb diff --git a/ivl_target.h b/ivl_target.h index fdbb9a8f6..30405c9f1 100644 --- a/ivl_target.h +++ b/ivl_target.h @@ -1695,6 +1695,9 @@ extern ivl_signal_t ivl_nexus_ptr_sig(ivl_nexus_ptr_t net); * Return whether parameter was local (localparam, implicit genvar etc) * or not. * + * ivl_parameter_is_type + * Return whether the parameter is a type parameter or not. + * * ivl_parameter_file * ivl_parameter_lineno * Returns the file and line where this parameter is defined @@ -1707,6 +1710,7 @@ extern int ivl_parameter_lsb(ivl_parameter_t net); extern unsigned ivl_parameter_width(ivl_parameter_t net); extern int ivl_parameter_signed(ivl_parameter_t net); extern int ivl_parameter_local(ivl_parameter_t net); +extern int ivl_parameter_is_type(ivl_parameter_t net); extern const char* ivl_parameter_file(ivl_parameter_t net); extern unsigned ivl_parameter_lineno(ivl_parameter_t net); diff --git a/net_design.cc b/net_design.cc index a546f7110..b9990365c 100644 --- a/net_design.cc +++ b/net_design.cc @@ -433,7 +433,7 @@ void NetScope::run_defparams(Design*des) continue; } - targ_scope->replace_parameter(des, perm_name, val, this); + targ_scope->replace_parameter(des, perm_name, val, this, true); } // If some of the defparams didn't find a scope in the name, @@ -468,7 +468,7 @@ void NetScope::run_defparams_later(Design*des) continue; } - targ_scope->replace_parameter(des, name, val, this); + targ_scope->replace_parameter(des, name, val, this, true); // We'll need to re-evaluate parameters in this scope target_scopes.insert(targ_scope); @@ -814,20 +814,31 @@ void NetScope::evaluate_parameter_string_(Design*des, param_ref_t cur) } } -void NetScope::evaluate_parameter_(Design*des, param_ref_t cur) +void NetScope::evaluate_type_parameter_(Design *des, param_ref_t cur) { - ivl_type_t param_type = cur->second.ivl_type; + const PETypename *type_expr = dynamic_cast(cur->second.val_expr); + if (!type_expr) { + cerr << this->get_fileline() << ": error: " + << "Type parameter `" << cur->first << "` value `" + << *cur->second.val_expr << "` is not a type." + << endl; + des->errors++; - // If the parameter type is present, then elaborate it now. Elaborate - // the type in the current scope, and not the scope of the expression. - if (cur->second.val_type) { - param_type = cur->second.val_type->elaborate_type(des, this); - cur->second.ivl_type = param_type; - cur->second.val_type = 0; + // Recover + cur->second.ivl_type = netvector_t::integer_type(); + return; } + data_type_t *type = type_expr->get_type(); + NetScope *type_scope = cur->second.val_scope; + cur->second.ivl_type = type->elaborate_type(des, type_scope); +} + +void NetScope::evaluate_parameter_(Design*des, param_ref_t cur) +{ + // If the parameter has already been evaluated, quietly return. - if (cur->second.val != 0) + if (cur->second.val || cur->second.ivl_type) return; if (cur->second.val_expr == 0) { @@ -840,6 +851,21 @@ void NetScope::evaluate_parameter_(Design*des, param_ref_t cur) return; } + if (cur->second.type_flag) { + evaluate_type_parameter_(des, cur); + return; + } + + ivl_type_t param_type = cur->second.ivl_type; + + // If the parameter type is present, then elaborate it now. Elaborate + // the type in the current scope, and not the scope of the expression. + if (cur->second.val_type) { + param_type = cur->second.val_type->elaborate_type(des, this); + cur->second.ivl_type = param_type; + cur->second.val_type = 0; + } + // Guess the varaiable type of the parameter. If the parameter has no // given type, then guess the type from the expression and use that to // evaluate (this is currently handled in evaluate_parameter_logic_()). diff --git a/net_scope.cc b/net_scope.cc index 77a91bc23..7e9be8035 100644 --- a/net_scope.cc +++ b/net_scope.cc @@ -269,23 +269,22 @@ NetScope*NetScope::find_typedef_scope(const Design*des, const typedef_t*type) * member. */ void NetScope::set_parameter(perm_string key, bool is_annotatable, - PExpr*val, data_type_t*val_type, - bool local_flag, bool overridable, - NetScope::range_t*range_list, - const LineInfo&file_line) + const LexicalScope::param_expr_t ¶m, + NetScope::range_t *range_list) { param_expr_t&ref = parameters[key]; ref.is_annotatable = is_annotatable; - ref.val_expr = val; - ref.val_type = val_type; + ref.val_expr = param.expr; + ref.val_type = param.data_type; ref.val_scope = this; - ref.local_flag = local_flag; - ref.overridable = overridable; - ivl_assert(file_line, ref.range == 0); + ref.local_flag = param.local_flag; + ref.overridable = param.overridable; + ref.type_flag = param.type_flag; + ivl_assert(param, !ref.range); ref.range = range_list; ref.val = 0; ref.ivl_type = 0; - ref.set_line(file_line); + ref.set_line(param); } /* @@ -348,7 +347,8 @@ bool NetScope::auto_name(const char*prefix, char pad, const char* suffix) * Return false if the parameter does not already exist. * A parameter is not automatically created. */ -void NetScope::replace_parameter(Design *des, perm_string key, PExpr*val, NetScope*scope) +void NetScope::replace_parameter(Design *des, perm_string key, PExpr*val, + NetScope*scope, bool defparam) { if (parameters.find(key) == parameters.end()) { cerr << val->get_fileline() << ": error: parameter `" @@ -376,6 +376,16 @@ void NetScope::replace_parameter(Design *des, perm_string key, PExpr*val, NetSco return; } + if (ref.type_flag && defparam) { + cerr << val->get_fileline() << ": error: " + << "Cannot override type parameter `" << key << "` in `" + << scope_path(this) << "`. It is not allowed to override type" + << " parameters using a defparam statement." + << endl; + des->errors++; + return; + } + ref.val_expr = val; ref.val_scope = scope; } diff --git a/netlist.h b/netlist.h index 7ad1454f4..78cb7a8ef 100644 --- a/netlist.h +++ b/netlist.h @@ -44,6 +44,7 @@ # include "HName.h" # include "LineInfo.h" # include "Attrib.h" +# include "PScope.h" # include "PUdp.h" #ifdef HAVE_IOSFWD @@ -952,10 +953,8 @@ class NetScope : public Definitions, public Attrib { struct range_t; void set_parameter(perm_string name, bool is_annotatable, - PExpr*val, data_type_t*data_type, - bool local_flag, bool overridable, - NetScope::range_t*range_list, - const LineInfo&file_line); + const LexicalScope::param_expr_t ¶m, + NetScope::range_t *range_list); void set_parameter(perm_string name, NetExpr*val, const LineInfo&file_line); @@ -968,7 +967,8 @@ class NetScope : public Definitions, public Attrib { expression with a new expression, without affecting the range or signed_flag. Return false if the name does not exist. */ - void replace_parameter(Design *des, perm_string name, PExpr*val, NetScope*scope); + void replace_parameter(Design *des, perm_string name, PExpr*val, + NetScope*scope, bool defparam = false); /* This is used to ensure the value of a parameter cannot be changed at run-time. This is required if a specparam is used @@ -1213,11 +1213,19 @@ class NetScope : public Definitions, public Attrib { // Is this a localparam? bool local_flag; // Can it be overriden? - bool overridable; + bool overridable = false; + // Is it a type parameter + bool type_flag = false; // range constraints struct range_t*range; - // Expression value and type (elaborated versoins of val_expr/val_type) + + // Expression value. Elaborated version of val_expr. + // For type parameters this will always be 0. NetExpr*val; + + // For non-type parameter this contains the elaborate type of the + // parameter itself. For type parameters this contains the + // elaborated assigned type value. ivl_type_t ivl_type; }; std::mapparameters; @@ -1240,6 +1248,7 @@ class NetScope : public Definitions, public Attrib { std::map loop_index_tmp; private: + void evaluate_type_parameter_(Design*des, param_ref_t cur); void evaluate_parameter_logic_(Design*des, param_ref_t cur); void evaluate_parameter_real_(Design*des, param_ref_t cur); void evaluate_parameter_string_(Design*des, param_ref_t cur); diff --git a/parse.y b/parse.y index a4d427704..2d57c8387 100644 --- a/parse.y +++ b/parse.y @@ -41,6 +41,7 @@ extern void lex_end_table(); static data_type_t* param_data_type = 0; static bool param_is_local = false; +static bool param_is_type = false; static std::list* specparam_active_range = 0; /* Port declaration lists use this structure for context. */ @@ -4730,6 +4731,10 @@ module_parameter_port_list_opt ')' ; +type_param + : K_type { param_is_type = true; } + ; + module_parameter : parameter param_type parameter_assign | localparam param_type parameter_assign @@ -4742,20 +4747,28 @@ module_parameter_port_list | data_type_opt { param_data_type = $1; param_is_local = false; + param_is_type = false; } parameter_assign { pform_requires_sv(@3, "Omitting initial `parameter` in parameter port " "list"); } + | type_param + { + param_is_local = false; + } + parameter_assign | module_parameter_port_list ',' module_parameter | module_parameter_port_list ',' data_type_opt { if ($3) { pform_requires_sv(@3, "Omitting `parameter`/`localparam` before " "data type in parameter port list"); param_data_type = $3; + param_is_type = false; } } parameter_assign + | module_parameter_port_list ',' type_param parameter_assign ; module_item @@ -5375,7 +5388,12 @@ net_type_or_var_opt This is used by parameter_assign, which is found to the right of the param_type in various rules. */ -param_type : data_type_or_implicit { param_data_type = $1; } +param_type + : data_type_or_implicit + { param_is_type = false; + param_data_type = $1; + } + | type_param parameter : K_parameter { param_is_local = false; }; localparam : K_localparam { param_is_local = true; }; @@ -5401,7 +5419,7 @@ parameter_assign_list parameter_assign : IDENTIFIER initializer_opt parameter_value_ranges_opt { pform_set_parameter(@1, lex_strings.make($1), param_is_local, - param_data_type, $2, $3); + param_is_type, param_data_type, $2, $3); delete[]$1; } ; diff --git a/pform.cc b/pform.cc index 7405a91c9..f25cd3c95 100644 --- a/pform.cc +++ b/pform.cc @@ -2961,8 +2961,21 @@ LexicalScope::range_t* pform_parameter_value_range(bool exclude_flag, return tmp; } +static void pform_set_type_parameter(const struct vlltype&loc, perm_string name, + LexicalScope::range_t*value_range) +{ + pform_requires_sv(loc, "Type parameter"); + + if (value_range) + VLerror(loc, "error: type parameter must not have value range."); + + type_parameter_t *type = new type_parameter_t(name); + pform_set_typedef(loc, name, type, 0); +} + void pform_set_parameter(const struct vlltype&loc, - perm_string name, bool is_local, data_type_t*data_type, PExpr*expr, + perm_string name, bool is_local, bool is_type, + data_type_t*data_type, PExpr*expr, LexicalScope::range_t*value_range) { LexicalScope*scope = lexical_scope; @@ -3008,13 +3021,17 @@ void pform_set_parameter(const struct vlltype&loc, Module::param_expr_t*parm = new Module::param_expr_t(); FILE_NAME(parm, loc); - add_local_symbol(scope, name, parm); + if (is_type) + pform_set_type_parameter(loc, name, value_range); + else + add_local_symbol(scope, name, parm); parm->expr = expr; parm->data_type = data_type; parm->range = value_range; parm->local_flag = is_local; parm->overridable = overridable; + parm->type_flag = is_type; scope->parameters[name] = parm; diff --git a/pform.h b/pform.h index ed7577616..d2f44c293 100644 --- a/pform.h +++ b/pform.h @@ -390,7 +390,7 @@ extern LexicalScope::range_t* pform_parameter_value_range(bool exclude_flag, extern void pform_set_parameter(const struct vlltype&loc, perm_string name, - bool is_local, + bool is_local, bool is_type, data_type_t*data_type, PExpr*expr, LexicalScope::range_t*value_range); extern void pform_set_specparam(const struct vlltype&loc, diff --git a/pform_types.h b/pform_types.h index 1029e7225..3a44d696f 100644 --- a/pform_types.h +++ b/pform_types.h @@ -212,6 +212,13 @@ private: typedef_t *type; }; +struct type_parameter_t : data_type_t { + explicit type_parameter_t(perm_string n) : name(n) { } + ivl_type_t elaborate_type_raw(Design *des, NetScope *scope) const; + + perm_string name; +}; + struct void_type_t : public data_type_t { virtual void pform_dump(std::ostream&out, unsigned indent) const; }; diff --git a/t-dll-api.cc b/t-dll-api.cc index 2b074722e..aefc0c5e5 100644 --- a/t-dll-api.cc +++ b/t-dll-api.cc @@ -1877,6 +1877,12 @@ extern "C" int ivl_parameter_local(ivl_parameter_t net) return net->local; } +extern "C" int ivl_parameter_is_type(ivl_parameter_t net) +{ + assert(net); + return net->is_type; +} + extern "C" int ivl_parameter_signed(ivl_parameter_t net) { assert(net); diff --git a/t-dll.cc b/t-dll.cc index b141e6262..df483d68e 100644 --- a/t-dll.cc +++ b/t-dll.cc @@ -509,10 +509,7 @@ void dll_target::make_scope_parameters(ivl_scope_t scop, const NetScope*net) cur_par->basename = cur_pit->first; cur_par->local = cur_pit->second.local_flag || !cur_pit->second.overridable; - calculate_param_range(cur_pit->second, - cur_pit->second.ivl_type, - cur_par->msb, cur_par->lsb, - cur_pit->second.val->expr_width()); + cur_par->is_type = cur_pit->second.type_flag; if (cur_pit->second.ivl_type == 0) { cerr << "?:?: internal error: " @@ -525,14 +522,23 @@ void dll_target::make_scope_parameters(ivl_scope_t scop, const NetScope*net) cur_par->scope = scop; FILE_NAME(cur_par, &(cur_pit->second)); - NetExpr*etmp = cur_pit->second.val; - if (etmp == 0) { - cerr << "?:?: internal error: What is the parameter " - << "expression for " << cur_pit->first - << " in " << net->fullname() << "?" << endl; + // Type parameters don't have a range or expression + if (!cur_pit->second.type_flag) { + calculate_param_range(cur_pit->second, + cur_pit->second.ivl_type, + cur_par->msb, cur_par->lsb, + cur_pit->second.val->expr_width()); + + NetExpr*etmp = cur_pit->second.val; + if (etmp == 0) { + cerr << "?:?: internal error: What is the parameter " + << "expression for " << cur_pit->first + << " in " << net->fullname() << "?" << endl; + } + assert(etmp); + make_scope_param_expr(cur_par, etmp); } - assert(etmp); - make_scope_param_expr(cur_par, etmp); + idx += 1; } } diff --git a/t-dll.h b/t-dll.h index 3cdaba095..c492d36e6 100644 --- a/t-dll.h +++ b/t-dll.h @@ -638,6 +638,7 @@ struct ivl_parameter_s { long lsb; bool signed_flag; bool local; + bool is_type; perm_string file; unsigned lineno; }; diff --git a/tgt-vhdl/scope.cc b/tgt-vhdl/scope.cc index 07917d175..c14606ad3 100644 --- a/tgt-vhdl/scope.cc +++ b/tgt-vhdl/scope.cc @@ -940,9 +940,15 @@ static void create_skeleton_entity_for(ivl_scope_t scope, int depth) unsigned nparams = ivl_scope_params(scope); for (unsigned i = 0; i < nparams; i++) { ivl_parameter_t param = ivl_scope_param(scope, i); - ss << "\n " << ivl_parameter_basename(param) << " = "; + + // Type parameter usages get replaced with their actual type + if (ivl_parameter_is_type(param)) + continue; ivl_expr_t value = ivl_parameter_expr(param); + + ss << "\n " << ivl_parameter_basename(param) << " = "; + switch (ivl_expr_type(value)) { case IVL_EX_STRING: ss << "\"" << ivl_expr_string(value) << "\""; diff --git a/tgt-vhdl/state.cc b/tgt-vhdl/state.cc index a6a23c12c..7dd6af40b 100644 --- a/tgt-vhdl/state.cc +++ b/tgt-vhdl/state.cc @@ -264,6 +264,14 @@ static bool same_scope_type_name(ivl_scope_t a, ivl_scope_t b) ivl_parameter_basename(param_b)) != 0) return false; + if (ivl_parameter_local(param_a) && ivl_parameter_local(param_b)) + continue; + + // If this is a type parameter consider the scopes not equal since we do + // not have support for comparing the actual types yet. + if (ivl_parameter_is_type(param_a) || ivl_parameter_is_type(param_b)) + return false; + ivl_expr_t value_a = ivl_parameter_expr(param_a); ivl_expr_t value_b = ivl_parameter_expr(param_b); diff --git a/tgt-vlog95/scope.c b/tgt-vlog95/scope.c index 75928099f..81177cd83 100644 --- a/tgt-vlog95/scope.c +++ b/tgt-vlog95/scope.c @@ -309,6 +309,13 @@ void emit_scope_variables(ivl_scope_t scope) count = ivl_scope_params(scope); for (idx = 0; idx < count; idx += 1) { ivl_parameter_t par = ivl_scope_param(scope, idx); + // vlog95 does not support type parameters. Places where type + // parameters have been used it will be replaced with the actual + // type that the module was instantiated with. Similar to + // typedefs. + if (ivl_parameter_is_type(par)) + continue; + ivl_expr_t pex = ivl_parameter_expr(par); fprintf(vlog_out, "%*cparameter ", indent, ' '); emit_id(ivl_parameter_basename(par)); diff --git a/tgt-vvp/vvp_scope.c b/tgt-vvp/vvp_scope.c index 58b94c9be..5ef76a200 100644 --- a/tgt-vvp/vvp_scope.c +++ b/tgt-vvp/vvp_scope.c @@ -2415,6 +2415,13 @@ int draw_scope(ivl_scope_t net, ivl_scope_t parent) for (idx = 0 ; idx < ivl_scope_params(net) ; idx += 1) { ivl_parameter_t par = ivl_scope_param(net, idx); + + // Skip type parameters for now. Support for type parameters + // should be added together with support for quering types through + // VPI. + if (ivl_parameter_is_type(par)) + continue; + ivl_expr_t pex = ivl_parameter_expr(par); switch (ivl_expr_type(pex)) { case IVL_EX_STRING: