aboutsummaryrefslogtreecommitdiffstats
path: root/passes
diff options
context:
space:
mode:
Diffstat (limited to 'passes')
-rw-r--r--passes/abc/Makefile.inc6
-rw-r--r--passes/abc/blifparse.cc244
-rw-r--r--passes/abc/blifparse.h31
-rw-r--r--passes/cmds/Makefile.inc4
-rw-r--r--passes/cmds/add.cc6
-rw-r--r--passes/cmds/check.cc162
-rw-r--r--passes/cmds/connect.cc10
-rw-r--r--passes/cmds/connwrappers.cc12
-rw-r--r--passes/cmds/copy.cc6
-rw-r--r--passes/cmds/cover.cc2
-rw-r--r--passes/cmds/delete.cc6
-rw-r--r--passes/cmds/design.cc6
-rw-r--r--passes/cmds/edgetypes.cc106
-rw-r--r--passes/cmds/plugin.cc10
-rw-r--r--passes/cmds/qwp.cc870
-rw-r--r--passes/cmds/rename.cc31
-rw-r--r--passes/cmds/scatter.cc6
-rw-r--r--passes/cmds/scc.cc67
-rw-r--r--passes/cmds/select.cc194
-rw-r--r--passes/cmds/setattr.cc119
-rw-r--r--passes/cmds/setundef.cc100
-rw-r--r--passes/cmds/show.cc42
-rw-r--r--passes/cmds/splice.cc41
-rw-r--r--passes/cmds/splitnets.cc103
-rw-r--r--passes/cmds/stat.cc91
-rw-r--r--passes/cmds/torder.cc123
-rw-r--r--passes/cmds/write_file.cc2
-rw-r--r--passes/equiv/Makefile.inc3
-rw-r--r--passes/equiv/equiv_add.cc156
-rw-r--r--passes/equiv/equiv_induct.cc49
-rw-r--r--passes/equiv/equiv_make.cc10
-rw-r--r--passes/equiv/equiv_mark.cc239
-rw-r--r--passes/equiv/equiv_miter.cc8
-rw-r--r--passes/equiv/equiv_purge.cc210
-rw-r--r--passes/equiv/equiv_remove.cc6
-rw-r--r--passes/equiv/equiv_simple.cc34
-rw-r--r--passes/equiv/equiv_status.cc6
-rw-r--r--passes/equiv/equiv_struct.cc367
-rw-r--r--passes/fsm/fsm.cc12
-rw-r--r--passes/fsm/fsm_detect.cc50
-rw-r--r--passes/fsm/fsm_expand.cc10
-rw-r--r--passes/fsm/fsm_export.cc6
-rw-r--r--passes/fsm/fsm_extract.cc33
-rw-r--r--passes/fsm/fsm_info.cc8
-rw-r--r--passes/fsm/fsm_map.cc8
-rw-r--r--passes/fsm/fsm_opt.cc14
-rw-r--r--passes/fsm/fsm_recode.cc14
-rw-r--r--passes/fsm/fsmdata.h6
-rw-r--r--passes/hierarchy/Makefile.inc1
-rw-r--r--passes/hierarchy/hierarchy.cc103
-rw-r--r--passes/hierarchy/singleton.cc101
-rw-r--r--passes/hierarchy/submod.cc53
-rw-r--r--passes/memory/memory.cc19
-rw-r--r--passes/memory/memory_bram.cc126
-rw-r--r--passes/memory/memory_collect.cc245
-rw-r--r--passes/memory/memory_dff.cc347
-rw-r--r--passes/memory/memory_map.cc68
-rw-r--r--passes/memory/memory_share.cc68
-rw-r--r--passes/memory/memory_unpack.cc44
-rw-r--r--passes/opt/Makefile.inc6
-rw-r--r--passes/opt/opt.cc65
-rw-r--r--passes/opt/opt_clean.cc115
-rw-r--r--passes/opt/opt_expr.cc (renamed from passes/opt/opt_const.cc)372
-rw-r--r--passes/opt/opt_merge.cc (renamed from passes/opt/opt_share.cc)103
-rw-r--r--passes/opt/opt_muxtree.cc12
-rw-r--r--passes/opt/opt_reduce.cc10
-rw-r--r--passes/opt/opt_rmdff.cc106
-rw-r--r--passes/opt/share.cc159
-rw-r--r--passes/opt/wreduce.cc93
-rw-r--r--passes/proc/Makefile.inc1
-rw-r--r--passes/proc/proc.cc27
-rw-r--r--passes/proc/proc_arst.cc13
-rw-r--r--passes/proc/proc_clean.cc8
-rw-r--r--passes/proc/proc_dff.cc8
-rw-r--r--passes/proc/proc_dlatch.cc449
-rw-r--r--passes/proc/proc_init.cc39
-rw-r--r--passes/proc/proc_mux.cc242
-rw-r--r--passes/proc/proc_rmdead.cc12
-rw-r--r--passes/sat/eval.cc38
-rw-r--r--passes/sat/expose.cc32
-rw-r--r--passes/sat/freduce.cc85
-rw-r--r--passes/sat/miter.cc110
-rw-r--r--passes/sat/sat.cc465
-rw-r--r--passes/techmap/Makefile.inc23
-rw-r--r--passes/techmap/abc.cc (renamed from passes/abc/abc.cc)427
-rw-r--r--passes/techmap/aigmap.cc149
-rw-r--r--passes/techmap/alumacc.cc24
-rw-r--r--passes/techmap/deminout.cc116
-rw-r--r--passes/techmap/dff2dffe.cc76
-rw-r--r--passes/techmap/dffinit.cc135
-rw-r--r--passes/techmap/dfflibmap.cc117
-rw-r--r--passes/techmap/dffsr2dff.cc213
-rw-r--r--passes/techmap/extract.cc32
-rw-r--r--passes/techmap/hilomap.cc8
-rw-r--r--passes/techmap/iopadmap.cc187
-rw-r--r--passes/techmap/libparse.cc12
-rw-r--r--passes/techmap/libparse.h4
-rw-r--r--passes/techmap/lut2mux.cc93
-rw-r--r--passes/techmap/maccmap.cc14
-rw-r--r--passes/techmap/muxcover.cc632
-rw-r--r--passes/techmap/nlutmap.cc187
-rw-r--r--passes/techmap/pmuxtree.cc112
-rw-r--r--passes/techmap/shregmap.cc584
-rw-r--r--passes/techmap/simplemap.cc115
-rw-r--r--passes/techmap/simplemap.h5
-rw-r--r--passes/techmap/techmap.cc224
-rw-r--r--passes/techmap/tribuf.cc186
-rw-r--r--passes/tests/test_abcloop.cc24
-rw-r--r--passes/tests/test_autotb.cc44
-rw-r--r--passes/tests/test_cell.cc72
110 files changed, 9346 insertions, 1863 deletions
diff --git a/passes/abc/Makefile.inc b/passes/abc/Makefile.inc
deleted file mode 100644
index a7c5b02c6..000000000
--- a/passes/abc/Makefile.inc
+++ /dev/null
@@ -1,6 +0,0 @@
-
-ifeq ($(ENABLE_ABC),1)
-OBJS += passes/abc/abc.o
-OBJS += passes/abc/blifparse.o
-endif
-
diff --git a/passes/abc/blifparse.cc b/passes/abc/blifparse.cc
deleted file mode 100644
index db87eec4a..000000000
--- a/passes/abc/blifparse.cc
+++ /dev/null
@@ -1,244 +0,0 @@
-/*
- * yosys -- Yosys Open SYnthesis Suite
- *
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- */
-
-#include "blifparse.h"
-
-YOSYS_NAMESPACE_BEGIN
-
-static bool read_next_line(char *&buffer, size_t &buffer_size, int &line_count, FILE *f)
-{
- int buffer_len = 0;
- buffer[0] = 0;
-
- while (1)
- {
- buffer_len += strlen(buffer + buffer_len);
- while (buffer_len > 0 && (buffer[buffer_len-1] == ' ' || buffer[buffer_len-1] == '\t' ||
- buffer[buffer_len-1] == '\r' || buffer[buffer_len-1] == '\n'))
- buffer[--buffer_len] = 0;
-
- if (buffer_size-buffer_len < 4096) {
- buffer_size *= 2;
- buffer = (char*)realloc(buffer, buffer_size);
- }
-
- if (buffer_len == 0 || buffer[buffer_len-1] == '\\') {
- if (buffer_len > 0 && buffer[buffer_len-1] == '\\')
- buffer[--buffer_len] = 0;
- line_count++;
- if (fgets(buffer+buffer_len, buffer_size-buffer_len, f) == NULL)
- return false;
- } else
- return true;
- }
-}
-
-RTLIL::Design *abc_parse_blif(FILE *f, std::string dff_name)
-{
- RTLIL::Design *design = new RTLIL::Design;
- RTLIL::Module *module = new RTLIL::Module;
-
- RTLIL::Const *lutptr = NULL;
- RTLIL::State lut_default_state = RTLIL::State::Sx;
-
- module->name = "\\netlist";
- design->add(module);
-
- size_t buffer_size = 4096;
- char *buffer = (char*)malloc(buffer_size);
- int line_count = 0;
-
- while (1)
- {
- if (!read_next_line(buffer, buffer_size, line_count, f))
- goto error;
-
- continue_without_read:
- if (buffer[0] == '#')
- continue;
-
- if (buffer[0] == '.')
- {
- if (lutptr) {
- for (auto &bit : lutptr->bits)
- if (bit == RTLIL::State::Sx)
- bit = lut_default_state;
- lutptr = NULL;
- lut_default_state = RTLIL::State::Sx;
- }
-
- char *cmd = strtok(buffer, " \t\r\n");
-
- if (!strcmp(cmd, ".model"))
- continue;
-
- if (!strcmp(cmd, ".end")) {
- module->fixup_ports();
- free(buffer);
- return design;
- }
-
- if (!strcmp(cmd, ".inputs") || !strcmp(cmd, ".outputs")) {
- char *p;
- while ((p = strtok(NULL, " \t\r\n")) != NULL) {
- RTLIL::Wire *wire = module->addWire(stringf("\\%s", p));
- if (!strcmp(cmd, ".inputs"))
- wire->port_input = true;
- else
- wire->port_output = true;
- }
- continue;
- }
-
- if (!strcmp(cmd, ".latch"))
- {
- char *d = strtok(NULL, " \t\r\n");
- char *q = strtok(NULL, " \t\r\n");
-
- if (module->wires_.count(RTLIL::escape_id(d)) == 0)
- module->addWire(RTLIL::escape_id(d));
-
- if (module->wires_.count(RTLIL::escape_id(q)) == 0)
- module->addWire(RTLIL::escape_id(q));
-
- RTLIL::Cell *cell = module->addCell(NEW_ID, dff_name);
- cell->setPort("\\D", module->wires_.at(RTLIL::escape_id(d)));
- cell->setPort("\\Q", module->wires_.at(RTLIL::escape_id(q)));
- continue;
- }
-
- if (!strcmp(cmd, ".gate"))
- {
- char *p = strtok(NULL, " \t\r\n");
- if (p == NULL)
- goto error;
-
- IdString celltype = RTLIL::escape_id(p);
- RTLIL::Cell *cell = module->addCell(NEW_ID, celltype);
-
- while ((p = strtok(NULL, " \t\r\n")) != NULL) {
- char *q = strchr(p, '=');
- if (q == NULL || !q[0] || !q[1])
- goto error;
- *(q++) = 0;
- if (module->wires_.count(RTLIL::escape_id(q)) == 0)
- module->addWire(RTLIL::escape_id(q));
- cell->setPort(RTLIL::escape_id(p), module->wires_.at(RTLIL::escape_id(q)));
- }
- continue;
- }
-
- if (!strcmp(cmd, ".names"))
- {
- char *p;
- RTLIL::SigSpec input_sig, output_sig;
- while ((p = strtok(NULL, " \t\r\n")) != NULL) {
- RTLIL::Wire *wire;
- if (module->wires_.count(stringf("\\%s", p)) > 0) {
- wire = module->wires_.at(stringf("\\%s", p));
- } else {
- wire = module->addWire(stringf("\\%s", p));
- }
- input_sig.append(wire);
- }
- output_sig = input_sig.extract(input_sig.size()-1, 1);
- input_sig = input_sig.extract(0, input_sig.size()-1);
-
- if (input_sig.size() == 0) {
- RTLIL::State state = RTLIL::State::Sa;
- while (1) {
- if (!read_next_line(buffer, buffer_size, line_count, f))
- goto error;
- for (int i = 0; buffer[i]; i++) {
- if (buffer[i] == ' ' || buffer[i] == '\t')
- continue;
- if (i == 0 && buffer[i] == '.')
- goto finished_parsing_constval;
- if (buffer[i] == '0') {
- if (state == RTLIL::State::S1)
- goto error;
- state = RTLIL::State::S0;
- continue;
- }
- if (buffer[i] == '1') {
- if (state == RTLIL::State::S0)
- goto error;
- state = RTLIL::State::S1;
- continue;
- }
- goto error;
- }
- }
- finished_parsing_constval:
- if (state == RTLIL::State::Sa)
- state = RTLIL::State::S1;
- module->connect(RTLIL::SigSig(output_sig, state));
- goto continue_without_read;
- }
-
- RTLIL::Cell *cell = module->addCell(NEW_ID, "$lut");
- cell->parameters["\\WIDTH"] = RTLIL::Const(input_sig.size());
- cell->parameters["\\LUT"] = RTLIL::Const(RTLIL::State::Sx, 1 << input_sig.size());
- cell->setPort("\\A", input_sig);
- cell->setPort("\\Y", output_sig);
- lutptr = &cell->parameters.at("\\LUT");
- lut_default_state = RTLIL::State::Sx;
- continue;
- }
-
- goto error;
- }
-
- if (lutptr == NULL)
- goto error;
-
- char *input = strtok(buffer, " \t\r\n");
- char *output = strtok(NULL, " \t\r\n");
-
- if (input == NULL || output == NULL || (strcmp(output, "0") && strcmp(output, "1")))
- goto error;
-
- int input_len = strlen(input);
- if (input_len > 8)
- goto error;
-
- for (int i = 0; i < (1 << input_len); i++) {
- for (int j = 0; j < input_len; j++) {
- char c1 = input[j];
- if (c1 != '-') {
- char c2 = (i & (1 << j)) != 0 ? '1' : '0';
- if (c1 != c2)
- goto try_next_value;
- }
- }
- lutptr->bits.at(i) = !strcmp(output, "0") ? RTLIL::State::S0 : RTLIL::State::S1;
- try_next_value:;
- }
-
- lut_default_state = !strcmp(output, "0") ? RTLIL::State::S1 : RTLIL::State::S0;
- }
-
-error:
- log_error("Syntax error in line %d!\n", line_count);
- // delete design;
- // return NULL;
-}
-
-YOSYS_NAMESPACE_END
-
diff --git a/passes/abc/blifparse.h b/passes/abc/blifparse.h
deleted file mode 100644
index 31f5b2e5c..000000000
--- a/passes/abc/blifparse.h
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * yosys -- Yosys Open SYnthesis Suite
- *
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- */
-
-#ifndef ABC_BLIFPARSE
-#define ABC_BLIFPARSE
-
-#include "kernel/yosys.h"
-
-YOSYS_NAMESPACE_BEGIN
-
-extern RTLIL::Design *abc_parse_blif(FILE *f, std::string dff_name);
-
-YOSYS_NAMESPACE_END
-
-#endif
diff --git a/passes/cmds/Makefile.inc b/passes/cmds/Makefile.inc
index 0e62abbc4..01ada7739 100644
--- a/passes/cmds/Makefile.inc
+++ b/passes/cmds/Makefile.inc
@@ -14,6 +14,7 @@ OBJS += passes/cmds/setattr.o
OBJS += passes/cmds/copy.o
OBJS += passes/cmds/splice.o
OBJS += passes/cmds/scc.o
+OBJS += passes/cmds/torder.o
OBJS += passes/cmds/logcmd.o
OBJS += passes/cmds/tee.o
OBJS += passes/cmds/write_file.o
@@ -21,4 +22,7 @@ OBJS += passes/cmds/connwrappers.o
OBJS += passes/cmds/cover.o
OBJS += passes/cmds/trace.o
OBJS += passes/cmds/plugin.o
+OBJS += passes/cmds/check.o
+OBJS += passes/cmds/qwp.o
+OBJS += passes/cmds/edgetypes.o
diff --git a/passes/cmds/add.cc b/passes/cmds/add.cc
index 054cfc1cb..e698926f9 100644
--- a/passes/cmds/add.cc
+++ b/passes/cmds/add.cc
@@ -2,11 +2,11 @@
* 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
@@ -150,5 +150,5 @@ struct AddPass : public Pass {
}
}
} AddPass;
-
+
PRIVATE_NAMESPACE_END
diff --git a/passes/cmds/check.cc b/passes/cmds/check.cc
new file mode 100644
index 000000000..b3622cb19
--- /dev/null
+++ b/passes/cmds/check.cc
@@ -0,0 +1,162 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "kernel/yosys.h"
+#include "kernel/sigtools.h"
+#include "kernel/celltypes.h"
+#include "kernel/utils.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct CheckPass : public Pass {
+ CheckPass() : Pass("check", "check for obvious problems in the design") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" check [options] [selection]\n");
+ log("\n");
+ log("This pass identifies the following problems in the current design:\n");
+ log("\n");
+ log(" - combinatorial loops\n");
+ log("\n");
+ log(" - two or more conflicting drivers for one wire\n");
+ log("\n");
+ log(" - used wires that do not have a driver\n");
+ log("\n");
+ log("When called with -noinit then this command also checks for wires which have\n");
+ log("the 'init' attribute set.\n");
+ log("\n");
+ log("When called with -assert then the command will produce an error if any\n");
+ log("problems are found in the current design.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ int counter = 0;
+ bool noinit = false;
+ bool assert_mode = false;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ if (args[argidx] == "-noinit") {
+ noinit = true;
+ continue;
+ }
+ if (args[argidx] == "-assert") {
+ assert_mode = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ log_header(design, "Executing CHECK pass (checking for obvious problems).\n");
+
+ for (auto module : design->selected_whole_modules_warn())
+ {
+ if (module->has_processes_warn())
+ continue;
+
+ log("checking module %s..\n", log_id(module));
+
+ SigMap sigmap(module);
+ dict<SigBit, vector<string>> wire_drivers;
+ dict<SigBit, int> wire_drivers_count;
+ pool<SigBit> used_wires;
+ TopoSort<string> topo;
+
+ for (auto cell : module->cells())
+ for (auto &conn : cell->connections()) {
+ SigSpec sig = sigmap(conn.second);
+ bool logic_cell = yosys_celltypes.cell_evaluable(cell->type);
+ if (cell->input(conn.first))
+ for (auto bit : sig)
+ if (bit.wire) {
+ if (logic_cell)
+ topo.edge(stringf("wire %s", log_signal(bit)),
+ stringf("cell %s (%s)", log_id(cell), log_id(cell->type)));
+ used_wires.insert(bit);
+ }
+ if (cell->output(conn.first))
+ for (int i = 0; i < GetSize(sig); i++) {
+ if (logic_cell)
+ topo.edge(stringf("cell %s (%s)", log_id(cell), log_id(cell->type)),
+ stringf("wire %s", log_signal(sig[i])));
+ if (sig[i].wire)
+ wire_drivers[sig[i]].push_back(stringf("port %s[%d] of cell %s (%s)",
+ log_id(conn.first), i, log_id(cell), log_id(cell->type)));
+ }
+ if (!cell->input(conn.first) && cell->output(conn.first))
+ for (auto bit : sig)
+ if (bit.wire) wire_drivers_count[bit]++;
+ }
+
+ for (auto wire : module->wires()) {
+ if (wire->port_input) {
+ SigSpec sig = sigmap(wire);
+ for (int i = 0; i < GetSize(sig); i++)
+ wire_drivers[sig[i]].push_back(stringf("module input %s[%d]", log_id(wire), i));
+ }
+ if (wire->port_output)
+ for (auto bit : sigmap(wire))
+ if (bit.wire) used_wires.insert(bit);
+ if (wire->port_input && !wire->port_output)
+ for (auto bit : sigmap(wire))
+ if (bit.wire) wire_drivers_count[bit]++;
+ if (noinit && wire->attributes.count("\\init")) {
+ log_warning("Wire %s.%s has an unprocessed 'init' attribute.\n", log_id(module), log_id(wire));
+ counter++;
+ }
+ }
+
+ for (auto it : wire_drivers)
+ if (wire_drivers_count[it.first] > 1) {
+ string message = stringf("multiple conflicting drivers for %s.%s:\n", log_id(module), log_signal(it.first));
+ for (auto str : it.second)
+ message += stringf(" %s\n", str.c_str());
+ log_warning("%s", message.c_str());
+ counter++;
+ }
+
+ for (auto bit : used_wires)
+ if (!wire_drivers.count(bit)) {
+ log_warning("Wire %s.%s is used but has no driver.\n", log_id(module), log_signal(bit));
+ counter++;
+ }
+
+ topo.sort();
+ for (auto &loop : topo.loops) {
+ string message = stringf("found logic loop in module %s:\n", log_id(module));
+ for (auto &str : loop)
+ message += stringf(" %s\n", str.c_str());
+ log_warning("%s", message.c_str());
+ counter++;
+ }
+ }
+
+ log("found and reported %d problems.\n", counter);
+
+ if (assert_mode && counter > 0)
+ log_error("Found %d problems in 'check -assert'.\n", counter);
+ }
+} CheckPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/cmds/connect.cc b/passes/cmds/connect.cc
index e17c1b1c3..52611cf44 100644
--- a/passes/cmds/connect.cc
+++ b/passes/cmds/connect.cc
@@ -2,11 +2,11 @@
* 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
@@ -49,8 +49,8 @@ struct ConnectPass : public Pass {
log("\n");
log(" connect [-nomap] [-nounset] -set <lhs-expr> <rhs-expr>\n");
log("\n");
- log("Create a connection. This is equivialent to adding the statement 'assign\n");
- log("<lhs-expr> = <rhs-expr>;' to the verilog input. Per default, all existing\n");
+ log("Create a connection. This is equivalent to adding the statement 'assign\n");
+ log("<lhs-expr> = <rhs-expr>;' to the Verilog input. Per default, all existing\n");
log("drivers for <lhs-expr> are unconnected. This can be overwritten by using\n");
log("the -nounset option.\n");
log("\n");
@@ -185,5 +185,5 @@ struct ConnectPass : public Pass {
log_cmd_error("Expected -set, -unset, or -port.\n");
}
} ConnectPass;
-
+
PRIVATE_NAMESPACE_END
diff --git a/passes/cmds/connwrappers.cc b/passes/cmds/connwrappers.cc
index a65a63644..c9ab226d5 100644
--- a/passes/cmds/connwrappers.cc
+++ b/passes/cmds/connwrappers.cc
@@ -2,11 +2,11 @@
* 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
@@ -158,8 +158,8 @@ struct ConnwrappersPass : public Pass {
log("\n");
log("Wrappers are used in coarse-grain synthesis to wrap cells with smaller ports\n");
log("in wrapper cells with a (larger) constant port size. I.e. the upper bits\n");
- log("of the wrapper outut are signed/unsigned bit extended. This command uses this\n");
- log("knowlege to rewire the inputs of the driven cells to match the output of\n");
+ log("of the wrapper output are signed/unsigned bit extended. This command uses this\n");
+ log("knowledge to rewire the inputs of the driven cells to match the output of\n");
log("the driving cell.\n");
log("\n");
log(" -signed <cell_type> <port_name> <width_param>\n");
@@ -198,12 +198,12 @@ struct ConnwrappersPass : public Pass {
}
extra_args(args, argidx, design);
- log_header("Executing CONNWRAPPERS pass (connect extended ports of wrapper cells).\n");
+ log_header(design, "Executing CONNWRAPPERS pass (connect extended ports of wrapper cells).\n");
for (auto &mod_it : design->modules_)
if (design->selected(mod_it.second))
worker.work(design, mod_it.second);
}
} ConnwrappersPass;
-
+
PRIVATE_NAMESPACE_END
diff --git a/passes/cmds/copy.cc b/passes/cmds/copy.cc
index 459e5b0e7..fb863512b 100644
--- a/passes/cmds/copy.cc
+++ b/passes/cmds/copy.cc
@@ -2,11 +2,11 @@
* 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
@@ -55,5 +55,5 @@ struct CopyPass : public Pass {
design->add(new_mod);
}
} CopyPass;
-
+
PRIVATE_NAMESPACE_END
diff --git a/passes/cmds/cover.cc b/passes/cmds/cover.cc
index 5644066af..1475475c3 100644
--- a/passes/cmds/cover.cc
+++ b/passes/cmds/cover.cc
@@ -124,7 +124,7 @@ struct CoverPass : public Pass {
extra_args(args, argidx, design);
if (do_log) {
- log_header("Printing code coverage counters.\n");
+ log_header(design, "Printing code coverage counters.\n");
log("\n");
}
diff --git a/passes/cmds/delete.cc b/passes/cmds/delete.cc
index b4362887e..6d51d30e7 100644
--- a/passes/cmds/delete.cc
+++ b/passes/cmds/delete.cc
@@ -2,11 +2,11 @@
* 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
@@ -140,5 +140,5 @@ struct DeletePass : public Pass {
}
}
} DeletePass;
-
+
PRIVATE_NAMESPACE_END
diff --git a/passes/cmds/design.cc b/passes/cmds/design.cc
index 9f800c31f..e900e7b9c 100644
--- a/passes/cmds/design.cc
+++ b/passes/cmds/design.cc
@@ -2,11 +2,11 @@
* 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
@@ -80,7 +80,7 @@ struct DesignPass : public Pass {
log("\n");
log(" design -copy-to <name> [-as <new_mod_name>] [selection]\n");
log("\n");
- log("Copy modules from the current design into the soecified one.\n");
+ log("Copy modules from the current design into the specified one.\n");
log("\n");
}
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
diff --git a/passes/cmds/edgetypes.cc b/passes/cmds/edgetypes.cc
new file mode 100644
index 000000000..7b75a009f
--- /dev/null
+++ b/passes/cmds/edgetypes.cc
@@ -0,0 +1,106 @@
+/*
+ * 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 EdgetypePass : public Pass {
+ EdgetypePass() : Pass("edgetypes", "list all types of edges in selection") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" edgetypes [options] [selection]\n");
+ log("\n");
+ log("This command lists all unique types of 'edges' found in the selection. An 'edge'\n");
+ log("is a 4-tuple of source and sink cell type and port name.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ // if (args[argidx] == "-ltr") {
+ // config.ltr = true;
+ // continue;
+ // }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ pool<string> edge_cache;
+
+ for (auto module : design->selected_modules())
+ {
+ SigMap sigmap(module);
+ dict<SigBit, pool<tuple<IdString, IdString, int>>> bit_sources, bit_sinks;
+ pool<std::pair<IdString, IdString>> multibit_ports;
+
+ for (auto cell : module->selected_cells())
+ for (auto conn : cell->connections())
+ {
+ IdString cell_type = cell->type;
+ IdString port_name = conn.first;
+ SigSpec sig = sigmap(conn.second);
+
+ if (GetSize(sig) > 1)
+ multibit_ports.insert(std::pair<IdString, IdString>(cell_type, port_name));
+
+ for (int i = 0; i < GetSize(sig); i++) {
+ if (cell->output(port_name))
+ bit_sources[sig[i]].insert(tuple<IdString, IdString, int>(cell_type, port_name, i));
+ if (cell->input(port_name))
+ bit_sinks[sig[i]].insert(tuple<IdString, IdString, int>(cell_type, port_name, i));
+ }
+ }
+
+ for (auto &it : bit_sources)
+ for (auto &source : it.second)
+ for (auto &sink : bit_sinks[it.first])
+ {
+ auto source_cell_type = std::get<0>(source);
+ auto source_port_name = std::get<1>(source);
+ auto source_bit_index = std::get<2>(source);
+
+ auto sink_cell_type = std::get<0>(sink);
+ auto sink_port_name = std::get<1>(sink);
+ auto sink_bit_index = std::get<2>(sink);
+
+ string source_str = multibit_ports.count(std::pair<IdString, IdString>(source_cell_type, source_port_name)) ?
+ stringf("%s.%s[%d]", log_id(source_cell_type), log_id(source_port_name), source_bit_index) :
+ stringf("%s.%s", log_id(source_cell_type), log_id(source_port_name));
+
+ string sink_str = multibit_ports.count(std::pair<IdString, IdString>(sink_cell_type, sink_port_name)) ?
+ stringf("%s.%s[%d]", log_id(sink_cell_type), log_id(sink_port_name), sink_bit_index) :
+ stringf("%s.%s", log_id(sink_cell_type), log_id(sink_port_name));
+
+ edge_cache.insert(source_str + " " + sink_str);
+ }
+ }
+
+ edge_cache.sort();
+ for (auto &str : edge_cache)
+ log("%s\n", str.c_str());
+ }
+} EdgetypePass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/cmds/plugin.cc b/passes/cmds/plugin.cc
index f7c65bbde..828c671de 100644
--- a/passes/cmds/plugin.cc
+++ b/passes/cmds/plugin.cc
@@ -31,19 +31,23 @@ std::map<std::string, std::string> loaded_plugin_aliases;
#ifdef YOSYS_ENABLE_PLUGINS
void load_plugin(std::string filename, std::vector<std::string> aliases)
{
+ std::string orig_filename = filename;
+
if (filename.find('/') == std::string::npos)
filename = "./" + filename;
if (!loaded_plugins.count(filename)) {
void *hdl = dlopen(filename.c_str(), RTLD_LAZY|RTLD_LOCAL);
+ if (hdl == NULL && orig_filename.find('/') == std::string::npos)
+ hdl = dlopen((proc_share_dirname() + "plugins/" + orig_filename + ".so").c_str(), RTLD_LAZY|RTLD_LOCAL);
if (hdl == NULL)
log_cmd_error("Can't load module `%s': %s\n", filename.c_str(), dlerror());
- loaded_plugins[filename] = hdl;
+ loaded_plugins[orig_filename] = hdl;
Pass::init_register();
}
for (auto &alias : aliases)
- loaded_plugin_aliases[alias] = filename;
+ loaded_plugin_aliases[alias] = orig_filename;
}
#else
void load_plugin(std::string, std::vector<std::string>)
@@ -115,7 +119,7 @@ struct PluginPass : public Pass {
log("\n");
int max_alias_len = 1;
for (auto &it : loaded_plugin_aliases)
- max_alias_len = std::max(max_alias_len, GetSize(it.first));
+ max_alias_len = max(max_alias_len, GetSize(it.first));
for (auto &it : loaded_plugin_aliases)
log("Alias: %-*s %s\n", max_alias_len, it.first.c_str(), it.second.c_str());
}
diff --git a/passes/cmds/qwp.cc b/passes/cmds/qwp.cc
new file mode 100644
index 000000000..1b800b6df
--- /dev/null
+++ b/passes/cmds/qwp.cc
@@ -0,0 +1,870 @@
+/*
+ * 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"
+
+#undef LOG_MATRICES
+#undef PYPLOT_EDGES
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+static uint32_t xorshift32_state;
+
+static double xorshift32()
+{
+ xorshift32_state ^= xorshift32_state << 13;
+ xorshift32_state ^= xorshift32_state >> 17;
+ xorshift32_state ^= xorshift32_state << 5;
+ return (xorshift32_state % 1000000) / 1e6;
+}
+
+struct QwpConfig
+{
+ bool ltr;
+ bool alpha;
+ bool verbose;
+ double grid;
+
+ std::ofstream dump_file;
+
+ QwpConfig() {
+ ltr = false;
+ alpha = false;
+ verbose = false;
+ grid = 1.0 / 16;
+ }
+};
+
+struct QwpWorker
+{
+ QwpConfig &config;
+ Module *module;
+ char direction;
+
+ struct Node {
+ Cell *cell;
+ bool tied, alt_tied;
+
+ // pos = position in current direction
+ // alt_pos = position in the other direction
+ double pos, alt_pos;
+
+ Node() {
+ cell = nullptr;
+ tied = false;
+ pos = xorshift32();
+ alt_tied = false;
+ alt_pos = xorshift32();
+ }
+
+ void tie(double v) {
+ tied = true;
+ pos = v;
+ }
+
+ void alt_tie(double v) {
+ alt_tied = true;
+ alt_pos = v;
+ }
+
+ void swap_alt() {
+ std::swap(tied, alt_tied);
+ std::swap(pos, alt_pos);
+ }
+
+ void proj_left(double midpos) {
+ cell = nullptr;
+ tie(pos > midpos ? midpos : pos);
+ }
+
+ void proj_right(double midpos) {
+ cell = nullptr;
+ tie(pos < midpos ? midpos : pos);
+ }
+ };
+
+ vector<Node> nodes;
+ dict<pair<int, int>, double> edges;
+ dict<Cell*, int> cell_to_node;
+
+ // worker state variables
+ double midpos;
+ double radius;
+ double alt_midpos;
+ double alt_radius;
+
+ QwpWorker(QwpConfig &config, Module *module, char direction = 'x') : config(config), module(module), direction(direction)
+ {
+ log_assert(direction == 'x' || direction == 'y');
+ }
+
+ void load_module()
+ {
+ log_assert(direction == 'x');
+
+ SigMap sigmap(module);
+ dict<SigBit, pool<int>> bits_to_nodes;
+
+ if (config.ltr || config.alpha)
+ {
+ dict<Wire*, double> alpha_inputs, alpha_outputs;
+
+ if (config.alpha)
+ {
+ dict<string, Wire*> alpha_order;
+
+ for (auto wire : module->wires()) {
+ if (wire->port_input || wire->port_output)
+ alpha_order[wire->name.str()] = wire;
+ }
+
+ alpha_order.sort();
+
+ for (auto &it : alpha_order) {
+ if (it.second->port_input) {
+ int idx = GetSize(alpha_inputs);
+ alpha_inputs[it.second] = idx + 0.5;
+ }
+ if (it.second->port_output) {
+ int idx = GetSize(alpha_outputs);
+ alpha_outputs[it.second] = idx + 0.5;
+ }
+ }
+ }
+
+ for (auto wire : module->wires())
+ {
+ if (!wire->port_input && !wire->port_output)
+ continue;
+
+ int idx = GetSize(nodes);
+ nodes.push_back(Node());
+
+ if (config.ltr) {
+ if (wire->port_input)
+ nodes[idx].tie(0.0);
+ else
+ nodes[idx].tie(1.0);
+ }
+
+ if (config.alpha) {
+ if (wire->port_input)
+ nodes[idx].alt_tie(alpha_inputs.at(wire) / GetSize(alpha_inputs));
+ else
+ nodes[idx].alt_tie(alpha_outputs.at(wire) / GetSize(alpha_outputs));
+ }
+
+ for (auto bit : sigmap(wire))
+ bits_to_nodes[bit].insert(idx);
+ }
+ }
+
+ for (auto cell : module->selected_cells())
+ {
+ log_assert(cell_to_node.count(cell) == 0);
+ int idx = GetSize(nodes);
+ nodes.push_back(Node());
+
+ cell_to_node[cell] = GetSize(nodes);
+ nodes[idx].cell = cell;
+
+ for (auto &conn : cell->connections())
+ for (auto bit : sigmap(conn.second))
+ bits_to_nodes[bit].insert(idx);
+ }
+
+ for (auto &it : bits_to_nodes)
+ {
+ if (GetSize(it.second) > 100)
+ continue;
+
+ for (int idx1 : it.second)
+ for (int idx2 : it.second)
+ if (idx1 < idx2)
+ edges[pair<int, int>(idx1, idx2)] += 1.0 / GetSize(it.second);
+ }
+ }
+
+ void solve(bool alt_mode = false)
+ {
+ // A := constraint_matrix
+ // y := constraint_rhs_vector
+ //
+ // AA = A' * A
+ // Ay = A' * y
+ //
+ // M := [AA Ay]
+
+ if (config.verbose)
+ log("> System size: %d^2\n", GetSize(nodes));
+
+ // Row major order
+ int N = GetSize(nodes), N1 = N+1;
+ vector<double> M(N * N1);
+
+ if (config.verbose)
+ log("> Edge constraints: %d\n", GetSize(edges));
+
+ // Edge constraints:
+ // A[i,:] := [ 0 0 .... 0 weight 0 ... 0 -weight 0 ... 0 0], y[i] := 0
+ //
+ // i.e. nonzero columns in A[i,:] at the two node indices.
+ for (auto &edge : edges)
+ {
+ int idx1 = edge.first.first;
+ int idx2 = edge.first.second;
+ double weight = edge.second * (1.0 + xorshift32() * 1e-3);
+
+ M[idx1 + idx1*N1] += weight * weight;
+ M[idx2 + idx2*N1] += weight * weight;
+
+ M[idx1 + idx2*N1] += -weight * weight;
+ M[idx2 + idx1*N1] += -weight * weight;
+ }
+
+ if (config.verbose)
+ log("> Node constraints: %d\n", GetSize(nodes));
+
+ // Node constraints:
+ // A[i,:] := [ 0 0 .... 0 weight 0 ... 0 0], y[i] := weight * pos
+ //
+ // i.e. nonzero column in A[i,:] at the node index
+ //
+ // "tied" nodes have a large weight, pinning them in position. Untied
+ // nodes have a small weight, giving then a tiny preference to stay at
+ // the current position, making sure that AA never is singular.
+ for (int idx = 0; idx < GetSize(nodes); idx++)
+ {
+ auto &node = nodes[idx];
+ double rhs = (alt_mode ? node.alt_pos : node.pos);
+
+ double weight = 1e-3;
+ if (alt_mode ? node.alt_tied : node.tied)
+ weight = 1e3;
+ weight *= (1.0 + xorshift32() * 1e-3);
+
+ M[idx + idx*N1] += weight * weight;
+ M[N + idx*N1] += rhs * weight * weight;
+ }
+
+#ifdef LOG_MATRICES
+ log("\n");
+ for (int i = 0; i < N; i++) {
+ for (int j = 0; j < N+1; j++)
+ log(" %10.2e", M[(N+1)*i + j]);
+ log("\n");
+ }
+#endif
+
+ if (config.verbose)
+ log("> Solving\n");
+
+ // Solve "AA*x = Ay"
+ // (least squares fit for "A*x = y")
+ //
+ // Using gaussian elimination to get M := [Id x]
+
+ vector<int> pivot_cache;
+ vector<int> queue;
+
+ for (int i = 0; i < N; i++)
+ queue.push_back(i);
+
+ // gaussian elimination
+ for (int i = 0; i < N; i++)
+ {
+ if (config.verbose && ((i+1) % (N/15)) == 0)
+ log("> Solved %d%%: %d/%d\n", (100*(i+1))/N, i+1, N);
+
+ // find best row
+ int best_row = queue.front();
+ int best_row_queue_idx = 0;
+ double best_row_absval = 0;
+
+ for (int k = 0; k < GetSize(queue); k++) {
+ int row = queue[k];
+ double absval = fabs(M[i + row*N1]);
+ if (absval > best_row_absval) {
+ best_row = row;
+ best_row_queue_idx = k;
+ best_row_absval = absval;
+ }
+ }
+
+ int row = best_row;
+ pivot_cache.push_back(row);
+
+ queue[best_row_queue_idx] = queue.back();
+ queue.pop_back();
+
+ // normalize row
+ for (int k = i+1; k < N1; k++)
+ M[k + row*N1] /= M[i + row*N1];
+ M[i + row*N1] = 1.0;
+
+ // elimination
+ for (int other_row : queue) {
+ double d = M[i + other_row*N1];
+ for (int k = i+1; k < N1; k++)
+ M[k + other_row*N1] -= d*M[k + row*N1];
+ M[i + other_row*N1] = 0.0;
+ }
+ }
+
+ if (config.verbose)
+ log("> Solved\n");
+
+ log_assert(queue.empty());
+ log_assert(GetSize(pivot_cache) == N);
+
+ // back substitution
+ for (int i = N-1; i >= 0; i--)
+ for (int j = i+1; j < N; j++)
+ {
+ int row = pivot_cache[i];
+ int other_row = pivot_cache[j];
+ M[N + row*N1] -= M[j + row*N1] * M[N + other_row*N1];
+ M[j + row*N1] = 0.0;
+ }
+
+#ifdef LOG_MATRICES
+ log("\n");
+ for (int i = 0; i < N; i++) {
+ for (int j = 0; j < N+1; j++)
+ log(" %10.2e", M[(N+1)*i + j]);
+ log("\n");
+ }
+#endif
+
+ if (config.verbose)
+ log("> Update nodes\n");
+
+ // update node positions
+ for (int i = 0; i < N; i++)
+ {
+ double v = M[(N+1)*i + N];
+ double c = alt_mode ? alt_midpos : midpos;
+ double r = alt_mode ? alt_radius : radius;
+
+ if (std::isfinite(v)) {
+ v = min(v, c+r);
+ v = max(v, c-r);
+ } else {
+ v = c;
+ }
+
+ if (alt_mode) {
+ if (!nodes[i].alt_tied)
+ nodes[i].alt_pos = v;
+ } else {
+ if (!nodes[i].tied)
+ nodes[i].pos = v;
+ }
+ }
+ }
+
+ void log_cell_coordinates(int indent, bool log_all_nodes = false)
+ {
+ for (auto &node : nodes)
+ {
+ if (node.cell == nullptr && !log_all_nodes)
+ continue;
+
+ for (int i = 0; i < indent; i++)
+ log(" ");
+
+ if (direction == 'x')
+ log("X=%.2f, Y=%.2f", node.pos, node.alt_pos);
+ else
+ log("X=%.2f, Y=%.2f", node.alt_pos, node.pos);
+
+ if (node.tied)
+ log(" [%c-tied]", direction);
+
+ if (node.alt_tied)
+ log(" [%c-tied]", direction == 'x' ? 'y' : 'x');
+
+ if (node.cell != nullptr)
+ log(" %s (%s)", log_id(node.cell), log_id(node.cell->type));
+ else
+ log(" (none)");
+
+ log("\n");
+ }
+ }
+
+ void dump_svg(const pool<int> *green_nodes = nullptr, double median = -1)
+ {
+ double x_center = direction == 'x' ? midpos : alt_midpos;
+ double y_center = direction == 'y' ? midpos : alt_midpos;
+
+ double x_radius = direction == 'x' ? radius : alt_radius;
+ double y_radius = direction == 'y' ? radius : alt_radius;
+
+ config.dump_file << stringf("<svg height=\"240\" width=\"470\">\n");
+ config.dump_file << stringf("<rect x=\"0\" y=\"0\" width=\"470\" height=\"240\" style=\"fill:rgb(250,250,200);\" />\n");
+ config.dump_file << stringf("<rect x=\"20\" y=\"20\" width=\"200\" height=\"200\" style=\"fill:rgb(200,200,200);\" />\n");
+ config.dump_file << stringf("<rect x=\"250\" y=\"20\" width=\"200\" height=\"200\" style=\"fill:rgb(200,200,200);\" />\n");
+
+ double win_x = 250 + 200 * (direction == 'x' ? midpos - radius : alt_midpos - alt_radius);
+ double win_y = 20 + 200 * (direction == 'y' ? midpos - radius : alt_midpos - alt_radius);
+
+ double win_w = 200 * (direction == 'x' ? 2*radius : 2*alt_radius);
+ double win_h = 200 * (direction == 'y' ? 2*radius : 2*alt_radius);
+
+ config.dump_file << stringf("<rect x=\"%.2f\" y=\"%.2f\" width=\"%.2f\" height=\"%.2f\" "
+ "style=\"stroke:rgb(0,0,0);stroke-width:1;fill:none\" />\n", win_x, win_y, win_w, win_h);
+
+ if (median >= 0)
+ {
+ double x1 = 20.0, x2 = 220.0, y1 = 20.0, y2 = 220.0;
+
+ if (direction == 'x')
+ x1 = x2 = 120 + 100 * (median - x_center) / x_radius;
+ else
+ y1 = y2 = 120 + 100 * (median - y_center) / y_radius;
+
+ config.dump_file << stringf("<line x1=\"%.2f\" y1=\"%.2f\" x2=\"%.2f\" y2=\"%.2f\" "
+ "style=\"stroke:rgb(150,0,150);stroke-width:1\" />\n", x1, y1, x2, y2);
+ }
+
+ for (auto &edge : edges)
+ {
+ auto &node1 = nodes[edge.first.first];
+ auto &node2 = nodes[edge.first.second];
+
+ double x1 = direction == 'x' ? node1.pos : node1.alt_pos;
+ double y1 = direction == 'y' ? node1.pos : node1.alt_pos;
+
+ double x2 = direction == 'x' ? node2.pos : node2.alt_pos;
+ double y2 = direction == 'y' ? node2.pos : node2.alt_pos;
+
+ x1 = 120 + 100 * (x1 - x_center) / x_radius;
+ y1 = 120 + 100 * (y1 - y_center) / y_radius;
+
+ x2 = 120 + 100 * (x2 - x_center) / x_radius;
+ y2 = 120 + 100 * (y2 - y_center) / y_radius;
+
+ config.dump_file << stringf("<line x1=\"%.2f\" y1=\"%.2f\" x2=\"%.2f\" y2=\"%.2f\" "
+ "style=\"stroke:rgb(0,0,0);stroke-width:1\" />\n", x1, y1, x2, y2);
+ }
+
+ for (int i = 0; i < GetSize(nodes); i++)
+ {
+ auto &node = nodes[i];
+
+ double x = direction == 'x' ? node.pos : node.alt_pos;
+ double y = direction == 'y' ? node.pos : node.alt_pos;
+
+ x = 120 + 100 * (x - x_center) / x_radius;
+ y = 120 + 100 * (y - y_center) / y_radius;
+
+ const char *color = node.cell == nullptr ? "blue" : "red";
+
+ if (green_nodes != nullptr && green_nodes->count(i))
+ color = "green";
+
+ config.dump_file << stringf("<circle cx=\"%.2f\" cy=\"%.2f\" r=\"3\" fill=\"%s\"/>\n", x, y, color);
+ }
+
+ config.dump_file << stringf("</svg>\n");
+ }
+
+ void run_worker(int indent)
+ {
+ int count_cells = 0;
+
+ for (auto &node : nodes)
+ if (node.cell != nullptr)
+ count_cells++;
+
+ for (int i = 0; i < indent; i++)
+ log(" ");
+
+ string range_str;
+
+ if (direction == 'x')
+ range_str = stringf("X=%.2f:%.2f, Y=%.2f:%.2f",
+ midpos - radius, midpos + radius,
+ alt_midpos - alt_radius, alt_midpos + alt_radius);
+ else
+ range_str = stringf("X=%.2f:%.2f, Y=%.2f:%.2f",
+ alt_midpos - alt_radius, alt_midpos + alt_radius,
+ midpos - radius, midpos + radius);
+
+ log("%c-qwp on %s with %d cells, %d nodes, and %d edges.\n", direction,
+ range_str.c_str(), count_cells, GetSize(nodes), GetSize(edges));
+
+ solve();
+ solve(true);
+
+ // detect median position and check for break condition
+
+ vector<pair<double, int>> sorted_pos;
+ for (int i = 0; i < GetSize(nodes); i++)
+ if (nodes[i].cell != nullptr)
+ sorted_pos.push_back(pair<double, int>(nodes[i].pos, i));
+
+ std::sort(sorted_pos.begin(), sorted_pos.end());
+ int median_sidx = GetSize(sorted_pos)/2;
+ double median = sorted_pos[median_sidx].first;
+
+ double left_scale = radius / (median - (midpos - radius));
+ double right_scale = radius / ((midpos + radius) - median);
+
+ if (config.dump_file.is_open())
+ {
+ config.dump_file << stringf("<h4>LSQ %c-Solution for %s:</h4>\n", direction, range_str.c_str());
+
+ pool<int> green_nodes;
+ for (int i = 0; i < median_sidx; i++)
+ green_nodes.insert(sorted_pos[i].second);
+
+ dump_svg(&green_nodes, median);
+ }
+
+ for (auto &node : nodes)
+ {
+ double rel_pos = node.pos - median;
+ if (rel_pos < 0) {
+ node.pos = midpos + left_scale*rel_pos;
+ if (std::isfinite(node.pos)) {
+ node.pos = min(node.pos, midpos);
+ node.pos = max(node.pos, midpos - radius);
+ } else
+ node.pos = midpos - radius/2;
+ } else {
+ node.pos = midpos + right_scale*rel_pos;
+ if (std::isfinite(node.pos)) {
+ node.pos = max(node.pos, midpos);
+ node.pos = min(node.pos, midpos + radius);
+ } else
+ node.pos = midpos + radius/2;
+ }
+ }
+
+ if (GetSize(sorted_pos) < 2 || (2*radius <= config.grid && 2*alt_radius <= config.grid)) {
+ log_cell_coordinates(indent + 1);
+ return;
+ }
+
+ // create child workers
+
+ char child_direction = direction == 'x' ? 'y' : 'x';
+
+ QwpWorker left_worker(config, module, child_direction);
+ QwpWorker right_worker(config, module, child_direction);
+
+ // duplicate nodes into child workers
+
+ dict<int, int> left_nodes, right_nodes;
+
+ for (int k = 0; k < GetSize(sorted_pos); k++)
+ {
+ int i = sorted_pos[k].second;
+
+ if (k < median_sidx) {
+ left_nodes[i] = GetSize(left_worker.nodes);
+ left_worker.nodes.push_back(nodes[i]);
+ if (left_worker.nodes.back().pos > midpos)
+ left_worker.nodes.back().pos = midpos;
+ left_worker.nodes.back().swap_alt();
+ } else {
+ right_nodes[i] = GetSize(right_worker.nodes);
+ right_worker.nodes.push_back(nodes[i]);
+ if (right_worker.nodes.back().pos < midpos)
+ right_worker.nodes.back().pos = midpos;
+ right_worker.nodes.back().swap_alt();
+ }
+ }
+
+ // duplicate edges into child workers, project nodes as needed
+
+ for (auto &edge : edges)
+ {
+ int idx1 = edge.first.first;
+ int idx2 = edge.first.second;
+ double weight = edge.second;
+
+ if (nodes[idx1].cell == nullptr && nodes[idx2].cell == nullptr)
+ continue;
+
+ int left_idx1 = left_nodes.count(idx1) ? left_nodes.at(idx1) : -1;
+ int left_idx2 = left_nodes.count(idx2) ? left_nodes.at(idx2) : -1;
+
+ int right_idx1 = right_nodes.count(idx1) ? right_nodes.at(idx1) : -1;
+ int right_idx2 = right_nodes.count(idx2) ? right_nodes.at(idx2) : -1;
+
+ if (left_idx1 >= 0 && left_worker.nodes[left_idx1].cell && left_idx2 < 0) {
+ left_idx2 = left_nodes[idx2] = GetSize(left_worker.nodes);
+ left_worker.nodes.push_back(nodes[idx2]);
+ left_worker.nodes.back().proj_left(midpos);
+ left_worker.nodes.back().swap_alt();
+ } else
+ if (left_idx2 >= 0 && left_worker.nodes[left_idx2].cell && left_idx1 < 0) {
+ left_idx1 = left_nodes[idx1] = GetSize(left_worker.nodes);
+ left_worker.nodes.push_back(nodes[idx1]);
+ left_worker.nodes.back().proj_left(midpos);
+ left_worker.nodes.back().swap_alt();
+ }
+
+ if (right_idx1 >= 0 && right_worker.nodes[right_idx1].cell && right_idx2 < 0) {
+ right_idx2 = right_nodes[idx2] = GetSize(right_worker.nodes);
+ right_worker.nodes.push_back(nodes[idx2]);
+ right_worker.nodes.back().proj_right(midpos);
+ right_worker.nodes.back().swap_alt();
+ } else
+ if (right_idx2 >= 0 && right_worker.nodes[right_idx2].cell && right_idx1 < 0) {
+ right_idx1 = right_nodes[idx1] = GetSize(right_worker.nodes);
+ right_worker.nodes.push_back(nodes[idx1]);
+ right_worker.nodes.back().proj_right(midpos);
+ right_worker.nodes.back().swap_alt();
+ }
+
+ if (left_idx1 >= 0 && left_idx2 >= 0)
+ left_worker.edges[pair<int, int>(left_idx1, left_idx2)] += weight;
+
+ if (right_idx1 >= 0 && right_idx2 >= 0)
+ right_worker.edges[pair<int, int>(right_idx1, right_idx2)] += weight;
+ }
+
+ // run child workers
+
+ left_worker.midpos = right_worker.midpos = alt_midpos;
+ left_worker.radius = right_worker.radius = alt_radius;
+
+ left_worker.alt_midpos = midpos - radius/2;
+ right_worker.alt_midpos = midpos + radius/2;
+ left_worker.alt_radius = right_worker.alt_radius = radius/2;
+
+ left_worker.run_worker(indent+1);
+ right_worker.run_worker(indent+1);
+
+ // re-integrate results
+
+ for (auto &it : left_nodes)
+ if (left_worker.nodes[it.second].cell != nullptr) {
+ nodes[it.first].pos = left_worker.nodes[it.second].alt_pos;
+ nodes[it.first].alt_pos = left_worker.nodes[it.second].pos;
+ }
+
+ for (auto &it : right_nodes)
+ if (right_worker.nodes[it.second].cell != nullptr) {
+ nodes[it.first].pos = right_worker.nodes[it.second].alt_pos;
+ nodes[it.first].alt_pos = right_worker.nodes[it.second].pos;
+ }
+
+ if (config.dump_file.is_open()) {
+ config.dump_file << stringf("<h4>Final %c-Solution for %s:</h4>\n", direction, range_str.c_str());
+ dump_svg();
+ }
+ }
+
+ void histogram(const vector<double> &values)
+ {
+ if (values.empty()) {
+ log("no data\n");
+ return;
+ }
+
+ double min_value = values.front();
+ double max_value = values.front();
+
+ for (auto &v : values) {
+ min_value = min(min_value, v);
+ max_value = max(max_value, v);
+ }
+
+ if (fabs(max_value - min_value) < 0.001) {
+ log("all values in range %f .. %f\n", min_value, max_value);
+ return;
+ }
+
+ vector<int> buckets(60);
+ int max_bucket_val = 0;
+
+ for (auto &v : values) {
+ int idx = min(int(GetSize(buckets) * (v - min_value) / (max_value - min_value)), GetSize(buckets)-1);
+ max_bucket_val = max(max_bucket_val, ++buckets.at(idx));
+ }
+
+ for (int i = 4; i >= 0; i--) {
+ for (int k = 0; k < GetSize(buckets); k++) {
+ int v = 10 * buckets[k] / max_bucket_val;
+ if (v >= 2*i+1)
+ log(v == 2*i+1 ? "." : ":");
+ else
+ log(i == 0 ? (buckets[k] > 0 ? "," : "_") : " ");
+ }
+ log("\n");
+ }
+ log("%-30f%30f\n", min_value, max_value);
+ }
+
+ void run()
+ {
+ log("\n");
+ log("Running qwp on module %s..\n", log_id(module));
+
+ if (config.dump_file.is_open())
+ config.dump_file << stringf("<h3>QWP protocol for module %s:</h3>\n", log_id(module));
+
+ load_module();
+
+ midpos = 0.5;
+ radius = 0.5;
+ alt_midpos = 0.5;
+ alt_radius = 0.5;
+ run_worker(1);
+
+ for (auto &node : nodes)
+ if (node.cell != nullptr)
+ node.cell->attributes["\\qwp_position"] = stringf("%f %f", node.pos, node.alt_pos);
+
+ vector<double> edge_lengths;
+ vector<double> weighted_edge_lengths;
+
+ double total_edge_length = 0;
+ double total_weighted_edge_length = 0;
+
+ for (auto &edge : edges)
+ {
+ auto &node1 = nodes[edge.first.first];
+ auto &node2 = nodes[edge.first.second];
+
+ double distance = sqrt(pow(node1.pos - node2.pos, 2) + pow(node1.alt_pos - node2.alt_pos, 2));
+ double weighted_distance = distance * edge.second;
+
+ edge_lengths.push_back(distance);
+ weighted_edge_lengths.push_back(weighted_distance);
+
+ total_edge_length += distance;
+ total_weighted_edge_length += weighted_distance;
+ }
+
+ log("\n");
+ log("Summary for module %s:\n", log_id(module));
+ log("Number of edges: %d\n", GetSize(edges));
+ log("Total edge length: %f\n", total_edge_length);
+ log("Total weighted edge length: %f\n", total_weighted_edge_length);
+
+ log("\n");
+ log("Histogram over edge lengths:\n");
+ histogram(edge_lengths);
+
+ log("\n");
+ log("Histogram over weighted edge lengths:\n");
+ histogram(weighted_edge_lengths);
+ }
+};
+
+struct QwpPass : public Pass {
+ QwpPass() : Pass("qwp", "quadratic wirelength placer") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" qwp [options] [selection]\n");
+ log("\n");
+ log("This command runs quadratic wirelength placement on the selected modules and\n");
+ log("annotates the cells in the design with 'qwp_position' attributes.\n");
+ log("\n");
+ log(" -ltr\n");
+ log(" Add left-to-right constraints: constrain all inputs on the left border\n");
+ log(" outputs to the right border.\n");
+ log("\n");
+ log(" -alpha\n");
+ log(" Add constraints for inputs/outputs to be placed in alphanumerical\n");
+ log(" order along the y-axis (top-to-bottom).\n");
+ log("\n");
+ log(" -grid N\n");
+ log(" Number of grid divisions in x- and y-direction. (default=16)\n");
+ log("\n");
+ log(" -dump <html_file_name>\n");
+ log(" Dump a protocol of the placement algorithm to the html file.\n");
+ log("\n");
+ log(" -v\n");
+ log(" Verbose solver output for profiling or debugging\n");
+ log("\n");
+ log("Note: This implementation of a quadratic wirelength placer uses exact\n");
+ log("dense matrix operations. It is only a toy-placer for small circuits.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ QwpConfig config;
+ xorshift32_state = 123456789;
+
+ log_header(design, "Executing QWP pass (quadratic wirelength placer).\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ if (args[argidx] == "-ltr") {
+ config.ltr = true;
+ continue;
+ }
+ if (args[argidx] == "-alpha") {
+ config.alpha = true;
+ continue;
+ }
+ if (args[argidx] == "-v") {
+ config.verbose = true;
+ continue;
+ }
+ if (args[argidx] == "-grid" && argidx+1 < args.size()) {
+ config.grid = 1.0 / atoi(args[++argidx].c_str());
+ continue;
+ }
+ if (args[argidx] == "-dump" && argidx+1 < args.size()) {
+ config.dump_file.open(args[++argidx], std::ofstream::trunc);
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto module : design->selected_modules())
+ {
+ QwpWorker worker(config, module);
+ worker.run();
+
+#ifdef PYPLOT_EDGES
+ log("\n");
+ log("plt.figure(figsize=(10, 10));\n");
+
+ for (auto &edge : worker.edges) {
+ log("plt.plot([%.2f, %.2f], [%.2f, %.2f], \"r-\");\n",
+ worker.nodes[edge.first.first].pos,
+ worker.nodes[edge.first.second].pos,
+ worker.nodes[edge.first.first].alt_pos,
+ worker.nodes[edge.first.second].alt_pos);
+ }
+
+ for (auto &node : worker.nodes) {
+ const char *style = node.cell != nullptr ? "ko" : "ks";
+ log("plt.plot([%.2f], [%.2f], \"%s\");\n", node.pos, node.alt_pos, style);
+ }
+#endif
+ }
+ }
+} QwpPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/cmds/rename.cc b/passes/cmds/rename.cc
index 17d803e96..6a002869b 100644
--- a/passes/cmds/rename.cc
+++ b/passes/cmds/rename.cc
@@ -2,11 +2,11 @@
* 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
@@ -76,12 +76,17 @@ struct RenamePass : public Pass {
log("Assign private names (the ones with $-prefix) to all selected wires and cells\n");
log("with public names. This ignores all selected ports.\n");
log("\n");
+ log(" rename -top new_name\n");
+ log("\n");
+ log("Rename top module.\n");
+ log("\n");
}
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
{
std::string pattern_prefix = "_", pattern_suffix = "_";
bool flag_enumerate = false;
bool flag_hide = false;
+ bool flag_top = false;
bool got_mode = false;
size_t argidx;
@@ -98,6 +103,11 @@ struct RenamePass : public Pass {
got_mode = true;
continue;
}
+ if (arg == "-top" && !got_mode) {
+ flag_top = true;
+ got_mode = true;
+ continue;
+ }
if (arg == "-pattern" && argidx+1 < args.size() && args[argidx+1].find('%') != std::string::npos) {
int pos = args[++argidx].find('%');
pattern_prefix = args[argidx].substr(0, pos);
@@ -171,6 +181,21 @@ struct RenamePass : public Pass {
}
}
else
+ if (flag_top)
+ {
+ if (argidx+1 != args.size())
+ log_cmd_error("Invalid number of arguments!\n");
+
+ IdString new_name = RTLIL::escape_id(args[argidx]);
+ RTLIL::Module *module = design->top_module();
+
+ if (module == NULL)
+ log_cmd_error("No top module found!\n");
+
+ log("Renaming module %s to %s.\n", log_id(module), log_id(new_name));
+ design->rename(module, new_name);
+ }
+ else
{
if (argidx+2 != args.size())
log_cmd_error("Invalid number of arguments!\n");
@@ -203,5 +228,5 @@ struct RenamePass : public Pass {
}
}
} RenamePass;
-
+
PRIVATE_NAMESPACE_END
diff --git a/passes/cmds/scatter.cc b/passes/cmds/scatter.cc
index 1cd55ecb0..f083e1f67 100644
--- a/passes/cmds/scatter.cc
+++ b/passes/cmds/scatter.cc
@@ -2,11 +2,11 @@
* 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
@@ -69,5 +69,5 @@ struct ScatterPass : public Pass {
}
}
} ScatterPass;
-
+
PRIVATE_NAMESPACE_END
diff --git a/passes/cmds/scc.cc b/passes/cmds/scc.cc
index f7f50ab2a..bb6d74474 100644
--- a/passes/cmds/scc.cc
+++ b/passes/cmds/scc.cc
@@ -2,11 +2,11 @@
* 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
@@ -18,7 +18,7 @@
*/
// [[CITE]] Tarjan's strongly connected components algorithm
-// Tarjan, R. E. (1972), "Depth-first search and linear graph algorithms", SIAM Journal on Computing 1 (2): 146–160, doi:10.1137/0201010
+// Tarjan, R. E. (1972), "Depth-first search and linear graph algorithms", SIAM Journal on Computing 1 (2): 146-160, doi:10.1137/0201010
// http://en.wikipedia.org/wiki/Tarjan's_strongly_connected_components_algorithm
#include "kernel/register.h"
@@ -69,10 +69,10 @@ struct SccWorker
for (auto nextCell : cellToNextCell[cell])
if (cellLabels.count(nextCell) == 0) {
run(nextCell, depth+1, maxDepth);
- cellLabels[cell].second = std::min(cellLabels[cell].second, cellLabels[nextCell].second);
+ cellLabels[cell].second = min(cellLabels[cell].second, cellLabels[nextCell].second);
} else
if (cellsOnStack.count(nextCell) > 0 && (maxDepth < 0 || cellDepth[nextCell] + maxDepth > depth)) {
- cellLabels[cell].second = std::min(cellLabels[cell].second, cellLabels[nextCell].second);
+ cellLabels[cell].second = min(cellLabels[cell].second, cellLabels[nextCell].second);
}
if (cellLabels[cell].first == cellLabels[cell].second)
@@ -100,7 +100,8 @@ struct SccWorker
}
}
- SccWorker(RTLIL::Design *design, RTLIL::Module *module, bool allCellTypes, int maxDepth) : design(design), module(module), sigmap(module)
+ SccWorker(RTLIL::Design *design, RTLIL::Module *module, bool nofeedbackMode, bool allCellTypes, int maxDepth) :
+ design(design), module(module), sigmap(module)
{
if (module->processes.size() > 0) {
log("Skipping module %s as it contains processes (run 'proc' pass first).\n", module->name.c_str());
@@ -167,10 +168,22 @@ struct SccWorker
labelCounter = 0;
cellLabels.clear();
- while (workQueue.size() > 0) {
+ while (workQueue.size() > 0)
+ {
RTLIL::Cell *cell = *workQueue.begin();
log_assert(cellStack.size() == 0);
cellDepth.clear();
+
+ if (!nofeedbackMode && cellToNextCell[cell].count(cell)) {
+ log("Found an SCC:");
+ std::set<RTLIL::Cell*> scc;
+ log(" %s", RTLIL::id2cstr(cell->name));
+ cell2scc[cell] = sccList.size();
+ scc.insert(cell);
+ sccList.push_back(scc);
+ log("\n");
+ }
+
run(cell, 0, maxDepth);
}
@@ -212,10 +225,18 @@ struct SccPass : public Pass {
log("This command identifies strongly connected components (aka logic loops) in the\n");
log("design.\n");
log("\n");
+ log(" -expect <num>\n");
+ log(" expect to find exactly <num> SSCs. A different number of SSCs will\n");
+ log(" produce an error.\n");
+ log("\n");
log(" -max_depth <num>\n");
- log(" limit to loops not longer than the specified number of cells. This can\n");
- log(" e.g. be useful in identifying local loops in a module that turns out\n");
- log(" to be one gigantic SCC.\n");
+ log(" limit to loops not longer than the specified number of cells. This\n");
+ log(" can e.g. be useful in identifying small local loops in a module that\n");
+ log(" implements one large SCC.\n");
+ log("\n");
+ log(" -nofeedback\n");
+ log(" do not count cells that have their output fed back into one of their\n");
+ log(" inputs as single-cell scc.\n");
log("\n");
log(" -all_cell_types\n");
log(" Usually this command only considers internal non-memory cells. With\n");
@@ -239,9 +260,11 @@ struct SccPass : public Pass {
std::map<std::string, std::string> setCellAttr, setWireAttr;
bool allCellTypes = false;
bool selectMode = false;
+ bool nofeedbackMode = false;
int maxDepth = -1;
+ int expect = -1;
- log_header("Executing SCC pass (detecting logic loops).\n");
+ log_header(design, "Executing SCC pass (detecting logic loops).\n");
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++) {
@@ -249,6 +272,14 @@ struct SccPass : public Pass {
maxDepth = atoi(args[++argidx].c_str());
continue;
}
+ if (args[argidx] == "-expect" && argidx+1 < args.size()) {
+ expect = atoi(args[++argidx].c_str());
+ continue;
+ }
+ if (args[argidx] == "-nofeedback") {
+ nofeedbackMode = true;
+ continue;
+ }
if (args[argidx] == "-all_cell_types") {
allCellTypes = true;
continue;
@@ -282,16 +313,26 @@ struct SccPass : public Pass {
log_cmd_error("The -set*_attr options are not implemented at the moment!\n");
RTLIL::Selection newSelection(false);
+ int scc_counter = 0;
for (auto &mod_it : design->modules_)
if (design->selected(mod_it.second))
{
- SccWorker worker(design, mod_it.second, allCellTypes, maxDepth);
+ SccWorker worker(design, mod_it.second, nofeedbackMode, allCellTypes, maxDepth);
+ scc_counter += GetSize(worker.sccList);
if (selectMode)
worker.select(newSelection);
}
+ if (expect >= 0) {
+ if (scc_counter == expect)
+ log("Found and expected %d SCCs.\n", scc_counter);
+ else
+ log_error("Found %d SCCs but expected %d.\n", scc_counter, expect);
+ } else
+ log("Found %d SCCs.\n", scc_counter);
+
if (selectMode) {
log_assert(origSelectPos >= 0);
design->selection_stack[origSelectPos] = newSelection;
@@ -299,5 +340,5 @@ struct SccPass : public Pass {
}
}
} SccPass;
-
+
PRIVATE_NAMESPACE_END
diff --git a/passes/cmds/select.cc b/passes/cmds/select.cc
index 247765f0d..d2e1a2e2b 100644
--- a/passes/cmds/select.cc
+++ b/passes/cmds/select.cc
@@ -2,11 +2,11 @@
* 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
@@ -180,6 +180,47 @@ static void select_op_neg(RTLIL::Design *design, RTLIL::Selection &lhs)
lhs.selected_members.swap(new_sel.selected_members);
}
+static int my_xorshift32_rng() {
+ static uint32_t x32 = 314159265;
+ x32 ^= x32 << 13;
+ x32 ^= x32 >> 17;
+ x32 ^= x32 << 5;
+ return x32 & 0x0fffffff;
+}
+
+static void select_op_random(RTLIL::Design *design, RTLIL::Selection &lhs, int count)
+{
+ vector<pair<IdString, IdString>> objects;
+
+ for (auto mod : design->modules())
+ {
+ if (!lhs.selected_module(mod->name))
+ continue;
+
+ for (auto cell : mod->cells()) {
+ if (lhs.selected_member(mod->name, cell->name))
+ objects.push_back(make_pair(mod->name, cell->name));
+ }
+
+ for (auto wire : mod->wires()) {
+ if (lhs.selected_member(mod->name, wire->name))
+ objects.push_back(make_pair(mod->name, wire->name));
+ }
+ }
+
+ lhs = RTLIL::Selection(false);
+
+ while (!objects.empty() && count-- > 0)
+ {
+ int idx = my_xorshift32_rng() % GetSize(objects);
+ lhs.selected_members[objects[idx].first].insert(objects[idx].second);
+ objects[idx] = objects.back();
+ objects.pop_back();
+ }
+
+ lhs.optimize(design);
+}
+
static void select_op_submod(RTLIL::Design *design, RTLIL::Selection &lhs)
{
for (auto &mod_it : design->modules_)
@@ -196,6 +237,27 @@ static void select_op_submod(RTLIL::Design *design, RTLIL::Selection &lhs)
}
}
+static void select_op_cells_to_modules(RTLIL::Design *design, RTLIL::Selection &lhs)
+{
+ RTLIL::Selection new_sel(false);
+ for (auto &mod_it : design->modules_)
+ if (lhs.selected_module(mod_it.first))
+ for (auto &cell_it : mod_it.second->cells_)
+ if (lhs.selected_member(mod_it.first, cell_it.first) && design->modules_.count(cell_it.second->type))
+ new_sel.selected_modules.insert(cell_it.second->type);
+ lhs = new_sel;
+}
+
+static void select_op_module_to_cells(RTLIL::Design *design, RTLIL::Selection &lhs)
+{
+ RTLIL::Selection new_sel(false);
+ for (auto &mod_it : design->modules_)
+ for (auto &cell_it : mod_it.second->cells_)
+ if (design->modules_.count(cell_it.second->type) && lhs.selected_whole_module(cell_it.second->type))
+ new_sel.selected_members[mod_it.first].insert(cell_it.first);
+ lhs = new_sel;
+}
+
static void select_op_fullmod(RTLIL::Design *design, RTLIL::Selection &lhs)
{
lhs.optimize(design);
@@ -365,7 +427,7 @@ static int parse_comma_list(std::set<RTLIL::IdString> &tokens, std::string str,
}
}
-static int select_op_expand(RTLIL::Design *design, RTLIL::Selection &lhs, std::vector<expand_rule_t> &rules, std::set<RTLIL::IdString> &limits, int max_objects, char mode, CellTypes &ct)
+static int select_op_expand(RTLIL::Design *design, RTLIL::Selection &lhs, std::vector<expand_rule_t> &rules, std::set<RTLIL::IdString> &limits, int max_objects, char mode, CellTypes &ct, bool eval_only)
{
int sel_objects = 0;
bool is_input, is_output;
@@ -401,6 +463,8 @@ static int select_op_expand(RTLIL::Design *design, RTLIL::Selection &lhs, std::v
for (auto &conn : cell.second->connections())
{
char last_mode = '-';
+ if (eval_only && !yosys_celltypes.cell_evaluable(cell.second->type))
+ goto exclude_match;
for (auto &rule : rules) {
last_mode = rule.mode;
if (rule.cell_types.size() > 0 && rule.cell_types.count(cell.second->type) == 0)
@@ -433,9 +497,10 @@ static int select_op_expand(RTLIL::Design *design, RTLIL::Selection &lhs, std::v
return sel_objects;
}
-static void select_op_expand(RTLIL::Design *design, std::string arg, char mode)
+static void select_op_expand(RTLIL::Design *design, std::string arg, char mode, bool eval_only)
{
- int pos = mode == 'x' ? 2 : 3, levels = 1, rem_objects = -1;
+ int pos = (mode == 'x' ? 2 : 3) + (eval_only ? 1 : 0);
+ int levels = 1, rem_objects = -1;
std::vector<expand_rule_t> rules;
std::set<RTLIL::IdString> limits;
@@ -526,7 +591,7 @@ static void select_op_expand(RTLIL::Design *design, std::string arg, char mode)
#endif
while (levels-- > 0 && rem_objects != 0) {
- int num_objects = select_op_expand(design, work_stack.back(), rules, limits, rem_objects, mode, ct);
+ int num_objects = select_op_expand(design, work_stack.back(), rules, limits, rem_objects, mode, ct, eval_only);
if (num_objects == 0)
break;
rem_objects -= num_objects;
@@ -540,7 +605,7 @@ static void select_filter_active_mod(RTLIL::Design *design, RTLIL::Selection &se
{
if (design->selected_active_module.empty())
return;
-
+
if (sel.full_selection) {
sel.full_selection = false;
sel.selected_modules.clear();
@@ -610,11 +675,27 @@ static void select_stmt(RTLIL::Design *design, std::string arg)
select_op_intersect(design, work_stack[work_stack.size()-2], work_stack[work_stack.size()-1]);
work_stack.pop_back();
} else
+ if (arg.size() >= 2 && arg[0] == '%' && arg[1] == 'R') {
+ if (work_stack.size() < 1)
+ log_cmd_error("Must have at least one element on the stack for operator %%R.\n");
+ int count = arg.size() > 2 ? atoi(arg.c_str() + 2) : 1;
+ select_op_random(design, work_stack[work_stack.size()-1], count);
+ } else
if (arg == "%s") {
if (work_stack.size() < 1)
log_cmd_error("Must have at least one element on the stack for operator %%s.\n");
select_op_submod(design, work_stack[work_stack.size()-1]);
} else
+ if (arg == "%M") {
+ if (work_stack.size() < 1)
+ log_cmd_error("Must have at least one element on the stack for operator %%M.\n");
+ select_op_cells_to_modules(design, work_stack[work_stack.size()-1]);
+ } else
+ if (arg == "%C") {
+ if (work_stack.size() < 1)
+ log_cmd_error("Must have at least one element on the stack for operator %%M.\n");
+ select_op_module_to_cells(design, work_stack[work_stack.size()-1]);
+ } else
if (arg == "%c") {
if (work_stack.size() < 1)
log_cmd_error("Must have at least one element on the stack for operator %%c.\n");
@@ -633,17 +714,32 @@ static void select_stmt(RTLIL::Design *design, std::string arg)
if (arg == "%x" || (arg.size() > 2 && arg.substr(0, 2) == "%x" && (arg[2] == ':' || arg[2] == '*' || arg[2] == '.' || ('0' <= arg[2] && arg[2] <= '9')))) {
if (work_stack.size() < 1)
log_cmd_error("Must have at least one element on the stack for operator %%x.\n");
- select_op_expand(design, arg, 'x');
+ select_op_expand(design, arg, 'x', false);
} else
if (arg == "%ci" || (arg.size() > 3 && arg.substr(0, 3) == "%ci" && (arg[3] == ':' || arg[3] == '*' || arg[3] == '.' || ('0' <= arg[3] && arg[3] <= '9')))) {
if (work_stack.size() < 1)
log_cmd_error("Must have at least one element on the stack for operator %%ci.\n");
- select_op_expand(design, arg, 'i');
+ select_op_expand(design, arg, 'i', false);
} else
if (arg == "%co" || (arg.size() > 3 && arg.substr(0, 3) == "%co" && (arg[3] == ':' || arg[3] == '*' || arg[3] == '.' || ('0' <= arg[3] && arg[3] <= '9')))) {
if (work_stack.size() < 1)
log_cmd_error("Must have at least one element on the stack for operator %%co.\n");
- select_op_expand(design, arg, 'o');
+ select_op_expand(design, arg, 'o', false);
+ } else
+ if (arg == "%xe" || (arg.size() > 3 && arg.substr(0, 3) == "%xe" && (arg[3] == ':' || arg[3] == '*' || arg[3] == '.' || ('0' <= arg[3] && arg[3] <= '9')))) {
+ if (work_stack.size() < 1)
+ log_cmd_error("Must have at least one element on the stack for operator %%xe.\n");
+ select_op_expand(design, arg, 'x', true);
+ } else
+ if (arg == "%cie" || (arg.size() > 4 && arg.substr(0, 4) == "%cie" && (arg[4] == ':' || arg[4] == '*' || arg[4] == '.' || ('0' <= arg[4] && arg[4] <= '9')))) {
+ if (work_stack.size() < 1)
+ log_cmd_error("Must have at least one element on the stack for operator %%cie.\n");
+ select_op_expand(design, arg, 'i', true);
+ } else
+ if (arg == "%coe" || (arg.size() > 4 && arg.substr(0, 4) == "%coe" && (arg[4] == ':' || arg[4] == '*' || arg[4] == '.' || ('0' <= arg[4] && arg[4] <= '9')))) {
+ if (work_stack.size() < 1)
+ log_cmd_error("Must have at least one element on the stack for operator %%coe.\n");
+ select_op_expand(design, arg, 'o', true);
} else
log_cmd_error("Unknown selection operator '%s'.\n", arg.c_str());
if (work_stack.size() >= 1)
@@ -684,7 +780,7 @@ static void select_stmt(RTLIL::Design *design, std::string arg)
select_filter_active_mod(design, work_stack.back());
return;
}
-
+
sel.full_selection = false;
for (auto &mod_it : design->modules_)
{
@@ -856,7 +952,7 @@ struct SelectPass : public Pass {
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
log(" select [ -add | -del | -set <name> ] {-read <filename> | <selection>}\n");
- log(" select [ -assert-none | -assert-any ] {-read <filename> | <selection>}\n");
+ log(" select [ <assert_option> ] {-read <filename> | <selection>}\n");
log(" select [ -list | -write <filename> | -count | -clear ]\n");
log(" select -module <modname>\n");
log("\n");
@@ -892,6 +988,14 @@ struct SelectPass : public Pass {
log(" do not modify the current selection. instead assert that the given\n");
log(" selection contains exactly N objects.\n");
log("\n");
+ log(" -assert-max N\n");
+ log(" do not modify the current selection. instead assert that the given\n");
+ log(" selection contains less than or exactly N objects.\n");
+ log("\n");
+ log(" -assert-min N\n");
+ log(" do not modify the current selection. instead assert that the given\n");
+ log(" selection contains at least N objects.\n");
+ log("\n");
log(" -list\n");
log(" list all objects in the current selection\n");
log("\n");
@@ -1012,7 +1116,7 @@ struct SelectPass : public Pass {
log(" like %%d but swap the roles of two top sets on the stack\n");
log("\n");
log(" %%c\n");
- log(" create a copy of the top set rom the stack and push it\n");
+ log(" create a copy of the top set from the stack and push it\n");
log("\n");
log(" %%x[<num1>|*][.<num2>][:<rule>[:<rule>..]]\n");
log(" expand top set <num1> num times according to the specified rules.\n");
@@ -1029,19 +1133,31 @@ struct SelectPass : public Pass {
log("\n");
log(" %%ci[<num1>|*][.<num2>][:<rule>[:<rule>..]]\n");
log(" %%co[<num1>|*][.<num2>][:<rule>[:<rule>..]]\n");
- log(" simmilar to %%x, but only select input (%%ci) or output cones (%%co)\n");
+ log(" similar to %%x, but only select input (%%ci) or output cones (%%co)\n");
+ log("\n");
+ log(" %%xe[...] %%cie[...] %%coe\n");
+ log(" like %%x, %%ci, and %%co but only consider combinatorial cells\n");
log("\n");
log(" %%a\n");
log(" expand top set by selecting all wires that are (at least in part)\n");
log(" aliases for selected wires.\n");
log("\n");
log(" %%s\n");
- log(" expand top set by adding all modules of instantiated cells in selected\n");
+ log(" expand top set by adding all modules that implement cells in selected\n");
log(" modules\n");
log("\n");
log(" %%m\n");
log(" expand top set by selecting all modules that contain selected objects\n");
log("\n");
+ log(" %%M\n");
+ log(" select modules that implement selected cells\n");
+ log("\n");
+ log(" %%C\n");
+ log(" select cells that implement selected modules\n");
+ log("\n");
+ log(" %%R[<num>]\n");
+ log(" select <num> random objects from top selection (default 1)\n");
+ log("\n");
log("Example: the following command selects all wires that are connected to a\n");
log("'GATE' input of a 'SWITCH' cell:\n");
log("\n");
@@ -1060,6 +1176,8 @@ struct SelectPass : public Pass {
bool assert_none = false;
bool assert_any = false;
int assert_count = -1;
+ int assert_max = -1;
+ int assert_min = -1;
std::string write_file, read_file;
std::string set_name, sel_str;
@@ -1089,6 +1207,14 @@ struct SelectPass : public Pass {
assert_count = atoi(args[++argidx].c_str());
continue;
}
+ if (arg == "-assert-max" && argidx+1 < args.size()) {
+ assert_max = atoi(args[++argidx].c_str());
+ continue;
+ }
+ if (arg == "-assert-min" && argidx+1 < args.size()) {
+ assert_min = atoi(args[++argidx].c_str());
+ continue;
+ }
if (arg == "-clear") {
clear_mode = true;
continue;
@@ -1165,14 +1291,14 @@ struct SelectPass : public Pass {
if (none_mode && args.size() != 2)
log_cmd_error("Option -none can not be combined with any other options.\n");
- if (add_mode + del_mode + assert_none + assert_any + (assert_count >= 0) > 1)
- log_cmd_error("Options -add, -del, -assert-none, -assert-any or -assert-count can not be combined.\n");
+ if (add_mode + del_mode + assert_none + assert_any + (assert_count >= 0) + (assert_max >= 0) + (assert_min >= 0) > 1)
+ log_cmd_error("Options -add, -del, -assert-none, -assert-any, assert-count, -assert-max or -assert-min can not be combined.\n");
- if ((list_mode || !write_file.empty() || count_mode) && (add_mode || del_mode || assert_none || assert_any || assert_count >= 0))
- log_cmd_error("Options -list, -write and -count can not be combined with -add, -del, -assert-none, -assert-any or -assert-count.\n");
+ if ((list_mode || !write_file.empty() || count_mode) && (add_mode || del_mode || assert_none || assert_any || assert_count >= 0 || assert_max >= 0 || assert_min >= 0))
+ log_cmd_error("Options -list, -write and -count can not be combined with -add, -del, -assert-none, -assert-any, assert-count, -assert-max, or -assert-min.\n");
- if (!set_name.empty() && (list_mode || !write_file.empty() || count_mode || add_mode || del_mode || assert_none || assert_any || assert_count >= 0))
- log_cmd_error("Option -set can not be combined with -list, -write, -count, -add, -del, -assert-none, -assert-any or -assert-count.\n");
+ if (!set_name.empty() && (list_mode || !write_file.empty() || count_mode || add_mode || del_mode || assert_none || assert_any || assert_count >= 0 || assert_max >= 0 || assert_min >= 0))
+ log_cmd_error("Option -set can not be combined with -list, -write, -count, -add, -del, -assert-none, -assert-any, -assert-count, -assert-max, or -assert-min.\n");
if (work_stack.size() == 0 && got_module) {
RTLIL::Selection sel;
@@ -1261,8 +1387,9 @@ struct SelectPass : public Pass {
{
if (work_stack.size() == 0)
log_cmd_error("No selection to check.\n");
+ work_stack.back().optimize(design);
if (!work_stack.back().empty())
- log_error("Assertation failed: selection is not empty:%s\n", sel_str.c_str());
+ log_error("Assertion failed: selection is not empty:%s\n", sel_str.c_str());
return;
}
@@ -1270,12 +1397,13 @@ struct SelectPass : public Pass {
{
if (work_stack.size() == 0)
log_cmd_error("No selection to check.\n");
+ work_stack.back().optimize(design);
if (work_stack.back().empty())
- log_error("Assertation failed: selection is empty:%s\n", sel_str.c_str());
+ log_error("Assertion failed: selection is empty:%s\n", sel_str.c_str());
return;
}
- if (assert_count >= 0)
+ if (assert_count >= 0 || assert_max >= 0 || assert_min >= 0)
{
int total_count = 0;
if (work_stack.size() == 0)
@@ -1297,9 +1425,15 @@ struct SelectPass : public Pass {
if (sel->selected_member(mod_it.first, it.first))
total_count++;
}
- if (assert_count != total_count)
- log_error("Assertation failed: selection contains %d elements instead of the asserted %d:%s\n",
+ if (assert_count >= 0 && assert_count != total_count)
+ log_error("Assertion failed: selection contains %d elements instead of the asserted %d:%s\n",
total_count, assert_count, sel_str.c_str());
+ if (assert_max >= 0 && assert_max < total_count)
+ log_error("Assertion failed: selection contains %d elements, more than the maximum number %d:%s\n",
+ total_count, assert_max, sel_str.c_str());
+ if (assert_min >= 0 && assert_min > total_count)
+ log_error("Assertion failed: selection contains %d elements, less than the minimum number %d:%s\n",
+ total_count, assert_min, sel_str.c_str());
return;
}
@@ -1328,7 +1462,7 @@ struct SelectPass : public Pass {
design->selection_stack.back().optimize(design);
}
} SelectPass;
-
+
struct CdPass : public Pass {
CdPass() : Pass("cd", "a shortcut for 'select -module <name>'") { }
virtual void help()
@@ -1343,7 +1477,7 @@ struct CdPass : public Pass {
log(" cd <cellname>\n");
log("\n");
log("When no module with the specified name is found, but there is a cell\n");
- log("with the specified name in the current module, then this is equivialent\n");
+ log("with the specified name in the current module, then this is equivalent\n");
log("to 'cd <celltype>'.\n");
log("\n");
log(" cd ..\n");
@@ -1400,7 +1534,7 @@ static void log_matches(const char *title, Module *module, T list)
log(" %s\n", RTLIL::id2cstr(id));
}
}
-
+
struct LsPass : public Pass {
LsPass() : Pass("ls", "list modules or objects in modules") { }
virtual void help()
@@ -1444,5 +1578,5 @@ struct LsPass : public Pass {
}
}
} LsPass;
-
+
PRIVATE_NAMESPACE_END
diff --git a/passes/cmds/setattr.cc b/passes/cmds/setattr.cc
index 9a6d8a038..9b05ae32f 100644
--- a/passes/cmds/setattr.cc
+++ b/passes/cmds/setattr.cc
@@ -2,11 +2,11 @@
* 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
@@ -32,25 +32,20 @@ struct setunset_t
setunset_t(std::string unset_name) : name(RTLIL::escape_id(unset_name)), value(), unset(true) { }
- setunset_t(std::string set_name, std::vector<std::string> args, size_t &argidx) : name(RTLIL::escape_id(set_name)), value(), unset(false)
+ setunset_t(std::string set_name, std::string set_value) : name(RTLIL::escape_id(set_name)), value(), unset(false)
{
- if (!args[argidx].empty() && args[argidx][0] == '"') {
- std::string str = args[argidx++].substr(1);
- while (str.size() != 0 && str[str.size()-1] != '"' && argidx < args.size())
- str += args[argidx++];
- if (str.size() != 0 && str[str.size()-1] == '"')
- str = str.substr(0, str.size()-1);
- value = RTLIL::Const(str);
+ if (set_value.substr(0, 1) == "\"" && set_value.substr(GetSize(set_value)-1) == "\"") {
+ value = RTLIL::Const(set_value.substr(1, GetSize(set_value)-2));
} else {
RTLIL::SigSpec sig_value;
- if (!RTLIL::SigSpec::parse(sig_value, NULL, args[argidx++]))
- log_cmd_error("Can't decode value '%s'!\n", args[argidx-1].c_str());
+ if (!RTLIL::SigSpec::parse(sig_value, NULL, set_value))
+ log_cmd_error("Can't decode value '%s'!\n", set_value.c_str());
value = sig_value.as_const();
}
}
};
-static void do_setunset(dict<RTLIL::IdString, RTLIL::Const> &attrs, std::vector<setunset_t> &list)
+static void do_setunset(dict<RTLIL::IdString, RTLIL::Const> &attrs, const std::vector<setunset_t> &list)
{
for (auto &item : list)
if (item.unset)
@@ -84,9 +79,9 @@ struct SetattrPass : public Pass {
{
std::string arg = args[argidx];
if (arg == "-set" && argidx+2 < args.size()) {
- argidx += 2;
- setunset_list.push_back(setunset_t(args[argidx-1], args, argidx));
- argidx--;
+ string set_key = args[++argidx];
+ string set_val = args[++argidx];
+ setunset_list.push_back(setunset_t(set_key, set_val));
continue;
}
if (arg == "-unset" && argidx+1 < args.size()) {
@@ -132,7 +127,7 @@ struct SetattrPass : public Pass {
}
}
} SetattrPass;
-
+
struct SetparamPass : public Pass {
SetparamPass() : Pass("setparam", "set/unset parameters on objects") { }
virtual void help()
@@ -154,9 +149,9 @@ struct SetparamPass : public Pass {
{
std::string arg = args[argidx];
if (arg == "-set" && argidx+2 < args.size()) {
- argidx += 2;
- setunset_list.push_back(setunset_t(args[argidx-1], args, argidx));
- argidx--;
+ string set_key = args[++argidx];
+ string set_val = args[++argidx];
+ setunset_list.push_back(setunset_t(set_key, set_val));
continue;
}
if (arg == "-unset" && argidx+1 < args.size()) {
@@ -180,5 +175,87 @@ struct SetparamPass : public Pass {
}
}
} SetparamPass;
-
+
+struct ChparamPass : public Pass {
+ ChparamPass() : Pass("chparam", "re-evaluate modules with new parameters") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" chparam [ -set name value ]... [selection]\n");
+ log("\n");
+ log("Re-evaluate the selected modules with new parameters. String values must be\n");
+ log("passed in double quotes (\").\n");
+ log("\n");
+ log("\n");
+ log(" chparam -list [selection]\n");
+ log("\n");
+ log("List the available parameters of the selected modules.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ std::vector<setunset_t> setunset_list;
+ dict<RTLIL::IdString, RTLIL::Const> new_parameters;
+ bool list_mode = false;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ std::string arg = args[argidx];
+ if (arg == "-set" && argidx+2 < args.size()) {
+ string set_key = args[++argidx];
+ string set_val = args[++argidx];
+ setunset_list.push_back(setunset_t(set_key, set_val));
+ continue;
+ }
+ if (arg == "-list") {
+ list_mode = true;
+ continue;
+ }
+ break;
+ }
+
+ for (int i = argidx; i < GetSize(args); i++)
+ if (design->module("$abstract\\" + args[i]) != nullptr &&
+ design->module(RTLIL::escape_id(args[i])) == nullptr)
+ args[i] = "$abstract\\" + args[i];
+
+ extra_args(args, argidx, design);
+
+ do_setunset(new_parameters, setunset_list);
+
+ if (list_mode) {
+ if (!new_parameters.empty())
+ log_cmd_error("The options -set and -list cannot be used together.\n");
+ for (auto module : design->selected_modules()) {
+ log("%s:\n", log_id(module));
+ for (auto param : module->avail_parameters)
+ log(" %s\n", log_id(param));
+ }
+ return;
+ }
+
+ pool<IdString> modnames, old_modnames;
+ for (auto module : design->selected_whole_modules_warn()) {
+ modnames.insert(module->name);
+ old_modnames.insert(module->name);
+ }
+ modnames.sort();
+
+ for (auto modname : modnames) {
+ Module *module = design->module(modname);
+ Module *new_module = design->module(module->derive(design, new_parameters));
+ if (module != new_module) {
+ Module *m = new_module->clone();
+ m->name = module->name;
+ design->remove(module);
+ design->add(m);
+ }
+ if (old_modnames.count(new_module->name) == 0)
+ design->remove(new_module);
+ }
+ }
+} ChparamPass;
+
PRIVATE_NAMESPACE_END
diff --git a/passes/cmds/setundef.cc b/passes/cmds/setundef.cc
index b9a29b7d2..26b2eb87d 100644
--- a/passes/cmds/setundef.cc
+++ b/passes/cmds/setundef.cc
@@ -2,11 +2,11 @@
* 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
@@ -79,11 +79,15 @@ struct SetundefPass : public Pass {
log(" replace with random bits using the specified integer als seed\n");
log(" value for the random number generator.\n");
log("\n");
+ log(" -init\n");
+ log(" also create/update init values for flip-flops\n");
+ log("\n");
}
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
{
bool got_value = false;
bool undriven_mode = false;
+ bool init_mode = false;
SetundefWorker worker;
size_t argidx;
@@ -103,6 +107,10 @@ struct SetundefPass : public Pass {
worker.next_bit_mode = 1;
continue;
}
+ if (args[argidx] == "-init") {
+ init_mode = true;
+ continue;
+ }
if (args[argidx] == "-random" && !got_value && argidx+1 < args.size()) {
got_value = true;
worker.next_bit_mode = 2;
@@ -118,12 +126,8 @@ struct SetundefPass : public Pass {
if (!got_value)
log_cmd_error("One of the options -zero, -one, or -random <seed> must be specified.\n");
- for (auto &mod_it : design->modules_)
+ for (auto module : design->selected_modules())
{
- RTLIL::Module *module = mod_it.second;
- if (!design->selected(module))
- continue;
-
if (undriven_mode)
{
if (!module->processes.empty())
@@ -151,9 +155,89 @@ struct SetundefPass : public Pass {
}
}
+ if (init_mode)
+ {
+ SigMap sigmap(module);
+ pool<SigBit> ffbits;
+ pool<Wire*> initwires;
+
+ pool<IdString> fftypes;
+ fftypes.insert("$dff");
+ fftypes.insert("$dffe");
+ fftypes.insert("$dffsr");
+ fftypes.insert("$adff");
+
+ std::vector<char> list_np = {'N', 'P'}, list_01 = {'0', '1'};
+
+ for (auto c1 : list_np)
+ fftypes.insert(stringf("$_DFF_%c_", c1));
+
+ for (auto c1 : list_np)
+ for (auto c2 : list_np)
+ fftypes.insert(stringf("$_DFFE_%c%c_", c1, c2));
+
+ for (auto c1 : list_np)
+ for (auto c2 : list_np)
+ for (auto c3 : list_01)
+ fftypes.insert(stringf("$_DFF_%c%c%c_", c1, c2, c3));
+
+ for (auto c1 : list_np)
+ for (auto c2 : list_np)
+ for (auto c3 : list_np)
+ fftypes.insert(stringf("$_DFFSR_%c%c%c_", c1, c2, c3));
+
+ for (auto cell : module->cells())
+ {
+ if (!fftypes.count(cell->type))
+ continue;
+
+ for (auto bit : sigmap(cell->getPort("\\Q")))
+ ffbits.insert(bit);
+ }
+
+ for (auto wire : module->wires())
+ {
+ if (!wire->attributes.count("\\init"))
+ continue;
+
+ for (auto bit : sigmap(wire))
+ ffbits.erase(bit);
+
+ initwires.insert(wire);
+ }
+
+ for (int wire_types = 0; wire_types < 2; wire_types++)
+ for (auto wire : module->wires())
+ {
+ if (wire->name[0] == (wire_types ? '\\' : '$'))
+ next_wire:
+ continue;
+
+ for (auto bit : sigmap(wire))
+ if (!ffbits.count(bit))
+ goto next_wire;
+
+ for (auto bit : sigmap(wire))
+ ffbits.erase(bit);
+
+ initwires.insert(wire);
+ }
+
+ for (auto wire : initwires)
+ {
+ Const &initval = wire->attributes["\\init"];
+
+ for (int i = 0; i < GetSize(wire); i++)
+ if (GetSize(initval) <= i)
+ initval.bits.push_back(worker.next_bit());
+ else if (initval.bits[i] == State::Sx)
+ initval.bits[i] = worker.next_bit();
+ }
+ }
+
module->rewrite_sigspecs(worker);
}
}
} SetundefPass;
-
+
PRIVATE_NAMESPACE_END
diff --git a/passes/cmds/show.cc b/passes/cmds/show.cc
index 63da29b94..87504a33f 100644
--- a/passes/cmds/show.cc
+++ b/passes/cmds/show.cc
@@ -2,11 +2,11 @@
* 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
@@ -41,7 +41,7 @@ struct ShowWorker
{
CellTypes ct;
- std::vector<std::string> dot_escape_store;
+ vector<shared_str> dot_escape_store;
std::map<RTLIL::IdString, int> dot_id2num_store;
std::map<RTLIL::IdString, int> autonames;
int single_idx_count;
@@ -552,7 +552,7 @@ struct ShowWorker
continue;
if (design->selected_whole_module(module->name)) {
if (module->get_bool_attribute("\\blackbox")) {
- log("Skipping blackbox module %s.\n", id2cstr(module->name));
+ // log("Skipping blackbox module %s.\n", id2cstr(module->name));
continue;
} else
if (module->cells_.empty() && module->connections().empty() && module->processes.empty()) {
@@ -590,6 +590,10 @@ struct ShowPass : public Pass {
log(" inputs or outputs. This option can be used multiple times to specify\n");
log(" more than one library.\n");
log("\n");
+ log(" note: in most cases it is better to load the library before calling\n");
+ log(" show with 'read_verilog -lib <filename>'. it is also possible to\n");
+ log(" load liberty files with 'read_liberty -lib <filename>'.\n");
+ log("\n");
log(" -prefix <prefix>\n");
log(" generate <prefix>.* instead of ~/.yosys_show.*\n");
log("\n");
@@ -606,7 +610,7 @@ struct ShowPass : public Pass {
log(" -colors <seed>\n");
log(" Randomly assign colors to the wires. The integer argument is the seed\n");
log(" for the random number generator. Change the seed value if the colored\n");
- log(" graph still is ambigous. A seed of zero deactivates the coloring.\n");
+ log(" graph still is ambiguous. A seed of zero deactivates the coloring.\n");
log("\n");
log(" -colorattr <attribute_name>\n");
log(" Use the specified attribute to assign colors. A unique color is\n");
@@ -616,7 +620,7 @@ struct ShowPass : public Pass {
log(" annotate busses with a label indicating the width of the bus.\n");
log("\n");
log(" -signed\n");
- log(" mark ports (A, B) that are declarted as signed (using the [AB]_SIGNED\n");
+ log(" mark ports (A, B) that are declared as signed (using the [AB]_SIGNED\n");
log(" cell parameter) with an asterisk next to the port name.\n");
log("\n");
log(" -stretch\n");
@@ -630,7 +634,7 @@ struct ShowPass : public Pass {
log(" enumerate objects with internal ($-prefixed) names\n");
log("\n");
log(" -long\n");
- log(" do not abbeviate objects with internal ($-prefixed) names\n");
+ log(" do not abbreviate objects with internal ($-prefixed) names\n");
log("\n");
log(" -notitle\n");
log(" do not add the module name as graph title to the dot file\n");
@@ -641,18 +645,26 @@ struct ShowPass : public Pass {
log("The generated output files are '~/.yosys_show.dot' and '~/.yosys_show.<format>',\n");
log("unless another prefix is specified using -prefix <prefix>.\n");
log("\n");
+ log("Yosys on Windows and YosysJS use different defaults: The output is written\n");
+ log("to 'show.dot' in the current directory and new viewer is launched.\n");
+ log("\n");
}
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
{
- log_header("Generating Graphviz representation of design.\n");
+ log_header(design, "Generating Graphviz representation of design.\n");
log_push();
std::vector<std::pair<std::string, RTLIL::Selection>> color_selections;
std::vector<std::pair<std::string, RTLIL::Selection>> label_selections;
+#if defined(EMSCRIPTEN) || defined(_WIN32)
+ std::string format = "dot";
+ std::string prefix = "show";
+#else
std::string format;
- std::string viewer_exe;
std::string prefix = stringf("%s/.yosys_show", getenv("HOME") ? getenv("HOME") : ".");
+#endif
+ std::string viewer_exe;
std::vector<std::string> libfiles;
std::vector<RTLIL::Design*> libs;
uint32_t colorSeed = 0;
@@ -661,7 +673,7 @@ struct ShowPass : public Pass {
bool flag_stretch = false;
bool flag_pause = false;
bool flag_enum = false;
- bool flag_abbeviate = true;
+ bool flag_abbreviate = true;
bool flag_notitle = false;
RTLIL::IdString colorattr;
@@ -731,12 +743,12 @@ struct ShowPass : public Pass {
}
if (arg == "-enum") {
flag_enum = true;
- flag_abbeviate = false;
+ flag_abbreviate = false;
continue;
}
if (arg == "-long") {
flag_enum = false;
- flag_abbeviate = false;
+ flag_abbreviate = false;
continue;
}
if (arg == "-notitle") {
@@ -772,7 +784,7 @@ struct ShowPass : public Pass {
}
if (libs.size() > 0)
- log_header("Continuing show pass.\n");
+ log_header(design, "Continuing show pass.\n");
std::string dot_file = stringf("%s.dot", prefix.c_str());
std::string out_file = stringf("%s.%s", prefix.c_str(), format.empty() ? "svg" : format.c_str());
@@ -784,7 +796,7 @@ struct ShowPass : public Pass {
delete lib;
log_cmd_error("Can't open dot file `%s' for writing.\n", dot_file.c_str());
}
- ShowWorker worker(f, design, libs, colorSeed, flag_width, flag_signed, flag_stretch, flag_enum, flag_abbeviate, flag_notitle, color_selections, label_selections, colorattr);
+ ShowWorker worker(f, design, libs, colorSeed, flag_width, flag_signed, flag_stretch, flag_enum, flag_abbreviate, flag_notitle, color_selections, label_selections, colorattr);
fclose(f);
for (auto lib : libs)
@@ -833,5 +845,5 @@ struct ShowPass : public Pass {
log_pop();
}
} ShowPass;
-
+
PRIVATE_NAMESPACE_END
diff --git a/passes/cmds/splice.cc b/passes/cmds/splice.cc
index 3e0158c5c..7418ec4d2 100644
--- a/passes/cmds/splice.cc
+++ b/passes/cmds/splice.cc
@@ -2,11 +2,11 @@
* 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
@@ -36,6 +36,8 @@ struct SpliceWorker
bool sel_by_wire;
bool sel_any_bit;
bool no_outputs;
+ bool do_wires;
+
std::set<RTLIL::IdString> ports;
std::set<RTLIL::IdString> no_ports;
@@ -62,7 +64,7 @@ struct SpliceWorker
return sliced_signals_cache.at(sig);
int offset = 0;
- int p = driven_bits_map.at(sig.extract(0, 1).to_single_sigbit()) - 1;
+ int p = driven_bits_map.at(sig.extract(0, 1).as_bit()) - 1;
while (driven_bits.at(p) != RTLIL::State::Sm)
p--, offset++;
@@ -209,23 +211,23 @@ struct SpliceWorker
std::vector<std::pair<RTLIL::Wire*, RTLIL::SigSpec>> rework_wires;
std::vector<Wire*> mod_wires = module->wires();
- for (auto mod : mod_wires)
- if (!no_outputs && mod->port_output) {
- if (!design->selected(module, mod))
+ for (auto wire : mod_wires)
+ if ((!no_outputs && wire->port_output) || (do_wires && wire->name[0] == '\\')) {
+ if (!design->selected(module, wire))
continue;
- RTLIL::SigSpec sig = sigmap(mod);
+ RTLIL::SigSpec sig = sigmap(wire);
if (driven_chunks.count(sig) > 0)
continue;
RTLIL::SigSpec new_sig = get_spliced_signal(sig);
if (new_sig != sig)
- rework_wires.push_back(std::pair<RTLIL::Wire*, RTLIL::SigSpec>(mod, new_sig));
+ rework_wires.push_back(std::pair<RTLIL::Wire*, RTLIL::SigSpec>(wire, new_sig));
} else
- if (!mod->port_input) {
- RTLIL::SigSpec sig = sigmap(mod);
+ if (!wire->port_input) {
+ RTLIL::SigSpec sig = sigmap(wire);
if (spliced_signals_cache.count(sig) && spliced_signals_cache.at(sig) != sig)
- rework_wires.push_back(std::pair<RTLIL::Wire*, RTLIL::SigSpec>(mod, spliced_signals_cache.at(sig)));
+ rework_wires.push_back(std::pair<RTLIL::Wire*, RTLIL::SigSpec>(wire, spliced_signals_cache.at(sig)));
else if (sliced_signals_cache.count(sig) && sliced_signals_cache.at(sig) != sig)
- rework_wires.push_back(std::pair<RTLIL::Wire*, RTLIL::SigSpec>(mod, sliced_signals_cache.at(sig)));
+ rework_wires.push_back(std::pair<RTLIL::Wire*, RTLIL::SigSpec>(wire, sliced_signals_cache.at(sig)));
}
for (auto &it : rework_wires)
@@ -253,7 +255,7 @@ struct SplicePass : public Pass {
log("\n");
log("This command adds $slice and $concat cells to the design to make the splicing\n");
log("of multi-bit signals explicit. This for example is useful for coarse grain\n");
- log("synthesis, where dedidacted hardware is needed to splice signals.\n");
+ log("synthesis, where dedicated hardware is needed to splice signals.\n");
log("\n");
log(" -sel_by_cell\n");
log(" only select the cell ports to rewire by the cell. if the selection\n");
@@ -268,6 +270,9 @@ struct SplicePass : public Pass {
log(" it is sufficient if the driver of any bit of a cell port is selected.\n");
log(" by default all bits must be selected.\n");
log("\n");
+ log(" -wires\n");
+ log(" also add $slice and $concat cells to drive otherwise unused wires.\n");
+ log("\n");
log(" -no_outputs\n");
log(" do not rewire selected module outputs.\n");
log("\n");
@@ -289,6 +294,7 @@ struct SplicePass : public Pass {
bool sel_by_wire = false;
bool sel_any_bit = false;
bool no_outputs = false;
+ bool do_wires = false;
std::set<RTLIL::IdString> ports, no_ports;
size_t argidx;
@@ -305,6 +311,10 @@ struct SplicePass : public Pass {
sel_any_bit = true;
continue;
}
+ if (args[argidx] == "-wires") {
+ do_wires = true;
+ continue;
+ }
if (args[argidx] == "-no_outputs") {
no_outputs = true;
continue;
@@ -331,7 +341,7 @@ struct SplicePass : public Pass {
if (!ports.empty() && !no_ports.empty())
log_cmd_error("The options -port and -no_port are exclusive!\n");
- log_header("Executing SPLICE pass (creating cells for signal splicing).\n");
+ log_header(design, "Executing SPLICE pass (creating cells for signal splicing).\n");
for (auto &mod_it : design->modules_)
{
@@ -348,11 +358,12 @@ struct SplicePass : public Pass {
worker.sel_by_wire = sel_by_wire;
worker.sel_any_bit = sel_any_bit;
worker.no_outputs = no_outputs;
+ worker.do_wires = do_wires;
worker.ports = ports;
worker.no_ports = no_ports;
worker.run();
}
}
} SplicePass;
-
+
PRIVATE_NAMESPACE_END
diff --git a/passes/cmds/splitnets.cc b/passes/cmds/splitnets.cc
index d4e721a5d..14eeb066f 100644
--- a/passes/cmds/splitnets.cc
+++ b/passes/cmds/splitnets.cc
@@ -2,11 +2,11 @@
* 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
@@ -50,10 +50,23 @@ struct SplitnetsWorker
new_wire_name += format.substr(1, 1);
RTLIL::Wire *new_wire = module->addWire(module->uniquify(new_wire_name), width);
- new_wire->port_id = wire->port_id;
+ new_wire->port_id = wire->port_id ? wire->port_id + offset : 0;
new_wire->port_input = wire->port_input;
new_wire->port_output = wire->port_output;
+ if (wire->attributes.count("\\src"))
+ new_wire->attributes["\\src"] = wire->attributes.at("\\src");
+
+ if (wire->attributes.count("\\keep"))
+ new_wire->attributes["\\keep"] = wire->attributes.at("\\keep");
+
+ if (wire->attributes.count("\\init")) {
+ Const old_init = wire->attributes.at("\\init"), new_init;
+ for (int i = offset; i < offset+width; i++)
+ new_init.bits.push_back(i < GetSize(old_init) ? old_init.bits.at(i) : State::Sx);
+ new_wire->attributes["\\init"] = new_init;
+ }
+
std::vector<RTLIL::SigBit> sigvec = RTLIL::SigSpec(new_wire).to_sigbit_vector();
splitmap[wire].insert(splitmap[wire].end(), sigvec.begin(), sigvec.end());
}
@@ -96,7 +109,7 @@ struct SplitnetsPass : public Pass {
bool flag_driver = false;
std::string format = "[]:";
- log_header("Executing SPLITNETS pass (splitting up multi-bit signals).\n");
+ log_header(design, "Executing SPLITNETS pass (splitting up multi-bit signals).\n");
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++)
@@ -117,14 +130,27 @@ struct SplitnetsPass : public Pass {
}
extra_args(args, argidx, design);
- for (auto &mod_it : design->modules_)
- {
- RTLIL::Module *module = mod_it.second;
- if (!design->selected(module))
- continue;
+ // module_ports_db[module_name][old_port_name] = new_port_name_list
+ dict<IdString, dict<IdString, vector<IdString>>> module_ports_db;
+ for (auto module : design->selected_modules())
+ {
SplitnetsWorker worker;
+ if (flag_ports)
+ {
+ int normalized_port_factor = 0;
+
+ for (auto wire : module->wires())
+ if (wire->port_id != 0) {
+ normalized_port_factor = max(normalized_port_factor, wire->port_id+1);
+ normalized_port_factor = max(normalized_port_factor, GetSize(wire)+1);
+ }
+
+ for (auto wire : module->wires())
+ wire->port_id *= normalized_port_factor;
+ }
+
if (flag_driver)
{
CellTypes ct(design);
@@ -176,14 +202,69 @@ struct SplitnetsPass : public Pass {
module->rewrite_sigspecs(worker);
+ if (flag_ports)
+ {
+ for (auto wire : module->wires())
+ {
+ if (wire->port_id == 0)
+ continue;
+
+ SigSpec sig(wire);
+ worker(sig);
+
+ if (sig == wire)
+ continue;
+
+ vector<IdString> &new_ports = module_ports_db[module->name][wire->name];
+
+ for (SigSpec c : sig.chunks())
+ new_ports.push_back(c.as_wire()->name);
+ }
+ }
+
pool<RTLIL::Wire*> delete_wires;
for (auto &it : worker.splitmap)
delete_wires.insert(it.first);
module->remove(delete_wires);
- module->fixup_ports();
+ if (flag_ports)
+ module->fixup_ports();
+ }
+
+ if (!module_ports_db.empty())
+ {
+ for (auto module : design->modules())
+ for (auto cell : module->cells())
+ {
+ if (module_ports_db.count(cell->type) == 0)
+ continue;
+
+ for (auto &it : module_ports_db.at(cell->type))
+ {
+ IdString port_id = it.first;
+ const auto &new_port_ids = it.second;
+
+ if (!cell->hasPort(port_id))
+ continue;
+
+ int offset = 0;
+ SigSpec sig = cell->getPort(port_id);
+
+ for (auto nid : new_port_ids)
+ {
+ int nlen = GetSize(design->module(cell->type)->wire(nid));
+ if (offset + nlen > GetSize(sig))
+ nlen = GetSize(sig) - offset;
+ if (nlen > 0)
+ cell->setPort(nid, sig.extract(offset, nlen));
+ offset += nlen;
+ }
+
+ cell->unsetPort(port_id);
+ }
+ }
}
}
} SplitnetsPass;
-
+
PRIVATE_NAMESPACE_END
diff --git a/passes/cmds/stat.cc b/passes/cmds/stat.cc
index d68c57b20..362a0edfc 100644
--- a/passes/cmds/stat.cc
+++ b/passes/cmds/stat.cc
@@ -2,11 +2,11 @@
* 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
@@ -19,6 +19,8 @@
#include "kernel/register.h"
#include "kernel/celltypes.h"
+#include "passes/techmap/libparse.h"
+
#include "kernel/log.h"
USING_YOSYS_NAMESPACE
@@ -29,17 +31,21 @@ struct statdata_t
#define STAT_INT_MEMBERS X(num_wires) X(num_wire_bits) X(num_pub_wires) X(num_pub_wire_bits) \
X(num_memories) X(num_memory_bits) X(num_cells) X(num_processes)
+ #define STAT_NUMERIC_MEMBERS STAT_INT_MEMBERS X(area)
+
#define X(_name) int _name;
STAT_INT_MEMBERS
#undef X
+ double area;
std::map<RTLIL::IdString, int, RTLIL::sort_by_id_str> num_cells_by_type;
+ std::set<RTLIL::IdString> unknown_cell_area;
statdata_t operator+(const statdata_t &other) const
{
statdata_t sum = other;
#define X(_name) sum._name += _name;
- STAT_INT_MEMBERS
+ STAT_NUMERIC_MEMBERS
#undef X
for (auto &it : num_cells_by_type)
sum.num_cells_by_type[it.first] += it.second;
@@ -50,7 +56,7 @@ struct statdata_t
{
statdata_t sum = *this;
#define X(_name) sum._name *= other;
- STAT_INT_MEMBERS
+ STAT_NUMERIC_MEMBERS
#undef X
for (auto &it : sum.num_cells_by_type)
it.second *= other;
@@ -60,14 +66,14 @@ struct statdata_t
statdata_t()
{
#define X(_name) _name = 0;
- STAT_INT_MEMBERS
+ STAT_NUMERIC_MEMBERS
#undef X
}
- statdata_t(RTLIL::Design *design, RTLIL::Module *mod, bool width_mode)
+ statdata_t(RTLIL::Design *design, RTLIL::Module *mod, bool width_mode, const dict<IdString, double> &cell_area)
{
#define X(_name) _name = 0;
- STAT_INT_MEMBERS
+ STAT_NUMERIC_MEMBERS
#undef X
for (auto &it : mod->wires_)
@@ -110,7 +116,7 @@ struct statdata_t
int width_a = it.second->hasPort("\\A") ? GetSize(it.second->getPort("\\A")) : 0;
int width_b = it.second->hasPort("\\B") ? GetSize(it.second->getPort("\\B")) : 0;
int width_y = it.second->hasPort("\\Y") ? GetSize(it.second->getPort("\\Y")) : 0;
- cell_type = stringf("%s_%d", cell_type.c_str(), std::max<int>({width_a, width_b, width_y}));
+ cell_type = stringf("%s_%d", cell_type.c_str(), max<int>({width_a, width_b, width_y}));
}
else if (cell_type.in("$mux", "$pmux"))
cell_type = stringf("%s_%d", cell_type.c_str(), GetSize(it.second->getPort("\\Y")));
@@ -118,6 +124,13 @@ struct statdata_t
cell_type = stringf("%s_%d", cell_type.c_str(), GetSize(it.second->getPort("\\Q")));
}
+ if (!cell_area.empty()) {
+ if (cell_area.count(cell_type))
+ area += cell_area.at(cell_type);
+ else
+ unknown_cell_area.insert(cell_type);
+ }
+
num_cells++;
num_cells_by_type[cell_type]++;
}
@@ -141,6 +154,17 @@ struct statdata_t
log(" Number of cells: %6d\n", num_cells);
for (auto &it : num_cells_by_type)
log(" %-26s %6d\n", RTLIL::id2cstr(it.first), it.second);
+
+ if (!unknown_cell_area.empty()) {
+ log("\n");
+ for (auto cell_type : unknown_cell_area)
+ log(" Area for cell type %s is unknown!\n", cell_type.c_str());
+ }
+
+ if (area != 0) {
+ log("\n");
+ log(" Chip area for this module: %f\n", area);
+ }
}
};
@@ -162,6 +186,26 @@ statdata_t hierarchy_worker(std::map<RTLIL::IdString, statdata_t> &mod_stat, RTL
return mod_data;
}
+void read_liberty_cellarea(dict<IdString, double> &cell_area, string liberty_file)
+{
+ std::ifstream f;
+ f.open(liberty_file.c_str());
+ if (f.fail())
+ log_cmd_error("Can't open liberty file `%s': %s\n", liberty_file.c_str(), strerror(errno));
+ LibertyParser libparser(f);
+ f.close();
+
+ for (auto cell : libparser.ast->children)
+ {
+ if (cell->id != "cell" || cell->args.size() != 1)
+ continue;
+
+ LibertyAst *ar = cell->find("area");
+ if (ar != NULL && !ar->value.empty())
+ cell_area["\\" + cell->args[0]] = atof(ar->value.c_str());
+ }
+}
+
struct StatPass : public Pass {
StatPass() : Pass("stat", "print some statistics") { }
virtual void help()
@@ -178,6 +222,9 @@ struct StatPass : public Pass {
log(" selected and a module has the 'top' attribute set, this module is used\n");
log(" default value for this option.\n");
log("\n");
+ log(" -liberty <liberty_file>\n");
+ log(" use cell area information from the provided liberty file\n");
+ log("\n");
log(" -width\n");
log(" annotate internal cell types with their word width.\n");
log(" e.g. $add_8 for an 8 bit wide $add cell.\n");
@@ -185,11 +232,12 @@ struct StatPass : public Pass {
}
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
{
- log_header("Printing statistics.\n");
+ log_header(design, "Printing statistics.\n");
bool width_mode = false;
RTLIL::Module *top_mod = NULL;
std::map<RTLIL::IdString, statdata_t> mod_stat;
+ dict<IdString, double> cell_area;
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++)
@@ -198,6 +246,12 @@ struct StatPass : public Pass {
width_mode = true;
continue;
}
+ if (args[argidx] == "-liberty" && argidx+1 < args.size()) {
+ string liberty_file = args[++argidx];
+ rewrite_filename(liberty_file);
+ read_liberty_cellarea(cell_area, liberty_file);
+ continue;
+ }
if (args[argidx] == "-top" && argidx+1 < args.size()) {
if (design->modules_.count(RTLIL::escape_id(args[argidx+1])) == 0)
log_cmd_error("Can't find module %s.\n", args[argidx+1].c_str());
@@ -208,25 +262,22 @@ struct StatPass : public Pass {
}
extra_args(args, argidx, design);
- for (auto &it : design->modules_)
+ for (auto mod : design->selected_modules())
{
- if (!design->selected_module(it.first))
- continue;
-
if (!top_mod && design->full_selection())
- if (it.second->get_bool_attribute("\\top"))
- top_mod = it.second;
+ if (mod->get_bool_attribute("\\top"))
+ top_mod = mod;
- statdata_t data(design, it.second, width_mode);
- mod_stat[it.first] = data;
+ statdata_t data(design, mod, width_mode, cell_area);
+ mod_stat[mod->name] = data;
log("\n");
- log("=== %s%s ===\n", RTLIL::id2cstr(it.first), design->selected_whole_module(it.first) ? "" : " (partially selected)");
+ log("=== %s%s ===\n", RTLIL::id2cstr(mod->name), design->selected_whole_module(mod->name) ? "" : " (partially selected)");
log("\n");
data.log_data();
}
- if (top_mod != NULL)
+ if (top_mod != NULL && GetSize(mod_stat) > 1)
{
log("\n");
log("=== design hierarchy ===\n");
@@ -242,5 +293,5 @@ struct StatPass : public Pass {
log("\n");
}
} StatPass;
-
+
PRIVATE_NAMESPACE_END
diff --git a/passes/cmds/torder.cc b/passes/cmds/torder.cc
new file mode 100644
index 000000000..56223610d
--- /dev/null
+++ b/passes/cmds/torder.cc
@@ -0,0 +1,123 @@
+/*
+ * 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/celltypes.h"
+#include "kernel/sigtools.h"
+#include "kernel/utils.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct TorderPass : public Pass {
+ TorderPass() : Pass("torder", "print cells in topological order") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" torder [options] [selection]\n");
+ log("\n");
+ log("This command prints the selected cells in topological order.\n");
+ log("\n");
+ log(" -stop <cell_type> <cell_port>\n");
+ log(" do not use the specified cell port in topological sorting\n");
+ log("\n");
+ log(" -noautostop\n");
+ log(" by default Q outputs of internal FF cells and memory read port outputs\n");
+ log(" are not used in topological sorting. this option deactivates that.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ bool noautostop = false;
+ dict<IdString, pool<IdString>> stop_db;
+
+ log_header(design, "Executing TORDER pass (print cells in topological order).\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ if (args[argidx] == "-stop" && argidx+2 < args.size()) {
+ IdString cell_type = RTLIL::escape_id(args[++argidx]);
+ IdString cell_port = RTLIL::escape_id(args[++argidx]);
+ stop_db[cell_type].insert(cell_port);
+ continue;
+ }
+ if (args[argidx] == "-noautostop") {
+ noautostop = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto module : design->selected_modules())
+ {
+ log("module %s\n", log_id(module));
+
+ SigMap sigmap(module);
+ dict<SigBit, pool<IdString>> bit_drivers, bit_users;
+ TopoSort<IdString, RTLIL::sort_by_id_str> toposort;
+
+ for (auto cell : module->selected_cells())
+ for (auto conn : cell->connections())
+ {
+ if (stop_db.count(cell->type) && stop_db.at(cell->type).count(conn.first))
+ continue;
+
+ if (!noautostop && yosys_celltypes.cell_known(cell->type)) {
+ if (conn.first.in("\\Q", "\\CTRL_OUT", "\\RD_DATA"))
+ continue;
+ if (cell->type == "$memrd" && conn.first == "\\DATA")
+ continue;
+ }
+
+ if (cell->input(conn.first))
+ for (auto bit : sigmap(conn.second))
+ bit_users[bit].insert(cell->name);
+
+ if (cell->output(conn.first))
+ for (auto bit : sigmap(conn.second))
+ bit_drivers[bit].insert(cell->name);
+
+ toposort.node(cell->name);
+ }
+
+ for (auto &it : bit_users)
+ if (bit_drivers.count(it.first))
+ for (auto driver_cell : bit_drivers.at(it.first))
+ for (auto user_cell : it.second)
+ toposort.edge(driver_cell, user_cell);
+
+ toposort.analyze_loops = true;
+ toposort.sort();
+
+ for (auto &it : toposort.loops) {
+ log(" loop");
+ for (auto cell : it)
+ log(" %s", log_id(cell));
+ log("\n");
+ }
+
+ for (auto cell : toposort.sorted)
+ log(" cell %s\n", log_id(cell));
+ }
+ }
+} TorderPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/cmds/write_file.cc b/passes/cmds/write_file.cc
index 25ec4acc2..b78265933 100644
--- a/passes/cmds/write_file.cc
+++ b/passes/cmds/write_file.cc
@@ -31,7 +31,7 @@ struct WriteFileFrontend : public Frontend {
log("\n");
log(" write_file [options] output_file [input_file]\n");
log("\n");
- log("Write the text fron the input file to the output file.\n");
+ log("Write the text from the input file to the output file.\n");
log("\n");
log(" -a\n");
log(" Append to output file (instead of overwriting)\n");
diff --git a/passes/equiv/Makefile.inc b/passes/equiv/Makefile.inc
index 548eaca3b..dd7b3be02 100644
--- a/passes/equiv/Makefile.inc
+++ b/passes/equiv/Makefile.inc
@@ -6,4 +6,7 @@ OBJS += passes/equiv/equiv_status.o
OBJS += passes/equiv/equiv_add.o
OBJS += passes/equiv/equiv_remove.o
OBJS += passes/equiv/equiv_induct.o
+OBJS += passes/equiv/equiv_struct.o
+OBJS += passes/equiv/equiv_purge.o
+OBJS += passes/equiv/equiv_mark.o
diff --git a/passes/equiv/equiv_add.cc b/passes/equiv/equiv_add.cc
index a6e2f01b4..0494a724f 100644
--- a/passes/equiv/equiv_add.cc
+++ b/passes/equiv/equiv_add.cc
@@ -2,11 +2,11 @@
* 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
@@ -29,15 +29,19 @@ struct EquivAddPass : public Pass {
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
- log(" equiv_add gold_sig gate_sig\n");
+ log(" equiv_add [-try] gold_sig gate_sig\n");
log("\n");
log("This command adds an $equiv cell for the specified signals.\n");
log("\n");
+ log("\n");
+ log(" equiv_add [-try] -cell gold_cell gate_cell\n");
+ log("\n");
+ log("This command adds $equiv cells for the ports of the specified cells.\n");
+ log("\n");
}
virtual void execute(std::vector<std::string> args, Design *design)
{
- if (GetSize(args) != 3)
- cmd_error(args, GetSize(args)-1, "Invalid number of arguments.");
+ bool try_mode = false;
if (design->selected_active_module.empty())
log_cmd_error("This command must be executed in module context!\n");
@@ -45,44 +49,128 @@ struct EquivAddPass : public Pass {
Module *module = design->module(design->selected_active_module);
log_assert(module != nullptr);
- SigSpec gold_signal, gate_signal;
+ if (GetSize(args) > 1 && args[1] == "-try") {
+ args.erase(args.begin() + 1);
+ try_mode = true;
+ }
+
+ if (GetSize(args) == 4 && args[1] == "-cell")
+ {
+ Cell *gold_cell = module->cell(RTLIL::escape_id(args[2]));
+ Cell *gate_cell = module->cell(RTLIL::escape_id(args[3]));
+
+ if (gold_cell == nullptr) {
+ if (try_mode) {
+ log_warning("Can't find gold cell '%s'.\n", args[2].c_str());
+ return;
+ }
+ log_cmd_error("Can't find gold cell '%s'.\n", args[2].c_str());
+ }
+
+ if (gate_cell == nullptr) {
+ if (try_mode) {
+ log_warning("Can't find gate cell '%s'.\n", args[3].c_str());
+ return;
+ }
+ log_cmd_error("Can't find gate cell '%s'.\n", args[3].c_str());
+ }
+
+ for (auto conn : gold_cell->connections())
+ {
+ auto port = conn.first;
+ SigSpec gold_sig = gold_cell->getPort(port);
+ SigSpec gate_sig = gate_cell->getPort(port);
+ int width = min(GetSize(gold_sig), GetSize(gate_sig));
- if (!SigSpec::parse(gate_signal, module, args[2]))
- log_cmd_error("Error in gate signal: %s\n", args[2].c_str());
+ if (gold_cell->input(port) && gate_cell->input(port))
+ {
+ SigSpec combined_sig = module->addWire(NEW_ID, width);
- if (!SigSpec::parse_rhs(gate_signal, gold_signal, module, args[1]))
- log_cmd_error("Error in gold signal: %s\n", args[1].c_str());
+ for (int i = 0; i < width; i++) {
+ module->addEquiv(NEW_ID, gold_sig[i], gate_sig[i], combined_sig[i]);
+ gold_sig[i] = gate_sig[i] = combined_sig[i];
+ }
- log_assert(GetSize(gold_signal) == GetSize(gate_signal));
- SigSpec equiv_signal = module->addWire(NEW_ID, GetSize(gold_signal));
+ gold_cell->setPort(port, gold_sig);
+ gate_cell->setPort(port, gate_sig);
+ continue;
+ }
- SigMap sigmap(module);
- sigmap.apply(gold_signal);
- sigmap.apply(gate_signal);
+ if (gold_cell->output(port) && gate_cell->output(port))
+ {
+ SigSpec new_gold_wire = module->addWire(NEW_ID, width);
+ SigSpec new_gate_wire = module->addWire(NEW_ID, width);
+ SigSig gg_conn;
- dict<SigBit, SigBit> to_equiv_bits;
- pool<Cell*> added_equiv_cells;
+ for (int i = 0; i < width; i++) {
+ module->addEquiv(NEW_ID, new_gold_wire[i], new_gold_wire[i], gold_sig[i]);
+ gg_conn.first.append(gate_sig[i]);
+ gg_conn.second.append(gold_sig[i]);
+ gold_sig[i] = new_gold_wire[i];
+ gate_sig[i] = new_gate_wire[i];
+ }
- for (int i = 0; i < GetSize(gold_signal); i++) {
- Cell *equiv_cell = module->addEquiv(NEW_ID, gold_signal[i], gate_signal[i], equiv_signal[i]);
- equiv_cell->set_bool_attribute("\\keep");
- to_equiv_bits[gold_signal[i]] = equiv_signal[i];
- to_equiv_bits[gate_signal[i]] = equiv_signal[i];
- added_equiv_cells.insert(equiv_cell);
+ module->connect(gg_conn);
+ gold_cell->setPort(port, gold_sig);
+ gate_cell->setPort(port, gate_sig);
+ continue;
+ }
+ }
}
+ else
+ {
+ if (GetSize(args) != 3)
+ cmd_error(args, GetSize(args)-1, "Invalid number of arguments.");
+
+ SigSpec gold_signal, gate_signal;
+
+ if (!SigSpec::parse(gate_signal, module, args[2])) {
+ if (try_mode) {
+ log_warning("Error in gate signal: %s\n", args[2].c_str());
+ return;
+ }
+ log_cmd_error("Error in gate signal: %s\n", args[2].c_str());
+ }
- for (auto cell : module->cells())
- for (auto conn : cell->connections())
- if (!added_equiv_cells.count(cell) && cell->input(conn.first)) {
- SigSpec new_sig;
- for (auto bit : conn.second)
- if (to_equiv_bits.count(sigmap(bit)))
- new_sig.append(to_equiv_bits.at(sigmap(bit)));
- else
- new_sig.append(bit);
- if (conn.second != new_sig)
- cell->setPort(conn.first, new_sig);
+ if (!SigSpec::parse_rhs(gate_signal, gold_signal, module, args[1])) {
+ if (try_mode) {
+ log_warning("Error in gold signal: %s\n", args[1].c_str());
+ return;
+ }
+ log_cmd_error("Error in gold signal: %s\n", args[1].c_str());
}
+
+ log_assert(GetSize(gold_signal) == GetSize(gate_signal));
+ SigSpec equiv_signal = module->addWire(NEW_ID, GetSize(gold_signal));
+
+ SigMap sigmap(module);
+ sigmap.apply(gold_signal);
+ sigmap.apply(gate_signal);
+
+ dict<SigBit, SigBit> to_equiv_bits;
+ pool<Cell*> added_equiv_cells;
+
+ for (int i = 0; i < GetSize(gold_signal); i++) {
+ Cell *equiv_cell = module->addEquiv(NEW_ID, gold_signal[i], gate_signal[i], equiv_signal[i]);
+ equiv_cell->set_bool_attribute("\\keep");
+ to_equiv_bits[gold_signal[i]] = equiv_signal[i];
+ to_equiv_bits[gate_signal[i]] = equiv_signal[i];
+ added_equiv_cells.insert(equiv_cell);
+ }
+
+ for (auto cell : module->cells())
+ for (auto conn : cell->connections())
+ if (!added_equiv_cells.count(cell) && cell->input(conn.first)) {
+ SigSpec new_sig;
+ for (auto bit : conn.second)
+ if (to_equiv_bits.count(sigmap(bit)))
+ new_sig.append(to_equiv_bits.at(sigmap(bit)));
+ else
+ new_sig.append(bit);
+ if (conn.second != new_sig)
+ cell->setPort(conn.first, new_sig);
+ }
+ }
}
} EquivAddPass;
diff --git a/passes/equiv/equiv_induct.cc b/passes/equiv/equiv_induct.cc
index c5b4eda72..c958c3de4 100644
--- a/passes/equiv/equiv_induct.cc
+++ b/passes/equiv/equiv_induct.cc
@@ -2,11 +2,11 @@
* 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
@@ -32,7 +32,7 @@ struct EquivInductWorker
vector<Cell*> cells;
pool<Cell*> workset;
- ezDefaultSAT ez;
+ ezSatPtr ez;
SatGen satgen;
int max_seq;
@@ -43,7 +43,8 @@ struct EquivInductWorker
SigPool undriven_signals;
EquivInductWorker(Module *module, const pool<Cell*> &unproven_equiv_cells, bool model_undef, int max_seq) : module(module), sigmap(module),
- cells(module->selected_cells()), workset(unproven_equiv_cells), satgen(&ez, &sigmap), max_seq(max_seq), success_counter(0)
+ cells(module->selected_cells()), workset(unproven_equiv_cells),
+ satgen(ez.get(), &sigmap), max_seq(max_seq), success_counter(0)
{
satgen.model_undef = model_undef;
}
@@ -58,14 +59,14 @@ struct EquivInductWorker
cell_warn_cache.insert(cell);
}
if (cell->type == "$equiv") {
- SigBit bit_a = sigmap(cell->getPort("\\A")).to_single_sigbit();
- SigBit bit_b = sigmap(cell->getPort("\\B")).to_single_sigbit();
+ SigBit bit_a = sigmap(cell->getPort("\\A")).as_bit();
+ SigBit bit_b = sigmap(cell->getPort("\\B")).as_bit();
if (bit_a != bit_b) {
int ez_a = satgen.importSigBit(bit_a, step);
int ez_b = satgen.importSigBit(bit_b, step);
- int cond = ez.IFF(ez_a, ez_b);
+ int cond = ez->IFF(ez_a, ez_b);
if (satgen.model_undef)
- cond = ez.OR(cond, satgen.importUndefSigBit(bit_a, step));
+ cond = ez->OR(cond, satgen.importUndefSigBit(bit_a, step));
ez_equal_terms.push_back(cond);
}
}
@@ -73,11 +74,11 @@ struct EquivInductWorker
if (satgen.model_undef) {
for (auto bit : undriven_signals.export_all())
- ez.assume(ez.NOT(satgen.importUndefSigBit(bit, step)));
+ ez->assume(ez->NOT(satgen.importUndefSigBit(bit, step)));
}
log_assert(!ez_step_is_consistent.count(step));
- ez_step_is_consistent[step] = ez.expression(ez.OpAnd, ez_equal_terms);
+ ez_step_is_consistent[step] = ez->expression(ez->OpAnd, ez_equal_terms);
}
void run()
@@ -101,27 +102,27 @@ struct EquivInductWorker
if (satgen.model_undef) {
for (auto bit : satgen.initial_state.export_all())
- ez.assume(ez.NOT(satgen.importUndefSigBit(bit, 1)));
+ ez->assume(ez->NOT(satgen.importUndefSigBit(bit, 1)));
log(" Undef modelling: force def on %d initial reg values and %d inputs.\n",
GetSize(satgen.initial_state), GetSize(undriven_signals));
}
for (int step = 1; step <= max_seq; step++)
{
- ez.assume(ez_step_is_consistent[step]);
+ ez->assume(ez_step_is_consistent[step]);
- log(" Proving existence of base case for step %d. (%d clauses over %d variables)\n", step, ez.numCnfClauses(), ez.numCnfVariables());
- if (!ez.solve()) {
+ log(" Proving existence of base case for step %d. (%d clauses over %d variables)\n", step, ez->numCnfClauses(), ez->numCnfVariables());
+ if (!ez->solve()) {
log(" Proof for base case failed. Circuit inherently diverges!\n");
return;
}
create_timestep(step+1);
- int new_step_not_consistent = ez.NOT(ez_step_is_consistent[step+1]);
- ez.bind(new_step_not_consistent);
+ int new_step_not_consistent = ez->NOT(ez_step_is_consistent[step+1]);
+ ez->bind(new_step_not_consistent);
- log(" Proving induction step %d. (%d clauses over %d variables)\n", step, ez.numCnfClauses(), ez.numCnfVariables());
- if (!ez.solve(new_step_not_consistent)) {
+ log(" Proving induction step %d. (%d clauses over %d variables)\n", step, ez->numCnfClauses(), ez->numCnfVariables());
+ if (!ez->solve(new_step_not_consistent)) {
log(" Proof for induction step holds. Entire workset of %d cells proven!\n", GetSize(workset));
for (auto cell : workset)
cell->setPort("\\B", cell->getPort("\\A"));
@@ -136,19 +137,19 @@ struct EquivInductWorker
for (auto cell : workset)
{
- SigBit bit_a = sigmap(cell->getPort("\\A")).to_single_sigbit();
- SigBit bit_b = sigmap(cell->getPort("\\B")).to_single_sigbit();
+ SigBit bit_a = sigmap(cell->getPort("\\A")).as_bit();
+ SigBit bit_b = sigmap(cell->getPort("\\B")).as_bit();
log(" Trying to prove $equiv for %s:", log_signal(sigmap(cell->getPort("\\Y"))));
int ez_a = satgen.importSigBit(bit_a, max_seq+1);
int ez_b = satgen.importSigBit(bit_b, max_seq+1);
- int cond = ez.XOR(ez_a, ez_b);
+ int cond = ez->XOR(ez_a, ez_b);
if (satgen.model_undef)
- cond = ez.AND(cond, ez.NOT(satgen.importUndefSigBit(bit_a, max_seq+1)));
+ cond = ez->AND(cond, ez->NOT(satgen.importUndefSigBit(bit_a, max_seq+1)));
- if (!ez.solve(cond)) {
+ if (!ez->solve(cond)) {
log(" success!\n");
cell->setPort("\\B", cell->getPort("\\A"));
success_counter++;
@@ -197,7 +198,7 @@ struct EquivInductPass : public Pass {
bool model_undef = false;
int max_seq = 4;
- log_header("Executing EQUIV_INDUCT pass.\n");
+ log_header(design, "Executing EQUIV_INDUCT pass.\n");
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++) {
diff --git a/passes/equiv/equiv_make.cc b/passes/equiv/equiv_make.cc
index 5635e7a7e..40ca42621 100644
--- a/passes/equiv/equiv_make.cc
+++ b/passes/equiv/equiv_make.cc
@@ -2,11 +2,11 @@
* 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
@@ -280,7 +280,7 @@ struct EquivMakeWorker
for (auto c : cells_list)
for (auto &conn : c->connections())
- if (ct.cell_input(c->type, conn.first)) {
+ if (!ct.cell_output(c->type, conn.first)) {
SigSpec old_sig = assign_map(conn.second);
SigSpec new_sig = rd_signal_map(old_sig);
if (old_sig != new_sig) {
@@ -407,7 +407,7 @@ struct EquivMakePass : public Pass {
log(" Do not match cells or signals that match the names in the file.\n");
log("\n");
log(" -encfile <file>\n");
- log(" Match FSM encodings using the desiption from the file.\n");
+ log(" Match FSM encodings using the description from the file.\n");
log(" See 'help fsm_recode' for details.\n");
log("\n");
log("Note: The circuit created by this command is not a miter (with something like\n");
@@ -464,7 +464,7 @@ struct EquivMakePass : public Pass {
worker.read_blacklists();
worker.read_encfiles();
- log_header("Executing EQUIV_MAKE pass (creating equiv checking module).\n");
+ log_header(design, "Executing EQUIV_MAKE pass (creating equiv checking module).\n");
worker.equiv_mod = design->addModule(RTLIL::escape_id(args[argidx+2]));
worker.run();
diff --git a/passes/equiv/equiv_mark.cc b/passes/equiv/equiv_mark.cc
new file mode 100644
index 000000000..22c501763
--- /dev/null
+++ b/passes/equiv/equiv_mark.cc
@@ -0,0 +1,239 @@
+/*
+ * 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 EquivMarkWorker
+{
+ Module *module;
+ SigMap sigmap;
+
+ // cache for traversing signal flow graph
+ dict<SigBit, pool<IdString>> up_bit2cells;
+ dict<IdString, pool<SigBit>> up_cell2bits;
+ pool<IdString> edge_cells, equiv_cells;
+
+ // graph traversal state
+ pool<SigBit> queue, visited;
+
+ // assigned regions
+ dict<IdString, int> cell_regions;
+ dict<SigBit, int> bit_regions;
+ int next_region;
+
+ // merge-find
+ mfp<int> region_mf;
+
+ EquivMarkWorker(Module *module) : module(module), sigmap(module)
+ {
+ for (auto cell : module->cells())
+ {
+ if (cell->type == "$equiv")
+ equiv_cells.insert(cell->name);
+
+ for (auto &port : cell->connections())
+ {
+ if (cell->input(port.first))
+ for (auto bit : sigmap(port.second))
+ up_cell2bits[cell->name].insert(bit);
+
+ if (cell->output(port.first))
+ for (auto bit : sigmap(port.second))
+ up_bit2cells[bit].insert(cell->name);
+ }
+ }
+
+ next_region = 0;
+ }
+
+ void mark()
+ {
+ while (!queue.empty())
+ {
+ pool<IdString> cells;
+
+ for (auto &bit : queue)
+ {
+ // log_assert(bit_regions.count(bit) == 0);
+ bit_regions[bit] = next_region;
+ visited.insert(bit);
+
+ for (auto cell : up_bit2cells[bit])
+ if (edge_cells.count(cell) == 0)
+ cells.insert(cell);
+ }
+
+ queue.clear();
+
+ for (auto cell : cells)
+ {
+ if (next_region == 0 && equiv_cells.count(cell))
+ continue;
+
+ if (cell_regions.count(cell)) {
+ if (cell_regions.at(cell) != 0)
+ region_mf.merge(cell_regions.at(cell), next_region);
+ continue;
+ }
+
+ cell_regions[cell] = next_region;
+
+ for (auto bit : up_cell2bits[cell])
+ if (visited.count(bit) == 0)
+ queue.insert(bit);
+ }
+ }
+
+ next_region++;
+ }
+
+ void run()
+ {
+ log("Running equiv_mark on module %s:\n", log_id(module));
+
+ // marking region 0
+
+ for (auto wire : module->wires())
+ if (wire->port_id > 0)
+ for (auto bit : sigmap(wire))
+ queue.insert(bit);
+
+ for (auto cell_name : equiv_cells)
+ {
+ auto cell = module->cell(cell_name);
+
+ SigSpec sig_a = sigmap(cell->getPort("\\A"));
+ SigSpec sig_b = sigmap(cell->getPort("\\B"));
+
+ if (sig_a == sig_b) {
+ for (auto bit : sig_a)
+ queue.insert(bit);
+ edge_cells.insert(cell_name);
+ cell_regions[cell_name] = 0;
+ }
+ }
+
+ mark();
+
+ // marking unsolved regions
+
+ for (auto cell : module->cells())
+ {
+ if (cell_regions.count(cell->name) || cell->type != "$equiv")
+ continue;
+
+ SigSpec sig_a = sigmap(cell->getPort("\\A"));
+ SigSpec sig_b = sigmap(cell->getPort("\\B"));
+
+ log_assert(sig_a != sig_b);
+
+ for (auto bit : sig_a)
+ queue.insert(bit);
+
+ for (auto bit : sig_b)
+ queue.insert(bit);
+
+ cell_regions[cell->name] = next_region;
+ mark();
+ }
+
+ // setting attributes
+
+ dict<int, int> final_region_map;
+ int next_final_region = 0;
+
+ dict<int, int> region_cell_count;
+ dict<int, int> region_wire_count;
+
+ for (int i = 0; i < next_region; i++) {
+ int r = region_mf.find(i);
+ if (final_region_map.count(r) == 0)
+ final_region_map[r] = next_final_region++;
+ final_region_map[i] = final_region_map[r];
+ }
+
+ for (auto cell : module->cells())
+ {
+ if (cell_regions.count(cell->name)) {
+ int r = final_region_map.at(cell_regions.at(cell->name));
+ cell->attributes["\\equiv_region"] = Const(r);
+ region_cell_count[r]++;
+ } else
+ cell->attributes.erase("\\equiv_region");
+ }
+
+ for (auto wire : module->wires())
+ {
+ pool<int> regions;
+ for (auto bit : sigmap(wire))
+ if (bit_regions.count(bit))
+ regions.insert(region_mf.find(bit_regions.at(bit)));
+
+ if (GetSize(regions) == 1) {
+ int r = final_region_map.at(*regions.begin());
+ wire->attributes["\\equiv_region"] = Const(r);
+ region_wire_count[r]++;
+ } else
+ wire->attributes.erase("\\equiv_region");
+ }
+
+ for (int i = 0; i < next_final_region; i++)
+ log(" region %d: %d cells, %d wires\n", i, region_wire_count[i], region_cell_count[i]);
+ }
+};
+
+struct EquivMarkPass : public Pass {
+ EquivMarkPass() : Pass("equiv_mark", "mark equivalence checking regions") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" equiv_mark [options] [selection]\n");
+ log("\n");
+ log("This command marks the regions in an equivalence checking module. Region 0 is\n");
+ log("the proven part of the circuit. Regions with higher numbers are connected\n");
+ log("unproven subcricuits. The integer attribute 'equiv_region' is set on all\n");
+ log("wires and cells.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, Design *design)
+ {
+ log_header(design, "Executing EQUIV_MARK pass.\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ // if (args[argidx] == "-foobar") {
+ // continue;
+ // }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto module : design->selected_whole_modules_warn()) {
+ EquivMarkWorker worker(module);
+ worker.run();
+ }
+ }
+} EquivMarkPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/equiv/equiv_miter.cc b/passes/equiv/equiv_miter.cc
index 23b348184..eb2e5a171 100644
--- a/passes/equiv/equiv_miter.cc
+++ b/passes/equiv/equiv_miter.cc
@@ -2,11 +2,11 @@
* 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
@@ -156,7 +156,7 @@ struct EquivMiterWorker
struct RewriteSigSpecWorker {
RTLIL::Module * mod;
void operator()(SigSpec &sig) {
- vector<RTLIL::SigChunk> chunks = sig.chunks();
+ vector<SigChunk> chunks = sig.chunks();
for (auto &c : chunks)
if (c.wire != NULL)
c.wire = mod->wires_.at(c.wire->name);
@@ -333,7 +333,7 @@ struct EquivMiterPass : public Pass {
found_two_modules:
log_cmd_error("Exactly one module must be selected for 'equiv_miter'!\n");
- log_header("Executing EQUIV_MITER pass.\n");
+ log_header(design, "Executing EQUIV_MITER pass.\n");
worker.miter_module = design->addModule(worker.miter_name);
worker.run();
diff --git a/passes/equiv/equiv_purge.cc b/passes/equiv/equiv_purge.cc
new file mode 100644
index 000000000..163b1009b
--- /dev/null
+++ b/passes/equiv/equiv_purge.cc
@@ -0,0 +1,210 @@
+/*
+ * 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 EquivPurgeWorker
+{
+ Module *module;
+ SigMap sigmap;
+ int name_cnt;
+
+ EquivPurgeWorker(Module *module) : module(module), sigmap(module), name_cnt(0) { }
+
+ SigSpec make_output(SigSpec sig, IdString cellname)
+ {
+ if (sig.is_wire()) {
+ Wire *wire = sig.as_wire();
+ if (wire->name[0] == '\\') {
+ if (!wire->port_output) {
+ log(" Module output: %s (%s)\n", log_signal(wire), log_id(cellname));
+ wire->port_output = true;
+ }
+ return wire;
+ }
+ }
+
+ while (1)
+ {
+ IdString name = stringf("\\equiv_%d", name_cnt++);
+ if (module->count_id(name))
+ continue;
+
+ Wire *wire = module->addWire(name, GetSize(sig));
+ wire->port_output = true;
+ module->connect(wire, sig);
+ log(" Module output: %s (%s)\n", log_signal(wire), log_id(cellname));
+ return wire;
+ }
+ }
+
+ SigSpec make_input(SigSpec sig)
+ {
+ if (sig.is_wire()) {
+ Wire *wire = sig.as_wire();
+ if (wire->name[0] == '\\') {
+ if (!wire->port_output) {
+ log(" Module input: %s\n", log_signal(wire));
+ wire->port_input = true;
+ }
+ return module->addWire(NEW_ID, GetSize(sig));
+ }
+ }
+
+ while (1)
+ {
+ IdString name = stringf("\\equiv_%d", name_cnt++);
+ if (module->count_id(name))
+ continue;
+
+ Wire *wire = module->addWire(name, GetSize(sig));
+ wire->port_input = true;
+ module->connect(sig, wire);
+ log(" Module input: %s\n", log_signal(wire));
+ return module->addWire(NEW_ID, GetSize(sig));
+ }
+ }
+
+ void run()
+ {
+ log("Running equiv_purge on module %s:\n", log_id(module));
+
+ for (auto wire : module->wires()) {
+ wire->port_input = false;
+ wire->port_output = false;
+ }
+
+ pool<SigBit> queue, visited;
+
+ // cache for traversing signal flow graph
+ dict<SigBit, pool<IdString>> up_bit2cells;
+ dict<IdString, pool<SigBit>> up_cell2bits;
+
+ for (auto cell : module->cells())
+ {
+ if (cell->type != "$equiv") {
+ for (auto &port : cell->connections()) {
+ if (cell->input(port.first))
+ for (auto bit : sigmap(port.second))
+ up_cell2bits[cell->name].insert(bit);
+ if (cell->output(port.first))
+ for (auto bit : sigmap(port.second))
+ up_bit2cells[bit].insert(cell->name);
+ }
+ continue;
+ }
+
+ SigSpec sig_a = sigmap(cell->getPort("\\A"));
+ SigSpec sig_b = sigmap(cell->getPort("\\B"));
+ SigSpec sig_y = sigmap(cell->getPort("\\Y"));
+
+ if (sig_a == sig_b)
+ continue;
+
+ for (auto bit : sig_a)
+ queue.insert(bit);
+
+ for (auto bit : sig_b)
+ queue.insert(bit);
+
+ for (auto bit : sig_y)
+ visited.insert(bit);
+
+ cell->setPort("\\Y", make_output(sig_y, cell->name));
+ }
+
+ SigSpec srcsig;
+ SigMap rewrite_sigmap(module);
+
+ while (!queue.empty())
+ {
+ pool<SigBit> next_queue;
+
+ for (auto bit : queue)
+ visited.insert(bit);
+
+ for (auto bit : queue)
+ {
+ auto &cells = up_bit2cells[bit];
+
+ if (cells.empty()) {
+ srcsig.append(bit);
+ } else {
+ for (auto cell : cells)
+ for (auto bit : up_cell2bits[cell])
+ if (visited.count(bit) == 0)
+ next_queue.insert(bit);
+ }
+ }
+
+ next_queue.swap(queue);
+ }
+
+ srcsig.sort_and_unify();
+
+ for (SigChunk chunk : srcsig.chunks())
+ if (chunk.wire != nullptr)
+ rewrite_sigmap.add(chunk, make_input(chunk));
+
+ for (auto cell : module->cells())
+ if (cell->type == "$equiv")
+ cell->setPort("\\Y", rewrite_sigmap(sigmap(cell->getPort("\\Y"))));
+
+ module->fixup_ports();
+ }
+};
+
+struct EquivPurgePass : public Pass {
+ EquivPurgePass() : Pass("equiv_purge", "purge equivalence checking module") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" equiv_purge [options] [selection]\n");
+ log("\n");
+ log("This command removes the proven part of an equivalence checking module, leaving\n");
+ log("only the unproven segments in the design. This will also remove and add module\n");
+ log("ports as needed.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, Design *design)
+ {
+ log_header(design, "Executing EQUIV_PURGE pass.\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ // if (args[argidx] == "-foobar") {
+ // continue;
+ // }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto module : design->selected_whole_modules_warn()) {
+ EquivPurgeWorker worker(module);
+ worker.run();
+ }
+ }
+} EquivPurgePass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/equiv/equiv_remove.cc b/passes/equiv/equiv_remove.cc
index b6e232f9b..770497a51 100644
--- a/passes/equiv/equiv_remove.cc
+++ b/passes/equiv/equiv_remove.cc
@@ -2,11 +2,11 @@
* 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
@@ -46,7 +46,7 @@ struct EquivRemovePass : public Pass {
bool mode_gate = false;
int remove_count = 0;
- log_header("Executing EQUIV_REMOVE pass.\n");
+ log_header(design, "Executing EQUIV_REMOVE pass.\n");
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++) {
diff --git a/passes/equiv/equiv_simple.cc b/passes/equiv/equiv_simple.cc
index 579877e65..49963ed68 100644
--- a/passes/equiv/equiv_simple.cc
+++ b/passes/equiv/equiv_simple.cc
@@ -2,11 +2,11 @@
* 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
@@ -32,7 +32,7 @@ struct EquivSimpleWorker
SigMap &sigmap;
dict<SigBit, Cell*> &bit2driver;
- ezDefaultSAT ez;
+ ezSatPtr ez;
SatGen satgen;
int max_seq;
bool verbose;
@@ -41,7 +41,7 @@ struct EquivSimpleWorker
EquivSimpleWorker(const vector<Cell*> &equiv_cells, SigMap &sigmap, dict<SigBit, Cell*> &bit2driver, int max_seq, bool verbose, bool model_undef) :
module(equiv_cells.front()->module), equiv_cells(equiv_cells), equiv_cell(nullptr),
- sigmap(sigmap), bit2driver(bit2driver), satgen(&ez, &sigmap), max_seq(max_seq), verbose(verbose)
+ sigmap(sigmap), bit2driver(bit2driver), satgen(ez.get(), &sigmap), max_seq(max_seq), verbose(verbose)
{
satgen.model_undef = model_undef;
}
@@ -89,9 +89,9 @@ struct EquivSimpleWorker
bool run_cell()
{
- SigBit bit_a = sigmap(equiv_cell->getPort("\\A")).to_single_sigbit();
- SigBit bit_b = sigmap(equiv_cell->getPort("\\B")).to_single_sigbit();
- int ez_context = ez.frozen_literal();
+ SigBit bit_a = sigmap(equiv_cell->getPort("\\A")).as_bit();
+ SigBit bit_b = sigmap(equiv_cell->getPort("\\B")).as_bit();
+ int ez_context = ez->frozen_literal();
if (satgen.model_undef)
{
@@ -99,14 +99,14 @@ struct EquivSimpleWorker
int ez_b = satgen.importDefSigBit(bit_b, max_seq+1);
int ez_undef_a = satgen.importUndefSigBit(bit_a, max_seq+1);
- ez.assume(ez.XOR(ez_a, ez_b), ez_context);
- ez.assume(ez.NOT(ez_undef_a), ez_context);
+ ez->assume(ez->XOR(ez_a, ez_b), ez_context);
+ ez->assume(ez->NOT(ez_undef_a), ez_context);
}
else
{
int ez_a = satgen.importSigBit(bit_a, max_seq+1);
int ez_b = satgen.importSigBit(bit_b, max_seq+1);
- ez.assume(ez.XOR(ez_a, ez_b), ez_context);
+ ez->assume(ez->XOR(ez_a, ez_b), ez_context);
}
pool<SigBit> seed_a = { bit_a };
@@ -168,16 +168,16 @@ struct EquivSimpleWorker
if (satgen.model_undef) {
for (auto bit : input_bits)
- ez.assume(ez.NOT(satgen.importUndefSigBit(bit, step+1)));
+ ez->assume(ez->NOT(satgen.importUndefSigBit(bit, step+1)));
}
if (verbose)
- log(" Problem size at t=%d: %d literals, %d clauses\n", step, ez.numCnfVariables(), ez.numCnfClauses());
+ log(" Problem size at t=%d: %d literals, %d clauses\n", step, ez->numCnfVariables(), ez->numCnfClauses());
- if (!ez.solve(ez_context)) {
+ if (!ez->solve(ez_context)) {
log(verbose ? " Proved equivalence! Marking $equiv cell as proven.\n" : " success!\n");
equiv_cell->setPort("\\B", equiv_cell->getPort("\\A"));
- ez.assume(ez.NOT(ez_context));
+ ez->assume(ez->NOT(ez_context));
return true;
}
@@ -224,7 +224,7 @@ struct EquivSimpleWorker
if (!verbose)
log(" failed.\n");
- ez.assume(ez.NOT(ez_context));
+ ez->assume(ez->NOT(ez_context));
return false;
}
@@ -277,7 +277,7 @@ struct EquivSimplePass : public Pass {
int success_counter = 0;
int max_seq = 1;
- log_header("Executing EQUIV_SIMPLE pass.\n");
+ log_header(design, "Executing EQUIV_SIMPLE pass.\n");
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++) {
@@ -314,7 +314,7 @@ struct EquivSimplePass : public Pass {
for (auto cell : module->selected_cells())
if (cell->type == "$equiv" && cell->getPort("\\A") != cell->getPort("\\B")) {
- auto bit = sigmap(cell->getPort("\\Y").to_single_sigbit());
+ auto bit = sigmap(cell->getPort("\\Y").as_bit());
auto bit_group = bit;
if (!nogroup && bit_group.wire)
bit_group.offset = 0;
diff --git a/passes/equiv/equiv_status.cc b/passes/equiv/equiv_status.cc
index 8ca1aacd5..7b9230b35 100644
--- a/passes/equiv/equiv_status.cc
+++ b/passes/equiv/equiv_status.cc
@@ -2,11 +2,11 @@
* 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
@@ -41,7 +41,7 @@ struct EquivStatusPass : public Pass {
bool assert_mode = false;
int unproven_count = 0;
- log_header("Executing EQUIV_STATUS pass.\n");
+ log_header(design, "Executing EQUIV_STATUS pass.\n");
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++) {
diff --git a/passes/equiv/equiv_struct.cc b/passes/equiv/equiv_struct.cc
new file mode 100644
index 000000000..c4ced6a71
--- /dev/null
+++ b/passes/equiv/equiv_struct.cc
@@ -0,0 +1,367 @@
+/*
+ * 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 EquivStructWorker
+{
+ Module *module;
+ SigMap sigmap;
+ SigMap equiv_bits;
+ bool mode_fwd;
+ bool mode_icells;
+ int merge_count;
+
+ const pool<IdString> &fwonly_cells;
+
+ struct merge_key_t
+ {
+ IdString type;
+ vector<pair<IdString, Const>> parameters;
+ vector<pair<IdString, int>> port_sizes;
+ vector<tuple<IdString, int, SigBit>> connections;
+
+ bool operator==(const merge_key_t &other) const {
+ return type == other.type && connections == other.connections &&
+ parameters == other.parameters && port_sizes == other.port_sizes;
+ }
+
+ unsigned int hash() const {
+ unsigned int h = mkhash_init;
+ h = mkhash(h, mkhash(type));
+ h = mkhash(h, mkhash(parameters));
+ h = mkhash(h, mkhash(connections));
+ return h;
+ }
+ };
+
+ dict<merge_key_t, pool<IdString>> merge_cache;
+ pool<merge_key_t> fwd_merge_cache, bwd_merge_cache;
+
+ void merge_cell_pair(Cell *cell_a, Cell *cell_b)
+ {
+ SigMap merged_map;
+ merge_count++;
+
+ SigSpec inputs_a, inputs_b;
+ vector<string> input_names;
+
+ for (auto &port_a : cell_a->connections())
+ {
+ SigSpec bits_a = sigmap(port_a.second);
+ SigSpec bits_b = sigmap(cell_b->getPort(port_a.first));
+
+ log_assert(GetSize(bits_a) == GetSize(bits_b));
+
+ if (!cell_a->output(port_a.first))
+ for (int i = 0; i < GetSize(bits_a); i++)
+ if (bits_a[i] != bits_b[i]) {
+ inputs_a.append(bits_a[i]);
+ inputs_b.append(bits_b[i]);
+ input_names.push_back(GetSize(bits_a) == 1 ? port_a.first.str() :
+ stringf("%s[%d]", log_id(port_a.first), i));
+ }
+ }
+
+ for (int i = 0; i < GetSize(inputs_a); i++) {
+ SigBit bit_a = inputs_a[i], bit_b = inputs_b[i];
+ SigBit bit_y = module->addWire(NEW_ID);
+ log(" New $equiv for input %s: A: %s, B: %s, Y: %s\n",
+ input_names[i].c_str(), log_signal(bit_a), log_signal(bit_b), log_signal(bit_y));
+ module->addEquiv(NEW_ID, bit_a, bit_b, bit_y);
+ merged_map.add(bit_a, bit_y);
+ merged_map.add(bit_b, bit_y);
+ }
+
+ std::vector<IdString> outport_names, inport_names;
+
+ for (auto &port_a : cell_a->connections())
+ if (cell_a->output(port_a.first))
+ outport_names.push_back(port_a.first);
+ else
+ inport_names.push_back(port_a.first);
+
+ for (auto &pn : inport_names)
+ cell_a->setPort(pn, merged_map(sigmap(cell_a->getPort(pn))));
+
+ for (auto &pn : outport_names) {
+ SigSpec sig_a = cell_a->getPort(pn);
+ SigSpec sig_b = cell_b->getPort(pn);
+ module->connect(sig_b, sig_a);
+ }
+
+ auto merged_attr = cell_b->get_strpool_attribute("\\equiv_merged");
+ merged_attr.insert(log_id(cell_b));
+ cell_a->add_strpool_attribute("\\equiv_merged", merged_attr);
+ module->remove(cell_b);
+ }
+
+ EquivStructWorker(Module *module, bool mode_fwd, bool mode_icells, const pool<IdString> &fwonly_cells, int iter_num) :
+ module(module), sigmap(module), equiv_bits(module),
+ mode_fwd(mode_fwd), mode_icells(mode_icells), merge_count(0), fwonly_cells(fwonly_cells)
+ {
+ log(" Starting iteration %d.\n", iter_num);
+
+ pool<SigBit> equiv_inputs;
+ pool<IdString> cells;
+
+ for (auto cell : module->selected_cells())
+ if (cell->type == "$equiv") {
+ SigBit sig_a = sigmap(cell->getPort("\\A").as_bit());
+ SigBit sig_b = sigmap(cell->getPort("\\B").as_bit());
+ equiv_bits.add(sig_b, sig_a);
+ equiv_inputs.insert(sig_a);
+ equiv_inputs.insert(sig_b);
+ cells.insert(cell->name);
+ } else {
+ if (mode_icells || module->design->module(cell->type))
+ cells.insert(cell->name);
+ }
+
+ for (auto cell : module->selected_cells())
+ if (cell->type == "$equiv") {
+ SigBit sig_a = sigmap(cell->getPort("\\A").as_bit());
+ SigBit sig_b = sigmap(cell->getPort("\\B").as_bit());
+ SigBit sig_y = sigmap(cell->getPort("\\Y").as_bit());
+ if (sig_a == sig_b && equiv_inputs.count(sig_y)) {
+ log(" Purging redundant $equiv cell %s.\n", log_id(cell));
+ module->connect(sig_y, sig_a);
+ module->remove(cell);
+ merge_count++;
+ }
+ }
+
+ if (merge_count > 0)
+ return;
+
+ for (auto cell_name : cells)
+ {
+ merge_key_t key;
+ vector<tuple<IdString, int, SigBit>> fwd_connections;
+
+ Cell *cell = module->cell(cell_name);
+ key.type = cell->type;
+
+ for (auto &it : cell->parameters)
+ key.parameters.push_back(it);
+ std::sort(key.parameters.begin(), key.parameters.end());
+
+ for (auto &it : cell->connections())
+ key.port_sizes.push_back(make_pair(it.first, GetSize(it.second)));
+ std::sort(key.port_sizes.begin(), key.port_sizes.end());
+
+ for (auto &conn : cell->connections())
+ {
+ if (cell->input(conn.first)) {
+ SigSpec sig = sigmap(conn.second);
+ for (int i = 0; i < GetSize(sig); i++)
+ fwd_connections.push_back(make_tuple(conn.first, i, sig[i]));
+ }
+
+ if (cell->output(conn.first)) {
+ SigSpec sig = equiv_bits(conn.second);
+ for (int i = 0; i < GetSize(sig); i++) {
+ key.connections.clear();
+ key.connections.push_back(make_tuple(conn.first, i, sig[i]));
+
+ if (merge_cache.count(key))
+ bwd_merge_cache.insert(key);
+ merge_cache[key].insert(cell_name);
+ }
+ }
+ }
+
+ std::sort(fwd_connections.begin(), fwd_connections.end());
+ key.connections.swap(fwd_connections);
+
+ if (merge_cache.count(key))
+ fwd_merge_cache.insert(key);
+ merge_cache[key].insert(cell_name);
+ }
+
+ for (int phase = 0; phase < 2; phase++)
+ {
+ auto &queue = phase ? bwd_merge_cache : fwd_merge_cache;
+
+ for (auto &key : queue)
+ {
+ const char *strategy = nullptr;
+ vector<Cell*> gold_cells, gate_cells, other_cells;
+ vector<pair<Cell*, Cell*>> cell_pairs;
+ IdString cells_type;
+
+ for (auto cell_name : merge_cache[key]) {
+ Cell *c = module->cell(cell_name);
+ if (c != nullptr) {
+ string n = cell_name.str();
+ cells_type = c->type;
+ if (GetSize(n) > 5 && n.substr(GetSize(n)-5) == "_gold")
+ gold_cells.push_back(c);
+ else if (GetSize(n) > 5 && n.substr(GetSize(n)-5) == "_gate")
+ gate_cells.push_back(c);
+ else
+ other_cells.push_back(c);
+ }
+ }
+
+ if (phase && fwonly_cells.count(cells_type))
+ continue;
+
+ if (GetSize(gold_cells) > 1 || GetSize(gate_cells) > 1 || GetSize(other_cells) > 1)
+ {
+ strategy = "deduplicate";
+ for (int i = 0; i+1 < GetSize(gold_cells); i += 2)
+ cell_pairs.push_back(make_pair(gold_cells[i], gold_cells[i+1]));
+ for (int i = 0; i+1 < GetSize(gate_cells); i += 2)
+ cell_pairs.push_back(make_pair(gate_cells[i], gate_cells[i+1]));
+ for (int i = 0; i+1 < GetSize(other_cells); i += 2)
+ cell_pairs.push_back(make_pair(other_cells[i], other_cells[i+1]));
+ goto run_strategy;
+ }
+
+ if (GetSize(gold_cells) == 1 && GetSize(gate_cells) == 1)
+ {
+ strategy = "gold-gate-pairs";
+ cell_pairs.push_back(make_pair(gold_cells[0], gate_cells[0]));
+ goto run_strategy;
+ }
+
+ if (GetSize(gold_cells) == 1 && GetSize(other_cells) == 1)
+ {
+ strategy = "gold-guess";
+ cell_pairs.push_back(make_pair(gold_cells[0], other_cells[0]));
+ goto run_strategy;
+ }
+
+ if (GetSize(other_cells) == 1 && GetSize(gate_cells) == 1)
+ {
+ strategy = "gate-guess";
+ cell_pairs.push_back(make_pair(other_cells[0], gate_cells[0]));
+ goto run_strategy;
+ }
+
+ log_assert(GetSize(gold_cells) + GetSize(gate_cells) + GetSize(other_cells) < 2);
+ continue;
+
+ run_strategy:
+ int total_group_size = GetSize(gold_cells) + GetSize(gate_cells) + GetSize(other_cells);
+ log(" %s merging %d %s cells (from group of %d) using strategy %s:\n", phase ? "Bwd" : "Fwd",
+ 2*GetSize(cell_pairs), log_id(cells_type), total_group_size, strategy);
+ for (auto it : cell_pairs) {
+ log(" Merging cells %s and %s.\n", log_id(it.first), log_id(it.second));
+ merge_cell_pair(it.first, it.second);
+ }
+ }
+
+ if (merge_count > 0)
+ return;
+ }
+
+ log(" Nothing to merge.\n");
+ }
+};
+
+struct EquivStructPass : public Pass {
+ EquivStructPass() : Pass("equiv_struct", "structural equivalence checking") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" equiv_struct [options] [selection]\n");
+ log("\n");
+ log("This command adds additional $equiv cells based on the assumption that the\n");
+ log("gold and gate circuit are structurally equivalent. Note that this can introduce\n");
+ log("bad $equiv cells in cases where the netlists are not structurally equivalent,\n");
+ log("for example when analyzing circuits with cells with commutative inputs. This\n");
+ log("command will also de-duplicate gates.\n");
+ log("\n");
+ log(" -fwd\n");
+ log(" by default this command performans forward sweeps until nothing can\n");
+ log(" be merged by forwards sweeps, then backward sweeps until forward\n");
+ log(" sweeps are effective again. with this option set only forward sweeps\n");
+ log(" are performed.\n");
+ log("\n");
+ log(" -fwonly <cell_type>\n");
+ log(" add the specified cell type to the list of cell types that are only\n");
+ log(" merged in forward sweeps and never in backward sweeps. $equiv is in\n");
+ log(" this list automatically.\n");
+ log("\n");
+ log(" -icells\n");
+ log(" by default, the internal RTL and gate cell types are ignored. add\n");
+ log(" this option to also process those cell types with this command.\n");
+ log("\n");
+ log(" -maxiter <N>\n");
+ log(" maximum number of iterations to run before aborting\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, Design *design)
+ {
+ pool<IdString> fwonly_cells({ "$equiv" });
+ bool mode_icells = false;
+ bool mode_fwd = false;
+ int max_iter = -1;
+
+ log_header(design, "Executing EQUIV_STRUCT pass.\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ if (args[argidx] == "-fwd") {
+ mode_fwd = true;
+ continue;
+ }
+ if (args[argidx] == "-icells") {
+ mode_icells = true;
+ continue;
+ }
+ if (args[argidx] == "-fwonly" && argidx+1 < args.size()) {
+ fwonly_cells.insert(RTLIL::escape_id(args[++argidx]));
+ continue;
+ }
+ if (args[argidx] == "-maxiter" && argidx+1 < args.size()) {
+ max_iter = atoi(args[++argidx].c_str());
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto module : design->selected_modules()) {
+ int module_merge_count = 0;
+ log("Running equiv_struct on module %s:\n", log_id(module));
+ for (int iter = 0;; iter++) {
+ if (iter == max_iter) {
+ log(" Reached iteration limit of %d.\n", iter);
+ break;
+ }
+ EquivStructWorker worker(module, mode_fwd, mode_icells, fwonly_cells, iter+1);
+ if (worker.merge_count == 0)
+ break;
+ module_merge_count += worker.merge_count;
+ }
+ if (module_merge_count)
+ log(" Performed a total of %d merges in module %s.\n", module_merge_count, log_id(module));
+ }
+ }
+} EquivStructPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/fsm/fsm.cc b/passes/fsm/fsm.cc
index e76be40c2..3b537ecd8 100644
--- a/passes/fsm/fsm.cc
+++ b/passes/fsm/fsm.cc
@@ -2,11 +2,11 @@
* 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
@@ -34,7 +34,7 @@ struct FsmPass : public Pass {
log(" fsm [options] [selection]\n");
log("\n");
log("This pass calls all the other fsm_* passes in a useful order. This performs\n");
- log("FSM extraction and optimiziation. It also calls opt_clean as needed:\n");
+ log("FSM extraction and optimization. It also calls opt_clean as needed:\n");
log("\n");
log(" fsm_detect unless got option -nodetect\n");
log(" fsm_extract\n");
@@ -59,7 +59,7 @@ struct FsmPass : public Pass {
log(" -expand, -norecode, -export, -nomap\n");
log(" enable or disable passes as indicated above\n");
log("\n");
- log(" -encoding tye\n");
+ log(" -encoding type\n");
log(" -fm_set_fsm_file file\n");
log(" -encfile file\n");
log(" passed through to fsm_recode pass\n");
@@ -76,7 +76,7 @@ struct FsmPass : public Pass {
std::string encfile_opt;
std::string encoding_opt;
- log_header("Executing FSM pass (extract and optimize FSM).\n");
+ log_header(design, "Executing FSM pass (extract and optimize FSM).\n");
log_push();
size_t argidx;
@@ -145,5 +145,5 @@ struct FsmPass : public Pass {
log_pop();
}
} FsmPass;
-
+
PRIVATE_NAMESPACE_END
diff --git a/passes/fsm/fsm_detect.cc b/passes/fsm/fsm_detect.cc
index c89553c6b..5a240ba60 100644
--- a/passes/fsm/fsm_detect.cc
+++ b/passes/fsm/fsm_detect.cc
@@ -2,11 +2,11 @@
* 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
@@ -34,7 +34,7 @@ static SigSet<sig2driver_entry_t> sig2driver, sig2user;
static std::set<RTLIL::Cell*> muxtree_cells;
static SigPool sig_at_port;
-static bool check_state_mux_tree(RTLIL::SigSpec old_sig, RTLIL::SigSpec sig, SigPool &recursion_monitor)
+static bool check_state_mux_tree(RTLIL::SigSpec old_sig, RTLIL::SigSpec sig, pool<Cell*> &recursion_monitor)
{
if (sig_at_port.check_any(assign_map(sig)))
return false;
@@ -42,31 +42,39 @@ static bool check_state_mux_tree(RTLIL::SigSpec old_sig, RTLIL::SigSpec sig, Sig
if (sig.is_fully_const() || old_sig == sig)
return true;
- if (recursion_monitor.check_any(sig)) {
- log_warning("logic loop in mux tree at signal %s in module %s.\n",
- log_signal(sig), RTLIL::id2cstr(module->name));
- return false;
- }
-
- recursion_monitor.add(sig);
-
std::set<sig2driver_entry_t> cellport_list;
sig2driver.find(sig, cellport_list);
- for (auto &cellport : cellport_list) {
+ for (auto &cellport : cellport_list)
+ {
if ((cellport.first->type != "$mux" && cellport.first->type != "$pmux") || cellport.second != "\\Y")
return false;
+
+ if (recursion_monitor.count(cellport.first)) {
+ log_warning("logic loop in mux tree at signal %s in module %s.\n",
+ log_signal(sig), RTLIL::id2cstr(module->name));
+ return false;
+ }
+
+ recursion_monitor.insert(cellport.first);
+
RTLIL::SigSpec sig_a = assign_map(cellport.first->getPort("\\A"));
RTLIL::SigSpec sig_b = assign_map(cellport.first->getPort("\\B"));
- if (!check_state_mux_tree(old_sig, sig_a, recursion_monitor))
+
+ if (!check_state_mux_tree(old_sig, sig_a, recursion_monitor)) {
+ recursion_monitor.erase(cellport.first);
return false;
+ }
+
for (int i = 0; i < sig_b.size(); i += sig_a.size())
- if (!check_state_mux_tree(old_sig, sig_b.extract(i, sig_a.size()), recursion_monitor))
+ if (!check_state_mux_tree(old_sig, sig_b.extract(i, sig_a.size()), recursion_monitor)) {
+ recursion_monitor.erase(cellport.first);
return false;
+ }
+
+ recursion_monitor.erase(cellport.first);
muxtree_cells.insert(cellport.first);
}
- recursion_monitor.del(sig);
-
return true;
}
@@ -81,6 +89,8 @@ static bool check_state_users(RTLIL::SigSpec sig)
RTLIL::Cell *cell = cellport.first;
if (muxtree_cells.count(cell) > 0)
continue;
+ if (cell->type == "$logic_not" && assign_map(cell->getPort("\\A")) == sig)
+ continue;
if (cellport.second != "\\A" && cellport.second != "\\B")
return false;
if (!cell->hasPort("\\A") || !cell->hasPort("\\B") || !cell->hasPort("\\Y"))
@@ -100,6 +110,8 @@ static bool check_state_users(RTLIL::SigSpec sig)
static void detect_fsm(RTLIL::Wire *wire)
{
+ if (wire->attributes.count("\\init") > 0)
+ return;
if (wire->attributes.count("\\fsm_encoding") > 0 || wire->width <= 1)
return;
if (sig_at_port.check_any(assign_map(RTLIL::SigSpec(wire))))
@@ -111,7 +123,7 @@ static void detect_fsm(RTLIL::Wire *wire)
if ((cellport.first->type != "$dff" && cellport.first->type != "$adff") || cellport.second != "\\Q")
continue;
muxtree_cells.clear();
- SigPool recursion_monitor;
+ pool<Cell*> recursion_monitor;
RTLIL::SigSpec sig_q = assign_map(cellport.first->getPort("\\Q"));
RTLIL::SigSpec sig_d = assign_map(cellport.first->getPort("\\D"));
if (sig_q == RTLIL::SigSpec(wire) && check_state_mux_tree(sig_q, sig_d, recursion_monitor) && check_state_users(sig_q)) {
@@ -142,7 +154,7 @@ struct FsmDetectPass : public Pass {
}
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
{
- log_header("Executing FSM_DETECT pass (finding FSMs in design).\n");
+ log_header(design, "Executing FSM_DETECT pass (finding FSMs in design).\n");
extra_args(args, 1, design);
CellTypes ct;
@@ -191,5 +203,5 @@ struct FsmDetectPass : public Pass {
muxtree_cells.clear();
}
} FsmDetectPass;
-
+
PRIVATE_NAMESPACE_END
diff --git a/passes/fsm/fsm_expand.cc b/passes/fsm/fsm_expand.cc
index a261eb22b..3ded3f377 100644
--- a/passes/fsm/fsm_expand.cc
+++ b/passes/fsm/fsm_expand.cc
@@ -2,11 +2,11 @@
* 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
@@ -253,12 +253,12 @@ struct FsmExpandPass : public Pass {
log("\n");
log("The fsm_extract pass is conservative about the cells that belong to a finite\n");
log("state machine. This pass can be used to merge additional auxiliary gates into\n");
- log("the finate state machine.\n");
+ log("the finite state machine.\n");
log("\n");
}
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
{
- log_header("Executing FSM_EXPAND pass (merging auxiliary logic into FSMs).\n");
+ log_header(design, "Executing FSM_EXPAND pass (merging auxiliary logic into FSMs).\n");
extra_args(args, 1, design);
for (auto &mod_it : design->modules_) {
@@ -275,5 +275,5 @@ struct FsmExpandPass : public Pass {
}
}
} FsmExpandPass;
-
+
PRIVATE_NAMESPACE_END
diff --git a/passes/fsm/fsm_export.cc b/passes/fsm/fsm_export.cc
index ad9270334..1cbfcfae8 100644
--- a/passes/fsm/fsm_export.cc
+++ b/passes/fsm/fsm_export.cc
@@ -3,11 +3,11 @@
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* Copyright (C) 2012 Martin Schmölzer <martin@schmoelzer.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
@@ -152,7 +152,7 @@ struct FsmExportPass : public Pass {
bool flag_origenc = false;
size_t argidx;
- log_header("Executing FSM_EXPORT pass (exporting FSMs in KISS2 file format).\n");
+ log_header(design, "Executing FSM_EXPORT pass (exporting FSMs in KISS2 file format).\n");
for (argidx = 1; argidx < args.size(); argidx++) {
arg = args[argidx];
diff --git a/passes/fsm/fsm_extract.cc b/passes/fsm/fsm_extract.cc
index 68667ef02..95cb498e3 100644
--- a/passes/fsm/fsm_extract.cc
+++ b/passes/fsm/fsm_extract.cc
@@ -2,11 +2,11 @@
* 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
@@ -56,6 +56,17 @@ static bool find_states(RTLIL::SigSpec sig, const RTLIL::SigSpec &dff_out, RTLIL
std::set<sig2driver_entry_t> cellport_list;
sig2driver.find(sig, cellport_list);
+
+ if (GetSize(cellport_list) > 1) {
+ log(" found %d combined drivers for state signal %s.\n", GetSize(cellport_list), log_signal(sig));
+ return false;
+ }
+
+ if (GetSize(cellport_list) < 1) {
+ log(" found no driver for state signal %s.\n", log_signal(sig));
+ return false;
+ }
+
for (auto &cellport : cellport_list)
{
RTLIL::Cell *cell = module->cells_.at(cellport.first);
@@ -90,9 +101,11 @@ static bool find_states(RTLIL::SigSpec sig, const RTLIL::SigSpec &dff_out, RTLIL
log(" found reset state: %s (guessed from mux tree)\n", log_signal(*reset_state));
} while (0);
- if (ctrl.extract(sig_s).size() == 0) {
- log(" found ctrl input: %s\n", log_signal(sig_s));
- ctrl.append(sig_s);
+ for (auto sig_s_bit : sig_s) {
+ if (ctrl.extract(sig_s_bit).empty()) {
+ log(" found ctrl input: %s\n", log_signal(sig_s_bit));
+ ctrl.append(sig_s_bit);
+ }
}
if (!find_states(sig_aa, dff_out, ctrl, states))
@@ -241,7 +254,7 @@ static void extract_fsm(RTLIL::Wire *wire)
{
log("Extracting FSM `%s' from module `%s'.\n", wire->name.c_str(), module->name.c_str());
- // get input and output signals for state ff
+ // get input and output signals for state ff
RTLIL::SigSpec dff_out = assign_map(RTLIL::SigSpec(wire));
RTLIL::SigSpec dff_in(RTLIL::State::Sm, wire->width);
@@ -305,7 +318,9 @@ static void extract_fsm(RTLIL::Wire *wire)
for (auto &cellport : cellport_list) {
RTLIL::Cell *cell = module->cells_.at(cellport.first);
RTLIL::SigSpec sig_a = assign_map(cell->getPort("\\A"));
- RTLIL::SigSpec sig_b = assign_map(cell->getPort("\\B"));
+ RTLIL::SigSpec sig_b;
+ if (cell->hasPort("\\B"))
+ sig_b = assign_map(cell->getPort("\\B"));
RTLIL::SigSpec sig_y = assign_map(cell->getPort("\\Y"));
if (cellport.second == "\\A" && !sig_b.is_fully_const())
continue;
@@ -401,7 +416,7 @@ struct FsmExtractPass : public Pass {
}
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
{
- log_header("Executing FSM_EXTRACT pass (extracting FSM from design).\n");
+ log_header(design, "Executing FSM_EXTRACT pass (extracting FSM from design).\n");
extra_args(args, 1, design);
CellTypes ct;
@@ -458,5 +473,5 @@ struct FsmExtractPass : public Pass {
sig2trigger.clear();
}
} FsmExtractPass;
-
+
PRIVATE_NAMESPACE_END
diff --git a/passes/fsm/fsm_info.cc b/passes/fsm/fsm_info.cc
index 4a1f1d9a2..2cc1a7d53 100644
--- a/passes/fsm/fsm_info.cc
+++ b/passes/fsm/fsm_info.cc
@@ -2,11 +2,11 @@
* 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
@@ -43,7 +43,7 @@ struct FsmInfoPass : public Pass {
}
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
{
- log_header("Executing FSM_INFO pass (dumping all available information on FSM cells).\n");
+ log_header(design, "Executing FSM_INFO pass (dumping all available information on FSM cells).\n");
extra_args(args, 1, design);
for (auto &mod_it : design->modules_)
@@ -58,5 +58,5 @@ struct FsmInfoPass : public Pass {
}
}
} FsmInfoPass;
-
+
PRIVATE_NAMESPACE_END
diff --git a/passes/fsm/fsm_map.cc b/passes/fsm/fsm_map.cc
index 155801a3a..5b32ed599 100644
--- a/passes/fsm/fsm_map.cc
+++ b/passes/fsm/fsm_map.cc
@@ -2,11 +2,11 @@
* 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
@@ -335,7 +335,7 @@ struct FsmMapPass : public Pass {
}
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
{
- log_header("Executing FSM_MAP pass (mapping FSMs to basic logic).\n");
+ log_header(design, "Executing FSM_MAP pass (mapping FSMs to basic logic).\n");
extra_args(args, 1, design);
for (auto &mod_it : design->modules_) {
@@ -350,5 +350,5 @@ struct FsmMapPass : public Pass {
}
}
} FsmMapPass;
-
+
PRIVATE_NAMESPACE_END
diff --git a/passes/fsm/fsm_opt.cc b/passes/fsm/fsm_opt.cc
index 4b93d79f9..5b1da44fc 100644
--- a/passes/fsm/fsm_opt.cc
+++ b/passes/fsm/fsm_opt.cc
@@ -2,11 +2,11 @@
* 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
@@ -75,14 +75,14 @@ struct FsmOpt
fsm_data.reset_state = old_to_new_state.at(fsm_data.reset_state);
}
}
-
+
bool signal_is_unused(RTLIL::SigSpec sig)
{
- RTLIL::SigBit bit = sig.to_single_sigbit();
+ RTLIL::SigBit bit = sig.as_bit();
if (bit.wire == NULL || bit.wire->attributes.count("\\unused_bits") == 0)
return false;
-
+
char *str = strdup(bit.wire->attributes["\\unused_bits"].decode_string().c_str());
for (char *tok = strtok(str, " "); tok != NULL; tok = strtok(NULL, " ")) {
if (tok[0] && bit.offset == atoi(tok)) {
@@ -336,7 +336,7 @@ struct FsmOptPass : public Pass {
}
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
{
- log_header("Executing FSM_OPT pass (simple optimizations of FSMs).\n");
+ log_header(design, "Executing FSM_OPT pass (simple optimizations of FSMs).\n");
extra_args(args, 1, design);
for (auto &mod_it : design->modules_) {
@@ -347,5 +347,5 @@ struct FsmOptPass : public Pass {
}
}
} FsmOptPass;
-
+
PRIVATE_NAMESPACE_END
diff --git a/passes/fsm/fsm_recode.cc b/passes/fsm/fsm_recode.cc
index 169968103..5102d8334 100644
--- a/passes/fsm/fsm_recode.cc
+++ b/passes/fsm/fsm_recode.cc
@@ -2,11 +2,11 @@
* 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
@@ -85,7 +85,7 @@ static void fsm_recode(RTLIL::Cell *cell, RTLIL::Module *module, FILE *fm_set_fs
fsm_data.state_bits = fsm_data.state_table.size();
} else
if (encoding == "binary") {
- int new_num_state_bits = ceil(log2(fsm_data.state_table.size()));
+ int new_num_state_bits = ceil_log2(fsm_data.state_table.size());
if (fsm_data.state_bits == new_num_state_bits) {
log(" existing encoding is already a packed binary encoding.\n");
return;
@@ -93,7 +93,7 @@ static void fsm_recode(RTLIL::Cell *cell, RTLIL::Module *module, FILE *fm_set_fs
fsm_data.state_bits = new_num_state_bits;
} else
log_error("FSM encoding `%s' is not supported!\n", encoding.c_str());
-
+
if (encfile)
fprintf(encfile, ".fsm %s %s\n", log_id(module), RTLIL::unescape_id(cell->parameters["\\NAME"].decode_string()).c_str());
@@ -134,7 +134,7 @@ struct FsmRecodePass : public Pass {
log("\n");
log("This pass reassign the state encodings for FSM cells. At the moment only\n");
log("one-hot encoding and binary encoding is supported.\n");
-
+
log(" -encoding <type>\n");
log(" specify the encoding scheme used for FSMs without the\n");
log(" 'fsm_encoding' attribute or with the attribute set to `auto'.\n");
@@ -157,7 +157,7 @@ struct FsmRecodePass : public Pass {
FILE *encfile = NULL;
std::string default_encoding;
- log_header("Executing FSM_RECODE pass (re-assigning FSM state encoding).\n");
+ log_header(design, "Executing FSM_RECODE pass (re-assigning FSM state encoding).\n");
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++) {
std::string arg = args[argidx];
@@ -193,5 +193,5 @@ struct FsmRecodePass : public Pass {
fclose(encfile);
}
} FsmRecodePass;
-
+
PRIVATE_NAMESPACE_END
diff --git a/passes/fsm/fsmdata.h b/passes/fsm/fsmdata.h
index 5671d0006..68222769a 100644
--- a/passes/fsm/fsmdata.h
+++ b/passes/fsm/fsmdata.h
@@ -2,11 +2,11 @@
* 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
@@ -39,7 +39,7 @@ struct FsmData
int state_num_log2 = 0;
for (int i = state_table.size(); i > 0; i = i >> 1)
state_num_log2++;
- state_num_log2 = std::max(state_num_log2, 1);
+ state_num_log2 = max(state_num_log2, 1);
cell->parameters["\\STATE_BITS"] = RTLIL::Const(state_bits);
cell->parameters["\\STATE_NUM"] = RTLIL::Const(state_table.size());
diff --git a/passes/hierarchy/Makefile.inc b/passes/hierarchy/Makefile.inc
index 99aa1e116..1fb669c11 100644
--- a/passes/hierarchy/Makefile.inc
+++ b/passes/hierarchy/Makefile.inc
@@ -1,4 +1,5 @@
OBJS += passes/hierarchy/hierarchy.o
+OBJS += passes/hierarchy/singleton.o
OBJS += passes/hierarchy/submod.o
diff --git a/passes/hierarchy/hierarchy.cc b/passes/hierarchy/hierarchy.cc
index 58b796a62..94b93de5d 100644
--- a/passes/hierarchy/hierarchy.cc
+++ b/passes/hierarchy/hierarchy.cc
@@ -2,11 +2,11 @@
* 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
@@ -66,7 +66,7 @@ void generate(RTLIL::Design *design, const std::vector<std::string> &celltypes,
for (auto &conn : i2.second->connections()) {
if (conn.first[0] != '$')
portnames.insert(conn.first);
- portwidths[conn.first] = std::max(portwidths[conn.first], conn.second.size());
+ portwidths[conn.first] = max(portwidths[conn.first], conn.second.size());
}
for (auto &para : i2.second->parameters)
parameters.insert(para.first);
@@ -84,8 +84,8 @@ void generate(RTLIL::Design *design, const std::vector<std::string> &celltypes,
for (auto &decl : portdecls)
if (decl.index > 0) {
- portwidths[decl.portname] = std::max(portwidths[decl.portname], 1);
- portwidths[decl.portname] = std::max(portwidths[decl.portname], portwidths[stringf("$%d", decl.index)]);
+ portwidths[decl.portname] = max(portwidths[decl.portname], 1);
+ portwidths[decl.portname] = max(portwidths[decl.portname], portwidths[stringf("$%d", decl.index)]);
log(" port %d: %s [%d:0] %s\n", decl.index, decl.input ? decl.output ? "inout" : "input" : "output", portwidths[decl.portname]-1, RTLIL::id2cstr(decl.portname));
if (indices.count(decl.index) > ports.size())
log_error("Port index (%d) exceeds number of found ports (%d).\n", decl.index, int(ports.size()));
@@ -106,7 +106,7 @@ void generate(RTLIL::Design *design, const std::vector<std::string> &celltypes,
log_assert(!indices.empty());
indices.erase(d.index);
ports[d.index-1] = d;
- portwidths[d.portname] = std::max(portwidths[d.portname], 1);
+ portwidths[d.portname] = max(portwidths[d.portname], 1);
log(" port %d: %s [%d:0] %s\n", d.index, d.input ? d.output ? "inout" : "input" : "output", portwidths[d.portname]-1, RTLIL::id2cstr(d.portname));
goto found_matching_decl;
}
@@ -261,14 +261,14 @@ bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check
return did_something;
}
-void hierarchy_worker(RTLIL::Design *design, std::set<RTLIL::Module*> &used, RTLIL::Module *mod, int indent)
+void hierarchy_worker(RTLIL::Design *design, std::set<RTLIL::Module*, IdString::compare_ptr_by_name<Module>> &used, RTLIL::Module *mod, int indent)
{
if (used.count(mod) > 0)
return;
if (indent == 0)
log("Top module: %s\n", mod->name.c_str());
- else
+ else if (!mod->get_bool_attribute("\\blackbox"))
log("Used module: %*s%s\n", indent, "", mod->name.c_str());
used.insert(mod);
@@ -285,9 +285,9 @@ void hierarchy_worker(RTLIL::Design *design, std::set<RTLIL::Module*> &used, RTL
}
}
-void hierarchy(RTLIL::Design *design, RTLIL::Module *top, bool purge_lib, bool first_pass)
+void hierarchy_clean(RTLIL::Design *design, RTLIL::Module *top, bool purge_lib)
{
- std::set<RTLIL::Module*> used;
+ std::set<RTLIL::Module*, IdString::compare_ptr_by_name<Module>> used;
hierarchy_worker(design, used, top, 0);
std::vector<RTLIL::Module*> del_modules;
@@ -295,17 +295,17 @@ void hierarchy(RTLIL::Design *design, RTLIL::Module *top, bool purge_lib, bool f
if (used.count(it.second) == 0)
del_modules.push_back(it.second);
+ int del_counter = 0;
for (auto mod : del_modules) {
- if (first_pass && mod->name.substr(0, 9) == "$abstract")
- continue;
if (!purge_lib && mod->get_bool_attribute("\\blackbox"))
continue;
log("Removing unused module `%s'.\n", mod->name.c_str());
design->modules_.erase(mod->name);
+ del_counter++;
delete mod;
}
- log("Removed %d unused modules.\n", GetSize(del_modules));
+ log("Removed %d unused modules.\n", del_counter);
}
bool set_keep_assert(std::map<RTLIL::Module*, bool> &cache, RTLIL::Module *mod)
@@ -313,12 +313,23 @@ bool set_keep_assert(std::map<RTLIL::Module*, bool> &cache, RTLIL::Module *mod)
if (cache.count(mod) == 0)
for (auto c : mod->cells()) {
RTLIL::Module *m = mod->design->module(c->type);
- if ((m != nullptr && set_keep_assert(cache, m)) || c->type == "$assert")
+ if ((m != nullptr && set_keep_assert(cache, m)) || c->type.in("$assert", "$assume"))
return cache[mod] = true;
}
return cache[mod];
}
+int find_top_mod_score(Design *design, Module *module, dict<Module*, int> &db)
+{
+ if (db.count(module) == 0) {
+ db[module] = 0;
+ for (auto cell : module->cells())
+ if (design->module(cell->type))
+ db[module] = max(db[module], find_top_mod_score(design, design->module(cell->type), db) + 1);
+ }
+ return db.at(module);
+}
+
struct HierarchyPass : public Pass {
HierarchyPass() : Pass("hierarchy", "check, expand and clean up design hierarchy") { }
virtual void help()
@@ -339,7 +350,7 @@ struct HierarchyPass : public Pass {
log("\n");
log(" -purge_lib\n");
log(" by default the hierarchy command will not remove library (blackbox)\n");
- log(" module. use this options to also remove unused blackbox modules.\n");
+ log(" modules. use this option to also remove unused blackbox modules.\n");
log("\n");
log(" -libdir <directory>\n");
log(" search for files named <module_name>.v in the specified directory\n");
@@ -363,6 +374,9 @@ struct HierarchyPass : public Pass {
log(" specified top module. otherwise a module with the 'top' attribute set\n");
log(" will implicitly be used as top module, if such a module exists.\n");
log("\n");
+ log(" -auto-top\n");
+ log(" automatically determine the top of the design hierarchy and mark it.\n");
+ log("\n");
log("In -generate mode this pass generates blackbox modules for the given cell\n");
log("types (wildcards supported). For this the design is searched for cells that\n");
log("match the given types and then the given port declarations are used to\n");
@@ -372,7 +386,7 @@ struct HierarchyPass : public Pass {
log("\n");
log("Input ports are specified with the 'i' prefix, output ports with the 'o'\n");
log("prefix and inout ports with the 'io' prefix. The optional <num> specifies\n");
- log("the position of the port in the parameter list (needed when instanciated\n");
+ log("the position of the port in the parameter list (needed when instantiated\n");
log("using positional arguments). When <num> is not specified, the <portname> can\n");
log("also contain wildcard characters.\n");
log("\n");
@@ -382,13 +396,14 @@ struct HierarchyPass : public Pass {
}
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
{
- log_header("Executing HIERARCHY pass (managing design hierarchy).\n");
+ log_header(design, "Executing HIERARCHY pass (managing design hierarchy).\n");
bool flag_check = false;
bool purge_lib = false;
RTLIL::Module *top_mod = NULL;
std::vector<std::string> libdirs;
+ bool auto_top_mode = false;
bool generate_mode = false;
bool keep_positionals = false;
bool nokeep_asserts = false;
@@ -470,6 +485,10 @@ struct HierarchyPass : public Pass {
log_cmd_error("Module `%s' not found!\n", args[argidx].c_str());
continue;
}
+ if (args[argidx] == "-auto-top") {
+ auto_top_mode = true;
+ continue;
+ }
break;
}
extra_args(args, argidx, design, false);
@@ -481,35 +500,47 @@ struct HierarchyPass : public Pass {
log_push();
- if (top_mod == NULL)
+ if (top_mod == nullptr)
for (auto &mod_it : design->modules_)
if (mod_it.second->get_bool_attribute("\\top"))
top_mod = mod_it.second;
- if (top_mod != NULL)
- hierarchy(design, top_mod, purge_lib, true);
+ if (top_mod == nullptr && auto_top_mode) {
+ log_header(design, "Finding top of design hierarchy..\n");
+ dict<Module*, int> db;
+ for (Module *mod : design->selected_modules()) {
+ int score = find_top_mod_score(design, mod, db);
+ log("root of %3d design levels: %-20s\n", score, log_id(mod));
+ if (!top_mod || score > db[top_mod])
+ top_mod = mod;
+ }
+ if (top_mod != nullptr)
+ log("Automatically selected %s as design top module.\n", log_id(top_mod));
+ }
bool did_something = true;
- bool did_something_once = false;
- while (did_something) {
+ while (did_something)
+ {
did_something = false;
- std::vector<RTLIL::IdString> modnames;
- modnames.reserve(design->modules_.size());
- for (auto &mod_it : design->modules_)
- modnames.push_back(mod_it.first);
- for (auto &modname : modnames) {
- if (design->modules_.count(modname) == 0)
- continue;
- if (expand_module(design, design->modules_[modname], flag_check, libdirs))
+
+ std::set<RTLIL::Module*, IdString::compare_ptr_by_name<Module>> used_modules;
+ if (top_mod != NULL) {
+ log_header(design, "Analyzing design hierarchy..\n");
+ hierarchy_worker(design, used_modules, top_mod, 0);
+ } else {
+ for (auto mod : design->modules())
+ used_modules.insert(mod);
+ }
+
+ for (auto module : used_modules) {
+ if (expand_module(design, module, flag_check, libdirs))
did_something = true;
}
- if (did_something)
- did_something_once = true;
}
- if (top_mod != NULL && did_something_once) {
- log_header("Re-running hierarchy analysis..\n");
- hierarchy(design, top_mod, purge_lib, false);
+ if (top_mod != NULL) {
+ log_header(design, "Analyzing design hierarchy..\n");
+ hierarchy_clean(design, top_mod, purge_lib);
}
if (top_mod != NULL) {
@@ -580,5 +611,5 @@ struct HierarchyPass : public Pass {
log_pop();
}
} HierarchyPass;
-
+
PRIVATE_NAMESPACE_END
diff --git a/passes/hierarchy/singleton.cc b/passes/hierarchy/singleton.cc
new file mode 100644
index 000000000..03c365fb5
--- /dev/null
+++ b/passes/hierarchy/singleton.cc
@@ -0,0 +1,101 @@
+/*
+ * 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"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct SingletonPass : public Pass {
+ SingletonPass() : Pass("singleton", "create singleton modules") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" singleton [selection]\n");
+ log("\n");
+ log("By default, a module that is instantiated by several other modules is only\n");
+ log("kept once in the design. This preserves the original modularity of the design\n");
+ log("and reduces the overall size of the design in memory. But it prevents certain\n");
+ log("optimizations and other operations on the design. This pass creates singleton\n");
+ log("modules for all selected cells. The created modules are marked with the\n");
+ log("'singleton' attribute.\n");
+ log("\n");
+ log("This commands only operates on modules that by themself have the 'singleton'\n");
+ log("attribute set (the 'top' module is a singleton implicitly).\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ log_header(design, "Executing SINGLETON pass (creating singleton modules).\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ // if (args[argidx] == "-check") {
+ // flag_check = true;
+ // continue;
+ // }
+ }
+ extra_args(args, argidx, design);
+
+ bool did_something = true;
+ int singleton_cnt = 0;
+
+ while (did_something)
+ {
+ did_something = false;
+
+ for (auto module : design->selected_modules())
+ {
+ if (!module->get_bool_attribute("\\singleton") && !module->get_bool_attribute("\\top"))
+ continue;
+
+ for (auto cell : module->selected_cells())
+ {
+ auto tmod = design->module(cell->type);
+
+ if (tmod == nullptr)
+ continue;
+
+ if (tmod->get_bool_attribute("\\blackbox"))
+ continue;
+
+ if (tmod->get_bool_attribute("\\singleton"))
+ continue;
+
+ cell->type = module->name.str() + "." + log_id(cell->name);
+ log("Creating singleton '%s'.\n", log_id(cell->type));
+
+ auto smod = tmod->clone();
+ smod->name = cell->type;
+ smod->set_bool_attribute("\\singleton");
+ design->add(smod);
+
+ did_something = true;
+ singleton_cnt++;
+ }
+ }
+ }
+
+ log("Created %d singleton modules.\n", singleton_cnt);
+ }
+} SingletonPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/hierarchy/submod.cc b/passes/hierarchy/submod.cc
index 8d4012c53..9f312f82d 100644
--- a/passes/hierarchy/submod.cc
+++ b/passes/hierarchy/submod.cc
@@ -2,11 +2,11 @@
* 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
@@ -32,6 +32,8 @@ struct SubmodWorker
CellTypes ct;
RTLIL::Design *design;
RTLIL::Module *module;
+
+ bool copy_mode;
std::string opt_name;
struct SubModule
@@ -177,21 +179,25 @@ struct SubmodWorker
bit.wire = wire_flags[bit.wire].new_wire;
}
log(" cell %s (%s)\n", new_cell->name.c_str(), new_cell->type.c_str());
- module->remove(cell);
+ if (!copy_mode)
+ module->remove(cell);
}
submod.cells.clear();
- RTLIL::Cell *new_cell = module->addCell(submod.full_name, submod.full_name);
- for (auto &it : wire_flags)
- {
- RTLIL::Wire *old_wire = it.first;
- RTLIL::Wire *new_wire = it.second.new_wire;
- if (new_wire->port_id > 0)
- new_cell->setPort(new_wire->name, RTLIL::SigSpec(old_wire));
+ if (!copy_mode) {
+ RTLIL::Cell *new_cell = module->addCell(submod.full_name, submod.full_name);
+ for (auto &it : wire_flags)
+ {
+ RTLIL::Wire *old_wire = it.first;
+ RTLIL::Wire *new_wire = it.second.new_wire;
+ if (new_wire->port_id > 0)
+ new_cell->setPort(new_wire->name, RTLIL::SigSpec(old_wire));
+ }
}
}
- SubmodWorker(RTLIL::Design *design, RTLIL::Module *module, std::string opt_name = std::string()) : design(design), module(module), opt_name(opt_name)
+ SubmodWorker(RTLIL::Design *design, RTLIL::Module *module, bool copy_mode = false, std::string opt_name = std::string()) :
+ design(design), module(module), copy_mode(copy_mode), opt_name(opt_name)
{
if (!design->selected_whole_module(module->name) && opt_name.empty())
return;
@@ -266,7 +272,7 @@ struct SubmodPass : public Pass {
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
- log(" submod [selection]\n");
+ log(" submod [-copy] [selection]\n");
log("\n");
log("This pass identifies all cells with the 'submod' attribute and moves them to\n");
log("a newly created module. The value of the attribute is used as name for the\n");
@@ -279,19 +285,24 @@ struct SubmodPass : public Pass {
log("or memories.\n");
log("\n");
log("\n");
- log(" submod -name <name> [selection]\n");
+ log(" submod -name <name> [-copy] [selection]\n");
log("\n");
log("As above, but don't use the 'submod' attribute but instead use the selection.\n");
log("Only objects from one module might be selected. The value of the -name option\n");
log("is used as the value of the 'submod' attribute above.\n");
log("\n");
+ log("By default the cells are 'moved' from the source module and the source module\n");
+ log("will use an instance of the new module after this command is finished. Call\n");
+ log("with -copy to not modify the source module.\n");
+ log("\n");
}
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
{
- log_header("Executing SUBMOD pass (moving cells to submodules as requested).\n");
+ log_header(design, "Executing SUBMOD pass (moving cells to submodules as requested).\n");
log_push();
std::string opt_name;
+ bool copy_mode = false;
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++) {
@@ -299,6 +310,10 @@ struct SubmodPass : public Pass {
opt_name = args[++argidx];
continue;
}
+ if (args[argidx] == "-copy") {
+ copy_mode = true;
+ continue;
+ }
break;
}
extra_args(args, argidx, design);
@@ -306,7 +321,7 @@ struct SubmodPass : public Pass {
if (opt_name.empty())
{
Pass::call(design, "opt_clean");
- log_header("Continuing SUBMOD pass.\n");
+ log_header(design, "Continuing SUBMOD pass.\n");
std::set<RTLIL::IdString> handled_modules;
@@ -319,7 +334,7 @@ struct SubmodPass : public Pass {
queued_modules.push_back(mod_it.first);
for (auto &modname : queued_modules)
if (design->modules_.count(modname) != 0) {
- SubmodWorker worker(design, design->modules_[modname]);
+ SubmodWorker worker(design, design->modules_[modname], copy_mode);
handled_modules.insert(modname);
did_something = true;
}
@@ -341,13 +356,13 @@ struct SubmodPass : public Pass {
log("Nothing selected -> do nothing.\n");
else {
Pass::call_on_module(design, module, "opt_clean");
- log_header("Continuing SUBMOD pass.\n");
- SubmodWorker worker(design, module, opt_name);
+ log_header(design, "Continuing SUBMOD pass.\n");
+ SubmodWorker worker(design, module, copy_mode, opt_name);
}
}
log_pop();
}
} SubmodPass;
-
+
PRIVATE_NAMESPACE_END
diff --git a/passes/memory/memory.cc b/passes/memory/memory.cc
index 866efae77..e3c627607 100644
--- a/passes/memory/memory.cc
+++ b/passes/memory/memory.cc
@@ -2,11 +2,11 @@
* 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
@@ -31,11 +31,11 @@ struct MemoryPass : public Pass {
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
- log(" memory [-nomap] [-bram <bram_rules>] [selection]\n");
+ log(" memory [-nomap] [-nordff] [-bram <bram_rules>] [selection]\n");
log("\n");
log("This pass calls all the other memory_* passes in a useful order:\n");
log("\n");
- log(" memory_dff\n");
+ log(" memory_dff [-nordff]\n");
log(" opt_clean\n");
log(" memory_share\n");
log(" opt_clean\n");
@@ -50,9 +50,10 @@ struct MemoryPass : public Pass {
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
{
bool flag_nomap = false;
+ bool flag_nordff = false;
string memory_bram_opts;
- log_header("Executing MEMORY pass.\n");
+ log_header(design, "Executing MEMORY pass.\n");
log_push();
size_t argidx;
@@ -61,6 +62,10 @@ struct MemoryPass : public Pass {
flag_nomap = true;
continue;
}
+ if (args[argidx] == "-nordff") {
+ flag_nordff = true;
+ continue;
+ }
if (argidx+1 < args.size() && args[argidx] == "-bram") {
memory_bram_opts += " -rules " + args[++argidx];
continue;
@@ -69,7 +74,7 @@ struct MemoryPass : public Pass {
}
extra_args(args, argidx, design);
- Pass::call(design, "memory_dff");
+ Pass::call(design, flag_nordff ? "memory_dff -nordff" : "memory_dff");
Pass::call(design, "opt_clean");
Pass::call(design, "memory_share");
Pass::call(design, "opt_clean");
@@ -84,5 +89,5 @@ struct MemoryPass : public Pass {
log_pop();
}
} MemoryPass;
-
+
PRIVATE_NAMESPACE_END
diff --git a/passes/memory/memory_bram.cc b/passes/memory/memory_bram.cc
index 8f4214027..7b5dd08ab 100644
--- a/passes/memory/memory_bram.cc
+++ b/passes/memory/memory_bram.cc
@@ -2,11 +2,11 @@
* 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
@@ -32,6 +32,7 @@ struct rules_t
SigSpec sig_addr, sig_data, sig_en;
bool effective_clkpol;
bool make_transp;
+ bool make_outreg;
int mapped_port;
};
@@ -85,6 +86,7 @@ struct rules_t
pi.clkpol = clkpol[i];
pi.mapped_port = -1;
pi.make_transp = false;
+ pi.make_outreg = false;
pi.effective_clkpol = false;
portinfos.push_back(pi);
}
@@ -110,15 +112,15 @@ struct rules_t
if (ports[i] != other.ports[i])
log_error("Bram %s variants %d and %d have different number of %c-ports.\n", log_id(name), variant, other.variant, 'A'+i);
if (wrmode[i] != other.wrmode[i])
- variant_params[stringf("\\CFG_WRMODE_%c", 'A' + i)] = wrmode[1];
+ variant_params[stringf("\\CFG_WRMODE_%c", 'A' + i)] = wrmode[i];
if (enable[i] != other.enable[i])
- variant_params[stringf("\\CFG_ENABLE_%c", 'A' + i)] = enable[1];
+ variant_params[stringf("\\CFG_ENABLE_%c", 'A' + i)] = enable[i];
if (transp[i] != other.transp[i])
- variant_params[stringf("\\CFG_TRANSP_%c", 'A' + i)] = transp[1];
+ variant_params[stringf("\\CFG_TRANSP_%c", 'A' + i)] = transp[i];
if (clocks[i] != other.clocks[i])
- variant_params[stringf("\\CFG_CLOCKS_%c", 'A' + i)] = clocks[1];
+ variant_params[stringf("\\CFG_CLOCKS_%c", 'A' + i)] = clocks[i];
if (clkpol[i] != other.clkpol[i])
- variant_params[stringf("\\CFG_CLKPOL_%c", 'A' + i)] = clkpol[1];
+ variant_params[stringf("\\CFG_CLKPOL_%c", 'A' + i)] = clkpol[i];
}
}
};
@@ -126,7 +128,7 @@ struct rules_t
struct match_t {
IdString name;
dict<string, int> min_limits, max_limits;
- bool or_next_if_better, make_transp;
+ bool or_next_if_better, make_transp, make_outreg;
char shuffle_enable;
};
@@ -277,6 +279,7 @@ struct rules_t
data.name = RTLIL::escape_id(tokens[1]);
data.or_next_if_better = false;
data.make_transp = false;
+ data.make_outreg = false;
data.shuffle_enable = 0;
while (next_line())
@@ -309,6 +312,12 @@ struct rules_t
continue;
}
+ if (GetSize(tokens) == 1 && tokens[0] == "make_outreg") {
+ data.make_transp = true;
+ data.make_outreg = true;
+ continue;
+ }
+
if (GetSize(tokens) == 1 && tokens[0] == "or_next_if_better") {
data.or_next_if_better = true;
continue;
@@ -320,9 +329,7 @@ struct rules_t
void parse(string filename)
{
- if (filename.substr(0, 2) == "+/")
- filename = proc_share_dirname() + filename.substr(1);
-
+ rewrite_filename(filename);
infile.open(filename);
linecount = 0;
@@ -380,9 +387,9 @@ bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram,
if (pi.clkpol > 1)
clkpol_wr_ports.insert(pi.clkpol);
}
- clocks_max = std::max(clocks_max, pi.clocks);
- clkpol_max = std::max(clkpol_max, pi.clkpol);
- transp_max = std::max(transp_max, pi.transp);
+ clocks_max = max(clocks_max, pi.clocks);
+ clkpol_max = max(clkpol_max, pi.clkpol);
+ transp_max = max(transp_max, pi.transp);
}
log(" Mapping to bram type %s (variant %d):\n", log_id(bram.name), bram.variant);
@@ -393,6 +400,16 @@ bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram,
int mem_width = cell->getParam("\\WIDTH").as_int();
// int mem_offset = cell->getParam("\\OFFSET").as_int();
+ bool cell_init = !SigSpec(cell->getParam("\\INIT")).is_fully_undef();
+ vector<Const> initdata;
+
+ if (cell_init) {
+ Const initparam = cell->getParam("\\INIT");
+ initdata.reserve(mem_size);
+ for (int i=0; i < mem_size; i++)
+ initdata.push_back(initparam.extract(mem_width*i, mem_width, State::Sx));
+ }
+
int wr_ports = cell->getParam("\\WR_PORTS").as_int();
auto wr_clken = SigSpec(cell->getParam("\\WR_CLK_ENABLE"));
auto wr_clkpol = SigSpec(cell->getParam("\\WR_CLK_POLARITY"));
@@ -412,11 +429,12 @@ bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram,
rd_clkpol.extend_u0(rd_ports);
rd_transp.extend_u0(rd_ports);
+ SigSpec rd_en = cell->getPort("\\RD_EN");
SigSpec rd_clk = cell->getPort("\\RD_CLK");
SigSpec rd_data = cell->getPort("\\RD_DATA");
SigSpec rd_addr = cell->getPort("\\RD_ADDR");
- if (match.shuffle_enable && bram.dbits >= portinfos.at(match.shuffle_enable - 'A').enable*2 && portinfos.at(match.shuffle_enable - 'A').enable > 0)
+ if (match.shuffle_enable && bram.dbits >= portinfos.at(match.shuffle_enable - 'A').enable*2 && portinfos.at(match.shuffle_enable - 'A').enable > 0 && wr_ports > 0)
{
int bucket_size = bram.dbits / portinfos.at(match.shuffle_enable - 'A').enable;
log(" Shuffle bit order to accommodate enable buckets of size %d..\n", bucket_size);
@@ -588,7 +606,7 @@ bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram,
mapped_wr_port:;
}
- // houskeeping stuff for growing more read ports and restarting read port assignments
+ // housekeeping stuff for growing more read ports and restarting read port assignments
int grow_read_ports_cursor = -1;
bool try_growing_more_read_ports = false;
@@ -656,6 +674,10 @@ grow_read_ports:;
if (clken) {
if (pi.clocks == 0) {
+ if (match.make_outreg) {
+ pi.make_outreg = true;
+ goto skip_bram_rport_clkcheck;
+ }
log(" Bram port %c%d.%d has incompatible clock type.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
goto skip_bram_rport;
}
@@ -667,12 +689,17 @@ 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 (rd_en[cell_port_i] != 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;
+ }
+ skip_bram_rport_clkcheck:
if (read_transp.count(pi.transp) && read_transp.at(pi.transp) != transp) {
if (match.make_transp && wr_ports <= 1) {
pi.make_transp = true;
enable_make_transp = true;
} else {
- log(" Bram port %c%d.%d has incompatible read transparancy.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
+ log(" Bram port %c%d.%d has incompatible read transparency.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
goto skip_bram_rport;
}
}
@@ -691,6 +718,7 @@ grow_read_ports:;
clock_polarities[pi.clkpol] = clkdom.second;
read_transp[pi.transp] = transp;
pi.sig_clock = clkdom.first;
+ pi.sig_en = rd_en[cell_port_i];
pi.effective_clkpol = clkdom.second;
}
@@ -789,6 +817,22 @@ grow_read_ports:;
for (auto &vp : variant_params)
c->setParam(vp.first, vp.second);
+ if (cell_init) {
+ int init_offset = grid_a*(1 << bram.abits);
+ int init_shift = grid_d*bram.dbits;
+ int init_size = (1 << bram.abits);
+ Const initparam(State::Sx, init_size*bram.dbits);
+ for (int i = 0; i < init_size; i++) {
+ State padding = State::Sx;
+ for (int j = 0; j < bram.dbits; j++)
+ if (init_offset+i < GetSize(initdata) && init_shift+j < GetSize(initdata[init_offset+i]))
+ initparam[i*bram.dbits+j] = initdata[init_offset+i][init_shift+j];
+ else
+ initparam[i*bram.dbits+j] = padding;
+ }
+ c->setParam("\\INIT", initparam);
+ }
+
for (auto &pi : portinfos)
{
if (pi.dupidx != dupidx)
@@ -815,8 +859,11 @@ grow_read_ports:;
if (pi.enable)
{
SigSpec sig_en = pi.sig_en;
- sig_en.extend_u0((grid_d+1) * pi.enable);
- sig_en = sig_en.extract(grid_d * pi.enable, pi.enable);
+
+ if (pi.wrmode == 1) {
+ sig_en.extend_u0((grid_d+1) * pi.enable);
+ sig_en = sig_en.extract(grid_d * pi.enable, pi.enable);
+ }
if (!addr_ok.empty())
sig_en = module->Mux(NEW_ID, SigSpec(0, GetSize(sig_en)), sig_en, addr_ok);
@@ -846,6 +893,14 @@ grow_read_ports:;
SigSpec bram_dout = module->addWire(NEW_ID, bram.dbits);
c->setPort(stringf("\\%sDATA", pf), bram_dout);
+ if (pi.make_outreg) {
+ SigSpec bram_dout_q = module->addWire(NEW_ID, bram.dbits);
+ if (!pi.sig_en.empty())
+ bram_dout = module->Mux(NEW_ID, bram_dout_q, bram_dout, pi.sig_en);
+ module->addDff(NEW_ID, pi.sig_clock, bram_dout, bram_dout_q, pi.effective_clkpol);
+ bram_dout = bram_dout_q;
+ }
+
if (pi.make_transp)
{
log(" Adding extra logic for transparent port %c%d.%d.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
@@ -871,7 +926,7 @@ grow_read_ports:;
}
SigSpec addr_ok_q = addr_ok;
- if (pi.clocks && !addr_ok.empty()) {
+ if ((pi.clocks || pi.make_outreg) && !addr_ok.empty()) {
addr_ok_q = module->addWire(NEW_ID);
module->addDff(NEW_ID, pi.sig_clock, addr_ok, addr_ok_q, pi.effective_clkpol);
}
@@ -905,6 +960,8 @@ void handle_cell(Cell *cell, const rules_t &rules)
{
log("Processing %s.%s:\n", log_id(cell->module), log_id(cell));
+ bool cell_init = !SigSpec(cell->getParam("\\INIT")).is_fully_undef();
+
dict<string, int> match_properties;
match_properties["words"] = cell->getParam("\\SIZE").as_int();
match_properties["abits"] = cell->getParam("\\ABITS").as_int();
@@ -920,7 +977,7 @@ void handle_cell(Cell *cell, const rules_t &rules)
log("\n");
pool<pair<IdString, int>> failed_brams;
- dict<pair<int, int>, std::tuple<int, int, int>> best_rule_cache;
+ dict<pair<int, int>, tuple<int, int, int>> best_rule_cache;
for (int i = 0; i < GetSize(rules.matches); i++)
{
@@ -975,6 +1032,12 @@ void handle_cell(Cell *cell, const rules_t &rules)
log(" Metrics for %s: awaste=%d dwaste=%d bwaste=%d waste=%d efficiency=%d\n",
log_id(match.name), awaste, dwaste, bwaste, waste, efficiency);
+ if (cell_init && bram.init == 0) {
+ log(" Rule #%d for bram type %s (variant %d) rejected: cannot be initialized.\n",
+ i+1, log_id(bram.name), bram.variant);
+ goto next_match_rule;
+ }
+
for (auto it : match.min_limits) {
if (it.first == "waste" || it.first == "dups" || it.first == "acells" || it.first == "dcells" || it.first == "cells")
continue;
@@ -987,6 +1050,7 @@ void handle_cell(Cell *cell, const rules_t &rules)
i+1, log_id(bram.name), bram.variant, it.first.c_str(), it.second);
goto next_match_rule;
}
+
for (auto it : match.max_limits) {
if (it.first == "acells" || it.first == "dcells" || it.first == "cells")
continue;
@@ -1014,10 +1078,7 @@ void handle_cell(Cell *cell, const rules_t &rules)
}
log(" Storing for later selection.\n");
- best_rule_cache[pair<int, int>(i, vi)] = std::tuple<int, int, int>(match_properties["efficiency"], -match_properties["cells"], -match_properties["acells"]);
-
- if (or_next_if_better)
- goto next_match_rule;
+ best_rule_cache[pair<int, int>(i, vi)] = tuple<int, int, int>(match_properties["efficiency"], -match_properties["cells"], -match_properties["acells"]);
next_match_rule:
if (or_next_if_better || best_rule_cache.empty())
@@ -1070,14 +1131,14 @@ struct MemoryBramPass : public Pass {
log("rules. A block ram description looks like this:\n");
log("\n");
log(" bram RAMB1024X32 # name of BRAM cell\n");
- // log(" init 1 # set to '1' if BRAM can be initialized\n");
+ log(" init 1 # set to '1' if BRAM can be initialized\n");
log(" abits 10 # number of address bits\n");
log(" dbits 32 # number of data bits\n");
log(" groups 2 # number of port groups\n");
log(" ports 1 1 # number of ports in each group\n");
log(" wrmode 1 0 # set to '1' if this groups is write ports\n");
- log(" enable 4 0 # number of enable bits (for write ports)\n");
- log(" transp 0 2 # transparatent (for read ports)\n");
+ log(" enable 4 1 # number of enable bits\n");
+ log(" transp 0 2 # transparent (for read ports)\n");
log(" clocks 1 2 # clock configuration\n");
log(" clkpol 2 2 # clock polarity configuration\n");
log(" endbram\n");
@@ -1095,7 +1156,7 @@ struct MemoryBramPass : public Pass {
log("greater than 1 share the same configuration bit.\n");
log("\n");
log("Using the same bram name in different bram blocks will create different variants\n");
- log("of the bram. Verilog configration parameters for the bram are created as needed.\n");
+ log("of the bram. Verilog configuration parameters for the bram are created as needed.\n");
log("\n");
log("It is also possible to create variants by repeating statements in the bram block\n");
log("and appending '@<label>' to the individual statements.\n");
@@ -1128,7 +1189,7 @@ struct MemoryBramPass : public Pass {
log(" dcells ....... number of cells in 'data-direction'\n");
log(" cells ........ total number of cells (acells*dcells*dups)\n");
log("\n");
- log("The interface for the created bram instances is dervived from the bram\n");
+ log("The interface for the created bram instances is derived from the bram\n");
log("description. Use 'techmap' to convert the created bram instances into\n");
log("instances of the actual bram cells of your target architecture.\n");
log("\n");
@@ -1139,6 +1200,9 @@ struct MemoryBramPass : public Pass {
log("A match containing the command 'make_transp' will add external circuitry\n");
log("to simulate 'transparent read', if necessary.\n");
log("\n");
+ log("A match containing the command 'make_outreg' will add external flip-flops\n");
+ log("to implement synchronous read ports, if necessary.\n");
+ log("\n");
log("A match containing the command 'shuffle_enable A' will re-organize\n");
log("the data bits to accommodate the enable pattern of port A.\n");
log("\n");
@@ -1147,7 +1211,7 @@ struct MemoryBramPass : public Pass {
{
rules_t rules;
- log_header("Executing MEMORY_BRAM pass (mapping $mem cells to block memories).\n");
+ log_header(design, "Executing MEMORY_BRAM pass (mapping $mem cells to block memories).\n");
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++) {
diff --git a/passes/memory/memory_collect.cc b/passes/memory/memory_collect.cc
index ccc196202..e068ef905 100644
--- a/passes/memory/memory_collect.cc
+++ b/passes/memory/memory_collect.cc
@@ -2,11 +2,11 @@
* 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
@@ -17,16 +17,13 @@
*
*/
-#include "kernel/register.h"
-#include "kernel/log.h"
-#include <sstream>
-#include <algorithm>
-#include <stdlib.h>
+#include "kernel/yosys.h"
+#include "kernel/sigtools.h"
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
-bool memcells_cmp(RTLIL::Cell *a, RTLIL::Cell *b)
+bool memcells_cmp(Cell *a, Cell *b)
{
if (a->type == "$memrd" && b->type == "$memrd")
return a->name < b->name;
@@ -35,108 +32,152 @@ bool memcells_cmp(RTLIL::Cell *a, RTLIL::Cell *b)
return a->parameters.at("\\PRIORITY").as_int() < b->parameters.at("\\PRIORITY").as_int();
}
-void handle_memory(RTLIL::Module *module, RTLIL::Memory *memory)
+Cell *handle_memory(Module *module, RTLIL::Memory *memory)
{
- log("Collecting $memrd and $memwr for memory `%s' in module `%s':\n",
+ log("Collecting $memrd, $memwr and $meminit for memory `%s' in module `%s':\n",
memory->name.c_str(), module->name.c_str());
int addr_bits = 0;
- while ((1 << addr_bits) < memory->size)
- addr_bits++;
+
+ Const init_data(State::Sx, memory->size * memory->width);
+ SigMap sigmap(module);
int wr_ports = 0;
- RTLIL::SigSpec sig_wr_clk;
- RTLIL::SigSpec sig_wr_clk_enable;
- RTLIL::SigSpec sig_wr_clk_polarity;
- RTLIL::SigSpec sig_wr_addr;
- RTLIL::SigSpec sig_wr_data;
- RTLIL::SigSpec sig_wr_en;
+ SigSpec sig_wr_clk;
+ SigSpec sig_wr_clk_enable;
+ SigSpec sig_wr_clk_polarity;
+ SigSpec sig_wr_addr;
+ SigSpec sig_wr_data;
+ SigSpec sig_wr_en;
int rd_ports = 0;
- RTLIL::SigSpec sig_rd_clk;
- RTLIL::SigSpec sig_rd_clk_enable;
- RTLIL::SigSpec sig_rd_clk_polarity;
- RTLIL::SigSpec sig_rd_transparent;
- RTLIL::SigSpec sig_rd_addr;
- RTLIL::SigSpec sig_rd_data;
+ SigSpec sig_rd_clk;
+ SigSpec sig_rd_clk_enable;
+ SigSpec sig_rd_clk_polarity;
+ SigSpec sig_rd_transparent;
+ SigSpec sig_rd_addr;
+ SigSpec sig_rd_data;
+ SigSpec sig_rd_en;
- std::vector<RTLIL::Cell*> del_cells;
- std::vector<RTLIL::Cell*> memcells;
+ std::vector<Cell*> memcells;
for (auto &cell_it : module->cells_) {
- RTLIL::Cell *cell = cell_it.second;
- if ((cell->type == "$memwr" || cell->type == "$memrd") && memory->name == cell->parameters["\\MEMID"].decode_string())
+ Cell *cell = cell_it.second;
+ if (cell->type.in("$memrd", "$memwr", "$meminit") && memory->name == cell->parameters["\\MEMID"].decode_string()) {
+ addr_bits = max(addr_bits, cell->getParam("\\ABITS").as_int());
memcells.push_back(cell);
+ }
+ }
+
+ if (memcells.empty()) {
+ log(" no cells found. removing memory.\n");
+ return nullptr;
}
std::sort(memcells.begin(), memcells.end(), memcells_cmp);
for (auto cell : memcells)
{
- if (cell->type == "$memwr" && memory->name == cell->parameters["\\MEMID"].decode_string())
+ log(" %s (%s)\n", log_id(cell), log_id(cell->type));
+
+ if (cell->type == "$meminit")
{
- wr_ports++;
- del_cells.push_back(cell);
-
- RTLIL::SigSpec clk = cell->getPort("\\CLK");
- RTLIL::SigSpec clk_enable = RTLIL::SigSpec(cell->parameters["\\CLK_ENABLE"]);
- RTLIL::SigSpec clk_polarity = RTLIL::SigSpec(cell->parameters["\\CLK_POLARITY"]);
- RTLIL::SigSpec addr = cell->getPort("\\ADDR");
- RTLIL::SigSpec data = cell->getPort("\\DATA");
- RTLIL::SigSpec en = cell->getPort("\\EN");
-
- clk.extend_u0(1, false);
- clk_enable.extend_u0(1, false);
- clk_polarity.extend_u0(1, false);
- addr.extend_u0(addr_bits, false);
- data.extend_u0(memory->width, false);
- en.extend_u0(memory->width, false);
-
- sig_wr_clk.append(clk);
- sig_wr_clk_enable.append(clk_enable);
- sig_wr_clk_polarity.append(clk_polarity);
- sig_wr_addr.append(addr);
- sig_wr_data.append(data);
- sig_wr_en.append(en);
+ SigSpec addr = sigmap(cell->getPort("\\ADDR"));
+ SigSpec data = sigmap(cell->getPort("\\DATA"));
+
+ if (!addr.is_fully_const())
+ log_error("Non-constant address %s in memory initialization %s.\n", log_signal(addr), log_id(cell));
+ if (!data.is_fully_const())
+ log_error("Non-constant data %s in memory initialization %s.\n", log_signal(data), log_id(cell));
+
+ int offset = (addr.as_int() - memory->start_offset) * memory->width;
+
+ if (offset < 0 || offset + GetSize(data) > GetSize(init_data))
+ log_warning("Address %s in memory initialization %s is out-of-bounds.\n", log_signal(addr), log_id(cell));
+
+ for (int i = 0; i < GetSize(data); i++)
+ if (0 <= i+offset && i+offset < GetSize(init_data))
+ init_data.bits[i+offset] = data[i].data;
+
+ continue;
}
- if (cell->type == "$memrd" && memory->name == cell->parameters["\\MEMID"].decode_string())
+ if (cell->type == "$memwr")
{
- rd_ports++;
- del_cells.push_back(cell);
-
- RTLIL::SigSpec clk = cell->getPort("\\CLK");
- RTLIL::SigSpec clk_enable = RTLIL::SigSpec(cell->parameters["\\CLK_ENABLE"]);
- RTLIL::SigSpec clk_polarity = RTLIL::SigSpec(cell->parameters["\\CLK_POLARITY"]);
- RTLIL::SigSpec transparent = RTLIL::SigSpec(cell->parameters["\\TRANSPARENT"]);
- RTLIL::SigSpec addr = cell->getPort("\\ADDR");
- RTLIL::SigSpec data = cell->getPort("\\DATA");
-
- clk.extend_u0(1, false);
- clk_enable.extend_u0(1, false);
- clk_polarity.extend_u0(1, false);
- transparent.extend_u0(1, false);
- addr.extend_u0(addr_bits, false);
- data.extend_u0(memory->width, false);
-
- sig_rd_clk.append(clk);
- sig_rd_clk_enable.append(clk_enable);
- sig_rd_clk_polarity.append(clk_polarity);
- sig_rd_transparent.append(transparent);
- sig_rd_addr.append(addr);
- sig_rd_data.append(data);
+ SigSpec clk = sigmap(cell->getPort("\\CLK"));
+ SigSpec clk_enable = SigSpec(cell->parameters["\\CLK_ENABLE"]);
+ SigSpec clk_polarity = SigSpec(cell->parameters["\\CLK_POLARITY"]);
+ SigSpec addr = sigmap(cell->getPort("\\ADDR"));
+ SigSpec data = sigmap(cell->getPort("\\DATA"));
+ SigSpec en = sigmap(cell->getPort("\\EN"));
+
+ if (!en.is_fully_zero())
+ {
+ clk.extend_u0(1, false);
+ clk_enable.extend_u0(1, false);
+ clk_polarity.extend_u0(1, false);
+ addr.extend_u0(addr_bits, false);
+ data.extend_u0(memory->width, false);
+ en.extend_u0(memory->width, false);
+
+ sig_wr_clk.append(clk);
+ sig_wr_clk_enable.append(clk_enable);
+ sig_wr_clk_polarity.append(clk_polarity);
+ sig_wr_addr.append(addr);
+ sig_wr_data.append(data);
+ sig_wr_en.append(en);
+
+ wr_ports++;
+ }
+ continue;
+ }
+
+ if (cell->type == "$memrd")
+ {
+ SigSpec clk = sigmap(cell->getPort("\\CLK"));
+ SigSpec clk_enable = SigSpec(cell->parameters["\\CLK_ENABLE"]);
+ SigSpec clk_polarity = SigSpec(cell->parameters["\\CLK_POLARITY"]);
+ SigSpec transparent = SigSpec(cell->parameters["\\TRANSPARENT"]);
+ SigSpec addr = sigmap(cell->getPort("\\ADDR"));
+ SigSpec data = sigmap(cell->getPort("\\DATA"));
+ SigSpec en = sigmap(cell->getPort("\\EN"));
+
+ if (!en.is_fully_zero())
+ {
+ clk.extend_u0(1, false);
+ clk_enable.extend_u0(1, false);
+ clk_polarity.extend_u0(1, false);
+ transparent.extend_u0(1, false);
+ addr.extend_u0(addr_bits, false);
+ data.extend_u0(memory->width, false);
+
+ sig_rd_clk.append(clk);
+ sig_rd_clk_enable.append(clk_enable);
+ sig_rd_clk_polarity.append(clk_polarity);
+ sig_rd_transparent.append(transparent);
+ sig_rd_addr.append(addr);
+ sig_rd_data.append(data);
+ sig_rd_en.append(en);
+
+ rd_ports++;
+ }
+ continue;
}
}
std::stringstream sstr;
sstr << "$mem$" << memory->name.str() << "$" << (autoidx++);
- RTLIL::Cell *mem = module->addCell(sstr.str(), "$mem");
- mem->parameters["\\MEMID"] = RTLIL::Const(memory->name.str());
- mem->parameters["\\WIDTH"] = RTLIL::Const(memory->width);
- mem->parameters["\\OFFSET"] = RTLIL::Const(memory->start_offset);
- mem->parameters["\\SIZE"] = RTLIL::Const(memory->size);
- mem->parameters["\\ABITS"] = RTLIL::Const(addr_bits);
+ Cell *mem = module->addCell(sstr.str(), "$mem");
+ mem->parameters["\\MEMID"] = Const(memory->name.str());
+ mem->parameters["\\WIDTH"] = Const(memory->width);
+ mem->parameters["\\OFFSET"] = Const(memory->start_offset);
+ mem->parameters["\\SIZE"] = Const(memory->size);
+ mem->parameters["\\ABITS"] = Const(addr_bits);
+
+ while (GetSize(init_data) > 1 && init_data.bits.back() == State::Sx && init_data.bits[GetSize(init_data)-2] == State::Sx)
+ init_data.bits.pop_back();
+ mem->parameters["\\INIT"] = init_data;
log_assert(sig_wr_clk.size() == wr_ports);
log_assert(sig_wr_clk_enable.size() == wr_ports && sig_wr_clk_enable.is_fully_const());
@@ -145,9 +186,9 @@ void handle_memory(RTLIL::Module *module, RTLIL::Memory *memory)
log_assert(sig_wr_data.size() == wr_ports * memory->width);
log_assert(sig_wr_en.size() == wr_ports * memory->width);
- mem->parameters["\\WR_PORTS"] = RTLIL::Const(wr_ports);
- mem->parameters["\\WR_CLK_ENABLE"] = wr_ports ? sig_wr_clk_enable.as_const() : RTLIL::Const(0, 0);
- mem->parameters["\\WR_CLK_POLARITY"] = wr_ports ? sig_wr_clk_polarity.as_const() : RTLIL::Const(0, 0);
+ mem->parameters["\\WR_PORTS"] = Const(wr_ports);
+ mem->parameters["\\WR_CLK_ENABLE"] = wr_ports ? sig_wr_clk_enable.as_const() : Const(0, 1);
+ mem->parameters["\\WR_CLK_POLARITY"] = wr_ports ? sig_wr_clk_polarity.as_const() : Const(0, 1);
mem->setPort("\\WR_CLK", sig_wr_clk);
mem->setPort("\\WR_ADDR", sig_wr_addr);
@@ -160,30 +201,36 @@ void handle_memory(RTLIL::Module *module, RTLIL::Memory *memory)
log_assert(sig_rd_addr.size() == rd_ports * addr_bits);
log_assert(sig_rd_data.size() == rd_ports * memory->width);
- mem->parameters["\\RD_PORTS"] = RTLIL::Const(rd_ports);
- mem->parameters["\\RD_CLK_ENABLE"] = rd_ports ? sig_rd_clk_enable.as_const() : RTLIL::Const(0, 0);
- mem->parameters["\\RD_CLK_POLARITY"] = rd_ports ? sig_rd_clk_polarity.as_const() : RTLIL::Const(0, 0);
- mem->parameters["\\RD_TRANSPARENT"] = rd_ports ? sig_rd_transparent.as_const() : RTLIL::Const(0, 0);
+ mem->parameters["\\RD_PORTS"] = Const(rd_ports);
+ mem->parameters["\\RD_CLK_ENABLE"] = rd_ports ? sig_rd_clk_enable.as_const() : Const(0, 1);
+ mem->parameters["\\RD_CLK_POLARITY"] = rd_ports ? sig_rd_clk_polarity.as_const() : Const(0, 1);
+ mem->parameters["\\RD_TRANSPARENT"] = rd_ports ? sig_rd_transparent.as_const() : Const(0, 1);
mem->setPort("\\RD_CLK", sig_rd_clk);
mem->setPort("\\RD_ADDR", sig_rd_addr);
mem->setPort("\\RD_DATA", sig_rd_data);
+ mem->setPort("\\RD_EN", sig_rd_en);
- for (auto c : del_cells)
+ for (auto c : memcells)
module->remove(c);
+
+ return mem;
}
-static void handle_module(RTLIL::Design *design, RTLIL::Module *module)
+static void handle_module(Design *design, Module *module)
{
- std::vector<RTLIL::IdString> delme;
+ std::vector<pair<Cell*, IdString>> finqueue;
+
for (auto &mem_it : module->memories)
if (design->selected(module, mem_it.second)) {
- handle_memory(module, mem_it.second);
- delme.push_back(mem_it.first);
+ Cell *c = handle_memory(module, mem_it.second);
+ finqueue.push_back(pair<Cell*, IdString>(c, mem_it.first));
}
- for (auto &it : delme) {
- delete module->memories.at(it);
- module->memories.erase(it);
+ for (auto &it : finqueue) {
+ delete module->memories.at(it.second);
+ module->memories.erase(it.second);
+ if (it.first)
+ module->rename(it.first, it.second);
}
}
@@ -200,12 +247,12 @@ struct MemoryCollectPass : public Pass {
log("\n");
}
virtual void execute(std::vector<std::string> args, RTLIL::Design *design) {
- log_header("Executing MEMORY_COLLECT pass (generating $mem cells).\n");
+ log_header(design, "Executing MEMORY_COLLECT pass (generating $mem cells).\n");
extra_args(args, 1, design);
for (auto &mod_it : design->modules_)
if (design->selected(mod_it.second))
handle_module(design, mod_it.second);
}
} MemoryCollectPass;
-
+
PRIVATE_NAMESPACE_END
diff --git a/passes/memory/memory_dff.cc b/passes/memory/memory_dff.cc
index d3cc681a2..40691d160 100644
--- a/passes/memory/memory_dff.cc
+++ b/passes/memory/memory_dff.cc
@@ -2,11 +2,11 @@
* 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
@@ -17,170 +17,251 @@
*
*/
-#include "kernel/register.h"
-#include "kernel/log.h"
-#include <stdlib.h>
-#include <sstream>
+#include "kernel/yosys.h"
+#include "kernel/sigtools.h"
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
-void normalize_sig(RTLIL::Module *module, RTLIL::SigSpec &sig)
+struct MemoryDffWorker
{
- for (auto &conn : module->connections())
- sig.replace(conn.first, conn.second);
-}
+ Module *module;
+ SigMap sigmap;
-bool find_sig_before_dff(RTLIL::Module *module, std::vector<RTLIL::Cell*> &dff_cells, RTLIL::SigSpec &sig, RTLIL::SigSpec &clk, bool &clk_polarity, bool after = false)
-{
- normalize_sig(module, sig);
+ vector<Cell*> dff_cells;
+ dict<SigBit, SigBit> invbits;
+ dict<SigBit, int> sigbit_users_count;
+ dict<SigSpec, Cell*> mux_cells_a, mux_cells_b;
+ pool<Cell*> forward_merged_dffs, candidate_dffs;
+
+ MemoryDffWorker(Module *module) : module(module), sigmap(module) { }
- for (auto &bit : sig)
+ bool find_sig_before_dff(RTLIL::SigSpec &sig, RTLIL::SigSpec &clk, bool &clk_polarity, bool after = false)
{
- if (bit.wire == NULL)
- continue;
+ sigmap.apply(sig);
- for (auto cell : dff_cells)
+ for (auto &bit : sig)
{
- if (clk != RTLIL::SigSpec(RTLIL::State::Sx)) {
- if (cell->getPort("\\CLK") != clk)
- continue;
- if (cell->parameters["\\CLK_POLARITY"].as_bool() != clk_polarity)
- continue;
- }
-
- RTLIL::SigSpec q_norm = cell->getPort(after ? "\\D" : "\\Q");
- normalize_sig(module, q_norm);
-
- RTLIL::SigSpec d = q_norm.extract(bit, &cell->getPort(after ? "\\Q" : "\\D"));
- if (d.size() != 1)
+ if (bit.wire == NULL)
continue;
- bit = d;
- clk = cell->getPort("\\CLK");
- clk_polarity = cell->parameters["\\CLK_POLARITY"].as_bool();
- goto replaced_this_bit;
- }
+ for (auto cell : dff_cells)
+ {
+ if (after && forward_merged_dffs.count(cell))
+ continue;
- return false;
- replaced_this_bit:;
- }
+ SigSpec this_clk = cell->getPort("\\CLK");
+ bool this_clk_polarity = cell->parameters["\\CLK_POLARITY"].as_bool();
- return true;
-}
+ if (invbits.count(this_clk)) {
+ this_clk = invbits.at(this_clk);
+ this_clk_polarity = !this_clk_polarity;
+ }
-void handle_wr_cell(RTLIL::Module *module, std::vector<RTLIL::Cell*> &dff_cells, RTLIL::Cell *cell)
-{
- log("Checking cell `%s' in module `%s': ", cell->name.c_str(), module->name.c_str());
+ if (clk != RTLIL::SigSpec(RTLIL::State::Sx)) {
+ if (this_clk != clk)
+ continue;
+ if (this_clk_polarity != clk_polarity)
+ continue;
+ }
- RTLIL::SigSpec clk = RTLIL::SigSpec(RTLIL::State::Sx);
- bool clk_polarity = 0;
+ RTLIL::SigSpec q_norm = cell->getPort(after ? "\\D" : "\\Q");
+ sigmap.apply(q_norm);
- RTLIL::SigSpec sig_addr = cell->getPort("\\ADDR");
- if (!find_sig_before_dff(module, dff_cells, sig_addr, clk, clk_polarity)) {
- log("no (compatible) $dff for address input found.\n");
- return;
- }
+ RTLIL::SigSpec d = q_norm.extract(bit, &cell->getPort(after ? "\\Q" : "\\D"));
+ if (d.size() != 1)
+ continue;
- RTLIL::SigSpec sig_data = cell->getPort("\\DATA");
- if (!find_sig_before_dff(module, dff_cells, sig_data, clk, clk_polarity)) {
- log("no (compatible) $dff for data input found.\n");
- return;
- }
+ bit = d;
+ clk = this_clk;
+ clk_polarity = this_clk_polarity;
+ candidate_dffs.insert(cell);
+ goto replaced_this_bit;
+ }
- RTLIL::SigSpec sig_en = cell->getPort("\\EN");
- if (!find_sig_before_dff(module, dff_cells, sig_en, clk, clk_polarity)) {
- log("no (compatible) $dff for enable input found.\n");
- return;
- }
+ return false;
+ replaced_this_bit:;
+ }
- if (clk != RTLIL::SigSpec(RTLIL::State::Sx)) {
- cell->setPort("\\CLK", clk);
- cell->setPort("\\ADDR", sig_addr);
- cell->setPort("\\DATA", sig_data);
- cell->setPort("\\EN", sig_en);
- cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(1);
- cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity);
- log("merged $dff to cell.\n");
- return;
+ return true;
}
- log("no (compatible) $dff found.\n");
-}
+ void handle_wr_cell(RTLIL::Cell *cell)
+ {
+ log("Checking cell `%s' in module `%s': ", cell->name.c_str(), module->name.c_str());
-void disconnect_dff(RTLIL::Module *module, RTLIL::SigSpec sig)
-{
- normalize_sig(module, sig);
- sig.sort_and_unify();
+ RTLIL::SigSpec clk = RTLIL::SigSpec(RTLIL::State::Sx);
+ bool clk_polarity = 0;
+ candidate_dffs.clear();
- std::stringstream sstr;
- sstr << "$memory_dff_disconnected$" << (autoidx++);
+ RTLIL::SigSpec sig_addr = cell->getPort("\\ADDR");
+ if (!find_sig_before_dff(sig_addr, clk, clk_polarity)) {
+ log("no (compatible) $dff for address input found.\n");
+ return;
+ }
- RTLIL::SigSpec new_sig = module->addWire(sstr.str(), sig.size());
+ RTLIL::SigSpec sig_data = cell->getPort("\\DATA");
+ if (!find_sig_before_dff(sig_data, clk, clk_polarity)) {
+ log("no (compatible) $dff for data input found.\n");
+ return;
+ }
- for (auto cell : module->cells())
- if (cell->type == "$dff") {
- RTLIL::SigSpec new_q = cell->getPort("\\Q");
- new_q.replace(sig, new_sig);
- cell->setPort("\\Q", new_q);
+ RTLIL::SigSpec sig_en = cell->getPort("\\EN");
+ if (!find_sig_before_dff(sig_en, clk, clk_polarity)) {
+ log("no (compatible) $dff for enable input found.\n");
+ return;
}
-}
-void handle_rd_cell(RTLIL::Module *module, std::vector<RTLIL::Cell*> &dff_cells, RTLIL::Cell *cell)
-{
- log("Checking cell `%s' in module `%s': ", cell->name.c_str(), module->name.c_str());
+ if (clk != RTLIL::SigSpec(RTLIL::State::Sx))
+ {
+ for (auto cell : candidate_dffs)
+ forward_merged_dffs.insert(cell);
+
+ cell->setPort("\\CLK", clk);
+ cell->setPort("\\ADDR", sig_addr);
+ cell->setPort("\\DATA", sig_data);
+ cell->setPort("\\EN", sig_en);
+ cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(1);
+ cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity);
+
+ log("merged $dff to cell.\n");
+ return;
+ }
- bool clk_polarity = 0;
+ log("no (compatible) $dff found.\n");
+ }
- RTLIL::SigSpec clk_data = RTLIL::SigSpec(RTLIL::State::Sx);
- RTLIL::SigSpec sig_data = cell->getPort("\\DATA");
- if (find_sig_before_dff(module, dff_cells, sig_data, clk_data, clk_polarity, true) &&
- clk_data != RTLIL::SigSpec(RTLIL::State::Sx))
+ void disconnect_dff(RTLIL::SigSpec sig)
{
- disconnect_dff(module, sig_data);
- cell->setPort("\\CLK", clk_data);
- cell->setPort("\\DATA", sig_data);
- cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(1);
- cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity);
- cell->parameters["\\TRANSPARENT"] = RTLIL::Const(0);
- log("merged data $dff to cell.\n");
- return;
+ sigmap.apply(sig);
+ sig.sort_and_unify();
+
+ std::stringstream sstr;
+ sstr << "$memory_dff_disconnected$" << (autoidx++);
+
+ RTLIL::SigSpec new_sig = module->addWire(sstr.str(), sig.size());
+
+ for (auto cell : module->cells())
+ if (cell->type == "$dff") {
+ RTLIL::SigSpec new_q = cell->getPort("\\Q");
+ new_q.replace(sig, new_sig);
+ cell->setPort("\\Q", new_q);
+ }
}
- RTLIL::SigSpec clk_addr = RTLIL::SigSpec(RTLIL::State::Sx);
- RTLIL::SigSpec sig_addr = cell->getPort("\\ADDR");
- if (find_sig_before_dff(module, dff_cells, sig_addr, clk_addr, clk_polarity) &&
- clk_addr != RTLIL::SigSpec(RTLIL::State::Sx))
+ void handle_rd_cell(RTLIL::Cell *cell)
{
- cell->setPort("\\CLK", clk_addr);
- cell->setPort("\\ADDR", sig_addr);
- cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(1);
- cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity);
- cell->parameters["\\TRANSPARENT"] = RTLIL::Const(1);
- log("merged address $dff to cell.\n");
- return;
- }
+ log("Checking cell `%s' in module `%s': ", cell->name.c_str(), module->name.c_str());
- log("no (compatible) $dff found.\n");
-}
+ bool clk_polarity = 0;
-void handle_module(RTLIL::Module *module, bool flag_wr_only)
-{
- std::vector<RTLIL::Cell*> dff_cells;
+ RTLIL::SigSpec clk_data = RTLIL::SigSpec(RTLIL::State::Sx);
+ RTLIL::SigSpec sig_data = cell->getPort("\\DATA");
+
+ for (auto bit : sigmap(sig_data))
+ if (sigbit_users_count[bit] > 1)
+ goto skip_ff_after_read_merging;
- for (auto cell : module->cells())
- if (cell->type == "$dff")
- dff_cells.push_back(cell);
+ if (mux_cells_a.count(sig_data) || mux_cells_b.count(sig_data))
+ {
+ bool enable_invert = mux_cells_a.count(sig_data) != 0;
+ Cell *mux = enable_invert ? mux_cells_a.at(sig_data) : mux_cells_b.at(sig_data);
+ SigSpec check_q = sigmap(mux->getPort(enable_invert ? "\\B" : "\\A"));
+
+ sig_data = sigmap(mux->getPort("\\Y"));
+ for (auto bit : sig_data)
+ if (sigbit_users_count[bit] > 1)
+ goto skip_ff_after_read_merging;
+
+ if (find_sig_before_dff(sig_data, clk_data, clk_polarity, true) && clk_data != RTLIL::SigSpec(RTLIL::State::Sx) && sig_data == check_q)
+ {
+ disconnect_dff(sig_data);
+ cell->setPort("\\CLK", clk_data);
+ cell->setPort("\\EN", enable_invert ? module->LogicNot(NEW_ID, mux->getPort("\\S")) : mux->getPort("\\S"));
+ cell->setPort("\\DATA", sig_data);
+ cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(1);
+ cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity);
+ cell->parameters["\\TRANSPARENT"] = RTLIL::Const(0);
+ log("merged data $dff with rd enable to cell.\n");
+ return;
+ }
+ }
+ else
+ {
+ if (find_sig_before_dff(sig_data, clk_data, clk_polarity, true) && clk_data != RTLIL::SigSpec(RTLIL::State::Sx))
+ {
+ disconnect_dff(sig_data);
+ cell->setPort("\\CLK", clk_data);
+ cell->setPort("\\EN", State::S1);
+ cell->setPort("\\DATA", sig_data);
+ cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(1);
+ cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity);
+ cell->parameters["\\TRANSPARENT"] = RTLIL::Const(0);
+ log("merged data $dff to cell.\n");
+ return;
+ }
+ }
- for (auto cell : module->selected_cells())
- if (cell->type == "$memwr" && !cell->parameters["\\CLK_ENABLE"].as_bool())
- handle_wr_cell(module, dff_cells, cell);
+ skip_ff_after_read_merging:;
+ RTLIL::SigSpec clk_addr = RTLIL::SigSpec(RTLIL::State::Sx);
+ RTLIL::SigSpec sig_addr = cell->getPort("\\ADDR");
+ if (find_sig_before_dff(sig_addr, clk_addr, clk_polarity) &&
+ clk_addr != RTLIL::SigSpec(RTLIL::State::Sx))
+ {
+ cell->setPort("\\CLK", clk_addr);
+ cell->setPort("\\EN", State::S1);
+ cell->setPort("\\ADDR", sig_addr);
+ cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(1);
+ cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity);
+ cell->parameters["\\TRANSPARENT"] = RTLIL::Const(1);
+ log("merged address $dff to cell.\n");
+ return;
+ }
+
+ log("no (compatible) $dff found.\n");
+ }
+
+ void run(bool flag_wr_only)
+ {
+ for (auto wire : module->wires()) {
+ if (wire->port_output)
+ for (auto bit : sigmap(wire))
+ sigbit_users_count[bit]++;
+ }
+
+ for (auto cell : module->cells()) {
+ if (cell->type == "$dff")
+ dff_cells.push_back(cell);
+ if (cell->type == "$mux") {
+ mux_cells_a[sigmap(cell->getPort("\\A"))] = cell;
+ mux_cells_b[sigmap(cell->getPort("\\B"))] = cell;
+ }
+ if (cell->type == "$not" || cell->type == "$_NOT_" || (cell->type == "$logic_not" && GetSize(cell->getPort("\\A")) == 1)) {
+ SigSpec sig_a = cell->getPort("\\A");
+ SigSpec sig_y = cell->getPort("\\Y");
+ if (cell->type == "$not")
+ sig_a.extend_u0(GetSize(sig_y), cell->getParam("\\A_SIGNED").as_bool());
+ if (cell->type == "$logic_not")
+ sig_y.extend_u0(1);
+ for (int i = 0; i < GetSize(sig_y); i++)
+ invbits[sig_y[i]] = sig_a[i];
+ }
+ for (auto &conn : cell->connections())
+ if (!cell->known() || cell->input(conn.first))
+ for (auto bit : sigmap(conn.second))
+ sigbit_users_count[bit]++;
+ }
- if (!flag_wr_only)
for (auto cell : module->selected_cells())
- if (cell->type == "$memrd" && !cell->parameters["\\CLK_ENABLE"].as_bool())
- handle_rd_cell(module, dff_cells, cell);
-}
+ if (cell->type == "$memwr" && !cell->parameters["\\CLK_ENABLE"].as_bool())
+ handle_wr_cell(cell);
+
+ if (!flag_wr_only)
+ for (auto cell : module->selected_cells())
+ if (cell->type == "$memrd" && !cell->parameters["\\CLK_ENABLE"].as_bool())
+ handle_rd_cell(cell);
+ }
+};
struct MemoryDffPass : public Pass {
MemoryDffPass() : Pass("memory_dff", "merge input/output DFFs into memories") { }
@@ -194,7 +275,7 @@ struct MemoryDffPass : public Pass {
log("I.e. it consumes an asynchronous memory port and the flip-flops at its\n");
log("interface and yields a synchronous memory port.\n");
log("\n");
- log(" -wr_only\n");
+ log(" -nordfff\n");
log(" do not merge registers on read ports\n");
log("\n");
}
@@ -202,11 +283,11 @@ struct MemoryDffPass : public Pass {
{
bool flag_wr_only = false;
- log_header("Executing MEMORY_DFF pass (merging $dff cells to $memrd and $memwr).\n");
+ log_header(design, "Executing MEMORY_DFF pass (merging $dff cells to $memrd and $memwr).\n");
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++) {
- if (args[argidx] == "-wr_only") {
+ if (args[argidx] == "-nordff" || args[argidx] == "-wr_only") {
flag_wr_only = true;
continue;
}
@@ -214,9 +295,11 @@ struct MemoryDffPass : public Pass {
}
extra_args(args, argidx, design);
- for (auto mod : design->selected_modules())
- handle_module(mod, flag_wr_only);
+ for (auto mod : design->selected_modules()) {
+ MemoryDffWorker worker(mod);
+ worker.run(flag_wr_only);
+ }
}
} MemoryDffPass;
-
+
PRIVATE_NAMESPACE_END
diff --git a/passes/memory/memory_map.cc b/passes/memory/memory_map.cc
index 4fb10a989..bffeec857 100644
--- a/passes/memory/memory_map.cc
+++ b/passes/memory/memory_map.cc
@@ -2,11 +2,11 @@
* 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
@@ -37,7 +37,7 @@ struct MemoryMapWorker
{
std::stringstream sstr;
sstr << "$memory" << name.str() << token1;
-
+
if (i >= 0)
sstr << "[" << i << "]";
@@ -80,13 +80,20 @@ struct MemoryMapWorker
{
std::set<int> static_ports;
std::map<int, RTLIL::SigSpec> static_cells_map;
+
+ int wr_ports = cell->parameters["\\WR_PORTS"].as_int();
+ int rd_ports = cell->parameters["\\RD_PORTS"].as_int();
+
int mem_size = cell->parameters["\\SIZE"].as_int();
int mem_width = cell->parameters["\\WIDTH"].as_int();
int mem_offset = cell->parameters["\\OFFSET"].as_int();
int mem_abits = cell->parameters["\\ABITS"].as_int();
+ SigSpec init_data = cell->getParam("\\INIT");
+ init_data.extend_u0(mem_size*mem_width, true);
+
// delete unused memory cell
- if (cell->parameters["\\RD_PORTS"].as_int() == 0 && cell->parameters["\\WR_PORTS"].as_int() == 0) {
+ if (wr_ports == 0 && rd_ports == 0) {
module->remove(cell);
return;
}
@@ -95,6 +102,8 @@ struct MemoryMapWorker
RTLIL::SigSpec clocks = cell->getPort("\\WR_CLK");
RTLIL::Const clocks_pol = cell->parameters["\\WR_CLK_POLARITY"];
RTLIL::Const clocks_en = cell->parameters["\\WR_CLK_ENABLE"];
+ clocks_pol.bits.resize(wr_ports);
+ clocks_en.bits.resize(wr_ports);
RTLIL::SigSpec refclock;
RTLIL::State refclock_pol = RTLIL::State::Sx;
for (int i = 0; i < clocks.size(); i++) {
@@ -110,7 +119,7 @@ struct MemoryMapWorker
// FIXME: Actually we should check for wr_en.is_fully_const() also and
// create a $adff cell with this ports wr_en input as reset pin when wr_en
// is not a simple static 1.
- static_cells_map[wr_addr.as_int()] = wr_data;
+ static_cells_map[wr_addr.as_int() - mem_offset] = wr_data;
static_ports.insert(i);
continue;
}
@@ -165,7 +174,10 @@ struct MemoryMapWorker
w_out_name = genid(cell->name, "", i, "$q");
RTLIL::Wire *w_out = module->addWire(w_out_name, mem_width);
- w_out->start_offset = mem_offset;
+ SigSpec w_init = init_data.extract(i*mem_width, mem_width);
+
+ if (!w_init.is_fully_undef())
+ w_out->attributes["\\init"] = w_init.as_const();
data_reg_out.push_back(RTLIL::SigSpec(w_out));
c->setPort("\\Q", data_reg_out.back());
@@ -180,39 +192,51 @@ struct MemoryMapWorker
{
RTLIL::SigSpec rd_addr = cell->getPort("\\RD_ADDR").extract(i*mem_abits, mem_abits);
+ if (mem_offset)
+ rd_addr = module->Sub(NEW_ID, rd_addr, SigSpec(mem_offset, GetSize(rd_addr)));
+
std::vector<RTLIL::SigSpec> rd_signals;
rd_signals.push_back(cell->getPort("\\RD_DATA").extract(i*mem_width, mem_width));
if (cell->parameters["\\RD_CLK_ENABLE"].bits[i] == RTLIL::State::S1)
{
+ RTLIL::Cell *dff_cell = nullptr;
+
if (cell->parameters["\\RD_TRANSPARENT"].bits[i] == RTLIL::State::S1)
{
- RTLIL::Cell *c = module->addCell(genid(cell->name, "$rdreg", i), "$dff");
- c->parameters["\\WIDTH"] = RTLIL::Const(mem_abits);
- c->parameters["\\CLK_POLARITY"] = RTLIL::Const(cell->parameters["\\RD_CLK_POLARITY"].bits[i]);
- c->setPort("\\CLK", cell->getPort("\\RD_CLK").extract(i, 1));
- c->setPort("\\D", rd_addr);
+ dff_cell = module->addCell(genid(cell->name, "$rdreg", i), "$dff");
+ dff_cell->parameters["\\WIDTH"] = RTLIL::Const(mem_abits);
+ dff_cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(cell->parameters["\\RD_CLK_POLARITY"].bits[i]);
+ dff_cell->setPort("\\CLK", cell->getPort("\\RD_CLK").extract(i, 1));
+ dff_cell->setPort("\\D", rd_addr);
count_dff++;
RTLIL::Wire *w = module->addWire(genid(cell->name, "$rdreg", i, "$q"), mem_abits);
- c->setPort("\\Q", RTLIL::SigSpec(w));
+ dff_cell->setPort("\\Q", RTLIL::SigSpec(w));
rd_addr = RTLIL::SigSpec(w);
}
else
{
- RTLIL::Cell *c = module->addCell(genid(cell->name, "$rdreg", i), "$dff");
- c->parameters["\\WIDTH"] = cell->parameters["\\WIDTH"];
- c->parameters["\\CLK_POLARITY"] = RTLIL::Const(cell->parameters["\\RD_CLK_POLARITY"].bits[i]);
- c->setPort("\\CLK", cell->getPort("\\RD_CLK").extract(i, 1));
- c->setPort("\\Q", rd_signals.back());
+ dff_cell = module->addCell(genid(cell->name, "$rdreg", i), "$dff");
+ dff_cell->parameters["\\WIDTH"] = cell->parameters["\\WIDTH"];
+ dff_cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(cell->parameters["\\RD_CLK_POLARITY"].bits[i]);
+ dff_cell->setPort("\\CLK", cell->getPort("\\RD_CLK").extract(i, 1));
+ dff_cell->setPort("\\Q", rd_signals.back());
count_dff++;
RTLIL::Wire *w = module->addWire(genid(cell->name, "$rdreg", i, "$d"), mem_width);
rd_signals.clear();
rd_signals.push_back(RTLIL::SigSpec(w));
- c->setPort("\\D", rd_signals.back());
+ dff_cell->setPort("\\D", rd_signals.back());
+ }
+
+ SigBit en_bit = cell->getPort("\\RD_EN").extract(i);
+ if (en_bit != State::S1) {
+ SigSpec new_d = module->Mux(genid(cell->name, "$rdenmux", i),
+ dff_cell->getPort("\\Q"), dff_cell->getPort("\\D"), en_bit);
+ dff_cell->setPort("\\D", new_d);
}
}
@@ -256,6 +280,10 @@ struct MemoryMapWorker
RTLIL::SigSpec wr_addr = cell->getPort("\\WR_ADDR").extract(j*mem_abits, mem_abits);
RTLIL::SigSpec wr_data = cell->getPort("\\WR_DATA").extract(j*mem_width, mem_width);
RTLIL::SigSpec wr_en = cell->getPort("\\WR_EN").extract(j*mem_width, mem_width);
+
+ if (mem_offset)
+ wr_addr = module->Sub(NEW_ID, wr_addr, SigSpec(mem_offset, GetSize(wr_addr)));
+
RTLIL::Wire *w_seladdr = addr_decode(wr_addr, RTLIL::SigSpec(i, mem_abits));
int wr_offset = 0;
@@ -335,11 +363,11 @@ struct MemoryMapPass : public Pass {
log("\n");
}
virtual void execute(std::vector<std::string> args, RTLIL::Design *design) {
- log_header("Executing MEMORY_MAP pass (converting $mem cells to logic and flip-flops).\n");
+ log_header(design, "Executing MEMORY_MAP pass (converting $mem cells to logic and flip-flops).\n");
extra_args(args, 1, design);
for (auto mod : design->selected_modules())
MemoryMapWorker(design, mod);
}
} MemoryMapPass;
-
+
PRIVATE_NAMESPACE_END
diff --git a/passes/memory/memory_share.cc b/passes/memory/memory_share.cc
index a2f89f6d9..f298169de 100644
--- a/passes/memory/memory_share.cc
+++ b/passes/memory/memory_share.cc
@@ -2,11 +2,11 @@
* 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
@@ -43,7 +43,7 @@ struct MemoryShareWorker
CellTypes cone_ct;
std::map<RTLIL::SigBit, std::pair<RTLIL::Cell*, int>> sig_to_mux;
- std::map<std::set<std::map<RTLIL::SigBit, bool>>, RTLIL::SigBit> conditions_logic_cache;
+ std::map<pair<std::set<std::map<SigBit, bool>>, SigBit>, SigBit> conditions_logic_cache;
// -----------------------------------------------------------------
@@ -79,7 +79,7 @@ struct MemoryShareWorker
}
return false;
}
-
+
for (int i = 0; i < int(sig_s.size()); i++)
{
@@ -109,10 +109,12 @@ struct MemoryShareWorker
return false;
}
- RTLIL::SigBit conditions_to_logic(std::set<std::map<RTLIL::SigBit, bool>> &conditions, int &created_conditions)
+ RTLIL::SigBit conditions_to_logic(std::set<std::map<RTLIL::SigBit, bool>> &conditions, SigBit olden, int &created_conditions)
{
- if (conditions_logic_cache.count(conditions))
- return conditions_logic_cache.at(conditions);
+ auto key = make_pair(conditions, olden);
+
+ if (conditions_logic_cache.count(key))
+ return conditions_logic_cache.at(key);
RTLIL::SigSpec terms;
for (auto &cond : conditions) {
@@ -125,10 +127,16 @@ struct MemoryShareWorker
created_conditions++;
}
- if (terms.size() > 1)
+ if (olden.wire != nullptr || olden != State::S1)
+ terms.append(olden);
+
+ if (GetSize(terms) == 0)
+ terms = State::S1;
+
+ if (GetSize(terms) > 1)
terms = module->ReduceAnd(NEW_ID, terms);
- return conditions_logic_cache[conditions] = terms;
+ return conditions_logic_cache[key] = terms;
}
void translate_rd_feedback_to_en(std::string memid, std::vector<RTLIL::Cell*> &rd_ports, std::vector<RTLIL::Cell*> &wr_ports)
@@ -137,15 +145,14 @@ struct MemoryShareWorker
std::map<RTLIL::SigBit, std::set<RTLIL::SigBit>> muxtree_upstream_map;
std::set<RTLIL::SigBit> non_feedback_nets;
- for (auto wire_it : module->wires_)
- if (wire_it.second->port_output) {
- std::vector<RTLIL::SigBit> bits = RTLIL::SigSpec(wire_it.second);
+ for (auto wire : module->wires())
+ if (wire->port_output) {
+ std::vector<RTLIL::SigBit> bits = sigmap(wire);
non_feedback_nets.insert(bits.begin(), bits.end());
}
- for (auto cell_it : module->cells_)
+ for (auto cell : module->cells())
{
- RTLIL::Cell *cell = cell_it.second;
bool ignore_data_port = false;
if (cell->type == "$mux" || cell->type == "$pmux")
@@ -170,7 +177,7 @@ struct MemoryShareWorker
cell->parameters.at("\\MEMID").decode_string() == memid)
ignore_data_port = true;
- for (auto conn : cell_it.second->connections())
+ for (auto conn : cell->connections())
{
if (ignore_data_port && conn.first == "\\DATA")
continue;
@@ -207,7 +214,7 @@ struct MemoryShareWorker
if (non_feedback_nets.count(sig_data[i]))
goto not_pure_feedback_port;
- async_rd_bits[sig_addr].resize(std::max(async_rd_bits.size(), sig_data.size()));
+ async_rd_bits[sig_addr].resize(max(async_rd_bits.size(), sig_data.size()));
for (int i = 0; i < int(sig_data.size()); i++)
async_rd_bits[sig_addr][i].insert(sig_data[i]);
@@ -237,13 +244,8 @@ struct MemoryShareWorker
std::map<RTLIL::SigBit, bool> state;
std::set<std::map<RTLIL::SigBit, bool>> conditions;
- if (cell_en[i].wire != NULL) {
- state[cell_en[i]] = false;
- conditions.insert(state);
- }
-
find_data_feedback(async_rd_bits.at(sig_addr).at(i), cell_data[i], state, conditions);
- cell_en[i] = conditions_to_logic(conditions, created_conditions);
+ cell_en[i] = conditions_to_logic(conditions, cell_en[i], created_conditions);
}
if (created_conditions) {
@@ -489,8 +491,8 @@ struct MemoryShareWorker
if (wr_ports.size() <= 1)
return;
- ezDefaultSAT ez;
- SatGen satgen(&ez, &modwalker.sigmap);
+ ezSatPtr ez;
+ SatGen satgen(ez.get(), &modwalker.sigmap);
// find list of considered ports and port pairs
@@ -553,7 +555,7 @@ struct MemoryShareWorker
if (considered_port_pairs.count(i) || considered_port_pairs.count(i+1))
{
RTLIL::SigSpec sig = modwalker.sigmap(wr_ports[i]->getPort("\\EN"));
- port_to_sat_variable[i] = ez.expression(ez.OpOr, satgen.importSigSpec(sig));
+ port_to_sat_variable[i] = ez->expression(ez->OpOr, satgen.importSigSpec(sig));
std::vector<RTLIL::SigBit> bits = sig;
bits_queue.insert(bits.begin(), bits.end());
@@ -582,7 +584,7 @@ struct MemoryShareWorker
vector<int> ez_wire_bits = satgen.importSigSpec(wire);
for (int i : ez_wire_bits)
for (int j : ez_wire_bits)
- if (i != j) ez.assume(ez.NOT(i), j);
+ if (i != j) ez->assume(ez->NOT(i), j);
}
log(" Common input cone for all EN signals: %d cells.\n", int(sat_cells.size()));
@@ -590,7 +592,7 @@ struct MemoryShareWorker
for (auto cell : sat_cells)
satgen.importCell(cell);
- log(" Size of unconstrained SAT problem: %d variables, %d clauses\n", ez.numCnfVariables(), ez.numCnfClauses());
+ log(" Size of unconstrained SAT problem: %d variables, %d clauses\n", ez->numCnfVariables(), ez->numCnfClauses());
// merge subsequent ports if possible
@@ -599,13 +601,13 @@ struct MemoryShareWorker
if (!considered_port_pairs.count(i))
continue;
- if (ez.solve(port_to_sat_variable.at(i-1), port_to_sat_variable.at(i))) {
+ if (ez->solve(port_to_sat_variable.at(i-1), port_to_sat_variable.at(i))) {
log(" According to SAT solver sharing of port %d with port %d is not possible.\n", i-1, i);
continue;
}
log(" Merging port %d into port %d.\n", i-1, i);
- port_to_sat_variable.at(i) = ez.OR(port_to_sat_variable.at(i-1), port_to_sat_variable.at(i));
+ port_to_sat_variable.at(i) = ez->OR(port_to_sat_variable.at(i-1), port_to_sat_variable.at(i));
RTLIL::SigSpec last_addr = wr_ports[i-1]->getPort("\\ADDR");
RTLIL::SigSpec last_data = wr_ports[i-1]->getPort("\\DATA");
@@ -663,10 +665,8 @@ struct MemoryShareWorker
std::map<std::string, std::pair<std::vector<RTLIL::Cell*>, std::vector<RTLIL::Cell*>>> memindex;
sigmap_xmux = sigmap;
- for (auto &it : module->cells_)
+ for (auto cell : module->cells())
{
- RTLIL::Cell *cell = it.second;
-
if (cell->type == "$memrd")
memindex[cell->parameters.at("\\MEMID").decode_string()].first.push_back(cell);
@@ -743,11 +743,11 @@ struct MemorySharePass : public Pass {
log("\n");
log("Note that in addition to the algorithms implemented in this pass, the $memrd\n");
log("and $memwr cells are also subject to generic resource sharing passes (and other\n");
- log("optimizations) such as opt_share.\n");
+ log("optimizations) such as \"share\" and \"opt_merge\".\n");
log("\n");
}
virtual void execute(std::vector<std::string> args, RTLIL::Design *design) {
- log_header("Executing MEMORY_SHARE pass (consolidating $memrc/$memwr cells).\n");
+ log_header(design, "Executing MEMORY_SHARE pass (consolidating $memrc/$memwr cells).\n");
extra_args(args, 1, design);
for (auto module : design->selected_modules())
MemoryShareWorker(design, module);
diff --git a/passes/memory/memory_unpack.cc b/passes/memory/memory_unpack.cc
index e650facb4..a0fc31b5e 100644
--- a/passes/memory/memory_unpack.cc
+++ b/passes/memory/memory_unpack.cc
@@ -2,11 +2,11 @@
* 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
@@ -57,6 +57,7 @@ void handle_memory(RTLIL::Module *module, RTLIL::Cell *memory)
cell->parameters["\\CLK_POLARITY"] = RTLIL::SigSpec(memory->parameters.at("\\RD_CLK_POLARITY")).extract(i, 1).as_const();
cell->parameters["\\TRANSPARENT"] = RTLIL::SigSpec(memory->parameters.at("\\RD_TRANSPARENT")).extract(i, 1).as_const();
cell->setPort("\\CLK", memory->getPort("\\RD_CLK").extract(i, 1));
+ cell->setPort("\\EN", memory->getPort("\\RD_EN").extract(i, 1));
cell->setPort("\\ADDR", memory->getPort("\\RD_ADDR").extract(i*abits, abits));
cell->setPort("\\DATA", memory->getPort("\\RD_DATA").extract(i*mem->width, mem->width));
}
@@ -76,6 +77,41 @@ void handle_memory(RTLIL::Module *module, RTLIL::Cell *memory)
cell->setPort("\\DATA", memory->getPort("\\WR_DATA").extract(i*mem->width, mem->width));
}
+ Const initval = memory->parameters.at("\\INIT");
+ RTLIL::Cell *last_init_cell = nullptr;
+ SigSpec last_init_data;
+ int last_init_addr=0;
+
+ for (int i = 0; i < GetSize(initval) && i/mem->width < (1 << abits); i += mem->width) {
+ Const val = initval.extract(i, mem->width, State::Sx);
+ for (auto bit : val.bits)
+ if (bit != State::Sx)
+ goto found_non_undef_initval;
+ continue;
+ found_non_undef_initval:
+ if (last_init_cell && last_init_addr+1 == i/mem->width) {
+ last_init_cell->parameters["\\WORDS"] = last_init_cell->parameters["\\WORDS"].as_int() + 1;
+ last_init_data.append(val);
+ last_init_addr++;
+ } else {
+ if (last_init_cell)
+ last_init_cell->setPort("\\DATA", last_init_data);
+ RTLIL::Cell *cell = module->addCell(NEW_ID, "$meminit");
+ cell->parameters["\\MEMID"] = mem_name.str();
+ cell->parameters["\\ABITS"] = memory->parameters.at("\\ABITS");
+ cell->parameters["\\WIDTH"] = memory->parameters.at("\\WIDTH");
+ cell->parameters["\\WORDS"] = 1;
+ cell->parameters["\\PRIORITY"] = i/mem->width;
+ cell->setPort("\\ADDR", SigSpec(i/mem->width, abits));
+ last_init_cell = cell;
+ last_init_addr = i/mem->width;
+ last_init_data = val;
+ }
+ }
+
+ if (last_init_cell)
+ last_init_cell->setPort("\\DATA", last_init_data);
+
module->remove(memory);
}
@@ -102,12 +138,12 @@ struct MemoryUnpackPass : public Pass {
log("\n");
}
virtual void execute(std::vector<std::string> args, RTLIL::Design *design) {
- log_header("Executing MEMORY_UNPACK pass (generating $memrd/$memwr cells form $mem cells).\n");
+ log_header(design, "Executing MEMORY_UNPACK pass (generating $memrd/$memwr cells form $mem cells).\n");
extra_args(args, 1, design);
for (auto &mod_it : design->modules_)
if (design->selected(mod_it.second))
handle_module(design, mod_it.second);
}
} MemoryUnpackPass;
-
+
PRIVATE_NAMESPACE_END
diff --git a/passes/opt/Makefile.inc b/passes/opt/Makefile.inc
index 6b075cd9a..a8b1537bb 100644
--- a/passes/opt/Makefile.inc
+++ b/passes/opt/Makefile.inc
@@ -1,11 +1,11 @@
OBJS += passes/opt/opt.o
-OBJS += passes/opt/opt_share.o
+OBJS += passes/opt/opt_merge.o
OBJS += passes/opt/opt_muxtree.o
OBJS += passes/opt/opt_reduce.o
OBJS += passes/opt/opt_rmdff.o
-OBJS += passes/opt/opt_clean.o
-OBJS += passes/opt/opt_const.o
+OBJS += passes/opt/opt_clean.o
+OBJS += passes/opt/opt_expr.o
ifneq ($(SMALL),1)
OBJS += passes/opt/share.o
diff --git a/passes/opt/opt.cc b/passes/opt/opt.cc
index 25419375e..13ea5469b 100644
--- a/passes/opt/opt.cc
+++ b/passes/opt/opt.cc
@@ -2,11 +2,11 @@
* 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
@@ -37,23 +37,23 @@ struct OptPass : public Pass {
log("a series of trivial optimizations and cleanups. This pass executes the other\n");
log("passes in the following order:\n");
log("\n");
- log(" opt_const [-mux_undef] [-mux_bool] [-undriven] [-fine] [-full] [-keepdc]\n");
- log(" opt_share -nomux\n");
+ log(" opt_expr [-mux_undef] [-mux_bool] [-undriven] [-clkinv] [-fine] [-full] [-keepdc]\n");
+ log(" opt_merge [-share_all] -nomux\n");
log("\n");
log(" do\n");
log(" opt_muxtree\n");
log(" opt_reduce [-fine] [-full]\n");
- log(" opt_share\n");
+ log(" opt_merge [-share_all]\n");
log(" opt_rmdff\n");
log(" opt_clean [-purge]\n");
- log(" opt_const [-mux_undef] [-mux_bool] [-undriven] [-fine] [-full] [-keepdc]\n");
+ log(" opt_expr [-mux_undef] [-mux_bool] [-undriven] [-clkinv] [-fine] [-full] [-keepdc]\n");
log(" while <changed design>\n");
log("\n");
log("When called with -fast the following script is used instead:\n");
log("\n");
log(" do\n");
- log(" opt_const [-mux_undef] [-mux_bool] [-undriven] [-fine] [-full] [-keepdc]\n");
- log(" opt_share\n");
+ log(" opt_expr [-mux_undef] [-mux_bool] [-undriven] [-clkinv] [-fine] [-full] [-keepdc]\n");
+ log(" opt_merge [-share_all]\n");
log(" opt_rmdff\n");
log(" opt_clean [-purge]\n");
log(" while <changed design in opt_rmdff>\n");
@@ -66,11 +66,12 @@ struct OptPass : public Pass {
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
{
std::string opt_clean_args;
- std::string opt_const_args;
+ std::string opt_expr_args;
std::string opt_reduce_args;
+ std::string opt_merge_args;
bool fast_mode = false;
- log_header("Executing OPT pass (performing simple optimizations).\n");
+ log_header(design, "Executing OPT pass (performing simple optimizations).\n");
log_push();
size_t argidx;
@@ -80,29 +81,37 @@ struct OptPass : public Pass {
continue;
}
if (args[argidx] == "-mux_undef") {
- opt_const_args += " -mux_undef";
+ opt_expr_args += " -mux_undef";
continue;
}
if (args[argidx] == "-mux_bool") {
- opt_const_args += " -mux_bool";
+ opt_expr_args += " -mux_bool";
continue;
}
if (args[argidx] == "-undriven") {
- opt_const_args += " -undriven";
+ opt_expr_args += " -undriven";
+ continue;
+ }
+ if (args[argidx] == "-clkinv") {
+ opt_expr_args += " -clkinv";
continue;
}
if (args[argidx] == "-fine") {
- opt_const_args += " -fine";
+ opt_expr_args += " -fine";
opt_reduce_args += " -fine";
continue;
}
if (args[argidx] == "-full") {
- opt_const_args += " -full";
+ opt_expr_args += " -full";
opt_reduce_args += " -full";
continue;
}
if (args[argidx] == "-keepdc") {
- opt_const_args += " -keepdc";
+ opt_expr_args += " -keepdc";
+ continue;
+ }
+ if (args[argidx] == "-share_all") {
+ opt_merge_args += " -share_all";
continue;
}
if (args[argidx] == "-fast") {
@@ -116,38 +125,42 @@ struct OptPass : public Pass {
if (fast_mode)
{
while (1) {
- Pass::call(design, "opt_const" + opt_const_args);
- Pass::call(design, "opt_share");
+ Pass::call(design, "opt_expr" + opt_expr_args);
+ Pass::call(design, "opt_merge" + opt_merge_args);
design->scratchpad_unset("opt.did_something");
Pass::call(design, "opt_rmdff");
if (design->scratchpad_get_bool("opt.did_something") == false)
break;
Pass::call(design, "opt_clean" + opt_clean_args);
- log_header("Rerunning OPT passes. (Removed registers in this run.)\n");
+ log_header(design, "Rerunning OPT passes. (Removed registers in this run.)\n");
}
Pass::call(design, "opt_clean" + opt_clean_args);
}
else
{
- Pass::call(design, "opt_const" + opt_const_args);
- Pass::call(design, "opt_share -nomux");
+ Pass::call(design, "opt_expr" + opt_expr_args);
+ Pass::call(design, "opt_merge -nomux" + opt_merge_args);
while (1) {
design->scratchpad_unset("opt.did_something");
Pass::call(design, "opt_muxtree");
Pass::call(design, "opt_reduce" + opt_reduce_args);
- Pass::call(design, "opt_share");
+ Pass::call(design, "opt_merge" + opt_merge_args);
Pass::call(design, "opt_rmdff");
Pass::call(design, "opt_clean" + opt_clean_args);
- Pass::call(design, "opt_const" + opt_const_args);
+ Pass::call(design, "opt_expr" + opt_expr_args);
if (design->scratchpad_get_bool("opt.did_something") == false)
break;
- log_header("Rerunning OPT passes. (Maybe there is more to do..)\n");
+ log_header(design, "Rerunning OPT passes. (Maybe there is more to do..)\n");
}
}
- log_header(fast_mode ? "Finished fast OPT passes.\n" : "Finished OPT passes. (There is nothing left to do.)\n");
+ design->optimize();
+ design->sort();
+ design->check();
+
+ log_header(design, fast_mode ? "Finished fast OPT passes.\n" : "Finished OPT passes. (There is nothing left to do.)\n");
log_pop();
}
} OptPass;
-
+
PRIVATE_NAMESPACE_END
diff --git a/passes/opt/opt_clean.cc b/passes/opt/opt_clean.cc
index 6a7e6051d..6600ffa25 100644
--- a/passes/opt/opt_clean.cc
+++ b/passes/opt/opt_clean.cc
@@ -2,11 +2,11 @@
* 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
@@ -30,7 +30,55 @@ PRIVATE_NAMESPACE_BEGIN
using RTLIL::id2cstr;
-CellTypes ct, ct_reg, ct_all;
+struct keep_cache_t
+{
+ Design *design;
+ dict<Module*, bool> cache;
+
+ void reset(Design *design = nullptr)
+ {
+ this->design = design;
+ cache.clear();
+ }
+
+ bool query(Module *module)
+ {
+ log_assert(design != nullptr);
+
+ if (module == nullptr)
+ return false;
+
+ if (cache.count(module))
+ return cache.at(module);
+
+ cache[module] = true;
+ if (!module->get_bool_attribute("\\keep")) {
+ bool found_keep = false;
+ for (auto cell : module->cells())
+ if (query(cell)) found_keep = true;
+ cache[module] = found_keep;
+ }
+
+ return cache[module];
+ }
+
+ bool query(Cell *cell)
+ {
+ if (cell->type.in("$memwr", "$meminit", "$assert", "$assume"))
+ return true;
+
+ if (cell->has_keep_attr())
+ return true;
+
+ if (cell->module && cell->module->design)
+ return query(cell->module->design->module(cell->type));
+
+ return false;
+ }
+};
+
+keep_cache_t keep_cache;
+CellTypes ct_reg, ct_all;
int count_rm_cells, count_rm_wires;
void rmunused_module_cells(Module *module, bool verbose)
@@ -42,12 +90,12 @@ void rmunused_module_cells(Module *module, bool verbose)
for (auto &it : module->cells_) {
Cell *cell = it.second;
for (auto &it2 : cell->connections()) {
- if (!ct.cell_input(cell->type, it2.first))
+ if (!ct_all.cell_known(cell->type) || ct_all.cell_output(cell->type, it2.first))
for (auto bit : sigmap(it2.second))
if (bit.wire != nullptr)
wire2driver[bit].insert(cell);
}
- if (cell->type == "$memwr" || cell->type == "$assert" || cell->has_keep_attr())
+ if (keep_cache.query(cell))
queue.insert(cell);
else
unused.insert(cell);
@@ -67,7 +115,7 @@ void rmunused_module_cells(Module *module, bool verbose)
pool<SigBit> bits;
for (auto cell : queue)
for (auto &it : cell->connections())
- if (!ct.cell_output(cell->type, it.first))
+ if (!ct_all.cell_known(cell->type) || ct_all.cell_input(cell->type, it.first))
for (auto bit : sigmap(it.second))
bits.insert(bit);
@@ -108,6 +156,9 @@ bool compare_signals(RTLIL::SigBit &s1, RTLIL::SigBit &s2, SigPool &regs, SigPoo
if (w1->port_input != w2->port_input)
return w2->port_input;
+ if ((w1->port_input && w1->port_output) != (w2->port_input && w2->port_output))
+ return !(w2->port_input && w2->port_output);
+
if (w1->name[0] == '\\' && w2->name[0] == '\\') {
if (regs.check_any(s1) != regs.check_any(s2))
return regs.check_any(s2);
@@ -129,7 +180,7 @@ bool compare_signals(RTLIL::SigBit &s1, RTLIL::SigBit &s2, SigPool &regs, SigPoo
if (attrs1 != attrs2)
return attrs2 > attrs1;
- return w2->name < w1->name;
+ return strcmp(w2->name.c_str(), w1->name.c_str()) < 0;
}
bool check_public_name(RTLIL::IdString id)
@@ -159,7 +210,7 @@ void rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos
for (auto &it2 : cell->connections())
connected_signals.add(it2.second);
}
-
+
SigMap assign_map(module);
pool<RTLIL::SigSpec> direct_sigs;
pool<RTLIL::Wire*> direct_wires;
@@ -193,7 +244,7 @@ void rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos
for (auto &it2 : cell->connections_) {
assign_map.apply(it2.second);
used_signals.add(it2.second);
- if (!ct.cell_output(cell->type, it2.first))
+ if (!ct_all.cell_output(cell->type, it2.first))
used_signals_nodrivers.add(it2.second);
}
}
@@ -216,7 +267,7 @@ void rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos
std::vector<RTLIL::Wire*> maybe_del_wires;
for (auto wire : module->wires())
{
- if ((!purge_mode && check_public_name(wire->name)) || wire->port_id != 0 || wire->get_bool_attribute("\\keep")) {
+ if ((!purge_mode && check_public_name(wire->name)) || wire->port_id != 0 || wire->get_bool_attribute("\\keep") || wire->attributes.count("\\init")) {
RTLIL::SigSpec s1 = RTLIL::SigSpec(wire), s2 = s1;
assign_map.apply(s2);
if (!used_signals.check_any(s2) && wire->port_id == 0 && !wire->get_bool_attribute("\\keep")) {
@@ -296,8 +347,14 @@ void rmunused_module(RTLIL::Module *module, bool purge_mode, bool verbose)
module->connect(y, a);
delcells.push_back(cell);
}
- for (auto cell : delcells)
+ for (auto cell : delcells) {
+ if (verbose)
+ log(" removing buffer cell `%s': %s = %s\n", cell->name.c_str(),
+ log_signal(cell->getPort("\\Y")), log_signal(cell->getPort("\\A")));
module->remove(cell);
+ }
+ if (!delcells.empty())
+ module->design->scratchpad_set_bool("opt.did_something", true);
rmunused_module_cells(module, verbose);
rmunused_module_signals(module, purge_mode, verbose);
@@ -326,7 +383,7 @@ struct OptCleanPass : public Pass {
{
bool purge_mode = false;
- log_header("Executing OPT_CLEAN pass (remove unused cells and wires).\n");
+ log_header(design, "Executing OPT_CLEAN pass (remove unused cells and wires).\n");
log_push();
size_t argidx;
@@ -339,26 +396,30 @@ struct OptCleanPass : public Pass {
}
extra_args(args, argidx, design);
- ct.setup_internals();
- ct.setup_internals_mem();
- ct.setup_stdcells();
- ct.setup_stdcells_mem();
+ keep_cache.reset(design);
ct_reg.setup_internals_mem();
ct_reg.setup_stdcells_mem();
+ ct_all.setup(design);
+
for (auto module : design->selected_whole_modules_warn()) {
if (module->has_processes_warn())
continue;
rmunused_module(module, purge_mode, true);
}
- ct.clear();
+ design->optimize();
+ design->sort();
+ design->check();
+
+ keep_cache.reset();
ct_reg.clear();
+ ct_all.clear();
log_pop();
}
} OptCleanPass;
-
+
struct CleanPass : public Pass {
CleanPass() : Pass("clean", "remove unused cells and wires") { }
virtual void help()
@@ -391,10 +452,7 @@ struct CleanPass : public Pass {
if (argidx < args.size())
extra_args(args, argidx, design);
- ct.setup_internals();
- ct.setup_internals_mem();
- ct.setup_stdcells();
- ct.setup_stdcells_mem();
+ keep_cache.reset(design);
ct_reg.setup_internals_mem();
ct_reg.setup_stdcells_mem();
@@ -404,13 +462,10 @@ struct CleanPass : public Pass {
count_rm_cells = 0;
count_rm_wires = 0;
- for (auto mod : design->selected_whole_modules()) {
- if (mod->has_processes())
+ for (auto module : design->selected_whole_modules()) {
+ if (module->has_processes())
continue;
- do {
- design->scratchpad_unset("opt.did_something");
- rmunused_module(mod, purge_mode, false);
- } while (design->scratchpad_get_bool("opt.did_something"));
+ rmunused_module(module, purge_mode, false);
}
if (count_rm_cells > 0 || count_rm_wires > 0)
@@ -420,10 +475,10 @@ struct CleanPass : public Pass {
design->sort();
design->check();
- ct.clear();
+ keep_cache.reset();
ct_reg.clear();
ct_all.clear();
}
} CleanPass;
-
+
PRIVATE_NAMESPACE_END
diff --git a/passes/opt/opt_const.cc b/passes/opt/opt_expr.cc
index 1758a34fa..b62eae285 100644
--- a/passes/opt/opt_const.cc
+++ b/passes/opt/opt_expr.cc
@@ -2,11 +2,11 @@
* 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
@@ -179,14 +179,87 @@ bool group_cell_inputs(RTLIL::Module *module, RTLIL::Cell *cell, bool commutativ
log("\n");
}
- cover_list("opt.opt_const.fine.group", "$not", "$pos", "$and", "$or", "$xor", "$xnor", cell->type.str());
+ cover_list("opt.opt_expr.fine.group", "$not", "$pos", "$and", "$or", "$xor", "$xnor", cell->type.str());
module->remove(cell);
did_something = true;
return true;
}
-void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool consume_x, bool mux_undef, bool mux_bool, bool do_fine, bool keepdc)
+void handle_polarity_inv(Cell *cell, IdString port, IdString param, const SigMap &assign_map, const dict<RTLIL::SigSpec, RTLIL::SigSpec> &invert_map)
+{
+ SigSpec sig = assign_map(cell->getPort(port));
+ if (invert_map.count(sig)) {
+ log("Inverting %s of %s cell `%s' in module `%s': %s -> %s\n",
+ log_id(port), log_id(cell->type), log_id(cell), log_id(cell->module),
+ log_signal(sig), log_signal(invert_map.at(sig)));
+ cell->setPort(port, (invert_map.at(sig)));
+ cell->setParam(param, !cell->getParam(param).as_bool());
+ }
+}
+
+void handle_clkpol_celltype_swap(Cell *cell, string type1, string type2, IdString port, const SigMap &assign_map, const dict<RTLIL::SigSpec, RTLIL::SigSpec> &invert_map)
+{
+ log_assert(GetSize(type1) == GetSize(type2));
+ string cell_type = cell->type.str();
+
+ if (GetSize(type1) != GetSize(cell_type))
+ return;
+
+ for (int i = 0; i < GetSize(type1); i++) {
+ log_assert((type1[i] == '?') == (type2[i] == '?'));
+ if (type1[i] == '?') {
+ if (cell_type[i] != '0' && cell_type[i] != '1' && cell_type[i] != 'N' && cell_type[i] != 'P')
+ return;
+ type1[i] = cell_type[i];
+ type2[i] = cell_type[i];
+ }
+ }
+
+ if (cell->type.in(type1, type2)) {
+ SigSpec sig = assign_map(cell->getPort(port));
+ if (invert_map.count(sig)) {
+ log("Inverting %s of %s cell `%s' in module `%s': %s -> %s\n",
+ log_id(port), log_id(cell->type), log_id(cell), log_id(cell->module),
+ log_signal(sig), log_signal(invert_map.at(sig)));
+ cell->setPort(port, (invert_map.at(sig)));
+ cell->type = cell->type == type1 ? type2 : type1;
+ }
+ }
+}
+
+bool is_one_or_minus_one(const Const &value, bool is_signed, bool &is_negative)
+{
+ bool all_bits_one = true;
+ bool last_bit_one = true;
+
+ if (GetSize(value.bits) < 1)
+ return false;
+
+ if (GetSize(value.bits) == 1) {
+ if (value.bits[0] != State::S1)
+ return false;
+ if (is_signed)
+ is_negative = true;
+ return true;
+ }
+
+ for (int i = 0; i < GetSize(value.bits); i++) {
+ if (value.bits[i] != State::S1)
+ all_bits_one = false;
+ if (value.bits[i] != (i ? State::S0 : State::S1))
+ last_bit_one = false;
+ }
+
+ if (all_bits_one && is_signed) {
+ is_negative = true;
+ return true;
+ }
+
+ return last_bit_one;
+}
+
+void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool consume_x, bool mux_undef, bool mux_bool, bool do_fine, bool keepdc, bool clkinv)
{
if (!design->selected(module))
return;
@@ -207,6 +280,8 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
if ((cell->type == "$_NOT_" || cell->type == "$not" || cell->type == "$logic_not") &&
cell->getPort("\\A").size() == 1 && cell->getPort("\\Y").size() == 1)
invert_map[assign_map(cell->getPort("\\Y"))] = assign_map(cell->getPort("\\A"));
+ if ((cell->type == "$mux" || cell->type == "$_MUX_") && cell->getPort("\\A") == SigSpec(State::S1) && cell->getPort("\\B") == SigSpec(State::S0))
+ invert_map[assign_map(cell->getPort("\\Y"))] = assign_map(cell->getPort("\\S"));
if (ct_combinational.cell_known(cell->type))
for (auto &conn : cell->connections()) {
RTLIL::SigSpec sig = assign_map(conn.second);
@@ -229,9 +304,106 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
for (auto cell : cells.sorted)
{
-#define ACTION_DO(_p_, _s_) do { cover("opt.opt_const.action_" S__LINE__); replace_cell(assign_map, module, cell, input.as_string(), _p_, _s_); goto next_cell; } while (0)
+#define ACTION_DO(_p_, _s_) do { cover("opt.opt_expr.action_" S__LINE__); replace_cell(assign_map, module, cell, input.as_string(), _p_, _s_); goto next_cell; } while (0)
#define ACTION_DO_Y(_v_) ACTION_DO("\\Y", RTLIL::SigSpec(RTLIL::State::S ## _v_))
+ if (clkinv)
+ {
+ if (cell->type.in("$dff", "$dffe", "$dffsr", "$adff", "$fsm", "$memrd", "$memwr"))
+ handle_polarity_inv(cell, "\\CLK", "\\CLK_POLARITY", assign_map, invert_map);
+
+ if (cell->type.in("$sr", "$dffsr", "$dlatchsr")) {
+ handle_polarity_inv(cell, "\\SET", "\\SET_POLARITY", assign_map, invert_map);
+ handle_polarity_inv(cell, "\\CLR", "\\CLR_POLARITY", assign_map, invert_map);
+ }
+
+ if (cell->type.in("$dffe", "$dlatch", "$dlatchsr"))
+ handle_polarity_inv(cell, "\\EN", "\\EN_POLARITY", assign_map, invert_map);
+
+ handle_clkpol_celltype_swap(cell, "$_SR_N?_", "$_SR_P?_", "\\S", assign_map, invert_map);
+ handle_clkpol_celltype_swap(cell, "$_SR_?N_", "$_SR_?P_", "\\R", assign_map, invert_map);
+
+ handle_clkpol_celltype_swap(cell, "$_DFF_N_", "$_DFF_P_", "\\C", assign_map, invert_map);
+
+ handle_clkpol_celltype_swap(cell, "$_DFFE_N?_", "$_DFFE_P?_", "\\C", assign_map, invert_map);
+ handle_clkpol_celltype_swap(cell, "$_DFFE_?N_", "$_DFFE_?P_", "\\E", assign_map, invert_map);
+
+ handle_clkpol_celltype_swap(cell, "$_DFF_N??_", "$_DFF_P??_", "\\C", assign_map, invert_map);
+ handle_clkpol_celltype_swap(cell, "$_DFF_?N?_", "$_DFF_?P?_", "\\R", assign_map, invert_map);
+
+ handle_clkpol_celltype_swap(cell, "$_DFFSR_N??_", "$_DFFSR_P??_", "\\C", assign_map, invert_map);
+ handle_clkpol_celltype_swap(cell, "$_DFFSR_?N?_", "$_DFFSR_?P?_", "\\S", assign_map, invert_map);
+ handle_clkpol_celltype_swap(cell, "$_DFFSR_??N_", "$_DFFSR_??P_", "\\R", assign_map, invert_map);
+
+ handle_clkpol_celltype_swap(cell, "$_DLATCH_N_", "$_DLATCH_P_", "\\E", assign_map, invert_map);
+
+ handle_clkpol_celltype_swap(cell, "$_DLATCHSR_N??_", "$_DLATCHSR_P??_", "\\E", assign_map, invert_map);
+ handle_clkpol_celltype_swap(cell, "$_DLATCHSR_?N?_", "$_DLATCHSR_?P?_", "\\S", assign_map, invert_map);
+ handle_clkpol_celltype_swap(cell, "$_DLATCHSR_??N_", "$_DLATCHSR_??P_", "\\R", assign_map, invert_map);
+ }
+
+ bool detect_const_and = false;
+ bool detect_const_or = false;
+
+ if (cell->type.in("$reduce_and", "$_AND_"))
+ detect_const_and = true;
+
+ if (cell->type.in("$and", "$logic_and") && GetSize(cell->getPort("\\A")) == 1 && GetSize(cell->getPort("\\B")) == 1)
+ detect_const_and = true;
+
+ if (cell->type.in("$reduce_or", "$reduce_bool", "$_OR_"))
+ detect_const_or = true;
+
+ if (cell->type.in("$or", "$logic_or") && GetSize(cell->getPort("\\A")) == 1 && GetSize(cell->getPort("\\B")) == 1)
+ detect_const_or = true;
+
+ if (detect_const_and || detect_const_or)
+ {
+ pool<SigBit> input_bits = assign_map(cell->getPort("\\A")).to_sigbit_pool();
+ bool found_zero = false, found_one = false, found_inv = false;
+
+ if (cell->hasPort("\\B")) {
+ vector<SigBit> more_bits = assign_map(cell->getPort("\\B")).to_sigbit_vector();
+ input_bits.insert(more_bits.begin(), more_bits.end());
+ }
+
+ for (auto bit : input_bits) {
+ if (bit == State::S0)
+ found_zero = true;
+ if (bit == State::S1)
+ found_one = true;
+ if (invert_map.count(bit) && input_bits.count(invert_map.at(bit)))
+ found_inv = true;
+ }
+
+ if (detect_const_and && (found_zero || found_inv)) {
+ cover("opt.opt_expr.const_and");
+ replace_cell(assign_map, module, cell, "const_and", "\\Y", RTLIL::State::S0);
+ goto next_cell;
+ }
+
+ if (detect_const_or && (found_one || found_inv)) {
+ cover("opt.opt_expr.const_or");
+ replace_cell(assign_map, module, cell, "const_or", "\\Y", RTLIL::State::S1);
+ goto next_cell;
+ }
+ }
+
+ if (cell->type.in("$reduce_and", "$reduce_or", "$reduce_bool", "$reduce_xor", "$reduce_xnor", "$neg") &&
+ GetSize(cell->getPort("\\A")) == 1 && GetSize(cell->getPort("\\Y")) == 1)
+ {
+ if (cell->type == "$reduce_xnor") {
+ cover("opt.opt_expr.reduce_xnor_not");
+ log("Replacing %s cell `%s' in module `%s' with $not cell.\n",
+ log_id(cell->type), log_id(cell->name), log_id(module));
+ cell->type = "$not";
+ } else {
+ cover("opt.opt_expr.unary_buffer");
+ replace_cell(assign_map, module, cell, "unary_buffer", "\\Y", cell->getPort("\\A"));
+ }
+ goto next_cell;
+ }
+
if (do_fine)
{
if (cell->type == "$not" || cell->type == "$pos" ||
@@ -256,7 +428,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
}
if (new_a != RTLIL::State::Sm && RTLIL::SigSpec(new_a) != sig_a) {
- cover("opt.opt_const.fine.$reduce_and");
+ cover("opt.opt_expr.fine.$reduce_and");
log("Replacing port A of %s cell `%s' in module `%s' with constant driver: %s -> %s\n",
cell->type.c_str(), cell->name.c_str(), module->name.c_str(), log_signal(sig_a), log_signal(new_a));
cell->setPort("\\A", sig_a = new_a);
@@ -282,7 +454,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
}
if (new_a != RTLIL::State::Sm && RTLIL::SigSpec(new_a) != sig_a) {
- cover_list("opt.opt_const.fine.A", "$logic_not", "$logic_and", "$logic_or", "$reduce_or", "$reduce_bool", cell->type.str());
+ cover_list("opt.opt_expr.fine.A", "$logic_not", "$logic_and", "$logic_or", "$reduce_or", "$reduce_bool", cell->type.str());
log("Replacing port A of %s cell `%s' in module `%s' with constant driver: %s -> %s\n",
cell->type.c_str(), cell->name.c_str(), module->name.c_str(), log_signal(sig_a), log_signal(new_a));
cell->setPort("\\A", sig_a = new_a);
@@ -308,7 +480,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
}
if (new_b != RTLIL::State::Sm && RTLIL::SigSpec(new_b) != sig_b) {
- cover_list("opt.opt_const.fine.B", "$logic_and", "$logic_or", cell->type.str());
+ cover_list("opt.opt_expr.fine.B", "$logic_and", "$logic_or", cell->type.str());
log("Replacing port B of %s cell `%s' in module `%s' with constant driver: %s -> %s\n",
cell->type.c_str(), cell->name.c_str(), module->name.c_str(), log_signal(sig_b), log_signal(new_b));
cell->setPort("\\B", sig_b = new_b);
@@ -318,18 +490,6 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
}
}
- if (cell->type == "$logic_or" && (assign_map(cell->getPort("\\A")) == RTLIL::State::S1 || assign_map(cell->getPort("\\B")) == RTLIL::State::S1)) {
- cover("opt.opt_const.one_high");
- replace_cell(assign_map, module, cell, "one high", "\\Y", RTLIL::State::S1);
- goto next_cell;
- }
-
- if (cell->type == "$logic_and" && (assign_map(cell->getPort("\\A")) == RTLIL::State::S0 || assign_map(cell->getPort("\\B")) == RTLIL::State::S0)) {
- cover("opt.opt_const.one_low");
- replace_cell(assign_map, module, cell, "one low", "\\Y", RTLIL::State::S0);
- goto next_cell;
- }
-
if (cell->type == "$reduce_xor" || cell->type == "$reduce_xnor" || cell->type == "$shift" || cell->type == "$shiftx" ||
cell->type == "$shl" || cell->type == "$shr" || cell->type == "$sshl" || cell->type == "$sshr" ||
cell->type == "$lt" || cell->type == "$le" || cell->type == "$ge" || cell->type == "$gt" ||
@@ -352,7 +512,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
if (0) {
found_the_x_bit:
- cover_list("opt.opt_const.xbit", "$reduce_xor", "$reduce_xnor", "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx",
+ cover_list("opt.opt_expr.xbit", "$reduce_xor", "$reduce_xnor", "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx",
"$lt", "$le", "$ge", "$gt", "$neg", "$add", "$sub", "$mul", "$div", "$mod", "$pow", cell->type.str());
if (cell->type == "$reduce_xor" || cell->type == "$reduce_xnor" ||
cell->type == "$lt" || cell->type == "$le" || cell->type == "$ge" || cell->type == "$gt")
@@ -365,13 +525,13 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
if ((cell->type == "$_NOT_" || cell->type == "$not" || cell->type == "$logic_not") && cell->getPort("\\Y").size() == 1 &&
invert_map.count(assign_map(cell->getPort("\\A"))) != 0) {
- cover_list("opt.opt_const.invert.double", "$_NOT_", "$not", "$logic_not", cell->type.str());
+ cover_list("opt.opt_expr.invert.double", "$_NOT_", "$not", "$logic_not", cell->type.str());
replace_cell(assign_map, module, cell, "double_invert", "\\Y", invert_map.at(assign_map(cell->getPort("\\A"))));
goto next_cell;
}
if ((cell->type == "$_MUX_" || cell->type == "$mux") && invert_map.count(assign_map(cell->getPort("\\S"))) != 0) {
- cover_list("opt.opt_const.invert.muxsel", "$_MUX_", "$mux", cell->type.str());
+ cover_list("opt.opt_expr.invert.muxsel", "$_MUX_", "$mux", cell->type.str());
log("Optimizing away select inverter for %s cell `%s' in module `%s'.\n", log_id(cell->type), log_id(cell), log_id(module));
RTLIL::SigSpec tmp = cell->getPort("\\A");
cell->setPort("\\A", cell->getPort("\\B"));
@@ -454,7 +614,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
if (input.match(" 1")) ACTION_DO("\\Y", input.extract(1, 1));
if (input.match("01 ")) ACTION_DO("\\Y", input.extract(0, 1));
if (input.match("10 ")) {
- cover("opt.opt_const.mux_to_inv");
+ cover("opt.opt_expr.mux_to_inv");
cell->type = "$_NOT_";
cell->setPort("\\A", input.extract(0, 1));
cell->unsetPort("\\B");
@@ -479,7 +639,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
RTLIL::SigSpec b = cell->getPort("\\B");
if (cell->parameters["\\A_WIDTH"].as_int() != cell->parameters["\\B_WIDTH"].as_int()) {
- int width = std::max(cell->parameters["\\A_WIDTH"].as_int(), cell->parameters["\\B_WIDTH"].as_int());
+ int width = max(cell->parameters["\\A_WIDTH"].as_int(), cell->parameters["\\B_WIDTH"].as_int());
a.extend_u0(width, cell->parameters["\\A_SIGNED"].as_bool() && cell->parameters["\\B_SIGNED"].as_bool());
b.extend_u0(width, cell->parameters["\\A_SIGNED"].as_bool() && cell->parameters["\\B_SIGNED"].as_bool());
}
@@ -489,7 +649,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
log_assert(GetSize(a) == GetSize(b));
for (int i = 0; i < GetSize(a); i++) {
if (a[i].wire == NULL && b[i].wire == NULL && a[i] != b[i] && a[i].data <= RTLIL::State::S1 && b[i].data <= RTLIL::State::S1) {
- cover_list("opt.opt_const.eqneq.isneq", "$eq", "$ne", "$eqx", "$nex", cell->type.str());
+ cover_list("opt.opt_expr.eqneq.isneq", "$eq", "$ne", "$eqx", "$nex", cell->type.str());
RTLIL::SigSpec new_y = RTLIL::SigSpec((cell->type == "$eq" || cell->type == "$eqx") ? RTLIL::State::S0 : RTLIL::State::S1);
new_y.extend_u0(cell->parameters["\\Y_WIDTH"].as_int(), false);
replace_cell(assign_map, module, cell, "isneq", "\\Y", new_y);
@@ -502,7 +662,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
}
if (new_a.size() == 0) {
- cover_list("opt.opt_const.eqneq.empty", "$eq", "$ne", "$eqx", "$nex", cell->type.str());
+ cover_list("opt.opt_expr.eqneq.empty", "$eq", "$ne", "$eqx", "$nex", cell->type.str());
RTLIL::SigSpec new_y = RTLIL::SigSpec((cell->type == "$eq" || cell->type == "$eqx") ? RTLIL::State::S1 : RTLIL::State::S0);
new_y.extend_u0(cell->parameters["\\Y_WIDTH"].as_int(), false);
replace_cell(assign_map, module, cell, "empty", "\\Y", new_y);
@@ -510,7 +670,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
}
if (new_a.size() < a.size() || new_b.size() < b.size()) {
- cover_list("opt.opt_const.eqneq.resize", "$eq", "$ne", "$eqx", "$nex", cell->type.str());
+ cover_list("opt.opt_expr.eqneq.resize", "$eq", "$ne", "$eqx", "$nex", cell->type.str());
cell->setPort("\\A", new_a);
cell->setPort("\\B", new_b);
cell->parameters["\\A_WIDTH"] = new_a.size();
@@ -525,7 +685,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
RTLIL::SigSpec b = assign_map(cell->getPort("\\B"));
if (a.is_fully_const() && !b.is_fully_const()) {
- cover_list("opt.opt_const.eqneq.swapconst", "$eq", "$ne", cell->type.str());
+ cover_list("opt.opt_expr.eqneq.swapconst", "$eq", "$ne", cell->type.str());
cell->setPort("\\A", b);
cell->setPort("\\B", a);
std::swap(a, b);
@@ -536,7 +696,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
RTLIL::SigSpec input = b;
ACTION_DO("\\Y", cell->getPort("\\A"));
} else {
- cover_list("opt.opt_const.eqneq.isnot", "$eq", "$ne", cell->type.str());
+ cover_list("opt.opt_expr.eqneq.isnot", "$eq", "$ne", cell->type.str());
log("Replacing %s cell `%s' in module `%s' with inverter.\n", log_id(cell->type), log_id(cell), log_id(module));
cell->type = "$not";
cell->parameters.erase("\\B_WIDTH");
@@ -548,6 +708,25 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
}
}
+ if ((cell->type == "$eq" || cell->type == "$ne") &&
+ (assign_map(cell->getPort("\\A")).is_fully_zero() || assign_map(cell->getPort("\\B")).is_fully_zero()))
+ {
+ cover_list("opt.opt_expr.eqneq.cmpzero", "$eq", "$ne", cell->type.str());
+ log("Replacing %s cell `%s' in module `%s' with %s.\n", log_id(cell->type), log_id(cell),
+ log_id(module), "$eq" ? "$logic_not" : "$reduce_bool");
+ cell->type = cell->type == "$eq" ? "$logic_not" : "$reduce_bool";
+ if (assign_map(cell->getPort("\\A")).is_fully_zero()) {
+ cell->setPort("\\A", cell->getPort("\\B"));
+ cell->setParam("\\A_SIGNED", cell->getParam("\\B_SIGNED"));
+ cell->setParam("\\A_WIDTH", cell->getParam("\\B_WIDTH"));
+ }
+ cell->unsetPort("\\B");
+ cell->unsetParam("\\B_SIGNED");
+ cell->unsetParam("\\B_WIDTH");
+ did_something = true;
+ goto next_cell;
+ }
+
if (cell->type.in("$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx") && assign_map(cell->getPort("\\B")).is_fully_const())
{
bool sign_ext = cell->type == "$sshr" && cell->getParam("\\A_SIGNED").as_bool();
@@ -570,7 +749,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
sig_y[i] = sig_a[GetSize(sig_a)-1];
}
- cover_list("opt.opt_const.constshift", "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx", cell->type.str());
+ cover_list("opt.opt_expr.constshift", "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx", cell->type.str());
log("Replacing %s cell `%s' (B=%s, SHR=%d) in module `%s' with fixed wiring: %s\n",
log_id(cell->type), log_id(cell), log_signal(assign_map(cell->getPort("\\B"))), shift_bits, log_id(module), log_signal(sig_y));
@@ -586,6 +765,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
{
bool identity_wrt_a = false;
bool identity_wrt_b = false;
+ bool arith_inverse = false;
if (cell->type == "$add" || cell->type == "$sub" || cell->type == "$or" || cell->type == "$xor")
{
@@ -612,10 +792,10 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
RTLIL::SigSpec a = assign_map(cell->getPort("\\A"));
RTLIL::SigSpec b = assign_map(cell->getPort("\\B"));
- if (a.is_fully_const() && a.size() <= 32 && a.as_int() == 1)
+ if (a.is_fully_const() && is_one_or_minus_one(a.as_const(), cell->getParam("\\A_SIGNED").as_bool(), arith_inverse))
identity_wrt_b = true;
-
- if (b.is_fully_const() && b.size() <= 32 && b.as_int() == 1)
+ else
+ if (b.is_fully_const() && is_one_or_minus_one(b.as_const(), cell->getParam("\\B_SIGNED").as_bool(), arith_inverse))
identity_wrt_a = true;
}
@@ -630,9 +810,9 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
if (identity_wrt_a || identity_wrt_b)
{
if (identity_wrt_a)
- cover_list("opt.opt_const.identwrt.a", "$add", "$sub", "$or", "$xor", "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx", "$mul", "$div", cell->type.str());
+ cover_list("opt.opt_expr.identwrt.a", "$add", "$sub", "$or", "$xor", "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx", "$mul", "$div", cell->type.str());
if (identity_wrt_b)
- cover_list("opt.opt_const.identwrt.b", "$add", "$sub", "$or", "$xor", "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx", "$mul", "$div", cell->type.str());
+ cover_list("opt.opt_expr.identwrt.b", "$add", "$sub", "$or", "$xor", "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx", "$mul", "$div", cell->type.str());
log("Replacing %s cell `%s' in module `%s' with identity for port %c.\n",
cell->type.c_str(), cell->name.c_str(), module->name.c_str(), identity_wrt_a ? 'A' : 'B');
@@ -643,7 +823,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
cell->parameters.at("\\A_SIGNED") = cell->parameters.at("\\B_SIGNED");
}
- cell->type = "$pos";
+ cell->type = arith_inverse ? "$neg" : "$pos";
cell->unsetPort("\\B");
cell->parameters.erase("\\B_WIDTH");
cell->parameters.erase("\\B_SIGNED");
@@ -656,14 +836,14 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
if (mux_bool && (cell->type == "$mux" || cell->type == "$_MUX_") &&
cell->getPort("\\A") == RTLIL::SigSpec(0, 1) && cell->getPort("\\B") == RTLIL::SigSpec(1, 1)) {
- cover_list("opt.opt_const.mux_bool", "$mux", "$_MUX_", cell->type.str());
+ cover_list("opt.opt_expr.mux_bool", "$mux", "$_MUX_", cell->type.str());
replace_cell(assign_map, module, cell, "mux_bool", "\\Y", cell->getPort("\\S"));
goto next_cell;
}
if (mux_bool && (cell->type == "$mux" || cell->type == "$_MUX_") &&
cell->getPort("\\A") == RTLIL::SigSpec(1, 1) && cell->getPort("\\B") == RTLIL::SigSpec(0, 1)) {
- cover_list("opt.opt_const.mux_invert", "$mux", "$_MUX_", cell->type.str());
+ cover_list("opt.opt_expr.mux_invert", "$mux", "$_MUX_", cell->type.str());
log("Replacing %s cell `%s' in module `%s' with inverter.\n", log_id(cell->type), log_id(cell), log_id(module));
cell->setPort("\\A", cell->getPort("\\S"));
cell->unsetPort("\\B");
@@ -682,7 +862,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
}
if (consume_x && mux_bool && (cell->type == "$mux" || cell->type == "$_MUX_") && cell->getPort("\\A") == RTLIL::SigSpec(0, 1)) {
- cover_list("opt.opt_const.mux_and", "$mux", "$_MUX_", cell->type.str());
+ cover_list("opt.opt_expr.mux_and", "$mux", "$_MUX_", cell->type.str());
log("Replacing %s cell `%s' in module `%s' with and-gate.\n", log_id(cell->type), log_id(cell), log_id(module));
cell->setPort("\\A", cell->getPort("\\S"));
cell->unsetPort("\\S");
@@ -702,7 +882,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
}
if (consume_x && mux_bool && (cell->type == "$mux" || cell->type == "$_MUX_") && cell->getPort("\\B") == RTLIL::SigSpec(1, 1)) {
- cover_list("opt.opt_const.mux_or", "$mux", "$_MUX_", cell->type.str());
+ cover_list("opt.opt_expr.mux_or", "$mux", "$_MUX_", cell->type.str());
log("Replacing %s cell `%s' in module `%s' with or-gate.\n", log_id(cell->type), log_id(cell), log_id(module));
cell->setPort("\\B", cell->getPort("\\S"));
cell->unsetPort("\\S");
@@ -726,7 +906,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
int width = cell->getPort("\\A").size();
if ((cell->getPort("\\A").is_fully_undef() && cell->getPort("\\B").is_fully_undef()) ||
cell->getPort("\\S").is_fully_undef()) {
- cover_list("opt.opt_const.mux_undef", "$mux", "$pmux", cell->type.str());
+ cover_list("opt.opt_expr.mux_undef", "$mux", "$pmux", cell->type.str());
replace_cell(assign_map, module, cell, "mux_undef", "\\Y", cell->getPort("\\A"));
goto next_cell;
}
@@ -745,17 +925,17 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
new_s = new_s.extract(0, new_s.size()-1);
}
if (new_s.size() == 0) {
- cover_list("opt.opt_const.mux_empty", "$mux", "$pmux", cell->type.str());
+ cover_list("opt.opt_expr.mux_empty", "$mux", "$pmux", cell->type.str());
replace_cell(assign_map, module, cell, "mux_empty", "\\Y", new_a);
goto next_cell;
}
if (new_a == RTLIL::SigSpec(RTLIL::State::S0) && new_b == RTLIL::SigSpec(RTLIL::State::S1)) {
- cover_list("opt.opt_const.mux_sel01", "$mux", "$pmux", cell->type.str());
+ cover_list("opt.opt_expr.mux_sel01", "$mux", "$pmux", cell->type.str());
replace_cell(assign_map, module, cell, "mux_sel01", "\\Y", new_s);
goto next_cell;
}
if (cell->getPort("\\S").size() != new_s.size()) {
- cover_list("opt.opt_const.mux_reduce", "$mux", "$pmux", cell->type.str());
+ cover_list("opt.opt_expr.mux_reduce", "$mux", "$pmux", cell->type.str());
log("Optimized away %d select inputs of %s cell `%s' in module `%s'.\n",
GetSize(cell->getPort("\\S")) - GetSize(new_s), log_id(cell->type), log_id(cell), log_id(module));
cell->setPort("\\A", new_a);
@@ -781,7 +961,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
RTLIL::SigSpec y(RTLIL::const_ ## _t(a.as_const(), dummy_arg, \
cell->parameters["\\A_SIGNED"].as_bool(), false, \
cell->parameters["\\Y_WIDTH"].as_int())); \
- cover("opt.opt_const.const.$" #_t); \
+ cover("opt.opt_expr.const.$" #_t); \
replace_cell(assign_map, module, cell, stringf("%s", log_signal(a)), "\\Y", y); \
goto next_cell; \
} \
@@ -796,7 +976,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
cell->parameters["\\A_SIGNED"].as_bool(), \
cell->parameters["\\B_SIGNED"].as_bool(), \
cell->parameters["\\Y_WIDTH"].as_int())); \
- cover("opt.opt_const.const.$" #_t); \
+ cover("opt.opt_expr.const.$" #_t); \
replace_cell(assign_map, module, cell, stringf("%s, %s", log_signal(a), log_signal(b)), "\\Y", y); \
goto next_cell; \
} \
@@ -872,7 +1052,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
if (a_val == 0)
{
- cover("opt.opt_const.mul_shift.zero");
+ cover("opt.opt_expr.mul_shift.zero");
log("Replacing multiply-by-zero cell `%s' in module `%s' with zero-driver.\n",
cell->name.c_str(), module->name.c_str());
@@ -888,9 +1068,9 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
if (a_val == (1 << i))
{
if (swapped_ab)
- cover("opt.opt_const.mul_shift.swapped");
+ cover("opt.opt_expr.mul_shift.swapped");
else
- cover("opt.opt_const.mul_shift.unswapped");
+ cover("opt.opt_expr.mul_shift.unswapped");
log("Replacing multiply-by-%d cell `%s' in module `%s' with shift-by-%d.\n",
a_val, cell->name.c_str(), module->name.c_str(), i);
@@ -918,6 +1098,75 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
}
}
+ if (!keepdc && cell->type.in("$div", "$mod"))
+ {
+ bool b_signed = cell->parameters["\\B_SIGNED"].as_bool();
+ SigSpec sig_b = assign_map(cell->getPort("\\B"));
+ SigSpec sig_y = assign_map(cell->getPort("\\Y"));
+
+ if (sig_b.is_fully_def() && sig_b.size() <= 32)
+ {
+ int b_val = sig_b.as_int();
+
+ if (b_val == 0)
+ {
+ cover("opt.opt_expr.divmod_zero");
+
+ log("Replacing divide-by-zero cell `%s' in module `%s' with undef-driver.\n",
+ cell->name.c_str(), module->name.c_str());
+
+ module->connect(RTLIL::SigSig(sig_y, RTLIL::SigSpec(State::Sx, sig_y.size())));
+ module->remove(cell);
+
+ did_something = true;
+ goto next_cell;
+ }
+
+ for (int i = 1; i < (b_signed ? sig_b.size()-1 : sig_b.size()); i++)
+ if (b_val == (1 << i))
+ {
+ if (cell->type == "$div")
+ {
+ cover("opt.opt_expr.div_shift");
+
+ log("Replacing divide-by-%d cell `%s' in module `%s' with shift-by-%d.\n",
+ b_val, cell->name.c_str(), module->name.c_str(), i);
+
+ std::vector<RTLIL::SigBit> new_b = RTLIL::SigSpec(i, 6);
+
+ while (GetSize(new_b) > 1 && new_b.back() == RTLIL::State::S0)
+ new_b.pop_back();
+
+ cell->type = "$shr";
+ cell->parameters["\\B_WIDTH"] = GetSize(new_b);
+ cell->parameters["\\B_SIGNED"] = false;
+ cell->setPort("\\B", new_b);
+ cell->check();
+ }
+ else
+ {
+ cover("opt.opt_expr.mod_mask");
+
+ log("Replacing modulo-by-%d cell `%s' in module `%s' with bitmask.\n",
+ b_val, cell->name.c_str(), module->name.c_str());
+
+ std::vector<RTLIL::SigBit> new_b = RTLIL::SigSpec(State::S1, i);
+
+ if (b_signed)
+ new_b.push_back(State::S0);
+
+ cell->type = "$and";
+ cell->parameters["\\B_WIDTH"] = GetSize(new_b);
+ cell->setPort("\\B", new_b);
+ cell->check();
+ }
+
+ did_something = true;
+ goto next_cell;
+ }
+ }
+ }
+
next_cell:;
#undef ACTION_DO
#undef ACTION_DO_Y
@@ -926,15 +1175,16 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
}
}
-struct OptConstPass : public Pass {
- OptConstPass() : Pass("opt_const", "perform const folding") { }
+struct OptExprPass : public Pass {
+ OptExprPass() : Pass("opt_expr", "perform const folding and simple expression rewriting") { }
virtual void help()
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
- log(" opt_const [options] [selection]\n");
+ log(" opt_expr [options] [selection]\n");
log("\n");
log("This pass performs const folding on internal cell types with constant inputs.\n");
+ log("It also performs some simple expression rewritring.\n");
log("\n");
log(" -mux_undef\n");
log(" remove 'undef' inputs from $mux, $pmux and $_MUX_ cells\n");
@@ -945,6 +1195,9 @@ struct OptConstPass : public Pass {
log(" -undriven\n");
log(" replace undriven nets with undef (x) constants\n");
log("\n");
+ log(" -clkinv\n");
+ log(" optimize clock inverters by changing FF types\n");
+ log("\n");
log(" -fine\n");
log(" perform fine-grain optimizations\n");
log("\n");
@@ -963,10 +1216,11 @@ struct OptConstPass : public Pass {
bool mux_undef = false;
bool mux_bool = false;
bool undriven = false;
+ bool clkinv = false;
bool do_fine = false;
bool keepdc = false;
- log_header("Executing OPT_CONST pass (perform const folding).\n");
+ log_header(design, "Executing OPT_EXPR pass (perform const folding).\n");
log_push();
size_t argidx;
@@ -983,6 +1237,10 @@ struct OptConstPass : public Pass {
undriven = true;
continue;
}
+ if (args[argidx] == "-clkinv") {
+ clkinv = true;
+ continue;
+ }
if (args[argidx] == "-fine") {
do_fine = true;
continue;
@@ -1010,16 +1268,16 @@ struct OptConstPass : public Pass {
do {
do {
did_something = false;
- replace_const_cells(design, module, false, mux_undef, mux_bool, do_fine, keepdc);
+ replace_const_cells(design, module, false, mux_undef, mux_bool, do_fine, keepdc, clkinv);
if (did_something)
design->scratchpad_set_bool("opt.did_something", true);
} while (did_something);
- replace_const_cells(design, module, true, mux_undef, mux_bool, do_fine, keepdc);
+ replace_const_cells(design, module, true, mux_undef, mux_bool, do_fine, keepdc, clkinv);
} while (did_something);
}
log_pop();
}
-} OptConstPass;
+} OptExprPass;
PRIVATE_NAMESPACE_END
diff --git a/passes/opt/opt_share.cc b/passes/opt/opt_merge.cc
index cce97d65b..97989d271 100644
--- a/passes/opt/opt_share.cc
+++ b/passes/opt/opt_merge.cc
@@ -2,11 +2,11 @@
* 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
@@ -31,12 +31,13 @@
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
-struct OptShareWorker
+struct OptMergeWorker
{
RTLIL::Design *design;
RTLIL::Module *module;
SigMap assign_map;
SigMap dff_init_map;
+ bool mode_share_all;
CellTypes ct;
int total_count;
@@ -44,6 +45,29 @@ struct OptShareWorker
dict<const RTLIL::Cell*, std::string> cell_hash_cache;
#endif
+ static void sort_pmux_conn(dict<RTLIL::IdString, RTLIL::SigSpec> &conn)
+ {
+ SigSpec sig_s = conn.at("\\S");
+ SigSpec sig_b = conn.at("\\B");
+
+ int s_width = GetSize(sig_s);
+ int width = GetSize(sig_b) / s_width;
+
+ vector<pair<SigBit, SigSpec>> sb_pairs;
+ for (int i = 0; i < s_width; i++)
+ sb_pairs.push_back(pair<SigBit, SigSpec>(sig_s[i], sig_b.extract(i*width, width)));
+
+ std::sort(sb_pairs.begin(), sb_pairs.end());
+
+ conn["\\S"] = SigSpec();
+ conn["\\B"] = SigSpec();
+
+ for (auto &it : sb_pairs) {
+ conn["\\S"].append(it.first);
+ conn["\\B"].append(it.second);
+ }
+ }
+
#ifdef USE_CELL_HASH_CACHE
std::string int_to_hash_string(unsigned int v)
{
@@ -90,25 +114,40 @@ struct OptShareWorker
assign_map.apply(alt_conn.at("\\A"));
alt_conn.at("\\A").sort_and_unify();
conn = &alt_conn;
+ } else
+ if (cell->type == "$pmux") {
+ alt_conn = *conn;
+ assign_map.apply(alt_conn.at("\\A"));
+ assign_map.apply(alt_conn.at("\\B"));
+ assign_map.apply(alt_conn.at("\\S"));
+ sort_pmux_conn(alt_conn);
+ conn = &alt_conn;
}
+ vector<string> hash_conn_strings;
+
for (auto &it : *conn) {
- if (ct.cell_output(cell->type, it.first))
+ if (cell->output(it.first))
continue;
RTLIL::SigSpec sig = it.second;
assign_map.apply(sig);
- hash_string += "C " + it.first.str() + "=";
+ string s = "C " + it.first.str() + "=";
for (auto &chunk : sig.chunks()) {
if (chunk.wire)
- hash_string += "{" + chunk.wire->name.str() + " " +
+ s += "{" + chunk.wire->name.str() + " " +
int_to_hash_string(chunk.offset) + " " +
int_to_hash_string(chunk.width) + "}";
else
- hash_string += RTLIL::Const(chunk.data).as_string();
+ s += RTLIL::Const(chunk.data).as_string();
}
- hash_string += "\n";
+ hash_conn_strings.push_back(s + "\n");
}
+ std::sort(hash_conn_strings.begin(), hash_conn_strings.end());
+
+ for (auto it : hash_conn_strings)
+ hash_string += it;
+
cell_hash_cache[cell] = sha1(hash_string);
return cell_hash_cache[cell];
}
@@ -137,14 +176,14 @@ struct OptShareWorker
dict<RTLIL::IdString, RTLIL::SigSpec> conn2 = cell2->connections();
for (auto &it : conn1) {
- if (ct.cell_output(cell1->type, it.first))
+ if (cell1->output(it.first))
it.second = RTLIL::SigSpec();
else
assign_map.apply(it.second);
}
for (auto &it : conn2) {
- if (ct.cell_output(cell2->type, it.first))
+ if (cell2->output(it.first))
it.second = RTLIL::SigSpec();
else
assign_map.apply(it.second);
@@ -170,6 +209,10 @@ struct OptShareWorker
if (cell1->type == "$reduce_and" || cell1->type == "$reduce_or" || cell1->type == "$reduce_bool") {
conn1["\\A"].sort_and_unify();
conn2["\\A"].sort_and_unify();
+ } else
+ if (cell1->type == "$pmux") {
+ sort_pmux_conn(conn1);
+ sort_pmux_conn(conn2);
}
if (conn1 != conn2) {
@@ -197,7 +240,7 @@ struct OptShareWorker
if (cell1->type != cell2->type)
return cell1->type < cell2->type;
- if (!ct.cell_known(cell1->type))
+ if ((!mode_share_all && !ct.cell_known(cell1->type)) || !cell1->known())
return cell1 < cell2;
if (cell1->has_keep_attr() || cell2->has_keep_attr())
@@ -211,15 +254,15 @@ struct OptShareWorker
}
struct CompareCells {
- OptShareWorker *that;
- CompareCells(OptShareWorker *that) : that(that) {}
+ OptMergeWorker *that;
+ CompareCells(OptMergeWorker *that) : that(that) {}
bool operator()(const RTLIL::Cell *cell1, const RTLIL::Cell *cell2) const {
return that->compare_cells(cell1, cell2);
}
};
- OptShareWorker(RTLIL::Design *design, RTLIL::Module *module, bool mode_nomux) :
- design(design), module(module), assign_map(module)
+ OptMergeWorker(RTLIL::Design *design, RTLIL::Module *module, bool mode_nomux, bool mode_share_all) :
+ design(design), module(module), assign_map(module), mode_share_all(mode_share_all)
{
total_count = 0;
ct.setup_internals();
@@ -249,7 +292,9 @@ struct OptShareWorker
std::vector<RTLIL::Cell*> cells;
cells.reserve(module->cells_.size());
for (auto &it : module->cells_) {
- if (ct.cell_known(it.second->type) && design->selected(module, it.second))
+ if (!design->selected(module, it.second))
+ continue;
+ if (ct.cell_known(it.second->type) || (mode_share_all && it.second->known()))
cells.push_back(it.second);
}
@@ -261,7 +306,7 @@ struct OptShareWorker
did_something = true;
log(" Cell `%s' is identical to cell `%s'.\n", cell->name.c_str(), sharemap[cell]->name.c_str());
for (auto &it : cell->connections()) {
- if (ct.cell_output(cell->type, it.first)) {
+ if (cell->output(it.first)) {
RTLIL::SigSpec other_sig = sharemap[cell]->getPort(it.first);
log(" Redirecting output %s: %s = %s\n", it.first.c_str(),
log_signal(it.second), log_signal(other_sig));
@@ -270,7 +315,9 @@ struct OptShareWorker
}
}
log(" Removing %s cell `%s' from module `%s'.\n", cell->type.c_str(), cell->name.c_str(), module->name.c_str());
+#ifdef USE_CELL_HASH_CACHE
cell_hash_cache.erase(cell);
+#endif
module->remove(cell);
total_count++;
} else {
@@ -281,13 +328,13 @@ struct OptShareWorker
}
};
-struct OptSharePass : public Pass {
- OptSharePass() : Pass("opt_share", "consolidate identical cells") { }
+struct OptMergePass : public Pass {
+ OptMergePass() : Pass("opt_merge", "consolidate identical cells") { }
virtual void help()
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
- log(" opt_share [-nomux] [selection]\n");
+ log(" opt_merge [options] [selection]\n");
log("\n");
log("This pass identifies cells with identical type and input signals. Such cells\n");
log("are then merged to one cell.\n");
@@ -295,12 +342,16 @@ struct OptSharePass : public Pass {
log(" -nomux\n");
log(" Do not merge MUX cells.\n");
log("\n");
+ log(" -share_all\n");
+ log(" Operate on all cell types, not just built-in types.\n");
+ log("\n");
}
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
{
- log_header("Executing OPT_SHARE pass (detect identical cells).\n");
+ log_header(design, "Executing OPT_MERGE pass (detect identical cells).\n");
bool mode_nomux = false;
+ bool mode_share_all = false;
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++) {
@@ -309,13 +360,17 @@ struct OptSharePass : public Pass {
mode_nomux = true;
continue;
}
+ if (arg == "-share_all") {
+ mode_share_all = true;
+ continue;
+ }
break;
}
extra_args(args, argidx, design);
int total_count = 0;
for (auto module : design->selected_modules()) {
- OptShareWorker worker(design, module, mode_nomux);
+ OptMergeWorker worker(design, module, mode_nomux, mode_share_all);
total_count += worker.total_count;
}
@@ -323,6 +378,6 @@ struct OptSharePass : public Pass {
design->scratchpad_set_bool("opt.did_something", true);
log("Removed a total of %d cells.\n", total_count);
}
-} OptSharePass;
-
+} OptMergePass;
+
PRIVATE_NAMESPACE_END
diff --git a/passes/opt/opt_muxtree.cc b/passes/opt/opt_muxtree.cc
index 982870745..f5ddc2af9 100644
--- a/passes/opt/opt_muxtree.cc
+++ b/passes/opt/opt_muxtree.cc
@@ -2,11 +2,11 @@
* 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
@@ -68,7 +68,7 @@ struct OptMuxtreeWorker
OptMuxtreeWorker(RTLIL::Design *design, RTLIL::Module *module) :
design(design), module(module), assign_map(module), removed_count(0)
{
- log("Running muxtree optimizier on module %s..\n", module->name.c_str());
+ log("Running muxtree optimizer on module %s..\n", module->name.c_str());
log(" Creating internal representation of mux trees.\n");
@@ -136,7 +136,7 @@ struct OptMuxtreeWorker
}
}
for (auto wire : module->wires()) {
- if (wire->port_output)
+ if (wire->port_output || wire->get_bool_attribute("\\keep"))
for (int idx : sig2bits(RTLIL::SigSpec(wire)))
bit2info[idx].seen_non_mux = true;
}
@@ -464,7 +464,7 @@ struct OptMuxtreePass : public Pass {
}
virtual void execute(vector<std::string> args, RTLIL::Design *design)
{
- log_header("Executing OPT_MUXTREE pass (detect dead branches in mux trees).\n");
+ log_header(design, "Executing OPT_MUXTREE pass (detect dead branches in mux trees).\n");
extra_args(args, 1, design);
int total_count = 0;
@@ -479,5 +479,5 @@ struct OptMuxtreePass : public Pass {
log("Removed %d multiplexer ports.\n", total_count);
}
} OptMuxtreePass;
-
+
PRIVATE_NAMESPACE_END
diff --git a/passes/opt/opt_reduce.cc b/passes/opt/opt_reduce.cc
index 5c36eb26b..eb9d02ad5 100644
--- a/passes/opt/opt_reduce.cc
+++ b/passes/opt/opt_reduce.cc
@@ -2,11 +2,11 @@
* 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
@@ -284,7 +284,7 @@ struct OptReduceWorker
did_something = false;
// merge trees of reduce_* cells to one single cell and unify input vectors
- // (only handle recduce_and and reduce_or for various reasons)
+ // (only handle reduce_and and reduce_or for various reasons)
const char *type_list[] = { "$reduce_or", "$reduce_and" };
for (auto type : type_list)
@@ -354,7 +354,7 @@ struct OptReducePass : public Pass {
{
bool do_fine = false;
- log_header("Executing OPT_REDUCE pass (consolidate $*mux and $reduce_* inputs).\n");
+ log_header(design, "Executing OPT_REDUCE pass (consolidate $*mux and $reduce_* inputs).\n");
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++) {
@@ -384,5 +384,5 @@ struct OptReducePass : public Pass {
log("Performed a total of %d changes.\n", total_count);
}
} OptReducePass;
-
+
PRIVATE_NAMESPACE_END
diff --git a/passes/opt/opt_rmdff.cc b/passes/opt/opt_rmdff.cc
index 5f52bb8d8..1711d0f45 100644
--- a/passes/opt/opt_rmdff.cc
+++ b/passes/opt/opt_rmdff.cc
@@ -2,11 +2,11 @@
* 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
@@ -28,6 +28,43 @@ PRIVATE_NAMESPACE_BEGIN
SigMap assign_map, dff_init_map;
SigSet<RTLIL::Cell*> mux_drivers;
+dict<SigBit, pool<SigBit>> init_attributes;
+
+void remove_init_attr(SigSpec sig)
+{
+ for (auto bit : assign_map(sig))
+ if (init_attributes.count(bit))
+ for (auto wbit : init_attributes.at(bit))
+ wbit.wire->attributes.at("\\init")[wbit.offset] = State::Sx;
+}
+
+bool handle_dlatch(RTLIL::Module *mod, RTLIL::Cell *dlatch)
+{
+ SigSpec sig_e = dlatch->getPort("\\EN");
+
+ if (sig_e == State::S0)
+ {
+ RTLIL::Const val_init;
+ for (auto bit : dff_init_map(dlatch->getPort("\\Q")))
+ val_init.bits.push_back(bit.wire == NULL ? bit.data : State::Sx);
+ mod->connect(dlatch->getPort("\\Q"), val_init);
+ goto delete_dlatch;
+ }
+
+ if (sig_e == State::S1)
+ {
+ mod->connect(dlatch->getPort("\\Q"), dlatch->getPort("\\D"));
+ goto delete_dlatch;
+ }
+
+ return false;
+
+delete_dlatch:
+ log("Removing %s (%s) from module %s.\n", dlatch->name.c_str(), dlatch->type.c_str(), mod->name.c_str());
+ remove_init_attr(dlatch->getPort("\\Q"));
+ mod->remove(dlatch);
+ return true;
+}
bool handle_dff(RTLIL::Module *mod, RTLIL::Cell *dff)
{
@@ -83,60 +120,50 @@ bool handle_dff(RTLIL::Module *mod, RTLIL::Cell *dff)
val_init.bits.push_back(bit.wire == NULL ? bit.data : RTLIL::State::Sx);
}
- if (dff->type == "$dff" && mux_drivers.has(sig_d) && !has_init) {
+ if (dff->type == "$dff" && mux_drivers.has(sig_d)) {
std::set<RTLIL::Cell*> muxes;
mux_drivers.find(sig_d, muxes);
for (auto mux : muxes) {
RTLIL::SigSpec sig_a = assign_map(mux->getPort("\\A"));
RTLIL::SigSpec sig_b = assign_map(mux->getPort("\\B"));
- if (sig_a == sig_q && sig_b.is_fully_const()) {
- RTLIL::SigSig conn(sig_q, sig_b);
- mod->connect(conn);
+ if (sig_a == sig_q && sig_b.is_fully_const() && (!has_init || val_init == sig_b.as_const())) {
+ mod->connect(sig_q, sig_b);
goto delete_dff;
}
- if (sig_b == sig_q && sig_a.is_fully_const()) {
- RTLIL::SigSig conn(sig_q, sig_a);
- mod->connect(conn);
+ if (sig_b == sig_q && sig_a.is_fully_const() && (!has_init || val_init == sig_a.as_const())) {
+ mod->connect(sig_q, sig_a);
goto delete_dff;
}
}
}
- if (sig_c.is_fully_const() && (!sig_r.size() || !has_init)) {
+ if (sig_c.is_fully_const() && (!sig_r.size() || !has_init || val_init == val_rv)) {
if (val_rv.bits.size() == 0)
val_rv = val_init;
- RTLIL::SigSig conn(sig_q, val_rv);
- mod->connect(conn);
+ mod->connect(sig_q, val_rv);
goto delete_dff;
}
- if (sig_d.is_fully_undef() && sig_r.size() && !has_init) {
- RTLIL::SigSig conn(sig_q, val_rv);
- mod->connect(conn);
+ if (sig_d.is_fully_undef() && sig_r.size() && (!has_init || val_init == val_rv)) {
+ mod->connect(sig_q, val_rv);
goto delete_dff;
}
if (sig_d.is_fully_undef() && !sig_r.size() && has_init) {
- RTLIL::SigSig conn(sig_q, val_init);
- mod->connect(conn);
+ mod->connect(sig_q, val_init);
goto delete_dff;
}
- if (sig_d.is_fully_const() && !sig_r.size() && !has_init) {
- RTLIL::SigSig conn(sig_q, sig_d);
- mod->connect(conn);
+ if (sig_d.is_fully_const() && (!sig_r.size() || val_rv == sig_d.as_const()) && (!has_init || val_init == sig_d.as_const())) {
+ mod->connect(sig_q, sig_d);
goto delete_dff;
}
- if (sig_d == sig_q && !(sig_r.size() && has_init)) {
- if (sig_r.size()) {
- RTLIL::SigSig conn(sig_q, val_rv);
- mod->connect(conn);
- }
- if (has_init) {
- RTLIL::SigSig conn(sig_q, val_init);
- mod->connect(conn);
- }
+ if (sig_d == sig_q && (!sig_r.size() || !has_init || val_init == val_rv)) {
+ if (sig_r.size())
+ mod->connect(sig_q, val_rv);
+ if (has_init)
+ mod->connect(sig_q, val_init);
goto delete_dff;
}
@@ -144,6 +171,7 @@ bool handle_dff(RTLIL::Module *mod, RTLIL::Cell *dff)
delete_dff:
log("Removing %s (%s) from module %s.\n", dff->name.c_str(), dff->type.c_str(), mod->name.c_str());
+ remove_init_attr(dff->getPort("\\Q"));
mod->remove(dff);
return true;
}
@@ -163,7 +191,7 @@ struct OptRmdffPass : public Pass {
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
{
int total_count = 0;
- log_header("Executing OPT_RMDFF pass (remove dff with constant values).\n");
+ log_header(design, "Executing OPT_RMDFF pass (remove dff with constant values).\n");
extra_args(args, 1, design);
@@ -175,11 +203,18 @@ struct OptRmdffPass : public Pass {
assign_map.set(mod_it.second);
dff_init_map.set(mod_it.second);
for (auto &it : mod_it.second->wires_)
- if (it.second->attributes.count("\\init") != 0)
+ if (it.second->attributes.count("\\init") != 0) {
dff_init_map.add(it.second, it.second->attributes.at("\\init"));
+ for (int i = 0; i < GetSize(it.second); i++) {
+ SigBit wire_bit(it.second, i), mapped_bit = assign_map(wire_bit);
+ if (mapped_bit.wire)
+ init_attributes[mapped_bit].insert(wire_bit);
+ }
+ }
mux_drivers.clear();
std::vector<RTLIL::IdString> dff_list;
+ std::vector<RTLIL::IdString> dlatch_list;
for (auto &it : mod_it.second->cells_) {
if (it.second->type == "$mux" || it.second->type == "$pmux") {
if (it.second->getPort("\\A").size() == it.second->getPort("\\B").size())
@@ -200,6 +235,7 @@ struct OptRmdffPass : public Pass {
if (it.second->type == "$_DFF_PP1_") dff_list.push_back(it.first);
if (it.second->type == "$dff") dff_list.push_back(it.first);
if (it.second->type == "$adff") dff_list.push_back(it.first);
+ if (it.second->type == "$dlatch") dlatch_list.push_back(it.first);
}
for (auto &id : dff_list) {
@@ -207,6 +243,12 @@ struct OptRmdffPass : public Pass {
handle_dff(mod_it.second, mod_it.second->cells_[id]))
total_count++;
}
+
+ for (auto &id : dlatch_list) {
+ if (mod_it.second->cells_.count(id) > 0 &&
+ handle_dlatch(mod_it.second, mod_it.second->cells_[id]))
+ total_count++;
+ }
}
assign_map.clear();
@@ -217,5 +259,5 @@ struct OptRmdffPass : public Pass {
log("Replaced %d DFF cells.\n", total_count);
}
} OptRmdffPass;
-
+
PRIVATE_NAMESPACE_END
diff --git a/passes/opt/share.cc b/passes/opt/share.cc
index 3133cb2a6..22914eaa7 100644
--- a/passes/opt/share.cc
+++ b/passes/opt/share.cc
@@ -2,11 +2,11 @@
* 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
@@ -113,8 +113,8 @@ struct ShareWorker
static int bits_macc_port(const Macc::port_t &p, int width)
{
if (GetSize(p.in_a) == 0 || GetSize(p.in_b) == 0)
- return std::min(std::max(GetSize(p.in_a), GetSize(p.in_b)), width);
- return std::min(GetSize(p.in_a), width) * std::min(GetSize(p.in_b), width) / 2;
+ return min(max(GetSize(p.in_a), GetSize(p.in_b)), width);
+ return min(GetSize(p.in_a), width) * min(GetSize(p.in_b), width) / 2;
}
static int bits_macc(const Macc &m, int width)
@@ -224,13 +224,13 @@ struct ShareWorker
supermacc->ports.push_back(p);
}
- int score = 1000 + abs(GetSize(p1.in_a) - GetSize(p2.in_a)) * std::max(abs(GetSize(p1.in_b) - GetSize(p2.in_b)), 1);
+ int score = 1000 + abs(GetSize(p1.in_a) - GetSize(p2.in_a)) * max(abs(GetSize(p1.in_b) - GetSize(p2.in_b)), 1);
- for (int i = 0; i < std::min(GetSize(p1.in_a), GetSize(p2.in_a)); i++)
+ for (int i = 0; i < min(GetSize(p1.in_a), GetSize(p2.in_a)); i++)
if (p1.in_a[i] == p2.in_a[i] && score > 0)
score--;
- for (int i = 0; i < std::min(GetSize(p1.in_b), GetSize(p2.in_b)); i++)
+ for (int i = 0; i < min(GetSize(p1.in_b), GetSize(p2.in_b)); i++)
if (p1.in_b[i] == p2.in_b[i] && score > 0)
score--;
@@ -243,7 +243,7 @@ struct ShareWorker
Macc m1(c1), m2(c2), supermacc;
int w1 = GetSize(c1->getPort("\\Y")), w2 = GetSize(c2->getPort("\\Y"));
- int width = std::max(w1, w2);
+ int width = max(w1, w2);
m1.optimize(w1);
m2.optimize(w2);
@@ -369,7 +369,9 @@ struct ShareWorker
}
if (cell->type == "$memrd") {
- if (!cell->parameters.at("\\CLK_ENABLE").as_bool())
+ if (cell->parameters.at("\\CLK_ENABLE").as_bool())
+ continue;
+ if (config.opt_aggressive || !modwalker.sigmap(cell->getPort("\\ADDR")).is_fully_const())
shareable_cells.insert(cell);
continue;
}
@@ -387,7 +389,7 @@ struct ShareWorker
}
if (generic_ops.count(cell->type)) {
- if (config.opt_aggressive || cell->parameters.at("\\Y_WIDTH").as_int() >= 10)
+ if (config.opt_aggressive)
shareable_cells.insert(cell);
continue;
}
@@ -417,8 +419,8 @@ struct ShareWorker
int a2_width = c2->parameters.at("\\A_WIDTH").as_int();
int y2_width = c2->parameters.at("\\Y_WIDTH").as_int();
- if (std::max(a1_width, a2_width) > 2 * std::min(a1_width, a2_width)) return false;
- if (std::max(y1_width, y2_width) > 2 * std::min(y1_width, y2_width)) return false;
+ if (max(a1_width, a2_width) > 2 * min(a1_width, a2_width)) return false;
+ if (max(y1_width, y2_width) > 2 * min(y1_width, y2_width)) return false;
}
return true;
@@ -436,9 +438,9 @@ struct ShareWorker
int b2_width = c2->parameters.at("\\B_WIDTH").as_int();
int y2_width = c2->parameters.at("\\Y_WIDTH").as_int();
- if (std::max(a1_width, a2_width) > 2 * std::min(a1_width, a2_width)) return false;
- if (std::max(b1_width, b2_width) > 2 * std::min(b1_width, b2_width)) return false;
- if (std::max(y1_width, y2_width) > 2 * std::min(y1_width, y2_width)) return false;
+ if (max(a1_width, a2_width) > 2 * min(a1_width, a2_width)) return false;
+ if (max(b1_width, b2_width) > 2 * min(b1_width, b2_width)) return false;
+ if (max(y1_width, y2_width) > 2 * min(y1_width, y2_width)) return false;
}
return true;
@@ -456,15 +458,15 @@ struct ShareWorker
int b2_width = c2->parameters.at("\\B_WIDTH").as_int();
int y2_width = c2->parameters.at("\\Y_WIDTH").as_int();
- int min1_width = std::min(a1_width, b1_width);
- int max1_width = std::max(a1_width, b1_width);
+ int min1_width = min(a1_width, b1_width);
+ int max1_width = max(a1_width, b1_width);
- int min2_width = std::min(a2_width, b2_width);
- int max2_width = std::max(a2_width, b2_width);
+ int min2_width = min(a2_width, b2_width);
+ int max2_width = max(a2_width, b2_width);
- if (std::max(min1_width, min2_width) > 2 * std::min(min1_width, min2_width)) return false;
- if (std::max(max1_width, max2_width) > 2 * std::min(max1_width, max2_width)) return false;
- if (std::max(y1_width, y2_width) > 2 * std::min(y1_width, y2_width)) return false;
+ if (max(min1_width, min2_width) > 2 * min(min1_width, min2_width)) return false;
+ if (max(max1_width, max2_width) > 2 * min(max1_width, max2_width)) return false;
+ if (max(y1_width, y2_width) > 2 * min(y1_width, y2_width)) return false;
}
return true;
@@ -473,7 +475,7 @@ struct ShareWorker
if (c1->type == "$macc")
{
if (!config.opt_aggressive)
- if (share_macc(c1, c2) > 2 * std::min(bits_macc(c1), bits_macc(c2))) return false;
+ if (share_macc(c1, c2) > 2 * min(bits_macc(c1), bits_macc(c2))) return false;
return true;
}
@@ -530,8 +532,8 @@ struct ShareWorker
RTLIL::SigSpec a2 = c2->getPort("\\A");
RTLIL::SigSpec y2 = c2->getPort("\\Y");
- int a_width = std::max(a1.size(), a2.size());
- int y_width = std::max(y1.size(), y2.size());
+ int a_width = max(a1.size(), a2.size());
+ int y_width = max(y1.size(), y2.size());
a1.extend_u0(a_width, a_signed);
a2.extend_u0(a_width, a_signed);
@@ -561,11 +563,11 @@ struct ShareWorker
if (config.generic_cbin_ops.count(c1->type))
{
- int score_unflipped = std::max(c1->parameters.at("\\A_WIDTH").as_int(), c2->parameters.at("\\A_WIDTH").as_int()) +
- std::max(c1->parameters.at("\\B_WIDTH").as_int(), c2->parameters.at("\\B_WIDTH").as_int());
+ int score_unflipped = max(c1->parameters.at("\\A_WIDTH").as_int(), c2->parameters.at("\\A_WIDTH").as_int()) +
+ max(c1->parameters.at("\\B_WIDTH").as_int(), c2->parameters.at("\\B_WIDTH").as_int());
- int score_flipped = std::max(c1->parameters.at("\\A_WIDTH").as_int(), c2->parameters.at("\\B_WIDTH").as_int()) +
- std::max(c1->parameters.at("\\B_WIDTH").as_int(), c2->parameters.at("\\A_WIDTH").as_int());
+ int score_flipped = max(c1->parameters.at("\\A_WIDTH").as_int(), c2->parameters.at("\\B_WIDTH").as_int()) +
+ max(c1->parameters.at("\\B_WIDTH").as_int(), c2->parameters.at("\\A_WIDTH").as_int());
if (score_flipped < score_unflipped)
{
@@ -628,13 +630,13 @@ struct ShareWorker
RTLIL::SigSpec b2 = c2->getPort("\\B");
RTLIL::SigSpec y2 = c2->getPort("\\Y");
- int a_width = std::max(a1.size(), a2.size());
- int b_width = std::max(b1.size(), b2.size());
- int y_width = std::max(y1.size(), y2.size());
+ int a_width = max(a1.size(), a2.size());
+ int b_width = max(b1.size(), b2.size());
+ int y_width = max(y1.size(), y2.size());
if (c1->type == "$shr" && a_signed)
{
- a_width = std::max(y_width, a_width);
+ a_width = max(y_width, a_width);
if (a1.size() < y1.size()) a1.extend_u0(y1.size(), true);
if (a2.size() < y2.size()) a2.extend_u0(y2.size(), true);
@@ -706,6 +708,10 @@ struct ShareWorker
if (c1->type == "$memrd")
{
RTLIL::Cell *supercell = module->addCell(NEW_ID, c1);
+ RTLIL::SigSpec addr1 = c1->getPort("\\ADDR");
+ RTLIL::SigSpec addr2 = c2->getPort("\\ADDR");
+ if (addr1 != addr2)
+ supercell->setPort("\\ADDR", module->Mux(NEW_ID, addr2, addr1, act));
supercell_aux.insert(module->addPos(NEW_ID, supercell->getPort("\\DATA"), c2->getPort("\\DATA")));
supercell_aux.insert(supercell);
return supercell;
@@ -787,10 +793,59 @@ struct ShareWorker
return true;
}
- void optimize_activation_patterns(pool<ssc_pair_t> & /* patterns */)
+ void optimize_activation_patterns(pool<ssc_pair_t> &patterns)
{
// TODO: Remove patterns that are contained in other patterns
- // TODO: Consolidate pairs of patterns that only differ in the value for one signal bit
+
+ dict<SigSpec, pool<Const>> db;
+ bool did_something = false;
+
+ for (auto const &p : patterns)
+ {
+ auto &sig = p.first;
+ auto &val = p.second;
+ int len = GetSize(sig);
+
+ for (int i = 0; i < len; i++)
+ {
+ auto otherval = val;
+
+ if (otherval.bits[i] == State::S0)
+ otherval.bits[i] = State::S1;
+ else if (otherval.bits[i] == State::S1)
+ otherval.bits[i] = State::S0;
+ else
+ continue;
+
+ if (db[sig].count(otherval))
+ {
+ auto newsig = sig;
+ newsig.remove(i);
+
+ auto newval = val;
+ newval.bits.erase(newval.bits.begin() + i);
+
+ db[newsig].insert(newval);
+ db[sig].erase(otherval);
+
+ did_something = true;
+ goto next_pattern;
+ }
+ }
+
+ db[sig].insert(val);
+ next_pattern:;
+ }
+
+ if (!did_something)
+ return;
+
+ patterns.clear();
+ for (auto &it : db)
+ for (auto &val : it.second)
+ patterns.insert(make_pair(it.first, val));
+
+ optimize_activation_patterns(patterns);
}
const pool<ssc_pair_t> &find_cell_activation_patterns(RTLIL::Cell *cell, const char *indent)
@@ -1105,7 +1160,7 @@ struct ShareWorker
RTLIL::Cell *cell = *shareable_cells.begin();
shareable_cells.erase(cell);
- log(" Analyzing resource sharing options for %s:\n", log_id(cell));
+ log(" Analyzing resource sharing options for %s (%s):\n", log_id(cell), log_id(cell->type));
const pool<ssc_pair_t> &cell_activation_patterns = find_cell_activation_patterns(cell, " ");
RTLIL::SigSpec cell_activation_signals = bits_from_activation_patterns(cell_activation_patterns);
@@ -1138,7 +1193,7 @@ struct ShareWorker
for (auto other_cell : candidates)
{
- log(" Analyzing resource sharing with %s:\n", log_id(other_cell));
+ log(" Analyzing resource sharing with %s (%s):\n", log_id(other_cell), log_id(other_cell->type));
const pool<ssc_pair_t> &other_cell_activation_patterns = find_cell_activation_patterns(other_cell, " ");
RTLIL::SigSpec other_cell_activation_signals = bits_from_activation_patterns(other_cell_activation_patterns);
@@ -1178,8 +1233,8 @@ struct ShareWorker
optimize_activation_patterns(filtered_cell_activation_patterns);
optimize_activation_patterns(filtered_other_cell_activation_patterns);
- ezDefaultSAT ez;
- SatGen satgen(&ez, &modwalker.sigmap);
+ ezSatPtr ez;
+ SatGen satgen(ez.get(), &modwalker.sigmap);
pool<RTLIL::Cell*> sat_cells;
std::set<RTLIL::SigBit> bits_queue;
@@ -1189,13 +1244,13 @@ struct ShareWorker
for (auto &p : filtered_cell_activation_patterns) {
log(" Activation pattern for cell %s: %s = %s\n", log_id(cell), log_signal(p.first), log_signal(p.second));
- cell_active.push_back(ez.vec_eq(satgen.importSigSpec(p.first), satgen.importSigSpec(p.second)));
+ cell_active.push_back(ez->vec_eq(satgen.importSigSpec(p.first), satgen.importSigSpec(p.second)));
all_ctrl_signals.append(p.first);
}
for (auto &p : filtered_other_cell_activation_patterns) {
log(" Activation pattern for cell %s: %s = %s\n", log_id(other_cell), log_signal(p.first), log_signal(p.second));
- other_cell_active.push_back(ez.vec_eq(satgen.importSigSpec(p.first), satgen.importSigSpec(p.second)));
+ other_cell_active.push_back(ez->vec_eq(satgen.importSigSpec(p.first), satgen.importSigSpec(p.second)));
all_ctrl_signals.append(p.first);
}
@@ -1230,36 +1285,36 @@ struct ShareWorker
log(" Adding exclusive control bits: %s vs. %s\n", log_signal(it.first), log_signal(it.second));
int sub1 = satgen.importSigBit(it.first);
int sub2 = satgen.importSigBit(it.second);
- ez.assume(ez.NOT(ez.AND(sub1, sub2)));
+ ez->assume(ez->NOT(ez->AND(sub1, sub2)));
}
- if (!ez.solve(ez.expression(ez.OpOr, cell_active))) {
+ if (!ez->solve(ez->expression(ez->OpOr, cell_active))) {
log(" According to the SAT solver the cell %s is never active. Sharing is pointless, we simply remove it.\n", log_id(cell));
cells_to_remove.insert(cell);
break;
}
- if (!ez.solve(ez.expression(ez.OpOr, other_cell_active))) {
+ if (!ez->solve(ez->expression(ez->OpOr, other_cell_active))) {
log(" According to the SAT solver the cell %s is never active. Sharing is pointless, we simply remove it.\n", log_id(other_cell));
cells_to_remove.insert(other_cell);
shareable_cells.erase(other_cell);
continue;
}
- ez.non_incremental();
+ ez->non_incremental();
all_ctrl_signals.sort_and_unify();
std::vector<int> sat_model = satgen.importSigSpec(all_ctrl_signals);
std::vector<bool> sat_model_values;
- int sub1 = ez.expression(ez.OpOr, cell_active);
- int sub2 = ez.expression(ez.OpOr, other_cell_active);
- ez.assume(ez.AND(sub1, sub2));
+ int sub1 = ez->expression(ez->OpOr, cell_active);
+ int sub2 = ez->expression(ez->OpOr, other_cell_active);
+ ez->assume(ez->AND(sub1, sub2));
log(" Size of SAT problem: %d cells, %d variables, %d clauses\n",
- GetSize(sat_cells), ez.numCnfVariables(), ez.numCnfClauses());
+ GetSize(sat_cells), ez->numCnfVariables(), ez->numCnfClauses());
- if (ez.solve(sat_model, sat_model_values)) {
+ if (ez->solve(sat_model, sat_model_values)) {
log(" According to the SAT solver this pair of cells can not be shared.\n");
log(" Model from SAT solver: %s = %d'", log_signal(all_ctrl_signals), GetSize(sat_model_values));
for (int i = GetSize(sat_model_values)-1; i >= 0; i--)
@@ -1391,7 +1446,7 @@ struct SharePass : public Pass {
log("\n");
log(" -fast\n");
log(" Only consider the simple part of the control logic in SAT solving, resulting\n");
- log(" in much easier SAT problems at the cost of maybe missing some oportunities\n");
+ log(" in much easier SAT problems at the cost of maybe missing some opportunities\n");
log(" for resource sharing.\n");
log("\n");
log(" -limit N\n");
@@ -1445,7 +1500,7 @@ struct SharePass : public Pass {
config.generic_other_ops.insert("$alu");
config.generic_other_ops.insert("$macc");
- log_header("Executing SHARE pass (SAT-based resource sharing).\n");
+ log_header(design, "Executing SHARE pass (SAT-based resource sharing).\n");
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++) {
diff --git a/passes/opt/wreduce.cc b/passes/opt/wreduce.cc
index 1609a8be7..333541eab 100644
--- a/passes/opt/wreduce.cc
+++ b/passes/opt/wreduce.cc
@@ -2,11 +2,11 @@
* 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
@@ -37,7 +37,7 @@ struct WreduceConfig
"$and", "$or", "$xor", "$xnor",
"$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx",
"$lt", "$le", "$eq", "$ne", "$eqx", "$nex", "$ge", "$gt",
- "$add", "$sub", // "$mul", "$div", "$mod", "$pow",
+ "$add", "$sub", "$mul", // "$div", "$mod", "$pow",
"$mux", "$pmux"
});
}
@@ -51,6 +51,7 @@ struct WreduceWorker
std::set<Cell*, IdString::compare_ptr_by_name<Cell>> work_queue_cells;
std::set<SigBit> work_queue_bits;
+ pool<SigBit> keep_bits;
WreduceWorker(WreduceConfig *config, Module *module) :
config(config), module(module), mi(module) { }
@@ -65,10 +66,13 @@ struct WreduceWorker
SigSpec sig_y = mi.sigmap(cell->getPort("\\Y"));
std::vector<SigBit> bits_removed;
+ if (sig_y.has_const())
+ return;
+
for (int i = GetSize(sig_y)-1; i >= 0; i--)
{
auto info = mi.query(sig_y[i]);
- if (!info->is_output && GetSize(info->ports) <= 1) {
+ if (!info->is_output && GetSize(info->ports) <= 1 && !keep_bits.count(mi.sigmap(sig_y[i]))) {
bits_removed.push_back(Sx);
continue;
}
@@ -172,6 +176,11 @@ struct WreduceWorker
if (cell->type.in("$mux", "$pmux"))
return run_cell_mux(cell);
+ SigSpec sig = mi.sigmap(cell->getPort("\\Y"));
+
+ if (sig.has_const())
+ return;
+
// Reduce size of ports A and B based on constant input bits and size of output port
@@ -179,8 +188,8 @@ struct WreduceWorker
int max_port_b_size = cell->hasPort("\\B") ? GetSize(cell->getPort("\\B")) : -1;
if (cell->type.in("$not", "$pos", "$neg", "$and", "$or", "$xor", "$add", "$sub")) {
- max_port_a_size = std::min(max_port_a_size, GetSize(cell->getPort("\\Y")));
- max_port_b_size = std::min(max_port_b_size, GetSize(cell->getPort("\\Y")));
+ max_port_a_size = min(max_port_a_size, GetSize(sig));
+ max_port_b_size = min(max_port_b_size, GetSize(sig));
}
bool port_a_signed = false;
@@ -192,10 +201,33 @@ struct WreduceWorker
if (max_port_b_size >= 0)
run_reduce_inport(cell, 'B', max_port_b_size, port_b_signed, did_something);
+ if (cell->hasPort("\\A") && cell->hasPort("\\B") && port_a_signed && port_b_signed) {
+ SigSpec sig_a = mi.sigmap(cell->getPort("\\A")), sig_b = mi.sigmap(cell->getPort("\\B"));
+ if (GetSize(sig_a) > 0 && sig_a[GetSize(sig_a)-1] == State::S0 &&
+ GetSize(sig_b) > 0 && sig_b[GetSize(sig_b)-1] == State::S0) {
+ log("Converting cell %s.%s (%s) from signed to unsigned.\n",
+ log_id(module), log_id(cell), log_id(cell->type));
+ cell->setParam("\\A_SIGNED", 0);
+ cell->setParam("\\B_SIGNED", 0);
+ port_a_signed = false;
+ port_b_signed = false;
+ did_something = true;
+ }
+ }
- // Reduce size of port Y based on sizes for A and B and unused bits in Y
+ if (cell->hasPort("\\A") && !cell->hasPort("\\B") && port_a_signed) {
+ SigSpec sig_a = mi.sigmap(cell->getPort("\\A"));
+ if (GetSize(sig_a) > 0 && sig_a[GetSize(sig_a)-1] == State::S0) {
+ log("Converting cell %s.%s (%s) from signed to unsigned.\n",
+ log_id(module), log_id(cell), log_id(cell->type));
+ cell->setParam("\\A_SIGNED", 0);
+ port_a_signed = false;
+ did_something = true;
+ }
+ }
- SigSpec sig = mi.sigmap(cell->getPort("\\Y"));
+
+ // Reduce size of port Y based on sizes for A and B and unused bits in Y
int bits_removed = 0;
if (port_a_signed && cell->type == "$shr") {
@@ -221,7 +253,7 @@ struct WreduceWorker
if (cell->hasPort("\\A")) a_size = GetSize(cell->getPort("\\A"));
if (cell->hasPort("\\B")) b_size = GetSize(cell->getPort("\\B"));
- int max_y_size = std::max(a_size, b_size);
+ int max_y_size = max(a_size, b_size);
if (cell->type == "$add")
max_y_size++;
@@ -265,6 +297,11 @@ struct WreduceWorker
void run()
{
+ for (auto w : module->wires())
+ if (w->get_bool_attribute("\\keep"))
+ for (auto bit : mi.sigmap(w))
+ keep_bits.insert(bit);
+
for (auto c : module->selected_cells())
work_queue_cells.insert(c);
@@ -281,6 +318,10 @@ struct WreduceWorker
work_queue_cells.insert(port.cell);
}
+ pool<SigSpec> complete_wires;
+ for (auto w : module->wires())
+ complete_wires.insert(mi.sigmap(w));
+
for (auto w : module->selected_wires())
{
int unused_top_bits = 0;
@@ -296,19 +337,22 @@ struct WreduceWorker
unused_top_bits++;
}
- if (0 < unused_top_bits && unused_top_bits < GetSize(w)) {
- log("Removed top %d bits (of %d) from wire %s.%s.\n", unused_top_bits, GetSize(w), log_id(module), log_id(w));
- Wire *nw = module->addWire(NEW_ID, w);
- nw->width = GetSize(w) - unused_top_bits;
- module->connect(nw, SigSpec(w).extract(0, GetSize(nw)));
- module->swap_names(w, nw);
- }
+ if (unused_top_bits == 0 || unused_top_bits == GetSize(w))
+ continue;
+
+ if (complete_wires[mi.sigmap(w).extract(0, GetSize(w) - unused_top_bits)])
+ continue;
+
+ log("Removed top %d bits (of %d) from wire %s.%s.\n", unused_top_bits, GetSize(w), log_id(module), log_id(w));
+ Wire *nw = module->addWire(NEW_ID, GetSize(w) - unused_top_bits);
+ module->connect(nw, SigSpec(w).extract(0, GetSize(nw)));
+ module->swap_names(w, nw);
}
}
};
struct WreducePass : public Pass {
- WreducePass() : Pass("wreduce", "reduce the word size of operations is possible") { }
+ WreducePass() : Pass("wreduce", "reduce the word size of operations if possible") { }
virtual void help()
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
@@ -327,7 +371,7 @@ struct WreducePass : public Pass {
{
WreduceConfig config;
- log_header("Executing WREDUCE pass (reducing word size of cells).\n");
+ log_header(design, "Executing WREDUCE pass (reducing word size of cells).\n");
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++) {
@@ -340,6 +384,19 @@ struct WreducePass : public Pass {
if (module->has_processes_warn())
continue;
+ for (auto c : module->selected_cells())
+ if (c->type.in("$reduce_and", "$reduce_or", "$reduce_xor", "$reduce_xnor", "$reduce_bool",
+ "$lt", "$le", "$eq", "$ne", "$eqx", "$nex", "$ge", "$gt",
+ "$logic_not", "$logic_and", "$logic_or") && GetSize(c->getPort("\\Y")) > 1) {
+ SigSpec sig = c->getPort("\\Y");
+ if (!sig.has_const()) {
+ c->setPort("\\Y", sig[0]);
+ c->setParam("\\Y_WIDTH", 1);
+ sig.remove(0);
+ module->connect(sig, Const(0, GetSize(sig)));
+ }
+ }
+
WreduceWorker worker(&config, module);
worker.run();
}
diff --git a/passes/proc/Makefile.inc b/passes/proc/Makefile.inc
index dfbc78eae..397fe46a1 100644
--- a/passes/proc/Makefile.inc
+++ b/passes/proc/Makefile.inc
@@ -5,5 +5,6 @@ OBJS += passes/proc/proc_rmdead.o
OBJS += passes/proc/proc_init.o
OBJS += passes/proc/proc_arst.o
OBJS += passes/proc/proc_mux.o
+OBJS += passes/proc/proc_dlatch.o
OBJS += passes/proc/proc_dff.o
diff --git a/passes/proc/proc.cc b/passes/proc/proc.cc
index 30efbab4b..d5366f266 100644
--- a/passes/proc/proc.cc
+++ b/passes/proc/proc.cc
@@ -2,11 +2,11 @@
* 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
@@ -40,22 +40,29 @@ struct ProcPass : public Pass {
log(" proc_init\n");
log(" proc_arst\n");
log(" proc_mux\n");
+ log(" proc_dlatch\n");
log(" proc_dff\n");
log(" proc_clean\n");
log("\n");
- log("This replaces the processes in the design with multiplexers and flip-flops.\n");
+ log("This replaces the processes in the design with multiplexers,\n");
+ log("flip-flops and latches.\n");
log("\n");
log("The following options are supported:\n");
log("\n");
log(" -global_arst [!]<netname>\n");
log(" This option is passed through to proc_arst.\n");
log("\n");
+ log(" -ifx\n");
+ log(" This option is passed through to proc_mux. proc_rmdead is not\n");
+ log(" executed in -ifx mode.\n");
+ log("\n");
}
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
{
std::string global_arst;
+ bool ifxmode = false;
- log_header("Executing PROC pass (convert processes to netlists).\n");
+ log_header(design, "Executing PROC pass (convert processes to netlists).\n");
log_push();
size_t argidx;
@@ -65,23 +72,29 @@ struct ProcPass : public Pass {
global_arst = args[++argidx];
continue;
}
+ if (args[argidx] == "-ifx") {
+ ifxmode = true;
+ continue;
+ }
break;
}
extra_args(args, argidx, design);
Pass::call(design, "proc_clean");
- Pass::call(design, "proc_rmdead");
+ if (!ifxmode)
+ Pass::call(design, "proc_rmdead");
Pass::call(design, "proc_init");
if (global_arst.empty())
Pass::call(design, "proc_arst");
else
Pass::call(design, "proc_arst -global_arst " + global_arst);
- Pass::call(design, "proc_mux");
+ Pass::call(design, ifxmode ? "proc_mux -ifx" : "proc_mux");
+ Pass::call(design, "proc_dlatch");
Pass::call(design, "proc_dff");
Pass::call(design, "proc_clean");
log_pop();
}
} ProcPass;
-
+
PRIVATE_NAMESPACE_END
diff --git a/passes/proc/proc_arst.cc b/passes/proc/proc_arst.cc
index 27c6b3bcf..216b00ddd 100644
--- a/passes/proc/proc_arst.cc
+++ b/passes/proc/proc_arst.cc
@@ -2,11 +2,11 @@
* 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
@@ -226,7 +226,7 @@ struct ProcArstPass : public Pass {
std::string global_arst;
bool global_arst_neg = false;
- log_header("Executing PROC_ARST pass (detect async resets in processes).\n");
+ log_header(design, "Executing PROC_ARST pass (detect async resets in processes).\n");
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++)
@@ -244,6 +244,7 @@ struct ProcArstPass : public Pass {
}
extra_args(args, argidx, design);
+ pool<Wire*> delete_initattr_wires;
for (auto mod : design->modules())
if (design->selected(mod)) {
@@ -265,6 +266,7 @@ struct ProcArstPass : public Pass {
value.extend_u0(chunk.wire->width, false);
arst_sig.append(chunk);
arst_val.append(value.extract(chunk.offset, chunk.width));
+ delete_initattr_wires.insert(chunk.wire);
}
if (arst_sig.size()) {
log("Added global reset to process %s: %s <- %s\n",
@@ -281,7 +283,10 @@ struct ProcArstPass : public Pass {
}
}
}
+
+ for (auto wire : delete_initattr_wires)
+ wire->attributes.erase("\\init");
}
} ProcArstPass;
-
+
PRIVATE_NAMESPACE_END
diff --git a/passes/proc/proc_clean.cc b/passes/proc/proc_clean.cc
index 82716cd06..7dbabc211 100644
--- a/passes/proc/proc_clean.cc
+++ b/passes/proc/proc_clean.cc
@@ -2,11 +2,11 @@
* 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
@@ -156,7 +156,7 @@ struct ProcCleanPass : public Pass {
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
{
int total_count = 0;
- log_header("Executing PROC_CLEAN pass (remove empty switches from decision trees).\n");
+ log_header(design, "Executing PROC_CLEAN pass (remove empty switches from decision trees).\n");
extra_args(args, 1, design);
@@ -183,5 +183,5 @@ struct ProcCleanPass : public Pass {
log("Cleaned up %d empty switch%s.\n", total_count, total_count == 1 ? "" : "es");
}
} ProcCleanPass;
-
+
PRIVATE_NAMESPACE_END
diff --git a/passes/proc/proc_dff.cc b/passes/proc/proc_dff.cc
index 76842da6b..f532990c2 100644
--- a/passes/proc/proc_dff.cc
+++ b/passes/proc/proc_dff.cc
@@ -2,11 +2,11 @@
* 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
@@ -369,7 +369,7 @@ struct ProcDffPass : public Pass {
}
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
{
- log_header("Executing PROC_DFF pass (convert process syncs to FFs).\n");
+ log_header(design, "Executing PROC_DFF pass (convert process syncs to FFs).\n");
extra_args(args, 1, design);
@@ -382,5 +382,5 @@ struct ProcDffPass : public Pass {
}
}
} ProcDffPass;
-
+
PRIVATE_NAMESPACE_END
diff --git a/passes/proc/proc_dlatch.cc b/passes/proc/proc_dlatch.cc
new file mode 100644
index 000000000..6621afd33
--- /dev/null
+++ b/passes/proc/proc_dlatch.cc
@@ -0,0 +1,449 @@
+/*
+ * 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/sigtools.h"
+#include "kernel/consteval.h"
+#include "kernel/log.h"
+#include <sstream>
+#include <stdlib.h>
+#include <stdio.h>
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct proc_dlatch_db_t
+{
+ Module *module;
+ SigMap sigmap;
+
+ pool<Cell*> generated_dlatches;
+ dict<Cell*, vector<SigBit>> mux_srcbits;
+ dict<SigBit, pair<Cell*, int>> mux_drivers;
+ dict<SigBit, int> sigusers;
+
+ proc_dlatch_db_t(Module *module) : module(module), sigmap(module)
+ {
+ for (auto cell : module->cells())
+ {
+ if (cell->type.in("$mux", "$pmux"))
+ {
+ auto sig_y = sigmap(cell->getPort("\\Y"));
+ for (int i = 0; i < GetSize(sig_y); i++)
+ mux_drivers[sig_y[i]] = pair<Cell*, int>(cell, i);
+
+ pool<SigBit> mux_srcbits_pool;
+ for (auto bit : sigmap(cell->getPort("\\A")))
+ mux_srcbits_pool.insert(bit);
+ for (auto bit : sigmap(cell->getPort("\\B")))
+ mux_srcbits_pool.insert(bit);
+
+ vector<SigBit> mux_srcbits_vec;
+ for (auto bit : mux_srcbits_pool)
+ if (bit.wire != nullptr)
+ mux_srcbits_vec.push_back(bit);
+
+ mux_srcbits[cell].swap(mux_srcbits_vec);
+ }
+
+ for (auto &conn : cell->connections())
+ if (!cell->known() || cell->input(conn.first))
+ for (auto bit : sigmap(conn.second))
+ sigusers[bit]++;
+ }
+
+ for (auto wire : module->wires())
+ if (wire->port_input)
+ for (auto bit : sigmap(wire))
+ sigusers[bit]++;
+ }
+
+ bool quickcheck(const SigSpec &haystack, const SigSpec &needle)
+ {
+ pool<SigBit> haystack_bits = sigmap(haystack).to_sigbit_pool();
+ pool<SigBit> needle_bits = sigmap(needle).to_sigbit_pool();
+
+ pool<Cell*> cells_queue, cells_visited;
+ pool<SigBit> bits_queue, bits_visited;
+
+ bits_queue = haystack_bits;
+ while (!bits_queue.empty())
+ {
+ for (auto &bit : bits_queue) {
+ auto it = mux_drivers.find(bit);
+ if (it != mux_drivers.end())
+ if (!cells_visited.count(it->second.first))
+ cells_queue.insert(it->second.first);
+ bits_visited.insert(bit);
+ }
+
+ bits_queue.clear();
+
+ for (auto c : cells_queue) {
+ for (auto bit : mux_srcbits[c]) {
+ if (needle_bits.count(bit))
+ return true;
+ if (!bits_visited.count(bit))
+ bits_queue.insert(bit);
+ }
+ }
+
+ cells_queue.clear();
+ }
+
+ return false;
+ }
+
+ struct rule_node_t
+ {
+ // a node is true if "signal" equals "match" and [any
+ // of the child nodes is true or "children" is empty]
+ SigBit signal, match;
+ vector<int> children;
+
+ bool operator==(const rule_node_t &other) const {
+ return signal == other.signal && match == other.match && children == other.children;
+ }
+
+ unsigned int hash() const {
+ unsigned int h = mkhash_init;
+ mkhash(h, signal.hash());
+ mkhash(h, match.hash());
+ for (auto i : children) mkhash(h, i);
+ return h;
+ }
+ };
+
+ enum tf_node_types_t : int {
+ true_node = 1,
+ false_node = 2
+ };
+
+ idict<rule_node_t, 3> rules_db;
+ dict<int, SigBit> rules_sig;
+
+ int make_leaf(SigBit signal, SigBit match)
+ {
+ rule_node_t node;
+ node.signal = signal;
+ node.match = match;
+ return rules_db(node);
+ }
+
+ int make_inner(SigBit signal, SigBit match, int child)
+ {
+ rule_node_t node;
+ node.signal = signal;
+ node.match = match;
+ node.children.push_back(child);
+ return rules_db(node);
+ }
+
+ int make_inner(const pool<int> &children)
+ {
+ rule_node_t node;
+ node.signal = State::S0;
+ node.match = State::S0;
+ node.children = vector<int>(children.begin(), children.end());
+ std::sort(node.children.begin(), node.children.end());
+ return rules_db(node);
+ }
+
+ int find_mux_feedback(SigBit haystack, SigBit needle, bool set_undef)
+ {
+ if (sigusers[haystack] > 1)
+ set_undef = false;
+
+ if (haystack == needle)
+ return true_node;
+
+ auto it = mux_drivers.find(haystack);
+ if (it == mux_drivers.end())
+ return false_node;
+
+ Cell *cell = it->second.first;
+ int index = it->second.second;
+
+ SigSpec sig_a = sigmap(cell->getPort("\\A"));
+ SigSpec sig_b = sigmap(cell->getPort("\\B"));
+ SigSpec sig_s = sigmap(cell->getPort("\\S"));
+ int width = GetSize(sig_a);
+
+ pool<int> children;
+
+ int n = find_mux_feedback(sig_a[index], needle, set_undef);
+ if (n != false_node) {
+ if (set_undef && sig_a[index] == needle) {
+ SigSpec sig = cell->getPort("\\A");
+ sig[index] = State::Sx;
+ cell->setPort("\\A", sig);
+ }
+ for (int i = 0; i < GetSize(sig_s); i++)
+ n = make_inner(sig_s[i], State::S0, n);
+ children.insert(n);
+ }
+
+ for (int i = 0; i < GetSize(sig_s); i++) {
+ n = find_mux_feedback(sig_b[i*width + index], needle, set_undef);
+ if (n != false_node) {
+ if (set_undef && sig_b[i*width + index] == needle) {
+ SigSpec sig = cell->getPort("\\B");
+ sig[i*width + index] = State::Sx;
+ cell->setPort("\\B", sig);
+ }
+ children.insert(make_inner(sig_s[i], State::S1, n));
+ }
+ }
+
+ if (children.empty())
+ return false_node;
+
+ return make_inner(children);
+ }
+
+ SigBit make_hold(int n)
+ {
+ if (n == true_node)
+ return State::S1;
+
+ if (n == false_node)
+ return State::S0;
+
+ if (rules_sig.count(n))
+ return rules_sig.at(n);
+
+ const rule_node_t &rule = rules_db[n];
+ SigSpec and_bits;
+
+ if (rule.signal != rule.match) {
+ if (rule.match == State::S1)
+ and_bits.append(rule.signal);
+ else if (rule.match == State::S0)
+ and_bits.append(module->Not(NEW_ID, rule.signal));
+ else
+ and_bits.append(module->Eq(NEW_ID, rule.signal, rule.match));
+ }
+
+ if (!rule.children.empty()) {
+ SigSpec or_bits;
+ for (int k : rule.children)
+ or_bits.append(make_hold(k));
+ and_bits.append(module->ReduceOr(NEW_ID, or_bits));
+ }
+
+ if (GetSize(and_bits) == 2)
+ and_bits = module->And(NEW_ID, and_bits[0], and_bits[1]);
+ log_assert(GetSize(and_bits) == 1);
+
+ rules_sig[n] = and_bits[0];
+ return and_bits[0];
+ }
+
+ void fixup_mux(Cell *cell)
+ {
+ SigSpec sig_a = cell->getPort("\\A");
+ SigSpec sig_b = cell->getPort("\\B");
+ SigSpec sig_s = cell->getPort("\\S");
+ SigSpec sig_any_valid_b;
+
+ SigSpec sig_new_b, sig_new_s;
+ for (int i = 0; i < GetSize(sig_s); i++) {
+ SigSpec b = sig_b.extract(i*GetSize(sig_a), GetSize(sig_a));
+ if (!b.is_fully_undef()) {
+ sig_any_valid_b = b;
+ sig_new_b.append(b);
+ sig_new_s.append(sig_s[i]);
+ }
+ }
+
+ if (sig_new_s.empty()) {
+ sig_new_b = sig_a;
+ sig_new_s = State::S0;
+ }
+
+ if (sig_a.is_fully_undef() && !sig_any_valid_b.empty())
+ cell->setPort("\\A", sig_any_valid_b);
+
+ if (GetSize(sig_new_s) == 1) {
+ cell->type = "$mux";
+ cell->unsetParam("\\S_WIDTH");
+ } else {
+ cell->type = "$pmux";
+ cell->setParam("\\S_WIDTH", GetSize(sig_new_s));
+ }
+
+ cell->setPort("\\B", sig_new_b);
+ cell->setPort("\\S", sig_new_s);
+ }
+
+ void fixup_muxes()
+ {
+ pool<Cell*> visited, queue;
+ dict<Cell*, pool<SigBit>> upstream_cell2net;
+ dict<SigBit, pool<Cell*>> upstream_net2cell;
+
+ CellTypes ct;
+ ct.setup_internals();
+
+ for (auto cell : module->cells())
+ for (auto conn : cell->connections()) {
+ if (cell->input(conn.first))
+ for (auto bit : sigmap(conn.second))
+ upstream_cell2net[cell].insert(bit);
+ if (cell->output(conn.first))
+ for (auto bit : sigmap(conn.second))
+ upstream_net2cell[bit].insert(cell);
+ }
+
+ queue = generated_dlatches;
+ while (!queue.empty())
+ {
+ pool<Cell*> next_queue;
+
+ for (auto cell : queue) {
+ if (cell->type.in("$mux", "$pmux"))
+ fixup_mux(cell);
+ for (auto bit : upstream_cell2net[cell])
+ for (auto cell : upstream_net2cell[bit])
+ next_queue.insert(cell);
+ visited.insert(cell);
+ }
+
+ queue.clear();
+ for (auto cell : next_queue) {
+ if (!visited.count(cell) && ct.cell_known(cell->type))
+ queue.insert(cell);
+ }
+ }
+ }
+};
+
+void proc_dlatch(proc_dlatch_db_t &db, RTLIL::Process *proc)
+{
+ std::vector<RTLIL::SyncRule*> new_syncs;
+ RTLIL::SigSig latches_bits, nolatches_bits;
+ dict<SigBit, SigBit> latches_out_in;
+ dict<SigBit, int> latches_hold;
+
+ for (auto sr : proc->syncs)
+ {
+ if (sr->type != RTLIL::SyncType::STa) {
+ new_syncs.push_back(sr);
+ continue;
+ }
+
+ for (auto ss : sr->actions)
+ {
+ db.sigmap.apply(ss.first);
+ db.sigmap.apply(ss.second);
+
+ if (!db.quickcheck(ss.second, ss.first)) {
+ nolatches_bits.first.append(ss.first);
+ nolatches_bits.second.append(ss.second);
+ continue;
+ }
+
+ for (int i = 0; i < GetSize(ss.first); i++)
+ latches_out_in[ss.first[i]] = ss.second[i];
+ }
+
+ delete sr;
+ }
+
+ latches_out_in.sort();
+ for (auto &it : latches_out_in) {
+ int n = db.find_mux_feedback(it.second, it.first, true);
+ if (n == db.false_node) {
+ nolatches_bits.first.append(it.first);
+ nolatches_bits.second.append(it.second);
+ } else {
+ latches_bits.first.append(it.first);
+ latches_bits.second.append(it.second);
+ latches_hold[it.first] = n;
+ }
+ }
+
+ int offset = 0;
+ for (auto chunk : nolatches_bits.first.chunks()) {
+ SigSpec lhs = chunk, rhs = nolatches_bits.second.extract(offset, chunk.width);
+ log("No latch inferred for signal `%s.%s' from process `%s.%s'.\n",
+ db.module->name.c_str(), log_signal(lhs), db.module->name.c_str(), proc->name.c_str());
+ db.module->connect(lhs, rhs);
+ offset += chunk.width;
+ }
+
+ offset = 0;
+ while (offset < GetSize(latches_bits.first))
+ {
+ int width = 1;
+ int n = latches_hold[latches_bits.first[offset]];
+ Wire *w = latches_bits.first[offset].wire;
+
+ if (w != nullptr)
+ {
+ while (offset+width < GetSize(latches_bits.first) &&
+ n == latches_hold[latches_bits.first[offset+width]] &&
+ w == latches_bits.first[offset+width].wire)
+ width++;
+
+ SigSpec lhs = latches_bits.first.extract(offset, width);
+ SigSpec rhs = latches_bits.second.extract(offset, width);
+
+ Cell *cell = db.module->addDlatch(NEW_ID, db.module->Not(NEW_ID, db.make_hold(n)), rhs, lhs);
+ db.generated_dlatches.insert(cell);
+
+ log("Latch inferred for signal `%s.%s' from process `%s.%s': %s\n",
+ db.module->name.c_str(), log_signal(lhs), db.module->name.c_str(), proc->name.c_str(), log_id(cell));
+ }
+
+ offset += width;
+ }
+
+ new_syncs.swap(proc->syncs);
+}
+
+struct ProcDlatchPass : public Pass {
+ ProcDlatchPass() : Pass("proc_dlatch", "extract latches from processes") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" proc_dlatch [selection]\n");
+ log("\n");
+ log("This pass identifies latches in the processes and converts them to\n");
+ log("d-type latches.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ log_header(design, "Executing PROC_DLATCH pass (convert process syncs to latches).\n");
+
+ extra_args(args, 1, design);
+
+ for (auto module : design->selected_modules()) {
+ proc_dlatch_db_t db(module);
+ for (auto &proc_it : module->processes)
+ if (design->selected(module, proc_it.second))
+ proc_dlatch(db, proc_it.second);
+ db.fixup_muxes();
+ }
+ }
+} ProcDlatchPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/proc/proc_init.cc b/passes/proc/proc_init.cc
index dff68159f..0c8fb83dc 100644
--- a/passes/proc/proc_init.cc
+++ b/passes/proc/proc_init.cc
@@ -2,11 +2,11 @@
* 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
@@ -61,13 +61,28 @@ void proc_init(RTLIL::Module *mod, RTLIL::Process *proc)
log_cmd_error("Failed to get a constant init value for %s: %s\n", log_signal(lhs), log_signal(rhs));
int offset = 0;
- for (auto &lhs_c : lhs.chunks()) {
- if (lhs_c.wire != NULL) {
- RTLIL::SigSpec value = rhs.extract(offset, lhs_c.width);
- if (value.size() != lhs_c.wire->width)
- log_cmd_error("Init value is not for the entire wire: %s = %s\n", log_signal(lhs_c), log_signal(value));
- log(" Setting init value: %s = %s\n", log_signal(lhs_c.wire), log_signal(value));
- lhs_c.wire->attributes["\\init"] = value.as_const();
+ for (auto &lhs_c : lhs.chunks())
+ {
+ if (lhs_c.wire != nullptr)
+ {
+ SigSpec valuesig = rhs.extract(offset, lhs_c.width);
+ if (!valuesig.is_fully_const())
+ log_cmd_error("Non-const initialization value: %s = %s\n", log_signal(lhs_c), log_signal(valuesig));
+
+ Const value = valuesig.as_const();
+ Const &wireinit = lhs_c.wire->attributes["\\init"];
+
+ while (GetSize(wireinit.bits) < lhs_c.wire->width)
+ wireinit.bits.push_back(State::Sx);
+
+ for (int i = 0; i < lhs_c.width; i++) {
+ auto &initbit = wireinit.bits[i + lhs_c.offset];
+ if (initbit != State::Sx && initbit != value[i])
+ log_cmd_error("Conflicting initialization values for %s.\n", log_signal(lhs_c));
+ initbit = value[i];
+ }
+
+ log(" Set init value: %s = %s\n", log_signal(lhs_c.wire), log_signal(wireinit));
}
offset += lhs_c.width;
}
@@ -93,14 +108,14 @@ struct ProcInitPass : public Pass {
log("\n");
log(" proc_init [selection]\n");
log("\n");
- log("This pass extracts the 'init' actions from processes (generated from verilog\n");
+ log("This pass extracts the 'init' actions from processes (generated from Verilog\n");
log("'initial' blocks) and sets the initial value to the 'init' attribute on the\n");
log("respective wire.\n");
log("\n");
}
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
{
- log_header("Executing PROC_INIT pass (extract init attributes).\n");
+ log_header(design, "Executing PROC_INIT pass (extract init attributes).\n");
extra_args(args, 1, design);
@@ -111,5 +126,5 @@ struct ProcInitPass : public Pass {
proc_init(mod, proc_it.second);
}
} ProcInitPass;
-
+
PRIVATE_NAMESPACE_END
diff --git a/passes/proc/proc_mux.cc b/passes/proc/proc_mux.cc
index 4aa1aab54..57e131ca5 100644
--- a/passes/proc/proc_mux.cc
+++ b/passes/proc/proc_mux.cc
@@ -2,11 +2,11 @@
* 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
@@ -27,37 +27,123 @@
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
-RTLIL::SigSpec find_any_lvalue(const RTLIL::CaseRule *cs)
+struct SigSnippets
{
- for (auto &action : cs->actions) {
- if (action.first.size())
- return action.first;
- }
+ idict<SigSpec> sigidx;
+ dict<SigBit, int> bit2snippet;
+ pool<int> snippets;
- for (auto sw : cs->switches)
- for (auto cs2 : sw->cases) {
- RTLIL::SigSpec sig = find_any_lvalue(cs2);
- if (sig.size())
- return sig;
+ void insert(SigSpec sig)
+ {
+ if (sig.empty())
+ return;
+
+ int key = sigidx(sig);
+ if (snippets.count(key))
+ return;
+
+ SigSpec new_sig;
+
+ for (int i = 0; i < GetSize(sig); i++)
+ {
+ int other_key = bit2snippet.at(sig[i], -1);
+
+ if (other_key < 0) {
+ new_sig.append(sig[i]);
+ continue;
+ }
+
+ if (!new_sig.empty()) {
+ int new_key = sigidx(new_sig);
+ snippets.insert(new_key);
+ for (auto bit : new_sig)
+ bit2snippet[bit] = new_key;
+ new_sig = SigSpec();
+ }
+
+ SigSpec other_sig = sigidx[other_key];
+ int k = 0, n = 1;
+
+ while (other_sig[k] != sig[i]) {
+ k++;
+ log_assert(k < GetSize(other_sig));
+ }
+
+ while (i+n < GetSize(sig) && k+n < GetSize(other_sig) && sig[i+n] == other_sig[k+n])
+ n++;
+
+ SigSpec sig1 = other_sig.extract(0, k);
+ SigSpec sig2 = other_sig.extract(k, n);
+ SigSpec sig3 = other_sig.extract(k+n, GetSize(other_sig)-k-n);
+
+ for (auto bit : other_sig)
+ bit2snippet.erase(bit);
+ snippets.erase(other_key);
+
+ insert(sig1);
+ insert(sig2);
+ insert(sig3);
+
+ i += n-1;
+ }
+
+ if (!new_sig.empty()) {
+ int new_key = sigidx(new_sig);
+ snippets.insert(new_key);
+ for (auto bit : new_sig)
+ bit2snippet[bit] = new_key;
+ }
}
- return RTLIL::SigSpec();
-}
+ void insert(const RTLIL::CaseRule *cs)
+ {
+ for (auto &action : cs->actions)
+ insert(action.first);
-void extract_core_signal(const RTLIL::CaseRule *cs, RTLIL::SigSpec &sig)
+ for (auto sw : cs->switches)
+ for (auto cs2 : sw->cases)
+ insert(cs2);
+ }
+};
+
+struct SnippetSwCache
{
- for (auto &action : cs->actions) {
- RTLIL::SigSpec lvalue = action.first.extract(sig);
- if (lvalue.size())
- sig = lvalue;
+ dict<RTLIL::SwitchRule*, pool<int>, hash_ptr_ops> cache;
+ const SigSnippets *snippets;
+ int current_snippet;
+
+ bool check(RTLIL::SwitchRule *sw)
+ {
+ return cache[sw].count(current_snippet) != 0;
}
- for (auto sw : cs->switches)
- for (auto cs2 : sw->cases)
- extract_core_signal(cs2, sig);
-}
+ void insert(const RTLIL::CaseRule *cs, vector<RTLIL::SwitchRule*> &sw_stack)
+ {
+ for (auto &action : cs->actions)
+ for (auto bit : action.first) {
+ int sn = snippets->bit2snippet.at(bit, -1);
+ if (sn < 0)
+ continue;
+ for (auto sw : sw_stack)
+ cache[sw].insert(sn);
+ }
+
+ for (auto sw : cs->switches) {
+ sw_stack.push_back(sw);
+ for (auto cs2 : sw->cases)
+ insert(cs2, sw_stack);
+ sw_stack.pop_back();
+ }
+ }
-RTLIL::SigSpec gen_cmp(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::vector<RTLIL::SigSpec> &compare, RTLIL::SwitchRule *sw)
+ void insert(const RTLIL::CaseRule *cs)
+ {
+ vector<RTLIL::SwitchRule*> sw_stack;
+ insert(cs, sw_stack);
+ }
+};
+
+RTLIL::SigSpec gen_cmp(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::vector<RTLIL::SigSpec> &compare, RTLIL::SwitchRule *sw, bool ifxmode)
{
std::stringstream sstr;
sstr << "$procmux$" << (autoidx++);
@@ -78,14 +164,14 @@ RTLIL::SigSpec gen_cmp(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const s
if (comp.size() == 0)
return RTLIL::SigSpec();
- if (sig.size() == 1 && comp == RTLIL::SigSpec(1,1))
+ if (sig.size() == 1 && comp == RTLIL::SigSpec(1,1) && !ifxmode)
{
mod->connect(RTLIL::SigSig(RTLIL::SigSpec(cmp_wire, cmp_wire->width++), sig));
}
else
{
// create compare cell
- RTLIL::Cell *eq_cell = mod->addCell(stringf("%s_CMP%d", sstr.str().c_str(), cmp_wire->width), "$eq");
+ RTLIL::Cell *eq_cell = mod->addCell(stringf("%s_CMP%d", sstr.str().c_str(), cmp_wire->width), ifxmode ? "$eqx" : "$eq");
eq_cell->attributes = sw->attributes;
eq_cell->parameters["\\A_SIGNED"] = RTLIL::Const(0);
@@ -125,7 +211,7 @@ RTLIL::SigSpec gen_cmp(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const s
return RTLIL::SigSpec(ctrl_wire);
}
-RTLIL::SigSpec gen_mux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::vector<RTLIL::SigSpec> &compare, RTLIL::SigSpec when_signal, RTLIL::SigSpec else_signal, RTLIL::Cell *&last_mux_cell, RTLIL::SwitchRule *sw)
+RTLIL::SigSpec gen_mux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::vector<RTLIL::SigSpec> &compare, RTLIL::SigSpec when_signal, RTLIL::SigSpec else_signal, RTLIL::Cell *&last_mux_cell, RTLIL::SwitchRule *sw, bool ifxmode)
{
log_assert(when_signal.size() == else_signal.size());
@@ -137,7 +223,7 @@ RTLIL::SigSpec gen_mux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const s
return when_signal;
// compare results
- RTLIL::SigSpec ctrl_sig = gen_cmp(mod, signal, compare, sw);
+ RTLIL::SigSpec ctrl_sig = gen_cmp(mod, signal, compare, sw, ifxmode);
if (ctrl_sig.size() == 0)
return when_signal;
log_assert(ctrl_sig.size() == 1);
@@ -159,12 +245,15 @@ RTLIL::SigSpec gen_mux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const s
return RTLIL::SigSpec(result_wire);
}
-void append_pmux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::vector<RTLIL::SigSpec> &compare, RTLIL::SigSpec when_signal, RTLIL::Cell *last_mux_cell, RTLIL::SwitchRule *sw)
+void append_pmux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::vector<RTLIL::SigSpec> &compare, RTLIL::SigSpec when_signal, RTLIL::Cell *last_mux_cell, RTLIL::SwitchRule *sw, bool ifxmode)
{
log_assert(last_mux_cell != NULL);
log_assert(when_signal.size() == last_mux_cell->getPort("\\A").size());
- RTLIL::SigSpec ctrl_sig = gen_cmp(mod, signal, compare, sw);
+ if (when_signal == last_mux_cell->getPort("\\A"))
+ return;
+
+ RTLIL::SigSpec ctrl_sig = gen_cmp(mod, signal, compare, sw, ifxmode);
log_assert(ctrl_sig.size() == 1);
last_mux_cell->type = "$pmux";
@@ -179,7 +268,8 @@ void append_pmux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::ve
last_mux_cell->parameters["\\S_WIDTH"] = last_mux_cell->getPort("\\S").size();
}
-RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, RTLIL::CaseRule *cs, const RTLIL::SigSpec &sig, const RTLIL::SigSpec &defval)
+RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, SnippetSwCache &swcache, dict<RTLIL::SwitchRule*, bool, hash_ptr_ops> &swpara,
+ RTLIL::CaseRule *cs, const RTLIL::SigSpec &sig, const RTLIL::SigSpec &defval, bool ifxmode)
{
RTLIL::SigSpec result = defval;
@@ -190,9 +280,37 @@ RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, RTLIL::CaseRule *cs, const
for (auto sw : cs->switches)
{
+ if (!swcache.check(sw))
+ continue;
+
// detect groups of parallel cases
std::vector<int> pgroups(sw->cases.size());
+ bool is_simple_parallel_case = true;
+
if (!sw->get_bool_attribute("\\parallel_case")) {
+ if (!swpara.count(sw)) {
+ pool<Const> case_values;
+ for (size_t i = 0; i < sw->cases.size(); i++) {
+ RTLIL::CaseRule *cs2 = sw->cases[i];
+ for (auto pat : cs2->compare) {
+ if (!pat.is_fully_def())
+ goto not_simple_parallel_case;
+ Const cpat = pat.as_const();
+ if (case_values.count(cpat))
+ goto not_simple_parallel_case;
+ case_values.insert(cpat);
+ }
+ }
+ if (0)
+ not_simple_parallel_case:
+ is_simple_parallel_case = false;
+ swpara[sw] = is_simple_parallel_case;
+ } else {
+ is_simple_parallel_case = swpara.at(sw);
+ }
+ }
+
+ if (!is_simple_parallel_case) {
BitPatternPool pool(sw->signal.size());
bool extra_group_for_next_case = false;
for (size_t i = 0; i < sw->cases.size(); i++) {
@@ -214,7 +332,7 @@ RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, RTLIL::CaseRule *cs, const
for (auto pat : cs2->compare)
if (!pat.is_fully_const())
extra_group_for_next_case = true;
- else
+ else if (!ifxmode)
pool.take(pat);
}
}
@@ -225,37 +343,39 @@ RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, RTLIL::CaseRule *cs, const
for (size_t i = 0; i < sw->cases.size(); i++) {
int case_idx = sw->cases.size() - i - 1;
RTLIL::CaseRule *cs2 = sw->cases[case_idx];
- RTLIL::SigSpec value = signal_to_mux_tree(mod, cs2, sig, initial_val);
+ RTLIL::SigSpec value = signal_to_mux_tree(mod, swcache, swpara, cs2, sig, initial_val, ifxmode);
if (last_mux_cell && pgroups[case_idx] == pgroups[case_idx+1])
- append_pmux(mod, sw->signal, cs2->compare, value, last_mux_cell, sw);
+ append_pmux(mod, sw->signal, cs2->compare, value, last_mux_cell, sw, ifxmode);
else
- result = gen_mux(mod, sw->signal, cs2->compare, value, result, last_mux_cell, sw);
+ result = gen_mux(mod, sw->signal, cs2->compare, value, result, last_mux_cell, sw, ifxmode);
}
}
return result;
}
-void proc_mux(RTLIL::Module *mod, RTLIL::Process *proc)
+void proc_mux(RTLIL::Module *mod, RTLIL::Process *proc, bool ifxmode)
{
- bool first = true;
- while (1)
- {
- RTLIL::SigSpec sig = find_any_lvalue(&proc->root_case);
+ log("Creating decoders for process `%s.%s'.\n", mod->name.c_str(), proc->name.c_str());
- if (sig.size() == 0)
- break;
+ SigSnippets sigsnip;
+ sigsnip.insert(&proc->root_case);
- if (first) {
- log("Creating decoders for process `%s.%s'.\n", mod->name.c_str(), proc->name.c_str());
- first = false;
- }
+ SnippetSwCache swcache;
+ swcache.snippets = &sigsnip;
+ swcache.insert(&proc->root_case);
- extract_core_signal(&proc->root_case, sig);
+ dict<RTLIL::SwitchRule*, bool, hash_ptr_ops> swpara;
- log(" creating decoder for signal `%s'.\n", log_signal(sig));
+ int cnt = 0;
+ for (int idx : sigsnip.snippets)
+ {
+ swcache.current_snippet = idx;
+ RTLIL::SigSpec sig = sigsnip.sigidx[idx];
- RTLIL::SigSpec value = signal_to_mux_tree(mod, &proc->root_case, sig, RTLIL::SigSpec(RTLIL::State::Sx, sig.size()));
+ log("%6d/%d: %s\n", ++cnt, GetSize(sigsnip.snippets), log_signal(sig));
+
+ RTLIL::SigSpec value = signal_to_mux_tree(mod, swcache, swpara, &proc->root_case, sig, RTLIL::SigSpec(RTLIL::State::Sx, sig.size()), ifxmode);
mod->connect(RTLIL::SigSig(sig, value));
}
}
@@ -266,24 +386,38 @@ struct ProcMuxPass : public Pass {
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
- log(" proc_mux [selection]\n");
+ log(" proc_mux [options] [selection]\n");
log("\n");
log("This pass converts the decision trees in processes (originating from if-else\n");
log("and case statements) to trees of multiplexer cells.\n");
log("\n");
+ log(" -ifx\n");
+ log(" Use Verilog simulation behavior with respect to undef values in\n");
+ log(" 'case' expressions and 'if' conditions.\n");
+ log("\n");
}
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
{
- log_header("Executing PROC_MUX pass (convert decision trees to multiplexers).\n");
+ bool ifxmode = false;
+ log_header(design, "Executing PROC_MUX pass (convert decision trees to multiplexers).\n");
- extra_args(args, 1, design);
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ if (args[argidx] == "-ifx") {
+ ifxmode = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
for (auto mod : design->modules())
if (design->selected(mod))
for (auto &proc_it : mod->processes)
if (design->selected(mod, proc_it.second))
- proc_mux(mod, proc_it.second);
+ proc_mux(mod, proc_it.second, ifxmode);
}
} ProcMuxPass;
-
+
PRIVATE_NAMESPACE_END
diff --git a/passes/proc/proc_rmdead.cc b/passes/proc/proc_rmdead.cc
index 427e0d567..5672fb475 100644
--- a/passes/proc/proc_rmdead.cc
+++ b/passes/proc/proc_rmdead.cc
@@ -2,11 +2,11 @@
* 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
@@ -51,8 +51,8 @@ void proc_rmdead(RTLIL::SwitchRule *sw, int &counter)
counter++;
continue;
}
- if (pool.empty())
- sw->cases[i]->compare.clear();
+ // if (pool.empty())
+ // sw->cases[i]->compare.clear();
}
for (auto switch_it : sw->cases[i]->switches)
@@ -76,7 +76,7 @@ struct ProcRmdeadPass : public Pass {
}
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
{
- log_header("Executing PROC_RMDEAD pass (remove dead branches from decision trees).\n");
+ log_header(design, "Executing PROC_RMDEAD pass (remove dead branches from decision trees).\n");
extra_args(args, 1, design);
@@ -100,5 +100,5 @@ struct ProcRmdeadPass : public Pass {
log("Removed a total of %d dead cases.\n", total_counter);
}
} ProcRmdeadPass;
-
+
PRIVATE_NAMESPACE_END
diff --git a/passes/sat/eval.cc b/passes/sat/eval.cc
index 62534ec0b..09f69cc5c 100644
--- a/passes/sat/eval.cc
+++ b/passes/sat/eval.cc
@@ -2,11 +2,11 @@
* 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
@@ -87,7 +87,7 @@ struct BruteForceEquivChecker
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 equivialence (brute-force): %s vs %s\n", mod1->name.c_str(), mod2->name.c_str());
+ 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;
@@ -143,16 +143,16 @@ struct VlogHammerReporter
{
log("Verifying SAT model (%s)..\n", model_undef ? "with undef" : "without undef");
- ezDefaultSAT ez;
+ ezSatPtr ez;
SigMap sigmap(module);
- SatGen satgen(&ez, &sigmap);
+ 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));
+ 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;
@@ -163,9 +163,9 @@ struct VlogHammerReporter
}
log(" Created SAT problem with %d variables and %d clauses.\n",
- ez.numCnfVariables(), ez.numCnfClauses());
+ ez->numCnfVariables(), ez->numCnfClauses());
- if (!ez.solve(y_vec, y_values))
+ 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++) {
@@ -204,7 +204,7 @@ struct VlogHammerReporter
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)))
+ 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));
@@ -220,15 +220,15 @@ struct VlogHammerReporter
}
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))
+ 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))
+ 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");
}
@@ -389,7 +389,7 @@ struct EvalPass : public Pass {
std::vector<std::string> shows, tables;
bool set_undef = false;
- log_header("Executing EVAL pass (evaluate the circuit given an input).\n");
+ log_header(design, "Executing EVAL pass (evaluate the circuit given an input).\n");
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++) {
@@ -421,7 +421,7 @@ struct EvalPass : public Pass {
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 equivialent!\n");
+ 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;
@@ -448,7 +448,7 @@ struct EvalPass : public Pass {
RTLIL::id2cstr(module->name), RTLIL::id2cstr(mod_it.first));
module = mod_it.second;
}
- if (module == NULL)
+ if (module == NULL)
log_cmd_error("Can't perform EVAL on an empty selection!\n");
ConstEval ce(module);
@@ -568,7 +568,7 @@ struct EvalPass : public Pass {
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] = std::max(tab_column_width[i], int(row[i].size()));
+ tab_column_width[i] = max(tab_column_width[i], int(row[i].size()));
}
log("\n");
@@ -594,10 +594,10 @@ struct EvalPass : public Pass {
log("\n");
if (undef.size() > 0) {
undef.sort_and_unify();
- log("Assumend undef (x) value for the following singals: %s\n\n", log_signal(undef));
+ log("Assumed undef (x) value for the following signals: %s\n\n", log_signal(undef));
}
}
}
} EvalPass;
-
+
PRIVATE_NAMESPACE_END
diff --git a/passes/sat/expose.cc b/passes/sat/expose.cc
index b012bc6a4..9427547f3 100644
--- a/passes/sat/expose.cc
+++ b/passes/sat/expose.cc
@@ -2,11 +2,11 @@
* 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
@@ -116,7 +116,7 @@ void create_dff_dq_map(std::map<RTLIL::IdString, dff_map_info_t> &map, RTLIL::De
info.cell = it.second;
if (info.cell->type == "$dff") {
- info.bit_clk = sigmap(info.cell->getPort("\\CLK")).to_single_sigbit();
+ 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();
@@ -128,8 +128,8 @@ void create_dff_dq_map(std::map<RTLIL::IdString, dff_map_info_t> &map, RTLIL::De
}
if (info.cell->type == "$adff") {
- info.bit_clk = sigmap(info.cell->getPort("\\CLK")).to_single_sigbit();
- info.bit_arst = sigmap(info.cell->getPort("\\ARST")).to_single_sigbit();
+ 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();
@@ -144,21 +144,21 @@ void create_dff_dq_map(std::map<RTLIL::IdString, dff_map_info_t> &map, RTLIL::De
}
if (info.cell->type == "$_DFF_N_" || info.cell->type == "$_DFF_P_") {
- info.bit_clk = sigmap(info.cell->getPort("\\C")).to_single_sigbit();
+ 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")).to_single_sigbit();
- bit_info[sigmap(info.cell->getPort("\\Q")).to_single_sigbit()] = info;
+ 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")).to_single_sigbit();
- info.bit_arst = sigmap(info.cell->getPort("\\R")).to_single_sigbit();
+ 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")).to_single_sigbit();
- bit_info[sigmap(info.cell->getPort("\\Q")).to_single_sigbit()] = info;
+ info.bit_d = sigmap(info.cell->getPort("\\D")).as_bit();
+ bit_info[sigmap(info.cell->getPort("\\Q")).as_bit()] = info;
continue;
}
}
@@ -237,8 +237,8 @@ struct ExposePass : public Pass {
log(" signal path at that wire.\n");
log("\n");
log(" -shared\n");
- log(" only expose those signals that are shared ammong the selected modules.\n");
- log(" this is useful for preparing modules for equivialence checking.\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");
@@ -262,7 +262,7 @@ struct ExposePass : public Pass {
bool flag_evert_dff = false;
std::string sep = ".";
- log_header("Executing EXPOSE pass (exposing internal signals as outputs).\n");
+ log_header(design, "Executing EXPOSE pass (exposing internal signals as outputs).\n");
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++)
@@ -646,5 +646,5 @@ struct ExposePass : public Pass {
}
}
} ExposePass;
-
+
PRIVATE_NAMESPACE_END
diff --git a/passes/sat/freduce.cc b/passes/sat/freduce.cc
index fbca35861..77263f6a2 100644
--- a/passes/sat/freduce.cc
+++ b/passes/sat/freduce.cc
@@ -2,11 +2,11 @@
* 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
@@ -73,7 +73,7 @@ struct FindReducedInputs
SigMap &sigmap;
drivers_t &drivers;
- ezDefaultSAT ez;
+ ezSatPtr ez;
std::set<RTLIL::Cell*> ez_cells;
SatGen satgen;
@@ -81,7 +81,7 @@ struct FindReducedInputs
std::vector<int> sat_pi_uniq_bitvec;
FindReducedInputs(SigMap &sigmap, drivers_t &drivers) :
- sigmap(sigmap), drivers(drivers), satgen(&ez, &sigmap)
+ sigmap(sigmap), drivers(drivers), satgen(ez.get(), &sigmap)
{
satgen.model_undef = true;
}
@@ -104,30 +104,30 @@ struct FindReducedInputs
satgen.setContext(&sigmap, "A");
int sat_a = satgen.importSigSpec(bit).front();
- ez.assume(ez.NOT(satgen.importUndefSigSpec(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()));
+ 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)));
+ 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())));
+ 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]));
+ 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])));
+ 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]));
+ 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)
@@ -201,7 +201,7 @@ struct FindReducedInputs
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)))
+ 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;
@@ -229,8 +229,9 @@ struct PerformReduction
SigMap &sigmap;
drivers_t &drivers;
std::set<std::pair<RTLIL::SigBit, RTLIL::SigBit>> &inv_pairs;
+ pool<SigBit> recursion_guard;
- ezDefaultSAT ez;
+ ezSatPtr ez;
SatGen satgen;
std::vector<int> sat_pi, sat_out, sat_def;
@@ -246,6 +247,15 @@ struct PerformReduction
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) {
@@ -255,20 +265,21 @@ struct PerformReduction
}
int max_child_depth = 0;
for (auto &bit : drv.second)
- max_child_depth = std::max(register_cone_worker(celldone, sigdepth, bit), max_child_depth);
+ 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()));
+ 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, &sigmap), out_bits(bits), cone_size(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;
@@ -278,15 +289,15 @@ struct PerformReduction
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()));
+ 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)))
+ 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]);
+ sat_out[i] = ez->NOT(sat_out[i]);
} else
out_inverted = std::vector<bool>(sat_out.size(), false);
}
@@ -296,8 +307,8 @@ struct PerformReduction
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]));
+ 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);
@@ -355,8 +366,8 @@ struct PerformReduction
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]));
+ 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;
@@ -366,7 +377,7 @@ struct PerformReduction
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)))
+ if (ez->solve(modelVars, model, ez->expression(ezSAT::OpOr, sat_set_list), ez->expression(ezSAT::OpOr, sat_clr_list)))
{
int iter_count = 1;
@@ -379,13 +390,13 @@ struct PerformReduction
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]));
+ 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)))
+ 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++;
}
@@ -431,7 +442,7 @@ struct PerformReduction
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)))
+ if (ez->solve(ez->NOT(sat_def[idx]), ez->expression(ezSAT::OpOr, sat_def_list)))
undef_slaves.push_back(idx);
}
@@ -446,7 +457,7 @@ struct PerformReduction
out_depth[idx] = std::numeric_limits<int>::max();
if (verbose_level >= 1) {
- log("%s Found %d equivialent signals:", indt, int(bucket.size()));
+ 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");
@@ -495,7 +506,7 @@ struct PerformReduction
std::vector<RTLIL::SigBit> r_sigbits;
for (int idx : r)
r_sigbits.push_back(out_bits[idx]);
- log(" Found group of %d equivialent signals: %s\n", int(r.size()), log_signal(r_sigbits));
+ log(" Found group of %d equivalent signals: %s\n", int(r.size()), log_signal(r_sigbits));
}
std::vector<int> undef_slaves;
@@ -505,7 +516,7 @@ struct PerformReduction
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)))
+ if (ez->solve(ez->NOT(sat_def[idx]), ez->expression(ezSAT::OpOr, sat_def_list)))
undef_slaves.push_back(idx);
}
@@ -681,7 +692,7 @@ struct FreduceWorker
if (!dump_prefix.empty())
dump();
- log(" Rewiring %d equivialent groups:\n", int(equiv.size()));
+ log(" Rewiring %d equivalent groups:\n", int(equiv.size()));
int rewired_sigbits = 0;
for (auto &grp : equiv)
{
@@ -755,7 +766,7 @@ struct FreducePass : public Pass {
log(" freduce [options] [selection]\n");
log("\n");
log("This pass performs functional reduction in the circuit. I.e. if two nodes are\n");
- log("equivialent, they are merged to one node and one of the redundant drivers is\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");
@@ -773,7 +784,7 @@ struct FreducePass : public Pass {
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("equivialent nodes.\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");
@@ -787,7 +798,7 @@ struct FreducePass : public Pass {
inv_mode = false;
dump_prefix = std::string();
- log_header("Executing FREDUCE pass (perform functional reduction).\n");
+ log_header(design, "Executing FREDUCE pass (perform functional reduction).\n");
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++) {
@@ -825,5 +836,5 @@ struct FreducePass : public Pass {
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
index 9853cd0c6..4854e19bf 100644
--- a/passes/sat/miter.cc
+++ b/passes/sat/miter.cc
@@ -2,11 +2,11 @@
* 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
@@ -32,7 +32,7 @@ void create_miter_equiv(struct Pass *that, std::vector<std::string> args, RTLIL:
bool flag_make_assert = false;
bool flag_flatten = false;
- log_header("Executing MITER pass (creating miter circuit).\n");
+ log_header(design, "Executing MITER pass (creating miter circuit).\n");
size_t argidx;
for (argidx = 2; argidx < args.size(); argidx++)
@@ -61,7 +61,7 @@ void create_miter_equiv(struct Pass *that, std::vector<std::string> args, RTLIL:
}
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++]);
@@ -71,7 +71,7 @@ void create_miter_equiv(struct Pass *that, std::vector<std::string> args, RTLIL:
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", gate_name.c_str());
+ 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);
@@ -254,7 +254,80 @@ void create_miter_equiv(struct Pass *that, std::vector<std::string> args, RTLIL:
if (flag_flatten) {
log_push();
- Pass::call_on_module(design, miter_module, "flatten; opt_const -keepdc -undriven;;");
+ 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 or_signals;
+ vector<Cell*> cell_list = module->cells();
+ for (auto cell : cell_list) {
+ if (cell->type == "$assert") {
+ SigBit is_active = module->Nex(NEW_ID, cell->getPort("\\A"), State::S1);
+ SigBit is_enabled = module->Eqx(NEW_ID, cell->getPort("\\EN"), State::S1);
+ or_signals.append(module->And(NEW_ID, is_active, is_enabled));
+ module->remove(cell);
+ }
+ }
+
+ module->addReduceOr(NEW_ID, or_signals, trigger);
+
+ if (flag_flatten) {
+ log_push();
+ Pass::call_on_module(design, module, "opt_expr -keepdc -undriven;;");
log_pop();
}
}
@@ -267,7 +340,7 @@ struct MiterPass : public Pass {
log("\n");
log(" miter -equiv [options] gold_name gate_name miter_name\n");
log("\n");
- log("Creates a miter circuit for equivialence checking. The gold- and gate- modules\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");
@@ -288,7 +361,21 @@ struct MiterPass : public Pass {
log(" also create an 'assert' cell that checks if trigger is always low.\n");
log("\n");
log(" -flatten\n");
- log(" call 'flatten; opt_const -keepdc -undriven;;' on the miter circuit.\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)
@@ -298,8 +385,13 @@ struct MiterPass : public Pass {
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
index 1aae421f0..c3cb435d1 100644
--- a/passes/sat/sat.cc
+++ b/passes/sat/sat.cc
@@ -2,11 +2,11 @@
* 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
@@ -41,16 +41,17 @@ struct SatHelper
RTLIL::Design *design;
RTLIL::Module *module;
- ezDefaultSAT ez;
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;
+ bool prove_asserts, set_assumes;
// undef constraints
bool enable_undef, set_init_def, set_init_undef, set_init_zero, ignore_unknown_cells;
@@ -65,7 +66,7 @@ struct SatHelper
bool gotTimeout;
SatHelper(RTLIL::Design *design, RTLIL::Module *module, bool enable_undef) :
- design(design), module(module), sigmap(module), ct(design), satgen(&ez, &sigmap)
+ design(design), module(module), sigmap(module), ct(design), satgen(ez.get(), &sigmap)
{
this->enable_undef = enable_undef;
satgen.model_undef = enable_undef;
@@ -155,7 +156,7 @@ struct SatHelper
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)));
+ ez->assume(ez->NOT(ez->expression(ezSAT::OpOr, undef_rem)));
}
if (set_init_undef) {
@@ -179,7 +180,7 @@ struct SatHelper
log("Final constraint equation: %s = %s\n\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, 1));
+ ez->assume(satgen.signals_eq(big_lhs, big_rhs, 1));
}
void setup(int timestep = -1)
@@ -250,7 +251,7 @@ struct SatHelper
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));
+ ez->assume(satgen.signals_eq(big_lhs, big_rhs, timestep));
// 0 = sets_def
// 1 = sets_any_undef
@@ -310,28 +311,36 @@ struct SatHelper
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)));
+ ez->assume(ez->NOT(ez->expression(ezSAT::OpOr, undef_sig)));
if (t == 1)
- ez.assume(ez.expression(ezSAT::OpOr, undef_sig));
+ ez->assume(ez->expression(ezSAT::OpOr, undef_sig));
if (t == 2)
- ez.assume(ez.expression(ezSAT::OpAnd, undef_sig));
+ ez->assume(ez->expression(ezSAT::OpAnd, undef_sig));
}
int import_cell_counter = 0;
- for (auto &c : module->cells_)
- if (design->selected(module, c.second)) {
- // log("Import cell: %s\n", RTLIL::id2cstr(c.first));
- if (satgen.importCell(c.second, timestep)) {
- for (auto &p : c.second->connections())
- if (ct.cell_output(c.second->type, p.first))
- show_drivers.insert(sigmap(p.second), c.second);
+ 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(c.first), RTLIL::id2cstr(c.second->type));
+ 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(c.first), RTLIL::id2cstr(c.second->type));
+ 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));
+ }
}
int setup_proof(int timestep = -1)
@@ -401,7 +410,7 @@ struct SatHelper
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))))));
+ 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) {
@@ -412,22 +421,22 @@ struct SatHelper
prove_bits.push_back(satgen.importAsserts(timestep));
}
- return ez.expression(ezSAT::OpAnd, prove_bits);
+ 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)));
+ 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())
+ ez->setSolverTimeout(timeout);
+ bool success = ez->solve(modelExpressions, modelValues, assumptions);
+ if (ez->getSolverTimoutStatus())
gotTimeout = true;
return success;
}
@@ -435,9 +444,9 @@ struct SatHelper
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())
+ ez->setSolverTimeout(timeout);
+ bool success = ez->solve(modelExpressions, modelValues, a, b, c, d, e, f);
+ if (ez->getSolverTimoutStatus())
gotTimeout = true;
return success;
}
@@ -478,7 +487,7 @@ struct SatHelper
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)))
+ if (!solve(ez->expression(ezSAT::OpAnd, must_undef), ez->expression(ezSAT::OpOr, maybe_undef)))
break;
}
@@ -597,8 +606,8 @@ struct SatHelper
int maxModelWidth = 10;
for (auto &info : modelInfo) {
- maxModelName = std::max(maxModelName, int(info.description.size()));
- maxModelWidth = std::max(maxModelWidth, info.width);
+ maxModelName = max(maxModelName, int(info.description.size()));
+ maxModelWidth = max(maxModelWidth, info.width);
}
log("\n");
@@ -621,11 +630,11 @@ struct SatHelper
"---------------------------------------------------------------------------------------------------";
if (last_timestep == -2) {
log(max_timestep > 0 ? " Time " : " ");
- log("%-*s %10s %10s %*s\n", maxModelName+10, "Signal Name", "Dec", "Hex", maxModelWidth+5, "Bin");
+ log("%-*s %11s %9s %*s\n", maxModelName+5, "Signal Name", "Dec", "Hex", maxModelWidth+3, "Bin");
}
log(max_timestep > 0 ? " ---- " : " ");
- log("%*.*s %10.10s %10.10s %*.*s\n", maxModelName+10, maxModelName+10,
- hline, hline, hline, maxModelWidth+5, maxModelWidth+5, hline);
+ log("%*.*s %11.11s %9.9s %*.*s\n", maxModelName+5, maxModelName+5,
+ hline, hline, hline, maxModelWidth+3, maxModelWidth+3, hline);
last_timestep = info.timestep;
}
@@ -638,9 +647,9 @@ struct SatHelper
log(" ");
if (info.width <= 32 && !found_undef)
- log("%-*s %10d %10x %*s\n", maxModelName+10, info.description.c_str(), value.as_int(), value.as_int(), maxModelWidth+5, value.as_string().c_str());
+ 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 %10s %10s %*s\n", maxModelName+10, info.description.c_str(), "--", "--", maxModelWidth+5, value.as_string().c_str());
+ log("%-*s %11s %9s %*s\n", maxModelName+5, info.description.c_str(), "--", "--", maxModelWidth+3, value.as_string().c_str());
}
if (last_timestep == -2)
@@ -671,7 +680,7 @@ struct SatHelper
fprintf(f, " %s\n", stime);
fprintf(f, "$end\n");
fprintf(f, "$version\n");
- fprintf(f, " Generated by %s\n", yosys_version_str);
+ 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",
@@ -750,6 +759,80 @@ struct SatHelper
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;
@@ -758,12 +841,12 @@ struct SatHelper
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);
+ 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));
+ clause.push_back(modelValues.at(i) ? ez->NOT(modelExpressions.at(i)) : modelExpressions.at(i));
+ ez->assume(ez->expression(ezSAT::OpOr, clause));
}
};
@@ -853,6 +936,9 @@ struct SatPass : public Pass {
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");
@@ -865,11 +951,17 @@ struct SatPass : public Pass {
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");
@@ -890,6 +982,9 @@ struct SatPass : public Pass {
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");
@@ -898,7 +993,7 @@ struct SatPass : public Pass {
log("is passed, a temporal induction proof is performed.\n");
log("\n");
log(" -tempinduct\n");
- log(" Perform a temporal induction proof. In a temporalinduction proof it is\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");
@@ -906,12 +1001,26 @@ struct SatPass : public Pass {
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 equivialence checking.\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");
@@ -924,6 +1033,13 @@ struct SatPass : public Pass {
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");
@@ -951,10 +1067,13 @@ struct SatPass : public Pass {
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;
- std::string vcd_file_name, cnf_file_name;
+ 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("Executing SAT pass (solving SAT problems in the circuit).\n");
+ log_header(design, "Executing SAT pass (solving SAT problems in the circuit).\n");
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++) {
@@ -996,6 +1115,10 @@ struct SatPass : public Pass {
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;
@@ -1035,6 +1158,10 @@ struct SatPass : public Pass {
enable_undef = true;
continue;
}
+ if (args[argidx] == "-set-assumes") {
+ set_assumes = true;
+ continue;
+ }
if (args[argidx] == "-tempinduct") {
tempinduct = true;
continue;
@@ -1044,6 +1171,20 @@ struct SatPass : public Pass {
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];
@@ -1135,6 +1276,18 @@ struct SatPass : public Pass {
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;
@@ -1143,6 +1296,10 @@ struct SatPass : public Pass {
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;
@@ -1152,14 +1309,12 @@ struct SatPass : public Pass {
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 SAT pass! (selected: %s and %s)\n",
- RTLIL::id2cstr(module->name), RTLIL::id2cstr(mod_it.first));
- module = mod_it.second;
- }
- if (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)
@@ -1192,6 +1347,29 @@ struct SatPass : public Pass {
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)
@@ -1199,8 +1377,10 @@ struct SatPass : public Pass {
SatHelper basecase(design, module, enable_undef);
SatHelper inductstep(design, module, enable_undef);
+ bool basecase_setup_init = true;
basecase.sets = sets;
+ basecase.set_assumes = set_assumes;
basecase.prove = prove;
basecase.prove_x = prove_x;
basecase.prove_asserts = prove_asserts;
@@ -1222,10 +1402,11 @@ struct SatPass : public Pass {
basecase.ignore_unknown_cells = ignore_unknown_cells;
for (int timestep = 1; timestep <= seq_len; timestep++)
- basecase.setup(timestep);
- basecase.setup_init();
+ if (!tempinduct_inductonly)
+ basecase.setup(timestep);
inductstep.sets = sets;
+ inductstep.set_assumes = set_assumes;
inductstep.prove = prove;
inductstep.prove_x = prove_x;
inductstep.prove_asserts = prove_asserts;
@@ -1237,12 +1418,14 @@ struct SatPass : public Pass {
inductstep.satgen.ignore_div_by_zero = ignore_div_by_zero;
inductstep.ignore_unknown_cells = ignore_unknown_cells;
- inductstep.setup(1);
- inductstep.ez.assume(inductstep.setup_proof(1));
+ 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)));
+ inductstep.ez->assume(inductstep.ez->NOT(inductstep.ez->expression(ezSAT::OpOr, undef_state)));
}
for (int inductlen = 1; inductlen <= maxsteps || maxsteps == 0; inductlen++)
@@ -1251,81 +1434,120 @@ struct SatPass : public Pass {
// phase 1: proving base case
- basecase.setup(seq_len + inductlen);
- 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_inductonly)
+ {
+ basecase.setup(seq_len + inductlen);
+ int property = basecase.setup_proof(seq_len + inductlen);
+ basecase.generate_model();
- log("\n[base case] Solving problem with %d variables and %d clauses..\n",
- basecase.ez.numCnfVariables(), basecase.ez.numCnfClauses());
+ if (basecase_setup_init) {
+ basecase.setup_init();
+ basecase_setup_init = false;
+ }
- 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);
- goto tip_failed;
- }
+ if (inductlen > 1)
+ basecase.force_unique_state(seq_len + 1, seq_len + inductlen);
- if (basecase.gotTimeout)
- goto timeout;
+ 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());
+
+ 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);
- basecase.ez.assume(property);
+ 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
- inductstep.setup(inductlen + 1);
- property = inductstep.setup_proof(inductlen + 1);
- inductstep.generate_model();
-
- if (inductlen > 1)
- inductstep.force_unique_state(1, inductlen + 1);
-
- if (inductlen < initsteps)
- {
- log("\n[induction step] Skipping problem with %d variables and %d clauses (below initsteps).\n",
- inductstep.ez.numCnfVariables(), inductstep.ez.numCnfClauses());
- inductstep.ez.assume(property);
- }
- else
+ if (!tempinduct_baseonly)
{
- 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));
+ inductstep.setup(inductlen + 1);
+ int property = inductstep.setup_proof(inductlen + 1);
+ inductstep.generate_model();
- log("Dumping CNF to file `%s'.\n", cnf_file_name.c_str());
- cnf_file_name.clear();
+ if (inductlen > 1)
+ inductstep.force_unique_state(1, inductlen + 1);
- inductstep.ez.printDIMACS(f, false);
- fclose(f);
+ 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);
}
-
- log("\n[induction step] Solving problem with %d variables and %d clauses..\n",
- inductstep.ez.numCnfVariables(), inductstep.ez.numCnfClauses());
-
- if (!inductstep.solve(inductstep.ez.NOT(property))) {
- if (inductstep.gotTimeout)
- goto timeout;
- log("Induction step proven: SUCCESS!\n");
- print_qed();
- goto tip_success;
+ 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());
+
+ 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();
}
-
- 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:
@@ -1349,6 +1571,7 @@ struct SatPass : public Pass {
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;
@@ -1372,7 +1595,7 @@ struct SatPass : public Pass {
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()));
+ sathelper.ez->assume(sathelper.ez->NOT(sathelper.setup_proof()));
} else {
std::vector<int> prove_bits;
for (int timestep = 1; timestep <= seq_len; timestep++) {
@@ -1382,7 +1605,7 @@ struct SatPass : public Pass {
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.ez->assume(sathelper.ez->NOT(sathelper.ez->expression(ezSAT::OpAnd, prove_bits)));
sathelper.setup_init();
}
sathelper.generate_model();
@@ -1396,7 +1619,7 @@ struct SatPass : public Pass {
log("Dumping CNF to file `%s'.\n", cnf_file_name.c_str());
cnf_file_name.clear();
- sathelper.ez.printDIMACS(f, false);
+ sathelper.ez->printDIMACS(f, false);
fclose(f);
}
@@ -1404,7 +1627,7 @@ struct SatPass : public Pass {
rerun_solver:
log("\nSolving problem with %d variables and %d clauses..\n",
- sathelper.ez.numCnfVariables(), sathelper.ez.numCnfClauses());
+ sathelper.ez->numCnfVariables(), sathelper.ez->numCnfClauses());
if (sathelper.solve())
{
@@ -1424,6 +1647,8 @@ struct SatPass : public Pass {
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++;
@@ -1487,5 +1712,5 @@ struct SatPass : public Pass {
}
}
} SatPass;
-
+
PRIVATE_NAMESPACE_END
diff --git a/passes/techmap/Makefile.inc b/passes/techmap/Makefile.inc
index d8a433164..96fa0d92a 100644
--- a/passes/techmap/Makefile.inc
+++ b/passes/techmap/Makefile.inc
@@ -5,17 +5,35 @@ OBJS += passes/techmap/dfflibmap.o
OBJS += passes/techmap/maccmap.o
OBJS += passes/techmap/libparse.o
+ifeq ($(ENABLE_ABC),1)
+OBJS += passes/techmap/abc.o
+ifneq ($(ABCEXTERNAL),)
+passes/techmap/abc.o: CXXFLAGS += -DABCEXTERNAL='"$(ABCEXTERNAL)"'
+endif
+endif
+
ifneq ($(SMALL),1)
OBJS += passes/techmap/iopadmap.o
OBJS += passes/techmap/hilomap.o
OBJS += passes/techmap/extract.o
OBJS += passes/techmap/alumacc.o
OBJS += passes/techmap/dff2dffe.o
+OBJS += passes/techmap/dffinit.o
+OBJS += passes/techmap/pmuxtree.o
+OBJS += passes/techmap/muxcover.o
+OBJS += passes/techmap/aigmap.o
+OBJS += passes/techmap/tribuf.o
+OBJS += passes/techmap/lut2mux.o
+OBJS += passes/techmap/nlutmap.o
+OBJS += passes/techmap/dffsr2dff.o
+OBJS += passes/techmap/shregmap.o
+OBJS += passes/techmap/deminout.o
endif
GENFILES += passes/techmap/techmap.inc
passes/techmap/techmap.inc: techlibs/common/techmap.v
+ $(Q) mkdir -p $(dir $@)
$(P) echo "// autogenerated from $<" > $@.new
$(Q) echo "static char stdcells_code[] = {" >> $@.new
$(Q) od -v -td1 -An $< | $(SED) -e 's/[0-9][0-9]*/&,/g' >> $@.new
@@ -24,9 +42,12 @@ passes/techmap/techmap.inc: techlibs/common/techmap.v
passes/techmap/techmap.o: passes/techmap/techmap.inc
+ifneq ($(CONFIG),emcc)
TARGETS += yosys-filterlib$(EXE)
EXTRA_OBJS += passes/techmap/filterlib.o
yosys-filterlib$(EXE): passes/techmap/filterlib.o
- $(P) $(CXX) -o yosys-filterlib$(EXE) $(LDFLAGS) $^ $(LDLIBS)
+ $(Q) mkdir -p $(dir $@)
+ $(P) $(LD) -o yosys-filterlib$(EXE) $(LDFLAGS) $^ $(LDLIBS)
+endif
diff --git a/passes/abc/abc.cc b/passes/techmap/abc.cc
index 69da710f2..cc79296c5 100644
--- a/passes/abc/abc.cc
+++ b/passes/techmap/abc.cc
@@ -2,11 +2,11 @@
* 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
@@ -26,18 +26,20 @@
// http://www.ece.cmu.edu/~ee760/760docs/blif.pdf
// [[CITE]] Kahn's Topological sorting algorithm
-// Kahn, Arthur B. (1962), "Topological sorting of large networks", Communications of the ACM 5 (11): 558–562, doi:10.1145/368996.369025
+// Kahn, Arthur B. (1962), "Topological sorting of large networks", Communications of the ACM 5 (11): 558-562, doi:10.1145/368996.369025
// http://en.wikipedia.org/wiki/Topological_sorting
-#define ABC_COMMAND_LIB "strash; scorr; ifraig; retime {D}; strash; dch -f; map {D}"
-#define ABC_COMMAND_CTR "strash; scorr; ifraig; retime {D}; strash; dch -f; map {D}; buffer; upsize {D}; dnsize {D}; stime -p"
-#define ABC_COMMAND_LUT "strash; scorr; ifraig; retime; strash; dch -f; if"
-#define ABC_COMMAND_DFL "strash; scorr; ifraig; retime; strash; dch -f; map"
+#define ABC_COMMAND_LIB "strash; dc2; scorr; ifraig; retime -o {D}; strash; dch -f; map {D}"
+#define ABC_COMMAND_CTR "strash; dc2; scorr; ifraig; retime -o {D}; strash; dch -f; map {D}; buffer; upsize {D}; dnsize {D}; stime -p"
+#define ABC_COMMAND_LUT "strash; dc2; scorr; ifraig; retime -o; strash; dch -f; if; mfs"
+#define ABC_COMMAND_SOP "strash; dc2; scorr; ifraig; retime -o; strash; dch -f; cover {I} {P}"
+#define ABC_COMMAND_DFL "strash; dc2; scorr; ifraig; retime -o; strash; dch -f; map"
-#define ABC_FAST_COMMAND_LIB "retime {D}; map {D}"
-#define ABC_FAST_COMMAND_CTR "retime {D}; map {D}; buffer; upsize {D}; dnsize {D}; stime -p"
-#define ABC_FAST_COMMAND_LUT "retime; if"
-#define ABC_FAST_COMMAND_DFL "retime; map"
+#define ABC_FAST_COMMAND_LIB "retime -o {D}; map {D}"
+#define ABC_FAST_COMMAND_CTR "retime -o {D}; map {D}; buffer; upsize {D}; dnsize {D}; stime -p"
+#define ABC_FAST_COMMAND_LUT "retime -o; if"
+#define ABC_FAST_COMMAND_SOP "retime -o; cover -I {I} -P {P}"
+#define ABC_FAST_COMMAND_DFL "retime -o; map"
#include "kernel/register.h"
#include "kernel/sigtools.h"
@@ -56,7 +58,7 @@
# include <dirent.h>
#endif
-#include "blifparse.h"
+#include "frontends/blif/blifparse.h"
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
@@ -90,12 +92,17 @@ struct gate_t
RTLIL::SigBit bit;
};
+bool map_mux4;
+bool map_mux8;
+bool map_mux16;
+
bool markgroups;
int map_autoidx;
SigMap assign_map;
RTLIL::Module *module;
std::vector<gate_t> signal_list;
std::map<RTLIL::SigBit, int> signal_map;
+pool<std::string> enabled_gates;
bool clk_polarity, en_polarity;
RTLIL::SigSpec clk_sig, en_sig;
@@ -587,8 +594,9 @@ struct abc_output_filter
};
void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::string script_file, std::string exe_file,
- std::string liberty_file, std::string constr_file, bool cleanup, int lut_mode, int lut_mode2, bool dff_mode, std::string clk_str,
- bool keepff, std::string delay_target, bool fast_mode, const std::vector<RTLIL::Cell*> &cells, bool show_tempdir)
+ std::string liberty_file, std::string constr_file, bool cleanup, vector<int> lut_costs, bool dff_mode, std::string clk_str,
+ bool keepff, std::string delay_target, std::string sop_inputs, std::string sop_products, bool fast_mode,
+ const std::vector<RTLIL::Cell*> &cells, bool show_tempdir, bool sop_mode)
{
module = current_module;
map_autoidx = autoidx++;
@@ -611,7 +619,7 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin
if (!cleanup)
tempdir_name[0] = tempdir_name[4] = '_';
tempdir_name = make_temp_dir(tempdir_name);
- log_header("Extracting gate netlist of module `%s' to `%s/input.blif'..\n",
+ log_header(design, "Extracting gate netlist of module `%s' to `%s/input.blif'..\n",
module->name.c_str(), replace_tempdir(tempdir_name, tempdir_name, show_tempdir).c_str());
std::string abc_script = stringf("read_blif %s/input.blif; ", tempdir_name.c_str());
@@ -621,7 +629,7 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin
if (!constr_file.empty())
abc_script += stringf("read_constr -v %s; ", constr_file.c_str());
} else
- if (lut_mode)
+ if (!lut_costs.empty())
abc_script += stringf("read_lut %s/lutdefs.txt; ", tempdir_name.c_str());
else
abc_script += stringf("read_library %s/stdcells.genlib; ", tempdir_name.c_str());
@@ -637,16 +645,30 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin
abc_script += script_file[i];
} else
abc_script += stringf("source %s", script_file.c_str());
- } else if (lut_mode)
+ } else if (!lut_costs.empty()) {
+ bool all_luts_cost_same = true;
+ for (int this_cost : lut_costs)
+ if (this_cost != lut_costs.front())
+ all_luts_cost_same = false;
abc_script += fast_mode ? ABC_FAST_COMMAND_LUT : ABC_COMMAND_LUT;
- else if (!liberty_file.empty())
+ if (all_luts_cost_same && !fast_mode)
+ abc_script += "; lutpack";
+ } else if (!liberty_file.empty())
abc_script += constr_file.empty() ? (fast_mode ? ABC_FAST_COMMAND_LIB : ABC_COMMAND_LIB) : (fast_mode ? ABC_FAST_COMMAND_CTR : ABC_COMMAND_CTR);
+ else if (sop_mode)
+ abc_script += fast_mode ? ABC_FAST_COMMAND_SOP : ABC_COMMAND_SOP;
else
abc_script += fast_mode ? ABC_FAST_COMMAND_DFL : ABC_COMMAND_DFL;
for (size_t pos = abc_script.find("{D}"); pos != std::string::npos; pos = abc_script.find("{D}", pos))
abc_script = abc_script.substr(0, pos) + delay_target + abc_script.substr(pos+3);
+ for (size_t pos = abc_script.find("{I}"); pos != std::string::npos; pos = abc_script.find("{D}", pos))
+ abc_script = abc_script.substr(0, pos) + sop_inputs + abc_script.substr(pos+3);
+
+ for (size_t pos = abc_script.find("{P}"); pos != std::string::npos; pos = abc_script.find("{D}", pos))
+ abc_script = abc_script.substr(0, pos) + sop_products + abc_script.substr(pos+3);
+
abc_script += stringf("; write_blif %s/output.blif", tempdir_name.c_str());
abc_script = add_echos_to_abc_cmd(abc_script);
@@ -823,7 +845,7 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin
if (count_output > 0)
{
- log_header("Executing ABC.\n");
+ log_header(design, "Executing ABC.\n");
buffer = stringf("%s/stdcells.genlib", tempdir_name.c_str());
f = fopen(buffer.c_str(), "wt");
@@ -833,28 +855,43 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin
fprintf(f, "GATE ONE 1 Y=CONST1;\n");
fprintf(f, "GATE BUF %d Y=A; PIN * NONINV 1 999 1 0 1 0\n", get_cell_cost("$_BUF_"));
fprintf(f, "GATE NOT %d Y=!A; PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_NOT_"));
- fprintf(f, "GATE AND %d Y=A*B; PIN * NONINV 1 999 1 0 1 0\n", get_cell_cost("$_AND_"));
- fprintf(f, "GATE NAND %d Y=!(A*B); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_NAND_"));
- fprintf(f, "GATE OR %d Y=A+B; PIN * NONINV 1 999 1 0 1 0\n", get_cell_cost("$_OR_"));
- fprintf(f, "GATE NOR %d Y=!(A+B); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_NOR_"));
- fprintf(f, "GATE XOR %d Y=(A*!B)+(!A*B); PIN * UNKNOWN 1 999 1 0 1 0\n", get_cell_cost("$_XOR_"));
- fprintf(f, "GATE XNOR %d Y=(A*B)+(!A*!B); PIN * UNKNOWN 1 999 1 0 1 0\n", get_cell_cost("$_XNOR_"));
- fprintf(f, "GATE AOI3 %d Y=!((A*B)+C); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_AOI3_"));
- fprintf(f, "GATE OAI3 %d Y=!((A+B)*C); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_OAI3_"));
- fprintf(f, "GATE AOI4 %d Y=!((A*B)+(C*D)); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_AOI4_"));
- fprintf(f, "GATE OAI4 %d Y=!((A+B)*(C+D)); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_OAI4_"));
- fprintf(f, "GATE MUX %d Y=(A*B)+(S*B)+(!S*A); PIN * UNKNOWN 1 999 1 0 1 0\n", get_cell_cost("$_MUX_"));
+ if (enabled_gates.empty() || enabled_gates.count("AND"))
+ fprintf(f, "GATE AND %d Y=A*B; PIN * NONINV 1 999 1 0 1 0\n", get_cell_cost("$_AND_"));
+ if (enabled_gates.empty() || enabled_gates.count("NAND"))
+ fprintf(f, "GATE NAND %d Y=!(A*B); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_NAND_"));
+ if (enabled_gates.empty() || enabled_gates.count("OR"))
+ fprintf(f, "GATE OR %d Y=A+B; PIN * NONINV 1 999 1 0 1 0\n", get_cell_cost("$_OR_"));
+ if (enabled_gates.empty() || enabled_gates.count("NOR"))
+ fprintf(f, "GATE NOR %d Y=!(A+B); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_NOR_"));
+ if (enabled_gates.empty() || enabled_gates.count("XOR"))
+ fprintf(f, "GATE XOR %d Y=(A*!B)+(!A*B); PIN * UNKNOWN 1 999 1 0 1 0\n", get_cell_cost("$_XOR_"));
+ if (enabled_gates.empty() || enabled_gates.count("XNOR"))
+ fprintf(f, "GATE XNOR %d Y=(A*B)+(!A*!B); PIN * UNKNOWN 1 999 1 0 1 0\n", get_cell_cost("$_XNOR_"));
+ if (enabled_gates.empty() || enabled_gates.count("AOI3"))
+ fprintf(f, "GATE AOI3 %d Y=!((A*B)+C); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_AOI3_"));
+ if (enabled_gates.empty() || enabled_gates.count("OAI3"))
+ fprintf(f, "GATE OAI3 %d Y=!((A+B)*C); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_OAI3_"));
+ if (enabled_gates.empty() || enabled_gates.count("AOI4"))
+ fprintf(f, "GATE AOI4 %d Y=!((A*B)+(C*D)); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_AOI4_"));
+ if (enabled_gates.empty() || enabled_gates.count("OAI4"))
+ fprintf(f, "GATE OAI4 %d Y=!((A+B)*(C+D)); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_OAI4_"));
+ if (enabled_gates.empty() || enabled_gates.count("MUX"))
+ fprintf(f, "GATE MUX %d Y=(A*B)+(S*B)+(!S*A); PIN * UNKNOWN 1 999 1 0 1 0\n", get_cell_cost("$_MUX_"));
+ if (map_mux4)
+ fprintf(f, "GATE MUX4 %d Y=(!S*!T*A)+(S*!T*B)+(!S*T*C)+(S*T*D); PIN * UNKNOWN 1 999 1 0 1 0\n", 2*get_cell_cost("$_MUX_"));
+ if (map_mux8)
+ fprintf(f, "GATE MUX8 %d Y=(!S*!T*!U*A)+(S*!T*!U*B)+(!S*T*!U*C)+(S*T*!U*D)+(!S*!T*U*E)+(S*!T*U*F)+(!S*T*U*G)+(S*T*U*H); PIN * UNKNOWN 1 999 1 0 1 0\n", 4*get_cell_cost("$_MUX_"));
+ if (map_mux16)
+ fprintf(f, "GATE MUX16 %d Y=(!S*!T*!U*!V*A)+(S*!T*!U*!V*B)+(!S*T*!U*!V*C)+(S*T*!U*!V*D)+(!S*!T*U*!V*E)+(S*!T*U*!V*F)+(!S*T*U*!V*G)+(S*T*U*!V*H)+(!S*!T*!U*V*I)+(S*!T*!U*V*J)+(!S*T*!U*V*K)+(S*T*!U*V*L)+(!S*!T*U*V*M)+(S*!T*U*V*N)+(!S*T*U*V*O)+(S*T*U*V*P); PIN * UNKNOWN 1 999 1 0 1 0\n", 8*get_cell_cost("$_MUX_"));
fclose(f);
- if (lut_mode) {
+ if (!lut_costs.empty()) {
buffer = stringf("%s/lutdefs.txt", tempdir_name.c_str());
f = fopen(buffer.c_str(), "wt");
if (f == NULL)
log_error("Opening %s for writing failed: %s\n", buffer.c_str(), strerror(errno));
- for (int i = 0; i < lut_mode; i++)
- fprintf(f, "%d 1.00 1.00\n", i+1);
- for (int i = lut_mode; i < lut_mode2; i++)
- fprintf(f, "%d %d.00 1.00\n", i+1, 2 << (i - lut_mode));
+ for (int i = 0; i < GetSize(lut_costs); i++)
+ fprintf(f, "%d %d.00 1.00\n", i+1, lut_costs.at(i));
fclose(f);
}
@@ -867,16 +904,18 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin
log_error("ABC: execution of command \"%s\" failed: return code %d.\n", buffer.c_str(), ret);
buffer = stringf("%s/%s", tempdir_name.c_str(), "output.blif");
- f = fopen(buffer.c_str(), "rt");
- if (f == NULL)
+ std::ifstream ifs;
+ ifs.open(buffer);
+ if (ifs.fail())
log_error("Can't open ABC output file `%s'.\n", buffer.c_str());
- bool builtin_lib = liberty_file.empty() && script_file.empty() && !lut_mode;
- RTLIL::Design *mapped_design = abc_parse_blif(f, builtin_lib ? "\\DFF" : "\\_dff_");
+ bool builtin_lib = liberty_file.empty();
+ RTLIL::Design *mapped_design = new RTLIL::Design;
+ parse_blif(mapped_design, ifs, builtin_lib ? "\\DFF" : "\\_dff_", false, sop_mode);
- fclose(f);
+ ifs.close();
- log_header("Re-integrating ABC results.\n");
+ log_header(design, "Re-integrating ABC results.\n");
RTLIL::Module *mapped_mod = mapped_design->modules_["\\netlist"];
if (mapped_mod == NULL)
log_error("ABC output file does not contain a module `netlist'.\n");
@@ -888,10 +927,10 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin
}
std::map<std::string, int> cell_stats;
- if (builtin_lib)
+ for (auto c : mapped_mod->cells())
{
- for (auto &it : mapped_mod->cells_) {
- RTLIL::Cell *c = it.second;
+ if (builtin_lib)
+ {
cell_stats[RTLIL::unescape_id(c->type)]++;
if (c->type == "\\ZERO" || c->type == "\\ONE") {
RTLIL::SigSig conn;
@@ -934,61 +973,86 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin
design->select(module, cell);
continue;
}
- if (c->type == "\\AOI3" || c->type == "\\OAI3") {
- RTLIL::Cell *cell = module->addCell(remap_name(c->name), "$_" + c->type.substr(1) + "_");
+ if (c->type == "\\MUX4") {
+ RTLIL::Cell *cell = module->addCell(remap_name(c->name), "$_MUX4_");
if (markgroups) cell->attributes["\\abcgroup"] = map_autoidx;
cell->setPort("\\A", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\A").as_wire()->name)]));
cell->setPort("\\B", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\B").as_wire()->name)]));
cell->setPort("\\C", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\C").as_wire()->name)]));
+ cell->setPort("\\D", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\D").as_wire()->name)]));
+ cell->setPort("\\S", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\S").as_wire()->name)]));
+ cell->setPort("\\T", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\T").as_wire()->name)]));
cell->setPort("\\Y", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\Y").as_wire()->name)]));
design->select(module, cell);
continue;
}
- if (c->type == "\\AOI4" || c->type == "\\OAI4") {
- RTLIL::Cell *cell = module->addCell(remap_name(c->name), "$_" + c->type.substr(1) + "_");
+ if (c->type == "\\MUX8") {
+ RTLIL::Cell *cell = module->addCell(remap_name(c->name), "$_MUX8_");
if (markgroups) cell->attributes["\\abcgroup"] = map_autoidx;
cell->setPort("\\A", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\A").as_wire()->name)]));
cell->setPort("\\B", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\B").as_wire()->name)]));
cell->setPort("\\C", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\C").as_wire()->name)]));
cell->setPort("\\D", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\D").as_wire()->name)]));
+ cell->setPort("\\E", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\E").as_wire()->name)]));
+ cell->setPort("\\F", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\F").as_wire()->name)]));
+ cell->setPort("\\G", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\G").as_wire()->name)]));
+ cell->setPort("\\H", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\H").as_wire()->name)]));
+ cell->setPort("\\S", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\S").as_wire()->name)]));
+ cell->setPort("\\T", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\T").as_wire()->name)]));
+ cell->setPort("\\U", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\U").as_wire()->name)]));
cell->setPort("\\Y", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\Y").as_wire()->name)]));
design->select(module, cell);
continue;
}
- if (c->type == "\\DFF") {
- log_assert(clk_sig.size() == 1);
- RTLIL::Cell *cell;
- if (en_sig.size() == 0) {
- cell = module->addCell(remap_name(c->name), clk_polarity ? "$_DFF_P_" : "$_DFF_N_");
- } else {
- log_assert(en_sig.size() == 1);
- cell = module->addCell(remap_name(c->name), stringf("$_DFFE_%c%c_", clk_polarity ? 'P' : 'N', en_polarity ? 'P' : 'N'));
- cell->setPort("\\E", en_sig);
- }
+ if (c->type == "\\MUX16") {
+ RTLIL::Cell *cell = module->addCell(remap_name(c->name), "$_MUX16_");
if (markgroups) cell->attributes["\\abcgroup"] = map_autoidx;
+ cell->setPort("\\A", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\A").as_wire()->name)]));
+ cell->setPort("\\B", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\B").as_wire()->name)]));
+ cell->setPort("\\C", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\C").as_wire()->name)]));
cell->setPort("\\D", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\D").as_wire()->name)]));
- cell->setPort("\\Q", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\Q").as_wire()->name)]));
- cell->setPort("\\C", clk_sig);
+ cell->setPort("\\E", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\E").as_wire()->name)]));
+ cell->setPort("\\F", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\F").as_wire()->name)]));
+ cell->setPort("\\G", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\G").as_wire()->name)]));
+ cell->setPort("\\H", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\H").as_wire()->name)]));
+ cell->setPort("\\I", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\I").as_wire()->name)]));
+ cell->setPort("\\J", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\J").as_wire()->name)]));
+ cell->setPort("\\K", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\K").as_wire()->name)]));
+ cell->setPort("\\L", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\L").as_wire()->name)]));
+ cell->setPort("\\M", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\M").as_wire()->name)]));
+ cell->setPort("\\N", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\N").as_wire()->name)]));
+ cell->setPort("\\O", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\O").as_wire()->name)]));
+ cell->setPort("\\P", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\P").as_wire()->name)]));
+ cell->setPort("\\S", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\S").as_wire()->name)]));
+ cell->setPort("\\T", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\T").as_wire()->name)]));
+ cell->setPort("\\U", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\U").as_wire()->name)]));
+ cell->setPort("\\V", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\V").as_wire()->name)]));
+ cell->setPort("\\Y", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\Y").as_wire()->name)]));
design->select(module, cell);
continue;
}
- log_abort();
- }
- }
- else
- {
- for (auto &it : mapped_mod->cells_)
- {
- RTLIL::Cell *c = it.second;
- cell_stats[RTLIL::unescape_id(c->type)]++;
- if (c->type == "\\_const0_" || c->type == "\\_const1_") {
- RTLIL::SigSig conn;
- conn.first = RTLIL::SigSpec(module->wires_[remap_name(c->connections().begin()->second.as_wire()->name)]);
- conn.second = RTLIL::SigSpec(c->type == "\\_const0_" ? 0 : 1, 1);
- module->connect(conn);
+ if (c->type == "\\AOI3" || c->type == "\\OAI3") {
+ RTLIL::Cell *cell = module->addCell(remap_name(c->name), "$_" + c->type.substr(1) + "_");
+ if (markgroups) cell->attributes["\\abcgroup"] = map_autoidx;
+ cell->setPort("\\A", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\A").as_wire()->name)]));
+ cell->setPort("\\B", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\B").as_wire()->name)]));
+ cell->setPort("\\C", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\C").as_wire()->name)]));
+ cell->setPort("\\Y", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\Y").as_wire()->name)]));
+ design->select(module, cell);
+ continue;
+ }
+ if (c->type == "\\AOI4" || c->type == "\\OAI4") {
+ RTLIL::Cell *cell = module->addCell(remap_name(c->name), "$_" + c->type.substr(1) + "_");
+ if (markgroups) cell->attributes["\\abcgroup"] = map_autoidx;
+ cell->setPort("\\A", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\A").as_wire()->name)]));
+ cell->setPort("\\B", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\B").as_wire()->name)]));
+ cell->setPort("\\C", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\C").as_wire()->name)]));
+ cell->setPort("\\D", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\D").as_wire()->name)]));
+ cell->setPort("\\Y", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\Y").as_wire()->name)]));
+ design->select(module, cell);
continue;
}
- if (c->type == "\\_dff_") {
+ if (c->type == "\\DFF") {
log_assert(clk_sig.size() == 1);
RTLIL::Cell *cell;
if (en_sig.size() == 0) {
@@ -1005,21 +1069,57 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin
design->select(module, cell);
continue;
}
- RTLIL::Cell *cell = module->addCell(remap_name(c->name), c->type);
- if (markgroups) cell->attributes["\\abcgroup"] = map_autoidx;
- cell->parameters = c->parameters;
- for (auto &conn : c->connections()) {
- RTLIL::SigSpec newsig;
- for (auto &c : conn.second.chunks()) {
- if (c.width == 0)
- continue;
- log_assert(c.width == 1);
- newsig.append(module->wires_[remap_name(c.wire->name)]);
- }
- cell->setPort(conn.first, newsig);
+ }
+
+ cell_stats[RTLIL::unescape_id(c->type)]++;
+
+ if (c->type == "\\_const0_" || c->type == "\\_const1_") {
+ RTLIL::SigSig conn;
+ conn.first = RTLIL::SigSpec(module->wires_[remap_name(c->connections().begin()->second.as_wire()->name)]);
+ conn.second = RTLIL::SigSpec(c->type == "\\_const0_" ? 0 : 1, 1);
+ module->connect(conn);
+ continue;
+ }
+
+ if (c->type == "\\_dff_") {
+ log_assert(clk_sig.size() == 1);
+ RTLIL::Cell *cell;
+ if (en_sig.size() == 0) {
+ cell = module->addCell(remap_name(c->name), clk_polarity ? "$_DFF_P_" : "$_DFF_N_");
+ } else {
+ log_assert(en_sig.size() == 1);
+ cell = module->addCell(remap_name(c->name), stringf("$_DFFE_%c%c_", clk_polarity ? 'P' : 'N', en_polarity ? 'P' : 'N'));
+ cell->setPort("\\E", en_sig);
}
+ if (markgroups) cell->attributes["\\abcgroup"] = map_autoidx;
+ cell->setPort("\\D", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\D").as_wire()->name)]));
+ cell->setPort("\\Q", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\Q").as_wire()->name)]));
+ cell->setPort("\\C", clk_sig);
design->select(module, cell);
+ continue;
+ }
+
+ if (c->type == "$lut" && GetSize(c->getPort("\\A")) == 1 && c->getParam("\\LUT").as_int() == 2) {
+ SigSpec my_a = module->wires_[remap_name(c->getPort("\\A").as_wire()->name)];
+ SigSpec my_y = module->wires_[remap_name(c->getPort("\\Y").as_wire()->name)];
+ module->connect(my_y, my_a);
+ continue;
+ }
+
+ RTLIL::Cell *cell = module->addCell(remap_name(c->name), c->type);
+ if (markgroups) cell->attributes["\\abcgroup"] = map_autoidx;
+ cell->parameters = c->parameters;
+ for (auto &conn : c->connections()) {
+ RTLIL::SigSpec newsig;
+ for (auto &c : conn.second.chunks()) {
+ if (c.width == 0)
+ continue;
+ log_assert(c.width == 1);
+ newsig.append(module->wires_[remap_name(c.wire->name)]);
+ }
+ cell->setPort(conn.first, newsig);
}
+ design->select(module, cell);
}
for (auto conn : mapped_mod->connections()) {
@@ -1081,14 +1181,18 @@ struct AbcPass : public Pass {
log("library to a target architecture.\n");
log("\n");
log(" -exe <command>\n");
- log(" use the specified command name instead of \"yosys-abc\" to execute ABC.\n");
+#ifdef ABCEXTERNAL
+ log(" use the specified command instead of \"" ABCEXTERNAL "\" to execute ABC.\n");
+#else
+ log(" use the specified command instead of \"<yosys-bindir>/yosys-abc\" to execute ABC.\n");
+#endif
log(" This can e.g. be used to call a specific version of ABC or a wrapper.\n");
log("\n");
log(" -script <file>\n");
log(" use the specified ABC script file instead of the default script.\n");
log("\n");
log(" if <file> starts with a plus sign (+), then the rest of the filename\n");
- log(" string is interprated as the command string to be passed to ABC. the\n");
+ log(" string is interpreted as the command string to be passed to ABC. The\n");
log(" leading plus sign is removed and all commas (,) in the string are\n");
log(" replaced with blanks before the string is passed to ABC.\n");
log("\n");
@@ -1100,9 +1204,15 @@ struct AbcPass : public Pass {
log(" for -liberty with -constr:\n");
log("%s\n", fold_abc_cmd(ABC_COMMAND_CTR).c_str());
log("\n");
- log(" for -lut:\n");
+ log(" for -lut/-luts (only one LUT size):\n");
+ log("%s\n", fold_abc_cmd(ABC_COMMAND_LUT "; lutpack").c_str());
+ log("\n");
+ log(" for -lut/-luts (different LUT sizes):\n");
log("%s\n", fold_abc_cmd(ABC_COMMAND_LUT).c_str());
log("\n");
+ log(" for -sop:\n");
+ log("%s\n", fold_abc_cmd(ABC_COMMAND_SOP).c_str());
+ log("\n");
log(" otherwise:\n");
log("%s\n", fold_abc_cmd(ABC_COMMAND_DFL).c_str());
log("\n");
@@ -1116,9 +1226,12 @@ struct AbcPass : public Pass {
log(" for -liberty with -constr:\n");
log("%s\n", fold_abc_cmd(ABC_FAST_COMMAND_CTR).c_str());
log("\n");
- log(" for -lut:\n");
+ log(" for -lut/-luts:\n");
log("%s\n", fold_abc_cmd(ABC_FAST_COMMAND_LUT).c_str());
log("\n");
+ log(" for -sop:\n");
+ log("%s\n", fold_abc_cmd(ABC_FAST_COMMAND_SOP).c_str());
+ log("\n");
log(" otherwise:\n");
log("%s\n", fold_abc_cmd(ABC_FAST_COMMAND_DFL).c_str());
log("\n");
@@ -1141,6 +1254,14 @@ struct AbcPass : public Pass {
log(" set delay target. the string {D} in the default scripts above is\n");
log(" replaced by this option when used, and an empty string otherwise.\n");
log("\n");
+ log(" -I <num>\n");
+ log(" maximum number of SOP inputs.\n");
+ log(" (replaces {I} in the default scripts above)\n");
+ log("\n");
+ log(" -P <num>\n");
+ log(" maximum number of SOP products.\n");
+ log(" (replaces {P} in the default scripts above)\n");
+ log("\n");
log(" -lut <width>\n");
log(" generate netlist using luts of (max) the specified width.\n");
log("\n");
@@ -1150,6 +1271,22 @@ struct AbcPass : public Pass {
log(" the area cost doubles with each additional input bit. the delay cost\n");
log(" is still constant for all lut widths.\n");
log("\n");
+ log(" -luts <cost1>,<cost2>,<cost3>,<sizeN>:<cost4-N>,..\n");
+ log(" generate netlist using luts. Use the specified costs for luts with 1,\n");
+ log(" 2, 3, .. inputs.\n");
+ log("\n");
+ log(" -sop\n");
+ log(" map to sum-of-product cells and inverters\n");
+ log("\n");
+ // log(" -mux4, -mux8, -mux16\n");
+ // log(" try to extract 4-input, 8-input, and/or 16-input muxes\n");
+ // log(" (ignored when used with -liberty or -lut)\n");
+ // log("\n");
+ log(" -g type1,type2,...\n");
+ log(" Map the the specified list of gate types. Supported gates types are:\n");
+ log(" AND, NAND, OR, NOR, XOR, XNOR, MUX, AOI3, OAI3, AOI4, OAI4.\n");
+ log(" (The NOT gate is always added to this list automatically.)\n");
+ log("\n");
log(" -dff\n");
log(" also pass $_DFF_?_ and $_DFFE_??_ cells through ABC. modules with many\n");
log(" clock domains are automatically partitioned in clock domains and each\n");
@@ -1161,7 +1298,7 @@ struct AbcPass : public Pass {
log("\n");
log(" -keepff\n");
log(" set the \"keep\" attribute on flip-flop output wires. (and thus preserve\n");
- log(" them, for example for equivialence checking.)\n");
+ log(" them, for example for equivalence checking.)\n");
log("\n");
log(" -nocleanup\n");
log(" when this option is used, the temporary files created by this pass\n");
@@ -1187,20 +1324,32 @@ struct AbcPass : public Pass {
}
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
{
- log_header("Executing ABC pass (technology mapping using ABC).\n");
+ log_header(design, "Executing ABC pass (technology mapping using ABC).\n");
log_push();
+#ifdef ABCEXTERNAL
+ std::string exe_file = ABCEXTERNAL;
+#else
std::string exe_file = proc_self_dirname() + "yosys-abc";
- std::string script_file, liberty_file, constr_file, clk_str, delay_target;
+#endif
+ std::string script_file, liberty_file, constr_file, clk_str;
+ std::string delay_target, sop_inputs, sop_products;
bool fast_mode = false, dff_mode = false, keepff = false, cleanup = true;
- bool show_tempdir = false;
- int lut_mode = 0, lut_mode2 = 0;
+ bool show_tempdir = false, sop_mode = false;
+ vector<int> lut_costs;
markgroups = false;
+ map_mux4 = false;
+ map_mux8 = false;
+ map_mux16 = false;
+ enabled_gates.clear();
+
#ifdef _WIN32
+#ifndef ABCEXTERNAL
if (!check_file_exists(exe_file + ".exe") && check_file_exists(proc_self_dirname() + "..\\yosys-abc.exe"))
exe_file = proc_self_dirname() + "..\\yosys-abc";
#endif
+#endif
size_t argidx;
char pwd [PATH_MAX];
@@ -1216,19 +1365,19 @@ struct AbcPass : public Pass {
}
if (arg == "-script" && argidx+1 < args.size()) {
script_file = args[++argidx];
- if (!script_file.empty() && script_file[0] != '/' && script_file[0] != '+')
+ if (!script_file.empty() && !is_absolute_path(script_file) && script_file[0] != '+')
script_file = std::string(pwd) + "/" + script_file;
continue;
}
if (arg == "-liberty" && argidx+1 < args.size()) {
liberty_file = args[++argidx];
- if (!liberty_file.empty() && liberty_file[0] != '/')
+ if (!liberty_file.empty() && !is_absolute_path(liberty_file))
liberty_file = std::string(pwd) + "/" + liberty_file;
continue;
}
if (arg == "-constr" && argidx+1 < args.size()) {
constr_file = args[++argidx];
- if (!constr_file.empty() && constr_file[0] != '/')
+ if (!constr_file.empty() && !is_absolute_path(constr_file))
constr_file = std::string(pwd) + "/" + constr_file;
continue;
}
@@ -1236,9 +1385,18 @@ struct AbcPass : public Pass {
delay_target = "-D " + args[++argidx];
continue;
}
+ if (arg == "-I" && argidx+1 < args.size()) {
+ sop_inputs = "-I " + args[++argidx];
+ continue;
+ }
+ if (arg == "-P" && argidx+1 < args.size()) {
+ sop_products = "-P " + args[++argidx];
+ continue;
+ }
if (arg == "-lut" && argidx+1 < args.size()) {
string arg = args[++argidx];
size_t pos = arg.find_first_of(':');
+ int lut_mode = 0, lut_mode2 = 0;
if (pos != string::npos) {
lut_mode = atoi(arg.substr(0, pos).c_str());
lut_mode2 = atoi(arg.substr(pos+1).c_str());
@@ -1246,6 +1404,62 @@ struct AbcPass : public Pass {
lut_mode = atoi(arg.c_str());
lut_mode2 = lut_mode;
}
+ lut_costs.clear();
+ for (int i = 0; i < lut_mode; i++)
+ lut_costs.push_back(1);
+ for (int i = lut_mode; i < lut_mode2; i++)
+ lut_costs.push_back(2 << (i - lut_mode));
+ continue;
+ }
+ if (arg == "-luts" && argidx+1 < args.size()) {
+ lut_costs.clear();
+ for (auto &tok : split_tokens(args[++argidx], ",")) {
+ auto parts = split_tokens(tok, ":");
+ if (GetSize(parts) == 0 && !lut_costs.empty())
+ lut_costs.push_back(lut_costs.back());
+ else if (GetSize(parts) == 1)
+ lut_costs.push_back(atoi(parts.at(0).c_str()));
+ else if (GetSize(parts) == 2)
+ while (GetSize(lut_costs) < atoi(parts.at(0).c_str()))
+ lut_costs.push_back(atoi(parts.at(1).c_str()));
+ else
+ log_cmd_error("Invalid -luts syntax.\n");
+ }
+ continue;
+ }
+ if (arg == "-sop") {
+ sop_mode = true;
+ continue;
+ }
+ if (arg == "-mux4") {
+ map_mux4 = true;
+ continue;
+ }
+ if (arg == "-mux8") {
+ map_mux8 = true;
+ continue;
+ }
+ if (arg == "-mux16") {
+ map_mux16 = true;
+ continue;
+ }
+ if (arg == "-g" && argidx+1 < args.size()) {
+ for (auto g : split_tokens(args[++argidx], ",")) {
+ if (g == "AND") goto ok_gate;
+ if (g == "NAND") goto ok_gate;
+ if (g == "OR") goto ok_gate;
+ if (g == "NOR") goto ok_gate;
+ if (g == "XOR") goto ok_gate;
+ if (g == "XNOR") goto ok_gate;
+ if (g == "MUX") goto ok_gate;
+ if (g == "AOI3") goto ok_gate;
+ if (g == "OAI3") goto ok_gate;
+ if (g == "AOI4") goto ok_gate;
+ if (g == "OAI4") goto ok_gate;
+ cmd_error(args, argidx, stringf("Unsupported gate type: %s", g.c_str()));
+ ok_gate:
+ enabled_gates.insert(g);
+ }
continue;
}
if (arg == "-fast") {
@@ -1281,7 +1495,7 @@ struct AbcPass : public Pass {
}
extra_args(args, argidx, design);
- if (lut_mode != 0 && !liberty_file.empty())
+ if (!lut_costs.empty() && !liberty_file.empty())
log_cmd_error("Got -lut and -liberty! This two options are exclusive.\n");
if (!constr_file.empty() && liberty_file.empty())
log_cmd_error("Got -constr but no -liberty!\n");
@@ -1290,7 +1504,8 @@ struct AbcPass : public Pass {
if (mod->processes.size() > 0)
log("Skipping module %s as it contains processes.\n", log_id(mod));
else if (!dff_mode || !clk_str.empty())
- abc_module(design, mod, script_file, exe_file, liberty_file, constr_file, cleanup, lut_mode, lut_mode2, dff_mode, clk_str, keepff, delay_target, fast_mode, mod->selected_cells(), show_tempdir);
+ abc_module(design, mod, script_file, exe_file, liberty_file, constr_file, cleanup, lut_costs, dff_mode, clk_str, keepff,
+ delay_target, sop_inputs, sop_products, fast_mode, mod->selected_cells(), show_tempdir, sop_mode);
else
{
assign_map.set(mod);
@@ -1303,7 +1518,7 @@ struct AbcPass : public Pass {
std::set<RTLIL::Cell*> expand_queue_up, next_expand_queue_up;
std::set<RTLIL::Cell*> expand_queue_down, next_expand_queue_down;
- typedef std::tuple<bool, RTLIL::SigSpec, bool, RTLIL::SigSpec> clkdomain_t;
+ typedef tuple<bool, RTLIL::SigSpec, bool, RTLIL::SigSpec> clkdomain_t;
std::map<clkdomain_t, std::vector<RTLIL::Cell*>> assigned_cells;
std::map<RTLIL::Cell*, clkdomain_t> assigned_cells_reverse;
@@ -1423,7 +1638,7 @@ struct AbcPass : public Pass {
assigned_cells_reverse[cell] = key;
}
- log_header("Summary of detected clock domains:\n");
+ log_header(design, "Summary of detected clock domains:\n");
for (auto &it : assigned_cells)
log(" %d cells in clk=%s%s, en=%s%s\n", GetSize(it.second),
std::get<0>(it.first) ? "" : "!", log_signal(std::get<1>(it.first)),
@@ -1434,8 +1649,8 @@ struct AbcPass : public Pass {
clk_sig = assign_map(std::get<1>(it.first));
en_polarity = std::get<2>(it.first);
en_sig = assign_map(std::get<3>(it.first));
- abc_module(design, mod, script_file, exe_file, liberty_file, constr_file, cleanup, lut_mode, lut_mode2,
- !clk_sig.empty(), "$", keepff, delay_target, fast_mode, it.second, show_tempdir);
+ abc_module(design, mod, script_file, exe_file, liberty_file, constr_file, cleanup, lut_costs, !clk_sig.empty(), "$",
+ keepff, delay_target, sop_inputs, sop_products, fast_mode, it.second, show_tempdir, sop_mode);
assign_map.set(mod);
}
}
@@ -1447,5 +1662,5 @@ struct AbcPass : public Pass {
log_pop();
}
} AbcPass;
-
+
PRIVATE_NAMESPACE_END
diff --git a/passes/techmap/aigmap.cc b/passes/techmap/aigmap.cc
new file mode 100644
index 000000000..b9ac7aded
--- /dev/null
+++ b/passes/techmap/aigmap.cc
@@ -0,0 +1,149 @@
+/*
+ * 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/cellaigs.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct AigmapPass : public Pass {
+ AigmapPass() : Pass("aigmap", "map logic to and-inverter-graph circuit") { }
+ virtual void help()
+ {
+ log("\n");
+ log(" aigmap [options] [selection]\n");
+ log("\n");
+ log("Replace all logic cells with circuits made of only $_AND_ and\n");
+ log("$_NOT_ cells.\n");
+ log("\n");
+ log(" -nand\n");
+ log(" Enable creation of $_NAND_ cells\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ bool nand_mode = false;
+
+ log_header(design, "Executing AIGMAP pass (map logic to AIG).\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ if (args[argidx] == "-nand") {
+ nand_mode = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto module : design->selected_modules())
+ {
+ vector<Cell*> replaced_cells;
+ int not_replaced_count = 0;
+ dict<IdString, int> stat_replaced;
+ dict<IdString, int> stat_not_replaced;
+ int orig_num_cells = GetSize(module->cells());
+
+ for (auto cell : module->selected_cells())
+ {
+ Aig aig(cell);
+
+ if (cell->type == "$_AND_" || cell->type == "$_NOT_")
+ aig.name.clear();
+
+ if (nand_mode && cell->type == "$_NAND_")
+ aig.name.clear();
+
+ if (aig.name.empty()) {
+ not_replaced_count++;
+ stat_not_replaced[cell->type]++;
+ continue;
+ }
+
+ vector<SigBit> sigs;
+ dict<pair<int, int>, SigBit> and_cache;
+
+ for (int node_idx = 0; node_idx < GetSize(aig.nodes); node_idx++)
+ {
+ SigBit bit;
+ auto &node = aig.nodes[node_idx];
+
+ if (node.portbit >= 0) {
+ bit = cell->getPort(node.portname)[node.portbit];
+ } else if (node.left_parent < 0 && node.right_parent < 0) {
+ bit = node.inverter ? State::S1 : State::S0;
+ goto skip_inverter;
+ } else {
+ SigBit A = sigs.at(node.left_parent);
+ SigBit B = sigs.at(node.right_parent);
+ if (nand_mode && node.inverter) {
+ bit = module->NandGate(NEW_ID, A, B);
+ goto skip_inverter;
+ } else {
+ pair<int, int> key(node.left_parent, node.right_parent);
+ if (and_cache.count(key))
+ bit = and_cache.at(key);
+ else
+ bit = module->AndGate(NEW_ID, A, B);
+ }
+ }
+
+ if (node.inverter)
+ bit = module->NotGate(NEW_ID, bit);
+
+ skip_inverter:
+ for (auto &op : node.outports)
+ module->connect(cell->getPort(op.first)[op.second], bit);
+
+ sigs.push_back(bit);
+ }
+
+ replaced_cells.push_back(cell);
+ stat_replaced[cell->type]++;
+ }
+
+ if (not_replaced_count == 0 && replaced_cells.empty())
+ continue;
+
+ log("Module %s: replaced %d cells with %d new cells, skipped %d cells.\n", log_id(module),
+ GetSize(replaced_cells), GetSize(module->cells()) - orig_num_cells, not_replaced_count);
+
+ if (!stat_replaced.empty()) {
+ stat_replaced.sort();
+ log(" replaced %d cell types:\n", GetSize(stat_replaced));
+ for (auto &it : stat_replaced)
+ log("%8d %s\n", it.second, log_id(it.first));
+ }
+
+ if (!stat_not_replaced.empty()) {
+ stat_not_replaced.sort();
+ log(" not replaced %d cell types:\n", GetSize(stat_not_replaced));
+ for (auto &it : stat_not_replaced)
+ log("%8d %s\n", it.second, log_id(it.first));
+ }
+
+ for (auto cell : replaced_cells)
+ module->remove(cell);
+ }
+ }
+} AigmapPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/techmap/alumacc.cc b/passes/techmap/alumacc.cc
index dcffed94d..9f6dd02d0 100644
--- a/passes/techmap/alumacc.cc
+++ b/passes/techmap/alumacc.cc
@@ -2,11 +2,11 @@
* 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
@@ -40,7 +40,7 @@ struct AlumaccWorker
{
std::vector<RTLIL::Cell*> cells;
RTLIL::SigSpec a, b, c, y;
- std::vector<std::tuple<bool, bool, bool, bool, RTLIL::SigSpec>> cmp;
+ std::vector<tuple<bool, bool, bool, bool, RTLIL::SigSpec>> cmp;
bool is_signed, invert_b;
RTLIL::Cell *alu_cell;
@@ -98,9 +98,9 @@ struct AlumaccWorker
}
};
- std::map<RTLIL::SigBit, int> bit_users;
- std::map<RTLIL::SigSpec, maccnode_t*> sig_macc;
- std::map<RTLIL::SigSig, std::set<alunode_t*>> sig_alu;
+ dict<RTLIL::SigBit, int> bit_users;
+ dict<RTLIL::SigSpec, maccnode_t*> sig_macc;
+ dict<RTLIL::SigSig, pool<alunode_t*, hash_ptr_ops>> sig_alu;
int macc_counter, alu_counter;
AlumaccWorker(RTLIL::Module *module) : module(module), sigmap(module)
@@ -138,7 +138,7 @@ struct AlumaccWorker
n->users = 0;
for (auto bit : n->y)
- n->users = std::max(n->users, bit_users.at(bit) - 1);
+ n->users = max(n->users, bit_users.at(bit) - 1);
if (cell->type.in("$pos", "$neg"))
{
@@ -215,7 +215,7 @@ struct AlumaccWorker
{
while (1)
{
- std::set<maccnode_t*> delete_nodes;
+ pool<maccnode_t*, hash_ptr_ops> delete_nodes;
for (auto &it : sig_macc)
{
@@ -267,7 +267,7 @@ struct AlumaccWorker
void macc_to_alu()
{
- std::set<maccnode_t*> delete_nodes;
+ pool<maccnode_t*, hash_ptr_ops> delete_nodes;
for (auto &it : sig_macc)
{
@@ -409,7 +409,7 @@ struct AlumaccWorker
n->a = A;
n->b = B;
n->c = RTLIL::S1;
- n->y = module->addWire(NEW_ID, std::max(GetSize(A), GetSize(B)));
+ n->y = module->addWire(NEW_ID, max(GetSize(A), GetSize(B)));
n->is_signed = is_signed;
n->invert_b = true;
sig_alu[RTLIL::SigSig(A, B)].insert(n);
@@ -544,7 +544,7 @@ struct AlumaccPass : public Pass {
}
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
{
- log_header("Executing ALUMACC pass (create $alu and $macc cells).\n");
+ log_header(design, "Executing ALUMACC pass (create $alu and $macc cells).\n");
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++) {
@@ -563,5 +563,5 @@ struct AlumaccPass : public Pass {
}
}
} AlumaccPass;
-
+
PRIVATE_NAMESPACE_END
diff --git a/passes/techmap/deminout.cc b/passes/techmap/deminout.cc
new file mode 100644
index 000000000..ed4e45762
--- /dev/null
+++ b/passes/techmap/deminout.cc
@@ -0,0 +1,116 @@
+/*
+ * 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 DeminoutPass : public Pass {
+ DeminoutPass() : Pass("deminout", "demote inout ports to input or output") { }
+ virtual void help()
+ {
+ log("\n");
+ log(" deminout [options] [selection]\n");
+ log("\n");
+ log("\"Demote\" inout ports to input or output ports, if possible.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ log_header(design, "Executing DEMINOUT pass (demote inout ports to input or output).\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ // if (args[argidx] == "-bits") {
+ // flag_bits = true;
+ // continue;
+ // }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ bool keep_running = true;
+
+ while (keep_running)
+ {
+ keep_running = false;
+
+ for (auto module : design->selected_modules())
+ {
+ SigMap sigmap(module);
+ pool<SigBit> bits_written, bits_used, bits_inout;
+ dict<SigBit, int> bits_numports;
+
+ for (auto wire : module->wires())
+ if (wire->port_id)
+ for (auto bit : sigmap(wire))
+ bits_numports[bit]++;
+
+ for (auto cell : module->cells())
+ for (auto &conn : cell->connections())
+ {
+ bool cellport_out = cell->output(conn.first) || !cell->known();
+ bool cellport_in = cell->input(conn.first) || !cell->known();
+
+ if (cellport_out && cellport_in)
+ for (auto bit : sigmap(conn.second))
+ bits_inout.insert(bit);
+
+ if (cellport_out)
+ for (auto bit : sigmap(conn.second))
+ bits_written.insert(bit);
+
+ if (cellport_in)
+ for (auto bit : sigmap(conn.second))
+ bits_used.insert(bit);
+ }
+
+ for (auto wire : module->selected_wires())
+ if (wire->port_input && wire->port_output)
+ {
+ bool new_input = false;
+ bool new_output = false;
+
+ for (auto bit : sigmap(wire))
+ {
+ if (bits_numports[bit] > 1 || bits_inout.count(bit))
+ new_input = true, new_output = true;
+
+ if (bits_written.count(bit))
+ new_output = true;
+ else if (bits_used.count(bit))
+ new_input = true;
+ }
+
+ if (new_input != new_output) {
+ log("Demoting inout port %s.%s to %s.\n", log_id(module), log_id(wire), new_input ? "input" : "output");
+ wire->port_input = new_input;
+ wire->port_output = new_output;
+ keep_running = true;
+ }
+ }
+ }
+ }
+ }
+} DeminoutPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/techmap/dff2dffe.cc b/passes/techmap/dff2dffe.cc
index 17549bd06..1b8920bb7 100644
--- a/passes/techmap/dff2dffe.cc
+++ b/passes/techmap/dff2dffe.cc
@@ -2,11 +2,11 @@
* 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
@@ -27,12 +27,12 @@ PRIVATE_NAMESPACE_BEGIN
struct Dff2dffeWorker
{
+ const dict<IdString, IdString> &direct_dict;
+
RTLIL::Module *module;
SigMap sigmap;
CellTypes ct;
- RTLIL::IdString direct_to;
-
typedef std::pair<RTLIL::Cell*, int> cell_int_t;
std::map<RTLIL::SigBit, cell_int_t> bit2mux;
std::vector<RTLIL::Cell*> dff_cells;
@@ -42,8 +42,8 @@ struct Dff2dffeWorker
typedef std::set<pattern_t> patterns_t;
- Dff2dffeWorker(RTLIL::Module *module, RTLIL::IdString direct_from, RTLIL::IdString direct_to) :
- module(module), sigmap(module), ct(module->design), direct_to(direct_to)
+ Dff2dffeWorker(RTLIL::Module *module, const dict<IdString, IdString> &direct_dict) :
+ direct_dict(direct_dict), module(module), sigmap(module), ct(module->design)
{
for (auto wire : module->wires()) {
if (wire->port_output)
@@ -57,11 +57,11 @@ struct Dff2dffeWorker
for (int i = 0; i < GetSize(sig_y); i++)
bit2mux[sig_y[i]] = cell_int_t(cell, i);
}
- if (direct_to.empty()) {
+ if (direct_dict.empty()) {
if (cell->type == "$dff" || cell->type == "$_DFF_N_" || cell->type == "$_DFF_P_")
dff_cells.push_back(cell);
} else {
- if (cell->type == direct_from)
+ if (direct_dict.count(cell->type))
dff_cells.push_back(cell);
}
for (auto conn : cell->connections()) {
@@ -206,10 +206,10 @@ struct Dff2dffeWorker
new_sig_d.append(sig_d[i]);
new_sig_q.append(sig_q[i]);
}
- if (!direct_to.empty()) {
- log(" converting %s cell %s to %s for %s -> %s.\n", log_id(dff_cell->type), log_id(dff_cell), log_id(direct_to), log_signal(new_sig_d), log_signal(new_sig_q));
+ if (!direct_dict.empty()) {
+ log(" converting %s cell %s to %s for %s -> %s.\n", log_id(dff_cell->type), log_id(dff_cell), log_id(direct_dict.at(dff_cell->type)), log_signal(new_sig_d), log_signal(new_sig_q));
dff_cell->setPort("\\E", make_patterns_logic(it.first, true));
- dff_cell->type = direct_to;
+ dff_cell->type = direct_dict.at(dff_cell->type);
} else
if (dff_cell->type == "$dff") {
RTLIL::Cell *new_cell = module->addDffe(NEW_ID, dff_cell->getPort("\\CLK"), make_patterns_logic(it.first, false),
@@ -222,7 +222,7 @@ struct Dff2dffeWorker
}
}
- if (!direct_to.empty())
+ if (!direct_dict.empty())
return;
if (remaining_indices.empty()) {
@@ -243,9 +243,11 @@ struct Dff2dffeWorker
void run()
{
- log("Transforming $dff to $dffe cells in module %s:\n", log_id(module));
- for (auto dff_cell : dff_cells)
+ log("Transforming FF to FF+Enable cells in module %s:\n", log_id(module));
+ for (auto dff_cell : dff_cells) {
+ // log("Handling candidate %s:\n", log_id(dff_cell));
handle_dff_cell(dff_cell);
+ }
}
};
@@ -255,7 +257,7 @@ struct Dff2dffePass : public Pass {
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
- log(" dff2dffe [selection]\n");
+ log(" dff2dffe [options] [selection]\n");
log("\n");
log("This pass transforms $dff cells driven by a tree of multiplexers with one or\n");
log("more feedback paths to $dffe cells. It also works on gate-level cells such as\n");
@@ -271,16 +273,22 @@ struct Dff2dffePass : public Pass {
log(" <external_gate_type> is the cell type name for a cell with an\n");
log(" identical interface to the <internal_gate_type>, except it\n");
log(" also has an high-active enable port 'E'.\n");
- log(" Usually <external_gate_type> is an intemediate cell type\n");
+ log(" Usually <external_gate_type> is an intermediate cell type\n");
log(" that is then translated to the final type using 'techmap'.\n");
log("\n");
+ log(" -direct-match <pattern>\n");
+ log(" like -direct for all DFF cell types matching the expression.\n");
+ log(" this will use $__DFFE_* as <external_gate_type> matching the\n");
+ log(" internal gate type $_DFF_*_, except for $_DFF_[NP]_, which is\n");
+ log(" converted to $_DFFE_[NP]_.\n");
+ log("\n");
}
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
{
- log_header("Executing DFF2DFFE pass (transform $dff to $dffe where applicable).\n");
+ log_header(design, "Executing DFF2DFFE pass (transform $dff to $dffe where applicable).\n");
bool unmap_mode = false;
- RTLIL::IdString direct_from, direct_to;
+ dict<IdString, IdString> direct_dict;
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++) {
@@ -289,14 +297,38 @@ struct Dff2dffePass : public Pass {
continue;
}
if (args[argidx] == "-direct" && argidx + 2 < args.size()) {
- direct_from = RTLIL::escape_id(args[++argidx]);
- direct_to = RTLIL::escape_id(args[++argidx]);
+ string direct_from = RTLIL::escape_id(args[++argidx]);
+ string direct_to = RTLIL::escape_id(args[++argidx]);
+ direct_dict[direct_from] = direct_to;
+ continue;
+ }
+ if (args[argidx] == "-direct-match" && argidx + 1 < args.size()) {
+ bool found_match = false;
+ const char *pattern = args[++argidx].c_str();
+ if (patmatch(pattern, "$_DFF_P_" )) found_match = true, direct_dict["$_DFF_P_" ] = "$_DFFE_PP_";
+ if (patmatch(pattern, "$_DFF_N_" )) found_match = true, direct_dict["$_DFF_N_" ] = "$_DFFE_NP_";
+ if (patmatch(pattern, "$_DFF_NN0_")) found_match = true, direct_dict["$_DFF_NN0_"] = "$__DFFE_NN0";
+ if (patmatch(pattern, "$_DFF_NN1_")) found_match = true, direct_dict["$_DFF_NN1_"] = "$__DFFE_NN1";
+ if (patmatch(pattern, "$_DFF_NP0_")) found_match = true, direct_dict["$_DFF_NP0_"] = "$__DFFE_NP0";
+ if (patmatch(pattern, "$_DFF_NP1_")) found_match = true, direct_dict["$_DFF_NP1_"] = "$__DFFE_NP1";
+ if (patmatch(pattern, "$_DFF_PN0_")) found_match = true, direct_dict["$_DFF_PN0_"] = "$__DFFE_PN0";
+ if (patmatch(pattern, "$_DFF_PN1_")) found_match = true, direct_dict["$_DFF_PN1_"] = "$__DFFE_PN1";
+ if (patmatch(pattern, "$_DFF_PP0_")) found_match = true, direct_dict["$_DFF_PP0_"] = "$__DFFE_PP0";
+ if (patmatch(pattern, "$_DFF_PP1_")) found_match = true, direct_dict["$_DFF_PP1_"] = "$__DFFE_PP1";
+ if (!found_match)
+ log_cmd_error("No cell types matched pattern '%s'.\n", pattern);
continue;
}
break;
}
extra_args(args, argidx, design);
+ if (!direct_dict.empty()) {
+ log("Selected cell types for direct conversion:\n");
+ for (auto &it : direct_dict)
+ log(" %s -> %s\n", log_id(it.first), log_id(it.second));
+ }
+
for (auto mod : design->selected_modules())
if (!mod->has_processes_warn())
{
@@ -328,10 +360,10 @@ struct Dff2dffePass : public Pass {
continue;
}
- Dff2dffeWorker worker(mod, direct_from, direct_to);
+ Dff2dffeWorker worker(mod, direct_dict);
worker.run();
}
}
} Dff2dffePass;
-
+
PRIVATE_NAMESPACE_END
diff --git a/passes/techmap/dffinit.cc b/passes/techmap/dffinit.cc
new file mode 100644
index 000000000..d737b3424
--- /dev/null
+++ b/passes/techmap/dffinit.cc
@@ -0,0 +1,135 @@
+/*
+ * 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 DffinitPass : public Pass {
+ DffinitPass() : Pass("dffinit", "set INIT param on FF cells") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" dffinit [options] [selection]\n");
+ log("\n");
+ log("This pass sets an FF cell parameter to the the initial value of the net it\n");
+ log("drives. (This is primarily used in FPGA flows.)\n");
+ log("\n");
+ log(" -ff <cell_name> <output_port> <init_param>\n");
+ log(" operate on the specified cell type. this option can be used\n");
+ log(" multiple times.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ log_header(design, "Executing DFFINIT pass (set INIT param on FF cells).\n");
+
+ dict<IdString, dict<IdString, IdString>> ff_types;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ if (args[argidx] == "-ff" && argidx+3 < args.size()) {
+ IdString cell_name = RTLIL::escape_id(args[++argidx]);
+ IdString output_port = RTLIL::escape_id(args[++argidx]);
+ IdString init_param = RTLIL::escape_id(args[++argidx]);
+ ff_types[cell_name][output_port] = init_param;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto module : design->selected_modules())
+ {
+ SigMap sigmap(module);
+ dict<SigBit, State> init_bits;
+ pool<SigBit> cleanup_bits;
+ pool<SigBit> used_bits;
+
+ for (auto wire : module->selected_wires()) {
+ if (wire->attributes.count("\\init")) {
+ Const value = wire->attributes.at("\\init");
+ for (int i = 0; i < min(GetSize(value), GetSize(wire)); i++)
+ init_bits[sigmap(SigBit(wire, i))] = value[i];
+ }
+ if (wire->port_output)
+ for (auto bit : sigmap(wire))
+ used_bits.insert(bit);
+ }
+
+ for (auto cell : module->selected_cells())
+ {
+ for (auto it : cell->connections())
+ if (!cell->known() || cell->input(it.first))
+ for (auto bit : sigmap(it.second))
+ used_bits.insert(bit);
+
+ if (ff_types.count(cell->type) == 0)
+ continue;
+
+ for (auto &it : ff_types[cell->type])
+ {
+ if (!cell->hasPort(it.first))
+ continue;
+
+ SigSpec sig = sigmap(cell->getPort(it.first));
+ Const value;
+
+ if (cell->hasParam(it.second))
+ value = cell->getParam(it.second);
+
+ for (int i = 0; i < GetSize(sig); i++) {
+ if (init_bits.count(sig[i]) == 0)
+ continue;
+ while (GetSize(value.bits) <= i)
+ value.bits.push_back(State::S0);
+ value.bits[i] = init_bits.at(sig[i]);
+ cleanup_bits.insert(sig[i]);
+ }
+
+ log("Setting %s.%s.%s (port=%s, net=%s) to %s.\n", log_id(module), log_id(cell), log_id(it.second),
+ log_id(it.first), log_signal(sig), log_signal(value));
+ cell->setParam(it.second, value);
+ }
+ }
+
+ for (auto wire : module->selected_wires())
+ if (wire->attributes.count("\\init")) {
+ Const &value = wire->attributes.at("\\init");
+ bool do_cleanup = true;
+ for (int i = 0; i < min(GetSize(value), GetSize(wire)); i++) {
+ SigBit bit = sigmap(SigBit(wire, i));
+ if (cleanup_bits.count(bit) || !used_bits.count(bit))
+ value[i] = State::Sx;
+ else if (value[i] != State::Sx)
+ do_cleanup = false;
+ }
+ if (do_cleanup) {
+ log("Removing init attribute from wire %s.%s.\n", log_id(module), log_id(wire));
+ wire->attributes.erase("\\init");
+ }
+ }
+ }
+ }
+} DffinitPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/techmap/dfflibmap.cc b/passes/techmap/dfflibmap.cc
index b0318a0b3..c8104fb7e 100644
--- a/passes/techmap/dfflibmap.cc
+++ b/passes/techmap/dfflibmap.cc
@@ -2,11 +2,11 @@
* 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
@@ -17,8 +17,8 @@
*
*/
-#include "kernel/register.h"
-#include "kernel/log.h"
+#include "kernel/yosys.h"
+#include "kernel/sigtools.h"
#include "libparse.h"
#include <string.h>
#include <errno.h>
@@ -80,7 +80,7 @@ static bool parse_pin(LibertyAst *cell, LibertyAst *attr, std::string &pin_name,
{
if (cell == NULL || attr == NULL || attr->value.empty())
return false;
-
+
std::string value = attr->value;
for (size_t pos = value.find_first_of("\" \t()"); pos != std::string::npos; pos = value.find_first_of("\" \t()"))
@@ -108,6 +108,7 @@ static void find_cell(LibertyAst *ast, std::string cell_type, bool clkpol, bool
LibertyAst *best_cell = NULL;
std::map<std::string, char> best_cell_ports;
int best_cell_pins = 0;
+ bool best_cell_noninv = false;
double best_cell_area = 0;
if (ast->id != "library")
@@ -118,6 +119,10 @@ static void find_cell(LibertyAst *ast, std::string cell_type, bool clkpol, bool
if (cell->id != "cell" || cell->args.size() != 1)
continue;
+ LibertyAst *dn = cell->find("dont_use");
+ if (dn != NULL && dn->value == "true")
+ continue;
+
LibertyAst *ff = cell->find("ff");
if (ff == NULL)
continue;
@@ -151,6 +156,7 @@ static void find_cell(LibertyAst *ast, std::string cell_type, bool clkpol, bool
int num_pins = 0;
bool found_output = false;
+ bool found_noninv_output = false;
for (auto pin : cell->children)
{
if (pin->id != "pin" || pin->args.size() != 1)
@@ -169,8 +175,16 @@ static void find_cell(LibertyAst *ast, std::string cell_type, bool clkpol, bool
std::string value = func->value;
for (size_t pos = value.find_first_of("\" \t"); pos != std::string::npos; pos = value.find_first_of("\" \t"))
value.erase(pos, 1);
- if ((cell_next_pol == true && value == ff->args[0]) || (cell_next_pol == false && value == ff->args[1])) {
- this_cell_ports[pin->args[0]] = 'Q';
+ if (value == ff->args[0]) {
+ this_cell_ports[pin->args[0]] = cell_next_pol ? 'Q' : 'q';
+ if (cell_next_pol)
+ found_noninv_output = true;
+ found_output = true;
+ } else
+ if (value == ff->args[1]) {
+ this_cell_ports[pin->args[0]] = cell_next_pol ? 'q' : 'Q';
+ if (!cell_next_pol)
+ found_noninv_output = true;
found_output = true;
}
}
@@ -179,7 +193,7 @@ static void find_cell(LibertyAst *ast, std::string cell_type, bool clkpol, bool
this_cell_ports[pin->args[0]] = 0;
}
- if (!found_output || (best_cell != NULL && num_pins > best_cell_pins))
+ if (!found_output || (best_cell != NULL && (num_pins > best_cell_pins || (best_cell_noninv && !found_noninv_output))))
continue;
if (best_cell != NULL && num_pins == best_cell_pins && area > best_cell_area)
@@ -188,12 +202,14 @@ static void find_cell(LibertyAst *ast, std::string cell_type, bool clkpol, bool
best_cell = cell;
best_cell_pins = num_pins;
best_cell_area = area;
+ best_cell_noninv = found_noninv_output;
best_cell_ports.swap(this_cell_ports);
continue_cell_loop:;
}
if (best_cell != NULL) {
- log(" cell %s (pins=%d, area=%.2f) is a direct match for cell type %s.\n", best_cell->args[0].c_str(), best_cell_pins, best_cell_area, cell_type.c_str());
+ log(" cell %s (%sinv, pins=%d, area=%.2f) is a direct match for cell type %s.\n",
+ best_cell->args[0].c_str(), best_cell_noninv ? "non" : "", best_cell_pins, best_cell_area, cell_type.c_str());
if (prepare_mode) {
cell_mappings[cell_type].cell_name = cell_type;
cell_mappings[cell_type].ports["C"] = 'C';
@@ -213,6 +229,7 @@ static void find_cell_sr(LibertyAst *ast, std::string cell_type, bool clkpol, bo
LibertyAst *best_cell = NULL;
std::map<std::string, char> best_cell_ports;
int best_cell_pins = 0;
+ bool best_cell_noninv = false;
double best_cell_area = 0;
if (ast->id != "library")
@@ -252,6 +269,7 @@ static void find_cell_sr(LibertyAst *ast, std::string cell_type, bool clkpol, bo
int num_pins = 0;
bool found_output = false;
+ bool found_noninv_output = false;
for (auto pin : cell->children)
{
if (pin->id != "pin" || pin->args.size() != 1)
@@ -270,8 +288,16 @@ static void find_cell_sr(LibertyAst *ast, std::string cell_type, bool clkpol, bo
std::string value = func->value;
for (size_t pos = value.find_first_of("\" \t"); pos != std::string::npos; pos = value.find_first_of("\" \t"))
value.erase(pos, 1);
- if ((cell_next_pol == true && value == ff->args[0]) || (cell_next_pol == false && value == ff->args[1])) {
- this_cell_ports[pin->args[0]] = 'Q';
+ if (value == ff->args[0]) {
+ this_cell_ports[pin->args[0]] = cell_next_pol ? 'Q' : 'q';
+ if (cell_next_pol)
+ found_noninv_output = true;
+ found_output = true;
+ } else
+ if (value == ff->args[1]) {
+ this_cell_ports[pin->args[0]] = cell_next_pol ? 'q' : 'Q';
+ if (!cell_next_pol)
+ found_noninv_output = true;
found_output = true;
}
}
@@ -280,7 +306,7 @@ static void find_cell_sr(LibertyAst *ast, std::string cell_type, bool clkpol, bo
this_cell_ports[pin->args[0]] = 0;
}
- if (!found_output || (best_cell != NULL && num_pins > best_cell_pins))
+ if (!found_output || (best_cell != NULL && (num_pins > best_cell_pins || (best_cell_noninv && !found_noninv_output))))
continue;
if (best_cell != NULL && num_pins == best_cell_pins && area > best_cell_area)
@@ -289,12 +315,14 @@ static void find_cell_sr(LibertyAst *ast, std::string cell_type, bool clkpol, bo
best_cell = cell;
best_cell_pins = num_pins;
best_cell_area = area;
+ best_cell_noninv = found_noninv_output;
best_cell_ports.swap(this_cell_ports);
continue_cell_loop:;
}
if (best_cell != NULL) {
- log(" cell %s (pins=%d, area=%.2f) is a direct match for cell type %s.\n", best_cell->args[0].c_str(), best_cell_pins, best_cell_area, cell_type.c_str());
+ log(" cell %s (%sinv, pins=%d, area=%.2f) is a direct match for cell type %s.\n",
+ best_cell->args[0].c_str(), best_cell_noninv ? "non" : "", best_cell_pins, best_cell_area, cell_type.c_str());
if (prepare_mode) {
cell_mappings[cell_type].cell_name = cell_type;
cell_mappings[cell_type].ports["C"] = 'C';
@@ -406,14 +434,42 @@ static void map_sr_to_arst(const char *from, const char *to)
}
}
+static void map_adff_to_dff(const char *from, const char *to)
+{
+ if (!cell_mappings.count(from) || cell_mappings.count(to) > 0)
+ return;
+
+ char from_clk_pol YS_ATTRIBUTE(unused) = from[6];
+ char from_rst_pol = from[7];
+ char to_clk_pol YS_ATTRIBUTE(unused) = to[6];
+
+ log_assert(from_clk_pol == to_clk_pol);
+
+ log(" create mapping for %s from mapping for %s.\n", to, from);
+ cell_mappings[to].cell_name = cell_mappings[from].cell_name;
+ cell_mappings[to].ports = cell_mappings[from].ports;
+
+ for (auto &it : cell_mappings[to].ports) {
+ if (it.second == 'S' || it.second == 'R')
+ it.second = from_rst_pol == 'P' ? '0' : '1';
+ if (it.second == 's' || it.second == 'r')
+ it.second = from_rst_pol == 'P' ? '1' : '0';
+ }
+}
+
static void dfflibmap(RTLIL::Design *design, RTLIL::Module *module, bool prepare_mode)
{
log("Mapping DFF cells in module `%s':\n", module->name.c_str());
+ dict<SigBit, pool<Cell*>> notmap;
+ SigMap sigmap(module);
+
std::vector<RTLIL::Cell*> cell_list;
for (auto &it : module->cells_) {
if (design->selected(module, it.second) && cell_mappings.count(it.second->type) > 0)
cell_list.push_back(it.second);
+ if (it.second->type == "$_NOT_")
+ notmap[sigmap(it.second->getPort("\\A"))].insert(it.second);
}
std::map<std::string, int> stats;
@@ -427,6 +483,12 @@ static void dfflibmap(RTLIL::Design *design, RTLIL::Module *module, bool prepare
cell_mapping &cm = cell_mappings[cell_type];
RTLIL::Cell *new_cell = module->addCell(cell_name, prepare_mode ? cm.cell_name : "\\" + cm.cell_name);
+ bool has_q = false, has_qn = false;
+ for (auto &port : cm.ports) {
+ if (port.second == 'Q') has_q = true;
+ if (port.second == 'q') has_qn = true;
+ }
+
for (auto &port : cm.ports) {
RTLIL::SigSpec sig;
if ('A' <= port.second && port.second <= 'Z') {
@@ -435,7 +497,14 @@ static void dfflibmap(RTLIL::Design *design, RTLIL::Module *module, bool prepare
if (port.second == 'q') {
RTLIL::SigSpec old_sig = cell_connections[std::string("\\") + char(port.second - ('a' - 'A'))];
sig = module->addWire(NEW_ID, GetSize(old_sig));
- module->addNotGate(NEW_ID, sig, old_sig);
+ if (has_q && has_qn) {
+ for (auto &it : notmap[sigmap(old_sig)]) {
+ module->connect(it->getPort("\\Y"), sig);
+ it->setPort("\\Y", module->addWire(NEW_ID, GetSize(old_sig)));
+ }
+ } else {
+ module->addNotGate(NEW_ID, sig, old_sig);
+ }
} else
if ('a' <= port.second && port.second <= 'z') {
sig = cell_connections[std::string("\\") + char(port.second - ('a' - 'A'))];
@@ -444,7 +513,9 @@ static void dfflibmap(RTLIL::Design *design, RTLIL::Module *module, bool prepare
if (port.second == '0' || port.second == '1') {
sig = RTLIL::SigSpec(port.second == '0' ? 0 : 1, 1);
} else
- if (port.second != 0)
+ if (port.second == 0) {
+ sig = module->addWire(NEW_ID);
+ } else
log_abort();
new_cell->setPort("\\" + port.first, sig);
}
@@ -476,7 +547,7 @@ struct DfflibmapPass : public Pass {
}
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
{
- log_header("Executing DFFLIBMAP pass (mapping DFF cells to sequential cells from liberty file).\n");
+ log_header(design, "Executing DFFLIBMAP pass (mapping DFF cells to sequential cells from liberty file).\n");
std::string liberty_file;
bool prepare_mode = false;
@@ -487,6 +558,7 @@ struct DfflibmapPass : public Pass {
std::string arg = args[argidx];
if (arg == "-liberty" && argidx+1 < args.size()) {
liberty_file = args[++argidx];
+ rewrite_filename(liberty_file);
continue;
}
if (arg == "-prepare") {
@@ -558,7 +630,16 @@ struct DfflibmapPass : public Pass {
map_sr_to_arst("$_DFFSR_PNN_", "$_DFF_PN1_");
map_sr_to_arst("$_DFFSR_PPP_", "$_DFF_PP0_");
map_sr_to_arst("$_DFFSR_PPP_", "$_DFF_PP1_");
-
+
+ map_adff_to_dff("$_DFF_NN0_", "$_DFF_N_");
+ map_adff_to_dff("$_DFF_NN1_", "$_DFF_N_");
+ map_adff_to_dff("$_DFF_NP0_", "$_DFF_N_");
+ map_adff_to_dff("$_DFF_NP1_", "$_DFF_N_");
+ map_adff_to_dff("$_DFF_PN0_", "$_DFF_P_");
+ map_adff_to_dff("$_DFF_PN1_", "$_DFF_P_");
+ map_adff_to_dff("$_DFF_PP0_", "$_DFF_P_");
+ map_adff_to_dff("$_DFF_PP1_", "$_DFF_P_");
+
log(" final dff cell mappings:\n");
logmap_all();
@@ -569,5 +650,5 @@ struct DfflibmapPass : public Pass {
cell_mappings.clear();
}
} DfflibmapPass;
-
+
PRIVATE_NAMESPACE_END
diff --git a/passes/techmap/dffsr2dff.cc b/passes/techmap/dffsr2dff.cc
new file mode 100644
index 000000000..0d4d53627
--- /dev/null
+++ b/passes/techmap/dffsr2dff.cc
@@ -0,0 +1,213 @@
+/*
+ * 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
+
+void dffsr_worker(SigMap &sigmap, Module *module, Cell *cell)
+{
+ if (cell->type == "$dffsr")
+ {
+ int width = cell->getParam("\\WIDTH").as_int();
+ bool setpol = cell->getParam("\\SET_POLARITY").as_bool();
+ bool clrpol = cell->getParam("\\CLR_POLARITY").as_bool();
+
+ SigBit setunused = setpol ? State::S0 : State::S1;
+ SigBit clrunused = clrpol ? State::S0 : State::S1;
+
+ SigSpec setsig = sigmap(cell->getPort("\\SET"));
+ SigSpec clrsig = sigmap(cell->getPort("\\CLR"));
+
+ Const reset_val;
+ SigSpec setctrl, clrctrl;
+
+ for (int i = 0; i < width; i++)
+ {
+ SigBit setbit = setsig[i], clrbit = clrsig[i];
+
+ if (setbit == setunused) {
+ clrctrl.append(clrbit);
+ reset_val.bits.push_back(State::S0);
+ continue;
+ }
+
+ if (clrbit == clrunused) {
+ setctrl.append(setbit);
+ reset_val.bits.push_back(State::S1);
+ continue;
+ }
+
+ return;
+ }
+
+ setctrl.sort_and_unify();
+ clrctrl.sort_and_unify();
+
+ if (GetSize(setctrl) > 1 || GetSize(clrctrl) > 1)
+ return;
+
+ if (GetSize(setctrl) == 0 && GetSize(clrctrl) == 0)
+ return;
+
+ if (GetSize(setctrl) == 1 && GetSize(clrctrl) == 1) {
+ if (setpol != clrpol)
+ return;
+ if (setctrl != clrctrl)
+ return;
+ }
+
+ log("Converting %s cell %s.%s to $adff.\n", log_id(cell->type), log_id(module), log_id(cell));
+
+ if (GetSize(setctrl) == 1) {
+ cell->setPort("\\ARST", setctrl);
+ cell->setParam("\\ARST_POLARITY", setpol);
+ } else {
+ cell->setPort("\\ARST", clrctrl);
+ cell->setParam("\\ARST_POLARITY", clrpol);
+ }
+
+ cell->type = "$adff";
+ cell->unsetPort("\\SET");
+ cell->unsetPort("\\CLR");
+ cell->setParam("\\ARST_VALUE", reset_val);
+ cell->unsetParam("\\SET_POLARITY");
+ cell->unsetParam("\\CLR_POLARITY");
+
+ return;
+ }
+
+ if (cell->type.in("$_DFFSR_NNN_", "$_DFFSR_NNP_", "$_DFFSR_NPN_", "$_DFFSR_NPP_",
+ "$_DFFSR_PNN_", "$_DFFSR_PNP_", "$_DFFSR_PPN_", "$_DFFSR_PPP_"))
+ {
+ char clkpol = cell->type.c_str()[8];
+ char setpol = cell->type.c_str()[9];
+ char clrpol = cell->type.c_str()[10];
+
+ SigBit setbit = sigmap(cell->getPort("\\S"));
+ SigBit clrbit = sigmap(cell->getPort("\\R"));
+
+ SigBit setunused = setpol == 'P' ? State::S0 : State::S1;
+ SigBit clrunused = clrpol == 'P' ? State::S0 : State::S1;
+
+ IdString oldtype = cell->type;
+
+ if (setbit == setunused) {
+ cell->type = stringf("$_DFF_%c%c0_", clkpol, clrpol);
+ cell->unsetPort("\\S");
+ goto converted_gate;
+ }
+
+ if (clrbit == clrunused) {
+ cell->type = stringf("$_DFF_%c%c1_", clkpol, setpol);
+ cell->setPort("\\R", cell->getPort("\\S"));
+ cell->unsetPort("\\S");
+ goto converted_gate;
+ }
+
+ return;
+
+ converted_gate:
+ log("Converting %s cell %s.%s to %s.\n", log_id(oldtype), log_id(module), log_id(cell), log_id(cell->type));
+ return;
+ }
+}
+
+void adff_worker(SigMap &sigmap, Module *module, Cell *cell)
+{
+ if (cell->type == "$adff")
+ {
+ bool rstpol = cell->getParam("\\ARST_POLARITY").as_bool();
+ SigBit rstunused = rstpol ? State::S0 : State::S1;
+ SigSpec rstsig = sigmap(cell->getPort("\\ARST"));
+
+ if (rstsig != rstunused)
+ return;
+
+ log("Converting %s cell %s.%s to $dff.\n", log_id(cell->type), log_id(module), log_id(cell));
+
+ cell->type = "$dff";
+ cell->unsetPort("\\ARST");
+ cell->unsetParam("\\ARST_VALUE");
+ cell->unsetParam("\\ARST_POLARITY");
+
+ return;
+ }
+
+ if (cell->type.in("$_DFF_NN0_", "$_DFF_NN1_", "$_DFF_NP0_", "$_DFF_NP1_",
+ "$_DFF_PN0_", "$_DFF_PN1_", "$_DFF_PP0_", "$_DFF_PP1_"))
+ {
+ char clkpol = cell->type.c_str()[6];
+ char rstpol = cell->type.c_str()[7];
+
+ SigBit rstbit = sigmap(cell->getPort("\\R"));
+ SigBit rstunused = rstpol == 'P' ? State::S0 : State::S1;
+
+ if (rstbit != rstunused)
+ return;
+
+ IdString newtype = stringf("$_DFF_%c_", clkpol);
+ log("Converting %s cell %s.%s to %s.\n", log_id(cell->type), log_id(module), log_id(cell), log_id(newtype));
+
+ cell->type = newtype;
+ cell->unsetPort("\\R");
+
+ return;
+ }
+}
+
+struct Dffsr2dffPass : public Pass {
+ Dffsr2dffPass() : Pass("dffsr2dff", "convert DFFSR cells to simpler FF cell types") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" dffsr2dff [options] [selection]\n");
+ log("\n");
+ log("This pass converts DFFSR cells ($dffsr, $_DFFSR_???_) and ADFF cells ($adff,\n");
+ log("$_DFF_???_) to simpler FF cell types when any of the set/reset inputs is unused.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ log_header(design, "Executing DFFSR2DFF pass (mapping DFFSR cells to simpler FFs).\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ // if (args[argidx] == "-v") {
+ // continue;
+ // }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto module : design->selected_modules()) {
+ SigMap sigmap(module);
+ for (auto cell : module->selected_cells()) {
+ dffsr_worker(sigmap, module, cell);
+ adff_worker(sigmap, module, cell);
+ }
+ }
+ }
+} Dffsr2dffPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/techmap/extract.cc b/passes/techmap/extract.cc
index ff99040e1..71e29c60b 100644
--- a/passes/techmap/extract.cc
+++ b/passes/techmap/extract.cc
@@ -2,11 +2,11 @@
* 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
@@ -130,7 +130,7 @@ public:
RTLIL::SigSpec needleSig = conn.second;
RTLIL::SigSpec haystackSig = haystackCell->getPort(portMapping.at(conn.first.str()));
- for (int i = 0; i < std::min(needleSig.size(), haystackSig.size()); i++) {
+ for (int i = 0; i < min(needleSig.size(), haystackSig.size()); i++) {
RTLIL::Wire *needleWire = needleSig[i].wire, *haystackWire = haystackSig[i].wire;
if (needleWire != lastNeedleWire || haystackWire != lastHaystackWire)
if (!compareAttributes(wire_attr, needleWire ? needleWire->attributes : emptyAttr, haystackWire ? haystackWire->attributes : emptyAttr))
@@ -361,7 +361,7 @@ struct ExtractPass : public Pass {
log("\n");
log("This pass looks for subcircuits that are isomorphic to any of the modules\n");
log("in the given map file and replaces them with instances of this modules. The\n");
- log("map file can be a verilog source file (*.v) or an ilang file (*.il).\n");
+ log("map file can be a Verilog source file (*.v) or an ilang file (*.il).\n");
log("\n");
log(" -map <map_file>\n");
log(" use the modules in this file as reference. This option can be used\n");
@@ -390,11 +390,11 @@ struct ExtractPass : public Pass {
log(" match. This option can be used multiple times.\n");
log("\n");
log(" -swap <needle_type> <port1>,<port2>[,...]\n");
- log(" Register a set of swapable ports for a needle cell type.\n");
+ log(" Register a set of swappable ports for a needle cell type.\n");
log(" This option can be used multiple times.\n");
log("\n");
log(" -perm <needle_type> <port1>,<port2>[,...] <portA>,<portB>[,...]\n");
- log(" Register a valid permutation of swapable ports for a needle\n");
+ log(" Register a valid permutation of swappable ports for a needle\n");
log(" cell type. This option can be used multiple times.\n");
log("\n");
log(" -cell_attr <attribute_name>\n");
@@ -409,7 +409,7 @@ struct ExtractPass : public Pass {
log(" -ignore_param <cell_type> <parameter_name>\n");
log(" Do not use this parameter when matching cells.\n");
log("\n");
- log("This pass does not operate on modules with uprocessed processes in it.\n");
+ log("This pass does not operate on modules with unprocessed processes in it.\n");
log("(I.e. the 'proc' pass should be used first to convert processes to netlists.)\n");
log("\n");
log("This pass can also be used for mining for frequent subcircuits. In this mode\n");
@@ -442,7 +442,7 @@ struct ExtractPass : public Pass {
}
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
{
- log_header("Executing EXTRACT pass (map subcircuits to cells).\n");
+ log_header(design, "Executing EXTRACT pass (map subcircuits to cells).\n");
log_push();
SubCircuitSolver solver;
@@ -607,6 +607,7 @@ struct ExtractPass : public Pass {
else
{
std::ifstream f;
+ rewrite_filename(filename);
f.open(filename.c_str());
if (f.fail()) {
delete map;
@@ -626,7 +627,7 @@ struct ExtractPass : public Pass {
std::map<std::string, RTLIL::Module*> needle_map, haystack_map;
std::vector<RTLIL::Module*> needle_list;
- log_header("Creating graphs for SubCircuit library.\n");
+ log_header(design, "Creating graphs for SubCircuit library.\n");
if (!mine_mode)
for (auto &mod_it : map->modules_) {
@@ -649,11 +650,11 @@ struct ExtractPass : public Pass {
haystack_map[graph_name] = mod_it.second;
}
}
-
+
if (!mine_mode)
{
std::vector<SubCircuit::Solver::Result> results;
- log_header("Running solver from SubCircuit library.\n");
+ log_header(design, "Running solver from SubCircuit library.\n");
std::sort(needle_list.begin(), needle_list.end(), compareSortNeedleList);
@@ -666,7 +667,7 @@ struct ExtractPass : public Pass {
if (results.size() > 0)
{
- log_header("Substitute SubCircuits with cells.\n");
+ log_header(design, "Substitute SubCircuits with cells.\n");
for (int i = 0; i < int(results.size()); i++) {
auto &result = results[i];
@@ -687,7 +688,7 @@ struct ExtractPass : public Pass {
{
std::vector<SubCircuit::Solver::MineResult> results;
- log_header("Running miner from SubCircuit library.\n");
+ log_header(design, "Running miner from SubCircuit library.\n");
solver.mine(results, mine_cells_min, mine_cells_max, mine_min_freq, mine_limit_mod);
map = new RTLIL::Design;
@@ -736,7 +737,7 @@ struct ExtractPass : public Pass {
RTLIL::Cell *newCell = newMod->addCell(cell->name, cell->type);
newCell->parameters = cell->parameters;
for (auto &conn : cell->connections()) {
- std::vector<RTLIL::SigChunk> chunks = sigmap(conn.second);
+ std::vector<SigChunk> chunks = sigmap(conn.second);
for (auto &chunk : chunks)
if (chunk.wire != NULL)
chunk.wire = newMod->wires_.at(chunk.wire->name);
@@ -746,6 +747,7 @@ struct ExtractPass : public Pass {
}
std::ofstream f;
+ rewrite_filename(mine_outfile);
f.open(mine_outfile.c_str(), std::ofstream::trunc);
if (f.fail())
log_error("Can't open output file `%s'.\n", mine_outfile.c_str());
@@ -757,5 +759,5 @@ struct ExtractPass : public Pass {
log_pop();
}
} ExtractPass;
-
+
PRIVATE_NAMESPACE_END
diff --git a/passes/techmap/hilomap.cc b/passes/techmap/hilomap.cc
index 9a14ffa3c..82cecac26 100644
--- a/passes/techmap/hilomap.cc
+++ b/passes/techmap/hilomap.cc
@@ -2,11 +2,11 @@
* 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
@@ -76,7 +76,7 @@ struct HilomapPass : public Pass {
}
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
{
- log_header("Executing HILOMAP pass (mapping to constant drivers).\n");
+ log_header(design, "Executing HILOMAP pass (mapping to constant drivers).\n");
hicell_celltype = std::string();
hicell_portname = std::string();
@@ -119,5 +119,5 @@ struct HilomapPass : public Pass {
}
}
} HilomapPass;
-
+
PRIVATE_NAMESPACE_END
diff --git a/passes/techmap/iopadmap.cc b/passes/techmap/iopadmap.cc
index 75d02c828..4acbf7c0d 100644
--- a/passes/techmap/iopadmap.cc
+++ b/passes/techmap/iopadmap.cc
@@ -2,11 +2,11 @@
* 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
@@ -17,9 +17,8 @@
*
*/
-#include "kernel/register.h"
-#include "kernel/rtlil.h"
-#include "kernel/log.h"
+#include "kernel/yosys.h"
+#include "kernel/sigtools.h"
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
@@ -45,15 +44,26 @@ struct IopadmapPass : public Pass {
log("the resulting cells to more sophisticated PAD cells.\n");
log("\n");
log(" -inpad <celltype> <portname>[:<portname>]\n");
- log(" Map module input ports to the given cell type with\n");
- log(" the given port name. if a 2nd portname is given, the\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(" portname as output.\n");
+ log(" portname as the port facing the module port.\n");
log("\n");
log(" -outpad <celltype> <portname>[:<portname>]\n");
log(" -inoutpad <celltype> <portname>[:<portname>]\n");
log(" Similar to -inpad, but for output and inout ports.\n");
log("\n");
+ log(" -toutpad <celltype> <portname>:<portname>[:<portname>]\n");
+ log(" Merges $_TBUF_ cells into the output pad cell. This takes precedence\n");
+ log(" over the other -outpad cell. The first portname is the enable input\n");
+ log(" of the tristate driver.\n");
+ log("\n");
+ log(" -tinoutpad <celltype> <portname>:<portname>:<portname>[:<portname>]\n");
+ log(" Merges $_TBUF_ cells into the inout pad cell. This takes precedence\n");
+ log(" over the other -inoutpad cell. The first portname is the enable input\n");
+ log(" of the tristate driver and the 2nd portname is the internal output\n");
+ log(" buffering the external signal.\n");
+ log("\n");
log(" -widthparam <param_name>\n");
log(" Use the specified parameter name to set the port width.\n");
log("\n");
@@ -65,14 +75,18 @@ struct IopadmapPass : public Pass {
log(" are wider. (the default behavior is to create word-wide\n");
log(" buffers using -widthparam to set the word size on the cell.)\n");
log("\n");
+ log("Tristate PADS (-toutpad, -tinoutpad) always operate in -bits mode.\n");
+ log("\n");
}
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
{
- log_header("Executing IOPADMAP pass (mapping inputs/outputs to IO-PAD cells).\n");
+ log_header(design, "Executing IOPADMAP pass (mapping inputs/outputs to IO-PAD cells).\n");
std::string inpad_celltype, inpad_portname, inpad_portname2;
std::string outpad_celltype, outpad_portname, outpad_portname2;
std::string inoutpad_celltype, inoutpad_portname, inoutpad_portname2;
+ std::string toutpad_celltype, toutpad_portname, toutpad_portname2, toutpad_portname3;
+ std::string tinoutpad_celltype, tinoutpad_portname, tinoutpad_portname2, tinoutpad_portname3, tinoutpad_portname4;
std::string widthparam, nameparam;
bool flag_bits = false;
@@ -98,6 +112,21 @@ struct IopadmapPass : public Pass {
split_portname_pair(inoutpad_portname, inoutpad_portname2);
continue;
}
+ if (arg == "-toutpad" && argidx+2 < args.size()) {
+ toutpad_celltype = args[++argidx];
+ toutpad_portname = args[++argidx];
+ split_portname_pair(toutpad_portname, toutpad_portname2);
+ split_portname_pair(toutpad_portname2, toutpad_portname3);
+ continue;
+ }
+ if (arg == "-tinoutpad" && argidx+2 < args.size()) {
+ tinoutpad_celltype = args[++argidx];
+ tinoutpad_portname = args[++argidx];
+ split_portname_pair(tinoutpad_portname, tinoutpad_portname2);
+ split_portname_pair(tinoutpad_portname2, tinoutpad_portname3);
+ split_portname_pair(tinoutpad_portname3, tinoutpad_portname4);
+ continue;
+ }
if (arg == "-widthparam" && argidx+1 < args.size()) {
widthparam = args[++argidx];
continue;
@@ -114,21 +143,134 @@ struct IopadmapPass : public Pass {
}
extra_args(args, argidx, design);
- for (auto &it : design->modules_)
+ for (auto module : design->selected_modules())
{
- RTLIL::Module *module = it.second;
-
- if (!design->selected(module) || module->get_bool_attribute("\\blackbox"))
- continue;
+ dict<IdString, pool<int>> skip_wires;
- for (auto &it2 : module->wires_)
+ if (!toutpad_celltype.empty() || !tinoutpad_celltype.empty())
{
- RTLIL::Wire *wire = it2.second;
+ SigMap sigmap(module);
+ dict<SigBit, pair<IdString, pool<IdString>>> tbuf_bits;
+
+ for (auto cell : module->cells())
+ if (cell->type == "$_TBUF_") {
+ SigBit bit = sigmap(cell->getPort("\\Y").as_bit());
+ tbuf_bits[bit].first = cell->name;
+ }
+
+ for (auto cell : module->cells())
+ for (auto port : cell->connections())
+ for (auto bit : sigmap(port.second))
+ if (tbuf_bits.count(bit))
+ tbuf_bits.at(bit).second.insert(cell->name);
+
+ for (auto wire : module->selected_wires())
+ {
+ if (!wire->port_output)
+ continue;
+
+ for (int i = 0; i < GetSize(wire); i++)
+ {
+ SigBit wire_bit(wire, i);
+ SigBit mapped_wire_bit = sigmap(wire_bit);
+
+ if (tbuf_bits.count(mapped_wire_bit) == 0)
+ continue;
+
+ auto &tbuf_cache = tbuf_bits.at(mapped_wire_bit);
+ Cell *tbuf_cell = module->cell(tbuf_cache.first);
+
+ if (tbuf_cell == nullptr)
+ continue;
- if (!wire->port_id || !design->selected(module, wire))
+ SigBit en_sig = tbuf_cell->getPort("\\E").as_bit();
+ SigBit data_sig = tbuf_cell->getPort("\\A").as_bit();
+
+ if (wire->port_input && !tinoutpad_celltype.empty())
+ {
+ log("Mapping port %s.%s[%d] using %s.\n", log_id(module), log_id(wire), i, tinoutpad_celltype.c_str());
+
+ Cell *cell = module->addCell(NEW_ID, RTLIL::escape_id(tinoutpad_celltype));
+ Wire *owire = module->addWire(NEW_ID);
+
+ cell->setPort(RTLIL::escape_id(tinoutpad_portname), en_sig);
+ cell->setPort(RTLIL::escape_id(tinoutpad_portname2), owire);
+ cell->setPort(RTLIL::escape_id(tinoutpad_portname3), data_sig);
+ cell->setPort(RTLIL::escape_id(tinoutpad_portname4), wire_bit);
+ cell->attributes["\\keep"] = RTLIL::Const(1);
+
+ for (auto cn : tbuf_cache.second) {
+ auto c = module->cell(cn);
+ if (c == nullptr)
+ continue;
+ for (auto port : c->connections()) {
+ SigSpec sig = port.second;
+ bool newsig = false;
+ for (auto &bit : sig)
+ if (sigmap(bit) == mapped_wire_bit) {
+ bit = owire;
+ newsig = true;
+ }
+ if (newsig)
+ c->setPort(port.first, sig);
+ }
+ }
+
+
+ module->remove(tbuf_cell);
+ skip_wires[wire->name].insert(i);
+ continue;
+ }
+
+ if (!wire->port_input && !toutpad_celltype.empty())
+ {
+ log("Mapping port %s.%s[%d] using %s.\n", log_id(module), log_id(wire), i, toutpad_celltype.c_str());
+
+ Cell *cell = module->addCell(NEW_ID, RTLIL::escape_id(toutpad_celltype));
+
+ cell->setPort(RTLIL::escape_id(toutpad_portname), en_sig);
+ cell->setPort(RTLIL::escape_id(toutpad_portname2), data_sig);
+ cell->setPort(RTLIL::escape_id(toutpad_portname3), wire_bit);
+ cell->attributes["\\keep"] = RTLIL::Const(1);
+
+ for (auto cn : tbuf_cache.second) {
+ auto c = module->cell(cn);
+ if (c == nullptr)
+ continue;
+ for (auto port : c->connections()) {
+ SigSpec sig = port.second;
+ bool newsig = false;
+ for (auto &bit : sig)
+ if (sigmap(bit) == mapped_wire_bit) {
+ bit = data_sig;
+ newsig = true;
+ }
+ if (newsig)
+ c->setPort(port.first, sig);
+ }
+ }
+
+ module->remove(tbuf_cell);
+ skip_wires[wire->name].insert(i);
+ continue;
+ }
+ }
+ }
+ }
+
+ for (auto wire : module->selected_wires())
+ {
+ if (!wire->port_id)
continue;
std::string celltype, portname, portname2;
+ pool<int> skip_bit_indices;
+
+ if (skip_wires.count(wire->name)) {
+ if (!flag_bits)
+ continue;
+ skip_bit_indices = skip_wires.at(wire->name);
+ }
if (wire->port_input && !wire->port_output) {
if (inpad_celltype.empty()) {
@@ -170,12 +312,21 @@ struct IopadmapPass : public Pass {
if (!portname2.empty()) {
new_wire = module->addWire(NEW_ID, wire);
module->swap_names(new_wire, wire);
+ wire->attributes.clear();
}
if (flag_bits)
{
for (int i = 0; i < wire->width; i++)
{
+ if (skip_bit_indices.count(i)) {
+ if (wire->port_output)
+ module->connect(SigSpec(new_wire, i), SigSpec(wire, i));
+ else
+ module->connect(SigSpec(wire, i), SigSpec(new_wire, i));
+ continue;
+ }
+
RTLIL::Cell *cell = module->addCell(NEW_ID, RTLIL::escape_id(celltype));
cell->setPort(RTLIL::escape_id(portname), RTLIL::SigSpec(wire, i));
if (!portname2.empty())
@@ -209,5 +360,5 @@ struct IopadmapPass : public Pass {
}
}
} IopadmapPass;
-
+
PRIVATE_NAMESPACE_END
diff --git a/passes/techmap/libparse.cc b/passes/techmap/libparse.cc
index 50d31ab5a..d5254c029 100644
--- a/passes/techmap/libparse.cc
+++ b/passes/techmap/libparse.cc
@@ -2,11 +2,11 @@
* 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
@@ -43,8 +43,6 @@ LibertyAst::~LibertyAst()
LibertyAst *LibertyAst::find(std::string name)
{
- if (this == NULL)
- return NULL;
for (auto child : children)
if (child->id == name)
return child;
@@ -107,14 +105,14 @@ int LibertyParser::lexer(std::string &str)
}
if (c == '"') {
- str = c;
+ str = "";
while (1) {
c = f.get();
if (c == '\n')
line++;
- str += c;
if (c == '"')
break;
+ str += c;
}
// fprintf(stderr, "LEX: string >>%s<<\n", str.c_str());
return 'v';
@@ -175,7 +173,7 @@ LibertyAst *LibertyParser::parse()
if (tok == '}' || tok < 0)
return NULL;
-
+
if (tok != 'v')
error();
diff --git a/passes/techmap/libparse.h b/passes/techmap/libparse.h
index e947bd8cd..cf6325570 100644
--- a/passes/techmap/libparse.h
+++ b/passes/techmap/libparse.h
@@ -2,11 +2,11 @@
* 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
diff --git a/passes/techmap/lut2mux.cc b/passes/techmap/lut2mux.cc
new file mode 100644
index 000000000..2bb0bd8b4
--- /dev/null
+++ b/passes/techmap/lut2mux.cc
@@ -0,0 +1,93 @@
+/*
+ * 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
+
+int lut2mux(Cell *cell)
+{
+ SigSpec sig_a = cell->getPort("\\A");
+ SigSpec sig_y = cell->getPort("\\Y");
+ Const lut = cell->getParam("\\LUT");
+ int count = 1;
+
+ if (GetSize(sig_a) == 1)
+ {
+ cell->module->addMuxGate(NEW_ID, lut[0], lut[1], sig_a, sig_y);
+ }
+ else
+ {
+ SigSpec sig_a_hi = sig_a[GetSize(sig_a)-1];
+ SigSpec sig_a_lo = sig_a.extract(0, GetSize(sig_a)-1);
+ SigSpec sig_y1 = cell->module->addWire(NEW_ID);
+ SigSpec sig_y2 = cell->module->addWire(NEW_ID);
+
+ Const lut1 = lut.extract(0, GetSize(lut)/2);
+ Const lut2 = lut.extract(GetSize(lut)/2, GetSize(lut)/2);
+
+ count += lut2mux(cell->module->addLut(NEW_ID, sig_a_lo, sig_y1, lut1));
+ count += lut2mux(cell->module->addLut(NEW_ID, sig_a_lo, sig_y2, lut2));
+
+ cell->module->addMuxGate(NEW_ID, sig_y1, sig_y2, sig_a_hi, sig_y);
+ }
+
+ cell->module->remove(cell);
+ return count;
+}
+
+struct Lut2muxPass : public Pass {
+ Lut2muxPass() : Pass("lut2mux", "convert $lut to $_MUX_") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" lut2mux [options] [selection]\n");
+ log("\n");
+ log("This pass converts $lut cells to $_MUX_ gates.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ log_header(design, "Executing LUT2MUX pass (convert $lut to $_MUX_).\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ // if (args[argidx] == "-v") {
+ // continue;
+ // }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto module : design->selected_modules())
+ for (auto cell : module->selected_cells()) {
+ if (cell->type == "$lut") {
+ IdString cell_name = cell->name;
+ int count = lut2mux(cell);
+ log("Converted %s.%s to %d MUX cells.\n", log_id(module), log_id(cell_name), count);
+ }
+ }
+ }
+} Lut2muxPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/techmap/maccmap.cc b/passes/techmap/maccmap.cc
index ffbd6289d..32569d076 100644
--- a/passes/techmap/maccmap.cc
+++ b/passes/techmap/maccmap.cc
@@ -2,11 +2,11 @@
* 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
@@ -134,7 +134,7 @@ struct MaccmapWorker
}
return retval;
#else
- return std::max(n - 1, 0);
+ return max(n - 1, 0);
#endif
}
@@ -371,15 +371,15 @@ struct MaccmapPass : public Pass {
log("\n");
log(" maccmap [-unmap] [selection]\n");
log("\n");
- log("This pass maps $macc cells to yosys gate primitives. When the -unmap option is\n");
- log("used then the $macc cell is mapped to $and, $sub, etc. cells instead.\n");
+ log("This pass maps $macc cells to yosys $fa and $alu cells. When the -unmap option\n");
+ log("is used then the $macc cell is mapped to $add, $sub, etc. cells instead.\n");
log("\n");
}
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
{
bool unmap_mode = false;
- log_header("Executing MACCMAP pass (map $macc cells).\n");
+ log_header(design, "Executing MACCMAP pass (map $macc cells).\n");
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++) {
@@ -400,5 +400,5 @@ struct MaccmapPass : public Pass {
}
}
} MaccmapPass;
-
+
PRIVATE_NAMESPACE_END
diff --git a/passes/techmap/muxcover.cc b/passes/techmap/muxcover.cc
new file mode 100644
index 000000000..1dc649587
--- /dev/null
+++ b/passes/techmap/muxcover.cc
@@ -0,0 +1,632 @@
+/*
+ * 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
+
+#define COST_MUX2 100
+#define COST_MUX4 220
+#define COST_MUX8 460
+#define COST_MUX16 940
+
+struct MuxcoverWorker
+{
+ Module *module;
+ SigMap sigmap;
+
+ struct newmux_t
+ {
+ int cost;
+ vector<SigBit> inputs, selects;
+ newmux_t() : cost(0) {}
+ };
+
+ struct tree_t
+ {
+ SigBit root;
+ dict<SigBit, Cell*> muxes;
+ dict<SigBit, newmux_t> newmuxes;
+ };
+
+ vector<tree_t> tree_list;
+
+ dict<tuple<SigBit, SigBit, SigBit>, tuple<SigBit, pool<SigBit>, bool>> decode_mux_cache;
+ dict<SigBit, tuple<SigBit, SigBit, SigBit>> decode_mux_reverse_cache;
+ int decode_mux_counter;
+
+ bool use_mux4;
+ bool use_mux8;
+ bool use_mux16;
+ bool nodecode;
+
+ MuxcoverWorker(Module *module) : module(module), sigmap(module)
+ {
+ use_mux4 = false;
+ use_mux8 = false;
+ use_mux16 = false;
+ nodecode = false;
+ decode_mux_counter = 0;
+ }
+
+ void treeify()
+ {
+ pool<SigBit> roots;
+ pool<SigBit> used_once;
+ dict<SigBit, Cell*> sig_to_mux;
+
+ for (auto wire : module->wires()) {
+ if (!wire->port_output)
+ continue;
+ for (auto bit : sigmap(wire))
+ roots.insert(bit);
+ }
+
+ for (auto cell : module->cells()) {
+ for (auto conn : cell->connections()) {
+ if (!cell->input(conn.first))
+ continue;
+ for (auto bit : sigmap(conn.second)) {
+ if (used_once.count(bit) || cell->type != "$_MUX_" || conn.first == "\\S")
+ roots.insert(bit);
+ used_once.insert(bit);
+ }
+ }
+ if (cell->type == "$_MUX_")
+ sig_to_mux[sigmap(cell->getPort("\\Y"))] = cell;
+ }
+
+ log(" Treeifying %d MUXes:\n", GetSize(sig_to_mux));
+
+ roots.sort();
+ for (auto rootsig : roots)
+ {
+ tree_t tree;
+ tree.root = rootsig;
+
+ pool<SigBit> wavefront;
+ wavefront.insert(rootsig);
+
+ while (!wavefront.empty()) {
+ SigBit bit = wavefront.pop();
+ if (sig_to_mux.count(bit) && (bit == rootsig || !roots.count(bit))) {
+ Cell *c = sig_to_mux.at(bit);
+ tree.muxes[bit] = c;
+ wavefront.insert(sigmap(c->getPort("\\A")));
+ wavefront.insert(sigmap(c->getPort("\\B")));
+ }
+ }
+
+ if (!tree.muxes.empty()) {
+ log(" Found tree with %d MUXes at root %s.\n", GetSize(tree.muxes), log_signal(tree.root));
+ tree_list.push_back(tree);
+ }
+ }
+
+ log(" Finished treeification: Found %d trees.\n", GetSize(tree_list));
+ }
+
+ bool follow_muxtree(SigBit &ret_bit, tree_t &tree, SigBit bit, const char *path)
+ {
+ if (*path) {
+ if (tree.muxes.count(bit) == 0)
+ return false;
+ char port_name[3] = {'\\', *path, 0};
+ return follow_muxtree(ret_bit, tree, sigmap(tree.muxes.at(bit)->getPort(port_name)), path+1);
+ } else {
+ ret_bit = bit;
+ return true;
+ }
+ }
+
+ int prepare_decode_mux(SigBit &A, SigBit B, SigBit sel, SigBit bit)
+ {
+ if (A == B)
+ return 0;
+
+ tuple<SigBit, SigBit, SigBit> key(A, B, sel);
+ if (decode_mux_cache.count(key) == 0) {
+ auto &entry = decode_mux_cache[key];
+ std::get<0>(entry) = module->addWire(NEW_ID);
+ std::get<2>(entry) = false;
+ decode_mux_reverse_cache[std::get<0>(entry)] = key;
+ }
+
+ auto &entry = decode_mux_cache[key];
+ A = std::get<0>(entry);
+ std::get<1>(entry).insert(bit);
+
+ if (std::get<2>(entry))
+ return 0;
+
+ return COST_MUX2 / GetSize(std::get<1>(entry));
+ }
+
+ void implement_decode_mux(SigBit ctrl_bit)
+ {
+ if (decode_mux_reverse_cache.count(ctrl_bit) == 0)
+ return;
+
+ auto &key = decode_mux_reverse_cache.at(ctrl_bit);
+ auto &entry = decode_mux_cache[key];
+
+ if (std::get<2>(entry))
+ return;
+
+ implement_decode_mux(std::get<0>(key));
+ implement_decode_mux(std::get<1>(key));
+
+ module->addMuxGate(NEW_ID, std::get<0>(key), std::get<1>(key), std::get<2>(key), ctrl_bit);
+ std::get<2>(entry) = true;
+ decode_mux_counter++;
+ }
+
+ int find_best_cover(tree_t &tree, SigBit bit)
+ {
+ if (tree.newmuxes.count(bit)) {
+ return tree.newmuxes.at(bit).cost;
+ }
+
+ SigBit A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P;
+ SigBit S1, S2, S3, S4, S5, S6, S7, S8;
+ SigBit T1, T2, T3, T4;
+ SigBit U1, U2;
+ SigBit V1;
+
+ newmux_t best_mux;
+ bool ok = true;
+
+ // 2-Input MUX
+
+ ok = ok && follow_muxtree(A, tree, bit, "A");
+ ok = ok && follow_muxtree(B, tree, bit, "B");
+
+ ok = ok && follow_muxtree(S1, tree, bit, "S");
+
+ if (ok)
+ {
+ newmux_t mux;
+
+ mux.inputs.push_back(A);
+ mux.inputs.push_back(B);
+ mux.selects.push_back(S1);
+
+ mux.cost += COST_MUX2;
+ mux.cost += find_best_cover(tree, A);
+ mux.cost += find_best_cover(tree, B);
+
+ best_mux = mux;
+ }
+
+ // 4-Input MUX
+
+ if (use_mux4)
+ {
+ ok = ok && follow_muxtree(A, tree, bit, "AA");
+ ok = ok && follow_muxtree(B, tree, bit, "AB");
+ ok = ok && follow_muxtree(C, tree, bit, "BA");
+ ok = ok && follow_muxtree(D, tree, bit, "BB");
+
+ ok = ok && follow_muxtree(S1, tree, bit, "AS");
+ ok = ok && follow_muxtree(S2, tree, bit, "BS");
+
+ if (nodecode)
+ ok = ok && S1 == S2;
+
+ ok = ok && follow_muxtree(T1, tree, bit, "S");
+
+ if (ok)
+ {
+ newmux_t mux;
+
+ mux.inputs.push_back(A);
+ mux.inputs.push_back(B);
+ mux.inputs.push_back(C);
+ mux.inputs.push_back(D);
+
+ mux.cost += prepare_decode_mux(S1, S2, T1, bit);
+
+ mux.selects.push_back(S1);
+ mux.selects.push_back(T1);
+
+ mux.cost += COST_MUX4;
+ mux.cost += find_best_cover(tree, A);
+ mux.cost += find_best_cover(tree, B);
+ mux.cost += find_best_cover(tree, C);
+ mux.cost += find_best_cover(tree, D);
+
+ if (best_mux.cost > mux.cost)
+ best_mux = mux;
+ }
+ }
+
+ // 8-Input MUX
+
+ if (use_mux8)
+ {
+ ok = ok && follow_muxtree(A, tree, bit, "AAA");
+ ok = ok && follow_muxtree(B, tree, bit, "AAB");
+ ok = ok && follow_muxtree(C, tree, bit, "ABA");
+ ok = ok && follow_muxtree(D, tree, bit, "ABB");
+ ok = ok && follow_muxtree(E, tree, bit, "BAA");
+ ok = ok && follow_muxtree(F, tree, bit, "BAB");
+ ok = ok && follow_muxtree(G, tree, bit, "BBA");
+ ok = ok && follow_muxtree(H, tree, bit, "BBB");
+
+ ok = ok && follow_muxtree(S1, tree, bit, "AAS");
+ ok = ok && follow_muxtree(S2, tree, bit, "ABS");
+ ok = ok && follow_muxtree(S3, tree, bit, "BAS");
+ ok = ok && follow_muxtree(S4, tree, bit, "BBS");
+
+ if (nodecode)
+ ok = ok && S1 == S2 && S2 == S3 && S3 == S4;
+
+ ok = ok && follow_muxtree(T1, tree, bit, "AS");
+ ok = ok && follow_muxtree(T2, tree, bit, "BS");
+
+ if (nodecode)
+ ok = ok && T1 == T2;
+
+ ok = ok && follow_muxtree(U1, tree, bit, "S");
+
+ if (ok)
+ {
+ newmux_t mux;
+
+ mux.inputs.push_back(A);
+ mux.inputs.push_back(B);
+ mux.inputs.push_back(C);
+ mux.inputs.push_back(D);
+ mux.inputs.push_back(E);
+ mux.inputs.push_back(F);
+ mux.inputs.push_back(G);
+ mux.inputs.push_back(H);
+
+ mux.cost += prepare_decode_mux(S1, S2, T1, bit);
+ mux.cost += prepare_decode_mux(S3, S4, T2, bit);
+ mux.cost += prepare_decode_mux(S1, S3, U1, bit);
+
+ mux.cost += prepare_decode_mux(T1, T2, U1, bit);
+
+ mux.selects.push_back(S1);
+ mux.selects.push_back(T1);
+ mux.selects.push_back(U1);
+
+ mux.cost += COST_MUX8;
+ mux.cost += find_best_cover(tree, A);
+ mux.cost += find_best_cover(tree, B);
+ mux.cost += find_best_cover(tree, C);
+ mux.cost += find_best_cover(tree, D);
+ mux.cost += find_best_cover(tree, E);
+ mux.cost += find_best_cover(tree, F);
+ mux.cost += find_best_cover(tree, G);
+ mux.cost += find_best_cover(tree, H);
+
+ if (best_mux.cost > mux.cost)
+ best_mux = mux;
+ }
+ }
+
+ // 16-Input MUX
+
+ if (use_mux16)
+ {
+ ok = ok && follow_muxtree(A, tree, bit, "AAAA");
+ ok = ok && follow_muxtree(B, tree, bit, "AAAB");
+ ok = ok && follow_muxtree(C, tree, bit, "AABA");
+ ok = ok && follow_muxtree(D, tree, bit, "AABB");
+ ok = ok && follow_muxtree(E, tree, bit, "ABAA");
+ ok = ok && follow_muxtree(F, tree, bit, "ABAB");
+ ok = ok && follow_muxtree(G, tree, bit, "ABBA");
+ ok = ok && follow_muxtree(H, tree, bit, "ABBB");
+ ok = ok && follow_muxtree(I, tree, bit, "BAAA");
+ ok = ok && follow_muxtree(J, tree, bit, "BAAB");
+ ok = ok && follow_muxtree(K, tree, bit, "BABA");
+ ok = ok && follow_muxtree(L, tree, bit, "BABB");
+ ok = ok && follow_muxtree(M, tree, bit, "BBAA");
+ ok = ok && follow_muxtree(N, tree, bit, "BBAB");
+ ok = ok && follow_muxtree(O, tree, bit, "BBBA");
+ ok = ok && follow_muxtree(P, tree, bit, "BBBB");
+
+ ok = ok && follow_muxtree(S1, tree, bit, "AAAS");
+ ok = ok && follow_muxtree(S2, tree, bit, "AABS");
+ ok = ok && follow_muxtree(S3, tree, bit, "ABAS");
+ ok = ok && follow_muxtree(S4, tree, bit, "ABBS");
+ ok = ok && follow_muxtree(S5, tree, bit, "BAAS");
+ ok = ok && follow_muxtree(S6, tree, bit, "BABS");
+ ok = ok && follow_muxtree(S7, tree, bit, "BBAS");
+ ok = ok && follow_muxtree(S8, tree, bit, "BBBS");
+
+ if (nodecode)
+ ok = ok && S1 == S2 && S2 == S3 && S3 == S4 && S4 == S5 && S5 == S6 && S6 == S7 && S7 == S8;
+
+ ok = ok && follow_muxtree(T1, tree, bit, "AAS");
+ ok = ok && follow_muxtree(T2, tree, bit, "ABS");
+ ok = ok && follow_muxtree(T3, tree, bit, "BAS");
+ ok = ok && follow_muxtree(T4, tree, bit, "BBS");
+
+ if (nodecode)
+ ok = ok && T1 == T2 && T2 == T3 && T3 == T4;
+
+ ok = ok && follow_muxtree(U1, tree, bit, "AS");
+ ok = ok && follow_muxtree(U2, tree, bit, "BS");
+
+ if (nodecode)
+ ok = ok && U1 == U2;
+
+ ok = ok && follow_muxtree(V1, tree, bit, "S");
+
+ if (ok)
+ {
+ newmux_t mux;
+
+ mux.inputs.push_back(A);
+ mux.inputs.push_back(B);
+ mux.inputs.push_back(C);
+ mux.inputs.push_back(D);
+ mux.inputs.push_back(E);
+ mux.inputs.push_back(F);
+ mux.inputs.push_back(G);
+ mux.inputs.push_back(H);
+ mux.inputs.push_back(I);
+ mux.inputs.push_back(J);
+ mux.inputs.push_back(K);
+ mux.inputs.push_back(L);
+ mux.inputs.push_back(M);
+ mux.inputs.push_back(N);
+ mux.inputs.push_back(O);
+ mux.inputs.push_back(P);
+
+ mux.cost += prepare_decode_mux(S1, S2, T1, bit);
+ mux.cost += prepare_decode_mux(S3, S4, T2, bit);
+ mux.cost += prepare_decode_mux(S5, S6, T3, bit);
+ mux.cost += prepare_decode_mux(S7, S8, T4, bit);
+ mux.cost += prepare_decode_mux(S1, S3, U1, bit);
+ mux.cost += prepare_decode_mux(S5, S7, U2, bit);
+ mux.cost += prepare_decode_mux(S1, S5, V1, bit);
+
+ mux.cost += prepare_decode_mux(T1, T2, U1, bit);
+ mux.cost += prepare_decode_mux(T3, T4, U2, bit);
+ mux.cost += prepare_decode_mux(T1, T3, V1, bit);
+
+ mux.cost += prepare_decode_mux(U1, U2, V1, bit);
+
+ mux.selects.push_back(S1);
+ mux.selects.push_back(T1);
+ mux.selects.push_back(U1);
+ mux.selects.push_back(V1);
+
+ mux.cost += COST_MUX16;
+ mux.cost += find_best_cover(tree, A);
+ mux.cost += find_best_cover(tree, B);
+ mux.cost += find_best_cover(tree, C);
+ mux.cost += find_best_cover(tree, D);
+ mux.cost += find_best_cover(tree, E);
+ mux.cost += find_best_cover(tree, F);
+ mux.cost += find_best_cover(tree, G);
+ mux.cost += find_best_cover(tree, H);
+ mux.cost += find_best_cover(tree, I);
+ mux.cost += find_best_cover(tree, J);
+ mux.cost += find_best_cover(tree, K);
+ mux.cost += find_best_cover(tree, L);
+ mux.cost += find_best_cover(tree, M);
+ mux.cost += find_best_cover(tree, N);
+ mux.cost += find_best_cover(tree, O);
+ mux.cost += find_best_cover(tree, P);
+
+ if (best_mux.cost > mux.cost)
+ best_mux = mux;
+ }
+ }
+
+ tree.newmuxes[bit] = best_mux;
+ return best_mux.cost;
+ }
+
+ void implement_best_cover(tree_t &tree, SigBit bit, int count_muxes_by_type[4])
+ {
+ newmux_t mux = tree.newmuxes.at(bit);
+
+ for (auto inbit : mux.inputs)
+ implement_best_cover(tree, inbit, count_muxes_by_type);
+
+ for (auto selbit : mux.selects)
+ implement_decode_mux(selbit);
+
+ if (GetSize(mux.inputs) == 0)
+ return;
+
+ if (GetSize(mux.inputs) == 2) {
+ count_muxes_by_type[0]++;
+ Cell *cell = module->addCell(NEW_ID, "$_MUX_");
+ cell->setPort("\\A", mux.inputs[0]);
+ cell->setPort("\\B", mux.inputs[1]);
+ cell->setPort("\\S", mux.selects[0]);
+ cell->setPort("\\Y", bit);
+ return;
+ }
+
+ if (GetSize(mux.inputs) == 4) {
+ count_muxes_by_type[1]++;
+ Cell *cell = module->addCell(NEW_ID, "$_MUX4_");
+ cell->setPort("\\A", mux.inputs[0]);
+ cell->setPort("\\B", mux.inputs[1]);
+ cell->setPort("\\C", mux.inputs[2]);
+ cell->setPort("\\D", mux.inputs[3]);
+ cell->setPort("\\S", mux.selects[0]);
+ cell->setPort("\\T", mux.selects[1]);
+ cell->setPort("\\Y", bit);
+ return;
+ }
+
+ if (GetSize(mux.inputs) == 8) {
+ count_muxes_by_type[2]++;
+ Cell *cell = module->addCell(NEW_ID, "$_MUX8_");
+ cell->setPort("\\A", mux.inputs[0]);
+ cell->setPort("\\B", mux.inputs[1]);
+ cell->setPort("\\C", mux.inputs[2]);
+ cell->setPort("\\D", mux.inputs[3]);
+ cell->setPort("\\E", mux.inputs[4]);
+ cell->setPort("\\F", mux.inputs[5]);
+ cell->setPort("\\G", mux.inputs[6]);
+ cell->setPort("\\H", mux.inputs[7]);
+ cell->setPort("\\S", mux.selects[0]);
+ cell->setPort("\\T", mux.selects[1]);
+ cell->setPort("\\U", mux.selects[2]);
+ cell->setPort("\\Y", bit);
+ return;
+ }
+
+ if (GetSize(mux.inputs) == 16) {
+ count_muxes_by_type[3]++;
+ Cell *cell = module->addCell(NEW_ID, "$_MUX16_");
+ cell->setPort("\\A", mux.inputs[0]);
+ cell->setPort("\\B", mux.inputs[1]);
+ cell->setPort("\\C", mux.inputs[2]);
+ cell->setPort("\\D", mux.inputs[3]);
+ cell->setPort("\\E", mux.inputs[4]);
+ cell->setPort("\\F", mux.inputs[5]);
+ cell->setPort("\\G", mux.inputs[6]);
+ cell->setPort("\\H", mux.inputs[7]);
+ cell->setPort("\\I", mux.inputs[8]);
+ cell->setPort("\\J", mux.inputs[9]);
+ cell->setPort("\\K", mux.inputs[10]);
+ cell->setPort("\\L", mux.inputs[11]);
+ cell->setPort("\\M", mux.inputs[12]);
+ cell->setPort("\\N", mux.inputs[13]);
+ cell->setPort("\\O", mux.inputs[14]);
+ cell->setPort("\\P", mux.inputs[15]);
+ cell->setPort("\\S", mux.selects[0]);
+ cell->setPort("\\T", mux.selects[1]);
+ cell->setPort("\\U", mux.selects[2]);
+ cell->setPort("\\V", mux.selects[3]);
+ cell->setPort("\\Y", bit);
+ return;
+ }
+
+ log_abort();
+ }
+
+ void treecover(tree_t &tree)
+ {
+ int count_muxes_by_type[4] = {0, 0, 0, 0};
+ find_best_cover(tree, tree.root);
+ implement_best_cover(tree, tree.root, count_muxes_by_type);
+ log(" Replaced tree at %s: %d MUX2, %d MUX4, %d MUX8, %d MUX16\n", log_signal(tree.root),
+ count_muxes_by_type[0], count_muxes_by_type[1], count_muxes_by_type[2], count_muxes_by_type[3]);
+ for (auto &it : tree.muxes)
+ module->remove(it.second);
+ }
+
+ void run()
+ {
+ log("Covering MUX trees in module %s..\n", log_id(module));
+
+ treeify();
+
+ log(" Covering trees:\n");
+
+ // pre-fill cache of decoder muxes
+ if (!nodecode)
+ for (auto &tree : tree_list) {
+ find_best_cover(tree, tree.root);
+ tree.newmuxes.clear();
+ }
+
+ for (auto &tree : tree_list)
+ treecover(tree);
+
+ if (!nodecode)
+ log(" Added a total of %d decoder MUXes.\n", decode_mux_counter);
+ }
+};
+
+struct MuxcoverPass : public Pass {
+ MuxcoverPass() : Pass("muxcover", "cover trees of MUX cells with wider MUXes") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" muxcover [options] [selection]\n");
+ log("\n");
+ log("Cover trees of $_MUX_ cells with $_MUX{4,8,16}_ cells\n");
+ log("\n");
+ log(" -mux4, -mux8, -mux16\n");
+ log(" Use the specified types of MUXes. If none of those options are used,\n");
+ log(" the effect is the same as if all of them where used.\n");
+ log("\n");
+ log(" -nodecode\n");
+ log(" Do not insert decoder logic. This reduces the number of possible\n");
+ log(" substitutions, but guarantees that the resulting circuit is not\n");
+ log(" less efficient than the original circuit.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ log_header(design, "Executing MUXCOVER pass (mapping to wider MUXes).\n");
+
+ bool use_mux4 = false;
+ bool use_mux8 = false;
+ bool use_mux16 = false;
+ bool nodecode = false;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ if (args[argidx] == "-mux4") {
+ use_mux4 = true;
+ continue;
+ }
+ if (args[argidx] == "-mux8") {
+ use_mux8 = true;
+ continue;
+ }
+ if (args[argidx] == "-mux16") {
+ use_mux16 = true;
+ continue;
+ }
+ if (args[argidx] == "-nodecode") {
+ nodecode = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ if (!use_mux4 && !use_mux8 && !use_mux16) {
+ use_mux4 = true;
+ use_mux8 = true;
+ use_mux16 = true;
+ }
+
+ for (auto module : design->selected_modules())
+ {
+ MuxcoverWorker worker(module);
+ worker.use_mux4 = use_mux4;
+ worker.use_mux8 = use_mux8;
+ worker.use_mux16 = use_mux16;
+ worker.nodecode = nodecode;
+ worker.run();
+ }
+ }
+} MuxcoverPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/techmap/nlutmap.cc b/passes/techmap/nlutmap.cc
new file mode 100644
index 000000000..6fcdf82bd
--- /dev/null
+++ b/passes/techmap/nlutmap.cc
@@ -0,0 +1,187 @@
+/*
+ * 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 NlutmapConfig
+{
+ vector<int> luts;
+ bool assert_mode = false;
+};
+
+struct NlutmapWorker
+{
+ const NlutmapConfig &config;
+ pool<Cell*> mapped_cells;
+ Module *module;
+
+ NlutmapWorker(const NlutmapConfig &config, Module *module) :
+ config(config), module(module)
+ {
+ }
+
+ RTLIL::Selection get_selection()
+ {
+ RTLIL::Selection sel(false);
+ for (auto cell : module->cells())
+ if (!mapped_cells.count(cell))
+ sel.select(module, cell);
+ return sel;
+ }
+
+ void run_abc(int lut_size)
+ {
+ Pass::call_on_selection(module->design, get_selection(), "lut2mux");
+
+ if (lut_size > 0)
+ Pass::call_on_selection(module->design, get_selection(), stringf("abc -lut 1:%d", lut_size));
+ else
+ Pass::call_on_selection(module->design, get_selection(), "abc");
+
+ Pass::call_on_module(module->design, module, "opt_clean");
+ }
+
+ void run()
+ {
+ vector<int> available_luts = config.luts;
+
+ while (GetSize(available_luts) > 1)
+ {
+ int n_luts = available_luts.back();
+ int lut_size = GetSize(available_luts);
+ available_luts.pop_back();
+
+ if (n_luts == 0)
+ continue;
+
+ run_abc(lut_size);
+
+ SigMap sigmap(module);
+ dict<Cell*, int> candidate_ratings;
+ dict<SigBit, int> bit_lut_count;
+
+ for (auto cell : module->cells())
+ {
+ if (cell->type != "$lut" || mapped_cells.count(cell))
+ continue;
+
+ if (GetSize(cell->getPort("\\A")) == lut_size || lut_size == 2)
+ candidate_ratings[cell] = 0;
+
+ for (auto &conn : cell->connections())
+ for (auto bit : sigmap(conn.second))
+ bit_lut_count[bit]++;
+ }
+
+ for (auto &cand : candidate_ratings)
+ {
+ for (auto &conn : cand.first->connections())
+ for (auto bit : sigmap(conn.second))
+ cand.second -= bit_lut_count[bit];
+ }
+
+ vector<pair<int, IdString>> rated_candidates;
+
+ for (auto &cand : candidate_ratings)
+ rated_candidates.push_back(pair<int, IdString>(cand.second, cand.first->name));
+
+ std::sort(rated_candidates.begin(), rated_candidates.end());
+
+ while (n_luts > 0 && !rated_candidates.empty()) {
+ mapped_cells.insert(module->cell(rated_candidates.back().second));
+ rated_candidates.pop_back();
+ n_luts--;
+ }
+
+ if (!available_luts.empty())
+ available_luts.back() += n_luts;
+ }
+
+ if (config.assert_mode) {
+ for (auto cell : module->cells())
+ if (cell->type == "$lut" && !mapped_cells.count(cell))
+ log_error("Insufficient number of LUTs to map all logic cells!\n");
+ }
+
+ run_abc(0);
+ }
+};
+
+struct NlutmapPass : public Pass {
+ NlutmapPass() : Pass("nlutmap", "map to LUTs of different sizes") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" nlutmap [options] [selection]\n");
+ log("\n");
+ log("This pass uses successive calls to 'abc' to map to an architecture. That\n");
+ log("provides a small number of differently sized LUTs.\n");
+ log("\n");
+ log(" -luts N_1,N_2,N_3,...\n");
+ log(" The number of LUTs with 1, 2, 3, ... inputs that are\n");
+ log(" available in the target architecture.\n");
+ log("\n");
+ log(" -assert\n");
+ log(" Create an error if not all logic can be mapped\n");
+ log("\n");
+ log("Excess logic that does not fit into the specified LUTs is mapped back\n");
+ log("to generic logic gates ($_AND_, etc.).\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ NlutmapConfig config;
+
+ log_header(design, "Executing NLUTMAP pass (mapping to constant drivers).\n");
+ log_push();
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ if (args[argidx] == "-luts" && argidx+1 < args.size()) {
+ vector<string> tokens = split_tokens(args[++argidx], ",");
+ config.luts.clear();
+ for (auto &token : tokens)
+ config.luts.push_back(atoi(token.c_str()));
+ continue;
+ }
+ if (args[argidx] == "-assert") {
+ config.assert_mode = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto module : design->selected_whole_modules_warn())
+ {
+ NlutmapWorker worker(config, module);
+ worker.run();
+ }
+
+ log_pop();
+ }
+} NlutmapPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/techmap/pmuxtree.cc b/passes/techmap/pmuxtree.cc
new file mode 100644
index 000000000..c626dbcc5
--- /dev/null
+++ b/passes/techmap/pmuxtree.cc
@@ -0,0 +1,112 @@
+/*
+ * 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
+
+static SigSpec or_generator(Module *module, const SigSpec &sig)
+{
+ switch (GetSize(sig))
+ {
+ case 0:
+ return State::S0;
+ case 1:
+ return sig;
+ case 2:
+ return module->Or(NEW_ID, sig[0], sig[1]);
+ default:
+ return module->ReduceOr(NEW_ID, sig);
+ }
+}
+
+static SigSpec recursive_mux_generator(Module *module, const SigSpec &sig_data, const SigSpec &sig_sel, SigSpec &sig_or)
+{
+ if (GetSize(sig_sel) == 1) {
+ sig_or.append(sig_sel);
+ return sig_data;
+ }
+
+ int left_size = GetSize(sig_sel) / 2;
+ int right_size = GetSize(sig_sel) - left_size;
+ int stride = GetSize(sig_data) / GetSize(sig_sel);
+
+ SigSpec left_data = sig_data.extract(0, stride*left_size);
+ SigSpec right_data = sig_data.extract(stride*left_size, stride*right_size);
+
+ SigSpec left_sel = sig_sel.extract(0, left_size);
+ SigSpec right_sel = sig_sel.extract(left_size, right_size);
+
+ SigSpec left_or, left_result, right_result;
+
+ left_result = recursive_mux_generator(module, left_data, left_sel, left_or);
+ right_result = recursive_mux_generator(module, right_data, right_sel, sig_or);
+ left_or = or_generator(module, left_or);
+ sig_or.append(left_or);
+
+ return module->Mux(NEW_ID, right_result, left_result, left_or);
+}
+
+struct PmuxtreePass : public Pass {
+ PmuxtreePass() : Pass("pmuxtree", "transform $pmux cells to trees of $mux cells") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" pmuxtree [options] [selection]\n");
+ log("\n");
+ log("This pass transforms $pmux cells to a trees of $mux cells.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ log_header(design, "Executing PMUXTREE 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 != "$pmux")
+ continue;
+
+ SigSpec sig_data = cell->getPort("\\B");
+ SigSpec sig_sel = cell->getPort("\\S");
+
+ if (!cell->getPort("\\A").is_fully_undef()) {
+ sig_data.append(cell->getPort("\\A"));
+ SigSpec sig_sel_or = module->ReduceOr(NEW_ID, sig_sel);
+ sig_sel.append(module->Not(NEW_ID, sig_sel_or));
+ }
+
+ SigSpec result, result_or;
+ result = recursive_mux_generator(module, sig_data, sig_sel, result_or);
+ module->connect(cell->getPort("\\Y"), result);
+ module->remove(cell);
+ }
+ }
+} PmuxtreePass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/techmap/shregmap.cc b/passes/techmap/shregmap.cc
new file mode 100644
index 000000000..6936b499e
--- /dev/null
+++ b/passes/techmap/shregmap.cc
@@ -0,0 +1,584 @@
+/*
+ * 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 ShregmapTech
+{
+ virtual ~ShregmapTech() { }
+ virtual bool analyze(vector<int> &taps) = 0;
+ virtual bool fixup(Cell *cell, dict<int, SigBit> &taps) = 0;
+};
+
+struct ShregmapOptions
+{
+ int minlen, maxlen;
+ int keep_before, keep_after;
+ bool zinit, init, params, ffe;
+ dict<IdString, pair<IdString, IdString>> ffcells;
+ ShregmapTech *tech;
+
+ ShregmapOptions()
+ {
+ minlen = 2;
+ maxlen = 0;
+ keep_before = 0;
+ keep_after = 0;
+ zinit = false;
+ init = false;
+ params = false;
+ ffe = false;
+ tech = nullptr;
+ }
+};
+
+struct ShregmapTechGreenpak4 : ShregmapTech
+{
+ bool analyze(vector<int> &taps)
+ {
+ if (GetSize(taps) > 2 && taps[0] == 0 && taps[2] < 17) {
+ taps.clear();
+ return true;
+ }
+
+ if (GetSize(taps) > 2)
+ return false;
+
+ if (taps.back() > 16) return false;
+
+ return true;
+ }
+
+ bool fixup(Cell *cell, dict<int, SigBit> &taps)
+ {
+ auto D = cell->getPort("\\D");
+ auto C = cell->getPort("\\C");
+
+ auto newcell = cell->module->addCell(NEW_ID, "\\GP_SHREG");
+ newcell->setPort("\\nRST", State::S1);
+ newcell->setPort("\\CLK", C);
+ newcell->setPort("\\IN", D);
+
+ int i = 0;
+ for (auto tap : taps) {
+ newcell->setPort(i ? "\\OUTB" : "\\OUTA", tap.second);
+ newcell->setParam(i ? "\\OUTB_TAP" : "\\OUTA_TAP", tap.first + 1);
+ i++;
+ }
+
+ cell->setParam("\\OUTA_INVERT", 0);
+ return false;
+ }
+};
+
+struct ShregmapWorker
+{
+ Module *module;
+ SigMap sigmap;
+
+ const ShregmapOptions &opts;
+ int dff_count, shreg_count;
+
+ pool<Cell*> remove_cells;
+ pool<SigBit> remove_init;
+
+ dict<SigBit, bool> sigbit_init;
+ dict<SigBit, Cell*> sigbit_chain_next;
+ dict<SigBit, Cell*> sigbit_chain_prev;
+ pool<SigBit> sigbit_with_non_chain_users;
+ pool<Cell*> chain_start_cells;
+
+ void make_sigbit_chain_next_prev()
+ {
+ for (auto wire : module->wires())
+ {
+ if (wire->port_output || wire->get_bool_attribute("\\keep")) {
+ for (auto bit : sigmap(wire))
+ sigbit_with_non_chain_users.insert(bit);
+ }
+
+ if (wire->attributes.count("\\init")) {
+ SigSpec initsig = sigmap(wire);
+ Const initval = wire->attributes.at("\\init");
+ for (int i = 0; i < GetSize(initsig) && i < GetSize(initval); i++)
+ if (initval[i] == State::S0 && !opts.zinit)
+ sigbit_init[initsig[i]] = false;
+ else if (initval[i] == State::S1)
+ sigbit_init[initsig[i]] = true;
+ }
+ }
+
+ for (auto cell : module->cells())
+ {
+ if (opts.ffcells.count(cell->type) && !cell->get_bool_attribute("\\keep"))
+ {
+ IdString d_port = opts.ffcells.at(cell->type).first;
+ IdString q_port = opts.ffcells.at(cell->type).second;
+
+ SigBit d_bit = sigmap(cell->getPort(d_port).as_bit());
+ SigBit q_bit = sigmap(cell->getPort(q_port).as_bit());
+
+ if (opts.init || sigbit_init.count(q_bit) == 0)
+ {
+ if (sigbit_chain_next.count(d_bit)) {
+ sigbit_with_non_chain_users.insert(d_bit);
+ } else
+ sigbit_chain_next[d_bit] = cell;
+
+ sigbit_chain_prev[q_bit] = cell;
+ continue;
+ }
+ }
+
+ for (auto conn : cell->connections())
+ if (cell->input(conn.first))
+ for (auto bit : sigmap(conn.second))
+ sigbit_with_non_chain_users.insert(bit);
+ }
+ }
+
+ void find_chain_start_cells()
+ {
+ for (auto it : sigbit_chain_next)
+ {
+ if (opts.tech == nullptr && sigbit_with_non_chain_users.count(it.first))
+ goto start_cell;
+
+ if (sigbit_chain_prev.count(it.first) != 0)
+ {
+ Cell *c1 = sigbit_chain_prev.at(it.first);
+ Cell *c2 = it.second;
+
+ if (c1->type != c2->type)
+ goto start_cell;
+
+ if (c1->parameters != c2->parameters)
+ goto start_cell;
+
+ IdString d_port = opts.ffcells.at(c1->type).first;
+ IdString q_port = opts.ffcells.at(c1->type).second;
+
+ auto c1_conn = c1->connections();
+ auto c2_conn = c1->connections();
+
+ c1_conn.erase(d_port);
+ c1_conn.erase(q_port);
+
+ c2_conn.erase(d_port);
+ c2_conn.erase(q_port);
+
+ if (c1_conn != c2_conn)
+ goto start_cell;
+
+ continue;
+ }
+
+ start_cell:
+ chain_start_cells.insert(it.second);
+ }
+ }
+
+ vector<Cell*> create_chain(Cell *start_cell)
+ {
+ vector<Cell*> chain;
+
+ Cell *c = start_cell;
+ while (c != nullptr)
+ {
+ chain.push_back(c);
+
+ IdString q_port = opts.ffcells.at(c->type).second;
+ SigBit q_bit = sigmap(c->getPort(q_port).as_bit());
+
+ if (sigbit_chain_next.count(q_bit) == 0)
+ break;
+
+ c = sigbit_chain_next.at(q_bit);
+ if (chain_start_cells.count(c) != 0)
+ break;
+ }
+
+ return chain;
+ }
+
+ void process_chain(vector<Cell*> &chain)
+ {
+ if (GetSize(chain) < opts.keep_before + opts.minlen + opts.keep_after)
+ return;
+
+ int cursor = opts.keep_before;
+ while (cursor < GetSize(chain) - opts.keep_after)
+ {
+ int depth = GetSize(chain) - opts.keep_after - cursor;
+
+ if (opts.maxlen > 0)
+ depth = std::min(opts.maxlen, depth);
+
+ Cell *first_cell = chain[cursor];
+ IdString q_port = opts.ffcells.at(first_cell->type).second;
+ dict<int, SigBit> taps_dict;
+
+ if (opts.tech)
+ {
+ vector<SigBit> qbits;
+ vector<int> taps;
+
+ for (int i = 0; i < depth; i++)
+ {
+ Cell *cell = chain[cursor+i];
+ auto qbit = sigmap(cell->getPort(q_port));
+ qbits.push_back(qbit);
+
+ if (sigbit_with_non_chain_users.count(qbit))
+ taps.push_back(i);
+ }
+
+ while (depth > 0)
+ {
+ if (taps.empty() || taps.back() < depth-1)
+ taps.push_back(depth-1);
+
+ if (opts.tech->analyze(taps))
+ break;
+
+ taps.pop_back();
+ depth--;
+ }
+
+ depth = 0;
+ for (auto tap : taps) {
+ taps_dict[tap] = qbits.at(tap);
+ log_assert(depth < tap+1);
+ depth = tap+1;
+ }
+ }
+
+ if (depth < 2) {
+ cursor++;
+ continue;
+ }
+
+ Cell *last_cell = chain[cursor+depth-1];
+
+ log("Converting %s.%s ... %s.%s to a shift register with depth %d.\n",
+ log_id(module), log_id(first_cell), log_id(module), log_id(last_cell), depth);
+
+ dff_count += depth;
+ shreg_count += 1;
+
+ string shreg_cell_type_str = "$__SHREG";
+ if (opts.params) {
+ shreg_cell_type_str += "_";
+ } else {
+ if (first_cell->type[1] != '_')
+ shreg_cell_type_str += "_";
+ shreg_cell_type_str += first_cell->type.substr(1);
+ }
+
+ if (opts.init) {
+ vector<State> initval;
+ for (int i = depth-1; i >= 0; i--) {
+ SigBit bit = sigmap(chain[cursor+i]->getPort(q_port).as_bit());
+ if (sigbit_init.count(bit) == 0)
+ initval.push_back(State::Sx);
+ else if (sigbit_init.at(bit))
+ initval.push_back(State::S1);
+ else
+ initval.push_back(State::S0);
+ remove_init.insert(bit);
+ }
+ first_cell->setParam("\\INIT", initval);
+ }
+
+ if (opts.zinit)
+ for (int i = depth-1; i >= 0; i--) {
+ SigBit bit = sigmap(chain[cursor+i]->getPort(q_port).as_bit());
+ remove_init.insert(bit);
+ }
+
+ if (opts.params)
+ {
+ int param_clkpol = -1;
+ int param_enpol = 2;
+
+ if (first_cell->type == "$_DFF_N_") param_clkpol = 0;
+ if (first_cell->type == "$_DFF_P_") param_clkpol = 1;
+
+ if (first_cell->type == "$_DFFE_NN_") param_clkpol = 0, param_enpol = 0;
+ if (first_cell->type == "$_DFFE_NP_") param_clkpol = 0, param_enpol = 1;
+ if (first_cell->type == "$_DFFE_PN_") param_clkpol = 1, param_enpol = 0;
+ if (first_cell->type == "$_DFFE_PP_") param_clkpol = 1, param_enpol = 1;
+
+ log_assert(param_clkpol >= 0);
+ first_cell->setParam("\\CLKPOL", param_clkpol);
+ if (opts.ffe) first_cell->setParam("\\ENPOL", param_enpol);
+ }
+
+ first_cell->type = shreg_cell_type_str;
+ first_cell->setPort(q_port, last_cell->getPort(q_port));
+ first_cell->setParam("\\DEPTH", depth);
+
+ if (opts.tech != nullptr && !opts.tech->fixup(first_cell, taps_dict))
+ remove_cells.insert(first_cell);
+
+ for (int i = 1; i < depth; i++)
+ remove_cells.insert(chain[cursor+i]);
+ cursor += depth;
+ }
+ }
+
+ void cleanup()
+ {
+ for (auto cell : remove_cells)
+ module->remove(cell);
+
+ for (auto wire : module->wires())
+ {
+ if (wire->attributes.count("\\init") == 0)
+ continue;
+
+ SigSpec initsig = sigmap(wire);
+ Const &initval = wire->attributes.at("\\init");
+
+ for (int i = 0; i < GetSize(initsig) && i < GetSize(initval); i++)
+ if (remove_init.count(initsig[i]))
+ initval[i] = State::Sx;
+
+ if (SigSpec(initval).is_fully_undef())
+ wire->attributes.erase("\\init");
+ }
+
+ remove_cells.clear();
+ sigbit_chain_next.clear();
+ sigbit_chain_prev.clear();
+ chain_start_cells.clear();
+ }
+
+ ShregmapWorker(Module *module, const ShregmapOptions &opts) :
+ module(module), sigmap(module), opts(opts), dff_count(0), shreg_count(0)
+ {
+ make_sigbit_chain_next_prev();
+ find_chain_start_cells();
+
+ for (auto c : chain_start_cells) {
+ vector<Cell*> chain = create_chain(c);
+ process_chain(chain);
+ }
+
+ cleanup();
+ }
+};
+
+struct ShregmapPass : public Pass {
+ ShregmapPass() : Pass("shregmap", "map shift registers") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" shregmap [options] [selection]\n");
+ log("\n");
+ log("This pass converts chains of $_DFF_[NP]_ gates to target specific shift register\n");
+ log("primitives. The generated shift register will be of type $__SHREG_DFF_[NP]_ and\n");
+ log("will use the same interface as the original $_DFF_*_ cells. The cell parameter\n");
+ log("'DEPTH' will contain the depth of the shift register. Use a target-specific\n");
+ log("'techmap' map file to convert those cells to the actual target cells.\n");
+ log("\n");
+ log(" -minlen N\n");
+ log(" minimum length of shift register (default = 2)\n");
+ log(" (this is the length after -keep_before and -keep_after)\n");
+ log("\n");
+ log(" -maxlen N\n");
+ log(" maximum length of shift register (default = no limit)\n");
+ log(" larger chains will be mapped to multiple shift register instances\n");
+ log("\n");
+ log(" -keep_before N\n");
+ log(" number of DFFs to keep before the shift register (default = 0)\n");
+ log("\n");
+ log(" -keep_after N\n");
+ log(" number of DFFs to keep after the shift register (default = 0)\n");
+ log("\n");
+ log(" -clkpol pos|neg|any\n");
+ log(" limit match to only positive or negative edge clocks. (default = any)\n");
+ log("\n");
+ log(" -enpol pos|neg|none|any_or_none|any\n");
+ log(" limit match to FFs with the specified enable polarity. (default = none)\n");
+ log("\n");
+ log(" -match <cell_type>[:<d_port_name>:<q_port_name>]\n");
+ log(" match the specified cells instead of $_DFF_N_ and $_DFF_P_. If\n");
+ log(" ':<d_port_name>:<q_port_name>' is omitted then 'D' and 'Q' is used\n");
+ log(" by default. E.g. the option '-clkpol pos' is just an alias for\n");
+ log(" '-match $_DFF_P_', which is an alias for '-match $_DFF_P_:D:Q'.\n");
+ log("\n");
+ log(" -params\n");
+ log(" instead of encoding the clock and enable polarity in the cell name by\n");
+ log(" deriving from the original cell name, simply name all generated cells\n");
+ log(" $__SHREG_ and use CLKPOL and ENPOL parameters. An ENPOL value of 2 is\n");
+ log(" used to denote cells without enable input. The ENPOL parameter is\n");
+ log(" omitted when '-enpol none' (or no -enpol option) is passed.\n");
+ log("\n");
+ log(" -zinit\n");
+ log(" assume the shift register is automatically zero-initialized, so it\n");
+ log(" becomes legal to merge zero initialized FFs into the shift register.\n");
+ log("\n");
+ log(" -init\n");
+ log(" map initialized registers to the shift reg, add an INIT parameter to\n");
+ log(" generated cells with the initialization value. (first bit to shift out\n");
+ log(" in LSB position)\n");
+ log("\n");
+ log(" -tech greenpak4\n");
+ log(" map to greenpak4 shift registers.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ ShregmapOptions opts;
+ string clkpol, enpol;
+
+ log_header(design, "Executing SHREGMAP pass (map shift registers).\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ if (args[argidx] == "-clkpol" && argidx+1 < args.size()) {
+ clkpol = args[++argidx];
+ continue;
+ }
+ if (args[argidx] == "-enpol" && argidx+1 < args.size()) {
+ enpol = args[++argidx];
+ continue;
+ }
+ if (args[argidx] == "-match" && argidx+1 < args.size()) {
+ vector<string> match_args = split_tokens(args[++argidx], ":");
+ if (GetSize(match_args) < 2)
+ match_args.push_back("D");
+ if (GetSize(match_args) < 3)
+ match_args.push_back("Q");
+ IdString id_cell_type(RTLIL::escape_id(match_args[0]));
+ IdString id_d_port_name(RTLIL::escape_id(match_args[1]));
+ IdString id_q_port_name(RTLIL::escape_id(match_args[2]));
+ opts.ffcells[id_cell_type] = make_pair(id_d_port_name, id_q_port_name);
+ continue;
+ }
+ if (args[argidx] == "-minlen" && argidx+1 < args.size()) {
+ opts.minlen = atoi(args[++argidx].c_str());
+ continue;
+ }
+ if (args[argidx] == "-maxlen" && argidx+1 < args.size()) {
+ opts.maxlen = atoi(args[++argidx].c_str());
+ continue;
+ }
+ if (args[argidx] == "-keep_before" && argidx+1 < args.size()) {
+ opts.keep_before = atoi(args[++argidx].c_str());
+ continue;
+ }
+ if (args[argidx] == "-keep_after" && argidx+1 < args.size()) {
+ opts.keep_after = atoi(args[++argidx].c_str());
+ continue;
+ }
+ if (args[argidx] == "-tech" && argidx+1 < args.size() && opts.tech == nullptr) {
+ string tech = args[++argidx];
+ if (tech == "greenpak4") {
+ clkpol = "pos";
+ opts.zinit = true;
+ opts.tech = new ShregmapTechGreenpak4;
+ } else {
+ argidx--;
+ break;
+ }
+ continue;
+ }
+ if (args[argidx] == "-zinit") {
+ opts.zinit = true;
+ continue;
+ }
+ if (args[argidx] == "-init") {
+ opts.init = true;
+ continue;
+ }
+ if (args[argidx] == "-params") {
+ opts.params = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ if (opts.zinit && opts.init)
+ log_cmd_error("Options -zinit and -init are exclusive!\n");
+
+ if (opts.ffcells.empty())
+ {
+ bool clk_pos = clkpol == "" || clkpol == "pos" || clkpol == "any";
+ bool clk_neg = clkpol == "" || clkpol == "neg" || clkpol == "any";
+
+ bool en_none = enpol == "" || enpol == "none" || enpol == "any_or_none";
+ bool en_pos = enpol == "pos" || enpol == "any" || enpol == "any_or_none";
+ bool en_neg = enpol == "neg" || enpol == "any" || enpol == "any_or_none";
+
+ if (clk_pos && en_none)
+ opts.ffcells["$_DFF_P_"] = make_pair(IdString("\\D"), IdString("\\Q"));
+ if (clk_neg && en_none)
+ opts.ffcells["$_DFF_N_"] = make_pair(IdString("\\D"), IdString("\\Q"));
+
+ if (clk_pos && en_pos)
+ opts.ffcells["$_DFFE_PP_"] = make_pair(IdString("\\D"), IdString("\\Q"));
+ if (clk_pos && en_neg)
+ opts.ffcells["$_DFFE_PN_"] = make_pair(IdString("\\D"), IdString("\\Q"));
+
+ if (clk_neg && en_pos)
+ opts.ffcells["$_DFFE_NP_"] = make_pair(IdString("\\D"), IdString("\\Q"));
+ if (clk_neg && en_neg)
+ opts.ffcells["$_DFFE_NN_"] = make_pair(IdString("\\D"), IdString("\\Q"));
+
+ if (en_pos || en_neg)
+ opts.ffe = true;
+ }
+ else
+ {
+ if (!clkpol.empty())
+ log_cmd_error("Options -clkpol and -match are exclusive!\n");
+ if (!enpol.empty())
+ log_cmd_error("Options -enpol and -match are exclusive!\n");
+ if (opts.params)
+ log_cmd_error("Options -params and -match are exclusive!\n");
+ }
+
+ int dff_count = 0;
+ int shreg_count = 0;
+
+ for (auto module : design->selected_modules()) {
+ ShregmapWorker worker(module, opts);
+ dff_count += worker.dff_count;
+ shreg_count += worker.shreg_count;
+ }
+
+ log("Converted %d dff cells into %d shift registers.\n", dff_count, shreg_count);
+
+ if (opts.tech != nullptr) {
+ delete opts.tech;
+ opts.tech = nullptr;
+ }
+ }
+} ShregmapPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/techmap/simplemap.cc b/passes/techmap/simplemap.cc
index 9cea5f45d..0fb647344 100644
--- a/passes/techmap/simplemap.cc
+++ b/passes/techmap/simplemap.cc
@@ -2,11 +2,11 @@
* 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
@@ -35,6 +35,7 @@ void simplemap_not(RTLIL::Module *module, RTLIL::Cell *cell)
for (int i = 0; i < GetSize(sig_y); i++) {
RTLIL::Cell *gate = module->addCell(NEW_ID, "$_NOT_");
+ gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src"));
gate->setPort("\\A", sig_a[i]);
gate->setPort("\\Y", sig_y[i]);
}
@@ -65,6 +66,7 @@ void simplemap_bitop(RTLIL::Module *module, RTLIL::Cell *cell)
for (int i = 0; i < GetSize(sig_y); i++) {
RTLIL::Cell *gate = module->addCell(NEW_ID, "$_NOT_");
+ gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src"));
gate->setPort("\\A", sig_t[i]);
gate->setPort("\\Y", sig_y[i]);
}
@@ -81,6 +83,7 @@ void simplemap_bitop(RTLIL::Module *module, RTLIL::Cell *cell)
for (int i = 0; i < GetSize(sig_y); i++) {
RTLIL::Cell *gate = module->addCell(NEW_ID, gate_type);
+ gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src"));
gate->setPort("\\A", sig_a[i]);
gate->setPort("\\B", sig_b[i]);
gate->setPort("\\Y", sig_y[i]);
@@ -94,7 +97,7 @@ void simplemap_reduce(RTLIL::Module *module, RTLIL::Cell *cell)
if (sig_y.size() == 0)
return;
-
+
if (sig_a.size() == 0) {
if (cell->type == "$reduce_and") module->connect(RTLIL::SigSig(sig_y, RTLIL::SigSpec(1, sig_y.size())));
if (cell->type == "$reduce_or") module->connect(RTLIL::SigSig(sig_y, RTLIL::SigSpec(0, sig_y.size())));
@@ -131,6 +134,7 @@ void simplemap_reduce(RTLIL::Module *module, RTLIL::Cell *cell)
}
RTLIL::Cell *gate = module->addCell(NEW_ID, gate_type);
+ gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src"));
gate->setPort("\\A", sig_a[i]);
gate->setPort("\\B", sig_a[i+1]);
gate->setPort("\\Y", sig_t[i/2]);
@@ -143,6 +147,7 @@ void simplemap_reduce(RTLIL::Module *module, RTLIL::Cell *cell)
if (cell->type == "$reduce_xnor") {
RTLIL::SigSpec sig_t = module->addWire(NEW_ID);
RTLIL::Cell *gate = module->addCell(NEW_ID, "$_NOT_");
+ gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src"));
gate->setPort("\\A", sig_a);
gate->setPort("\\Y", sig_t);
last_output_cell = gate;
@@ -156,7 +161,7 @@ void simplemap_reduce(RTLIL::Module *module, RTLIL::Cell *cell)
}
}
-static void logic_reduce(RTLIL::Module *module, RTLIL::SigSpec &sig)
+static void logic_reduce(RTLIL::Module *module, RTLIL::SigSpec &sig, RTLIL::Cell *cell)
{
while (sig.size() > 1)
{
@@ -170,6 +175,7 @@ static void logic_reduce(RTLIL::Module *module, RTLIL::SigSpec &sig)
}
RTLIL::Cell *gate = module->addCell(NEW_ID, "$_OR_");
+ gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src"));
gate->setPort("\\A", sig[i]);
gate->setPort("\\B", sig[i+1]);
gate->setPort("\\Y", sig_t[i/2]);
@@ -185,19 +191,20 @@ static void logic_reduce(RTLIL::Module *module, RTLIL::SigSpec &sig)
void simplemap_lognot(RTLIL::Module *module, RTLIL::Cell *cell)
{
RTLIL::SigSpec sig_a = cell->getPort("\\A");
- logic_reduce(module, sig_a);
+ logic_reduce(module, sig_a, cell);
RTLIL::SigSpec sig_y = cell->getPort("\\Y");
if (sig_y.size() == 0)
return;
-
+
if (sig_y.size() > 1) {
module->connect(RTLIL::SigSig(sig_y.extract(1, sig_y.size()-1), RTLIL::SigSpec(0, sig_y.size()-1)));
sig_y = sig_y.extract(0, 1);
}
RTLIL::Cell *gate = module->addCell(NEW_ID, "$_NOT_");
+ gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src"));
gate->setPort("\\A", sig_a);
gate->setPort("\\Y", sig_y);
}
@@ -205,16 +212,16 @@ void simplemap_lognot(RTLIL::Module *module, RTLIL::Cell *cell)
void simplemap_logbin(RTLIL::Module *module, RTLIL::Cell *cell)
{
RTLIL::SigSpec sig_a = cell->getPort("\\A");
- logic_reduce(module, sig_a);
+ logic_reduce(module, sig_a, cell);
RTLIL::SigSpec sig_b = cell->getPort("\\B");
- logic_reduce(module, sig_b);
+ logic_reduce(module, sig_b, cell);
RTLIL::SigSpec sig_y = cell->getPort("\\Y");
if (sig_y.size() == 0)
return;
-
+
if (sig_y.size() > 1) {
module->connect(RTLIL::SigSig(sig_y.extract(1, sig_y.size()-1), RTLIL::SigSpec(0, sig_y.size()-1)));
sig_y = sig_y.extract(0, 1);
@@ -226,6 +233,7 @@ void simplemap_logbin(RTLIL::Module *module, RTLIL::Cell *cell)
log_assert(!gate_type.empty());
RTLIL::Cell *gate = module->addCell(NEW_ID, gate_type);
+ gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src"));
gate->setPort("\\A", sig_a);
gate->setPort("\\B", sig_b);
gate->setPort("\\Y", sig_y);
@@ -239,18 +247,21 @@ void simplemap_eqne(RTLIL::Module *module, RTLIL::Cell *cell)
bool is_signed = cell->parameters.at("\\A_SIGNED").as_bool();
bool is_ne = cell->type == "$ne" || cell->type == "$nex";
- RTLIL::SigSpec xor_out = module->addWire(NEW_ID, std::max(GetSize(sig_a), GetSize(sig_b)));
+ RTLIL::SigSpec xor_out = module->addWire(NEW_ID, max(GetSize(sig_a), GetSize(sig_b)));
RTLIL::Cell *xor_cell = module->addXor(NEW_ID, sig_a, sig_b, xor_out, is_signed);
+ xor_cell->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src"));
simplemap_bitop(module, xor_cell);
module->remove(xor_cell);
RTLIL::SigSpec reduce_out = is_ne ? sig_y : module->addWire(NEW_ID);
RTLIL::Cell *reduce_cell = module->addReduceOr(NEW_ID, xor_out, reduce_out);
+ reduce_cell->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src"));
simplemap_reduce(module, reduce_cell);
module->remove(reduce_cell);
if (!is_ne) {
RTLIL::Cell *not_cell = module->addLogicNot(NEW_ID, reduce_out, sig_y);
+ not_cell->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src"));
simplemap_lognot(module, not_cell);
module->remove(not_cell);
}
@@ -264,6 +275,7 @@ void simplemap_mux(RTLIL::Module *module, RTLIL::Cell *cell)
for (int i = 0; i < GetSize(sig_y); i++) {
RTLIL::Cell *gate = module->addCell(NEW_ID, "$_MUX_");
+ gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src"));
gate->setPort("\\A", sig_a[i]);
gate->setPort("\\B", sig_b[i]);
gate->setPort("\\S", cell->getPort("\\S"));
@@ -271,6 +283,74 @@ void simplemap_mux(RTLIL::Module *module, RTLIL::Cell *cell)
}
}
+void simplemap_tribuf(RTLIL::Module *module, RTLIL::Cell *cell)
+{
+ RTLIL::SigSpec sig_a = cell->getPort("\\A");
+ RTLIL::SigSpec sig_e = cell->getPort("\\EN");
+ RTLIL::SigSpec sig_y = cell->getPort("\\Y");
+
+ for (int i = 0; i < GetSize(sig_y); i++) {
+ RTLIL::Cell *gate = module->addCell(NEW_ID, "$_TBUF_");
+ gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src"));
+ gate->setPort("\\A", sig_a[i]);
+ gate->setPort("\\E", sig_e);
+ gate->setPort("\\Y", sig_y[i]);
+ }
+}
+
+void simplemap_lut(RTLIL::Module *module, RTLIL::Cell *cell)
+{
+ SigSpec lut_ctrl = cell->getPort("\\A");
+ SigSpec lut_data = cell->getParam("\\LUT");
+ lut_data.extend_u0(1 << cell->getParam("\\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, "$_MUX_");
+ gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src"));
+ gate->setPort("\\A", lut_data[i]);
+ gate->setPort("\\B", lut_data[i+1]);
+ gate->setPort("\\S", lut_ctrl[idx]);
+ gate->setPort("\\Y", new_lut_data[i/2]);
+ }
+ lut_data = new_lut_data;
+ }
+
+ module->connect(cell->getPort("\\Y"), lut_data);
+}
+
+void simplemap_sop(RTLIL::Module *module, RTLIL::Cell *cell)
+{
+ SigSpec ctrl = cell->getPort("\\A");
+ SigSpec table = cell->getParam("\\TABLE");
+
+ int width = cell->getParam("\\WIDTH").as_int();
+ int depth = cell->getParam("\\DEPTH").as_int();
+ table.extend_u0(2 * width * depth);
+
+ SigSpec products;
+
+ for (int i = 0; i < depth; i++) {
+ SigSpec in, pat;
+ for (int j = 0; j < width; j++) {
+ if (table[2*i*width + 2*j + 0] == State::S1) {
+ in.append(ctrl[j]);
+ pat.append(State::S0);
+ }
+ if (table[2*i*width + 2*j + 1] == State::S1) {
+ in.append(ctrl[j]);
+ pat.append(State::S1);
+ }
+ }
+
+ products.append(GetSize(in) > 0 ? module->Eq(NEW_ID, in, pat) : State::S1);
+ }
+
+ module->connect(cell->getPort("\\Y"), module->ReduceOr(NEW_ID, products));
+}
+
void simplemap_slice(RTLIL::Module *module, RTLIL::Cell *cell)
{
int offset = cell->parameters.at("\\OFFSET").as_int();
@@ -301,6 +381,7 @@ void simplemap_sr(RTLIL::Module *module, RTLIL::Cell *cell)
for (int i = 0; i < width; i++) {
RTLIL::Cell *gate = module->addCell(NEW_ID, gate_type);
+ gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src"));
gate->setPort("\\S", sig_s[i]);
gate->setPort("\\R", sig_r[i]);
gate->setPort("\\Q", sig_q[i]);
@@ -320,6 +401,7 @@ void simplemap_dff(RTLIL::Module *module, RTLIL::Cell *cell)
for (int i = 0; i < width; i++) {
RTLIL::Cell *gate = module->addCell(NEW_ID, gate_type);
+ gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src"));
gate->setPort("\\C", sig_clk);
gate->setPort("\\D", sig_d[i]);
gate->setPort("\\Q", sig_q[i]);
@@ -341,6 +423,7 @@ void simplemap_dffe(RTLIL::Module *module, RTLIL::Cell *cell)
for (int i = 0; i < width; i++) {
RTLIL::Cell *gate = module->addCell(NEW_ID, gate_type);
+ gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src"));
gate->setPort("\\C", sig_clk);
gate->setPort("\\E", sig_en);
gate->setPort("\\D", sig_d[i]);
@@ -365,6 +448,7 @@ void simplemap_dffsr(RTLIL::Module *module, RTLIL::Cell *cell)
for (int i = 0; i < width; i++) {
RTLIL::Cell *gate = module->addCell(NEW_ID, gate_type);
+ gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src"));
gate->setPort("\\C", sig_clk);
gate->setPort("\\S", sig_s[i]);
gate->setPort("\\R", sig_r[i]);
@@ -393,6 +477,7 @@ void simplemap_adff(RTLIL::Module *module, RTLIL::Cell *cell)
for (int i = 0; i < width; i++) {
RTLIL::Cell *gate = module->addCell(NEW_ID, rst_val.at(i) == RTLIL::State::S1 ? gate_type_1 : gate_type_0);
+ gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src"));
gate->setPort("\\C", sig_clk);
gate->setPort("\\R", sig_rst);
gate->setPort("\\D", sig_d[i]);
@@ -413,6 +498,7 @@ void simplemap_dlatch(RTLIL::Module *module, RTLIL::Cell *cell)
for (int i = 0; i < width; i++) {
RTLIL::Cell *gate = module->addCell(NEW_ID, gate_type);
+ gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src"));
gate->setPort("\\E", sig_en);
gate->setPort("\\D", sig_d[i]);
gate->setPort("\\Q", sig_q[i]);
@@ -440,6 +526,9 @@ void simplemap_get_mappers(std::map<RTLIL::IdString, void(*)(RTLIL::Module*, RTL
mappers["$ne"] = simplemap_eqne;
mappers["$nex"] = simplemap_eqne;
mappers["$mux"] = simplemap_mux;
+ mappers["$tribuf"] = simplemap_tribuf;
+ mappers["$lut"] = simplemap_lut;
+ mappers["$sop"] = simplemap_sop;
mappers["$slice"] = simplemap_slice;
mappers["$concat"] = simplemap_concat;
mappers["$sr"] = simplemap_sr;
@@ -479,13 +568,13 @@ struct SimplemapPass : public Pass {
log("\n");
log(" $not, $pos, $and, $or, $xor, $xnor\n");
log(" $reduce_and, $reduce_or, $reduce_xor, $reduce_xnor, $reduce_bool\n");
- log(" $logic_not, $logic_and, $logic_or, $mux\n");
+ log(" $logic_not, $logic_and, $logic_or, $mux, $tribuf\n");
log(" $sr, $dff, $dffsr, $adff, $dlatch\n");
log("\n");
}
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
{
- log_header("Executing SIMPLEMAP pass (map simple cells to gate primitives).\n");
+ log_header(design, "Executing SIMPLEMAP pass (map simple cells to gate primitives).\n");
extra_args(args, 1, design);
std::map<RTLIL::IdString, void(*)(RTLIL::Module*, RTLIL::Cell*)> mappers;
@@ -507,5 +596,5 @@ struct SimplemapPass : public Pass {
}
}
} SimplemapPass;
-
+
PRIVATE_NAMESPACE_END
diff --git a/passes/techmap/simplemap.h b/passes/techmap/simplemap.h
index dc2a395d3..c2d73ea79 100644
--- a/passes/techmap/simplemap.h
+++ b/passes/techmap/simplemap.h
@@ -2,11 +2,11 @@
* 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
@@ -31,6 +31,7 @@ extern void simplemap_reduce(RTLIL::Module *module, RTLIL::Cell *cell);
extern void simplemap_lognot(RTLIL::Module *module, RTLIL::Cell *cell);
extern void simplemap_logbin(RTLIL::Module *module, RTLIL::Cell *cell);
extern void simplemap_mux(RTLIL::Module *module, RTLIL::Cell *cell);
+extern void simplemap_lut(RTLIL::Module *module, RTLIL::Cell *cell);
extern void simplemap_slice(RTLIL::Module *module, RTLIL::Cell *cell);
extern void simplemap_concat(RTLIL::Module *module, RTLIL::Cell *cell);
extern void simplemap_sr(RTLIL::Module *module, RTLIL::Cell *cell);
diff --git a/passes/techmap/techmap.cc b/passes/techmap/techmap.cc
index 521ac61a0..1ab6df1dc 100644
--- a/passes/techmap/techmap.cc
+++ b/passes/techmap/techmap.cc
@@ -2,11 +2,11 @@
* 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
@@ -49,7 +49,7 @@ void apply_prefix(std::string prefix, std::string &id)
void apply_prefix(std::string prefix, RTLIL::SigSpec &sig, RTLIL::Module *module)
{
- std::vector<RTLIL::SigChunk> chunks = sig;
+ vector<SigChunk> chunks = sig;
for (auto &chunk : chunks)
if (chunk.wire != NULL) {
std::string wire_name = chunk.wire->name.str();
@@ -66,6 +66,11 @@ struct TechmapWorker
std::map<std::pair<RTLIL::IdString, std::map<RTLIL::IdString, RTLIL::Const>>, RTLIL::Module*> techmap_cache;
std::map<RTLIL::Module*, bool> techmap_do_cache;
std::set<RTLIL::Module*, RTLIL::IdString::compare_ptr_by_name<RTLIL::Module>> module_queue;
+ dict<Module*, SigMap> sigmaps;
+
+ pool<IdString> flatten_do_list;
+ pool<IdString> flatten_done_list;
+ pool<Cell*> flatten_keep_list;
struct TechmapWireData {
RTLIL::Wire *wire;
@@ -154,9 +159,10 @@ struct TechmapWorker
void techmap_module_worker(RTLIL::Design *design, RTLIL::Module *module, RTLIL::Cell *cell, RTLIL::Module *tpl)
{
if (tpl->processes.size() != 0) {
- log("Technology map yielded processes:\n");
+ log("Technology map yielded processes:");
for (auto &it : tpl->processes)
- log(" %s",RTLIL::id2cstr(it.first));
+ log(" %s",RTLIL::id2cstr(it.first));
+ log("\n");
if (autoproc_mode) {
Pass::call_on_module(tpl->design, tpl, "proc");
log_assert(GetSize(tpl->processes) == 0);
@@ -165,7 +171,10 @@ struct TechmapWorker
}
std::string orig_cell_name;
+ pool<string> extra_src_attrs;
+
if (!flatten_mode)
+ {
for (auto &it : tpl->cells_)
if (it.first == "\\_TECHMAP_REPLACE_") {
orig_cell_name = cell->name.str();
@@ -173,6 +182,9 @@ struct TechmapWorker
break;
}
+ extra_src_attrs = cell->get_strpool_attribute("\\src");
+ }
+
dict<IdString, IdString> memory_renames;
for (auto &it : tpl->memories) {
@@ -184,6 +196,8 @@ struct TechmapWorker
m->start_offset = it.second->start_offset;
m->size = it.second->size;
m->attributes = it.second->attributes;
+ if (m->attributes.count("\\src"))
+ m->add_strpool_attribute("\\src", extra_src_attrs);
module->memories[m->name] = m;
memory_renames[it.first] = m->name;
design->select(module, m);
@@ -202,12 +216,28 @@ struct TechmapWorker
w->port_id = 0;
if (it.second->get_bool_attribute("\\_techmap_special_"))
w->attributes.clear();
+ if (w->attributes.count("\\src"))
+ w->add_strpool_attribute("\\src", extra_src_attrs);
design->select(module, w);
}
+ SigMap tpl_sigmap(tpl);
+ pool<SigBit> tpl_written_bits;
+
+ for (auto &it1 : tpl->cells_)
+ for (auto &it2 : it1.second->connections_)
+ if (it1.second->output(it2.first))
+ for (auto bit : tpl_sigmap(it2.second))
+ tpl_written_bits.insert(bit);
+ for (auto &it1 : tpl->connections_)
+ for (auto bit : tpl_sigmap(it1.first))
+ tpl_written_bits.insert(bit);
+
SigMap port_signal_map;
+ SigSig port_signal_assign;
- for (auto &it : cell->connections()) {
+ for (auto &it : cell->connections())
+ {
RTLIL::IdString portname = it.first;
if (positional_ports.count(portname) > 0)
portname = positional_ports.at(portname);
@@ -216,33 +246,76 @@ struct TechmapWorker
log_error("Can't map port `%s' of cell `%s' to template `%s'!\n", portname.c_str(), cell->name.c_str(), tpl->name.c_str());
continue;
}
+
RTLIL::Wire *w = tpl->wires_.at(portname);
- RTLIL::SigSig c;
- if (w->port_output) {
+ RTLIL::SigSig c, extra_connect;
+
+ if (w->port_output && !w->port_input) {
c.first = it.second;
c.second = RTLIL::SigSpec(w);
apply_prefix(cell->name.str(), c.second, module);
- } else {
+ extra_connect.first = c.second;
+ extra_connect.second = c.first;
+ } else if (!w->port_output && w->port_input) {
c.first = RTLIL::SigSpec(w);
c.second = it.second;
apply_prefix(cell->name.str(), c.first, module);
+ extra_connect.first = c.first;
+ extra_connect.second = c.second;
+ } else {
+ SigSpec sig_tpl = w, sig_tpl_pf = w, sig_mod = it.second;
+ apply_prefix(cell->name.str(), sig_tpl_pf, module);
+ for (int i = 0; i < GetSize(sig_tpl) && i < GetSize(sig_mod); i++) {
+ if (tpl_written_bits.count(tpl_sigmap(sig_tpl[i]))) {
+ c.first.append(sig_mod[i]);
+ c.second.append(sig_tpl_pf[i]);
+ } else {
+ c.first.append(sig_tpl_pf[i]);
+ c.second.append(sig_mod[i]);
+ }
+ }
+ extra_connect.first = sig_tpl_pf;
+ extra_connect.second = sig_mod;
}
+
if (c.second.size() > c.first.size())
c.second.remove(c.first.size(), c.second.size() - c.first.size());
+
if (c.second.size() < c.first.size())
c.second.append(RTLIL::SigSpec(RTLIL::State::S0, c.first.size() - c.second.size()));
+
log_assert(c.first.size() == c.second.size());
- if (flatten_mode) {
+
+ if (flatten_mode)
+ {
// more conservative approach:
// connect internal and external wires
+
+ if (sigmaps.count(module) == 0)
+ sigmaps[module].set(module);
+
+ if (sigmaps.at(module)(c.first).has_const())
+ log_error("Mismatch in directionality for cell port %s.%s.%s: %s <= %s\n",
+ log_id(module), log_id(cell), log_id(it.first), log_signal(c.first), log_signal(c.second));
+
module->connect(c);
- } else {
+ }
+ else
+ {
// approach that yields nicer outputs:
// replace internal wires that are connected to external wires
+
if (w->port_output)
port_signal_map.add(c.second, c.first);
else
port_signal_map.add(c.first, c.second);
+
+ for (auto &attr : w->attributes) {
+ if (attr.first == "\\src")
+ continue;
+ module->connect(extra_connect);
+ break;
+ }
}
}
@@ -266,11 +339,14 @@ struct TechmapWorker
port_signal_map.apply(it2.second);
}
- if (c->type == "$memrd" || c->type == "$memwr") {
+ if (c->type == "$memrd" || c->type == "$memwr" || c->type == "$meminit") {
IdString memid = c->getParam("\\MEMID").decode_string();
- log_assert(memory_renames.count(memid));
+ log_assert(memory_renames.count(memid) != 0);
c->setParam("\\MEMID", Const(memory_renames[memid].str()));
}
+
+ if (c->attributes.count("\\src"))
+ c->add_strpool_attribute("\\src", extra_src_attrs);
}
for (auto &it : tpl->connections()) {
@@ -317,6 +393,22 @@ struct TechmapWorker
continue;
}
+ if (flatten_mode) {
+ bool keepit = cell->get_bool_attribute("\\keep_hierarchy");
+ for (auto &tpl_name : celltypeMap.at(cell_type))
+ if (map->modules_[tpl_name]->get_bool_attribute("\\keep_hierarchy"))
+ keepit = true;
+ if (keepit) {
+ if (!flatten_keep_list[cell]) {
+ log("Keeping %s.%s (found keep_hierarchy property).\n", log_id(module), log_id(cell));
+ flatten_keep_list.insert(cell);
+ }
+ if (!flatten_done_list[cell->type])
+ flatten_do_list.insert(cell->type);
+ continue;
+ }
+ }
+
for (auto &conn : cell->connections())
{
RTLIL::SigSpec sig = sigmap(conn.second);
@@ -715,7 +807,7 @@ struct TechmapWorker
if (recursive_mode) {
if (log_continue) {
- log_header("Continuing TECHMAP pass.\n");
+ log_header(design, "Continuing TECHMAP pass.\n");
log_continue = false;
}
while (techmap_module(map, tpl, map, handled_cells, celltypeMap, true)) { }
@@ -726,7 +818,7 @@ struct TechmapWorker
continue;
if (log_continue) {
- log_header("Continuing TECHMAP pass.\n");
+ log_header(design, "Continuing TECHMAP pass.\n");
log_continue = false;
}
@@ -769,7 +861,7 @@ struct TechmapWorker
}
if (log_continue) {
- log_header("Continuing TECHMAP pass.\n");
+ log_header(design, "Continuing TECHMAP pass.\n");
log_continue = false;
}
@@ -786,7 +878,7 @@ struct TechmapPass : public Pass {
log(" techmap [-map filename] [selection]\n");
log("\n");
log("This pass implements a very simple technology mapper that replaces cells in\n");
- log("the design with implementations given in form of a verilog or ilang source\n");
+ log("the design with implementations given in form of a Verilog or ilang source\n");
log("file.\n");
log("\n");
log(" -map filename\n");
@@ -798,11 +890,6 @@ struct TechmapPass : public Pass {
log(" -map %%<design-name>\n");
log(" like -map above, but with an in-memory design instead of a file.\n");
log("\n");
- log(" -share_map filename\n");
- log(" like -map, but look for the file in the share directory (where the\n");
- log(" yosys data files are). this is mainly used internally when techmap\n");
- log(" is called from other commands.\n");
- log("\n");
log(" -extern\n");
log(" load the cell implementations as separate modules into the design\n");
log(" instead of inlining them.\n");
@@ -812,7 +899,7 @@ struct TechmapPass : public Pass {
log("\n");
log(" -recursive\n");
log(" instead of the iterative breadth-first algorithm use a recursive\n");
- log(" depth-first algorithm. both methods should yield equivialent results,\n");
+ log(" depth-first algorithm. both methods should yield equivalent results,\n");
log(" but may differ in performance.\n");
log("\n");
log(" -autoproc\n");
@@ -824,8 +911,8 @@ struct TechmapPass : public Pass {
log(" as final cell types by this mode.\n");
log("\n");
log(" -D <define>, -I <incdir>\n");
- log(" this options are passed as-is to the verilog frontend for loading the\n");
- log(" map file. Note that the verilog frontend is also called with the\n");
+ log(" this options are passed as-is to the Verilog frontend for loading the\n");
+ log(" map file. Note that the Verilog frontend is also called with the\n");
log(" '-ignore_redef' option set.\n");
log("\n");
log("When a module in the map file has the 'techmap_celltype' attribute set, it will\n");
@@ -871,7 +958,7 @@ struct TechmapPass : public Pass {
log(" of constant inputs and shorted inputs at this point and import the\n");
log(" constant and connected bits into the map module. All further commands\n");
log(" are executed in this copy. This is a very convenient way of creating\n");
- log(" optimizied specializations of techmap modules without using the special\n");
+ log(" optimized specializations of techmap modules without using the special\n");
log(" parameters described below.\n");
log("\n");
log(" A _TECHMAP_DO_* command may start with the special token 'RECURSION; '.\n");
@@ -907,17 +994,17 @@ struct TechmapPass : public Pass {
log("constant value.\n");
log("\n");
log("A cell with the name _TECHMAP_REPLACE_ in the map file will inherit the name\n");
- log("of the cell that is beeing replaced.\n");
+ log("of the cell that is being replaced.\n");
log("\n");
log("See 'help extract' for a pass that does the opposite thing.\n");
log("\n");
log("See 'help flatten' for a pass that does flatten the design (which is\n");
- log("esentially techmap but using the design itself as map library).\n");
+ log("essentially techmap but using the design itself as map library).\n");
log("\n");
}
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
{
- log_header("Executing TECHMAP pass (map to technology primitives).\n");
+ log_header(design, "Executing TECHMAP pass (map to technology primitives).\n");
log_push();
TechmapWorker worker;
@@ -930,14 +1017,7 @@ struct TechmapPass : public Pass {
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++) {
if (args[argidx] == "-map" && argidx+1 < args.size()) {
- if (args[argidx+1].substr(0, 2) == "+/")
- map_files.push_back(proc_share_dirname() + args[++argidx].substr(2));
- else
- map_files.push_back(args[++argidx]);
- continue;
- }
- if (args[argidx] == "-share_map" && argidx+1 < args.size()) {
- map_files.push_back(proc_share_dirname() + args[++argidx]);
+ map_files.push_back(args[++argidx]);
continue;
}
if (args[argidx] == "-max_iter" && argidx+1 < args.size()) {
@@ -988,20 +1068,13 @@ struct TechmapPass : public Pass {
map->add(mod->clone());
} else {
std::ifstream f;
+ rewrite_filename(fn);
f.open(fn.c_str());
if (f.fail())
log_cmd_error("Can't open map file `%s'\n", fn.c_str());
Frontend::frontend_call(map, &f, fn, (fn.size() > 3 && fn.substr(fn.size()-3) == ".il") ? "ilang" : verilog_frontend);
}
- dict<RTLIL::IdString, RTLIL::Module*> modules_new;
- for (auto &it : map->modules_) {
- if (it.first.substr(0, 2) == "\\$")
- it.second->name = it.first.substr(1);
- modules_new[it.second->name] = it.second;
- }
- map->modules_.swap(modules_new);
-
std::map<RTLIL::IdString, std::set<RTLIL::IdString, RTLIL::sort_by_id_str>> celltypeMap;
for (auto &it : map->modules_) {
if (it.second->attributes.count("\\techmap_celltype") && !it.second->attributes.at("\\techmap_celltype").bits.empty()) {
@@ -1009,8 +1082,12 @@ struct TechmapPass : public Pass {
for (char *q = strtok(p, " \t\r\n"); q; q = strtok(NULL, " \t\r\n"))
celltypeMap[RTLIL::escape_id(q)].insert(it.first);
free(p);
- } else
- celltypeMap[it.first].insert(it.first);
+ } else {
+ string module_name = it.first.str();
+ if (module_name.substr(0, 2) == "\\$")
+ module_name = module_name.substr(1);
+ celltypeMap[module_name].insert(it.first);
+ }
}
for (auto module : design->modules())
@@ -1040,7 +1117,7 @@ struct TechmapPass : public Pass {
log_pop();
}
} TechmapPass;
-
+
struct FlattenPass : public Pass {
FlattenPass() : Pass("flatten", "flatten design") { }
virtual void help()
@@ -1050,13 +1127,16 @@ struct FlattenPass : public Pass {
log(" flatten [selection]\n");
log("\n");
log("This pass flattens the design by replacing cells by their implementation. This\n");
- log("pass is very simmilar to the 'techmap' pass. The only difference is that this\n");
+ log("pass is very similar to the 'techmap' pass. The only difference is that this\n");
log("pass is using the current design as mapping library.\n");
log("\n");
+ log("Cells and/or modules with the 'keep_hierarchy' attribute set will not be\n");
+ log("flattened by this command.\n");
+ log("\n");
}
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
{
- log_header("Executing FLATTEN pass (flatten design).\n");
+ log_header(design, "Executing FLATTEN pass (flatten design).\n");
log_push();
extra_args(args, 1, design);
@@ -1065,8 +1145,8 @@ struct FlattenPass : public Pass {
worker.flatten_mode = true;
std::map<RTLIL::IdString, std::set<RTLIL::IdString, RTLIL::sort_by_id_str>> celltypeMap;
- for (auto &it : design->modules_)
- celltypeMap[it.first].insert(it.first);
+ for (auto module : design->modules())
+ celltypeMap[module->name].insert(module->name);
RTLIL::Module *top_mod = NULL;
if (design->full_selection())
@@ -1074,26 +1154,40 @@ struct FlattenPass : public Pass {
if (mod->get_bool_attribute("\\top"))
top_mod = mod;
- bool did_something = true;
std::set<RTLIL::Cell*> handled_cells;
- while (did_something) {
- did_something = false;
- if (top_mod != NULL) {
- if (worker.techmap_module(design, top_mod, design, handled_cells, celltypeMap, false))
- did_something = true;
- } else {
- for (auto mod : design->modules())
- if (worker.techmap_module(design, mod, design, handled_cells, celltypeMap, false))
- did_something = true;
+ if (top_mod != NULL) {
+ worker.flatten_do_list.insert(top_mod->name);
+ while (!worker.flatten_do_list.empty()) {
+ auto mod = design->module(*worker.flatten_do_list.begin());
+ while (worker.techmap_module(design, mod, design, handled_cells, celltypeMap, false)) { }
+ worker.flatten_done_list.insert(mod->name);
+ worker.flatten_do_list.erase(mod->name);
}
+ } else {
+ for (auto mod : vector<Module*>(design->modules()))
+ while (worker.techmap_module(design, mod, design, handled_cells, celltypeMap, false)) { }
}
log("No more expansions possible.\n");
- if (top_mod != NULL) {
+ if (top_mod != NULL)
+ {
+ pool<RTLIL::IdString> used_modules, new_used_modules;
+ new_used_modules.insert(top_mod->name);
+ while (!new_used_modules.empty()) {
+ pool<RTLIL::IdString> queue;
+ queue.swap(new_used_modules);
+ for (auto modname : queue)
+ used_modules.insert(modname);
+ for (auto modname : queue)
+ for (auto cell : design->module(modname)->cells())
+ if (design->module(cell->type) && !used_modules[cell->type])
+ new_used_modules.insert(cell->type);
+ }
+
dict<RTLIL::IdString, RTLIL::Module*> new_modules;
- for (auto mod : design->modules())
- if (mod == top_mod || mod->get_bool_attribute("\\blackbox")) {
+ for (auto mod : vector<Module*>(design->modules()))
+ if (used_modules[mod->name] || mod->get_bool_attribute("\\blackbox")) {
new_modules[mod->name] = mod;
} else {
log("Deleting now unused module %s.\n", log_id(mod));
diff --git a/passes/techmap/tribuf.cc b/passes/techmap/tribuf.cc
new file mode 100644
index 000000000..03629082c
--- /dev/null
+++ b/passes/techmap/tribuf.cc
@@ -0,0 +1,186 @@
+/*
+ * 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 TribufConfig {
+ bool merge_mode;
+ bool logic_mode;
+
+ TribufConfig() {
+ merge_mode = false;
+ logic_mode = false;
+ }
+};
+
+struct TribufWorker {
+ Module *module;
+ SigMap sigmap;
+ const TribufConfig &config;
+
+ TribufWorker(Module *module, const TribufConfig &config) : module(module), sigmap(module), config(config)
+ {
+ }
+
+ static bool is_all_z(SigSpec sig)
+ {
+ for (auto bit : sig)
+ if (bit != State::Sz)
+ return false;
+ return true;
+ }
+
+ void run()
+ {
+ dict<SigSpec, vector<Cell*>> tribuf_cells;
+ pool<SigBit> output_bits;
+
+ if (config.logic_mode)
+ for (auto wire : module->wires())
+ if (wire->port_output)
+ for (auto bit : sigmap(wire))
+ output_bits.insert(bit);
+
+ for (auto cell : module->selected_cells())
+ {
+ if (cell->type == "$tribuf")
+ tribuf_cells[sigmap(cell->getPort("\\Y"))].push_back(cell);
+
+ if (cell->type == "$_TBUF_")
+ tribuf_cells[sigmap(cell->getPort("\\Y"))].push_back(cell);
+
+ if (cell->type.in("$mux", "$_MUX_"))
+ {
+ IdString en_port = cell->type == "$mux" ? "\\EN" : "\\E";
+ IdString tri_type = cell->type == "$mux" ? "$tribuf" : "$_TBUF_";
+
+ if (is_all_z(cell->getPort("\\A")) && is_all_z(cell->getPort("\\B"))) {
+ module->remove(cell);
+ continue;
+ }
+
+ if (is_all_z(cell->getPort("\\A"))) {
+ cell->setPort("\\A", cell->getPort("\\B"));
+ cell->setPort(en_port, cell->getPort("\\S"));
+ cell->unsetPort("\\B");
+ cell->unsetPort("\\S");
+ cell->type = tri_type;
+ tribuf_cells[sigmap(cell->getPort("\\Y"))].push_back(cell);
+ continue;
+ }
+
+ if (is_all_z(cell->getPort("\\B"))) {
+ cell->setPort(en_port, module->Not(NEW_ID, cell->getPort("\\S")));
+ cell->unsetPort("\\B");
+ cell->unsetPort("\\S");
+ cell->type = tri_type;
+ tribuf_cells[sigmap(cell->getPort("\\Y"))].push_back(cell);
+ continue;
+ }
+ }
+ }
+
+ if (config.merge_mode || config.logic_mode)
+ {
+ for (auto &it : tribuf_cells)
+ {
+ bool no_tribuf = false;
+
+ if (config.logic_mode) {
+ no_tribuf = true;
+ for (auto bit : it.first)
+ if (output_bits.count(bit))
+ no_tribuf = false;
+ }
+
+ if (GetSize(it.second) <= 1 && !no_tribuf)
+ continue;
+
+ SigSpec pmux_b, pmux_s;
+ for (auto cell : it.second) {
+ if (cell->type == "$tribuf")
+ pmux_s.append(cell->getPort("\\EN"));
+ else
+ pmux_s.append(cell->getPort("\\E"));
+ pmux_b.append(cell->getPort("\\A"));
+ module->remove(cell);
+ }
+
+ SigSpec muxout = GetSize(pmux_s) > 1 ? module->Pmux(NEW_ID, SigSpec(State::Sx, GetSize(it.first)), pmux_b, pmux_s) : pmux_b;
+
+ if (no_tribuf)
+ module->connect(it.first, muxout);
+ else
+ module->addTribuf(NEW_ID, muxout, module->ReduceOr(NEW_ID, pmux_s), it.first);
+ }
+ }
+ }
+};
+
+struct TribufPass : public Pass {
+ TribufPass() : Pass("tribuf", "infer tri-state buffers") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" tribuf [options] [selection]\n");
+ log("\n");
+ log("This pass transforms $mux cells with 'z' inputs to tristate buffers.\n");
+ log("\n");
+ log(" -merge\n");
+ log(" merge multiple tri-state buffers driving the same net\n");
+ log(" into a single buffer.\n");
+ log("\n");
+ log(" -logic\n");
+ log(" convert tri-state buffers that do not drive output ports\n");
+ log(" to non-tristate logic. this option implies -merge.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ TribufConfig config;
+
+ log_header(design, "Executing TRIBUF pass.\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ if (args[argidx] == "-merge") {
+ config.merge_mode = true;
+ continue;
+ }
+ if (args[argidx] == "-logic") {
+ config.logic_mode = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto module : design->selected_modules()) {
+ TribufWorker worker(module, config);
+ worker.run();
+ }
+ }
+} TribufPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/tests/test_abcloop.cc b/passes/tests/test_abcloop.cc
index 753fa7bf2..09cb41954 100644
--- a/passes/tests/test_abcloop.cc
+++ b/passes/tests/test_abcloop.cc
@@ -127,9 +127,9 @@ static void test_abcloop()
module->fixup_ports();
Pass::call(design, "clean");
- ezDefaultSAT ez;
+ ezSatPtr ez;
SigMap sigmap(module);
- SatGen satgen(&ez, &sigmap);
+ SatGen satgen(ez.get(), &sigmap);
for (auto c : module->cells()) {
bool ok YS_ATTRIBUTE(unused) = satgen.importCell(c);
@@ -137,7 +137,7 @@ static void test_abcloop()
}
std::vector<int> in_vec = satgen.importSigSpec(in_sig);
- std::vector<int> inverse_in_vec = ez.vec_not(in_vec);
+ std::vector<int> inverse_in_vec = ez->vec_not(in_vec);
std::vector<int> out_vec = satgen.importSigSpec(out_sig);
@@ -148,7 +148,7 @@ static void test_abcloop()
assumptions.push_back((i & (1 << j)) ? in_vec.at(j) : inverse_in_vec.at(j));
std::vector<bool> results;
- if (!ez.solve(out_vec, results, assumptions)) {
+ if (!ez->solve(out_vec, results, assumptions)) {
log("No stable solution for input %d found -> recreate module.\n", i);
goto recreate_module;
}
@@ -156,10 +156,10 @@ static void test_abcloop()
for (int j = 0; j < 4; j++)
truthtab[i][j] = results[j];
- assumptions.push_back(ez.vec_ne(out_vec, ez.vec_const(results)));
+ assumptions.push_back(ez->vec_ne(out_vec, ez->vec_const(results)));
std::vector<bool> results2;
- if (ez.solve(out_vec, results2, assumptions)) {
+ if (ez->solve(out_vec, results2, assumptions)) {
log("Two stable solutions for input %d found -> recreate module.\n", i);
goto recreate_module;
}
@@ -177,9 +177,9 @@ static void test_abcloop()
log("\n");
log("Pre- and post-abc truth table:\n");
- ezDefaultSAT ez;
+ ezSatPtr ez;
SigMap sigmap(module);
- SatGen satgen(&ez, &sigmap);
+ SatGen satgen(ez.get(), &sigmap);
for (auto c : module->cells()) {
bool ok YS_ATTRIBUTE(unused) = satgen.importCell(c);
@@ -187,7 +187,7 @@ static void test_abcloop()
}
std::vector<int> in_vec = satgen.importSigSpec(in_sig);
- std::vector<int> inverse_in_vec = ez.vec_not(in_vec);
+ std::vector<int> inverse_in_vec = ez->vec_not(in_vec);
std::vector<int> out_vec = satgen.importSigSpec(out_sig);
@@ -204,7 +204,7 @@ static void test_abcloop()
truthtab2[i][j] = truthtab[i][j];
std::vector<bool> results;
- if (!ez.solve(out_vec, results, assumptions)) {
+ if (!ez->solve(out_vec, results, assumptions)) {
log("No stable solution for input %d found.\n", i);
found_error = true;
continue;
@@ -213,10 +213,10 @@ static void test_abcloop()
for (int j = 0; j < 4; j++)
truthtab2[i][j] = results[j];
- assumptions.push_back(ez.vec_ne(out_vec, ez.vec_const(results)));
+ assumptions.push_back(ez->vec_ne(out_vec, ez->vec_const(results)));
std::vector<bool> results2;
- if (ez.solve(out_vec, results2, assumptions)) {
+ if (ez->solve(out_vec, results2, assumptions)) {
log("Two stable solutions for input %d found -> recreate module.\n", i);
found_error = true;
}
diff --git a/passes/tests/test_autotb.cc b/passes/tests/test_autotb.cc
index 74ee0f5a9..59de111c2 100644
--- a/passes/tests/test_autotb.cc
+++ b/passes/tests/test_autotb.cc
@@ -2,11 +2,11 @@
* 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
@@ -73,9 +73,14 @@ static std::string idy(std::string str1, std::string str2 = std::string(), std::
static void autotest(std::ostream &f, RTLIL::Design *design, int num_iter)
{
+ f << stringf("`ifndef outfile\n");
+ f << stringf("\t`define outfile \"/dev/stdout\"\n");
+ f << stringf("`endif\n");
+
f << stringf("module testbench;\n\n");
- f << stringf("integer i;\n\n");
+ f << stringf("integer i;\n");
+ f << stringf("integer file;\n\n");
f << stringf("reg [31:0] xorshift128_x = 123456789;\n");
f << stringf("reg [31:0] xorshift128_y = 362436069;\n");
@@ -195,7 +200,7 @@ static void autotest(std::ostream &f, RTLIL::Design *design, int num_iter)
f << stringf(" } = {");
for (auto it = signal_clk.begin(); it != signal_clk.end(); it++)
f << stringf("%s %s", it == signal_clk.begin() ? "" : ",", it->first.c_str());
- f << stringf(" } ^ (%d'b1 << (xorshift128_w %% %d));\n", total_clock_bits, total_clock_bits);
+ f << stringf(" } ^ (%d'b1 << (xorshift128_w %% %d));\n", total_clock_bits, total_clock_bits + 1);
}
f << stringf("end\n");
f << stringf("endtask\n\n");
@@ -206,7 +211,7 @@ static void autotest(std::ostream &f, RTLIL::Design *design, int num_iter)
f << stringf("task %s;\n", idy(mod->name.str(), "print_status").c_str());
f << stringf("begin\n");
- f << stringf("\t$display(\"#OUT# %%b %%b %%b %%t %%d\", {");
+ f << stringf("\t$fdisplay(file, \"#OUT# %%b %%b %%b %%t %%d\", {");
if (signal_in.size())
for (auto it = signal_in.begin(); it != signal_in.end(); it++) {
f << stringf("%s %s", it == signal_in.begin() ? "" : ",", it->first.c_str());
@@ -218,7 +223,8 @@ static void autotest(std::ostream &f, RTLIL::Design *design, int num_iter)
if (len > 0)
header2 += shorthand, len--;
header1.push_back(" " + it->first);
- header1.back()[0] = shorthand++;
+ header1.back()[0] = shorthand;
+ shorthand = shorthand == 'Z' ? 'A' : shorthand+1;
}
else {
f << stringf(" 1'bx");
@@ -237,7 +243,8 @@ static void autotest(std::ostream &f, RTLIL::Design *design, int num_iter)
if (len > 0)
header2 += shorthand, len--;
header1.push_back(" " + it->first);
- header1.back()[0] = shorthand++;
+ header1.back()[0] = shorthand;
+ shorthand = shorthand == 'Z' ? 'A' : shorthand+1;
}
} else {
f << stringf(" 1'bx");
@@ -256,7 +263,8 @@ static void autotest(std::ostream &f, RTLIL::Design *design, int num_iter)
if (len > 0)
header2 += shorthand, len--;
header1.push_back(" " + it->first);
- header1.back()[0] = shorthand++;
+ header1.back()[0] = shorthand;
+ shorthand = shorthand == 'Z' ? 'A' : shorthand+1;
}
} else {
f << stringf(" 1'bx");
@@ -268,17 +276,17 @@ static void autotest(std::ostream &f, RTLIL::Design *design, int num_iter)
f << stringf("task %s;\n", idy(mod->name.str(), "print_header").c_str());
f << stringf("begin\n");
- f << stringf("\t$display(\"#OUT#\");\n");
+ f << stringf("\t$fdisplay(file, \"#OUT#\");\n");
for (auto &hdr : header1)
- f << stringf("\t$display(\"#OUT# %s\");\n", hdr.c_str());
- f << stringf("\t$display(\"#OUT#\");\n");
- f << stringf("\t$display(\"#OUT# %s\");\n", header2.c_str());
+ f << stringf("\t$fdisplay(file, \"#OUT# %s\");\n", hdr.c_str());
+ f << stringf("\t$fdisplay(file, \"#OUT#\");\n");
+ f << stringf("\t$fdisplay(file, \"#OUT# %s\");\n", header2.c_str());
f << stringf("end\n");
f << stringf("endtask\n\n");
f << stringf("task %s;\n", idy(mod->name.str(), "test").c_str());
f << stringf("begin\n");
- f << stringf("\t$display(\"#OUT#\\n#OUT# ==== %s ====\");\n", idy(mod->name.str()).c_str());
+ f << stringf("\t$fdisplay(file, \"#OUT#\\n#OUT# ==== %s ====\");\n", idy(mod->name.str()).c_str());
f << stringf("\t%s;\n", idy(mod->name.str(), "reset").c_str());
f << stringf("\tfor (i=0; i<%d; i=i+1) begin\n", num_iter);
f << stringf("\t\tif (i %% 20 == 0) %s;\n", idy(mod->name.str(), "print_header").c_str());
@@ -293,9 +301,11 @@ static void autotest(std::ostream &f, RTLIL::Design *design, int num_iter)
f << stringf("initial begin\n");
f << stringf("\t// $dumpfile(\"testbench.vcd\");\n");
f << stringf("\t// $dumpvars(0, testbench);\n");
+ f << stringf("\tfile = $fopen(`outfile);\n");
for (auto it = design->modules_.begin(); it != design->modules_.end(); ++it)
if (!it->second->get_bool_attribute("\\gentb_skip"))
f << stringf("\t%s;\n", idy(it->first.str(), "test").c_str());
+ f << stringf("\t$fclose(file);\n");
f << stringf("\t$finish;\n");
f << stringf("end\n\n");
@@ -310,7 +320,7 @@ struct TestAutotbBackend : public Backend {
log("\n");
log(" test_autotb [options] [filename]\n");
log("\n");
- log("Automatically create primitive verilog test benches for all modules in the\n");
+ log("Automatically create primitive Verilog test benches for all modules in the\n");
log("design. The generated testbenches toggle the input pins of the module in\n");
log("a semi-random manner and dumps the resulting output signals.\n");
log("\n");
@@ -326,14 +336,14 @@ struct TestAutotbBackend : public Backend {
log("low in order to explore more inner states in a state machine.\n");
log("\n");
log(" -n <int>\n");
- log(" number of iterations the test bench shuld run (default = 1000)\n");
+ log(" number of iterations the test bench should run (default = 1000)\n");
log("\n");
}
virtual void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design)
{
int num_iter = 1000;
- log_header("Executing TEST_AUTOTB backend (auto-generate pseudo-random test benches).\n");
+ log_header(design, "Executing TEST_AUTOTB backend (auto-generate pseudo-random test benches).\n");
int argidx;
for (argidx = 1; argidx < GetSize(args); argidx++)
@@ -349,6 +359,6 @@ struct TestAutotbBackend : public Backend {
autotest(*f, design, num_iter);
}
} TestAutotbBackend;
-
+
PRIVATE_NAMESPACE_END
diff --git a/passes/tests/test_cell.cc b/passes/tests/test_cell.cc
index ea2ab1e65..8b800d414 100644
--- a/passes/tests/test_cell.cc
+++ b/passes/tests/test_cell.cc
@@ -164,6 +164,41 @@ static void create_gold_module(RTLIL::Design *design, RTLIL::IdString cell_type,
cell->setParam("\\LUT", config.as_const());
}
+ if (cell_type == "$sop")
+ {
+ int width = 1 + xorshift32(8);
+ int depth = 1 + xorshift32(8);
+
+ wire = module->addWire("\\A");
+ wire->width = width;
+ wire->port_input = true;
+ cell->setPort("\\A", wire);
+
+ wire = module->addWire("\\Y");
+ wire->port_output = true;
+ cell->setPort("\\Y", wire);
+
+ RTLIL::SigSpec config;
+ for (int i = 0; i < width*depth; i++)
+ switch (xorshift32(3)) {
+ case 0:
+ config.append(RTLIL::S1);
+ config.append(RTLIL::S0);
+ break;
+ case 1:
+ config.append(RTLIL::S0);
+ config.append(RTLIL::S1);
+ break;
+ case 2:
+ config.append(RTLIL::S0);
+ config.append(RTLIL::S0);
+ break;
+ }
+
+ cell->setParam("\\DEPTH", depth);
+ cell->setParam("\\TABLE", config.as_const());
+ }
+
if (cell_type_flags.find('A') != std::string::npos) {
wire = module->addWire("\\A");
wire->width = 1 + xorshift32(8);
@@ -256,7 +291,7 @@ static void create_gold_module(RTLIL::Design *design, RTLIL::IdString cell_type,
case 2:
n = xorshift32(GetSize(sig));
m = xorshift32(GetSize(sig));
- for (int i = std::min(n, m); i < std::max(n, m); i++)
+ for (int i = min(n, m); i < max(n, m); i++)
sig[i] = xorshift32(2) == 1 ? RTLIL::S1 : RTLIL::S0;
break;
}
@@ -278,10 +313,10 @@ static void run_eval_test(RTLIL::Design *design, bool verbose, bool nosat, std::
RTLIL::Module *gate_mod = design->module("\\gate");
ConstEval gold_ce(gold_mod), gate_ce(gate_mod);
- ezDefaultSAT ez1, ez2;
+ ezSatPtr ez1, ez2;
SigMap sigmap(gold_mod);
- SatGen satgen1(&ez1, &sigmap);
- SatGen satgen2(&ez2, &sigmap);
+ SatGen satgen1(ez1.get(), &sigmap);
+ SatGen satgen2(ez2.get(), &sigmap);
satgen2.model_undef = true;
if (!nosat)
@@ -433,7 +468,7 @@ static void run_eval_test(RTLIL::Design *design, bool verbose, bool nosat, std::
std::vector<int> sat1_model = satgen1.importSigSpec(out_sig);
std::vector<bool> sat1_model_value;
- if (!ez1.solve(sat1_model, sat1_model_value, ez1.vec_eq(sat1_in_sig, sat1_in_val)))
+ if (!ez1->solve(sat1_model, sat1_model_value, ez1->vec_eq(sat1_in_sig, sat1_in_val)))
log_error("Evaluating sat model 1 (no undef modeling) failed!\n");
if (verbose) {
@@ -468,7 +503,7 @@ static void run_eval_test(RTLIL::Design *design, bool verbose, bool nosat, std::
std::vector<bool> sat2_model_value;
- if (!ez2.solve(sat2_model, sat2_model_value, ez2.vec_eq(sat2_in_def_sig, sat2_in_def_val), ez2.vec_eq(sat2_in_undef_sig, sat2_in_undef_val)))
+ if (!ez2->solve(sat2_model, sat2_model_value, ez2->vec_eq(sat2_in_def_sig, sat2_in_def_val), ez2->vec_eq(sat2_in_undef_sig, sat2_in_undef_val)))
log_error("Evaluating sat model 2 (undef modeling) failed!\n");
if (verbose) {
@@ -534,7 +569,10 @@ struct TestCellPass : public Pass {
log(" pass this option to techmap.\n");
log("\n");
log(" -simlib\n");
- log(" use \"techmap -map +/simlib.v -max_iter 2 -autoproc\"\n");
+ log(" use \"techmap -D SIMLIB_NOCHECKS -map +/simlib.v -max_iter 2 -autoproc\"\n");
+ log("\n");
+ log(" -aigmap\n");
+ log(" instead of calling \"techmap\", call \"aigmap\"\n");
log("\n");
log(" -muxdiv\n");
log(" when creating test benches with dividers, create an additional mux\n");
@@ -549,11 +587,14 @@ struct TestCellPass : public Pass {
log(" -nosat\n");
log(" do not check SAT model or run SAT equivalence checking\n");
log("\n");
+ log(" -noeval\n");
+ log(" do not check const-eval models\n");
+ log("\n");
log(" -v\n");
log(" print additional debug information to the console\n");
log("\n");
log(" -vlog {filename}\n");
- log(" create a verilog test bench to test simlib and write_verilog\n");
+ log(" create a Verilog test bench to test simlib and write_verilog\n");
log("\n");
}
virtual void execute(std::vector<std::string> args, RTLIL::Design*)
@@ -567,6 +608,7 @@ struct TestCellPass : public Pass {
bool verbose = false;
bool constmode = false;
bool nosat = false;
+ bool noeval = false;
int argidx;
for (argidx = 1; argidx < GetSize(args); argidx++)
@@ -597,7 +639,11 @@ struct TestCellPass : public Pass {
continue;
}
if (args[argidx] == "-simlib") {
- techmap_cmd = "techmap -map +/simlib.v -max_iter 2 -autoproc";
+ techmap_cmd = "techmap -D SIMLIB_NOCHECKS -map +/simlib.v -max_iter 2 -autoproc";
+ continue;
+ }
+ if (args[argidx] == "-aigmap") {
+ techmap_cmd = "aigmap";
continue;
}
if (args[argidx] == "-muxdiv") {
@@ -612,6 +658,10 @@ struct TestCellPass : public Pass {
nosat = true;
continue;
}
+ if (args[argidx] == "-noeval") {
+ noeval = true;
+ continue;
+ }
if (args[argidx] == "-v") {
verbose = true;
continue;
@@ -682,6 +732,7 @@ struct TestCellPass : public Pass {
// cell_types["$assert"] = "A";
cell_types["$lut"] = "*";
+ cell_types["$sop"] = "*";
cell_types["$alu"] = "ABSY";
cell_types["$lcu"] = "*";
cell_types["$macc"] = "*";
@@ -765,7 +816,8 @@ struct TestCellPass : public Pass {
Backend::backend_call(design, &vlog_file, "<test_cell -vlog>", "verilog -selected -noexpr");
uut_names.push_back(uut_name);
}
- run_eval_test(design, verbose, nosat, uut_name, vlog_file);
+ if (!noeval)
+ run_eval_test(design, verbose, nosat, uut_name, vlog_file);
}
delete design;
}