aboutsummaryrefslogtreecommitdiffstats
path: root/passes
diff options
context:
space:
mode:
Diffstat (limited to 'passes')
-rw-r--r--passes/cmds/Makefile.inc1
-rw-r--r--passes/cmds/clean_zerowidth.cc2
-rw-r--r--passes/cmds/glift.cc599
-rw-r--r--passes/cmds/stat.cc4
-rw-r--r--passes/memory/memory_bram.cc28
-rw-r--r--passes/opt/opt_dff.cc210
-rw-r--r--passes/opt/opt_reduce.cc397
-rw-r--r--passes/pmgen/ice40_dsp.pmg5
-rw-r--r--passes/sat/sim.cc457
-rw-r--r--passes/techmap/Makefile.inc2
-rw-r--r--passes/techmap/bmuxmap.cc76
-rw-r--r--passes/techmap/demuxmap.cc80
-rw-r--r--passes/techmap/iopadmap.cc2
-rw-r--r--passes/techmap/simplemap.cc26
-rw-r--r--passes/tests/test_cell.cc46
15 files changed, 1731 insertions, 204 deletions
diff --git a/passes/cmds/Makefile.inc b/passes/cmds/Makefile.inc
index 917856767..16a38b511 100644
--- a/passes/cmds/Makefile.inc
+++ b/passes/cmds/Makefile.inc
@@ -18,6 +18,7 @@ OBJS += passes/cmds/setattr.o
OBJS += passes/cmds/copy.o
OBJS += passes/cmds/splice.o
OBJS += passes/cmds/scc.o
+OBJS += passes/cmds/glift.o
OBJS += passes/cmds/torder.o
OBJS += passes/cmds/logcmd.o
OBJS += passes/cmds/tee.o
diff --git a/passes/cmds/clean_zerowidth.cc b/passes/cmds/clean_zerowidth.cc
index 4e7c68093..bac6b1521 100644
--- a/passes/cmds/clean_zerowidth.cc
+++ b/passes/cmds/clean_zerowidth.cc
@@ -80,7 +80,7 @@ struct CleanZeroWidthPass : public Pass {
if (GetSize(cell->getPort(ID::Q)) == 0) {
module->remove(cell);
}
- } else if (cell->type == ID($pmux)) {
+ } else if (cell->type.in(ID($pmux), ID($bmux), ID($demux))) {
// Remove altogether if WIDTH is 0, replace with
// a connection if S_WIDTH is 0.
if (cell->getParam(ID::WIDTH).as_int() == 0) {
diff --git a/passes/cmds/glift.cc b/passes/cmds/glift.cc
new file mode 100644
index 000000000..b398c3e04
--- /dev/null
+++ b/passes/cmds/glift.cc
@@ -0,0 +1,599 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2020 Alberto Gonzalez <boqwxp@airmail.cc>
+ *
+ * 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/utils.h"
+#include "kernel/log.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct GliftWorker {
+private:
+ bool is_top_module = false;
+ bool opt_create_precise_model = false, opt_create_imprecise_model = false, opt_create_instrumented_model = false;
+ bool opt_taintconstants = false, opt_keepoutputs = false, opt_simplecostmodel = false, opt_nocostmodel = false;
+ bool opt_instrumentmore = false;
+ std::vector<RTLIL::Wire *> new_taint_outputs;
+ std::vector<std::pair<RTLIL::SigSpec, RTLIL::IdString>> meta_mux_selects;
+ RTLIL::Module *module = nullptr;
+
+ const RTLIL::IdString cost_model_wire_name = ID(__glift_weight);
+ const RTLIL::IdString glift_attribute_name = ID(glift);
+
+
+ RTLIL::SigSpec get_corresponding_taint_signal(RTLIL::SigSpec sig) {
+ RTLIL::SigSpec ret;
+
+ //Get the connected wire for the cell port:
+ log_assert(sig.is_wire() || sig.is_fully_const());
+ log_assert(sig.is_wire() || sig.is_fully_const());
+
+ //Get a SigSpec for the corresponding taint signal for the cell port, creating one if necessary:
+ if (sig.is_wire()) {
+ RTLIL::Wire *w = module->wire(sig.as_wire()->name.str() + "_t");
+ if (w == nullptr) w = module->addWire(sig.as_wire()->name.str() + "_t", 1);
+ ret = w;
+ }
+ else if (sig.is_fully_const() && opt_taintconstants)
+ ret = RTLIL::State::S1;
+ else if (sig.is_fully_const())
+ ret = RTLIL::State::S0;
+ else
+ log_cmd_error("Cell port SigSpec has unexpected type.\n");
+
+ //Finally, if the cell port was a module input or output, make sure the corresponding taint signal is marked, too:
+ if(sig.is_wire() && sig.as_wire()->port_input)
+ ret.as_wire()->port_input = true;
+ if(sig.is_wire() && sig.as_wire()->port_output)
+ new_taint_outputs.push_back(ret.as_wire());
+
+ return ret;
+ }
+
+ void add_precise_GLIFT_logic(const RTLIL::Cell *cell, RTLIL::SigSpec &port_a, RTLIL::SigSpec &port_a_taint, RTLIL::SigSpec &port_b, RTLIL::SigSpec &port_b_taint, RTLIL::SigSpec &port_y_taint) {
+ //AKA AN2_SH2 or OR2_SH2
+ bool is_and = cell->type.in(ID($_AND_), ID($_NAND_));
+ RTLIL::SigSpec n_port_a = module->LogicNot(cell->name.str() + "_t_1_1", port_a, false, cell->get_src_attribute());
+ RTLIL::SigSpec n_port_b = module->LogicNot(cell->name.str() + "_t_1_2", port_b, false, cell->get_src_attribute());
+ auto subexpr1 = module->And(cell->name.str() + "_t_1_3", is_and? port_a : n_port_a, port_b_taint, false, cell->get_src_attribute());
+ auto subexpr2 = module->And(cell->name.str() + "_t_1_4", is_and? port_b : n_port_b, port_a_taint, false, cell->get_src_attribute());
+ auto subexpr3 = module->And(cell->name.str() + "_t_1_5", port_a_taint, port_b_taint, false, cell->get_src_attribute());
+ auto subexpr4 = module->Or(cell->name.str() + "_t_1_6", subexpr1, subexpr2, false, cell->get_src_attribute());
+ module->addOr(cell->name.str() + "_t_1_7", subexpr4, subexpr3, port_y_taint, false, cell->get_src_attribute());
+ }
+
+ void add_imprecise_GLIFT_logic_1(const RTLIL::Cell *cell, RTLIL::SigSpec &port_a, RTLIL::SigSpec &port_a_taint, RTLIL::SigSpec &port_b, RTLIL::SigSpec &port_b_taint, RTLIL::SigSpec &port_y_taint) {
+ //AKA AN2_SH3 or OR2_SH3
+ bool is_and = cell->type.in(ID($_AND_), ID($_NAND_));
+ RTLIL::SigSpec n_port_a = module->LogicNot(cell->name.str() + "_t_2_1", port_a, false, cell->get_src_attribute());
+ auto subexpr1 = module->And(cell->name.str() + "_t_2_2", is_and? port_b : n_port_a, is_and? port_a_taint : port_b_taint, false, cell->get_src_attribute());
+ module->addOr(cell->name.str() + "_t_2_3", is_and? port_b_taint : port_a_taint, subexpr1, port_y_taint, false, cell->get_src_attribute());
+ }
+
+ void add_imprecise_GLIFT_logic_2(const RTLIL::Cell *cell, RTLIL::SigSpec &port_a, RTLIL::SigSpec &port_a_taint, RTLIL::SigSpec &port_b, RTLIL::SigSpec &port_b_taint, RTLIL::SigSpec &port_y_taint) {
+ //AKA AN2_SH4 or OR2_SH4
+ bool is_and = cell->type.in(ID($_AND_), ID($_NAND_));
+ RTLIL::SigSpec n_port_b = module->LogicNot(cell->name.str() + "_t_3_1", port_b, false, cell->get_src_attribute());
+ auto subexpr1 = module->And(cell->name.str() + "_t_3_2", is_and? port_a : n_port_b, is_and? port_b_taint : port_a_taint, false, cell->get_src_attribute());
+ module->addOr(cell->name.str() + "_t_3_3", is_and? port_a_taint : port_b_taint, subexpr1, port_y_taint, false, cell->get_src_attribute());
+ }
+
+ void add_imprecise_GLIFT_logic_3(const RTLIL::Cell *cell, RTLIL::SigSpec &port_a_taint, RTLIL::SigSpec &port_b_taint, RTLIL::SigSpec &port_y_taint) {
+ //AKA AN2_SH5 or OR2_SH5 or XR2_SH2
+ module->addOr(cell->name.str() + "_t_4_1", port_a_taint, port_b_taint, port_y_taint, false, cell->get_src_attribute());
+ }
+
+ void add_imprecise_GLIFT_logic_4(RTLIL::SigSpec &port_a_taint, RTLIL::SigSpec &port_y_taint) {
+ module->connect(port_y_taint, port_a_taint);
+ }
+
+ void add_imprecise_GLIFT_logic_5(RTLIL::SigSpec &port_b_taint, RTLIL::SigSpec &port_y_taint) {
+ module->connect(port_y_taint, port_b_taint);
+ }
+
+ void add_imprecise_GLIFT_logic_6(RTLIL::SigSpec &port_y_taint) {
+ module->connect(port_y_taint, RTLIL::Const(1, 1));
+ }
+
+ void add_imprecise_GLIFT_logic_7(RTLIL::SigSpec &port_y_taint) {
+ module->connect(port_y_taint, RTLIL::Const(0, 1));
+ }
+
+ void add_precise_GLIFT_mux(const RTLIL::Cell *cell, RTLIL::SigSpec &port_a, RTLIL::SigSpec &port_a_taint, RTLIL::SigSpec &port_b, RTLIL::SigSpec &port_b_taint, RTLIL::SigSpec &port_s, RTLIL::SigSpec &port_s_taint, RTLIL::SigSpec &port_y_taint) {
+ //S&At | ~S&Bt | ~A&B&St | A&~B&St | At&St | Bt&St
+ RTLIL::SigSpec n_port_a = module->LogicNot(cell->name.str() + "_t_4_1", port_a, false, cell->get_src_attribute());
+ RTLIL::SigSpec n_port_b = module->LogicNot(cell->name.str() + "_t_4_2", port_b, false, cell->get_src_attribute());
+ RTLIL::SigSpec n_port_s = module->LogicNot(cell->name.str() + "_t_4_3", port_s, false, cell->get_src_attribute());
+ auto subexpr1 = module->And(cell->name.str() + "_t_4_4", port_s, port_a_taint, false, cell->get_src_attribute());
+ auto subexpr2 = module->And(cell->name.str() + "_t_4_5", n_port_s, port_b_taint, false, cell->get_src_attribute());
+ auto subexpr3 = module->And(cell->name.str() + "_t_4_6", n_port_a, port_b, false, cell->get_src_attribute());
+ auto subexpr4 = module->And(cell->name.str() + "_t_4_7", subexpr3, port_s_taint, false, cell->get_src_attribute());
+ auto subexpr5 = module->And(cell->name.str() + "_t_4_8", port_a, n_port_b, false, cell->get_src_attribute());
+ auto subexpr6 = module->And(cell->name.str() + "_t_4_9", subexpr5, port_s_taint, false, cell->get_src_attribute());
+ auto subexpr7 = module->And(cell->name.str() + "_t_4_10", port_a_taint, port_s_taint, false, cell->get_src_attribute());
+ auto subexpr8 = module->And(cell->name.str() + "_t_4_11", port_b_taint, port_s_taint, false, cell->get_src_attribute());
+ auto subexpr9 = module->Or(cell->name.str() + "_t_4_12", subexpr1, subexpr2, false, cell->get_src_attribute());
+ auto subexpr10 = module->Or(cell->name.str() + "_t_4_13", subexpr4, subexpr6, false, cell->get_src_attribute());
+ auto subexpr11 = module->Or(cell->name.str() + "_t_4_14", subexpr7, subexpr8, false, cell->get_src_attribute());
+ auto subexpr12 = module->Or(cell->name.str() + "_t_4_15", subexpr9, subexpr10, false, cell->get_src_attribute());
+ module->addOr(cell->name.str() + "_t_4_16", subexpr11, subexpr12, port_y_taint, false, cell->get_src_attribute());
+ }
+
+ RTLIL::SigSpec score_metamux_select(const RTLIL::SigSpec &metamux_select, const RTLIL::IdString celltype) {
+ log_assert(metamux_select.is_wire());
+
+ if (opt_simplecostmodel) {
+ //The complex model is an area model, so a lower score should mean smaller.
+ //In this case, a nonzero hole metamux select value means less logic.
+ //Thus we should invert the ReduceOr over the metamux_select signal.
+ RTLIL::SigSpec pmux_select = module->ReduceOr(metamux_select.as_wire()->name.str() + "_nonzero", metamux_select);
+ return module->Pmux(NEW_ID, RTLIL::Const(1), RTLIL::Const(0), pmux_select, metamux_select.as_wire()->get_src_attribute());
+ } else {
+ auto select_width = metamux_select.as_wire()->width;
+
+ std::vector<RTLIL::Const> costs;
+ if (celltype == ID($_AND_) || celltype == ID($_OR_)) {
+ costs = {5, 2, 2, 1, 0, 0, 0, 0};
+ log_assert(select_width == 2 || select_width == 3);
+ log_assert(opt_instrumentmore || select_width == 2);
+ log_assert(!opt_instrumentmore || select_width == 3);
+ }
+ else if (celltype == ID($_XOR_) || celltype == ID($_XNOR_)) {
+ costs = {1, 0, 0, 0};
+ log_assert(select_width == 2);
+ }
+
+ std::vector<RTLIL::SigSpec> next_pmux_y_ports, pmux_y_ports(costs.begin(), costs.begin() + exp2(select_width));
+ for (auto i = 0; pmux_y_ports.size() > 1; ++i) {
+ for (auto j = 0; j+1 < GetSize(pmux_y_ports); j += 2) {
+ next_pmux_y_ports.emplace_back(module->Pmux(stringf("%s_mux_%d_%d", metamux_select.as_wire()->name.c_str(), i, j), pmux_y_ports[j], pmux_y_ports[j+1], metamux_select[GetSize(metamux_select) - 1 - i], metamux_select.as_wire()->get_src_attribute()));
+ }
+ if (GetSize(pmux_y_ports) % 2 == 1)
+ next_pmux_y_ports.push_back(pmux_y_ports[GetSize(pmux_y_ports) - 1]);
+ pmux_y_ports.swap(next_pmux_y_ports);
+ next_pmux_y_ports.clear();
+ }
+
+ log_assert(pmux_y_ports.size() == 1);
+ return pmux_y_ports[0];
+ }
+ }
+
+ void create_glift_logic() {
+ if (module->get_bool_attribute(glift_attribute_name))
+ return;
+
+ std::vector<RTLIL::SigSig> connections(module->connections());
+
+ for(auto &cell : module->cells().to_vector()) {
+ if (!cell->type.in({ID($_AND_), ID($_NAND_), ID($_OR_), ID($_NOR_), ID($_XOR_), ID($_XNOR_), ID($_MUX_), ID($_NMUX_), ID($_NOT_), ID($anyconst), ID($allconst), ID($assume), ID($assert)}) && module->design->module(cell->type) == nullptr) {
+ log_cmd_error("Unsupported cell type \"%s\" found. Run `techmap` first.\n", cell->type.c_str());
+ }
+ if (cell->type.in(ID($_AND_), ID($_NAND_), ID($_OR_), ID($_NOR_))) {
+ const unsigned int A = 0, B = 1, Y = 2;
+ const unsigned int NUM_PORTS = 3;
+ RTLIL::SigSpec ports[NUM_PORTS] = {cell->getPort(ID::A), cell->getPort(ID::B), cell->getPort(ID::Y)};
+ RTLIL::SigSpec port_taints[NUM_PORTS];
+
+ if (ports[A].size() != 1 || ports[B].size() != 1 || ports[Y].size() != 1)
+ log_cmd_error("Multi-bit signal found. Run `splitnets` first.\n");
+ for (unsigned int i = 0; i < NUM_PORTS; ++i)
+ port_taints[i] = get_corresponding_taint_signal(ports[i]);
+
+ if (opt_create_precise_model)
+ add_precise_GLIFT_logic(cell, ports[A], port_taints[A], ports[B], port_taints[B], port_taints[Y]);
+ else if (opt_create_imprecise_model)
+ add_imprecise_GLIFT_logic_3(cell, port_taints[A], port_taints[B], port_taints[Y]);
+ else if (opt_create_instrumented_model) {
+ std::vector<RTLIL::SigSpec> taint_version;
+ int num_versions = opt_instrumentmore? 8 : 4;
+
+ for (auto i = 1; i <= num_versions; ++i)
+ taint_version.emplace_back(RTLIL::SigSpec(module->addWire(stringf("%s_y%d", cell->name.c_str(), i), 1)));
+
+ for (auto i = 0; i < num_versions; ++i) {
+ switch(i) {
+ case 0: add_precise_GLIFT_logic(cell, ports[A], port_taints[A], ports[B], port_taints[B], taint_version[i]);
+ break;
+ case 1: add_imprecise_GLIFT_logic_1(cell, ports[A], port_taints[A], ports[B], port_taints[B], taint_version[i]);
+ break;
+ case 2: add_imprecise_GLIFT_logic_2(cell, ports[A], port_taints[A], ports[B], port_taints[B], taint_version[i]);
+ break;
+ case 3: add_imprecise_GLIFT_logic_3(cell, port_taints[A], port_taints[B], taint_version[i]);
+ break;
+ case 4: add_imprecise_GLIFT_logic_4(port_taints[A], taint_version[i]);
+ break;
+ case 5: add_imprecise_GLIFT_logic_5(port_taints[B], taint_version[i]);
+ break;
+ case 6: add_imprecise_GLIFT_logic_6(taint_version[i]);
+ break;
+ case 7: add_imprecise_GLIFT_logic_7(taint_version[i]);
+ break;
+ default: log_assert(false);
+ }
+ }
+
+ auto select_width = log2(num_versions);
+ log_assert(exp2(select_width) == num_versions);
+ RTLIL::SigSpec meta_mux_select(module->addWire(cell->name.str() + "_sel", select_width));
+ meta_mux_selects.push_back(make_pair(meta_mux_select, cell->type));
+ module->connect(meta_mux_select, module->Anyconst(cell->name.str() + "_hole", select_width, cell->get_src_attribute()));
+
+ std::vector<RTLIL::SigSpec> next_meta_mux_y_ports, meta_mux_y_ports(taint_version);
+ for (auto i = 0; meta_mux_y_ports.size() > 1; ++i) {
+ for (auto j = 0; j+1 < GetSize(meta_mux_y_ports); j += 2) {
+ next_meta_mux_y_ports.emplace_back(module->Mux(stringf("%s_mux_%d_%d", cell->name.c_str(), i, j), meta_mux_y_ports[j], meta_mux_y_ports[j+1], meta_mux_select[GetSize(meta_mux_select) - 1 - i]));
+ }
+ if (GetSize(meta_mux_y_ports) % 2 == 1)
+ next_meta_mux_y_ports.push_back(meta_mux_y_ports[GetSize(meta_mux_y_ports) - 1]);
+ meta_mux_y_ports.swap(next_meta_mux_y_ports);
+ next_meta_mux_y_ports.clear();
+ }
+ log_assert(meta_mux_y_ports.size() == 1);
+ module->connect(port_taints[Y], meta_mux_y_ports[0]);
+ }
+ else log_cmd_error("This is a bug (1).\n");
+ }
+ else if (cell->type.in(ID($_XOR_), ID($_XNOR_))) {
+ const unsigned int A = 0, B = 1, Y = 2;
+ const unsigned int NUM_PORTS = 3;
+ RTLIL::SigSpec ports[NUM_PORTS] = {cell->getPort(ID::A), cell->getPort(ID::B), cell->getPort(ID::Y)};
+ RTLIL::SigSpec port_taints[NUM_PORTS];
+
+ if (ports[A].size() != 1 || ports[B].size() != 1 || ports[Y].size() != 1)
+ log_cmd_error("Multi-bit signal found. Run `splitnets` first.\n");
+ for (unsigned int i = 0; i < NUM_PORTS; ++i)
+ port_taints[i] = get_corresponding_taint_signal(ports[i]);
+
+ if (opt_create_precise_model || opt_create_imprecise_model)
+ add_imprecise_GLIFT_logic_3(cell, port_taints[A], port_taints[B], port_taints[Y]);
+ else if (opt_create_instrumented_model) {
+ std::vector<RTLIL::SigSpec> taint_version;
+ int num_versions = 4;
+ auto select_width = log2(num_versions);
+ log_assert(exp2(select_width) == num_versions);
+
+ for (auto i = 1; i <= num_versions; ++i)
+ taint_version.emplace_back(RTLIL::SigSpec(module->addWire(stringf("%s_y%d", cell->name.c_str(), i), 1)));
+
+ for (auto i = 0; i < num_versions; ++i) {
+ switch(i) {
+ case 0: add_imprecise_GLIFT_logic_3(cell, port_taints[A], port_taints[B], taint_version[i]);
+ break;
+ case 1: add_imprecise_GLIFT_logic_4(port_taints[A], taint_version[i]);
+ break;
+ case 2: add_imprecise_GLIFT_logic_5(port_taints[B], taint_version[i]);
+ break;
+ case 3: add_imprecise_GLIFT_logic_6(taint_version[i]);
+ break;
+ default: log_assert(false);
+ }
+ }
+
+ RTLIL::SigSpec meta_mux_select(module->addWire(cell->name.str() + "_sel", select_width));
+ meta_mux_selects.push_back(make_pair(meta_mux_select, cell->type));
+ module->connect(meta_mux_select, module->Anyconst(cell->name.str() + "_hole", select_width, cell->get_src_attribute()));
+
+ std::vector<RTLIL::SigSpec> next_meta_mux_y_ports, meta_mux_y_ports(taint_version);
+ for (auto i = 0; meta_mux_y_ports.size() > 1; ++i) {
+ for (auto j = 0; j+1 < GetSize(meta_mux_y_ports); j += 2) {
+ next_meta_mux_y_ports.emplace_back(module->Mux(stringf("%s_mux_%d_%d", cell->name.c_str(), i, j), meta_mux_y_ports[j], meta_mux_y_ports[j+1], meta_mux_select[GetSize(meta_mux_select) - 1 - i]));
+ }
+ if (GetSize(meta_mux_y_ports) % 2 == 1)
+ next_meta_mux_y_ports.push_back(meta_mux_y_ports[GetSize(meta_mux_y_ports) - 1]);
+ meta_mux_y_ports.swap(next_meta_mux_y_ports);
+ next_meta_mux_y_ports.clear();
+ }
+ log_assert(meta_mux_y_ports.size() == 1);
+ module->connect(port_taints[Y], meta_mux_y_ports[0]);
+ }
+ else log_cmd_error("This is a bug (2).\n");
+
+ }
+ else if (cell->type.in(ID($_MUX_), ID($_NMUX_))) {
+ const unsigned int A = 0, B = 1, S = 2, Y = 3;
+ const unsigned int NUM_PORTS = 4;
+ RTLIL::SigSpec ports[NUM_PORTS] = {cell->getPort(ID::A), cell->getPort(ID::B), cell->getPort(ID::S), cell->getPort(ID::Y)};
+ RTLIL::SigSpec port_taints[NUM_PORTS];
+
+ if (ports[A].size() != 1 || ports[B].size() != 1 || ports[S].size() != 1 || ports[Y].size() != 1)
+ log_cmd_error("Multi-bit signal found. Run `splitnets` first.\n");
+ for (unsigned int i = 0; i < NUM_PORTS; ++i)
+ port_taints[i] = get_corresponding_taint_signal(ports[i]);
+
+ add_precise_GLIFT_mux(cell, ports[A], port_taints[A], ports[B], port_taints[B], ports[S], port_taints[S], port_taints[Y]);
+ }
+ else if (cell->type.in(ID($_NOT_))) {
+ const unsigned int A = 0, Y = 1;
+ const unsigned int NUM_PORTS = 2;
+ RTLIL::SigSpec ports[NUM_PORTS] = {cell->getPort(ID::A), cell->getPort(ID::Y)};
+ RTLIL::SigSpec port_taints[NUM_PORTS];
+
+ if (ports[A].size() != 1 || ports[Y].size() != 1)
+ log_cmd_error("Multi-bit signal found. Run `splitnets` first.\n");
+ for (unsigned int i = 0; i < NUM_PORTS; ++i)
+ port_taints[i] = get_corresponding_taint_signal(ports[i]);
+
+ if (cell->type == ID($_NOT_)) {
+ module->connect(port_taints[Y], port_taints[A]);
+ }
+ else log_cmd_error("This is a bug (3).\n");
+ }
+ else if (module->design->module(cell->type) != nullptr) {
+ //User cell type
+ //This function is called on modules according to topological order, so we do not need to
+ //recurse to GLIFT model the child module. However, we need to augment the ports list
+ //with taint signals and connect the new ports to the corresponding taint signals.
+ RTLIL::Module *cell_module_def = module->design->module(cell->type);
+ dict<RTLIL::IdString, RTLIL::SigSpec> orig_ports = cell->connections();
+ log("Adding cell %s\n", cell_module_def->name.c_str());
+ for (auto &it : orig_ports) {
+ RTLIL::SigSpec port = it.second;
+ RTLIL::SigSpec port_taint = get_corresponding_taint_signal(port);
+
+ log_assert(port_taint.is_wire());
+ log_assert(std::find(cell_module_def->ports.begin(), cell_module_def->ports.end(), port_taint.as_wire()->name) != cell_module_def->ports.end());
+ cell->setPort(port_taint.as_wire()->name, port_taint);
+ }
+ }
+ else log_cmd_error("This is a bug (4).\n");
+ } //end foreach cell in cells
+
+ for (auto &conn : connections) {
+ RTLIL::SigSpec first = get_corresponding_taint_signal(conn.first);
+ RTLIL::SigSpec second = get_corresponding_taint_signal(conn.second);
+
+ module->connect(first, second);
+
+ if(conn.second.is_wire() && conn.second.as_wire()->port_input)
+ second.as_wire()->port_input = true;
+ if(conn.first.is_wire() && conn.first.as_wire()->port_output)
+ new_taint_outputs.push_back(first.as_wire());
+ } //end foreach conn in connections
+
+ //Create a rough model of area by summing the (potentially simplified) "weight" score of each meta-mux select:
+ if (!opt_nocostmodel) {
+ std::vector<RTLIL::SigSpec> meta_mux_select_sums;
+ std::vector<RTLIL::SigSpec> meta_mux_select_sums_buf;
+ for (auto &it : meta_mux_selects) {
+ meta_mux_select_sums.emplace_back(score_metamux_select(it.first, it.second));
+ }
+ for (unsigned int i = 0; meta_mux_select_sums.size() > 1; ) {
+ meta_mux_select_sums_buf.clear();
+ for (i = 0; i + 1 < meta_mux_select_sums.size(); i += 2) {
+ meta_mux_select_sums_buf.push_back(module->Add(meta_mux_select_sums[i].as_wire()->name.str() + "_add", meta_mux_select_sums[i], meta_mux_select_sums[i+1], false));
+ }
+ if (meta_mux_select_sums.size() % 2 == 1)
+ meta_mux_select_sums_buf.push_back(meta_mux_select_sums[meta_mux_select_sums.size()-1]);
+ meta_mux_select_sums.swap(meta_mux_select_sums_buf);
+ }
+ if (meta_mux_select_sums.size() > 0) {
+ meta_mux_select_sums[0].as_wire()->set_bool_attribute("\\minimize");
+ meta_mux_select_sums[0].as_wire()->set_bool_attribute("\\keep");
+ module->rename(meta_mux_select_sums[0].as_wire(), cost_model_wire_name);
+ }
+ }
+
+ //Mark new module outputs:
+ for (auto &port_name : module->ports) {
+ RTLIL::Wire *port = module->wire(port_name);
+ log_assert(port != nullptr);
+ if (is_top_module && port->port_output && !opt_keepoutputs)
+ port->port_output = false;
+ }
+ for (auto &output : new_taint_outputs)
+ output->port_output = true;
+ module->fixup_ports(); //we have some new taint signals in the module interface
+ module->set_bool_attribute(glift_attribute_name, true);
+ }
+
+public:
+ GliftWorker(RTLIL::Module *_module, bool _is_top_module, bool _opt_create_precise_model, bool _opt_create_imprecise_model, bool _opt_create_instrumented_model, bool _opt_taintconstants, bool _opt_keepoutputs, bool _opt_simplecostmodel, bool _opt_nocostmodel, bool _opt_instrumentmore) {
+ module = _module;
+ is_top_module = _is_top_module;
+ opt_create_precise_model = _opt_create_precise_model;
+ opt_create_imprecise_model = _opt_create_imprecise_model;
+ opt_create_instrumented_model = _opt_create_instrumented_model;
+ opt_taintconstants = _opt_taintconstants;
+ opt_keepoutputs = _opt_keepoutputs;
+ opt_simplecostmodel = _opt_simplecostmodel;
+ opt_nocostmodel = _opt_nocostmodel;
+ opt_instrumentmore = _opt_instrumentmore;
+
+ create_glift_logic();
+ }
+};
+
+struct GliftPass : public Pass {
+ GliftPass() : Pass("glift", "create GLIFT models and optimization problems") {}
+
+ void help() override
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" glift <command> [options] [selection]\n");
+ log("\n");
+ log("Augments the current or specified module with gate-level information flow tracking\n");
+ log("(GLIFT) logic using the \"constructive mapping\" approach. Also can set up QBF-SAT\n");
+ log("optimization problems in order to optimize GLIFT models or trade off precision and\n");
+ log("complexity.\n");
+ log("\n");
+ log("\n");
+ log("Commands:\n");
+ log("\n");
+ log(" -create-precise-model\n");
+ log(" Replaces the current or specified module with one that has corresponding \"taint\"\n");
+ log(" inputs, outputs, and internal nets along with precise taint tracking logic.\n");
+ log(" For example, precise taint tracking logic for an AND gate is:\n");
+ log("\n");
+ log(" y_t = a & b_t | b & a_t | a_t & b_t\n");
+ log("\n");
+ log("\n");
+ log(" -create-imprecise-model\n");
+ log(" Replaces the current or specified module with one that has corresponding \"taint\"\n");
+ log(" inputs, outputs, and internal nets along with imprecise \"All OR\" taint tracking\n");
+ log(" logic:\n");
+ log("\n");
+ log(" y_t = a_t | b_t\n");
+ log("\n");
+ log("\n");
+ log(" -create-instrumented-model\n");
+ log(" Replaces the current or specified module with one that has corresponding \"taint\"\n");
+ log(" inputs, outputs, and internal nets along with 4 varying-precision versions of taint\n");
+ log(" tracking logic. Which version of taint tracking logic is used for a given gate is\n");
+ log(" determined by a MUX selected by an $anyconst cell. By default, unless the\n");
+ log(" `-no-cost-model` option is provided, an additional wire named `__glift_weight` with\n");
+ log(" the `keep` and `minimize` attributes is added to the module along with pmuxes and\n");
+ log(" adders to calculate a rough estimate of the number of logic gates in the GLIFT model\n");
+ log(" given an assignment for the $anyconst cells. The four versions of taint tracking logic\n");
+ log(" for an AND gate are:");
+ log("\n");
+ log(" y_t = a & b_t | b & a_t | a_t & b_t (like `-create-precise-model`)\n");
+ log(" y_t = a_t | a & b_t\n");
+ log(" y_t = b_t | b & a_t\n");
+ log(" y_t = a_t | b_t (like `-create-imprecise-model`)\n");
+ log("\n");
+ log("\n");
+ log("Options:\n");
+ log("\n");
+ log(" -taint-constants\n");
+ log(" Constant values in the design are labeled as tainted.\n");
+ log(" (default: label constants as un-tainted)\n");
+ log("\n");
+ log(" -keep-outputs\n");
+ log(" Do not remove module outputs. Taint tracking outputs will appear in the module ports\n");
+ log(" alongside the orignal outputs.\n");
+ log(" (default: original module outputs are removed)\n");
+ log("\n");
+ log(" -simple-cost-model\n");
+ log(" Do not model logic area. Instead model the number of non-zero assignments to $anyconsts.\n");
+ log(" Taint tracking logic versions vary in their size, but all reduced-precision versions are\n");
+ log(" significantly smaller than the fully-precise version. A non-zero $anyconst assignment means\n");
+ log(" that reduced-precision taint tracking logic was chosen for some gate.\n");
+ log(" Only applicable in combination with `-create-instrumented-model`.\n");
+ log(" (default: use a complex model and give that wire the \"keep\" and \"minimize\" attributes)\n");
+ log("\n");
+ log(" -no-cost-model\n");
+ log(" Do not model taint tracking logic area and do not create a `__glift_weight` wire.\n");
+ log(" Only applicable in combination with `-create-instrumented-model`.\n");
+ log(" (default: model area and give that wire the \"keep\" and \"minimize\" attributes)\n");
+ log("\n");
+ log(" -instrument-more\n");
+ log(" Allow choice from more versions of (even simpler) taint tracking logic. A total\n");
+ log(" of 8 versions of taint tracking logic will be added per gate, including the 4\n");
+ log(" versions from `-create-instrumented-model` and these additional versions:\n");
+ log("\n");
+ log(" y_t = a_t\n");
+ log(" y_t = b_t\n");
+ log(" y_t = 1\n");
+ log(" y_t = 0\n");
+ log("\n");
+ log(" Only applicable in combination with `-create-instrumented-model`.\n");
+ log(" (default: do not add more versions of taint tracking logic.\n");
+ log("\n");
+ }
+
+ void execute(std::vector<std::string> args, RTLIL::Design *design) override
+ {
+ bool opt_create_precise_model = false, opt_create_imprecise_model = false, opt_create_instrumented_model = false;
+ bool opt_taintconstants = false, opt_keepoutputs = false, opt_simplecostmodel = false, opt_nocostmodel = false;
+ bool opt_instrumentmore = false;
+ log_header(design, "Executing GLIFT pass (creating and manipulating GLIFT models).\n");
+ std::vector<std::string>::size_type argidx;
+
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ if (args[argidx] == "-create-precise-model") {
+ opt_create_precise_model = true;
+ continue;
+ }
+ if (args[argidx] == "-create-imprecise-model") {
+ opt_create_imprecise_model = true;
+ continue;
+ }
+ if (args[argidx] == "-create-instrumented-model") {
+ opt_create_instrumented_model = true;
+ continue;
+ }
+ if (args[argidx] == "-taint-constants") {
+ opt_taintconstants = true;
+ continue;
+ }
+ if (args[argidx] == "-keep-outputs") {
+ opt_keepoutputs = true;
+ continue;
+ }
+ if (args[argidx] == "-simple-cost-model") {
+ opt_simplecostmodel = true;
+ continue;
+ }
+ if (args[argidx] == "-no-cost-model") {
+ opt_nocostmodel = true;
+ continue;
+ }
+ if (args[argidx] == "-instrument-more") {
+ opt_instrumentmore = true;
+ continue;
+ }
+ break;
+ }
+ if(!opt_create_precise_model && !opt_create_imprecise_model && !opt_create_instrumented_model)
+ log_cmd_error("No command provided. See help for usage.\n");
+ if(static_cast<int>(opt_create_precise_model) + static_cast<int>(opt_create_imprecise_model) + static_cast<int>(opt_create_instrumented_model) != 1)
+ log_cmd_error("Only one command may be specified. See help for usage.\n");
+ if(opt_simplecostmodel && opt_nocostmodel)
+ log_cmd_error("Only one of `-simple-cost-model` and `-no-cost-model` may be specified. See help for usage.\n");
+ if((opt_simplecostmodel || opt_nocostmodel) && !opt_create_instrumented_model)
+ log_cmd_error("Options `-simple-cost-model` and `-no-cost-model` may only be used with `-create-instrumented-model`. See help for usage.\n");
+ extra_args(args, argidx, design);
+
+ if (GetSize(design->selected_modules()) == 0)
+ log_cmd_error("Can't operate on an empty selection!\n");
+
+ TopoSort<RTLIL::Module*, IdString::compare_ptr_by_name<RTLIL::Module>> topo_modules; //cribbed from passes/techmap/flatten.cc
+ auto worklist = design->selected_modules();
+ pool<RTLIL::IdString> non_top_modules;
+ while (!worklist.empty()) {
+ RTLIL::Module *module = *(worklist.begin());
+ worklist.erase(worklist.begin());
+ topo_modules.node(module);
+
+ for (auto cell : module->selected_cells()) {
+ RTLIL::Module *tpl = design->module(cell->type);
+ if (tpl != nullptr) {
+ if (topo_modules.database.count(tpl) == 0)
+ worklist.push_back(tpl);
+ topo_modules.edge(tpl, module);
+ non_top_modules.insert(cell->type);
+ }
+ }
+ }
+
+ if (!topo_modules.sort())
+ log_cmd_error("Cannot handle recursive module instantiations.\n");
+
+ for (auto i = 0; i < GetSize(topo_modules.sorted); ++i) {
+ RTLIL::Module *module = topo_modules.sorted[i];
+ GliftWorker(module, !non_top_modules[module->name], opt_create_precise_model, opt_create_imprecise_model, opt_create_instrumented_model, opt_taintconstants, opt_keepoutputs, opt_simplecostmodel, opt_nocostmodel, opt_instrumentmore);
+ }
+ }
+} GliftPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/cmds/stat.cc b/passes/cmds/stat.cc
index 14a27ed99..fffdda48e 100644
--- a/passes/cmds/stat.cc
+++ b/passes/cmds/stat.cc
@@ -117,6 +117,10 @@ struct statdata_t
}
else if (cell_type.in(ID($mux), ID($pmux)))
cell_type = stringf("%s_%d", cell_type.c_str(), GetSize(cell->getPort(ID::Y)));
+ else if (cell_type == ID($bmux))
+ cell_type = stringf("%s_%d_%d", cell_type.c_str(), GetSize(cell->getPort(ID::Y)), GetSize(cell->getPort(ID::S)));
+ else if (cell_type == ID($demux))
+ cell_type = stringf("%s_%d_%d", cell_type.c_str(), GetSize(cell->getPort(ID::A)), GetSize(cell->getPort(ID::S)));
else if (cell_type.in(
ID($sr), ID($ff), ID($dff), ID($dffe), ID($dffsr), ID($dffsre),
ID($adff), ID($adffe), ID($sdff), ID($sdffe), ID($sdffce),
diff --git a/passes/memory/memory_bram.cc b/passes/memory/memory_bram.cc
index fed9d60c0..b1f45d5fc 100644
--- a/passes/memory/memory_bram.cc
+++ b/passes/memory/memory_bram.cc
@@ -644,22 +644,6 @@ grow_read_ports:;
log(" Bram port %c%d.%d has incompatible clock polarity.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
goto skip_bram_rport;
}
- if (port.en != State::S1 && pi.enable == 0) {
- log(" Bram port %c%d.%d has no read enable input.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
- goto skip_bram_rport;
- }
- if (port.arst != State::S0) {
- log(" Bram port %c%d.%d has no async reset input.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
- goto skip_bram_rport;
- }
- if (port.srst != State::S0) {
- log(" Bram port %c%d.%d has no sync reset input.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
- goto skip_bram_rport;
- }
- if (!port.init_value.is_fully_undef()) {
- log(" Bram port %c%d.%d has no initial value support.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
- goto skip_bram_rport;
- }
if (non_transp && read_transp.count(pi.transp) && read_transp.at(pi.transp)) {
log(" Bram port %c%d.%d has incompatible read transparency.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
goto skip_bram_rport;
@@ -794,10 +778,18 @@ grow_read_ports:;
// Apply make_outreg and make_transp where necessary.
for (auto &pi : portinfos) {
- if (pi.make_outreg)
+ if (pi.mapped_port == -1 || pi.wrmode)
+ continue;
+ auto &port = mem.rd_ports[pi.mapped_port];
+ if (pi.make_outreg) {
mem.extract_rdff(pi.mapped_port, initvals);
+ } else if (port.clk_enable) {
+ if (!pi.enable && port.en != State::S1)
+ mem.emulate_rden(pi.mapped_port, initvals);
+ else
+ mem.emulate_reset(pi.mapped_port, true, true, true, initvals);
+ }
if (pi.make_transp) {
- auto &port = mem.rd_ports[pi.mapped_port];
for (int i = 0; i < GetSize(mem.wr_ports); i++)
if (port.transparency_mask[i])
mem.emulate_transparency(i, pi.mapped_port, initvals);
diff --git a/passes/opt/opt_dff.cc b/passes/opt/opt_dff.cc
index 98b66dee3..73d674c8d 100644
--- a/passes/opt/opt_dff.cc
+++ b/passes/opt/opt_dff.cc
@@ -58,13 +58,10 @@ struct OptDffWorker
typedef std::pair<RTLIL::SigBit, bool> ctrl_t;
typedef std::set<ctrl_t> ctrls_t;
- ModWalker modwalker;
- QuickConeSat qcsat;
-
// Used as a queue.
std::vector<Cell *> dff_cells;
- OptDffWorker(const OptDffOptions &opt, Module *mod) : opt(opt), module(mod), sigmap(mod), initvals(&sigmap, mod), modwalker(module->design, module), qcsat(modwalker) {
+ OptDffWorker(const OptDffOptions &opt, Module *mod) : opt(opt), module(mod), sigmap(mod), initvals(&sigmap, mod) {
// Gathering two kinds of information here for every sigmapped SigBit:
//
// - bitusers: how many users it has (muxes will only be merged into FFs if this is 1, making the FF the only user)
@@ -569,100 +566,6 @@ struct OptDffWorker
changed = true;
}
- // Now check if any bit can be replaced by a constant.
- pool<int> removed_sigbits;
- for (int i = 0; i < ff.width; i++) {
- State val = ff.val_init[i];
- if (ff.has_arst)
- val = combine_const(val, ff.val_arst[i]);
- if (ff.has_srst)
- val = combine_const(val, ff.val_srst[i]);
- if (ff.has_sr) {
- if (ff.sig_clr[i] != (ff.pol_clr ? State::S0 : State::S1))
- val = combine_const(val, State::S0);
- if (ff.sig_set[i] != (ff.pol_set ? State::S0 : State::S1))
- val = combine_const(val, State::S1);
- }
- if (val == State::Sm)
- continue;
- if (ff.has_clk || ff.has_gclk) {
- if (!ff.sig_d[i].wire) {
- val = combine_const(val, ff.sig_d[i].data);
- if (val == State::Sm)
- continue;
- } else {
- if (!opt.sat)
- continue;
- // For each register bit, try to prove that it cannot change from the initial value. If so, remove it
- if (!modwalker.has_drivers(ff.sig_d.extract(i)))
- continue;
- if (val != State::S0 && val != State::S1)
- continue;
-
- int init_sat_pi = qcsat.importSigBit(val);
- int q_sat_pi = qcsat.importSigBit(ff.sig_q[i]);
- int d_sat_pi = qcsat.importSigBit(ff.sig_d[i]);
-
- qcsat.prepare();
-
- // Try to find out whether the register bit can change under some circumstances
- bool counter_example_found = qcsat.ez->solve(qcsat.ez->IFF(q_sat_pi, init_sat_pi), qcsat.ez->NOT(qcsat.ez->IFF(d_sat_pi, init_sat_pi)));
-
- // If the register bit cannot change, we can replace it with a constant
- if (counter_example_found)
- continue;
- }
- }
- if (ff.has_aload) {
- if (!ff.sig_ad[i].wire) {
- val = combine_const(val, ff.sig_ad[i].data);
- if (val == State::Sm)
- continue;
- } else {
- if (!opt.sat)
- continue;
- // For each register bit, try to prove that it cannot change from the initial value. If so, remove it
- if (!modwalker.has_drivers(ff.sig_ad.extract(i)))
- continue;
- if (val != State::S0 && val != State::S1)
- continue;
-
- int init_sat_pi = qcsat.importSigBit(val);
- int q_sat_pi = qcsat.importSigBit(ff.sig_q[i]);
- int d_sat_pi = qcsat.importSigBit(ff.sig_ad[i]);
-
- qcsat.prepare();
-
- // Try to find out whether the register bit can change under some circumstances
- bool counter_example_found = qcsat.ez->solve(qcsat.ez->IFF(q_sat_pi, init_sat_pi), qcsat.ez->NOT(qcsat.ez->IFF(d_sat_pi, init_sat_pi)));
-
- // If the register bit cannot change, we can replace it with a constant
- if (counter_example_found)
- continue;
- }
- }
- log("Setting constant %d-bit at position %d on %s (%s) from module %s.\n", val ? 1 : 0,
- i, log_id(cell), log_id(cell->type), log_id(module));
-
- initvals.remove_init(ff.sig_q[i]);
- module->connect(ff.sig_q[i], val);
- removed_sigbits.insert(i);
- }
- if (!removed_sigbits.empty()) {
- std::vector<int> keep_bits;
- for (int i = 0; i < ff.width; i++)
- if (!removed_sigbits.count(i))
- keep_bits.push_back(i);
- if (keep_bits.empty()) {
- module->remove(cell);
- did_something = true;
- continue;
- }
- ff = ff.slice(keep_bits);
- ff.cell = cell;
- changed = true;
- }
-
// The cell has been simplified as much as possible already. Now try to spice it up with enables / sync resets.
if (ff.has_clk) {
if (!ff.has_arst && !ff.has_sr && (!ff.has_srst || !ff.has_ce || ff.ce_over_srst) && !opt.nosdff) {
@@ -818,6 +721,115 @@ struct OptDffWorker
}
return did_something;
}
+
+ bool run_constbits() {
+ ModWalker modwalker(module->design, module);
+ QuickConeSat qcsat(modwalker);
+
+ // Run as a separate sub-pass, so that we don't mutate (non-FF) cells under ModWalker.
+ bool did_something = false;
+ for (auto cell : module->selected_cells()) {
+ if (!RTLIL::builtin_ff_cell_types().count(cell->type))
+ continue;
+ FfData ff(&initvals, cell);
+
+ // Now check if any bit can be replaced by a constant.
+ pool<int> removed_sigbits;
+ for (int i = 0; i < ff.width; i++) {
+ State val = ff.val_init[i];
+ if (ff.has_arst)
+ val = combine_const(val, ff.val_arst[i]);
+ if (ff.has_srst)
+ val = combine_const(val, ff.val_srst[i]);
+ if (ff.has_sr) {
+ if (ff.sig_clr[i] != (ff.pol_clr ? State::S0 : State::S1))
+ val = combine_const(val, State::S0);
+ if (ff.sig_set[i] != (ff.pol_set ? State::S0 : State::S1))
+ val = combine_const(val, State::S1);
+ }
+ if (val == State::Sm)
+ continue;
+ if (ff.has_clk || ff.has_gclk) {
+ if (!ff.sig_d[i].wire) {
+ val = combine_const(val, ff.sig_d[i].data);
+ if (val == State::Sm)
+ continue;
+ } else {
+ if (!opt.sat)
+ continue;
+ // For each register bit, try to prove that it cannot change from the initial value. If so, remove it
+ if (!modwalker.has_drivers(ff.sig_d.extract(i)))
+ continue;
+ if (val != State::S0 && val != State::S1)
+ continue;
+
+ int init_sat_pi = qcsat.importSigBit(val);
+ int q_sat_pi = qcsat.importSigBit(ff.sig_q[i]);
+ int d_sat_pi = qcsat.importSigBit(ff.sig_d[i]);
+
+ qcsat.prepare();
+
+ // Try to find out whether the register bit can change under some circumstances
+ bool counter_example_found = qcsat.ez->solve(qcsat.ez->IFF(q_sat_pi, init_sat_pi), qcsat.ez->NOT(qcsat.ez->IFF(d_sat_pi, init_sat_pi)));
+
+ // If the register bit cannot change, we can replace it with a constant
+ if (counter_example_found)
+ continue;
+ }
+ }
+ if (ff.has_aload) {
+ if (!ff.sig_ad[i].wire) {
+ val = combine_const(val, ff.sig_ad[i].data);
+ if (val == State::Sm)
+ continue;
+ } else {
+ if (!opt.sat)
+ continue;
+ // For each register bit, try to prove that it cannot change from the initial value. If so, remove it
+ if (!modwalker.has_drivers(ff.sig_ad.extract(i)))
+ continue;
+ if (val != State::S0 && val != State::S1)
+ continue;
+
+ int init_sat_pi = qcsat.importSigBit(val);
+ int q_sat_pi = qcsat.importSigBit(ff.sig_q[i]);
+ int d_sat_pi = qcsat.importSigBit(ff.sig_ad[i]);
+
+ qcsat.prepare();
+
+ // Try to find out whether the register bit can change under some circumstances
+ bool counter_example_found = qcsat.ez->solve(qcsat.ez->IFF(q_sat_pi, init_sat_pi), qcsat.ez->NOT(qcsat.ez->IFF(d_sat_pi, init_sat_pi)));
+
+ // If the register bit cannot change, we can replace it with a constant
+ if (counter_example_found)
+ continue;
+ }
+ }
+ log("Setting constant %d-bit at position %d on %s (%s) from module %s.\n", val ? 1 : 0,
+ i, log_id(cell), log_id(cell->type), log_id(module));
+
+ initvals.remove_init(ff.sig_q[i]);
+ module->connect(ff.sig_q[i], val);
+ removed_sigbits.insert(i);
+ }
+ if (!removed_sigbits.empty()) {
+ std::vector<int> keep_bits;
+ for (int i = 0; i < ff.width; i++)
+ if (!removed_sigbits.count(i))
+ keep_bits.push_back(i);
+ if (keep_bits.empty()) {
+ module->remove(cell);
+ did_something = true;
+ continue;
+ }
+ ff = ff.slice(keep_bits);
+ ff.cell = cell;
+ ff.emit();
+ did_something = true;
+ }
+ }
+ return did_something;
+ }
};
struct OptDffPass : public Pass {
@@ -894,6 +906,8 @@ struct OptDffPass : public Pass {
OptDffWorker worker(opt, mod);
if (worker.run())
did_something = true;
+ if (worker.run_constbits())
+ did_something = true;
}
if (did_something)
diff --git a/passes/opt/opt_reduce.cc b/passes/opt/opt_reduce.cc
index b558f547e..1a7c93fbd 100644
--- a/passes/opt/opt_reduce.cc
+++ b/passes/opt/opt_reduce.cc
@@ -100,7 +100,7 @@ struct OptReduceWorker
return;
}
- void opt_mux(RTLIL::Cell *cell)
+ void opt_pmux(RTLIL::Cell *cell)
{
RTLIL::SigSpec sig_a = assign_map(cell->getPort(ID::A));
RTLIL::SigSpec sig_b = assign_map(cell->getPort(ID::B));
@@ -141,20 +141,20 @@ struct OptReduceWorker
handled_sig.insert(this_b);
}
- if (new_sig_s.size() != sig_s.size()) {
- log(" New ctrl vector for %s cell %s: %s\n", cell->type.c_str(), cell->name.c_str(), log_signal(new_sig_s));
- did_something = true;
- total_count++;
- }
-
if (new_sig_s.size() == 0)
{
- module->connect(RTLIL::SigSig(cell->getPort(ID::Y), cell->getPort(ID::A)));
+ module->connect(cell->getPort(ID::Y), cell->getPort(ID::A));
assign_map.add(cell->getPort(ID::Y), cell->getPort(ID::A));
module->remove(cell);
+ did_something = true;
+ total_count++;
+ return;
}
- else
- {
+
+ if (new_sig_s.size() != sig_s.size() || (new_sig_s.size() == 1 && cell->type == ID($pmux))) {
+ log(" New ctrl vector for %s cell %s: %s\n", cell->type.c_str(), cell->name.c_str(), log_signal(new_sig_s));
+ did_something = true;
+ total_count++;
cell->setPort(ID::B, new_sig_b);
cell->setPort(ID::S, new_sig_s);
if (new_sig_s.size() > 1) {
@@ -166,81 +166,347 @@ struct OptReduceWorker
}
}
- void opt_mux_bits(RTLIL::Cell *cell)
+ void opt_bmux(RTLIL::Cell *cell)
{
- std::vector<RTLIL::SigBit> sig_a = assign_map(cell->getPort(ID::A)).to_sigbit_vector();
- std::vector<RTLIL::SigBit> sig_b = assign_map(cell->getPort(ID::B)).to_sigbit_vector();
- std::vector<RTLIL::SigBit> sig_y = assign_map(cell->getPort(ID::Y)).to_sigbit_vector();
+ RTLIL::SigSpec sig_a = assign_map(cell->getPort(ID::A));
+ RTLIL::SigSpec sig_s = assign_map(cell->getPort(ID::S));
+ int width = cell->getParam(ID::WIDTH).as_int();
+
+ RTLIL::SigSpec new_sig_a, new_sig_s;
+ dict<RTLIL::SigBit, int> handled_bits;
+
+ // 0 and up: index of new_sig_s bit
+ // -1: const 0
+ // -2: const 1
+ std::vector<int> swizzle;
+
+ for (int i = 0; i < sig_s.size(); i++)
+ {
+ SigBit bit = sig_s[i];
+ if (bit == State::S0) {
+ swizzle.push_back(-1);
+ } else if (bit == State::S1) {
+ swizzle.push_back(-2);
+ } else {
+ auto it = handled_bits.find(bit);
+ if (it == handled_bits.end()) {
+ int new_idx = GetSize(new_sig_s);
+ new_sig_s.append(bit);
+ handled_bits[bit] = new_idx;
+ swizzle.push_back(new_idx);
+ } else {
+ swizzle.push_back(it->second);
+ }
+ }
+ }
+
+ for (int i = 0; i < (1 << GetSize(new_sig_s)); i++) {
+ int idx = 0;
+ for (int j = 0; j < GetSize(sig_s); j++) {
+ if (swizzle[j] == -1) {
+ // const 0.
+ } else if (swizzle[j] == -2) {
+ // const 1.
+ idx |= 1 << j;
+ } else {
+ if (i & 1 << swizzle[j])
+ idx |= 1 << j;
+ }
+ }
+ new_sig_a.append(sig_a.extract(idx * width, width));
+ }
+
+ if (new_sig_s.size() == 0)
+ {
+ module->connect(cell->getPort(ID::Y), new_sig_a);
+ assign_map.add(cell->getPort(ID::Y), new_sig_a);
+ module->remove(cell);
+ did_something = true;
+ total_count++;
+ return;
+ }
+
+ if (new_sig_s.size() == 1)
+ {
+ cell->type = ID($mux);
+ cell->setPort(ID::A, new_sig_a.extract(0, width));
+ cell->setPort(ID::B, new_sig_a.extract(width, width));
+ cell->setPort(ID::S, new_sig_s);
+ cell->parameters.erase(ID::S_WIDTH);
+ did_something = true;
+ total_count++;
+ return;
+ }
+
+ if (new_sig_s.size() != sig_s.size()) {
+ log(" New ctrl vector for %s cell %s: %s\n", cell->type.c_str(), cell->name.c_str(), log_signal(new_sig_s));
+ did_something = true;
+ total_count++;
+ cell->setPort(ID::A, new_sig_a);
+ cell->setPort(ID::S, new_sig_s);
+ cell->parameters[ID::S_WIDTH] = RTLIL::Const(new_sig_s.size());
+ }
+ }
+
+ void opt_demux(RTLIL::Cell *cell)
+ {
+ RTLIL::SigSpec sig_y = assign_map(cell->getPort(ID::Y));
+ RTLIL::SigSpec sig_s = assign_map(cell->getPort(ID::S));
+ int width = cell->getParam(ID::WIDTH).as_int();
+
+ RTLIL::SigSpec new_sig_y, new_sig_s;
+ dict<RTLIL::SigBit, int> handled_bits;
+
+ // 0 and up: index of new_sig_s bit
+ // -1: const 0
+ // -2: const 1
+ std::vector<int> swizzle;
+
+ for (int i = 0; i < sig_s.size(); i++)
+ {
+ SigBit bit = sig_s[i];
+ if (bit == State::S0) {
+ swizzle.push_back(-1);
+ } else if (bit == State::S1) {
+ swizzle.push_back(-2);
+ } else {
+ auto it = handled_bits.find(bit);
+ if (it == handled_bits.end()) {
+ int new_idx = GetSize(new_sig_s);
+ new_sig_s.append(bit);
+ handled_bits[bit] = new_idx;
+ swizzle.push_back(new_idx);
+ } else {
+ swizzle.push_back(it->second);
+ }
+ }
+ }
+
+ pool<int> nonzero_idx;
+
+ for (int i = 0; i < (1 << GetSize(new_sig_s)); i++) {
+ int idx = 0;
+ for (int j = 0; j < GetSize(sig_s); j++) {
+ if (swizzle[j] == -1) {
+ // const 0.
+ } else if (swizzle[j] == -2) {
+ // const 1.
+ idx |= 1 << j;
+ } else {
+ if (i & 1 << swizzle[j])
+ idx |= 1 << j;
+ }
+ }
+ log_assert(!nonzero_idx.count(idx));
+ nonzero_idx.insert(idx);
+ new_sig_y.append(sig_y.extract(idx * width, width));
+ }
+
+ if (new_sig_s.size() == sig_s.size() && sig_s.size() > 0)
+ return;
+
+ log(" New ctrl vector for %s cell %s: %s\n", cell->type.c_str(), cell->name.c_str(), log_signal(new_sig_s));
+ did_something = true;
+ total_count++;
+
+ for (int i = 0; i < (1 << GetSize(sig_s)); i++) {
+ if (!nonzero_idx.count(i)) {
+ SigSpec slice = sig_y.extract(i * width, width);
+ module->connect(slice, Const(State::S0, width));
+ assign_map.add(slice, Const(State::S0, width));
+ }
+ }
+
+ if (new_sig_s.size() == 0)
+ {
+ module->connect(new_sig_y, cell->getPort(ID::A));
+ assign_map.add(new_sig_y, cell->getPort(ID::A));
+ module->remove(cell);
+ }
+ else
+ {
+ cell->setPort(ID::S, new_sig_s);
+ cell->setPort(ID::Y, new_sig_y);
+ cell->parameters[ID::S_WIDTH] = RTLIL::Const(new_sig_s.size());
+ }
+ }
+
+ bool opt_mux_bits(RTLIL::Cell *cell)
+ {
+ SigSpec sig_a = assign_map(cell->getPort(ID::A));
+ SigSpec sig_b;
+ SigSpec sig_y = assign_map(cell->getPort(ID::Y));
+ int width = GetSize(sig_y);
+
+ if (cell->type != ID($bmux))
+ sig_b = assign_map(cell->getPort(ID::B));
- std::vector<RTLIL::SigBit> new_sig_y;
RTLIL::SigSig old_sig_conn;
- std::vector<std::vector<RTLIL::SigBit>> consolidated_in_tuples;
- std::map<std::vector<RTLIL::SigBit>, RTLIL::SigBit> consolidated_in_tuples_map;
+ dict<SigSpec, SigBit> consolidated_in_tuples;
+ std::vector<int> swizzle;
- for (int i = 0; i < int(sig_y.size()); i++)
+ for (int i = 0; i < width; i++)
{
- std::vector<RTLIL::SigBit> in_tuple;
+ SigSpec in_tuple;
bool all_tuple_bits_same = true;
- in_tuple.push_back(sig_a.at(i));
- for (int j = i; j < int(sig_b.size()); j += int(sig_a.size())) {
- if (sig_b.at(j) != sig_a.at(i))
+ in_tuple.append(sig_a[i]);
+ for (int j = i; j < GetSize(sig_a); j += width) {
+ in_tuple.append(sig_a[j]);
+ if (sig_a[j] != in_tuple[0])
+ all_tuple_bits_same = false;
+ }
+ for (int j = i; j < GetSize(sig_b); j += width) {
+ in_tuple.append(sig_b[j]);
+ if (sig_b[j] != in_tuple[0])
all_tuple_bits_same = false;
- in_tuple.push_back(sig_b.at(j));
}
if (all_tuple_bits_same)
{
- old_sig_conn.first.append(sig_y.at(i));
- old_sig_conn.second.append(sig_a.at(i));
+ old_sig_conn.first.append(sig_y[i]);
+ old_sig_conn.second.append(sig_a[i]);
+ continue;
}
- else if (consolidated_in_tuples_map.count(in_tuple))
+
+ auto it = consolidated_in_tuples.find(in_tuple);
+ if (it == consolidated_in_tuples.end())
{
- old_sig_conn.first.append(sig_y.at(i));
- old_sig_conn.second.append(consolidated_in_tuples_map.at(in_tuple));
+ consolidated_in_tuples[in_tuple] = sig_y[i];
+ swizzle.push_back(i);
}
else
{
- consolidated_in_tuples_map[in_tuple] = sig_y.at(i);
- consolidated_in_tuples.push_back(in_tuple);
- new_sig_y.push_back(sig_y.at(i));
+ old_sig_conn.first.append(sig_y[i]);
+ old_sig_conn.second.append(it->second);
}
}
- if (new_sig_y.size() != sig_y.size())
+ if (GetSize(swizzle) != width)
{
log(" Consolidated identical input bits for %s cell %s:\n", cell->type.c_str(), cell->name.c_str());
- log(" Old ports: A=%s, B=%s, Y=%s\n", log_signal(cell->getPort(ID::A)),
- log_signal(cell->getPort(ID::B)), log_signal(cell->getPort(ID::Y)));
-
- cell->setPort(ID::A, RTLIL::SigSpec());
- for (auto &in_tuple : consolidated_in_tuples) {
- RTLIL::SigSpec new_a = cell->getPort(ID::A);
- new_a.append(in_tuple.at(0));
- cell->setPort(ID::A, new_a);
+ if (cell->type != ID($bmux)) {
+ log(" Old ports: A=%s, B=%s, Y=%s\n", log_signal(cell->getPort(ID::A)),
+ log_signal(cell->getPort(ID::B)), log_signal(cell->getPort(ID::Y)));
+ } else {
+ log(" Old ports: A=%s, Y=%s\n", log_signal(cell->getPort(ID::A)),
+ log_signal(cell->getPort(ID::Y)));
}
- cell->setPort(ID::B, RTLIL::SigSpec());
- for (int i = 1; i <= cell->getPort(ID::S).size(); i++)
- for (auto &in_tuple : consolidated_in_tuples) {
- RTLIL::SigSpec new_b = cell->getPort(ID::B);
- new_b.append(in_tuple.at(i));
- cell->setPort(ID::B, new_b);
+ if (swizzle.empty()) {
+ module->remove(cell);
+ } else {
+ SigSpec new_sig_a;
+ for (int i = 0; i < GetSize(sig_a); i += width)
+ for (int j: swizzle)
+ new_sig_a.append(sig_a[i+j]);
+ cell->setPort(ID::A, new_sig_a);
+
+ if (cell->type != ID($bmux)) {
+ SigSpec new_sig_b;
+ for (int i = 0; i < GetSize(sig_b); i += width)
+ for (int j: swizzle)
+ new_sig_b.append(sig_b[i+j]);
+ cell->setPort(ID::B, new_sig_b);
}
- cell->parameters[ID::WIDTH] = RTLIL::Const(new_sig_y.size());
- cell->setPort(ID::Y, new_sig_y);
+ SigSpec new_sig_y;
+ for (int j: swizzle)
+ new_sig_y.append(sig_y[j]);
+ cell->setPort(ID::Y, new_sig_y);
+
+ cell->parameters[ID::WIDTH] = RTLIL::Const(GetSize(swizzle));
+
+ if (cell->type != ID($bmux)) {
+ log(" New ports: A=%s, B=%s, Y=%s\n", log_signal(cell->getPort(ID::A)),
+ log_signal(cell->getPort(ID::B)), log_signal(cell->getPort(ID::Y)));
+ } else {
+ log(" New ports: A=%s, Y=%s\n", log_signal(cell->getPort(ID::A)),
+ log_signal(cell->getPort(ID::Y)));
+ }
+ }
- log(" New ports: A=%s, B=%s, Y=%s\n", log_signal(cell->getPort(ID::A)),
- log_signal(cell->getPort(ID::B)), log_signal(cell->getPort(ID::Y)));
log(" New connections: %s = %s\n", log_signal(old_sig_conn.first), log_signal(old_sig_conn.second));
+ module->connect(old_sig_conn);
+
+ did_something = true;
+ total_count++;
+ }
+ return swizzle.empty();
+ }
+
+ bool opt_demux_bits(RTLIL::Cell *cell) {
+ SigSpec sig_a = assign_map(cell->getPort(ID::A));
+ SigSpec sig_y = assign_map(cell->getPort(ID::Y));
+ int width = GetSize(sig_a);
+
+ RTLIL::SigSig old_sig_conn;
+
+ dict<SigBit, int> handled_bits;
+ std::vector<int> swizzle;
+
+ for (int i = 0; i < width; i++)
+ {
+ if (sig_a[i] == State::S0)
+ {
+ for (int j = i; j < GetSize(sig_y); j += width)
+ {
+ old_sig_conn.first.append(sig_y[j]);
+ old_sig_conn.second.append(State::S0);
+ }
+ continue;
+ }
+ auto it = handled_bits.find(sig_a[i]);
+ if (it == handled_bits.end())
+ {
+ handled_bits[sig_a[i]] = i;
+ swizzle.push_back(i);
+ }
+ else
+ {
+ for (int j = 0; j < GetSize(sig_y); j += width)
+ {
+ old_sig_conn.first.append(sig_y[i+j]);
+ old_sig_conn.second.append(sig_y[it->second+j]);
+ }
+ }
+ }
+
+ if (GetSize(swizzle) != width)
+ {
+ log(" Consolidated identical input bits for %s cell %s:\n", cell->type.c_str(), cell->name.c_str());
+ log(" Old ports: A=%s, Y=%s\n", log_signal(cell->getPort(ID::A)),
+ log_signal(cell->getPort(ID::Y)));
+
+ if (swizzle.empty()) {
+ module->remove(cell);
+ } else {
+ SigSpec new_sig_a;
+ for (int j: swizzle)
+ new_sig_a.append(sig_a[j]);
+ cell->setPort(ID::A, new_sig_a);
+
+ SigSpec new_sig_y;
+ for (int i = 0; i < GetSize(sig_y); i += width)
+ for (int j: swizzle)
+ new_sig_y.append(sig_y[i+j]);
+ cell->setPort(ID::Y, new_sig_y);
+
+ cell->parameters[ID::WIDTH] = RTLIL::Const(GetSize(swizzle));
+
+ log(" New ports: A=%s, Y=%s\n", log_signal(cell->getPort(ID::A)),
+ log_signal(cell->getPort(ID::Y)));
+ }
+
+ log(" New connections: %s = %s\n", log_signal(old_sig_conn.first), log_signal(old_sig_conn.second));
module->connect(old_sig_conn);
did_something = true;
total_count++;
}
+ return swizzle.empty();
}
OptReduceWorker(RTLIL::Design *design, RTLIL::Module *module, bool do_fine) :
@@ -309,20 +575,31 @@ struct OptReduceWorker
// merge identical inputs on $mux and $pmux cells
- std::vector<RTLIL::Cell*> cells;
-
- for (auto &it : module->cells_)
- if ((it.second->type == ID($mux) || it.second->type == ID($pmux)) && design->selected(module, it.second))
- cells.push_back(it.second);
-
- for (auto cell : cells)
+ for (auto cell : module->selected_cells())
{
+ if (!cell->type.in(ID($mux), ID($pmux), ID($bmux), ID($demux)))
+ continue;
+
// this optimization is to aggressive for most coarse-grain applications.
// but we always want it for multiplexers driving write enable ports.
- if (do_fine || mem_wren_sigs.check_any(assign_map(cell->getPort(ID::Y))))
- opt_mux_bits(cell);
+ if (do_fine || mem_wren_sigs.check_any(assign_map(cell->getPort(ID::Y)))) {
+ if (cell->type == ID($demux)) {
+ if (opt_demux_bits(cell))
+ continue;
+ } else {
+ if (opt_mux_bits(cell))
+ continue;
+ }
+ }
+
+ if (cell->type.in(ID($mux), ID($pmux)))
+ opt_pmux(cell);
+
+ if (cell->type == ID($bmux))
+ opt_bmux(cell);
- opt_mux(cell);
+ if (cell->type == ID($demux))
+ opt_demux(cell);
}
}
diff --git a/passes/pmgen/ice40_dsp.pmg b/passes/pmgen/ice40_dsp.pmg
index 7a01cbd51..4de479122 100644
--- a/passes/pmgen/ice40_dsp.pmg
+++ b/passes/pmgen/ice40_dsp.pmg
@@ -28,9 +28,8 @@ code sigA sigB sigH
for (i = GetSize(sig)-1; i > 0; i--)
if (sig[i] != sig[i-1])
break;
- // Do not remove non-const sign bit
- if (sig[i].wire)
- ++i;
+ // Do not remove sign bit
+ ++i;
return sig.extract(0, i);
};
sigA = unextend(port(mul, \A));
diff --git a/passes/sat/sim.cc b/passes/sat/sim.cc
index 4e158da62..a7c109374 100644
--- a/passes/sat/sim.cc
+++ b/passes/sat/sim.cc
@@ -21,12 +21,49 @@
#include "kernel/sigtools.h"
#include "kernel/celltypes.h"
#include "kernel/mem.h"
+#include "kernel/fstdata.h"
#include <ctime>
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
+enum class SimulationMode {
+ sim,
+ cmp,
+ gold,
+ gate,
+};
+
+static const std::map<std::string, int> g_units =
+{
+ { "", -9 }, // default is ns
+ { "s", 0 },
+ { "ms", -3 },
+ { "us", -6 },
+ { "ns", -9 },
+ { "ps", -12 },
+ { "fs", -15 },
+ { "as", -18 },
+ { "zs", -21 },
+};
+
+static double stringToTime(std::string str)
+{
+ if (str=="END") return -1;
+
+ char *endptr;
+ long value = strtol(str.c_str(), &endptr, 10);
+
+ if (g_units.find(endptr)==g_units.end())
+ log_error("Cannot parse '%s', bad unit '%s'\n", str.c_str(), endptr);
+
+ if (value < 0)
+ log_error("Time value '%s' must be positive\n", str.c_str());
+
+ return value * pow(10.0, g_units.at(endptr));
+}
+
struct SimShared
{
bool debug = false;
@@ -34,6 +71,11 @@ struct SimShared
bool writeback = false;
bool zinit = false;
int rstlen = 1;
+ FstData *fst = nullptr;
+ double start_time = 0;
+ double stop_time = -1;
+ SimulationMode sim_mode = SimulationMode::sim;
+ bool cycles_set = false;
};
void zinit(State &v)
@@ -51,7 +93,8 @@ void zinit(Const &v)
struct SimInstance
{
SimShared *shared;
-
+
+ std::string scope;
Module *module;
Cell *instance;
@@ -92,9 +135,11 @@ struct SimInstance
std::vector<Mem> memories;
dict<Wire*, pair<int, Const>> vcd_database;
+ dict<Wire*, pair<fstHandle, Const>> fst_database;
+ dict<Wire*, fstHandle> fst_handles;
- SimInstance(SimShared *shared, Module *module, Cell *instance = nullptr, SimInstance *parent = nullptr) :
- shared(shared), module(module), instance(instance), parent(parent), sigmap(module)
+ SimInstance(SimShared *shared, std::string scope, Module *module, Cell *instance = nullptr, SimInstance *parent = nullptr) :
+ shared(shared), scope(scope), module(module), instance(instance), parent(parent), sigmap(module)
{
log_assert(module);
@@ -116,6 +161,13 @@ struct SimInstance
}
}
+ if ((shared->fst) && !(shared->hide_internal && wire->name[0] == '$')) {
+ fstHandle id = shared->fst->getHandle(scope + "." + RTLIL::unescape_id(wire->name));
+ if (id==0 && wire->name.isPublic())
+ log_warning("Unable to found wire %s in input file.\n", (scope + "." + RTLIL::unescape_id(wire->name)).c_str());
+ fst_handles[wire] = id;
+ }
+
if (wire->attributes.count(ID::init)) {
Const initval = wire->attributes.at(ID::init);
for (int i = 0; i < GetSize(sig) && i < GetSize(initval); i++)
@@ -144,7 +196,7 @@ struct SimInstance
Module *mod = module->design->module(cell->type);
if (mod != nullptr) {
- dirty_children.insert(new SimInstance(shared, mod, cell, this));
+ dirty_children.insert(new SimInstance(shared, scope + "." + RTLIL::unescape_id(cell->name), mod, cell, this));
}
for (auto &port : cell->connections()) {
@@ -313,6 +365,12 @@ struct SimInstance
return;
}
+ // (A,S -> Y) cells
+ if (has_a && !has_b && !has_c && !has_d && has_s && has_y) {
+ set_state(sig_y, CellTypes::eval(cell, get_state(sig_a), get_state(sig_s)));
+ return;
+ }
+
// (A,B,S -> Y) cells
if (has_a && has_b && !has_c && !has_d && has_s && has_y) {
set_state(sig_y, CellTypes::eval(cell, get_state(sig_a), get_state(sig_b), get_state(sig_s)));
@@ -616,14 +674,125 @@ struct SimInstance
for (auto child : children)
child.second->write_vcd_step(f);
}
+
+ void write_fst_header(struct fstContext *f)
+ {
+ fstWriterSetScope(f, FST_ST_VCD_MODULE, stringf("%s",log_id(name())).c_str(), nullptr);
+ for (auto wire : module->wires())
+ {
+ if (shared->hide_internal && wire->name[0] == '$')
+ continue;
+
+ fstHandle id = fstWriterCreateVar(f, FST_VT_VCD_WIRE, FST_VD_IMPLICIT, GetSize(wire),
+ stringf("%s%s", wire->name[0] == '$' ? "\\" : "", log_id(wire)).c_str(), 0);
+ fst_database[wire] = make_pair(id, Const());
+ }
+
+ for (auto child : children)
+ child.second->write_fst_header(f);
+
+ fstWriterSetUpscope(f);
+ }
+
+ void write_fst_step(struct fstContext *f)
+ {
+ for (auto &it : fst_database)
+ {
+ Wire *wire = it.first;
+ Const value = get_state(wire);
+ fstHandle id = it.second.first;
+
+ if (it.second.second == value)
+ continue;
+
+ it.second.second = value;
+ std::stringstream ss;
+ for (int i = GetSize(value)-1; i >= 0; i--) {
+ switch (value[i]) {
+ case State::S0: ss << "0"; break;
+ case State::S1: ss << "1"; break;
+ case State::Sx: ss << "x"; break;
+ default: ss << "z";
+ }
+ }
+ fstWriterEmitValueChange(f, id, ss.str().c_str());
+ }
+
+ for (auto child : children)
+ child.second->write_fst_step(f);
+ }
+
+ void setInitState(uint64_t time)
+ {
+ for (auto &it : ff_database)
+ {
+ Cell *cell = it.first;
+
+ SigSpec qsig = cell->getPort(ID::Q);
+ if (qsig.is_wire()) {
+ IdString name = qsig.as_wire()->name;
+ fstHandle id = shared->fst->getHandle(scope + "." + RTLIL::unescape_id(name));
+ if (id==0 && name.isPublic())
+ log_warning("Unable to found wire %s in input file.\n", (scope + "." + RTLIL::unescape_id(name)).c_str());
+ if (id!=0) {
+ Const fst_val = Const::from_string(shared->fst->valueAt(id, time));
+ set_state(qsig, fst_val);
+ }
+ }
+ }
+ for (auto child : children)
+ child.second->setInitState(time);
+ }
+
+ bool checkSignals(uint64_t time)
+ {
+ bool retVal = false;
+ for(auto &item : fst_handles) {
+ if (item.second==0) continue; // Ignore signals not found
+ Const fst_val = Const::from_string(shared->fst->valueAt(item.second, time));
+ Const sim_val = get_state(item.first);
+ if (sim_val.size()!=fst_val.size())
+ log_error("Signal '%s' size is different in gold and gate.\n", log_id(item.first));
+ if (shared->sim_mode == SimulationMode::sim) {
+ // No checks performed when using stimulus
+ } else if (shared->sim_mode == SimulationMode::gate && !fst_val.is_fully_def()) { // FST data contains X
+ for(int i=0;i<fst_val.size();i++) {
+ if (fst_val[i]!=State::Sx && fst_val[i]!=sim_val[i]) {
+ log_warning("Signal '%s' in file %s in simulation %s\n", log_id(item.first), log_signal(fst_val), log_signal(sim_val));
+ retVal = true;
+ break;
+ }
+ }
+ } else if (shared->sim_mode == SimulationMode::gold && !sim_val.is_fully_def()) { // sim data contains X
+ for(int i=0;i<sim_val.size();i++) {
+ if (sim_val[i]!=State::Sx && fst_val[i]!=sim_val[i]) {
+ log_warning("Signal '%s' in file %s in simulation %s\n", log_id(item.first), log_signal(fst_val), log_signal(sim_val));
+ retVal = true;
+ break;
+ }
+ }
+ } else {
+ if (fst_val!=sim_val) {
+ log_warning("Signal '%s' in file %s in simulation '%s'\n", log_id(item.first), log_signal(fst_val), log_signal(sim_val));
+ retVal = true;
+ }
+ }
+ }
+ for (auto child : children)
+ retVal |= child.second->checkSignals(time);
+ return retVal;
+ }
};
struct SimWorker : SimShared
{
SimInstance *top = nullptr;
std::ofstream vcdfile;
+ struct fstContext *fstfile = nullptr;
pool<IdString> clock, clockn, reset, resetn;
std::string timescale;
+ std::string sim_filename;
+ std::string scope;
~SimWorker()
{
@@ -632,9 +801,6 @@ struct SimWorker : SimShared
void write_vcd_header()
{
- if (!vcdfile.is_open())
- return;
-
vcdfile << stringf("$version %s $end\n", yosys_version_str);
std::time_t t = std::time(nullptr);
@@ -654,13 +820,53 @@ struct SimWorker : SimShared
void write_vcd_step(int t)
{
- if (!vcdfile.is_open())
- return;
-
vcdfile << stringf("#%d\n", t);
top->write_vcd_step(vcdfile);
}
+ void write_fst_header()
+ {
+ std::time_t t = std::time(nullptr);
+ fstWriterSetDate(fstfile, asctime(std::localtime(&t)));
+ fstWriterSetVersion(fstfile, yosys_version_str);
+ if (!timescale.empty())
+ fstWriterSetTimescaleFromString(fstfile, timescale.c_str());
+
+ fstWriterSetPackType(fstfile, FST_WR_PT_FASTLZ);
+ fstWriterSetRepackOnClose(fstfile, 1);
+
+ top->write_fst_header(fstfile);
+ }
+
+ void write_fst_step(int t)
+ {
+ fstWriterEmitTimeChange(fstfile, t);
+
+ top->write_fst_step(fstfile);
+ }
+
+ void write_output_header()
+ {
+ if (vcdfile.is_open())
+ write_vcd_header();
+ if (fstfile)
+ write_fst_header();
+ }
+
+ void write_output_step(int t)
+ {
+ if (vcdfile.is_open())
+ write_vcd_step(t);
+ if (fstfile)
+ write_fst_step(t);
+ }
+
+ void write_output_end()
+ {
+ if (fstfile)
+ fstWriterClose(fstfile);
+ }
+
void update()
{
while (1)
@@ -699,7 +905,7 @@ struct SimWorker : SimShared
void run(Module *topmod, int numcycles)
{
log_assert(top == nullptr);
- top = new SimInstance(this, topmod);
+ top = new SimInstance(this, scope, topmod);
if (debug)
log("\n===== 0 =====\n");
@@ -714,24 +920,25 @@ struct SimWorker : SimShared
update();
- write_vcd_header();
- write_vcd_step(0);
+ write_output_header();
+ write_output_step(0);
for (int cycle = 0; cycle < numcycles; cycle++)
{
if (debug)
log("\n===== %d =====\n", 10*cycle + 5);
-
+ else
+ log("Simulating cycle %d.\n", (cycle*2)+1);
set_inports(clock, State::S0);
set_inports(clockn, State::S1);
update();
- write_vcd_step(10*cycle + 5);
+ write_output_step(10*cycle + 5);
if (debug)
log("\n===== %d =====\n", 10*cycle + 10);
else
- log("Simulating cycle %d.\n", cycle+1);
+ log("Simulating cycle %d.\n", (cycle*2)+2);
set_inports(clock, State::S1);
set_inports(clockn, State::S0);
@@ -742,11 +949,132 @@ struct SimWorker : SimShared
}
update();
- write_vcd_step(10*cycle + 10);
+ write_output_step(10*cycle + 10);
}
- write_vcd_step(10*numcycles + 2);
+ write_output_step(10*numcycles + 2);
+
+ write_output_end();
+
+ if (writeback) {
+ pool<Module*> wbmods;
+ top->writeback(wbmods);
+ }
+ }
+
+ void run_cosim(Module *topmod, int numcycles)
+ {
+ log_assert(top == nullptr);
+ fst = new FstData(sim_filename);
+
+ if (scope.empty())
+ log_error("Scope must be defined for co-simulation.\n");
+
+ top = new SimInstance(this, scope, topmod);
+
+ std::vector<fstHandle> fst_clock;
+
+ for (auto portname : clock)
+ {
+ Wire *w = topmod->wire(portname);
+ if (!w)
+ log_error("Can't find port %s on module %s.\n", log_id(portname), log_id(top->module));
+ if (!w->port_input)
+ log_error("Clock port %s on module %s is not input.\n", log_id(portname), log_id(top->module));
+ fstHandle id = fst->getHandle(scope + "." + RTLIL::unescape_id(portname));
+ if (id==0)
+ log_error("Can't find port %s.%s in FST.\n", scope.c_str(), log_id(portname));
+ fst_clock.push_back(id);
+ }
+ for (auto portname : clockn)
+ {
+ Wire *w = topmod->wire(portname);
+ if (!w)
+ log_error("Can't find port %s on module %s.\n", log_id(portname), log_id(top->module));
+ if (!w->port_input)
+ log_error("Clock port %s on module %s is not input.\n", log_id(portname), log_id(top->module));
+ fstHandle id = fst->getHandle(scope + "." + RTLIL::unescape_id(portname));
+ if (id==0)
+ log_error("Can't find port %s.%s in FST.\n", scope.c_str(), log_id(portname));
+ fst_clock.push_back(id);
+ }
+ if (fst_clock.size()==0)
+ log_error("No clock signals defined for input file\n");
+
+ SigMap sigmap(topmod);
+ std::map<Wire*,fstHandle> inputs;
+
+ for (auto wire : topmod->wires()) {
+ if (wire->port_input) {
+ fstHandle id = fst->getHandle(scope + "." + RTLIL::unescape_id(wire->name));
+ if (id==0)
+ log_error("Unable to find required '%s' signal in file\n",(scope + "." + RTLIL::unescape_id(wire->name)).c_str());
+ inputs[wire] = id;
+ }
+ }
+
+ uint64_t startCount = 0;
+ uint64_t stopCount = 0;
+ if (start_time==0) {
+ if (start_time < fst->getStartTime())
+ log_warning("Start time is before simulation file start time\n");
+ startCount = fst->getStartTime();
+ } else if (start_time==-1)
+ startCount = fst->getEndTime();
+ else {
+ startCount = start_time / fst->getTimescale();
+ if (startCount > fst->getEndTime()) {
+ startCount = fst->getEndTime();
+ log_warning("Start time is after simulation file end time\n");
+ }
+ }
+ if (stop_time==0) {
+ if (stop_time < fst->getStartTime())
+ log_warning("Stop time is before simulation file start time\n");
+ stopCount = fst->getStartTime();
+ } else if (stop_time==-1)
+ stopCount = fst->getEndTime();
+ else {
+ stopCount = stop_time / fst->getTimescale();
+ if (stopCount > fst->getEndTime()) {
+ stopCount = fst->getEndTime();
+ log_warning("Stop time is after simulation file end time\n");
+ }
+ }
+ if (stopCount<startCount) {
+ log_error("Stop time is before start time\n");
+ }
+ auto samples = fst->getAllEdges(fst_clock, startCount, stopCount);
+
+ // Limit to number of cycles if provided
+ if (cycles_set && ((size_t)(numcycles *2) < samples.size()))
+ samples.erase(samples.begin() + (numcycles*2), samples.end());
+
+ // Add setup time (start time)
+ if (samples.empty() || samples.front()!=startCount)
+ samples.insert(samples.begin(), startCount);
+
+ fst->reconstructAllAtTimes(samples);
+ bool initial = true;
+ int cycle = 0;
+ log("Co-simulation from %lu%s to %lu%s\n", (unsigned long)startCount, fst->getTimescaleString(), (unsigned long)stopCount, fst->getTimescaleString());
+ for(auto &time : samples) {
+ log("Co-simulating cycle %d [%lu%s].\n", cycle, (unsigned long)time, fst->getTimescaleString());
+ for(auto &item : inputs) {
+ std::string v = fst->valueAt(item.second, time);
+ top->set_state(item.first, Const::from_string(v));
+ }
+ if (initial) {
+ top->setInitState(time);
+ initial = false;
+ }
+ update();
+ bool status = top->checkSignals(time);
+ if (status)
+ log_error("Signal difference\n");
+ cycle++;
+ }
if (writeback) {
pool<Module*> wbmods;
top->writeback(wbmods);
@@ -767,6 +1095,9 @@ struct SimPass : public Pass {
log(" -vcd <filename>\n");
log(" write the simulation results to the given VCD file\n");
log("\n");
+ log(" -fst <filename>\n");
+ log(" write the simulation results to the given FST file\n");
+ log("\n");
log(" -clock <portname>\n");
log(" name of top-level clock input\n");
log("\n");
@@ -789,14 +1120,41 @@ struct SimPass : public Pass {
log(" include the specified timescale declaration in the vcd\n");
log("\n");
log(" -n <integer>\n");
- log(" number of cycles to simulate (default: 20)\n");
+ log(" number of clock cycles to simulate (default: 20)\n");
log("\n");
log(" -a\n");
- log(" include all nets in VCD output, not just those with public names\n");
+ log(" use all nets in VCD/FST operations, not just those with public names\n");
log("\n");
log(" -w\n");
log(" writeback mode: use final simulation state as new init state\n");
log("\n");
+ log(" -r\n");
+ log(" read simulation results file (file formats supported: FST)\n");
+ log("\n");
+ log(" -scope\n");
+ log(" scope of simulation top model\n");
+ log("\n");
+ log(" -at <time>\n");
+ log(" sets start and stop time\n");
+ log("\n");
+ log(" -start <time>\n");
+ log(" start co-simulation in arbitary time (default 0)\n");
+ log("\n");
+ log(" -stop <time>\n");
+ log(" stop co-simulation in arbitary time (default END)\n");
+ log("\n");
+ log(" -sim\n");
+ log(" simulation with stimulus from FST (default)\n");
+ log("\n");
+ log(" -sim-cmp\n");
+ log(" co-simulation expect exact match\n");
+ log("\n");
+ log(" -sim-gold\n");
+ log(" co-simulation, x in simulation can match any value in FST\n");
+ log("\n");
+ log(" -sim-gate\n");
+ log(" co-simulation, x in FST can match any value in simulation\n");
+ log("\n");
log(" -d\n");
log(" enable debug output\n");
log("\n");
@@ -805,6 +1163,7 @@ struct SimPass : public Pass {
{
SimWorker worker;
int numcycles = 20;
+ bool start_set = false, stop_set = false, at_set = false;
log_header(design, "Executing SIM pass (simulate the circuit).\n");
@@ -816,8 +1175,15 @@ struct SimPass : public Pass {
worker.vcdfile.open(vcd_filename.c_str());
continue;
}
+ if (args[argidx] == "-fst" && argidx+1 < args.size()) {
+ std::string fst_filename = args[++argidx];
+ rewrite_filename(fst_filename);
+ worker.fstfile = (struct fstContext *)fstWriterCreate(fst_filename.c_str(),1);
+ continue;
+ }
if (args[argidx] == "-n" && argidx+1 < args.size()) {
numcycles = atoi(args[++argidx].c_str());
+ worker.cycles_set = true;
continue;
}
if (args[argidx] == "-rstlen" && argidx+1 < args.size()) {
@@ -860,9 +1226,55 @@ struct SimPass : public Pass {
worker.zinit = true;
continue;
}
+ if (args[argidx] == "-r" && argidx+1 < args.size()) {
+ std::string sim_filename = args[++argidx];
+ rewrite_filename(sim_filename);
+ worker.sim_filename = sim_filename;
+ continue;
+ }
+ if (args[argidx] == "-scope" && argidx+1 < args.size()) {
+ worker.scope = args[++argidx];
+ continue;
+ }
+ if (args[argidx] == "-start" && argidx+1 < args.size()) {
+ worker.start_time = stringToTime(args[++argidx]);
+ start_set = true;
+ continue;
+ }
+ if (args[argidx] == "-stop" && argidx+1 < args.size()) {
+ worker.stop_time = stringToTime(args[++argidx]);
+ stop_set = true;
+ continue;
+ }
+ if (args[argidx] == "-at" && argidx+1 < args.size()) {
+ worker.start_time = stringToTime(args[++argidx]);
+ worker.stop_time = worker.start_time;
+ at_set = true;
+ continue;
+ }
+ if (args[argidx] == "-sim") {
+ worker.sim_mode = SimulationMode::sim;
+ continue;
+ }
+ if (args[argidx] == "-sim-cmp") {
+ worker.sim_mode = SimulationMode::cmp;
+ continue;
+ }
+ if (args[argidx] == "-sim-gold") {
+ worker.sim_mode = SimulationMode::gold;
+ continue;
+ }
+ if (args[argidx] == "-sim-gate") {
+ worker.sim_mode = SimulationMode::gate;
+ continue;
+ }
break;
}
extra_args(args, argidx, design);
+ if (at_set && (start_set || stop_set || worker.cycles_set))
+ log_error("'at' option can only be defined separate of 'start','stop' and 'n'\n");
+ if (stop_set && worker.cycles_set)
+ log_error("'stop' and 'n' can only be used exclusively'\n");
Module *top_mod = nullptr;
@@ -878,7 +1290,10 @@ struct SimPass : public Pass {
top_mod = mods.front();
}
- worker.run(top_mod, numcycles);
+ if (worker.sim_filename.empty())
+ worker.run(top_mod, numcycles);
+ else
+ worker.run_cosim(top_mod, numcycles);
}
} SimPass;
diff --git a/passes/techmap/Makefile.inc b/passes/techmap/Makefile.inc
index 035699603..98ccfc303 100644
--- a/passes/techmap/Makefile.inc
+++ b/passes/techmap/Makefile.inc
@@ -29,6 +29,8 @@ OBJS += passes/techmap/extract_reduce.o
OBJS += passes/techmap/alumacc.o
OBJS += passes/techmap/dffinit.o
OBJS += passes/techmap/pmuxtree.o
+OBJS += passes/techmap/bmuxmap.o
+OBJS += passes/techmap/demuxmap.o
OBJS += passes/techmap/muxcover.o
OBJS += passes/techmap/aigmap.o
OBJS += passes/techmap/tribuf.o
diff --git a/passes/techmap/bmuxmap.cc b/passes/techmap/bmuxmap.cc
new file mode 100644
index 000000000..03673c278
--- /dev/null
+++ b/passes/techmap/bmuxmap.cc
@@ -0,0 +1,76 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2022 Marcelina Koƛcielnicka <mwk@0x04.net>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "kernel/yosys.h"
+#include "kernel/sigtools.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct BmuxmapPass : public Pass {
+ BmuxmapPass() : Pass("bmuxmap", "transform $bmux cells to trees of $mux cells") { }
+ void help() override
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" bmuxmap [selection]\n");
+ log("\n");
+ log("This pass transforms $bmux cells to trees of $mux cells.\n");
+ log("\n");
+ }
+ void execute(std::vector<std::string> args, RTLIL::Design *design) override
+ {
+ log_header(design, "Executing BMUXMAP pass.\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto module : design->selected_modules())
+ for (auto cell : module->selected_cells())
+ {
+ if (cell->type != ID($bmux))
+ continue;
+
+ SigSpec sel = cell->getPort(ID::S);
+ SigSpec data = cell->getPort(ID::A);
+ int width = GetSize(cell->getPort(ID::Y));
+
+ for (int idx = 0; idx < GetSize(sel); idx++) {
+ SigSpec new_data = module->addWire(NEW_ID, GetSize(data)/2);
+ for (int i = 0; i < GetSize(new_data); i += width) {
+ RTLIL::Cell *mux = module->addMux(NEW_ID,
+ data.extract(i*2, width),
+ data.extract(i*2+width, width),
+ sel[idx],
+ new_data.extract(i, width));
+ mux->add_strpool_attribute(ID::src, cell->get_strpool_attribute(ID::src));
+ }
+ data = new_data;
+ }
+
+ module->connect(cell->getPort(ID::Y), data);
+ module->remove(cell);
+ }
+ }
+} BmuxmapPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/techmap/demuxmap.cc b/passes/techmap/demuxmap.cc
new file mode 100644
index 000000000..292b18bad
--- /dev/null
+++ b/passes/techmap/demuxmap.cc
@@ -0,0 +1,80 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2022 Marcelina Koƛcielnicka <mwk@0x04.net>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "kernel/yosys.h"
+#include "kernel/sigtools.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct DemuxmapPass : public Pass {
+ DemuxmapPass() : Pass("demuxmap", "transform $demux cells to $eq + $mux cells") { }
+ void help() override
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" demuxmap [selection]\n");
+ log("\n");
+ log("This pass transforms $demux cells to a bunch of equality comparisons.\n");
+ log("\n");
+ }
+ void execute(std::vector<std::string> args, RTLIL::Design *design) override
+ {
+ log_header(design, "Executing DEMUXMAP pass.\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto module : design->selected_modules())
+ for (auto cell : module->selected_cells())
+ {
+ if (cell->type != ID($demux))
+ continue;
+
+ SigSpec sel = cell->getPort(ID::S);
+ SigSpec data = cell->getPort(ID::A);
+ SigSpec out = cell->getPort(ID::Y);
+ int width = GetSize(cell->getPort(ID::A));
+
+ for (int i = 0; i < 1 << GetSize(sel); i++) {
+ if (width == 1 && data == State::S1) {
+ RTLIL::Cell *eq_cell = module->addEq(NEW_ID, sel, Const(i, GetSize(sel)), out[i]);
+ eq_cell->add_strpool_attribute(ID::src, cell->get_strpool_attribute(ID::src));
+ } else {
+ Wire *eq = module->addWire(NEW_ID);
+ RTLIL::Cell *eq_cell = module->addEq(NEW_ID, sel, Const(i, GetSize(sel)), eq);
+ eq_cell->add_strpool_attribute(ID::src, cell->get_strpool_attribute(ID::src));
+ RTLIL::Cell *mux = module->addMux(NEW_ID,
+ Const(State::S0, width),
+ data,
+ eq,
+ out.extract(i*width, width));
+ mux->add_strpool_attribute(ID::src, cell->get_strpool_attribute(ID::src));
+ }
+ }
+
+ module->remove(cell);
+ }
+ }
+} DemuxmapPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/techmap/iopadmap.cc b/passes/techmap/iopadmap.cc
index 990e28876..437ad5156 100644
--- a/passes/techmap/iopadmap.cc
+++ b/passes/techmap/iopadmap.cc
@@ -46,7 +46,7 @@ struct IopadmapPass : public Pass {
log(" -inpad <celltype> <in_port>[:<ext_port>]\n");
log(" Map module input ports to the given cell type with the\n");
log(" given output port name. if a 2nd portname is given, the\n");
- log(" signal is passed through the pad call, using the 2nd\n");
+ log(" signal is passed through the pad cell, using the 2nd\n");
log(" portname as the port facing the module port.\n");
log("\n");
log(" -outpad <celltype> <out_port>[:<ext_port>]\n");
diff --git a/passes/techmap/simplemap.cc b/passes/techmap/simplemap.cc
index 68f44cf6d..7d8dba439 100644
--- a/passes/techmap/simplemap.cc
+++ b/passes/techmap/simplemap.cc
@@ -299,6 +299,30 @@ void simplemap_tribuf(RTLIL::Module *module, RTLIL::Cell *cell)
}
}
+void simplemap_bmux(RTLIL::Module *module, RTLIL::Cell *cell)
+{
+ SigSpec sel = cell->getPort(ID::S);
+ SigSpec data = cell->getPort(ID::A);
+ int width = GetSize(cell->getPort(ID::Y));
+
+ for (int idx = 0; idx < GetSize(sel); idx++) {
+ SigSpec new_data = module->addWire(NEW_ID, GetSize(data)/2);
+ for (int i = 0; i < GetSize(new_data); i += width) {
+ for (int k = 0; k < width; k++) {
+ RTLIL::Cell *gate = module->addCell(NEW_ID, ID($_MUX_));
+ gate->add_strpool_attribute(ID::src, cell->get_strpool_attribute(ID::src));
+ gate->setPort(ID::A, data[i*2+k]);
+ gate->setPort(ID::B, data[i*2+width+k]);
+ gate->setPort(ID::S, sel[idx]);
+ gate->setPort(ID::Y, new_data[i+k]);
+ }
+ }
+ data = new_data;
+ }
+
+ module->connect(cell->getPort(ID::Y), data);
+}
+
void simplemap_lut(RTLIL::Module *module, RTLIL::Cell *cell)
{
SigSpec lut_ctrl = cell->getPort(ID::A);
@@ -306,7 +330,6 @@ void simplemap_lut(RTLIL::Module *module, RTLIL::Cell *cell)
lut_data.extend_u0(1 << cell->getParam(ID::WIDTH).as_int());
for (int idx = 0; GetSize(lut_data) > 1; idx++) {
- SigSpec sig_s = lut_ctrl[idx];
SigSpec new_lut_data = module->addWire(NEW_ID, GetSize(lut_data)/2);
for (int i = 0; i < GetSize(lut_data); i += 2) {
RTLIL::Cell *gate = module->addCell(NEW_ID, ID($_MUX_));
@@ -400,6 +423,7 @@ void simplemap_get_mappers(dict<IdString, void(*)(RTLIL::Module*, RTLIL::Cell*)>
mappers[ID($nex)] = simplemap_eqne;
mappers[ID($mux)] = simplemap_mux;
mappers[ID($tribuf)] = simplemap_tribuf;
+ mappers[ID($bmux)] = simplemap_bmux;
mappers[ID($lut)] = simplemap_lut;
mappers[ID($sop)] = simplemap_sop;
mappers[ID($slice)] = simplemap_slice;
diff --git a/passes/tests/test_cell.cc b/passes/tests/test_cell.cc
index 4e437e409..e21ec452c 100644
--- a/passes/tests/test_cell.cc
+++ b/passes/tests/test_cell.cc
@@ -69,6 +69,48 @@ static void create_gold_module(RTLIL::Design *design, RTLIL::IdString cell_type,
cell->setPort(ID::Y, wire);
}
+ if (cell_type == ID($bmux))
+ {
+ int width = 1 + xorshift32(8);
+ int swidth = 1 + xorshift32(4);
+
+ wire = module->addWire(ID::A);
+ wire->width = width << swidth;
+ wire->port_input = true;
+ cell->setPort(ID::A, wire);
+
+ wire = module->addWire(ID::S);
+ wire->width = swidth;
+ wire->port_input = true;
+ cell->setPort(ID::S, wire);
+
+ wire = module->addWire(ID::Y);
+ wire->width = width;
+ wire->port_output = true;
+ cell->setPort(ID::Y, wire);
+ }
+
+ if (cell_type == ID($demux))
+ {
+ int width = 1 + xorshift32(8);
+ int swidth = 1 + xorshift32(6);
+
+ wire = module->addWire(ID::A);
+ wire->width = width;
+ wire->port_input = true;
+ cell->setPort(ID::A, wire);
+
+ wire = module->addWire(ID::S);
+ wire->width = swidth;
+ wire->port_input = true;
+ cell->setPort(ID::S, wire);
+
+ wire = module->addWire(ID::Y);
+ wire->width = width << swidth;
+ wire->port_output = true;
+ cell->setPort(ID::Y, wire);
+ }
+
if (cell_type == ID($fa))
{
int width = 1 + xorshift32(8);
@@ -855,8 +897,10 @@ struct TestCellPass : public Pass {
cell_types[ID($logic_and)] = "ABSY";
cell_types[ID($logic_or)] = "ABSY";
+ cell_types[ID($mux)] = "*";
+ cell_types[ID($bmux)] = "*";
+ cell_types[ID($demux)] = "*";
if (edges) {
- cell_types[ID($mux)] = "*";
cell_types[ID($pmux)] = "*";
}