diff --git a/Makefile.in b/Makefile.in index 2304cda5b..1ad89867e 100644 --- a/Makefile.in +++ b/Makefile.in @@ -118,8 +118,8 @@ O = main.o async.o design_dump.o discipline.o dup_expr.o elaborate.o \ pform_class_type.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 PPackage.o PScope.o PSpec.o PTask.o PUdp.o PFunction.o PWire.o \ - Statement.o AStatement.o $M $(FF) $(TT) + PGenerate.o PModport.o PPackage.o PScope.o PSpec.o PTask.o PUdp.o \ + PFunction.o PWire.o Statement.o AStatement.o $M $(FF) $(TT) all: dep config.h _pli_types.h version_tag.h ivl@EXEEXT@ version.exe iverilog-vpi.man $(foreach dir,$(SUBDIRS),$(MAKE) -C $(dir) $@ && ) true diff --git a/Module.h b/Module.h index 81d3c767d..f142a761d 100644 --- a/Module.h +++ b/Module.h @@ -1,7 +1,7 @@ #ifndef IVL_Module_H #define IVL_Module_H /* - * Copyright (c) 1998-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 1998-2015 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 @@ -35,6 +35,7 @@ class PExpr; class PEIdent; class PGate; class PGenerate; +class PModport; class PSpecPath; class PTask; class PFunction; @@ -68,7 +69,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. */ + name. This name must be a permallocated string. */ explicit Module(LexicalScope*parent, perm_string name); ~Module(); @@ -134,6 +135,11 @@ class Module : public PScopeExtra, public LineInfo { unless they are instantiated, implicitly or explicitly. */ std::map nested_modules; + /* An interface can contain one or more named modport lists. + The parser will ensure these don't appear in modules or + program blocks. */ + map modports; + list specify_paths; // The mod_name() is the name of the module type. diff --git a/PModport.cc b/PModport.cc new file mode 100644 index 000000000..8ce774088 --- /dev/null +++ b/PModport.cc @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2015 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +# include "config.h" + +# include "PModport.h" + +PModport::PModport(perm_string n) +: name_(n) +{ +} + +PModport::~PModport() +{ +} diff --git a/PModport.h b/PModport.h new file mode 100644 index 000000000..339154784 --- /dev/null +++ b/PModport.h @@ -0,0 +1,52 @@ +#ifndef IVL_PModport_H +#define IVL_PModport_H +/* + * Copyright (c) 2015 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +# include "LineInfo.h" +# include "PScope.h" +# include "StringHeap.h" +# include "netlist.h" +# include + +/* + * The PModport class represents a parsed SystemVerilog modport list. + */ +class PModport : public LineInfo { + + public: + // The name is a perm-allocated string. It is the simple name + // of the modport, without any scope. + explicit PModport(perm_string name); + ~PModport(); + + perm_string name() const { return name_; } + + typedef pair simple_port_t; + map simple_ports; + + private: + perm_string name_; + + private: // not implemented + PModport(const PModport&); + PModport& operator= (const PModport&); +}; + +#endif /* IVL_PModport_H */ diff --git a/parse.y b/parse.y index fb5502707..4b8d2efa1 100644 --- a/parse.y +++ b/parse.y @@ -1,7 +1,7 @@ %{ /* - * Copyright (c) 1998-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 1998-2015 Stephen Williams (steve@icarus.com) * Copyright CERN 2012-2013 / Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it @@ -49,6 +49,16 @@ static struct { data_type_t* data_type; } port_declaration_context = {NetNet::NONE, NetNet::NOT_A_PORT, 0}; +/* Modport port declaration lists use this structure for context. */ +enum modport_port_type_t { MP_NONE, MP_SIMPLE, MP_TF, MP_CLOCKING }; +static struct { + modport_port_type_t type; + union { + NetNet::PortType direction; + bool is_import; + }; +} last_modport_port = { MP_NONE, NetNet::NOT_A_PORT}; + /* The task and function rules need to briefly hold the pointer to the task/function that is currently in progress. */ static PTask* current_task = 0; @@ -549,6 +559,7 @@ static void current_function_set_statement(const YYLTYPE&loc, vector %type from_exclude block_item_decls_opt %type number pos_neg_number %type signing unsigned_signed_opt signed_unsigned_opt +%type import_export %type K_automatic_opt K_packed_opt K_reg_opt K_static_opt K_virtual_opt %type udp_reg_opt edge_operator %type drive_strength drive_strength_opt dr_strength0 dr_strength1 @@ -583,7 +594,7 @@ static void current_function_set_statement(const YYLTYPE&loc, vector %type task_item task_item_list task_item_list_opt %type tf_port_declaration tf_port_item tf_port_list tf_port_list_opt -%type port_name parameter_value_byname +%type modport_simple_port port_name parameter_value_byname %type port_name_list parameter_value_byname_list %type attribute @@ -1270,6 +1281,11 @@ function_declaration /* IEEE1800-2005: A.2.6 */ ; +import_export /* IEEE1800-2012: A.2.9 */ + : K_import { $$ = true; } + | K_export { $$ = false; } + ; + implicit_class_handle /* IEEE1800-2005: A.8.4 */ : K_this { $$ = pform_create_this(); } | K_super { $$ = pform_create_super(); } @@ -1546,6 +1562,115 @@ method_qualifier_opt | ; +modport_declaration /* IEEE1800-2012: A.2.9 */ + : K_modport + { if (!pform_in_interface()) + yyerror(@1, "error: modport declarations are only allowed " + "in interfaces."); + } + modport_item_list ';' + +modport_item_list + : modport_item + | modport_item_list ',' modport_item + ; + +modport_item + : IDENTIFIER + { pform_start_modport_item(@1, $1); } + '(' modport_ports_list ')' + { pform_end_modport_item(@1); } + ; + + /* The modport_ports_list is a LALR(2) grammar. When the parser sees a + ',' it needs to look ahead to the next token to decide whether it is + a continuation of the preceeding modport_ports_declaration, or the + start of a new modport_ports_declaration. bison only supports LALR(1), + so we have to handcraft a mini parser for this part of the syntax. + last_modport_port holds the state for this mini parser.*/ + +modport_ports_list + : modport_ports_declaration + | modport_ports_list ',' modport_ports_declaration + | modport_ports_list ',' modport_simple_port + { if (last_modport_port.type == MP_SIMPLE) { + pform_add_modport_port(@3, last_modport_port.direction, + $3->name, $3->parm); + } else { + yyerror(@3, "error: modport expression not allowed here."); + } + delete $3; + } + | modport_ports_list ',' modport_tf_port + { if (last_modport_port.type != MP_TF) + yyerror(@3, "error: task/function declaration not allowed here."); + } + | modport_ports_list ',' IDENTIFIER + { if (last_modport_port.type == MP_SIMPLE) { + pform_add_modport_port(@3, last_modport_port.direction, + lex_strings.make($3), 0); + } else if (last_modport_port.type != MP_TF) { + yyerror(@3, "error: list of identifiers not allowed here."); + } + delete[] $3; + } + | modport_ports_list ',' + { yyerror(@2, "error: NULL port declarations are not allowed"); } + ; + +modport_ports_declaration + : attribute_list_opt port_direction IDENTIFIER + { last_modport_port.type = MP_SIMPLE; + last_modport_port.direction = $2; + pform_add_modport_port(@3, $2, lex_strings.make($3), 0); + delete[] $3; + delete $1; + } + | attribute_list_opt port_direction modport_simple_port + { last_modport_port.type = MP_SIMPLE; + last_modport_port.direction = $2; + pform_add_modport_port(@3, $2, $3->name, $3->parm); + delete $3; + delete $1; + } + | attribute_list_opt import_export IDENTIFIER + { last_modport_port.type = MP_TF; + last_modport_port.is_import = $2; + yyerror(@3, "sorry: modport task/function ports are not yet supported."); + delete[] $3; + delete $1; + } + | attribute_list_opt import_export modport_tf_port + { last_modport_port.type = MP_TF; + last_modport_port.is_import = $2; + yyerror(@3, "sorry: modport task/function ports are not yet supported."); + delete $1; + } + | attribute_list_opt K_clocking IDENTIFIER + { last_modport_port.type = MP_CLOCKING; + last_modport_port.direction = NetNet::NOT_A_PORT; + yyerror(@3, "sorry: modport clocking declaration is not yet supported."); + delete[] $3; + delete $1; + } + ; + +modport_simple_port + : '.' IDENTIFIER '(' expression ')' + { named_pexpr_t*tmp = new named_pexpr_t; + tmp->name = lex_strings.make($2); + tmp->parm = $4; + delete[]$2; + $$ = tmp; + } + ; + +modport_tf_port + : K_task IDENTIFIER + | K_task IDENTIFIER '(' tf_port_list_opt ')' + | K_function data_type_or_implicit_or_void IDENTIFIER + | K_function data_type_or_implicit_or_void IDENTIFIER '(' tf_port_list_opt ')' + ; non_integer_type /* IEEE1800-2005: A.2.2.1 */ : K_real { $$ = real_type_t::REAL; } @@ -4684,6 +4809,8 @@ module_item pform_endgenerate(); } + | modport_declaration + | package_import_declaration /* 1364-2001 and later allow specparam declarations outside specify blocks. */ @@ -6608,7 +6735,7 @@ udp_primitive presence is significant. This is a fairly common pattern so collect those rules here. */ -K_automatic_opt: K_automatic { $$ = true; } | { $$ = false;} ; +K_automatic_opt: K_automatic { $$ = true; } | { $$ = false; } ; K_packed_opt : K_packed { $$ = true; } | { $$ = false; } ; K_reg_opt : K_reg { $$ = true; } | { $$ = false; } ; K_static_opt : K_static { $$ = true; } | { $$ = false; } ; diff --git a/pform.cc b/pform.cc index e77ec065f..1e42fa0ad 100644 --- a/pform.cc +++ b/pform.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 1998-2015 Stephen Williams (steve@icarus.com) * Copyright CERN 2013 / Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it @@ -29,6 +29,7 @@ # include "PPackage.h" # include "PUdp.h" # include "PGenerate.h" +# include "PModport.h" # include "PSpec.h" # include "discipline.h" # include @@ -265,6 +266,10 @@ static unsigned scope_generate_counter = 1; always within a module. */ static PGenerate*pform_cur_generate = 0; + /* This tracks the current modport list being processed. This is + always within an interface. */ +static PModport*pform_cur_modport = 0; + static NetNet::Type pform_default_nettype = NetNet::WIRE; /* @@ -3356,6 +3361,46 @@ PProcess* pform_make_behavior(ivl_process_type_t type, Statement*st, return pp; } +void pform_start_modport_item(const struct vlltype&loc, const char*name) +{ + Module*scope = pform_cur_module.front(); + ivl_assert(loc, scope && scope->is_interface); + ivl_assert(loc, pform_cur_modport == 0); + + perm_string use_name = lex_strings.make(name); + pform_cur_modport = new PModport(use_name); + FILE_NAME(pform_cur_modport, loc); + if (scope->modports.find(use_name) != scope->modports.end()) { + cerr << loc << ": error: duplicate declaration for modport '" + << name << "' in '" << scope->mod_name() << "'." << endl; + error_count += 1; + } + scope->modports[use_name] = pform_cur_modport; + delete[] name; +} + +void pform_end_modport_item(const struct vlltype&loc) +{ + ivl_assert(loc, pform_cur_modport); + pform_cur_modport = 0; +} + +void pform_add_modport_port(const struct vlltype&loc, + NetNet::PortType port_type, + perm_string name, PExpr*expr) +{ + ivl_assert(loc, pform_cur_modport); + + if (pform_cur_modport->simple_ports.find(name) + != pform_cur_modport->simple_ports.end()) { + cerr << loc << ": error: duplicate declaration of port '" + << name << "' in modport list '" + << pform_cur_modport->name() << "'." << endl; + error_count += 1; + } + pform_cur_modport->simple_ports[name] = make_pair(port_type, expr); +} + FILE*vl_input = 0; extern void reset_lexor(); diff --git a/pform.h b/pform.h index ed6519bed..5ea113c37 100644 --- a/pform.h +++ b/pform.h @@ -1,7 +1,7 @@ #ifndef IVL_pform_H #define IVL_pform_H /* - * Copyright (c) 1998-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 1998-2015 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 @@ -219,6 +219,15 @@ extern void pform_package_import(const struct vlltype&loc, extern PExpr* pform_package_ident(const struct vlltype&loc, PPackage*pkg, pform_name_t*ident); +/* + * Interface related functions. + */ +extern void pform_start_modport_item(const struct vlltype&loc, const char*name); +extern void pform_end_modport_item(const struct vlltype&loc); +extern void pform_add_modport_port(const struct vlltype&loc, + NetNet::PortType port_type, + perm_string name, PExpr*expr); + /* * This creates an identifier aware of names that may have been * imported from other packages.