| /* |
| * yosys -- Yosys Open SYnthesis Suite |
| * |
| * Copyright (C) 2019 whitequark <whitequark@whitequark.org> |
| * |
| * 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 <stdlib.h> |
| #include <stdio.h> |
| |
| USING_YOSYS_NAMESPACE |
| PRIVATE_NAMESPACE_BEGIN |
| |
| struct PruneWorker |
| { |
| RTLIL::Module *module; |
| SigMap sigmap; |
| |
| int removed_count = 0, promoted_count = 0; |
| |
| PruneWorker(RTLIL::Module *mod) : module(mod), sigmap(mod) {} |
| |
| pool<RTLIL::SigBit> do_switch(RTLIL::SwitchRule *sw, pool<RTLIL::SigBit> assigned, pool<RTLIL::SigBit> &affected) |
| { |
| pool<RTLIL::SigBit> all_assigned; |
| bool full_case = sw->get_bool_attribute("\\full_case"); |
| bool first = true; |
| for (auto it : sw->cases) { |
| if (it->compare.empty()) |
| full_case = true; |
| pool<RTLIL::SigBit> case_assigned = do_case(it, assigned, affected); |
| if (first) { |
| first = false; |
| all_assigned = case_assigned; |
| } else { |
| for (auto &bit : all_assigned) |
| if (!case_assigned[bit]) |
| all_assigned.erase(bit); |
| } |
| } |
| if (full_case) |
| assigned.insert(all_assigned.begin(), all_assigned.end()); |
| return assigned; |
| } |
| |
| pool<RTLIL::SigBit> do_case(RTLIL::CaseRule *cs, pool<RTLIL::SigBit> assigned, pool<RTLIL::SigBit> &affected, |
| bool root = false) |
| { |
| for (auto it = cs->switches.rbegin(); it != cs->switches.rend(); ++it) { |
| pool<RTLIL::SigBit> sw_assigned = do_switch((*it), assigned, affected); |
| assigned.insert(sw_assigned.begin(), sw_assigned.end()); |
| } |
| for (auto it = cs->actions.rbegin(); it != cs->actions.rend(); ) { |
| RTLIL::SigSpec lhs = sigmap(it->first); |
| bool redundant = true; |
| for (auto &bit : lhs) { |
| if (bit.wire && !assigned[bit]) { |
| redundant = false; |
| break; |
| } |
| } |
| bool remove = false; |
| if (redundant) { |
| removed_count++; |
| remove = true; |
| } else { |
| if (root) { |
| bool promotable = true; |
| for (auto &bit : lhs) { |
| if (bit.wire && affected[bit] && !assigned[bit]) { |
| promotable = false; |
| break; |
| } |
| } |
| if (promotable) { |
| RTLIL::SigSpec rhs = sigmap(it->second); |
| RTLIL::SigSig conn; |
| for (int i = 0; i < GetSize(lhs); i++) { |
| RTLIL::SigBit lhs_bit = lhs[i]; |
| if (lhs_bit.wire && !assigned[lhs_bit]) { |
| conn.first.append_bit(lhs_bit); |
| conn.second.append(rhs.extract(i)); |
| } |
| } |
| promoted_count++; |
| module->connect(conn); |
| remove = true; |
| } |
| } |
| for (auto &bit : lhs) |
| if (bit.wire) |
| assigned.insert(bit); |
| for (auto &bit : lhs) |
| if (bit.wire) |
| affected.insert(bit); |
| } |
| if (remove) |
| cs->actions.erase((it++).base() - 1); |
| else it++; |
| } |
| return assigned; |
| } |
| |
| void do_process(RTLIL::Process *pr) |
| { |
| pool<RTLIL::SigBit> affected; |
| do_case(&pr->root_case, {}, affected, /*root=*/true); |
| } |
| }; |
| |
| struct ProcPrunePass : public Pass { |
| ProcPrunePass() : Pass("proc_prune", "remove redundant assignments") { } |
| void help() YS_OVERRIDE |
| { |
| // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| |
| log("\n"); |
| log(" proc_prune [selection]\n"); |
| log("\n"); |
| log("This pass identifies assignments in processes that are always overwritten by\n"); |
| log("a later assignment to the same signal and removes them.\n"); |
| log("\n"); |
| } |
| void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE |
| { |
| int total_removed_count = 0, total_promoted_count = 0; |
| log_header(design, "Executing PROC_PRUNE pass (remove redundant assignments in processes).\n"); |
| |
| extra_args(args, 1, design); |
| |
| for (auto mod : design->modules()) { |
| if (!design->selected(mod)) |
| continue; |
| PruneWorker worker(mod); |
| for (auto &proc_it : mod->processes) { |
| if (!design->selected(mod, proc_it.second)) |
| continue; |
| worker.do_process(proc_it.second); |
| } |
| total_removed_count += worker.removed_count; |
| total_promoted_count += worker.promoted_count; |
| } |
| |
| log("Removed %d redundant assignment%s.\n", |
| total_removed_count, total_removed_count == 1 ? "" : "s"); |
| log("Promoted %d assignment%s to connection%s.\n", |
| total_promoted_count, total_promoted_count == 1 ? "" : "s", total_promoted_count == 1 ? "" : "s"); |
| } |
| } ProcPrunePass; |
| |
| PRIVATE_NAMESPACE_END |