diff options
author | Ahmed Irfan <ahmedirfan1983@gmail.com> | 2014-09-22 11:35:04 +0200 |
---|---|---|
committer | Ahmed Irfan <ahmedirfan1983@gmail.com> | 2014-09-22 11:35:04 +0200 |
commit | d3c67ad9b61f602de1100cd264efd227dcacb417 (patch) | |
tree | 88c462c53bdab128cd1edbded42483772f82612a /passes/opt | |
parent | b783dbe148e6d246ebd107c0913de2989ab5af48 (diff) | |
parent | 13117bb346dd02d2345f716b4403239aebe3d0e2 (diff) | |
download | yosys-d3c67ad9b61f602de1100cd264efd227dcacb417.tar.gz yosys-d3c67ad9b61f602de1100cd264efd227dcacb417.tar.bz2 yosys-d3c67ad9b61f602de1100cd264efd227dcacb417.zip |
Merge branch 'master' of https://github.com/cliffordwolf/yosys into btor
added case for memwr cell that is used in muxes (same cell is used more than one time)
corrected bug for xnor and logic_not
added pmux cell translation
Conflicts:
backends/btor/btor.cc
Diffstat (limited to 'passes/opt')
-rw-r--r-- | passes/opt/Makefile.inc | 2 | ||||
-rw-r--r-- | passes/opt/opt.cc | 88 | ||||
-rw-r--r-- | passes/opt/opt_clean.cc | 127 | ||||
-rw-r--r-- | passes/opt/opt_const.cc | 753 | ||||
-rw-r--r-- | passes/opt/opt_muxtree.cc | 93 | ||||
-rw-r--r-- | passes/opt/opt_reduce.cc | 258 | ||||
-rw-r--r-- | passes/opt/opt_rmdff.cc | 84 | ||||
-rw-r--r-- | passes/opt/opt_share.cc | 52 | ||||
-rw-r--r-- | passes/opt/opt_status.h | 26 | ||||
-rw-r--r-- | passes/opt/share.cc | 1171 | ||||
-rw-r--r-- | passes/opt/wreduce.cc | 350 |
11 files changed, 2555 insertions, 449 deletions
diff --git a/passes/opt/Makefile.inc b/passes/opt/Makefile.inc index 9dfb32c87..3a8d27f93 100644 --- a/passes/opt/Makefile.inc +++ b/passes/opt/Makefile.inc @@ -6,4 +6,6 @@ OBJS += passes/opt/opt_reduce.o OBJS += passes/opt/opt_rmdff.o OBJS += passes/opt/opt_clean.o OBJS += passes/opt/opt_const.o +OBJS += passes/opt/share.o +OBJS += passes/opt/wreduce.o diff --git a/passes/opt/opt.cc b/passes/opt/opt.cc index 8f4725e1c..b20521d1e 100644 --- a/passes/opt/opt.cc +++ b/passes/opt/opt.cc @@ -17,43 +17,55 @@ * */ -#include "opt_status.h" #include "kernel/register.h" #include "kernel/log.h" #include <stdlib.h> #include <stdio.h> -bool OPT_DID_SOMETHING; - struct OptPass : public Pass { OptPass() : Pass("opt", "perform simple optimizations") { } virtual void help() { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); - log(" opt [-purge] [-mux_undef] [-mux_bool] [-undriven] [selection]\n"); + log(" opt [options] [selection]\n"); log("\n"); log("This pass calls all the other opt_* passes in a useful order. This performs\n"); log("a series of trivial optimizations and cleanups. This pass executes the other\n"); log("passes in the following order:\n"); log("\n"); - log(" opt_const\n"); + log(" opt_const [-mux_undef] [-mux_bool] [-undriven] [-fine] [-keepdc]\n"); log(" opt_share -nomux\n"); log("\n"); log(" do\n"); log(" opt_muxtree\n"); - log(" opt_reduce\n"); + log(" opt_reduce [-fine]\n"); + log(" opt_share\n"); + log(" opt_rmdff\n"); + log(" opt_clean [-purge]\n"); + log(" opt_const [-mux_undef] [-mux_bool] [-undriven] [-fine] [-keepdc]\n"); + log(" while <changed design>\n"); + log("\n"); + log("When called with -fast the following script is used instead:\n"); + log("\n"); + log(" do\n"); + log(" opt_const [-mux_undef] [-mux_bool] [-undriven] [-fine] [-keepdc]\n"); log(" opt_share\n"); log(" opt_rmdff\n"); log(" opt_clean [-purge]\n"); - log(" opt_const [-mux_undef] [-mux_bool] [-undriven]\n"); - log(" while [changed design]\n"); + log(" while <changed design in opt_rmdff>\n"); + log("\n"); + log("Note: Options in square brackets (such as [-keepdc]) are passed through to\n"); + log("the opt_* commands when given to 'opt'.\n"); + log("\n"); log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { std::string opt_clean_args; std::string opt_const_args; + std::string opt_reduce_args; + bool fast_mode = false; log_header("Executing OPT pass (performing simple optimizations).\n"); log_push(); @@ -76,32 +88,56 @@ struct OptPass : public Pass { opt_const_args += " -undriven"; continue; } + if (args[argidx] == "-fine") { + opt_const_args += " -fine"; + opt_reduce_args += " -fine"; + continue; + } + if (args[argidx] == "-keepdc") { + opt_const_args += " -keepdc"; + continue; + } + if (args[argidx] == "-fast") { + fast_mode = true; + continue; + } break; } extra_args(args, argidx, design); - log_header("Optimizing in-memory representation of design.\n"); - design->optimize(); - - Pass::call(design, "opt_const"); - Pass::call(design, "opt_share -nomux"); - while (1) { - OPT_DID_SOMETHING = false; - Pass::call(design, "opt_muxtree"); - Pass::call(design, "opt_reduce"); - Pass::call(design, "opt_share"); - Pass::call(design, "opt_rmdff"); + if (fast_mode) + { + while (1) { + Pass::call(design, "opt_const" + opt_const_args); + Pass::call(design, "opt_share"); + design->scratchpad_unset("opt.did_something"); + Pass::call(design, "opt_rmdff"); + if (design->scratchpad_get_bool("opt.did_something") == false) + break; + Pass::call(design, "opt_clean" + opt_clean_args); + log_header("Rerunning OPT passes. (Removed registers in this run.)\n"); + } Pass::call(design, "opt_clean" + opt_clean_args); + } + else + { Pass::call(design, "opt_const" + opt_const_args); - if (OPT_DID_SOMETHING == false) - break; - log_header("Rerunning OPT passes. (Maybe there is more to do..)\n"); + Pass::call(design, "opt_share -nomux"); + while (1) { + design->scratchpad_unset("opt.did_something"); + Pass::call(design, "opt_muxtree"); + Pass::call(design, "opt_reduce" + opt_reduce_args); + Pass::call(design, "opt_share"); + Pass::call(design, "opt_rmdff"); + Pass::call(design, "opt_clean" + opt_clean_args); + Pass::call(design, "opt_const" + opt_const_args); + if (design->scratchpad_get_bool("opt.did_something") == false) + break; + log_header("Rerunning OPT passes. (Maybe there is more to do..)\n"); + } } - log_header("Optimizing in-memory representation of design.\n"); - design->optimize(); - - log_header("Finished OPT passes. (There is nothing left to do.)\n"); + log_header(fast_mode ? "Finished fast OPT passes." : "Finished OPT passes. (There is nothing left to do.)\n"); log_pop(); } } OptPass; diff --git a/passes/opt/opt_clean.cc b/passes/opt/opt_clean.cc index 733a1cbf1..5046752f9 100644 --- a/passes/opt/opt_clean.cc +++ b/passes/opt/opt_clean.cc @@ -17,13 +17,11 @@ * */ -#include "opt_status.h" #include "kernel/register.h" #include "kernel/sigtools.h" #include "kernel/log.h" #include "kernel/celltypes.h" #include <stdlib.h> -#include <assert.h> #include <stdio.h> #include <set> @@ -35,12 +33,12 @@ static int count_rm_cells, count_rm_wires; static void rmunused_module_cells(RTLIL::Module *module, bool verbose) { SigMap assign_map(module); - std::set<RTLIL::Cell*, RTLIL::sort_by_name<RTLIL::Cell>> queue, unused; + std::set<RTLIL::Cell*, RTLIL::sort_by_name_id<RTLIL::Cell>> queue, unused; SigSet<RTLIL::Cell*> wire2driver; - for (auto &it : module->cells) { + for (auto &it : module->cells_) { RTLIL::Cell *cell = it.second; - for (auto &it2 : cell->connections) { + for (auto &it2 : cell->connections()) { if (!ct.cell_input(cell->type, it2.first)) { RTLIL::SigSpec sig = it2.second; assign_map.apply(sig); @@ -52,7 +50,7 @@ static void rmunused_module_cells(RTLIL::Module *module, bool verbose) unused.insert(cell); } - for (auto &it : module->wires) { + for (auto &it : module->wires_) { RTLIL::Wire *wire = it.second; if (wire->port_output || wire->get_bool_attribute("\\keep")) { std::set<RTLIL::Cell*> cell_list; @@ -66,11 +64,11 @@ static void rmunused_module_cells(RTLIL::Module *module, bool verbose) while (queue.size() > 0) { - std::set<RTLIL::Cell*, RTLIL::sort_by_name<RTLIL::Cell>> new_queue; + std::set<RTLIL::Cell*, RTLIL::sort_by_name_id<RTLIL::Cell>> new_queue; for (auto cell : queue) unused.erase(cell); for (auto cell : queue) { - for (auto &it : cell->connections) { + for (auto &it : cell->connections()) { if (!ct.cell_output(cell->type, it.first)) { std::set<RTLIL::Cell*> cell_list; RTLIL::SigSpec sig = it.second; @@ -89,10 +87,9 @@ static void rmunused_module_cells(RTLIL::Module *module, bool verbose) for (auto cell : unused) { if (verbose) log(" removing unused `%s' cell `%s'.\n", cell->type.c_str(), cell->name.c_str()); - OPT_DID_SOMETHING = true; - module->cells.erase(cell->name); + module->design->scratchpad_set_bool("opt.did_something", true); + module->remove(cell); count_rm_cells++; - delete cell; } } @@ -104,15 +101,10 @@ static int count_nontrivial_wire_attrs(RTLIL::Wire *w) return count; } -static bool compare_signals(RTLIL::SigSpec &s1, RTLIL::SigSpec &s2, SigPool ®s, SigPool &conns, std::set<RTLIL::Wire*> &direct_wires) +static bool compare_signals(RTLIL::SigBit &s1, RTLIL::SigBit &s2, SigPool ®s, SigPool &conns, std::set<RTLIL::Wire*> &direct_wires) { - assert(s1.width == 1); - assert(s2.width == 1); - assert(s1.chunks.size() == 1); - assert(s2.chunks.size() == 1); - - RTLIL::Wire *w1 = s1.chunks[0].wire; - RTLIL::Wire *w2 = s2.chunks[0].wire; + RTLIL::Wire *w1 = s1.wire; + RTLIL::Wire *w2 = s2.wire; if (w1 == NULL || w2 == NULL) return w2 == NULL; @@ -146,11 +138,12 @@ static bool compare_signals(RTLIL::SigSpec &s1, RTLIL::SigSpec &s2, SigPool ® static bool check_public_name(RTLIL::IdString id) { - if (id[0] == '$') + const std::string &id_str = id.str(); + if (id_str[0] == '$') return false; - if (id.substr(0, 2) == "\\_" && (id[id.size()-1] == '_' || id.find("_[") != std::string::npos)) + if (id_str.substr(0, 2) == "\\_" && (id_str[id_str.size()-1] == '_' || id_str.find("_[") != std::string::npos)) return false; - if (id.find(".$") != std::string::npos) + if (id_str.find(".$") != std::string::npos) return false; return true; } @@ -161,54 +154,54 @@ static void rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool SigPool connected_signals; if (!purge_mode) - for (auto &it : module->cells) { + for (auto &it : module->cells_) { RTLIL::Cell *cell = it.second; if (ct_reg.cell_known(cell->type)) - for (auto &it2 : cell->connections) + for (auto &it2 : cell->connections()) if (ct_reg.cell_output(cell->type, it2.first)) register_signals.add(it2.second); - for (auto &it2 : cell->connections) + for (auto &it2 : cell->connections()) connected_signals.add(it2.second); } SigMap assign_map(module); std::set<RTLIL::SigSpec> direct_sigs; std::set<RTLIL::Wire*> direct_wires; - for (auto &it : module->cells) { + for (auto &it : module->cells_) { RTLIL::Cell *cell = it.second; if (ct_all.cell_known(cell->type)) - for (auto &it2 : cell->connections) + for (auto &it2 : cell->connections()) if (ct_all.cell_output(cell->type, it2.first)) direct_sigs.insert(assign_map(it2.second)); } - for (auto &it : module->wires) { + for (auto &it : module->wires_) { if (direct_sigs.count(assign_map(it.second)) || it.second->port_input) direct_wires.insert(it.second); } - for (auto &it : module->wires) { + for (auto &it : module->wires_) { RTLIL::Wire *wire = it.second; for (int i = 0; i < wire->width; i++) { - RTLIL::SigSpec s1 = RTLIL::SigSpec(wire, 1, i), s2 = assign_map(s1); + RTLIL::SigBit s1 = RTLIL::SigBit(wire, i), s2 = assign_map(s1); if (!compare_signals(s1, s2, register_signals, connected_signals, direct_wires)) assign_map.add(s1); } } - module->connections.clear(); + module->connections_.clear(); SigPool used_signals; SigPool used_signals_nodrivers; - for (auto &it : module->cells) { + for (auto &it : module->cells_) { RTLIL::Cell *cell = it.second; - for (auto &it2 : cell->connections) { + for (auto &it2 : cell->connections_) { assign_map.apply(it2.second); used_signals.add(it2.second); if (!ct.cell_output(cell->type, it2.first)) used_signals_nodrivers.add(it2.second); } } - for (auto &it : module->wires) { + for (auto &it : module->wires_) { RTLIL::Wire *wire = it.second; if (wire->port_id > 0) { RTLIL::SigSpec sig = RTLIL::SigSpec(wire); @@ -224,47 +217,43 @@ static void rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool } } - std::vector<RTLIL::Wire*> del_wires; - for (auto &it : module->wires) { - RTLIL::Wire *wire = it.second; - if ((!purge_mode && check_public_name(wire->name)) || wire->port_id != 0) { + std::vector<RTLIL::Wire*> maybe_del_wires; + for (auto wire : module->wires()) + { + if ((!purge_mode && check_public_name(wire->name)) || wire->port_id != 0 || wire->get_bool_attribute("\\keep")) { RTLIL::SigSpec s1 = RTLIL::SigSpec(wire), s2 = s1; assign_map.apply(s2); - if (!used_signals.check_any(s2) && wire->port_id == 0) { - del_wires.push_back(wire); + if (!used_signals.check_any(s2) && wire->port_id == 0 && !wire->get_bool_attribute("\\keep")) { + maybe_del_wires.push_back(wire); } else { - s1.expand(); - s2.expand(); - assert(s1.chunks.size() == s2.chunks.size()); + log_assert(SIZE(s1) == SIZE(s2)); RTLIL::SigSig new_conn; - for (size_t i = 0; i < s1.chunks.size(); i++) - if (s1.chunks[i] != s2.chunks[i]) { - new_conn.first.append(s1.chunks[i]); - new_conn.second.append(s2.chunks[i]); + for (int i = 0; i < SIZE(s1); i++) + if (s1[i] != s2[i]) { + new_conn.first.append_bit(s1[i]); + new_conn.second.append_bit(s2[i]); } - if (new_conn.first.width > 0) { - new_conn.first.optimize(); - new_conn.second.optimize(); + if (new_conn.first.size() > 0) { used_signals.add(new_conn.first); used_signals.add(new_conn.second); - module->connections.push_back(new_conn); + module->connect(new_conn); } } } else { if (!used_signals.check_any(RTLIL::SigSpec(wire))) - del_wires.push_back(wire); + maybe_del_wires.push_back(wire); } + RTLIL::SigSpec sig = assign_map(RTLIL::SigSpec(wire)); if (!used_signals_nodrivers.check_any(sig)) { std::string unused_bits; - sig.expand(); - for (size_t i = 0; i < sig.chunks.size(); i++) { - if (sig.chunks[i].wire == NULL) + for (int i = 0; i < SIZE(sig); i++) { + if (sig[i].wire == NULL) continue; - if (!used_signals_nodrivers.check_any(sig)) { + if (!used_signals_nodrivers.check(sig[i])) { if (!unused_bits.empty()) unused_bits += " "; - unused_bits += stringf("%zd", i); + unused_bits += stringf("%d", i); } } if (unused_bits.empty() || wire->port_id != 0) @@ -276,18 +265,22 @@ static void rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool } } + + std::set<RTLIL::Wire*> del_wires; + int del_wires_count = 0; - for (auto wire : del_wires) + for (auto wire : maybe_del_wires) if (!used_signals.check_any(RTLIL::SigSpec(wire))) { if (check_public_name(wire->name) && verbose) { log(" removing unused non-port wire %s.\n", wire->name.c_str()); del_wires_count++; } - module->wires.erase(wire->name); - count_rm_wires++; - delete wire; + del_wires.insert(wire); } + module->remove(del_wires); + count_rm_wires += del_wires.size();; + if (del_wires_count > 0) log(" removed %d unused temporary wires.\n", del_wires_count); } @@ -345,7 +338,7 @@ struct OptCleanPass : public Pass { ct_reg.setup_internals_mem(); ct_reg.setup_stdcells_mem(); - for (auto &mod_it : design->modules) { + for (auto &mod_it : design->modules_) { if (!design->selected_whole_module(mod_it.first)) { if (design->selected(mod_it.second)) log("Skipping module %s as it is only partially selected.\n", id2cstr(mod_it.second->name)); @@ -374,10 +367,10 @@ struct CleanPass : public Pass { log("\n"); log("This is identical to 'opt_clean', but less verbose.\n"); log("\n"); - log("When commands are seperated using the ';;' token, this command will be executed\n"); + log("When commands are separated using the ';;' token, this command will be executed\n"); log("between the commands.\n"); log("\n"); - log("When commands are seperated using the ';;;' token, this command will be executed\n"); + log("When commands are separated using the ';;;' token, this command will be executed\n"); log("in -purge mode between the commands.\n"); log("\n"); } @@ -409,12 +402,12 @@ struct CleanPass : public Pass { count_rm_cells = 0; count_rm_wires = 0; - for (auto &mod_it : design->modules) { + for (auto &mod_it : design->modules_) { if (design->selected_whole_module(mod_it.first) && mod_it.second->processes.size() == 0) do { - OPT_DID_SOMETHING = false; + design->scratchpad_unset("opt.did_something"); rmunused_module(mod_it.second, purge_mode, false); - } while (OPT_DID_SOMETHING); + } while (design->scratchpad_get_bool("opt.did_something")); } if (count_rm_cells > 0 || count_rm_wires > 0) diff --git a/passes/opt/opt_const.cc b/passes/opt/opt_const.cc index 34d1a69c1..f9b78c053 100644 --- a/passes/opt/opt_const.cc +++ b/passes/opt/opt_const.cc @@ -17,19 +17,18 @@ * */ -#include "opt_status.h" #include "kernel/register.h" #include "kernel/sigtools.h" #include "kernel/celltypes.h" +#include "kernel/utils.h" #include "kernel/log.h" #include <stdlib.h> -#include <assert.h> #include <stdio.h> -#include <set> +#include <algorithm> static bool did_something; -void replace_undriven(RTLIL::Design *design, RTLIL::Module *module) +static void replace_undriven(RTLIL::Design *design, RTLIL::Module *module) { CellTypes ct(design); SigMap sigmap(module); @@ -37,95 +36,350 @@ void replace_undriven(RTLIL::Design *design, RTLIL::Module *module) SigPool used_signals; SigPool all_signals; - for (auto &it : module->cells) - for (auto &conn : it.second->connections) { - if (!ct.cell_known(it.second->type) || ct.cell_output(it.second->type, conn.first)) + for (auto cell : module->cells()) + for (auto &conn : cell->connections()) { + if (!ct.cell_known(cell->type) || ct.cell_output(cell->type, conn.first)) driven_signals.add(sigmap(conn.second)); - if (!ct.cell_known(it.second->type) || ct.cell_input(it.second->type, conn.first)) + if (!ct.cell_known(cell->type) || ct.cell_input(cell->type, conn.first)) used_signals.add(sigmap(conn.second)); } - for (auto &it : module->wires) { - if (it.second->port_input) - driven_signals.add(sigmap(it.second)); - if (it.second->port_output) - used_signals.add(sigmap(it.second)); - all_signals.add(sigmap(it.second)); + for (auto wire : module->wires()) { + if (wire->port_input) + driven_signals.add(sigmap(wire)); + if (wire->port_output) + used_signals.add(sigmap(wire)); + all_signals.add(sigmap(wire)); } all_signals.del(driven_signals); RTLIL::SigSpec undriven_signals = all_signals.export_all(); - for (auto &c : undriven_signals.chunks) + for (auto &c : undriven_signals.chunks()) { RTLIL::SigSpec sig = c; if (c.wire->name[0] == '$') sig = used_signals.extract(sig); - if (sig.width == 0) + if (sig.size() == 0) continue; log("Setting undriven signal in %s to undef: %s\n", RTLIL::id2cstr(module->name), log_signal(c)); - module->connections.push_back(RTLIL::SigSig(c, RTLIL::SigSpec(RTLIL::State::Sx, c.width))); - OPT_DID_SOMETHING = true; + module->connect(RTLIL::SigSig(c, RTLIL::SigSpec(RTLIL::State::Sx, c.width))); + did_something = true; } } -void replace_cell(RTLIL::Module *module, RTLIL::Cell *cell, std::string info, std::string out_port, RTLIL::SigSpec out_val) +static void replace_cell(SigMap &assign_map, RTLIL::Module *module, RTLIL::Cell *cell, std::string info, std::string out_port, RTLIL::SigSpec out_val) { - RTLIL::SigSpec Y = cell->connections[out_port]; + RTLIL::SigSpec Y = cell->getPort(out_port); + out_val.extend_u0(Y.size(), false); + log("Replacing %s cell `%s' (%s) in module `%s' with constant driver `%s = %s'.\n", cell->type.c_str(), cell->name.c_str(), info.c_str(), module->name.c_str(), log_signal(Y), log_signal(out_val)); - // ILANG_BACKEND::dump_cell(stderr, "--> ", cell); - module->connections.push_back(RTLIL::SigSig(Y, out_val)); - module->cells.erase(cell->name); - delete cell; - OPT_DID_SOMETHING = true; + // log_cell(cell); + assign_map.add(Y, out_val); + module->connect(Y, out_val); + module->remove(cell); did_something = true; } -void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool consume_x, bool mux_undef, bool mux_bool) +static bool group_cell_inputs(RTLIL::Module *module, RTLIL::Cell *cell, bool commutative, SigMap &sigmap) +{ + std::string b_name = cell->hasPort("\\B") ? "\\B" : "\\A"; + + bool a_signed = cell->parameters.at("\\A_SIGNED").as_bool(); + bool b_signed = cell->parameters.at(b_name + "_SIGNED").as_bool(); + + RTLIL::SigSpec sig_a = sigmap(cell->getPort("\\A")); + RTLIL::SigSpec sig_b = sigmap(cell->getPort(b_name)); + RTLIL::SigSpec sig_y = sigmap(cell->getPort("\\Y")); + + sig_a.extend_u0(sig_y.size(), a_signed); + sig_b.extend_u0(sig_y.size(), b_signed); + + std::vector<RTLIL::SigBit> bits_a = sig_a, bits_b = sig_b, bits_y = sig_y; + + enum { GRP_DYN, GRP_CONST_A, GRP_CONST_B, GRP_CONST_AB, GRP_N }; + std::map<std::pair<RTLIL::SigBit, RTLIL::SigBit>, std::set<RTLIL::SigBit>> grouped_bits[GRP_N]; + + for (int i = 0; i < SIZE(bits_y); i++) + { + int group_idx = GRP_DYN; + RTLIL::SigBit bit_a = bits_a[i], bit_b = bits_b[i]; + + if (cell->type == "$or" && (bit_a == RTLIL::State::S1 || bit_b == RTLIL::State::S1)) + bit_a = bit_b = RTLIL::State::S1; + + if (cell->type == "$and" && (bit_a == RTLIL::State::S0 || bit_b == RTLIL::State::S0)) + bit_a = bit_b = RTLIL::State::S0; + + if (bit_a.wire == NULL && bit_b.wire == NULL) + group_idx = GRP_CONST_AB; + else if (bit_a.wire == NULL) + group_idx = GRP_CONST_A; + else if (bit_b.wire == NULL && commutative) + group_idx = GRP_CONST_A, std::swap(bit_a, bit_b); + else if (bit_b.wire == NULL) + group_idx = GRP_CONST_B; + + grouped_bits[group_idx][std::pair<RTLIL::SigBit, RTLIL::SigBit>(bit_a, bit_b)].insert(bits_y[i]); + } + + for (int i = 0; i < GRP_N; i++) + if (SIZE(grouped_bits[i]) == SIZE(bits_y)) + return false; + + log("Replacing %s cell `%s' in module `%s' with cells using grouped bits:\n", + log_id(cell->type), log_id(cell), log_id(module)); + + for (int i = 0; i < GRP_N; i++) + { + if (grouped_bits[i].empty()) + continue; + + RTLIL::Wire *new_y = module->addWire(NEW_ID, SIZE(grouped_bits[i])); + RTLIL::SigSpec new_a, new_b; + RTLIL::SigSig new_conn; + + for (auto &it : grouped_bits[i]) { + for (auto &bit : it.second) { + new_conn.first.append_bit(bit); + new_conn.second.append_bit(RTLIL::SigBit(new_y, new_a.size())); + } + new_a.append_bit(it.first.first); + new_b.append_bit(it.first.second); + } + + RTLIL::Cell *c = module->addCell(NEW_ID, cell->type); + + c->setPort("\\A", new_a); + c->parameters["\\A_WIDTH"] = new_a.size(); + c->parameters["\\A_SIGNED"] = false; + + if (b_name == "\\B") { + c->setPort("\\B", new_b); + c->parameters["\\B_WIDTH"] = new_b.size(); + c->parameters["\\B_SIGNED"] = false; + } + + c->setPort("\\Y", new_y); + c->parameters["\\Y_WIDTH"] = new_y->width; + c->check(); + + module->connect(new_conn); + + log(" New cell `%s': A=%s", log_id(c), log_signal(new_a)); + if (b_name == "\\B") + log(", B=%s", log_signal(new_b)); + log("\n"); + } + + cover_list("opt.opt_const.fine.group", "$not", "$pos", "$and", "$or", "$xor", "$xnor", cell->type.str()); + + module->remove(cell); + did_something = true; + return true; +} + +static void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool consume_x, bool mux_undef, bool mux_bool, bool do_fine, bool keepdc) { if (!design->selected(module)) return; + CellTypes ct_combinational; + ct_combinational.setup_internals(); + ct_combinational.setup_stdcells(); + SigMap assign_map(module); std::map<RTLIL::SigSpec, RTLIL::SigSpec> invert_map; - std::vector<RTLIL::Cell*> cells; - cells.reserve(module->cells.size()); - for (auto &cell_it : module->cells) - if (design->selected(module, cell_it.second)) { - if ((cell_it.second->type == "$_INV_" || cell_it.second->type == "$not" || cell_it.second->type == "$logic_not") && - cell_it.second->connections["\\A"].width == 1 && cell_it.second->connections["\\Y"].width == 1) - invert_map[assign_map(cell_it.second->connections["\\Y"])] = assign_map(cell_it.second->connections["\\A"]); - cells.push_back(cell_it.second); + TopoSort<RTLIL::Cell*> cells; + std::map<RTLIL::Cell*, std::set<RTLIL::SigBit>> cell_to_inbit; + std::map<RTLIL::SigBit, std::set<RTLIL::Cell*>> outbit_to_cell; + + for (auto cell : module->cells()) + if (design->selected(module, cell) && cell->type[0] == '$') { + if ((cell->type == "$_NOT_" || cell->type == "$not" || cell->type == "$logic_not") && + cell->getPort("\\A").size() == 1 && cell->getPort("\\Y").size() == 1) + invert_map[assign_map(cell->getPort("\\Y"))] = assign_map(cell->getPort("\\A")); + if (ct_combinational.cell_known(cell->type)) + for (auto &conn : cell->connections()) { + RTLIL::SigSpec sig = assign_map(conn.second); + sig.remove_const(); + if (ct_combinational.cell_input(cell->type, conn.first)) + cell_to_inbit[cell].insert(sig.begin(), sig.end()); + if (ct_combinational.cell_output(cell->type, conn.first)) + for (auto &bit : sig) + outbit_to_cell[bit].insert(cell); + } + cells.node(cell); } - for (auto cell : cells) + for (auto &it_right : cell_to_inbit) + for (auto &it_sigbit : it_right.second) + for (auto &it_left : outbit_to_cell[it_sigbit]) + cells.edge(it_left, it_right.first); + + cells.sort(); + + for (auto cell : cells.sorted) { -#define ACTION_DO(_p_, _s_) do { replace_cell(module, cell, input.as_string(), _p_, _s_); goto next_cell; } while (0) +#define ACTION_DO(_p_, _s_) do { cover("opt.opt_const.action_" S__LINE__); replace_cell(assign_map, module, cell, input.as_string(), _p_, _s_); goto next_cell; } while (0) #define ACTION_DO_Y(_v_) ACTION_DO("\\Y", RTLIL::SigSpec(RTLIL::State::S ## _v_)) - if ((cell->type == "$_INV_" || cell->type == "$not" || cell->type == "$logic_not") && - invert_map.count(assign_map(cell->connections["\\A"])) != 0) { - replace_cell(module, cell, "double_invert", "\\Y", invert_map.at(assign_map(cell->connections["\\A"]))); + if (do_fine) + { + if (cell->type == "$not" || cell->type == "$pos" || + cell->type == "$and" || cell->type == "$or" || cell->type == "$xor" || cell->type == "$xnor") + if (group_cell_inputs(module, cell, true, assign_map)) + goto next_cell; + + if (cell->type == "$reduce_and") + { + RTLIL::SigSpec sig_a = assign_map(cell->getPort("\\A")); + + RTLIL::State new_a = RTLIL::State::S1; + for (auto &bit : sig_a.to_sigbit_vector()) + if (bit == RTLIL::State::Sx) { + if (new_a == RTLIL::State::S1) + new_a = RTLIL::State::Sx; + } else if (bit == RTLIL::State::S0) { + new_a = RTLIL::State::S0; + break; + } else if (bit.wire != NULL) { + new_a = RTLIL::State::Sm; + } + + if (new_a != RTLIL::State::Sm && RTLIL::SigSpec(new_a) != sig_a) { + cover("opt.opt_const.fine.$reduce_and"); + log("Replacing port A of %s cell `%s' in module `%s' with constant driver: %s -> %s\n", + cell->type.c_str(), cell->name.c_str(), module->name.c_str(), log_signal(sig_a), log_signal(new_a)); + cell->setPort("\\A", sig_a = new_a); + cell->parameters.at("\\A_WIDTH") = 1; + did_something = true; + } + } + + if (cell->type == "$logic_not" || cell->type == "$logic_and" || cell->type == "$logic_or" || cell->type == "$reduce_or" || cell->type == "$reduce_bool") + { + RTLIL::SigSpec sig_a = assign_map(cell->getPort("\\A")); + + RTLIL::State new_a = RTLIL::State::S0; + for (auto &bit : sig_a.to_sigbit_vector()) + if (bit == RTLIL::State::Sx) { + if (new_a == RTLIL::State::S0) + new_a = RTLIL::State::Sx; + } else if (bit == RTLIL::State::S1) { + new_a = RTLIL::State::S1; + break; + } else if (bit.wire != NULL) { + new_a = RTLIL::State::Sm; + } + + if (new_a != RTLIL::State::Sm && RTLIL::SigSpec(new_a) != sig_a) { + cover_list("opt.opt_const.fine.A", "$logic_not", "$logic_and", "$logic_or", "$reduce_or", "$reduce_bool", cell->type.str()); + log("Replacing port A of %s cell `%s' in module `%s' with constant driver: %s -> %s\n", + cell->type.c_str(), cell->name.c_str(), module->name.c_str(), log_signal(sig_a), log_signal(new_a)); + cell->setPort("\\A", sig_a = new_a); + cell->parameters.at("\\A_WIDTH") = 1; + did_something = true; + } + } + + if (cell->type == "$logic_and" || cell->type == "$logic_or") + { + RTLIL::SigSpec sig_b = assign_map(cell->getPort("\\B")); + + RTLIL::State new_b = RTLIL::State::S0; + for (auto &bit : sig_b.to_sigbit_vector()) + if (bit == RTLIL::State::Sx) { + if (new_b == RTLIL::State::S0) + new_b = RTLIL::State::Sx; + } else if (bit == RTLIL::State::S1) { + new_b = RTLIL::State::S1; + break; + } else if (bit.wire != NULL) { + new_b = RTLIL::State::Sm; + } + + if (new_b != RTLIL::State::Sm && RTLIL::SigSpec(new_b) != sig_b) { + cover_list("opt.opt_const.fine.B", "$logic_and", "$logic_or", cell->type.str()); + log("Replacing port B of %s cell `%s' in module `%s' with constant driver: %s -> %s\n", + cell->type.c_str(), cell->name.c_str(), module->name.c_str(), log_signal(sig_b), log_signal(new_b)); + cell->setPort("\\B", sig_b = new_b); + cell->parameters.at("\\B_WIDTH") = 1; + did_something = true; + } + } + } + + if (cell->type == "$logic_or" && (assign_map(cell->getPort("\\A")) == RTLIL::State::S1 || assign_map(cell->getPort("\\B")) == RTLIL::State::S1)) { + cover("opt.opt_const.one_high"); + replace_cell(assign_map, module, cell, "one high", "\\Y", RTLIL::State::S1); goto next_cell; } - if ((cell->type == "$_MUX_" || cell->type == "$mux") && invert_map.count(assign_map(cell->connections["\\S"])) != 0) { - RTLIL::SigSpec tmp = cell->connections["\\A"]; - cell->connections["\\A"] = cell->connections["\\B"]; - cell->connections["\\B"] = tmp; - cell->connections["\\S"] = invert_map.at(assign_map(cell->connections["\\S"])); - OPT_DID_SOMETHING = true; + if (cell->type == "$logic_and" && (assign_map(cell->getPort("\\A")) == RTLIL::State::S0 || assign_map(cell->getPort("\\B")) == RTLIL::State::S0)) { + cover("opt.opt_const.one_low"); + replace_cell(assign_map, module, cell, "one low", "\\Y", RTLIL::State::S0); + goto next_cell; + } + + if (cell->type == "$reduce_xor" || cell->type == "$reduce_xnor" || cell->type == "$shift" || cell->type == "$shiftx" || + cell->type == "$shl" || cell->type == "$shr" || cell->type == "$sshl" || cell->type == "$sshr" || + cell->type == "$lt" || cell->type == "$le" || cell->type == "$ge" || cell->type == "$gt" || + cell->type == "$neg" || cell->type == "$add" || cell->type == "$sub" || + cell->type == "$mul" || cell->type == "$div" || cell->type == "$mod" || cell->type == "$pow") + { + RTLIL::SigSpec sig_a = assign_map(cell->getPort("\\A")); + RTLIL::SigSpec sig_b = cell->hasPort("\\B") ? assign_map(cell->getPort("\\B")) : RTLIL::SigSpec(); + + if (cell->type == "$shl" || cell->type == "$shr" || cell->type == "$sshl" || cell->type == "$sshr" || cell->type == "$shift" || cell->type == "$shiftx") + sig_a = RTLIL::SigSpec(); + + for (auto &bit : sig_a.to_sigbit_vector()) + if (bit == RTLIL::State::Sx) + goto found_the_x_bit; + + for (auto &bit : sig_b.to_sigbit_vector()) + if (bit == RTLIL::State::Sx) + goto found_the_x_bit; + + if (0) { + found_the_x_bit: + cover_list("opt.opt_const.xbit", "$reduce_xor", "$reduce_xnor", "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx", + "$lt", "$le", "$ge", "$gt", "$neg", "$add", "$sub", "$mul", "$div", "$mod", "$pow", cell->type.str()); + if (cell->type == "$reduce_xor" || cell->type == "$reduce_xnor" || + cell->type == "$lt" || cell->type == "$le" || cell->type == "$ge" || cell->type == "$gt") + replace_cell(assign_map, module, cell, "x-bit in input", "\\Y", RTLIL::State::Sx); + else + replace_cell(assign_map, module, cell, "x-bit in input", "\\Y", RTLIL::SigSpec(RTLIL::State::Sx, cell->getPort("\\Y").size())); + goto next_cell; + } + } + + if ((cell->type == "$_NOT_" || cell->type == "$not" || cell->type == "$logic_not") && cell->getPort("\\Y").size() == 1 && + invert_map.count(assign_map(cell->getPort("\\A"))) != 0) { + cover_list("opt.opt_const.invert.double", "$_NOT_", "$not", "$logic_not", cell->type.str()); + replace_cell(assign_map, module, cell, "double_invert", "\\Y", invert_map.at(assign_map(cell->getPort("\\A")))); + goto next_cell; + } + + if ((cell->type == "$_MUX_" || cell->type == "$mux") && invert_map.count(assign_map(cell->getPort("\\S"))) != 0) { + cover_list("opt.opt_const.invert.muxsel", "$_MUX_", "$mux", cell->type.str()); + log("Optimizing away select inverter for %s cell `%s' in module `%s'.\n", log_id(cell->type), log_id(cell), log_id(module)); + RTLIL::SigSpec tmp = cell->getPort("\\A"); + cell->setPort("\\A", cell->getPort("\\B")); + cell->setPort("\\B", tmp); + cell->setPort("\\S", invert_map.at(assign_map(cell->getPort("\\S")))); did_something = true; goto next_cell; } - if (cell->type == "$_INV_") { - RTLIL::SigSpec input = cell->connections["\\A"]; + if (cell->type == "$_NOT_") { + RTLIL::SigSpec input = cell->getPort("\\A"); assign_map.apply(input); if (input.match("1")) ACTION_DO_Y(0); if (input.match("0")) ACTION_DO_Y(1); @@ -134,8 +388,8 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons if (cell->type == "$_AND_") { RTLIL::SigSpec input; - input.append(cell->connections["\\B"]); - input.append(cell->connections["\\A"]); + input.append(cell->getPort("\\B")); + input.append(cell->getPort("\\A")); assign_map.apply(input); if (input.match(" 0")) ACTION_DO_Y(0); if (input.match("0 ")) ACTION_DO_Y(0); @@ -153,8 +407,8 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons if (cell->type == "$_OR_") { RTLIL::SigSpec input; - input.append(cell->connections["\\B"]); - input.append(cell->connections["\\A"]); + input.append(cell->getPort("\\B")); + input.append(cell->getPort("\\A")); assign_map.apply(input); if (input.match(" 1")) ACTION_DO_Y(1); if (input.match("1 ")) ACTION_DO_Y(1); @@ -172,8 +426,8 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons if (cell->type == "$_XOR_") { RTLIL::SigSpec input; - input.append(cell->connections["\\B"]); - input.append(cell->connections["\\A"]); + input.append(cell->getPort("\\B")); + input.append(cell->getPort("\\A")); assign_map.apply(input); if (input.match("00")) ACTION_DO_Y(0); if (input.match("01")) ACTION_DO_Y(1); @@ -187,9 +441,9 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons if (cell->type == "$_MUX_") { RTLIL::SigSpec input; - input.append(cell->connections["\\S"]); - input.append(cell->connections["\\B"]); - input.append(cell->connections["\\A"]); + input.append(cell->getPort("\\S")); + input.append(cell->getPort("\\B")); + input.append(cell->getPort("\\A")); assign_map.apply(input); if (input.extract(2, 1) == input.extract(1, 1)) ACTION_DO("\\Y", input.extract(2, 1)); @@ -197,10 +451,11 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons if (input.match(" 1")) ACTION_DO("\\Y", input.extract(1, 1)); if (input.match("01 ")) ACTION_DO("\\Y", input.extract(0, 1)); if (input.match("10 ")) { - cell->type = "$_INV_"; - cell->connections["\\A"] = input.extract(0, 1); - cell->connections.erase("\\B"); - cell->connections.erase("\\S"); + cover("opt.opt_const.mux_to_inv"); + cell->type = "$_NOT_"; + cell->setPort("\\A", input.extract(0, 1)); + cell->unsetPort("\\B"); + cell->unsetPort("\\S"); goto next_cell; } if (input.match("11 ")) ACTION_DO_Y(1); @@ -217,8 +472,8 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons if (cell->type == "$eq" || cell->type == "$ne" || cell->type == "$eqx" || cell->type == "$nex") { - RTLIL::SigSpec a = cell->connections["\\A"]; - RTLIL::SigSpec b = cell->connections["\\B"]; + RTLIL::SigSpec a = cell->getPort("\\A"); + RTLIL::SigSpec b = cell->getPort("\\B"); if (cell->parameters["\\A_WIDTH"].as_int() != cell->parameters["\\B_WIDTH"].as_int()) { int width = std::max(cell->parameters["\\A_WIDTH"].as_int(), cell->parameters["\\B_WIDTH"].as_int()); @@ -227,78 +482,189 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons } RTLIL::SigSpec new_a, new_b; - a.expand(), b.expand(); - assert(a.chunks.size() == b.chunks.size()); - for (size_t i = 0; i < a.chunks.size(); i++) { - if (a.chunks[i].wire == NULL && b.chunks[i].wire == NULL && a.chunks[i].data.bits[0] != b.chunks[i].data.bits[0] && - a.chunks[i].data.bits[0] <= RTLIL::State::S1 && b.chunks[i].data.bits[0] <= RTLIL::State::S1) { + log_assert(SIZE(a) == SIZE(b)); + for (int i = 0; i < SIZE(a); i++) { + if (a[i].wire == NULL && b[i].wire == NULL && a[i] != b[i] && a[i].data <= RTLIL::State::S1 && b[i].data <= RTLIL::State::S1) { + cover_list("opt.opt_const.eqneq.isneq", "$eq", "$ne", "$eqx", "$nex", cell->type.str()); RTLIL::SigSpec new_y = RTLIL::SigSpec((cell->type == "$eq" || cell->type == "$eqx") ? RTLIL::State::S0 : RTLIL::State::S1); new_y.extend(cell->parameters["\\Y_WIDTH"].as_int(), false); - replace_cell(module, cell, "empty", "\\Y", new_y); + replace_cell(assign_map, module, cell, "isneq", "\\Y", new_y); goto next_cell; } - if (a.chunks[i] == b.chunks[i]) + if (a[i] == b[i]) continue; - new_a.append(a.chunks[i]); - new_b.append(b.chunks[i]); + new_a.append(a[i]); + new_b.append(b[i]); } - if (new_a.width == 0) { + if (new_a.size() == 0) { + cover_list("opt.opt_const.eqneq.empty", "$eq", "$ne", "$eqx", "$nex", cell->type.str()); RTLIL::SigSpec new_y = RTLIL::SigSpec((cell->type == "$eq" || cell->type == "$eqx") ? RTLIL::State::S1 : RTLIL::State::S0); new_y.extend(cell->parameters["\\Y_WIDTH"].as_int(), false); - replace_cell(module, cell, "empty", "\\Y", new_y); + replace_cell(assign_map, module, cell, "empty", "\\Y", new_y); goto next_cell; } - if (new_a.width < a.width || new_b.width < b.width) { - new_a.optimize(); - new_b.optimize(); - cell->connections["\\A"] = new_a; - cell->connections["\\B"] = new_b; - cell->parameters["\\A_WIDTH"] = new_a.width; - cell->parameters["\\B_WIDTH"] = new_b.width; + if (new_a.size() < a.size() || new_b.size() < b.size()) { + cover_list("opt.opt_const.eqneq.resize", "$eq", "$ne", "$eqx", "$nex", cell->type.str()); + cell->setPort("\\A", new_a); + cell->setPort("\\B", new_b); + cell->parameters["\\A_WIDTH"] = new_a.size(); + cell->parameters["\\B_WIDTH"] = new_b.size(); } } if ((cell->type == "$eq" || cell->type == "$ne") && cell->parameters["\\Y_WIDTH"].as_int() == 1 && cell->parameters["\\A_WIDTH"].as_int() == 1 && cell->parameters["\\B_WIDTH"].as_int() == 1) { - RTLIL::SigSpec a = assign_map(cell->connections["\\A"]); - RTLIL::SigSpec b = assign_map(cell->connections["\\B"]); + RTLIL::SigSpec a = assign_map(cell->getPort("\\A")); + RTLIL::SigSpec b = assign_map(cell->getPort("\\B")); if (a.is_fully_const()) { - RTLIL::SigSpec tmp; - tmp = a, a = b, b = tmp; - cell->connections["\\A"] = a; - cell->connections["\\B"] = b; + cover_list("opt.opt_const.eqneq.swapconst", "$eq", "$ne", cell->type.str()); + RTLIL::SigSpec tmp = cell->getPort("\\A"); + cell->setPort("\\A", cell->getPort("\\B")); + cell->setPort("\\B", tmp); } if (b.is_fully_const()) { if (b.as_bool() == (cell->type == "$eq")) { RTLIL::SigSpec input = b; - ACTION_DO("\\Y", cell->connections["\\A"]); + ACTION_DO("\\Y", cell->getPort("\\A")); } else { + cover_list("opt.opt_const.eqneq.isnot", "$eq", "$ne", cell->type.str()); + log("Replacing %s cell `%s' in module `%s' with inverter.\n", log_id(cell->type), log_id(cell), log_id(module)); cell->type = "$not"; cell->parameters.erase("\\B_WIDTH"); cell->parameters.erase("\\B_SIGNED"); - cell->connections.erase("\\B"); + cell->unsetPort("\\B"); + did_something = true; + } + goto next_cell; + } + } + + if (cell->type.in("$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx") && assign_map(cell->getPort("\\B")).is_fully_const()) + { + bool sign_ext = cell->type == "$sshr" && cell->getParam("\\A_SIGNED").as_bool(); + int shift_bits = assign_map(cell->getPort("\\B")).as_int(cell->type.in("$shift", "$shiftx") && cell->getParam("\\B_SIGNED").as_bool()); + + if (cell->type.in("$shl", "$sshl")) + shift_bits *= -1; + + RTLIL::SigSpec sig_a = assign_map(cell->getPort("\\A")); + RTLIL::SigSpec sig_y(cell->type == "$shiftx" ? RTLIL::State::Sx : RTLIL::State::S0, cell->getParam("\\Y_WIDTH").as_int()); + + if (SIZE(sig_a) < SIZE(sig_y)) + sig_a.extend(SIZE(sig_y), cell->getParam("\\A_SIGNED").as_bool()); + + for (int i = 0; i < SIZE(sig_y); i++) { + int idx = i + shift_bits; + if (0 <= idx && idx < SIZE(sig_a)) + sig_y[i] = sig_a[idx]; + else if (SIZE(sig_a) <= idx && sign_ext) + sig_y[i] = sig_a[SIZE(sig_a)-1]; + } + + cover_list("opt.opt_const.constshift", "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx", cell->type.str()); + + log("Replacing %s cell `%s' (B=%s, SHR=%d) in module `%s' with fixed wiring: %s\n", + log_id(cell->type), log_id(cell), log_signal(assign_map(cell->getPort("\\B"))), shift_bits, log_id(module), log_signal(sig_y)); + + module->connect(cell->getPort("\\Y"), sig_y); + module->remove(cell); + + did_something = true; + goto next_cell; + } + + if (!keepdc) + { + bool identity_wrt_a = false; + bool identity_wrt_b = false; + + if (cell->type == "$add" || cell->type == "$sub" || cell->type == "$or" || cell->type == "$xor") + { + RTLIL::SigSpec a = assign_map(cell->getPort("\\A")); + RTLIL::SigSpec b = assign_map(cell->getPort("\\B")); + + if (cell->type != "$sub" && a.is_fully_const() && a.as_bool() == false) + identity_wrt_b = true; + + if (b.is_fully_const() && b.as_bool() == false) + identity_wrt_a = true; + } + + if (cell->type == "$shl" || cell->type == "$shr" || cell->type == "$sshl" || cell->type == "$sshr" || cell->type == "$shift" || cell->type == "$shiftx") + { + RTLIL::SigSpec b = assign_map(cell->getPort("\\B")); + + if (b.is_fully_const() && b.as_bool() == false) + identity_wrt_a = true; + } + + if (cell->type == "$mul") + { + RTLIL::SigSpec a = assign_map(cell->getPort("\\A")); + RTLIL::SigSpec b = assign_map(cell->getPort("\\B")); + + if (a.is_fully_const() && a.size() <= 32 && a.as_int() == 1) + identity_wrt_b = true; + + if (b.is_fully_const() && b.size() <= 32 && b.as_int() == 1) + identity_wrt_a = true; + } + + if (cell->type == "$div") + { + RTLIL::SigSpec b = assign_map(cell->getPort("\\B")); + + if (b.is_fully_const() && b.size() <= 32 && b.as_int() == 1) + identity_wrt_a = true; + } + + if (identity_wrt_a || identity_wrt_b) + { + if (identity_wrt_a) + cover_list("opt.opt_const.identwrt.a", "$add", "$sub", "$or", "$xor", "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx", "$mul", "$div", cell->type.str()); + if (identity_wrt_b) + cover_list("opt.opt_const.identwrt.b", "$add", "$sub", "$or", "$xor", "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx", "$mul", "$div", cell->type.str()); + + log("Replacing %s cell `%s' in module `%s' with identity for port %c.\n", + cell->type.c_str(), cell->name.c_str(), module->name.c_str(), identity_wrt_a ? 'A' : 'B'); + + if (!identity_wrt_a) { + cell->setPort("\\A", cell->getPort("\\B")); + cell->parameters.at("\\A_WIDTH") = cell->parameters.at("\\B_WIDTH"); + cell->parameters.at("\\A_SIGNED") = cell->parameters.at("\\B_SIGNED"); } + + cell->type = "$pos"; + cell->unsetPort("\\B"); + cell->parameters.erase("\\B_WIDTH"); + cell->parameters.erase("\\B_SIGNED"); + cell->check(); + + did_something = true; goto next_cell; } } if (mux_bool && (cell->type == "$mux" || cell->type == "$_MUX_") && - cell->connections["\\A"] == RTLIL::SigSpec(0, 1) && cell->connections["\\B"] == RTLIL::SigSpec(1, 1)) { - replace_cell(module, cell, "mux_bool", "\\Y", cell->connections["\\S"]); + cell->getPort("\\A") == RTLIL::SigSpec(0, 1) && cell->getPort("\\B") == RTLIL::SigSpec(1, 1)) { + cover_list("opt.opt_const.mux_bool", "$mux", "$_MUX_", cell->type.str()); + replace_cell(assign_map, module, cell, "mux_bool", "\\Y", cell->getPort("\\S")); goto next_cell; } if (mux_bool && (cell->type == "$mux" || cell->type == "$_MUX_") && - cell->connections["\\A"] == RTLIL::SigSpec(1, 1) && cell->connections["\\B"] == RTLIL::SigSpec(0, 1)) { - cell->connections["\\A"] = cell->connections["\\S"]; - cell->connections.erase("\\B"); - cell->connections.erase("\\S"); + cell->getPort("\\A") == RTLIL::SigSpec(1, 1) && cell->getPort("\\B") == RTLIL::SigSpec(0, 1)) { + cover_list("opt.opt_const.mux_invert", "$mux", "$_MUX_", cell->type.str()); + log("Replacing %s cell `%s' in module `%s' with inverter.\n", log_id(cell->type), log_id(cell), log_id(module)); + cell->setPort("\\A", cell->getPort("\\S")); + cell->unsetPort("\\B"); + cell->unsetPort("\\S"); if (cell->type == "$mux") { cell->parameters["\\A_WIDTH"] = cell->parameters["\\WIDTH"]; cell->parameters["\\Y_WIDTH"] = cell->parameters["\\WIDTH"]; @@ -306,15 +672,16 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons cell->parameters.erase("\\WIDTH"); cell->type = "$not"; } else - cell->type = "$_INV_"; - OPT_DID_SOMETHING = true; + cell->type = "$_NOT_"; did_something = true; goto next_cell; } - if (consume_x && mux_bool && (cell->type == "$mux" || cell->type == "$_MUX_") && cell->connections["\\A"] == RTLIL::SigSpec(0, 1)) { - cell->connections["\\A"] = cell->connections["\\S"]; - cell->connections.erase("\\S"); + if (consume_x && mux_bool && (cell->type == "$mux" || cell->type == "$_MUX_") && cell->getPort("\\A") == RTLIL::SigSpec(0, 1)) { + cover_list("opt.opt_const.mux_and", "$mux", "$_MUX_", cell->type.str()); + log("Replacing %s cell `%s' in module `%s' with and-gate.\n", log_id(cell->type), log_id(cell), log_id(module)); + cell->setPort("\\A", cell->getPort("\\S")); + cell->unsetPort("\\S"); if (cell->type == "$mux") { cell->parameters["\\A_WIDTH"] = cell->parameters["\\WIDTH"]; cell->parameters["\\B_WIDTH"] = cell->parameters["\\WIDTH"]; @@ -325,14 +692,15 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons cell->type = "$and"; } else cell->type = "$_AND_"; - OPT_DID_SOMETHING = true; did_something = true; goto next_cell; } - if (consume_x && mux_bool && (cell->type == "$mux" || cell->type == "$_MUX_") && cell->connections["\\B"] == RTLIL::SigSpec(1, 1)) { - cell->connections["\\B"] = cell->connections["\\S"]; - cell->connections.erase("\\S"); + if (consume_x && mux_bool && (cell->type == "$mux" || cell->type == "$_MUX_") && cell->getPort("\\B") == RTLIL::SigSpec(1, 1)) { + cover_list("opt.opt_const.mux_or", "$mux", "$_MUX_", cell->type.str()); + log("Replacing %s cell `%s' in module `%s' with or-gate.\n", log_id(cell->type), log_id(cell), log_id(module)); + cell->setPort("\\B", cell->getPort("\\S")); + cell->unsetPort("\\S"); if (cell->type == "$mux") { cell->parameters["\\A_WIDTH"] = cell->parameters["\\WIDTH"]; cell->parameters["\\B_WIDTH"] = cell->parameters["\\WIDTH"]; @@ -342,87 +710,88 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons cell->parameters.erase("\\WIDTH"); cell->type = "$or"; } else - cell->type = "$_or_"; - OPT_DID_SOMETHING = true; + cell->type = "$_OR_"; did_something = true; goto next_cell; } if (mux_undef && (cell->type == "$mux" || cell->type == "$pmux")) { RTLIL::SigSpec new_a, new_b, new_s; - int width = cell->connections.at("\\A").width; - if ((cell->connections.at("\\A").is_fully_undef() && cell->connections.at("\\B").is_fully_undef()) || - cell->connections.at("\\S").is_fully_undef()) { - replace_cell(module, cell, "mux undef", "\\Y", cell->connections.at("\\A")); + int width = cell->getPort("\\A").size(); + if ((cell->getPort("\\A").is_fully_undef() && cell->getPort("\\B").is_fully_undef()) || + cell->getPort("\\S").is_fully_undef()) { + cover_list("opt.opt_const.mux_undef", "$mux", "$pmux", cell->type.str()); + replace_cell(assign_map, module, cell, "mux_undef", "\\Y", cell->getPort("\\A")); goto next_cell; } - for (int i = 0; i < cell->connections.at("\\S").width; i++) { - RTLIL::SigSpec old_b = cell->connections.at("\\B").extract(i*width, width); - RTLIL::SigSpec old_s = cell->connections.at("\\S").extract(i, 1); + for (int i = 0; i < cell->getPort("\\S").size(); i++) { + RTLIL::SigSpec old_b = cell->getPort("\\B").extract(i*width, width); + RTLIL::SigSpec old_s = cell->getPort("\\S").extract(i, 1); if (old_b.is_fully_undef() || old_s.is_fully_undef()) continue; new_b.append(old_b); new_s.append(old_s); } - new_a = cell->connections.at("\\A"); - if (new_a.is_fully_undef() && new_s.width > 0) { - new_a = new_b.extract((new_s.width-1)*width, width); - new_b = new_b.extract(0, (new_s.width-1)*width); - new_s = new_s.extract(0, new_s.width-1); + new_a = cell->getPort("\\A"); + if (new_a.is_fully_undef() && new_s.size() > 0) { + new_a = new_b.extract((new_s.size()-1)*width, width); + new_b = new_b.extract(0, (new_s.size()-1)*width); + new_s = new_s.extract(0, new_s.size()-1); } - if (new_s.width == 0) { - replace_cell(module, cell, "mux undef", "\\Y", new_a); + if (new_s.size() == 0) { + cover_list("opt.opt_const.mux_empty", "$mux", "$pmux", cell->type.str()); + replace_cell(assign_map, module, cell, "mux_empty", "\\Y", new_a); goto next_cell; } if (new_a == RTLIL::SigSpec(RTLIL::State::S0) && new_b == RTLIL::SigSpec(RTLIL::State::S1)) { - replace_cell(module, cell, "mux undef", "\\Y", new_s); + cover_list("opt.opt_const.mux_sel01", "$mux", "$pmux", cell->type.str()); + replace_cell(assign_map, module, cell, "mux_sel01", "\\Y", new_s); goto next_cell; } - if (cell->connections.at("\\S").width != new_s.width) { - cell->connections.at("\\A") = new_a; - cell->connections.at("\\B") = new_b; - cell->connections.at("\\S") = new_s; - if (new_s.width > 1) { + if (cell->getPort("\\S").size() != new_s.size()) { + cover_list("opt.opt_const.mux_reduce", "$mux", "$pmux", cell->type.str()); + log("Optimized away %d select inputs of %s cell `%s' in module `%s'.\n", + SIZE(cell->getPort("\\S")) - SIZE(new_s), log_id(cell->type), log_id(cell), log_id(module)); + cell->setPort("\\A", new_a); + cell->setPort("\\B", new_b); + cell->setPort("\\S", new_s); + if (new_s.size() > 1) { cell->type = "$pmux"; - cell->parameters["\\S_WIDTH"] = new_s.width; + cell->parameters["\\S_WIDTH"] = new_s.size(); } else { cell->type = "$mux"; cell->parameters.erase("\\S_WIDTH"); } - OPT_DID_SOMETHING = true; did_something = true; } } #define FOLD_1ARG_CELL(_t) \ if (cell->type == "$" #_t) { \ - RTLIL::SigSpec a = cell->connections["\\A"]; \ + RTLIL::SigSpec a = cell->getPort("\\A"); \ assign_map.apply(a); \ if (a.is_fully_const()) { \ - a.optimize(); \ - if (a.chunks.empty()) a.chunks.push_back(RTLIL::SigChunk()); \ RTLIL::Const dummy_arg(RTLIL::State::S0, 1); \ - RTLIL::SigSpec y(RTLIL::const_ ## _t(a.chunks[0].data, dummy_arg, \ + RTLIL::SigSpec y(RTLIL::const_ ## _t(a.as_const(), dummy_arg, \ cell->parameters["\\A_SIGNED"].as_bool(), false, \ cell->parameters["\\Y_WIDTH"].as_int())); \ - replace_cell(module, cell, stringf("%s", log_signal(a)), "\\Y", y); \ + cover("opt.opt_const.const.$" #_t); \ + replace_cell(assign_map, module, cell, stringf("%s", log_signal(a)), "\\Y", y); \ goto next_cell; \ } \ } #define FOLD_2ARG_CELL(_t) \ if (cell->type == "$" #_t) { \ - RTLIL::SigSpec a = cell->connections["\\A"]; \ - RTLIL::SigSpec b = cell->connections["\\B"]; \ + RTLIL::SigSpec a = cell->getPort("\\A"); \ + RTLIL::SigSpec b = cell->getPort("\\B"); \ assign_map.apply(a), assign_map.apply(b); \ if (a.is_fully_const() && b.is_fully_const()) { \ - a.optimize(), b.optimize(); \ - if (a.chunks.empty()) a.chunks.push_back(RTLIL::SigChunk()); \ - if (b.chunks.empty()) b.chunks.push_back(RTLIL::SigChunk()); \ - RTLIL::SigSpec y(RTLIL::const_ ## _t(a.chunks[0].data, b.chunks[0].data, \ + RTLIL::SigSpec y(RTLIL::const_ ## _t(a.as_const(), b.as_const(), \ cell->parameters["\\A_SIGNED"].as_bool(), \ cell->parameters["\\B_SIGNED"].as_bool(), \ cell->parameters["\\Y_WIDTH"].as_int())); \ - replace_cell(module, cell, stringf("%s, %s", log_signal(a), log_signal(b)), "\\Y", y); \ + cover("opt.opt_const.const.$" #_t); \ + replace_cell(assign_map, module, cell, stringf("%s, %s", log_signal(a), log_signal(b)), "\\Y", y); \ goto next_cell; \ } \ } @@ -447,6 +816,8 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons FOLD_2ARG_CELL(shr) FOLD_2ARG_CELL(sshl) FOLD_2ARG_CELL(sshr) + FOLD_2ARG_CELL(shift) + FOLD_2ARG_CELL(shiftx) FOLD_2ARG_CELL(lt) FOLD_2ARG_CELL(le) @@ -467,13 +838,78 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons // be very conservative with optimizing $mux cells as we do not want to break mux trees if (cell->type == "$mux") { - RTLIL::SigSpec input = assign_map(cell->connections["\\S"]); - RTLIL::SigSpec inA = assign_map(cell->connections["\\A"]); - RTLIL::SigSpec inB = assign_map(cell->connections["\\B"]); + RTLIL::SigSpec input = assign_map(cell->getPort("\\S")); + RTLIL::SigSpec inA = assign_map(cell->getPort("\\A")); + RTLIL::SigSpec inB = assign_map(cell->getPort("\\B")); if (input.is_fully_const()) - ACTION_DO("\\Y", input.as_bool() ? cell->connections["\\B"] : cell->connections["\\A"]); + ACTION_DO("\\Y", input.as_bool() ? cell->getPort("\\B") : cell->getPort("\\A")); else if (inA == inB) - ACTION_DO("\\Y", cell->connections["\\A"]); + ACTION_DO("\\Y", cell->getPort("\\A")); + } + + if (!keepdc && cell->type == "$mul") + { + bool a_signed = cell->parameters["\\A_SIGNED"].as_bool(); + bool b_signed = cell->parameters["\\B_SIGNED"].as_bool(); + bool swapped_ab = false; + + RTLIL::SigSpec sig_a = assign_map(cell->getPort("\\A")); + RTLIL::SigSpec sig_b = assign_map(cell->getPort("\\B")); + RTLIL::SigSpec sig_y = assign_map(cell->getPort("\\Y")); + + if (sig_b.is_fully_const() && sig_b.size() <= 32) + std::swap(sig_a, sig_b), std::swap(a_signed, b_signed), swapped_ab = true; + + if (sig_a.is_fully_def() && sig_a.size() <= 32) + { + int a_val = sig_a.as_int(); + + if (a_val == 0) + { + cover("opt.opt_const.mul_shift.zero"); + + log("Replacing multiply-by-zero cell `%s' in module `%s' with zero-driver.\n", + cell->name.c_str(), module->name.c_str()); + + module->connect(RTLIL::SigSig(sig_y, RTLIL::SigSpec(0, sig_y.size()))); + module->remove(cell); + + did_something = true; + goto next_cell; + } + + for (int i = 1; i < (a_signed ? sig_a.size()-1 : sig_a.size()); i++) + if (a_val == (1 << i)) + { + if (swapped_ab) + cover("opt.opt_const.mul_shift.swapped"); + else + cover("opt.opt_const.mul_shift.unswapped"); + + log("Replacing multiply-by-%d cell `%s' in module `%s' with shift-by-%d.\n", + a_val, cell->name.c_str(), module->name.c_str(), i); + + if (!swapped_ab) { + cell->setPort("\\A", cell->getPort("\\B")); + cell->parameters["\\A_WIDTH"] = cell->parameters["\\B_WIDTH"]; + cell->parameters["\\A_SIGNED"] = cell->parameters["\\B_SIGNED"]; + } + + std::vector<RTLIL::SigBit> new_b = RTLIL::SigSpec(i, 6); + + while (SIZE(new_b) > 1 && new_b.back() == RTLIL::State::S0) + new_b.pop_back(); + + cell->type = "$shl"; + cell->parameters["\\B_WIDTH"] = SIZE(new_b); + cell->parameters["\\B_SIGNED"] = false; + cell->setPort("\\B", new_b); + cell->check(); + + did_something = true; + goto next_cell; + } + } } next_cell:; @@ -503,12 +939,23 @@ struct OptConstPass : public Pass { log(" -undriven\n"); log(" replace undriven nets with undef (x) constants\n"); log("\n"); + log(" -keepdc\n"); + log(" some optimizations change the behavior of the circuit with respect to\n"); + log(" don't-care bits. for example in 'a+0' a single x-bit in 'a' will cause\n"); + log(" all result bits to be set to x. this behavior changes when 'a+0' is\n"); + log(" replaced by 'a'. the -keepdc option disables all such optimizations.\n"); + log("\n"); + log(" -fine\n"); + log(" perform fine-grain optimizations\n"); + log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { bool mux_undef = false; bool mux_bool = false; bool undriven = false; + bool do_fine = false; + bool keepdc = false; log_header("Executing OPT_CONST pass (perform const folding).\n"); log_push(); @@ -527,21 +974,31 @@ struct OptConstPass : public Pass { undriven = true; continue; } + if (args[argidx] == "-fine") { + do_fine = true; + continue; + } + if (args[argidx] == "-keepdc") { + keepdc = true; + continue; + } break; } extra_args(args, argidx, design); - for (auto &mod_it : design->modules) + for (auto module : design->modules()) { if (undriven) - replace_undriven(design, mod_it.second); + replace_undriven(design, module); do { do { did_something = false; - replace_const_cells(design, mod_it.second, false, mux_undef, mux_bool); + replace_const_cells(design, module, false, mux_undef, mux_bool, do_fine, keepdc); + if (did_something) + design->scratchpad_set_bool("opt.did_something", true); } while (did_something); - replace_const_cells(design, mod_it.second, true, mux_undef, mux_bool); + replace_const_cells(design, module, true, mux_undef, mux_bool, do_fine, keepdc); } while (did_something); } diff --git a/passes/opt/opt_muxtree.cc b/passes/opt/opt_muxtree.cc index 47100869c..2c5dcf668 100644 --- a/passes/opt/opt_muxtree.cc +++ b/passes/opt/opt_muxtree.cc @@ -17,13 +17,11 @@ * */ -#include "opt_status.h" #include "kernel/register.h" #include "kernel/sigtools.h" #include "kernel/log.h" #include "kernel/celltypes.h" #include <stdlib.h> -#include <assert.h> #include <stdio.h> #include <set> @@ -36,7 +34,11 @@ struct OptMuxtreeWorker SigMap assign_map; int removed_count; - typedef std::pair<RTLIL::Wire*,int> bitDef_t; + struct bitDef_t : public std::pair<RTLIL::Wire*, int> { + bitDef_t() : std::pair<RTLIL::Wire*, int>(NULL, 0) { } + bitDef_t(const RTLIL::SigBit &bit) : std::pair<RTLIL::Wire*, int>(bit.wire, bit.offset) { } + }; + struct bitinfo_t { int num; @@ -79,21 +81,20 @@ struct OptMuxtreeWorker // .ctrl_sigs // .input_sigs // .const_activated - for (auto &cell_it : module->cells) + for (auto cell : module->cells()) { - RTLIL::Cell *cell = cell_it.second; - if (cell->type == "$mux" || cell->type == "$pmux" || cell->type == "$safe_pmux") + if (cell->type == "$mux" || cell->type == "$pmux") { - RTLIL::SigSpec sig_a = cell->connections["\\A"]; - RTLIL::SigSpec sig_b = cell->connections["\\B"]; - RTLIL::SigSpec sig_s = cell->connections["\\S"]; - RTLIL::SigSpec sig_y = cell->connections["\\Y"]; + RTLIL::SigSpec sig_a = cell->getPort("\\A"); + RTLIL::SigSpec sig_b = cell->getPort("\\B"); + RTLIL::SigSpec sig_s = cell->getPort("\\S"); + RTLIL::SigSpec sig_y = cell->getPort("\\Y"); muxinfo_t muxinfo; muxinfo.cell = cell; - for (int i = 0; i < sig_s.width; i++) { - RTLIL::SigSpec sig = sig_b.extract(i*sig_a.width, sig_a.width); + for (int i = 0; i < sig_s.size(); i++) { + RTLIL::SigSpec sig = sig_b.extract(i*sig_a.size(), sig_a.size()); RTLIL::SigSpec ctrl_sig = assign_map(sig_s.extract(i, 1)); portinfo_t portinfo; for (int idx : sig2bits(sig)) { @@ -126,15 +127,15 @@ struct OptMuxtreeWorker } else { - for (auto &it : cell->connections) { + for (auto &it : cell->connections()) { for (int idx : sig2bits(it.second)) bit2info[idx].seen_non_mux = true; } } } - for (auto &it : module->wires) { - if (it.second->port_output) - for (int idx : sig2bits(RTLIL::SigSpec(it.second))) + for (auto wire : module->wires()) { + if (wire->port_output) + for (int idx : sig2bits(RTLIL::SigSpec(wire))) bit2info[idx].seen_non_mux = true; } @@ -177,7 +178,6 @@ struct OptMuxtreeWorker } else { log(" dead port %zd/%zd on %s %s.\n", port_idx+1, mi.ports.size(), mi.cell->type.c_str(), mi.cell->name.c_str()); - OPT_DID_SOMETHING = true; removed_count++; } } @@ -186,32 +186,30 @@ struct OptMuxtreeWorker continue; if (live_ports.size() == 0) { - module->cells.erase(mi.cell->name); - delete mi.cell; + module->remove(mi.cell); continue; } - RTLIL::SigSpec sig_a = mi.cell->connections["\\A"]; - RTLIL::SigSpec sig_b = mi.cell->connections["\\B"]; - RTLIL::SigSpec sig_s = mi.cell->connections["\\S"]; - RTLIL::SigSpec sig_y = mi.cell->connections["\\Y"]; + RTLIL::SigSpec sig_a = mi.cell->getPort("\\A"); + RTLIL::SigSpec sig_b = mi.cell->getPort("\\B"); + RTLIL::SigSpec sig_s = mi.cell->getPort("\\S"); + RTLIL::SigSpec sig_y = mi.cell->getPort("\\Y"); RTLIL::SigSpec sig_ports = sig_b; sig_ports.append(sig_a); if (live_ports.size() == 1) { - RTLIL::SigSpec sig_in = sig_ports.extract(live_ports[0]*sig_a.width, sig_a.width); - module->connections.push_back(RTLIL::SigSig(sig_y, sig_in)); - module->cells.erase(mi.cell->name); - delete mi.cell; + RTLIL::SigSpec sig_in = sig_ports.extract(live_ports[0]*sig_a.size(), sig_a.size()); + module->connect(RTLIL::SigSig(sig_y, sig_in)); + module->remove(mi.cell); } else { RTLIL::SigSpec new_sig_a, new_sig_b, new_sig_s; for (size_t i = 0; i < live_ports.size(); i++) { - RTLIL::SigSpec sig_in = sig_ports.extract(live_ports[i]*sig_a.width, sig_a.width); + RTLIL::SigSpec sig_in = sig_ports.extract(live_ports[i]*sig_a.size(), sig_a.size()); if (i == live_ports.size()-1) { new_sig_a = sig_in; } else { @@ -220,14 +218,14 @@ struct OptMuxtreeWorker } } - mi.cell->connections["\\A"] = new_sig_a; - mi.cell->connections["\\B"] = new_sig_b; - mi.cell->connections["\\S"] = new_sig_s; - if (new_sig_s.width == 1) { + mi.cell->setPort("\\A", new_sig_a); + mi.cell->setPort("\\B", new_sig_b); + mi.cell->setPort("\\S", new_sig_s); + if (new_sig_s.size() == 1) { mi.cell->type = "$mux"; mi.cell->parameters.erase("\\S_WIDTH"); } else { - mi.cell->parameters["\\S_WIDTH"] = RTLIL::Const(new_sig_s.width); + mi.cell->parameters["\\S_WIDTH"] = RTLIL::Const(new_sig_s.size()); } } } @@ -259,10 +257,8 @@ struct OptMuxtreeWorker { std::vector<int> results; assign_map.apply(sig); - sig.expand(); - for (auto &c : sig.chunks) - if (c.wire != NULL) { - bitDef_t bit(c.wire, c.offset); + for (auto &bit : sig) + if (bit.wire != NULL) { if (bit2num.count(bit) == 0) { bitinfo_t info; info.num = bit2info.size(); @@ -309,13 +305,17 @@ struct OptMuxtreeWorker if (port_idx < int(muxinfo.ports.size())-1 && !muxinfo.ports[port_idx].const_activated) knowledge.known_active.push_back(muxinfo.ports[port_idx].ctrl_sigs); + std::vector<int> parent_muxes; for (int m : muxinfo.ports[port_idx].input_muxes) { if (knowledge.visited_muxes.count(m) > 0) continue; knowledge.visited_muxes.insert(m); + parent_muxes.push_back(m); + } + for (int m : parent_muxes) eval_mux(knowledge, m); + for (int m : parent_muxes) knowledge.visited_muxes.erase(m); - } if (port_idx < int(muxinfo.ports.size())-1 && !muxinfo.ports[port_idx].const_activated) knowledge.known_active.pop_back(); @@ -393,6 +393,7 @@ struct OptMuxtreeWorker void eval_root_mux(int mux_idx) { knowledge_t knowledge; + knowledge.visited_muxes.insert(mux_idx); eval_mux(knowledge, mux_idx); } }; @@ -418,19 +419,21 @@ struct OptMuxtreePass : public Pass { extra_args(args, 1, design); int total_count = 0; - for (auto &mod_it : design->modules) { - if (!design->selected_whole_module(mod_it.first)) { - if (design->selected(mod_it.second)) - log("Skipping module %s as it is only partially selected.\n", id2cstr(mod_it.second->name)); + for (auto mod : design->modules()) { + if (!design->selected_whole_module(mod)) { + if (design->selected(mod)) + log("Skipping module %s as it is only partially selected.\n", log_id(mod)); continue; } - if (mod_it.second->processes.size() > 0) { - log("Skipping module %s as it contains processes.\n", id2cstr(mod_it.second->name)); + if (mod->processes.size() > 0) { + log("Skipping module %s as it contains processes.\n", log_id(mod)); } else { - OptMuxtreeWorker worker(design, mod_it.second); + OptMuxtreeWorker worker(design, mod); total_count += worker.removed_count; } } + if (total_count) + design->scratchpad_set_bool("opt.did_something", true); log("Removed %d multiplexer ports.\n", total_count); } } OptMuxtreePass; diff --git a/passes/opt/opt_reduce.cc b/passes/opt/opt_reduce.cc index dd1299810..e9e2bb399 100644 --- a/passes/opt/opt_reduce.cc +++ b/passes/opt/opt_reduce.cc @@ -17,14 +17,11 @@ * */ -#include "opt_status.h" #include "kernel/register.h" #include "kernel/sigtools.h" #include "kernel/log.h" #include "kernel/celltypes.h" -#include "libs/sha1/sha1.h" #include <stdlib.h> -#include <assert.h> #include <stdio.h> #include <set> @@ -43,97 +40,95 @@ struct OptReduceWorker return; cells.erase(cell); - RTLIL::SigSpec sig_a = assign_map(cell->connections["\\A"]); - sig_a.sort_and_unify(); - sig_a.expand(); + RTLIL::SigSpec sig_a = assign_map(cell->getPort("\\A")); + std::set<RTLIL::SigBit> new_sig_a_bits; - RTLIL::SigSpec new_sig_a; - for (auto &chunk : sig_a.chunks) + for (auto &bit : sig_a.to_sigbit_set()) { - if (chunk.wire == NULL && chunk.data.bits[0] == RTLIL::State::S0) { + if (bit == RTLIL::State::S0) { if (cell->type == "$reduce_and") { - new_sig_a = RTLIL::SigSpec(RTLIL::State::S0); + new_sig_a_bits.clear(); + new_sig_a_bits.insert(RTLIL::State::S0); break; } continue; } - if (chunk.wire == NULL && chunk.data.bits[0] == RTLIL::State::S1) { + if (bit == RTLIL::State::S1) { if (cell->type == "$reduce_or") { - new_sig_a = RTLIL::SigSpec(RTLIL::State::S1); + new_sig_a_bits.clear(); + new_sig_a_bits.insert(RTLIL::State::S1); break; } continue; } - if (chunk.wire == NULL) { - new_sig_a = RTLIL::SigSpec(RTLIL::State::Sx); - break; + if (bit.wire == NULL) { + new_sig_a_bits.insert(bit); + continue; } bool imported_children = false; - for (auto child_cell : drivers.find(chunk)) { + for (auto child_cell : drivers.find(bit)) { if (child_cell->type == cell->type) { opt_reduce(cells, drivers, child_cell); - new_sig_a.append(child_cell->connections["\\A"]); + if (child_cell->getPort("\\Y")[0] == bit) { + std::set<RTLIL::SigBit> child_sig_a_bits = assign_map(child_cell->getPort("\\A")).to_sigbit_set(); + new_sig_a_bits.insert(child_sig_a_bits.begin(), child_sig_a_bits.end()); + } else + new_sig_a_bits.insert(RTLIL::State::S0); imported_children = true; } } if (!imported_children) - new_sig_a.append(chunk); + new_sig_a_bits.insert(bit); } - new_sig_a.sort_and_unify(); - if (new_sig_a != sig_a || sig_a.width != cell->connections["\\A"].width) { + RTLIL::SigSpec new_sig_a(new_sig_a_bits); + + if (new_sig_a != sig_a || sig_a.size() != cell->getPort("\\A").size()) { log(" New input vector for %s cell %s: %s\n", cell->type.c_str(), cell->name.c_str(), log_signal(new_sig_a)); did_something = true; - OPT_DID_SOMETHING = true; total_count++; } - cell->connections["\\A"] = new_sig_a; - cell->parameters["\\A_WIDTH"] = RTLIL::Const(new_sig_a.width); + cell->setPort("\\A", new_sig_a); + cell->parameters["\\A_WIDTH"] = RTLIL::Const(new_sig_a.size()); return; } void opt_mux(RTLIL::Cell *cell) { - RTLIL::SigSpec sig_a = assign_map(cell->connections["\\A"]); - RTLIL::SigSpec sig_b = assign_map(cell->connections["\\B"]); - RTLIL::SigSpec sig_s = assign_map(cell->connections["\\S"]); + RTLIL::SigSpec sig_a = assign_map(cell->getPort("\\A")); + RTLIL::SigSpec sig_b = assign_map(cell->getPort("\\B")); + RTLIL::SigSpec sig_s = assign_map(cell->getPort("\\S")); RTLIL::SigSpec new_sig_b, new_sig_s; std::set<RTLIL::SigSpec> handled_sig; handled_sig.insert(sig_a); - for (int i = 0; i < sig_s.width; i++) + for (int i = 0; i < sig_s.size(); i++) { - RTLIL::SigSpec this_b = sig_b.extract(i*sig_a.width, sig_a.width); + RTLIL::SigSpec this_b = sig_b.extract(i*sig_a.size(), sig_a.size()); if (handled_sig.count(this_b) > 0) continue; RTLIL::SigSpec this_s = sig_s.extract(i, 1); - for (int j = i+1; j < sig_s.width; j++) { - RTLIL::SigSpec that_b = sig_b.extract(j*sig_a.width, sig_a.width); + for (int j = i+1; j < sig_s.size(); j++) { + RTLIL::SigSpec that_b = sig_b.extract(j*sig_a.size(), sig_a.size()); if (this_b == that_b) this_s.append(sig_s.extract(j, 1)); } - if (this_s.width > 1) + if (this_s.size() > 1) { - RTLIL::Wire *reduce_or_wire = new RTLIL::Wire; - reduce_or_wire->name = NEW_ID; - module->wires[reduce_or_wire->name] = reduce_or_wire; - - RTLIL::Cell *reduce_or_cell = new RTLIL::Cell; - reduce_or_cell->name = NEW_ID; - reduce_or_cell->type = "$reduce_or"; - reduce_or_cell->connections["\\A"] = this_s; + RTLIL::Cell *reduce_or_cell = module->addCell(NEW_ID, "$reduce_or"); + reduce_or_cell->setPort("\\A", this_s); reduce_or_cell->parameters["\\A_SIGNED"] = RTLIL::Const(0); - reduce_or_cell->parameters["\\A_WIDTH"] = RTLIL::Const(this_s.width); + reduce_or_cell->parameters["\\A_WIDTH"] = RTLIL::Const(this_s.size()); reduce_or_cell->parameters["\\Y_WIDTH"] = RTLIL::Const(1); - module->cells[reduce_or_cell->name] = reduce_or_cell; + RTLIL::Wire *reduce_or_wire = module->addWire(NEW_ID); this_s = RTLIL::SigSpec(reduce_or_wire); - reduce_or_cell->connections["\\Y"] = this_s; + reduce_or_cell->setPort("\\Y", this_s); } new_sig_b.append(this_b); @@ -141,26 +136,24 @@ struct OptReduceWorker handled_sig.insert(this_b); } - if (new_sig_s.width != sig_s.width) { + if (new_sig_s.size() != sig_s.size()) { log(" New ctrl vector for %s cell %s: %s\n", cell->type.c_str(), cell->name.c_str(), log_signal(new_sig_s)); did_something = true; - OPT_DID_SOMETHING = true; total_count++; } - if (new_sig_s.width == 0) + if (new_sig_s.size() == 0) { - module->connections.push_back(RTLIL::SigSig(cell->connections["\\Y"], cell->connections["\\A"])); - assign_map.add(cell->connections["\\Y"], cell->connections["\\A"]); - module->cells.erase(cell->name); - delete cell; + module->connect(RTLIL::SigSig(cell->getPort("\\Y"), cell->getPort("\\A"))); + assign_map.add(cell->getPort("\\Y"), cell->getPort("\\A")); + module->remove(cell); } else { - cell->connections["\\B"] = new_sig_b; - cell->connections["\\S"] = new_sig_s; - if (new_sig_s.width > 1) { - cell->parameters["\\S_WIDTH"] = RTLIL::Const(new_sig_s.width); + cell->setPort("\\B", new_sig_b); + cell->setPort("\\S", new_sig_s); + if (new_sig_s.size() > 1) { + cell->parameters["\\S_WIDTH"] = RTLIL::Const(new_sig_s.size()); } else { cell->type = "$mux"; cell->parameters.erase("\\S_WIDTH"); @@ -168,7 +161,85 @@ struct OptReduceWorker } } - OptReduceWorker(RTLIL::Design *design, RTLIL::Module *module) : + void opt_mux_bits(RTLIL::Cell *cell) + { + std::vector<RTLIL::SigBit> sig_a = assign_map(cell->getPort("\\A")).to_sigbit_vector(); + std::vector<RTLIL::SigBit> sig_b = assign_map(cell->getPort("\\B")).to_sigbit_vector(); + std::vector<RTLIL::SigBit> sig_y = assign_map(cell->getPort("\\Y")).to_sigbit_vector(); + + std::vector<RTLIL::SigBit> new_sig_y; + RTLIL::SigSig old_sig_conn; + + std::vector<std::vector<RTLIL::SigBit>> consolidated_in_tuples; + std::map<std::vector<RTLIL::SigBit>, RTLIL::SigBit> consolidated_in_tuples_map; + + for (int i = 0; i < int(sig_y.size()); i++) + { + std::vector<RTLIL::SigBit> in_tuple; + bool all_tuple_bits_same = true; + + in_tuple.push_back(sig_a.at(i)); + for (int j = i; j < int(sig_b.size()); j += int(sig_a.size())) { + if (sig_b.at(j) != sig_a.at(i)) + all_tuple_bits_same = false; + in_tuple.push_back(sig_b.at(j)); + } + + if (all_tuple_bits_same) + { + old_sig_conn.first.append_bit(sig_y.at(i)); + old_sig_conn.second.append_bit(sig_a.at(i)); + } + else if (consolidated_in_tuples_map.count(in_tuple)) + { + old_sig_conn.first.append_bit(sig_y.at(i)); + old_sig_conn.second.append_bit(consolidated_in_tuples_map.at(in_tuple)); + } + else + { + consolidated_in_tuples_map[in_tuple] = sig_y.at(i); + consolidated_in_tuples.push_back(in_tuple); + new_sig_y.push_back(sig_y.at(i)); + } + } + + if (new_sig_y.size() != sig_y.size()) + { + log(" Consolidated identical input bits for %s cell %s:\n", cell->type.c_str(), cell->name.c_str()); + log(" Old ports: A=%s, B=%s, Y=%s\n", log_signal(cell->getPort("\\A")), + log_signal(cell->getPort("\\B")), log_signal(cell->getPort("\\Y"))); + + cell->setPort("\\A", RTLIL::SigSpec()); + for (auto &in_tuple : consolidated_in_tuples) { + RTLIL::SigSpec new_a = cell->getPort("\\A"); + new_a.append(in_tuple.at(0)); + cell->setPort("\\A", new_a); + } + + cell->setPort("\\B", RTLIL::SigSpec()); + for (int i = 1; i <= cell->getPort("\\S").size(); i++) + for (auto &in_tuple : consolidated_in_tuples) { + RTLIL::SigSpec new_b = cell->getPort("\\B"); + new_b.append(in_tuple.at(i)); + cell->setPort("\\B", new_b); + } + + cell->parameters["\\WIDTH"] = RTLIL::Const(new_sig_y.size()); + cell->setPort("\\Y", new_sig_y); + + log(" New ports: A=%s, B=%s, Y=%s\n", log_signal(cell->getPort("\\A")), + log_signal(cell->getPort("\\B")), log_signal(cell->getPort("\\Y"))); + log(" New connections: %s = %s\n", log_signal(old_sig_conn.first), log_signal(old_sig_conn.second)); + + module->connect(old_sig_conn); + module->check(); + + did_something = true; + total_count++; + } + } + + OptReduceWorker(RTLIL::Design *design, RTLIL::Module *module, bool do_fine) : design(design), module(module), assign_map(module) { log(" Optimizing cells in module %s.\n", module->name.c_str()); @@ -176,6 +247,35 @@ struct OptReduceWorker total_count = 0; did_something = true; + SigPool mem_wren_sigs; + for (auto &cell_it : module->cells_) { + RTLIL::Cell *cell = cell_it.second; + if (cell->type == "$mem") + mem_wren_sigs.add(assign_map(cell->getPort("\\WR_EN"))); + if (cell->type == "$memwr") + mem_wren_sigs.add(assign_map(cell->getPort("\\EN"))); + } + for (auto &cell_it : module->cells_) { + RTLIL::Cell *cell = cell_it.second; + if (cell->type == "$dff" && mem_wren_sigs.check_any(assign_map(cell->getPort("\\Q")))) + mem_wren_sigs.add(assign_map(cell->getPort("\\D"))); + } + + bool keep_expanding_mem_wren_sigs = true; + while (keep_expanding_mem_wren_sigs) { + keep_expanding_mem_wren_sigs = false; + for (auto &cell_it : module->cells_) { + RTLIL::Cell *cell = cell_it.second; + if (cell->type == "$mux" && mem_wren_sigs.check_any(assign_map(cell->getPort("\\Y")))) { + if (!mem_wren_sigs.check_all(assign_map(cell->getPort("\\A"))) || + !mem_wren_sigs.check_all(assign_map(cell->getPort("\\B")))) + keep_expanding_mem_wren_sigs = true; + mem_wren_sigs.add(assign_map(cell->getPort("\\A"))); + mem_wren_sigs.add(assign_map(cell->getPort("\\B"))); + } + } + } + while (did_something) { did_something = false; @@ -189,11 +289,11 @@ struct OptReduceWorker SigSet<RTLIL::Cell*> drivers; std::set<RTLIL::Cell*> cells; - for (auto &cell_it : module->cells) { + for (auto &cell_it : module->cells_) { RTLIL::Cell *cell = cell_it.second; if (cell->type != type || !design->selected(module, cell)) continue; - drivers.insert(assign_map(cell->connections["\\Y"]), cell); + drivers.insert(assign_map(cell->getPort("\\Y")), cell); cells.insert(cell); } @@ -205,11 +305,19 @@ struct OptReduceWorker // merge identical inputs on $mux and $pmux cells - for (auto &cell_it : module->cells) + std::vector<RTLIL::Cell*> cells; + + for (auto &it : module->cells_) + if ((it.second->type == "$mux" || it.second->type == "$pmux") && design->selected(module, it.second)) + cells.push_back(it.second); + + for (auto cell : cells) { - RTLIL::Cell *cell = cell_it.second; - if ((cell->type != "$mux" && cell->type != "$pmux" && cell->type != "$safe_pmux") || !design->selected(module, cell)) - continue; + // this optimization is to aggressive for most coarse-grain applications. + // but we always want it for multiplexers driving write enable ports. + if (do_fine || mem_wren_sigs.check_any(assign_map(cell->getPort("\\Y")))) + opt_mux_bits(cell); + opt_mux(cell); } } @@ -222,7 +330,7 @@ struct OptReducePass : public Pass { { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); - log(" opt_reduce [selection]\n"); + log(" opt_reduce [options] [selection]\n"); log("\n"); log("This pass performs two interlinked optimizations:\n"); log("\n"); @@ -232,20 +340,40 @@ struct OptReducePass : public Pass { log("2. it identifies duplicated inputs to MUXes and replaces them with a single\n"); log("input with the original control signals OR'ed together.\n"); log("\n"); + log(" -fine\n"); + log(" perform fine-grain optimizations\n"); + log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { + bool do_fine = false; + log_header("Executing OPT_REDUCE pass (consolidate $*mux and $reduce_* inputs).\n"); - extra_args(args, 1, design); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-fine") { + do_fine = true; + continue; + } + break; + } + extra_args(args, argidx, design); int total_count = 0; - for (auto &mod_it : design->modules) { + for (auto &mod_it : design->modules_) { if (!design->selected(mod_it.second)) continue; - OptReduceWorker worker(design, mod_it.second); - total_count += worker.total_count; + do { + OptReduceWorker worker(design, mod_it.second, do_fine); + total_count += worker.total_count; + if (worker.total_count == 0) + break; + } while (1); } + if (total_count) + design->scratchpad_set_bool("opt.did_something", true); log("Performed a total of %d changes.\n", total_count); } } OptReducePass; diff --git a/passes/opt/opt_rmdff.cc b/passes/opt/opt_rmdff.cc index 9a438537c..48f406f65 100644 --- a/passes/opt/opt_rmdff.cc +++ b/passes/opt/opt_rmdff.cc @@ -17,7 +17,6 @@ * */ -#include "opt_status.h" #include "kernel/register.h" #include "kernel/sigtools.h" #include "kernel/log.h" @@ -33,34 +32,34 @@ static bool handle_dff(RTLIL::Module *mod, RTLIL::Cell *dff) RTLIL::Const val_cp, val_rp, val_rv; if (dff->type == "$_DFF_N_" || dff->type == "$_DFF_P_") { - sig_d = dff->connections["\\D"]; - sig_q = dff->connections["\\Q"]; - sig_c = dff->connections["\\C"]; + sig_d = dff->getPort("\\D"); + sig_q = dff->getPort("\\Q"); + sig_c = dff->getPort("\\C"); val_cp = RTLIL::Const(dff->type == "$_DFF_P_", 1); } else if (dff->type.substr(0,6) == "$_DFF_" && dff->type.substr(9) == "_" && (dff->type[6] == 'N' || dff->type[6] == 'P') && (dff->type[7] == 'N' || dff->type[7] == 'P') && (dff->type[8] == '0' || dff->type[8] == '1')) { - sig_d = dff->connections["\\D"]; - sig_q = dff->connections["\\Q"]; - sig_c = dff->connections["\\C"]; - sig_r = dff->connections["\\R"]; + sig_d = dff->getPort("\\D"); + sig_q = dff->getPort("\\Q"); + sig_c = dff->getPort("\\C"); + sig_r = dff->getPort("\\R"); val_cp = RTLIL::Const(dff->type[6] == 'P', 1); val_rp = RTLIL::Const(dff->type[7] == 'P', 1); val_rv = RTLIL::Const(dff->type[8] == '1', 1); } else if (dff->type == "$dff") { - sig_d = dff->connections["\\D"]; - sig_q = dff->connections["\\Q"]; - sig_c = dff->connections["\\CLK"]; + sig_d = dff->getPort("\\D"); + sig_q = dff->getPort("\\Q"); + sig_c = dff->getPort("\\CLK"); val_cp = RTLIL::Const(dff->parameters["\\CLK_POLARITY"].as_bool(), 1); } else if (dff->type == "$adff") { - sig_d = dff->connections["\\D"]; - sig_q = dff->connections["\\Q"]; - sig_c = dff->connections["\\CLK"]; - sig_r = dff->connections["\\ARST"]; + sig_d = dff->getPort("\\D"); + sig_q = dff->getPort("\\Q"); + sig_c = dff->getPort("\\CLK"); + sig_r = dff->getPort("\\ARST"); val_cp = RTLIL::Const(dff->parameters["\\CLK_POLARITY"].as_bool(), 1); val_rp = RTLIL::Const(dff->parameters["\\ARST_POLARITY"].as_bool(), 1); val_rv = dff->parameters["\\ARST_VALUE"]; @@ -85,55 +84,55 @@ static bool handle_dff(RTLIL::Module *mod, RTLIL::Cell *dff) std::set<RTLIL::Cell*> muxes; mux_drivers.find(sig_d, muxes); for (auto mux : muxes) { - RTLIL::SigSpec sig_a = assign_map(mux->connections.at("\\A")); - RTLIL::SigSpec sig_b = assign_map(mux->connections.at("\\B")); + RTLIL::SigSpec sig_a = assign_map(mux->getPort("\\A")); + RTLIL::SigSpec sig_b = assign_map(mux->getPort("\\B")); if (sig_a == sig_q && sig_b.is_fully_const()) { RTLIL::SigSig conn(sig_q, sig_b); - mod->connections.push_back(conn); + mod->connect(conn); goto delete_dff; } if (sig_b == sig_q && sig_a.is_fully_const()) { RTLIL::SigSig conn(sig_q, sig_a); - mod->connections.push_back(conn); + mod->connect(conn); goto delete_dff; } } } - if (sig_c.is_fully_const() && (!sig_r.width || !has_init)) { + if (sig_c.is_fully_const() && (!sig_r.size() || !has_init)) { if (val_rv.bits.size() == 0) val_rv = val_init; RTLIL::SigSig conn(sig_q, val_rv); - mod->connections.push_back(conn); + mod->connect(conn); goto delete_dff; } - if (sig_d.is_fully_undef() && sig_r.width && !has_init) { + if (sig_d.is_fully_undef() && sig_r.size() && !has_init) { RTLIL::SigSig conn(sig_q, val_rv); - mod->connections.push_back(conn); + mod->connect(conn); goto delete_dff; } - if (sig_d.is_fully_undef() && !sig_r.width && has_init) { + if (sig_d.is_fully_undef() && !sig_r.size() && has_init) { RTLIL::SigSig conn(sig_q, val_init); - mod->connections.push_back(conn); + mod->connect(conn); goto delete_dff; } - if (sig_d.is_fully_const() && !sig_r.width && !has_init) { + if (sig_d.is_fully_const() && !sig_r.size() && !has_init) { RTLIL::SigSig conn(sig_q, sig_d); - mod->connections.push_back(conn); + mod->connect(conn); goto delete_dff; } - if (sig_d == sig_q && !(sig_r.width && has_init)) { - if (sig_r.width) { + if (sig_d == sig_q && !(sig_r.size() && has_init)) { + if (sig_r.size()) { RTLIL::SigSig conn(sig_q, val_rv); - mod->connections.push_back(conn); + mod->connect(conn); } if (has_init) { RTLIL::SigSig conn(sig_q, val_init); - mod->connections.push_back(conn); + mod->connect(conn); } goto delete_dff; } @@ -142,9 +141,7 @@ static bool handle_dff(RTLIL::Module *mod, RTLIL::Cell *dff) delete_dff: log("Removing %s (%s) from module %s.\n", dff->name.c_str(), dff->type.c_str(), mod->name.c_str()); - OPT_DID_SOMETHING = true; - mod->cells.erase(dff->name); - delete dff; + mod->remove(dff); return true; } @@ -167,23 +164,23 @@ struct OptRmdffPass : public Pass { extra_args(args, 1, design); - for (auto &mod_it : design->modules) + for (auto &mod_it : design->modules_) { if (!design->selected(mod_it.second)) continue; assign_map.set(mod_it.second); dff_init_map.set(mod_it.second); - for (auto &it : mod_it.second->wires) + for (auto &it : mod_it.second->wires_) if (it.second->attributes.count("\\init") != 0) dff_init_map.add(it.second, it.second->attributes.at("\\init")); mux_drivers.clear(); - std::vector<std::string> dff_list; - for (auto &it : mod_it.second->cells) { + std::vector<RTLIL::IdString> dff_list; + for (auto &it : mod_it.second->cells_) { if (it.second->type == "$mux" || it.second->type == "$pmux") { - if (it.second->connections.at("\\A").width == it.second->connections.at("\\B").width) - mux_drivers.insert(assign_map(it.second->connections.at("\\Y")), it.second); + if (it.second->getPort("\\A").size() == it.second->getPort("\\B").size()) + mux_drivers.insert(assign_map(it.second->getPort("\\Y")), it.second); continue; } if (!design->selected(mod_it.second, it.second)) @@ -203,14 +200,17 @@ struct OptRmdffPass : public Pass { } for (auto &id : dff_list) { - if (mod_it.second->cells.count(id) > 0 && - handle_dff(mod_it.second, mod_it.second->cells[id])) + if (mod_it.second->cells_.count(id) > 0 && + handle_dff(mod_it.second, mod_it.second->cells_[id])) total_count++; } } assign_map.clear(); mux_drivers.clear(); + + if (total_count) + design->scratchpad_set_bool("opt.did_something", true); log("Replaced %d DFF cells.\n", total_count); } } OptRmdffPass; diff --git a/passes/opt/opt_share.cc b/passes/opt/opt_share.cc index eb639d8ab..4b76a5a2d 100644 --- a/passes/opt/opt_share.cc +++ b/passes/opt/opt_share.cc @@ -17,14 +17,12 @@ * */ -#include "opt_status.h" #include "kernel/register.h" #include "kernel/sigtools.h" #include "kernel/log.h" #include "kernel/celltypes.h" #include "libs/sha1/sha1.h" #include <stdlib.h> -#include <assert.h> #include <stdio.h> #include <set> @@ -61,12 +59,12 @@ struct OptShareWorker if (cell_hash_cache.count(cell) > 0) return cell_hash_cache[cell]; - std::string hash_string = cell->type + "\n"; + std::string hash_string = cell->type.str() + "\n"; for (auto &it : cell->parameters) - hash_string += "P " + it.first + "=" + it.second.as_string() + "\n"; + hash_string += "P " + it.first.str() + "=" + it.second.as_string() + "\n"; - const std::map<RTLIL::IdString, RTLIL::SigSpec> *conn = &cell->connections; + const std::map<RTLIL::IdString, RTLIL::SigSpec> *conn = &cell->connections(); std::map<RTLIL::IdString, RTLIL::SigSpec> alt_conn; if (cell->type == "$and" || cell->type == "$or" || cell->type == "$xor" || cell->type == "$xnor" || cell->type == "$add" || cell->type == "$mul" || @@ -96,24 +94,19 @@ struct OptShareWorker continue; RTLIL::SigSpec sig = it.second; assign_map.apply(sig); - hash_string += "C " + it.first + "="; - for (auto &chunk : sig.chunks) { + hash_string += "C " + it.first.str() + "="; + for (auto &chunk : sig.chunks()) { if (chunk.wire) - hash_string += "{" + chunk.wire->name + " " + + hash_string += "{" + chunk.wire->name.str() + " " + int_to_hash_string(chunk.offset) + " " + int_to_hash_string(chunk.width) + "}"; else - hash_string += chunk.data.as_string(); + hash_string += RTLIL::Const(chunk.data).as_string(); } hash_string += "\n"; } - unsigned char hash[20]; - char hash_hex_string[41]; - sha1::calc(hash_string.c_str(), hash_string.size(), hash); - sha1::toHexString(hash, hash_hex_string); - cell_hash_cache[cell] = hash_hex_string; - + cell_hash_cache[cell] = sha1(hash_string); return cell_hash_cache[cell]; } #endif @@ -135,8 +128,8 @@ struct OptShareWorker return true; } - std::map<RTLIL::IdString, RTLIL::SigSpec> conn1 = cell1->connections; - std::map<RTLIL::IdString, RTLIL::SigSpec> conn2 = cell2->connections; + std::map<RTLIL::IdString, RTLIL::SigSpec> conn1 = cell1->connections(); + std::map<RTLIL::IdString, RTLIL::SigSpec> conn2 = cell2->connections(); for (auto &it : conn1) { if (ct.cell_output(cell1->type, it.first)) @@ -180,8 +173,8 @@ struct OptShareWorker } if (cell1->type.substr(0, 1) == "$" && conn1.count("\\Q") != 0) { - std::vector<RTLIL::SigBit> q1 = dff_init_map(cell1->connections.at("\\Q")).to_sigbit_vector(); - std::vector<RTLIL::SigBit> q2 = dff_init_map(cell2->connections.at("\\Q")).to_sigbit_vector(); + std::vector<RTLIL::SigBit> q1 = dff_init_map(cell1->getPort("\\Q")).to_sigbit_vector(); + std::vector<RTLIL::SigBit> q2 = dff_init_map(cell2->getPort("\\Q")).to_sigbit_vector(); for (size_t i = 0; i < q1.size(); i++) if ((q1.at(i).wire == NULL || q2.at(i).wire == NULL) && q1.at(i) != q2.at(i)) { lt = q1.at(i) < q2.at(i); @@ -230,14 +223,13 @@ struct OptShareWorker if (mode_nomux) { ct.cell_types.erase("$mux"); ct.cell_types.erase("$pmux"); - ct.cell_types.erase("$safe_pmux"); } log("Finding identical cells in module `%s'.\n", module->name.c_str()); assign_map.set(module); dff_init_map.set(module); - for (auto &it : module->wires) + for (auto &it : module->wires_) if (it.second->attributes.count("\\init") != 0) dff_init_map.add(it.second, it.second->attributes.at("\\init")); @@ -248,8 +240,8 @@ struct OptShareWorker cell_hash_cache.clear(); #endif std::vector<RTLIL::Cell*> cells; - cells.reserve(module->cells.size()); - for (auto &it : module->cells) { + cells.reserve(module->cells_.size()); + for (auto &it : module->cells_) { if (ct.cell_known(it.second->type) && design->selected(module, it.second)) cells.push_back(it.second); } @@ -261,20 +253,18 @@ struct OptShareWorker if (sharemap.count(cell) > 0) { did_something = true; log(" Cell `%s' is identical to cell `%s'.\n", cell->name.c_str(), sharemap[cell]->name.c_str()); - for (auto &it : cell->connections) { + for (auto &it : cell->connections()) { if (ct.cell_output(cell->type, it.first)) { - RTLIL::SigSpec other_sig = sharemap[cell]->connections[it.first]; + RTLIL::SigSpec other_sig = sharemap[cell]->getPort(it.first); log(" Redirecting output %s: %s = %s\n", it.first.c_str(), log_signal(it.second), log_signal(other_sig)); - module->connections.push_back(RTLIL::SigSig(it.second, other_sig)); + module->connect(RTLIL::SigSig(it.second, other_sig)); assign_map.add(it.second, other_sig); } } log(" Removing %s cell `%s' from module `%s'.\n", cell->type.c_str(), cell->name.c_str(), module->name.c_str()); - module->cells.erase(cell->name); - OPT_DID_SOMETHING = true; + module->remove(cell); total_count++; - delete cell; } else { sharemap[cell] = cell; } @@ -316,13 +306,15 @@ struct OptSharePass : public Pass { extra_args(args, argidx, design); int total_count = 0; - for (auto &mod_it : design->modules) { + for (auto &mod_it : design->modules_) { if (!design->selected(mod_it.second)) continue; OptShareWorker worker(design, mod_it.second, mode_nomux); total_count += worker.total_count; } + if (total_count) + design->scratchpad_set_bool("opt.did_something", true); log("Removed a total of %d cells.\n", total_count); } } OptSharePass; diff --git a/passes/opt/opt_status.h b/passes/opt/opt_status.h deleted file mode 100644 index 3d12baa7d..000000000 --- a/passes/opt/opt_status.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * 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. - * - */ - -#ifndef OPT_STATUS_H -#define OPT_STATUS_H - -extern bool OPT_DID_SOMETHING; - -#endif - diff --git a/passes/opt/share.cc b/passes/opt/share.cc new file mode 100644 index 000000000..74b049bb6 --- /dev/null +++ b/passes/opt/share.cc @@ -0,0 +1,1171 @@ +/* + * 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/yosys.h" +#include "kernel/satgen.h" +#include "kernel/sigtools.h" +#include "kernel/modtools.h" +#include "kernel/utils.h" + +PRIVATE_NAMESPACE_BEGIN + +struct ShareWorkerConfig +{ + int limit; + bool opt_force; + bool opt_aggressive; + bool opt_fast; + std::set<RTLIL::IdString> generic_uni_ops, generic_bin_ops, generic_cbin_ops; +}; + +struct ShareWorker +{ + ShareWorkerConfig config; + std::set<RTLIL::IdString> generic_ops; + + RTLIL::Design *design; + RTLIL::Module *module; + + CellTypes fwd_ct, cone_ct; + ModWalker modwalker; + ModIndex mi; + + std::set<RTLIL::Cell*> cells_to_remove; + std::set<RTLIL::Cell*> recursion_state; + + SigMap topo_sigmap; + std::map<RTLIL::Cell*, std::set<RTLIL::Cell*>> topo_cell_drivers; + std::map<RTLIL::SigBit, std::set<RTLIL::Cell*>> topo_bit_drivers; + + + // ------------------------------------------------------------------------------ + // Find terminal bits -- i.e. bits that do not (exclusively) feed into a mux tree + // ------------------------------------------------------------------------------ + + std::set<RTLIL::SigBit> terminal_bits; + + void find_terminal_bits() + { + std::set<RTLIL::SigBit> queue_bits; + std::set<RTLIL::Cell*> visited_cells; + + queue_bits.insert(modwalker.signal_outputs.begin(), modwalker.signal_outputs.end()); + + for (auto &it : module->cells_) + if (!fwd_ct.cell_known(it.second->type)) { + std::set<RTLIL::SigBit> &bits = modwalker.cell_inputs[it.second]; + queue_bits.insert(bits.begin(), bits.end()); + } + + terminal_bits.insert(queue_bits.begin(), queue_bits.end()); + + while (!queue_bits.empty()) + { + std::set<ModWalker::PortBit> portbits; + modwalker.get_drivers(portbits, queue_bits); + queue_bits.clear(); + + for (auto &pbit : portbits) { + if (pbit.cell->type == "$mux" || pbit.cell->type == "$pmux") { + std::set<RTLIL::SigBit> bits = modwalker.sigmap(pbit.cell->getPort("\\S")).to_sigbit_set(); + terminal_bits.insert(bits.begin(), bits.end()); + queue_bits.insert(bits.begin(), bits.end()); + visited_cells.insert(pbit.cell); + } + if (fwd_ct.cell_known(pbit.cell->type) && visited_cells.count(pbit.cell) == 0) { + std::set<RTLIL::SigBit> &bits = modwalker.cell_inputs[pbit.cell]; + terminal_bits.insert(bits.begin(), bits.end()); + queue_bits.insert(bits.begin(), bits.end()); + visited_cells.insert(pbit.cell); + } + } + } + } + + + // --------------------------------------------------- + // Find shareable cells and compatible groups of cells + // --------------------------------------------------- + + std::set<RTLIL::Cell*, RTLIL::sort_by_name_str<RTLIL::Cell>> shareable_cells; + + void find_shareable_cells() + { + for (auto &it : module->cells_) + { + RTLIL::Cell *cell = it.second; + + if (!design->selected(module, cell) || !modwalker.ct.cell_known(cell->type)) + continue; + + for (auto &bit : modwalker.cell_outputs[cell]) + if (terminal_bits.count(bit)) + goto not_a_muxed_cell; + + if (0) + not_a_muxed_cell: + continue; + + if (config.opt_force) { + shareable_cells.insert(cell); + continue; + } + + if (cell->type == "$memrd") { + if (!cell->parameters.at("\\CLK_ENABLE").as_bool()) + shareable_cells.insert(cell); + continue; + } + + if (cell->type == "$mul" || cell->type == "$div" || cell->type == "$mod") { + if (config.opt_aggressive || cell->parameters.at("\\Y_WIDTH").as_int() >= 4) + shareable_cells.insert(cell); + continue; + } + + if (cell->type == "$shl" || cell->type == "$shr" || cell->type == "$sshl" || cell->type == "$sshr") { + if (config.opt_aggressive || cell->parameters.at("\\Y_WIDTH").as_int() >= 8) + shareable_cells.insert(cell); + continue; + } + + if (generic_ops.count(cell->type)) { + if (config.opt_aggressive || cell->parameters.at("\\Y_WIDTH").as_int() >= 10) + shareable_cells.insert(cell); + continue; + } + } + } + + bool is_shareable_pair(RTLIL::Cell *c1, RTLIL::Cell *c2) + { + if (c1->type != c2->type) + return false; + + if (c1->type == "$memrd") + { + if (c1->parameters.at("\\MEMID").decode_string() != c2->parameters.at("\\MEMID").decode_string()) + return false; + + return true; + } + + if (config.generic_uni_ops.count(c1->type)) + { + if (!config.opt_aggressive) + { + int a1_width = c1->parameters.at("\\A_WIDTH").as_int(); + int y1_width = c1->parameters.at("\\Y_WIDTH").as_int(); + + int a2_width = c2->parameters.at("\\A_WIDTH").as_int(); + int y2_width = c2->parameters.at("\\Y_WIDTH").as_int(); + + if (std::max(a1_width, a2_width) > 2 * std::min(a1_width, a2_width)) return false; + if (std::max(y1_width, y2_width) > 2 * std::min(y1_width, y2_width)) return false; + } + + return true; + } + + if (config.generic_bin_ops.count(c1->type)) + { + if (!config.opt_aggressive) + { + int a1_width = c1->parameters.at("\\A_WIDTH").as_int(); + int b1_width = c1->parameters.at("\\B_WIDTH").as_int(); + int y1_width = c1->parameters.at("\\Y_WIDTH").as_int(); + + int a2_width = c2->parameters.at("\\A_WIDTH").as_int(); + int b2_width = c2->parameters.at("\\B_WIDTH").as_int(); + int y2_width = c2->parameters.at("\\Y_WIDTH").as_int(); + + if (std::max(a1_width, a2_width) > 2 * std::min(a1_width, a2_width)) return false; + if (std::max(b1_width, b2_width) > 2 * std::min(b1_width, b2_width)) return false; + if (std::max(y1_width, y2_width) > 2 * std::min(y1_width, y2_width)) return false; + } + + return true; + } + + if (config.generic_cbin_ops.count(c1->type)) + { + if (!config.opt_aggressive) + { + int a1_width = c1->parameters.at("\\A_WIDTH").as_int(); + int b1_width = c1->parameters.at("\\B_WIDTH").as_int(); + int y1_width = c1->parameters.at("\\Y_WIDTH").as_int(); + + int a2_width = c2->parameters.at("\\A_WIDTH").as_int(); + int b2_width = c2->parameters.at("\\B_WIDTH").as_int(); + int y2_width = c2->parameters.at("\\Y_WIDTH").as_int(); + + int min1_width = std::min(a1_width, b1_width); + int max1_width = std::max(a1_width, b1_width); + + int min2_width = std::min(a2_width, b2_width); + int max2_width = std::max(a2_width, b2_width); + + if (std::max(min1_width, min2_width) > 2 * std::min(min1_width, min2_width)) return false; + if (std::max(max1_width, max2_width) > 2 * std::min(max1_width, max2_width)) return false; + if (std::max(y1_width, y2_width) > 2 * std::min(y1_width, y2_width)) return false; + } + + return true; + } + + for (auto &it : c1->parameters) + if (c2->parameters.count(it.first) == 0 || c2->parameters.at(it.first) != it.second) + return false; + + for (auto &it : c2->parameters) + if (c1->parameters.count(it.first) == 0 || c1->parameters.at(it.first) != it.second) + return false; + + return true; + } + + void find_shareable_partners(std::vector<RTLIL::Cell*> &results, RTLIL::Cell *cell) + { + results.clear(); + for (auto c : shareable_cells) + if (c != cell && is_shareable_pair(c, cell)) + results.push_back(c); + } + + + // ----------------------- + // Create replacement cell + // ----------------------- + + RTLIL::Cell *make_supercell(RTLIL::Cell *c1, RTLIL::Cell *c2, RTLIL::SigSpec act, std::set<RTLIL::Cell*> &supercell_aux) + { + log_assert(c1->type == c2->type); + + if (config.generic_uni_ops.count(c1->type)) + { + if (c1->parameters.at("\\A_SIGNED").as_bool() != c2->parameters.at("\\A_SIGNED").as_bool()) + { + RTLIL::Cell *unsigned_cell = c1->parameters.at("\\A_SIGNED").as_bool() ? c2 : c1; + if (unsigned_cell->getPort("\\A").to_sigbit_vector().back() != RTLIL::State::S0) { + unsigned_cell->parameters.at("\\A_WIDTH") = unsigned_cell->parameters.at("\\A_WIDTH").as_int() + 1; + RTLIL::SigSpec new_a = unsigned_cell->getPort("\\A"); + new_a.append_bit(RTLIL::State::S0); + unsigned_cell->setPort("\\A", new_a); + } + unsigned_cell->parameters.at("\\A_SIGNED") = true; + unsigned_cell->check(); + } + + bool a_signed = c1->parameters.at("\\A_SIGNED").as_bool(); + log_assert(a_signed == c2->parameters.at("\\A_SIGNED").as_bool()); + + RTLIL::SigSpec a1 = c1->getPort("\\A"); + RTLIL::SigSpec y1 = c1->getPort("\\Y"); + + RTLIL::SigSpec a2 = c2->getPort("\\A"); + RTLIL::SigSpec y2 = c2->getPort("\\Y"); + + int a_width = std::max(a1.size(), a2.size()); + int y_width = std::max(y1.size(), y2.size()); + + a1.extend_u0(a_width, a_signed); + a2.extend_u0(a_width, a_signed); + + RTLIL::SigSpec a = module->addWire(NEW_ID, a_width); + supercell_aux.insert(module->addMux(NEW_ID, a2, a1, act, a)); + + RTLIL::Wire *y = module->addWire(NEW_ID, y_width); + + RTLIL::Cell *supercell = module->addCell(NEW_ID, c1->type); + supercell->parameters["\\A_SIGNED"] = a_signed; + supercell->parameters["\\A_WIDTH"] = a_width; + supercell->parameters["\\Y_WIDTH"] = y_width; + supercell->setPort("\\A", a); + supercell->setPort("\\Y", y); + + supercell_aux.insert(module->addPos(NEW_ID, y, y1)); + supercell_aux.insert(module->addPos(NEW_ID, y, y2)); + + supercell_aux.insert(supercell); + return supercell; + } + + if (config.generic_bin_ops.count(c1->type) || config.generic_cbin_ops.count(c1->type)) + { + bool modified_src_cells = false; + + if (config.generic_cbin_ops.count(c1->type)) + { + int score_unflipped = std::max(c1->parameters.at("\\A_WIDTH").as_int(), c2->parameters.at("\\A_WIDTH").as_int()) + + std::max(c1->parameters.at("\\B_WIDTH").as_int(), c2->parameters.at("\\B_WIDTH").as_int()); + + int score_flipped = std::max(c1->parameters.at("\\A_WIDTH").as_int(), c2->parameters.at("\\B_WIDTH").as_int()) + + std::max(c1->parameters.at("\\B_WIDTH").as_int(), c2->parameters.at("\\A_WIDTH").as_int()); + + if (score_flipped < score_unflipped) + { + RTLIL::SigSpec tmp = c2->getPort("\\A"); + c2->setPort("\\A", c2->getPort("\\B")); + c2->setPort("\\B", tmp); + + std::swap(c2->parameters.at("\\A_WIDTH"), c2->parameters.at("\\B_WIDTH")); + std::swap(c2->parameters.at("\\A_SIGNED"), c2->parameters.at("\\B_SIGNED")); + modified_src_cells = true; + } + } + + if (c1->parameters.at("\\A_SIGNED").as_bool() != c2->parameters.at("\\A_SIGNED").as_bool()) + + { + RTLIL::Cell *unsigned_cell = c1->parameters.at("\\A_SIGNED").as_bool() ? c2 : c1; + if (unsigned_cell->getPort("\\A").to_sigbit_vector().back() != RTLIL::State::S0) { + unsigned_cell->parameters.at("\\A_WIDTH") = unsigned_cell->parameters.at("\\A_WIDTH").as_int() + 1; + RTLIL::SigSpec new_a = unsigned_cell->getPort("\\A"); + new_a.append_bit(RTLIL::State::S0); + unsigned_cell->setPort("\\A", new_a); + } + unsigned_cell->parameters.at("\\A_SIGNED") = true; + modified_src_cells = true; + } + + if (c1->parameters.at("\\B_SIGNED").as_bool() != c2->parameters.at("\\B_SIGNED").as_bool()) + { + RTLIL::Cell *unsigned_cell = c1->parameters.at("\\B_SIGNED").as_bool() ? c2 : c1; + if (unsigned_cell->getPort("\\B").to_sigbit_vector().back() != RTLIL::State::S0) { + unsigned_cell->parameters.at("\\B_WIDTH") = unsigned_cell->parameters.at("\\B_WIDTH").as_int() + 1; + RTLIL::SigSpec new_b = unsigned_cell->getPort("\\B"); + new_b.append_bit(RTLIL::State::S0); + unsigned_cell->setPort("\\B", new_b); + } + unsigned_cell->parameters.at("\\B_SIGNED") = true; + modified_src_cells = true; + } + + if (modified_src_cells) { + c1->check(); + c2->check(); + } + + bool a_signed = c1->parameters.at("\\A_SIGNED").as_bool(); + bool b_signed = c1->parameters.at("\\B_SIGNED").as_bool(); + + log_assert(a_signed == c2->parameters.at("\\A_SIGNED").as_bool()); + log_assert(b_signed == c2->parameters.at("\\B_SIGNED").as_bool()); + + if (c1->type == "$shl" || c1->type == "$shr" || c1->type == "$sshl" || c1->type == "$sshr") + b_signed = false; + + RTLIL::SigSpec a1 = c1->getPort("\\A"); + RTLIL::SigSpec b1 = c1->getPort("\\B"); + RTLIL::SigSpec y1 = c1->getPort("\\Y"); + + RTLIL::SigSpec a2 = c2->getPort("\\A"); + RTLIL::SigSpec b2 = c2->getPort("\\B"); + RTLIL::SigSpec y2 = c2->getPort("\\Y"); + + int a_width = std::max(a1.size(), a2.size()); + int b_width = std::max(b1.size(), b2.size()); + int y_width = std::max(y1.size(), y2.size()); + + if (c1->type == "$shr" && a_signed) + { + a_width = std::max(y_width, a_width); + + if (a1.size() < y1.size()) a1.extend_u0(y1.size(), true); + if (a2.size() < y2.size()) a2.extend_u0(y2.size(), true); + + a1.extend_u0(a_width, false); + a2.extend_u0(a_width, false); + } + else + { + a1.extend_u0(a_width, a_signed); + a2.extend_u0(a_width, a_signed); + } + + b1.extend_u0(b_width, b_signed); + b2.extend_u0(b_width, b_signed); + + RTLIL::SigSpec a = module->addWire(NEW_ID, a_width); + RTLIL::SigSpec b = module->addWire(NEW_ID, b_width); + + supercell_aux.insert(module->addMux(NEW_ID, a2, a1, act, a)); + supercell_aux.insert(module->addMux(NEW_ID, b2, b1, act, b)); + + RTLIL::Wire *y = module->addWire(NEW_ID, y_width); + + RTLIL::Cell *supercell = module->addCell(NEW_ID, c1->type); + supercell->parameters["\\A_SIGNED"] = a_signed; + supercell->parameters["\\B_SIGNED"] = b_signed; + supercell->parameters["\\A_WIDTH"] = a_width; + supercell->parameters["\\B_WIDTH"] = b_width; + supercell->parameters["\\Y_WIDTH"] = y_width; + supercell->setPort("\\A", a); + supercell->setPort("\\B", b); + supercell->setPort("\\Y", y); + supercell->check(); + + supercell_aux.insert(module->addPos(NEW_ID, y, y1)); + supercell_aux.insert(module->addPos(NEW_ID, y, y2)); + + supercell_aux.insert(supercell); + return supercell; + } + + if (c1->type == "$memrd") + { + RTLIL::Cell *supercell = module->addCell(NEW_ID, c1); + supercell_aux.insert(module->addPos(NEW_ID, supercell->getPort("\\DATA"), c2->getPort("\\DATA"))); + supercell_aux.insert(supercell); + return supercell; + } + + log_abort(); + } + + + // ------------------------------------------- + // Finding forbidden control inputs for a cell + // ------------------------------------------- + + std::map<RTLIL::Cell*, std::set<RTLIL::SigBit>> forbidden_controls_cache; + + const std::set<RTLIL::SigBit> &find_forbidden_controls(RTLIL::Cell *cell) + { + if (recursion_state.count(cell)) { + static std::set<RTLIL::SigBit> empty_controls_set; + return empty_controls_set; + } + + if (forbidden_controls_cache.count(cell)) + return forbidden_controls_cache.at(cell); + + std::set<ModWalker::PortBit> pbits; + std::set<RTLIL::Cell*> consumer_cells; + + modwalker.get_consumers(pbits, modwalker.cell_outputs[cell]); + + for (auto &bit : pbits) { + if ((bit.cell->type == "$mux" || bit.cell->type == "$pmux") && bit.port == "\\S") + forbidden_controls_cache[cell].insert(bit.cell->getPort("\\S").extract(bit.offset, 1)); + consumer_cells.insert(bit.cell); + } + + recursion_state.insert(cell); + + for (auto c : consumer_cells) + if (fwd_ct.cell_known(c->type)) { + const std::set<RTLIL::SigBit> &bits = find_forbidden_controls(c); + forbidden_controls_cache[cell].insert(bits.begin(), bits.end()); + } + + log_assert(recursion_state.count(cell)); + recursion_state.erase(cell); + + return forbidden_controls_cache[cell]; + } + + + // -------------------------------------------------------- + // Finding control inputs and activation pattern for a cell + // -------------------------------------------------------- + + std::map<RTLIL::Cell*, std::set<std::pair<RTLIL::SigSpec, RTLIL::Const>>> activation_patterns_cache; + + bool sort_check_activation_pattern(std::pair<RTLIL::SigSpec, RTLIL::Const> &p) + { + std::map<RTLIL::SigBit, RTLIL::State> p_bits; + + std::vector<RTLIL::SigBit> p_first_bits = p.first; + for (int i = 0; i < SIZE(p_first_bits); i++) { + RTLIL::SigBit b = p_first_bits[i]; + RTLIL::State v = p.second.bits[i]; + if (p_bits.count(b) && p_bits.at(b) != v) + return false; + p_bits[b] = v; + } + + p.first = RTLIL::SigSpec(); + p.second.bits.clear(); + + for (auto &it : p_bits) { + p.first.append_bit(it.first); + p.second.bits.push_back(it.second); + } + + return true; + } + + void optimize_activation_patterns(std::set<std::pair<RTLIL::SigSpec, RTLIL::Const>> & /* patterns */) + { + // TODO: Remove patterns that are contained in other patterns + // TODO: Consolidate pairs of patterns that only differ in the value for one signal bit + } + + const std::set<std::pair<RTLIL::SigSpec, RTLIL::Const>> &find_cell_activation_patterns(RTLIL::Cell *cell, const char *indent) + { + if (recursion_state.count(cell)) { + static std::set<std::pair<RTLIL::SigSpec, RTLIL::Const>> empty_patterns_set; + return empty_patterns_set; + } + + if (activation_patterns_cache.count(cell)) + return activation_patterns_cache.at(cell); + + const std::set<RTLIL::SigBit> &cell_out_bits = modwalker.cell_outputs[cell]; + std::set<RTLIL::Cell*> driven_cells, driven_data_muxes; + + for (auto &bit : cell_out_bits) + { + if (terminal_bits.count(bit)) { + // Terminal cells are always active: unconditional activation pattern + activation_patterns_cache[cell].insert(std::pair<RTLIL::SigSpec, RTLIL::Const>()); + return activation_patterns_cache.at(cell); + } + for (auto &pbit : modwalker.signal_consumers[bit]) { + log_assert(fwd_ct.cell_known(pbit.cell->type)); + if ((pbit.cell->type == "$mux" || pbit.cell->type == "$pmux") && (pbit.port == "\\A" || pbit.port == "\\B")) + driven_data_muxes.insert(pbit.cell); + else + driven_cells.insert(pbit.cell); + } + } + + recursion_state.insert(cell); + + for (auto c : driven_data_muxes) + { + const std::set<std::pair<RTLIL::SigSpec, RTLIL::Const>> &c_patterns = find_cell_activation_patterns(c, indent); + + bool used_in_a = false; + std::set<int> used_in_b_parts; + + int width = c->parameters.at("\\WIDTH").as_int(); + std::vector<RTLIL::SigBit> sig_a = modwalker.sigmap(c->getPort("\\A")); + std::vector<RTLIL::SigBit> sig_b = modwalker.sigmap(c->getPort("\\B")); + std::vector<RTLIL::SigBit> sig_s = modwalker.sigmap(c->getPort("\\S")); + + for (auto &bit : sig_a) + if (cell_out_bits.count(bit)) + used_in_a = true; + + for (int i = 0; i < SIZE(sig_b); i++) + if (cell_out_bits.count(sig_b[i])) + used_in_b_parts.insert(i / width); + + if (used_in_a) + for (auto p : c_patterns) { + for (int i = 0; i < SIZE(sig_s); i++) + p.first.append_bit(sig_s[i]), p.second.bits.push_back(RTLIL::State::S0); + if (sort_check_activation_pattern(p)) + activation_patterns_cache[cell].insert(p); + } + + for (int idx : used_in_b_parts) + for (auto p : c_patterns) { + p.first.append_bit(sig_s[idx]), p.second.bits.push_back(RTLIL::State::S1); + if (sort_check_activation_pattern(p)) + activation_patterns_cache[cell].insert(p); + } + } + + for (auto c : driven_cells) { + const std::set<std::pair<RTLIL::SigSpec, RTLIL::Const>> &c_patterns = find_cell_activation_patterns(c, indent); + activation_patterns_cache[cell].insert(c_patterns.begin(), c_patterns.end()); + } + + log_assert(recursion_state.count(cell)); + recursion_state.erase(cell); + + optimize_activation_patterns(activation_patterns_cache[cell]); + if (activation_patterns_cache[cell].empty()) { + log("%sFound cell that is never activated: %s\n", indent, log_id(cell)); + RTLIL::SigSpec cell_outputs = modwalker.cell_outputs[cell]; + module->connect(RTLIL::SigSig(cell_outputs, RTLIL::SigSpec(RTLIL::State::Sx, cell_outputs.size()))); + cells_to_remove.insert(cell); + } + + return activation_patterns_cache[cell]; + } + + RTLIL::SigSpec bits_from_activation_patterns(const std::set<std::pair<RTLIL::SigSpec, RTLIL::Const>> &activation_patterns) + { + std::set<RTLIL::SigBit> all_bits; + for (auto &it : activation_patterns) { + std::vector<RTLIL::SigBit> bits = it.first; + all_bits.insert(bits.begin(), bits.end()); + } + + RTLIL::SigSpec signal; + for (auto &bit : all_bits) + signal.append_bit(bit); + + return signal; + } + + void filter_activation_patterns(std::set<std::pair<RTLIL::SigSpec, RTLIL::Const>> &out, + const std::set<std::pair<RTLIL::SigSpec, RTLIL::Const>> &in, const std::set<RTLIL::SigBit> &filter_bits) + { + for (auto &p : in) + { + std::vector<RTLIL::SigBit> p_first = p.first; + std::pair<RTLIL::SigSpec, RTLIL::Const> new_p; + + for (int i = 0; i < SIZE(p_first); i++) + if (filter_bits.count(p_first[i]) == 0) { + new_p.first.append_bit(p_first[i]); + new_p.second.bits.push_back(p.second.bits.at(i)); + } + + out.insert(new_p); + } + } + + RTLIL::SigSpec make_cell_activation_logic(const std::set<std::pair<RTLIL::SigSpec, RTLIL::Const>> &activation_patterns, std::set<RTLIL::Cell*> &supercell_aux) + { + RTLIL::Wire *all_cases_wire = module->addWire(NEW_ID, 0); + + for (auto &p : activation_patterns) { + all_cases_wire->width++; + supercell_aux.insert(module->addEq(NEW_ID, p.first, p.second, RTLIL::SigSpec(all_cases_wire, all_cases_wire->width - 1))); + } + + if (all_cases_wire->width == 1) + return all_cases_wire; + + RTLIL::Wire *result_wire = module->addWire(NEW_ID); + supercell_aux.insert(module->addReduceOr(NEW_ID, all_cases_wire, result_wire)); + return result_wire; + } + + + // ------------------------------------------------------------------------------------- + // Helper functions used to make sure that this pass does not introduce new logic loops. + // ------------------------------------------------------------------------------------- + + bool module_has_scc() + { + CellTypes ct; + ct.setup_internals(); + ct.setup_stdcells(); + + TopoSort<RTLIL::Cell*> toposort; + toposort.analyze_loops = false; + + topo_sigmap.set(module); + topo_bit_drivers.clear(); + + std::map<RTLIL::Cell*, std::set<RTLIL::SigBit>> cell_to_bits; + std::map<RTLIL::SigBit, std::set<RTLIL::Cell*>> bit_to_cells; + + for (auto cell : module->cells()) + if (ct.cell_known(cell->type)) + for (auto &conn : cell->connections()) { + if (ct.cell_output(cell->type, conn.first)) + for (auto bit : topo_sigmap(conn.second)) { + cell_to_bits[cell].insert(bit); + topo_bit_drivers[bit].insert(cell); + } + else + for (auto bit : topo_sigmap(conn.second)) + bit_to_cells[bit].insert(cell); + } + + for (auto &it : cell_to_bits) + { + RTLIL::Cell *c1 = it.first; + + for (auto bit : it.second) + for (auto c2 : bit_to_cells[bit]) + toposort.edge(c1, c2); + } + + bool found_scc = !toposort.sort(); + topo_cell_drivers = std::move(toposort.database); + + if (found_scc && toposort.analyze_loops) + for (auto &loop : toposort.loops) { + log("### loop ###\n"); + for (auto &c : loop) + log("%s (%s)\n", log_id(c), log_id(c->type)); + } + + return found_scc; + } + + bool find_in_input_cone_worker(RTLIL::Cell *root, RTLIL::Cell *needle, std::set<RTLIL::Cell*> &stop) + { + if (root == needle) + return true; + + if (stop.count(root)) + return false; + + stop.insert(root); + + for (auto c : topo_cell_drivers[root]) + if (find_in_input_cone_worker(c, needle, stop)) + return true; + return false; + } + + bool find_in_input_cone(RTLIL::Cell *root, RTLIL::Cell *needle) + { + std::set<RTLIL::Cell*> stop; + return find_in_input_cone_worker(root, needle, stop); + } + + bool is_part_of_scc(RTLIL::Cell *cell) + { + CellTypes ct; + ct.setup_internals(); + ct.setup_stdcells(); + + std::set<RTLIL::Cell*> queue, covered; + queue.insert(cell); + + while (!queue.empty()) + { + std::set<RTLIL::Cell*> new_queue; + + for (auto c : queue) { + if (!ct.cell_known(c->type)) + continue; + for (auto &conn : c->connections()) + if (ct.cell_input(c->type, conn.first)) + for (auto bit : conn.second) + for (auto &pi : mi.query_ports(bit)) + if (ct.cell_known(pi.cell->type) && ct.cell_output(pi.cell->type, pi.port)) + new_queue.insert(pi.cell); + covered.insert(c); + } + + queue.clear(); + for (auto c : new_queue) { + if (cells_to_remove.count(c)) + continue; + if (c == cell) + return true; + if (!covered.count(c)) + queue.insert(c); + } + } + + return false; + } + + + // ------------- + // Setup and run + // ------------- + + ShareWorker(ShareWorkerConfig config, RTLIL::Design *design, RTLIL::Module *module) : + config(config), design(design), module(module), mi(module) + { + bool before_scc = module_has_scc(); + + generic_ops.insert(config.generic_uni_ops.begin(), config.generic_uni_ops.end()); + generic_ops.insert(config.generic_bin_ops.begin(), config.generic_bin_ops.end()); + generic_ops.insert(config.generic_cbin_ops.begin(), config.generic_cbin_ops.end()); + + fwd_ct.setup_internals(); + + cone_ct.setup_internals(); + cone_ct.cell_types.erase("$mul"); + cone_ct.cell_types.erase("$mod"); + cone_ct.cell_types.erase("$div"); + cone_ct.cell_types.erase("$pow"); + cone_ct.cell_types.erase("$shl"); + cone_ct.cell_types.erase("$shr"); + cone_ct.cell_types.erase("$sshl"); + cone_ct.cell_types.erase("$sshr"); + + modwalker.setup(design, module); + + find_terminal_bits(); + find_shareable_cells(); + + if (shareable_cells.size() < 2) + return; + + log("Found %d cells in module %s that may be considered for resource sharing.\n", + SIZE(shareable_cells), log_id(module)); + + while (!shareable_cells.empty() && config.limit != 0) + { + RTLIL::Cell *cell = *shareable_cells.begin(); + shareable_cells.erase(cell); + + log(" Analyzing resource sharing options for %s:\n", log_id(cell)); + + const std::set<std::pair<RTLIL::SigSpec, RTLIL::Const>> &cell_activation_patterns = find_cell_activation_patterns(cell, " "); + RTLIL::SigSpec cell_activation_signals = bits_from_activation_patterns(cell_activation_patterns); + + if (cell_activation_patterns.empty()) { + log(" Cell is never active. Sharing is pointless, we simply remove it.\n"); + cells_to_remove.insert(cell); + continue; + } + + if (cell_activation_patterns.count(std::pair<RTLIL::SigSpec, RTLIL::Const>())) { + log(" Cell is always active. Therefore no sharing is possible.\n"); + continue; + } + + log(" Found %d activation_patterns using ctrl signal %s.\n", SIZE(cell_activation_patterns), log_signal(cell_activation_signals)); + + std::vector<RTLIL::Cell*> candidates; + find_shareable_partners(candidates, cell); + + if (candidates.empty()) { + log(" No candidates found.\n"); + continue; + } + + log(" Found %d candidates:", SIZE(candidates)); + for (auto c : candidates) + log(" %s", log_id(c)); + log("\n"); + + for (auto other_cell : candidates) + { + log(" Analyzing resource sharing with %s:\n", log_id(other_cell)); + + const std::set<std::pair<RTLIL::SigSpec, RTLIL::Const>> &other_cell_activation_patterns = find_cell_activation_patterns(other_cell, " "); + RTLIL::SigSpec other_cell_activation_signals = bits_from_activation_patterns(other_cell_activation_patterns); + + if (other_cell_activation_patterns.empty()) { + log(" Cell is never active. Sharing is pointless, we simply remove it.\n"); + shareable_cells.erase(other_cell); + cells_to_remove.insert(other_cell); + continue; + } + + if (other_cell_activation_patterns.count(std::pair<RTLIL::SigSpec, RTLIL::Const>())) { + log(" Cell is always active. Therefore no sharing is possible.\n"); + shareable_cells.erase(other_cell); + continue; + } + + log(" Found %d activation_patterns using ctrl signal %s.\n", + SIZE(other_cell_activation_patterns), log_signal(other_cell_activation_signals)); + + const std::set<RTLIL::SigBit> &cell_forbidden_controls = find_forbidden_controls(cell); + const std::set<RTLIL::SigBit> &other_cell_forbidden_controls = find_forbidden_controls(other_cell); + + std::set<RTLIL::SigBit> union_forbidden_controls; + union_forbidden_controls.insert(cell_forbidden_controls.begin(), cell_forbidden_controls.end()); + union_forbidden_controls.insert(other_cell_forbidden_controls.begin(), other_cell_forbidden_controls.end()); + + if (!union_forbidden_controls.empty()) + log(" Forbidden control signals for this pair of cells: %s\n", log_signal(union_forbidden_controls)); + + std::set<std::pair<RTLIL::SigSpec, RTLIL::Const>> filtered_cell_activation_patterns; + std::set<std::pair<RTLIL::SigSpec, RTLIL::Const>> filtered_other_cell_activation_patterns; + + filter_activation_patterns(filtered_cell_activation_patterns, cell_activation_patterns, union_forbidden_controls); + filter_activation_patterns(filtered_other_cell_activation_patterns, other_cell_activation_patterns, union_forbidden_controls); + + optimize_activation_patterns(filtered_cell_activation_patterns); + optimize_activation_patterns(filtered_other_cell_activation_patterns); + + ezDefaultSAT ez; + SatGen satgen(&ez, &modwalker.sigmap); + + std::set<RTLIL::Cell*> sat_cells; + std::set<RTLIL::SigBit> bits_queue; + + std::vector<int> cell_active, other_cell_active; + RTLIL::SigSpec all_ctrl_signals; + + for (auto &p : filtered_cell_activation_patterns) { + log(" Activation pattern for cell %s: %s = %s\n", log_id(cell), log_signal(p.first), log_signal(p.second)); + cell_active.push_back(ez.vec_eq(satgen.importSigSpec(p.first), satgen.importSigSpec(p.second))); + all_ctrl_signals.append(p.first); + } + + for (auto &p : filtered_other_cell_activation_patterns) { + log(" Activation pattern for cell %s: %s = %s\n", log_id(other_cell), log_signal(p.first), log_signal(p.second)); + other_cell_active.push_back(ez.vec_eq(satgen.importSigSpec(p.first), satgen.importSigSpec(p.second))); + all_ctrl_signals.append(p.first); + } + + for (auto &bit : cell_activation_signals.to_sigbit_vector()) + bits_queue.insert(bit); + + for (auto &bit : other_cell_activation_signals.to_sigbit_vector()) + bits_queue.insert(bit); + + while (!bits_queue.empty()) + { + std::set<ModWalker::PortBit> portbits; + modwalker.get_drivers(portbits, bits_queue); + bits_queue.clear(); + + for (auto &pbit : portbits) + if (sat_cells.count(pbit.cell) == 0 && cone_ct.cell_known(pbit.cell->type)) { + if (config.opt_fast && modwalker.cell_outputs[pbit.cell].size() >= 4) + continue; + // log(" Adding cell %s (%s) to SAT problem.\n", log_id(pbit.cell), log_id(pbit.cell->type)); + bits_queue.insert(modwalker.cell_inputs[pbit.cell].begin(), modwalker.cell_inputs[pbit.cell].end()); + satgen.importCell(pbit.cell); + sat_cells.insert(pbit.cell); + } + + if (config.opt_fast && sat_cells.size() > 100) + break; + } + + if (!ez.solve(ez.expression(ez.OpOr, cell_active))) { + log(" According to the SAT solver the cell %s is never active. Sharing is pointless, we simply remove it.\n", log_id(cell)); + cells_to_remove.insert(cell); + break; + } + + if (!ez.solve(ez.expression(ez.OpOr, other_cell_active))) { + log(" According to the SAT solver the cell %s is never active. Sharing is pointless, we simply remove it.\n", log_id(other_cell)); + cells_to_remove.insert(other_cell); + shareable_cells.erase(other_cell); + continue; + } + + ez.non_incremental(); + + all_ctrl_signals.sort_and_unify(); + std::vector<int> sat_model = satgen.importSigSpec(all_ctrl_signals); + std::vector<bool> sat_model_values; + + ez.assume(ez.AND(ez.expression(ez.OpOr, cell_active), ez.expression(ez.OpOr, other_cell_active))); + + log(" Size of SAT problem: %d cells, %d variables, %d clauses\n", + SIZE(sat_cells), ez.numCnfVariables(), ez.numCnfClauses()); + + if (ez.solve(sat_model, sat_model_values)) { + log(" According to the SAT solver this pair of cells can not be shared.\n"); + log(" Model from SAT solver: %s = %d'", log_signal(all_ctrl_signals), SIZE(sat_model_values)); + for (int i = SIZE(sat_model_values)-1; i >= 0; i--) + log("%c", sat_model_values[i] ? '1' : '0'); + log("\n"); + continue; + } + + log(" According to the SAT solver this pair of cells can be shared.\n"); + + if (find_in_input_cone(cell, other_cell)) { + log(" Sharing not possible: %s is in input cone of %s.\n", log_id(other_cell), log_id(cell)); + continue; + } + + if (find_in_input_cone(other_cell, cell)) { + log(" Sharing not possible: %s is in input cone of %s.\n", log_id(cell), log_id(other_cell)); + continue; + } + + shareable_cells.erase(other_cell); + + int cell_select_score = 0; + int other_cell_select_score = 0; + + for (auto &p : filtered_cell_activation_patterns) + cell_select_score += p.first.size(); + + for (auto &p : filtered_other_cell_activation_patterns) + other_cell_select_score += p.first.size(); + + RTLIL::Cell *supercell; + std::set<RTLIL::Cell*> supercell_aux; + if (cell_select_score <= other_cell_select_score) { + RTLIL::SigSpec act = make_cell_activation_logic(filtered_cell_activation_patterns, supercell_aux); + supercell = make_supercell(cell, other_cell, act, supercell_aux); + log(" Activation signal for %s: %s\n", log_id(cell), log_signal(act)); + } else { + RTLIL::SigSpec act = make_cell_activation_logic(filtered_other_cell_activation_patterns, supercell_aux); + supercell = make_supercell(other_cell, cell, act, supercell_aux); + log(" Activation signal for %s: %s\n", log_id(other_cell), log_signal(act)); + } + + log(" New cell: %s (%s)\n", log_id(supercell), log_id(supercell->type)); + + cells_to_remove.insert(cell); + cells_to_remove.insert(other_cell); + + for (auto c : supercell_aux) + if (is_part_of_scc(c)) + goto do_rollback; + + if (0) { + do_rollback: + log(" New topology contains loops! Rolling back..\n"); + cells_to_remove.erase(cell); + cells_to_remove.erase(other_cell); + shareable_cells.insert(other_cell); + for (auto cc : supercell_aux) + module->remove(cc); + continue; + } + + std::set<std::pair<RTLIL::SigSpec, RTLIL::Const>> supercell_activation_patterns; + supercell_activation_patterns.insert(filtered_cell_activation_patterns.begin(), filtered_cell_activation_patterns.end()); + supercell_activation_patterns.insert(filtered_other_cell_activation_patterns.begin(), filtered_other_cell_activation_patterns.end()); + optimize_activation_patterns(supercell_activation_patterns); + activation_patterns_cache[supercell] = supercell_activation_patterns; + shareable_cells.insert(supercell); + + for (auto bit : topo_sigmap(all_ctrl_signals)) + for (auto c : topo_bit_drivers[bit]) + topo_cell_drivers[supercell].insert(c); + + topo_cell_drivers[supercell].insert(topo_cell_drivers[cell].begin(), topo_cell_drivers[cell].end()); + topo_cell_drivers[supercell].insert(topo_cell_drivers[other_cell].begin(), topo_cell_drivers[other_cell].end()); + + topo_cell_drivers[cell] = { supercell }; + topo_cell_drivers[other_cell] = { supercell }; + + if (config.limit > 0) + config.limit--; + + break; + } + } + + if (!cells_to_remove.empty()) { + log("Removing %d cells in module %s:\n", SIZE(cells_to_remove), log_id(module)); + for (auto c : cells_to_remove) { + log(" Removing cell %s (%s).\n", log_id(c), log_id(c->type)); + module->remove(c); + } + } + + log_assert(recursion_state.empty()); + + bool after_scc = before_scc || module_has_scc(); + log_assert(before_scc == after_scc); + } +}; + +struct SharePass : public Pass { + SharePass() : Pass("share", "perform sat-based resource sharing") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" share [options] [selection]\n"); + log("\n"); + log("This pass merges shareable resources into a single resource. A SAT solver\n"); + log("is used to determine if two resources are share-able.\n"); + log("\n"); + log(" -force\n"); + log(" Per default the selection of cells that is considered for sharing is\n"); + log(" narrowed using a list of cell types. With this option all selected\n"); + log(" cells are considered for resource sharing.\n"); + log("\n"); + log(" IMPORTANT NOTE: If the -all option is used then no cells with internal\n"); + log(" state must be selected!\n"); + log("\n"); + log(" -aggressive\n"); + log(" Per default some heuristics are used to reduce the number of cells\n"); + log(" considered for resource sharing to only large resources. This options\n"); + log(" turns this heuristics off, resulting in much more cells being considered\n"); + log(" for resource sharing.\n"); + log("\n"); + log(" -fast\n"); + log(" Only consider the simple part of the control logic in SAT solving, resulting\n"); + log(" in much easier SAT problems at the cost of maybe missing some oportunities\n"); + log(" for resource sharing.\n"); + log("\n"); + log(" -limit N\n"); + log(" Only perform the first N merges, then stop. This is useful for debugging.\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + { + ShareWorkerConfig config; + + config.limit = -1; + config.opt_force = false; + config.opt_aggressive = false; + config.opt_fast = false; + + config.generic_uni_ops.insert("$not"); + // config.generic_uni_ops.insert("$pos"); + config.generic_uni_ops.insert("$neg"); + + config.generic_cbin_ops.insert("$and"); + config.generic_cbin_ops.insert("$or"); + config.generic_cbin_ops.insert("$xor"); + config.generic_cbin_ops.insert("$xnor"); + + config.generic_bin_ops.insert("$shl"); + config.generic_bin_ops.insert("$shr"); + config.generic_bin_ops.insert("$sshl"); + config.generic_bin_ops.insert("$sshr"); + + config.generic_bin_ops.insert("$lt"); + config.generic_bin_ops.insert("$le"); + config.generic_bin_ops.insert("$eq"); + config.generic_bin_ops.insert("$ne"); + config.generic_bin_ops.insert("$eqx"); + config.generic_bin_ops.insert("$nex"); + config.generic_bin_ops.insert("$ge"); + config.generic_bin_ops.insert("$gt"); + + config.generic_cbin_ops.insert("$add"); + config.generic_cbin_ops.insert("$mul"); + + config.generic_bin_ops.insert("$sub"); + config.generic_bin_ops.insert("$div"); + config.generic_bin_ops.insert("$mod"); + // config.generic_bin_ops.insert("$pow"); + + config.generic_uni_ops.insert("$logic_not"); + config.generic_cbin_ops.insert("$logic_and"); + config.generic_cbin_ops.insert("$logic_or"); + + log_header("Executing SHARE pass (SAT-based resource sharing).\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-force") { + config.opt_force = true; + continue; + } + if (args[argidx] == "-aggressive") { + config.opt_aggressive = true; + continue; + } + if (args[argidx] == "-fast") { + config.opt_fast = true; + continue; + } + if (args[argidx] == "-limit" && argidx+1 < args.size()) { + config.limit = atoi(args[++argidx].c_str()); + continue; + } + break; + } + extra_args(args, argidx, design); + + for (auto &mod_it : design->modules_) + if (design->selected(mod_it.second)) + ShareWorker(config, design, mod_it.second); + } +} SharePass; + +PRIVATE_NAMESPACE_END + diff --git a/passes/opt/wreduce.cc b/passes/opt/wreduce.cc new file mode 100644 index 000000000..58a6d1b0d --- /dev/null +++ b/passes/opt/wreduce.cc @@ -0,0 +1,350 @@ +/* + * 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/yosys.h" +#include "kernel/sigtools.h" +#include "kernel/modtools.h" + +USING_YOSYS_NAMESPACE +using namespace RTLIL; + +PRIVATE_NAMESPACE_BEGIN + +struct WreduceConfig +{ + std::set<IdString> supported_cell_types; + + WreduceConfig() + { + supported_cell_types = { + "$not", "$pos", "$neg", + "$and", "$or", "$xor", "$xnor", + "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx", + "$lt", "$le", "$eq", "$ne", "$eqx", "$nex", "$ge", "$gt", + "$add", "$sub", // "$mul", "$div", "$mod", "$pow", + "$mux", "$pmux" + }; + } +}; + +struct WreduceWorker +{ + WreduceConfig *config; + Module *module; + ModIndex mi; + + std::set<Cell*, IdString::compare_ptr_by_name<Cell>> work_queue_cells; + std::set<SigBit> work_queue_bits; + + WreduceWorker(WreduceConfig *config, Module *module) : + config(config), module(module), mi(module) { } + + void run_cell_mux(Cell *cell) + { + // Reduce size of MUX if inputs agree on a value for a bit or a output bit is unused + + SigSpec sig_a = mi.sigmap(cell->getPort("\\A")); + SigSpec sig_b = mi.sigmap(cell->getPort("\\B")); + SigSpec sig_s = mi.sigmap(cell->getPort("\\S")); + SigSpec sig_y = mi.sigmap(cell->getPort("\\Y")); + std::vector<SigBit> bits_removed; + + for (int i = SIZE(sig_y)-1; i >= 0; i--) + { + auto info = mi.query(sig_y[i]); + if (!info->is_output && SIZE(info->ports) <= 1) { + bits_removed.push_back(Sx); + continue; + } + + SigBit ref = sig_a[i]; + for (int k = 0; k < SIZE(sig_s); k++) { + if (ref != Sx && sig_b[k*SIZE(sig_a) + i] != Sx && ref != sig_b[k*SIZE(sig_a) + i]) + goto no_match_ab; + if (sig_b[k*SIZE(sig_a) + i] != Sx) + ref = sig_b[k*SIZE(sig_a) + i]; + } + if (0) + no_match_ab: + break; + bits_removed.push_back(ref); + } + + if (bits_removed.empty()) + return; + + SigSpec sig_removed; + for (int i = SIZE(bits_removed)-1; i >= 0; i--) + sig_removed.append_bit(bits_removed[i]); + + if (SIZE(bits_removed) == SIZE(sig_y)) { + log("Removed cell %s.%s (%s).\n", log_id(module), log_id(cell), log_id(cell->type)); + module->connect(sig_y, sig_removed); + module->remove(cell); + return; + } + + log("Removed top %d bits (of %d) from mux cell %s.%s (%s).\n", + SIZE(sig_removed), SIZE(sig_y), log_id(module), log_id(cell), log_id(cell->type)); + + int n_removed = SIZE(sig_removed); + int n_kept = SIZE(sig_y) - SIZE(sig_removed); + + SigSpec new_work_queue_bits; + new_work_queue_bits.append(sig_a.extract(n_kept, n_removed)); + new_work_queue_bits.append(sig_y.extract(n_kept, n_removed)); + + SigSpec new_sig_a = sig_a.extract(0, n_kept); + SigSpec new_sig_y = sig_y.extract(0, n_kept); + SigSpec new_sig_b; + + for (int k = 0; k < SIZE(sig_s); k++) { + new_sig_b.append(sig_b.extract(k*SIZE(sig_a), n_kept)); + new_work_queue_bits.append(sig_b.extract(k*SIZE(sig_a) + n_kept, n_removed)); + } + + for (auto bit : new_work_queue_bits) + work_queue_bits.insert(bit); + + cell->setPort("\\A", new_sig_a); + cell->setPort("\\B", new_sig_b); + cell->setPort("\\Y", new_sig_y); + cell->fixup_parameters(); + + module->connect(sig_y.extract(n_kept, n_removed), sig_removed); + } + + void run_reduce_inport(Cell *cell, char port, int max_port_size, bool &port_signed, bool &did_something) + { + port_signed = cell->getParam(stringf("\\%c_SIGNED", port)).as_bool(); + SigSpec sig = mi.sigmap(cell->getPort(stringf("\\%c", port))); + + if (port == 'B' && cell->type.in("$shl", "$shr", "$sshl", "$sshr")) + port_signed = false; + + int bits_removed = 0; + if (SIZE(sig) > max_port_size) { + bits_removed = SIZE(sig) - max_port_size; + for (auto bit : sig.extract(max_port_size, bits_removed)) + work_queue_bits.insert(bit); + sig = sig.extract(0, max_port_size); + } + + if (port_signed) { + while (SIZE(sig) > 1 && sig[SIZE(sig)-1] == sig[SIZE(sig)-2]) + work_queue_bits.insert(sig[SIZE(sig)-1]), sig.remove(SIZE(sig)-1), bits_removed++; + } else { + while (SIZE(sig) > 1 && sig[SIZE(sig)-1] == S0) + work_queue_bits.insert(sig[SIZE(sig)-1]), sig.remove(SIZE(sig)-1), bits_removed++; + } + + if (bits_removed) { + log("Removed top %d bits (of %d) from port %c of cell %s.%s (%s).\n", + bits_removed, SIZE(sig) + bits_removed, port, log_id(module), log_id(cell), log_id(cell->type)); + cell->setPort(stringf("\\%c", port), sig); + did_something = true; + } + } + + void run_cell(Cell *cell) + { + bool did_something = false; + + if (!cell->type.in(config->supported_cell_types)) + return; + + if (cell->type.in("$mux", "$pmux")) + return run_cell_mux(cell); + + + // Reduce size of ports A and B based on constant input bits and size of output port + + int max_port_a_size = cell->hasPort("\\A") ? SIZE(cell->getPort("\\A")) : -1; + int max_port_b_size = cell->hasPort("\\B") ? SIZE(cell->getPort("\\B")) : -1; + + if (cell->type.in("$not", "$pos", "$neg", "$and", "$or", "$xor", "$add", "$sub")) { + max_port_a_size = std::min(max_port_a_size, SIZE(cell->getPort("\\Y"))); + max_port_b_size = std::min(max_port_b_size, SIZE(cell->getPort("\\Y"))); + } + + bool port_a_signed = false; + bool port_b_signed = false; + + if (max_port_a_size >= 0 && cell->type != "$shiftx") + run_reduce_inport(cell, 'A', max_port_a_size, port_a_signed, did_something); + + if (max_port_b_size >= 0) + run_reduce_inport(cell, 'B', max_port_b_size, port_b_signed, did_something); + + + // Reduce size of port Y based on sizes for A and B and unused bits in Y + + SigSpec sig = mi.sigmap(cell->getPort("\\Y")); + + int bits_removed = 0; + if (port_a_signed && cell->type == "$shr") { + // do not reduce size of output on $shr cells with signed A inputs + } else { + while (SIZE(sig) > 0) + { + auto info = mi.query(sig[SIZE(sig)-1]); + + if (info->is_output || SIZE(info->ports) > 1) + break; + + sig.remove(SIZE(sig)-1); + bits_removed++; + } + } + + if (cell->type.in("$pos", "$add", "$mul", "$and", "$or", "$xor")) + { + bool is_signed = cell->getParam("\\A_SIGNED").as_bool(); + + int a_size = 0, b_size = 0; + if (cell->hasPort("\\A")) a_size = SIZE(cell->getPort("\\A")); + if (cell->hasPort("\\B")) b_size = SIZE(cell->getPort("\\B")); + + int max_y_size = std::max(a_size, b_size); + + if (cell->type == "$add") + max_y_size++; + + if (cell->type == "$mul") + max_y_size = a_size + b_size; + + while (SIZE(sig) > 1 && SIZE(sig) > max_y_size) { + module->connect(sig[SIZE(sig)-1], is_signed ? sig[SIZE(sig)-2] : S0); + sig.remove(SIZE(sig)-1); + bits_removed++; + } + } + + if (SIZE(sig) == 0) { + log("Removed cell %s.%s (%s).\n", log_id(module), log_id(cell), log_id(cell->type)); + module->remove(cell); + return; + } + + if (bits_removed) { + log("Removed top %d bits (of %d) from port Y of cell %s.%s (%s).\n", + bits_removed, SIZE(sig) + bits_removed, log_id(module), log_id(cell), log_id(cell->type)); + cell->setPort("\\Y", sig); + did_something = true; + } + + if (did_something) { + cell->fixup_parameters(); + run_cell(cell); + } + } + + static int count_nontrivial_wire_attrs(RTLIL::Wire *w) + { + int count = w->attributes.size(); + count -= w->attributes.count("\\src"); + count -= w->attributes.count("\\unused_bits"); + return count; + } + + void run() + { + for (auto c : module->selected_cells()) + work_queue_cells.insert(c); + + while (!work_queue_cells.empty()) + { + work_queue_bits.clear(); + for (auto c : work_queue_cells) + run_cell(c); + + work_queue_cells.clear(); + for (auto bit : work_queue_bits) + for (auto port : mi.query_ports(bit)) + if (module->selected(port.cell)) + work_queue_cells.insert(port.cell); + } + + for (auto w : module->selected_wires()) + { + int unused_top_bits = 0; + + if (w->port_id > 0 || count_nontrivial_wire_attrs(w) > 0) + continue; + + for (int i = SIZE(w)-1; i >= 0; i--) { + SigBit bit(w, i); + auto info = mi.query(bit); + if (info && (info->is_input || info->is_output || SIZE(info->ports) > 0)) + break; + unused_top_bits++; + } + + if (0 < unused_top_bits && unused_top_bits < SIZE(w)) { + log("Removed top %d bits (of %d) from wire %s.%s.\n", unused_top_bits, SIZE(w), log_id(module), log_id(w)); + Wire *nw = module->addWire(NEW_ID, w); + nw->width = SIZE(w) - unused_top_bits; + module->connect(nw, SigSpec(w).extract(0, SIZE(nw))); + module->swap_names(w, nw); + } + } + } +}; + +struct WreducePass : public Pass { + WreducePass() : Pass("wreduce", "reduce the word size of operations is possible") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" wreduce [options] [selection]\n"); + log("\n"); + log("This command reduces the word size of operations. For example it will replace\n"); + log("the 32 bit adders in the following code with adders of more appropriate widths:\n"); + log("\n"); + log(" module test(input [3:0] a, b, c, output [7:0] y);\n"); + log(" assign y = a + b + c + 1;\n"); + log(" endmodule\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, Design *design) + { + WreduceConfig config; + + log_header("Executing WREDUCE pass (reducing word size of cells).\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + break; + } + extra_args(args, argidx, design); + + for (auto module : design->selected_modules()) + { + if (module->has_processes_warn()) + continue; + + WreduceWorker worker(&config, module); + worker.run(); + } + } +} WreducePass; + +PRIVATE_NAMESPACE_END + |