aboutsummaryrefslogtreecommitdiffstats
path: root/passes/opt
diff options
context:
space:
mode:
Diffstat (limited to 'passes/opt')
-rw-r--r--passes/opt/Makefile.inc5
-rw-r--r--passes/opt/muxpack.cc2
-rw-r--r--passes/opt/opt.cc36
-rw-r--r--passes/opt/opt_clean.cc88
-rw-r--r--passes/opt/opt_demorgan.cc2
-rw-r--r--passes/opt/opt_dff.cc918
-rw-r--r--passes/opt/opt_expr.cc276
-rw-r--r--passes/opt/opt_lut.cc59
-rw-r--r--passes/opt/opt_lut_ins.cc12
-rw-r--r--passes/opt/opt_mem.cc113
-rw-r--r--passes/opt/opt_mem_feedback.cc350
-rw-r--r--passes/opt/opt_mem_priority.cc109
-rw-r--r--passes/opt/opt_mem_widen.cc107
-rw-r--r--passes/opt/opt_merge.cc46
-rw-r--r--passes/opt/opt_muxtree.cc35
-rw-r--r--passes/opt/opt_reduce.cc403
-rw-r--r--passes/opt/opt_rmdff.cc711
-rw-r--r--passes/opt/opt_share.cc363
-rw-r--r--passes/opt/pmux2shiftx.cc21
-rw-r--r--passes/opt/rmports.cc2
-rw-r--r--passes/opt/share.cc86
-rw-r--r--passes/opt/wreduce.cc48
22 files changed, 2351 insertions, 1441 deletions
diff --git a/passes/opt/Makefile.inc b/passes/opt/Makefile.inc
index 3133927bb..4e52ad8da 100644
--- a/passes/opt/Makefile.inc
+++ b/passes/opt/Makefile.inc
@@ -2,9 +2,12 @@
OBJS += passes/opt/opt.o
OBJS += passes/opt/opt_merge.o
OBJS += passes/opt/opt_mem.o
+OBJS += passes/opt/opt_mem_feedback.o
+OBJS += passes/opt/opt_mem_priority.o
+OBJS += passes/opt/opt_mem_widen.o
OBJS += passes/opt/opt_muxtree.o
OBJS += passes/opt/opt_reduce.o
-OBJS += passes/opt/opt_rmdff.o
+OBJS += passes/opt/opt_dff.o
OBJS += passes/opt/opt_share.o
OBJS += passes/opt/opt_clean.o
OBJS += passes/opt/opt_expr.o
diff --git a/passes/opt/muxpack.cc b/passes/opt/muxpack.cc
index aa5f82437..b5e151098 100644
--- a/passes/opt/muxpack.cc
+++ b/passes/opt/muxpack.cc
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
* 2019 Eddie Hung <eddie@fpgeh.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
diff --git a/passes/opt/opt.cc b/passes/opt/opt.cc
index 8be94e345..c3e418c07 100644
--- a/passes/opt/opt.cc
+++ b/passes/opt/opt.cc
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -37,7 +37,7 @@ struct OptPass : public Pass {
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_expr [-mux_undef] [-mux_bool] [-undriven] [-clkinv] [-fine] [-full] [-keepdc]\n");
+ log(" opt_expr [-mux_undef] [-mux_bool] [-undriven] [-noclkinv] [-fine] [-full] [-keepdc]\n");
log(" opt_merge [-share_all] -nomux\n");
log("\n");
log(" do\n");
@@ -45,19 +45,19 @@ struct OptPass : public Pass {
log(" opt_reduce [-fine] [-full]\n");
log(" opt_merge [-share_all]\n");
log(" opt_share (-full only)\n");
- log(" opt_rmdff [-keepdc] [-sat] (except when called with -noff)\n");
+ log(" opt_dff [-nodffe] [-nosdff] [-keepdc] [-sat] (except when called with -noff)\n");
log(" opt_clean [-purge]\n");
- log(" opt_expr [-mux_undef] [-mux_bool] [-undriven] [-clkinv] [-fine] [-full] [-keepdc]\n");
+ log(" opt_expr [-mux_undef] [-mux_bool] [-undriven] [-noclkinv] [-fine] [-full] [-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_expr [-mux_undef] [-mux_bool] [-undriven] [-clkinv] [-fine] [-full] [-keepdc]\n");
+ log(" opt_expr [-mux_undef] [-mux_bool] [-undriven] [-noclkinv] [-fine] [-full] [-keepdc]\n");
log(" opt_merge [-share_all]\n");
- log(" opt_rmdff [-keepdc] [-sat] (except when called with -noff)\n");
+ log(" opt_dff [-nodffe] [-nosdff] [-keepdc] [-sat] (except when called with -noff)\n");
log(" opt_clean [-purge]\n");
- log(" while <changed design in opt_rmdff>\n");
+ log(" while <changed design in opt_dff>\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");
@@ -70,7 +70,7 @@ struct OptPass : public Pass {
std::string opt_expr_args;
std::string opt_reduce_args;
std::string opt_merge_args;
- std::string opt_rmdff_args;
+ std::string opt_dff_args;
bool opt_share = false;
bool fast_mode = false;
bool noff_mode = false;
@@ -96,8 +96,8 @@ struct OptPass : public Pass {
opt_expr_args += " -undriven";
continue;
}
- if (args[argidx] == "-clkinv") {
- opt_expr_args += " -clkinv";
+ if (args[argidx] == "-noclkinv") {
+ opt_expr_args += " -noclkinv";
continue;
}
if (args[argidx] == "-fine") {
@@ -113,11 +113,19 @@ struct OptPass : public Pass {
}
if (args[argidx] == "-keepdc") {
opt_expr_args += " -keepdc";
- opt_rmdff_args += " -keepdc";
+ opt_dff_args += " -keepdc";
+ continue;
+ }
+ if (args[argidx] == "-nodffe") {
+ opt_dff_args += " -nodffe";
+ continue;
+ }
+ if (args[argidx] == "-nosdff") {
+ opt_dff_args += " -nosdff";
continue;
}
if (args[argidx] == "-sat") {
- opt_rmdff_args += " -sat";
+ opt_dff_args += " -sat";
continue;
}
if (args[argidx] == "-share_all") {
@@ -143,7 +151,7 @@ struct OptPass : public Pass {
Pass::call(design, "opt_merge" + opt_merge_args);
design->scratchpad_unset("opt.did_something");
if (!noff_mode)
- Pass::call(design, "opt_rmdff" + opt_rmdff_args);
+ Pass::call(design, "opt_dff" + opt_dff_args);
if (design->scratchpad_get_bool("opt.did_something") == false)
break;
Pass::call(design, "opt_clean" + opt_clean_args);
@@ -163,7 +171,7 @@ struct OptPass : public Pass {
if (opt_share)
Pass::call(design, "opt_share");
if (!noff_mode)
- Pass::call(design, "opt_rmdff" + opt_rmdff_args);
+ Pass::call(design, "opt_dff" + opt_dff_args);
Pass::call(design, "opt_clean" + opt_clean_args);
Pass::call(design, "opt_expr" + opt_expr_args);
if (design->scratchpad_get_bool("opt.did_something") == false)
diff --git a/passes/opt/opt_clean.cc b/passes/opt/opt_clean.cc
index 44de60b48..cb2c261c4 100644
--- a/passes/opt/opt_clean.cc
+++ b/passes/opt/opt_clean.cc
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -21,6 +21,7 @@
#include "kernel/sigtools.h"
#include "kernel/log.h"
#include "kernel/celltypes.h"
+#include "kernel/ffinit.h"
#include <stdlib.h>
#include <stdio.h>
#include <set>
@@ -59,6 +60,11 @@ struct keep_cache_t
found_keep = true;
break;
}
+ for (auto wire : module->wires())
+ if (wire->get_bool_attribute(ID::keep)) {
+ found_keep = true;
+ break;
+ }
cache[module] = found_keep;
}
@@ -67,7 +73,7 @@ struct keep_cache_t
bool query(Cell *cell, bool ignore_specify = false)
{
- if (cell->type.in(ID($memwr), ID($meminit), ID($assert), ID($assume), ID($live), ID($fair), ID($cover)))
+ if (cell->type.in(ID($assert), ID($assume), ID($live), ID($fair), ID($cover)))
return true;
if (!ignore_specify && cell->type.in(ID($specify2), ID($specify3), ID($specrule)))
@@ -90,10 +96,13 @@ int count_rm_cells, count_rm_wires;
void rmunused_module_cells(Module *module, bool verbose)
{
SigMap sigmap(module);
+ dict<IdString, pool<Cell*>> mem2cells;
+ pool<IdString> mem_unused;
pool<Cell*> queue, unused;
pool<SigBit> used_raw_bits;
dict<SigBit, pool<Cell*>> wire2driver;
dict<SigBit, vector<string>> driver_driver_logs;
+ FfInitVals ffinit(&sigmap, module);
SigMap raw_sigmap;
for (auto &it : module->connections_) {
@@ -103,6 +112,17 @@ void rmunused_module_cells(Module *module, bool verbose)
}
}
+ for (auto &it : module->memories) {
+ mem_unused.insert(it.first);
+ }
+
+ for (Cell *cell : module->cells()) {
+ if (cell->type.in(ID($memwr), ID($memwr_v2), ID($meminit), ID($meminit_v2))) {
+ IdString mem_id = cell->getParam(ID::MEMID).decode_string();
+ mem2cells[mem_id].insert(cell);
+ }
+ }
+
for (auto &it : module->cells_) {
Cell *cell = it.second;
for (auto &it2 : cell->connections()) {
@@ -140,17 +160,33 @@ void rmunused_module_cells(Module *module, bool verbose)
while (!queue.empty())
{
pool<SigBit> bits;
- for (auto cell : queue)
- for (auto &it : cell->connections())
- if (!ct_all.cell_known(cell->type) || ct_all.cell_input(cell->type, it.first))
- for (auto bit : sigmap(it.second))
- bits.insert(bit);
+ pool<IdString> mems;
+ for (auto cell : queue) {
+ for (auto &it : cell->connections())
+ if (!ct_all.cell_known(cell->type) || ct_all.cell_input(cell->type, it.first))
+ for (auto bit : sigmap(it.second))
+ bits.insert(bit);
+
+ if (cell->type.in(ID($memrd), ID($memrd_v2))) {
+ IdString mem_id = cell->getParam(ID::MEMID).decode_string();
+ if (mem_unused.count(mem_id)) {
+ mem_unused.erase(mem_id);
+ mems.insert(mem_id);
+ }
+ }
+ }
queue.clear();
+
for (auto bit : bits)
for (auto c : wire2driver[bit])
if (unused.count(c))
queue.insert(c), unused.erase(c);
+
+ for (auto mem : mems)
+ for (auto c : mem2cells[mem])
+ if (unused.count(c))
+ queue.insert(c), unused.erase(c);
}
unused.sort(RTLIL::sort_by_name_id<RTLIL::Cell>());
@@ -159,10 +195,20 @@ void rmunused_module_cells(Module *module, bool verbose)
if (verbose)
log_debug(" removing unused `%s' cell `%s'.\n", cell->type.c_str(), cell->name.c_str());
module->design->scratchpad_set_bool("opt.did_something", true);
+ if (RTLIL::builtin_ff_cell_types().count(cell->type))
+ ffinit.remove_init(cell->getPort(ID::Q));
module->remove(cell);
count_rm_cells++;
}
+ for (auto it : mem_unused)
+ {
+ if (verbose)
+ log_debug(" removing unused memory `%s'.\n", it.c_str());
+ delete module->memories.at(it);
+ module->memories.erase(it);
+ }
+
for (auto &it : module->cells_) {
Cell *cell = it.second;
for (auto &it2 : cell->connections()) {
@@ -202,7 +248,7 @@ bool compare_signals(RTLIL::SigBit &s1, RTLIL::SigBit &s2, SigPool &regs, SigPoo
if ((w1->port_input && w1->port_output) != (w2->port_input && w2->port_output))
return !(w2->port_input && w2->port_output);
- if (w1->name[0] == '\\' && w2->name[0] == '\\') {
+ if (w1->name.isPublic() && w2->name.isPublic()) {
if (regs.check(s1) != regs.check(s2))
return regs.check(s2);
if (direct_wires.count(w1) != direct_wires.count(w2))
@@ -215,7 +261,7 @@ bool compare_signals(RTLIL::SigBit &s1, RTLIL::SigBit &s2, SigPool &regs, SigPoo
return w2->port_output;
if (w1->name[0] != w2->name[0])
- return w2->name[0] == '\\';
+ return w2->name.isPublic();
int attrs1 = count_nontrivial_wire_attrs(w1);
int attrs2 = count_nontrivial_wire_attrs(w2);
@@ -293,6 +339,7 @@ bool rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos
used_signals_nodrivers.add(it2.second);
}
}
+ dict<RTLIL::SigBit, RTLIL::State> init_bits;
for (auto &it : module->wires_) {
RTLIL::Wire *wire = it.second;
if (wire->port_id > 0) {
@@ -308,6 +355,29 @@ bool rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos
assign_map.apply(sig);
used_signals.add(sig);
}
+ auto it2 = wire->attributes.find(ID::init);
+ if (it2 != wire->attributes.end()) {
+ RTLIL::Const &val = it2->second;
+ SigSpec sig = assign_map(wire);
+ for (int i = 0; i < GetSize(val) && i < GetSize(sig); i++)
+ if (val.bits[i] != State::Sx)
+ init_bits[sig[i]] = val.bits[i];
+ wire->attributes.erase(it2);
+ }
+ }
+
+ for (auto wire : module->wires()) {
+ bool found = false;
+ Const val(State::Sx, wire->width);
+ for (int i = 0; i < wire->width; i++) {
+ auto it = init_bits.find(RTLIL::SigBit(wire, i));
+ if (it != init_bits.end()) {
+ val.bits[i] = it->second;
+ found = true;
+ }
+ }
+ if (found)
+ wire->attributes[ID::init] = val;
}
pool<RTLIL::Wire*> del_wires_queue;
diff --git a/passes/opt/opt_demorgan.cc b/passes/opt/opt_demorgan.cc
index f0fa86f42..1464c4177 100644
--- a/passes/opt/opt_demorgan.cc
+++ b/passes/opt/opt_demorgan.cc
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2017 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2017 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
diff --git a/passes/opt/opt_dff.cc b/passes/opt/opt_dff.cc
new file mode 100644
index 000000000..73d674c8d
--- /dev/null
+++ b/passes/opt/opt_dff.cc
@@ -0,0 +1,918 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
+ * Copyright (C) 2020 Marcelina Kościelnicka <mwk@0x04.net>
+ *
+ * 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/log.h"
+#include "kernel/register.h"
+#include "kernel/rtlil.h"
+#include "kernel/qcsat.h"
+#include "kernel/modtools.h"
+#include "kernel/sigtools.h"
+#include "kernel/ffinit.h"
+#include "kernel/ff.h"
+#include "passes/techmap/simplemap.h"
+#include <stdio.h>
+#include <stdlib.h>
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct OptDffOptions
+{
+ bool nosdff;
+ bool nodffe;
+ bool simple_dffe;
+ bool sat;
+ bool keepdc;
+};
+
+struct OptDffWorker
+{
+ const OptDffOptions &opt;
+
+ Module *module;
+ typedef std::pair<RTLIL::Cell*, int> cell_int_t;
+ SigMap sigmap;
+ FfInitVals initvals;
+ dict<SigBit, int> bitusers;
+ dict<SigBit, cell_int_t> bit2mux;
+
+ typedef std::map<RTLIL::SigBit, bool> pattern_t;
+ typedef std::set<pattern_t> patterns_t;
+ typedef std::pair<RTLIL::SigBit, bool> ctrl_t;
+ typedef std::set<ctrl_t> ctrls_t;
+
+ // Used as a queue.
+ std::vector<Cell *> dff_cells;
+
+ OptDffWorker(const OptDffOptions &opt, Module *mod) : opt(opt), module(mod), sigmap(mod), initvals(&sigmap, mod) {
+ // Gathering two kinds of information here for every sigmapped SigBit:
+ //
+ // - bitusers: how many users it has (muxes will only be merged into FFs if this is 1, making the FF the only user)
+ // - bit2mux: the mux cell and bit index that drives it, if any
+
+ for (auto wire : module->wires())
+ {
+ if (wire->port_output)
+ for (auto bit : sigmap(wire))
+ bitusers[bit]++;
+ }
+
+ for (auto cell : module->cells()) {
+ if (cell->type.in(ID($mux), ID($pmux), ID($_MUX_))) {
+ RTLIL::SigSpec sig_y = sigmap(cell->getPort(ID::Y));
+ for (int i = 0; i < GetSize(sig_y); i++)
+ bit2mux[sig_y[i]] = cell_int_t(cell, i);
+ }
+
+ for (auto conn : cell->connections()) {
+ bool is_output = cell->output(conn.first);
+ if (!is_output || !cell->known()) {
+ for (auto bit : sigmap(conn.second))
+ bitusers[bit]++;
+ }
+ }
+
+ if (module->design->selected(module, cell) && RTLIL::builtin_ff_cell_types().count(cell->type))
+ dff_cells.push_back(cell);
+ }
+
+ }
+
+ State combine_const(State a, State b) {
+ if (a == State::Sx && !opt.keepdc)
+ return b;
+ if (b == State::Sx && !opt.keepdc)
+ return a;
+ if (a == b)
+ return a;
+ return State::Sm;
+ }
+
+ patterns_t find_muxtree_feedback_patterns(RTLIL::SigBit d, RTLIL::SigBit q, pattern_t path)
+ {
+ patterns_t ret;
+
+ if (d == q) {
+ ret.insert(path);
+ return ret;
+ }
+
+ if (bit2mux.count(d) == 0 || bitusers[d] > 1)
+ return ret;
+
+ cell_int_t mbit = bit2mux.at(d);
+ RTLIL::SigSpec sig_a = sigmap(mbit.first->getPort(ID::A));
+ RTLIL::SigSpec sig_b = sigmap(mbit.first->getPort(ID::B));
+ RTLIL::SigSpec sig_s = sigmap(mbit.first->getPort(ID::S));
+ int width = GetSize(sig_a), index = mbit.second;
+
+ for (int i = 0; i < GetSize(sig_s); i++)
+ if (path.count(sig_s[i]) && path.at(sig_s[i]))
+ {
+ ret = find_muxtree_feedback_patterns(sig_b[i*width + index], q, path);
+
+ if (sig_b[i*width + index] == q) {
+ RTLIL::SigSpec s = mbit.first->getPort(ID::B);
+ s[i*width + index] = RTLIL::Sx;
+ mbit.first->setPort(ID::B, s);
+ }
+
+ return ret;
+ }
+
+ pattern_t path_else = path;
+
+ for (int i = 0; i < GetSize(sig_s); i++)
+ {
+ if (path.count(sig_s[i]))
+ continue;
+
+ pattern_t path_this = path;
+ path_else[sig_s[i]] = false;
+ path_this[sig_s[i]] = true;
+
+ for (auto &pat : find_muxtree_feedback_patterns(sig_b[i*width + index], q, path_this))
+ ret.insert(pat);
+
+ if (sig_b[i*width + index] == q) {
+ RTLIL::SigSpec s = mbit.first->getPort(ID::B);
+ s[i*width + index] = RTLIL::Sx;
+ mbit.first->setPort(ID::B, s);
+ }
+ }
+
+ for (auto &pat : find_muxtree_feedback_patterns(sig_a[index], q, path_else))
+ ret.insert(pat);
+
+ if (sig_a[index] == q) {
+ RTLIL::SigSpec s = mbit.first->getPort(ID::A);
+ s[index] = RTLIL::Sx;
+ mbit.first->setPort(ID::A, s);
+ }
+
+ return ret;
+ }
+
+ void simplify_patterns(patterns_t&)
+ {
+ // TBD
+ }
+
+ ctrl_t make_patterns_logic(const patterns_t &patterns, const ctrls_t &ctrls, bool make_gates)
+ {
+ if (patterns.empty() && GetSize(ctrls) == 1) {
+ return *ctrls.begin();
+ }
+
+ RTLIL::SigSpec or_input;
+
+ for (auto pat : patterns)
+ {
+ RTLIL::SigSpec s1, s2;
+ for (auto it : pat) {
+ s1.append(it.first);
+ s2.append(it.second);
+ }
+
+ RTLIL::SigSpec y = module->addWire(NEW_ID);
+ RTLIL::Cell *c = module->addNe(NEW_ID, s1, s2, y);
+
+ if (make_gates) {
+ simplemap(module, c);
+ module->remove(c);
+ }
+
+ or_input.append(y);
+ }
+ for (auto item : ctrls) {
+ if (item.second)
+ or_input.append(item.first);
+ else if (make_gates)
+ or_input.append(module->NotGate(NEW_ID, item.first));
+ else
+ or_input.append(module->Not(NEW_ID, item.first));
+ }
+
+ if (GetSize(or_input) == 0)
+ return ctrl_t(State::S1, true);
+
+ if (GetSize(or_input) == 1)
+ return ctrl_t(or_input, true);
+
+ RTLIL::SigSpec y = module->addWire(NEW_ID);
+ RTLIL::Cell *c = module->addReduceAnd(NEW_ID, or_input, y);
+
+ if (make_gates) {
+ simplemap(module, c);
+ module->remove(c);
+ }
+
+ return ctrl_t(y, true);
+ }
+
+ ctrl_t combine_resets(const ctrls_t &ctrls, bool make_gates)
+ {
+ if (GetSize(ctrls) == 1) {
+ return *ctrls.begin();
+ }
+
+ RTLIL::SigSpec or_input;
+
+ bool final_pol = false;
+ for (auto item : ctrls) {
+ if (item.second)
+ final_pol = true;
+ }
+
+ for (auto item : ctrls) {
+ if (item.second == final_pol)
+ or_input.append(item.first);
+ else if (make_gates)
+ or_input.append(module->NotGate(NEW_ID, item.first));
+ else
+ or_input.append(module->Not(NEW_ID, item.first));
+ }
+
+ RTLIL::SigSpec y = module->addWire(NEW_ID);
+ RTLIL::Cell *c = final_pol ? module->addReduceOr(NEW_ID, or_input, y) : module->addReduceAnd(NEW_ID, or_input, y);
+
+ if (make_gates) {
+ simplemap(module, c);
+ module->remove(c);
+ }
+
+ return ctrl_t(y, final_pol);
+ }
+
+ bool run() {
+ // We have all the information we need, and the list of FFs to process as well. Do it.
+ bool did_something = false;
+ while (!dff_cells.empty()) {
+ Cell *cell = dff_cells.back();
+ dff_cells.pop_back();
+ // Break down the FF into pieces.
+ FfData ff(&initvals, cell);
+ bool changed = false;
+
+ if (!ff.width) {
+ ff.remove();
+ did_something = true;
+ continue;
+ }
+
+ if (ff.has_sr) {
+ bool sr_removed = false;
+ std::vector<int> keep_bits;
+ // Check for always-active S/R bits.
+ for (int i = 0; i < ff.width; i++) {
+ if (ff.sig_clr[i] == (ff.pol_clr ? State::S1 : State::S0) || (!opt.keepdc && ff.sig_clr[i] == State::Sx)) {
+ // Always-active clear — connect Q bit to 0.
+ initvals.remove_init(ff.sig_q[i]);
+ module->connect(ff.sig_q[i], State::S0);
+ log("Handling always-active CLR at position %d on %s (%s) from module %s (changing to const driver).\n",
+ i, log_id(cell), log_id(cell->type), log_id(module));
+ sr_removed = true;
+ } else if (ff.sig_set[i] == (ff.pol_set ? State::S1 : State::S0) || (!opt.keepdc && ff.sig_set[i] == State::Sx)) {
+ // Always-active set — connect Q bit to 1 if clear inactive, 0 if reset active.
+ initvals.remove_init(ff.sig_q[i]);
+ if (!ff.pol_clr) {
+ module->connect(ff.sig_q[i], ff.sig_clr[i]);
+ } else if (ff.is_fine) {
+ module->addNotGate(NEW_ID, ff.sig_clr[i], ff.sig_q[i]);
+ } else {
+ module->addNot(NEW_ID, ff.sig_clr[i], ff.sig_q[i]);
+ }
+ log("Handling always-active SET at position %d on %s (%s) from module %s (changing to combinatorial circuit).\n",
+ i, log_id(cell), log_id(cell->type), log_id(module));
+ sr_removed = true;
+ } else {
+ keep_bits.push_back(i);
+ }
+ }
+ if (sr_removed) {
+ if (keep_bits.empty()) {
+ module->remove(cell);
+ did_something = true;
+ continue;
+ }
+ ff = ff.slice(keep_bits);
+ ff.cell = cell;
+ changed = true;
+ }
+
+ if (ff.pol_clr ? ff.sig_clr.is_fully_zero() : ff.sig_clr.is_fully_ones()) {
+ // CLR is useless, try to kill it.
+ bool failed = false;
+ for (int i = 0; i < ff.width; i++)
+ if (ff.sig_set[i] != ff.sig_set[0])
+ failed = true;
+ if (!failed) {
+ log("Removing never-active CLR on %s (%s) from module %s.\n",
+ log_id(cell), log_id(cell->type), log_id(module));
+ ff.has_sr = false;
+ ff.has_arst = true;
+ ff.pol_arst = ff.pol_set;
+ ff.sig_arst = ff.sig_set[0];
+ ff.val_arst = Const(State::S1, ff.width);
+ changed = true;
+ }
+ } else if (ff.pol_set ? ff.sig_set.is_fully_zero() : ff.sig_set.is_fully_ones()) {
+ // SET is useless, try to kill it.
+ bool failed = false;
+ for (int i = 0; i < ff.width; i++)
+ if (ff.sig_clr[i] != ff.sig_clr[0])
+ failed = true;
+ if (!failed) {
+ log("Removing never-active SET on %s (%s) from module %s.\n",
+ log_id(cell), log_id(cell->type), log_id(module));
+ ff.has_sr = false;
+ ff.has_arst = true;
+ ff.pol_arst = ff.pol_clr;
+ ff.sig_arst = ff.sig_clr[0];
+ ff.val_arst = Const(State::S0, ff.width);
+ changed = true;
+ }
+ } else if (ff.pol_clr == ff.pol_set) {
+ // Try a more complex conversion to plain async reset.
+ State val_neutral = ff.pol_set ? State::S0 : State::S1;
+ Const val_arst;
+ SigSpec sig_arst;
+ if (ff.sig_clr[0] == val_neutral)
+ sig_arst = ff.sig_set[0];
+ else
+ sig_arst = ff.sig_clr[0];
+ bool failed = false;
+ for (int i = 0; i < ff.width; i++) {
+ if (ff.sig_clr[i] == sig_arst && ff.sig_set[i] == val_neutral)
+ val_arst.bits.push_back(State::S0);
+ else if (ff.sig_set[i] == sig_arst && ff.sig_clr[i] == val_neutral)
+ val_arst.bits.push_back(State::S1);
+ else
+ failed = true;
+ }
+ if (!failed) {
+ log("Converting CLR/SET to ARST on %s (%s) from module %s.\n",
+ log_id(cell), log_id(cell->type), log_id(module));
+ ff.has_sr = false;
+ ff.has_arst = true;
+ ff.val_arst = val_arst;
+ ff.sig_arst = sig_arst;
+ ff.pol_arst = ff.pol_clr;
+ changed = true;
+ }
+ }
+ }
+
+ if (ff.has_aload) {
+ if (ff.sig_aload == (ff.pol_aload ? State::S0 : State::S1) || (!opt.keepdc && ff.sig_aload == State::Sx)) {
+ // Always-inactive enable — remove.
+ log("Removing never-active async load on %s (%s) from module %s.\n",
+ log_id(cell), log_id(cell->type), log_id(module));
+ ff.has_aload = false;
+ changed = true;
+ } else if (ff.sig_aload == (ff.pol_aload ? State::S1 : State::S0)) {
+ // Always-active enable. Make a comb circuit, nuke the FF/latch.
+ log("Handling always-active async load on %s (%s) from module %s (changing to combinatorial circuit).\n",
+ log_id(cell), log_id(cell->type), log_id(module));
+ ff.remove();
+ if (ff.has_sr) {
+ SigSpec tmp;
+ if (ff.is_fine) {
+ if (ff.pol_set)
+ tmp = module->MuxGate(NEW_ID, ff.sig_ad, State::S1, ff.sig_set);
+ else
+ tmp = module->MuxGate(NEW_ID, State::S1, ff.sig_ad, ff.sig_set);
+ if (ff.pol_clr)
+ module->addMuxGate(NEW_ID, tmp, State::S0, ff.sig_clr, ff.sig_q);
+ else
+ module->addMuxGate(NEW_ID, State::S0, tmp, ff.sig_clr, ff.sig_q);
+ } else {
+ if (ff.pol_set)
+ tmp = module->Or(NEW_ID, ff.sig_ad, ff.sig_set);
+ else
+ tmp = module->Or(NEW_ID, ff.sig_ad, module->Not(NEW_ID, ff.sig_set));
+ if (ff.pol_clr)
+ module->addAnd(NEW_ID, tmp, module->Not(NEW_ID, ff.sig_clr), ff.sig_q);
+ else
+ module->addAnd(NEW_ID, tmp, ff.sig_clr, ff.sig_q);
+ }
+ } else if (ff.has_arst) {
+ if (ff.is_fine) {
+ if (ff.pol_arst)
+ module->addMuxGate(NEW_ID, ff.sig_ad, ff.val_arst[0], ff.sig_arst, ff.sig_q);
+ else
+ module->addMuxGate(NEW_ID, ff.val_arst[0], ff.sig_ad, ff.sig_arst, ff.sig_q);
+ } else {
+ if (ff.pol_arst)
+ module->addMux(NEW_ID, ff.sig_ad, ff.val_arst, ff.sig_arst, ff.sig_q);
+ else
+ module->addMux(NEW_ID, ff.val_arst, ff.sig_ad, ff.sig_arst, ff.sig_q);
+ }
+ } else {
+ module->connect(ff.sig_q, ff.sig_ad);
+ }
+ did_something = true;
+ continue;
+ } else if (ff.sig_ad.is_fully_const() && !ff.has_arst && !ff.has_sr) {
+ log("Changing const-value async load to async reset on %s (%s) from module %s.\n",
+ log_id(cell), log_id(cell->type), log_id(module));
+ ff.has_arst = true;
+ ff.has_aload = false;
+ ff.sig_arst = ff.sig_aload;
+ ff.pol_arst = ff.pol_aload;
+ ff.val_arst = ff.sig_ad.as_const();
+ changed = true;
+ }
+ }
+
+ if (ff.has_arst) {
+ if (ff.sig_arst == (ff.pol_arst ? State::S0 : State::S1)) {
+ // Always-inactive reset — remove.
+ log("Removing never-active ARST on %s (%s) from module %s.\n",
+ log_id(cell), log_id(cell->type), log_id(module));
+ ff.has_arst = false;
+ changed = true;
+ } else if (ff.sig_arst == (ff.pol_arst ? State::S1 : State::S0) || (!opt.keepdc && ff.sig_arst == State::Sx)) {
+ // Always-active async reset — change to const driver.
+ log("Handling always-active ARST on %s (%s) from module %s (changing to const driver).\n",
+ log_id(cell), log_id(cell->type), log_id(module));
+ ff.remove();
+ module->connect(ff.sig_q, ff.val_arst);
+ did_something = true;
+ continue;
+ }
+ }
+
+ if (ff.has_srst) {
+ if (ff.sig_srst == (ff.pol_srst ? State::S0 : State::S1)) {
+ // Always-inactive reset — remove.
+ log("Removing never-active SRST on %s (%s) from module %s.\n",
+ log_id(cell), log_id(cell->type), log_id(module));
+ ff.has_srst = false;
+ changed = true;
+ } else if (ff.sig_srst == (ff.pol_srst ? State::S1 : State::S0) || (!opt.keepdc && ff.sig_srst == State::Sx)) {
+ // Always-active sync reset — connect to D instead.
+ log("Handling always-active SRST on %s (%s) from module %s (changing to const D).\n",
+ log_id(cell), log_id(cell->type), log_id(module));
+ ff.has_srst = false;
+ if (!ff.ce_over_srst)
+ ff.has_ce = false;
+ ff.sig_d = ff.val_srst;
+ changed = true;
+ }
+ }
+
+ if (ff.has_ce) {
+ if (ff.sig_ce == (ff.pol_ce ? State::S0 : State::S1) || (!opt.keepdc && ff.sig_ce == State::Sx)) {
+ // Always-inactive enable — remove.
+ if (ff.has_srst && !ff.ce_over_srst) {
+ log("Handling never-active EN on %s (%s) from module %s (connecting SRST instead).\n",
+ log_id(cell), log_id(cell->type), log_id(module));
+ // FF with sync reset — connect the sync reset to D instead.
+ ff.pol_ce = ff.pol_srst;
+ ff.sig_ce = ff.sig_srst;
+ ff.has_srst = false;
+ ff.sig_d = ff.val_srst;
+ changed = true;
+ } else {
+ log("Handling never-active EN on %s (%s) from module %s (removing D path).\n",
+ log_id(cell), log_id(cell->type), log_id(module));
+ // The D input path is effectively useless, so remove it (this will be a D latch, SR latch, or a const driver).
+ ff.has_ce = ff.has_clk = ff.has_srst = false;
+ changed = true;
+ }
+ } else if (ff.sig_ce == (ff.pol_ce ? State::S1 : State::S0)) {
+ // Always-active enable. Just remove it.
+ // For FF, just remove the useless enable.
+ log("Removing always-active EN on %s (%s) from module %s.\n",
+ log_id(cell), log_id(cell->type), log_id(module));
+ ff.has_ce = false;
+ changed = true;
+ }
+ }
+
+ if (ff.has_clk) {
+ if (ff.sig_clk.is_fully_const()) {
+ // Const clock — the D input path is effectively useless, so remove it (this will be a D latch, SR latch, or a const driver).
+ log("Handling const CLK on %s (%s) from module %s (removing D path).\n",
+ log_id(cell), log_id(cell->type), log_id(module));
+ ff.has_ce = ff.has_clk = ff.has_srst = false;
+ changed = true;
+ }
+ }
+
+ if ((ff.has_clk || ff.has_gclk) && ff.sig_d == ff.sig_q) {
+ // Q wrapped back to D, can be removed.
+ if (ff.has_clk && ff.has_srst) {
+ // FF with sync reset — connect the sync reset to D instead.
+ log("Handling D = Q on %s (%s) from module %s (conecting SRST instead).\n",
+ log_id(cell), log_id(cell->type), log_id(module));
+ if (ff.has_ce && ff.ce_over_srst) {
+ if (!ff.pol_ce) {
+ if (ff.is_fine)
+ ff.sig_ce = module->NotGate(NEW_ID, ff.sig_ce);
+ else
+ ff.sig_ce = module->Not(NEW_ID, ff.sig_ce);
+ }
+ if (!ff.pol_srst) {
+ if (ff.is_fine)
+ ff.sig_srst = module->NotGate(NEW_ID, ff.sig_srst);
+ else
+ ff.sig_srst = module->Not(NEW_ID, ff.sig_srst);
+ }
+ if (ff.is_fine)
+ ff.sig_ce = module->AndGate(NEW_ID, ff.sig_ce, ff.sig_srst);
+ else
+ ff.sig_ce = module->And(NEW_ID, ff.sig_ce, ff.sig_srst);
+ ff.pol_ce = true;
+ } else {
+ ff.pol_ce = ff.pol_srst;
+ ff.sig_ce = ff.sig_srst;
+ }
+ ff.has_ce = true;
+ ff.has_srst = false;
+ ff.sig_d = ff.val_srst;
+ changed = true;
+ } else {
+ // The D input path is effectively useless, so remove it (this will be a const-input D latch, SR latch, or a const driver).
+ log("Handling D = Q on %s (%s) from module %s (removing D path).\n",
+ log_id(cell), log_id(cell->type), log_id(module));
+ ff.has_clk = ff.has_ce = false;
+ changed = true;
+ }
+ }
+
+ if (ff.has_aload && !ff.has_clk && ff.sig_ad == ff.sig_q) {
+ log("Handling AD = Q on %s (%s) from module %s (removing async load path).\n",
+ log_id(cell), log_id(cell->type), log_id(module));
+ ff.has_aload = false;
+ changed = true;
+ }
+
+ // The cell has been simplified as much as possible already. Now try to spice it up with enables / sync resets.
+ if (ff.has_clk) {
+ if (!ff.has_arst && !ff.has_sr && (!ff.has_srst || !ff.has_ce || ff.ce_over_srst) && !opt.nosdff) {
+ // Try to merge sync resets.
+ std::map<ctrls_t, std::vector<int>> groups;
+ std::vector<int> remaining_indices;
+ Const val_srst;
+
+ for (int i = 0 ; i < ff.width; i++) {
+ ctrls_t resets;
+ State reset_val = State::Sx;
+ if (ff.has_srst)
+ reset_val = ff.val_srst[i];
+ while (bit2mux.count(ff.sig_d[i]) && bitusers[ff.sig_d[i]] == 1) {
+ cell_int_t mbit = bit2mux.at(ff.sig_d[i]);
+ if (GetSize(mbit.first->getPort(ID::S)) != 1)
+ break;
+ SigBit s = mbit.first->getPort(ID::S);
+ SigBit a = mbit.first->getPort(ID::A)[mbit.second];
+ SigBit b = mbit.first->getPort(ID::B)[mbit.second];
+ // Workaround for funny memory WE pattern.
+ if ((a == State::S0 || a == State::S1) && (b == State::S0 || b == State::S1))
+ break;
+ if ((b == State::S0 || b == State::S1) && (b == reset_val || reset_val == State::Sx)) {
+ // This is better handled by CE pattern.
+ if (a == ff.sig_q[i])
+ break;
+ reset_val = b.data;
+ resets.insert(ctrl_t(s, true));
+ ff.sig_d[i] = a;
+ } else if ((a == State::S0 || a == State::S1) && (a == reset_val || reset_val == State::Sx)) {
+ // This is better handled by CE pattern.
+ if (b == ff.sig_q[i])
+ break;
+ reset_val = a.data;
+ resets.insert(ctrl_t(s, false));
+ ff.sig_d[i] = b;
+ } else {
+ break;
+ }
+ }
+
+ if (!resets.empty()) {
+ if (ff.has_srst)
+ resets.insert(ctrl_t(ff.sig_srst, ff.pol_srst));
+ groups[resets].push_back(i);
+ } else
+ remaining_indices.push_back(i);
+ val_srst.bits.push_back(reset_val);
+ }
+
+ for (auto &it : groups) {
+ FfData new_ff = ff.slice(it.second);
+ new_ff.val_srst = Const();
+ for (int i = 0; i < new_ff.width; i++) {
+ int j = it.second[i];
+ new_ff.val_srst.bits.push_back(val_srst[j]);
+ }
+ ctrl_t srst = combine_resets(it.first, ff.is_fine);
+
+ new_ff.has_srst = true;
+ new_ff.sig_srst = srst.first;
+ new_ff.pol_srst = srst.second;
+ if (new_ff.has_ce)
+ new_ff.ce_over_srst = true;
+ Cell *new_cell = new_ff.emit();
+ if (new_cell)
+ dff_cells.push_back(new_cell);
+ log("Adding SRST signal on %s (%s) from module %s (D = %s, Q = %s, rval = %s).\n",
+ log_id(cell), log_id(cell->type), log_id(module), log_signal(new_ff.sig_d), log_signal(new_ff.sig_q), log_signal(new_ff.val_srst));
+ }
+
+ if (remaining_indices.empty()) {
+ module->remove(cell);
+ did_something = true;
+ continue;
+ } else if (GetSize(remaining_indices) != ff.width) {
+ ff = ff.slice(remaining_indices);
+ ff.cell = cell;
+ changed = true;
+ }
+ }
+ if ((!ff.has_srst || !ff.has_ce || !ff.ce_over_srst) && !opt.nodffe) {
+ // Try to merge enables.
+ std::map<std::pair<patterns_t, ctrls_t>, std::vector<int>> groups;
+ std::vector<int> remaining_indices;
+
+ for (int i = 0 ; i < ff.width; i++) {
+ // First, eat up as many simple muxes as possible.
+ ctrls_t enables;
+ while (bit2mux.count(ff.sig_d[i]) && bitusers[ff.sig_d[i]] == 1) {
+ cell_int_t mbit = bit2mux.at(ff.sig_d[i]);
+ if (GetSize(mbit.first->getPort(ID::S)) != 1)
+ break;
+ SigBit s = mbit.first->getPort(ID::S);
+ SigBit a = mbit.first->getPort(ID::A)[mbit.second];
+ SigBit b = mbit.first->getPort(ID::B)[mbit.second];
+ if (a == ff.sig_q[i]) {
+ enables.insert(ctrl_t(s, true));
+ ff.sig_d[i] = b;
+ } else if (b == ff.sig_q[i]) {
+ enables.insert(ctrl_t(s, false));
+ ff.sig_d[i] = a;
+ } else {
+ break;
+ }
+ }
+
+ patterns_t patterns;
+ if (!opt.simple_dffe)
+ patterns = find_muxtree_feedback_patterns(ff.sig_d[i], ff.sig_q[i], pattern_t());
+ if (!patterns.empty() || !enables.empty()) {
+ if (ff.has_ce)
+ enables.insert(ctrl_t(ff.sig_ce, ff.pol_ce));
+ simplify_patterns(patterns);
+ groups[std::make_pair(patterns, enables)].push_back(i);
+ } else
+ remaining_indices.push_back(i);
+ }
+
+ for (auto &it : groups) {
+ FfData new_ff = ff.slice(it.second);
+ ctrl_t en = make_patterns_logic(it.first.first, it.first.second, ff.is_fine);
+
+ new_ff.has_ce = true;
+ new_ff.sig_ce = en.first;
+ new_ff.pol_ce = en.second;
+ new_ff.ce_over_srst = false;
+ Cell *new_cell = new_ff.emit();
+ if (new_cell)
+ dff_cells.push_back(new_cell);
+ log("Adding EN signal on %s (%s) from module %s (D = %s, Q = %s).\n",
+ log_id(cell), log_id(cell->type), log_id(module), log_signal(new_ff.sig_d), log_signal(new_ff.sig_q));
+ }
+
+ if (remaining_indices.empty()) {
+ module->remove(cell);
+ did_something = true;
+ continue;
+ } else if (GetSize(remaining_indices) != ff.width) {
+ ff = ff.slice(remaining_indices);
+ ff.cell = cell;
+ changed = true;
+ }
+ }
+ }
+
+ if (changed) {
+ // Rebuild the FF.
+ ff.emit();
+ did_something = true;
+ }
+ }
+ return did_something;
+ }
+
+ bool run_constbits() {
+ ModWalker modwalker(module->design, module);
+ QuickConeSat qcsat(modwalker);
+
+ // Run as a separate sub-pass, so that we don't mutate (non-FF) cells under ModWalker.
+ bool did_something = false;
+ for (auto cell : module->selected_cells()) {
+ if (!RTLIL::builtin_ff_cell_types().count(cell->type))
+ continue;
+ FfData ff(&initvals, cell);
+
+ // Now check if any bit can be replaced by a constant.
+ pool<int> removed_sigbits;
+ for (int i = 0; i < ff.width; i++) {
+ State val = ff.val_init[i];
+ if (ff.has_arst)
+ val = combine_const(val, ff.val_arst[i]);
+ if (ff.has_srst)
+ val = combine_const(val, ff.val_srst[i]);
+ if (ff.has_sr) {
+ if (ff.sig_clr[i] != (ff.pol_clr ? State::S0 : State::S1))
+ val = combine_const(val, State::S0);
+ if (ff.sig_set[i] != (ff.pol_set ? State::S0 : State::S1))
+ val = combine_const(val, State::S1);
+ }
+ if (val == State::Sm)
+ continue;
+ if (ff.has_clk || ff.has_gclk) {
+ if (!ff.sig_d[i].wire) {
+ val = combine_const(val, ff.sig_d[i].data);
+ if (val == State::Sm)
+ continue;
+ } else {
+ if (!opt.sat)
+ continue;
+ // For each register bit, try to prove that it cannot change from the initial value. If so, remove it
+ if (!modwalker.has_drivers(ff.sig_d.extract(i)))
+ continue;
+ if (val != State::S0 && val != State::S1)
+ continue;
+
+ int init_sat_pi = qcsat.importSigBit(val);
+ int q_sat_pi = qcsat.importSigBit(ff.sig_q[i]);
+ int d_sat_pi = qcsat.importSigBit(ff.sig_d[i]);
+
+ qcsat.prepare();
+
+ // Try to find out whether the register bit can change under some circumstances
+ bool counter_example_found = qcsat.ez->solve(qcsat.ez->IFF(q_sat_pi, init_sat_pi), qcsat.ez->NOT(qcsat.ez->IFF(d_sat_pi, init_sat_pi)));
+
+ // If the register bit cannot change, we can replace it with a constant
+ if (counter_example_found)
+ continue;
+ }
+ }
+ if (ff.has_aload) {
+ if (!ff.sig_ad[i].wire) {
+ val = combine_const(val, ff.sig_ad[i].data);
+ if (val == State::Sm)
+ continue;
+ } else {
+ if (!opt.sat)
+ continue;
+ // For each register bit, try to prove that it cannot change from the initial value. If so, remove it
+ if (!modwalker.has_drivers(ff.sig_ad.extract(i)))
+ continue;
+ if (val != State::S0 && val != State::S1)
+ continue;
+
+ int init_sat_pi = qcsat.importSigBit(val);
+ int q_sat_pi = qcsat.importSigBit(ff.sig_q[i]);
+ int d_sat_pi = qcsat.importSigBit(ff.sig_ad[i]);
+
+ qcsat.prepare();
+
+ // Try to find out whether the register bit can change under some circumstances
+ bool counter_example_found = qcsat.ez->solve(qcsat.ez->IFF(q_sat_pi, init_sat_pi), qcsat.ez->NOT(qcsat.ez->IFF(d_sat_pi, init_sat_pi)));
+
+ // If the register bit cannot change, we can replace it with a constant
+ if (counter_example_found)
+ continue;
+ }
+ }
+ log("Setting constant %d-bit at position %d on %s (%s) from module %s.\n", val ? 1 : 0,
+ i, log_id(cell), log_id(cell->type), log_id(module));
+
+ initvals.remove_init(ff.sig_q[i]);
+ module->connect(ff.sig_q[i], val);
+ removed_sigbits.insert(i);
+ }
+ if (!removed_sigbits.empty()) {
+ std::vector<int> keep_bits;
+ for (int i = 0; i < ff.width; i++)
+ if (!removed_sigbits.count(i))
+ keep_bits.push_back(i);
+ if (keep_bits.empty()) {
+ module->remove(cell);
+ did_something = true;
+ continue;
+ }
+ ff = ff.slice(keep_bits);
+ ff.cell = cell;
+ ff.emit();
+ did_something = true;
+ }
+ }
+ return did_something;
+ }
+};
+
+struct OptDffPass : public Pass {
+ OptDffPass() : Pass("opt_dff", "perform DFF optimizations") { }
+ void help() override
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" opt_dff [-nodffe] [-nosdff] [-keepdc] [-sat] [selection]\n");
+ log("\n");
+ log("This pass converts flip-flops to a more suitable type by merging clock enables\n");
+ log("and synchronous reset multiplexers, removing unused control inputs, or potentially\n");
+ log("removes the flip-flop altogether, converting it to a constant driver.\n");
+ log("\n");
+ log(" -nodffe\n");
+ log(" disables dff -> dffe conversion, and other transforms recognizing clock enable\n");
+ log("\n");
+ log(" -nosdff\n");
+ log(" disables dff -> sdff conversion, and other transforms recognizing sync resets\n");
+ log("\n");
+ log(" -simple-dffe\n");
+ log(" only enables clock enable recognition transform for obvious cases\n");
+ log("\n");
+ log(" -sat\n");
+ log(" additionally invoke SAT solver to detect and remove flip-flops (with\n");
+ log(" non-constant inputs) that can also be replaced with a constant driver\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");
+ }
+
+ void execute(std::vector<std::string> args, RTLIL::Design *design) override
+ {
+ log_header(design, "Executing OPT_DFF pass (perform DFF optimizations).\n");
+ OptDffOptions opt;
+ opt.nodffe = false;
+ opt.nosdff = false;
+ opt.simple_dffe = false;
+ opt.keepdc = false;
+ opt.sat = false;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ if (args[argidx] == "-nodffe") {
+ opt.nodffe = true;
+ continue;
+ }
+ if (args[argidx] == "-nosdff") {
+ opt.nosdff = true;
+ continue;
+ }
+ if (args[argidx] == "-simple-dffe") {
+ opt.simple_dffe = true;
+ continue;
+ }
+ if (args[argidx] == "-keepdc") {
+ opt.keepdc = true;
+ continue;
+ }
+ if (args[argidx] == "-sat") {
+ opt.sat = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ bool did_something = false;
+ for (auto mod : design->selected_modules()) {
+ OptDffWorker worker(opt, mod);
+ if (worker.run())
+ did_something = true;
+ if (worker.run_constbits())
+ did_something = true;
+ }
+
+ if (did_something)
+ design->scratchpad_set_bool("opt.did_something", true);
+ }
+} OptDffPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/opt/opt_expr.cc b/passes/opt/opt_expr.cc
index 1051a59f2..be0cd470b 100644
--- a/passes/opt/opt_expr.cc
+++ b/passes/opt/opt_expr.cc
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -393,34 +393,8 @@ int get_highest_hot_index(RTLIL::SigSpec signal)
return -1;
}
-// if the signal has only one bit set, return the index of that bit.
-// otherwise return -1
-int get_onehot_bit_index(RTLIL::SigSpec signal)
+void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool consume_x, bool mux_undef, bool mux_bool, bool do_fine, bool keepdc, bool noclkinv)
{
- int bit_index = -1;
-
- for (int i = 0; i < GetSize(signal); i++)
- {
- if (signal[i] == RTLIL::State::S0)
- continue;
-
- if (signal[i] != RTLIL::State::S1)
- return -1;
-
- if (bit_index != -1)
- return -1;
-
- bit_index = i;
- }
-
- return bit_index;
-}
-
-void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool consume_x, bool mux_undef, bool mux_bool, bool do_fine, bool keepdc, bool clkinv)
-{
- if (!design->selected(module))
- return;
-
CellTypes ct_combinational;
ct_combinational.setup_internals();
ct_combinational.setup_stdcells();
@@ -465,9 +439,9 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
#define ACTION_DO(_p_, _s_) do { cover("opt.opt_expr.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(ID::Y, RTLIL::SigSpec(RTLIL::State::S ## _v_))
- if (clkinv)
+ if (!noclkinv)
{
- if (cell->type.in(ID($dff), ID($dffe), ID($dffsr), ID($dffsre), ID($adff), ID($adffe), ID($sdff), ID($sdffe), ID($sdffce), ID($fsm), ID($memrd), ID($memwr)))
+ if (cell->type.in(ID($dff), ID($dffe), ID($dffsr), ID($dffsre), ID($adff), ID($adffe), ID($aldff), ID($aldffe), ID($sdff), ID($sdffe), ID($sdffce), ID($fsm), ID($memrd), ID($memrd_v2), ID($memwr), ID($memwr_v2)))
handle_polarity_inv(cell, ID::CLK, ID::CLK_POLARITY, assign_map, invert_map);
if (cell->type.in(ID($sr), ID($dffsr), ID($dffsre), ID($dlatchsr))) {
@@ -478,10 +452,13 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
if (cell->type.in(ID($adff), ID($adffe), ID($adlatch)))
handle_polarity_inv(cell, ID::ARST, ID::ARST_POLARITY, assign_map, invert_map);
+ if (cell->type.in(ID($aldff), ID($aldffe)))
+ handle_polarity_inv(cell, ID::ALOAD, ID::ALOAD_POLARITY, assign_map, invert_map);
+
if (cell->type.in(ID($sdff), ID($sdffe), ID($sdffce)))
handle_polarity_inv(cell, ID::SRST, ID::SRST_POLARITY, assign_map, invert_map);
- if (cell->type.in(ID($dffe), ID($adffe), ID($sdffe), ID($sdffce), ID($dffsre), ID($dlatch), ID($adlatch), ID($dlatchsr)))
+ if (cell->type.in(ID($dffe), ID($adffe), ID($aldffe), ID($sdffe), ID($sdffce), ID($dffsre), ID($dlatch), ID($adlatch), ID($dlatchsr)))
handle_polarity_inv(cell, ID::EN, ID::EN_POLARITY, assign_map, invert_map);
handle_clkpol_celltype_swap(cell, "$_SR_N?_", "$_SR_P?_", ID::S, assign_map, invert_map);
@@ -510,6 +487,13 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
handle_clkpol_celltype_swap(cell, "$_SDFFCE_?N??_", "$_SDFFCE_?P??_", ID::R, assign_map, invert_map);
handle_clkpol_celltype_swap(cell, "$_SDFFCE_???N_", "$_SDFFCE_???P_", ID::E, assign_map, invert_map);
+ handle_clkpol_celltype_swap(cell, "$_ALDFF_N?_", "$_ALDFF_P?_", ID::C, assign_map, invert_map);
+ handle_clkpol_celltype_swap(cell, "$_ALDFF_?N_", "$_ALDFF_?P_", ID::L, assign_map, invert_map);
+
+ handle_clkpol_celltype_swap(cell, "$_ALDFFE_N??_", "$_ALDFFE_P??_", ID::C, assign_map, invert_map);
+ handle_clkpol_celltype_swap(cell, "$_ALDFFE_?N?_", "$_ALDFFE_?P?_", ID::L, assign_map, invert_map);
+ handle_clkpol_celltype_swap(cell, "$_ALDFFE_??N_", "$_ALDFFE_??P_", ID::E, assign_map, invert_map);
+
handle_clkpol_celltype_swap(cell, "$_DFFSR_N??_", "$_DFFSR_P??_", ID::C, assign_map, invert_map);
handle_clkpol_celltype_swap(cell, "$_DFFSR_?N?_", "$_DFFSR_?P?_", ID::S, assign_map, invert_map);
handle_clkpol_celltype_swap(cell, "$_DFFSR_??N_", "$_DFFSR_??P_", ID::R, assign_map, invert_map);
@@ -604,7 +588,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
if (cell->type.in(ID($xnor), ID($_XNOR_))) {
cover("opt.opt_expr.const_xnor");
// For consistency since simplemap does $xnor -> $_XOR_ + $_NOT_
- int width = cell->getParam(ID::Y_WIDTH).as_int();
+ int width = GetSize(cell->getPort(ID::Y));
replace_cell(assign_map, module, cell, "const_xnor", ID::Y, SigSpec(RTLIL::State::S1, width));
goto next_cell;
}
@@ -1526,14 +1510,12 @@ skip_identity:
RTLIL::SigSpec sig_b = assign_map(cell->getPort(ID::B));
RTLIL::SigSpec sig_y = assign_map(cell->getPort(ID::Y));
- if (sig_b.is_fully_const() && sig_b.size() <= 32)
+ if (sig_b.is_fully_const())
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)
+ if (sig_a.is_fully_def())
{
- int a_val = sig_a.as_int();
-
- if (a_val == 0)
+ if (sig_a.is_fully_zero())
{
cover("opt.opt_expr.mul_shift.zero");
@@ -1547,37 +1529,34 @@ skip_identity:
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_expr.mul_shift.swapped");
- else
- cover("opt.opt_expr.mul_shift.unswapped");
-
- log_debug("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);
+ int exp;
+ if (sig_a.is_onehot(&exp) && !(a_signed && exp == GetSize(sig_a) - 1))
+ {
+ if (swapped_ab)
+ cover("opt.opt_expr.mul_shift.swapped");
+ else
+ cover("opt.opt_expr.mul_shift.unswapped");
- if (!swapped_ab) {
- cell->setPort(ID::A, cell->getPort(ID::B));
- cell->parameters.at(ID::A_WIDTH) = cell->parameters.at(ID::B_WIDTH);
- cell->parameters.at(ID::A_SIGNED) = cell->parameters.at(ID::B_SIGNED);
- }
+ log_debug("Replacing multiply-by-%s cell `%s' in module `%s' with shift-by-%d.\n",
+ log_signal(sig_a), cell->name.c_str(), module->name.c_str(), exp);
- std::vector<RTLIL::SigBit> new_b = RTLIL::SigSpec(i, 6);
+ if (!swapped_ab) {
+ cell->setPort(ID::A, cell->getPort(ID::B));
+ cell->parameters.at(ID::A_WIDTH) = cell->parameters.at(ID::B_WIDTH);
+ cell->parameters.at(ID::A_SIGNED) = cell->parameters.at(ID::B_SIGNED);
+ }
- while (GetSize(new_b) > 1 && new_b.back() == RTLIL::State::S0)
- new_b.pop_back();
+ Const new_b = exp;
- cell->type = ID($shl);
- cell->parameters[ID::B_WIDTH] = GetSize(new_b);
- cell->parameters[ID::B_SIGNED] = false;
- cell->setPort(ID::B, new_b);
- cell->check();
+ cell->type = ID($shl);
+ cell->parameters[ID::B_WIDTH] = GetSize(new_b);
+ cell->parameters[ID::B_SIGNED] = false;
+ cell->setPort(ID::B, new_b);
+ cell->check();
- did_something = true;
- goto next_cell;
- }
+ did_something = true;
+ goto next_cell;
+ }
}
sig_a = assign_map(cell->getPort(ID::A));
@@ -1596,6 +1575,14 @@ skip_identity:
log_debug("Removing low %d A and %d B bits from cell `%s' in module `%s'.\n",
a_zeros, b_zeros, cell->name.c_str(), module->name.c_str());
+ if (y_zeros >= GetSize(sig_y)) {
+ module->connect(sig_y, RTLIL::SigSpec(0, GetSize(sig_y)));
+ module->remove(cell);
+
+ did_something = true;
+ goto next_cell;
+ }
+
if (a_zeros) {
cell->setPort(ID::A, sig_a.extract_end(a_zeros));
cell->parameters[ID::A_WIDTH] = GetSize(sig_a) - a_zeros;
@@ -1614,7 +1601,7 @@ skip_identity:
}
}
- if (!keepdc && cell->type.in(ID($div), ID($mod), ID($divfloor), ID($modfloor)))
+ if (cell->type.in(ID($div), ID($mod), ID($divfloor), ID($modfloor)))
{
bool a_signed = cell->parameters[ID::A_SIGNED].as_bool();
bool b_signed = cell->parameters[ID::B_SIGNED].as_bool();
@@ -1622,11 +1609,9 @@ skip_identity:
SigSpec sig_b = assign_map(cell->getPort(ID::B));
SigSpec sig_y = assign_map(cell->getPort(ID::Y));
- if (sig_b.is_fully_def() && sig_b.size() <= 32)
+ if (sig_b.is_fully_def())
{
- int b_val = sig_b.as_int();
-
- if (b_val == 0)
+ if (sig_b.is_fully_zero())
{
cover("opt.opt_expr.divmod_zero");
@@ -1640,86 +1625,79 @@ skip_identity:
goto next_cell;
}
- for (int i = 1; i < (b_signed ? sig_b.size()-1 : sig_b.size()); i++)
- if (b_val == (1 << i))
+ int exp;
+ if (!keepdc && sig_b.is_onehot(&exp) && !(b_signed && exp == GetSize(sig_b) - 1))
+ {
+ if (cell->type.in(ID($div), ID($divfloor)))
{
- if (cell->type.in(ID($div), ID($divfloor)))
- {
- cover("opt.opt_expr.div_shift");
+ cover("opt.opt_expr.div_shift");
- bool is_truncating = cell->type == ID($div);
- log_debug("Replacing %s-divide-by-%d cell `%s' in module `%s' with shift-by-%d.\n",
- is_truncating ? "truncating" : "flooring",
- b_val, cell->name.c_str(), module->name.c_str(), i);
+ bool is_truncating = cell->type == ID($div);
+ log_debug("Replacing %s-divide-by-%s cell `%s' in module `%s' with shift-by-%d.\n",
+ is_truncating ? "truncating" : "flooring",
+ log_signal(sig_b), cell->name.c_str(), module->name.c_str(), exp);
- std::vector<RTLIL::SigBit> new_b = RTLIL::SigSpec(i, 6);
+ Const new_b = exp;
- while (GetSize(new_b) > 1 && new_b.back() == RTLIL::State::S0)
- new_b.pop_back();
-
- cell->type = ID($sshr);
- cell->parameters[ID::B_WIDTH] = GetSize(new_b);
- cell->parameters[ID::B_SIGNED] = false;
- cell->setPort(ID::B, new_b);
+ cell->type = ID($sshr);
+ cell->parameters[ID::B_WIDTH] = GetSize(new_b);
+ cell->parameters[ID::B_SIGNED] = false;
+ cell->setPort(ID::B, new_b);
- // Truncating division is the same as flooring division, except when
- // the result is negative and there is a remainder - then trunc = floor + 1
- if (is_truncating && a_signed) {
- Wire *flooring = module->addWire(NEW_ID, sig_y.size());
- cell->setPort(ID::Y, flooring);
-
- Wire *result_neg = module->addWire(NEW_ID);
- module->addXor(NEW_ID, sig_a[sig_a.size()-1], sig_b[sig_b.size()-1], result_neg);
- Wire *rem_nonzero = module->addWire(NEW_ID);
- module->addReduceOr(NEW_ID, sig_a.extract(0, i), rem_nonzero);
- Wire *should_add = module->addWire(NEW_ID);
- module->addAnd(NEW_ID, result_neg, rem_nonzero, should_add);
- module->addAdd(NEW_ID, flooring, should_add, sig_y);
- }
+ // Truncating division is the same as flooring division, except when
+ // the result is negative and there is a remainder - then trunc = floor + 1
+ if (is_truncating && a_signed && GetSize(sig_a) != 0 && exp != 0) {
+ Wire *flooring = module->addWire(NEW_ID, sig_y.size());
+ cell->setPort(ID::Y, flooring);
- cell->check();
+ SigSpec a_sign = sig_a[sig_a.size()-1];
+ SigSpec rem_nonzero = module->ReduceOr(NEW_ID, sig_a.extract(0, exp));
+ SigSpec should_add = module->And(NEW_ID, a_sign, rem_nonzero);
+ module->addAdd(NEW_ID, flooring, should_add, sig_y);
}
- else if (cell->type.in(ID($mod), ID($modfloor)))
+
+ cell->check();
+ }
+ else if (cell->type.in(ID($mod), ID($modfloor)))
+ {
+ cover("opt.opt_expr.mod_mask");
+
+ bool is_truncating = cell->type == ID($mod);
+ log_debug("Replacing %s-modulo-by-%s cell `%s' in module `%s' with bitmask.\n",
+ is_truncating ? "truncating" : "flooring",
+ log_signal(sig_b), cell->name.c_str(), module->name.c_str());
+
+ // truncating modulo has the same masked bits as flooring modulo, but
+ // the sign bits are those of A (except when R=0)
+ if (is_truncating && a_signed && GetSize(sig_a) != 0 && exp != 0)
{
- cover("opt.opt_expr.mod_mask");
+ module->remove(cell);
+ SigSpec truncating = sig_a.extract(0, exp);
- bool is_truncating = cell->type == ID($mod);
- log_debug("Replacing %s-modulo-by-%d cell `%s' in module `%s' with bitmask.\n",
- is_truncating ? "truncating" : "flooring",
- b_val, cell->name.c_str(), module->name.c_str());
+ SigSpec a_sign = sig_a[sig_a.size()-1];
+ SigSpec rem_nonzero = module->ReduceOr(NEW_ID, sig_a.extract(0, exp));
+ SigSpec extend_bit = module->And(NEW_ID, a_sign, rem_nonzero);
- std::vector<RTLIL::SigBit> new_b = RTLIL::SigSpec(State::S1, i);
+ truncating.append(extend_bit);
+ module->addPos(NEW_ID, truncating, sig_y, true);
+ }
+ else
+ {
+ std::vector<RTLIL::SigBit> new_b = RTLIL::SigSpec(State::S1, exp);
- if (b_signed)
+ if (b_signed || exp == 0)
new_b.push_back(State::S0);
cell->type = ID($and);
cell->parameters[ID::B_WIDTH] = GetSize(new_b);
cell->setPort(ID::B, new_b);
-
- // truncating modulo has the same masked bits as flooring modulo, but
- // the sign bits are those of A (except when R=0)
- if (is_truncating && a_signed) {
- Wire *flooring = module->addWire(NEW_ID, sig_y.size());
- cell->setPort(ID::Y, flooring);
- SigSpec truncating = SigSpec(flooring).extract(0, i);
-
- Wire *rem_nonzero = module->addWire(NEW_ID);
- module->addReduceOr(NEW_ID, truncating, rem_nonzero);
- SigSpec a_sign = sig_a[sig_a.size()-1];
- Wire *extend_bit = module->addWire(NEW_ID);
- module->addAnd(NEW_ID, a_sign, rem_nonzero, extend_bit);
-
- truncating.append(extend_bit);
- module->addPos(NEW_ID, truncating, sig_y, true);
- }
-
cell->check();
}
-
- did_something = true;
- goto next_cell;
}
+
+ did_something = true;
+ goto next_cell;
+ }
}
}
@@ -1949,8 +1927,8 @@ skip_alu_split:
replace = true;
}
- int const_bit_hot = get_onehot_bit_index(const_sig);
- if (const_bit_hot >= 0 && const_bit_hot < var_width)
+ int const_bit_hot;
+ if (const_sig.is_onehot(&const_bit_hot) && const_bit_hot < var_width)
{
RTLIL::SigSpec var_high_sig(RTLIL::State::S0, var_width - const_bit_hot);
for (int i = const_bit_hot; i < var_width; i++) {
@@ -2036,6 +2014,23 @@ skip_alu_split:
}
}
+void replace_const_connections(RTLIL::Module *module) {
+ SigMap assign_map(module);
+ for (auto cell : module->selected_cells())
+ {
+ std::vector<std::pair<RTLIL::IdString, SigSpec>> changes;
+ for (auto &conn : cell->connections()) {
+ SigSpec mapped = assign_map(conn.second);
+ if (conn.second != mapped && mapped.is_fully_const())
+ changes.push_back({conn.first, mapped});
+ }
+ if (!changes.empty())
+ did_something = true;
+ for (auto &it : changes)
+ cell->setPort(it.first, it.second);
+ }
+}
+
struct OptExprPass : public Pass {
OptExprPass() : Pass("opt_expr", "perform const folding and simple expression rewriting") { }
void help() override
@@ -2056,8 +2051,8 @@ struct OptExprPass : public Pass {
log(" -undriven\n");
log(" replace undriven nets with undef (x) constants\n");
log("\n");
- log(" -clkinv\n");
- log(" optimize clock inverters by changing FF types\n");
+ log(" -noclkinv\n");
+ log(" do not optimize clock inverters by changing FF types\n");
log("\n");
log(" -fine\n");
log(" perform fine-grain optimizations\n");
@@ -2077,7 +2072,7 @@ struct OptExprPass : public Pass {
bool mux_undef = false;
bool mux_bool = false;
bool undriven = false;
- bool clkinv = false;
+ bool noclkinv = false;
bool do_fine = false;
bool keepdc = false;
@@ -2098,8 +2093,8 @@ struct OptExprPass : public Pass {
undriven = true;
continue;
}
- if (args[argidx] == "-clkinv") {
- clkinv = true;
+ if (args[argidx] == "-noclkinv") {
+ noclkinv = true;
continue;
}
if (args[argidx] == "-fine") {
@@ -2136,16 +2131,21 @@ struct OptExprPass : public Pass {
do {
do {
did_something = false;
- replace_const_cells(design, module, false /* consume_x */, mux_undef, mux_bool, do_fine, keepdc, clkinv);
+ replace_const_cells(design, module, false /* consume_x */, mux_undef, mux_bool, do_fine, keepdc, noclkinv);
if (did_something)
design->scratchpad_set_bool("opt.did_something", true);
} while (did_something);
if (!keepdc)
- replace_const_cells(design, module, true /* consume_x */, mux_undef, mux_bool, do_fine, keepdc, clkinv);
+ replace_const_cells(design, module, true /* consume_x */, mux_undef, mux_bool, do_fine, keepdc, noclkinv);
if (did_something)
design->scratchpad_set_bool("opt.did_something", true);
} while (did_something);
+ did_something = false;
+ replace_const_connections(module);
+ if (did_something)
+ design->scratchpad_set_bool("opt.did_something", true);
+
log_suppressed();
}
diff --git a/passes/opt/opt_lut.cc b/passes/opt/opt_lut.cc
index 07a91af8a..3b079d964 100644
--- a/passes/opt/opt_lut.cc
+++ b/passes/opt/opt_lut.cc
@@ -24,16 +24,22 @@
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
+struct dlogic_t {
+ IdString cell_type;
+ // LUT input idx -> hard cell's port name
+ dict<int, IdString> lut_input_port;
+};
+
struct OptLutWorker
{
- dict<IdString, dict<int, IdString>> &dlogic;
+ const std::vector<dlogic_t> &dlogic;
RTLIL::Module *module;
ModIndex index;
SigMap sigmap;
pool<RTLIL::Cell*> luts;
dict<RTLIL::Cell*, int> luts_arity;
- dict<RTLIL::Cell*, pool<RTLIL::Cell*>> luts_dlogics;
+ dict<RTLIL::Cell*, pool<std::pair<int, RTLIL::Cell*>>> luts_dlogics;
dict<RTLIL::Cell*, pool<int>> luts_dlogic_inputs;
int eliminated_count = 0, combined_count = 0;
@@ -64,7 +70,7 @@ struct OptLutWorker
void show_stats_by_arity()
{
dict<int, int> arity_counts;
- dict<IdString, int> dlogic_counts;
+ std::vector<int> dlogic_counts(dlogic.size());
int max_arity = 0;
for (auto lut_arity : luts_arity)
@@ -77,7 +83,7 @@ struct OptLutWorker
{
for (auto &lut_dlogic : lut_dlogics.second)
{
- dlogic_counts[lut_dlogic->type]++;
+ dlogic_counts[lut_dlogic.first]++;
}
}
@@ -87,13 +93,13 @@ struct OptLutWorker
if (arity_counts[arity])
log(" %d-LUT %16d\n", arity, arity_counts[arity]);
}
- for (auto &dlogic_count : dlogic_counts)
+ for (int i = 0; i < GetSize(dlogic); i++)
{
- log(" with %-12s %4d\n", dlogic_count.first.c_str(), dlogic_count.second);
+ log(" with %-12s (#%d) %4d\n", dlogic[i].cell_type.c_str(), i, dlogic_counts[i]);
}
}
- OptLutWorker(dict<IdString, dict<int, IdString>> &dlogic, RTLIL::Module *module, int limit) :
+ OptLutWorker(const std::vector<dlogic_t> &dlogic, RTLIL::Module *module, int limit) :
dlogic(dlogic), module(module), index(module), sigmap(module)
{
log("Discovering LUTs.\n");
@@ -116,20 +122,19 @@ struct OptLutWorker
// First, find all dedicated logic we're connected to. This results in an overapproximation
// of such connections.
- pool<RTLIL::Cell*> lut_all_dlogics;
+ pool<std::pair<int, RTLIL::Cell*>> lut_all_dlogics;
for (int i = 0; i < lut_width; i++)
{
SigBit bit = lut_input[i];
for (auto &port : index.query_ports(bit))
{
- if (dlogic.count(port.cell->type))
+ for (int j = 0; j < GetSize(dlogic); j++)
{
- auto &dlogic_map = dlogic[port.cell->type];
- if (dlogic_map.count(i))
+ if (dlogic[j].cell_type == port.cell->type)
{
- if (port.port == dlogic_map[i])
+ if (port.port == dlogic[j].lut_input_port.at(i, IdString()))
{
- lut_all_dlogics.insert(port.cell);
+ lut_all_dlogics.insert({j, port.cell});
}
}
}
@@ -143,25 +148,25 @@ struct OptLutWorker
// * The connection is illegal.
// In either of these cases, we don't need to concern ourselves with preserving the connection
// between this LUT and this dedicated logic cell.
- pool<RTLIL::Cell*> lut_legal_dlogics;
+ pool<std::pair<int, RTLIL::Cell*>> lut_legal_dlogics;
pool<int> lut_dlogic_inputs;
for (auto lut_dlogic : lut_all_dlogics)
{
- auto &dlogic_map = dlogic[lut_dlogic->type];
+ auto &dlogic_map = dlogic[lut_dlogic.first].lut_input_port;
bool legal = true;
for (auto &dlogic_conn : dlogic_map)
{
if (lut_width <= dlogic_conn.first)
{
- log_debug(" LUT has illegal connection to %s cell %s.%s.\n", lut_dlogic->type.c_str(), log_id(module), log_id(lut_dlogic));
+ log_debug(" LUT has illegal connection to %s cell %s.%s.\n", lut_dlogic.second->type.c_str(), log_id(module), log_id(lut_dlogic.second));
log_debug(" LUT input A[%d] not present.\n", dlogic_conn.first);
legal = false;
break;
}
- if (sigmap(lut_input[dlogic_conn.first]) != sigmap(lut_dlogic->getPort(dlogic_conn.second)))
+ if (sigmap(lut_input[dlogic_conn.first]) != sigmap(lut_dlogic.second->getPort(dlogic_conn.second)))
{
- log_debug(" LUT has illegal connection to %s cell %s.%s.\n", lut_dlogic->type.c_str(), log_id(module), log_id(lut_dlogic));
- log_debug(" LUT input A[%d] (wire %s) not connected to %s port %s (wire %s).\n", dlogic_conn.first, log_signal(lut_input[dlogic_conn.first]), lut_dlogic->type.c_str(), dlogic_conn.second.c_str(), log_signal(lut_dlogic->getPort(dlogic_conn.second)));
+ log_debug(" LUT has illegal connection to %s cell %s.%s.\n", lut_dlogic.second->type.c_str(), log_id(module), log_id(lut_dlogic.second));
+ log_debug(" LUT input A[%d] (wire %s) not connected to %s port %s (wire %s).\n", dlogic_conn.first, log_signal(lut_input[dlogic_conn.first]), lut_dlogic.second->type.c_str(), dlogic_conn.second.c_str(), log_signal(lut_dlogic.second->getPort(dlogic_conn.second)));
legal = false;
break;
}
@@ -169,7 +174,7 @@ struct OptLutWorker
if (legal)
{
- log_debug(" LUT has legal connection to %s cell %s.%s.\n", lut_dlogic->type.c_str(), log_id(module), log_id(lut_dlogic));
+ log_debug(" LUT has legal connection to %s cell %s.%s.\n", lut_dlogic.second->type.c_str(), log_id(module), log_id(lut_dlogic.second));
lut_legal_dlogics.insert(lut_dlogic);
for (auto &dlogic_conn : dlogic_map)
lut_dlogic_inputs.insert(dlogic_conn.first);
@@ -277,12 +282,13 @@ struct OptLutWorker
module->connect(lut_output, value);
sigmap.add(lut_output, value);
- module->remove(lut);
luts.erase(lut);
luts_arity.erase(lut);
luts_dlogics.erase(lut);
luts_dlogic_inputs.erase(lut);
+ module->remove(lut);
+
eliminated_count++;
if (limit > 0)
limit--;
@@ -493,11 +499,12 @@ struct OptLutWorker
luts_arity[lutM] = lutM_arity;
luts.erase(lutR);
luts_arity.erase(lutR);
- lutR->module->remove(lutR);
worklist.insert(lutM);
worklist.erase(lutR);
+ lutR->module->remove(lutR);
+
combined_count++;
if (limit > 0)
limit--;
@@ -542,7 +549,7 @@ struct OptLutPass : public Pass {
{
log_header(design, "Executing OPT_LUT pass (optimize LUTs).\n");
- dict<IdString, dict<int, IdString>> dlogic;
+ std::vector<dlogic_t> dlogic;
int limit = -1;
size_t argidx;
@@ -554,7 +561,8 @@ struct OptLutPass : public Pass {
split(tokens, args[++argidx], ':');
if (tokens.size() < 2)
log_cmd_error("The -dlogic option requires at least one connection.\n");
- IdString type = "\\" + tokens[0];
+ dlogic_t entry;
+ entry.cell_type = "\\" + tokens[0];
for (auto it = tokens.begin() + 1; it != tokens.end(); ++it) {
std::vector<std::string> conn_tokens;
split(conn_tokens, *it, '=');
@@ -562,8 +570,9 @@ struct OptLutPass : public Pass {
log_cmd_error("Invalid format of -dlogic signal mapping.\n");
IdString logic_port = "\\" + conn_tokens[0];
int lut_input = atoi(conn_tokens[1].c_str());
- dlogic[type][lut_input] = logic_port;
+ entry.lut_input_port[lut_input] = logic_port;
}
+ dlogic.push_back(entry);
continue;
}
if (args[argidx] == "-limit" && argidx + 1 < args.size())
diff --git a/passes/opt/opt_lut_ins.cc b/passes/opt/opt_lut_ins.cc
index bb40e1e55..2f7c392b2 100644
--- a/passes/opt/opt_lut_ins.cc
+++ b/passes/opt/opt_lut_ins.cc
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -193,6 +193,12 @@ struct OptLutInsPass : public Pass {
swz += extra;
}
}
+ if (techname == "gowin") {
+ // Pad the LUT to 1 input, adding consts from the front.
+ if (new_inputs.empty()) {
+ new_inputs.insert(new_inputs.begin(), State::S0);
+ }
+ }
Const new_lut(0, 1 << GetSize(new_inputs));
for (int i = 0; i < GetSize(new_lut); i++) {
int lidx = 0;
@@ -209,9 +215,9 @@ struct OptLutInsPass : public Pass {
}
new_lut[i] = lut[lidx];
}
- // For ecp5, do not replace with a const driver — the nextpnr
+ // For ecp5, and gowin do not replace with a const driver — the nextpnr
// packer requires a complete set of LUTs for wide LUT muxes.
- if (new_inputs.empty() && techname != "ecp5") {
+ if (new_inputs.empty() && techname != "ecp5" && techname != "gowin") {
// const driver.
remove_cells.push_back(cell);
module->connect(output, new_lut[0]);
diff --git a/passes/opt/opt_mem.cc b/passes/opt/opt_mem.cc
index 24df1356b..edadf2c7b 100644
--- a/passes/opt/opt_mem.cc
+++ b/passes/opt/opt_mem.cc
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -19,82 +19,11 @@
#include "kernel/yosys.h"
#include "kernel/sigtools.h"
+#include "kernel/mem.h"
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
-struct OptMemWorker
-{
- RTLIL::Design *design;
- RTLIL::Module *module;
- SigMap sigmap;
- bool restart;
-
- dict<IdString, vector<IdString>> memrd, memwr, meminit;
- pool<IdString> remove_mem, remove_cells;
-
- OptMemWorker(RTLIL::Module *module) : design(module->design), module(module), sigmap(module), restart(false)
- {
- for (auto &it : module->memories)
- {
- memrd[it.first];
- memwr[it.first];
- meminit[it.first];
- }
-
- for (auto cell : module->cells())
- {
- if (cell->type == ID($memrd)) {
- IdString id = cell->getParam(ID::MEMID).decode_string();
- memrd.at(id).push_back(cell->name);
- }
-
- if (cell->type == ID($memwr)) {
- IdString id = cell->getParam(ID::MEMID).decode_string();
- memwr.at(id).push_back(cell->name);
- }
-
- if (cell->type == ID($meminit)) {
- IdString id = cell->getParam(ID::MEMID).decode_string();
- meminit.at(id).push_back(cell->name);
- }
- }
- }
-
- ~OptMemWorker()
- {
- for (auto it : remove_mem)
- {
- for (auto cell_name : memrd[it])
- module->remove(module->cell(cell_name));
- for (auto cell_name : memwr[it])
- module->remove(module->cell(cell_name));
- for (auto cell_name : meminit[it])
- module->remove(module->cell(cell_name));
-
- delete module->memories.at(it);
- module->memories.erase(it);
- }
-
- for (auto cell_name : remove_cells)
- module->remove(module->cell(cell_name));
- }
-
- int run(RTLIL::Memory *mem)
- {
- if (restart || remove_mem.count(mem->name))
- return 0;
-
- if (memwr.at(mem->name).empty() && meminit.at(mem->name).empty()) {
- log("Removing memory %s.%s with no write ports or init data.\n", log_id(module), log_id(mem));
- remove_mem.insert(mem->name);
- return 1;
- }
-
- return 0;
- }
-};
-
struct OptMemPass : public Pass {
OptMemPass() : Pass("opt_mem", "optimize memories") { }
void help() override
@@ -122,15 +51,35 @@ struct OptMemPass : public Pass {
int total_count = 0;
for (auto module : design->selected_modules()) {
- while (1) {
- int cnt = 0;
- OptMemWorker worker(module);
- for (auto &it : module->memories)
- if (module->selected(it.second))
- cnt += worker.run(it.second);
- if (!cnt && !worker.restart)
- break;
- total_count += cnt;
+ SigMap sigmap(module);
+ FfInitVals initvals(&sigmap, module);
+ for (auto &mem : Mem::get_selected_memories(module)) {
+ bool changed = false;
+ for (auto &port : mem.wr_ports) {
+ if (port.en.is_fully_zero()) {
+ port.removed = true;
+ changed = true;
+ total_count++;
+ }
+ }
+ if (changed) {
+ mem.emit();
+ }
+
+ if (mem.wr_ports.empty() && mem.inits.empty()) {
+ // The whole memory array will contain
+ // only State::Sx, but the embedded read
+ // registers could have reset or init values.
+ // They will probably be optimized away by
+ // opt_dff later.
+ for (int i = 0; i < GetSize(mem.rd_ports); i++) {
+ mem.extract_rdff(i, &initvals);
+ auto &port = mem.rd_ports[i];
+ module->connect(port.data, Const(State::Sx, GetSize(port.data)));
+ }
+ mem.remove();
+ total_count++;
+ }
}
}
diff --git a/passes/opt/opt_mem_feedback.cc b/passes/opt/opt_mem_feedback.cc
new file mode 100644
index 000000000..20a2a79ed
--- /dev/null
+++ b/passes/opt/opt_mem_feedback.cc
@@ -0,0 +1,350 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
+ *
+ * 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/mem.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+// Describes found feedback path.
+struct FeedbackPath {
+ // Which write port it is.
+ int wrport_idx;
+ // Which data bit of that write port it is.
+ int data_bit_idx;
+ // Values of all mux select signals that need to be set to select this path.
+ dict<RTLIL::SigBit, bool> condition;
+ // The exact feedback bit used (used to match read port).
+ SigBit feedback_bit;
+
+ FeedbackPath(int wrport_idx, int data_bit_idx, dict<RTLIL::SigBit, bool> condition, SigBit feedback_bit) : wrport_idx(wrport_idx), data_bit_idx(data_bit_idx), condition(condition), feedback_bit(feedback_bit) {}
+};
+
+struct OptMemFeedbackWorker
+{
+ RTLIL::Design *design;
+ RTLIL::Module *module;
+ SigMap sigmap, sigmap_xmux;
+ FfInitVals initvals;
+
+ dict<RTLIL::SigBit, std::pair<RTLIL::Cell*, int>> sig_to_mux;
+ dict<RTLIL::SigBit, int> sig_users_count;
+ dict<pair<pool<dict<SigBit, bool>>, SigBit>, SigBit> conditions_logic_cache;
+
+
+ // -----------------------------------------------------------------
+ // Converting feedbacks to async read ports to proper enable signals
+ // -----------------------------------------------------------------
+
+ void find_data_feedback(const pool<RTLIL::SigBit> &async_rd_bits, RTLIL::SigBit sig,
+ const dict<RTLIL::SigBit, bool> &state,
+ int wrport_idx, int data_bit_idx,
+ std::vector<FeedbackPath> &paths)
+ {
+ if (async_rd_bits.count(sig)) {
+ paths.push_back(FeedbackPath(wrport_idx, data_bit_idx, state, sig));
+ return;
+ }
+
+ if (sig_users_count[sig] != 1) {
+ // Only descend into muxes if we're the only user.
+ return;
+ }
+
+ if (sig_to_mux.count(sig) == 0)
+ return;
+
+ RTLIL::Cell *cell = sig_to_mux.at(sig).first;
+ int bit_idx = sig_to_mux.at(sig).second;
+
+ std::vector<RTLIL::SigBit> sig_a = sigmap(cell->getPort(ID::A));
+ std::vector<RTLIL::SigBit> sig_b = sigmap(cell->getPort(ID::B));
+ std::vector<RTLIL::SigBit> sig_s = sigmap(cell->getPort(ID::S));
+ std::vector<RTLIL::SigBit> sig_y = sigmap(cell->getPort(ID::Y));
+ log_assert(sig_y.at(bit_idx) == sig);
+
+ for (int i = 0; i < GetSize(sig_s); i++)
+ if (state.count(sig_s[i]) && state.at(sig_s[i]) == true) {
+ find_data_feedback(async_rd_bits, sig_b.at(bit_idx + i*sig_y.size()), state, wrport_idx, data_bit_idx, paths);
+ return;
+ }
+
+
+ for (int i = 0; i < GetSize(sig_s); i++)
+ {
+ if (state.count(sig_s[i]) && state.at(sig_s[i]) == false)
+ continue;
+
+ dict<RTLIL::SigBit, bool> new_state = state;
+ new_state[sig_s[i]] = true;
+
+ find_data_feedback(async_rd_bits, sig_b.at(bit_idx + i*sig_y.size()), new_state, wrport_idx, data_bit_idx, paths);
+ }
+
+ dict<RTLIL::SigBit, bool> new_state = state;
+ for (auto bit : sig_s)
+ new_state[bit] = false;
+
+ find_data_feedback(async_rd_bits, sig_a.at(bit_idx), new_state, wrport_idx, data_bit_idx, paths);
+ }
+
+ RTLIL::SigBit conditions_to_logic(pool<dict<RTLIL::SigBit, bool>> &conditions, SigBit olden)
+ {
+ auto key = make_pair(conditions, olden);
+
+ if (conditions_logic_cache.count(key))
+ return conditions_logic_cache.at(key);
+
+ RTLIL::SigSpec terms;
+ for (auto &cond : conditions) {
+ RTLIL::SigSpec sig1, sig2;
+ for (auto &it : cond) {
+ sig1.append(it.first);
+ sig2.append(it.second ? RTLIL::State::S1 : RTLIL::State::S0);
+ }
+ terms.append(module->Ne(NEW_ID, sig1, sig2));
+ }
+
+ if (olden != State::S1)
+ terms.append(olden);
+
+ if (GetSize(terms) == 0)
+ terms = State::S1;
+
+ if (GetSize(terms) > 1)
+ terms = module->ReduceAnd(NEW_ID, terms);
+
+ return conditions_logic_cache[key] = terms;
+ }
+
+ void translate_rd_feedback_to_en(Mem &mem)
+ {
+ // Look for async read ports that may be suitable for feedback paths.
+ dict<RTLIL::SigSpec, std::vector<pool<RTLIL::SigBit>>> async_rd_bits;
+
+ for (auto &port : mem.rd_ports)
+ {
+ if (port.clk_enable)
+ continue;
+
+ for (int sub = 0; sub < (1 << port.wide_log2); sub++) {
+ SigSpec addr = sigmap_xmux(port.sub_addr(sub));
+ async_rd_bits[addr].resize(mem.width);
+ for (int i = 0; i < mem.width; i++)
+ async_rd_bits[addr][i].insert(sigmap(port.data[i + sub * mem.width]));
+ }
+ }
+
+ if (async_rd_bits.empty())
+ return;
+
+ // Look for actual feedback paths.
+ std::vector<FeedbackPath> paths;
+
+ for (int i = 0; i < GetSize(mem.wr_ports); i++)
+ {
+ auto &port = mem.wr_ports[i];
+
+ log(" Analyzing %s.%s write port %d.\n", log_id(module), log_id(mem.memid), i);
+
+ for (int sub = 0; sub < (1 << port.wide_log2); sub++)
+ {
+ SigSpec addr = sigmap_xmux(port.sub_addr(sub));
+
+ if (!async_rd_bits.count(addr))
+ continue;
+
+ for (int j = 0; j < mem.width; j++)
+ {
+ int bit_idx = sub * mem.width + j;
+
+ if (port.en[bit_idx] == State::S0)
+ continue;
+
+ dict<RTLIL::SigBit, bool> state;
+
+ find_data_feedback(async_rd_bits.at(addr).at(j), sigmap(port.data[bit_idx]), state, i, bit_idx, paths);
+ }
+ }
+ }
+
+ if (paths.empty())
+ return;
+
+ // Now determine which read ports are actually used only for
+ // feedback paths, and can be removed.
+
+ dict<SigBit, int> feedback_users_count;
+ for (auto &path : paths)
+ feedback_users_count[path.feedback_bit]++;
+
+ pool<SigBit> feedback_ok;
+ for (auto &port : mem.rd_ports)
+ {
+ if (port.clk_enable)
+ continue;
+
+ bool ok = true;
+ for (auto bit : sigmap(port.data))
+ if (sig_users_count[bit] != feedback_users_count[bit])
+ ok = false;
+
+ if (ok)
+ {
+ // This port is going bye-bye.
+ for (auto bit : sigmap(port.data))
+ feedback_ok.insert(bit);
+
+ port.removed = true;
+ }
+ }
+
+ if (feedback_ok.empty())
+ return;
+
+ // Prepare a feedback condition list grouped by port bits.
+
+ dict<std::pair<int, int>, pool<dict<SigBit, bool>>> portbit_conds;
+ for (auto &path : paths)
+ if (feedback_ok.count(path.feedback_bit))
+ portbit_conds[std::make_pair(path.wrport_idx, path.data_bit_idx)].insert(path.condition);
+
+ if (portbit_conds.empty())
+ return;
+
+ // Okay, let's do it.
+
+ log("Populating enable bits on write ports of memory %s.%s with async read feedback:\n", log_id(module), log_id(mem.memid));
+
+ // If a write port has a feedback path that we're about to bypass,
+ // but also has priority over some other write port, the feedback
+ // path is not necessarily a NOP — it may overwrite the other port.
+ // Emulate this effect by converting the priority to soft logic
+ // (this will affect the other port's enable signal).
+ for (auto &it : portbit_conds)
+ {
+ int wrport_idx = it.first.first;
+ auto &port = mem.wr_ports[wrport_idx];
+
+ for (int i = 0; i < wrport_idx; i++)
+ if (port.priority_mask[i])
+ mem.emulate_priority(i, wrport_idx, &initvals);
+ }
+
+ for (auto &it : portbit_conds)
+ {
+ int wrport_idx = it.first.first;
+ int bit = it.first.second;
+ auto &port = mem.wr_ports[wrport_idx];
+
+ port.en[bit] = conditions_to_logic(it.second, port.en[bit]);
+ log(" Port %d bit %d: added enable logic for %d different cases.\n", wrport_idx, bit, GetSize(it.second));
+ }
+
+ mem.emit();
+
+ for (auto bit : feedback_ok)
+ module->connect(bit, State::Sx);
+
+ design->scratchpad_set_bool("opt.did_something", true);
+ }
+
+ // -------------
+ // Setup and run
+ // -------------
+
+ OptMemFeedbackWorker(RTLIL::Design *design) : design(design) {}
+
+ void operator()(RTLIL::Module* module)
+ {
+ std::vector<Mem> memories = Mem::get_selected_memories(module);
+
+ this->module = module;
+ sigmap.set(module);
+ initvals.set(&sigmap, module);
+ sig_to_mux.clear();
+ conditions_logic_cache.clear();
+
+ sigmap_xmux = sigmap;
+
+ for (auto wire : module->wires()) {
+ if (wire->port_output)
+ for (auto bit : sigmap(wire))
+ sig_users_count[bit]++;
+ }
+
+ for (auto cell : module->cells())
+ {
+ if (cell->type == ID($mux))
+ {
+ RTLIL::SigSpec sig_a = sigmap_xmux(cell->getPort(ID::A));
+ RTLIL::SigSpec sig_b = sigmap_xmux(cell->getPort(ID::B));
+
+ if (sig_a.is_fully_undef())
+ sigmap_xmux.add(cell->getPort(ID::Y), sig_b);
+ else if (sig_b.is_fully_undef())
+ sigmap_xmux.add(cell->getPort(ID::Y), sig_a);
+ }
+
+ if (cell->type.in(ID($mux), ID($pmux)))
+ {
+ std::vector<RTLIL::SigBit> sig_y = sigmap(cell->getPort(ID::Y));
+ for (int i = 0; i < int(sig_y.size()); i++)
+ sig_to_mux[sig_y[i]] = std::pair<RTLIL::Cell*, int>(cell, i);
+ }
+
+ for (auto &conn : cell->connections())
+ if (!cell->known() || cell->input(conn.first))
+ for (auto bit : sigmap(conn.second))
+ sig_users_count[bit]++;
+ }
+
+ for (auto &mem : memories)
+ translate_rd_feedback_to_en(mem);
+ }
+};
+
+struct OptMemFeedbackPass : public Pass {
+ OptMemFeedbackPass() : Pass("opt_mem_feedback", "convert memory read-to-write port feedback paths to write enables") { }
+ void help() override
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" opt_mem_feedback [selection]\n");
+ log("\n");
+ log("This pass detects cases where an asynchronous read port is only connected via\n");
+ log("a mux tree to a write port with the same address. When such a connection is\n");
+ log("found, it is replaced with a new condition on an enable signal, allowing\n");
+ log("for removal of the read port.\n");
+ log("\n");
+ }
+ void execute(std::vector<std::string> args, RTLIL::Design *design) override {
+ log_header(design, "Executing OPT_MEM_FEEDBACK pass (finding memory read-to-write feedback paths).\n");
+ extra_args(args, 1, design);
+ OptMemFeedbackWorker worker(design);
+
+ for (auto module : design->selected_modules())
+ worker(module);
+ }
+} OptMemFeedbackPass;
+
+PRIVATE_NAMESPACE_END
+
diff --git a/passes/opt/opt_mem_priority.cc b/passes/opt/opt_mem_priority.cc
new file mode 100644
index 000000000..a9b145bea
--- /dev/null
+++ b/passes/opt/opt_mem_priority.cc
@@ -0,0 +1,109 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2021 Marcelina Kościelnicka <mwk@0x04.net>
+ *
+ * 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/modtools.h"
+#include "kernel/qcsat.h"
+#include "kernel/mem.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct OptMemPriorityPass : public Pass {
+ OptMemPriorityPass() : Pass("opt_mem_priority", "remove priority relations between write ports that can never collide") { }
+ void help() override
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" opt_mem_priority [selection]\n");
+ log("\n");
+ log("This pass detects cases where one memory write port has priority over another\n");
+ log("even though they can never collide with each other -- ie. there can never be\n");
+ log("a situation where a given memory bit is written by both ports at the same\n");
+ log("time, for example because of always-different addresses, or mutually exclusive\n");
+ log("enable signals. In such cases, the priority relation is removed.\n");
+ log("\n");
+ }
+ void execute(std::vector<std::string> args, RTLIL::Design *design) override {
+ log_header(design, "Executing OPT_MEM_PRIORITY pass (removing unnecessary memory write priority relations).\n");
+ extra_args(args, 1, design);
+
+ ModWalker modwalker(design);
+
+ int total_count = 0;
+ for (auto module : design->selected_modules()) {
+ modwalker.setup(module);
+ for (auto &mem : Mem::get_selected_memories(module)) {
+ bool mem_changed = false;
+ QuickConeSat qcsat(modwalker);
+ for (int i = 0; i < GetSize(mem.wr_ports); i++) {
+ auto &wport1 = mem.wr_ports[i];
+ for (int j = 0; j < GetSize(mem.wr_ports); j++) {
+ auto &wport2 = mem.wr_ports[j];
+ if (!wport1.priority_mask[j])
+ continue;
+ // No mixed width support — we could do it, but
+ // that would complicate code and wouldn't help
+ // anything since we run this pass before
+ // wide ports are created in normal flow.
+ if (wport1.wide_log2 != wport2.wide_log2)
+ continue;
+ // Two ports with priority, let's go.
+ pool<std::pair<SigBit, SigBit>> checked;
+ SigSpec addr1 = wport1.addr;
+ SigSpec addr2 = wport2.addr;
+ int abits = std::max(GetSize(addr1), GetSize(addr2));
+ addr1.extend_u0(abits);
+ addr2.extend_u0(abits);
+ int addr_eq = qcsat.ez->vec_eq(qcsat.importSig(addr1), qcsat.importSig(addr2));
+ bool ok = true;
+ for (int k = 0; k < GetSize(wport1.data); k++) {
+ SigBit wen1 = wport1.en[k];
+ SigBit wen2 = wport2.en[k];
+ if (checked.count({wen1, wen2}))
+ continue;
+ int wen1_sat = qcsat.importSigBit(wen1);
+ int wen2_sat = qcsat.importSigBit(wen2);
+ qcsat.prepare();
+ if (qcsat.ez->solve(wen1_sat, wen2_sat, addr_eq)) {
+ ok = false;
+ break;
+ }
+ checked.insert({wen1, wen2});
+ }
+ if (ok) {
+ total_count++;
+ mem_changed = true;
+ wport1.priority_mask[j] = false;
+ }
+ }
+ }
+ if (mem_changed)
+ mem.emit();
+ }
+ }
+
+ if (total_count)
+ design->scratchpad_set_bool("opt.did_something", true);
+ log("Performed a total of %d transformations.\n", total_count);
+ }
+} OptMemPriorityPass;
+
+PRIVATE_NAMESPACE_END
+
diff --git a/passes/opt/opt_mem_widen.cc b/passes/opt/opt_mem_widen.cc
new file mode 100644
index 000000000..95e01088c
--- /dev/null
+++ b/passes/opt/opt_mem_widen.cc
@@ -0,0 +1,107 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2021 Marcelina Kościelnicka <mwk@0x04.net>
+ *
+ * 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/mem.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct OptMemWidenPass : public Pass {
+ OptMemWidenPass() : Pass("opt_mem_widen", "optimize memories where all ports are wide") { }
+ void help() override
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" opt_mem_widen [options] [selection]\n");
+ log("\n");
+ log("This pass looks for memories where all ports are wide and adjusts the base\n");
+ log("memory width up until that stops being the case.\n");
+ log("\n");
+ }
+ void execute(std::vector<std::string> args, RTLIL::Design *design) override
+ {
+ log_header(design, "Executing OPT_MEM_WIDEN pass (optimize memories where all ports are wide).\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ // if (args[argidx] == "-nomux") {
+ // mode_nomux = true;
+ // continue;
+ // }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ int total_count = 0;
+ for (auto module : design->selected_modules()) {
+ for (auto &mem : Mem::get_selected_memories(module)) {
+ // If the memory has no read ports, opt_clean will remove it
+ // instead.
+ if (mem.rd_ports.empty())
+ continue;
+ int factor_log2 = mem.rd_ports[0].wide_log2;
+ for (auto &port : mem.rd_ports)
+ if (port.wide_log2 < factor_log2)
+ factor_log2 = port.wide_log2;
+ for (auto &port : mem.wr_ports)
+ if (port.wide_log2 < factor_log2)
+ factor_log2 = port.wide_log2;
+ if (factor_log2 == 0)
+ continue;
+ log("Widening base width of memory %s in module %s by factor %d.\n", log_id(mem.memid), log_id(module->name), 1 << factor_log2);
+ total_count++;
+ // The inits are too messy to expand one-by-one, for they may
+ // collide with one another after expansion. Just hit it with
+ // a hammer.
+ bool has_init = !mem.inits.empty();
+ Const init_data;
+ if (has_init) {
+ init_data = mem.get_init_data();
+ mem.clear_inits();
+ }
+ mem.width <<= factor_log2;
+ mem.size >>= factor_log2;
+ mem.start_offset >>= factor_log2;
+ if (has_init) {
+ MemInit new_init;
+ new_init.addr = mem.start_offset;
+ new_init.data = init_data;
+ new_init.en = Const(State::S1, mem.width);
+ mem.inits.push_back(new_init);
+ }
+ for (auto &port : mem.rd_ports) {
+ port.wide_log2 -= factor_log2;
+ port.addr = port.addr.extract_end(factor_log2);
+ }
+ for (auto &port : mem.wr_ports) {
+ port.wide_log2 -= factor_log2;
+ port.addr = port.addr.extract_end(factor_log2);
+ }
+ mem.emit();
+ }
+ }
+
+ if (total_count)
+ design->scratchpad_set_bool("opt.did_something", true);
+ log("Performed a total of %d transformations.\n", total_count);
+ }
+} OptMemWidenPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/opt/opt_merge.cc b/passes/opt/opt_merge.cc
index a95aa74c1..115eb97a9 100644
--- a/passes/opt/opt_merge.cc
+++ b/passes/opt/opt_merge.cc
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -18,6 +18,7 @@
*/
#include "kernel/register.h"
+#include "kernel/ffinit.h"
#include "kernel/sigtools.h"
#include "kernel/log.h"
#include "kernel/celltypes.h"
@@ -35,7 +36,7 @@ struct OptMergeWorker
RTLIL::Design *design;
RTLIL::Module *module;
SigMap assign_map;
- SigMap dff_init_map;
+ FfInitVals initvals;
bool mode_share_all;
CellTypes ct;
@@ -121,8 +122,7 @@ struct OptMergeWorker
if (it.first == ID::Q && RTLIL::builtin_ff_cell_types().count(cell->type)) {
// For the 'Q' output of state elements,
// use its (* init *) attribute value
- for (const auto &b : dff_init_map(it.second))
- sig.append(b.wire ? State::Sx : b);
+ sig = initvals(it.second);
}
else
continue;
@@ -173,17 +173,11 @@ struct OptMergeWorker
for (const auto &it : cell1->connections_) {
if (cell1->output(it.first)) {
- if (it.first == ID::Q && (cell1->type.begins_with("$dff") || cell1->type.begins_with("$dlatch") ||
- cell1->type.begins_with("$_DFF") || cell1->type.begins_with("$_DLATCH") || cell1->type.begins_with("$_SR_") ||
- cell1->type.in(ID($adff), ID($sr), ID($ff), ID($_FF_)))) {
+ if (it.first == ID::Q && RTLIL::builtin_ff_cell_types().count(cell1->type)) {
// For the 'Q' output of state elements,
// use the (* init *) attribute value
- auto &sig1 = conn1[it.first];
- for (const auto &b : dff_init_map(it.second))
- sig1.append(b.wire ? State::Sx : b);
- auto &sig2 = conn2[it.first];
- for (const auto &b : dff_init_map(cell2->getPort(it.first)))
- sig2.append(b.wire ? State::Sx : b);
+ conn1[it.first] = initvals(it.second);
+ conn2[it.first] = initvals(cell2->getPort(it.first));
}
else {
conn1[it.first] = RTLIL::SigSpec();
@@ -249,14 +243,7 @@ struct OptMergeWorker
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_)
- if (it.second->attributes.count(ID::init) != 0) {
- Const initval = it.second->attributes.at(ID::init);
- for (int i = 0; i < GetSize(initval) && i < GetSize(it.second); i++)
- if (initval[i] == State::S0 || initval[i] == State::S1)
- dff_init_map.add(SigBit(it.second, i), initval[i]);
- }
+ initvals.set(&assign_map, module);
bool did_something = true;
while (did_something)
@@ -295,21 +282,12 @@ struct OptMergeWorker
RTLIL::SigSpec other_sig = r.first->second->getPort(it.first);
log_debug(" Redirecting output %s: %s = %s\n", it.first.c_str(),
log_signal(it.second), log_signal(other_sig));
+ Const init = initvals(other_sig);
+ initvals.remove_init(it.second);
+ initvals.remove_init(other_sig);
module->connect(RTLIL::SigSig(it.second, other_sig));
assign_map.add(it.second, other_sig);
-
- if (it.first == ID::Q && (cell->type.begins_with("$dff") || cell->type.begins_with("$dlatch") ||
- cell->type.begins_with("$_DFF") || cell->type.begins_with("$_DLATCH") || cell->type.begins_with("$_SR_") ||
- cell->type.in(ID($adff), ID($sr), ID($ff), ID($_FF_)))) {
- for (auto c : it.second.chunks()) {
- auto jt = c.wire->attributes.find(ID::init);
- if (jt == c.wire->attributes.end())
- continue;
- for (int i = c.offset; i < c.offset + c.width; i++)
- jt->second[i] = State::Sx;
- }
- dff_init_map.add(it.second, Const(State::Sx, GetSize(it.second)));
- }
+ initvals.set_init(other_sig, init);
}
}
log_debug(" Removing %s cell `%s' from module `%s'.\n", cell->type.c_str(), cell->name.c_str(), module->name.c_str());
diff --git a/passes/opt/opt_muxtree.cc b/passes/opt/opt_muxtree.cc
index 67b283e11..100b1b495 100644
--- a/passes/opt/opt_muxtree.cc
+++ b/passes/opt/opt_muxtree.cc
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -372,29 +372,28 @@ struct OptMuxtreeWorker
int port_idx = 0, port_off = 0;
vector<int> bits = sig2bits(sig, false);
for (int i = 0; i < GetSize(bits); i++) {
- if (bits[i] < 0)
- continue;
- if (knowledge.known_inactive.at(bits[i])) {
- sig[i] = State::S0;
- did_something = true;
- } else
- if (knowledge.known_active.at(bits[i])) {
- sig[i] = State::S1;
- did_something = true;
- }
- if (width) {
- if (ctrl_bits.count(bits[i])) {
- sig[i] = ctrl_bits.at(bits[i]) == port_idx ? State::S1 : State::S0;
+ if (bits[i] >= 0) {
+ if (knowledge.known_inactive.at(bits[i])) {
+ sig[i] = State::S0;
+ did_something = true;
+ } else
+ if (knowledge.known_active.at(bits[i])) {
+ sig[i] = State::S1;
did_something = true;
}
- if (++port_off == width)
- port_idx++, port_off=0;
- } else {
if (ctrl_bits.count(bits[i])) {
- sig[i] = State::S0;
+ if (width) {
+ sig[i] = ctrl_bits.at(bits[i]) == port_idx ? State::S1 : State::S0;
+ } else {
+ sig[i] = State::S0;
+ }
did_something = true;
}
}
+ if (width) {
+ if (++port_off == width)
+ port_idx++, port_off=0;
+ }
}
if (did_something) {
diff --git a/passes/opt/opt_reduce.cc b/passes/opt/opt_reduce.cc
index 28de9ceb6..1a7c93fbd 100644
--- a/passes/opt/opt_reduce.cc
+++ b/passes/opt/opt_reduce.cc
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -100,7 +100,7 @@ struct OptReduceWorker
return;
}
- void opt_mux(RTLIL::Cell *cell)
+ void opt_pmux(RTLIL::Cell *cell)
{
RTLIL::SigSpec sig_a = assign_map(cell->getPort(ID::A));
RTLIL::SigSpec sig_b = assign_map(cell->getPort(ID::B));
@@ -141,20 +141,20 @@ struct OptReduceWorker
handled_sig.insert(this_b);
}
- 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;
- total_count++;
- }
-
if (new_sig_s.size() == 0)
{
- module->connect(RTLIL::SigSig(cell->getPort(ID::Y), cell->getPort(ID::A)));
+ module->connect(cell->getPort(ID::Y), cell->getPort(ID::A));
assign_map.add(cell->getPort(ID::Y), cell->getPort(ID::A));
module->remove(cell);
+ did_something = true;
+ total_count++;
+ return;
}
- else
- {
+
+ if (new_sig_s.size() != sig_s.size() || (new_sig_s.size() == 1 && cell->type == ID($pmux))) {
+ 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;
+ total_count++;
cell->setPort(ID::B, new_sig_b);
cell->setPort(ID::S, new_sig_s);
if (new_sig_s.size() > 1) {
@@ -166,81 +166,347 @@ struct OptReduceWorker
}
}
- void opt_mux_bits(RTLIL::Cell *cell)
+ void opt_bmux(RTLIL::Cell *cell)
{
- std::vector<RTLIL::SigBit> sig_a = assign_map(cell->getPort(ID::A)).to_sigbit_vector();
- std::vector<RTLIL::SigBit> sig_b = assign_map(cell->getPort(ID::B)).to_sigbit_vector();
- std::vector<RTLIL::SigBit> sig_y = assign_map(cell->getPort(ID::Y)).to_sigbit_vector();
+ RTLIL::SigSpec sig_a = assign_map(cell->getPort(ID::A));
+ RTLIL::SigSpec sig_s = assign_map(cell->getPort(ID::S));
+ int width = cell->getParam(ID::WIDTH).as_int();
+
+ RTLIL::SigSpec new_sig_a, new_sig_s;
+ dict<RTLIL::SigBit, int> handled_bits;
+
+ // 0 and up: index of new_sig_s bit
+ // -1: const 0
+ // -2: const 1
+ std::vector<int> swizzle;
+
+ for (int i = 0; i < sig_s.size(); i++)
+ {
+ SigBit bit = sig_s[i];
+ if (bit == State::S0) {
+ swizzle.push_back(-1);
+ } else if (bit == State::S1) {
+ swizzle.push_back(-2);
+ } else {
+ auto it = handled_bits.find(bit);
+ if (it == handled_bits.end()) {
+ int new_idx = GetSize(new_sig_s);
+ new_sig_s.append(bit);
+ handled_bits[bit] = new_idx;
+ swizzle.push_back(new_idx);
+ } else {
+ swizzle.push_back(it->second);
+ }
+ }
+ }
+
+ for (int i = 0; i < (1 << GetSize(new_sig_s)); i++) {
+ int idx = 0;
+ for (int j = 0; j < GetSize(sig_s); j++) {
+ if (swizzle[j] == -1) {
+ // const 0.
+ } else if (swizzle[j] == -2) {
+ // const 1.
+ idx |= 1 << j;
+ } else {
+ if (i & 1 << swizzle[j])
+ idx |= 1 << j;
+ }
+ }
+ new_sig_a.append(sig_a.extract(idx * width, width));
+ }
+
+ if (new_sig_s.size() == 0)
+ {
+ module->connect(cell->getPort(ID::Y), new_sig_a);
+ assign_map.add(cell->getPort(ID::Y), new_sig_a);
+ module->remove(cell);
+ did_something = true;
+ total_count++;
+ return;
+ }
+
+ if (new_sig_s.size() == 1)
+ {
+ cell->type = ID($mux);
+ cell->setPort(ID::A, new_sig_a.extract(0, width));
+ cell->setPort(ID::B, new_sig_a.extract(width, width));
+ cell->setPort(ID::S, new_sig_s);
+ cell->parameters.erase(ID::S_WIDTH);
+ did_something = true;
+ total_count++;
+ return;
+ }
+
+ 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;
+ total_count++;
+ cell->setPort(ID::A, new_sig_a);
+ cell->setPort(ID::S, new_sig_s);
+ cell->parameters[ID::S_WIDTH] = RTLIL::Const(new_sig_s.size());
+ }
+ }
+
+ void opt_demux(RTLIL::Cell *cell)
+ {
+ RTLIL::SigSpec sig_y = assign_map(cell->getPort(ID::Y));
+ RTLIL::SigSpec sig_s = assign_map(cell->getPort(ID::S));
+ int width = cell->getParam(ID::WIDTH).as_int();
+
+ RTLIL::SigSpec new_sig_y, new_sig_s;
+ dict<RTLIL::SigBit, int> handled_bits;
+
+ // 0 and up: index of new_sig_s bit
+ // -1: const 0
+ // -2: const 1
+ std::vector<int> swizzle;
+
+ for (int i = 0; i < sig_s.size(); i++)
+ {
+ SigBit bit = sig_s[i];
+ if (bit == State::S0) {
+ swizzle.push_back(-1);
+ } else if (bit == State::S1) {
+ swizzle.push_back(-2);
+ } else {
+ auto it = handled_bits.find(bit);
+ if (it == handled_bits.end()) {
+ int new_idx = GetSize(new_sig_s);
+ new_sig_s.append(bit);
+ handled_bits[bit] = new_idx;
+ swizzle.push_back(new_idx);
+ } else {
+ swizzle.push_back(it->second);
+ }
+ }
+ }
+
+ pool<int> nonzero_idx;
+
+ for (int i = 0; i < (1 << GetSize(new_sig_s)); i++) {
+ int idx = 0;
+ for (int j = 0; j < GetSize(sig_s); j++) {
+ if (swizzle[j] == -1) {
+ // const 0.
+ } else if (swizzle[j] == -2) {
+ // const 1.
+ idx |= 1 << j;
+ } else {
+ if (i & 1 << swizzle[j])
+ idx |= 1 << j;
+ }
+ }
+ log_assert(!nonzero_idx.count(idx));
+ nonzero_idx.insert(idx);
+ new_sig_y.append(sig_y.extract(idx * width, width));
+ }
+
+ if (new_sig_s.size() == sig_s.size() && sig_s.size() > 0)
+ return;
+
+ 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;
+ total_count++;
+
+ for (int i = 0; i < (1 << GetSize(sig_s)); i++) {
+ if (!nonzero_idx.count(i)) {
+ SigSpec slice = sig_y.extract(i * width, width);
+ module->connect(slice, Const(State::S0, width));
+ assign_map.add(slice, Const(State::S0, width));
+ }
+ }
+
+ if (new_sig_s.size() == 0)
+ {
+ module->connect(new_sig_y, cell->getPort(ID::A));
+ assign_map.add(new_sig_y, cell->getPort(ID::A));
+ module->remove(cell);
+ }
+ else
+ {
+ cell->setPort(ID::S, new_sig_s);
+ cell->setPort(ID::Y, new_sig_y);
+ cell->parameters[ID::S_WIDTH] = RTLIL::Const(new_sig_s.size());
+ }
+ }
+
+ bool opt_mux_bits(RTLIL::Cell *cell)
+ {
+ SigSpec sig_a = assign_map(cell->getPort(ID::A));
+ SigSpec sig_b;
+ SigSpec sig_y = assign_map(cell->getPort(ID::Y));
+ int width = GetSize(sig_y);
+
+ if (cell->type != ID($bmux))
+ sig_b = assign_map(cell->getPort(ID::B));
- 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;
+ dict<SigSpec, SigBit> consolidated_in_tuples;
+ std::vector<int> swizzle;
- for (int i = 0; i < int(sig_y.size()); i++)
+ for (int i = 0; i < width; i++)
{
- std::vector<RTLIL::SigBit> in_tuple;
+ SigSpec 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))
+ in_tuple.append(sig_a[i]);
+ for (int j = i; j < GetSize(sig_a); j += width) {
+ in_tuple.append(sig_a[j]);
+ if (sig_a[j] != in_tuple[0])
+ all_tuple_bits_same = false;
+ }
+ for (int j = i; j < GetSize(sig_b); j += width) {
+ in_tuple.append(sig_b[j]);
+ if (sig_b[j] != in_tuple[0])
all_tuple_bits_same = false;
- in_tuple.push_back(sig_b.at(j));
}
if (all_tuple_bits_same)
{
- old_sig_conn.first.append(sig_y.at(i));
- old_sig_conn.second.append(sig_a.at(i));
+ old_sig_conn.first.append(sig_y[i]);
+ old_sig_conn.second.append(sig_a[i]);
+ continue;
}
- else if (consolidated_in_tuples_map.count(in_tuple))
+
+ auto it = consolidated_in_tuples.find(in_tuple);
+ if (it == consolidated_in_tuples.end())
{
- old_sig_conn.first.append(sig_y.at(i));
- old_sig_conn.second.append(consolidated_in_tuples_map.at(in_tuple));
+ consolidated_in_tuples[in_tuple] = sig_y[i];
+ swizzle.push_back(i);
}
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));
+ old_sig_conn.first.append(sig_y[i]);
+ old_sig_conn.second.append(it->second);
}
}
- if (new_sig_y.size() != sig_y.size())
+ if (GetSize(swizzle) != width)
{
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(ID::A)),
- log_signal(cell->getPort(ID::B)), log_signal(cell->getPort(ID::Y)));
-
- cell->setPort(ID::A, RTLIL::SigSpec());
- for (auto &in_tuple : consolidated_in_tuples) {
- RTLIL::SigSpec new_a = cell->getPort(ID::A);
- new_a.append(in_tuple.at(0));
- cell->setPort(ID::A, new_a);
+ if (cell->type != ID($bmux)) {
+ log(" Old ports: A=%s, B=%s, Y=%s\n", log_signal(cell->getPort(ID::A)),
+ log_signal(cell->getPort(ID::B)), log_signal(cell->getPort(ID::Y)));
+ } else {
+ log(" Old ports: A=%s, Y=%s\n", log_signal(cell->getPort(ID::A)),
+ log_signal(cell->getPort(ID::Y)));
}
- cell->setPort(ID::B, RTLIL::SigSpec());
- for (int i = 1; i <= cell->getPort(ID::S).size(); i++)
- for (auto &in_tuple : consolidated_in_tuples) {
- RTLIL::SigSpec new_b = cell->getPort(ID::B);
- new_b.append(in_tuple.at(i));
- cell->setPort(ID::B, new_b);
+ if (swizzle.empty()) {
+ module->remove(cell);
+ } else {
+ SigSpec new_sig_a;
+ for (int i = 0; i < GetSize(sig_a); i += width)
+ for (int j: swizzle)
+ new_sig_a.append(sig_a[i+j]);
+ cell->setPort(ID::A, new_sig_a);
+
+ if (cell->type != ID($bmux)) {
+ SigSpec new_sig_b;
+ for (int i = 0; i < GetSize(sig_b); i += width)
+ for (int j: swizzle)
+ new_sig_b.append(sig_b[i+j]);
+ cell->setPort(ID::B, new_sig_b);
}
- cell->parameters[ID::WIDTH] = RTLIL::Const(new_sig_y.size());
- cell->setPort(ID::Y, new_sig_y);
+ SigSpec new_sig_y;
+ for (int j: swizzle)
+ new_sig_y.append(sig_y[j]);
+ cell->setPort(ID::Y, new_sig_y);
+
+ cell->parameters[ID::WIDTH] = RTLIL::Const(GetSize(swizzle));
+
+ if (cell->type != ID($bmux)) {
+ log(" New ports: A=%s, B=%s, Y=%s\n", log_signal(cell->getPort(ID::A)),
+ log_signal(cell->getPort(ID::B)), log_signal(cell->getPort(ID::Y)));
+ } else {
+ log(" New ports: A=%s, Y=%s\n", log_signal(cell->getPort(ID::A)),
+ log_signal(cell->getPort(ID::Y)));
+ }
+ }
- log(" New ports: A=%s, B=%s, Y=%s\n", log_signal(cell->getPort(ID::A)),
- log_signal(cell->getPort(ID::B)), log_signal(cell->getPort(ID::Y)));
log(" New connections: %s = %s\n", log_signal(old_sig_conn.first), log_signal(old_sig_conn.second));
+ module->connect(old_sig_conn);
+
+ did_something = true;
+ total_count++;
+ }
+ return swizzle.empty();
+ }
+
+ bool opt_demux_bits(RTLIL::Cell *cell) {
+ SigSpec sig_a = assign_map(cell->getPort(ID::A));
+ SigSpec sig_y = assign_map(cell->getPort(ID::Y));
+ int width = GetSize(sig_a);
+
+ RTLIL::SigSig old_sig_conn;
+
+ dict<SigBit, int> handled_bits;
+ std::vector<int> swizzle;
+
+ for (int i = 0; i < width; i++)
+ {
+ if (sig_a[i] == State::S0)
+ {
+ for (int j = i; j < GetSize(sig_y); j += width)
+ {
+ old_sig_conn.first.append(sig_y[j]);
+ old_sig_conn.second.append(State::S0);
+ }
+ continue;
+ }
+ auto it = handled_bits.find(sig_a[i]);
+ if (it == handled_bits.end())
+ {
+ handled_bits[sig_a[i]] = i;
+ swizzle.push_back(i);
+ }
+ else
+ {
+ for (int j = 0; j < GetSize(sig_y); j += width)
+ {
+ old_sig_conn.first.append(sig_y[i+j]);
+ old_sig_conn.second.append(sig_y[it->second+j]);
+ }
+ }
+ }
+
+ if (GetSize(swizzle) != width)
+ {
+ log(" Consolidated identical input bits for %s cell %s:\n", cell->type.c_str(), cell->name.c_str());
+ log(" Old ports: A=%s, Y=%s\n", log_signal(cell->getPort(ID::A)),
+ log_signal(cell->getPort(ID::Y)));
+
+ if (swizzle.empty()) {
+ module->remove(cell);
+ } else {
+ SigSpec new_sig_a;
+ for (int j: swizzle)
+ new_sig_a.append(sig_a[j]);
+ cell->setPort(ID::A, new_sig_a);
+
+ SigSpec new_sig_y;
+ for (int i = 0; i < GetSize(sig_y); i += width)
+ for (int j: swizzle)
+ new_sig_y.append(sig_y[i+j]);
+ cell->setPort(ID::Y, new_sig_y);
+
+ cell->parameters[ID::WIDTH] = RTLIL::Const(GetSize(swizzle));
+
+ log(" New ports: A=%s, Y=%s\n", log_signal(cell->getPort(ID::A)),
+ log_signal(cell->getPort(ID::Y)));
+ }
+
+ log(" New connections: %s = %s\n", log_signal(old_sig_conn.first), log_signal(old_sig_conn.second));
module->connect(old_sig_conn);
did_something = true;
total_count++;
}
+ return swizzle.empty();
}
OptReduceWorker(RTLIL::Design *design, RTLIL::Module *module, bool do_fine) :
@@ -254,9 +520,9 @@ struct OptReduceWorker
SigPool mem_wren_sigs;
for (auto &cell_it : module->cells_) {
RTLIL::Cell *cell = cell_it.second;
- if (cell->type == ID($mem))
+ if (cell->type.in(ID($mem), ID($mem_v2)))
mem_wren_sigs.add(assign_map(cell->getPort(ID::WR_EN)));
- if (cell->type == ID($memwr))
+ if (cell->type.in(ID($memwr), ID($memwr_v2)))
mem_wren_sigs.add(assign_map(cell->getPort(ID::EN)));
}
for (auto &cell_it : module->cells_) {
@@ -309,20 +575,31 @@ struct OptReduceWorker
// merge identical inputs on $mux and $pmux cells
- std::vector<RTLIL::Cell*> cells;
-
- for (auto &it : module->cells_)
- if ((it.second->type == ID($mux) || it.second->type == ID($pmux)) && design->selected(module, it.second))
- cells.push_back(it.second);
-
- for (auto cell : cells)
+ for (auto cell : module->selected_cells())
{
+ if (!cell->type.in(ID($mux), ID($pmux), ID($bmux), ID($demux)))
+ 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(ID::Y))))
- opt_mux_bits(cell);
+ if (do_fine || mem_wren_sigs.check_any(assign_map(cell->getPort(ID::Y)))) {
+ if (cell->type == ID($demux)) {
+ if (opt_demux_bits(cell))
+ continue;
+ } else {
+ if (opt_mux_bits(cell))
+ continue;
+ }
+ }
+
+ if (cell->type.in(ID($mux), ID($pmux)))
+ opt_pmux(cell);
+
+ if (cell->type == ID($bmux))
+ opt_bmux(cell);
- opt_mux(cell);
+ if (cell->type == ID($demux))
+ opt_demux(cell);
}
}
diff --git a/passes/opt/opt_rmdff.cc b/passes/opt/opt_rmdff.cc
deleted file mode 100644
index 8f7628a4a..000000000
--- a/passes/opt/opt_rmdff.cc
+++ /dev/null
@@ -1,711 +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.
- *
- */
-
-#include "kernel/log.h"
-#include "kernel/register.h"
-#include "kernel/rtlil.h"
-#include "kernel/satgen.h"
-#include "kernel/sigtools.h"
-#include <stdio.h>
-#include <stdlib.h>
-
-USING_YOSYS_NAMESPACE
-PRIVATE_NAMESPACE_BEGIN
-
-SigMap assign_map, dff_init_map;
-SigSet<RTLIL::Cell*> mux_drivers;
-dict<SigBit, RTLIL::Cell*> bit2driver;
-dict<SigBit, pool<SigBit>> init_attributes;
-
-bool keepdc;
-bool sat;
-
-void remove_init_attr(SigSpec sig)
-{
- for (auto bit : assign_map(sig))
- if (init_attributes.count(bit))
- for (auto wbit : init_attributes.at(bit))
- wbit.wire->attributes.at(ID::init)[wbit.offset] = State::Sx;
-}
-
-bool handle_dffsr(RTLIL::Module *mod, RTLIL::Cell *cell)
-{
- SigSpec sig_set, sig_clr;
- State pol_set, pol_clr;
-
- if (cell->hasPort(ID::S))
- sig_set = cell->getPort(ID::S);
-
- if (cell->hasPort(ID::R))
- sig_clr = cell->getPort(ID::R);
-
- if (cell->hasPort(ID::SET))
- sig_set = cell->getPort(ID::SET);
-
- if (cell->hasPort(ID::CLR))
- sig_clr = cell->getPort(ID::CLR);
-
- log_assert(GetSize(sig_set) == GetSize(sig_clr));
-
- if (cell->type.begins_with("$_DFFSR_")) {
- pol_set = cell->type[9] == 'P' ? State::S1 : State::S0;
- pol_clr = cell->type[10] == 'P' ? State::S1 : State::S0;
- } else
- if (cell->type.begins_with("$_DLATCHSR_")) {
- pol_set = cell->type[12] == 'P' ? State::S1 : State::S0;
- pol_clr = cell->type[13] == 'P' ? State::S1 : State::S0;
- } else
- if (cell->type.in(ID($dffsr), ID($dlatchsr))) {
- pol_set = cell->parameters[ID::SET_POLARITY].as_bool() ? State::S1 : State::S0;
- pol_clr = cell->parameters[ID::CLR_POLARITY].as_bool() ? State::S1 : State::S0;
- } else
- log_abort();
-
- State npol_set = pol_set == State::S0 ? State::S1 : State::S0;
- State npol_clr = pol_clr == State::S0 ? State::S1 : State::S0;
-
- SigSpec sig_d = cell->getPort(ID::D);
- SigSpec sig_q = cell->getPort(ID::Q);
-
- bool did_something = false;
- bool proper_sr = false;
- bool used_pol_set = false;
- bool used_pol_clr = false;
- bool hasreset = false;
- Const reset_val;
- SigSpec sig_reset;
-
- for (int i = 0; i < GetSize(sig_set); i++)
- {
- SigBit s = sig_set[i], c = sig_clr[i];
-
- if (s != npol_set || c != npol_clr)
- hasreset = true;
-
- if (s == pol_set || c == pol_clr)
- {
- log("Constantly %s Q bit %s for SR cell %s (%s) from module %s.\n",
- s == pol_set ? "set" : "cleared", log_signal(sig_q[i]),
- log_id(cell), log_id(cell->type), log_id(mod));
-
- remove_init_attr(sig_q[i]);
- mod->connect(sig_q[i], s == pol_set ? State::S1 : State::S0);
- sig_set.remove(i);
- sig_clr.remove(i);
- sig_d.remove(i);
- sig_q.remove(i--);
- did_something = true;
- continue;
- }
- if (sig_reset.empty() && s.wire != nullptr) sig_reset = s;
- if (sig_reset.empty() && c.wire != nullptr) sig_reset = c;
-
- if (s.wire != nullptr && s != sig_reset) proper_sr = true;
- if (c.wire != nullptr && c != sig_reset) proper_sr = true;
-
- if ((s.wire == nullptr) != (c.wire == nullptr)) {
- if (s.wire != nullptr) used_pol_set = true;
- if (c.wire != nullptr) used_pol_clr = true;
- reset_val.bits.push_back(c.wire == nullptr ? State::S1 : State::S0);
- } else
- proper_sr = true;
- }
-
- if (!hasreset)
- proper_sr = false;
-
- if (GetSize(sig_set) == 0)
- {
- log("Removing %s (%s) from module %s.\n", log_id(cell), log_id(cell->type), log_id(mod));
- mod->remove(cell);
- return true;
- }
-
- if (cell->type.in(ID($dffsr), ID($dlatchsr)))
- {
- cell->setParam(ID::WIDTH, GetSize(sig_d));
- cell->setPort(ID::SET, sig_set);
- cell->setPort(ID::CLR, sig_clr);
- cell->setPort(ID::D, sig_d);
- cell->setPort(ID::Q, sig_q);
- }
- else
- {
- cell->setPort(ID::S, sig_set);
- cell->setPort(ID::R, sig_clr);
- cell->setPort(ID::D, sig_d);
- cell->setPort(ID::Q, sig_q);
- }
-
- if (proper_sr)
- return did_something;
-
- if (used_pol_set && used_pol_clr && pol_set != pol_clr)
- return did_something;
-
- if (cell->type == ID($dlatchsr))
- return did_something;
-
- State unified_pol = used_pol_set ? pol_set : pol_clr;
-
- if (cell->type == ID($dffsr))
- {
- if (hasreset)
- {
- log("Converting %s (%s) to %s in module %s.\n", log_id(cell), log_id(cell->type), "$adff", log_id(mod));
-
- cell->type = ID($adff);
- cell->setParam(ID::ARST_POLARITY, unified_pol);
- cell->setParam(ID::ARST_VALUE, reset_val);
- cell->setPort(ID::ARST, sig_reset);
-
- cell->unsetParam(ID::SET_POLARITY);
- cell->unsetParam(ID::CLR_POLARITY);
- cell->unsetPort(ID::SET);
- cell->unsetPort(ID::CLR);
- }
- else
- {
- log("Converting %s (%s) to %s in module %s.\n", log_id(cell), log_id(cell->type), "$dff", log_id(mod));
-
- cell->type = ID($dff);
- cell->unsetParam(ID::SET_POLARITY);
- cell->unsetParam(ID::CLR_POLARITY);
- cell->unsetPort(ID::SET);
- cell->unsetPort(ID::CLR);
- }
-
- return true;
- }
-
- if (!hasreset)
- {
- IdString new_type;
-
- if (cell->type.begins_with("$_DFFSR_"))
- new_type = stringf("$_DFF_%c_", cell->type[8]);
- else if (cell->type.begins_with("$_DLATCHSR_"))
- new_type = stringf("$_DLATCH_%c_", cell->type[11]);
- else
- log_abort();
-
- log("Converting %s (%s) to %s in module %s.\n", log_id(cell), log_id(cell->type), log_id(new_type), log_id(mod));
-
- cell->type = new_type;
- cell->unsetPort(ID::S);
- cell->unsetPort(ID::R);
-
- return true;
- }
-
- return did_something;
-}
-
-bool handle_dlatch(RTLIL::Module *mod, RTLIL::Cell *dlatch)
-{
- SigSpec sig_e;
- State on_state, off_state;
-
- if (dlatch->type == ID($dlatch)) {
- sig_e = assign_map(dlatch->getPort(ID::EN));
- on_state = dlatch->getParam(ID::EN_POLARITY).as_bool() ? State::S1 : State::S0;
- off_state = dlatch->getParam(ID::EN_POLARITY).as_bool() ? State::S0 : State::S1;
- } else
- if (dlatch->type == ID($_DLATCH_P_)) {
- sig_e = assign_map(dlatch->getPort(ID::E));
- on_state = State::S1;
- off_state = State::S0;
- } else
- if (dlatch->type == ID($_DLATCH_N_)) {
- sig_e = assign_map(dlatch->getPort(ID::E));
- on_state = State::S0;
- off_state = State::S1;
- } else
- log_abort();
-
- if (sig_e == off_state)
- {
- RTLIL::Const val_init;
- for (auto bit : dff_init_map(dlatch->getPort(ID::Q)))
- val_init.bits.push_back(bit.wire == NULL ? bit.data : State::Sx);
- mod->connect(dlatch->getPort(ID::Q), val_init);
- goto delete_dlatch;
- }
-
- if (sig_e == on_state)
- {
- mod->connect(dlatch->getPort(ID::Q), dlatch->getPort(ID::D));
- goto delete_dlatch;
- }
-
- return false;
-
-delete_dlatch:
- log("Removing %s (%s) from module %s.\n", log_id(dlatch), log_id(dlatch->type), log_id(mod));
- remove_init_attr(dlatch->getPort(ID::Q));
- mod->remove(dlatch);
- return true;
-}
-
-bool handle_dff(RTLIL::Module *mod, RTLIL::Cell *dff)
-{
- RTLIL::SigSpec sig_d, sig_q, sig_c, sig_r, sig_e;
- RTLIL::Const val_cp, val_rp, val_rv, val_ep;
-
- if (dff->type == ID($_FF_)) {
- sig_d = dff->getPort(ID::D);
- sig_q = dff->getPort(ID::Q);
- }
- else if (dff->type == ID($_DFF_N_) || dff->type == ID($_DFF_P_)) {
- sig_d = dff->getPort(ID::D);
- sig_q = dff->getPort(ID::Q);
- sig_c = dff->getPort(ID::C);
- val_cp = RTLIL::Const(dff->type == ID($_DFF_P_), 1);
- }
- else if (dff->type.begins_with("$_DFF_") && dff->type.compare(9, 1, "_") == 0 &&
- (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->getPort(ID::D);
- sig_q = dff->getPort(ID::Q);
- sig_c = dff->getPort(ID::C);
- sig_r = dff->getPort(ID::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.begins_with("$_DFFE_") && dff->type.compare(9, 1, "_") == 0 &&
- (dff->type[7] == 'N' || dff->type[7] == 'P') &&
- (dff->type[8] == 'N' || dff->type[8] == 'P')) {
- sig_d = dff->getPort(ID::D);
- sig_q = dff->getPort(ID::Q);
- sig_c = dff->getPort(ID::C);
- sig_e = dff->getPort(ID::E);
- val_cp = RTLIL::Const(dff->type[7] == 'P', 1);
- val_ep = RTLIL::Const(dff->type[8] == 'P', 1);
- }
- else if (dff->type == ID($ff)) {
- sig_d = dff->getPort(ID::D);
- sig_q = dff->getPort(ID::Q);
- }
- else if (dff->type == ID($dff)) {
- sig_d = dff->getPort(ID::D);
- sig_q = dff->getPort(ID::Q);
- sig_c = dff->getPort(ID::CLK);
- val_cp = RTLIL::Const(dff->parameters[ID::CLK_POLARITY].as_bool(), 1);
- }
- else if (dff->type == ID($dffe)) {
- sig_e = dff->getPort(ID::EN);
- sig_d = dff->getPort(ID::D);
- sig_q = dff->getPort(ID::Q);
- sig_c = dff->getPort(ID::CLK);
- val_cp = RTLIL::Const(dff->parameters[ID::CLK_POLARITY].as_bool(), 1);
- val_ep = RTLIL::Const(dff->parameters[ID::EN_POLARITY].as_bool(), 1);
- }
- else if (dff->type == ID($adff)) {
- sig_d = dff->getPort(ID::D);
- sig_q = dff->getPort(ID::Q);
- sig_c = dff->getPort(ID::CLK);
- sig_r = dff->getPort(ID::ARST);
- val_cp = RTLIL::Const(dff->parameters[ID::CLK_POLARITY].as_bool(), 1);
- val_rp = RTLIL::Const(dff->parameters[ID::ARST_POLARITY].as_bool(), 1);
- val_rv = dff->parameters[ID::ARST_VALUE];
- }
- else
- log_abort();
-
- assign_map.apply(sig_d);
- assign_map.apply(sig_q);
- assign_map.apply(sig_c);
- assign_map.apply(sig_r);
-
- bool has_init = false;
- RTLIL::Const val_init;
- for (auto bit : dff_init_map(sig_q).to_sigbit_vector()) {
- if (bit.wire == NULL || keepdc)
- has_init = true;
- val_init.bits.push_back(bit.wire == NULL ? bit.data : RTLIL::State::Sx);
- }
-
- if (dff->type.in(ID($ff), ID($dff)) && mux_drivers.has(sig_d)) {
- std::set<RTLIL::Cell*> muxes;
- mux_drivers.find(sig_d, muxes);
- for (auto mux : muxes) {
- RTLIL::SigSpec sig_a = assign_map(mux->getPort(ID::A));
- RTLIL::SigSpec sig_b = assign_map(mux->getPort(ID::B));
- if (sig_a == sig_q && sig_b.is_fully_const() && (!has_init || val_init == sig_b.as_const())) {
- mod->connect(sig_q, sig_b);
- goto delete_dff;
- }
- if (sig_b == sig_q && sig_a.is_fully_const() && (!has_init || val_init == sig_a.as_const())) {
- mod->connect(sig_q, sig_a);
- goto delete_dff;
- }
- }
- }
-
- // If clock is driven by a constant and (i) no reset signal
- // (ii) Q has no initial value
- // (iii) initial value is same as reset value
- if (!sig_c.empty() && sig_c.is_fully_const() && (!sig_r.size() || !has_init || val_init == val_rv)) {
- if (val_rv.bits.size() == 0)
- val_rv = val_init;
- // Q is permanently reset value or initial value
- mod->connect(sig_q, val_rv);
- goto delete_dff;
- }
-
- // If D is fully undefined and reset signal present and (i) Q has no initial value
- // (ii) initial value is same as reset value
- if (sig_d.is_fully_undef() && sig_r.size() && (!has_init || val_init == val_rv)) {
- // Q is permanently reset value
- mod->connect(sig_q, val_rv);
- goto delete_dff;
- }
-
- // If D is fully undefined and no reset signal and Q has an initial value
- if (sig_d.is_fully_undef() && !sig_r.size() && has_init) {
- // Q is permanently initial value
- mod->connect(sig_q, val_init);
- goto delete_dff;
- }
-
- // If D is fully constant and (i) no reset signal
- // (ii) reset value is same as constant D
- // and (a) has no initial value
- // (b) initial value same as constant D
- if (sig_d.is_fully_const() && (!sig_r.size() || val_rv == sig_d.as_const()) && (!has_init || val_init == sig_d.as_const())) {
- // Q is permanently D
- mod->connect(sig_q, sig_d);
- goto delete_dff;
- }
-
- // If D input is same as Q output and (i) no reset signal
- // (ii) no initial signal
- // (iii) initial value is same as reset value
- if (sig_d == sig_q && (sig_r.empty() || !has_init || val_init == val_rv)) {
- // Q is permanently reset value or initial value
- if (sig_r.size())
- mod->connect(sig_q, val_rv);
- else if (has_init)
- mod->connect(sig_q, val_init);
- goto delete_dff;
- }
-
- // If reset signal is present, and is fully constant
- if (!sig_r.empty() && sig_r.is_fully_const())
- {
- // If reset value is permanently active or if reset is undefined
- if (sig_r == val_rp || sig_r.is_fully_undef()) {
- // Q is permanently reset value
- mod->connect(sig_q, val_rv);
- goto delete_dff;
- }
-
- log("Removing unused reset from %s (%s) from module %s.\n", log_id(dff), log_id(dff->type), log_id(mod));
-
- if (dff->type == ID($adff)) {
- dff->type = ID($dff);
- dff->unsetPort(ID::ARST);
- dff->unsetParam(ID::ARST_POLARITY);
- dff->unsetParam(ID::ARST_VALUE);
- return true;
- }
-
- log_assert(dff->type.begins_with("$_DFF_"));
- dff->type = stringf("$_DFF_%c_", + dff->type[6]);
- dff->unsetPort(ID::R);
- }
-
- // If enable signal is present, and is fully constant
- if (!sig_e.empty() && sig_e.is_fully_const())
- {
- // If enable value is permanently inactive
- if (sig_e != val_ep) {
- // Q is permanently initial value
- mod->connect(sig_q, val_init);
- goto delete_dff;
- }
-
- log("Removing unused enable from %s (%s) from module %s.\n", log_id(dff), log_id(dff->type), log_id(mod));
-
- if (dff->type == ID($dffe)) {
- dff->type = ID($dff);
- dff->unsetPort(ID::EN);
- dff->unsetParam(ID::EN_POLARITY);
- return true;
- }
-
- log_assert(dff->type.begins_with("$_DFFE_"));
- dff->type = stringf("$_DFF_%c_", + dff->type[7]);
- dff->unsetPort(ID::E);
- }
-
- if (sat && has_init && (!sig_r.size() || val_init == val_rv))
- {
- bool removed_sigbits = false;
-
- ezSatPtr ez;
- SatGen satgen(ez.get(), &assign_map);
- pool<Cell*> sat_cells;
-
- std::function<void(Cell*)> sat_import_cell = [&](Cell *c) {
- if (!sat_cells.insert(c).second)
- return;
- if (!satgen.importCell(c))
- return;
- for (auto &conn : c->connections()) {
- if (!c->input(conn.first))
- continue;
- for (auto bit : assign_map(conn.second))
- if (bit2driver.count(bit))
- sat_import_cell(bit2driver.at(bit));
- }
- };
-
- // For each register bit, try to prove that it cannot change from the initial value. If so, remove it
- for (int position = 0; position < GetSize(sig_d); position += 1) {
- RTLIL::SigBit q_sigbit = sig_q[position];
- RTLIL::SigBit d_sigbit = sig_d[position];
-
- if ((!q_sigbit.wire) || (!d_sigbit.wire))
- continue;
-
- if (!bit2driver.count(d_sigbit))
- continue;
-
- sat_import_cell(bit2driver.at(d_sigbit));
-
- RTLIL::State sigbit_init_val = val_init[position];
- if (sigbit_init_val != State::S0 && sigbit_init_val != State::S1)
- continue;
-
- int init_sat_pi = satgen.importSigSpec(sigbit_init_val).front();
- int q_sat_pi = satgen.importSigBit(q_sigbit);
- int d_sat_pi = satgen.importSigBit(d_sigbit);
-
- // Try to find out whether the register bit can change under some circumstances
- bool counter_example_found = ez->solve(ez->IFF(q_sat_pi, init_sat_pi), ez->NOT(ez->IFF(d_sat_pi, init_sat_pi)));
-
- // If the register bit cannot change, we can replace it with a constant
- if (!counter_example_found)
- {
- log("Setting constant %d-bit at position %d on %s (%s) from module %s.\n", sigbit_init_val ? 1 : 0,
- position, log_id(dff), log_id(dff->type), log_id(mod));
-
- SigSpec tmp = dff->getPort(ID::D);
- tmp[position] = sigbit_init_val;
- dff->setPort(ID::D, tmp);
-
- removed_sigbits = true;
- }
- }
-
- if (removed_sigbits) {
- handle_dff(mod, dff);
- return true;
- }
- }
-
-
- return false;
-
-delete_dff:
- log("Removing %s (%s) from module %s.\n", log_id(dff), log_id(dff->type), log_id(mod));
- remove_init_attr(dff->getPort(ID::Q));
- mod->remove(dff);
-
- for (auto &entry : bit2driver)
- if (entry.second == dff)
- bit2driver.erase(entry.first);
-
- return true;
-}
-
-struct OptRmdffPass : public Pass {
- OptRmdffPass() : Pass("opt_rmdff", "remove DFFs with constant inputs") { }
- void help() override
- {
- // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
- log("\n");
- log(" opt_rmdff [-keepdc] [-sat] [selection]\n");
- log("\n");
- log("This pass identifies flip-flops with constant inputs and replaces them with\n");
- log("a constant driver.\n");
- log("\n");
- log(" -sat\n");
- log(" additionally invoke SAT solver to detect and remove flip-flops (with \n");
- log(" non-constant inputs) that can also be replaced with a constant driver\n");
- log("\n");
- }
- void execute(std::vector<std::string> args, RTLIL::Design *design) override
- {
- int total_count = 0, total_initdrv = 0;
- log_header(design, "Executing OPT_RMDFF pass (remove dff with constant values).\n");
-
- keepdc = false;
- sat = false;
-
- size_t argidx;
- for (argidx = 1; argidx < args.size(); argidx++) {
- if (args[argidx] == "-keepdc") {
- keepdc = true;
- continue;
- }
- if (args[argidx] == "-sat") {
- sat = true;
- continue;
- }
- break;
- }
- extra_args(args, argidx, design);
-
- for (auto module : design->selected_modules()) {
- pool<SigBit> driven_bits;
- dict<SigBit, State> init_bits;
-
- assign_map.set(module);
- dff_init_map.set(module);
- mux_drivers.clear();
- bit2driver.clear();
- init_attributes.clear();
-
- for (auto wire : module->wires())
- {
- if (wire->attributes.count(ID::init) != 0) {
- Const initval = wire->attributes.at(ID::init);
- for (int i = 0; i < GetSize(initval) && i < GetSize(wire); i++)
- if (initval[i] == State::S0 || initval[i] == State::S1)
- dff_init_map.add(SigBit(wire, i), initval[i]);
- for (int i = 0; i < GetSize(wire); i++) {
- SigBit wire_bit(wire, i), mapped_bit = assign_map(wire_bit);
- if (mapped_bit.wire) {
- init_attributes[mapped_bit].insert(wire_bit);
- if (i < GetSize(initval))
- init_bits[mapped_bit] = initval[i];
- }
- }
- }
-
- if (wire->port_input) {
- for (auto bit : assign_map(wire))
- driven_bits.insert(bit);
- }
- }
-
- std::vector<RTLIL::IdString> dff_list;
- std::vector<RTLIL::IdString> dffsr_list;
- std::vector<RTLIL::IdString> dlatch_list;
- for (auto cell : module->cells())
- {
- for (auto &conn : cell->connections()) {
- bool is_output = cell->output(conn.first);
- if (is_output || !cell->known())
- for (auto bit : assign_map(conn.second)) {
- if (is_output)
- bit2driver[bit] = cell;
- driven_bits.insert(bit);
- }
- }
-
- if (cell->type.in(ID($mux), ID($pmux))) {
- if (cell->getPort(ID::A).size() == cell->getPort(ID::B).size())
- mux_drivers.insert(assign_map(cell->getPort(ID::Y)), cell);
- continue;
- }
-
- if (!design->selected(module, cell))
- continue;
-
- if (cell->type.in(ID($_DFFSR_NNN_), ID($_DFFSR_NNP_), ID($_DFFSR_NPN_), ID($_DFFSR_NPP_),
- ID($_DFFSR_PNN_), ID($_DFFSR_PNP_), ID($_DFFSR_PPN_), ID($_DFFSR_PPP_), ID($dffsr),
- ID($_DLATCHSR_NNN_), ID($_DLATCHSR_NNP_), ID($_DLATCHSR_NPN_), ID($_DLATCHSR_NPP_),
- ID($_DLATCHSR_PNN_), ID($_DLATCHSR_PNP_), ID($_DLATCHSR_PPN_), ID($_DLATCHSR_PPP_), ID($dlatchsr)))
- dffsr_list.push_back(cell->name);
-
- if (cell->type.in(ID($_FF_), ID($_DFF_N_), ID($_DFF_P_),
- ID($_DFF_NN0_), ID($_DFF_NN1_), ID($_DFF_NP0_), ID($_DFF_NP1_),
- ID($_DFF_PN0_), ID($_DFF_PN1_), ID($_DFF_PP0_), ID($_DFF_PP1_),
- ID($_DFFE_NN_), ID($_DFFE_NP_), ID($_DFFE_PN_), ID($_DFFE_PP_),
- ID($ff), ID($dff), ID($dffe), ID($adff)))
- dff_list.push_back(cell->name);
-
- if (cell->type.in(ID($dlatch), ID($_DLATCH_P_), ID($_DLATCH_N_)))
- dlatch_list.push_back(cell->name);
- }
-
- for (auto &id : dffsr_list) {
- if (module->cell(id) != nullptr &&
- handle_dffsr(module, module->cells_[id]))
- total_count++;
- }
-
- for (auto &id : dff_list) {
- if (module->cell(id) != nullptr &&
- handle_dff(module, module->cells_[id]))
- total_count++;
- }
-
- for (auto &id : dlatch_list) {
- if (module->cell(id) != nullptr &&
- handle_dlatch(module, module->cells_[id]))
- total_count++;
- }
-
- SigSpec const_init_sigs;
-
- for (auto bit : init_bits)
- if (!driven_bits.count(bit.first))
- const_init_sigs.append(bit.first);
-
- const_init_sigs.sort_and_unify();
-
- for (SigSpec sig : const_init_sigs.chunks())
- {
- Const val;
-
- for (auto bit : sig)
- val.bits.push_back(init_bits.at(bit));
-
- log("Promoting init spec %s = %s to constant driver in module %s.\n",
- log_signal(sig), log_signal(val), log_id(module));
-
- module->connect(sig, val);
- remove_init_attr(sig);
- total_initdrv++;
- }
- }
-
- assign_map.clear();
- mux_drivers.clear();
- bit2driver.clear();
- init_attributes.clear();
-
- if (total_count || total_initdrv)
- design->scratchpad_set_bool("opt.did_something", true);
-
- if (total_initdrv)
- log("Promoted %d init specs to constant drivers.\n", total_initdrv);
-
- if (total_count)
- log("Replaced %d DFF cells.\n", total_count);
- }
-} OptRmdffPass;
-
-PRIVATE_NAMESPACE_END
diff --git a/passes/opt/opt_share.cc b/passes/opt/opt_share.cc
index db21cef28..ba85df975 100644
--- a/passes/opt/opt_share.cc
+++ b/passes/opt/opt_share.cc
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
* 2019 Bogdan Vukobratovic <bogdan.vukobratovic@gmail.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
@@ -30,8 +30,6 @@
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
-SigMap assign_map;
-
struct OpMuxConn {
RTLIL::SigSpec sig;
RTLIL::Cell *mux;
@@ -157,9 +155,9 @@ bool decode_port_signed(RTLIL::Cell *cell, RTLIL::IdString port_name)
return false;
}
-ExtSigSpec decode_port(RTLIL::Cell *cell, RTLIL::IdString port_name, SigMap *sigmap)
+ExtSigSpec decode_port(RTLIL::Cell *cell, RTLIL::IdString port_name, const SigMap &sigmap)
{
- auto sig = (*sigmap)(cell->getPort(port_name));
+ auto sig = sigmap(cell->getPort(port_name));
RTLIL::SigSpec sign = decode_port_sign(cell, port_name);
RTLIL::IdString semantics = decode_port_semantics(cell, port_name);
@@ -169,7 +167,7 @@ ExtSigSpec decode_port(RTLIL::Cell *cell, RTLIL::IdString port_name, SigMap *sig
return ExtSigSpec(sig, sign, is_signed, semantics);
}
-void merge_operators(RTLIL::Module *module, RTLIL::Cell *mux, const std::vector<OpMuxConn> &ports, const ExtSigSpec &operand)
+void merge_operators(RTLIL::Module *module, RTLIL::Cell *mux, const std::vector<OpMuxConn> &ports, const ExtSigSpec &operand, const SigMap &sigmap)
{
std::vector<ExtSigSpec> muxed_operands;
int max_width = 0;
@@ -177,10 +175,10 @@ void merge_operators(RTLIL::Module *module, RTLIL::Cell *mux, const std::vector<
auto op = p.op;
RTLIL::IdString muxed_port_name = ID::A;
- if (decode_port(op, ID::A, &assign_map) == operand)
+ if (decode_port(op, ID::A, sigmap) == operand)
muxed_port_name = ID::B;
- auto operand = decode_port(op, muxed_port_name, &assign_map);
+ auto operand = decode_port(op, muxed_port_name, sigmap);
if (operand.sig.size() > max_width)
max_width = operand.sig.size();
@@ -190,11 +188,13 @@ void merge_operators(RTLIL::Module *module, RTLIL::Cell *mux, const std::vector<
auto shared_op = ports[0].op;
if (std::any_of(muxed_operands.begin(), muxed_operands.end(), [&](ExtSigSpec &op) { return op.sign != muxed_operands[0].sign; }))
- max_width = std::max(max_width, shared_op->getParam(ID::Y_WIDTH).as_int());
-
+ max_width = std::max(max_width, shared_op->getParam(ID::Y_WIDTH).as_int());
- for (auto &operand : muxed_operands)
+ for (auto &operand : muxed_operands) {
operand.sig.extend_u0(max_width, operand.is_signed);
+ if (operand.sign != muxed_operands[0].sign)
+ operand = ExtSigSpec(module->Neg(NEW_ID, operand.sig, operand.is_signed));
+ }
for (const auto& p : ports) {
auto op = p.op;
@@ -203,61 +203,58 @@ void merge_operators(RTLIL::Module *module, RTLIL::Cell *mux, const std::vector<
module->remove(op);
}
- for (auto &muxed_op : muxed_operands)
- if (muxed_op.sign != muxed_operands[0].sign)
- muxed_op = ExtSigSpec(module->Neg(NEW_ID, muxed_op.sig, muxed_op.is_signed));
-
- RTLIL::SigSpec mux_y = mux->getPort(ID::Y);
RTLIL::SigSpec mux_a = mux->getPort(ID::A);
RTLIL::SigSpec mux_b = mux->getPort(ID::B);
RTLIL::SigSpec mux_s = mux->getPort(ID::S);
+ int conn_width = ports[0].sig.size();
+ int conn_mux_offset = ports[0].mux_port_offset;
+ int conn_op_offset = ports[0].op_outsig_offset;
+
RTLIL::SigSpec shared_pmux_a = RTLIL::Const(RTLIL::State::Sx, max_width);
RTLIL::SigSpec shared_pmux_b;
RTLIL::SigSpec shared_pmux_s;
- int conn_width = ports[0].sig.size();
- int conn_offset = ports[0].mux_port_offset;
-
- shared_op->setPort(ID::Y, shared_op->getPort(ID::Y).extract(0, conn_width));
+ // Make a new wire to avoid false equivalence with whatever the former shared output was connected to.
+ Wire *new_out = module->addWire(NEW_ID, conn_op_offset + conn_width);
+ SigSpec new_sig_out = SigSpec(new_out, conn_op_offset, conn_width);
- if (mux->type == ID($pmux)) {
- shared_pmux_s = RTLIL::SigSpec();
-
- for (const auto &p : ports) {
+ for (int i = 0; i < GetSize(ports); i++) {
+ auto &p = ports[i];
+ auto &op = muxed_operands[i];
+ if (p.mux_port_id == GetSize(mux_s)) {
+ shared_pmux_a = op.sig;
+ mux_a.replace(conn_mux_offset, new_sig_out);
+ } else {
shared_pmux_s.append(mux_s[p.mux_port_id]);
- mux_b.replace(p.mux_port_id * mux_a.size() + conn_offset, shared_op->getPort(ID::Y));
+ shared_pmux_b.append(op.sig);
+ mux_b.replace(p.mux_port_id * mux_a.size() + conn_mux_offset, new_sig_out);
}
- } else {
- shared_pmux_s = RTLIL::SigSpec{mux_s, module->Not(NEW_ID, mux_s)};
- mux_a.replace(conn_offset, shared_op->getPort(ID::Y));
- mux_b.replace(conn_offset, shared_op->getPort(ID::Y));
}
mux->setPort(ID::A, mux_a);
mux->setPort(ID::B, mux_b);
- mux->setPort(ID::Y, mux_y);
mux->setPort(ID::S, mux_s);
- for (const auto &op : muxed_operands)
- shared_pmux_b.append(op.sig);
-
- auto mux_to_oper = module->Pmux(NEW_ID, shared_pmux_a, shared_pmux_b, shared_pmux_s);
+ SigSpec mux_to_oper;
+ if (GetSize(shared_pmux_s) == 1) {
+ mux_to_oper = module->Mux(NEW_ID, shared_pmux_a, shared_pmux_b, shared_pmux_s);
+ } else {
+ mux_to_oper = module->Pmux(NEW_ID, shared_pmux_a, shared_pmux_b, shared_pmux_s);
+ }
if (shared_op->type.in(ID($alu))) {
- RTLIL::SigSpec alu_x = shared_op->getPort(ID::X);
- RTLIL::SigSpec alu_co = shared_op->getPort(ID::CO);
-
- shared_op->setPort(ID::X, alu_x.extract(0, conn_width));
- shared_op->setPort(ID::CO, alu_co.extract(0, conn_width));
+ shared_op->setPort(ID::X, module->addWire(NEW_ID, GetSize(new_out)));
+ shared_op->setPort(ID::CO, module->addWire(NEW_ID, GetSize(new_out)));
}
bool is_fine = shared_op->type.in(FINE_BITWISE_OPS);
+ shared_op->setPort(ID::Y, new_out);
if (!is_fine)
- shared_op->setParam(ID::Y_WIDTH, conn_width);
+ shared_op->setParam(ID::Y_WIDTH, GetSize(new_out));
- if (decode_port(shared_op, ID::A, &assign_map) == operand) {
+ if (decode_port(shared_op, ID::A, sigmap) == operand) {
shared_op->setPort(ID::B, mux_to_oper);
if (!is_fine)
shared_op->setParam(ID::B_WIDTH, max_width);
@@ -275,17 +272,7 @@ typedef struct {
} merged_op_t;
-template <typename T> void remove_val(std::vector<T> &v, const std::vector<T> &vals)
-{
- auto val_iter = vals.rbegin();
- for (auto i = v.rbegin(); i != v.rend(); ++i)
- if ((val_iter != vals.rend()) && (*i == *val_iter)) {
- v.erase(i.base() - 1);
- ++val_iter;
- }
-}
-
-void check_muxed_operands(std::vector<const OpMuxConn *> &ports, const ExtSigSpec &shared_operand)
+void check_muxed_operands(std::vector<const OpMuxConn *> &ports, const ExtSigSpec &shared_operand, const SigMap &sigmap)
{
auto it = ports.begin();
ExtSigSpec seed;
@@ -295,11 +282,11 @@ void check_muxed_operands(std::vector<const OpMuxConn *> &ports, const ExtSigSpe
auto op = p->op;
RTLIL::IdString muxed_port_name = ID::A;
- if (decode_port(op, ID::A, &assign_map) == shared_operand) {
+ if (decode_port(op, ID::A, sigmap) == shared_operand) {
muxed_port_name = ID::B;
}
- auto operand = decode_port(op, muxed_port_name, &assign_map);
+ auto operand = decode_port(op, muxed_port_name, sigmap);
if (seed.empty())
seed = operand;
@@ -312,7 +299,7 @@ void check_muxed_operands(std::vector<const OpMuxConn *> &ports, const ExtSigSpe
}
}
-ExtSigSpec find_shared_operand(const OpMuxConn* seed, std::vector<const OpMuxConn *> &ports, const std::map<ExtSigSpec, std::set<RTLIL::Cell *>> &operand_to_users)
+ExtSigSpec find_shared_operand(const OpMuxConn* seed, std::vector<const OpMuxConn *> &ports, const std::map<ExtSigSpec, std::set<RTLIL::Cell *>> &operand_to_users, const SigMap &sigmap)
{
std::set<RTLIL::Cell *> ops_using_operand;
std::set<RTLIL::Cell *> ops_set;
@@ -324,7 +311,7 @@ ExtSigSpec find_shared_operand(const OpMuxConn* seed, std::vector<const OpMuxCon
auto op_a = seed->op;
for (RTLIL::IdString port_name : {ID::A, ID::B}) {
- oper = decode_port(op_a, port_name, &assign_map);
+ oper = decode_port(op_a, port_name, sigmap);
auto operand_users = operand_to_users.at(oper);
if (operand_users.size() == 1)
@@ -345,132 +332,6 @@ ExtSigSpec find_shared_operand(const OpMuxConn* seed, std::vector<const OpMuxCon
return ExtSigSpec();
}
-dict<RTLIL::SigSpec, OpMuxConn> find_valid_op_mux_conns(RTLIL::Module *module, dict<RTLIL::SigBit, RTLIL::SigSpec> &op_outbit_to_outsig,
- dict<RTLIL::SigSpec, RTLIL::Cell *> outsig_to_operator,
- dict<RTLIL::SigBit, RTLIL::SigSpec> &op_aux_to_outsig)
-{
- dict<RTLIL::SigSpec, int> op_outsig_user_track;
- dict<RTLIL::SigSpec, OpMuxConn> op_mux_conn_map;
-
- std::function<void(RTLIL::SigSpec)> remove_outsig = [&](RTLIL::SigSpec outsig) {
- for (auto op_outbit : outsig)
- op_outbit_to_outsig.erase(op_outbit);
-
- if (op_mux_conn_map.count(outsig))
- op_mux_conn_map.erase(outsig);
- };
-
- std::function<void(RTLIL::SigBit)> remove_outsig_from_aux_bit = [&](RTLIL::SigBit auxbit) {
- auto aux_outsig = op_aux_to_outsig.at(auxbit);
- auto op = outsig_to_operator.at(aux_outsig);
- auto op_outsig = assign_map(op->getPort(ID::Y));
- remove_outsig(op_outsig);
-
- for (auto aux_outbit : aux_outsig)
- op_aux_to_outsig.erase(aux_outbit);
- };
-
- std::function<void(RTLIL::Cell *)> find_op_mux_conns = [&](RTLIL::Cell *mux) {
- RTLIL::SigSpec sig;
- int mux_port_size;
-
- if (mux->type.in(ID($mux), ID($_MUX_))) {
- mux_port_size = mux->getPort(ID::A).size();
- sig = RTLIL::SigSpec{mux->getPort(ID::B), mux->getPort(ID::A)};
- } else {
- mux_port_size = mux->getPort(ID::A).size();
- sig = mux->getPort(ID::B);
- }
-
- auto mux_insig = assign_map(sig);
-
- for (int i = 0; i < mux_insig.size(); ++i) {
- if (op_aux_to_outsig.count(mux_insig[i])) {
- remove_outsig_from_aux_bit(mux_insig[i]);
- continue;
- }
-
- if (!op_outbit_to_outsig.count(mux_insig[i]))
- continue;
-
- auto op_outsig = op_outbit_to_outsig.at(mux_insig[i]);
-
- if (op_mux_conn_map.count(op_outsig)) {
- remove_outsig(op_outsig);
- continue;
- }
-
- int mux_port_id = i / mux_port_size;
- int mux_port_offset = i % mux_port_size;
-
- int op_outsig_offset;
- for (op_outsig_offset = 0; op_outsig[op_outsig_offset] != mux_insig[i]; ++op_outsig_offset)
- ;
-
- int j = op_outsig_offset;
- do {
- if (!op_outbit_to_outsig.count(mux_insig[i]))
- break;
-
- if (op_outbit_to_outsig.at(mux_insig[i]) != op_outsig)
- break;
-
- ++i;
- ++j;
- } while ((i / mux_port_size == mux_port_id) && (j < op_outsig.size()));
-
- int op_conn_width = j - op_outsig_offset;
- OpMuxConn inp = {
- op_outsig.extract(op_outsig_offset, op_conn_width),
- mux,
- outsig_to_operator.at(op_outsig),
- mux_port_id,
- mux_port_offset,
- op_outsig_offset,
- };
-
- op_mux_conn_map[op_outsig] = inp;
-
- --i;
- }
- };
-
- std::function<void(RTLIL::SigSpec)> remove_connected_ops = [&](RTLIL::SigSpec sig) {
- auto mux_insig = assign_map(sig);
- for (auto outbit : mux_insig) {
- if (op_aux_to_outsig.count(outbit)) {
- remove_outsig_from_aux_bit(outbit);
- continue;
- }
-
- if (!op_outbit_to_outsig.count(outbit))
- continue;
-
- remove_outsig(op_outbit_to_outsig.at(outbit));
- }
- };
-
- for (auto cell : module->cells()) {
- if (cell->type.in(ID($mux), ID($_MUX_), ID($pmux))) {
- remove_connected_ops(cell->getPort(ID::S));
- find_op_mux_conns(cell);
- } else {
- for (auto &conn : cell->connections())
- if (cell->input(conn.first))
- remove_connected_ops(conn.second);
- }
- }
-
- for (auto w : module->wires()) {
- if (!w->port_output)
- continue;
-
- remove_connected_ops(w);
- }
-
- return op_mux_conn_map;
-}
-
struct OptSharePass : public Pass {
OptSharePass() : Pass("opt_share", "merge mutually exclusive cells of the same type that share an input signal") {}
void help() override
@@ -495,37 +356,46 @@ struct OptSharePass : public Pass {
extra_args(args, 1, design);
for (auto module : design->selected_modules()) {
- assign_map.clear();
- assign_map.set(module);
+ SigMap sigmap(module);
+
+ dict<RTLIL::SigBit, int> bit_users;
+
+ for (auto cell : module->cells())
+ for (auto conn : cell->connections())
+ for (auto bit : conn.second)
+ bit_users[sigmap(bit)]++;
+
+ for (auto wire : module->wires())
+ if (wire->port_id != 0)
+ for (auto bit : SigSpec(wire))
+ bit_users[sigmap(bit)]++;
std::map<ExtSigSpec, std::set<RTLIL::Cell *>> operand_to_users;
- dict<RTLIL::SigSpec, RTLIL::Cell *> outsig_to_operator;
- dict<RTLIL::SigBit, RTLIL::SigSpec> op_outbit_to_outsig;
- dict<RTLIL::SigBit, RTLIL::SigSpec> op_aux_to_outsig;
+ dict<RTLIL::SigBit, std::pair<RTLIL::Cell *, int>> op_outbit_to_outsig;
bool any_shared_operands = false;
- std::vector<ExtSigSpec> op_insigs;
- for (auto cell : module->cells()) {
+ for (auto cell : module->selected_cells()) {
if (!cell_supported(cell))
continue;
+ bool skip = false;
if (cell->type == ID($alu)) {
for (RTLIL::IdString port_name : {ID::X, ID::CO}) {
- auto mux_insig = assign_map(cell->getPort(port_name));
- outsig_to_operator[mux_insig] = cell;
- for (auto outbit : mux_insig)
- op_aux_to_outsig[outbit] = mux_insig;
+ for (auto outbit : sigmap(cell->getPort(port_name)))
+ if (bit_users[outbit] > 1)
+ skip = true;
}
}
- auto mux_insig = assign_map(cell->getPort(ID::Y));
- outsig_to_operator[mux_insig] = cell;
- for (auto outbit : mux_insig)
- op_outbit_to_outsig[outbit] = mux_insig;
+ if (skip)
+ continue;
+
+ auto mux_insig = sigmap(cell->getPort(ID::Y));
+ for (int i = 0; i < GetSize(mux_insig); i++)
+ op_outbit_to_outsig[mux_insig[i]] = std::make_pair(cell, i);
for (RTLIL::IdString port_name : {ID::A, ID::B}) {
- auto op_insig = decode_port(cell, port_name, &assign_map);
- op_insigs.push_back(op_insig);
+ auto op_insig = decode_port(cell, port_name, sigmap);
operand_to_users[op_insig].insert(cell);
if (operand_to_users[op_insig].size() > 1)
any_shared_operands = true;
@@ -537,34 +407,79 @@ struct OptSharePass : public Pass {
// Operator outputs need to be exclusively connected to the $mux inputs in order to be mergeable. Hence we count to
// how many points are operator output bits connected.
- dict<RTLIL::SigSpec, OpMuxConn> op_mux_conn_map =
- find_valid_op_mux_conns(module, op_outbit_to_outsig, outsig_to_operator, op_aux_to_outsig);
+ std::vector<merged_op_t> merged_ops;
- // Group op connections connected to same ports of the same $mux. Sort them in ascending order of their port offset
- dict<RTLIL::Cell*, std::vector<std::set<OpMuxConn>>> mux_port_op_conns;
- for (auto& val: op_mux_conn_map) {
- OpMuxConn p = val.second;
- auto& mux_port_conns = mux_port_op_conns[p.mux];
+ for (auto mux : module->selected_cells()) {
+ if (!mux->type.in(ID($mux), ID($_MUX_), ID($pmux)))
+ continue;
- if (mux_port_conns.size() == 0) {
- int mux_port_num;
+ int mux_port_size = GetSize(mux->getPort(ID::A));
+ int mux_port_num = GetSize(mux->getPort(ID::S)) + 1;
- if (p.mux->type.in(ID($mux), ID($_MUX_)))
- mux_port_num = 2;
- else
- mux_port_num = p.mux->getPort(ID::S).size();
+ RTLIL::SigSpec mux_insig = sigmap(RTLIL::SigSpec{mux->getPort(ID::B), mux->getPort(ID::A)});
+ std::vector<std::set<OpMuxConn>> mux_port_conns(mux_port_num);
+ int found = 0;
- mux_port_conns.resize(mux_port_num);
- }
+ for (int mux_port_id = 0; mux_port_id < mux_port_num; mux_port_id++) {
+ SigSpec mux_insig;
+ if (mux_port_id == mux_port_num - 1) {
+ mux_insig = sigmap(mux->getPort(ID::A));
+ } else {
+ mux_insig = sigmap(mux->getPort(ID::B).extract(mux_port_id * mux_port_size, mux_port_size));
+ }
- mux_port_conns[p.mux_port_id].insert(p);
- }
+ for (int mux_port_offset = 0; mux_port_offset < mux_port_size; ++mux_port_offset) {
+ if (!op_outbit_to_outsig.count(mux_insig[mux_port_offset]))
+ continue;
- std::vector<merged_op_t> merged_ops;
- for (auto& val: mux_port_op_conns) {
+ RTLIL::Cell *cell;
+ int op_outsig_offset;
+ std::tie(cell, op_outsig_offset) = op_outbit_to_outsig.at(mux_insig[mux_port_offset]);
+ SigSpec op_outsig = sigmap(cell->getPort(ID::Y));
+ int op_outsig_size = GetSize(op_outsig);
+ int op_conn_width = 0;
+
+ while (mux_port_offset + op_conn_width < mux_port_size &&
+ op_outsig_offset + op_conn_width < op_outsig_size &&
+ mux_insig[mux_port_offset + op_conn_width] == op_outsig[op_outsig_offset + op_conn_width])
+ op_conn_width++;
+
+ log_assert(op_conn_width >= 1);
+
+ bool skip = false;
+ for (int i = 0; i < op_outsig_size; i++) {
+ int expected = 1;
+ if (i >= op_outsig_offset && i < op_outsig_offset + op_conn_width)
+ expected = 2;
+ if (bit_users[op_outsig[i]] != expected)
+ skip = true;
+ }
+ if (skip) {
+ mux_port_offset += op_conn_width;
+ mux_port_offset--;
+ continue;
+ }
+
+ OpMuxConn inp = {
+ op_outsig.extract(op_outsig_offset, op_conn_width),
+ mux,
+ cell,
+ mux_port_id,
+ mux_port_offset,
+ op_outsig_offset,
+ };
+
+ mux_port_conns[mux_port_id].insert(inp);
+
+ mux_port_offset += op_conn_width;
+ mux_port_offset--;
- RTLIL::Cell* cell = val.first;
- auto &mux_port_conns = val.second;
+ found++;
+ }
+ }
+
+ if (found < 2)
+ continue;
const OpMuxConn *seed = NULL;
@@ -612,12 +527,12 @@ struct OptSharePass : public Pass {
continue;
// Filter mergeable connections whose ops share an operand with seed connection's op
- auto shared_operand = find_shared_operand(seed, mergeable_conns, operand_to_users);
+ auto shared_operand = find_shared_operand(seed, mergeable_conns, operand_to_users, sigmap);
if (shared_operand.empty())
continue;
- check_muxed_operands(mergeable_conns, shared_operand);
+ check_muxed_operands(mergeable_conns, shared_operand, sigmap);
if (mergeable_conns.size() < 2)
continue;
@@ -631,7 +546,7 @@ struct OptSharePass : public Pass {
seed = NULL;
- merged_ops.push_back(merged_op_t{cell, merged_ports, shared_operand});
+ merged_ops.push_back(merged_op_t{mux, merged_ports, shared_operand});
design->scratchpad_set_bool("opt.did_something", true);
}
@@ -647,7 +562,7 @@ struct OptSharePass : public Pass {
log(" %s\n", log_id(op.op));
log("\n");
- merge_operators(module, shared.mux, shared.ports, shared.shared_operand);
+ merge_operators(module, shared.mux, shared.ports, shared.shared_operand, sigmap);
}
}
}
diff --git a/passes/opt/pmux2shiftx.cc b/passes/opt/pmux2shiftx.cc
index 9f226e12d..90ddf8dd7 100644
--- a/passes/opt/pmux2shiftx.cc
+++ b/passes/opt/pmux2shiftx.cc
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -19,6 +19,7 @@
#include "kernel/yosys.h"
#include "kernel/sigtools.h"
+#include "kernel/ffinit.h"
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
@@ -30,7 +31,7 @@ struct OnehotDatabase
bool verbose = false;
bool initialized = false;
- pool<SigBit> init_ones;
+ FfInitVals initvals;
dict<SigSpec, pool<SigSpec>> sig_sources_db;
dict<SigSpec, bool> sig_onehot_cache;
pool<SigSpec> recursion_guard;
@@ -44,19 +45,7 @@ struct OnehotDatabase
log_assert(!initialized);
initialized = true;
- for (auto wire : module->wires())
- {
- auto it = wire->attributes.find(ID::init);
- if (it == wire->attributes.end())
- continue;
-
- auto &val = it->second;
- int width = std::max(GetSize(wire), GetSize(val));
-
- for (int i = 0; i < width; i++)
- if (val[i] == State::S1)
- init_ones.insert(sigmap(SigBit(wire, i)));
- }
+ initvals.set(&sigmap, module);
for (auto cell : module->cells())
{
@@ -119,7 +108,7 @@ struct OnehotDatabase
bool found_init_ones = false;
for (auto bit : sig) {
- if (init_ones.count(bit)) {
+ if (initvals(bit) == State::S1) {
if (found_init_ones) {
if (verbose)
log("%*s - non-onehot init value\n", indent, "");
diff --git a/passes/opt/rmports.cc b/passes/opt/rmports.cc
index 99a2a61c8..9fa9f5c2d 100644
--- a/passes/opt/rmports.cc
+++ b/passes/opt/rmports.cc
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
diff --git a/passes/opt/share.cc b/passes/opt/share.cc
index f7848e01d..abef71937 100644
--- a/passes/opt/share.cc
+++ b/passes/opt/share.cc
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -18,7 +18,7 @@
*/
#include "kernel/yosys.h"
-#include "kernel/satgen.h"
+#include "kernel/qcsat.h"
#include "kernel/sigtools.h"
#include "kernel/modtools.h"
#include "kernel/utils.h"
@@ -58,8 +58,6 @@ struct ShareWorker
std::map<RTLIL::Cell*, std::set<RTLIL::Cell*, cell_ptr_cmp>, cell_ptr_cmp> topo_cell_drivers;
std::map<RTLIL::SigBit, std::set<RTLIL::Cell*, cell_ptr_cmp>> topo_bit_drivers;
- std::vector<std::pair<RTLIL::SigBit, RTLIL::SigBit>> exclusive_ctrls;
-
// ------------------------------------------------------------------------------
// Find terminal bits -- i.e. bits that do not (exclusively) feed into a mux tree
@@ -368,7 +366,7 @@ struct ShareWorker
continue;
}
- if (cell->type == ID($memrd)) {
+ if (cell->type.in(ID($memrd), ID($memrd_v2))) {
if (cell->parameters.at(ID::CLK_ENABLE).as_bool())
continue;
if (config.opt_aggressive || !modwalker.sigmap(cell->getPort(ID::ADDR)).is_fully_const())
@@ -401,11 +399,14 @@ struct ShareWorker
if (c1->type != c2->type)
return false;
- if (c1->type == ID($memrd))
+ if (c1->type.in(ID($memrd), ID($memrd_v2)))
{
if (c1->parameters.at(ID::MEMID).decode_string() != c2->parameters.at(ID::MEMID).decode_string())
return false;
+ if (c1->parameters.at(ID::WIDTH) != c2->parameters.at(ID::WIDTH))
+ return false;
+
return true;
}
@@ -705,7 +706,7 @@ struct ShareWorker
return supercell;
}
- if (c1->type == ID($memrd))
+ if (c1->type.in(ID($memrd), ID($memrd_v2)))
{
RTLIL::Cell *supercell = module->addCell(NEW_ID, c1);
RTLIL::SigSpec addr1 = c1->getPort(ID::ADDR);
@@ -1156,7 +1157,6 @@ struct ShareWorker
recursion_state.clear();
topo_cell_drivers.clear();
topo_bit_drivers.clear();
- exclusive_ctrls.clear();
terminal_bits.clear();
shareable_cells.clear();
forbidden_controls_cache.clear();
@@ -1171,13 +1171,6 @@ struct ShareWorker
log("Found %d cells in module %s that may be considered for resource sharing.\n",
GetSize(shareable_cells), log_id(module));
- for (auto cell : module->cells())
- if (cell->type == ID($pmux))
- for (auto bit : cell->getPort(ID::S))
- for (auto other_bit : cell->getPort(ID::S))
- if (bit < other_bit)
- exclusive_ctrls.push_back(std::pair<RTLIL::SigBit, RTLIL::SigBit>(bit, other_bit));
-
while (!shareable_cells.empty() && config.limit != 0)
{
RTLIL::Cell *cell = *shareable_cells.begin();
@@ -1256,8 +1249,11 @@ struct ShareWorker
optimize_activation_patterns(filtered_cell_activation_patterns);
optimize_activation_patterns(filtered_other_cell_activation_patterns);
- ezSatPtr ez;
- SatGen satgen(ez.get(), &modwalker.sigmap);
+ QuickConeSat qcsat(modwalker);
+ if (config.opt_fast) {
+ qcsat.max_cell_outs = 3;
+ qcsat.max_cell_count = 100;
+ }
pool<RTLIL::Cell*> sat_cells;
std::set<RTLIL::SigBit> bits_queue;
@@ -1267,77 +1263,45 @@ struct ShareWorker
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)));
+ cell_active.push_back(qcsat.ez->vec_eq(qcsat.importSig(p.first), qcsat.importSig(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)));
+ other_cell_active.push_back(qcsat.ez->vec_eq(qcsat.importSig(p.first), qcsat.importSig(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())
- {
- pool<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;
- }
-
- for (auto it : exclusive_ctrls)
- if (satgen.importedSigBit(it.first) && satgen.importedSigBit(it.second)) {
- log(" Adding exclusive control bits: %s vs. %s\n", log_signal(it.first), log_signal(it.second));
- int sub1 = satgen.importSigBit(it.first);
- int sub2 = satgen.importSigBit(it.second);
- ez->assume(ez->NOT(ez->AND(sub1, sub2)));
- }
+ qcsat.prepare();
- if (!ez->solve(ez->expression(ez->OpOr, cell_active))) {
+ int sub1 = qcsat.ez->expression(qcsat.ez->OpOr, cell_active);
+ if (!qcsat.ez->solve(sub1)) {
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))) {
+ int sub2 = qcsat.ez->expression(qcsat.ez->OpOr, other_cell_active);
+ if (!qcsat.ez->solve(sub2)) {
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();
+ qcsat.ez->non_incremental();
all_ctrl_signals.sort_and_unify();
- std::vector<int> sat_model = satgen.importSigSpec(all_ctrl_signals);
+ std::vector<int> sat_model = qcsat.importSig(all_ctrl_signals);
std::vector<bool> sat_model_values;
- int sub1 = ez->expression(ez->OpOr, cell_active);
- int sub2 = ez->expression(ez->OpOr, other_cell_active);
- ez->assume(ez->AND(sub1, sub2));
+ qcsat.ez->assume(qcsat.ez->AND(sub1, sub2));
log(" Size of SAT problem: %d cells, %d variables, %d clauses\n",
- GetSize(sat_cells), ez->numCnfVariables(), ez->numCnfClauses());
+ GetSize(sat_cells), qcsat.ez->numCnfVariables(), qcsat.ez->numCnfClauses());
- if (ez->solve(sat_model, sat_model_values)) {
+ if (qcsat.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), GetSize(sat_model_values));
for (int i = GetSize(sat_model_values)-1; i >= 0; i--)
diff --git a/passes/opt/wreduce.cc b/passes/opt/wreduce.cc
index 78e2bcbea..aaad28ef0 100644
--- a/passes/opt/wreduce.cc
+++ b/passes/opt/wreduce.cc
@@ -1,7 +1,7 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -20,6 +20,7 @@
#include "kernel/yosys.h"
#include "kernel/sigtools.h"
#include "kernel/modtools.h"
+#include "kernel/ffinit.h"
USING_YOSYS_NAMESPACE
@@ -54,8 +55,7 @@ struct WreduceWorker
std::set<Cell*, IdString::compare_ptr_by_name<Cell>> work_queue_cells;
std::set<SigBit> work_queue_bits;
pool<SigBit> keep_bits;
- dict<SigBit, State> init_bits;
- pool<SigBit> remove_init_bits;
+ FfInitVals initvals;
WreduceWorker(WreduceConfig *config, Module *module) :
config(config), module(module), mi(module) { }
@@ -145,7 +145,7 @@ struct WreduceWorker
SigSpec sig_d = mi.sigmap(cell->getPort(ID::D));
SigSpec sig_q = mi.sigmap(cell->getPort(ID::Q));
bool has_reset = false;
- Const initval, rst_value;
+ Const initval = initvals(sig_q), rst_value;
int width_before = GetSize(sig_q);
@@ -163,20 +163,12 @@ struct WreduceWorker
bool zero_ext = sig_d[GetSize(sig_d)-1] == State::S0;
bool sign_ext = !zero_ext;
- for (int i = 0; i < GetSize(sig_q); i++) {
- SigBit bit = sig_q[i];
- if (init_bits.count(bit))
- initval.bits.push_back(init_bits.at(bit));
- else
- initval.bits.push_back(State::Sx);
- }
-
for (int i = GetSize(sig_q)-1; i >= 0; i--)
{
if (zero_ext && sig_d[i] == State::S0 && (initval[i] == State::S0 || initval[i] == State::Sx) &&
(!has_reset || i >= GetSize(rst_value) || rst_value[i] == State::S0 || rst_value[i] == State::Sx)) {
module->connect(sig_q[i], State::S0);
- remove_init_bits.insert(sig_q[i]);
+ initvals.remove_init(sig_q[i]);
sig_d.remove(i);
sig_q.remove(i);
continue;
@@ -185,7 +177,7 @@ struct WreduceWorker
if (sign_ext && i > 0 && sig_d[i] == sig_d[i-1] && initval[i] == initval[i-1] &&
(!has_reset || i >= GetSize(rst_value) || rst_value[i] == rst_value[i-1])) {
module->connect(sig_q[i], sig_q[i-1]);
- remove_init_bits.insert(sig_q[i]);
+ initvals.remove_init(sig_q[i]);
sig_d.remove(i);
sig_q.remove(i);
continue;
@@ -195,7 +187,7 @@ struct WreduceWorker
if (info == nullptr)
return;
if (!info->is_output && GetSize(info->ports) == 1 && !keep_bits.count(mi.sigmap(sig_q[i]))) {
- remove_init_bits.insert(sig_q[i]);
+ initvals.remove_init(sig_q[i]);
sig_d.remove(i);
sig_q.remove(i);
zero_ext = false;
@@ -409,18 +401,12 @@ struct WreduceWorker
{
// create a copy as mi.sigmap will be updated as we process the module
SigMap init_attr_sigmap = mi.sigmap;
+ initvals.set(&init_attr_sigmap, module);
for (auto w : module->wires()) {
if (w->get_bool_attribute(ID::keep))
for (auto bit : mi.sigmap(w))
keep_bits.insert(bit);
- if (w->attributes.count(ID::init)) {
- Const initval = w->attributes.at(ID::init);
- SigSpec initsig = init_attr_sigmap(w);
- int width = std::min(GetSize(initval), GetSize(initsig));
- for (int i = 0; i < width; i++)
- init_bits[initsig[i]] = initval[i];
- }
}
for (auto c : module->selected_cells())
@@ -469,22 +455,6 @@ struct WreduceWorker
module->connect(nw, SigSpec(w).extract(0, GetSize(nw)));
module->swap_names(w, nw);
}
-
- if (!remove_init_bits.empty()) {
- for (auto w : module->wires()) {
- if (w->attributes.count(ID::init)) {
- Const initval = w->attributes.at(ID::init);
- Const new_initval(State::Sx, GetSize(w));
- SigSpec initsig = init_attr_sigmap(w);
- int width = std::min(GetSize(initval), GetSize(initsig));
- for (int i = 0; i < width; i++) {
- if (!remove_init_bits.count(initsig[i]))
- new_initval[i] = initval[i];
- }
- w->attributes.at(ID::init) = new_initval;
- }
- }
- }
}
};
@@ -588,7 +558,7 @@ struct WreducePass : public Pass {
}
}
- if (!opt_memx && c->type.in(ID($memrd), ID($memwr), ID($meminit))) {
+ if (!opt_memx && c->type.in(ID($memrd), ID($memrd_v2), ID($memwr), ID($memwr_v2), ID($meminit), ID($meminit_v2))) {
IdString memid = c->getParam(ID::MEMID).decode_string();
RTLIL::Memory *mem = module->memories.at(memid);
if (mem->start_offset >= 0) {