summaryrefslogtreecommitdiff
path: root/passes
diff options
context:
space:
mode:
Diffstat (limited to 'passes')
-rw-r--r--passes/cmds/Makefile.inc2
-rw-r--r--passes/cmds/bugpoint.cc369
-rw-r--r--passes/cmds/chformal.cc4
-rw-r--r--passes/cmds/connect.cc6
-rw-r--r--passes/cmds/rename.cc148
-rw-r--r--passes/cmds/select.cc60
-rw-r--r--passes/cmds/setundef.cc22
-rw-r--r--passes/cmds/show.cc2
-rw-r--r--passes/cmds/tee.cc4
-rw-r--r--passes/equiv/Makefile.inc2
-rw-r--r--passes/equiv/equiv_make.cc86
-rw-r--r--passes/equiv/equiv_opt.cc157
-rw-r--r--passes/fsm/fsm_detect.cc8
-rw-r--r--passes/fsm/fsm_extract.cc4
-rw-r--r--passes/fsm/fsm_opt.cc3
-rw-r--r--passes/hierarchy/hierarchy.cc279
-rw-r--r--passes/hierarchy/uniquify.cc2
-rw-r--r--passes/memory/memory_bram.cc44
-rw-r--r--passes/memory/memory_collect.cc3
-rw-r--r--passes/memory/memory_dff.cc2
-rw-r--r--passes/opt/Makefile.inc1
-rw-r--r--passes/opt/opt_expr.cc240
-rw-r--r--passes/opt/opt_lut.cc607
-rw-r--r--passes/opt/opt_muxtree.cc18
-rw-r--r--passes/opt/opt_rmdff.cc13
-rw-r--r--passes/opt/share.cc8
-rw-r--r--passes/opt/wreduce.cc129
-rw-r--r--passes/pmgen/.gitignore1
-rw-r--r--passes/pmgen/Makefile.inc8
-rw-r--r--passes/pmgen/README.md224
-rw-r--r--passes/pmgen/ice40_dsp.cc237
-rw-r--r--passes/pmgen/ice40_dsp.pmg160
-rw-r--r--passes/pmgen/pmgen.py486
-rw-r--r--passes/proc/proc_clean.cc44
-rw-r--r--passes/sat/Makefile.inc4
-rw-r--r--passes/sat/async2sync.cc53
-rw-r--r--passes/sat/cutpoint.cc168
-rw-r--r--passes/sat/fmcombine.cc341
-rw-r--r--passes/sat/mutate.cc956
-rw-r--r--passes/sat/supercover.cc92
-rw-r--r--passes/techmap/Makefile.inc1
-rw-r--r--passes/techmap/abc.cc93
-rw-r--r--passes/techmap/deminout.cc7
-rw-r--r--passes/techmap/dff2dffe.cc35
-rw-r--r--passes/techmap/dffinit.cc33
-rw-r--r--passes/techmap/dfflibmap.cc16
-rw-r--r--passes/techmap/flowmap.cc1613
-rw-r--r--passes/techmap/libparse.cc176
-rw-r--r--passes/techmap/libparse.h10
-rw-r--r--passes/techmap/lut2mux.cc2
-rw-r--r--passes/tests/flowmap/flow.v22
-rw-r--r--passes/tests/flowmap/flowp.v16
-rw-r--r--passes/tests/flowmap/pack1.v11
-rw-r--r--passes/tests/flowmap/pack1p.v11
-rw-r--r--passes/tests/flowmap/pack2.v15
-rw-r--r--passes/tests/flowmap/pack2p.v15
-rw-r--r--passes/tests/flowmap/pack3.v15
-rw-r--r--passes/tests/flowmap/pack3p.v15
58 files changed, 6847 insertions, 256 deletions
diff --git a/passes/cmds/Makefile.inc b/passes/cmds/Makefile.inc
index 44a83b2b..c8067a8b 100644
--- a/passes/cmds/Makefile.inc
+++ b/passes/cmds/Makefile.inc
@@ -29,4 +29,4 @@ OBJS += passes/cmds/chformal.o
OBJS += passes/cmds/chtype.o
OBJS += passes/cmds/blackbox.o
OBJS += passes/cmds/ltp.o
-
+OBJS += passes/cmds/bugpoint.o
diff --git a/passes/cmds/bugpoint.cc b/passes/cmds/bugpoint.cc
new file mode 100644
index 00000000..606276e6
--- /dev/null
+++ b/passes/cmds/bugpoint.cc
@@ -0,0 +1,369 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2018 whitequark <whitequark@whitequark.org>
+ *
+ * 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 "backends/ilang/ilang_backend.h"
+
+USING_YOSYS_NAMESPACE
+using namespace ILANG_BACKEND;
+PRIVATE_NAMESPACE_BEGIN
+
+struct BugpointPass : public Pass {
+ BugpointPass() : Pass("bugpoint", "minimize testcases") { }
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" bugpoint [options]\n");
+ log("\n");
+ log("This command minimizes testcases that crash Yosys. It removes an arbitrary part\n");
+ log("of the design and recursively invokes Yosys with a given script, repeating these\n");
+ log("steps while it can find a smaller design that still causes a crash. Once this\n");
+ log("command finishes, it replaces the current design with the smallest testcase it\n");
+ log("was able to produce.\n");
+ log("\n");
+ log("It is possible to specify the kinds of design part that will be removed. If none\n");
+ log("are specified, all parts of design will be removed.\n");
+ log("\n");
+ log(" -yosys <filename>\n");
+ log(" use this Yosys binary. if not specified, `yosys` is used.\n");
+ log("\n");
+ log(" -script <filename>\n");
+ log(" use this script to crash Yosys. required.\n");
+ log("\n");
+ log(" -grep <string>\n");
+ log(" only consider crashes that place this string in the log file.\n");
+ log("\n");
+ log(" -fast\n");
+ log(" run `clean -purge` after each minimization step. converges faster, but\n");
+ log(" produces larger testcases, and may fail to produce any testcase at all if\n");
+ log(" the crash is related to dangling wires.\n");
+ log("\n");
+ log(" -clean\n");
+ log(" run `clean -purge` before checking testcase and after finishing. produces\n");
+ log(" smaller and more useful testcases, but may fail to produce any testcase\n");
+ log(" at all if the crash is related to dangling wires.\n");
+ log("\n");
+ log(" -modules\n");
+ log(" try to remove modules.\n");
+ log("\n");
+ log(" -ports\n");
+ log(" try to remove module ports.\n");
+ log("\n");
+ log(" -cells\n");
+ log(" try to remove cells.\n");
+ log("\n");
+ log(" -connections\n");
+ log(" try to reconnect ports to 'x.\n");
+ log("\n");
+ }
+
+ bool run_yosys(RTLIL::Design *design, string yosys_cmd, string script)
+ {
+ design->sort();
+
+ std::ofstream f("bugpoint-case.il");
+ ILANG_BACKEND::dump_design(f, design, /*only_selected=*/false, /*flag_m=*/true, /*flag_n=*/false);
+ f.close();
+
+ string yosys_cmdline = stringf("%s -qq -L bugpoint-case.log -s %s bugpoint-case.il", yosys_cmd.c_str(), script.c_str());
+ return run_command(yosys_cmdline) == 0;
+ }
+
+ bool check_logfile(string grep)
+ {
+ if (grep.empty())
+ return true;
+
+ std::ifstream f("bugpoint-case.log");
+ while (!f.eof())
+ {
+ string line;
+ getline(f, line);
+ if (line.find(grep) != std::string::npos)
+ return true;
+ }
+ return false;
+ }
+
+ RTLIL::Design *clean_design(RTLIL::Design *design, bool do_clean = true, bool do_delete = false)
+ {
+ if (!do_clean)
+ return design;
+
+ RTLIL::Design *design_copy = new RTLIL::Design;
+ for (auto &it : design->modules_)
+ design_copy->add(it.second->clone());
+ Pass::call(design_copy, "clean -purge");
+
+ if (do_delete)
+ delete design;
+ return design_copy;
+ }
+
+ RTLIL::Design *simplify_something(RTLIL::Design *design, int &seed, bool stage2, bool modules, bool ports, bool cells, bool connections)
+ {
+ RTLIL::Design *design_copy = new RTLIL::Design;
+ for (auto &it : design->modules_)
+ design_copy->add(it.second->clone());
+
+ int index = 0;
+ if (modules)
+ {
+ for (auto &it : design_copy->modules_)
+ {
+ if (it.second->get_bool_attribute("\\blackbox"))
+ continue;
+
+ if (index++ == seed)
+ {
+ log("Trying to remove module %s.\n", it.first.c_str());
+ design_copy->remove(it.second);
+ return design_copy;
+ }
+ }
+ }
+ if (ports)
+ {
+ for (auto mod : design_copy->modules())
+ {
+ if (mod->get_bool_attribute("\\blackbox"))
+ continue;
+
+ for (auto wire : mod->wires())
+ {
+ if (!stage2 && wire->get_bool_attribute("$bugpoint"))
+ continue;
+
+ if (wire->port_input || wire->port_output)
+ {
+ if (index++ == seed)
+ {
+ log("Trying to remove module port %s.\n", log_signal(wire));
+ wire->port_input = wire->port_output = false;
+ mod->fixup_ports();
+ return design_copy;
+ }
+ }
+ }
+ }
+ }
+ if (cells)
+ {
+ for (auto mod : design_copy->modules())
+ {
+ if (mod->get_bool_attribute("\\blackbox"))
+ continue;
+
+ for (auto &it : mod->cells_)
+ {
+ if (index++ == seed)
+ {
+ log("Trying to remove cell %s.%s.\n", mod->name.c_str(), it.first.c_str());
+ mod->remove(it.second);
+ return design_copy;
+ }
+ }
+ }
+ }
+ if (connections)
+ {
+ for (auto mod : design_copy->modules())
+ {
+ if (mod->get_bool_attribute("\\blackbox"))
+ continue;
+
+ for (auto cell : mod->cells())
+ {
+ for (auto it : cell->connections_)
+ {
+ RTLIL::SigSpec port = cell->getPort(it.first);
+ bool is_undef = port.is_fully_undef();
+ bool is_port = port.is_wire() && (port.as_wire()->port_input || port.as_wire()->port_output);
+
+ if(is_undef || (!stage2 && is_port))
+ continue;
+
+ if (index++ == seed)
+ {
+ log("Trying to remove cell port %s.%s.%s.\n", mod->name.c_str(), cell->name.c_str(), it.first.c_str());
+ RTLIL::SigSpec port_x(State::Sx, port.size());
+ cell->unsetPort(it.first);
+ cell->setPort(it.first, port_x);
+ return design_copy;
+ }
+
+ if (!stage2 && (cell->input(it.first) || cell->output(it.first)) && index++ == seed)
+ {
+ log("Trying to expose cell port %s.%s.%s as module port.\n", mod->name.c_str(), cell->name.c_str(), it.first.c_str());
+ RTLIL::Wire *wire = mod->addWire(NEW_ID, port.size());
+ wire->set_bool_attribute("$bugpoint");
+ wire->port_input = cell->input(it.first);
+ wire->port_output = cell->output(it.first);
+ cell->unsetPort(it.first);
+ cell->setPort(it.first, wire);
+ mod->fixup_ports();
+ return design_copy;
+ }
+ }
+ }
+ }
+ }
+ return NULL;
+ }
+
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ string yosys_cmd = "yosys", script, grep;
+ bool fast = false, clean = false;
+ bool modules = false, ports = false, cells = false, connections = false, has_part = false;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ if (args[argidx] == "-yosys" && argidx + 1 < args.size()) {
+ yosys_cmd = args[++argidx];
+ continue;
+ }
+ if (args[argidx] == "-script" && argidx + 1 < args.size()) {
+ script = args[++argidx];
+ continue;
+ }
+ if (args[argidx] == "-grep" && argidx + 1 < args.size()) {
+ grep = args[++argidx];
+ continue;
+ }
+ if (args[argidx] == "-fast") {
+ fast = true;
+ continue;
+ }
+ if (args[argidx] == "-clean") {
+ clean = true;
+ continue;
+ }
+ if (args[argidx] == "-modules") {
+ modules = true;
+ has_part = true;
+ continue;
+ }
+ if (args[argidx] == "-ports") {
+ ports = true;
+ has_part = true;
+ continue;
+ }
+ if (args[argidx] == "-cells") {
+ cells = true;
+ has_part = true;
+ continue;
+ }
+ if (args[argidx] == "-connections") {
+ connections = true;
+ has_part = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ if (!has_part)
+ {
+ modules = true;
+ ports = true;
+ cells = true;
+ connections = true;
+ }
+
+ if (!design->full_selection())
+ log_cmd_error("This command only operates on fully selected designs!\n");
+
+ RTLIL::Design *crashing_design = clean_design(design, clean);
+ if (run_yosys(crashing_design, yosys_cmd, script))
+ log_cmd_error("The provided script file and Yosys binary do not crash on this design!\n");
+ if (!check_logfile(grep))
+ log_cmd_error("The provided grep string is not found in the log file!\n");
+
+ int seed = 0, crashing_seed = seed;
+ bool found_something = false, stage2 = false;
+ while (true)
+ {
+ if (RTLIL::Design *simplified = simplify_something(crashing_design, seed, stage2, modules, ports, cells, connections))
+ {
+ simplified = clean_design(simplified, fast, /*do_delete=*/true);
+
+ bool crashes;
+ if (clean)
+ {
+ RTLIL::Design *testcase = clean_design(simplified);
+ crashes = !run_yosys(testcase, yosys_cmd, script);
+ delete testcase;
+ }
+ else
+ {
+ crashes = !run_yosys(simplified, yosys_cmd, script);
+ }
+
+ if (crashes && check_logfile(grep))
+ {
+ log("Testcase crashes.\n");
+ if (crashing_design != design)
+ delete crashing_design;
+ crashing_design = simplified;
+ crashing_seed = seed;
+ found_something = true;
+ }
+ else
+ {
+ log("Testcase does not crash.\n");
+ delete simplified;
+ seed++;
+ }
+ }
+ else
+ {
+ seed = 0;
+ if (found_something)
+ found_something = false;
+ else
+ {
+ if (!stage2)
+ {
+ log("Demoting introduced module ports.\n");
+ stage2 = true;
+ }
+ else
+ {
+ log("Simplifications exhausted.\n");
+ break;
+ }
+ }
+ }
+ }
+
+ if (crashing_design != design)
+ {
+ Pass::call(design, "design -reset");
+ crashing_design = clean_design(crashing_design, clean, /*do_delete=*/true);
+ for (auto &it : crashing_design->modules_)
+ design->add(it.second->clone());
+ delete crashing_design;
+ }
+ }
+} BugpointPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/cmds/chformal.cc b/passes/cmds/chformal.cc
index 522758ea..7e32da65 100644
--- a/passes/cmds/chformal.cc
+++ b/passes/cmds/chformal.cc
@@ -32,7 +32,7 @@ struct ChformalPass : public Pass {
log(" chformal [types] [mode] [options] [selection]\n");
log("\n");
log("Make changes to the formal constraints of the design. The [types] options\n");
- log("the type of constraint to operate on. If none of the folling options is given,\n");
+ log("the type of constraint to operate on. If none of the following options are given,\n");
log("the command will operate on all constraint types:\n");
log("\n");
log(" -assert $assert cells, representing assert(...) constraints\n");
@@ -59,7 +59,7 @@ struct ChformalPass : public Pass {
log(" -assume2assert\n");
log(" -live2fair\n");
log(" -fair2live\n");
- log(" change the roles of cells as indicated. this options can be combined\n");
+ log(" change the roles of cells as indicated. these options can be combined\n");
log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
diff --git a/passes/cmds/connect.cc b/passes/cmds/connect.cc
index d480b79a..f93bada2 100644
--- a/passes/cmds/connect.cc
+++ b/passes/cmds/connect.cc
@@ -137,7 +137,7 @@ struct ConnectPass : public Pass {
if (!set_lhs.empty())
{
if (!unset_expr.empty() || !port_cell.empty())
- log_cmd_error("Cant use -set together with -unset and/or -port.\n");
+ log_cmd_error("Can't use -set together with -unset and/or -port.\n");
RTLIL::SigSpec sig_lhs, sig_rhs;
if (!RTLIL::SigSpec::parse_sel(sig_lhs, design, module, set_lhs))
@@ -157,7 +157,7 @@ struct ConnectPass : public Pass {
if (!unset_expr.empty())
{
if (!port_cell.empty() || flag_nounset)
- log_cmd_error("Cant use -unset together with -port and/or -nounset.\n");
+ log_cmd_error("Can't use -unset together with -port and/or -nounset.\n");
RTLIL::SigSpec sig;
if (!RTLIL::SigSpec::parse_sel(sig, design, module, unset_expr))
@@ -170,7 +170,7 @@ struct ConnectPass : public Pass {
if (!port_cell.empty())
{
if (flag_nounset)
- log_cmd_error("Cant use -port together with -nounset.\n");
+ log_cmd_error("Can't use -port together with -nounset.\n");
if (module->cells_.count(RTLIL::escape_id(port_cell)) == 0)
log_cmd_error("Can't find cell %s.\n", port_cell.c_str());
diff --git a/passes/cmds/rename.cc b/passes/cmds/rename.cc
index dce576fd..9b1830b7 100644
--- a/passes/cmds/rename.cc
+++ b/passes/cmds/rename.cc
@@ -24,7 +24,7 @@
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
-static void rename_in_module(RTLIL::Module *module, std::string from_name, std::string to_name)
+static void rename_in_module(RTLIL::Module *module, std::string from_name, std::string to_name, bool flag_output)
{
from_name = RTLIL::escape_id(from_name);
to_name = RTLIL::escape_id(to_name);
@@ -37,13 +37,18 @@ static void rename_in_module(RTLIL::Module *module, std::string from_name, std::
Wire *w = it.second;
log("Renaming wire %s to %s in module %s.\n", log_id(w), log_id(to_name), log_id(module));
module->rename(w, to_name);
- if (w->port_id)
+ if (w->port_id || flag_output) {
+ if (flag_output)
+ w->port_output = true;
module->fixup_ports();
+ }
return;
}
for (auto &it : module->cells_)
if (it.first == from_name) {
+ if (flag_output)
+ log_cmd_error("Called with -output but the specified object is a cell.\n");
log("Renaming cell %s to %s in module %s.\n", log_id(it.second), log_id(to_name), log_id(module));
module->rename(it.second, to_name);
return;
@@ -52,6 +57,51 @@ static void rename_in_module(RTLIL::Module *module, std::string from_name, std::
log_cmd_error("Object `%s' not found!\n", from_name.c_str());
}
+static std::string derive_name_from_src(const std::string &src, int counter)
+{
+ std::string src_base = src.substr(0, src.find('|'));
+ if (src_base.empty())
+ return stringf("$%d", counter);
+ else
+ return stringf("\\%s$%d", src_base.c_str(), counter);
+}
+
+static IdString derive_name_from_wire(const RTLIL::Cell &cell)
+{
+ // Find output
+ const SigSpec *output = nullptr;
+ int num_outputs = 0;
+ for (auto &connection : cell.connections()) {
+ if (cell.output(connection.first)) {
+ output = &connection.second;
+ num_outputs++;
+ }
+ }
+
+ if (num_outputs != 1) // Skip cells thad drive multiple outputs
+ return cell.name;
+
+ std::string name = "";
+ for (auto &chunk : output->chunks()) {
+ // Skip cells that drive privately named wires
+ if (!chunk.wire || chunk.wire->name.str()[0] == '$')
+ return cell.name;
+
+ if (name != "")
+ name += "$";
+
+ name += chunk.wire->name.str();
+ if (chunk.wire->width != chunk.width) {
+ name += "[";
+ if (chunk.width != 1)
+ name += std::to_string(chunk.offset + chunk.width) + ":";
+ name += std::to_string(chunk.offset) + "]";
+ }
+ }
+
+ return name + cell.type.str();
+}
+
struct RenamePass : public Pass {
RenamePass() : Pass("rename", "rename object in the design") { }
void help() YS_OVERRIDE
@@ -64,6 +114,25 @@ struct RenamePass : public Pass {
log("by this command.\n");
log("\n");
log("\n");
+ log("\n");
+ log(" rename -output old_name new_name\n");
+ log("\n");
+ log("Like above, but also make the wire an output. This will fail if the object is\n");
+ log("not a wire.\n");
+ log("\n");
+ log("\n");
+ log(" rename -src [selection]\n");
+ log("\n");
+ log("Assign names auto-generated from the src attribute to all selected wires and\n");
+ log("cells with private names.\n");
+ log("\n");
+ log("\n");
+ log(" rename -wire [selection]\n");
+ log("\n");
+ log("Assign auto-generated names based on the wires they drive to all selected\n");
+ log("cells with private names. Ignores cells driving privatly named wires.\n");
+ log("\n");
+ log("\n");
log(" rename -enumerate [-pattern <pattern>] [selection]\n");
log("\n");
log("Assign short auto-generated names to all selected wires and cells with private\n");
@@ -71,11 +140,13 @@ struct RenamePass : public Pass {
log("The character %% in the pattern is replaced with a integer number. The default\n");
log("pattern is '_%%_'.\n");
log("\n");
+ log("\n");
log(" rename -hide [selection]\n");
log("\n");
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("\n");
log(" rename -top new_name\n");
log("\n");
log("Rename top module.\n");
@@ -84,15 +155,33 @@ struct RenamePass : public Pass {
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
std::string pattern_prefix = "_", pattern_suffix = "_";
+ bool flag_src = false;
+ bool flag_wire = false;
bool flag_enumerate = false;
bool flag_hide = false;
bool flag_top = false;
+ bool flag_output = false;
bool got_mode = false;
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++)
{
std::string arg = args[argidx];
+ if (arg == "-src" && !got_mode) {
+ flag_src = true;
+ got_mode = true;
+ continue;
+ }
+ if (arg == "-output" && !got_mode) {
+ flag_output = true;
+ got_mode = true;
+ continue;
+ }
+ if (arg == "-wire" && !got_mode) {
+ flag_wire = true;
+ got_mode = true;
+ continue;
+ }
if (arg == "-enumerate" && !got_mode) {
flag_enumerate = true;
got_mode = true;
@@ -117,6 +206,57 @@ struct RenamePass : public Pass {
break;
}
+ if (flag_src)
+ {
+ extra_args(args, argidx, design);
+
+ for (auto &mod : design->modules_)
+ {
+ int counter = 0;
+
+ RTLIL::Module *module = mod.second;
+ if (!design->selected(module))
+ continue;
+
+ dict<RTLIL::IdString, RTLIL::Wire*> new_wires;
+ for (auto &it : module->wires_) {
+ if (it.first[0] == '$' && design->selected(module, it.second))
+ it.second->name = derive_name_from_src(it.second->get_src_attribute(), counter++);
+ new_wires[it.second->name] = it.second;
+ }
+ module->wires_.swap(new_wires);
+ module->fixup_ports();
+
+ dict<RTLIL::IdString, RTLIL::Cell*> new_cells;
+ for (auto &it : module->cells_) {
+ if (it.first[0] == '$' && design->selected(module, it.second))
+ it.second->name = derive_name_from_src(it.second->get_src_attribute(), counter++);
+ new_cells[it.second->name] = it.second;
+ }
+ module->cells_.swap(new_cells);
+ }
+ }
+ else
+ if (flag_wire)
+ {
+ extra_args(args, argidx, design);
+
+ for (auto &mod : design->modules_)
+ {
+ RTLIL::Module *module = mod.second;
+ if (!design->selected(module))
+ continue;
+
+ dict<RTLIL::IdString, RTLIL::Cell*> new_cells;
+ for (auto &it : module->cells_) {
+ if (it.first[0] == '$' && design->selected(module, it.second))
+ it.second->name = derive_name_from_wire(*it.second);
+ new_cells[it.second->name] = it.second;
+ }
+ module->cells_.swap(new_cells);
+ }
+ }
+ else
if (flag_enumerate)
{
extra_args(args, argidx, design);
@@ -206,10 +346,12 @@ struct RenamePass : public Pass {
if (!design->selected_active_module.empty())
{
if (design->modules_.count(design->selected_active_module) > 0)
- rename_in_module(design->modules_.at(design->selected_active_module), from_name, to_name);
+ rename_in_module(design->modules_.at(design->selected_active_module), from_name, to_name, flag_output);
}
else
{
+ if (flag_output)
+ log_cmd_error("Mode -output requires that there is an active module selected.\n");
for (auto &mod : design->modules_) {
if (mod.first == from_name || RTLIL::unescape_id(mod.first) == from_name) {
to_name = RTLIL::escape_id(to_name);
diff --git a/passes/cmds/select.cc b/passes/cmds/select.cc
index d97aa2b3..b5e8ef1a 100644
--- a/passes/cmds/select.cc
+++ b/passes/cmds/select.cc
@@ -896,6 +896,29 @@ static void select_stmt(RTLIL::Design *design, std::string arg)
select_filter_active_mod(design, work_stack.back());
}
+static std::string describe_selection_for_assert(RTLIL::Design *design, RTLIL::Selection *sel)
+{
+ std::string desc = "Selection contains:\n";
+ for (auto mod_it : design->modules_)
+ {
+ if (sel->selected_module(mod_it.first)) {
+ for (auto &it : mod_it.second->wires_)
+ if (sel->selected_member(mod_it.first, it.first))
+ desc += stringf("%s/%s\n", id2cstr(mod_it.first), id2cstr(it.first));
+ for (auto &it : mod_it.second->memories)
+ if (sel->selected_member(mod_it.first, it.first))
+ desc += stringf("%s/%s\n", id2cstr(mod_it.first), id2cstr(it.first));
+ for (auto &it : mod_it.second->cells_)
+ if (sel->selected_member(mod_it.first, it.first))
+ desc += stringf("%s/%s\n", id2cstr(mod_it.first), id2cstr(it.first));
+ for (auto &it : mod_it.second->processes)
+ if (sel->selected_member(mod_it.first, it.first))
+ desc += stringf("%s/%s\n", id2cstr(mod_it.first), id2cstr(it.first));
+ }
+ }
+ return desc;
+}
+
PRIVATE_NAMESPACE_END
YOSYS_NAMESPACE_BEGIN
@@ -964,7 +987,7 @@ struct SelectPass : public Pass {
log("list of selected objects.\n");
log("\n");
log("Note that many commands support an optional [selection] argument that can be\n");
- log("used to YS_OVERRIDE the global selection for the command. The syntax of this\n");
+ log("used to override the global selection for the command. The syntax of this\n");
log("optional argument is identical to the syntax of the <selection> argument\n");
log("described here.\n");
log("\n");
@@ -1394,7 +1417,12 @@ struct SelectPass : public Pass {
log_cmd_error("No selection to check.\n");
work_stack.back().optimize(design);
if (!work_stack.back().empty())
- log_error("Assertion failed: selection is not empty:%s\n", sel_str.c_str());
+ {
+ RTLIL::Selection *sel = &work_stack.back();
+ sel->optimize(design);
+ std::string desc = describe_selection_for_assert(design, sel);
+ log_error("Assertion failed: selection is not empty:%s\n%s", sel_str.c_str(), desc.c_str());
+ }
return;
}
@@ -1404,7 +1432,12 @@ struct SelectPass : public Pass {
log_cmd_error("No selection to check.\n");
work_stack.back().optimize(design);
if (work_stack.back().empty())
- log_error("Assertion failed: selection is empty:%s\n", sel_str.c_str());
+ {
+ RTLIL::Selection *sel = &work_stack.back();
+ sel->optimize(design);
+ std::string desc = describe_selection_for_assert(design, sel);
+ log_error("Assertion failed: selection is empty:%s\n%s", sel_str.c_str(), desc.c_str());
+ }
return;
}
@@ -1431,14 +1464,23 @@ struct SelectPass : public Pass {
total_count++;
}
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());
+ {
+ std::string desc = describe_selection_for_assert(design, sel);
+ log_error("Assertion failed: selection contains %d elements instead of the asserted %d:%s\n%s",
+ total_count, assert_count, sel_str.c_str(), desc.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());
+ {
+ std::string desc = describe_selection_for_assert(design, sel);
+ log_error("Assertion failed: selection contains %d elements, more than the maximum number %d:%s\n%s",
+ total_count, assert_max, sel_str.c_str(), desc.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());
+ {
+ std::string desc = describe_selection_for_assert(design, sel);
+ log_error("Assertion failed: selection contains %d elements, less than the minimum number %d:%s\n%s",
+ total_count, assert_min, sel_str.c_str(), desc.c_str());
+ }
return;
}
diff --git a/passes/cmds/setundef.cc b/passes/cmds/setundef.cc
index a1dfa9b5..f6949c82 100644
--- a/passes/cmds/setundef.cc
+++ b/passes/cmds/setundef.cc
@@ -137,12 +137,15 @@ struct SetundefPass : public Pass {
log(" replace with $anyconst drivers (for formal)\n");
log("\n");
log(" -random <seed>\n");
- log(" replace with random bits using the specified integer als seed\n");
+ log(" replace with random bits using the specified integer as 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");
+ log(" -params\n");
+ log(" replace undef in cell parameters\n");
+ log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
@@ -150,6 +153,7 @@ struct SetundefPass : public Pass {
bool undriven_mode = false;
bool expose_mode = false;
bool init_mode = false;
+ bool params_mode = false;
SetundefWorker worker;
log_header(design, "Executing SETUNDEF pass (replace undef values with defined constants).\n");
@@ -199,6 +203,10 @@ struct SetundefPass : public Pass {
init_mode = true;
continue;
}
+ if (args[argidx] == "-params") {
+ params_mode = true;
+ continue;
+ }
if (args[argidx] == "-random" && !got_value && argidx+1 < args.size()) {
got_value = true;
worker.next_bit_mode = MODE_RANDOM;
@@ -228,6 +236,18 @@ struct SetundefPass : public Pass {
for (auto module : design->selected_modules())
{
+ if (params_mode)
+ {
+ for (auto *cell : module->selected_cells()) {
+ for (auto &parameter : cell->parameters) {
+ for (auto &bit : parameter.second.bits) {
+ if (bit > RTLIL::State::S1)
+ bit = worker.next_bit();
+ }
+ }
+ }
+ }
+
if (undriven_mode)
{
if (!module->processes.empty())
diff --git a/passes/cmds/show.cc b/passes/cmds/show.cc
index a4887324..58acd302 100644
--- a/passes/cmds/show.cc
+++ b/passes/cmds/show.cc
@@ -623,7 +623,7 @@ struct ShowPass : public Pass {
log(" assigned to each unique value of this attribute.\n");
log("\n");
log(" -width\n");
- log(" annotate busses with a label indicating the width of the bus.\n");
+ log(" annotate buses with a label indicating the width of the bus.\n");
log("\n");
log(" -signed\n");
log(" mark ports (A, B) that are declared as signed (using the [AB]_SIGNED\n");
diff --git a/passes/cmds/tee.cc b/passes/cmds/tee.cc
index ff80f385..ee96ace8 100644
--- a/passes/cmds/tee.cc
+++ b/passes/cmds/tee.cc
@@ -37,7 +37,7 @@ struct TeePass : public Pass {
log("specified logfile(s).\n");
log("\n");
log(" -q\n");
- log(" Do not print output to the normal destination (console and/or log file)\n");
+ log(" Do not print output to the normal destination (console and/or log file).\n");
log("\n");
log(" -o logfile\n");
log(" Write output to this file, truncate if exists.\n");
@@ -46,7 +46,7 @@ struct TeePass : public Pass {
log(" Write output to this file, append if exists.\n");
log("\n");
log(" +INT, -INT\n");
- log(" Add/subract INT from the -v setting for this command.\n");
+ log(" Add/subtract INT from the -v setting for this command.\n");
log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
diff --git a/passes/equiv/Makefile.inc b/passes/equiv/Makefile.inc
index dd7b3be0..27ea54b2 100644
--- a/passes/equiv/Makefile.inc
+++ b/passes/equiv/Makefile.inc
@@ -9,4 +9,4 @@ OBJS += passes/equiv/equiv_induct.o
OBJS += passes/equiv/equiv_struct.o
OBJS += passes/equiv/equiv_purge.o
OBJS += passes/equiv/equiv_mark.o
-
+OBJS += passes/equiv/equiv_opt.o
diff --git a/passes/equiv/equiv_make.cc b/passes/equiv/equiv_make.cc
index b1f88d55..dbd8682e 100644
--- a/passes/equiv/equiv_make.cc
+++ b/passes/equiv/equiv_make.cc
@@ -40,6 +40,16 @@ struct EquivMakeWorker
pool<SigBit> undriven_bits;
SigMap assign_map;
+ dict<SigBit, pool<Cell*>> bit2driven; // map: bit <--> and its driven cells
+
+ CellTypes comb_ct;
+
+ EquivMakeWorker()
+ {
+ comb_ct.setup_internals();
+ comb_ct.setup_stdcells();
+ }
+
void read_blacklists()
{
for (auto fn : blacklists)
@@ -278,16 +288,31 @@ struct EquivMakeWorker
}
}
+ init_bit2driven();
+
+ pool<Cell*> visited_cells;
for (auto c : cells_list)
for (auto &conn : c->connections())
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) {
- log("Changing input %s of cell %s (%s): %s -> %s\n",
- log_id(conn.first), log_id(c), log_id(c->type),
- log_signal(old_sig), log_signal(new_sig));
- c->setPort(conn.first, new_sig);
+
+ if(old_sig != new_sig) {
+ SigSpec tmp_sig = old_sig;
+ for (int i = 0; i < GetSize(old_sig); i++) {
+ SigBit old_bit = old_sig[i], new_bit = new_sig[i];
+
+ visited_cells.clear();
+ if (check_signal_in_fanout(visited_cells, old_bit, new_bit))
+ continue;
+
+ log("Changing input %s of cell %s (%s): %s -> %s\n",
+ log_id(conn.first), log_id(c), log_id(c->type),
+ log_signal(old_bit), log_signal(new_bit));
+
+ tmp_sig[i] = new_bit;
+ }
+ c->setPort(conn.first, tmp_sig);
}
}
@@ -378,6 +403,57 @@ struct EquivMakeWorker
}
}
+ void init_bit2driven()
+ {
+ for (auto cell : equiv_mod->cells()) {
+ if (!ct.cell_known(cell->type) && !cell->type.in("$dff", "$_DFF_P_", "$_DFF_N_", "$ff", "$_FF_"))
+ continue;
+ for (auto &conn : cell->connections())
+ {
+ if (yosys_celltypes.cell_input(cell->type, conn.first))
+ for (auto bit : assign_map(conn.second))
+ {
+ bit2driven[bit].insert(cell);
+ }
+ }
+ }
+ }
+
+ bool check_signal_in_fanout(pool<Cell*> & visited_cells, SigBit source_bit, SigBit target_bit)
+ {
+ if (source_bit == target_bit)
+ return true;
+
+ if (bit2driven.count(source_bit) == 0)
+ return false;
+
+ auto driven_cells = bit2driven.at(source_bit);
+ for (auto driven_cell: driven_cells)
+ {
+ bool is_comb = comb_ct.cell_known(driven_cell->type);
+ if (!is_comb)
+ continue;
+
+ if (visited_cells.count(driven_cell) > 0)
+ continue;
+ visited_cells.insert(driven_cell);
+
+ for (auto &conn: driven_cell->connections())
+ {
+ if (yosys_celltypes.cell_input(driven_cell->type, conn.first))
+ continue;
+
+ for (auto bit: conn.second) {
+ bool is_in_fanout = check_signal_in_fanout(visited_cells, bit, target_bit);
+ if (is_in_fanout == true)
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
void run()
{
copy_to_equiv();
diff --git a/passes/equiv/equiv_opt.cc b/passes/equiv/equiv_opt.cc
new file mode 100644
index 00000000..86550a69
--- /dev/null
+++ b/passes/equiv/equiv_opt.cc
@@ -0,0 +1,157 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2018 whitequark <whitequark@whitequark.org>
+ *
+ * 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"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct EquivOptPass:public ScriptPass
+{
+ EquivOptPass() : ScriptPass("equiv_opt", "prove equivalence for optimized circuit") { }
+
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" equiv_opt [options] [command]\n");
+ log("\n");
+ log("This command checks circuit equivalence before and after an optimization pass.\n");
+ log("\n");
+ log(" -run <from_label>:<to_label>\n");
+ log(" only run the commands between the labels (see below). an empty\n");
+ log(" from label is synonymous to the start of the command list, and empty to\n");
+ log(" label is synonymous to the end of the command list.\n");
+ log("\n");
+ log(" -map <filename>\n");
+ log(" expand the modules in this file before proving equivalence. this is\n");
+ log(" useful for handling architecture-specific primitives.\n");
+ log("\n");
+ log(" -assert\n");
+ log(" produce an error if the circuits are not equivalent\n");
+ log("\n");
+ log("The following commands are executed by this verification command:\n");
+ help_script();
+ log("\n");
+ }
+
+ std::string command, techmap_opts;
+ bool assert;
+
+ void clear_flags() YS_OVERRIDE
+ {
+ command = "";
+ techmap_opts = "";
+ assert = false;
+ }
+
+ void execute(std::vector < std::string > args, RTLIL::Design * design) YS_OVERRIDE
+ {
+ string run_from, run_to;
+ clear_flags();
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ if (args[argidx] == "-run" && argidx + 1 < args.size()) {
+ size_t pos = args[argidx + 1].find(':');
+ if (pos == std::string::npos)
+ break;
+ run_from = args[++argidx].substr(0, pos);
+ run_to = args[argidx].substr(pos + 1);
+ continue;
+ }
+ if (args[argidx] == "-map" && argidx + 1 < args.size()) {
+ techmap_opts += " -map " + args[++argidx];
+ continue;
+ }
+ if (args[argidx] == "-assert") {
+ assert = true;
+ continue;
+ }
+ break;
+ }
+
+ for (; argidx < args.size(); argidx++) {
+ if (command.empty()) {
+ if (args[argidx].substr(0, 1) == "-")
+ cmd_error(args, argidx, "Unknown option.");
+ } else {
+ command += " ";
+ }
+ command += args[argidx];
+ }
+
+ if (command.empty())
+ log_cmd_error("No optimization pass specified!\n");
+
+ if (!design->full_selection())
+ log_cmd_error("This command only operates on fully selected designs!\n");
+
+ log_header(design, "Executing EQUIV_OPT pass.\n");
+ log_push();
+
+ run_script(design, run_from, run_to);
+
+ log_pop();
+ }
+
+ void script() YS_OVERRIDE
+ {
+ if (check_label("run_pass")) {
+ run("hierarchy -auto-top");
+ run("design -save preopt");
+ if (help_mode)
+ run("[command]");
+ else
+ run(command);
+ run("design -stash postopt");
+ }
+
+ if (check_label("prepare")) {
+ run("design -copy-from preopt -as gold A:top");
+ run("design -copy-from postopt -as gate A:top");
+ }
+
+ if ((!techmap_opts.empty() || help_mode) && check_label("techmap", "(only with -map)")) {
+ string opts;
+ if (help_mode)
+ opts = " -map <filename> ...";
+ else
+ opts = techmap_opts;
+ run("techmap -D EQUIV -autoproc" + opts);
+ }
+
+ if (check_label("prove")) {
+ run("equiv_make gold gate equiv");
+ run("equiv_induct equiv");
+ if (help_mode)
+ run("equiv_status [-assert] equiv");
+ else if (assert)
+ run("equiv_status -assert equiv");
+ else
+ run("equiv_status equiv");
+ }
+
+ if (check_label("restore")) {
+ run("design -load preopt");
+ }
+ }
+} EquivOptPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/fsm/fsm_detect.cc b/passes/fsm/fsm_detect.cc
index fc504e98..5ae991b2 100644
--- a/passes/fsm/fsm_detect.cc
+++ b/passes/fsm/fsm_detect.cc
@@ -196,13 +196,13 @@ static void detect_fsm(RTLIL::Wire *wire)
vector<string> warnings;
if (is_module_port)
- warnings.push_back("Forcing fsm recoding on module port might result in larger circuit.\n");
+ warnings.push_back("Forcing FSM recoding on module port might result in larger circuit.\n");
if (!looks_like_good_state_reg)
- warnings.push_back("Users of state reg look like fsm recoding might result in larger circuit.\n");
+ warnings.push_back("Users of state reg look like FSM recoding might result in larger circuit.\n");
if (has_init_attr)
- warnings.push_back("Init value on fsm state registers are ignored. Possible simulation-synthesis mismatch!");
+ warnings.push_back("Initialization value on FSM state register is ignored. Possible simulation-synthesis mismatch!\n");
if (!looks_like_state_reg)
warnings.push_back("Doesn't look like a proper FSM. Possible simulation-synthesis mismatch!\n");
@@ -236,7 +236,7 @@ static void detect_fsm(RTLIL::Wire *wire)
log(" Users of register don't seem to benefit from recoding.\n");
if (has_init_attr)
- log(" Register has an initialization value.");
+ log(" Register has an initialization value.\n");
if (is_self_resetting)
log(" Circuit seems to be self-resetting.\n");
diff --git a/passes/fsm/fsm_extract.cc b/passes/fsm/fsm_extract.cc
index 67551f67..6095eaf3 100644
--- a/passes/fsm/fsm_extract.cc
+++ b/passes/fsm/fsm_extract.cc
@@ -178,7 +178,7 @@ undef_bit_in_next_state:
log_state_in = fsm_data.state_table.at(state_in);
if (states.count(ce.values_map(ce.assign_map(dff_in)).as_const()) == 0) {
- log(" transition: %10s %s -> INVALID_STATE(%s) %s <ignored invalid transistion!>%s\n",
+ log(" transition: %10s %s -> INVALID_STATE(%s) %s <ignored invalid transition!>%s\n",
log_signal(log_state_in), log_signal(tr.ctrl_in),
log_signal(ce.values_map(ce.assign_map(dff_in))), log_signal(tr.ctrl_out),
undef_bit_in_next_state_mode ? " SHORTENED" : "");
@@ -194,7 +194,7 @@ undef_bit_in_next_state:
log_signal(log_state_in), log_signal(tr.ctrl_in),
log_signal(fsm_data.state_table[tr.state_out]), log_signal(tr.ctrl_out));
} else {
- log(" transition: %10s %s -> %10s %s <ignored undef transistion!>\n",
+ log(" transition: %10s %s -> %10s %s <ignored undef transition!>\n",
log_signal(log_state_in), log_signal(tr.ctrl_in),
log_signal(fsm_data.state_table[tr.state_out]), log_signal(tr.ctrl_out));
}
diff --git a/passes/fsm/fsm_opt.cc b/passes/fsm/fsm_opt.cc
index 3a6ac274..048daee5 100644
--- a/passes/fsm/fsm_opt.cc
+++ b/passes/fsm/fsm_opt.cc
@@ -72,7 +72,8 @@ struct FsmOpt
new_transition_table.swap(fsm_data.transition_table);
new_state_table.swap(fsm_data.state_table);
- fsm_data.reset_state = old_to_new_state.at(fsm_data.reset_state);
+ if (fsm_data.reset_state != -1)
+ fsm_data.reset_state = old_to_new_state.at(fsm_data.reset_state);
}
}
diff --git a/passes/hierarchy/hierarchy.cc b/passes/hierarchy/hierarchy.cc
index 5df69848..88c339e8 100644
--- a/passes/hierarchy/hierarchy.cc
+++ b/passes/hierarchy/hierarchy.cc
@@ -2,6 +2,7 @@
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2018 Ruben Undheim <ruben.undheim@gmail.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -139,25 +140,73 @@ void generate(RTLIL::Design *design, const std::vector<std::string> &celltypes,
}
}
+// Return the "basic" type for an array item.
+std::string basic_cell_type(const std::string celltype, int pos[3] = nullptr) {
+ std::string basicType = celltype;
+ if (celltype.substr(0, 7) == "$array:") {
+ int pos_idx = celltype.find_first_of(':');
+ int pos_num = celltype.find_first_of(':', pos_idx + 1);
+ int pos_type = celltype.find_first_of(':', pos_num + 1);
+ basicType = celltype.substr(pos_type + 1);
+ if (pos != nullptr) {
+ pos[0] = pos_idx;
+ pos[1] = pos_num;
+ pos[2] = pos_type;
+ }
+ }
+ return basicType;
+}
+
bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check, bool flag_simcheck, std::vector<std::string> &libdirs)
{
bool did_something = false;
std::map<RTLIL::Cell*, std::pair<int, int>> array_cells;
std::string filename;
+ bool has_interface_ports = false;
+
+ // If any of the ports are actually interface ports, we will always need to
+ // reprocess the module:
+ if(!module->get_bool_attribute("\\interfaces_replaced_in_module")) {
+ for (auto &wire : module->wires_) {
+ if ((wire.second->port_input || wire.second->port_output) && wire.second->get_bool_attribute("\\is_interface"))
+ has_interface_ports = true;
+ }
+ }
+
+ // Always keep track of all derived interfaces available in the current module in 'interfaces_in_module':
+ dict<RTLIL::IdString, RTLIL::Module*> interfaces_in_module;
+ for (auto &cell_it : module->cells_)
+ {
+ RTLIL::Cell *cell = cell_it.second;
+ if(cell->get_bool_attribute("\\is_interface")) {
+ RTLIL::Module *intf_module = design->modules_[cell->type];
+ interfaces_in_module[cell->name] = intf_module;
+ }
+ }
+
for (auto &cell_it : module->cells_)
{
RTLIL::Cell *cell = cell_it.second;
+ bool has_interfaces_not_found = false;
+
+ std::vector<RTLIL::IdString> connections_to_remove;
+ std::vector<RTLIL::IdString> connections_to_add_name;
+ std::vector<RTLIL::SigSpec> connections_to_add_signal;
if (cell->type.substr(0, 7) == "$array:") {
- int pos_idx = cell->type.str().find_first_of(':');
- int pos_num = cell->type.str().find_first_of(':', pos_idx + 1);
- int pos_type = cell->type.str().find_first_of(':', pos_num + 1);
+ int pos[3];
+ basic_cell_type(cell->type.str(), pos);
+ int pos_idx = pos[0];
+ int pos_num = pos[1];
+ int pos_type = pos[2];
int idx = atoi(cell->type.str().substr(pos_idx + 1, pos_num).c_str());
int num = atoi(cell->type.str().substr(pos_num + 1, pos_type).c_str());
array_cells[cell] = std::pair<int, int>(idx, num);
cell->type = cell->type.str().substr(pos_type + 1);
}
+ dict<RTLIL::IdString, RTLIL::Module*> interfaces_to_add_to_submodule;
+ dict<RTLIL::IdString, RTLIL::IdString> modports_used_in_submodule;
if (design->modules_.count(cell->type) == 0)
{
@@ -200,11 +249,85 @@ bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check
if (design->modules_.count(cell->type) == 0)
log_error("File `%s' from libdir does not declare module `%s'.\n", filename.c_str(), cell->type.c_str());
did_something = true;
- } else
+ } else {
+
+ RTLIL::Module *mod = design->module(cell->type);
+
+ // Go over all connections and see if any of them are SV interfaces. If they are, then add the replacements to
+ // some lists, so that the ports for sub-modules can be replaced further down:
+ for (auto &conn : cell->connections()) {
+ if(mod->wires_.count(conn.first) != 0 && mod->wire(conn.first)->get_bool_attribute("\\is_interface")) { // Check if the connection is present as an interface in the sub-module's port list
+ //const pool<string> &interface_type_pool = mod->wire(conn.first)->get_strpool_attribute("\\interface_type");
+ //for (auto &d : interface_type_pool) { // TODO: Compare interface type to type in parent module (not crucially important, but good for robustness)
+ //}
+
+ // Find if the sub-module has set a modport for the current interface connection:
+ const pool<string> &interface_modport_pool = mod->wire(conn.first)->get_strpool_attribute("\\interface_modport");
+ std::string interface_modport = "";
+ for (auto &d : interface_modport_pool) {
+ interface_modport = "\\" + d;
+ }
+ if(conn.second.bits().size() == 1 && conn.second.bits()[0].wire->get_bool_attribute("\\is_interface")) { // Check if the connected wire is a potential interface in the parent module
+ std::string interface_name_str = conn.second.bits()[0].wire->name.str();
+ interface_name_str.replace(0,23,""); // Strip the prefix '$dummywireforinterface' from the dummy wire to get the name
+ interface_name_str = "\\" + interface_name_str;
+ RTLIL::IdString interface_name = interface_name_str;
+ bool not_found_interface = false;
+ if(module->get_bool_attribute("\\interfaces_replaced_in_module")) { // If 'interfaces' in the cell have not be been handled yet, there is no need to derive the sub-module either
+ // Check if the interface instance is present in module:
+ // Interface instances may either have the plain name or the name appended with '_inst_from_top_dummy'.
+ // Check for both of them here
+ int nexactmatch = interfaces_in_module.count(interface_name) > 0;
+ std::string interface_name_str2 = interface_name_str + "_inst_from_top_dummy";
+ RTLIL::IdString interface_name2 = interface_name_str2;
+ int nmatch2 = interfaces_in_module.count(interface_name2) > 0;
+ if (nexactmatch > 0 || nmatch2 > 0) {
+ if (nexactmatch != 0) // Choose the one with the plain name if it exists
+ interface_name2 = interface_name;
+ RTLIL::Module *mod_replace_ports = interfaces_in_module.at(interface_name2);
+ for (auto &mod_wire : mod_replace_ports->wires_) { // Go over all wires in interface, and add replacements to lists.
+ std::string signal_name1 = conn.first.str() + "." + log_id(mod_wire.first);
+ std::string signal_name2 = interface_name.str() + "." + log_id(mod_wire.first);
+ connections_to_add_name.push_back(RTLIL::IdString(signal_name1));
+ if(module->wires_.count(signal_name2) == 0) {
+ log_error("Could not find signal '%s' in '%s'\n", signal_name2.c_str(), log_id(module->name));
+ }
+ else {
+ RTLIL::Wire *wire_in_parent = module->wire(signal_name2);
+ connections_to_add_signal.push_back(wire_in_parent);
+ }
+ }
+ connections_to_remove.push_back(conn.first);
+ interfaces_to_add_to_submodule[conn.first] = interfaces_in_module.at(interface_name2);
+
+ // Add modports to a dict which will be passed to AstModule::derive
+ if (interface_modport != "") {
+ modports_used_in_submodule[conn.first] = interface_modport;
+ }
+ }
+ else not_found_interface = true;
+ }
+ else not_found_interface = true;
+ // If the interface instance has not already been derived in the module, we cannot complete at this stage. Set "has_interfaces_not_found"
+ // which will delay the expansion of this cell:
+ if (not_found_interface) {
+ // If we have already gone over all cells in this module, and the interface has still not been found - flag it as an error:
+ if(!(module->get_bool_attribute("\\cells_not_processed"))) {
+ log_warning("Could not find interface instance for `%s' in `%s'\n", log_id(interface_name), log_id(module));
+ }
+ else {
+ // Only set has_interfaces_not_found if it would be possible to find them, since otherwiser we will end up in an infinite loop:
+ has_interfaces_not_found = true;
+ }
+ }
+ }
+ }
+ }
+ //
+
if (flag_check || flag_simcheck)
{
- RTLIL::Module *mod = design->module(cell->type);
- for (auto &conn : cell->connections())
+ for (auto &conn : cell->connections()) {
if (conn.first[0] == '$' && '0' <= conn.first[1] && conn.first[1] <= '9') {
int id = atoi(conn.first.c_str()+1);
if (id <= 0 || id > GetSize(mod->ports))
@@ -213,11 +336,15 @@ bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check
} else if (mod->wire(conn.first) == nullptr || mod->wire(conn.first)->port_id == 0)
log_error("Module `%s' referenced in module `%s' in cell `%s' does not have a port named '%s'.\n",
log_id(cell->type), log_id(module), log_id(cell), log_id(conn.first));
+ }
for (auto &param : cell->parameters)
if (mod->avail_parameters.count(param.first) == 0 && param.first[0] != '$' && strchr(param.first.c_str(), '.') == NULL)
log_error("Module `%s' referenced in module `%s' in cell `%s' does not have a parameter named '%s'.\n",
log_id(cell->type), log_id(module), log_id(cell), log_id(param.first));
+
+ }
}
+ RTLIL::Module *mod = design->modules_[cell->type];
if (design->modules_.at(cell->type)->get_bool_attribute("\\blackbox")) {
if (flag_simcheck)
@@ -226,15 +353,62 @@ bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check
continue;
}
- if (cell->parameters.size() == 0)
+ // If interface instances not yet found, skip cell for now, and say we did something, so that we will return back here:
+ if(has_interfaces_not_found) {
+ did_something = true; // waiting for interfaces to be handled
continue;
+ }
- RTLIL::Module *mod = design->modules_[cell->type];
- cell->type = mod->derive(design, cell->parameters);
+ // Do the actual replacements of the SV interface port connection with the individual signal connections:
+ for(unsigned int i=0;i<connections_to_add_name.size();i++) {
+ cell->connections_[connections_to_add_name[i]] = connections_to_add_signal[i];
+ }
+ // Remove the connection for the interface itself:
+ for(unsigned int i=0;i<connections_to_remove.size();i++) {
+ cell->connections_.erase(connections_to_remove[i]);
+ }
+
+ // If there are no overridden parameters AND not interfaces, then we can use the existing module instance as the type
+ // for the cell:
+ if (cell->parameters.size() == 0 && (interfaces_to_add_to_submodule.size() == 0 || !(cell->get_bool_attribute("\\module_not_derived")))) {
+ // If the cell being processed is an the interface instance itself, go down to "handle_interface_instance:",
+ // so that the signals of the interface are added to the parent module.
+ if (mod->get_bool_attribute("\\is_interface")) {
+ goto handle_interface_instance;
+ }
+ continue;
+ }
+
+ cell->type = mod->derive(design, cell->parameters, interfaces_to_add_to_submodule, modports_used_in_submodule);
cell->parameters.clear();
did_something = true;
+
+ handle_interface_instance:
+
+ // We add all the signals of the interface explicitly to the parent module. This is always needed when we encounter
+ // an interface instance:
+ if (mod->get_bool_attribute("\\is_interface") && cell->get_bool_attribute("\\module_not_derived")) {
+ cell->set_bool_attribute("\\is_interface");
+ RTLIL::Module *derived_module = design->modules_[cell->type];
+ interfaces_in_module[cell->name] = derived_module;
+ did_something = true;
+ }
+ // We clear 'module_not_derived' such that we will not rederive the cell again (needed when there are interfaces connected to the cell)
+ cell->attributes.erase("\\module_not_derived");
+ }
+ // Clear the attribute 'cells_not_processed' such that it can be known that we
+ // have been through all cells at least once, and that we can know whether
+ // to flag an error because of interface instances not found:
+ module->attributes.erase("\\cells_not_processed");
+
+
+ // If any interface instances or interface ports were found in the module, we need to rederive it completely:
+ if ((interfaces_in_module.size() > 0 || has_interface_ports) && !module->get_bool_attribute("\\interfaces_replaced_in_module")) {
+ module->reprocess_module(design, interfaces_in_module);
+ return did_something;
}
+
for (auto &it : array_cells)
{
RTLIL::Cell *cell = it.first;
@@ -284,10 +458,7 @@ void hierarchy_worker(RTLIL::Design *design, std::set<RTLIL::Module*, IdString::
for (auto cell : mod->cells()) {
std::string celltype = cell->type.str();
if (celltype.substr(0, 7) == "$array:") {
- int pos_idx = celltype.find_first_of(':');
- int pos_num = celltype.find_first_of(':', pos_idx + 1);
- int pos_type = celltype.find_first_of(':', pos_num + 1);
- celltype = celltype.substr(pos_type + 1);
+ celltype = basic_cell_type(celltype);
}
if (design->module(celltype))
hierarchy_worker(design, used, design->module(celltype), indent+4);
@@ -303,6 +474,20 @@ void hierarchy_clean(RTLIL::Design *design, RTLIL::Module *top, bool purge_lib)
for (auto &it : design->modules_)
if (used.count(it.second) == 0)
del_modules.push_back(it.second);
+ else {
+ // Now all interface ports must have been exploded, and it is hence
+ // safe to delete all of the remaining dummy interface ports:
+ pool<RTLIL::Wire*> del_wires;
+ for(auto &wire : it.second->wires_) {
+ if ((wire.second->port_input || wire.second->port_output) && wire.second->get_bool_attribute("\\is_interface")) {
+ del_wires.insert(wire.second);
+ }
+ }
+ if (del_wires.size() > 0) {
+ it.second->remove(del_wires);
+ it.second->fixup_ports();
+ }
+ }
int del_counter = 0;
for (auto mod : del_modules) {
@@ -333,14 +518,38 @@ int find_top_mod_score(Design *design, Module *module, dict<Module*, int> &db)
if (db.count(module) == 0) {
int score = 0;
db[module] = 0;
- for (auto cell : module->cells())
- if (design->module(cell->type))
- score = max(score, find_top_mod_score(design, design->module(cell->type), db) + 1);
+ for (auto cell : module->cells()) {
+ std::string celltype = cell->type.str();
+ // Is this an array instance
+ if (celltype.substr(0, 7) == "$array:") {
+ celltype = basic_cell_type(celltype);
+ }
+ // Is this cell a module instance?
+ auto instModule = design->module(celltype);
+ // If there is no instance for this, issue a warning.
+ if (instModule != nullptr) {
+ score = max(score, find_top_mod_score(design, instModule, db) + 1);
+ }
+ }
db[module] = score;
}
return db.at(module);
}
+RTLIL::Module *check_if_top_has_changed(Design *design, Module *top_mod)
+{
+ if(top_mod != NULL && top_mod->get_bool_attribute("\\initial_top"))
+ return top_mod;
+ else {
+ for (auto mod : design->modules()) {
+ if (mod->get_bool_attribute("\\top")) {
+ return mod;
+ }
+ }
+ }
+ return NULL;
+}
+
struct HierarchyPass : public Pass {
HierarchyPass() : Pass("hierarchy", "check, expand and clean up design hierarchy") { }
void help() YS_OVERRIDE
@@ -360,7 +569,7 @@ struct HierarchyPass : public Pass {
log(" an unknown module is used as cell type.\n");
log("\n");
log(" -simcheck\n");
- log(" like -check, but also thow an error if blackbox modules are\n");
+ log(" like -check, but also throw an error if blackbox modules are\n");
log(" instantiated, and throw an error if the design has no top module\n");
log("\n");
log(" -purge_lib\n");
@@ -568,6 +777,14 @@ struct HierarchyPass : public Pass {
if (flag_simcheck && top_mod == nullptr)
log_error("Design has no top module.\n");
+ if (top_mod != NULL) {
+ for (auto &mod_it : design->modules_)
+ if (mod_it.second == top_mod)
+ mod_it.second->attributes["\\initial_top"] = RTLIL::Const(1);
+ else
+ mod_it.second->attributes.erase("\\initial_top");
+ }
+
bool did_something = true;
while (did_something)
{
@@ -586,19 +803,43 @@ struct HierarchyPass : public Pass {
if (expand_module(design, module, flag_check, flag_simcheck, libdirs))
did_something = true;
}
+
+
+ // The top module might have changed if interface instances have been detected in it:
+ RTLIL::Module *tmp_top_mod = check_if_top_has_changed(design, top_mod);
+ if (tmp_top_mod != NULL) {
+ if (tmp_top_mod != top_mod){
+ top_mod = tmp_top_mod;
+ did_something = true;
+ }
+ }
+
+ // Delete modules marked as 'to_delete':
+ std::vector<RTLIL::Module *> modules_to_delete;
+ for(auto &mod_it : design->modules_) {
+ if (mod_it.second->get_bool_attribute("\\to_delete")) {
+ modules_to_delete.push_back(mod_it.second);
+ }
+ }
+ for(size_t i=0; i<modules_to_delete.size(); i++) {
+ design->remove(modules_to_delete[i]);
+ }
}
+
if (top_mod != NULL) {
log_header(design, "Analyzing design hierarchy..\n");
hierarchy_clean(design, top_mod, purge_lib);
}
if (top_mod != NULL) {
- for (auto &mod_it : design->modules_)
+ for (auto &mod_it : design->modules_) {
if (mod_it.second == top_mod)
mod_it.second->attributes["\\top"] = RTLIL::Const(1);
else
mod_it.second->attributes.erase("\\top");
+ mod_it.second->attributes.erase("\\initial_top");
+ }
}
if (!nokeep_asserts) {
@@ -669,7 +910,7 @@ struct HierarchyPass : public Pass {
if (m == nullptr)
continue;
- if (m->get_bool_attribute("\\blackbox") && !cell->parameters.empty()) {
+ if (m->get_bool_attribute("\\blackbox") && !cell->parameters.empty() && m->get_bool_attribute("\\dynports")) {
IdString new_m_name = m->derive(design, cell->parameters, true);
if (new_m_name.empty())
continue;
diff --git a/passes/hierarchy/uniquify.cc b/passes/hierarchy/uniquify.cc
index c88ecd82..e6154e94 100644
--- a/passes/hierarchy/uniquify.cc
+++ b/passes/hierarchy/uniquify.cc
@@ -87,6 +87,8 @@ struct UniquifyPass : public Pass {
smod->name = newname;
cell->type = newname;
smod->set_bool_attribute("\\unique");
+ if (smod->attributes.count("\\hdlname") == 0)
+ smod->attributes["\\hdlname"] = string(log_id(tmod->name));
design->add(smod);
did_something = true;
diff --git a/passes/memory/memory_bram.cc b/passes/memory/memory_bram.cc
index e8552bbc..85ed1c05 100644
--- a/passes/memory/memory_bram.cc
+++ b/passes/memory/memory_bram.cc
@@ -472,8 +472,12 @@ bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram,
std::vector<SigSpec> new_wr_en(GetSize(old_wr_en));
std::vector<SigSpec> new_wr_data(GetSize(old_wr_data));
std::vector<SigSpec> new_rd_data(GetSize(old_rd_data));
+ std::vector<std::vector<State>> new_initdata;
std::vector<int> shuffle_map;
+ if (cell_init)
+ new_initdata.resize(mem_size);
+
for (auto &it : en_order)
{
auto &bits = bits_wr_en.at(it);
@@ -489,6 +493,10 @@ bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram,
}
for (int j = 0; j < rd_ports; j++)
new_rd_data[j].append(old_rd_data[j][bits[i]]);
+ if (cell_init) {
+ for (int j = 0; j < mem_size; j++)
+ new_initdata[j].push_back(initdata[j][bits[i]]);
+ }
shuffle_map.push_back(bits[i]);
}
@@ -499,6 +507,10 @@ bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram,
}
for (int j = 0; j < rd_ports; j++)
new_rd_data[j].append(State::Sx);
+ if (cell_init) {
+ for (int j = 0; j < mem_size; j++)
+ new_initdata[j].push_back(State::Sx);
+ }
shuffle_map.push_back(-1);
}
}
@@ -522,10 +534,15 @@ bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram,
for (int i = 0; i < rd_ports; i++)
rd_data.replace(i*mem_width, new_rd_data[i]);
+
+ if (cell_init) {
+ for (int i = 0; i < mem_size; i++)
+ initdata[i] = Const(new_initdata[i]);
+ }
}
// assign write ports
-
+ pair<SigBit, bool> wr_clkdom;
for (int cell_port_i = 0, bram_port_i = 0; cell_port_i < wr_ports; cell_port_i++)
{
bool clken = wr_clken[cell_port_i] == State::S1;
@@ -535,7 +552,7 @@ bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram,
pair<SigBit, bool> clkdom(clksig, clkpol);
if (!clken)
clkdom = pair<SigBit, bool>(State::S1, false);
-
+ wr_clkdom = clkdom;
log(" Write port #%d is in clock domain %s%s.\n",
cell_port_i, clkdom.second ? "" : "!",
clken ? log_signal(clkdom.first) : "~async~");
@@ -623,6 +640,8 @@ grow_read_ports:;
pi.sig_addr = SigSpec();
pi.sig_data = SigSpec();
pi.sig_en = SigSpec();
+ pi.make_outreg = false;
+ pi.make_transp = false;
}
new_portinfos.push_back(pi);
if (pi.dupidx == dup_count-1) {
@@ -700,7 +719,13 @@ grow_read_ports:;
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;
+ if (pi.clocks != 0) {
+ if (wr_ports == 1 && wr_clkdom != clkdom) {
+ log(" Bram port %c%d.%d cannot have soft transparency logic added as read and write clock domains differ.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
+ goto skip_bram_rport;
+ }
+ enable_make_transp = true;
+ }
} else {
log(" Bram port %c%d.%d has incompatible read transparency.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
goto skip_bram_rport;
@@ -895,17 +920,18 @@ grow_read_ports:;
} else {
SigSpec bram_dout = module->addWire(NEW_ID, bram.dbits);
c->setPort(stringf("\\%sDATA", pf), bram_dout);
-
- if (pi.make_outreg) {
+ if (pi.make_outreg && pi.make_transp) {
+ log(" Moving output register to address for transparent port %c%d.%d.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
+ SigSpec sig_addr_q = module->addWire(NEW_ID, bram.abits);
+ module->addDff(NEW_ID, pi.sig_clock, sig_addr, sig_addr_q, pi.effective_clkpol);
+ c->setPort(stringf("\\%sADDR", pf), sig_addr_q);
+ } else 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)
- {
+ } else if (pi.make_transp) {
log(" Adding extra logic for transparent port %c%d.%d.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
SigSpec transp_en_d = module->Mux(NEW_ID, SigSpec(0, make_transp_enbits),
diff --git a/passes/memory/memory_collect.cc b/passes/memory/memory_collect.cc
index 70d98713..369fcc84 100644
--- a/passes/memory/memory_collect.cc
+++ b/passes/memory/memory_collect.cc
@@ -184,9 +184,6 @@ Cell *handle_memory(Module *module, RTLIL::Memory *memory)
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);
diff --git a/passes/memory/memory_dff.cc b/passes/memory/memory_dff.cc
index 32df1917..220d2929 100644
--- a/passes/memory/memory_dff.cc
+++ b/passes/memory/memory_dff.cc
@@ -41,7 +41,7 @@ struct MemoryDffWorker
if (wire->attributes.count("\\init") == 0)
continue;
SigSpec sig = sigmap(wire);
- Const initval = wire->attributes.count("\\init");
+ Const initval = wire->attributes.at("\\init");
for (int i = 0; i < GetSize(sig) && i < GetSize(initval); i++)
if (initval[i] == State::S0 || initval[i] == State::S1)
init_bits.insert(sig[i]);
diff --git a/passes/opt/Makefile.inc b/passes/opt/Makefile.inc
index 0d01e9d3..c3e0a2a4 100644
--- a/passes/opt/Makefile.inc
+++ b/passes/opt/Makefile.inc
@@ -12,5 +12,6 @@ OBJS += passes/opt/share.o
OBJS += passes/opt/wreduce.o
OBJS += passes/opt/opt_demorgan.o
OBJS += passes/opt/rmports.o
+OBJS += passes/opt/opt_lut.o
endif
diff --git a/passes/opt/opt_expr.cc b/passes/opt/opt_expr.cc
index 0ba233c6..a05db2a4 100644
--- a/passes/opt/opt_expr.cc
+++ b/passes/opt/opt_expr.cc
@@ -155,6 +155,13 @@ bool group_cell_inputs(RTLIL::Module *module, RTLIL::Cell *cell, bool commutativ
new_b.append_bit(it.first.second);
}
+ if (cell->type.in("$and", "$or") && i == GRP_CONST_A) {
+ log(" Direct Connection: %s (%s with %s)\n", log_signal(new_b), log_id(cell->type), log_signal(new_a));
+ module->connect(new_y, new_b);
+ module->connect(new_conn);
+ continue;
+ }
+
RTLIL::Cell *c = module->addCell(NEW_ID, cell->type);
c->setPort("\\A", new_a);
@@ -259,6 +266,22 @@ bool is_one_or_minus_one(const Const &value, bool is_signed, bool &is_negative)
return last_bit_one;
}
+int get_highest_hot_index(RTLIL::SigSpec signal)
+{
+ for (int i = GetSize(signal) - 1; i >= 0; i--)
+ {
+ if (signal[i] == RTLIL::State::S0)
+ continue;
+
+ if (signal[i] == RTLIL::State::S1)
+ return i;
+
+ break;
+ }
+
+ return -1;
+}
+
// if the signal has only one bit set, return the index of that bit.
// otherwise return -1
int get_onehot_bit_index(RTLIL::SigSpec signal)
@@ -1344,118 +1367,139 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
}
}
- // replace a<0 or a>=0 with the top bit of a
+ // simplify comparisons
if (do_fine && (cell->type == "$lt" || cell->type == "$ge" || cell->type == "$gt" || cell->type == "$le"))
{
- //used to decide whether the signal needs to be negated
- bool is_lt = false;
-
- //references the variable signal in the comparison
- RTLIL::SigSpec sigVar;
-
- //references the constant signal in the comparison
- RTLIL::SigSpec sigConst;
-
- // note that this signal must be constant for the optimization
- // to take place, but it is not checked beforehand.
- // If new passes are added, this signal must be checked for const-ness
-
- //width of the variable port
- int width;
- int const_width;
-
- bool var_signed;
-
- if (cell->type == "$lt" || cell->type == "$ge") {
- is_lt = cell->type == "$lt" ? 1 : 0;
- sigVar = cell->getPort("\\A");
- sigConst = cell->getPort("\\B");
- width = cell->parameters["\\A_WIDTH"].as_int();
- const_width = cell->parameters["\\B_WIDTH"].as_int();
- var_signed = cell->parameters["\\A_SIGNED"].as_bool();
- } else
- if (cell->type == "$gt" || cell->type == "$le") {
- is_lt = cell->type == "$gt" ? 1 : 0;
- sigVar = cell->getPort("\\B");
- sigConst = cell->getPort("\\A");
- width = cell->parameters["\\B_WIDTH"].as_int();
- const_width = cell->parameters["\\A_WIDTH"].as_int();
- var_signed = cell->parameters["\\B_SIGNED"].as_bool();
- } else
- log_abort();
+ IdString cmp_type = cell->type;
+ SigSpec var_sig = cell->getPort("\\A");
+ SigSpec const_sig = cell->getPort("\\B");
+ int var_width = cell->parameters["\\A_WIDTH"].as_int();
+ int const_width = cell->parameters["\\B_WIDTH"].as_int();
+ bool is_signed = cell->getParam("\\A_SIGNED").as_bool();
- // replace a(signed) < 0 with the high bit of a
- if (sigConst.is_fully_const() && sigConst.is_fully_zero() && var_signed == true)
+ if (!const_sig.is_fully_const())
{
- RTLIL::SigSpec a_prime(RTLIL::State::S0, cell->parameters["\\Y_WIDTH"].as_int());
- a_prime[0] = sigVar[width - 1];
- if (is_lt) {
- log("Replacing %s cell `%s' (implementing X<0) with X[%d]: %s\n",
- log_id(cell->type), log_id(cell), width-1, log_signal(a_prime));
- module->connect(cell->getPort("\\Y"), a_prime);
- module->remove(cell);
- } else {
- log("Replacing %s cell `%s' (implementing X>=0) with ~X[%d]: %s\n",
- log_id(cell->type), log_id(cell), width-1, log_signal(a_prime));
- module->addNot(NEW_ID, a_prime, cell->getPort("\\Y"));
- module->remove(cell);
- }
- did_something = true;
- goto next_cell;
- } else
- if (sigConst.is_fully_const() && sigConst.is_fully_def() && var_signed == false)
+ std::swap(var_sig, const_sig);
+ std::swap(var_width, const_width);
+ if (cmp_type == "$gt")
+ cmp_type = "$lt";
+ else if (cmp_type == "$lt")
+ cmp_type = "$gt";
+ else if (cmp_type == "$ge")
+ cmp_type = "$le";
+ else if (cmp_type == "$le")
+ cmp_type = "$ge";
+ }
+
+ if (const_sig.is_fully_def() && const_sig.is_fully_const())
{
- if (sigConst.is_fully_zero()) {
- RTLIL::SigSpec a_prime(RTLIL::State::S0, 1);
- if (is_lt) {
- log("Replacing %s cell `%s' (implementing unsigned X<0) with constant false.\n",
- log_id(cell->type), log_id(cell));
- a_prime[0] = RTLIL::State::S0;
- } else {
- log("Replacing %s cell `%s' (implementing unsigned X>=0) with constant true.\n",
- log_id(cell->type), log_id(cell));
- a_prime[0] = RTLIL::State::S1;
+ std::string condition, replacement;
+ SigSpec replace_sig(State::S0, GetSize(cell->getPort("\\Y")));
+ bool replace = false;
+ bool remove = false;
+
+ if (!is_signed)
+ { /* unsigned */
+ if (const_sig.is_fully_zero() && cmp_type == "$lt") {
+ condition = "unsigned X<0";
+ replacement = "constant 0";
+ replace_sig[0] = State::S0;
+ replace = true;
+ }
+ if (const_sig.is_fully_zero() && cmp_type == "$ge") {
+ condition = "unsigned X>=0";
+ replacement = "constant 1";
+ replace_sig[0] = State::S1;
+ replace = true;
+ }
+ if (const_width == var_width && const_sig.is_fully_ones() && cmp_type == "$gt") {
+ condition = "unsigned X>~0";
+ replacement = "constant 0";
+ replace_sig[0] = State::S0;
+ replace = true;
+ }
+ if (const_width == var_width && const_sig.is_fully_ones() && cmp_type == "$le") {
+ condition = "unsigned X<=~0";
+ replacement = "constant 1";
+ replace_sig[0] = State::S1;
+ replace = true;
}
- module->connect(cell->getPort("\\Y"), a_prime);
- module->remove(cell);
- did_something = true;
- goto next_cell;
- }
- int const_bit_set = get_onehot_bit_index(sigConst);
- if (const_bit_set >= 0 && const_bit_set < width) {
- int bit_set = const_bit_set;
- RTLIL::SigSpec a_prime(RTLIL::State::S0, width - bit_set);
- for (int i = bit_set; i < width; i++) {
- a_prime[i - bit_set] = sigVar[i];
+ int const_bit_hot = get_onehot_bit_index(const_sig);
+ if (const_bit_hot >= 0 && const_bit_hot < var_width)
+ {
+ RTLIL::SigSpec var_high_sig(RTLIL::State::S0, var_width - const_bit_hot);
+ for (int i = const_bit_hot; i < var_width; i++) {
+ var_high_sig[i - const_bit_hot] = var_sig[i];
+ }
+
+ if (cmp_type == "$lt")
+ {
+ condition = stringf("unsigned X<%s", log_signal(const_sig));
+ replacement = stringf("!X[%d:%d]", var_width - 1, const_bit_hot);
+ module->addLogicNot(NEW_ID, var_high_sig, cell->getPort("\\Y"));
+ remove = true;
+ }
+ if (cmp_type == "$ge")
+ {
+ condition = stringf("unsigned X>=%s", log_signal(const_sig));
+ replacement = stringf("|X[%d:%d]", var_width - 1, const_bit_hot);
+ module->addReduceOr(NEW_ID, var_high_sig, cell->getPort("\\Y"));
+ remove = true;
+ }
}
- if (is_lt) {
- log("Replacing %s cell `%s' (implementing unsigned X<%s) with !X[%d:%d]: %s.\n",
- log_id(cell->type), log_id(cell), log_signal(sigConst), width - 1, bit_set, log_signal(a_prime));
- module->addLogicNot(NEW_ID, a_prime, cell->getPort("\\Y"));
- } else {
- log("Replacing %s cell `%s' (implementing unsigned X>=%s) with |X[%d:%d]: %s.\n",
- log_id(cell->type), log_id(cell), log_signal(sigConst), width - 1, bit_set, log_signal(a_prime));
- module->addReduceOr(NEW_ID, a_prime, cell->getPort("\\Y"));
+
+ int const_bit_set = get_highest_hot_index(const_sig);
+ if(const_bit_set >= var_width)
+ {
+ string cmp_name;
+ if (cmp_type == "$lt" || cmp_type == "$le")
+ {
+ if (cmp_type == "$lt") cmp_name = "<";
+ if (cmp_type == "$le") cmp_name = "<=";
+ condition = stringf("unsigned X[%d:0]%s%s", var_width - 1, cmp_name.c_str(), log_signal(const_sig));
+ replacement = "constant 1";
+ replace_sig[0] = State::S1;
+ replace = true;
+ }
+ if (cmp_type == "$gt" || cmp_type == "$ge")
+ {
+ if (cmp_type == "$gt") cmp_name = ">";
+ if (cmp_type == "$ge") cmp_name = ">=";
+ condition = stringf("unsigned X[%d:0]%s%s", var_width - 1, cmp_name.c_str(), log_signal(const_sig));
+ replacement = "constant 0";
+ replace_sig[0] = State::S0;
+ replace = true;
+ }
}
- module->remove(cell);
- did_something = true;
- goto next_cell;
}
- else if(const_bit_set >= width && const_bit_set >= 0){
- RTLIL::SigSpec a_prime(RTLIL::State::S0, 1);
- if(is_lt){
- a_prime[0] = RTLIL::State::S1;
- log("Replacing %s cell `%s' (implementing unsigned X[%d:0] < %s[%d:0]) with constant 0.\n", log_id(cell->type), log_id(cell), width-1, log_signal(sigConst),const_width-1);
+ else
+ { /* signed */
+ if (const_sig.is_fully_zero() && cmp_type == "$lt")
+ {
+ condition = "signed X<0";
+ replacement = stringf("X[%d]", var_width - 1);
+ replace_sig[0] = var_sig[var_width - 1];
+ replace = true;
}
- else{
- log("Replacing %s cell `%s' (implementing unsigned X[%d:0]>= %s[%d:0]) with constant 1.\n", log_id(cell->type), log_id(cell), width-1, log_signal(sigConst),const_width-1);
+ if (const_sig.is_fully_zero() && cmp_type == "$ge")
+ {
+ condition = "signed X>=0";
+ replacement = stringf("X[%d]", var_width - 1);
+ module->addNot(NEW_ID, var_sig[var_width - 1], cell->getPort("\\Y"));
+ remove = true;
}
- module->connect(cell->getPort("\\Y"), a_prime);
+ }
+
+ if (replace || remove)
+ {
+ log("Replacing %s cell `%s' (implementing %s) with %s.\n",
+ log_id(cell->type), log_id(cell), condition.c_str(), replacement.c_str());
+ if (replace)
+ module->connect(cell->getPort("\\Y"), replace_sig);
module->remove(cell);
did_something = true;
goto next_cell;
-
}
}
}
@@ -1477,7 +1521,7 @@ struct OptExprPass : public Pass {
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("It also performs some simple expression rewriting.\n");
log("\n");
log(" -mux_undef\n");
log(" remove 'undef' inputs from $mux, $pmux and $_MUX_ cells\n");
diff --git a/passes/opt/opt_lut.cc b/passes/opt/opt_lut.cc
new file mode 100644
index 00000000..26855fd7
--- /dev/null
+++ b/passes/opt/opt_lut.cc
@@ -0,0 +1,607 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2018 whitequark <whitequark@whitequark.org>
+ *
+ * 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/modtools.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct OptLutWorker
+{
+ dict<IdString, dict<int, IdString>> &dlogic;
+ RTLIL::Module *module;
+ ModIndex index;
+ SigMap sigmap;
+
+ pool<RTLIL::Cell*> luts;
+ dict<RTLIL::Cell*, int> luts_arity;
+ dict<RTLIL::Cell*, pool<RTLIL::Cell*>> luts_dlogics;
+ dict<RTLIL::Cell*, pool<int>> luts_dlogic_inputs;
+
+ int eliminated_count = 0, combined_count = 0;
+
+ bool evaluate_lut(RTLIL::Cell *lut, dict<SigBit, bool> inputs)
+ {
+ SigSpec lut_input = sigmap(lut->getPort("\\A"));
+ int lut_width = lut->getParam("\\WIDTH").as_int();
+ Const lut_table = lut->getParam("\\LUT");
+ int lut_index = 0;
+
+ for (int i = 0; i < lut_width; i++)
+ {
+ SigBit input = sigmap(lut_input[i]);
+ if (inputs.count(input))
+ {
+ lut_index |= inputs[input] << i;
+ }
+ else
+ {
+ lut_index |= SigSpec(lut_input[i]).as_bool() << i;
+ }
+ }
+
+ return lut_table.extract(lut_index).as_bool();
+ }
+
+ void show_stats_by_arity()
+ {
+ dict<int, int> arity_counts;
+ dict<IdString, int> dlogic_counts;
+ int max_arity = 0;
+
+ for (auto lut_arity : luts_arity)
+ {
+ max_arity = max(max_arity, lut_arity.second);
+ arity_counts[lut_arity.second]++;
+ }
+
+ for (auto &lut_dlogics : luts_dlogics)
+ {
+ for (auto &lut_dlogic : lut_dlogics.second)
+ {
+ dlogic_counts[lut_dlogic->type]++;
+ }
+ }
+
+ log("Number of LUTs: %8zu\n", luts.size());
+ for (int arity = 1; arity <= max_arity; arity++)
+ {
+ if (arity_counts[arity])
+ log(" %d-LUT %16d\n", arity, arity_counts[arity]);
+ }
+ for (auto &dlogic_count : dlogic_counts)
+ {
+ log(" with %-12s %4d\n", dlogic_count.first.c_str(), dlogic_count.second);
+ }
+ }
+
+ OptLutWorker(dict<IdString, dict<int, IdString>> &dlogic, RTLIL::Module *module, int limit) :
+ dlogic(dlogic), module(module), index(module), sigmap(module)
+ {
+ log("Discovering LUTs.\n");
+ for (auto cell : module->selected_cells())
+ {
+ if (cell->type == "$lut")
+ {
+ int lut_width = cell->getParam("\\WIDTH").as_int();
+ SigSpec lut_input = cell->getPort("\\A");
+ int lut_arity = 0;
+
+ log("Found $lut\\WIDTH=%d cell %s.%s.\n", lut_width, log_id(module), log_id(cell));
+ luts.insert(cell);
+
+ // First, find all dedicated logic we're connected to. This results in an overapproximation
+ // of such connections.
+ pool<RTLIL::Cell*> lut_all_dlogics;
+ for (int i = 0; i < lut_width; i++)
+ {
+ SigBit bit = lut_input[i];
+ for (auto &port : index.query_ports(bit))
+ {
+ if (dlogic.count(port.cell->type))
+ {
+ auto &dlogic_map = dlogic[port.cell->type];
+ if (dlogic_map.count(i))
+ {
+ if (port.port == dlogic_map[i])
+ {
+ lut_all_dlogics.insert(port.cell);
+ }
+ }
+ }
+ }
+ }
+
+ // Second, make sure that the connection to dedicated logic is legal. If it is not legal,
+ // it means one of the two things:
+ // * The connection is spurious. I.e. this is dedicated logic that will be packed
+ // with some other LUT, and it just happens to be connected to this LUT as well.
+ // * The connection is illegal.
+ // In either of these cases, we don't need to concern ourselves with preserving the connection
+ // between this LUT and this dedicated logic cell.
+ pool<RTLIL::Cell*> lut_legal_dlogics;
+ pool<int> lut_dlogic_inputs;
+ for (auto lut_dlogic : lut_all_dlogics)
+ {
+ auto &dlogic_map = dlogic[lut_dlogic->type];
+ bool legal = true;
+ for (auto &dlogic_conn : dlogic_map)
+ {
+ if (lut_width <= dlogic_conn.first)
+ {
+ log(" LUT has illegal connection to %s cell %s.%s.\n", lut_dlogic->type.c_str(), log_id(module), log_id(lut_dlogic));
+ log(" LUT input A[%d] not present.\n", dlogic_conn.first);
+ legal = false;
+ break;
+ }
+ if (sigmap(lut_input[dlogic_conn.first]) != sigmap(lut_dlogic->getPort(dlogic_conn.second)))
+ {
+ log(" LUT has illegal connection to %s cell %s.%s.\n", lut_dlogic->type.c_str(), log_id(module), log_id(lut_dlogic));
+ log(" LUT input A[%d] (wire %s) not connected to %s port %s (wire %s).\n", dlogic_conn.first, log_signal(lut_input[dlogic_conn.first]), lut_dlogic->type.c_str(), dlogic_conn.second.c_str(), log_signal(lut_dlogic->getPort(dlogic_conn.second)));
+ legal = false;
+ break;
+ }
+ }
+
+ if (legal)
+ {
+ log(" LUT has legal connection to %s cell %s.%s.\n", lut_dlogic->type.c_str(), log_id(module), log_id(lut_dlogic));
+ lut_legal_dlogics.insert(lut_dlogic);
+ for (auto &dlogic_conn : dlogic_map)
+ lut_dlogic_inputs.insert(dlogic_conn.first);
+ }
+ }
+
+ // Third, determine LUT arity. An n-wide LUT that has k constant inputs and m inputs shared with dedicated
+ // logic implements an (n-k-m)-ary function.
+ for (int i = 0; i < lut_width; i++)
+ {
+ SigBit bit = lut_input[i];
+ if (bit.wire || lut_dlogic_inputs.count(i))
+ lut_arity++;
+ }
+
+ log(" Cell implements a %d-LUT.\n", lut_arity);
+ luts_arity[cell] = lut_arity;
+ luts_dlogics[cell] = lut_legal_dlogics;
+ luts_dlogic_inputs[cell] = lut_dlogic_inputs;
+ }
+ }
+ show_stats_by_arity();
+
+ log("\n");
+ log("Eliminating LUTs.\n");
+ pool<RTLIL::Cell*> worklist = luts;
+ while (worklist.size())
+ {
+ if (limit == 0)
+ {
+ log("Limit reached.\n");
+ break;
+ }
+
+ auto lut = worklist.pop();
+ SigSpec lut_input = sigmap(lut->getPort("\\A"));
+ pool<int> &lut_dlogic_inputs = luts_dlogic_inputs[lut];
+
+ vector<SigBit> lut_inputs;
+ for (auto &bit : lut_input)
+ {
+ if (bit.wire)
+ lut_inputs.push_back(sigmap(bit));
+ }
+
+ bool const0_match = true;
+ bool const1_match = true;
+ vector<bool> input_matches;
+ for (size_t i = 0; i < lut_inputs.size(); i++)
+ input_matches.push_back(true);
+
+ for (int eval = 0; eval < 1 << lut_inputs.size(); eval++)
+ {
+ dict<SigBit, bool> eval_inputs;
+ for (size_t i = 0; i < lut_inputs.size(); i++)
+ eval_inputs[lut_inputs[i]] = (eval >> i) & 1;
+ bool value = evaluate_lut(lut, eval_inputs);
+ if (value != 0)
+ const0_match = false;
+ if (value != 1)
+ const1_match = false;
+ for (size_t i = 0; i < lut_inputs.size(); i++)
+ {
+ if (value != eval_inputs[lut_inputs[i]])
+ input_matches[i] = false;
+ }
+ }
+
+ int input_match = -1;
+ for (size_t i = 0; i < lut_inputs.size(); i++)
+ if (input_matches[i])
+ input_match = i;
+
+ if (const0_match || const1_match || input_match != -1)
+ {
+ log("Found redundant cell %s.%s.\n", log_id(module), log_id(lut));
+
+ SigBit value;
+ if (const0_match)
+ {
+ log(" Cell evaluates constant 0.\n");
+ value = State::S0;
+ }
+ if (const1_match)
+ {
+ log(" Cell evaluates constant 1.\n");
+ value = State::S1;
+ }
+ if (input_match != -1) {
+ log(" Cell evaluates signal %s.\n", log_signal(lut_inputs[input_match]));
+ value = lut_inputs[input_match];
+ }
+
+ if (lut_dlogic_inputs.size())
+ {
+ log(" Not eliminating cell (connected to dedicated logic).\n");
+ }
+ else
+ {
+ SigSpec lut_output = lut->getPort("\\Y");
+ for (auto &port : index.query_ports(lut_output))
+ {
+ if (port.cell != lut && luts.count(port.cell))
+ worklist.insert(port.cell);
+ }
+
+ module->connect(lut_output, value);
+ sigmap.add(lut_output, value);
+
+ module->remove(lut);
+ luts.erase(lut);
+ luts_arity.erase(lut);
+ luts_dlogics.erase(lut);
+ luts_dlogic_inputs.erase(lut);
+
+ eliminated_count++;
+ if (limit > 0)
+ limit--;
+ }
+ }
+ }
+ show_stats_by_arity();
+
+ log("\n");
+ log("Combining LUTs.\n");
+ worklist = luts;
+ while (worklist.size())
+ {
+ if (limit == 0)
+ {
+ log("Limit reached.\n");
+ break;
+ }
+
+ auto lutA = worklist.pop();
+ SigSpec lutA_input = sigmap(lutA->getPort("\\A"));
+ SigSpec lutA_output = sigmap(lutA->getPort("\\Y")[0]);
+ int lutA_width = lutA->getParam("\\WIDTH").as_int();
+ int lutA_arity = luts_arity[lutA];
+ pool<int> &lutA_dlogic_inputs = luts_dlogic_inputs[lutA];
+
+ auto lutA_output_ports = index.query_ports(lutA->getPort("\\Y"));
+ if (lutA_output_ports.size() != 2)
+ continue;
+
+ for (auto &port : lutA_output_ports)
+ {
+ if (port.cell == lutA)
+ continue;
+
+ if (luts.count(port.cell))
+ {
+ auto lutB = port.cell;
+ SigSpec lutB_input = sigmap(lutB->getPort("\\A"));
+ SigSpec lutB_output = sigmap(lutB->getPort("\\Y")[0]);
+ int lutB_width = lutB->getParam("\\WIDTH").as_int();
+ int lutB_arity = luts_arity[lutB];
+ pool<int> &lutB_dlogic_inputs = luts_dlogic_inputs[lutB];
+
+ log("Found %s.%s (cell A) feeding %s.%s (cell B).\n", log_id(module), log_id(lutA), log_id(module), log_id(lutB));
+
+ if (index.query_is_output(lutA->getPort("\\Y")))
+ {
+ log(" Not combining LUTs (cascade connection feeds module output).\n");
+ continue;
+ }
+
+ pool<SigBit> lutA_inputs;
+ pool<SigBit> lutB_inputs;
+ for (auto &bit : lutA_input)
+ {
+ if (bit.wire)
+ lutA_inputs.insert(sigmap(bit));
+ }
+ for (auto &bit : lutB_input)
+ {
+ if (bit.wire)
+ lutB_inputs.insert(sigmap(bit));
+ }
+
+ pool<SigBit> common_inputs;
+ for (auto &bit : lutA_inputs)
+ {
+ if (lutB_inputs.count(bit))
+ common_inputs.insert(bit);
+ }
+
+ int lutM_arity = lutA_arity + lutB_arity - 1 - common_inputs.size();
+ if (lutA_dlogic_inputs.size())
+ log(" Cell A is a %d-LUT with %zu dedicated connections. ", lutA_arity, lutA_dlogic_inputs.size());
+ else
+ log(" Cell A is a %d-LUT. ", lutA_arity);
+ if (lutB_dlogic_inputs.size())
+ log("Cell B is a %d-LUT with %zu dedicated connections.\n", lutB_arity, lutB_dlogic_inputs.size());
+ else
+ log("Cell B is a %d-LUT.\n", lutB_arity);
+ log(" Cells share %zu input(s) and can be merged into one %d-LUT.\n", common_inputs.size(), lutM_arity);
+
+ const int COMBINE_A = 1, COMBINE_B = 2, COMBINE_EITHER = COMBINE_A | COMBINE_B;
+ int combine_mask = 0;
+ if (lutM_arity > lutA_width)
+ {
+ log(" Not combining LUTs into cell A (combined LUT wider than cell A).\n");
+ }
+ else if (lutB_dlogic_inputs.size() > 0)
+ {
+ log(" Not combining LUTs into cell A (cell B is connected to dedicated logic).\n");
+ }
+ else if (lutB->get_bool_attribute("\\lut_keep"))
+ {
+ log(" Not combining LUTs into cell A (cell B has attribute \\lut_keep).\n");
+ }
+ else
+ {
+ combine_mask |= COMBINE_A;
+ }
+ if (lutM_arity > lutB_width)
+ {
+ log(" Not combining LUTs into cell B (combined LUT wider than cell B).\n");
+ }
+ else if (lutA_dlogic_inputs.size() > 0)
+ {
+ log(" Not combining LUTs into cell B (cell A is connected to dedicated logic).\n");
+ }
+ else if (lutA->get_bool_attribute("\\lut_keep"))
+ {
+ log(" Not combining LUTs into cell B (cell A has attribute \\lut_keep).\n");
+ }
+ else
+ {
+ combine_mask |= COMBINE_B;
+ }
+
+ int combine = combine_mask;
+ if (combine == COMBINE_EITHER)
+ {
+ log(" Can combine into either cell.\n");
+ if (lutA_arity == 1)
+ {
+ log(" Cell A is a buffer or inverter, combining into cell B.\n");
+ combine = COMBINE_B;
+ }
+ else if (lutB_arity == 1)
+ {
+ log(" Cell B is a buffer or inverter, combining into cell A.\n");
+ combine = COMBINE_A;
+ }
+ else
+ {
+ log(" Arbitrarily combining into cell A.\n");
+ combine = COMBINE_A;
+ }
+ }
+
+ RTLIL::Cell *lutM, *lutR;
+ pool<SigBit> lutM_inputs, lutR_inputs;
+ pool<int> lutM_dlogic_inputs;
+ if (combine == COMBINE_A)
+ {
+ log(" Combining LUTs into cell A.\n");
+ lutM = lutA;
+ lutM_inputs = lutA_inputs;
+ lutM_dlogic_inputs = lutA_dlogic_inputs;
+ lutR = lutB;
+ lutR_inputs = lutB_inputs;
+ }
+ else if (combine == COMBINE_B)
+ {
+ log(" Combining LUTs into cell B.\n");
+ lutM = lutB;
+ lutM_inputs = lutB_inputs;
+ lutM_dlogic_inputs = lutB_dlogic_inputs;
+ lutR = lutA;
+ lutR_inputs = lutA_inputs;
+ }
+ else
+ {
+ log(" Cannot combine LUTs.\n");
+ continue;
+ }
+
+ pool<SigBit> lutR_unique;
+ for (auto &bit : lutR_inputs)
+ {
+ if (!common_inputs.count(bit) && bit != lutA_output)
+ lutR_unique.insert(bit);
+ }
+
+ int lutM_width = lutM->getParam("\\WIDTH").as_int();
+ SigSpec lutM_input = sigmap(lutM->getPort("\\A"));
+ std::vector<SigBit> lutM_new_inputs;
+ for (int i = 0; i < lutM_width; i++)
+ {
+ bool input_unused = false;
+ if (sigmap(lutM_input[i]) == lutA_output)
+ input_unused = true;
+ if (!lutM_input[i].wire && !lutM_dlogic_inputs.count(i))
+ input_unused = true;
+
+ if (input_unused && lutR_unique.size())
+ {
+ SigBit new_input = lutR_unique.pop();
+ log(" Connecting input %d as %s.\n", i, log_signal(new_input));
+ lutM_new_inputs.push_back(new_input);
+ }
+ else if (sigmap(lutM_input[i]) == lutA_output)
+ {
+ log(" Disconnecting cascade input %d.\n", i);
+ lutM_new_inputs.push_back(SigBit());
+ }
+ else
+ {
+ log(" Leaving input %d as %s.\n", i, log_signal(lutM_input[i]));
+ lutM_new_inputs.push_back(lutM_input[i]);
+ }
+ }
+ log_assert(lutR_unique.size() == 0);
+
+ RTLIL::Const lutM_new_table(State::Sx, 1 << lutM_width);
+ for (int eval = 0; eval < 1 << lutM_width; eval++)
+ {
+ dict<SigBit, bool> eval_inputs;
+ for (size_t i = 0; i < lutM_new_inputs.size(); i++)
+ {
+ eval_inputs[lutM_new_inputs[i]] = (eval >> i) & 1;
+ }
+ eval_inputs[lutA_output] = evaluate_lut(lutA, eval_inputs);
+ lutM_new_table[eval] = (RTLIL::State) evaluate_lut(lutB, eval_inputs);
+ }
+
+ log(" Cell A truth table: %s.\n", lutA->getParam("\\LUT").as_string().c_str());
+ log(" Cell B truth table: %s.\n", lutB->getParam("\\LUT").as_string().c_str());
+ log(" Merged truth table: %s.\n", lutM_new_table.as_string().c_str());
+
+ lutM->setParam("\\LUT", lutM_new_table);
+ lutM->setPort("\\A", lutM_new_inputs);
+ lutM->setPort("\\Y", lutB_output);
+
+ luts_arity[lutM] = lutM_arity;
+ luts.erase(lutR);
+ luts_arity.erase(lutR);
+ lutR->module->remove(lutR);
+
+ worklist.insert(lutM);
+ worklist.erase(lutR);
+
+ combined_count++;
+ if (limit > 0)
+ limit--;
+ }
+ }
+ }
+ show_stats_by_arity();
+ }
+};
+
+static void split(std::vector<std::string> &tokens, const std::string &text, char sep)
+{
+ size_t start = 0, end = 0;
+ while ((end = text.find(sep, start)) != std::string::npos) {
+ tokens.push_back(text.substr(start, end - start));
+ start = end + 1;
+ }
+ tokens.push_back(text.substr(start));
+}
+
+struct OptLutPass : public Pass {
+ OptLutPass() : Pass("opt_lut", "optimize LUT cells") { }
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" opt_lut [options] [selection]\n");
+ log("\n");
+ log("This pass combines cascaded $lut cells with unused inputs.\n");
+ log("\n");
+ log(" -dlogic <type>:<cell-port>=<LUT-input>[:<cell-port>=<LUT-input>...]\n");
+ log(" preserve connections to dedicated logic cell <type> that has ports\n");
+ log(" <cell-port> connected to LUT inputs <LUT-input>. this includes\n");
+ log(" the case where both LUT and dedicated logic input are connected to\n");
+ log(" the same constant.\n");
+ log("\n");
+ log(" -limit N\n");
+ log(" only perform the first N combines, then stop. useful for debugging.\n");
+ log("\n");
+ }
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ log_header(design, "Executing OPT_LUT pass (optimize LUTs).\n");
+
+ dict<IdString, dict<int, IdString>> dlogic;
+ int limit = -1;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ if (args[argidx] == "-dlogic" && argidx+1 < args.size())
+ {
+ std::vector<std::string> tokens;
+ split(tokens, args[++argidx], ':');
+ if (tokens.size() < 2)
+ log_cmd_error("The -dlogic option requires at least one connection.\n");
+ IdString type = "\\" + tokens[0];
+ for (auto it = tokens.begin() + 1; it != tokens.end(); ++it) {
+ std::vector<std::string> conn_tokens;
+ split(conn_tokens, *it, '=');
+ if (conn_tokens.size() != 2)
+ log_cmd_error("Invalid format of -dlogic signal mapping.\n");
+ IdString logic_port = "\\" + conn_tokens[0];
+ int lut_input = atoi(conn_tokens[1].c_str());
+ dlogic[type][lut_input] = logic_port;
+ }
+ continue;
+ }
+ if (args[argidx] == "-limit" && argidx + 1 < args.size())
+ {
+ limit = atoi(args[++argidx].c_str());
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ int eliminated_count = 0, combined_count = 0;
+ for (auto module : design->selected_modules())
+ {
+ OptLutWorker worker(dlogic, module, limit - eliminated_count - combined_count);
+ eliminated_count += worker.eliminated_count;
+ combined_count += worker.combined_count;
+ }
+ if (eliminated_count)
+ design->scratchpad_set_bool("opt.did_something", true);
+ if (combined_count)
+ design->scratchpad_set_bool("opt.did_something", true);
+ log("\n");
+ log("Eliminated %d LUTs.\n", eliminated_count);
+ log("Combined %d LUTs.\n", combined_count);
+ }
+} OptLutPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/opt/opt_muxtree.cc b/passes/opt/opt_muxtree.cc
index 87c7ce9b..375697dc 100644
--- a/passes/opt/opt_muxtree.cc
+++ b/passes/opt/opt_muxtree.cc
@@ -36,6 +36,7 @@ struct OptMuxtreeWorker
RTLIL::Module *module;
SigMap assign_map;
int removed_count;
+ int glob_abort_cnt = 100000;
struct bitinfo_t {
bool seen_non_mux;
@@ -293,6 +294,9 @@ struct OptMuxtreeWorker
void eval_mux_port(knowledge_t &knowledge, int mux_idx, int port_idx, bool do_replace_known, bool do_enable_ports, int abort_count)
{
+ if (glob_abort_cnt == 0)
+ return;
+
muxinfo_t &muxinfo = mux2info[mux_idx];
if (do_enable_ports)
@@ -315,7 +319,7 @@ struct OptMuxtreeWorker
knowledge.visited_muxes[m] = true;
parent_muxes.push_back(m);
}
- for (int m : parent_muxes)
+ for (int m : parent_muxes) {
if (root_enable_muxes.at(m))
continue;
else if (root_muxes.at(m)) {
@@ -327,6 +331,9 @@ struct OptMuxtreeWorker
eval_mux(knowledge, m, false, do_enable_ports, abort_count - 1);
} else
eval_mux(knowledge, m, do_replace_known, do_enable_ports, abort_count);
+ if (glob_abort_cnt == 0)
+ return;
+ }
for (int m : parent_muxes)
knowledge.visited_muxes[m] = false;
@@ -390,6 +397,12 @@ struct OptMuxtreeWorker
void eval_mux(knowledge_t &knowledge, int mux_idx, bool do_replace_known, bool do_enable_ports, int abort_count)
{
+ if (glob_abort_cnt == 0) {
+ log(" Giving up (too many iterations)\n");
+ return;
+ }
+ glob_abort_cnt--;
+
muxinfo_t &muxinfo = mux2info[mux_idx];
// set input ports to constants if we find known active or inactive signals
@@ -433,6 +446,9 @@ struct OptMuxtreeWorker
if (knowledge.known_inactive.at(portinfo.ctrl_sig))
continue;
eval_mux_port(knowledge, mux_idx, port_idx, do_replace_known, do_enable_ports, abort_count);
+
+ if (glob_abort_cnt == 0)
+ return;
}
}
diff --git a/passes/opt/opt_rmdff.cc b/passes/opt/opt_rmdff.cc
index 5880254c..e8570f0e 100644
--- a/passes/opt/opt_rmdff.cc
+++ b/passes/opt/opt_rmdff.cc
@@ -174,8 +174,6 @@ bool handle_dffsr(RTLIL::Module *mod, RTLIL::Cell *cell)
cell->unsetParam("\\CLR_POLARITY");
cell->unsetPort("\\SET");
cell->unsetPort("\\CLR");
-
- return true;
}
else
{
@@ -186,11 +184,12 @@ bool handle_dffsr(RTLIL::Module *mod, RTLIL::Cell *cell)
cell->unsetParam("\\CLR_POLARITY");
cell->unsetPort("\\SET");
cell->unsetPort("\\CLR");
-
- return true;
}
+
+ return true;
}
- else
+
+ if (!hasreset)
{
IdString new_type;
@@ -207,8 +206,10 @@ bool handle_dffsr(RTLIL::Module *mod, RTLIL::Cell *cell)
cell->unsetPort("\\S");
cell->unsetPort("\\R");
- return did_something;
+ return true;
}
+
+ return did_something;
}
bool handle_dlatch(RTLIL::Module *mod, RTLIL::Cell *dlatch)
diff --git a/passes/opt/share.cc b/passes/opt/share.cc
index b8028082..c85c2742 100644
--- a/passes/opt/share.cc
+++ b/passes/opt/share.cc
@@ -710,8 +710,12 @@ struct ShareWorker
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));
+ if (GetSize(addr1) < GetSize(addr2))
+ addr1.extend_u0(GetSize(addr2));
+ else
+ addr2.extend_u0(GetSize(addr1));
+ supercell->setPort("\\ADDR", addr1 != addr2 ? module->Mux(NEW_ID, addr2, addr1, act) : addr1);
+ supercell->parameters["\\ABITS"] = RTLIL::Const(GetSize(addr1));
supercell_aux.insert(module->addPos(NEW_ID, supercell->getPort("\\DATA"), c2->getPort("\\DATA")));
supercell_aux.insert(supercell);
return supercell;
diff --git a/passes/opt/wreduce.cc b/passes/opt/wreduce.cc
index 0164f58d..52245ce3 100644
--- a/passes/opt/wreduce.cc
+++ b/passes/opt/wreduce.cc
@@ -38,7 +38,8 @@ struct WreduceConfig
"$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx",
"$lt", "$le", "$eq", "$ne", "$eqx", "$nex", "$ge", "$gt",
"$add", "$sub", "$mul", // "$div", "$mod", "$pow",
- "$mux", "$pmux"
+ "$mux", "$pmux",
+ "$dff", "$adff"
});
}
};
@@ -52,6 +53,8 @@ struct WreduceWorker
std::set<Cell*, IdString::compare_ptr_by_name<Cell>> work_queue_cells;
std::set<SigBit> work_queue_bits;
pool<SigBit> keep_bits;
+ dict<SigBit, State> init_bits;
+ pool<SigBit> remove_init_bits;
WreduceWorker(WreduceConfig *config, Module *module) :
config(config), module(module), mi(module) { }
@@ -134,6 +137,91 @@ struct WreduceWorker
module->connect(sig_y.extract(n_kept, n_removed), sig_removed);
}
+ void run_cell_dff(Cell *cell)
+ {
+ // Reduce size of FF if inputs are just sign/zero extended or output bit is not used
+
+ SigSpec sig_d = mi.sigmap(cell->getPort("\\D"));
+ SigSpec sig_q = mi.sigmap(cell->getPort("\\Q"));
+ Const initval;
+
+ int width_before = GetSize(sig_q);
+
+ if (width_before == 0)
+ return;
+
+ bool zero_ext = sig_d[GetSize(sig_d)-1] == State::S0;
+ bool sign_ext = !zero_ext;
+
+ for (int i = 0; i < GetSize(sig_q); i++) {
+ SigBit bit = sig_q[i];
+ if (init_bits.count(bit))
+ initval.bits.push_back(init_bits.at(bit));
+ else
+ initval.bits.push_back(State::Sx);
+ }
+
+ for (int i = GetSize(sig_q)-1; i >= 0; i--)
+ {
+ if (zero_ext && sig_d[i] == State::S0 && (initval[i] == State::S0 || initval[i] == State::Sx)) {
+ module->connect(sig_q[i], State::S0);
+ remove_init_bits.insert(sig_q[i]);
+ sig_d.remove(i);
+ sig_q.remove(i);
+ continue;
+ }
+
+ if (sign_ext && i > 0 && sig_d[i] == sig_d[i-1] && initval[i] == initval[i-1]) {
+ module->connect(sig_q[i], sig_q[i-1]);
+ remove_init_bits.insert(sig_q[i]);
+ sig_d.remove(i);
+ sig_q.remove(i);
+ continue;
+ }
+
+ auto info = mi.query(sig_q[i]);
+ if (!info->is_output && GetSize(info->ports) == 1 && !keep_bits.count(mi.sigmap(sig_q[i]))) {
+ remove_init_bits.insert(sig_q[i]);
+ sig_d.remove(i);
+ sig_q.remove(i);
+ zero_ext = false;
+ sign_ext = false;
+ continue;
+ }
+
+ break;
+ }
+
+ if (width_before == GetSize(sig_q))
+ return;
+
+ if (GetSize(sig_q) == 0) {
+ log("Removed cell %s.%s (%s).\n", log_id(module), log_id(cell), log_id(cell->type));
+ module->remove(cell);
+ return;
+ }
+
+ log("Removed top %d bits (of %d) from FF cell %s.%s (%s).\n", width_before - GetSize(sig_q), width_before,
+ log_id(module), log_id(cell), log_id(cell->type));
+
+ for (auto bit : sig_d)
+ work_queue_bits.insert(bit);
+
+ for (auto bit : sig_q)
+ work_queue_bits.insert(bit);
+
+ // Narrow ARST_VALUE parameter to new size.
+ if (cell->parameters.count("\\ARST_VALUE")) {
+ Const arst_value = cell->getParam("\\ARST_VALUE");
+ arst_value.bits.resize(GetSize(sig_q));
+ cell->setParam("\\ARST_VALUE", arst_value);
+ }
+
+ cell->setPort("\\D", sig_d);
+ cell->setPort("\\Q", sig_q);
+ cell->fixup_parameters();
+ }
+
void run_reduce_inport(Cell *cell, char port, int max_port_size, bool &port_signed, bool &did_something)
{
port_signed = cell->getParam(stringf("\\%c_SIGNED", port)).as_bool();
@@ -176,6 +264,9 @@ struct WreduceWorker
if (cell->type.in("$mux", "$pmux"))
return run_cell_mux(cell);
+ if (cell->type.in("$dff", "$adff"))
+ return run_cell_dff(cell);
+
SigSpec sig = mi.sigmap(cell->getPort("\\Y"));
if (sig.has_const())
@@ -235,8 +326,11 @@ struct WreduceWorker
} else {
while (GetSize(sig) > 0)
{
- auto info = mi.query(sig[GetSize(sig)-1]);
+ auto bit = sig[GetSize(sig)-1];
+ if (keep_bits.count(bit))
+ break;
+ auto info = mi.query(bit);
if (info->is_output || GetSize(info->ports) > 1)
break;
@@ -297,10 +391,21 @@ struct WreduceWorker
void run()
{
- for (auto w : module->wires())
+ // create a copy as mi.sigmap will be updated as we process the module
+ SigMap init_attr_sigmap = mi.sigmap;
+
+ for (auto w : module->wires()) {
if (w->get_bool_attribute("\\keep"))
for (auto bit : mi.sigmap(w))
keep_bits.insert(bit);
+ if (w->attributes.count("\\init")) {
+ Const initval = w->attributes.at("\\init");
+ SigSpec initsig = init_attr_sigmap(w);
+ int width = std::min(GetSize(initval), GetSize(initsig));
+ for (int i = 0; i < width; i++)
+ init_bits[initsig[i]] = initval[i];
+ }
+ }
for (auto c : module->selected_cells())
work_queue_cells.insert(c);
@@ -348,6 +453,24 @@ struct WreduceWorker
module->connect(nw, SigSpec(w).extract(0, GetSize(nw)));
module->swap_names(w, nw);
}
+
+ if (!remove_init_bits.empty()) {
+ for (auto w : module->wires()) {
+ if (w->attributes.count("\\init")) {
+ Const initval = w->attributes.at("\\init");
+ Const new_initval(State::Sx, GetSize(w));
+ SigSpec initsig = init_attr_sigmap(w);
+ int width = std::min(GetSize(initval), GetSize(initsig));
+ for (int i = 0; i < width; i++) {
+ log_dump(initsig[i], remove_init_bits.count(initsig[i]));
+ if (!remove_init_bits.count(initsig[i]))
+ new_initval[i] = initval[i];
+ }
+ w->attributes.at("\\init") = new_initval;
+ log_dump(w->name, initval, new_initval);
+ }
+ }
+ }
}
};
diff --git a/passes/pmgen/.gitignore b/passes/pmgen/.gitignore
new file mode 100644
index 00000000..c9263057
--- /dev/null
+++ b/passes/pmgen/.gitignore
@@ -0,0 +1 @@
+/ice40_dsp_pm.h
diff --git a/passes/pmgen/Makefile.inc b/passes/pmgen/Makefile.inc
new file mode 100644
index 00000000..e0609d9b
--- /dev/null
+++ b/passes/pmgen/Makefile.inc
@@ -0,0 +1,8 @@
+OBJS += passes/pmgen/ice40_dsp.o
+
+passes/pmgen/ice40_dsp.o: passes/pmgen/ice40_dsp_pm.h
+EXTRA_OBJS += passes/pmgen/ice40_dsp_pm.h
+.SECONDARY: passes/pmgen/ice40_dsp_pm.h
+
+passes/pmgen/ice40_dsp_pm.h: passes/pmgen/pmgen.py passes/pmgen/ice40_dsp.pmg
+ $(P) mkdir -p passes/pmgen && python3 $^ $@
diff --git a/passes/pmgen/README.md b/passes/pmgen/README.md
new file mode 100644
index 00000000..223b4305
--- /dev/null
+++ b/passes/pmgen/README.md
@@ -0,0 +1,224 @@
+Pattern Matcher Generator
+=========================
+
+The program `pmgen.py` reads a `.pmg` (Pattern Matcher Generator) file and
+writes a header-only C++ library that implements that pattern matcher.
+
+The "patterns" in this context are subgraphs in a Yosys RTLIL netlist.
+
+The algorithm used in the generated pattern matcher is a simple recursive
+search with backtracking. It is left to the author of the `.pmg` file to
+determine an efficient cell order for the search that allows for maximum
+use of indices and early backtracking.
+
+
+API of Generated Matcher
+========================
+
+When `pmgen.py` reads a `foobar.pmg` file, it writes `foobar_pm.h` containing
+a class `foobar_pm`. That class is instantiated with an RTLIL module and a
+list of cells from that module:
+
+ foobar_pm pm(module, module->selected_cells());
+
+The caller must make sure that none of the cells in the 2nd argument are
+deleted for as long as the patter matcher instance is used.
+
+At any time it is possible to disable cells, preventing them from showing
+up in any future matches:
+
+ pm.blacklist(some_cell);
+
+The `.run(callback_function)` method searches for all matches and calls the
+callback function for each found match:
+
+ pm.run([&](){
+ log("found matching 'foo' cell: %s\n", log_id(pm.st.foo));
+ log(" with 'bar' cell: %s\n", log_id(pm.st.bar));
+ });
+
+The `.pmg` file declares matcher state variables that are accessible via the
+`.st.<state_name>` members. (The `.st` member is of type `foobar_pm::state_t`.)
+
+Similarly the `.pmg` file declares user data variables that become members of
+`.ud`, a struct of type `foobar_pm::udata_t`.
+
+
+The .pmg File Format
+====================
+
+The `.pmg` file format is a simple line-based file format. For the most part
+lines consist of whitespace-separated tokens.
+
+Lines in `.pmg` files starting with `//` are comments.
+
+Declaring state variables
+-------------------------
+
+One or more state variables can be declared using the `state` statement,
+followed by a C++ type in angle brackets, followed by a whitespace separated
+list of variable names. For example:
+
+ state <bool> flag1 flag2 happy big
+ state <SigSpec> sigA sigB sigY
+
+State variables are automatically managed by the generated backtracking algorithm
+and saved and restored as needed.
+
+They are automatically initialized to the default constructed value of their type
+when `.run(callback_function)` is called.
+
+Declaring udata variables
+-------------------------
+
+Udata (user-data) variables can be used for example to configure the matcher or
+the callback function used to perform actions on found matches.
+
+There is no automatic management of udata variables. For this reason it is
+recommended that the user-supplied matcher code treats them as read-only
+variables.
+
+They are declared like state variables, just using the `udata` statement:
+
+ udata <int> min_data_width max_data_width
+ udata <IdString> data_port_name
+
+They are atomatically initialzed to the default constructed value of their type
+when ther pattern matcher object is constructed.
+
+Embedded C++ code
+-----------------
+
+Many statements in a `.pmg` file contain C++ code. However, there are some
+slight additions to regular C++/Yosys/RTLIL code that make it a bit easier to
+write matchers:
+
+- Identifiers starting with a dollar sign or backslash are automatically
+ converted to special IdString variables that are initialized when the
+ matcher object is constructed.
+
+- The `port(<cell>, <portname>)` function is a handy alias for
+ `sigmap(<cell>->getPort(<portname>))`.
+
+- Similarly `param(<cell>, <paramname>)` looks up a parameter on a cell.
+
+- The function `nusers(<sigspec>)` returns the number of different cells
+ connected to any of the given signal bits, plus one if any of the signal
+ bits is also a primary input or primary output.
+
+- In `code..endcode` blocks there exist `accept`, `reject`, and `branch`
+ statements.
+
+- In `index` statements there is a special `===` operator for the index
+ lookup.
+
+Matching cells
+--------------
+
+Cells are matched using `match..endmatch` blocks. For example:
+
+ match mul
+ if ff
+ select mul->type == $mul
+ select nusers(port(mul, \Y) == 2
+ index <SigSpec> port(mul, \Y) === port(ff, \D)
+ filter some_weird_function(mul) < other_weird_function(ff)
+ optional
+ endmatch
+
+A `match` block starts with `match <statevar>` and implicitly generates
+a state variable `<statevar>` of type `RTLIL::Cell*`.
+
+All statements in the match block are optional. (An empty match block
+would simply match each and every cell in the module.)
+
+The `if <expression>` statement makes the match block conditional. If
+`<expression>` evaluates to `false` then the match block will be ignored
+and the corresponding state variable is set to `nullptr`. In our example
+we only try to match the `mul` cell if the `ff` state variable points
+to a cell. (Presumably `ff` is provided by a prior `match` block.)
+
+The `select` lines are evaluated once for each cell when the matcher is
+initialized. A `match` block will only consider cells for which all `select`
+expressions evaluated to `true`. Note that the state variable corresponding to
+the match (in the example `mul`) is the only state variable that may be used
+in `select` lines.
+
+Index lines are using the `index <type> expr1 === expr2` syntax. `expr1` is
+evaluated during matcher initialization and the same restrictions apply as for
+`select` expressions. `expr2` is evaluated when the match is calulated. It is a
+function of any state variables assigned to by previous blocks. Both expression
+are converted to the given type and compared for equality. Only cells for which
+all `index` statements in the block pass are considered by the match.
+
+Note that `select` and `index` are fast operations. Thus `select` and `index`
+should be used whenever possible to create efficient matchers.
+
+Finally, `filter <expression>` narrows down the remaining list of cells. For
+performance reasons `filter` statements should only be used for things that
+can't be done using `select` and `index`.
+
+The `optional` statement marks optional matches. I.e. the matcher will also
+explore the case where `mul` is set to `nullptr`. Without the `optional`
+statement a match may only be assigned nullptr when one of the `if` expressions
+evaluates to `false`.
+
+Additional code
+---------------
+
+Interleaved with `match..endmatch` blocks there may be `code..endcode` blocks.
+Such a block starts with the keyword `code` followed by a list of state variables
+that the block may modify. For example:
+
+ code addAB sigS
+ if (addA) {
+ addAB = addA;
+ sigS = port(addA, \B);
+ }
+ if (addB) {
+ addAB = addB;
+ sigS = port(addB, \A);
+ }
+ endcode
+
+The special keyword `reject` can be used to reject the current state and
+backtrack. For example:
+
+ code
+ if (ffA && ffB) {
+ if (port(ffA, \CLK) != port(ffB, \CLK))
+ reject;
+ if (param(ffA, \CLK_POLARITY) != param(ffB, \CLK_POLARITY))
+ reject;
+ }
+ endcode
+
+Similarly, the special keyword `accept` can be used to accept the current
+state. (`accept` will not backtrack. This means it continues with the current
+branch and may accept a larger match later.)
+
+The special keyword `branch` can be used to explore different cases. Note that
+each code block has an implicit `branch` at the end. So most use-cases of the
+`branch` keyword need to end the block with `reject` to avoid the implicit
+branch at the end. For example:
+
+ state <int> mode
+
+ code mode
+ for (mode = 0; mode < 8; mode++)
+ branch;
+ reject;
+ endcode
+
+But in some cases it is more natural to utilize the implicit branch statement:
+
+ state <IdString> portAB
+
+ code portAB
+ portAB = \A;
+ branch;
+ portAB = \B;
+ endcode
+
+There is an implicit `code..endcode` block at the end of each `.pgm` file
+that just accepts everything that gets all the way there.
diff --git a/passes/pmgen/ice40_dsp.cc b/passes/pmgen/ice40_dsp.cc
new file mode 100644
index 00000000..3a054a46
--- /dev/null
+++ b/passes/pmgen/ice40_dsp.cc
@@ -0,0 +1,237 @@
+/*
+ * 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 "passes/pmgen/ice40_dsp_pm.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+void create_ice40_dsp(ice40_dsp_pm &pm)
+{
+#if 0
+ log("\n");
+ log("ffA: %s\n", log_id(pm.st.ffA, "--"));
+ log("ffB: %s\n", log_id(pm.st.ffB, "--"));
+ log("mul: %s\n", log_id(pm.st.mul, "--"));
+ log("ffY: %s\n", log_id(pm.st.ffY, "--"));
+ log("addAB: %s\n", log_id(pm.st.addAB, "--"));
+ log("muxAB: %s\n", log_id(pm.st.muxAB, "--"));
+ log("ffS: %s\n", log_id(pm.st.ffS, "--"));
+#endif
+
+ log("Checking %s.%s for iCE40 DSP inference.\n", log_id(pm.module), log_id(pm.st.mul));
+
+ if (GetSize(pm.st.sigA) > 16) {
+ log(" input A (%s) is too large (%d > 16).\n", log_signal(pm.st.sigA), GetSize(pm.st.sigA));
+ return;
+ }
+
+ if (GetSize(pm.st.sigB) > 16) {
+ log(" input B (%s) is too large (%d > 16).\n", log_signal(pm.st.sigB), GetSize(pm.st.sigB));
+ return;
+ }
+
+ if (GetSize(pm.st.sigS) > 32) {
+ log(" accumulator (%s) is too large (%d > 32).\n", log_signal(pm.st.sigS), GetSize(pm.st.sigS));
+ return;
+ }
+
+ if (GetSize(pm.st.sigY) > 32) {
+ log(" output (%s) is too large (%d > 32).\n", log_signal(pm.st.sigY), GetSize(pm.st.sigY));
+ return;
+ }
+
+ bool mul_signed = pm.st.mul->getParam("\\A_SIGNED").as_bool();
+
+ if (mul_signed) {
+ log(" inference of signed iCE40 DSP arithmetic is currently not supported.\n");
+ return;
+ }
+
+ log(" replacing $mul with SB_MAC16 cell.\n");
+
+ Cell *cell = pm.module->addCell(NEW_ID, "\\SB_MAC16");
+ pm.module->swap_names(cell, pm.st.mul);
+
+ // SB_MAC16 Input Interface
+
+ SigSpec A = pm.st.sigA;
+ A.extend_u0(16, mul_signed);
+
+ SigSpec B = pm.st.sigB;
+ B.extend_u0(16, mul_signed);
+
+ SigSpec CD;
+ if (pm.st.muxA)
+ CD = pm.st.muxA->getPort("\\B");
+ if (pm.st.muxB)
+ CD = pm.st.muxB->getPort("\\A");
+ CD.extend_u0(32, mul_signed);
+
+ cell->setPort("\\A", A);
+ cell->setPort("\\B", B);
+ cell->setPort("\\C", CD.extract(0, 16));
+ cell->setPort("\\D", CD.extract(16, 16));
+
+ cell->setParam("\\A_REG", pm.st.ffA ? State::S1 : State::S0);
+ cell->setParam("\\B_REG", pm.st.ffB ? State::S1 : State::S0);
+
+ cell->setPort("\\AHOLD", State::S0);
+ cell->setPort("\\BHOLD", State::S0);
+ cell->setPort("\\CHOLD", State::S0);
+ cell->setPort("\\DHOLD", State::S0);
+
+ cell->setPort("\\IRSTTOP", State::S0);
+ cell->setPort("\\IRSTBOT", State::S0);
+
+ if (pm.st.clock_vld)
+ {
+ cell->setPort("\\CLK", pm.st.clock);
+ cell->setPort("\\CE", State::S1);
+ cell->setParam("\\NEG_TRIGGER", pm.st.clock_pol ? State::S0 : State::S1);
+
+ log(" clock: %s (%s)", log_signal(pm.st.clock), pm.st.clock_pol ? "posedge" : "negedge");
+
+ if (pm.st.ffA)
+ log(" ffA:%s", log_id(pm.st.ffA));
+
+ if (pm.st.ffB)
+ log(" ffB:%s", log_id(pm.st.ffB));
+
+ if (pm.st.ffY)
+ log(" ffY:%s", log_id(pm.st.ffY));
+
+ if (pm.st.ffS)
+ log(" ffS:%s", log_id(pm.st.ffS));
+
+ log("\n");
+ }
+ else
+ {
+ cell->setPort("\\CLK", State::S0);
+ cell->setPort("\\CE", State::S0);
+ cell->setParam("\\NEG_TRIGGER", State::S0);
+ }
+
+ // SB_MAC16 Cascade Interface
+
+ cell->setPort("\\SIGNEXTIN", State::Sx);
+ cell->setPort("\\SIGNEXTOUT", pm.module->addWire(NEW_ID));
+
+ cell->setPort("\\CI", State::Sx);
+ cell->setPort("\\CO", pm.module->addWire(NEW_ID));
+
+ cell->setPort("\\ACCUMCI", State::Sx);
+ cell->setPort("\\ACCUMCO", pm.module->addWire(NEW_ID));
+
+ // SB_MAC16 Output Interface
+
+ SigSpec O = pm.st.ffS ? pm.st.sigS : pm.st.sigY;
+ if (GetSize(O) < 32)
+ O.append(pm.module->addWire(NEW_ID, 32-GetSize(O)));
+
+ cell->setPort("\\O", O);
+
+ if (pm.st.addAB) {
+ log(" accumulator %s (%s)\n", log_id(pm.st.addAB), log_id(pm.st.addAB->type));
+ cell->setPort("\\ADDSUBTOP", pm.st.addAB->type == "$add" ? State::S0 : State::S1);
+ cell->setPort("\\ADDSUBBOT", pm.st.addAB->type == "$add" ? State::S0 : State::S1);
+ } else {
+ cell->setPort("\\ADDSUBTOP", State::S0);
+ cell->setPort("\\ADDSUBBOT", State::S0);
+ }
+
+ cell->setPort("\\ORSTTOP", State::S0);
+ cell->setPort("\\ORSTBOT", State::S0);
+
+ cell->setPort("\\OHOLDTOP", State::S0);
+ cell->setPort("\\OHOLDBOT", State::S0);
+
+ SigSpec acc_reset = State::S0;
+ if (pm.st.muxA)
+ acc_reset = pm.st.muxA->getPort("\\S");
+ if (pm.st.muxB)
+ acc_reset = pm.module->Not(NEW_ID, pm.st.muxB->getPort("\\S"));
+
+ cell->setPort("\\OLOADTOP", acc_reset);
+ cell->setPort("\\OLOADBOT", acc_reset);
+
+ // SB_MAC16 Remaining Parameters
+
+ cell->setParam("\\C_REG", State::S0);
+ cell->setParam("\\D_REG", State::S0);
+
+ cell->setParam("\\TOP_8x8_MULT_REG", pm.st.ffY ? State::S1 : State::S0);
+ cell->setParam("\\BOT_8x8_MULT_REG", pm.st.ffY ? State::S1 : State::S0);
+ cell->setParam("\\PIPELINE_16x16_MULT_REG1", pm.st.ffY ? State::S1 : State::S0);
+ cell->setParam("\\PIPELINE_16x16_MULT_REG2", State::S0);
+
+ cell->setParam("\\TOPOUTPUT_SELECT", Const(pm.st.ffS ? 1 : 3, 2));
+ cell->setParam("\\TOPADDSUB_LOWERINPUT", Const(2, 2));
+ cell->setParam("\\TOPADDSUB_UPPERINPUT", State::S0);
+ cell->setParam("\\TOPADDSUB_CARRYSELECT", Const(3, 2));
+
+ cell->setParam("\\BOTOUTPUT_SELECT", Const(pm.st.ffS ? 1 : 3, 2));
+ cell->setParam("\\BOTADDSUB_LOWERINPUT", Const(2, 2));
+ cell->setParam("\\BOTADDSUB_UPPERINPUT", State::S0);
+ cell->setParam("\\BOTADDSUB_CARRYSELECT", Const(0, 2));
+
+ cell->setParam("\\MODE_8x8", State::S0);
+ cell->setParam("\\A_SIGNED", mul_signed ? State::S1 : State::S0);
+ cell->setParam("\\B_SIGNED", mul_signed ? State::S1 : State::S0);
+
+ pm.autoremove(pm.st.mul);
+ pm.autoremove(pm.st.ffY);
+ pm.autoremove(pm.st.ffS);
+}
+
+struct Ice40DspPass : public Pass {
+ Ice40DspPass() : Pass("ice40_dsp", "iCE40: map multipliers") { }
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" ice40_dsp [options] [selection]\n");
+ log("\n");
+ log("Map multipliers and multiply-accumulate blocks to iCE40 DSP resources.\n");
+ log("\n");
+ }
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ log_header(design, "Executing ICE40_DSP pass (map multipliers).\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ // if (args[argidx] == "-singleton") {
+ // singleton_mode = true;
+ // continue;
+ // }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto module : design->selected_modules())
+ ice40_dsp_pm(module, module->selected_cells()).run(create_ice40_dsp);
+ }
+} Ice40DspPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/pmgen/ice40_dsp.pmg b/passes/pmgen/ice40_dsp.pmg
new file mode 100644
index 00000000..96c62e31
--- /dev/null
+++ b/passes/pmgen/ice40_dsp.pmg
@@ -0,0 +1,160 @@
+state <SigBit> clock
+state <bool> clock_pol clock_vld
+state <SigSpec> sigA sigB sigY sigS
+state <Cell*> addAB muxAB
+
+match mul
+ select mul->type.in($mul)
+ select GetSize(mul->getPort(\A)) + GetSize(mul->getPort(\B)) > 10
+ select GetSize(mul->getPort(\Y)) > 10
+endmatch
+
+match ffA
+ select ffA->type.in($dff)
+ // select nusers(port(ffA, \Q)) == 2
+ index <SigSpec> port(ffA, \Q) === port(mul, \A)
+ optional
+endmatch
+
+code sigA clock clock_pol clock_vld
+ sigA = port(mul, \A);
+
+ if (ffA) {
+ sigA = port(ffA, \D);
+
+ clock = port(ffA, \CLK).as_bit();
+ clock_pol = param(ffA, \CLK_POLARITY).as_bool();
+ clock_vld = true;
+ }
+endcode
+
+match ffB
+ select ffB->type.in($dff)
+ // select nusers(port(ffB, \Q)) == 2
+ index <SigSpec> port(ffB, \Q) === port(mul, \B)
+ optional
+endmatch
+
+code sigB clock clock_pol clock_vld
+ sigB = port(mul, \B);
+
+ if (ffB) {
+ sigB = port(ffB, \D);
+ SigBit c = port(ffB, \CLK).as_bit();
+ bool cp = param(ffB, \CLK_POLARITY).as_bool();
+
+ if (clock_vld && (c != clock || cp != clock_pol))
+ reject;
+
+ clock = c;
+ clock_pol = cp;
+ clock_vld = true;
+ }
+endcode
+
+match ffY
+ select ffY->type.in($dff)
+ select nusers(port(ffY, \D)) == 2
+ index <SigSpec> port(ffY, \D) === port(mul, \Y)
+ optional
+endmatch
+
+code sigY clock clock_pol clock_vld
+ sigY = port(mul, \Y);
+
+ if (ffY) {
+ sigY = port(ffY, \Q);
+ SigBit c = port(ffY, \CLK).as_bit();
+ bool cp = param(ffY, \CLK_POLARITY).as_bool();
+
+ if (clock_vld && (c != clock || cp != clock_pol))
+ reject;
+
+ clock = c;
+ clock_pol = cp;
+ clock_vld = true;
+ }
+endcode
+
+match addA
+ select addA->type.in($add)
+ select nusers(port(addA, \A)) == 2
+ index <SigSpec> port(addA, \A) === sigY
+ optional
+endmatch
+
+match addB
+ if !addA
+ select addB->type.in($add, $sub)
+ select nusers(port(addB, \B)) == 2
+ index <SigSpec> port(addB, \B) === sigY
+ optional
+endmatch
+
+code addAB sigS
+ if (addA) {
+ addAB = addA;
+ sigS = port(addA, \B);
+ }
+ if (addB) {
+ addAB = addB;
+ sigS = port(addB, \A);
+ }
+ if (addAB) {
+ int natural_mul_width = GetSize(sigA) + GetSize(sigB);
+ int actual_mul_width = GetSize(sigY);
+ int actual_acc_width = GetSize(sigS);
+
+ if ((actual_acc_width > actual_mul_width) && (natural_mul_width > actual_mul_width))
+ reject;
+ if ((actual_acc_width != actual_mul_width) && (param(mul, \A_SIGNED).as_bool() != param(addAB, \A_SIGNED).as_bool()))
+ reject;
+ }
+endcode
+
+match muxA
+ if addAB
+ select muxA->type.in($mux)
+ select nusers(port(muxA, \A)) == 2
+ index <SigSpec> port(muxA, \A) === port(addAB, \Y)
+ optional
+endmatch
+
+match muxB
+ if addAB
+ if !muxA
+ select muxB->type.in($mux)
+ select nusers(port(muxB, \B)) == 2
+ index <SigSpec> port(muxB, \B) === port(addAB, \Y)
+ optional
+endmatch
+
+code muxAB
+ muxAB = addAB;
+ if (muxA)
+ muxAB = muxA;
+ if (muxB)
+ muxAB = muxB;
+endcode
+
+match ffS
+ if muxAB
+ select ffS->type.in($dff)
+ select nusers(port(ffS, \D)) == 2
+ index <SigSpec> port(ffS, \D) === port(muxAB, \Y)
+ index <SigSpec> port(ffS, \Q) === sigS
+endmatch
+
+code clock clock_pol clock_vld
+ if (ffS) {
+ SigBit c = port(ffS, \CLK).as_bit();
+ bool cp = param(ffS, \CLK_POLARITY).as_bool();
+
+ if (clock_vld && (c != clock || cp != clock_pol))
+ reject;
+
+ clock = c;
+ clock_pol = cp;
+ clock_vld = true;
+ }
+endcode
diff --git a/passes/pmgen/pmgen.py b/passes/pmgen/pmgen.py
new file mode 100644
index 00000000..d9747b06
--- /dev/null
+++ b/passes/pmgen/pmgen.py
@@ -0,0 +1,486 @@
+#!/usr/bin/env python3
+
+import re
+import sys
+import pprint
+
+pp = pprint.PrettyPrinter(indent=4)
+
+pmgfile = sys.argv[1]
+assert pmgfile.endswith(".pmg")
+prefix = pmgfile[0:-4]
+prefix = prefix.split('/')[-1]
+outfile = sys.argv[2]
+
+state_types = dict()
+udata_types = dict()
+blocks = list()
+ids = dict()
+
+def rewrite_cpp(s):
+ t = list()
+ i = 0
+ while i < len(s):
+ if s[i] in ("'", '"') and i + 1 < len(s):
+ j = i + 1
+ while j + 1 < len(s) and s[j] != s[i]:
+ if s[j] == '\\' and j + 1 < len(s):
+ j += 1
+ j += 1
+ t.append(s[i:j+1])
+ i = j + 1
+ continue
+
+ if s[i] in ('$', '\\') and i + 1 < len(s):
+ j = i + 1
+ while True:
+ if j == len(s):
+ j -= 1
+ break
+ if ord('a') <= ord(s[j]) <= ord('z'):
+ j += 1
+ continue
+ if ord('A') <= ord(s[j]) <= ord('Z'):
+ j += 1
+ continue
+ if ord('0') <= ord(s[j]) <= ord('9'):
+ j += 1
+ continue
+ if s[j] == '_':
+ j += 1
+ continue
+ j -= 1
+ break
+
+ n = s[i:j+1]
+ i = j + 1
+
+ if n[0] == '$':
+ v = "id_d_" + n[1:]
+ else:
+ v = "id_b_" + n[1:]
+
+ if v not in ids:
+ ids[v] = n
+ else:
+ assert ids[v] == n
+
+ t.append(v)
+ continue
+
+ if s[i] == "\t":
+ t.append(" ")
+ else:
+ t.append(s[i])
+
+ i += 1
+
+ return "".join(t)
+
+with open(pmgfile, "r") as f:
+ while True:
+ line = f.readline()
+ if line == "": break
+ line = line.strip()
+
+ cmd = line.split()
+ if len(cmd) == 0 or cmd[0].startswith("//"): continue
+ cmd = cmd[0]
+
+ if cmd == "state":
+ m = re.match(r"^state\s+<(.*?)>\s+(([A-Za-z_][A-Za-z_0-9]*\s+)*[A-Za-z_][A-Za-z_0-9]*)\s*$", line)
+ assert m
+ type_str = m.group(1)
+ states_str = m.group(2)
+ for s in re.split(r"\s+", states_str):
+ assert s not in state_types
+ state_types[s] = type_str
+ continue
+
+ if cmd == "udata":
+ m = re.match(r"^udata\s+<(.*?)>\s+(([A-Za-z_][A-Za-z_0-9]*\s+)*[A-Za-z_][A-Za-z_0-9]*)\s*$", line)
+ assert m
+ type_str = m.group(1)
+ udatas_str = m.group(2)
+ for s in re.split(r"\s+", udatas_str):
+ assert s not in udata_types
+ udata_types[s] = type_str
+ continue
+
+ if cmd == "match":
+ block = dict()
+ block["type"] = "match"
+
+ line = line.split()
+ assert len(line) == 2
+ assert line[1] not in state_types
+ block["cell"] = line[1]
+ state_types[line[1]] = "Cell*";
+
+ block["if"] = list()
+ block["select"] = list()
+ block["index"] = list()
+ block["filter"] = list()
+ block["optional"] = False
+
+ while True:
+ l = f.readline()
+ assert l != ""
+ a = l.split()
+ if len(a) == 0 or a[0].startswith("//"): continue
+ if a[0] == "endmatch": break
+
+ if a[0] == "if":
+ b = l.lstrip()[2:]
+ block["if"].append(rewrite_cpp(b.strip()))
+ continue
+
+ if a[0] == "select":
+ b = l.lstrip()[6:]
+ block["select"].append(rewrite_cpp(b.strip()))
+ continue
+
+ if a[0] == "index":
+ m = re.match(r"^\s*index\s+<(.*?)>\s+(.*?)\s*===\s*(.*?)\s*$", l)
+ assert m
+ block["index"].append((m.group(1), rewrite_cpp(m.group(2)), rewrite_cpp(m.group(3))))
+ continue
+
+ if a[0] == "filter":
+ b = l.lstrip()[6:]
+ block["filter"].append(rewrite_cpp(b.strip()))
+ continue
+
+ if a[0] == "optional":
+ block["optional"] = True
+ continue
+
+ assert False
+
+ blocks.append(block)
+
+ if cmd == "code":
+ block = dict()
+ block["type"] = "code"
+ block["code"] = list()
+ block["states"] = set()
+
+ for s in line.split()[1:]:
+ assert s in state_types
+ block["states"].add(s)
+
+ while True:
+ l = f.readline()
+ assert l != ""
+ a = l.split()
+ if len(a) == 0: continue
+ if a[0] == "endcode": break
+
+ block["code"].append(rewrite_cpp(l.rstrip()))
+
+ blocks.append(block)
+
+with open(outfile, "w") as f:
+ print("// Generated by pmgen.py from {}.pgm".format(prefix), file=f)
+ print("", file=f)
+
+ print("#include \"kernel/yosys.h\"", file=f)
+ print("#include \"kernel/sigtools.h\"", file=f)
+ print("", file=f)
+
+ print("YOSYS_NAMESPACE_BEGIN", file=f)
+ print("", file=f)
+
+ print("struct {}_pm {{".format(prefix), file=f)
+ print(" Module *module;", file=f)
+ print(" SigMap sigmap;", file=f)
+ print(" std::function<void()> on_accept;".format(prefix), file=f)
+ print("", file=f)
+
+ for index in range(len(blocks)):
+ block = blocks[index]
+ if block["type"] == "match":
+ index_types = list()
+ for entry in block["index"]:
+ index_types.append(entry[0])
+ print(" typedef std::tuple<{}> index_{}_key_type;".format(", ".join(index_types), index), file=f)
+ print(" dict<index_{}_key_type, vector<Cell*>> index_{};".format(index, index), file=f)
+ print(" dict<SigBit, pool<Cell*>> sigusers;", file=f)
+ print(" pool<Cell*> blacklist_cells;", file=f)
+ print(" pool<Cell*> autoremove_cells;", file=f)
+ print(" bool blacklist_dirty;", file=f)
+ print(" int rollback;", file=f)
+ print("", file=f)
+
+ print(" struct state_t {", file=f)
+ for s, t in sorted(state_types.items()):
+ print(" {} {};".format(t, s), file=f)
+ print(" } st;", file=f)
+ print("", file=f)
+
+ print(" struct udata_t {", file=f)
+ for s, t in sorted(udata_types.items()):
+ print(" {} {};".format(t, s), file=f)
+ print(" } ud;", file=f)
+ print("", file=f)
+
+ for v, n in sorted(ids.items()):
+ if n[0] == "\\":
+ print(" IdString {}{{\"\\{}\"}};".format(v, n), file=f)
+ else:
+ print(" IdString {}{{\"{}\"}};".format(v, n), file=f)
+ print("", file=f)
+
+ print(" void add_siguser(const SigSpec &sig, Cell *cell) {", file=f)
+ print(" for (auto bit : sigmap(sig)) {", file=f)
+ print(" if (bit.wire == nullptr) continue;", file=f)
+ print(" if (sigusers.count(bit) == 0 && bit.wire->port_id)", file=f)
+ print(" sigusers[bit].insert(nullptr);", file=f)
+ print(" sigusers[bit].insert(cell);", file=f)
+ print(" }", file=f)
+ print(" }", file=f)
+ print("", file=f)
+
+ print(" void blacklist(Cell *cell) {", file=f)
+ print(" if (cell != nullptr) {", file=f)
+ print(" if (blacklist_cells.insert(cell).second)", file=f)
+ print(" blacklist_dirty = true;", file=f)
+ print(" }", file=f)
+ print(" }", file=f)
+ print("", file=f)
+
+ print(" void autoremove(Cell *cell) {", file=f)
+ print(" if (cell != nullptr) {", file=f)
+ print(" if (blacklist_cells.insert(cell).second)", file=f)
+ print(" blacklist_dirty = true;", file=f)
+ print(" autoremove_cells.insert(cell);", file=f)
+ print(" }", file=f)
+ print(" }", file=f)
+ print("", file=f)
+
+ print(" void check_blacklist() {", file=f)
+ print(" if (!blacklist_dirty)", file=f)
+ print(" return;", file=f)
+ print(" blacklist_dirty = false;", file=f)
+ for index in range(len(blocks)):
+ block = blocks[index]
+ if block["type"] == "match":
+ print(" if (st.{} != nullptr && blacklist_cells.count(st.{})) {{".format(block["cell"], block["cell"]), file=f)
+ print(" rollback = {};".format(index+1), file=f)
+ print(" return;", file=f)
+ print(" }", file=f)
+ print(" rollback = 0;", file=f)
+ print(" }", file=f)
+ print("", file=f)
+
+ print(" SigSpec port(Cell *cell, IdString portname) {", file=f)
+ print(" return sigmap(cell->getPort(portname));", file=f)
+ print(" }", file=f)
+ print("", file=f)
+
+ print(" Const param(Cell *cell, IdString paramname) {", file=f)
+ print(" return cell->getParam(paramname);", file=f)
+ print(" }", file=f)
+ print("", file=f)
+
+ print(" int nusers(const SigSpec &sig) {", file=f)
+ print(" pool<Cell*> users;", file=f)
+ print(" for (auto bit : sigmap(sig))", file=f)
+ print(" for (auto user : sigusers[bit])", file=f)
+ print(" users.insert(user);", file=f)
+ print(" return GetSize(users);", file=f)
+ print(" }", file=f)
+ print("", file=f)
+
+ print(" {}_pm(Module *module, const vector<Cell*> &cells) :".format(prefix), file=f)
+ print(" module(module), sigmap(module) {", file=f)
+ for s, t in sorted(udata_types.items()):
+ if t.endswith("*"):
+ print(" ud.{} = nullptr;".format(s), file=f)
+ else:
+ print(" ud.{} = {}();".format(s, t), file=f)
+ print(" for (auto cell : module->cells()) {", file=f)
+ print(" for (auto &conn : cell->connections())", file=f)
+ print(" add_siguser(conn.second, cell);", file=f)
+ print(" }", file=f)
+ print(" for (auto cell : cells) {", file=f)
+
+ for index in range(len(blocks)):
+ block = blocks[index]
+ if block["type"] == "match":
+ print(" do {", file=f)
+ print(" Cell *{} = cell;".format(block["cell"]), file=f)
+ for expr in block["select"]:
+ print(" if (!({})) break;".format(expr), file=f)
+ print(" index_{}_key_type key;".format(index), file=f)
+ for field, entry in enumerate(block["index"]):
+ print(" std::get<{}>(key) = {};".format(field, entry[1]), file=f)
+ print(" index_{}[key].push_back(cell);".format(index), file=f)
+ print(" } while (0);", file=f)
+
+ print(" }", file=f)
+ print(" }", file=f)
+ print("", file=f)
+
+ print(" ~{}_pm() {{".format(prefix), file=f)
+ print(" for (auto cell : autoremove_cells)", file=f)
+ print(" module->remove(cell);", file=f)
+ print(" }", file=f)
+ print("", file=f)
+
+ print(" void run(std::function<void()> on_accept_f) {", file=f)
+ print(" on_accept = on_accept_f;", file=f)
+ print(" rollback = 0;", file=f)
+ print(" blacklist_dirty = false;", file=f)
+ for s, t in sorted(state_types.items()):
+ if t.endswith("*"):
+ print(" st.{} = nullptr;".format(s), file=f)
+ else:
+ print(" st.{} = {}();".format(s, t), file=f)
+ print(" block_0();", file=f)
+ print(" }", file=f)
+ print("", file=f)
+
+ print(" void run(std::function<void({}_pm&)> on_accept_f) {{".format(prefix), file=f)
+ print(" run([&](){on_accept_f(*this);});", file=f)
+ print(" }", file=f)
+ print("", file=f)
+
+ for index in range(len(blocks)):
+ block = blocks[index]
+
+ print(" void block_{}() {{".format(index), file=f)
+
+ const_st = set()
+ nonconst_st = set()
+ restore_st = set()
+
+ for i in range(index):
+ if blocks[i]["type"] == "code":
+ for s in blocks[i]["states"]:
+ const_st.add(s)
+ elif blocks[i]["type"] == "match":
+ const_st.add(blocks[i]["cell"])
+ else:
+ assert False
+
+ if block["type"] == "code":
+ for s in block["states"]:
+ if s in const_st:
+ const_st.remove(s)
+ restore_st.add(s)
+ nonconst_st.add(s)
+ elif block["type"] == "match":
+ s = block["cell"]
+ assert s not in const_st
+ nonconst_st.add(s)
+ else:
+ assert False
+
+ for s in sorted(const_st):
+ t = state_types[s]
+ if t.endswith("*"):
+ print(" {} const &{} YS_ATTRIBUTE(unused) = st.{};".format(t, s, s), file=f)
+ else:
+ print(" const {} &{} YS_ATTRIBUTE(unused) = st.{};".format(t, s, s), file=f)
+
+ for s in sorted(nonconst_st):
+ t = state_types[s]
+ print(" {} &{} YS_ATTRIBUTE(unused) = st.{};".format(t, s, s), file=f)
+
+ if len(restore_st):
+ print("", file=f)
+ for s in sorted(restore_st):
+ t = state_types[s]
+ print(" {} backup_{} = {};".format(t, s, s), file=f)
+
+ if block["type"] == "code":
+ print("", file=f)
+ print(" do {", file=f)
+ print("#define reject do { check_blacklist(); goto rollback_label; } while(0)", file=f)
+ print("#define accept do { on_accept(); check_blacklist(); if (rollback) goto rollback_label; } while(0)", file=f)
+ print("#define branch do {{ block_{}(); if (rollback) goto rollback_label; }} while(0)".format(index+1), file=f)
+
+ for line in block["code"]:
+ print(" " + line, file=f)
+
+ print("", file=f)
+ print(" block_{}();".format(index+1), file=f)
+ print("#undef reject", file=f)
+ print("#undef accept", file=f)
+ print("#undef branch", file=f)
+ print(" } while (0);", file=f)
+ print("", file=f)
+ print("rollback_label:", file=f)
+ print(" YS_ATTRIBUTE(unused);", file=f)
+
+ if len(restore_st) or len(nonconst_st):
+ print("", file=f)
+ for s in sorted(restore_st):
+ t = state_types[s]
+ print(" {} = backup_{};".format(s, s), file=f)
+ for s in sorted(nonconst_st):
+ if s not in restore_st:
+ t = state_types[s]
+ if t.endswith("*"):
+ print(" {} = nullptr;".format(s), file=f)
+ else:
+ print(" {} = {}();".format(s, t), file=f)
+
+ elif block["type"] == "match":
+ assert len(restore_st) == 0
+
+ if len(block["if"]):
+ for expr in block["if"]:
+ print("", file=f)
+ print(" if (!({})) {{".format(expr), file=f)
+ print(" {} = nullptr;".format(block["cell"]), file=f)
+ print(" block_{}();".format(index+1), file=f)
+ print(" return;", file=f)
+ print(" }", file=f)
+
+ print("", file=f)
+ print(" index_{}_key_type key;".format(index), file=f)
+ for field, entry in enumerate(block["index"]):
+ print(" std::get<{}>(key) = {};".format(field, entry[2]), file=f)
+ print(" const vector<Cell*> &cells = index_{}[key];".format(index), file=f)
+
+ print("", file=f)
+ print(" for (int idx = 0; idx < GetSize(cells); idx++) {", file=f)
+ print(" {} = cells[idx];".format(block["cell"]), file=f)
+ print(" if (blacklist_cells.count({})) continue;".format(block["cell"]), file=f)
+ for expr in block["filter"]:
+ print(" if (!({})) continue;".format(expr), file=f)
+ print(" block_{}();".format(index+1), file=f)
+ print(" if (rollback) {", file=f)
+ print(" if (rollback != {}) {{".format(index+1), file=f)
+ print(" {} = nullptr;".format(block["cell"]), file=f)
+ print(" return;", file=f)
+ print(" }", file=f)
+ print(" rollback = 0;", file=f)
+ print(" }", file=f)
+ print(" }", file=f)
+
+ print("", file=f)
+ print(" {} = nullptr;".format(block["cell"]), file=f)
+
+ if block["optional"]:
+ print(" block_{}();".format(index+1), file=f)
+
+ else:
+ assert False
+
+
+ print(" }", file=f)
+ print("", file=f)
+
+ print(" void block_{}() {{".format(len(blocks)), file=f)
+ print(" on_accept();", file=f)
+ print(" check_blacklist();", file=f)
+ print(" }", file=f)
+ print("};", file=f)
+
+ print("", file=f)
+ print("YOSYS_NAMESPACE_END", file=f)
+
+# pp.pprint(blocks)
diff --git a/passes/proc/proc_clean.cc b/passes/proc/proc_clean.cc
index b9e43d1d..52141a8e 100644
--- a/passes/proc/proc_clean.cc
+++ b/passes/proc/proc_clean.cc
@@ -77,18 +77,42 @@ void proc_clean_switch(RTLIL::SwitchRule *sw, RTLIL::CaseRule *parent, bool &did
}
else
{
- bool all_cases_are_empty = true;
- for (auto cs : sw->cases) {
- if (cs->actions.size() != 0 || cs->switches.size() != 0)
- all_cases_are_empty = false;
+ bool all_fully_def = true;
+ for (auto cs : sw->cases)
+ {
if (max_depth != 0)
proc_clean_case(cs, did_something, count, max_depth-1);
+ int size = 0;
+ for (auto cmp : cs->compare)
+ {
+ size += cmp.size();
+ if (!cmp.is_fully_def())
+ all_fully_def = false;
+ }
+ if (sw->signal.size() != size)
+ all_fully_def = false;
}
- if (all_cases_are_empty) {
- did_something = true;
- for (auto cs : sw->cases)
- delete cs;
- sw->cases.clear();
+ if (all_fully_def)
+ {
+ for (auto cs = sw->cases.begin(); cs != sw->cases.end();)
+ {
+ if ((*cs)->empty())
+ {
+ did_something = true;
+ delete *cs;
+ cs = sw->cases.erase(cs);
+ }
+ else ++cs;
+ }
+ }
+ else
+ {
+ while (!sw->cases.empty() && sw->cases.back()->empty())
+ {
+ did_something = true;
+ delete sw->cases.back();
+ sw->cases.pop_back();
+ }
}
}
}
@@ -106,7 +130,7 @@ void proc_clean_case(RTLIL::CaseRule *cs, bool &did_something, int &count, int m
}
for (size_t i = 0; i < cs->switches.size(); i++) {
RTLIL::SwitchRule *sw = cs->switches[i];
- if (sw->cases.size() == 0) {
+ if (sw->empty()) {
cs->switches.erase(cs->switches.begin() + (i--));
did_something = true;
delete sw;
diff --git a/passes/sat/Makefile.inc b/passes/sat/Makefile.inc
index 8ab0280c..fc3ac879 100644
--- a/passes/sat/Makefile.inc
+++ b/passes/sat/Makefile.inc
@@ -8,4 +8,8 @@ OBJS += passes/sat/expose.o
OBJS += passes/sat/assertpmux.o
OBJS += passes/sat/clk2fflogic.o
OBJS += passes/sat/async2sync.o
+OBJS += passes/sat/supercover.o
+OBJS += passes/sat/fmcombine.o
+OBJS += passes/sat/mutate.o
+OBJS += passes/sat/cutpoint.o
diff --git a/passes/sat/async2sync.cc b/passes/sat/async2sync.cc
index c92db711..d045d0dc 100644
--- a/passes/sat/async2sync.cc
+++ b/passes/sat/async2sync.cc
@@ -39,7 +39,7 @@ struct Async2syncPass : public Pass {
log("reset value in the next cycle regardless of the data-in value at the time of\n");
log("the clock edge.\n");
log("\n");
- log("Currently only $adff cells are supported by this pass.\n");
+ log("Currently only $adff and $dffsr cells are supported by this pass.\n");
log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
@@ -84,7 +84,7 @@ struct Async2syncPass : public Pass {
bool arst_pol = cell->parameters["\\ARST_POLARITY"].as_bool();
Const arst_val = cell->parameters["\\ARST_VALUE"];
- SigSpec sig_clk = cell->getPort("\\CLK");
+ // SigSpec sig_clk = cell->getPort("\\CLK");
SigSpec sig_arst = cell->getPort("\\ARST");
SigSpec sig_d = cell->getPort("\\D");
SigSpec sig_q = cell->getPort("\\Q");
@@ -120,6 +120,55 @@ struct Async2syncPass : public Pass {
cell->type = "$dff";
continue;
}
+
+ if (cell->type.in("$dffsr"))
+ {
+ // bool clk_pol = cell->parameters["\\CLK_POLARITY"].as_bool();
+ bool set_pol = cell->parameters["\\SET_POLARITY"].as_bool();
+ bool clr_pol = cell->parameters["\\CLR_POLARITY"].as_bool();
+
+ // SigSpec sig_clk = cell->getPort("\\CLK");
+ SigSpec sig_set = cell->getPort("\\SET");
+ SigSpec sig_clr = cell->getPort("\\CLR");
+ SigSpec sig_d = cell->getPort("\\D");
+ SigSpec sig_q = cell->getPort("\\Q");
+
+ log("Replacing %s.%s (%s): SET=%s, CLR=%s, D=%s, Q=%s\n",
+ log_id(module), log_id(cell), log_id(cell->type),
+ log_signal(sig_set), log_signal(sig_clr), log_signal(sig_d), log_signal(sig_q));
+
+ Const init_val;
+ for (int i = 0; i < GetSize(sig_q); i++) {
+ SigBit bit = sigmap(sig_q[i]);
+ init_val.bits.push_back(initbits.count(bit) ? initbits.at(bit) : State::Sx);
+ del_initbits.insert(bit);
+ }
+
+ Wire *new_d = module->addWire(NEW_ID, GetSize(sig_d));
+ Wire *new_q = module->addWire(NEW_ID, GetSize(sig_q));
+ new_q->attributes["\\init"] = init_val;
+
+ if (!set_pol)
+ sig_set = module->Not(NEW_ID, sig_set);
+
+ if (clr_pol)
+ sig_clr = module->Not(NEW_ID, sig_clr);
+
+ SigSpec tmp = module->Or(NEW_ID, sig_d, sig_set);
+ module->addAnd(NEW_ID, tmp, sig_clr, new_d);
+
+ tmp = module->Or(NEW_ID, new_q, sig_set);
+ module->addAnd(NEW_ID, tmp, sig_clr, sig_q);
+
+ cell->setPort("\\D", new_d);
+ cell->setPort("\\Q", new_q);
+ cell->unsetPort("\\SET");
+ cell->unsetPort("\\CLR");
+ cell->unsetParam("\\SET_POLARITY");
+ cell->unsetParam("\\CLR_POLARITY");
+ cell->type = "$dff";
+ continue;
+ }
}
for (auto wire : module->wires())
diff --git a/passes/sat/cutpoint.cc b/passes/sat/cutpoint.cc
new file mode 100644
index 00000000..048aec7f
--- /dev/null
+++ b/passes/sat/cutpoint.cc
@@ -0,0 +1,168 @@
+/*
+ * 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 CutpointPass : public Pass {
+ CutpointPass() : Pass("cutpoint", "add hi/lo cover cells for each wire bit") { }
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" cutpoint [options] [selection]\n");
+ log("\n");
+ log("This command adds formal cut points to the design.\n");
+ log("\n");
+ log(" -undef\n");
+ log(" set cupoint nets to undef (x). the default behavior is to create a\n");
+ log(" $anyseq cell and drive the cutpoint net from that\n");
+ log("\n");
+ }
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ bool flag_undef = false;
+
+ log_header(design, "Executing CUTPOINT pass.\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ if (args[argidx] == "-undef") {
+ flag_undef = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto module : design->selected_modules())
+ {
+ if (design->selected_whole_module(module->name)) {
+ log("Making all outputs of module %s cut points, removing module contents.\n", log_id(module));
+ module->new_connections(std::vector<RTLIL::SigSig>());
+ for (auto cell : vector<Cell*>(module->cells()))
+ module->remove(cell);
+ vector<Wire*> output_wires;
+ for (auto wire : module->wires())
+ if (wire->port_output)
+ output_wires.push_back(wire);
+ for (auto wire : output_wires)
+ module->connect(wire, flag_undef ? Const(State::Sx, GetSize(wire)) : module->Anyseq(NEW_ID, GetSize(wire)));
+ continue;
+ }
+
+ SigMap sigmap(module);
+ pool<SigBit> cutpoint_bits;
+
+ for (auto cell : module->selected_cells()) {
+ if (cell->type == "$anyseq")
+ continue;
+ log("Removing cell %s.%s, making all cell outputs cutpoints.\n", log_id(module), log_id(cell));
+ for (auto &conn : cell->connections()) {
+ if (cell->output(conn.first))
+ module->connect(conn.second, flag_undef ? Const(State::Sx, GetSize(conn.second)) : module->Anyseq(NEW_ID, GetSize(conn.second)));
+ }
+ module->remove(cell);
+ }
+
+ for (auto wire : module->selected_wires()) {
+ if (wire->port_output) {
+ log("Making output wire %s.%s a cutpoint.\n", log_id(module), log_id(wire));
+ Wire *new_wire = module->addWire(NEW_ID, wire);
+ module->swap_names(wire, new_wire);
+ module->connect(new_wire, flag_undef ? Const(State::Sx, GetSize(new_wire)) : module->Anyseq(NEW_ID, GetSize(new_wire)));
+ wire->port_id = 0;
+ wire->port_input = false;
+ wire->port_output = false;
+ continue;
+ }
+ log("Making wire %s.%s a cutpoint.\n", log_id(module), log_id(wire));
+ for (auto bit : sigmap(wire))
+ cutpoint_bits.insert(bit);
+ }
+
+ if (!cutpoint_bits.empty())
+ {
+ for (auto cell : module->cells()) {
+ for (auto &conn : cell->connections()) {
+ if (!cell->output(conn.first))
+ continue;
+ SigSpec sig = sigmap(conn.second);
+ int bit_count = 0;
+ for (auto &bit : sig) {
+ if (cutpoint_bits.count(bit))
+ bit_count++;
+ }
+ if (bit_count == 0)
+ continue;
+ SigSpec dummy = module->addWire(NEW_ID, bit_count);
+ bit_count = 0;
+ for (auto &bit : sig) {
+ if (cutpoint_bits.count(bit))
+ bit = dummy[bit_count++];
+ }
+ cell->setPort(conn.first, sig);
+ }
+ }
+
+ vector<Wire*> rewrite_wires;
+ for (auto wire : module->wires()) {
+ if (!wire->port_input)
+ continue;
+ int bit_count = 0;
+ for (auto &bit : sigmap(wire))
+ if (cutpoint_bits.count(bit))
+ bit_count++;
+ if (bit_count)
+ rewrite_wires.push_back(wire);
+ }
+
+ for (auto wire : rewrite_wires) {
+ Wire *new_wire = module->addWire(NEW_ID, wire);
+ SigSpec lhs, rhs, sig = sigmap(wire);
+ for (int i = 0; i < GetSize(sig); i++)
+ if (!cutpoint_bits.count(sig[i])) {
+ lhs.append(SigBit(wire, i));
+ rhs.append(SigBit(new_wire, i));
+ }
+ if (GetSize(lhs))
+ module->connect(lhs, rhs);
+ module->swap_names(wire, new_wire);
+ wire->port_id = 0;
+ wire->port_input = false;
+ wire->port_output = false;
+ }
+
+ SigSpec sig(cutpoint_bits);
+ sig.sort_and_unify();
+
+ for (auto chunk : sig.chunks()) {
+ SigSpec s(chunk);
+ module->connect(s, flag_undef ? Const(State::Sx, GetSize(s)) : module->Anyseq(NEW_ID, GetSize(s)));
+ }
+ }
+ }
+ }
+} CutpointPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/sat/fmcombine.cc b/passes/sat/fmcombine.cc
new file mode 100644
index 00000000..cd75ca86
--- /dev/null
+++ b/passes/sat/fmcombine.cc
@@ -0,0 +1,341 @@
+/*
+ * 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"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct opts_t
+{
+ bool fwd = false;
+ bool bwd = false;
+ bool nop = false;
+};
+
+struct FmcombineWorker
+{
+ const opts_t &opts;
+ Design *design;
+ Module *original = nullptr;
+ Module *module = nullptr;
+ IdString orig_type, combined_type;
+
+ FmcombineWorker(Design *design, IdString orig_type, const opts_t &opts) :
+ opts(opts), design(design), original(design->module(orig_type)),
+ orig_type(orig_type), combined_type("$fmcombine" + orig_type.str())
+ {
+ }
+
+ SigSpec import_sig(SigSpec sig, const string &suffix)
+ {
+ SigSpec newsig;
+ for (auto chunk : sig.chunks()) {
+ if (chunk.wire != nullptr)
+ chunk.wire = module->wire(chunk.wire->name.str() + suffix);
+ newsig.append(chunk);
+ }
+ return newsig;
+ }
+
+ void import_prim_cell(Cell *cell, const string &suffix)
+ {
+ Cell *c = module->addCell(cell->name.str() + suffix, cell->type);
+ c->parameters = cell->parameters;
+ c->attributes = cell->attributes;
+
+ for (auto &conn : cell->connections())
+ c->setPort(conn.first, import_sig(conn.second, suffix));
+ }
+
+ void import_hier_cell(Cell *cell)
+ {
+ if (!cell->parameters.empty())
+ log_cmd_error("Cell %s.%s has unresolved instance parameters.\n", log_id(original), log_id(cell));
+
+ FmcombineWorker sub_worker(design, cell->type, opts);
+ sub_worker.generate();
+
+ Cell *c = module->addCell(cell->name.str() + "_combined", sub_worker.combined_type);
+ // c->parameters = cell->parameters;
+ c->attributes = cell->attributes;
+
+ for (auto &conn : cell->connections()) {
+ c->setPort(conn.first.str() + "_gold", import_sig(conn.second, "_gold"));
+ c->setPort(conn.first.str() + "_gate", import_sig(conn.second, "_gate"));
+ }
+ }
+
+ void generate()
+ {
+ if (design->module(combined_type)) {
+ // log("Combined module %s already exists.\n", log_id(combined_type));
+ return;
+ }
+
+ log("Generating combined module %s from module %s.\n", log_id(combined_type), log_id(orig_type));
+ module = design->addModule(combined_type);
+
+ for (auto wire : original->wires()) {
+ module->addWire(wire->name.str() + "_gold", wire);
+ module->addWire(wire->name.str() + "_gate", wire);
+ }
+ module->fixup_ports();
+
+ for (auto cell : original->cells()) {
+ if (design->module(cell->type) == nullptr) {
+ import_prim_cell(cell, "_gold");
+ import_prim_cell(cell, "_gate");
+ } else {
+ import_hier_cell(cell);
+ }
+ }
+
+ for (auto &conn : original->connections()) {
+ module->connect(import_sig(conn.first, "_gold"), import_sig(conn.second, "_gold"));
+ module->connect(import_sig(conn.first, "_gate"), import_sig(conn.second, "_gate"));
+ }
+
+ if (opts.nop)
+ return;
+
+ CellTypes ct;
+ ct.setup_internals_eval();
+ ct.setup_stdcells_eval();
+
+ SigMap sigmap(module);
+
+ dict<SigBit, SigBit> data_bit_to_eq_net;
+ dict<Cell*, SigSpec> cell_to_eq_nets;
+ dict<SigSpec, SigSpec> reduce_db;
+ dict<SigSpec, SigSpec> invert_db;
+
+ for (auto cell : original->cells())
+ {
+ if (!ct.cell_known(cell->type))
+ continue;
+
+ for (auto &conn : cell->connections())
+ {
+ if (!cell->output(conn.first))
+ continue;
+
+ SigSpec A = import_sig(conn.second, "_gold");
+ SigSpec B = import_sig(conn.second, "_gate");
+ SigBit EQ = module->Eq(NEW_ID, A, B);
+
+ for (auto bit : sigmap({A, B}))
+ data_bit_to_eq_net[bit] = EQ;
+
+ cell_to_eq_nets[cell].append(EQ);
+ }
+ }
+
+ for (auto cell : original->cells())
+ {
+ if (!ct.cell_known(cell->type))
+ continue;
+
+ bool skip_cell = !cell_to_eq_nets.count(cell);
+ pool<SigBit> src_eq_bits;
+
+ for (auto &conn : cell->connections())
+ {
+ if (skip_cell)
+ break;
+
+ if (cell->output(conn.first))
+ continue;
+
+ SigSpec A = import_sig(conn.second, "_gold");
+ SigSpec B = import_sig(conn.second, "_gate");
+
+ for (auto bit : sigmap({A, B})) {
+ if (data_bit_to_eq_net.count(bit))
+ src_eq_bits.insert(data_bit_to_eq_net.at(bit));
+ else
+ skip_cell = true;
+ }
+ }
+
+ if (!skip_cell) {
+ SigSpec antecedent = SigSpec(src_eq_bits);
+ antecedent.sort_and_unify();
+
+ if (GetSize(antecedent) > 1) {
+ if (reduce_db.count(antecedent) == 0)
+ reduce_db[antecedent] = module->ReduceAnd(NEW_ID, antecedent);
+ antecedent = reduce_db.at(antecedent);
+ }
+
+ SigSpec consequent = cell_to_eq_nets.at(cell);
+ consequent.sort_and_unify();
+
+ if (GetSize(consequent) > 1) {
+ if (reduce_db.count(consequent) == 0)
+ reduce_db[consequent] = module->ReduceAnd(NEW_ID, consequent);
+ consequent = reduce_db.at(consequent);
+ }
+
+ if (opts.fwd)
+ module->addAssume(NEW_ID, consequent, antecedent);
+
+ if (opts.bwd)
+ {
+ if (invert_db.count(antecedent) == 0)
+ invert_db[antecedent] = module->Not(NEW_ID, antecedent);
+
+ if (invert_db.count(consequent) == 0)
+ invert_db[consequent] = module->Not(NEW_ID, consequent);
+
+ module->addAssume(NEW_ID, invert_db.at(antecedent), invert_db.at(consequent));
+ }
+ }
+ }
+ }
+};
+
+struct FmcombinePass : public Pass {
+ FmcombinePass() : Pass("fmcombine", "combine two instances of a cell into one") { }
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" fmcombine [options] module_name gold_cell gate_cell\n");
+ // log(" fmcombine [options] @gold_cell @gate_cell\n");
+ log("\n");
+ log("This pass takes two cells, which are instances of the same module, and replaces\n");
+ log("them with one instance of a special 'combined' module, that effectively\n");
+ log("contains two copies of the original module, plus some formal properties.\n");
+ log("\n");
+ log("This is useful for formal test benches that check what differences in behavior\n");
+ log("a slight difference in input causes in a module.\n");
+ log("\n");
+ log(" -fwd\n");
+ log(" Insert forward hint assumptions into the combined module.\n");
+ log("\n");
+ log(" -bwd\n");
+ log(" Insert backward hint assumptions into the combined module.\n");
+ log(" (Backward hints are logically equivalend to fordward hits, but\n");
+ log(" some solvers are faster with bwd hints, or even both -bwd and -fwd.)\n");
+ log("\n");
+ log(" -nop\n");
+ log(" Don't insert hint assumptions into the combined module.\n");
+ log(" (This should not provide any speedup over the original design, but\n");
+ log(" strangely sometimes it does.)\n");
+ log("\n");
+ log("If none of -fwd, -bwd, and -nop is given, then -fwd is used as default.\n");
+ log("\n");
+ }
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ opts_t opts;
+ Module *module = nullptr;
+ Cell *gold_cell = nullptr;
+ Cell *gate_cell = nullptr;
+
+ log_header(design, "Executing FMCOMBINE pass.\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ // if (args[argidx] == "-o" && argidx+1 < args.size()) {
+ // filename = args[++argidx];
+ // continue;
+ // }
+ if (args[argidx] == "-fwd") {
+ opts.fwd = true;
+ continue;
+ }
+ if (args[argidx] == "-bwd") {
+ opts.bwd = true;
+ continue;
+ }
+ if (args[argidx] == "-nop") {
+ opts.nop = true;
+ continue;
+ }
+ break;
+ }
+ if (argidx+2 == args.size())
+ {
+ string gold_name = args[argidx++];
+ string gate_name = args[argidx++];
+ log_cmd_error("fmcombine @gold_cell @gate_cell call style is not implemented yet.");
+ }
+ else if (argidx+3 == args.size())
+ {
+ IdString module_name = RTLIL::escape_id(args[argidx++]);
+ IdString gold_name = RTLIL::escape_id(args[argidx++]);
+ IdString gate_name = RTLIL::escape_id(args[argidx++]);
+
+ module = design->module(module_name);
+ if (module == nullptr)
+ log_cmd_error("Module %s not found.\n", log_id(module_name));
+
+ gold_cell = module->cell(gold_name);
+ if (gold_cell == nullptr)
+ log_cmd_error("Gold cell %s not found in module %s.\n", log_id(gold_name), log_id(module));
+
+ gate_cell = module->cell(gate_name);
+ if (gate_cell == nullptr)
+ log_cmd_error("Gold cell %s not found in module %s.\n", log_id(gate_name), log_id(module));
+ }
+ else
+ {
+ log_cmd_error("Invalid number of arguments.\n");
+ }
+ // extra_args(args, argidx, design);
+
+ if (opts.nop && (opts.fwd || opts.bwd))
+ log_cmd_error("Option -nop can not be combined with -fwd and/or -bwd.\n");
+
+ if (!opts.nop && !opts.fwd && !opts.bwd)
+ opts.fwd = true;
+
+ if (gold_cell->type != gate_cell->type)
+ log_cmd_error("Types of gold and gate cells do not match.\n");
+ if (!gold_cell->parameters.empty())
+ log_cmd_error("Gold cell has unresolved instance parameters.\n");
+ if (!gate_cell->parameters.empty())
+ log_cmd_error("Gold cell has unresolved instance parameters.\n");
+
+ FmcombineWorker worker(design, gold_cell->type, opts);
+ worker.generate();
+ IdString combined_cell_name = module->uniquify(stringf("\\%s_%s", log_id(gold_cell), log_id(gate_cell)));
+
+ Cell *cell = module->addCell(combined_cell_name, worker.combined_type);
+ cell->attributes = gold_cell->attributes;
+ cell->add_strpool_attribute("\\src", gate_cell->get_strpool_attribute("\\src"));
+
+ log("Combining cells %s and %s in module %s into new cell %s.\n", log_id(gold_cell), log_id(gate_cell), log_id(module), log_id(cell));
+
+ for (auto &conn : gold_cell->connections())
+ cell->setPort(conn.first.str() + "_gold", conn.second);
+ module->remove(gold_cell);
+
+ for (auto &conn : gate_cell->connections())
+ cell->setPort(conn.first.str() + "_gate", conn.second);
+ module->remove(gate_cell);
+ }
+} FmcombinePass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/sat/mutate.cc b/passes/sat/mutate.cc
new file mode 100644
index 00000000..c50678c5
--- /dev/null
+++ b/passes/sat/mutate.cc
@@ -0,0 +1,956 @@
+/*
+ * 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 mutate_t {
+ string mode;
+ pool<string> src;
+ IdString module, cell;
+ IdString port, wire;
+ int portbit = -1;
+ int ctrlbit = -1;
+ int wirebit = -1;
+ bool used = false;
+};
+
+struct mutate_opts_t {
+ int seed = 0;
+ std::string mode;
+ pool<string> src;
+ IdString module, cell, port, wire;
+ int portbit = -1;
+ int ctrlbit = -1;
+ int wirebit = -1;
+
+ IdString ctrl_name;
+ int ctrl_width = -1, ctrl_value = -1;
+
+ bool none = false;
+
+ int pick_cover_prcnt = 80;
+
+ int weight_cover = 500;
+
+ int weight_pq_w = 100;
+ int weight_pq_b = 100;
+ int weight_pq_c = 100;
+ int weight_pq_s = 100;
+
+ int weight_pq_mw = 100;
+ int weight_pq_mb = 100;
+ int weight_pq_mc = 100;
+ int weight_pq_ms = 100;
+};
+
+void database_add(std::vector<mutate_t> &database, const mutate_opts_t &opts, const mutate_t &entry)
+{
+ if (!opts.mode.empty() && opts.mode != entry.mode)
+ return;
+
+ if (!opts.src.empty()) {
+ bool found_match = false;
+ for (auto &s : opts.src) {
+ if (entry.src.count(s))
+ found_match = true;
+ }
+ if (!found_match)
+ return;
+ }
+
+ if (!opts.module.empty() && opts.module != entry.module)
+ return;
+
+ if (!opts.cell.empty() && opts.cell != entry.cell)
+ return;
+
+ if (!opts.port.empty() && opts.port != entry.port)
+ return;
+
+ if (opts.portbit >= 0 && opts.portbit != entry.portbit)
+ return;
+
+ if (opts.ctrlbit >= 0 && opts.ctrlbit != entry.ctrlbit)
+ return;
+
+ if (!opts.wire.empty() && opts.wire != entry.wire)
+ return;
+
+ if (opts.wirebit >= 0 && opts.wirebit != entry.wirebit)
+ return;
+
+ database.push_back(entry);
+}
+
+struct xs128_t
+{
+ uint32_t x = 123456789;
+ uint32_t y = 0, z = 0, w = 0;
+
+ xs128_t(int seed = 0) : w(seed) {
+ next();
+ next();
+ next();
+ }
+
+ void next() {
+ uint32_t t = x ^ (x << 11);
+ x = y, y = z, z = w;
+ w ^= (w >> 19) ^ t ^ (t >> 8);
+ }
+
+ int operator()() {
+ next();
+ return w & 0x3fffffff;
+ }
+
+ int operator()(int n) {
+ if (n < 2)
+ return 0;
+ while (1) {
+ int k = (*this)(), p = k % n;
+ if ((k - p + n) <= 0x40000000)
+ return p;
+ }
+ }
+};
+
+struct coverdb_t
+{
+ dict<string, int> src_db;
+ dict<tuple<IdString, IdString>, int> wire_db;
+ dict<tuple<IdString, IdString, int>, int> wirebit_db;
+
+ void insert(const mutate_t &m) {
+ if (!m.wire.empty()) {
+ wire_db[tuple<IdString, IdString>(m.module, m.wire)] = 0;
+ wirebit_db[tuple<IdString, IdString, int>(m.module, m.wire, m.wirebit)] = 0;
+ }
+ for (auto &s : m.src) {
+ src_db[s] = 0;
+ }
+ }
+
+ void update(const mutate_t &m) {
+ if (!m.wire.empty()) {
+ wire_db.at(tuple<IdString, IdString>(m.module, m.wire))++;
+ wirebit_db.at(tuple<IdString, IdString, int>(m.module, m.wire, m.wirebit))++;
+ }
+ for (auto &s : m.src) {
+ src_db.at(s)++;
+ }
+ }
+
+ int score(const mutate_t &m) {
+ int this_score = m.src.empty() ? 0 : 1;
+ if (!m.wire.empty()) {
+ this_score += wire_db.at(tuple<IdString, IdString>(m.module, m.wire)) ? 0 : 5;
+ this_score += wirebit_db.at(tuple<IdString, IdString, int>(m.module, m.wire, m.wirebit)) ? 0 : 1;
+ }
+ for (auto &s : m.src) {
+ this_score += src_db.at(s) ? 0 : 5;
+ }
+ return this_score;
+ }
+};
+
+struct mutate_queue_t
+{
+ pool<mutate_t*, hash_ptr_ops> db;
+
+ mutate_t *pick(xs128_t &rng, coverdb_t &coverdb, const mutate_opts_t &opts) {
+ mutate_t *m = nullptr;
+ if (rng(100) < opts.pick_cover_prcnt) {
+ vector<mutate_t*> candidates, rmqueue;
+ int best_score = -1;
+ for (auto p : db) {
+ if (p->used) {
+ rmqueue.push_back(p);
+ continue;
+ }
+ int this_score = coverdb.score(*p);
+ if (this_score > best_score) {
+ best_score = this_score;
+ candidates.clear();
+ }
+ if (best_score == this_score)
+ candidates.push_back(p);
+ }
+ for (auto p : rmqueue)
+ db.erase(p);
+ if (!candidates.empty())
+ m = candidates[rng(GetSize(candidates))];
+ }
+ if (m == nullptr) {
+ while (!db.empty()) {
+ int i = rng(GetSize(db));
+ auto it = db.element(i);
+ mutate_t *p = *it;
+ db.erase(it);
+ if (p->used == false) {
+ m = p;
+ break;
+ }
+ }
+ }
+ return m;
+ }
+
+ void add(mutate_t *m) {
+ db.insert(m);
+ }
+};
+
+template <typename K, typename T>
+struct mutate_chain_queue_t
+{
+ dict<K, T> db;
+
+ mutate_t *pick(xs128_t &rng, coverdb_t &coverdb, const mutate_opts_t &opts) {
+ while (!db.empty()) {
+ int i = rng(GetSize(db));
+ auto it = db.element(i);
+ mutate_t *m = it->second.pick(rng, coverdb, opts);
+ if (m != nullptr)
+ return m;
+ db.erase(it);
+ }
+ return nullptr;
+ }
+
+ template<typename... Args>
+ void add(mutate_t *m, K key, Args... args) {
+ db[key].add(m, args...);
+ }
+};
+
+template <typename K, typename T>
+struct mutate_once_queue_t
+{
+ dict<K, T> db;
+
+ mutate_t *pick(xs128_t &rng, coverdb_t &coverdb, const mutate_opts_t &opts) {
+ while (!db.empty()) {
+ int i = rng(GetSize(db));
+ auto it = db.element(i);
+ mutate_t *m = it->second.pick(rng, coverdb, opts);
+ db.erase(it);
+ if (m != nullptr)
+ return m;
+ }
+ return nullptr;
+ }
+
+ template<typename... Args>
+ void add(mutate_t *m, K key, Args... args) {
+ db[key].add(m, args...);
+ }
+};
+
+void database_reduce(std::vector<mutate_t> &database, const mutate_opts_t &opts, int N, xs128_t &rng)
+{
+ std::vector<mutate_t> new_database;
+ coverdb_t coverdb;
+
+ int total_weight = opts.weight_cover + opts.weight_pq_w + opts.weight_pq_b + opts.weight_pq_c + opts.weight_pq_s;
+ total_weight += opts.weight_pq_mw + opts.weight_pq_mb + opts.weight_pq_mc + opts.weight_pq_ms;
+
+ if (N >= GetSize(database))
+ return;
+
+ mutate_once_queue_t<tuple<IdString, IdString>, mutate_queue_t> primary_queue_wire;
+ mutate_once_queue_t<tuple<IdString, IdString, int>, mutate_queue_t> primary_queue_bit;
+ mutate_once_queue_t<tuple<IdString, IdString>, mutate_queue_t> primary_queue_cell;
+ mutate_once_queue_t<string, mutate_queue_t> primary_queue_src;
+
+ mutate_chain_queue_t<IdString, mutate_once_queue_t<IdString, mutate_queue_t>> primary_queue_module_wire;
+ mutate_chain_queue_t<IdString, mutate_once_queue_t<pair<IdString, int>, mutate_queue_t>> primary_queue_module_bit;
+ mutate_chain_queue_t<IdString, mutate_once_queue_t<IdString, mutate_queue_t>> primary_queue_module_cell;
+ mutate_chain_queue_t<IdString, mutate_once_queue_t<string, mutate_queue_t>> primary_queue_module_src;
+
+ for (auto &m : database)
+ {
+ coverdb.insert(m);
+
+ if (!m.wire.empty()) {
+ primary_queue_wire.add(&m, tuple<IdString, IdString>(m.module, m.wire));
+ primary_queue_bit.add(&m, tuple<IdString, IdString, int>(m.module, m.wire, m.wirebit));
+ primary_queue_module_wire.add(&m, m.module, m.wire);
+ primary_queue_module_bit.add(&m, m.module, pair<IdString, int>(m.wire, m.wirebit));
+ }
+
+ primary_queue_cell.add(&m, tuple<IdString, IdString>(m.module, m.cell));
+ primary_queue_module_cell.add(&m, m.module, m.cell);
+
+ for (auto &s : m.src) {
+ primary_queue_src.add(&m, s);
+ primary_queue_module_src.add(&m, m.module, s);
+ }
+ }
+
+ vector<mutate_t*> cover_candidates;
+ int best_cover_score = -1;
+ bool skip_cover = false;
+
+ while (GetSize(new_database) < N)
+ {
+ int k = rng(total_weight);
+
+ k -= opts.weight_cover;
+ if (k < 0) {
+ while (!skip_cover) {
+ if (cover_candidates.empty()) {
+ best_cover_score = -1;
+ for (auto &m : database) {
+ if (m.used || m.src.empty())
+ continue;
+ int this_score = -1;
+ for (auto &s : m.src) {
+ if (this_score == -1 || this_score > coverdb.src_db.at(s))
+ this_score = coverdb.src_db.at(s);
+ }
+ log_assert(this_score != -1);
+ if (best_cover_score == -1 || this_score < best_cover_score) {
+ cover_candidates.clear();
+ best_cover_score = this_score;
+ }
+ if (best_cover_score == this_score)
+ cover_candidates.push_back(&m);
+ }
+ if (best_cover_score == -1) {
+ skip_cover = true;
+ break;
+ }
+ }
+
+ mutate_t *m = nullptr;
+ while (!cover_candidates.empty())
+ {
+ int idx = rng(GetSize(cover_candidates));
+ mutate_t *p = cover_candidates[idx];
+ cover_candidates[idx] = cover_candidates.back();
+ cover_candidates.pop_back();
+
+ if (p->used)
+ continue;
+
+ int this_score = -1;
+ for (auto &s : p->src) {
+ if (this_score == -1 || this_score > coverdb.src_db.at(s))
+ this_score = coverdb.src_db.at(s);
+ }
+
+ if (this_score != best_cover_score)
+ continue;
+
+ m = p;
+ break;
+ }
+
+ if (m != nullptr) {
+ m->used = true;
+ coverdb.update(*m);
+ new_database.push_back(*m);
+ break;
+ }
+ }
+ continue;
+ }
+
+#define X(__wght, __queue) \
+ k -= __wght; \
+ if (k < 0) { \
+ mutate_t *m = __queue.pick(rng, coverdb, opts); \
+ if (m != nullptr) { \
+ m->used = true; \
+ coverdb.update(*m); \
+ new_database.push_back(*m); \
+ }; \
+ continue; \
+ }
+
+ X(opts.weight_pq_w, primary_queue_wire)
+ X(opts.weight_pq_b, primary_queue_bit)
+ X(opts.weight_pq_c, primary_queue_cell)
+ X(opts.weight_pq_s, primary_queue_src)
+
+ X(opts.weight_pq_mw, primary_queue_module_wire)
+ X(opts.weight_pq_mb, primary_queue_module_bit)
+ X(opts.weight_pq_mc, primary_queue_module_cell)
+ X(opts.weight_pq_ms, primary_queue_module_src)
+#undef X
+ }
+
+ std::swap(new_database, database);
+
+ int covered_src_cnt = 0;
+ int covered_wire_cnt = 0;
+ int covered_wirebit_cnt = 0;
+
+ for (auto &it : coverdb.src_db)
+ if (it.second)
+ covered_src_cnt++;
+
+ for (auto &it : coverdb.wire_db)
+ if (it.second)
+ covered_wire_cnt++;
+
+ for (auto &it : coverdb.wirebit_db)
+ if (it.second)
+ covered_wirebit_cnt++;
+
+ log("Covered %d/%d src attributes (%.2f%%).\n", covered_src_cnt, GetSize(coverdb.src_db), 100.0 * covered_src_cnt / GetSize(coverdb.src_db));
+ log("Covered %d/%d wires (%.2f%%).\n", covered_wire_cnt, GetSize(coverdb.wire_db), 100.0 * covered_wire_cnt / GetSize(coverdb.wire_db));
+ log("Covered %d/%d wire bits (%.2f%%).\n", covered_wirebit_cnt, GetSize(coverdb.wirebit_db), 100.0 * covered_wirebit_cnt / GetSize(coverdb.wirebit_db));
+}
+
+void mutate_list(Design *design, const mutate_opts_t &opts, const string &filename, const string &srcsfile, int N)
+{
+ pool<string> sources;
+ std::vector<mutate_t> database;
+ xs128_t rng(opts.seed);
+
+ for (auto module : design->selected_modules())
+ {
+ if (!opts.module.empty() && module->name != opts.module)
+ continue;
+
+ SigMap sigmap(module);
+ dict<SigBit, int> bit_user_cnt;
+
+ for (auto wire : module->wires()) {
+ if (wire->name[0] == '\\' && wire->attributes.count("\\src"))
+ sigmap.add(wire);
+ }
+
+ for (auto cell : module->cells()) {
+ for (auto &conn : cell->connections()) {
+ if (cell->output(conn.first))
+ continue;
+ for (auto bit : sigmap(conn.second))
+ bit_user_cnt[bit]++;
+ }
+ }
+
+ for (auto wire : module->selected_wires())
+ {
+ for (SigBit bit : SigSpec(wire))
+ {
+ SigBit sigbit = sigmap(bit);
+
+ if (bit.wire == nullptr || sigbit.wire == nullptr)
+ continue;
+
+ if (!bit.wire->port_id != !sigbit.wire->port_id) {
+ if (bit.wire->port_id)
+ sigmap.add(bit);
+ continue;
+ }
+
+ if (!bit.wire->name[0] != !sigbit.wire->name[0]) {
+ if (bit.wire->name[0] == '\\')
+ sigmap.add(bit);
+ continue;
+ }
+ }
+ }
+
+ for (auto cell : module->selected_cells())
+ {
+ if (!opts.cell.empty() && cell->name != opts.cell)
+ continue;
+
+ for (auto &conn : cell->connections())
+ {
+ for (int i = 0; i < GetSize(conn.second); i++) {
+ mutate_t entry;
+ entry.module = module->name;
+ entry.cell = cell->name;
+ entry.port = conn.first;
+ entry.portbit = i;
+
+ for (auto &s : cell->get_strpool_attribute("\\src"))
+ entry.src.insert(s);
+
+ SigBit bit = sigmap(conn.second[i]);
+ if (bit.wire && bit.wire->name[0] == '\\' && (cell->output(conn.first) || bit_user_cnt[bit] == 1)) {
+ for (auto &s : bit.wire->get_strpool_attribute("\\src"))
+ entry.src.insert(s);
+ entry.wire = bit.wire->name;
+ entry.wirebit = bit.offset;
+ }
+
+ if (!srcsfile.empty())
+ sources.insert(entry.src.begin(), entry.src.end());
+
+ entry.mode = "inv";
+ database_add(database, opts, entry);
+
+ entry.mode = "const0";
+ database_add(database, opts, entry);
+
+ entry.mode = "const1";
+ database_add(database, opts, entry);
+
+ entry.mode = "cnot0";
+ entry.ctrlbit = rng(GetSize(conn.second));
+ if (entry.ctrlbit != entry.portbit && conn.second[entry.ctrlbit].wire)
+ database_add(database, opts, entry);
+
+ entry.mode = "cnot1";
+ entry.ctrlbit = rng(GetSize(conn.second));
+ if (entry.ctrlbit != entry.portbit && conn.second[entry.ctrlbit].wire)
+ database_add(database, opts, entry);
+ }
+ }
+ }
+ }
+
+ log("Raw database size: %d\n", GetSize(database));
+ if (N != 0) {
+ database_reduce(database, opts, opts.none ? N-1 : N, rng);
+ log("Reduced database size: %d\n", GetSize(database));
+ }
+
+ if (!srcsfile.empty()) {
+ std::ofstream sout;
+ sout.open(srcsfile, std::ios::out | std::ios::trunc);
+ if (!sout.is_open())
+ log_error("Could not open file \"%s\" with write access.\n", srcsfile.c_str());
+ sources.sort();
+ for (auto &s : sources)
+ sout << s << std::endl;
+ }
+
+ std::ofstream fout;
+
+ if (!filename.empty()) {
+ fout.open(filename, std::ios::out | std::ios::trunc);
+ if (!fout.is_open())
+ log_error("Could not open file \"%s\" with write access.\n", filename.c_str());
+ }
+
+ int ctrl_value = opts.ctrl_value;
+
+ if (opts.none) {
+ string str = "mutate";
+ if (!opts.ctrl_name.empty())
+ str += stringf(" -ctrl %s %d %d", log_id(opts.ctrl_name), opts.ctrl_width, ctrl_value++);
+ str += " -mode none";
+ if (filename.empty())
+ log("%s\n", str.c_str());
+ else
+ fout << str << std::endl;
+ }
+
+ for (auto &entry : database) {
+ string str = "mutate";
+ if (!opts.ctrl_name.empty())
+ str += stringf(" -ctrl %s %d %d", log_id(opts.ctrl_name), opts.ctrl_width, ctrl_value++);
+ str += stringf(" -mode %s", entry.mode.c_str());
+ if (!entry.module.empty())
+ str += stringf(" -module %s", log_id(entry.module));
+ if (!entry.cell.empty())
+ str += stringf(" -cell %s", log_id(entry.cell));
+ if (!entry.port.empty())
+ str += stringf(" -port %s", log_id(entry.port));
+ if (entry.portbit >= 0)
+ str += stringf(" -portbit %d", entry.portbit);
+ if (entry.ctrlbit >= 0)
+ str += stringf(" -ctrlbit %d", entry.ctrlbit);
+ if (!entry.wire.empty())
+ str += stringf(" -wire %s", log_id(entry.wire));
+ if (entry.wirebit >= 0)
+ str += stringf(" -wirebit %d", entry.wirebit);
+ for (auto &s : entry.src)
+ str += stringf(" -src %s", s.c_str());
+ if (filename.empty())
+ log("%s\n", str.c_str());
+ else
+ fout << str << std::endl;
+ }
+}
+
+SigSpec mutate_ctrl_sig(Module *module, IdString name, int width)
+{
+ Wire *ctrl_wire = module->wire(name);
+
+ if (ctrl_wire == nullptr)
+ {
+ log("Adding ctrl port %s to module %s.\n", log_id(name), log_id(module));
+
+ ctrl_wire = module->addWire(name, width);
+ ctrl_wire->port_input = true;
+ module->fixup_ports();
+
+ for (auto mod : module->design->modules())
+ for (auto cell : mod->cells())
+ {
+ if (cell->type != module->name)
+ continue;
+
+ SigSpec ctrl = mutate_ctrl_sig(mod, name, width);
+
+ log("Connecting ctrl port to cell %s in module %s.\n", log_id(cell), log_id(mod));
+ cell->setPort(name, ctrl);
+ }
+ }
+
+ log_assert(GetSize(ctrl_wire) == width);
+ return ctrl_wire;
+}
+
+SigBit mutate_ctrl(Module *module, const mutate_opts_t &opts)
+{
+ if (opts.ctrl_name.empty())
+ return State::S1;
+
+ SigSpec sig = mutate_ctrl_sig(module, opts.ctrl_name, opts.ctrl_width);
+ return module->Eq(NEW_ID, sig, Const(opts.ctrl_value, GetSize(sig)));
+}
+
+SigSpec mutate_ctrl_mux(Module *module, const mutate_opts_t &opts, SigSpec unchanged_sig, SigSpec changed_sig)
+{
+ SigBit ctrl_bit = mutate_ctrl(module, opts);
+ if (ctrl_bit == State::S0)
+ return unchanged_sig;
+ if (ctrl_bit == State::S1)
+ return changed_sig;
+ return module->Mux(NEW_ID, unchanged_sig, changed_sig, ctrl_bit);
+}
+
+void mutate_inv(Design *design, const mutate_opts_t &opts)
+{
+ Module *module = design->module(opts.module);
+ Cell *cell = module->cell(opts.cell);
+
+ SigBit bit = cell->getPort(opts.port)[opts.portbit];
+ SigBit inbit, outbit;
+
+ if (cell->input(opts.port))
+ {
+ log("Add input inverter at %s.%s.%s[%d].\n", log_id(module), log_id(cell), log_id(opts.port), opts.portbit);
+ SigBit outbit = module->Not(NEW_ID, bit);
+ bit = mutate_ctrl_mux(module, opts, bit, outbit);
+ }
+ else
+ {
+ log("Add output inverter at %s.%s.%s[%d].\n", log_id(module), log_id(cell), log_id(opts.port), opts.portbit);
+ SigBit inbit = module->addWire(NEW_ID);
+ SigBit outbit = module->Not(NEW_ID, inbit);
+ module->connect(bit, mutate_ctrl_mux(module, opts, inbit, outbit));
+ bit = inbit;
+ }
+
+ SigSpec s = cell->getPort(opts.port);
+ s[opts.portbit] = bit;
+ cell->setPort(opts.port, s);
+}
+
+void mutate_const(Design *design, const mutate_opts_t &opts, bool one)
+{
+ Module *module = design->module(opts.module);
+ Cell *cell = module->cell(opts.cell);
+
+ SigBit bit = cell->getPort(opts.port)[opts.portbit];
+ SigBit inbit, outbit;
+
+ if (cell->input(opts.port))
+ {
+ log("Add input constant %d at %s.%s.%s[%d].\n", one ? 1 : 0, log_id(module), log_id(cell), log_id(opts.port), opts.portbit);
+ SigBit outbit = one ? State::S1 : State::S0;
+ bit = mutate_ctrl_mux(module, opts, bit, outbit);
+ }
+ else
+ {
+ log("Add output constant %d at %s.%s.%s[%d].\n", one ? 1 : 0, log_id(module), log_id(cell), log_id(opts.port), opts.portbit);
+ SigBit inbit = module->addWire(NEW_ID);
+ SigBit outbit = one ? State::S1 : State::S0;
+ module->connect(bit, mutate_ctrl_mux(module, opts, inbit, outbit));
+ bit = inbit;
+ }
+
+ SigSpec s = cell->getPort(opts.port);
+ s[opts.portbit] = bit;
+ cell->setPort(opts.port, s);
+}
+
+void mutate_cnot(Design *design, const mutate_opts_t &opts, bool one)
+{
+ Module *module = design->module(opts.module);
+ Cell *cell = module->cell(opts.cell);
+
+ SigBit bit = cell->getPort(opts.port)[opts.portbit];
+ SigBit ctrl = cell->getPort(opts.port)[opts.ctrlbit];
+ SigBit inbit, outbit;
+
+ if (cell->input(opts.port))
+ {
+ log("Add input cnot%d at %s.%s.%s[%d,%d].\n", one ? 1 : 0, log_id(module), log_id(cell), log_id(opts.port), opts.portbit, opts.ctrlbit);
+ SigBit outbit = one ? module->Xor(NEW_ID, bit, ctrl) : module->Xnor(NEW_ID, bit, ctrl);
+ bit = mutate_ctrl_mux(module, opts, bit, outbit);
+ }
+ else
+ {
+ log("Add output cnot%d at %s.%s.%s[%d,%d].\n", one ? 1 : 0, log_id(module), log_id(cell), log_id(opts.port), opts.portbit, opts.ctrlbit);
+ SigBit inbit = module->addWire(NEW_ID);
+ SigBit outbit = one ? module->Xor(NEW_ID, inbit, ctrl) : module->Xnor(NEW_ID, inbit, ctrl);
+ module->connect(bit, mutate_ctrl_mux(module, opts, inbit, outbit));
+ bit = inbit;
+ }
+
+ SigSpec s = cell->getPort(opts.port);
+ s[opts.portbit] = bit;
+ cell->setPort(opts.port, s);
+}
+
+struct MutatePass : public Pass {
+ MutatePass() : Pass("mutate", "generate or apply design mutations") { }
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" mutate -list N [options] [selection]\n");
+ log("\n");
+ log("Create a list of N mutations using an even sampling.\n");
+ log("\n");
+ log(" -o filename\n");
+ log(" Write list to this file instead of console output\n");
+ log("\n");
+ log(" -s filename\n");
+ log(" Write a list of all src tags found in the design to the specified file\n");
+ log("\n");
+ log(" -seed N\n");
+ log(" RNG seed for selecting mutations\n");
+ log("\n");
+ log(" -none\n");
+ log(" Include a \"none\" mutation in the output\n");
+ log("\n");
+ log(" -ctrl name width value\n");
+ log(" Add -ctrl options to the output. Use 'value' for first mutation, then\n");
+ log(" simply count up from there.\n");
+ log("\n");
+ log(" -mode name\n");
+ log(" -module name\n");
+ log(" -cell name\n");
+ log(" -port name\n");
+ log(" -portbit int\n");
+ log(" -ctrlbit int\n");
+ log(" -wire name\n");
+ log(" -wirebit int\n");
+ log(" -src string\n");
+ log(" Filter list of mutation candidates to those matching\n");
+ log(" the given parameters.\n");
+ log("\n");
+ log(" -cfg option int\n");
+ log(" Set a configuration option. Options available:\n");
+ log(" weight_pq_w weight_pq_b weight_pq_c weight_pq_s\n");
+ log(" weight_pq_mw weight_pq_mb weight_pq_mc weight_pq_ms\n");
+ log(" weight_cover pick_cover_prcnt\n");
+ log("\n");
+ log("\n");
+ log(" mutate -mode MODE [options]\n");
+ log("\n");
+ log("Apply the given mutation.\n");
+ log("\n");
+ log(" -ctrl name width value\n");
+ log(" Add a control signal with the given name and width. The mutation is\n");
+ log(" activated if the control signal equals the given value.\n");
+ log("\n");
+ log(" -module name\n");
+ log(" -cell name\n");
+ log(" -port name\n");
+ log(" -portbit int\n");
+ log(" -ctrlbit int\n");
+ log(" Mutation parameters, as generated by 'mutate -list N'.\n");
+ log("\n");
+ log(" -wire name\n");
+ log(" -wirebit int\n");
+ log(" -src string\n");
+ log(" Ignored. (They are generated by -list for documentation purposes.)\n");
+ log("\n");
+ }
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ mutate_opts_t opts;
+ string filename;
+ string srcsfile;
+ int N = -1;
+
+ log_header(design, "Executing MUTATE pass.\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ if (args[argidx] == "-list" && argidx+1 < args.size()) {
+ N = atoi(args[++argidx].c_str());
+ continue;
+ }
+ if (args[argidx] == "-o" && argidx+1 < args.size()) {
+ filename = args[++argidx];
+ continue;
+ }
+ if (args[argidx] == "-s" && argidx+1 < args.size()) {
+ srcsfile = args[++argidx];
+ continue;
+ }
+ if (args[argidx] == "-seed" && argidx+1 < args.size()) {
+ opts.seed = atoi(args[++argidx].c_str());
+ continue;
+ }
+ if (args[argidx] == "-none") {
+ opts.none = true;
+ continue;
+ }
+ if (args[argidx] == "-mode" && argidx+1 < args.size()) {
+ opts.mode = args[++argidx];
+ continue;
+ }
+ if (args[argidx] == "-ctrl" && argidx+3 < args.size()) {
+ opts.ctrl_name = RTLIL::escape_id(args[++argidx]);
+ opts.ctrl_width = atoi(args[++argidx].c_str());
+ opts.ctrl_value = atoi(args[++argidx].c_str());
+ continue;
+ }
+ if (args[argidx] == "-module" && argidx+1 < args.size()) {
+ opts.module = RTLIL::escape_id(args[++argidx]);
+ continue;
+ }
+ if (args[argidx] == "-cell" && argidx+1 < args.size()) {
+ opts.cell = RTLIL::escape_id(args[++argidx]);
+ continue;
+ }
+ if (args[argidx] == "-port" && argidx+1 < args.size()) {
+ opts.port = RTLIL::escape_id(args[++argidx]);
+ continue;
+ }
+ if (args[argidx] == "-portbit" && argidx+1 < args.size()) {
+ opts.portbit = atoi(args[++argidx].c_str());
+ continue;
+ }
+ if (args[argidx] == "-ctrlbit" && argidx+1 < args.size()) {
+ opts.ctrlbit = atoi(args[++argidx].c_str());
+ continue;
+ }
+ if (args[argidx] == "-wire" && argidx+1 < args.size()) {
+ opts.wire = RTLIL::escape_id(args[++argidx]);
+ continue;
+ }
+ if (args[argidx] == "-wirebit" && argidx+1 < args.size()) {
+ opts.wirebit = atoi(args[++argidx].c_str());
+ continue;
+ }
+ if (args[argidx] == "-src" && argidx+1 < args.size()) {
+ opts.src.insert(args[++argidx]);
+ continue;
+ }
+ if (args[argidx] == "-cfg" && argidx+2 < args.size()) {
+ if (args[argidx+1] == "pick_cover_prcnt") {
+ opts.pick_cover_prcnt = atoi(args[argidx+2].c_str());
+ argidx += 2;
+ continue;
+ }
+ if (args[argidx+1] == "weight_cover") {
+ opts.weight_cover = atoi(args[argidx+2].c_str());
+ argidx += 2;
+ continue;
+ }
+ if (args[argidx+1] == "weight_pq_w") {
+ opts.weight_pq_w = atoi(args[argidx+2].c_str());
+ argidx += 2;
+ continue;
+ }
+ if (args[argidx+1] == "weight_pq_b") {
+ opts.weight_pq_b = atoi(args[argidx+2].c_str());
+ argidx += 2;
+ continue;
+ }
+ if (args[argidx+1] == "weight_pq_c") {
+ opts.weight_pq_c = atoi(args[argidx+2].c_str());
+ argidx += 2;
+ continue;
+ }
+ if (args[argidx+1] == "weight_pq_s") {
+ opts.weight_pq_s = atoi(args[argidx+2].c_str());
+ argidx += 2;
+ continue;
+ }
+ if (args[argidx+1] == "weight_pq_mw") {
+ opts.weight_pq_mw = atoi(args[argidx+2].c_str());
+ argidx += 2;
+ continue;
+ }
+ if (args[argidx+1] == "weight_pq_mb") {
+ opts.weight_pq_mb = atoi(args[argidx+2].c_str());
+ argidx += 2;
+ continue;
+ }
+ if (args[argidx+1] == "weight_pq_mc") {
+ opts.weight_pq_mc = atoi(args[argidx+2].c_str());
+ argidx += 2;
+ continue;
+ }
+ if (args[argidx+1] == "weight_pq_ms") {
+ opts.weight_pq_ms = atoi(args[argidx+2].c_str());
+ argidx += 2;
+ continue;
+ }
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ if (N >= 0) {
+ mutate_list(design, opts, filename, srcsfile, N);
+ return;
+ }
+
+ if (opts.mode == "none") {
+ if (!opts.ctrl_name.empty()) {
+ Module *topmod = opts.module.empty() ? design->top_module() : design->module(opts.module);
+ if (topmod)
+ mutate_ctrl_sig(topmod, opts.ctrl_name, opts.ctrl_width);
+ }
+ return;
+ }
+
+ if (opts.mode == "inv") {
+ mutate_inv(design, opts);
+ return;
+ }
+
+ if (opts.mode == "const0" || opts.mode == "const1") {
+ mutate_const(design, opts, opts.mode == "const1");
+ return;
+ }
+
+ if (opts.mode == "cnot0" || opts.mode == "cnot1") {
+ mutate_cnot(design, opts, opts.mode == "cnot1");
+ return;
+ }
+
+ log_cmd_error("Invalid mode: %s\n", opts.mode.c_str());
+ }
+} MutatePass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/sat/supercover.cc b/passes/sat/supercover.cc
new file mode 100644
index 00000000..ba44f02d
--- /dev/null
+++ b/passes/sat/supercover.cc
@@ -0,0 +1,92 @@
+/*
+ * 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 SupercoverPass : public Pass {
+ SupercoverPass() : Pass("supercover", "add hi/lo cover cells for each wire bit") { }
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" supercover [options] [selection]\n");
+ log("\n");
+ log("This command adds two cover cells for each bit of each selected wire, one\n");
+ log("checking for a hi signal level and one checking for lo level.\n");
+ log("\n");
+ }
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ // bool flag_noinit = false;
+
+ log_header(design, "Executing SUPERCOVER pass.\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ // if (args[argidx] == "-noinit") {
+ // flag_noinit = true;
+ // continue;
+ // }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto module : design->selected_modules())
+ {
+ SigMap sigmap(module);
+ pool<SigBit> handled_bits;
+
+ int cnt_wire = 0, cnt_bits = 0;
+ log("Adding cover cells to module %s.\n", log_id(module));
+ for (auto wire : module->selected_wires())
+ {
+ bool counted_wire = false;
+ std::string src = wire->get_src_attribute();
+
+ for (auto bit : sigmap(SigSpec(wire)))
+ {
+ if (bit.wire == nullptr)
+ continue;
+
+ if (handled_bits.count(bit))
+ continue;
+
+ SigSpec inv = module->Not(NEW_ID, bit);
+ module->addCover(NEW_ID, bit, State::S1, src);
+ module->addCover(NEW_ID, inv, State::S1, src);
+
+ handled_bits.insert(bit);
+ if (!counted_wire) {
+ counted_wire = false;
+ cnt_wire++;
+ }
+ cnt_bits++;
+ }
+ }
+ log(" added cover cells to %d wires, %d bits.\n", cnt_wire, cnt_bits);
+ }
+ }
+} SupercoverPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/techmap/Makefile.inc b/passes/techmap/Makefile.inc
index 4faa0ab0..cf9e198a 100644
--- a/passes/techmap/Makefile.inc
+++ b/passes/techmap/Makefile.inc
@@ -36,6 +36,7 @@ OBJS += passes/techmap/attrmvcp.o
OBJS += passes/techmap/attrmap.o
OBJS += passes/techmap/zinit.o
OBJS += passes/techmap/dff2dffs.o
+OBJS += passes/techmap/flowmap.o
endif
GENFILES += passes/techmap/techmap.inc
diff --git a/passes/techmap/abc.cc b/passes/techmap/abc.cc
index d2d15a4a..21b70f49 100644
--- a/passes/techmap/abc.cc
+++ b/passes/techmap/abc.cc
@@ -327,8 +327,26 @@ void extract_cell(RTLIL::Cell *cell, bool keepff)
}
}
-std::string remap_name(RTLIL::IdString abc_name)
+std::string remap_name(RTLIL::IdString abc_name, RTLIL::Wire **orig_wire = nullptr)
{
+ std::string abc_sname = abc_name.substr(1);
+ if (abc_sname.substr(0, 5) == "ys__n") {
+ int sid = std::stoi(abc_sname.substr(5));
+ bool inv = abc_sname.back() == 'v';
+ for (auto sig : signal_list) {
+ if (sig.id == sid && sig.bit.wire != nullptr) {
+ std::stringstream sstr;
+ sstr << "$abc$" << map_autoidx << "$" << sig.bit.wire->name.substr(1);
+ if (sig.bit.wire->width != 1)
+ sstr << "[" << sig.bit.offset << "]";
+ if (inv)
+ sstr << "_inv";
+ if (orig_wire != nullptr)
+ *orig_wire = sig.bit.wire;
+ return sstr.str();
+ }
+ }
+ }
std::stringstream sstr;
sstr << "$abc$" << map_autoidx << "$" << abc_name.substr(1);
return sstr.str();
@@ -353,12 +371,12 @@ void dump_loop_graph(FILE *f, int &nr, std::map<int, std::set<int>> &edges, std:
}
for (auto n : nodes)
- fprintf(f, " n%d [label=\"%s\\nid=%d, count=%d\"%s];\n", n, log_signal(signal_list[n].bit),
+ fprintf(f, " ys__n%d [label=\"%s\\nid=%d, count=%d\"%s];\n", n, log_signal(signal_list[n].bit),
n, in_counts[n], workpool.count(n) ? ", shape=box" : "");
for (auto &e : edges)
for (auto n : e.second)
- fprintf(f, " n%d -> n%d;\n", e.first, n);
+ fprintf(f, " ys__n%d -> ys__n%d;\n", e.first, n);
fprintf(f, "}\n");
}
@@ -624,7 +642,7 @@ 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, 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, std::string lutin_shared, bool fast_mode,
- const std::vector<RTLIL::Cell*> &cells, bool show_tempdir, bool sop_mode)
+ const std::vector<RTLIL::Cell*> &cells, bool show_tempdir, bool sop_mode, bool abc_dress)
{
module = current_module;
map_autoidx = autoidx++;
@@ -728,7 +746,8 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin
for (size_t pos = abc_script.find("{S}"); pos != std::string::npos; pos = abc_script.find("{S}", pos))
abc_script = abc_script.substr(0, pos) + lutin_shared + abc_script.substr(pos+3);
-
+ if (abc_dress)
+ abc_script += "; dress";
abc_script += stringf("; write_blif %s/output.blif", tempdir_name.c_str());
abc_script = add_echos_to_abc_cmd(abc_script);
@@ -784,7 +803,7 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin
for (auto &si : signal_list) {
if (!si.is_port || si.type != G(NONE))
continue;
- fprintf(f, " n%d", si.id);
+ fprintf(f, " ys__n%d", si.id);
pi_map[count_input++] = log_signal(si.bit);
}
if (count_input == 0)
@@ -796,17 +815,17 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin
for (auto &si : signal_list) {
if (!si.is_port || si.type == G(NONE))
continue;
- fprintf(f, " n%d", si.id);
+ fprintf(f, " ys__n%d", si.id);
po_map[count_output++] = log_signal(si.bit);
}
fprintf(f, "\n");
for (auto &si : signal_list)
- fprintf(f, "# n%-5d %s\n", si.id, log_signal(si.bit));
+ fprintf(f, "# ys__n%-5d %s\n", si.id, log_signal(si.bit));
for (auto &si : signal_list) {
if (si.bit.wire == NULL) {
- fprintf(f, ".names n%d\n", si.id);
+ fprintf(f, ".names ys__n%d\n", si.id);
if (si.bit == RTLIL::State::S1)
fprintf(f, "1\n");
}
@@ -815,68 +834,68 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin
int count_gates = 0;
for (auto &si : signal_list) {
if (si.type == G(BUF)) {
- fprintf(f, ".names n%d n%d\n", si.in1, si.id);
+ fprintf(f, ".names ys__n%d ys__n%d\n", si.in1, si.id);
fprintf(f, "1 1\n");
} else if (si.type == G(NOT)) {
- fprintf(f, ".names n%d n%d\n", si.in1, si.id);
+ fprintf(f, ".names ys__n%d ys__n%d\n", si.in1, si.id);
fprintf(f, "0 1\n");
} else if (si.type == G(AND)) {
- fprintf(f, ".names n%d n%d n%d\n", si.in1, si.in2, si.id);
+ fprintf(f, ".names ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.id);
fprintf(f, "11 1\n");
} else if (si.type == G(NAND)) {
- fprintf(f, ".names n%d n%d n%d\n", si.in1, si.in2, si.id);
+ fprintf(f, ".names ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.id);
fprintf(f, "0- 1\n");
fprintf(f, "-0 1\n");
} else if (si.type == G(OR)) {
- fprintf(f, ".names n%d n%d n%d\n", si.in1, si.in2, si.id);
+ fprintf(f, ".names ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.id);
fprintf(f, "-1 1\n");
fprintf(f, "1- 1\n");
} else if (si.type == G(NOR)) {
- fprintf(f, ".names n%d n%d n%d\n", si.in1, si.in2, si.id);
+ fprintf(f, ".names ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.id);
fprintf(f, "00 1\n");
} else if (si.type == G(XOR)) {
- fprintf(f, ".names n%d n%d n%d\n", si.in1, si.in2, si.id);
+ fprintf(f, ".names ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.id);
fprintf(f, "01 1\n");
fprintf(f, "10 1\n");
} else if (si.type == G(XNOR)) {
- fprintf(f, ".names n%d n%d n%d\n", si.in1, si.in2, si.id);
+ fprintf(f, ".names ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.id);
fprintf(f, "00 1\n");
fprintf(f, "11 1\n");
} else if (si.type == G(ANDNOT)) {
- fprintf(f, ".names n%d n%d n%d\n", si.in1, si.in2, si.id);
+ fprintf(f, ".names ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.id);
fprintf(f, "10 1\n");
} else if (si.type == G(ORNOT)) {
- fprintf(f, ".names n%d n%d n%d\n", si.in1, si.in2, si.id);
+ fprintf(f, ".names ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.id);
fprintf(f, "1- 1\n");
fprintf(f, "-0 1\n");
} else if (si.type == G(MUX)) {
- fprintf(f, ".names n%d n%d n%d n%d\n", si.in1, si.in2, si.in3, si.id);
+ fprintf(f, ".names ys__n%d ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.in3, si.id);
fprintf(f, "1-0 1\n");
fprintf(f, "-11 1\n");
} else if (si.type == G(AOI3)) {
- fprintf(f, ".names n%d n%d n%d n%d\n", si.in1, si.in2, si.in3, si.id);
+ fprintf(f, ".names ys__n%d ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.in3, si.id);
fprintf(f, "-00 1\n");
fprintf(f, "0-0 1\n");
} else if (si.type == G(OAI3)) {
- fprintf(f, ".names n%d n%d n%d n%d\n", si.in1, si.in2, si.in3, si.id);
+ fprintf(f, ".names ys__n%d ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.in3, si.id);
fprintf(f, "00- 1\n");
fprintf(f, "--0 1\n");
} else if (si.type == G(AOI4)) {
- fprintf(f, ".names n%d n%d n%d n%d n%d\n", si.in1, si.in2, si.in3, si.in4, si.id);
+ fprintf(f, ".names ys__n%d ys__n%d ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.in3, si.in4, si.id);
fprintf(f, "-0-0 1\n");
fprintf(f, "-00- 1\n");
fprintf(f, "0--0 1\n");
fprintf(f, "0-0- 1\n");
} else if (si.type == G(OAI4)) {
- fprintf(f, ".names n%d n%d n%d n%d n%d\n", si.in1, si.in2, si.in3, si.in4, si.id);
+ fprintf(f, ".names ys__n%d ys__n%d ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.in3, si.in4, si.id);
fprintf(f, "00-- 1\n");
fprintf(f, "--00 1\n");
} else if (si.type == G(FF)) {
if (si.init == State::S0 || si.init == State::S1) {
- fprintf(f, ".latch n%d n%d %d\n", si.in1, si.id, si.init == State::S1 ? 1 : 0);
+ fprintf(f, ".latch ys__n%d ys__n%d %d\n", si.in1, si.id, si.init == State::S1 ? 1 : 0);
recover_init = true;
} else
- fprintf(f, ".latch n%d n%d 2\n", si.in1, si.id);
+ fprintf(f, ".latch ys__n%d ys__n%d 2\n", si.in1, si.id);
} else if (si.type != G(NONE))
log_abort();
if (si.type != G(NONE))
@@ -889,7 +908,6 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin
log("Extracted %d gates and %d wires to a netlist network with %d inputs and %d outputs.\n",
count_gates, GetSize(signal_list), count_input, count_output);
log_push();
-
if (count_output > 0)
{
log_header(design, "Executing ABC.\n");
@@ -988,7 +1006,10 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin
log_error("ABC output file does not contain a module `netlist'.\n");
for (auto &it : mapped_mod->wires_) {
RTLIL::Wire *w = it.second;
- RTLIL::Wire *wire = module->addWire(remap_name(w->name));
+ RTLIL::Wire *orig_wire = nullptr;
+ RTLIL::Wire *wire = module->addWire(remap_name(w->name, &orig_wire));
+ if (orig_wire != nullptr && orig_wire->attributes.count("\\src"))
+ wire->attributes["\\src"] = orig_wire->attributes["\\src"];
if (markgroups) wire->attributes["\\abcgroup"] = map_autoidx;
design->select(module, wire);
}
@@ -1213,7 +1234,7 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin
for (auto &si : signal_list)
if (si.is_port) {
char buffer[100];
- snprintf(buffer, 100, "\\n%d", si.id);
+ snprintf(buffer, 100, "\\ys__n%d", si.id);
RTLIL::SigSig conn;
if (si.type != G(NONE)) {
conn.first = si.bit;
@@ -1407,6 +1428,11 @@ struct AbcPass : public Pass {
log(" this attribute is a unique integer for each ABC process started. This\n");
log(" is useful for debugging the partitioning of clock domains.\n");
log("\n");
+ log(" -dress\n");
+ log(" run the 'dress' command after all other ABC commands. This aims to\n");
+ log(" preserve naming by an equivalence check between the original and post-ABC\n");
+ log(" netlists (experimental).\n");
+ log("\n");
log("When neither -liberty nor -lut is used, the Yosys standard cell library is\n");
log("loaded into ABC before the ABC script is executed.\n");
log("\n");
@@ -1441,6 +1467,7 @@ struct AbcPass : public Pass {
std::string delay_target, sop_inputs, sop_products, lutin_shared = "-S 1";
bool fast_mode = false, dff_mode = false, keepff = false, cleanup = true;
bool show_tempdir = false, sop_mode = false;
+ bool abc_dress = false;
vector<int> lut_costs;
markgroups = false;
@@ -1555,6 +1582,10 @@ struct AbcPass : public Pass {
map_mux16 = true;
continue;
}
+ if (arg == "-dress") {
+ abc_dress = true;
+ continue;
+ }
if (arg == "-g" && argidx+1 < args.size()) {
for (auto g : split_tokens(args[++argidx], ",")) {
vector<string> gate_list;
@@ -1704,7 +1735,7 @@ struct AbcPass : public Pass {
if (!dff_mode || !clk_str.empty()) {
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, lutin_shared, fast_mode, mod->selected_cells(), show_tempdir, sop_mode);
+ delay_target, sop_inputs, sop_products, lutin_shared, fast_mode, mod->selected_cells(), show_tempdir, sop_mode, abc_dress);
continue;
}
@@ -1849,7 +1880,7 @@ struct AbcPass : public Pass {
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_costs, !clk_sig.empty(), "$",
- keepff, delay_target, sop_inputs, sop_products, lutin_shared, fast_mode, it.second, show_tempdir, sop_mode);
+ keepff, delay_target, sop_inputs, sop_products, lutin_shared, fast_mode, it.second, show_tempdir, sop_mode, abc_dress);
assign_map.set(mod);
}
}
diff --git a/passes/techmap/deminout.cc b/passes/techmap/deminout.cc
index 9f0c7bf6..47d0ff41 100644
--- a/passes/techmap/deminout.cc
+++ b/passes/techmap/deminout.cc
@@ -83,9 +83,9 @@ struct DeminoutPass : public Pass {
for (auto bit : sigmap(conn.second))
bits_used.insert(bit);
- if (conn.first == "\\Y" && cell->type.in("$mux", "$pmux", "$_MUX_", "$_TBUF_"))
+ if (conn.first == "\\Y" && cell->type.in("$mux", "$pmux", "$_MUX_", "$_TBUF_", "$tribuf"))
{
- bool tribuf = (cell->type == "$_TBUF_");
+ bool tribuf = (cell->type == "$_TBUF_" || cell->type == "$tribuf");
if (!tribuf) {
for (auto &c : cell->connections()) {
@@ -113,7 +113,8 @@ struct DeminoutPass : public Pass {
{
if (bits_numports[bit] > 1 || bits_inout.count(bit))
new_input = true, new_output = true;
-
+ if (bit == State::S0 || bit == State::S1)
+ new_output = true;
if (bits_written.count(bit)) {
new_output = true;
if (bits_tribuf.count(bit))
diff --git a/passes/techmap/dff2dffe.cc b/passes/techmap/dff2dffe.cc
index 3291f5a4..7e104096 100644
--- a/passes/techmap/dff2dffe.cc
+++ b/passes/techmap/dff2dffe.cc
@@ -267,6 +267,10 @@ struct Dff2dffePass : public Pass {
log(" operate in the opposite direction: replace $dffe cells with combinations\n");
log(" of $dff and $mux cells. the options below are ignore in unmap mode.\n");
log("\n");
+ log(" -unmap-mince N\n");
+ log(" Same as -unmap but only unmap $dffe where the clock enable port\n");
+ log(" signal is used by less $dffe than the specified number\n");
+ log("\n");
log(" -direct <internal_gate_type> <external_gate_type>\n");
log(" map directly to external gate type. <internal_gate_type> can\n");
log(" be any internal gate-level FF cell (except $_DFFE_??_). the\n");
@@ -289,6 +293,7 @@ struct Dff2dffePass : public Pass {
log_header(design, "Executing DFF2DFFE pass (transform $dff to $dffe where applicable).\n");
bool unmap_mode = false;
+ int min_ce_use = -1;
dict<IdString, IdString> direct_dict;
size_t argidx;
@@ -297,6 +302,11 @@ struct Dff2dffePass : public Pass {
unmap_mode = true;
continue;
}
+ if (args[argidx] == "-unmap-mince" && argidx + 1 < args.size()) {
+ unmap_mode = true;
+ min_ce_use = std::stoi(args[++argidx]);
+ continue;
+ }
if (args[argidx] == "-direct" && argidx + 2 < args.size()) {
string direct_from = RTLIL::escape_id(args[++argidx]);
string direct_to = RTLIL::escape_id(args[++argidx]);
@@ -343,8 +353,21 @@ struct Dff2dffePass : public Pass {
if (!mod->has_processes_warn())
{
if (unmap_mode) {
+ SigMap sigmap(mod);
for (auto cell : mod->selected_cells()) {
if (cell->type == "$dffe") {
+ if (min_ce_use >= 0) {
+ int ce_use = 0;
+ for (auto cell_other : mod->selected_cells()) {
+ if (cell_other->type != cell->type)
+ continue;
+ if (sigmap(cell->getPort("\\EN")) == sigmap(cell_other->getPort("\\EN")))
+ ce_use++;
+ }
+ if (ce_use >= min_ce_use)
+ continue;
+ }
+
RTLIL::SigSpec tmp = mod->addWire(NEW_ID, GetSize(cell->getPort("\\D")));
mod->addDff(NEW_ID, cell->getPort("\\CLK"), tmp, cell->getPort("\\Q"), cell->getParam("\\CLK_POLARITY").as_bool());
if (cell->getParam("\\EN_POLARITY").as_bool())
@@ -355,6 +378,18 @@ struct Dff2dffePass : public Pass {
continue;
}
if (cell->type.substr(0, 7) == "$_DFFE_") {
+ if (min_ce_use >= 0) {
+ int ce_use = 0;
+ for (auto cell_other : mod->selected_cells()) {
+ if (cell_other->type != cell->type)
+ continue;
+ if (sigmap(cell->getPort("\\E")) == sigmap(cell_other->getPort("\\E")))
+ ce_use++;
+ }
+ if (ce_use >= min_ce_use)
+ continue;
+ }
+
bool clk_pol = cell->type.substr(7, 1) == "P";
bool en_pol = cell->type.substr(8, 1) == "P";
RTLIL::SigSpec tmp = mod->addWire(NEW_ID);
diff --git a/passes/techmap/dffinit.cc b/passes/techmap/dffinit.cc
index a8eecc97..48390488 100644
--- a/passes/techmap/dffinit.cc
+++ b/passes/techmap/dffinit.cc
@@ -43,18 +43,37 @@ struct DffinitPass : public Pass {
log(" initial value of 1 or 0. (multi-bit values are not supported in this\n");
log(" mode.)\n");
log("\n");
+ log(" -strinit <string for high> <string for low> \n");
+ log(" use string values in the command line to represent a single-bit\n");
+ log(" initial value of 1 or 0. (multi-bit values are not supported in this\n");
+ log(" mode.)\n");
+ log("\n");
+ log(" -noreinit\n");
+ log(" fail if the FF cell has already a defined initial value set in other\n");
+ log(" passes and the initial value of the net it drives is not equal to\n");
+ log(" the already defined initial value.\n");
+ log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
log_header(design, "Executing DFFINIT pass (set INIT param on FF cells).\n");
dict<IdString, dict<IdString, IdString>> ff_types;
- bool highlow_mode = false;
+ bool highlow_mode = false, noreinit = false;
+ std::string high_string, low_string;
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++) {
if (args[argidx] == "-highlow") {
highlow_mode = true;
+ high_string = "high";
+ low_string = "low";
+ continue;
+ }
+ if (args[argidx] == "-strinit" && argidx+2 < args.size()) {
+ highlow_mode = true;
+ high_string = args[++argidx];
+ low_string = args[++argidx];
continue;
}
if (args[argidx] == "-ff" && argidx+3 < args.size()) {
@@ -64,6 +83,10 @@ struct DffinitPass : public Pass {
ff_types[cell_name][output_port] = init_param;
continue;
}
+ if (args[argidx] == "-noreinit") {
+ noreinit = true;
+ continue;
+ }
break;
}
extra_args(args, argidx, design);
@@ -112,6 +135,10 @@ struct DffinitPass : public Pass {
continue;
while (GetSize(value.bits) <= i)
value.bits.push_back(State::S0);
+ if (noreinit && value.bits[i] != State::Sx && value.bits[i] != init_bits.at(sig[i]))
+ log_error("Trying to assign a different init value for %s.%s.%s which technically "
+ "have a conflicted init value.\n",
+ log_id(module), log_id(cell), log_id(it.second));
value.bits[i] = init_bits.at(sig[i]);
cleanup_bits.insert(sig[i]);
}
@@ -121,9 +148,9 @@ struct DffinitPass : public Pass {
log_error("Multi-bit init value for %s.%s.%s is incompatible with -highlow mode.\n",
log_id(module), log_id(cell), log_id(it.second));
if (value[0] == State::S1)
- value = Const("high");
+ value = Const(high_string);
else
- value = Const("low");
+ value = Const(low_string);
}
log("Setting %s.%s.%s (port=%s, net=%s) to %s.\n", log_id(module), log_id(cell), log_id(it.second),
diff --git a/passes/techmap/dfflibmap.cc b/passes/techmap/dfflibmap.cc
index 416ed2bd..274177a6 100644
--- a/passes/techmap/dfflibmap.cc
+++ b/passes/techmap/dfflibmap.cc
@@ -100,6 +100,18 @@ static bool parse_pin(LibertyAst *cell, LibertyAst *attr, std::string &pin_name,
for (auto child : cell->children)
if (child->id == "pin" && child->args.size() == 1 && child->args[0] == pin_name)
return true;
+
+ /* If we end up here, the pin specified in the attribute does not exist, which is an error,
+ or, the attribute contains an expression which we do not yet support.
+ For now, we'll simply produce a warning to let the user know something is up.
+ */
+ if (pin_name.find_first_of("^*|&") == std::string::npos) {
+ log_warning("Malformed liberty file - cannot find pin '%s' in cell '%s' - skipping.\n", pin_name.c_str(), cell->args[0].c_str());
+ }
+ else {
+ log_warning("Found unsupported expression '%s' in pin attribute of cell '%s' - skipping.\n", pin_name.c_str(), cell->args[0].c_str());
+ }
+
return false;
}
@@ -648,8 +660,8 @@ struct DfflibmapPass : public Pass {
map_adff_to_dff("$_DFF_PP0_", "$_DFF_P_");
map_adff_to_dff("$_DFF_PP1_", "$_DFF_P_");
- log(" final dff cell mappings:\n");
- logmap_all();
+ log(" final dff cell mappings:\n");
+ logmap_all();
for (auto &it : design->modules_)
if (design->selected(it.second) && !it.second->get_bool_attribute("\\blackbox"))
diff --git a/passes/techmap/flowmap.cc b/passes/techmap/flowmap.cc
new file mode 100644
index 00000000..0b7931e4
--- /dev/null
+++ b/passes/techmap/flowmap.cc
@@ -0,0 +1,1613 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2018 whitequark <whitequark@whitequark.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+// [[CITE]] FlowMap algorithm
+// Jason Cong; Yuzheng Ding, "An Optimal Technology Mapping Algorithm for Delay Optimization in Lookup-Table Based FPGA Designs,"
+// Computer-Aided Design of Integrated Circuits and Systems, IEEE Transactions on, Vol. 13, pp. 1-12, Jan. 1994.
+// doi: 10.1109/43.273754
+
+// [[CITE]] FlowMap-r algorithm
+// Jason Cong; Yuzheng Ding, "On Area/Depth Tradeoff in LUT-Based FPGA Technology Mapping,"
+// Very Large Scale Integration Systems, IEEE Transactions on, Vol. 2, June 1994.
+// doi: 10.1109/92.28574
+
+// Required reading material:
+//
+// Min-cut max-flow theorem:
+// https://www.coursera.org/lecture/algorithms-part2/maxflow-mincut-theorem-beb9G
+// FlowMap paper:
+// http://cadlab.cs.ucla.edu/~cong/papers/iccad92.pdf (short version)
+// https://limsk.ece.gatech.edu/book/papers/flowmap.pdf (long version)
+// FlowMap-r paper:
+// http://cadlab.cs.ucla.edu/~cong/papers/dac93.pdf (short version)
+// https://sci-hub.tw/10.1109/92.285741 (long version)
+
+// Notes on correspondence between paper and implementation:
+//
+// 1. In the FlowMap paper, the nodes are logic elements (analogous to Yosys cells) and edges are wires. However, in our implementation,
+// we use an inverted approach: the nodes are Yosys wire bits, and the edges are derived from (but aren't represented by) Yosys cells.
+// This may seem counterintuitive. Three observations may help understanding this. First, for a cell with a 1-bit Y output that is
+// the sole driver of its output net (which is the typical case), these representations are equivalent, because there is an exact
+// correspondence between cells and output wires. Second, in the paper, primary inputs (analogous to Yosys cell or module ports) are
+// nodes, and in Yosys, inputs are wires; our approach allows a direct mapping from both primary inputs and 1-output logic elements to
+// flow graph nodes. Third, Yosys cells may have multiple outputs or multi-bit outputs, and by using Yosys wire bits as flow graph nodes,
+// such cells are supported without any additional effort; any Yosys cell with n output wire bits ends up being split into n flow graph
+// nodes.
+//
+// 2. The FlowMap paper introduces three networks: Nt, Nt', and Nt''. The network Nt is directly represented by a subgraph of RTLIL graph,
+// which is parsed into an equivalent but easier to traverse representation in FlowmapWorker. The network Nt' is built explicitly
+// from a subgraph of Nt, and uses a similar representation in FlowGraph. The network Nt'' is implicit in FlowGraph, which is possible
+// because of the following observation: each Nt' node corresponds to an Nt'' edge of capacity 1, and each Nt' edge corresponds to
+// an Nt'' edge of capacity ∞. Therefore, we only need to explicitly record flow for Nt' edges and through Nt' nodes.
+//
+// 3. The FlowMap paper ambiguously states: "Moreover, we can find such a cut (X′′, X̅′′) by performing a depth first search starting at
+// the source s, and including in X′′ all the nodes which are reachable from s." This actually refers to a specific kind of search,
+// min-cut computation. Min-cut computation involves computing the set of nodes reachable from s by an undirected path with no full
+// (i.e. zero capacity) forward edges or empty (i.e. no flow) backward edges. In addition, the depth first search is required to compute
+// a max-volume max-flow min-cut specifically, because a max-flow min-cut is not, in general, unique.
+
+// Notes on implementation:
+//
+// 1. To compute depth optimal packing, an intermediate representation is used, where each cell with n output bits is split into n graph
+// nodes. Each such graph node is represented directly with the wire bit (RTLIL::SigBit instance) that corresponds to the output bit
+// it is created from. Fan-in and fan-out are represented explicitly by edge lists derived from the RTLIL graph. This IR never changes
+// after it has been computed.
+//
+// In terms of data, this IR is comprised of `inputs`, `outputs`, `nodes`, `edges_fw` and `edges_bw` fields.
+//
+// We call this IR "gate IR".
+//
+// 2. To compute area optimal packing, another intermediate representation is used, which consists of some K-feasible cone for every node
+// that exists in the gate IR. Immediately after depth optimal packing with FlowMap, each such cone occupies the lowest possible depth,
+// but this is not true in general, and transformations of this IR may change the cones, although each transformation has to keep each
+// cone K-feasible. In this IR, LUT fan-in and fan-out are represented explicitly by edge lists; if a K-feasible cone chosen for node A
+// includes nodes B and C, there are edges between all predecessors of A, B and C in the gate IR and node A in this IR. Moreover, in
+// this IR, cones may be *realized* or *derealized*. Only realized cones will end up mapped to actual LUTs in the output of this pass.
+//
+// Intuitively, this IR contains (some, ideally but not necessarily optimal) LUT representation for each input cell. By starting at outputs
+// and traversing the graph of this IR backwards, each K-feasible cone is converted to an actual LUT at the end of the pass. This is
+// the same as iterating through each realized LUT.
+//
+// The following are the invariants of this IR:
+// a) Each gate IR node corresponds to a K-feasible cut.
+// b) Each realized LUT is reachable through backward edges from some output.
+// c) The LUT fan-in is exactly the fan-in of its constituent gates minus the fan-out of its constituent gates.
+// The invariants are kept even for derealized LUTs, since the whole point of this IR is ease of packing, unpacking, and repacking LUTs.
+//
+// In terms of data, this IR is comprised of `lut_nodes` (the set of all realized LUTs), `lut_gates` (the map from a LUT to its
+// constituent gates), `lut_edges_fw` and `lut_edges_bw` fields. The `inputs` and `outputs` fields are shared with the gate IR.
+//
+// We call this IR "LUT IR".
+
+#include "kernel/yosys.h"
+#include "kernel/sigtools.h"
+#include "kernel/modtools.h"
+#include "kernel/consteval.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct GraphStyle
+{
+ string label;
+ string color, fillcolor;
+
+ GraphStyle(string label = "", string color = "black", string fillcolor = "") :
+ label(label), color(color), fillcolor(fillcolor) {}
+};
+
+static string dot_escape(string value)
+{
+ std::string escaped;
+ for (char c : value) {
+ if (c == '\n')
+ {
+ escaped += "\\n";
+ continue;
+ }
+ if (c == '\\' || c == '"')
+ escaped += "\\";
+ escaped += c;
+ }
+ return escaped;
+}
+
+static void dump_dot_graph(string filename,
+ pool<RTLIL::SigBit> nodes, dict<RTLIL::SigBit, pool<RTLIL::SigBit>> edges,
+ pool<RTLIL::SigBit> inputs, pool<RTLIL::SigBit> outputs,
+ std::function<GraphStyle(RTLIL::SigBit)> node_style =
+ [](RTLIL::SigBit) { return GraphStyle{}; },
+ std::function<GraphStyle(RTLIL::SigBit, RTLIL::SigBit)> edge_style =
+ [](RTLIL::SigBit, RTLIL::SigBit) { return GraphStyle{}; },
+ string name = "")
+{
+ FILE *f = fopen(filename.c_str(), "w");
+ fprintf(f, "digraph \"%s\" {\n", name.c_str());
+ fprintf(f, " rankdir=\"TB\";\n");
+
+ dict<RTLIL::SigBit, int> ids;
+ for (auto node : nodes)
+ {
+ ids[node] = ids.size();
+
+ string shape = "ellipse";
+ if (inputs[node])
+ shape = "box";
+ if (outputs[node])
+ shape = "octagon";
+ auto prop = node_style(node);
+ string style = "";
+ if (!prop.fillcolor.empty())
+ style = "filled";
+ fprintf(f, " n%d [ shape=%s, fontname=\"Monospace\", label=\"%s\", color=\"%s\", fillcolor=\"%s\", style=\"%s\" ];\n",
+ ids[node], shape.c_str(), dot_escape(prop.label.c_str()).c_str(), prop.color.c_str(), prop.fillcolor.c_str(), style.c_str());
+ }
+
+ fprintf(f, " { rank=\"source\"; ");
+ for (auto input : inputs)
+ if (nodes[input])
+ fprintf(f, "n%d; ", ids[input]);
+ fprintf(f, "}\n");
+
+ fprintf(f, " { rank=\"sink\"; ");
+ for (auto output : outputs)
+ if (nodes[output])
+ fprintf(f, "n%d; ", ids[output]);
+ fprintf(f, "}\n");
+
+ for (auto edge : edges)
+ {
+ auto source = edge.first;
+ for (auto sink : edge.second) {
+ if (nodes[source] && nodes[sink])
+ {
+ auto prop = edge_style(source, sink);
+ fprintf(f, " n%d -> n%d [ label=\"%s\", color=\"%s\", fillcolor=\"%s\" ];\n",
+ ids[source], ids[sink], dot_escape(prop.label.c_str()).c_str(), prop.color.c_str(), prop.fillcolor.c_str());
+ }
+ }
+ }
+
+ fprintf(f, "}\n");
+ fclose(f);
+}
+
+struct FlowGraph
+{
+ const RTLIL::SigBit source;
+ RTLIL::SigBit sink;
+ pool<RTLIL::SigBit> nodes = {source};
+ dict<RTLIL::SigBit, pool<RTLIL::SigBit>> edges_fw, edges_bw;
+
+ const int MAX_NODE_FLOW = 1;
+ dict<RTLIL::SigBit, int> node_flow;
+ dict<pair<RTLIL::SigBit, RTLIL::SigBit>, int> edge_flow;
+
+ dict<RTLIL::SigBit, pool<RTLIL::SigBit>> collapsed;
+
+ void dump_dot_graph(string filename)
+ {
+ auto node_style = [&](RTLIL::SigBit node) {
+ string label = (node == source) ? "(source)" : log_signal(node);
+ for (auto collapsed_node : collapsed[node])
+ label += stringf(" %s", log_signal(collapsed_node));
+ int flow = node_flow[node];
+ if (node != source && node != sink)
+ label += stringf("\n%d/%d", flow, MAX_NODE_FLOW);
+ else
+ label += stringf("\n%d/∞", flow);
+ return GraphStyle{label, flow < MAX_NODE_FLOW ? "green" : "black"};
+ };
+ auto edge_style = [&](RTLIL::SigBit source, RTLIL::SigBit sink) {
+ int flow = edge_flow[{source, sink}];
+ return GraphStyle{stringf("%d/∞", flow), flow > 0 ? "blue" : "black"};
+ };
+ ::dump_dot_graph(filename, nodes, edges_fw, {source}, {sink}, node_style, edge_style);
+ }
+
+ // Here, we are working on the Nt'' network, but our representation is the Nt' network.
+ // The difference between these is that where in Nt' we have a subgraph:
+ //
+ // v1 -> v2 -> v3
+ //
+ // in Nt'' we have a corresponding subgraph:
+ //
+ // v'1b -∞-> v'2t -f-> v'2b -∞-> v'3t
+ //
+ // To address this, we split each node v into two nodes, v't and v'b. This representation is virtual,
+ // in the sense that nodes v't and v'b are overlaid on top of the original node v, and only exist
+ // in paths and worklists.
+
+ struct NodePrime
+ {
+ RTLIL::SigBit node;
+ bool is_bottom;
+
+ NodePrime(RTLIL::SigBit node, bool is_bottom) :
+ node(node), is_bottom(is_bottom) {}
+
+ bool operator==(const NodePrime &other) const
+ {
+ return node == other.node && is_bottom == other.is_bottom;
+ }
+ bool operator!=(const NodePrime &other) const
+ {
+ return !(*this == other);
+ }
+ unsigned int hash() const
+ {
+ return hash_ops<pair<RTLIL::SigBit, int>>::hash({node, is_bottom});
+ }
+
+ static NodePrime top(RTLIL::SigBit node)
+ {
+ return NodePrime(node, /*is_bottom=*/false);
+ }
+
+ static NodePrime bottom(RTLIL::SigBit node)
+ {
+ return NodePrime(node, /*is_bottom=*/true);
+ }
+
+ NodePrime as_top() const
+ {
+ log_assert(is_bottom);
+ return top(node);
+ }
+
+ NodePrime as_bottom() const
+ {
+ log_assert(!is_bottom);
+ return bottom(node);
+ }
+ };
+
+ bool find_augmenting_path(bool commit)
+ {
+ NodePrime source_prime = {source, true};
+ NodePrime sink_prime = {sink, false};
+ vector<NodePrime> path = {source_prime};
+ pool<NodePrime> visited = {};
+ bool found;
+ do {
+ found = false;
+
+ auto node_prime = path.back();
+ visited.insert(node_prime);
+
+ if (!node_prime.is_bottom) // vt
+ {
+ if (!visited[node_prime.as_bottom()] && node_flow[node_prime.node] < MAX_NODE_FLOW)
+ {
+ path.push_back(node_prime.as_bottom());
+ found = true;
+ }
+ else
+ {
+ for (auto node_pred : edges_bw[node_prime.node])
+ {
+ if (!visited[NodePrime::bottom(node_pred)] && edge_flow[{node_pred, node_prime.node}] > 0)
+ {
+ path.push_back(NodePrime::bottom(node_pred));
+ found = true;
+ break;
+ }
+ }
+ }
+ }
+ else // vb
+ {
+ if (!visited[node_prime.as_top()] && node_flow[node_prime.node] > 0)
+ {
+ path.push_back(node_prime.as_top());
+ found = true;
+ }
+ else
+ {
+ for (auto node_succ : edges_fw[node_prime.node])
+ {
+ if (!visited[NodePrime::top(node_succ)] /* && edge_flow[...] < ∞ */)
+ {
+ path.push_back(NodePrime::top(node_succ));
+ found = true;
+ break;
+ }
+ }
+ }
+ }
+
+ if (!found && path.size() > 1)
+ {
+ path.pop_back();
+ found = true;
+ }
+ } while(path.back() != sink_prime && found);
+
+ if (commit && path.back() == sink_prime)
+ {
+ auto prev_prime = path.front();
+ for (auto node_prime : path)
+ {
+ if (node_prime == source_prime)
+ continue;
+
+ log_assert(prev_prime.is_bottom ^ node_prime.is_bottom);
+ if (prev_prime.node == node_prime.node)
+ {
+ auto node = node_prime.node;
+ if (!prev_prime.is_bottom && node_prime.is_bottom)
+ {
+ log_assert(node_flow[node] == 0);
+ node_flow[node]++;
+ }
+ else
+ {
+ log_assert(node_flow[node] != 0);
+ node_flow[node]--;
+ }
+ }
+ else
+ {
+ if (prev_prime.is_bottom && !node_prime.is_bottom)
+ {
+ log_assert(true /* edge_flow[...] < ∞ */);
+ edge_flow[{prev_prime.node, node_prime.node}]++;
+ }
+ else
+ {
+ log_assert((edge_flow[{node_prime.node, prev_prime.node}] > 0));
+ edge_flow[{node_prime.node, prev_prime.node}]--;
+ }
+ }
+ prev_prime = node_prime;
+ }
+
+ node_flow[source]++;
+ node_flow[sink]++;
+ }
+ return path.back() == sink_prime;
+ }
+
+ int maximum_flow(int order)
+ {
+ int flow = 0;
+ while (flow < order && find_augmenting_path(/*commit=*/true))
+ flow++;
+ return flow + find_augmenting_path(/*commit=*/false);
+ }
+
+ pair<pool<RTLIL::SigBit>, pool<RTLIL::SigBit>> edge_cut()
+ {
+ pool<RTLIL::SigBit> x, xi;
+
+ NodePrime source_prime = {source, true};
+ NodePrime sink_prime = {sink, false};
+ pool<NodePrime> visited;
+ vector<NodePrime> worklist = {source_prime};
+ while (!worklist.empty())
+ {
+ auto node_prime = worklist.back();
+ worklist.pop_back();
+ if (visited[node_prime])
+ continue;
+ visited.insert(node_prime);
+
+ if (!node_prime.is_bottom)
+ x.insert(node_prime.node);
+
+ // Mincut is constructed by traversing a graph in an undirected way along forward edges that aren't full, or backward edges
+ // that aren't empty.
+ if (!node_prime.is_bottom) // top
+ {
+ if (node_flow[node_prime.node] < MAX_NODE_FLOW)
+ worklist.push_back(node_prime.as_bottom());
+ for (auto node_pred : edges_bw[node_prime.node])
+ if (edge_flow[{node_pred, node_prime.node}] > 0)
+ worklist.push_back(NodePrime::bottom(node_pred));
+ }
+ else // bottom
+ {
+ if (node_flow[node_prime.node] > 0)
+ worklist.push_back(node_prime.as_top());
+ for (auto node_succ : edges_fw[node_prime.node])
+ if (true /* edge_flow[...] < ∞ */)
+ worklist.push_back(NodePrime::top(node_succ));
+ }
+ }
+
+ for (auto node : nodes)
+ if (!x[node])
+ xi.insert(node);
+
+ for (auto collapsed_node : collapsed[sink])
+ xi.insert(collapsed_node);
+
+ log_assert(!x[sink] && xi[sink]);
+ return {x, xi};
+ }
+};
+
+struct FlowmapWorker
+{
+ int order;
+ int r_alpha, r_beta, r_gamma;
+ bool debug, debug_relax;
+
+ RTLIL::Module *module;
+ SigMap sigmap;
+ ModIndex index;
+
+ dict<RTLIL::SigBit, ModIndex::PortInfo> node_origins;
+
+ // Gate IR
+ pool<RTLIL::SigBit> nodes, inputs, outputs;
+ dict<RTLIL::SigBit, pool<RTLIL::SigBit>> edges_fw, edges_bw;
+ dict<RTLIL::SigBit, int> labels;
+
+ // LUT IR
+ pool<RTLIL::SigBit> lut_nodes;
+ dict<RTLIL::SigBit, pool<RTLIL::SigBit>> lut_gates;
+ dict<RTLIL::SigBit, pool<RTLIL::SigBit>> lut_edges_fw, lut_edges_bw;
+ dict<RTLIL::SigBit, int> lut_depths, lut_altitudes, lut_slacks;
+
+ int gate_count = 0, lut_count = 0, packed_count = 0;
+ int gate_area = 0, lut_area = 0;
+
+ enum class GraphMode {
+ Label,
+ Cut,
+ Slack,
+ };
+
+ void dump_dot_graph(string filename, GraphMode mode,
+ pool<RTLIL::SigBit> subgraph_nodes = {}, dict<RTLIL::SigBit, pool<RTLIL::SigBit>> subgraph_edges = {},
+ dict<RTLIL::SigBit, pool<RTLIL::SigBit>> collapsed = {},
+ pair<pool<RTLIL::SigBit>, pool<RTLIL::SigBit>> cut = {})
+ {
+ if (subgraph_nodes.empty())
+ subgraph_nodes = nodes;
+ if (subgraph_edges.empty())
+ subgraph_edges = edges_fw;
+
+ auto node_style = [&](RTLIL::SigBit node) {
+ string label = log_signal(node);
+ for (auto collapsed_node : collapsed[node])
+ if (collapsed_node != node)
+ label += stringf(" %s", log_signal(collapsed_node));
+ switch (mode)
+ {
+ case GraphMode::Label:
+ if (labels[node] == -1)
+ {
+ label += "\nl=?";
+ return GraphStyle{label};
+ }
+ else
+ {
+ label += stringf("\nl=%d", labels[node]);
+ string fillcolor = stringf("/set311/%d", 1 + labels[node] % 11);
+ return GraphStyle{label, "", fillcolor};
+ }
+
+ case GraphMode::Cut:
+ if (cut.first[node])
+ return GraphStyle{label, "blue"};
+ if (cut.second[node])
+ return GraphStyle{label, "red"};
+ return GraphStyle{label};
+
+ case GraphMode::Slack:
+ label += stringf("\nd=%d a=%d\ns=%d", lut_depths[node], lut_altitudes[node], lut_slacks[node]);
+ return GraphStyle{label, lut_slacks[node] == 0 ? "red" : "black"};
+ }
+ return GraphStyle{label};
+ };
+ auto edge_style = [&](RTLIL::SigBit, RTLIL::SigBit) {
+ return GraphStyle{};
+ };
+ ::dump_dot_graph(filename, subgraph_nodes, subgraph_edges, inputs, outputs, node_style, edge_style, module->name.str());
+ }
+
+ void dump_dot_lut_graph(string filename, GraphMode mode)
+ {
+ pool<RTLIL::SigBit> lut_and_input_nodes;
+ lut_and_input_nodes.insert(lut_nodes.begin(), lut_nodes.end());
+ lut_and_input_nodes.insert(inputs.begin(), inputs.end());
+ dump_dot_graph(filename, mode, lut_and_input_nodes, lut_edges_fw, lut_gates);
+ }
+
+ pool<RTLIL::SigBit> find_subgraph(RTLIL::SigBit sink)
+ {
+ pool<RTLIL::SigBit> subgraph;
+ pool<RTLIL::SigBit> worklist = {sink};
+ while (!worklist.empty())
+ {
+ auto node = worklist.pop();
+ subgraph.insert(node);
+ for (auto source : edges_bw[node])
+ {
+ if (!subgraph[source])
+ worklist.insert(source);
+ }
+ }
+ return subgraph;
+ }
+
+ FlowGraph build_flow_graph(RTLIL::SigBit sink, int p)
+ {
+ FlowGraph flow_graph;
+ flow_graph.sink = sink;
+
+ pool<RTLIL::SigBit> worklist = {sink}, visited;
+ while (!worklist.empty())
+ {
+ auto node = worklist.pop();
+ visited.insert(node);
+
+ auto collapsed_node = labels[node] == p ? sink : node;
+ if (node != collapsed_node)
+ flow_graph.collapsed[collapsed_node].insert(node);
+ flow_graph.nodes.insert(collapsed_node);
+
+ for (auto node_pred : edges_bw[node])
+ {
+ auto collapsed_node_pred = labels[node_pred] == p ? sink : node_pred;
+ if (node_pred != collapsed_node_pred)
+ flow_graph.collapsed[collapsed_node_pred].insert(node_pred);
+ if (collapsed_node != collapsed_node_pred)
+ {
+ flow_graph.edges_bw[collapsed_node].insert(collapsed_node_pred);
+ flow_graph.edges_fw[collapsed_node_pred].insert(collapsed_node);
+ }
+ if (inputs[node_pred])
+ {
+ flow_graph.edges_bw[collapsed_node_pred].insert(flow_graph.source);
+ flow_graph.edges_fw[flow_graph.source].insert(collapsed_node_pred);
+ }
+
+ if (!visited[node_pred])
+ worklist.insert(node_pred);
+ }
+ }
+ return flow_graph;
+ }
+
+ void discover_nodes(pool<IdString> cell_types)
+ {
+ for (auto cell : module->selected_cells())
+ {
+ if (!cell_types[cell->type])
+ continue;
+
+ if (!cell->known())
+ log_error("Cell %s (%s.%s) is unknown.\n", cell->type.c_str(), log_id(module), log_id(cell));
+
+ pool<RTLIL::SigBit> fanout;
+ for (auto conn : cell->connections())
+ {
+ if (!cell->output(conn.first)) continue;
+ int offset = -1;
+ for (auto bit : conn.second)
+ {
+ offset++;
+ if (!bit.wire) continue;
+ auto mapped_bit = sigmap(bit);
+ if (nodes[mapped_bit])
+ log_error("Multiple drivers found for wire %s.\n", log_signal(mapped_bit));
+ nodes.insert(mapped_bit);
+ node_origins[mapped_bit] = ModIndex::PortInfo(cell, conn.first, offset);
+ fanout.insert(mapped_bit);
+ }
+ }
+
+ int fanin = 0;
+ for (auto conn : cell->connections())
+ {
+ if (!cell->input(conn.first)) continue;
+ for (auto bit : sigmap(conn.second))
+ {
+ if (!bit.wire) continue;
+ for (auto fanout_bit : fanout)
+ {
+ edges_fw[bit].insert(fanout_bit);
+ edges_bw[fanout_bit].insert(bit);
+ }
+ fanin++;
+ }
+ }
+
+ if (fanin > order)
+ log_error("Cell %s (%s.%s) with fan-in %d cannot be mapped to a %d-LUT.\n",
+ cell->type.c_str(), log_id(module), log_id(cell), fanin, order);
+
+ gate_count++;
+ gate_area += 1 << fanin;
+ }
+
+ for (auto edge : edges_fw)
+ {
+ if (!nodes[edge.first])
+ {
+ inputs.insert(edge.first);
+ nodes.insert(edge.first);
+ }
+ }
+
+ for (auto node : nodes)
+ {
+ auto node_info = index.query(node);
+ if (node_info->is_output && !inputs[node])
+ outputs.insert(node);
+ for (auto port : node_info->ports)
+ if (!cell_types[port.cell->type] && !inputs[node])
+ outputs.insert(node);
+ }
+
+ if (debug)
+ {
+ dump_dot_graph("flowmap-initial.dot", GraphMode::Label);
+ log("Dumped initial graph to `flowmap-initial.dot`.\n");
+ }
+ }
+
+ void label_nodes()
+ {
+ for (auto node : nodes)
+ labels[node] = -1;
+ for (auto input : inputs)
+ {
+ if (input.wire->attributes.count("\\$flowmap_level"))
+ labels[input] = input.wire->attributes["\\$flowmap_level"].as_int();
+ else
+ labels[input] = 0;
+ }
+
+ pool<RTLIL::SigBit> worklist = nodes;
+ int debug_num = 0;
+ while (!worklist.empty())
+ {
+ auto sink = worklist.pop();
+ if (labels[sink] != -1)
+ continue;
+
+ bool inputs_have_labels = true;
+ for (auto sink_input : edges_bw[sink])
+ {
+ if (labels[sink_input] == -1)
+ {
+ inputs_have_labels = false;
+ break;
+ }
+ }
+ if (!inputs_have_labels)
+ continue;
+
+ if (debug)
+ {
+ debug_num++;
+ log("Examining subgraph %d rooted in %s.\n", debug_num, log_signal(sink));
+ }
+
+ pool<RTLIL::SigBit> subgraph = find_subgraph(sink);
+
+ int p = 1;
+ for (auto subgraph_node : subgraph)
+ p = max(p, labels[subgraph_node]);
+
+ FlowGraph flow_graph = build_flow_graph(sink, p);
+ int flow = flow_graph.maximum_flow(order);
+ pool<RTLIL::SigBit> x, xi;
+ if (flow <= order)
+ {
+ labels[sink] = p;
+ auto cut = flow_graph.edge_cut();
+ x = cut.first;
+ xi = cut.second;
+ }
+ else
+ {
+ labels[sink] = p + 1;
+ x = subgraph;
+ x.erase(sink);
+ xi.insert(sink);
+ }
+ lut_gates[sink] = xi;
+
+ pool<RTLIL::SigBit> k;
+ for (auto xi_node : xi)
+ {
+ for (auto xi_node_pred : edges_bw[xi_node])
+ if (x[xi_node_pred])
+ k.insert(xi_node_pred);
+ }
+ log_assert((int)k.size() <= order);
+ lut_edges_bw[sink] = k;
+ for (auto k_node : k)
+ lut_edges_fw[k_node].insert(sink);
+
+ if (debug)
+ {
+ log(" Maximum flow: %d. Assigned label %d.\n", flow, labels[sink]);
+ dump_dot_graph(stringf("flowmap-%d-sub.dot", debug_num), GraphMode::Cut, subgraph, {}, {}, {x, xi});
+ log(" Dumped subgraph to `flowmap-%d-sub.dot`.\n", debug_num);
+ flow_graph.dump_dot_graph(stringf("flowmap-%d-flow.dot", debug_num));
+ log(" Dumped flow graph to `flowmap-%d-flow.dot`.\n", debug_num);
+ log(" LUT inputs:");
+ for (auto k_node : k)
+ log(" %s", log_signal(k_node));
+ log(".\n");
+ log(" LUT packed gates:");
+ for (auto xi_node : xi)
+ log(" %s", log_signal(xi_node));
+ log(".\n");
+ }
+
+ for (auto sink_succ : edges_fw[sink])
+ worklist.insert(sink_succ);
+ }
+
+ if (debug)
+ {
+ dump_dot_graph("flowmap-labeled.dot", GraphMode::Label);
+ log("Dumped labeled graph to `flowmap-labeled.dot`.\n");
+ }
+ }
+
+ int map_luts()
+ {
+ pool<RTLIL::SigBit> worklist = outputs;
+ while (!worklist.empty())
+ {
+ auto lut_node = worklist.pop();
+ lut_nodes.insert(lut_node);
+ for (auto input_node : lut_edges_bw[lut_node])
+ if (!lut_nodes[input_node] && !inputs[input_node])
+ worklist.insert(input_node);
+ }
+
+ int depth = 0;
+ for (auto label : labels)
+ depth = max(depth, label.second);
+ log("Mapped to %zu LUTs with maximum depth %d.\n", lut_nodes.size(), depth);
+
+ if (debug)
+ {
+ dump_dot_lut_graph("flowmap-mapped.dot", GraphMode::Label);
+ log("Dumped mapped graph to `flowmap-mapped.dot`.\n");
+ }
+
+ return depth;
+ }
+
+ void realize_derealize_lut(RTLIL::SigBit lut, pool<RTLIL::SigBit> *changed = nullptr)
+ {
+ pool<RTLIL::SigBit> worklist = {lut};
+ while (!worklist.empty())
+ {
+ auto lut = worklist.pop();
+ if (inputs[lut])
+ continue;
+
+ bool realized_successors = false;
+ for (auto lut_succ : lut_edges_fw[lut])
+ if (lut_nodes[lut_succ])
+ realized_successors = true;
+
+ if (realized_successors && !lut_nodes[lut])
+ lut_nodes.insert(lut);
+ else if (!realized_successors && lut_nodes[lut])
+ lut_nodes.erase(lut);
+ else
+ continue;
+
+ for (auto lut_pred : lut_edges_bw[lut])
+ worklist.insert(lut_pred);
+
+ if (changed)
+ changed->insert(lut);
+ }
+ }
+
+ void add_lut_edge(RTLIL::SigBit pred, RTLIL::SigBit succ, pool<RTLIL::SigBit> *changed = nullptr)
+ {
+ log_assert(!lut_edges_fw[pred][succ] && !lut_edges_bw[succ][pred]);
+ log_assert((int)lut_edges_bw[succ].size() < order);
+
+ lut_edges_fw[pred].insert(succ);
+ lut_edges_bw[succ].insert(pred);
+ realize_derealize_lut(pred, changed);
+
+ if (changed)
+ {
+ changed->insert(pred);
+ changed->insert(succ);
+ }
+ }
+
+ void remove_lut_edge(RTLIL::SigBit pred, RTLIL::SigBit succ, pool<RTLIL::SigBit> *changed = nullptr)
+ {
+ log_assert(lut_edges_fw[pred][succ] && lut_edges_bw[succ][pred]);
+
+ lut_edges_fw[pred].erase(succ);
+ lut_edges_bw[succ].erase(pred);
+ realize_derealize_lut(pred, changed);
+
+ if (changed)
+ {
+ if (lut_nodes[pred])
+ changed->insert(pred);
+ changed->insert(succ);
+ }
+ }
+
+ pair<pool<RTLIL::SigBit>, pool<RTLIL::SigBit>> cut_lut_at_gate(RTLIL::SigBit lut, RTLIL::SigBit lut_gate)
+ {
+ pool<RTLIL::SigBit> gate_inputs = lut_edges_bw[lut];
+ pool<RTLIL::SigBit> other_inputs;
+ pool<RTLIL::SigBit> worklist = {lut};
+ while (!worklist.empty())
+ {
+ auto node = worklist.pop();
+ for (auto node_pred : edges_bw[node])
+ {
+ if (node_pred == lut_gate)
+ continue;
+ if (lut_gates[lut][node_pred])
+ worklist.insert(node_pred);
+ else
+ {
+ gate_inputs.erase(node_pred);
+ other_inputs.insert(node_pred);
+ }
+ }
+ }
+ return {gate_inputs, other_inputs};
+ }
+
+ void compute_lut_distances(dict<RTLIL::SigBit, int> &lut_distances, bool forward,
+ pool<RTLIL::SigBit> initial = {}, pool<RTLIL::SigBit> *changed = nullptr)
+ {
+ pool<RTLIL::SigBit> terminals = forward ? inputs : outputs;
+ auto &lut_edges_next = forward ? lut_edges_fw : lut_edges_bw;
+ auto &lut_edges_prev = forward ? lut_edges_bw : lut_edges_fw;
+
+ if (initial.empty())
+ initial = terminals;
+ for (auto node : initial)
+ lut_distances.erase(node);
+
+ pool<RTLIL::SigBit> worklist = initial;
+ while (!worklist.empty())
+ {
+ auto lut = worklist.pop();
+ int lut_distance = 0;
+ if (forward && inputs[lut])
+ lut_distance = labels[lut]; // to support (* $flowmap_level=n *)
+ for (auto lut_prev : lut_edges_prev[lut])
+ if ((lut_nodes[lut_prev] || inputs[lut_prev]) && lut_distances.count(lut_prev))
+ lut_distance = max(lut_distance, lut_distances[lut_prev] + 1);
+ if (!lut_distances.count(lut) || lut_distances[lut] != lut_distance)
+ {
+ lut_distances[lut] = lut_distance;
+ if (changed != nullptr && !inputs[lut])
+ changed->insert(lut);
+ for (auto lut_next : lut_edges_next[lut])
+ if (lut_nodes[lut_next] || inputs[lut_next])
+ worklist.insert(lut_next);
+ }
+ }
+ }
+
+ void check_lut_distances(const dict<RTLIL::SigBit, int> &lut_distances, bool forward)
+ {
+ dict<RTLIL::SigBit, int> gold_lut_distances;
+ compute_lut_distances(gold_lut_distances, forward);
+ for (auto lut_distance : lut_distances)
+ if (lut_nodes[lut_distance.first])
+ log_assert(lut_distance.second == gold_lut_distances[lut_distance.first]);
+ }
+
+ // LUT depth is the length of the longest path from any input in LUT fan-in to LUT.
+ // LUT altitude (for lack of a better term) is the length of the longest path from LUT to any output in LUT fan-out.
+ void update_lut_depths_altitudes(pool<RTLIL::SigBit> worklist = {}, pool<RTLIL::SigBit> *changed = nullptr)
+ {
+ compute_lut_distances(lut_depths, /*forward=*/true, worklist, changed);
+ compute_lut_distances(lut_altitudes, /*forward=*/false, worklist, changed);
+ if (debug_relax && !worklist.empty()) {
+ check_lut_distances(lut_depths, /*forward=*/true);
+ check_lut_distances(lut_altitudes, /*forward=*/false);
+ }
+ }
+
+ // LUT critical output set is the set of outputs whose depth will increase (equivalently, slack will decrease) if the depth of
+ // the LUT increases. (This is referred to as RPOv for LUTv in the paper.)
+ void compute_lut_critical_outputs(dict<RTLIL::SigBit, pool<RTLIL::SigBit>> &lut_critical_outputs,
+ pool<RTLIL::SigBit> worklist = {})
+ {
+ if (worklist.empty())
+ worklist = lut_nodes;
+
+ while (!worklist.empty())
+ {
+ bool updated_some = false;
+ for (auto lut : worklist)
+ {
+ if (outputs[lut])
+ lut_critical_outputs[lut] = {lut};
+ else
+ {
+ bool all_succ_computed = true;
+ lut_critical_outputs[lut] = {};
+ for (auto lut_succ : lut_edges_fw[lut])
+ {
+ if (lut_nodes[lut_succ] && lut_depths[lut_succ] == lut_depths[lut] + 1)
+ {
+ if (lut_critical_outputs.count(lut_succ))
+ lut_critical_outputs[lut].insert(lut_critical_outputs[lut_succ].begin(), lut_critical_outputs[lut_succ].end());
+ else
+ {
+ all_succ_computed = false;
+ break;
+ }
+ }
+ }
+ if (!all_succ_computed)
+ {
+ lut_critical_outputs.erase(lut);
+ continue;
+ }
+ }
+ worklist.erase(lut);
+ updated_some = true;
+ }
+ log_assert(updated_some);
+ }
+ }
+
+ // Invalidating LUT critical output sets is tricky, because increasing the depth of a LUT may take other, adjacent LUTs off the critical
+ // path to the output. Conservatively, if we increase depth of some LUT, every LUT in its input cone needs to have its critical output
+ // set invalidated, too.
+ pool<RTLIL::SigBit> invalidate_lut_critical_outputs(dict<RTLIL::SigBit, pool<RTLIL::SigBit>> &lut_critical_outputs,
+ pool<RTLIL::SigBit> worklist)
+ {
+ pool<RTLIL::SigBit> changed;
+ while (!worklist.empty())
+ {
+ auto lut = worklist.pop();
+ changed.insert(lut);
+ lut_critical_outputs.erase(lut);
+ for (auto lut_pred : lut_edges_bw[lut])
+ {
+ if (lut_nodes[lut_pred] && !changed[lut_pred])
+ {
+ changed.insert(lut_pred);
+ worklist.insert(lut_pred);
+ }
+ }
+ }
+ return changed;
+ }
+
+ void check_lut_critical_outputs(const dict<RTLIL::SigBit, pool<RTLIL::SigBit>> &lut_critical_outputs)
+ {
+ dict<RTLIL::SigBit, pool<RTLIL::SigBit>> gold_lut_critical_outputs;
+ compute_lut_critical_outputs(gold_lut_critical_outputs);
+ for (auto lut_critical_output : lut_critical_outputs)
+ if (lut_nodes[lut_critical_output.first])
+ log_assert(lut_critical_output.second == gold_lut_critical_outputs[lut_critical_output.first]);
+ }
+
+ void update_lut_critical_outputs(dict<RTLIL::SigBit, pool<RTLIL::SigBit>> &lut_critical_outputs,
+ pool<RTLIL::SigBit> worklist = {})
+ {
+ if (!worklist.empty())
+ {
+ pool<RTLIL::SigBit> invalidated = invalidate_lut_critical_outputs(lut_critical_outputs, worklist);
+ compute_lut_critical_outputs(lut_critical_outputs, invalidated);
+ check_lut_critical_outputs(lut_critical_outputs);
+ }
+ else
+ compute_lut_critical_outputs(lut_critical_outputs);
+ }
+
+ void update_breaking_node_potentials(dict<RTLIL::SigBit, dict<RTLIL::SigBit, int>> &potentials,
+ const dict<RTLIL::SigBit, pool<RTLIL::SigBit>> &lut_critical_outputs)
+ {
+ for (auto lut : lut_nodes)
+ {
+ if (potentials.count(lut))
+ continue;
+ if (lut_gates[lut].size() == 1 || lut_slacks[lut] == 0)
+ continue;
+
+ if (debug_relax)
+ log(" Computing potentials for LUT %s.\n", log_signal(lut));
+
+ for (auto lut_gate : lut_gates[lut])
+ {
+ if (lut == lut_gate)
+ continue;
+
+ if (debug_relax)
+ log(" Considering breaking node %s.\n", log_signal(lut_gate));
+
+ int r_ex, r_im, r_slk;
+
+ auto cut_inputs = cut_lut_at_gate(lut, lut_gate);
+ pool<RTLIL::SigBit> gate_inputs = cut_inputs.first, other_inputs = cut_inputs.second;
+ if (gate_inputs.empty() && (int)other_inputs.size() == order)
+ {
+ if (debug_relax)
+ log(" Breaking would result in a (k+1)-LUT.\n");
+ continue;
+ }
+
+ pool<RTLIL::SigBit> elim_fanin_luts;
+ for (auto gate_input : gate_inputs)
+ {
+ if (lut_edges_fw[gate_input].size() == 1)
+ {
+ log_assert(lut_edges_fw[gate_input][lut]);
+ elim_fanin_luts.insert(gate_input);
+ }
+ }
+ if (debug_relax)
+ {
+ if (!lut_nodes[lut_gate])
+ log(" Breaking requires a new LUT.\n");
+ if (!gate_inputs.empty())
+ {
+ log(" Breaking eliminates LUT inputs");
+ for (auto gate_input : gate_inputs)
+ log(" %s", log_signal(gate_input));
+ log(".\n");
+ }
+ if (!elim_fanin_luts.empty())
+ {
+ log(" Breaking eliminates fan-in LUTs");
+ for (auto elim_fanin_lut : elim_fanin_luts)
+ log(" %s", log_signal(elim_fanin_lut));
+ log(".\n");
+ }
+ }
+ r_ex = (lut_nodes[lut_gate] ? 0 : -1) + elim_fanin_luts.size();
+
+ pool<pair<RTLIL::SigBit, RTLIL::SigBit>> maybe_mergeable_luts;
+
+ // Try to merge LUTv with one of its successors.
+ RTLIL::SigBit last_lut_succ;
+ int fanout = 0;
+ for (auto lut_succ : lut_edges_fw[lut])
+ {
+ if (lut_nodes[lut_succ])
+ {
+ fanout++;
+ last_lut_succ = lut_succ;
+ }
+ }
+ if (fanout == 1)
+ maybe_mergeable_luts.insert({lut, last_lut_succ});
+
+ // Try to merge LUTv with one of its predecessors.
+ for (auto lut_pred : other_inputs)
+ {
+ int fanout = 0;
+ for (auto lut_pred_succ : lut_edges_fw[lut_pred])
+ if (lut_nodes[lut_pred_succ] || lut_pred_succ == lut_gate)
+ fanout++;
+ if (fanout == 1)
+ maybe_mergeable_luts.insert({lut_pred, lut});
+ }
+
+ // Try to merge LUTw with one of its predecessors.
+ for (auto lut_gate_pred : lut_edges_bw[lut_gate])
+ {
+ int fanout = 0;
+ for (auto lut_gate_pred_succ : lut_edges_fw[lut_gate_pred])
+ if (lut_nodes[lut_gate_pred_succ] || lut_gate_pred_succ == lut_gate)
+ fanout++;
+ if (fanout == 1)
+ maybe_mergeable_luts.insert({lut_gate_pred, lut_gate});
+ }
+
+ r_im = 0;
+ for (auto maybe_mergeable_pair : maybe_mergeable_luts)
+ {
+ log_assert(lut_edges_fw[maybe_mergeable_pair.first][maybe_mergeable_pair.second]);
+ pool<RTLIL::SigBit> unique_inputs;
+ for (auto fst_lut_pred : lut_edges_bw[maybe_mergeable_pair.first])
+ if (lut_nodes[fst_lut_pred])
+ unique_inputs.insert(fst_lut_pred);
+ for (auto snd_lut_pred : lut_edges_bw[maybe_mergeable_pair.second])
+ if (lut_nodes[snd_lut_pred])
+ unique_inputs.insert(snd_lut_pred);
+ unique_inputs.erase(maybe_mergeable_pair.first);
+ if ((int)unique_inputs.size() <= order)
+ {
+ if (debug_relax)
+ log(" Breaking may allow merging %s and %s.\n",
+ log_signal(maybe_mergeable_pair.first), log_signal(maybe_mergeable_pair.second));
+ r_im++;
+ }
+ }
+
+ int lut_gate_depth;
+ if (lut_nodes[lut_gate])
+ lut_gate_depth = lut_depths[lut_gate];
+ else
+ {
+ lut_gate_depth = 0;
+ for (auto lut_gate_pred : lut_edges_bw[lut_gate])
+ lut_gate_depth = max(lut_gate_depth, lut_depths[lut_gate_pred] + 1);
+ }
+ if (lut_depths[lut] >= lut_gate_depth + 1)
+ r_slk = 0;
+ else
+ {
+ int depth_delta = lut_gate_depth + 1 - lut_depths[lut];
+ if (depth_delta > lut_slacks[lut])
+ {
+ if (debug_relax)
+ log(" Breaking would increase depth by %d, which is more than available slack.\n", depth_delta);
+ continue;
+ }
+
+ if (debug_relax)
+ {
+ log(" Breaking increases depth of LUT by %d.\n", depth_delta);
+ if (lut_critical_outputs.at(lut).size())
+ {
+ log(" Breaking decreases slack of outputs");
+ for (auto lut_critical_output : lut_critical_outputs.at(lut))
+ {
+ log(" %s", log_signal(lut_critical_output));
+ log_assert(lut_slacks[lut_critical_output] > 0);
+ }
+ log(".\n");
+ }
+ }
+ r_slk = lut_critical_outputs.at(lut).size() * depth_delta;
+ }
+
+ int p = 100 * (r_alpha * r_ex + r_beta * r_im + r_gamma) / (r_slk + 1);
+ if (debug_relax)
+ log(" Potential for breaking node %s: %d (Rex=%d, Rim=%d, Rslk=%d).\n",
+ log_signal(lut_gate), p, r_ex, r_im, r_slk);
+ potentials[lut][lut_gate] = p;
+ }
+ }
+ }
+
+ bool relax_depth_for_bound(bool first, int depth_bound, dict<RTLIL::SigBit, pool<RTLIL::SigBit>> &lut_critical_outputs)
+ {
+ size_t initial_count = lut_nodes.size();
+
+ for (auto node : lut_nodes)
+ {
+ lut_slacks[node] = depth_bound - (lut_depths[node] + lut_altitudes[node]);
+ log_assert(lut_slacks[node] >= 0);
+ }
+ if (debug)
+ {
+ dump_dot_lut_graph(stringf("flowmap-relax-%d-initial.dot", depth_bound), GraphMode::Slack);
+ log(" Dumped initial slack graph to `flowmap-relax-%d-initial.dot`.\n", depth_bound);
+ }
+
+ dict<RTLIL::SigBit, dict<RTLIL::SigBit, int>> potentials;
+ for (int break_num = 1; ; break_num++)
+ {
+ update_breaking_node_potentials(potentials, lut_critical_outputs);
+
+ if (potentials.empty())
+ {
+ log(" Relaxed to %zu (+%zu) LUTs.\n", lut_nodes.size(), lut_nodes.size() - initial_count);
+ if (!first && break_num == 1)
+ {
+ log(" Design fully relaxed.\n");
+ return true;
+ }
+ else
+ {
+ log(" Slack exhausted.\n");
+ break;
+ }
+ }
+
+ RTLIL::SigBit breaking_lut, breaking_gate;
+ int best_potential = INT_MIN;
+ for (auto lut_gate_potentials : potentials)
+ {
+ for (auto gate_potential : lut_gate_potentials.second)
+ {
+ if (gate_potential.second > best_potential)
+ {
+ breaking_lut = lut_gate_potentials.first;
+ breaking_gate = gate_potential.first;
+ best_potential = gate_potential.second;
+ }
+ }
+ }
+ log(" Breaking LUT %s to %s LUT %s (potential %d).\n",
+ log_signal(breaking_lut), lut_nodes[breaking_gate] ? "reuse" : "extract", log_signal(breaking_gate), best_potential);
+
+ if (debug_relax)
+ log(" Removing breaking gate %s from LUT.\n", log_signal(breaking_gate));
+ lut_gates[breaking_lut].erase(breaking_gate);
+
+ auto cut_inputs = cut_lut_at_gate(breaking_lut, breaking_gate);
+ pool<RTLIL::SigBit> gate_inputs = cut_inputs.first, other_inputs = cut_inputs.second;
+
+ pool<RTLIL::SigBit> worklist = lut_gates[breaking_lut];
+ pool<RTLIL::SigBit> elim_gates = gate_inputs;
+ while (!worklist.empty())
+ {
+ auto lut_gate = worklist.pop();
+ bool all_gate_preds_elim = true;
+ for (auto lut_gate_pred : edges_bw[lut_gate])
+ if (!elim_gates[lut_gate_pred])
+ all_gate_preds_elim = false;
+ if (all_gate_preds_elim)
+ {
+ if (debug_relax)
+ log(" Removing gate %s from LUT.\n", log_signal(lut_gate));
+ lut_gates[breaking_lut].erase(lut_gate);
+ for (auto lut_gate_succ : edges_fw[lut_gate])
+ worklist.insert(lut_gate_succ);
+ }
+ }
+ log_assert(!lut_gates[breaking_lut].empty());
+
+ pool<RTLIL::SigBit> directly_affected_nodes = {breaking_lut};
+ for (auto gate_input : gate_inputs)
+ {
+ if (debug_relax)
+ log(" Removing LUT edge %s -> %s.\n", log_signal(gate_input), log_signal(breaking_lut));
+ remove_lut_edge(gate_input, breaking_lut, &directly_affected_nodes);
+ }
+ if (debug_relax)
+ log(" Adding LUT edge %s -> %s.\n", log_signal(breaking_gate), log_signal(breaking_lut));
+ add_lut_edge(breaking_gate, breaking_lut, &directly_affected_nodes);
+
+ if (debug_relax)
+ log(" Updating slack and potentials.\n");
+
+ pool<RTLIL::SigBit> indirectly_affected_nodes = {};
+ update_lut_depths_altitudes(directly_affected_nodes, &indirectly_affected_nodes);
+ update_lut_critical_outputs(lut_critical_outputs, indirectly_affected_nodes);
+ for (auto node : indirectly_affected_nodes)
+ {
+ lut_slacks[node] = depth_bound - (lut_depths[node] + lut_altitudes[node]);
+ log_assert(lut_slacks[node] >= 0);
+ if (debug_relax)
+ log(" LUT %s now has depth %d and slack %d.\n", log_signal(node), lut_depths[node], lut_slacks[node]);
+ }
+
+ worklist = indirectly_affected_nodes;
+ pool<RTLIL::SigBit> visited;
+ while (!worklist.empty())
+ {
+ auto node = worklist.pop();
+ visited.insert(node);
+ potentials.erase(node);
+ // We are invalidating the entire output cone of the gate IR node, not just of the LUT IR node. This is done to also invalidate
+ // all LUTs that could contain one of the indirectly affected nodes as a *part* of them, as they may not be in the output cone
+ // of any of the LUT IR nodes, e.g. if we have a LUT IR node A and node B as predecessors of node C, where node B includes all
+ // gates from node A.
+ for (auto node_succ : edges_fw[node])
+ if (!visited[node_succ])
+ worklist.insert(node_succ);
+ }
+
+ if (debug)
+ {
+ dump_dot_lut_graph(stringf("flowmap-relax-%d-break-%d.dot", depth_bound, break_num), GraphMode::Slack);
+ log(" Dumped slack graph after break %d to `flowmap-relax-%d-break-%d.dot`.\n", break_num, depth_bound, break_num);
+ }
+ }
+
+ return false;
+ }
+
+ void optimize_area(int depth, int optarea)
+ {
+ dict<RTLIL::SigBit, pool<RTLIL::SigBit>> lut_critical_outputs;
+ update_lut_depths_altitudes();
+ update_lut_critical_outputs(lut_critical_outputs);
+
+ for (int depth_bound = depth; depth_bound <= depth + optarea; depth_bound++)
+ {
+ log("Relaxing with depth bound %d.\n", depth_bound);
+ bool fully_relaxed = relax_depth_for_bound(depth_bound == depth, depth_bound, lut_critical_outputs);
+
+ if (fully_relaxed)
+ break;
+ }
+ }
+
+ void pack_cells(int minlut)
+ {
+ ConstEval ce(module);
+ for (auto input_node : inputs)
+ ce.stop(input_node);
+
+ pool<RTLIL::SigBit> mapped_nodes;
+ for (auto node : lut_nodes)
+ {
+ if (node_origins.count(node))
+ {
+ auto origin = node_origins[node];
+ if (origin.cell->getPort(origin.port).size() == 1)
+ log("Packing %s.%s.%s (%s).\n",
+ log_id(module), log_id(origin.cell), origin.port.c_str(), log_signal(node));
+ else
+ log("Packing %s.%s.%s [%d] (%s).\n",
+ log_id(module), log_id(origin.cell), origin.port.c_str(), origin.offset, log_signal(node));
+ }
+ else
+ {
+ log("Packing %s.%s.\n", log_id(module), log_signal(node));
+ }
+
+ for (auto gate_node : lut_gates[node])
+ {
+ log_assert(node_origins.count(gate_node));
+
+ if (gate_node == node)
+ continue;
+
+ auto gate_origin = node_origins[gate_node];
+ if (gate_origin.cell->getPort(gate_origin.port).size() == 1)
+ log(" Packing %s.%s.%s (%s).\n",
+ log_id(module), log_id(gate_origin.cell), gate_origin.port.c_str(), log_signal(gate_node));
+ else
+ log(" Packing %s.%s.%s [%d] (%s).\n",
+ log_id(module), log_id(gate_origin.cell), gate_origin.port.c_str(), gate_origin.offset, log_signal(gate_node));
+ }
+
+ vector<RTLIL::SigBit> input_nodes(lut_edges_bw[node].begin(), lut_edges_bw[node].end());
+ RTLIL::Const lut_table(State::Sx, max(1 << input_nodes.size(), 1 << minlut));
+ for (unsigned i = 0; i < (1 << input_nodes.size()); i++)
+ {
+ ce.push();
+ for (size_t n = 0; n < input_nodes.size(); n++)
+ ce.set(input_nodes[n], ((i >> n) & 1) ? State::S1 : State::S0);
+
+ RTLIL::SigSpec value = node, undef;
+ if (!ce.eval(value, undef))
+ {
+ string env;
+ for (auto input_node : input_nodes)
+ env += stringf(" %s = %s\n", log_signal(input_node), log_signal(ce.values_map(input_node)));
+ log_error("Cannot evaluate %s because %s is not defined.\nEvaluation environment:\n%s",
+ log_signal(node), log_signal(undef), env.c_str());
+ }
+
+ lut_table[i] = value.as_bool() ? State::S1 : State::S0;
+ ce.pop();
+ }
+
+ RTLIL::SigSpec lut_a, lut_y = node;
+ for (auto input_node : input_nodes)
+ lut_a.append_bit(input_node);
+ lut_a.append(RTLIL::Const(State::Sx, minlut - input_nodes.size()));
+
+ RTLIL::Cell *lut = module->addLut(NEW_ID, lut_a, lut_y, lut_table);
+ mapped_nodes.insert(node);
+ for (auto gate_node : lut_gates[node])
+ {
+ auto gate_origin = node_origins[gate_node];
+ lut->add_strpool_attribute("\\src", gate_origin.cell->get_strpool_attribute("\\src"));
+ packed_count++;
+ }
+ lut_count++;
+ lut_area += lut_table.size();
+
+ if ((int)input_nodes.size() >= minlut)
+ log(" Packed into a %zu-LUT %s.%s.\n", input_nodes.size(), log_id(module), log_id(lut));
+ else
+ log(" Packed into a %zu-LUT %s.%s (implemented as %d-LUT).\n", input_nodes.size(), log_id(module), log_id(lut), minlut);
+ }
+
+ for (auto node : mapped_nodes)
+ {
+ auto origin = node_origins[node];
+ RTLIL::SigSpec driver = origin.cell->getPort(origin.port);
+ driver[origin.offset] = module->addWire(NEW_ID);
+ origin.cell->setPort(origin.port, driver);
+ }
+ }
+
+ FlowmapWorker(int order, int minlut, pool<IdString> cell_types, int r_alpha, int r_beta, int r_gamma,
+ bool relax, int optarea, bool debug, bool debug_relax,
+ RTLIL::Module *module) :
+ order(order), r_alpha(r_alpha), r_beta(r_beta), r_gamma(r_gamma), debug(debug), debug_relax(debug_relax),
+ module(module), sigmap(module), index(module)
+ {
+ log("Labeling cells.\n");
+ discover_nodes(cell_types);
+ label_nodes();
+ int depth = map_luts();
+
+ if (relax)
+ {
+ log("\n");
+ log("Optimizing area.\n");
+ optimize_area(depth, optarea);
+ }
+
+ log("\n");
+ log("Packing cells.\n");
+ pack_cells(minlut);
+ }
+};
+
+static void split(std::vector<std::string> &tokens, const std::string &text, char sep)
+{
+ size_t start = 0, end = 0;
+ while ((end = text.find(sep, start)) != std::string::npos) {
+ tokens.push_back(text.substr(start, end - start));
+ start = end + 1;
+ }
+ tokens.push_back(text.substr(start));
+}
+
+struct FlowmapPass : public Pass {
+ FlowmapPass() : Pass("flowmap", "pack LUTs with FlowMap") { }
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" flowmap [options] [selection]\n");
+ log("\n");
+ log("This pass uses the FlowMap technology mapping algorithm to pack logic gates\n");
+ log("into k-LUTs with optimal depth. It allows mapping any circuit elements that can\n");
+ log("be evaluated with the `eval` pass, including cells with multiple output ports\n");
+ log("and multi-bit input and output ports.\n");
+ log("\n");
+ log(" -maxlut k\n");
+ log(" perform technology mapping for a k-LUT architecture. if not specified,\n");
+ log(" defaults to 3.\n");
+ log("\n");
+ log(" -minlut n\n");
+ log(" only produce n-input or larger LUTs. if not specified, defaults to 1.\n");
+ log("\n");
+ log(" -cells <cell>[,<cell>,...]\n");
+ log(" map specified cells. if not specified, maps $_NOT_, $_AND_, $_OR_,\n");
+ log(" $_XOR_ and $_MUX_, which are the outputs of the `simplemap` pass.\n");
+ log("\n");
+ log(" -relax\n");
+ log(" perform depth relaxation and area minimization.\n");
+ log("\n");
+ log(" -r-alpha n, -r-beta n, -r-gamma n\n");
+ log(" parameters of depth relaxation heuristic potential function.\n");
+ log(" if not specified, alpha=8, beta=2, gamma=1.\n");
+ log("\n");
+ log(" -optarea n\n");
+ log(" optimize for area by trading off at most n logic levels for fewer LUTs.\n");
+ log(" n may be zero, to optimize for area without increasing depth.\n");
+ log(" implies -relax.\n");
+ log("\n");
+ log(" -debug\n");
+ log(" dump intermediate graphs.\n");
+ log("\n");
+ log(" -debug-relax\n");
+ log(" explain decisions performed during depth relaxation.\n");
+ log("\n");
+ }
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ int order = 3;
+ int minlut = 1;
+ vector<string> cells;
+ bool relax = false;
+ int r_alpha = 8, r_beta = 2, r_gamma = 1;
+ int optarea = 0;
+ bool debug = false, debug_relax = false;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ if (args[argidx] == "-maxlut" && argidx + 1 < args.size())
+ {
+ order = atoi(args[++argidx].c_str());
+ continue;
+ }
+ if (args[argidx] == "-minlut" && argidx + 1 < args.size())
+ {
+ minlut = atoi(args[++argidx].c_str());
+ continue;
+ }
+ if (args[argidx] == "-cells" && argidx + 1 < args.size())
+ {
+ split(cells, args[++argidx], ',');
+ continue;
+ }
+ if (args[argidx] == "-relax")
+ {
+ relax = true;
+ continue;
+ }
+ if (args[argidx] == "-r-alpha" && argidx + 1 < args.size())
+ {
+ r_alpha = atoi(args[++argidx].c_str());
+ continue;
+ }
+ if (args[argidx] == "-r-beta" && argidx + 1 < args.size())
+ {
+ r_beta = atoi(args[++argidx].c_str());
+ continue;
+ }
+ if (args[argidx] == "-r-gamma" && argidx + 1 < args.size())
+ {
+ r_gamma = atoi(args[++argidx].c_str());
+ continue;
+ }
+ if (args[argidx] == "-optarea" && argidx + 1 < args.size())
+ {
+ relax = true;
+ optarea = atoi(args[++argidx].c_str());
+ continue;
+ }
+ if (args[argidx] == "-debug")
+ {
+ debug = true;
+ continue;
+ }
+ if (args[argidx] == "-debug-relax")
+ {
+ debug = debug_relax = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ pool<IdString> cell_types;
+ if (!cells.empty())
+ {
+ for (auto &cell : cells)
+ cell_types.insert(cell);
+ }
+ else
+ {
+ cell_types = {"$_NOT_", "$_AND_", "$_OR_", "$_XOR_", "$_MUX_"};
+ }
+
+ const char *algo_r = relax ? "-r" : "";
+ log_header(design, "Executing FLOWMAP pass (pack LUTs with FlowMap%s).\n", algo_r);
+
+ int gate_count = 0, lut_count = 0, packed_count = 0;
+ int gate_area = 0, lut_area = 0;
+ for (auto module : design->selected_modules())
+ {
+ FlowmapWorker worker(order, minlut, cell_types, r_alpha, r_beta, r_gamma, relax, optarea, debug, debug_relax, module);
+ gate_count += worker.gate_count;
+ lut_count += worker.lut_count;
+ packed_count += worker.packed_count;
+ gate_area += worker.gate_area;
+ lut_area += worker.lut_area;
+ }
+
+ log("\n");
+ log("Packed %d cells (%d of them duplicated) into %d LUTs.\n", packed_count, packed_count - gate_count, lut_count);
+ log("Solution takes %.1f%% of original gate area.\n", lut_area * 100.0 / gate_area);
+ }
+} FlowmapPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/techmap/libparse.cc b/passes/techmap/libparse.cc
index d3b1ff02..991cc449 100644
--- a/passes/techmap/libparse.cc
+++ b/passes/techmap/libparse.cc
@@ -24,6 +24,7 @@
#include <istream>
#include <fstream>
#include <iostream>
+#include <sstream>
#ifndef FILTERLIB
#include "kernel/log.h"
@@ -86,10 +87,12 @@ int LibertyParser::lexer(std::string &str)
{
int c;
+ // eat whitespace
do {
c = f.get();
} while (c == ' ' || c == '\t' || c == '\r');
+ // search for identifiers, numbers, plus or minus.
if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') || c == '_' || c == '-' || c == '+' || c == '.') {
str = c;
while (1) {
@@ -111,6 +114,8 @@ int LibertyParser::lexer(std::string &str)
}
}
+ // if it wasn't an identifer, number of array range,
+ // maybe it's a string?
if (c == '"') {
str = "";
while (1) {
@@ -125,9 +130,10 @@ int LibertyParser::lexer(std::string &str)
return 'v';
}
+ // if it wasn't a string, perhaps it's a comment or a forward slash?
if (c == '/') {
c = f.get();
- if (c == '*') {
+ if (c == '*') { // start of '/*' block comment
int last_c = 0;
while (c > 0 && (last_c != '*' || c != '/')) {
last_c = c;
@@ -136,7 +142,7 @@ int LibertyParser::lexer(std::string &str)
line++;
}
return lexer(str);
- } else if (c == '/') {
+ } else if (c == '/') { // start of '//' line comment
while (c > 0 && c != '\n')
c = f.get();
line++;
@@ -144,24 +150,31 @@ int LibertyParser::lexer(std::string &str)
}
f.unget();
// fprintf(stderr, "LEX: char >>/<<\n");
- return '/';
+ return '/'; // a single '/' charater.
}
+ // check for a backslash
if (c == '\\') {
- c = f.get();
+ c = f.get();
if (c == '\r')
c = f.get();
- if (c == '\n')
+ if (c == '\n') {
+ line++;
return lexer(str);
+ }
f.unget();
return '\\';
}
+ // check for a new line
if (c == '\n') {
line++;
- return ';';
+ return 'n';
}
+ // anything else, such as ';' will get passed
+ // through as literal items.
+
// if (c >= 32 && c < 255)
// fprintf(stderr, "LEX: char >>%c<<\n", c);
// else
@@ -175,14 +188,39 @@ LibertyAst *LibertyParser::parse()
int tok = lexer(str);
- while (tok == ';')
+ // there are liberty files in the wild that
+ // have superfluous ';' at the end of
+ // a { ... }. We simply ignore a ';' here.
+ // and get to the next statement.
+
+ while ((tok == 'n') || (tok == ';'))
tok = lexer(str);
if (tok == '}' || tok < 0)
return NULL;
- if (tok != 'v')
- error();
+ if (tok != 'v') {
+ std::string eReport;
+ switch(tok)
+ {
+ case 'n':
+ error("Unexpected newline.");
+ break;
+ case '[':
+ case ']':
+ case '}':
+ case '{':
+ case '\"':
+ case ':':
+ eReport = "Unexpected '";
+ eReport += static_cast<char>(tok);
+ eReport += "'.";
+ error(eReport);
+ break;
+ default:
+ error();
+ }
+ }
LibertyAst *ast = new LibertyAst;
ast->id = str;
@@ -191,7 +229,9 @@ LibertyAst *LibertyParser::parse()
{
tok = lexer(str);
- if (tok == ';')
+ // allow both ';' and new lines to
+ // terminate a statement.
+ if ((tok == ';') || (tok == 'n'))
break;
if (tok == ':' && ast->value.empty()) {
@@ -207,7 +247,12 @@ LibertyAst *LibertyParser::parse()
ast->value += str;
tok = lexer(str);
}
- if (tok == ';')
+
+ // In a liberty file, all key : value pairs should end in ';'
+ // However, there are some liberty files in the wild that
+ // just have a newline. We'll be kind and accept a newline
+ // instead of the ';' too..
+ if ((tok == ';') || (tok == 'n'))
break;
else
error();
@@ -222,8 +267,70 @@ LibertyAst *LibertyParser::parse()
continue;
if (tok == ')')
break;
- if (tok != 'v')
- error();
+
+ // FIXME: the AST needs to be extended to store
+ // these vector ranges.
+ if (tok == '[')
+ {
+ // parse vector range [A] or [A:B]
+ std::string arg;
+ tok = lexer(arg);
+ if (tok != 'v')
+ {
+ // expected a vector array index
+ error("Expected a number.");
+ }
+ else
+ {
+ // fixme: check for number A
+ }
+ tok = lexer(arg);
+ // optionally check for : in case of [A:B]
+ // if it isn't we just expect ']'
+ // as we have [A]
+ if (tok == ':')
+ {
+ tok = lexer(arg);
+ if (tok != 'v')
+ {
+ // expected a vector array index
+ error("Expected a number.");
+ }
+ else
+ {
+ // fixme: check for number B
+ tok = lexer(arg);
+ }
+ }
+ // expect a closing bracket of array range
+ if (tok != ']')
+ {
+ error("Expected ']' on array range.");
+ }
+ continue;
+ }
+ if (tok != 'v') {
+ std::string eReport;
+ switch(tok)
+ {
+ case 'n':
+ error("Unexpected newline.");
+ break;
+ case '[':
+ case ']':
+ case '}':
+ case '{':
+ case '\"':
+ case ':':
+ eReport = "Unexpected '";
+ eReport += static_cast<char>(tok);
+ eReport += "'.";
+ error(eReport);
+ break;
+ default:
+ error();
+ }
+ }
ast->args.push_back(arg);
}
continue;
@@ -249,14 +356,31 @@ LibertyAst *LibertyParser::parse()
void LibertyParser::error()
{
- log_error("Syntax error in line %d.\n", line);
+ log_error("Syntax error in liberty file on line %d.\n", line);
+}
+
+void LibertyParser::error(const std::string &str)
+{
+ std::stringstream ss;
+ ss << "Syntax error in liberty file on line " << line << ".\n";
+ ss << " " << str << "\n";
+ log_error("%s", ss.str().c_str());
}
#else
void LibertyParser::error()
{
- fprintf(stderr, "Syntax error in line %d.\n", line);
+ fprintf(stderr, "Syntax error in liberty file on line %d.\n", line);
+ exit(1);
+}
+
+void LibertyParser::error(const std::string &str)
+{
+ std::stringstream ss;
+ ss << "Syntax error in liberty file on line " << line << ".\n";
+ ss << " " << str << "\n";
+ printf("%s", ss.str().c_str());
exit(1);
}
@@ -264,21 +388,21 @@ void LibertyParser::error()
#define CHECK_NV(result, check) \
do { \
- auto _R = (result); \
- if (!(_R check)) { \
- fprintf(stderr, "Error from '%s' (%ld %s) in %s:%d.\n", \
- #result, (long int)_R, #check, __FILE__, __LINE__); \
- abort(); \
- } \
+ auto _R = (result); \
+ if (!(_R check)) { \
+ fprintf(stderr, "Error from '%s' (%ld %s) in %s:%d.\n", \
+ #result, (long int)_R, #check, __FILE__, __LINE__); \
+ abort(); \
+ } \
} while(0)
#define CHECK_COND(result) \
do { \
- if (!(result)) { \
- fprintf(stderr, "Error from '%s' in %s:%d.\n", \
- #result, __FILE__, __LINE__); \
- abort(); \
- } \
+ if (!(result)) { \
+ fprintf(stderr, "Error from '%s' in %s:%d.\n", \
+ #result, __FILE__, __LINE__); \
+ abort(); \
+ } \
} while(0)
/**** END: http://svn.clifford.at/tools/trunk/examples/check.h ****/
diff --git a/passes/techmap/libparse.h b/passes/techmap/libparse.h
index cf632557..c9ebd06c 100644
--- a/passes/techmap/libparse.h
+++ b/passes/techmap/libparse.h
@@ -46,9 +46,17 @@ namespace Yosys
LibertyAst *ast;
LibertyParser(std::istream &f) : f(f), line(1), ast(parse()) {}
~LibertyParser() { if (ast) delete ast; }
+
+ /* lexer return values:
+ 'v': identifier, string, array range [...] -> str holds the token string
+ 'n': newline
+ anything else is a single character.
+ */
int lexer(std::string &str);
- LibertyAst *parse();
+
+ LibertyAst *parse();
void error();
+ void error(const std::string &str);
};
}
diff --git a/passes/techmap/lut2mux.cc b/passes/techmap/lut2mux.cc
index d32bbff1..a4ed7955 100644
--- a/passes/techmap/lut2mux.cc
+++ b/passes/techmap/lut2mux.cc
@@ -32,7 +32,7 @@ int lut2mux(Cell *cell)
if (GetSize(sig_a) == 1)
{
- cell->module->addMuxGate(NEW_ID, lut[0], lut[1], sig_a, sig_y);
+ cell->module->addMuxGate(NEW_ID, lut.extract(0)[0], lut.extract(1)[0], sig_a, sig_y);
}
else
{
diff --git a/passes/tests/flowmap/flow.v b/passes/tests/flowmap/flow.v
new file mode 100644
index 00000000..297ef910
--- /dev/null
+++ b/passes/tests/flowmap/flow.v
@@ -0,0 +1,22 @@
+// Exact reproduction of Figure 2(a) from 10.1109/43.273754.
+module top(...);
+ input a,b,c,d,e,f;
+ wire nA = b&c;
+ wire A = !nA;
+ wire nB = c|d;
+ wire B = !nB;
+ wire nC = e&f;
+ wire C = !nC;
+ wire D = A|B;
+ wire E = a&D;
+ wire nF = D&C;
+ wire F = !nF;
+ wire nG = F|B;
+ wire G = !nG;
+ wire H = a&F;
+ wire I = E|G;
+ wire J = G&C;
+ wire np = H&I;
+ output p = !np;
+ output q = A|J;
+endmodule
diff --git a/passes/tests/flowmap/flowp.v b/passes/tests/flowmap/flowp.v
new file mode 100644
index 00000000..2fb40ffa
--- /dev/null
+++ b/passes/tests/flowmap/flowp.v
@@ -0,0 +1,16 @@
+// Like flow.v, but results in a network identical to Figure 2(b).
+module top(...);
+ input a,b,c,d,e,f;
+ wire A = b&c;
+ wire B = c|d;
+ wire C = e&f;
+ wire D = A|B;
+ wire E = a&D;
+ wire F = D&C;
+ wire G = F|B;
+ wire H = a&F;
+ wire I = E|G;
+ wire J = G&C;
+ output p = H&I;
+ output q = A|J;
+endmodule
diff --git a/passes/tests/flowmap/pack1.v b/passes/tests/flowmap/pack1.v
new file mode 100644
index 00000000..9454edf3
--- /dev/null
+++ b/passes/tests/flowmap/pack1.v
@@ -0,0 +1,11 @@
+// Exact reproduction of Figure 3(a) from 10.1109/92.285741.
+module top(...);
+ input a,b,c,d,e,f,g,h;
+ wire x = !(c|d);
+ wire y = !(e&f);
+ wire u = !(a&b);
+ wire v = !(x|y);
+ wire w = !(g&h);
+ output s = !(u|v);
+ output t = !(v|w);
+endmodule
diff --git a/passes/tests/flowmap/pack1p.v b/passes/tests/flowmap/pack1p.v
new file mode 100644
index 00000000..fdb27883
--- /dev/null
+++ b/passes/tests/flowmap/pack1p.v
@@ -0,0 +1,11 @@
+// Like pack1.v, but results in a simpler network.
+module top(...);
+ input a,b,c,d,e,f,g,h;
+ wire x = c|d;
+ wire y = e&f;
+ wire u = a&b;
+ wire v = x|y;
+ wire w = g&h;
+ output s = u|v;
+ output t = v|w;
+endmodule
diff --git a/passes/tests/flowmap/pack2.v b/passes/tests/flowmap/pack2.v
new file mode 100644
index 00000000..445e4afb
--- /dev/null
+++ b/passes/tests/flowmap/pack2.v
@@ -0,0 +1,15 @@
+// Exact reproduction of Figure 4(a) from 10.1109/92.285741.
+module top(...);
+ (* $flowmap_level=1 *) input a;
+ (* $flowmap_level=1 *) input b;
+ (* $flowmap_level=2 *) input c;
+ (* $flowmap_level=1 *) input d;
+ (* $flowmap_level=3 *) input e;
+ (* $flowmap_level=1 *) input f;
+ wire u = !(a&b);
+ wire w = !(c|d);
+ wire v = !(u|w);
+ wire n0 = !(w&e);
+ wire n1 = !(n0|f);
+ output n2 = !(v&n1);
+endmodule
diff --git a/passes/tests/flowmap/pack2p.v b/passes/tests/flowmap/pack2p.v
new file mode 100644
index 00000000..d4b41733
--- /dev/null
+++ b/passes/tests/flowmap/pack2p.v
@@ -0,0 +1,15 @@
+// Like pack2.v, but results in a simpler network.
+module top(...);
+ (* $flowmap_level=1 *) input a;
+ (* $flowmap_level=1 *) input b;
+ (* $flowmap_level=2 *) input c;
+ (* $flowmap_level=1 *) input d;
+ (* $flowmap_level=3 *) input e;
+ (* $flowmap_level=1 *) input f;
+ wire u = a&b;
+ wire w = c|d;
+ wire v = u|w;
+ wire n0 = w&e;
+ wire n1 = n0|f;
+ output n2 = v&n1;
+endmodule
diff --git a/passes/tests/flowmap/pack3.v b/passes/tests/flowmap/pack3.v
new file mode 100644
index 00000000..06147a1a
--- /dev/null
+++ b/passes/tests/flowmap/pack3.v
@@ -0,0 +1,15 @@
+// Exact reproduction of Figure 5(a) (bottom) from 10.1109/92.285741.
+module top(...);
+ input a,b,c,d,e,f,g,h,i,j;
+ wire x = !(a&b);
+ wire y = !(c|d);
+ wire z = !(e|f);
+ wire n0 = !(g&h);
+ wire n1 = !(i|j);
+ wire w = !(x&y);
+ wire n2 = !(z&n0);
+ wire n3 = !(n0|n1);
+ wire n4 = !(n2|n3);
+ wire v = !(w|n5);
+ output u = !(w&v);
+endmodule
diff --git a/passes/tests/flowmap/pack3p.v b/passes/tests/flowmap/pack3p.v
new file mode 100644
index 00000000..bc6ac175
--- /dev/null
+++ b/passes/tests/flowmap/pack3p.v
@@ -0,0 +1,15 @@
+// Like pack2.v, but results in a simpler network.
+module top(...);
+ input a,b,c,d,e,f,g,h,i,j;
+ wire x = a&b;
+ wire y = c|d;
+ wire z = e|f;
+ wire n0 = g&h;
+ wire n1 = i|j;
+ wire w = x&y;
+ wire n2 = z&n0;
+ wire n3 = n0|n1;
+ wire n4 = n2|n3;
+ wire v = w|n5;
+ output u = w&v;
+endmodule