diff options
author | Clifford Wolf <clifford@clifford.at> | 2018-10-18 10:58:47 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-10-18 10:58:47 +0200 |
commit | f24bc1ed0a80e48bc23ae68169b6b0bbce5f113c (patch) | |
tree | 1778829a6932d18730a3a085a80a65205189c7ba | |
parent | 24a5c6585678f89058382fe2c3f36b821b419e90 (diff) | |
parent | 736105b0468f9468f00915cad60949535ce5a496 (diff) | |
download | yosys-f24bc1ed0a80e48bc23ae68169b6b0bbce5f113c.tar.gz yosys-f24bc1ed0a80e48bc23ae68169b6b0bbce5f113c.tar.bz2 yosys-f24bc1ed0a80e48bc23ae68169b6b0bbce5f113c.zip |
Merge pull request #659 from rubund/sv_interfaces
Support for SystemVerilog interfaces and modports
-rw-r--r-- | README.md | 3 | ||||
-rw-r--r-- | frontends/ast/ast.cc | 190 | ||||
-rw-r--r-- | frontends/ast/ast.h | 8 | ||||
-rw-r--r-- | frontends/ast/genrtlil.cc | 79 | ||||
-rw-r--r-- | frontends/ast/simplify.cc | 2 | ||||
-rw-r--r-- | frontends/verilog/verilog_lexer.l | 8 | ||||
-rw-r--r-- | frontends/verilog/verilog_parser.y | 80 | ||||
-rw-r--r-- | kernel/rtlil.cc | 13 | ||||
-rw-r--r-- | kernel/rtlil.h | 2 | ||||
-rw-r--r-- | passes/hierarchy/hierarchy.cc | 195 | ||||
-rw-r--r-- | tests/simple/svinterface1.sv | 90 |
11 files changed, 649 insertions, 21 deletions
@@ -452,6 +452,9 @@ from SystemVerilog: into a design with ``read_verilog``, all its packages are available to SystemVerilog files being read into the same design afterwards. +- SystemVerilog interfaces (SVIs) are supported. Modports for specifying whether + ports are inputs or outputs are supported. + Building the documentation ========================== diff --git a/frontends/ast/ast.cc b/frontends/ast/ast.cc index e79be953a..7600e2912 100644 --- a/frontends/ast/ast.cc +++ b/frontends/ast/ast.cc @@ -2,6 +2,7 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2018 Ruben Undheim <ruben.undheim@gmail.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -905,7 +906,7 @@ RTLIL::Const AstNode::realAsConst(int width) // create a new AstModule from an AST_MODULE AST node static AstModule* process_module(AstNode *ast, bool defer) { - log_assert(ast->type == AST_MODULE); + log_assert(ast->type == AST_MODULE || ast->type == AST_INTERFACE); if (defer) log("Storing AST representation for module `%s'.\n", ast->str.c_str()); @@ -916,6 +917,7 @@ static AstModule* process_module(AstNode *ast, bool defer) current_module->ast = NULL; current_module->name = ast->str; current_module->attributes["\\src"] = stringf("%s:%d", ast->filename.c_str(), ast->linenum); + current_module->set_bool_attribute("\\cells_not_processed"); current_ast_mod = ast; AstNode *ast_before_simplify = ast->clone(); @@ -989,6 +991,8 @@ static AstModule* process_module(AstNode *ast, bool defer) ignoreThisSignalsInInitial = RTLIL::SigSpec(); } + if (ast->type == AST_INTERFACE) + current_module->set_bool_attribute("\\is_interface"); current_module->ast = ast_before_simplify; current_module->nolatches = flag_nolatches; current_module->nomeminit = flag_nomeminit; @@ -1031,7 +1035,7 @@ void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump log_assert(current_ast->type == AST_DESIGN); for (auto it = current_ast->children.begin(); it != current_ast->children.end(); it++) { - if ((*it)->type == AST_MODULE) + if ((*it)->type == AST_MODULE || (*it)->type == AST_INTERFACE) { for (auto n : design->verilog_globals) (*it)->children.push_back(n->clone()); @@ -1083,8 +1087,179 @@ AstModule::~AstModule() delete ast; } +// When an interface instance is found in a module, the whole RTLIL for the module will be rederived again +// from AST. The interface members are copied into the AST module with the prefix of the interface. +void AstModule::reprocess_module(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Module*> local_interfaces) +{ + bool is_top = false; + AstNode *new_ast = ast->clone(); + for (auto &intf : local_interfaces) { + std::string intfname = intf.first.str(); + RTLIL::Module *intfmodule = intf.second; + for (auto &wire_it : intfmodule->wires_){ + AstNode *wire = new AstNode(AST_WIRE, new AstNode(AST_RANGE, AstNode::mkconst_int(wire_it.second->width -1, true), AstNode::mkconst_int(0, true))); + std::string newname = log_id(wire_it.first); + newname = intfname + "." + newname; + wire->str = newname; + new_ast->children.push_back(wire); + } + } + + // The old module will be deleted. Rename and mark for deletion: + std::string original_name = this->name.str(); + std::string changed_name = original_name + "_before_replacing_local_interfaces"; + design->rename(this, changed_name); + this->set_bool_attribute("\\to_delete"); + + // Check if the module was the top module. If it was, we need to remove the top attribute and put it on the + // new module. + if (this->get_bool_attribute("\\initial_top")) { + this->attributes.erase("\\initial_top"); + is_top = true; + } + + // Generate RTLIL from AST for the new module and add to the design: + AstModule *newmod = process_module(new_ast, false); + design->add(newmod); + RTLIL::Module* mod = design->module(original_name); + if (is_top) + mod->set_bool_attribute("\\top"); + + // Set the attribute "interfaces_replaced_in_module" so that it does not happen again. + mod->set_bool_attribute("\\interfaces_replaced_in_module"); +} + +// create a new parametric module (when needed) and return the name of the generated module - WITH support for interfaces +// This method is used to explode the interface when the interface is a port of the module (not instantiated inside) +RTLIL::IdString AstModule::derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, dict<RTLIL::IdString, RTLIL::Module*> interfaces, dict<RTLIL::IdString, RTLIL::IdString> modports, bool mayfail) +{ + AstNode *new_ast = NULL; + std::string modname = derive_common(design, parameters, &new_ast, mayfail); + + // Since interfaces themselves may be instantiated with different parameters, + // "modname" must also take those into account, so that unique modules + // are derived for any variant of interface connections: + std::string interf_info = ""; + + bool has_interfaces = false; + for(auto &intf : interfaces) { + interf_info += log_id(intf.second->name); + has_interfaces = true; + } + + if (has_interfaces) + modname += "$interfaces$" + interf_info; + + + if (!design->has(modname)) { + new_ast->str = modname; + + // Iterate over all interfaces which are ports in this module: + for(auto &intf : interfaces) { + RTLIL::Module * intfmodule = intf.second; + std::string intfname = intf.first.str(); + // Check if a modport applies for the interface port: + AstNode *modport = NULL; + if (modports.count(intfname) > 0) { + std::string interface_modport = modports.at(intfname).str(); + AstModule *ast_module_of_interface = (AstModule*)intfmodule; + AstNode *ast_node_of_interface = ast_module_of_interface->ast; + for (auto &ch : ast_node_of_interface->children) { + if (ch->type == AST_MODPORT) { + if (ch->str == interface_modport) { // Modport found + modport = ch; + } + } + } + } + // Iterate over all wires in the interface and add them to the module: + for (auto &wire_it : intfmodule->wires_){ + AstNode *wire = new AstNode(AST_WIRE, new AstNode(AST_RANGE, AstNode::mkconst_int(wire_it.second->width -1, true), AstNode::mkconst_int(0, true))); + std::string origname = log_id(wire_it.first); + std::string newname = intfname + "." + origname; + wire->str = newname; + if (modport != NULL) { + bool found_in_modport = false; + // Search for the current wire in the wire list for the current modport + for (auto &ch : modport->children) { + if (ch->type == AST_MODPORTMEMBER) { + std::string compare_name = "\\" + origname; + if (ch->str == compare_name) { // Found signal. The modport decides whether it is input or output + found_in_modport = true; + wire->is_input = ch->is_input; + wire->is_output = ch->is_output; + break; + } + } + } + if (found_in_modport) { + new_ast->children.push_back(wire); + } + else { // If not found in modport, do not create port + delete wire; + } + } + else { // If no modport, set inout + wire->is_input = true; + wire->is_output = true; + new_ast->children.push_back(wire); + } + } + } + + design->add(process_module(new_ast, false)); + design->module(modname)->check(); + + RTLIL::Module* mod = design->module(modname); + + // Now that the interfaces have been exploded, we can delete the dummy port related to every interface. + for(auto &intf : interfaces) { + if(mod->wires_.count(intf.first)) { + mod->wires_.erase(intf.first); + mod->fixup_ports(); + // We copy the cell of the interface to the sub-module such that it can further be found if it is propagated + // down to sub-sub-modules etc. + RTLIL::Cell * new_subcell = mod->addCell(intf.first, intf.second->name); + new_subcell->set_bool_attribute("\\is_interface"); + } + else { + log_error("No port with matching name found (%s) in %s. Stopping\n", log_id(intf.first), modname.c_str()); + } + } + + // If any interfaces were replaced, set the attribute 'interfaces_replaced_in_module': + if (interfaces.size() > 0) { + mod->set_bool_attribute("\\interfaces_replaced_in_module"); + } + + } else { + log("Found cached RTLIL representation for module `%s'.\n", modname.c_str()); + } + + delete new_ast; + return modname; +} + +// create a new parametric module (when needed) and return the name of the generated module - without support for interfaces +RTLIL::IdString AstModule::derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, bool mayfail) +{ + AstNode *new_ast = NULL; + std::string modname = derive_common(design, parameters, &new_ast, mayfail); + + if (!design->has(modname)) { + new_ast->str = modname; + design->add(process_module(new_ast, false)); + design->module(modname)->check(); + } else { + log("Found cached RTLIL representation for module `%s'.\n", modname.c_str()); + } + + delete new_ast; + return modname; +} + // create a new parametric module (when needed) and return the name of the generated module -RTLIL::IdString AstModule::derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, bool) +std::string AstModule::derive_common(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, AstNode **new_ast_out, bool) { std::string stripped_name = name.str(); @@ -1156,15 +1331,8 @@ RTLIL::IdString AstModule::derive(RTLIL::Design *design, dict<RTLIL::IdString, R else modname = "$paramod" + stripped_name + para_info; - if (!design->has(modname)) { - new_ast->str = modname; - design->add(process_module(new_ast, false)); - design->module(modname)->check(); - } else { - log("Found cached RTLIL representation for module `%s'.\n", modname.c_str()); - } - delete new_ast; + (*new_ast_out) = new_ast; return modname; } diff --git a/frontends/ast/ast.h b/frontends/ast/ast.h index 7e97bdb3b..8187b1ac6 100644 --- a/frontends/ast/ast.h +++ b/frontends/ast/ast.h @@ -142,6 +142,11 @@ namespace AST AST_NEGEDGE, AST_EDGE, + AST_INTERFACE, + AST_INTERFACEPORT, + AST_INTERFACEPORTTYPE, + AST_MODPORT, + AST_MODPORTMEMBER, AST_PACKAGE }; @@ -284,6 +289,9 @@ namespace AST bool nolatches, nomeminit, nomem2reg, mem2reg, lib, noopt, icells, autowire; ~AstModule() YS_OVERRIDE; RTLIL::IdString derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, bool mayfail) YS_OVERRIDE; + RTLIL::IdString derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, dict<RTLIL::IdString, RTLIL::Module*> interfaces, dict<RTLIL::IdString, RTLIL::IdString> modports, bool mayfail) YS_OVERRIDE; + std::string derive_common(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, AstNode **new_ast_out, bool mayfail); + void reprocess_module(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Module *> local_interfaces) YS_OVERRIDE; RTLIL::Module *clone() const YS_OVERRIDE; }; diff --git a/frontends/ast/genrtlil.cc b/frontends/ast/genrtlil.cc index c9345ff08..32b9af6e9 100644 --- a/frontends/ast/genrtlil.cc +++ b/frontends/ast/genrtlil.cc @@ -853,6 +853,52 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) case AST_GENIF: case AST_GENCASE: case AST_PACKAGE: + case AST_MODPORT: + case AST_MODPORTMEMBER: + break; + case AST_INTERFACEPORT: { + // If a port in a module with unknown type is found, mark it with the attribute 'is_interface' + // This is used by the hierarchy pass to know when it can replace interface connection with the individual + // signals. + RTLIL::Wire *wire = current_module->addWire(str, 1); + wire->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum); + wire->start_offset = 0; + wire->port_id = port_id; + wire->port_input = true; + wire->port_output = true; + wire->set_bool_attribute("\\is_interface"); + if (children.size() > 0) { + for(size_t i=0; i<children.size();i++) { + if(children[i]->type == AST_INTERFACEPORTTYPE) { + std::string name_type = children[i]->str; + size_t ndots = std::count(name_type.begin(), name_type.end(), '.'); + // Separate the interface instance name from any modports: + if (ndots == 0) { // Does not have modport + wire->attributes["\\interface_type"] = name_type; + } + else { + std::stringstream name_type_stream(name_type); + std::string segment; + std::vector<std::string> seglist; + while(std::getline(name_type_stream, segment, '.')) { + seglist.push_back(segment); + } + if (ndots == 1) { // Has modport + wire->attributes["\\interface_type"] = seglist[0]; + wire->attributes["\\interface_modport"] = seglist[1]; + } + else { // Erroneous port type + log_error("More than two '.' in signal port type (%s)\n", name_type.c_str()); + } + } + break; + } + } + } + wire->upto = 0; + } + break; + case AST_INTERFACEPORTTYPE: break; // remember the parameter, needed for example in techmap @@ -949,6 +995,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) { RTLIL::Wire *wire = NULL; RTLIL::SigChunk chunk; + bool is_interface = false; int add_undef_bits_msb = 0; int add_undef_bits_lsb = 0; @@ -969,15 +1016,41 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) chunk = RTLIL::Const(id2ast->children[0]->bits); goto use_const_chunk; } - else if (!id2ast || (id2ast->type != AST_WIRE && id2ast->type != AST_AUTOWIRE && - id2ast->type != AST_MEMORY) || current_module->wires_.count(str) == 0) + else if (id2ast && (id2ast->type == AST_WIRE || id2ast->type == AST_AUTOWIRE || id2ast->type == AST_MEMORY) && current_module->wires_.count(str) != 0) { + RTLIL::Wire *current_wire = current_module->wire(str); + if (current_wire->get_bool_attribute("\\is_interface")) + is_interface = true; + // Ignore + } + // If an identifier is found that is not already known, assume that it is an interface: + else if (1) { // FIXME: Check if sv_mode first? + is_interface = true; + } + else { log_file_error(filename, linenum, "Identifier `%s' doesn't map to any signal!\n", str.c_str()); + } if (id2ast->type == AST_MEMORY) log_file_error(filename, linenum, "Identifier `%s' does map to an unexpanded memory!\n", str.c_str()); + // If identifier is an interface, create a RTLIL::SigSpec with a dummy wire with a attribute called 'is_interface' + // This makes it possible for the hierarchy pass to see what are interface connections and then replace them + // with the individual signals: + if (is_interface) { + RTLIL::Wire *dummy_wire; + std::string dummy_wire_name = "$dummywireforinterface" + str; + if (current_module->wires_.count(dummy_wire_name)) + dummy_wire = current_module->wires_[dummy_wire_name]; + else { + dummy_wire = current_module->addWire(dummy_wire_name); + dummy_wire->set_bool_attribute("\\is_interface"); + } + RTLIL::SigSpec tmp = RTLIL::SigSpec(dummy_wire); + return tmp; + } + wire = current_module->wires_[str]; chunk.wire = wire; chunk.width = wire->width; @@ -1423,6 +1496,8 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) RTLIL::Cell *cell = current_module->addCell(str, ""); cell->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum); + // Set attribute 'module_not_derived' which will be cleared again after the hierarchy pass + cell->set_bool_attribute("\\module_not_derived"); for (auto it = children.begin(); it != children.end(); it++) { AstNode *child = *it; diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc index dca9da198..e56a62563 100644 --- a/frontends/ast/simplify.cc +++ b/frontends/ast/simplify.cc @@ -71,7 +71,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, if (stage == 0) { - log_assert(type == AST_MODULE); + log_assert(type == AST_MODULE || type == AST_INTERFACE); last_blocking_assignment_warn = pair<string, int>(); deep_recursion_warning = true; diff --git a/frontends/verilog/verilog_lexer.l b/frontends/verilog/verilog_lexer.l index 2c4880b84..f9118e142 100644 --- a/frontends/verilog/verilog_lexer.l +++ b/frontends/verilog/verilog_lexer.l @@ -153,6 +153,9 @@ YOSYS_NAMESPACE_END "specparam" { return TOK_SPECPARAM; } "package" { SV_KEYWORD(TOK_PACKAGE); } "endpackage" { SV_KEYWORD(TOK_ENDPACKAGE); } +"interface" { SV_KEYWORD(TOK_INTERFACE); } +"endinterface" { SV_KEYWORD(TOK_ENDINTERFACE); } +"modport" { SV_KEYWORD(TOK_MODPORT); } "parameter" { return TOK_PARAMETER; } "localparam" { return TOK_LOCALPARAM; } "defparam" { return TOK_DEFPARAM; } @@ -298,6 +301,11 @@ supply1 { return TOK_SUPPLY1; } return TOK_ID; } +[a-zA-Z_$][a-zA-Z0-9_$\.]* { + frontend_verilog_yylval.string = new std::string(std::string("\\") + yytext); + return TOK_ID; +} + "/*"[ \t]*(synopsys|synthesis)[ \t]*translate_off[ \t]*"*/" { static bool printed_warning = false; if (!printed_warning) { diff --git a/frontends/verilog/verilog_parser.y b/frontends/verilog/verilog_parser.y index 16cac1460..4dbe028a0 100644 --- a/frontends/verilog/verilog_parser.y +++ b/frontends/verilog/verilog_parser.y @@ -61,6 +61,7 @@ namespace VERILOG_FRONTEND { bool noassert_mode, noassume_mode, norestrict_mode; bool assume_asserts_mode, assert_assumes_mode; bool current_wire_rand, current_wire_const; + bool current_modport_input, current_modport_output; std::istream *lexin; } YOSYS_NAMESPACE_END @@ -106,6 +107,7 @@ static void free_attr(std::map<std::string, AstNode*> *al) %token ATTR_BEGIN ATTR_END DEFATTR_BEGIN DEFATTR_END %token TOK_MODULE TOK_ENDMODULE TOK_PARAMETER TOK_LOCALPARAM TOK_DEFPARAM %token TOK_PACKAGE TOK_ENDPACKAGE TOK_PACKAGESEP +%token TOK_INTERFACE TOK_ENDINTERFACE TOK_MODPORT %token TOK_INPUT TOK_OUTPUT TOK_INOUT TOK_WIRE TOK_REG TOK_LOGIC %token TOK_INTEGER TOK_SIGNED TOK_ASSIGN TOK_ALWAYS TOK_INITIAL %token TOK_BEGIN TOK_END TOK_IF TOK_ELSE TOK_FOR TOK_WHILE TOK_REPEAT @@ -168,6 +170,7 @@ design: param_decl design | localparam_decl design | package design | + interface design | /* empty */; attr: @@ -320,6 +323,21 @@ module_arg: } delete $1; } module_arg_opt_assignment | + TOK_ID { + astbuf1 = new AstNode(AST_INTERFACEPORT); + astbuf1->children.push_back(new AstNode(AST_INTERFACEPORTTYPE)); + astbuf1->children[0]->str = *$1; + delete $1; + } TOK_ID { /* SV interfaces */ + if (!sv_mode) + frontend_verilog_yyerror("Interface found in port list (%s). This is not supported unless read_verilog is called with -sv!", $3->c_str()); + astbuf2 = astbuf1->clone(); // really only needed if multiple instances of same type. + astbuf2->str = *$3; + delete $3; + astbuf2->port_id = ++port_counter; + ast_stack.back()->children.push_back(astbuf2); + delete astbuf1; // really only needed if multiple instances of same type. + } module_arg_opt_assignment | attr wire_type range TOK_ID { AstNode *node = $2; node->str = *$4; @@ -357,6 +375,33 @@ package_body: package_body_stmt: localparam_decl; +interface: + TOK_INTERFACE TOK_ID { + do_not_require_port_stubs = false; + AstNode *intf = new AstNode(AST_INTERFACE); + ast_stack.back()->children.push_back(intf); + ast_stack.push_back(intf); + current_ast_mod = intf; + port_stubs.clear(); + port_counter = 0; + intf->str = *$2; + delete $2; + } module_para_opt module_args_opt ';' interface_body TOK_ENDINTERFACE { + if (port_stubs.size() != 0) + frontend_verilog_yyerror("Missing details for module port `%s'.", + port_stubs.begin()->first.c_str()); + ast_stack.pop_back(); + log_assert(ast_stack.size() == 1); + current_ast_mod = NULL; + }; + +interface_body: + interface_body interface_body_stmt |; + +interface_body_stmt: + param_decl | localparam_decl | defparam_decl | wire_decl | always_stmt | assign_stmt | + modport_stmt; + non_opt_delay: '#' TOK_ID { delete $2; } | '#' TOK_CONSTVAL { delete $2; } | @@ -1280,6 +1325,41 @@ opt_property: opt_stmt_label: TOK_ID ':' | /* empty */; +modport_stmt: + TOK_MODPORT TOK_ID { + AstNode *modport = new AstNode(AST_MODPORT); + ast_stack.back()->children.push_back(modport); + ast_stack.push_back(modport); + modport->str = *$2; + delete $2; + } modport_args_opt { + ast_stack.pop_back(); + log_assert(ast_stack.size() == 2); + } ';' + +modport_args_opt: + '(' ')' | '(' modport_args optional_comma ')'; + +modport_args: + modport_arg | modport_args ',' modport_arg; + +modport_arg: + modport_type_token modport_member | + modport_member + +modport_member: + TOK_ID { + AstNode *modport_member = new AstNode(AST_MODPORTMEMBER); + ast_stack.back()->children.push_back(modport_member); + modport_member->str = *$1; + modport_member->is_input = current_modport_input; + modport_member->is_output = current_modport_output; + delete $1; + } + +modport_type_token: + TOK_INPUT {current_modport_input = 1; current_modport_output = 0;} | TOK_OUTPUT {current_modport_input = 0; current_modport_output = 1;} + assert: opt_stmt_label TOK_ASSERT opt_property '(' expr ')' ';' { if (noassert_mode) diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc index a4fa2cf04..14259f8ed 100644 --- a/kernel/rtlil.cc +++ b/kernel/rtlil.cc @@ -639,6 +639,11 @@ RTLIL::Module::~Module() delete it->second; } +void RTLIL::Module::reprocess_module(RTLIL::Design *, dict<RTLIL::IdString, RTLIL::Module *>) +{ + log_error("Cannot reprocess_module module `%s' !\n", id2cstr(name)); +} + RTLIL::IdString RTLIL::Module::derive(RTLIL::Design*, dict<RTLIL::IdString, RTLIL::Const>, bool mayfail) { if (mayfail) @@ -646,6 +651,14 @@ RTLIL::IdString RTLIL::Module::derive(RTLIL::Design*, dict<RTLIL::IdString, RTLI log_error("Module `%s' is used with parameters but is not parametric!\n", id2cstr(name)); } + +RTLIL::IdString RTLIL::Module::derive(RTLIL::Design*, dict<RTLIL::IdString, RTLIL::Const>, dict<RTLIL::IdString, RTLIL::Module*>, dict<RTLIL::IdString, RTLIL::IdString>, bool mayfail) +{ + if (mayfail) + return RTLIL::IdString(); + log_error("Module `%s' is used with parameters but is not parametric!\n", id2cstr(name)); +} + size_t RTLIL::Module::count_id(RTLIL::IdString id) { return wires_.count(id) + memories.count(id) + cells_.count(id) + processes.count(id); diff --git a/kernel/rtlil.h b/kernel/rtlil.h index 027faf416..276540aa1 100644 --- a/kernel/rtlil.h +++ b/kernel/rtlil.h @@ -907,7 +907,9 @@ public: Module(); virtual ~Module(); virtual RTLIL::IdString derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, bool mayfail = false); + virtual RTLIL::IdString derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, dict<RTLIL::IdString, RTLIL::Module*> interfaces, dict<RTLIL::IdString, RTLIL::IdString> modports, bool mayfail = false); virtual size_t count_id(RTLIL::IdString id); + virtual void reprocess_module(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Module *> local_interfaces); virtual void sort(); virtual void check(); diff --git a/passes/hierarchy/hierarchy.cc b/passes/hierarchy/hierarchy.cc index 5df69848e..7e93a6f9a 100644 --- a/passes/hierarchy/hierarchy.cc +++ b/passes/hierarchy/hierarchy.cc @@ -2,6 +2,7 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2018 Ruben Undheim <ruben.undheim@gmail.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -145,9 +146,25 @@ bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check std::map<RTLIL::Cell*, std::pair<int, int>> array_cells; std::string filename; + // Always keep track of all derived interfaces available in the current module in 'interfaces_in_module': + dict<RTLIL::IdString, RTLIL::Module*> interfaces_in_module; for (auto &cell_it : module->cells_) { RTLIL::Cell *cell = cell_it.second; + if(cell->get_bool_attribute("\\is_interface")) { + RTLIL::Module *intf_module = design->modules_[cell->type]; + interfaces_in_module[cell->name] = intf_module; + } + } + + for (auto &cell_it : module->cells_) + { + RTLIL::Cell *cell = cell_it.second; + bool has_interfaces_not_found = false; + + std::vector<RTLIL::IdString> connections_to_remove; + std::vector<RTLIL::IdString> connections_to_add_name; + std::vector<RTLIL::SigSpec> connections_to_add_signal; if (cell->type.substr(0, 7) == "$array:") { int pos_idx = cell->type.str().find_first_of(':'); @@ -158,6 +175,8 @@ bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check array_cells[cell] = std::pair<int, int>(idx, num); cell->type = cell->type.str().substr(pos_type + 1); } + dict<RTLIL::IdString, RTLIL::Module*> interfaces_to_add_to_submodule; + dict<RTLIL::IdString, RTLIL::IdString> modports_used_in_submodule; if (design->modules_.count(cell->type) == 0) { @@ -200,11 +219,76 @@ bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check if (design->modules_.count(cell->type) == 0) log_error("File `%s' from libdir does not declare module `%s'.\n", filename.c_str(), cell->type.c_str()); did_something = true; - } else + } else { + + RTLIL::Module *mod = design->module(cell->type); + + // Go over all connections and see if any of them are SV interfaces. If they are, then add the replacements to + // some lists, so that the ports for sub-modules can be replaced further down: + for (auto &conn : cell->connections()) { + if(mod->wires_.count(conn.first) != 0 && mod->wire(conn.first)->get_bool_attribute("\\is_interface")) { // Check if the connection is present as an interface in the sub-module's port list + //const pool<string> &interface_type_pool = mod->wire(conn.first)->get_strpool_attribute("\\interface_type"); + //for (auto &d : interface_type_pool) { // TODO: Compare interface type to type in parent module (not crucially important, but good for robustness) + //} + + // Find if the sub-module has set a modport for the current interface connection: + const pool<string> &interface_modport_pool = mod->wire(conn.first)->get_strpool_attribute("\\interface_modport"); + std::string interface_modport = ""; + for (auto &d : interface_modport_pool) { + interface_modport = "\\" + d; + } + if(conn.second.bits().size() == 1 && conn.second.bits()[0].wire->get_bool_attribute("\\is_interface")) { // Check if the connected wire is a potential interface in the parent module + std::string interface_name_str = conn.second.bits()[0].wire->name.str(); + interface_name_str.replace(0,23,""); // Strip the prefix '$dummywireforinterface' from the dummy wire to get the name + interface_name_str = "\\" + interface_name_str; + RTLIL::IdString interface_name = interface_name_str; + bool not_found_interface = false; + if(module->get_bool_attribute("\\interfaces_replaced_in_module")) { // If 'interfaces' in the cell have not be been handled yet, there is no need to derive the sub-module either + if (interfaces_in_module.count(interface_name) > 0) { // Check if the interface instance is present in module + RTLIL::Module *mod_replace_ports = interfaces_in_module.at(interface_name); + for (auto &mod_wire : mod_replace_ports->wires_) { // Go over all wires in interface, and add replacements to lists. + std::string signal_name1 = conn.first.str() + "." + log_id(mod_wire.first); + std::string signal_name2 = interface_name.str() + "." + log_id(mod_wire.first); + connections_to_add_name.push_back(RTLIL::IdString(signal_name1)); + if(module->wires_.count(signal_name2) == 0) { + log_error("Could not find signal '%s' in '%s'\n", signal_name2.c_str(), log_id(module->name)); + } + else { + RTLIL::Wire *wire_in_parent = module->wire(signal_name2); + connections_to_add_signal.push_back(wire_in_parent); + } + } + connections_to_remove.push_back(conn.first); + interfaces_to_add_to_submodule[conn.first] = interfaces_in_module.at(interface_name); + + // Add modports to a dict which will be passed to AstModule::derive + if (interface_modport != "") { + modports_used_in_submodule[conn.first] = interface_modport; + } + } + else not_found_interface = true; + } + else not_found_interface = true; + // If the interface instance has not already been derived in the module, we cannot complete at this stage. Set "has_interfaces_not_found" + // which will delay the expansion of this cell: + if (not_found_interface) { + // If we have already gone over all cells in this module, and the interface has still not been found - flag it as an error: + if(!(module->get_bool_attribute("\\cells_not_processed"))) { + log_warning("Could not find interface instance for `%s' in `%s'\n", log_id(interface_name), log_id(module)); + } + else { + // Only set has_interfaces_not_found if it would be possible to find them, since otherwiser we will end up in an infinite loop: + has_interfaces_not_found = true; + } + } + } + } + } + // + if (flag_check || flag_simcheck) { - RTLIL::Module *mod = design->module(cell->type); - for (auto &conn : cell->connections()) + for (auto &conn : cell->connections()) { if (conn.first[0] == '$' && '0' <= conn.first[1] && conn.first[1] <= '9') { int id = atoi(conn.first.c_str()+1); if (id <= 0 || id > GetSize(mod->ports)) @@ -213,11 +297,15 @@ bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check } else if (mod->wire(conn.first) == nullptr || mod->wire(conn.first)->port_id == 0) log_error("Module `%s' referenced in module `%s' in cell `%s' does not have a port named '%s'.\n", log_id(cell->type), log_id(module), log_id(cell), log_id(conn.first)); + } for (auto ¶m : cell->parameters) if (mod->avail_parameters.count(param.first) == 0 && param.first[0] != '$' && strchr(param.first.c_str(), '.') == NULL) log_error("Module `%s' referenced in module `%s' in cell `%s' does not have a parameter named '%s'.\n", log_id(cell->type), log_id(module), log_id(cell), log_id(param.first)); + + } } + RTLIL::Module *mod = design->modules_[cell->type]; if (design->modules_.at(cell->type)->get_bool_attribute("\\blackbox")) { if (flag_simcheck) @@ -226,15 +314,62 @@ bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check continue; } - if (cell->parameters.size() == 0) + // If interface instances not yet found, skip cell for now, and say we did something, so that we will return back here: + if(has_interfaces_not_found) { + did_something = true; // waiting for interfaces to be handled continue; + } - RTLIL::Module *mod = design->modules_[cell->type]; - cell->type = mod->derive(design, cell->parameters); + // Do the actual replacements of the SV interface port connection with the individual signal connections: + for(unsigned int i=0;i<connections_to_add_name.size();i++) { + cell->connections_[connections_to_add_name[i]] = connections_to_add_signal[i]; + } + // Remove the connection for the interface itself: + for(unsigned int i=0;i<connections_to_remove.size();i++) { + cell->connections_.erase(connections_to_remove[i]); + } + + // If there are no overridden parameters AND not interfaces, then we can use the existing module instance as the type + // for the cell: + if (cell->parameters.size() == 0 && (interfaces_to_add_to_submodule.size() == 0 || !(cell->get_bool_attribute("\\module_not_derived")))) { + // If the cell being processed is an the interface instance itself, go down to "handle_interface_instance:", + // so that the signals of the interface are added to the parent module. + if (mod->get_bool_attribute("\\is_interface")) { + goto handle_interface_instance; + } + continue; + } + + cell->type = mod->derive(design, cell->parameters, interfaces_to_add_to_submodule, modports_used_in_submodule); cell->parameters.clear(); did_something = true; + + handle_interface_instance: + + // We add all the signals of the interface explicitly to the parent module. This is always needed when we encounter + // an interface instance: + if (mod->get_bool_attribute("\\is_interface") && cell->get_bool_attribute("\\module_not_derived")) { + cell->set_bool_attribute("\\is_interface"); + RTLIL::Module *derived_module = design->modules_[cell->type]; + interfaces_in_module[cell->name] = derived_module; + did_something = true; + } + // We clear 'module_not_derived' such that we will not rederive the cell again (needed when there are interfaces connected to the cell) + cell->attributes.erase("\\module_not_derived"); + } + // Clear the attribute 'cells_not_processed' such that it can be known that we + // have been through all cells at least once, and that we can know whether + // to flag an error because of interface instances not found: + module->attributes.erase("\\cells_not_processed"); + + + // If any interface instances were found in the module, we need to rederive it completely: + if (interfaces_in_module.size() > 0 && !module->get_bool_attribute("\\interfaces_replaced_in_module")) { + module->reprocess_module(design, interfaces_in_module); + return did_something; } + for (auto &it : array_cells) { RTLIL::Cell *cell = it.first; @@ -341,6 +476,20 @@ int find_top_mod_score(Design *design, Module *module, dict<Module*, int> &db) return db.at(module); } +RTLIL::Module *check_if_top_has_changed(Design *design, Module *top_mod) +{ + if(top_mod != NULL && top_mod->get_bool_attribute("\\initial_top")) + return top_mod; + else { + for (auto mod : design->modules()) { + if (mod->get_bool_attribute("\\top")) { + return mod; + } + } + } + return NULL; +} + struct HierarchyPass : public Pass { HierarchyPass() : Pass("hierarchy", "check, expand and clean up design hierarchy") { } void help() YS_OVERRIDE @@ -568,6 +717,14 @@ struct HierarchyPass : public Pass { if (flag_simcheck && top_mod == nullptr) log_error("Design has no top module.\n"); + if (top_mod != NULL) { + for (auto &mod_it : design->modules_) + if (mod_it.second == top_mod) + mod_it.second->attributes["\\initial_top"] = RTLIL::Const(1); + else + mod_it.second->attributes.erase("\\initial_top"); + } + bool did_something = true; while (did_something) { @@ -586,19 +743,43 @@ struct HierarchyPass : public Pass { if (expand_module(design, module, flag_check, flag_simcheck, libdirs)) did_something = true; } + + + // The top module might have changed if interface instances have been detected in it: + RTLIL::Module *tmp_top_mod = check_if_top_has_changed(design, top_mod); + if (tmp_top_mod != NULL) { + if (tmp_top_mod != top_mod){ + top_mod = tmp_top_mod; + did_something = true; + } + } + + // Delete modules marked as 'to_delete': + std::vector<RTLIL::Module *> modules_to_delete; + for(auto &mod_it : design->modules_) { + if (mod_it.second->get_bool_attribute("\\to_delete")) { + modules_to_delete.push_back(mod_it.second); + } + } + for(size_t i=0; i<modules_to_delete.size(); i++) { + design->remove(modules_to_delete[i]); + } } + if (top_mod != NULL) { log_header(design, "Analyzing design hierarchy..\n"); hierarchy_clean(design, top_mod, purge_lib); } if (top_mod != NULL) { - for (auto &mod_it : design->modules_) + for (auto &mod_it : design->modules_) { if (mod_it.second == top_mod) mod_it.second->attributes["\\top"] = RTLIL::Const(1); else mod_it.second->attributes.erase("\\top"); + mod_it.second->attributes.erase("\\initial_top"); + } } if (!nokeep_asserts) { diff --git a/tests/simple/svinterface1.sv b/tests/simple/svinterface1.sv new file mode 100644 index 000000000..64383a06c --- /dev/null +++ b/tests/simple/svinterface1.sv @@ -0,0 +1,90 @@ + + +module TopModule( + input logic clk, + input logic rst, + input logic [1:0] sig, + output logic [1:0] sig_out); + + MyInterface #(.WIDTH(4)) MyInterfaceInstance(); + + SubModule1 u_SubModule1 ( + .clk(clk), + .rst(rst), + .u_MyInterface(MyInterfaceInstance), + .sig (sig) + ); + + assign sig_out = MyInterfaceInstance.mysig_out; + + + assign MyInterfaceInstance.setting = 1; +// assign MyInterfaceInstance.other_setting[2:0] = 3'b101; + +endmodule + +interface MyInterface #( + parameter WIDTH = 3)( + ); + + logic setting; + logic [WIDTH-1:0] other_setting; + + logic [1:0] mysig_out; + + modport submodule1 ( + input setting, + output other_setting, + output mysig_out + ); + + modport submodule2 ( + input setting, + output other_setting, + input mysig_out + ); + +endinterface + + +module SubModule1( + input logic clk, + input logic rst, + MyInterface.submodule1 u_MyInterface, + input logic [1:0] sig + + ); + + always_ff @(posedge clk or posedge rst) + if(rst) + u_MyInterface.mysig_out <= 0; + else begin + if(u_MyInterface.setting) + u_MyInterface.mysig_out <= sig; + else + u_MyInterface.mysig_out <= ~sig; + end + + MyInterface #(.WIDTH(22)) MyInterfaceInstanceInSub(); + + SubModule2 u_SubModule2 ( + .clk(clk), + .rst(rst), + .u_MyInterfaceInSub2(u_MyInterface), + .sig (sig) + ); + +endmodule + +module SubModule2( + + input logic clk, + input logic rst, + MyInterface.submodule2 u_MyInterfaceInSub2, + input logic [1:0] sig + + ); + + assign u_MyInterfaceInSub2.other_setting[2:0] = 9; + +endmodule |