summaryrefslogtreecommitdiff
path: root/passes/sat
diff options
context:
space:
mode:
Diffstat (limited to 'passes/sat')
-rw-r--r--passes/sat/Makefile.inc9
-rw-r--r--passes/sat/assertpmux.cc240
-rw-r--r--passes/sat/clk2fflogic.cc226
-rw-r--r--passes/sat/eval.cc603
-rw-r--r--passes/sat/example.v85
-rw-r--r--passes/sat/example.ys14
-rw-r--r--passes/sat/expose.cc650
-rw-r--r--passes/sat/freduce.cc840
-rw-r--r--passes/sat/miter.cc421
-rw-r--r--passes/sat/sat.cc1713
10 files changed, 4801 insertions, 0 deletions
diff --git a/passes/sat/Makefile.inc b/passes/sat/Makefile.inc
new file mode 100644
index 00000000..6785b750
--- /dev/null
+++ b/passes/sat/Makefile.inc
@@ -0,0 +1,9 @@
+
+OBJS += passes/sat/sat.o
+OBJS += passes/sat/freduce.o
+OBJS += passes/sat/eval.o
+OBJS += passes/sat/miter.o
+OBJS += passes/sat/expose.o
+OBJS += passes/sat/assertpmux.o
+OBJS += passes/sat/clk2fflogic.o
+
diff --git a/passes/sat/assertpmux.cc b/passes/sat/assertpmux.cc
new file mode 100644
index 00000000..63a90767
--- /dev/null
+++ b/passes/sat/assertpmux.cc
@@ -0,0 +1,240 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "kernel/yosys.h"
+#include "kernel/sigtools.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct AssertpmuxWorker
+{
+ Module *module;
+ SigMap sigmap;
+
+ bool flag_noinit;
+ bool flag_always;
+
+ // get<0> ... mux cell
+ // get<1> ... mux port index
+ // get<2> ... mux bit index
+ dict<SigBit, pool<tuple<Cell*, int, int>>> sigbit_muxusers;
+
+ dict<SigBit, SigBit> sigbit_actsignals;
+ dict<SigSpec, SigBit> sigspec_actsignals;
+ dict<tuple<Cell*, int>, SigBit> muxport_actsignal;
+
+ AssertpmuxWorker(Module *module, bool flag_noinit, bool flag_always) :
+ module(module), sigmap(module), flag_noinit(flag_noinit), flag_always(flag_always)
+ {
+ for (auto wire : module->wires())
+ {
+ if (wire->port_output)
+ for (auto bit : sigmap(wire))
+ sigbit_actsignals[bit] = State::S1;
+ }
+
+ for (auto cell : module->cells())
+ {
+ if (cell->type.in("$mux", "$pmux"))
+ {
+ int width = cell->getParam("\\WIDTH").as_int();
+ int numports = cell->type == "$mux" ? 2 : cell->getParam("\\S_WIDTH").as_int() + 1;
+
+ SigSpec sig_a = sigmap(cell->getPort("\\A"));
+ SigSpec sig_b = sigmap(cell->getPort("\\B"));
+ SigSpec sig_s = sigmap(cell->getPort("\\S"));
+
+ for (int i = 0; i < numports; i++) {
+ SigSpec bits = i == 0 ? sig_a : sig_b.extract(width*(i-1), width);
+ for (int k = 0; k < width; k++) {
+ tuple<Cell*, int, int> muxuser(cell, i, k);
+ sigbit_muxusers[bits[k]].insert(muxuser);
+ }
+ }
+ }
+ else
+ {
+ for (auto &conn : cell->connections()) {
+ if (!cell->known() || cell->input(conn.first))
+ for (auto bit : sigmap(conn.second))
+ sigbit_actsignals[bit] = State::S1;
+ }
+ }
+ }
+ }
+
+ SigBit get_bit_activation(SigBit bit)
+ {
+ sigmap.apply(bit);
+
+ if (sigbit_actsignals.count(bit) == 0)
+ {
+ SigSpec output;
+
+ for (auto muxuser : sigbit_muxusers.at(bit))
+ {
+ Cell *cell = std::get<0>(muxuser);
+ int portidx = std::get<1>(muxuser);
+ int bitidx = std::get<2>(muxuser);
+
+ tuple<Cell*, int> muxport(cell, portidx);
+
+ if (muxport_actsignal.count(muxport) == 0) {
+ if (portidx == 0)
+ muxport_actsignal[muxport] = module->LogicNot(NEW_ID, cell->getPort("\\S"));
+ else
+ muxport_actsignal[muxport] = cell->getPort("\\S")[portidx-1];
+ }
+
+ output.append(module->LogicAnd(NEW_ID, muxport_actsignal.at(muxport), get_bit_activation(cell->getPort("\\Y")[bitidx])));
+ }
+
+ output.sort_and_unify();
+
+ if (GetSize(output) == 0)
+ output = State::S0;
+ else if (GetSize(output) > 1)
+ output = module->ReduceOr(NEW_ID, output);
+
+ sigbit_actsignals[bit] = output.as_bit();
+ }
+
+ return sigbit_actsignals.at(bit);
+ }
+
+ SigBit get_activation(SigSpec sig)
+ {
+ sigmap.apply(sig);
+ sig.sort_and_unify();
+
+ if (sigspec_actsignals.count(sig) == 0)
+ {
+ SigSpec output;
+
+ for (auto bit : sig)
+ output.append(get_bit_activation(bit));
+
+ output.sort_and_unify();
+
+ if (GetSize(output) == 0)
+ output = State::S0;
+ else if (GetSize(output) > 1)
+ output = module->ReduceOr(NEW_ID, output);
+
+ sigspec_actsignals[sig] = output.as_bit();
+ }
+
+ return sigspec_actsignals.at(sig);
+ }
+
+ void run(Cell *pmux)
+ {
+ log("Adding assert for $pmux cell %s.%s.\n", log_id(module), log_id(pmux));
+
+ int swidth = pmux->getParam("\\S_WIDTH").as_int();
+ int cntbits = ceil_log2(swidth+1);
+
+ SigSpec sel = pmux->getPort("\\S");
+ SigSpec cnt(State::S0, cntbits);
+
+ for (int i = 0; i < swidth; i++)
+ cnt = module->Add(NEW_ID, cnt, sel[i]);
+
+ SigSpec assert_a = module->Le(NEW_ID, cnt, SigSpec(1, cntbits));
+ SigSpec assert_en;
+
+ if (flag_noinit)
+ assert_en.append(module->LogicNot(NEW_ID, module->Initstate(NEW_ID)));
+
+ if (!flag_always)
+ assert_en.append(get_activation(pmux->getPort("\\Y")));
+
+ if (GetSize(assert_en) == 0)
+ assert_en = State::S1;
+
+ if (GetSize(assert_en) == 2)
+ assert_en = module->LogicAnd(NEW_ID, assert_en[0], assert_en[1]);
+
+ Cell *assert_cell = module->addAssert(NEW_ID, assert_a, assert_en);
+
+ if (pmux->attributes.count("\\src") != 0)
+ assert_cell->attributes["\\src"] = pmux->attributes.at("\\src");
+ }
+};
+
+struct AssertpmuxPass : public Pass {
+ AssertpmuxPass() : Pass("assertpmux", "convert internal signals to module ports") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" assertpmux [options] [selection]\n");
+ log("\n");
+ log("This command adds asserts to the design that assert that all parallel muxes\n");
+ log("($pmux cells) have a maximum of one of their inputs enable at any time.\n");
+ log("\n");
+ log(" -noinit\n");
+ log(" do not enforce the pmux condition during the init state\n");
+ log("\n");
+ log(" -always\n");
+ log(" usually the $pmux condition is only checked when the $pmux output\n");
+ log(" is used be the mux tree it drives. this option will deactivate this\n");
+ log(" additional constrained and check the $pmux condition always.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ bool flag_noinit = false;
+ bool flag_always = false;
+
+ log_header(design, "Executing ASSERTPMUX pass (add asserts for $pmux cells).\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ if (args[argidx] == "-noinit") {
+ flag_noinit = true;
+ continue;
+ }
+ if (args[argidx] == "-always") {
+ flag_always = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto module : design->selected_modules())
+ {
+ AssertpmuxWorker worker(module, flag_noinit, flag_always);
+ vector<Cell*> pmux_cells;
+
+ for (auto cell : module->selected_cells())
+ if (cell->type == "$pmux")
+ pmux_cells.push_back(cell);
+
+ for (auto cell : pmux_cells)
+ worker.run(cell);
+ }
+
+ }
+} AssertpmuxPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/sat/clk2fflogic.cc b/passes/sat/clk2fflogic.cc
new file mode 100644
index 00000000..ef6d5dd7
--- /dev/null
+++ b/passes/sat/clk2fflogic.cc
@@ -0,0 +1,226 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "kernel/yosys.h"
+#include "kernel/sigtools.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct Clk2fflogicPass : public Pass {
+ Clk2fflogicPass() : Pass("clk2fflogic", "convert clocked FFs to generic $ff cells") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" clk2fflogic [options] [selection]\n");
+ log("\n");
+ log("This command replaces clocked flip-flops with generic $ff cells that use the\n");
+ log("implicit global clock. This is useful for formal verification of designs with\n");
+ log("multiple clocks.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ // bool flag_noinit = false;
+
+ log_header(design, "Executing CLK2FFLOGIC pass (convert clocked FFs to generic $ff cells).\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ // if (args[argidx] == "-noinit") {
+ // flag_noinit = true;
+ // continue;
+ // }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto module : design->selected_modules())
+ {
+ SigMap sigmap(module);
+ dict<SigBit, State> initbits;
+ pool<SigBit> del_initbits;
+
+ for (auto wire : module->wires())
+ if (wire->attributes.count("\\init") > 0)
+ {
+ Const initval = wire->attributes.at("\\init");
+ SigSpec initsig = sigmap(wire);
+
+ for (int i = 0; i < GetSize(initval) && i < GetSize(initsig); i++)
+ if (initval[i] == State::S0 || initval[i] == State::S1)
+ initbits[initsig[i]] = initval[i];
+ }
+
+ for (auto cell : vector<Cell*>(module->selected_cells()))
+ {
+ if (cell->type.in("$dlatch"))
+ {
+ bool enpol = cell->parameters["\\EN_POLARITY"].as_bool();
+
+ SigSpec sig_en = cell->getPort("\\EN");
+ SigSpec sig_d = cell->getPort("\\D");
+ SigSpec sig_q = cell->getPort("\\Q");
+
+ log("Replacing %s.%s (%s): EN=%s, D=%s, Q=%s\n",
+ log_id(module), log_id(cell), log_id(cell->type),
+ log_signal(sig_en), log_signal(sig_d), log_signal(sig_q));
+
+ Wire *past_q = module->addWire(NEW_ID, GetSize(sig_q));
+ module->addFf(NEW_ID, sig_q, past_q);
+
+ if (enpol)
+ module->addMux(NEW_ID, past_q, sig_d, sig_en, sig_q);
+ else
+ module->addMux(NEW_ID, sig_d, past_q, sig_en, sig_q);
+
+ Const initval;
+ bool assign_initval = false;
+ for (int i = 0; i < GetSize(sig_d); i++) {
+ SigBit qbit = sigmap(sig_q[i]);
+ if (initbits.count(qbit)) {
+ initval.bits.push_back(initbits.at(qbit));
+ del_initbits.insert(qbit);
+ } else
+ initval.bits.push_back(State::Sx);
+ if (initval.bits.back() != State::Sx)
+ assign_initval = true;
+ }
+
+ if (assign_initval)
+ past_q->attributes["\\init"] = initval;
+
+ module->remove(cell);
+ continue;
+ }
+
+ if (cell->type.in("$dff", "$adff", "$dffsr"))
+ {
+ bool clkpol = cell->parameters["\\CLK_POLARITY"].as_bool();
+
+ SigSpec clk = cell->getPort("\\CLK");
+ Wire *past_clk = module->addWire(NEW_ID);
+ past_clk->attributes["\\init"] = clkpol ? State::S1 : State::S0;
+ module->addFf(NEW_ID, clk, past_clk);
+
+ SigSpec sig_d = cell->getPort("\\D");
+ SigSpec sig_q = cell->getPort("\\Q");
+
+ log("Replacing %s.%s (%s): CLK=%s, D=%s, Q=%s\n",
+ log_id(module), log_id(cell), log_id(cell->type),
+ log_signal(clk), log_signal(sig_d), log_signal(sig_q));
+
+ SigSpec clock_edge_pattern;
+
+ if (clkpol) {
+ clock_edge_pattern.append_bit(State::S0);
+ clock_edge_pattern.append_bit(State::S1);
+ } else {
+ clock_edge_pattern.append_bit(State::S1);
+ clock_edge_pattern.append_bit(State::S0);
+ }
+
+ SigSpec clock_edge = module->Eqx(NEW_ID, {clk, SigSpec(past_clk)}, clock_edge_pattern);
+
+ Wire *past_d = module->addWire(NEW_ID, GetSize(sig_d));
+ Wire *past_q = module->addWire(NEW_ID, GetSize(sig_q));
+ module->addFf(NEW_ID, sig_d, past_d);
+ module->addFf(NEW_ID, sig_q, past_q);
+
+ if (cell->type == "$adff")
+ {
+ SigSpec arst = cell->getPort("\\ARST");
+ SigSpec qval = module->Mux(NEW_ID, past_q, past_d, clock_edge);
+ Const rstval = cell->parameters["\\ARST_VALUE"];
+
+ if (cell->parameters["\\ARST_POLARITY"].as_bool())
+ module->addMux(NEW_ID, qval, rstval, arst, sig_q);
+ else
+ module->addMux(NEW_ID, rstval, qval, arst, sig_q);
+ }
+ else
+ if (cell->type == "$dffsr")
+ {
+ SigSpec qval = module->Mux(NEW_ID, past_q, past_d, clock_edge);
+ SigSpec setval = cell->getPort("\\SET");
+ SigSpec clrval = cell->getPort("\\CLR");
+
+ if (!cell->parameters["\\SET_POLARITY"].as_bool())
+ setval = module->Not(NEW_ID, setval);
+
+ if (cell->parameters["\\CLR_POLARITY"].as_bool())
+ clrval = module->Not(NEW_ID, clrval);
+
+ qval = module->Or(NEW_ID, qval, setval);
+ module->addAnd(NEW_ID, qval, clrval, sig_q);
+ }
+ else
+ {
+ module->addMux(NEW_ID, past_q, past_d, clock_edge, sig_q);
+ }
+
+ Const initval;
+ bool assign_initval = false;
+ for (int i = 0; i < GetSize(sig_d); i++) {
+ SigBit qbit = sigmap(sig_q[i]);
+ if (initbits.count(qbit)) {
+ initval.bits.push_back(initbits.at(qbit));
+ del_initbits.insert(qbit);
+ } else
+ initval.bits.push_back(State::Sx);
+ if (initval.bits.back() != State::Sx)
+ assign_initval = true;
+ }
+
+ if (assign_initval) {
+ past_d->attributes["\\init"] = initval;
+ past_q->attributes["\\init"] = initval;
+ }
+
+ module->remove(cell);
+ continue;
+ }
+ }
+
+ for (auto wire : module->wires())
+ if (wire->attributes.count("\\init") > 0)
+ {
+ bool delete_initattr = true;
+ Const initval = wire->attributes.at("\\init");
+ SigSpec initsig = sigmap(wire);
+
+ for (int i = 0; i < GetSize(initval) && i < GetSize(initsig); i++)
+ if (del_initbits.count(initsig[i]) > 0)
+ initval[i] = State::Sx;
+ else if (initval[i] != State::Sx)
+ delete_initattr = false;
+
+ if (delete_initattr)
+ wire->attributes.erase("\\init");
+ else
+ wire->attributes.at("\\init") = initval;
+ }
+ }
+
+ }
+} Clk2fflogicPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/sat/eval.cc b/passes/sat/eval.cc
new file mode 100644
index 00000000..09f69cc5
--- /dev/null
+++ b/passes/sat/eval.cc
@@ -0,0 +1,603 @@
+/*
+ * 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.
+ *
+ */
+
+// [[CITE]] VlogHammer Verilog Regression Test Suite
+// http://www.clifford.at/yosys/vloghammer.html
+
+#include "kernel/register.h"
+#include "kernel/celltypes.h"
+#include "kernel/consteval.h"
+#include "kernel/sigtools.h"
+#include "kernel/satgen.h"
+#include "kernel/log.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <algorithm>
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+/* this should only be used for regression testing of ConstEval -- see vloghammer */
+struct BruteForceEquivChecker
+{
+ RTLIL::Module *mod1, *mod2;
+ RTLIL::SigSpec mod1_inputs, mod1_outputs;
+ RTLIL::SigSpec mod2_inputs, mod2_outputs;
+ int counter, errors;
+ bool ignore_x_mod1;
+
+ void run_checker(RTLIL::SigSpec &inputs)
+ {
+ if (inputs.size() < mod1_inputs.size()) {
+ RTLIL::SigSpec inputs0 = inputs, inputs1 = inputs;
+ inputs0.append(RTLIL::Const(0, 1));
+ inputs1.append(RTLIL::Const(1, 1));
+ run_checker(inputs0);
+ run_checker(inputs1);
+ return;
+ }
+
+ ConstEval ce1(mod1), ce2(mod2);
+ ce1.set(mod1_inputs, inputs.as_const());
+ ce2.set(mod2_inputs, inputs.as_const());
+
+ RTLIL::SigSpec sig1 = mod1_outputs, undef1;
+ RTLIL::SigSpec sig2 = mod2_outputs, undef2;
+
+ if (!ce1.eval(sig1, undef1))
+ log("Failed ConstEval of module 1 outputs at signal %s (input: %s = %s).\n",
+ log_signal(undef1), log_signal(mod1_inputs), log_signal(inputs));
+ if (!ce2.eval(sig2, undef2))
+ log("Failed ConstEval of module 2 outputs at signal %s (input: %s = %s).\n",
+ log_signal(undef2), log_signal(mod1_inputs), log_signal(inputs));
+
+ if (ignore_x_mod1) {
+ for (int i = 0; i < GetSize(sig1); i++)
+ if (sig1[i] == RTLIL::State::Sx)
+ sig2[i] = RTLIL::State::Sx;
+ }
+
+ if (sig1 != sig2) {
+ log("Found counter-example (ignore_x_mod1 = %s):\n", ignore_x_mod1 ? "active" : "inactive");
+ log(" Module 1: %s = %s => %s = %s\n", log_signal(mod1_inputs), log_signal(inputs), log_signal(mod1_outputs), log_signal(sig1));
+ log(" Module 2: %s = %s => %s = %s\n", log_signal(mod2_inputs), log_signal(inputs), log_signal(mod2_outputs), log_signal(sig2));
+ errors++;
+ }
+
+ counter++;
+ }
+
+ BruteForceEquivChecker(RTLIL::Module *mod1, RTLIL::Module *mod2, bool ignore_x_mod1) :
+ mod1(mod1), mod2(mod2), counter(0), errors(0), ignore_x_mod1(ignore_x_mod1)
+ {
+ log("Checking for equivalence (brute-force): %s vs %s\n", mod1->name.c_str(), mod2->name.c_str());
+ for (auto &w : mod1->wires_)
+ {
+ RTLIL::Wire *wire1 = w.second;
+ if (wire1->port_id == 0)
+ continue;
+
+ if (mod2->wires_.count(wire1->name) == 0)
+ log_cmd_error("Port %s in module 1 has no counterpart in module 2!\n", wire1->name.c_str());
+
+ RTLIL::Wire *wire2 = mod2->wires_.at(wire1->name);
+ if (wire1->width != wire2->width || wire1->port_input != wire2->port_input || wire1->port_output != wire2->port_output)
+ log_cmd_error("Port %s in module 1 does not match its counterpart in module 2!\n", wire1->name.c_str());
+
+ if (wire1->port_input) {
+ mod1_inputs.append(wire1);
+ mod2_inputs.append(wire2);
+ } else {
+ mod1_outputs.append(wire1);
+ mod2_outputs.append(wire2);
+ }
+ }
+
+ RTLIL::SigSpec inputs;
+ run_checker(inputs);
+ }
+};
+
+/* this should only be used for regression testing of ConstEval -- see vloghammer */
+struct VlogHammerReporter
+{
+ RTLIL::Design *design;
+ std::vector<RTLIL::Module*> modules;
+ std::vector<std::string> module_names;
+ std::vector<RTLIL::IdString> inputs;
+ std::vector<int> input_widths;
+ std::vector<RTLIL::Const> patterns;
+ int total_input_width;
+
+ std::vector<std::string> split(std::string text, const char *delim)
+ {
+ std::vector<std::string> list;
+ char *p = strdup(text.c_str());
+ char *t = strtok(p, delim);
+ while (t != NULL) {
+ list.push_back(t);
+ t = strtok(NULL, delim);
+ }
+ free(p);
+ return list;
+ }
+
+ void sat_check(RTLIL::Module *module, RTLIL::SigSpec recorded_set_vars, RTLIL::Const recorded_set_vals, RTLIL::SigSpec expected_y, bool model_undef)
+ {
+ log("Verifying SAT model (%s)..\n", model_undef ? "with undef" : "without undef");
+
+ ezSatPtr ez;
+ SigMap sigmap(module);
+ SatGen satgen(ez.get(), &sigmap);
+ satgen.model_undef = model_undef;
+
+ for (auto &c : module->cells_)
+ if (!satgen.importCell(c.second))
+ log_error("Failed to import cell %s (type %s) to SAT database.\n", RTLIL::id2cstr(c.first), RTLIL::id2cstr(c.second->type));
+
+ ez->assume(satgen.signals_eq(recorded_set_vars, recorded_set_vals));
+
+ std::vector<int> y_vec = satgen.importDefSigSpec(module->wires_.at("\\y"));
+ std::vector<bool> y_values;
+
+ if (model_undef) {
+ std::vector<int> y_undef_vec = satgen.importUndefSigSpec(module->wires_.at("\\y"));
+ y_vec.insert(y_vec.end(), y_undef_vec.begin(), y_undef_vec.end());
+ }
+
+ log(" Created SAT problem with %d variables and %d clauses.\n",
+ ez->numCnfVariables(), ez->numCnfClauses());
+
+ if (!ez->solve(y_vec, y_values))
+ log_error("Failed to find solution to SAT problem.\n");
+
+ for (int i = 0; i < expected_y.size(); i++) {
+ RTLIL::State solution_bit = y_values.at(i) ? RTLIL::State::S1 : RTLIL::State::S0;
+ RTLIL::State expected_bit = expected_y[i].data;
+ if (model_undef) {
+ if (y_values.at(expected_y.size()+i))
+ solution_bit = RTLIL::State::Sx;
+ } else {
+ if (expected_bit == RTLIL::State::Sx)
+ continue;
+ }
+ if (solution_bit != expected_bit) {
+ std::string sat_bits, rtl_bits;
+ for (int k = expected_y.size()-1; k >= 0; k--) {
+ if (model_undef && y_values.at(expected_y.size()+k))
+ sat_bits += "x";
+ else
+ sat_bits += y_values.at(k) ? "1" : "0";
+ rtl_bits += expected_y[k] == RTLIL::State::Sx ? "x" : expected_y[k] == RTLIL::State::S1 ? "1" : "0";
+ }
+ log_error("Found error in SAT model: y[%d] = %s, should be %s:\n SAT: %s\n RTL: %s\n %*s^\n",
+ int(i), log_signal(solution_bit), log_signal(expected_bit),
+ sat_bits.c_str(), rtl_bits.c_str(), expected_y.size()-i-1, "");
+ }
+ }
+
+ if (model_undef)
+ {
+ std::vector<int> cmp_vars;
+ std::vector<bool> cmp_vals;
+
+ std::vector<bool> y_undef(y_values.begin() + expected_y.size(), y_values.end());
+
+ for (int i = 0; i < expected_y.size(); i++)
+ if (y_undef.at(i))
+ {
+ log(" Toggling undef bit %d to test undef gating.\n", i);
+ if (!ez->solve(y_vec, y_values, ez->IFF(y_vec.at(i), y_values.at(i) ? ez->CONST_FALSE : ez->CONST_TRUE)))
+ log_error("Failed to find solution with toggled bit!\n");
+
+ cmp_vars.push_back(y_vec.at(expected_y.size() + i));
+ cmp_vals.push_back(true);
+ }
+ else
+ {
+ cmp_vars.push_back(y_vec.at(i));
+ cmp_vals.push_back(y_values.at(i));
+
+ cmp_vars.push_back(y_vec.at(expected_y.size() + i));
+ cmp_vals.push_back(false);
+ }
+
+ log(" Testing if SAT solution is unique.\n");
+ ez->assume(ez->vec_ne(cmp_vars, ez->vec_const(cmp_vals)));
+ if (ez->solve(y_vec, y_values))
+ log_error("Found two distinct solutions to SAT problem.\n");
+ }
+ else
+ {
+ log(" Testing if SAT solution is unique.\n");
+ ez->assume(ez->vec_ne(y_vec, ez->vec_const(y_values)));
+ if (ez->solve(y_vec, y_values))
+ log_error("Found two distinct solutions to SAT problem.\n");
+ }
+
+ log(" SAT model verified.\n");
+ }
+
+ void run()
+ {
+ for (int idx = 0; idx < int(patterns.size()); idx++)
+ {
+ log("Creating report for pattern %d: %s\n", idx, log_signal(patterns[idx]));
+ std::string input_pattern_list;
+ RTLIL::SigSpec rtl_sig;
+
+ for (int mod = 0; mod < int(modules.size()); mod++)
+ {
+ RTLIL::SigSpec recorded_set_vars;
+ RTLIL::Const recorded_set_vals;
+ RTLIL::Module *module = modules[mod];
+ std::string module_name = module_names[mod].c_str();
+ ConstEval ce(module);
+
+ std::vector<RTLIL::State> bits(patterns[idx].bits.begin(), patterns[idx].bits.begin() + total_input_width);
+ for (int i = 0; i < int(inputs.size()); i++) {
+ RTLIL::Wire *wire = module->wires_.at(inputs[i]);
+ for (int j = input_widths[i]-1; j >= 0; j--) {
+ ce.set(RTLIL::SigSpec(wire, j), bits.back());
+ recorded_set_vars.append(RTLIL::SigSpec(wire, j));
+ recorded_set_vals.bits.push_back(bits.back());
+ bits.pop_back();
+ }
+ if (module == modules.front()) {
+ RTLIL::SigSpec sig(wire);
+ if (!ce.eval(sig))
+ log_error("Can't read back value for port %s!\n", RTLIL::id2cstr(inputs[i]));
+ input_pattern_list += stringf(" %s", sig.as_const().as_string().c_str());
+ log("++PAT++ %d %s %s #\n", idx, RTLIL::id2cstr(inputs[i]), sig.as_const().as_string().c_str());
+ }
+ }
+
+ if (module->wires_.count("\\y") == 0)
+ log_error("No output wire (y) found in module %s!\n", RTLIL::id2cstr(module->name));
+
+ RTLIL::SigSpec sig(module->wires_.at("\\y"));
+ RTLIL::SigSpec undef;
+
+ while (!ce.eval(sig, undef)) {
+ // log_error("Evaluation of y in module %s failed: sig=%s, undef=%s\n", RTLIL::id2cstr(module->name), log_signal(sig), log_signal(undef));
+ log_warning("Setting signal %s in module %s to undef.\n", log_signal(undef), RTLIL::id2cstr(module->name));
+ ce.set(undef, RTLIL::Const(RTLIL::State::Sx, undef.size()));
+ }
+
+ log("++VAL++ %d %s %s #\n", idx, module_name.c_str(), sig.as_const().as_string().c_str());
+
+ if (module_name == "rtl") {
+ rtl_sig = sig;
+ sat_check(module, recorded_set_vars, recorded_set_vals, sig, false);
+ sat_check(module, recorded_set_vars, recorded_set_vals, sig, true);
+ } else if (rtl_sig.size() > 0) {
+ if (rtl_sig.size() != sig.size())
+ log_error("Output (y) has a different width in module %s compared to rtl!\n", RTLIL::id2cstr(module->name));
+ for (int i = 0; i < GetSize(sig); i++)
+ if (rtl_sig[i] == RTLIL::State::Sx)
+ sig[i] = RTLIL::State::Sx;
+ }
+
+ log("++RPT++ %d%s %s %s\n", idx, input_pattern_list.c_str(), sig.as_const().as_string().c_str(), module_name.c_str());
+ }
+
+ log("++RPT++ ----\n");
+ }
+ log("++OK++\n");
+ }
+
+ VlogHammerReporter(RTLIL::Design *design, std::string module_prefix, std::string module_list, std::string input_list, std::string pattern_list) : design(design)
+ {
+ for (auto name : split(module_list, ",")) {
+ RTLIL::IdString esc_name = RTLIL::escape_id(module_prefix + name);
+ if (design->modules_.count(esc_name) == 0)
+ log_error("Can't find module %s in current design!\n", name.c_str());
+ log("Using module %s (%s).\n", esc_name.c_str(), name.c_str());
+ modules.push_back(design->modules_.at(esc_name));
+ module_names.push_back(name);
+ }
+
+ total_input_width = 0;
+ for (auto name : split(input_list, ",")) {
+ int width = -1;
+ RTLIL::IdString esc_name = RTLIL::escape_id(name);
+ for (auto mod : modules) {
+ if (mod->wires_.count(esc_name) == 0)
+ log_error("Can't find input %s in module %s!\n", name.c_str(), RTLIL::id2cstr(mod->name));
+ RTLIL::Wire *port = mod->wires_.at(esc_name);
+ if (!port->port_input || port->port_output)
+ log_error("Wire %s in module %s is not an input!\n", name.c_str(), RTLIL::id2cstr(mod->name));
+ if (width >= 0 && width != port->width)
+ log_error("Port %s has different sizes in the different modules!\n", name.c_str());
+ width = port->width;
+ }
+ log("Using input port %s with width %d.\n", esc_name.c_str(), width);
+ inputs.push_back(esc_name);
+ input_widths.push_back(width);
+ total_input_width += width;
+ }
+
+ for (auto pattern : split(pattern_list, ",")) {
+ RTLIL::SigSpec sig;
+ bool invert_pattern = false;
+ if (pattern.size() > 0 && pattern[0] == '~') {
+ invert_pattern = true;
+ pattern = pattern.substr(1);
+ }
+ if (!RTLIL::SigSpec::parse(sig, NULL, pattern) || !sig.is_fully_const())
+ log_error("Failed to parse pattern %s!\n", pattern.c_str());
+ if (sig.size() < total_input_width)
+ log_error("Pattern %s is to short!\n", pattern.c_str());
+ patterns.push_back(sig.as_const());
+ if (invert_pattern) {
+ for (auto &bit : patterns.back().bits)
+ if (bit == RTLIL::State::S0)
+ bit = RTLIL::State::S1;
+ else if (bit == RTLIL::State::S1)
+ bit = RTLIL::State::S0;
+ }
+ log("Using pattern %s.\n", patterns.back().as_string().c_str());
+ }
+ }
+};
+
+struct EvalPass : public Pass {
+ EvalPass() : Pass("eval", "evaluate the circuit given an input") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" eval [options] [selection]\n");
+ log("\n");
+ log("This command evaluates the value of a signal given the value of all required\n");
+ log("inputs.\n");
+ log("\n");
+ log(" -set <signal> <value>\n");
+ log(" set the specified signal to the specified value.\n");
+ log("\n");
+ log(" -set-undef\n");
+ log(" set all unspecified source signals to undef (x)\n");
+ log("\n");
+ log(" -table <signal>\n");
+ log(" create a truth table using the specified input signals\n");
+ log("\n");
+ log(" -show <signal>\n");
+ log(" show the value for the specified signal. if no -show option is passed\n");
+ log(" then all output ports of the current module are used.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ std::vector<std::pair<std::string, std::string>> sets;
+ std::vector<std::string> shows, tables;
+ bool set_undef = false;
+
+ log_header(design, "Executing EVAL pass (evaluate the circuit given an input).\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ if (args[argidx] == "-set" && argidx+2 < args.size()) {
+ std::string lhs = args[++argidx].c_str();
+ std::string rhs = args[++argidx].c_str();
+ sets.push_back(std::pair<std::string, std::string>(lhs, rhs));
+ continue;
+ }
+ if (args[argidx] == "-set-undef") {
+ set_undef = true;
+ continue;
+ }
+ if (args[argidx] == "-show" && argidx+1 < args.size()) {
+ shows.push_back(args[++argidx]);
+ continue;
+ }
+ if (args[argidx] == "-table" && argidx+1 < args.size()) {
+ tables.push_back(args[++argidx]);
+ continue;
+ }
+ if ((args[argidx] == "-brute_force_equiv_checker" || args[argidx] == "-brute_force_equiv_checker_x") && argidx+3 == args.size()) {
+ /* this should only be used for regression testing of ConstEval -- see vloghammer */
+ std::string mod1_name = RTLIL::escape_id(args[++argidx]);
+ std::string mod2_name = RTLIL::escape_id(args[++argidx]);
+ if (design->modules_.count(mod1_name) == 0)
+ log_error("Can't find module `%s'!\n", mod1_name.c_str());
+ if (design->modules_.count(mod2_name) == 0)
+ log_error("Can't find module `%s'!\n", mod2_name.c_str());
+ BruteForceEquivChecker checker(design->modules_.at(mod1_name), design->modules_.at(mod2_name), args[argidx-2] == "-brute_force_equiv_checker_x");
+ if (checker.errors > 0)
+ log_cmd_error("Modules are not equivalent!\n");
+ log("Verified %s = %s (using brute-force check on %d cases).\n",
+ mod1_name.c_str(), mod2_name.c_str(), checker.counter);
+ return;
+ }
+ if (args[argidx] == "-vloghammer_report" && argidx+5 == args.size()) {
+ /* this should only be used for regression testing of ConstEval -- see vloghammer */
+ std::string module_prefix = args[++argidx];
+ std::string module_list = args[++argidx];
+ std::string input_list = args[++argidx];
+ std::string pattern_list = args[++argidx];
+ VlogHammerReporter reporter(design, module_prefix, module_list, input_list, pattern_list);
+ reporter.run();
+ return;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ RTLIL::Module *module = NULL;
+ for (auto &mod_it : design->modules_)
+ if (design->selected(mod_it.second)) {
+ if (module)
+ log_cmd_error("Only one module must be selected for the EVAL pass! (selected: %s and %s)\n",
+ RTLIL::id2cstr(module->name), RTLIL::id2cstr(mod_it.first));
+ module = mod_it.second;
+ }
+ if (module == NULL)
+ log_cmd_error("Can't perform EVAL on an empty selection!\n");
+
+ ConstEval ce(module);
+
+ for (auto &it : sets) {
+ RTLIL::SigSpec lhs, rhs;
+ if (!RTLIL::SigSpec::parse_sel(lhs, design, module, it.first))
+ log_cmd_error("Failed to parse lhs set expression `%s'.\n", it.first.c_str());
+ if (!RTLIL::SigSpec::parse_rhs(lhs, rhs, module, it.second))
+ log_cmd_error("Failed to parse rhs set expression `%s'.\n", it.second.c_str());
+ if (!rhs.is_fully_const())
+ log_cmd_error("Right-hand-side set expression `%s' is not constant.\n", it.second.c_str());
+ if (lhs.size() != rhs.size())
+ log_cmd_error("Set expression with different lhs and rhs sizes: %s (%s, %d bits) vs. %s (%s, %d bits)\n",
+ it.first.c_str(), log_signal(lhs), lhs.size(), it.second.c_str(), log_signal(rhs), rhs.size());
+ ce.set(lhs, rhs.as_const());
+ }
+
+ if (shows.size() == 0) {
+ for (auto &it : module->wires_)
+ if (it.second->port_output)
+ shows.push_back(it.second->name.str());
+ }
+
+ if (tables.empty())
+ {
+ for (auto &it : shows) {
+ RTLIL::SigSpec signal, value, undef;
+ if (!RTLIL::SigSpec::parse_sel(signal, design, module, it))
+ log_cmd_error("Failed to parse show expression `%s'.\n", it.c_str());
+ value = signal;
+ if (set_undef) {
+ while (!ce.eval(value, undef)) {
+ log("Failed to evaluate signal %s: Missing value for %s. -> setting to undef\n", log_signal(signal), log_signal(undef));
+ ce.set(undef, RTLIL::Const(RTLIL::State::Sx, undef.size()));
+ undef = RTLIL::SigSpec();
+ }
+ log("Eval result: %s = %s.\n", log_signal(signal), log_signal(value));
+ } else {
+ if (!ce.eval(value, undef))
+ log("Failed to evaluate signal %s: Missing value for %s.\n", log_signal(signal), log_signal(undef));
+ else
+ log("Eval result: %s = %s.\n", log_signal(signal), log_signal(value));
+ }
+ }
+ }
+ else
+ {
+ RTLIL::SigSpec tabsigs, signal, value, undef;
+ std::vector<std::vector<std::string>> tab;
+ int tab_sep_colidx = 0;
+
+ for (auto &it : shows) {
+ RTLIL::SigSpec sig;
+ if (!RTLIL::SigSpec::parse_sel(sig, design, module, it))
+ log_cmd_error("Failed to parse show expression `%s'.\n", it.c_str());
+ signal.append(sig);
+ }
+
+ for (auto &it : tables) {
+ RTLIL::SigSpec sig;
+ if (!RTLIL::SigSpec::parse_sel(sig, design, module, it))
+ log_cmd_error("Failed to parse table expression `%s'.\n", it.c_str());
+ tabsigs.append(sig);
+ }
+
+ std::vector<std::string> tab_line;
+ for (auto &c : tabsigs.chunks())
+ tab_line.push_back(log_signal(c));
+ tab_sep_colidx = tab_line.size();
+ for (auto &c : signal.chunks())
+ tab_line.push_back(log_signal(c));
+ tab.push_back(tab_line);
+ tab_line.clear();
+
+ RTLIL::Const tabvals(0, tabsigs.size());
+ do
+ {
+ ce.push();
+ ce.set(tabsigs, tabvals);
+ value = signal;
+
+ RTLIL::SigSpec this_undef;
+ while (!ce.eval(value, this_undef)) {
+ if (!set_undef) {
+ log("Failed to evaluate signal %s at %s = %s: Missing value for %s.\n", log_signal(signal),
+ log_signal(tabsigs), log_signal(tabvals), log_signal(this_undef));
+ return;
+ }
+ ce.set(this_undef, RTLIL::Const(RTLIL::State::Sx, this_undef.size()));
+ undef.append(this_undef);
+ this_undef = RTLIL::SigSpec();
+ }
+
+ int pos = 0;
+ for (auto &c : tabsigs.chunks()) {
+ tab_line.push_back(log_signal(RTLIL::SigSpec(tabvals).extract(pos, c.width)));
+ pos += c.width;
+ }
+
+ pos = 0;
+ for (auto &c : signal.chunks()) {
+ tab_line.push_back(log_signal(value.extract(pos, c.width)));
+ pos += c.width;
+ }
+
+ tab.push_back(tab_line);
+ tab_line.clear();
+ ce.pop();
+
+ tabvals = RTLIL::const_add(tabvals, RTLIL::Const(1), false, false, tabvals.bits.size());
+ }
+ while (tabvals.as_bool());
+
+ std::vector<int> tab_column_width;
+ for (auto &row : tab) {
+ if (tab_column_width.size() < row.size())
+ tab_column_width.resize(row.size());
+ for (size_t i = 0; i < row.size(); i++)
+ tab_column_width[i] = max(tab_column_width[i], int(row[i].size()));
+ }
+
+ log("\n");
+ bool first = true;
+ for (auto &row : tab) {
+ for (size_t i = 0; i < row.size(); i++) {
+ int k = int(i) < tab_sep_colidx ? tab_sep_colidx - i - 1 : i;
+ log(" %s%*s", k == tab_sep_colidx ? "| " : "", tab_column_width[k], row[k].c_str());
+ }
+ log("\n");
+ if (first) {
+ for (size_t i = 0; i < row.size(); i++) {
+ int k = int(i) < tab_sep_colidx ? tab_sep_colidx - i - 1 : i;
+ log(" %s", k == tab_sep_colidx ? "| " : "");
+ for (int j = 0; j < tab_column_width[k]; j++)
+ log("-");
+ }
+ log("\n");
+ first = false;
+ }
+ }
+
+ log("\n");
+ if (undef.size() > 0) {
+ undef.sort_and_unify();
+ log("Assumed undef (x) value for the following signals: %s\n\n", log_signal(undef));
+ }
+ }
+ }
+} EvalPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/sat/example.v b/passes/sat/example.v
new file mode 100644
index 00000000..aa0ddb6e
--- /dev/null
+++ b/passes/sat/example.v
@@ -0,0 +1,85 @@
+
+module example001(a, y);
+
+input [15:0] a;
+output y;
+
+wire gt = a > 12345;
+wire lt = a < 12345;
+assign y = !gt && !lt;
+
+endmodule
+
+// ------------------------------------
+
+module example002(a, y);
+
+input [3:0] a;
+output y;
+reg [1:0] t1, t2;
+
+always @* begin
+ casex (a)
+ 16'b1xxx:
+ t1 <= 1;
+ 16'bx1xx:
+ t1 <= 2;
+ 16'bxx1x:
+ t1 <= 3;
+ 16'bxxx1:
+ t1 <= 4;
+ default:
+ t1 <= 0;
+ endcase
+ casex (a)
+ 16'b1xxx:
+ t2 <= 1;
+ 16'b01xx:
+ t2 <= 2;
+ 16'b001x:
+ t2 <= 3;
+ 16'b0001:
+ t2 <= 4;
+ default:
+ t2 <= 0;
+ endcase
+end
+
+assign y = t1 != t2;
+
+endmodule
+
+// ------------------------------------
+
+module example003(a_shl, a_shr, a_sshl, a_sshr, sh, y_shl, y_shr, y_sshl, y_sshr);
+
+input [7:0] a_shl, a_shr;
+input signed [7:0] a_sshl, a_sshr;
+input [3:0] sh;
+
+output [7:0] y_shl = a_shl << sh, y_shr = a_shr >> sh;
+output signed [7:0] y_sshl = a_sshl <<< sh, y_sshr = a_sshr >>> sh;
+
+endmodule
+
+// ------------------------------------
+
+module example004(clk, rst, y);
+
+input clk, rst;
+output y;
+
+reg [3:0] counter;
+
+always @(posedge clk)
+ case (1'b1)
+ rst, counter == 9:
+ counter <= 0;
+ default:
+ counter <= counter+1;
+ endcase
+
+assign y = counter == 12;
+
+endmodule
+
diff --git a/passes/sat/example.ys b/passes/sat/example.ys
new file mode 100644
index 00000000..cc72faac
--- /dev/null
+++ b/passes/sat/example.ys
@@ -0,0 +1,14 @@
+
+read_verilog example.v
+proc; opt_clean
+echo on
+
+sat -set y 1'b1 example001
+sat -set y 1'b1 example002
+sat -set y_sshl 8'hf0 -set y_sshr 8'hf0 -set sh 4'd3 example003
+sat -set y 1'b1 -ignore_unknown_cells example004
+sat -show rst,counter -set-at 3 y 1'b1 -seq 4 example004
+
+sat -prove y 1'b0 -show rst,counter,y -ignore_unknown_cells example004
+sat -prove y 1'b0 -tempinduct -show rst,counter,y -set-at 1 rst 1'b1 -seq 1 example004
+
diff --git a/passes/sat/expose.cc b/passes/sat/expose.cc
new file mode 100644
index 00000000..9427547f
--- /dev/null
+++ b/passes/sat/expose.cc
@@ -0,0 +1,650 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "kernel/register.h"
+#include "kernel/celltypes.h"
+#include "kernel/sigtools.h"
+#include "kernel/rtlil.h"
+#include "kernel/log.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct dff_map_info_t {
+ RTLIL::SigSpec sig_d, sig_clk, sig_arst;
+ bool clk_polarity, arst_polarity;
+ RTLIL::Const arst_value;
+ std::vector<RTLIL::IdString> cells;
+};
+
+struct dff_map_bit_info_t {
+ RTLIL::SigBit bit_d, bit_clk, bit_arst;
+ bool clk_polarity, arst_polarity;
+ RTLIL::State arst_value;
+ RTLIL::Cell *cell;
+};
+
+bool consider_wire(RTLIL::Wire *wire, std::map<RTLIL::IdString, dff_map_info_t> &dff_dq_map)
+{
+ if (wire->name[0] == '$' || dff_dq_map.count(wire->name))
+ return false;
+ if (wire->port_input)
+ return false;
+ return true;
+}
+
+bool consider_cell(RTLIL::Design *design, std::set<RTLIL::IdString> &dff_cells, RTLIL::Cell *cell)
+{
+ if (cell->name[0] == '$' || dff_cells.count(cell->name))
+ return false;
+ if (cell->type[0] == '\\' && !design->modules_.count(cell->type))
+ return false;
+ return true;
+}
+
+bool compare_wires(RTLIL::Wire *wire1, RTLIL::Wire *wire2)
+{
+ log_assert(wire1->name == wire2->name);
+ if (wire1->width != wire2->width)
+ return false;
+ return true;
+}
+
+bool compare_cells(RTLIL::Cell *cell1, RTLIL::Cell *cell2)
+{
+ log_assert(cell1->name == cell2->name);
+ if (cell1->type != cell2->type)
+ return false;
+ if (cell1->parameters != cell2->parameters)
+ return false;
+ return true;
+}
+
+void find_dff_wires(std::set<RTLIL::IdString> &dff_wires, RTLIL::Module *module)
+{
+ CellTypes ct;
+ ct.setup_internals_mem();
+ ct.setup_stdcells_mem();
+
+ SigMap sigmap(module);
+ SigPool dffsignals;
+
+ for (auto &it : module->cells_) {
+ if (ct.cell_known(it.second->type) && it.second->hasPort("\\Q"))
+ dffsignals.add(sigmap(it.second->getPort("\\Q")));
+ }
+
+ for (auto &it : module->wires_) {
+ if (dffsignals.check_any(it.second))
+ dff_wires.insert(it.first);
+ }
+}
+
+void create_dff_dq_map(std::map<RTLIL::IdString, dff_map_info_t> &map, RTLIL::Design *design, RTLIL::Module *module)
+{
+ std::map<RTLIL::SigBit, dff_map_bit_info_t> bit_info;
+ SigMap sigmap(module);
+
+ for (auto &it : module->cells_)
+ {
+ if (!design->selected(module, it.second))
+ continue;
+
+ dff_map_bit_info_t info;
+ info.bit_d = RTLIL::State::Sm;
+ info.bit_clk = RTLIL::State::Sm;
+ info.bit_arst = RTLIL::State::Sm;
+ info.clk_polarity = false;
+ info.arst_polarity = false;
+ info.arst_value = RTLIL::State::Sm;
+ info.cell = it.second;
+
+ if (info.cell->type == "$dff") {
+ info.bit_clk = sigmap(info.cell->getPort("\\CLK")).as_bit();
+ info.clk_polarity = info.cell->parameters.at("\\CLK_POLARITY").as_bool();
+ std::vector<RTLIL::SigBit> sig_d = sigmap(info.cell->getPort("\\D")).to_sigbit_vector();
+ std::vector<RTLIL::SigBit> sig_q = sigmap(info.cell->getPort("\\Q")).to_sigbit_vector();
+ for (size_t i = 0; i < sig_d.size(); i++) {
+ info.bit_d = sig_d.at(i);
+ bit_info[sig_q.at(i)] = info;
+ }
+ continue;
+ }
+
+ if (info.cell->type == "$adff") {
+ info.bit_clk = sigmap(info.cell->getPort("\\CLK")).as_bit();
+ info.bit_arst = sigmap(info.cell->getPort("\\ARST")).as_bit();
+ info.clk_polarity = info.cell->parameters.at("\\CLK_POLARITY").as_bool();
+ info.arst_polarity = info.cell->parameters.at("\\ARST_POLARITY").as_bool();
+ std::vector<RTLIL::SigBit> sig_d = sigmap(info.cell->getPort("\\D")).to_sigbit_vector();
+ std::vector<RTLIL::SigBit> sig_q = sigmap(info.cell->getPort("\\Q")).to_sigbit_vector();
+ std::vector<RTLIL::State> arst_value = info.cell->parameters.at("\\ARST_VALUE").bits;
+ for (size_t i = 0; i < sig_d.size(); i++) {
+ info.bit_d = sig_d.at(i);
+ info.arst_value = arst_value.at(i);
+ bit_info[sig_q.at(i)] = info;
+ }
+ continue;
+ }
+
+ if (info.cell->type == "$_DFF_N_" || info.cell->type == "$_DFF_P_") {
+ info.bit_clk = sigmap(info.cell->getPort("\\C")).as_bit();
+ info.clk_polarity = info.cell->type == "$_DFF_P_";
+ info.bit_d = sigmap(info.cell->getPort("\\D")).as_bit();
+ bit_info[sigmap(info.cell->getPort("\\Q")).as_bit()] = info;
+ continue;
+ }
+
+ if (info.cell->type.size() == 10 && info.cell->type.substr(0, 6) == "$_DFF_") {
+ info.bit_clk = sigmap(info.cell->getPort("\\C")).as_bit();
+ info.bit_arst = sigmap(info.cell->getPort("\\R")).as_bit();
+ info.clk_polarity = info.cell->type[6] == 'P';
+ info.arst_polarity = info.cell->type[7] == 'P';
+ info.arst_value = info.cell->type[0] == '1' ? RTLIL::State::S1 : RTLIL::State::S0;
+ info.bit_d = sigmap(info.cell->getPort("\\D")).as_bit();
+ bit_info[sigmap(info.cell->getPort("\\Q")).as_bit()] = info;
+ continue;
+ }
+ }
+
+ std::map<RTLIL::IdString, dff_map_info_t> empty_dq_map;
+ for (auto &it : module->wires_)
+ {
+ if (!consider_wire(it.second, empty_dq_map))
+ continue;
+
+ std::vector<RTLIL::SigBit> bits_q = sigmap(it.second).to_sigbit_vector();
+ std::vector<RTLIL::SigBit> bits_d;
+ std::vector<RTLIL::State> arst_value;
+ std::set<RTLIL::Cell*> cells;
+
+ if (bits_q.empty() || !bit_info.count(bits_q.front()))
+ continue;
+
+ dff_map_bit_info_t ref_info = bit_info.at(bits_q.front());
+ for (auto &bit : bits_q) {
+ if (!bit_info.count(bit))
+ break;
+ dff_map_bit_info_t info = bit_info.at(bit);
+ if (info.bit_clk != ref_info.bit_clk)
+ break;
+ if (info.bit_arst != ref_info.bit_arst)
+ break;
+ if (info.clk_polarity != ref_info.clk_polarity)
+ break;
+ if (info.arst_polarity != ref_info.arst_polarity)
+ break;
+ bits_d.push_back(info.bit_d);
+ arst_value.push_back(info.arst_value);
+ cells.insert(info.cell);
+ }
+
+ if (bits_d.size() != bits_q.size())
+ continue;
+
+ dff_map_info_t info;
+ info.sig_d = bits_d;
+ info.sig_clk = ref_info.bit_clk;
+ info.sig_arst = ref_info.bit_arst;
+ info.clk_polarity = ref_info.clk_polarity;
+ info.arst_polarity = ref_info.arst_polarity;
+ info.arst_value = arst_value;
+ for (auto it : cells)
+ info.cells.push_back(it->name);
+ map[it.first] = info;
+ }
+}
+
+RTLIL::Wire *add_new_wire(RTLIL::Module *module, RTLIL::IdString name, int width = 1)
+{
+ if (module->count_id(name))
+ log_error("Attempting to create wire %s, but a wire of this name exists already! Hint: Try another value for -sep.\n", log_id(name));
+ return module->addWire(name, width);
+}
+
+struct ExposePass : public Pass {
+ ExposePass() : Pass("expose", "convert internal signals to module ports") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" expose [options] [selection]\n");
+ log("\n");
+ log("This command exposes all selected internal signals of a module as additional\n");
+ log("outputs.\n");
+ log("\n");
+ log(" -dff\n");
+ log(" only consider wires that are directly driven by register cell.\n");
+ log("\n");
+ log(" -cut\n");
+ log(" when exposing a wire, create an input/output pair and cut the internal\n");
+ log(" signal path at that wire.\n");
+ log("\n");
+ log(" -shared\n");
+ log(" only expose those signals that are shared among the selected modules.\n");
+ log(" this is useful for preparing modules for equivalence checking.\n");
+ log("\n");
+ log(" -evert\n");
+ log(" also turn connections to instances of other modules to additional\n");
+ log(" inputs and outputs and remove the module instances.\n");
+ log("\n");
+ log(" -evert-dff\n");
+ log(" turn flip-flops to sets of inputs and outputs.\n");
+ log("\n");
+ log(" -sep <separator>\n");
+ log(" when creating new wire/port names, the original object name is suffixed\n");
+ log(" with this separator (default: '.') and the port name or a type\n");
+ log(" designator for the exposed signal.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ bool flag_shared = false;
+ bool flag_evert = false;
+ bool flag_dff = false;
+ bool flag_cut = false;
+ bool flag_evert_dff = false;
+ std::string sep = ".";
+
+ log_header(design, "Executing EXPOSE pass (exposing internal signals as outputs).\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ if (args[argidx] == "-shared") {
+ flag_shared = true;
+ continue;
+ }
+ if (args[argidx] == "-evert") {
+ flag_evert = true;
+ continue;
+ }
+ if (args[argidx] == "-dff") {
+ flag_dff = true;
+ continue;
+ }
+ if (args[argidx] == "-cut") {
+ flag_cut = true;
+ continue;
+ }
+ if (args[argidx] == "-evert-dff") {
+ flag_evert_dff = true;
+ continue;
+ }
+ if (args[argidx] == "-sep" && argidx+1 < args.size()) {
+ sep = args[++argidx];
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ CellTypes ct(design);
+
+ std::map<RTLIL::Module*, std::map<RTLIL::IdString, dff_map_info_t>> dff_dq_maps;
+ std::map<RTLIL::Module*, std::set<RTLIL::IdString>> dff_cells;
+
+ if (flag_evert_dff)
+ {
+ RTLIL::Module *first_module = NULL;
+ std::set<RTLIL::IdString> shared_dff_wires;
+
+ for (auto &mod_it : design->modules_)
+ {
+ if (!design->selected(mod_it.second))
+ continue;
+
+ create_dff_dq_map(dff_dq_maps[mod_it.second], design, mod_it.second);
+
+ if (!flag_shared)
+ continue;
+
+ if (first_module == NULL) {
+ for (auto &it : dff_dq_maps[mod_it.second])
+ shared_dff_wires.insert(it.first);
+ first_module = mod_it.second;
+ } else {
+ std::set<RTLIL::IdString> new_shared_dff_wires;
+ for (auto &it : shared_dff_wires) {
+ if (!dff_dq_maps[mod_it.second].count(it))
+ continue;
+ if (!compare_wires(first_module->wires_.at(it), mod_it.second->wires_.at(it)))
+ continue;
+ new_shared_dff_wires.insert(it);
+ }
+ shared_dff_wires.swap(new_shared_dff_wires);
+ }
+ }
+
+ if (flag_shared)
+ for (auto &map_it : dff_dq_maps)
+ {
+ std::map<RTLIL::IdString, dff_map_info_t> new_map;
+ for (auto &it : map_it.second)
+ if (shared_dff_wires.count(it.first))
+ new_map[it.first] = it.second;
+ map_it.second.swap(new_map);
+ }
+
+ for (auto &it1 : dff_dq_maps)
+ for (auto &it2 : it1.second)
+ for (auto &it3 : it2.second.cells)
+ dff_cells[it1.first].insert(it3);
+ }
+
+ std::set<RTLIL::IdString> shared_wires, shared_cells;
+ std::set<RTLIL::IdString> used_names;
+
+ if (flag_shared)
+ {
+ RTLIL::Module *first_module = NULL;
+
+ for (auto &mod_it : design->modules_)
+ {
+ RTLIL::Module *module = mod_it.second;
+
+ if (!design->selected(module))
+ continue;
+
+ std::set<RTLIL::IdString> dff_wires;
+ if (flag_dff)
+ find_dff_wires(dff_wires, module);
+
+ if (first_module == NULL)
+ {
+ for (auto &it : module->wires_)
+ if (design->selected(module, it.second) && consider_wire(it.second, dff_dq_maps[module]))
+ if (!flag_dff || dff_wires.count(it.first))
+ shared_wires.insert(it.first);
+
+ if (flag_evert)
+ for (auto &it : module->cells_)
+ if (design->selected(module, it.second) && consider_cell(design, dff_cells[module], it.second))
+ shared_cells.insert(it.first);
+
+ first_module = module;
+ }
+ else
+ {
+ std::vector<RTLIL::IdString> delete_shared_wires, delete_shared_cells;
+
+ for (auto &it : shared_wires)
+ {
+ RTLIL::Wire *wire;
+
+ if (module->wires_.count(it) == 0)
+ goto delete_shared_wire;
+
+ wire = module->wires_.at(it);
+
+ if (!design->selected(module, wire))
+ goto delete_shared_wire;
+ if (!consider_wire(wire, dff_dq_maps[module]))
+ goto delete_shared_wire;
+ if (!compare_wires(first_module->wires_.at(it), wire))
+ goto delete_shared_wire;
+ if (flag_dff && !dff_wires.count(it))
+ goto delete_shared_wire;
+
+ if (0)
+ delete_shared_wire:
+ delete_shared_wires.push_back(it);
+ }
+
+ if (flag_evert)
+ for (auto &it : shared_cells)
+ {
+ RTLIL::Cell *cell;
+
+ if (module->cells_.count(it) == 0)
+ goto delete_shared_cell;
+
+ cell = module->cells_.at(it);
+
+ if (!design->selected(module, cell))
+ goto delete_shared_cell;
+ if (!consider_cell(design, dff_cells[module], cell))
+ goto delete_shared_cell;
+ if (!compare_cells(first_module->cells_.at(it), cell))
+ goto delete_shared_cell;
+
+ if (0)
+ delete_shared_cell:
+ delete_shared_cells.push_back(it);
+ }
+
+ for (auto &it : delete_shared_wires)
+ shared_wires.erase(it);
+ for (auto &it : delete_shared_cells)
+ shared_cells.erase(it);
+ }
+ }
+ }
+
+ for (auto &mod_it : design->modules_)
+ {
+ RTLIL::Module *module = mod_it.second;
+
+ if (!design->selected(module))
+ continue;
+
+ std::set<RTLIL::IdString> dff_wires;
+ if (flag_dff && !flag_shared)
+ find_dff_wires(dff_wires, module);
+
+ SigMap sigmap(module);
+
+ SigMap out_to_in_map;
+
+ for (auto &it : module->wires_)
+ {
+ if (flag_shared) {
+ if (shared_wires.count(it.first) == 0)
+ continue;
+ } else {
+ if (!design->selected(module, it.second) || !consider_wire(it.second, dff_dq_maps[module]))
+ continue;
+ if (flag_dff && !dff_wires.count(it.first))
+ continue;
+ }
+
+ if (!it.second->port_output) {
+ it.second->port_output = true;
+ log("New module port: %s/%s\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(it.second->name));
+ }
+
+ if (flag_cut) {
+ RTLIL::Wire *in_wire = add_new_wire(module, it.second->name.str() + sep + "i", it.second->width);
+ in_wire->port_input = true;
+ out_to_in_map.add(sigmap(it.second), in_wire);
+ }
+ }
+
+ if (flag_cut)
+ {
+ for (auto &it : module->cells_) {
+ if (!ct.cell_known(it.second->type))
+ continue;
+ for (auto &conn : it.second->connections_)
+ if (ct.cell_input(it.second->type, conn.first))
+ conn.second = out_to_in_map(sigmap(conn.second));
+ }
+
+ for (auto &conn : module->connections_)
+ conn.second = out_to_in_map(sigmap(conn.second));
+ }
+
+ std::set<RTLIL::SigBit> set_q_bits;
+
+ for (auto &dq : dff_dq_maps[module])
+ {
+ if (!module->wires_.count(dq.first))
+ continue;
+
+ RTLIL::Wire *wire = module->wires_.at(dq.first);
+ std::set<RTLIL::SigBit> wire_bits_set = sigmap(wire).to_sigbit_set();
+ std::vector<RTLIL::SigBit> wire_bits_vec = sigmap(wire).to_sigbit_vector();
+
+ dff_map_info_t &info = dq.second;
+
+ RTLIL::Wire *wire_dummy_q = add_new_wire(module, NEW_ID, 0);
+
+ for (auto &cell_name : info.cells) {
+ RTLIL::Cell *cell = module->cells_.at(cell_name);
+ std::vector<RTLIL::SigBit> cell_q_bits = sigmap(cell->getPort("\\Q")).to_sigbit_vector();
+ for (auto &bit : cell_q_bits)
+ if (wire_bits_set.count(bit))
+ bit = RTLIL::SigBit(wire_dummy_q, wire_dummy_q->width++);
+ cell->setPort("\\Q", cell_q_bits);
+ }
+
+ RTLIL::Wire *wire_q = add_new_wire(module, wire->name.str() + sep + "q", wire->width);
+ wire_q->port_input = true;
+ log("New module port: %s/%s\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(wire_q->name));
+
+ RTLIL::SigSig connect_q;
+ for (size_t i = 0; i < wire_bits_vec.size(); i++) {
+ if (set_q_bits.count(wire_bits_vec[i]))
+ continue;
+ connect_q.first.append(wire_bits_vec[i]);
+ connect_q.second.append(RTLIL::SigBit(wire_q, i));
+ set_q_bits.insert(wire_bits_vec[i]);
+ }
+ module->connect(connect_q);
+
+ RTLIL::Wire *wire_d = add_new_wire(module, wire->name.str() + sep + "d", wire->width);
+ wire_d->port_output = true;
+ log("New module port: %s/%s\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(wire_d->name));
+ module->connect(RTLIL::SigSig(wire_d, info.sig_d));
+
+ RTLIL::Wire *wire_c = add_new_wire(module, wire->name.str() + sep + "c");
+ wire_c->port_output = true;
+ log("New module port: %s/%s\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(wire_c->name));
+ if (info.clk_polarity) {
+ module->connect(RTLIL::SigSig(wire_c, info.sig_clk));
+ } else {
+ RTLIL::Cell *c = module->addCell(NEW_ID, "$not");
+ c->parameters["\\A_SIGNED"] = 0;
+ c->parameters["\\A_WIDTH"] = 1;
+ c->parameters["\\Y_WIDTH"] = 1;
+ c->setPort("\\A", info.sig_clk);
+ c->setPort("\\Y", wire_c);
+ }
+
+ if (info.sig_arst != RTLIL::State::Sm)
+ {
+ RTLIL::Wire *wire_r = add_new_wire(module, wire->name.str() + sep + "r");
+ wire_r->port_output = true;
+ log("New module port: %s/%s\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(wire_r->name));
+ if (info.arst_polarity) {
+ module->connect(RTLIL::SigSig(wire_r, info.sig_arst));
+ } else {
+ RTLIL::Cell *c = module->addCell(NEW_ID, "$not");
+ c->parameters["\\A_SIGNED"] = 0;
+ c->parameters["\\A_WIDTH"] = 1;
+ c->parameters["\\Y_WIDTH"] = 1;
+ c->setPort("\\A", info.sig_arst);
+ c->setPort("\\Y", wire_r);
+ }
+
+ RTLIL::Wire *wire_v = add_new_wire(module, wire->name.str() + sep + "v", wire->width);
+ wire_v->port_output = true;
+ log("New module port: %s/%s\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(wire_v->name));
+ module->connect(RTLIL::SigSig(wire_v, info.arst_value));
+ }
+ }
+
+ if (flag_evert)
+ {
+ std::vector<RTLIL::Cell*> delete_cells;
+
+ for (auto &it : module->cells_)
+ {
+ if (flag_shared) {
+ if (shared_cells.count(it.first) == 0)
+ continue;
+ } else {
+ if (!design->selected(module, it.second) || !consider_cell(design, dff_cells[module], it.second))
+ continue;
+ }
+
+ RTLIL::Cell *cell = it.second;
+
+ if (design->modules_.count(cell->type))
+ {
+ RTLIL::Module *mod = design->modules_.at(cell->type);
+
+ for (auto &it : mod->wires_)
+ {
+ RTLIL::Wire *p = it.second;
+ if (!p->port_input && !p->port_output)
+ continue;
+
+ RTLIL::Wire *w = add_new_wire(module, cell->name.str() + sep + RTLIL::unescape_id(p->name), p->width);
+ if (p->port_input)
+ w->port_output = true;
+ if (p->port_output)
+ w->port_input = true;
+
+ log("New module port: %s/%s (%s)\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(w->name), RTLIL::id2cstr(cell->type));
+
+ RTLIL::SigSpec sig;
+ if (cell->hasPort(p->name))
+ sig = cell->getPort(p->name);
+ sig.extend_u0(w->width);
+ if (w->port_input)
+ module->connect(RTLIL::SigSig(sig, w));
+ else
+ module->connect(RTLIL::SigSig(w, sig));
+ }
+ }
+ else
+ {
+ for (auto &it : cell->connections())
+ {
+ RTLIL::Wire *w = add_new_wire(module, cell->name.str() + sep + RTLIL::unescape_id(it.first), it.second.size());
+ if (ct.cell_input(cell->type, it.first))
+ w->port_output = true;
+ if (ct.cell_output(cell->type, it.first))
+ w->port_input = true;
+
+ log("New module port: %s/%s (%s)\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(w->name), RTLIL::id2cstr(cell->type));
+
+ if (w->port_input)
+ module->connect(RTLIL::SigSig(it.second, w));
+ else
+ module->connect(RTLIL::SigSig(w, it.second));
+ }
+ }
+
+ delete_cells.push_back(cell);
+ }
+
+ for (auto cell : delete_cells) {
+ log("Removing cell: %s/%s (%s)\n", log_id(module), log_id(cell), log_id(cell->type));
+ module->remove(cell);
+ }
+ }
+
+ module->fixup_ports();
+ }
+ }
+} ExposePass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/sat/freduce.cc b/passes/sat/freduce.cc
new file mode 100644
index 00000000..77263f6a
--- /dev/null
+++ b/passes/sat/freduce.cc
@@ -0,0 +1,840 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "kernel/register.h"
+#include "kernel/celltypes.h"
+#include "kernel/consteval.h"
+#include "kernel/sigtools.h"
+#include "kernel/log.h"
+#include "kernel/satgen.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <algorithm>
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+bool inv_mode;
+int verbose_level, reduce_counter, reduce_stop_at;
+typedef std::map<RTLIL::SigBit, std::pair<RTLIL::Cell*, std::set<RTLIL::SigBit>>> drivers_t;
+std::string dump_prefix;
+
+struct equiv_bit_t
+{
+ int depth;
+ bool inverted;
+ RTLIL::Cell *drv;
+ RTLIL::SigBit bit;
+
+ bool operator<(const equiv_bit_t &other) const {
+ if (depth != other.depth)
+ return depth < other.depth;
+ if (inverted != other.inverted)
+ return inverted < other.inverted;
+ if (drv != other.drv)
+ return drv < other.drv;
+ return bit < other.bit;
+ }
+};
+
+struct CountBitUsage
+{
+ SigMap &sigmap;
+ std::map<RTLIL::SigBit, int> &cache;
+
+ CountBitUsage(SigMap &sigmap, std::map<RTLIL::SigBit, int> &cache) : sigmap(sigmap), cache(cache) { }
+
+ void operator()(RTLIL::SigSpec &sig) {
+ std::vector<RTLIL::SigBit> vec = sigmap(sig).to_sigbit_vector();
+ for (auto &bit : vec)
+ cache[bit]++;
+ }
+};
+
+struct FindReducedInputs
+{
+ SigMap &sigmap;
+ drivers_t &drivers;
+
+ ezSatPtr ez;
+ std::set<RTLIL::Cell*> ez_cells;
+ SatGen satgen;
+
+ std::map<RTLIL::SigBit, int> sat_pi;
+ std::vector<int> sat_pi_uniq_bitvec;
+
+ FindReducedInputs(SigMap &sigmap, drivers_t &drivers) :
+ sigmap(sigmap), drivers(drivers), satgen(ez.get(), &sigmap)
+ {
+ satgen.model_undef = true;
+ }
+
+ int get_bits(int val)
+ {
+ int bits = 0;
+ for (int i = 8*sizeof(int); val; i = i >> 1)
+ if (val >> (i-1)) {
+ bits += i;
+ val = val >> i;
+ }
+ return bits;
+ }
+
+ void register_pi_bit(RTLIL::SigBit bit)
+ {
+ if (sat_pi.count(bit) != 0)
+ return;
+
+ satgen.setContext(&sigmap, "A");
+ int sat_a = satgen.importSigSpec(bit).front();
+ ez->assume(ez->NOT(satgen.importUndefSigSpec(bit).front()));
+
+ satgen.setContext(&sigmap, "B");
+ int sat_b = satgen.importSigSpec(bit).front();
+ ez->assume(ez->NOT(satgen.importUndefSigSpec(bit).front()));
+
+ int idx = sat_pi.size();
+ size_t idx_bits = get_bits(idx);
+
+ if (sat_pi_uniq_bitvec.size() != idx_bits) {
+ sat_pi_uniq_bitvec.push_back(ez->frozen_literal(stringf("uniq_%d", int(idx_bits)-1)));
+ for (auto &it : sat_pi)
+ ez->assume(ez->OR(ez->NOT(it.second), ez->NOT(sat_pi_uniq_bitvec.back())));
+ }
+ log_assert(sat_pi_uniq_bitvec.size() == idx_bits);
+
+ sat_pi[bit] = ez->frozen_literal(stringf("p, falsei_%s", log_signal(bit)));
+ ez->assume(ez->IFF(ez->XOR(sat_a, sat_b), sat_pi[bit]));
+
+ for (size_t i = 0; i < idx_bits; i++)
+ if ((idx & (1 << i)) == 0)
+ ez->assume(ez->OR(ez->NOT(sat_pi[bit]), ez->NOT(sat_pi_uniq_bitvec[i])));
+ else
+ ez->assume(ez->OR(ez->NOT(sat_pi[bit]), sat_pi_uniq_bitvec[i]));
+ }
+
+ void register_cone_worker(std::set<RTLIL::SigBit> &pi, std::set<RTLIL::SigBit> &sigdone, RTLIL::SigBit out)
+ {
+ if (out.wire == NULL)
+ return;
+ if (sigdone.count(out) != 0)
+ return;
+ sigdone.insert(out);
+
+ if (drivers.count(out) != 0) {
+ std::pair<RTLIL::Cell*, std::set<RTLIL::SigBit>> &drv = drivers.at(out);
+ if (ez_cells.count(drv.first) == 0) {
+ satgen.setContext(&sigmap, "A");
+ if (!satgen.importCell(drv.first))
+ log_error("Can't create SAT model for cell %s (%s)!\n", RTLIL::id2cstr(drv.first->name), RTLIL::id2cstr(drv.first->type));
+ satgen.setContext(&sigmap, "B");
+ if (!satgen.importCell(drv.first))
+ log_abort();
+ ez_cells.insert(drv.first);
+ }
+ for (auto &bit : drv.second)
+ register_cone_worker(pi, sigdone, bit);
+ } else {
+ register_pi_bit(out);
+ pi.insert(out);
+ }
+ }
+
+ void register_cone(std::vector<RTLIL::SigBit> &pi, RTLIL::SigBit out)
+ {
+ std::set<RTLIL::SigBit> pi_set, sigdone;
+ register_cone_worker(pi_set, sigdone, out);
+ pi.clear();
+ pi.insert(pi.end(), pi_set.begin(), pi_set.end());
+ }
+
+ void analyze(std::vector<RTLIL::SigBit> &reduced_inputs, RTLIL::SigBit output, int prec)
+ {
+ if (verbose_level >= 1)
+ log("[%2d%%] Analyzing input cone for signal %s:\n", prec, log_signal(output));
+
+ std::vector<RTLIL::SigBit> pi;
+ register_cone(pi, output);
+
+ if (verbose_level >= 1)
+ log(" Found %d input signals and %d cells.\n", int(pi.size()), int(ez_cells.size()));
+
+ satgen.setContext(&sigmap, "A");
+ int output_a = satgen.importSigSpec(output).front();
+ int output_undef_a = satgen.importUndefSigSpec(output).front();
+
+ satgen.setContext(&sigmap, "B");
+ int output_b = satgen.importSigSpec(output).front();
+ int output_undef_b = satgen.importUndefSigSpec(output).front();
+
+ std::set<int> unused_pi_idx;
+
+ for (size_t i = 0; i < pi.size(); i++)
+ unused_pi_idx.insert(i);
+
+ while (1)
+ {
+ std::vector<int> model_pi_idx;
+ std::vector<int> model_expr;
+ std::vector<bool> model;
+
+ for (size_t i = 0; i < pi.size(); i++)
+ if (unused_pi_idx.count(i) != 0) {
+ model_pi_idx.push_back(i);
+ model_expr.push_back(sat_pi.at(pi[i]));
+ }
+
+ if (!ez->solve(model_expr, model, ez->expression(ezSAT::OpOr, model_expr), ez->XOR(output_a, output_b), ez->NOT(output_undef_a), ez->NOT(output_undef_b)))
+ break;
+
+ int found_count = 0;
+ for (size_t i = 0; i < model_pi_idx.size(); i++)
+ if (model[i]) {
+ if (verbose_level >= 2)
+ log(" Found relevant input: %s\n", log_signal(pi[model_pi_idx[i]]));
+ unused_pi_idx.erase(model_pi_idx[i]);
+ found_count++;
+ }
+ log_assert(found_count == 1);
+ }
+
+ for (size_t i = 0; i < pi.size(); i++)
+ if (unused_pi_idx.count(i) == 0)
+ reduced_inputs.push_back(pi[i]);
+
+ if (verbose_level >= 1)
+ log(" Reduced input cone contains %d inputs.\n", int(reduced_inputs.size()));
+ }
+};
+
+struct PerformReduction
+{
+ SigMap &sigmap;
+ drivers_t &drivers;
+ std::set<std::pair<RTLIL::SigBit, RTLIL::SigBit>> &inv_pairs;
+ pool<SigBit> recursion_guard;
+
+ ezSatPtr ez;
+ SatGen satgen;
+
+ std::vector<int> sat_pi, sat_out, sat_def;
+ std::vector<RTLIL::SigBit> out_bits, pi_bits;
+ std::vector<bool> out_inverted;
+ std::vector<int> out_depth;
+ int cone_size;
+
+ int register_cone_worker(std::set<RTLIL::Cell*> &celldone, std::map<RTLIL::SigBit, int> &sigdepth, RTLIL::SigBit out)
+ {
+ if (out.wire == NULL)
+ return 0;
+ if (sigdepth.count(out) != 0)
+ return sigdepth.at(out);
+
+ if (recursion_guard.count(out)) {
+ string loop_signals;
+ for (auto loop_bit : recursion_guard)
+ loop_signals += string(" ") + log_signal(loop_bit);
+ log_error("Found logic loop:%s\n", loop_signals.c_str());
+ }
+
+ recursion_guard.insert(out);
+
+ if (drivers.count(out) != 0) {
+ std::pair<RTLIL::Cell*, std::set<RTLIL::SigBit>> &drv = drivers.at(out);
+ if (celldone.count(drv.first) == 0) {
+ if (!satgen.importCell(drv.first))
+ log_error("Can't create SAT model for cell %s (%s)!\n", RTLIL::id2cstr(drv.first->name), RTLIL::id2cstr(drv.first->type));
+ celldone.insert(drv.first);
+ }
+ int max_child_depth = 0;
+ for (auto &bit : drv.second)
+ max_child_depth = max(register_cone_worker(celldone, sigdepth, bit), max_child_depth);
+ sigdepth[out] = max_child_depth + 1;
+ } else {
+ pi_bits.push_back(out);
+ sat_pi.push_back(satgen.importSigSpec(out).front());
+ ez->assume(ez->NOT(satgen.importUndefSigSpec(out).front()));
+ sigdepth[out] = 0;
+ }
+
+ recursion_guard.erase(out);
+ return sigdepth.at(out);
+ }
+
+ PerformReduction(SigMap &sigmap, drivers_t &drivers, std::set<std::pair<RTLIL::SigBit, RTLIL::SigBit>> &inv_pairs, std::vector<RTLIL::SigBit> &bits, int cone_size) :
+ sigmap(sigmap), drivers(drivers), inv_pairs(inv_pairs), satgen(ez.get(), &sigmap), out_bits(bits), cone_size(cone_size)
+ {
+ satgen.model_undef = true;
+
+ std::set<RTLIL::Cell*> celldone;
+ std::map<RTLIL::SigBit, int> sigdepth;
+
+ for (auto &bit : bits) {
+ out_depth.push_back(register_cone_worker(celldone, sigdepth, bit));
+ sat_out.push_back(satgen.importSigSpec(bit).front());
+ sat_def.push_back(ez->NOT(satgen.importUndefSigSpec(bit).front()));
+ }
+
+ if (inv_mode && cone_size > 0) {
+ if (!ez->solve(sat_out, out_inverted, ez->expression(ezSAT::OpAnd, sat_def)))
+ log_error("Solving for initial model failed!\n");
+ for (size_t i = 0; i < sat_out.size(); i++)
+ if (out_inverted.at(i))
+ sat_out[i] = ez->NOT(sat_out[i]);
+ } else
+ out_inverted = std::vector<bool>(sat_out.size(), false);
+ }
+
+ void analyze_const(std::vector<std::vector<equiv_bit_t>> &results, int idx)
+ {
+ if (verbose_level == 1)
+ log(" Finding const value for %s.\n", log_signal(out_bits[idx]));
+
+ bool can_be_set = ez->solve(ez->AND(sat_out[idx], sat_def[idx]));
+ bool can_be_clr = ez->solve(ez->AND(ez->NOT(sat_out[idx]), sat_def[idx]));
+ log_assert(!can_be_set || !can_be_clr);
+
+ RTLIL::SigBit value(RTLIL::State::Sx);
+ if (can_be_set)
+ value = RTLIL::State::S1;
+ if (can_be_clr)
+ value = RTLIL::State::S0;
+ if (verbose_level == 1)
+ log(" Constant value for this signal: %s\n", log_signal(value));
+
+ int result_idx = -1;
+ for (size_t i = 0; i < results.size(); i++) {
+ if (results[i].front().bit == value) {
+ result_idx = i;
+ break;
+ }
+ }
+
+ if (result_idx == -1) {
+ result_idx = results.size();
+ results.push_back(std::vector<equiv_bit_t>());
+ equiv_bit_t bit;
+ bit.depth = 0;
+ bit.inverted = false;
+ bit.drv = NULL;
+ bit.bit = value;
+ results.back().push_back(bit);
+ }
+
+ equiv_bit_t bit;
+ bit.depth = 1;
+ bit.inverted = false;
+ bit.drv = drivers.count(out_bits[idx]) ? drivers.at(out_bits[idx]).first : NULL;
+ bit.bit = out_bits[idx];
+ results[result_idx].push_back(bit);
+ }
+
+ void analyze(std::vector<std::set<int>> &results, std::map<int, int> &results_map, std::vector<int> &bucket, std::string indent1, std::string indent2)
+ {
+ std::string indent = indent1 + indent2;
+ const char *indt = indent.c_str();
+
+ if (bucket.size() <= 1)
+ return;
+
+ if (verbose_level == 1)
+ log("%s Trying to shatter bucket with %d signals.\n", indt, int(bucket.size()));
+
+ if (verbose_level > 1) {
+ std::vector<RTLIL::SigBit> bucket_sigbits;
+ for (int idx : bucket)
+ bucket_sigbits.push_back(out_bits[idx]);
+ log("%s Trying to shatter bucket with %d signals: %s\n", indt, int(bucket.size()), log_signal(bucket_sigbits));
+ }
+
+ std::vector<int> sat_set_list, sat_clr_list;
+ for (int idx : bucket) {
+ sat_set_list.push_back(ez->AND(sat_out[idx], sat_def[idx]));
+ sat_clr_list.push_back(ez->AND(ez->NOT(sat_out[idx]), sat_def[idx]));
+ }
+
+ std::vector<int> modelVars = sat_out;
+ std::vector<bool> model;
+
+ modelVars.insert(modelVars.end(), sat_def.begin(), sat_def.end());
+ if (verbose_level >= 2)
+ modelVars.insert(modelVars.end(), sat_pi.begin(), sat_pi.end());
+
+ if (ez->solve(modelVars, model, ez->expression(ezSAT::OpOr, sat_set_list), ez->expression(ezSAT::OpOr, sat_clr_list)))
+ {
+ int iter_count = 1;
+
+ while (1)
+ {
+ sat_set_list.clear();
+ sat_clr_list.clear();
+
+ std::vector<int> sat_def_list;
+
+ for (int idx : bucket)
+ if (!model[sat_out.size() + idx]) {
+ sat_set_list.push_back(ez->AND(sat_out[idx], sat_def[idx]));
+ sat_clr_list.push_back(ez->AND(ez->NOT(sat_out[idx]), sat_def[idx]));
+ } else {
+ sat_def_list.push_back(sat_def[idx]);
+ }
+
+ if (!ez->solve(modelVars, model, ez->expression(ezSAT::OpOr, sat_set_list), ez->expression(ezSAT::OpOr, sat_clr_list), ez->expression(ezSAT::OpAnd, sat_def_list)))
+ break;
+ iter_count++;
+ }
+
+ if (verbose_level >= 1) {
+ int count_set = 0, count_clr = 0, count_undef = 0;
+ for (int idx : bucket)
+ if (!model[sat_out.size() + idx])
+ count_undef++;
+ else if (model[idx])
+ count_set++;
+ else
+ count_clr++;
+ log("%s After %d iterations: %d set vs. %d clr vs %d undef\n", indt, iter_count, count_set, count_clr, count_undef);
+ }
+
+ if (verbose_level >= 2) {
+ for (size_t i = 0; i < pi_bits.size(); i++)
+ log("%s -> PI %c == %s\n", indt, model[2*sat_out.size() + i] ? '1' : '0', log_signal(pi_bits[i]));
+ for (int idx : bucket)
+ log("%s -> OUT %c == %s%s\n", indt, model[sat_out.size() + idx] ? model[idx] ? '1' : '0' : 'x',
+ out_inverted.at(idx) ? "~" : "", log_signal(out_bits[idx]));
+ }
+
+ std::vector<int> buckets_a;
+ std::vector<int> buckets_b;
+
+ for (int idx : bucket) {
+ if (!model[sat_out.size() + idx] || model[idx])
+ buckets_a.push_back(idx);
+ if (!model[sat_out.size() + idx] || !model[idx])
+ buckets_b.push_back(idx);
+ }
+ analyze(results, results_map, buckets_a, indent1 + ".", indent2 + " ");
+ analyze(results, results_map, buckets_b, indent1 + "x", indent2 + " ");
+ }
+ else
+ {
+ std::vector<int> undef_slaves;
+
+ for (int idx : bucket) {
+ std::vector<int> sat_def_list;
+ for (int idx2 : bucket)
+ if (idx != idx2)
+ sat_def_list.push_back(sat_def[idx2]);
+ if (ez->solve(ez->NOT(sat_def[idx]), ez->expression(ezSAT::OpOr, sat_def_list)))
+ undef_slaves.push_back(idx);
+ }
+
+ if (undef_slaves.size() == bucket.size()) {
+ if (verbose_level >= 1)
+ log("%s Complex undef overlap. None of the signals covers the others.\n", indt);
+ // FIXME: We could try to further shatter a group with complex undef overlaps
+ return;
+ }
+
+ for (int idx : undef_slaves)
+ out_depth[idx] = std::numeric_limits<int>::max();
+
+ if (verbose_level >= 1) {
+ log("%s Found %d equivalent signals:", indt, int(bucket.size()));
+ for (int idx : bucket)
+ log("%s%s%s", idx == bucket.front() ? " " : ", ", out_inverted[idx] ? "~" : "", log_signal(out_bits[idx]));
+ log("\n");
+ }
+
+ int result_idx = -1;
+ for (int idx : bucket) {
+ if (results_map.count(idx) == 0)
+ continue;
+ if (result_idx == -1) {
+ result_idx = results_map.at(idx);
+ continue;
+ }
+ int result_idx2 = results_map.at(idx);
+ results[result_idx].insert(results[result_idx2].begin(), results[result_idx2].end());
+ for (int idx2 : results[result_idx2])
+ results_map[idx2] = result_idx;
+ results[result_idx2].clear();
+ }
+
+ if (result_idx == -1) {
+ result_idx = results.size();
+ results.push_back(std::set<int>());
+ }
+
+ results[result_idx].insert(bucket.begin(), bucket.end());
+ }
+ }
+
+ void analyze(std::vector<std::vector<equiv_bit_t>> &results, int perc)
+ {
+ std::vector<int> bucket;
+ for (size_t i = 0; i < sat_out.size(); i++)
+ bucket.push_back(i);
+
+ std::vector<std::set<int>> results_buf;
+ std::map<int, int> results_map;
+ analyze(results_buf, results_map, bucket, stringf("[%2d%%] %d ", perc, cone_size), "");
+
+ for (auto &r : results_buf)
+ {
+ if (r.size() <= 1)
+ continue;
+
+ if (verbose_level >= 1) {
+ std::vector<RTLIL::SigBit> r_sigbits;
+ for (int idx : r)
+ r_sigbits.push_back(out_bits[idx]);
+ log(" Found group of %d equivalent signals: %s\n", int(r.size()), log_signal(r_sigbits));
+ }
+
+ std::vector<int> undef_slaves;
+
+ for (int idx : r) {
+ std::vector<int> sat_def_list;
+ for (int idx2 : r)
+ if (idx != idx2)
+ sat_def_list.push_back(sat_def[idx2]);
+ if (ez->solve(ez->NOT(sat_def[idx]), ez->expression(ezSAT::OpOr, sat_def_list)))
+ undef_slaves.push_back(idx);
+ }
+
+ if (undef_slaves.size() == bucket.size()) {
+ if (verbose_level >= 1)
+ log(" Complex undef overlap. None of the signals covers the others.\n");
+ // FIXME: We could try to further shatter a group with complex undef overlaps
+ return;
+ }
+
+ for (int idx : undef_slaves)
+ out_depth[idx] = std::numeric_limits<int>::max();
+
+ std::vector<equiv_bit_t> result;
+
+ for (int idx : r) {
+ equiv_bit_t bit;
+ bit.depth = out_depth[idx];
+ bit.inverted = out_inverted[idx];
+ bit.drv = drivers.count(out_bits[idx]) ? drivers.at(out_bits[idx]).first : NULL;
+ bit.bit = out_bits[idx];
+ result.push_back(bit);
+ }
+
+ std::sort(result.begin(), result.end());
+
+ if (result.front().inverted)
+ for (auto &bit : result)
+ bit.inverted = !bit.inverted;
+
+ for (size_t i = 1; i < result.size(); i++) {
+ std::pair<RTLIL::SigBit, RTLIL::SigBit> p(result[0].bit, result[i].bit);
+ if (inv_pairs.count(p) != 0)
+ result.erase(result.begin() + i);
+ }
+
+ if (result.size() > 1)
+ results.push_back(result);
+ }
+ }
+};
+
+struct FreduceWorker
+{
+ RTLIL::Design *design;
+ RTLIL::Module *module;
+
+ SigMap sigmap;
+ drivers_t drivers;
+ std::set<std::pair<RTLIL::SigBit, RTLIL::SigBit>> inv_pairs;
+
+ FreduceWorker(RTLIL::Design *design, RTLIL::Module *module) : design(design), module(module), sigmap(module)
+ {
+ }
+
+ bool find_bit_in_cone(std::set<RTLIL::Cell*> &celldone, RTLIL::SigBit needle, RTLIL::SigBit haystack)
+ {
+ if (needle == haystack)
+ return true;
+ if (haystack.wire == NULL || needle.wire == NULL || drivers.count(haystack) == 0)
+ return false;
+
+ std::pair<RTLIL::Cell*, std::set<RTLIL::SigBit>> &drv = drivers.at(haystack);
+
+ if (celldone.count(drv.first))
+ return false;
+ celldone.insert(drv.first);
+
+ for (auto &bit : drv.second)
+ if (find_bit_in_cone(celldone, needle, bit))
+ return true;
+ return false;
+ }
+
+ bool find_bit_in_cone(RTLIL::SigBit needle, RTLIL::SigBit haystack)
+ {
+ std::set<RTLIL::Cell*> celldone;
+ return find_bit_in_cone(celldone, needle, haystack);
+ }
+
+ void dump()
+ {
+ std::string filename = stringf("%s_%s_%05d.il", dump_prefix.c_str(), RTLIL::id2cstr(module->name), reduce_counter);
+ log("%s Writing dump file `%s'.\n", reduce_counter ? " " : "", filename.c_str());
+ Pass::call(design, stringf("dump -outfile %s %s", filename.c_str(), design->selected_active_module.empty() ? module->name.c_str() : ""));
+ }
+
+ int run()
+ {
+ log("Running functional reduction on module %s:\n", RTLIL::id2cstr(module->name));
+
+ CellTypes ct;
+ ct.setup_internals();
+ ct.setup_stdcells();
+
+ int bits_full_total = 0;
+ std::vector<std::set<RTLIL::SigBit>> batches;
+ for (auto &it : module->wires_)
+ if (it.second->port_input) {
+ batches.push_back(sigmap(it.second).to_sigbit_set());
+ bits_full_total += it.second->width;
+ }
+ for (auto &it : module->cells_) {
+ if (ct.cell_known(it.second->type)) {
+ std::set<RTLIL::SigBit> inputs, outputs;
+ for (auto &port : it.second->connections()) {
+ std::vector<RTLIL::SigBit> bits = sigmap(port.second).to_sigbit_vector();
+ if (ct.cell_output(it.second->type, port.first))
+ outputs.insert(bits.begin(), bits.end());
+ else
+ inputs.insert(bits.begin(), bits.end());
+ }
+ std::pair<RTLIL::Cell*, std::set<RTLIL::SigBit>> drv(it.second, inputs);
+ for (auto &bit : outputs)
+ drivers[bit] = drv;
+ batches.push_back(outputs);
+ bits_full_total += outputs.size();
+ }
+ if (inv_mode && it.second->type == "$_NOT_")
+ inv_pairs.insert(std::pair<RTLIL::SigBit, RTLIL::SigBit>(sigmap(it.second->getPort("\\A")), sigmap(it.second->getPort("\\Y"))));
+ }
+
+ int bits_count = 0;
+ int bits_full_count = 0;
+ std::map<std::vector<RTLIL::SigBit>, std::vector<RTLIL::SigBit>> buckets;
+ for (auto &batch : batches)
+ {
+ for (auto &bit : batch)
+ if (bit.wire != NULL && design->selected(module, bit.wire))
+ goto found_selected_wire;
+ bits_full_count += batch.size();
+ continue;
+
+ found_selected_wire:
+ log(" Finding reduced input cone for signal batch %s%c\n",
+ log_signal(batch), verbose_level ? ':' : '.');
+
+ FindReducedInputs infinder(sigmap, drivers);
+ for (auto &bit : batch) {
+ std::vector<RTLIL::SigBit> inputs;
+ infinder.analyze(inputs, bit, 100 * bits_full_count / bits_full_total);
+ buckets[inputs].push_back(bit);
+ bits_full_count++;
+ bits_count++;
+ }
+ }
+ log(" Sorted %d signal bits into %d buckets.\n", bits_count, int(buckets.size()));
+
+ int bucket_count = 0;
+ std::vector<std::vector<equiv_bit_t>> equiv;
+ for (auto &bucket : buckets)
+ {
+ bucket_count++;
+
+ if (bucket.second.size() == 1)
+ continue;
+
+ if (bucket.first.size() == 0) {
+ log(" Finding const values for bucket %s%c\n", log_signal(bucket.second), verbose_level ? ':' : '.');
+ PerformReduction worker(sigmap, drivers, inv_pairs, bucket.second, bucket.first.size());
+ for (size_t idx = 0; idx < bucket.second.size(); idx++)
+ worker.analyze_const(equiv, idx);
+ } else {
+ log(" Trying to shatter bucket %s%c\n", log_signal(bucket.second), verbose_level ? ':' : '.');
+ PerformReduction worker(sigmap, drivers, inv_pairs, bucket.second, bucket.first.size());
+ worker.analyze(equiv, 100 * bucket_count / (buckets.size() + 1));
+ }
+ }
+
+ std::map<RTLIL::SigBit, int> bitusage;
+ module->rewrite_sigspecs(CountBitUsage(sigmap, bitusage));
+
+ if (!dump_prefix.empty())
+ dump();
+
+ log(" Rewiring %d equivalent groups:\n", int(equiv.size()));
+ int rewired_sigbits = 0;
+ for (auto &grp : equiv)
+ {
+ log(" [%05d] Using as master for group: %s\n", ++reduce_counter, log_signal(grp.front().bit));
+
+ RTLIL::SigSpec inv_sig;
+ for (size_t i = 1; i < grp.size(); i++)
+ {
+ if (!design->selected(module, grp[i].bit.wire)) {
+ log(" Skipping not-selected slave: %s\n", log_signal(grp[i].bit));
+ continue;
+ }
+
+ if (grp[i].bit.wire->port_id == 0 && bitusage[grp[i].bit] <= 1) {
+ log(" Skipping unused slave: %s\n", log_signal(grp[i].bit));
+ continue;
+ }
+
+ if (find_bit_in_cone(grp[i].bit, grp.front().bit)) {
+ log(" Skipping dependency of master: %s\n", log_signal(grp[i].bit));
+ continue;
+ }
+
+ log(" Connect slave%s: %s\n", grp[i].inverted ? " using inverter" : "", log_signal(grp[i].bit));
+
+ RTLIL::Cell *drv = drivers.at(grp[i].bit).first;
+ RTLIL::Wire *dummy_wire = module->addWire(NEW_ID);
+ for (auto &port : drv->connections_)
+ if (ct.cell_output(drv->type, port.first))
+ sigmap(port.second).replace(grp[i].bit, dummy_wire, &port.second);
+
+ if (grp[i].inverted)
+ {
+ if (inv_sig.size() == 0)
+ {
+ inv_sig = module->addWire(NEW_ID);
+
+ RTLIL::Cell *inv_cell = module->addCell(NEW_ID, "$_NOT_");
+ inv_cell->setPort("\\A", grp[0].bit);
+ inv_cell->setPort("\\Y", inv_sig);
+ }
+
+ module->connect(RTLIL::SigSig(grp[i].bit, inv_sig));
+ }
+ else
+ module->connect(RTLIL::SigSig(grp[i].bit, grp[0].bit));
+
+ rewired_sigbits++;
+ }
+
+ if (!dump_prefix.empty())
+ dump();
+
+ if (reduce_counter == reduce_stop_at) {
+ log(" Reached limit passed using -stop option. Skipping all further reductions.\n");
+ break;
+ }
+ }
+
+ log(" Rewired a total of %d signal bits in module %s.\n", rewired_sigbits, RTLIL::id2cstr(module->name));
+ return rewired_sigbits;
+ }
+};
+
+struct FreducePass : public Pass {
+ FreducePass() : Pass("freduce", "perform functional reduction") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" freduce [options] [selection]\n");
+ log("\n");
+ log("This pass performs functional reduction in the circuit. I.e. if two nodes are\n");
+ log("equivalent, they are merged to one node and one of the redundant drivers is\n");
+ log("disconnected. A subsequent call to 'clean' will remove the redundant drivers.\n");
+ log("\n");
+ log(" -v, -vv\n");
+ log(" enable verbose or very verbose output\n");
+ log("\n");
+ log(" -inv\n");
+ log(" enable explicit handling of inverted signals\n");
+ log("\n");
+ log(" -stop <n>\n");
+ log(" stop after <n> reduction operations. this is mostly used for\n");
+ log(" debugging the freduce command itself.\n");
+ log("\n");
+ log(" -dump <prefix>\n");
+ log(" dump the design to <prefix>_<module>_<num>.il after each reduction\n");
+ log(" operation. this is mostly used for debugging the freduce command.\n");
+ log("\n");
+ log("This pass is undef-aware, i.e. it considers don't-care values for detecting\n");
+ log("equivalent nodes.\n");
+ log("\n");
+ log("All selected wires are considered for rewiring. The selected cells cover the\n");
+ log("circuit that is analyzed.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ reduce_counter = 0;
+ reduce_stop_at = 0;
+ verbose_level = 0;
+ inv_mode = false;
+ dump_prefix = std::string();
+
+ log_header(design, "Executing FREDUCE pass (perform functional reduction).\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ if (args[argidx] == "-v") {
+ verbose_level = 1;
+ continue;
+ }
+ if (args[argidx] == "-vv") {
+ verbose_level = 2;
+ continue;
+ }
+ if (args[argidx] == "-inv") {
+ inv_mode = true;
+ continue;
+ }
+ if (args[argidx] == "-stop" && argidx+1 < args.size()) {
+ reduce_stop_at = atoi(args[++argidx].c_str());
+ continue;
+ }
+ if (args[argidx] == "-dump" && argidx+1 < args.size()) {
+ dump_prefix = args[++argidx];
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ int bitcount = 0;
+ for (auto &mod_it : design->modules_) {
+ RTLIL::Module *module = mod_it.second;
+ if (design->selected(module))
+ bitcount += FreduceWorker(design, module).run();
+ }
+
+ log("Rewired a total of %d signal bits.\n", bitcount);
+ }
+} FreducePass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/sat/miter.cc b/passes/sat/miter.cc
new file mode 100644
index 00000000..9e150b60
--- /dev/null
+++ b/passes/sat/miter.cc
@@ -0,0 +1,421 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "kernel/register.h"
+#include "kernel/rtlil.h"
+#include "kernel/log.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+void create_miter_equiv(struct Pass *that, std::vector<std::string> args, RTLIL::Design *design)
+{
+ bool flag_ignore_gold_x = false;
+ bool flag_make_outputs = false;
+ bool flag_make_outcmp = false;
+ bool flag_make_assert = false;
+ bool flag_flatten = false;
+
+ log_header(design, "Executing MITER pass (creating miter circuit).\n");
+
+ size_t argidx;
+ for (argidx = 2; argidx < args.size(); argidx++)
+ {
+ if (args[argidx] == "-ignore_gold_x") {
+ flag_ignore_gold_x = true;
+ continue;
+ }
+ if (args[argidx] == "-make_outputs") {
+ flag_make_outputs = true;
+ continue;
+ }
+ if (args[argidx] == "-make_outcmp") {
+ flag_make_outcmp = true;
+ continue;
+ }
+ if (args[argidx] == "-make_assert") {
+ flag_make_assert = true;
+ continue;
+ }
+ if (args[argidx] == "-flatten") {
+ flag_flatten = true;
+ continue;
+ }
+ break;
+ }
+ if (argidx+3 != args.size() || args[argidx].substr(0, 1) == "-")
+ that->cmd_error(args, argidx, "command argument error");
+
+ RTLIL::IdString gold_name = RTLIL::escape_id(args[argidx++]);
+ RTLIL::IdString gate_name = RTLIL::escape_id(args[argidx++]);
+ RTLIL::IdString miter_name = RTLIL::escape_id(args[argidx++]);
+
+ if (design->modules_.count(gold_name) == 0)
+ log_cmd_error("Can't find gold module %s!\n", gold_name.c_str());
+ if (design->modules_.count(gate_name) == 0)
+ log_cmd_error("Can't find gate module %s!\n", gate_name.c_str());
+ if (design->modules_.count(miter_name) != 0)
+ log_cmd_error("There is already a module %s!\n", miter_name.c_str());
+
+ RTLIL::Module *gold_module = design->modules_.at(gold_name);
+ RTLIL::Module *gate_module = design->modules_.at(gate_name);
+
+ for (auto &it : gold_module->wires_) {
+ RTLIL::Wire *w1 = it.second, *w2;
+ if (w1->port_id == 0)
+ continue;
+ if (gate_module->wires_.count(it.second->name) == 0)
+ goto match_gold_port_error;
+ w2 = gate_module->wires_.at(it.second->name);
+ if (w1->port_input != w2->port_input)
+ goto match_gold_port_error;
+ if (w1->port_output != w2->port_output)
+ goto match_gold_port_error;
+ if (w1->width != w2->width)
+ goto match_gold_port_error;
+ continue;
+ match_gold_port_error:
+ log_cmd_error("No matching port in gate module was found for %s!\n", it.second->name.c_str());
+ }
+
+ for (auto &it : gate_module->wires_) {
+ RTLIL::Wire *w1 = it.second, *w2;
+ if (w1->port_id == 0)
+ continue;
+ if (gold_module->wires_.count(it.second->name) == 0)
+ goto match_gate_port_error;
+ w2 = gold_module->wires_.at(it.second->name);
+ if (w1->port_input != w2->port_input)
+ goto match_gate_port_error;
+ if (w1->port_output != w2->port_output)
+ goto match_gate_port_error;
+ if (w1->width != w2->width)
+ goto match_gate_port_error;
+ continue;
+ match_gate_port_error:
+ log_cmd_error("No matching port in gold module was found for %s!\n", it.second->name.c_str());
+ }
+
+ log("Creating miter cell \"%s\" with gold cell \"%s\" and gate cell \"%s\".\n", RTLIL::id2cstr(miter_name), RTLIL::id2cstr(gold_name), RTLIL::id2cstr(gate_name));
+
+ RTLIL::Module *miter_module = new RTLIL::Module;
+ miter_module->name = miter_name;
+ design->add(miter_module);
+
+ RTLIL::Cell *gold_cell = miter_module->addCell("\\gold", gold_name);
+ RTLIL::Cell *gate_cell = miter_module->addCell("\\gate", gate_name);
+
+ RTLIL::SigSpec all_conditions;
+
+ for (auto &it : gold_module->wires_)
+ {
+ RTLIL::Wire *w1 = it.second;
+
+ if (w1->port_input)
+ {
+ RTLIL::Wire *w2 = miter_module->addWire("\\in_" + RTLIL::unescape_id(w1->name), w1->width);
+ w2->port_input = true;
+
+ gold_cell->setPort(w1->name, w2);
+ gate_cell->setPort(w1->name, w2);
+ }
+
+ if (w1->port_output)
+ {
+ RTLIL::Wire *w2_gold = miter_module->addWire("\\gold_" + RTLIL::unescape_id(w1->name), w1->width);
+ w2_gold->port_output = flag_make_outputs;
+
+ RTLIL::Wire *w2_gate = miter_module->addWire("\\gate_" + RTLIL::unescape_id(w1->name), w1->width);
+ w2_gate->port_output = flag_make_outputs;
+
+ gold_cell->setPort(w1->name, w2_gold);
+ gate_cell->setPort(w1->name, w2_gate);
+
+ RTLIL::SigSpec this_condition;
+
+ if (flag_ignore_gold_x)
+ {
+ RTLIL::SigSpec gold_x = miter_module->addWire(NEW_ID, w2_gold->width);
+ for (int i = 0; i < w2_gold->width; i++) {
+ RTLIL::Cell *eqx_cell = miter_module->addCell(NEW_ID, "$eqx");
+ eqx_cell->parameters["\\A_WIDTH"] = 1;
+ eqx_cell->parameters["\\B_WIDTH"] = 1;
+ eqx_cell->parameters["\\Y_WIDTH"] = 1;
+ eqx_cell->parameters["\\A_SIGNED"] = 0;
+ eqx_cell->parameters["\\B_SIGNED"] = 0;
+ eqx_cell->setPort("\\A", RTLIL::SigSpec(w2_gold, i));
+ eqx_cell->setPort("\\B", RTLIL::State::Sx);
+ eqx_cell->setPort("\\Y", gold_x.extract(i, 1));
+ }
+
+ RTLIL::SigSpec gold_masked = miter_module->addWire(NEW_ID, w2_gold->width);
+ RTLIL::SigSpec gate_masked = miter_module->addWire(NEW_ID, w2_gate->width);
+
+ RTLIL::Cell *or_gold_cell = miter_module->addCell(NEW_ID, "$or");
+ or_gold_cell->parameters["\\A_WIDTH"] = w2_gold->width;
+ or_gold_cell->parameters["\\B_WIDTH"] = w2_gold->width;
+ or_gold_cell->parameters["\\Y_WIDTH"] = w2_gold->width;
+ or_gold_cell->parameters["\\A_SIGNED"] = 0;
+ or_gold_cell->parameters["\\B_SIGNED"] = 0;
+ or_gold_cell->setPort("\\A", w2_gold);
+ or_gold_cell->setPort("\\B", gold_x);
+ or_gold_cell->setPort("\\Y", gold_masked);
+
+ RTLIL::Cell *or_gate_cell = miter_module->addCell(NEW_ID, "$or");
+ or_gate_cell->parameters["\\A_WIDTH"] = w2_gate->width;
+ or_gate_cell->parameters["\\B_WIDTH"] = w2_gate->width;
+ or_gate_cell->parameters["\\Y_WIDTH"] = w2_gate->width;
+ or_gate_cell->parameters["\\A_SIGNED"] = 0;
+ or_gate_cell->parameters["\\B_SIGNED"] = 0;
+ or_gate_cell->setPort("\\A", w2_gate);
+ or_gate_cell->setPort("\\B", gold_x);
+ or_gate_cell->setPort("\\Y", gate_masked);
+
+ RTLIL::Cell *eq_cell = miter_module->addCell(NEW_ID, "$eqx");
+ eq_cell->parameters["\\A_WIDTH"] = w2_gold->width;
+ eq_cell->parameters["\\B_WIDTH"] = w2_gate->width;
+ eq_cell->parameters["\\Y_WIDTH"] = 1;
+ eq_cell->parameters["\\A_SIGNED"] = 0;
+ eq_cell->parameters["\\B_SIGNED"] = 0;
+ eq_cell->setPort("\\A", gold_masked);
+ eq_cell->setPort("\\B", gate_masked);
+ eq_cell->setPort("\\Y", miter_module->addWire(NEW_ID));
+ this_condition = eq_cell->getPort("\\Y");
+ }
+ else
+ {
+ RTLIL::Cell *eq_cell = miter_module->addCell(NEW_ID, "$eqx");
+ eq_cell->parameters["\\A_WIDTH"] = w2_gold->width;
+ eq_cell->parameters["\\B_WIDTH"] = w2_gate->width;
+ eq_cell->parameters["\\Y_WIDTH"] = 1;
+ eq_cell->parameters["\\A_SIGNED"] = 0;
+ eq_cell->parameters["\\B_SIGNED"] = 0;
+ eq_cell->setPort("\\A", w2_gold);
+ eq_cell->setPort("\\B", w2_gate);
+ eq_cell->setPort("\\Y", miter_module->addWire(NEW_ID));
+ this_condition = eq_cell->getPort("\\Y");
+ }
+
+ if (flag_make_outcmp)
+ {
+ RTLIL::Wire *w_cmp = miter_module->addWire("\\cmp_" + RTLIL::unescape_id(w1->name));
+ w_cmp->port_output = true;
+ miter_module->connect(RTLIL::SigSig(w_cmp, this_condition));
+ }
+
+ all_conditions.append(this_condition);
+ }
+ }
+
+ if (all_conditions.size() != 1) {
+ RTLIL::Cell *reduce_cell = miter_module->addCell(NEW_ID, "$reduce_and");
+ reduce_cell->parameters["\\A_WIDTH"] = all_conditions.size();
+ reduce_cell->parameters["\\Y_WIDTH"] = 1;
+ reduce_cell->parameters["\\A_SIGNED"] = 0;
+ reduce_cell->setPort("\\A", all_conditions);
+ reduce_cell->setPort("\\Y", miter_module->addWire(NEW_ID));
+ all_conditions = reduce_cell->getPort("\\Y");
+ }
+
+ if (flag_make_assert) {
+ RTLIL::Cell *assert_cell = miter_module->addCell(NEW_ID, "$assert");
+ assert_cell->setPort("\\A", all_conditions);
+ assert_cell->setPort("\\EN", RTLIL::SigSpec(1, 1));
+ }
+
+ RTLIL::Wire *w_trigger = miter_module->addWire("\\trigger");
+ w_trigger->port_output = true;
+
+ RTLIL::Cell *not_cell = miter_module->addCell(NEW_ID, "$not");
+ not_cell->parameters["\\A_WIDTH"] = all_conditions.size();
+ not_cell->parameters["\\A_WIDTH"] = all_conditions.size();
+ not_cell->parameters["\\Y_WIDTH"] = w_trigger->width;
+ not_cell->parameters["\\A_SIGNED"] = 0;
+ not_cell->setPort("\\A", all_conditions);
+ not_cell->setPort("\\Y", w_trigger);
+
+ miter_module->fixup_ports();
+
+ if (flag_flatten) {
+ log_push();
+ Pass::call_on_module(design, miter_module, "flatten; opt_expr -keepdc -undriven;;");
+ log_pop();
+ }
+}
+
+void create_miter_assert(struct Pass *that, std::vector<std::string> args, RTLIL::Design *design)
+{
+ bool flag_make_outputs = false;
+ bool flag_flatten = false;
+
+ log_header(design, "Executing MITER pass (creating miter circuit).\n");
+
+ size_t argidx;
+ for (argidx = 2; argidx < args.size(); argidx++)
+ {
+ if (args[argidx] == "-make_outputs") {
+ flag_make_outputs = true;
+ continue;
+ }
+ if (args[argidx] == "-flatten") {
+ flag_flatten = true;
+ continue;
+ }
+ break;
+ }
+ if ((argidx+1 != args.size() && argidx+2 != args.size()) || args[argidx].substr(0, 1) == "-")
+ that->cmd_error(args, argidx, "command argument error");
+
+ IdString module_name = RTLIL::escape_id(args[argidx++]);
+ IdString miter_name = argidx < args.size() ? RTLIL::escape_id(args[argidx++]) : "";
+
+ if (design->modules_.count(module_name) == 0)
+ log_cmd_error("Can't find module %s!\n", module_name.c_str());
+ if (!miter_name.empty() && design->modules_.count(miter_name) != 0)
+ log_cmd_error("There is already a module %s!\n", miter_name.c_str());
+
+ Module *module = design->module(module_name);
+
+ if (!miter_name.empty()) {
+ module = module->clone();
+ module->name = miter_name;
+ design->add(module);
+ }
+
+ if (!flag_make_outputs)
+ for (auto wire : module->wires())
+ wire->port_output = false;
+
+ Wire *trigger = module->addWire("\\trigger");
+ trigger->port_output = true;
+ module->fixup_ports();
+
+ if (flag_flatten) {
+ log_push();
+ Pass::call_on_module(design, module, "flatten;;");
+ log_pop();
+ }
+
+ SigSpec assert_signals, assume_signals;
+ vector<Cell*> cell_list = module->cells();
+ for (auto cell : cell_list)
+ {
+ if (!cell->type.in("$assert", "$assume"))
+ continue;
+
+ SigBit is_active = module->Nex(NEW_ID, cell->getPort("\\A"), State::S1);
+ SigBit is_enabled = module->Eqx(NEW_ID, cell->getPort("\\EN"), State::S1);
+
+ if (cell->type == "$assert") {
+ assert_signals.append(module->And(NEW_ID, is_active, is_enabled));
+ } else {
+ assume_signals.append(module->And(NEW_ID, is_active, is_enabled));
+ }
+
+ module->remove(cell);
+ }
+
+ if (assume_signals.empty())
+ {
+ module->addReduceOr(NEW_ID, assert_signals, trigger);
+ }
+ else
+ {
+ Wire *assume_q = module->addWire(NEW_ID);
+ assume_q->attributes["\\init"] = State::S0;
+ assume_signals.append(assume_q);
+
+ SigSpec assume_nok = module->ReduceOr(NEW_ID, assume_signals);
+ SigSpec assume_ok = module->Not(NEW_ID, assume_nok);
+ module->addFf(NEW_ID, assume_nok, assume_q);
+
+ SigSpec assert_fail = module->ReduceOr(NEW_ID, assert_signals);
+ module->addAnd(NEW_ID, assert_fail, assume_ok, trigger);
+ }
+
+ if (flag_flatten) {
+ log_push();
+ Pass::call_on_module(design, module, "opt_expr -keepdc -undriven;;");
+ log_pop();
+ }
+}
+
+struct MiterPass : public Pass {
+ MiterPass() : Pass("miter", "automatically create a miter circuit") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" miter -equiv [options] gold_name gate_name miter_name\n");
+ log("\n");
+ log("Creates a miter circuit for equivalence checking. The gold- and gate- modules\n");
+ log("must have the same interfaces. The miter circuit will have all inputs of the\n");
+ log("two source modules, prefixed with 'in_'. The miter circuit has a 'trigger'\n");
+ log("output that goes high if an output mismatch between the two source modules is\n");
+ log("detected.\n");
+ log("\n");
+ log(" -ignore_gold_x\n");
+ log(" a undef (x) bit in the gold module output will match any value in\n");
+ log(" the gate module output.\n");
+ log("\n");
+ log(" -make_outputs\n");
+ log(" also route the gold- and gate-outputs to 'gold_*' and 'gate_*' outputs\n");
+ log(" on the miter circuit.\n");
+ log("\n");
+ log(" -make_outcmp\n");
+ log(" also create a cmp_* output for each gold/gate output pair.\n");
+ log("\n");
+ log(" -make_assert\n");
+ log(" also create an 'assert' cell that checks if trigger is always low.\n");
+ log("\n");
+ log(" -flatten\n");
+ log(" call 'flatten; opt_expr -keepdc -undriven;;' on the miter circuit.\n");
+ log("\n");
+ log("\n");
+ log(" miter -assert [options] module [miter_name]\n");
+ log("\n");
+ log("Creates a miter circuit for property checking. All input ports are kept,\n");
+ log("output ports are discarded. An additional output 'trigger' is created that\n");
+ log("goes high when an assert is violated. Without a miter_name, the existing\n");
+ log("module is modified.\n");
+ log("\n");
+ log(" -make_outputs\n");
+ log(" keep module output ports.\n");
+ log("\n");
+ log(" -flatten\n");
+ log(" call 'flatten; opt_expr -keepdc -undriven;;' on the miter circuit.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ if (args.size() > 1 && args[1] == "-equiv") {
+ create_miter_equiv(this, args, design);
+ return;
+ }
+
+ if (args.size() > 1 && args[1] == "-assert") {
+ create_miter_assert(this, args, design);
+ return;
+ }
+
+ log_cmd_error("Missing mode parameter!\n");
+ }
+} MiterPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/sat/sat.cc b/passes/sat/sat.cc
new file mode 100644
index 00000000..a6ac7afd
--- /dev/null
+++ b/passes/sat/sat.cc
@@ -0,0 +1,1713 @@
+/*
+ * 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.
+ *
+ */
+
+// [[CITE]] Temporal Induction by Incremental SAT Solving
+// Niklas Een and Niklas Sörensson (2003)
+// http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.4.8161
+
+#include "kernel/register.h"
+#include "kernel/celltypes.h"
+#include "kernel/consteval.h"
+#include "kernel/sigtools.h"
+#include "kernel/log.h"
+#include "kernel/satgen.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <algorithm>
+#include <errno.h>
+#include <string.h>
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct SatHelper
+{
+ RTLIL::Design *design;
+ RTLIL::Module *module;
+
+ SigMap sigmap;
+ CellTypes ct;
+
+ ezSatPtr ez;
+ SatGen satgen;
+
+ // additional constraints
+ std::vector<std::pair<std::string, std::string>> sets, prove, prove_x, sets_init;
+ std::map<int, std::vector<std::pair<std::string, std::string>>> sets_at;
+ std::map<int, std::vector<std::string>> unsets_at;
+ bool prove_asserts, set_assumes;
+
+ // undef constraints
+ bool enable_undef, set_init_def, set_init_undef, set_init_zero, ignore_unknown_cells;
+ std::vector<std::string> sets_def, sets_any_undef, sets_all_undef;
+ std::map<int, std::vector<std::string>> sets_def_at, sets_any_undef_at, sets_all_undef_at;
+
+ // model variables
+ std::vector<std::string> shows;
+ SigPool show_signal_pool;
+ SigSet<RTLIL::Cell*> show_drivers;
+ int max_timestep, timeout;
+ bool gotTimeout;
+
+ SatHelper(RTLIL::Design *design, RTLIL::Module *module, bool enable_undef) :
+ design(design), module(module), sigmap(module), ct(design), satgen(ez.get(), &sigmap)
+ {
+ this->enable_undef = enable_undef;
+ satgen.model_undef = enable_undef;
+ set_init_def = false;
+ set_init_undef = false;
+ set_init_zero = false;
+ ignore_unknown_cells = false;
+ max_timestep = -1;
+ timeout = 0;
+ gotTimeout = false;
+ }
+
+ void check_undef_enabled(const RTLIL::SigSpec &sig)
+ {
+ if (enable_undef)
+ return;
+
+ std::vector<RTLIL::SigBit> sigbits = sig.to_sigbit_vector();
+ for (size_t i = 0; i < sigbits.size(); i++)
+ if (sigbits[i].wire == NULL && sigbits[i].data == RTLIL::State::Sx)
+ log_cmd_error("Bit %d of %s is undef but option -enable_undef is missing!\n", int(i), log_signal(sig));
+ }
+
+ void setup(int timestep = -1, bool initstate = false)
+ {
+ if (timestep > 0)
+ log ("\nSetting up time step %d:\n", timestep);
+ else
+ log ("\nSetting up SAT problem:\n");
+
+ if (initstate)
+ satgen.setInitState(timestep);
+
+ if (timestep > max_timestep)
+ max_timestep = timestep;
+
+ RTLIL::SigSpec big_lhs, big_rhs;
+
+ for (auto &s : sets)
+ {
+ RTLIL::SigSpec lhs, rhs;
+
+ if (!RTLIL::SigSpec::parse_sel(lhs, design, module, s.first))
+ log_cmd_error("Failed to parse lhs set expression `%s'.\n", s.first.c_str());
+ if (!RTLIL::SigSpec::parse_rhs(lhs, rhs, module, s.second))
+ log_cmd_error("Failed to parse rhs set expression `%s'.\n", s.second.c_str());
+ show_signal_pool.add(sigmap(lhs));
+ show_signal_pool.add(sigmap(rhs));
+
+ if (lhs.size() != rhs.size())
+ log_cmd_error("Set expression with different lhs and rhs sizes: %s (%s, %d bits) vs. %s (%s, %d bits)\n",
+ s.first.c_str(), log_signal(lhs), lhs.size(), s.second.c_str(), log_signal(rhs), rhs.size());
+
+ log("Import set-constraint: %s = %s\n", log_signal(lhs), log_signal(rhs));
+ big_lhs.remove2(lhs, &big_rhs);
+ big_lhs.append(lhs);
+ big_rhs.append(rhs);
+ }
+
+ for (auto &s : sets_at[timestep])
+ {
+ RTLIL::SigSpec lhs, rhs;
+
+ if (!RTLIL::SigSpec::parse_sel(lhs, design, module, s.first))
+ log_cmd_error("Failed to parse lhs set expression `%s'.\n", s.first.c_str());
+ if (!RTLIL::SigSpec::parse_rhs(lhs, rhs, module, s.second))
+ log_cmd_error("Failed to parse rhs set expression `%s'.\n", s.second.c_str());
+ show_signal_pool.add(sigmap(lhs));
+ show_signal_pool.add(sigmap(rhs));
+
+ if (lhs.size() != rhs.size())
+ log_cmd_error("Set expression with different lhs and rhs sizes: %s (%s, %d bits) vs. %s (%s, %d bits)\n",
+ s.first.c_str(), log_signal(lhs), lhs.size(), s.second.c_str(), log_signal(rhs), rhs.size());
+
+ log("Import set-constraint for this timestep: %s = %s\n", log_signal(lhs), log_signal(rhs));
+ big_lhs.remove2(lhs, &big_rhs);
+ big_lhs.append(lhs);
+ big_rhs.append(rhs);
+ }
+
+ for (auto &s : unsets_at[timestep])
+ {
+ RTLIL::SigSpec lhs;
+
+ if (!RTLIL::SigSpec::parse_sel(lhs, design, module, s))
+ log_cmd_error("Failed to parse lhs set expression `%s'.\n", s.c_str());
+ show_signal_pool.add(sigmap(lhs));
+
+ log("Import unset-constraint for this timestep: %s\n", log_signal(lhs));
+ big_lhs.remove2(lhs, &big_rhs);
+ }
+
+ log("Final constraint equation: %s = %s\n", log_signal(big_lhs), log_signal(big_rhs));
+ check_undef_enabled(big_lhs), check_undef_enabled(big_rhs);
+ ez->assume(satgen.signals_eq(big_lhs, big_rhs, timestep));
+
+ // 0 = sets_def
+ // 1 = sets_any_undef
+ // 2 = sets_all_undef
+ std::set<RTLIL::SigSpec> sets_def_undef[3];
+
+ for (auto &s : sets_def) {
+ RTLIL::SigSpec sig;
+ if (!RTLIL::SigSpec::parse_sel(sig, design, module, s))
+ log_cmd_error("Failed to parse set-def expression `%s'.\n", s.c_str());
+ sets_def_undef[0].insert(sig);
+ }
+
+ for (auto &s : sets_any_undef) {
+ RTLIL::SigSpec sig;
+ if (!RTLIL::SigSpec::parse_sel(sig, design, module, s))
+ log_cmd_error("Failed to parse set-def expression `%s'.\n", s.c_str());
+ sets_def_undef[1].insert(sig);
+ }
+
+ for (auto &s : sets_all_undef) {
+ RTLIL::SigSpec sig;
+ if (!RTLIL::SigSpec::parse_sel(sig, design, module, s))
+ log_cmd_error("Failed to parse set-def expression `%s'.\n", s.c_str());
+ sets_def_undef[2].insert(sig);
+ }
+
+ for (auto &s : sets_def_at[timestep]) {
+ RTLIL::SigSpec sig;
+ if (!RTLIL::SigSpec::parse_sel(sig, design, module, s))
+ log_cmd_error("Failed to parse set-def expression `%s'.\n", s.c_str());
+ sets_def_undef[0].insert(sig);
+ sets_def_undef[1].erase(sig);
+ sets_def_undef[2].erase(sig);
+ }
+
+ for (auto &s : sets_any_undef_at[timestep]) {
+ RTLIL::SigSpec sig;
+ if (!RTLIL::SigSpec::parse_sel(sig, design, module, s))
+ log_cmd_error("Failed to parse set-def expression `%s'.\n", s.c_str());
+ sets_def_undef[0].erase(sig);
+ sets_def_undef[1].insert(sig);
+ sets_def_undef[2].erase(sig);
+ }
+
+ for (auto &s : sets_all_undef_at[timestep]) {
+ RTLIL::SigSpec sig;
+ if (!RTLIL::SigSpec::parse_sel(sig, design, module, s))
+ log_cmd_error("Failed to parse set-def expression `%s'.\n", s.c_str());
+ sets_def_undef[0].erase(sig);
+ sets_def_undef[1].erase(sig);
+ sets_def_undef[2].insert(sig);
+ }
+
+ for (int t = 0; t < 3; t++)
+ for (auto &sig : sets_def_undef[t]) {
+ log("Import %s constraint for this timestep: %s\n", t == 0 ? "def" : t == 1 ? "any_undef" : "all_undef", log_signal(sig));
+ std::vector<int> undef_sig = satgen.importUndefSigSpec(sig, timestep);
+ if (t == 0)
+ ez->assume(ez->NOT(ez->expression(ezSAT::OpOr, undef_sig)));
+ if (t == 1)
+ ez->assume(ez->expression(ezSAT::OpOr, undef_sig));
+ if (t == 2)
+ ez->assume(ez->expression(ezSAT::OpAnd, undef_sig));
+ }
+
+ int import_cell_counter = 0;
+ for (auto cell : module->cells())
+ if (design->selected(module, cell)) {
+ // log("Import cell: %s\n", RTLIL::id2cstr(cell->name));
+ if (satgen.importCell(cell, timestep)) {
+ for (auto &p : cell->connections())
+ if (ct.cell_output(cell->type, p.first))
+ show_drivers.insert(sigmap(p.second), cell);
+ import_cell_counter++;
+ } else if (ignore_unknown_cells)
+ log_warning("Failed to import cell %s (type %s) to SAT database.\n", RTLIL::id2cstr(cell->name), RTLIL::id2cstr(cell->type));
+ else
+ log_error("Failed to import cell %s (type %s) to SAT database.\n", RTLIL::id2cstr(cell->name), RTLIL::id2cstr(cell->type));
+ }
+ log("Imported %d cells to SAT database.\n", import_cell_counter);
+
+ if (set_assumes) {
+ RTLIL::SigSpec assumes_a, assumes_en;
+ satgen.getAssumes(assumes_a, assumes_en, timestep);
+ for (int i = 0; i < GetSize(assumes_a); i++)
+ log("Import constraint from assume cell: %s when %s.\n", log_signal(assumes_a[i]), log_signal(assumes_en[i]));
+ ez->assume(satgen.importAssumes(timestep));
+ }
+
+ if (initstate)
+ {
+ RTLIL::SigSpec big_lhs, big_rhs;
+
+ for (auto &it : module->wires_)
+ {
+ if (it.second->attributes.count("\\init") == 0)
+ continue;
+
+ RTLIL::SigSpec lhs = sigmap(it.second);
+ RTLIL::SigSpec rhs = it.second->attributes.at("\\init");
+ log_assert(lhs.size() == rhs.size());
+
+ RTLIL::SigSpec removed_bits;
+ for (int i = 0; i < lhs.size(); i++) {
+ RTLIL::SigSpec bit = lhs.extract(i, 1);
+ if (!satgen.initial_state.check_all(bit)) {
+ removed_bits.append(bit);
+ lhs.remove(i, 1);
+ rhs.remove(i, 1);
+ i--;
+ }
+ }
+
+ if (removed_bits.size())
+ log_warning("ignoring initial value on non-register: %s\n", log_signal(removed_bits));
+
+ if (lhs.size()) {
+ log("Import set-constraint from init attribute: %s = %s\n", log_signal(lhs), log_signal(rhs));
+ big_lhs.remove2(lhs, &big_rhs);
+ big_lhs.append(lhs);
+ big_rhs.append(rhs);
+ }
+ }
+
+ for (auto &s : sets_init)
+ {
+ RTLIL::SigSpec lhs, rhs;
+
+ if (!RTLIL::SigSpec::parse_sel(lhs, design, module, s.first))
+ log_cmd_error("Failed to parse lhs set expression `%s'.\n", s.first.c_str());
+ if (!RTLIL::SigSpec::parse_rhs(lhs, rhs, module, s.second))
+ log_cmd_error("Failed to parse rhs set expression `%s'.\n", s.second.c_str());
+ show_signal_pool.add(sigmap(lhs));
+ show_signal_pool.add(sigmap(rhs));
+
+ if (lhs.size() != rhs.size())
+ log_cmd_error("Set expression with different lhs and rhs sizes: %s (%s, %d bits) vs. %s (%s, %d bits)\n",
+ s.first.c_str(), log_signal(lhs), lhs.size(), s.second.c_str(), log_signal(rhs), rhs.size());
+
+ log("Import init set-constraint: %s = %s\n", log_signal(lhs), log_signal(rhs));
+ big_lhs.remove2(lhs, &big_rhs);
+ big_lhs.append(lhs);
+ big_rhs.append(rhs);
+ }
+
+ if (!satgen.initial_state.check_all(big_lhs)) {
+ RTLIL::SigSpec rem = satgen.initial_state.remove(big_lhs);
+ log_cmd_error("Found -set-init bits that are not part of the initial_state: %s\n", log_signal(rem));
+ }
+
+ if (set_init_def) {
+ RTLIL::SigSpec rem = satgen.initial_state.export_all();
+ std::vector<int> undef_rem = satgen.importUndefSigSpec(rem, 1);
+ ez->assume(ez->NOT(ez->expression(ezSAT::OpOr, undef_rem)));
+ }
+
+ if (set_init_undef) {
+ RTLIL::SigSpec rem = satgen.initial_state.export_all();
+ rem.remove(big_lhs);
+ big_lhs.append(rem);
+ big_rhs.append(RTLIL::SigSpec(RTLIL::State::Sx, rem.size()));
+ }
+
+ if (set_init_zero) {
+ RTLIL::SigSpec rem = satgen.initial_state.export_all();
+ rem.remove(big_lhs);
+ big_lhs.append(rem);
+ big_rhs.append(RTLIL::SigSpec(RTLIL::State::S0, rem.size()));
+ }
+
+ if (big_lhs.size() == 0) {
+ log("No constraints for initial state found.\n\n");
+ return;
+ }
+
+ log("Final init constraint equation: %s = %s\n", log_signal(big_lhs), log_signal(big_rhs));
+ check_undef_enabled(big_lhs), check_undef_enabled(big_rhs);
+ ez->assume(satgen.signals_eq(big_lhs, big_rhs, timestep));
+ }
+ }
+
+ int setup_proof(int timestep = -1)
+ {
+ log_assert(prove.size() || prove_x.size() || prove_asserts);
+
+ RTLIL::SigSpec big_lhs, big_rhs;
+ std::vector<int> prove_bits;
+
+ if (prove.size() > 0)
+ {
+ for (auto &s : prove)
+ {
+ RTLIL::SigSpec lhs, rhs;
+
+ if (!RTLIL::SigSpec::parse_sel(lhs, design, module, s.first))
+ log_cmd_error("Failed to parse lhs proof expression `%s'.\n", s.first.c_str());
+ if (!RTLIL::SigSpec::parse_rhs(lhs, rhs, module, s.second))
+ log_cmd_error("Failed to parse rhs proof expression `%s'.\n", s.second.c_str());
+ show_signal_pool.add(sigmap(lhs));
+ show_signal_pool.add(sigmap(rhs));
+
+ if (lhs.size() != rhs.size())
+ log_cmd_error("Proof expression with different lhs and rhs sizes: %s (%s, %d bits) vs. %s (%s, %d bits)\n",
+ s.first.c_str(), log_signal(lhs), lhs.size(), s.second.c_str(), log_signal(rhs), rhs.size());
+
+ log("Import proof-constraint: %s = %s\n", log_signal(lhs), log_signal(rhs));
+ big_lhs.remove2(lhs, &big_rhs);
+ big_lhs.append(lhs);
+ big_rhs.append(rhs);
+ }
+
+ log("Final proof equation: %s = %s\n", log_signal(big_lhs), log_signal(big_rhs));
+ check_undef_enabled(big_lhs), check_undef_enabled(big_rhs);
+ prove_bits.push_back(satgen.signals_eq(big_lhs, big_rhs, timestep));
+ }
+
+ if (prove_x.size() > 0)
+ {
+ for (auto &s : prove_x)
+ {
+ RTLIL::SigSpec lhs, rhs;
+
+ if (!RTLIL::SigSpec::parse_sel(lhs, design, module, s.first))
+ log_cmd_error("Failed to parse lhs proof-x expression `%s'.\n", s.first.c_str());
+ if (!RTLIL::SigSpec::parse_rhs(lhs, rhs, module, s.second))
+ log_cmd_error("Failed to parse rhs proof-x expression `%s'.\n", s.second.c_str());
+ show_signal_pool.add(sigmap(lhs));
+ show_signal_pool.add(sigmap(rhs));
+
+ if (lhs.size() != rhs.size())
+ log_cmd_error("Proof-x expression with different lhs and rhs sizes: %s (%s, %d bits) vs. %s (%s, %d bits)\n",
+ s.first.c_str(), log_signal(lhs), lhs.size(), s.second.c_str(), log_signal(rhs), rhs.size());
+
+ log("Import proof-x-constraint: %s = %s\n", log_signal(lhs), log_signal(rhs));
+ big_lhs.remove2(lhs, &big_rhs);
+ big_lhs.append(lhs);
+ big_rhs.append(rhs);
+ }
+
+ log("Final proof-x equation: %s = %s\n", log_signal(big_lhs), log_signal(big_rhs));
+
+ std::vector<int> value_lhs = satgen.importDefSigSpec(big_lhs, timestep);
+ std::vector<int> value_rhs = satgen.importDefSigSpec(big_rhs, timestep);
+
+ std::vector<int> undef_lhs = satgen.importUndefSigSpec(big_lhs, timestep);
+ std::vector<int> undef_rhs = satgen.importUndefSigSpec(big_rhs, timestep);
+
+ for (size_t i = 0; i < value_lhs.size(); i++)
+ prove_bits.push_back(ez->OR(undef_lhs.at(i), ez->AND(ez->NOT(undef_rhs.at(i)), ez->NOT(ez->XOR(value_lhs.at(i), value_rhs.at(i))))));
+ }
+
+ if (prove_asserts) {
+ RTLIL::SigSpec asserts_a, asserts_en;
+ satgen.getAsserts(asserts_a, asserts_en, timestep);
+ for (int i = 0; i < GetSize(asserts_a); i++)
+ log("Import proof for assert: %s when %s.\n", log_signal(asserts_a[i]), log_signal(asserts_en[i]));
+ prove_bits.push_back(satgen.importAsserts(timestep));
+ }
+
+ return ez->expression(ezSAT::OpAnd, prove_bits);
+ }
+
+ void force_unique_state(int timestep_from, int timestep_to)
+ {
+ RTLIL::SigSpec state_signals = satgen.initial_state.export_all();
+ for (int i = timestep_from; i < timestep_to; i++)
+ ez->assume(ez->NOT(satgen.signals_eq(state_signals, state_signals, i, timestep_to)));
+ }
+
+ bool solve(const std::vector<int> &assumptions)
+ {
+ log_assert(gotTimeout == false);
+ ez->setSolverTimeout(timeout);
+ bool success = ez->solve(modelExpressions, modelValues, assumptions);
+ if (ez->getSolverTimoutStatus())
+ gotTimeout = true;
+ return success;
+ }
+
+ bool solve(int a = 0, int b = 0, int c = 0, int d = 0, int e = 0, int f = 0)
+ {
+ log_assert(gotTimeout == false);
+ ez->setSolverTimeout(timeout);
+ bool success = ez->solve(modelExpressions, modelValues, a, b, c, d, e, f);
+ if (ez->getSolverTimoutStatus())
+ gotTimeout = true;
+ return success;
+ }
+
+ struct ModelBlockInfo {
+ int timestep, offset, width;
+ std::string description;
+ bool operator < (const ModelBlockInfo &other) const {
+ if (timestep != other.timestep)
+ return timestep < other.timestep;
+ if (description != other.description)
+ return description < other.description;
+ if (offset != other.offset)
+ return offset < other.offset;
+ if (width != other.width)
+ return width < other.width;
+ return false;
+ }
+ };
+
+ std::vector<int> modelExpressions;
+ std::vector<bool> modelValues;
+ std::set<ModelBlockInfo> modelInfo;
+
+ void maximize_undefs()
+ {
+ log_assert(enable_undef);
+ std::vector<bool> backupValues;
+
+ while (1)
+ {
+ std::vector<int> must_undef, maybe_undef;
+
+ for (size_t i = 0; i < modelExpressions.size()/2; i++)
+ if (modelValues.at(modelExpressions.size()/2 + i))
+ must_undef.push_back(modelExpressions.at(modelExpressions.size()/2 + i));
+ else
+ maybe_undef.push_back(modelExpressions.at(modelExpressions.size()/2 + i));
+
+ backupValues.swap(modelValues);
+ if (!solve(ez->expression(ezSAT::OpAnd, must_undef), ez->expression(ezSAT::OpOr, maybe_undef)))
+ break;
+ }
+
+ backupValues.swap(modelValues);
+ }
+
+ void generate_model()
+ {
+ RTLIL::SigSpec modelSig;
+ modelExpressions.clear();
+ modelInfo.clear();
+
+ // Add "show" signals or alternatively the leaves on the input cone on all set and prove signals
+
+ if (shows.size() == 0)
+ {
+ SigPool queued_signals, handled_signals, final_signals;
+ queued_signals = show_signal_pool;
+ while (queued_signals.size() > 0) {
+ RTLIL::SigSpec sig = queued_signals.export_one();
+ queued_signals.del(sig);
+ handled_signals.add(sig);
+ std::set<RTLIL::Cell*> drivers = show_drivers.find(sig);
+ if (drivers.size() == 0) {
+ final_signals.add(sig);
+ } else {
+ for (auto &d : drivers)
+ for (auto &p : d->connections()) {
+ if (d->type == "$dff" && p.first == "\\CLK")
+ continue;
+ if (d->type.substr(0, 6) == "$_DFF_" && p.first == "\\C")
+ continue;
+ queued_signals.add(handled_signals.remove(sigmap(p.second)));
+ }
+ }
+ }
+ modelSig = final_signals.export_all();
+
+ // additionally add all set and prove signals directly
+ // (it improves user confidence if we write the constraints back ;-)
+ modelSig.append(show_signal_pool.export_all());
+ }
+ else
+ {
+ for (auto &s : shows) {
+ RTLIL::SigSpec sig;
+ if (!RTLIL::SigSpec::parse_sel(sig, design, module, s))
+ log_cmd_error("Failed to parse show expression `%s'.\n", s.c_str());
+ log("Import show expression: %s\n", log_signal(sig));
+ modelSig.append(sig);
+ }
+ }
+
+ modelSig.sort_and_unify();
+ // log("Model signals: %s\n", log_signal(modelSig));
+
+ std::vector<int> modelUndefExpressions;
+
+ for (auto &c : modelSig.chunks())
+ if (c.wire != NULL)
+ {
+ ModelBlockInfo info;
+ RTLIL::SigSpec chunksig = c;
+ info.width = chunksig.size();
+ info.description = log_signal(chunksig);
+
+ for (int timestep = -1; timestep <= max_timestep; timestep++)
+ {
+ if ((timestep == -1 && max_timestep > 0) || timestep == 0)
+ continue;
+
+ info.timestep = timestep;
+ info.offset = modelExpressions.size();
+ modelInfo.insert(info);
+
+ std::vector<int> vec = satgen.importSigSpec(chunksig, timestep);
+ modelExpressions.insert(modelExpressions.end(), vec.begin(), vec.end());
+
+ if (enable_undef) {
+ std::vector<int> undef_vec = satgen.importUndefSigSpec(chunksig, timestep);
+ modelUndefExpressions.insert(modelUndefExpressions.end(), undef_vec.begin(), undef_vec.end());
+ }
+ }
+ }
+
+ // Add initial state signals as collected by satgen
+ //
+ modelSig = satgen.initial_state.export_all();
+ for (auto &c : modelSig.chunks())
+ if (c.wire != NULL)
+ {
+ ModelBlockInfo info;
+ RTLIL::SigSpec chunksig = c;
+
+ info.timestep = 0;
+ info.offset = modelExpressions.size();
+ info.width = chunksig.size();
+ info.description = log_signal(chunksig);
+ modelInfo.insert(info);
+
+ std::vector<int> vec = satgen.importSigSpec(chunksig, 1);
+ modelExpressions.insert(modelExpressions.end(), vec.begin(), vec.end());
+
+ if (enable_undef) {
+ std::vector<int> undef_vec = satgen.importUndefSigSpec(chunksig, 1);
+ modelUndefExpressions.insert(modelUndefExpressions.end(), undef_vec.begin(), undef_vec.end());
+ }
+ }
+
+ modelExpressions.insert(modelExpressions.end(), modelUndefExpressions.begin(), modelUndefExpressions.end());
+ }
+
+ void print_model()
+ {
+ int maxModelName = 10;
+ int maxModelWidth = 10;
+
+ for (auto &info : modelInfo) {
+ maxModelName = max(maxModelName, int(info.description.size()));
+ maxModelWidth = max(maxModelWidth, info.width);
+ }
+
+ log("\n");
+
+ int last_timestep = -2;
+ for (auto &info : modelInfo)
+ {
+ RTLIL::Const value;
+ bool found_undef = false;
+
+ for (int i = 0; i < info.width; i++) {
+ value.bits.push_back(modelValues.at(info.offset+i) ? RTLIL::State::S1 : RTLIL::State::S0);
+ if (enable_undef && modelValues.at(modelExpressions.size()/2 + info.offset + i))
+ value.bits.back() = RTLIL::State::Sx, found_undef = true;
+ }
+
+ if (info.timestep != last_timestep) {
+ const char *hline = "---------------------------------------------------------------------------------------------------"
+ "---------------------------------------------------------------------------------------------------"
+ "---------------------------------------------------------------------------------------------------";
+ if (last_timestep == -2) {
+ log(max_timestep > 0 ? " Time " : " ");
+ log("%-*s %11s %9s %*s\n", maxModelName+5, "Signal Name", "Dec", "Hex", maxModelWidth+3, "Bin");
+ }
+ log(max_timestep > 0 ? " ---- " : " ");
+ log("%*.*s %11.11s %9.9s %*.*s\n", maxModelName+5, maxModelName+5,
+ hline, hline, hline, maxModelWidth+3, maxModelWidth+3, hline);
+ last_timestep = info.timestep;
+ }
+
+ if (max_timestep > 0) {
+ if (info.timestep > 0)
+ log(" %4d ", info.timestep);
+ else
+ log(" init ");
+ } else
+ log(" ");
+
+ if (info.width <= 32 && !found_undef)
+ log("%-*s %11d %9x %*s\n", maxModelName+5, info.description.c_str(), value.as_int(), value.as_int(), maxModelWidth+3, value.as_string().c_str());
+ else
+ log("%-*s %11s %9s %*s\n", maxModelName+5, info.description.c_str(), "--", "--", maxModelWidth+3, value.as_string().c_str());
+ }
+
+ if (last_timestep == -2)
+ log(" no model variables selected for display.\n");
+ }
+
+ void dump_model_to_vcd(std::string vcd_file_name)
+ {
+ FILE *f = fopen(vcd_file_name.c_str(), "w");
+ if (!f)
+ log_cmd_error("Can't open output file `%s' for writing: %s\n", vcd_file_name.c_str(), strerror(errno));
+
+ log("Dumping SAT model to VCD file %s\n", vcd_file_name.c_str());
+
+ time_t timestamp;
+ struct tm* now;
+ char stime[128] = {};
+ time(&timestamp);
+ now = localtime(&timestamp);
+ strftime(stime, sizeof(stime), "%c", now);
+
+ std::string module_fname = "unknown";
+ auto apos = module->attributes.find("\\src");
+ if(apos != module->attributes.end())
+ module_fname = module->attributes["\\src"].decode_string();
+
+ fprintf(f, "$date\n");
+ fprintf(f, " %s\n", stime);
+ fprintf(f, "$end\n");
+ fprintf(f, "$version\n");
+ fprintf(f, " Generated by %s\n", yosys_version_str);
+ fprintf(f, "$end\n");
+ fprintf(f, "$comment\n");
+ fprintf(f, " Generated from SAT problem in module %s (declared at %s)\n",
+ module->name.c_str(), module_fname.c_str());
+ fprintf(f, "$end\n");
+
+ // VCD has some limits on internal (non-display) identifier names, so make legal ones
+ std::map<std::string, std::string> vcdnames;
+
+ fprintf(f, "$timescale 1ns\n"); // arbitrary time scale since actual clock period is unknown/unimportant
+ fprintf(f, "$scope module %s $end\n", module->name.c_str());
+ for (auto &info : modelInfo)
+ {
+ if (vcdnames.find(info.description) != vcdnames.end())
+ continue;
+
+ char namebuf[16];
+ snprintf(namebuf, sizeof(namebuf), "v%d", static_cast<int>(vcdnames.size()));
+ vcdnames[info.description] = namebuf;
+
+ // Even display identifiers can't use some special characters
+ std::string legal_desc = info.description.c_str();
+ for (auto &c : legal_desc) {
+ if(c == '$')
+ c = '_';
+ if(c == ':')
+ c = '_';
+ }
+
+ fprintf(f, "$var wire %d %s %s $end\n", info.width, namebuf, legal_desc.c_str());
+
+ // Need to look at first *two* cycles!
+ // We need to put a name on all variables but those without an initialization clause
+ // have no value at timestep 0
+ if(info.timestep > 1)
+ break;
+ }
+ fprintf(f, "$upscope $end\n");
+ fprintf(f, "$enddefinitions $end\n");
+ fprintf(f, "$dumpvars\n");
+
+ static const char bitvals[] = "01xzxx";
+
+ int last_timestep = -2;
+ for (auto &info : modelInfo)
+ {
+ RTLIL::Const value;
+
+ for (int i = 0; i < info.width; i++) {
+ value.bits.push_back(modelValues.at(info.offset+i) ? RTLIL::State::S1 : RTLIL::State::S0);
+ if (enable_undef && modelValues.at(modelExpressions.size()/2 + info.offset + i))
+ value.bits.back() = RTLIL::State::Sx;
+ }
+
+ if (info.timestep != last_timestep) {
+ if(last_timestep == 0)
+ fprintf(f, "$end\n");
+ else
+ fprintf(f, "#%d\n", info.timestep);
+ last_timestep = info.timestep;
+ }
+
+ if(info.width == 1) {
+ fprintf(f, "%c%s\n", bitvals[value.bits[0]], vcdnames[info.description].c_str());
+ } else {
+ fprintf(f, "b");
+ for(int k=info.width-1; k >= 0; k --) //need to flip bit ordering for VCD
+ fprintf(f, "%c", bitvals[value.bits[k]]);
+ fprintf(f, " %s\n", vcdnames[info.description].c_str());
+ }
+ }
+
+ if (last_timestep == -2)
+ log(" no model variables selected for display.\n");
+
+ fclose(f);
+ }
+
+ void dump_model_to_json(std::string json_file_name)
+ {
+ FILE *f = fopen(json_file_name.c_str(), "w");
+ if (!f)
+ log_cmd_error("Can't open output file `%s' for writing: %s\n", json_file_name.c_str(), strerror(errno));
+
+ log("Dumping SAT model to WaveJSON file '%s'.\n", json_file_name.c_str());
+
+ int mintime = 1, maxtime = 0, maxwidth = 0;;
+ dict<string, pair<int, dict<int, Const>>> wavedata;
+
+ for (auto &info : modelInfo)
+ {
+ Const value;
+ for (int i = 0; i < info.width; i++) {
+ value.bits.push_back(modelValues.at(info.offset+i) ? RTLIL::State::S1 : RTLIL::State::S0);
+ if (enable_undef && modelValues.at(modelExpressions.size()/2 + info.offset + i))
+ value.bits.back() = RTLIL::State::Sx;
+ }
+
+ wavedata[info.description].first = info.width;
+ wavedata[info.description].second[info.timestep] = value;
+ mintime = min(mintime, info.timestep);
+ maxtime = max(maxtime, info.timestep);
+ maxwidth = max(maxwidth, info.width);
+ }
+
+ fprintf(f, "{ \"signal\": [");
+ bool fist_wavedata = true;
+ for (auto &wd : wavedata)
+ {
+ fprintf(f, "%s", fist_wavedata ? "\n" : ",\n");
+ fist_wavedata = false;
+
+ vector<string> data;
+ string name = wd.first.c_str();
+ while (name.substr(0, 1) == "\\")
+ name = name.substr(1);
+
+ fprintf(f, " { \"name\": \"%s\", \"wave\": \"", name.c_str());
+ for (int i = mintime; i <= maxtime; i++) {
+ if (wd.second.second.count(i)) {
+ string this_data = wd.second.second[i].as_string();
+ char ch = '=';
+ if (wd.second.first == 1)
+ ch = this_data[0];
+ if (!data.empty() && data.back() == this_data) {
+ fprintf(f, ".");
+ } else {
+ data.push_back(this_data);
+ fprintf(f, "%c", ch);
+ }
+ } else {
+ data.push_back("");
+ fprintf(f, "4");
+ }
+ }
+ if (wd.second.first != 1) {
+ fprintf(f, "\", \"data\": [");
+ for (int i = 0; i < GetSize(data); i++)
+ fprintf(f, "%s\"%s\"", i ? ", " : "", data[i].c_str());
+ fprintf(f, "] }");
+ } else {
+ fprintf(f, "\" }");
+ }
+ }
+ fprintf(f, "\n ],\n");
+ fprintf(f, " \"config\": {\n");
+ fprintf(f, " \"hscale\": %.2f\n", maxwidth / 4.0);
+ fprintf(f, " }\n");
+ fprintf(f, "}\n");
+ fclose(f);
+ }
+
+ void invalidate_model(bool max_undef)
+ {
+ std::vector<int> clause;
+ if (enable_undef) {
+ for (size_t i = 0; i < modelExpressions.size()/2; i++) {
+ int bit = modelExpressions.at(i), bit_undef = modelExpressions.at(modelExpressions.size()/2 + i);
+ bool val = modelValues.at(i), val_undef = modelValues.at(modelExpressions.size()/2 + i);
+ if (!max_undef || !val_undef)
+ clause.push_back(val_undef ? ez->NOT(bit_undef) : val ? ez->NOT(bit) : bit);
+ }
+ } else
+ for (size_t i = 0; i < modelExpressions.size(); i++)
+ clause.push_back(modelValues.at(i) ? ez->NOT(modelExpressions.at(i)) : modelExpressions.at(i));
+ ez->assume(ez->expression(ezSAT::OpOr, clause));
+ }
+};
+
+void print_proof_failed()
+{
+ log("\n");
+ log(" ______ ___ ___ _ _ _ _ \n");
+ log(" (_____ \\ / __) / __) (_) | | | |\n");
+ log(" _____) )___ ___ ___ _| |__ _| |__ _____ _| | _____ __| | |\n");
+ log(" | ____/ ___) _ \\ / _ (_ __) (_ __|____ | | || ___ |/ _ |_|\n");
+ log(" | | | | | |_| | |_| || | | | / ___ | | || ____( (_| |_ \n");
+ log(" |_| |_| \\___/ \\___/ |_| |_| \\_____|_|\\_)_____)\\____|_|\n");
+ log("\n");
+}
+
+void print_timeout()
+{
+ log("\n");
+ log(" _____ _ _ _____ ____ _ _____\n");
+ log(" /__ __\\/ \\/ \\__/|/ __// _ \\/ \\ /\\/__ __\\\n");
+ log(" / \\ | || |\\/||| \\ | / \\|| | || / \\\n");
+ log(" | | | || | ||| /_ | \\_/|| \\_/| | |\n");
+ log(" \\_/ \\_/\\_/ \\|\\____\\\\____/\\____/ \\_/\n");
+ log("\n");
+}
+
+void print_qed()
+{
+ log("\n");
+ log(" /$$$$$$ /$$$$$$$$ /$$$$$$$ \n");
+ log(" /$$__ $$ | $$_____/ | $$__ $$ \n");
+ log(" | $$ \\ $$ | $$ | $$ \\ $$ \n");
+ log(" | $$ | $$ | $$$$$ | $$ | $$ \n");
+ log(" | $$ | $$ | $$__/ | $$ | $$ \n");
+ log(" | $$/$$ $$ | $$ | $$ | $$ \n");
+ log(" | $$$$$$/ /$$| $$$$$$$$ /$$| $$$$$$$//$$\n");
+ log(" \\____ $$$|__/|________/|__/|_______/|__/\n");
+ log(" \\__/ \n");
+ log("\n");
+}
+
+struct SatPass : public Pass {
+ SatPass() : Pass("sat", "solve a SAT problem in the circuit") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" sat [options] [selection]\n");
+ log("\n");
+ log("This command solves a SAT problem defined over the currently selected circuit\n");
+ log("and additional constraints passed as parameters.\n");
+ log("\n");
+ log(" -all\n");
+ log(" show all solutions to the problem (this can grow exponentially, use\n");
+ log(" -max <N> instead to get <N> solutions)\n");
+ log("\n");
+ log(" -max <N>\n");
+ log(" like -all, but limit number of solutions to <N>\n");
+ log("\n");
+ log(" -enable_undef\n");
+ log(" enable modeling of undef value (aka 'x-bits')\n");
+ log(" this option is implied by -set-def, -set-undef et. cetera\n");
+ log("\n");
+ log(" -max_undef\n");
+ log(" maximize the number of undef bits in solutions, giving a better\n");
+ log(" picture of which input bits are actually vital to the solution.\n");
+ log("\n");
+ log(" -set <signal> <value>\n");
+ log(" set the specified signal to the specified value.\n");
+ log("\n");
+ log(" -set-def <signal>\n");
+ log(" add a constraint that all bits of the given signal must be defined\n");
+ log("\n");
+ log(" -set-any-undef <signal>\n");
+ log(" add a constraint that at least one bit of the given signal is undefined\n");
+ log("\n");
+ log(" -set-all-undef <signal>\n");
+ log(" add a constraint that all bits of the given signal are undefined\n");
+ log("\n");
+ log(" -set-def-inputs\n");
+ log(" add -set-def constraints for all module inputs\n");
+ log("\n");
+ log(" -show <signal>\n");
+ log(" show the model for the specified signal. if no -show option is\n");
+ log(" passed then a set of signals to be shown is automatically selected.\n");
+ log("\n");
+ log(" -show-inputs, -show-outputs, -show-ports\n");
+ log(" add all module (input/output) ports to the list of shown signals\n");
+ log("\n");
+ log(" -show-regs, -show-public, -show-all\n");
+ log(" show all registers, show signals with 'public' names, show all signals\n");
+ log("\n");
+ log(" -ignore_div_by_zero\n");
+ log(" ignore all solutions that involve a division by zero\n");
+ log("\n");
+ log(" -ignore_unknown_cells\n");
+ log(" ignore all cells that can not be matched to a SAT model\n");
+ log("\n");
+ log("The following options can be used to set up a sequential problem:\n");
+ log("\n");
+ log(" -seq <N>\n");
+ log(" set up a sequential problem with <N> time steps. The steps will\n");
+ log(" be numbered from 1 to N.\n");
+ log("\n");
+ log(" note: for large <N> it can be significantly faster to use\n");
+ log(" -tempinduct-baseonly -maxsteps <N> instead of -seq <N>.\n");
+ log("\n");
+ log(" -set-at <N> <signal> <value>\n");
+ log(" -unset-at <N> <signal>\n");
+ log(" set or unset the specified signal to the specified value in the\n");
+ log(" given timestep. this has priority over a -set for the same signal.\n");
+ log("\n");
+ log(" -set-assumes\n");
+ log(" set all assumptions provided via $assume cells\n");
+ log("\n");
+ log(" -set-def-at <N> <signal>\n");
+ log(" -set-any-undef-at <N> <signal>\n");
+ log(" -set-all-undef-at <N> <signal>\n");
+ log(" add undef constraints in the given timestep.\n");
+ log("\n");
+ log(" -set-init <signal> <value>\n");
+ log(" set the initial value for the register driving the signal to the value\n");
+ log("\n");
+ log(" -set-init-undef\n");
+ log(" set all initial states (not set using -set-init) to undef\n");
+ log("\n");
+ log(" -set-init-def\n");
+ log(" do not force a value for the initial state but do not allow undef\n");
+ log("\n");
+ log(" -set-init-zero\n");
+ log(" set all initial states (not set using -set-init) to zero\n");
+ log("\n");
+ log(" -dump_vcd <vcd-file-name>\n");
+ log(" dump SAT model (counter example in proof) to VCD file\n");
+ log("\n");
+ log(" -dump_json <json-file-name>\n");
+ log(" dump SAT model (counter example in proof) to a WaveJSON file.\n");
+ log("\n");
+ log(" -dump_cnf <cnf-file-name>\n");
+ log(" dump CNF of SAT problem (in DIMACS format). in temporal induction\n");
+ log(" proofs this is the CNF of the first induction step.\n");
+ log("\n");
+ log("The following additional options can be used to set up a proof. If also -seq\n");
+ log("is passed, a temporal induction proof is performed.\n");
+ log("\n");
+ log(" -tempinduct\n");
+ log(" Perform a temporal induction proof. In a temporal induction proof it is\n");
+ log(" proven that the condition holds forever after the number of time steps\n");
+ log(" specified using -seq.\n");
+ log("\n");
+ log(" -tempinduct-def\n");
+ log(" Perform a temporal induction proof. Assume an initial state with all\n");
+ log(" registers set to defined values for the induction step.\n");
+ log("\n");
+ log(" -tempinduct-baseonly\n");
+ log(" Run only the basecase half of temporal induction (requires -maxsteps)\n");
+ log("\n");
+ log(" -tempinduct-inductonly\n");
+ log(" Run only the induction half of temporal induction\n");
+ log("\n");
+ log(" -tempinduct-skip <N>\n");
+ log(" Skip the first <N> steps of the induction proof.\n");
+ log("\n");
+ log(" note: this will assume that the base case holds for <N> steps.\n");
+ log(" this must be proven independently with \"-tempinduct-baseonly\n");
+ log(" -maxsteps <N>\". Use -initsteps if you just want to set a\n");
+ log(" minimal induction length.\n");
+ log("\n");
+ log(" -prove <signal> <value>\n");
+ log(" Attempt to proof that <signal> is always <value>.\n");
+ log("\n");
+ log(" -prove-x <signal> <value>\n");
+ log(" Like -prove, but an undef (x) bit in the lhs matches any value on\n");
+ log(" the right hand side. Useful for equivalence checking.\n");
+ log("\n");
+ log(" -prove-asserts\n");
+ log(" Prove that all asserts in the design hold.\n");
+ log("\n");
+ log(" -prove-skip <N>\n");
+ log(" Do not enforce the prove-condition for the first <N> time steps.\n");
+ log("\n");
+ log(" -maxsteps <N>\n");
+ log(" Set a maximum length for the induction.\n");
+ log("\n");
+ log(" -initsteps <N>\n");
+ log(" Set initial length for the induction.\n");
+ log(" This will speed up the search of the right induction length\n");
+ log(" for deep induction proofs.\n");
+ log("\n");
+ log(" -stepsize <N>\n");
+ log(" Increase the size of the induction proof in steps of <N>.\n");
+ log(" This will speed up the search of the right induction length\n");
+ log(" for deep induction proofs.\n");
+ log("\n");
+ log(" -timeout <N>\n");
+ log(" Maximum number of seconds a single SAT instance may take.\n");
+ log("\n");
+ log(" -verify\n");
+ log(" Return an error and stop the synthesis script if the proof fails.\n");
+ log("\n");
+ log(" -verify-no-timeout\n");
+ log(" Like -verify but do not return an error for timeouts.\n");
+ log("\n");
+ log(" -falsify\n");
+ log(" Return an error and stop the synthesis script if the proof succeeds.\n");
+ log("\n");
+ log(" -falsify-no-timeout\n");
+ log(" Like -falsify but do not return an error for timeouts.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ std::vector<std::pair<std::string, std::string>> sets, sets_init, prove, prove_x;
+ std::map<int, std::vector<std::pair<std::string, std::string>>> sets_at;
+ std::map<int, std::vector<std::string>> unsets_at, sets_def_at, sets_any_undef_at, sets_all_undef_at;
+ std::vector<std::string> shows, sets_def, sets_any_undef, sets_all_undef;
+ int loopcount = 0, seq_len = 0, maxsteps = 0, initsteps = 0, timeout = 0, prove_skip = 0;
+ bool verify = false, fail_on_timeout = false, enable_undef = false, set_def_inputs = false;
+ bool ignore_div_by_zero = false, set_init_undef = false, set_init_zero = false, max_undef = false;
+ bool tempinduct = false, prove_asserts = false, show_inputs = false, show_outputs = false;
+ bool show_regs = false, show_public = false, show_all = false;
+ bool ignore_unknown_cells = false, falsify = false, tempinduct_def = false, set_init_def = false;
+ bool tempinduct_baseonly = false, tempinduct_inductonly = false, set_assumes = false;
+ int tempinduct_skip = 0, stepsize = 1;
+ std::string vcd_file_name, json_file_name, cnf_file_name;
+
+ log_header(design, "Executing SAT pass (solving SAT problems in the circuit).\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ if (args[argidx] == "-all") {
+ loopcount = -1;
+ continue;
+ }
+ if (args[argidx] == "-verify") {
+ fail_on_timeout = true;
+ verify = true;
+ continue;
+ }
+ if (args[argidx] == "-verify-no-timeout") {
+ verify = true;
+ continue;
+ }
+ if (args[argidx] == "-falsify") {
+ fail_on_timeout = true;
+ falsify = true;
+ continue;
+ }
+ if (args[argidx] == "-falsify-no-timeout") {
+ falsify = true;
+ continue;
+ }
+ if (args[argidx] == "-timeout" && argidx+1 < args.size()) {
+ timeout = atoi(args[++argidx].c_str());
+ continue;
+ }
+ if (args[argidx] == "-max" && argidx+1 < args.size()) {
+ loopcount = atoi(args[++argidx].c_str());
+ continue;
+ }
+ if (args[argidx] == "-maxsteps" && argidx+1 < args.size()) {
+ maxsteps = atoi(args[++argidx].c_str());
+ continue;
+ }
+ if (args[argidx] == "-initsteps" && argidx+1 < args.size()) {
+ initsteps = atoi(args[++argidx].c_str());
+ continue;
+ }
+ if (args[argidx] == "-stepsize" && argidx+1 < args.size()) {
+ stepsize = max(1, atoi(args[++argidx].c_str()));
+ continue;
+ }
+ if (args[argidx] == "-ignore_div_by_zero") {
+ ignore_div_by_zero = true;
+ continue;
+ }
+ if (args[argidx] == "-enable_undef") {
+ enable_undef = true;
+ continue;
+ }
+ if (args[argidx] == "-max_undef") {
+ enable_undef = true;
+ max_undef = true;
+ continue;
+ }
+ if (args[argidx] == "-set-def-inputs") {
+ enable_undef = true;
+ set_def_inputs = true;
+ continue;
+ }
+ if (args[argidx] == "-set" && argidx+2 < args.size()) {
+ std::string lhs = args[++argidx];
+ std::string rhs = args[++argidx];
+ sets.push_back(std::pair<std::string, std::string>(lhs, rhs));
+ continue;
+ }
+ if (args[argidx] == "-set-def" && argidx+1 < args.size()) {
+ sets_def.push_back(args[++argidx]);
+ enable_undef = true;
+ continue;
+ }
+ if (args[argidx] == "-set-any-undef" && argidx+1 < args.size()) {
+ sets_any_undef.push_back(args[++argidx]);
+ enable_undef = true;
+ continue;
+ }
+ if (args[argidx] == "-set-all-undef" && argidx+1 < args.size()) {
+ sets_all_undef.push_back(args[++argidx]);
+ enable_undef = true;
+ continue;
+ }
+ if (args[argidx] == "-set-assumes") {
+ set_assumes = true;
+ continue;
+ }
+ if (args[argidx] == "-tempinduct") {
+ tempinduct = true;
+ continue;
+ }
+ if (args[argidx] == "-tempinduct-def") {
+ tempinduct = true;
+ tempinduct_def = true;
+ continue;
+ }
+ if (args[argidx] == "-tempinduct-baseonly") {
+ tempinduct = true;
+ tempinduct_baseonly = true;
+ continue;
+ }
+ if (args[argidx] == "-tempinduct-inductonly") {
+ tempinduct = true;
+ tempinduct_inductonly = true;
+ continue;
+ }
+ if (args[argidx] == "-tempinduct-skip" && argidx+1 < args.size()) {
+ tempinduct_skip = atoi(args[++argidx].c_str());
+ continue;
+ }
+ if (args[argidx] == "-prove" && argidx+2 < args.size()) {
+ std::string lhs = args[++argidx];
+ std::string rhs = args[++argidx];
+ prove.push_back(std::pair<std::string, std::string>(lhs, rhs));
+ continue;
+ }
+ if (args[argidx] == "-prove-x" && argidx+2 < args.size()) {
+ std::string lhs = args[++argidx];
+ std::string rhs = args[++argidx];
+ prove_x.push_back(std::pair<std::string, std::string>(lhs, rhs));
+ enable_undef = true;
+ continue;
+ }
+ if (args[argidx] == "-prove-asserts") {
+ prove_asserts = true;
+ continue;
+ }
+ if (args[argidx] == "-prove-skip" && argidx+1 < args.size()) {
+ prove_skip = atoi(args[++argidx].c_str());
+ continue;
+ }
+ if (args[argidx] == "-seq" && argidx+1 < args.size()) {
+ seq_len = atoi(args[++argidx].c_str());
+ continue;
+ }
+ if (args[argidx] == "-set-at" && argidx+3 < args.size()) {
+ int timestep = atoi(args[++argidx].c_str());
+ std::string lhs = args[++argidx];
+ std::string rhs = args[++argidx];
+ sets_at[timestep].push_back(std::pair<std::string, std::string>(lhs, rhs));
+ continue;
+ }
+ if (args[argidx] == "-unset-at" && argidx+2 < args.size()) {
+ int timestep = atoi(args[++argidx].c_str());
+ unsets_at[timestep].push_back(args[++argidx]);
+ continue;
+ }
+ if (args[argidx] == "-set-def-at" && argidx+2 < args.size()) {
+ int timestep = atoi(args[++argidx].c_str());
+ sets_def_at[timestep].push_back(args[++argidx]);
+ enable_undef = true;
+ continue;
+ }
+ if (args[argidx] == "-set-any-undef-at" && argidx+2 < args.size()) {
+ int timestep = atoi(args[++argidx].c_str());
+ sets_any_undef_at[timestep].push_back(args[++argidx]);
+ enable_undef = true;
+ continue;
+ }
+ if (args[argidx] == "-set-all-undef-at" && argidx+2 < args.size()) {
+ int timestep = atoi(args[++argidx].c_str());
+ sets_all_undef_at[timestep].push_back(args[++argidx]);
+ enable_undef = true;
+ continue;
+ }
+ if (args[argidx] == "-set-init" && argidx+2 < args.size()) {
+ std::string lhs = args[++argidx];
+ std::string rhs = args[++argidx];
+ sets_init.push_back(std::pair<std::string, std::string>(lhs, rhs));
+ continue;
+ }
+ if (args[argidx] == "-set-init-undef") {
+ set_init_undef = true;
+ enable_undef = true;
+ continue;
+ }
+ if (args[argidx] == "-set-init-def") {
+ set_init_def = true;
+ continue;
+ }
+ if (args[argidx] == "-set-init-zero") {
+ set_init_zero = true;
+ continue;
+ }
+ if (args[argidx] == "-show" && argidx+1 < args.size()) {
+ shows.push_back(args[++argidx]);
+ continue;
+ }
+ if (args[argidx] == "-show-inputs") {
+ show_inputs = true;
+ continue;
+ }
+ if (args[argidx] == "-show-outputs") {
+ show_outputs = true;
+ continue;
+ }
+ if (args[argidx] == "-show-ports") {
+ show_inputs = true;
+ show_outputs = true;
+ continue;
+ }
+ if (args[argidx] == "-show-regs") {
+ show_regs = true;
+ continue;
+ }
+ if (args[argidx] == "-show-public") {
+ show_public = true;
+ continue;
+ }
+ if (args[argidx] == "-show-all") {
+ show_all = true;
+ continue;
+ }
+ if (args[argidx] == "-ignore_unknown_cells") {
+ ignore_unknown_cells = true;
+ continue;
+ }
+ if (args[argidx] == "-dump_vcd" && argidx+1 < args.size()) {
+ vcd_file_name = args[++argidx];
+ continue;
+ }
+ if (args[argidx] == "-dump_json" && argidx+1 < args.size()) {
+ json_file_name = args[++argidx];
+ continue;
+ }
+ if (args[argidx] == "-dump_cnf" && argidx+1 < args.size()) {
+ cnf_file_name = args[++argidx];
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ RTLIL::Module *module = NULL;
+ for (auto mod : design->selected_modules()) {
+ if (module)
+ log_cmd_error("Only one module must be selected for the SAT pass! (selected: %s and %s)\n", log_id(module), log_id(mod));
+ module = mod;
+ }
+ if (module == NULL)
+ log_cmd_error("Can't perform SAT on an empty selection!\n");
+
+ if (!prove.size() && !prove_x.size() && !prove_asserts && tempinduct)
+ log_cmd_error("Got -tempinduct but nothing to prove!\n");
+
+ if (prove_skip && tempinduct)
+ log_cmd_error("Options -prove-skip and -tempinduct don't work with each other. Use -seq instead of -prove-skip.\n");
+
+ if (prove_skip >= seq_len && prove_skip > 0)
+ log_cmd_error("The value of -prove-skip must be smaller than the one of -seq.\n");
+
+ if (set_init_undef + set_init_zero + set_init_def > 1)
+ log_cmd_error("The options -set-init-undef, -set-init-def, and -set-init-zero are exclusive!\n");
+
+ if (set_def_inputs) {
+ for (auto &it : module->wires_)
+ if (it.second->port_input)
+ sets_def.push_back(it.second->name.str());
+ }
+
+ if (show_inputs) {
+ for (auto &it : module->wires_)
+ if (it.second->port_input)
+ shows.push_back(it.second->name.str());
+ }
+
+ if (show_outputs) {
+ for (auto &it : module->wires_)
+ if (it.second->port_output)
+ shows.push_back(it.second->name.str());
+ }
+
+ if (show_regs) {
+ pool<Wire*> reg_wires;
+ for (auto cell : module->cells()) {
+ if (cell->type == "$dff" || cell->type.substr(0, 6) == "$_DFF_")
+ for (auto bit : cell->getPort("\\Q"))
+ if (bit.wire)
+ reg_wires.insert(bit.wire);
+ }
+ for (auto wire : reg_wires)
+ shows.push_back(wire->name.str());
+ }
+
+ if (show_public) {
+ for (auto wire : module->wires())
+ if (wire->name[0] == '\\')
+ shows.push_back(wire->name.str());
+ }
+
+ if (show_all) {
+ for (auto wire : module->wires())
+ shows.push_back(wire->name.str());
+ }
+
+ if (tempinduct)
+ {
+ if (loopcount > 0 || max_undef)
+ log_cmd_error("The options -max, -all, and -max_undef are not supported for temporal induction proofs!\n");
+
+ SatHelper basecase(design, module, enable_undef);
+ SatHelper inductstep(design, module, enable_undef);
+
+ basecase.sets = sets;
+ basecase.set_assumes = set_assumes;
+ basecase.prove = prove;
+ basecase.prove_x = prove_x;
+ basecase.prove_asserts = prove_asserts;
+ basecase.sets_at = sets_at;
+ basecase.unsets_at = unsets_at;
+ basecase.shows = shows;
+ basecase.timeout = timeout;
+ basecase.sets_def = sets_def;
+ basecase.sets_any_undef = sets_any_undef;
+ basecase.sets_all_undef = sets_all_undef;
+ basecase.sets_def_at = sets_def_at;
+ basecase.sets_any_undef_at = sets_any_undef_at;
+ basecase.sets_all_undef_at = sets_all_undef_at;
+ basecase.sets_init = sets_init;
+ basecase.set_init_def = set_init_def;
+ basecase.set_init_undef = set_init_undef;
+ basecase.set_init_zero = set_init_zero;
+ basecase.satgen.ignore_div_by_zero = ignore_div_by_zero;
+ basecase.ignore_unknown_cells = ignore_unknown_cells;
+
+ for (int timestep = 1; timestep <= seq_len; timestep++)
+ if (!tempinduct_inductonly)
+ basecase.setup(timestep, timestep == 1);
+
+ inductstep.sets = sets;
+ inductstep.set_assumes = set_assumes;
+ inductstep.prove = prove;
+ inductstep.prove_x = prove_x;
+ inductstep.prove_asserts = prove_asserts;
+ inductstep.shows = shows;
+ inductstep.timeout = timeout;
+ inductstep.sets_def = sets_def;
+ inductstep.sets_any_undef = sets_any_undef;
+ inductstep.sets_all_undef = sets_all_undef;
+ inductstep.satgen.ignore_div_by_zero = ignore_div_by_zero;
+ inductstep.ignore_unknown_cells = ignore_unknown_cells;
+
+ if (!tempinduct_baseonly) {
+ inductstep.setup(1);
+ inductstep.ez->assume(inductstep.setup_proof(1));
+ }
+
+ if (tempinduct_def) {
+ std::vector<int> undef_state = inductstep.satgen.importUndefSigSpec(inductstep.satgen.initial_state.export_all(), 1);
+ inductstep.ez->assume(inductstep.ez->NOT(inductstep.ez->expression(ezSAT::OpOr, undef_state)));
+ }
+
+ for (int inductlen = 1; inductlen <= maxsteps || maxsteps == 0; inductlen++)
+ {
+ log("\n** Trying induction with length %d **\n", inductlen);
+
+ // phase 1: proving base case
+
+ if (!tempinduct_inductonly)
+ {
+ basecase.setup(seq_len + inductlen, seq_len + inductlen == 1);
+ int property = basecase.setup_proof(seq_len + inductlen);
+ basecase.generate_model();
+
+ if (inductlen > 1)
+ basecase.force_unique_state(seq_len + 1, seq_len + inductlen);
+
+ if (tempinduct_skip < inductlen)
+ {
+ log("\n[base case %d] Solving problem with %d variables and %d clauses..\n",
+ inductlen, basecase.ez->numCnfVariables(), basecase.ez->numCnfClauses());
+ log_flush();
+
+ if (basecase.solve(basecase.ez->NOT(property))) {
+ log("SAT temporal induction proof finished - model found for base case: FAIL!\n");
+ print_proof_failed();
+ basecase.print_model();
+ if(!vcd_file_name.empty())
+ basecase.dump_model_to_vcd(vcd_file_name);
+ if(!json_file_name.empty())
+ basecase.dump_model_to_json(json_file_name);
+ goto tip_failed;
+ }
+
+ if (basecase.gotTimeout)
+ goto timeout;
+
+ log("Base case for induction length %d proven.\n", inductlen);
+ }
+ else
+ {
+ log("\n[base case %d] Skipping prove for this step (-tempinduct-skip %d).",
+ inductlen, tempinduct_skip);
+ log("\n[base case %d] Problem size so far: %d variables and %d clauses.\n",
+ inductlen, basecase.ez->numCnfVariables(), basecase.ez->numCnfClauses());
+ }
+ basecase.ez->assume(property);
+ }
+
+ // phase 2: proving induction step
+
+ if (!tempinduct_baseonly)
+ {
+ inductstep.setup(inductlen + 1);
+ int property = inductstep.setup_proof(inductlen + 1);
+ inductstep.generate_model();
+
+ if (inductlen > 1)
+ inductstep.force_unique_state(1, inductlen + 1);
+
+ if (inductlen <= tempinduct_skip || inductlen <= initsteps || inductlen % stepsize != 0)
+ {
+ if (inductlen < tempinduct_skip)
+ log("\n[induction step %d] Skipping prove for this step (-tempinduct-skip %d).",
+ inductlen, tempinduct_skip);
+ if (inductlen < initsteps)
+ log("\n[induction step %d] Skipping prove for this step (-initsteps %d).",
+ inductlen, tempinduct_skip);
+ if (inductlen % stepsize != 0)
+ log("\n[induction step %d] Skipping prove for this step (-stepsize %d).",
+ inductlen, stepsize);
+ log("\n[induction step %d] Problem size so far: %d variables and %d clauses.\n",
+ inductlen, inductstep.ez->numCnfVariables(), inductstep.ez->numCnfClauses());
+ inductstep.ez->assume(property);
+ }
+ else
+ {
+ if (!cnf_file_name.empty())
+ {
+ FILE *f = fopen(cnf_file_name.c_str(), "w");
+ if (!f)
+ log_cmd_error("Can't open output file `%s' for writing: %s\n", cnf_file_name.c_str(), strerror(errno));
+
+ log("Dumping CNF to file `%s'.\n", cnf_file_name.c_str());
+ cnf_file_name.clear();
+
+ inductstep.ez->printDIMACS(f, false);
+ fclose(f);
+ }
+
+ log("\n[induction step %d] Solving problem with %d variables and %d clauses..\n",
+ inductlen, inductstep.ez->numCnfVariables(), inductstep.ez->numCnfClauses());
+ log_flush();
+
+ if (!inductstep.solve(inductstep.ez->NOT(property))) {
+ if (inductstep.gotTimeout)
+ goto timeout;
+ log("Induction step proven: SUCCESS!\n");
+ print_qed();
+ goto tip_success;
+ }
+
+ log("Induction step failed. Incrementing induction length.\n");
+ inductstep.ez->assume(property);
+ inductstep.print_model();
+ }
+ }
+ }
+
+ if (tempinduct_baseonly) {
+ log("\nReached maximum number of time steps -> proved base case for %d steps: SUCCESS!\n", maxsteps);
+ goto tip_success;
+ }
+
+ log("\nReached maximum number of time steps -> proof failed.\n");
+ if(!vcd_file_name.empty())
+ inductstep.dump_model_to_vcd(vcd_file_name);
+ if(!json_file_name.empty())
+ inductstep.dump_model_to_json(json_file_name);
+ print_proof_failed();
+
+ tip_failed:
+ if (verify) {
+ log("\n");
+ log_error("Called with -verify and proof did fail!\n");
+ }
+
+ if (0)
+ tip_success:
+ if (falsify) {
+ log("\n");
+ log_error("Called with -falsify and proof did succeed!\n");
+ }
+ }
+ else
+ {
+ if (maxsteps > 0)
+ log_cmd_error("The options -maxsteps is only supported for temporal induction proofs!\n");
+
+ SatHelper sathelper(design, module, enable_undef);
+
+ sathelper.sets = sets;
+ sathelper.set_assumes = set_assumes;
+ sathelper.prove = prove;
+ sathelper.prove_x = prove_x;
+ sathelper.prove_asserts = prove_asserts;
+ sathelper.sets_at = sets_at;
+ sathelper.unsets_at = unsets_at;
+ sathelper.shows = shows;
+ sathelper.timeout = timeout;
+ sathelper.sets_def = sets_def;
+ sathelper.sets_any_undef = sets_any_undef;
+ sathelper.sets_all_undef = sets_all_undef;
+ sathelper.sets_def_at = sets_def_at;
+ sathelper.sets_any_undef_at = sets_any_undef_at;
+ sathelper.sets_all_undef_at = sets_all_undef_at;
+ sathelper.sets_init = sets_init;
+ sathelper.set_init_def = set_init_def;
+ sathelper.set_init_undef = set_init_undef;
+ sathelper.set_init_zero = set_init_zero;
+ sathelper.satgen.ignore_div_by_zero = ignore_div_by_zero;
+ sathelper.ignore_unknown_cells = ignore_unknown_cells;
+
+ if (seq_len == 0) {
+ sathelper.setup();
+ if (sathelper.prove.size() || sathelper.prove_x.size() || sathelper.prove_asserts)
+ sathelper.ez->assume(sathelper.ez->NOT(sathelper.setup_proof()));
+ } else {
+ std::vector<int> prove_bits;
+ for (int timestep = 1; timestep <= seq_len; timestep++) {
+ sathelper.setup(timestep, timestep == 1);
+ if (sathelper.prove.size() || sathelper.prove_x.size() || sathelper.prove_asserts)
+ if (timestep > prove_skip)
+ prove_bits.push_back(sathelper.setup_proof(timestep));
+ }
+ if (sathelper.prove.size() || sathelper.prove_x.size() || sathelper.prove_asserts)
+ sathelper.ez->assume(sathelper.ez->NOT(sathelper.ez->expression(ezSAT::OpAnd, prove_bits)));
+ }
+ sathelper.generate_model();
+
+ if (!cnf_file_name.empty())
+ {
+ FILE *f = fopen(cnf_file_name.c_str(), "w");
+ if (!f)
+ log_cmd_error("Can't open output file `%s' for writing: %s\n", cnf_file_name.c_str(), strerror(errno));
+
+ log("Dumping CNF to file `%s'.\n", cnf_file_name.c_str());
+ cnf_file_name.clear();
+
+ sathelper.ez->printDIMACS(f, false);
+ fclose(f);
+ }
+
+ int rerun_counter = 0;
+
+ rerun_solver:
+ log("\nSolving problem with %d variables and %d clauses..\n",
+ sathelper.ez->numCnfVariables(), sathelper.ez->numCnfClauses());
+ log_flush();
+
+ if (sathelper.solve())
+ {
+ if (max_undef) {
+ log("SAT model found. maximizing number of undefs.\n");
+ sathelper.maximize_undefs();
+ }
+
+ if (!prove.size() && !prove_x.size() && !prove_asserts) {
+ log("SAT solving finished - model found:\n");
+ } else {
+ log("SAT proof finished - model found: FAIL!\n");
+ print_proof_failed();
+ }
+
+ sathelper.print_model();
+
+ if(!vcd_file_name.empty())
+ sathelper.dump_model_to_vcd(vcd_file_name);
+ if(!json_file_name.empty())
+ sathelper.dump_model_to_json(json_file_name);
+
+ if (loopcount != 0) {
+ loopcount--, rerun_counter++;
+ sathelper.invalidate_model(max_undef);
+ goto rerun_solver;
+ }
+
+ if (!prove.size() && !prove_x.size() && !prove_asserts) {
+ if (falsify) {
+ log("\n");
+ log_error("Called with -falsify and found a model!\n");
+ }
+ } else {
+ if (verify) {
+ log("\n");
+ log_error("Called with -verify and proof did fail!\n");
+ }
+ }
+ }
+ else
+ {
+ if (sathelper.gotTimeout)
+ goto timeout;
+ if (rerun_counter)
+ log("SAT solving finished - no more models found (after %d distinct solutions).\n", rerun_counter);
+ else if (!prove.size() && !prove_x.size() && !prove_asserts) {
+ log("SAT solving finished - no model found.\n");
+ if (verify) {
+ log("\n");
+ log_error("Called with -verify and found no model!\n");
+ }
+ } else {
+ log("SAT proof finished - no model found: SUCCESS!\n");
+ print_qed();
+ if (falsify) {
+ log("\n");
+ log_error("Called with -falsify and proof did succeed!\n");
+ }
+ }
+ }
+
+ if (!prove.size() && !prove_x.size() && !prove_asserts) {
+ if (falsify && rerun_counter) {
+ log("\n");
+ log_error("Called with -falsify and found a model!\n");
+ }
+ } else {
+ if (verify && rerun_counter) {
+ log("\n");
+ log_error("Called with -verify and proof did fail!\n");
+ }
+ }
+ }
+
+ if (0) {
+ timeout:
+ log("Interrupted SAT solver: TIMEOUT!\n");
+ print_timeout();
+ if (fail_on_timeout)
+ log_error("Called with -verify and proof did time out!\n");
+ }
+ }
+} SatPass;
+
+PRIVATE_NAMESPACE_END