| /* | |
| * yosys -- Yosys Open SYnthesis Suite | |
| * | |
| * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> | |
| * Copyright (C) 2018 Ruben Undheim <ruben.undheim@gmail.com> | |
| * | |
| * Permission to use, copy, modify, and/or distribute this software for any | |
| * purpose with or without fee is hereby granted, provided that the above | |
| * 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 <stdlib.h> | |
| #include <stdio.h> | |
| #include <set> | |
| #ifndef _WIN32 | |
| # include <unistd.h> | |
| #endif | |
| USING_YOSYS_NAMESPACE | |
| PRIVATE_NAMESPACE_BEGIN | |
| struct generate_port_decl_t { | |
| bool input, output; | |
| string portname; | |
| int index; | |
| }; | |
| void generate(RTLIL::Design *design, const std::vector<std::string> &celltypes, const std::vector<generate_port_decl_t> &portdecls) | |
| { | |
| std::set<RTLIL::IdString> found_celltypes; | |
| for (auto i1 : design->modules_) | |
| for (auto i2 : i1.second->cells_) | |
| { | |
| RTLIL::Cell *cell = i2.second; | |
| if (design->has(cell->type)) | |
| continue; | |
| if (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<RTLIL::IdString> portnames; | |
| std::set<RTLIL::IdString> parameters; | |
| std::map<RTLIL::IdString, int> portwidths; | |
| log("Generate module for cell type %s:\n", celltype.c_str()); | |
| for (auto i1 : design->modules_) | |
| for (auto i2 : i1.second->cells_) | |
| if (i2.second->type == celltype) { | |
| for (auto &conn : i2.second->connections()) { | |
| if (conn.first[0] != '$') | |
| portnames.insert(conn.first); | |
| portwidths[conn.first] = max(portwidths[conn.first], conn.second.size()); | |
| } | |
| for (auto ¶ : i2.second->parameters) | |
| parameters.insert(para.first); | |
| } | |
| for (auto &decl : portdecls) | |
| if (decl.index > 0) | |
| portnames.insert(decl.portname); | |
| std::set<int> indices; | |
| for (int i = 0; i < int(portnames.size()); i++) | |
| indices.insert(i+1); | |
| std::vector<generate_port_decl_t> 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["\\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; | |
| } | |
| bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check, bool flag_simcheck, std::vector<std::string> &libdirs) | |
| { | |
| bool did_something = false; | |
| std::map<RTLIL::Cell*, std::pair<int, int>> 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("\\interfaces_replaced_in_module")) { | |
| for (auto &wire : module->wires_) { | |
| if ((wire.second->port_input || wire.second->port_output) && wire.second->get_bool_attribute("\\is_interface")) | |
| has_interface_ports = true; | |
| } | |
| } | |
| // Always keep track of all derived interfaces available in the current module in 'interfaces_in_module': | |
| dict<RTLIL::IdString, RTLIL::Module*> interfaces_in_module; | |
| for (auto &cell_it : module->cells_) | |
| { | |
| RTLIL::Cell *cell = cell_it.second; | |
| if(cell->get_bool_attribute("\\is_interface")) { | |
| RTLIL::Module *intf_module = design->modules_[cell->type]; | |
| interfaces_in_module[cell->name] = intf_module; | |
| } | |
| } | |
| for (auto &cell_it : module->cells_) | |
| { | |
| RTLIL::Cell *cell = cell_it.second; | |
| bool has_interfaces_not_found = false; | |
| std::vector<RTLIL::IdString> connections_to_remove; | |
| std::vector<RTLIL::IdString> connections_to_add_name; | |
| std::vector<RTLIL::SigSpec> connections_to_add_signal; | |
| if (cell->type.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<int, int>(idx, num); | |
| cell->type = cell->type.substr(pos_type + 1); | |
| } | |
| dict<RTLIL::IdString, RTLIL::Module*> interfaces_to_add_to_submodule; | |
| dict<RTLIL::IdString, RTLIL::IdString> modports_used_in_submodule; | |
| if (design->modules_.count(cell->type) == 0) | |
| { | |
| if (design->modules_.count("$abstract" + cell->type.str())) | |
| { | |
| cell->type = design->modules_.at("$abstract" + cell->type.str())->derive(design, cell->parameters); | |
| cell->parameters.clear(); | |
| did_something = true; | |
| continue; | |
| } | |
| if (cell->type[0] == '$') | |
| continue; | |
| for (auto &dir : libdirs) | |
| { | |
| static const vector<pair<string, string>> extensions_list = | |
| { | |
| {".v", "verilog"}, | |
| {".sv", "verilog -sv"}, | |
| {".il", "ilang"} | |
| }; | |
| for (auto &ext : extensions_list) | |
| { | |
| filename = dir + "/" + RTLIL::unescape_id(cell->type) + ext.first; | |
| if (check_file_exists(filename)) { | |
| Frontend::frontend_call(design, NULL, filename, ext.second); | |
| goto loaded_module; | |
| } | |
| } | |
| } | |
| if ((flag_check || flag_simcheck) && cell->type[0] != '$') | |
| log_error("Module `%s' referenced in module `%s' in cell `%s' is not part of the design.\n", | |
| cell->type.c_str(), module->name.c_str(), cell->name.c_str()); | |
| continue; | |
| loaded_module: | |
| if (design->modules_.count(cell->type) == 0) | |
| log_error("File `%s' from libdir does not declare module `%s'.\n", filename.c_str(), cell->type.c_str()); | |
| did_something = true; | |
| } else { | |
| RTLIL::Module *mod = design->module(cell->type); | |
| // Go over all connections and see if any of them are SV interfaces. If they are, then add the replacements to | |
| // some lists, so that the ports for sub-modules can be replaced further down: | |
| for (auto &conn : cell->connections()) { | |
| if(mod->wires_.count(conn.first) != 0 && mod->wire(conn.first)->get_bool_attribute("\\is_interface")) { // Check if the connection is present as an interface in the sub-module's port list | |
| //const pool<string> &interface_type_pool = mod->wire(conn.first)->get_strpool_attribute("\\interface_type"); | |
| //for (auto &d : interface_type_pool) { // TODO: Compare interface type to type in parent module (not crucially important, but good for robustness) | |
| //} | |
| // Find if the sub-module has set a modport for the current interface connection: | |
| const pool<string> &interface_modport_pool = mod->wire(conn.first)->get_strpool_attribute("\\interface_modport"); | |
| std::string interface_modport = ""; | |
| for (auto &d : interface_modport_pool) { | |
| interface_modport = "\\" + d; | |
| } | |
| if(conn.second.bits().size() == 1 && conn.second.bits()[0].wire->get_bool_attribute("\\is_interface")) { // Check if the connected wire is a potential interface in the parent module | |
| std::string interface_name_str = conn.second.bits()[0].wire->name.str(); | |
| interface_name_str.replace(0,23,""); // Strip the prefix '$dummywireforinterface' from the dummy wire to get the name | |
| interface_name_str = "\\" + interface_name_str; | |
| RTLIL::IdString interface_name = interface_name_str; | |
| bool not_found_interface = false; | |
| if(module->get_bool_attribute("\\interfaces_replaced_in_module")) { // If 'interfaces' in the cell have not be been handled yet, there is no need to derive the sub-module either | |
| // 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 (nexactmatch > 0 || nmatch2 > 0) { | |
| 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); | |
| for (auto &mod_wire : mod_replace_ports->wires_) { // Go over all wires in interface, and add replacements to lists. | |
| std::string signal_name1 = conn.first.str() + "." + log_id(mod_wire.first); | |
| std::string signal_name2 = interface_name.str() + "." + log_id(mod_wire.first); | |
| connections_to_add_name.push_back(RTLIL::IdString(signal_name1)); | |
| if(module->wires_.count(signal_name2) == 0) { | |
| log_error("Could not find signal '%s' in '%s'\n", signal_name2.c_str(), log_id(module->name)); | |
| } | |
| else { | |
| RTLIL::Wire *wire_in_parent = module->wire(signal_name2); | |
| connections_to_add_signal.push_back(wire_in_parent); | |
| } | |
| } | |
| connections_to_remove.push_back(conn.first); | |
| interfaces_to_add_to_submodule[conn.first] = interfaces_in_module.at(interface_name2); | |
| // Add modports to a dict which will be passed to AstModule::derive | |
| if (interface_modport != "") { | |
| modports_used_in_submodule[conn.first] = interface_modport; | |
| } | |
| } | |
| else not_found_interface = true; | |
| } | |
| else not_found_interface = true; | |
| // If the interface instance has not already been derived in the module, we cannot complete at this stage. Set "has_interfaces_not_found" | |
| // which will delay the expansion of this cell: | |
| if (not_found_interface) { | |
| // If we have already gone over all cells in this module, and the interface has still not been found - flag it as an error: | |
| if(!(module->get_bool_attribute("\\cells_not_processed"))) { | |
| log_warning("Could not find interface instance for `%s' in `%s'\n", log_id(interface_name), log_id(module)); | |
| } | |
| else { | |
| // Only set has_interfaces_not_found if it would be possible to find them, since otherwiser we will end up in an infinite loop: | |
| has_interfaces_not_found = true; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| // | |
| if (flag_check || flag_simcheck) | |
| { | |
| for (auto &conn : cell->connections()) { | |
| if (conn.first[0] == '$' && '0' <= conn.first[1] && conn.first[1] <= '9') { | |
| int id = atoi(conn.first.c_str()+1); | |
| if (id <= 0 || id > GetSize(mod->ports)) | |
| 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); | |
| } else if (mod->wire(conn.first) == nullptr || mod->wire(conn.first)->port_id == 0) | |
| log_error("Module `%s' referenced in module `%s' in cell `%s' does not have a port named '%s'.\n", | |
| log_id(cell->type), log_id(module), log_id(cell), log_id(conn.first)); | |
| } | |
| for (auto ¶m : cell->parameters) | |
| if (mod->avail_parameters.count(param.first) == 0 && param.first[0] != '$' && strchr(param.first.c_str(), '.') == NULL) | |
| log_error("Module `%s' referenced in module `%s' in cell `%s' does not have a parameter named '%s'.\n", | |
| log_id(cell->type), log_id(module), log_id(cell), log_id(param.first)); | |
| } | |
| } | |
| RTLIL::Module *mod = design->modules_[cell->type]; | |
| if (design->modules_.at(cell->type)->get_blackbox_attribute()) { | |
| if (flag_simcheck) | |
| 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(has_interfaces_not_found) { | |
| did_something = true; // waiting for interfaces to be handled | |
| continue; | |
| } | |
| // Do the actual replacements of the SV interface port connection with the individual signal connections: | |
| for(unsigned int i=0;i<connections_to_add_name.size();i++) { | |
| cell->connections_[connections_to_add_name[i]] = connections_to_add_signal[i]; | |
| } | |
| // Remove the connection for the interface itself: | |
| for(unsigned int i=0;i<connections_to_remove.size();i++) { | |
| cell->connections_.erase(connections_to_remove[i]); | |
| } | |
| // If there are no overridden parameters AND not interfaces, then we can use the existing module instance as the type | |
| // for the cell: | |
| if (cell->parameters.size() == 0 && (interfaces_to_add_to_submodule.size() == 0 || !(cell->get_bool_attribute("\\module_not_derived")))) { | |
| // If the cell being processed is an the interface instance itself, go down to "handle_interface_instance:", | |
| // so that the signals of the interface are added to the parent module. | |
| if (mod->get_bool_attribute("\\is_interface")) { | |
| goto handle_interface_instance; | |
| } | |
| continue; | |
| } | |
| cell->type = mod->derive(design, cell->parameters, interfaces_to_add_to_submodule, modports_used_in_submodule); | |
| cell->parameters.clear(); | |
| did_something = true; | |
| handle_interface_instance: | |
| // We add all the signals of the interface explicitly to the parent module. This is always needed when we encounter | |
| // an interface instance: | |
| if (mod->get_bool_attribute("\\is_interface") && cell->get_bool_attribute("\\module_not_derived")) { | |
| cell->set_bool_attribute("\\is_interface"); | |
| RTLIL::Module *derived_module = design->modules_[cell->type]; | |
| interfaces_in_module[cell->name] = derived_module; | |
| did_something = true; | |
| } | |
| // We clear 'module_not_derived' such that we will not rederive the cell again (needed when there are interfaces connected to the cell) | |
| cell->attributes.erase("\\module_not_derived"); | |
| } | |
| // Clear the attribute 'cells_not_processed' such that it can be known that we | |
| // have been through all cells at least once, and that we can know whether | |
| // to flag an error because of interface instances not found: | |
| module->attributes.erase("\\cells_not_processed"); | |
| // If any interface instances or interface ports were found in the module, we need to rederive it completely: | |
| if ((interfaces_in_module.size() > 0 || has_interface_ports) && !module->get_bool_attribute("\\interfaces_replaced_in_module")) { | |
| module->reprocess_module(design, interfaces_in_module); | |
| return did_something; | |
| } | |
| for (auto &it : array_cells) | |
| { | |
| RTLIL::Cell *cell = it.first; | |
| int idx = it.second.first, num = it.second.second; | |
| if (design->modules_.count(cell->type) == 0) | |
| 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->modules_[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_it : mod->wires_) | |
| if (wire_it.second->port_id == port_id) { | |
| portname = wire_it.first; | |
| break; | |
| } | |
| } | |
| if (mod->wires_.count(portname) == 0) | |
| 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->wires_.at(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<RTLIL::Module*, IdString::compare_ptr_by_name<Module>> &used, RTLIL::Module *mod, int indent) | |
| { | |
| if (used.count(mod) > 0) | |
| return; | |
| if (indent == 0) | |
| log("Top module: %s\n", mod->name.c_str()); | |
| else if (!mod->get_blackbox_attribute()) | |
| log("Used module: %*s%s\n", indent, "", mod->name.c_str()); | |
| 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<RTLIL::Module*, IdString::compare_ptr_by_name<Module>> used; | |
| hierarchy_worker(design, used, top, 0); | |
| std::vector<RTLIL::Module*> del_modules; | |
| for (auto &it : design->modules_) | |
| if (used.count(it.second) == 0) | |
| del_modules.push_back(it.second); | |
| else { | |
| // Now all interface ports must have been exploded, and it is hence | |
| // safe to delete all of the remaining dummy interface ports: | |
| pool<RTLIL::Wire*> del_wires; | |
| for(auto &wire : it.second->wires_) { | |
| if ((wire.second->port_input || wire.second->port_output) && wire.second->get_bool_attribute("\\is_interface")) { | |
| del_wires.insert(wire.second); | |
| } | |
| } | |
| if (del_wires.size() > 0) { | |
| it.second->remove(del_wires); | |
| it.second->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.c_str()); | |
| design->modules_.erase(mod->name); | |
| del_counter++; | |
| delete mod; | |
| } | |
| log("Removed %d unused modules.\n", del_counter); | |
| } | |
| bool set_keep_assert(std::map<RTLIL::Module*, bool> &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("$assert", "$assume", "$live", "$fair", "$cover")) | |
| return cache[mod] = true; | |
| } | |
| return cache[mod]; | |
| } | |
| int find_top_mod_score(Design *design, Module *module, dict<Module*, int> &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("\\initial_top")) | |
| return top_mod; | |
| else { | |
| for (auto mod : design->modules()) { | |
| if (mod->get_bool_attribute("\\top")) { | |
| return mod; | |
| } | |
| } | |
| } | |
| return NULL; | |
| } | |
| struct HierarchyPass : public Pass { | |
| HierarchyPass() : Pass("hierarchy", "check, expand and clean up design hierarchy") { } | |
| void help() YS_OVERRIDE | |
| { | |
| // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| | |
| log("\n"); | |
| log(" hierarchy [-check] [-top <module>]\n"); | |
| log(" hierarchy -generate <cell-types> <port-decls>\n"); | |
| log("\n"); | |
| log("In parametric designs, a module might exists in several variations with\n"); | |
| log("different parameter values. This pass looks at all modules in the current\n"); | |
| log("design an re-runs the language frontends for the parametric modules as\n"); | |
| log("needed. It also resolves assignments to wired logic data types (wand/wor),\n"); | |
| log("resolves positional module parameters, unroll array instances, and more.\n"); | |
| log("\n"); | |
| log(" -check\n"); | |
| log(" also check the design hierarchy. this generates an error when\n"); | |
| log(" an unknown module is used as cell type.\n"); | |
| log("\n"); | |
| log(" -simcheck\n"); | |
| log(" like -check, but also throw an error if blackbox modules are\n"); | |
| log(" instantiated, and throw an error if the design has no top module.\n"); | |
| log("\n"); | |
| log(" -purge_lib\n"); | |
| log(" by default the hierarchy command will not remove library (blackbox)\n"); | |
| log(" modules. use this option to also remove unused blackbox modules.\n"); | |
| log("\n"); | |
| log(" -libdir <directory>\n"); | |
| log(" search for files named <module_name>.v in the specified directory\n"); | |
| log(" for unknown modules and automatically run read_verilog for each\n"); | |
| log(" unknown module.\n"); | |
| log("\n"); | |
| log(" -keep_positionals\n"); | |
| log(" per default this pass also converts positional arguments in cells\n"); | |
| log(" to arguments using port names. This option disables this behavior.\n"); | |
| log("\n"); | |
| log(" -keep_portwidths\n"); | |
| log(" per default this pass adjusts the port width on cells that are\n"); | |
| log(" module instances when the width does not match the module port. This\n"); | |
| log(" option disables this behavior.\n"); | |
| log("\n"); | |
| log(" -nodefaults\n"); | |
| log(" do not resolve input port default values\n"); | |
| log("\n"); | |
| log(" -nokeep_asserts\n"); | |
| log(" per default this pass sets the \"keep\" attribute on all modules\n"); | |
| log(" that directly or indirectly contain one or more formal properties.\n"); | |
| log(" This option disables this behavior.\n"); | |
| log("\n"); | |
| log(" -top <module>\n"); | |
| log(" use the specified top module to build the design hierarchy. Modules\n"); | |
| log(" outside this tree (unused modules) are removed.\n"); | |
| log("\n"); | |
| log(" when the -top option is used, the 'top' attribute will be set on the\n"); | |
| log(" specified top module. otherwise a module with the 'top' attribute set\n"); | |
| log(" will implicitly be used as top module, if such a module exists.\n"); | |
| log("\n"); | |
| log(" -auto-top\n"); | |
| log(" automatically determine the top of the design hierarchy and mark it.\n"); | |
| log("\n"); | |
| log(" -chparam name value \n"); | |
| log(" elaborate the top module using this parameter value. Modules on which\n"); | |
| log(" this parameter does not exist may cause a warning message to be output.\n"); | |
| log(" This option can be specified multiple times to override multiple\n"); | |
| log(" parameters. String values must be passed in double quotes (\").\n"); | |
| log("\n"); | |
| log("In -generate mode this pass generates blackbox modules for the given cell\n"); | |
| log("types (wildcards supported). For this the design is searched for cells that\n"); | |
| log("match the given types and then the given port declarations are used to\n"); | |
| log("determine the direction of the ports. The syntax for a port declaration is:\n"); | |
| log("\n"); | |
| log(" {i|o|io}[@<num>]:<portname>\n"); | |
| log("\n"); | |
| log("Input ports are specified with the 'i' prefix, output ports with the 'o'\n"); | |
| log("prefix and inout ports with the 'io' prefix. The optional <num> specifies\n"); | |
| log("the position of the port in the parameter list (needed when instantiated\n"); | |
| log("using positional arguments). When <num> is not specified, the <portname> can\n"); | |
| log("also contain wildcard characters.\n"); | |
| log("\n"); | |
| log("This pass ignores the current selection and always operates on all modules\n"); | |
| log("in the current design.\n"); | |
| log("\n"); | |
| } | |
| void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE | |
| { | |
| log_header(design, "Executing HIERARCHY pass (managing design hierarchy).\n"); | |
| bool flag_check = false; | |
| bool flag_simcheck = false; | |
| bool purge_lib = false; | |
| RTLIL::Module *top_mod = NULL; | |
| std::string load_top_mod; | |
| std::vector<std::string> libdirs; | |
| bool auto_top_mode = false; | |
| bool generate_mode = false; | |
| bool keep_positionals = false; | |
| bool keep_portwidths = false; | |
| bool nodefaults = false; | |
| bool nokeep_asserts = false; | |
| std::vector<std::string> generate_cells; | |
| std::vector<generate_port_decl_t> generate_ports; | |
| std::map<std::string, std::string> parameters; | |
| size_t argidx; | |
| for (argidx = 1; argidx < args.size(); argidx++) | |
| { | |
| if (args[argidx] == "-generate" && !flag_check && !flag_simcheck && !top_mod) { | |
| 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.c_str()); | |
| generate_ports.push_back(decl); | |
| continue; | |
| is_celltype: | |
| log("Celltype: %s\n", args[argidx].c_str()); | |
| generate_cells.push_back(RTLIL::unescape_id(args[argidx])); | |
| } | |
| continue; | |
| } | |
| if (args[argidx] == "-check") { | |
| flag_check = true; | |
| continue; | |
| } | |
| if (args[argidx] == "-simcheck") { | |
| flag_simcheck = true; | |
| continue; | |
| } | |
| if (args[argidx] == "-purge_lib") { | |
| purge_lib = true; | |
| continue; | |
| } | |
| if (args[argidx] == "-keep_positionals") { | |
| keep_positionals = true; | |
| continue; | |
| } | |
| if (args[argidx] == "-keep_portwidths") { | |
| keep_portwidths = true; | |
| continue; | |
| } | |
| if (args[argidx] == "-nodefaults") { | |
| nodefaults = true; | |
| continue; | |
| } | |
| if (args[argidx] == "-nokeep_asserts") { | |
| nokeep_asserts = true; | |
| continue; | |
| } | |
| if (args[argidx] == "-libdir" && argidx+1 < args.size()) { | |
| libdirs.push_back(args[++argidx]); | |
| continue; | |
| } | |
| if (args[argidx] == "-top") { | |
| if (++argidx >= args.size()) | |
| log_cmd_error("Option -top requires an additional argument!\n"); | |
| load_top_mod = args[argidx]; | |
| continue; | |
| } | |
| if (args[argidx] == "-auto-top") { | |
| auto_top_mode = true; | |
| continue; | |
| } | |
| if (args[argidx] == "-chparam" && argidx+2 < args.size()) { | |
| const std::string &key = args[++argidx]; | |
| const std::string &value = args[++argidx]; | |
| auto r = parameters.emplace(key, value); | |
| if (!r.second) { | |
| log_warning("-chparam %s already specified: overwriting.\n", key.c_str()); | |
| r.first->second = value; | |
| } | |
| continue; | |
| } | |
| break; | |
| } | |
| 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<RTLIL::IdString, RTLIL::Const> 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.c_str()); | |
| 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; | |
| } | |
| } | |
| if (top_mod == nullptr && !load_top_mod.empty()) { | |
| #ifdef YOSYS_ENABLE_VERIFIC | |
| if (verific_import_pending) { | |
| 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.c_str()); | |
| } else { | |
| #ifdef YOSYS_ENABLE_VERIFIC | |
| if (verific_import_pending) | |
| verific_import(design, parameters); | |
| #endif | |
| } | |
| if (generate_mode) { | |
| generate(design, generate_cells, generate_ports); | |
| return; | |
| } | |
| log_push(); | |
| if (top_mod == nullptr) | |
| for (auto &mod_it : design->modules_) | |
| if (mod_it.second->get_bool_attribute("\\top")) | |
| top_mod = mod_it.second; | |
| if (top_mod != nullptr && top_mod->name.begins_with("$abstract")) { | |
| IdString top_name = top_mod->name.substr(strlen("$abstract")); | |
| dict<RTLIL::IdString, RTLIL::Const> 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.c_str()); | |
| 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; | |
| } | |
| } | |
| if (top_mod == nullptr && auto_top_mode) { | |
| log_header(design, "Finding top of design hierarchy..\n"); | |
| dict<Module*, int> 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 (flag_simcheck && top_mod == nullptr) | |
| log_error("Design has no top module.\n"); | |
| if (top_mod != NULL) { | |
| for (auto &mod_it : design->modules_) | |
| if (mod_it.second == top_mod) | |
| mod_it.second->attributes["\\initial_top"] = RTLIL::Const(1); | |
| else | |
| mod_it.second->attributes.erase("\\initial_top"); | |
| } | |
| bool did_something = true; | |
| while (did_something) | |
| { | |
| did_something = false; | |
| std::set<RTLIL::Module*, IdString::compare_ptr_by_name<Module>> 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, libdirs)) | |
| did_something = true; | |
| } | |
| // The top module might have changed if interface instances have been detected in it: | |
| RTLIL::Module *tmp_top_mod = check_if_top_has_changed(design, top_mod); | |
| if (tmp_top_mod != NULL) { | |
| if (tmp_top_mod != top_mod){ | |
| top_mod = tmp_top_mod; | |
| did_something = true; | |
| } | |
| } | |
| // Delete modules marked as 'to_delete': | |
| std::vector<RTLIL::Module *> modules_to_delete; | |
| for(auto &mod_it : design->modules_) { | |
| if (mod_it.second->get_bool_attribute("\\to_delete")) { | |
| modules_to_delete.push_back(mod_it.second); | |
| } | |
| } | |
| for(size_t i=0; i<modules_to_delete.size(); i++) { | |
| design->remove(modules_to_delete[i]); | |
| } | |
| } | |
| if (top_mod != NULL) { | |
| log_header(design, "Analyzing design hierarchy..\n"); | |
| hierarchy_clean(design, top_mod, purge_lib); | |
| } | |
| if (top_mod != NULL) { | |
| for (auto &mod_it : design->modules_) { | |
| if (mod_it.second == top_mod) | |
| mod_it.second->attributes["\\top"] = RTLIL::Const(1); | |
| else | |
| mod_it.second->attributes.erase("\\top"); | |
| mod_it.second->attributes.erase("\\initial_top"); | |
| } | |
| } | |
| if (!nokeep_asserts) { | |
| std::map<RTLIL::Module*, bool> 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("\\keep"); | |
| } | |
| } | |
| if (!keep_positionals) | |
| { | |
| std::set<RTLIL::Module*> pos_mods; | |
| std::map<std::pair<RTLIL::Module*,int>, RTLIL::IdString> pos_map; | |
| std::vector<std::pair<RTLIL::Module*,RTLIL::Cell*>> pos_work; | |
| for (auto &mod_it : design->modules_) | |
| for (auto &cell_it : mod_it.second->cells_) { | |
| RTLIL::Cell *cell = cell_it.second; | |
| if (design->modules_.count(cell->type) == 0) | |
| continue; | |
| for (auto &conn : cell->connections()) | |
| if (conn.first[0] == '$' && '0' <= conn.first[1] && conn.first[1] <= '9') { | |
| pos_mods.insert(design->modules_.at(cell->type)); | |
| pos_work.push_back(std::pair<RTLIL::Module*,RTLIL::Cell*>(mod_it.second, cell)); | |
| break; | |
| } | |
| } | |
| for (auto module : pos_mods) | |
| for (auto &wire_it : module->wires_) { | |
| RTLIL::Wire *wire = wire_it.second; | |
| if (wire->port_id > 0) | |
| pos_map[std::pair<RTLIL::Module*,int>(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<RTLIL::IdString, RTLIL::SigSpec> new_connections; | |
| for (auto &conn : cell->connections()) | |
| if (conn.first[0] == '$' && '0' <= conn.first[1] && conn.first[1] <= '9') { | |
| int id = atoi(conn.first.c_str()+1); | |
| std::pair<RTLIL::Module*,int> key(design->modules_.at(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; | |
| } | |
| } | |
| if (!nodefaults) | |
| { | |
| dict<IdString, dict<IdString, Const>> defaults_db; | |
| for (auto module : design->modules()) | |
| for (auto wire : module->wires()) | |
| if (wire->port_input && wire->attributes.count("\\defaultvalue")) | |
| defaults_db[module->name][wire->name] = wire->attributes.at("\\defaultvalue"); | |
| 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); | |
| } | |
| } | |
| std::set<Module*> blackbox_derivatives; | |
| std::vector<Module*> design_modules = design->modules(); | |
| for (auto module : design_modules) | |
| { | |
| pool<Wire*> wand_wor_index; | |
| dict<Wire*, SigSpec> wand_map, wor_map; | |
| vector<SigSig> new_connections; | |
| for (auto wire : module->wires()) | |
| { | |
| if (wire->get_bool_attribute("\\wand")) { | |
| wand_map[wire] = SigSpec(); | |
| wand_wor_index.insert(wire); | |
| } | |
| if (wire->get_bool_attribute("\\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; | |
| if (m->get_blackbox_attribute() && !cell->parameters.empty() && m->get_bool_attribute("\\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); | |
| } | |
| } | |
| 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; | |
| if (!keep_portwidths && GetSize(w) != GetSize(conn.second)) | |
| { | |
| if (GetSize(w) < GetSize(conn.second)) | |
| { | |
| int n = GetSize(conn.second) - GetSize(w); | |
| if (!w->port_input && w->port_output) | |
| module->connect(sig.extract(GetSize(w), n), Const(0, n)); | |
| sig.remove(GetSize(w), n); | |
| } | |
| else | |
| { | |
| int n = GetSize(w) - GetSize(conn.second); | |
| if (w->port_input && !w->port_output) | |
| sig.append(Const(0, n)); | |
| 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); | |
| log_pop(); | |
| } | |
| } HierarchyPass; | |
| PRIVATE_NAMESPACE_END |