From 963cad919ccfa6cee9cb63008d916762e22d4f2d Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Tue, 21 Apr 2026 22:44:21 +0200 Subject: [PATCH] hierarchy: refactor --- passes/hierarchy/Makefile.inc | 1 + passes/hierarchy/hierarchy.cc | 1335 +------------------------- passes/hierarchy/hierarchy.h | 0 passes/hierarchy/util/Makefile.inc | 7 + passes/hierarchy/util/clean.cc | 85 ++ passes/hierarchy/util/clean.h | 35 + passes/hierarchy/util/generate.cc | 176 ++++ passes/hierarchy/util/generate.h | 44 + passes/hierarchy/util/interfaces.cc | 525 ++++++++++ passes/hierarchy/util/interfaces.h | 79 ++ passes/hierarchy/util/misc.cc | 95 ++ passes/hierarchy/util/misc.h | 49 + passes/hierarchy/util/positionals.cc | 93 ++ passes/hierarchy/util/positionals.h | 34 + passes/hierarchy/util/top.cc | 220 +++++ passes/hierarchy/util/top.h | 49 + passes/hierarchy/util/verilog.cc | 351 +++++++ passes/hierarchy/util/verilog.h | 37 + 18 files changed, 1907 insertions(+), 1308 deletions(-) create mode 100644 passes/hierarchy/hierarchy.h create mode 100644 passes/hierarchy/util/Makefile.inc create mode 100644 passes/hierarchy/util/clean.cc create mode 100644 passes/hierarchy/util/clean.h create mode 100644 passes/hierarchy/util/generate.cc create mode 100644 passes/hierarchy/util/generate.h create mode 100644 passes/hierarchy/util/interfaces.cc create mode 100644 passes/hierarchy/util/interfaces.h create mode 100644 passes/hierarchy/util/misc.cc create mode 100644 passes/hierarchy/util/misc.h create mode 100644 passes/hierarchy/util/positionals.cc create mode 100644 passes/hierarchy/util/positionals.h create mode 100644 passes/hierarchy/util/top.cc create mode 100644 passes/hierarchy/util/top.h create mode 100644 passes/hierarchy/util/verilog.cc create mode 100644 passes/hierarchy/util/verilog.h diff --git a/passes/hierarchy/Makefile.inc b/passes/hierarchy/Makefile.inc index ff1a2fcc5..9e7efd349 100644 --- a/passes/hierarchy/Makefile.inc +++ b/passes/hierarchy/Makefile.inc @@ -5,3 +5,4 @@ OBJS += passes/hierarchy/uniquify.o OBJS += passes/hierarchy/submod.o OBJS += passes/hierarchy/keep_hierarchy.o +include $(YOSYS_SRC)/passes/hierarchy/util/Makefile.inc diff --git a/passes/hierarchy/hierarchy.cc b/passes/hierarchy/hierarchy.cc index 416997bee..469bbb180 100644 --- a/passes/hierarchy/hierarchy.cc +++ b/passes/hierarchy/hierarchy.cc @@ -19,7 +19,13 @@ */ #include "kernel/yosys.h" -#include "frontends/verific/verific.h" +#include "passes/hierarchy/util/interfaces.h" +#include "passes/hierarchy/util/misc.h" +#include "passes/hierarchy/util/clean.h" +#include "passes/hierarchy/util/top.h" +#include "passes/hierarchy/util/positionals.h" +#include "passes/hierarchy/util/verilog.h" +#include "passes/hierarchy/util/generate.h" #include #include #include @@ -30,729 +36,10 @@ USING_YOSYS_NAMESPACE +using namespace Hierarchy; + PRIVATE_NAMESPACE_BEGIN -struct generate_port_decl_t { - bool input, output; - string portname; - int index; -}; - -void generate(RTLIL::Design *design, const std::vector &celltypes, const std::vector &portdecls) -{ - std::set found_celltypes; - - for (auto mod : design->modules()) - for (auto cell : mod->cells()) - { - if (design->module(cell->type) != nullptr) - continue; - if (cell->type.begins_with("$") && !cell->type.begins_with("$__")) - continue; - for (auto &pattern : celltypes) - if (patmatch(pattern.c_str(), RTLIL::unescape_id(cell->type).c_str())) - found_celltypes.insert(cell->type); - } - - for (auto &celltype : found_celltypes) - { - std::set portnames; - std::set parameters; - std::map portwidths; - log("Generate module for cell type %s:\n", celltype); - - for (auto mod : design->modules()) - for (auto cell : mod->cells()) - if (cell->type == celltype) { - for (auto &conn : cell->connections()) { - if (conn.first[0] != '$') - portnames.insert(conn.first); - portwidths[conn.first] = max(portwidths[conn.first], conn.second.size()); - } - for (auto ¶ : cell->parameters) - parameters.insert(para.first); - } - - for (auto &decl : portdecls) - if (decl.index > 0) - portnames.insert(decl.portname); - - std::set indices; - for (int i = 0; i < int(portnames.size()); i++) - indices.insert(i+1); - - std::vector ports(portnames.size()); - - for (auto &decl : portdecls) - if (decl.index > 0) { - portwidths[decl.portname] = max(portwidths[decl.portname], 1); - portwidths[decl.portname] = max(portwidths[decl.portname], portwidths[stringf("$%d", decl.index)]); - log(" port %d: %s [%d:0] %s\n", decl.index, decl.input ? decl.output ? "inout" : "input" : "output", portwidths[decl.portname]-1, RTLIL::id2cstr(decl.portname)); - if (indices.count(decl.index) > ports.size()) - log_error("Port index (%d) exceeds number of found ports (%d).\n", decl.index, int(ports.size())); - if (indices.count(decl.index) == 0) - log_error("Conflict on port index %d.\n", decl.index); - indices.erase(decl.index); - portnames.erase(decl.portname); - ports[decl.index-1] = decl; - } - - while (portnames.size() > 0) { - RTLIL::IdString portname = *portnames.begin(); - for (auto &decl : portdecls) - if (decl.index == 0 && patmatch(decl.portname.c_str(), RTLIL::unescape_id(portname).c_str())) { - generate_port_decl_t d = decl; - d.portname = portname.str(); - d.index = *indices.begin(); - log_assert(!indices.empty()); - indices.erase(d.index); - ports[d.index-1] = d; - portwidths[d.portname] = max(portwidths[d.portname], 1); - log(" port %d: %s [%d:0] %s\n", d.index, d.input ? d.output ? "inout" : "input" : "output", portwidths[d.portname]-1, RTLIL::id2cstr(d.portname)); - goto found_matching_decl; - } - log_error("Can't match port %s.\n", RTLIL::id2cstr(portname)); - found_matching_decl:; - portnames.erase(portname); - } - - log_assert(indices.empty()); - - RTLIL::Module *mod = new RTLIL::Module; - mod->name = celltype; - mod->attributes[ID::blackbox] = RTLIL::Const(1); - design->add(mod); - - for (auto &decl : ports) { - RTLIL::Wire *wire = mod->addWire(decl.portname, portwidths.at(decl.portname)); - wire->port_id = decl.index; - wire->port_input = decl.input; - wire->port_output = decl.output; - } - - mod->fixup_ports(); - - for (auto ¶ : parameters) - log(" ignoring parameter %s.\n", RTLIL::id2cstr(para)); - - log(" module %s created.\n", RTLIL::id2cstr(mod->name)); - } -} - -// Return the "basic" type for an array item. -std::string basic_cell_type(const std::string celltype, int pos[3] = nullptr) { - std::string basicType = celltype; - if (celltype.compare(0, strlen("$array:"), "$array:") == 0) { - int pos_idx = celltype.find_first_of(':'); - int pos_num = celltype.find_first_of(':', pos_idx + 1); - int pos_type = celltype.find_first_of(':', pos_num + 1); - basicType = celltype.substr(pos_type + 1); - if (pos != nullptr) { - pos[0] = pos_idx; - pos[1] = pos_num; - pos[2] = pos_type; - } - } - return basicType; -} - -// Try to read an IdString as a numbered connection name ("$123" or similar), -// writing the result to dst. If the string isn't of the right format, ignore -// dst and return false. -bool read_id_num(RTLIL::IdString str, int *dst) -{ - log_assert(dst); - - const char *c_str = str.c_str(); - if (c_str[0] != '$' || !('0' <= c_str[1] && c_str[1] <= '9')) - return false; - - *dst = atoi(c_str + 1); - return true; -} - -// A helper struct for expanding a module's interface connections in expand_module -struct IFExpander -{ - IFExpander (RTLIL::Design &design, RTLIL::Module &m) - : module(m), has_interfaces_not_found(false) - { - // Keep track of all derived interfaces available in the current - // module in 'interfaces_in_module': - for (auto cell : module.cells()) { - if(!cell->get_bool_attribute(ID::is_interface)) - continue; - - interfaces_in_module[cell->name] = design.module(cell->type); - } - } - - RTLIL::Module &module; - dict interfaces_in_module; - - bool has_interfaces_not_found; - std::vector connections_to_remove; - std::vector connections_to_add_name; - std::vector connections_to_add_signal; - dict interfaces_to_add_to_submodule; - dict modports_used_in_submodule; - - // Reset the per-cell state - void start_cell() - { - has_interfaces_not_found = false; - connections_to_remove.clear(); - connections_to_add_name.clear(); - connections_to_add_signal.clear(); - interfaces_to_add_to_submodule.clear(); - modports_used_in_submodule.clear(); - } - - // Set has_interfaces_not_found if there are pending interfaces that - // haven't been found yet (and might be found in the future). Print a - // warning if we've already gone over all the cells in the module. - void on_missing_interface(RTLIL::IdString interface_name) - { - // If there are cells that haven't yet been processed, maybe - // we'll find this interface in the future. - if (module.get_bool_attribute(ID::cells_not_processed)) { - has_interfaces_not_found = true; - return; - } - - // Otherwise, we have already gone over all cells in this - // module and the interface has still not been found. Warn - // about it and don't set has_interfaces_not_found (to avoid a - // loop). - log_warning("Could not find interface instance for `%s' in `%s'\n", - log_id(interface_name), log_id(&module)); - } - - // Handle an interface connection from the module - void on_interface(RTLIL::Module &submodule, - RTLIL::IdString conn_name, - const RTLIL::SigSpec &conn_signals) - { - // Check if the connected wire is a potential interface in the parent module - std::string interface_name_str = conn_signals[0].wire->name.str(); - // Strip the prefix '$dummywireforinterface' from the dummy wire to get the name - interface_name_str.replace(0,23,""); - interface_name_str = "\\" + interface_name_str; - RTLIL::IdString interface_name = interface_name_str; - - // If 'interfaces' in the cell have not be been handled yet, we aren't - // ready to derive the sub-module either - if (!module.get_bool_attribute(ID::interfaces_replaced_in_module)) { - on_missing_interface(interface_name); - return; - } - - // Check if the interface instance is present in module. Interface - // instances may either have the plain name or the name appended with - // '_inst_from_top_dummy'. Check for both of them here - int nexactmatch = interfaces_in_module.count(interface_name) > 0; - std::string interface_name_str2 = interface_name_str + "_inst_from_top_dummy"; - RTLIL::IdString interface_name2 = interface_name_str2; - int nmatch2 = interfaces_in_module.count(interface_name2) > 0; - - // If we can't find either name, this is a missing interface. - if (! (nexactmatch || nmatch2)) { - on_missing_interface(interface_name); - return; - } - - if (nexactmatch != 0) // Choose the one with the plain name if it exists - interface_name2 = interface_name; - - RTLIL::Module *mod_replace_ports = interfaces_in_module.at(interface_name2); - - // Go over all wires in interface, and add replacements to lists. - for (auto mod_wire : mod_replace_ports->wires()) { - std::string signal_name1 = conn_name.str() + "." + log_id(mod_wire->name); - std::string signal_name2 = interface_name.str() + "." + log_id(mod_wire); - connections_to_add_name.push_back(RTLIL::IdString(signal_name1)); - if(module.wire(signal_name2) == nullptr) { - 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_name); - interfaces_to_add_to_submodule[conn_name] = interfaces_in_module.at(interface_name2); - - // Find if the sub-module has set a modport for the current interface - // connection. Add any modports to a dict which will be passed to - // AstModule::derive - string modport_name = submodule.wire(conn_name)->get_string_attribute(ID::interface_modport); - if (!modport_name.empty()) { - modports_used_in_submodule[conn_name] = "\\" + modport_name; - } - } - - // Handle a single connection from the module, making a note to expand - // it if it's an interface connection. - void on_connection(RTLIL::Module &submodule, - RTLIL::IdString conn_name, - const RTLIL::SigSpec &conn_signals) - { - // Does the connection look like an interface - if ( - conn_signals.size() != 1 || - conn_signals[0].wire == nullptr || - conn_signals[0].wire->get_bool_attribute(ID::is_interface) == false || - conn_signals[0].wire->name.str().find("$dummywireforinterface") != 0 - ) - return; - - // Check if the connection is present as an interface in the sub-module's port list - int id; - if (read_id_num(conn_name, &id)) { - /* Interface expansion is incompatible with positional arguments - * during expansion, the port list gets each interface signal - * inserted after the interface itself which means that the argument - * positions in the parent module no longer match. - * - * Supporting this would require expanding the interfaces in the - * parent module, renumbering the arguments to match, and then - * iterating over the ports list to find the matching interface - * (refactoring on_interface to accept different conn_names on the - * parent and child). - */ - log_error("Unable to connect `%s' to submodule `%s' with positional interface argument `%s'!\n", - module.name, - submodule.name, - conn_signals[0].wire->name.str().substr(23) - ); - } else { - // Lookup connection by name - const RTLIL::Wire *wire = submodule.wire(conn_name); - if (!wire || !wire->get_bool_attribute(ID::is_interface)) - return; - } - // If the connection looks like an interface, handle it. - on_interface(submodule, conn_name, conn_signals); - } - - // Iterate over the connections in a cell, tracking any interface - // connections - void visit_connections(const RTLIL::Cell &cell, - RTLIL::Module &submodule) - { - for (const auto &conn : cell.connections()) { - on_connection(submodule, conn.first, conn.second); - } - } - - // Add/remove connections to the cell as necessary, replacing any SV - // interface port connection with the individual signal connections. - void rewrite_interface_connections(RTLIL::Cell &cell) const - { - for(unsigned int i=0;i &libdirs) -{ - std::string cell_type = cell.type.str(); - RTLIL::Module *abs_mod = design.module("$abstract" + cell_type); - if (abs_mod) { - cell.type = abs_mod->derive(&design, cell.parameters); - cell.parameters.clear(); - RTLIL::Module *mod = design.module(cell.type); - log_assert(mod); - return mod; - } - - // If the cell type starts with '$' and isn't '$abstract', we should - // treat it as a black box and skip. - if (cell_type[0] == '$') - return nullptr; - - for (auto &dir : libdirs) { - static const vector> extensions_list = - { - {".v", "verilog"}, - {".sv", "verilog -sv"}, - {".il", "rtlil"} - }; - - for (auto &ext : extensions_list) { - std::string filename = dir + "/" + RTLIL::unescape_id(cell.type) + ext.first; - if (!check_file_exists(filename)) - continue; - - Frontend::frontend_call(&design, NULL, filename, ext.second); - RTLIL::Module *mod = design.module(cell.type); - if (!mod) - log_error("File `%s' from libdir does not declare module `%s'.\n", - filename.c_str(), cell_type.c_str()); - return mod; - } - } - - // We couldn't find the module anywhere. Complain if check is set. - if (check) - log_error("Module `%s' referenced in module `%s' in cell `%s' is not part of the design.\n", - cell_type.c_str(), parent.name.c_str(), cell.name.c_str()); - - return nullptr; -} - -// Check that the connections on the cell match those that are defined -// on the type: each named connection should match the name of a port -// and each positional connection should have an index smaller than -// the number of ports. -// -// Also do the same checks on the specified parameters. -void check_cell_connections(const RTLIL::Module &module, RTLIL::Cell &cell, RTLIL::Module &mod) -{ - int id; - for (auto &conn : cell.connections()) { - if (read_id_num(conn.first, &id)) { - if (id <= 0 || id > GetSize(mod.ports)) - log_error("Module `%s' referenced in module `%s' in cell `%s' " - "has only %d ports, requested port %d.\n", - log_id(cell.type), log_id(&module), log_id(&cell), - GetSize(mod.ports), id); - continue; - } - - const RTLIL::Wire* wire = mod.wire(conn.first); - if (!wire || wire->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 (read_id_num(param.first, &id)) { - if (id <= 0 || id > GetSize(mod.avail_parameters)) - log_error("Module `%s' referenced in module `%s' in cell `%s' " - "has only %d parameters, requested parameter %d.\n", - log_id(cell.type), log_id(&module), log_id(&cell), - GetSize(mod.avail_parameters), id); - continue; - } - - 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)); - } - } -} - -bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check, bool flag_simcheck, bool flag_smtcheck, - std::vector &libdirs) -{ - bool did_something = false; - std::map> array_cells; - std::string filename; - - bool has_interface_ports = false; - - // If any of the ports are actually interface ports, we will always need to - // reprocess the module: - if(!module->get_bool_attribute(ID::interfaces_replaced_in_module)) { - for (auto wire : module->wires()) { - if ((wire->port_input || wire->port_output) && wire->get_bool_attribute(ID::is_interface)) - has_interface_ports = true; - } - } - - IFExpander if_expander(*design, *module); - - for (auto cell : module->cells()) - { - if_expander.start_cell(); - - if (cell->type.begins_with("$array:")) { - int pos[3]; - basic_cell_type(cell->type.str(), pos); - int pos_idx = pos[0]; - int pos_num = pos[1]; - int pos_type = pos[2]; - int idx = atoi(cell->type.substr(pos_idx + 1, pos_num).c_str()); - int num = atoi(cell->type.substr(pos_num + 1, pos_type).c_str()); - array_cells[cell] = std::pair(idx, num); - cell->type = cell->type.substr(pos_type + 1); - } - - RTLIL::Module *mod = design->module(cell->type); - if (!mod) - { - mod = get_module(*design, *cell, *module, flag_check || flag_simcheck || flag_smtcheck, libdirs); - - // If we still don't have a module, treat the cell as a black box and skip - // it. Otherwise, we either loaded or derived something so should set the - // did_something flag before returning (to ensure we come back and expand - // the thing we just loaded). - if (mod) - did_something = true; - - continue; - } - - log_assert(mod); - - // Go over all connections and check if any of them are SV - // interfaces. - if_expander.visit_connections(*cell, *mod); - - if (flag_check || flag_simcheck || flag_smtcheck) - check_cell_connections(*module, *cell, *mod); - - if (mod->get_blackbox_attribute()) { - if (flag_simcheck || (flag_smtcheck && !mod->get_bool_attribute(ID::smtlib2_module))) - log_error("Module `%s' referenced in module `%s' in cell `%s' is a blackbox/whitebox module.\n", - cell->type.c_str(), module->name.c_str(), cell->name.c_str()); - continue; - } - - // If interface instances not yet found, skip cell for now, and say we did something, so that we will return back here: - if(if_expander.has_interfaces_not_found) { - did_something = true; // waiting for interfaces to be handled - continue; - } - - if_expander.rewrite_interface_connections(*cell); - - // 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 && - (if_expander.interfaces_to_add_to_submodule.size() == 0 || - !(cell->get_bool_attribute(ID::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(ID::is_interface)) { - goto handle_interface_instance; - } - continue; - } - - cell->type = mod->derive(design, - cell->parameters, - if_expander.interfaces_to_add_to_submodule, - if_expander.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(ID::is_interface) && cell->get_bool_attribute(ID::module_not_derived)) { - cell->set_bool_attribute(ID::is_interface); - RTLIL::Module *derived_module = design->module(cell->type); - if_expander.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(ID::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(ID::cells_not_processed); - - - // If any interface instances or interface ports were found in the module, we need to rederive it completely: - if ((if_expander.interfaces_in_module.size() > 0 || has_interface_ports) && !module->get_bool_attribute(ID::interfaces_replaced_in_module)) { - module->expand_interfaces(design, if_expander.interfaces_in_module); - return did_something; - } - - // Now that modules have been derived, we may want to reprocess this - // module given the additional available context. - if (module->reprocess_if_necessary(design)) - return true; - - for (auto &it : array_cells) - { - RTLIL::Cell *cell = it.first; - int idx = it.second.first, num = it.second.second; - - if (design->module(cell->type) == nullptr) - log_error("Array cell `%s.%s' of unknown type `%s'.\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(cell->name), RTLIL::id2cstr(cell->type)); - - RTLIL::Module *mod = design->module(cell->type); - - for (auto &conn : cell->connections_) { - int conn_size = conn.second.size(); - RTLIL::IdString portname = conn.first; - if (portname.begins_with("$")) { - int port_id = atoi(portname.substr(1).c_str()); - for (auto wire : mod->wires()) - if (wire->port_id == port_id) { - portname = wire->name; - break; - } - } - if (mod->wire(portname) == nullptr) - log_error("Array cell `%s.%s' connects to unknown port `%s'.\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(cell->name), RTLIL::id2cstr(conn.first)); - int port_size = mod->wire(portname)->width; - if (conn_size == port_size || conn_size == 0) - continue; - if (conn_size != port_size*num) - log_error("Array cell `%s.%s' has invalid port vs. signal size for port `%s'.\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(cell->name), RTLIL::id2cstr(conn.first)); - conn.second = conn.second.extract(port_size*idx, port_size); - } - } - - return did_something; -} - -void hierarchy_worker(RTLIL::Design *design, std::set> &used, RTLIL::Module *mod, int indent) -{ - if (used.count(mod) > 0) - return; - - if (indent == 0) - log("Top module: %s\n", mod->name); - else if (!mod->get_blackbox_attribute()) - log("Used module: %*s%s\n", indent, "", mod->name); - used.insert(mod); - - for (auto cell : mod->cells()) { - std::string celltype = cell->type.str(); - if (celltype.compare(0, strlen("$array:"), "$array:") == 0) - celltype = basic_cell_type(celltype); - if (design->module(celltype)) - hierarchy_worker(design, used, design->module(celltype), indent+4); - } -} - -void hierarchy_clean(RTLIL::Design *design, RTLIL::Module *top, bool purge_lib) -{ - std::set> used; - hierarchy_worker(design, used, top, 0); - - std::vector del_modules; - for (auto mod : design->modules()) - if (used.count(mod) == 0) - del_modules.push_back(mod); - else { - // Now all interface ports must have been exploded, and it is hence - // safe to delete all of the remaining dummy interface ports: - pool del_wires; - for(auto wire : mod->wires()) { - if ((wire->port_input || wire->port_output) && wire->get_bool_attribute(ID::is_interface)) { - del_wires.insert(wire); - } - } - if (del_wires.size() > 0) { - mod->remove(del_wires); - mod->fixup_ports(); - } - } - - int del_counter = 0; - for (auto mod : del_modules) { - if (!purge_lib && mod->get_blackbox_attribute()) - continue; - log("Removing unused module `%s'.\n", mod->name); - design->remove(mod); - del_counter++; - } - - log("Removed %d unused modules.\n", del_counter); -} - -bool set_keep_print(std::map &cache, RTLIL::Module *mod) -{ - if (cache.count(mod) == 0) - for (auto c : mod->cells()) { - RTLIL::Module *m = mod->design->module(c->type); - if ((m != nullptr && set_keep_print(cache, m)) || c->type == ID($print)) - return cache[mod] = true; - } - return cache[mod]; -} - -bool set_keep_assert(std::map &cache, RTLIL::Module *mod) -{ - if (cache.count(mod) == 0) - for (auto c : mod->cells()) { - RTLIL::Module *m = mod->design->module(c->type); - if ((m != nullptr && set_keep_assert(cache, m)) || c->type.in(ID($check), ID($assert), ID($assume), ID($live), ID($fair), ID($cover))) - return cache[mod] = true; - } - return cache[mod]; -} - -int find_top_mod_score(Design *design, Module *module, dict &db) -{ - if (db.count(module) == 0) { - int score = 0; - db[module] = 0; - for (auto cell : module->cells()) { - std::string celltype = cell->type.str(); - // Is this an array instance - if (celltype.compare(0, strlen("$array:"), "$array:") == 0) - celltype = basic_cell_type(celltype); - // Is this cell a module instance? - auto instModule = design->module(celltype); - // If there is no instance for this, issue a warning. - if (instModule != nullptr) { - score = max(score, find_top_mod_score(design, instModule, db) + 1); - } - } - db[module] = score; - } - 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(ID::initial_top)) - return top_mod; - else { - for (auto mod : design->modules()) { - if (mod->get_bool_attribute(ID::top)) { - return mod; - } - } - } - return NULL; -} - -// Find a matching wire for an implicit port connection; traversing generate block scope -RTLIL::Wire *find_implicit_port_wire(Module *module, Cell *cell, const std::string& port) -{ - const std::string &cellname = cell->name.str(); - size_t idx = cellname.size(); - while ((idx = cellname.find_last_of('.', idx-1)) != std::string::npos) { - Wire *found = module->wire(cellname.substr(0, idx+1) + port.substr(1)); - if (found != nullptr) - return found; - } - return module->wire(port); -} - struct HierarchyPass : public Pass { HierarchyPass() : Pass("hierarchy", "check, expand and clean up design hierarchy") { } void help() override @@ -852,8 +139,7 @@ struct HierarchyPass : public Pass { bool flag_simcheck = false; bool flag_smtcheck = false; bool purge_lib = false; - RTLIL::Module *top_mod = NULL; - std::string load_top_mod; + std::string top_mod_name_to_load = ""; std::vector libdirs; bool auto_top_mode = false; @@ -863,49 +149,18 @@ struct HierarchyPass : public Pass { bool nodefaults = false; bool nokeep_prints = false; bool nokeep_asserts = false; - std::vector generate_cells; - std::vector generate_ports; + Generator generator; + std::map parameters; size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { - if (args[argidx] == "-generate" && !flag_check && !flag_simcheck && !flag_smtcheck && !top_mod) { + if (args[argidx] == "-generate" && !flag_check && !flag_simcheck && !flag_smtcheck) { generate_mode = true; log("Entering generate mode.\n"); while (++argidx < args.size()) { - const char *p = args[argidx].c_str(); - generate_port_decl_t decl; - if (p[0] == 'i' && p[1] == 'o') - decl.input = true, decl.output = true, p += 2; - else if (*p == 'i') - decl.input = true, decl.output = false, p++; - else if (*p == 'o') - decl.input = false, decl.output = true, p++; - else - goto is_celltype; - if (*p == '@') { - char *endptr; - decl.index = strtol(++p, &endptr, 10); - if (decl.index < 1) - goto is_celltype; - p = endptr; - } else - decl.index = 0; - if (*(p++) != ':') - goto is_celltype; - if (*p == 0) - goto is_celltype; - decl.portname = p; - log("Port declaration: %s", decl.input ? decl.output ? "inout" : "input" : "output"); - if (decl.index >= 1) - log(" [at position %d]", decl.index); - log(" %s\n", decl.portname); - generate_ports.push_back(decl); - continue; - is_celltype: - log("Celltype: %s\n", args[argidx]); - generate_cells.push_back(RTLIL::unescape_id(args[argidx])); + generator.parse_arg(args[argidx].c_str()); } continue; } @@ -952,7 +207,7 @@ struct HierarchyPass : public Pass { if (args[argidx] == "-top") { if (++argidx >= args.size()) log_cmd_error("Option -top requires an additional argument!\n"); - load_top_mod = args[argidx]; + top_mod_name_to_load = args[argidx]; continue; } if (args[argidx] == "-auto-top") { @@ -973,570 +228,34 @@ struct HierarchyPass : public Pass { } extra_args(args, argidx, design, false); - if (!load_top_mod.empty()) - { - IdString top_name = RTLIL::escape_id(load_top_mod); - IdString abstract_id = "$abstract" + RTLIL::escape_id(load_top_mod); - top_mod = design->module(top_name); - - dict top_parameters; - if ((top_mod == nullptr && design->module(abstract_id)) || top_mod != nullptr) { - for (auto ¶ : parameters) { - SigSpec sig_value; - if (!RTLIL::SigSpec::parse(sig_value, NULL, para.second)) - log_cmd_error("Can't decode value '%s'!\n", para.second); - top_parameters[RTLIL::escape_id(para.first)] = sig_value.as_const(); - } - } - - if (top_mod == nullptr && design->module(abstract_id)) - top_mod = design->module(design->module(abstract_id)->derive(design, top_parameters)); - else if (top_mod != nullptr && !top_parameters.empty()) - top_mod = design->module(top_mod->derive(design, top_parameters)); - - if (top_mod != nullptr && top_mod->name != top_name) { - Module *m = top_mod->clone(); - m->name = top_name; - Module *old_mod = design->module(top_name); - if (old_mod) - design->remove(old_mod); - design->add(m); - top_mod = m; - } - } - - bool verific_mod = false; -#ifdef YOSYS_ENABLE_VERIFIC - verific_mod = verific_import_pending; -#endif - - if (top_mod == nullptr && !load_top_mod.empty()) { -#ifdef YOSYS_ENABLE_VERIFIC - if (verific_import_pending) { - load_top_mod = verific_import(design, parameters, load_top_mod); - top_mod = design->module(RTLIL::escape_id(load_top_mod)); - } -#endif - if (top_mod == NULL) - log_cmd_error("Module `%s' not found!\n", load_top_mod); - } else { -#ifdef YOSYS_ENABLE_VERIFIC - if (verific_import_pending) - verific_import(design, parameters); -#endif - } - - if (generate_mode) { - generate(design, generate_cells, generate_ports); - return; - } + if (generate_mode) + return generator.generate(design); log_push(); - if (top_mod == nullptr) - for (auto mod : design->modules()) - if (mod->get_bool_attribute(ID::top)) { - log("Attribute `top' found on module `%s'. Setting top module to %s.\n", log_id(mod), log_id(mod)); - top_mod = mod; - } - - if (top_mod == nullptr) - { - std::vector abstract_ids; - for (auto module : design->modules()) - if (module->name.begins_with("$abstract")) - abstract_ids.push_back(module->name); - for (auto abstract_id : abstract_ids) - design->module(abstract_id)->derive(design, {}); - for (auto abstract_id : abstract_ids) - design->remove(design->module(abstract_id)); - } - - if (top_mod == nullptr && auto_top_mode) { - log_header(design, "Finding top of design hierarchy..\n"); - dict db; - for (Module *mod : design->selected_modules()) { - int score = find_top_mod_score(design, mod, db); - log("root of %3d design levels: %-20s\n", score, log_id(mod)); - if (!top_mod || score > db[top_mod]) - top_mod = mod; - } - if (top_mod != nullptr) - log("Automatically selected %s as design top module.\n", log_id(top_mod)); - } - - if (top_mod != nullptr && top_mod->name.begins_with("$abstract")) { - IdString top_name = top_mod->name.substr(strlen("$abstract")); - - dict top_parameters; - for (auto ¶ : parameters) { - SigSpec sig_value; - if (!RTLIL::SigSpec::parse(sig_value, NULL, para.second)) - log_cmd_error("Can't decode value '%s'!\n", para.second); - top_parameters[RTLIL::escape_id(para.first)] = sig_value.as_const(); - } - - top_mod = design->module(top_mod->derive(design, top_parameters)); - - if (top_mod != nullptr && top_mod->name != top_name) { - Module *m = top_mod->clone(); - m->name = top_name; - Module *old_mod = design->module(top_name); - if (old_mod) - design->remove(old_mod); - design->add(m); - top_mod = m; - } - } + TopModulePrepare top {design, parameters}; + Module* top_mod = top.load_top(top_mod_name_to_load, auto_top_mode); if ((flag_simcheck || flag_smtcheck) && top_mod == nullptr) - log_error("Design has no top module.\n"); - - if (top_mod != NULL) { - for (auto mod : design->modules()) - if (mod == top_mod) - mod->attributes[ID::initial_top] = RTLIL::Const(1); - else - mod->attributes.erase(ID::initial_top); - } - - bool did_something = true; - while (did_something) - { - did_something = false; - - std::set> used_modules; - if (top_mod != NULL) { - log_header(design, "Analyzing design hierarchy..\n"); - hierarchy_worker(design, used_modules, top_mod, 0); - } else { - for (auto mod : design->modules()) - used_modules.insert(mod); - } - - for (auto module : used_modules) { - if (expand_module(design, module, flag_check, flag_simcheck, flag_smtcheck, 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; - top_mod->attributes[ID::initial_top] = RTLIL::Const(1); - did_something = true; - } - } - - // Delete modules marked as 'to_delete': - std::vector modules_to_delete; - for(auto mod : design->modules()) { - if (mod->get_bool_attribute(ID::to_delete)) { - modules_to_delete.push_back(mod); - } - } - for(size_t i=0; iremove(modules_to_delete[i]); - } - } + log_error("Design has no top module.\n"); + expand_all_interfaces(design, top_mod, flag_check, flag_simcheck, flag_smtcheck, libdirs); if (top_mod != NULL) { log_header(design, "Analyzing design hierarchy..\n"); - hierarchy_clean(design, top_mod, purge_lib); + clean(design, top_mod, purge_lib); } - if (top_mod != NULL) { - for (auto mod : design->modules()) { - if (mod == top_mod) - mod->attributes[ID::top] = RTLIL::Const(1); - else - mod->attributes.erase(ID::top); - mod->attributes.erase(ID::initial_top); - } - } + if (top_mod != NULL) + ensure_unique_top_attribute(top_mod, design); - if (!nokeep_prints) { - std::map cache; - for (auto mod : design->modules()) - if (set_keep_print(cache, mod)) { - log("Module %s directly or indirectly displays text -> setting \"keep\" attribute.\n", log_id(mod)); - mod->set_bool_attribute(ID::keep); - } - } - - if (!nokeep_asserts) { - std::map cache; - for (auto mod : design->modules()) - if (set_keep_assert(cache, mod)) { - log("Module %s directly or indirectly contains formal properties -> setting \"keep\" attribute.\n", log_id(mod)); - mod->set_bool_attribute(ID::keep); - } - } + set_keeps(design, !nokeep_prints, !nokeep_asserts); if (flag_simcheck || flag_smtcheck) { - for (auto mod : design->modules()) { - for (auto cell : mod->cells()) { - if (!cell->type.in(ID($check), ID($assert), ID($assume), ID($live), ID($fair), ID($cover))) - continue; - if (!cell->has_attribute(ID(unsupported_sva))) - continue; - - auto src = cell->get_src_attribute(); - - if (!src.empty()) - src += ": "; - - log_error("%sProperty `%s' in module `%s' uses unsupported SVA constructs. See frontend warnings for details, run `chformal -remove a:unsupported_sva' to ignore.\n", - src, log_id(cell->name), log_id(mod->name)); - } - } + check_supported_formal(design); } - if (!keep_positionals) - { - std::set pos_mods; - std::map, RTLIL::IdString> pos_map; - std::vector> pos_work; - - for (auto mod : design->modules()) - for (auto cell : mod->cells()) { - RTLIL::Module *cell_mod = design->module(cell->type); - if (cell_mod == nullptr) - continue; - for (auto &conn : cell->connections()) - if (conn.first[0] == '$' && '0' <= conn.first[1] && conn.first[1] <= '9') { - pos_mods.insert(design->module(cell->type)); - pos_work.push_back(std::pair(mod, cell)); - break; - } - - pool> params_rename; - for (const auto &p : cell->parameters) { - int id; - if (read_id_num(p.first, &id)) { - if (id <= 0 || id > GetSize(cell_mod->avail_parameters)) { - log(" Failed to map positional parameter %d of cell %s.%s (%s).\n", - id, RTLIL::id2cstr(mod->name), RTLIL::id2cstr(cell->name), RTLIL::id2cstr(cell->type)); - } else { - params_rename.insert(std::make_pair(p.first, cell_mod->avail_parameters[id - 1])); - } - } - } - for (const auto &p : params_rename) { - cell->setParam(p.second, cell->getParam(p.first)); - cell->unsetParam(p.first); - } - } - - for (auto module : pos_mods) - for (auto wire : module->wires()) { - if (wire->port_id > 0) - pos_map[std::pair(module, wire->port_id)] = wire->name; - } - - for (auto &work : pos_work) { - RTLIL::Module *module = work.first; - RTLIL::Cell *cell = work.second; - log("Mapping positional arguments of cell %s.%s (%s).\n", - RTLIL::id2cstr(module->name), RTLIL::id2cstr(cell->name), RTLIL::id2cstr(cell->type)); - dict new_connections; - for (auto &conn : cell->connections()) { - int id; - if (read_id_num(conn.first, &id)) { - std::pair key(design->module(cell->type), id); - if (pos_map.count(key) == 0) { - log(" Failed to map positional argument %d of cell %s.%s (%s).\n", - id, RTLIL::id2cstr(module->name), RTLIL::id2cstr(cell->name), RTLIL::id2cstr(cell->type)); - new_connections[conn.first] = conn.second; - } else - new_connections[pos_map.at(key)] = conn.second; - } else - new_connections[conn.first] = conn.second; - } - cell->connections_ = new_connections; - } - } - - // Determine default values - dict> defaults_db; - if (!nodefaults) - { - for (auto module : design->modules()) - for (auto wire : module->wires()) - if (wire->port_input && wire->attributes.count(ID::defaultvalue)) - defaults_db[module->name][wire->name] = wire->attributes.at(ID::defaultvalue); - } - // Process SV implicit wildcard port connections - std::set blackbox_derivatives; - std::vector design_modules = design->modules(); - - for (auto module : design_modules) - { - for (auto cell : module->cells()) - { - if (!cell->get_bool_attribute(ID::wildcard_port_conns)) - continue; - Module *m = design->module(cell->type); - - if (m == nullptr) - log_error("Cell %s.%s (%s) has implicit port connections but the module it instantiates is unknown.\n", - RTLIL::id2cstr(module->name), RTLIL::id2cstr(cell->name), RTLIL::id2cstr(cell->type)); - - // Need accurate port widths for error checking; so must derive blackboxes with dynamic port widths - if (m->get_blackbox_attribute() && !cell->parameters.empty() && m->get_bool_attribute(ID::dynports)) { - IdString new_m_name = m->derive(design, cell->parameters, true); - if (new_m_name.empty()) - continue; - if (new_m_name != m->name) { - m = design->module(new_m_name); - blackbox_derivatives.insert(m); - } - } - - auto old_connections = cell->connections(); - for (auto wire : m->wires()) { - // Find ports of the module that aren't explicitly connected - if (!wire->port_input && !wire->port_output) - continue; - if (old_connections.count(wire->name)) - continue; - // Make sure a wire of correct name exists in the parent - Wire* parent_wire = find_implicit_port_wire(module, cell, wire->name.str()); - - // Missing wires are OK when a default value is set - if (!nodefaults && parent_wire == nullptr && defaults_db.count(cell->type) && defaults_db.at(cell->type).count(wire->name)) - continue; - - if (parent_wire == nullptr) - log_error("No matching wire for implicit port connection `%s' of cell %s.%s (%s).\n", - RTLIL::id2cstr(wire->name), RTLIL::id2cstr(module->name), RTLIL::id2cstr(cell->name), RTLIL::id2cstr(cell->type)); - if (parent_wire->width != wire->width) - log_error("Width mismatch between wire (%d bits) and port (%d bits) for implicit port connection `%s' of cell %s.%s (%s).\n", - parent_wire->width, wire->width, - RTLIL::id2cstr(wire->name), RTLIL::id2cstr(module->name), RTLIL::id2cstr(cell->name), RTLIL::id2cstr(cell->type)); - cell->setPort(wire->name, parent_wire); - } - cell->attributes.erase(ID::wildcard_port_conns); - } - } - - if (!nodefaults) - { - for (auto module : design->modules()) - for (auto cell : module->cells()) - { - if (defaults_db.count(cell->type) == 0) - continue; - - if (keep_positionals) { - bool found_positionals = false; - for (auto &conn : cell->connections()) - if (conn.first[0] == '$' && '0' <= conn.first[1] && conn.first[1] <= '9') - found_positionals = true; - if (found_positionals) - continue; - } - - for (auto &it : defaults_db.at(cell->type)) - if (!cell->hasPort(it.first)) - cell->setPort(it.first, it.second); - } - } - - for (auto module : design_modules) - { - pool wand_wor_index; - dict wand_map, wor_map; - vector new_connections; - - for (auto wire : module->wires()) - { - if (wire->get_bool_attribute(ID::wand)) { - wand_map[wire] = SigSpec(); - wand_wor_index.insert(wire); - } - if (wire->get_bool_attribute(ID::wor)) { - wor_map[wire] = SigSpec(); - wand_wor_index.insert(wire); - } - } - - for (auto &conn : module->connections()) - { - SigSig new_conn; - int cursor = 0; - - for (auto c : conn.first.chunks()) - { - Wire *w = c.wire; - SigSpec rhs = conn.second.extract(cursor, GetSize(c)); - - if (wand_wor_index.count(w) == 0) { - new_conn.first.append(c); - new_conn.second.append(rhs); - } else { - if (wand_map.count(w)) { - SigSpec sig = SigSpec(State::S1, GetSize(w)); - sig.replace(c.offset, rhs); - wand_map.at(w).append(sig); - } else { - SigSpec sig = SigSpec(State::S0, GetSize(w)); - sig.replace(c.offset, rhs); - wor_map.at(w).append(sig); - } - } - cursor += GetSize(c); - } - new_connections.push_back(new_conn); - } - module->new_connections(new_connections); - - for (auto cell : module->cells()) - { - if (!cell->known()) - continue; - - for (auto &conn : cell->connections()) - { - if (!cell->output(conn.first)) - continue; - - SigSpec new_sig; - bool update_port = false; - - for (auto c : conn.second.chunks()) - { - Wire *w = c.wire; - - if (wand_wor_index.count(w) == 0) { - new_sig.append(c); - continue; - } - - Wire *t = module->addWire(NEW_ID, GetSize(c)); - new_sig.append(t); - update_port = true; - - if (wand_map.count(w)) { - SigSpec sig = SigSpec(State::S1, GetSize(w)); - sig.replace(c.offset, t); - wand_map.at(w).append(sig); - } else { - SigSpec sig = SigSpec(State::S0, GetSize(w)); - sig.replace(c.offset, t); - wor_map.at(w).append(sig); - } - } - - if (update_port) - cell->setPort(conn.first, new_sig); - } - } - - for (auto w : wand_wor_index) - { - bool wand = wand_map.count(w); - SigSpec sigs = wand ? wand_map.at(w) : wor_map.at(w); - - if (GetSize(sigs) == 0) - continue; - - if (GetSize(w) == 1) { - if (wand) - module->addReduceAnd(NEW_ID, sigs, w); - else - module->addReduceOr(NEW_ID, sigs, w); - continue; - } - - SigSpec s = sigs.extract(0, GetSize(w)); - for (int i = GetSize(w); i < GetSize(sigs); i += GetSize(w)) { - if (wand) - s = module->And(NEW_ID, s, sigs.extract(i, GetSize(w))); - else - s = module->Or(NEW_ID, s, sigs.extract(i, GetSize(w))); - } - module->connect(w, s); - } - - for (auto cell : module->cells()) - { - Module *m = design->module(cell->type); - - if (m == nullptr) - continue; - - bool boxed_params = false; - if (m->get_blackbox_attribute() && !cell->parameters.empty()) { - if (m->get_bool_attribute(ID::dynports)) { - IdString new_m_name = m->derive(design, cell->parameters, true); - if (new_m_name.empty()) - continue; - if (new_m_name != m->name) { - m = design->module(new_m_name); - blackbox_derivatives.insert(m); - } - } else { - boxed_params = true; - } - } - - for (auto &conn : cell->connections()) - { - Wire *w = m->wire(conn.first); - - if (w == nullptr || w->port_id == 0) - continue; - - if (GetSize(conn.second) == 0) - continue; - - SigSpec sig = conn.second; - - bool resize_widths = !keep_portwidths && GetSize(w) != GetSize(conn.second); - if (resize_widths && verific_mod && boxed_params) - log_debug("Ignoring width mismatch on %s.%s.%s from verific, is port width parametrizable?\n", - log_id(module), log_id(cell), log_id(conn.first) - ); - else if (resize_widths) { - if (GetSize(w) < GetSize(conn.second)) - { - int n = GetSize(conn.second) - GetSize(w); - if (!w->port_input && w->port_output) - { - RTLIL::SigSpec out = sig.extract(0, GetSize(w)); - out.extend_u0(GetSize(sig), w->is_signed); - module->connect(sig.extract(GetSize(w), n), out.extract(GetSize(w), n)); - } - sig.remove(GetSize(w), n); - } - else - { - int n = GetSize(w) - GetSize(conn.second); - if (w->port_input && !w->port_output) - sig.extend_u0(GetSize(w), sig.is_wire() && sig.as_wire()->is_signed); - else - sig.append(module->addWire(NEW_ID, n)); - } - - if (!conn.second.is_fully_const() || !w->port_input || w->port_output) - log_warning("Resizing cell port %s.%s.%s from %d bits to %d bits.\n", log_id(module), log_id(cell), - log_id(conn.first), GetSize(conn.second), GetSize(sig)); - cell->setPort(conn.first, sig); - } - - if (w->port_output && !w->port_input && sig.has_const()) - log_error("Output port %s.%s.%s (%s) is connected to constants: %s\n", - log_id(module), log_id(cell), log_id(conn.first), log_id(cell->type), log_signal(sig)); - } - } - } - - for (auto module : blackbox_derivatives) - design->remove(module); + resolve_verilog(design, nodefaults, keep_positionals, keep_portwidths, top.is_from_verific); log_pop(); } diff --git a/passes/hierarchy/hierarchy.h b/passes/hierarchy/hierarchy.h new file mode 100644 index 000000000..e69de29bb diff --git a/passes/hierarchy/util/Makefile.inc b/passes/hierarchy/util/Makefile.inc new file mode 100644 index 000000000..764fbe925 --- /dev/null +++ b/passes/hierarchy/util/Makefile.inc @@ -0,0 +1,7 @@ +OBJS += passes/hierarchy/util/clean.o +OBJS += passes/hierarchy/util/generate.o +OBJS += passes/hierarchy/util/interfaces.o +OBJS += passes/hierarchy/util/misc.o +OBJS += passes/hierarchy/util/positionals.o +OBJS += passes/hierarchy/util/top.o +OBJS += passes/hierarchy/util/verilog.o diff --git a/passes/hierarchy/util/clean.cc b/passes/hierarchy/util/clean.cc new file mode 100644 index 000000000..8bc040275 --- /dev/null +++ b/passes/hierarchy/util/clean.cc @@ -0,0 +1,85 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Claire Xenia Wolf + * Copyright (C) 2018 Ruben Undheim + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/yosys_common.h" +#include "passes/hierarchy/util/misc.h" +#include "passes/hierarchy/util/clean.h" + +YOSYS_NAMESPACE_BEGIN +namespace Hierarchy { + +void clean(RTLIL::Design *design, RTLIL::Module *top, bool purge_lib) +{ + std::set> used; + mark_used(design, used, top, 0); + + std::vector del_modules; + for (auto mod : design->modules()) + if (used.count(mod) == 0) + del_modules.push_back(mod); + else { + // Now all interface ports must have been exploded, and it is hence + // safe to delete all of the remaining dummy interface ports: + pool del_wires; + for(auto wire : mod->wires()) { + if ((wire->port_input || wire->port_output) && wire->get_bool_attribute(ID::is_interface)) { + del_wires.insert(wire); + } + } + if (del_wires.size() > 0) { + mod->remove(del_wires); + mod->fixup_ports(); + } + } + + int del_counter = 0; + for (auto mod : del_modules) { + if (!purge_lib && mod->get_blackbox_attribute()) + continue; + log("Removing unused module `%s'.\n", mod->name); + design->remove(mod); + del_counter++; + } + + log("Removed %d unused modules.\n", del_counter); +} + +void mark_used(RTLIL::Design *design, std::set> &used, RTLIL::Module *mod, int indent) +{ + if (used.count(mod) > 0) + return; + + if (indent == 0) + log("Top module: %s\n", mod->name); + else if (!mod->get_blackbox_attribute()) + log("Used module: %*s%s\n", indent, "", mod->name); + used.insert(mod); + + for (auto cell : mod->cells()) { + std::string celltype = cell->type.str(); + if (auto array_type = try_make_array(celltype)) + celltype = array_type->name; + if (design->module(celltype)) + mark_used(design, used, design->module(celltype), indent+4); + } +} + +}; +YOSYS_NAMESPACE_END \ No newline at end of file diff --git a/passes/hierarchy/util/clean.h b/passes/hierarchy/util/clean.h new file mode 100644 index 000000000..5570b7aed --- /dev/null +++ b/passes/hierarchy/util/clean.h @@ -0,0 +1,35 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Claire Xenia Wolf + * Copyright (C) 2018 Ruben Undheim + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#ifndef HIERARCHY_CLEAN_H +#define HIERARCHY_CLEAN_H + +#include "kernel/yosys.h" + +YOSYS_NAMESPACE_BEGIN + +namespace Hierarchy { + void clean(RTLIL::Design *design, RTLIL::Module *top, bool purge_lib); + void mark_used(RTLIL::Design *design, std::set> &used, RTLIL::Module *mod, int indent); +}; + +YOSYS_NAMESPACE_END + +#endif /* HIERARCHY_CLEAN_H */ diff --git a/passes/hierarchy/util/generate.cc b/passes/hierarchy/util/generate.cc new file mode 100644 index 000000000..52b3c0ad5 --- /dev/null +++ b/passes/hierarchy/util/generate.cc @@ -0,0 +1,176 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Claire Xenia Wolf + * Copyright (C) 2018 Ruben Undheim + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/yosys_common.h" +#include "passes/hierarchy/util/generate.h" + +PRIVATE_NAMESPACE_BEGIN +USING_YOSYS_NAMESPACE +using namespace Hierarchy; + +static void generate_cell(const char* p, std::vector& cells) { + log("Celltype: %s\n", p); + cells.push_back(RTLIL::unescape_id(string(p))); +} +static void parse_one(const char* p, std::vector& cells, std::vector& ports) { + generate_port_decl_t decl; + if (p[0] == 'i' && p[1] == 'o') { + decl.input = true; + decl.output = true; + p += 2; + } else if (*p == 'i') { + decl.input = true; + decl.output = false; + p++; + } else if (*p == 'o') { + decl.input = false; + decl.output = true; + p++; + } else { + return generate_cell(p, cells); + } + if (*p == '@') { + char *endptr; + decl.index = strtol(++p, &endptr, 10); + if (decl.index < 1) + return generate_cell(p, cells); + p = endptr; + } else + decl.index = 0; + if (*(p++) != ':') + return generate_cell(p, cells); + if (*p == 0) + return generate_cell(p, cells); + decl.portname = p; + log("Port declaration: %s", decl.input ? decl.output ? "inout" : "input" : "output"); + if (decl.index >= 1) + log(" [at position %d]", decl.index); + log(" %s\n", decl.portname); + ports.push_back(decl); + return; +} + +PRIVATE_NAMESPACE_END + +YOSYS_NAMESPACE_BEGIN +namespace Hierarchy { + void Generator::generate(Design* design) { + std::set found_celltypes; + + for (auto mod : design->modules()) + for (auto cell : mod->cells()) + { + if (design->module(cell->type) != nullptr) + continue; + if (cell->type.begins_with("$") && !cell->type.begins_with("$__")) + continue; + for (auto &pattern : cells) + if (patmatch(pattern.c_str(), RTLIL::unescape_id(cell->type).c_str())) + found_celltypes.insert(cell->type); + } + + for (auto &celltype : found_celltypes) + { + std::set portnames; + std::set parameters; + std::map portwidths; + log("Generate module for cell type %s:\n", celltype); + + for (auto mod : design->modules()) + for (auto cell : mod->cells()) + if (cell->type == celltype) { + for (auto &conn : cell->connections()) { + if (conn.first[0] != '$') + portnames.insert(conn.first); + portwidths[conn.first] = max(portwidths[conn.first], conn.second.size()); + } + for (auto ¶ : cell->parameters) + parameters.insert(para.first); + } + + for (auto &decl : ports) + if (decl.index > 0) + portnames.insert(decl.portname); + + std::set indices; + for (int i = 0; i < int(portnames.size()); i++) + indices.insert(i+1); + + for (auto &decl : ports) + if (decl.index > 0) { + portwidths[decl.portname] = max(portwidths[decl.portname], 1); + portwidths[decl.portname] = max(portwidths[decl.portname], portwidths[stringf("$%d", decl.index)]); + log(" port %d: %s [%d:0] %s\n", decl.index, decl.input ? decl.output ? "inout" : "input" : "output", portwidths[decl.portname]-1, RTLIL::id2cstr(decl.portname)); + if (indices.count(decl.index) > ports.size()) + log_error("Port index (%d) exceeds number of found ports (%d).\n", decl.index, int(ports.size())); + if (indices.count(decl.index) == 0) + log_error("Conflict on port index %d.\n", decl.index); + indices.erase(decl.index); + portnames.erase(decl.portname); + ports[decl.index-1] = decl; + } + + while (portnames.size() > 0) { + RTLIL::IdString portname = *portnames.begin(); + for (auto &decl : ports) + if (decl.index == 0 && patmatch(decl.portname.c_str(), RTLIL::unescape_id(portname).c_str())) { + generate_port_decl_t d = decl; + d.portname = portname.str(); + d.index = *indices.begin(); + log_assert(!indices.empty()); + indices.erase(d.index); + ports[d.index-1] = d; + portwidths[d.portname] = max(portwidths[d.portname], 1); + log(" port %d: %s [%d:0] %s\n", d.index, d.input ? d.output ? "inout" : "input" : "output", portwidths[d.portname]-1, RTLIL::id2cstr(d.portname)); + goto found_matching_decl; + } + log_error("Can't match port %s.\n", RTLIL::id2cstr(portname)); + found_matching_decl:; + portnames.erase(portname); + } + + log_assert(indices.empty()); + + RTLIL::Module *mod = new RTLIL::Module; + mod->name = celltype; + mod->attributes[ID::blackbox] = RTLIL::Const(1); + design->add(mod); + + for (auto &decl : ports) { + RTLIL::Wire *wire = mod->addWire(decl.portname, portwidths.at(decl.portname)); + wire->port_id = decl.index; + wire->port_input = decl.input; + wire->port_output = decl.output; + } + + mod->fixup_ports(); + + for (auto ¶ : parameters) + log(" ignoring parameter %s.\n", RTLIL::id2cstr(para)); + + log(" module %s created.\n", RTLIL::id2cstr(mod->name)); + } + } + void Generator::parse_arg(const char* p) { + parse_one(p, cells, ports); + } + +}; +YOSYS_NAMESPACE_END \ No newline at end of file diff --git a/passes/hierarchy/util/generate.h b/passes/hierarchy/util/generate.h new file mode 100644 index 000000000..5896f12d7 --- /dev/null +++ b/passes/hierarchy/util/generate.h @@ -0,0 +1,44 @@ +/* +* yosys -- Yosys Open SYnthesis Suite +* +* Copyright (C) 2012 Claire Xenia Wolf +* Copyright (C) 2018 Ruben Undheim +* +* Permission to use, copy, modify, and/or distribute this software for any +* purpose with or without fee is hereby granted, provided that the above +* copyright notice and this permission notice appear in all copies. +* +* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +* +*/ + +#ifndef HIERARCHY_GENERATE_H +#define HIERARCHY_GENERATE_H + +#include "kernel/yosys.h" + +YOSYS_NAMESPACE_BEGIN + +namespace Hierarchy { + struct generate_port_decl_t { + bool input, output; + string portname; + int index; + }; + struct Generator { + std::vector cells; + std::vector ports; + void parse_arg(const char* p); + void generate(Design* design); + }; +}; + +YOSYS_NAMESPACE_END + +#endif /* HIERARCHY_GENERATE_H */ diff --git a/passes/hierarchy/util/interfaces.cc b/passes/hierarchy/util/interfaces.cc new file mode 100644 index 000000000..570d1303c --- /dev/null +++ b/passes/hierarchy/util/interfaces.cc @@ -0,0 +1,525 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Claire Xenia Wolf + * Copyright (C) 2018 Ruben Undheim + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/yosys_common.h" +#include "passes/hierarchy/util/misc.h" +#include "passes/hierarchy/util/interfaces.h" +#include "passes/hierarchy/util/clean.h" + +YOSYS_NAMESPACE_BEGIN +namespace Hierarchy { + +RTLIL::Module *check_if_top_has_changed(Design *design, Module *top_mod) +{ + if(top_mod != NULL && top_mod->get_bool_attribute(ID::initial_top)) + return top_mod; + else { + for (auto mod : design->modules()) { + if (mod->get_bool_attribute(ID::top)) { + return mod; + } + } + } + return NULL; +} + +// Check that the connections on the cell match those that are defined +// on the type: each named connection should match the name of a port +// and each positional connection should have an index smaller than +// the number of ports. +// +// Also do the same checks on the specified parameters. +void check_cell_connections(const RTLIL::Module &module, RTLIL::Cell &cell, RTLIL::Module &mod) +{ + int id; + for (auto &conn : cell.connections()) { + if (read_id_num(conn.first, &id)) { + if (id <= 0 || id > GetSize(mod.ports)) + log_error("Module `%s' referenced in module `%s' in cell `%s' " + "has only %d ports, requested port %d.\n", + log_id(cell.type), log_id(&module), log_id(&cell), + GetSize(mod.ports), id); + continue; + } + + const RTLIL::Wire* wire = mod.wire(conn.first); + if (!wire || wire->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 (read_id_num(param.first, &id)) { + if (id <= 0 || id > GetSize(mod.avail_parameters)) + log_error("Module `%s' referenced in module `%s' in cell `%s' " + "has only %d parameters, requested parameter %d.\n", + log_id(cell.type), log_id(&module), log_id(&cell), + GetSize(mod.avail_parameters), id); + continue; + } + + 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)); + } + } +} + + +// Get a module needed by a cell, either by deriving an abstract module or by +// loading one from a directory in libdirs. +// +// If the module can't be found and check is true then exit with an error +// message. Otherwise, return a pointer to the module if we derived or loaded +// something. or null otherwise (the module should be blackbox or we couldn't +// find it and check is not set). +RTLIL::Module *get_module(RTLIL::Design &design, + RTLIL::Cell &cell, + RTLIL::Module &parent, + bool check, + const std::vector &libdirs) +{ + std::string cell_type = cell.type.str(); + RTLIL::Module *abs_mod = design.module("$abstract" + cell_type); + if (abs_mod) { + cell.type = abs_mod->derive(&design, cell.parameters); + cell.parameters.clear(); + RTLIL::Module *mod = design.module(cell.type); + log_assert(mod); + return mod; + } + + // If the cell type starts with '$' and isn't '$abstract', we should + // treat it as a black box and skip. + if (cell_type[0] == '$') + return nullptr; + + for (auto &dir : libdirs) { + static const vector> extensions_list = + { + {".v", "verilog"}, + {".sv", "verilog -sv"}, + {".il", "rtlil"} + }; + + for (auto &ext : extensions_list) { + std::string filename = dir + "/" + RTLIL::unescape_id(cell.type) + ext.first; + if (!check_file_exists(filename)) + continue; + + Frontend::frontend_call(&design, NULL, filename, ext.second); + RTLIL::Module *mod = design.module(cell.type); + if (!mod) + log_error("File `%s' from libdir does not declare module `%s'.\n", + filename.c_str(), cell_type.c_str()); + return mod; + } + } + + // We couldn't find the module anywhere. Complain if check is set. + if (check) + log_error("Module `%s' referenced in module `%s' in cell `%s' is not part of the design.\n", + cell_type.c_str(), parent.name.c_str(), cell.name.c_str()); + + return nullptr; +} + + +void expand_all_interfaces(Design* design, Module* top_mod, bool flag_check, bool flag_simcheck, bool flag_smtcheck, const std::vector &libdirs) { + bool did_something = true; + while (did_something) + { + did_something = false; + + std::set> used_modules; + if (top_mod != NULL) { + log_header(design, "Analyzing design hierarchy..\n"); + mark_used(design, used_modules, top_mod, 0); + } else { + for (auto mod : design->modules()) + used_modules.insert(mod); + } + + for (auto module : used_modules) { + if (expand_module(design, module, flag_check, flag_simcheck, flag_smtcheck, 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; + top_mod->attributes[ID::initial_top] = RTLIL::Const(1); + did_something = true; + } + } + + // Delete modules marked as 'to_delete': + std::vector modules_to_delete; + for(auto mod : design->modules()) { + if (mod->get_bool_attribute(ID::to_delete)) { + modules_to_delete.push_back(mod); + } + } + for(size_t i=0; iremove(modules_to_delete[i]); + } + } +} + +bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check, bool flag_simcheck, bool flag_smtcheck, const std::vector &libdirs) +{ + bool did_something = false; + std::map> array_cells; + std::string filename; + + bool has_interface_ports = false; + + // If any of the ports are actually interface ports, we will always need to + // reprocess the module: + if(!module->get_bool_attribute(ID::interfaces_replaced_in_module)) { + for (auto wire : module->wires()) { + if ((wire->port_input || wire->port_output) && wire->get_bool_attribute(ID::is_interface)) + has_interface_ports = true; + } + } + + IFExpander if_expander(*design, *module); + + for (auto cell : module->cells()) + { + if_expander.start_cell(); + + if (auto a = try_make_array(cell->type.str())) { + int idx = atoi(cell->type.substr(a->pos_idx + 1, a->pos_num).c_str()); + int num = atoi(cell->type.substr(a->pos_num + 1, a->pos_type).c_str()); + array_cells[cell] = std::pair(idx, num); + cell->type = cell->type.substr(a->pos_type + 1); + } + + RTLIL::Module *mod = design->module(cell->type); + if (!mod) + { + mod = get_module(*design, *cell, *module, flag_check || flag_simcheck || flag_smtcheck, libdirs); + + // If we still don't have a module, treat the cell as a black box and skip + // it. Otherwise, we either loaded or derived something so should set the + // did_something flag before returning (to ensure we come back and expand + // the thing we just loaded). + if (mod) + did_something = true; + + continue; + } + + log_assert(mod); + + // Go over all connections and check if any of them are SV + // interfaces. + if_expander.visit_connections(*cell, *mod); + + if (flag_check || flag_simcheck || flag_smtcheck) + check_cell_connections(*module, *cell, *mod); + + if (mod->get_blackbox_attribute()) { + if (flag_simcheck || (flag_smtcheck && !mod->get_bool_attribute(ID::smtlib2_module))) + log_error("Module `%s' referenced in module `%s' in cell `%s' is a blackbox/whitebox module.\n", + cell->type.c_str(), module->name.c_str(), cell->name.c_str()); + continue; + } + + // If interface instances not yet found, skip cell for now, and say we did something, so that we will return back here: + if(if_expander.has_interfaces_not_found) { + did_something = true; // waiting for interfaces to be handled + continue; + } + + if_expander.rewrite_interface_connections(*cell); + + // 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 && + (if_expander.interfaces_to_add_to_submodule.size() == 0 || + !(cell->get_bool_attribute(ID::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(ID::is_interface)) { + goto handle_interface_instance; + } + continue; + } + + cell->type = mod->derive(design, + cell->parameters, + if_expander.interfaces_to_add_to_submodule, + if_expander.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(ID::is_interface) && cell->get_bool_attribute(ID::module_not_derived)) { + cell->set_bool_attribute(ID::is_interface); + RTLIL::Module *derived_module = design->module(cell->type); + if_expander.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(ID::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(ID::cells_not_processed); + + + // If any interface instances or interface ports were found in the module, we need to rederive it completely: + if ((if_expander.interfaces_in_module.size() > 0 || has_interface_ports) && !module->get_bool_attribute(ID::interfaces_replaced_in_module)) { + module->expand_interfaces(design, if_expander.interfaces_in_module); + return did_something; + } + + // Now that modules have been derived, we may want to reprocess this + // module given the additional available context. + if (module->reprocess_if_necessary(design)) + return true; + + for (auto &it : array_cells) + { + RTLIL::Cell *cell = it.first; + int idx = it.second.first, num = it.second.second; + + if (design->module(cell->type) == nullptr) + log_error("Array cell `%s.%s' of unknown type `%s'.\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(cell->name), RTLIL::id2cstr(cell->type)); + + RTLIL::Module *mod = design->module(cell->type); + + for (auto &conn : cell->connections_) { + int conn_size = conn.second.size(); + RTLIL::IdString portname = conn.first; + if (portname.begins_with("$")) { + int port_id = atoi(portname.substr(1).c_str()); + for (auto wire : mod->wires()) + if (wire->port_id == port_id) { + portname = wire->name; + break; + } + } + if (mod->wire(portname) == nullptr) + log_error("Array cell `%s.%s' connects to unknown port `%s'.\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(cell->name), RTLIL::id2cstr(conn.first)); + int port_size = mod->wire(portname)->width; + if (conn_size == port_size || conn_size == 0) + continue; + if (conn_size != port_size*num) + log_error("Array cell `%s.%s' has invalid port vs. signal size for port `%s'.\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(cell->name), RTLIL::id2cstr(conn.first)); + conn.second = conn.second.extract(port_size*idx, port_size); + } + } + + return did_something; +} + +IFExpander::IFExpander (RTLIL::Design &design, RTLIL::Module &m) + : module(m), has_interfaces_not_found(false) +{ + // Keep track of all derived interfaces available in the current + // module in 'interfaces_in_module': + for (auto cell : module.cells()) { + if(!cell->get_bool_attribute(ID::is_interface)) + continue; + + interfaces_in_module[cell->name] = design.module(cell->type); + } +} + +// Reset the per-cell state +void IFExpander::start_cell() +{ + has_interfaces_not_found = false; + connections_to_remove.clear(); + connections_to_add_name.clear(); + connections_to_add_signal.clear(); + interfaces_to_add_to_submodule.clear(); + modports_used_in_submodule.clear(); +} + +// Set has_interfaces_not_found if there are pending interfaces that +// haven't been found yet (and might be found in the future). Print a +// warning if we've already gone over all the cells in the module. +void IFExpander::on_missing_interface(RTLIL::IdString interface_name) +{ + // If there are cells that haven't yet been processed, maybe + // we'll find this interface in the future. + if (module.get_bool_attribute(ID::cells_not_processed)) { + has_interfaces_not_found = true; + return; + } + + // Otherwise, we have already gone over all cells in this + // module and the interface has still not been found. Warn + // about it and don't set has_interfaces_not_found (to avoid a + // loop). + log_warning("Could not find interface instance for `%s' in `%s'\n", + log_id(interface_name), log_id(&module)); +} + +// Handle an interface connection from the module +void IFExpander::on_interface(RTLIL::Module &submodule, + RTLIL::IdString conn_name, + const RTLIL::SigSpec &conn_signals) +{ + // Check if the connected wire is a potential interface in the parent module + std::string interface_name_str = conn_signals[0].wire->name.str(); + // Strip the prefix '$dummywireforinterface' from the dummy wire to get the name + interface_name_str.replace(0,23,""); + interface_name_str = "\\" + interface_name_str; + RTLIL::IdString interface_name = interface_name_str; + + // If 'interfaces' in the cell have not be been handled yet, we aren't + // ready to derive the sub-module either + if (!module.get_bool_attribute(ID::interfaces_replaced_in_module)) { + on_missing_interface(interface_name); + return; + } + + // Check if the interface instance is present in module. Interface + // instances may either have the plain name or the name appended with + // '_inst_from_top_dummy'. Check for both of them here + int nexactmatch = interfaces_in_module.count(interface_name) > 0; + std::string interface_name_str2 = interface_name_str + "_inst_from_top_dummy"; + RTLIL::IdString interface_name2 = interface_name_str2; + int nmatch2 = interfaces_in_module.count(interface_name2) > 0; + + // If we can't find either name, this is a missing interface. + if (! (nexactmatch || nmatch2)) { + on_missing_interface(interface_name); + return; + } + + if (nexactmatch != 0) // Choose the one with the plain name if it exists + interface_name2 = interface_name; + + RTLIL::Module *mod_replace_ports = interfaces_in_module.at(interface_name2); + + // Go over all wires in interface, and add replacements to lists. + for (auto mod_wire : mod_replace_ports->wires()) { + std::string signal_name1 = conn_name.str() + "." + log_id(mod_wire->name); + std::string signal_name2 = interface_name.str() + "." + log_id(mod_wire); + connections_to_add_name.push_back(RTLIL::IdString(signal_name1)); + if(module.wire(signal_name2) == nullptr) { + 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_name); + interfaces_to_add_to_submodule[conn_name] = interfaces_in_module.at(interface_name2); + + // Find if the sub-module has set a modport for the current interface + // connection. Add any modports to a dict which will be passed to + // AstModule::derive + string modport_name = submodule.wire(conn_name)->get_string_attribute(ID::interface_modport); + if (!modport_name.empty()) { + modports_used_in_submodule[conn_name] = "\\" + modport_name; + } +} + +// Handle a single connection from the module, making a note to expand +// it if it's an interface connection. +void IFExpander::on_connection(RTLIL::Module &submodule, + RTLIL::IdString conn_name, + const RTLIL::SigSpec &conn_signals) +{ + // Does the connection look like an interface + if ( + conn_signals.size() != 1 || + conn_signals[0].wire == nullptr || + conn_signals[0].wire->get_bool_attribute(ID::is_interface) == false || + conn_signals[0].wire->name.str().find("$dummywireforinterface") != 0 + ) + return; + + // Check if the connection is present as an interface in the sub-module's port list + int id; + if (read_id_num(conn_name, &id)) { + /* Interface expansion is incompatible with positional arguments + * during expansion, the port list gets each interface signal + * inserted after the interface itself which means that the argument + * positions in the parent module no longer match. + * + * Supporting this would require expanding the interfaces in the + * parent module, renumbering the arguments to match, and then + * iterating over the ports list to find the matching interface + * (refactoring on_interface to accept different conn_names on the + * parent and child). + */ + log_error("Unable to connect `%s' to submodule `%s' with positional interface argument `%s'!\n", + module.name, + submodule.name, + conn_signals[0].wire->name.str().substr(23) + ); + } else { + // Lookup connection by name + const RTLIL::Wire *wire = submodule.wire(conn_name); + if (!wire || !wire->get_bool_attribute(ID::is_interface)) + return; + } + // If the connection looks like an interface, handle it. + on_interface(submodule, conn_name, conn_signals); +} + +// Iterate over the connections in a cell, tracking any interface +// connections +void IFExpander::visit_connections(const RTLIL::Cell &cell, + RTLIL::Module &submodule) +{ + for (const auto &conn : cell.connections()) { + on_connection(submodule, conn.first, conn.second); + } +} + +// Add/remove connections to the cell as necessary, replacing any SV +// interface port connection with the individual signal connections. +void IFExpander::rewrite_interface_connections(RTLIL::Cell &cell) const +{ + for(unsigned int i=0;i + * Copyright (C) 2018 Ruben Undheim + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#ifndef HIERARCHY_INTERFACES_H +#define HIERARCHY_INTERFACES_H + +#include "kernel/yosys.h" + +YOSYS_NAMESPACE_BEGIN +namespace Hierarchy { + +void expand_all_interfaces(Design* design, Module* top_mod, bool flag_check, bool flag_simcheck, bool flag_smtcheck, const std::vector &libdirs); +bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check, bool flag_simcheck, bool flag_smtcheck, const std::vector &libdirs); + +// For expanding a module's interface connections +struct IFExpander +{ + IFExpander (RTLIL::Design &design, RTLIL::Module &m); + + RTLIL::Module &module; + dict interfaces_in_module; + + bool has_interfaces_not_found; + std::vector connections_to_remove; + std::vector connections_to_add_name; + std::vector connections_to_add_signal; + dict interfaces_to_add_to_submodule; + dict modports_used_in_submodule; + + // Reset the per-cell state + void start_cell(); + + // Set has_interfaces_not_found if there are pending interfaces that + // haven't been found yet (and might be found in the future). Print a + // warning if we've already gone over all the cells in the module. + void on_missing_interface(RTLIL::IdString interface_name); + + // Handle an interface connection from the module + void on_interface(RTLIL::Module &submodule, + RTLIL::IdString conn_name, + const RTLIL::SigSpec &conn_signals); + + // Handle a single connection from the module, making a note to expand + // it if it's an interface connection. + void on_connection(RTLIL::Module &submodule, + RTLIL::IdString conn_name, + const RTLIL::SigSpec &conn_signals); + + // Iterate over the connections in a cell, tracking any interface + // connections + void visit_connections(const RTLIL::Cell &cell, + RTLIL::Module &submodule); + + // Add/remove connections to the cell as necessary, replacing any SV + // interface port connection with the individual signal connections. + void rewrite_interface_connections(RTLIL::Cell &cell) const; +}; + +}; +YOSYS_NAMESPACE_END + +#endif /* HIERARCHY_INTERFACES_H */ diff --git a/passes/hierarchy/util/misc.cc b/passes/hierarchy/util/misc.cc new file mode 100644 index 000000000..b56979e1c --- /dev/null +++ b/passes/hierarchy/util/misc.cc @@ -0,0 +1,95 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Claire Xenia Wolf + * Copyright (C) 2018 Ruben Undheim + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/yosys_common.h" +#include "passes/hierarchy/util/misc.h" + +YOSYS_NAMESPACE_BEGIN + +namespace Hierarchy { + +std::optional try_make_array(const std::string celltype) { + if (celltype.compare(0, strlen("$array:"), "$array:") == 0) { + int pos_idx = celltype.find_first_of(':'); + int pos_num = celltype.find_first_of(':', pos_idx + 1); + int pos_type = celltype.find_first_of(':', pos_num + 1); + return ArrayType { + celltype.substr(pos_type + 1), + pos_idx, + pos_num, + pos_type, + }; + } + return std::nullopt; +} + +bool read_id_num(RTLIL::IdString str, int *dst) { + log_assert(dst); + + const char *c_str = str.c_str(); + if (c_str[0] != '$' || !('0' <= c_str[1] && c_str[1] <= '9')) + return false; + + *dst = atoi(c_str + 1); + return true; +} + +void set_keeps(Design* design, bool keep_prints, bool keep_asserts) { + if (keep_prints) { + std::map cache; + for (auto mod : design->modules()) + if (set_keep_print(cache, mod)) { + log("Module %s directly or indirectly displays text -> setting \"keep\" attribute.\n", log_id(mod)); + mod->set_bool_attribute(ID::keep); + } + } + + if (keep_asserts) { + std::map cache; + for (auto mod : design->modules()) + if (set_keep_assert(cache, mod)) { + log("Module %s directly or indirectly contains formal properties -> setting \"keep\" attribute.\n", log_id(mod)); + mod->set_bool_attribute(ID::keep); + } + } +} + +bool set_keep_print(std::map &cache, RTLIL::Module *mod) { + if (cache.count(mod) == 0) + for (auto c : mod->cells()) { + RTLIL::Module *m = mod->design->module(c->type); + if ((m != nullptr && set_keep_print(cache, m)) || c->type == ID($print)) + return cache[mod] = true; + } + return cache[mod]; +} + +bool set_keep_assert(std::map &cache, RTLIL::Module *mod) { + if (cache.count(mod) == 0) + for (auto c : mod->cells()) { + RTLIL::Module *m = mod->design->module(c->type); + if ((m != nullptr && set_keep_assert(cache, m)) || c->type.in(ID($check), ID($assert), ID($assume), ID($live), ID($fair), ID($cover))) + return cache[mod] = true; + } + return cache[mod]; +} + +}; +YOSYS_NAMESPACE_END diff --git a/passes/hierarchy/util/misc.h b/passes/hierarchy/util/misc.h new file mode 100644 index 000000000..5527aebb2 --- /dev/null +++ b/passes/hierarchy/util/misc.h @@ -0,0 +1,49 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Claire Xenia Wolf + * Copyright (C) 2018 Ruben Undheim + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#ifndef HIERARCHY_MISC_H +#define HIERARCHY_MISC_H + +#include "kernel/yosys.h" + +YOSYS_NAMESPACE_BEGIN + +namespace Hierarchy { + struct ArrayType { + std::string name; + int pos_idx; + int pos_num; + int pos_type; + }; + std::optional try_make_array(const std::string celltype); + + // Try to read an IdString as a numbered connection name ("$123" or similar), + // writing the result to dst. If the string isn't of the right format, ignore + // dst and return false. + bool read_id_num(RTLIL::IdString str, int *dst); + + void set_keeps(Design* design, bool keep_prints, bool keep_asserts); + bool set_keep_print(std::map &cache, RTLIL::Module *mod); + bool set_keep_assert(std::map &cache, RTLIL::Module *mod); +}; + +YOSYS_NAMESPACE_END + +#endif /* HIERARCHY_MISC_H */ diff --git a/passes/hierarchy/util/positionals.cc b/passes/hierarchy/util/positionals.cc new file mode 100644 index 000000000..b8d8f03f8 --- /dev/null +++ b/passes/hierarchy/util/positionals.cc @@ -0,0 +1,93 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Claire Xenia Wolf + * Copyright (C) 2018 Ruben Undheim + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/yosys_common.h" +#include "passes/hierarchy/util/positionals.h" +#include "passes/hierarchy/util/misc.h" + +YOSYS_NAMESPACE_BEGIN +namespace Hierarchy { + + void resolve_positionals(RTLIL::Design* design) { + std::set pos_mods; + std::map, RTLIL::IdString> pos_map; + std::vector> pos_work; + + for (auto mod : design->modules()) + for (auto cell : mod->cells()) { + RTLIL::Module *cell_mod = design->module(cell->type); + if (cell_mod == nullptr) + continue; + for (auto &conn : cell->connections()) + if (conn.first[0] == '$' && '0' <= conn.first[1] && conn.first[1] <= '9') { + pos_mods.insert(design->module(cell->type)); + pos_work.push_back(std::pair(mod, cell)); + break; + } + + pool> params_rename; + for (const auto &p : cell->parameters) { + int id; + if (read_id_num(p.first, &id)) { + if (id <= 0 || id > GetSize(cell_mod->avail_parameters)) { + log(" Failed to map positional parameter %d of cell %s.%s (%s).\n", + id, RTLIL::id2cstr(mod->name), RTLIL::id2cstr(cell->name), RTLIL::id2cstr(cell->type)); + } else { + params_rename.insert(std::make_pair(p.first, cell_mod->avail_parameters[id - 1])); + } + } + } + for (const auto &p : params_rename) { + cell->setParam(p.second, cell->getParam(p.first)); + cell->unsetParam(p.first); + } + } + + for (auto module : pos_mods) + for (auto wire : module->wires()) { + if (wire->port_id > 0) + pos_map[std::pair(module, wire->port_id)] = wire->name; + } + + for (auto &work : pos_work) { + RTLIL::Module *module = work.first; + RTLIL::Cell *cell = work.second; + log("Mapping positional arguments of cell %s.%s (%s).\n", + RTLIL::id2cstr(module->name), RTLIL::id2cstr(cell->name), RTLIL::id2cstr(cell->type)); + dict new_connections; + for (auto &conn : cell->connections()) { + int id; + if (read_id_num(conn.first, &id)) { + std::pair key(design->module(cell->type), id); + if (pos_map.count(key) == 0) { + log(" Failed to map positional argument %d of cell %s.%s (%s).\n", + id, RTLIL::id2cstr(module->name), RTLIL::id2cstr(cell->name), RTLIL::id2cstr(cell->type)); + new_connections[conn.first] = conn.second; + } else + new_connections[pos_map.at(key)] = conn.second; + } else + new_connections[conn.first] = conn.second; + } + cell->connections_ = new_connections; + } + } + +}; +YOSYS_NAMESPACE_END diff --git a/passes/hierarchy/util/positionals.h b/passes/hierarchy/util/positionals.h new file mode 100644 index 000000000..38f0e2627 --- /dev/null +++ b/passes/hierarchy/util/positionals.h @@ -0,0 +1,34 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Claire Xenia Wolf + * Copyright (C) 2018 Ruben Undheim + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#ifndef HIERARCHY_POSITIONALS_H +#define HIERARCHY_POSITIONALS_H + +#include "kernel/yosys.h" + +YOSYS_NAMESPACE_BEGIN + +namespace Hierarchy { + void resolve_positionals(Design* design); +}; + +YOSYS_NAMESPACE_END + +#endif /* HIERARCHY_POSITIONALS_H */ diff --git a/passes/hierarchy/util/top.cc b/passes/hierarchy/util/top.cc new file mode 100644 index 000000000..5325f9690 --- /dev/null +++ b/passes/hierarchy/util/top.cc @@ -0,0 +1,220 @@ +/* +* yosys -- Yosys Open SYnthesis Suite +* +* Copyright (C) 2012 Claire Xenia Wolf +* Copyright (C) 2018 Ruben Undheim +* +* Permission to use, copy, modify, and/or distribute this software for any +* purpose with or without fee is hereby granted, provided that the above +* copyright notice and this permission notice appear in all copies. +* +* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +* +*/ + +#include "kernel/yosys.h" +#include "frontends/verific/verific.h" +#include "passes/hierarchy/util/top.h" +#include "passes/hierarchy/util/misc.h" + +YOSYS_NAMESPACE_BEGIN +namespace Hierarchy { + +Module* TopModulePrepare::load_top(const string& name, bool auto_top) { + Module* top_mod = nullptr; + if (!name.empty()) + top_mod = load_top_module_regular(name); + + top_mod = load_top_module_verific(is_from_verific, name, top_mod); + + if (top_mod == nullptr) + top_mod = load_top_module_attribute(); + + if (top_mod == nullptr) + derive_all(); + + if (top_mod == nullptr && auto_top) + top_mod = load_top_module_auto(); + + if (top_mod != nullptr && top_mod->name.begins_with("$abstract")) { + top_mod = rederived_top(top_mod); + } + + if (top_mod != NULL) + clear_initial_top_attribute(top_mod); + + return top_mod; +} + +Module* TopModulePrepare::load_top_module_regular(const string& name) { + Module* top_mod = nullptr; + IdString top_name = RTLIL::escape_id(name); + IdString abstract_id = "$abstract" + RTLIL::escape_id(name); + top_mod = design->module(top_name); + + dict top_parameters; + if ((top_mod == nullptr && design->module(abstract_id)) || top_mod != nullptr) { + for (auto ¶ : params) { + SigSpec sig_value; + if (!RTLIL::SigSpec::parse(sig_value, NULL, para.second)) + log_cmd_error("Can't decode value '%s'!\n", para.second); + top_parameters[RTLIL::escape_id(para.first)] = sig_value.as_const(); + } + } + + if (top_mod == nullptr && design->module(abstract_id)) + top_mod = design->module(design->module(abstract_id)->derive(design, top_parameters)); + else if (top_mod != nullptr && !top_parameters.empty()) + top_mod = design->module(top_mod->derive(design, top_parameters)); + + // Delete template of derived top module or something idk + if (top_mod != nullptr && top_mod->name != top_name) { + Module *m = top_mod->clone(); + m->name = top_name; + Module *old_mod = design->module(top_name); + if (old_mod) + design->remove(old_mod); + design->add(m); + top_mod = m; + } + return top_mod; +} + +Module* TopModulePrepare::load_top_module_verific(bool& is_from_verific, string name, Module* top_mod) { + // Unused if not YOSYS_ENABLE_VERIFIC + (void)is_from_verific; + (void)params; + (void)design; + Module* new_top_mod = top_mod; +#ifdef YOSYS_ENABLE_VERIFIC + is_from_verific = verific_import_pending; +#endif + + if (top_mod == nullptr && !name.empty()) { +#ifdef YOSYS_ENABLE_VERIFIC + if (verific_import_pending) { + name = verific_import(design, params, name); + log("verific name: %s\n", name); + new_top_mod = design->module(RTLIL::escape_id(name)); + } +#endif + if (new_top_mod == NULL) + log_cmd_error("Module `%s' not found!\n", name); + } else { +#ifdef YOSYS_ENABLE_VERIFIC + if (verific_import_pending) + verific_import(design, params); +#endif + } + return new_top_mod; +} + +void TopModulePrepare::derive_all() { + std::vector abstract_ids; + for (auto module : design->modules()) + if (module->name.begins_with("$abstract")) + abstract_ids.push_back(module->name); + for (auto abstract_id : abstract_ids) + design->module(abstract_id)->derive(design, {}); + for (auto abstract_id : abstract_ids) + design->remove(design->module(abstract_id)); +} + +Module* TopModulePrepare::load_top_module_attribute() { + Module* top_mod = nullptr; + for (auto mod : design->modules()) + if (mod->get_bool_attribute(ID::top)) { + log("Attribute `top' found on module `%s'. Setting top module to %s.\n", log_id(mod), log_id(mod)); + top_mod = mod; + } + return top_mod; +} + +static int find_top_mod_score(Design *design, Module *module, dict &db) +{ + if (db.count(module) == 0) { + int score = 0; + db[module] = 0; + for (auto cell : module->cells()) { + std::string celltype = cell->type.str(); + // Is this an array instance + if (auto array_type = try_make_array(celltype)) + celltype = array_type->name; + // Is this cell a module instance? + auto instModule = design->module(celltype); + // If there is no instance for this, issue a warning. + if (instModule != nullptr) { + score = max(score, find_top_mod_score(design, instModule, db) + 1); + } + } + db[module] = score; + } + return db.at(module); +} + +Module* TopModulePrepare::load_top_module_auto() { + Module* top_mod = nullptr; + log_header(design, "Finding top of design hierarchy..\n"); + dict db; + for (Module *mod : design->selected_modules()) { + int score = find_top_mod_score(design, mod, db); + log("root of %3d design levels: %-20s\n", score, log_id(mod)); + if (!top_mod || score > db[top_mod]) + top_mod = mod; + } + if (top_mod != nullptr) + log("Automatically selected %s as design top module.\n", log_id(top_mod)); + return top_mod; +} + +Module* TopModulePrepare::rederived_top(Module* top_mod) { + IdString top_name = top_mod->name.substr(strlen("$abstract")); + + dict top_parameters; + for (auto ¶ : params) { + SigSpec sig_value; + if (!RTLIL::SigSpec::parse(sig_value, NULL, para.second)) + log_cmd_error("Can't decode value '%s'!\n", para.second); + top_parameters[RTLIL::escape_id(para.first)] = sig_value.as_const(); + } + + top_mod = design->module(top_mod->derive(design, top_parameters)); + + if (top_mod != nullptr && top_mod->name != top_name) { + Module *m = top_mod->clone(); + m->name = top_name; + Module *old_mod = design->module(top_name); + if (old_mod) + design->remove(old_mod); + design->add(m); + top_mod = m; + } + return top_mod; +} + +void TopModulePrepare::clear_initial_top_attribute(Module* top_mod) { + for (auto mod : design->modules()) + if (mod == top_mod) + mod->attributes[ID::initial_top] = RTLIL::Const(1); + else + mod->attributes.erase(ID::initial_top); +} + +void ensure_unique_top_attribute(Module* top_mod, Design* design) { + for (auto mod : design->modules()) { + if (mod == top_mod) + mod->attributes[ID::top] = RTLIL::Const(1); + else + mod->attributes.erase(ID::top); + mod->attributes.erase(ID::initial_top); + } +} + +}; +YOSYS_NAMESPACE_END diff --git a/passes/hierarchy/util/top.h b/passes/hierarchy/util/top.h new file mode 100644 index 000000000..8829bf24e --- /dev/null +++ b/passes/hierarchy/util/top.h @@ -0,0 +1,49 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Claire Xenia Wolf + * Copyright (C) 2018 Ruben Undheim + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#ifndef HIERARCHY_TOP_H +#define HIERARCHY_TOP_H + +#include "kernel/yosys.h" + +YOSYS_NAMESPACE_BEGIN + +namespace Hierarchy { + struct TopModulePrepare { + Design* design; + const std::map& params; + bool is_from_verific = false; + Module* load_top(const string& name, bool auto_top); + private: + Module* load_top_module_regular(const string& name); + Module* load_top_module_verific(bool& is_from_verific, string name, Module* top_mod); + void derive_all(); + Module* load_top_module_attribute(); + Module* load_top_module_auto(); + Module* rederived_top(Module* top_mod); + void clear_initial_top_attribute(Module* top_mod); + }; + + void ensure_unique_top_attribute(Module* top_mod, Design* design); +}; + +YOSYS_NAMESPACE_END + +#endif /* HIERARCHY_TOP_H */ diff --git a/passes/hierarchy/util/verilog.cc b/passes/hierarchy/util/verilog.cc new file mode 100644 index 000000000..0cd153a35 --- /dev/null +++ b/passes/hierarchy/util/verilog.cc @@ -0,0 +1,351 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Claire Xenia Wolf + * Copyright (C) 2018 Ruben Undheim + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/yosys_common.h" +#include "passes/hierarchy/util/verilog.h" +#include "passes/hierarchy/util/positionals.h" + +PRIVATE_NAMESPACE_BEGIN +USING_YOSYS_NAMESPACE +// Find a matching wire for an implicit port connection; traversing generate block scope +RTLIL::Wire *find_implicit_port_wire(Module *module, Cell *cell, const std::string& port) +{ + const std::string &cellname = cell->name.str(); + size_t idx = cellname.size(); + while ((idx = cellname.find_last_of('.', idx-1)) != std::string::npos) { + Wire *found = module->wire(cellname.substr(0, idx+1) + port.substr(1)); + if (found != nullptr) + return found; + } + return module->wire(port); +} + +PRIVATE_NAMESPACE_END + +YOSYS_NAMESPACE_BEGIN +namespace Hierarchy { + + void resolve_verilog(Design* design, bool nodefaults, bool keep_positionals, bool keep_portwidths, bool top_is_from_verific) { + if (!keep_positionals) + resolve_positionals(design); + + // Determine default values + dict> defaults_db; + if (!nodefaults) + { + for (auto module : design->modules()) + for (auto wire : module->wires()) + if (wire->port_input && wire->attributes.count(ID::defaultvalue)) + defaults_db[module->name][wire->name] = wire->attributes.at(ID::defaultvalue); + } + + // Process SV implicit wildcard port connections + std::set blackbox_derivatives; + std::vector design_modules = design->modules(); + + for (auto module : design_modules) + for (auto cell : module->cells()) + resolve_wildcards(cell, blackbox_derivatives, nodefaults, defaults_db); + + if (!nodefaults) + { + for (auto module : design->modules()) + for (auto cell : module->cells()) + { + if (defaults_db.count(cell->type) == 0) + continue; + + if (keep_positionals) { + bool found_positionals = false; + for (auto &conn : cell->connections()) + if (conn.first[0] == '$' && '0' <= conn.first[1] && conn.first[1] <= '9') + found_positionals = true; + if (found_positionals) + continue; + } + + for (auto &it : defaults_db.at(cell->type)) + if (!cell->hasPort(it.first)) + cell->setPort(it.first, it.second); + } + } + + for (auto module : design_modules) + resolve_wand_wor(module, blackbox_derivatives, keep_portwidths, top_is_from_verific); + + for (auto module : blackbox_derivatives) + design->remove(module); + } + void resolve_wildcards(Cell* cell, std::set& blackbox_derivatives, bool nodefaults, dict>& defaults_db) { + if (!cell->get_bool_attribute(ID::wildcard_port_conns)) + return; + Module* module = cell->module; + Design* design = module->design; + Module* submod = design->module(cell->type); + + if (submod == nullptr) + log_error("Cell %s.%s (%s) has implicit port connections but the module it instantiates is unknown.\n", + RTLIL::id2cstr(module->name), RTLIL::id2cstr(cell->name), RTLIL::id2cstr(cell->type)); + + // Need accurate port widths for error checking; so must derive blackboxes with dynamic port widths + if (submod->get_blackbox_attribute() && !cell->parameters.empty() && submod->get_bool_attribute(ID::dynports)) { + IdString new_m_name = submod->derive(design, cell->parameters, true); + if (new_m_name.empty()) + return; + if (new_m_name != submod->name) { + submod = design->module(new_m_name); + blackbox_derivatives.insert(submod); + } + } + + auto old_connections = cell->connections(); + for (auto wire : submod->wires()) { + // Find ports of the module that aren't explicitly connected + if (!wire->port_input && !wire->port_output) + continue; + if (old_connections.count(wire->name)) + continue; + // Make sure a wire of correct name exists in the parent + Wire* parent_wire = find_implicit_port_wire(module, cell, wire->name.str()); + + // Missing wires are OK when a default value is set + if (!nodefaults && parent_wire == nullptr && defaults_db.count(cell->type) && defaults_db.at(cell->type).count(wire->name)) + continue; + + if (parent_wire == nullptr) + log_error("No matching wire for implicit port connection `%s' of cell %s.%s (%s).\n", + RTLIL::id2cstr(wire->name), RTLIL::id2cstr(module->name), RTLIL::id2cstr(cell->name), RTLIL::id2cstr(cell->type)); + if (parent_wire->width != wire->width) + log_error("Width mismatch between wire (%d bits) and port (%d bits) for implicit port connection `%s' of cell %s.%s (%s).\n", + parent_wire->width, wire->width, + RTLIL::id2cstr(wire->name), RTLIL::id2cstr(module->name), RTLIL::id2cstr(cell->name), RTLIL::id2cstr(cell->type)); + cell->setPort(wire->name, parent_wire); + } + cell->attributes.erase(ID::wildcard_port_conns); + } + + void resolve_wand_wor(Module* module, std::set& blackbox_derivatives, bool keep_portwidths, bool top_is_from_verific) { + Design* design = module->design; + pool wand_wor_index; + dict wand_map, wor_map; + vector new_connections; + + for (auto wire : module->wires()) + { + if (wire->get_bool_attribute(ID::wand)) { + wand_map[wire] = SigSpec(); + wand_wor_index.insert(wire); + } + if (wire->get_bool_attribute(ID::wor)) { + wor_map[wire] = SigSpec(); + wand_wor_index.insert(wire); + } + } + + for (auto &conn : module->connections()) + { + SigSig new_conn; + int cursor = 0; + + for (auto c : conn.first.chunks()) + { + Wire *w = c.wire; + SigSpec rhs = conn.second.extract(cursor, GetSize(c)); + + if (wand_wor_index.count(w) == 0) { + new_conn.first.append(c); + new_conn.second.append(rhs); + } else { + if (wand_map.count(w)) { + SigSpec sig = SigSpec(State::S1, GetSize(w)); + sig.replace(c.offset, rhs); + wand_map.at(w).append(sig); + } else { + SigSpec sig = SigSpec(State::S0, GetSize(w)); + sig.replace(c.offset, rhs); + wor_map.at(w).append(sig); + } + } + cursor += GetSize(c); + } + new_connections.push_back(new_conn); + } + module->new_connections(new_connections); + + for (auto cell : module->cells()) + { + if (!cell->known()) + continue; + + for (auto &conn : cell->connections()) + { + if (!cell->output(conn.first)) + continue; + + SigSpec new_sig; + bool update_port = false; + + for (auto c : conn.second.chunks()) + { + Wire *w = c.wire; + + if (wand_wor_index.count(w) == 0) { + new_sig.append(c); + continue; + } + + Wire *t = module->addWire(NEW_ID, GetSize(c)); + new_sig.append(t); + update_port = true; + + if (wand_map.count(w)) { + SigSpec sig = SigSpec(State::S1, GetSize(w)); + sig.replace(c.offset, t); + wand_map.at(w).append(sig); + } else { + SigSpec sig = SigSpec(State::S0, GetSize(w)); + sig.replace(c.offset, t); + wor_map.at(w).append(sig); + } + } + + if (update_port) + cell->setPort(conn.first, new_sig); + } + } + + for (auto w : wand_wor_index) + { + bool wand = wand_map.count(w); + SigSpec sigs = wand ? wand_map.at(w) : wor_map.at(w); + + if (GetSize(sigs) == 0) + continue; + + if (GetSize(w) == 1) { + if (wand) + module->addReduceAnd(NEW_ID, sigs, w); + else + module->addReduceOr(NEW_ID, sigs, w); + continue; + } + + SigSpec s = sigs.extract(0, GetSize(w)); + for (int i = GetSize(w); i < GetSize(sigs); i += GetSize(w)) { + if (wand) + s = module->And(NEW_ID, s, sigs.extract(i, GetSize(w))); + else + s = module->Or(NEW_ID, s, sigs.extract(i, GetSize(w))); + } + module->connect(w, s); + } + + for (auto cell : module->cells()) + { + Module *m = design->module(cell->type); + + if (m == nullptr) + continue; + + bool boxed_params = false; + if (m->get_blackbox_attribute() && !cell->parameters.empty()) { + if (m->get_bool_attribute(ID::dynports)) { + IdString new_m_name = m->derive(design, cell->parameters, true); + if (new_m_name.empty()) + continue; + if (new_m_name != m->name) { + m = design->module(new_m_name); + blackbox_derivatives.insert(m); + } + } else { + boxed_params = true; + } + } + + for (auto &conn : cell->connections()) + { + Wire *w = m->wire(conn.first); + + if (w == nullptr || w->port_id == 0) + continue; + + if (GetSize(conn.second) == 0) + continue; + + SigSpec sig = conn.second; + + bool resize_widths = !keep_portwidths && GetSize(w) != GetSize(conn.second); + if (resize_widths && top_is_from_verific && boxed_params) + log_debug("Ignoring width mismatch on %s.%s.%s from verific, is port width parametrizable?\n", + log_id(module), log_id(cell), log_id(conn.first) + ); + else if (resize_widths) { + if (GetSize(w) < GetSize(conn.second)) + { + int n = GetSize(conn.second) - GetSize(w); + if (!w->port_input && w->port_output) + { + RTLIL::SigSpec out = sig.extract(0, GetSize(w)); + out.extend_u0(GetSize(sig), w->is_signed); + module->connect(sig.extract(GetSize(w), n), out.extract(GetSize(w), n)); + } + sig.remove(GetSize(w), n); + } + else + { + int n = GetSize(w) - GetSize(conn.second); + if (w->port_input && !w->port_output) + sig.extend_u0(GetSize(w), sig.is_wire() && sig.as_wire()->is_signed); + else + sig.append(module->addWire(NEW_ID, n)); + } + + if (!conn.second.is_fully_const() || !w->port_input || w->port_output) + log_warning("Resizing cell port %s.%s.%s from %d bits to %d bits.\n", log_id(module), log_id(cell), + log_id(conn.first), GetSize(conn.second), GetSize(sig)); + cell->setPort(conn.first, sig); + } + + if (w->port_output && !w->port_input && sig.has_const()) + log_error("Output port %s.%s.%s (%s) is connected to constants: %s\n", + log_id(module), log_id(cell), log_id(conn.first), log_id(cell->type), log_signal(sig)); + } + } + } + void check_supported_formal(Design* design) { + for (auto mod : design->modules()) { + for (auto cell : mod->cells()) { + if (!cell->type.in(ID($check), ID($assert), ID($assume), ID($live), ID($fair), ID($cover))) + continue; + if (!cell->has_attribute(ID(unsupported_sva))) + continue; + + auto src = cell->get_src_attribute(); + + if (!src.empty()) + src += ": "; + + log_error("%sProperty `%s' in module `%s' uses unsupported SVA constructs. See frontend warnings for details, run `chformal -remove a:unsupported_sva' to ignore.\n", + src, log_id(cell->name), log_id(mod->name)); + } + } + } +}; +YOSYS_NAMESPACE_END diff --git a/passes/hierarchy/util/verilog.h b/passes/hierarchy/util/verilog.h new file mode 100644 index 000000000..4cd5f06df --- /dev/null +++ b/passes/hierarchy/util/verilog.h @@ -0,0 +1,37 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Claire Xenia Wolf + * Copyright (C) 2018 Ruben Undheim + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#ifndef HIERARCHY_VERILOG_H +#define HIERARCHY_VERILOG_H + +#include "kernel/yosys.h" + +YOSYS_NAMESPACE_BEGIN + +namespace Hierarchy { + void resolve_verilog(Design* design, bool nodefaults, bool keep_positionals, bool keep_portwidths, bool top_is_from_verific); + void resolve_wildcards(Cell* cell, std::set& blackbox_derivatives, bool nodefaults, dict>& defaults_db); + void resolve_wand_wor(Module* module, std::set& blackbox_derivatives, bool keep_portwidths, bool top_is_from_verific); + void check_supported_formal(Design* design); +}; + +YOSYS_NAMESPACE_END + +#endif /* HIERARCHY_VERILOG_H */