diff --git a/Module.h b/Module.h index 3facf3a7f..81d3c767d 100644 --- a/Module.h +++ b/Module.h @@ -47,6 +47,10 @@ class NetScope; * A module is a named container and scope. A module holds a bunch of * semantic quantities such as wires and gates. The module is * therefore the handle for grasping the described circuit. + * + * SystemVerilog introduces program blocks and interfaces. These have + * much in common with modules, so the Module class is used to represent + * these containers as well. */ class Module : public PScopeExtra, public LineInfo { @@ -81,6 +85,11 @@ class Module : public PScopeExtra, public LineInfo { restrictions and slightly modify scheduling semantics. */ bool program_block; + /* This is true if the module represents a interface + instead of a module/cell. Interfaces have different + content restrictions and some extra allowed items. */ + bool is_interface; + enum UCDriveType { UCD_NONE, UCD_PULL0, UCD_PULL1 }; UCDriveType uc_drive; diff --git a/design_dump.cc b/design_dump.cc index 36dd81369..123917d16 100644 --- a/design_dump.cc +++ b/design_dump.cc @@ -1375,6 +1375,7 @@ void NetScope::dump(ostream&o) const if (is_cell()) o << " (cell)"; if (nested_module()) o << " (nested)"; if (program_block()) o << " (program)"; + if (is_interface()) o << " (interface)"; o << " " << children_.size() << " children, " << classes_.size() << " classes" << endl; diff --git a/elab_scope.cc b/elab_scope.cc index 6e594c241..6a0e00075 100644 --- a/elab_scope.cc +++ b/elab_scope.cc @@ -1742,7 +1742,8 @@ void PGModule::elaborate_scope_mod_instances_(Design*des, Module*mod, NetScope*s // scope searches will continue into the parent scope. NetScope*my_scope = new NetScope(sc, use_name, NetScope::MODULE, bound_type_? true : false, - mod->program_block); + mod->program_block, + mod->is_interface); 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/elaborate.cc b/elaborate.cc index 809ad68f3..9a99826a2 100644 --- a/elaborate.cc +++ b/elaborate.cc @@ -6090,7 +6090,8 @@ 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, rmod->program_block); + NetScope*scope = des->make_root_scope(*root, rmod->program_block, + rmod->is_interface); // Collect some basic properties of this scope from the // Module definition. diff --git a/net_design.cc b/net_design.cc index 5dd021390..dfaf51d56 100644 --- a/net_design.cc +++ b/net_design.cc @@ -101,11 +101,12 @@ uint64_t Design::scale_to_precision(uint64_t val, return val; } -NetScope* Design::make_root_scope(perm_string root, bool program_block) +NetScope* Design::make_root_scope(perm_string root, bool program_block, + bool is_interface) { NetScope *root_scope_; root_scope_ = new NetScope(0, hname_t(root), NetScope::MODULE, - false, program_block); + false, program_block, is_interface); /* 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 1d8653e79..e651b067a 100644 --- a/net_scope.cc +++ b/net_scope.cc @@ -110,8 +110,10 @@ void Definitions::add_class(netclass_t*net_class) * in question. */ -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) +NetScope::NetScope(NetScope*up, const hname_t&n, NetScope::TYPE t, bool nest, + bool program, bool interface) +: type_(t), name_(n), nested_module_(nest), program_block_(program), + is_interface_(interface), up_(up) { events_ = 0; lcounter_ = 0; diff --git a/netlist.h b/netlist.h index 59b146330..28dc78667 100644 --- a/netlist.h +++ b/netlist.h @@ -918,7 +918,8 @@ class NetScope : public Definitions, public Attrib { /* 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, bool nest=false, bool prog=false); + NetScope(NetScope*up, const hname_t&name, TYPE t, bool nest=false, + bool program=false, bool interface=false); ~NetScope(); /* Rename the scope using the name generated by inserting as @@ -1008,8 +1009,9 @@ class NetScope : public Definitions, public Attrib { // Nested modules have slightly different scope search rules. inline bool nested_module() const { return nested_module_; } - // Program blocks have elaboration constraints. + // Program blocks and interfaces have elaboration constraints. inline bool program_block() const { return program_block_; } + inline bool is_interface() const { return is_interface_; } TYPE type() const; void print_type(ostream&) const; @@ -1221,6 +1223,8 @@ class NetScope : public Definitions, public Attrib { bool nested_module_; // True if the scope is a program block bool program_block_; + // True if the scope is an interface + bool is_interface_; perm_string file_; perm_string def_file_; @@ -4747,7 +4751,8 @@ class Design : public Definitions { const char* get_flag(const string&key) const; - NetScope* make_root_scope(perm_string name, bool program_block); + NetScope* make_root_scope(perm_string name, bool program_block, + bool is_interface); NetScope* find_root_scope(); std::list find_root_scopes() const; diff --git a/parse.y b/parse.y index d9696431a..fb5502707 100644 --- a/parse.y +++ b/parse.y @@ -1117,8 +1117,8 @@ data_type_or_implicit_or_void statements may go. This may be a bad idea, but it is legacy now. */ /* NOTE 2: The "module" rule of the description combines the - module_declaration and program_declaration rules from the - standard description. */ + module_declaration, program_declaration, and interface_declaration + rules from the standard description. */ description /* IEEE1800-2005: A.1.2 */ : module @@ -4213,13 +4213,13 @@ local_timeunit_prec_decl2 } ; - /* This is the global structure of a module. A module in a start + /* This is the global structure of a module. A module is a start section, with optional ports, then an optional list of module items, and finally an end marker. */ module : attribute_list_opt module_start IDENTIFIER - { pform_startmodule(@2, $3, $2==K_program, $1); } + { pform_startmodule(@2, $3, $2==K_program, $2==K_interface, $1); } module_package_import_list_opt module_parameter_port_list_opt module_port_list_opt @@ -4258,6 +4258,9 @@ module case K_program: yyerror(@14, "error: program not closed by endprogram."); break; + case K_interface: + yyerror(@14, "error: interface not closed by endinterface."); + break; default: break; } @@ -4283,6 +4286,10 @@ module yyerror(@16, "error: End label doesn't match " "program name."); break; + case K_interface: + yyerror(@16, "error: End label doesn't match " + "interface name."); + break; default: break; } @@ -4297,19 +4304,21 @@ module } ; - /* Modules start with module/macromodule or program keyword, and end - with the endmodule or endprogram keyword. The syntax for modules - and programs is almost identical, so let semantics sort out the - differences. */ + /* Modules start with a module/macromodule, program, or interface + keyword, and end with a endmodule, endprogram, or endinterface + keyword. The syntax for modules programs, and interfaces is + almost identical, so let semantics sort out the differences. */ module_start : K_module { $$ = K_module; } | K_macromodule { $$ = K_module; } | K_program { $$ = K_program; } + | K_interface { $$ = K_interface; } ; module_end - : K_endmodule { $$ = K_module; } - | K_endprogram { $$ = K_program; } + : K_endmodule { $$ = K_module; } + | K_endprogram { $$ = K_program; } + | K_endinterface { $$ = K_interface; } ; endlabel_opt @@ -4505,7 +4514,12 @@ module_item /* */ - | K_defparam defparam_assign_list ';' + | K_defparam + { if (pform_in_interface()) + yyerror(@1, "error: Parameter overrides are not allowed " + "in interfaces."); + } + defparam_assign_list ';' /* Most gate types have an optional drive strength and optional two/three-value delay. These rules handle the different cases. @@ -4674,21 +4688,26 @@ module_item /* 1364-2001 and later allow specparam declarations outside specify blocks. */ - | attribute_list_opt K_specparam specparam_decl ';' + | attribute_list_opt K_specparam + { if (pform_in_interface()) + yyerror(@1, "error: specparam declarations are not allowed " + "in interfaces."); + } + specparam_decl ';' /* specify blocks are parsed but ignored. */ - | K_specify K_endspecify - { /* empty lists are legal syntax. */ } + | K_specify + { if (pform_in_interface()) + yyerror(@1, "error: specify blocks are not allowed " + "in interfaces."); + } + specify_item_list_opt K_endspecify - | K_specify specify_item_list K_endspecify - { - } - - | K_specify error K_endspecify - { yyerror(@1, "error: syntax error in specify block"); - yyerrok; - } + | K_specify error K_endspecify + { yyerror(@1, "error: syntax error in specify block"); + yyerrok; + } /* These rules match various errors that the user can type into module items. These rules try to catch them at a point where a @@ -5467,6 +5486,12 @@ specify_item_list | specify_item_list specify_item ; +specify_item_list_opt + : /* empty */ + { } + | specify_item_list + { } + specify_edge_path_decl : specify_edge_path '=' '(' delay_value_list ')' { $$ = pform_assign_path_delay($1, $4); } diff --git a/pform.cc b/pform.cc index ecb81b721..e77ec065f 100644 --- a/pform.cc +++ b/pform.cc @@ -534,6 +534,15 @@ bool pform_in_program_block() return false; } +bool pform_in_interface() +{ + if (pform_cur_module.empty()) + return false; + if (pform_cur_module.front()->is_interface) + return true; + return false; +} + static bool pform_at_module_level() { return (lexical_scope == pform_cur_module.front()) @@ -1107,7 +1116,8 @@ verinum* pform_verinum_with_size(verinum*siz, verinum*val, } void pform_startmodule(const struct vlltype&loc, const char*name, - bool program_block, list*attr) + bool program_block, bool is_interface, + list*attr) { if (! pform_cur_module.empty() && !gn_system_verilog()) { cerr << loc << ": error: Module definition " << name @@ -1115,15 +1125,25 @@ void pform_startmodule(const struct vlltype&loc, const char*name, error_count += 1; } - if (gn_system_verilog() && ! pform_cur_module.empty() && - pform_cur_module.front()->program_block) { - cerr << loc << ": error: Program blocks cannot contain nested modules/program blocks." << endl; - error_count += 1; + if (gn_system_verilog() && ! pform_cur_module.empty()) { + if (pform_cur_module.front()->program_block) { + cerr << loc << ": error: module, program, or interface " + "declarations are not allowed in program " + "blocks." << endl; + error_count += 1; + } + if (pform_cur_module.front()->is_interface + && !(program_block || is_interface)) { + cerr << loc << ": error: module declarations are not " + "allowed in interfaces." << endl; + error_count += 1; + } } perm_string lex_name = lex_strings.make(name); Module*cur_module = new Module(lexical_scope, lex_name); cur_module->program_block = program_block; + cur_module->is_interface = is_interface; /* Set the local time unit/precision to the global value. */ cur_module->time_unit = pform_time_unit; cur_module->time_precision = pform_time_prec; @@ -2001,8 +2021,13 @@ void pform_makegates(const struct vlltype&loc, { assert(! pform_cur_module.empty()); if (pform_cur_module.front()->program_block) { - cerr << loc << ": error: Gates and switches may not be instantiated" - << " in program blocks." << endl; + cerr << loc << ": error: Gates and switches may not be instantiated in " + << "program blocks." << endl; + error_count += 1; + } + if (pform_cur_module.front()->is_interface) { + cerr << loc << ": error: Gates and switches may not be instantiated in " + << "interfaces." << endl; error_count += 1; } @@ -2117,8 +2142,13 @@ void pform_make_modgates(const struct vlltype&loc, { assert(! pform_cur_module.empty()); if (pform_cur_module.front()->program_block) { - cerr << loc << ": error: Module instantiations are not allowed" - << " in program blocks." << endl; + cerr << loc << ": error: Module instantiations are not allowed in " + << "program blocks." << endl; + error_count += 1; + } + if (pform_cur_module.front()->is_interface) { + cerr << loc << ": error: Module instantiations are not allowed in " + << "interfaces." << endl; error_count += 1; } diff --git a/pform.h b/pform.h index b1b3039c7..ed6519bed 100644 --- a/pform.h +++ b/pform.h @@ -136,6 +136,10 @@ extern void pform_set_default_nettype(NetNet::Type net, used to reject statements that cannot exist in program blocks. */ extern bool pform_in_program_block(void); + /* Return true if currently processing an interface. This can be + used to reject statements that cannot exist in interfaces. */ +extern bool pform_in_interface(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 @@ -152,12 +156,14 @@ extern PWire* pform_get_make_wire_in_scope(perm_string name, NetNet::Type net_ty * 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 + * The program_block flag indicates that the module is actually a program + * block. The is_interface flag indicates that the module is actually + * an interface. These flags have implications during parse and during * elaboration/code generation. */ extern void pform_startmodule(const struct vlltype&loc, const char*name, - bool program_block, list*attr); + bool program_block, bool is_interface, + list*attr); extern void pform_check_timeunit_prec(); extern void pform_module_set_ports(vector*);