diff --git a/passes/silimate/Makefile.inc b/passes/silimate/Makefile.inc index d265df524..c936b0f54 100644 --- a/passes/silimate/Makefile.inc +++ b/passes/silimate/Makefile.inc @@ -5,6 +5,7 @@ OBJS += passes/silimate/annotate_logic_depth.o OBJS += passes/silimate/breaksop.o OBJS += passes/silimate/bus_rebuild.o OBJS += passes/silimate/longloop_select.o +OBJS += passes/silimate/obs_clean.o OBJS += passes/silimate/opt_balance_tree.o OBJS += passes/silimate/segv.o OBJS += passes/silimate/selectconst.o diff --git a/passes/silimate/obs_clean.cc b/passes/silimate/obs_clean.cc new file mode 100644 index 000000000..32d469401 --- /dev/null +++ b/passes/silimate/obs_clean.cc @@ -0,0 +1,273 @@ +#include "kernel/sigtools.h" +#include "kernel/yosys.h" +#include + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +// Signal cell driver(s), precompute a cell output signal to a cell map +void sigCellDrivers(RTLIL::Module *module, SigMap &sigmap, dict> &sig2CellsInFanout, + dict> &sig2CellsInFanin) +{ + for (auto cell : module->selected_cells()) { + for (auto &conn : cell->connections()) { + IdString portName = conn.first; + RTLIL::SigSpec actual = conn.second; + if (cell->output(portName)) { + sig2CellsInFanin[sigmap(actual)].insert(cell); + for (int i = 0; i < actual.size(); i++) { + SigSpec bit_sig = actual.extract(i, 1); + sig2CellsInFanin[sigmap(bit_sig)].insert(cell); + } + } else { + sig2CellsInFanout[sigmap(actual)].insert(cell); + for (int i = 0; i < actual.size(); i++) { + SigSpec bit_sig = actual.extract(i, 1); + if (!bit_sig.is_fully_const()) { + sig2CellsInFanout[sigmap(bit_sig)].insert(cell); + } + } + } + } + } +} + +// Assign statements fanin, fanout, traces the lhs2rhs and rhs2lhs sigspecs and precompute maps +void lhs2rhs_rhs2lhs(RTLIL::Module *module, SigMap &sigmap, dict> &rhsSig2LhsSig, + dict &lhsSig2rhsSig) +{ + for (auto it = module->connections().begin(); it != module->connections().end(); ++it) { + RTLIL::SigSpec lhs = it->first; + RTLIL::SigSpec rhs = it->second; + if (!lhs.is_chunk()) { + std::vector lhsBits; + for (int i = 0; i < lhs.size(); i++) { + SigSpec bit_sig = lhs.extract(i, 1); + lhsBits.push_back(bit_sig); + } + std::vector rhsBits; + for (int i = 0; i < rhs.size(); i++) { + SigSpec bit_sig = rhs.extract(i, 1); + rhsBits.push_back(bit_sig); + } + + for (uint32_t i = 0; i < lhsBits.size(); i++) { + if (i < rhsBits.size()) { + rhsSig2LhsSig[sigmap(rhsBits[i])].insert(sigmap(lhsBits[i])); + lhsSig2rhsSig[lhsBits[i]] = sigmap(rhsBits[i]); + } + } + } else { + rhsSig2LhsSig[sigmap(rhs)].insert(sigmap(lhs)); + lhsSig2rhsSig[lhs] = sigmap(rhs); + } + } +} + +// Collect transitive fanin of a sig +void collectTransitiveFanin(RTLIL::SigSpec &sig, SigMap &sigmap, dict> &sig2CellsInFanin, + dict &lhsSig2RhsSig, std::set &visitedCells, + std::set &visitedSigSpec) +{ + if (visitedSigSpec.count(sigmap(sig))) { + return; + } + visitedSigSpec.insert(sigmap(sig)); + + if (sig2CellsInFanin.count(sigmap(sig))) { + for (Cell *cell : sig2CellsInFanin[sigmap(sig)]) { + if (visitedCells.count(cell)) { + continue; + } + visitedCells.insert(cell); + for (auto &conn : cell->connections()) { + IdString portName = conn.first; + RTLIL::SigSpec actual = conn.second; + if (cell->input(portName)) { + collectTransitiveFanin(actual, sigmap, sig2CellsInFanin, lhsSig2RhsSig, visitedCells, visitedSigSpec); + for (int i = 0; i < actual.size(); i++) { + SigSpec bit_sig = actual.extract(i, 1); + collectTransitiveFanin(bit_sig, sigmap, sig2CellsInFanin, lhsSig2RhsSig, visitedCells, + visitedSigSpec); + } + } + } + } + } + if (lhsSig2RhsSig.count(sigmap(sig))) { + RTLIL::SigSpec rhs = lhsSig2RhsSig[sigmap(sig)]; + collectTransitiveFanin(rhs, sigmap, sig2CellsInFanin, lhsSig2RhsSig, visitedCells, visitedSigSpec); + for (int i = 0; i < rhs.size(); i++) { + SigSpec bit_sig = rhs.extract(i, 1); + collectTransitiveFanin(bit_sig, sigmap, sig2CellsInFanin, lhsSig2RhsSig, visitedCells, visitedSigSpec); + } + } +} + +// Only keep the cells and wires that are visited using the transitive fanin reached from output ports or keep signals +void observabilityClean(RTLIL::Module *module, SigMap &sigmap, dict> &sig2CellsInFanin, + dict &lhsSig2RhsSig, bool unused_wires) +{ + if (module->get_bool_attribute(ID::keep)) + return; + std::set visitedCells; + std::set visitedSigSpec; + + // Collect observable logic (connected to one output) + for (auto elt : sig2CellsInFanin) { + RTLIL::SigSpec po = elt.first; + RTLIL::Wire *w = po[0].wire; + if (w && (!w->port_output) && (!w->get_bool_attribute(ID::keep))) { + continue; + } + RTLIL::SigSpec spo = sigmap(po); + collectTransitiveFanin(spo, sigmap, sig2CellsInFanin, lhsSig2RhsSig, visitedCells, visitedSigSpec); + for (int i = 0; i < po.size(); i++) { + SigSpec bit_sig = po.extract(i, 1); + bit_sig = sigmap(bit_sig); + collectTransitiveFanin(bit_sig, sigmap, sig2CellsInFanin, lhsSig2RhsSig, visitedCells, visitedSigSpec); + } + } + + for (auto elt : lhsSig2RhsSig) { + RTLIL::SigSpec po = elt.first; + RTLIL::Wire *w = po[0].wire; + if (w && (!w->port_output) && (!w->get_bool_attribute(ID::keep))) { + continue; + } + RTLIL::SigSpec spo = sigmap(po); + collectTransitiveFanin(spo, sigmap, sig2CellsInFanin, lhsSig2RhsSig, visitedCells, visitedSigSpec); + for (int i = 0; i < po.size(); i++) { + SigSpec bit_sig = po.extract(i, 1); + bit_sig = sigmap(bit_sig); + collectTransitiveFanin(bit_sig, sigmap, sig2CellsInFanin, lhsSig2RhsSig, visitedCells, visitedSigSpec); + } + } + + // Remove unused assign stmts + std::vector newConnections; + for (auto it = module->connections().begin(); it != module->connections().end(); ++it) { + RTLIL::SigSpec lhs = it->first; + if (visitedSigSpec.count(sigmap(lhs))) { + newConnections.push_back(*it); + } else { + for (int i = 0; i < lhs.size(); i++) { + SigSpec bit_sig = lhs.extract(i, 1); + if (visitedSigSpec.count(sigmap(bit_sig))) { + newConnections.push_back(*it); + break; + } + } + } + } + + module->connections_.clear(); + for (auto conn : newConnections) { + module->connect(conn); + } + + if (unused_wires) { + // Remove unused wires + // TODO: This impacts equiv_opt ability to perform equivalence checking + pool wiresToRemove; + for (auto wire : module->wires()) { + RTLIL::SigSpec sig = wire; + if (visitedSigSpec.count(sigmap(sig))) { + continue; + } + bool bitVisited = false; + for (int i = 0; i < sig.size(); i++) { + SigSpec bit_sig = sig.extract(i, 1); + if (visitedSigSpec.count(bit_sig)) { + bitVisited = true; + break; + } + } + if (bitVisited) + continue; + if (wire->port_id) { + continue; + } + if (wire->get_bool_attribute(ID::keep)) + continue; + wiresToRemove.insert(wire); + } + + module->remove(wiresToRemove); + } + + // Remove unused cells + std::set cellsToRemove; + for (auto cell : module->cells()) { + if (visitedCells.count(cell)) { + continue; + } + if (cell->has_keep_attr()) + continue; + cellsToRemove.insert(cell); + } + + for (auto cell : cellsToRemove) { + module->remove(cell); + } +} + +struct ObsClean : public ScriptPass { + ObsClean() : ScriptPass("obs_clean", "Observability-based cleanup") {} + void script() override {} + void help() override + { + log("\n"); + log(" obs_clean [options] [selection]\n"); + log("\n"); + log("This pass performs an obversability-based logic removal.\n"); + log("\n"); + log(" -wires\n"); + log(" Also removes dangling wires. This option prevents formal verifciation at this time.\n"); + log("\n"); + } + void execute(std::vector args, RTLIL::Design *design) override + { + bool unused_wires = false; + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-wires") { + unused_wires = true; + continue; + } + break; + } + extra_args(args, argidx, design); + + if (design == nullptr) { + log_error("No design object"); + return; + } + log("Running obs_clean pass\n"); + log_flush(); + for (auto module : design->selected_modules()) { + // We cannot safely perform this analysis when processes or memories are present + if (module->has_processes_warn()) + continue; + if (module->has_memories_warn()) + continue; + + SigMap sigmap(module); + // Precompute cell output sigspec to cell map + dict> sig2CellsInFanin; + dict> sig2CellsInFanout; + sigCellDrivers(module, sigmap, sig2CellsInFanout, sig2CellsInFanin); + // Precompute lhs2rhs and rhs2lhs sigspec map + dict lhsSig2RhsSig; + dict> rhsSig2LhsSig; + lhs2rhs_rhs2lhs(module, sigmap, rhsSig2LhsSig, lhsSig2RhsSig); + // Actual cleanup + observabilityClean(module, sigmap, sig2CellsInFanin, lhsSig2RhsSig, unused_wires); + } + log("End obs_clean pass\n"); + log_flush(); + } +} SplitNetlist; + +PRIVATE_NAMESPACE_END