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.
This commit is contained in:
Stephen Williams 2012-05-09 19:35:11 -07:00
parent 580c44c015
commit dfe7beec31
18 changed files with 218 additions and 148 deletions

View File

@ -27,8 +27,8 @@
list<Module::named_expr_t> Module::user_defparms; list<Module::named_expr_t> Module::user_defparms;
/* n is a permallocated string. */ /* n is a permallocated string. */
Module::Module(perm_string n) Module::Module(LexicalScope*parent, perm_string n)
: PScopeExtra(n) : PScopeExtra(n, parent)
{ {
library_flag = false; library_flag = false;
is_cell = false; is_cell = false;

View File

@ -65,7 +65,7 @@ class Module : public PScopeExtra, public LineInfo {
public: public:
/* The name passed here is the module name, not the instance /* The name passed here is the module name, not the instance
name. This make must be a permallocated string. */ name. This make must be a permallocated string. */
explicit Module(perm_string name); explicit Module(LexicalScope*parent, perm_string name);
~Module(); ~Module();
/* Initially false. This is set to true if the module has been /* 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. */ the module definition. These are used at elaboration time. */
list<PGenerate*> generate_schemes; list<PGenerate*> generate_schemes;
/* Nested modules are placed here, and are not elaborated
unless they are instantiated, implicitly or explicitly. */
std::map<perm_string,Module*> nested_modules;
list<PSpecPath*> specify_paths; list<PSpecPath*> specify_paths;
// The mod_name() is the name of the module type. // The mod_name() is the name of the module type.

View File

@ -260,7 +260,7 @@ const char* PGBuiltin::gate_name() const
} }
PGModule::PGModule(perm_string type, perm_string name, list<PExpr*>*pins) PGModule::PGModule(perm_string type, perm_string name, list<PExpr*>*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) npins_(0), parms_(0), nparms_(0), msb_(0), lsb_(0)
{ {
type_ = type; type_ = type;
@ -268,12 +268,18 @@ PGModule::PGModule(perm_string type, perm_string name, list<PExpr*>*pins)
PGModule::PGModule(perm_string type, perm_string name, PGModule::PGModule(perm_string type, perm_string name,
named<PExpr*>*pins, unsigned npins) named<PExpr*>*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) npins_(npins), parms_(0), nparms_(0), msb_(0), lsb_(0)
{ {
type_ = type; 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() PGModule::~PGModule()
{ {
} }

View File

@ -201,6 +201,9 @@ class PGModule : public PGate {
explicit PGModule(perm_string type, perm_string name, explicit PGModule(perm_string type, perm_string name,
named<PExpr*>*pins, unsigned npins); named<PExpr*>*pins, unsigned npins);
// If the module type is known by design, then use this
// constructor.
explicit PGModule(Module*type, perm_string name);
~PGModule(); ~PGModule();
@ -223,6 +226,7 @@ class PGModule : public PGate {
perm_string get_type() const; perm_string get_type() const;
private: private:
Module*bound_type_;
perm_string type_; perm_string type_;
list<PExpr*>*overrides_; list<PExpr*>*overrides_;
named<PExpr*>*pins_; named<PExpr*>*pins_;

View File

@ -177,6 +177,11 @@ static inline bool gn_system_verilog(void)
return false; return false;
} }
static inline bool gn_modules_nest(void)
{
return gn_system_verilog();
}
/* The bits of these GN_KEYWORDS_* constants define non-intersecting /* The bits of these GN_KEYWORDS_* constants define non-intersecting
sets of keywords. The compiler enables groups of keywords by setting 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 lexor_keyword_mask with the OR of the bits for the keywords to be

View File

@ -693,7 +693,7 @@ NetNet* PEIdent::elaborate_bi_net(Design*des, NetScope*scope) const
*/ */
NetNet* PEIdent::elaborate_port(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_); NetNet*sig = des->find_signal(scope, path_);
if (sig == 0) { if (sig == 0) {
cerr << get_fileline() << ": error: no wire/reg " << path_ cerr << get_fileline() << ": error: no wire/reg " << path_

View File

@ -541,6 +541,19 @@ bool Module::elaborate_scope(Design*des, NetScope*scope,
elaborate_scope_funcs(des, scope, funcs); elaborate_scope_funcs(des, scope, funcs);
// Look for implicit modules and implicit gates for them.
for (map<perm_string,Module*>::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 // Gates include modules, which might introduce new scopes, so
// scan all of them to create those scopes. // 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; continue;
} }
if (scn->type() != NetScope::MODULE) continue; if (! scn->type_is_module()) continue;
if (strcmp(mod->mod_name(), scn->module_name()) != 0) 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; << "." << endl;
} }
// Create the new scope as a MODULE with my name. // Create the new scope as a MODULE with my name. Note
NetScope*my_scope = new NetScope(sc, use_name, NetScope::MODULE); // 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(), my_scope->set_line(get_file(), mod->get_file(),
get_lineno(), mod->get_lineno()); get_lineno(), mod->get_lineno());
my_scope->set_module_name(mod->mod_name()); my_scope->set_module_name(mod->mod_name());

View File

@ -98,7 +98,7 @@ bool PScope::elaborate_sig_wires_(Design*des, NetScope*scope) const
reg, then report an error. */ reg, then report an error. */
if (sig && (sig->scope() == scope) if (sig && (sig->scope() == scope)
&& (scope->type() == NetScope::MODULE) && (scope->type_is_module())
&& (sig->port_type() == NetNet::PINPUT) && (sig->port_type() == NetNet::PINPUT)
&& (sig->type() == NetNet::REG)) { && (sig->type() == NetNet::REG)) {
@ -110,7 +110,7 @@ bool PScope::elaborate_sig_wires_(Design*des, NetScope*scope) const
} }
if (sig && (sig->scope() == scope) if (sig && (sig->scope() == scope)
&& (scope->type() == NetScope::MODULE) && (scope->type_is_module())
&& (sig->port_type() == NetNet::PINOUT) && (sig->port_type() == NetNet::PINOUT)
&& (sig->type() == NetNet::REG)) { && (sig->type() == NetNet::REG)) {
@ -122,7 +122,7 @@ bool PScope::elaborate_sig_wires_(Design*des, NetScope*scope) const
} }
if (sig && (sig->scope() == scope) if (sig && (sig->scope() == scope)
&& (scope->type() == NetScope::MODULE) && scope->type_is_module()
&& (sig->port_type() == NetNet::PINOUT) && (sig->port_type() == NetNet::PINOUT)
&& (sig->data_type() == IVL_VT_REAL)) { && (sig->data_type() == IVL_VT_REAL)) {

View File

@ -2068,6 +2068,10 @@ void PGModule::elaborate_udp_(Design*des, PUdp*udp, NetScope*scope) const
bool PGModule::elaborate_sig(Design*des, 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 // Look for the module type
map<perm_string,Module*>::const_iterator mod = pform_modules.find(type_); map<perm_string,Module*>::const_iterator mod = pform_modules.find(type_);
if (mod != pform_modules.end()) 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 void PGModule::elaborate(Design*des, NetScope*scope) const
{ {
if (bound_type_) {
elaborate_mod_(des, bound_type_, scope);
return;
}
// Look for the module type // Look for the module type
map<perm_string,Module*>::const_iterator mod = pform_modules.find(type_); map<perm_string,Module*>::const_iterator mod = pform_modules.find(type_);
if (mod != pform_modules.end()) { 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 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 // Look for the module type
map<perm_string,Module*>::const_iterator mod = pform_modules.find(type_); map<perm_string,Module*>::const_iterator mod = pform_modules.find(type_);
if (mod != pform_modules.end()) { if (mod != pform_modules.end()) {
elaborate_scope_mod_(des, (*mod).second, sc); elaborate_scope_mod_(des, mod->second, sc);
return; return;
} }
@ -2128,7 +2143,7 @@ void PGModule::elaborate_scope(Design*des, NetScope*sc) const
// Try again to find the module type // Try again to find the module type
mod = pform_modules.find(type_); mod = pform_modules.find(type_);
if (mod != pform_modules.end()) { if (mod != pform_modules.end()) {
elaborate_scope_mod_(des, (*mod).second, sc); elaborate_scope_mod_(des, mod->second, sc);
return; return;
} }
@ -3241,6 +3256,7 @@ NetProc* PDisable::elaborate(Design*des, NetScope*scope) const
return 0; return 0;
case NetScope::MODULE: case NetScope::MODULE:
case NetScope::NESTED_MODULE:
cerr << get_fileline() << ": error: Cannot disable modules." << endl; cerr << get_fileline() << ": error: Cannot disable modules." << endl;
des->errors += 1; des->errors += 1;
return 0; return 0;

View File

@ -302,6 +302,9 @@ void NetScope::print_type(ostream&stream) const
case FUNC: case FUNC:
stream << "function"; stream << "function";
break; break;
case NESTED_MODULE:
stream << "nested_module <" << (module_name_ ? module_name_.str() : "")
<< "> instance";
case MODULE: case MODULE:
stream << "module <" << (module_name_ ? module_name_.str() : "") stream << "module <" << (module_name_ ? module_name_.str() : "")
<< "> instance"; << "> instance";
@ -360,31 +363,31 @@ const NetFuncDef* NetScope::func_def() const
void NetScope::set_module_name(perm_string n) 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. */ module_name_ = n; /* NOTE: n must have been permallocated. */
} }
perm_string NetScope::module_name() const perm_string NetScope::module_name() const
{ {
assert(type_ == MODULE); assert(type_ == MODULE || type_ == NESTED_MODULE);
return module_name_; return module_name_;
} }
void NetScope::add_module_port(NetNet*port) void NetScope::add_module_port(NetNet*port)
{ {
assert(type_ == MODULE); assert(type_ == MODULE || type_ == NESTED_MODULE);
ports_.push_back(port); ports_.push_back(port);
} }
unsigned NetScope::module_ports() const unsigned NetScope::module_ports() const
{ {
assert(type_ == MODULE); assert(type_ == MODULE || type_ == NESTED_MODULE);
return ports_.size(); return ports_.size();
} }
NetNet* NetScope::module_port(unsigned idx) const NetNet* NetScope::module_port(unsigned idx) const
{ {
assert(type_ == MODULE); assert(type_ == MODULE || type_ == NESTED_MODULE);
assert(idx < ports_.size()); assert(idx < ports_.size());
return ports_[idx]; return ports_[idx];
} }

View File

@ -724,7 +724,7 @@ extern std::ostream&operator << (std::ostream&out, const std::list<netrange_t>&r
class NetScope : public Attrib { class NetScope : public Attrib {
public: 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 /* Create a new scope, and attach it to the given parent. The
name is expected to have been permallocated. */ name is expected to have been permallocated. */
@ -806,6 +806,7 @@ class NetScope : public Attrib {
const NetScope* child(const hname_t&name) const; const NetScope* child(const hname_t&name) const;
TYPE type() const; TYPE type() const;
bool type_is_module() const { return type()==MODULE || type()==NESTED_MODULE; }
void print_type(ostream&) const; void print_type(ostream&) const;
void set_task_def(NetTaskDef*); void set_task_def(NetTaskDef*);

View File

@ -136,6 +136,7 @@ void nodangle_f::signal(Design*, NetNet*sig)
if ((sig->port_type() != NetNet::NOT_A_PORT) && if ((sig->port_type() != NetNet::NOT_A_PORT) &&
((sig->scope()->type() == NetScope::TASK) || ((sig->scope()->type() == NetScope::TASK) ||
(sig->scope()->type() == NetScope::FUNC) || (sig->scope()->type() == NetScope::FUNC) ||
(sig->scope()->type() == NetScope::NESTED_MODULE) ||
(sig->scope()->type() == NetScope::MODULE))) (sig->scope()->type() == NetScope::MODULE)))
return; return;

68
parse.y
View File

@ -3866,43 +3866,46 @@ module_parameter_port_list
module_item module_item
/* Modules can contain further sub-module definitions. */
: module
/* This rule detects net declarations that possibly include a /* This rule detects net declarations that possibly include a
primitive type, an optional vector range and signed flag. This primitive type, an optional vector range and signed flag. This
also includes an optional delay set. The values are then applied also includes an optional delay set. The values are then applied
to a list of names. If the primitive type is not specified, then to a list of names. If the primitive type is not specified, then
resort to the default type LOGIC. */ resort to the default type LOGIC. */
: attribute_list_opt net_type | attribute_list_opt net_type
primitive_type_opt unsigned_signed_opt range_opt primitive_type_opt unsigned_signed_opt range_opt
delay3_opt delay3_opt
net_variable_list ';' net_variable_list ';'
{ ivl_variable_type_t dtype = $3; { ivl_variable_type_t dtype = $3;
if (dtype == IVL_VT_NO_TYPE) if (dtype == IVL_VT_NO_TYPE)
dtype = IVL_VT_LOGIC; dtype = IVL_VT_LOGIC;
pform_makewire(@2, $5, $4, $7, $2, pform_makewire(@2, $5, $4, $7, $2, NetNet::NOT_A_PORT, dtype, $1);
NetNet::NOT_A_PORT, dtype, $1); if ($6 != 0) {
if ($6 != 0) { yyerror(@6, "sorry: net delays not supported.");
yyerror(@6, "sorry: net delays not supported."); delete $6;
delete $6; }
} delete $1;
delete $1; }
}
| attribute_list_opt K_wreal delay3 net_variable_list ';' | attribute_list_opt K_wreal delay3 net_variable_list ';'
{ pform_makewire(@2, 0, true, $4, NetNet::WIRE, { pform_makewire(@2, 0, true, $4, NetNet::WIRE,
NetNet::NOT_A_PORT, IVL_VT_REAL, $1); NetNet::NOT_A_PORT, IVL_VT_REAL, $1);
if ($3 != 0) { if ($3 != 0) {
yyerror(@3, "sorry: net delays not supported."); yyerror(@3, "sorry: net delays not supported.");
delete $3; delete $3;
} }
delete $1; delete $1;
} }
| attribute_list_opt K_wreal net_variable_list ';'
{ pform_makewire(@2, 0, true, $3, NetNet::WIRE, | attribute_list_opt K_wreal net_variable_list ';'
NetNet::NOT_A_PORT, IVL_VT_REAL, $1); { pform_makewire(@2, 0, true, $3, NetNet::WIRE,
delete $1; NetNet::NOT_A_PORT, IVL_VT_REAL, $1);
} delete $1;
}
/* Very similar to the rule above, but this takes a list of /* Very similar to the rule above, but this takes a list of
net_decl_assigns, which are <name> = <expr> assignment net_decl_assigns, which are <name> = <expr> assignment
@ -4248,13 +4251,6 @@ module_item
module items. These rules try to catch them at a point where a module items. These rules try to catch them at a point where a
reasonable error message can be produced. */ 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 ';' | error ';'
{ yyerror(@2, "error: invalid module item."); { yyerror(@2, "error: invalid module item.");
yyerrok; yyerrok;

View File

@ -33,6 +33,8 @@ std::ostream& operator << (std::ostream&o, const YYLTYPE&loc)
{ {
if (loc.text) if (loc.text)
o << loc.text << ":"; o << loc.text << ":";
else
o << "<>:";
o << loc.first_line; o << loc.first_line;
return o; return o;
} }

189
pform.cc
View File

@ -41,7 +41,13 @@
# include "ivl_assert.h" # include "ivl_assert.h"
# include "ivl_alloc.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<perm_string,Module*> pform_modules; map<perm_string,Module*> pform_modules;
/*
*/
map<perm_string,PUdp*> pform_primitives; map<perm_string,PUdp*> pform_primitives;
@ -217,7 +223,7 @@ extern int VLparse();
/* This tracks the current module being processed. There can only be /* This tracks the current module being processed. There can only be
exactly one module currently being parsed, since Verilog does not exactly one module currently being parsed, since Verilog does not
allow nested module definitions. */ allow nested module definitions. */
static Module*pform_cur_module = 0; static list<Module*>pform_cur_module;
bool pform_library_flag = false; 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()) { pform_cur_generate->tasks.end()) {
cerr << task->get_fileline() << ": error: duplicate " cerr << task->get_fileline() << ": error: duplicate "
"definition for task '" << name << "' in '" "definition for task '" << name << "' in '"
<< pform_cur_module->mod_name() << "' (generate)." << pform_cur_module.front()->mod_name() << "' (generate)."
<< endl; << endl;
error_count += 1; error_count += 1;
} }
@ -350,7 +356,7 @@ PFunction* pform_push_function_scope(const struct vlltype&loc, char*name,
pform_cur_generate->funcs.end()) { pform_cur_generate->funcs.end()) {
cerr << func->get_fileline() << ": error: duplicate " cerr << func->get_fileline() << ": error: duplicate "
"definition for function '" << name << "' in '" "definition for function '" << name << "' in '"
<< pform_cur_module->mod_name() << "' (generate)." << pform_cur_module.front()->mod_name() << "' (generate)."
<< endl; << endl;
error_count += 1; error_count += 1;
} }
@ -402,16 +408,16 @@ void pform_bind_attributes(map<perm_string,PExpr*>&attributes,
bool pform_in_program_block() bool pform_in_program_block()
{ {
if (pform_cur_module == 0) if (pform_cur_module.size() == 0)
return false; return false;
if (pform_cur_module->program_block) if (pform_cur_module.front()->program_block)
return true; return true;
return false; return false;
} }
static bool pform_at_module_level() static bool pform_at_module_level()
{ {
return (lexical_scope == pform_cur_module) return (lexical_scope == pform_cur_module.front())
|| (lexical_scope == pform_cur_generate); || (lexical_scope == pform_cur_generate);
} }
@ -488,15 +494,15 @@ void pform_set_default_nettype(NetNet::Type type,
{ {
pform_default_nettype = type; pform_default_nettype = type;
if (pform_cur_module) { if (pform_cur_module.size() > 0) {
cerr << file<<":"<<lineno << ": error: " cerr << file<<":"<<lineno << ": error: "
<< "`default_nettype directives must appear" << endl; << "`default_nettype directives must appear" << endl;
cerr << file<<":"<<lineno << ": : " cerr << file<<":"<<lineno << ": : "
<< "outside module definitions. The containing" << endl; << "outside module definitions. The containing" << endl;
cerr << file<<":"<<lineno << ": : " cerr << file<<":"<<lineno << ": : "
<< "module " << pform_cur_module->mod_name() << "module " << pform_cur_module.back()->mod_name()
<< " starts on line " << " starts on line "
<< pform_cur_module->get_fileline() << "." << endl; << pform_cur_module.back()->get_fileline() << "." << endl;
error_count += 1; error_count += 1;
} }
} }
@ -699,14 +705,14 @@ void pform_set_timeunit(const char*txt, bool in_module, bool only_check)
if (in_module) { if (in_module) {
if (!only_check) { if (!only_check) {
pform_cur_module->time_unit = val; pform_cur_module.front()->time_unit = val;
tu_decl_flag = true; tu_decl_flag = true;
tu_local_flag = true; tu_local_flag = true;
} else if (!tu_decl_flag) { } else if (!tu_decl_flag) {
VLerror(yylloc, "error: repeat timeunit found and the " VLerror(yylloc, "error: repeat timeunit found and the "
"initial module timeunit is missing."); "initial module timeunit is missing.");
return; 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 " VLerror(yylloc, "error: repeat timeunit does not match "
"the initial module timeunit " "the initial module timeunit "
"declaration."); "declaration.");
@ -723,7 +729,7 @@ void pform_set_timeunit(const char*txt, bool in_module, bool only_check)
int pform_get_timeunit() 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) 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 (in_module) {
if (!only_check) { if (!only_check) {
pform_cur_module->time_precision = val; pform_cur_module.front()->time_precision = val;
tp_decl_flag = true; tp_decl_flag = true;
tp_local_flag = true; tp_local_flag = true;
} else if (!tp_decl_flag) { } else if (!tp_decl_flag) {
VLerror(yylloc, "error: repeat timeprecision found and the " VLerror(yylloc, "error: repeat timeprecision found and the "
"initial module timeprecision is missing."); "initial module timeprecision is missing.");
return; 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 " VLerror(yylloc, "error: repeat timeprecision does not match "
"the initial module timeprecision " "the initial module timeprecision "
"declaration."); "declaration.");
@ -808,26 +814,32 @@ verinum* pform_verinum_with_size(verinum*siz, verinum*val,
void pform_startmodule(const struct vlltype&loc, const char*name, void pform_startmodule(const struct vlltype&loc, const char*name,
bool program_block, list<named_pexpr_t>*attr) bool program_block, list<named_pexpr_t>*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); perm_string lex_name = lex_strings.make(name);
pform_cur_module = new Module(lex_name); Module*cur_module = new Module(lexical_scope, lex_name);
pform_cur_module->program_block = program_block; cur_module->program_block = program_block;
/* Set the local time unit/precision to the global value. */ /* Set the local time unit/precision to the global value. */
pform_cur_module->time_unit = pform_time_unit; cur_module->time_unit = pform_time_unit;
pform_cur_module->time_precision = pform_time_prec; cur_module->time_precision = pform_time_prec;
tu_local_flag = tu_global_flag; tu_local_flag = tu_global_flag;
tp_local_flag = tp_global_flag; tp_local_flag = tp_global_flag;
/* If we have a timescale file then the time information is from /* If we have a timescale file then the time information is from
* a timescale directive. */ * 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); FILE_NAME(cur_module, loc);
pform_cur_module->library_flag = pform_library_flag; cur_module->library_flag = pform_library_flag;
ivl_assert(*pform_cur_module, lexical_scope == 0); pform_cur_module.push_front(cur_module);
lexical_scope = pform_cur_module;
lexical_scope = cur_module;
/* The generate scheme numbering starts with *1*, not /* The generate scheme numbering starts with *1*, not
zero. That's just the way it is, thanks to the standard. */ 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 if (warn_timescale && pform_timescale_file
&& (strcmp(pform_timescale_file,loc.text) != 0)) { && (strcmp(pform_timescale_file,loc.text) != 0)) {
cerr << pform_cur_module->get_fileline() << ": warning: " cerr << cur_module->get_fileline() << ": warning: "
<< "timescale for " << name << "timescale for " << name
<< " inherited from another file." << endl; << " inherited from another file." << endl;
cerr << pform_timescale_file << ":" << pform_timescale_line cerr << pform_timescale_file << ":" << pform_timescale_line
<< ": ...: The inherited timescale is here." << endl; << ": ...: 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() void pform_check_timeunit_prec()
{ {
assert(pform_cur_module); assert(pform_cur_module.size() > 0);
if ((generation_flag & (GN_VER2005_SV | GN_VER2009)) && if ((generation_flag & (GN_VER2005_SV | GN_VER2009)) &&
(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 " VLerror("error: a timeprecision is missing or is too large!");
"large!"); } else assert(pform_cur_module.front()->time_unit >=
} else assert(pform_cur_module->time_unit >= pform_cur_module.front()->time_precision);
pform_cur_module->time_precision);
} }
/* /*
@ -881,7 +892,7 @@ Module::port_t* pform_module_port_reference(perm_string name,
void pform_module_set_ports(vector<Module::port_t*>*ports) void pform_module_set_ports(vector<Module::port_t*>*ports)
{ {
assert(pform_cur_module); assert(pform_cur_module.size() > 0);
/* The parser parses ``module foo()'' as having one /* The parser parses ``module foo()'' as having one
unconnected port, but it is really a module with no unconnected port, but it is really a module with no
@ -892,7 +903,7 @@ void pform_module_set_ports(vector<Module::port_t*>*ports)
} }
if (ports != 0) { if (ports != 0) {
pform_cur_module->ports = *ports; pform_cur_module.front()->ports = *ports;
delete ports; delete ports;
} }
} }
@ -900,34 +911,44 @@ void pform_module_set_ports(vector<Module::port_t*>*ports)
void pform_endmodule(const char*name, bool inside_celldefine, void pform_endmodule(const char*name, bool inside_celldefine,
Module::UCDriveType uc_drive_def) Module::UCDriveType uc_drive_def)
{ {
assert(pform_cur_module); assert(pform_cur_module.size() > 0);
pform_cur_module->time_from_timescale = (tu_local_flag && Module*cur_module = pform_cur_module.front();
tp_local_flag) || pform_cur_module.pop_front();
(pform_timescale_file != 0);
perm_string mod_name = pform_cur_module->mod_name(); 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); assert(strcmp(name, mod_name) == 0);
pform_cur_module->is_cell = inside_celldefine; cur_module->is_cell = inside_celldefine;
pform_cur_module->uc_drive = uc_drive_def; 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<perm_string,Module*>&use_module_map = (pform_cur_module.size() == 0)
? pform_modules
: pform_cur_module.front()->nested_modules;
map<perm_string,Module*>::const_iterator test = map<perm_string,Module*>::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; ostringstream msg;
msg << "Module " << name << " was already declared here: " msg << "Module " << name << " was already declared here: "
<< (*test).second->get_fileline() << endl; << test->second->get_fileline() << endl;
VLerror(msg.str().c_str()); VLerror(msg.str().c_str());
} else { } 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 // The current lexical scope should be this module by now, and
// this module should not have a parent lexical scope. // 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(); 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; tp_decl_flag = false;
tu_decl_flag = false; tu_decl_flag = false;
tu_local_flag = false; tu_local_flag = false;
@ -958,7 +979,7 @@ void pform_genvars(const struct vlltype&li, list<perm_string>*names)
if (pform_cur_generate) if (pform_cur_generate)
pform_add_genvar(li, *cur, pform_cur_generate->genvars); pform_add_genvar(li, *cur, pform_cur_generate->genvars);
else else
pform_add_genvar(li, *cur, pform_cur_module->genvars); pform_add_genvar(li, *cur, pform_cur_module.front()->genvars);
} }
delete names; delete names;
@ -1113,7 +1134,7 @@ void pform_generate_block_name(char*name)
void pform_endgenerate() void pform_endgenerate()
{ {
assert(pform_cur_generate != 0); 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 // If there is no explicit block name then generate a temporary
// name. This will be replaced by the correct name later, once // 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); parent_generate->generate_schemes.push_back(pform_cur_generate);
} else { } else {
assert(pform_cur_generate->scheme_type != PGenerate::GS_CASE_ITEM); 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; 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); FILE_NAME(&tloc, fn, ln);
cerr << tloc.get_fileline() << ": error: duplicate definition " cerr << tloc.get_fileline() << ": error: duplicate definition "
"for named event '" << name << "' in '" "for named event '" << name << "' in '"
<< pform_cur_module->mod_name() << "'." << endl; << pform_cur_module.front()->mod_name() << "'." << endl;
error_count += 1; error_count += 1;
} }
@ -1653,7 +1674,7 @@ static void pform_makegate(PGBuiltin::Type type,
if (pform_cur_generate) if (pform_cur_generate)
pform_cur_generate->add_gate(cur); pform_cur_generate->add_gate(cur);
else else
pform_cur_module->add_gate(cur); pform_cur_module.front()->add_gate(cur);
} }
void pform_makegates(PGBuiltin::Type type, void pform_makegates(PGBuiltin::Type type,
@ -1719,7 +1740,7 @@ static void pform_make_modgate(perm_string type,
if (pform_cur_generate) if (pform_cur_generate)
pform_cur_generate->add_gate(cur); pform_cur_generate->add_gate(cur);
else else
pform_cur_module->add_gate(cur); pform_cur_module.front()->add_gate(cur);
} }
static void pform_make_modgate(perm_string type, static void pform_make_modgate(perm_string type,
@ -1763,7 +1784,7 @@ static void pform_make_modgate(perm_string type,
if (pform_cur_generate) if (pform_cur_generate)
pform_cur_generate->add_gate(cur); pform_cur_generate->add_gate(cur);
else else
pform_cur_module->add_gate(cur); pform_cur_module.front()->add_gate(cur);
} }
void pform_make_modgates(perm_string type, void pform_make_modgates(perm_string type,
@ -1833,7 +1854,7 @@ static PGAssign* pform_make_pgassign(PExpr*lval, PExpr*rval,
if (pform_cur_generate) if (pform_cur_generate)
pform_cur_generate->add_gate(cur); pform_cur_generate->add_gate(cur);
else else
pform_cur_module->add_gate(cur); pform_cur_module.front()->add_gate(cur);
return 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)) { if (PWire*cur = lexical_scope->wires_find(name)) {
cur->attributes[key] = new PEString(value); 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); curg->attributes[key] = new PEString(value);
} else { } else {
@ -2384,23 +2405,23 @@ void pform_set_parameter(const struct vlltype&loc,
FILE_NAME(&tloc, loc); FILE_NAME(&tloc, loc);
cerr << tloc.get_fileline() << ": error: duplicate definition " cerr << tloc.get_fileline() << ": error: duplicate definition "
"for parameter '" << name << "' in '" "for parameter '" << name << "' in '"
<< pform_cur_module->mod_name() << "'." << endl; << pform_cur_module.front()->mod_name() << "'." << endl;
error_count += 1; error_count += 1;
} }
if (scope->localparams.find(name) != scope->localparams.end()) { if (scope->localparams.find(name) != scope->localparams.end()) {
LineInfo tloc; LineInfo tloc;
FILE_NAME(&tloc, loc); FILE_NAME(&tloc, loc);
cerr << tloc.get_fileline() << ": error: localparam and " 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; << "' have the same name '" << name << "'." << endl;
error_count += 1; error_count += 1;
} }
if ((scope == pform_cur_module) && if ((scope == pform_cur_module.front()) &&
(pform_cur_module->specparams.find(name) != pform_cur_module->specparams.end())) { (pform_cur_module.front()->specparams.find(name) != pform_cur_module.front()->specparams.end())) {
LineInfo tloc; LineInfo tloc;
FILE_NAME(&tloc, loc); FILE_NAME(&tloc, loc);
cerr << tloc.get_fileline() << ": error: specparam and " 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; << "' have the same name '" << name << "'." << endl;
error_count += 1; error_count += 1;
} }
@ -2426,8 +2447,8 @@ void pform_set_parameter(const struct vlltype&loc,
parm.signed_flag = signed_flag; parm.signed_flag = signed_flag;
parm.range = value_range; parm.range = value_range;
if (scope == pform_cur_module) if (scope == pform_cur_module.front())
pform_cur_module->param_names.push_back(name); pform_cur_module.front()->param_names.push_back(name);
} }
void pform_set_localparam(const struct vlltype&loc, void pform_set_localparam(const struct vlltype&loc,
@ -2442,23 +2463,23 @@ void pform_set_localparam(const struct vlltype&loc,
FILE_NAME(&tloc, loc); FILE_NAME(&tloc, loc);
cerr << tloc.get_fileline() << ": error: duplicate definition " cerr << tloc.get_fileline() << ": error: duplicate definition "
"for localparam '" << name << "' in '" "for localparam '" << name << "' in '"
<< pform_cur_module->mod_name() << "'." << endl; << pform_cur_module.front()->mod_name() << "'." << endl;
error_count += 1; error_count += 1;
} }
if (scope->parameters.find(name) != scope->parameters.end()) { if (scope->parameters.find(name) != scope->parameters.end()) {
LineInfo tloc; LineInfo tloc;
FILE_NAME(&tloc, loc); FILE_NAME(&tloc, loc);
cerr << tloc.get_fileline() << ": error: parameter and " 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; << "' have the same name '" << name << "'." << endl;
error_count += 1; error_count += 1;
} }
if ((scope == pform_cur_module) && if ((scope == pform_cur_module.front()) &&
(pform_cur_module->specparams.find(name) != pform_cur_module->specparams.end())) { (pform_cur_module.front()->specparams.find(name) != pform_cur_module.front()->specparams.end())) {
LineInfo tloc; LineInfo tloc;
FILE_NAME(&tloc, loc); FILE_NAME(&tloc, loc);
cerr << tloc.get_fileline() << ": error: specparam and " 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; << "' have the same name '" << name << "'." << endl;
error_count += 1; 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, void pform_set_specparam(const struct vlltype&loc, perm_string name,
list<pform_range_t>*range, PExpr*expr) list<pform_range_t>*range, PExpr*expr)
{ {
Module*scope = pform_cur_module; assert(pform_cur_module.size() > 0);
Module*scope = pform_cur_module.front();
assert(scope == lexical_scope); assert(scope == lexical_scope);
// Check if the specparam name is already in the dictionary. // 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; LineInfo tloc;
FILE_NAME(&tloc, loc); FILE_NAME(&tloc, loc);
cerr << tloc.get_fileline() << ": error: duplicate definition " cerr << tloc.get_fileline() << ": error: duplicate definition "
"for specparam '" << name << "' in '" "for specparam '" << name << "' in '"
<< pform_cur_module->mod_name() << "'." << endl; << pform_cur_module.front()->mod_name() << "'." << endl;
error_count += 1; error_count += 1;
} }
if (scope->parameters.find(name) != scope->parameters.end()) { if (scope->parameters.find(name) != scope->parameters.end()) {
LineInfo tloc; LineInfo tloc;
FILE_NAME(&tloc, loc); FILE_NAME(&tloc, loc);
cerr << tloc.get_fileline() << ": error: parameter and " 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; << "' have the same name '" << name << "'." << endl;
error_count += 1; error_count += 1;
} }
@ -2512,13 +2535,14 @@ void pform_set_specparam(const struct vlltype&loc, perm_string name,
LineInfo tloc; LineInfo tloc;
FILE_NAME(&tloc, loc); FILE_NAME(&tloc, loc);
cerr << tloc.get_fileline() << ": error: localparam and " 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; << "' have the same name '" << name << "'." << endl;
error_count += 1; error_count += 1;
} }
assert(expr); 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); FILE_NAME(&parm, loc);
parm.expr = expr; 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) void pform_set_defparam(const pform_name_t&name, PExpr*expr)
{ {
assert(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) if (obj == 0)
return; 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); pform_put_behavior_in_scope(pp);
ivl_assert(*st, pform_cur_module); ivl_assert(*st, pform_cur_module.size() > 0);
if (pform_cur_module->program_block && type == IVL_PR_ALWAYS) { if (pform_cur_module.front()->program_block && type == IVL_PR_ALWAYS) {
cerr << st->get_fileline() << ": error: Always statements not allowed" cerr << st->get_fileline() << ": error: Always statements not allowed"
<< " in program blocks." << endl; << " in program blocks." << endl;
error_count += 1; error_count += 1;
@ -2908,10 +2932,3 @@ int pform_parse(const char*path, FILE*file)
destroy_lexor(); destroy_lexor();
return error_count; 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;
}

View File

@ -424,12 +424,6 @@ extern PAssign* pform_compressed_assign_from_inc_dec(const struct vlltype&loc,
*/ */
extern void pform_dump(ostream&out, Module*mod); 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 /* ** pform_discipline.cc
* Functions for handling the parse of natures and disciplines. These * Functions for handling the parse of natures and disciplines. These
* functions are in pform_disciplines.cc * functions are in pform_disciplines.cc

View File

@ -1249,6 +1249,11 @@ void Module::dump(ostream&out) const
out << ")" << endl; out << ")" << endl;
} }
for (map<perm_string,Module*>::const_iterator cur = nested_modules.begin()
; cur != nested_modules.end() ; ++cur) {
out << setw(4) << "" << "Nested module " << cur->first << ";" << endl;
}
dump_typedefs_(out, 4); dump_typedefs_(out, 4);
dump_parameters_(out, 4); dump_parameters_(out, 4);

View File

@ -2297,6 +2297,7 @@ void dll_target::scope(const NetScope*net)
switch (net->type()) { switch (net->type()) {
case NetScope::MODULE: case NetScope::MODULE:
case NetScope::NESTED_MODULE:
scop->type_ = IVL_SCT_MODULE; scop->type_ = IVL_SCT_MODULE;
scop->tname_ = net->module_name(); scop->tname_ = net->module_name();
scop->ports = net->module_ports(); scop->ports = net->module_ports();