diff --git a/passes/opt/Makefile.inc b/passes/opt/Makefile.inc index a8b1537bb..a145cf946 100644 --- a/passes/opt/Makefile.inc +++ b/passes/opt/Makefile.inc @@ -6,6 +6,7 @@ OBJS += passes/opt/opt_reduce.o OBJS += passes/opt/opt_rmdff.o OBJS += passes/opt/opt_clean.o OBJS += passes/opt/opt_expr.o +OBJS += passes/opt/rmports.o ifneq ($(SMALL),1) OBJS += passes/opt/share.o diff --git a/passes/opt/rmports.cc b/passes/opt/rmports.cc new file mode 100644 index 000000000..756be7473 --- /dev/null +++ b/passes/opt/rmports.cc @@ -0,0 +1,187 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * 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/register.h" +#include "kernel/sigtools.h" +#include "kernel/log.h" +#include +#include + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct RmportsPassPass : public Pass { + RmportsPassPass() : Pass("rmports", "remove module ports with no connections") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" rmports [selection]\n"); + log("\n"); + log("This pass identifies ports in the selected modules which are not used or\n"); + log("driven and removes them.\n"); + log("\n"); + } + + virtual void execute(std::vector args, RTLIL::Design *design) + { + log_header(design, "Executing RMPORTS pass (remove ports with no connections).\n"); + + size_t argidx = 1; + extra_args(args, argidx, design); + + // The set of ports we removed + dict> removed_ports; + + // Find all of the unused ports, and remove them from that module + auto modules = design->selected_modules(); + for(auto mod : modules) + ScanModule(mod, removed_ports); + + // Remove the unused ports from all instances of those modules + for(auto mod : modules) + CleanupModule(mod, removed_ports); + } + + void CleanupModule(Module *module, dict> &removed_ports) + { + log("Removing now-unused cell ports in module %s\n", module->name.c_str()); + + auto cells = module->cells(); + for(auto cell : cells) + { + if(removed_ports.find(cell->type) == removed_ports.end()) + { + // log(" Not touching instance \"%s\" because we didn't remove any ports from module \"%s\"\n", + // cell->name.c_str(), cell->type.c_str()); + continue; + } + + auto ports_to_remove = removed_ports[cell->type]; + for(auto p : ports_to_remove) + { + log(" Removing port \"%s\" from instance \"%s\"\n", + p.c_str(), cell->type.c_str()); + cell->unsetPort(p); + } + } + } + + void ScanModule(Module* module, dict> &removed_ports) + { + log("Finding unconnected ports in module %s\n", module->name.c_str()); + + pool used_ports; + + // See what wires are used. + // Start by checking connections between named wires + auto &conns = module->connections(); + for(auto sigsig : conns) + { + auto s1 = sigsig.first; + auto s2 = sigsig.second; + + int len1 = s1.size(); + int len2 = s2.size(); + int len = len1; + if(len2 < len1) + len = len2; + + for(int i=0; iname.c_str(), w2->name.c_str()); + + if( (w1->port_input || w1->port_output) && (used_ports.find(w1->name) == used_ports.end()) ) + used_ports.insert(w1->name); + + if( (w2->port_input || w2->port_output) && (used_ports.find(w2->name) == used_ports.end()) ) + used_ports.insert(w2->name); + } + } + + // Then check connections to cells + auto cells = module->cells(); + for(auto cell : cells) + { + auto &cconns = cell->connections(); + for(auto conn : cconns) + { + for(int i=0; iname.c_str()); + if( (sig->port_input || sig->port_output) && (used_ports.find(sig->name) == used_ports.end()) ) + used_ports.insert(sig->name); + } + } + } + + // Now that we know what IS used, get rid of anything that isn't in that list + pool unused_ports; + for(auto port : module->ports) + { + if(used_ports.find(port) != used_ports.end()) + continue; + unused_ports.insert(port); + } + + // Print the ports out as we go through them + for(auto port : unused_ports) + { + log(" removing unused port %s\n", port.c_str()); + removed_ports[module->name].insert(port); + + // Remove from ports list + for(size_t i=0; iports.size(); i++) + { + if(module->ports[i] == port) + { + module->ports.erase(module->ports.begin() + i); + break; + } + } + + // Mark the wire as no longer a port + auto wire = module->wire(port); + wire->port_input = false; + wire->port_output = false; + wire->port_id = 0; + } + log("Removed %zu unused ports.\n", unused_ports.size()); + + // Re-number all of the wires that DO have ports still on them + for(size_t i=0; iports.size(); i++) + { + auto port = module->ports[i]; + auto wire = module->wire(port); + wire->port_id = i+1; + } + } + +} RmportsPassPass; + +PRIVATE_NAMESPACE_END