aboutsummaryrefslogtreecommitdiffstats
path: root/passes/opt
diff options
context:
space:
mode:
Diffstat (limited to 'passes/opt')
-rw-r--r--passes/opt/Makefile.inc2
-rw-r--r--passes/opt/muxpack.cc4
-rw-r--r--passes/opt/opt.cc47
-rw-r--r--passes/opt/opt_clean.cc66
-rw-r--r--passes/opt/opt_demorgan.cc4
-rw-r--r--passes/opt/opt_dff.cc875
-rw-r--r--passes/opt/opt_expr.cc69
-rw-r--r--passes/opt/opt_lut.cc4
-rw-r--r--passes/opt/opt_lut_ins.cc4
-rw-r--r--passes/opt/opt_mem.cc91
-rw-r--r--passes/opt/opt_merge.cc12
-rw-r--r--passes/opt/opt_muxtree.cc4
-rw-r--r--passes/opt/opt_reduce.cc4
-rw-r--r--passes/opt/opt_rmdff.cc711
-rw-r--r--passes/opt/opt_share.cc365
-rw-r--r--passes/opt/pmux2shiftx.cc33
-rw-r--r--passes/opt/rmports.cc4
-rw-r--r--passes/opt/share.cc4
-rw-r--r--passes/opt/wreduce.cc72
19 files changed, 1219 insertions, 1156 deletions
diff --git a/passes/opt/Makefile.inc b/passes/opt/Makefile.inc
index 3133927bb..4ae9b8895 100644
--- a/passes/opt/Makefile.inc
+++ b/passes/opt/Makefile.inc
@@ -4,7 +4,7 @@ OBJS += passes/opt/opt_merge.o
OBJS += passes/opt/opt_mem.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 9df49ab3c..aa5f82437 100644
--- a/passes/opt/muxpack.cc
+++ b/passes/opt/muxpack.cc
@@ -326,7 +326,7 @@ struct MuxpackWorker
struct MuxpackPass : public Pass {
MuxpackPass() : Pass("muxpack", "$mux/$pmux cascades to $pmux") { }
- void help() YS_OVERRIDE
+ void help() override
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -341,7 +341,7 @@ struct MuxpackPass : public Pass {
log("certain that their select inputs are mutually exclusive.\n");
log("\n");
}
- void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ void execute(std::vector<std::string> args, RTLIL::Design *design) override
{
log_header(design, "Executing MUXPACK pass ($mux cell cascades to $pmux).\n");
diff --git a/passes/opt/opt.cc b/passes/opt/opt.cc
index 396819883..4b052d9a2 100644
--- a/passes/opt/opt.cc
+++ b/passes/opt/opt.cc
@@ -27,7 +27,7 @@ PRIVATE_NAMESPACE_BEGIN
struct OptPass : public Pass {
OptPass() : Pass("opt", "perform simple optimizations") { }
- void help() YS_OVERRIDE
+ void help() override
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -37,42 +37,43 @@ 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");
log(" opt_muxtree\n");
log(" opt_reduce [-fine] [-full]\n");
log(" opt_merge [-share_all]\n");
- log(" opt_share (-full only)\n");
- log(" opt_rmdff [-keepdc] [-sat]\n");
+ log(" opt_share (-full only)\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]\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");
log("\n");
log("\n");
}
- void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ void execute(std::vector<std::string> args, RTLIL::Design *design) override
{
std::string opt_clean_args;
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;
log_header(design, "Executing OPT pass (performing simple optimizations).\n");
log_push();
@@ -95,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") {
@@ -112,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") {
@@ -127,6 +136,10 @@ struct OptPass : public Pass {
fast_mode = true;
continue;
}
+ if (args[argidx] == "-noff") {
+ noff_mode = true;
+ continue;
+ }
break;
}
extra_args(args, argidx, design);
@@ -137,7 +150,8 @@ struct OptPass : public Pass {
Pass::call(design, "opt_expr" + opt_expr_args);
Pass::call(design, "opt_merge" + opt_merge_args);
design->scratchpad_unset("opt.did_something");
- Pass::call(design, "opt_rmdff" + opt_rmdff_args);
+ if (!noff_mode)
+ 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);
@@ -156,7 +170,8 @@ struct OptPass : public Pass {
Pass::call(design, "opt_merge" + opt_merge_args);
if (opt_share)
Pass::call(design, "opt_share");
- Pass::call(design, "opt_rmdff" + opt_rmdff_args);
+ if (!noff_mode)
+ 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 f7de02164..883374cf6 100644
--- a/passes/opt/opt_clean.cc
+++ b/passes/opt/opt_clean.cc
@@ -59,6 +59,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 +72,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,6 +95,8 @@ 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;
@@ -103,6 +110,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($meminit))) {
+ 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 +158,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 == ID($memrd)) {
+ 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>());
@@ -163,6 +197,14 @@ void rmunused_module_cells(Module *module, bool verbose)
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 +244,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 +257,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);
@@ -526,7 +568,7 @@ void rmunused_module(RTLIL::Module *module, bool purge_mode, bool verbose, bool
struct OptCleanPass : public Pass {
OptCleanPass() : Pass("opt_clean", "remove unused cells and wires") { }
- void help() YS_OVERRIDE
+ void help() override
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -543,7 +585,7 @@ struct OptCleanPass : public Pass {
log(" also remove internal nets if they have a public name\n");
log("\n");
}
- void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ void execute(std::vector<std::string> args, RTLIL::Design *design) override
{
bool purge_mode = false;
@@ -592,7 +634,7 @@ struct OptCleanPass : public Pass {
struct CleanPass : public Pass {
CleanPass() : Pass("clean", "remove unused cells and wires") { }
- void help() YS_OVERRIDE
+ void help() override
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -607,7 +649,7 @@ struct CleanPass : public Pass {
log("in -purge mode between the commands.\n");
log("\n");
}
- void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ void execute(std::vector<std::string> args, RTLIL::Design *design) override
{
bool purge_mode = false;
diff --git a/passes/opt/opt_demorgan.cc b/passes/opt/opt_demorgan.cc
index 4bc82815b..f0fa86f42 100644
--- a/passes/opt/opt_demorgan.cc
+++ b/passes/opt/opt_demorgan.cc
@@ -169,7 +169,7 @@ void demorgan_worker(
struct OptDemorganPass : public Pass {
OptDemorganPass() : Pass("opt_demorgan", "Optimize reductions with DeMorgan equivalents") { }
- void help() YS_OVERRIDE
+ void help() override
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -179,7 +179,7 @@ struct OptDemorganPass : public Pass {
log("overall gate count of the circuit\n");
log("\n");
}
- void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ void execute(std::vector<std::string> args, RTLIL::Design *design) override
{
log_header(design, "Executing OPT_DEMORGAN pass (push inverters through $reduce_* cells).\n");
diff --git a/passes/opt/opt_dff.cc b/passes/opt/opt_dff.cc
new file mode 100644
index 000000000..a47071a30
--- /dev/null
+++ b/passes/opt/opt_dff.cc
@@ -0,0 +1,875 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * 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/satgen.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;
+ dict<SigBit, RTLIL::Cell*> bit2driver;
+
+ 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;
+
+ ezSatPtr ez;
+ SatGen satgen;
+ pool<Cell*> sat_cells;
+
+ // Used as a queue.
+ std::vector<Cell *> dff_cells;
+
+ OptDffWorker(const OptDffOptions &opt, Module *mod) : opt(opt), module(mod), sigmap(mod), initvals(&sigmap, mod), ez(), satgen(ez.get(), &sigmap) {
+ // Gathering three 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
+ // - bit2driver: the cell driving 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) {
+ for (auto bit : sigmap(conn.second))
+ bit2driver[bit] = cell;
+ }
+ 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);
+ }
+
+ }
+
+ 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 : sigmap(conn.second))
+ if (bit2driver.count(bit))
+ sat_import_cell(bit2driver.at(bit));
+ }
+ };
+
+ 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) {
+ module->remove(cell);
+ 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_q[i], ff.sig_clr[i]);
+ } else {
+ module->addNot(NEW_ID, ff.sig_q[i], ff.sig_clr[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);
+ 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_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));
+ initvals.remove_init(ff.sig_q);
+ module->remove(cell);
+ 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_en = false;
+ ff.sig_d = ff.val_d = ff.val_srst;
+ ff.d_is_const = true;
+ changed = true;
+ }
+ }
+
+ if (ff.has_en) {
+ if (ff.sig_en == (ff.pol_en ? State::S0 : State::S1) || (!opt.keepdc && ff.sig_en == State::Sx)) {
+ // Always-inactive enable — remove.
+ if (ff.has_clk && 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_en = ff.pol_srst;
+ ff.sig_en = ff.sig_srst;
+ ff.has_srst = false;
+ ff.sig_d = ff.val_d = ff.val_srst;
+ ff.d_is_const = true;
+ 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 const-input D latch, SR latch, or a const driver).
+ ff.has_d = ff.has_en = ff.has_clk = false;
+ changed = true;
+ }
+ } else if (ff.sig_en == (ff.pol_en ? State::S1 : State::S0)) {
+ // Always-active enable.
+ if (ff.has_clk) {
+ // 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_en = false;
+ changed = true;
+ } else {
+ // For latches, make a comb circuit, nuke the latch.
+ log("Handling always-active EN on %s (%s) from module %s (changing to combinatorial circuit).\n",
+ log_id(cell), log_id(cell->type), log_id(module));
+ initvals.remove_init(ff.sig_q);
+ module->remove(cell);
+ if (ff.has_sr) {
+ SigSpec tmp;
+ if (ff.is_fine) {
+ if (ff.pol_set)
+ tmp = module->MuxGate(NEW_ID, ff.sig_d, State::S1, ff.sig_set);
+ else
+ tmp = module->MuxGate(NEW_ID, State::S1, ff.sig_d, 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_d, ff.sig_set);
+ else
+ tmp = module->Or(NEW_ID, ff.sig_d, 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_d, ff.val_arst[0], ff.sig_arst, ff.sig_q);
+ else
+ module->addMuxGate(NEW_ID, ff.val_arst[0], ff.sig_d, ff.sig_arst, ff.sig_q);
+ } else {
+ if (ff.pol_arst)
+ module->addMux(NEW_ID, ff.sig_d, ff.val_arst, ff.sig_arst, ff.sig_q);
+ else
+ module->addMux(NEW_ID, ff.val_arst, ff.sig_d, ff.sig_arst, ff.sig_q);
+ }
+ } else {
+ module->connect(ff.sig_q, ff.sig_d);
+ }
+ did_something = true;
+ continue;
+ }
+ }
+ }
+
+ 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 const-input 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_d = ff.has_en = ff.has_clk = ff.has_srst = false;
+ changed = true;
+ }
+ }
+
+ if (ff.has_d && 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_en && ff.ce_over_srst) {
+ if (!ff.pol_en) {
+ if (ff.is_fine)
+ ff.sig_en = module->NotGate(NEW_ID, ff.sig_en);
+ else
+ ff.sig_en = module->Not(NEW_ID, ff.sig_en);
+ }
+ 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_en = module->AndGate(NEW_ID, ff.sig_en, ff.sig_srst);
+ else
+ ff.sig_en = module->And(NEW_ID, ff.sig_en, ff.sig_srst);
+ ff.pol_en = true;
+ } else {
+ ff.pol_en = ff.pol_srst;
+ ff.sig_en = ff.sig_srst;
+ }
+ ff.has_en = true;
+ ff.has_srst = false;
+ ff.sig_d = ff.val_d = ff.val_srst;
+ ff.d_is_const = true;
+ 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_d = ff.has_en = ff.has_clk = false;
+ changed = true;
+ }
+ }
+
+ // 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_d) {
+ 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 (!bit2driver.count(ff.sig_d[i]))
+ continue;
+ if (val != State::S0 && val != State::S1)
+ continue;
+
+ sat_import_cell(bit2driver.at(ff.sig_d[i]));
+
+ int init_sat_pi = satgen.importSigSpec(val).front();
+ int q_sat_pi = satgen.importSigBit(ff.sig_q[i]);
+ int d_sat_pi = satgen.importSigBit(ff.sig_d[i]);
+
+ // 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)
+ 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);
+ 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_en || 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_en)
+ new_ff.ce_over_srst = true;
+ Cell *new_cell = new_ff.emit(module, NEW_ID);
+ 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);
+ changed = true;
+ }
+ }
+ if ((!ff.has_srst || !ff.has_en || !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_en)
+ enables.insert(ctrl_t(ff.sig_en, ff.pol_en));
+ 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_en = true;
+ new_ff.sig_en = en.first;
+ new_ff.pol_en = en.second;
+ new_ff.ce_over_srst = false;
+ Cell *new_cell = new_ff.emit(module, NEW_ID);
+ 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);
+ changed = true;
+ }
+ }
+ }
+
+ if (changed) {
+ // Rebuild the FF.
+ IdString name = cell->name;
+ module->remove(cell);
+ ff.emit(module, name);
+ 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 (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 e5b8bda95..e36e4419d 100644
--- a/passes/opt/opt_expr.cc
+++ b/passes/opt/opt_expr.cc
@@ -117,7 +117,7 @@ void replace_undriven(RTLIL::Module *module, const CellTypes &ct)
}
void replace_cell(SigMap &assign_map, RTLIL::Module *module, RTLIL::Cell *cell,
- const std::string &info YS_ATTRIBUTE(unused), IdString out_port, RTLIL::SigSpec out_val)
+ const std::string &info, IdString out_port, RTLIL::SigSpec out_val)
{
RTLIL::SigSpec Y = cell->getPort(out_port);
out_val.extend_u0(Y.size(), false);
@@ -416,7 +416,7 @@ int get_onehot_bit_index(RTLIL::SigSpec signal)
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)
+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)
{
if (!design->selected(module))
return;
@@ -465,17 +465,23 @@ 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($adff), ID($fsm), ID($memrd), ID($memwr)))
+ 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)))
handle_polarity_inv(cell, ID::CLK, ID::CLK_POLARITY, assign_map, invert_map);
- if (cell->type.in(ID($sr), ID($dffsr), ID($dlatchsr))) {
+ if (cell->type.in(ID($sr), ID($dffsr), ID($dffsre), ID($dlatchsr))) {
handle_polarity_inv(cell, ID::SET, ID::SET_POLARITY, assign_map, invert_map);
handle_polarity_inv(cell, ID::CLR, ID::CLR_POLARITY, assign_map, invert_map);
}
- if (cell->type.in(ID($dffe), ID($dlatch), ID($dlatchsr)))
+ 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($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)))
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);
@@ -489,12 +495,35 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
handle_clkpol_celltype_swap(cell, "$_DFF_N??_", "$_DFF_P??_", ID::C, assign_map, invert_map);
handle_clkpol_celltype_swap(cell, "$_DFF_?N?_", "$_DFF_?P?_", ID::R, assign_map, invert_map);
+ handle_clkpol_celltype_swap(cell, "$_DFFE_N???_", "$_DFFE_P???_", ID::C, assign_map, invert_map);
+ handle_clkpol_celltype_swap(cell, "$_DFFE_?N??_", "$_DFFE_?P??_", ID::R, assign_map, invert_map);
+ handle_clkpol_celltype_swap(cell, "$_DFFE_???N_", "$_DFFE_???P_", ID::E, assign_map, invert_map);
+
+ handle_clkpol_celltype_swap(cell, "$_SDFF_N??_", "$_SDFF_P??_", ID::C, assign_map, invert_map);
+ handle_clkpol_celltype_swap(cell, "$_SDFF_?N?_", "$_SDFF_?P?_", ID::R, assign_map, invert_map);
+
+ handle_clkpol_celltype_swap(cell, "$_SDFFE_N???_", "$_SDFFE_P???_", ID::C, assign_map, invert_map);
+ handle_clkpol_celltype_swap(cell, "$_SDFFE_?N??_", "$_SDFFE_?P??_", ID::R, assign_map, invert_map);
+ handle_clkpol_celltype_swap(cell, "$_SDFFE_???N_", "$_SDFFE_???P_", ID::E, assign_map, invert_map);
+
+ handle_clkpol_celltype_swap(cell, "$_SDFFCE_N???_", "$_SDFFCE_P???_", ID::C, assign_map, invert_map);
+ 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, "$_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);
+ handle_clkpol_celltype_swap(cell, "$_DFFSRE_N???_", "$_DFFSRE_P???_", ID::C, assign_map, invert_map);
+ handle_clkpol_celltype_swap(cell, "$_DFFSRE_?N??_", "$_DFFSRE_?P??_", ID::S, assign_map, invert_map);
+ handle_clkpol_celltype_swap(cell, "$_DFFSRE_??N?_", "$_DFFSRE_??P?_", ID::R, assign_map, invert_map);
+ handle_clkpol_celltype_swap(cell, "$_DFFSRE_???N_", "$_DFFSRE_???P_", ID::E, assign_map, invert_map);
+
handle_clkpol_celltype_swap(cell, "$_DLATCH_N_", "$_DLATCH_P_", ID::E, assign_map, invert_map);
+ handle_clkpol_celltype_swap(cell, "$_DLATCH_N??_", "$_DLATCH_P??_", ID::E, assign_map, invert_map);
+ handle_clkpol_celltype_swap(cell, "$_DLATCH_?N?_", "$_DLATCH_?P?_", ID::R, assign_map, invert_map);
+
handle_clkpol_celltype_swap(cell, "$_DLATCHSR_N??_", "$_DLATCHSR_P??_", ID::E, assign_map, invert_map);
handle_clkpol_celltype_swap(cell, "$_DLATCHSR_?N?_", "$_DLATCHSR_?P?_", ID::S, assign_map, invert_map);
handle_clkpol_celltype_swap(cell, "$_DLATCHSR_??N_", "$_DLATCHSR_??P_", ID::R, assign_map, invert_map);
@@ -575,7 +604,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;
}
@@ -1567,6 +1596,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;
@@ -2009,7 +2046,7 @@ skip_alu_split:
struct OptExprPass : public Pass {
OptExprPass() : Pass("opt_expr", "perform const folding and simple expression rewriting") { }
- void help() YS_OVERRIDE
+ void help() override
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -2027,8 +2064,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");
@@ -2043,12 +2080,12 @@ struct OptExprPass : public Pass {
log(" replaced by 'a'. the -keepdc option disables all such optimizations.\n");
log("\n");
}
- void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ void execute(std::vector<std::string> args, RTLIL::Design *design) override
{
bool mux_undef = false;
bool mux_bool = false;
bool undriven = false;
- bool clkinv = false;
+ bool noclkinv = false;
bool do_fine = false;
bool keepdc = false;
@@ -2069,8 +2106,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") {
@@ -2107,12 +2144,12 @@ 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);
diff --git a/passes/opt/opt_lut.cc b/passes/opt/opt_lut.cc
index 12927d052..07a91af8a 100644
--- a/passes/opt/opt_lut.cc
+++ b/passes/opt/opt_lut.cc
@@ -520,7 +520,7 @@ static void split(std::vector<std::string> &tokens, const std::string &text, cha
struct OptLutPass : public Pass {
OptLutPass() : Pass("opt_lut", "optimize LUT cells") { }
- void help() YS_OVERRIDE
+ void help() override
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -538,7 +538,7 @@ struct OptLutPass : public Pass {
log(" only perform the first N combines, then stop. useful for debugging.\n");
log("\n");
}
- void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ void execute(std::vector<std::string> args, RTLIL::Design *design) override
{
log_header(design, "Executing OPT_LUT pass (optimize LUTs).\n");
diff --git a/passes/opt/opt_lut_ins.cc b/passes/opt/opt_lut_ins.cc
index 1d32e84bb..bb40e1e55 100644
--- a/passes/opt/opt_lut_ins.cc
+++ b/passes/opt/opt_lut_ins.cc
@@ -25,7 +25,7 @@ PRIVATE_NAMESPACE_BEGIN
struct OptLutInsPass : public Pass {
OptLutInsPass() : Pass("opt_lut_ins", "discard unused LUT inputs") { }
- void help() YS_OVERRIDE
+ void help() override
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -42,7 +42,7 @@ struct OptLutInsPass : public Pass {
log(" to the given technology. Valid values are: xilinx, ecp5, gowin.\n");
log("\n");
}
- void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ void execute(std::vector<std::string> args, RTLIL::Design *design) override
{
log_header(design, "Executing OPT_LUT_INS pass (discard unused LUT inputs).\n");
string techname;
diff --git a/passes/opt/opt_mem.cc b/passes/opt/opt_mem.cc
index ff9c06453..49a0ac51a 100644
--- a/passes/opt/opt_mem.cc
+++ b/passes/opt/opt_mem.cc
@@ -19,85 +19,14 @@
#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() YS_OVERRIDE
+ void help() override
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -106,7 +35,7 @@ struct OptMemPass : public Pass {
log("This pass performs various optimizations on memories in the design.\n");
log("\n");
}
- void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ void execute(std::vector<std::string> args, RTLIL::Design *design) override
{
log_header(design, "Executing OPT_MEM pass (optimize memories).\n");
@@ -122,15 +51,11 @@ 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;
+ for (auto &mem : Mem::get_selected_memories(module)) {
+ if (mem.wr_ports.empty() && mem.inits.empty()) {
+ mem.remove();
+ total_count++;
+ }
}
}
diff --git a/passes/opt/opt_merge.cc b/passes/opt/opt_merge.cc
index d845926fc..9086943dc 100644
--- a/passes/opt/opt_merge.cc
+++ b/passes/opt/opt_merge.cc
@@ -173,9 +173,7 @@ 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];
@@ -298,9 +296,7 @@ struct OptMergeWorker
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_)))) {
+ if (it.first == ID::Q && RTLIL::builtin_ff_cell_types().count(cell->type)) {
for (auto c : it.second.chunks()) {
auto jt = c.wire->attributes.find(ID::init);
if (jt == c.wire->attributes.end())
@@ -326,7 +322,7 @@ struct OptMergeWorker
struct OptMergePass : public Pass {
OptMergePass() : Pass("opt_merge", "consolidate identical cells") { }
- void help() YS_OVERRIDE
+ void help() override
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -342,7 +338,7 @@ struct OptMergePass : public Pass {
log(" Operate on all cell types, not just built-in types.\n");
log("\n");
}
- void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ void execute(std::vector<std::string> args, RTLIL::Design *design) override
{
log_header(design, "Executing OPT_MERGE pass (detect identical cells).\n");
diff --git a/passes/opt/opt_muxtree.cc b/passes/opt/opt_muxtree.cc
index d076addae..67b283e11 100644
--- a/passes/opt/opt_muxtree.cc
+++ b/passes/opt/opt_muxtree.cc
@@ -473,7 +473,7 @@ struct OptMuxtreeWorker
struct OptMuxtreePass : public Pass {
OptMuxtreePass() : Pass("opt_muxtree", "eliminate dead trees in multiplexer trees") { }
- void help() YS_OVERRIDE
+ void help() override
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -486,7 +486,7 @@ struct OptMuxtreePass : public Pass {
log("This pass only operates on completely selected modules without processes.\n");
log("\n");
}
- void execute(vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ void execute(vector<std::string> args, RTLIL::Design *design) override
{
log_header(design, "Executing OPT_MUXTREE pass (detect dead branches in mux trees).\n");
extra_args(args, 1, design);
diff --git a/passes/opt/opt_reduce.cc b/passes/opt/opt_reduce.cc
index f640f50a0..28de9ceb6 100644
--- a/passes/opt/opt_reduce.cc
+++ b/passes/opt/opt_reduce.cc
@@ -332,7 +332,7 @@ struct OptReduceWorker
struct OptReducePass : public Pass {
OptReducePass() : Pass("opt_reduce", "simplify large MUXes and AND/OR gates") { }
- void help() YS_OVERRIDE
+ void help() override
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -353,7 +353,7 @@ struct OptReducePass : public Pass {
log(" alias for -fine\n");
log("\n");
}
- void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ void execute(std::vector<std::string> args, RTLIL::Design *design) override
{
bool do_fine = false;
diff --git a/passes/opt/opt_rmdff.cc b/passes/opt/opt_rmdff.cc
deleted file mode 100644
index 81326a417..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() YS_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) YS_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 cbace7bac..53296699c 100644
--- a/passes/opt/opt_share.cc
+++ b/passes/opt/opt_share.cc
@@ -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_sig_out)));
+ shared_op->setPort(ID::CO, module->addWire(NEW_ID, GetSize(new_sig_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,135 +332,9 @@ 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() YS_OVERRIDE
+ void help() override
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -488,44 +349,53 @@ struct OptSharePass : public Pass {
log("multiplexing its output to multiplexing the non-shared input signals.\n");
log("\n");
}
- void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ void execute(std::vector<std::string> args, RTLIL::Design *design) override
{
log_header(design, "Executing OPT_SHARE pass.\n");
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 11b80b6b3..f3b1fd377 100644
--- a/passes/opt/pmux2shiftx.cc
+++ b/passes/opt/pmux2shiftx.cc
@@ -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,30 +45,20 @@ 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())
{
vector<SigSpec> inputs;
SigSpec output;
- if (cell->type.in(ID($adff), ID($dff), ID($dffe), ID($dlatch), ID($ff)))
+ if (cell->type.in(ID($adff), ID($adffe), ID($dff), ID($dffe), ID($sdff), ID($sdffe), ID($sdffce), ID($dlatch), ID($adlatch), ID($ff)))
{
output = cell->getPort(ID::Q);
- if (cell->type == ID($adff))
+ if (cell->type.in(ID($adff), ID($adffe), ID($adlatch)))
inputs.push_back(cell->getParam(ID::ARST_VALUE));
+ if (cell->type.in(ID($sdff), ID($sdffe), ID($sdffce)))
+ inputs.push_back(cell->getParam(ID::SRST_VALUE));
inputs.push_back(cell->getPort(ID::D));
}
@@ -117,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, "");
@@ -198,7 +189,7 @@ struct OnehotDatabase
struct Pmux2ShiftxPass : public Pass {
Pmux2ShiftxPass() : Pass("pmux2shiftx", "transform $pmux cells to $shiftx cells") { }
- void help() YS_OVERRIDE
+ void help() override
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -225,7 +216,7 @@ struct Pmux2ShiftxPass : public Pass {
log(" disable $sub inference for \"range decoders\"\n");
log("\n");
}
- void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ void execute(std::vector<std::string> args, RTLIL::Design *design) override
{
int min_density = 50;
int min_choices = 3;
@@ -737,7 +728,7 @@ struct Pmux2ShiftxPass : public Pass {
struct OnehotPass : public Pass {
OnehotPass() : Pass("onehot", "optimize $eq cells for onehot signals") { }
- void help() YS_OVERRIDE
+ void help() override
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -749,7 +740,7 @@ struct OnehotPass : public Pass {
log(" verbose output\n");
log("\n");
}
- void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ void execute(std::vector<std::string> args, RTLIL::Design *design) override
{
bool verbose = false;
bool verbose_onehot = false;
diff --git a/passes/opt/rmports.cc b/passes/opt/rmports.cc
index 32363dd68..99a2a61c8 100644
--- a/passes/opt/rmports.cc
+++ b/passes/opt/rmports.cc
@@ -28,7 +28,7 @@ PRIVATE_NAMESPACE_BEGIN
struct RmportsPassPass : public Pass {
RmportsPassPass() : Pass("rmports", "remove module ports with no connections") { }
- void help() YS_OVERRIDE
+ void help() override
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -39,7 +39,7 @@ struct RmportsPassPass : public Pass {
log("\n");
}
- void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ void execute(std::vector<std::string> args, RTLIL::Design *design) override
{
log_header(design, "Executing RMPORTS pass (remove ports with no connections).\n");
diff --git a/passes/opt/share.cc b/passes/opt/share.cc
index 988253edf..f7848e01d 100644
--- a/passes/opt/share.cc
+++ b/passes/opt/share.cc
@@ -1444,7 +1444,7 @@ struct ShareWorker
struct SharePass : public Pass {
SharePass() : Pass("share", "perform sat-based resource sharing") { }
- void help() YS_OVERRIDE
+ void help() override
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -1476,7 +1476,7 @@ struct SharePass : public Pass {
log(" Only perform the first N merges, then stop. This is useful for debugging.\n");
log("\n");
}
- void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ void execute(std::vector<std::string> args, RTLIL::Design *design) override
{
ShareWorkerConfig config;
diff --git a/passes/opt/wreduce.cc b/passes/opt/wreduce.cc
index f60f2f8a8..a216f36d4 100644
--- a/passes/opt/wreduce.cc
+++ b/passes/opt/wreduce.cc
@@ -20,6 +20,7 @@
#include "kernel/yosys.h"
#include "kernel/sigtools.h"
#include "kernel/modtools.h"
+#include "kernel/ffinit.h"
USING_YOSYS_NAMESPACE
@@ -39,7 +40,8 @@ struct WreduceConfig
ID($lt), ID($le), ID($eq), ID($ne), ID($eqx), ID($nex), ID($ge), ID($gt),
ID($add), ID($sub), ID($mul), // ID($div), ID($mod), ID($divfloor), ID($modfloor), ID($pow),
ID($mux), ID($pmux),
- ID($dff), ID($adff)
+ ID($dff), ID($dffe), ID($adff), ID($adffe), ID($sdff), ID($sdffe), ID($sdffce),
+ ID($dlatch), ID($adlatch),
});
}
};
@@ -53,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) { }
@@ -143,8 +144,8 @@ struct WreduceWorker
SigSpec sig_d = mi.sigmap(cell->getPort(ID::D));
SigSpec sig_q = mi.sigmap(cell->getPort(ID::Q));
- bool is_adff = (cell->type == ID($adff));
- Const initval, arst_value;
+ bool has_reset = false;
+ Const initval = initvals(sig_q), rst_value;
int width_before = GetSize(sig_q);
@@ -152,35 +153,31 @@ struct WreduceWorker
return;
if (cell->parameters.count(ID::ARST_VALUE)) {
- arst_value = cell->parameters[ID::ARST_VALUE];
+ rst_value = cell->parameters[ID::ARST_VALUE];
+ has_reset = true;
+ } else if (cell->parameters.count(ID::SRST_VALUE)) {
+ rst_value = cell->parameters[ID::SRST_VALUE];
+ has_reset = true;
}
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) &&
- (!is_adff || i >= GetSize(arst_value) || arst_value[i] == State::S0 || arst_value[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;
}
if (sign_ext && i > 0 && sig_d[i] == sig_d[i-1] && initval[i] == initval[i-1] &&
- (!is_adff || i >= GetSize(arst_value) || arst_value[i] == arst_value[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;
@@ -190,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;
@@ -221,8 +218,11 @@ struct WreduceWorker
// Narrow ARST_VALUE parameter to new size.
if (cell->parameters.count(ID::ARST_VALUE)) {
- arst_value.bits.resize(GetSize(sig_q));
- cell->setParam(ID::ARST_VALUE, arst_value);
+ rst_value.bits.resize(GetSize(sig_q));
+ cell->setParam(ID::ARST_VALUE, rst_value);
+ } else if (cell->parameters.count(ID::SRST_VALUE)) {
+ rst_value.bits.resize(GetSize(sig_q));
+ cell->setParam(ID::SRST_VALUE, rst_value);
}
cell->setPort(ID::D, sig_d);
@@ -272,7 +272,7 @@ struct WreduceWorker
if (cell->type.in(ID($mux), ID($pmux)))
return run_cell_mux(cell);
- if (cell->type.in(ID($dff), ID($adff)))
+ if (cell->type.in(ID($dff), ID($dffe), ID($adff), ID($adffe), ID($sdff), ID($sdffe), ID($sdffce), ID($dlatch), ID($adlatch)))
return run_cell_dff(cell);
SigSpec sig = mi.sigmap(cell->getPort(ID::Y));
@@ -401,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())
@@ -461,28 +455,12 @@ 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;
- }
- }
- }
}
};
struct WreducePass : public Pass {
WreducePass() : Pass("wreduce", "reduce the word size of operations if possible") { }
- void help() YS_OVERRIDE
+ void help() override
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -505,7 +483,7 @@ struct WreducePass : public Pass {
log(" Do not optimize explicit don't-care values.\n");
log("\n");
}
- void execute(std::vector<std::string> args, Design *design) YS_OVERRIDE
+ void execute(std::vector<std::string> args, Design *design) override
{
WreduceConfig config;
bool opt_memx = false;