Allow specparam declarations outside specify blocks.

This patch extends the compiler to support all specparam declarations
allowed by the 1364-2005 standard. For compatibility with other
simulators, it allows specparam values to be used in any constant
expression, but outputs a warning message and disables run-time
annotation of a specparam if it is used in an expression that must
be evaluated at compile time.
This commit is contained in:
Martin Whitaker 2012-05-06 23:11:26 +01:00 committed by Stephen Williams
parent 5d05d97eb0
commit 44c5a37ab8
15 changed files with 194 additions and 169 deletions

View File

@ -1,7 +1,7 @@
#ifndef __Module_H
#define __Module_H
/*
* Copyright (c) 1998-2010 Stephen Williams (steve@icarus.com)
* Copyright (c) 1998-2010,2012 Stephen Williams (steve@icarus.com)
*
* This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU
@ -79,10 +79,10 @@ class Module : public PScopeExtra, public LineInfo {
enum UCDriveType { UCD_NONE, UCD_PULL0, UCD_PULL1 };
UCDriveType uc_drive;
/* specparams are simpler than other params, in that they have
no type information. They are merely constant
expressions. */
map<perm_string,PExpr*>specparams;
/* specparams are simpler than other parameters, in that they
can have a range, but not an explicit type. The restrictions
are enforced by the parser. */
map<perm_string,param_expr_t>specparams;
/* The module also has defparam assignments which don't create
new parameters within the module, but may be used to set
@ -140,6 +140,7 @@ class Module : public PScopeExtra, public LineInfo {
bool elaborate_sig(Design*, NetScope*scope) const;
private:
void dump_specparams_(ostream&out, unsigned indent) const;
list<PGate*> gates_;
private: // Not implemented

View File

@ -49,6 +49,7 @@ class PExpr : public LineInfo {
static const unsigned NO_FLAGS = 0x0;
static const unsigned NEED_CONST = 0x1;
static const unsigned SYS_TASK_ARG = 0x2;
static const unsigned ANNOTATABLE = 0x4;
PExpr();
virtual ~PExpr();

View File

@ -1141,7 +1141,10 @@ void NetScope::dump(ostream&o) const
map<perm_string,param_expr_t>::const_iterator pp;
for (pp = parameters.begin()
; pp != parameters.end() ; ++ pp ) {
o << " parameter ";
if ((*pp).second.is_annotatable)
o << " specparam ";
else
o << " parameter ";
o << pp->second.type << " ";
@ -1245,27 +1248,6 @@ void NetScope::dump(ostream&o) const
cur->second->dump_net(o, 4);
}
// Dump specparams
typedef map<perm_string,spec_val_t>::const_iterator specparam_it_t;
for (specparam_it_t cur = specparams.begin()
; cur != specparams.end() ; ++ cur ) {
o << " specparam " << (*cur).first
<< " = ";
spec_val_t value = (*cur).second;
switch (value.type) {
case IVL_VT_REAL:
o << "R:" << value.real_val;
break;
case IVL_VT_BOOL:
o << "I:" << value.integer;
break;
default:
o << "<bad type>";
break;
}
o << endl;
}
switch (type_) {
case FUNC:
if (func_def())

View File

@ -2172,32 +2172,6 @@ unsigned PEIdent::test_width(Design*des, NetScope*scope, width_mode_t&mode)
return expr_width_;
}
// The width of a specparam is the width of the specparam value
// (as evaluated earlier). Note that specparams aren't fully
// supported yet, so this code is likely to need rework when
// they are.
map<perm_string,NetScope::spec_val_t>::const_iterator specp;
perm_string key = peek_tail_name(path_);
if (path_.size() == 1 &&
((specp = scope->specparams.find(key)) != scope->specparams.end())) {
NetScope::spec_val_t value = (*specp).second;
if (value.type == IVL_VT_REAL) {
expr_type_ = IVL_VT_REAL;
expr_width_ = 1;
min_width_ = 1;
signed_flag_ = true;
} else {
verinum val (value.integer);
expr_type_ = IVL_VT_BOOL;
expr_width_ = val.len();
min_width_ = expr_width_;
signed_flag_ = true;
if (mode < LOSSLESS) mode = LOSSLESS;
}
return expr_width_;
}
// If this is SystemVerilog then maybe this is a structure element.
if (gn_system_verilog() && found_in==0 && path_.size() >= 2) {
pform_name_t use_path = path_;
@ -2371,34 +2345,6 @@ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope,
return tmp;
}
// A specparam? Look up the name to see if it is a
// specparam. If we find it, then turn it into a NetEConst
// value and return that.
map<perm_string,NetScope::spec_val_t>::const_iterator specp;
perm_string key = peek_tail_name(path_);
if (path_.size() == 1 &&
((specp = scope->specparams.find(key)) != scope->specparams.end())) {
NetScope::spec_val_t value = (*specp).second;
NetExpr*tmp = 0;
switch (value.type) {
case IVL_VT_BOOL:
tmp = new NetEConst(verinum(value.integer));
break;
case IVL_VT_REAL:
tmp = new NetECReal(verireal(value.real_val));
break;
default:
break;
}
assert(tmp);
tmp->set_line(*this);
if (debug_elaborate)
cerr << get_fileline() << ": debug: " << path_
<< " is a specparam" << endl;
return tmp;
}
// Maybe this is a method attached to an enumeration name? If
// this is system verilog, then test to see if the name is
// really a method attached to an object.
@ -2862,6 +2808,16 @@ NetExpr* PEIdent::elaborate_expr_param_(Design*des,
{
bool need_const = NEED_CONST & flags;
if (need_const && !(ANNOTATABLE & flags)) {
perm_string name = peek_tail_name(path_);
if (found_in->make_parameter_unannotatable(name)) {
cerr << get_fileline() << ": warning: specparam '" << name
<< "' is being used in a constant expression." << endl;
cerr << get_fileline() << ": : This will prevent it "
"being annotated at run time." << endl;
}
}
const name_component_t&name_tail = path_.back();
index_component_t::ctype_t use_sel = index_component_t::SEL_NONE;
if (!name_tail.index.empty())

View File

@ -51,7 +51,8 @@
typedef map<perm_string,LexicalScope::param_expr_t>::const_iterator mparm_it_t;
static void collect_parm_item_(Design*des, NetScope*scope, perm_string name,
const LexicalScope::param_expr_t&cur)
const LexicalScope::param_expr_t&cur,
bool is_annotatable)
{
NetScope::range_t*range_list = 0;
for (LexicalScope::range_t*range = cur.range ; range ; range = range->next) {
@ -87,8 +88,8 @@ static void collect_parm_item_(Design*des, NetScope*scope, perm_string name,
range_list = tmp;
}
scope->set_parameter(name, cur.expr, cur.type, cur.msb, cur.lsb,
cur.signed_flag, range_list, cur);
scope->set_parameter(name, is_annotatable, cur.expr, cur.type, cur.msb,
cur.lsb, cur.signed_flag, range_list, cur);
}
static void collect_scope_parameters_(Design*des, NetScope*scope,
@ -106,7 +107,7 @@ static void collect_scope_parameters_(Design*des, NetScope*scope,
des->errors += 1;
}
collect_parm_item_(des, scope, (*cur).first, (*cur).second);
collect_parm_item_(des, scope, (*cur).first, (*cur).second, false);
}
}
@ -125,7 +126,26 @@ static void collect_scope_localparams_(Design*des, NetScope*scope,
des->errors += 1;
}
collect_parm_item_(des, scope, (*cur).first, (*cur).second);
collect_parm_item_(des, scope, (*cur).first, (*cur).second, false);
}
}
static void collect_scope_specparams_(Design*des, NetScope*scope,
const map<perm_string,LexicalScope::param_expr_t>&specparams)
{
for (mparm_it_t cur = specparams.begin()
; cur != specparams.end() ; ++ cur ) {
// A specparam can not have the same name as a genvar.
if (scope->find_genvar((*cur).first)) {
cerr << cur->second.get_fileline()
<< ": error: specparam and genvar in '"
<< scope->fullname() << "' have the same name '"
<< (*cur).first << "'." << endl;
des->errors += 1;
}
collect_parm_item_(des, scope, (*cur).first, (*cur).second, true);
}
}
@ -469,6 +489,8 @@ bool Module::elaborate_scope(Design*des, NetScope*scope,
collect_scope_localparams_(des, scope, localparams);
collect_scope_specparams_(des, scope, specparams);
// Run parameter replacements that were collected from the
// containing scope and meant for me.

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1998-2011 Stephen Williams (steve@icarus.com)
* Copyright (c) 1998-2012 Stephen Williams (steve@icarus.com)
*
* This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU
@ -4447,49 +4447,6 @@ bool Module::elaborate(Design*des, NetScope*scope) const
{
bool result_flag = true;
// Elaborate specparams
typedef map<perm_string,PExpr*>::const_iterator specparam_it_t;
for (specparam_it_t cur = specparams.begin() ;
cur != specparams.end() ; ++ cur ) {
NetExpr*val = elab_and_eval(des, scope, (*cur).second, -1, true);
NetScope::spec_val_t value;
if (NetECReal*val_cr = dynamic_cast<NetECReal*> (val)) {
value.type = IVL_VT_REAL;
value.real_val = val_cr->value().as_double();
if (debug_elaborate) {
cerr << get_fileline() << ": debug: Elaborate "
<< "specparam " << (*cur).first
<< " value=" << value.real_val << endl;
}
} else if (NetEConst*val_c = dynamic_cast<NetEConst*> (val)) {
value.type = IVL_VT_BOOL;
value.integer = val_c->value().as_long();
if (debug_elaborate) {
cerr << get_fileline() << ": debug: Elaborate "
<< "specparam " << (*cur).first
<< " value=" << value.integer << endl;
}
} else {
value.type = IVL_VT_NO_TYPE;
cerr << (*cur).second->get_fileline() << ": error: "
<< "specparam " << (*cur).first
<< " value is not constant: " << *val << endl;
des->errors += 1;
}
assert(val);
delete val;
scope->specparams[(*cur).first] = value;
}
// Elaborate within the generate blocks.
typedef list<PGenerate*>::const_iterator generate_it_t;
for (generate_it_t cur = generate_schemes.begin()

View File

@ -370,7 +370,8 @@ void NetScope::evaluate_parameter_logic_(Design*des, param_ref_t cur)
if (range_flag)
lv_width = (msb >= lsb) ? 1 + msb - lsb : 1 + lsb - msb;
NetExpr*expr = elab_and_eval(des, val_scope, val_expr, lv_width, true);
NetExpr*expr = elab_and_eval(des, val_scope, val_expr, lv_width, true,
(*cur).second.is_annotatable);
if (! expr)
return;
@ -491,7 +492,8 @@ void NetScope::evaluate_parameter_real_(Design*des, param_ref_t cur)
PExpr*val_expr = (*cur).second.val_expr;
NetScope*val_scope = (*cur).second.val_scope;
NetExpr*expr = elab_and_eval(des, val_scope, val_expr, -1, true);
NetExpr*expr = elab_and_eval(des, val_scope, val_expr, -1, true,
(*cur).second.is_annotatable);
if (! expr)
return;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2000-2011 Stephen Williams (steve@icarus.com)
* Copyright (c) 2000-2012 Stephen Williams (steve@icarus.com)
*
* This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU
@ -114,13 +114,14 @@ void NetScope::set_line(perm_string file, perm_string def_file,
def_lineno_ = def_lineno;
}
void NetScope::set_parameter(perm_string key, PExpr*val,
ivl_variable_type_t type__,
void NetScope::set_parameter(perm_string key, bool is_annotatable,
PExpr*val, ivl_variable_type_t type__,
PExpr*msb, PExpr*lsb, bool signed_flag,
NetScope::range_t*range_list,
const LineInfo&file_line)
{
param_expr_t&ref = parameters[key];
ref.is_annotatable = is_annotatable;
ref.msb_expr = msb;
ref.lsb_expr = lsb;
ref.val_expr = val;
@ -188,6 +189,19 @@ bool NetScope::replace_parameter(perm_string key, PExpr*val, NetScope*scope)
return flag;
}
bool NetScope::make_parameter_unannotatable(perm_string key)
{
bool flag = false;
if (parameters.find(key) != parameters.end()) {
param_expr_t&ref = parameters[key];
flag = ref.is_annotatable;
ref.is_annotatable = false;
}
return flag;
}
/*
* This is not really complete (msb, lsb, sign). It is currently only
* used to add a genvar to the local parameter list.
@ -197,6 +211,7 @@ NetExpr* NetScope::set_localparam(perm_string key, NetExpr*val,
{
param_expr_t&ref = localparams[key];
NetExpr* res = ref.val;
ref.is_annotatable = false;
ref.msb_expr = 0;
ref.lsb_expr = 0;
ref.val_expr = 0;

View File

@ -766,8 +766,8 @@ class NetScope : public Attrib {
previous expression, if there was one. */
struct range_t;
void set_parameter(perm_string name, PExpr*val,
ivl_variable_type_t type,
void set_parameter(perm_string name, bool is_annotatable,
PExpr*val, ivl_variable_type_t type,
PExpr*msb, PExpr*lsb, bool signed_flag,
NetScope::range_t*range_list,
const LineInfo&file_line);
@ -789,6 +789,13 @@ class NetScope : public Attrib {
exist. */
bool replace_parameter(perm_string name, PExpr*val, NetScope*scope);
/* This is used to ensure the value of a parameter cannot be
changed at run-time. This is required if a specparam is used
in an expression that must be evaluated at compile-time.
Returns true if the named parameter is a specparam and has
not already been set to be unannotatable. */
bool make_parameter_unannotatable(perm_string name);
/* These methods set or access events that live in this
scope. */
@ -955,7 +962,8 @@ class NetScope : public Attrib {
access to these things to make up the parameter lists. */
struct param_expr_t : public LineInfo {
param_expr_t() : msb_expr(0), lsb_expr(0), val_expr(0), val_scope(0),
solving(false), type(IVL_VT_NO_TYPE), signed_flag(false),
solving(false), is_annotatable(false),
type(IVL_VT_NO_TYPE), signed_flag(false),
msb(0), lsb(0), range(0), val(0) { }
// Source expressions
PExpr*msb_expr;
@ -965,6 +973,8 @@ class NetScope : public Attrib {
NetScope*val_scope;
// Evaluation status
bool solving;
// specparam status
bool is_annotatable;
// Type information
ivl_variable_type_t type;
bool signed_flag;
@ -982,15 +992,6 @@ class NetScope : public Attrib {
param_ref_t find_parameter(perm_string name);
struct spec_val_t {
ivl_variable_type_t type;
union {
double real_val; // type == IVL_VT_REAL
long integer; // type == IVL_VT_BOOL
};
};
map<perm_string,spec_val_t>specparams;
/* Module instance arrays are collected here for access during
the multiple elaboration passes. */
typedef vector<NetScope*> scope_vec_t;

View File

@ -519,7 +519,7 @@ static const char*width_mode_name(PExpr::width_mode_t mode)
}
NetExpr* elab_and_eval(Design*des, NetScope*scope, PExpr*pe,
int context_width, bool need_const)
int context_width, bool need_const, bool annotatable)
{
PExpr::width_mode_t mode = PExpr::SIZED;
if ((context_width == -2) && !gn_strict_expr_width_flag)
@ -562,6 +562,8 @@ NetExpr* elab_and_eval(Design*des, NetScope*scope, PExpr*pe,
unsigned flags = PExpr::NO_FLAGS;
if (need_const)
flags |= PExpr::NEED_CONST;
if (annotatable)
flags |= PExpr::ANNOTATABLE;
NetExpr*tmp = pe->elaborate_expr(des, scope, expr_width, flags);
if (tmp == 0) return 0;

View File

@ -192,7 +192,8 @@ class PExpr;
extern NetExpr* elab_and_eval(Design*des, NetScope*scope,
PExpr*pe, int context_width,
bool need_const =false);
bool need_const =false,
bool annotatable =false);
/*
* This function is a variant of elab_and_eval that elaborates and

26
parse.y
View File

@ -4296,6 +4296,10 @@ module_item
pform_endgenerate();
}
/* 1364-2001 and later allow specparam declarations outside specify blocks. */
| attribute_list_opt K_specparam specparam_decl ';'
/* specify blocks are parsed but ignored. */
| K_specify K_endspecify
@ -5126,7 +5130,7 @@ net_variable_list
;
specify_item
: K_specparam specparam_list ';'
: K_specparam specparam_decl ';'
| specify_simple_path_decl ';'
{ pform_module_specify_path($1);
}
@ -5332,7 +5336,8 @@ specify_path_identifiers
specparam
: IDENTIFIER '=' expression
{ PExpr*tmp = $3;
pform_set_specparam(lex_strings.make($1), tmp);
pform_set_specparam(@1, lex_strings.make($1),
param_active_range, tmp);
delete[]$1;
}
| IDENTIFIER '=' expression ':' expression ':' expression
@ -5370,7 +5375,8 @@ specparam
cerr << " expression." << endl;
min_typ_max_warn -= 1;
}
pform_set_specparam(lex_strings.make($1), tmp);
pform_set_specparam(@1, lex_strings.make($1),
param_active_range, tmp);
delete[]$1;
}
| PATHPULSE_IDENTIFIER '=' expression
@ -5385,9 +5391,17 @@ specparam
;
specparam_list
: specparam
| specparam_list ',' specparam
;
: specparam
| specparam_list ',' specparam
;
specparam_decl
: specparam_list
| range
{ param_active_range = $1; }
specparam_list
{ param_active_range = 0; }
;
spec_polarity
: '+' { $$ = '+'; }

View File

@ -2331,6 +2331,15 @@ void pform_set_parameter(const struct vlltype&loc,
<< "' have the same name '" << name << "'." << endl;
error_count += 1;
}
if ((scope == pform_cur_module) &&
(pform_cur_module->specparams.find(name) != pform_cur_module->specparams.end())) {
LineInfo tloc;
FILE_NAME(&tloc, loc);
cerr << tloc.get_fileline() << ": error: specparam and "
"parameter in '" << pform_cur_module->mod_name()
<< "' have the same name '" << name << "'." << endl;
error_count += 1;
}
assert(expr);
Module::param_expr_t&parm = scope->parameters[name];
@ -2380,6 +2389,15 @@ void pform_set_localparam(const struct vlltype&loc,
<< "' have the same name '" << name << "'." << endl;
error_count += 1;
}
if ((scope == pform_cur_module) &&
(pform_cur_module->specparams.find(name) != pform_cur_module->specparams.end())) {
LineInfo tloc;
FILE_NAME(&tloc, loc);
cerr << tloc.get_fileline() << ": error: specparam and "
"localparam in '" << pform_cur_module->mod_name()
<< "' have the same name '" << name << "'." << endl;
error_count += 1;
}
assert(expr);
Module::param_expr_t&parm = scope->localparams[name];
@ -2403,19 +2421,58 @@ void pform_set_localparam(const struct vlltype&loc,
parm.range = 0;
}
void pform_set_specparam(perm_string name, PExpr*expr)
void pform_set_specparam(const struct vlltype&loc, perm_string name,
list<pform_range_t>*range, PExpr*expr)
{
Module*scope = pform_cur_module;
assert(scope == lexical_scope);
// Check if the specparam name is already in the dictionary.
if (pform_cur_module->specparams.find(name) !=
pform_cur_module->specparams.end()) {
cerr << expr->get_fileline() << ": error: duplicate definition "
if (scope->specparams.find(name) != scope->specparams.end()) {
LineInfo tloc;
FILE_NAME(&tloc, loc);
cerr << tloc.get_fileline() << ": error: duplicate definition "
"for specparam '" << name << "' in '"
<< pform_cur_module->mod_name() << "'." << endl;
error_count += 1;
}
if (scope->parameters.find(name) != scope->parameters.end()) {
LineInfo tloc;
FILE_NAME(&tloc, loc);
cerr << tloc.get_fileline() << ": error: parameter and "
"specparam in '" << pform_cur_module->mod_name()
<< "' have the same name '" << name << "'." << endl;
error_count += 1;
}
if (scope->localparams.find(name) != scope->localparams.end()) {
LineInfo tloc;
FILE_NAME(&tloc, loc);
cerr << tloc.get_fileline() << ": error: localparam and "
"specparam in '" << pform_cur_module->mod_name()
<< "' have the same name '" << name << "'." << endl;
error_count += 1;
}
assert(expr);
pform_cur_module->specparams[name] = expr;
Module::param_expr_t&parm = scope->specparams[name];
FILE_NAME(&parm, loc);
parm.expr = expr;
parm.type = IVL_VT_LOGIC;
if (range) {
assert(range->size() == 1);
pform_range_t&rng = range->front();
assert(rng.first);
assert(rng.second);
parm.msb = rng.first;
parm.lsb = rng.second;
} else {
parm.msb = 0;
parm.lsb = 0;
}
parm.signed_flag = false;
parm.range = 0;
}
void pform_set_defparam(const pform_name_t&name, PExpr*expr)

View File

@ -331,13 +331,15 @@ extern void pform_set_localparam(const struct vlltype&loc,
bool signed_flag,
list<pform_range_t>*range,
PExpr*expr);
extern void pform_set_specparam(const struct vlltype&loc,
perm_string name,
list<pform_range_t>*range,
PExpr*expr);
extern void pform_set_defparam(const pform_name_t&name, PExpr*expr);
/*
* Functions related to specify blocks.
*/
extern void pform_set_specparam(perm_string name, PExpr*expr);
extern PSpecPath*pform_make_specify_path(const struct vlltype&li,
list<perm_string>*src, char pol,
bool full_flag, list<perm_string>*dst);

View File

@ -1194,6 +1194,23 @@ void LexicalScope::dump_wires_(ostream&out, unsigned indent) const
}
}
void Module::dump_specparams_(ostream&out, unsigned indent) const
{
typedef map<perm_string,param_expr_t>::const_iterator parm_iter_t;
for (parm_iter_t cur = specparams.begin()
; cur != specparams.end() ; ++ cur ) {
out << setw(indent) << "" << "specparam ";
if ((*cur).second.msb)
out << "[" << *(*cur).second.msb << ":"
<< *(*cur).second.lsb << "] ";
out << (*cur).first << " = ";
if ((*cur).second.expr)
out << *(*cur).second.expr << ";" << endl;
else
out << "/* ERROR */;" << endl;
}
}
void Module::dump(ostream&out) const
{
if (attributes.begin() != attributes.end()) {
@ -1237,6 +1254,8 @@ void Module::dump(ostream&out) const
dump_localparams_(out, 4);
dump_specparams_(out, 4);
dump_enumerations_(out, 4);
typedef map<perm_string,LineInfo*>::const_iterator genvar_iter_t;
@ -1251,13 +1270,6 @@ void Module::dump(ostream&out) const
(*cur)->dump(out, 4);
}
typedef map<perm_string,PExpr*>::const_iterator specparm_iter_t;
for (specparm_iter_t cur = specparams.begin()
; cur != specparams.end() ; ++ cur ) {
out << " specparam " << (*cur).first << " = "
<< *(*cur).second << ";" << endl;
}
typedef list<Module::named_expr_t>::const_iterator parm_hiter_t;
for (parm_hiter_t cur = defparms.begin()
; cur != defparms.end() ; ++ cur ) {