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 <lars@metafoo.de>
This commit is contained in:
Lars-Peter Clausen 2021-12-20 17:00:24 +01:00
parent 3509cc86f8
commit 5ef847ea87
19 changed files with 195 additions and 49 deletions

View File

@ -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;
};

View File

@ -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,

View File

@ -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;
}

View File

@ -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

View File

@ -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);

View File

@ -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<const PETypename*>(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_()).

View File

@ -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 &param,
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;
}

View File

@ -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 &param,
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::map<perm_string,param_expr_t>parameters;
@ -1240,6 +1248,7 @@ class NetScope : public Definitions, public Attrib {
std::map<perm_string,LocalVar> 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);

22
parse.y
View File

@ -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<pform_range_t>* 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;
}
;

View File

@ -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;

View File

@ -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,

View File

@ -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;
};

View File

@ -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);

View File

@ -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;
}
}

View File

@ -638,6 +638,7 @@ struct ivl_parameter_s {
long lsb;
bool signed_flag;
bool local;
bool is_type;
perm_string file;
unsigned lineno;
};

View File

@ -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) << "\"";

View File

@ -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);

View File

@ -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));

View File

@ -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: