| /* |
| * yosys -- Yosys Open SYnthesis Suite |
| * |
| * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> |
| * |
| * 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. |
| * |
| */ |
| |
| // [[CITE]] Tarjan's strongly connected components algorithm |
| // Tarjan, R. E. (1972), "Depth-first search and linear graph algorithms", SIAM Journal on Computing 1 (2): 146-160, doi:10.1137/0201010 |
| // http://en.wikipedia.org/wiki/Tarjan's_strongly_connected_components_algorithm |
| |
| #include "kernel/register.h" |
| #include "kernel/celltypes.h" |
| #include "kernel/sigtools.h" |
| #include "kernel/log.h" |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <set> |
| |
| USING_YOSYS_NAMESPACE |
| PRIVATE_NAMESPACE_BEGIN |
| |
| struct SccWorker |
| { |
| RTLIL::Design *design; |
| RTLIL::Module *module; |
| SigMap sigmap; |
| CellTypes ct; |
| |
| std::set<RTLIL::Cell*> workQueue; |
| std::map<RTLIL::Cell*, std::set<RTLIL::Cell*>> cellToNextCell; |
| std::map<RTLIL::Cell*, RTLIL::SigSpec> cellToPrevSig, cellToNextSig; |
| |
| std::map<RTLIL::Cell*, std::pair<int, int>> cellLabels; |
| std::map<RTLIL::Cell*, int> cellDepth; |
| std::set<RTLIL::Cell*> cellsOnStack; |
| std::vector<RTLIL::Cell*> cellStack; |
| int labelCounter; |
| |
| std::map<RTLIL::Cell*, int> cell2scc; |
| std::vector<std::set<RTLIL::Cell*>> sccList; |
| |
| void run(RTLIL::Cell *cell, int depth, int maxDepth) |
| { |
| log_assert(workQueue.count(cell) > 0); |
| |
| workQueue.erase(cell); |
| cellLabels[cell] = std::pair<int, int>(labelCounter, labelCounter); |
| labelCounter++; |
| |
| cellsOnStack.insert(cell); |
| cellStack.push_back(cell); |
| |
| if (maxDepth >= 0) |
| cellDepth[cell] = depth; |
| |
| for (auto nextCell : cellToNextCell[cell]) |
| if (cellLabels.count(nextCell) == 0) { |
| run(nextCell, depth+1, maxDepth); |
| cellLabels[cell].second = min(cellLabels[cell].second, cellLabels[nextCell].second); |
| } else |
| if (cellsOnStack.count(nextCell) > 0 && (maxDepth < 0 || cellDepth[nextCell] + maxDepth > depth)) { |
| cellLabels[cell].second = min(cellLabels[cell].second, cellLabels[nextCell].second); |
| } |
| |
| if (cellLabels[cell].first == cellLabels[cell].second) |
| { |
| if (cellStack.back() == cell) |
| { |
| cellStack.pop_back(); |
| cellsOnStack.erase(cell); |
| } |
| else |
| { |
| log("Found an SCC:"); |
| std::set<RTLIL::Cell*> scc; |
| while (cellsOnStack.count(cell) > 0) { |
| RTLIL::Cell *c = cellStack.back(); |
| cellStack.pop_back(); |
| cellsOnStack.erase(c); |
| log(" %s", RTLIL::id2cstr(c->name)); |
| cell2scc[c] = sccList.size(); |
| scc.insert(c); |
| } |
| sccList.push_back(scc); |
| log("\n"); |
| } |
| } |
| } |
| |
| SccWorker(RTLIL::Design *design, RTLIL::Module *module, bool nofeedbackMode, bool allCellTypes, int maxDepth) : |
| design(design), module(module), sigmap(module) |
| { |
| if (module->processes.size() > 0) { |
| log("Skipping module %s as it contains processes (run 'proc' pass first).\n", module->name.c_str()); |
| return; |
| } |
| |
| if (allCellTypes) { |
| ct.setup(design); |
| } else { |
| ct.setup_internals(); |
| ct.setup_stdcells(); |
| } |
| |
| SigPool selectedSignals; |
| SigSet<RTLIL::Cell*> sigToNextCells; |
| |
| for (auto &it : module->wires_) |
| if (design->selected(module, it.second)) |
| selectedSignals.add(sigmap(RTLIL::SigSpec(it.second))); |
| |
| for (auto &it : module->cells_) |
| { |
| RTLIL::Cell *cell = it.second; |
| |
| if (!design->selected(module, cell)) |
| continue; |
| |
| if (!allCellTypes && !ct.cell_known(cell->type)) |
| continue; |
| |
| workQueue.insert(cell); |
| |
| RTLIL::SigSpec inputSignals, outputSignals; |
| |
| for (auto &conn : cell->connections()) |
| { |
| bool isInput = true, isOutput = true; |
| |
| if (ct.cell_known(cell->type)) { |
| isInput = ct.cell_input(cell->type, conn.first); |
| isOutput = ct.cell_output(cell->type, conn.first); |
| } |
| |
| RTLIL::SigSpec sig = selectedSignals.extract(sigmap(conn.second)); |
| sig.sort_and_unify(); |
| |
| if (isInput) |
| inputSignals.append(sig); |
| if (isOutput) |
| outputSignals.append(sig); |
| } |
| |
| inputSignals.sort_and_unify(); |
| outputSignals.sort_and_unify(); |
| |
| cellToPrevSig[cell] = inputSignals; |
| cellToNextSig[cell] = outputSignals; |
| sigToNextCells.insert(inputSignals, cell); |
| } |
| |
| for (auto cell : workQueue) |
| { |
| cellToNextCell[cell] = sigToNextCells.find(cellToNextSig[cell]); |
| |
| if (!nofeedbackMode && cellToNextCell[cell].count(cell)) { |
| log("Found an SCC:"); |
| std::set<RTLIL::Cell*> scc; |
| log(" %s", RTLIL::id2cstr(cell->name)); |
| cell2scc[cell] = sccList.size(); |
| scc.insert(cell); |
| sccList.push_back(scc); |
| log("\n"); |
| } |
| } |
| |
| labelCounter = 0; |
| cellLabels.clear(); |
| |
| while (!workQueue.empty()) |
| { |
| RTLIL::Cell *cell = *workQueue.begin(); |
| log_assert(cellStack.size() == 0); |
| cellDepth.clear(); |
| |
| run(cell, 0, maxDepth); |
| } |
| |
| log("Found %d SCCs in module %s.\n", int(sccList.size()), RTLIL::id2cstr(module->name)); |
| } |
| |
| void select(RTLIL::Selection &sel) |
| { |
| for (int i = 0; i < int(sccList.size()); i++) |
| { |
| std::set<RTLIL::Cell*> &cells = sccList[i]; |
| RTLIL::SigSpec prevsig, nextsig, sig; |
| |
| for (auto cell : cells) { |
| sel.selected_members[module->name].insert(cell->name); |
| prevsig.append(cellToPrevSig[cell]); |
| nextsig.append(cellToNextSig[cell]); |
| } |
| |
| prevsig.sort_and_unify(); |
| nextsig.sort_and_unify(); |
| sig = prevsig.extract(nextsig); |
| |
| for (auto &chunk : sig.chunks()) |
| if (chunk.wire != NULL) |
| sel.selected_members[module->name].insert(chunk.wire->name); |
| } |
| } |
| }; |
| |
| struct SccPass : public Pass { |
| SccPass() : Pass("scc", "detect strongly connected components (logic loops)") { } |
| void help() YS_OVERRIDE |
| { |
| // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| |
| log("\n"); |
| log(" scc [options] [selection]\n"); |
| log("\n"); |
| log("This command identifies strongly connected components (aka logic loops) in the\n"); |
| log("design.\n"); |
| log("\n"); |
| log(" -expect <num>\n"); |
| log(" expect to find exactly <num> SSCs. A different number of SSCs will\n"); |
| log(" produce an error.\n"); |
| log("\n"); |
| log(" -max_depth <num>\n"); |
| log(" limit to loops not longer than the specified number of cells. This\n"); |
| log(" can e.g. be useful in identifying small local loops in a module that\n"); |
| log(" implements one large SCC.\n"); |
| log("\n"); |
| log(" -nofeedback\n"); |
| log(" do not count cells that have their output fed back into one of their\n"); |
| log(" inputs as single-cell scc.\n"); |
| log("\n"); |
| log(" -all_cell_types\n"); |
| log(" Usually this command only considers internal non-memory cells. With\n"); |
| log(" this option set, all cells are considered. For unknown cells all ports\n"); |
| log(" are assumed to be bidirectional 'inout' ports.\n"); |
| log("\n"); |
| log(" -set_attr <name> <value>\n"); |
| log(" set the specified attribute on all cells that are part of a logic\n"); |
| log(" loop. the special token {} in the value is replaced with a unique\n"); |
| log(" identifier for the logic loop.\n"); |
| log("\n"); |
| log(" -select\n"); |
| log(" replace the current selection with a selection of all cells and wires\n"); |
| log(" that are part of a found logic loop\n"); |
| log("\n"); |
| } |
| void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE |
| { |
| std::map<std::string, std::string> setAttr; |
| bool allCellTypes = false; |
| bool selectMode = false; |
| bool nofeedbackMode = false; |
| int maxDepth = -1; |
| int expect = -1; |
| |
| log_header(design, "Executing SCC pass (detecting logic loops).\n"); |
| |
| size_t argidx; |
| for (argidx = 1; argidx < args.size(); argidx++) { |
| if (args[argidx] == "-max_depth" && argidx+1 < args.size()) { |
| maxDepth = atoi(args[++argidx].c_str()); |
| continue; |
| } |
| if (args[argidx] == "-expect" && argidx+1 < args.size()) { |
| expect = atoi(args[++argidx].c_str()); |
| continue; |
| } |
| if (args[argidx] == "-nofeedback") { |
| nofeedbackMode = true; |
| continue; |
| } |
| if (args[argidx] == "-all_cell_types") { |
| allCellTypes = true; |
| continue; |
| } |
| if (args[argidx] == "-set_attr" && argidx+2 < args.size()) { |
| setAttr[args[argidx+1]] = args[argidx+2]; |
| argidx += 2; |
| continue; |
| } |
| if (args[argidx] == "-select") { |
| selectMode = true; |
| continue; |
| } |
| break; |
| } |
| int origSelectPos = design->selection_stack.size() - 1; |
| extra_args(args, argidx, design); |
| |
| RTLIL::Selection newSelection(false); |
| int scc_counter = 0; |
| |
| for (auto &mod_it : design->modules_) |
| if (design->selected(mod_it.second)) |
| { |
| SccWorker worker(design, mod_it.second, nofeedbackMode, allCellTypes, maxDepth); |
| |
| if (!setAttr.empty()) |
| { |
| for (const auto &cells : worker.sccList) |
| { |
| for (auto attr : setAttr) |
| { |
| IdString attr_name(RTLIL::escape_id(attr.first)); |
| string attr_valstr = attr.second; |
| string index = stringf("%d", scc_counter); |
| |
| for (size_t pos = 0; (pos = attr_valstr.find("{}", pos)) != string::npos; pos += index.size()) |
| attr_valstr.replace(pos, 2, index); |
| |
| Const attr_value(attr_valstr); |
| |
| for (auto cell : cells) |
| cell->attributes[attr_name] = attr_value; |
| } |
| |
| scc_counter++; |
| } |
| } |
| else |
| { |
| scc_counter += GetSize(worker.sccList); |
| } |
| |
| if (selectMode) |
| worker.select(newSelection); |
| } |
| |
| if (expect >= 0) { |
| if (scc_counter == expect) |
| log("Found and expected %d SCCs.\n", scc_counter); |
| else |
| log_error("Found %d SCCs but expected %d.\n", scc_counter, expect); |
| } else |
| log("Found %d SCCs.\n", scc_counter); |
| |
| if (selectMode) { |
| log_assert(origSelectPos >= 0); |
| design->selection_stack[origSelectPos] = newSelection; |
| design->selection_stack[origSelectPos].optimize(design); |
| } |
| } |
| } SccPass; |
| |
| PRIVATE_NAMESPACE_END |