From 44c5a37ab88f9adf54721fab00876edb9a38add2 Mon Sep 17 00:00:00 2001 From: Martin Whitaker Date: Sun, 6 May 2012 23:11:26 +0100 Subject: [PATCH 001/179] 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. --- Module.h | 11 +++++---- PExpr.h | 1 + design_dump.cc | 26 +++----------------- elab_expr.cc | 64 ++++++++--------------------------------------- elab_scope.cc | 32 ++++++++++++++++++++---- elaborate.cc | 45 +-------------------------------- net_design.cc | 6 +++-- net_scope.cc | 21 +++++++++++++--- netlist.h | 25 ++++++++++--------- netmisc.cc | 4 ++- netmisc.h | 3 ++- parse.y | 26 +++++++++++++++----- pform.cc | 67 ++++++++++++++++++++++++++++++++++++++++++++++---- pform.h | 6 +++-- pform_dump.cc | 26 ++++++++++++++------ 15 files changed, 194 insertions(+), 169 deletions(-) diff --git a/Module.h b/Module.h index a791a6be2..1f9c32349 100644 --- a/Module.h +++ b/Module.h @@ -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. */ - mapspecparams; + /* 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. */ + mapspecparams; /* 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 gates_; private: // Not implemented diff --git a/PExpr.h b/PExpr.h index 257f3e977..a4036680b 100644 --- a/PExpr.h +++ b/PExpr.h @@ -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(); diff --git a/design_dump.cc b/design_dump.cc index e3d14f3b0..bc44b69f1 100644 --- a/design_dump.cc +++ b/design_dump.cc @@ -1141,7 +1141,10 @@ void NetScope::dump(ostream&o) const map::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::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 << ""; - break; - } - o << endl; - } - switch (type_) { case FUNC: if (func_def()) diff --git a/elab_expr.cc b/elab_expr.cc index 2e51f4f3d..213a580db 100644 --- a/elab_expr.cc +++ b/elab_expr.cc @@ -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::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::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()) diff --git a/elab_scope.cc b/elab_scope.cc index 5d8af02dc..565b1f452 100644 --- a/elab_scope.cc +++ b/elab_scope.cc @@ -51,7 +51,8 @@ typedef map::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&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. diff --git a/elaborate.cc b/elaborate.cc index 77a82f745..579f2753a 100644 --- a/elaborate.cc +++ b/elaborate.cc @@ -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::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 (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 (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::const_iterator generate_it_t; for (generate_it_t cur = generate_schemes.begin() diff --git a/net_design.cc b/net_design.cc index 3ed2fef8c..ac5517bd4 100644 --- a/net_design.cc +++ b/net_design.cc @@ -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; diff --git a/net_scope.cc b/net_scope.cc index a1231055b..3d55e54c2 100644 --- a/net_scope.cc +++ b/net_scope.cc @@ -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; diff --git a/netlist.h b/netlist.h index a99a41730..efbcd6ad7 100644 --- a/netlist.h +++ b/netlist.h @@ -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 - }; - }; - mapspecparams; - /* Module instance arrays are collected here for access during the multiple elaboration passes. */ typedef vector scope_vec_t; diff --git a/netmisc.cc b/netmisc.cc index d6b5e6b98..9ce4ebf00 100644 --- a/netmisc.cc +++ b/netmisc.cc @@ -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; diff --git a/netmisc.h b/netmisc.h index 7f34131fa..0504db472 100644 --- a/netmisc.h +++ b/netmisc.h @@ -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 diff --git a/parse.y b/parse.y index 5d67e237b..8b196015c 100644 --- a/parse.y +++ b/parse.y @@ -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 : '+' { $$ = '+'; } diff --git a/pform.cc b/pform.cc index ac8cb2a9b..a21ae1182 100644 --- a/pform.cc +++ b/pform.cc @@ -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*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) diff --git a/pform.h b/pform.h index c907c6b95..c17a2e2b6 100644 --- a/pform.h +++ b/pform.h @@ -331,13 +331,15 @@ extern void pform_set_localparam(const struct vlltype&loc, bool signed_flag, list*range, PExpr*expr); +extern void pform_set_specparam(const struct vlltype&loc, + perm_string name, + list*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*src, char pol, bool full_flag, list*dst); diff --git a/pform_dump.cc b/pform_dump.cc index f5859b69a..5adb1b407 100644 --- a/pform_dump.cc +++ b/pform_dump.cc @@ -1194,6 +1194,23 @@ void LexicalScope::dump_wires_(ostream&out, unsigned indent) const } } +void Module::dump_specparams_(ostream&out, unsigned indent) const +{ + typedef map::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::const_iterator genvar_iter_t; @@ -1251,13 +1270,6 @@ void Module::dump(ostream&out) const (*cur)->dump(out, 4); } - typedef map::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::const_iterator parm_hiter_t; for (parm_hiter_t cur = defparms.begin() ; cur != defparms.end() ; ++ cur ) { From 5cbdac2a46258c36d00bc5c27350f8c9c27056e1 Mon Sep 17 00:00:00 2001 From: Martin Whitaker Date: Sun, 6 May 2012 23:44:22 +0100 Subject: [PATCH 002/179] Add missing semi-colons in main compiler parse.y This fixes some warnings from Bison. --- parse.y | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/parse.y b/parse.y index 8b196015c..c067278a0 100644 --- a/parse.y +++ b/parse.y @@ -836,7 +836,7 @@ constraint_block_item_list_opt constraint_declaration /* IEEE1800-2005: A.1.9 */ : K_static_opt K_constraint IDENTIFIER '{' constraint_block_item_list_opt '}' - { yyerror(@2, "sorry: Constraint declarations not supported.") } + { yyerror(@2, "sorry: Constraint declarations not supported."); } /* Error handling rules... */ @@ -860,7 +860,7 @@ constraint_expression_list /* */ constraint_prototype /* IEEE1800-2005: A.1.9 */ : K_static_opt K_constraint IDENTIFIER ';' - { yyerror(@2, "sorry: Constraint prototypes not supported.") } + { yyerror(@2, "sorry: Constraint prototypes not supported."); } ; constraint_set /* IEEE1800-2005 A.1.9 */ @@ -1893,11 +1893,11 @@ type_declaration delete[]$3; } | K_typedef K_enum IDENTIFIER ';' - { yyerror(@1, "sorry: Enum forward declarations not supported yet.") } + { yyerror(@1, "sorry: Enum forward declarations not supported yet."); } | K_typedef K_struct IDENTIFIER ';' - { yyerror(@1, "sorry: Struct forward declarations not supported yet.") } + { yyerror(@1, "sorry: Struct forward declarations not supported yet."); } | K_typedef K_union IDENTIFIER ';' - { yyerror(@1, "sorry: Union forward declarations not supported yet.") } + { yyerror(@1, "sorry: Union forward declarations not supported yet."); } | K_typedef IDENTIFIER ';' { // Create a synthetic typedef for the class name so that the // lexor detects the name as a type. From 509ec1dcb18e630f17b1cb8219256486fc2a775e Mon Sep 17 00:00:00 2001 From: Martin Whitaker Date: Mon, 7 May 2012 13:01:12 +0100 Subject: [PATCH 003/179] Simplify parameter handling in the elaborated netlist. A NetScope object currently has two lists of parameters, 'parameters' and 'localparams'. However, user-declared localparams are stored in the 'parameters' list, and 'localparams' is only used for adding genvar values to the parameter list. There seems no good reason to maintain separate lists, as the lists are merged before being passed to the target DLL. This is most likely a hang-over from older code. --- design_dump.cc | 6 ----- elab_scope.cc | 2 +- net_scope.cc | 63 ++++++++++++++++++++++---------------------------- netlist.h | 8 +++---- t-dll.cc | 15 +----------- 5 files changed, 32 insertions(+), 62 deletions(-) diff --git a/design_dump.cc b/design_dump.cc index bc44b69f1..2ded152a2 100644 --- a/design_dump.cc +++ b/design_dump.cc @@ -1191,12 +1191,6 @@ void NetScope::dump(ostream&o) const o << ";" << endl; } - - for (pp = localparams.begin() - ; pp != localparams.end() ; ++ pp ) { - o << " localparam " << (*pp).first << " = " << - *(*pp).second.val << ";" << endl; - } } /* Dump the saved defparam assignments here. */ diff --git a/elab_scope.cc b/elab_scope.cc index 565b1f452..234c8c983 100644 --- a/elab_scope.cc +++ b/elab_scope.cc @@ -730,7 +730,7 @@ bool PGenerate::generate_scope_loop_(Design*des, NetScope*container) genvar_verinum); // The file and line information should really come // from the genvar statement, not the for loop. - scope->set_localparam(loop_index, gp, *this); + scope->set_parameter(loop_index, gp, *this); if (debug_scopes) cerr << get_fileline() << ": debug: " << "Create implicit localparam " diff --git a/net_scope.cc b/net_scope.cc index 3d55e54c2..d51ade0b4 100644 --- a/net_scope.cc +++ b/net_scope.cc @@ -114,6 +114,11 @@ void NetScope::set_line(perm_string file, perm_string def_file, def_lineno_ = def_lineno; } +/* + * This is the full-featured version of set_parameter. It is used for + * adding parameter, localparam, and specparam declarations to the + * parameter list. + */ void NetScope::set_parameter(perm_string key, bool is_annotatable, PExpr*val, ivl_variable_type_t type__, PExpr*msb, PExpr*lsb, bool signed_flag, @@ -138,6 +143,28 @@ void NetScope::set_parameter(perm_string key, bool is_annotatable, ivl_assert(file_line, type__ != IVL_VT_NO_TYPE); } +/* + * This is a simplified version of set_parameter, for use when the + * parameter value is already known. It is currently only used to + * add a genvar to the parameter list. + */ +void NetScope::set_parameter(perm_string key, NetExpr*val, + const LineInfo&file_line) +{ + param_expr_t&ref = parameters[key]; + ref.is_annotatable = false; + ref.msb_expr = 0; + ref.lsb_expr = 0; + ref.val_expr = 0; + ref.val_scope = this; + ref.type = IVL_VT_BOOL; + ref.msb = 0; + ref.lsb = 0; + ref.signed_flag = false; + ref.val = val; + ref.set_line(file_line); +} + bool NetScope::auto_name(const char*prefix, char pad, const char* suffix) { // Find the current reference to myself in the parent scope. @@ -202,29 +229,6 @@ bool NetScope::make_parameter_unannotatable(perm_string key) return flag; } -/* - * This is not really complete (msb, lsb, sign). It is currently only - * used to add a genvar to the local parameter list. - */ -NetExpr* NetScope::set_localparam(perm_string key, NetExpr*val, - const LineInfo&file_line) -{ - 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; - ref.val_scope = this; - ref.type = IVL_VT_BOOL; - ref.msb = 0; - ref.lsb = 0; - ref.signed_flag = false; - ref.val = val; - ref.set_line(file_line); - return res; -} - /* * NOTE: This method takes a const char* as a key to lookup a * parameter, because we don't save that pointer. However, due to the @@ -257,16 +261,6 @@ const NetExpr* NetScope::get_parameter(Design*des, return idx->second.val; } - idx = localparams.find(key); - if (idx != localparams.end()) { - if (idx->second.val_expr) - evaluate_parameter_(des, idx); - - msb = idx->second.msb; - lsb = idx->second.lsb; - return idx->second.val; - } - map::const_iterator eidx; eidx = enum_names_.find(key); @@ -286,9 +280,6 @@ NetScope::param_ref_t NetScope::find_parameter(perm_string key) idx = parameters.find(key); if (idx != parameters.end()) return idx; - idx = localparams.find(perm_string::literal(key)); - if (idx != localparams.end()) return idx; - // To get here the parameter must already exist, so we should // never get here. assert(0); diff --git a/netlist.h b/netlist.h index efbcd6ad7..ba9ff80e3 100644 --- a/netlist.h +++ b/netlist.h @@ -762,8 +762,7 @@ class NetScope : public Attrib { /* Parameters exist within a scope, and these methods allow one to manipulate the set. In these cases, the name is the *simple* name of the parameter, the hierarchy is implicit in - the scope. The return value from set_parameter is the - previous expression, if there was one. */ + the scope. */ struct range_t; void set_parameter(perm_string name, bool is_annotatable, @@ -771,8 +770,8 @@ class NetScope : public Attrib { PExpr*msb, PExpr*lsb, bool signed_flag, NetScope::range_t*range_list, const LineInfo&file_line); - NetExpr* set_localparam(perm_string name, NetExpr*val, - const LineInfo&file_line); + void set_parameter(perm_string name, NetExpr*val, + const LineInfo&file_line); const NetExpr*get_parameter(Design*des, const char* name, @@ -986,7 +985,6 @@ class NetScope : public Attrib { NetExpr*val; }; mapparameters; - maplocalparams; typedef map::iterator param_ref_t; diff --git a/t-dll.cc b/t-dll.cc index e6b44c75f..e16f8477f 100644 --- a/t-dll.cc +++ b/t-dll.cc @@ -461,7 +461,7 @@ ivl_parameter_t dll_target::scope_find_param(ivl_scope_t scope, */ void dll_target::make_scope_parameters(ivl_scope_t scop, const NetScope*net) { - scop->nparam_ = net->parameters.size() + net->localparams.size(); + scop->nparam_ = net->parameters.size(); if (scop->nparam_ == 0) { scop->param_ = 0; return; @@ -481,19 +481,6 @@ 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; - make_scope_param_expr(cur_par, etmp); - idx += 1; - } - for (pit_t cur_pit = net->localparams.begin() - ; cur_pit != net->localparams.end() ; ++ cur_pit ) { - - assert(idx < scop->nparam_); - ivl_parameter_t cur_par = scop->param_ + idx; - cur_par->basename = (*cur_pit).first; - cur_par->scope = scop; - FILE_NAME(cur_par, &((*cur_pit).second)); - NetExpr*etmp = (*cur_pit).second.val; make_scope_param_expr(cur_par, etmp); idx += 1; From 84f14dbc815b6b5720a9e62cd09bc76b4bca8bfd Mon Sep 17 00:00:00 2001 From: Larry Doolittle Date: Mon, 14 May 2012 09:58:49 -0700 Subject: [PATCH 004/179] Spelling fixes to vhdlpp tree Mostly comments as usual, but one error message is changed. --- vhdlpp/README.txt | 2 +- vhdlpp/architec_elaborate.cc | 4 ++-- vhdlpp/entity.h | 2 +- vhdlpp/expression.h | 2 +- vhdlpp/expression_elaborate.cc | 4 ++-- vhdlpp/lexor.lex | 8 ++++---- vhdlpp/parse_api.h | 2 +- vhdlpp/vtype.h | 2 +- 8 files changed, 13 insertions(+), 13 deletions(-) diff --git a/vhdlpp/README.txt b/vhdlpp/README.txt index e904d94c0..7ee8efe50 100644 --- a/vhdlpp/README.txt +++ b/vhdlpp/README.txt @@ -49,4 +49,4 @@ in your VHDL code, access packages like this: The *.pkg files are just VHDL code containing only the package with the same name. When Icarus Verilog encounters the "use ..*;" statement, it looks for the .pkg file in the library and -parses that file to get the package header declared therin. +parses that file to get the package header declared therein. diff --git a/vhdlpp/architec_elaborate.cc b/vhdlpp/architec_elaborate.cc index 4a289a466..b820d3f39 100644 --- a/vhdlpp/architec_elaborate.cc +++ b/vhdlpp/architec_elaborate.cc @@ -120,11 +120,11 @@ int ForGenerate::elaborate(Entity*ent, Architecture*arc) /* * This method attempts to rewrite the process content as an * always-@(n-edge ) version of the same statement. This makes - * for a more natural translation to verilog, if it comes to that. + * for a more natural translation to Verilog, if it comes to that. */ int ProcessStatement::rewrite_as_always_edge_(Entity*, Architecture*) { - // If thare are multiple sensitivity expressions, I give up. + // If there are multiple sensitivity expressions, I give up. if (sensitivity_list_.size() != 1) return -1; diff --git a/vhdlpp/entity.h b/vhdlpp/entity.h index 850a4a587..3b446c352 100644 --- a/vhdlpp/entity.h +++ b/vhdlpp/entity.h @@ -104,7 +104,7 @@ class Entity : public ComponentBase { // After the architecture is bound, elaboration calls this // method to elaborate this entity. This method arranges for - // elaboration to hapen all the way through the architecture + // elaboration to happen all the way through the architecture // that is bound to this entity. int elaborate(); diff --git a/vhdlpp/expression.h b/vhdlpp/expression.h index 5e6c49c5a..2c42aa44e 100644 --- a/vhdlpp/expression.h +++ b/vhdlpp/expression.h @@ -189,7 +189,7 @@ class ExpAggregate : public Expression { // true if this represents an "others" choice bool others() const; - // Return expression if this reprents simple_expression. + // Return expression if this represents a simple_expression. Expression*simple_expression(bool detach_flag =true); void dump(ostream&out, int indent) const; diff --git a/vhdlpp/expression_elaborate.cc b/vhdlpp/expression_elaborate.cc index d93db16ba..baa54e08c 100644 --- a/vhdlpp/expression_elaborate.cc +++ b/vhdlpp/expression_elaborate.cc @@ -120,7 +120,7 @@ int ExpName::elaborate_rval(Entity*ent, Architecture*arc, const InterfacePort*lv /* IEEE 1076-2008, p.80: * For a formal port IN, associated port should be IN, OUT, INOUT or BUFFER * For a formal port OUT, associated port should be OUT, INOUT or BUFFER - * For a formal port INOUT, associated prot should be OUT, INOUT or BUFFER + * For a formal port INOUT, associated port should be OUT, INOUT or BUFFER * For a formal port BUFFER, associated port should be OUT, INOUT or BUFFER */ switch(lval->mode) { @@ -193,7 +193,7 @@ int ExpBinary::elaborate_exprs(Entity*ent, Architecture*arc, const VType*ltype) int ExpAggregate::elaborate_expr(Entity*ent, Architecture*arc, const VType*ltype) { if (ltype == 0) { - cerr << get_fileline() << ": error: Elaboration of aggregate types needs wel known type context?" << endl; + cerr << get_fileline() << ": error: Elaboration of aggregate types needs well known type context?" << endl; return 1; } diff --git a/vhdlpp/lexor.lex b/vhdlpp/lexor.lex index 9ab591168..9d41c3b3b 100644 --- a/vhdlpp/lexor.lex +++ b/vhdlpp/lexor.lex @@ -44,10 +44,10 @@ extern int lexor_keyword_code (const char*str, unsigned len); /* - * Lexical location information is passed in the yylloc variable to th + * Lexical location information is passed in the yylloc variable to the * parser. The file names, strings, are kept in a list so that I can * re-use them. The set_file_name function will return a pointer to - * the name as it exists in the list (and delete the passed string.) + * the name as it exists in the list (and delete the passed string). * If the name is new, it will be added to the list. */ #define yylloc (*yyllocp) @@ -89,7 +89,7 @@ based_integer [0-9a-fA-F](_?[0-9a-fA-F])* [ \t\b\f\r] { ; } \n { yylloc.first_line += 1; } - /* Single-line comments start with - - and run to the end of the + /* Single-line comments start with -- and run to the end of the current line. These are very easy to handle. */ "--".* { comment_enter = YY_START; BEGIN(LCOMMENT); } @@ -635,7 +635,7 @@ static double make_double_from_based(char* text) *first_hash_ptr = '\0'; *second_hash_ptr = '\0'; - //now lets deduce the base + //now let's deduce the base unsigned base = (unsigned)strtol(text, 0, 10) ; double mantissa = 0.0; diff --git a/vhdlpp/parse_api.h b/vhdlpp/parse_api.h index d4da8543b..4dbf4ffe3 100644 --- a/vhdlpp/parse_api.h +++ b/vhdlpp/parse_api.h @@ -25,7 +25,7 @@ typedef void*yyscan_t; /* - * The vlltype supports the passing of detailed source file location + * The yyltype supports the passing of detailed source file location * information between the lexical analyzer and the parser. Defining * YYLTYPE compels the lexor to use this type and not something other. */ diff --git a/vhdlpp/vtype.h b/vhdlpp/vtype.h index bd3ffb788..2e0f96d2a 100644 --- a/vhdlpp/vtype.h +++ b/vhdlpp/vtype.h @@ -83,7 +83,7 @@ inline std::ostream&operator << (std::ostream&out, const VType&item) extern void preload_global_types(void); /* - * This class represents the primative types that are available to the + * This class represents the primitive types that are available to the * type subsystem. */ class VTypePrimitive : public VType { From 2d0c786bfb546e64fd28ae7ac6fc04ff015e285d Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Fri, 18 May 2012 08:07:27 -0700 Subject: [PATCH 005/179] Remove excess evaluate_parameters method calls. --- elaborate.cc | 19 ++++++++++++++++++- net_design.cc | 8 +++----- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/elaborate.cc b/elaborate.cc index 579f2753a..c684b0de1 100644 --- a/elaborate.cc +++ b/elaborate.cc @@ -4695,6 +4695,9 @@ class top_defparams : public elaborator_work_item_t { virtual void elaborate_runrun() { + if (debug_scopes) { + cerr << "debug: top_defparams::elaborate_runrun()" << endl; + } // This method recurses through the scopes, looking for // defparam assignments to apply to the parameters in the // various scopes. This needs to be done after all the scopes @@ -4706,6 +4709,10 @@ class top_defparams : public elaborator_work_item_t { // scopes and evaluate the parameters all the way down to // constants. des->evaluate_parameters(); + + if (debug_scopes) { + cerr << "debug: top_defparams::elaborate_runrun() done" << endl; + } } }; @@ -4719,6 +4726,10 @@ class later_defparams : public elaborator_work_item_t { virtual void elaborate_runrun() { + if (debug_scopes) { + cerr << "debug: later_defparams::elaborate_runrun()" << endl; + } + listtmp_list; for (set::iterator cur = des->defparams_later.begin() ; cur != des->defparams_later.end() ; ++ cur ) @@ -4731,7 +4742,13 @@ class later_defparams : public elaborator_work_item_t { tmp_list.pop_front(); cur->run_defparams_later(des); } - des->evaluate_parameters(); + + // The overridden parameters will be evaluated later in + // a top_defparams work item. + + if (debug_scopes) { + cerr << "debuf: later_defparams::elaborate_runrun() done" << endl; + } } }; diff --git a/net_design.cc b/net_design.cc index ac5517bd4..0ea857cf4 100644 --- a/net_design.cc +++ b/net_design.cc @@ -303,11 +303,9 @@ void NetScope::run_defparams_later(Design*des) target_scopes.insert(targ_scope); } - // All the scopes that this defparam set touched should have - // their parameters re-evaluated. - for (set::iterator cur = target_scopes.begin() - ; cur != target_scopes.end() ; ++ cur ) - (*cur)->evaluate_parameters(des); + // The scopes that this defparam set touched will be + // re-evaluated later it a top_defparams work item. So do not + // do the evaluation now. // If there are some scopes that still have missing scopes, // then save them back into the defparams_later list for a From f7ba954ef74f320124f40698b7d8942f1ec65c66 Mon Sep 17 00:00:00 2001 From: Martin Whitaker Date: Fri, 18 May 2012 19:43:26 +0100 Subject: [PATCH 006/179] Fix for pr3527022. This patch adds support for an explicit range or type in a parameter declaration that is part of a module parameter port list. --- parse.y | 194 ++++++++++++-------------------------------------------- 1 file changed, 41 insertions(+), 153 deletions(-) diff --git a/parse.y b/parse.y index c067278a0..71a17b01a 100644 --- a/parse.y +++ b/parse.y @@ -1833,8 +1833,8 @@ block_item_decl { pform_make_events($2, @1.text, @1.first_line); } - | K_parameter parameter_assign_decl ';' - | K_localparam localparam_assign_decl ';' + | K_parameter param_type parameter_assign_list ';' + | K_localparam param_type localparam_assign_list ';' /* Blocks can have type declarations. */ @@ -1853,15 +1853,15 @@ block_item_decl yyerrok; } - | K_parameter error ';' - { yyerror(@1, "error: syntax error in parameter list."); - yyerrok; - } - | K_localparam error ';' - { yyerror(@1, "error: syntax error localparam list."); - yyerrok; - } - ; + | K_parameter error ';' + { yyerror(@1, "error: syntax error in parameter list."); + yyerrok; + } + | K_localparam error ';' + { yyerror(@1, "error: syntax error localparam list."); + yyerrok; + } + ; block_item_decls : block_item_decl @@ -3932,9 +3932,9 @@ module_parameter_port_list_opt ; module_parameter_port_list - : K_parameter parameter_assign + : K_parameter param_type parameter_assign | module_parameter_port_list ',' parameter_assign - | module_parameter_port_list ',' K_parameter parameter_assign + | module_parameter_port_list ',' K_parameter param_type parameter_assign ; module_item @@ -4474,19 +4474,15 @@ var_type : K_reg { $$ = NetNet::REG; } ; - /* In this rule we have matched the "parameter" keyword. The rule - generates a type (optional) and a list of assignments. */ - -parameter_assign_decl - : parameter_assign_list - | range - { param_active_range = $1; +param_type + : + { param_active_range = 0; param_active_signed = false; param_active_type = IVL_VT_LOGIC; } - parameter_assign_list - { param_active_range = 0; - param_active_signed = false; + | range + { param_active_range = $1; + param_active_signed = false; param_active_type = IVL_VT_LOGIC; } | K_signed @@ -4494,67 +4490,47 @@ parameter_assign_decl param_active_signed = true; param_active_type = IVL_VT_LOGIC; } - parameter_assign_list - { param_active_range = 0; - param_active_signed = false; - param_active_type = IVL_VT_LOGIC; - } | K_signed range { param_active_range = $2; param_active_signed = true; param_active_type = IVL_VT_LOGIC; } - parameter_assign_list - { param_active_range = 0; - param_active_signed = false; - param_active_type = IVL_VT_LOGIC; - } | K_integer { param_active_range = make_range_from_width(integer_width); param_active_signed = true; param_active_type = IVL_VT_LOGIC; } - parameter_assign_list - { param_active_range = 0; - param_active_signed = false; - param_active_type = IVL_VT_LOGIC; - } | K_time { param_active_range = make_range_from_width(64); param_active_signed = false; param_active_type = IVL_VT_LOGIC; } - parameter_assign_list - { param_active_range = 0; - param_active_signed = false; - param_active_type = IVL_VT_LOGIC; - } | real_or_realtime { param_active_range = 0; param_active_signed = true; param_active_type = IVL_VT_REAL; } - parameter_assign_list - { param_active_range = 0; - param_active_signed = false; - param_active_type = IVL_VT_LOGIC; - } | atom2_type { param_active_range = make_range_from_width($1); param_active_signed = true; param_active_type = IVL_VT_BOOL; } - parameter_assign_list - { param_active_range = 0; - param_active_signed = false; - param_active_type = IVL_VT_LOGIC; - } ; + /* parameter and localparam assignment lists are broken into + separate BNF so that I can call slightly different parameter + handling code. localparams parse the same as parameters, they + just behave differently when someone tries to override them. */ + parameter_assign_list - : parameter_assign - | parameter_assign_list ',' parameter_assign - ; + : parameter_assign + | parameter_assign_list ',' parameter_assign + ; + +localparam_assign_list + : localparam_assign + | localparam_assign_list ',' localparam_assign + ; parameter_assign : IDENTIFIER '=' expression parameter_value_ranges_opt @@ -4565,6 +4541,15 @@ parameter_assign } ; +localparam_assign + : IDENTIFIER '=' expression + { PExpr*tmp = $3; + pform_set_localparam(@1, lex_strings.make($1), param_active_type, + param_active_signed, param_active_range, tmp); + delete[]$1; + } + ; + parameter_value_ranges_opt : parameter_value_ranges { $$ = $1; } | { $$ = 0; } ; parameter_value_ranges @@ -4596,103 +4581,6 @@ value_range_expression from_exclude : K_from { $$ = false; } | K_exclude { $$ = true; } ; - /* Localparam assignments and assignment lists are broken into - separate BNF so that I can call slightly different parameter - handling code. They parse the same as parameters, they just - behave differently when someone tries to override them. */ - -localparam_assign - : IDENTIFIER '=' expression - { PExpr*tmp = $3; - pform_set_localparam(@1, lex_strings.make($1), - param_active_type, - param_active_signed, - param_active_range, tmp); - delete[]$1; - } - ; - -localparam_assign_decl - : localparam_assign_list - | range - { param_active_range = $1; - param_active_signed = false; - param_active_type = IVL_VT_LOGIC; - } - localparam_assign_list - { param_active_range = 0; - param_active_signed = false; - param_active_type = IVL_VT_LOGIC; - } - | K_signed - { param_active_range = 0; - param_active_signed = true; - param_active_type = IVL_VT_LOGIC; - } - localparam_assign_list - { param_active_range = 0; - param_active_signed = false; - param_active_type = IVL_VT_LOGIC; - } - | K_signed range - { param_active_range = $2; - param_active_signed = true; - param_active_type = IVL_VT_LOGIC; - } - localparam_assign_list - { param_active_range = 0; - param_active_signed = false; - param_active_type = IVL_VT_LOGIC; - } - | K_integer - { param_active_range = make_range_from_width(integer_width); - param_active_signed = true; - param_active_type = IVL_VT_LOGIC; - } - localparam_assign_list - { param_active_range = 0; - param_active_signed = false; - param_active_type = IVL_VT_LOGIC; - } - | K_time - { param_active_range = make_range_from_width(64); - param_active_signed = false; - param_active_type = IVL_VT_LOGIC; - } - localparam_assign_list - { param_active_range = 0; - param_active_signed = false; - param_active_type = IVL_VT_LOGIC; - } - | real_or_realtime - { param_active_range = 0; - param_active_signed = true; - param_active_type = IVL_VT_REAL; - } - localparam_assign_list - { param_active_range = 0; - param_active_signed = false; - param_active_type = IVL_VT_LOGIC; - } - | atom2_type - { param_active_range = make_range_from_width($1); - param_active_signed = true; - param_active_type = IVL_VT_BOOL; - } - localparam_assign_list - { param_active_range = 0; - param_active_signed = false; - param_active_type = IVL_VT_LOGIC; - } - ; - -localparam_assign_list - : localparam_assign - | localparam_assign_list ',' localparam_assign - ; - - - /* The parameters of a module instance can be overridden by writing a list of expressions in a syntax much like a delay list. (The difference being the list can have any length.) The pform that From 9ed56a6354aad55d52572076395c9b80527a5662 Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Sat, 17 Mar 2012 15:03:19 -0700 Subject: [PATCH 007/179] Parse record types, and some new aggregate types. --- vhdlpp/expression.h | 2 + vhdlpp/parse.y | 122 +++++++++++++++++++++++++++++++++++++++---- vhdlpp/vtype.cc | 37 +++++++++++++ vhdlpp/vtype.h | 32 ++++++++++++ vhdlpp/vtype_emit.cc | 14 +++++ 5 files changed, 196 insertions(+), 11 deletions(-) diff --git a/vhdlpp/expression.h b/vhdlpp/expression.h index 2c42aa44e..fdc218652 100644 --- a/vhdlpp/expression.h +++ b/vhdlpp/expression.h @@ -215,6 +215,8 @@ class ExpAggregate : public Expression { size_t count_choices() const { return fields_.size(); } void map_choices(choice_element*dst); + inline Expression* extract_expression() { return val_; } + void dump(ostream&out, int indent) const; private: diff --git a/vhdlpp/parse.y b/vhdlpp/parse.y index 8b519537e..844a7c307 100644 --- a/vhdlpp/parse.y +++ b/vhdlpp/parse.y @@ -139,6 +139,47 @@ const VType*parse_type_by_name(perm_string name) return active_scope->find_type(name); } +// This funciton is called when an aggregate expression is detected by +// the parser. It makes the ExpAggregate. It also tries to detect the +// special case that the aggregate is really a primary. The problem is +// that this: +// ( ) +// also matches the pattern: +// ( [ choices => ] ... ) +// so try to assume that a single expression in parentheses is a +// primary and fix the parse by returning an Expression instead of an +// ExpAggregate. +static Expression*aggregate_or_primary(const YYLTYPE&loc, std::list*el) +{ + if (el->size() != 1) { + ExpAggregate*tmp = new ExpAggregate(el); + FILE_NAME(tmp,loc); + return tmp; + } + + ExpAggregate::element_t*el1 = el->front(); + if (el1->count_choices() > 0) { + ExpAggregate*tmp = new ExpAggregate(el); + FILE_NAME(tmp,loc); + return tmp; + } + + return el1->extract_expression(); +} + +static list* record_elements(list*names, + const VType*type) +{ + list*res = new list; + + for (list::iterator cur = names->begin() + ; cur != names->end() ; ++cur) { + res->push_back(new VTypeRecord::element_t(*cur, type)); + } + + return res; +} + %} @@ -185,6 +226,8 @@ const VType*parse_type_by_name(perm_string name) ExpAggregate::element_t*element; std::list*element_list; + std::list*record_elements; + std::list* interface_list; Architecture::Statement* arch_statement; @@ -264,8 +307,11 @@ const VType*parse_type_by_name(perm_string name) %type association_list port_map_aspect port_map_aspect_opt %type generic_map_aspect generic_map_aspect_opt +%type composite_type_definition record_type_definition %type subtype_indication type_definition +%type element_declaration element_declaration_list + %type architecture_body_start package_declaration_start %type identifier_opt identifier_colon_opt logical_name suffix %type logical_name_list identifier_list @@ -595,6 +641,17 @@ component_specification } ; +composite_type_definition + /* constrained_array_definition */ + : K_array index_constraint K_of subtype_indication + { VTypeArray*tmp = new VTypeArray($4, $2); + delete $2; + $$ = tmp; + } + | record_type_definition + { $$ = $1; } + ; + concurrent_signal_assignment_statement : name LEQ waveform ';' { ExpName*name = dynamic_cast ($1); @@ -699,6 +756,19 @@ constant_declaration { sorrymsg(@1, "Deferred constant declarations not supported\n"); delete $2; } + + /* Some error handling... */ + + | K_constant identifier_list ':' subtype_indication VASSIGN error ';' + { // The syntax allows mutliple names to have the same type/value. + errormsg(@6, "Error in value expression for constants.\n"); + yyerrok; + for (std::list::iterator cur = $2->begin() + ; cur != $2->end() ; ++cur) { + active_scope->bind_name(*cur, $4, 0); + } + delete $2; + } ; context_clause : context_items | ; @@ -732,6 +802,10 @@ element_association { ExpAggregate::element_t*tmp = new ExpAggregate::element_t($1, $3); $$ = tmp; } + | expression + { ExpAggregate::element_t*tmp = new ExpAggregate::element_t(0, $1); + $$ = tmp; + } ; element_association_list @@ -747,6 +821,21 @@ element_association_list } ; +element_declaration + : identifier_list ':' subtype_indication ';' + { $$ = record_elements($1, $3); } + ; + +element_declaration_list + : element_declaration_list element_declaration + { $$ = $1; + $$->splice($$->end(), *$2); + delete $2; + } + | element_declaration + { $$ = $1; } + ; + /* As an entity is declared, add it to the map of design entities. */ entity_aspect : K_entity name @@ -1372,7 +1461,12 @@ package_declarative_item : component_declaration | constant_declaration | subtype_declaration + | type_declaration | use_clause + | error ';' + { errormsg(@1, "Syntax error in package declarative item.\n"); + yyerrok; + } ; package_declarative_items @@ -1479,7 +1573,10 @@ primary delete[]$1; $$ = tmp; } - +/*XXXX Caught up in element_association_list? + | '(' expression ')' + { $$ = $2; } +*/ /* This catches function calls that use association lists for the argument list. The position argument list is discovered elsewhere and must be discovered by elaboration (thanks to the ambiguity of @@ -1489,11 +1586,10 @@ primary $$ = 0; } - | '(' expression ')' - { $$ = $2; } + /* Aggregates */ + | '(' element_association_list ')' - { ExpAggregate*tmp = new ExpAggregate($2); - FILE_NAME(tmp,@1); + { Expression*tmp = aggregate_or_primary(@1, $2); $$ = tmp; } ; @@ -1638,6 +1734,13 @@ range_list } ; +record_type_definition + : K_record element_declaration_list K_end K_record + { VTypeRecord*tmp = new VTypeRecord($2); + $$ = tmp; + } + ; + relation : shift_expression { $$ = $1; } @@ -1911,12 +2014,9 @@ type_definition delete $2; $$ = tmp; } - /* constrained_array_definition */ - | K_array index_constraint K_of subtype_indication - { VTypeArray*tmp = new VTypeArray($4, $2); - delete $2; - $$ = tmp; - } + | composite_type_definition + { $$ = $1; } + ; use_clause diff --git a/vhdlpp/vtype.cc b/vhdlpp/vtype.cc index 4a8e4ced7..25f669813 100644 --- a/vhdlpp/vtype.cc +++ b/vhdlpp/vtype.cc @@ -162,6 +162,43 @@ void VTypeEnum::show(ostream&out) const out << ")"; } +VTypeRecord::VTypeRecord(std::list*elements) +: elements_(elements->size()) +{ + for (size_t idx = 0 ; idx < elements_.size() ; idx += 1) { + elements_[idx] = elements->front(); + elements->pop_front(); + } + delete elements; +} + +VTypeRecord::~VTypeRecord() +{ + for (size_t idx = 0 ; idx < elements_.size() ; idx += 1) + delete elements_[idx]; +} + +void VTypeRecord::show(ostream&out) const +{ + out << "record "; + for (size_t idx = 0 ; idx < elements_.size() ; idx += 1) { + elements_[idx]->show(out); + out << "; "; + } + out << "endrecord"; +} + +VTypeRecord::element_t::element_t(perm_string name, const VType*typ) +: name_(name), type_(typ) +{ +} + +void VTypeRecord::element_t::show(ostream&out) const +{ + out << name_ << ":"; + type_->show(out); +} + VTypeDef::VTypeDef(perm_string nam, const VType*typ) : name_(nam), type_(typ) { diff --git a/vhdlpp/vtype.h b/vhdlpp/vtype.h index 2e0f96d2a..bdda995ed 100644 --- a/vhdlpp/vtype.h +++ b/vhdlpp/vtype.h @@ -198,6 +198,38 @@ class VTypeEnum : public VType { std::vectornames_; }; +class VTypeRecord : public VType { + + public: + class element_t { + public: + element_t(perm_string name, const VType*type); + + void show(std::ostream&) const; + + private: + perm_string name_; + const VType*type_; + + private:// Not implement + element_t(const element_t&); + element_t& operator= (const element_t); + }; + + public: + explicit VTypeRecord(std::list*elements); + ~VTypeRecord(); + + void show(std::ostream&) const; + + int emit_def(std::ostream&out, perm_string name) const; + private: + int emit_decl(std::ostream&out, perm_string name, bool reg_flag) const; + + private: + std::vector elements_; +}; + class VTypeDef : public VType { public: diff --git a/vhdlpp/vtype_emit.cc b/vhdlpp/vtype_emit.cc index e9a59c3b0..e6042f0e7 100644 --- a/vhdlpp/vtype_emit.cc +++ b/vhdlpp/vtype_emit.cc @@ -159,6 +159,20 @@ int VTypeRange::emit_decl(ostream&out, perm_string name, bool reg_flag) const return errors; } +int VTypeRecord::emit_def(ostream&out, perm_string name) const +{ + int errors = 0; + assert(0); + return errors; +} + +int VTypeRecord::emit_decl(ostream&out, perm_string name, bool reg_flag) const +{ + int errors = 0; + assert(0); + return errors; +} + int VTypeDef::emit_def(ostream&out, perm_string name) const { int errors = 0; From 79435924f24d2f62b8d34821611596d3dabfcde6 Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Sun, 18 Mar 2012 11:21:23 -0700 Subject: [PATCH 008/179] Move some VType::show methods to ::write_to_stream methods. --- vhdlpp/vtype.cc | 13 +--------- vhdlpp/vtype.h | 5 +++- vhdlpp/vtype_stream.cc | 55 ++++++++++++++++++++++++++++++++++++------ 3 files changed, 53 insertions(+), 20 deletions(-) diff --git a/vhdlpp/vtype.cc b/vhdlpp/vtype.cc index 25f669813..5c346ea5f 100644 --- a/vhdlpp/vtype.cc +++ b/vhdlpp/vtype.cc @@ -180,12 +180,7 @@ VTypeRecord::~VTypeRecord() void VTypeRecord::show(ostream&out) const { - out << "record "; - for (size_t idx = 0 ; idx < elements_.size() ; idx += 1) { - elements_[idx]->show(out); - out << "; "; - } - out << "endrecord"; + write_to_stream(out); } VTypeRecord::element_t::element_t(perm_string name, const VType*typ) @@ -193,12 +188,6 @@ VTypeRecord::element_t::element_t(perm_string name, const VType*typ) { } -void VTypeRecord::element_t::show(ostream&out) const -{ - out << name_ << ":"; - type_->show(out); -} - VTypeDef::VTypeDef(perm_string nam, const VType*typ) : name_(nam), type_(typ) { diff --git a/vhdlpp/vtype.h b/vhdlpp/vtype.h index bdda995ed..5d86f05c9 100644 --- a/vhdlpp/vtype.h +++ b/vhdlpp/vtype.h @@ -173,6 +173,8 @@ class VTypeRange : public VType { VTypeRange(const VType*base, int64_t max_val, int64_t min_val); ~VTypeRange(); + void write_to_stream(std::ostream&fd) const; + int emit_def(std::ostream&out, perm_string name) const; private: int emit_decl(std::ostream&out, perm_string name, bool reg_flag) const; @@ -205,7 +207,7 @@ class VTypeRecord : public VType { public: element_t(perm_string name, const VType*type); - void show(std::ostream&) const; + void write_to_stream(std::ostream&) const; private: perm_string name_; @@ -220,6 +222,7 @@ class VTypeRecord : public VType { explicit VTypeRecord(std::list*elements); ~VTypeRecord(); + void write_to_stream(std::ostream&fd) const; void show(std::ostream&) const; int emit_def(std::ostream&out, perm_string name) const; diff --git a/vhdlpp/vtype_stream.cc b/vhdlpp/vtype_stream.cc index c87b80ecc..179096211 100644 --- a/vhdlpp/vtype_stream.cc +++ b/vhdlpp/vtype_stream.cc @@ -37,9 +37,15 @@ void VTypeArray::write_to_stream(ostream&fd) const if (! ranges_.empty()) { assert(ranges_.size() < 2); fd << " ("; - ranges_[0].msb()->write_to_stream(fd); + if (ranges_[0].msb()) + ranges_[0].msb()->write_to_stream(fd); + else + fd << "<>"; fd << " downto "; - ranges_[0].lsb()->write_to_stream(fd); + if (ranges_[0].lsb()) + ranges_[0].lsb()->write_to_stream(fd); + else + fd << "<>"; fd << ") "; } return; @@ -48,11 +54,22 @@ void VTypeArray::write_to_stream(ostream&fd) const fd << "array "; if (! ranges_.empty()) { assert(ranges_.size() < 2); - fd << "("; - ranges_[0].msb()->write_to_stream(fd); - fd << " downto "; - ranges_[0].lsb()->write_to_stream(fd); - fd << ") "; + if (ranges_[0].is_box()) { + fd << "(INTEGER range <>) "; + } else { + assert(ranges_[0].msb() && ranges_[0].lsb()); + fd << "("; + if (ranges_[0].msb()) + ranges_[0].msb()->write_to_stream(fd); + else + fd << "<>"; + fd << " downto "; + if (ranges_[0].lsb()) + ranges_[0].lsb()->write_to_stream(fd); + else + fd << "<>"; + fd << ") "; + } } fd << "of "; @@ -71,9 +88,33 @@ void VTypePrimitive::write_to_stream(ostream&fd) const case STDLOGIC: fd << "std_logic"; break; + case BOOLEAN: + fd << "boolean"; + break; default: assert(0); fd << "/* PRIMITIVE: " << type_ << " */"; break; } } + +void VTypeRange::write_to_stream(ostream&fd) const +{ + fd << "range " << min_ << " to " << max_; +} + +void VTypeRecord::write_to_stream(ostream&fd) const +{ + fd << "record "; + for (size_t idx = 0 ; idx < elements_.size() ; idx += 1) { + elements_[idx]->write_to_stream(fd); + fd << "; "; + } + fd << "endrecord"; +} + +void VTypeRecord::element_t::write_to_stream(ostream&fd) const +{ + fd << name_ << ":"; + type_->write_to_stream(fd); +} From ed3da959f3d491817278c16bc8f4c620f70db58d Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Sun, 18 Mar 2012 12:49:19 -0700 Subject: [PATCH 009/179] Support types in packages. Types declared in packages should be written into the package library. --- vhdlpp/library.cc | 11 +++++++++++ vhdlpp/package.cc | 35 ++++++++++++++++++++++++++++++++++- vhdlpp/parse_misc.h | 2 ++ vhdlpp/scope.cc | 8 +++++++- vhdlpp/scope.h | 4 ++-- vhdlpp/vtype_stream.cc | 2 +- 6 files changed, 57 insertions(+), 5 deletions(-) diff --git a/vhdlpp/library.cc b/vhdlpp/library.cc index 776789eb1..f262a1f15 100644 --- a/vhdlpp/library.cc +++ b/vhdlpp/library.cc @@ -338,6 +338,17 @@ void generate_global_types(ActiveScope*res) res->bind_name(perm_string::literal("natural"), primitive_NATURAL); } +bool is_global_type(perm_string name) +{ + if (name == "boolean") return true; + if (name == "bit") return true; + if (name == "integer") return true; + if (name == "std_logic") return true; + if (name == "bit_vector") return true; + if (name == "natural") return true; + return false; +} + void library_set_work_path(const char*path) { assert(library_work_path == 0); diff --git a/vhdlpp/package.cc b/vhdlpp/package.cc index 211b422f9..b9bbe58a8 100644 --- a/vhdlpp/package.cc +++ b/vhdlpp/package.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011 Stephen Williams (steve@icarus.com) + * Copyright (c) 2011-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 @@ -19,6 +19,7 @@ # include "package.h" # include "entity.h" +# include "parse_misc.h" Package::Package(perm_string n, const ScopeBase&ref) : Scope(ref), name_(n) @@ -30,10 +31,42 @@ Package::~Package() ScopeBase::cleanup(); } +/* + * The Package::write_to_stream is used to write the package to the + * work space (or library) so writes proper VHDL that the library + * parser can bring back in as needed. + */ void Package::write_to_stream(ostream&fd) const { fd << "package " << name_ << " is" << endl; + for (map::const_iterator cur = old_types_.begin() + ; cur != old_types_.end() ; ++cur) { + + // Do not include global types in types dump + if (is_global_type(cur->first)) + continue; + if (cur->first == "std_logic_vector") + continue; + + fd << cur->first << ": "; + cur->second->write_to_stream(fd); + fd << ";" << endl; + } + for (map::const_iterator cur = new_types_.begin() + ; cur != new_types_.end() ; ++cur) { + + // Do not include primitive types in type dump + if (is_global_type(cur->first)) + continue; + if (cur->first == "std_logic_vector") + continue; + + fd << cur->first << ": "; + cur->second->write_to_stream(fd); + fd << ";" << endl; + } + for (map::const_iterator cur = old_components_.begin() ; cur != old_components_.end() ; ++cur) { diff --git a/vhdlpp/parse_misc.h b/vhdlpp/parse_misc.h index d62c3e92c..9bb36b28d 100644 --- a/vhdlpp/parse_misc.h +++ b/vhdlpp/parse_misc.h @@ -63,4 +63,6 @@ extern void library_use(const YYLTYPE&loc, ActiveScope*res, const char*libname, extern void generate_global_types(ActiveScope*res); +extern bool is_global_type(perm_string type_name); + #endif diff --git a/vhdlpp/scope.cc b/vhdlpp/scope.cc index a0c51bb2e..7dbfe0e23 100644 --- a/vhdlpp/scope.cc +++ b/vhdlpp/scope.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011 Stephen Williams (steve@icarus.com) + * Copyright (c) 2011-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 @@ -24,6 +24,12 @@ using namespace std; +/* + * If the merge_flag is passed in, then the new scope is a merge of + * the parent scopes. This brings in all of the parent scopes into the + * "old_*_" variables. This clears up the "new_*_" variables to + * accumulate new scope values. + */ ScopeBase::ScopeBase(const ScopeBase&ref) { merge(ref.old_constants_.begin(), ref.old_constants_.end(), diff --git a/vhdlpp/scope.h b/vhdlpp/scope.h index 121b46627..8e5997781 100644 --- a/vhdlpp/scope.h +++ b/vhdlpp/scope.h @@ -1,7 +1,7 @@ #ifndef __scope_H #define __scope_H /* - * Copyright (c) 2011 Stephen Williams (steve@icarus.com) + * Copyright (c) 2011-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 @@ -95,7 +95,7 @@ class ScopeBase { class Scope : public ScopeBase { public: - Scope(const ScopeBase&ref); + explicit Scope(const ScopeBase&ref); ~Scope(); ComponentBase* find_component(perm_string by_name); diff --git a/vhdlpp/vtype_stream.cc b/vhdlpp/vtype_stream.cc index 179096211..c39dd76f9 100644 --- a/vhdlpp/vtype_stream.cc +++ b/vhdlpp/vtype_stream.cc @@ -34,7 +34,7 @@ void VTypeArray::write_to_stream(ostream&fd) const // Special case: std_logic_vector if (etype_ == primitive_STDLOGIC) { fd << "std_logic_vector"; - if (! ranges_.empty()) { + if (! ranges_.empty() && ! ranges_[0].is_box()) { assert(ranges_.size() < 2); fd << " ("; if (ranges_[0].msb()) From 0775e36a67525b3fe58373ff441d0cb38c7b0460 Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Sun, 18 Mar 2012 17:36:13 -0700 Subject: [PATCH 010/179] Properly elaborate argument types for binary relation expressions. The argument types of binary relation expressions are decoupled from the return type for the expression itself. --- vhdlpp/expression_elaborate.cc | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/vhdlpp/expression_elaborate.cc b/vhdlpp/expression_elaborate.cc index baa54e08c..fed635a5c 100644 --- a/vhdlpp/expression_elaborate.cc +++ b/vhdlpp/expression_elaborate.cc @@ -177,7 +177,12 @@ const VType* ExpBinary::probe_type(Entity*ent, Architecture*arc) const if (t1 == t2) return t1; + // FIXME: I should at this point try harder to find an + // operator that has the proper argument list and use this + // here, but for now we leave it for the back-end to figure out. +#if 0 cerr << get_fileline() << ": internal error: I don't know how to resolve types of generic binary expressions." << endl; +#endif return 0; } @@ -406,8 +411,10 @@ const VType* ExpName::probe_type(Entity*ent, Architecture*arc) const int ExpName::elaborate_expr(Entity*, Architecture*, const VType*ltype) { - ivl_assert(*this, ltype != 0); - set_type(ltype); + if (ltype) { + ivl_assert(*this, ltype != 0); + set_type(ltype); + } return 0; } @@ -434,7 +441,13 @@ int ExpRelation::elaborate_expr(Entity*ent, Architecture*arc, const VType*ltype) } ivl_assert(*this, ltype != 0); - errors += elaborate_exprs(ent, arc, ltype); + + // The type of the operands must match, but need not match the + // type for the ExpRelation itself. So get the operand type + // separately. + const VType*otype = ExpBinary::probe_type(ent, arc); + errors += elaborate_exprs(ent, arc, otype); + return errors; } From 1249b5dd3296c110edcb3b304f55f2b25bdee8a0 Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Sun, 18 Mar 2012 17:37:19 -0700 Subject: [PATCH 011/179] Initial support for if_generate syntax. --- vhdlpp/architec.cc | 10 ++++++++++ vhdlpp/architec.h | 13 +++++++++++++ vhdlpp/architec_elaborate.cc | 7 +++++++ vhdlpp/architec_emit.cc | 18 ++++++++++++++++-- vhdlpp/parse.y | 33 +++++++++++++++++++++++++++++++-- 5 files changed, 77 insertions(+), 4 deletions(-) diff --git a/vhdlpp/architec.cc b/vhdlpp/architec.cc index 05a31809b..32af037ea 100644 --- a/vhdlpp/architec.cc +++ b/vhdlpp/architec.cc @@ -69,6 +69,16 @@ ForGenerate::~ForGenerate() { } +IfGenerate::IfGenerate(perm_string gname, Expression*cond, + std::list&s) +: GenerateStatement(gname, s), cond_(cond) +{ +} + +IfGenerate::~IfGenerate() +{ +} + SignalAssignment::SignalAssignment(ExpName*name, list&rv) : lval_(name) { diff --git a/vhdlpp/architec.h b/vhdlpp/architec.h index 2a40189ef..4e46652ed 100644 --- a/vhdlpp/architec.h +++ b/vhdlpp/architec.h @@ -127,6 +127,19 @@ class ForGenerate : public GenerateStatement { Expression*msb_; }; +class IfGenerate : public GenerateStatement { + + public: + IfGenerate(perm_string gname, Expression*cond, + std::list&s); + ~IfGenerate(); + + int elaborate(Entity*ent, Architecture*arc); + int emit(ostream&out, Entity*entity, Architecture*arc); + + private: + Expression*cond_; +}; /* * The SignalAssignment class represents the diff --git a/vhdlpp/architec_elaborate.cc b/vhdlpp/architec_elaborate.cc index b820d3f39..d399c141d 100644 --- a/vhdlpp/architec_elaborate.cc +++ b/vhdlpp/architec_elaborate.cc @@ -117,6 +117,13 @@ int ForGenerate::elaborate(Entity*ent, Architecture*arc) return errors; } +int IfGenerate::elaborate(Entity*ent, Architecture*arc) +{ + int errors = 0; + errors += elaborate_statements(ent, arc); + return errors; +} + /* * This method attempts to rewrite the process content as an * always-@(n-edge ) version of the same statement. This makes diff --git a/vhdlpp/architec_emit.cc b/vhdlpp/architec_emit.cc index c2a855b61..78f3131f8 100644 --- a/vhdlpp/architec_emit.cc +++ b/vhdlpp/architec_emit.cc @@ -181,7 +181,7 @@ int GenerateStatement::emit_statements(ostream&out, Entity*ent, Architecture*arc int ForGenerate::emit(ostream&out, Entity*ent, Architecture*arc) { int errors = 0; - out << "generate genvar \\" << genvar_ << " ;" << endl; + out << "genvar \\" << genvar_ << " ;" << endl; out << "for (\\" << genvar_ << " = "; errors += lsb_->emit(out, ent, arc); out << "; \\" << genvar_ << " <= "; @@ -192,7 +192,21 @@ int ForGenerate::emit(ostream&out, Entity*ent, Architecture*arc) errors += emit_statements(out, ent, arc); out << "end" << endl; - out << "endgenerate" << endl; + + return errors; +} + +int IfGenerate::emit(ostream&out, Entity*ent, Architecture*arc) +{ + int errors = 0; + out << "if ("; + cond_->emit(out, ent, arc); + out << ") begin : \\" << get_name() << endl; + + errors += emit_statements(out, ent, arc); + + out << "end" << endl; + return errors; } diff --git a/vhdlpp/parse.y b/vhdlpp/parse.y index 844a7c307..81d71db3c 100644 --- a/vhdlpp/parse.y +++ b/vhdlpp/parse.y @@ -283,7 +283,8 @@ static list* record_elements(list*names, %type component_specification %type concurrent_statement component_instantiation_statement concurrent_signal_assignment_statement -%type for_generate_statement process_statement +%type for_generate_statement generate_statement if_generate_statement +%type process_statement %type architecture_statement_part generate_statement_body %type choice @@ -692,7 +693,7 @@ concurrent_signal_assignment_statement concurrent_statement : component_instantiation_statement | concurrent_signal_assignment_statement - | for_generate_statement + | generate_statement | process_statement ; @@ -1068,6 +1069,11 @@ for_generate_statement } ; +generate_statement /* IEEE 1076-2008 P11.8 */ + : if_generate_statement + | for_generate_statement + ; + generate_statement_body : architecture_statement_part { $$ = $1; } ; @@ -1123,6 +1129,29 @@ identifier_opt : IDENTIFIER { $$ = $1; } | { $$ = 0; } ; identifier_colon_opt : IDENTIFIER ':' { $$ = $1; } | { $$ = 0; }; + /* The if_generate_statement rule describes the if_generate syntax. + + NOTE: This does not yet implement the elsif and else parts of the + syntax. This shouldn't be hard, but is simply not done yet. */ +if_generate_statement /* IEEE 1076-2008 P11.8 */ + : IDENTIFIER ':' K_if expression + K_generate generate_statement_body + K_end K_generate identifier_opt ';' + { perm_string name = lex_strings.make($1); + IfGenerate*tmp = new IfGenerate(name, $4, *$6); + FILE_NAME(tmp, @3); + + if ($9 && name != $9) { + errormsg(@1, "if-generate name %s does not match closing name %s\n", + name.str(), $9); + } + delete[]$1; + delete $6; + delete[]$9; + $$ = tmp; + } + ; + if_statement : K_if expression K_then sequence_of_statements if_statement_elsif_list_opt if_statement_else From 9b816f6478497f89657a2bb39af8e6381bfbf7b9 Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Sat, 24 Mar 2012 10:23:50 -0700 Subject: [PATCH 012/179] Add support for nested when/else expressions. --- vhdlpp/debug.cc | 15 ++++++- vhdlpp/expression.cc | 24 ++++++++++-- vhdlpp/expression.h | 20 +++++++++- vhdlpp/expression_elaborate.cc | 17 +++++++- vhdlpp/expression_emit.cc | 59 ++++++++++++++++++++++++++-- vhdlpp/parse.y | 71 +++++++++++++++++++++++++++------- 6 files changed, 181 insertions(+), 25 deletions(-) diff --git a/vhdlpp/debug.cc b/vhdlpp/debug.cc index 1c2569139..520a3e1bc 100644 --- a/vhdlpp/debug.cc +++ b/vhdlpp/debug.cc @@ -292,11 +292,22 @@ void ExpConditional::dump(ostream&out, int indent) const (*cur)->dump(out, indent+4); } - out << setw(indent) << "" << " else:" << endl; - for (list::const_iterator cur = else_clause_.begin() + for (list::const_iterator cur = else_clause_.begin() ; cur != else_clause_.end() ; ++cur) { + (*cur)->dump(out, indent); + } +} + +void ExpConditional::else_t::dump(ostream&out, int indent) const +{ + out << setw(indent) << "" << "when:" << endl; + if (cond_) cond_->dump(out, indent+4); + out << setw(indent) << "" << "do:" << endl; + for (list::const_iterator cur = true_clause_.begin() + ; cur != true_clause_.end() ; ++cur) { (*cur)->dump(out, indent+4); } + } void ExpEdge::dump(ostream&out, int indent) const diff --git a/vhdlpp/expression.cc b/vhdlpp/expression.cc index ed767565c..1334df43e 100644 --- a/vhdlpp/expression.cc +++ b/vhdlpp/expression.cc @@ -256,11 +256,12 @@ ExpCharacter::~ExpCharacter() { } -ExpConditional::ExpConditional(Expression*co, list*tru, list*els) +ExpConditional::ExpConditional(Expression*co, list*tru, + list*fal) : cond_(co) { if (tru) true_clause_.splice(true_clause_.end(), *tru); - if (els) else_clause_.splice(else_clause_.end(), *els); + if (fal) else_clause_.splice(else_clause_.end(), *fal); } ExpConditional::~ExpConditional() @@ -272,12 +273,29 @@ ExpConditional::~ExpConditional() delete tmp; } while (! else_clause_.empty()) { - Expression*tmp = else_clause_.front(); + else_t*tmp = else_clause_.front(); else_clause_.pop_front(); delete tmp; } } +ExpConditional::else_t::else_t(Expression*cond, std::list*tru) +: cond_(cond) +{ + if (tru) true_clause_.splice(true_clause_.end(), *tru); +} + +ExpConditional::else_t::~else_t() +{ + delete cond_; + while (! true_clause_.empty()) { + Expression*tmp = true_clause_.front(); + true_clause_.pop_front(); + delete tmp; + } +} + + ExpEdge::ExpEdge(ExpEdge::fun_t typ, Expression*op) : ExpUnary(op), fun_(typ) { diff --git a/vhdlpp/expression.h b/vhdlpp/expression.h index fdc218652..f4b7ced26 100644 --- a/vhdlpp/expression.h +++ b/vhdlpp/expression.h @@ -339,9 +339,25 @@ class ExpCharacter : public Expression { */ class ExpConditional : public Expression { + public: + class else_t : public LineInfo { + public: + else_t(Expression*cond, std::list*tru); + ~else_t(); + + int elaborate_expr(Entity*ent, Architecture*arc, const VType*lt); + int emit_when_else(ostream&out, Entity*ent, Architecture*arc); + int emit_else(ostream&out, Entity*ent, Architecture*arc); + void dump(ostream&out, int indent = 0) const; + + private: + Expression*cond_; + std::list true_clause_; + }; + public: ExpConditional(Expression*cond, std::list*tru, - std::list*els); + std::list*fal); ~ExpConditional(); const VType*probe_type(Entity*ent, Architecture*arc) const; @@ -353,7 +369,7 @@ class ExpConditional : public Expression { private: Expression*cond_; std::list true_clause_; - std::list else_clause_; + std::list else_clause_; }; /* diff --git a/vhdlpp/expression_elaborate.cc b/vhdlpp/expression_elaborate.cc index fed635a5c..679f2fbb7 100644 --- a/vhdlpp/expression_elaborate.cc +++ b/vhdlpp/expression_elaborate.cc @@ -330,7 +330,7 @@ int ExpConditional::elaborate_expr(Entity*ent, Architecture*arc, const VType*lty errors += (*cur)->elaborate_expr(ent, arc, ltype); } - for (list::const_iterator cur = else_clause_.begin() + for (list::const_iterator cur = else_clause_.begin() ; cur != else_clause_.end() ; ++cur) { errors += (*cur)->elaborate_expr(ent, arc, ltype); } @@ -338,6 +338,21 @@ int ExpConditional::elaborate_expr(Entity*ent, Architecture*arc, const VType*lty return errors; } +int ExpConditional::else_t::elaborate_expr(Entity*ent, Architecture*arc, const VType*ltype) +{ + int errors = 0; + + if (cond_) + errors += cond_->elaborate_expr(ent, arc, 0); + + for (list::const_iterator cur = true_clause_.begin() + ; cur != true_clause_.end() ; ++cur) { + errors += (*cur)->elaborate_expr(ent, arc, ltype); + } + + return errors; +} + int ExpFunc::elaborate_expr(Entity*ent, Architecture*arc, const VType*) { int errors = 0; diff --git a/vhdlpp/expression_emit.cc b/vhdlpp/expression_emit.cc index 96e4dec4d..c5141c4f6 100644 --- a/vhdlpp/expression_emit.cc +++ b/vhdlpp/expression_emit.cc @@ -328,7 +328,7 @@ int ExpConditional::emit(ostream&out, Entity*ent, Architecture*arc) out << ")? ("; if (true_clause_.size() > 1) { - cerr << get_fileline() << ": sorry: Multiple expressions not supported here." << endl; + cerr << get_fileline() << ": sorry: Multiple expression waveforms not supported here." << endl; errors += 1; } @@ -337,15 +337,66 @@ int ExpConditional::emit(ostream&out, Entity*ent, Architecture*arc) out << ") : ("; + // Draw out any when-else expressions. These are all the else_ + // clauses besides the last. if (else_clause_.size() > 1) { - cerr << get_fileline() << ": sorry: Multiple expressions not supported here." << endl; + list::iterator last = else_clause_.end(); + -- last; + + for (list::iterator cur = else_clause_.begin() + ; cur != last ; ++cur) { + errors += (*cur) ->emit_when_else(out, ent, arc); + } + } + + errors += else_clause_.back()->emit_else(out, ent, arc); + out << ")"; + + // The emit_when_else() functions do not close the last + // parentheses so that the following expression can be + // nested. But that means come the end, we have some + // expressions to close. + for (size_t idx = 1 ; idx < else_clause_.size() ; idx += 1) + out << ")"; + + return errors; +} + +int ExpConditional::else_t::emit_when_else(ostream&out, Entity*ent, Architecture*arc) +{ + int errors = 0; + assert(cond_ != 0); + + out << "("; + errors += cond_->emit(out, ent, arc); + out << ")? ("; + + if (true_clause_.size() > 1) { + cerr << get_fileline() << ": sorry: Multiple expression waveforms not supported here." << endl; errors += 1; } - tmp = else_clause_.front(); + Expression*tmp = true_clause_.front(); errors += tmp->emit(out, ent, arc); - out << ")"; + out << ") : ("; + + return errors; +} + +int ExpConditional::else_t::emit_else(ostream&out, Entity*ent, Architecture*arc) +{ + int errors = 0; + // Trailing else must have no condition. + assert(cond_ == 0); + + if (true_clause_.size() > 1) { + cerr << get_fileline() << ": sorry: Multiple expression waveforms not supported here." << endl; + errors += 1; + } + + Expression*tmp = true_clause_.front(); + errors += tmp->emit(out, ent, arc); return errors; } diff --git a/vhdlpp/parse.y b/vhdlpp/parse.y index 81d71db3c..f2d26ffdf 100644 --- a/vhdlpp/parse.y +++ b/vhdlpp/parse.y @@ -205,6 +205,9 @@ static list* record_elements(list*names, IfSequential::Elsif*elsif; std::list*elsif_list; + ExpConditional::else_t*exp_else; + std::list*exp_else_list; + CaseSeqStmt::CaseStmtAlternative* case_alt; std::list* case_alt_list; @@ -282,7 +285,9 @@ static list* record_elements(list*names, %type instantiation_list %type component_specification -%type concurrent_statement component_instantiation_statement concurrent_signal_assignment_statement +%type concurrent_statement component_instantiation_statement +%type concurrent_conditional_signal_assignment +%type concurrent_signal_assignment_statement %type for_generate_statement generate_statement if_generate_statement %type process_statement %type architecture_statement_part generate_statement_body @@ -334,6 +339,9 @@ static list* record_elements(list*names, %type if_statement_elsif %type if_statement_elsif_list if_statement_elsif_list_opt +%type else_when_waveform +%type else_when_waveforms + %% /* The design_file is the root for the VHDL parse. This rule is also @@ -653,7 +661,54 @@ composite_type_definition { $$ = $1; } ; -concurrent_signal_assignment_statement + + /* The when...else..when...else syntax is not a general expression + in VHDL but a specific sort of assignment statement model. We + create Exppression objects for it, but the parser will only + recognize it it in specific situations. */ +concurrent_conditional_signal_assignment /* IEEE 1076-2008 P11.6 */ + : name LEQ waveform K_when expression else_when_waveforms ';' + { ExpConditional*tmp = new ExpConditional($5, $3, $6); + FILE_NAME(tmp, @3); + delete $3; + delete $6; + + ExpName*name = dynamic_cast ($1); + assert(name); + SignalAssignment*tmpa = new SignalAssignment(name, tmp); + FILE_NAME(tmpa, @1); + + $$ = tmpa; + } + ; + +else_when_waveforms + : else_when_waveforms else_when_waveform + { list*tmp = $1; + tmp ->push_back($2); + $$ = tmp; + } + | else_when_waveform + { list*tmp = new list; + tmp->push_back($1); + $$ = tmp; + } + ; + +else_when_waveform + : K_else waveform K_when expression + { ExpConditional::else_t*tmp = new ExpConditional::else_t($4, $2); + FILE_NAME(tmp, @1); + $$ = tmp; + } + | K_else waveform + { ExpConditional::else_t*tmp = new ExpConditional::else_t(0, $2); + FILE_NAME(tmp, @1); + $$ = tmp; + } + ; + +concurrent_signal_assignment_statement /* IEEE 1076-2008 P11.6 */ : name LEQ waveform ';' { ExpName*name = dynamic_cast ($1); assert(name); @@ -663,19 +718,9 @@ concurrent_signal_assignment_statement $$ = tmp; delete $3; } - | name LEQ waveform K_when expression K_else waveform ';' - { ExpConditional*tmp = new ExpConditional($5, $3, $7); - FILE_NAME(tmp, @3); - delete $3; - delete $7; - ExpName*name = dynamic_cast ($1); - assert(name); - SignalAssignment*tmpa = new SignalAssignment(name, tmp); - FILE_NAME(tmpa, @1); + | concurrent_conditional_signal_assignment - $$ = tmpa; - } | name LEQ error ';' { errormsg(@2, "Syntax error in signal assignment waveform.\n"); delete $1; From 78b0b49a4eed6a3c26f58acea8318ec6ebb4d02f Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Sun, 25 Mar 2012 17:59:05 -0700 Subject: [PATCH 013/179] Support struct members that are packed arrays. --- Makefile.in | 3 +- design_dump.cc | 4 +- elab_expr.cc | 61 ++++++++++++++++++++++------- elab_lval.cc | 61 ++++++++++++++++++++++++++--- elab_sig.cc | 102 ++++++++++++++++++++++--------------------------- netlist.cc | 77 +++++-------------------------------- netlist.h | 33 ++++------------ netmisc.cc | 16 ++++---- netmisc.h | 2 +- netstruct.h | 11 ++---- nettypes.cc | 96 ++++++++++++++++++++++++++++++++++++++++++++++ nettypes.h | 55 ++++++++++++++++++++++++++ t-dll.cc | 2 +- t-dll.h | 2 +- 14 files changed, 335 insertions(+), 190 deletions(-) create mode 100644 nettypes.cc create mode 100644 nettypes.h diff --git a/Makefile.in b/Makefile.in index 7bd62d4f1..57500f88e 100644 --- a/Makefile.in +++ b/Makefile.in @@ -106,7 +106,8 @@ O = main.o async.o design_dump.o discipline.o dup_expr.o elaborate.o \ elab_expr.o elaborate_analog.o elab_lval.o elab_net.o \ elab_scope.o elab_sig.o elab_sig_analog.o emit.o eval.o eval_attrib.o \ eval_tree.o expr_synth.o functor.o lexor.o lexor_keyword.o link_const.o \ - load_module.o netlist.o netmisc.o net_analog.o net_assign.o net_design.o \ + load_module.o netlist.o netmisc.o nettypes.o net_analog.o net_assign.o \ + net_design.o \ netenum.o netstruct.o net_event.o net_expr.o net_func.o net_link.o net_modulo.o \ net_nex_input.o net_nex_output.o net_proc.o net_scope.o net_tran.o \ net_udp.o pad_to_width.o parse.o parse_misc.o pform.o pform_analog.o \ diff --git a/design_dump.cc b/design_dump.cc index 2ded152a2..f42676183 100644 --- a/design_dump.cc +++ b/design_dump.cc @@ -187,9 +187,9 @@ void NetDelaySrc::dump(ostream&o, unsigned ind) const dump_node_pins(o, ind+4); } -ostream&operator<<(ostream&out, const list&rlist) +ostream&operator<<(ostream&out, const list&rlist) { - for (list::const_iterator cur = rlist.begin() + for (list::const_iterator cur = rlist.begin() ; cur != rlist.end() ; ++cur) { out << "[" << cur->msb << ":" << cur->lsb << "]"; } diff --git a/elab_expr.cc b/elab_expr.cc index 213a580db..f0b6a2896 100644 --- a/elab_expr.cc +++ b/elab_expr.cc @@ -1442,24 +1442,59 @@ static const netstruct_t::member_t*get_struct_member(const LineInfo*li, return type->packed_member(method_name, off); } - +/* + * Test if the tail name (method_name argument) is a member name and + * the net is a struct. If that turns out to be the case, and the + * struct is packed, then return a NetExpr that selects the member out + * of the variable. + */ static NetExpr* check_for_struct_members(const LineInfo*li, - Design*des, NetScope*, - NetNet*net, perm_string method_name) + Design*des, NetScope*scope, + NetNet*net, const name_component_t&comp) { unsigned long off; const netstruct_t::member_t*mem = get_struct_member(li, des, 0, net, - method_name, off); + comp.name, off); if (mem == 0) return 0; if (debug_elaborate) { - cerr << li->get_fileline() << ": debug: Found struct member " <name + cerr << li->get_fileline() << ": debug: Found struct member " << mem->name << " At offset " << off << endl; } + unsigned use_width = mem->width(); + + if ( ! comp.index.empty() ) { + // Evaluate all but the last index expression, into prefix_indices. + listprefix_indices; + bool rc = evaluate_index_prefix(des, scope, prefix_indices, comp.index); + ivl_assert(*li, rc); + + // Evaluate the last index expression into a constant long. + NetExpr*texpr = elab_and_eval(des, scope, comp.index.back().msb, -1, true); + long tmp; + if (texpr == 0 || !eval_as_long(tmp, texpr)) { + cerr << li->get_fileline() << ": error: " + "Array index expressions must be constant here." << endl; + des->errors += 1; + return false; + } + + delete texpr; + + // Now use the prefix_to_slice function to calculate the + // offset and width of the addressed slice of the member. + long loff; + unsigned long lwid; + prefix_to_slice(mem->packed_dims, prefix_indices, tmp, loff, lwid); + + off += loff; + use_width = lwid; + } + NetESignal*sig = new NetESignal(net); NetEConst*base = make_const_val(off); - NetESelect*sel = new NetESelect(sig, base, mem->width()); + NetESelect*sel = new NetESelect(sig, base, use_width); return sel; } @@ -2350,7 +2385,7 @@ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope, // really a method attached to an object. if (gn_system_verilog() && found_in==0 && path_.size() >= 2) { pform_name_t use_path = path_; - perm_string method_name = peek_tail_name(use_path); + name_component_t member_comp = use_path.back(); use_path.pop_back(); found_in = symbol_search(this, des, scope, use_path, @@ -2372,7 +2407,7 @@ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope, return check_for_enum_methods(this, des, scope, netenum, - use_path, method_name, + use_path, member_comp.name, expr, expr_wid, NULL, 0); } @@ -2382,7 +2417,7 @@ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope, ivl_assert(*this, use_path.back().index.empty()); return check_for_struct_members(this, des, scope, - net, method_name); + net, member_comp); } } @@ -3279,11 +3314,11 @@ NetExpr* PEIdent::elaborate_expr_net_idx_up_(Design*des, NetScope*scope, long lsv = base_c->value().as_long(); long offset = 0; // Get the signal range. - const list&packed = net->sig()->packed_dims(); + const list&packed = net->sig()->packed_dims(); ivl_assert(*this, packed.size() == prefix_indices.size()+1); // We want the last range, which is where we work. - const NetNet::range_t&rng = packed.back(); + const netrange_t&rng = packed.back(); if (rng.msb < rng.lsb) { offset = -wid + 1; } @@ -3494,7 +3529,7 @@ NetExpr* PEIdent::elaborate_expr_net_bit_(Design*des, NetScope*scope, long msv = msc->value().as_long(); - const list& sig_packed = net->sig()->packed_dims(); + const list& sig_packed = net->sig()->packed_dims(); if (prefix_indices.size()+2 <= sig_packed.size()) { // Special case: this is a slice of a multi-dimensional // packed array. For example: @@ -3561,7 +3596,7 @@ NetExpr* PEIdent::elaborate_expr_net_bit_(Design*des, NetScope*scope, return res; } - const list& sig_packed = net->sig()->packed_dims(); + const list& sig_packed = net->sig()->packed_dims(); if (prefix_indices.size()+2 <= sig_packed.size()) { // Special case: this is a slice of a multi-dimensional // packed array. For example: diff --git a/elab_lval.cc b/elab_lval.cc index 2cfb98016..bed52b95a 100644 --- a/elab_lval.cc +++ b/elab_lval.cc @@ -455,7 +455,7 @@ bool PEIdent::elaborate_lval_net_part_(Design*des, NetNet*reg = lv->sig(); ivl_assert(*this, reg); - const list&packed = reg->packed_dims(); + const list&packed = reg->packed_dims(); // Part selects cannot select slices. So there must be enough // prefix_indices to get all the way to the final dimension. @@ -544,11 +544,11 @@ bool PEIdent::elaborate_lval_net_idx_(Design*des, long lsv = base_c->value().as_long(); long offset = 0; // Get the signal range. - const list&packed = reg->packed_dims(); + const list&packed = reg->packed_dims(); ivl_assert(*this, packed.size() == prefix_indices.size()+1); // We want the last range, which is where we work. - const NetNet::range_t&rng = packed.back(); + const netrange_t&rng = packed.back(); if (((rng.msb < rng.lsb) && use_sel == index_component_t::SEL_IDX_UP) || ((rng.msb > rng.lsb) && @@ -621,8 +621,7 @@ bool PEIdent::elaborate_lval_net_idx_(Design*des, return true; } -bool PEIdent::elaborate_lval_net_packed_member_(Design*des, - NetScope*, +bool PEIdent::elaborate_lval_net_packed_member_(Design*des, NetScope*scope, NetAssign_*lv, const perm_string&member_name) const { @@ -649,7 +648,57 @@ bool PEIdent::elaborate_lval_net_packed_member_(Design*des, return false; } - lv->set_part(new NetEConst(verinum(off)), member->width()); + unsigned long use_width = member->width(); + + // We are processing the tail of a string of names. For + // example, the verilog may be "a.b.c", so we are processing + // "c" at this point. Of course, "c" is the name of the member + // we are working on and "a.b" is the name of reg. + const name_component_t&name_tail = path_.back(); + + if (name_tail.index.size() > member->packed_dims.size()) { + cerr << get_fileline() << ": error: Too make index expressions for member." << endl; + des->errors += 1; + return false; + } + + // Get the index component type. At this point, we only + // support bit select or none. + index_component_t::ctype_t use_sel = index_component_t::SEL_NONE; + if (!name_tail.index.empty()) + use_sel = name_tail.index.back().sel; + + ivl_assert(*this, use_sel == index_component_t::SEL_NONE || use_sel == index_component_t::SEL_BIT); + + if (name_tail.index.size() > 0) { + // Evaluate all but the last index expression, into prefix_indices. + listprefix_indices; + bool rc = evaluate_index_prefix(des, scope, prefix_indices, name_tail.index); + ivl_assert(*this, rc); + + // Evaluate the last index expression into a constant long. + NetExpr*texpr = elab_and_eval(des, scope, name_tail.index.back().msb, -1, true); + long tmp; + if (texpr == 0 || !eval_as_long(tmp, texpr)) { + cerr << get_fileline() << ": error: " + "Array index expressions must be constant here." << endl; + des->errors += 1; + return false; + } + + delete texpr; + + // Now use the prefix_to_slice function to calculate the + // offset and width of the addressed slice of the member. + long loff; + unsigned long lwid; + prefix_to_slice(member->packed_dims, prefix_indices, tmp, loff, lwid); + + off += loff; + use_width = lwid; + } + + lv->set_part(new NetEConst(verinum(off)), use_width); return true; } diff --git a/elab_sig.cc b/elab_sig.cc index f5ba9d4ca..93b6aa06b 100644 --- a/elab_sig.cc +++ b/elab_sig.cc @@ -514,8 +514,8 @@ void PFunction::elaborate_sig(Design*des, NetScope*scope) const des->errors += 1; } - list packed; - packed.push_back(NetNet::range_t(mnum, lnum)); + list packed; + packed.push_back(netrange_t(mnum, lnum)); ret_sig = new NetNet(scope, fname, NetNet::REG, packed); ret_sig->set_scalar(false); @@ -813,61 +813,15 @@ void PWhile::elaborate_sig(Design*des, NetScope*scope) const statement_->elaborate_sig(des, scope); } -static netstruct_t* elaborate_struct_type(Design*des, NetScope*scope, - struct_type_t*struct_type) -{ - netstruct_t*res = new netstruct_t; - - res->packed(struct_type->packed_flag); - - for (list::iterator cur = struct_type->members->begin() - ; cur != struct_type->members->end() ; ++ cur) { - - struct_member_t*curp = *cur; - long use_msb = 0; - long use_lsb = 0; - if (curp->range.get() && ! curp->range->empty()) { - ivl_assert(*curp, curp->range->size() == 1); - pform_range_t&rangep = curp->range->front(); - PExpr*msb_pex = rangep.first; - PExpr*lsb_pex = rangep.second; - - NetExpr*tmp = elab_and_eval(des, scope, msb_pex, -2, true); - ivl_assert(*curp, tmp); - bool rc = eval_as_long(use_msb, tmp); - ivl_assert(*curp, rc); - - tmp = elab_and_eval(des, scope, lsb_pex, -2, true); - ivl_assert(*curp, tmp); - rc = eval_as_long(use_lsb, tmp); - ivl_assert(*curp, rc); - } - - for (list::iterator name = curp->names->begin() - ; name != curp->names->end() ; ++ name) { - decl_assignment_t*namep = *name; - - netstruct_t::member_t memb; - memb.name = namep->name; - memb.type = curp->type; - memb.msb = use_msb; - memb.lsb = use_lsb; - res->append_member(memb); - } - } - - return res; -} - static bool evaluate_ranges(Design*des, NetScope*scope, - list&llist, + list&llist, const list&rlist) { bool bad_msb = false, bad_lsb = false; for (list::const_iterator cur = rlist.begin() ; cur != rlist.end() ; ++cur) { - NetNet::range_t lrng; + netrange_t lrng; NetExpr*texpr = elab_and_eval(des, scope, cur->first, -1, true); if (! eval_as_long(lrng.msb, texpr)) { @@ -901,13 +855,49 @@ static bool evaluate_ranges(Design*des, NetScope*scope, return bad_msb | bad_lsb; } -bool test_ranges_eeq(const list&lef, const list&rig) +static netstruct_t* elaborate_struct_type(Design*des, NetScope*scope, + struct_type_t*struct_type) +{ + netstruct_t*res = new netstruct_t; + + res->packed(struct_type->packed_flag); + + for (list::iterator cur = struct_type->members->begin() + ; cur != struct_type->members->end() ; ++ cur) { + + listpacked_dimensions; + + struct_member_t*curp = *cur; + if (curp->range.get() && ! curp->range->empty()) { + bool bad_range; + bad_range = evaluate_ranges(des, scope, packed_dimensions, *curp->range); + ivl_assert(*curp, !bad_range); + } else { + packed_dimensions.push_back(netrange_t(0,0)); + } + + for (list::iterator name = curp->names->begin() + ; name != curp->names->end() ; ++ name) { + decl_assignment_t*namep = *name; + + netstruct_t::member_t memb; + memb.name = namep->name; + memb.type = curp->type; + memb.packed_dims = packed_dimensions; + res->append_member(memb); + } + } + + return res; +} + +bool test_ranges_eeq(const list&lef, const list&rig) { if (lef.size() != rig.size()) return false; - list::const_iterator lcur = lef.begin(); - list::const_iterator rcur = rig.begin(); + list::const_iterator lcur = lef.begin(); + list::const_iterator rcur = rig.begin(); while (lcur != lef.end()) { if (lcur->msb != rcur->msb) return false; @@ -942,7 +932,7 @@ NetNet* PWire::elaborate_sig(Design*des, NetScope*scope) const } unsigned wid = 1; - listpacked_dimensions; + listpacked_dimensions; des->errors += error_cnt_; @@ -983,7 +973,7 @@ NetNet* PWire::elaborate_sig(Design*des, NetScope*scope) const if (port_set_ || net_set_) { bool bad_range = false; - list plist, nlist; + list plist, nlist; /* If they exist get the port definition MSB and LSB */ if (port_set_ && !port_.empty()) { bad_range |= evaluate_ranges(des, scope, plist, port_); @@ -1049,7 +1039,7 @@ NetNet* PWire::elaborate_sig(Design*des, NetScope*scope) const } packed_dimensions = nlist; - wid = NetNet::vector_width(packed_dimensions); + wid = netrange_width(packed_dimensions); } diff --git a/netlist.cc b/netlist.cc index ef0b6c1a6..8dec0c40b 100644 --- a/netlist.cc +++ b/netlist.cc @@ -458,7 +458,7 @@ NetNet::NetNet(NetScope*s, perm_string n, Type t, unsigned npins) assert(npins>0); // Synthesize a single range to describe this canonical vector. - packed_dims_.push_back(NetNet::range_t(npins-1, 0)); + packed_dims_.push_back(netrange_t(npins-1, 0)); Link::DIR dir = Link::PASSIVE; @@ -496,7 +496,7 @@ void NetNet::initialize_dir_(Link::DIR dir) } NetNet::NetNet(NetScope*s, perm_string n, Type t, - const list&packed) + const list&packed) : NetObj(s, n, 1), type_(t), port_type_(NOT_A_PORT), data_type_(IVL_VT_NO_TYPE), signed_(false), isint_(false), is_scalar_(false), local_flag_(false), @@ -544,7 +544,7 @@ static unsigned calculate_count(long s, long e) } NetNet::NetNet(NetScope*s, perm_string n, Type t, - const list&packed, long array_s, long array_e) + const list&packed, long array_s, long array_e) : NetObj(s, n, calculate_count(array_s, array_e)), type_(t), port_type_(NOT_A_PORT), data_type_(IVL_VT_NO_TYPE), signed_(false), isint_(false), @@ -605,7 +605,7 @@ NetNet::NetNet(NetScope*s, perm_string n, Type t, netstruct_t*ty) dimensions_(0), s0_(0), e0_(0), eref_count_(0), lref_count_(0) { - packed_dims_.push_back(range_t(calculate_count(ty)-1, 0)); + packed_dims_.push_back(netrange_t(calculate_count(ty)-1, 0)); Link::DIR dir = Link::PASSIVE; switch (t) { @@ -764,27 +764,11 @@ void NetNet::set_discipline(ivl_discipline_t dis) discipline_ = dis; } -unsigned long NetNet::vector_width(const list&packed) -{ - unsigned wid = 1; - for (list::const_iterator cur = packed.begin() - ; cur != packed.end() ; ++cur) { - unsigned use_wid; - if (cur->msb >= cur->lsb) - use_wid = cur->msb - cur->lsb + 1; - else - use_wid = cur->lsb - cur->msb + 1; - wid *= use_wid; - } - - return wid; -} - bool NetNet::sb_is_valid(const list&indices, long sb) const { ivl_assert(*this, indices.size()+1 == packed_dims_.size()); assert(packed_dims_.size() == 1); - const range_t&rng = packed_dims_.back(); + const netrange_t&rng = packed_dims_.back(); if (rng.msb >= rng.lsb) return (sb <= rng.msb) && (sb >= rng.lsb); else @@ -795,7 +779,7 @@ long NetNet::sb_to_idx(const list&indices, long sb) const { ivl_assert(*this, indices.size()+1 == packed_dims_.size()); - list::const_iterator pcur = packed_dims_.end(); + list::const_iterator pcur = packed_dims_.end(); -- pcur; long acc_off; @@ -832,50 +816,7 @@ long NetNet::sb_to_idx(const list&indices, long sb) const bool NetNet::sb_to_slice(const list&indices, long sb, long&loff, unsigned long&lwid) const { ivl_assert(*this, indices.size() < packed_dims_.size()); - - size_t acc_wid = 1; - list::const_iterator pcur = packed_dims_.end(); - for (size_t idx = indices.size()+1 ; idx < packed_dims_.size() ; idx += 1) { - -- pcur; - acc_wid *= pcur->width(); - } - - lwid = acc_wid; - - -- pcur; - if (sb < pcur->msb && sb < pcur->lsb) - return false; - if (sb > pcur->msb && sb > pcur->lsb) - return false; - - long acc_off = 0; - if (pcur->msb >= pcur->lsb) - acc_off += (sb - pcur->lsb) * acc_wid; - else - acc_off += (sb - pcur->msb) * acc_wid; - - if (indices.size() == 0) { - loff = acc_off; - return true; - } - - lwid *= pcur->width(); - - list::const_iterator icur = indices.end(); - do { - -- pcur; - -- icur; - acc_wid *= pcur->width(); - if (pcur->msb >= pcur->lsb) - acc_off += (*icur - pcur->lsb) * acc_wid; - else - acc_off += (*icur - pcur->msb) * acc_wid; - - } while (icur != indices.begin()); - - loff = acc_off; - - return true; + return prefix_to_slice(packed_dims_, indices, sb, loff, lwid); } @@ -2461,14 +2402,14 @@ NetNet* NetESignal::sig() */ long NetESignal::lsi() const { - const list&packed = net_->packed_dims(); + const list&packed = net_->packed_dims(); ivl_assert(*this, packed.size() == 1); return packed.back().lsb; } long NetESignal::msi() const { - const list&packed = net_->packed_dims(); + const list&packed = net_->packed_dims(); ivl_assert(*this, packed.size() == 1); return packed.back().msb; } diff --git a/netlist.h b/netlist.h index ba9ff80e3..0300f5cc8 100644 --- a/netlist.h +++ b/netlist.h @@ -35,6 +35,7 @@ # include "ivl_target_priv.h" # include "pform_types.h" # include "config.h" +# include "nettypes.h" # include "verinum.h" # include "verireal.h" # include "StringHeap.h" @@ -563,21 +564,6 @@ class NetNet : public NetObj { enum PortType { NOT_A_PORT, PIMPLICIT, PINPUT, POUTPUT, PINOUT, PREF }; - struct range_t { - inline range_t() : msb(0), lsb(0) { } - inline range_t(long m, long l) : msb(m), lsb(l) { } - inline range_t(const range_t&that) - : msb(that.msb), lsb(that.lsb) { } - inline range_t& operator = (const range_t&that) - { msb = that.msb; lsb = that.lsb; return *this; } - - long msb; - long lsb; - - inline unsigned long width()const - { if (msb >= lsb) return msb-lsb+1; else return lsb-msb+1; } - }; - public: // The width in this case is a shorthand for ms=width-1 and // ls=0. Only one pin is created, the width is of the vector @@ -589,9 +575,9 @@ class NetNet : public NetObj { // dimensions. If s0==e0, then this is not an array after // all. explicit NetNet(NetScope*s, perm_string n, Type t, - const std::list&packed); + const std::list&packed); explicit NetNet(NetScope*s, perm_string n, Type t, - const std::list&packed, long s0, long e0); + const std::list&packed, long s0, long e0); // This form builds a NetNet from its record definition. explicit NetNet(NetScope*s, perm_string n, Type t, netstruct_t*type); @@ -633,14 +619,11 @@ class NetNet : public NetObj { for the vector. These are arranged as a list where the first range in the list (front) is the left-most range in the verilog declaration. */ - const std::list& packed_dims() const { return packed_dims_; } + const std::list& packed_dims() const { return packed_dims_; } /* The vector_width returns the bit width of the packed array, - vector or scaler that is this NetNet object. The static - method is also a convenient way to convert a range list to - a vector width. */ - static unsigned long vector_width(const std::list&); - unsigned long vector_width() const { return vector_width(packed_dims_); } + vector or scaler that is this NetNet object. */ + unsigned long vector_width() const { return netrange_width(packed_dims_); } /* Given a prefix of indices, figure out how wide the resulting slice would be. This is a generalization of the @@ -722,7 +705,7 @@ class NetNet : public NetObj { netstruct_t*struct_type_; ivl_discipline_t discipline_; - std::list packed_dims_; + std::list packed_dims_; const unsigned dimensions_; long s0_, e0_; @@ -736,7 +719,7 @@ class NetNet : public NetObj { vector delay_paths_; }; -extern std::ostream&operator << (std::ostream&out, const std::list&rlist); +extern std::ostream&operator << (std::ostream&out, const std::list&rlist); /* * This object type is used to contain a logical scope within a diff --git a/netmisc.cc b/netmisc.cc index 9ce4ebf00..06b8d0a3d 100644 --- a/netmisc.cc +++ b/netmisc.cc @@ -320,24 +320,24 @@ NetExpr *normalize_variable_base(NetExpr *base, long msb, long lsb, * vector. For now, we assert that there is only one set of dimensions. */ NetExpr *normalize_variable_base(NetExpr *base, - const list&dims, + const list&dims, unsigned long wid, bool is_up) { ivl_assert(*base, dims.size() == 1); - const NetNet::range_t&rng = dims.back(); + const netrange_t&rng = dims.back(); return normalize_variable_base(base, rng.msb, rng.lsb, wid, is_up); } NetExpr *normalize_variable_bit_base(const list&indices, NetExpr*base, const NetNet*reg) { - const list&packed_dims = reg->packed_dims(); + const list&packed_dims = reg->packed_dims(); ivl_assert(*base, indices.size()+1 == packed_dims.size()); // Get the canonical offset of the slice within which we are // addressing. We need that address as a slice offset to // calculate the proper complete address - const NetNet::range_t&rng = packed_dims.back(); + const netrange_t&rng = packed_dims.back(); long slice_off = reg->sb_to_idx(indices, rng.lsb); return normalize_variable_base(base, rng.msb, rng.lsb, 1, true, slice_off); @@ -347,13 +347,13 @@ NetExpr *normalize_variable_part_base(const list&indices, NetExpr*base, const NetNet*reg, unsigned long wid, bool is_up) { - const list&packed_dims = reg->packed_dims(); + const list&packed_dims = reg->packed_dims(); ivl_assert(*base, indices.size()+1 == packed_dims.size()); // Get the canonical offset of the slice within which we are // addressing. We need that address as a slice offset to // calculate the proper complete address - const NetNet::range_t&rng = packed_dims.back(); + const netrange_t&rng = packed_dims.back(); long slice_off = reg->sb_to_idx(indices, rng.lsb); return normalize_variable_base(base, rng.msb, rng.lsb, wid, is_up, slice_off); @@ -362,10 +362,10 @@ NetExpr *normalize_variable_part_base(const list&indices, NetExpr*base, NetExpr *normalize_variable_slice_base(const list&indices, NetExpr*base, const NetNet*reg, unsigned long&lwid) { - const list&packed_dims = reg->packed_dims(); + const list&packed_dims = reg->packed_dims(); ivl_assert(*base, indices.size() < packed_dims.size()); - list::const_iterator pcur = packed_dims.end(); + list::const_iterator pcur = packed_dims.end(); for (size_t idx = indices.size() ; idx < packed_dims.size(); idx += 1) { -- pcur; } diff --git a/netmisc.h b/netmisc.h index 0504db472..f9104910f 100644 --- a/netmisc.h +++ b/netmisc.h @@ -102,7 +102,7 @@ extern NetExpr*normalize_variable_base(NetExpr *base, long msb, long lsb, unsigned long wid, bool is_up, long slice_off =0); extern NetExpr*normalize_variable_base(NetExpr *base, - const list&dims, + const list&dims, unsigned long wid, bool is_up); /* diff --git a/netstruct.h b/netstruct.h index b84df6a4a..0ffb2d55c 100644 --- a/netstruct.h +++ b/netstruct.h @@ -22,6 +22,7 @@ # include "LineInfo.h" # include # include "ivl_target.h" +# include "nettypes.h" class netstruct_t : public LineInfo { @@ -29,8 +30,7 @@ class netstruct_t : public LineInfo { struct member_t { perm_string name; ivl_variable_type_t type; - long msb; - long lsb; + list packed_dims; long width() const; ivl_variable_type_t data_type() const { return type; }; // We need to keep the individual element sign information. @@ -63,11 +63,6 @@ class netstruct_t : public LineInfo { inline bool netstruct_t::packed(void) const { return packed_; } inline long netstruct_t::member_t::width() const -{ - if (msb >= lsb) - return msb - lsb + 1; - else - return lsb - msb + 1; -} +{ return netrange_width(packed_dims); } #endif diff --git a/nettypes.cc b/nettypes.cc new file mode 100644 index 000000000..bec698378 --- /dev/null +++ b/nettypes.cc @@ -0,0 +1,96 @@ +/* + * Copyright (c) 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 + * General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +# include "nettypes.h" +# include + +using namespace std; + +unsigned long netrange_width(const list&packed) +{ + unsigned wid = 1; + for (list::const_iterator cur = packed.begin() + ; cur != packed.end() ; ++cur) { + unsigned use_wid; + if (cur->msb >= cur->lsb) + use_wid = cur->msb - cur->lsb + 1; + else + use_wid = cur->lsb - cur->msb + 1; + wid *= use_wid; + } + + return wid; +} + +/* + * Given a netrange_t list (which represent packed dimensions) and a + * prefix of calculated index values, calculate the canonical offset + * and width of the resulting slice. In this case, the "sb" argument + * is an extra index of the prefix. + */ +bool prefix_to_slice(const std::list&dims, + const std::list&prefix, long sb, + long&loff, unsigned long&lwid) +{ + assert(prefix.size() < dims.size()); + + size_t acc_wid = 1; + list::const_iterator pcur = dims.end(); + for (size_t idx = prefix.size()+1 ; idx < dims.size() ; idx += 1) { + -- pcur; + acc_wid *= pcur->width(); + } + + lwid = acc_wid; + + -- pcur; + if (sb < pcur->msb && sb < pcur->lsb) + return false; + if (sb > pcur->msb && sb > pcur->lsb) + return false; + + long acc_off = 0; + if (pcur->msb >= pcur->lsb) + acc_off += (sb - pcur->lsb) * acc_wid; + else + acc_off += (sb - pcur->msb) * acc_wid; + + if (prefix.size() == 0) { + loff = acc_off; + return true; + } + + lwid *= pcur->width(); + + list::const_iterator icur = prefix.end(); + do { + -- pcur; + -- icur; + acc_wid *= pcur->width(); + if (pcur->msb >= pcur->lsb) + acc_off += (*icur - pcur->lsb) * acc_wid; + else + acc_off += (*icur - pcur->msb) * acc_wid; + + } while (icur != prefix.begin()); + + loff = acc_off; + + return true; +} diff --git a/nettypes.h b/nettypes.h new file mode 100644 index 000000000..6c1c5e60b --- /dev/null +++ b/nettypes.h @@ -0,0 +1,55 @@ +#ifndef __nettypes_H +#define __nettypes_H +/* + * Copyright (c) 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 + * General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +# include + +class netrange_t { + + public: + inline netrange_t() : msb(0), lsb(0) { } + inline netrange_t(long m, long l) : msb(m), lsb(l) { } + + inline netrange_t(const netrange_t&that) + : msb(that.msb), lsb(that.lsb) { } + + inline netrange_t& operator = (const netrange_t&that) + { msb = that.msb; lsb = that.lsb; return *this; } + + public: + long msb; + long lsb; + + inline unsigned long width()const + { if (msb >= lsb) return msb-lsb+1; else return lsb-msb+1; } +}; + +extern unsigned long netrange_width(const std::list&dims); + +/* + * Take as input a list of packed dimensions and a list of prefix + * indices, and calculate the offset/width of the resulting slice into + * the packed array. + */ +extern bool prefix_to_slice(const std::list&dims, + const std::list&prefix, long sb, + long&loff, unsigned long&lwid); + +#endif diff --git a/t-dll.cc b/t-dll.cc index e16f8477f..2381d1202 100644 --- a/t-dll.cc +++ b/t-dll.cc @@ -2387,7 +2387,7 @@ void dll_target::signal(const NetNet*net) ivl_signal_t object. */ { size_t idx = 0; - list::const_iterator cur; + list::const_iterator cur; obj->packed_dims.resize(net->packed_dims().size()); for (cur = net->packed_dims().begin(), idx = 0 ; cur != net->packed_dims().end() ; ++cur, idx += 1) { diff --git a/t-dll.h b/t-dll.h index 2bdae36bb..9b64f2831 100644 --- a/t-dll.h +++ b/t-dll.h @@ -690,7 +690,7 @@ struct ivl_signal_s { /* These encode the declared packed dimensions for the signal, in case they are needed by the run-time */ - std::vector packed_dims; + std::vector packed_dims; perm_string name_; ivl_scope_t scope_; From 5e7f61ea42b916d1e729f05649f01d60196afd50 Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Sat, 31 Mar 2012 10:42:35 -0700 Subject: [PATCH 014/179] VHDL process sensitivities go to the end of each iteration --- vhdlpp/architec_emit.cc | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/vhdlpp/architec_emit.cc b/vhdlpp/architec_emit.cc index 78f3131f8..bf19c7bf2 100644 --- a/vhdlpp/architec_emit.cc +++ b/vhdlpp/architec_emit.cc @@ -210,14 +210,27 @@ int IfGenerate::emit(ostream&out, Entity*ent, Architecture*arc) return errors; } +/* + * Emit a process statement using "always" syntax. + * + * Note that VHDL is different from Verilog, in that the sensitivity + * list goes at the END of the statement list, not at the + * beginning. In VHDL, all the statements are initially executed once + * before blocking in the first wait on the sensitivity list. + */ int ProcessStatement::emit(ostream&out, Entity*ent, Architecture*arc) { int errors = 0; - out << "always"; + out << "always begin" << endl; + + for (list::iterator cur = statements_list_.begin() + ; cur != statements_list_.end() ; ++cur) { + errors += (*cur)->emit(out, ent, arc); + } if (! sensitivity_list_.empty()) { - out << " @("; + out << "@("; const char*comma = 0; for (list::iterator cur = sensitivity_list_.begin() ; cur != sensitivity_list_.end() ; ++cur) { @@ -226,14 +239,7 @@ int ProcessStatement::emit(ostream&out, Entity*ent, Architecture*arc) errors += (*cur)->emit(out, ent, arc); comma = ", "; } - out << ")"; - } - - out << " begin" << endl; - - for (list::iterator cur = statements_list_.begin() - ; cur != statements_list_.end() ; ++cur) { - errors += (*cur)->emit(out, ent, arc); + out << ") /* sensitivity list for process */;" << endl; } out << "end" << endl; From 7eb89c5548fe676585bae60ca7cfb477048e3194 Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Sat, 31 Mar 2012 16:29:40 -0700 Subject: [PATCH 015/179] Parse name prefix syntax for record member reference. When signals/variables are records, they are often referenced by their members, using a prefix.name syntax. Parse that syntax and generate "sorry" messages in elaboration. --- vhdlpp/debug.cc | 9 ++++- vhdlpp/expression.cc | 23 ++++++++--- vhdlpp/expression.h | 10 ++++- vhdlpp/expression_elaborate.cc | 19 +++++++++ vhdlpp/expression_emit.cc | 5 +++ vhdlpp/expression_evaluate.cc | 10 +++++ vhdlpp/expression_stream.cc | 5 +++ vhdlpp/parse.y | 71 +++++++++++++++++++++++++++++++--- 8 files changed, 138 insertions(+), 14 deletions(-) diff --git a/vhdlpp/debug.cc b/vhdlpp/debug.cc index 520a3e1bc..6201a6325 100644 --- a/vhdlpp/debug.cc +++ b/vhdlpp/debug.cc @@ -208,11 +208,16 @@ void ExpAggregate::choice_t::dump(ostream&out, int indent) const return; } - if (expr_) { + if (expr_.get()) { expr_->dump(out, indent); return; } + if (range_.get()) { + range_->dump(out, indent); + return; + } + out << setw(indent) << "" << "?choice_t?" << endl; } @@ -375,6 +380,8 @@ void ExpName::dump(ostream&out, int indent) const { out << setw(indent) << "" << "ExpName(\"" << name_ << "\")" << " at " << get_fileline() << endl; + if (prefix_.get()) + prefix_->dump(out, indent+8); if (index_) index_->dump(out, indent+6); if (lsb_) diff --git a/vhdlpp/expression.cc b/vhdlpp/expression.cc index 1334df43e..a6aee6e47 100644 --- a/vhdlpp/expression.cc +++ b/vhdlpp/expression.cc @@ -137,25 +137,26 @@ ExpAggregate::choice_t::choice_t(Expression*exp) } ExpAggregate::choice_t::choice_t() -: expr_(0) +{ +} + +ExpAggregate::choice_t::choice_t(prange_t*rang) +: range_(rang) { } ExpAggregate::choice_t::~choice_t() { - if (expr_) delete expr_; } bool ExpAggregate::choice_t::others() const { - return expr_ == 0; + return expr_.get() == 0 && range_.get() == 0; } Expression*ExpAggregate::choice_t::simple_expression(bool detach_flag) { - Expression*res = expr_; - if (detach_flag) - expr_ = 0; + Expression*res = detach_flag? expr_.release() : expr_.get(); return res; } @@ -371,6 +372,16 @@ ExpName::ExpName(perm_string nn, Expression*msb, Expression*lsb) { } +ExpName::ExpName(ExpName*prefix, perm_string nn) +: prefix_(prefix), name_(nn), index_(0), lsb_(0) +{ +} + +ExpName::ExpName(ExpName*prefix, perm_string nn, Expression*msb, Expression*lsb) +: prefix_(prefix), name_(nn), index_(msb), lsb_(lsb) +{ +} + ExpName::~ExpName() { delete index_; diff --git a/vhdlpp/expression.h b/vhdlpp/expression.h index f4b7ced26..0f44da44d 100644 --- a/vhdlpp/expression.h +++ b/vhdlpp/expression.h @@ -24,8 +24,10 @@ # include "entity.h" # include # include +# include # include +class prange_t; class Entity; class Architecture; class ScopeBase; @@ -185,6 +187,8 @@ class ExpAggregate : public Expression { explicit choice_t(Expression*exp); // Create a named choice explicit choice_t(perm_string name); + // discreate_range choice + explicit choice_t(prange_t*ran); ~choice_t(); // true if this represents an "others" choice @@ -195,7 +199,8 @@ class ExpAggregate : public Expression { void dump(ostream&out, int indent) const; private: - Expression*expr_; + std::auto_ptrexpr_; + std::auto_ptr range_; private: // not implemented choice_t(const choice_t&); choice_t& operator= (const choice_t&); @@ -461,6 +466,8 @@ class ExpName : public Expression { explicit ExpName(perm_string nn); ExpName(perm_string nn, std::list*indices); ExpName(perm_string nn, Expression*msb, Expression*lsb); + ExpName(ExpName*prefix, perm_string nn); + ExpName(ExpName*prefix, perm_string nn, Expression*msb, Expression*lsb); ~ExpName(); public: // Base methods @@ -478,6 +485,7 @@ class ExpName : public Expression { const char* name() const; private: + std::auto_ptr prefix_; perm_string name_; Expression*index_; Expression*lsb_; diff --git a/vhdlpp/expression_elaborate.cc b/vhdlpp/expression_elaborate.cc index 679f2fbb7..6d5dc797f 100644 --- a/vhdlpp/expression_elaborate.cc +++ b/vhdlpp/expression_elaborate.cc @@ -43,6 +43,13 @@ int ExpName::elaborate_lval(Entity*ent, Architecture*arc, bool is_sequ) { int errors = 0; + if (prefix_.get()) { + cerr << get_fileline() << ": sorry: I don't know how to elaborate " + << "ExpName prefix of " << name_ + << " in l-value expressions." << endl; + errors += 1; + } + const VType*found_type = 0; if (const InterfacePort*cur = ent->find_port(name_)) { @@ -116,6 +123,12 @@ int ExpName::elaborate_rval(Entity*ent, Architecture*arc, const InterfacePort*lv { int errors = 0; + if (prefix_.get()) { + cerr << get_fileline() << ": sorry: I don't know how to elaborate " + << "ExpName prefix parts in r-value expressions." << endl; + errors += 1; + } + if (const InterfacePort*cur = ent->find_port(name_)) { /* IEEE 1076-2008, p.80: * For a formal port IN, associated port should be IN, OUT, INOUT or BUFFER @@ -398,6 +411,12 @@ int ExpLogical::elaborate_expr(Entity*ent, Architecture*arc, const VType*ltype) const VType* ExpName::probe_type(Entity*ent, Architecture*arc) const { + if (prefix_.get()) { + cerr << get_fileline() << ": sorry: I don't know how to probe type " + << "of " << name_ << " with prefix parts." << endl; + return 0; + } + if (const InterfacePort*cur = ent->find_port(name_)) { ivl_assert(*this, cur->type); return cur->type; diff --git a/vhdlpp/expression_emit.cc b/vhdlpp/expression_emit.cc index c5141c4f6..30b66dbfc 100644 --- a/vhdlpp/expression_emit.cc +++ b/vhdlpp/expression_emit.cc @@ -515,6 +515,11 @@ int ExpName::emit(ostream&out, Entity*ent, Architecture*arc) { int errors = 0; + if (prefix_.get()) { + cerr << get_fileline() << ": sorry: I don't know how to emit ExpName prefix parts." << endl; + errors += 1; + } + out << "\\" << name_ << " "; if (index_) { out << "["; diff --git a/vhdlpp/expression_evaluate.cc b/vhdlpp/expression_evaluate.cc index 3596e9fd5..62c3929b7 100644 --- a/vhdlpp/expression_evaluate.cc +++ b/vhdlpp/expression_evaluate.cc @@ -76,6 +76,11 @@ bool ExpName::evaluate(ScopeBase*scope, int64_t&val) const const VType*type; Expression*exp; + if (prefix_.get()) { + cerr << get_fileline() << ": sorry: I don't know how to evaluate ExpName prefix parts." << endl; + return false; + } + bool rc = scope->find_constant(name_, type, exp); if (rc == false) return false; @@ -85,6 +90,11 @@ bool ExpName::evaluate(ScopeBase*scope, int64_t&val) const bool ExpName::evaluate(Entity*ent, Architecture*arc, int64_t&val) const { + if (prefix_.get()) { + cerr << get_fileline() << ": sorry: I don't know how to evaluate ExpName prefix parts." << endl; + return false; + } + const InterfacePort*gen = ent->find_generic(name_); if (gen) { cerr << get_fileline() << ": sorry: I don't necessarily handle generic overrides." << endl; diff --git a/vhdlpp/expression_stream.cc b/vhdlpp/expression_stream.cc index 2427f7fba..16b47fae0 100644 --- a/vhdlpp/expression_stream.cc +++ b/vhdlpp/expression_stream.cc @@ -108,6 +108,11 @@ void ExpLogical::write_to_stream(ostream&) void ExpName::write_to_stream(ostream&fd) { + if (prefix_.get()) { + prefix_->write_to_stream(fd); + fd << "."; + } + fd << name_; if (index_) { fd << "("; diff --git a/vhdlpp/parse.y b/vhdlpp/parse.y index f2d26ffdf..b7d69ede0 100644 --- a/vhdlpp/parse.y +++ b/vhdlpp/parse.y @@ -167,6 +167,37 @@ static Expression*aggregate_or_primary(const YYLTYPE&loc, std::listextract_expression(); } +static ExpName*make_name_from_prefix(const YYLTYPE&loc, const vector*names) +{ + ExpName*cur = new ExpName(names->at(0)); + FILE_NAME(cur, loc); + + for (size_t idx = 1 ; idx < names->size() ; idx += 1) { + ExpName*tmp = new ExpName(cur, names->at(idx)); + FILE_NAME(tmp, loc); + cur = tmp; + } + + return cur; +} + +static ExpName*make_name_from_prefix(const YYLTYPE&loc, const vector*names, Expression*msb, Expression*lsb) +{ + ExpName*cur = new ExpName(names->at(0)); + FILE_NAME(cur, loc); + + for (size_t idx = 1 ; idx < (names->size()-1) ; idx += 1) { + ExpName*tmp = new ExpName(cur, names->at(idx)); + FILE_NAME(tmp, loc); + cur = tmp; + } + + ExpName*result = new ExpName(cur, names->back(), msb, lsb); + FILE_NAME(result, loc); + + return result; +} + static list* record_elements(list*names, const VType*type) { @@ -561,6 +592,8 @@ choice { $$ = new ExpAggregate::choice_t($1);} | K_others { $$ = new ExpAggregate::choice_t; } + | range /* discrete_range: range */ + { $$ = new ExpAggregate::choice_t($1); } ; choices @@ -678,6 +711,22 @@ concurrent_conditional_signal_assignment /* IEEE 1076-2008 P11.6 */ SignalAssignment*tmpa = new SignalAssignment(name, tmp); FILE_NAME(tmpa, @1); + $$ = tmpa; + } + + /* Error recovery rules. */ + + | name LEQ error K_when expression else_when_waveforms ';' + { errormsg(@3, "Syntax error in waveform of conditional signal assignment.\n"); + ExpConditional*tmp = new ExpConditional($5, 0, $6); + FILE_NAME(tmp, @3); + delete $6; + + ExpName*name = dynamic_cast ($1); + assert(name); + SignalAssignment*tmpa = new SignalAssignment(name, tmp); + FILE_NAME(tmpa, @1); + $$ = tmpa; } ; @@ -1445,6 +1494,13 @@ name delete[]$1; $$ = tmp; } + + | selected_name + { ExpName*tmp = make_name_from_prefix(@1, $1); + delete $1; + $$ = tmp; + } + /* Note that this rule can match array element selects and various function calls. The only way we can tell the difference is from left context, namely whether the name is a type name or function @@ -1467,6 +1523,11 @@ name delete[]$1; $$ = tmp; } + | selected_name '(' range ')' + { ExpName*tmp = make_name_from_prefix(@1, $1, $3->msb(), $3->lsb()); + delete $1; + $$ = tmp; + } ; /* Handle name lists as lists of expressions. */ @@ -1857,12 +1918,10 @@ secondary_unit selected_name : prefix '.' suffix - { - std::vector* tmp = $1; - tmp->push_back(lex_strings.make($3)); - delete[] $3; - - $$ = tmp; + { std::vector* tmp = $1; + tmp->push_back(lex_strings.make($3)); + delete[] $3; + $$ = tmp; } ; From 021d944a30e5de3d14a0730f1a5c65d780dd3b8b Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Sun, 1 Apr 2012 18:48:19 -0700 Subject: [PATCH 016/179] Probe type of ExpName with a record prefix. --- vhdlpp/expression.h | 4 +++ vhdlpp/expression_elaborate.cc | 46 +++++++++++++++++++++++++++++++--- vhdlpp/vtype.cc | 12 +++++++++ vhdlpp/vtype.h | 6 +++++ 4 files changed, 65 insertions(+), 3 deletions(-) diff --git a/vhdlpp/expression.h b/vhdlpp/expression.h index 0f44da44d..739f8079d 100644 --- a/vhdlpp/expression.h +++ b/vhdlpp/expression.h @@ -484,6 +484,10 @@ class ExpName : public Expression { void dump(ostream&out, int indent = 0) const; const char* name() const; + private: + const VType* probe_prefix_type_(Entity*ent, Architecture*arc) const; + const VType* probe_prefixed_type_(Entity*ent, Architecture*arc) const; + private: std::auto_ptr prefix_; perm_string name_; diff --git a/vhdlpp/expression_elaborate.cc b/vhdlpp/expression_elaborate.cc index 6d5dc797f..1386a549d 100644 --- a/vhdlpp/expression_elaborate.cc +++ b/vhdlpp/expression_elaborate.cc @@ -409,14 +409,54 @@ int ExpLogical::elaborate_expr(Entity*ent, Architecture*arc, const VType*ltype) return errors; } -const VType* ExpName::probe_type(Entity*ent, Architecture*arc) const +const VType* ExpName::probe_prefix_type_(Entity*ent, Architecture*arc) const { if (prefix_.get()) { - cerr << get_fileline() << ": sorry: I don't know how to probe type " - << "of " << name_ << " with prefix parts." << endl; + cerr << get_fileline() << ": sorry: I do not know how to support nested prefix parts." << endl; return 0; } + const VType*type = probe_type(ent, arc); + return type; +} + +/* + * This method is the probe_type() imlementation for ExpName objects + * that have prefix parts. In this case we try to get the type of the + * prefix and interpret the name in that context. + */ +const VType* ExpName::probe_prefixed_type_(Entity*ent, Architecture*arc) const +{ + // First, get the type of the prefix. + const VType*prefix_type = prefix_->probe_prefix_type_(ent, arc); + if (prefix_type == 0) { + return 0; + } + + // If the prefix type is a record, then the current name is + // the name of a member. + if (const VTypeRecord*pref_record = dynamic_cast (prefix_type)) { + const VTypeRecord::element_t*element = pref_record->element_by_name(name_); + ivl_assert(*this, element); + + const VType*element_type = element->peek_type(); + ivl_assert(*this, element_type); + + return element_type; + } + + cerr << get_fileline() << ": sorry: I don't know how to probe " + << "prefix type " << typeid(*prefix_type).name() + << " of " << name_ << "." << endl; + + return 0; +} + +const VType* ExpName::probe_type(Entity*ent, Architecture*arc) const +{ + if (prefix_.get()) + return probe_prefixed_type_(ent, arc); + if (const InterfacePort*cur = ent->find_port(name_)) { ivl_assert(*this, cur->type); return cur->type; diff --git a/vhdlpp/vtype.cc b/vhdlpp/vtype.cc index 5c346ea5f..cd39a4fe9 100644 --- a/vhdlpp/vtype.cc +++ b/vhdlpp/vtype.cc @@ -183,6 +183,18 @@ void VTypeRecord::show(ostream&out) const write_to_stream(out); } +const VTypeRecord::element_t* VTypeRecord::element_by_name(perm_string name) const +{ + for (vector::const_iterator cur = elements_.begin() + ; cur != elements_.end() ; ++cur) { + element_t*curp = *cur; + if (curp->peek_name() == name) + return curp; + } + + return 0; +} + VTypeRecord::element_t::element_t(perm_string name, const VType*typ) : name_(name), type_(typ) { diff --git a/vhdlpp/vtype.h b/vhdlpp/vtype.h index 5d86f05c9..ac15a68b3 100644 --- a/vhdlpp/vtype.h +++ b/vhdlpp/vtype.h @@ -209,6 +209,9 @@ class VTypeRecord : public VType { void write_to_stream(std::ostream&) const; + inline perm_string peek_name() const { return name_; } + inline const VType* peek_type() const { return type_; } + private: perm_string name_; const VType*type_; @@ -226,6 +229,9 @@ class VTypeRecord : public VType { void show(std::ostream&) const; int emit_def(std::ostream&out, perm_string name) const; + + const element_t* element_by_name(perm_string name) const; + private: int emit_decl(std::ostream&out, perm_string name, bool reg_flag) const; From a5458828cd36ccaf23eb6bd6adda41c1e3543ce1 Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Thu, 5 Apr 2012 20:20:34 -0700 Subject: [PATCH 017/179] Some vhdl parser error handling. --- vhdlpp/parse.y | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/vhdlpp/parse.y b/vhdlpp/parse.y index b7d69ede0..349f49b7c 100644 --- a/vhdlpp/parse.y +++ b/vhdlpp/parse.y @@ -864,6 +864,16 @@ constant_declaration } delete $2; } + | K_constant identifier_list ':' error ';' + { errormsg(@4, "Syntax error in constant declaration type.\n"); + yyerrok; + delete $2; + } + | K_constant error ';' + { errormsg(@2, "Syntax error in constant declaration.\n"); + yyerrok; + } + ; context_clause : context_items | ; @@ -1595,6 +1605,7 @@ package_body_declarative_part_opt package_declarative_item : component_declaration | constant_declaration + | type_declaration | subtype_declaration | type_declaration | use_clause @@ -2139,6 +2150,10 @@ type_declaration yyerrok; delete[]$2; } + | K_type error ';' + { errormsg(@1, "Error in type definition\n"); + yyerrok; + } ; type_definition From ae06863db123f0d214861de3ce6602e5f8f6eeed Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Sun, 8 Apr 2012 15:45:17 -0700 Subject: [PATCH 018/179] Elaborate prefix names which may be structure variables. --- vhdlpp/architec_elaborate.cc | 13 +++- vhdlpp/expression.h | 5 +- vhdlpp/expression_elaborate.cc | 131 ++++++++++++++++++++++++++------- 3 files changed, 118 insertions(+), 31 deletions(-) diff --git a/vhdlpp/architec_elaborate.cc b/vhdlpp/architec_elaborate.cc index d399c141d..23dc837f2 100644 --- a/vhdlpp/architec_elaborate.cc +++ b/vhdlpp/architec_elaborate.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011 Stephen Williams (steve@icarus.com) + * Copyright (c) 2011-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 @@ -31,10 +31,12 @@ int Architecture::elaborate(Entity*entity) for (list::iterator cur = statements_.begin() ; cur != statements_.end() ; ++cur) { - errors += (*cur)->elaborate(entity, this); + int cur_errors = (*cur)->elaborate(entity, this); + errors += cur_errors; } - return errors; + cerr << errors << " errors in " << name_ << " architecture of " << entity->get_name() << "." << endl; + return errors; } int Architecture::Statement::elaborate(Entity*, Architecture*) @@ -276,7 +278,10 @@ int SignalAssignment::elaborate(Entity*ent, Architecture*arc) // r-value. const VType*lval_type = lval_->peek_type(); if (lval_type == 0) { - if (errors == 0) errors += 1; + if (errors == 0) { + errors += 1; + cerr << get_fileline() << ": error: Unable to calculate type for l-value expression." << endl; + } return errors; } diff --git a/vhdlpp/expression.h b/vhdlpp/expression.h index 739f8079d..5004e2ce6 100644 --- a/vhdlpp/expression.h +++ b/vhdlpp/expression.h @@ -1,7 +1,7 @@ #ifndef __expression_H #define __expression_H /* - * Copyright (c) 2011 Stephen Williams (steve@icarus.com) + * Copyright (c) 2011-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 @@ -485,6 +485,9 @@ class ExpName : public Expression { const char* name() const; private: + const VType* elaborate_adjust_type_with_range_(Entity*ent, Architecture*arc, const VType*type); + + int elaborate_lval_(Entity*ent, Architecture*arc, bool, ExpName*suffix); const VType* probe_prefix_type_(Entity*ent, Architecture*arc) const; const VType* probe_prefixed_type_(Entity*ent, Architecture*arc) const; diff --git a/vhdlpp/expression_elaborate.cc b/vhdlpp/expression_elaborate.cc index 1386a549d..8e3d4588d 100644 --- a/vhdlpp/expression_elaborate.cc +++ b/vhdlpp/expression_elaborate.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011 Stephen Williams (steve@icarus.com) + * Copyright (c) 2011-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 @@ -39,7 +39,38 @@ const VType* Expression::probe_type(Entity*, Architecture*) const return 0; } -int ExpName::elaborate_lval(Entity*ent, Architecture*arc, bool is_sequ) +const VType*ExpName::elaborate_adjust_type_with_range_(Entity*, Architecture*arc, const VType*type) +{ + + if (const VTypeArray*array = dynamic_cast(type)) { + if (index_ && !lsb_) { + // If the name is an array or a vector, then an + // indexed name has the type of the element. + type = array->element_type(); + + } else if (index_ && lsb_) { + // If the name is an array, then a part select is + // also an array, but with different bounds. + int64_t use_msb, use_lsb; + bool flag; + + flag = index_->evaluate(arc, use_msb); + ivl_assert(*this, flag); + flag = lsb_->evaluate(arc, use_lsb); + ivl_assert(*this, flag); + + Expression*exp_msb = new ExpInteger(use_msb); + Expression*exp_lsb = new ExpInteger(use_lsb); + vector use_dims (1); + use_dims[0] = VTypeArray::range_t(exp_msb, exp_lsb); + type = new VTypeArray(array->element_type(), use_dims); + } + } + + return type; +} + +int ExpName::elaborate_lval_(Entity*ent, Architecture*arc, bool is_sequ, ExpName*suffix) { int errors = 0; @@ -52,6 +83,77 @@ int ExpName::elaborate_lval(Entity*ent, Architecture*arc, bool is_sequ) const VType*found_type = 0; + if (const InterfacePort*cur = ent->find_port(name_)) { + if (cur->mode != PORT_OUT) { + cerr << get_fileline() << ": error: Assignment to " + "input port " << name_ << "." << endl; + return errors + 1; + } + + if (is_sequ) + ent->set_declaration_l_value(name_, is_sequ); + + found_type = cur->type; + + } else if (ent->find_generic(name_)) { + + cerr << get_fileline() << ": error: Assignment to generic " + << name_ << " from entity " + << ent->get_name() << "." << endl; + return errors + 1; + + } else if (Signal*sig = arc->find_signal(name_)) { + // Tell the target signal that this may be a sequential l-value. + if (is_sequ) sig->count_ref_sequ(); + + found_type = sig->peek_type(); + + } else if (Variable*var = arc->find_variable(name_)) { + // Tell the target signal that this may be a sequential l-value. + if (is_sequ) var->count_ref_sequ(); + + found_type = var->peek_type(); + } + + ivl_assert(*this, found_type); + + const VType*suffix_type = 0; + + if (const VTypeRecord*record = dynamic_cast (found_type)) { + const VTypeRecord::element_t*element = record->element_by_name(suffix->name_); + ivl_assert(*this, element); + + const VType*element_type = element->peek_type(); + ivl_assert(*this, element_type); + + suffix_type = element_type; + } + + if (suffix_type == 0) { + cerr << get_fileline() << ": error: I don't know how to handle prefix " << name_ + << " with suffix " << suffix->name_ << endl; + errors += 1; + return errors; + } + + suffix_type = suffix->elaborate_adjust_type_with_range_(ent, arc, suffix_type); + + ivl_assert(*this, suffix_type); + suffix->set_type(suffix_type); + + return errors; +} + +int ExpName::elaborate_lval(Entity*ent, Architecture*arc, bool is_sequ) +{ + int errors = 0; + + if (prefix_.get()) { + return prefix_->elaborate_lval_(ent, arc, is_sequ, this); + } + + const VType*found_type = 0; + if (const InterfacePort*cur = ent->find_port(name_)) { if (cur->mode != PORT_OUT) { cerr << get_fileline() << ": error: Assignment to " @@ -90,30 +192,7 @@ int ExpName::elaborate_lval(Entity*ent, Architecture*arc, bool is_sequ) return errors + 1; } - if (const VTypeArray*array = dynamic_cast(found_type)) { - if (index_ && !lsb_) { - // If the name is an array or a vector, then an - // indexed name has the type of the element. - found_type = array->element_type(); - - } else if (index_ && lsb_) { - // If the name is an array, then a part select is - // also an array, but with different bounds. - int64_t use_msb, use_lsb; - bool flag; - - flag = index_->evaluate(arc, use_msb); - ivl_assert(*this, flag); - flag = lsb_->evaluate(arc, use_lsb); - ivl_assert(*this, flag); - - Expression*exp_msb = new ExpInteger(use_msb); - Expression*exp_lsb = new ExpInteger(use_lsb); - vector use_dims (1); - use_dims[0] = VTypeArray::range_t(exp_msb, exp_lsb); - found_type = new VTypeArray(array->element_type(), use_dims); - } - } + found_type = elaborate_adjust_type_with_range_(ent, arc, found_type); set_type(found_type); return errors; From 71d2401221e5b1e74d036c6c6f5c36f43f7b48f1 Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Sun, 8 Apr 2012 19:06:58 -0700 Subject: [PATCH 019/179] Handle VHDL records. Elaborate records and emit them as packed SV records. Also handle record members so handle name prefixes. While we are at it, handle some cases of array aggregate expressions. --- vhdlpp/architec_elaborate.cc | 20 +++++- vhdlpp/expression.h | 2 + vhdlpp/expression_elaborate.cc | 32 ++++++++-- vhdlpp/expression_emit.cc | 107 ++++++++++++++++++++++++--------- vhdlpp/vtype_emit.cc | 16 ++++- 5 files changed, 141 insertions(+), 36 deletions(-) diff --git a/vhdlpp/architec_elaborate.cc b/vhdlpp/architec_elaborate.cc index 23dc837f2..812fc1f2f 100644 --- a/vhdlpp/architec_elaborate.cc +++ b/vhdlpp/architec_elaborate.cc @@ -28,6 +28,19 @@ int Architecture::elaborate(Entity*entity) { int errors = 0; + // Constant assignments in the architecture get their types + // from the constant declaration itself. Elaborate the value + // expression with the declared type. + + for (map::iterator cur = old_constants_.begin() + ; cur != old_constants_.end() ; ++cur) { + cur->second->val->elaborate_expr(entity, this, cur->second->typ); + } + for (map::iterator cur = new_constants_.begin() + ; cur != new_constants_.end() ; ++cur) { + cur->second->val->elaborate_expr(entity, this, cur->second->typ); + } + for (list::iterator cur = statements_.begin() ; cur != statements_.end() ; ++cur) { @@ -35,7 +48,12 @@ int Architecture::elaborate(Entity*entity) errors += cur_errors; } - cerr << errors << " errors in " << name_ << " architecture of " << entity->get_name() << "." << endl; + if (errors > 0) { + cerr << errors << " errors in " + << name_ << " architecture of " + << entity->get_name() << "." << endl; + } + return errors; } diff --git a/vhdlpp/expression.h b/vhdlpp/expression.h index 5004e2ce6..4736a5d06 100644 --- a/vhdlpp/expression.h +++ b/vhdlpp/expression.h @@ -491,6 +491,8 @@ class ExpName : public Expression { const VType* probe_prefix_type_(Entity*ent, Architecture*arc) const; const VType* probe_prefixed_type_(Entity*ent, Architecture*arc) const; + int emit_as_prefix_(ostream&out, Entity*ent, Architecture*arc); + private: std::auto_ptr prefix_; perm_string name_; diff --git a/vhdlpp/expression_elaborate.cc b/vhdlpp/expression_elaborate.cc index 8e3d4588d..05c8fd4e3 100644 --- a/vhdlpp/expression_elaborate.cc +++ b/vhdlpp/expression_elaborate.cc @@ -306,8 +306,8 @@ int ExpAggregate::elaborate_expr(Entity*ent, Architecture*arc, const VType*ltype /* * Elaboration of array aggregates is elaboration of the element - * expressions using the element type as the ltype for the - * subexpression. + * expressions (the elements_ member) using the element type as the + * ltype for the subexpression. */ int ExpAggregate::elaborate_expr_array_(Entity*ent, Architecture*arc, const VTypeArray*ltype) { @@ -315,22 +315,42 @@ int ExpAggregate::elaborate_expr_array_(Entity*ent, Architecture*arc, const VTyp int errors = 0; size_t choice_count = 0; + // Figure out how many total elements we have here. Note that + // each parsed element may be bound to multiple choices, so + // account for that. for (size_t edx = 0 ; edx < elements_.size() ; edx += 1) { element_t*ecur = elements_[edx]; - choice_count += ecur->count_choices(); + if (ecur->count_choices() == 0) + choice_count += 1; + else + choice_count += ecur->count_choices(); } aggregate_.resize(choice_count); + // Translate the elements_ array to the aggregate_ array. In + // the target array, each expression is attached to a single + // choice. size_t cdx = 0; for (size_t edx = 0 ; edx < elements_.size() ; edx += 1) { element_t*ecur = elements_[edx]; - ecur->map_choices(&aggregate_[cdx]); - cdx += ecur->count_choices(); + if (ecur->count_choices() == 0) { + // positional associations have no "choice" + // associated with them. + aggregate_[cdx].choice = 0; + aggregate_[cdx].expr = ecur->extract_expression(); + aggregate_[cdx].alias_flag; + cdx += 1; + } else { + ecur->map_choices(&aggregate_[cdx]); + cdx += ecur->count_choices(); + } } ivl_assert(*this, cdx == choice_count); + // Now run through the more convenient mapping and elaborate + // all the expressions that I find. for (size_t idx = 0 ; idx < aggregate_.size() ; idx += 1) { if (aggregate_[idx].alias_flag) continue; @@ -338,7 +358,9 @@ int ExpAggregate::elaborate_expr_array_(Entity*ent, Architecture*arc, const VTyp errors += aggregate_[idx].expr->elaborate_expr(ent, arc, element_type); } + // done with the obsolete elements_ vector. elements_.clear(); + return errors; } diff --git a/vhdlpp/expression_emit.cc b/vhdlpp/expression_emit.cc index 30b66dbfc..371cb3af6 100644 --- a/vhdlpp/expression_emit.cc +++ b/vhdlpp/expression_emit.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011 Stephen Williams (steve@icarus.com) + * Copyright (c) 2011-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 @@ -70,10 +70,19 @@ int ExpUnary::emit_operand1(ostream&out, Entity*ent, Architecture*arc) int ExpAggregate::emit(ostream&out, Entity*ent, Architecture*arc) { + if (peek_type() == 0) { + out << "/* " << get_fileline() << ": internal error: " + << "Aggregate literal needs well defined type." << endl; + return 1; + } + if (const VTypeArray*atype = dynamic_cast (peek_type())) return emit_array_(out, ent, arc, atype); - return Expression::emit(out, ent, arc); + out << "/* " << get_fileline() << ": internal error: " + << "I don't know how to elaborate aggregate in " << typeid(peek_type()).name() + << " type context. */"; + return 1; } int ExpAggregate::emit_array_(ostream&out, Entity*ent, Architecture*arc, const VTypeArray*atype) @@ -126,27 +135,7 @@ int ExpAggregate::emit_array_(ostream&out, Entity*ent, Architecture*arc, const V const VTypeArray::range_t&rang = atype->dimension(0); assert(! rang.is_box()); - map element_map; - choice_element*element_other = 0; - - for (size_t idx = 0 ; idx < aggregate_.size() ; idx += 1) { - if (aggregate_[idx].choice->others()) { - ivl_assert(*this, element_other == 0); - element_other = &aggregate_[idx]; - continue; - } - - Expression*tmp = aggregate_[idx].choice->simple_expression(false); - int64_t tmp_val; - if (! tmp->evaluate(ent, arc, tmp_val)) { - cerr << tmp->get_fileline() << ": error: Unable to evaluate aggregate choice expression." << endl; - errors += 1; - continue; - } - - element_map[tmp_val] = &aggregate_[idx]; - } - + // Fully calculate the range numbers. int64_t use_msb, use_lsb; bool rc; rc = rang.msb()->evaluate(ent, arc, use_msb); @@ -155,16 +144,68 @@ int ExpAggregate::emit_array_(ostream&out, Entity*ent, Architecture*arc, const V ivl_assert(*this, rc); ivl_assert(*this, use_msb >= use_lsb); + map element_map; + choice_element*element_other = 0; + + bool positional_section = true; + int64_t positional_idx = use_msb; + + for (size_t idx = 0 ; idx < aggregate_.size() ; idx += 1) { + + if (aggregate_[idx].choice == 0) { + // positional association! + if (!positional_section) { + cerr << get_fileline() << ": error: " + << "All positional associations must be before" + << " any named associations." << endl; + errors += 1; + } + element_map[positional_idx] = &aggregate_[idx]; + positional_idx -= 1; + continue; + } + + if (aggregate_[idx].choice->others()) { + ivl_assert(*this, element_other == 0); + element_other = &aggregate_[idx]; + continue; + } + + int64_t tmp_val; + Expression*tmp = aggregate_[idx].choice->simple_expression(false); + + // Named aggregate element. Once we see one of + // these, we can no longer accept positional + // elements so disable further positional + // processing. + positional_section = false; + if (! tmp->evaluate(ent, arc, tmp_val)) { + cerr << tmp->get_fileline() << ": error: " + << "Unable to evaluate aggregate choice expression." << endl; + errors += 1; + continue; + } + + element_map[tmp_val] = &aggregate_[idx]; + } + + // Emit the elements as a concatenation. This works great for + // vectors of bits. We implement VHDL arrays as packed arrays, + // so this should be generally correct. out << "{"; for (int64_t idx = use_msb ; idx >= use_lsb ; idx -= 1) { choice_element*cur = element_map[idx]; if (cur == 0) cur = element_other; - ivl_assert(*this, cur != 0); if (idx < use_msb) out << ", "; - errors += cur->expr->emit(out, ent, arc); + if (cur == 0) { + out << "/* Missing element " << idx << " */"; + errors += 1; + } else { + errors += cur->expr->emit(out, ent, arc); + } } out << "}"; @@ -511,13 +552,25 @@ int ExpLogical::emit(ostream&out, Entity*ent, Architecture*arc) return errors; } +int ExpName::emit_as_prefix_(ostream&out, Entity*ent, Architecture*arc) +{ + int errors = 0; + if (prefix_.get()) { + errors += prefix_->emit_as_prefix_(out, ent, arc); + } + + out << "\\" << name_ << " "; + ivl_assert(*this, index_ == 0); + out << "."; + return errors; +} + int ExpName::emit(ostream&out, Entity*ent, Architecture*arc) { int errors = 0; if (prefix_.get()) { - cerr << get_fileline() << ": sorry: I don't know how to emit ExpName prefix parts." << endl; - errors += 1; + errors += prefix_->emit_as_prefix_(out, ent, arc); } out << "\\" << name_ << " "; diff --git a/vhdlpp/vtype_emit.cc b/vhdlpp/vtype_emit.cc index e6042f0e7..8225bc917 100644 --- a/vhdlpp/vtype_emit.cc +++ b/vhdlpp/vtype_emit.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011 Stephen Williams (steve@icarus.com) + * Copyright (c) 2011-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 @@ -162,14 +162,24 @@ int VTypeRange::emit_decl(ostream&out, perm_string name, bool reg_flag) const int VTypeRecord::emit_def(ostream&out, perm_string name) const { int errors = 0; - assert(0); + out << "struct packed {"; + + for (vector::const_iterator cur = elements_.begin() + ; cur != elements_.end() ; ++cur) { + perm_string element_name = (*cur)->peek_name(); + const VType*element_type = (*cur)->peek_type(); + element_type->emit_def(out, element_name); + out << "; "; + } + + out << "} "; return errors; } int VTypeRecord::emit_decl(ostream&out, perm_string name, bool reg_flag) const { int errors = 0; - assert(0); + errors += emit_def(out, name); return errors; } From c7366e65cd04a1cc6e0669439cb6ad8210afd4ee Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Thu, 12 Apr 2012 20:20:37 -0700 Subject: [PATCH 020/179] Rework port input and inout to use data_type_or_implicit rule for types. --- parse.y | 110 ++++++++++++++++++++------------------------------ parse_misc.h | 1 + pform.cc | 41 +++++++++++++++++++ pform.h | 1 + pform_types.h | 5 ++- 5 files changed, 90 insertions(+), 68 deletions(-) diff --git a/parse.y b/parse.y index 71a17b01a..75491e45a 100644 --- a/parse.y +++ b/parse.y @@ -122,19 +122,6 @@ list* make_range_from_width(uint64_t wid) return rlist; } -static list* make_range_from_pair(list*rpair) -{ - pform_range_t range; - assert(rpair && rpair->size() == 2); - range.first = rpair->front(); - range.second = rpair->back(); - delete rpair; - - list*rlist = new list; - rlist->push_back(range); - return rlist; -} - static list* list_from_identifier(char*id) { list*tmp = new list; @@ -391,6 +378,7 @@ static void current_function_set_statement(const YYLTYPE&loc, vector data_type_t*data_type; class_type_t*class_type; + real_type_t::type_t real_type; verinum* number; @@ -592,7 +580,8 @@ static void current_function_set_statement(const YYLTYPE&loc, vector %type specify_simple_path specify_simple_path_decl %type specify_edge_path specify_edge_path_decl -%type atom2_type non_integer_type +%type non_integer_type +%type atom2_type %type module_start module_end %token K_TAND @@ -1239,9 +1228,9 @@ method_qualifier_opt non_integer_type /* IEEE1800-2005: A.2.2.1 */ - : K_real { $$ = K_real; } - | K_realtime { $$ = K_real; } - | K_shortreal { $$ = K_shortreal; } + : K_real { $$ = real_type_t::REAL; } + | K_realtime { $$ = real_type_t::REAL; } + | K_shortreal { $$ = real_type_t::SHORTREAL; } ; number : BASED_NUMBER @@ -3467,6 +3456,7 @@ list_of_port_declarations port_declaration_context.port_net_type, port_declaration_context.var_type, port_declaration_context.sign_flag, + port_declaration_context.data_type, port_declaration_context.range, 0); delete[]$3; $$ = tmp; @@ -3484,39 +3474,19 @@ list_of_port_declarations ; port_declaration - : attribute_list_opt - K_input net_type_opt primitive_type_opt unsigned_signed_opt range_opt IDENTIFIER - { Module::port_t*ptmp; - perm_string name = lex_strings.make($7); - ptmp = pform_module_port_reference(name, @2.text, - @2.first_line); - pform_module_define_port(@2, name, NetNet::PINPUT, - $3, $4, $5, $6, $1); - port_declaration_context.port_type = NetNet::PINPUT; - port_declaration_context.port_net_type = $3; - port_declaration_context.var_type = $4; - port_declaration_context.sign_flag = $5; - delete port_declaration_context.range; - port_declaration_context.range = $6; - delete[]$7; - $$ = ptmp; - } - | attribute_list_opt - K_input atom2_type signed_unsigned_opt IDENTIFIER + : attribute_list_opt K_input net_type_opt data_type_or_implicit IDENTIFIER { Module::port_t*ptmp; perm_string name = lex_strings.make($5); - list* use_range = make_range_from_width($3); - ptmp = pform_module_port_reference(name, @2.text, - @2.first_line); - pform_module_define_port(@2, name, NetNet::PINPUT, - NetNet::UNRESOLVED_WIRE, IVL_VT_BOOL, - $4, use_range, $1); + ptmp = pform_module_port_reference(name, @2.text, @2.first_line); + pform_module_define_port(@2, name, NetNet::PINPUT, $3, IVL_VT_NO_TYPE, + false, $4, 0, $1); port_declaration_context.port_type = NetNet::PINPUT; - port_declaration_context.port_net_type = NetNet::UNRESOLVED_WIRE; - port_declaration_context.var_type = IVL_VT_BOOL; - port_declaration_context.sign_flag = $4; + port_declaration_context.port_net_type = $3; + port_declaration_context.var_type = IVL_VT_NO_TYPE; + port_declaration_context.sign_flag = false; delete port_declaration_context.range; - port_declaration_context.range = use_range; + port_declaration_context.range = 0; + port_declaration_context.data_type = $4; delete[]$5; $$ = ptmp; } @@ -3527,31 +3497,31 @@ port_declaration ptmp = pform_module_port_reference(name, @2.text, @2.first_line); pform_module_define_port(@2, name, NetNet::PINPUT, - NetNet::WIRE, IVL_VT_REAL, true, 0, $1); + NetNet::WIRE, IVL_VT_REAL, true, 0, 0, $1); port_declaration_context.port_type = NetNet::PINPUT; port_declaration_context.port_net_type = NetNet::WIRE; port_declaration_context.var_type = IVL_VT_REAL; port_declaration_context.sign_flag = true; delete port_declaration_context.range; port_declaration_context.range = 0; + port_declaration_context.data_type = 0; delete[]$4; $$ = ptmp; } - | attribute_list_opt - K_inout net_type_opt primitive_type_opt unsigned_signed_opt range_opt IDENTIFIER + | attribute_list_opt K_inout net_type_opt data_type_or_implicit IDENTIFIER { Module::port_t*ptmp; - perm_string name = lex_strings.make($7); - ptmp = pform_module_port_reference(name, @2.text, - @2.first_line); - pform_module_define_port(@2, name, NetNet::PINOUT, - $3, $4, $5, $6, $1); + perm_string name = lex_strings.make($5); + ptmp = pform_module_port_reference(name, @2.text, @2.first_line); + pform_module_define_port(@2, name, NetNet::PINOUT, $3, IVL_VT_NO_TYPE, + false, $4, 0, $1); port_declaration_context.port_type = NetNet::PINOUT; port_declaration_context.port_net_type = $3; - port_declaration_context.var_type = $4; - port_declaration_context.sign_flag = $5; + port_declaration_context.var_type = IVL_VT_NO_TYPE; + port_declaration_context.sign_flag = false; delete port_declaration_context.range; - port_declaration_context.range = $6; - delete[]$7; + port_declaration_context.range = 0; + port_declaration_context.data_type = $4; + delete[]$5; $$ = ptmp; } | attribute_list_opt @@ -3561,13 +3531,14 @@ port_declaration ptmp = pform_module_port_reference(name, @2.text, @2.first_line); pform_module_define_port(@2, name, NetNet::PINOUT, - NetNet::WIRE, IVL_VT_REAL, true, 0, $1); + NetNet::WIRE, IVL_VT_REAL, true, 0, 0, $1); port_declaration_context.port_type = NetNet::PINOUT; port_declaration_context.port_net_type = NetNet::WIRE; port_declaration_context.var_type = IVL_VT_REAL; port_declaration_context.sign_flag = true; delete port_declaration_context.range; port_declaration_context.range = 0; + port_declaration_context.data_type = 0; delete[]$4; $$ = ptmp; } @@ -3578,13 +3549,14 @@ port_declaration ptmp = pform_module_port_reference(name, @2.text, @2.first_line); pform_module_define_port(@2, name, NetNet::POUTPUT, - $3, $4, $5, $6, $1); + $3, $4, $5, 0, $6, $1); port_declaration_context.port_type = NetNet::POUTPUT; port_declaration_context.port_net_type = $3; port_declaration_context.var_type = $4; port_declaration_context.sign_flag = $5; delete port_declaration_context.range; port_declaration_context.range = $6; + port_declaration_context.data_type = 0; delete[]$7; $$ = ptmp; } @@ -3595,13 +3567,14 @@ port_declaration ptmp = pform_module_port_reference(name, @2.text, @2.first_line); pform_module_define_port(@2, name, NetNet::POUTPUT, - $3, $4, $5, $6, $1); + $3, $4, $5, 0, $6, $1); port_declaration_context.port_type = NetNet::POUTPUT; port_declaration_context.port_net_type = $3; port_declaration_context.var_type = $4; port_declaration_context.sign_flag = $5; delete port_declaration_context.range; port_declaration_context.range = $6; + port_declaration_context.data_type = 0; delete[]$7; $$ = ptmp; } @@ -3612,13 +3585,14 @@ port_declaration ptmp = pform_module_port_reference(name, @2.text, @2.first_line); pform_module_define_port(@2, name, NetNet::POUTPUT, - $3, $4, $5, $6, $1); + $3, $4, $5, 0, $6, $1); port_declaration_context.port_type = NetNet::POUTPUT; port_declaration_context.port_net_type = $3; port_declaration_context.var_type = $4; port_declaration_context.sign_flag = $5; delete port_declaration_context.range; port_declaration_context.range = $6; + port_declaration_context.data_type = 0; pform_make_reginit(@7, name, $9); @@ -3634,13 +3608,14 @@ port_declaration ptmp = pform_module_port_reference(name, @2.text, @2.first_line); pform_module_define_port(@2, name, NetNet::POUTPUT, - t, $4, $5, $6, $1); + t, $4, $5, 0, $6, $1); port_declaration_context.port_type = NetNet::POUTPUT; port_declaration_context.port_net_type = t; port_declaration_context.var_type = $4; port_declaration_context.sign_flag = $5; delete port_declaration_context.range; port_declaration_context.range = $6; + port_declaration_context.data_type = 0; pform_make_reginit(@7, name, $9); @@ -3656,13 +3631,14 @@ port_declaration @2.first_line); pform_module_define_port(@2, name, NetNet::POUTPUT, NetNet::IMPLICIT_REG, IVL_VT_BOOL, - $4, use_range, $1); + $4, 0, use_range, $1); port_declaration_context.port_type = NetNet::POUTPUT; port_declaration_context.port_net_type = NetNet::IMPLICIT_REG; port_declaration_context.var_type = IVL_VT_BOOL; port_declaration_context.sign_flag = $4; delete port_declaration_context.range; port_declaration_context.range = use_range; + port_declaration_context.data_type = 0; delete[]$5; $$ = ptmp; } @@ -3675,13 +3651,14 @@ port_declaration @2.first_line); pform_module_define_port(@2, name, NetNet::POUTPUT, NetNet::IMPLICIT_REG, IVL_VT_BOOL, - $4, use_range, $1); + $4, 0, use_range, $1); port_declaration_context.port_type = NetNet::POUTPUT; port_declaration_context.port_net_type = NetNet::IMPLICIT_REG; port_declaration_context.var_type = IVL_VT_BOOL; port_declaration_context.sign_flag = $4; delete port_declaration_context.range; port_declaration_context.range = use_range; + port_declaration_context.data_type = 0; pform_make_reginit(@5, name, $7); @@ -3695,13 +3672,14 @@ port_declaration ptmp = pform_module_port_reference(name, @2.text, @2.first_line); pform_module_define_port(@2, name, NetNet::POUTPUT, - NetNet::WIRE, IVL_VT_REAL, true, 0, $1); + NetNet::WIRE, IVL_VT_REAL, true, 0, 0, $1); port_declaration_context.port_type = NetNet::POUTPUT; port_declaration_context.port_net_type = NetNet::WIRE; port_declaration_context.var_type = IVL_VT_REAL; port_declaration_context.sign_flag = true; delete port_declaration_context.range; port_declaration_context.range = 0; + port_declaration_context.data_type = 0; delete[]$4; $$ = ptmp; } diff --git a/parse_misc.h b/parse_misc.h index 037fefffe..5b18f72e6 100644 --- a/parse_misc.h +++ b/parse_misc.h @@ -35,6 +35,7 @@ struct vlltype { unsigned last_line; unsigned last_column; const char*text; + std::string get_fileline() const; }; # define YYLTYPE struct vlltype diff --git a/pform.cc b/pform.cc index a21ae1182..4b98c029a 100644 --- a/pform.cc +++ b/pform.cc @@ -45,6 +45,15 @@ map pform_modules; map pform_primitives; +std::string vlltype::get_fileline() const +{ + ostringstream buf; + buf << (text? text : "") << ":" << first_line; + string res = buf.str(); + return res; + +} + /* * Parse configuration file with format =, where key * is the hierarchical name of a valid parameter name, and value @@ -1893,6 +1902,7 @@ void pform_module_define_port(const struct vlltype&li, NetNet::Type type, ivl_variable_type_t data_type, bool signed_flag, + data_type_t*vtype, list*range, list*attr) { @@ -1906,6 +1916,37 @@ void pform_module_define_port(const struct vlltype&li, return; } + if (vtype) { + ivl_assert(li, data_type == IVL_VT_NO_TYPE); + ivl_assert(li, range == 0); + } + + if (vector_type_t*vec_type = dynamic_cast (vtype)) { + data_type = vec_type->base_type; + signed_flag = vec_type->signed_flag; + range = vec_type->pdims.get(); + + } else if (atom2_type_t*atype = dynamic_cast(vtype)) { + data_type = IVL_VT_BOOL; + signed_flag = atype->signed_flag; + range = make_range_from_width(atype->type_code); + + } else if (real_type_t*rtype = dynamic_cast(vtype)) { + data_type = IVL_VT_REAL; + signed_flag = true; + range = 0; + + if (rtype->type_code != real_type_t::REAL) { + VLerror(li, "sorry: Only real (not shortreal) supported here (%s:%d).", + __FILE__, __LINE__); + } + + } else if (vtype) { + VLerror(li, "sorry: Given type %s not supported here (%s:%d).", + typeid(*vtype).name(), __FILE__, __LINE__); + } + + // The default type for all flavor of ports is LOGIC. if (data_type == IVL_VT_NO_TYPE) data_type = IVL_VT_LOGIC; diff --git a/pform.h b/pform.h index c17a2e2b6..9fd2a3d80 100644 --- a/pform.h +++ b/pform.h @@ -160,6 +160,7 @@ extern void pform_module_define_port(const struct vlltype&li, NetNet::Type type, ivl_variable_type_t data_type, bool signed_flag, + data_type_t*vtype, list*range, list*attr); diff --git a/pform_types.h b/pform_types.h index ed3f20f53..6fe835e77 100644 --- a/pform_types.h +++ b/pform_types.h @@ -118,8 +118,9 @@ struct vector_type_t : public data_type_t { }; struct real_type_t : public data_type_t { - inline explicit real_type_t(int tc) : type_code(tc) { } - int type_code; + enum type_t { REAL, SHORTREAL }; + inline explicit real_type_t(type_t tc) : type_code(tc) { } + type_t type_code; }; struct class_type_t : public data_type_t { From 7e202bb5cafb38f353323e42fd52412a1647a4df Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Sun, 15 Apr 2012 09:57:42 -0700 Subject: [PATCH 021/179] Fix emit of struct ports/declarations. --- parse.y | 13 +++++++++---- vhdlpp/entity_emit.cc | 2 +- vhdlpp/vtype_emit.cc | 2 +- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/parse.y b/parse.y index 75491e45a..c1cbcb06b 100644 --- a/parse.y +++ b/parse.y @@ -3896,10 +3896,15 @@ module_attribute_foreign ; module_port_list_opt - : '(' list_of_ports ')' { $$ = $2; } - | '(' list_of_port_declarations ')' { $$ = $2; } - | { $$ = 0; } - ; + : '(' list_of_ports ')' { $$ = $2; } + | '(' list_of_port_declarations ')' { $$ = $2; } + | { $$ = 0; } + | '(' error ')' + { yyerror(@2, "Errors in port declarations."); + yyerrok; + $$ = 0; + } + ; /* Module declarations include optional ANSI style module parameter ports. These are simply advance ways to declare parameters, so diff --git a/vhdlpp/entity_emit.cc b/vhdlpp/entity_emit.cc index 6361c6eaf..f07f064b4 100644 --- a/vhdlpp/entity_emit.cc +++ b/vhdlpp/entity_emit.cc @@ -67,7 +67,7 @@ int Entity::emit(ostream&out) VType::decl_t&decl = declarations_[port->name]; - if (sep) out << sep; + if (sep) out << sep << endl; else sep = ", "; switch (port->mode) { diff --git a/vhdlpp/vtype_emit.cc b/vhdlpp/vtype_emit.cc index 8225bc917..1e06916ea 100644 --- a/vhdlpp/vtype_emit.cc +++ b/vhdlpp/vtype_emit.cc @@ -172,7 +172,7 @@ int VTypeRecord::emit_def(ostream&out, perm_string name) const out << "; "; } - out << "} "; + out << "} \\" << name << " "; return errors; } From b7b633b613dd27c7937426a6a7b51e5bf98662b8 Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Sun, 15 Apr 2012 15:45:49 -0700 Subject: [PATCH 022/179] module output ports use data_type_t types. This is in place of ad hoc type information passed to the pform_module_define_port function. --- parse.y | 191 ++++++++++++++++++++++++++------------------------ pform.cc | 2 + pform_types.h | 3 +- 3 files changed, 105 insertions(+), 91 deletions(-) diff --git a/parse.y b/parse.y index c1cbcb06b..2406d7c16 100644 --- a/parse.y +++ b/parse.y @@ -859,7 +859,14 @@ constraint_set /* IEEE1800-2005 A.1.9 */ data_type /* IEEE1800-2005: A.2.2.1 */ : integer_vector_type unsigned_signed_opt range_opt - { vector_type_t*tmp = new vector_type_t($1, $2, $3); + { ivl_variable_type_t use_vtype = $1; + bool reg_flag = false; + if (use_vtype == IVL_VT_NO_TYPE) { + use_vtype = IVL_VT_LOGIC; + reg_flag = true; + } + vector_type_t*tmp = new vector_type_t(use_vtype, $2, $3); + tmp->reg_flag = reg_flag; FILE_NAME(tmp, @1); $$ = tmp; } @@ -1065,7 +1072,7 @@ inside_expression /* IEEE1800-2005 A.8.3 */ ; integer_vector_type /* IEEE1800-2005: A.2.2.1 */ - : K_reg { $$ = IVL_VT_LOGIC; } + : K_reg { $$ = IVL_VT_NO_TYPE; } /* Usually a synonym for logic. */ | K_bit { $$ = IVL_VT_BOOL; } | K_logic { $$ = IVL_VT_LOGIC; } | K_bool { $$ = IVL_VT_BOOL; } /* Icarus Verilog xtypes extension */ @@ -3548,77 +3555,36 @@ port_declaration perm_string name = lex_strings.make($7); ptmp = pform_module_port_reference(name, @2.text, @2.first_line); - pform_module_define_port(@2, name, NetNet::POUTPUT, - $3, $4, $5, 0, $6, $1); + vector_type_t*tmp = new vector_type_t($4, $5, $6); + pform_module_define_port(@2, name, NetNet::POUTPUT, $3, IVL_VT_NO_TYPE, + false, tmp, 0, $1); port_declaration_context.port_type = NetNet::POUTPUT; port_declaration_context.port_net_type = $3; - port_declaration_context.var_type = $4; - port_declaration_context.sign_flag = $5; + port_declaration_context.var_type = IVL_VT_NO_TYPE; + port_declaration_context.sign_flag = false; delete port_declaration_context.range; - port_declaration_context.range = $6; - port_declaration_context.data_type = 0; + port_declaration_context.range = 0; + port_declaration_context.data_type = tmp; delete[]$7; $$ = ptmp; } | attribute_list_opt - K_output var_type primitive_type_opt unsigned_signed_opt range_opt IDENTIFIER + K_output K_reg primitive_type_opt unsigned_signed_opt range_opt IDENTIFIER { Module::port_t*ptmp; perm_string name = lex_strings.make($7); ptmp = pform_module_port_reference(name, @2.text, @2.first_line); - pform_module_define_port(@2, name, NetNet::POUTPUT, - $3, $4, $5, 0, $6, $1); + vector_type_t*tmp = new vector_type_t($4, $5, $6); + tmp->reg_flag = true; + pform_module_define_port(@2, name, NetNet::POUTPUT, NetNet::REG, IVL_VT_NO_TYPE, + false, tmp, 0, $1); port_declaration_context.port_type = NetNet::POUTPUT; - port_declaration_context.port_net_type = $3; - port_declaration_context.var_type = $4; - port_declaration_context.sign_flag = $5; + port_declaration_context.port_net_type = NetNet::REG; + port_declaration_context.var_type = IVL_VT_NO_TYPE; + port_declaration_context.sign_flag = false; delete port_declaration_context.range; - port_declaration_context.range = $6; - port_declaration_context.data_type = 0; - delete[]$7; - $$ = ptmp; - } - | attribute_list_opt - K_output var_type primitive_type_opt unsigned_signed_opt range_opt IDENTIFIER '=' expression - { Module::port_t*ptmp; - perm_string name = lex_strings.make($7); - ptmp = pform_module_port_reference(name, @2.text, - @2.first_line); - pform_module_define_port(@2, name, NetNet::POUTPUT, - $3, $4, $5, 0, $6, $1); - port_declaration_context.port_type = NetNet::POUTPUT; - port_declaration_context.port_net_type = $3; - port_declaration_context.var_type = $4; - port_declaration_context.sign_flag = $5; - delete port_declaration_context.range; - port_declaration_context.range = $6; - port_declaration_context.data_type = 0; - - pform_make_reginit(@7, name, $9); - - delete[]$7; - $$ = ptmp; - } - | attribute_list_opt - K_output net_type_opt primitive_type_opt unsigned_signed_opt range_opt IDENTIFIER '=' expression - { Module::port_t*ptmp; - perm_string name = lex_strings.make($7); - NetNet::Type t = ($3 == NetNet::IMPLICIT) ? NetNet::IMPLICIT_REG : $3; - - ptmp = pform_module_port_reference(name, @2.text, - @2.first_line); - pform_module_define_port(@2, name, NetNet::POUTPUT, - t, $4, $5, 0, $6, $1); - port_declaration_context.port_type = NetNet::POUTPUT; - port_declaration_context.port_net_type = t; - port_declaration_context.var_type = $4; - port_declaration_context.sign_flag = $5; - delete port_declaration_context.range; - port_declaration_context.range = $6; - port_declaration_context.data_type = 0; - - pform_make_reginit(@7, name, $9); - + port_declaration_context.range = 0; + port_declaration_context.data_type = tmp; delete[]$7; $$ = ptmp; } @@ -3626,42 +3592,19 @@ port_declaration K_output atom2_type signed_unsigned_opt IDENTIFIER { Module::port_t*ptmp; perm_string name = lex_strings.make($5); - list*use_range = make_range_from_width($3); ptmp = pform_module_port_reference(name, @2.text, @2.first_line); + atom2_type_t*tmp = new atom2_type_t($3, $4); pform_module_define_port(@2, name, NetNet::POUTPUT, - NetNet::IMPLICIT_REG, IVL_VT_BOOL, - $4, 0, use_range, $1); + NetNet::IMPLICIT_REG, IVL_VT_NO_TYPE, + false, tmp, 0, $1); port_declaration_context.port_type = NetNet::POUTPUT; port_declaration_context.port_net_type = NetNet::IMPLICIT_REG; - port_declaration_context.var_type = IVL_VT_BOOL; - port_declaration_context.sign_flag = $4; + port_declaration_context.var_type = IVL_VT_NO_TYPE; + port_declaration_context.sign_flag = false; delete port_declaration_context.range; - port_declaration_context.range = use_range; - port_declaration_context.data_type = 0; - delete[]$5; - $$ = ptmp; - } - | attribute_list_opt - K_output atom2_type signed_unsigned_opt IDENTIFIER '=' expression - { Module::port_t*ptmp; - perm_string name = lex_strings.make($5); - list*use_range = make_range_from_width($3); - ptmp = pform_module_port_reference(name, @2.text, - @2.first_line); - pform_module_define_port(@2, name, NetNet::POUTPUT, - NetNet::IMPLICIT_REG, IVL_VT_BOOL, - $4, 0, use_range, $1); - port_declaration_context.port_type = NetNet::POUTPUT; - port_declaration_context.port_net_type = NetNet::IMPLICIT_REG; - port_declaration_context.var_type = IVL_VT_BOOL; - port_declaration_context.sign_flag = $4; - delete port_declaration_context.range; - port_declaration_context.range = use_range; - port_declaration_context.data_type = 0; - - pform_make_reginit(@5, name, $7); - + port_declaration_context.range = 0; + port_declaration_context.data_type = tmp; delete[]$5; $$ = ptmp; } @@ -3683,6 +3626,74 @@ port_declaration delete[]$4; $$ = ptmp; } + | attribute_list_opt + K_output K_reg primitive_type_opt unsigned_signed_opt range_opt IDENTIFIER '=' expression + { Module::port_t*ptmp; + perm_string name = lex_strings.make($7); + ptmp = pform_module_port_reference(name, @2.text, + @2.first_line); + vector_type_t*tmp = new vector_type_t($4, $5, $6); + pform_module_define_port(@2, name, NetNet::POUTPUT, NetNet::REG, IVL_VT_NO_TYPE, + false, tmp, 0, $1); + port_declaration_context.port_type = NetNet::POUTPUT; + port_declaration_context.port_net_type = NetNet::REG; + port_declaration_context.var_type = IVL_VT_NO_TYPE; + port_declaration_context.sign_flag = false; + delete port_declaration_context.range; + port_declaration_context.range = 0; + port_declaration_context.data_type = tmp; + + pform_make_reginit(@7, name, $9); + + delete[]$7; + $$ = ptmp; + } + | attribute_list_opt + K_output net_type_opt primitive_type_opt unsigned_signed_opt range_opt IDENTIFIER '=' expression + { Module::port_t*ptmp; + perm_string name = lex_strings.make($7); + NetNet::Type t = ($3 == NetNet::IMPLICIT) ? NetNet::IMPLICIT_REG : $3; + ptmp = pform_module_port_reference(name, @2.text, + @2.first_line); + vector_type_t*tmp = new vector_type_t($4, $5, $6); + pform_module_define_port(@2, name, NetNet::POUTPUT, t, IVL_VT_NO_TYPE, + false, tmp, 0, $1); + port_declaration_context.port_type = NetNet::POUTPUT; + port_declaration_context.port_net_type = t; + port_declaration_context.var_type = IVL_VT_NO_TYPE; + port_declaration_context.sign_flag = false; + delete port_declaration_context.range; + port_declaration_context.range = 0; + port_declaration_context.data_type = tmp; + + pform_make_reginit(@7, name, $9); + + delete[]$7; + $$ = ptmp; + } + | attribute_list_opt + K_output atom2_type signed_unsigned_opt IDENTIFIER '=' expression + { Module::port_t*ptmp; + perm_string name = lex_strings.make($5); + ptmp = pform_module_port_reference(name, @2.text, + @2.first_line); + atom2_type_t*tmp = new atom2_type_t($3, $4); + pform_module_define_port(@2, name, NetNet::POUTPUT, + NetNet::IMPLICIT_REG, IVL_VT_NO_TYPE, + false, tmp, 0, $1); + port_declaration_context.port_type = NetNet::POUTPUT; + port_declaration_context.port_net_type = NetNet::IMPLICIT_REG; + port_declaration_context.var_type = IVL_VT_NO_TYPE; + port_declaration_context.sign_flag = false; + delete port_declaration_context.range; + port_declaration_context.range = 0; + port_declaration_context.data_type = tmp; + + pform_make_reginit(@5, name, $7); + + delete[]$5; + $$ = ptmp; + } ; diff --git a/pform.cc b/pform.cc index 4b98c029a..d62e35b36 100644 --- a/pform.cc +++ b/pform.cc @@ -1925,6 +1925,8 @@ void pform_module_define_port(const struct vlltype&li, data_type = vec_type->base_type; signed_flag = vec_type->signed_flag; range = vec_type->pdims.get(); + if (vec_type->reg_flag) + type = NetNet::REG; } else if (atom2_type_t*atype = dynamic_cast(vtype)) { data_type = IVL_VT_BOOL; diff --git a/pform_types.h b/pform_types.h index 6fe835e77..04ee7a0bd 100644 --- a/pform_types.h +++ b/pform_types.h @@ -111,9 +111,10 @@ struct atom2_type_t : public data_type_t { struct vector_type_t : public data_type_t { inline explicit vector_type_t(ivl_variable_type_t bt, bool sf, std::list*pd) - : base_type(bt), signed_flag(sf), pdims(pd) { } + : base_type(bt), signed_flag(sf), reg_flag(false), pdims(pd) { } ivl_variable_type_t base_type; bool signed_flag; + bool reg_flag; std::auto_ptr< list > pdims; }; From 67af96fee77f758c48073cd44471fe3fa86bcee9 Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Sun, 15 Apr 2012 17:58:07 -0700 Subject: [PATCH 023/179] Module output ports use data_type_or_implicit This cleans up the parsing of module output ports, allows for more complex types on the ports, and fixes some bugs. --- parse.y | 144 ++++++++++++------------------------------- pform_types.h | 24 +++++++- vhdlpp/vtype_emit.cc | 8 +-- 3 files changed, 63 insertions(+), 113 deletions(-) diff --git a/parse.y b/parse.y index 2406d7c16..fe6ba14c1 100644 --- a/parse.y +++ b/parse.y @@ -903,11 +903,13 @@ data_type_or_implicit /* IEEE1800-2005: A.2.2.1 */ { $$ = $1; } | signing range_opt { vector_type_t*tmp = new vector_type_t(IVL_VT_LOGIC, $1, $2); + tmp->implicit_flag = true; FILE_NAME(tmp, @1); $$ = tmp; } | range { vector_type_t*tmp = new vector_type_t(IVL_VT_LOGIC, false, $1); + tmp->implicit_flag = true; FILE_NAME(tmp, @1); $$ = tmp; } @@ -3549,62 +3551,32 @@ port_declaration delete[]$4; $$ = ptmp; } - | attribute_list_opt - K_output net_type_opt primitive_type_opt unsigned_signed_opt range_opt IDENTIFIER - { Module::port_t*ptmp; - perm_string name = lex_strings.make($7); - ptmp = pform_module_port_reference(name, @2.text, - @2.first_line); - vector_type_t*tmp = new vector_type_t($4, $5, $6); - pform_module_define_port(@2, name, NetNet::POUTPUT, $3, IVL_VT_NO_TYPE, - false, tmp, 0, $1); - port_declaration_context.port_type = NetNet::POUTPUT; - port_declaration_context.port_net_type = $3; - port_declaration_context.var_type = IVL_VT_NO_TYPE; - port_declaration_context.sign_flag = false; - delete port_declaration_context.range; - port_declaration_context.range = 0; - port_declaration_context.data_type = tmp; - delete[]$7; - $$ = ptmp; - } - | attribute_list_opt - K_output K_reg primitive_type_opt unsigned_signed_opt range_opt IDENTIFIER - { Module::port_t*ptmp; - perm_string name = lex_strings.make($7); - ptmp = pform_module_port_reference(name, @2.text, - @2.first_line); - vector_type_t*tmp = new vector_type_t($4, $5, $6); - tmp->reg_flag = true; - pform_module_define_port(@2, name, NetNet::POUTPUT, NetNet::REG, IVL_VT_NO_TYPE, - false, tmp, 0, $1); - port_declaration_context.port_type = NetNet::POUTPUT; - port_declaration_context.port_net_type = NetNet::REG; - port_declaration_context.var_type = IVL_VT_NO_TYPE; - port_declaration_context.sign_flag = false; - delete port_declaration_context.range; - port_declaration_context.range = 0; - port_declaration_context.data_type = tmp; - delete[]$7; - $$ = ptmp; - } - | attribute_list_opt - K_output atom2_type signed_unsigned_opt IDENTIFIER + | attribute_list_opt K_output net_type_opt data_type_or_implicit IDENTIFIER { Module::port_t*ptmp; perm_string name = lex_strings.make($5); - ptmp = pform_module_port_reference(name, @2.text, - @2.first_line); - atom2_type_t*tmp = new atom2_type_t($3, $4); - pform_module_define_port(@2, name, NetNet::POUTPUT, - NetNet::IMPLICIT_REG, IVL_VT_NO_TYPE, - false, tmp, 0, $1); + NetNet::Type use_type = $3; + if (use_type == NetNet::IMPLICIT) { + if (vector_type_t*dtype = dynamic_cast ($4)) { + if (dtype->reg_flag) + use_type = NetNet::REG; + else if (dtype->implicit_flag) + use_type = NetNet::IMPLICIT; + else + use_type = NetNet::IMPLICIT_REG; + } else if (dynamic_cast ($4)) { + use_type = NetNet::IMPLICIT_REG; + } + } + ptmp = pform_module_port_reference(name, @2.text, @2.first_line); + pform_module_define_port(@2, name, NetNet::POUTPUT, use_type, IVL_VT_NO_TYPE, + false, $4, 0, $1); port_declaration_context.port_type = NetNet::POUTPUT; - port_declaration_context.port_net_type = NetNet::IMPLICIT_REG; + port_declaration_context.port_net_type = use_type; port_declaration_context.var_type = IVL_VT_NO_TYPE; port_declaration_context.sign_flag = false; delete port_declaration_context.range; port_declaration_context.range = 0; - port_declaration_context.data_type = tmp; + port_declaration_context.data_type = $4; delete[]$5; $$ = ptmp; } @@ -3626,68 +3598,30 @@ port_declaration delete[]$4; $$ = ptmp; } - | attribute_list_opt - K_output K_reg primitive_type_opt unsigned_signed_opt range_opt IDENTIFIER '=' expression - { Module::port_t*ptmp; - perm_string name = lex_strings.make($7); - ptmp = pform_module_port_reference(name, @2.text, - @2.first_line); - vector_type_t*tmp = new vector_type_t($4, $5, $6); - pform_module_define_port(@2, name, NetNet::POUTPUT, NetNet::REG, IVL_VT_NO_TYPE, - false, tmp, 0, $1); - port_declaration_context.port_type = NetNet::POUTPUT; - port_declaration_context.port_net_type = NetNet::REG; - port_declaration_context.var_type = IVL_VT_NO_TYPE; - port_declaration_context.sign_flag = false; - delete port_declaration_context.range; - port_declaration_context.range = 0; - port_declaration_context.data_type = tmp; - - pform_make_reginit(@7, name, $9); - - delete[]$7; - $$ = ptmp; - } - | attribute_list_opt - K_output net_type_opt primitive_type_opt unsigned_signed_opt range_opt IDENTIFIER '=' expression - { Module::port_t*ptmp; - perm_string name = lex_strings.make($7); - NetNet::Type t = ($3 == NetNet::IMPLICIT) ? NetNet::IMPLICIT_REG : $3; - ptmp = pform_module_port_reference(name, @2.text, - @2.first_line); - vector_type_t*tmp = new vector_type_t($4, $5, $6); - pform_module_define_port(@2, name, NetNet::POUTPUT, t, IVL_VT_NO_TYPE, - false, tmp, 0, $1); - port_declaration_context.port_type = NetNet::POUTPUT; - port_declaration_context.port_net_type = t; - port_declaration_context.var_type = IVL_VT_NO_TYPE; - port_declaration_context.sign_flag = false; - delete port_declaration_context.range; - port_declaration_context.range = 0; - port_declaration_context.data_type = tmp; - - pform_make_reginit(@7, name, $9); - - delete[]$7; - $$ = ptmp; - } - | attribute_list_opt - K_output atom2_type signed_unsigned_opt IDENTIFIER '=' expression + | attribute_list_opt K_output net_type_opt data_type_or_implicit IDENTIFIER '=' expression { Module::port_t*ptmp; perm_string name = lex_strings.make($5); - ptmp = pform_module_port_reference(name, @2.text, - @2.first_line); - atom2_type_t*tmp = new atom2_type_t($3, $4); - pform_module_define_port(@2, name, NetNet::POUTPUT, - NetNet::IMPLICIT_REG, IVL_VT_NO_TYPE, - false, tmp, 0, $1); - port_declaration_context.port_type = NetNet::POUTPUT; - port_declaration_context.port_net_type = NetNet::IMPLICIT_REG; + NetNet::Type use_type = $3; + if (use_type == NetNet::IMPLICIT) { + if (vector_type_t*dtype = dynamic_cast ($4)) { + if (dtype->reg_flag) + use_type = NetNet::REG; + else + use_type = NetNet::IMPLICIT_REG; + } else { + use_type = NetNet::IMPLICIT_REG; + } + } + ptmp = pform_module_port_reference(name, @2.text, @2.first_line); + pform_module_define_port(@2, name, NetNet::POUTPUT, use_type, IVL_VT_NO_TYPE, + false, $4, 0, $1); + port_declaration_context.port_type = NetNet::PINOUT; + port_declaration_context.port_net_type = use_type; port_declaration_context.var_type = IVL_VT_NO_TYPE; port_declaration_context.sign_flag = false; delete port_declaration_context.range; port_declaration_context.range = 0; - port_declaration_context.data_type = tmp; + port_declaration_context.data_type = $4; pform_make_reginit(@5, name, $7); diff --git a/pform_types.h b/pform_types.h index 04ee7a0bd..08f4fced2 100644 --- a/pform_types.h +++ b/pform_types.h @@ -108,13 +108,33 @@ struct atom2_type_t : public data_type_t { bool signed_flag; }; +/* + * The vector_type_t class represents types in the old Verilog + * way. Some typical examples: + * + * logic signed [7:0] foo + * bit unsigned foo + * reg foo + * + * There are a few special cases: + * + * For the most part, Verilog treats "logic" and "reg" as synonyms, + * but there are a few cases where the parser needs to know the + * difference. So "reg_flag" is set to true if the IVL_VT_LOGIC type + * is due to the "reg" keyword. + * + * If there are no reg/logic/bit/bool keywords, then Verilog will + * assume the type is logic, but the context may need to know about + * this case, so the implicit_flag member is set to true in that case. + */ struct vector_type_t : public data_type_t { inline explicit vector_type_t(ivl_variable_type_t bt, bool sf, std::list*pd) - : base_type(bt), signed_flag(sf), reg_flag(false), pdims(pd) { } + : base_type(bt), signed_flag(sf), reg_flag(false), implicit_flag(false), pdims(pd) { } ivl_variable_type_t base_type; bool signed_flag; - bool reg_flag; + bool reg_flag; // True if "reg" was used + bool implicit_flag; // True if this type is implicitly logic/reg std::auto_ptr< list > pdims; }; diff --git a/vhdlpp/vtype_emit.cc b/vhdlpp/vtype_emit.cc index 1e06916ea..41c6dcc3b 100644 --- a/vhdlpp/vtype_emit.cc +++ b/vhdlpp/vtype_emit.cc @@ -72,9 +72,7 @@ int VTypeArray::emit_decl(ostream&out, perm_string name, bool reg_flag) const int errors = 0; assert(dimensions() == 1); - if (reg_flag) - out << "reg "; - else + if (!reg_flag) out << "wire "; errors += emit_def(out, name); @@ -135,9 +133,7 @@ int VTypePrimitive::emit_def(ostream&out, perm_string name) const int VTypePrimitive::emit_decl(ostream&out, perm_string name, bool reg_flag) const { int errors = 0; - if (reg_flag) - out << "reg "; - else + if (!reg_flag) out << "wire "; errors += emit_def(out, name); From 247f6a4ecffd9fd93afa20229128b356430e8a58 Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Sat, 21 Apr 2012 08:56:47 -0700 Subject: [PATCH 024/179] Handle properly parsed structs in module ports. --- pform.cc | 12 +++++++++++- pform_struct_type.cc | 2 +- pform_types.h | 1 + 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/pform.cc b/pform.cc index d62e35b36..30c2fae81 100644 --- a/pform.cc +++ b/pform.cc @@ -1906,6 +1906,8 @@ void pform_module_define_port(const struct vlltype&li, list*range, list*attr) { + struct_type_t*struct_type = 0; + PWire*cur = pform_get_wire_in_scope(name); if (cur) { ostringstream msg; @@ -1943,6 +1945,11 @@ void pform_module_define_port(const struct vlltype&li, __FILE__, __LINE__); } + } else if (struct_type = dynamic_cast(vtype)) { + data_type = figure_struct_base_type(struct_type); + signed_flag = false; + range = 0; + } else if (vtype) { VLerror(li, "sorry: Given type %s not supported here (%s:%d).", typeid(*vtype).name(), __FILE__, __LINE__); @@ -1958,7 +1965,10 @@ void pform_module_define_port(const struct vlltype&li, cur->set_signed(signed_flag); - if (range == 0) { + if (struct_type) { + cur->set_struct_type(struct_type); + + } else if (range == 0) { cur->set_range_scalar((type == NetNet::IMPLICIT) ? SR_PORT : SR_BOTH); } else { diff --git a/pform_struct_type.cc b/pform_struct_type.cc index 16ef1debd..6a41d5fc5 100644 --- a/pform_struct_type.cc +++ b/pform_struct_type.cc @@ -21,7 +21,7 @@ # include "parse_misc.h" # include "ivl_assert.h" -static ivl_variable_type_t figure_struct_base_type(struct_type_t*struct_type) +ivl_variable_type_t figure_struct_base_type(struct_type_t*struct_type) { ivl_variable_type_t base_type = IVL_VT_BOOL; diff --git a/pform_types.h b/pform_types.h index 08f4fced2..d89631501 100644 --- a/pform_types.h +++ b/pform_types.h @@ -100,6 +100,7 @@ struct struct_type_t : public data_type_t { bool packed_flag; std::auto_ptr< list > members; }; +extern ivl_variable_type_t figure_struct_base_type(struct_type_t*struct_type); struct atom2_type_t : public data_type_t { inline explicit atom2_type_t(int tc, bool flag) From 63b7fe059df67cabe1120f9e329b334a917ba86d Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Sun, 22 Apr 2012 11:42:16 -0700 Subject: [PATCH 025/179] Reword concat to handle aggregate arguments. When concatenation expressions have aggregate arguments, we need to get the type of the result down to the aggregate expressions so that it can know how to interpret the elements. --- vhdlpp/Makefile.in | 2 +- vhdlpp/debug.cc | 35 -------- vhdlpp/expression.cc | 62 ++++---------- vhdlpp/expression.h | 53 +++++++++++- vhdlpp/expression_debug.cc | 70 ++++++++++++++++ vhdlpp/expression_elaborate.cc | 146 ++++++++++++++++++++++++++++++++- vhdlpp/expression_emit.cc | 75 +++++++++++++---- vhdlpp/expression_evaluate.cc | 53 ++++++++++++ vhdlpp/expression_stream.cc | 13 ++- vhdlpp/parse.y | 13 ++- 10 files changed, 417 insertions(+), 105 deletions(-) create mode 100644 vhdlpp/expression_debug.cc diff --git a/vhdlpp/Makefile.in b/vhdlpp/Makefile.in index b7c9115c8..36e49fae6 100644 --- a/vhdlpp/Makefile.in +++ b/vhdlpp/Makefile.in @@ -68,7 +68,7 @@ O = main.o architec.o compiler.o entity.o \ lexor.o lexor_keyword.o parse.o \ parse_misc.o library.o vhdlreal.o vhdlint.o \ architec_emit.o entity_emit.o expression_emit.o sequential_emit.o vtype_emit.o \ - debug.o architec_debug.o sequential_debug.o \ + debug.o architec_debug.o expression_debug.o sequential_debug.o \ $M all: dep vhdlpp@EXEEXT@ diff --git a/vhdlpp/debug.cc b/vhdlpp/debug.cc index 6201a6325..3a2de4f5f 100644 --- a/vhdlpp/debug.cc +++ b/vhdlpp/debug.cc @@ -221,41 +221,6 @@ void ExpAggregate::choice_t::dump(ostream&out, int indent) const out << setw(indent) << "" << "?choice_t?" << endl; } -void ExpArithmetic::dump(ostream&out, int indent) const -{ - const char*fun_name = "?"; - switch (fun_) { - case PLUS: - fun_name = "+"; - break; - case MINUS: - fun_name = "-"; - break; - case MULT: - fun_name = "*"; - break; - case DIV: - fun_name = "/"; - break; - case MOD: - fun_name = "mod"; - break; - case REM: - fun_name = "rem"; - break; - case POW: - fun_name = "**"; - break; - case CONCAT: - fun_name = "&"; - break; - } - - out << setw(indent) << "" << "Arithmetic " << fun_name - << " at " << get_fileline() << endl; - dump_operands(out, indent+4); -} - void ExpAttribute::dump(ostream&out, int indent) const { out << setw(indent) << "" << "Attribute " << name_ diff --git a/vhdlpp/expression.cc b/vhdlpp/expression.cc index a6aee6e47..898c07475 100644 --- a/vhdlpp/expression.cc +++ b/vhdlpp/expression.cc @@ -160,6 +160,11 @@ Expression*ExpAggregate::choice_t::simple_expression(bool detach_flag) return res; } +prange_t*ExpAggregate::choice_t::range_expressions(void) +{ + return range_.get(); +} + ExpAggregate::element_t::element_t(list*fields, Expression*val) : fields_(fields? fields->size() : 0), val_(val) { @@ -184,56 +189,14 @@ ExpAggregate::element_t::~element_t() ExpArithmetic::ExpArithmetic(ExpArithmetic::fun_t op, Expression*op1, Expression*op2) : ExpBinary(op1, op2), fun_(op) { + // The xCONCAT type is not actually used. + assert(op != xCONCAT); } ExpArithmetic::~ExpArithmetic() { } -bool ExpArithmetic::evaluate(ScopeBase*scope, int64_t&val) const -{ - int64_t val1, val2; - bool rc; - - rc = eval_operand1(scope, val1); - if (rc == false) - return false; - - rc = eval_operand2(scope, val2); - if (rc == false) - return false; - - switch (fun_) { - case PLUS: - val = val1 + val2; - break; - case MINUS: - val = val1 - val2; - break; - case MULT: - val = val1 * val2; - break; - case DIV: - if (val2 == 0) - return false; - val = val1 / val2; - break; - case MOD: - if (val2 == 0) - return false; - val = val1 % val2; - break; - case REM: - return false; - case POW: - return false; - case CONCAT: - return false; - } - - return true; -} - /* * Store bitstrings in little-endian order. */ @@ -257,6 +220,17 @@ ExpCharacter::~ExpCharacter() { } +ExpConcat::ExpConcat(Expression*op1, Expression*op2) +: operand1_(op1), operand2_(op2) +{ +} + +ExpConcat::~ExpConcat() +{ + delete operand1_; + delete operand2_; +} + ExpConditional::ExpConditional(Expression*co, list*tru, list*fal) : cond_(co) diff --git a/vhdlpp/expression.h b/vhdlpp/expression.h index 4736a5d06..6e89174ce 100644 --- a/vhdlpp/expression.h +++ b/vhdlpp/expression.h @@ -61,6 +61,14 @@ class Expression : public LineInfo { // this may be called before the elaborate_expr method. virtual const VType*probe_type(Entity*ent, Architecture*arc) const; + // The fit_type virtual method is used by the ExpConcat class + // to probe the type of operands. The atype argument is the + // type of the ExpConcat expression itself. This expression + // returns its type as interpreted in this context. Really, + // this is mostly about helping aggregate expressions within + // concatenations to figure out their type. + virtual const VType*fit_type(Entity*ent, Architecture*arc, const VTypeArray*atype) const; + // This virtual method elaborates an expression. The ltype is // the type of the lvalue expression, if known, and can be // used to calculate the type for the expression being @@ -129,6 +137,8 @@ class ExpUnary : public Expression { ExpUnary(Expression*op1); virtual ~ExpUnary() =0; + const VType*fit_type(Entity*ent, Architecture*arc, const VTypeArray*atype) const; + protected: inline void write_to_stream_operand1(std::ostream&fd) { operand1_->write_to_stream(fd); } @@ -179,6 +189,10 @@ class ExpBinary : public Expression { class ExpAggregate : public Expression { public: + // A "choice" is only part of an element. It is the thing that + // is used to identify an element of the aggregate. It can + // represent the index (or range) of an array, or the name of + // a record member. class choice_t { public: // Create an "others" choice @@ -195,6 +209,8 @@ class ExpAggregate : public Expression { bool others() const; // Return expression if this represents a simple_expression. Expression*simple_expression(bool detach_flag =true); + // Return prange_t if this represents a range_expression + prange_t*range_expressions(void); void dump(ostream&out, int indent) const; @@ -212,6 +228,9 @@ class ExpAggregate : public Expression { bool alias_flag; }; + // Elements are the syntactic items in an aggregate + // expression. Each element expressions a bunch of fields + // (choices) and binds them to a single expression class element_t { public: explicit element_t(std::list*fields, Expression*val); @@ -236,6 +255,9 @@ class ExpAggregate : public Expression { ExpAggregate(std::list*el); ~ExpAggregate(); + + const VType*probe_type(Entity*ent, Architecture*arc) const; + const VType*fit_type(Entity*ent, Architecture*arc, const VTypeArray*atype) const; int elaborate_expr(Entity*ent, Architecture*arc, const VType*ltype); void write_to_stream(std::ostream&fd); int emit(ostream&out, Entity*ent, Architecture*arc); @@ -257,7 +279,7 @@ class ExpAggregate : public Expression { class ExpArithmetic : public ExpBinary { public: - enum fun_t { PLUS, MINUS, MULT, DIV, MOD, REM, POW, CONCAT }; + enum fun_t { PLUS, MINUS, MULT, DIV, MOD, REM, POW, xCONCAT }; public: ExpArithmetic(ExpArithmetic::fun_t op, Expression*op1, Expression*op2); @@ -269,9 +291,6 @@ class ExpArithmetic : public ExpBinary { virtual bool evaluate(ScopeBase*scope, int64_t&val) const; void dump(ostream&out, int indent = 0) const; - private: - int emit_concat_(ostream&out, Entity*ent, Architecture*arc); - private: fun_t fun_; }; @@ -305,6 +324,7 @@ class ExpBitstring : public Expression { explicit ExpBitstring(const char*); ~ExpBitstring(); + const VType*fit_type(Entity*ent, Architecture*arc, const VTypeArray*atype) const; int elaborate_expr(Entity*ent, Architecture*arc, const VType*ltype); void write_to_stream(std::ostream&fd); int emit(ostream&out, Entity*ent, Architecture*arc); @@ -321,6 +341,7 @@ class ExpCharacter : public Expression { ExpCharacter(char val); ~ExpCharacter(); + const VType*fit_type(Entity*ent, Architecture*arc, const VTypeArray*atype) const; int elaborate_expr(Entity*ent, Architecture*arc, const VType*ltype); void write_to_stream(std::ostream&fd); int emit(ostream&out, Entity*ent, Architecture*arc); @@ -337,6 +358,28 @@ class ExpCharacter : public Expression { char value_; }; +class ExpConcat : public Expression { + + public: + ExpConcat(Expression*op1, Expression*op2); + ~ExpConcat(); + + const VType*probe_type(Entity*ent, Architecture*arc) const; + int elaborate_expr(Entity*ent, Architecture*arc, const VType*ltype); + void write_to_stream(std::ostream&fd); + int emit(ostream&out, Entity*ent, Architecture*arc); + virtual bool evaluate(ScopeBase*scope, int64_t&val) const; + bool is_primary(void) const; + void dump(ostream&out, int indent = 0) const; + + private: + int elaborate_expr_array_(Entity*ent, Architecture*arc, const VTypeArray*ltype); + + private: + Expression*operand1_; + Expression*operand2_; +}; + /* * The conditional expression represents the VHDL when-else * expressions. Note that by the VHDL syntax rules, these cannot show @@ -474,6 +517,7 @@ class ExpName : public Expression { int elaborate_lval(Entity*ent, Architecture*arc, bool); int elaborate_rval(Entity*ent, Architecture*arc, const InterfacePort*); const VType* probe_type(Entity*ent, Architecture*arc) const; + const VType* fit_type(Entity*ent, Architecture*arc, const VTypeArray*host) const; int elaborate_expr(Entity*ent, Architecture*arc, const VType*ltype); void write_to_stream(std::ostream&fd); int emit(ostream&out, Entity*ent, Architecture*arc); @@ -538,6 +582,7 @@ class ExpString : public Expression { explicit ExpString(const char*); ~ExpString(); + const VType*fit_type(Entity*ent, Architecture*arc, const VTypeArray*atype) const; int elaborate_expr(Entity*ent, Architecture*arc, const VType*ltype); void write_to_stream(std::ostream&fd); int emit(ostream&out, Entity*ent, Architecture*arc); diff --git a/vhdlpp/expression_debug.cc b/vhdlpp/expression_debug.cc new file mode 100644 index 000000000..9754e842e --- /dev/null +++ b/vhdlpp/expression_debug.cc @@ -0,0 +1,70 @@ +/* + * Copyright (c) 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 + * General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +# include "entity.h" +# include "architec.h" +# include "expression.h" +# include +# include +# include +# include + +using namespace std; + +void ExpArithmetic::dump(ostream&out, int indent) const +{ + const char*fun_name = "?"; + switch (fun_) { + case PLUS: + fun_name = "+"; + break; + case MINUS: + fun_name = "-"; + break; + case MULT: + fun_name = "*"; + break; + case DIV: + fun_name = "/"; + break; + case MOD: + fun_name = "mod"; + break; + case REM: + fun_name = "rem"; + break; + case POW: + fun_name = "**"; + break; + case xCONCAT: + ivl_assert(*this, 0); + break; + } + + out << setw(indent) << "" << "Arithmetic " << fun_name + << " at " << get_fileline() << endl; + dump_operands(out, indent+4); +} + +void ExpConcat::dump(ostream&out, int indent) const +{ + out << setw(indent) << "" << "Concatenation at " << get_fileline() << endl; + operand1_->dump(out, indent); + operand2_->dump(out, indent); +} diff --git a/vhdlpp/expression_elaborate.cc b/vhdlpp/expression_elaborate.cc index 05c8fd4e3..3e7d3cc39 100644 --- a/vhdlpp/expression_elaborate.cc +++ b/vhdlpp/expression_elaborate.cc @@ -24,6 +24,7 @@ # include "vsignal.h" # include # include +# include "parse_types.h" # include "ivl_assert.h" using namespace std; @@ -39,6 +40,18 @@ const VType* Expression::probe_type(Entity*, Architecture*) const return 0; } +const VType* Expression::fit_type(Entity*ent, Architecture*arc, const VTypeArray*) const +{ + const VType*res = probe_type(ent,arc); + if (res == 0) { + cerr << get_fileline() << ": internal error: " + << "fit_type for " << typeid(*this).name() + << " is not implemented." << endl; + } + + return res; +} + const VType*ExpName::elaborate_adjust_type_with_range_(Entity*, Architecture*arc, const VType*type) { @@ -287,6 +300,47 @@ int ExpBinary::elaborate_exprs(Entity*ent, Architecture*arc, const VType*ltype) return errors; } +/* + * the default fit_type method for unary operator expressions is to + * return the fit_type for the operand. The assumption is that the + * operator doesn't change the type. + */ +const VType*ExpUnary::fit_type(Entity*ent, Architecture*arc, const VTypeArray*atype) const +{ + return operand1_->fit_type(ent, arc, atype); +} + +const VType*ExpAggregate::probe_type(Entity*ent, Architecture*arc) const +{ + return Expression::probe_type(ent, arc); +} + +const VType*ExpAggregate::fit_type(Entity*, Architecture*, const VTypeArray*host) const +{ + ivl_assert(*this, elements_.size() == 1); + size_t choice_count = elements_[0]->count_choices(); + + ivl_assert(*this, choice_count > 0); + vector ce (choice_count); + elements_[0]->map_choices(&ce[0]); + + ivl_assert(*this, ce.size() == 1); + prange_t*prange = ce[0].choice->range_expressions(); + ivl_assert(*this, prange); + + Expression*use_msb = prange->msb(); + Expression*use_lsb = prange->lsb(); + + ivl_assert(*this, host->dimensions() == 1); + vector range (1); + + range[0] = VTypeArray::range_t(use_msb, use_lsb); + + const VTypeArray*res = new VTypeArray(host->element_type(), range); + + return res; +} + int ExpAggregate::elaborate_expr(Entity*ent, Architecture*arc, const VType*ltype) { if (ltype == 0) { @@ -406,12 +460,26 @@ int ExpAttribute::elaborate_expr(Entity*ent, Architecture*arc, const VType*) return errors; } +const VType*ExpBitstring::fit_type(Entity*, Architecture*, const VTypeArray*atype) const +{ + // Really should check that this string can work with the + // array element type? + return atype->element_type(); +} + int ExpBitstring::elaborate_expr(Entity*, Architecture*, const VType*) { int errors = 0; return errors; } +const VType*ExpCharacter::fit_type(Entity*, Architecture*, const VTypeArray*atype) const +{ + // Really should check that this character can work with the + // array element type? + return atype->element_type(); +} + int ExpCharacter::elaborate_expr(Entity*, Architecture*, const VType*ltype) { ivl_assert(*this, ltype != 0); @@ -419,6 +487,54 @@ int ExpCharacter::elaborate_expr(Entity*, Architecture*, const VType*ltype) return 0; } +/* + * I don't know how to probe the type of a concatenation, quite yet. + */ +const VType*ExpConcat::probe_type(Entity*, Architecture*) const +{ + ivl_assert(*this, 0); + return 0; +} + +int ExpConcat::elaborate_expr(Entity*ent, Architecture*arc, const VType*ltype) +{ + int errors = 0; + + if (ltype == 0) { + ltype = probe_type(ent, arc); + } + + ivl_assert(*this, ltype != 0); + + if (const VTypeArray*atype = dynamic_cast(ltype)) { + errors += elaborate_expr_array_(ent, arc, atype); + } else { + errors += operand1_->elaborate_expr(ent, arc, ltype); + errors += operand2_->elaborate_expr(ent, arc, ltype); + } + + return errors; +} + +int ExpConcat::elaborate_expr_array_(Entity*ent, Architecture*arc, const VTypeArray*atype) +{ + int errors = 0; + + // For now, only support single-dimension arrays here. + ivl_assert(*this, atype->dimensions() == 1); + + const VType*type1 = operand1_->fit_type(ent, arc, atype); + ivl_assert(*this, type1); + + const VType*type2 = operand2_->fit_type(ent, arc, atype); + ivl_assert(*this, type2); + + errors += operand1_->elaborate_expr(ent, arc, type1); + errors += operand2_->elaborate_expr(ent, arc, type2); + + return errors; +} + const VType* ExpConditional::probe_type(Entity*, Architecture*) const { return 0; @@ -584,6 +700,11 @@ const VType* ExpName::probe_type(Entity*ent, Architecture*arc) const return 0; } +const VType* ExpName::fit_type(Entity*ent, Architecture*arc, const VTypeArray*)const +{ + return probe_type(ent, arc); +} + int ExpName::elaborate_expr(Entity*, Architecture*, const VType*ltype) { if (ltype) { @@ -601,8 +722,8 @@ const VType* ExpNameALL::probe_type(Entity*, Architecture*) const const VType* ExpRelation::probe_type(Entity*ent, Architecture*arc) const { - const VType*type1 = peek_operand1()->probe_type(ent, arc); - const VType*type2 = peek_operand2()->probe_type(ent, arc); + /* const VType*type1 = */ peek_operand1()->probe_type(ent, arc); + /* const VType*type2 = */ peek_operand2()->probe_type(ent, arc); return primitive_BOOLEAN; } @@ -626,6 +747,27 @@ int ExpRelation::elaborate_expr(Entity*ent, Architecture*arc, const VType*ltype) return errors; } +/* + * When a string appears in a concatenation, then the type of the + * string is an array with the same element type of the concatenation, + * but with elements for each character of the string. + */ +const VType*ExpString::fit_type(Entity*, Architecture*, const VTypeArray*atype) const +{ + vector range (atype->dimensions()); + + // Generate an array range for this string + ivl_assert(*this, range.size() == 1); + ExpInteger*use_msb = new ExpInteger(value_.size()); + ExpInteger*use_lsb = new ExpInteger(0); + FILE_NAME(use_msb, this); + FILE_NAME(use_lsb, this); + range[0] = VTypeArray::range_t(use_msb, use_lsb); + + VTypeArray*type = new VTypeArray(atype->element_type(), range); + return type; +} + int ExpString::elaborate_expr(Entity*, Architecture*, const VType*ltype) { ivl_assert(*this, ltype != 0); diff --git a/vhdlpp/expression_emit.cc b/vhdlpp/expression_emit.cc index 371cb3af6..5d8ec72bb 100644 --- a/vhdlpp/expression_emit.cc +++ b/vhdlpp/expression_emit.cc @@ -20,6 +20,7 @@ # include "expression.h" # include "vtype.h" # include "architec.h" +# include "parse_types.h" # include # include # include @@ -171,8 +172,43 @@ int ExpAggregate::emit_array_(ostream&out, Entity*ent, Architecture*arc, const V continue; } + // If this is a range choice, then calculate the bounds + // of the range and scan through the values, mapping the + // value to the aggregate_[idx] element. + if (prange_t*range = aggregate_[idx].choice->range_expressions()) { + int64_t begin_val, end_val; + + if (! range->msb()->evaluate(ent, arc, begin_val)) { + cerr << range->msb()->get_fileline() << ": error: " + << "Unable to evaluate aggregate choice expression." << endl; + errors += 1; + continue; + } + + if (! range->lsb()->evaluate(ent, arc, end_val)) { + cerr << range->msb()->get_fileline() << ": error: " + << "Unable to evaluate aggregate choice expression." << endl; + errors += 1; + continue; + } + + if (begin_val < end_val) { + int64_t tmp = begin_val; + begin_val = end_val; + end_val = tmp; + } + + while (begin_val >= end_val) { + element_map[begin_val] = &aggregate_[idx]; + begin_val -= 1; + } + + continue; + } + int64_t tmp_val; Expression*tmp = aggregate_[idx].choice->simple_expression(false); + ivl_assert(*this, tmp); // Named aggregate element. Once we see one of // these, we can no longer accept positional @@ -202,6 +238,8 @@ int ExpAggregate::emit_array_(ostream&out, Entity*ent, Architecture*arc, const V out << ", "; if (cur == 0) { out << "/* Missing element " << idx << " */"; + cerr << get_fileline() << ": error: " + << "Missing element " << idx << "." << endl; errors += 1; } else { errors += cur->expr->emit(out, ent, arc); @@ -247,9 +285,6 @@ int ExpArithmetic::emit(ostream&out, Entity*ent, Architecture*arc) { int errors = 0; - if (fun_ == CONCAT) - return emit_concat_(out, ent, arc); - errors += emit_operand1(out, ent, arc); switch (fun_) { @@ -274,7 +309,8 @@ int ExpArithmetic::emit(ostream&out, Entity*ent, Architecture*arc) case REM: out << " /* ?remainder? */ "; break; - case CONCAT: + case xCONCAT: + ivl_assert(*this, 0); out << " /* ?concat? */ "; break; } @@ -284,17 +320,6 @@ int ExpArithmetic::emit(ostream&out, Entity*ent, Architecture*arc) return errors; } -int ExpArithmetic::emit_concat_(ostream&out, Entity*ent, Architecture*arc) -{ - int errors = 0; - out << "{"; - errors += emit_operand1(out, ent, arc); - out << ", "; - errors += emit_operand2(out, ent, arc); - out << "}"; - return errors; -} - int ExpBitstring::emit(ostream&out, Entity*, Architecture*) { int errors = 0; @@ -361,6 +386,26 @@ bool ExpCharacter::is_primary(void) const return true; } +/* + * This is not exactly a "primary", but it is wrapped in its own + * parentheses (braces) so we return true here. + */ +bool ExpConcat::is_primary(void) const +{ + return true; +} + +int ExpConcat::emit(ostream&out, Entity*ent, Architecture*arc) +{ + int errors = 0; + out << "{"; + errors += operand1_->emit(out, ent, arc); + out << ", "; + errors += operand2_->emit(out, ent, arc); + out << "}"; + return errors; +} + int ExpConditional::emit(ostream&out, Entity*ent, Architecture*arc) { int errors = 0; diff --git a/vhdlpp/expression_evaluate.cc b/vhdlpp/expression_evaluate.cc index 62c3929b7..ffc646b3b 100644 --- a/vhdlpp/expression_evaluate.cc +++ b/vhdlpp/expression_evaluate.cc @@ -32,6 +32,50 @@ bool Expression::evaluate(Entity*, Architecture*arc, int64_t&val) const } +bool ExpArithmetic::evaluate(ScopeBase*scope, int64_t&val) const +{ + int64_t val1, val2; + bool rc; + + rc = eval_operand1(scope, val1); + if (rc == false) + return false; + + rc = eval_operand2(scope, val2); + if (rc == false) + return false; + + switch (fun_) { + case PLUS: + val = val1 + val2; + break; + case MINUS: + val = val1 - val2; + break; + case MULT: + val = val1 * val2; + break; + case DIV: + if (val2 == 0) + return false; + val = val1 / val2; + break; + case MOD: + if (val2 == 0) + return false; + val = val1 % val2; + break; + case REM: + return false; + case POW: + return false; + case xCONCAT: // not possible + return false; + } + + return true; +} + bool ExpAttribute::evaluate(ScopeBase*, int64_t&val) const { /* Special Case: The length attribute can be calculated all @@ -71,6 +115,15 @@ bool ExpAttribute::evaluate(Entity*, Architecture*arc, int64_t&val) const return evaluate(arc, val); } +/* + * I don't yet know how to evaluate concatenations. It is not likely + * to come up anyhow. + */ +bool ExpConcat::evaluate(ScopeBase*scope, int64_t&val) const +{ + return false; +} + bool ExpName::evaluate(ScopeBase*scope, int64_t&val) const { const VType*type; diff --git a/vhdlpp/expression_stream.cc b/vhdlpp/expression_stream.cc index 16b47fae0..1f2ff23ed 100644 --- a/vhdlpp/expression_stream.cc +++ b/vhdlpp/expression_stream.cc @@ -56,8 +56,8 @@ void ExpArithmetic::write_to_stream(ostream&out) case POW: out << "**"; break; - case CONCAT: - out << "&"; + case xCONCAT: + ivl_assert(*this, 0); break; } @@ -81,6 +81,15 @@ void ExpCharacter::write_to_stream(ostream&) ivl_assert(*this, !"Not supported"); } +void ExpConcat::write_to_stream(ostream&fd) +{ + fd << "("; + operand1_->write_to_stream(fd); + fd << ")&("; + operand2_->write_to_stream(fd); + fd << ")"; +} + void ExpConditional::write_to_stream(ostream&) { ivl_assert(*this, !"Not supported"); diff --git a/vhdlpp/parse.y b/vhdlpp/parse.y index 349f49b7c..5da156fc7 100644 --- a/vhdlpp/parse.y +++ b/vhdlpp/parse.y @@ -382,7 +382,7 @@ design_file : { yylloc.text = file_path; } design_units ; adding_operator : '+' { $$ = ExpArithmetic::PLUS; } | '-' { $$ = ExpArithmetic::MINUS; } - | '&' { $$ = ExpArithmetic::CONCAT; } + | '&' { $$ = ExpArithmetic::xCONCAT; } ; architecture_body @@ -2024,12 +2024,21 @@ signal_declaration_assign_opt * however, is right-recursive, which is not to nice is real LALR * parsers. The solution is to rewrite it as below, to make it * left-recursive. This is must more effecient use of the parse stack. + * + * Note that although the concatenation operator '&' is syntactically + * an addition operator, it is handled differently during elaboration + * so detect it and create a different expression type. */ simple_expression : term { $$ = $1; } | simple_expression adding_operator term - { ExpArithmetic*tmp = new ExpArithmetic($2, $1, $3); + { Expression*tmp; + if ($2 == ExpArithmetic::xCONCAT) { + tmp = new ExpConcat($1, $3); + } else { + tmp = new ExpArithmetic($2, $1, $3); + } FILE_NAME(tmp, @2); $$ = tmp; } From e7a974347eb5ace09da5fc77b0f116add6c0c1b4 Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Sun, 22 Apr 2012 19:08:22 -0700 Subject: [PATCH 026/179] Handle packed array slices at module ports. --- elab_net.cc | 27 +++++++++++++++++++-------- tgt-vvp/vvp_scope.c | 35 +++++++++++++++++++++++++++-------- 2 files changed, 46 insertions(+), 16 deletions(-) diff --git a/elab_net.cc b/elab_net.cc index db70c4069..cb574ea3b 100644 --- a/elab_net.cc +++ b/elab_net.cc @@ -364,15 +364,26 @@ bool PEIdent::eval_part_select_(Design*des, NetScope*scope, NetNet*sig, bool bit_defined_flag; /* bool flag = */ calculate_bits_(des, scope, msb, bit_defined_flag); ivl_assert(*this, bit_defined_flag); - midx = sig->sb_to_idx(prefix_indices, msb); - if (midx >= (long)sig->vector_width()) { - cerr << get_fileline() << ": error: Index " << sig->name() - << "[" << msb << "] is out of range." - << endl; - des->errors += 1; - midx = 0; + + if (prefix_indices.size()+2 <= sig->packed_dims().size()) { + long tmp_loff; + unsigned long tmp_lwid; + bool rcl = sig->sb_to_slice(prefix_indices, msb, + tmp_loff, tmp_lwid); + ivl_assert(*this, rcl); + midx = tmp_loff + tmp_lwid - 1; + lidx = tmp_loff; + } else { + midx = sig->sb_to_idx(prefix_indices, msb); + if (midx >= (long)sig->vector_width()) { + cerr << get_fileline() << ": error: Index " << sig->name() + << "[" << msb << "] is out of range." + << endl; + des->errors += 1; + midx = 0; + } + lidx = midx; } - lidx = midx; } else { cerr << get_fileline() << ": internal error: " diff --git a/tgt-vvp/vvp_scope.c b/tgt-vvp/vvp_scope.c index edb29b5e7..37555c5ca 100644 --- a/tgt-vvp/vvp_scope.c +++ b/tgt-vvp/vvp_scope.c @@ -427,14 +427,19 @@ static void draw_reg_in_scope(ivl_signal_t sig) { int msb; int lsb; - if (ivl_signal_packed_dimensions(sig) > 1) { - // FIX ME: Improve this when vvp becomes aware of packed - // arrays. + switch (ivl_signal_packed_dimensions(sig)) { + case 0: + msb = 0; + lsb = 0; + break; + case 1: + msb = ivl_signal_packed_msb(sig, 0); + lsb = ivl_signal_packed_lsb(sig, 0); + break; + default: msb = ivl_signal_width(sig) - 1; lsb = 0; - } else { - msb = ivl_signal_msb(sig); - lsb = ivl_signal_lsb(sig); + break; } const char*datatype_flag = ivl_signal_integer(sig) ? "/i" : @@ -483,8 +488,22 @@ static void draw_reg_in_scope(ivl_signal_t sig) */ static void draw_net_in_scope(ivl_signal_t sig) { - int msb = ivl_signal_msb(sig); - int lsb = ivl_signal_lsb(sig); + int msb; + int lsb; + switch (ivl_signal_packed_dimensions(sig)) { + case 0: + msb = 0; + lsb = 0; + break; + case 1: + msb = ivl_signal_packed_msb(sig, 0); + lsb = ivl_signal_packed_lsb(sig, 0); + break; + default: + msb = ivl_signal_width(sig) - 1; + lsb = 0; + break; + } const char*datatype_flag = ivl_signal_signed(sig)? "/s" : ""; const char*local_flag = ivl_signal_local(sig)? "*" : ""; From 039e6014fe095f782c5ca11a5473b7943fa94b96 Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Sat, 28 Apr 2012 18:37:22 -0700 Subject: [PATCH 027/179] Rework VType::emit_def methods / use packed arrays to implement arrays. VHDL arrays are more like SV packed arrays, so use packed arrays to implement them. --- vhdlpp/vtype.h | 32 +++-------- vhdlpp/vtype_emit.cc | 124 ++++++++++++++++--------------------------- 2 files changed, 54 insertions(+), 102 deletions(-) diff --git a/vhdlpp/vtype.h b/vhdlpp/vtype.h index ac15a68b3..fa0d0058f 100644 --- a/vhdlpp/vtype.h +++ b/vhdlpp/vtype.h @@ -52,13 +52,13 @@ class VType { // This virtual method emits a definition for the specific // type. It is used to emit typedef's. - virtual int emit_def(std::ostream&out, perm_string name) const =0; + virtual int emit_def(std::ostream&out) const =0; private: friend class decl_t; // This virtual method is called to emit the declaration. This // is used by the decl_t object to emit variable/wire/port declarations. - virtual int emit_decl(std::ostream&out, perm_string name, bool reg_flag) const =0; + virtual int emit_decl(std::ostream&out, perm_string name, bool reg_flag) const; public: // A couple places use the VType along with a few @@ -101,10 +101,7 @@ class VTypePrimitive : public VType { type_t type() const { return type_; } int emit_primitive_type(std::ostream&fd) const; - - int emit_def(std::ostream&out, perm_string name) const; - private: - int emit_decl(std::ostream&out, perm_string name, bool reg_flag) const; + int emit_def(std::ostream&out) const; private: type_t type_; @@ -155,10 +152,7 @@ class VTypeArray : public VType { const VType* element_type() const; - int emit_def(std::ostream&out, perm_string name) const; - - private: - int emit_decl(std::ostream&out, perm_string name, bool reg_flag) const; + int emit_def(std::ostream&out) const; private: const VType*etype_; @@ -174,10 +168,7 @@ class VTypeRange : public VType { ~VTypeRange(); void write_to_stream(std::ostream&fd) const; - - int emit_def(std::ostream&out, perm_string name) const; - private: - int emit_decl(std::ostream&out, perm_string name, bool reg_flag) const; + int emit_def(std::ostream&out) const; private: const VType*base_; @@ -191,10 +182,7 @@ class VTypeEnum : public VType { ~VTypeEnum(); void show(std::ostream&) const; - - int emit_def(std::ostream&out, perm_string name) const; - private: - int emit_decl(std::ostream&out, perm_string name, bool reg_flag) const; + int emit_def(std::ostream&out) const; private: std::vectornames_; @@ -227,14 +215,10 @@ class VTypeRecord : public VType { void write_to_stream(std::ostream&fd) const; void show(std::ostream&) const; - - int emit_def(std::ostream&out, perm_string name) const; + int emit_def(std::ostream&out) const; const element_t* element_by_name(perm_string name) const; - private: - int emit_decl(std::ostream&out, perm_string name, bool reg_flag) const; - private: std::vector elements_; }; @@ -247,7 +231,7 @@ class VTypeDef : public VType { int emit_typedef(std::ostream&out) const; - int emit_def(std::ostream&out, perm_string name) const; + int emit_def(std::ostream&out) const; private: int emit_decl(std::ostream&out, perm_string name, bool reg_flag) const; diff --git a/vhdlpp/vtype_emit.cc b/vhdlpp/vtype_emit.cc index 41c6dcc3b..2dbe36427 100644 --- a/vhdlpp/vtype_emit.cc +++ b/vhdlpp/vtype_emit.cc @@ -33,54 +33,54 @@ int VType::decl_t::emit(ostream&out, perm_string name) const } -int VTypeArray::emit_def(ostream&out, perm_string name) const +int VType::emit_decl(ostream&out, perm_string name, bool reg_flag) const { int errors = 0; - if (const VTypeArray*sub = dynamic_cast (etype_)) { - sub->emit_def(out, name); - assert(dimensions() == 1); - out << "["; - errors += dimension(0).msb()->emit(out, 0, 0); - out << ":"; - errors += dimension(0).lsb()->emit(out, 0, 0); - out << "] "; - return errors; + if (!reg_flag) + out << "wire "; + + errors += emit_def(out); + + out << " \\" << name << " "; + return errors; +} + +int VTypeArray::emit_def(ostream&out) const +{ + int errors = 0; + + list dims; + const VTypeArray*cur = this; + while (const VTypeArray*sub = dynamic_cast (cur->etype_)) { + dims.push_back(cur); + cur = sub; } - const VTypePrimitive*base = dynamic_cast (etype_); + const VTypePrimitive*base = dynamic_cast (cur->etype_); assert(base != 0); assert(dimensions() == 1); base->emit_primitive_type(out); if (signed_flag_) - out << "signed "; + out << " signed"; - out << "["; - errors += dimension(0).msb()->emit(out, 0, 0); - out << ":"; - errors += dimension(0).lsb()->emit(out, 0, 0); - out << "] "; + dims.push_back(cur); - out << "\\" << name << " "; + while (! dims.empty()) { + cur = dims.front(); + dims.pop_front(); + out << "["; + errors += cur->dimension(0).msb()->emit(out, 0, 0); + out << ":"; + errors += cur->dimension(0).lsb()->emit(out, 0, 0); + out << "]"; + } return errors; } -int VTypeArray::emit_decl(ostream&out, perm_string name, bool reg_flag) const -{ - int errors = 0; - assert(dimensions() == 1); - - if (!reg_flag) - out << "wire "; - - errors += emit_def(out, name); - - return errors; -} - -int VTypeEnum::emit_def(ostream&out, perm_string name) const +int VTypeEnum::emit_def(ostream&out) const { int errors = 0; out << "enum {"; @@ -89,31 +89,24 @@ int VTypeEnum::emit_def(ostream&out, perm_string name) const for (size_t idx = 1 ; idx < names_.size() ; idx += 1) out << ", \\" << names_[idx] << " "; - out << "} \\" << name << " "; + out << "}"; return errors; } -int VTypeEnum::emit_decl(ostream&out, perm_string name, bool reg_flag) const -{ - int errors = 0; - errors += emit_def(out, name); - return errors; -} - int VTypePrimitive::emit_primitive_type(ostream&out) const { int errors = 0; switch (type_) { case BOOLEAN: case BIT: - out << "bool "; + out << "bool"; break; case STDLOGIC: - out << "logic "; + out << "logic"; break; case INTEGER: - out << "bool [31:0] "; + out << "bool [31:0]"; break; default: assert(0); @@ -122,40 +115,22 @@ int VTypePrimitive::emit_primitive_type(ostream&out) const return errors; } -int VTypePrimitive::emit_def(ostream&out, perm_string name) const +int VTypePrimitive::emit_def(ostream&out) const { int errors = 0; errors += emit_primitive_type(out); - out << "\\" << name << " "; return errors; } -int VTypePrimitive::emit_decl(ostream&out, perm_string name, bool reg_flag) const -{ - int errors = 0; - if (!reg_flag) - out << "wire "; - - errors += emit_def(out, name); - - return errors; -} - -int VTypeRange::emit_def(ostream&out, perm_string name) const +int VTypeRange::emit_def(ostream&out) const { int errors = 0; assert(0); + out << "/* Internal error: Don't know how to emit type */"; return errors; } -int VTypeRange::emit_decl(ostream&out, perm_string name, bool reg_flag) const -{ - int errors = 0; - assert(0); - return errors; -} - -int VTypeRecord::emit_def(ostream&out, perm_string name) const +int VTypeRecord::emit_def(ostream&out) const { int errors = 0; out << "struct packed {"; @@ -164,22 +139,15 @@ int VTypeRecord::emit_def(ostream&out, perm_string name) const ; cur != elements_.end() ; ++cur) { perm_string element_name = (*cur)->peek_name(); const VType*element_type = (*cur)->peek_type(); - element_type->emit_def(out, element_name); - out << "; "; + element_type->emit_def(out); + out << " \\" << element_name << " ; "; } - out << "} \\" << name << " "; + out << "}"; return errors; } -int VTypeRecord::emit_decl(ostream&out, perm_string name, bool reg_flag) const -{ - int errors = 0; - errors += emit_def(out, name); - return errors; -} - -int VTypeDef::emit_def(ostream&out, perm_string name) const +int VTypeDef::emit_def(ostream&) const { int errors = 0; assert(0); @@ -202,7 +170,7 @@ int VTypeDef::emit_typedef(ostream&out) const { int errors = 0; out << "typedef "; - errors += type_->emit_def(out, name_); - out << ";" << endl; + errors += type_->emit_def(out); + out << " \\" << name_ << " ;" << endl; return errors; } From 315ac4f17926ce97a6cf09512c8dbb74487acc29 Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Sun, 29 Apr 2012 13:46:37 -0700 Subject: [PATCH 028/179] support part select of struct members that are packed arrays. --- elab_expr.cc | 108 +++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 91 insertions(+), 17 deletions(-) diff --git a/elab_expr.cc b/elab_expr.cc index f0b6a2896..752568d17 100644 --- a/elab_expr.cc +++ b/elab_expr.cc @@ -1442,6 +1442,58 @@ static const netstruct_t::member_t*get_struct_member(const LineInfo*li, return type->packed_member(method_name, off); } + +bool calculate_part(const LineInfo*li, Design*des, NetScope*scope, + const index_component_t&index, long&off, unsigned long&wid) +{ + // Evaluate the last index expression into a constant long. + NetExpr*texpr = elab_and_eval(des, scope, index.msb, -1, true); + long msb; + if (texpr == 0 || !eval_as_long(msb, texpr)) { + cerr << li->get_fileline() << ": error: " + "Array/part index expressions must be constant here." << endl; + des->errors += 1; + return false; + } + + delete texpr; + + long lsb = msb; + if (index.lsb) { + texpr = elab_and_eval(des, scope, index.lsb, -1, true); + if (texpr==0 || !eval_as_long(lsb, texpr)) { + cerr << li->get_fileline() << ": error: " + "Array/part index expressions must be constant here." << endl; + des->errors += 1; + return false; + } + + delete texpr; + } + + switch (index.sel) { + case index_component_t::SEL_BIT: + off = msb; + wid = 1; + return true; + + case index_component_t::SEL_PART: + if (msb >= lsb) { + off = lsb; + wid = msb - lsb + 1; + } else { + off = msb; + wid = lsb - msb + 1; + } + return true; + + default: + ivl_assert(*li, 0); + break; + } + return true; +} + /* * Test if the tail name (method_name argument) is a member name and * the net is a struct. If that turns out to be the case, and the @@ -1457,39 +1509,53 @@ static NetExpr* check_for_struct_members(const LineInfo*li, comp.name, off); if (mem == 0) return 0; - if (debug_elaborate) { - cerr << li->get_fileline() << ": debug: Found struct member " << mem->name - << " At offset " << off << endl; - } - unsigned use_width = mem->width(); + if (debug_elaborate) { + cerr << li->get_fileline() << ": check_for_struct_members: " + << "Found struct member " << mem->name + << " At offset " << off + << ", member width = " << use_width << endl; + } + if ( ! comp.index.empty() ) { // Evaluate all but the last index expression, into prefix_indices. listprefix_indices; bool rc = evaluate_index_prefix(des, scope, prefix_indices, comp.index); ivl_assert(*li, rc); - // Evaluate the last index expression into a constant long. - NetExpr*texpr = elab_and_eval(des, scope, comp.index.back().msb, -1, true); - long tmp; - if (texpr == 0 || !eval_as_long(tmp, texpr)) { - cerr << li->get_fileline() << ": error: " - "Array index expressions must be constant here." << endl; - des->errors += 1; - return false; - } + // Make sure that index values that select array + // elements are in fact like bit selects. The tail may + // be part selects only if we are taking the part-select + // of the word of an array. + ivl_assert(*li, comp.index.size() >= mem->packed_dims.size() || comp.index.back().sel == index_component_t::SEL_BIT); - delete texpr; + // Evaluate the part/bit select expressions. This may be + // a bit select or a part select. In any case, assume + // the arguments are constant and generate a part select + // of the appropriate width. + long poff = 0; + unsigned long pwid = 0; + rc = calculate_part(li, des, scope, comp.index.back(), poff, pwid); + ivl_assert(*li, rc); // Now use the prefix_to_slice function to calculate the // offset and width of the addressed slice of the member. long loff; unsigned long lwid; - prefix_to_slice(mem->packed_dims, prefix_indices, tmp, loff, lwid); + prefix_to_slice(mem->packed_dims, prefix_indices, poff, loff, lwid); + + if (debug_elaborate) { + cerr << li->get_fileline() << ": check_for_struct_members: " + << "Evaluate prefix gives slice loff=" << loff + << ", lwid=" << lwid << ", part select pwid=" << pwid << endl; + } off += loff; - use_width = lwid; + if (comp.index.size() >= mem->packed_dims.size()) + use_width = pwid; + else + use_width = lwid; } NetESignal*sig = new NetESignal(net); @@ -2288,6 +2354,9 @@ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope, scope->is_const_func(false); } + if (debug_elaborate) + cerr << get_fileline() << ": PEIdent::elaborate_expr: path_=" << path_ << endl; + NetScope*found_in = symbol_search(this, des, scope, path_, net, par, eve, ex1, ex2); @@ -2388,6 +2457,11 @@ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope, name_component_t member_comp = use_path.back(); use_path.pop_back(); + if (debug_elaborate) + cerr << get_fileline() << ": PEIdent::elaborate_expr: " + << "Look for base_path " << use_path + << " for member " << member_comp << "." << endl; + found_in = symbol_search(this, des, scope, use_path, net, par, eve, ex1, ex2); From 4748f0cb5e81c81015593583f409dbc81c28a2c9 Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Sat, 5 May 2012 15:15:45 -0700 Subject: [PATCH 029/179] Handle incomplete type declarations Use these in the package library to allow for arbitrary mixing of type declaration and use. This makes writing libraries much easier. --- vhdlpp/package.cc | 45 ++++++++++++++++++++++++++++++++++++++---- vhdlpp/parse.y | 21 ++++++++++++++++---- vhdlpp/scope.h | 4 ++++ vhdlpp/vtype.cc | 12 +++++++++++ vhdlpp/vtype.h | 13 +++++++++++- vhdlpp/vtype_stream.cc | 22 ++++++++++++++++++--- 6 files changed, 105 insertions(+), 12 deletions(-) diff --git a/vhdlpp/package.cc b/vhdlpp/package.cc index b9bbe58a8..05559886f 100644 --- a/vhdlpp/package.cc +++ b/vhdlpp/package.cc @@ -40,6 +40,43 @@ void Package::write_to_stream(ostream&fd) const { fd << "package " << name_ << " is" << endl; + // Start out pre-declaring all the type definitions so that + // there is no confusion later in the package between types + // and identifiers. + for (map::const_iterator cur = old_types_.begin() + ; cur != old_types_.end() ; ++cur) { + const VTypeDef*def = dynamic_cast (cur->second); + if (def == 0) + continue; + fd << "type " << cur->first << ";" << endl; + } + + for (map::const_iterator cur = new_types_.begin() + ; cur != new_types_.end() ; ++cur) { + const VTypeDef*def = dynamic_cast (cur->second); + if (def == 0) + continue; + fd << "type " << cur->first << ";" << endl; + } + + for (map::const_iterator cur = old_constants_.begin() + ; cur != old_constants_.end() ; ++ cur) { + fd << "constant " << cur->first << ": "; + cur->second->typ->write_to_stream(fd); + fd << " := "; + cur->second->val->write_to_stream(fd); + fd << ";" << endl; + } + + for (map::const_iterator cur = new_constants_.begin() + ; cur != new_constants_.end() ; ++ cur) { + fd << "constant " << cur->first << ": "; + cur->second->typ->write_to_stream(fd); + fd << " := "; + cur->second->val->write_to_stream(fd); + fd << ";" << endl; + } + for (map::const_iterator cur = old_types_.begin() ; cur != old_types_.end() ; ++cur) { @@ -49,8 +86,8 @@ void Package::write_to_stream(ostream&fd) const if (cur->first == "std_logic_vector") continue; - fd << cur->first << ": "; - cur->second->write_to_stream(fd); + fd << "type " << cur->first << " is "; + cur->second->write_type_to_stream(fd); fd << ";" << endl; } for (map::const_iterator cur = new_types_.begin() @@ -62,8 +99,8 @@ void Package::write_to_stream(ostream&fd) const if (cur->first == "std_logic_vector") continue; - fd << cur->first << ": "; - cur->second->write_to_stream(fd); + fd << "type " << cur->first << " is "; + cur->second->write_type_to_stream(fd); fd << ";" << endl; } diff --git a/vhdlpp/parse.y b/vhdlpp/parse.y index 5da156fc7..21abf32ab 100644 --- a/vhdlpp/parse.y +++ b/vhdlpp/parse.y @@ -1605,7 +1605,6 @@ package_body_declarative_part_opt package_declarative_item : component_declaration | constant_declaration - | type_declaration | subtype_declaration | type_declaration | use_clause @@ -2148,12 +2147,26 @@ type_declaration if ($4 == 0) { errormsg(@1, "Failed to declare type name %s.\n", name.str()); } else { - //VTypeDef*tmp = new VTypeDef(name, $4); - //active_scope->bind_name(name, tmp); - active_scope->bind_name(name, $4); + VTypeDef*tmp; + map::iterator cur = active_scope->incomplete_types.find(name); + if (cur == active_scope->incomplete_types.end()) { + tmp = new VTypeDef(name, $4); + active_scope->bind_name(name, tmp); + } else { + tmp = cur->second; + tmp->set_definition($4); + active_scope->incomplete_types.erase(cur); + } } delete[]$2; } + | K_type IDENTIFIER ';' + { perm_string name = lex_strings.make($2); + VTypeDef*tmp = new VTypeDef(name); + active_scope->incomplete_types[name] = tmp; + active_scope->bind_name(name, tmp); + delete[]$2; + } | K_type IDENTIFIER K_is error ';' { errormsg(@4, "Error in type definition for %s\n", $2); yyerrok; diff --git a/vhdlpp/scope.h b/vhdlpp/scope.h index 8e5997781..adbda776f 100644 --- a/vhdlpp/scope.h +++ b/vhdlpp/scope.h @@ -179,6 +179,10 @@ class ActiveScope : public ScopeBase { cleanup(); } + // Keep track of incomplete types until their proper + // definition shows up. + std::map incomplete_types; + private: Entity*context_entity_; }; diff --git a/vhdlpp/vtype.cc b/vhdlpp/vtype.cc index cd39a4fe9..3f9676528 100644 --- a/vhdlpp/vtype.cc +++ b/vhdlpp/vtype.cc @@ -21,6 +21,7 @@ # include "parse_types.h" # include # include +# include using namespace std; @@ -200,6 +201,11 @@ VTypeRecord::element_t::element_t(perm_string name, const VType*typ) { } +VTypeDef::VTypeDef(perm_string nam) +: name_(nam), type_(0) +{ +} + VTypeDef::VTypeDef(perm_string nam, const VType*typ) : name_(nam), type_(typ) { @@ -208,3 +214,9 @@ VTypeDef::VTypeDef(perm_string nam, const VType*typ) VTypeDef::~VTypeDef() { } + +void VTypeDef::set_definition(const VType*typ) +{ + assert(type_ == 0); + type_ = typ; +} diff --git a/vhdlpp/vtype.h b/vhdlpp/vtype.h index fa0d0058f..102206e34 100644 --- a/vhdlpp/vtype.h +++ b/vhdlpp/vtype.h @@ -44,6 +44,10 @@ class VType { // of this type to the designated stream. This is used for // writing parsed types to library files. virtual void write_to_stream(std::ostream&fd) const; + // This is like the above, but is the root function called + // directly after the "type is..." when writing type + // definitions. Most types accept the default definition of this. + virtual void write_type_to_stream(std::ostream&fd) const; // This virtual method writes a human-readable version of the // type to a given file for debug purposes. (Question: is this @@ -226,9 +230,16 @@ class VTypeRecord : public VType { class VTypeDef : public VType { public: - VTypeDef(perm_string name, const VType*is); + explicit VTypeDef(perm_string name); + explicit VTypeDef(perm_string name, const VType*is); ~VTypeDef(); + // If the type is not given a definition in the constructor, + // then this must be used to set the definition later. + void set_definition(const VType*is); + + void write_to_stream(std::ostream&fd) const; + void write_type_to_stream(ostream&fd) const; int emit_typedef(std::ostream&out) const; int emit_def(std::ostream&out) const; diff --git a/vhdlpp/vtype_stream.cc b/vhdlpp/vtype_stream.cc index c39dd76f9..6a99a5cb0 100644 --- a/vhdlpp/vtype_stream.cc +++ b/vhdlpp/vtype_stream.cc @@ -29,6 +29,11 @@ void VType::write_to_stream(ostream&fd) const fd << "/* UNKNOWN TYPE: " << typeid(*this).name() << " */"; } +void VType::write_type_to_stream(ostream&fd) const +{ + write_to_stream(fd); +} + void VTypeArray::write_to_stream(ostream&fd) const { // Special case: std_logic_vector @@ -76,6 +81,16 @@ void VTypeArray::write_to_stream(ostream&fd) const etype_->write_to_stream(fd); } +void VTypeDef::write_type_to_stream(ostream&fd) const +{ + type_->write_to_stream(fd); +} + +void VTypeDef::write_to_stream(ostream&fd) const +{ + fd << name_; +} + void VTypePrimitive::write_to_stream(ostream&fd) const { switch (type_) { @@ -100,7 +115,8 @@ void VTypePrimitive::write_to_stream(ostream&fd) const void VTypeRange::write_to_stream(ostream&fd) const { - fd << "range " << min_ << " to " << max_; + base_->write_to_stream(fd); + fd << " range " << min_ << " to " << max_; } void VTypeRecord::write_to_stream(ostream&fd) const @@ -110,11 +126,11 @@ void VTypeRecord::write_to_stream(ostream&fd) const elements_[idx]->write_to_stream(fd); fd << "; "; } - fd << "endrecord"; + fd << "end record"; } void VTypeRecord::element_t::write_to_stream(ostream&fd) const { - fd << name_ << ":"; + fd << name_ << ": "; type_->write_to_stream(fd); } From 2443884779367d1f18f06fbbe99e106a6ac7846c Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Sat, 5 May 2012 16:54:21 -0700 Subject: [PATCH 030/179] Parse/sorry nets of named type. --- lexor.lex | 10 +++++++++- parse.y | 3 +++ pform.h | 6 ------ 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/lexor.lex b/lexor.lex index 01a891437..7a432361e 100644 --- a/lexor.lex +++ b/lexor.lex @@ -325,7 +325,15 @@ TU [munpf] \\[^ \t\b\f\r\n]+ { yylval.text = strdupnew(yytext+1); - return IDENTIFIER; } + if (gn_system_verilog()) { + if (data_type_t*type = pform_test_type_identifier(yylval.text)) { + delete[]yylval.text; + yylval.data_type = type; + return TYPE_IDENTIFIER; + } + } + return IDENTIFIER; + } \$([a-zA-Z0-9$_]+) { /* The 1364-1995 timing checks. */ diff --git a/parse.y b/parse.y index fe6ba14c1..1c88baa17 100644 --- a/parse.y +++ b/parse.y @@ -3938,6 +3938,9 @@ module_item { yyerror(@5, "error: Errors in net variable list."); } + | attribute_list_opt net_type TYPE_IDENTIFIER net_variable_list ';' + { yyerror(@2, "sorry: Nets of named types not supported."); } + /* This form doesn't have the range, but does have strengths. This gives strength to the assignment drivers. */ diff --git a/pform.h b/pform.h index 9fd2a3d80..0bde0e30c 100644 --- a/pform.h +++ b/pform.h @@ -255,12 +255,6 @@ extern void pform_makewire(const struct vlltype&li, list*attr, PWSRType rt = SR_NET); -extern void pform_makewire(const struct vlltype&li, - struct_type_t*struct_type, - list*names, - NetNet::PortType, - list*attr); - /* This form handles assignment declarations. */ extern void pform_makewire(const struct vlltype&li, list*range, From 369a0b9eca3f0f017238b5121dc1908880b67637 Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Sat, 5 May 2012 16:55:29 -0700 Subject: [PATCH 031/179] VHDL named types work in more places. --- vhdlpp/expression_elaborate.cc | 4 ++++ vhdlpp/vtype.h | 4 ++++ vhdlpp/vtype_emit.cc | 29 ++++++++++++++++++----------- 3 files changed, 26 insertions(+), 11 deletions(-) diff --git a/vhdlpp/expression_elaborate.cc b/vhdlpp/expression_elaborate.cc index 3e7d3cc39..75ae6e597 100644 --- a/vhdlpp/expression_elaborate.cc +++ b/vhdlpp/expression_elaborate.cc @@ -350,6 +350,10 @@ int ExpAggregate::elaborate_expr(Entity*ent, Architecture*arc, const VType*ltype set_type(ltype); + while (const VTypeDef*cur = dynamic_cast(ltype)) { + ltype = cur->peek_definition(); + } + if (const VTypeArray*larray = dynamic_cast(ltype)) { return elaborate_expr_array_(ent, arc, larray); } diff --git a/vhdlpp/vtype.h b/vhdlpp/vtype.h index 102206e34..44220729b 100644 --- a/vhdlpp/vtype.h +++ b/vhdlpp/vtype.h @@ -238,6 +238,10 @@ class VTypeDef : public VType { // then this must be used to set the definition later. void set_definition(const VType*is); + // In some situations, we only need the definition of the + // type, and this method gets it for us. + inline const VType* peek_definition(void) const { return type_; } + void write_to_stream(std::ostream&fd) const; void write_type_to_stream(ostream&fd) const; int emit_typedef(std::ostream&out) const; diff --git a/vhdlpp/vtype_emit.cc b/vhdlpp/vtype_emit.cc index 2dbe36427..7144bad6d 100644 --- a/vhdlpp/vtype_emit.cc +++ b/vhdlpp/vtype_emit.cc @@ -57,13 +57,19 @@ int VTypeArray::emit_def(ostream&out) const cur = sub; } - const VTypePrimitive*base = dynamic_cast (cur->etype_); - assert(base != 0); - assert(dimensions() == 1); + const VType*raw_base = cur->etype_; - base->emit_primitive_type(out); - if (signed_flag_) - out << " signed"; + const VTypePrimitive*base = dynamic_cast (raw_base); + + if (base) { + assert(dimensions() == 1); + + base->emit_def(out); + if (signed_flag_) + out << " signed"; + } else { + raw_base->emit_def(out); + } dims.push_back(cur); @@ -125,8 +131,8 @@ int VTypePrimitive::emit_def(ostream&out) const int VTypeRange::emit_def(ostream&out) const { int errors = 0; - assert(0); - out << "/* Internal error: Don't know how to emit type */"; + out << "/* Internal error: Don't know how to emit range */"; + errors += base_->emit_def(out); return errors; } @@ -147,10 +153,10 @@ int VTypeRecord::emit_def(ostream&out) const return errors; } -int VTypeDef::emit_def(ostream&) const +int VTypeDef::emit_def(ostream&out) const { int errors = 0; - assert(0); + errors += type_->emit_def(out); return errors; } @@ -162,7 +168,8 @@ int VTypeDef::emit_decl(ostream&out, perm_string name, bool reg_flag) const else out << "wire "; - out << "\\" << name_ << " \\" << name << " "; + errors += type_->emit_def(out); + out << " \\" << name << " "; return errors; } From 621c09105c08ac631b62b4e9e76599629e164dbf Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Sat, 12 May 2012 18:38:49 -0700 Subject: [PATCH 032/179] Sort the typedef emits so that types are emitted in the order used. It is common for typedefs of complex types to use further typedefs. Emit the type definitions depth first so that the types that are used are defined first. This reduces the need for pre-declaration of types. --- vhdlpp/architec_emit.cc | 4 +++- vhdlpp/expression_emit.cc | 9 +++++++-- vhdlpp/vtype.h | 14 +++++++++++++- vhdlpp/vtype_emit.cc | 40 ++++++++++++++++++++++++++++++++++++--- 4 files changed, 60 insertions(+), 7 deletions(-) diff --git a/vhdlpp/architec_emit.cc b/vhdlpp/architec_emit.cc index bf19c7bf2..4420bb815 100644 --- a/vhdlpp/architec_emit.cc +++ b/vhdlpp/architec_emit.cc @@ -67,6 +67,8 @@ int Architecture::emit(ostream&out, Entity*entity) // Find typedefs that are present in the architecture body and // emit them, so that following code can use the name instead // of the full definition. + + typedef_context_t typedef_ctx; for (map::iterator cur = old_types_.begin() ; cur != old_types_.end() ; ++cur) { @@ -74,7 +76,7 @@ int Architecture::emit(ostream&out, Entity*entity) if (def == 0) continue; - errors += def->emit_typedef(out); + errors += def->emit_typedef(out, typedef_ctx); } for (map::iterator cur = old_constants_.begin() diff --git a/vhdlpp/expression_emit.cc b/vhdlpp/expression_emit.cc index 5d8ec72bb..bf815ca7e 100644 --- a/vhdlpp/expression_emit.cc +++ b/vhdlpp/expression_emit.cc @@ -77,11 +77,16 @@ int ExpAggregate::emit(ostream&out, Entity*ent, Architecture*arc) return 1; } - if (const VTypeArray*atype = dynamic_cast (peek_type())) + const VType*use_type = peek_type(); + while (const VTypeDef*def = dynamic_cast (use_type)) { + use_type = def->peek_definition(); + } + + if (const VTypeArray*atype = dynamic_cast (use_type)) return emit_array_(out, ent, arc, atype); out << "/* " << get_fileline() << ": internal error: " - << "I don't know how to elaborate aggregate in " << typeid(peek_type()).name() + << "I don't know how to elab/emit aggregate in " << typeid(use_type).name() << " type context. */"; return 1; } diff --git a/vhdlpp/vtype.h b/vhdlpp/vtype.h index 44220729b..e6eb7da3d 100644 --- a/vhdlpp/vtype.h +++ b/vhdlpp/vtype.h @@ -21,6 +21,7 @@ # include # include +# include # include # include # include @@ -28,6 +29,10 @@ class Expression; class prange_t; +class VTypeDef; + +typedef enum typedef_topo_e { NONE=0, PENDING, MARKED } typedef_topo_t; +typedef std::map typedef_context_t; /* * A description of a VHDL type consists of a graph of VType @@ -58,6 +63,12 @@ class VType { // type. It is used to emit typedef's. virtual int emit_def(std::ostream&out) const =0; + // This virtual method causes VTypeDef types to emit typedefs + // of themselves. The VTypeDef implementation of this method + // uses this method recursively to do a depth-first emit of + // all the types that it emits. + virtual int emit_typedef(std::ostream&out, typedef_context_t&ctx) const; + private: friend class decl_t; // This virtual method is called to emit the declaration. This @@ -157,6 +168,7 @@ class VTypeArray : public VType { const VType* element_type() const; int emit_def(std::ostream&out) const; + int emit_typedef(std::ostream&out, typedef_context_t&ctx) const; private: const VType*etype_; @@ -244,7 +256,7 @@ class VTypeDef : public VType { void write_to_stream(std::ostream&fd) const; void write_type_to_stream(ostream&fd) const; - int emit_typedef(std::ostream&out) const; + int emit_typedef(std::ostream&out, typedef_context_t&ctx) const; int emit_def(std::ostream&out) const; private: diff --git a/vhdlpp/vtype_emit.cc b/vhdlpp/vtype_emit.cc index 7144bad6d..58cfc18a7 100644 --- a/vhdlpp/vtype_emit.cc +++ b/vhdlpp/vtype_emit.cc @@ -46,6 +46,11 @@ int VType::emit_decl(ostream&out, perm_string name, bool reg_flag) const return errors; } +int VType::emit_typedef(std::ostream&, typedef_context_t&) const +{ + return 0; +} + int VTypeArray::emit_def(ostream&out) const { int errors = 0; @@ -86,6 +91,11 @@ int VTypeArray::emit_def(ostream&out) const return errors; } +int VTypeArray::emit_typedef(std::ostream&out, typedef_context_t&ctx) const +{ + etype_->emit_typedef(out, ctx); +} + int VTypeEnum::emit_def(ostream&out) const { int errors = 0; @@ -153,10 +163,15 @@ int VTypeRecord::emit_def(ostream&out) const return errors; } +/* + * For VTypeDef objects, use the name of the defined type as the + * type. (We are defining a variable here, not the type itself.) The + * emit_typedef() method was presumably called to define type already. + */ int VTypeDef::emit_def(ostream&out) const { int errors = 0; - errors += type_->emit_def(out); + out << "\\" << name_ << " "; return errors; } @@ -173,9 +188,28 @@ int VTypeDef::emit_decl(ostream&out, perm_string name, bool reg_flag) const return errors; } -int VTypeDef::emit_typedef(ostream&out) const +int VTypeDef::emit_typedef(ostream&out, typedef_context_t&ctx) const { - int errors = 0; + // The typedef_context_t is used by me to determine if this + // typedef has already been emitted in this architecture. If + // it has, then it is MARKED, give up. Otherwise, recurse the + // emit_typedef to make sure all sub-types that I use have + // been emitted, then emit my typedef. + typedef_topo_t&flag = ctx[this]; + switch (flag) { + case MARKED: + return 0; + case PENDING: + out << "typedef \\" << name_ << " ; /* typedef cycle? */" << endl; + return 0; + case NONE: + break; + } + + flag = PENDING; + int errors = type_->emit_typedef(out, ctx); + flag = MARKED; + out << "typedef "; errors += type_->emit_def(out); out << " \\" << name_ << " ;" << endl; From ceaa60d2f4f6c571230ee9a9af9df27a8411ea72 Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Sun, 13 May 2012 17:05:09 -0700 Subject: [PATCH 033/179] Parse (to "sorry") arrays of named types. --- parse.y | 6 ++++-- pform.cc | 5 +++++ pform_types.h | 11 +++++++++++ 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/parse.y b/parse.y index 1c88baa17..40fb6375b 100644 --- a/parse.y +++ b/parse.y @@ -884,8 +884,10 @@ data_type /* IEEE1800-2005: A.2.2.1 */ FILE_NAME(tmp, @1); $$ = tmp; } - | TYPE_IDENTIFIER - { $$ = $1; } + | TYPE_IDENTIFIER range_opt + { if ($2) $$ = new array_type_t($1, $2); + else $$ = $1; + } | K_string { yyerror(@1, "sorry: String data type not supported."); $$ = 0; diff --git a/pform.cc b/pform.cc index 30c2fae81..b4f8f5535 100644 --- a/pform.cc +++ b/pform.cc @@ -2770,6 +2770,11 @@ void pform_set_data_type(const struct vlltype&li, data_type_t*data_type, list (data_type)) { + VLerror(li, "sorry: General array types not supported."); + return; + } + assert(0); } diff --git a/pform_types.h b/pform_types.h index d89631501..cfd56e5c3 100644 --- a/pform_types.h +++ b/pform_types.h @@ -139,6 +139,17 @@ struct vector_type_t : public data_type_t { std::auto_ptr< list > pdims; }; +/* + * The array_type_t is a generalization of the vector_type_t in that + * the base type is another general data type. + */ +struct array_type_t : public data_type_t { + inline explicit array_type_t(data_type_t*btype, std::list*pd) + : base_type(btype), packed_dims(pd) { } + data_type_t*base_type; + std::auto_ptr< list > packed_dims; +}; + struct real_type_t : public data_type_t { enum type_t { REAL, SHORTREAL }; inline explicit real_type_t(type_t tc) : type_code(tc) { } From 6e8aef82626d174aba7e53666652cb5087e780b2 Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Fri, 25 May 2012 15:58:29 -0700 Subject: [PATCH 034/179] Get unpacked arrays working. --- PWire.cc | 9 +- PWire.h | 7 +- design_dump.cc | 11 ++- elab_expr.cc | 115 +++++++++++------------ elab_lval.cc | 79 +++++++++------- elab_net.cc | 81 +++++++++-------- elab_sig.cc | 45 ++++----- net_nex_input.cc | 2 +- netlist.cc | 85 ++++++----------- netlist.h | 21 ++--- netmisc.cc | 231 +++++++++++++++++++++++++++++++++++++++-------- netmisc.h | 33 ++++++- parse.y | 22 +---- pform.cc | 5 +- pform.h | 3 +- pform_dump.cc | 9 +- t-dll.cc | 23 ++++- t-dll.h | 4 +- 18 files changed, 468 insertions(+), 317 deletions(-) diff --git a/PWire.cc b/PWire.cc index 4539bba17..6db15b071 100644 --- a/PWire.cc +++ b/PWire.cc @@ -29,7 +29,7 @@ PWire::PWire(perm_string n, : name_(n), type_(t), port_type_(pt), data_type_(dt), signed_(false), isint_(false), port_set_(false), net_set_(false), is_scalar_(false), - error_cnt_(0), lidx_(0), ridx_(0), enum_type_(0), struct_type_(0), + error_cnt_(0), enum_type_(0), struct_type_(0), discipline_(0) { if (t == NetNet::INTEGER) { @@ -242,15 +242,14 @@ void PWire::set_range(const list&rlist, PWSRType type) } } -void PWire::set_memory_idx(PExpr*ldx, PExpr*rdx) +void PWire::set_unpacked_idx(const list&ranges) { - if (lidx_ != 0 || ridx_ != 0) { + if (! unpacked_.empty()) { cerr << get_fileline() << ": error: Array ``" << name_ << "'' has already been declared." << endl; error_cnt_ += 1; } else { - lidx_ = ldx; - ridx_ = rdx; + unpacked_ = ranges; } } diff --git a/PWire.h b/PWire.h index 23c496ba5..52c761821 100644 --- a/PWire.h +++ b/PWire.h @@ -78,7 +78,7 @@ class PWire : public LineInfo { void set_range_scalar(PWSRType type); void set_range(const std::list&ranges, PWSRType type); - void set_memory_idx(PExpr*ldx, PExpr*rdx); + void set_unpacked_idx(const std::list&ranges); void set_enumeration(enum_type_t*enum_type); void set_struct_type(struct_type_t*type); @@ -115,9 +115,8 @@ class PWire : public LineInfo { unsigned error_cnt_; // If this wire is actually a memory, these indices will give - // me the size and address range of the memory. - PExpr*lidx_; - PExpr*ridx_; + // me the size and address ranges of the memory. + std::listunpacked_; enum_type_t*enum_type_; struct_type_t*struct_type_; diff --git a/design_dump.cc b/design_dump.cc index 2ded152a2..3e7e7bb07 100644 --- a/design_dump.cc +++ b/design_dump.cc @@ -196,11 +196,20 @@ ostream&operator<<(ostream&out, const list&rlist) return out; } +ostream&operator<<(ostream&out, const vector&rlist) +{ + for (vector::const_iterator cur = rlist.begin() + ; cur != rlist.end() ; ++cur) { + out << "[" << cur->msb << ":" << cur->lsb << "]"; + } + return out; +} + /* Dump a net. This can be a wire or register. */ void NetNet::dump_net(ostream&o, unsigned ind) const { o << setw(ind) << "" << type() << ": " << name() - << "[" << s0_ << ":" << e0_ << " count=" << pin_count() << "]"; + << unpacked_dims_ << " unpacked dims=" << unpacked_dimensions() << " count=" << pin_count(); if (local_flag_) o << " (local)"; o << " " << data_type_; diff --git a/elab_expr.cc b/elab_expr.cc index 213a580db..979d2b45c 100644 --- a/elab_expr.cc +++ b/elab_expr.cc @@ -1873,7 +1873,7 @@ bool PEIdent::calculate_packed_indices_(Design*des, NetScope*scope, NetNet*net, { list index; index = path_.back().index; - for (size_t idx = 0 ; idx < net->array_dimensions() ; idx += 1) + for (size_t idx = 0 ; idx < net->unpacked_dimensions() ; idx += 1) index.pop_front(); return evaluate_index_prefix(des, scope, prefix_indices, index); @@ -2073,7 +2073,7 @@ unsigned PEIdent::test_width(Design*des, NetScope*scope, width_mode_t&mode) if (!name_tail.index.empty()) { const index_component_t&index_tail = name_tail.index.back(); // Skip full array word net selects. - if (!net || (name_tail.index.size() > net->array_dimensions())) { + if (!net || (name_tail.index.size() > net->unpacked_dimensions())) { use_sel = index_tail.sel; } } @@ -3037,78 +3037,69 @@ NetExpr* PEIdent::elaborate_expr_net_word_(Design*des, NetScope*scope, const name_component_t&name_tail = path_.back(); - if (name_tail.index.empty() && !(SYS_TASK_ARG & flags)) { + // Special case: This is the entire array, and we are a direct + // argument of a system task. + if (name_tail.index.empty() && (SYS_TASK_ARG & flags)) { + NetESignal*res = new NetESignal(net, 0); + res->set_line(*this); + return res; + } + + if (name_tail.index.empty()) { cerr << get_fileline() << ": error: Array " << path() << " Needs an array index here." << endl; des->errors += 1; return 0; } - index_component_t index_front; - if (! name_tail.index.empty()) { - index_front = name_tail.index.front(); - ivl_assert(*this, index_front.sel != index_component_t::SEL_NONE); - if (index_front.sel != index_component_t::SEL_BIT) { - cerr << get_fileline() << ": error: Array " << path_ - << " cannot be indexed by a range." << endl; - des->errors += 1; - return 0; - } - ivl_assert(*this, index_front.msb); - ivl_assert(*this, !index_front.lsb); - } - - NetExpr*word_index = 0; - if (index_front.sel != index_component_t::SEL_NONE) - word_index = elab_and_eval(des, scope, index_front.msb, -1, - need_const); - - if (word_index == 0 && !(SYS_TASK_ARG & flags)) + // Make sure there are enough indices to address an array element. + if (name_tail.index.size() < net->unpacked_dimensions()) { + cerr << get_fileline() << ": error: Array " << path() + << " needs " << net->unpacked_dimensions() << " indices," + << " but got only " << name_tail.index.size() << "." << endl; + des->errors += 1; return 0; - - if (NetEConst*word_addr = dynamic_cast(word_index)) { - long addr = word_addr->value().as_long(); - - // Special case: The index is out of range, so the value - // of this expression is a 'bx vector the width of a word. - if (!net->array_index_is_valid(addr)) { - cerr << get_fileline() << ": warning: returning 'bx for out " - "of bounds array access " << net->name() - << "[" << addr << "]." << endl; - NetEConst*resx = make_const_x(net->vector_width()); - resx->set_line(*this); - delete word_index; - return resx; - } - - // Recalculate the constant address with the adjusted base. - unsigned use_addr = net->array_index_to_address(addr); - if (addr < 0 || use_addr != (unsigned long)addr) { - verinum val ( (uint64_t)use_addr, 8*sizeof(use_addr)); - NetEConst*tmp = new NetEConst(val); - tmp->set_line(*this); - delete word_index; - word_index = tmp; - } - - } else if (word_index) { - // If there is a non-zero base to the memory, then build an - // expression to calculate the canonical address. - if (long base = net->array_first()) { - - word_index = normalize_variable_array_base( - word_index, base, net->array_count()); - eval_expr(word_index); - } } - NetESignal*res = new NetESignal(net, word_index); + // Evaluate all the index expressions into an + // "unpacked_indices" array. + listunpacked_indices; + list unpacked_indices_const; + bool flag = indices_to_expressions(des, scope, this, + name_tail.index, net->unpacked_dimensions(), + need_const, + unpacked_indices, + unpacked_indices_const); + + NetExpr*canon_index = 0; + if (flag) { + ivl_assert(*this, unpacked_indices_const.size() == net->unpacked_dimensions()); + canon_index = normalize_variable_unpacked(net, unpacked_indices_const); + + if (canon_index == 0) { + cerr << get_fileline() << ": warning: " + << "returning 'bx for out of bounds array access " + << net->name() << as_indices(unpacked_indices_const) << "." << endl; + } + + } else { + ivl_assert(*this, unpacked_indices.size() == net->unpacked_dimensions()); + canon_index = normalize_variable_unpacked(net, unpacked_indices); + } + + if (canon_index == 0) { + NetEConst*xxx = make_const_x(net->vector_width()); + xxx->set_line(*this); + return xxx; + } + + NetESignal*res = new NetESignal(net, canon_index); res->set_line(*this); // Detect that the word has a bit/part select as well. index_component_t::ctype_t word_sel = index_component_t::SEL_NONE; - if (name_tail.index.size() > 1) + if (name_tail.index.size() > net->unpacked_dimensions()) word_sel = name_tail.index.back().sel; if (net->get_scalar() && @@ -3117,7 +3108,7 @@ NetExpr* PEIdent::elaborate_expr_net_word_(Design*des, NetScope*scope, if (res->expr_type() == IVL_VT_REAL) cerr << "real"; else cerr << "scalar"; cerr << " array word: " << net->name() - <<"[" << *word_index << "]" << endl; + << as_indices(unpacked_indices) << endl; des->errors += 1; delete res; return 0; @@ -3599,7 +3590,7 @@ NetExpr* PEIdent::elaborate_expr_net(Design*des, NetScope*scope, unsigned expr_wid, unsigned flags) const { - if (net->array_dimensions() > 0) + if (net->unpacked_dimensions() > 0) return elaborate_expr_net_word_(des, scope, net, found_in, expr_wid, flags); diff --git a/elab_lval.cc b/elab_lval.cc index 2cfb98016..ba95061d5 100644 --- a/elab_lval.cc +++ b/elab_lval.cc @@ -202,7 +202,7 @@ NetAssign_* PEIdent::elaborate_lval(Design*des, // slice. This is, in fact, an error in l-values. Detect the // situation by noting if the index count is less than the // array dimensions (unpacked). - if (reg->array_dimensions() > name_tail.index.size()) { + if (reg->unpacked_dimensions() > name_tail.index.size()) { cerr << get_fileline() << ": error: Cannot assign to array " << path_ << ". Did you forget a word index?" << endl; des->errors += 1; @@ -228,7 +228,7 @@ NetAssign_* PEIdent::elaborate_lval(Design*des, return lv; } - if (reg->array_dimensions() > 0) + if (reg->unpacked_dimensions() > 0) return elaborate_lval_net_word_(des, scope, reg); // This must be after the array word elaboration above! @@ -278,6 +278,15 @@ NetAssign_* PEIdent::elaborate_lval_net_word_(Design*des, const name_component_t&name_tail = path_.back(); ivl_assert(*this, !name_tail.index.empty()); + if (name_tail.index.size() < reg->unpacked_dimensions()) { + cerr << get_fileline() << ": error: Array " << reg->name() + << " needs " << reg->unpacked_dimensions() << " indices," + << " but got only " << name_tail.index.size() << "." << endl; + des->errors += 1; + return 0; + } + + // Make sure there are enough indices to address an array element. const index_component_t&index_head = name_tail.index.front(); if (index_head.sel == index_component_t::SEL_PART) { cerr << get_fileline() << ": error: cannot perform a part " @@ -286,47 +295,47 @@ NetAssign_* PEIdent::elaborate_lval_net_word_(Design*des, return 0; } - ivl_assert(*this, index_head.sel == index_component_t::SEL_BIT); - ivl_assert(*this, index_head.msb != 0); - ivl_assert(*this, index_head.lsb == 0); - NetExpr*word = elab_and_eval(des, scope, index_head.msb, -1); + // Evaluate all the index expressions into an + // "unpacked_indices" array. + listunpacked_indices; + list unpacked_indices_const; + bool flag = indices_to_expressions(des, scope, this, + name_tail.index, reg->unpacked_dimensions(), + false, + unpacked_indices, + unpacked_indices_const); - // If there is a non-zero base to the memory, then build an - // expression to calculate the canonical address. - if (long base = reg->array_first()) { - - word = normalize_variable_array_base(word, base, - reg->array_count()); - eval_expr(word); + NetExpr*canon_index = 0; + if (flag) { + ivl_assert(*this, unpacked_indices_const.size() == reg->unpacked_dimensions()); + canon_index = normalize_variable_unpacked(reg, unpacked_indices_const); + if (canon_index == 0) { + cerr << get_fileline() << ": warning: " + << "ignoring out of bounds l-value array access " << reg->name(); + for (list::const_iterator cur = unpacked_indices_const.begin() + ; cur != unpacked_indices_const.end() ; ++cur) { + cerr << "[" << *cur << "]"; + } + cerr << "." << endl; + } + } else { + ivl_assert(*this, unpacked_indices.size() == reg->unpacked_dimensions()); + canon_index = normalize_variable_unpacked(reg, unpacked_indices); } + NetAssign_*lv = new NetAssign_(reg); - lv->set_word(word); + lv->set_word(canon_index); if (debug_elaborate) - cerr << get_fileline() << ": debug: Set array word=" << *word << endl; + cerr << get_fileline() << ": debug: Set array word=" << *canon_index << endl; - // Test for the case that the index is a constant, and is out - // of bounds. The "word" expression is the word index already - // converted to canonical address, so this just needs to check - // that the address is not too big. - if (NetEConst*word_const = dynamic_cast(word)) { - verinum word_val = word_const->value(); - long index = word_val.as_long(); - - if (index < 0 || index >= (long) reg->array_count()) { - cerr << get_fileline() << ": warning: Constant array index " - << (index + reg->array_first()) - << " is out of range for array " - << reg->name() << "." << endl; - } - } /* An array word may also have part selects applied to them. */ index_component_t::ctype_t use_sel = index_component_t::SEL_NONE; - if (name_tail.index.size() > 1) + if (name_tail.index.size() > reg->unpacked_dimensions()) use_sel = name_tail.index.back().sel; if (reg->get_scalar() && @@ -335,7 +344,7 @@ NetAssign_* PEIdent::elaborate_lval_net_word_(Design*des, if (reg->data_type() == IVL_VT_REAL) cerr << "real"; else cerr << "scalar"; cerr << " array word: " << reg->name() - << "[" << *word << "]" << endl; + << "[" << *canon_index << "]" << endl; des->errors += 1; return 0; } @@ -563,7 +572,7 @@ bool PEIdent::elaborate_lval_net_idx_(Design*des, if (warn_ob_select) { if (rel_base < 0) { cerr << get_fileline() << ": warning: " << reg->name(); - if (reg->array_dimensions() > 0) cerr << "[]"; + if (reg->unpacked_dimensions() > 0) cerr << "[]"; cerr << "[" << lsv; if (use_sel == index_component_t::SEL_IDX_UP) { cerr << "+:"; @@ -574,7 +583,7 @@ bool PEIdent::elaborate_lval_net_idx_(Design*des, } if (rel_base + wid > reg->vector_width()) { cerr << get_fileline() << ": warning: " << reg->name(); - if (reg->array_dimensions() > 0) cerr << "[]"; + if (reg->unpacked_dimensions() > 0) cerr << "[]"; cerr << "[" << lsv; if (use_sel == index_component_t::SEL_IDX_UP) { cerr << "+:"; @@ -587,7 +596,7 @@ bool PEIdent::elaborate_lval_net_idx_(Design*des, } else { if (warn_ob_select) { cerr << get_fileline() << ": warning: " << reg->name(); - if (reg->array_dimensions() > 0) cerr << "[]"; + if (reg->unpacked_dimensions() > 0) cerr << "[]"; cerr << "['bx"; if (use_sel == index_component_t::SEL_IDX_UP) { cerr << "+:"; diff --git a/elab_net.cc b/elab_net.cc index db70c4069..81aaa55ea 100644 --- a/elab_net.cc +++ b/elab_net.cc @@ -208,7 +208,7 @@ bool PEIdent::eval_part_select_(Design*des, NetScope*scope, NetNet*sig, // Only treat as part/bit selects any index that is beyond the // word selects for an array. This is not an array, then // dimensions==0 and any index is treated as a select. - if (name_tail.index.size() <= sig->array_dimensions()) { + if (name_tail.index.size() <= sig->unpacked_dimensions()) { midx = sig->vector_width()-1; lidx = 0; return true; @@ -247,7 +247,7 @@ bool PEIdent::eval_part_select_(Design*des, NetScope*scope, NetNet*sig, if (warn_ob_select) { cerr << get_fileline() << ": warning: " << sig->name(); - if (sig->array_dimensions() > 0) cerr << "[]"; + if (sig->unpacked_dimensions() > 0) cerr << "[]"; cerr << "['bx"; if (index_tail.sel == index_component_t::SEL_IDX_UP) { @@ -279,7 +279,7 @@ bool PEIdent::eval_part_select_(Design*des, NetScope*scope, NetNet*sig, /* Warn about an indexed part select that is out of range. */ if (warn_ob_select && (lidx < 0)) { cerr << get_fileline() << ": warning: " << sig->name(); - if (sig->array_dimensions() > 0) cerr << "[]"; + if (sig->unpacked_dimensions() > 0) cerr << "[]"; cerr << "[" << midx_val; if (index_tail.sel == index_component_t::SEL_IDX_UP) { cerr << "+:"; @@ -290,7 +290,7 @@ bool PEIdent::eval_part_select_(Design*des, NetScope*scope, NetNet*sig, } if (warn_ob_select && (midx >= (long)sig->vector_width())) { cerr << get_fileline() << ": warning: " << sig->name(); - if (sig->array_dimensions() > 0) { + if (sig->unpacked_dimensions() > 0) { cerr << "[]"; } cerr << "[" << midx_val; @@ -337,16 +337,11 @@ bool PEIdent::eval_part_select_(Design*des, NetScope*scope, NetNet*sig, if (midx_tmp >= (long)sig->vector_width() || lidx_tmp < 0) { cerr << get_fileline() << ": warning: Part select " << sig->name(); - if (sig->array_dimensions() > 0) { + if (sig->unpacked_dimensions() > 0) { cerr << "[]"; } cerr << "[" << msb << ":" << lsb << "] is out of range." << endl; -#if 0 - midx_tmp = sig->vector_width() - 1; - lidx_tmp = 0; - des->errors += 1; -#endif } /* This is completely out side the signal so just skip it. */ if (lidx_tmp >= (long)sig->vector_width() || midx_tmp < 0) { @@ -359,7 +354,7 @@ bool PEIdent::eval_part_select_(Design*des, NetScope*scope, NetNet*sig, } case index_component_t::SEL_BIT: - if (name_tail.index.size() > sig->array_dimensions()) { + if (name_tail.index.size() > sig->unpacked_dimensions()) { long msb; bool bit_defined_flag; /* bool flag = */ calculate_bits_(des, scope, msb, bit_defined_flag); @@ -460,11 +455,9 @@ NetNet* PEIdent::elaborate_lnet_common_(Design*des, NetScope*scope, unsigned midx = sig->vector_width()-1, lidx = 0; // The default word select is the first. long widx = 0; - // The widx_val is the word select as entered in the source - // code. It's used for error messages. - long widx_val = 0; const name_component_t&name_tail = path_.back(); + list unpacked_indices_const; netstruct_t*struct_type = 0; if ((struct_type = sig->struct_type()) && !method_name.nil()) { @@ -485,53 +478,64 @@ NetNet* PEIdent::elaborate_lnet_common_(Design*des, NetScope*scope, lidx = member_off; midx = lidx + member->width() - 1; - } else if (sig->array_dimensions() > 0) { + } else if (sig->unpacked_dimensions() > 0) { - if (name_tail.index.empty()) { - cerr << get_fileline() << ": error: array " << sig->name() - << " must be used with an index." << endl; + // Make sure there are enough indices to address an array element. + if (name_tail.index.size() < sig->unpacked_dimensions()) { + cerr << get_fileline() << ": error: Array " << path() + << " needs " << sig->unpacked_dimensions() << " indices," + << " but got only " << name_tail.index.size() << "." << endl; des->errors += 1; return 0; } - const index_component_t&index_head = name_tail.index.front(); - if (index_head.sel == index_component_t::SEL_PART) { - cerr << get_fileline() << ": error: cannot perform a part " - << "select on array " << sig->name() << "." << endl; - des->errors += 1; - return 0; - } - ivl_assert(*this, index_head.sel == index_component_t::SEL_BIT); - - NetExpr*tmp_ex = elab_and_eval(des, scope, index_head.msb, -1, true); - NetEConst*tmp = dynamic_cast(tmp_ex); - if (!tmp) { + // Evaluate all the index expressions into an + // "unpacked_indices" array. + listunpacked_indices; + bool flag = indices_to_expressions(des, scope, this, + name_tail.index, sig->unpacked_dimensions(), + true, + unpacked_indices, + unpacked_indices_const); + // Note that !flag includes that there were any other + // elaboration errors generating the unpacked_indices list. + if (!flag) { cerr << get_fileline() << ": error: array " << sig->name() << " index must be a constant in this context." << endl; des->errors += 1; return 0; } - widx_val = tmp->value().as_long(); - if (sig->array_index_is_valid(widx_val)) - widx = sig->array_index_to_address(widx_val); - else + NetExpr*canon_index = 0; + ivl_assert(*this, unpacked_indices_const.size() == sig->unpacked_dimensions()); + canon_index = normalize_variable_unpacked(sig, unpacked_indices_const); + if (canon_index == 0) { + // Normalize detected an out-of-bounds + // index. Indicate that by setting the generated + // widx to -1. widx = -1; - delete tmp_ex; + + } else { + NetEConst*canon_const = dynamic_cast(canon_index); + ivl_assert(*this, canon_const); + + widx = canon_const->value().as_long(); + delete canon_index; + } if (debug_elaborate) cerr << get_fileline() << ": debug: Use [" << widx << "]" << " to index l-value array." << endl; /* The array has a part/bit select at the end. */ - if (name_tail.index.size() > sig->array_dimensions()) { + if (name_tail.index.size() > sig->unpacked_dimensions()) { if (sig->get_scalar()) { cerr << get_fileline() << ": error: " << "can not select part of "; if (sig->data_type() == IVL_VT_REAL) cerr << "real"; else cerr << "scalar"; cerr << " array word: " << sig->name() - << "[" << widx_val << "]" << endl; + << as_indices(unpacked_indices_const) << endl; des->errors += 1; return 0; } @@ -550,6 +554,7 @@ NetNet* PEIdent::elaborate_lnet_common_(Design*des, NetScope*scope, midx = midx_tmp; lidx = lidx_tmp; } + } else if (!name_tail.index.empty()) { if (sig->get_scalar()) { cerr << get_fileline() << ": error: " @@ -590,7 +595,7 @@ NetNet* PEIdent::elaborate_lnet_common_(Design*des, NetScope*scope, if (widx < 0 || widx >= (long) sig->pin_count()) { cerr << get_fileline() << ": warning: ignoring out of " "bounds l-value array access " - << sig->name() << "[" << widx_val << "]." << endl; + << sig->name() << as_indices(unpacked_indices_const) << "." << endl; return 0; } diff --git a/elab_sig.cc b/elab_sig.cc index f5ba9d4ca..8d6ed3486 100644 --- a/elab_sig.cc +++ b/elab_sig.cc @@ -1057,19 +1057,17 @@ NetNet* PWire::elaborate_sig(Design*des, NetScope*scope) const attrib_list_t*attrib_list = evaluate_attributes(attributes, nattrib, des, scope); - long array_s0 = 0; - long array_e0 = 0; - unsigned array_dimensions = 0; - /* If the ident has idx expressions, then this is a - memory. It can only have the idx registers after the msb - and lsb expressions are filled. And, if it has one index, - it has both. */ - if (lidx_ || ridx_) { - assert(lidx_ && ridx_); + listunpacked_dimensions; - NetExpr*lexp = elab_and_eval(des, scope, lidx_, -1, true); - NetExpr*rexp = elab_and_eval(des, scope, ridx_, -1, true); + for (list::const_iterator cur = unpacked_.begin() + ; cur != unpacked_.end() ; ++cur) { + PExpr*use_lidx = cur->first; + PExpr*use_ridx = cur->second; + assert(use_lidx && use_ridx); + + NetExpr*lexp = elab_and_eval(des, scope, use_lidx, -1, true); + NetExpr*rexp = elab_and_eval(des, scope, use_ridx, -1, true); if ((lexp == 0) || (rexp == 0)) { cerr << get_fileline() << ": internal error: There is " @@ -1086,19 +1084,21 @@ NetNet* PWire::elaborate_sig(Design*des, NetScope*scope) const delete rexp; delete lexp; - if (!const_flag) { + long index_l, index_r; + if (! const_flag) { cerr << get_fileline() << ": error: The indices " << "are not constant for array ``" << name_ << "''." << endl; des->errors += 1; /* Attempt to recover from error, */ - array_s0 = 0; - array_e0 = 0; + index_l = 0; + index_r = 0; } else { - array_s0 = lval.as_long(); - array_e0 = rval.as_long(); - } - array_dimensions = 1; + index_l = lval.as_long(); + index_r = rval.as_long(); + } + + unpacked_dimensions.push_back(NetNet::range_t(index_l, index_r)); } if (data_type_ == IVL_VT_REAL && !packed_dimensions.empty()) { @@ -1173,16 +1173,11 @@ NetNet* PWire::elaborate_sig(Design*des, NetScope*scope) const if (!get_scalar()) { cerr << " " << packed_dimensions; } - cerr << " " << name_; - if (array_dimensions > 0) { - cerr << " [" << array_s0 << ":" << array_e0 << "]" << endl; - } + cerr << " " << name_ << unpacked_dimensions; cerr << " in scope " << scope_path(scope) << endl; } - sig = array_dimensions > 0 - ? new NetNet(scope, name_, wtype, packed_dimensions, array_s0, array_e0) - : new NetNet(scope, name_, wtype, packed_dimensions); + sig = new NetNet(scope, name_, wtype, packed_dimensions, unpacked_dimensions); } // If this is an enumeration, then set the enumeration set for diff --git a/net_nex_input.cc b/net_nex_input.cc index 712ef37b0..5642e6164 100644 --- a/net_nex_input.cc +++ b/net_nex_input.cc @@ -159,7 +159,7 @@ NexusSet* NetESignal::nex_input(bool rem_out) delete tmp; if (warn_sens_entire_arr) { cerr << get_fileline() << ": warning: @* is sensitive to all " - << net_->array_count() << " words in array '" + << net_->unpacked_count() << " words in array '" << name() << "'." << endl; } } diff --git a/netlist.cc b/netlist.cc index ef0b6c1a6..200d3fd35 100644 --- a/netlist.cc +++ b/netlist.cc @@ -452,7 +452,7 @@ NetNet::NetNet(NetScope*s, perm_string n, Type t, unsigned npins) type_(t), port_type_(NOT_A_PORT), data_type_(IVL_VT_NO_TYPE), signed_(false), isint_(false), is_scalar_(false), local_flag_(false), enumeration_(0), struct_type_(0), discipline_(0), - dimensions_(0), s0_(0), e0_(0), eref_count_(0), lref_count_(0) + eref_count_(0), lref_count_(0) { assert(s); assert(npins>0); @@ -501,7 +501,6 @@ NetNet::NetNet(NetScope*s, perm_string n, Type t, port_type_(NOT_A_PORT), data_type_(IVL_VT_NO_TYPE), signed_(false), isint_(false), is_scalar_(false), local_flag_(false), enumeration_(0), struct_type_(0), discipline_(0), - dimensions_(0), s0_(0), e0_(0), eref_count_(0), lref_count_(0) { packed_dims_ = packed; @@ -529,34 +528,41 @@ NetNet::NetNet(NetScope*s, perm_string n, Type t, s->add_signal(this); } -static unsigned calculate_count(long s, long e) +static unsigned calculate_count(const list&unpacked) { - unsigned long r; - if (s >= e) { - r = s - e; - } else { - r = e - s; + unsigned long sum = 1; + for (list::const_iterator cur = unpacked.begin() + ; cur != unpacked.end() ; ++cur) { + sum *= cur->width(); } - if (r >= UINT_MAX) { + + if (sum >= UINT_MAX) return 0; - } - return r + 1; + + return sum; } NetNet::NetNet(NetScope*s, perm_string n, Type t, - const list&packed, long array_s, long array_e) -: NetObj(s, n, calculate_count(array_s, array_e)), + const list&packed, + const list&unpacked) +: NetObj(s, n, calculate_count(unpacked)), type_(t), port_type_(NOT_A_PORT), data_type_(IVL_VT_NO_TYPE), signed_(false), isint_(false), is_scalar_(false), local_flag_(false), enumeration_(0), struct_type_(0), - discipline_(0), - dimensions_(1), s0_(array_s), e0_(array_e), + discipline_(0), unpacked_dims_(unpacked.size()), eref_count_(0), lref_count_(0) { packed_dims_ = packed; + size_t idx = 0; + for (list::const_iterator cur = unpacked.begin() + ; cur != unpacked.end() ; ++cur, idx += 1) { + unpacked_dims_[idx] = *cur; + } + assert(idx == unpacked_dims_.size()); + ivl_assert(*this, s); if (pin_count() == 0) { - cerr << "Array too big [" << array_s << ":" << array_e << "]" << endl; + cerr << "Array too big " << unpacked << endl; ivl_assert(*this, 0); } @@ -602,7 +608,6 @@ NetNet::NetNet(NetScope*s, perm_string n, Type t, netstruct_t*ty) data_type_(IVL_VT_NO_TYPE), signed_(false), isint_(false), is_scalar_(false), local_flag_(false), enumeration_(0), struct_type_(ty), discipline_(0), - dimensions_(0), s0_(0), e0_(0), eref_count_(0), lref_count_(0) { packed_dims_.push_back(range_t(calculate_count(ty)-1, 0)); @@ -878,52 +883,16 @@ bool NetNet::sb_to_slice(const list&indices, long sb, long&loff, unsigned return true; } - -unsigned NetNet::array_dimensions() const +unsigned NetNet::unpacked_count() const { - return dimensions_; -} + unsigned c = 1; + for (size_t idx = 0 ; idx < unpacked_dims_.size() ; idx += 1) { + c *= unpacked_dims_[idx].width(); + } -long NetNet::array_first() const -{ - if (s0_ <= e0_) - return s0_; - else - return e0_; -} - -bool NetNet::array_addr_swapped() const -{ - if (s0_ <= e0_) - return false; - else - return true; -} - -unsigned NetNet::array_count() const -{ - unsigned c = calculate_count(s0_, e0_); - ivl_assert(*this, c > 0); return c; } -bool NetNet::array_index_is_valid(long sb) const -{ - if (sb < s0_ && sb < e0_) - return false; - if (sb > e0_ && sb > s0_) - return false; - return true; -} - -unsigned NetNet::array_index_to_address(long sb) const -{ - if (s0_ <= e0_) - return sb - s0_; - else - return sb - e0_; -} - void NetNet::incr_eref() { eref_count_ += 1; diff --git a/netlist.h b/netlist.h index ba9ff80e3..27a8cd829 100644 --- a/netlist.h +++ b/netlist.h @@ -591,7 +591,8 @@ class NetNet : public NetObj { explicit NetNet(NetScope*s, perm_string n, Type t, const std::list&packed); explicit NetNet(NetScope*s, perm_string n, Type t, - const std::list&packed, long s0, long e0); + const std::list&packed, + const std::list&unpacked); // This form builds a NetNet from its record definition. explicit NetNet(NetScope*s, perm_string n, Type t, netstruct_t*type); @@ -635,6 +636,8 @@ class NetNet : public NetObj { the verilog declaration. */ const std::list& packed_dims() const { return packed_dims_; } + const std::vector& unpacked_dims() const { return unpacked_dims_; } + /* The vector_width returns the bit width of the packed array, vector or scaler that is this NetNet object. The static method is also a convenient way to convert a range list to @@ -670,18 +673,10 @@ class NetNet : public NetObj { /* This method returns 0 for scalars and vectors, and greater for arrays. The value is the number of array indices. (Currently only one array index is supported.) */ - unsigned array_dimensions() const; - long array_first() const; - bool array_addr_swapped() const; + inline unsigned unpacked_dimensions() const { return unpacked_dims_.size(); } // This is the number of array elements. - unsigned array_count() const; - - // This method returns a 0 based address of an array entry as - // indexed by idx. The Verilog source may give index ranges - // that are not zero based. - bool array_index_is_valid(long idx) const; - unsigned array_index_to_address(long idx) const; + unsigned unpacked_count() const; bool local_flag() const { return local_flag_; } void local_flag(bool f) { local_flag_ = f; } @@ -723,11 +718,11 @@ class NetNet : public NetObj { ivl_discipline_t discipline_; std::list packed_dims_; - const unsigned dimensions_; - long s0_, e0_; + std::vector unpacked_dims_; unsigned eref_count_; unsigned lref_count_; + // When the signal is an unresolved wire, we need more detail // which bits are assigned. This mask is true for each bit // that is known to be driven. diff --git a/netmisc.cc b/netmisc.cc index 9ce4ebf00..475b22b91 100644 --- a/netmisc.cc +++ b/netmisc.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001-2011 Stephen Williams (steve@icarus.com) + * Copyright (c) 2001-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 @@ -384,51 +384,202 @@ NetExpr *normalize_variable_slice_base(const list&indices, NetExpr*base, return base; } -/* - * This routine generates the normalization expression needed for a variable - * array word select. - */ -NetExpr *normalize_variable_array_base(NetExpr *base, long offset, - unsigned count) +ostream& operator << (ostream&o, __IndicesManip val) { - assert(offset != 0); - /* Calculate the space needed for the offset. */ - unsigned min_wid = num_bits(-offset); - /* We need enough space for the larger of the offset or the base - * expression. */ - if (min_wid < base->expr_width()) min_wid = base->expr_width(); - /* Now that we have the minimum needed width increase it by one - * to make room for the normalization calculation. */ - min_wid += 1; - /* Pad the base expression to the correct width. */ - base = pad_to_width(base, min_wid, *base); - /* If the offset is greater than zero then we need to do signed - * math to get the location value correct. */ - if (offset > 0 && ! base->has_sign()) { - /* We need this extra select to hide the signed property - * from the padding above. It will be removed automatically - * during code generation. */ - NetESelect *tmp = new NetESelect(base, 0 , min_wid); - tmp->set_line(*base); - tmp->cast_signed(true); - base = tmp; + for (list::const_iterator cur = val.val.begin() + ; cur != val.val.end() ; ++cur) { + o << "[" << *cur << "]"; } - /* Normalize the expression. */ - base = make_add_expr(base, -offset); + return o; +} - /* We should not need to do this, but .array/port does not - * handle a small signed index correctly and it is a major - * effort to fix it. For now we will just pad the expression - * enough so that any negative value when converted to - * unsigned is larger than the maximum array word. */ - if (base->has_sign()) { - unsigned range_wid = num_bits(count-1) + 1; - if (min_wid < range_wid) { - base = pad_to_width(base, range_wid, *base); +ostream& operator << (ostream&o, __IndicesManip val) +{ + for (list::const_iterator cur = val.val.begin() + ; cur != val.val.end() ; ++cur) { + o << "[" << *(*cur) << "]"; + } + return o; +} + +/* + * The src is the input index expression list from the expression, and + * the count is the number that are to be elaborated into the indices + * list. At the same time, create a indices_const list that contains + * the evaluated values for the expression, if they can be + * evaluated. This function will return "true" if all the constants + * can be evaluated. + */ +bool indices_to_expressions(Design*des, NetScope*scope, + // loc is for error messages. + const LineInfo*loc, + // src is the index list, and count is + // the number of items in the list to use. + const list&src, unsigned count, + // True if the expression MUST be constant. + bool need_const, + // These are the outputs. + list&indices, list&indices_const) +{ + ivl_assert(*loc, count <= src.size()); + + bool flag = true; + for (list::const_iterator cur = src.begin() + ; count > 0 ; ++cur, --count) { + ivl_assert(*loc, cur->sel != index_component_t::SEL_NONE); + + if (cur->sel != index_component_t::SEL_BIT) { + cerr << loc->get_fileline() << ": error: " + << "Array cannot be indexed by a range." << endl; + des->errors += 1; + } + ivl_assert(*loc, cur->msb); + + NetExpr*word_index = elab_and_eval(des, scope, cur->msb, -1, need_const); + + // If the elaboration failed, then it is most certainly + // not constant, either. + if (word_index == 0) + flag = false; + + // Track if we detect any non-constant expressions + // here. This may allow for a special case. + if (flag) { + NetEConst*word_const = dynamic_cast (word_index); + if (word_const) + indices_const.push_back(word_const->value().as_long()); + else + flag = false; + } + + indices.push_back(word_index); + } + + return flag; +} + +static void make_strides(const vector&dims, + vector&stride) +{ + stride[dims.size()-1] = 1; + for (size_t idx = stride.size()-1 ; idx > 0 ; --idx) { + long tmp = dims[idx].width(); + if (idx < stride.size()) + tmp *= stride[idx]; + stride[idx-1] = tmp; + } +} + +/* + * Take in a vector of constant indices and convert them to a single + * number that is the canonical address (zero based, 1-d) of the + * word. If any of the indices are out of bounds, return nil instead + * of an expression. + */ +NetExpr* normalize_variable_unpacked(const NetNet*net, list&indices) +{ + const vector&dims = net->unpacked_dims(); + + // Make strides for each index. The stride is the distance (in + // words) to the next element in the canonical array. + vector stride (dims.size()); + make_strides(dims, stride); + + int64_t canonical_addr = 0; + + int idx = 0; + for (list::const_iterator cur = indices.begin() + ; cur != indices.end() ; ++cur, ++idx) { + long tmp = *cur; + + if (dims[idx].lsb <= dims[idx].msb) + tmp -= dims[idx].lsb; + else + tmp -= dims[idx].msb; + + // Notice of this index is out of range. + if (tmp < 0 || tmp >= dims[idx].width()) { + return 0; + } + + canonical_addr += tmp * stride[idx]; + } + + NetEConst*canonical_expr = new NetEConst(verinum(canonical_addr)); + return canonical_expr; +} + +NetExpr* normalize_variable_unpacked(const NetNet*net, list&indices) +{ + const vector&dims = net->unpacked_dims(); + + // Make strides for each index. The stride is the distance (in + // words) to the next element in the canonical array. + vector stride (dims.size()); + make_strides(dims, stride); + + NetExpr*canonical_expr = 0; + + int idx = 0; + for (list::const_iterator cur = indices.begin() + ; cur != indices.end() ; ++cur, ++idx) { + NetExpr*tmp = *cur; + // If the expression elaboration generated errors, then + // give up. Presumably, the error during expression + // elaboration already generated the error message. + if (tmp == 0) + return 0; + + int64_t use_base; + if (dims[idx].lsb <= dims[idx].msb) + use_base = dims[idx].lsb; + else + use_base = dims[idx].msb; + + int64_t use_stride = stride[idx]; + + // Account for that we are doing arithmatic and should + // have a proper width to make sure there ar no + // losses. So calculate a min_wid width. + unsigned tmp_wid; + unsigned min_wid = tmp->expr_width(); + if (use_stride != 1 && ((tmp_wid = num_bits(use_stride)) >= min_wid)) + min_wid = tmp_wid + 1; + if (use_base != 0 && ((tmp_wid = num_bits(use_base)) >= min_wid)) + min_wid = tmp_wid + 1; + if ((tmp_wid = num_bits(dims[idx].width()+1)) >= min_wid) + min_wid = tmp_wid + 1; + + tmp = pad_to_width(tmp, min_wid, *net); + + // Now generate the math to calculate the canonical address. + NetExpr*tmp_scaled = 0; + if (NetEConst*tmp_const = dynamic_cast (tmp)) { + // Special case: the index is constant, so this + // iteration can be replaced with a constant + // expression. + int64_t val = tmp_const->value().as_long(); + val -= use_base; + val *= use_stride; + tmp_scaled = new NetEConst(verinum(val)); + + } else { + tmp_scaled = tmp; + if (use_base != 0) + tmp_scaled = make_add_expr(tmp_scaled, -use_base); + if (use_stride != 1) + tmp_scaled = make_mult_expr(tmp_scaled, use_stride); + } + + if (canonical_expr == 0) { + canonical_expr = tmp_scaled; + } else { + canonical_expr = new NetEBAdd('+', canonical_expr, tmp_scaled, + canonical_expr->expr_width()+1, false); } } - return base; + return canonical_expr; } NetEConst* make_const_x(unsigned long wid) diff --git a/netmisc.h b/netmisc.h index 0504db472..c64a5a996 100644 --- a/netmisc.h +++ b/netmisc.h @@ -142,8 +142,37 @@ extern NetExpr *normalize_variable_part_base(const list&indices, NetExpr*b extern NetExpr*normalize_variable_slice_base(const list&indices, NetExpr *base, const NetNet*reg, unsigned long&lwid); -extern NetExpr*normalize_variable_array_base(NetExpr *base, long offset, - unsigned count); +/* + * The as_indices() manipulator is a convenient way to emit a list of + * index values in the form [<>][<>].... + */ +template struct __IndicesManip { + inline __IndicesManip(const std::list&v) : val(v) { } + const std::list&val; +}; +template inline __IndicesManip as_indices(const std::list&indices) +{ return __IndicesManip(indices); } + +extern ostream& operator << (ostream&o, __IndicesManip); +extern ostream& operator << (ostream&o, __IndicesManip); + +/* + * Given a list of index expressions, generate elaborated expressions + * and constant values, if possible. + */ +extern bool indices_to_expressions(Design*des, NetScope*scope, + // loc is for error messages. + const LineInfo*loc, + // src is the index list, and count is + // the number of items in the list to use. + const list&src, unsigned count, + // True if the expression MUST be constant. + bool need_const, + // These are the outputs. + list&indices, list&indices_const); + +extern NetExpr*normalize_variable_unpacked(const NetNet*net, list&indices); +extern NetExpr*normalize_variable_unpacked(const NetNet*net, list&indices); /* * This function takes as input a NetNet signal and adds a constant diff --git a/parse.y b/parse.y index 71a17b01a..6fcfdffd8 100644 --- a/parse.y +++ b/parse.y @@ -4947,16 +4947,7 @@ register_variable { perm_string ident_name = lex_strings.make($1); pform_makewire(@1, ident_name, NetNet::REG, NetNet::NOT_A_PORT, IVL_VT_NO_TYPE, 0); - if ($2 != 0) { - pform_range_t index; - if ($2->size() > 1) { - yyerror(@2, "sorry: only 1 dimensional arrays " - "are currently supported."); - } - index = $2->front(); - pform_set_reg_idx(ident_name, index.first, index.second); - delete $2; - } + pform_set_reg_idx(ident_name, $2); $$ = $1; } | IDENTIFIER '=' expression @@ -4988,16 +4979,7 @@ net_variable { perm_string name = lex_strings.make($1); pform_makewire(@1, name, NetNet::IMPLICIT, NetNet::NOT_A_PORT, IVL_VT_NO_TYPE, 0); - if ($2 != 0) { - pform_range_t index; - if ($2->size() > 1) { - yyerror(@2, "sorry: only 1 dimensional arrays " - "are currently supported."); - } - index = $2->front(); - pform_set_reg_idx(name, index.first, index.second); - delete $2; - } + pform_set_reg_idx(name, $2); $$ = $1; } ; diff --git a/pform.cc b/pform.cc index a21ae1182..1ba1e2feb 100644 --- a/pform.cc +++ b/pform.cc @@ -2273,7 +2273,7 @@ void pform_set_type_attrib(perm_string name, const string&key, * This function attaches a memory index range to an existing * register. (The named wire must be a register. */ -void pform_set_reg_idx(perm_string name, PExpr*l, PExpr*r) +void pform_set_reg_idx(perm_string name, list*indices) { PWire*cur = lexical_scope->wires_find(name); if (cur == 0) { @@ -2281,7 +2281,8 @@ void pform_set_reg_idx(perm_string name, PExpr*l, PExpr*r) return; } - cur->set_memory_idx(l, r); + if (indices && !indices->empty()) + cur->set_unpacked_idx(*indices); } LexicalScope::range_t* pform_parameter_value_range(bool exclude_flag, diff --git a/pform.h b/pform.h index c17a2e2b6..d88237c8e 100644 --- a/pform.h +++ b/pform.h @@ -294,7 +294,8 @@ extern void pform_set_net_range(list*names, bool signed_flag, ivl_variable_type_t, std::list*attr); -extern void pform_set_reg_idx(perm_string name, PExpr*l, PExpr*r); +extern void pform_set_reg_idx(perm_string name, + std::list*indices); extern void pform_set_reg_integer(list*names, list*attr); extern void pform_set_reg_time(list*names, list*attr); diff --git a/pform_dump.cc b/pform_dump.cc index 5adb1b407..364a26b67 100644 --- a/pform_dump.cc +++ b/pform_dump.cc @@ -376,11 +376,12 @@ void PWire::dump(ostream&out, unsigned ind) const out << " " << name_; - // If the wire has indices, dump them. - if (lidx_ || ridx_) { + // If the wire has unpacked indices, dump them. + for (list::const_iterator cur = unpacked_.begin() + ; cur != unpacked_.end() ; ++cur) { out << "["; - if (lidx_) out << *lidx_; - if (ridx_) out << ":" << *ridx_; + if (cur->first) out << *cur->first; + if (cur->second) out << ":" << *cur->second; out << "]"; } diff --git a/t-dll.cc b/t-dll.cc index e16f8477f..a8c5f12f5 100644 --- a/t-dll.cc +++ b/t-dll.cc @@ -2403,7 +2403,8 @@ void dll_target::signal(const NetNet*net) (net->peek_lref() > 0) ? 1 : 0; obj->discipline = net->get_discipline(); - obj->array_dimensions_ = net->array_dimensions(); + obj->array_dimensions_ = net->unpacked_dimensions(); + assert(obj->array_dimensions_ == net->unpacked_dimensions()); switch (net->port_type()) { @@ -2493,9 +2494,23 @@ void dll_target::signal(const NetNet*net) t_cookie of the Nexus object so that I find it again when I next encounter the nexus. */ - obj->array_base = net->array_first(); - obj->array_words = net->array_count(); - obj->array_addr_swapped = net->array_addr_swapped() ? 1 : 0; + if (obj->array_dimensions_ == 1) { + const vector& dims = net->unpacked_dims(); + if (dims[0].msb < dims[0].lsb) { + obj->array_base = dims[0].msb; + obj->array_addr_swapped = false; + } else { + obj->array_base = dims[0].lsb; + obj->array_addr_swapped = true; + } + obj->array_words = net->unpacked_count(); + } else { + // The back-end API doesn't yet support multi-dimension + // unpacked arrays, so just report the canonical dimensions. + obj->array_base = 0; + obj->array_words = net->unpacked_count(); + obj->array_addr_swapped = 0; + } ivl_assert(*net, obj->array_words == net->pin_count()); if (debug_optimizer && obj->array_words > 1000) cerr << "debug: " diff --git a/t-dll.h b/t-dll.h index 2bdae36bb..54065e7d5 100644 --- a/t-dll.h +++ b/t-dll.h @@ -1,7 +1,7 @@ #ifndef __t_dll_H #define __t_dll_H /* - * 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 @@ -685,7 +685,7 @@ struct ivl_signal_s { unsigned forced_net_ : 1; /* For now, support only 0 or 1 array dimensions. */ - unsigned array_dimensions_ : 1; + unsigned array_dimensions_ : 8; unsigned array_addr_swapped : 1; /* These encode the declared packed dimensions for the From 0833d9e37ae647a150d69fb3a0e5678cbd6af463 Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Mon, 30 Apr 2012 21:12:59 -0700 Subject: [PATCH 035/179] Basic support for program blocks. --- Module.cc | 1 + Module.h | 5 +++++ parse.y | 5 +---- pform.cc | 9 +++++---- pform.h | 8 ++++++-- 5 files changed, 18 insertions(+), 10 deletions(-) diff --git a/Module.cc b/Module.cc index 66efe26b0..589e3e7e3 100644 --- a/Module.cc +++ b/Module.cc @@ -32,6 +32,7 @@ Module::Module(perm_string n) { library_flag = false; is_cell = false; + program_block = false; uc_drive = UCD_NONE; timescale_warn_done = false; time_unit = 0; diff --git a/Module.h b/Module.h index 1f9c32349..29da46ae1 100644 --- a/Module.h +++ b/Module.h @@ -76,6 +76,11 @@ class Module : public PScopeExtra, public LineInfo { bool is_cell; + /* This is true if the module represents a program block + instead of a module/cell. Program blocks have content + restrictions and slightly modify scheduling semantics. */ + bool program_block; + enum UCDriveType { UCD_NONE, UCD_PULL0, UCD_PULL1 }; UCDriveType uc_drive; diff --git a/parse.y b/parse.y index cc5fe859e..56443e206 100644 --- a/parse.y +++ b/parse.y @@ -3754,7 +3754,7 @@ local_timeunit_prec_decl module : attribute_list_opt module_start IDENTIFIER - { pform_startmodule($3, @2.text, @2.first_line, $1); } + { pform_startmodule(@2, $3, $2==K_program, $1); } module_parameter_port_list_opt module_port_list_opt module_attribute_foreign ';' @@ -3796,9 +3796,6 @@ module break; } } - if ($2 == K_program) { - yyerror(@2, "sorry: Program blocks not supported yet."); - } pform_endmodule($3, in_celldefine, ucd); delete[]$3; have_timeunit_decl = false; // We will allow decls again. diff --git a/pform.cc b/pform.cc index aa51b4468..4ff15138c 100644 --- a/pform.cc +++ b/pform.cc @@ -796,13 +796,14 @@ verinum* pform_verinum_with_size(verinum*siz, verinum*val, return res; } -void pform_startmodule(const char*name, const char*file, unsigned lineno, - list*attr) +void pform_startmodule(const struct vlltype&loc, const char*name, + bool program_block, list*attr) { assert( pform_cur_module == 0 ); perm_string lex_name = lex_strings.make(name); pform_cur_module = new Module(lex_name); + pform_cur_module->program_block = program_block; /* Set the local time unit/precision to the global value. */ pform_cur_module->time_unit = pform_time_unit; pform_cur_module->time_precision = pform_time_prec; @@ -813,7 +814,7 @@ void pform_startmodule(const char*name, const char*file, unsigned lineno, * a timescale directive. */ pform_cur_module->time_from_timescale = pform_timescale_file != 0; - FILE_NAME(pform_cur_module, file, lineno); + FILE_NAME(pform_cur_module, loc); pform_cur_module->library_flag = pform_library_flag; ivl_assert(*pform_cur_module, lexical_scope == 0); @@ -824,7 +825,7 @@ void pform_startmodule(const char*name, const char*file, unsigned lineno, scope_generate_counter = 1; if (warn_timescale && pform_timescale_file - && (strcmp(pform_timescale_file,file) != 0)) { + && (strcmp(pform_timescale_file,loc.text) != 0)) { cerr << pform_cur_module->get_fileline() << ": warning: " << "timescale for " << name diff --git a/pform.h b/pform.h index 838fad779..d1a5d8df5 100644 --- a/pform.h +++ b/pform.h @@ -145,9 +145,13 @@ extern PWire* pform_get_make_wire_in_scope(perm_string name, NetNet::Type net_ty * module has been noticed in the source file and the following events * are to apply to the scope of that module. The endmodule causes the * pform to close up and finish the named module. + * + * The program_flag indicates that the module is actually a program + * block. This has implications during parse and during + * elaboration/code generation. */ -extern void pform_startmodule(const char*, const char*file, unsigned lineno, - list*attr); +extern void pform_startmodule(const struct vlltype&loc, const char*name, + bool program_block, list*attr); extern void pform_check_timeunit_prec(); extern void pform_module_set_ports(vector*); From 580c44c015a01e19134f9760427517b78b17be0b Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Sun, 6 May 2012 10:21:51 -0700 Subject: [PATCH 036/179] Prevent non-blocking assignment in program blocks. --- parse.y | 110 ++++++++++++++++++++++++++++++------------------------- pform.cc | 17 +++++++++ pform.h | 4 ++ 3 files changed, 81 insertions(+), 50 deletions(-) diff --git a/parse.y b/parse.y index 56443e206..c4a21fa3d 100644 --- a/parse.y +++ b/parse.y @@ -5484,56 +5484,66 @@ statement_item /* This is roughly statement_item in the LRM */ $$ = tmp; } - | error '=' expression ';' - { yyerror(@2, "Syntax in assignment statement l-value."); - yyerrok; - $$ = new PNoop; - } - | lpvalue K_LE expression ';' - { PAssignNB*tmp = new PAssignNB($1,$3); - FILE_NAME(tmp, @1); - $$ = tmp; - } - | error K_LE expression ';' - { yyerror(@2, "Syntax in assignment statement l-value."); - yyerrok; - $$ = new PNoop; - } - | lpvalue '=' delay1 expression ';' - { PExpr*del = $3->front(); $3->pop_front(); - assert($3->empty()); - PAssign*tmp = new PAssign($1,del,$4); - FILE_NAME(tmp, @1); - $$ = tmp; - } - | lpvalue K_LE delay1 expression ';' - { PExpr*del = $3->front(); $3->pop_front(); - assert($3->empty()); - PAssignNB*tmp = new PAssignNB($1,del,$4); - FILE_NAME(tmp, @1); - $$ = tmp; - } - | lpvalue '=' event_control expression ';' - { PAssign*tmp = new PAssign($1,0,$3,$4); - FILE_NAME(tmp, @1); - $$ = tmp; - } - | lpvalue '=' K_repeat '(' expression ')' event_control expression ';' - { PAssign*tmp = new PAssign($1,$5,$7,$8); - FILE_NAME(tmp,@1); - tmp->set_lineno(@1.first_line); - $$ = tmp; - } - | lpvalue K_LE event_control expression ';' - { PAssignNB*tmp = new PAssignNB($1,0,$3,$4); - FILE_NAME(tmp, @1); - $$ = tmp; - } - | lpvalue K_LE K_repeat '(' expression ')' event_control expression ';' - { PAssignNB*tmp = new PAssignNB($1,$5,$7,$8); - FILE_NAME(tmp, @1); - $$ = tmp; - } + | error '=' expression ';' + { yyerror(@2, "Syntax in assignment statement l-value."); + yyerrok; + $$ = new PNoop; + } + | lpvalue K_LE expression ';' + { PAssignNB*tmp = new PAssignNB($1,$3); + FILE_NAME(tmp, @1); + $$ = tmp; + if (pform_in_program_block()) + yyerror(@2, "Non-blocking assignments not permitted in program blocks."); + } + | error K_LE expression ';' + { yyerror(@2, "Syntax in assignment statement l-value."); + yyerrok; + $$ = new PNoop; + if (pform_in_program_block()) + yyerror(@2, "Non-blocking assignments not permitted in program blocks."); + } + | lpvalue '=' delay1 expression ';' + { PExpr*del = $3->front(); $3->pop_front(); + assert($3->empty()); + PAssign*tmp = new PAssign($1,del,$4); + FILE_NAME(tmp, @1); + $$ = tmp; + } + | lpvalue K_LE delay1 expression ';' + { PExpr*del = $3->front(); $3->pop_front(); + assert($3->empty()); + PAssignNB*tmp = new PAssignNB($1,del,$4); + FILE_NAME(tmp, @1); + $$ = tmp; + if (pform_in_program_block()) + yyerror(@2, "Non-blocking assignments not permitted in program blocks."); + } + | lpvalue '=' event_control expression ';' + { PAssign*tmp = new PAssign($1,0,$3,$4); + FILE_NAME(tmp, @1); + $$ = tmp; + } + | lpvalue '=' K_repeat '(' expression ')' event_control expression ';' + { PAssign*tmp = new PAssign($1,$5,$7,$8); + FILE_NAME(tmp,@1); + tmp->set_lineno(@1.first_line); + $$ = tmp; + } + | lpvalue K_LE event_control expression ';' + { PAssignNB*tmp = new PAssignNB($1,0,$3,$4); + FILE_NAME(tmp, @1); + $$ = tmp; + if (pform_in_program_block()) + yyerror(@2, "Non-blocking assignments not permitted in program blocks."); + } + | lpvalue K_LE K_repeat '(' expression ')' event_control expression ';' + { PAssignNB*tmp = new PAssignNB($1,$5,$7,$8); + FILE_NAME(tmp, @1); + $$ = tmp; + if (pform_in_program_block()) + yyerror(@2, "Non-blocking assignments not permitted in program blocks."); + } /* The IEEE1800 standard defines dynamic_array_new assignment as a different rule from regular assignment. That implies that the diff --git a/pform.cc b/pform.cc index 4ff15138c..d8acf08ce 100644 --- a/pform.cc +++ b/pform.cc @@ -400,6 +400,15 @@ void pform_bind_attributes(map&attributes, delete attr; } +bool pform_in_program_block() +{ + if (pform_cur_module == 0) + return false; + if (pform_cur_module->program_block) + return true; + return false; +} + static bool pform_at_module_level() { return (lexical_scope == pform_cur_module) @@ -2850,6 +2859,14 @@ PProcess* pform_make_behavior(ivl_process_type_t type, Statement*st, pform_bind_attributes(pp->attributes, attr); pform_put_behavior_in_scope(pp); + + ivl_assert(*st, pform_cur_module); + if (pform_cur_module->program_block && type == IVL_PR_ALWAYS) { + cerr << st->get_fileline() << ": error: Always statements not allowed" + << " in program blocks." << endl; + error_count += 1; + } + return pp; } diff --git a/pform.h b/pform.h index d1a5d8df5..c3b292871 100644 --- a/pform.h +++ b/pform.h @@ -130,6 +130,10 @@ extern void pform_set_default_nettype(NetNet::Type net, const char*file, unsigned lineno); + /* Return true if currently processing a program block. This can be + used to reject statements that cannot exist in program blocks. */ +extern bool pform_in_program_block(void); + /* * Look for the given wire in the current lexical scope. If the wire * (including variables of any type) cannot be found in the current From dfe7beec31aafcee19ed95ea6b7d10114e91e8a9 Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Wed, 9 May 2012 19:35:11 -0700 Subject: [PATCH 037/179] Allow modules (and program blocks in particular) to nest. An important advantage of program blocks is its ability to nest within a module. This winds up also allowing modules to nest, which is legal but presumably less used feature. --- Module.cc | 4 +- Module.h | 6 +- PGate.cc | 10 ++- PGate.h | 4 ++ compiler.h | 5 ++ elab_net.cc | 2 +- elab_scope.cc | 21 +++++- elab_sig.cc | 6 +- elaborate.cc | 20 +++++- net_scope.cc | 13 ++-- netlist.h | 3 +- nodangle.cc | 1 + parse.y | 68 +++++++++--------- parse_misc.cc | 2 + pform.cc | 189 +++++++++++++++++++++++++++----------------------- pform.h | 6 -- pform_dump.cc | 5 ++ t-dll.cc | 1 + 18 files changed, 218 insertions(+), 148 deletions(-) diff --git a/Module.cc b/Module.cc index 589e3e7e3..d448f6dfc 100644 --- a/Module.cc +++ b/Module.cc @@ -27,8 +27,8 @@ list Module::user_defparms; /* n is a permallocated string. */ -Module::Module(perm_string n) -: PScopeExtra(n) +Module::Module(LexicalScope*parent, perm_string n) +: PScopeExtra(n, parent) { library_flag = false; is_cell = false; diff --git a/Module.h b/Module.h index 29da46ae1..ad6604b37 100644 --- a/Module.h +++ b/Module.h @@ -65,7 +65,7 @@ class Module : public PScopeExtra, public LineInfo { public: /* The name passed here is the module name, not the instance name. This make must be a permallocated string. */ - explicit Module(perm_string name); + explicit Module(LexicalScope*parent, perm_string name); ~Module(); /* Initially false. This is set to true if the module has been @@ -121,6 +121,10 @@ class Module : public PScopeExtra, public LineInfo { the module definition. These are used at elaboration time. */ list generate_schemes; + /* Nested modules are placed here, and are not elaborated + unless they are instantiated, implicitly or explicitly. */ + std::map nested_modules; + list specify_paths; // The mod_name() is the name of the module type. diff --git a/PGate.cc b/PGate.cc index 9ec25d33f..05a1dc572 100644 --- a/PGate.cc +++ b/PGate.cc @@ -260,7 +260,7 @@ const char* PGBuiltin::gate_name() const } PGModule::PGModule(perm_string type, perm_string name, list*pins) -: PGate(name, pins), overrides_(0), pins_(0), +: PGate(name, pins), bound_type_(0), overrides_(0), pins_(0), npins_(0), parms_(0), nparms_(0), msb_(0), lsb_(0) { type_ = type; @@ -268,12 +268,18 @@ PGModule::PGModule(perm_string type, perm_string name, list*pins) PGModule::PGModule(perm_string type, perm_string name, named*pins, unsigned npins) -: PGate(name, 0), overrides_(0), pins_(pins), +: PGate(name, 0), bound_type_(0), overrides_(0), pins_(pins), npins_(npins), parms_(0), nparms_(0), msb_(0), lsb_(0) { type_ = type; } +PGModule::PGModule(Module*type, perm_string name) +: PGate(name, 0), bound_type_(type), overrides_(0), pins_(0), + npins_(0), parms_(0), nparms_(0), msb_(0), lsb_(0) +{ +} + PGModule::~PGModule() { } diff --git a/PGate.h b/PGate.h index 84fb26b9d..97e8261c9 100644 --- a/PGate.h +++ b/PGate.h @@ -201,6 +201,9 @@ class PGModule : public PGate { explicit PGModule(perm_string type, perm_string name, named*pins, unsigned npins); + // If the module type is known by design, then use this + // constructor. + explicit PGModule(Module*type, perm_string name); ~PGModule(); @@ -223,6 +226,7 @@ class PGModule : public PGate { perm_string get_type() const; private: + Module*bound_type_; perm_string type_; list*overrides_; named*pins_; diff --git a/compiler.h b/compiler.h index 976cbc831..8fb7b009e 100644 --- a/compiler.h +++ b/compiler.h @@ -177,6 +177,11 @@ static inline bool gn_system_verilog(void) return false; } +static inline bool gn_modules_nest(void) +{ + return gn_system_verilog(); +} + /* The bits of these GN_KEYWORDS_* constants define non-intersecting sets of keywords. The compiler enables groups of keywords by setting lexor_keyword_mask with the OR of the bits for the keywords to be diff --git a/elab_net.cc b/elab_net.cc index 030e22544..4d70820b7 100644 --- a/elab_net.cc +++ b/elab_net.cc @@ -693,7 +693,7 @@ NetNet* PEIdent::elaborate_bi_net(Design*des, NetScope*scope) const */ NetNet* PEIdent::elaborate_port(Design*des, NetScope*scope) const { - assert(scope->type() == NetScope::MODULE); + assert(scope->type_is_module()); NetNet*sig = des->find_signal(scope, path_); if (sig == 0) { cerr << get_fileline() << ": error: no wire/reg " << path_ diff --git a/elab_scope.cc b/elab_scope.cc index 234c8c983..eb17273ed 100644 --- a/elab_scope.cc +++ b/elab_scope.cc @@ -541,6 +541,19 @@ bool Module::elaborate_scope(Design*des, NetScope*scope, elaborate_scope_funcs(des, scope, funcs); + // Look for implicit modules and implicit gates for them. + + for (map::iterator cur = nested_modules.begin() + ; cur != nested_modules.end() ; ++cur) { + // Skip modules that must be explicitly instantiated. + if (cur->second->port_count() > 0) + continue; + + PGModule*nested_gate = new PGModule(cur->second, cur->second->mod_name()); + nested_gate->set_line(*cur->second); + gates_.push_back(nested_gate); + } + // Gates include modules, which might introduce new scopes, so // scan all of them to create those scopes. @@ -1212,7 +1225,7 @@ void PGModule::elaborate_scope_mod_(Design*des, Module*mod, NetScope*sc) const continue; } - if (scn->type() != NetScope::MODULE) continue; + if (! scn->type_is_module()) continue; if (strcmp(mod->mod_name(), scn->module_name()) != 0) continue; @@ -1329,8 +1342,10 @@ void PGModule::elaborate_scope_mod_instances_(Design*des, Module*mod, NetScope*s << "." << endl; } - // Create the new scope as a MODULE with my name. - NetScope*my_scope = new NetScope(sc, use_name, NetScope::MODULE); + // Create the new scope as a MODULE with my name. Note + // that if this is a nested module, mark it thus so that + // scope searches will continue into the parent scope. + NetScope*my_scope = new NetScope(sc, use_name, bound_type_? NetScope::NESTED_MODULE : NetScope::MODULE); my_scope->set_line(get_file(), mod->get_file(), get_lineno(), mod->get_lineno()); my_scope->set_module_name(mod->mod_name()); diff --git a/elab_sig.cc b/elab_sig.cc index 9f2e6b60e..90969c2fa 100644 --- a/elab_sig.cc +++ b/elab_sig.cc @@ -98,7 +98,7 @@ bool PScope::elaborate_sig_wires_(Design*des, NetScope*scope) const reg, then report an error. */ if (sig && (sig->scope() == scope) - && (scope->type() == NetScope::MODULE) + && (scope->type_is_module()) && (sig->port_type() == NetNet::PINPUT) && (sig->type() == NetNet::REG)) { @@ -110,7 +110,7 @@ bool PScope::elaborate_sig_wires_(Design*des, NetScope*scope) const } if (sig && (sig->scope() == scope) - && (scope->type() == NetScope::MODULE) + && (scope->type_is_module()) && (sig->port_type() == NetNet::PINOUT) && (sig->type() == NetNet::REG)) { @@ -122,7 +122,7 @@ bool PScope::elaborate_sig_wires_(Design*des, NetScope*scope) const } if (sig && (sig->scope() == scope) - && (scope->type() == NetScope::MODULE) + && scope->type_is_module() && (sig->port_type() == NetNet::PINOUT) && (sig->data_type() == IVL_VT_REAL)) { diff --git a/elaborate.cc b/elaborate.cc index c684b0de1..fa309110d 100644 --- a/elaborate.cc +++ b/elaborate.cc @@ -2068,6 +2068,10 @@ void PGModule::elaborate_udp_(Design*des, PUdp*udp, NetScope*scope) const bool PGModule::elaborate_sig(Design*des, NetScope*scope) const { + if (bound_type_) { + return elaborate_sig_mod_(des, scope, bound_type_); + } + // Look for the module type map::const_iterator mod = pform_modules.find(type_); if (mod != pform_modules.end()) @@ -2087,6 +2091,11 @@ bool PGModule::elaborate_sig(Design*des, NetScope*scope) const void PGModule::elaborate(Design*des, NetScope*scope) const { + if (bound_type_) { + elaborate_mod_(des, bound_type_, scope); + return; + } + // Look for the module type map::const_iterator mod = pform_modules.find(type_); if (mod != pform_modules.end()) { @@ -2108,10 +2117,16 @@ void PGModule::elaborate(Design*des, NetScope*scope) const void PGModule::elaborate_scope(Design*des, NetScope*sc) const { + // If the module type is known by design, then go right to it. + if (bound_type_) { + elaborate_scope_mod_(des, bound_type_, sc); + return; + } + // Look for the module type map::const_iterator mod = pform_modules.find(type_); if (mod != pform_modules.end()) { - elaborate_scope_mod_(des, (*mod).second, sc); + elaborate_scope_mod_(des, mod->second, sc); return; } @@ -2128,7 +2143,7 @@ void PGModule::elaborate_scope(Design*des, NetScope*sc) const // Try again to find the module type mod = pform_modules.find(type_); if (mod != pform_modules.end()) { - elaborate_scope_mod_(des, (*mod).second, sc); + elaborate_scope_mod_(des, mod->second, sc); return; } @@ -3241,6 +3256,7 @@ NetProc* PDisable::elaborate(Design*des, NetScope*scope) const return 0; case NetScope::MODULE: + case NetScope::NESTED_MODULE: cerr << get_fileline() << ": error: Cannot disable modules." << endl; des->errors += 1; return 0; diff --git a/net_scope.cc b/net_scope.cc index d51ade0b4..22a67e6fc 100644 --- a/net_scope.cc +++ b/net_scope.cc @@ -302,6 +302,9 @@ void NetScope::print_type(ostream&stream) const case FUNC: stream << "function"; break; + case NESTED_MODULE: + stream << "nested_module <" << (module_name_ ? module_name_.str() : "") + << "> instance"; case MODULE: stream << "module <" << (module_name_ ? module_name_.str() : "") << "> instance"; @@ -360,31 +363,31 @@ const NetFuncDef* NetScope::func_def() const void NetScope::set_module_name(perm_string n) { - assert(type_ == MODULE); + assert(type_ == MODULE || type_ == NESTED_MODULE); module_name_ = n; /* NOTE: n must have been permallocated. */ } perm_string NetScope::module_name() const { - assert(type_ == MODULE); + assert(type_ == MODULE || type_ == NESTED_MODULE); return module_name_; } void NetScope::add_module_port(NetNet*port) { - assert(type_ == MODULE); + assert(type_ == MODULE || type_ == NESTED_MODULE); ports_.push_back(port); } unsigned NetScope::module_ports() const { - assert(type_ == MODULE); + assert(type_ == MODULE || type_ == NESTED_MODULE); return ports_.size(); } NetNet* NetScope::module_port(unsigned idx) const { - assert(type_ == MODULE); + assert(type_ == MODULE || type_ == NESTED_MODULE); assert(idx < ports_.size()); return ports_[idx]; } diff --git a/netlist.h b/netlist.h index 06684d804..ddfc70e3d 100644 --- a/netlist.h +++ b/netlist.h @@ -724,7 +724,7 @@ extern std::ostream&operator << (std::ostream&out, const std::list&r class NetScope : public Attrib { public: - enum TYPE { MODULE, TASK, FUNC, BEGIN_END, FORK_JOIN, GENBLOCK }; + enum TYPE { MODULE, NESTED_MODULE, TASK, FUNC, BEGIN_END, FORK_JOIN, GENBLOCK }; /* Create a new scope, and attach it to the given parent. The name is expected to have been permallocated. */ @@ -806,6 +806,7 @@ class NetScope : public Attrib { const NetScope* child(const hname_t&name) const; TYPE type() const; + bool type_is_module() const { return type()==MODULE || type()==NESTED_MODULE; } void print_type(ostream&) const; void set_task_def(NetTaskDef*); diff --git a/nodangle.cc b/nodangle.cc index 6e01d4aba..229d21214 100644 --- a/nodangle.cc +++ b/nodangle.cc @@ -136,6 +136,7 @@ void nodangle_f::signal(Design*, NetNet*sig) if ((sig->port_type() != NetNet::NOT_A_PORT) && ((sig->scope()->type() == NetScope::TASK) || (sig->scope()->type() == NetScope::FUNC) || + (sig->scope()->type() == NetScope::NESTED_MODULE) || (sig->scope()->type() == NetScope::MODULE))) return; diff --git a/parse.y b/parse.y index c4a21fa3d..185038531 100644 --- a/parse.y +++ b/parse.y @@ -3866,43 +3866,46 @@ module_parameter_port_list module_item + /* Modules can contain further sub-module definitions. */ + : module + /* This rule detects net declarations that possibly include a primitive type, an optional vector range and signed flag. This also includes an optional delay set. The values are then applied to a list of names. If the primitive type is not specified, then resort to the default type LOGIC. */ - : attribute_list_opt net_type - primitive_type_opt unsigned_signed_opt range_opt - delay3_opt - net_variable_list ';' + | attribute_list_opt net_type + primitive_type_opt unsigned_signed_opt range_opt + delay3_opt + net_variable_list ';' - { ivl_variable_type_t dtype = $3; - if (dtype == IVL_VT_NO_TYPE) - dtype = IVL_VT_LOGIC; - pform_makewire(@2, $5, $4, $7, $2, - NetNet::NOT_A_PORT, dtype, $1); - if ($6 != 0) { - yyerror(@6, "sorry: net delays not supported."); - delete $6; - } - delete $1; - } + { ivl_variable_type_t dtype = $3; + if (dtype == IVL_VT_NO_TYPE) + dtype = IVL_VT_LOGIC; + pform_makewire(@2, $5, $4, $7, $2, NetNet::NOT_A_PORT, dtype, $1); + if ($6 != 0) { + yyerror(@6, "sorry: net delays not supported."); + delete $6; + } + delete $1; + } - | attribute_list_opt K_wreal delay3 net_variable_list ';' - { pform_makewire(@2, 0, true, $4, NetNet::WIRE, - NetNet::NOT_A_PORT, IVL_VT_REAL, $1); - if ($3 != 0) { - yyerror(@3, "sorry: net delays not supported."); - delete $3; - } - delete $1; - } - | attribute_list_opt K_wreal net_variable_list ';' - { pform_makewire(@2, 0, true, $3, NetNet::WIRE, - NetNet::NOT_A_PORT, IVL_VT_REAL, $1); - delete $1; - } + | attribute_list_opt K_wreal delay3 net_variable_list ';' + { pform_makewire(@2, 0, true, $4, NetNet::WIRE, + NetNet::NOT_A_PORT, IVL_VT_REAL, $1); + if ($3 != 0) { + yyerror(@3, "sorry: net delays not supported."); + delete $3; + } + delete $1; + } + + | attribute_list_opt K_wreal net_variable_list ';' + { pform_makewire(@2, 0, true, $3, NetNet::WIRE, + NetNet::NOT_A_PORT, IVL_VT_REAL, $1); + delete $1; + } /* Very similar to the rule above, but this takes a list of net_decl_assigns, which are = assignment @@ -4248,13 +4251,6 @@ module_item module items. These rules try to catch them at a point where a reasonable error message can be produced. */ - | K_module error ';' - { yyerror(@1, "error: missing endmodule or attempt to " - "nest modules."); - pform_error_nested_modules(); - yyerrok; - } - | error ';' { yyerror(@2, "error: invalid module item."); yyerrok; diff --git a/parse_misc.cc b/parse_misc.cc index 241341564..32f1bafc6 100644 --- a/parse_misc.cc +++ b/parse_misc.cc @@ -33,6 +33,8 @@ std::ostream& operator << (std::ostream&o, const YYLTYPE&loc) { if (loc.text) o << loc.text << ":"; + else + o << "<>:"; o << loc.first_line; return o; } diff --git a/pform.cc b/pform.cc index d8acf08ce..726cead0d 100644 --- a/pform.cc +++ b/pform.cc @@ -41,7 +41,13 @@ # include "ivl_assert.h" # include "ivl_alloc.h" +/* + * The pform_modules is a map of the modules that have been defined in + * the top level. This should not contain nested modules/programs. + */ map pform_modules; +/* + */ map pform_primitives; @@ -217,7 +223,7 @@ extern int VLparse(); /* This tracks the current module being processed. There can only be exactly one module currently being parsed, since Verilog does not allow nested module definitions. */ -static Module*pform_cur_module = 0; +static listpform_cur_module; bool pform_library_flag = false; @@ -307,7 +313,7 @@ PTask* pform_push_task_scope(const struct vlltype&loc, char*name, bool is_auto) pform_cur_generate->tasks.end()) { cerr << task->get_fileline() << ": error: duplicate " "definition for task '" << name << "' in '" - << pform_cur_module->mod_name() << "' (generate)." + << pform_cur_module.front()->mod_name() << "' (generate)." << endl; error_count += 1; } @@ -350,7 +356,7 @@ PFunction* pform_push_function_scope(const struct vlltype&loc, char*name, pform_cur_generate->funcs.end()) { cerr << func->get_fileline() << ": error: duplicate " "definition for function '" << name << "' in '" - << pform_cur_module->mod_name() << "' (generate)." + << pform_cur_module.front()->mod_name() << "' (generate)." << endl; error_count += 1; } @@ -402,16 +408,16 @@ void pform_bind_attributes(map&attributes, bool pform_in_program_block() { - if (pform_cur_module == 0) + if (pform_cur_module.size() == 0) return false; - if (pform_cur_module->program_block) + if (pform_cur_module.front()->program_block) return true; return false; } static bool pform_at_module_level() { - return (lexical_scope == pform_cur_module) + return (lexical_scope == pform_cur_module.front()) || (lexical_scope == pform_cur_generate); } @@ -488,15 +494,15 @@ void pform_set_default_nettype(NetNet::Type type, { pform_default_nettype = type; - if (pform_cur_module) { + if (pform_cur_module.size() > 0) { cerr << file<<":"<mod_name() + << "module " << pform_cur_module.back()->mod_name() << " starts on line " - << pform_cur_module->get_fileline() << "." << endl; + << pform_cur_module.back()->get_fileline() << "." << endl; error_count += 1; } } @@ -699,14 +705,14 @@ void pform_set_timeunit(const char*txt, bool in_module, bool only_check) if (in_module) { if (!only_check) { - pform_cur_module->time_unit = val; + pform_cur_module.front()->time_unit = val; tu_decl_flag = true; tu_local_flag = true; } else if (!tu_decl_flag) { VLerror(yylloc, "error: repeat timeunit found and the " "initial module timeunit is missing."); return; - } else if (pform_cur_module->time_unit != val) { + } else if (pform_cur_module.front()->time_unit != val) { VLerror(yylloc, "error: repeat timeunit does not match " "the initial module timeunit " "declaration."); @@ -723,7 +729,7 @@ void pform_set_timeunit(const char*txt, bool in_module, bool only_check) int pform_get_timeunit() { - return pform_cur_module->time_unit; + return pform_cur_module.front()->time_unit; } void pform_set_timeprecision(const char*txt, bool in_module, bool only_check) @@ -734,14 +740,14 @@ void pform_set_timeprecision(const char*txt, bool in_module, bool only_check) if (in_module) { if (!only_check) { - pform_cur_module->time_precision = val; + pform_cur_module.front()->time_precision = val; tp_decl_flag = true; tp_local_flag = true; } else if (!tp_decl_flag) { VLerror(yylloc, "error: repeat timeprecision found and the " "initial module timeprecision is missing."); return; - } else if (pform_cur_module->time_precision != val) { + } else if (pform_cur_module.front()->time_precision != val) { VLerror(yylloc, "error: repeat timeprecision does not match " "the initial module timeprecision " "declaration."); @@ -808,26 +814,32 @@ verinum* pform_verinum_with_size(verinum*siz, verinum*val, void pform_startmodule(const struct vlltype&loc, const char*name, bool program_block, list*attr) { - assert( pform_cur_module == 0 ); + if (pform_cur_module.size() > 0 && !gn_system_verilog()) { + cerr << loc << ": error: Module definition " << name + << " cannot nest into module " << pform_cur_module.front()->mod_name() << "." << endl; + error_count += 1; + } + perm_string lex_name = lex_strings.make(name); - pform_cur_module = new Module(lex_name); - pform_cur_module->program_block = program_block; + Module*cur_module = new Module(lexical_scope, lex_name); + cur_module->program_block = program_block; /* Set the local time unit/precision to the global value. */ - pform_cur_module->time_unit = pform_time_unit; - pform_cur_module->time_precision = pform_time_prec; + cur_module->time_unit = pform_time_unit; + cur_module->time_precision = pform_time_prec; tu_local_flag = tu_global_flag; tp_local_flag = tp_global_flag; /* If we have a timescale file then the time information is from * a timescale directive. */ - pform_cur_module->time_from_timescale = pform_timescale_file != 0; + cur_module->time_from_timescale = pform_timescale_file != 0; - FILE_NAME(pform_cur_module, loc); - pform_cur_module->library_flag = pform_library_flag; + FILE_NAME(cur_module, loc); + cur_module->library_flag = pform_library_flag; - ivl_assert(*pform_cur_module, lexical_scope == 0); - lexical_scope = pform_cur_module; + pform_cur_module.push_front(cur_module); + + lexical_scope = cur_module; /* The generate scheme numbering starts with *1*, not zero. That's just the way it is, thanks to the standard. */ @@ -836,13 +848,13 @@ void pform_startmodule(const struct vlltype&loc, const char*name, if (warn_timescale && pform_timescale_file && (strcmp(pform_timescale_file,loc.text) != 0)) { - cerr << pform_cur_module->get_fileline() << ": warning: " + cerr << cur_module->get_fileline() << ": warning: " << "timescale for " << name << " inherited from another file." << endl; cerr << pform_timescale_file << ":" << pform_timescale_line << ": ...: The inherited timescale is here." << endl; } - pform_bind_attributes(pform_cur_module->attributes, attr); + pform_bind_attributes(cur_module->attributes, attr); } /* @@ -852,13 +864,12 @@ void pform_startmodule(const struct vlltype&loc, const char*name, */ void pform_check_timeunit_prec() { - assert(pform_cur_module); + assert(pform_cur_module.size() > 0); if ((generation_flag & (GN_VER2005_SV | GN_VER2009)) && - (pform_cur_module->time_unit < pform_cur_module->time_precision)) { - VLerror("error: a timeprecision is missing or is too " - "large!"); - } else assert(pform_cur_module->time_unit >= - pform_cur_module->time_precision); + (pform_cur_module.front()->time_unit < pform_cur_module.front()->time_precision)) { + VLerror("error: a timeprecision is missing or is too large!"); + } else assert(pform_cur_module.front()->time_unit >= + pform_cur_module.front()->time_precision); } /* @@ -881,7 +892,7 @@ Module::port_t* pform_module_port_reference(perm_string name, void pform_module_set_ports(vector*ports) { - assert(pform_cur_module); + assert(pform_cur_module.size() > 0); /* The parser parses ``module foo()'' as having one unconnected port, but it is really a module with no @@ -892,7 +903,7 @@ void pform_module_set_ports(vector*ports) } if (ports != 0) { - pform_cur_module->ports = *ports; + pform_cur_module.front()->ports = *ports; delete ports; } } @@ -900,34 +911,44 @@ void pform_module_set_ports(vector*ports) void pform_endmodule(const char*name, bool inside_celldefine, Module::UCDriveType uc_drive_def) { - assert(pform_cur_module); - pform_cur_module->time_from_timescale = (tu_local_flag && - tp_local_flag) || - (pform_timescale_file != 0); - perm_string mod_name = pform_cur_module->mod_name(); + assert(pform_cur_module.size() > 0); + Module*cur_module = pform_cur_module.front(); + pform_cur_module.pop_front(); + + cur_module->time_from_timescale = (tu_local_flag && tp_local_flag) + || (pform_timescale_file != 0); + perm_string mod_name = cur_module->mod_name(); assert(strcmp(name, mod_name) == 0); - pform_cur_module->is_cell = inside_celldefine; - pform_cur_module->uc_drive = uc_drive_def; + cur_module->is_cell = inside_celldefine; + cur_module->uc_drive = uc_drive_def; + + // If this is a root module, then there is no parent module + // and we try to put this newly defined module into the global + // root list of modules. Otherwise, this is a nested module + // and we put it into the parent module scope to be elaborated + // if needed. + map&use_module_map = (pform_cur_module.size() == 0) + ? pform_modules + : pform_cur_module.front()->nested_modules; map::const_iterator test = - pform_modules.find(mod_name); + use_module_map.find(mod_name); - if (test != pform_modules.end()) { + if (test != use_module_map.end()) { ostringstream msg; msg << "Module " << name << " was already declared here: " - << (*test).second->get_fileline() << endl; + << test->second->get_fileline() << endl; VLerror(msg.str().c_str()); } else { - pform_modules[mod_name] = pform_cur_module; + use_module_map[mod_name] = cur_module; } // The current lexical scope should be this module by now, and // this module should not have a parent lexical scope. - ivl_assert(*pform_cur_module, lexical_scope == pform_cur_module); + ivl_assert(*cur_module, lexical_scope == cur_module); pform_pop_scope(); - ivl_assert(*pform_cur_module, lexical_scope == 0); + ivl_assert(*cur_module, pform_cur_module.size()>0 || lexical_scope == 0); - pform_cur_module = 0; tp_decl_flag = false; tu_decl_flag = false; tu_local_flag = false; @@ -958,7 +979,7 @@ void pform_genvars(const struct vlltype&li, list*names) if (pform_cur_generate) pform_add_genvar(li, *cur, pform_cur_generate->genvars); else - pform_add_genvar(li, *cur, pform_cur_module->genvars); + pform_add_genvar(li, *cur, pform_cur_module.front()->genvars); } delete names; @@ -1113,7 +1134,7 @@ void pform_generate_block_name(char*name) void pform_endgenerate() { assert(pform_cur_generate != 0); - assert(pform_cur_module); + assert(pform_cur_module.size() > 0); // If there is no explicit block name then generate a temporary // name. This will be replaced by the correct name later, once @@ -1137,7 +1158,7 @@ void pform_endgenerate() parent_generate->generate_schemes.push_back(pform_cur_generate); } else { assert(pform_cur_generate->scheme_type != PGenerate::GS_CASE_ITEM); - pform_cur_module->generate_schemes.push_back(pform_cur_generate); + pform_cur_module.front()->generate_schemes.push_back(pform_cur_generate); } pform_cur_generate = parent_generate; } @@ -1593,7 +1614,7 @@ static void pform_make_event(perm_string name, const char*fn, unsigned ln) FILE_NAME(&tloc, fn, ln); cerr << tloc.get_fileline() << ": error: duplicate definition " "for named event '" << name << "' in '" - << pform_cur_module->mod_name() << "'." << endl; + << pform_cur_module.front()->mod_name() << "'." << endl; error_count += 1; } @@ -1653,7 +1674,7 @@ static void pform_makegate(PGBuiltin::Type type, if (pform_cur_generate) pform_cur_generate->add_gate(cur); else - pform_cur_module->add_gate(cur); + pform_cur_module.front()->add_gate(cur); } void pform_makegates(PGBuiltin::Type type, @@ -1719,7 +1740,7 @@ static void pform_make_modgate(perm_string type, if (pform_cur_generate) pform_cur_generate->add_gate(cur); else - pform_cur_module->add_gate(cur); + pform_cur_module.front()->add_gate(cur); } static void pform_make_modgate(perm_string type, @@ -1763,7 +1784,7 @@ static void pform_make_modgate(perm_string type, if (pform_cur_generate) pform_cur_generate->add_gate(cur); else - pform_cur_module->add_gate(cur); + pform_cur_module.front()->add_gate(cur); } void pform_make_modgates(perm_string type, @@ -1833,7 +1854,7 @@ static PGAssign* pform_make_pgassign(PExpr*lval, PExpr*rval, if (pform_cur_generate) pform_cur_generate->add_gate(cur); else - pform_cur_module->add_gate(cur); + pform_cur_module.front()->add_gate(cur); return cur; } @@ -2305,7 +2326,7 @@ void pform_set_attrib(perm_string name, perm_string key, char*value) if (PWire*cur = lexical_scope->wires_find(name)) { cur->attributes[key] = new PEString(value); - } else if (PGate*curg = pform_cur_module->get_gate(name)) { + } else if (PGate*curg = pform_cur_module.front()->get_gate(name)) { curg->attributes[key] = new PEString(value); } else { @@ -2384,23 +2405,23 @@ void pform_set_parameter(const struct vlltype&loc, FILE_NAME(&tloc, loc); cerr << tloc.get_fileline() << ": error: duplicate definition " "for parameter '" << name << "' in '" - << pform_cur_module->mod_name() << "'." << endl; + << pform_cur_module.front()->mod_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 " - "parameter in '" << pform_cur_module->mod_name() + << "parameter in '" << pform_cur_module.front()->mod_name() << "' 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())) { + if ((scope == pform_cur_module.front()) && + (pform_cur_module.front()->specparams.find(name) != pform_cur_module.front()->specparams.end())) { LineInfo tloc; FILE_NAME(&tloc, loc); cerr << tloc.get_fileline() << ": error: specparam and " - "parameter in '" << pform_cur_module->mod_name() + "parameter in '" << pform_cur_module.front()->mod_name() << "' have the same name '" << name << "'." << endl; error_count += 1; } @@ -2426,8 +2447,8 @@ void pform_set_parameter(const struct vlltype&loc, parm.signed_flag = signed_flag; parm.range = value_range; - if (scope == pform_cur_module) - pform_cur_module->param_names.push_back(name); + if (scope == pform_cur_module.front()) + pform_cur_module.front()->param_names.push_back(name); } void pform_set_localparam(const struct vlltype&loc, @@ -2442,23 +2463,23 @@ void pform_set_localparam(const struct vlltype&loc, FILE_NAME(&tloc, loc); cerr << tloc.get_fileline() << ": error: duplicate definition " "for localparam '" << name << "' in '" - << pform_cur_module->mod_name() << "'." << endl; + << pform_cur_module.front()->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 " - "localparam in '" << pform_cur_module->mod_name() + << "localparam in '" << pform_cur_module.front()->mod_name() << "' 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())) { + if ((scope == pform_cur_module.front()) && + (pform_cur_module.front()->specparams.find(name) != pform_cur_module.front()->specparams.end())) { LineInfo tloc; FILE_NAME(&tloc, loc); cerr << tloc.get_fileline() << ": error: specparam and " - "localparam in '" << pform_cur_module->mod_name() + "localparam in '" << pform_cur_module.front()->mod_name() << "' have the same name '" << name << "'." << endl; error_count += 1; } @@ -2488,23 +2509,25 @@ void pform_set_localparam(const struct vlltype&loc, void pform_set_specparam(const struct vlltype&loc, perm_string name, list*range, PExpr*expr) { - Module*scope = pform_cur_module; + assert(pform_cur_module.size() > 0); + Module*scope = pform_cur_module.front(); assert(scope == lexical_scope); // Check if the specparam name is already in the dictionary. - if (scope->specparams.find(name) != scope->specparams.end()) { + if (pform_cur_module.front()->specparams.find(name) != + pform_cur_module.front()->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; + << pform_cur_module.front()->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() + "specparam in '" << pform_cur_module.front()->mod_name() << "' have the same name '" << name << "'." << endl; error_count += 1; } @@ -2512,13 +2535,14 @@ void pform_set_specparam(const struct vlltype&loc, perm_string name, LineInfo tloc; FILE_NAME(&tloc, loc); cerr << tloc.get_fileline() << ": error: localparam and " - "specparam in '" << pform_cur_module->mod_name() + "specparam in '" << pform_cur_module.front()->mod_name() << "' have the same name '" << name << "'." << endl; error_count += 1; } assert(expr); - Module::param_expr_t&parm = scope->specparams[name]; + + Module::param_expr_t&parm = pform_cur_module.front()->specparams[name]; FILE_NAME(&parm, loc); parm.expr = expr; @@ -2542,7 +2566,7 @@ void pform_set_specparam(const struct vlltype&loc, perm_string name, void pform_set_defparam(const pform_name_t&name, PExpr*expr) { assert(expr); - pform_cur_module->defparms.push_back(make_pair(name,expr)); + pform_cur_module.front()->defparms.push_back(make_pair(name,expr)); } /* @@ -2609,7 +2633,7 @@ extern void pform_module_specify_path(PSpecPath*obj) { if (obj == 0) return; - pform_cur_module->specify_paths.push_back(obj); + pform_cur_module.front()->specify_paths.push_back(obj); } @@ -2860,8 +2884,8 @@ PProcess* pform_make_behavior(ivl_process_type_t type, Statement*st, pform_put_behavior_in_scope(pp); - ivl_assert(*st, pform_cur_module); - if (pform_cur_module->program_block && type == IVL_PR_ALWAYS) { + ivl_assert(*st, pform_cur_module.size() > 0); + if (pform_cur_module.front()->program_block && type == IVL_PR_ALWAYS) { cerr << st->get_fileline() << ": error: Always statements not allowed" << " in program blocks." << endl; error_count += 1; @@ -2908,10 +2932,3 @@ int pform_parse(const char*path, FILE*file) destroy_lexor(); return error_count; } - -void pform_error_nested_modules() -{ - assert( pform_cur_module != 0 ); - cerr << pform_cur_module->get_fileline() << ": error: original module " - "(" << pform_cur_module->mod_name() << ") defined here." << endl; -} diff --git a/pform.h b/pform.h index c3b292871..a1892cbe9 100644 --- a/pform.h +++ b/pform.h @@ -424,12 +424,6 @@ extern PAssign* pform_compressed_assign_from_inc_dec(const struct vlltype&loc, */ extern void pform_dump(ostream&out, Module*mod); -/* - * Used to report the original module location when a nested module - * (missing endmodule) is found by the parser. - */ -extern void pform_error_nested_modules(); - /* ** pform_discipline.cc * Functions for handling the parse of natures and disciplines. These * functions are in pform_disciplines.cc diff --git a/pform_dump.cc b/pform_dump.cc index 364a26b67..12f2db915 100644 --- a/pform_dump.cc +++ b/pform_dump.cc @@ -1249,6 +1249,11 @@ void Module::dump(ostream&out) const out << ")" << endl; } + for (map::const_iterator cur = nested_modules.begin() + ; cur != nested_modules.end() ; ++cur) { + out << setw(4) << "" << "Nested module " << cur->first << ";" << endl; + } + dump_typedefs_(out, 4); dump_parameters_(out, 4); diff --git a/t-dll.cc b/t-dll.cc index 0bee8e88d..ed644be40 100644 --- a/t-dll.cc +++ b/t-dll.cc @@ -2297,6 +2297,7 @@ void dll_target::scope(const NetScope*net) switch (net->type()) { case NetScope::MODULE: + case NetScope::NESTED_MODULE: scop->type_ = IVL_SCT_MODULE; scop->tname_ = net->module_name(); scop->ports = net->module_ports(); From 8154ce2a4a797fd29bc5e4dfa3fb555247c3e220 Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Sun, 13 May 2012 17:48:47 -0700 Subject: [PATCH 038/179] Reword how we enforce program block constraints Making the scope type NESTED_MODULE was just plain wrong, because it didn't really encapsulate the meaning of program blocks OR nested modules. So instead create nested_module() and program_block() flags and use those to test scope constraints. --- design_dump.cc | 3 +++ elab_net.cc | 2 +- elab_scope.cc | 6 ++++-- elab_sig.cc | 6 +++--- elaborate.cc | 45 +++++++++++++++++++++++++++++++++++++++++++-- net_design.cc | 5 +++-- net_scope.cc | 17 +++++++---------- netlist.h | 16 ++++++++++++---- nodangle.cc | 1 - parse.y | 10 ---------- symbol_search.cc | 2 +- t-dll.cc | 1 - 12 files changed, 77 insertions(+), 37 deletions(-) diff --git a/design_dump.cc b/design_dump.cc index 1518f1f21..ce16ec862 100644 --- a/design_dump.cc +++ b/design_dump.cc @@ -1136,6 +1136,9 @@ void NetScope::dump(ostream&o) const print_type(o); if (is_auto()) o << " (automatic)"; if (is_cell()) o << " (cell)"; + if (nested_module()) o << " (nested)"; + if (program_block()) o << " (program)"; + o << endl; for (unsigned idx = 0 ; idx < attr_cnt() ; idx += 1) diff --git a/elab_net.cc b/elab_net.cc index 4d70820b7..fa4a3fd2a 100644 --- a/elab_net.cc +++ b/elab_net.cc @@ -693,7 +693,7 @@ NetNet* PEIdent::elaborate_bi_net(Design*des, NetScope*scope) const */ NetNet* PEIdent::elaborate_port(Design*des, NetScope*scope) const { - assert(scope->type_is_module()); + ivl_assert(*this, scope->type() == NetScope::MODULE); NetNet*sig = des->find_signal(scope, path_); if (sig == 0) { cerr << get_fileline() << ": error: no wire/reg " << path_ diff --git a/elab_scope.cc b/elab_scope.cc index eb17273ed..685dfac2c 100644 --- a/elab_scope.cc +++ b/elab_scope.cc @@ -1225,7 +1225,7 @@ void PGModule::elaborate_scope_mod_(Design*des, Module*mod, NetScope*sc) const continue; } - if (! scn->type_is_module()) continue; + if (scn->type() != NetScope::MODULE) continue; if (strcmp(mod->mod_name(), scn->module_name()) != 0) continue; @@ -1345,7 +1345,9 @@ void PGModule::elaborate_scope_mod_instances_(Design*des, Module*mod, NetScope*s // Create the new scope as a MODULE with my name. Note // that if this is a nested module, mark it thus so that // scope searches will continue into the parent scope. - NetScope*my_scope = new NetScope(sc, use_name, bound_type_? NetScope::NESTED_MODULE : NetScope::MODULE); + NetScope*my_scope = new NetScope(sc, use_name, NetScope::MODULE, + bound_type_? true : false, + mod->program_block); my_scope->set_line(get_file(), mod->get_file(), get_lineno(), mod->get_lineno()); my_scope->set_module_name(mod->mod_name()); diff --git a/elab_sig.cc b/elab_sig.cc index 90969c2fa..9f2e6b60e 100644 --- a/elab_sig.cc +++ b/elab_sig.cc @@ -98,7 +98,7 @@ bool PScope::elaborate_sig_wires_(Design*des, NetScope*scope) const reg, then report an error. */ if (sig && (sig->scope() == scope) - && (scope->type_is_module()) + && (scope->type() == NetScope::MODULE) && (sig->port_type() == NetNet::PINPUT) && (sig->type() == NetNet::REG)) { @@ -110,7 +110,7 @@ bool PScope::elaborate_sig_wires_(Design*des, NetScope*scope) const } if (sig && (sig->scope() == scope) - && (scope->type_is_module()) + && (scope->type() == NetScope::MODULE) && (sig->port_type() == NetNet::PINOUT) && (sig->type() == NetNet::REG)) { @@ -122,7 +122,7 @@ bool PScope::elaborate_sig_wires_(Design*des, NetScope*scope) const } if (sig && (sig->scope() == scope) - && scope->type_is_module() + && (scope->type() == NetScope::MODULE) && (sig->port_type() == NetNet::PINOUT) && (sig->data_type() == IVL_VT_REAL)) { diff --git a/elaborate.cc b/elaborate.cc index fa309110d..25767e5c7 100644 --- a/elaborate.cc +++ b/elaborate.cc @@ -2312,6 +2312,18 @@ NetProc* PAssign::elaborate_compressed_(Design*des, NetScope*scope) const return cur; } +static bool lval_not_program_variable(const NetAssign_*lv) +{ + while (lv) { + NetScope*sig_scope = lv->sig()->scope(); + if (! sig_scope->program_block()) + return true; + + lv = lv->more; + } + return false; +} + NetProc* PAssign::elaborate(Design*des, NetScope*scope) const { assert(scope); @@ -2326,6 +2338,12 @@ NetProc* PAssign::elaborate(Design*des, NetScope*scope) const NetAssign_*lv = elaborate_lval(des, scope); if (lv == 0) return 0; + if (scope->program_block() && lval_not_program_variable(lv)) { + cerr << get_fileline() << ": error: Blocking assignments to " + << "non-program variables are not allowed." << endl; + des->errors += 1; + } + /* If there is an internal delay expression, elaborate it. */ NetExpr*delay = 0; if (delay_ != 0) @@ -2469,6 +2487,22 @@ NetProc* PAssign::elaborate(Design*des, NetScope*scope) const return cur; } +/* + * Return true if any lvalue parts are in a program block scope. + */ +static bool lval_is_program_variable(const NetAssign_*lv) +{ + while (lv) { + NetScope*sig_scope = lv->sig()->scope(); + if (sig_scope->program_block()) + return true; + + lv = lv->more; + } + + return false; +} + /* * Elaborate non-blocking assignments. The statement is of the general * form: @@ -2505,6 +2539,14 @@ NetProc* PAssignNB::elaborate(Design*des, NetScope*scope) const NetAssign_*lv = elaborate_lval(des, scope); if (lv == 0) return 0; + if (scope->program_block() && lval_is_program_variable(lv)) { + cerr << get_fileline() << ": error: Non-blocking assignments to " + << "program variables are not allowed." << endl; + des->errors += 1; + // This is an error, but we can let elaboration continue + // because it would necessarily trigger other errors. + } + NetExpr*rv = elaborate_rval_(des, scope, count_lval_width(lv), lv->expr_type()); if (rv == 0) return 0; @@ -3256,7 +3298,6 @@ NetProc* PDisable::elaborate(Design*des, NetScope*scope) const return 0; case NetScope::MODULE: - case NetScope::NESTED_MODULE: cerr << get_fileline() << ": error: Cannot disable modules." << endl; des->errors += 1; return 0; @@ -4849,7 +4890,7 @@ Design* elaborate(listroots) // Make the root scope. This makes a NetScope object and // pushes it into the list of root scopes in the Design. - NetScope*scope = des->make_root_scope(*root); + NetScope*scope = des->make_root_scope(*root, rmod->program_block); // Collect some basic properties of this scope from the // Module definition. diff --git a/net_design.cc b/net_design.cc index 0ea857cf4..216a7fc20 100644 --- a/net_design.cc +++ b/net_design.cc @@ -101,10 +101,11 @@ uint64_t Design::scale_to_precision(uint64_t val, return val; } -NetScope* Design::make_root_scope(perm_string root) +NetScope* Design::make_root_scope(perm_string root, bool program_block) { NetScope *root_scope_; - root_scope_ = new NetScope(0, hname_t(root), NetScope::MODULE); + root_scope_ = new NetScope(0, hname_t(root), NetScope::MODULE, + false, program_block); /* This relies on the fact that the basename return value is permallocated. */ root_scope_->set_module_name(root_scope_->basename()); diff --git a/net_scope.cc b/net_scope.cc index 22a67e6fc..47d4751ce 100644 --- a/net_scope.cc +++ b/net_scope.cc @@ -38,8 +38,8 @@ class PExpr; * in question. */ -NetScope::NetScope(NetScope*up, const hname_t&n, NetScope::TYPE t) -: type_(t), name_(n), up_(up) +NetScope::NetScope(NetScope*up, const hname_t&n, NetScope::TYPE t, bool nest, bool prog) +: type_(t), name_(n), nested_module_(nest), program_block_(prog), up_(up) { events_ = 0; lcounter_ = 0; @@ -302,9 +302,6 @@ void NetScope::print_type(ostream&stream) const case FUNC: stream << "function"; break; - case NESTED_MODULE: - stream << "nested_module <" << (module_name_ ? module_name_.str() : "") - << "> instance"; case MODULE: stream << "module <" << (module_name_ ? module_name_.str() : "") << "> instance"; @@ -363,31 +360,31 @@ const NetFuncDef* NetScope::func_def() const void NetScope::set_module_name(perm_string n) { - assert(type_ == MODULE || type_ == NESTED_MODULE); + assert(type_ == MODULE); module_name_ = n; /* NOTE: n must have been permallocated. */ } perm_string NetScope::module_name() const { - assert(type_ == MODULE || type_ == NESTED_MODULE); + assert(type_ == MODULE); return module_name_; } void NetScope::add_module_port(NetNet*port) { - assert(type_ == MODULE || type_ == NESTED_MODULE); + assert(type_ == MODULE); ports_.push_back(port); } unsigned NetScope::module_ports() const { - assert(type_ == MODULE || type_ == NESTED_MODULE); + assert(type_ == MODULE); return ports_.size(); } NetNet* NetScope::module_port(unsigned idx) const { - assert(type_ == MODULE || type_ == NESTED_MODULE); + assert(type_ == MODULE); assert(idx < ports_.size()); return ports_[idx]; } diff --git a/netlist.h b/netlist.h index ddfc70e3d..284d28896 100644 --- a/netlist.h +++ b/netlist.h @@ -724,11 +724,11 @@ extern std::ostream&operator << (std::ostream&out, const std::list&r class NetScope : public Attrib { public: - enum TYPE { MODULE, NESTED_MODULE, TASK, FUNC, BEGIN_END, FORK_JOIN, GENBLOCK }; + enum TYPE { MODULE, TASK, FUNC, BEGIN_END, FORK_JOIN, GENBLOCK }; /* Create a new scope, and attach it to the given parent. The name is expected to have been permallocated. */ - NetScope(NetScope*up, const hname_t&name, TYPE t); + NetScope(NetScope*up, const hname_t&name, TYPE t, bool nest=false, bool prog=false); ~NetScope(); /* Rename the scope using the name generated by inserting as @@ -805,8 +805,11 @@ class NetScope : public Attrib { const NetScope* parent() const { return up_; } const NetScope* child(const hname_t&name) const; + // Nested modules have slightly different scope search rules. + inline bool nested_module() const { return nested_module_; } + // Program blocks have elaboration constraints. + inline bool program_block() const { return program_block_; } TYPE type() const; - bool type_is_module() const { return type()==MODULE || type()==NESTED_MODULE; } void print_type(ostream&) const; void set_task_def(NetTaskDef*); @@ -989,6 +992,11 @@ class NetScope : public Attrib { TYPE type_; hname_t name_; + // True if the scope is a nested module/program block + bool nested_module_; + // True if the scope is a program block + bool program_block_; + perm_string file_; perm_string def_file_; unsigned lineno_; @@ -4012,7 +4020,7 @@ class Design { const char* get_flag(const string&key) const; - NetScope* make_root_scope(perm_string name); + NetScope* make_root_scope(perm_string name, bool program_block); NetScope* find_root_scope(); list find_root_scopes(); diff --git a/nodangle.cc b/nodangle.cc index 229d21214..6e01d4aba 100644 --- a/nodangle.cc +++ b/nodangle.cc @@ -136,7 +136,6 @@ void nodangle_f::signal(Design*, NetNet*sig) if ((sig->port_type() != NetNet::NOT_A_PORT) && ((sig->scope()->type() == NetScope::TASK) || (sig->scope()->type() == NetScope::FUNC) || - (sig->scope()->type() == NetScope::NESTED_MODULE) || (sig->scope()->type() == NetScope::MODULE))) return; diff --git a/parse.y b/parse.y index 185038531..736dacf77 100644 --- a/parse.y +++ b/parse.y @@ -5489,15 +5489,11 @@ statement_item /* This is roughly statement_item in the LRM */ { PAssignNB*tmp = new PAssignNB($1,$3); FILE_NAME(tmp, @1); $$ = tmp; - if (pform_in_program_block()) - yyerror(@2, "Non-blocking assignments not permitted in program blocks."); } | error K_LE expression ';' { yyerror(@2, "Syntax in assignment statement l-value."); yyerrok; $$ = new PNoop; - if (pform_in_program_block()) - yyerror(@2, "Non-blocking assignments not permitted in program blocks."); } | lpvalue '=' delay1 expression ';' { PExpr*del = $3->front(); $3->pop_front(); @@ -5512,8 +5508,6 @@ statement_item /* This is roughly statement_item in the LRM */ PAssignNB*tmp = new PAssignNB($1,del,$4); FILE_NAME(tmp, @1); $$ = tmp; - if (pform_in_program_block()) - yyerror(@2, "Non-blocking assignments not permitted in program blocks."); } | lpvalue '=' event_control expression ';' { PAssign*tmp = new PAssign($1,0,$3,$4); @@ -5530,15 +5524,11 @@ statement_item /* This is roughly statement_item in the LRM */ { PAssignNB*tmp = new PAssignNB($1,0,$3,$4); FILE_NAME(tmp, @1); $$ = tmp; - if (pform_in_program_block()) - yyerror(@2, "Non-blocking assignments not permitted in program blocks."); } | lpvalue K_LE K_repeat '(' expression ')' event_control expression ';' { PAssignNB*tmp = new PAssignNB($1,$5,$7,$8); FILE_NAME(tmp, @1); $$ = tmp; - if (pform_in_program_block()) - yyerror(@2, "Non-blocking assignments not permitted in program blocks."); } /* The IEEE1800 standard defines dynamic_array_new assignment as a diff --git a/symbol_search.cc b/symbol_search.cc index d2d6b118c..a4a7a9d25 100644 --- a/symbol_search.cc +++ b/symbol_search.cc @@ -79,7 +79,7 @@ NetScope*symbol_search(const LineInfo*li, Design*des, NetScope*scope, /* We can't look up if we are at the enclosing module scope * or if a hierarchical path was given. */ - if ((scope->type() == NetScope::MODULE) || hier_path) + if ((scope->type()==NetScope::MODULE && !scope->nested_module()) || hier_path) scope = 0; else scope = scope->parent(); diff --git a/t-dll.cc b/t-dll.cc index ed644be40..0bee8e88d 100644 --- a/t-dll.cc +++ b/t-dll.cc @@ -2297,7 +2297,6 @@ void dll_target::scope(const NetScope*net) switch (net->type()) { case NetScope::MODULE: - case NetScope::NESTED_MODULE: scop->type_ = IVL_SCT_MODULE; scop->tname_ = net->module_name(); scop->ports = net->module_ports(); From c0f35cbe622c98917e80c1e2e8f346b18d888901 Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Mon, 14 May 2012 19:13:57 -0700 Subject: [PATCH 039/179] Disallow modules/gates in program blocks. --- parse.y | 88 ++++++++++++++++++++++++++------------------------------ pform.cc | 23 +++++++++++++-- pform.h | 6 ++-- 3 files changed, 65 insertions(+), 52 deletions(-) diff --git a/parse.y b/parse.y index 736dacf77..219a1960b 100644 --- a/parse.y +++ b/parse.y @@ -1082,6 +1082,14 @@ integer_vector_type /* IEEE1800-2005: A.2.2.1 */ | K_bool { $$ = IVL_VT_BOOL; } /* Icarus Verilog xtypes extension */ ; +join_keyword /* IEEE1800-2005: A.6.3 */ + : K_join + | K_join_none + { yyerror(@1, "sorry: join_none not supported."); } + | K_join_any + { yyerror(@1, "sorry: join_any not supported."); } + ; + jump_statement /* IEEE1800-2005: A.6.5 */ : K_break ';' { yyerror(@1, "sorry: break statements not supported."); @@ -4056,66 +4064,50 @@ module_item two/three-value delay. These rules handle the different cases. We check that the actual number of delays is correct later. */ - | attribute_list_opt gatetype gate_instance_list ';' - { pform_makegates($2, str_strength, 0, $3, $1); - } + | attribute_list_opt gatetype gate_instance_list ';' + { pform_makegates(@2, $2, str_strength, 0, $3, $1); } - | attribute_list_opt gatetype delay3 gate_instance_list ';' - { pform_makegates($2, str_strength, $3, $4, $1); - } + | attribute_list_opt gatetype delay3 gate_instance_list ';' + { pform_makegates(@2, $2, str_strength, $3, $4, $1); } - | attribute_list_opt gatetype drive_strength gate_instance_list ';' - { pform_makegates($2, $3, 0, $4, $1); - } + | attribute_list_opt gatetype drive_strength gate_instance_list ';' + { pform_makegates(@2, $2, $3, 0, $4, $1); } - | attribute_list_opt gatetype drive_strength delay3 gate_instance_list ';' - { pform_makegates($2, $3, $4, $5, $1); - } + | attribute_list_opt gatetype drive_strength delay3 gate_instance_list ';' + { pform_makegates(@2, $2, $3, $4, $5, $1); } /* The switch type gates do not support a strength. */ - | attribute_list_opt switchtype gate_instance_list ';' - { pform_makegates($2, str_strength, 0, $3, $1); - } + | attribute_list_opt switchtype gate_instance_list ';' + { pform_makegates(@2, $2, str_strength, 0, $3, $1); } - | attribute_list_opt switchtype delay3 gate_instance_list ';' - { pform_makegates($2, str_strength, $3, $4, $1); - } + | attribute_list_opt switchtype delay3 gate_instance_list ';' + { pform_makegates(@2, $2, str_strength, $3, $4, $1); } /* Pullup and pulldown devices cannot have delays, and their strengths are limited. */ - | K_pullup gate_instance_list ';' - { pform_makegates(PGBuiltin::PULLUP, pull_strength, 0, - $2, 0); - } - | K_pulldown gate_instance_list ';' - { pform_makegates(PGBuiltin::PULLDOWN, pull_strength, - 0, $2, 0); - } + | K_pullup gate_instance_list ';' + { pform_makegates(@1, PGBuiltin::PULLUP, pull_strength, 0, $2, 0); } + | K_pulldown gate_instance_list ';' + { pform_makegates(@1, PGBuiltin::PULLDOWN, pull_strength, 0, $2, 0); } - | K_pullup '(' dr_strength1 ')' gate_instance_list ';' - { pform_makegates(PGBuiltin::PULLUP, $3, 0, $5, 0); - } + | K_pullup '(' dr_strength1 ')' gate_instance_list ';' + { pform_makegates(@1, PGBuiltin::PULLUP, $3, 0, $5, 0); } - | K_pullup '(' dr_strength1 ',' dr_strength0 ')' gate_instance_list ';' - { pform_makegates(PGBuiltin::PULLUP, $3, 0, $7, 0); - } + | K_pullup '(' dr_strength1 ',' dr_strength0 ')' gate_instance_list ';' + { pform_makegates(@1, PGBuiltin::PULLUP, $3, 0, $7, 0); } - | K_pullup '(' dr_strength0 ',' dr_strength1 ')' gate_instance_list ';' - { pform_makegates(PGBuiltin::PULLUP, $5, 0, $7, 0); - } + | K_pullup '(' dr_strength0 ',' dr_strength1 ')' gate_instance_list ';' + { pform_makegates(@1, PGBuiltin::PULLUP, $5, 0, $7, 0); } - | K_pulldown '(' dr_strength0 ')' gate_instance_list ';' - { pform_makegates(PGBuiltin::PULLDOWN, $3, 0, $5, 0); - } + | K_pulldown '(' dr_strength0 ')' gate_instance_list ';' + { pform_makegates(@1, PGBuiltin::PULLDOWN, $3, 0, $5, 0); } - | K_pulldown '(' dr_strength1 ',' dr_strength0 ')' gate_instance_list ';' - { pform_makegates(PGBuiltin::PULLDOWN, $5, 0, $7, 0); - } + | K_pulldown '(' dr_strength1 ',' dr_strength0 ')' gate_instance_list ';' + { pform_makegates(@1, PGBuiltin::PULLDOWN, $5, 0, $7, 0); } - | K_pulldown '(' dr_strength0 ',' dr_strength1 ')' gate_instance_list ';' - { pform_makegates(PGBuiltin::PULLDOWN, $3, 0, $7, 0); - } + | K_pulldown '(' dr_strength0 ',' dr_strength1 ')' gate_instance_list ';' + { pform_makegates(@1, PGBuiltin::PULLDOWN, $3, 0, $7, 0); } /* This rule handles instantiations of modules and user defined primitives. These devices to not have delay lists or strengths, @@ -4124,7 +4116,7 @@ module_item | attribute_list_opt IDENTIFIER parameter_value_opt gate_instance_list ';' { perm_string tmp1 = lex_strings.make($2); - pform_make_modgates(tmp1, $3, $4); + pform_make_modgates(@2, tmp1, $3, $4); delete[]$2; if ($1) delete $1; } @@ -5342,12 +5334,12 @@ statement_item /* This is roughly statement_item in the LRM */ need to do is remember that this is a parallel block so that the code generator can do the right thing. */ - | K_fork K_join + | K_fork join_keyword { PBlock*tmp = new PBlock(PBlock::BL_PAR); FILE_NAME(tmp, @1); $$ = tmp; } - | K_fork statement_or_null_list K_join + | K_fork statement_or_null_list join_keyword { PBlock*tmp = new PBlock(PBlock::BL_PAR); FILE_NAME(tmp, @1); tmp->set_statement(*$2); @@ -5360,7 +5352,7 @@ statement_item /* This is roughly statement_item in the LRM */ current_block_stack.push(tmp); } block_item_decls_opt - statement_or_null_list_opt K_join + statement_or_null_list_opt join_keyword { pform_pop_scope(); assert(! current_block_stack.empty()); PBlock*tmp = current_block_stack.top(); diff --git a/pform.cc b/pform.cc index 726cead0d..ee878a868 100644 --- a/pform.cc +++ b/pform.cc @@ -820,6 +820,10 @@ void pform_startmodule(const struct vlltype&loc, const char*name, error_count += 1; } + if (gn_system_verilog() && pform_cur_module.size() > 0 && pform_cur_module.front()->program_block) { + cerr << loc << ": error: Program blocks cannot contain nested modules/program blocks." << endl; + error_count += 1; + } perm_string lex_name = lex_strings.make(name); Module*cur_module = new Module(lexical_scope, lex_name); @@ -1677,12 +1681,20 @@ static void pform_makegate(PGBuiltin::Type type, pform_cur_module.front()->add_gate(cur); } -void pform_makegates(PGBuiltin::Type type, +void pform_makegates(const struct vlltype&loc, + PGBuiltin::Type type, struct str_pair_t str, list*delay, svector*gates, list*attr) { + assert(pform_cur_module.size() > 0); + if (pform_cur_module.front()->program_block) { + cerr << loc << ": error: Gates and switches may not be instantiated" + << " in program blocks." << endl; + error_count += 1; + } + for (unsigned idx = 0 ; idx < gates->count() ; idx += 1) { pform_makegate(type, str, delay, (*gates)[idx], attr); } @@ -1787,10 +1799,17 @@ static void pform_make_modgate(perm_string type, pform_cur_module.front()->add_gate(cur); } -void pform_make_modgates(perm_string type, +void pform_make_modgates(const struct vlltype&loc, + perm_string type, struct parmvalue_t*overrides, svector*gates) { + assert(pform_cur_module.size() > 0); + if (pform_cur_module.front()->program_block) { + cerr << loc << ": error: Module instantiations are not allowed" + << " in program blocks." << endl; + error_count += 1; + } for (unsigned idx = 0 ; idx < gates->count() ; idx += 1) { lgate cur = (*gates)[idx]; diff --git a/pform.h b/pform.h index a1892cbe9..23d45c558 100644 --- a/pform.h +++ b/pform.h @@ -377,13 +377,15 @@ extern void pform_make_reals(list*names, * The makegate function creates a new gate (which need not have a * name) and connects it to the specified wires. */ -extern void pform_makegates(PGBuiltin::Type type, +extern void pform_makegates(const struct vlltype&loc, + PGBuiltin::Type type, struct str_pair_t str, list*delay, svector*gates, list*attr); -extern void pform_make_modgates(perm_string type, +extern void pform_make_modgates(const struct vlltype&loc, + perm_string type, struct parmvalue_t*overrides, svector*gates); From 6a57764e0ec48cc89f10afd4e762dae9d17d5e87 Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Sat, 19 May 2012 17:34:13 -0700 Subject: [PATCH 040/179] Elaborate fork-join_none and fork-join_any statements. --- Statement.cc | 9 ++++++++- Statement.h | 10 +++++++--- design_dump.cc | 6 ++++++ elab_scope.cc | 4 +++- elaborate.cc | 18 +++++++++++++++--- ivl_target.h | 10 ++++++---- net_nex_input.cc | 4 ++-- netlist.h | 2 +- parse.y | 13 +++++++++---- t-dll-api.cc | 8 +++++++- t-dll-proc.cc | 19 +++++++++++++++---- tgt-stub/statement.c | 24 +++++++++++++++++++++++- 12 files changed, 102 insertions(+), 25 deletions(-) diff --git a/Statement.cc b/Statement.cc index a99e8b37e..64df85ebd 100644 --- a/Statement.cc +++ b/Statement.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998-2008,2010 Stephen Williams (steve@icarus.com) + * Copyright (c) 1998-2008,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 @@ -114,6 +114,13 @@ PBlock::~PBlock() delete list_[idx]; } +void PBlock::set_join_type(PBlock::BL_TYPE type) +{ + assert(bl_type_ == BL_PAR); + assert(type == BL_JOIN_NONE || type==BL_JOIN_ANY); + bl_type_ = type; +} + void PBlock::set_statement(const vector&st) { list_ = st; diff --git a/Statement.h b/Statement.h index 049aa118e..48c58a800 100644 --- a/Statement.h +++ b/Statement.h @@ -1,7 +1,7 @@ #ifndef __Statement_H #define __Statement_H /* - * Copyright (c) 1998-2008 Stephen Williams (steve@icarus.com) + * Copyright (c) 1998-2008,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 @@ -165,7 +165,7 @@ class PAssignNB : public PAssign_ { class PBlock : public PScope, public Statement { public: - enum BL_TYPE { BL_SEQ, BL_PAR }; + enum BL_TYPE { BL_SEQ, BL_PAR, BL_JOIN_NONE, BL_JOIN_ANY }; // If the block has a name, it is a scope and also has a parent. explicit PBlock(perm_string n, LexicalScope*parent, BL_TYPE t); @@ -175,6 +175,10 @@ class PBlock : public PScope, public Statement { BL_TYPE bl_type() const { return bl_type_; } + // If the bl_type() is BL_PAR, it is possible to replace it + // with JOIN_NONE or JOIN_ANY. This is to help the parser. + void set_join_type(BL_TYPE); + void set_statement(const std::vector&st); virtual void dump(ostream&out, unsigned ind) const; @@ -183,7 +187,7 @@ class PBlock : public PScope, public Statement { virtual void elaborate_sig(Design*des, NetScope*scope) const; private: - const BL_TYPE bl_type_; + BL_TYPE bl_type_; std::vectorlist_; }; diff --git a/design_dump.cc b/design_dump.cc index ce16ec862..9d5b3b678 100644 --- a/design_dump.cc +++ b/design_dump.cc @@ -40,6 +40,12 @@ static ostream& operator<< (ostream&o, NetBlock::Type t) case NetBlock::PARA: o << "fork"; break; + case NetBlock::PARA_JOIN_NONE: + o << "fork-join_none"; + break; + case NetBlock::PARA_JOIN_ANY: + o << "fork-join_any"; + break; } return o; } diff --git a/elab_scope.cc b/elab_scope.cc index 685dfac2c..796f60633 100644 --- a/elab_scope.cc +++ b/elab_scope.cc @@ -1564,7 +1564,9 @@ void PBlock::elaborate_scope(Design*des, NetScope*scope) const << "Elaborate block scope " << use_name << " within " << scope_path(scope) << endl; - my_scope = new NetScope(scope, use_name, bl_type_==BL_PAR + // The scope type is begin-end or fork-join. The + // sub-types of fork-join are not interesting to the scope. + my_scope = new NetScope(scope, use_name, bl_type_!=BL_SEQ ? NetScope::FORK_JOIN : NetScope::BEGIN_END); my_scope->set_line(get_file(), get_lineno()); diff --git a/elaborate.cc b/elaborate.cc index 25767e5c7..319051b18 100644 --- a/elaborate.cc +++ b/elaborate.cc @@ -2633,9 +2633,21 @@ NetProc* PBlock::elaborate(Design*des, NetScope*scope) const { assert(scope); - NetBlock::Type type = (bl_type_==PBlock::BL_PAR) - ? NetBlock::PARA - : NetBlock::SEQU; + NetBlock::Type type; + switch (bl_type_) { + case PBlock::BL_SEQ: + type = NetBlock::SEQU; + break; + case PBlock::BL_PAR: + type = NetBlock::PARA; + break; + case PBlock::BL_JOIN_NONE: + type = NetBlock::PARA_JOIN_NONE; + break; + case PBlock::BL_JOIN_ANY: + type = NetBlock::PARA_JOIN_ANY; + break; + } NetScope*nscope = 0; if (pscope_name() != 0) { diff --git a/ivl_target.h b/ivl_target.h index fddcf86f5..2933ac0e6 100644 --- a/ivl_target.h +++ b/ivl_target.h @@ -1,7 +1,7 @@ #ifndef __ivl_target_H #define __ivl_target_H /* - * 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 @@ -406,6 +406,8 @@ typedef enum ivl_statement_type_e { IVL_ST_FORCE = 14, IVL_ST_FOREVER = 15, IVL_ST_FORK = 16, + IVL_ST_FORK_JOIN_ANY = 28, + IVL_ST_FORK_JOIN_NONE = 29, IVL_ST_FREE = 26, IVL_ST_RELEASE = 17, IVL_ST_REPEAT = 18, @@ -2069,11 +2071,11 @@ extern unsigned ivl_stmt_lineno(ivl_statement_t net); * triggers. The statement waits even if the sub-statement is nul. */ - /* IVL_ST_BLOCK, IVL_ST_FORK */ + /* IVL_ST_BLOCK, IVL_ST_FORK, IVL_ST_FORK_JOIN_ANY, IVL_ST_FORK_JOIN_NONE */ extern unsigned ivl_stmt_block_count(ivl_statement_t net); - /* IVL_ST_BLOCK, IVL_ST_FORK */ + /* IVL_ST_BLOCK, IVL_ST_FORK, IVL_ST_FORK_JOIN_ANY, IVL_ST_FORK_JOIN_NONE */ extern ivl_scope_t ivl_stmt_block_scope(ivl_statement_t net); - /* IVL_ST_BLOCK, IVL_ST_FORK */ + /* IVL_ST_BLOCK, IVL_ST_FORK, IVL_ST_FORK_JOIN_ANY, IVL_ST_FORK_JOIN_NONE */ extern ivl_statement_t ivl_stmt_block_stmt(ivl_statement_t net, unsigned i); /* IVL_ST_UTASK IVL_ST_DISABLE */ extern ivl_scope_t ivl_stmt_call(ivl_statement_t net); diff --git a/net_nex_input.cc b/net_nex_input.cc index 5642e6164..784ff9d01 100644 --- a/net_nex_input.cc +++ b/net_nex_input.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2011 Stephen Williams (steve@icarus.com) + * Copyright (c) 2002-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 @@ -257,7 +257,7 @@ NexusSet* NetBlock::nex_input(bool rem_out) if (last_ == 0) return new NexusSet; - if (type_ == PARA) { + if (type_ != SEQU) { cerr << get_fileline() << ": internal error: Sorry, " << "I don't know how to synthesize fork/join blocks." << endl; diff --git a/netlist.h b/netlist.h index 284d28896..3dc0a628c 100644 --- a/netlist.h +++ b/netlist.h @@ -2491,7 +2491,7 @@ class NetAssignNB : public NetAssignBase { class NetBlock : public NetProc { public: - enum Type { SEQU, PARA }; + enum Type { SEQU, PARA, PARA_JOIN_ANY, PARA_JOIN_NONE }; NetBlock(Type t, NetScope*subscope); ~NetBlock(); diff --git a/parse.y b/parse.y index 219a1960b..27c8d7e5a 100644 --- a/parse.y +++ b/parse.y @@ -356,6 +356,7 @@ static void current_function_set_statement(const YYLTYPE&loc, vector PGBuiltin::Type gatetype; NetNet::PortType porttype; ivl_variable_type_t vartype; + PBlock::BL_TYPE join_keyword; PWire*wire; svector*wires; @@ -574,6 +575,8 @@ static void current_function_set_statement(const YYLTYPE&loc, vector %type analog_statement +%type join_keyword + %type spec_polarity %type specify_path_identifiers @@ -1084,10 +1087,11 @@ integer_vector_type /* IEEE1800-2005: A.2.2.1 */ join_keyword /* IEEE1800-2005: A.6.3 */ : K_join + { $$ = PBlock::BL_PAR; } | K_join_none - { yyerror(@1, "sorry: join_none not supported."); } + { $$ = PBlock::BL_JOIN_NONE; } | K_join_any - { yyerror(@1, "sorry: join_any not supported."); } + { $$ = PBlock::BL_JOIN_ANY; } ; jump_statement /* IEEE1800-2005: A.6.5 */ @@ -5335,12 +5339,12 @@ statement_item /* This is roughly statement_item in the LRM */ code generator can do the right thing. */ | K_fork join_keyword - { PBlock*tmp = new PBlock(PBlock::BL_PAR); + { PBlock*tmp = new PBlock($2); FILE_NAME(tmp, @1); $$ = tmp; } | K_fork statement_or_null_list join_keyword - { PBlock*tmp = new PBlock(PBlock::BL_PAR); + { PBlock*tmp = new PBlock($3); FILE_NAME(tmp, @1); tmp->set_statement(*$2); delete $2; @@ -5357,6 +5361,7 @@ statement_item /* This is roughly statement_item in the LRM */ assert(! current_block_stack.empty()); PBlock*tmp = current_block_stack.top(); current_block_stack.pop(); + tmp->set_join_type($7); if ($6) tmp->set_statement(*$6); delete[]$3; delete $6; diff --git a/t-dll-api.cc b/t-dll-api.cc index 008847127..150258cac 100644 --- a/t-dll-api.cc +++ b/t-dll-api.cc @@ -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 @@ -2271,6 +2271,8 @@ extern "C" ivl_scope_t ivl_stmt_block_scope(ivl_statement_t net) switch (net->type_) { case IVL_ST_BLOCK: case IVL_ST_FORK: + case IVL_ST_FORK_JOIN_ANY: + case IVL_ST_FORK_JOIN_NONE: return net->u_.block_.scope; default: assert(0); @@ -2283,6 +2285,8 @@ extern "C" unsigned ivl_stmt_block_count(ivl_statement_t net) switch (net->type_) { case IVL_ST_BLOCK: case IVL_ST_FORK: + case IVL_ST_FORK_JOIN_ANY: + case IVL_ST_FORK_JOIN_NONE: return net->u_.block_.nstmt_; default: assert(0); @@ -2296,6 +2300,8 @@ extern "C" ivl_statement_t ivl_stmt_block_stmt(ivl_statement_t net, switch (net->type_) { case IVL_ST_BLOCK: case IVL_ST_FORK: + case IVL_ST_FORK_JOIN_ANY: + case IVL_ST_FORK_JOIN_NONE: return net->u_.block_.stmt_ + i; default: assert(0); diff --git a/t-dll-proc.cc b/t-dll-proc.cc index fbc55464e..8874dbb5d 100644 --- a/t-dll-proc.cc +++ b/t-dll-proc.cc @@ -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 @@ -386,9 +386,20 @@ bool dll_target::proc_block(const NetBlock*net) it, so fill in the block fields of the existing statement, and generate the contents for the statement array. */ - stmt_cur_->type_ = (net->type() == NetBlock::SEQU) - ? IVL_ST_BLOCK - : IVL_ST_FORK; + switch (net->type()) { + case NetBlock::SEQU: + stmt_cur_->type_ = IVL_ST_BLOCK; + break; + case NetBlock::PARA: + stmt_cur_->type_ = IVL_ST_FORK; + break; + case NetBlock::PARA_JOIN_ANY: + stmt_cur_->type_ = IVL_ST_FORK_JOIN_ANY; + break; + case NetBlock::PARA_JOIN_NONE: + stmt_cur_->type_ = IVL_ST_FORK_JOIN_NONE; + break; + } stmt_cur_->u_.block_.nstmt_ = count; stmt_cur_->u_.block_.stmt_ = (struct ivl_statement_s*) calloc(count, sizeof(struct ivl_statement_s)); diff --git a/tgt-stub/statement.c b/tgt-stub/statement.c index 401b049e7..1b89e43ec 100644 --- a/tgt-stub/statement.c +++ b/tgt-stub/statement.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2007 Stephen Williams (steve@icarus.com) + * Copyright (c) 2004-2007,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 @@ -329,6 +329,28 @@ void show_statement(ivl_statement_t net, unsigned ind) break; } + case IVL_ST_FORK_JOIN_ANY: { + unsigned cnt = ivl_stmt_block_count(net); + fprintf(out, "%*sfork\n", ind, ""); + for (idx = 0 ; idx < cnt ; idx += 1) { + ivl_statement_t cur = ivl_stmt_block_stmt(net, idx); + show_statement(cur, ind+4); + } + fprintf(out, "%*sjoin_any\n", ind, ""); + break; + } + + case IVL_ST_FORK_JOIN_NONE: { + unsigned cnt = ivl_stmt_block_count(net); + fprintf(out, "%*sfork\n", ind, ""); + for (idx = 0 ; idx < cnt ; idx += 1) { + ivl_statement_t cur = ivl_stmt_block_stmt(net, idx); + show_statement(cur, ind+4); + } + fprintf(out, "%*sjoin_none\n", ind, ""); + break; + } + case IVL_ST_FREE: fprintf(out, "%*sfree automatic storage ...\n", ind, ""); break; From 47ddf5220c07567d7e536a84040d778a709829a0 Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Sun, 20 May 2012 11:39:05 -0700 Subject: [PATCH 041/179] Fix assertion settin join type is PBlock statements. --- Statement.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Statement.cc b/Statement.cc index 64df85ebd..4fcedc777 100644 --- a/Statement.cc +++ b/Statement.cc @@ -117,7 +117,7 @@ PBlock::~PBlock() void PBlock::set_join_type(PBlock::BL_TYPE type) { assert(bl_type_ == BL_PAR); - assert(type == BL_JOIN_NONE || type==BL_JOIN_ANY); + assert(type==BL_PAR || type==BL_JOIN_NONE || type==BL_JOIN_ANY); bl_type_ = type; } From 3b7619b46cbcfd309f33c4b9f59f282768786f44 Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Sun, 20 May 2012 11:39:52 -0700 Subject: [PATCH 042/179] Implement fork-join_none in vvp. --- tgt-vvp/vvp_process.c | 58 +++++++++++++++++++++++++++++++++---------- vvp/codes.h | 1 + vvp/compile.cc | 1 + vvp/vthread.cc | 38 ++++++++++++++++++++++++++++ 4 files changed, 85 insertions(+), 13 deletions(-) diff --git a/tgt-vvp/vvp_process.c b/tgt-vvp/vvp_process.c index 7a0e9c1e7..c30fab792 100644 --- a/tgt-vvp/vvp_process.c +++ b/tgt-vvp/vvp_process.c @@ -1314,55 +1314,85 @@ static int show_stmt_fork(ivl_statement_t net, ivl_scope_t sscope) { unsigned idx; int rc = 0; - unsigned cnt = ivl_stmt_block_count(net); + unsigned join_count = ivl_stmt_block_count(net); + unsigned join_detach_count = 0; ivl_scope_t scope = ivl_stmt_block_scope(net); - unsigned is_named = (scope != 0); + int is_named = (scope != 0); + /* This is TRUE if it is allowed to embed one of the threads + into this thread. */ + int is_embeddable = 1; unsigned out = transient_id++; unsigned id_base = transient_id; + /* Children are certainly not embeddable if they are going + into a new scope. */ + if (is_named) + is_embeddable = 0; + + switch (ivl_statement_type(net)) { + case IVL_ST_FORK: + break; + case IVL_ST_FORK_JOIN_ANY: + if (join_count < 2) + break; + is_embeddable = 0; + join_detach_count = join_count - 1; + join_count = 1; + break; + case IVL_ST_FORK_JOIN_NONE: + is_embeddable = 0; + join_detach_count = join_count; + join_count = 0; + break; + default: + assert(0); + } + /* cnt is the number of sub-threads. If the fork-join has no name, then we can put one of the sub-threads in the current thread, so decrement the count by one and use the current scope for all the threads. */ - if (! is_named) { - cnt -= 1; + if (is_embeddable) + join_count -= 1; + if (scope==0) scope = sscope; - } - transient_id += cnt; + transient_id += join_count; /* Draw a fork statement for all but one of the threads of the fork/join. Send the threads off to a bit of code where they are implemented. */ - for (idx = 0 ; idx < cnt ; idx += 1) { + for (idx = 0 ; idx < (join_count+join_detach_count) ; idx += 1) { fprintf(vvp_out, " %%fork t_%u, S_%p;\n", id_base+idx, scope); } /* If we are putting one sub-thread into the current thread, then draw its code here. */ - if (! is_named) - rc += show_statement(ivl_stmt_block_stmt(net, cnt), scope); + if (is_embeddable) + rc += show_statement(ivl_stmt_block_stmt(net, join_count), scope); /* Generate enough joins to collect all the sub-threads. */ - for (idx = 0 ; idx < cnt ; idx += 1) { + for (idx = 0 ; idx < join_count ; idx += 1) fprintf(vvp_out, " %%join;\n"); - } + if (join_detach_count > 0) + fprintf(vvp_out, " %%join/detach %u;\n", join_detach_count); + /* Jump around all the threads that I'm creating. */ fprintf(vvp_out, " %%jmp t_%u;\n", out); /* Change the compiling scope to be the named forks scope. */ if (is_named) fprintf(vvp_out, " .scope S_%p;\n", scope); /* Generate the sub-threads themselves. */ - for (idx = 0 ; idx < cnt ; idx += 1) { + for (idx = 0 ; idx < (join_count + join_detach_count) ; idx += 1) { fprintf(vvp_out, "t_%u ;\n", id_base+idx); clear_expression_lookaside(); rc += show_statement(ivl_stmt_block_stmt(net, idx), scope); fprintf(vvp_out, " %%end;\n"); } /* Return to the previous scope. */ - if (is_named) fprintf(vvp_out, " .scope S_%p;\n", sscope); + if (sscope) fprintf(vvp_out, " .scope S_%p;\n", sscope); /* This is the label for the out. Use this to branch around the implementations of all the child threads. */ @@ -2073,6 +2103,8 @@ static int show_statement(ivl_statement_t net, ivl_scope_t sscope) break; case IVL_ST_FORK: + case IVL_ST_FORK_JOIN_ANY: + case IVL_ST_FORK_JOIN_NONE: rc += show_stmt_fork(net, sscope); break; diff --git a/vvp/codes.h b/vvp/codes.h index e7632d5b2..77a69a70a 100644 --- a/vvp/codes.h +++ b/vvp/codes.h @@ -115,6 +115,7 @@ extern bool of_JMP0(vthread_t thr, vvp_code_t code); extern bool of_JMP0XZ(vthread_t thr, vvp_code_t code); extern bool of_JMP1(vthread_t thr, vvp_code_t code); extern bool of_JOIN(vthread_t thr, vvp_code_t code); +extern bool of_JOIN_DETACH(vthread_t thr, vvp_code_t code); extern bool of_LOAD_AR(vthread_t thr, vvp_code_t code); extern bool of_LOAD_AV(vthread_t thr, vvp_code_t code); extern bool of_LOAD_AVP0(vthread_t thr, vvp_code_t code); diff --git a/vvp/compile.cc b/vvp/compile.cc index 955dc5425..9edb2e97f 100644 --- a/vvp/compile.cc +++ b/vvp/compile.cc @@ -157,6 +157,7 @@ static const struct opcode_table_s opcode_table[] = { { "%jmp/0xz",of_JMP0XZ, 2, {OA_CODE_PTR, OA_BIT1, OA_NONE} }, { "%jmp/1", of_JMP1, 2, {OA_CODE_PTR, OA_BIT1, OA_NONE} }, { "%join", of_JOIN, 0, {OA_NONE, OA_NONE, OA_NONE} }, + { "%join/detach",of_JOIN_DETACH,1,{OA_NUMBER,OA_NONE, OA_NONE} }, { "%load/ar",of_LOAD_AR,3, {OA_BIT1, OA_ARR_PTR, OA_BIT2} }, { "%load/av",of_LOAD_AV,3, {OA_BIT1, OA_ARR_PTR, OA_BIT2} }, { "%load/avp0",of_LOAD_AVP0,3, {OA_BIT1, OA_ARR_PTR, OA_BIT2} }, diff --git a/vvp/vthread.cc b/vvp/vthread.cc index 86e1d44ff..563f5e465 100644 --- a/vvp/vthread.cc +++ b/vvp/vthread.cc @@ -2892,6 +2892,44 @@ bool of_JOIN(vthread_t thr, vvp_code_t) return false; } +/* + * This %join/detach instruction causes the thread to detach + * threads that were created by an earlier %fork. + */ +bool of_JOIN_DETACH(vthread_t thr, vvp_code_t cp) +{ + unsigned long count = cp->number; + + while (count > 0) { + assert(thr->child); + assert(thr->child->parent == thr); + assert(thr->fork_count > 0); + assert(thr->child->wt_context == 0); + + thr->fork_count -= 1; + + if (thr->child->i_have_ended) { + /* If the child has already ended, then reap it + instead of pulling it from the list. */ + vthread_reap(thr->child); + } else { + /* Pull the child from the parent->child list. */ + struct vthread_s*tmp = thr->child; + assert(tmp->fork_count == 0); + thr->child = tmp->child; + if (thr->child) + thr->child->parent = thr; + /* set up detach. */ + tmp->parent = 0; + tmp->child = 0; + } + + count -= 1; + } + + return true; +} + /* * %load/ar , , ; */ From 25f72e31d4cb4ac2c8278f8fb41785b900142fb6 Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Sun, 27 May 2012 18:03:55 -0700 Subject: [PATCH 043/179] Re-implement fork/join in vvp The fork/join list did not adequately support the tree of processes that can happen in Verilog, so this patch reworks that support to make it all more natural. --- vvp/opcodes.txt | 8 ++ vvp/vthread.cc | 241 ++++++++++++++++++++++++++---------------------- 2 files changed, 138 insertions(+), 111 deletions(-) diff --git a/vvp/opcodes.txt b/vvp/opcodes.txt index b8ce9332e..369aa04b2 100644 --- a/vvp/opcodes.txt +++ b/vvp/opcodes.txt @@ -526,6 +526,14 @@ If the matching child instruction is still running, a %join suspends the calling thread until the child ends. If the child is already ended, then the %join does not block or yield the thread. +* %join/detach + +This is also a partner to the %ork. This instruction causes the thread +to detach threads from the current thread. The should be ALL +the children, and none of those children may be automatic. This +instruction is used to implement join_none and join_any from the +Verilog source. + * %load/av , , This instruction loads a word from the specified array. The word diff --git a/vvp/vthread.cc b/vvp/vthread.cc index 563f5e465..ccef7946b 100644 --- a/vvp/vthread.cc +++ b/vvp/vthread.cc @@ -28,7 +28,9 @@ #ifdef CHECK_WITH_VALGRIND # include "vvp_cleanup.h" #endif +# include # include +# include # include # include # include @@ -38,6 +40,8 @@ # include # include +using namespace std; + /* This is the size of an unsigned long in bits. This is just a convenience macro. */ # define CPU_WORD_BITS (8*sizeof(unsigned long)) @@ -51,35 +55,26 @@ * * ** Notes On The Interactions of %fork/%join/%end: * - * The %fork instruction creates a new thread and pushes that onto the - * stack of children for the thread. This new thread, then, becomes - * the new direct descendant of the thread. This new thread is - * therefore also the first thread to be reaped when the parent does a - * %join. + * The %fork instruction creates a new thread and pushes that into a + * set of children for the thread. This new thread, then, becomes a + * child of the current thread, and the current thread a parent of the + * new thread. Any child can be reaped by a %join. + * + * Children placed into an automatic scope are given special + * treatment, which is required to make function/tasks calls that they + * represent work correctly. These automatic children are copied into + * an automatic_children set to mark them for this handling. %join + * operations will guarantee that automatic threads are joined first, + * before any non-automatic threads. * * It is a programming error for a thread that created threads to not - * %join as many as it created before it %ends. The linear stack for - * tracking thread relationships will create a mess otherwise. For - * example, if A creates B then C, the stack is: + * %join (or %join/detach) as many as it created before it %ends. The + * children set will get messed up otherwise. * - * A --> C --> B - * - * If C then %forks X, the stack is: - * - * A --> C --> X --> B - * - * If C %ends without a join, then the stack is: - * - * A --> C(zombie) --> X --> B - * - * If A then executes 2 %joins, it will reap C and X (when it ends) - * leaving B in purgatory. What's worse, A will block on the schedules - * of X and C instead of C and B, possibly creating incorrect timing. - * - * The schedule_parent_on_end flag is used by threads to tell their - * children that they are waiting for it to end. It is set by a %join - * instruction if the child is not already done. The thread that - * executes a %join instruction sets the flag in its child. + * the i_am_joining flag is a clue to children that the parent is + * blocked in a %join and may need to be scheduled. The %end + * instruction will check this flag in the parent to see if it should + * notify the parent that something is interesting. * * The i_have_ended flag, on the other hand, is used by threads to * tell their parents that they are already dead. A thread that @@ -105,14 +100,15 @@ struct vthread_s { } words[16]; /* My parent sets this when it wants me to wake it up. */ - unsigned schedule_parent_on_end :1; + unsigned i_am_joining :1; unsigned i_have_ended :1; unsigned waiting_for_event :1; unsigned is_scheduled :1; unsigned delay_delete :1; - unsigned fork_count :8; - /* This points to the sole child of the thread. */ - struct vthread_s*child; + /* This points to the children of the thread. */ + setchildren; + /* No more then 1 of the children is automatic. */ + setautomatic_children; /* This points to my parent, if I have one. */ struct vthread_s*parent; /* This points to the containing scope. */ @@ -126,6 +122,9 @@ struct vthread_s { uint64_t ecount; }; +static bool test_joinable(vthread_t thr, vthread_t child); +static void do_join(vthread_t thr, vthread_t child); + struct __vpiScope* vthread_scope(struct vthread_s*thr) { return thr->parent_scope; @@ -400,19 +399,17 @@ vthread_t vthread_new(vvp_code_t pc, struct __vpiScope*scope) vthread_t thr = new struct vthread_s; thr->pc = pc; thr->bits4 = vvp_vector4_t(32); - thr->child = 0; thr->parent = 0; thr->parent_scope = scope; thr->wait_next = 0; thr->wt_context = 0; thr->rd_context = 0; - thr->schedule_parent_on_end = 0; + thr->i_am_joining = 0; thr->is_scheduled = 0; thr->i_have_ended = 0; thr->delay_delete = 0; thr->waiting_for_event = 0; - thr->fork_count = 0; thr->event = 0; thr->ecount = 0; @@ -469,16 +466,19 @@ void vthreads_delete(struct __vpiScope*scope) */ static void vthread_reap(vthread_t thr) { - if (thr->child) { - assert(thr->child->parent == thr); - thr->child->parent = thr->parent; + if (thr->children.size() > 0) { + for (set::iterator cur = thr->children.begin() + ; cur != thr->children.end() ; ++cur) { + vthread_t curp = *cur; + assert(curp->parent == thr); + curp->parent = thr->parent; + } } if (thr->parent) { - assert(thr->parent->child == thr); - thr->parent->child = thr->child; + //assert(thr->parent->child == thr); + thr->parent->children.erase(thr); } - thr->child = 0; thr->parent = 0; // Remove myself from the containing scope. @@ -490,7 +490,7 @@ static void vthread_reap(vthread_t thr) it now. Otherwise, let the schedule event (which will execute the thread at of_ZOMBIE) delete the object. */ if ((thr->is_scheduled == 0) && (thr->waiting_for_event == 0)) { - assert(thr->fork_count == 0); + assert(thr->children.size() == 0); assert(thr->wait_next == 0); if (thr->delay_delete) schedule_del_thr(thr); @@ -1956,28 +1956,30 @@ static bool do_disable(vthread_t thr, vthread_t match) /* Turn off all the children of the thread. Simulate a %join for as many times as needed to clear the results of all the %forks that this thread has done. */ - while (thr->fork_count > 0) { + while (!thr->children.empty()) { - vthread_t tmp = thr->child; + vthread_t tmp = *(thr->children.begin()); assert(tmp); assert(tmp->parent == thr); - tmp->schedule_parent_on_end = 0; + thr->i_am_joining = 0; if (do_disable(tmp, match)) flag = true; - thr->fork_count -= 1; - vthread_reap(tmp); } + if (thr->parent && thr->parent->i_am_joining) { + // If a parent is waiting in a %join, wake it up. Note + // that it is possible to be waiting in a %join yet + // already scheduled if multiple child threads are + // ending. So check if the thread is already scheduled + // before scheduling it again. + vthread_t parent = thr->parent; + parent->i_am_joining = 0; + if (! parent->i_have_ended) + schedule_vthread(parent, 0, true); - if (thr->schedule_parent_on_end) { - /* If a parent is waiting in a %join, wake it up. */ - assert(thr->parent); - assert(thr->parent->fork_count > 0); - - thr->parent->fork_count -= 1; - schedule_vthread(thr->parent, 0, true); + // Let the parent do the reaping. vthread_reap(thr); } else if (thr->parent) { @@ -2007,7 +2009,7 @@ bool of_DISABLE(vthread_t thr, vvp_code_t cp) while (! scope->threads.empty()) { set::iterator cur = scope->threads.begin(); - /* If I am disabling myself, that remember that fact so + /* If I am disabling myself, then remember that fact so that I can finish this statement differently. */ if (*cur == thr) disabled_myself_flag = true; @@ -2350,20 +2352,25 @@ bool of_DIV_WR(vthread_t thr, vvp_code_t cp) bool of_END(vthread_t thr, vvp_code_t) { assert(! thr->waiting_for_event); - assert( thr->fork_count == 0 ); thr->i_have_ended = 1; thr->pc = codespace_null(); /* If I have a parent who is waiting for me, then mark that I have ended, and schedule that parent. Also, finish the %join for the parent. */ - if (thr->schedule_parent_on_end) { - assert(thr->parent); - assert(thr->parent->fork_count > 0); + if (thr->parent && thr->parent->i_am_joining) { + vthread_t tmp = thr->parent; - thr->parent->fork_count -= 1; - schedule_vthread(thr->parent, 0, true); - vthread_reap(thr); + // Detect that the parent is waiting on an automatic + // thread. Automatic threads must be reaped first. If + // the parent is waiting on an auto (other then me) then + // go into zomple state to be picked up later. + if (!test_joinable(tmp, thr)) + return false; + + tmp->i_am_joining = 0; + schedule_vthread(tmp, 0, true); + do_join(tmp, thr); return false; } @@ -2375,7 +2382,7 @@ bool of_END(vthread_t thr, vvp_code_t) main thread (there is no other parent) and an error (not enough %joins) has been detected. */ if (thr->parent == 0) { - assert(thr->child == 0); + assert(thr->children.empty()); vthread_reap(thr); return false; } @@ -2523,29 +2530,25 @@ bool of_FORCE_X0(vthread_t thr, vvp_code_t cp) /* * The %fork instruction causes a new child to be created and pushed - * in front of any existing child. This causes the new child to be the - * parent of any previous children, and for me to be the parent of the + * in front of any existing child. This causes the new child to be + * added to the list of children, and for me to be the parent of the * new child. */ bool of_FORK(vthread_t thr, vvp_code_t cp) { vthread_t child = vthread_new(cp->cptr2, cp->scope); + if (cp->scope->is_automatic) { /* The context allocated for this child is the top entry on the write context stack. */ child->wt_context = thr->wt_context; child->rd_context = thr->wt_context; + + thr->automatic_children.insert(child); } - child->child = thr->child; child->parent = thr; - thr->child = child; - if (child->child) { - assert(child->child->parent == thr); - child->child->parent = child; - } - - thr->fork_count += 1; + thr->children.insert(child); /* If the new child was created to evaluate a function, run it immediately, then return to this thread. */ @@ -2854,20 +2857,27 @@ bool of_JMP1(vthread_t thr, vvp_code_t cp) } /* - * The %join instruction causes the thread to wait for the one and - * only child to die. If it is already dead (and a zombie) then I - * reap it and go on. Otherwise, I tell the child that I am ready for - * it to die, and it will reschedule me when it does. + * The %join instruction causes the thread to wait for one child + * to die. If a child is already dead (and a zombie) then I reap + * it and go on. Otherwise, I mark myself as waiting in a join so that + * children know to wake me when they finish. */ -bool of_JOIN(vthread_t thr, vvp_code_t) + +static bool test_joinable(vthread_t thr, vthread_t child) { - assert(thr->child); - assert(thr->child->parent == thr); + set::iterator auto_cur = thr->automatic_children.find(child); + if (!thr->automatic_children.empty() && auto_cur == thr->automatic_children.end()) + return false; - assert(thr->fork_count > 0); + return true; +} - /* If the child thread is in an automatic scope... */ - if (thr->child->wt_context) { +static void do_join(vthread_t thr, vthread_t child) +{ + assert(child->parent == thr); + + /* If the immediate child thread is in an automatic scope... */ + if (thr->automatic_children.erase(child) != 0) { /* and is the top level task/function thread... */ if (thr->wt_context != thr->rd_context) { /* Pop the child context from the write context stack. */ @@ -2880,15 +2890,33 @@ bool of_JOIN(vthread_t thr, vvp_code_t) } } - /* If the child has already ended, reap it now. */ - if (thr->child->i_have_ended) { - thr->fork_count -= 1; - vthread_reap(thr->child); + vthread_reap(child); +} + +bool of_JOIN(vthread_t thr, vvp_code_t) +{ + assert( !thr->i_am_joining ); + assert( !thr->children.empty()); + + // Are there any children that have already ended? If so, then + // join with that one. + for (set::iterator cur = thr->children.begin() + ; cur != thr->children.end() ; ++cur) { + vthread_t curp = *cur; + if (!curp->i_have_ended) + continue; + + if (!test_joinable(thr, curp)) + continue; + + // found somenting! + do_join(thr, curp); return true; } - /* Otherwise, I get to start waiting. */ - thr->child->schedule_parent_on_end = 1; + // Otherwise, tell my children to awaken me when they end, + // then pause. + thr->i_am_joining = 1; return false; } @@ -2900,31 +2928,23 @@ bool of_JOIN_DETACH(vthread_t thr, vvp_code_t cp) { unsigned long count = cp->number; - while (count > 0) { - assert(thr->child); - assert(thr->child->parent == thr); - assert(thr->fork_count > 0); - assert(thr->child->wt_context == 0); + assert(thr->automatic_children.empty()); + assert(count == thr->children.size()); - thr->fork_count -= 1; + while (!thr->children.empty()) { + vthread_t child = *thr->children.begin(); + assert(child->parent == thr); + + // We cannot detach automatic tasks/functions + assert(child->wt_context == 0); + if (child->i_have_ended) { + // If the child has already ended, then reap it. + vthread_reap(child); - if (thr->child->i_have_ended) { - /* If the child has already ended, then reap it - instead of pulling it from the list. */ - vthread_reap(thr->child); } else { - /* Pull the child from the parent->child list. */ - struct vthread_s*tmp = thr->child; - assert(tmp->fork_count == 0); - thr->child = tmp->child; - if (thr->child) - thr->child->parent = thr; - /* set up detach. */ - tmp->parent = 0; - tmp->child = 0; + thr->children.erase(child); + child->parent = 0; } - - count -= 1; } return true; @@ -4646,7 +4666,7 @@ bool of_XOR(vthread_t thr, vvp_code_t cp) bool of_ZOMBIE(vthread_t thr, vvp_code_t) { thr->pc = codespace_null(); - if ((thr->parent == 0) && (thr->child == 0)) { + if ((thr->parent == 0) && (thr->children.empty())) { if (thr->delay_delete) schedule_del_thr(thr); else @@ -4667,8 +4687,7 @@ bool of_EXEC_UFUNC(vthread_t thr, vvp_code_t cp) struct __vpiScope*child_scope = cp->ufunc_core_ptr->func_scope(); assert(child_scope); - assert(thr->child == 0); - assert(thr->fork_count == 0); + assert(thr->children.empty()); /* We can take a number of shortcuts because we know that a continuous assignment can only occur in a static scope. */ From d10e4bca4cf076adb9da5eb21538d8251cf9f594 Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Mon, 28 May 2012 16:49:41 -0700 Subject: [PATCH 044/179] Remove some uses of the svector class. --- design_dump.cc | 4 ++-- dup_expr.cc | 4 ++-- elab_expr.cc | 4 ++-- elab_sig.cc | 2 +- expr_synth.cc | 6 +++--- net_nex_input.cc | 2 +- netlist.cc | 14 +++++++------- netlist.h | 8 ++++---- 8 files changed, 22 insertions(+), 22 deletions(-) diff --git a/design_dump.cc b/design_dump.cc index 1518f1f21..ef5c4f75f 100644 --- a/design_dump.cc +++ b/design_dump.cc @@ -1518,9 +1518,9 @@ void NetETernary::dump(ostream&o) const void NetEUFunc::dump(ostream&o) const { o << func_->basename() << "("; - if (parms_.count() > 0) { + if (parms_.size() > 0) { parms_[0]->dump(o); - for (unsigned idx = 1 ; idx < parms_.count() ; idx += 1) { + for (unsigned idx = 1 ; idx < parms_.size() ; idx += 1) { o << ", "; parms_[idx]->dump(o); } diff --git a/dup_expr.cc b/dup_expr.cc index cccdff8c6..c1258eba6 100644 --- a/dup_expr.cc +++ b/dup_expr.cc @@ -232,9 +232,9 @@ NetETernary* NetETernary::dup_expr() const NetEUFunc* NetEUFunc::dup_expr() const { NetEUFunc*tmp; - svector tmp_parms (parms_.count()); + vector tmp_parms (parms_.size()); - for (unsigned idx = 0 ; idx < tmp_parms.count() ; idx += 1) { + for (unsigned idx = 0 ; idx < tmp_parms.size() ; idx += 1) { ivl_assert(*this, parms_[idx]); tmp_parms[idx] = parms_[idx]->dup_expr(); } diff --git a/elab_expr.cc b/elab_expr.cc index 5ed9832be..7d10bb0d8 100644 --- a/elab_expr.cc +++ b/elab_expr.cc @@ -1663,7 +1663,7 @@ NetExpr* PECallFunction::elaborate_expr(Design*des, NetScope*scope, if ((parms_count == 1) && (parms_[0] == 0)) parms_count = 0; - svector parms (parms_count); + vector parms (parms_count); /* Elaborate the input expressions for the function. This is done in the scope of the function call, and not the scope @@ -1672,7 +1672,7 @@ NetExpr* PECallFunction::elaborate_expr(Design*des, NetScope*scope, unsigned parm_errors = 0; unsigned missing_parms = 0; - for (unsigned idx = 0 ; idx < parms.count() ; idx += 1) { + for (unsigned idx = 0 ; idx < parms.size() ; idx += 1) { PExpr*tmp = parms_[idx]; if (tmp) { parms[idx] = elaborate_rval_expr(des, scope, diff --git a/elab_sig.cc b/elab_sig.cc index 9f2e6b60e..a795a3a33 100644 --- a/elab_sig.cc +++ b/elab_sig.cc @@ -624,7 +624,7 @@ void PFunction::elaborate_sig(Design*des, NetScope*scope) const } } - svectorports (ports_? ports_->count() : 0); + vectorports (ports_? ports_->count() : 0); if (ports_) for (unsigned idx = 0 ; idx < ports_->count() ; idx += 1) { diff --git a/expr_synth.cc b/expr_synth.cc index 8a7138a39..e30940025 100644 --- a/expr_synth.cc +++ b/expr_synth.cc @@ -1386,11 +1386,11 @@ NetNet* NetESFunc::synthesize(Design*des, NetScope*scope, NetExpr*root) NetNet* NetEUFunc::synthesize(Design*des, NetScope*scope, NetExpr*root) { - svector eparms (parms_.count()); + vector eparms (parms_.size()); /* Synthesize the arguments. */ bool errors = false; - for (unsigned idx = 0; idx < eparms.count(); idx += 1) { + for (unsigned idx = 0; idx < eparms.size(); idx += 1) { if (dynamic_cast (parms_[idx])) { errors = true; continue; @@ -1432,7 +1432,7 @@ NetNet* NetEUFunc::synthesize(Design*des, NetScope*scope, NetExpr*root) /* Connect the pins to the arguments. */ NetFuncDef*def = func_->func_def(); - for (unsigned idx = 0; idx < eparms.count(); idx += 1) { + for (unsigned idx = 0; idx < eparms.size(); idx += 1) { unsigned width = def->port(idx)->vector_width(); NetNet*tmp; if (eparms[idx]->get_signed()) { diff --git a/net_nex_input.cc b/net_nex_input.cc index 5642e6164..2d78542a9 100644 --- a/net_nex_input.cc +++ b/net_nex_input.cc @@ -188,7 +188,7 @@ NexusSet* NetETernary::nex_input(bool rem_out) NexusSet* NetEUFunc::nex_input(bool rem_out) { NexusSet*result = new NexusSet; - for (unsigned idx = 0 ; idx < parms_.count() ; idx += 1) { + for (unsigned idx = 0 ; idx < parms_.size() ; idx += 1) { NexusSet*tmp = parms_[idx]->nex_input(rem_out); result->add(*tmp); delete tmp; diff --git a/netlist.cc b/netlist.cc index 1677b16da..b3bb702e2 100644 --- a/netlist.cc +++ b/netlist.cc @@ -1979,7 +1979,7 @@ bool NetConst::is_string() const return is_string_; } -NetFuncDef::NetFuncDef(NetScope*s, NetNet*result, const svector&po) +NetFuncDef::NetFuncDef(NetScope*s, NetNet*result, const vector&po) : scope_(s), statement_(0), result_sig_(result), ports_(po) { } @@ -2012,12 +2012,12 @@ NetScope*NetFuncDef::scope() unsigned NetFuncDef::port_count() const { - return ports_.count(); + return ports_.size(); } const NetNet* NetFuncDef::port(unsigned idx) const { - assert(idx < ports_.count()); + assert(idx < ports_.size()); return ports_[idx]; } @@ -2063,7 +2063,7 @@ const NetExpr* NetSTask::parm(unsigned idx) const } NetEUFunc::NetEUFunc(NetScope*scope, NetScope*def, NetESignal*res, - svector&p, bool nc) + vector&p, bool nc) : scope_(scope), func_(def), result_sig_(res), parms_(p), need_const_(nc) { expr_width(result_sig_->expr_width()); @@ -2071,7 +2071,7 @@ NetEUFunc::NetEUFunc(NetScope*scope, NetScope*def, NetESignal*res, NetEUFunc::~NetEUFunc() { - for (unsigned idx = 0 ; idx < parms_.count() ; idx += 1) + for (unsigned idx = 0 ; idx < parms_.size() ; idx += 1) delete parms_[idx]; } #if 0 @@ -2087,12 +2087,12 @@ const NetESignal*NetEUFunc::result_sig() const unsigned NetEUFunc::parm_count() const { - return parms_.count(); + return parms_.size(); } const NetExpr* NetEUFunc::parm(unsigned idx) const { - assert(idx < parms_.count()); + assert(idx < parms_.size()); return parms_[idx]; } diff --git a/netlist.h b/netlist.h index 06684d804..e1262a89a 100644 --- a/netlist.h +++ b/netlist.h @@ -2981,7 +2981,7 @@ class NetFree : public NetProc { class NetFuncDef { public: - NetFuncDef(NetScope*, NetNet*result, const svector&po); + NetFuncDef(NetScope*, NetNet*result, const std::vector&po); ~NetFuncDef(); void set_proc(NetProc*st); @@ -3002,7 +3002,7 @@ class NetFuncDef { NetScope*scope_; NetProc*statement_; NetNet*result_sig_; - svectorports_; + std::vectorports_; }; /* @@ -3164,7 +3164,7 @@ class NetTaskDef { class NetEUFunc : public NetExpr { public: - NetEUFunc(NetScope*, NetScope*, NetESignal*, svector&, bool); + NetEUFunc(NetScope*, NetScope*, NetESignal*, std::vector&, bool); ~NetEUFunc(); const NetESignal*result_sig() const; @@ -3187,7 +3187,7 @@ class NetEUFunc : public NetExpr { NetScope*scope_; NetScope*func_; NetESignal*result_sig_; - svector parms_; + std::vector parms_; bool need_const_; private: // not implemented From def9d0ea1d4d4d8441cce61bad22765806c837d1 Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Tue, 29 May 2012 10:02:10 -0700 Subject: [PATCH 045/179] Basic infrastructure for compile-time function evaluation. --- Makefile.in | 3 +- eval_tree.cc | 8 ++- net_func_eval.cc | 133 +++++++++++++++++++++++++++++++++++++++++++++++ netlist.h | 30 +++++++++++ 4 files changed, 171 insertions(+), 3 deletions(-) create mode 100644 net_func_eval.cc diff --git a/Makefile.in b/Makefile.in index 57500f88e..bcebfa407 100644 --- a/Makefile.in +++ b/Makefile.in @@ -108,7 +108,8 @@ O = main.o async.o design_dump.o discipline.o dup_expr.o elaborate.o \ eval_tree.o expr_synth.o functor.o lexor.o lexor_keyword.o link_const.o \ load_module.o netlist.o netmisc.o nettypes.o net_analog.o net_assign.o \ net_design.o \ - netenum.o netstruct.o net_event.o net_expr.o net_func.o net_link.o net_modulo.o \ + netenum.o netstruct.o net_event.o net_expr.o net_func.o net_func_eval.o \ + net_link.o net_modulo.o \ net_nex_input.o net_nex_output.o net_proc.o net_scope.o net_tran.o \ net_udp.o pad_to_width.o parse.o parse_misc.o pform.o pform_analog.o \ pform_disciplines.o pform_dump.o pform_pclass.o pform_struct_type.o \ diff --git a/eval_tree.cc b/eval_tree.cc index bbce3cbc2..2db0f181c 100644 --- a/eval_tree.cc +++ b/eval_tree.cc @@ -1963,8 +1963,12 @@ NetExpr* NetEUFunc::eval_tree() } if (need_const_) { - cerr << get_fileline() << ": sorry: Constant user functions are " - "not yet supported." << endl; + NetFuncDef*def = func_->func_def(); + ivl_assert(*this, def); + + NetExpr*res = def->evaluate_function(*this, parms_); + return res; } + return 0; } diff --git a/net_func_eval.cc b/net_func_eval.cc new file mode 100644 index 000000000..662eca9bf --- /dev/null +++ b/net_func_eval.cc @@ -0,0 +1,133 @@ +/* + * Copyright (c) 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 + * General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +# include "netlist.h" +# include +# include "ivl_assert.h" + +using namespace std; + +NetExpr* NetFuncDef::evaluate_function(const LineInfo&loc, const std::vector&args) const +{ + // Make the context map; + map::iterator ptr; + mapcontext_map; + + // Put the return value into the map... + context_map[scope_->basename()] = 0; + // Load the input ports into the map... + ivl_assert(loc, ports_.size() == args.size()); + for (size_t idx = 0 ; idx < ports_.size() ; idx += 1) { + NetExpr*tmp = args[idx]->dup_expr(); + perm_string aname = ports_[idx]->name(); + context_map[aname] = tmp; + } + + // Perform the evaluation + bool flag = statement_->evaluate_function(loc, context_map); + + // Extract the result... + ptr = context_map.find(scope_->basename()); + NetExpr*res = ptr->second; + context_map.erase(ptr); + + + // Cleanup the rest of the context. + for (ptr = context_map.begin() ; ptr != context_map.end() ; ++ptr) { + delete ptr->second; + } + + // Done. + if (flag) + return res; + + delete res; + return 0; +} + +NetExpr* NetExpr::evaluate_function(const LineInfo&, + map&) const +{ + cerr << get_fileline() << ": sorry: I don't know how to evaluate this expression at compile time." << endl; + cerr << get_fileline() << ": : Expression type:" << typeid(*this).name() << endl; + + return 0; +} + +bool NetProc::evaluate_function(const LineInfo&, + map&) const +{ + cerr << get_fileline() << ": sorry: I don't know how to evaluate this statement at compile time." << endl; + cerr << get_fileline() << ": : Statement type:" << typeid(*this).name() << endl; + + return false; +} + +bool NetAssign::evaluate_function(const LineInfo&loc, + map&context_map) const +{ + if (l_val_count() != 1) { + cerr << get_fileline() << ": sorry: I don't know how to evaluate " + "concatenated l-values here." << endl; + return false; + } + + const NetAssign_*lval = l_val(0); + + ivl_assert(loc, lval->word() == 0); + ivl_assert(loc, lval->get_base() == 0); + + NetExpr*rval_result = rval()->evaluate_function(loc, context_map); + if (rval_result == 0) + return false; + + map::iterator ptr = context_map.find(lval->name()); + if (ptr->second) + delete ptr->second; + ptr->second = rval_result; + + return true; +} + +/* + * Evaluating a NetBlock in a function is a simple matter of + * evaluating the statements in order. + */ +bool NetBlock::evaluate_function(const LineInfo&loc, + map&context_map) const +{ + bool flag = true; + NetProc*cur = last_; + do { + cur = cur->next_; + bool cur_flag = cur->evaluate_function(loc, context_map); + flag = flag && cur_flag; + } while (cur != last_); + + return flag; +} + + +NetExpr* NetEConst::evaluate_function(const LineInfo&, + map&) const +{ + NetEConst*res = new NetEConst(value_); + res->set_line(*this); + return res; +} diff --git a/netlist.h b/netlist.h index e1262a89a..e5eb200f8 100644 --- a/netlist.h +++ b/netlist.h @@ -28,6 +28,7 @@ # include # include # include +# include # include # include # include @@ -1715,6 +1716,14 @@ class NetExpr : public LineInfo { // any. This is a deep copy operation. virtual NetExpr*dup_expr() const =0; + // Evaluate the expression at compile time, a la within a + // constant function. This is used by the constant function + // evaluation function code, and the return value is an + // allocated constant, or nil if the expression cannot be + // evaluated for any reason. + virtual NetExpr*evaluate_function(const LineInfo&loc, + std::map&ctx) const; + // Get the Nexus that are the input to this // expression. Normally this descends down to the reference to // a signal that reads from its input. @@ -1776,6 +1785,9 @@ class NetEConst : public NetExpr { virtual NetNet*synthesize(Design*, NetScope*scope, NetExpr*); virtual NexusSet* nex_input(bool rem_out = true); + virtual NetExpr*evaluate_function(const LineInfo&loc, + std::map&ctx) const; + private: verinum value_; }; @@ -2244,6 +2256,15 @@ class NetProc : public virtual LineInfo { // target. The target returns true if OK, false for errors. virtual bool emit_proc(struct target_t*) const; + // This method is used by the NetFuncDef object to evaluate a + // constant function at compile time. The loc is the location + // of the function call, and is used for error messages. The + // ctx is a map of name to expression. This is for mapping + // identifiers to values. The function returns true if the + // processing succeeds, or false otherwise. + virtual bool evaluate_function(const LineInfo&loc, + std::map&ctx) const; + // This method is called by functors that want to scan a // process in search of matchable patterns. virtual int match_proc(struct proc_match_t*); @@ -2446,6 +2467,7 @@ class NetAssign : public NetAssignBase { virtual bool emit_proc(struct target_t*) const; virtual int match_proc(struct proc_match_t*); virtual void dump(ostream&, unsigned ind) const; + virtual bool evaluate_function(const LineInfo&loc, std::map&context_map) const; private: char op_; @@ -2495,6 +2517,8 @@ class NetBlock : public NetProc { const NetProc*proc_first() const; const NetProc*proc_next(const NetProc*cur) const; + bool evaluate_function(const LineInfo&loc, + std::map&ctx) const; // synthesize as asynchronous logic, and return true. bool synth_async(Design*des, NetScope*scope, @@ -2996,6 +3020,12 @@ class NetFuncDef { const NetNet*return_sig() const; + // When we want to evaluate the function during compile time, + // use this method to pass in the argument and get out a + // result. The result should be a constant. If the function + // cannot evaluate to a constant, this returns nil. + NetExpr* evaluate_function(const LineInfo&loc, const std::vector&args) const; + void dump(ostream&, unsigned ind) const; private: From a5a7050120d8b21ea2593d82fa2861551bf036da Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Tue, 29 May 2012 13:56:16 -0700 Subject: [PATCH 046/179] More expression types work in constant functions. --- eval_tree.cc | 300 +++++++++++++++++++++++++---------------------- net_func_eval.cc | 133 +++++++++++++++++++++ netlist.h | 37 ++++-- 3 files changed, 322 insertions(+), 148 deletions(-) diff --git a/eval_tree.cc b/eval_tree.cc index 2db0f181c..1a393fabb 100644 --- a/eval_tree.cc +++ b/eval_tree.cc @@ -34,11 +34,11 @@ NetExpr* NetExpr::eval_tree() return 0; } -static bool get_real_arg_(NetExpr*expr, verireal&val) +static bool get_real_arg_(const NetExpr*expr, verireal&val) { switch (expr->expr_type()) { case IVL_VT_REAL: { - NetECReal*c = dynamic_cast (expr); + const NetECReal*c = dynamic_cast (expr); if (c == 0) return false; val = c->value(); break; @@ -46,7 +46,7 @@ static bool get_real_arg_(NetExpr*expr, verireal&val) case IVL_VT_BOOL: case IVL_VT_LOGIC: { - NetEConst*c = dynamic_cast(expr); + const NetEConst*c = dynamic_cast(expr); if (c == 0) return false; verinum tmp = c->value(); val = verireal(tmp.as_double()); @@ -60,7 +60,7 @@ static bool get_real_arg_(NetExpr*expr, verireal&val) return true; } -static bool get_real_arguments(NetExpr*le, NetExpr*re, +static bool get_real_arguments(const NetExpr*le, const NetExpr*re, double&lval, double&rval) { verireal val; @@ -82,15 +82,15 @@ bool NetEBinary::get_real_arguments_(verireal&lval, verireal&rval) return true; } -NetECReal* NetEBAdd::eval_tree_real_() +NetECReal* NetEBAdd::eval_tree_real_(const NetExpr*l, const NetExpr*r) const { - verireal lval; - verireal rval; + double lval; + double rval; - bool flag = get_real_arguments_(lval, rval); + bool flag = get_real_arguments(l, r, lval, rval); if (!flag) return 0; - verireal res_val; + double res_val; switch (op()) { case '+': @@ -103,7 +103,7 @@ NetECReal* NetEBAdd::eval_tree_real_() ivl_assert(*this, 0); } - NetECReal*res = new NetECReal( res_val ); + NetECReal*res = new NetECReal( verireal(res_val) ); ivl_assert(*this, res); res->set_line(*this); @@ -119,52 +119,24 @@ NetExpr* NetEBAdd::eval_tree() eval_expr(left_); eval_expr(right_); - if (expr_type() == IVL_VT_REAL) - return eval_tree_real_(); - - NetEConst*lc = dynamic_cast(left_); - NetEConst*rc = dynamic_cast(right_); - - /* If both operands are constant, then replace the entire - expression with a constant value. */ - if (lc != 0 && rc != 0) { - verinum lval = lc->value(); - verinum rval = rc->value(); - - unsigned wid = expr_width(); - ivl_assert(*this, wid > 0); - ivl_assert(*this, lval.len() == wid); - ivl_assert(*this, rval.len() == wid); - - verinum val; - switch (op_) { - case '+': - val = verinum(lval + rval, wid); - break; - case '-': - val = verinum(lval - rval, wid); - break; - default: - return 0; - } - - NetEConst *res = new NetEConst(val); - ivl_assert(*this, res); - res->set_line(*this); - - if (debug_eval_tree) - cerr << get_fileline() << ": debug: Evaluated: " << *this - << " --> " << *res << endl; - + // First try to elaborate the expression completely. + NetExpr*res = eval_arguments_(left_,right_); + if (res != 0) return res; - } - /* Try to combine a right constant value with the right - constant value of a sub-expression add. For example, the - expression (a + 2) - 1 can be rewritten as a + 1. */ + // If the expression type is real, then do not attempt the + // following alternative processing. + if (expr_type() == IVL_VT_REAL) + return 0; + + // The expression has not evaluated to a constant. Let's still + // try to optimize by trying to combine a right constant value + // with the right constant value of a sub-expression add. For + // example, the expression (a + 2) - 1 can be rewritten as a + 1. NetEBAdd*se = dynamic_cast(left_); - lc = se? dynamic_cast(se->right_) : 0; + NetEConst*lc = se? dynamic_cast(se->right_) : 0; + NetEConst*rc = dynamic_cast(right_); if (lc != 0 && rc != 0) { ivl_assert(*this, se != 0); @@ -200,11 +172,56 @@ NetExpr* NetEBAdd::eval_tree() tmp->set_line(*right_); delete right_; right_ = tmp; - /* We've changed the subexpression, but the result is - still not constant, so return nil here anyhow. */ - return 0; } + // We may have changed the subexpression, but the result is + // still not constant, so return nil here anyhow. + return 0; +} + +NetExpr* NetEBAdd::eval_arguments_(const NetExpr*l, const NetExpr*r) const +{ + if (expr_type() == IVL_VT_REAL) + return eval_tree_real_(l,r); + + const NetEConst*lc = dynamic_cast(l); + const NetEConst*rc = dynamic_cast(r); + + /* If both operands are constant, then replace the entire + expression with a constant value. */ + if (lc != 0 && rc != 0) { + verinum lval = lc->value(); + verinum rval = rc->value(); + + unsigned wid = expr_width(); + ivl_assert(*this, wid > 0); + ivl_assert(*this, lval.len() == wid); + ivl_assert(*this, rval.len() == wid); + + verinum val; + switch (op_) { + case '+': + val = verinum(lval + rval, wid); + break; + case '-': + val = verinum(lval - rval, wid); + break; + default: + return 0; + } + + NetEConst *res = new NetEConst(val); + ivl_assert(*this, res); + res->set_line(*this); + + if (debug_eval_tree) + cerr << get_fileline() << ": debug: Evaluated: " << *this + << " --> " << *res << endl; + + return res; + } + + /* Nothing more to be done, the value is not constant. */ return 0; } @@ -276,13 +293,12 @@ NetEConst* NetEBBits::eval_tree() return new NetEConst(res); } -NetEConst* NetEBComp::eval_less_() +NetEConst* NetEBComp::eval_less_(const NetExpr*le, const NetExpr*re) const { - if (right_->expr_type() == IVL_VT_REAL || - left_->expr_type() == IVL_VT_REAL) - return eval_leeq_real_(left_, right_, false); + if (le->expr_type() == IVL_VT_REAL || re->expr_type() == IVL_VT_REAL) + return eval_leeq_real_(le, re, false); - NetEConst*rc = dynamic_cast(right_); + const NetEConst*rc = dynamic_cast(re); if (rc == 0) return 0; verinum rv = rc->value(); @@ -292,12 +308,12 @@ NetEConst* NetEBComp::eval_less_() return res; } - if (NetEConst*tmp = must_be_leeq_(left_, rv, false)) { + if (NetEConst*tmp = must_be_leeq_(le, rv, false)) { return tmp; } /* Now go on to the normal test of the values. */ - NetEConst*lc = dynamic_cast(left_); + const NetEConst*lc = dynamic_cast(le); if (lc == 0) return 0; verinum lv = lc->value(); @@ -318,7 +334,7 @@ NetEConst* NetEBComp::eval_less_() } } -NetEConst* NetEBComp::must_be_leeq_(NetExpr*le, const verinum&rv, bool eq_flag) +NetEConst* NetEBComp::must_be_leeq_(const NetExpr*le, const verinum&rv, bool eq_flag) const { assert(le->expr_width() > 0); verinum lv (verinum::V1, le->expr_width()); @@ -339,7 +355,7 @@ NetEConst* NetEBComp::must_be_leeq_(NetExpr*le, const verinum&rv, bool eq_flag) return 0; } -NetEConst* NetEBComp::eval_leeq_real_(NetExpr*le, NetExpr*re, bool eq_flag) +NetEConst* NetEBComp::eval_leeq_real_(const NetExpr*le, const NetExpr*re, bool eq_flag) const { double lval; double rval; @@ -358,14 +374,13 @@ NetEConst* NetEBComp::eval_leeq_real_(NetExpr*le, NetExpr*re, bool eq_flag) return res; } -NetEConst* NetEBComp::eval_leeq_() +NetEConst* NetEBComp::eval_leeq_(const NetExpr*le, const NetExpr*re) const { - if (right_->expr_type() == IVL_VT_REAL || - left_->expr_type() == IVL_VT_REAL) - return eval_leeq_real_(left_, right_, true); + if (le->expr_type() == IVL_VT_REAL || re->expr_type() == IVL_VT_REAL) + return eval_leeq_real_(le, re, true); // assert(expr_type() == IVL_VT_LOGIC); - NetEConst*r = dynamic_cast(right_); + const NetEConst*r = dynamic_cast(re); if (r == 0) return 0; verinum rv = r->value(); @@ -375,18 +390,18 @@ NetEConst* NetEBComp::eval_leeq_() return res; } - if (left_->expr_width() == 0) { + if (le->expr_width() == 0) { cerr << get_fileline() << ": internal error: Something wrong " << "with the left side width of <= ?" << endl; cerr << get_fileline() << ": : " << *this << endl; } - if (NetEConst*tmp = must_be_leeq_(left_, rv, true)) { + if (NetEConst*tmp = must_be_leeq_(le, rv, true)) { return tmp; } /* Now go on to the normal test of the values. */ - NetEConst*l = dynamic_cast(left_); + const NetEConst*l = dynamic_cast(le); if (l == 0) return 0; verinum lv = l->value(); @@ -407,13 +422,12 @@ NetEConst* NetEBComp::eval_leeq_() } } -NetEConst* NetEBComp::eval_gt_() +NetEConst* NetEBComp::eval_gt_(const NetExpr*le, const NetExpr*re) const { - if (right_->expr_type() == IVL_VT_REAL || - left_->expr_type() == IVL_VT_REAL) - return eval_leeq_real_(right_, left_, false); + if (le->expr_type() == IVL_VT_REAL || re->expr_type() == IVL_VT_REAL) + return eval_leeq_real_(re, le, false); - NetEConst*l = dynamic_cast(left_); + const NetEConst*l = dynamic_cast(le); if (l == 0) return 0; verinum lv = l->value(); @@ -423,12 +437,12 @@ NetEConst* NetEBComp::eval_gt_() return res; } - if (NetEConst*tmp = must_be_leeq_(right_, lv, false)) { + if (NetEConst*tmp = must_be_leeq_(re, lv, false)) { return tmp; } /* Now go on to the normal test of the values. */ - NetEConst*r = dynamic_cast(right_); + const NetEConst*r = dynamic_cast(re); if (r == 0) return 0; verinum rv = r->value(); @@ -449,13 +463,12 @@ NetEConst* NetEBComp::eval_gt_() } } -NetEConst* NetEBComp::eval_gteq_() +NetEConst* NetEBComp::eval_gteq_(const NetExpr*le, const NetExpr*re) const { - if (right_->expr_type() == IVL_VT_REAL || - left_->expr_type() == IVL_VT_REAL) - return eval_leeq_real_(right_, left_, true); + if (le->expr_type() == IVL_VT_REAL || re->expr_type() == IVL_VT_REAL) + return eval_leeq_real_(re, le, true); - NetEConst*l = dynamic_cast(left_); + const NetEConst*l = dynamic_cast(left_); if (l == 0) return 0; verinum lv = l->value(); @@ -465,12 +478,12 @@ NetEConst* NetEBComp::eval_gteq_() return res; } - if (NetEConst*tmp = must_be_leeq_(right_, lv, true)) { + if (NetEConst*tmp = must_be_leeq_(re, lv, true)) { return tmp; } /* Now go on to the normal test of the values. */ - NetEConst*r = dynamic_cast(right_); + const NetEConst*r = dynamic_cast(re); if (r == 0) return 0; verinum rv = r->value(); @@ -498,15 +511,15 @@ NetEConst* NetEBComp::eval_gteq_() * are equal, but there are are x/z bits, then the situation is * ambiguous so the result is x. */ -NetEConst* NetEBComp::eval_eqeq_real_(bool ne_flag) +NetEConst* NetEBComp::eval_eqeq_real_(bool ne_flag, const NetExpr*le, const NetExpr*re) const { - verireal lval; - verireal rval; + double lval; + double rval; - bool flag = get_real_arguments_(lval, rval); + bool flag = get_real_arguments(le, re, lval, rval); if (! flag) return 0; - verinum result(((lval.as_double() == rval.as_double()) ^ ne_flag) ? + verinum result(((lval == rval) ^ ne_flag) ? verinum::V1 : verinum::V0, 1); NetEConst*res = new NetEConst(result); ivl_assert(*this, res); @@ -514,14 +527,14 @@ NetEConst* NetEBComp::eval_eqeq_real_(bool ne_flag) return res; } -NetEConst* NetEBComp::eval_eqeq_(bool ne_flag) +NetEConst* NetEBComp::eval_eqeq_(bool ne_flag, const NetExpr*le, const NetExpr*re) const { - if (left_->expr_type() == IVL_VT_REAL || - right_->expr_type() == IVL_VT_REAL) - return eval_eqeq_real_(ne_flag); + if (le->expr_type() == IVL_VT_REAL || + re->expr_type() == IVL_VT_REAL) + return eval_eqeq_real_(ne_flag, le, re); - NetEConst*lc = dynamic_cast(left_); - NetEConst*rc = dynamic_cast(right_); + const NetEConst*lc = dynamic_cast(le); + const NetEConst*rc = dynamic_cast(re); if (lc == 0 || rc == 0) return 0; const verinum&lv = lc->value(); @@ -631,10 +644,10 @@ NetEConst* NetEBComp::eval_eqeq_(bool ne_flag) return result; } -NetEConst* NetEBComp::eval_eqeqeq_(bool ne_flag) +NetEConst* NetEBComp::eval_eqeqeq_(bool ne_flag, const NetExpr*le, const NetExpr*re) const { - NetEConst*lc = dynamic_cast(left_); - NetEConst*rc = dynamic_cast(right_); + const NetEConst*lc = dynamic_cast(le); + const NetEConst*rc = dynamic_cast(re); if (lc == 0 || rc == 0) return 0; const verinum&lv = lc->value(); @@ -687,47 +700,54 @@ NetEConst* NetEBComp::eval_eqeqeq_(bool ne_flag) return result; } +NetEConst* NetEBComp::eval_arguments_(const NetExpr*l, const NetExpr*r) const +{ + NetEConst*res = 0; + + switch (op_) { + case 'E': // Case equality (===) + res = eval_eqeqeq_(false, l, r); + break; + + case 'e': // Equality (==) + res = eval_eqeq_(false, l, r); + break; + + case 'G': // >= + res = eval_gteq_(l, r); + break; + + case 'L': // <= + res = eval_leeq_(l, r); + break; + + case 'N': // Case inequality (!==) + res = eval_eqeqeq_(true, l, r); + break; + + case 'n': // not-equal (!=) + res = eval_eqeq_(true, l, r); + break; + + case '<': // Less than + res = eval_less_(l, r); + break; + + case '>': // Greater than + res = eval_gt_(l, r); + break; + + } + + return res; +} + NetEConst* NetEBComp::eval_tree() { eval_expr(left_); eval_expr(right_); - NetEConst*res = 0; - - switch (op_) { - case 'E': // Case equality (===) - res = eval_eqeqeq_(false); - break; - - case 'e': // Equality (==) - res = eval_eqeq_(false); - break; - - case 'G': // >= - res = eval_gteq_(); - break; - - case 'L': // <= - res = eval_leeq_(); - break; - - case 'N': // Case inequality (!==) - res = eval_eqeqeq_(true); - break; - - case 'n': // not-equal (!=) - res = eval_eqeq_(true); - break; - - case '<': // Less than - res = eval_less_(); - break; - - case '>': // Greater than - res = eval_gt_(); - break; - - } + NetEConst*res = eval_arguments_(left_, right_); if (res == 0) return 0; res->set_line(*this); @@ -1043,9 +1063,13 @@ NetEConst* NetEBShift::eval_tree() { eval_expr(left_); eval_expr(right_); + return eval_arguments_(left_,right_); +} - NetEConst*le = dynamic_cast(left_); - NetEConst*re = dynamic_cast(right_); +NetEConst* NetEBShift::eval_arguments_(const NetExpr*l, const NetExpr*r) const +{ + const NetEConst*le = dynamic_cast(l); + const NetEConst*re = dynamic_cast(r); if (le == 0 || re == 0) return 0; NetEConst*res; diff --git a/net_func_eval.cc b/net_func_eval.cc index 662eca9bf..7e40bf581 100644 --- a/net_func_eval.cc +++ b/net_func_eval.cc @@ -18,6 +18,7 @@ */ # include "netlist.h" +# include "compiler.h" # include # include "ivl_assert.h" @@ -29,6 +30,11 @@ NetExpr* NetFuncDef::evaluate_function(const LineInfo&loc, const std::vector::iterator ptr; mapcontext_map; + if (debug_eval_tree) { + cerr << loc.get_fileline() << ": debug: " + << "Evaluate function " << scope_->basename() << endl; + } + // Put the return value into the map... context_map[scope_->basename()] = 0; // Load the input ports into the map... @@ -37,6 +43,11 @@ NetExpr* NetFuncDef::evaluate_function(const LineInfo&loc, const std::vectordup_expr(); perm_string aname = ports_[idx]->name(); context_map[aname] = tmp; + + if (debug_eval_tree) { + cerr << loc.get_fileline() << ": debug: " + << " input " << aname << " = " << *tmp << endl; + } } // Perform the evaluation @@ -100,6 +111,13 @@ bool NetAssign::evaluate_function(const LineInfo&loc, map::iterator ptr = context_map.find(lval->name()); if (ptr->second) delete ptr->second; + + if (debug_eval_tree) { + cerr << get_fileline() << ": debug: " + << "NetAssign::evaluate_function: " << lval->name() + << " = " << *rval_result << endl; + } + ptr->second = rval_result; return true; @@ -123,6 +141,103 @@ bool NetBlock::evaluate_function(const LineInfo&loc, return flag; } +bool NetWhile::evaluate_function(const LineInfo&loc, + map&context_map) const +{ + bool flag = true; + + if (debug_eval_tree) { + cerr << get_fileline() << ": debug: NetWhile::evaluate_fuction: " + << "Start loop" << endl; + } + + while (flag) { + // Evaluate the condition expression to try and get the + // condition for the loop. + NetExpr*cond = cond_->evaluate_function(loc, context_map); + if (cond == 0) { + flag = false; + break; + } + + NetEConst*cond_const = dynamic_cast (cond); + ivl_assert(loc, cond_const); + + long val = cond_const->value().as_long(); + delete cond; + + // If the condition is false, then break. + if (val == 0) + break; + + // The condition is true, so evalutate the statement + // another time. + bool tmp_flag = proc_->evaluate_function(loc, context_map); + if (! tmp_flag) + flag = false; + } + + if (debug_eval_tree) { + cerr << get_fileline() << ": debug: NetWhile::evaluate_fuction: " + << "Done loop" << endl; + } + + return flag; +} + +NetExpr* NetEBComp::evaluate_function(const LineInfo&loc, + map&context_map) const +{ + NetExpr*lval = left_->evaluate_function(loc, context_map); + NetExpr*rval = right_->evaluate_function(loc, context_map); + + if (lval == 0 || rval == 0) { + delete lval; + delete rval; + return 0; + } + + NetEConst*res = eval_arguments_(lval, rval); + delete lval; + delete rval; + return res; +} + +NetExpr* NetEBAdd::evaluate_function(const LineInfo&loc, + map&context_map) const +{ + NetExpr*lval = left_->evaluate_function(loc, context_map); + NetExpr*rval = right_->evaluate_function(loc, context_map); + + if (lval == 0 || rval == 0) { + delete lval; + delete rval; + return 0; + } + + NetExpr*res = eval_arguments_(lval, rval); + delete lval; + delete rval; + return res; +} + +NetExpr* NetEBShift::evaluate_function(const LineInfo&loc, + map&context_map) const +{ + NetExpr*lval = left_->evaluate_function(loc, context_map); + NetExpr*rval = right_->evaluate_function(loc, context_map); + + if (lval == 0 || rval == 0) { + delete lval; + delete rval; + return 0; + } + + NetEConst*res = eval_arguments_(lval, rval); + delete lval; + delete rval; + return res; +} NetExpr* NetEConst::evaluate_function(const LineInfo&, map&) const @@ -131,3 +246,21 @@ NetExpr* NetEConst::evaluate_function(const LineInfo&, res->set_line(*this); return res; } + +NetExpr* NetESignal::evaluate_function(const LineInfo&, + map&context_map) const +{ + if (word_) { + cerr << get_fileline() << ": sorry: I don't know how to evaluate signal word selects at compile time." << endl; + return 0; + } + + map::iterator ptr = context_map.find(name()); + if (ptr == context_map.end()) { + cerr << get_fileline() << ": error: Cannot evaluate " << name() + << " in this context." << endl; + return 0; + } + + return ptr->second->dup_expr(); +} diff --git a/netlist.h b/netlist.h index e5eb200f8..1409d1182 100644 --- a/netlist.h +++ b/netlist.h @@ -3294,6 +3294,8 @@ class NetWhile : public NetProc { virtual bool emit_proc(struct target_t*) const; virtual void dump(ostream&, unsigned ind) const; virtual DelayType delay_type() const; + virtual bool evaluate_function(const LineInfo&loc, + map&ctx) const; private: NetExpr* cond_; @@ -3451,10 +3453,14 @@ class NetEBAdd : public NetEBinary { virtual NetEBAdd* dup_expr() const; virtual NetExpr* eval_tree(); + virtual NetExpr* evaluate_function(const LineInfo&loc, + std::map&ctx) const; + virtual NetNet* synthesize(Design*, NetScope*scope, NetExpr*root); private: - NetECReal* eval_tree_real_(); + NetExpr * eval_arguments_(const NetExpr*l, const NetExpr*r) const; + NetECReal* eval_tree_real_(const NetExpr*l, const NetExpr*r) const; }; /* @@ -3530,19 +3536,23 @@ class NetEBComp : public NetEBinary { virtual NetEBComp* dup_expr() const; virtual NetEConst* eval_tree(); + virtual NetExpr*evaluate_function(const LineInfo&loc, + std::map&ctx) const; + virtual NetNet* synthesize(Design*, NetScope*scope, NetExpr*root); private: - NetEConst* must_be_leeq_(NetExpr*le, const verinum&rv, bool eq_flag); + NetEConst* must_be_leeq_(const NetExpr*le, const verinum&rv, bool eq_flag) const; - NetEConst*eval_eqeq_(bool ne_flag); - NetEConst*eval_eqeq_real_(bool ne_flag); - NetEConst*eval_less_(); - NetEConst*eval_leeq_(); - NetEConst*eval_leeq_real_(NetExpr*le, NetExpr*ri, bool eq_flag); - NetEConst*eval_gt_(); - NetEConst*eval_gteq_(); - NetEConst*eval_eqeqeq_(bool ne_flag); + NetEConst*eval_arguments_(const NetExpr*le, const NetExpr*re) const; + NetEConst*eval_eqeq_(bool ne_flag, const NetExpr*le, const NetExpr*re) const; + NetEConst*eval_eqeq_real_(bool ne_flag, const NetExpr*le, const NetExpr*re) const; + NetEConst*eval_less_(const NetExpr*le, const NetExpr*re) const; + NetEConst*eval_leeq_(const NetExpr*le, const NetExpr*re) const; + NetEConst*eval_leeq_real_(const NetExpr*le, const NetExpr*ri, bool eq_flag) const; + NetEConst*eval_gt_(const NetExpr*le, const NetExpr*re) const; + NetEConst*eval_gteq_(const NetExpr*le, const NetExpr*re) const; + NetEConst*eval_eqeqeq_(bool ne_flag, const NetExpr*le, const NetExpr*re) const; }; /* @@ -3644,9 +3654,13 @@ class NetEBShift : public NetEBinary { virtual NetEBShift* dup_expr() const; virtual NetEConst* eval_tree(); + virtual NetExpr*evaluate_function(const LineInfo&loc, + std::map&ctx) const; + virtual NetNet* synthesize(Design*, NetScope*scope, NetExpr*root); private: + NetEConst* eval_arguments_(const NetExpr*l, const NetExpr*r) const; }; @@ -3978,6 +3992,9 @@ class NetESignal : public NetExpr { NetNet* synthesize(Design*des, NetScope*scope, NetExpr*root); NexusSet* nex_input(bool rem_out = true); + virtual NetExpr*evaluate_function(const LineInfo&loc, + std::map&ctx) const; + // This is the expression for selecting an array word, if this // signal refers to an array. const NetExpr* word_index() const; From 62b67c184370fbe025381c78bddfb6c6287a43c4 Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Tue, 29 May 2012 17:59:29 -0700 Subject: [PATCH 047/179] More expression types supported during constant function evaluation. --- eval_tree.cc | 35 +++++++---- net_func_eval.cc | 147 ++++++++++++++++++++++++++++++++++++++++++++++- netlist.h | 17 +++++- 3 files changed, 183 insertions(+), 16 deletions(-) diff --git a/eval_tree.cc b/eval_tree.cc index 1a393fabb..e4f792989 100644 --- a/eval_tree.cc +++ b/eval_tree.cc @@ -954,15 +954,15 @@ NetEConst* NetEBLogic::eval_tree() } -NetExpr* NetEBMult::eval_tree_real_() +NetExpr* NetEBMult::eval_tree_real_(const NetExpr*l, const NetExpr*r) const { - verireal lval; - verireal rval; + double lval; + double rval; - bool flag = get_real_arguments_(lval, rval); + bool flag = get_real_arguments(l, r, lval, rval); if (! flag) return 0; - NetECReal*res = new NetECReal(lval * rval); + NetECReal*res = new NetECReal( verireal(lval * rval) ); ivl_assert(*this, res); res->set_line(*this); @@ -978,11 +978,16 @@ NetExpr* NetEBMult::eval_tree() eval_expr(left_); eval_expr(right_); - if (expr_type() == IVL_VT_REAL) return eval_tree_real_(); + return eval_arguments_(left_, right_); +} + +NetExpr* NetEBMult::eval_arguments_(const NetExpr*l, const NetExpr*r) const +{ + if (expr_type() == IVL_VT_REAL) return eval_tree_real_(l,r); assert(expr_type() == IVL_VT_LOGIC); - NetEConst*lc = dynamic_cast(left_); - NetEConst*rc = dynamic_cast(right_); + const NetEConst*lc = dynamic_cast(l); + const NetEConst*rc = dynamic_cast(r); if (lc == 0 || rc == 0) return 0; verinum lval = lc->value(); @@ -1338,12 +1343,18 @@ NetExpr* NetETernary::eval_tree() eval_expr(true_val_); eval_expr(false_val_); - NetEConst*t = dynamic_cast(true_val_); - NetEConst*f = dynamic_cast(false_val_); + return blended_arguments_(true_val_, false_val_); +} + +NetExpr*NetETernary::blended_arguments_(const NetExpr*te, const NetExpr*fe) const +{ + + const NetEConst*t = dynamic_cast(te); + const NetEConst*f = dynamic_cast(fe); if (t == 0 || f == 0) { verireal tv, fv; - if (!get_real_arg_(true_val_, tv)) return 0; - if (!get_real_arg_(false_val_, fv)) return 0; + if (!get_real_arg_(te, tv)) return 0; + if (!get_real_arg_(te, fv)) return 0; verireal val = verireal(0.0); if (tv.as_double() == fv.as_double()) val = tv; diff --git a/net_func_eval.cc b/net_func_eval.cc index 7e40bf581..82a9bb863 100644 --- a/net_func_eval.cc +++ b/net_func_eval.cc @@ -18,6 +18,7 @@ */ # include "netlist.h" +# include "netmisc.h" # include "compiler.h" # include # include "ivl_assert.h" @@ -37,6 +38,7 @@ NetExpr* NetFuncDef::evaluate_function(const LineInfo&loc, const std::vectorbasename()] = 0; + // Load the input ports into the map... ivl_assert(loc, ports_.size() == args.size()); for (size_t idx = 0 ; idx < ports_.size() ; idx += 1) { @@ -50,6 +52,10 @@ NetExpr* NetFuncDef::evaluate_function(const LineInfo&loc, const std::vectorevaluate_function_find_locals(loc, context_map); + // Perform the evaluation bool flag = statement_->evaluate_function(loc, context_map); @@ -72,6 +78,26 @@ NetExpr* NetFuncDef::evaluate_function(const LineInfo&loc, const std::vector&context_map) const +{ + for (map::const_iterator cur = signals_map_.begin() + ; cur != signals_map_.end() ; ++cur) { + + const NetNet*tmp = cur->second; + // Skip ports, which are handled elsewhere. + if (tmp->port_type() != NetNet::NOT_A_PORT) + continue; + + context_map[tmp->name()] = 0; + + if (debug_eval_tree) { + cerr << loc.get_fileline() << ": debug: " + << " (local) " << tmp->name() << endl; + } + } +} + NetExpr* NetExpr::evaluate_function(const LineInfo&, map&) const { @@ -101,14 +127,50 @@ bool NetAssign::evaluate_function(const LineInfo&loc, const NetAssign_*lval = l_val(0); - ivl_assert(loc, lval->word() == 0); - ivl_assert(loc, lval->get_base() == 0); + map::iterator ptr = context_map.find(lval->name()); + ivl_assert(*this, ptr != context_map.end()); + // Do not support having l-values that are unpacked arrays. + ivl_assert(loc, lval->word() == 0); + + // Evaluate the r-value expression. NetExpr*rval_result = rval()->evaluate_function(loc, context_map); if (rval_result == 0) return false; - map::iterator ptr = context_map.find(lval->name()); + if (const NetExpr*base_expr = lval->get_base()) { + NetExpr*base_result = base_expr->evaluate_function(loc, context_map); + if (base_result == 0) { + delete rval_result; + return false; + } + + NetEConst*base_const = dynamic_cast(base_result); + ivl_assert(loc, base_const); + + long base = base_const->value().as_long(); + + listprefix (0); + base = lval->sig()->sb_to_idx(prefix, base); + + if (ptr->second == 0) + ptr->second = make_const_x(lval->sig()->vector_width()); + + ivl_assert(loc, base + lval->lwidth() <= ptr->second->expr_width()); + + NetEConst*ptr_const = dynamic_cast(ptr->second); + verinum lval_v = ptr_const->value(); + NetEConst*rval_const = dynamic_cast(rval_result); + verinum rval_v = cast_to_width(rval_const->value(), lval->lwidth()); + + for (unsigned idx = 0 ; idx < rval_v.len() ; idx += 1) + lval_v.set(idx+base, rval_v[idx]); + + delete base_result; + delete rval_result; + rval_result = new NetEConst(lval_v); + } + if (ptr->second) delete ptr->second; @@ -221,6 +283,24 @@ NetExpr* NetEBAdd::evaluate_function(const LineInfo&loc, return res; } +NetExpr* NetEBMult::evaluate_function(const LineInfo&loc, + map&context_map) const +{ + NetExpr*lval = left_->evaluate_function(loc, context_map); + NetExpr*rval = right_->evaluate_function(loc, context_map); + + if (lval == 0 || rval == 0) { + delete lval; + delete rval; + return 0; + } + + NetExpr*res = eval_arguments_(lval, rval); + delete lval; + delete rval; + return res; +} + NetExpr* NetEBShift::evaluate_function(const LineInfo&loc, map&context_map) const { @@ -247,6 +327,40 @@ NetExpr* NetEConst::evaluate_function(const LineInfo&, return res; } +NetExpr* NetESelect::evaluate_function(const LineInfo&loc, + map&context_map) const +{ + NetExpr*sub_exp = expr_->evaluate_function(loc, context_map); + ivl_assert(loc, sub_exp); + + NetEConst*sub_const = dynamic_cast (sub_exp); + ivl_assert(loc, sub_exp); + + verinum sub = sub_const->value(); + delete sub_exp; + + long base = 0; + if (base_) { + NetExpr*base_val = base_->evaluate_function(loc, context_map); + ivl_assert(loc, base_val); + + NetEConst*base_const = dynamic_cast(base_val); + ivl_assert(loc, base_const); + + base = base_const->value().as_long(); + delete base_val; + } else { + sub = pad_to_width(sub, expr_width()); + } + + verinum res (verinum::Vx, expr_width()); + for (unsigned idx = 0 ; idx < res.len() ; idx += 1) + res.set(idx, sub[base+idx]); + + NetEConst*res_const = new NetEConst(res); + return res_const; +} + NetExpr* NetESignal::evaluate_function(const LineInfo&, map&context_map) const { @@ -264,3 +378,30 @@ NetExpr* NetESignal::evaluate_function(const LineInfo&, return ptr->second->dup_expr(); } + +NetExpr* NetETernary::evaluate_function(const LineInfo&loc, + map&context_map) const +{ + auto_ptr cval (cond_->evaluate_function(loc, context_map)); + + switch (const_logical(cval.get())) { + + case C_0: + return false_val_->evaluate_function(loc, context_map); + case C_1: + return true_val_->evaluate_function(loc, context_map); + case C_X: + break; + default: + cerr << get_fileline() << ": error: Condition expression is not constant here." << endl; + return 0; + } + + NetExpr*tval = true_val_->evaluate_function(loc, context_map); + NetExpr*fval = false_val_->evaluate_function(loc, context_map); + + NetExpr*res = blended_arguments_(tval, fval); + delete tval; + delete fval; + return res; +} diff --git a/netlist.h b/netlist.h index 1409d1182..3138fe90d 100644 --- a/netlist.h +++ b/netlist.h @@ -816,6 +816,11 @@ class NetScope : public Attrib { NetTaskDef* task_def(); NetFuncDef* func_def(); + // This is used by the evaluate_function setup to collect + // local variables from the scope. + void evaluate_function_find_locals(const LineInfo&loc, + map&context_map) const; + void set_line(perm_string file, perm_string def_file, unsigned lineno, unsigned def_lineno); void set_line(perm_string file, unsigned lineno); @@ -3607,10 +3612,13 @@ class NetEBMult : public NetEBinary { virtual NetEBMult* dup_expr() const; virtual NetExpr* eval_tree(); + virtual NetExpr* evaluate_function(const LineInfo&loc, + std::map&ctx) const; virtual NetNet* synthesize(Design*, NetScope*scope, NetExpr*root); private: - NetExpr* eval_tree_real_(); + NetExpr* eval_arguments_(const NetExpr*l, const NetExpr*r) const; + NetExpr* eval_tree_real_(const NetExpr*l, const NetExpr*r) const; }; /* @@ -3729,6 +3737,8 @@ class NetESelect : public NetExpr { virtual bool has_width() const; virtual void expr_scan(struct expr_scan_t*) const; virtual NetEConst* eval_tree(); + virtual NetExpr*evaluate_function(const LineInfo&loc, + std::map&ctx) const; virtual NetESelect* dup_expr() const; virtual NetNet*synthesize(Design*des, NetScope*scope, NetExpr*root); virtual void dump(ostream&) const; @@ -3866,6 +3876,9 @@ class NetETernary : public NetExpr { virtual NetETernary* dup_expr() const; virtual NetExpr* eval_tree(); + virtual NetExpr*evaluate_function(const LineInfo&loc, + std::map&ctx) const; + virtual ivl_variable_type_t expr_type() const; virtual NexusSet* nex_input(bool rem_out = true); virtual void expr_scan(struct expr_scan_t*) const; @@ -3876,6 +3889,8 @@ class NetETernary : public NetExpr { static bool test_operand_compat(ivl_variable_type_t tru, ivl_variable_type_t fal); private: + NetExpr* blended_arguments_(const NetExpr*t, const NetExpr*f) const; + NetExpr*cond_; NetExpr*true_val_; NetExpr*false_val_; From 3354d833910627b29b6da608f882464756e6e9bf Mon Sep 17 00:00:00 2001 From: Martin Whitaker Date: Tue, 5 Jun 2012 09:13:06 +0100 Subject: [PATCH 048/179] Support if statements and function calls in constant functions. --- eval_tree.cc | 6 +++++- net_func_eval.cc | 40 +++++++++++++++++++++++++++++++++++++--- netlist.h | 5 +++++ 3 files changed, 47 insertions(+), 4 deletions(-) diff --git a/eval_tree.cc b/eval_tree.cc index e4f792989..bb63ba09c 100644 --- a/eval_tree.cc +++ b/eval_tree.cc @@ -2001,7 +2001,11 @@ NetExpr* NetEUFunc::eval_tree() NetFuncDef*def = func_->func_def(); ivl_assert(*this, def); - NetExpr*res = def->evaluate_function(*this, parms_); + vectorargs(parms_.size()); + for (unsigned idx = 0 ; idx < parms_.size() ; idx += 1) + args[idx] = parms_[idx]->dup_expr(); + + NetExpr*res = def->evaluate_function(*this, args); return res; } diff --git a/net_func_eval.cc b/net_func_eval.cc index 82a9bb863..35f481acb 100644 --- a/net_func_eval.cc +++ b/net_func_eval.cc @@ -42,13 +42,12 @@ NetExpr* NetFuncDef::evaluate_function(const LineInfo&loc, const std::vectordup_expr(); perm_string aname = ports_[idx]->name(); - context_map[aname] = tmp; + context_map[aname] = args[idx]; if (debug_eval_tree) { cerr << loc.get_fileline() << ": debug: " - << " input " << aname << " = " << *tmp << endl; + << " input " << aname << " = " << *args[idx] << endl; } } @@ -203,6 +202,27 @@ bool NetBlock::evaluate_function(const LineInfo&loc, return flag; } +bool NetCondit::evaluate_function(const LineInfo&loc, + map&context_map) const +{ + NetExpr*cond = expr_->evaluate_function(loc, context_map); + if (cond == 0) + return false; + + NetEConst*cond_const = dynamic_cast (cond); + ivl_assert(loc, cond_const); + + long val = cond_const->value().as_long(); + delete cond; + + if (val) + // The condition is true, so evaluate the if clause + return (if_ == 0) || if_->evaluate_function(loc, context_map); + else + // The condition is false, so evaluate the else clause + return (else_ == 0) || else_->evaluate_function(loc, context_map); +} + bool NetWhile::evaluate_function(const LineInfo&loc, map&context_map) const { @@ -405,3 +425,17 @@ NetExpr* NetETernary::evaluate_function(const LineInfo&loc, delete fval; return res; } + +NetExpr* NetEUFunc::evaluate_function(const LineInfo&loc, + map&context_map) const +{ + NetFuncDef*def = func_->func_def(); + ivl_assert(*this, def); + + vectorargs(parms_.size()); + for (unsigned idx = 0 ; idx < parms_.size() ; idx += 1) + args[idx] = parms_[idx]->evaluate_function(loc, context_map); + + NetExpr*res = def->evaluate_function(*this, args); + return res; +} diff --git a/netlist.h b/netlist.h index 8c95994e3..12a0384d0 100644 --- a/netlist.h +++ b/netlist.h @@ -2671,6 +2671,8 @@ class NetCondit : public NetProc { virtual int match_proc(struct proc_match_t*); virtual void dump(ostream&, unsigned ind) const; virtual DelayType delay_type() const; + virtual bool evaluate_function(const LineInfo&loc, + map&ctx) const; private: NetExpr* expr_; @@ -3225,6 +3227,9 @@ class NetEUFunc : public NetExpr { virtual NetEUFunc*dup_expr() const; virtual NexusSet* nex_input(bool rem_out = true); virtual NetExpr* eval_tree(); + virtual NetExpr*evaluate_function(const LineInfo&loc, + std::map&ctx) const; + virtual NetNet* synthesize(Design*des, NetScope*scope, NetExpr*root); private: From 9b3d20239a6688de9127a1d8593d4c83c1c25fce Mon Sep 17 00:00:00 2001 From: Andrew Stevens Date: Mon, 4 Jun 2012 21:43:33 +0200 Subject: [PATCH 049/179] Extend VPI and build to for SIMetrix cosimulation Added: basic vpiPort VPI Objects for vpiModulkes vpiDirection, vpiPortIndex, vpiName, vpiSize attributes Since ports do not exist as net-like entities (nets either side module instance boundaries are in effect connect directly in the language front-ends internal representation) the port information is effectively just meta-data passed through t-dll interface and output as a additional annotation of module scopes in vvp. Added: vpiLocalParam attribute for vpiParameter VPI objects Added: support build for 32-bit target on 64-bit host (--with-m32 option to configure.in and minor tweaks to Makefiles and systemc-vpi). --- Makefile.in | 4 +- Module.cc | 16 +++++++ Module.h | 3 ++ PExpr.h | 2 +- configure.in | 28 ++++++++++++ elab_net.cc | 6 +-- elab_scope.cc | 12 +++--- elaborate.cc | 54 ++++++++++++++++++++--- iverilog-vpi.sh | 2 +- ivl_target.h | 16 ++++++- libveriuser/Makefile.in | 6 ++- main.cc | 10 ++++- net_scope.cc | 51 ++++++++++++++++------ netlist.cc | 28 +++++++++++- netlist.h | 61 +++++++++++++++++++++++--- t-dll-api.cc | 46 ++++++++++++++++++++ t-dll.cc | 14 ++++-- t-dll.h | 7 +++ tgt-vvp/vvp_scope.c | 90 +++++++++++++++++++++++++++++++++------ vpi_user.h | 12 ++++++ vvp/compile.cc | 17 +++++--- vvp/compile.h | 15 ++++++- vvp/delay.h | 1 + vvp/lexor.lex | 7 +++ vvp/main.cc | 10 +++++ vvp/parse.y | 35 +++++++++++---- vvp/parse_misc.h | 1 + vvp/schedule.cc | 2 +- vvp/vpi_const.cc | 48 +++++++++++++++------ vvp/vpi_priv.cc | 7 ++- vvp/vpi_priv.h | 10 +++-- vvp/vpi_scope.cc | 94 +++++++++++++++++++++++++++++++++++++++++ vvp/words.cc | 1 + 33 files changed, 623 insertions(+), 93 deletions(-) diff --git a/Makefile.in b/Makefile.in index bcebfa407..26f2f13ef 100644 --- a/Makefile.in +++ b/Makefile.in @@ -95,6 +95,7 @@ CFLAGS = @WARNING_FLAGS@ @CFLAGS@ CXXFLAGS = @WARNING_FLAGS@ @WARNING_FLAGS_CXX@ @CXXFLAGS@ PICFLAGS = @PICFLAG@ LDFLAGS = @rdynamic@ @LDFLAGS@ +CTARGETFLAGS = @CTARGETFLAGS@ # Source files in the libmisc directory M = LineInfo.o StringHeap.o @@ -228,6 +229,7 @@ iverilog-vpi: $(srcdir)/iverilog-vpi.sh Makefile -e 's;@IVCXX@;$(CXX);' \ -e 's;@IVCFLAGS@;$(CFLAGS);' \ -e 's;@IVCXXFLAGS@;$(CXXFLAGS);' \ + -e 's;@IVCTARGETFLAGS@;$(CTARGETFLAGS);' \ -e 's;@INCLUDEDIR@;$(includedir);' \ -e 's;@LIBDIR@;@libdir@;' $< > $@ chmod +x $@ @@ -239,7 +241,7 @@ version.exe: $(srcdir)/version.c $(srcdir)/version_base.h version_tag.h %.o: %.cc config.h $(CXX) $(CPPFLAGS) $(CXXFLAGS) @DEPENDENCY_FLAG@ -c $< -o $*.o mv $*.d dep/$*.d - + # Here are some explicit dependencies needed to get things going. main.o: main.cc version_tag.h diff --git a/Module.cc b/Module.cc index d448f6dfc..ce70a9164 100644 --- a/Module.cc +++ b/Module.cc @@ -90,6 +90,22 @@ unsigned Module::find_port(const char*name) const return ports.size(); } +perm_string Module::get_port_name(unsigned idx) const +{ + + assert(idx < ports.size()); + if (ports[idx] == 0) { + /* It is possible to have undeclared ports. These + are ports that are skipped in the declaration, + for example like so: module foo(x ,, y); The + port between x and y is unnamed and thus + inaccessible to binding by name. */ + return perm_string::literal(""); + } + return ports[idx]->name; +} + + PGate* Module::get_gate(perm_string name) { diff --git a/Module.h b/Module.h index ad6604b37..a414b9e68 100644 --- a/Module.h +++ b/Module.h @@ -136,6 +136,9 @@ class Module : public PScopeExtra, public LineInfo { const vector& get_port(unsigned idx) const; unsigned find_port(const char*name) const; + // Return port name ("" for undeclared port) + perm_string get_port_name(unsigned idx) const; + PGate* get_gate(perm_string name); const list& get_gates() const; diff --git a/PExpr.h b/PExpr.h index a4036680b..98a16e14a 100644 --- a/PExpr.h +++ b/PExpr.h @@ -311,7 +311,7 @@ class PEIdent : public PExpr { // Elaborate the PEIdent as a port to a module. This method // only applies to Ident expressions. - NetNet* elaborate_port(Design*des, NetScope*sc) const; + NetNet* elaborate_subport(Design*des, NetScope*sc) const; verinum* eval_const(Design*des, NetScope*sc) const; diff --git a/configure.in b/configure.in index 28349c475..6910d511c 100644 --- a/configure.in +++ b/configure.in @@ -91,6 +91,20 @@ fi AC_LANG(C++) +AC_ARG_WITH([m32], [AC_HELP_STRING([--with-m32], [Compile 32-bit on x86_64])], + [ with_m32=yes ],[ with_m32=no ]) + +AS_IF( [test "x$with_m32" = xyes], + [ AC_MSG_NOTICE([Compiling for 32-bit environment - needs gcc on x86_64]) + LDTARGETFLAGS="-m elf_i386" + CTARGETFLAGS="-m32" + ], + []) + +CFLAGS="$CTARGETFLAGS $CFLAGS" +CXXFLAGS="$CTARGETFLAGS $CXXFLAGS" +LDFLAGS="$CTARGETFLAGS $LDFLAGS" + # Check that we are using either the GNU compilers or the Sun compilers # but not a mixture of the two (not currently supported). AC_CHECK_DECL(__SUNPRO_CC, using_sunpro_cc=1, using_sunpro_cc=0) @@ -111,7 +125,11 @@ else fi fi +iverilog_temp_cxxflags="$CXXFLAGS" +CXXFLAGS="-DHAVE_DECL_BASENAME $CXXFLAGS" + AC_CHECK_HEADERS(getopt.h inttypes.h libiberty.h iosfwd sys/wait.h) +CXXFLAGS="$iverilog_temp_cxxflags" AC_CHECK_SIZEOF(unsigned long long) AC_CHECK_SIZEOF(unsigned long) @@ -178,6 +196,11 @@ if test -z "$DLLIB" ; then AC_CHECK_LIB(dld,shl_load,[DLLIB=-ldld]) fi AC_SUBST(DLLIB) +AC_SUBST(LDRELOCFLAGS) + +AC_SUBST(CTARGETFLAGS) +AC_SUBST(LDTARGETFLAGS) + AC_PROG_INSTALL @@ -304,4 +327,9 @@ AC_MSG_RESULT(ok) # XXX disable tgt-fpga for the moment +# +# Ensure compiler target options go in... + + + AC_OUTPUT(Makefile ivlpp/Makefile vhdlpp/Makefile vvp/Makefile vpi/Makefile driver/Makefile driver-vpi/Makefile cadpli/Makefile libveriuser/Makefile tgt-null/Makefile tgt-stub/Makefile tgt-vvp/Makefile tgt-vhdl/Makefile tgt-fpga/Makefile tgt-verilog/Makefile tgt-pal/Makefile tgt-vlog95/Makefile tgt-pcb/Makefile) diff --git a/elab_net.cc b/elab_net.cc index fa4a3fd2a..11019a989 100644 --- a/elab_net.cc +++ b/elab_net.cc @@ -691,7 +691,7 @@ NetNet* PEIdent::elaborate_bi_net(Design*des, NetScope*scope) const * instantiation (PGModule::elaborate_mod_) to get NetNet objects for * the port. */ -NetNet* PEIdent::elaborate_port(Design*des, NetScope*scope) const +NetNet* PEIdent::elaborate_subport(Design*des, NetScope*scope) const { ivl_assert(*this, scope->type() == NetScope::MODULE); NetNet*sig = des->find_signal(scope, path_); @@ -748,7 +748,7 @@ NetNet* PEIdent::elaborate_port(Design*des, NetScope*scope) const /* If this is a part select of the entire signal (or no part select at all) then we're done. */ if ((lidx == 0) && (midx == (long)sig->vector_width()-1)) { - scope->add_module_port(sig); + scope->add_module_port_net(sig); return sig; } @@ -795,7 +795,7 @@ NetNet* PEIdent::elaborate_port(Design*des, NetScope*scope) const ps->set_line(*this); des->add_node(ps); - scope->add_module_port(sig); + scope->add_module_port_net(sig); return sig; } diff --git a/elab_scope.cc b/elab_scope.cc index 796f60633..617e8bac8 100644 --- a/elab_scope.cc +++ b/elab_scope.cc @@ -52,7 +52,8 @@ typedef map::const_iterator mparm_it_t; static void collect_parm_item_(Design*des, NetScope*scope, perm_string name, const LexicalScope::param_expr_t&cur, - bool is_annotatable) + bool is_annotatable, + bool local_flag) { NetScope::range_t*range_list = 0; for (LexicalScope::range_t*range = cur.range ; range ; range = range->next) { @@ -88,8 +89,9 @@ static void collect_parm_item_(Design*des, NetScope*scope, perm_string name, range_list = tmp; } + scope->set_parameter(name, is_annotatable, cur.expr, cur.type, cur.msb, - cur.lsb, cur.signed_flag, range_list, cur); + cur.lsb, cur.signed_flag, local_flag, range_list, cur); } static void collect_scope_parameters_(Design*des, NetScope*scope, @@ -107,7 +109,7 @@ static void collect_scope_parameters_(Design*des, NetScope*scope, des->errors += 1; } - collect_parm_item_(des, scope, (*cur).first, (*cur).second, false); + collect_parm_item_(des, scope, (*cur).first, (*cur).second, false, false); } } @@ -126,7 +128,7 @@ static void collect_scope_localparams_(Design*des, NetScope*scope, des->errors += 1; } - collect_parm_item_(des, scope, (*cur).first, (*cur).second, false); + collect_parm_item_(des, scope, (*cur).first, (*cur).second, false, true); } } @@ -145,7 +147,7 @@ static void collect_scope_specparams_(Design*des, NetScope*scope, des->errors += 1; } - collect_parm_item_(des, scope, (*cur).first, (*cur).second, true); + collect_parm_item_(des, scope, (*cur).first, (*cur).second, true, false); } } diff --git a/elaborate.cc b/elaborate.cc index 319051b18..c777fb65a 100644 --- a/elaborate.cc +++ b/elaborate.cc @@ -1250,6 +1250,7 @@ void PGModule::elaborate_mod_(Design*des, Module*rmod, NetScope*scope) const get_name() << "..." << endl; for (unsigned inst = 0 ; inst < instance.size() ; inst += 1) { rmod->elaborate(des, instance[inst]); + instance[inst]->set_num_ports( rmod->port_count() ); } if (debug_elaborate) cerr << get_fileline() << ": debug: ...done." << endl; @@ -1329,7 +1330,7 @@ void PGModule::elaborate_mod_(Design*des, Module*rmod, NetScope*scope) const unconnected_port = true; } - // Inside the module, the port is zero or more signals + // Inside the module, the port connects zero or more signals // that were already elaborated. List all those signals // and the NetNet equivalents, for all the instances. vector mport = rmod->get_port(idx); @@ -1349,19 +1350,24 @@ void PGModule::elaborate_mod_(Design*des, Module*rmod, NetScope*scope) const // will be assembled in that order as well. NetScope*inst_scope = instance[instance.size()-inst-1]; + unsigned int prt_vector_width = 0; + PortType::Enum ptype = PortType::PIMPLICIT; // Scan the module sub-ports for this instance... for (unsigned ldx = 0 ; ldx < mport.size() ; ldx += 1) { unsigned lbase = inst * mport.size(); PEIdent*pport = mport[ldx]; assert(pport); - prts[lbase + ldx] - = pport->elaborate_port(des, inst_scope); - if (prts[lbase + ldx] == 0) + NetNet *netnet = pport->elaborate_subport(des, inst_scope); + prts[lbase + ldx] = netnet; + if (netnet == 0) continue; - assert(prts[lbase + ldx]); - prts_vector_width += prts[lbase + ldx]->vector_width(); + assert(netnet); + prts_vector_width += netnet->vector_width(); + prt_vector_width += netnet->vector_width(); + ptype = PortType::merged(netnet->port_type(), ptype); } + inst_scope->add_module_port_info(idx, rmod->get_port_name(idx), ptype, prt_vector_width ); } // If I find that the port is unconnected inside the @@ -4512,6 +4518,7 @@ static void elaborate_tasks(Design*des, NetScope*scope, * When a module is instantiated, it creates the scope then uses this * method to elaborate the contents of the module. */ + bool Module::elaborate(Design*des, NetScope*scope) const { bool result_flag = true; @@ -4976,10 +4983,22 @@ Design* elaborate(listroots) // creates all the NetNet and NetMemory objects for declared // objects. for (i = 0; i < root_elems.count(); i++) { + Module *rmod = root_elems[i]->mod; NetScope *scope = root_elems[i]->scope; + scope->set_num_ports( rmod->port_count() ); + + if (debug_elaborate) { + cerr << "" << ": debug: " << rmod->mod_name() + << ": port elaboration root " + << rmod->port_count() << " ports" << endl; + } if (! rmod->elaborate_sig(des, scope)) { + if (debug_elaborate) { + cerr << "" << ": debug: " << rmod->mod_name() + << ": elaborate_sig failed!!!" << endl; + } delete des; return 0; } @@ -4988,11 +5007,26 @@ Design* elaborate(listroots) // defined for the root modules. This code does that. for (unsigned idx = 0; idx < rmod->port_count(); idx += 1) { vector mport = rmod->get_port(idx); + unsigned int prt_vector_width = 0; + PortType::Enum ptype = PortType::PIMPLICIT; for (unsigned pin = 0; pin < mport.size(); pin += 1) { // This really does more than we need and adds extra // stuff to the design that should be cleaned later. - (void) mport[pin]->elaborate_port(des, scope); + (void) mport[pin]->elaborate_subport(des, scope); + NetNet *netnet = mport[pin]->elaborate_subport(des, scope); + if( netnet != 0 ) + { + // Elaboration may actually fail with erroneous input source + prt_vector_width += netnet->vector_width(); + ptype = PortType::merged(netnet->port_type(), ptype); + } } + if (debug_elaborate) { + cerr << "" << ": debug: " << rmod->mod_name() + << ": adding module port " + << rmod->get_port_name(idx) << endl; + } + scope->add_module_port_info(idx, rmod->get_port_name(idx), ptype, prt_vector_width ); } } @@ -5019,5 +5053,11 @@ Design* elaborate(listroots) des = 0; } + if (debug_elaborate) { + cerr << "" << ": debug: " + << " finishing with " + << des->find_root_scopes().size() << " root scopes " << endl; + } + return des; } diff --git a/iverilog-vpi.sh b/iverilog-vpi.sh index c280733bd..38f81613b 100644 --- a/iverilog-vpi.sh +++ b/iverilog-vpi.sh @@ -28,7 +28,7 @@ SUFFIX=@SUFFIX@ # These are used for linking... LD=$CC -LDFLAGS="@SHARED@ -L@LIBDIR@" +LDFLAGS="@IVCTARGETFLAGS@ @SHARED@ -L@LIBDIR@" LDLIBS="-lveriuser$SUFFIX -lvpi$SUFFIX" CCSRC= diff --git a/ivl_target.h b/ivl_target.h index 2933ac0e6..0639ca7e5 100644 --- a/ivl_target.h +++ b/ivl_target.h @@ -193,6 +193,7 @@ typedef struct ivl_parameter_s*ivl_parameter_t; typedef struct ivl_process_s *ivl_process_t; typedef struct ivl_scope_s *ivl_scope_t; typedef struct ivl_signal_s *ivl_signal_t; +typedef struct ivl_port_info_s *ivl_port_info_t; typedef struct ivl_switch_s *ivl_switch_t; typedef struct ivl_memory_s *ivl_memory_t; //XXXX __attribute__((deprecated)); typedef struct ivl_statement_s*ivl_statement_t; @@ -363,7 +364,7 @@ typedef enum ivl_scope_type_e { /* Signals (ivl_signal_t) that are ports into the scope that contains them have a port type. Otherwise, they are port IVL_SIP_NONE. */ -typedef enum ivl_signal_port_e { +typedef enum OUT { IVL_SIP_NONE = 0, IVL_SIP_INPUT = 1, IVL_SIP_OUTPUT= 2, @@ -1568,6 +1569,10 @@ extern ivl_signal_t ivl_nexus_ptr_sig(ivl_nexus_ptr_t net); * Return the value of the parameter. This should be a simple * constant expression, an IVL_EX_STRING or IVL_EX_NUMBER. * + * ivl_parameter_local + * Return whether parameter was local (localparam, implicit genvar etc) + * or not. + * * ivl_parameter_file * ivl_parameter_lineno * Returns the file and line where this parameter is defined @@ -1575,7 +1580,7 @@ extern ivl_signal_t ivl_nexus_ptr_sig(ivl_nexus_ptr_t net); extern const char* ivl_parameter_basename(ivl_parameter_t net); extern ivl_scope_t ivl_parameter_scope(ivl_parameter_t net); extern ivl_expr_t ivl_parameter_expr(ivl_parameter_t net); - +extern int ivl_parameter_local(ivl_parameter_t net); extern const char* ivl_parameter_file(ivl_parameter_t net); extern unsigned ivl_parameter_lineno(ivl_parameter_t net); @@ -1738,6 +1743,12 @@ extern const char* ivl_scope_basename(ivl_scope_t net); extern unsigned ivl_scope_params(ivl_scope_t net); extern ivl_parameter_t ivl_scope_param(ivl_scope_t net, unsigned idx); extern ivl_scope_t ivl_scope_parent(ivl_scope_t net); + +extern unsigned ivl_scope_mod_module_ports(ivl_scope_t net); +extern const char *ivl_scope_mod_module_port_name(ivl_scope_t net, unsigned idx ); +extern ivl_signal_port_t ivl_scope_mod_module_port_type(ivl_scope_t net, unsigned idx ); +extern unsigned ivl_scope_mod_module_port_width(ivl_scope_t net, unsigned idx ); + extern unsigned ivl_scope_ports(ivl_scope_t net); extern ivl_signal_t ivl_scope_port(ivl_scope_t net, unsigned idx); extern ivl_nexus_t ivl_scope_mod_port(ivl_scope_t net, unsigned idx); @@ -1878,6 +1889,7 @@ extern int ivl_signal_msb(ivl_signal_t net) __attribute__((deprecated)); extern int ivl_signal_lsb(ivl_signal_t net) __attribute__((deprecated)); extern unsigned ivl_signal_width(ivl_signal_t net); extern ivl_signal_port_t ivl_signal_port(ivl_signal_t net); +extern int ivl_signal_module_port_index(ivl_signal_t net); extern int ivl_signal_signed(ivl_signal_t net); extern int ivl_signal_integer(ivl_signal_t net); extern int ivl_signal_local(ivl_signal_t net); diff --git a/libveriuser/Makefile.in b/libveriuser/Makefile.in index 557c12fa0..ceaf8e7a3 100644 --- a/libveriuser/Makefile.in +++ b/libveriuser/Makefile.in @@ -44,6 +44,10 @@ else INCLUDE_PATH = -I. -I.. -I$(srcdir) -I$(srcdir)/.. endif +LDRELOCFLAGS = @LDRELOCFLAGS@ + +LDTARGETFLAGS = @LDTARGETFLAGS@ + CPPFLAGS = $(INCLUDE_PATH) @CPPFLAGS@ @DEFS@ @PICFLAG@ CFLAGS = @WARNING_FLAGS@ @CFLAGS@ @@ -87,7 +91,7 @@ stamp-config-h: $(srcdir)/config.h.in ../config.status config.h: stamp-config-h libveriuser.o: $O - $(LD) -r -o $@ $O + $(LD) $(LDTARGETFLAGS) -r -o $@ $O libveriuser.a: libveriuser.o rm -f $@ diff --git a/main.cc b/main.cc index 2b8066576..2218ff872 100644 --- a/main.cc +++ b/main.cc @@ -784,7 +784,13 @@ int main(int argc, char*argv[]) #if defined(TRAP_SIGINT_FOR_DEBUG) signal(SIGINT, &signals_handler); #endif - + if( ::getenv("IVL_WAIT_FOR_DEBUGGER") != 0 ) { + fprintf( stderr, "Waiting for debugger...\n"); + bool debugger_release = false; + while( !debugger_release ) { + sleep(1); + } + } library_suff.push_back(strdup(".v")); // Start the module list with the base system module. @@ -839,6 +845,8 @@ int main(int argc, char*argv[]) cout << COPYRIGHT << endl << endl; cout << NOTICE << endl; + cout << " FLAGS DLL " << flags["DLL"] << endl; + dll_target_obj.test_version(flags["DLL"]); return 0; diff --git a/net_scope.cc b/net_scope.cc index 47d4751ce..f6a4131c3 100644 --- a/net_scope.cc +++ b/net_scope.cc @@ -114,14 +114,10 @@ void NetScope::set_line(perm_string file, perm_string def_file, def_lineno_ = def_lineno; } -/* - * This is the full-featured version of set_parameter. It is used for - * adding parameter, localparam, and specparam declarations to the - * parameter list. - */ void NetScope::set_parameter(perm_string key, bool is_annotatable, PExpr*val, ivl_variable_type_t type__, PExpr*msb, PExpr*lsb, bool signed_flag, + bool local_flag, NetScope::range_t*range_list, const LineInfo&file_line) { @@ -135,6 +131,7 @@ void NetScope::set_parameter(perm_string key, bool is_annotatable, ref.msb = 0; ref.lsb = 0; ref.signed_flag = signed_flag; + ref.local_flag = local_flag; ivl_assert(file_line, ref.range == 0); ref.range = range_list; ref.val = 0; @@ -370,23 +367,51 @@ perm_string NetScope::module_name() const return module_name_; } -void NetScope::add_module_port(NetNet*port) +void NetScope::set_num_ports(unsigned int num_ports) { - assert(type_ == MODULE); - ports_.push_back(port); + assert(type_ == MODULE); + assert( ports_.size() == 0 ); + ports_.resize( num_ports ); } -unsigned NetScope::module_ports() const +void NetScope::add_module_port_net(NetNet*subport) { assert(type_ == MODULE); - return ports_.size(); + port_nets.push_back(subport); } -NetNet* NetScope::module_port(unsigned idx) const + +void NetScope::add_module_port_info( unsigned idx, perm_string name, PortType::Enum ptype, + unsigned long width ) { assert(type_ == MODULE); - assert(idx < ports_.size()); - return ports_[idx]; + PortInfo &info = ports_[idx]; + info.name = name; + info.type = ptype; + info.width = width; +} + + +unsigned NetScope::module_port_nets() const +{ + assert(type_ == MODULE); + return port_nets.size(); +} + + +const std::vector & NetScope::module_port_info() const +{ + assert(type_ == MODULE); + return ports_; +} + + + +NetNet* NetScope::module_port_net(unsigned idx) const +{ + assert(type_ == MODULE); + assert(idx < port_nets.size()); + return port_nets[idx]; } void NetScope::time_unit(int val) diff --git a/netlist.cc b/netlist.cc index b3bb702e2..03a393a00 100644 --- a/netlist.cc +++ b/netlist.cc @@ -447,12 +447,27 @@ const Link& NetDelaySrc::condit_pin() const return pin(pin_count()-1); } +PortType::Enum PortType::merged( Enum lhs, Enum rhs ) +{ + if( lhs == NOT_A_PORT || rhs == NOT_A_PORT ) + return NOT_A_PORT; + if( lhs == PIMPLICIT ) + return rhs; + if( rhs == PIMPLICIT ) + return lhs; + if( lhs == rhs ) { + return lhs; + } + return PINOUT; +} + NetNet::NetNet(NetScope*s, perm_string n, Type t, unsigned npins) : NetObj(s, n, 1), type_(t), port_type_(NOT_A_PORT), data_type_(IVL_VT_NO_TYPE), signed_(false), isint_(false), is_scalar_(false), local_flag_(false), enumeration_(0), struct_type_(0), discipline_(0), - eref_count_(0), lref_count_(0) + eref_count_(0), lref_count_(0), + port_index_(-1) { assert(s); assert(npins>0); @@ -698,6 +713,17 @@ void NetNet::port_type(NetNet::PortType t) port_type_ = t; } +int NetNet::get_module_port_index() const +{ + return port_index_; +} + +void NetNet::set_module_port_index(unsigned idx) +{ + port_index_ = idx; + assert( port_index_ >= 0 ); +} + ivl_variable_type_t NetNet::data_type() const { return data_type_; diff --git a/netlist.h b/netlist.h index 12a0384d0..7ba64280d 100644 --- a/netlist.h +++ b/netlist.h @@ -556,14 +556,42 @@ class NetDelaySrc : public NetObj { * anything and they are not a data sink, per se. The pins follow the * values on the nexus. */ -class NetNet : public NetObj { + +class PortType +{ +public: + enum Enum { NOT_A_PORT, PIMPLICIT, PINPUT, POUTPUT, PINOUT, PREF }; + + /* + * Merge Port types (used to construct a sane combined port-type + * for module ports with complex defining expressions). + * + */ + static Enum merged( Enum lhs, Enum rhs ); +}; + + + /* + * Information on actual ports (rather than port-connected signals) of + * module. + * N.b. must be POD as passed through a "C" interface in the t-dll-api. + */ +struct PortInfo +{ + PortType::Enum type; + unsigned long width; + perm_string name; +}; + + +class NetNet : public NetObj, public PortType { public: enum Type { NONE, IMPLICIT, IMPLICIT_REG, INTEGER, WIRE, TRI, TRI1, SUPPLY0, SUPPLY1, WAND, TRIAND, TRI0, WOR, TRIOR, REG, UNRESOLVED_WIRE }; - enum PortType { NOT_A_PORT, PIMPLICIT, PINPUT, POUTPUT, PINOUT, PREF }; + typedef PortType::Enum PortType; public: // The width in this case is a shorthand for ms=width-1 and @@ -592,6 +620,11 @@ class NetNet : public NetObj { PortType port_type() const; void port_type(PortType t); + // If this net net is a port (i.e. a *sub*port net of a module port) + // its port index is number of the module it connects through + int get_module_port_index() const; // -1 Not connected to port... + void set_module_port_index(unsigned idx); + ivl_variable_type_t data_type() const; void data_type(ivl_variable_type_t t); @@ -713,6 +746,7 @@ class NetNet : public NetObj { std::vector lref_mask_; vector delay_paths_; + int port_index_; }; extern std::ostream&operator << (std::ostream&out, const std::list&rlist); @@ -747,6 +781,7 @@ class NetScope : public Attrib { void set_parameter(perm_string name, bool is_annotatable, PExpr*val, ivl_variable_type_t type, PExpr*msb, PExpr*lsb, bool signed_flag, + bool local_flag, NetScope::range_t*range_list, const LineInfo&file_line); void set_parameter(perm_string name, NetExpr*val, @@ -878,9 +913,18 @@ class NetScope : public Attrib { perm_string module_name() const; /* If the scope is a module then it may have ports that we need * to keep track of. */ - void add_module_port(NetNet*port); - unsigned module_ports() const; - NetNet*module_port(unsigned idx) const; + + void set_num_ports(unsigned int num_ports); + void add_module_port_net(NetNet*port); + unsigned module_port_nets() const; + NetNet*module_port_net(unsigned idx) const; + + void add_module_port_info( unsigned idx, + perm_string name, // May be "" for undeclared port + PortType::Enum type, + unsigned long width ); + + const std::vector &module_port_info() const; /* Scopes have their own time units and time precision. The unit and precision are given as power of 10, i.e., -3 is @@ -951,6 +995,7 @@ class NetScope : public Attrib { param_expr_t() : msb_expr(0), lsb_expr(0), val_expr(0), val_scope(0), solving(false), is_annotatable(false), type(IVL_VT_NO_TYPE), signed_flag(false), + local_flag(false), msb(0), lsb(0), range(0), val(0) { } // Source expressions PExpr*msb_expr; @@ -965,6 +1010,7 @@ class NetScope : public Attrib { // Type information ivl_variable_type_t type; bool signed_flag; + bool local_flag; NetExpr*msb; NetExpr*lsb; // range constraints @@ -1018,7 +1064,10 @@ class NetScope : public Attrib { typedef std::map::const_iterator signals_map_iter_t; std::map signals_map_; perm_string module_name_; - vectorports_; + vector port_nets; + + vector ports_; + union { NetTaskDef*task_; NetFuncDef*func_; diff --git a/t-dll-api.cc b/t-dll-api.cc index 150258cac..d38025c2b 100644 --- a/t-dll-api.cc +++ b/t-dll-api.cc @@ -1681,6 +1681,13 @@ extern "C" const char* ivl_parameter_basename(ivl_parameter_t net) return net->basename; } +extern "C" int ivl_parameter_local(ivl_parameter_t net) +{ + assert(net); + return net->local; +} + + extern "C" ivl_expr_t ivl_parameter_expr(ivl_parameter_t net) { assert(net); @@ -1979,6 +1986,40 @@ extern "C" ivl_scope_t ivl_scope_parent(ivl_scope_t net) return net->parent; } + +extern "C" unsigned ivl_scope_mod_module_ports(ivl_scope_t net) +{ + assert(net); + assert (net->type_ == IVL_SCT_MODULE ); + return static_cast(net->module_ports_info.size()); +} + +extern "C" const char *ivl_scope_mod_module_port_name(ivl_scope_t net, unsigned idx ) +{ + assert(net); + assert (net->type_ == IVL_SCT_MODULE ); + assert( idx < net->module_ports_info.size()); + + return net->module_ports_info[idx].name; +} + +extern "C" ivl_signal_port_t ivl_scope_mod_module_port_type(ivl_scope_t net, unsigned idx ) +{ + switch( net->module_ports_info[idx].type ) + { + case PortType::PINPUT : return IVL_SIP_INPUT; + case PortType::POUTPUT : return IVL_SIP_OUTPUT; + case PortType::PINOUT : return IVL_SIP_INOUT; + default : return IVL_SIP_NONE; + } +} + +extern "C" unsigned ivl_scope_mod_module_port_width(ivl_scope_t net, unsigned idx ) +{ + return net->module_ports_info[idx].width; +} + + extern "C" unsigned ivl_scope_ports(ivl_scope_t net) { assert(net); @@ -2198,6 +2239,11 @@ extern "C" ivl_signal_port_t ivl_signal_port(ivl_signal_t net) return net->port_; } +extern "C" int ivl_signal_module_port_index(ivl_signal_t net) +{ + return net->module_port_index_; +} + extern "C" int ivl_signal_local(ivl_signal_t net) { return net->local_; diff --git a/t-dll.cc b/t-dll.cc index 0bee8e88d..3960681fe 100644 --- a/t-dll.cc +++ b/t-dll.cc @@ -478,6 +478,7 @@ void dll_target::make_scope_parameters(ivl_scope_t scop, const NetScope*net) assert(idx < scop->nparam_); ivl_parameter_t cur_par = scop->param_ + idx; cur_par->basename = (*cur_pit).first; + cur_par->local = cur_pit->second.local_flag; cur_par->scope = scop; FILE_NAME(cur_par, &((*cur_pit).second)); @@ -550,13 +551,14 @@ void dll_target::add_root(ivl_design_s &des__, const NetScope *s) root_->attr = fill_in_attributes(s); root_->is_auto = 0; root_->is_cell = s->is_cell(); - root_->ports = s->module_ports(); + root_->ports = s->module_port_nets(); if (root_->ports > 0) { root_->u_.net = new NetNet*[root_->ports]; for (unsigned idx = 0; idx < root_->ports; idx += 1) { - root_->u_.net[idx] = s->module_port(idx); + root_->u_.net[idx] = s->module_port_net(idx); } } + root_->module_ports_info = s->module_port_info(); des__.nroots_++; if (des__.roots_) @@ -2299,14 +2301,16 @@ void dll_target::scope(const NetScope*net) case NetScope::MODULE: scop->type_ = IVL_SCT_MODULE; scop->tname_ = net->module_name(); - scop->ports = net->module_ports(); + scop->ports = net->module_port_nets(); if (scop->ports > 0) { scop->u_.net = new NetNet*[scop->ports]; for (unsigned idx = 0; idx < scop->ports; idx += 1) { - scop->u_.net[idx] = net->module_port(idx); + scop->u_.net[idx] = net->module_port_net(idx); } } + scop->module_ports_info = net->module_port_info(); break; + case NetScope::TASK: { const NetTaskDef*def = net->task_def(); if (def == 0) { @@ -2425,6 +2429,8 @@ void dll_target::signal(const NetNet*net) break; } + obj->module_port_index_ = net->get_module_port_index(); + switch (net->type()) { case NetNet::REG: diff --git a/t-dll.h b/t-dll.h index 265c59590..ca5f4db3c 100644 --- a/t-dll.h +++ b/t-dll.h @@ -584,6 +584,7 @@ struct ivl_parameter_s { perm_string basename; ivl_scope_t scope; ivl_expr_t value; + bool local; perm_string file; unsigned lineno; }; @@ -647,6 +648,10 @@ struct ivl_scope_s { unsigned is_cell; + // Ports of Module scope (just introspection data for VPI) - actual connections + // are nets defined in u_.net (may be > 1 per module port) + std::vector module_ports_info; + unsigned ports; union { ivl_signal_t*port; @@ -672,6 +677,7 @@ struct ivl_scope_s { struct ivl_signal_s { ivl_signal_type_t type_; ivl_signal_port_t port_; + int module_port_index_; ivl_variable_type_t data_type; ivl_discipline_t discipline; perm_string file; @@ -709,6 +715,7 @@ struct ivl_signal_s { unsigned nattr; }; + /* * The ivl_statement_t represents any statement. The type of statement * is defined by the ivl_statement_type_t enumeration. Given the type, diff --git a/tgt-vvp/vvp_scope.c b/tgt-vvp/vvp_scope.c index 37555c5ca..c927ed932 100644 --- a/tgt-vvp/vvp_scope.c +++ b/tgt-vvp/vvp_scope.c @@ -417,6 +417,36 @@ const char*draw_input_from_net(ivl_nexus_t nex) } +/* Create flag string for port nature */ + +static const char *port_type_str( ivl_signal_port_t ptype ) +{ + switch( ptype ) + { + case IVL_SIP_INPUT : + return "INPUT"; + case IVL_SIP_OUTPUT : + return "OUTPUT"; + case IVL_SIP_INOUT : + return "INOUT"; + case IVL_SIP_NONE : + default : + return "NOT_PORT"; + } +} +/* Create flag string for et nature" port nature / localness */ + +static const char *port_nature_flag_str( ivl_signal_t sig ) +{ + return port_type_str( ivl_signal_port(sig) ); +} + + +static const char *local_flag_str( ivl_signal_t sig ) +{ + return ivl_signal_local(sig)? "*" : ""; +} + /* * This function draws a reg/int/variable in the scope. This is a very * simple device to draw as there are no inputs to connect so no need @@ -442,9 +472,9 @@ static void draw_reg_in_scope(ivl_signal_t sig) break; } - const char*datatype_flag = ivl_signal_integer(sig) ? "/i" : + const char *datatype_flag = ivl_signal_integer(sig) ? "/i" : ivl_signal_signed(sig)? "/s" : ""; - const char*local_flag = ivl_signal_local(sig)? "*" : ""; + const char *local_flag = local_flag_str(sig); switch (ivl_signal_data_type(sig)) { case IVL_VT_BOOL: @@ -477,7 +507,7 @@ static void draw_reg_in_scope(ivl_signal_t sig) fprintf(vvp_out, "v%p_0 .var%s %s\"%s\", %d %d;%s\n", sig, datatype_flag, local_flag, vvp_mangle_name(ivl_signal_basename(sig)), msb, lsb, - ivl_signal_local(sig)? " Local signal" : ""); + ivl_signal_local(sig)? " Local signal" : "" ); } } @@ -506,7 +536,8 @@ static void draw_net_in_scope(ivl_signal_t sig) } const char*datatype_flag = ivl_signal_signed(sig)? "/s" : ""; - const char*local_flag = ivl_signal_local(sig)? "*" : ""; + const char *local_flag = local_flag_str(sig); + unsigned iword; switch (ivl_signal_data_type(sig)) { @@ -562,7 +593,7 @@ static void draw_net_in_scope(ivl_signal_t sig) sig, iword, vec8, datatype_flag, sig, iword, msb, lsb, driver, nex_data->drivers_count, - strength_aware_flag?", strength-aware":""); + strength_aware_flag?", strength-aware":"" ); } else if (ivl_signal_local(sig) && ivl_scope_is_auto(ivl_signal_scope(sig))) { assert(word_count == 1); @@ -578,13 +609,13 @@ static void draw_net_in_scope(ivl_signal_t sig) /* If this is an isolated word, it uses its own name. */ assert(word_count == 1); - fprintf(vvp_out, "v%p_%u .net%s%s %s\"%s\", %d %d, %s;" - " %u drivers%s\n", + fprintf(vvp_out, "v%p_%u .net%s%s %s\"%s\", %d %d, %s; " + " %u drivers %s\n", sig, iword, vec8, datatype_flag, local_flag, vvp_mangle_name(ivl_signal_basename(sig)), msb, lsb, driver, nex_data->drivers_count, - strength_aware_flag?", strength-aware":""); + strength_aware_flag?", strength-aware":"" ); } nex_data->net = sig; nex_data->net_word = iword; @@ -600,7 +631,7 @@ static void draw_net_in_scope(ivl_signal_t sig) if (word_count == ivl_signal_array_count(nex_data->net)) { if (iword == 0) { - fprintf(vvp_out, "v%p .array \"%s\", v%p; Alias to %s\n", + fprintf(vvp_out, "v%p .array \"%s\", v%p; Alias to %s \n", sig, vvp_mangle_name(ivl_signal_basename(sig)), nex_data->net, ivl_signal_basename(nex_data->net)); @@ -615,7 +646,7 @@ static void draw_net_in_scope(ivl_signal_t sig) sig, vvp_mangle_name(ivl_signal_basename(sig)), swapped ? first : last, - swapped ? last : first); + swapped ? last : first ); } fprintf(vvp_out, "v%p_%u .alias%s v%p %u, %d %d, " @@ -637,7 +668,7 @@ static void draw_net_in_scope(ivl_signal_t sig) if (strength_aware_flag) vec8 = "8"; - fprintf(vvp_out, "v%p_%u .net%s%s %s\"%s\", %d %d, %s;" + fprintf(vvp_out, "v%p_%u .net%s%s %s\"%s\", %d %d, %s; " " alias, %u drivers%s\n", sig, iword, vec8, datatype_flag, local_flag, vvp_mangle_name(ivl_signal_basename(sig)), @@ -2075,6 +2106,20 @@ static void draw_lpm_in_scope(ivl_lpm_t net) } } + +static const char *vvp_port_info_type_str(ivl_signal_port_t ptype) +{ + switch( ptype ) + { + case IVL_SIP_INPUT : return "/INPUT"; + case IVL_SIP_OUTPUT : return "/OUTPUT"; + case IVL_SIP_INOUT : return "/INOUT"; + case IVL_SIP_NONE : return "/NODIR"; + default : + abort(); // NO SUPPORT FOR ANYTHING ELSE YET... + } +} + int draw_scope(ivl_scope_t net, ivl_scope_t parent) { unsigned idx; @@ -2109,20 +2154,36 @@ int draw_scope(ivl_scope_t net, ivl_scope_t parent) fprintf(vvp_out, " .timescale %d %d;\n", ivl_scope_time_units(net), ivl_scope_time_precision(net)); + if( ivl_scope_type(net) == IVL_SCT_MODULE ) { + + // Port data for VPI: needed for vpiPorts property of vpiModule + for( idx = 0; idx < ivl_scope_mod_module_ports(net); ++idx ) { + const char *name = ivl_scope_mod_module_port_name(net,idx); + ivl_signal_port_t ptype = ivl_scope_mod_module_port_type(net,idx); + unsigned width = ivl_scope_mod_module_port_width(net,idx); + if( name == 0 ) + name = ""; + fprintf( vvp_out, " .port_info %d %s %u \"%s\"\n", + idx, vvp_port_info_type_str(ptype), width, name ); + } + } + for (idx = 0 ; idx < ivl_scope_params(net) ; idx += 1) { ivl_parameter_t par = ivl_scope_param(net, idx); ivl_expr_t pex = ivl_parameter_expr(par); switch (ivl_expr_type(pex)) { case IVL_EX_STRING: - fprintf(vvp_out, "P_%p .param/str \"%s\" %d %d, \"%s\";\n", + fprintf(vvp_out, "P_%p .param/str \"%s\" %d %d %d, \"%s\";\n", par, ivl_parameter_basename(par), + ivl_parameter_local(par), ivl_file_table_index(ivl_parameter_file(par)), ivl_parameter_lineno(par), ivl_expr_string(pex)); break; case IVL_EX_NUMBER: - fprintf(vvp_out, "P_%p .param/l \"%s\" %d %d, %sC4<", + fprintf(vvp_out, "P_%p .param/l \"%s\" %d %d %d, %sC4<", par, ivl_parameter_basename(par), + ivl_parameter_local(par), ivl_file_table_index(ivl_parameter_file(par)), ivl_parameter_lineno(par), ivl_expr_signed(pex)? "+":""); @@ -2136,8 +2197,9 @@ int draw_scope(ivl_scope_t net, ivl_scope_t parent) break; case IVL_EX_REALNUM: { char *res = draw_Cr_to_string(ivl_expr_dvalue(pex)); - fprintf(vvp_out, "P_%p .param/real \"%s\" %d %d, %s; " + fprintf(vvp_out, "P_%p .param/real \"%s\" %d %d %d, %s; " "value=%#g\n", par, ivl_parameter_basename(par), + ivl_parameter_local(par), ivl_file_table_index(ivl_parameter_file(par)), ivl_parameter_lineno(par), res, ivl_expr_dvalue(pex)); diff --git a/vpi_user.h b/vpi_user.h index 3d5811835..d0fe2b415 100644 --- a/vpi_user.h +++ b/vpi_user.h @@ -43,6 +43,7 @@ EXTERN_C_START # include # include +# include # include "_pli_types.h" #define ICARUS_VPI_CONST @@ -286,6 +287,7 @@ typedef struct t_vpi_delay { #define vpiParameter 41 #define vpiPartSelect 42 #define vpiPathTerm 43 +#define vpiPort 44 #define vpiRealVar 47 #define vpiReg 48 #define vpiSysFuncCall 56 @@ -325,6 +327,14 @@ typedef struct t_vpi_delay { #define vpiTimePrecision 12 #define vpiDefFile 15 #define vpiDefLineNo 16 + +#define vpiDirection 20 /* direction of port: */ +# define vpiInput 1 +# define vpiOutput 2 +# define vpiInout 3 +# define vpiMixedIO 4 /* Not currently output */ +# define vpiNoDirection 5 + #define vpiNetType 22 # define vpiWire 1 # define vpiWand 2 @@ -338,6 +348,7 @@ typedef struct t_vpi_delay { # define vpiSupply1 10 # define vpiSupply0 11 #define vpiArray 28 +#define vpiPortIndex 29 #define vpiEdge 36 # define vpiNoEdge 0x00 /* No edge */ # define vpiEdge01 0x01 /* 0 --> 1 */ @@ -371,6 +382,7 @@ typedef struct t_vpi_delay { #define vpiAutomatic 50 #define vpiConstantSelect 53 #define vpiSigned 65 +#define vpiLocalParam 70 /* IVL private properties, also see vvp/vpi_priv.h for other properties */ #define _vpiNexusId 0x1000000 /* used in vvp/vpi_priv.h 0x1000001 */ diff --git a/vvp/compile.cc b/vvp/compile.cc index 9edb2e97f..2d863726f 100644 --- a/vvp/compile.cc +++ b/vvp/compile.cc @@ -17,6 +17,9 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ + +# include "config.h" +# include "delay.h" # include "arith.h" # include "compile.h" # include "logic.h" @@ -28,6 +31,7 @@ # include "vpi_priv.h" # include "parse_misc.h" # include "statistics.h" +# include "schedule.h" # include # include # include @@ -610,7 +614,7 @@ static void compile_array_lookup(struct vvp_code_s*code, char*label) resolv_submit(res); } -static list scheduled_compiletf; +static std::list scheduled_compiletf; void compile_compiletf(struct __vpiSysTaskCall*obj) { @@ -859,7 +863,7 @@ void input_connect(vvp_net_t*fdx, unsigned port, char*label) void inputs_connect(vvp_net_t*fdx, unsigned argc, struct symb_s*argv) { if (argc > 4) { - cerr << "XXXX argv[0] = " << argv[0].text << endl; + std::cerr << "XXXX argv[0] = " << argv[0].text << std::endl; } assert(argc <= 4); @@ -1808,11 +1812,12 @@ void compile_thread(char*start_sym, char*flag) } void compile_param_logic(char*label, char*name, char*value, bool signed_flag, + bool local_flag, long file_idx, long lineno) { vvp_vector4_t value4 = c4string_to_vector4(value); vpiHandle obj = vpip_make_binary_param(name, value4, signed_flag, - file_idx, lineno); + local_flag, file_idx, lineno); compile_vpi_symbol(label, obj); vpip_attach_to_current_scope(obj); @@ -1821,10 +1826,11 @@ void compile_param_logic(char*label, char*name, char*value, bool signed_flag, } void compile_param_string(char*label, char*name, char*value, + bool local_flag, long file_idx, long lineno) { // name and value become owned bi vpip_make_string_param - vpiHandle obj = vpip_make_string_param(name, value, file_idx, lineno); + vpiHandle obj = vpip_make_string_param(name, value, local_flag, file_idx, lineno); compile_vpi_symbol(label, obj); vpip_attach_to_current_scope(obj); @@ -1832,10 +1838,11 @@ void compile_param_string(char*label, char*name, char*value, } void compile_param_real(char*label, char*name, char*value, + bool local_flag, long file_idx, long lineno) { double dvalue = crstring_to_double(value); - vpiHandle obj = vpip_make_real_param(name, dvalue, file_idx, lineno); + vpiHandle obj = vpip_make_real_param(name, dvalue, local_flag, file_idx, lineno); compile_vpi_symbol(label, obj); vpip_attach_to_current_scope(obj); diff --git a/vvp/compile.h b/vvp/compile.h index 29fb2cb1e..35d69d845 100644 --- a/vvp/compile.h +++ b/vvp/compile.h @@ -254,11 +254,13 @@ extern void compile_vpi_symbol(const char*label, vpiHandle obj); extern void compile_vpi_lookup(vpiHandle *objref, char*label); extern void compile_param_string(char*label, char*name, char*value, + bool local_flag, long file_idx, long lineno); extern void compile_param_logic(char*label, char*name, char*value, - bool signed_flag, + bool signed_flag, bool local_flag, long file_idx, long lineno); extern void compile_param_real(char*label, char*name, char*value, + bool local_flag, long file_idx, long lineno); /* @@ -456,6 +458,17 @@ extern void compile_variable(char*label, char*name, extern void compile_var_real(char*label, char*name, int msb, int lsb); +/* + * This function is used to create a scope port + * Current ONLY module ports are supported and ports exist purely + * as meta-data for VPI queries (i.e. there is NO corresponding net etc) + * as elaboration internally eliminates port-nets by directly connecting + * nets connected through module ports. + */ + +extern void compile_port_info( unsigned index, int vpi_port_type, unsigned width, const char *name ); + + /* * The compile_net function is called to create a .net vector with a * given name. diff --git a/vvp/delay.h b/vvp/delay.h index 567abe4dd..62864656f 100644 --- a/vvp/delay.h +++ b/vvp/delay.h @@ -19,6 +19,7 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ +# include # include "vvp_net.h" # include "schedule.h" diff --git a/vvp/lexor.lex b/vvp/lexor.lex index 1109205cd..db33b636b 100644 --- a/vvp/lexor.lex +++ b/vvp/lexor.lex @@ -171,6 +171,7 @@ static char* strdupnew(char const *str) ".part/v" { return K_PART_V; } ".part/v.s" { return K_PART_V_S; } ".port" { return K_PORT; } +".port_info" { return K_PORT_INFO; } ".reduce/and" { return K_REDUCE_AND; } ".reduce/or" { return K_REDUCE_OR; } ".reduce/xor" { return K_REDUCE_XOR; } @@ -262,6 +263,12 @@ static char* strdupnew(char const *str) yylval.text = strdup(yytext); assert(yylval.text); return T_SYMBOL; } + + "/INPUT" { return K_PORT_INPUT; } + "/OUTPUT" { return K_PORT_OUTPUT; } + "/INOUT" { return K_PORT_INOUT; } + "/MIXED" { return K_PORT_MIXED; } + "/NODIR" { return K_PORT_NODIR; } /* Symbols are pretty much what is left. They are used to refer to labels so the rule must match a string that a label would match. */ diff --git a/vvp/main.cc b/vvp/main.cc index cb2f63d5b..9a3595d9a 100644 --- a/vvp/main.cc +++ b/vvp/main.cc @@ -284,6 +284,16 @@ int main(int argc, char*argv[]) vpip_module_path[0] = strdup(basepath); #endif + + if( ::getenv("VVP_WAIT_FOR_DEBUGGER") != 0 ) { + fprintf( stderr, "Waiting for debugger...\n"); + bool debugger_release = false; + while( !debugger_release ) { + sleep(1); + } + } + + /* For non-interactive runs we do not want to run the interactive * debugger, so make $stop just execute a $finish. */ stop_is_finish = false; diff --git a/vvp/parse.y b/vvp/parse.y index aff881aa2..846ec9e1a 100644 --- a/vvp/parse.y +++ b/vvp/parse.y @@ -68,6 +68,8 @@ static struct __vpiModPath*modpath_dst = 0; vpiHandle vpi; vvp_delay_t*cdelay; + + int vpi_enum; }; %token K_A K_ALIAS K_ALIAS_R K_APV @@ -84,7 +86,7 @@ static struct __vpiModPath*modpath_dst = 0; %token K_EXPORT K_EXTEND_S K_FUNCTOR K_IMPORT K_ISLAND K_MODPATH %token K_NET K_NET_S K_NET_R K_NET_2S K_NET_2U K_NET8 K_NET8_S %token K_PARAM_STR K_PARAM_L K_PARAM_REAL K_PART K_PART_PV -%token K_PART_V K_PART_V_S K_PORT K_PV K_REDUCE_AND K_REDUCE_OR K_REDUCE_XOR +%token K_PART_V K_PART_V_S K_PORT K_PORT_INFO K_PV K_REDUCE_AND K_REDUCE_OR K_REDUCE_XOR %token K_REDUCE_NAND K_REDUCE_NOR K_REDUCE_XNOR K_REPEAT %token K_RESOLV K_SCOPE K_SFUNC K_SFUNC_E K_SHIFTL K_SHIFTR K_SHIFTRS %token K_THREAD K_TIMESCALE K_TRAN K_TRANIF0 K_TRANIF1 K_TRANVP @@ -95,6 +97,7 @@ static struct __vpiModPath*modpath_dst = 0; %token K_disable K_fork %token K_ivl_version K_ivl_delay_selection %token K_vpi_module K_vpi_time_precision K_file_names K_file_line +%token K_PORT_INPUT K_PORT_OUTPUT K_PORT_INOUT K_PORT_MIXED K_PORT_NODIR %token T_INSTR %token T_LABEL @@ -104,6 +107,7 @@ static struct __vpiModPath*modpath_dst = 0; %token T_VECTOR %type local_flag +%type port_type %type signed_t_number %type symbol symbol_opt %type symbols symbols_net @@ -628,6 +632,7 @@ statement T_NUMBER T_NUMBER T_NUMBER ',' T_SYMBOL ';' { compile_scope_decl($1, $3, $5, $6, $14, $7, $8, $10, $11, $12); } + /* Legacy declaration that does not have `celldefine information. */ | T_LABEL K_SCOPE T_SYMBOL ',' T_STRING T_STRING T_NUMBER T_NUMBER ',' @@ -649,6 +654,10 @@ statement { compile_scope_recall($2); } + /* Port information for scopes... currently this is just meta-data for VPI queries */ + | K_PORT_INFO T_NUMBER port_type T_NUMBER T_STRING + { compile_port_info( $2 /* port_index */, $3, $4 /* width */, $5 /*&name */ ); } + | K_TIMESCALE T_NUMBER T_NUMBER';' { compile_timescale($2, $3); } | K_TIMESCALE '-' T_NUMBER T_NUMBER';' @@ -758,17 +767,17 @@ statement /* Parameter statements come in a few simple forms. The most basic is the string parameter. */ - | T_LABEL K_PARAM_STR T_STRING T_NUMBER T_NUMBER',' T_STRING ';' - { compile_param_string($1, $3, $7, $4, $5); } + | T_LABEL K_PARAM_STR T_STRING T_NUMBER T_NUMBER T_NUMBER',' T_STRING ';' + { compile_param_string($1, $3, $8, $4, $5, $6); } - | T_LABEL K_PARAM_L T_STRING T_NUMBER T_NUMBER',' T_SYMBOL ';' - { compile_param_logic($1, $3, $7, false, $4, $5); } + | T_LABEL K_PARAM_L T_STRING T_NUMBER T_NUMBER T_NUMBER',' T_SYMBOL ';' + { compile_param_logic($1, $3, $8, false, $4, $5, $6); } - | T_LABEL K_PARAM_L T_STRING T_NUMBER T_NUMBER',' '+' T_SYMBOL ';' - { compile_param_logic($1, $3, $8, true, $4, $5); } + | T_LABEL K_PARAM_L T_STRING T_NUMBER T_NUMBER T_NUMBER',' '+' T_SYMBOL ';' + { compile_param_logic($1, $3, $9, true, $4, $5, $6 ); } - | T_LABEL K_PARAM_REAL T_STRING T_NUMBER T_NUMBER',' T_SYMBOL ';' - { compile_param_real($1, $3, $7, $4, $5); } + | T_LABEL K_PARAM_REAL T_STRING T_NUMBER T_NUMBER T_NUMBER',' T_SYMBOL ';' + { compile_param_real($1, $3, $8, $4, $5, $6); } /* Islands */ @@ -1049,6 +1058,14 @@ modpath_src_list | modpath_src_list ',' modpath_src ; +port_type + : K_PORT_INPUT { $$ = vpiInput; } + | K_PORT_OUTPUT { $$ = vpiOutput; } + | K_PORT_INOUT { $$ = vpiInout; } + | K_PORT_MIXED { $$ = vpiMixedIO; } + | K_PORT_NODIR { $$ = vpiNoDirection; } + ; + modpath_src : symbol '(' numbers ')' symbol { compile_modpath_src(modpath_dst, 0, $1, $3, 0, $5, false); } diff --git a/vvp/parse_misc.h b/vvp/parse_misc.h index 3ac77505d..33caa8730 100644 --- a/vvp/parse_misc.h +++ b/vvp/parse_misc.h @@ -86,6 +86,7 @@ struct argv_s { char **syms; }; + extern void argv_init(struct argv_s*obj); extern void argv_add(struct argv_s*obj, vpiHandle); extern void argv_sym_add(struct argv_s*obj, char *); diff --git a/vvp/schedule.cc b/vvp/schedule.cc index 83d627ae2..220cc0149 100644 --- a/vvp/schedule.cc +++ b/vvp/schedule.cc @@ -63,7 +63,7 @@ struct event_s { void event_s::single_step_display(void) { - cerr << "event_s: Step into event " << typeid(*this).name() << endl; + std::cerr << "event_s: Step into event " << typeid(*this).name() << std::endl; } struct event_time_s { diff --git a/vvp/vpi_const.cc b/vvp/vpi_const.cc index f7ca58757..03fbb728d 100644 --- a/vvp/vpi_const.cc +++ b/vvp/vpi_const.cc @@ -258,6 +258,7 @@ class __vpiStringParam : public __vpiStringConst { vpiHandle vpi_handle(int code); struct __vpiScope* scope; + bool local_flag; unsigned file_idx; unsigned lineno; private: @@ -280,10 +281,16 @@ int __vpiStringParam::get_type_code(void) const int __vpiStringParam::vpi_get(int code) { - if (code == vpiLineNo) - return lineno; + switch (code) { + case vpiLineNo : + return lineno; - return __vpiStringConst::vpi_get(code); + case vpiLocalParam : + return local_flag; + + default : + return __vpiStringConst::vpi_get(code); + } } @@ -312,10 +319,11 @@ vpiHandle __vpiStringParam::vpi_handle(int code) } vpiHandle vpip_make_string_param(char*name, char*text, - long file_idx, long lineno) + bool local_flag, long file_idx, long lineno) { __vpiStringParam*obj = new __vpiStringParam(text, name); obj->scope = vpip_peek_current_scope(); + obj->local_flag = local_flag; obj->file_idx = (unsigned) file_idx; obj->lineno = (unsigned) lineno; @@ -449,6 +457,7 @@ struct __vpiBinaryParam : public __vpiBinaryConst { struct __vpiScope*scope; unsigned file_idx; unsigned lineno; + bool local_flag; private: char*basename_; }; @@ -469,10 +478,16 @@ int __vpiBinaryParam::get_type_code(void) const int __vpiBinaryParam::vpi_get(int code) { - if (code == vpiLineNo) - return lineno; + switch (code) { + case vpiLineNo : + return lineno; - return __vpiBinaryConst::vpi_get(code); + case vpiLocalParam : + return local_flag; + + default : + return __vpiBinaryConst::vpi_get(code); + } } char*__vpiBinaryParam::vpi_get_str(int code) @@ -500,13 +515,14 @@ vpiHandle __vpiBinaryParam::vpi_handle(int code) vpiHandle vpip_make_binary_param(char*name, const vvp_vector4_t&bits, - bool signed_flag, + bool signed_flag, bool local_flag, long file_idx, long lineno) { struct __vpiBinaryParam*obj = new __vpiBinaryParam(bits, name); obj->signed_flag = signed_flag? 1 : 0; obj->sized_flag = 0; + obj->local_flag = local_flag; obj->scope = vpip_peek_current_scope(); obj->file_idx = (unsigned) file_idx; obj->lineno = (unsigned) lineno; @@ -662,6 +678,7 @@ struct __vpiRealParam : public __vpiRealConst { vpiHandle vpi_handle(int code); struct __vpiScope* scope; + bool local_flag; unsigned file_idx; unsigned lineno; private: @@ -686,10 +703,16 @@ int __vpiRealParam::get_type_code(void) const int __vpiRealParam::vpi_get(int code) { - if (code == vpiLineNo) - return lineno; + switch (code) { + case vpiLineNo : + return lineno; - return __vpiRealConst::vpi_get(code); + case vpiLocalParam : + return local_flag; + + default : + return __vpiRealConst::vpi_get(code); + } } char* __vpiRealParam::vpi_get_str(int code) @@ -716,11 +739,12 @@ vpiHandle __vpiRealParam::vpi_handle(int code) vpiHandle vpip_make_real_param(char*name, double value, - long file_idx, long lineno) + bool local_flag, long file_idx, long lineno) { struct __vpiRealParam*obj = new __vpiRealParam(value, name); obj->scope = vpip_peek_current_scope(); + obj->local_flag = local_flag; obj->file_idx = (unsigned) file_idx; obj->lineno = (unsigned) lineno; diff --git a/vvp/vpi_priv.cc b/vvp/vpi_priv.cc index 309993783..5d6c8b96b 100644 --- a/vvp/vpi_priv.cc +++ b/vvp/vpi_priv.cc @@ -322,6 +322,10 @@ static const char* vpi_type_values(PLI_INT32 code) return "vpiNamedEvent"; case vpiNamedFork: return "vpiNamedFork"; + case vpiPathTerm: + return "vpiPathTerm"; + case vpiPort: + return "vpiPort"; case vpiNet: return "vpiNet"; case vpiNetArray: @@ -541,8 +545,7 @@ void vpi_set_vlog_info(int argc, char** argv) vpi_vlog_info.argv = argv; static char trace_buf[1024]; - - if (const char*path = getenv("VPI_TRACE")) { + if (const char*path = getenv("VPI_TRACE")) { if (!strcmp(path,"-")) vpi_trace = stdout; else { diff --git a/vvp/vpi_priv.h b/vvp/vpi_priv.h index 2f4237957..332a90d04 100644 --- a/vvp/vpi_priv.h +++ b/vvp/vpi_priv.h @@ -200,6 +200,10 @@ struct __vpiScopedRealtime : public __vpiSystemTime { void vpi_get_value(p_vpi_value val); }; +struct __vpiPortInfo : public __vpiHandle { + +}; + /* * Scopes are created by .scope statements in the source. These @@ -536,7 +540,7 @@ extern struct __vpiSysTaskCall*vpip_cur_task; */ vpiHandle vpip_make_string_const(char*text, bool persistent =true); -vpiHandle vpip_make_string_param(char*name, char*value, +vpiHandle vpip_make_string_param(char*name, char*value, bool local_flag, long file_idx, long lineno); struct __vpiBinaryConst : public __vpiHandle { @@ -554,7 +558,7 @@ struct __vpiBinaryConst : public __vpiHandle { vpiHandle vpip_make_binary_const(unsigned wid, const char*bits); vpiHandle vpip_make_binary_param(char*name, const vvp_vector4_t&bits, - bool signed_flag, + bool signed_flag, bool local_flag, long file_idx, long lineno); struct __vpiDecConst : public __vpiHandle { @@ -577,7 +581,7 @@ class __vpiRealConst : public __vpiHandle { }; vpiHandle vpip_make_real_const(double value); -vpiHandle vpip_make_real_param(char*name, double value, +vpiHandle vpip_make_real_param(char*name, double value, bool local_flag, long file_idx, long lineno); /* diff --git a/vvp/vpi_scope.cc b/vvp/vpi_scope.cc index a93e2aa0a..3e06ae5d2 100644 --- a/vvp/vpi_scope.cc +++ b/vvp/vpi_scope.cc @@ -30,6 +30,7 @@ # include # include "ivl_alloc.h" + static vpiHandle *vpip_root_table_ptr = 0; static unsigned vpip_root_table_cnt = 0; @@ -525,3 +526,96 @@ unsigned vpip_add_item_to_context(automatic_hooks_s*item, /* Offset the context index by 2 to leave space for the list links. */ return 2 + idx; } + + +struct vpiPortInfo : public __vpiHandle { + vpiPortInfo( __vpiScope *parent, + unsigned index, + int vpi_direction, + unsigned width, + const char *name ); + + + int get_type_code(void) const { return vpiPort; } + + int vpi_get(int code); + char* vpi_get_str(int code); + vpiHandle vpi_handle(int code); + +protected: + __vpiScope *parent_; + unsigned index_; + int direction_; + unsigned width_; + const char *name_; +}; + +vpiPortInfo::vpiPortInfo( __vpiScope *parent, + unsigned index, + int vpi_direction, + unsigned width, + const char *name ) : + parent_(parent), + index_(index), + direction_(vpi_direction), + width_(width), + name_(name) +{ +} + +int vpiPortInfo::vpi_get(int code) +{ + switch( code ) { + + case vpiDirection : + return direction_; + case vpiPortIndex : + return index_; + case vpiSize : + return width_; + default : + return vpiUndefined; + } + +} + + +char *vpiPortInfo::vpi_get_str(int code) +{ + switch( code ) { + case vpiName : + return simple_set_rbuf_str(name_); + default : + return NULL; + } + +} + + +vpiHandle vpiPortInfo::vpi_handle(int code) +{ + + switch (code) { + + case vpiParent: + case vpiScope: + case vpiModule: + return parent_; + default : + break; + } + + return 0; +} + + +/* Port info is meta-data to allow vpi queries of the port signature of modules for + * code-generators etc. There are no actual nets corresponding to instances of module ports + * as elaboration directly connects nets connected through module ports. + */ +void compile_port_info( unsigned index, int vpi_direction, unsigned width, const char *name ) +{ + vpiHandle obj = new vpiPortInfo( vpip_peek_current_scope(), + index, vpi_direction, width, name ); + vpip_attach_to_current_scope(obj); +} diff --git a/vvp/words.cc b/vvp/words.cc index 2cfdcd3dc..895f331f6 100644 --- a/vvp/words.cc +++ b/vvp/words.cc @@ -141,6 +141,7 @@ void compile_variable(char*label, char*name, delete[] name; } + vvp_net_t* create_constant_node(const char*val_str) { if (c4string_test(val_str)) { From c46823e280fc16283d0cd521d852565d9a9d24b8 Mon Sep 17 00:00:00 2001 From: Cary R Date: Thu, 7 Jun 2012 09:35:58 -0700 Subject: [PATCH 050/179] Update ivl.def to support new ivl interface functions --- ivl.def | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ivl.def b/ivl.def index 603c116e8..dc023a174 100644 --- a/ivl.def +++ b/ivl.def @@ -166,6 +166,7 @@ ivl_parameter_basename ivl_parameter_expr ivl_parameter_file ivl_parameter_lineno +ivl_parameter_local ivl_path_condit ivl_path_delay @@ -194,6 +195,10 @@ ivl_scope_logs ivl_scope_log ivl_scope_lpms ivl_scope_lpm +ivl_scope_mod_module_ports +ivl_scope_mod_module_port_name +ivl_scope_mod_module_port_type +ivl_scope_mod_module_port_width ivl_scope_mod_port ivl_scope_name ivl_scope_param From f36faa8ab3b6359f02215d731412a8274966bdd4 Mon Sep 17 00:00:00 2001 From: Cary R Date: Thu, 7 Jun 2012 09:59:54 -0700 Subject: [PATCH 051/179] Mingw needs to use Sleep vs sleep (also seconds go to ms) --- main.cc | 6 +++++- vvp/main.cc | 8 ++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/main.cc b/main.cc index 2218ff872..b2b643db7 100644 --- a/main.cc +++ b/main.cc @@ -1,5 +1,5 @@ const char COPYRIGHT[] = - "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 @@ -788,7 +788,11 @@ int main(int argc, char*argv[]) fprintf( stderr, "Waiting for debugger...\n"); bool debugger_release = false; while( !debugger_release ) { +#if defined(__MINGW32__) + Sleep(1000); +#else sleep(1); +#endif } } library_suff.push_back(strdup(".v")); diff --git a/vvp/main.cc b/vvp/main.cc index 9a3595d9a..3ce61f908 100644 --- a/vvp/main.cc +++ b/vvp/main.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001-2011 Stephen Williams (steve@icarus.com) + * Copyright (c) 2001-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 @@ -289,7 +289,11 @@ int main(int argc, char*argv[]) fprintf( stderr, "Waiting for debugger...\n"); bool debugger_release = false; while( !debugger_release ) { +#if defined(__MINGW32__) + Sleep(1000); +#else sleep(1); +#endif } } @@ -353,7 +357,7 @@ int main(int argc, char*argv[]) if (version_flag) { fprintf(stderr, "Icarus Verilog runtime version " VERSION " (" VERSION_TAG ")\n\n"); - fprintf(stderr, "Copyright 1998-2010 Stephen Williams\n\n"); + fprintf(stderr, "Copyright 1998-2012 Stephen Williams\n\n"); fprintf(stderr, " This program is free software; you can redistribute it and/or modify\n" " it under the terms of the GNU General Public License as published by\n" From 0d0e6d7845a916648d0639cf9fff6b7db8e1bace Mon Sep 17 00:00:00 2001 From: Martin Whitaker Date: Mon, 11 Jun 2012 19:48:37 +0100 Subject: [PATCH 052/179] Fix elaboration order in generate blocks. Currently, localparam declarations inside generate blocks are elaborated after any nested generate constructs are elaborated. This prevents the localparams being used by the nested constructs. Reversing the elaboration order fixes this bug. --- elab_scope.cc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/elab_scope.cc b/elab_scope.cc index 617e8bac8..23666eb40 100644 --- a/elab_scope.cc +++ b/elab_scope.cc @@ -1091,6 +1091,12 @@ void PGenerate::elaborate_subscope_(Design*des, NetScope*scope) scope->add_genvar((*cur).first, (*cur).second); } + // Scan the localparams in this scope, and store the information + // needed to evaluate the parameter expressions. The expressions + // will be evaluated later, once all parameter overrides for this + // module have been done. + collect_scope_localparams_(des, scope, localparams); + // Scan the generated scope for nested generate schemes, // and *generate* new scopes, which is slightly different // from simple elaboration. @@ -1101,12 +1107,6 @@ void PGenerate::elaborate_subscope_(Design*des, NetScope*scope) (*cur) -> generate_scope(des, scope); } - // Scan the localparams in this scope, and store the information - // needed to evaluate the parameter expressions. The expressions - // will be evaluated later, once all parameter overrides for this - // module have been done. - collect_scope_localparams_(des, scope, localparams); - // Scan through all the task and function declarations in this // scope. elaborate_scope_tasks(des, scope, tasks); From bef59e0b6c8deed0b6864a30bce434e1764061c0 Mon Sep 17 00:00:00 2001 From: Martin Whitaker Date: Sat, 16 Jun 2012 23:00:19 +0100 Subject: [PATCH 053/179] Fix for pr3534333. The parser had been changed to support null statements in sequential and parallel blocks (a feature introduced in SystemVerilog), but was not supported in elaboration, leading to a compiler crash. This patch fixes this by discarding the null statements during parsing. --- parse.y | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/parse.y b/parse.y index 27c8d7e5a..0a0a1b744 100644 --- a/parse.y +++ b/parse.y @@ -249,9 +249,12 @@ static void current_task_set_statement(const YYLTYPE&loc, vector*s) current_task->set_statement(tmp); return; } + assert(s); - /* The parser assures that there is a non-empty vector. */ - assert(s && !s->empty()); + /* An empty vector represents one or more null statements. Handle + this as a simple null statement. */ + if (s->empty()) + return; /* A vector of 1 is handled as a simple statement. */ if (s->size() == 1) { @@ -284,9 +287,12 @@ static void current_function_set_statement(const YYLTYPE&loc, vector current_function->set_statement(tmp); return; } + assert(s); - /* The parser assures that there is a non-empty vector. */ - assert(s && !s->empty()); + /* An empty vector represents one or more null statements. Handle + this as a simple null statement. */ + if (s->empty()) + return; /* A vector of 1 is handled as a simple statement. */ if (s->size() == 1) { @@ -5699,12 +5705,12 @@ statement_or_null_list_opt statement_or_null_list : statement_or_null_list statement_or_null { vector*tmp = $1; - tmp->push_back($2); + if ($2) tmp->push_back($2); $$ = tmp; } | statement_or_null - { vector*tmp = new vector(1); - tmp->at(0) = $1; + { vector*tmp = new vector(0); + if ($1) tmp->push_back($1); $$ = tmp; } ; From a35ca1d70b81879808a98439dee3b8bdc11b8288 Mon Sep 17 00:00:00 2001 From: Cary R Date: Mon, 9 Jul 2012 10:29:50 -0700 Subject: [PATCH 054/179] Remove debug message in vvp/vpi_signal.cc --- vvp/vpi_signal.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/vvp/vpi_signal.cc b/vvp/vpi_signal.cc index 3478fed40..00038d31c 100644 --- a/vvp/vpi_signal.cc +++ b/vvp/vpi_signal.cc @@ -1292,7 +1292,6 @@ static vpiHandle PV_put_value(vpiHandle ref, p_vpi_value vp, int) vvp_vector4_t val = vec4_from_vpi_value(vp, width); - fprintf(stderr, "XXXX PV_put_value(..)\n"); /* * If the base is less than zero then trim off any unneeded * lower bits. From ea420d94acc0fb5264a3a72e32234724bd3fd985 Mon Sep 17 00:00:00 2001 From: Cary R Date: Mon, 9 Jul 2012 11:40:38 -0700 Subject: [PATCH 055/179] Fix for pr3539372. This patch matches what was proposed in pr3539372 to fix ivlpp to correctly handle an escapped \ at the end of a string. --- ivlpp/lexor.lex | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ivlpp/lexor.lex b/ivlpp/lexor.lex index 348f78ba3..39d9e894b 100644 --- a/ivlpp/lexor.lex +++ b/ivlpp/lexor.lex @@ -1,7 +1,7 @@ %option prefix="yy" %{ /* - * Copyright (c) 1999-2010 Stephen Williams (steve@icarus.com) + * Copyright (c) 1999-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 @@ -260,6 +260,7 @@ keywords (include|define|undef|ifdef|ifndef|else|elseif|endif) * string. */ \" { string_enter = YY_START; BEGIN(CSTRING); ECHO; } +\\\\ | \\\" | \\` { ECHO; } \r\n | From d48362b861b76f13de5548227dc5f7a2719e68b0 Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Sun, 17 Jun 2012 18:22:50 -0700 Subject: [PATCH 056/179] First pass at getting strings to work. In vvp, create the .var/str variable for representing strings, and handle strings in the $display system task. Add to vvp threads the concept of a stack of strings. This is going to be how complex objects are to me handled in the future: forth-like operation stacks. Also add the first two instructions to minimally get strings to work. In the parser, handle the variable declaration and make it available to the ivl_target.h code generator. The vvp code generator can use this information to generate the code for new vvp support. --- Makefile.in | 4 +- parse.y | 5 +- pform.cc | 5 ++ pform.h | 2 + pform_string_type.cc | 37 +++++++++++ pform_types.cc | 4 ++ pform_types.h | 5 ++ sv_vpi_user.h | 1 + tgt-stub/stub.c | 4 ++ tgt-vvp/stmt_assign.c | 32 ++++++++++ tgt-vvp/vvp_scope.c | 5 ++ vpi/sys_display.c | 11 ++++ vvp/Makefile.in | 2 +- vvp/README.txt | 1 + vvp/codes.h | 2 + vvp/compile.cc | 7 ++ vvp/compile.h | 4 +- vvp/lexor.lex | 1 + vvp/opcodes.txt | 8 +++ vvp/parse.y | 7 +- vvp/vpi_callback.cc | 16 +++++ vvp/vpi_priv.cc | 5 ++ vvp/vpi_priv.h | 18 ++++++ vvp/vpi_string.cc | 68 ++++++++++++++++++++ vvp/vthread.cc | 58 +++++++++++++++++ vvp/vvp_net.cc | 9 ++- vvp/vvp_net.h | 24 ++++++- vvp/vvp_net_sig.cc | 144 ++++++++++++++++++++++++++++++++++++++++++ vvp/vvp_net_sig.h | 88 ++++++++++++++++++++++++++ vvp/words.cc | 39 +++++++++++- 30 files changed, 603 insertions(+), 13 deletions(-) create mode 100644 pform_string_type.cc create mode 100644 vvp/vpi_string.cc diff --git a/Makefile.in b/Makefile.in index 26f2f13ef..ee1254316 100644 --- a/Makefile.in +++ b/Makefile.in @@ -113,8 +113,8 @@ O = main.o async.o design_dump.o discipline.o dup_expr.o elaborate.o \ net_link.o net_modulo.o \ net_nex_input.o net_nex_output.o net_proc.o net_scope.o net_tran.o \ net_udp.o pad_to_width.o parse.o parse_misc.o pform.o pform_analog.o \ - pform_disciplines.o pform_dump.o pform_pclass.o pform_struct_type.o \ - pform_types.o \ + pform_disciplines.o pform_dump.o pform_pclass.o pform_string_type.o \ + pform_struct_type.o pform_types.o \ symbol_search.o sync.o sys_funcs.o verinum.o verireal.o target.o \ Attrib.o HName.o Module.o PClass.o PDelays.o PEvent.o PExpr.o PGate.o \ PGenerate.o PScope.o PSpec.o PTask.o PUdp.o PFunction.o PWire.o \ diff --git a/parse.y b/parse.y index 0a0a1b744..39d97c5c5 100644 --- a/parse.y +++ b/parse.y @@ -898,8 +898,9 @@ data_type /* IEEE1800-2005: A.2.2.1 */ else $$ = $1; } | K_string - { yyerror(@1, "sorry: String data type not supported."); - $$ = 0; + { string_type_t*tmp = new string_type_t; + FILE_NAME(tmp, @1); + $$ = tmp; } ; diff --git a/pform.cc b/pform.cc index ee878a868..9223fe0eb 100644 --- a/pform.cc +++ b/pform.cc @@ -2829,6 +2829,11 @@ void pform_set_data_type(const struct vlltype&li, data_type_t*data_type, list (data_type)) { + pform_set_string_type(string_type, names, attr); + return; + } + assert(0); } diff --git a/pform.h b/pform.h index 23d45c558..12101259a 100644 --- a/pform.h +++ b/pform.h @@ -310,6 +310,8 @@ extern void pform_set_enum(const struct vlltype&li, enum_type_t*enum_type, list< extern void pform_set_struct_type(struct_type_t*struct_type, std::list*names, std::list*attr); +extern void pform_set_string_type(string_type_t*string_type, std::list*names, std::list*attr); + /* pform_set_attrib and pform_set_type_attrib exist to support the $attribute syntax, which can only set string values to attributes. The functions keep the value strings that are diff --git a/pform_string_type.cc b/pform_string_type.cc new file mode 100644 index 000000000..c7f626d63 --- /dev/null +++ b/pform_string_type.cc @@ -0,0 +1,37 @@ +/* + * Copyright (c) 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 + * General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +# include "pform.h" +# include "parse_misc.h" +# include "ivl_assert.h" + +static void pform_set_string_type(string_type_t*string_type, perm_string name, list*attr) +{ + PWire*net = pform_get_make_wire_in_scope(name, NetNet::REG, NetNet::NOT_A_PORT, IVL_VT_STRING); + pform_bind_attributes(net->attributes, attr, true); +} + +void pform_set_string_type(string_type_t*string_type, list*names, list*attr) +{ + for (list::iterator cur = names->begin() + ; cur != names->end() ; ++ cur) { + pform_set_string_type(string_type, *cur, attr); + } +} + diff --git a/pform_types.cc b/pform_types.cc index 9a85b5b7e..7650ec36e 100644 --- a/pform_types.cc +++ b/pform_types.cc @@ -23,3 +23,7 @@ data_type_t::~data_type_t() { } + +string_type_t::~string_type_t() +{ +} diff --git a/pform_types.h b/pform_types.h index cfd56e5c3..b55d31013 100644 --- a/pform_types.h +++ b/pform_types.h @@ -156,6 +156,11 @@ struct real_type_t : public data_type_t { type_t type_code; }; +struct string_type_t : public data_type_t { + inline explicit string_type_t() { } + ~string_type_t(); +}; + struct class_type_t : public data_type_t { inline explicit class_type_t(perm_string n) : name(n) { } diff --git a/sv_vpi_user.h b/sv_vpi_user.h index 606f7f73f..0bc5ca22f 100644 --- a/sv_vpi_user.h +++ b/sv_vpi_user.h @@ -48,6 +48,7 @@ EXTERN_C_START #define vpiIntVar 612 #define vpiByteVar 614 #define vpiLogicVar vpiReg +#define vpiStringVar 616 #define vpiBitVar 620 /********* TYPESPECS *************/ diff --git a/tgt-stub/stub.c b/tgt-stub/stub.c index 3cddd2ab6..4180a977e 100644 --- a/tgt-stub/stub.c +++ b/tgt-stub/stub.c @@ -1274,6 +1274,10 @@ static void show_signal(ivl_signal_t net) data_type = "real"; break; + case IVL_VT_STRING: + data_type = "string"; + break; + default: data_type = "?data?"; break; diff --git a/tgt-vvp/stmt_assign.c b/tgt-vvp/stmt_assign.c index 13f2e2391..242fef33d 100644 --- a/tgt-vvp/stmt_assign.c +++ b/tgt-vvp/stmt_assign.c @@ -731,6 +731,34 @@ static int show_stmt_assign_sig_real(ivl_statement_t net) return 0; } +static int show_stmt_assign_sig_string(ivl_statement_t net) +{ + struct vector_info res; + ivl_lval_t lval = ivl_stmt_lval(net, 0); + ivl_expr_t rval = ivl_stmt_rval(net); + ivl_signal_t var; + assert(ivl_stmt_lvals(net) == 1); + assert(ivl_stmt_opcode(net) == 0); + + var = ivl_lval_sig(lval); + + switch (ivl_expr_value(rval)) { + case IVL_VT_BOOL: + case IVL_VT_LOGIC: + res = draw_eval_expr(rval, 0); + fprintf(vvp_out, " %%pushv/str %u, %u;\n", + res.base, res.wid); + fprintf(vvp_out, " %%store/str v%p_0;\n", var); + if (res.base > 0) + clr_vector(res); + break; + default: + assert(0); + break; + } + + return 0; +} int show_stmt_assign(ivl_statement_t net) { @@ -746,5 +774,9 @@ int show_stmt_assign(ivl_statement_t net) return show_stmt_assign_sig_real(net); } + if (sig && (ivl_signal_data_type(sig) == IVL_VT_STRING)) { + return show_stmt_assign_sig_string(net); + } + return show_stmt_assign_vector(net); } diff --git a/tgt-vvp/vvp_scope.c b/tgt-vvp/vvp_scope.c index c927ed932..554403436 100644 --- a/tgt-vvp/vvp_scope.c +++ b/tgt-vvp/vvp_scope.c @@ -502,6 +502,11 @@ static void draw_reg_in_scope(ivl_signal_t sig) vvp_mangle_name(ivl_signal_basename(sig)), swapped ? first: last, swapped ? last : first, msb, lsb); + } else if (ivl_signal_data_type(sig) == IVL_VT_STRING) { + fprintf(vvp_out, "v%p_0 .var/str \"%s\";%s\n", sig, + vvp_mangle_name(ivl_signal_basename(sig)), + ivl_signal_local(sig)? " Local signal" : ""); + } else { fprintf(vvp_out, "v%p_0 .var%s %s\"%s\", %d %d;%s\n", diff --git a/vpi/sys_display.c b/vpi/sys_display.c index d5fdb4234..1dfe1b19e 100644 --- a/vpi/sys_display.c +++ b/vpi/sys_display.c @@ -964,6 +964,16 @@ static char *get_display(unsigned int *rtnsz, const struct strobe_cb_info *info) memcpy(rtn+size-1, buf, width); break; + /* Process string variables like string constants: interpret + the contained strings like format strings. */ + case vpiStringVar: + value.format = vpiStringVal; + vpi_get_value(item, &value); + width = strlen(value.value.str); + rtn = realloc(rtn, (size+width)*sizeof(char)); + memcpy(rtn+size-1, value.value.str, width); + break; + case vpiSysFuncCall: func_name = vpi_get_str(vpiName, item); if (strcmp(func_name, "$time") == 0) { @@ -1071,6 +1081,7 @@ static int sys_check_args(vpiHandle callh, vpiHandle argv, const PLI_BYTE8*name, case vpiLongIntVar: case vpiTimeVar: case vpiRealVar: + case vpiStringVar: case vpiSysFuncCall: break; diff --git a/vvp/Makefile.in b/vvp/Makefile.in index 4f3a59a80..0aed0f35b 100644 --- a/vvp/Makefile.in +++ b/vvp/Makefile.in @@ -67,7 +67,7 @@ dllib=@DLLIB@ MDIR1 = -DMODULE_DIR1='"$(libdir)/ivl$(suffix)"' V = vpi_modules.o vpi_callback.o vpi_const.o vpi_event.o vpi_iter.o vpi_mcd.o \ - vpi_priv.o vpi_scope.o vpi_real.o vpi_signal.o vpi_tasks.o vpi_time.o \ + vpi_priv.o vpi_scope.o vpi_real.o vpi_signal.o vpi_string.o vpi_tasks.o vpi_time.o \ vpi_vthr_vector.o vpip_bin.o vpip_hex.o vpip_oct.o \ vpip_to_dec.o vpip_format.o vvp_vpi.o diff --git a/vvp/README.txt b/vvp/README.txt index a45854ded..329902f6a 100644 --- a/vvp/README.txt +++ b/vvp/README.txt @@ -284,6 +284,7 @@ general syntax of a variable is: