| /* |
| * 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. |
| * |
| */ |
| |
| #include "kernel/register.h" |
| #include "kernel/log.h" |
| #include <stdlib.h> |
| #include <stdio.h> |
| |
| YOSYS_NAMESPACE_BEGIN |
| extern void proc_clean_case(RTLIL::CaseRule *cs, bool &did_something, int &count, int max_depth); |
| YOSYS_NAMESPACE_END |
| |
| USING_YOSYS_NAMESPACE |
| PRIVATE_NAMESPACE_BEGIN |
| |
| void proc_clean_switch(RTLIL::SwitchRule *sw, RTLIL::CaseRule *parent, bool &did_something, int &count, int max_depth) |
| { |
| if (sw->signal.size() > 0 && sw->signal.is_fully_const()) |
| { |
| int found_matching_case_idx = -1; |
| for (int i = 0; i < int(sw->cases.size()) && found_matching_case_idx < 0; i++) |
| { |
| RTLIL::CaseRule *cs = sw->cases[i]; |
| if (cs->compare.size() == 0) |
| break; |
| for (int j = 0; j < int(cs->compare.size()); j++) { |
| RTLIL::SigSpec &val = cs->compare[j]; |
| if (!val.is_fully_const()) |
| continue; |
| if (val == sw->signal) { |
| cs->compare.clear(); |
| found_matching_case_idx = i; |
| break; |
| } else |
| cs->compare.erase(cs->compare.begin()+(j--)); |
| } |
| if (cs->compare.size() == 0 && found_matching_case_idx < 0) { |
| sw->cases.erase(sw->cases.begin()+(i--)); |
| delete cs; |
| } |
| } |
| while (found_matching_case_idx >= 0 && int(sw->cases.size()) > found_matching_case_idx+1) { |
| delete sw->cases.back(); |
| sw->cases.pop_back(); |
| } |
| if (found_matching_case_idx == 0) |
| sw->signal = RTLIL::SigSpec(); |
| } |
| |
| if (parent->switches.front() == sw && sw->cases.size() == 1 && |
| (sw->signal.size() == 0 || sw->cases[0]->compare.empty())) |
| { |
| did_something = true; |
| for (auto &action : sw->cases[0]->actions) |
| parent->actions.push_back(action); |
| parent->switches.insert(parent->switches.begin(), sw->cases[0]->switches.begin(), sw->cases[0]->switches.end()); |
| sw->cases[0]->switches.clear(); |
| delete sw->cases[0]; |
| sw->cases.clear(); |
| } |
| else |
| { |
| bool all_fully_def = true; |
| for (auto cs : sw->cases) |
| { |
| if (max_depth != 0) |
| proc_clean_case(cs, did_something, count, max_depth-1); |
| int size = 0; |
| for (auto cmp : cs->compare) |
| { |
| size += cmp.size(); |
| if (!cmp.is_fully_def()) |
| all_fully_def = false; |
| } |
| if (sw->signal.size() != size) |
| all_fully_def = false; |
| } |
| if (all_fully_def) |
| { |
| for (auto cs = sw->cases.begin(); cs != sw->cases.end();) |
| { |
| if ((*cs)->empty()) |
| { |
| did_something = true; |
| delete *cs; |
| cs = sw->cases.erase(cs); |
| } |
| else ++cs; |
| } |
| } |
| else |
| { |
| while (!sw->cases.empty() && sw->cases.back()->empty()) |
| { |
| did_something = true; |
| delete sw->cases.back(); |
| sw->cases.pop_back(); |
| } |
| } |
| } |
| } |
| |
| PRIVATE_NAMESPACE_END |
| YOSYS_NAMESPACE_BEGIN |
| |
| void proc_clean_case(RTLIL::CaseRule *cs, bool &did_something, int &count, int max_depth) |
| { |
| for (size_t i = 0; i < cs->actions.size(); i++) { |
| if (cs->actions[i].first.size() == 0) { |
| did_something = true; |
| cs->actions.erase(cs->actions.begin() + (i--)); |
| } |
| } |
| for (size_t i = 0; i < cs->switches.size(); i++) { |
| RTLIL::SwitchRule *sw = cs->switches[i]; |
| if (sw->empty()) { |
| cs->switches.erase(cs->switches.begin() + (i--)); |
| did_something = true; |
| delete sw; |
| count++; |
| } else if (max_depth != 0) |
| proc_clean_switch(sw, cs, did_something, count, max_depth-1); |
| } |
| } |
| |
| YOSYS_NAMESPACE_END |
| PRIVATE_NAMESPACE_BEGIN |
| |
| void proc_clean(RTLIL::Module *mod, RTLIL::Process *proc, int &total_count, bool quiet) |
| { |
| int count = 0; |
| bool did_something = true; |
| for (size_t i = 0; i < proc->syncs.size(); i++) { |
| for (size_t j = 0; j < proc->syncs[i]->actions.size(); j++) |
| if (proc->syncs[i]->actions[j].first.size() == 0) |
| proc->syncs[i]->actions.erase(proc->syncs[i]->actions.begin() + (j--)); |
| if (proc->syncs[i]->actions.size() == 0) { |
| delete proc->syncs[i]; |
| proc->syncs.erase(proc->syncs.begin() + (i--)); |
| } |
| } |
| while (did_something) { |
| did_something = false; |
| proc_clean_case(&proc->root_case, did_something, count, -1); |
| } |
| if (count > 0 && !quiet) |
| log("Found and cleaned up %d empty switch%s in `%s.%s'.\n", count, count == 1 ? "" : "es", mod->name.c_str(), proc->name.c_str()); |
| total_count += count; |
| } |
| |
| struct ProcCleanPass : public Pass { |
| ProcCleanPass() : Pass("proc_clean", "remove empty parts of processes") { } |
| void help() YS_OVERRIDE |
| { |
| // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| |
| log("\n"); |
| log(" proc_clean [options] [selection]\n"); |
| log("\n"); |
| log(" -quiet\n"); |
| log(" do not print any messages.\n"); |
| log("\n"); |
| log("This pass removes empty parts of processes and ultimately removes a process\n"); |
| log("if it contains only empty structures.\n"); |
| log("\n"); |
| } |
| void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE |
| { |
| int total_count = 0; |
| bool quiet = false; |
| |
| if (find(args.begin(), args.end(), "-quiet") == args.end()) |
| log_header(design, "Executing PROC_CLEAN pass (remove empty switches from decision trees).\n"); |
| |
| size_t argidx; |
| for (argidx = 1; argidx < args.size(); argidx++) |
| { |
| if (args[argidx] == "-quiet") { |
| quiet = true; |
| continue; |
| } |
| } |
| extra_args(args, argidx, design); |
| |
| for (auto mod : design->modules()) { |
| std::vector<RTLIL::IdString> delme; |
| if (!design->selected(mod)) |
| continue; |
| for (auto &proc_it : mod->processes) { |
| if (!design->selected(mod, proc_it.second)) |
| continue; |
| proc_clean(mod, proc_it.second, total_count, quiet); |
| if (proc_it.second->syncs.size() == 0 && proc_it.second->root_case.switches.size() == 0 && |
| proc_it.second->root_case.actions.size() == 0) { |
| if (!quiet) |
| log("Removing empty process `%s.%s'.\n", log_id(mod), proc_it.second->name.c_str()); |
| delme.push_back(proc_it.first); |
| } |
| } |
| for (auto &id : delme) { |
| delete mod->processes[id]; |
| mod->processes.erase(id); |
| } |
| } |
| |
| if (!quiet) |
| log("Cleaned up %d empty switch%s.\n", total_count, total_count == 1 ? "" : "es"); |
| } |
| } ProcCleanPass; |
| |
| PRIVATE_NAMESPACE_END |