aboutsummaryrefslogtreecommitdiffstats
path: root/passes
diff options
context:
space:
mode:
Diffstat (limited to 'passes')
-rw-r--r--passes/cmds/Makefile.inc6
-rw-r--r--passes/cmds/add.cc24
-rw-r--r--passes/cmds/autoname.cc134
-rw-r--r--passes/cmds/blackbox.cc6
-rw-r--r--passes/cmds/bugpoint.cc443
-rw-r--r--passes/cmds/check.cc86
-rw-r--r--passes/cmds/chformal.cc8
-rw-r--r--passes/cmds/chtype.cc4
-rw-r--r--passes/cmds/connect.cc10
-rw-r--r--passes/cmds/connwrappers.cc6
-rw-r--r--passes/cmds/copy.cc4
-rw-r--r--passes/cmds/cover.cc18
-rw-r--r--passes/cmds/delete.cc6
-rw-r--r--passes/cmds/design.cc6
-rw-r--r--passes/cmds/edgetypes.cc4
-rw-r--r--passes/cmds/logcmd.cc4
-rw-r--r--passes/cmds/ltp.cc185
-rw-r--r--passes/cmds/plugin.cc54
-rw-r--r--passes/cmds/portlist.cc93
-rw-r--r--passes/cmds/qwp.cc7
-rw-r--r--passes/cmds/rename.cc152
-rw-r--r--passes/cmds/scatter.cc4
-rw-r--r--passes/cmds/scc.cc55
-rw-r--r--passes/cmds/scratchpad.cc141
-rw-r--r--passes/cmds/select.cc129
-rw-r--r--passes/cmds/setattr.cc53
-rw-r--r--passes/cmds/setundef.cc382
-rw-r--r--passes/cmds/show.cc81
-rw-r--r--passes/cmds/splice.cc4
-rw-r--r--passes/cmds/splitnets.cc4
-rw-r--r--passes/cmds/stat.cc116
-rw-r--r--passes/cmds/tee.cc14
-rw-r--r--passes/cmds/torder.cc4
-rw-r--r--passes/cmds/trace.cc51
-rw-r--r--passes/cmds/write_file.cc7
-rw-r--r--passes/equiv/Makefile.inc2
-rw-r--r--passes/equiv/equiv_add.cc4
-rw-r--r--passes/equiv/equiv_induct.cc4
-rw-r--r--passes/equiv/equiv_make.cc98
-rw-r--r--passes/equiv/equiv_mark.cc4
-rw-r--r--passes/equiv/equiv_miter.cc4
-rw-r--r--passes/equiv/equiv_opt.cc208
-rw-r--r--passes/equiv/equiv_purge.cc4
-rw-r--r--passes/equiv/equiv_remove.cc4
-rw-r--r--passes/equiv/equiv_simple.cc4
-rw-r--r--passes/equiv/equiv_status.cc4
-rw-r--r--passes/equiv/equiv_struct.cc8
-rw-r--r--passes/fsm/fsm.cc4
-rw-r--r--passes/fsm/fsm_detect.cc54
-rw-r--r--passes/fsm/fsm_expand.cc6
-rw-r--r--passes/fsm/fsm_export.cc4
-rw-r--r--passes/fsm/fsm_extract.cc36
-rw-r--r--passes/fsm/fsm_info.cc4
-rw-r--r--passes/fsm/fsm_map.cc8
-rw-r--r--passes/fsm/fsm_opt.cc7
-rw-r--r--passes/fsm/fsm_recode.cc4
-rw-r--r--passes/hierarchy/hierarchy.cc693
-rw-r--r--passes/hierarchy/submod.cc4
-rw-r--r--passes/hierarchy/uniquify.cc8
-rw-r--r--passes/memory/Makefile.inc1
-rw-r--r--passes/memory/memory.cc6
-rw-r--r--passes/memory/memory_bram.cc153
-rw-r--r--passes/memory/memory_collect.cc21
-rw-r--r--passes/memory/memory_dff.cc46
-rw-r--r--passes/memory/memory_map.cc6
-rw-r--r--passes/memory/memory_memx.cc4
-rw-r--r--passes/memory/memory_nordff.cc121
-rw-r--r--passes/memory/memory_share.cc10
-rw-r--r--passes/memory/memory_unpack.cc4
-rw-r--r--passes/opt/Makefile.inc6
-rw-r--r--passes/opt/muxpack.cc368
-rw-r--r--passes/opt/opt.cc17
-rw-r--r--passes/opt/opt_clean.cc251
-rw-r--r--passes/opt/opt_demorgan.cc28
-rw-r--r--passes/opt/opt_expr.cc1121
-rw-r--r--passes/opt/opt_lut.cc595
-rw-r--r--passes/opt/opt_mem.cc143
-rw-r--r--passes/opt/opt_merge.cc112
-rw-r--r--passes/opt/opt_muxtree.cc78
-rw-r--r--passes/opt/opt_reduce.cc119
-rw-r--r--passes/opt/opt_rmdff.cc422
-rw-r--r--passes/opt/opt_share.cc657
-rw-r--r--passes/opt/pmux2shiftx.cc860
-rw-r--r--passes/opt/rmports.cc6
-rw-r--r--passes/opt/share.cc354
-rw-r--r--passes/opt/wreduce.cc292
-rw-r--r--passes/pmgen/.gitignore1
-rw-r--r--passes/pmgen/Makefile.inc48
-rw-r--r--passes/pmgen/README.md384
-rw-r--r--passes/pmgen/generate.h140
-rw-r--r--passes/pmgen/ice40_dsp.cc317
-rw-r--r--passes/pmgen/ice40_dsp.pmg581
-rw-r--r--passes/pmgen/ice40_wrapcarry.cc158
-rw-r--r--passes/pmgen/ice40_wrapcarry.pmg15
-rw-r--r--passes/pmgen/peepopt.cc131
-rw-r--r--passes/pmgen/peepopt_dffmux.pmg171
-rw-r--r--passes/pmgen/peepopt_muldiv.pmg36
-rw-r--r--passes/pmgen/peepopt_shiftmul.pmg96
-rw-r--r--passes/pmgen/pmgen.py797
-rw-r--r--passes/pmgen/test_pmgen.cc260
-rw-r--r--passes/pmgen/test_pmgen.pmg189
-rw-r--r--passes/pmgen/xilinx_dsp.cc846
-rw-r--r--passes/pmgen/xilinx_dsp.pmg727
-rw-r--r--passes/pmgen/xilinx_dsp48a.pmg673
-rw-r--r--passes/pmgen/xilinx_dsp_CREG.pmg235
-rw-r--r--passes/pmgen/xilinx_dsp_cascade.pmg486
-rw-r--r--passes/pmgen/xilinx_srl.cc258
-rw-r--r--passes/pmgen/xilinx_srl.pmg326
-rw-r--r--passes/proc/Makefile.inc2
-rw-r--r--passes/proc/proc.cc6
-rw-r--r--passes/proc/proc_arst.cc10
-rw-r--r--passes/proc/proc_clean.cc83
-rw-r--r--passes/proc/proc_dff.cc9
-rw-r--r--passes/proc/proc_dlatch.cc24
-rw-r--r--passes/proc/proc_init.cc30
-rw-r--r--passes/proc/proc_mux.cc80
-rw-r--r--passes/proc/proc_prune.cc165
-rw-r--r--passes/proc/proc_rmdead.cc22
-rw-r--r--passes/sat/Makefile.inc6
-rw-r--r--passes/sat/assertpmux.cc10
-rw-r--r--passes/sat/async2sync.cc232
-rw-r--r--passes/sat/clk2fflogic.cc121
-rw-r--r--passes/sat/cutpoint.cc168
-rw-r--r--passes/sat/eval.cc8
-rw-r--r--passes/sat/expose.cc59
-rw-r--r--passes/sat/fmcombine.cc376
-rw-r--r--passes/sat/fminit.cc197
-rw-r--r--passes/sat/freduce.cc4
-rw-r--r--passes/sat/miter.cc18
-rw-r--r--passes/sat/mutate.cc988
-rw-r--r--passes/sat/sat.cc21
-rw-r--r--passes/sat/sim.cc13
-rw-r--r--passes/sat/supercover.cc92
-rw-r--r--passes/techmap/Makefile.inc9
-rw-r--r--passes/techmap/abc.cc948
-rw-r--r--passes/techmap/abc9.cc332
-rw-r--r--passes/techmap/abc9_exe.cc531
-rw-r--r--passes/techmap/abc9_ops.cc825
-rw-r--r--passes/techmap/aigmap.cc54
-rw-r--r--passes/techmap/alumacc.cc117
-rw-r--r--passes/techmap/attrmap.cc220
-rw-r--r--passes/techmap/attrmvcp.cc4
-rw-r--r--passes/techmap/clkbufmap.cc339
-rw-r--r--passes/techmap/deminout.cc39
-rw-r--r--passes/techmap/dff2dffe.cc147
-rw-r--r--passes/techmap/dff2dffs.cc165
-rw-r--r--passes/techmap/dffinit.cc64
-rw-r--r--passes/techmap/dfflibmap.cc172
-rw-r--r--passes/techmap/dffsr2dff.cc72
-rw-r--r--passes/techmap/extract.cc76
-rw-r--r--passes/techmap/extract_counter.cc156
-rw-r--r--passes/techmap/extract_fa.cc74
-rw-r--r--passes/techmap/extract_reduce.cc49
-rw-r--r--passes/techmap/extractinv.cc123
-rw-r--r--passes/techmap/flowmap.cc1614
-rw-r--r--passes/techmap/hilomap.cc4
-rw-r--r--passes/techmap/insbuf.cc4
-rw-r--r--passes/techmap/iopadmap.cc334
-rw-r--r--passes/techmap/libparse.cc200
-rw-r--r--passes/techmap/libparse.h10
-rw-r--r--passes/techmap/lut2mux.cc14
-rw-r--r--passes/techmap/maccmap.cc66
-rw-r--r--passes/techmap/muxcover.cc348
-rw-r--r--passes/techmap/nlutmap.cc10
-rw-r--r--passes/techmap/pmuxtree.cc20
-rw-r--r--passes/techmap/shregmap.cc88
-rw-r--r--passes/techmap/simplemap.cc470
-rw-r--r--passes/techmap/techmap.cc412
-rw-r--r--passes/techmap/tribuf.cc56
-rw-r--r--passes/techmap/zinit.cc36
-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
-rw-r--r--passes/tests/test_abcloop.cc4
-rw-r--r--passes/tests/test_autotb.cc12
-rw-r--r--passes/tests/test_cell.cc44
181 files changed, 23939 insertions, 3348 deletions
diff --git a/passes/cmds/Makefile.inc b/passes/cmds/Makefile.inc
index 9e3934e16..07a5d3ddc 100644
--- a/passes/cmds/Makefile.inc
+++ b/passes/cmds/Makefile.inc
@@ -5,6 +5,7 @@ OBJS += passes/cmds/design.o
OBJS += passes/cmds/select.o
OBJS += passes/cmds/show.o
OBJS += passes/cmds/rename.o
+OBJS += passes/cmds/autoname.o
OBJS += passes/cmds/connect.o
OBJS += passes/cmds/scatter.o
OBJS += passes/cmds/setundef.o
@@ -25,7 +26,10 @@ OBJS += passes/cmds/plugin.o
OBJS += passes/cmds/check.o
OBJS += passes/cmds/qwp.o
OBJS += passes/cmds/edgetypes.o
+OBJS += passes/cmds/portlist.o
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
+OBJS += passes/cmds/scratchpad.o
diff --git a/passes/cmds/add.cc b/passes/cmds/add.cc
index e698926f9..dd05ac81f 100644
--- a/passes/cmds/add.cc
+++ b/passes/cmds/add.cc
@@ -71,7 +71,7 @@ static void add_wire(RTLIL::Design *design, RTLIL::Module *module, std::string n
RTLIL::Module *mod = design->modules_.at(it.second->type);
if (!design->selected_whole_module(mod->name))
continue;
- if (mod->get_bool_attribute("\\blackbox"))
+ if (mod->get_blackbox_attribute())
continue;
if (it.second->hasPort(name))
continue;
@@ -83,7 +83,7 @@ static void add_wire(RTLIL::Design *design, RTLIL::Module *module, std::string n
struct AddPass : public Pass {
AddPass() : Pass("add", "add objects to the design") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -105,14 +105,20 @@ struct AddPass : public Pass {
log("Like 'add -input', but also connect the signal between instances of the\n");
log("selected modules.\n");
log("\n");
+ log("\n");
+ log(" add -mod <name[s]>\n");
+ log("\n");
+ log("Add module[s] with the specified name[s].\n");
+ log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
std::string command;
std::string arg_name;
bool arg_flag_input = false;
bool arg_flag_output = false;
bool arg_flag_global = false;
+ bool mod_mode = false;
int arg_width = 0;
size_t argidx;
@@ -133,8 +139,20 @@ struct AddPass : public Pass {
arg_width = atoi(args[++argidx].c_str());
continue;
}
+ if (arg == "-mod") {
+ mod_mode = true;
+ argidx++;
+ break;
+ }
break;
}
+
+ if (mod_mode) {
+ for (; argidx < args.size(); argidx++)
+ design->addModule(RTLIL::escape_id(args[argidx]));
+ return;
+ }
+
extra_args(args, argidx, design);
for (auto &mod : design->modules_)
diff --git a/passes/cmds/autoname.cc b/passes/cmds/autoname.cc
new file mode 100644
index 000000000..50632201e
--- /dev/null
+++ b/passes/cmds/autoname.cc
@@ -0,0 +1,134 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "kernel/yosys.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+int autoname_worker(Module *module)
+{
+ dict<Cell*, pair<int, IdString>> proposed_cell_names;
+ dict<Wire*, pair<int, IdString>> proposed_wire_names;
+ dict<Wire*, int> wire_score;
+ int best_score = -1;
+
+ for (auto cell : module->selected_cells())
+ for (auto &conn : cell->connections())
+ for (auto bit : conn.second)
+ if (bit.wire != nullptr)
+ wire_score[bit.wire]++;
+
+ for (auto cell : module->selected_cells()) {
+ if (cell->name[0] == '$') {
+ for (auto &conn : cell->connections()) {
+ string suffix = stringf("_%s_%s", log_id(cell->type), log_id(conn.first));
+ for (auto bit : conn.second)
+ if (bit.wire != nullptr && bit.wire->name[0] != '$') {
+ IdString new_name(bit.wire->name.str() + suffix);
+ int score = wire_score.at(bit.wire);
+ if (cell->output(conn.first)) score = 0;
+ score = 10000*score + new_name.size();
+ if (!proposed_cell_names.count(cell) || score < proposed_cell_names.at(cell).first) {
+ if (best_score < 0 || score < best_score)
+ best_score = score;
+ proposed_cell_names[cell] = make_pair(score, new_name);
+ }
+ }
+ }
+ } else {
+ for (auto &conn : cell->connections()) {
+ string suffix = stringf("_%s", log_id(conn.first));
+ for (auto bit : conn.second)
+ if (bit.wire != nullptr && bit.wire->name[0] == '$' && !bit.wire->port_id) {
+ IdString new_name(cell->name.str() + suffix);
+ int score = wire_score.at(bit.wire);
+ if (cell->output(conn.first)) score = 0;
+ score = 10000*score + new_name.size();
+ if (!proposed_wire_names.count(bit.wire) || score < proposed_wire_names.at(bit.wire).first) {
+ if (best_score < 0 || score < best_score)
+ best_score = score;
+ proposed_wire_names[bit.wire] = make_pair(score, new_name);
+ }
+ }
+ }
+ }
+ }
+
+ for (auto &it : proposed_cell_names) {
+ if (best_score*2 < it.second.first)
+ continue;
+ IdString n = module->uniquify(it.second.second);
+ log_debug("Rename cell %s in %s to %s.\n", log_id(it.first), log_id(module), log_id(n));
+ module->rename(it.first, n);
+ }
+
+ for (auto &it : proposed_wire_names) {
+ if (best_score*2 < it.second.first)
+ continue;
+ IdString n = module->uniquify(it.second.second);
+ log_debug("Rename wire %s in %s to %s.\n", log_id(it.first), log_id(module), log_id(n));
+ module->rename(it.first, n);
+ }
+
+ return proposed_cell_names.size() + proposed_wire_names.size();
+}
+
+struct AutonamePass : public Pass {
+ AutonamePass() : Pass("autoname", "automatically assign names to objects") { }
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" autoname [selection]\n");
+ log("\n");
+ log("Assign auto-generated public names to objects with private names (the ones\n");
+ log("with $-prefix).\n");
+ log("\n");
+ }
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ // if (args[argidx] == "-foo") {
+ // foo = true;
+ // continue;
+ // }
+ break;
+ }
+
+ log_header(design, "Executing AUTONAME pass.\n");
+
+ for (auto module : design->selected_modules())
+ {
+ int count = 0, iter = 0;
+ while (1) {
+ iter++;
+ int n = autoname_worker(module);
+ if (!n) break;
+ count += n;
+ }
+ if (count > 0)
+ log("Renamed %d objects in module %s (%d iterations).\n", count, log_id(module), iter);
+ }
+ }
+} AutonamePass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/cmds/blackbox.cc b/passes/cmds/blackbox.cc
index 497b35f5c..d09ed872e 100644
--- a/passes/cmds/blackbox.cc
+++ b/passes/cmds/blackbox.cc
@@ -23,8 +23,8 @@ USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
struct BlackboxPass : public Pass {
- BlackboxPass() : Pass("blackbox", "change type of cells in the design") { }
- virtual void help()
+ BlackboxPass() : Pass("blackbox", "convert modules into blackbox modules") { }
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -34,7 +34,7 @@ struct BlackboxPass : public Pass {
log("module attribute).\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++)
diff --git a/passes/cmds/bugpoint.cc b/passes/cmds/bugpoint.cc
new file mode 100644
index 000000000..5a47988ec
--- /dev/null
+++ b/passes/cmds/bugpoint.cc
@@ -0,0 +1,443 @@
+/*
+ * 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 `proc_clean; clean -purge` after each minimization step. converges\n");
+ log(" faster, but produces larger testcases, and may fail to produce any\n");
+ log(" testcase at all if the crash is related to dangling wires.\n");
+ log("\n");
+ log(" -clean\n");
+ log(" run `proc_clean; clean -purge` before checking testcase and after\n");
+ log(" finishing. produces smaller and more useful testcases, but may fail to\n");
+ log(" produce any testcase 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");
+ log(" -assigns\n");
+ log(" try to remove process assigns from cases.\n");
+ log("\n");
+ log(" -updates\n");
+ log(" try to remove process updates from syncs.\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, "proc_clean -quiet");
+ 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, bool assigns, bool updates)
+ {
+ 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_blackbox_attribute())
+ 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_blackbox_attribute())
+ 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_blackbox_attribute())
+ 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_blackbox_attribute())
+ 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;
+ }
+ }
+ }
+ }
+ }
+ if (assigns)
+ {
+ for (auto mod : design_copy->modules())
+ {
+ if (mod->get_blackbox_attribute())
+ continue;
+
+ for (auto &pr : mod->processes)
+ {
+ vector<RTLIL::CaseRule*> cases = {&pr.second->root_case};
+ while (!cases.empty())
+ {
+ RTLIL::CaseRule *cs = cases[0];
+ cases.erase(cases.begin());
+ for (auto it = cs->actions.begin(); it != cs->actions.end(); ++it)
+ {
+ if (index++ == seed)
+ {
+ log("Trying to remove assign %s %s in %s.%s.\n", log_signal((*it).first), log_signal((*it).second), mod->name.c_str(), pr.first.c_str());
+ cs->actions.erase(it);
+ return design_copy;
+ }
+ }
+ for (auto &sw : cs->switches)
+ cases.insert(cases.end(), sw->cases.begin(), sw->cases.end());
+ }
+ }
+ }
+ }
+ if (updates)
+ {
+ for (auto mod : design_copy->modules())
+ {
+ if (mod->get_blackbox_attribute())
+ continue;
+
+ for (auto &pr : mod->processes)
+ {
+ for (auto &sy : pr.second->syncs)
+ {
+ for (auto it = sy->actions.begin(); it != sy->actions.end(); ++it)
+ {
+ if (index++ == seed)
+ {
+ log("Trying to remove sync %s update %s %s in %s.%s.\n", log_signal(sy->signal), log_signal((*it).first), log_signal((*it).second), mod->name.c_str(), pr.first.c_str());
+ sy->actions.erase(it);
+ 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, assigns = false, updates = 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;
+ }
+ if (args[argidx] == "-assigns") {
+ assigns = true;
+ has_part = true;
+ continue;
+ }
+ if (args[argidx] == "-updates") {
+ updates = true;
+ has_part = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ if (script.empty())
+ log_cmd_error("Missing -script option.\n");
+
+ if (!has_part)
+ {
+ modules = true;
+ ports = true;
+ cells = true;
+ connections = true;
+ assigns = true;
+ updates = 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;
+ bool found_something = false, stage2 = false;
+ while (true)
+ {
+ if (RTLIL::Design *simplified = simplify_something(crashing_design, seed, stage2, modules, ports, cells, connections, assigns, updates))
+ {
+ 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;
+ 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/check.cc b/passes/cmds/check.cc
index bb8fe6856..820ecac7b 100644
--- a/passes/cmds/check.cc
+++ b/passes/cmds/check.cc
@@ -27,7 +27,7 @@ PRIVATE_NAMESPACE_BEGIN
struct CheckPass : public Pass {
CheckPass() : Pass("check", "check for obvious problems in the design") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -41,21 +41,33 @@ struct CheckPass : public Pass {
log("\n");
log(" - used wires that do not have a driver\n");
log("\n");
- log("When called with -noinit then this command also checks for wires which have\n");
- log("the 'init' attribute set.\n");
+ log("Options:\n");
log("\n");
- log("When called with -initdrv then this command also checks for wires which have\n");
- log("the 'init' attribute set and aren't driven by a FF cell type.\n");
+ log(" -noinit\n");
+ log(" Also check for wires which have the 'init' attribute set.\n");
log("\n");
- log("When called with -assert then the command will produce an error if any\n");
- log("problems are found in the current design.\n");
+ log(" -initdrv\n");
+ log(" Also check for wires that have the 'init' attribute set and are not\n");
+ log(" driven by an FF cell type.\n");
+ log("\n");
+ log(" -mapped\n");
+ log(" Also check for internal cells that have not been mapped to cells of the\n");
+ log(" target architecture.\n");
+ log("\n");
+ log(" -allow-tbuf\n");
+ log(" Modify the -mapped behavior to still allow $_TBUF_ cells.\n");
+ log("\n");
+ log(" -assert\n");
+ log(" Produce a runtime error if any problems are found in the current design.\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
int counter = 0;
bool noinit = false;
bool initdrv = false;
+ bool mapped = false;
+ bool allow_tbuf = false;
bool assert_mode = false;
size_t argidx;
@@ -68,6 +80,14 @@ struct CheckPass : public Pass {
initdrv = true;
continue;
}
+ if (args[argidx] == "-mapped") {
+ mapped = true;
+ continue;
+ }
+ if (args[argidx] == "-allow-tbuf") {
+ allow_tbuf = true;
+ continue;
+ }
if (args[argidx] == "-assert") {
assert_mode = true;
continue;
@@ -135,29 +155,37 @@ struct CheckPass : public Pass {
TopoSort<string> topo;
for (auto cell : module->cells())
- for (auto &conn : cell->connections()) {
- SigSpec sig = sigmap(conn.second);
- bool logic_cell = yosys_celltypes.cell_evaluable(cell->type);
- if (cell->input(conn.first))
- for (auto bit : sig)
- if (bit.wire) {
+ {
+ if (mapped && cell->type.begins_with("$") && design->module(cell->type) == nullptr) {
+ if (allow_tbuf && cell->type == ID($_TBUF_)) goto cell_allowed;
+ log_warning("Cell %s.%s is an unmapped internal cell of type %s.\n", log_id(module), log_id(cell), log_id(cell->type));
+ counter++;
+ cell_allowed:;
+ }
+ for (auto &conn : cell->connections()) {
+ SigSpec sig = sigmap(conn.second);
+ bool logic_cell = yosys_celltypes.cell_evaluable(cell->type);
+ if (cell->input(conn.first))
+ for (auto bit : sig)
+ if (bit.wire) {
+ if (logic_cell)
+ topo.edge(stringf("wire %s", log_signal(bit)),
+ stringf("cell %s (%s)", log_id(cell), log_id(cell->type)));
+ used_wires.insert(bit);
+ }
+ if (cell->output(conn.first))
+ for (int i = 0; i < GetSize(sig); i++) {
if (logic_cell)
- topo.edge(stringf("wire %s", log_signal(bit)),
- stringf("cell %s (%s)", log_id(cell), log_id(cell->type)));
- used_wires.insert(bit);
+ topo.edge(stringf("cell %s (%s)", log_id(cell), log_id(cell->type)),
+ stringf("wire %s", log_signal(sig[i])));
+ if (sig[i].wire)
+ wire_drivers[sig[i]].push_back(stringf("port %s[%d] of cell %s (%s)",
+ log_id(conn.first), i, log_id(cell), log_id(cell->type)));
}
- if (cell->output(conn.first))
- for (int i = 0; i < GetSize(sig); i++) {
- if (logic_cell)
- topo.edge(stringf("cell %s (%s)", log_id(cell), log_id(cell->type)),
- stringf("wire %s", log_signal(sig[i])));
- if (sig[i].wire)
- wire_drivers[sig[i]].push_back(stringf("port %s[%d] of cell %s (%s)",
- log_id(conn.first), i, log_id(cell), log_id(cell->type)));
- }
- if (!cell->input(conn.first) && cell->output(conn.first))
- for (auto bit : sig)
- if (bit.wire) wire_drivers_count[bit]++;
+ if (!cell->input(conn.first) && cell->output(conn.first))
+ for (auto bit : sig)
+ if (bit.wire) wire_drivers_count[bit]++;
+ }
}
pool<SigBit> init_bits;
diff --git a/passes/cmds/chformal.cc b/passes/cmds/chformal.cc
index a8c1fef85..7e32da65f 100644
--- a/passes/cmds/chformal.cc
+++ b/passes/cmds/chformal.cc
@@ -25,14 +25,14 @@ PRIVATE_NAMESPACE_BEGIN
struct ChformalPass : public Pass {
ChformalPass() : Pass("chformal", "change formal constraints of the design") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
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,10 +59,10 @@ 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");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
bool assert2assume = false;
bool assume2assert = false;
diff --git a/passes/cmds/chtype.cc b/passes/cmds/chtype.cc
index 90d51667c..979aeadd4 100644
--- a/passes/cmds/chtype.cc
+++ b/passes/cmds/chtype.cc
@@ -24,7 +24,7 @@ PRIVATE_NAMESPACE_BEGIN
struct ChtypePass : public Pass {
ChtypePass() : Pass("chtype", "change type of cells in the design") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -40,7 +40,7 @@ struct ChtypePass : public Pass {
log("\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
IdString set_type;
dict<IdString, IdString> map_types;
diff --git a/passes/cmds/connect.cc b/passes/cmds/connect.cc
index 52611cf44..f93bada27 100644
--- a/passes/cmds/connect.cc
+++ b/passes/cmds/connect.cc
@@ -43,7 +43,7 @@ static void unset_drivers(RTLIL::Design *design, RTLIL::Module *module, SigMap &
struct ConnectPass : public Pass {
ConnectPass() : Pass("connect", "create or remove connections") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -75,7 +75,7 @@ struct ConnectPass : public Pass {
log("This command does not operate on module with processes.\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
RTLIL::Module *module = NULL;
for (auto &it : design->modules_) {
@@ -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/connwrappers.cc b/passes/cmds/connwrappers.cc
index c9ab226d5..5a15cbbaf 100644
--- a/passes/cmds/connwrappers.cc
+++ b/passes/cmds/connwrappers.cc
@@ -149,8 +149,8 @@ struct ConnwrappersWorker
};
struct ConnwrappersPass : public Pass {
- ConnwrappersPass() : Pass("connwrappers", "replace undef values with defined constants") { }
- virtual void help()
+ ConnwrappersPass() : Pass("connwrappers", "match width of input-output port pairs") { }
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -172,7 +172,7 @@ struct ConnwrappersPass : public Pass {
log("The options -signed, -unsigned, and -port can be specified multiple times.\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
ConnwrappersWorker worker;
diff --git a/passes/cmds/copy.cc b/passes/cmds/copy.cc
index fb863512b..acd2dba52 100644
--- a/passes/cmds/copy.cc
+++ b/passes/cmds/copy.cc
@@ -26,7 +26,7 @@ PRIVATE_NAMESPACE_BEGIN
struct CopyPass : public Pass {
CopyPass() : Pass("copy", "copy modules in the design") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -36,7 +36,7 @@ struct CopyPass : public Pass {
log("by this command.\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
if (args.size() != 3)
log_cmd_error("Invalid number of arguments!\n");
diff --git a/passes/cmds/cover.cc b/passes/cmds/cover.cc
index 1475475c3..628ac4c5e 100644
--- a/passes/cmds/cover.cc
+++ b/passes/cmds/cover.cc
@@ -35,7 +35,7 @@ PRIVATE_NAMESPACE_BEGIN
struct CoverPass : public Pass {
CoverPass() : Pass("cover", "print code coverage counters") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -83,7 +83,7 @@ struct CoverPass : public Pass {
log("Coverage counters are only available in Yosys for Linux.\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
std::vector<FILE*> out_files;
std::vector<std::string> patterns;
@@ -98,28 +98,30 @@ struct CoverPass : public Pass {
}
if ((args[argidx] == "-o" || args[argidx] == "-a" || args[argidx] == "-d") && argidx+1 < args.size()) {
const char *open_mode = args[argidx] == "-a" ? "a+" : "w";
- std::string filename = args[++argidx];
+ const std::string &filename = args[++argidx];
+ FILE *f = nullptr;
if (args[argidx-1] == "-d") {
#ifdef _WIN32
log_cmd_error("The 'cover -d' option is not supported on win32.\n");
#else
char filename_buffer[4096];
snprintf(filename_buffer, 4096, "%s/yosys_cover_%d_XXXXXX.txt", filename.c_str(), getpid());
- filename = mkstemps(filename_buffer, 4);
+ f = fdopen(mkstemps(filename_buffer, 4), "w");
#endif
+ } else {
+ f = fopen(filename.c_str(), open_mode);
}
- FILE *f = fopen(filename.c_str(), open_mode);
if (f == NULL) {
for (auto f : out_files)
fclose(f);
- log_cmd_error("Can't create file %s.\n", args[argidx].c_str());
+ log_cmd_error("Can't create file %s%s.\n", args[argidx-1] == "-d" ? "in directory " : "", args[argidx].c_str());
}
out_files.push_back(f);
continue;
}
break;
}
- while (argidx < args.size() && args[argidx].substr(0, 1) != "-")
+ while (argidx < args.size() && args[argidx].compare(0, 1, "-") != 0)
patterns.push_back(args[argidx++]);
extra_args(args, argidx, design);
@@ -128,7 +130,7 @@ struct CoverPass : public Pass {
log("\n");
}
-#if defined(YOSYS_ENABLE_COVER) && defined(__linux__)
+#if defined(YOSYS_ENABLE_COVER) && (defined(__linux__) || defined(__FreeBSD__))
for (auto &it : get_coverage_data()) {
if (!patterns.empty()) {
for (auto &p : patterns)
diff --git a/passes/cmds/delete.cc b/passes/cmds/delete.cc
index 6d51d30e7..5822c09f8 100644
--- a/passes/cmds/delete.cc
+++ b/passes/cmds/delete.cc
@@ -24,7 +24,7 @@ PRIVATE_NAMESPACE_BEGIN
struct DeletePass : public Pass {
DeletePass() : Pass("delete", "delete objects in the design") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -40,7 +40,7 @@ struct DeletePass : public Pass {
log("selected wires, thus 'deleting' module ports.\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
bool flag_input = false;
bool flag_output = false;
@@ -107,7 +107,7 @@ struct DeletePass : public Pass {
for (auto &it : module->cells_) {
if (design->selected(module, it.second))
delete_cells.insert(it.second);
- if ((it.second->type == "$memrd" || it.second->type == "$memwr") &&
+ if (it.second->type.in("$memrd", "$memwr") &&
delete_mems.count(it.second->parameters.at("\\MEMID").decode_string()) != 0)
delete_cells.insert(it.second);
}
diff --git a/passes/cmds/design.cc b/passes/cmds/design.cc
index 490c8dde5..172addcc1 100644
--- a/passes/cmds/design.cc
+++ b/passes/cmds/design.cc
@@ -27,7 +27,7 @@ std::vector<RTLIL::Design*> pushed_designs;
struct DesignPass : public Pass {
DesignPass() : Pass("design", "save, restore and reset current design") { }
- virtual ~DesignPass() {
+ ~DesignPass() YS_OVERRIDE {
for (auto &it : saved_designs)
delete it.second;
saved_designs.clear();
@@ -35,7 +35,7 @@ struct DesignPass : public Pass {
delete it;
pushed_designs.clear();
}
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -94,7 +94,7 @@ struct DesignPass : public Pass {
log("between calls to 'read_verilog'. This command resets this memory.\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
bool got_mode = false;
bool reset_mode = false;
diff --git a/passes/cmds/edgetypes.cc b/passes/cmds/edgetypes.cc
index 7b75a009f..58ed6457d 100644
--- a/passes/cmds/edgetypes.cc
+++ b/passes/cmds/edgetypes.cc
@@ -25,7 +25,7 @@ PRIVATE_NAMESPACE_BEGIN
struct EdgetypePass : public Pass {
EdgetypePass() : Pass("edgetypes", "list all types of edges in selection") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -35,7 +35,7 @@ struct EdgetypePass : public Pass {
log("is a 4-tuple of source and sink cell type and port name.\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++) {
diff --git a/passes/cmds/logcmd.cc b/passes/cmds/logcmd.cc
index 85386f3d2..522e1089d 100644
--- a/passes/cmds/logcmd.cc
+++ b/passes/cmds/logcmd.cc
@@ -27,7 +27,7 @@ PRIVATE_NAMESPACE_BEGIN
struct LogPass : public Pass {
LogPass() : Pass("log", "print text and log files") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -52,7 +52,7 @@ struct LogPass : public Pass {
log(" do not append a newline\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design*)
+ void execute(std::vector<std::string> args, RTLIL::Design*) YS_OVERRIDE
{
size_t argidx;
bool to_stdout = false;
diff --git a/passes/cmds/ltp.cc b/passes/cmds/ltp.cc
new file mode 100644
index 000000000..05701710b
--- /dev/null
+++ b/passes/cmds/ltp.cc
@@ -0,0 +1,185 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "kernel/yosys.h"
+#include "kernel/celltypes.h"
+#include "kernel/sigtools.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct LtpWorker
+{
+ RTLIL::Design *design;
+ RTLIL::Module *module;
+ SigMap sigmap;
+
+ dict<SigBit, tuple<int, SigBit, Cell*>> bits;
+ dict<SigBit, dict<SigBit, Cell*>> bit2bits;
+ dict<SigBit, tuple<SigBit, Cell*>> bit2ff;
+
+ int maxlvl;
+ SigBit maxbit;
+ pool<SigBit> busy;
+
+ LtpWorker(RTLIL::Module *module, bool noff) : design(module->design), module(module), sigmap(module)
+ {
+ CellTypes ff_celltypes;
+
+ if (noff) {
+ ff_celltypes.setup_internals_mem();
+ ff_celltypes.setup_stdcells_mem();
+ }
+
+ for (auto wire : module->selected_wires())
+ for (auto bit : sigmap(wire))
+ bits[bit] = tuple<int, SigBit, Cell*>(-1, State::Sx, nullptr);
+
+ for (auto cell : module->selected_cells())
+ {
+ pool<SigBit> src_bits, dst_bits;
+
+ for (auto &conn : cell->connections())
+ for (auto bit : sigmap(conn.second)) {
+ if (cell->input(conn.first))
+ src_bits.insert(bit);
+ if (cell->output(conn.first))
+ dst_bits.insert(bit);
+ }
+
+ if (noff && ff_celltypes.cell_known(cell->type)) {
+ for (auto s : src_bits)
+ for (auto d : dst_bits) {
+ bit2ff[s] = tuple<SigBit, Cell*>(d, cell);
+ break;
+ }
+ continue;
+ }
+
+ for (auto s : src_bits)
+ for (auto d : dst_bits)
+ bit2bits[s][d] = cell;
+ }
+
+ maxlvl = -1;
+ maxbit = State::Sx;
+ }
+
+ void runner(SigBit bit, int level, SigBit from, Cell *via)
+ {
+ auto &bitinfo = bits.at(bit);
+
+ if (get<0>(bitinfo) >= level)
+ return;
+
+ if (busy.count(bit) > 0) {
+ log_warning("Detected loop at %s in %s\n", log_signal(bit), log_id(module));
+ return;
+ }
+
+ busy.insert(bit);
+ get<0>(bitinfo) = level;
+ get<1>(bitinfo) = from;
+ get<2>(bitinfo) = via;
+
+ if (level > maxlvl) {
+ maxlvl = level;
+ maxbit = bit;
+ }
+
+ if (bit2bits.count(bit)) {
+ for (auto &it : bit2bits.at(bit))
+ runner(it.first, level+1, bit, it.second);
+ }
+
+ busy.erase(bit);
+ }
+
+ void printpath(SigBit bit)
+ {
+ auto &bitinfo = bits.at(bit);
+ if (get<2>(bitinfo)) {
+ printpath(get<1>(bitinfo));
+ log("%5d: %s (via %s)\n", get<0>(bitinfo), log_signal(bit), log_id(get<2>(bitinfo)));
+ } else {
+ log("%5d: %s\n", get<0>(bitinfo), log_signal(bit));
+ }
+ }
+
+ void run()
+ {
+ for (auto &it : bits)
+ if (get<0>(it.second) < 0)
+ runner(it.first, 0, State::Sx, nullptr);
+
+ log("\n");
+ log("Longest topological path in %s (length=%d):\n", log_id(module), maxlvl);
+
+ if (maxlvl >= 0)
+ printpath(maxbit);
+
+ if (bit2ff.count(maxbit))
+ log("%5s: %s (via %s)\n", "ff", log_signal(get<0>(bit2ff.at(maxbit))), log_id(get<1>(bit2ff.at(maxbit))));
+ }
+};
+
+struct LtpPass : public Pass {
+ LtpPass() : Pass("ltp", "print longest topological path") { }
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" ltp [options] [selection]\n");
+ log("\n");
+ log("This command prints the longest topological path in the design. (Only considers\n");
+ log("paths within a single module, so the design must be flattened.)\n");
+ log("\n");
+ log(" -noff\n");
+ log(" automatically exclude FF cell types\n");
+ log("\n");
+ }
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ bool noff = false;
+
+ log_header(design, "Executing LTP pass (find longest path).\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ if (args[argidx] == "-noff") {
+ noff = true;
+ continue;
+ }
+ break;
+ }
+
+ extra_args(args, argidx, design);
+
+ for (Module *module : design->selected_modules())
+ {
+ if (module->has_processes_warn())
+ continue;
+
+ LtpWorker worker(module, noff);
+ worker.run();
+ }
+ }
+} LtpPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/cmds/plugin.cc b/passes/cmds/plugin.cc
index 828c671de..4c16b56c4 100644
--- a/passes/cmds/plugin.cc
+++ b/passes/cmds/plugin.cc
@@ -23,9 +23,18 @@
# include <dlfcn.h>
#endif
+#ifdef WITH_PYTHON
+# include <boost/algorithm/string/predicate.hpp>
+# include <Python.h>
+# include <boost/filesystem.hpp>
+#endif
+
YOSYS_NAMESPACE_BEGIN
std::map<std::string, void*> loaded_plugins;
+#ifdef WITH_PYTHON
+std::map<std::string, void*> loaded_python_plugins;
+#endif
std::map<std::string, std::string> loaded_plugin_aliases;
#ifdef YOSYS_ENABLE_PLUGINS
@@ -36,7 +45,35 @@ void load_plugin(std::string filename, std::vector<std::string> aliases)
if (filename.find('/') == std::string::npos)
filename = "./" + filename;
+ #ifdef WITH_PYTHON
+ if (!loaded_plugins.count(filename) && !loaded_python_plugins.count(filename)) {
+ #else
if (!loaded_plugins.count(filename)) {
+ #endif
+
+ #ifdef WITH_PYTHON
+
+ boost::filesystem::path full_path(filename);
+
+ if(strcmp(full_path.extension().c_str(), ".py") == 0)
+ {
+ std::string path(full_path.parent_path().c_str());
+ filename = full_path.filename().c_str();
+ filename = filename.substr(0,filename.size()-3);
+ PyRun_SimpleString(("sys.path.insert(0,\""+path+"\")").c_str());
+ PyErr_Print();
+ PyObject *module_p = PyImport_ImportModule(filename.c_str());
+ if(module_p == NULL)
+ {
+ PyErr_Print();
+ log_cmd_error("Can't load python module `%s'\n", full_path.filename().c_str());
+ return;
+ }
+ loaded_python_plugins[orig_filename] = module_p;
+ Pass::init_register();
+ } else {
+ #endif
+
void *hdl = dlopen(filename.c_str(), RTLD_LAZY|RTLD_LOCAL);
if (hdl == NULL && orig_filename.find('/') == std::string::npos)
hdl = dlopen((proc_share_dirname() + "plugins/" + orig_filename + ".so").c_str(), RTLD_LAZY|RTLD_LOCAL);
@@ -44,6 +81,10 @@ void load_plugin(std::string filename, std::vector<std::string> aliases)
log_cmd_error("Can't load module `%s': %s\n", filename.c_str(), dlerror());
loaded_plugins[orig_filename] = hdl;
Pass::init_register();
+
+ #ifdef WITH_PYTHON
+ }
+ #endif
}
for (auto &alias : aliases)
@@ -58,7 +99,7 @@ void load_plugin(std::string, std::vector<std::string>)
struct PluginPass : public Pass {
PluginPass() : Pass("plugin", "load and list loaded plugins") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -76,7 +117,7 @@ struct PluginPass : public Pass {
log(" List loaded plugins\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
std::string plugin_filename;
std::vector<std::string> plugin_aliases;
@@ -107,7 +148,11 @@ struct PluginPass : public Pass {
if (list_mode)
{
log("\n");
+#ifdef WITH_PYTHON
+ if (loaded_plugins.empty() and loaded_python_plugins.empty())
+#else
if (loaded_plugins.empty())
+#endif
log("No plugins loaded.\n");
else
log("Loaded plugins:\n");
@@ -115,6 +160,11 @@ struct PluginPass : public Pass {
for (auto &it : loaded_plugins)
log(" %s\n", it.first.c_str());
+#ifdef WITH_PYTHON
+ for (auto &it : loaded_python_plugins)
+ log(" %s\n", it.first.c_str());
+#endif
+
if (!loaded_plugin_aliases.empty()) {
log("\n");
int max_alias_len = 1;
diff --git a/passes/cmds/portlist.cc b/passes/cmds/portlist.cc
new file mode 100644
index 000000000..38c4a8597
--- /dev/null
+++ b/passes/cmds/portlist.cc
@@ -0,0 +1,93 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "kernel/yosys.h"
+#include "kernel/sigtools.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct PortlistPass : public Pass {
+ PortlistPass() : Pass("portlist", "list (top-level) ports") { }
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" portlist [options] [selection]\n");
+ log("\n");
+ log("This command lists all module ports found in the selected modules.\n");
+ log("\n");
+ log("If no selection is provided then it lists the ports on the top module.\n");
+ log("\n");
+ log(" -m\n");
+ log(" print verilog blackbox module definitions instead of port lists\n");
+ log("\n");
+ }
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ bool m_mode = false;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ if (args[argidx] == "-m") {
+ m_mode = true;
+ continue;
+ }
+ break;
+ }
+
+ bool first_module = true;
+
+ auto handle_module = [&](RTLIL::Module *module) {
+ vector<string> ports;
+ if (first_module)
+ first_module = false;
+ else
+ log("\n");
+ for (auto port : module->ports) {
+ auto *w = module->wire(port);
+ ports.push_back(stringf("%s [%d:%d] %s", w->port_input ? w->port_output ? "inout" : "input" : "output",
+ w->upto ? w->start_offset : w->start_offset + w->width - 1,
+ w->upto ? w->start_offset + w->width - 1 : w->start_offset,
+ log_id(w)));
+ }
+ log("module %s%s\n", log_id(module), m_mode ? " (" : "");
+ for (int i = 0; i < GetSize(ports); i++)
+ log("%s%s\n", ports[i].c_str(), m_mode && i+1 < GetSize(ports) ? "," : "");
+ if (m_mode)
+ log(");\nendmodule\n");
+ };
+
+ if (argidx == args.size())
+ {
+ auto *top = design->top_module();
+ if (top == nullptr)
+ log_cmd_error("Can't find top module in current design!\n");
+ handle_module(top);
+ }
+ else
+ {
+ extra_args(args, argidx, design);
+ for (auto module : design->selected_modules())
+ handle_module(module);
+ }
+ }
+} PortlistPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/cmds/qwp.cc b/passes/cmds/qwp.cc
index 1b800b6df..adbe89e31 100644
--- a/passes/cmds/qwp.cc
+++ b/passes/cmds/qwp.cc
@@ -291,7 +291,7 @@ struct QwpWorker
// gaussian elimination
for (int i = 0; i < N; i++)
{
- if (config.verbose && ((i+1) % (N/15)) == 0)
+ if (config.verbose && N > 15 && ((i+1) % (N/15)) == 0)
log("> Solved %d%%: %d/%d\n", (100*(i+1))/N, i+1, N);
// find best row
@@ -778,7 +778,7 @@ struct QwpWorker
struct QwpPass : public Pass {
QwpPass() : Pass("qwp", "quadratic wirelength placer") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -808,7 +808,7 @@ struct QwpPass : public Pass {
log("dense matrix operations. It is only a toy-placer for small circuits.\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
QwpConfig config;
xorshift32_state = 123456789;
@@ -835,6 +835,7 @@ struct QwpPass : public Pass {
}
if (args[argidx] == "-dump" && argidx+1 < args.size()) {
config.dump_file.open(args[++argidx], std::ofstream::trunc);
+ yosys_output_files.insert(args[argidx]);
continue;
}
break;
diff --git a/passes/cmds/rename.cc b/passes/cmds/rename.cc
index 6a002869b..9b1830b7b 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,9 +57,54 @@ 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") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -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,28 +140,48 @@ 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");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ 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/scatter.cc b/passes/cmds/scatter.cc
index f083e1f67..7123ba9fb 100644
--- a/passes/cmds/scatter.cc
+++ b/passes/cmds/scatter.cc
@@ -27,7 +27,7 @@ PRIVATE_NAMESPACE_BEGIN
struct ScatterPass : public Pass {
ScatterPass() : Pass("scatter", "add additional intermediate nets") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -41,7 +41,7 @@ struct ScatterPass : public Pass {
log("Use the opt_clean command to get rid of the additional nets.\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
CellTypes ct(design);
extra_args(args, 1, design);
diff --git a/passes/cmds/scc.cc b/passes/cmds/scc.cc
index 76363e051..ad0554bae 100644
--- a/passes/cmds/scc.cc
+++ b/passes/cmds/scc.cc
@@ -218,7 +218,7 @@ struct SccWorker
struct SccPass : public Pass {
SccPass() : Pass("scc", "detect strongly connected components (logic loops)") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -255,7 +255,7 @@ struct SccPass : public Pass {
log(" that are part of a found logic loop\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
std::map<std::string, std::string> setAttr;
bool allCellTypes = false;
@@ -301,42 +301,41 @@ struct SccPass : public Pass {
RTLIL::Selection newSelection(false);
int scc_counter = 0;
- for (auto &mod_it : design->modules_)
- if (design->selected(mod_it.second))
- {
- SccWorker worker(design, mod_it.second, nofeedbackMode, allCellTypes, maxDepth);
+ for (auto mod : design->selected_modules())
+ {
+ SccWorker worker(design, mod, nofeedbackMode, allCellTypes, maxDepth);
- if (!setAttr.empty())
+ if (!setAttr.empty())
+ {
+ for (const auto &cells : worker.sccList)
{
- for (const auto &cells : worker.sccList)
+ for (auto attr : setAttr)
{
- for (auto attr : setAttr)
- {
- IdString attr_name(RTLIL::escape_id(attr.first));
- string attr_valstr = attr.second;
- string index = stringf("%d", scc_counter);
-
- for (size_t pos = 0; (pos = attr_valstr.find("{}", pos)) != string::npos; pos += index.size())
- attr_valstr.replace(pos, 2, index);
+ IdString attr_name(RTLIL::escape_id(attr.first));
+ string attr_valstr = attr.second;
+ string index = stringf("%d", scc_counter);
- Const attr_value(attr_valstr);
+ for (size_t pos = 0; (pos = attr_valstr.find("{}", pos)) != string::npos; pos += index.size())
+ attr_valstr.replace(pos, 2, index);
- for (auto cell : cells)
- cell->attributes[attr_name] = attr_value;
- }
+ Const attr_value(attr_valstr);
- scc_counter++;
+ for (auto cell : cells)
+ cell->attributes[attr_name] = attr_value;
}
- }
- else
- {
- scc_counter += GetSize(worker.sccList);
- }
- if (selectMode)
- worker.select(newSelection);
+ scc_counter++;
+ }
+ }
+ else
+ {
+ scc_counter += GetSize(worker.sccList);
}
+ if (selectMode)
+ worker.select(newSelection);
+ }
+
if (expect >= 0) {
if (scc_counter == expect)
log("Found and expected %d SCCs.\n", scc_counter);
diff --git a/passes/cmds/scratchpad.cc b/passes/cmds/scratchpad.cc
new file mode 100644
index 000000000..34ec0863a
--- /dev/null
+++ b/passes/cmds/scratchpad.cc
@@ -0,0 +1,141 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * 2019 Nina Engelhardt <nak@symbioticeda.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
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "kernel/register.h"
+#include "kernel/rtlil.h"
+#include "kernel/log.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct ScratchpadPass : public Pass {
+ ScratchpadPass() : Pass("scratchpad", "get/set values in the scratchpad") { }
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" scratchpad [options]\n");
+ log("\n");
+ log("This pass allows to read and modify values from the scratchpad of the current\n");
+ log("design. Options:\n");
+ log("\n");
+ log(" -get <identifier>\n");
+ log(" print the value saved in the scratchpad under the given identifier.\n");
+ log("\n");
+ log(" -set <identifier> <value>\n");
+ log(" save the given value in the scratchpad under the given identifier.\n");
+ log("\n");
+ log(" -unset <identifier>\n");
+ log(" remove the entry for the given identifier from the scratchpad.\n");
+ log("\n");
+ log(" -copy <identifier_from> <identifier_to>\n");
+ log(" copy the value of the first identifier to the second identifier.\n");
+ log("\n");
+ log(" -assert <identifier> <value>\n");
+ log(" assert that the entry for the given identifier is set to the given value.\n");
+ log("\n");
+ log(" -assert-set <identifier>\n");
+ log(" assert that the entry for the given identifier exists.\n");
+ log("\n");
+ log(" -assert-unset <identifier>\n");
+ log(" assert that the entry for the given identifier does not exist.\n");
+ log("\n");
+ log("The identifier may not contain whitespace. By convention, it is usually prefixed\n");
+ log("by the name of the pass that uses it, e.g. 'opt.did_something'. If the value\n");
+ log("contains whitespace, it must be enclosed in double quotes.\n");
+ log("\n");
+ }
+
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ if (args[argidx] == "-get" && argidx+1 < args.size()) {
+ string identifier = args[++argidx];
+ if (design->scratchpad.count(identifier)) {
+ log("%s\n", design->scratchpad_get_string(identifier).c_str());
+ } else if (RTLIL::constpad.count(identifier)) {
+ log("%s\n", RTLIL::constpad.at(identifier).c_str());
+ } else {
+ log("\"%s\" not set\n", identifier.c_str());
+ }
+ continue;
+ }
+ if (args[argidx] == "-set" && argidx+2 < args.size()) {
+ string identifier = args[++argidx];
+ if (RTLIL::constpad.count(identifier))
+ log_error("scratchpad entry \"%s\" is a global constant\n", identifier.c_str());
+ string value = args[++argidx];
+ if (value.front() == '\"' && value.back() == '\"') value = value.substr(1, value.size() - 2);
+ design->scratchpad_set_string(identifier, value);
+ continue;
+ }
+ if (args[argidx] == "-unset" && argidx+1 < args.size()) {
+ string identifier = args[++argidx];
+ design->scratchpad_unset(identifier);
+ continue;
+ }
+ if (args[argidx] == "-copy" && argidx+2 < args.size()) {
+ string identifier_from = args[++argidx];
+ string identifier_to = args[++argidx];
+ string value;
+ if (design->scratchpad.count(identifier_from))
+ value = design->scratchpad_get_string(identifier_from);
+ else if (RTLIL::constpad.count(identifier_from))
+ value = RTLIL::constpad.at(identifier_from);
+ else
+ log_error("\"%s\" not set\n", identifier_from.c_str());
+ if (RTLIL::constpad.count(identifier_to))
+ log_error("scratchpad entry \"%s\" is a global constant\n", identifier_to.c_str());
+ design->scratchpad_set_string(identifier_to, value);
+ continue;
+ }
+ if (args[argidx] == "-assert" && argidx+2 < args.size()) {
+ string identifier = args[++argidx];
+ string expected = args[++argidx];
+ if (expected.front() == '\"' && expected.back() == '\"') expected = expected.substr(1, expected.size() - 2);
+ if (design->scratchpad.count(identifier) == 0)
+ log_error("scratchpad entry '%s' is not defined\n", identifier.c_str());
+ string value = design->scratchpad_get_string(identifier);
+ if (value != expected) {
+ log_error("scratchpad entry '%s' is set to '%s' instead of the asserted '%s'\n",
+ identifier.c_str(), value.c_str(), expected.c_str());
+ }
+ continue;
+ }
+ if (args[argidx] == "-assert-set" && argidx+1 < args.size()) {
+ string identifier = args[++argidx];
+ if (design->scratchpad.count(identifier) == 0)
+ log_error("scratchpad entry '%s' is not defined\n", identifier.c_str());
+ continue;
+ }
+ if (args[argidx] == "-assert-unset" && argidx+1 < args.size()) {
+ string identifier = args[++argidx];
+ if (design->scratchpad.count(identifier) > 0)
+ log_error("scratchpad entry '%s' is defined\n", identifier.c_str());
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design, false);
+ }
+} ScratchpadPass;
+PRIVATE_NAMESPACE_END
diff --git a/passes/cmds/select.cc b/passes/cmds/select.cc
index 7d2f4262b..0f1f05ccb 100644
--- a/passes/cmds/select.cc
+++ b/passes/cmds/select.cc
@@ -34,7 +34,7 @@ static bool match_ids(RTLIL::IdString id, std::string pattern)
{
if (id == pattern)
return true;
- if (id.size() > 0 && id[0] == '\\' && id.substr(1) == pattern)
+ if (id.size() > 0 && id[0] == '\\' && id.compare(1, std::string::npos, pattern.c_str()) == 0)
return true;
if (patmatch(pattern.c_str(), id.c_str()))
return true;
@@ -124,11 +124,11 @@ static bool match_attr(const dict<RTLIL::IdString, RTLIL::Const> &attributes, st
size_t pos = match_expr.find_first_of("<!=>");
if (pos != std::string::npos) {
- if (match_expr.substr(pos, 2) == "!=")
+ if (match_expr.compare(pos, 2, "!=") == 0)
return match_attr(attributes, match_expr.substr(0, pos), match_expr.substr(pos+2), '!');
- if (match_expr.substr(pos, 2) == "<=")
+ if (match_expr.compare(pos, 2, "<=") == 0)
return match_attr(attributes, match_expr.substr(0, pos), match_expr.substr(pos+2), '[');
- if (match_expr.substr(pos, 2) == ">=")
+ if (match_expr.compare(pos, 2, ">=") == 0)
return match_attr(attributes, match_expr.substr(0, pos), match_expr.substr(pos+2), ']');
return match_attr(attributes, match_expr.substr(0, pos), match_expr.substr(pos+1), match_expr[pos]);
}
@@ -664,7 +664,7 @@ static void select_stmt(RTLIL::Design *design, std::string arg)
} else
if (arg == "%D") {
if (work_stack.size() < 2)
- log_cmd_error("Must have at least two elements on the stack for operator %%d.\n");
+ log_cmd_error("Must have at least two elements on the stack for operator %%D.\n");
select_op_diff(design, work_stack[work_stack.size()-1], work_stack[work_stack.size()-2]);
work_stack[work_stack.size()-2] = work_stack[work_stack.size()-1];
work_stack.pop_back();
@@ -693,7 +693,7 @@ static void select_stmt(RTLIL::Design *design, std::string arg)
} else
if (arg == "%C") {
if (work_stack.size() < 1)
- log_cmd_error("Must have at least one element on the stack for operator %%M.\n");
+ log_cmd_error("Must have at least one element on the stack for operator %%C.\n");
select_op_module_to_cells(design, work_stack[work_stack.size()-1]);
} else
if (arg == "%c") {
@@ -711,32 +711,32 @@ static void select_stmt(RTLIL::Design *design, std::string arg)
log_cmd_error("Must have at least one element on the stack for operator %%a.\n");
select_op_alias(design, work_stack[work_stack.size()-1]);
} else
- if (arg == "%x" || (arg.size() > 2 && arg.substr(0, 2) == "%x" && (arg[2] == ':' || arg[2] == '*' || arg[2] == '.' || ('0' <= arg[2] && arg[2] <= '9')))) {
+ if (arg == "%x" || (arg.size() > 2 && arg.compare(0, 2, "%x") == 0 && (arg[2] == ':' || arg[2] == '*' || arg[2] == '.' || ('0' <= arg[2] && arg[2] <= '9')))) {
if (work_stack.size() < 1)
log_cmd_error("Must have at least one element on the stack for operator %%x.\n");
select_op_expand(design, arg, 'x', false);
} else
- if (arg == "%ci" || (arg.size() > 3 && arg.substr(0, 3) == "%ci" && (arg[3] == ':' || arg[3] == '*' || arg[3] == '.' || ('0' <= arg[3] && arg[3] <= '9')))) {
+ if (arg == "%ci" || (arg.size() > 3 && arg.compare(0, 3, "%ci") == 0 && (arg[3] == ':' || arg[3] == '*' || arg[3] == '.' || ('0' <= arg[3] && arg[3] <= '9')))) {
if (work_stack.size() < 1)
log_cmd_error("Must have at least one element on the stack for operator %%ci.\n");
select_op_expand(design, arg, 'i', false);
} else
- if (arg == "%co" || (arg.size() > 3 && arg.substr(0, 3) == "%co" && (arg[3] == ':' || arg[3] == '*' || arg[3] == '.' || ('0' <= arg[3] && arg[3] <= '9')))) {
+ if (arg == "%co" || (arg.size() > 3 && arg.compare(0, 3, "%co") == 0 && (arg[3] == ':' || arg[3] == '*' || arg[3] == '.' || ('0' <= arg[3] && arg[3] <= '9')))) {
if (work_stack.size() < 1)
log_cmd_error("Must have at least one element on the stack for operator %%co.\n");
select_op_expand(design, arg, 'o', false);
} else
- if (arg == "%xe" || (arg.size() > 3 && arg.substr(0, 3) == "%xe" && (arg[3] == ':' || arg[3] == '*' || arg[3] == '.' || ('0' <= arg[3] && arg[3] <= '9')))) {
+ if (arg == "%xe" || (arg.size() > 3 && arg.compare(0, 3, "%xe") == 0 && (arg[3] == ':' || arg[3] == '*' || arg[3] == '.' || ('0' <= arg[3] && arg[3] <= '9')))) {
if (work_stack.size() < 1)
log_cmd_error("Must have at least one element on the stack for operator %%xe.\n");
select_op_expand(design, arg, 'x', true);
} else
- if (arg == "%cie" || (arg.size() > 4 && arg.substr(0, 4) == "%cie" && (arg[4] == ':' || arg[4] == '*' || arg[4] == '.' || ('0' <= arg[4] && arg[4] <= '9')))) {
+ if (arg == "%cie" || (arg.size() > 4 && arg.compare(0, 4, "%cie") == 0 && (arg[4] == ':' || arg[4] == '*' || arg[4] == '.' || ('0' <= arg[4] && arg[4] <= '9')))) {
if (work_stack.size() < 1)
log_cmd_error("Must have at least one element on the stack for operator %%cie.\n");
select_op_expand(design, arg, 'i', true);
} else
- if (arg == "%coe" || (arg.size() > 4 && arg.substr(0, 4) == "%coe" && (arg[4] == ':' || arg[4] == '*' || arg[4] == '.' || ('0' <= arg[4] && arg[4] <= '9')))) {
+ if (arg == "%coe" || (arg.size() > 4 && arg.compare(0, 4, "%coe") == 0 && (arg[4] == ':' || arg[4] == '*' || arg[4] == '.' || ('0' <= arg[4] && arg[4] <= '9')))) {
if (work_stack.size() < 1)
log_cmd_error("Must have at least one element on the stack for operator %%coe.\n");
select_op_expand(design, arg, 'o', true);
@@ -760,10 +760,13 @@ static void select_stmt(RTLIL::Design *design, std::string arg)
if (!design->selected_active_module.empty()) {
arg_mod = design->selected_active_module;
arg_memb = arg;
+ } else
+ if (GetSize(arg) >= 2 && arg[0] >= 'a' && arg[0] <= 'z' && arg[1] == ':') {
+ arg_mod = "*", arg_memb = arg;
} else {
size_t pos = arg.find('/');
if (pos == std::string::npos) {
- if (arg.find(':') == std::string::npos || arg.substr(0, 1) == "A")
+ if (arg.find(':') == std::string::npos || arg.compare(0, 1, "A") == 0)
arg_mod = arg;
else
arg_mod = "*", arg_memb = arg;
@@ -784,7 +787,7 @@ static void select_stmt(RTLIL::Design *design, std::string arg)
sel.full_selection = false;
for (auto &mod_it : design->modules_)
{
- if (arg_mod.substr(0, 2) == "A:") {
+ if (arg_mod.compare(0, 2, "A:") == 0) {
if (!match_attr(mod_it.second->attributes, arg_mod.substr(2)))
continue;
} else
@@ -797,27 +800,27 @@ static void select_stmt(RTLIL::Design *design, std::string arg)
}
RTLIL::Module *mod = mod_it.second;
- if (arg_memb.substr(0, 2) == "w:") {
+ if (arg_memb.compare(0, 2, "w:") == 0) {
for (auto &it : mod->wires_)
if (match_ids(it.first, arg_memb.substr(2)))
sel.selected_members[mod->name].insert(it.first);
} else
- if (arg_memb.substr(0, 2) == "i:") {
+ if (arg_memb.compare(0, 2, "i:") == 0) {
for (auto &it : mod->wires_)
if (it.second->port_input && match_ids(it.first, arg_memb.substr(2)))
sel.selected_members[mod->name].insert(it.first);
} else
- if (arg_memb.substr(0, 2) == "o:") {
+ if (arg_memb.compare(0, 2, "o:") == 0) {
for (auto &it : mod->wires_)
if (it.second->port_output && match_ids(it.first, arg_memb.substr(2)))
sel.selected_members[mod->name].insert(it.first);
} else
- if (arg_memb.substr(0, 2) == "x:") {
+ if (arg_memb.compare(0, 2, "x:") == 0) {
for (auto &it : mod->wires_)
if ((it.second->port_input || it.second->port_output) && match_ids(it.first, arg_memb.substr(2)))
sel.selected_members[mod->name].insert(it.first);
} else
- if (arg_memb.substr(0, 2) == "s:") {
+ if (arg_memb.compare(0, 2, "s:") == 0) {
size_t delim = arg_memb.substr(2).find(':');
if (delim == std::string::npos) {
int width = atoi(arg_memb.substr(2).c_str());
@@ -834,27 +837,27 @@ static void select_stmt(RTLIL::Design *design, std::string arg)
sel.selected_members[mod->name].insert(it.first);
}
} else
- if (arg_memb.substr(0, 2) == "m:") {
+ if (arg_memb.compare(0, 2, "m:") == 0) {
for (auto &it : mod->memories)
if (match_ids(it.first, arg_memb.substr(2)))
sel.selected_members[mod->name].insert(it.first);
} else
- if (arg_memb.substr(0, 2) == "c:") {
+ if (arg_memb.compare(0, 2, "c:") ==0) {
for (auto &it : mod->cells_)
if (match_ids(it.first, arg_memb.substr(2)))
sel.selected_members[mod->name].insert(it.first);
} else
- if (arg_memb.substr(0, 2) == "t:") {
+ if (arg_memb.compare(0, 2, "t:") == 0) {
for (auto &it : mod->cells_)
if (match_ids(it.second->type, arg_memb.substr(2)))
sel.selected_members[mod->name].insert(it.first);
} else
- if (arg_memb.substr(0, 2) == "p:") {
+ if (arg_memb.compare(0, 2, "p:") == 0) {
for (auto &it : mod->processes)
if (match_ids(it.first, arg_memb.substr(2)))
sel.selected_members[mod->name].insert(it.first);
} else
- if (arg_memb.substr(0, 2) == "a:") {
+ if (arg_memb.compare(0, 2, "a:") == 0) {
for (auto &it : mod->wires_)
if (match_attr(it.second->attributes, arg_memb.substr(2)))
sel.selected_members[mod->name].insert(it.first);
@@ -868,12 +871,12 @@ static void select_stmt(RTLIL::Design *design, std::string arg)
if (match_attr(it.second->attributes, arg_memb.substr(2)))
sel.selected_members[mod->name].insert(it.first);
} else
- if (arg_memb.substr(0, 2) == "r:") {
+ if (arg_memb.compare(0, 2, "r:") == 0) {
for (auto &it : mod->cells_)
if (match_attr(it.second->parameters, arg_memb.substr(2)))
sel.selected_members[mod->name].insert(it.first);
} else {
- if (arg_memb.substr(0, 2) == "n:")
+ if (arg_memb.compare(0, 2, "n:") == 0)
arg_memb = arg_memb.substr(2);
for (auto &it : mod->wires_)
if (match_ids(it.first, arg_memb))
@@ -893,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
@@ -901,7 +927,7 @@ void handle_extra_select_args(Pass *pass, vector<string> args, size_t argidx, si
{
work_stack.clear();
for (; argidx < args_size; argidx++) {
- if (args[argidx].substr(0, 1) == "-") {
+ if (args[argidx].compare(0, 1, "-") == 0) {
if (pass != NULL)
pass->cmd_error(args, argidx, "Unexpected option in selection arguments.");
else
@@ -947,7 +973,7 @@ PRIVATE_NAMESPACE_BEGIN
struct SelectPass : public Pass {
SelectPass() : Pass("select", "modify and view the list of selected objects") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -1164,7 +1190,7 @@ struct SelectPass : public Pass {
log(" select */t:SWITCH %%x:+[GATE] */t:SWITCH %%d\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
bool add_mode = false;
bool del_mode = false;
@@ -1263,6 +1289,7 @@ struct SelectPass : public Pass {
log_cmd_error("Option -read can not be combined with a selection expression.\n");
std::ifstream f(read_file);
+ yosys_input_files.insert(read_file);
if (f.fail())
log_error("Can't open '%s' for reading: %s\n", read_file.c_str(), strerror(errno));
@@ -1331,6 +1358,7 @@ struct SelectPass : public Pass {
FILE *f = NULL;
if (!write_file.empty()) {
f = fopen(write_file.c_str(), "w");
+ yosys_output_files.insert(write_file);
if (f == NULL)
log_error("Can't open '%s' for writing: %s\n", write_file.c_str(), strerror(errno));
}
@@ -1389,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;
}
@@ -1399,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;
}
@@ -1426,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;
}
@@ -1465,7 +1512,7 @@ struct SelectPass : public Pass {
struct CdPass : public Pass {
CdPass() : Pass("cd", "a shortcut for 'select -module <name>'") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -1491,7 +1538,7 @@ struct CdPass : public Pass {
log("This is just a shortcut for 'select -clear'.\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
if (args.size() != 1 && args.size() != 2)
log_cmd_error("Invalid number of arguments.\n");
@@ -1573,7 +1620,7 @@ static void log_matches(const char *title, Module *module, T list)
struct LsPass : public Pass {
LsPass() : Pass("ls", "list modules or objects in modules") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -1584,7 +1631,7 @@ struct LsPass : public Pass {
log("When an active module is selected, this prints a list of objects in the module.\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
size_t argidx = 1;
extra_args(args, argidx, design);
diff --git a/passes/cmds/setattr.cc b/passes/cmds/setattr.cc
index 689e3148b..1ccfc2e86 100644
--- a/passes/cmds/setattr.cc
+++ b/passes/cmds/setattr.cc
@@ -34,7 +34,7 @@ struct setunset_t
setunset_t(std::string set_name, std::string set_value) : name(RTLIL::escape_id(set_name)), value(), unset(false)
{
- if (set_value.substr(0, 1) == "\"" && set_value.substr(GetSize(set_value)-1) == "\"") {
+ if (set_value.compare(0, 1, "\"") == 0 && set_value.compare(GetSize(set_value)-1, std::string::npos, "\"") == 0) {
value = RTLIL::Const(set_value.substr(1, GetSize(set_value)-2));
} else {
RTLIL::SigSpec sig_value;
@@ -56,7 +56,7 @@ static void do_setunset(dict<RTLIL::IdString, RTLIL::Const> &attrs, const std::v
struct SetattrPass : public Pass {
SetattrPass() : Pass("setattr", "set/unset attributes on objects") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -69,7 +69,7 @@ struct SetattrPass : public Pass {
log("instead of objects within modules.\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
std::vector<setunset_t> setunset_list;
bool flag_mod = false;
@@ -128,9 +128,48 @@ struct SetattrPass : public Pass {
}
} SetattrPass;
+struct WbflipPass : public Pass {
+ WbflipPass() : Pass("wbflip", "flip the whitebox attribute") { }
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" wbflip [selection]\n");
+ log("\n");
+ log("Flip the whitebox attribute on selected cells. I.e. if it's set, unset it, and\n");
+ log("vice-versa. Blackbox cells are not effected by this command.\n");
+ log("\n");
+ }
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ std::string arg = args[argidx];
+ // if (arg == "-mod") {
+ // flag_mod = true;
+ // continue;
+ // }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (Module *module : design->modules())
+ {
+ if (!design->selected(module))
+ continue;
+
+ if (module->get_bool_attribute("\\blackbox"))
+ continue;
+
+ module->set_bool_attribute("\\whitebox", !module->get_bool_attribute("\\whitebox"));
+ }
+ }
+} WbflipPass;
+
struct SetparamPass : public Pass {
SetparamPass() : Pass("setparam", "set/unset parameters on objects") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -142,7 +181,7 @@ struct SetparamPass : public Pass {
log("The -type option can be used to change the cell type of the selected cells.\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
vector<setunset_t> setunset_list;
string new_cell_type;
@@ -188,7 +227,7 @@ struct SetparamPass : public Pass {
struct ChparamPass : public Pass {
ChparamPass() : Pass("chparam", "re-evaluate modules with new parameters") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -203,7 +242,7 @@ struct ChparamPass : public Pass {
log("List the available parameters of the selected modules.\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
std::vector<setunset_t> setunset_list;
dict<RTLIL::IdString, RTLIL::Const> new_parameters;
diff --git a/passes/cmds/setundef.cc b/passes/cmds/setundef.cc
index 9827ac0b1..3eedc86b8 100644
--- a/passes/cmds/setundef.cc
+++ b/passes/cmds/setundef.cc
@@ -23,9 +23,44 @@
#include "kernel/rtlil.h"
#include "kernel/log.h"
+#define MODE_ZERO 0
+#define MODE_ONE 1
+#define MODE_UNDEF 2
+#define MODE_RANDOM 3
+#define MODE_ANYSEQ 4
+#define MODE_ANYCONST 5
+
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
+static RTLIL::Wire * add_wire(RTLIL::Module *module, std::string name, int width, bool flag_input, bool flag_output)
+{
+ RTLIL::Wire *wire = NULL;
+ name = RTLIL::escape_id(name);
+
+ if (module->count_id(name) != 0)
+ {
+ log("Module %s already has such an object %s.\n", module->name.c_str(), name.c_str());
+ name += "$";
+ return add_wire(module, name, width, flag_input, flag_output);
+ }
+ else
+ {
+ wire = module->addWire(name, width);
+ wire->port_input = flag_input;
+ wire->port_output = flag_output;
+
+ if (flag_input || flag_output) {
+ wire->port_id = module->wires_.size();
+ module->fixup_ports();
+ }
+
+ log("Added wire %s to module %s.\n", name.c_str(), module->name.c_str());
+ }
+
+ return wire;
+}
+
struct SetundefWorker
{
int next_bit_mode;
@@ -34,24 +69,32 @@ struct SetundefWorker
RTLIL::State next_bit()
{
- if (next_bit_mode == 0)
+ if (next_bit_mode == MODE_ZERO)
return RTLIL::State::S0;
- if (next_bit_mode == 1)
+ if (next_bit_mode == MODE_ONE)
return RTLIL::State::S1;
- // xorshift32
- next_bit_state ^= next_bit_state << 13;
- next_bit_state ^= next_bit_state >> 17;
- next_bit_state ^= next_bit_state << 5;
- log_assert(next_bit_state != 0);
+ if (next_bit_mode == MODE_UNDEF)
+ return RTLIL::State::Sx;
+
+ if (next_bit_mode == MODE_RANDOM)
+ {
+ // xorshift32
+ next_bit_state ^= next_bit_state << 13;
+ next_bit_state ^= next_bit_state >> 17;
+ next_bit_state ^= next_bit_state << 5;
+ log_assert(next_bit_state != 0);
+
+ return ((next_bit_state >> (next_bit_state & 15)) & 16) ? RTLIL::State::S0 : RTLIL::State::S1;
+ }
- return ((next_bit_state >> (next_bit_state & 15)) & 16) ? RTLIL::State::S0 : RTLIL::State::S1;
+ log_abort();
}
void operator()(RTLIL::SigSpec &sig)
{
- if (next_bit_mode == 2) {
+ if (next_bit_mode == MODE_ANYSEQ || next_bit_mode == MODE_ANYCONST) {
siglist.push_back(&sig);
return;
}
@@ -64,7 +107,7 @@ struct SetundefWorker
struct SetundefPass : public Pass {
SetundefPass() : Pass("setundef", "replace undef values with defined constants") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -75,28 +118,42 @@ struct SetundefPass : public Pass {
log(" -undriven\n");
log(" also set undriven nets to constant values\n");
log("\n");
+ log(" -expose\n");
+ log(" also expose undriven nets as inputs (use with -undriven)\n");
+ log("\n");
log(" -zero\n");
log(" replace with bits cleared (0)\n");
log("\n");
log(" -one\n");
log(" replace with bits set (1)\n");
log("\n");
+ log(" -undef\n");
+ log(" replace with undef (x) bits, may be used with -undriven\n");
+ log("\n");
log(" -anyseq\n");
log(" replace with $anyseq drivers (for formal)\n");
log("\n");
+ log(" -anyconst\n");
+ 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");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
bool got_value = false;
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");
@@ -108,28 +165,51 @@ struct SetundefPass : public Pass {
undriven_mode = true;
continue;
}
+ if (args[argidx] == "-expose") {
+ expose_mode = true;
+ continue;
+ }
if (args[argidx] == "-zero") {
got_value = true;
- worker.next_bit_mode = 0;
+ worker.next_bit_mode = MODE_ZERO;
+ worker.next_bit_state = 0;
continue;
}
if (args[argidx] == "-one") {
got_value = true;
- worker.next_bit_mode = 1;
+ worker.next_bit_mode = MODE_ONE;
+ worker.next_bit_state = 0;
continue;
}
if (args[argidx] == "-anyseq") {
got_value = true;
- worker.next_bit_mode = 2;
+ worker.next_bit_mode = MODE_ANYSEQ;
+ worker.next_bit_state = 0;
+ continue;
+ }
+ if (args[argidx] == "-anyconst") {
+ got_value = true;
+ worker.next_bit_mode = MODE_ANYCONST;
+ worker.next_bit_state = 0;
+ continue;
+ }
+ if (args[argidx] == "-undef") {
+ got_value = true;
+ worker.next_bit_mode = MODE_UNDEF;
+ worker.next_bit_state = 0;
continue;
}
if (args[argidx] == "-init") {
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 = 3;
+ worker.next_bit_mode = MODE_RANDOM;
worker.next_bit_state = atoi(args[++argidx].c_str()) + 1;
for (int i = 0; i < 10; i++)
worker.next_bit();
@@ -139,38 +219,137 @@ struct SetundefPass : public Pass {
}
extra_args(args, argidx, design);
+ if (!got_value && expose_mode) {
+ log("Using default as -undef with -expose.\n");
+ got_value = true;
+ worker.next_bit_mode = MODE_UNDEF;
+ worker.next_bit_state = 0;
+ }
+
+ if (expose_mode && !undriven_mode)
+ log_cmd_error("Option -expose must be used with option -undriven.\n");
if (!got_value)
- log_cmd_error("One of the options -zero, -one, -anyseq, or -random <seed> must be specified.\n");
+ log_cmd_error("One of the options -zero, -one, -anyseq, -anyconst, or -random <seed> must be specified.\n");
+
+ if (init_mode && (worker.next_bit_mode == MODE_ANYSEQ || worker.next_bit_mode == MODE_ANYCONST))
+ log_cmd_error("The options -init and -anyseq / -anyconst are exclusive.\n");
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())
log_error("The 'setundef' command can't operate in -undriven mode on modules with processes. Run 'proc' first.\n");
- SigMap sigmap(module);
- SigPool undriven_signals;
-
- for (auto &it : module->wires_)
- undriven_signals.add(sigmap(it.second));
-
- for (auto &it : module->wires_)
- if (it.second->port_input)
- undriven_signals.del(sigmap(it.second));
-
- CellTypes ct(design);
- for (auto &it : module->cells_)
- for (auto &conn : it.second->connections())
- if (!ct.cell_known(it.second->type) || ct.cell_output(it.second->type, conn.first))
- undriven_signals.del(sigmap(conn.second));
-
- RTLIL::SigSpec sig = undriven_signals.export_all();
- for (auto &c : sig.chunks()) {
- RTLIL::SigSpec bits;
- for (int i = 0; i < c.width; i++)
- bits.append(worker.next_bit());
- module->connect(RTLIL::SigSig(c, bits));
+ if (expose_mode)
+ {
+ SigMap sigmap(module);
+ dict<SigBit, bool> wire_drivers;
+ pool<SigBit> used_wires;
+ SigPool undriven_signals;
+
+ for (auto cell : module->cells())
+ for (auto &conn : cell->connections()) {
+ SigSpec sig = sigmap(conn.second);
+ if (cell->input(conn.first))
+ for (auto bit : sig)
+ if (bit.wire)
+ used_wires.insert(bit);
+ if (cell->output(conn.first))
+ for (int i = 0; i < GetSize(sig); i++)
+ if (sig[i].wire)
+ wire_drivers[sig[i]] = true;
+ }
+
+ for (auto wire : module->wires()) {
+ if (wire->port_input) {
+ SigSpec sig = sigmap(wire);
+ for (int i = 0; i < GetSize(sig); i++)
+ wire_drivers[sig[i]] = true;
+ }
+ if (wire->port_output) {
+ SigSpec sig = sigmap(wire);
+ for (auto bit : sig)
+ if (bit.wire)
+ used_wires.insert(bit);
+ }
+ }
+
+ pool<RTLIL::Wire*> undriven_wires;
+ for (auto bit : used_wires)
+ if (!wire_drivers.count(bit))
+ undriven_wires.insert(bit.wire);
+
+ for (auto &it : undriven_wires)
+ undriven_signals.add(sigmap(it));
+
+ for (auto &it : undriven_wires)
+ if (it->port_input)
+ undriven_signals.del(sigmap(it));
+
+ CellTypes ct(design);
+ for (auto &it : module->cells_)
+ for (auto &conn : it.second->connections())
+ if (!ct.cell_known(it.second->type) || ct.cell_output(it.second->type, conn.first))
+ undriven_signals.del(sigmap(conn.second));
+
+ RTLIL::SigSpec sig = undriven_signals.export_all();
+ for (auto &c : sig.chunks()) {
+ RTLIL::Wire * wire;
+ if (c.wire->width == c.width) {
+ wire = c.wire;
+ wire->port_input = true;
+ } else {
+ string name = c.wire->name.str() + "$[" + std::to_string(c.width + c.offset) + ":" + std::to_string(c.offset) + "]";
+ wire = add_wire(module, name, c.width, true, false);
+ module->connect(RTLIL::SigSig(c, wire));
+ }
+ log("Exposing undriven wire %s as input.\n", wire->name.c_str());
+ }
+ module->fixup_ports();
+ }
+ else
+ {
+ SigMap sigmap(module);
+ SigPool undriven_signals;
+
+ for (auto &it : module->wires_)
+ undriven_signals.add(sigmap(it.second));
+
+ for (auto &it : module->wires_)
+ if (it.second->port_input)
+ undriven_signals.del(sigmap(it.second));
+
+ CellTypes ct(design);
+ for (auto &it : module->cells_)
+ for (auto &conn : it.second->connections())
+ if (!ct.cell_known(it.second->type) || ct.cell_output(it.second->type, conn.first))
+ undriven_signals.del(sigmap(conn.second));
+
+ RTLIL::SigSpec sig = undriven_signals.export_all();
+ for (auto &c : sig.chunks()) {
+ RTLIL::SigSpec bits;
+ if (worker.next_bit_mode == MODE_ANYSEQ)
+ bits = module->Anyseq(NEW_ID, c.width);
+ else if (worker.next_bit_mode == MODE_ANYCONST)
+ bits = module->Anyconst(NEW_ID, c.width);
+ else
+ for (int i = 0; i < c.width; i++)
+ bits.append(worker.next_bit());
+ module->connect(RTLIL::SigSig(c, bits));
+ }
}
}
@@ -214,49 +393,117 @@ struct SetundefPass : public Pass {
ffbits.insert(bit);
}
- for (auto wire : module->wires())
+ auto process_initwires = [&]()
{
- if (!wire->attributes.count("\\init"))
- continue;
+ dict<Wire*, int> wire_weights;
+
+ for (auto wire : initwires)
+ {
+ int weight = 0;
- for (auto bit : sigmap(wire))
- ffbits.erase(bit);
+ for (auto bit : sigmap(wire))
+ weight += ffbits.count(bit) ? +1 : -1;
- initwires.insert(wire);
- }
+ wire_weights[wire] = weight;
+ }
+
+ initwires.sort([&](Wire *a, Wire *b) { return wire_weights.at(a) > wire_weights.at(b); });
+
+ for (auto wire : initwires)
+ {
+ Const &initval = wire->attributes["\\init"];
+ initval.bits.resize(GetSize(wire), State::Sx);
+
+ for (int i = 0; i < GetSize(wire); i++) {
+ SigBit bit = sigmap(SigBit(wire, i));
+ if (initval[i] == State::Sx && ffbits.count(bit)) {
+ initval[i] = worker.next_bit();
+ ffbits.erase(bit);
+ }
+ }
+
+ if (initval.is_fully_undef())
+ wire->attributes.erase("\\init");
+ }
+
+ initwires.clear();
+ };
for (int wire_types = 0; wire_types < 2; wire_types++)
- for (auto wire : module->wires())
+ {
+ // prioritize wires that already have an init attribute
+ if (!ffbits.empty())
{
- if (wire->name[0] == (wire_types ? '\\' : '$'))
- next_wire:
- continue;
+ for (auto wire : module->wires())
+ {
+ if (wire->name[0] == (wire_types ? '\\' : '$'))
+ continue;
- for (auto bit : sigmap(wire))
- if (!ffbits.count(bit))
- goto next_wire;
+ if (!wire->attributes.count("\\init"))
+ continue;
- for (auto bit : sigmap(wire))
- ffbits.erase(bit);
+ Const &initval = wire->attributes["\\init"];
+ initval.bits.resize(GetSize(wire), State::Sx);
+
+ if (initval.is_fully_undef()) {
+ wire->attributes.erase("\\init");
+ continue;
+ }
+
+ for (int i = 0; i < GetSize(wire); i++)
+ if (initval[i] != State::Sx)
+ ffbits.erase(sigmap(SigBit(wire, i)));
- initwires.insert(wire);
+ initwires.insert(wire);
+ }
+
+ process_initwires();
}
- for (auto wire : initwires)
- {
- Const &initval = wire->attributes["\\init"];
+ // next consider wires that completely contain bits to be initialized
+ if (!ffbits.empty())
+ {
+ for (auto wire : module->wires())
+ {
+ if (wire->name[0] == (wire_types ? '\\' : '$'))
+ continue;
+
+ for (auto bit : sigmap(wire))
+ if (!ffbits.count(bit))
+ goto next_wire;
- for (int i = 0; i < GetSize(wire); i++)
- if (GetSize(initval) <= i)
- initval.bits.push_back(worker.next_bit());
- else if (initval.bits[i] == State::Sx)
- initval.bits[i] = worker.next_bit();
+ initwires.insert(wire);
+
+ next_wire:
+ continue;
+ }
+
+ process_initwires();
+ }
+
+ // finally use whatever wire we can find.
+ if (!ffbits.empty())
+ {
+ for (auto wire : module->wires())
+ {
+ if (wire->name[0] == (wire_types ? '\\' : '$'))
+ continue;
+
+ for (auto bit : sigmap(wire))
+ if (ffbits.count(bit))
+ initwires.insert(wire);
+ }
+
+ process_initwires();
+ }
}
+
+ log_assert(ffbits.empty());
}
module->rewrite_sigspecs(worker);
- if (worker.next_bit_mode == 2)
+ if (worker.next_bit_mode == MODE_ANYSEQ || worker.next_bit_mode == MODE_ANYCONST)
{
vector<SigSpec*> siglist;
siglist.swap(worker.siglist);
@@ -273,7 +520,10 @@ struct SetundefPass : public Pass {
width++;
if (width > 0) {
- sig.replace(cursor, module->Anyseq(NEW_ID, width));
+ if (worker.next_bit_mode == MODE_ANYSEQ)
+ sig.replace(cursor, module->Anyseq(NEW_ID, width));
+ else
+ sig.replace(cursor, module->Anyconst(NEW_ID, width));
cursor += width;
} else {
cursor++;
diff --git a/passes/cmds/show.cc b/passes/cmds/show.cc
index 3a3939a81..eeef24bde 100644
--- a/passes/cmds/show.cc
+++ b/passes/cmds/show.cc
@@ -26,10 +26,18 @@
# include <dirent.h>
#endif
+#ifdef __APPLE__
+# include <unistd.h>
+#endif
+
#ifdef YOSYS_ENABLE_READLINE
# include <readline/readline.h>
#endif
+#ifdef YOSYS_ENABLE_EDITLINE
+# include <editline/readline.h>
+#endif
+
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
@@ -233,15 +241,34 @@ struct ShowWorker
int idx = single_idx_count++;
for (int rep, i = int(sig.chunks().size())-1; i >= 0; i -= rep) {
const RTLIL::SigChunk &c = sig.chunks().at(i);
- net = gen_signode_simple(c, false);
- log_assert(!net.empty());
+ if (!driver && c.wire == nullptr) {
+ RTLIL::State s1 = c.data.front();
+ for (auto s2 : c.data)
+ if (s1 != s2)
+ goto not_const_stream;
+ net.clear();
+ } else {
+ not_const_stream:
+ net = gen_signode_simple(c, false);
+ log_assert(!net.empty());
+ }
for (rep = 1; i-rep >= 0 && c == sig.chunks().at(i-rep); rep++) {}
std::string repinfo = rep > 1 ? stringf("%dx ", rep) : "";
if (driver) {
+ log_assert(!net.empty());
label_string += stringf("<s%d> %d:%d - %s%d:%d |", i, pos, pos-c.width+1, repinfo.c_str(), c.offset+c.width-1, c.offset);
net_conn_map[net].in.insert(stringf("x%d:s%d", idx, i));
net_conn_map[net].bits = rep*c.width;
net_conn_map[net].color = nextColor(c, net_conn_map[net].color);
+ } else
+ if (net.empty()) {
+ log_assert(rep == 1);
+ label_string += stringf("%c -&gt; %d:%d |",
+ c.data.front() == State::S0 ? '0' :
+ c.data.front() == State::S1 ? '1' :
+ c.data.front() == State::Sx ? 'X' :
+ c.data.front() == State::Sz ? 'Z' : '?',
+ pos, pos-rep*c.width+1);
} else {
label_string += stringf("<s%d> %s%d:%d - %d:%d |", i, repinfo.c_str(), c.offset+c.width-1, c.offset, pos, pos-rep*c.width+1);
net_conn_map[net].out.insert(stringf("x%d:s%d", idx, i));
@@ -504,11 +531,11 @@ struct ShowWorker
{
currentColor = xorshift32(currentColor);
if (wires_on_demand.count(it.first) > 0) {
- if (it.second.in.size() == 1 && it.second.out.size() > 1 && it.second.in.begin()->substr(0, 1) == "p")
+ if (it.second.in.size() == 1 && it.second.out.size() > 1 && it.second.in.begin()->compare(0, 1, "p") == 0)
it.second.out.erase(*it.second.in.begin());
if (it.second.in.size() == 1 && it.second.out.size() == 1) {
std::string from = *it.second.in.begin(), to = *it.second.out.begin();
- if (from != to || from.substr(0, 1) != "p")
+ if (from != to || from.compare(0, 1, "p") != 0)
fprintf(f, "%s:e -> %s:w [%s, %s];\n", from.c_str(), to.c_str(), nextColor(it.second.color).c_str(), widthLabel(it.second.bits).c_str());
continue;
}
@@ -551,7 +578,7 @@ struct ShowWorker
if (!design->selected_module(module->name))
continue;
if (design->selected_whole_module(module->name)) {
- if (module->get_bool_attribute("\\blackbox")) {
+ if (module->get_blackbox_attribute()) {
// log("Skipping blackbox module %s.\n", id2cstr(module->name));
continue;
} else
@@ -569,7 +596,7 @@ struct ShowWorker
struct ShowPass : public Pass {
ShowPass() : Pass("show", "generate schematics using graphviz") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -580,6 +607,7 @@ struct ShowPass : public Pass {
log("\n");
log(" -viewer <viewer>\n");
log(" Run the specified command with the graphics file as parameter.\n");
+ log(" On Windows, this pauses yosys until the viewer exits.\n");
log("\n");
log(" -format <format>\n");
log(" Generate a graphics file in the specified format. Use 'dot' to just\n");
@@ -618,7 +646,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");
@@ -641,7 +669,7 @@ struct ShowPass : public Pass {
log(" do not add the module name as graph title to the dot file\n");
log("\n");
log("When no <format> is specified, 'dot' is used. When no <format> and <viewer> is\n");
- log("specified, 'xdot' is used to display the schematic.\n");
+ log("specified, 'xdot' is used to display the schematic (POSIX systems only).\n");
log("\n");
log("The generated output files are '~/.yosys_show.dot' and '~/.yosys_show.<format>',\n");
log("unless another prefix is specified using -prefix <prefix>.\n");
@@ -651,7 +679,7 @@ struct ShowPass : public Pass {
log("the 'show' command is executed.\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
log_header(design, "Generating Graphviz representation of design.\n");
log_push();
@@ -677,6 +705,7 @@ struct ShowPass : public Pass {
bool flag_enum = false;
bool flag_abbreviate = true;
bool flag_notitle = false;
+ bool custom_prefix = false;
RTLIL::IdString colorattr;
size_t argidx;
@@ -693,6 +722,7 @@ struct ShowPass : public Pass {
}
if (arg == "-prefix" && argidx+1 < args.size()) {
prefix = args[++argidx];
+ custom_prefix = true;
continue;
}
if (arg == "-color" && argidx+2 < args.size()) {
@@ -764,7 +794,7 @@ struct ShowPass : public Pass {
if (format != "ps" && format != "dot") {
int modcount = 0;
for (auto &mod_it : design->modules_) {
- if (mod_it.second->get_bool_attribute("\\blackbox"))
+ if (mod_it.second->get_blackbox_attribute())
continue;
if (mod_it.second->cells_.empty() && mod_it.second->connections().empty())
continue;
@@ -778,10 +808,11 @@ struct ShowPass : public Pass {
for (auto filename : libfiles) {
std::ifstream f;
f.open(filename.c_str());
+ yosys_input_files.insert(filename);
if (f.fail())
log_error("Can't open lib file `%s'.\n", filename.c_str());
RTLIL::Design *lib = new RTLIL::Design;
- Frontend::frontend_call(lib, &f, filename, (filename.size() > 3 && filename.substr(filename.size()-3) == ".il") ? "ilang" : "verilog");
+ Frontend::frontend_call(lib, &f, filename, (filename.size() > 3 && filename.compare(filename.size()-3, std::string::npos, ".il") == 0 ? "ilang" : "verilog"));
libs.push_back(lib);
}
@@ -793,6 +824,8 @@ struct ShowPass : public Pass {
log("Writing dot description to `%s'.\n", dot_file.c_str());
FILE *f = fopen(dot_file.c_str(), "w");
+ if (custom_prefix)
+ yosys_output_files.insert(dot_file);
if (f == NULL) {
for (auto lib : libs)
delete lib;
@@ -808,20 +841,40 @@ struct ShowPass : public Pass {
log_cmd_error("Nothing there to show.\n");
if (format != "dot" && !format.empty()) {
- std::string cmd = stringf("dot -T%s '%s' > '%s.new' && mv '%s.new' '%s'", format.c_str(), dot_file.c_str(), out_file.c_str(), out_file.c_str(), out_file.c_str());
+ #ifdef _WIN32
+ // system()/cmd.exe does not understand single quotes on Windows.
+ #define DOT_CMD "dot -T%s \"%s\" > \"%s.new\" && move \"%s.new\" \"%s\""
+ #else
+ #define DOT_CMD "dot -T%s '%s' > '%s.new' && mv '%s.new' '%s'"
+ #endif
+ std::string cmd = stringf(DOT_CMD, format.c_str(), dot_file.c_str(), out_file.c_str(), out_file.c_str(), out_file.c_str());
+ #undef DOT_CMD
log("Exec: %s\n", cmd.c_str());
if (run_command(cmd) != 0)
log_cmd_error("Shell command failed!\n");
}
if (!viewer_exe.empty()) {
- std::string cmd = stringf("%s '%s' &", viewer_exe.c_str(), out_file.c_str());
+ #ifdef _WIN32
+ // system()/cmd.exe does not understand single quotes nor
+ // background tasks on Windows. So we have to pause yosys
+ // until the viewer exits.
+ #define VIEW_CMD "%s \"%s\""
+ #else
+ #define VIEW_CMD "%s '%s' &"
+ #endif
+ std::string cmd = stringf(VIEW_CMD, viewer_exe.c_str(), out_file.c_str());
+ #undef VIEW_CMD
log("Exec: %s\n", cmd.c_str());
if (run_command(cmd) != 0)
log_cmd_error("Shell command failed!\n");
} else
if (format.empty()) {
- std::string cmd = stringf("{ test -f '%s.pid' && fuser -s '%s.pid'; } || ( echo $$ >&3; exec xdot '%s'; ) 3> '%s.pid' &", dot_file.c_str(), dot_file.c_str(), dot_file.c_str(), dot_file.c_str());
+ #ifdef __APPLE__
+ std::string cmd = stringf("ps -fu %d | grep -q '[ ]%s' || xdot '%s' &", getuid(), dot_file.c_str(), dot_file.c_str());
+ #else
+ std::string cmd = stringf("{ test -f '%s.pid' && fuser -s '%s.pid' 2> /dev/null; } || ( echo $$ >&3; exec xdot '%s'; ) 3> '%s.pid' &", dot_file.c_str(), dot_file.c_str(), dot_file.c_str(), dot_file.c_str());
+ #endif
log("Exec: %s\n", cmd.c_str());
if (run_command(cmd) != 0)
log_cmd_error("Shell command failed!\n");
diff --git a/passes/cmds/splice.cc b/passes/cmds/splice.cc
index 7418ec4d2..bafafca4e 100644
--- a/passes/cmds/splice.cc
+++ b/passes/cmds/splice.cc
@@ -247,7 +247,7 @@ struct SpliceWorker
struct SplicePass : public Pass {
SplicePass() : Pass("splice", "create explicit splicing cells") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -288,7 +288,7 @@ struct SplicePass : public Pass {
log("by selected wires are rewired.\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
bool sel_by_cell = false;
bool sel_by_wire = false;
diff --git a/passes/cmds/splitnets.cc b/passes/cmds/splitnets.cc
index 5e37fc597..f5a1f17b3 100644
--- a/passes/cmds/splitnets.cc
+++ b/passes/cmds/splitnets.cc
@@ -87,7 +87,7 @@ struct SplitnetsWorker
struct SplitnetsPass : public Pass {
SplitnetsPass() : Pass("splitnets", "split up multi-bit nets") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -109,7 +109,7 @@ struct SplitnetsPass : public Pass {
log(" and split nets so that no driver drives only part of a net.\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
bool flag_ports = false;
bool flag_driver = false;
diff --git a/passes/cmds/stat.cc b/passes/cmds/stat.cc
index a887fbb6a..c8e4f3981 100644
--- a/passes/cmds/stat.cc
+++ b/passes/cmds/stat.cc
@@ -17,11 +17,10 @@
*
*/
-#include "kernel/register.h"
+#include "kernel/yosys.h"
#include "kernel/celltypes.h"
#include "passes/techmap/libparse.h"
-
-#include "kernel/log.h"
+#include "kernel/cost.h"
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
@@ -37,7 +36,9 @@ struct statdata_t
STAT_INT_MEMBERS
#undef X
double area;
+ string tech;
+ std::map<RTLIL::IdString, int> techinfo;
std::map<RTLIL::IdString, int, RTLIL::sort_by_id_str> num_cells_by_type;
std::set<RTLIL::IdString> unknown_cell_area;
@@ -70,8 +71,10 @@ struct statdata_t
#undef X
}
- statdata_t(RTLIL::Design *design, RTLIL::Module *mod, bool width_mode, const dict<IdString, double> &cell_area)
+ statdata_t(RTLIL::Design *design, RTLIL::Module *mod, bool width_mode, const dict<IdString, double> &cell_area, string techname)
{
+ tech = techname;
+
#define X(_name) _name = 0;
STAT_NUMERIC_MEMBERS
#undef X
@@ -142,7 +145,7 @@ struct statdata_t
}
}
- void log_data()
+ void log_data(RTLIL::IdString mod_name, bool top_mod)
{
log(" Number of wires: %6d\n", num_wires);
log(" Number of wire bits: %6d\n", num_wire_bits);
@@ -153,7 +156,8 @@ struct statdata_t
log(" Number of processes: %6d\n", num_processes);
log(" Number of cells: %6d\n", num_cells);
for (auto &it : num_cells_by_type)
- log(" %-26s %6d\n", RTLIL::id2cstr(it.first), it.second);
+ if (it.second)
+ log(" %-26s %6d\n", RTLIL::id2cstr(it.first), it.second);
if (!unknown_cell_area.empty()) {
log("\n");
@@ -163,7 +167,82 @@ struct statdata_t
if (area != 0) {
log("\n");
- log(" Chip area for this module: %f\n", area);
+ log(" Chip area for %smodule '%s': %f\n", (top_mod) ? "top " : "", mod_name.c_str(), area);
+ }
+
+ if (tech == "xilinx")
+ {
+ int lut6_cnt = num_cells_by_type["\\LUT6"];
+ int lut5_cnt = num_cells_by_type["\\LUT5"];
+ int lut4_cnt = num_cells_by_type["\\LUT4"];
+ int lut3_cnt = num_cells_by_type["\\LUT3"];
+ int lut2_cnt = num_cells_by_type["\\LUT2"];
+ int lut1_cnt = num_cells_by_type["\\LUT1"];
+ int lc_cnt = 0;
+
+ lc_cnt += lut6_cnt;
+
+ lc_cnt += lut5_cnt;
+ if (lut1_cnt) {
+ int cnt = std::min(lut5_cnt, lut1_cnt);
+ lut5_cnt -= cnt;
+ lut1_cnt -= cnt;
+ }
+
+ lc_cnt += lut4_cnt;
+ if (lut1_cnt) {
+ int cnt = std::min(lut4_cnt, lut1_cnt);
+ lut4_cnt -= cnt;
+ lut1_cnt -= cnt;
+ }
+ if (lut2_cnt) {
+ int cnt = std::min(lut4_cnt, lut2_cnt);
+ lut4_cnt -= cnt;
+ lut2_cnt -= cnt;
+ }
+
+ lc_cnt += lut3_cnt;
+ if (lut1_cnt) {
+ int cnt = std::min(lut3_cnt, lut1_cnt);
+ lut3_cnt -= cnt;
+ lut1_cnt -= cnt;
+ }
+ if (lut2_cnt) {
+ int cnt = std::min(lut3_cnt, lut2_cnt);
+ lut3_cnt -= cnt;
+ lut2_cnt -= cnt;
+ }
+ if (lut3_cnt) {
+ int cnt = (lut3_cnt + 1) / 2;
+ lut3_cnt -= cnt;
+ }
+
+ lc_cnt += (lut2_cnt + lut1_cnt + 1) / 2;
+
+ log("\n");
+ log(" Estimated number of LCs: %10d\n", lc_cnt);
+ }
+
+ if (tech == "cmos")
+ {
+ int tran_cnt = 0;
+ bool tran_cnt_exact = true;
+ auto &gate_costs = CellCosts::cmos_gate_cost();
+
+ for (auto it : num_cells_by_type) {
+ auto ctype = it.first;
+ auto cnum = it.second;
+
+ if (gate_costs.count(ctype))
+ tran_cnt += cnum * gate_costs.at(ctype);
+ else if (ctype.in("$_DFF_P_", "$_DFF_N_"))
+ tran_cnt += cnum * 16;
+ else
+ tran_cnt_exact = false;
+ }
+
+ log("\n");
+ log(" Estimated number of transistors: %10d%s\n", tran_cnt, tran_cnt_exact ? "" : "+");
}
}
};
@@ -190,6 +269,7 @@ void read_liberty_cellarea(dict<IdString, double> &cell_area, string liberty_fil
{
std::ifstream f;
f.open(liberty_file.c_str());
+ yosys_input_files.insert(liberty_file);
if (f.fail())
log_cmd_error("Can't open liberty file `%s': %s\n", liberty_file.c_str(), strerror(errno));
LibertyParser libparser(f);
@@ -208,7 +288,7 @@ void read_liberty_cellarea(dict<IdString, double> &cell_area, string liberty_fil
struct StatPass : public Pass {
StatPass() : Pass("stat", "print some statistics") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -225,12 +305,16 @@ struct StatPass : public Pass {
log(" -liberty <liberty_file>\n");
log(" use cell area information from the provided liberty file\n");
log("\n");
+ log(" -tech <technology>\n");
+ log(" print area estemate for the specified technology. Currently supported\n");
+ log(" values for <technology>: xilinx, cmos\n");
+ log("\n");
log(" -width\n");
log(" annotate internal cell types with their word width.\n");
log(" e.g. $add_8 for an 8 bit wide $add cell.\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
log_header(design, "Printing statistics.\n");
@@ -238,6 +322,7 @@ struct StatPass : public Pass {
RTLIL::Module *top_mod = NULL;
std::map<RTLIL::IdString, statdata_t> mod_stat;
dict<IdString, double> cell_area;
+ string techname;
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++)
@@ -252,6 +337,10 @@ struct StatPass : public Pass {
read_liberty_cellarea(cell_area, liberty_file);
continue;
}
+ if (args[argidx] == "-tech" && argidx+1 < args.size()) {
+ techname = args[++argidx];
+ continue;
+ }
if (args[argidx] == "-top" && argidx+1 < args.size()) {
if (design->modules_.count(RTLIL::escape_id(args[argidx+1])) == 0)
log_cmd_error("Can't find module %s.\n", args[argidx+1].c_str());
@@ -262,19 +351,22 @@ struct StatPass : public Pass {
}
extra_args(args, argidx, design);
+ if (techname != "" && techname != "xilinx" && techname != "cmos")
+ log_cmd_error("Unsupported technology: '%s'\n", techname.c_str());
+
for (auto mod : design->selected_modules())
{
if (!top_mod && design->full_selection())
if (mod->get_bool_attribute("\\top"))
top_mod = mod;
- statdata_t data(design, mod, width_mode, cell_area);
+ statdata_t data(design, mod, width_mode, cell_area, techname);
mod_stat[mod->name] = data;
log("\n");
log("=== %s%s ===\n", RTLIL::id2cstr(mod->name), design->selected_whole_module(mod->name) ? "" : " (partially selected)");
log("\n");
- data.log_data();
+ data.log_data(mod->name, false);
}
if (top_mod != NULL && GetSize(mod_stat) > 1)
@@ -287,7 +379,7 @@ struct StatPass : public Pass {
statdata_t data = hierarchy_worker(mod_stat, top_mod->name, 0);
log("\n");
- data.log_data();
+ data.log_data(top_mod->name, true);
}
log("\n");
diff --git a/passes/cmds/tee.cc b/passes/cmds/tee.cc
index 3db2dbf0e..1a44bdaec 100644
--- a/passes/cmds/tee.cc
+++ b/passes/cmds/tee.cc
@@ -27,7 +27,7 @@ PRIVATE_NAMESPACE_BEGIN
struct TeePass : public Pass {
TeePass() : Pass("tee", "redirect command output to file") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -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,13 +46,15 @@ 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");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
std::vector<FILE*> backup_log_files, files_to_close;
+ std::vector<std::ostream*> backup_log_streams;
int backup_log_verbose_level = log_verbose_level;
+ backup_log_streams = log_streams;
backup_log_files = log_files;
size_t argidx;
@@ -60,11 +62,13 @@ struct TeePass : public Pass {
{
if (args[argidx] == "-q" && files_to_close.empty()) {
log_files.clear();
+ log_streams.clear();
continue;
}
if ((args[argidx] == "-o" || args[argidx] == "-a") && argidx+1 < args.size()) {
const char *open_mode = args[argidx] == "-o" ? "w" : "a+";
FILE *f = fopen(args[++argidx].c_str(), open_mode);
+ yosys_input_files.insert(args[argidx]);
if (f == NULL) {
for (auto cf : files_to_close)
fclose(cf);
@@ -88,6 +92,7 @@ struct TeePass : public Pass {
for (auto cf : files_to_close)
fclose(cf);
log_files = backup_log_files;
+ log_streams = backup_log_streams;
throw;
}
@@ -96,6 +101,7 @@ struct TeePass : public Pass {
log_verbose_level = backup_log_verbose_level;
log_files = backup_log_files;
+ log_streams = backup_log_streams;
}
} TeePass;
diff --git a/passes/cmds/torder.cc b/passes/cmds/torder.cc
index 56223610d..3c0eac8de 100644
--- a/passes/cmds/torder.cc
+++ b/passes/cmds/torder.cc
@@ -27,7 +27,7 @@ PRIVATE_NAMESPACE_BEGIN
struct TorderPass : public Pass {
TorderPass() : Pass("torder", "print cells in topological order") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -43,7 +43,7 @@ struct TorderPass : public Pass {
log(" are not used in topological sorting. this option deactivates that.\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
bool noautostop = false;
dict<IdString, pool<IdString>> stop_db;
diff --git a/passes/cmds/trace.cc b/passes/cmds/trace.cc
index 1a5f873ff..cf3e46ace 100644
--- a/passes/cmds/trace.cc
+++ b/passes/cmds/trace.cc
@@ -25,34 +25,34 @@ PRIVATE_NAMESPACE_BEGIN
struct TraceMonitor : public RTLIL::Monitor
{
- virtual void notify_module_add(RTLIL::Module *module) YS_OVERRIDE
+ void notify_module_add(RTLIL::Module *module) YS_OVERRIDE
{
log("#TRACE# Module add: %s\n", log_id(module));
}
- virtual void notify_module_del(RTLIL::Module *module) YS_OVERRIDE
+ void notify_module_del(RTLIL::Module *module) YS_OVERRIDE
{
log("#TRACE# Module delete: %s\n", log_id(module));
}
- virtual void notify_connect(RTLIL::Cell *cell, const RTLIL::IdString &port, const RTLIL::SigSpec &old_sig, RTLIL::SigSpec &sig) YS_OVERRIDE
+ void notify_connect(RTLIL::Cell *cell, const RTLIL::IdString &port, const RTLIL::SigSpec &old_sig, RTLIL::SigSpec &sig) YS_OVERRIDE
{
log("#TRACE# Cell connect: %s.%s.%s = %s (was: %s)\n", log_id(cell->module), log_id(cell), log_id(port), log_signal(sig), log_signal(old_sig));
}
- virtual void notify_connect(RTLIL::Module *module, const RTLIL::SigSig &sigsig) YS_OVERRIDE
+ void notify_connect(RTLIL::Module *module, const RTLIL::SigSig &sigsig) YS_OVERRIDE
{
log("#TRACE# Connection in module %s: %s = %s\n", log_id(module), log_signal(sigsig.first), log_signal(sigsig.second));
}
- virtual void notify_connect(RTLIL::Module *module, const std::vector<RTLIL::SigSig> &sigsig_vec) YS_OVERRIDE
+ void notify_connect(RTLIL::Module *module, const std::vector<RTLIL::SigSig> &sigsig_vec) YS_OVERRIDE
{
log("#TRACE# New connections in module %s:\n", log_id(module));
for (auto &sigsig : sigsig_vec)
log("## %s = %s\n", log_signal(sigsig.first), log_signal(sigsig.second));
}
- virtual void notify_blackout(RTLIL::Module *module) YS_OVERRIDE
+ void notify_blackout(RTLIL::Module *module) YS_OVERRIDE
{
log("#TRACE# Blackout in module %s:\n", log_id(module));
}
@@ -60,7 +60,7 @@ struct TraceMonitor : public RTLIL::Monitor
struct TracePass : public Pass {
TracePass() : Pass("trace", "redirect command output to file") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -70,7 +70,7 @@ struct TracePass : public Pass {
log("the design in real time.\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++)
@@ -94,5 +94,38 @@ struct TracePass : public Pass {
}
} TracePass;
-PRIVATE_NAMESPACE_END
+struct DebugPass : public Pass {
+ DebugPass() : Pass("debug", "run command with debug log messages enabled") { }
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" debug cmd\n");
+ log("\n");
+ log("Execute the specified command with debug log messages enabled\n");
+ log("\n");
+ }
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ // .. parse options ..
+ break;
+ }
+
+ log_force_debug++;
+
+ try {
+ std::vector<std::string> new_args(args.begin() + argidx, args.end());
+ Pass::call(design, new_args);
+ } catch (...) {
+ log_force_debug--;
+ throw;
+ }
+ log_force_debug--;
+ }
+} DebugPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/cmds/write_file.cc b/passes/cmds/write_file.cc
index b78265933..64a762d7c 100644
--- a/passes/cmds/write_file.cc
+++ b/passes/cmds/write_file.cc
@@ -25,7 +25,7 @@ PRIVATE_NAMESPACE_BEGIN
struct WriteFileFrontend : public Frontend {
WriteFileFrontend() : Frontend("=write_file", "write a text to a file") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -44,7 +44,7 @@ struct WriteFileFrontend : public Frontend {
log(" EOT\n");
log("\n");
}
- virtual void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design*)
+ void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design*) YS_OVERRIDE
{
bool append_mode = false;
std::string output_filename;
@@ -62,11 +62,12 @@ struct WriteFileFrontend : public Frontend {
if (argidx < args.size() && args[argidx].rfind("-", 0) != 0)
output_filename = args[argidx++];
else
- log_cmd_error("Missing putput filename.\n");
+ log_cmd_error("Missing output filename.\n");
extra_args(f, filename, args, argidx);
FILE *of = fopen(output_filename.c_str(), append_mode ? "a" : "w");
+ yosys_output_files.insert(output_filename);
char buffer[64 * 1024];
int bytes;
diff --git a/passes/equiv/Makefile.inc b/passes/equiv/Makefile.inc
index dd7b3be02..27ea54b22 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_add.cc b/passes/equiv/equiv_add.cc
index 0494a724f..71599f46e 100644
--- a/passes/equiv/equiv_add.cc
+++ b/passes/equiv/equiv_add.cc
@@ -25,7 +25,7 @@ PRIVATE_NAMESPACE_BEGIN
struct EquivAddPass : public Pass {
EquivAddPass() : Pass("equiv_add", "add a $equiv cell") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -39,7 +39,7 @@ struct EquivAddPass : public Pass {
log("This command adds $equiv cells for the ports of the specified cells.\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, Design *design)
+ void execute(std::vector<std::string> args, Design *design) YS_OVERRIDE
{
bool try_mode = false;
diff --git a/passes/equiv/equiv_induct.cc b/passes/equiv/equiv_induct.cc
index c958c3de4..bcc68d6d2 100644
--- a/passes/equiv/equiv_induct.cc
+++ b/passes/equiv/equiv_induct.cc
@@ -162,7 +162,7 @@ struct EquivInductWorker
struct EquivInductPass : public Pass {
EquivInductPass() : Pass("equiv_induct", "proving $equiv cells using temporal induction") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -192,7 +192,7 @@ struct EquivInductPass : public Pass {
log("after reset.\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, Design *design)
+ void execute(std::vector<std::string> args, Design *design) YS_OVERRIDE
{
int success_counter = 0;
bool model_undef = false;
diff --git a/passes/equiv/equiv_make.cc b/passes/equiv/equiv_make.cc
index 40ca42621..4855ce29e 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)
@@ -260,11 +270,11 @@ struct EquivMakeWorker
for (int i = 0; i < wire->width; i++) {
if (undriven_bits.count(assign_map(SigBit(gold_wire, i)))) {
- log(" Skipping signal bit %d: undriven on gold side.\n", i);
+ log(" Skipping signal bit %s [%d]: undriven on gold side.\n", id2cstr(gold_wire->name), i);
continue;
}
if (undriven_bits.count(assign_map(SigBit(gate_wire, i)))) {
- log(" Skipping signal bit %d: undriven on gate side.\n", i);
+ log(" Skipping signal bit %s [%d]: undriven on gate side.\n", id2cstr(gate_wire->name), i);
continue;
}
equiv_mod->addEquiv(NEW_ID, SigSpec(gold_wire, i), SigSpec(gate_wire, i), SigSpec(wire, i));
@@ -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();
@@ -390,7 +466,7 @@ struct EquivMakeWorker
struct EquivMakePass : public Pass {
EquivMakePass() : Pass("equiv_make", "prepare a circuit for equivalence checking") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -415,7 +491,7 @@ struct EquivMakePass : public Pass {
log("checking problem. Use 'miter -equiv' if you want to create a miter circuit.\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
EquivMakeWorker worker;
worker.ct.setup(design);
@@ -456,10 +532,10 @@ struct EquivMakePass : public Pass {
log_cmd_error("Equiv module %s already exists.\n", args[argidx+2].c_str());
if (worker.gold_mod->has_memories() || worker.gold_mod->has_processes())
- log_cmd_error("Gold module contains memories or procresses. Run 'memory' or 'proc' respectively.\n");
+ log_cmd_error("Gold module contains memories or processes. Run 'memory' or 'proc' respectively.\n");
if (worker.gate_mod->has_memories() || worker.gate_mod->has_processes())
- log_cmd_error("Gate module contains memories or procresses. Run 'memory' or 'proc' respectively.\n");
+ log_cmd_error("Gate module contains memories or processes. Run 'memory' or 'proc' respectively.\n");
worker.read_blacklists();
worker.read_encfiles();
diff --git a/passes/equiv/equiv_mark.cc b/passes/equiv/equiv_mark.cc
index 22c501763..135eaf145 100644
--- a/passes/equiv/equiv_mark.cc
+++ b/passes/equiv/equiv_mark.cc
@@ -204,7 +204,7 @@ struct EquivMarkWorker
struct EquivMarkPass : public Pass {
EquivMarkPass() : Pass("equiv_mark", "mark equivalence checking regions") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -216,7 +216,7 @@ struct EquivMarkPass : public Pass {
log("wires and cells.\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, Design *design)
+ void execute(std::vector<std::string> args, Design *design) YS_OVERRIDE
{
log_header(design, "Executing EQUIV_MARK pass.\n");
diff --git a/passes/equiv/equiv_miter.cc b/passes/equiv/equiv_miter.cc
index eb2e5a171..e06f9515b 100644
--- a/passes/equiv/equiv_miter.cc
+++ b/passes/equiv/equiv_miter.cc
@@ -261,7 +261,7 @@ struct EquivMiterWorker
struct EquivMiterPass : public Pass {
EquivMiterPass() : Pass("equiv_miter", "extract miter from equiv circuit") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -282,7 +282,7 @@ struct EquivMiterPass : public Pass {
log(" Create compare logic that handles undefs correctly\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
EquivMiterWorker worker;
worker.ct.setup(design);
diff --git a/passes/equiv/equiv_opt.cc b/passes/equiv/equiv_opt.cc
new file mode 100644
index 000000000..7c6c2e685
--- /dev/null
+++ b/passes/equiv/equiv_opt.cc
@@ -0,0 +1,208 @@
+/*
+ * 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 uses temporal induction to check circuit equivalence before and\n");
+ log("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(" -blacklist <file>\n");
+ log(" Do not match cells or signals that match the names in the file\n");
+ log(" (passed to equiv_make).\n");
+ log("\n");
+ log(" -assert\n");
+ log(" produce an error if the circuits are not equivalent.\n");
+ log("\n");
+ log(" -multiclock\n");
+ log(" run clk2fflogic before equivalence checking.\n");
+ log("\n");
+ log(" -async2sync\n");
+ log(" run async2sync before equivalence checking.\n");
+ log("\n");
+ log(" -undef\n");
+ log(" enable modelling of undef states during equiv_induct.\n");
+ log("\n");
+ log("The following commands are executed by this verification command:\n");
+ help_script();
+ log("\n");
+ }
+
+ std::string command, techmap_opts, make_opts;
+ bool assert, undef, multiclock, async2sync;
+
+ void clear_flags() YS_OVERRIDE
+ {
+ command = "";
+ techmap_opts = "";
+ make_opts = "";
+ assert = false;
+ undef = false;
+ multiclock = false;
+ async2sync = 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] == "-blacklist" && argidx + 1 < args.size()) {
+ make_opts += " -blacklist " + args[++argidx];
+ continue;
+ }
+ if (args[argidx] == "-assert") {
+ assert = true;
+ continue;
+ }
+ if (args[argidx] == "-undef") {
+ undef = true;
+ continue;
+ }
+ if (args[argidx] == "-multiclock") {
+ multiclock = true;
+ continue;
+ }
+ if (args[argidx] == "-async2sync") {
+ async2sync = true;
+ continue;
+ }
+ break;
+ }
+
+ for (; argidx < args.size(); argidx++) {
+ if (command.empty()) {
+ if (args[argidx].compare(0, 1, "-") == 0)
+ 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");
+
+ if (async2sync && multiclock)
+ log_cmd_error("The '-async2sync' and '-multiclock' options are mutually exclusive!\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 -wb -D EQUIV -autoproc" + opts);
+ }
+
+ if (check_label("prove")) {
+ if (multiclock || help_mode)
+ run("clk2fflogic", "(only with -multiclock)");
+ if (async2sync || help_mode)
+ run("async2sync", " (only with -async2sync)");
+ string opts;
+ if (help_mode)
+ opts = " -blacklist <filename> ...";
+ else
+ opts = make_opts;
+ run("equiv_make" + opts + " gold gate equiv");
+ if (help_mode)
+ run("equiv_induct [-undef] equiv");
+ else if (undef)
+ run("equiv_induct -undef equiv");
+ else
+ 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/equiv/equiv_purge.cc b/passes/equiv/equiv_purge.cc
index f8d3cd0af..18b3e7d36 100644
--- a/passes/equiv/equiv_purge.cc
+++ b/passes/equiv/equiv_purge.cc
@@ -176,7 +176,7 @@ struct EquivPurgeWorker
struct EquivPurgePass : public Pass {
EquivPurgePass() : Pass("equiv_purge", "purge equivalence checking module") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -187,7 +187,7 @@ struct EquivPurgePass : public Pass {
log("ports as needed.\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, Design *design)
+ void execute(std::vector<std::string> args, Design *design) YS_OVERRIDE
{
log_header(design, "Executing EQUIV_PURGE pass.\n");
diff --git a/passes/equiv/equiv_remove.cc b/passes/equiv/equiv_remove.cc
index 770497a51..c5c28c7d9 100644
--- a/passes/equiv/equiv_remove.cc
+++ b/passes/equiv/equiv_remove.cc
@@ -24,7 +24,7 @@ PRIVATE_NAMESPACE_BEGIN
struct EquivRemovePass : public Pass {
EquivRemovePass() : Pass("equiv_remove", "remove $equiv cells") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -40,7 +40,7 @@ struct EquivRemovePass : public Pass {
log(" keep gate circuit\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, Design *design)
+ void execute(std::vector<std::string> args, Design *design) YS_OVERRIDE
{
bool mode_gold = false;
bool mode_gate = false;
diff --git a/passes/equiv/equiv_simple.cc b/passes/equiv/equiv_simple.cc
index e69e17ac6..c2fab26f2 100644
--- a/passes/equiv/equiv_simple.cc
+++ b/passes/equiv/equiv_simple.cc
@@ -273,7 +273,7 @@ struct EquivSimpleWorker
struct EquivSimplePass : public Pass {
EquivSimplePass() : Pass("equiv_simple", "try proving simple $equiv instances") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -298,7 +298,7 @@ struct EquivSimplePass : public Pass {
log(" the max. number of time steps to be considered (default = 1)\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, Design *design)
+ void execute(std::vector<std::string> args, Design *design) YS_OVERRIDE
{
bool verbose = false, short_cones = false, model_undef = false, nogroup = false;
int success_counter = 0;
diff --git a/passes/equiv/equiv_status.cc b/passes/equiv/equiv_status.cc
index 7b9230b35..b4a93ccf5 100644
--- a/passes/equiv/equiv_status.cc
+++ b/passes/equiv/equiv_status.cc
@@ -24,7 +24,7 @@ PRIVATE_NAMESPACE_BEGIN
struct EquivStatusPass : public Pass {
EquivStatusPass() : Pass("equiv_status", "print status of equivalent checking module") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -36,7 +36,7 @@ struct EquivStatusPass : public Pass {
log(" produce an error if any unproven $equiv cell is found\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, Design *design)
+ void execute(std::vector<std::string> args, Design *design) YS_OVERRIDE
{
bool assert_mode = false;
int unproven_count = 0;
diff --git a/passes/equiv/equiv_struct.cc b/passes/equiv/equiv_struct.cc
index c4ced6a71..6672948b9 100644
--- a/passes/equiv/equiv_struct.cc
+++ b/passes/equiv/equiv_struct.cc
@@ -215,9 +215,9 @@ struct EquivStructWorker
if (c != nullptr) {
string n = cell_name.str();
cells_type = c->type;
- if (GetSize(n) > 5 && n.substr(GetSize(n)-5) == "_gold")
+ if (GetSize(n) > 5 && n.compare(GetSize(n)-5, std::string::npos, "_gold") == 0)
gold_cells.push_back(c);
- else if (GetSize(n) > 5 && n.substr(GetSize(n)-5) == "_gate")
+ else if (GetSize(n) > 5 && n.compare(GetSize(n)-5, std::string::npos, "_gate") == 0)
gate_cells.push_back(c);
else
other_cells.push_back(c);
@@ -283,7 +283,7 @@ struct EquivStructWorker
struct EquivStructPass : public Pass {
EquivStructPass() : Pass("equiv_struct", "structural equivalence checking") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -314,7 +314,7 @@ struct EquivStructPass : public Pass {
log(" maximum number of iterations to run before aborting\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, Design *design)
+ void execute(std::vector<std::string> args, Design *design) YS_OVERRIDE
{
pool<IdString> fwonly_cells({ "$equiv" });
bool mode_icells = false;
diff --git a/passes/fsm/fsm.cc b/passes/fsm/fsm.cc
index 997558b85..c5cb338ab 100644
--- a/passes/fsm/fsm.cc
+++ b/passes/fsm/fsm.cc
@@ -27,7 +27,7 @@ PRIVATE_NAMESPACE_BEGIN
struct FsmPass : public Pass {
FsmPass() : Pass("fsm", "extract and optimize finite state machines") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -68,7 +68,7 @@ struct FsmPass : public Pass {
log(" passed through to fsm_recode pass\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
bool flag_nomap = false;
bool flag_norecode = false;
diff --git a/passes/fsm/fsm_detect.cc b/passes/fsm/fsm_detect.cc
index 9fb5446e7..a1c8067b4 100644
--- a/passes/fsm/fsm_detect.cc
+++ b/passes/fsm/fsm_detect.cc
@@ -34,13 +34,20 @@ static SigSet<sig2driver_entry_t> sig2driver, sig2user;
static std::set<RTLIL::Cell*> muxtree_cells;
static SigPool sig_at_port;
-static bool check_state_mux_tree(RTLIL::SigSpec old_sig, RTLIL::SigSpec sig, pool<Cell*> &recursion_monitor)
+static bool check_state_mux_tree(RTLIL::SigSpec old_sig, RTLIL::SigSpec sig, pool<Cell*> &recursion_monitor, dict<RTLIL::SigSpec, bool> &mux_tree_cache)
{
+ if (mux_tree_cache.find(sig) != mux_tree_cache.end())
+ return mux_tree_cache.at(sig);
+
if (sig.is_fully_const() || old_sig == sig) {
+ret_true:
+ mux_tree_cache[sig] = true;
return true;
}
if (sig_at_port.check_any(assign_map(sig))) {
+ret_false:
+ mux_tree_cache[sig] = false;
return false;
}
@@ -49,13 +56,13 @@ static bool check_state_mux_tree(RTLIL::SigSpec old_sig, RTLIL::SigSpec sig, poo
for (auto &cellport : cellport_list)
{
if ((cellport.first->type != "$mux" && cellport.first->type != "$pmux") || cellport.second != "\\Y") {
- return false;
+ goto ret_false;
}
if (recursion_monitor.count(cellport.first)) {
log_warning("logic loop in mux tree at signal %s in module %s.\n",
log_signal(sig), RTLIL::id2cstr(module->name));
- return false;
+ goto ret_false;
}
recursion_monitor.insert(cellport.first);
@@ -63,22 +70,22 @@ static bool check_state_mux_tree(RTLIL::SigSpec old_sig, RTLIL::SigSpec sig, poo
RTLIL::SigSpec sig_a = assign_map(cellport.first->getPort("\\A"));
RTLIL::SigSpec sig_b = assign_map(cellport.first->getPort("\\B"));
- if (!check_state_mux_tree(old_sig, sig_a, recursion_monitor)) {
+ if (!check_state_mux_tree(old_sig, sig_a, recursion_monitor, mux_tree_cache)) {
recursion_monitor.erase(cellport.first);
- return false;
+ goto ret_false;
}
for (int i = 0; i < sig_b.size(); i += sig_a.size())
- if (!check_state_mux_tree(old_sig, sig_b.extract(i, sig_a.size()), recursion_monitor)) {
+ if (!check_state_mux_tree(old_sig, sig_b.extract(i, sig_a.size()), recursion_monitor, mux_tree_cache)) {
recursion_monitor.erase(cellport.first);
- return false;
+ goto ret_false;
}
recursion_monitor.erase(cellport.first);
muxtree_cells.insert(cellport.first);
}
- return true;
+ goto ret_true;
}
static bool check_state_users(RTLIL::SigSpec sig)
@@ -143,11 +150,12 @@ static void detect_fsm(RTLIL::Wire *wire)
pool<Cell*> recursion_monitor;
RTLIL::SigSpec sig_q = assign_map(cellport.first->getPort("\\Q"));
RTLIL::SigSpec sig_d = assign_map(cellport.first->getPort("\\D"));
+ dict<RTLIL::SigSpec, bool> mux_tree_cache;
if (sig_q != assign_map(wire))
continue;
- looks_like_state_reg = check_state_mux_tree(sig_q, sig_d, recursion_monitor);
+ looks_like_state_reg = check_state_mux_tree(sig_q, sig_d, recursion_monitor, mux_tree_cache);
looks_like_good_state_reg = check_state_users(sig_q);
if (!looks_like_state_reg)
@@ -158,22 +166,25 @@ static void detect_fsm(RTLIL::Wire *wire)
std::set<sig2driver_entry_t> cellport_list;
sig2user.find(sig_q, cellport_list);
+ auto sig_q_bits = sig_q.to_sigbit_pool();
+
for (auto &cellport : cellport_list)
{
RTLIL::Cell *cell = cellport.first;
bool set_output = false, clr_output = false;
- if (cell->type == "$ne")
+ if (cell->type.in("$ne", "$reduce_or", "$reduce_bool"))
set_output = true;
- if (cell->type == "$eq")
+ if (cell->type.in("$eq", "$logic_not", "$reduce_and"))
clr_output = true;
- if (!set_output && !clr_output) {
- clr_output = true;
+ if (set_output || clr_output) {
for (auto &port_it : cell->connections())
- if (port_it.first != "\\A" || port_it.first != "\\Y")
- clr_output = false;
+ if (cell->input(port_it.first))
+ for (auto bit : assign_map(port_it.second))
+ if (bit.wire != nullptr && !sig_q_bits.count(bit))
+ goto next_cellport;
}
if (set_output || clr_output) {
@@ -184,6 +195,7 @@ static void detect_fsm(RTLIL::Wire *wire)
ce.set(sig, val);
}
}
+ next_cellport:;
}
SigSpec sig_y = sig_d, sig_undef;
@@ -196,13 +208,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 +248,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");
@@ -245,7 +257,7 @@ static void detect_fsm(RTLIL::Wire *wire)
struct FsmDetectPass : public Pass {
FsmDetectPass() : Pass("fsm_detect", "finding FSMs in design") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -261,7 +273,7 @@ struct FsmDetectPass : public Pass {
log("'fsm_encoding' attribute to \"none\".\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
log_header(design, "Executing FSM_DETECT pass (finding FSMs in design).\n");
extra_args(args, 1, design);
diff --git a/passes/fsm/fsm_expand.cc b/passes/fsm/fsm_expand.cc
index 2c344a1c1..1610ec751 100644
--- a/passes/fsm/fsm_expand.cc
+++ b/passes/fsm/fsm_expand.cc
@@ -50,7 +50,7 @@ struct FsmExpand
if (full_mode || cell->type == "$_MUX_")
return true;
- if (cell->type == "$mux" || cell->type == "$pmux")
+ if (cell->type.in("$mux", "$pmux"))
if (cell->getPort("\\A").size() < 2)
return true;
@@ -265,7 +265,7 @@ struct FsmExpand
struct FsmExpandPass : public Pass {
FsmExpandPass() : Pass("fsm_expand", "expand FSM cells by merging logic into it") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -279,7 +279,7 @@ struct FsmExpandPass : public Pass {
log("word-wide cells. Call with -full to consider all cells for merging.\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
bool full_mode = false;
diff --git a/passes/fsm/fsm_export.cc b/passes/fsm/fsm_export.cc
index 1cbfcfae8..8eb1872f0 100644
--- a/passes/fsm/fsm_export.cc
+++ b/passes/fsm/fsm_export.cc
@@ -120,7 +120,7 @@ void write_kiss2(struct RTLIL::Module *module, struct RTLIL::Cell *cell, std::st
*/
struct FsmExportPass : public Pass {
FsmExportPass() : Pass("fsm_export", "exporting FSMs to KISS2 files") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -143,7 +143,7 @@ struct FsmExportPass : public Pass {
log(" use binary state encoding as state names instead of s0, s1, ...\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
dict<RTLIL::IdString, RTLIL::Const>::iterator attr_it;
std::string arg;
diff --git a/passes/fsm/fsm_extract.cc b/passes/fsm/fsm_extract.cc
index 8a4ee3f26..a85c3bec0 100644
--- a/passes/fsm/fsm_extract.cc
+++ b/passes/fsm/fsm_extract.cc
@@ -168,7 +168,7 @@ undef_bit_in_next_state:
ctrl_in_bit_indices[ctrl_in[i]] = i;
for (auto &it : ctrl_in_bit_indices)
- if (tr.ctrl_in.bits.at(it.second) == RTLIL::S1 && exclusive_ctrls.count(it.first) != 0)
+ if (tr.ctrl_in.bits.at(it.second) == State::S1 && exclusive_ctrls.count(it.first) != 0)
for (auto &dc_bit : exclusive_ctrls.at(it.first))
if (ctrl_in_bit_indices.count(dc_bit))
tr.ctrl_in.bits.at(ctrl_in_bit_indices.at(dc_bit)) = RTLIL::State::Sa;
@@ -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));
}
@@ -216,13 +216,13 @@ undef_bit_in_next_state:
ce.push();
dont_care.append(undef);
ce.set(undef, constval.as_const());
- if (exclusive_ctrls.count(undef) && constval == RTLIL::S1)
+ if (exclusive_ctrls.count(undef) && constval == State::S1)
for (auto &bit : exclusive_ctrls.at(undef)) {
RTLIL::SigSpec bitval = bit;
- if (ce.eval(bitval) && bitval != RTLIL::S0)
+ if (ce.eval(bitval) && bitval != State::S0)
goto found_contradiction_1;
else
- ce.set(bit, RTLIL::S0);
+ ce.set(bit, State::S0);
}
find_transitions(ce, ce_nostop, fsm_data, states, state_in, ctrl_in, ctrl_out, dff_in, dont_care);
found_contradiction_1:
@@ -231,21 +231,21 @@ undef_bit_in_next_state:
else
{
ce.push(), ce_nostop.push();
- ce.set(undef, RTLIL::S0);
- ce_nostop.set(undef, RTLIL::S0);
+ ce.set(undef, State::S0);
+ ce_nostop.set(undef, State::S0);
find_transitions(ce, ce_nostop, fsm_data, states, state_in, ctrl_in, ctrl_out, dff_in, dont_care);
ce.pop(), ce_nostop.pop();
ce.push(), ce_nostop.push();
- ce.set(undef, RTLIL::S1);
- ce_nostop.set(undef, RTLIL::S1);
+ ce.set(undef, State::S1);
+ ce_nostop.set(undef, State::S1);
if (exclusive_ctrls.count(undef))
for (auto &bit : exclusive_ctrls.at(undef)) {
RTLIL::SigSpec bitval = bit;
- if ((ce.eval(bitval) || ce_nostop.eval(bitval)) && bitval != RTLIL::S0)
+ if ((ce.eval(bitval) || ce_nostop.eval(bitval)) && bitval != State::S0)
goto found_contradiction_2;
else
- ce.set(bit, RTLIL::S0), ce_nostop.set(bit, RTLIL::S0);
+ ce.set(bit, State::S0), ce_nostop.set(bit, RTLIL::S0);
}
find_transitions(ce, ce_nostop, fsm_data, states, state_in, ctrl_in, ctrl_out, dff_in, dont_care);
found_contradiction_2:
@@ -263,8 +263,8 @@ static void extract_fsm(RTLIL::Wire *wire)
RTLIL::SigSpec dff_in(RTLIL::State::Sm, wire->width);
RTLIL::Const reset_state(RTLIL::State::Sx, wire->width);
- RTLIL::SigSpec clk = RTLIL::S0;
- RTLIL::SigSpec arst = RTLIL::S0;
+ RTLIL::SigSpec clk = State::S0;
+ RTLIL::SigSpec arst = State::S0;
bool clk_polarity = true;
bool arst_polarity = true;
@@ -371,8 +371,8 @@ static void extract_fsm(RTLIL::Wire *wire)
RTLIL::Cell *fsm_cell = module->addCell(stringf("$fsm$%s$%d", wire->name.c_str(), autoidx++), "$fsm");
fsm_cell->setPort("\\CLK", clk);
fsm_cell->setPort("\\ARST", arst);
- fsm_cell->parameters["\\CLK_POLARITY"] = clk_polarity ? RTLIL::S1 : RTLIL::S0;
- fsm_cell->parameters["\\ARST_POLARITY"] = arst_polarity ? RTLIL::S1 : RTLIL::S0;
+ fsm_cell->parameters["\\CLK_POLARITY"] = clk_polarity ? State::S1 : State::S0;
+ fsm_cell->parameters["\\ARST_POLARITY"] = arst_polarity ? State::S1 : State::S0;
fsm_cell->setPort("\\CTRL_IN", ctrl_in);
fsm_cell->setPort("\\CTRL_OUT", ctrl_out);
fsm_cell->parameters["\\NAME"] = RTLIL::Const(wire->name.str());
@@ -401,7 +401,7 @@ static void extract_fsm(RTLIL::Wire *wire)
struct FsmExtractPass : public Pass {
FsmExtractPass() : Pass("fsm_extract", "extracting FSMs in design") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -417,7 +417,7 @@ struct FsmExtractPass : public Pass {
log("'opt_clean' pass to eliminate this signal.\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
log_header(design, "Executing FSM_EXTRACT pass (extracting FSM from design).\n");
extra_args(args, 1, design);
diff --git a/passes/fsm/fsm_info.cc b/passes/fsm/fsm_info.cc
index 2cc1a7d53..0548259ee 100644
--- a/passes/fsm/fsm_info.cc
+++ b/passes/fsm/fsm_info.cc
@@ -30,7 +30,7 @@ PRIVATE_NAMESPACE_BEGIN
struct FsmInfoPass : public Pass {
FsmInfoPass() : Pass("fsm_info", "print information on finite state machines") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -41,7 +41,7 @@ struct FsmInfoPass : public Pass {
log("pass so that this information is included in the synthesis log file.\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
log_header(design, "Executing FSM_INFO pass (dumping all available information on FSM cells).\n");
extra_args(args, 1, design);
diff --git a/passes/fsm/fsm_map.cc b/passes/fsm/fsm_map.cc
index 3edaf84d2..80913fda8 100644
--- a/passes/fsm/fsm_map.cc
+++ b/passes/fsm/fsm_map.cc
@@ -133,7 +133,7 @@ static void implement_pattern_cache(RTLIL::Module *module, std::map<RTLIL::Const
cases_vector.append(and_sig);
break;
case 0:
- cases_vector.append(RTLIL::SigSpec(1, 1));
+ cases_vector.append(State::S1);
break;
default:
log_abort();
@@ -150,7 +150,7 @@ static void implement_pattern_cache(RTLIL::Module *module, std::map<RTLIL::Const
} else if (cases_vector.size() == 1) {
module->connect(RTLIL::SigSig(output, cases_vector));
} else {
- module->connect(RTLIL::SigSig(output, RTLIL::SigSpec(0, 1)));
+ module->connect(RTLIL::SigSig(output, State::S0));
}
}
@@ -322,7 +322,7 @@ static void map_fsm(RTLIL::Cell *fsm_cell, RTLIL::Module *module)
struct FsmMapPass : public Pass {
FsmMapPass() : Pass("fsm_map", "mapping FSMs to basic logic") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -331,7 +331,7 @@ struct FsmMapPass : public Pass {
log("This pass translates FSM cells to flip-flops and logic.\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
log_header(design, "Executing FSM_MAP pass (mapping FSMs to basic logic).\n");
extra_args(args, 1, design);
diff --git a/passes/fsm/fsm_opt.cc b/passes/fsm/fsm_opt.cc
index 5b1da44fc..048daee55 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);
}
}
@@ -323,7 +324,7 @@ PRIVATE_NAMESPACE_BEGIN
struct FsmOptPass : public Pass {
FsmOptPass() : Pass("fsm_opt", "optimize finite state machines") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -334,7 +335,7 @@ struct FsmOptPass : public Pass {
log("combination with the 'opt_clean' pass (see also 'help fsm').\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
log_header(design, "Executing FSM_OPT pass (simple optimizations of FSMs).\n");
extra_args(args, 1, design);
diff --git a/passes/fsm/fsm_recode.cc b/passes/fsm/fsm_recode.cc
index e1bde728f..fa1ff48cc 100644
--- a/passes/fsm/fsm_recode.cc
+++ b/passes/fsm/fsm_recode.cc
@@ -126,7 +126,7 @@ static void fsm_recode(RTLIL::Cell *cell, RTLIL::Module *module, FILE *fm_set_fs
struct FsmRecodePass : public Pass {
FsmRecodePass() : Pass("fsm_recode", "recoding finite state machines") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -151,7 +151,7 @@ struct FsmRecodePass : public Pass {
log(" .map <old_bitpattern> <new_bitpattern>\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
FILE *fm_set_fsm_file = NULL;
FILE *encfile = NULL;
diff --git a/passes/hierarchy/hierarchy.cc b/passes/hierarchy/hierarchy.cc
index c460fbbfc..d8a628448 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
@@ -18,6 +19,7 @@
*/
#include "kernel/yosys.h"
+#include "frontends/verific/verific.h"
#include <stdlib.h>
#include <stdio.h>
#include <set>
@@ -46,7 +48,7 @@ void generate(RTLIL::Design *design, const std::vector<std::string> &celltypes,
RTLIL::Cell *cell = i2.second;
if (design->has(cell->type))
continue;
- if (cell->type.substr(0, 1) == "$" && cell->type.substr(0, 3) != "$__")
+ if (cell->type.begins_with("$__"))
continue;
for (auto &pattern : celltypes)
if (patmatch(pattern.c_str(), RTLIL::unescape_id(cell->type).c_str()))
@@ -138,25 +140,73 @@ void generate(RTLIL::Design *design, const std::vector<std::string> &celltypes,
}
}
-bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check, std::vector<std::string> &libdirs)
+// 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.compare(0, strlen("$array:"), "$array:") == 0) {
+ 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;
+ }
+ }
- 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 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());
+ 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.begins_with("$array:")) {
+ 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.substr(pos_idx + 1, pos_num).c_str());
+ int num = atoi(cell->type.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);
+ cell->type = cell->type.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)
{
@@ -173,20 +223,24 @@ bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check
for (auto &dir : libdirs)
{
- filename = dir + "/" + RTLIL::unescape_id(cell->type) + ".v";
- if (check_file_exists(filename)) {
- Frontend::frontend_call(design, NULL, filename, "verilog");
- goto loaded_module;
- }
+ static const vector<pair<string, string>> extensions_list =
+ {
+ {".v", "verilog"},
+ {".sv", "verilog -sv"},
+ {".il", "ilang"}
+ };
- filename = dir + "/" + RTLIL::unescape_id(cell->type) + ".il";
- if (check_file_exists(filename)) {
- Frontend::frontend_call(design, NULL, filename, "ilang");
- goto loaded_module;
+ for (auto &ext : extensions_list)
+ {
+ filename = dir + "/" + RTLIL::unescape_id(cell->type) + ext.first;
+ if (check_file_exists(filename)) {
+ Frontend::frontend_call(design, NULL, filename, ext.second);
+ goto loaded_module;
+ }
}
}
- if (flag_check && cell->type[0] != '$')
+ if ((flag_check || flag_simcheck) && cell->type[0] != '$')
log_error("Module `%s' referenced in module `%s' in cell `%s' is not part of the design.\n",
cell->type.c_str(), module->name.c_str(), cell->name.c_str());
continue;
@@ -195,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
- if (flag_check)
+ } 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))
@@ -208,24 +336,79 @@ 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_blackbox_attribute()) {
+ if (flag_simcheck)
+ log_error("Module `%s' referenced in module `%s' in cell `%s' is a blackbox/whitebox module.\n",
+ cell->type.c_str(), module->name.c_str(), cell->name.c_str());
+ 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;
+ }
+
+ // 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 (design->modules_.at(cell->type)->get_bool_attribute("\\blackbox"))
+ // 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;
+ }
- RTLIL::Module *mod = design->modules_[cell->type];
- cell->type = mod->derive(design, cell->parameters);
+ 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;
@@ -239,7 +422,7 @@ bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check
for (auto &conn : cell->connections_) {
int conn_size = conn.second.size();
RTLIL::IdString portname = conn.first;
- if (portname.substr(0, 1) == "$") {
+ if (portname.begins_with("$")) {
int port_id = atoi(portname.substr(1).c_str());
for (auto &wire_it : mod->wires_)
if (wire_it.second->port_id == port_id) {
@@ -250,7 +433,7 @@ bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check
if (mod->wires_.count(portname) == 0)
log_error("Array cell `%s.%s' connects to unknown port `%s'.\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(cell->name), RTLIL::id2cstr(conn.first));
int port_size = mod->wires_.at(portname)->width;
- if (conn_size == port_size)
+ if (conn_size == port_size || conn_size == 0)
continue;
if (conn_size != port_size*num)
log_error("Array cell `%s.%s' has invalid port vs. signal size for port `%s'.\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(cell->name), RTLIL::id2cstr(conn.first));
@@ -268,18 +451,14 @@ void hierarchy_worker(RTLIL::Design *design, std::set<RTLIL::Module*, IdString::
if (indent == 0)
log("Top module: %s\n", mod->name.c_str());
- else if (!mod->get_bool_attribute("\\blackbox"))
+ else if (!mod->get_blackbox_attribute())
log("Used module: %*s%s\n", indent, "", mod->name.c_str());
used.insert(mod);
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);
- }
+ if (celltype.compare(0, strlen("$array:"), "$array:") == 0)
+ celltype = basic_cell_type(celltype);
if (design->module(celltype))
hierarchy_worker(design, used, design->module(celltype), indent+4);
}
@@ -294,10 +473,24 @@ 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) {
- if (!purge_lib && mod->get_bool_attribute("\\blackbox"))
+ if (!purge_lib && mod->get_blackbox_attribute())
continue;
log("Removing unused module `%s'.\n", mod->name.c_str());
design->modules_.erase(mod->name);
@@ -324,17 +517,40 @@ 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.compare(0, strlen("$array:"), "$array:") == 0)
+ 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") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -344,12 +560,17 @@ struct HierarchyPass : public Pass {
log("In parametric designs, a module might exists in several variations with\n");
log("different parameter values. This pass looks at all modules in the current\n");
log("design an re-runs the language frontends for the parametric modules as\n");
- log("needed.\n");
+ log("needed. It also resolves assignments to wired logic data types (wand/wor),\n");
+ log("resolves positional module parameters, unroll array instances, and more.\n");
log("\n");
log(" -check\n");
log(" also check the design hierarchy. this generates an error when\n");
log(" an unknown module is used as cell type.\n");
log("\n");
+ log(" -simcheck\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");
log(" by default the hierarchy command will not remove library (blackbox)\n");
log(" modules. use this option to also remove unused blackbox modules.\n");
@@ -361,20 +582,23 @@ struct HierarchyPass : public Pass {
log("\n");
log(" -keep_positionals\n");
log(" per default this pass also converts positional arguments in cells\n");
- log(" to arguments using port names. this option disables this behavior.\n");
+ log(" to arguments using port names. This option disables this behavior.\n");
log("\n");
log(" -keep_portwidths\n");
log(" per default this pass adjusts the port width on cells that are\n");
- log(" module instances when the width does not match the module port. this\n");
+ log(" module instances when the width does not match the module port. This\n");
log(" option disables this behavior.\n");
log("\n");
+ log(" -nodefaults\n");
+ log(" do not resolve input port default values\n");
+ log("\n");
log(" -nokeep_asserts\n");
log(" per default this pass sets the \"keep\" attribute on all modules\n");
- log(" that directly or indirectly contain one or more $assert cells. this\n");
- log(" option disables this behavior.\n");
+ log(" that directly or indirectly contain one or more formal properties.\n");
+ log(" This option disables this behavior.\n");
log("\n");
log(" -top <module>\n");
- log(" use the specified top module to built a design hierarchy. modules\n");
+ log(" use the specified top module to build the design hierarchy. Modules\n");
log(" outside this tree (unused modules) are removed.\n");
log("\n");
log(" when the -top option is used, the 'top' attribute will be set on the\n");
@@ -384,6 +608,12 @@ struct HierarchyPass : public Pass {
log(" -auto-top\n");
log(" automatically determine the top of the design hierarchy and mark it.\n");
log("\n");
+ log(" -chparam name value \n");
+ log(" elaborate the top module using this parameter value. Modules on which\n");
+ log(" this parameter does not exist may cause a warning message to be output.\n");
+ log(" This option can be specified multiple times to override multiple\n");
+ log(" parameters. String values must be passed in double quotes (\").\n");
+ log("\n");
log("In -generate mode this pass generates blackbox modules for the given cell\n");
log("types (wildcards supported). For this the design is searched for cells that\n");
log("match the given types and then the given port declarations are used to\n");
@@ -401,27 +631,31 @@ struct HierarchyPass : public Pass {
log("in the current design.\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
log_header(design, "Executing HIERARCHY pass (managing design hierarchy).\n");
bool flag_check = false;
+ bool flag_simcheck = false;
bool purge_lib = false;
RTLIL::Module *top_mod = NULL;
+ std::string load_top_mod;
std::vector<std::string> libdirs;
bool auto_top_mode = false;
bool generate_mode = false;
bool keep_positionals = false;
bool keep_portwidths = false;
+ bool nodefaults = false;
bool nokeep_asserts = false;
std::vector<std::string> generate_cells;
std::vector<generate_port_decl_t> generate_ports;
+ std::map<std::string, std::string> parameters;
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++)
{
- if (args[argidx] == "-generate" && !flag_check && !top_mod) {
+ if (args[argidx] == "-generate" && !flag_check && !flag_simcheck && !top_mod) {
generate_mode = true;
log("Entering generate mode.\n");
while (++argidx < args.size()) {
@@ -464,6 +698,10 @@ struct HierarchyPass : public Pass {
flag_check = true;
continue;
}
+ if (args[argidx] == "-simcheck") {
+ flag_simcheck = true;
+ continue;
+ }
if (args[argidx] == "-purge_lib") {
purge_lib = true;
continue;
@@ -476,6 +714,10 @@ struct HierarchyPass : public Pass {
keep_portwidths = true;
continue;
}
+ if (args[argidx] == "-nodefaults") {
+ nodefaults = true;
+ continue;
+ }
if (args[argidx] == "-nokeep_asserts") {
nokeep_asserts = true;
continue;
@@ -487,24 +729,73 @@ struct HierarchyPass : public Pass {
if (args[argidx] == "-top") {
if (++argidx >= args.size())
log_cmd_error("Option -top requires an additional argument!\n");
- top_mod = design->modules_.count(RTLIL::escape_id(args[argidx])) ? design->modules_.at(RTLIL::escape_id(args[argidx])) : NULL;
- if (top_mod == NULL && design->modules_.count("$abstract" + RTLIL::escape_id(args[argidx]))) {
- dict<RTLIL::IdString, RTLIL::Const> empty_parameters;
- design->modules_.at("$abstract" + RTLIL::escape_id(args[argidx]))->derive(design, empty_parameters);
- top_mod = design->modules_.count(RTLIL::escape_id(args[argidx])) ? design->modules_.at(RTLIL::escape_id(args[argidx])) : NULL;
- }
- if (top_mod == NULL)
- log_cmd_error("Module `%s' not found!\n", args[argidx].c_str());
+ load_top_mod = args[argidx];
continue;
}
if (args[argidx] == "-auto-top") {
auto_top_mode = true;
continue;
}
+ if (args[argidx] == "-chparam" && argidx+2 < args.size()) {
+ const std::string &key = args[++argidx];
+ const std::string &value = args[++argidx];
+ auto r = parameters.emplace(key, value);
+ if (!r.second) {
+ log_warning("-chparam %s already specified: overwriting.\n", key.c_str());
+ r.first->second = value;
+ }
+ continue;
+ }
break;
}
extra_args(args, argidx, design, false);
+ if (!load_top_mod.empty())
+ {
+ IdString top_name = RTLIL::escape_id(load_top_mod);
+ IdString abstract_id = "$abstract" + RTLIL::escape_id(load_top_mod);
+ top_mod = design->module(top_name);
+
+ dict<RTLIL::IdString, RTLIL::Const> top_parameters;
+ for (auto &para : parameters) {
+ SigSpec sig_value;
+ if (!RTLIL::SigSpec::parse(sig_value, NULL, para.second))
+ log_cmd_error("Can't decode value '%s'!\n", para.second.c_str());
+ top_parameters[RTLIL::escape_id(para.first)] = sig_value.as_const();
+ }
+
+ if (top_mod == nullptr && design->module(abstract_id))
+ top_mod = design->module(design->module(abstract_id)->derive(design, top_parameters));
+ else if (top_mod != nullptr && !top_parameters.empty())
+ top_mod = design->module(top_mod->derive(design, top_parameters));
+
+ if (top_mod != nullptr && top_mod->name != top_name) {
+ Module *m = top_mod->clone();
+ m->name = top_name;
+ Module *old_mod = design->module(top_name);
+ if (old_mod)
+ design->remove(old_mod);
+ design->add(m);
+ top_mod = m;
+ }
+ }
+
+ if (top_mod == nullptr && !load_top_mod.empty()) {
+#ifdef YOSYS_ENABLE_VERIFIC
+ if (verific_import_pending) {
+ verific_import(design, parameters, load_top_mod);
+ top_mod = design->module(RTLIL::escape_id(load_top_mod));
+ }
+#endif
+ if (top_mod == NULL)
+ log_cmd_error("Module `%s' not found!\n", load_top_mod.c_str());
+ } else {
+#ifdef YOSYS_ENABLE_VERIFIC
+ if (verific_import_pending)
+ verific_import(design, parameters);
+#endif
+ }
+
if (generate_mode) {
generate(design, generate_cells, generate_ports);
return;
@@ -517,6 +808,30 @@ struct HierarchyPass : public Pass {
if (mod_it.second->get_bool_attribute("\\top"))
top_mod = mod_it.second;
+ if (top_mod != nullptr && top_mod->name.begins_with("$abstract")) {
+ IdString top_name = top_mod->name.substr(strlen("$abstract"));
+
+ dict<RTLIL::IdString, RTLIL::Const> top_parameters;
+ for (auto &para : parameters) {
+ SigSpec sig_value;
+ if (!RTLIL::SigSpec::parse(sig_value, NULL, para.second))
+ log_cmd_error("Can't decode value '%s'!\n", para.second.c_str());
+ top_parameters[RTLIL::escape_id(para.first)] = sig_value.as_const();
+ }
+
+ top_mod = design->module(top_mod->derive(design, top_parameters));
+
+ if (top_mod != nullptr && top_mod->name != top_name) {
+ Module *m = top_mod->clone();
+ m->name = top_name;
+ Module *old_mod = design->module(top_name);
+ if (old_mod)
+ design->remove(old_mod);
+ design->add(m);
+ top_mod = m;
+ }
+ }
+
if (top_mod == nullptr && auto_top_mode) {
log_header(design, "Finding top of design hierarchy..\n");
dict<Module*, int> db;
@@ -530,6 +845,17 @@ struct HierarchyPass : public Pass {
log("Automatically selected %s as design top module.\n", log_id(top_mod));
}
+ 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)
{
@@ -545,29 +871,53 @@ struct HierarchyPass : public Pass {
}
for (auto module : used_modules) {
- if (expand_module(design, module, flag_check, libdirs))
+ 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) {
std::map<RTLIL::Module*, bool> cache;
for (auto mod : design->modules())
if (set_keep_assert(cache, mod)) {
- log("Module %s directly or indirectly contains $assert cells -> setting \"keep\" attribute.\n", log_id(mod));
+ log("Module %s directly or indirectly contains formal properties -> setting \"keep\" attribute.\n", log_id(mod));
mod->set_bool_attribute("\\keep");
}
}
@@ -620,59 +970,218 @@ struct HierarchyPass : public Pass {
}
}
- for (auto module : design->modules())
- for (auto cell : module->cells())
+ if (!nodefaults)
{
- if (GetSize(cell->parameters) != 0)
- continue;
+ dict<IdString, dict<IdString, Const>> defaults_db;
- Module *m = design->module(cell->type);
+ for (auto module : design->modules())
+ for (auto wire : module->wires())
+ if (wire->port_input && wire->attributes.count("\\defaultvalue"))
+ defaults_db[module->name][wire->name] = wire->attributes.at("\\defaultvalue");
- if (m == nullptr || m->get_bool_attribute("\\blackbox"))
- continue;
+ for (auto module : design->modules())
+ for (auto cell : module->cells())
+ {
+ if (defaults_db.count(cell->type) == 0)
+ continue;
+
+ if (keep_positionals) {
+ bool found_positionals = false;
+ for (auto &conn : cell->connections())
+ if (conn.first[0] == '$' && '0' <= conn.first[1] && conn.first[1] <= '9')
+ found_positionals = true;
+ if (found_positionals)
+ continue;
+ }
- for (auto &conn : cell->connections())
+ for (auto &it : defaults_db.at(cell->type))
+ if (!cell->hasPort(it.first))
+ cell->setPort(it.first, it.second);
+ }
+ }
+
+ std::set<Module*> blackbox_derivatives;
+ std::vector<Module*> design_modules = design->modules();
+
+ for (auto module : design_modules)
+ {
+ pool<Wire*> wand_wor_index;
+ dict<Wire*, SigSpec> wand_map, wor_map;
+ vector<SigSig> new_connections;
+
+ for (auto wire : module->wires())
{
- Wire *w = m->wire(conn.first);
+ if (wire->get_bool_attribute("\\wand")) {
+ wand_map[wire] = SigSpec();
+ wand_wor_index.insert(wire);
+ }
+ if (wire->get_bool_attribute("\\wor")) {
+ wor_map[wire] = SigSpec();
+ wand_wor_index.insert(wire);
+ }
+ }
- if (w == nullptr || w->port_id == 0)
- continue;
+ for (auto &conn : module->connections())
+ {
+ SigSig new_conn;
+ int cursor = 0;
- if (GetSize(conn.second) == 0)
- continue;
+ for (auto c : conn.first.chunks())
+ {
+ Wire *w = c.wire;
+ SigSpec rhs = conn.second.extract(cursor, GetSize(c));
+
+ if (wand_wor_index.count(w) == 0) {
+ new_conn.first.append(c);
+ new_conn.second.append(rhs);
+ } else {
+ if (wand_map.count(w)) {
+ SigSpec sig = SigSpec(State::S1, GetSize(w));
+ sig.replace(c.offset, rhs);
+ wand_map.at(w).append(sig);
+ } else {
+ SigSpec sig = SigSpec(State::S0, GetSize(w));
+ sig.replace(c.offset, rhs);
+ wor_map.at(w).append(sig);
+ }
+ }
+ cursor += GetSize(c);
+ }
+ new_connections.push_back(new_conn);
+ }
+ module->new_connections(new_connections);
- SigSpec sig = conn.second;
+ for (auto cell : module->cells())
+ {
+ if (!cell->known())
+ continue;
- if (!keep_portwidths && GetSize(w) != GetSize(conn.second))
+ for (auto &conn : cell->connections())
{
- if (GetSize(w) < GetSize(conn.second))
+ if (!cell->output(conn.first))
+ continue;
+
+ SigSpec new_sig;
+ bool update_port = false;
+
+ for (auto c : conn.second.chunks())
{
- int n = GetSize(conn.second) - GetSize(w);
- if (!w->port_input && w->port_output)
- module->connect(sig.extract(GetSize(w), n), Const(0, n));
- sig.remove(GetSize(w), n);
+ Wire *w = c.wire;
+
+ if (wand_wor_index.count(w) == 0) {
+ new_sig.append(c);
+ continue;
+ }
+
+ Wire *t = module->addWire(NEW_ID, GetSize(c));
+ new_sig.append(t);
+ update_port = true;
+
+ if (wand_map.count(w)) {
+ SigSpec sig = SigSpec(State::S1, GetSize(w));
+ sig.replace(c.offset, t);
+ wand_map.at(w).append(sig);
+ } else {
+ SigSpec sig = SigSpec(State::S0, GetSize(w));
+ sig.replace(c.offset, t);
+ wor_map.at(w).append(sig);
+ }
}
+
+ if (update_port)
+ cell->setPort(conn.first, new_sig);
+ }
+ }
+
+ for (auto w : wand_wor_index)
+ {
+ bool wand = wand_map.count(w);
+ SigSpec sigs = wand ? wand_map.at(w) : wor_map.at(w);
+
+ if (GetSize(sigs) == 0)
+ continue;
+
+ if (GetSize(w) == 1) {
+ if (wand)
+ module->addReduceAnd(NEW_ID, sigs, w);
else
+ module->addReduceOr(NEW_ID, sigs, w);
+ continue;
+ }
+
+ SigSpec s = sigs.extract(0, GetSize(w));
+ for (int i = GetSize(w); i < GetSize(sigs); i += GetSize(w)) {
+ if (wand)
+ s = module->And(NEW_ID, s, sigs.extract(i, GetSize(w)));
+ else
+ s = module->Or(NEW_ID, s, sigs.extract(i, GetSize(w)));
+ }
+ module->connect(w, s);
+ }
+
+ for (auto cell : module->cells())
+ {
+ Module *m = design->module(cell->type);
+
+ if (m == nullptr)
+ continue;
+
+ if (m->get_blackbox_attribute() && !cell->parameters.empty() && m->get_bool_attribute("\\dynports")) {
+ IdString new_m_name = m->derive(design, cell->parameters, true);
+ if (new_m_name.empty())
+ continue;
+ if (new_m_name != m->name) {
+ m = design->module(new_m_name);
+ blackbox_derivatives.insert(m);
+ }
+ }
+
+ for (auto &conn : cell->connections())
+ {
+ Wire *w = m->wire(conn.first);
+
+ if (w == nullptr || w->port_id == 0)
+ continue;
+
+ if (GetSize(conn.second) == 0)
+ continue;
+
+ SigSpec sig = conn.second;
+
+ if (!keep_portwidths && GetSize(w) != GetSize(conn.second))
{
- int n = GetSize(w) - GetSize(conn.second);
- if (w->port_input && !w->port_output)
- sig.append(Const(0, n));
+ if (GetSize(w) < GetSize(conn.second))
+ {
+ int n = GetSize(conn.second) - GetSize(w);
+ if (!w->port_input && w->port_output)
+ module->connect(sig.extract(GetSize(w), n), Const(0, n));
+ sig.remove(GetSize(w), n);
+ }
else
- sig.append(module->addWire(NEW_ID, n));
+ {
+ int n = GetSize(w) - GetSize(conn.second);
+ if (w->port_input && !w->port_output)
+ sig.append(Const(0, n));
+ else
+ sig.append(module->addWire(NEW_ID, n));
+ }
+
+ if (!conn.second.is_fully_const() || !w->port_input || w->port_output)
+ log_warning("Resizing cell port %s.%s.%s from %d bits to %d bits.\n", log_id(module), log_id(cell),
+ log_id(conn.first), GetSize(conn.second), GetSize(sig));
+ cell->setPort(conn.first, sig);
}
- if (!conn.second.is_fully_const() || !w->port_input || w->port_output)
- log_warning("Resizing cell port %s.%s.%s from %d bits to %d bits.\n", log_id(module), log_id(cell),
- log_id(conn.first), GetSize(conn.second), GetSize(sig));
- cell->setPort(conn.first, sig);
+ if (w->port_output && !w->port_input && sig.has_const())
+ log_error("Output port %s.%s.%s (%s) is connected to constants: %s\n",
+ log_id(module), log_id(cell), log_id(conn.first), log_id(cell->type), log_signal(sig));
}
-
- if (w->port_output && !w->port_input && sig.has_const())
- log_error("Output port %s.%s.%s (%s) is connected to constants: %s\n",
- log_id(module), log_id(cell), log_id(conn.first), log_id(cell->type), log_signal(sig));
}
}
+ for (auto module : blackbox_derivatives)
+ design->remove(module);
+
log_pop();
}
} HierarchyPass;
diff --git a/passes/hierarchy/submod.cc b/passes/hierarchy/submod.cc
index e9ee4eef9..ec242aa1f 100644
--- a/passes/hierarchy/submod.cc
+++ b/passes/hierarchy/submod.cc
@@ -269,7 +269,7 @@ struct SubmodWorker
struct SubmodPass : public Pass {
SubmodPass() : Pass("submod", "moving part of a module to a new submodule") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -297,7 +297,7 @@ struct SubmodPass : public Pass {
log("with -copy to not modify the source module.\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
log_header(design, "Executing SUBMOD pass (moving cells to submodules as requested).\n");
log_push();
diff --git a/passes/hierarchy/uniquify.cc b/passes/hierarchy/uniquify.cc
index 1da0870f6..ad3220918 100644
--- a/passes/hierarchy/uniquify.cc
+++ b/passes/hierarchy/uniquify.cc
@@ -24,7 +24,7 @@ PRIVATE_NAMESPACE_BEGIN
struct UniquifyPass : public Pass {
UniquifyPass() : Pass("uniquify", "create unique copies of modules") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -41,7 +41,7 @@ struct UniquifyPass : public Pass {
log("attribute set (the 'top' module is unique implicitly).\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
log_header(design, "Executing UNIQUIFY pass (creating unique copies of modules).\n");
@@ -75,7 +75,7 @@ struct UniquifyPass : public Pass {
if (tmod == nullptr)
continue;
- if (tmod->get_bool_attribute("\\blackbox"))
+ if (tmod->get_blackbox_attribute())
continue;
if (tmod->get_bool_attribute("\\unique") && newname == tmod->name)
@@ -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/Makefile.inc b/passes/memory/Makefile.inc
index ad359c01b..e468c3a07 100644
--- a/passes/memory/Makefile.inc
+++ b/passes/memory/Makefile.inc
@@ -7,4 +7,5 @@ OBJS += passes/memory/memory_unpack.o
OBJS += passes/memory/memory_bram.o
OBJS += passes/memory/memory_map.o
OBJS += passes/memory/memory_memx.o
+OBJS += passes/memory/memory_nordff.o
diff --git a/passes/memory/memory.cc b/passes/memory/memory.cc
index 947d598be..cee63bdd8 100644
--- a/passes/memory/memory.cc
+++ b/passes/memory/memory.cc
@@ -27,7 +27,7 @@ PRIVATE_NAMESPACE_BEGIN
struct MemoryPass : public Pass {
MemoryPass() : Pass("memory", "translate memories to basic cells") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -35,6 +35,7 @@ struct MemoryPass : public Pass {
log("\n");
log("This pass calls all the other memory_* passes in a useful order:\n");
log("\n");
+ log(" opt_mem\n");
log(" memory_dff [-nordff] (-memx implies -nordff)\n");
log(" opt_clean\n");
log(" memory_share\n");
@@ -48,7 +49,7 @@ struct MemoryPass : public Pass {
log("or multiport memory blocks if called with the -nomap option.\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
bool flag_nomap = false;
bool flag_nordff = false;
@@ -81,6 +82,7 @@ struct MemoryPass : public Pass {
}
extra_args(args, argidx, design);
+ Pass::call(design, "opt_mem");
Pass::call(design, flag_nordff ? "memory_dff -nordff" : "memory_dff");
Pass::call(design, "opt_clean");
Pass::call(design, "memory_share");
diff --git a/passes/memory/memory_bram.cc b/passes/memory/memory_bram.cc
index a7f9cf382..24478f2ee 100644
--- a/passes/memory/memory_bram.cc
+++ b/passes/memory/memory_bram.cc
@@ -68,6 +68,10 @@ struct rules_t
if (groups != GetSize(transp)) log_error("Bram %s variant %d has %d groups but only %d entries in 'transp'.\n", log_id(name), variant, groups, GetSize(transp));
if (groups != GetSize(clocks)) log_error("Bram %s variant %d has %d groups but only %d entries in 'clocks'.\n", log_id(name), variant, groups, GetSize(clocks));
if (groups != GetSize(clkpol)) log_error("Bram %s variant %d has %d groups but only %d entries in 'clkpol'.\n", log_id(name), variant, groups, GetSize(clkpol));
+
+ int group = 0;
+ for (auto e : enable)
+ if (e > dbits) log_error("Bram %s variant %d group %d has %d enable bits but only %d dbits.\n", log_id(name), variant, group, e, dbits);
}
vector<portinfo_t> make_portinfos() const
@@ -130,6 +134,7 @@ struct rules_t
dict<string, int> min_limits, max_limits;
bool or_next_if_better, make_transp, make_outreg;
char shuffle_enable;
+ vector<vector<std::tuple<bool,IdString,Const>>> attributes;
};
dict<IdString, vector<bram_t>> brams;
@@ -323,6 +328,20 @@ struct rules_t
continue;
}
+ if (GetSize(tokens) >= 2 && tokens[0] == "attribute") {
+ data.attributes.emplace_back();
+ for (int idx = 1; idx < GetSize(tokens); idx++) {
+ size_t c1 = tokens[idx][0] == '!' ? 1 : 0;
+ size_t c2 = tokens[idx].find("=");
+ bool exists = (c1 == 0);
+ IdString key = RTLIL::escape_id(tokens[idx].substr(c1, c2));
+ Const val = c2 != std::string::npos ? tokens[idx].substr(c2+1) : RTLIL::Const(1);
+
+ data.attributes.back().emplace_back(exists, key, val);
+ }
+ continue;
+ }
+
syntax_error();
}
}
@@ -472,8 +491,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 +512,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 +526,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 +553,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 +571,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 +659,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 +738,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;
@@ -719,7 +763,8 @@ grow_read_ports:;
if (clken) {
clock_domains[pi.clocks] = clkdom;
clock_polarities[pi.clkpol] = clkdom.second;
- read_transp[pi.transp] = transp;
+ if (!pi.make_transp)
+ read_transp[pi.transp] = transp;
pi.sig_clock = clkdom.first;
pi.sig_en = rd_en[cell_port_i];
pi.effective_clkpol = clkdom.second;
@@ -783,6 +828,43 @@ grow_read_ports:;
return false;
}
+ for (const auto &sums : match.attributes) {
+ bool found = false;
+ for (const auto &term : sums) {
+ bool exists = std::get<0>(term);
+ IdString key = std::get<1>(term);
+ const Const &value = std::get<2>(term);
+ auto it = cell->attributes.find(key);
+ if (it == cell->attributes.end()) {
+ if (exists)
+ continue;
+ found = true;
+ break;
+ }
+ else if (!exists)
+ continue;
+ if (it->second != value)
+ continue;
+ found = true;
+ break;
+ }
+ if (!found) {
+ std::stringstream ss;
+ bool exists = std::get<0>(sums.front());
+ if (!exists)
+ ss << "!";
+ IdString key = std::get<1>(sums.front());
+ ss << log_id(key);
+ const Const &value = std::get<2>(sums.front());
+ if (exists && value != Const(1))
+ ss << "=\"" << value.decode_string() << "\"";
+
+ log(" Rule for bram type %s rejected: requirement 'attribute %s ...' not met.\n",
+ log_id(match.name), ss.str().c_str());
+ return false;
+ }
+ }
+
if (mode == 1)
return true;
}
@@ -895,17 +977,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),
@@ -931,6 +1014,8 @@ grow_read_ports:;
SigSpec addr_ok_q = addr_ok;
if ((pi.clocks || pi.make_outreg) && !addr_ok.empty()) {
addr_ok_q = module->addWire(NEW_ID);
+ if (!pi.sig_en.empty())
+ addr_ok = module->Mux(NEW_ID, addr_ok_q, addr_ok, pi.sig_en);
module->addDff(NEW_ID, pi.sig_clock, addr_ok, addr_ok_q, pi.effective_clkpol);
}
@@ -1067,6 +1152,43 @@ void handle_cell(Cell *cell, const rules_t &rules)
goto next_match_rule;
}
+ for (const auto &sums : match.attributes) {
+ bool found = false;
+ for (const auto &term : sums) {
+ bool exists = std::get<0>(term);
+ IdString key = std::get<1>(term);
+ const Const &value = std::get<2>(term);
+ auto it = cell->attributes.find(key);
+ if (it == cell->attributes.end()) {
+ if (exists)
+ continue;
+ found = true;
+ break;
+ }
+ else if (!exists)
+ continue;
+ if (it->second != value)
+ continue;
+ found = true;
+ break;
+ }
+ if (!found) {
+ std::stringstream ss;
+ bool exists = std::get<0>(sums.front());
+ if (!exists)
+ ss << "!";
+ IdString key = std::get<1>(sums.front());
+ ss << log_id(key);
+ const Const &value = std::get<2>(sums.front());
+ if (exists && value != Const(1))
+ ss << "=\"" << value.decode_string() << "\"";
+
+ log(" Rule for bram type %s (variant %d) rejected: requirement 'attribute %s ...' not met.\n",
+ log_id(bram.name), bram.variant, ss.str().c_str());
+ goto next_match_rule;
+ }
+ }
+
log(" Rule #%d for bram type %s (variant %d) accepted.\n", i+1, log_id(bram.name), bram.variant);
if (or_next_if_better || !best_rule_cache.empty())
@@ -1120,7 +1242,7 @@ void handle_cell(Cell *cell, const rules_t &rules)
struct MemoryBramPass : public Pass {
MemoryBramPass() : Pass("memory_bram", "map memories to block rams") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -1192,6 +1314,13 @@ struct MemoryBramPass : public Pass {
log(" dcells ....... number of cells in 'data-direction'\n");
log(" cells ........ total number of cells (acells*dcells*dups)\n");
log("\n");
+ log("A match containing the command 'attribute' followed by a list of space\n");
+ log("separated 'name[=string_value]' values requires that the memory contains any\n");
+ log("one of the given attribute name and string values (where specified), or name\n");
+ log("and integer 1 value (if no string_value given, since Verilog will interpret\n");
+ log("'(* attr *)' as '(* attr=1 *)').\n");
+ log("A name prefixed with '!' indicates that the attribute must not exist.\n");
+ log("\n");
log("The interface for the created bram instances is derived from the bram\n");
log("description. Use 'techmap' to convert the created bram instances into\n");
log("instances of the actual bram cells of your target architecture.\n");
@@ -1210,7 +1339,7 @@ struct MemoryBramPass : public Pass {
log("the data bits to accommodate the enable pattern of port A.\n");
log("\n");
}
- virtual void execute(vector<string> args, Design *design)
+ void execute(vector<string> args, Design *design) YS_OVERRIDE
{
rules_t rules;
diff --git a/passes/memory/memory_collect.cc b/passes/memory/memory_collect.cc
index ab66e3fb8..9dcb3f024 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);
@@ -197,8 +194,8 @@ Cell *handle_memory(Module *module, RTLIL::Memory *memory)
log_assert(sig_wr_en.size() == wr_ports * memory->width);
mem->parameters["\\WR_PORTS"] = Const(wr_ports);
- mem->parameters["\\WR_CLK_ENABLE"] = wr_ports ? sig_wr_clk_enable.as_const() : Const(0, 1);
- mem->parameters["\\WR_CLK_POLARITY"] = wr_ports ? sig_wr_clk_polarity.as_const() : Const(0, 1);
+ mem->parameters["\\WR_CLK_ENABLE"] = wr_ports ? sig_wr_clk_enable.as_const() : State::S0;
+ mem->parameters["\\WR_CLK_POLARITY"] = wr_ports ? sig_wr_clk_polarity.as_const() : State::S0;
mem->setPort("\\WR_CLK", sig_wr_clk);
mem->setPort("\\WR_ADDR", sig_wr_addr);
@@ -212,15 +209,19 @@ Cell *handle_memory(Module *module, RTLIL::Memory *memory)
log_assert(sig_rd_data.size() == rd_ports * memory->width);
mem->parameters["\\RD_PORTS"] = Const(rd_ports);
- mem->parameters["\\RD_CLK_ENABLE"] = rd_ports ? sig_rd_clk_enable.as_const() : Const(0, 1);
- mem->parameters["\\RD_CLK_POLARITY"] = rd_ports ? sig_rd_clk_polarity.as_const() : Const(0, 1);
- mem->parameters["\\RD_TRANSPARENT"] = rd_ports ? sig_rd_transparent.as_const() : Const(0, 1);
+ mem->parameters["\\RD_CLK_ENABLE"] = rd_ports ? sig_rd_clk_enable.as_const() : State::S0;
+ mem->parameters["\\RD_CLK_POLARITY"] = rd_ports ? sig_rd_clk_polarity.as_const() : State::S0;
+ mem->parameters["\\RD_TRANSPARENT"] = rd_ports ? sig_rd_transparent.as_const() : State::S0;
mem->setPort("\\RD_CLK", sig_rd_clk);
mem->setPort("\\RD_ADDR", sig_rd_addr);
mem->setPort("\\RD_DATA", sig_rd_data);
mem->setPort("\\RD_EN", sig_rd_en);
+ // Copy attributes from RTLIL memory to $mem
+ for (auto attr : memory->attributes)
+ mem->attributes[attr.first] = attr.second;
+
for (auto c : memcells)
module->remove(c);
@@ -246,7 +247,7 @@ static void handle_module(Design *design, Module *module)
struct MemoryCollectPass : public Pass {
MemoryCollectPass() : Pass("memory_collect", "creating multi-port memory cells") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -256,7 +257,7 @@ struct MemoryCollectPass : public Pass {
log("memory cells.\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design) {
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE {
log_header(design, "Executing MEMORY_COLLECT pass (generating $mem cells).\n");
extra_args(args, 1, design);
for (auto &mod_it : design->modules_)
diff --git a/passes/memory/memory_dff.cc b/passes/memory/memory_dff.cc
index 40691d160..be4b3c100 100644
--- a/passes/memory/memory_dff.cc
+++ b/passes/memory/memory_dff.cc
@@ -17,6 +17,7 @@
*
*/
+#include <algorithm>
#include "kernel/yosys.h"
#include "kernel/sigtools.h"
@@ -33,8 +34,20 @@ struct MemoryDffWorker
dict<SigBit, int> sigbit_users_count;
dict<SigSpec, Cell*> mux_cells_a, mux_cells_b;
pool<Cell*> forward_merged_dffs, candidate_dffs;
+ pool<SigBit> init_bits;
- MemoryDffWorker(Module *module) : module(module), sigmap(module) { }
+ MemoryDffWorker(Module *module) : module(module), sigmap(module)
+ {
+ for (auto wire : module->wires()) {
+ if (wire->attributes.count("\\init") == 0)
+ continue;
+ SigSpec sig = sigmap(wire);
+ 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]);
+ }
+ }
bool find_sig_before_dff(RTLIL::SigSpec &sig, RTLIL::SigSpec &clk, bool &clk_polarity, bool after = false)
{
@@ -45,6 +58,9 @@ struct MemoryDffWorker
if (bit.wire == NULL)
continue;
+ if (!after && init_bits.count(sigmap(bit)))
+ return false;
+
for (auto cell : dff_cells)
{
if (after && forward_merged_dffs.count(cell))
@@ -72,6 +88,9 @@ struct MemoryDffWorker
if (d.size() != 1)
continue;
+ if (after && init_bits.count(d))
+ return false;
+
bit = d;
clk = this_clk;
clk_polarity = this_clk_polarity;
@@ -164,20 +183,27 @@ struct MemoryDffWorker
if (mux_cells_a.count(sig_data) || mux_cells_b.count(sig_data))
{
- bool enable_invert = mux_cells_a.count(sig_data) != 0;
- Cell *mux = enable_invert ? mux_cells_a.at(sig_data) : mux_cells_b.at(sig_data);
- SigSpec check_q = sigmap(mux->getPort(enable_invert ? "\\B" : "\\A"));
+ RTLIL::SigSpec en;
+ std::vector<RTLIL::SigSpec> check_q;
+
+ do {
+ bool enable_invert = mux_cells_a.count(sig_data) != 0;
+ Cell *mux = enable_invert ? mux_cells_a.at(sig_data) : mux_cells_b.at(sig_data);
+ check_q.push_back(sigmap(mux->getPort(enable_invert ? "\\B" : "\\A")));
+ sig_data = sigmap(mux->getPort("\\Y"));
+ en.append(enable_invert ? module->LogicNot(NEW_ID, mux->getPort("\\S")) : mux->getPort("\\S"));
+ } while (mux_cells_a.count(sig_data) || mux_cells_b.count(sig_data));
- sig_data = sigmap(mux->getPort("\\Y"));
for (auto bit : sig_data)
if (sigbit_users_count[bit] > 1)
goto skip_ff_after_read_merging;
- if (find_sig_before_dff(sig_data, clk_data, clk_polarity, true) && clk_data != RTLIL::SigSpec(RTLIL::State::Sx) && sig_data == check_q)
+ if (find_sig_before_dff(sig_data, clk_data, clk_polarity, true) && clk_data != RTLIL::SigSpec(RTLIL::State::Sx) &&
+ std::all_of(check_q.begin(), check_q.end(), [&](const SigSpec &cq) {return cq == sig_data; }))
{
disconnect_dff(sig_data);
cell->setPort("\\CLK", clk_data);
- cell->setPort("\\EN", enable_invert ? module->LogicNot(NEW_ID, mux->getPort("\\S")) : mux->getPort("\\S"));
+ cell->setPort("\\EN", en.size() > 1 ? module->ReduceAnd(NEW_ID, en) : en);
cell->setPort("\\DATA", sig_data);
cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(1);
cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity);
@@ -236,7 +262,7 @@ struct MemoryDffWorker
mux_cells_a[sigmap(cell->getPort("\\A"))] = cell;
mux_cells_b[sigmap(cell->getPort("\\B"))] = cell;
}
- if (cell->type == "$not" || cell->type == "$_NOT_" || (cell->type == "$logic_not" && GetSize(cell->getPort("\\A")) == 1)) {
+ if (cell->type.in("$not", "$_NOT_") || (cell->type == "$logic_not" && GetSize(cell->getPort("\\A")) == 1)) {
SigSpec sig_a = cell->getPort("\\A");
SigSpec sig_y = cell->getPort("\\Y");
if (cell->type == "$not")
@@ -265,7 +291,7 @@ struct MemoryDffWorker
struct MemoryDffPass : public Pass {
MemoryDffPass() : Pass("memory_dff", "merge input/output DFFs into memories") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -279,7 +305,7 @@ struct MemoryDffPass : public Pass {
log(" do not merge registers on read ports\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
bool flag_wr_only = false;
diff --git a/passes/memory/memory_map.cc b/passes/memory/memory_map.cc
index bffeec857..65bccb5ef 100644
--- a/passes/memory/memory_map.cc
+++ b/passes/memory/memory_map.cc
@@ -301,7 +301,7 @@ struct MemoryMapWorker
RTLIL::Wire *w = w_seladdr;
- if (wr_bit != RTLIL::SigSpec(1, 1))
+ if (wr_bit != State::S1)
{
RTLIL::Cell *c = module->addCell(genid(cell->name, "$wren", i, "", j, "", wr_offset), "$and");
c->parameters["\\A_SIGNED"] = RTLIL::Const(0);
@@ -352,7 +352,7 @@ struct MemoryMapWorker
struct MemoryMapPass : public Pass {
MemoryMapPass() : Pass("memory_map", "translate multiport memories to basic cells") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -362,7 +362,7 @@ struct MemoryMapPass : public Pass {
log("pass to word-wide DFFs and address decoders.\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design) {
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE {
log_header(design, "Executing MEMORY_MAP pass (converting $mem cells to logic and flip-flops).\n");
extra_args(args, 1, design);
for (auto mod : design->selected_modules())
diff --git a/passes/memory/memory_memx.cc b/passes/memory/memory_memx.cc
index 2b02e2490..958370164 100644
--- a/passes/memory/memory_memx.cc
+++ b/passes/memory/memory_memx.cc
@@ -28,7 +28,7 @@ PRIVATE_NAMESPACE_BEGIN
struct MemoryMemxPass : public Pass {
MemoryMemxPass() : Pass("memory_memx", "emulate vlog sim behavior for mem ports") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -38,7 +38,7 @@ struct MemoryMemxPass : public Pass {
log("behavior for out-of-bounds memory reads and writes.\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design) {
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE {
log_header(design, "Executing MEMORY_MEMX pass (converting $mem cells to logic and flip-flops).\n");
extra_args(args, 1, design);
diff --git a/passes/memory/memory_nordff.cc b/passes/memory/memory_nordff.cc
new file mode 100644
index 000000000..ba0361c0f
--- /dev/null
+++ b/passes/memory/memory_nordff.cc
@@ -0,0 +1,121 @@
+/*
+ * 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 MemoryNordffPass : public Pass {
+ MemoryNordffPass() : Pass("memory_nordff", "extract read port FFs from memories") { }
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" memory_nordff [options] [selection]\n");
+ log("\n");
+ log("This pass extracts FFs from memory read ports. This results in a netlist\n");
+ log("similar to what one would get from calling memory_dff with -nordff.\n");
+ log("\n");
+ }
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ log_header(design, "Executing MEMORY_NORDFF pass (extracting $dff cells from $mem).\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ // if (args[argidx] == "-nordff" || args[argidx] == "-wr_only") {
+ // flag_wr_only = true;
+ // continue;
+ // }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto module : design->selected_modules())
+ for (auto cell : vector<Cell*>(module->selected_cells()))
+ {
+ if (cell->type != "$mem")
+ continue;
+
+ int rd_ports = cell->getParam("\\RD_PORTS").as_int();
+ int abits = cell->getParam("\\ABITS").as_int();
+ int width = cell->getParam("\\WIDTH").as_int();
+
+ SigSpec rd_addr = cell->getPort("\\RD_ADDR");
+ SigSpec rd_data = cell->getPort("\\RD_DATA");
+ SigSpec rd_clk = cell->getPort("\\RD_CLK");
+ SigSpec rd_en = cell->getPort("\\RD_EN");
+ Const rd_clk_enable = cell->getParam("\\RD_CLK_ENABLE");
+ Const rd_clk_polarity = cell->getParam("\\RD_CLK_POLARITY");
+
+ for (int i = 0; i < rd_ports; i++)
+ {
+ bool clk_enable = rd_clk_enable[i] == State::S1;
+
+ if (clk_enable)
+ {
+ bool clk_polarity = cell->getParam("\\RD_CLK_POLARITY")[i] == State::S1;
+ bool transparent = cell->getParam("\\RD_TRANSPARENT")[i] == State::S1;
+
+ SigSpec clk = cell->getPort("\\RD_CLK")[i] ;
+ SigSpec en = cell->getPort("\\RD_EN")[i];
+ Cell *c;
+
+ if (transparent)
+ {
+ SigSpec sig_q = module->addWire(NEW_ID, abits);
+ SigSpec sig_d = rd_addr.extract(abits * i, abits);
+ rd_addr.replace(abits * i, sig_q);
+ if (en != State::S1)
+ sig_d = module->Mux(NEW_ID, sig_q, sig_d, en);
+ c = module->addDff(NEW_ID, clk, sig_d, sig_q, clk_polarity);
+ }
+ else
+ {
+ SigSpec sig_d = module->addWire(NEW_ID, width);
+ SigSpec sig_q = rd_data.extract(width * i, width);
+ rd_data.replace(width *i, sig_d);
+ if (en != State::S1)
+ sig_d = module->Mux(NEW_ID, sig_q, sig_d, en);
+ c = module->addDff(NEW_ID, clk, sig_d, sig_q, clk_polarity);
+ }
+
+ log("Extracted %s FF from read port %d of %s.%s: %s\n", transparent ? "addr" : "data",
+ i, log_id(module), log_id(cell), log_id(c));
+ }
+
+ rd_en[i] = State::S1;
+ rd_clk[i] = State::S0;
+ rd_clk_enable[i] = State::S0;
+ rd_clk_polarity[i] = State::S1;
+ }
+
+ cell->setPort("\\RD_ADDR", rd_addr);
+ cell->setPort("\\RD_DATA", rd_data);
+ cell->setPort("\\RD_CLK", rd_clk);
+ cell->setPort("\\RD_EN", rd_en);
+ cell->setParam("\\RD_CLK_ENABLE", rd_clk_enable);
+ cell->setParam("\\RD_CLK_POLARITY", rd_clk_polarity);
+ }
+ }
+} MemoryNordffPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/memory/memory_share.cc b/passes/memory/memory_share.cc
index ca09ac52c..eb912cfd4 100644
--- a/passes/memory/memory_share.cc
+++ b/passes/memory/memory_share.cc
@@ -155,7 +155,7 @@ struct MemoryShareWorker
{
bool ignore_data_port = false;
- if (cell->type == "$mux" || cell->type == "$pmux")
+ if (cell->type.in("$mux", "$pmux"))
{
std::vector<RTLIL::SigBit> sig_a = sigmap(cell->getPort("\\A"));
std::vector<RTLIL::SigBit> sig_b = sigmap(cell->getPort("\\B"));
@@ -173,7 +173,7 @@ struct MemoryShareWorker
continue;
}
- if ((cell->type == "$memwr" || cell->type == "$memrd") &&
+ if (cell->type.in("$memwr", "$memrd") &&
cell->parameters.at("\\MEMID").decode_string() == memid)
ignore_data_port = true;
@@ -690,7 +690,7 @@ struct MemoryShareWorker
sigmap_xmux.add(cell->getPort("\\Y"), sig_a);
}
- if (cell->type == "$mux" || cell->type == "$pmux")
+ if (cell->type.in("$mux", "$pmux"))
{
std::vector<RTLIL::SigBit> sig_y = sigmap(cell->getPort("\\Y"));
for (int i = 0; i < int(sig_y.size()); i++)
@@ -726,7 +726,7 @@ struct MemoryShareWorker
struct MemorySharePass : public Pass {
MemorySharePass() : Pass("memory_share", "consolidate memory ports") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -752,7 +752,7 @@ struct MemorySharePass : public Pass {
log("optimizations) such as \"share\" and \"opt_merge\".\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design) {
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE {
log_header(design, "Executing MEMORY_SHARE pass (consolidating $memrd/$memwr cells).\n");
extra_args(args, 1, design);
for (auto module : design->selected_modules())
diff --git a/passes/memory/memory_unpack.cc b/passes/memory/memory_unpack.cc
index a0fc31b5e..49ec66792 100644
--- a/passes/memory/memory_unpack.cc
+++ b/passes/memory/memory_unpack.cc
@@ -127,7 +127,7 @@ void handle_module(RTLIL::Design *design, RTLIL::Module *module)
struct MemoryUnpackPass : public Pass {
MemoryUnpackPass() : Pass("memory_unpack", "unpack multi-port memory cells") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -137,7 +137,7 @@ struct MemoryUnpackPass : public Pass {
log("$memwr cells. It is the counterpart to the memory_collect pass.\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design) {
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE {
log_header(design, "Executing MEMORY_UNPACK pass (generating $memrd/$memwr cells form $mem cells).\n");
extra_args(args, 1, design);
for (auto &mod_it : design->modules_)
diff --git a/passes/opt/Makefile.inc b/passes/opt/Makefile.inc
index 0d01e9d35..002c1a6a1 100644
--- a/passes/opt/Makefile.inc
+++ b/passes/opt/Makefile.inc
@@ -1,9 +1,11 @@
OBJS += passes/opt/opt.o
OBJS += passes/opt/opt_merge.o
+OBJS += passes/opt/opt_mem.o
OBJS += passes/opt/opt_muxtree.o
OBJS += passes/opt/opt_reduce.o
OBJS += passes/opt/opt_rmdff.o
+OBJS += passes/opt/opt_share.o
OBJS += passes/opt/opt_clean.o
OBJS += passes/opt/opt_expr.o
@@ -12,5 +14,7 @@ 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
+OBJS += passes/opt/pmux2shiftx.o
+OBJS += passes/opt/muxpack.o
endif
-
diff --git a/passes/opt/muxpack.cc b/passes/opt/muxpack.cc
new file mode 100644
index 000000000..c40c02acd
--- /dev/null
+++ b/passes/opt/muxpack.cc
@@ -0,0 +1,368 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * 2019 Eddie Hung <eddie@fpgeh.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
+ * 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 ExclusiveDatabase
+{
+ Module *module;
+ const SigMap &sigmap;
+
+ dict<SigBit, std::pair<SigSpec,std::vector<Const>>> sig_cmp_prev;
+
+ ExclusiveDatabase(Module *module, const SigMap &sigmap) : module(module), sigmap(sigmap)
+ {
+ SigSpec const_sig, nonconst_sig;
+ SigBit y_port;
+ pool<Cell*> reduce_or;
+ for (auto cell : module->cells()) {
+ if (cell->type == ID($eq)) {
+ nonconst_sig = sigmap(cell->getPort(ID::A));
+ const_sig = sigmap(cell->getPort(ID::B));
+ if (!const_sig.is_fully_const()) {
+ if (!nonconst_sig.is_fully_const())
+ continue;
+ std::swap(nonconst_sig, const_sig);
+ }
+ y_port = sigmap(cell->getPort(ID::Y));
+ }
+ else if (cell->type == ID($logic_not)) {
+ nonconst_sig = sigmap(cell->getPort(ID::A));
+ const_sig = Const(State::S0, GetSize(nonconst_sig));
+ y_port = sigmap(cell->getPort(ID::Y));
+ }
+ else if (cell->type == ID($reduce_or)) {
+ reduce_or.insert(cell);
+ continue;
+ }
+ else continue;
+
+ log_assert(!nonconst_sig.empty());
+ log_assert(!const_sig.empty());
+ sig_cmp_prev[y_port] = std::make_pair(nonconst_sig,std::vector<Const>{const_sig.as_const()});
+ }
+
+ for (auto cell : reduce_or) {
+ nonconst_sig = SigSpec();
+ std::vector<Const> values;
+ SigSpec a_port = sigmap(cell->getPort(ID::A));
+ for (auto bit : a_port) {
+ auto it = sig_cmp_prev.find(bit);
+ if (it == sig_cmp_prev.end()) {
+ nonconst_sig = SigSpec();
+ break;
+ }
+ if (nonconst_sig.empty())
+ nonconst_sig = it->second.first;
+ else if (nonconst_sig != it->second.first) {
+ nonconst_sig = SigSpec();
+ break;
+ }
+ for (auto value : it->second.second)
+ values.push_back(value);
+ }
+ if (nonconst_sig.empty())
+ continue;
+ y_port = sigmap(cell->getPort(ID::Y));
+ sig_cmp_prev[y_port] = std::make_pair(nonconst_sig,std::move(values));
+ }
+ }
+
+ bool query(const SigSpec &sig) const
+ {
+ SigSpec nonconst_sig;
+ pool<Const> const_values;
+
+ for (auto bit : sig.bits()) {
+ auto it = sig_cmp_prev.find(bit);
+ if (it == sig_cmp_prev.end())
+ return false;
+
+ if (nonconst_sig.empty())
+ nonconst_sig = it->second.first;
+ else if (nonconst_sig != it->second.first)
+ return false;
+
+ for (auto value : it->second.second)
+ if (!const_values.insert(value).second)
+ return false;
+ }
+
+ return true;
+ }
+};
+
+
+struct MuxpackWorker
+{
+ Module *module;
+ SigMap sigmap;
+
+ int mux_count, pmux_count;
+
+ pool<Cell*> remove_cells;
+
+ dict<SigSpec, Cell*> sig_chain_next;
+ dict<SigSpec, Cell*> sig_chain_prev;
+ pool<SigBit> sigbit_with_non_chain_users;
+ pool<Cell*> chain_start_cells;
+ pool<Cell*> candidate_cells;
+
+ ExclusiveDatabase excl_db;
+
+ void make_sig_chain_next_prev()
+ {
+ for (auto wire : module->wires())
+ {
+ if (wire->port_output || wire->get_bool_attribute(ID::keep)) {
+ for (auto bit : sigmap(wire))
+ sigbit_with_non_chain_users.insert(bit);
+ }
+ }
+
+ for (auto cell : module->cells())
+ {
+ if (cell->type.in(ID($mux), ID($pmux)) && !cell->get_bool_attribute(ID::keep))
+ {
+ SigSpec a_sig = sigmap(cell->getPort(ID::A));
+ SigSpec b_sig;
+ if (cell->type == ID($mux))
+ b_sig = sigmap(cell->getPort(ID::B));
+ SigSpec y_sig = sigmap(cell->getPort(ID::Y));
+
+ if (sig_chain_next.count(a_sig))
+ for (auto a_bit : a_sig.bits())
+ sigbit_with_non_chain_users.insert(a_bit);
+ else {
+ sig_chain_next[a_sig] = cell;
+ candidate_cells.insert(cell);
+ }
+
+ if (!b_sig.empty()) {
+ if (sig_chain_next.count(b_sig))
+ for (auto b_bit : b_sig.bits())
+ sigbit_with_non_chain_users.insert(b_bit);
+ else {
+ sig_chain_next[b_sig] = cell;
+ candidate_cells.insert(cell);
+ }
+ }
+
+ sig_chain_prev[y_sig] = cell;
+ continue;
+ }
+
+ for (auto conn : cell->connections())
+ if (cell->input(conn.first))
+ for (auto bit : sigmap(conn.second))
+ sigbit_with_non_chain_users.insert(bit);
+ }
+ }
+
+ void find_chain_start_cells()
+ {
+ for (auto cell : candidate_cells)
+ {
+ log_debug("Considering %s (%s)\n", log_id(cell), log_id(cell->type));
+
+ SigSpec a_sig = sigmap(cell->getPort(ID::A));
+ if (cell->type == ID($mux)) {
+ SigSpec b_sig = sigmap(cell->getPort(ID::B));
+ if (sig_chain_prev.count(a_sig) + sig_chain_prev.count(b_sig) != 1)
+ goto start_cell;
+
+ if (!sig_chain_prev.count(a_sig))
+ a_sig = b_sig;
+ }
+ else if (cell->type == ID($pmux)) {
+ if (!sig_chain_prev.count(a_sig))
+ goto start_cell;
+ }
+ else log_abort();
+
+ for (auto bit : a_sig.bits())
+ if (sigbit_with_non_chain_users.count(bit))
+ goto start_cell;
+
+ {
+ Cell *prev_cell = sig_chain_prev.at(a_sig);
+ log_assert(prev_cell);
+ SigSpec s_sig = sigmap(cell->getPort(ID(S)));
+ s_sig.append(sigmap(prev_cell->getPort(ID(S))));
+ if (!excl_db.query(s_sig))
+ goto start_cell;
+ }
+
+ continue;
+
+ start_cell:
+ chain_start_cells.insert(cell);
+ }
+ }
+
+ vector<Cell*> create_chain(Cell *start_cell)
+ {
+ vector<Cell*> chain;
+
+ Cell *c = start_cell;
+ while (c != nullptr)
+ {
+ chain.push_back(c);
+
+ SigSpec y_sig = sigmap(c->getPort(ID::Y));
+
+ if (sig_chain_next.count(y_sig) == 0)
+ break;
+
+ c = sig_chain_next.at(y_sig);
+ if (chain_start_cells.count(c) != 0)
+ break;
+ }
+
+ return chain;
+ }
+
+ void process_chain(vector<Cell*> &chain)
+ {
+ if (GetSize(chain) < 2)
+ return;
+
+ int cursor = 0;
+ while (cursor < GetSize(chain))
+ {
+ int cases = GetSize(chain) - cursor;
+
+ Cell *first_cell = chain[cursor];
+ dict<int, SigBit> taps_dict;
+
+ if (cases < 2) {
+ cursor++;
+ continue;
+ }
+
+ Cell *last_cell = chain[cursor+cases-1];
+
+ log("Converting %s.%s ... %s.%s to a pmux with %d cases.\n",
+ log_id(module), log_id(first_cell), log_id(module), log_id(last_cell), cases);
+
+ mux_count += cases;
+ pmux_count += 1;
+
+ first_cell->type = ID($pmux);
+ SigSpec b_sig = first_cell->getPort(ID::B);
+ SigSpec s_sig = first_cell->getPort(ID(S));
+
+ for (int i = 1; i < cases; i++) {
+ Cell* prev_cell = chain[cursor+i-1];
+ Cell* cursor_cell = chain[cursor+i];
+ if (sigmap(prev_cell->getPort(ID::Y)) == sigmap(cursor_cell->getPort(ID::A))) {
+ b_sig.append(cursor_cell->getPort(ID::B));
+ s_sig.append(cursor_cell->getPort(ID(S)));
+ }
+ else {
+ log_assert(cursor_cell->type == ID($mux));
+ b_sig.append(cursor_cell->getPort(ID::A));
+ s_sig.append(module->LogicNot(NEW_ID, cursor_cell->getPort(ID(S))));
+ }
+ remove_cells.insert(cursor_cell);
+ }
+
+ first_cell->setPort(ID::B, b_sig);
+ first_cell->setPort(ID(S), s_sig);
+ first_cell->setParam(ID(S_WIDTH), GetSize(s_sig));
+ first_cell->setPort(ID::Y, last_cell->getPort(ID::Y));
+
+ cursor += cases;
+ }
+ }
+
+ void cleanup()
+ {
+ for (auto cell : remove_cells)
+ module->remove(cell);
+
+ remove_cells.clear();
+ sig_chain_next.clear();
+ sig_chain_prev.clear();
+ chain_start_cells.clear();
+ candidate_cells.clear();
+ }
+
+ MuxpackWorker(Module *module) :
+ module(module), sigmap(module), mux_count(0), pmux_count(0), excl_db(module, sigmap)
+ {
+ make_sig_chain_next_prev();
+ find_chain_start_cells();
+
+ for (auto c : chain_start_cells) {
+ vector<Cell*> chain = create_chain(c);
+ process_chain(chain);
+ }
+
+ cleanup();
+ }
+};
+
+struct MuxpackPass : public Pass {
+ MuxpackPass() : Pass("muxpack", "$mux/$pmux cascades to $pmux") { }
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" muxpack [selection]\n");
+ log("\n");
+ log("This pass converts cascaded chains of $pmux cells (e.g. those create from case\n");
+ log("constructs) and $mux cells (e.g. those created by if-else constructs) into\n");
+ log("$pmux cells.\n");
+ log("\n");
+ log("This optimisation is conservative --- it will only pack $mux or $pmux cells\n");
+ log("whose select lines are driven by '$eq' cells with other such cells if it can be\n");
+ log("certain that their select inputs are mutually exclusive.\n");
+ log("\n");
+ }
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ log_header(design, "Executing MUXPACK pass ($mux cell cascades to $pmux).\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ int mux_count = 0;
+ int pmux_count = 0;
+
+ for (auto module : design->selected_modules()) {
+ MuxpackWorker worker(module);
+ mux_count += worker.mux_count;
+ pmux_count += worker.pmux_count;
+ }
+
+ log("Converted %d (p)mux cells into %d pmux cells.\n", mux_count, pmux_count);
+ }
+} MuxpackPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/opt/opt.cc b/passes/opt/opt.cc
index 021c1a03f..396819883 100644
--- a/passes/opt/opt.cc
+++ b/passes/opt/opt.cc
@@ -27,7 +27,7 @@ PRIVATE_NAMESPACE_BEGIN
struct OptPass : public Pass {
OptPass() : Pass("opt", "perform simple optimizations") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -44,7 +44,8 @@ struct OptPass : public Pass {
log(" opt_muxtree\n");
log(" opt_reduce [-fine] [-full]\n");
log(" opt_merge [-share_all]\n");
- log(" opt_rmdff [-keepdc]\n");
+ log(" opt_share (-full only)\n");
+ log(" opt_rmdff [-keepdc] [-sat]\n");
log(" opt_clean [-purge]\n");
log(" opt_expr [-mux_undef] [-mux_bool] [-undriven] [-clkinv] [-fine] [-full] [-keepdc]\n");
log(" while <changed design>\n");
@@ -54,7 +55,7 @@ struct OptPass : public Pass {
log(" do\n");
log(" opt_expr [-mux_undef] [-mux_bool] [-undriven] [-clkinv] [-fine] [-full] [-keepdc]\n");
log(" opt_merge [-share_all]\n");
- log(" opt_rmdff [-keepdc]\n");
+ log(" opt_rmdff [-keepdc] [-sat]\n");
log(" opt_clean [-purge]\n");
log(" while <changed design in opt_rmdff>\n");
log("\n");
@@ -63,13 +64,14 @@ struct OptPass : public Pass {
log("\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
std::string opt_clean_args;
std::string opt_expr_args;
std::string opt_reduce_args;
std::string opt_merge_args;
std::string opt_rmdff_args;
+ bool opt_share = false;
bool fast_mode = false;
log_header(design, "Executing OPT pass (performing simple optimizations).\n");
@@ -105,6 +107,7 @@ struct OptPass : public Pass {
if (args[argidx] == "-full") {
opt_expr_args += " -full";
opt_reduce_args += " -full";
+ opt_share = true;
continue;
}
if (args[argidx] == "-keepdc") {
@@ -112,6 +115,10 @@ struct OptPass : public Pass {
opt_rmdff_args += " -keepdc";
continue;
}
+ if (args[argidx] == "-sat") {
+ opt_rmdff_args += " -sat";
+ continue;
+ }
if (args[argidx] == "-share_all") {
opt_merge_args += " -share_all";
continue;
@@ -147,6 +154,8 @@ struct OptPass : public Pass {
Pass::call(design, "opt_muxtree");
Pass::call(design, "opt_reduce" + opt_reduce_args);
Pass::call(design, "opt_merge" + opt_merge_args);
+ if (opt_share)
+ Pass::call(design, "opt_share");
Pass::call(design, "opt_rmdff" + opt_rmdff_args);
Pass::call(design, "opt_clean" + opt_clean_args);
Pass::call(design, "opt_expr" + opt_expr_args);
diff --git a/passes/opt/opt_clean.cc b/passes/opt/opt_clean.cc
index fb1851868..2f69b3d4c 100644
--- a/passes/opt/opt_clean.cc
+++ b/passes/opt/opt_clean.cc
@@ -52,7 +52,7 @@ struct keep_cache_t
return cache.at(module);
cache[module] = true;
- if (!module->get_bool_attribute("\\keep")) {
+ if (!module->get_bool_attribute(ID::keep)) {
bool found_keep = false;
for (auto cell : module->cells())
if (query(cell)) found_keep = true;
@@ -64,7 +64,7 @@ struct keep_cache_t
bool query(Cell *cell)
{
- if (cell->type.in("$memwr", "$meminit", "$assert", "$assume", "$live", "$fair", "$cover"))
+ if (cell->type.in(ID($memwr), ID($meminit), ID($assert), ID($assume), ID($live), ID($fair), ID($cover), ID($specify2), ID($specify3), ID($specrule)))
return true;
if (cell->has_keep_attr())
@@ -85,15 +85,34 @@ void rmunused_module_cells(Module *module, bool verbose)
{
SigMap sigmap(module);
pool<Cell*> queue, unused;
+ pool<SigBit> used_raw_bits;
dict<SigBit, pool<Cell*>> wire2driver;
+ dict<SigBit, vector<string>> driver_driver_logs;
+
+ SigMap raw_sigmap;
+ for (auto &it : module->connections_) {
+ for (int i = 0; i < GetSize(it.second); i++) {
+ if (it.second[i].wire != nullptr)
+ raw_sigmap.add(it.first[i], it.second[i]);
+ }
+ }
for (auto &it : module->cells_) {
Cell *cell = it.second;
for (auto &it2 : cell->connections()) {
- if (!ct_all.cell_known(cell->type) || ct_all.cell_output(cell->type, it2.first))
- for (auto bit : sigmap(it2.second))
- if (bit.wire != nullptr)
- wire2driver[bit].insert(cell);
+ if (ct_all.cell_known(cell->type) && !ct_all.cell_output(cell->type, it2.first))
+ continue;
+ for (auto raw_bit : it2.second) {
+ if (raw_bit.wire == nullptr)
+ continue;
+ auto bit = sigmap(raw_bit);
+ if (bit.wire == nullptr && ct_all.cell_known(cell->type))
+ driver_driver_logs[raw_sigmap(raw_bit)].push_back(stringf("Driver-driver conflict "
+ "for %s between cell %s.%s and constant %s in %s: Resolved using constant.",
+ log_signal(raw_bit), log_id(cell), log_id(it2.first), log_signal(bit), log_id(module)));
+ if (bit.wire != nullptr)
+ wire2driver[bit].insert(cell);
+ }
}
if (keep_cache.query(cell))
queue.insert(cell);
@@ -103,10 +122,12 @@ void rmunused_module_cells(Module *module, bool verbose)
for (auto &it : module->wires_) {
Wire *wire = it.second;
- if (wire->port_output || wire->get_bool_attribute("\\keep")) {
+ if (wire->port_output || wire->get_bool_attribute(ID::keep)) {
for (auto bit : sigmap(wire))
for (auto c : wire2driver[bit])
queue.insert(c), unused.erase(c);
+ for (auto raw_bit : SigSpec(wire))
+ used_raw_bits.insert(raw_sigmap(raw_bit));
}
}
@@ -130,18 +151,34 @@ void rmunused_module_cells(Module *module, bool verbose)
for (auto cell : unused) {
if (verbose)
- log(" removing unused `%s' cell `%s'.\n", cell->type.c_str(), cell->name.c_str());
+ log_debug(" removing unused `%s' cell `%s'.\n", cell->type.c_str(), cell->name.c_str());
module->design->scratchpad_set_bool("opt.did_something", true);
module->remove(cell);
count_rm_cells++;
}
+
+ for (auto &it : module->cells_) {
+ Cell *cell = it.second;
+ for (auto &it2 : cell->connections()) {
+ if (ct_all.cell_known(cell->type) && !ct_all.cell_input(cell->type, it2.first))
+ continue;
+ for (auto raw_bit : raw_sigmap(it2.second))
+ used_raw_bits.insert(raw_bit);
+ }
+ }
+
+ for (auto it : driver_driver_logs) {
+ if (used_raw_bits.count(it.first))
+ for (auto msg : it.second)
+ log_warning("%s\n", msg.c_str());
+ }
}
int count_nontrivial_wire_attrs(RTLIL::Wire *w)
{
int count = w->attributes.size();
- count -= w->attributes.count("\\src");
- count -= w->attributes.count("\\unused_bits");
+ count -= w->attributes.count(ID(src));
+ count -= w->attributes.count(ID(unused_bits));
return count;
}
@@ -185,17 +222,17 @@ bool compare_signals(RTLIL::SigBit &s1, RTLIL::SigBit &s2, SigPool &regs, SigPoo
bool check_public_name(RTLIL::IdString id)
{
- const std::string &id_str = id.str();
- if (id_str[0] == '$')
+ if (id.begins_with("$"))
return false;
- if (id_str.substr(0, 2) == "\\_" && (id_str[id_str.size()-1] == '_' || id_str.find("_[") != std::string::npos))
+ const std::string &id_str = id.str();
+ if (id.begins_with("\\_") && (id.ends_with("_") || id_str.find("_[") != std::string::npos))
return false;
if (id_str.find(".$") != std::string::npos)
return false;
return true;
}
-void rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbose)
+bool rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbose)
{
SigPool register_signals;
SigPool connected_signals;
@@ -238,11 +275,13 @@ void rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos
module->connections_.clear();
SigPool used_signals;
+ SigPool raw_used_signals;
SigPool used_signals_nodrivers;
for (auto &it : module->cells_) {
RTLIL::Cell *cell = it.second;
for (auto &it2 : cell->connections_) {
assign_map.apply(it2.second);
+ raw_used_signals.add(it2.second);
used_signals.add(it2.second);
if (!ct_all.cell_output(cell->type, it2.first))
used_signals_nodrivers.add(it2.second);
@@ -252,84 +291,116 @@ void rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos
RTLIL::Wire *wire = it.second;
if (wire->port_id > 0) {
RTLIL::SigSpec sig = RTLIL::SigSpec(wire);
+ raw_used_signals.add(sig);
assign_map.apply(sig);
used_signals.add(sig);
if (!wire->port_input)
used_signals_nodrivers.add(sig);
}
- if (wire->get_bool_attribute("\\keep")) {
+ if (wire->get_bool_attribute(ID::keep)) {
RTLIL::SigSpec sig = RTLIL::SigSpec(wire);
assign_map.apply(sig);
used_signals.add(sig);
}
}
- std::vector<RTLIL::Wire*> maybe_del_wires;
+ pool<RTLIL::Wire*> del_wires_queue;
for (auto wire : module->wires())
{
- if ((!purge_mode && check_public_name(wire->name)) || wire->port_id != 0 || wire->get_bool_attribute("\\keep") || wire->attributes.count("\\init")) {
- RTLIL::SigSpec s1 = RTLIL::SigSpec(wire), s2 = s1;
- assign_map.apply(s2);
- if (!used_signals.check_any(s2) && wire->port_id == 0 && !wire->get_bool_attribute("\\keep")) {
- maybe_del_wires.push_back(wire);
- } else {
- log_assert(GetSize(s1) == GetSize(s2));
- RTLIL::SigSig new_conn;
- for (int i = 0; i < GetSize(s1); i++)
- if (s1[i] != s2[i]) {
- new_conn.first.append_bit(s1[i]);
- new_conn.second.append_bit(s2[i]);
+ SigSpec s1 = SigSpec(wire), s2 = assign_map(s1);
+ log_assert(GetSize(s1) == GetSize(s2));
+
+ Const initval;
+ if (wire->attributes.count(ID(init)))
+ initval = wire->attributes.at(ID(init));
+ if (GetSize(initval) != GetSize(wire))
+ initval.bits.resize(GetSize(wire), State::Sx);
+ if (initval.is_fully_undef())
+ wire->attributes.erase(ID(init));
+
+ if (GetSize(wire) == 0) {
+ // delete zero-width wires, unless they are module ports
+ if (wire->port_id == 0)
+ goto delete_this_wire;
+ } else
+ if (wire->port_id != 0 || wire->get_bool_attribute(ID::keep) || !initval.is_fully_undef()) {
+ // do not delete anything with "keep" or module ports or initialized wires
+ } else
+ if (!purge_mode && check_public_name(wire->name) && (raw_used_signals.check_any(s1) || used_signals.check_any(s2) || s1 != s2)) {
+ // do not get rid of public names unless in purge mode or if the wire is entirely unused, not even aliased
+ } else
+ if (!raw_used_signals.check_any(s1)) {
+ // delete wires that aren't used by anything directly
+ goto delete_this_wire;
+ } else
+ if (!used_signals.check_any(s2)) {
+ // delete wires that aren't used by anything indirectly, even though other wires may alias it
+ goto delete_this_wire;
+ }
+
+ if (0)
+ {
+ delete_this_wire:
+ del_wires_queue.insert(wire);
+ }
+ else
+ {
+ RTLIL::SigSig new_conn;
+ for (int i = 0; i < GetSize(s1); i++)
+ if (s1[i] != s2[i]) {
+ if (s2[i] == State::Sx && (initval[i] == State::S0 || initval[i] == State::S1)) {
+ s2[i] = initval[i];
+ initval[i] = State::Sx;
}
- if (new_conn.first.size() > 0) {
- used_signals.add(new_conn.first);
- used_signals.add(new_conn.second);
- module->connect(new_conn);
+ new_conn.first.append_bit(s1[i]);
+ new_conn.second.append_bit(s2[i]);
}
+ if (new_conn.first.size() > 0) {
+ if (initval.is_fully_undef())
+ wire->attributes.erase(ID(init));
+ else
+ wire->attributes.at(ID(init)) = initval;
+ used_signals.add(new_conn.first);
+ used_signals.add(new_conn.second);
+ module->connect(new_conn);
}
- } else {
- if (!used_signals.check_any(RTLIL::SigSpec(wire)))
- maybe_del_wires.push_back(wire);
- }
- RTLIL::SigSpec sig = assign_map(RTLIL::SigSpec(wire));
- if (!used_signals_nodrivers.check_any(sig)) {
- std::string unused_bits;
- for (int i = 0; i < GetSize(sig); i++) {
- if (sig[i].wire == NULL)
- continue;
- if (!used_signals_nodrivers.check(sig[i])) {
- if (!unused_bits.empty())
- unused_bits += " ";
- unused_bits += stringf("%d", i);
+ if (!used_signals_nodrivers.check_all(s2)) {
+ std::string unused_bits;
+ for (int i = 0; i < GetSize(s2); i++) {
+ if (s2[i].wire == NULL)
+ continue;
+ if (!used_signals_nodrivers.check(s2[i])) {
+ if (!unused_bits.empty())
+ unused_bits += " ";
+ unused_bits += stringf("%d", i);
+ }
}
+ if (unused_bits.empty() || wire->port_id != 0)
+ wire->attributes.erase(ID(unused_bits));
+ else
+ wire->attributes[ID(unused_bits)] = RTLIL::Const(unused_bits);
+ } else {
+ wire->attributes.erase(ID(unused_bits));
}
- if (unused_bits.empty() || wire->port_id != 0)
- wire->attributes.erase("\\unused_bits");
- else
- wire->attributes["\\unused_bits"] = RTLIL::Const(unused_bits);
- } else {
- wire->attributes.erase("\\unused_bits");
}
}
+ int del_temp_wires_count = 0;
+ for (auto wire : del_wires_queue) {
+ if (ys_debug() || (check_public_name(wire->name) && verbose))
+ log_debug(" removing unused non-port wire %s.\n", wire->name.c_str());
+ else
+ del_temp_wires_count++;
+ }
- pool<RTLIL::Wire*> del_wires;
+ module->remove(del_wires_queue);
+ count_rm_wires += GetSize(del_wires_queue);
- int del_wires_count = 0;
- for (auto wire : maybe_del_wires)
- if (!used_signals.check_any(RTLIL::SigSpec(wire))) {
- if (check_public_name(wire->name) && verbose) {
- log(" removing unused non-port wire %s.\n", wire->name.c_str());
- }
- del_wires.insert(wire);
- del_wires_count++;
- }
+ if (verbose && del_temp_wires_count)
+ log_debug(" removed %d unused temporary wires.\n", del_temp_wires_count);
- module->remove(del_wires);
- count_rm_wires += del_wires.size();
-
- if (verbose && del_wires_count > 0)
- log(" removed %d unused temporary wires.\n", del_wires_count);
+ return !del_wires_queue.empty();
}
bool rmunused_module_init(RTLIL::Module *module, bool purge_mode, bool verbose)
@@ -342,18 +413,18 @@ bool rmunused_module_init(RTLIL::Module *module, bool purge_mode, bool verbose)
dict<SigBit, State> qbits;
for (auto cell : module->cells())
- if (fftypes.cell_known(cell->type) && cell->hasPort("\\Q"))
+ if (fftypes.cell_known(cell->type) && cell->hasPort(ID(Q)))
{
- SigSpec sig = cell->getPort("\\Q");
+ SigSpec sig = cell->getPort(ID(Q));
for (int i = 0; i < GetSize(sig); i++)
{
SigBit bit = sig[i];
- if (bit.wire == nullptr || bit.wire->attributes.count("\\init") == 0)
+ if (bit.wire == nullptr || bit.wire->attributes.count(ID(init)) == 0)
continue;
- Const init = bit.wire->attributes.at("\\init");
+ Const init = bit.wire->attributes.at(ID(init));
if (i >= GetSize(init) || init[i] == State::Sx || init[i] == State::Sz)
continue;
@@ -368,10 +439,10 @@ bool rmunused_module_init(RTLIL::Module *module, bool purge_mode, bool verbose)
if (!purge_mode && wire->name[0] == '\\')
continue;
- if (wire->attributes.count("\\init") == 0)
+ if (wire->attributes.count(ID(init)) == 0)
continue;
- Const init = wire->attributes.at("\\init");
+ Const init = wire->attributes.at(ID(init));
for (int i = 0; i < GetSize(wire) && i < GetSize(init); i++)
{
@@ -392,9 +463,9 @@ bool rmunused_module_init(RTLIL::Module *module, bool purge_mode, bool verbose)
}
if (verbose)
- log(" removing redundent init attribute on %s.\n", log_id(wire));
+ log_debug(" removing redundant init attribute on %s.\n", log_id(wire));
- wire->attributes.erase("\\init");
+ wire->attributes.erase(ID(init));
did_something = true;
next_wire:;
}
@@ -409,33 +480,33 @@ void rmunused_module(RTLIL::Module *module, bool purge_mode, bool verbose, bool
std::vector<RTLIL::Cell*> delcells;
for (auto cell : module->cells())
- if (cell->type.in("$pos", "$_BUF_")) {
- bool is_signed = cell->type == "$pos" && cell->getParam("\\A_SIGNED").as_bool();
- RTLIL::SigSpec a = cell->getPort("\\A");
- RTLIL::SigSpec y = cell->getPort("\\Y");
+ if (cell->type.in(ID($pos), ID($_BUF_)) && !cell->has_keep_attr()) {
+ bool is_signed = cell->type == ID($pos) && cell->getParam(ID(A_SIGNED)).as_bool();
+ RTLIL::SigSpec a = cell->getPort(ID::A);
+ RTLIL::SigSpec y = cell->getPort(ID::Y);
a.extend_u0(GetSize(y), is_signed);
module->connect(y, a);
delcells.push_back(cell);
}
for (auto cell : delcells) {
if (verbose)
- log(" removing buffer cell `%s': %s = %s\n", cell->name.c_str(),
- log_signal(cell->getPort("\\Y")), log_signal(cell->getPort("\\A")));
+ log_debug(" removing buffer cell `%s': %s = %s\n", cell->name.c_str(),
+ log_signal(cell->getPort(ID::Y)), log_signal(cell->getPort(ID::A)));
module->remove(cell);
}
if (!delcells.empty())
module->design->scratchpad_set_bool("opt.did_something", true);
rmunused_module_cells(module, verbose);
- rmunused_module_signals(module, purge_mode, verbose);
+ while (rmunused_module_signals(module, purge_mode, verbose)) { }
if (rminit && rmunused_module_init(module, purge_mode, verbose))
- rmunused_module_signals(module, purge_mode, verbose);
+ while (rmunused_module_signals(module, purge_mode, verbose)) { }
}
struct OptCleanPass : public Pass {
OptCleanPass() : Pass("opt_clean", "remove unused cells and wires") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -452,7 +523,7 @@ struct OptCleanPass : public Pass {
log(" also remove internal nets if they have a public name\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
bool purge_mode = false;
@@ -476,6 +547,9 @@ struct OptCleanPass : public Pass {
ct_all.setup(design);
+ count_rm_cells = 0;
+ count_rm_wires = 0;
+
for (auto module : design->selected_whole_modules_warn()) {
if (module->has_processes_warn())
continue;
@@ -498,7 +572,7 @@ struct OptCleanPass : public Pass {
struct CleanPass : public Pass {
CleanPass() : Pass("clean", "remove unused cells and wires") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -513,7 +587,7 @@ struct CleanPass : public Pass {
log("in -purge mode between the commands.\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
bool purge_mode = false;
@@ -541,9 +615,10 @@ struct CleanPass : public Pass {
for (auto module : design->selected_whole_modules()) {
if (module->has_processes())
continue;
- rmunused_module(module, purge_mode, false, false);
+ rmunused_module(module, purge_mode, ys_debug(), false);
}
+ log_suppressed();
if (count_rm_cells > 0 || count_rm_wires > 0)
log("Removed %d unused cells and %d unused wires.\n", count_rm_cells, count_rm_wires);
diff --git a/passes/opt/opt_demorgan.cc b/passes/opt/opt_demorgan.cc
index f2af1cb93..4bc82815b 100644
--- a/passes/opt/opt_demorgan.cc
+++ b/passes/opt/opt_demorgan.cc
@@ -35,10 +35,10 @@ void demorgan_worker(
//TODO: Add support for reduce_xor
//DeMorgan of XOR is either XOR (if even number of inputs) or XNOR (if odd number)
- if( (cell->type != "$reduce_and") && (cell->type != "$reduce_or") )
+ if( (cell->type != ID($reduce_and)) && (cell->type != ID($reduce_or)) )
return;
- auto insig = sigmap(cell->getPort("\\A"));
+ auto insig = sigmap(cell->getPort(ID::A));
log("Inspecting %s cell %s (%d inputs)\n", log_id(cell->type), log_id(cell->name), GetSize(insig));
int num_inverted = 0;
for(int i=0; i<GetSize(insig); i++)
@@ -51,7 +51,7 @@ void demorgan_worker(
bool inverted = false;
for(auto x : ports)
{
- if(x.port == "\\Y" && x.cell->type == "$_NOT_")
+ if(x.port == ID::Y && x.cell->type == ID($_NOT_))
{
inverted = true;
break;
@@ -85,7 +85,7 @@ void demorgan_worker(
RTLIL::Cell* srcinv = NULL;
for(auto x : ports)
{
- if(x.port == "\\Y" && x.cell->type == "$_NOT_")
+ if(x.port == ID::Y && x.cell->type == ID($_NOT_))
{
srcinv = x.cell;
break;
@@ -103,7 +103,7 @@ void demorgan_worker(
//We ARE inverted - bypass it
//Don't automatically delete the inverter since other stuff might still use it
else
- insig[i] = srcinv->getPort("\\A");
+ insig[i] = srcinv->getPort(ID::A);
}
//Cosmetic fixup: If our input is just a scrambled version of one bus, rearrange it
@@ -151,25 +151,25 @@ void demorgan_worker(
}
//Push the new input signal back to the reduction (after bypassing/adding inverters)
- cell->setPort("\\A", insig);
+ cell->setPort(ID::A, insig);
//Change the cell type
- if(cell->type == "$reduce_and")
- cell->type = "$reduce_or";
- else if(cell->type == "$reduce_or")
- cell->type = "$reduce_and";
+ if(cell->type == ID($reduce_and))
+ cell->type = ID($reduce_or);
+ else if(cell->type == ID($reduce_or))
+ cell->type = ID($reduce_and);
//don't change XOR
//Add an inverter to the output
- auto inverted_output = cell->getPort("\\Y");
+ auto inverted_output = cell->getPort(ID::Y);
auto uninverted_output = m->addWire(NEW_ID);
m->addNot(NEW_ID, RTLIL::SigSpec(uninverted_output), inverted_output);
- cell->setPort("\\Y", uninverted_output);
+ cell->setPort(ID::Y, uninverted_output);
}
struct OptDemorganPass : public Pass {
OptDemorganPass() : Pass("opt_demorgan", "Optimize reductions with DeMorgan equivalents") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -179,7 +179,7 @@ struct OptDemorganPass : public Pass {
log("overall gate count of the circuit\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
log_header(design, "Executing OPT_DEMORGAN pass (push inverters through $reduce_* cells).\n");
diff --git a/passes/opt/opt_expr.cc b/passes/opt/opt_expr.cc
index 45331aa0b..4a2f170b8 100644
--- a/passes/opt/opt_expr.cc
+++ b/passes/opt/opt_expr.cc
@@ -39,6 +39,9 @@ void replace_undriven(RTLIL::Design *design, RTLIL::Module *module)
SigPool used_signals;
SigPool all_signals;
+ dict<SigBit, pair<Wire*, State>> initbits;
+ pool<Wire*> revisit_initwires;
+
for (auto cell : module->cells())
for (auto &conn : cell->connections()) {
if (!ct.cell_known(cell->type) || ct.cell_output(cell->type, conn.first))
@@ -48,9 +51,17 @@ void replace_undriven(RTLIL::Design *design, RTLIL::Module *module)
}
for (auto wire : module->wires()) {
+ if (wire->attributes.count(ID(init))) {
+ SigSpec sig = sigmap(wire);
+ Const initval = wire->attributes.at(ID(init));
+ for (int i = 0; i < GetSize(initval) && i < GetSize(wire); i++) {
+ if (initval[i] == State::S0 || initval[i] == State::S1)
+ initbits[sig[i]] = make_pair(wire, initval[i]);
+ }
+ }
if (wire->port_input)
driven_signals.add(sigmap(wire));
- if (wire->port_output)
+ if (wire->port_output || wire->get_bool_attribute(ID::keep))
used_signals.add(sigmap(wire));
all_signals.add(sigmap(wire));
}
@@ -67,18 +78,52 @@ void replace_undriven(RTLIL::Design *design, RTLIL::Module *module)
if (sig.size() == 0)
continue;
- log("Setting undriven signal in %s to undef: %s\n", RTLIL::id2cstr(module->name), log_signal(c));
- module->connect(RTLIL::SigSig(c, RTLIL::SigSpec(RTLIL::State::Sx, c.width)));
+ Const val(RTLIL::State::Sx, GetSize(sig));
+ for (int i = 0; i < GetSize(sig); i++) {
+ SigBit bit = sigmap(sig[i]);
+ auto cursor = initbits.find(bit);
+ if (cursor != initbits.end()) {
+ revisit_initwires.insert(cursor->second.first);
+ val[i] = cursor->second.second;
+ }
+ }
+
+ log_debug("Setting undriven signal in %s to constant: %s = %s\n", log_id(module), log_signal(sig), log_signal(val));
+ module->connect(sig, val);
did_something = true;
}
+
+ if (!revisit_initwires.empty())
+ {
+ SigMap sm2(module);
+
+ for (auto wire : revisit_initwires) {
+ SigSpec sig = sm2(wire);
+ Const initval = wire->attributes.at(ID(init));
+ for (int i = 0; i < GetSize(initval) && i < GetSize(wire); i++) {
+ if (SigBit(initval[i]) == sig[i])
+ initval[i] = State::Sx;
+ }
+ if (initval.is_fully_undef()) {
+ log_debug("Removing init attribute from %s/%s.\n", log_id(module), log_id(wire));
+ wire->attributes.erase(ID(init));
+ did_something = true;
+ } else if (initval != wire->attributes.at(ID(init))) {
+ log_debug("Updating init attribute on %s/%s: %s\n", log_id(module), log_id(wire), log_signal(initval));
+ wire->attributes[ID(init)] = initval;
+ did_something = true;
+ }
+ }
+ }
}
-void replace_cell(SigMap &assign_map, RTLIL::Module *module, RTLIL::Cell *cell, std::string info, std::string out_port, RTLIL::SigSpec out_val)
+void replace_cell(SigMap &assign_map, RTLIL::Module *module, RTLIL::Cell *cell,
+ const std::string &info YS_ATTRIBUTE(unused), IdString out_port, RTLIL::SigSpec out_val)
{
RTLIL::SigSpec Y = cell->getPort(out_port);
out_val.extend_u0(Y.size(), false);
- log("Replacing %s cell `%s' (%s) in module `%s' with constant driver `%s = %s'.\n",
+ log_debug("Replacing %s cell `%s' (%s) in module `%s' with constant driver `%s = %s'.\n",
cell->type.c_str(), cell->name.c_str(), info.c_str(),
module->name.c_str(), log_signal(Y), log_signal(out_val));
// log_cell(cell);
@@ -90,14 +135,14 @@ void replace_cell(SigMap &assign_map, RTLIL::Module *module, RTLIL::Cell *cell,
bool group_cell_inputs(RTLIL::Module *module, RTLIL::Cell *cell, bool commutative, SigMap &sigmap)
{
- std::string b_name = cell->hasPort("\\B") ? "\\B" : "\\A";
+ IdString b_name = cell->hasPort(ID::B) ? ID::B : ID::A;
- bool a_signed = cell->parameters.at("\\A_SIGNED").as_bool();
- bool b_signed = cell->parameters.at(b_name + "_SIGNED").as_bool();
+ bool a_signed = cell->parameters.at(ID(A_SIGNED)).as_bool();
+ bool b_signed = cell->parameters.at(b_name.str() + "_SIGNED").as_bool();
- RTLIL::SigSpec sig_a = sigmap(cell->getPort("\\A"));
+ RTLIL::SigSpec sig_a = sigmap(cell->getPort(ID::A));
RTLIL::SigSpec sig_b = sigmap(cell->getPort(b_name));
- RTLIL::SigSpec sig_y = sigmap(cell->getPort("\\Y"));
+ RTLIL::SigSpec sig_y = sigmap(cell->getPort(ID::Y));
sig_a.extend_u0(sig_y.size(), a_signed);
sig_b.extend_u0(sig_y.size(), b_signed);
@@ -112,10 +157,10 @@ bool group_cell_inputs(RTLIL::Module *module, RTLIL::Cell *cell, bool commutativ
int group_idx = GRP_DYN;
RTLIL::SigBit bit_a = bits_a[i], bit_b = bits_b[i];
- if (cell->type == "$or" && (bit_a == RTLIL::State::S1 || bit_b == RTLIL::State::S1))
+ if (cell->type == ID($or) && (bit_a == RTLIL::State::S1 || bit_b == RTLIL::State::S1))
bit_a = bit_b = RTLIL::State::S1;
- if (cell->type == "$and" && (bit_a == RTLIL::State::S0 || bit_b == RTLIL::State::S0))
+ if (cell->type == ID($and) && (bit_a == RTLIL::State::S0 || bit_b == RTLIL::State::S0))
bit_a = bit_b = RTLIL::State::S0;
if (bit_a.wire == NULL && bit_b.wire == NULL)
@@ -134,7 +179,7 @@ bool group_cell_inputs(RTLIL::Module *module, RTLIL::Cell *cell, bool commutativ
if (GetSize(grouped_bits[i]) == GetSize(bits_y))
return false;
- log("Replacing %s cell `%s' in module `%s' with cells using grouped bits:\n",
+ log_debug("Replacing %s cell `%s' in module `%s' with cells using grouped bits:\n",
log_id(cell->type), log_id(cell), log_id(module));
for (int i = 0; i < GRP_N; i++)
@@ -155,28 +200,35 @@ bool group_cell_inputs(RTLIL::Module *module, RTLIL::Cell *cell, bool commutativ
new_b.append_bit(it.first.second);
}
+ if (cell->type.in(ID($and), ID($or)) && i == GRP_CONST_A) {
+ log_debug(" 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);
- c->parameters["\\A_WIDTH"] = new_a.size();
- c->parameters["\\A_SIGNED"] = false;
+ c->setPort(ID::A, new_a);
+ c->parameters[ID(A_WIDTH)] = new_a.size();
+ c->parameters[ID(A_SIGNED)] = false;
- if (b_name == "\\B") {
- c->setPort("\\B", new_b);
- c->parameters["\\B_WIDTH"] = new_b.size();
- c->parameters["\\B_SIGNED"] = false;
+ if (b_name == ID::B) {
+ c->setPort(ID::B, new_b);
+ c->parameters[ID(B_WIDTH)] = new_b.size();
+ c->parameters[ID(B_SIGNED)] = false;
}
- c->setPort("\\Y", new_y);
- c->parameters["\\Y_WIDTH"] = new_y->width;
+ c->setPort(ID::Y, new_y);
+ c->parameters[ID(Y_WIDTH)] = new_y->width;
c->check();
module->connect(new_conn);
- log(" New cell `%s': A=%s", log_id(c), log_signal(new_a));
- if (b_name == "\\B")
- log(", B=%s", log_signal(new_b));
- log("\n");
+ log_debug(" New cell `%s': A=%s", log_id(c), log_signal(new_a));
+ if (b_name == ID::B)
+ log_debug(", B=%s", log_signal(new_b));
+ log_debug("\n");
}
cover_list("opt.opt_expr.fine.group", "$not", "$pos", "$and", "$or", "$xor", "$xnor", cell->type.str());
@@ -190,7 +242,7 @@ void handle_polarity_inv(Cell *cell, IdString port, IdString param, const SigMap
{
SigSpec sig = assign_map(cell->getPort(port));
if (invert_map.count(sig)) {
- log("Inverting %s of %s cell `%s' in module `%s': %s -> %s\n",
+ log_debug("Inverting %s of %s cell `%s' in module `%s': %s -> %s\n",
log_id(port), log_id(cell->type), log_id(cell), log_id(cell->module),
log_signal(sig), log_signal(invert_map.at(sig)));
cell->setPort(port, (invert_map.at(sig)));
@@ -219,7 +271,7 @@ void handle_clkpol_celltype_swap(Cell *cell, string type1, string type2, IdStrin
if (cell->type.in(type1, type2)) {
SigSpec sig = assign_map(cell->getPort(port));
if (invert_map.count(sig)) {
- log("Inverting %s of %s cell `%s' in module `%s': %s -> %s\n",
+ log_debug("Inverting %s of %s cell `%s' in module `%s': %s -> %s\n",
log_id(port), log_id(cell->type), log_id(cell), log_id(cell->module),
log_signal(sig), log_signal(invert_map.at(sig)));
cell->setPort(port, (invert_map.at(sig)));
@@ -259,6 +311,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)
@@ -300,11 +368,12 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
for (auto cell : module->cells())
if (design->selected(module, cell) && cell->type[0] == '$') {
- if ((cell->type == "$_NOT_" || cell->type == "$not" || cell->type == "$logic_not") &&
- cell->getPort("\\A").size() == 1 && cell->getPort("\\Y").size() == 1)
- invert_map[assign_map(cell->getPort("\\Y"))] = assign_map(cell->getPort("\\A"));
- if ((cell->type == "$mux" || cell->type == "$_MUX_") && cell->getPort("\\A") == SigSpec(State::S1) && cell->getPort("\\B") == SigSpec(State::S0))
- invert_map[assign_map(cell->getPort("\\Y"))] = assign_map(cell->getPort("\\S"));
+ if (cell->type.in(ID($_NOT_), ID($not), ID($logic_not)) &&
+ GetSize(cell->getPort(ID::A)) == 1 && GetSize(cell->getPort(ID::Y)) == 1)
+ invert_map[assign_map(cell->getPort(ID::Y))] = assign_map(cell->getPort(ID::A));
+ if (cell->type.in(ID($mux), ID($_MUX_)) &&
+ cell->getPort(ID::A) == SigSpec(State::S1) && cell->getPort(ID::B) == SigSpec(State::S0))
+ invert_map[assign_map(cell->getPort(ID::Y))] = assign_map(cell->getPort(ID(S)));
if (ct_combinational.cell_known(cell->type))
for (auto &conn : cell->connections()) {
RTLIL::SigSpec sig = assign_map(conn.second);
@@ -328,66 +397,66 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
for (auto cell : cells.sorted)
{
#define ACTION_DO(_p_, _s_) do { cover("opt.opt_expr.action_" S__LINE__); replace_cell(assign_map, module, cell, input.as_string(), _p_, _s_); goto next_cell; } while (0)
-#define ACTION_DO_Y(_v_) ACTION_DO("\\Y", RTLIL::SigSpec(RTLIL::State::S ## _v_))
+#define ACTION_DO_Y(_v_) ACTION_DO(ID::Y, RTLIL::SigSpec(RTLIL::State::S ## _v_))
if (clkinv)
{
- if (cell->type.in("$dff", "$dffe", "$dffsr", "$adff", "$fsm", "$memrd", "$memwr"))
- handle_polarity_inv(cell, "\\CLK", "\\CLK_POLARITY", assign_map, invert_map);
+ if (cell->type.in(ID($dff), ID($dffe), ID($dffsr), ID($adff), ID($fsm), ID($memrd), ID($memwr)))
+ handle_polarity_inv(cell, ID(CLK), ID(CLK_POLARITY), assign_map, invert_map);
- if (cell->type.in("$sr", "$dffsr", "$dlatchsr")) {
- handle_polarity_inv(cell, "\\SET", "\\SET_POLARITY", assign_map, invert_map);
- handle_polarity_inv(cell, "\\CLR", "\\CLR_POLARITY", assign_map, invert_map);
+ if (cell->type.in(ID($sr), ID($dffsr), ID($dlatchsr))) {
+ handle_polarity_inv(cell, ID(SET), ID(SET_POLARITY), assign_map, invert_map);
+ handle_polarity_inv(cell, ID(CLR), ID(CLR_POLARITY), assign_map, invert_map);
}
- if (cell->type.in("$dffe", "$dlatch", "$dlatchsr"))
- handle_polarity_inv(cell, "\\EN", "\\EN_POLARITY", assign_map, invert_map);
+ if (cell->type.in(ID($dffe), ID($dlatch), ID($dlatchsr)))
+ handle_polarity_inv(cell, ID(EN), ID(EN_POLARITY), assign_map, invert_map);
- handle_clkpol_celltype_swap(cell, "$_SR_N?_", "$_SR_P?_", "\\S", assign_map, invert_map);
- handle_clkpol_celltype_swap(cell, "$_SR_?N_", "$_SR_?P_", "\\R", assign_map, invert_map);
+ handle_clkpol_celltype_swap(cell, "$_SR_N?_", "$_SR_P?_", ID(S), assign_map, invert_map);
+ handle_clkpol_celltype_swap(cell, "$_SR_?N_", "$_SR_?P_", ID(R), assign_map, invert_map);
- handle_clkpol_celltype_swap(cell, "$_DFF_N_", "$_DFF_P_", "\\C", assign_map, invert_map);
+ handle_clkpol_celltype_swap(cell, "$_DFF_N_", "$_DFF_P_", ID(C), assign_map, invert_map);
- handle_clkpol_celltype_swap(cell, "$_DFFE_N?_", "$_DFFE_P?_", "\\C", assign_map, invert_map);
- handle_clkpol_celltype_swap(cell, "$_DFFE_?N_", "$_DFFE_?P_", "\\E", assign_map, invert_map);
+ handle_clkpol_celltype_swap(cell, "$_DFFE_N?_", "$_DFFE_P?_", ID(C), assign_map, invert_map);
+ handle_clkpol_celltype_swap(cell, "$_DFFE_?N_", "$_DFFE_?P_", ID(E), assign_map, invert_map);
- handle_clkpol_celltype_swap(cell, "$_DFF_N??_", "$_DFF_P??_", "\\C", assign_map, invert_map);
- handle_clkpol_celltype_swap(cell, "$_DFF_?N?_", "$_DFF_?P?_", "\\R", assign_map, invert_map);
+ handle_clkpol_celltype_swap(cell, "$_DFF_N??_", "$_DFF_P??_", ID(C), assign_map, invert_map);
+ handle_clkpol_celltype_swap(cell, "$_DFF_?N?_", "$_DFF_?P?_", ID(R), assign_map, invert_map);
- handle_clkpol_celltype_swap(cell, "$_DFFSR_N??_", "$_DFFSR_P??_", "\\C", assign_map, invert_map);
- handle_clkpol_celltype_swap(cell, "$_DFFSR_?N?_", "$_DFFSR_?P?_", "\\S", assign_map, invert_map);
- handle_clkpol_celltype_swap(cell, "$_DFFSR_??N_", "$_DFFSR_??P_", "\\R", assign_map, invert_map);
+ handle_clkpol_celltype_swap(cell, "$_DFFSR_N??_", "$_DFFSR_P??_", ID(C), assign_map, invert_map);
+ handle_clkpol_celltype_swap(cell, "$_DFFSR_?N?_", "$_DFFSR_?P?_", ID(S), assign_map, invert_map);
+ handle_clkpol_celltype_swap(cell, "$_DFFSR_??N_", "$_DFFSR_??P_", ID(R), assign_map, invert_map);
- handle_clkpol_celltype_swap(cell, "$_DLATCH_N_", "$_DLATCH_P_", "\\E", assign_map, invert_map);
+ handle_clkpol_celltype_swap(cell, "$_DLATCH_N_", "$_DLATCH_P_", ID(E), assign_map, invert_map);
- handle_clkpol_celltype_swap(cell, "$_DLATCHSR_N??_", "$_DLATCHSR_P??_", "\\E", assign_map, invert_map);
- handle_clkpol_celltype_swap(cell, "$_DLATCHSR_?N?_", "$_DLATCHSR_?P?_", "\\S", assign_map, invert_map);
- handle_clkpol_celltype_swap(cell, "$_DLATCHSR_??N_", "$_DLATCHSR_??P_", "\\R", assign_map, invert_map);
+ handle_clkpol_celltype_swap(cell, "$_DLATCHSR_N??_", "$_DLATCHSR_P??_", ID(E), assign_map, invert_map);
+ handle_clkpol_celltype_swap(cell, "$_DLATCHSR_?N?_", "$_DLATCHSR_?P?_", ID(S), assign_map, invert_map);
+ handle_clkpol_celltype_swap(cell, "$_DLATCHSR_??N_", "$_DLATCHSR_??P_", ID(R), assign_map, invert_map);
}
bool detect_const_and = false;
bool detect_const_or = false;
- if (cell->type.in("$reduce_and", "$_AND_"))
+ if (cell->type.in(ID($reduce_and), ID($_AND_)))
detect_const_and = true;
- if (cell->type.in("$and", "$logic_and") && GetSize(cell->getPort("\\A")) == 1 && GetSize(cell->getPort("\\B")) == 1 && !cell->getParam("\\A_SIGNED").as_bool())
+ if (cell->type.in(ID($and), ID($logic_and)) && GetSize(cell->getPort(ID::A)) == 1 && GetSize(cell->getPort(ID::B)) == 1 && !cell->getParam(ID(A_SIGNED)).as_bool())
detect_const_and = true;
- if (cell->type.in("$reduce_or", "$reduce_bool", "$_OR_"))
+ if (cell->type.in(ID($reduce_or), ID($reduce_bool), ID($_OR_)))
detect_const_or = true;
- if (cell->type.in("$or", "$logic_or") && GetSize(cell->getPort("\\A")) == 1 && GetSize(cell->getPort("\\B")) == 1 && !cell->getParam("\\A_SIGNED").as_bool())
+ if (cell->type.in(ID($or), ID($logic_or)) && GetSize(cell->getPort(ID::A)) == 1 && GetSize(cell->getPort(ID::B)) == 1 && !cell->getParam(ID(A_SIGNED)).as_bool())
detect_const_or = true;
if (detect_const_and || detect_const_or)
{
- pool<SigBit> input_bits = assign_map(cell->getPort("\\A")).to_sigbit_pool();
+ pool<SigBit> input_bits = assign_map(cell->getPort(ID::A)).to_sigbit_pool();
bool found_zero = false, found_one = false, found_undef = false, found_inv = false, many_conconst = false;
SigBit non_const_input = State::Sm;
- if (cell->hasPort("\\B")) {
- vector<SigBit> more_bits = assign_map(cell->getPort("\\B")).to_sigbit_vector();
+ if (cell->hasPort(ID::B)) {
+ vector<SigBit> more_bits = assign_map(cell->getPort(ID::B)).to_sigbit_vector();
input_bits.insert(more_bits.begin(), more_bits.end());
}
@@ -410,51 +479,50 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
if (detect_const_and && (found_zero || found_inv)) {
cover("opt.opt_expr.const_and");
- replace_cell(assign_map, module, cell, "const_and", "\\Y", RTLIL::State::S0);
+ replace_cell(assign_map, module, cell, "const_and", ID::Y, RTLIL::State::S0);
goto next_cell;
}
if (detect_const_or && (found_one || found_inv)) {
cover("opt.opt_expr.const_or");
- replace_cell(assign_map, module, cell, "const_or", "\\Y", RTLIL::State::S1);
+ replace_cell(assign_map, module, cell, "const_or", ID::Y, RTLIL::State::S1);
goto next_cell;
}
if (non_const_input != State::Sm && !found_undef) {
cover("opt.opt_expr.and_or_buffer");
- replace_cell(assign_map, module, cell, "and_or_buffer", "\\Y", non_const_input);
+ replace_cell(assign_map, module, cell, "and_or_buffer", ID::Y, non_const_input);
goto next_cell;
}
}
- if (cell->type.in("$reduce_and", "$reduce_or", "$reduce_bool", "$reduce_xor", "$reduce_xnor", "$neg") &&
- GetSize(cell->getPort("\\A")) == 1 && GetSize(cell->getPort("\\Y")) == 1)
+ if (cell->type.in(ID($reduce_and), ID($reduce_or), ID($reduce_bool), ID($reduce_xor), ID($reduce_xnor), ID($neg)) &&
+ GetSize(cell->getPort(ID::A)) == 1 && GetSize(cell->getPort(ID::Y)) == 1)
{
- if (cell->type == "$reduce_xnor") {
+ if (cell->type == ID($reduce_xnor)) {
cover("opt.opt_expr.reduce_xnor_not");
- log("Replacing %s cell `%s' in module `%s' with $not cell.\n",
+ log_debug("Replacing %s cell `%s' in module `%s' with $not cell.\n",
log_id(cell->type), log_id(cell->name), log_id(module));
- cell->type = "$not";
+ cell->type = ID($not);
+ did_something = true;
} else {
cover("opt.opt_expr.unary_buffer");
- replace_cell(assign_map, module, cell, "unary_buffer", "\\Y", cell->getPort("\\A"));
+ replace_cell(assign_map, module, cell, "unary_buffer", ID::Y, cell->getPort(ID::A));
}
goto next_cell;
}
if (do_fine)
{
- if (cell->type == "$not" || cell->type == "$pos" ||
- cell->type == "$and" || cell->type == "$or" || cell->type == "$xor" || cell->type == "$xnor")
+ if (cell->type.in(ID($not), ID($pos), ID($and), ID($or), ID($xor), ID($xnor)))
if (group_cell_inputs(module, cell, true, assign_map))
goto next_cell;
- if (cell->type == "$logic_not" || cell->type == "$logic_and" || cell->type == "$logic_or" ||
- cell->type == "$reduce_or" || cell->type == "$reduce_and" || cell->type == "$reduce_bool")
+ if (cell->type.in(ID($logic_not), ID($logic_and), ID($logic_or), ID($reduce_or), ID($reduce_and), ID($reduce_bool)))
{
- SigBit neutral_bit = cell->type == "$reduce_and" ? State::S1 : State::S0;
+ SigBit neutral_bit = cell->type == ID($reduce_and) ? State::S1 : State::S0;
- RTLIL::SigSpec sig_a = assign_map(cell->getPort("\\A"));
+ RTLIL::SigSpec sig_a = assign_map(cell->getPort(ID::A));
RTLIL::SigSpec new_sig_a;
for (auto bit : sig_a)
@@ -465,19 +533,19 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
if (GetSize(new_sig_a) < GetSize(sig_a)) {
cover_list("opt.opt_expr.fine.neutral_A", "$logic_not", "$logic_and", "$logic_or", "$reduce_or", "$reduce_and", "$reduce_bool", cell->type.str());
- log("Replacing port A of %s cell `%s' in module `%s' with shorter expression: %s -> %s\n",
+ log_debug("Replacing port A of %s cell `%s' in module `%s' with shorter expression: %s -> %s\n",
cell->type.c_str(), cell->name.c_str(), module->name.c_str(), log_signal(sig_a), log_signal(new_sig_a));
- cell->setPort("\\A", new_sig_a);
- cell->parameters.at("\\A_WIDTH") = GetSize(new_sig_a);
+ cell->setPort(ID::A, new_sig_a);
+ cell->parameters.at(ID(A_WIDTH)) = GetSize(new_sig_a);
did_something = true;
}
}
- if (cell->type == "$logic_and" || cell->type == "$logic_or")
+ if (cell->type.in(ID($logic_and), ID($logic_or)))
{
SigBit neutral_bit = State::S0;
- RTLIL::SigSpec sig_b = assign_map(cell->getPort("\\B"));
+ RTLIL::SigSpec sig_b = assign_map(cell->getPort(ID::B));
RTLIL::SigSpec new_sig_b;
for (auto bit : sig_b)
@@ -488,17 +556,17 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
if (GetSize(new_sig_b) < GetSize(sig_b)) {
cover_list("opt.opt_expr.fine.neutral_B", "$logic_and", "$logic_or", cell->type.str());
- log("Replacing port B of %s cell `%s' in module `%s' with shorter expression: %s -> %s\n",
+ log_debug("Replacing port B of %s cell `%s' in module `%s' with shorter expression: %s -> %s\n",
cell->type.c_str(), cell->name.c_str(), module->name.c_str(), log_signal(sig_b), log_signal(new_sig_b));
- cell->setPort("\\B", new_sig_b);
- cell->parameters.at("\\B_WIDTH") = GetSize(new_sig_b);
+ cell->setPort(ID::B, new_sig_b);
+ cell->parameters.at(ID(B_WIDTH)) = GetSize(new_sig_b);
did_something = true;
}
}
- if (cell->type == "$reduce_and")
+ if (cell->type == ID($reduce_and))
{
- RTLIL::SigSpec sig_a = assign_map(cell->getPort("\\A"));
+ RTLIL::SigSpec sig_a = assign_map(cell->getPort(ID::A));
RTLIL::State new_a = RTLIL::State::S1;
for (auto &bit : sig_a.to_sigbit_vector())
@@ -514,17 +582,17 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
if (new_a != RTLIL::State::Sm && RTLIL::SigSpec(new_a) != sig_a) {
cover("opt.opt_expr.fine.$reduce_and");
- log("Replacing port A of %s cell `%s' in module `%s' with constant driver: %s -> %s\n",
+ log_debug("Replacing port A of %s cell `%s' in module `%s' with constant driver: %s -> %s\n",
cell->type.c_str(), cell->name.c_str(), module->name.c_str(), log_signal(sig_a), log_signal(new_a));
- cell->setPort("\\A", sig_a = new_a);
- cell->parameters.at("\\A_WIDTH") = 1;
+ cell->setPort(ID::A, sig_a = new_a);
+ cell->parameters.at(ID(A_WIDTH)) = 1;
did_something = true;
}
}
- if (cell->type == "$logic_not" || cell->type == "$logic_and" || cell->type == "$logic_or" || cell->type == "$reduce_or" || cell->type == "$reduce_bool")
+ if (cell->type.in(ID($logic_not), ID($logic_and), ID($logic_or), ID($reduce_or), ID($reduce_bool)))
{
- RTLIL::SigSpec sig_a = assign_map(cell->getPort("\\A"));
+ RTLIL::SigSpec sig_a = assign_map(cell->getPort(ID::A));
RTLIL::State new_a = RTLIL::State::S0;
for (auto &bit : sig_a.to_sigbit_vector())
@@ -540,17 +608,17 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
if (new_a != RTLIL::State::Sm && RTLIL::SigSpec(new_a) != sig_a) {
cover_list("opt.opt_expr.fine.A", "$logic_not", "$logic_and", "$logic_or", "$reduce_or", "$reduce_bool", cell->type.str());
- log("Replacing port A of %s cell `%s' in module `%s' with constant driver: %s -> %s\n",
+ log_debug("Replacing port A of %s cell `%s' in module `%s' with constant driver: %s -> %s\n",
cell->type.c_str(), cell->name.c_str(), module->name.c_str(), log_signal(sig_a), log_signal(new_a));
- cell->setPort("\\A", sig_a = new_a);
- cell->parameters.at("\\A_WIDTH") = 1;
+ cell->setPort(ID::A, sig_a = new_a);
+ cell->parameters.at(ID(A_WIDTH)) = 1;
did_something = true;
}
}
- if (cell->type == "$logic_and" || cell->type == "$logic_or")
+ if (cell->type.in(ID($logic_and), ID($logic_or)))
{
- RTLIL::SigSpec sig_b = assign_map(cell->getPort("\\B"));
+ RTLIL::SigSpec sig_b = assign_map(cell->getPort(ID::B));
RTLIL::State new_b = RTLIL::State::S0;
for (auto &bit : sig_b.to_sigbit_vector())
@@ -566,25 +634,95 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
if (new_b != RTLIL::State::Sm && RTLIL::SigSpec(new_b) != sig_b) {
cover_list("opt.opt_expr.fine.B", "$logic_and", "$logic_or", cell->type.str());
- log("Replacing port B of %s cell `%s' in module `%s' with constant driver: %s -> %s\n",
+ log_debug("Replacing port B of %s cell `%s' in module `%s' with constant driver: %s -> %s\n",
cell->type.c_str(), cell->name.c_str(), module->name.c_str(), log_signal(sig_b), log_signal(new_b));
- cell->setPort("\\B", sig_b = new_b);
- cell->parameters.at("\\B_WIDTH") = 1;
+ cell->setPort(ID::B, sig_b = new_b);
+ cell->parameters.at(ID(B_WIDTH)) = 1;
+ did_something = true;
+ }
+ }
+
+ if (cell->type.in(ID($add), ID($sub)))
+ {
+ RTLIL::SigSpec sig_a = assign_map(cell->getPort(ID::A));
+ RTLIL::SigSpec sig_b = assign_map(cell->getPort(ID::B));
+ RTLIL::SigSpec sig_y = cell->getPort(ID::Y);
+ bool sub = cell->type == ID($sub);
+
+ int i;
+ for (i = 0; i < GetSize(sig_y); i++) {
+ if (sig_b.at(i, State::Sx) == State::S0 && sig_a.at(i, State::Sx) != State::Sx)
+ module->connect(sig_y[i], sig_a[i]);
+ else if (!sub && sig_a.at(i, State::Sx) == State::S0 && sig_b.at(i, State::Sx) != State::Sx)
+ module->connect(sig_y[i], sig_b[i]);
+ else
+ break;
+ }
+ if (i > 0) {
+ cover_list("opt.opt_expr.fine", "$add", "$sub", cell->type.str());
+ cell->setPort(ID::A, sig_a.extract_end(i));
+ cell->setPort(ID::B, sig_b.extract_end(i));
+ cell->setPort(ID::Y, sig_y.extract_end(i));
+ cell->fixup_parameters();
+ did_something = true;
+ }
+ }
+
+ if (cell->type == "$alu")
+ {
+ RTLIL::SigSpec sig_a = assign_map(cell->getPort(ID::A));
+ RTLIL::SigSpec sig_b = assign_map(cell->getPort(ID::B));
+ RTLIL::SigBit sig_ci = assign_map(cell->getPort(ID(CI)));
+ RTLIL::SigBit sig_bi = assign_map(cell->getPort(ID(BI)));
+ RTLIL::SigSpec sig_x = cell->getPort(ID(X));
+ RTLIL::SigSpec sig_y = cell->getPort(ID::Y);
+ RTLIL::SigSpec sig_co = cell->getPort(ID(CO));
+
+ if (sig_ci.wire || sig_bi.wire)
+ goto next_cell;
+
+ bool sub = (sig_ci == State::S1 && sig_bi == State::S1);
+
+ // If not a subtraction, yet there is a carry or B is inverted
+ // then no optimisation is possible as carry will not be constant
+ if (!sub && (sig_ci != State::S0 || sig_bi != State::S0))
+ goto next_cell;
+
+ int i;
+ for (i = 0; i < GetSize(sig_y); i++) {
+ if (sig_b.at(i, State::Sx) == State::S0 && sig_a.at(i, State::Sx) != State::Sx) {
+ module->connect(sig_x[i], sub ? module->Not(NEW_ID, sig_a[i]).as_bit() : sig_a[i]);
+ module->connect(sig_y[i], sig_a[i]);
+ module->connect(sig_co[i], sub ? State::S1 : State::S0);
+ }
+ else if (!sub && sig_a.at(i, State::Sx) == State::S0 && sig_b.at(i, State::Sx) != State::Sx) {
+ module->connect(sig_x[i], sig_b[i]);
+ module->connect(sig_y[i], sig_b[i]);
+ module->connect(sig_co[i], State::S0);
+ }
+ else
+ break;
+ }
+ if (i > 0) {
+ cover("opt.opt_expr.fine.$alu");
+ cell->setPort(ID::A, sig_a.extract_end(i));
+ cell->setPort(ID::B, sig_b.extract_end(i));
+ cell->setPort(ID(X), sig_x.extract_end(i));
+ cell->setPort(ID::Y, sig_y.extract_end(i));
+ cell->setPort(ID(CO), sig_co.extract_end(i));
+ cell->fixup_parameters();
did_something = true;
}
}
}
- if (cell->type == "$reduce_xor" || cell->type == "$reduce_xnor" || cell->type == "$shift" || cell->type == "$shiftx" ||
- cell->type == "$shl" || cell->type == "$shr" || cell->type == "$sshl" || cell->type == "$sshr" ||
- cell->type == "$lt" || cell->type == "$le" || cell->type == "$ge" || cell->type == "$gt" ||
- cell->type == "$neg" || cell->type == "$add" || cell->type == "$sub" ||
- cell->type == "$mul" || cell->type == "$div" || cell->type == "$mod" || cell->type == "$pow")
+ if (cell->type.in(ID($reduce_xor), ID($reduce_xnor), ID($shift), ID($shiftx), ID($shl), ID($shr), ID($sshl), ID($sshr),
+ ID($lt), ID($le), ID($ge), ID($gt), ID($neg), ID($add), ID($sub), ID($mul), ID($div), ID($mod), ID($pow)))
{
- RTLIL::SigSpec sig_a = assign_map(cell->getPort("\\A"));
- RTLIL::SigSpec sig_b = cell->hasPort("\\B") ? assign_map(cell->getPort("\\B")) : RTLIL::SigSpec();
+ RTLIL::SigSpec sig_a = assign_map(cell->getPort(ID::A));
+ RTLIL::SigSpec sig_b = cell->hasPort(ID::B) ? assign_map(cell->getPort(ID::B)) : RTLIL::SigSpec();
- if (cell->type == "$shl" || cell->type == "$shr" || cell->type == "$sshl" || cell->type == "$sshr" || cell->type == "$shift" || cell->type == "$shiftx")
+ if (cell->type.in(ID($shl), ID($shr), ID($sshl), ID($sshr), ID($shift), ID($shiftx)))
sig_a = RTLIL::SigSpec();
for (auto &bit : sig_a.to_sigbit_vector())
@@ -599,45 +737,66 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
found_the_x_bit:
cover_list("opt.opt_expr.xbit", "$reduce_xor", "$reduce_xnor", "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx",
"$lt", "$le", "$ge", "$gt", "$neg", "$add", "$sub", "$mul", "$div", "$mod", "$pow", cell->type.str());
- if (cell->type == "$reduce_xor" || cell->type == "$reduce_xnor" ||
- cell->type == "$lt" || cell->type == "$le" || cell->type == "$ge" || cell->type == "$gt")
- replace_cell(assign_map, module, cell, "x-bit in input", "\\Y", RTLIL::State::Sx);
+ if (cell->type.in(ID($reduce_xor), ID($reduce_xnor), ID($lt), ID($le), ID($ge), ID($gt)))
+ replace_cell(assign_map, module, cell, "x-bit in input", ID::Y, RTLIL::State::Sx);
else
- replace_cell(assign_map, module, cell, "x-bit in input", "\\Y", RTLIL::SigSpec(RTLIL::State::Sx, cell->getPort("\\Y").size()));
+ replace_cell(assign_map, module, cell, "x-bit in input", ID::Y, RTLIL::SigSpec(RTLIL::State::Sx, GetSize(cell->getPort(ID::Y))));
+ goto next_cell;
+ }
+ }
+
+ if (cell->type.in(ID($shiftx), ID($shift))) {
+ SigSpec sig_a = assign_map(cell->getPort(ID::A));
+ int width;
+ bool trim_x = cell->type == ID($shiftx) || !keepdc;
+ bool trim_0 = cell->type == ID($shift);
+ for (width = GetSize(sig_a); width > 1; width--) {
+ if ((trim_x && sig_a[width-1] == State::Sx) ||
+ (trim_0 && sig_a[width-1] == State::S0))
+ continue;
+ break;
+ }
+
+ if (width < GetSize(sig_a)) {
+ cover_list("opt.opt_expr.trim", "$shiftx", "$shift", cell->type.str());
+ sig_a.remove(width, GetSize(sig_a)-width);
+ cell->setPort(ID::A, sig_a);
+ cell->setParam(ID(A_WIDTH), width);
+ did_something = true;
goto next_cell;
}
}
- if ((cell->type == "$_NOT_" || cell->type == "$not" || cell->type == "$logic_not") && cell->getPort("\\Y").size() == 1 &&
- invert_map.count(assign_map(cell->getPort("\\A"))) != 0) {
+ if (cell->type.in(ID($_NOT_), ID($not), ID($logic_not)) && GetSize(cell->getPort(ID::Y)) == 1 &&
+ invert_map.count(assign_map(cell->getPort(ID::A))) != 0) {
cover_list("opt.opt_expr.invert.double", "$_NOT_", "$not", "$logic_not", cell->type.str());
- replace_cell(assign_map, module, cell, "double_invert", "\\Y", invert_map.at(assign_map(cell->getPort("\\A"))));
+ replace_cell(assign_map, module, cell, "double_invert", ID::Y, invert_map.at(assign_map(cell->getPort(ID::A))));
goto next_cell;
}
- if ((cell->type == "$_MUX_" || cell->type == "$mux") && invert_map.count(assign_map(cell->getPort("\\S"))) != 0) {
+ if (cell->type.in(ID($_MUX_), ID($mux)) && invert_map.count(assign_map(cell->getPort(ID(S)))) != 0) {
cover_list("opt.opt_expr.invert.muxsel", "$_MUX_", "$mux", cell->type.str());
- log("Optimizing away select inverter for %s cell `%s' in module `%s'.\n", log_id(cell->type), log_id(cell), log_id(module));
- RTLIL::SigSpec tmp = cell->getPort("\\A");
- cell->setPort("\\A", cell->getPort("\\B"));
- cell->setPort("\\B", tmp);
- cell->setPort("\\S", invert_map.at(assign_map(cell->getPort("\\S"))));
+ log_debug("Optimizing away select inverter for %s cell `%s' in module `%s'.\n", log_id(cell->type), log_id(cell), log_id(module));
+ RTLIL::SigSpec tmp = cell->getPort(ID::A);
+ cell->setPort(ID::A, cell->getPort(ID::B));
+ cell->setPort(ID::B, tmp);
+ cell->setPort(ID(S), invert_map.at(assign_map(cell->getPort(ID(S)))));
did_something = true;
goto next_cell;
}
- if (cell->type == "$_NOT_") {
- RTLIL::SigSpec input = cell->getPort("\\A");
+ if (cell->type == ID($_NOT_)) {
+ RTLIL::SigSpec input = cell->getPort(ID::A);
assign_map.apply(input);
if (input.match("1")) ACTION_DO_Y(0);
if (input.match("0")) ACTION_DO_Y(1);
if (input.match("*")) ACTION_DO_Y(x);
}
- if (cell->type == "$_AND_") {
+ if (cell->type == ID($_AND_)) {
RTLIL::SigSpec input;
- input.append(cell->getPort("\\B"));
- input.append(cell->getPort("\\A"));
+ input.append(cell->getPort(ID::B));
+ input.append(cell->getPort(ID::A));
assign_map.apply(input);
if (input.match(" 0")) ACTION_DO_Y(0);
if (input.match("0 ")) ACTION_DO_Y(0);
@@ -649,14 +808,14 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
if (input.match(" *")) ACTION_DO_Y(0);
if (input.match("* ")) ACTION_DO_Y(0);
}
- if (input.match(" 1")) ACTION_DO("\\Y", input.extract(1, 1));
- if (input.match("1 ")) ACTION_DO("\\Y", input.extract(0, 1));
+ if (input.match(" 1")) ACTION_DO(ID::Y, input.extract(1, 1));
+ if (input.match("1 ")) ACTION_DO(ID::Y, input.extract(0, 1));
}
- if (cell->type == "$_OR_") {
+ if (cell->type == ID($_OR_)) {
RTLIL::SigSpec input;
- input.append(cell->getPort("\\B"));
- input.append(cell->getPort("\\A"));
+ input.append(cell->getPort(ID::B));
+ input.append(cell->getPort(ID::A));
assign_map.apply(input);
if (input.match(" 1")) ACTION_DO_Y(1);
if (input.match("1 ")) ACTION_DO_Y(1);
@@ -668,14 +827,14 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
if (input.match(" *")) ACTION_DO_Y(1);
if (input.match("* ")) ACTION_DO_Y(1);
}
- if (input.match(" 0")) ACTION_DO("\\Y", input.extract(1, 1));
- if (input.match("0 ")) ACTION_DO("\\Y", input.extract(0, 1));
+ if (input.match(" 0")) ACTION_DO(ID::Y, input.extract(1, 1));
+ if (input.match("0 ")) ACTION_DO(ID::Y, input.extract(0, 1));
}
- if (cell->type == "$_XOR_") {
+ if (cell->type == ID($_XOR_)) {
RTLIL::SigSpec input;
- input.append(cell->getPort("\\B"));
- input.append(cell->getPort("\\A"));
+ input.append(cell->getPort(ID::B));
+ input.append(cell->getPort(ID::A));
assign_map.apply(input);
if (input.match("00")) ACTION_DO_Y(0);
if (input.match("01")) ACTION_DO_Y(1);
@@ -683,27 +842,27 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
if (input.match("11")) ACTION_DO_Y(0);
if (input.match(" *")) ACTION_DO_Y(x);
if (input.match("* ")) ACTION_DO_Y(x);
- if (input.match(" 0")) ACTION_DO("\\Y", input.extract(1, 1));
- if (input.match("0 ")) ACTION_DO("\\Y", input.extract(0, 1));
+ if (input.match(" 0")) ACTION_DO(ID::Y, input.extract(1, 1));
+ if (input.match("0 ")) ACTION_DO(ID::Y, input.extract(0, 1));
}
- if (cell->type == "$_MUX_") {
+ if (cell->type == ID($_MUX_)) {
RTLIL::SigSpec input;
- input.append(cell->getPort("\\S"));
- input.append(cell->getPort("\\B"));
- input.append(cell->getPort("\\A"));
+ input.append(cell->getPort(ID(S)));
+ input.append(cell->getPort(ID::B));
+ input.append(cell->getPort(ID::A));
assign_map.apply(input);
if (input.extract(2, 1) == input.extract(1, 1))
- ACTION_DO("\\Y", input.extract(2, 1));
- if (input.match(" 0")) ACTION_DO("\\Y", input.extract(2, 1));
- if (input.match(" 1")) ACTION_DO("\\Y", input.extract(1, 1));
- if (input.match("01 ")) ACTION_DO("\\Y", input.extract(0, 1));
+ ACTION_DO(ID::Y, input.extract(2, 1));
+ if (input.match(" 0")) ACTION_DO(ID::Y, input.extract(2, 1));
+ if (input.match(" 1")) ACTION_DO(ID::Y, input.extract(1, 1));
+ if (input.match("01 ")) ACTION_DO(ID::Y, input.extract(0, 1));
if (input.match("10 ")) {
cover("opt.opt_expr.mux_to_inv");
- cell->type = "$_NOT_";
- cell->setPort("\\A", input.extract(0, 1));
- cell->unsetPort("\\B");
- cell->unsetPort("\\S");
+ cell->type = ID($_NOT_);
+ cell->setPort(ID::A, input.extract(0, 1));
+ cell->unsetPort(ID::B);
+ cell->unsetPort(ID(S));
goto next_cell;
}
if (input.match("11 ")) ACTION_DO_Y(1);
@@ -712,21 +871,38 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
if (input.match("01*")) ACTION_DO_Y(x);
if (input.match("10*")) ACTION_DO_Y(x);
if (mux_undef) {
- if (input.match("* ")) ACTION_DO("\\Y", input.extract(1, 1));
- if (input.match(" * ")) ACTION_DO("\\Y", input.extract(2, 1));
- if (input.match(" *")) ACTION_DO("\\Y", input.extract(2, 1));
+ if (input.match("* ")) ACTION_DO(ID::Y, input.extract(1, 1));
+ if (input.match(" * ")) ACTION_DO(ID::Y, input.extract(2, 1));
+ if (input.match(" *")) ACTION_DO(ID::Y, input.extract(2, 1));
+ }
+ }
+
+ if (cell->type.in(ID($_TBUF_), ID($tribuf))) {
+ RTLIL::SigSpec input = cell->getPort(cell->type == ID($_TBUF_) ? ID(E) : ID(EN));
+ RTLIL::SigSpec a = cell->getPort(ID::A);
+ assign_map.apply(input);
+ assign_map.apply(a);
+ if (input == State::S1)
+ ACTION_DO(ID::Y, cell->getPort(ID::A));
+ if (input == State::S0 && !a.is_fully_undef()) {
+ cover("opt.opt_expr.action_" S__LINE__);
+ log_debug("Replacing data input of %s cell `%s' in module `%s' with constant undef.\n",
+ cell->type.c_str(), cell->name.c_str(), module->name.c_str());
+ cell->setPort(ID::A, SigSpec(State::Sx, GetSize(a)));
+ did_something = true;
+ goto next_cell;
}
}
- if (cell->type == "$eq" || cell->type == "$ne" || cell->type == "$eqx" || cell->type == "$nex")
+ if (cell->type.in(ID($eq), ID($ne), ID($eqx), ID($nex)))
{
- RTLIL::SigSpec a = cell->getPort("\\A");
- RTLIL::SigSpec b = cell->getPort("\\B");
+ RTLIL::SigSpec a = cell->getPort(ID::A);
+ RTLIL::SigSpec b = cell->getPort(ID::B);
- if (cell->parameters["\\A_WIDTH"].as_int() != cell->parameters["\\B_WIDTH"].as_int()) {
- int width = max(cell->parameters["\\A_WIDTH"].as_int(), cell->parameters["\\B_WIDTH"].as_int());
- a.extend_u0(width, cell->parameters["\\A_SIGNED"].as_bool() && cell->parameters["\\B_SIGNED"].as_bool());
- b.extend_u0(width, cell->parameters["\\A_SIGNED"].as_bool() && cell->parameters["\\B_SIGNED"].as_bool());
+ if (cell->parameters[ID(A_WIDTH)].as_int() != cell->parameters[ID(B_WIDTH)].as_int()) {
+ int width = max(cell->parameters[ID(A_WIDTH)].as_int(), cell->parameters[ID(B_WIDTH)].as_int());
+ a.extend_u0(width, cell->parameters[ID(A_SIGNED)].as_bool() && cell->parameters[ID(B_SIGNED)].as_bool());
+ b.extend_u0(width, cell->parameters[ID(A_SIGNED)].as_bool() && cell->parameters[ID(B_SIGNED)].as_bool());
}
RTLIL::SigSpec new_a, new_b;
@@ -735,9 +911,9 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
for (int i = 0; i < GetSize(a); i++) {
if (a[i].wire == NULL && b[i].wire == NULL && a[i] != b[i] && a[i].data <= RTLIL::State::S1 && b[i].data <= RTLIL::State::S1) {
cover_list("opt.opt_expr.eqneq.isneq", "$eq", "$ne", "$eqx", "$nex", cell->type.str());
- RTLIL::SigSpec new_y = RTLIL::SigSpec((cell->type == "$eq" || cell->type == "$eqx") ? RTLIL::State::S0 : RTLIL::State::S1);
- new_y.extend_u0(cell->parameters["\\Y_WIDTH"].as_int(), false);
- replace_cell(assign_map, module, cell, "isneq", "\\Y", new_y);
+ RTLIL::SigSpec new_y = RTLIL::SigSpec(cell->type.in(ID($eq), ID($eqx)) ? RTLIL::State::S0 : RTLIL::State::S1);
+ new_y.extend_u0(cell->parameters[ID(Y_WIDTH)].as_int(), false);
+ replace_cell(assign_map, module, cell, "isneq", ID::Y, new_y);
goto next_cell;
}
if (a[i] == b[i])
@@ -748,83 +924,87 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
if (new_a.size() == 0) {
cover_list("opt.opt_expr.eqneq.empty", "$eq", "$ne", "$eqx", "$nex", cell->type.str());
- RTLIL::SigSpec new_y = RTLIL::SigSpec((cell->type == "$eq" || cell->type == "$eqx") ? RTLIL::State::S1 : RTLIL::State::S0);
- new_y.extend_u0(cell->parameters["\\Y_WIDTH"].as_int(), false);
- replace_cell(assign_map, module, cell, "empty", "\\Y", new_y);
+ RTLIL::SigSpec new_y = RTLIL::SigSpec(cell->type.in(ID($eq), ID($eqx)) ? RTLIL::State::S1 : RTLIL::State::S0);
+ new_y.extend_u0(cell->parameters[ID(Y_WIDTH)].as_int(), false);
+ replace_cell(assign_map, module, cell, "empty", ID::Y, new_y);
goto next_cell;
}
if (new_a.size() < a.size() || new_b.size() < b.size()) {
cover_list("opt.opt_expr.eqneq.resize", "$eq", "$ne", "$eqx", "$nex", cell->type.str());
- cell->setPort("\\A", new_a);
- cell->setPort("\\B", new_b);
- cell->parameters["\\A_WIDTH"] = new_a.size();
- cell->parameters["\\B_WIDTH"] = new_b.size();
+ cell->setPort(ID::A, new_a);
+ cell->setPort(ID::B, new_b);
+ cell->parameters[ID(A_WIDTH)] = new_a.size();
+ cell->parameters[ID(B_WIDTH)] = new_b.size();
}
}
- if ((cell->type == "$eq" || cell->type == "$ne") && cell->parameters["\\Y_WIDTH"].as_int() == 1 &&
- cell->parameters["\\A_WIDTH"].as_int() == 1 && cell->parameters["\\B_WIDTH"].as_int() == 1)
+ if (cell->type.in(ID($eq), ID($ne)) && cell->parameters[ID(Y_WIDTH)].as_int() == 1 &&
+ cell->parameters[ID(A_WIDTH)].as_int() == 1 && cell->parameters[ID(B_WIDTH)].as_int() == 1)
{
- RTLIL::SigSpec a = assign_map(cell->getPort("\\A"));
- RTLIL::SigSpec b = assign_map(cell->getPort("\\B"));
+ RTLIL::SigSpec a = assign_map(cell->getPort(ID::A));
+ RTLIL::SigSpec b = assign_map(cell->getPort(ID::B));
if (a.is_fully_const() && !b.is_fully_const()) {
cover_list("opt.opt_expr.eqneq.swapconst", "$eq", "$ne", cell->type.str());
- cell->setPort("\\A", b);
- cell->setPort("\\B", a);
+ cell->setPort(ID::A, b);
+ cell->setPort(ID::B, a);
std::swap(a, b);
}
if (b.is_fully_const()) {
- if (b.as_bool() == (cell->type == "$eq")) {
+ if (b.is_fully_undef()) {
+ RTLIL::SigSpec input = b;
+ ACTION_DO(ID::Y, Const(State::Sx, GetSize(cell->getPort(ID::Y))));
+ } else
+ if (b.as_bool() == (cell->type == ID($eq))) {
RTLIL::SigSpec input = b;
- ACTION_DO("\\Y", cell->getPort("\\A"));
+ ACTION_DO(ID::Y, cell->getPort(ID::A));
} else {
cover_list("opt.opt_expr.eqneq.isnot", "$eq", "$ne", cell->type.str());
- log("Replacing %s cell `%s' in module `%s' with inverter.\n", log_id(cell->type), log_id(cell), log_id(module));
- cell->type = "$not";
- cell->parameters.erase("\\B_WIDTH");
- cell->parameters.erase("\\B_SIGNED");
- cell->unsetPort("\\B");
+ log_debug("Replacing %s cell `%s' in module `%s' with inverter.\n", log_id(cell->type), log_id(cell), log_id(module));
+ cell->type = ID($not);
+ cell->parameters.erase(ID(B_WIDTH));
+ cell->parameters.erase(ID(B_SIGNED));
+ cell->unsetPort(ID::B);
did_something = true;
}
goto next_cell;
}
}
- if ((cell->type == "$eq" || cell->type == "$ne") &&
- (assign_map(cell->getPort("\\A")).is_fully_zero() || assign_map(cell->getPort("\\B")).is_fully_zero()))
+ if (cell->type.in(ID($eq), ID($ne)) &&
+ (assign_map(cell->getPort(ID::A)).is_fully_zero() || assign_map(cell->getPort(ID::B)).is_fully_zero()))
{
cover_list("opt.opt_expr.eqneq.cmpzero", "$eq", "$ne", cell->type.str());
- log("Replacing %s cell `%s' in module `%s' with %s.\n", log_id(cell->type), log_id(cell),
- log_id(module), "$eq" ? "$logic_not" : "$reduce_bool");
- cell->type = cell->type == "$eq" ? "$logic_not" : "$reduce_bool";
- if (assign_map(cell->getPort("\\A")).is_fully_zero()) {
- cell->setPort("\\A", cell->getPort("\\B"));
- cell->setParam("\\A_SIGNED", cell->getParam("\\B_SIGNED"));
- cell->setParam("\\A_WIDTH", cell->getParam("\\B_WIDTH"));
+ log_debug("Replacing %s cell `%s' in module `%s' with %s.\n", log_id(cell->type), log_id(cell),
+ log_id(module), cell->type == ID($eq) ? "$logic_not" : "$reduce_bool");
+ cell->type = cell->type == ID($eq) ? ID($logic_not) : ID($reduce_bool);
+ if (assign_map(cell->getPort(ID::A)).is_fully_zero()) {
+ cell->setPort(ID::A, cell->getPort(ID::B));
+ cell->setParam(ID(A_SIGNED), cell->getParam(ID(B_SIGNED)));
+ cell->setParam(ID(A_WIDTH), cell->getParam(ID(B_WIDTH)));
}
- cell->unsetPort("\\B");
- cell->unsetParam("\\B_SIGNED");
- cell->unsetParam("\\B_WIDTH");
+ cell->unsetPort(ID::B);
+ cell->unsetParam(ID(B_SIGNED));
+ cell->unsetParam(ID(B_WIDTH));
did_something = true;
goto next_cell;
}
- if (cell->type.in("$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx") && assign_map(cell->getPort("\\B")).is_fully_const())
+ if (cell->type.in(ID($shl), ID($shr), ID($sshl), ID($sshr), ID($shift), ID($shiftx)) && assign_map(cell->getPort(ID::B)).is_fully_const())
{
- bool sign_ext = cell->type == "$sshr" && cell->getParam("\\A_SIGNED").as_bool();
- int shift_bits = assign_map(cell->getPort("\\B")).as_int(cell->type.in("$shift", "$shiftx") && cell->getParam("\\B_SIGNED").as_bool());
+ bool sign_ext = cell->type == ID($sshr) && cell->getParam(ID(A_SIGNED)).as_bool();
+ int shift_bits = assign_map(cell->getPort(ID::B)).as_int(cell->type.in(ID($shift), ID($shiftx)) && cell->getParam(ID(B_SIGNED)).as_bool());
- if (cell->type.in("$shl", "$sshl"))
+ if (cell->type.in(ID($shl), ID($sshl)))
shift_bits *= -1;
- RTLIL::SigSpec sig_a = assign_map(cell->getPort("\\A"));
- RTLIL::SigSpec sig_y(cell->type == "$shiftx" ? RTLIL::State::Sx : RTLIL::State::S0, cell->getParam("\\Y_WIDTH").as_int());
+ RTLIL::SigSpec sig_a = assign_map(cell->getPort(ID::A));
+ RTLIL::SigSpec sig_y(cell->type == ID($shiftx) ? RTLIL::State::Sx : RTLIL::State::S0, cell->getParam(ID(Y_WIDTH)).as_int());
if (GetSize(sig_a) < GetSize(sig_y))
- sig_a.extend_u0(GetSize(sig_y), cell->getParam("\\A_SIGNED").as_bool());
+ sig_a.extend_u0(GetSize(sig_y), cell->getParam(ID(A_SIGNED)).as_bool());
for (int i = 0; i < GetSize(sig_y); i++) {
int idx = i + shift_bits;
@@ -836,10 +1016,10 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
cover_list("opt.opt_expr.constshift", "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx", cell->type.str());
- log("Replacing %s cell `%s' (B=%s, SHR=%d) in module `%s' with fixed wiring: %s\n",
- log_id(cell->type), log_id(cell), log_signal(assign_map(cell->getPort("\\B"))), shift_bits, log_id(module), log_signal(sig_y));
+ log_debug("Replacing %s cell `%s' (B=%s, SHR=%d) in module `%s' with fixed wiring: %s\n",
+ log_id(cell->type), log_id(cell), log_signal(assign_map(cell->getPort(ID::B))), shift_bits, log_id(module), log_signal(sig_y));
- module->connect(cell->getPort("\\Y"), sig_y);
+ module->connect(cell->getPort(ID::Y), sig_y);
module->remove(cell);
did_something = true;
@@ -852,41 +1032,41 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
bool identity_wrt_b = false;
bool arith_inverse = false;
- if (cell->type == "$add" || cell->type == "$sub" || cell->type == "$or" || cell->type == "$xor")
+ if (cell->type.in(ID($add), ID($sub), ID($or), ID($xor)))
{
- RTLIL::SigSpec a = assign_map(cell->getPort("\\A"));
- RTLIL::SigSpec b = assign_map(cell->getPort("\\B"));
+ RTLIL::SigSpec a = assign_map(cell->getPort(ID::A));
+ RTLIL::SigSpec b = assign_map(cell->getPort(ID::B));
- if (cell->type != "$sub" && a.is_fully_const() && a.as_bool() == false)
+ if (cell->type != ID($sub) && a.is_fully_const() && a.as_bool() == false)
identity_wrt_b = true;
if (b.is_fully_const() && b.as_bool() == false)
identity_wrt_a = true;
}
- if (cell->type == "$shl" || cell->type == "$shr" || cell->type == "$sshl" || cell->type == "$sshr" || cell->type == "$shift" || cell->type == "$shiftx")
+ if (cell->type.in(ID($shl), ID($shr), ID($sshl), ID($sshr), ID($shift), ID($shiftx)))
{
- RTLIL::SigSpec b = assign_map(cell->getPort("\\B"));
+ RTLIL::SigSpec b = assign_map(cell->getPort(ID::B));
if (b.is_fully_const() && b.as_bool() == false)
identity_wrt_a = true;
}
- if (cell->type == "$mul")
+ if (cell->type == ID($mul))
{
- RTLIL::SigSpec a = assign_map(cell->getPort("\\A"));
- RTLIL::SigSpec b = assign_map(cell->getPort("\\B"));
+ RTLIL::SigSpec a = assign_map(cell->getPort(ID::A));
+ RTLIL::SigSpec b = assign_map(cell->getPort(ID::B));
- if (a.is_fully_const() && is_one_or_minus_one(a.as_const(), cell->getParam("\\A_SIGNED").as_bool(), arith_inverse))
+ if (a.is_fully_const() && is_one_or_minus_one(a.as_const(), cell->getParam(ID(A_SIGNED)).as_bool(), arith_inverse))
identity_wrt_b = true;
else
- if (b.is_fully_const() && is_one_or_minus_one(b.as_const(), cell->getParam("\\B_SIGNED").as_bool(), arith_inverse))
+ if (b.is_fully_const() && is_one_or_minus_one(b.as_const(), cell->getParam(ID(B_SIGNED)).as_bool(), arith_inverse))
identity_wrt_a = true;
}
- if (cell->type == "$div")
+ if (cell->type == ID($div))
{
- RTLIL::SigSpec b = assign_map(cell->getPort("\\B"));
+ RTLIL::SigSpec b = assign_map(cell->getPort(ID::B));
if (b.is_fully_const() && b.size() <= 32 && b.as_int() == 1)
identity_wrt_a = true;
@@ -899,19 +1079,19 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
if (identity_wrt_b)
cover_list("opt.opt_expr.identwrt.b", "$add", "$sub", "$or", "$xor", "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx", "$mul", "$div", cell->type.str());
- log("Replacing %s cell `%s' in module `%s' with identity for port %c.\n",
+ log_debug("Replacing %s cell `%s' in module `%s' with identity for port %c.\n",
cell->type.c_str(), cell->name.c_str(), module->name.c_str(), identity_wrt_a ? 'A' : 'B');
if (!identity_wrt_a) {
- cell->setPort("\\A", cell->getPort("\\B"));
- cell->parameters.at("\\A_WIDTH") = cell->parameters.at("\\B_WIDTH");
- cell->parameters.at("\\A_SIGNED") = cell->parameters.at("\\B_SIGNED");
+ cell->setPort(ID::A, cell->getPort(ID::B));
+ cell->parameters.at(ID(A_WIDTH)) = cell->parameters.at(ID(B_WIDTH));
+ cell->parameters.at(ID(A_SIGNED)) = cell->parameters.at(ID(B_SIGNED));
}
- cell->type = arith_inverse ? "$neg" : "$pos";
- cell->unsetPort("\\B");
- cell->parameters.erase("\\B_WIDTH");
- cell->parameters.erase("\\B_SIGNED");
+ cell->type = arith_inverse ? ID($neg) : ID($pos);
+ cell->unsetPort(ID::B);
+ cell->parameters.erase(ID(B_WIDTH));
+ cell->parameters.erase(ID(B_SIGNED));
cell->check();
did_something = true;
@@ -919,91 +1099,91 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
}
}
- if (mux_bool && (cell->type == "$mux" || cell->type == "$_MUX_") &&
- cell->getPort("\\A") == RTLIL::SigSpec(0, 1) && cell->getPort("\\B") == RTLIL::SigSpec(1, 1)) {
+ if (mux_bool && cell->type.in(ID($mux), ID($_MUX_)) &&
+ cell->getPort(ID::A) == State::S0 && cell->getPort(ID::B) == State::S1) {
cover_list("opt.opt_expr.mux_bool", "$mux", "$_MUX_", cell->type.str());
- replace_cell(assign_map, module, cell, "mux_bool", "\\Y", cell->getPort("\\S"));
+ replace_cell(assign_map, module, cell, "mux_bool", ID::Y, cell->getPort(ID(S)));
goto next_cell;
}
- if (mux_bool && (cell->type == "$mux" || cell->type == "$_MUX_") &&
- cell->getPort("\\A") == RTLIL::SigSpec(1, 1) && cell->getPort("\\B") == RTLIL::SigSpec(0, 1)) {
+ if (mux_bool && cell->type.in(ID($mux), ID($_MUX_)) &&
+ cell->getPort(ID::A) == State::S1 && cell->getPort(ID::B) == State::S0) {
cover_list("opt.opt_expr.mux_invert", "$mux", "$_MUX_", cell->type.str());
- log("Replacing %s cell `%s' in module `%s' with inverter.\n", log_id(cell->type), log_id(cell), log_id(module));
- cell->setPort("\\A", cell->getPort("\\S"));
- cell->unsetPort("\\B");
- cell->unsetPort("\\S");
- if (cell->type == "$mux") {
- Const width = cell->parameters["\\WIDTH"];
- cell->parameters["\\A_WIDTH"] = width;
- cell->parameters["\\Y_WIDTH"] = width;
- cell->parameters["\\A_SIGNED"] = 0;
- cell->parameters.erase("\\WIDTH");
- cell->type = "$not";
+ log_debug("Replacing %s cell `%s' in module `%s' with inverter.\n", log_id(cell->type), log_id(cell), log_id(module));
+ cell->setPort(ID::A, cell->getPort(ID(S)));
+ cell->unsetPort(ID::B);
+ cell->unsetPort(ID(S));
+ if (cell->type == ID($mux)) {
+ Const width = cell->parameters[ID(WIDTH)];
+ cell->parameters[ID(A_WIDTH)] = width;
+ cell->parameters[ID(Y_WIDTH)] = width;
+ cell->parameters[ID(A_SIGNED)] = 0;
+ cell->parameters.erase(ID(WIDTH));
+ cell->type = ID($not);
} else
- cell->type = "$_NOT_";
+ cell->type = ID($_NOT_);
did_something = true;
goto next_cell;
}
- if (consume_x && mux_bool && (cell->type == "$mux" || cell->type == "$_MUX_") && cell->getPort("\\A") == RTLIL::SigSpec(0, 1)) {
+ if (consume_x && mux_bool && cell->type.in(ID($mux), ID($_MUX_)) && cell->getPort(ID::A) == State::S0) {
cover_list("opt.opt_expr.mux_and", "$mux", "$_MUX_", cell->type.str());
- log("Replacing %s cell `%s' in module `%s' with and-gate.\n", log_id(cell->type), log_id(cell), log_id(module));
- cell->setPort("\\A", cell->getPort("\\S"));
- cell->unsetPort("\\S");
- if (cell->type == "$mux") {
- Const width = cell->parameters["\\WIDTH"];
- cell->parameters["\\A_WIDTH"] = width;
- cell->parameters["\\B_WIDTH"] = width;
- cell->parameters["\\Y_WIDTH"] = width;
- cell->parameters["\\A_SIGNED"] = 0;
- cell->parameters["\\B_SIGNED"] = 0;
- cell->parameters.erase("\\WIDTH");
- cell->type = "$and";
+ log_debug("Replacing %s cell `%s' in module `%s' with and-gate.\n", log_id(cell->type), log_id(cell), log_id(module));
+ cell->setPort(ID::A, cell->getPort(ID(S)));
+ cell->unsetPort(ID(S));
+ if (cell->type == ID($mux)) {
+ Const width = cell->parameters[ID(WIDTH)];
+ cell->parameters[ID(A_WIDTH)] = width;
+ cell->parameters[ID(B_WIDTH)] = width;
+ cell->parameters[ID(Y_WIDTH)] = width;
+ cell->parameters[ID(A_SIGNED)] = 0;
+ cell->parameters[ID(B_SIGNED)] = 0;
+ cell->parameters.erase(ID(WIDTH));
+ cell->type = ID($and);
} else
- cell->type = "$_AND_";
+ cell->type = ID($_AND_);
did_something = true;
goto next_cell;
}
- if (consume_x && mux_bool && (cell->type == "$mux" || cell->type == "$_MUX_") && cell->getPort("\\B") == RTLIL::SigSpec(1, 1)) {
+ if (consume_x && mux_bool && cell->type.in(ID($mux), ID($_MUX_)) && cell->getPort(ID::B) == State::S1) {
cover_list("opt.opt_expr.mux_or", "$mux", "$_MUX_", cell->type.str());
- log("Replacing %s cell `%s' in module `%s' with or-gate.\n", log_id(cell->type), log_id(cell), log_id(module));
- cell->setPort("\\B", cell->getPort("\\S"));
- cell->unsetPort("\\S");
- if (cell->type == "$mux") {
- Const width = cell->parameters["\\WIDTH"];
- cell->parameters["\\A_WIDTH"] = width;
- cell->parameters["\\B_WIDTH"] = width;
- cell->parameters["\\Y_WIDTH"] = width;
- cell->parameters["\\A_SIGNED"] = 0;
- cell->parameters["\\B_SIGNED"] = 0;
- cell->parameters.erase("\\WIDTH");
- cell->type = "$or";
+ log_debug("Replacing %s cell `%s' in module `%s' with or-gate.\n", log_id(cell->type), log_id(cell), log_id(module));
+ cell->setPort(ID::B, cell->getPort(ID(S)));
+ cell->unsetPort(ID(S));
+ if (cell->type == ID($mux)) {
+ Const width = cell->parameters[ID(WIDTH)];
+ cell->parameters[ID(A_WIDTH)] = width;
+ cell->parameters[ID(B_WIDTH)] = width;
+ cell->parameters[ID(Y_WIDTH)] = width;
+ cell->parameters[ID(A_SIGNED)] = 0;
+ cell->parameters[ID(B_SIGNED)] = 0;
+ cell->parameters.erase(ID(WIDTH));
+ cell->type = ID($or);
} else
- cell->type = "$_OR_";
+ cell->type = ID($_OR_);
did_something = true;
goto next_cell;
}
- if (mux_undef && (cell->type == "$mux" || cell->type == "$pmux")) {
+ if (mux_undef && cell->type.in(ID($mux), ID($pmux))) {
RTLIL::SigSpec new_a, new_b, new_s;
- int width = cell->getPort("\\A").size();
- if ((cell->getPort("\\A").is_fully_undef() && cell->getPort("\\B").is_fully_undef()) ||
- cell->getPort("\\S").is_fully_undef()) {
+ int width = GetSize(cell->getPort(ID::A));
+ if ((cell->getPort(ID::A).is_fully_undef() && cell->getPort(ID::B).is_fully_undef()) ||
+ cell->getPort(ID(S)).is_fully_undef()) {
cover_list("opt.opt_expr.mux_undef", "$mux", "$pmux", cell->type.str());
- replace_cell(assign_map, module, cell, "mux_undef", "\\Y", cell->getPort("\\A"));
+ replace_cell(assign_map, module, cell, "mux_undef", ID::Y, cell->getPort(ID::A));
goto next_cell;
}
- for (int i = 0; i < cell->getPort("\\S").size(); i++) {
- RTLIL::SigSpec old_b = cell->getPort("\\B").extract(i*width, width);
- RTLIL::SigSpec old_s = cell->getPort("\\S").extract(i, 1);
+ for (int i = 0; i < cell->getPort(ID(S)).size(); i++) {
+ RTLIL::SigSpec old_b = cell->getPort(ID::B).extract(i*width, width);
+ RTLIL::SigSpec old_s = cell->getPort(ID(S)).extract(i, 1);
if (old_b.is_fully_undef() || old_s.is_fully_undef())
continue;
new_b.append(old_b);
new_s.append(old_s);
}
- new_a = cell->getPort("\\A");
+ new_a = cell->getPort(ID::A);
if (new_a.is_fully_undef() && new_s.size() > 0) {
new_a = new_b.extract((new_s.size()-1)*width, width);
new_b = new_b.extract(0, (new_s.size()-1)*width);
@@ -1011,27 +1191,27 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
}
if (new_s.size() == 0) {
cover_list("opt.opt_expr.mux_empty", "$mux", "$pmux", cell->type.str());
- replace_cell(assign_map, module, cell, "mux_empty", "\\Y", new_a);
+ replace_cell(assign_map, module, cell, "mux_empty", ID::Y, new_a);
goto next_cell;
}
if (new_a == RTLIL::SigSpec(RTLIL::State::S0) && new_b == RTLIL::SigSpec(RTLIL::State::S1)) {
cover_list("opt.opt_expr.mux_sel01", "$mux", "$pmux", cell->type.str());
- replace_cell(assign_map, module, cell, "mux_sel01", "\\Y", new_s);
+ replace_cell(assign_map, module, cell, "mux_sel01", ID::Y, new_s);
goto next_cell;
}
- if (cell->getPort("\\S").size() != new_s.size()) {
+ if (cell->getPort(ID(S)).size() != new_s.size()) {
cover_list("opt.opt_expr.mux_reduce", "$mux", "$pmux", cell->type.str());
- log("Optimized away %d select inputs of %s cell `%s' in module `%s'.\n",
- GetSize(cell->getPort("\\S")) - GetSize(new_s), log_id(cell->type), log_id(cell), log_id(module));
- cell->setPort("\\A", new_a);
- cell->setPort("\\B", new_b);
- cell->setPort("\\S", new_s);
+ log_debug("Optimized away %d select inputs of %s cell `%s' in module `%s'.\n",
+ GetSize(cell->getPort(ID(S))) - GetSize(new_s), log_id(cell->type), log_id(cell), log_id(module));
+ cell->setPort(ID::A, new_a);
+ cell->setPort(ID::B, new_b);
+ cell->setPort(ID(S), new_s);
if (new_s.size() > 1) {
- cell->type = "$pmux";
- cell->parameters["\\S_WIDTH"] = new_s.size();
+ cell->type = ID($pmux);
+ cell->parameters[ID(S_WIDTH)] = new_s.size();
} else {
- cell->type = "$mux";
- cell->parameters.erase("\\S_WIDTH");
+ cell->type = ID($mux);
+ cell->parameters.erase(ID(S_WIDTH));
}
did_something = true;
}
@@ -1039,30 +1219,30 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
#define FOLD_1ARG_CELL(_t) \
if (cell->type == "$" #_t) { \
- RTLIL::SigSpec a = cell->getPort("\\A"); \
+ RTLIL::SigSpec a = cell->getPort(ID::A); \
assign_map.apply(a); \
if (a.is_fully_const()) { \
RTLIL::Const dummy_arg(RTLIL::State::S0, 1); \
RTLIL::SigSpec y(RTLIL::const_ ## _t(a.as_const(), dummy_arg, \
- cell->parameters["\\A_SIGNED"].as_bool(), false, \
- cell->parameters["\\Y_WIDTH"].as_int())); \
+ cell->parameters[ID(A_SIGNED)].as_bool(), false, \
+ cell->parameters[ID(Y_WIDTH)].as_int())); \
cover("opt.opt_expr.const.$" #_t); \
- replace_cell(assign_map, module, cell, stringf("%s", log_signal(a)), "\\Y", y); \
+ replace_cell(assign_map, module, cell, stringf("%s", log_signal(a)), ID::Y, y); \
goto next_cell; \
} \
}
#define FOLD_2ARG_CELL(_t) \
if (cell->type == "$" #_t) { \
- RTLIL::SigSpec a = cell->getPort("\\A"); \
- RTLIL::SigSpec b = cell->getPort("\\B"); \
+ RTLIL::SigSpec a = cell->getPort(ID::A); \
+ RTLIL::SigSpec b = cell->getPort(ID::B); \
assign_map.apply(a), assign_map.apply(b); \
if (a.is_fully_const() && b.is_fully_const()) { \
RTLIL::SigSpec y(RTLIL::const_ ## _t(a.as_const(), b.as_const(), \
- cell->parameters["\\A_SIGNED"].as_bool(), \
- cell->parameters["\\B_SIGNED"].as_bool(), \
- cell->parameters["\\Y_WIDTH"].as_int())); \
+ cell->parameters[ID(A_SIGNED)].as_bool(), \
+ cell->parameters[ID(B_SIGNED)].as_bool(), \
+ cell->parameters[ID(Y_WIDTH)].as_int())); \
cover("opt.opt_expr.const.$" #_t); \
- replace_cell(assign_map, module, cell, stringf("%s, %s", log_signal(a), log_signal(b)), "\\Y", y); \
+ replace_cell(assign_map, module, cell, stringf("%s, %s", log_signal(a), log_signal(b)), ID::Y, y); \
goto next_cell; \
} \
}
@@ -1108,25 +1288,25 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
FOLD_1ARG_CELL(neg)
// be very conservative with optimizing $mux cells as we do not want to break mux trees
- if (cell->type == "$mux") {
- RTLIL::SigSpec input = assign_map(cell->getPort("\\S"));
- RTLIL::SigSpec inA = assign_map(cell->getPort("\\A"));
- RTLIL::SigSpec inB = assign_map(cell->getPort("\\B"));
+ if (cell->type == ID($mux)) {
+ RTLIL::SigSpec input = assign_map(cell->getPort(ID(S)));
+ RTLIL::SigSpec inA = assign_map(cell->getPort(ID::A));
+ RTLIL::SigSpec inB = assign_map(cell->getPort(ID::B));
if (input.is_fully_const())
- ACTION_DO("\\Y", input.as_bool() ? cell->getPort("\\B") : cell->getPort("\\A"));
+ ACTION_DO(ID::Y, input.as_bool() ? cell->getPort(ID::B) : cell->getPort(ID::A));
else if (inA == inB)
- ACTION_DO("\\Y", cell->getPort("\\A"));
+ ACTION_DO(ID::Y, cell->getPort(ID::A));
}
- if (!keepdc && cell->type == "$mul")
+ if (!keepdc && cell->type == ID($mul))
{
- bool a_signed = cell->parameters["\\A_SIGNED"].as_bool();
- bool b_signed = cell->parameters["\\B_SIGNED"].as_bool();
+ bool a_signed = cell->parameters[ID(A_SIGNED)].as_bool();
+ bool b_signed = cell->parameters[ID(B_SIGNED)].as_bool();
bool swapped_ab = false;
- RTLIL::SigSpec sig_a = assign_map(cell->getPort("\\A"));
- RTLIL::SigSpec sig_b = assign_map(cell->getPort("\\B"));
- RTLIL::SigSpec sig_y = assign_map(cell->getPort("\\Y"));
+ RTLIL::SigSpec sig_a = assign_map(cell->getPort(ID::A));
+ RTLIL::SigSpec sig_b = assign_map(cell->getPort(ID::B));
+ RTLIL::SigSpec sig_y = assign_map(cell->getPort(ID::Y));
if (sig_b.is_fully_const() && sig_b.size() <= 32)
std::swap(sig_a, sig_b), std::swap(a_signed, b_signed), swapped_ab = true;
@@ -1139,7 +1319,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
{
cover("opt.opt_expr.mul_shift.zero");
- log("Replacing multiply-by-zero cell `%s' in module `%s' with zero-driver.\n",
+ log_debug("Replacing multiply-by-zero cell `%s' in module `%s' with zero-driver.\n",
cell->name.c_str(), module->name.c_str());
module->connect(RTLIL::SigSig(sig_y, RTLIL::SigSpec(0, sig_y.size())));
@@ -1157,13 +1337,13 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
else
cover("opt.opt_expr.mul_shift.unswapped");
- log("Replacing multiply-by-%d cell `%s' in module `%s' with shift-by-%d.\n",
+ log_debug("Replacing multiply-by-%d cell `%s' in module `%s' with shift-by-%d.\n",
a_val, cell->name.c_str(), module->name.c_str(), i);
if (!swapped_ab) {
- cell->setPort("\\A", cell->getPort("\\B"));
- cell->parameters.at("\\A_WIDTH") = cell->parameters.at("\\B_WIDTH");
- cell->parameters.at("\\A_SIGNED") = cell->parameters.at("\\B_SIGNED");
+ cell->setPort(ID::A, cell->getPort(ID::B));
+ cell->parameters.at(ID(A_WIDTH)) = cell->parameters.at(ID(B_WIDTH));
+ cell->parameters.at(ID(A_SIGNED)) = cell->parameters.at(ID(B_SIGNED));
}
std::vector<RTLIL::SigBit> new_b = RTLIL::SigSpec(i, 6);
@@ -1171,10 +1351,10 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
while (GetSize(new_b) > 1 && new_b.back() == RTLIL::State::S0)
new_b.pop_back();
- cell->type = "$shl";
- cell->parameters["\\B_WIDTH"] = GetSize(new_b);
- cell->parameters["\\B_SIGNED"] = false;
- cell->setPort("\\B", new_b);
+ cell->type = ID($shl);
+ cell->parameters[ID(B_WIDTH)] = GetSize(new_b);
+ cell->parameters[ID(B_SIGNED)] = false;
+ cell->setPort(ID::B, new_b);
cell->check();
did_something = true;
@@ -1183,11 +1363,11 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
}
}
- if (!keepdc && cell->type.in("$div", "$mod"))
+ if (!keepdc && cell->type.in(ID($div), ID($mod)))
{
- bool b_signed = cell->parameters["\\B_SIGNED"].as_bool();
- SigSpec sig_b = assign_map(cell->getPort("\\B"));
- SigSpec sig_y = assign_map(cell->getPort("\\Y"));
+ bool b_signed = cell->parameters[ID(B_SIGNED)].as_bool();
+ SigSpec sig_b = assign_map(cell->getPort(ID::B));
+ SigSpec sig_y = assign_map(cell->getPort(ID::Y));
if (sig_b.is_fully_def() && sig_b.size() <= 32)
{
@@ -1197,7 +1377,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
{
cover("opt.opt_expr.divmod_zero");
- log("Replacing divide-by-zero cell `%s' in module `%s' with undef-driver.\n",
+ log_debug("Replacing divide-by-zero cell `%s' in module `%s' with undef-driver.\n",
cell->name.c_str(), module->name.c_str());
module->connect(RTLIL::SigSig(sig_y, RTLIL::SigSpec(State::Sx, sig_y.size())));
@@ -1210,11 +1390,11 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
for (int i = 1; i < (b_signed ? sig_b.size()-1 : sig_b.size()); i++)
if (b_val == (1 << i))
{
- if (cell->type == "$div")
+ if (cell->type == ID($div))
{
cover("opt.opt_expr.div_shift");
- log("Replacing divide-by-%d cell `%s' in module `%s' with shift-by-%d.\n",
+ log_debug("Replacing divide-by-%d cell `%s' in module `%s' with shift-by-%d.\n",
b_val, cell->name.c_str(), module->name.c_str(), i);
std::vector<RTLIL::SigBit> new_b = RTLIL::SigSpec(i, 6);
@@ -1222,17 +1402,17 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
while (GetSize(new_b) > 1 && new_b.back() == RTLIL::State::S0)
new_b.pop_back();
- cell->type = "$shr";
- cell->parameters["\\B_WIDTH"] = GetSize(new_b);
- cell->parameters["\\B_SIGNED"] = false;
- cell->setPort("\\B", new_b);
+ cell->type = ID($shr);
+ cell->parameters[ID(B_WIDTH)] = GetSize(new_b);
+ cell->parameters[ID(B_SIGNED)] = false;
+ cell->setPort(ID::B, new_b);
cell->check();
}
else
{
cover("opt.opt_expr.mod_mask");
- log("Replacing modulo-by-%d cell `%s' in module `%s' with bitmask.\n",
+ log_debug("Replacing modulo-by-%d cell `%s' in module `%s' with bitmask.\n",
b_val, cell->name.c_str(), module->name.c_str());
std::vector<RTLIL::SigBit> new_b = RTLIL::SigSpec(State::S1, i);
@@ -1240,9 +1420,9 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
if (b_signed)
new_b.push_back(State::S0);
- cell->type = "$and";
- cell->parameters["\\B_WIDTH"] = GetSize(new_b);
- cell->setPort("\\B", new_b);
+ cell->type = ID($and);
+ cell->parameters[ID(B_WIDTH)] = GetSize(new_b);
+ cell->setPort(ID::B, new_b);
cell->check();
}
@@ -1254,7 +1434,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
// remove redundant pairs of bits in ==, ===, !=, and !==
// replace cell with const driver if inputs can't be equal
- if (do_fine && cell->type.in("$eq", "$ne", "$eqx", "$nex"))
+ if (do_fine && cell->type.in(ID($eq), ID($ne), ID($eqx), ID($nex)))
{
pool<pair<SigBit, SigBit>> redundant_cache;
mfp<SigBit> contradiction_cache;
@@ -1262,14 +1442,14 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
contradiction_cache.promote(State::S0);
contradiction_cache.promote(State::S1);
- int a_width = cell->getParam("\\A_WIDTH").as_int();
- int b_width = cell->getParam("\\B_WIDTH").as_int();
+ int a_width = cell->getParam(ID(A_WIDTH)).as_int();
+ int b_width = cell->getParam(ID(B_WIDTH)).as_int();
- bool is_signed = cell->getParam("\\A_SIGNED").as_bool();
+ bool is_signed = cell->getParam(ID(A_SIGNED)).as_bool();
int width = is_signed ? std::min(a_width, b_width) : std::max(a_width, b_width);
- SigSpec sig_a = cell->getPort("\\A");
- SigSpec sig_b = cell->getPort("\\B");
+ SigSpec sig_a = cell->getPort(ID::A);
+ SigSpec sig_b = cell->getPort(ID::B);
int redundant_bits = 0;
@@ -1299,10 +1479,10 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
if (contradiction_cache.find(State::S0) == contradiction_cache.find(State::S1))
{
- SigSpec y_sig = cell->getPort("\\Y");
- Const y_value(cell->type.in("$eq", "$eqx") ? 0 : 1, GetSize(y_sig));
+ SigSpec y_sig = cell->getPort(ID::Y);
+ Const y_value(cell->type.in(ID($eq), ID($eqx)) ? 0 : 1, GetSize(y_sig));
- log("Replacing cell `%s' in module `%s' with constant driver %s.\n",
+ log_debug("Replacing cell `%s' in module `%s' with constant driver %s.\n",
log_id(cell), log_id(module), log_signal(y_value));
module->connect(y_sig, y_value);
@@ -1314,131 +1494,152 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
if (redundant_bits)
{
- log("Removed %d redundant input bits from %s cell `%s' in module `%s'.\n",
+ log_debug("Removed %d redundant input bits from %s cell `%s' in module `%s'.\n",
redundant_bits, log_id(cell->type), log_id(cell), log_id(module));
- cell->setPort("\\A", sig_a);
- cell->setPort("\\B", sig_b);
- cell->setParam("\\A_WIDTH", GetSize(sig_a));
- cell->setParam("\\B_WIDTH", GetSize(sig_b));
+ cell->setPort(ID::A, sig_a);
+ cell->setPort(ID::B, sig_b);
+ cell->setParam(ID(A_WIDTH), GetSize(sig_a));
+ cell->setParam(ID(B_WIDTH), GetSize(sig_b));
did_something = true;
goto next_cell;
}
}
- // replace a<0 or a>=0 with the top bit of a
- if (do_fine && (cell->type == "$lt" || cell->type == "$ge" || cell->type == "$gt" || cell->type == "$le"))
+ // simplify comparisons
+ if (do_fine && cell->type.in(ID($lt), ID($ge), ID($gt), ID($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();
-
- // replace a(signed) < 0 with the high bit of a
- if (sigConst.is_fully_const() && sigConst.is_fully_zero() && var_signed == true)
+ IdString cmp_type = cell->type;
+ SigSpec var_sig = cell->getPort(ID::A);
+ SigSpec const_sig = cell->getPort(ID::B);
+ int var_width = cell->parameters[ID(A_WIDTH)].as_int();
+ int const_width = cell->parameters[ID(B_WIDTH)].as_int();
+ bool is_signed = cell->getParam(ID(A_SIGNED)).as_bool();
+
+ 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 == ID($gt))
+ cmp_type = ID($lt);
+ else if (cmp_type == ID($lt))
+ cmp_type = ID($gt);
+ else if (cmp_type == ID($ge))
+ cmp_type = ID($le);
+ else if (cmp_type == ID($le))
+ cmp_type = ID($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(ID::Y)));
+ bool replace = false;
+ bool remove = false;
+
+ if (!is_signed)
+ { /* unsigned */
+ if (const_sig.is_fully_zero() && cmp_type == ID($lt)) {
+ condition = "unsigned X<0";
+ replacement = "constant 0";
+ replace_sig[0] = State::S0;
+ replace = true;
+ }
+ if (const_sig.is_fully_zero() && cmp_type == ID($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 == ID($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 == ID($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 == ID($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(ID::Y));
+ remove = true;
+ }
+ if (cmp_type == ID($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(ID::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 == ID($lt) || cmp_type == ID($le))
+ {
+ if (cmp_type == ID($lt)) cmp_name = "<";
+ if (cmp_type == ID($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 == ID($gt) || cmp_type == ID($ge))
+ {
+ if (cmp_type == ID($gt)) cmp_name = ">";
+ if (cmp_type == ID($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 == ID($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 == ID($ge))
+ {
+ condition = "signed X>=0";
+ replacement = stringf("X[%d]", var_width - 1);
+ module->addNot(NEW_ID, var_sig[var_width - 1], cell->getPort(ID::Y));
+ remove = true;
}
- module->connect(cell->getPort("\\Y"), a_prime);
+ }
+
+ if (replace || remove)
+ {
+ log_debug("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(ID::Y), replace_sig);
module->remove(cell);
did_something = true;
goto next_cell;
-
}
}
}
@@ -1453,14 +1654,14 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
struct OptExprPass : public Pass {
OptExprPass() : Pass("opt_expr", "perform const folding and simple expression rewriting") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
log(" opt_expr [options] [selection]\n");
log("\n");
log("This pass performs const folding on internal cell types with constant inputs.\n");
- log("It also performs some simple expression rewritring.\n");
+ log("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");
@@ -1487,7 +1688,7 @@ struct OptExprPass : public Pass {
log(" replaced by 'a'. the -keepdc option disables all such optimizations.\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
bool mux_undef = false;
bool mux_bool = false;
@@ -1538,8 +1739,14 @@ struct OptExprPass : public Pass {
for (auto module : design->selected_modules())
{
- if (undriven)
+ log("Optimizing module %s.\n", log_id(module));
+
+ if (undriven) {
+ did_something = false;
replace_undriven(design, module);
+ if (did_something)
+ design->scratchpad_set_bool("opt.did_something", true);
+ }
do {
do {
@@ -1549,7 +1756,11 @@ struct OptExprPass : public Pass {
design->scratchpad_set_bool("opt.did_something", true);
} while (did_something);
replace_const_cells(design, module, true, mux_undef, mux_bool, do_fine, keepdc, clkinv);
+ if (did_something)
+ design->scratchpad_set_bool("opt.did_something", true);
} while (did_something);
+
+ log_suppressed();
}
log_pop();
diff --git a/passes/opt/opt_lut.cc b/passes/opt/opt_lut.cc
new file mode 100644
index 000000000..c4f278706
--- /dev/null
+++ b/passes/opt/opt_lut.cc
@@ -0,0 +1,595 @@
+/*
+ * 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(ID::A));
+ int lut_width = lut->getParam(ID(WIDTH)).as_int();
+ Const lut_table = lut->getParam(ID(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: %8d\n", GetSize(luts));
+ 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 == ID($lut))
+ {
+ if (cell->has_keep_attr())
+ continue;
+ SigBit lut_output = cell->getPort(ID::Y);
+ if (lut_output.wire->get_bool_attribute(ID::keep))
+ continue;
+
+ int lut_width = cell->getParam(ID(WIDTH)).as_int();
+ SigSpec lut_input = cell->getPort(ID::A);
+ int lut_arity = 0;
+
+ log_debug("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_debug(" LUT has illegal connection to %s cell %s.%s.\n", lut_dlogic->type.c_str(), log_id(module), log_id(lut_dlogic));
+ log_debug(" 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_debug(" LUT has illegal connection to %s cell %s.%s.\n", lut_dlogic->type.c_str(), log_id(module), log_id(lut_dlogic));
+ log_debug(" 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_debug(" 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_debug(" 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(ID::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_debug("Found redundant cell %s.%s.\n", log_id(module), log_id(lut));
+
+ SigBit value;
+ if (const0_match)
+ {
+ log_debug(" Cell evaluates constant 0.\n");
+ value = State::S0;
+ }
+ if (const1_match)
+ {
+ log_debug(" Cell evaluates constant 1.\n");
+ value = State::S1;
+ }
+ if (input_match != -1) {
+ log_debug(" Cell evaluates signal %s.\n", log_signal(lut_inputs[input_match]));
+ value = lut_inputs[input_match];
+ }
+
+ if (lut_dlogic_inputs.size())
+ log_debug(" Not eliminating cell (connected to dedicated logic).\n");
+ else
+ {
+ SigSpec lut_output = lut->getPort(ID::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(ID::A));
+ SigSpec lutA_output = sigmap(lutA->getPort(ID::Y)[0]);
+ int lutA_width = lutA->getParam(ID(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(ID::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(ID::A));
+ SigSpec lutB_output = sigmap(lutB->getPort(ID::Y)[0]);
+ int lutB_width = lutB->getParam(ID(WIDTH)).as_int();
+ int lutB_arity = luts_arity[lutB];
+ pool<int> &lutB_dlogic_inputs = luts_dlogic_inputs[lutB];
+
+ log_debug("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(ID::Y)))
+ {
+ log_debug(" 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_debug(" Cell A is a %d-LUT with %d dedicated connections. ", lutA_arity, GetSize(lutA_dlogic_inputs));
+ else
+ log_debug(" Cell A is a %d-LUT. ", lutA_arity);
+ if (lutB_dlogic_inputs.size())
+ log_debug("Cell B is a %d-LUT with %d dedicated connections.\n", lutB_arity, GetSize(lutB_dlogic_inputs));
+ else
+ log_debug("Cell B is a %d-LUT.\n", lutB_arity);
+ log_debug(" Cells share %d input(s) and can be merged into one %d-LUT.\n", GetSize(common_inputs), 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_debug(" Not combining LUTs into cell A (combined LUT wider than cell A).\n");
+ else if (lutB_dlogic_inputs.size() > 0)
+ log_debug(" Not combining LUTs into cell A (cell B is connected to dedicated logic).\n");
+ else if (lutB->get_bool_attribute(ID(lut_keep)))
+ log_debug(" Not combining LUTs into cell A (cell B has attribute \\lut_keep).\n");
+ else
+ combine_mask |= COMBINE_A;
+ if (lutM_arity > lutB_width)
+ log_debug(" Not combining LUTs into cell B (combined LUT wider than cell B).\n");
+ else if (lutA_dlogic_inputs.size() > 0)
+ log_debug(" Not combining LUTs into cell B (cell A is connected to dedicated logic).\n");
+ else if (lutA->get_bool_attribute(ID(lut_keep)))
+ log_debug(" 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_debug(" Can combine into either cell.\n");
+ if (lutA_arity == 1)
+ {
+ log_debug(" Cell A is a buffer or inverter, combining into cell B.\n");
+ combine = COMBINE_B;
+ }
+ else if (lutB_arity == 1)
+ {
+ log_debug(" Cell B is a buffer or inverter, combining into cell A.\n");
+ combine = COMBINE_A;
+ }
+ else
+ {
+ log_debug(" 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_debug(" 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_debug(" 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_debug(" 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(ID(WIDTH)).as_int();
+ SigSpec lutM_input = sigmap(lutM->getPort(ID::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_debug(" 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_debug(" Disconnecting cascade input %d.\n", i);
+ lutM_new_inputs.push_back(SigBit());
+ }
+ else
+ {
+ log_debug(" 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_debug(" Cell A truth table: %s.\n", lutA->getParam(ID(LUT)).as_string().c_str());
+ log_debug(" Cell B truth table: %s.\n", lutB->getParam(ID(LUT)).as_string().c_str());
+ log_debug(" Merged truth table: %s.\n", lutM_new_table.as_string().c_str());
+
+ lutM->setParam(ID(LUT), lutM_new_table);
+ lutM->setPort(ID::A, lutM_new_inputs);
+ lutM->setPort(ID::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_mem.cc b/passes/opt/opt_mem.cc
new file mode 100644
index 000000000..98d3551eb
--- /dev/null
+++ b/passes/opt/opt_mem.cc
@@ -0,0 +1,143 @@
+/*
+ * 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 OptMemWorker
+{
+ RTLIL::Design *design;
+ RTLIL::Module *module;
+ SigMap sigmap;
+ bool restart;
+
+ dict<IdString, vector<IdString>> memrd, memwr, meminit;
+ pool<IdString> remove_mem, remove_cells;
+
+ OptMemWorker(RTLIL::Module *module) : design(module->design), module(module), sigmap(module), restart(false)
+ {
+ for (auto &it : module->memories)
+ {
+ memrd[it.first];
+ memwr[it.first];
+ meminit[it.first];
+ }
+
+ for (auto cell : module->cells())
+ {
+ if (cell->type == ID($memrd)) {
+ IdString id = cell->getParam(ID(MEMID)).decode_string();
+ memrd.at(id).push_back(cell->name);
+ }
+
+ if (cell->type == ID($memwr)) {
+ IdString id = cell->getParam(ID(MEMID)).decode_string();
+ memwr.at(id).push_back(cell->name);
+ }
+
+ if (cell->type == ID($meminit)) {
+ IdString id = cell->getParam(ID(MEMID)).decode_string();
+ meminit.at(id).push_back(cell->name);
+ }
+ }
+ }
+
+ ~OptMemWorker()
+ {
+ for (auto it : remove_mem)
+ {
+ for (auto cell_name : memrd[it])
+ module->remove(module->cell(cell_name));
+ for (auto cell_name : memwr[it])
+ module->remove(module->cell(cell_name));
+ for (auto cell_name : meminit[it])
+ module->remove(module->cell(cell_name));
+
+ delete module->memories.at(it);
+ module->memories.erase(it);
+ }
+
+ for (auto cell_name : remove_cells)
+ module->remove(module->cell(cell_name));
+ }
+
+ int run(RTLIL::Memory *mem)
+ {
+ if (restart || remove_mem.count(mem->name))
+ return 0;
+
+ if (memwr.at(mem->name).empty() && meminit.at(mem->name).empty()) {
+ log("Removing memory %s.%s with no write ports or init data.\n", log_id(module), log_id(mem));
+ remove_mem.insert(mem->name);
+ return 1;
+ }
+
+ return 0;
+ }
+};
+
+struct OptMemPass : public Pass {
+ OptMemPass() : Pass("opt_mem", "optimize memories") { }
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" opt_mem [options] [selection]\n");
+ log("\n");
+ log("This pass performs various optimizations on memories in the design.\n");
+ log("\n");
+ }
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ log_header(design, "Executing OPT_MEM pass (optimize memories).\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ // if (args[argidx] == "-nomux") {
+ // mode_nomux = true;
+ // continue;
+ // }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ int total_count = 0;
+ for (auto module : design->selected_modules()) {
+ while (1) {
+ int cnt = 0;
+ OptMemWorker worker(module);
+ for (auto &it : module->memories)
+ if (module->selected(it.second))
+ cnt += worker.run(it.second);
+ if (!cnt && !worker.restart)
+ break;
+ total_count += cnt;
+ }
+ }
+
+ if (total_count)
+ design->scratchpad_set_bool("opt.did_something", true);
+ log("Performed a total of %d transformations.\n", total_count);
+ }
+} OptMemPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/opt/opt_merge.cc b/passes/opt/opt_merge.cc
index 93d9f012f..aaea6159e 100644
--- a/passes/opt/opt_merge.cc
+++ b/passes/opt/opt_merge.cc
@@ -47,8 +47,8 @@ struct OptMergeWorker
static void sort_pmux_conn(dict<RTLIL::IdString, RTLIL::SigSpec> &conn)
{
- SigSpec sig_s = conn.at("\\S");
- SigSpec sig_b = conn.at("\\B");
+ SigSpec sig_s = conn.at(ID(S));
+ SigSpec sig_b = conn.at(ID::B);
int s_width = GetSize(sig_s);
int width = GetSize(sig_b) / s_width;
@@ -59,12 +59,12 @@ struct OptMergeWorker
std::sort(sb_pairs.begin(), sb_pairs.end());
- conn["\\S"] = SigSpec();
- conn["\\B"] = SigSpec();
+ conn[ID(S)] = SigSpec();
+ conn[ID::B] = SigSpec();
for (auto &it : sb_pairs) {
- conn["\\S"].append(it.first);
- conn["\\B"].append(it.second);
+ conn[ID(S)].append(it.first);
+ conn[ID::B].append(it.second);
}
}
@@ -94,32 +94,32 @@ struct OptMergeWorker
const dict<RTLIL::IdString, RTLIL::SigSpec> *conn = &cell->connections();
dict<RTLIL::IdString, RTLIL::SigSpec> alt_conn;
- if (cell->type == "$and" || cell->type == "$or" || cell->type == "$xor" || cell->type == "$xnor" || cell->type == "$add" || cell->type == "$mul" ||
- cell->type == "$logic_and" || cell->type == "$logic_or" || cell->type == "$_AND_" || cell->type == "$_OR_" || cell->type == "$_XOR_") {
+ if (cell->type.in(ID($and), ID($or), ID($xor), ID($xnor), ID($add), ID($mul),
+ ID($logic_and), ID($logic_or), ID($_AND_), ID($_OR_), ID($_XOR_))) {
alt_conn = *conn;
- if (assign_map(alt_conn.at("\\A")) < assign_map(alt_conn.at("\\B"))) {
- alt_conn["\\A"] = conn->at("\\B");
- alt_conn["\\B"] = conn->at("\\A");
+ if (assign_map(alt_conn.at(ID::A)) < assign_map(alt_conn.at(ID::B))) {
+ alt_conn[ID::A] = conn->at(ID::B);
+ alt_conn[ID::B] = conn->at(ID::A);
}
conn = &alt_conn;
} else
- if (cell->type == "$reduce_xor" || cell->type == "$reduce_xnor") {
+ if (cell->type.in(ID($reduce_xor), ID($reduce_xnor))) {
alt_conn = *conn;
- assign_map.apply(alt_conn.at("\\A"));
- alt_conn.at("\\A").sort();
+ assign_map.apply(alt_conn.at(ID::A));
+ alt_conn.at(ID::A).sort();
conn = &alt_conn;
} else
- if (cell->type == "$reduce_and" || cell->type == "$reduce_or" || cell->type == "$reduce_bool") {
+ if (cell->type.in(ID($reduce_and), ID($reduce_or), ID($reduce_bool))) {
alt_conn = *conn;
- assign_map.apply(alt_conn.at("\\A"));
- alt_conn.at("\\A").sort_and_unify();
+ assign_map.apply(alt_conn.at(ID::A));
+ alt_conn.at(ID::A).sort_and_unify();
conn = &alt_conn;
} else
- if (cell->type == "$pmux") {
+ if (cell->type == ID($pmux)) {
alt_conn = *conn;
- assign_map.apply(alt_conn.at("\\A"));
- assign_map.apply(alt_conn.at("\\B"));
- assign_map.apply(alt_conn.at("\\S"));
+ assign_map.apply(alt_conn.at(ID::A));
+ assign_map.apply(alt_conn.at(ID::B));
+ assign_map.apply(alt_conn.at(ID(S)));
sort_pmux_conn(alt_conn);
conn = &alt_conn;
}
@@ -189,28 +189,28 @@ struct OptMergeWorker
assign_map.apply(it.second);
}
- if (cell1->type == "$and" || cell1->type == "$or" || cell1->type == "$xor" || cell1->type == "$xnor" || cell1->type == "$add" || cell1->type == "$mul" ||
- cell1->type == "$logic_and" || cell1->type == "$logic_or" || cell1->type == "$_AND_" || cell1->type == "$_OR_" || cell1->type == "$_XOR_") {
- if (conn1.at("\\A") < conn1.at("\\B")) {
- RTLIL::SigSpec tmp = conn1["\\A"];
- conn1["\\A"] = conn1["\\B"];
- conn1["\\B"] = tmp;
+ if (cell1->type == ID($and) || cell1->type == ID($or) || cell1->type == ID($xor) || cell1->type == ID($xnor) || cell1->type == ID($add) || cell1->type == ID($mul) ||
+ cell1->type == ID($logic_and) || cell1->type == ID($logic_or) || cell1->type == ID($_AND_) || cell1->type == ID($_OR_) || cell1->type == ID($_XOR_)) {
+ if (conn1.at(ID::A) < conn1.at(ID::B)) {
+ RTLIL::SigSpec tmp = conn1[ID::A];
+ conn1[ID::A] = conn1[ID::B];
+ conn1[ID::B] = tmp;
}
- if (conn2.at("\\A") < conn2.at("\\B")) {
- RTLIL::SigSpec tmp = conn2["\\A"];
- conn2["\\A"] = conn2["\\B"];
- conn2["\\B"] = tmp;
+ if (conn2.at(ID::A) < conn2.at(ID::B)) {
+ RTLIL::SigSpec tmp = conn2[ID::A];
+ conn2[ID::A] = conn2[ID::B];
+ conn2[ID::B] = tmp;
}
} else
- if (cell1->type == "$reduce_xor" || cell1->type == "$reduce_xnor") {
- conn1["\\A"].sort();
- conn2["\\A"].sort();
+ if (cell1->type == ID($reduce_xor) || cell1->type == ID($reduce_xnor)) {
+ conn1[ID::A].sort();
+ conn2[ID::A].sort();
} else
- if (cell1->type == "$reduce_and" || cell1->type == "$reduce_or" || cell1->type == "$reduce_bool") {
- conn1["\\A"].sort_and_unify();
- conn2["\\A"].sort_and_unify();
+ if (cell1->type == ID($reduce_and) || cell1->type == ID($reduce_or) || cell1->type == ID($reduce_bool)) {
+ conn1[ID::A].sort_and_unify();
+ conn2[ID::A].sort_and_unify();
} else
- if (cell1->type == "$pmux") {
+ if (cell1->type == ID($pmux)) {
sort_pmux_conn(conn1);
sort_pmux_conn(conn2);
}
@@ -222,9 +222,9 @@ struct OptMergeWorker
return true;
}
- if (cell1->type.substr(0, 1) == "$" && conn1.count("\\Q") != 0) {
- std::vector<RTLIL::SigBit> q1 = dff_init_map(cell1->getPort("\\Q")).to_sigbit_vector();
- std::vector<RTLIL::SigBit> q2 = dff_init_map(cell2->getPort("\\Q")).to_sigbit_vector();
+ if (cell1->type.begins_with("$") && conn1.count(ID(Q)) != 0) {
+ std::vector<RTLIL::SigBit> q1 = dff_init_map(cell1->getPort(ID(Q))).to_sigbit_vector();
+ std::vector<RTLIL::SigBit> q2 = dff_init_map(cell2->getPort(ID(Q))).to_sigbit_vector();
for (size_t i = 0; i < q1.size(); i++)
if ((q1.at(i).wire == NULL || q2.at(i).wire == NULL) && q1.at(i) != q2.at(i)) {
lt = q1.at(i) < q2.at(i);
@@ -271,22 +271,24 @@ struct OptMergeWorker
ct.setup_stdcells_mem();
if (mode_nomux) {
- ct.cell_types.erase("$mux");
- ct.cell_types.erase("$pmux");
+ ct.cell_types.erase(ID($mux));
+ ct.cell_types.erase(ID($pmux));
}
- ct.cell_types.erase("$tribuf");
- ct.cell_types.erase("$_TBUF_");
- ct.cell_types.erase("$anyseq");
- ct.cell_types.erase("$anyconst");
+ ct.cell_types.erase(ID($tribuf));
+ ct.cell_types.erase(ID($_TBUF_));
+ ct.cell_types.erase(ID($anyseq));
+ ct.cell_types.erase(ID($anyconst));
+ ct.cell_types.erase(ID($allseq));
+ ct.cell_types.erase(ID($allconst));
log("Finding identical cells in module `%s'.\n", module->name.c_str());
assign_map.set(module);
dff_init_map.set(module);
for (auto &it : module->wires_)
- if (it.second->attributes.count("\\init") != 0) {
- Const initval = it.second->attributes.at("\\init");
+ if (it.second->attributes.count(ID(init)) != 0) {
+ Const initval = it.second->attributes.at(ID(init));
for (int i = 0; i < GetSize(initval) && i < GetSize(it.second); i++)
if (initval[i] == State::S0 || initval[i] == State::S1)
dff_init_map.add(SigBit(it.second, i), initval[i]);
@@ -313,17 +315,17 @@ struct OptMergeWorker
{
if (sharemap.count(cell) > 0) {
did_something = true;
- log(" Cell `%s' is identical to cell `%s'.\n", cell->name.c_str(), sharemap[cell]->name.c_str());
+ log_debug(" Cell `%s' is identical to cell `%s'.\n", cell->name.c_str(), sharemap[cell]->name.c_str());
for (auto &it : cell->connections()) {
if (cell->output(it.first)) {
RTLIL::SigSpec other_sig = sharemap[cell]->getPort(it.first);
- log(" Redirecting output %s: %s = %s\n", it.first.c_str(),
+ log_debug(" Redirecting output %s: %s = %s\n", it.first.c_str(),
log_signal(it.second), log_signal(other_sig));
module->connect(RTLIL::SigSig(it.second, other_sig));
assign_map.add(it.second, other_sig);
}
}
- log(" Removing %s cell `%s' from module `%s'.\n", cell->type.c_str(), cell->name.c_str(), module->name.c_str());
+ log_debug(" Removing %s cell `%s' from module `%s'.\n", cell->type.c_str(), cell->name.c_str(), module->name.c_str());
#ifdef USE_CELL_HASH_CACHE
cell_hash_cache.erase(cell);
#endif
@@ -334,12 +336,14 @@ struct OptMergeWorker
}
}
}
+
+ log_suppressed();
}
};
struct OptMergePass : public Pass {
OptMergePass() : Pass("opt_merge", "consolidate identical cells") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -355,7 +359,7 @@ struct OptMergePass : public Pass {
log(" Operate on all cell types, not just built-in types.\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
log_header(design, "Executing OPT_MERGE pass (detect identical cells).\n");
diff --git a/passes/opt/opt_muxtree.cc b/passes/opt/opt_muxtree.cc
index f5ddc2af9..3c486bbcc 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;
@@ -83,12 +84,12 @@ struct OptMuxtreeWorker
// .const_deactivated
for (auto cell : module->cells())
{
- if (cell->type == "$mux" || cell->type == "$pmux")
+ if (cell->type.in(ID($mux), ID($pmux)))
{
- RTLIL::SigSpec sig_a = cell->getPort("\\A");
- RTLIL::SigSpec sig_b = cell->getPort("\\B");
- RTLIL::SigSpec sig_s = cell->getPort("\\S");
- RTLIL::SigSpec sig_y = cell->getPort("\\Y");
+ RTLIL::SigSpec sig_a = cell->getPort(ID::A);
+ RTLIL::SigSpec sig_b = cell->getPort(ID::B);
+ RTLIL::SigSpec sig_s = cell->getPort(ID(S));
+ RTLIL::SigSpec sig_y = cell->getPort(ID::Y);
muxinfo_t muxinfo;
muxinfo.cell = cell;
@@ -136,7 +137,7 @@ struct OptMuxtreeWorker
}
}
for (auto wire : module->wires()) {
- if (wire->port_output || wire->get_bool_attribute("\\keep"))
+ if (wire->port_output || wire->get_bool_attribute(ID::keep))
for (int idx : sig2bits(RTLIL::SigSpec(wire)))
bit2info[idx].seen_non_mux = true;
}
@@ -180,20 +181,29 @@ struct OptMuxtreeWorker
for (int mux_idx = 0; mux_idx < GetSize(root_muxes); mux_idx++)
if (root_muxes.at(mux_idx)) {
- log(" Root of a mux tree: %s%s\n", log_id(mux2info[mux_idx].cell), root_enable_muxes.at(mux_idx) ? " (pure)" : "");
+ log_debug(" Root of a mux tree: %s%s\n", log_id(mux2info[mux_idx].cell), root_enable_muxes.at(mux_idx) ? " (pure)" : "");
root_mux_rerun.erase(mux_idx);
eval_root_mux(mux_idx);
+ if (glob_abort_cnt == 0) {
+ log(" Giving up (too many iterations)\n");
+ return;
+ }
}
while (!root_mux_rerun.empty()) {
int mux_idx = *root_mux_rerun.begin();
- log(" Root of a mux tree: %s (rerun as non-pure)\n", log_id(mux2info[mux_idx].cell));
+ log_debug(" Root of a mux tree: %s (rerun as non-pure)\n", log_id(mux2info[mux_idx].cell));
log_assert(root_enable_muxes.at(mux_idx));
root_mux_rerun.erase(mux_idx);
eval_root_mux(mux_idx);
+ if (glob_abort_cnt == 0) {
+ log(" Giving up (too many iterations)\n");
+ return;
+ }
}
log(" Analyzing evaluation results.\n");
+ log_assert(glob_abort_cnt > 0);
for (auto &mi : mux2info)
{
@@ -217,10 +227,10 @@ struct OptMuxtreeWorker
continue;
}
- RTLIL::SigSpec sig_a = mi.cell->getPort("\\A");
- RTLIL::SigSpec sig_b = mi.cell->getPort("\\B");
- RTLIL::SigSpec sig_s = mi.cell->getPort("\\S");
- RTLIL::SigSpec sig_y = mi.cell->getPort("\\Y");
+ RTLIL::SigSpec sig_a = mi.cell->getPort(ID::A);
+ RTLIL::SigSpec sig_b = mi.cell->getPort(ID::B);
+ RTLIL::SigSpec sig_s = mi.cell->getPort(ID(S));
+ RTLIL::SigSpec sig_y = mi.cell->getPort(ID::Y);
RTLIL::SigSpec sig_ports = sig_b;
sig_ports.append(sig_a);
@@ -245,14 +255,14 @@ struct OptMuxtreeWorker
}
}
- mi.cell->setPort("\\A", new_sig_a);
- mi.cell->setPort("\\B", new_sig_b);
- mi.cell->setPort("\\S", new_sig_s);
+ mi.cell->setPort(ID::A, new_sig_a);
+ mi.cell->setPort(ID::B, new_sig_b);
+ mi.cell->setPort(ID(S), new_sig_s);
if (GetSize(new_sig_s) == 1) {
- mi.cell->type = "$mux";
- mi.cell->parameters.erase("\\S_WIDTH");
+ mi.cell->type = ID($mux);
+ mi.cell->parameters.erase(ID(S_WIDTH));
} else {
- mi.cell->parameters["\\S_WIDTH"] = RTLIL::Const(GetSize(new_sig_s));
+ mi.cell->parameters[ID(S_WIDTH)] = RTLIL::Const(GetSize(new_sig_s));
}
}
}
@@ -293,6 +303,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,18 +328,21 @@ 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)) {
if (abort_count == 0) {
root_mux_rerun.insert(m);
root_enable_muxes.at(m) = true;
- log(" Removing pure flag from root mux %s.\n", log_id(mux2info[m].cell));
+ log_debug(" Removing pure flag from root mux %s.\n", log_id(mux2info[m].cell));
} else
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;
@@ -348,9 +364,9 @@ struct OptMuxtreeWorker
int width = 0;
idict<int> ctrl_bits;
- if (portname == "\\B")
- width = GetSize(muxinfo.cell->getPort("\\A"));
- for (int bit : sig2bits(muxinfo.cell->getPort("\\S"), false))
+ if (portname == ID::B)
+ width = GetSize(muxinfo.cell->getPort(ID::A));
+ for (int bit : sig2bits(muxinfo.cell->getPort(ID(S)), false))
ctrl_bits(bit);
int port_idx = 0, port_off = 0;
@@ -390,12 +406,16 @@ 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)
+ return;
+ glob_abort_cnt--;
+
muxinfo_t &muxinfo = mux2info[mux_idx];
// set input ports to constants if we find known active or inactive signals
if (do_replace_known) {
- replace_known(knowledge, muxinfo, "\\A");
- replace_known(knowledge, muxinfo, "\\B");
+ replace_known(knowledge, muxinfo, ID::A);
+ replace_known(knowledge, muxinfo, ID::B);
}
// if there is a constant activated port we just use it
@@ -433,11 +453,15 @@ 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;
}
}
void eval_root_mux(int mux_idx)
{
+ log_assert(glob_abort_cnt > 0);
knowledge_t knowledge;
knowledge.known_inactive.resize(GetSize(bit2info));
knowledge.known_active.resize(GetSize(bit2info));
@@ -449,7 +473,7 @@ struct OptMuxtreeWorker
struct OptMuxtreePass : public Pass {
OptMuxtreePass() : Pass("opt_muxtree", "eliminate dead trees in multiplexer trees") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -462,7 +486,7 @@ struct OptMuxtreePass : public Pass {
log("This pass only operates on completely selected modules without processes.\n");
log("\n");
}
- virtual void execute(vector<std::string> args, RTLIL::Design *design)
+ void execute(vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
log_header(design, "Executing OPT_MUXTREE pass (detect dead branches in mux trees).\n");
extra_args(args, 1, design);
diff --git a/passes/opt/opt_reduce.cc b/passes/opt/opt_reduce.cc
index 8126f3c0d..f74655d1c 100644
--- a/passes/opt/opt_reduce.cc
+++ b/passes/opt/opt_reduce.cc
@@ -43,14 +43,14 @@ struct OptReduceWorker
return;
cells.erase(cell);
- RTLIL::SigSpec sig_a = assign_map(cell->getPort("\\A"));
+ RTLIL::SigSpec sig_a = assign_map(cell->getPort(ID::A));
sig_a.sort_and_unify();
pool<RTLIL::SigBit> new_sig_a_bits;
- for (auto &bit : sig_a.to_sigbit_set())
+ for (auto &bit : sig_a)
{
if (bit == RTLIL::State::S0) {
- if (cell->type == "$reduce_and") {
+ if (cell->type == ID($reduce_and)) {
new_sig_a_bits.clear();
new_sig_a_bits.insert(RTLIL::State::S0);
break;
@@ -58,7 +58,7 @@ struct OptReduceWorker
continue;
}
if (bit == RTLIL::State::S1) {
- if (cell->type == "$reduce_or") {
+ if (cell->type == ID($reduce_or)) {
new_sig_a_bits.clear();
new_sig_a_bits.insert(RTLIL::State::S1);
break;
@@ -74,8 +74,8 @@ struct OptReduceWorker
for (auto child_cell : drivers.find(bit)) {
if (child_cell->type == cell->type) {
opt_reduce(cells, drivers, child_cell);
- if (child_cell->getPort("\\Y")[0] == bit) {
- pool<RTLIL::SigBit> child_sig_a_bits = assign_map(child_cell->getPort("\\A")).to_sigbit_pool();
+ if (child_cell->getPort(ID::Y)[0] == bit) {
+ pool<RTLIL::SigBit> child_sig_a_bits = assign_map(child_cell->getPort(ID::A)).to_sigbit_pool();
new_sig_a_bits.insert(child_sig_a_bits.begin(), child_sig_a_bits.end());
} else
new_sig_a_bits.insert(RTLIL::State::S0);
@@ -89,22 +89,22 @@ struct OptReduceWorker
RTLIL::SigSpec new_sig_a(new_sig_a_bits);
new_sig_a.sort_and_unify();
- if (new_sig_a != sig_a || sig_a.size() != cell->getPort("\\A").size()) {
+ if (new_sig_a != sig_a || sig_a.size() != cell->getPort(ID::A).size()) {
log(" New input vector for %s cell %s: %s\n", cell->type.c_str(), cell->name.c_str(), log_signal(new_sig_a));
did_something = true;
total_count++;
}
- cell->setPort("\\A", new_sig_a);
- cell->parameters["\\A_WIDTH"] = RTLIL::Const(new_sig_a.size());
+ cell->setPort(ID::A, new_sig_a);
+ cell->parameters[ID(A_WIDTH)] = RTLIL::Const(new_sig_a.size());
return;
}
void opt_mux(RTLIL::Cell *cell)
{
- RTLIL::SigSpec sig_a = assign_map(cell->getPort("\\A"));
- RTLIL::SigSpec sig_b = assign_map(cell->getPort("\\B"));
- RTLIL::SigSpec sig_s = assign_map(cell->getPort("\\S"));
+ RTLIL::SigSpec sig_a = assign_map(cell->getPort(ID::A));
+ RTLIL::SigSpec sig_b = assign_map(cell->getPort(ID::B));
+ RTLIL::SigSpec sig_s = assign_map(cell->getPort(ID(S)));
RTLIL::SigSpec new_sig_b, new_sig_s;
pool<RTLIL::SigSpec> handled_sig;
@@ -125,15 +125,15 @@ struct OptReduceWorker
if (this_s.size() > 1)
{
- RTLIL::Cell *reduce_or_cell = module->addCell(NEW_ID, "$reduce_or");
- reduce_or_cell->setPort("\\A", this_s);
- reduce_or_cell->parameters["\\A_SIGNED"] = RTLIL::Const(0);
- reduce_or_cell->parameters["\\A_WIDTH"] = RTLIL::Const(this_s.size());
- reduce_or_cell->parameters["\\Y_WIDTH"] = RTLIL::Const(1);
+ RTLIL::Cell *reduce_or_cell = module->addCell(NEW_ID, ID($reduce_or));
+ reduce_or_cell->setPort(ID::A, this_s);
+ reduce_or_cell->parameters[ID(A_SIGNED)] = RTLIL::Const(0);
+ reduce_or_cell->parameters[ID(A_WIDTH)] = RTLIL::Const(this_s.size());
+ reduce_or_cell->parameters[ID(Y_WIDTH)] = RTLIL::Const(1);
RTLIL::Wire *reduce_or_wire = module->addWire(NEW_ID);
this_s = RTLIL::SigSpec(reduce_or_wire);
- reduce_or_cell->setPort("\\Y", this_s);
+ reduce_or_cell->setPort(ID::Y, this_s);
}
new_sig_b.append(this_b);
@@ -149,28 +149,28 @@ struct OptReduceWorker
if (new_sig_s.size() == 0)
{
- module->connect(RTLIL::SigSig(cell->getPort("\\Y"), cell->getPort("\\A")));
- assign_map.add(cell->getPort("\\Y"), cell->getPort("\\A"));
+ module->connect(RTLIL::SigSig(cell->getPort(ID::Y), cell->getPort(ID::A)));
+ assign_map.add(cell->getPort(ID::Y), cell->getPort(ID::A));
module->remove(cell);
}
else
{
- cell->setPort("\\B", new_sig_b);
- cell->setPort("\\S", new_sig_s);
+ cell->setPort(ID::B, new_sig_b);
+ cell->setPort(ID(S), new_sig_s);
if (new_sig_s.size() > 1) {
- cell->parameters["\\S_WIDTH"] = RTLIL::Const(new_sig_s.size());
+ cell->parameters[ID(S_WIDTH)] = RTLIL::Const(new_sig_s.size());
} else {
- cell->type = "$mux";
- cell->parameters.erase("\\S_WIDTH");
+ cell->type = ID($mux);
+ cell->parameters.erase(ID(S_WIDTH));
}
}
}
void opt_mux_bits(RTLIL::Cell *cell)
{
- std::vector<RTLIL::SigBit> sig_a = assign_map(cell->getPort("\\A")).to_sigbit_vector();
- std::vector<RTLIL::SigBit> sig_b = assign_map(cell->getPort("\\B")).to_sigbit_vector();
- std::vector<RTLIL::SigBit> sig_y = assign_map(cell->getPort("\\Y")).to_sigbit_vector();
+ std::vector<RTLIL::SigBit> sig_a = assign_map(cell->getPort(ID::A)).to_sigbit_vector();
+ std::vector<RTLIL::SigBit> sig_b = assign_map(cell->getPort(ID::B)).to_sigbit_vector();
+ std::vector<RTLIL::SigBit> sig_y = assign_map(cell->getPort(ID::Y)).to_sigbit_vector();
std::vector<RTLIL::SigBit> new_sig_y;
RTLIL::SigSig old_sig_conn;
@@ -211,33 +211,32 @@ struct OptReduceWorker
if (new_sig_y.size() != sig_y.size())
{
log(" Consolidated identical input bits for %s cell %s:\n", cell->type.c_str(), cell->name.c_str());
- log(" Old ports: A=%s, B=%s, Y=%s\n", log_signal(cell->getPort("\\A")),
- log_signal(cell->getPort("\\B")), log_signal(cell->getPort("\\Y")));
+ log(" Old ports: A=%s, B=%s, Y=%s\n", log_signal(cell->getPort(ID::A)),
+ log_signal(cell->getPort(ID::B)), log_signal(cell->getPort(ID::Y)));
- cell->setPort("\\A", RTLIL::SigSpec());
+ cell->setPort(ID::A, RTLIL::SigSpec());
for (auto &in_tuple : consolidated_in_tuples) {
- RTLIL::SigSpec new_a = cell->getPort("\\A");
+ RTLIL::SigSpec new_a = cell->getPort(ID::A);
new_a.append(in_tuple.at(0));
- cell->setPort("\\A", new_a);
+ cell->setPort(ID::A, new_a);
}
- cell->setPort("\\B", RTLIL::SigSpec());
- for (int i = 1; i <= cell->getPort("\\S").size(); i++)
+ cell->setPort(ID::B, RTLIL::SigSpec());
+ for (int i = 1; i <= cell->getPort(ID(S)).size(); i++)
for (auto &in_tuple : consolidated_in_tuples) {
- RTLIL::SigSpec new_b = cell->getPort("\\B");
+ RTLIL::SigSpec new_b = cell->getPort(ID::B);
new_b.append(in_tuple.at(i));
- cell->setPort("\\B", new_b);
+ cell->setPort(ID::B, new_b);
}
- cell->parameters["\\WIDTH"] = RTLIL::Const(new_sig_y.size());
- cell->setPort("\\Y", new_sig_y);
+ cell->parameters[ID(WIDTH)] = RTLIL::Const(new_sig_y.size());
+ cell->setPort(ID::Y, new_sig_y);
- log(" New ports: A=%s, B=%s, Y=%s\n", log_signal(cell->getPort("\\A")),
- log_signal(cell->getPort("\\B")), log_signal(cell->getPort("\\Y")));
+ log(" New ports: A=%s, B=%s, Y=%s\n", log_signal(cell->getPort(ID::A)),
+ log_signal(cell->getPort(ID::B)), log_signal(cell->getPort(ID::Y)));
log(" New connections: %s = %s\n", log_signal(old_sig_conn.first), log_signal(old_sig_conn.second));
module->connect(old_sig_conn);
- module->check();
did_something = true;
total_count++;
@@ -255,15 +254,15 @@ struct OptReduceWorker
SigPool mem_wren_sigs;
for (auto &cell_it : module->cells_) {
RTLIL::Cell *cell = cell_it.second;
- if (cell->type == "$mem")
- mem_wren_sigs.add(assign_map(cell->getPort("\\WR_EN")));
- if (cell->type == "$memwr")
- mem_wren_sigs.add(assign_map(cell->getPort("\\EN")));
+ if (cell->type == ID($mem))
+ mem_wren_sigs.add(assign_map(cell->getPort(ID(WR_EN))));
+ if (cell->type == ID($memwr))
+ mem_wren_sigs.add(assign_map(cell->getPort(ID(EN))));
}
for (auto &cell_it : module->cells_) {
RTLIL::Cell *cell = cell_it.second;
- if (cell->type == "$dff" && mem_wren_sigs.check_any(assign_map(cell->getPort("\\Q"))))
- mem_wren_sigs.add(assign_map(cell->getPort("\\D")));
+ if (cell->type == ID($dff) && mem_wren_sigs.check_any(assign_map(cell->getPort(ID(Q)))))
+ mem_wren_sigs.add(assign_map(cell->getPort(ID(D))));
}
bool keep_expanding_mem_wren_sigs = true;
@@ -271,12 +270,12 @@ struct OptReduceWorker
keep_expanding_mem_wren_sigs = false;
for (auto &cell_it : module->cells_) {
RTLIL::Cell *cell = cell_it.second;
- if (cell->type == "$mux" && mem_wren_sigs.check_any(assign_map(cell->getPort("\\Y")))) {
- if (!mem_wren_sigs.check_all(assign_map(cell->getPort("\\A"))) ||
- !mem_wren_sigs.check_all(assign_map(cell->getPort("\\B"))))
+ if (cell->type == ID($mux) && mem_wren_sigs.check_any(assign_map(cell->getPort(ID::Y)))) {
+ if (!mem_wren_sigs.check_all(assign_map(cell->getPort(ID::A))) ||
+ !mem_wren_sigs.check_all(assign_map(cell->getPort(ID::B))))
keep_expanding_mem_wren_sigs = true;
- mem_wren_sigs.add(assign_map(cell->getPort("\\A")));
- mem_wren_sigs.add(assign_map(cell->getPort("\\B")));
+ mem_wren_sigs.add(assign_map(cell->getPort(ID::A)));
+ mem_wren_sigs.add(assign_map(cell->getPort(ID::B)));
}
}
}
@@ -288,7 +287,7 @@ struct OptReduceWorker
// merge trees of reduce_* cells to one single cell and unify input vectors
// (only handle reduce_and and reduce_or for various reasons)
- const char *type_list[] = { "$reduce_or", "$reduce_and" };
+ const IdString type_list[] = { ID($reduce_or), ID($reduce_and) };
for (auto type : type_list)
{
SigSet<RTLIL::Cell*> drivers;
@@ -298,7 +297,7 @@ struct OptReduceWorker
RTLIL::Cell *cell = cell_it.second;
if (cell->type != type || !design->selected(module, cell))
continue;
- drivers.insert(assign_map(cell->getPort("\\Y")), cell);
+ drivers.insert(assign_map(cell->getPort(ID::Y)), cell);
cells.insert(cell);
}
@@ -313,25 +312,27 @@ struct OptReduceWorker
std::vector<RTLIL::Cell*> cells;
for (auto &it : module->cells_)
- if ((it.second->type == "$mux" || it.second->type == "$pmux") && design->selected(module, it.second))
+ if ((it.second->type == ID($mux) || it.second->type == ID($pmux)) && design->selected(module, it.second))
cells.push_back(it.second);
for (auto cell : cells)
{
// this optimization is to aggressive for most coarse-grain applications.
// but we always want it for multiplexers driving write enable ports.
- if (do_fine || mem_wren_sigs.check_any(assign_map(cell->getPort("\\Y"))))
+ if (do_fine || mem_wren_sigs.check_any(assign_map(cell->getPort(ID::Y))))
opt_mux_bits(cell);
opt_mux(cell);
}
}
+
+ module->check();
}
};
struct OptReducePass : public Pass {
OptReducePass() : Pass("opt_reduce", "simplify large MUXes and AND/OR gates") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -352,7 +353,7 @@ struct OptReducePass : public Pass {
log(" alias for -fine\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
bool do_fine = false;
diff --git a/passes/opt/opt_rmdff.cc b/passes/opt/opt_rmdff.cc
index 02f3e93f5..0bf74098a 100644
--- a/passes/opt/opt_rmdff.cc
+++ b/passes/opt/opt_rmdff.cc
@@ -17,26 +17,31 @@
*
*/
+#include "kernel/log.h"
#include "kernel/register.h"
+#include "kernel/rtlil.h"
+#include "kernel/satgen.h"
#include "kernel/sigtools.h"
-#include "kernel/log.h"
-#include <stdlib.h>
#include <stdio.h>
+#include <stdlib.h>
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
SigMap assign_map, dff_init_map;
SigSet<RTLIL::Cell*> mux_drivers;
+dict<SigBit, RTLIL::Cell*> bit2driver;
dict<SigBit, pool<SigBit>> init_attributes;
+
bool keepdc;
+bool sat;
void remove_init_attr(SigSpec sig)
{
for (auto bit : assign_map(sig))
if (init_attributes.count(bit))
for (auto wbit : init_attributes.at(bit))
- wbit.wire->attributes.at("\\init")[wbit.offset] = State::Sx;
+ wbit.wire->attributes.at(ID(init))[wbit.offset] = State::Sx;
}
bool handle_dffsr(RTLIL::Module *mod, RTLIL::Cell *cell)
@@ -44,39 +49,39 @@ bool handle_dffsr(RTLIL::Module *mod, RTLIL::Cell *cell)
SigSpec sig_set, sig_clr;
State pol_set, pol_clr;
- if (cell->hasPort("\\S"))
- sig_set = cell->getPort("\\S");
+ if (cell->hasPort(ID(S)))
+ sig_set = cell->getPort(ID(S));
- if (cell->hasPort("\\R"))
- sig_clr = cell->getPort("\\R");
+ if (cell->hasPort(ID(R)))
+ sig_clr = cell->getPort(ID(R));
- if (cell->hasPort("\\SET"))
- sig_set = cell->getPort("\\SET");
+ if (cell->hasPort(ID(SET)))
+ sig_set = cell->getPort(ID(SET));
- if (cell->hasPort("\\CLR"))
- sig_clr = cell->getPort("\\CLR");
+ if (cell->hasPort(ID(CLR)))
+ sig_clr = cell->getPort(ID(CLR));
log_assert(GetSize(sig_set) == GetSize(sig_clr));
- if (cell->type.substr(0,8) == "$_DFFSR_") {
+ if (cell->type.begins_with("$_DFFSR_")) {
pol_set = cell->type[9] == 'P' ? State::S1 : State::S0;
pol_clr = cell->type[10] == 'P' ? State::S1 : State::S0;
} else
- if (cell->type.substr(0,11) == "$_DLATCHSR_") {
+ if (cell->type.begins_with("$_DLATCHSR_")) {
pol_set = cell->type[12] == 'P' ? State::S1 : State::S0;
pol_clr = cell->type[13] == 'P' ? State::S1 : State::S0;
} else
- if (cell->type == "$dffsr" || cell->type == "$dlatchsr") {
- pol_set = cell->parameters["\\SET_POLARITY"].as_bool() ? State::S1 : State::S0;
- pol_clr = cell->parameters["\\CLR_POLARITY"].as_bool() ? State::S1 : State::S0;
+ if (cell->type.in(ID($dffsr), ID($dlatchsr))) {
+ pol_set = cell->parameters[ID(SET_POLARITY)].as_bool() ? State::S1 : State::S0;
+ pol_clr = cell->parameters[ID(CLR_POLARITY)].as_bool() ? State::S1 : State::S0;
} else
log_abort();
State npol_set = pol_set == State::S0 ? State::S1 : State::S0;
State npol_clr = pol_clr == State::S0 ? State::S1 : State::S0;
- SigSpec sig_d = cell->getPort("\\D");
- SigSpec sig_q = cell->getPort("\\Q");
+ SigSpec sig_d = cell->getPort(ID(D));
+ SigSpec sig_q = cell->getPort(ID(Q));
bool did_something = false;
bool proper_sr = false;
@@ -132,20 +137,20 @@ bool handle_dffsr(RTLIL::Module *mod, RTLIL::Cell *cell)
return true;
}
- if (cell->type == "$dffsr" || cell->type == "$dlatchsr")
+ if (cell->type.in(ID($dffsr), ID($dlatchsr)))
{
- cell->setParam("\\WIDTH", GetSize(sig_d));
- cell->setPort("\\SET", sig_set);
- cell->setPort("\\CLR", sig_clr);
- cell->setPort("\\D", sig_d);
- cell->setPort("\\Q", sig_q);
+ cell->setParam(ID(WIDTH), GetSize(sig_d));
+ cell->setPort(ID(SET), sig_set);
+ cell->setPort(ID(CLR), sig_clr);
+ cell->setPort(ID(D), sig_d);
+ cell->setPort(ID(Q), sig_q);
}
else
{
- cell->setPort("\\S", sig_set);
- cell->setPort("\\R", sig_clr);
- cell->setPort("\\D", sig_d);
- cell->setPort("\\Q", sig_q);
+ cell->setPort(ID(S), sig_set);
+ cell->setPort(ID(R), sig_clr);
+ cell->setPort(ID(D), sig_d);
+ cell->setPort(ID(Q), sig_q);
}
if (proper_sr)
@@ -154,46 +159,48 @@ bool handle_dffsr(RTLIL::Module *mod, RTLIL::Cell *cell)
if (used_pol_set && used_pol_clr && pol_set != pol_clr)
return did_something;
+ if (cell->type == ID($dlatchsr))
+ return did_something;
+
State unified_pol = used_pol_set ? pol_set : pol_clr;
- if (cell->type == "$dffsr")
+ if (cell->type == ID($dffsr))
{
if (hasreset)
{
log("Converting %s (%s) to %s in module %s.\n", log_id(cell), log_id(cell->type), "$adff", log_id(mod));
- cell->type = "$adff";
- cell->setParam("\\ARST_POLARITY", unified_pol);
- cell->setParam("\\ARST_VALUE", reset_val);
- cell->setPort("\\ARST", sig_reset);
+ cell->type = ID($adff);
+ cell->setParam(ID(ARST_POLARITY), unified_pol);
+ cell->setParam(ID(ARST_VALUE), reset_val);
+ cell->setPort(ID(ARST), sig_reset);
- cell->unsetParam("\\SET_POLARITY");
- cell->unsetParam("\\CLR_POLARITY");
- cell->unsetPort("\\SET");
- cell->unsetPort("\\CLR");
-
- return true;
+ cell->unsetParam(ID(SET_POLARITY));
+ cell->unsetParam(ID(CLR_POLARITY));
+ cell->unsetPort(ID(SET));
+ cell->unsetPort(ID(CLR));
}
else
{
log("Converting %s (%s) to %s in module %s.\n", log_id(cell), log_id(cell->type), "$dff", log_id(mod));
- cell->type = "$dff";
- cell->unsetParam("\\SET_POLARITY");
- cell->unsetParam("\\CLR_POLARITY");
- cell->unsetPort("\\SET");
- cell->unsetPort("\\CLR");
-
- return true;
+ cell->type = ID($dff);
+ cell->unsetParam(ID(SET_POLARITY));
+ cell->unsetParam(ID(CLR_POLARITY));
+ cell->unsetPort(ID(SET));
+ cell->unsetPort(ID(CLR));
}
+
+ return true;
}
- else
+
+ if (!hasreset)
{
IdString new_type;
- if (cell->type.substr(0,8) == "$_DFFSR_")
+ if (cell->type.begins_with("$_DFFSR_"))
new_type = stringf("$_DFF_%c_", cell->type[8]);
- else if (cell->type.substr(0,11) == "$_DLATCHSR_")
+ else if (cell->type.begins_with("$_DLATCHSR_"))
new_type = stringf("$_DLATCH_%c_", cell->type[11]);
else
log_abort();
@@ -201,11 +208,13 @@ bool handle_dffsr(RTLIL::Module *mod, RTLIL::Cell *cell)
log("Converting %s (%s) to %s in module %s.\n", log_id(cell), log_id(cell->type), log_id(new_type), log_id(mod));
cell->type = new_type;
- cell->unsetPort("\\S");
- cell->unsetPort("\\R");
+ cell->unsetPort(ID(S));
+ cell->unsetPort(ID(R));
- return did_something;
+ return true;
}
+
+ return did_something;
}
bool handle_dlatch(RTLIL::Module *mod, RTLIL::Cell *dlatch)
@@ -213,18 +222,18 @@ bool handle_dlatch(RTLIL::Module *mod, RTLIL::Cell *dlatch)
SigSpec sig_e;
State on_state, off_state;
- if (dlatch->type == "$dlatch") {
- sig_e = assign_map(dlatch->getPort("\\EN"));
- on_state = dlatch->getParam("\\EN_POLARITY").as_bool() ? State::S1 : State::S0;
- off_state = dlatch->getParam("\\EN_POLARITY").as_bool() ? State::S0 : State::S1;
+ if (dlatch->type == ID($dlatch)) {
+ sig_e = assign_map(dlatch->getPort(ID(EN)));
+ on_state = dlatch->getParam(ID(EN_POLARITY)).as_bool() ? State::S1 : State::S0;
+ off_state = dlatch->getParam(ID(EN_POLARITY)).as_bool() ? State::S0 : State::S1;
} else
- if (dlatch->type == "$_DLATCH_P_") {
- sig_e = assign_map(dlatch->getPort("\\E"));
+ if (dlatch->type == ID($_DLATCH_P_)) {
+ sig_e = assign_map(dlatch->getPort(ID(E)));
on_state = State::S1;
off_state = State::S0;
} else
- if (dlatch->type == "$_DLATCH_N_") {
- sig_e = assign_map(dlatch->getPort("\\E"));
+ if (dlatch->type == ID($_DLATCH_N_)) {
+ sig_e = assign_map(dlatch->getPort(ID(E)));
on_state = State::S0;
off_state = State::S1;
} else
@@ -233,15 +242,15 @@ bool handle_dlatch(RTLIL::Module *mod, RTLIL::Cell *dlatch)
if (sig_e == off_state)
{
RTLIL::Const val_init;
- for (auto bit : dff_init_map(dlatch->getPort("\\Q")))
+ for (auto bit : dff_init_map(dlatch->getPort(ID(Q))))
val_init.bits.push_back(bit.wire == NULL ? bit.data : State::Sx);
- mod->connect(dlatch->getPort("\\Q"), val_init);
+ mod->connect(dlatch->getPort(ID(Q)), val_init);
goto delete_dlatch;
}
if (sig_e == on_state)
{
- mod->connect(dlatch->getPort("\\Q"), dlatch->getPort("\\D"));
+ mod->connect(dlatch->getPort(ID(Q)), dlatch->getPort(ID(D)));
goto delete_dlatch;
}
@@ -249,56 +258,74 @@ bool handle_dlatch(RTLIL::Module *mod, RTLIL::Cell *dlatch)
delete_dlatch:
log("Removing %s (%s) from module %s.\n", log_id(dlatch), log_id(dlatch->type), log_id(mod));
- remove_init_attr(dlatch->getPort("\\Q"));
+ remove_init_attr(dlatch->getPort(ID(Q)));
mod->remove(dlatch);
return true;
}
bool handle_dff(RTLIL::Module *mod, RTLIL::Cell *dff)
{
- RTLIL::SigSpec sig_d, sig_q, sig_c, sig_r;
- RTLIL::Const val_cp, val_rp, val_rv;
+ RTLIL::SigSpec sig_d, sig_q, sig_c, sig_r, sig_e;
+ RTLIL::Const val_cp, val_rp, val_rv, val_ep;
- if (dff->type == "$_FF_") {
- sig_d = dff->getPort("\\D");
- sig_q = dff->getPort("\\Q");
+ if (dff->type == ID($_FF_)) {
+ sig_d = dff->getPort(ID(D));
+ sig_q = dff->getPort(ID(Q));
}
- else if (dff->type == "$_DFF_N_" || dff->type == "$_DFF_P_") {
- sig_d = dff->getPort("\\D");
- sig_q = dff->getPort("\\Q");
- sig_c = dff->getPort("\\C");
- val_cp = RTLIL::Const(dff->type == "$_DFF_P_", 1);
+ else if (dff->type == ID($_DFF_N_) || dff->type == ID($_DFF_P_)) {
+ sig_d = dff->getPort(ID(D));
+ sig_q = dff->getPort(ID(Q));
+ sig_c = dff->getPort(ID(C));
+ val_cp = RTLIL::Const(dff->type == ID($_DFF_P_), 1);
}
- else if (dff->type.substr(0,6) == "$_DFF_" && dff->type.substr(9) == "_" &&
+ else if (dff->type.begins_with("$_DFF_") && dff->type.compare(9, 1, "_") == 0 &&
(dff->type[6] == 'N' || dff->type[6] == 'P') &&
(dff->type[7] == 'N' || dff->type[7] == 'P') &&
(dff->type[8] == '0' || dff->type[8] == '1')) {
- sig_d = dff->getPort("\\D");
- sig_q = dff->getPort("\\Q");
- sig_c = dff->getPort("\\C");
- sig_r = dff->getPort("\\R");
+ sig_d = dff->getPort(ID(D));
+ sig_q = dff->getPort(ID(Q));
+ sig_c = dff->getPort(ID(C));
+ sig_r = dff->getPort(ID(R));
val_cp = RTLIL::Const(dff->type[6] == 'P', 1);
val_rp = RTLIL::Const(dff->type[7] == 'P', 1);
val_rv = RTLIL::Const(dff->type[8] == '1', 1);
}
- else if (dff->type == "$ff") {
- sig_d = dff->getPort("\\D");
- sig_q = dff->getPort("\\Q");
+ else if (dff->type.begins_with("$_DFFE_") && dff->type.compare(9, 1, "_") == 0 &&
+ (dff->type[7] == 'N' || dff->type[7] == 'P') &&
+ (dff->type[8] == 'N' || dff->type[8] == 'P')) {
+ sig_d = dff->getPort(ID(D));
+ sig_q = dff->getPort(ID(Q));
+ sig_c = dff->getPort(ID(C));
+ sig_e = dff->getPort(ID(E));
+ val_cp = RTLIL::Const(dff->type[7] == 'P', 1);
+ val_ep = RTLIL::Const(dff->type[8] == 'P', 1);
+ }
+ else if (dff->type == ID($ff)) {
+ sig_d = dff->getPort(ID(D));
+ sig_q = dff->getPort(ID(Q));
+ }
+ else if (dff->type == ID($dff)) {
+ sig_d = dff->getPort(ID(D));
+ sig_q = dff->getPort(ID(Q));
+ sig_c = dff->getPort(ID(CLK));
+ val_cp = RTLIL::Const(dff->parameters[ID(CLK_POLARITY)].as_bool(), 1);
}
- else if (dff->type == "$dff") {
- sig_d = dff->getPort("\\D");
- sig_q = dff->getPort("\\Q");
- sig_c = dff->getPort("\\CLK");
- val_cp = RTLIL::Const(dff->parameters["\\CLK_POLARITY"].as_bool(), 1);
+ else if (dff->type == ID($dffe)) {
+ sig_e = dff->getPort(ID(EN));
+ sig_d = dff->getPort(ID(D));
+ sig_q = dff->getPort(ID(Q));
+ sig_c = dff->getPort(ID(CLK));
+ val_cp = RTLIL::Const(dff->parameters[ID(CLK_POLARITY)].as_bool(), 1);
+ val_ep = RTLIL::Const(dff->parameters[ID(EN_POLARITY)].as_bool(), 1);
}
- else if (dff->type == "$adff") {
- sig_d = dff->getPort("\\D");
- sig_q = dff->getPort("\\Q");
- sig_c = dff->getPort("\\CLK");
- sig_r = dff->getPort("\\ARST");
- val_cp = RTLIL::Const(dff->parameters["\\CLK_POLARITY"].as_bool(), 1);
- val_rp = RTLIL::Const(dff->parameters["\\ARST_POLARITY"].as_bool(), 1);
- val_rv = dff->parameters["\\ARST_VALUE"];
+ else if (dff->type == ID($adff)) {
+ sig_d = dff->getPort(ID(D));
+ sig_q = dff->getPort(ID(Q));
+ sig_c = dff->getPort(ID(CLK));
+ sig_r = dff->getPort(ID(ARST));
+ val_cp = RTLIL::Const(dff->parameters[ID(CLK_POLARITY)].as_bool(), 1);
+ val_rp = RTLIL::Const(dff->parameters[ID(ARST_POLARITY)].as_bool(), 1);
+ val_rv = dff->parameters[ID(ARST_VALUE)];
}
else
log_abort();
@@ -316,12 +343,12 @@ bool handle_dff(RTLIL::Module *mod, RTLIL::Cell *dff)
val_init.bits.push_back(bit.wire == NULL ? bit.data : RTLIL::State::Sx);
}
- if (dff->type.in("$ff", "$dff") && mux_drivers.has(sig_d)) {
+ if (dff->type.in(ID($ff), ID($dff)) && mux_drivers.has(sig_d)) {
std::set<RTLIL::Cell*> muxes;
mux_drivers.find(sig_d, muxes);
for (auto mux : muxes) {
- RTLIL::SigSpec sig_a = assign_map(mux->getPort("\\A"));
- RTLIL::SigSpec sig_b = assign_map(mux->getPort("\\B"));
+ RTLIL::SigSpec sig_a = assign_map(mux->getPort(ID::A));
+ RTLIL::SigSpec sig_b = assign_map(mux->getPort(ID::B));
if (sig_a == sig_q && sig_b.is_fully_const() && (!has_init || val_init == sig_b.as_const())) {
mod->connect(sig_q, sig_b);
goto delete_dff;
@@ -333,85 +360,207 @@ bool handle_dff(RTLIL::Module *mod, RTLIL::Cell *dff)
}
}
+ // If clock is driven by a constant and (i) no reset signal
+ // (ii) Q has no initial value
+ // (iii) initial value is same as reset value
if (!sig_c.empty() && sig_c.is_fully_const() && (!sig_r.size() || !has_init || val_init == val_rv)) {
if (val_rv.bits.size() == 0)
val_rv = val_init;
+ // Q is permanently reset value or initial value
mod->connect(sig_q, val_rv);
goto delete_dff;
}
+ // If D is fully undefined and reset signal present and (i) Q has no initial value
+ // (ii) initial value is same as reset value
if (sig_d.is_fully_undef() && sig_r.size() && (!has_init || val_init == val_rv)) {
+ // Q is permanently reset value
mod->connect(sig_q, val_rv);
goto delete_dff;
}
+ // If D is fully undefined and no reset signal and Q has an initial value
if (sig_d.is_fully_undef() && !sig_r.size() && has_init) {
+ // Q is permanently initial value
mod->connect(sig_q, val_init);
goto delete_dff;
}
+ // If D is fully constant and (i) no reset signal
+ // (ii) reset value is same as constant D
+ // and (a) has no initial value
+ // (b) initial value same as constant D
if (sig_d.is_fully_const() && (!sig_r.size() || val_rv == sig_d.as_const()) && (!has_init || val_init == sig_d.as_const())) {
+ // Q is permanently D
mod->connect(sig_q, sig_d);
goto delete_dff;
}
+ // If D input is same as Q output and (i) no reset signal
+ // (ii) no initial signal
+ // (iii) initial value is same as reset value
if (sig_d == sig_q && (sig_r.empty() || !has_init || val_init == val_rv)) {
+ // Q is permanently reset value or initial value
if (sig_r.size())
mod->connect(sig_q, val_rv);
- if (has_init)
+ else if (has_init)
mod->connect(sig_q, val_init);
goto delete_dff;
}
+ // If reset signal is present, and is fully constant
if (!sig_r.empty() && sig_r.is_fully_const())
{
+ // If reset value is permanently active or if reset is undefined
if (sig_r == val_rp || sig_r.is_fully_undef()) {
+ // Q is permanently reset value
mod->connect(sig_q, val_rv);
goto delete_dff;
}
log("Removing unused reset from %s (%s) from module %s.\n", log_id(dff), log_id(dff->type), log_id(mod));
- if (dff->type == "$adff") {
- dff->type = "$dff";
- dff->unsetPort("\\ARST");
- dff->unsetParam("\\ARST_POLARITY");
- dff->unsetParam("\\ARST_VALUE");
+ if (dff->type == ID($adff)) {
+ dff->type = ID($dff);
+ dff->unsetPort(ID(ARST));
+ dff->unsetParam(ID(ARST_POLARITY));
+ dff->unsetParam(ID(ARST_VALUE));
return true;
}
- log_assert(dff->type.substr(0,6) == "$_DFF_");
+ log_assert(dff->type.begins_with("$_DFF_"));
dff->type = stringf("$_DFF_%c_", + dff->type[6]);
- dff->unsetPort("\\R");
+ dff->unsetPort(ID(R));
+ }
+
+ // If enable signal is present, and is fully constant
+ if (!sig_e.empty() && sig_e.is_fully_const())
+ {
+ // If enable value is permanently inactive
+ if (sig_e != val_ep) {
+ // Q is permanently initial value
+ mod->connect(sig_q, val_init);
+ goto delete_dff;
+ }
+
+ log("Removing unused enable from %s (%s) from module %s.\n", log_id(dff), log_id(dff->type), log_id(mod));
+
+ if (dff->type == ID($dffe)) {
+ dff->type = ID($dff);
+ dff->unsetPort(ID(EN));
+ dff->unsetParam(ID(EN_POLARITY));
+ return true;
+ }
+
+ log_assert(dff->type.begins_with("$_DFFE_"));
+ dff->type = stringf("$_DFF_%c_", + dff->type[7]);
+ dff->unsetPort(ID(E));
+ }
+
+ if (sat && has_init && (!sig_r.size() || val_init == val_rv))
+ {
+ bool removed_sigbits = false;
+
+ ezSatPtr ez;
+ SatGen satgen(ez.get(), &assign_map);
+ pool<Cell*> sat_cells;
+
+ std::function<void(Cell*)> sat_import_cell = [&](Cell *c) {
+ if (!sat_cells.insert(c).second)
+ return;
+ if (!satgen.importCell(c))
+ return;
+ for (auto &conn : c->connections()) {
+ if (!c->input(conn.first))
+ continue;
+ for (auto bit : assign_map(conn.second))
+ if (bit2driver.count(bit))
+ sat_import_cell(bit2driver.at(bit));
+ }
+ };
+
+ // For each register bit, try to prove that it cannot change from the initial value. If so, remove it
+ for (int position = 0; position < GetSize(sig_d); position += 1) {
+ RTLIL::SigBit q_sigbit = sig_q[position];
+ RTLIL::SigBit d_sigbit = sig_d[position];
+
+ if ((!q_sigbit.wire) || (!d_sigbit.wire))
+ continue;
+
+ if (!bit2driver.count(d_sigbit))
+ continue;
+
+ sat_import_cell(bit2driver.at(d_sigbit));
+
+ RTLIL::State sigbit_init_val = val_init[position];
+ if (sigbit_init_val != State::S0 && sigbit_init_val != State::S1)
+ continue;
+
+ int init_sat_pi = satgen.importSigSpec(sigbit_init_val).front();
+ int q_sat_pi = satgen.importSigBit(q_sigbit);
+ int d_sat_pi = satgen.importSigBit(d_sigbit);
+
+ // Try to find out whether the register bit can change under some circumstances
+ bool counter_example_found = ez->solve(ez->IFF(q_sat_pi, init_sat_pi), ez->NOT(ez->IFF(d_sat_pi, init_sat_pi)));
+
+ // If the register bit cannot change, we can replace it with a constant
+ if (!counter_example_found)
+ {
+ log("Setting constant %d-bit at position %d on %s (%s) from module %s.\n", sigbit_init_val ? 1 : 0,
+ position, log_id(dff), log_id(dff->type), log_id(mod));
+
+ SigSpec tmp = dff->getPort(ID(D));
+ tmp[position] = sigbit_init_val;
+ dff->setPort(ID(D), tmp);
+
+ removed_sigbits = true;
+ }
+ }
+
+ if (removed_sigbits) {
+ handle_dff(mod, dff);
+ return true;
+ }
}
+
return false;
delete_dff:
log("Removing %s (%s) from module %s.\n", log_id(dff), log_id(dff->type), log_id(mod));
- remove_init_attr(dff->getPort("\\Q"));
+ remove_init_attr(dff->getPort(ID(Q)));
mod->remove(dff);
+
+ for (auto &entry : bit2driver)
+ if (entry.second == dff)
+ bit2driver.erase(entry.first);
+
return true;
}
struct OptRmdffPass : public Pass {
OptRmdffPass() : Pass("opt_rmdff", "remove DFFs with constant inputs") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
- log(" opt_rmdff [-keepdc] [selection]\n");
+ log(" opt_rmdff [-keepdc] [-sat] [selection]\n");
log("\n");
log("This pass identifies flip-flops with constant inputs and replaces them with\n");
log("a constant driver.\n");
log("\n");
+ log(" -sat\n");
+ log(" additionally invoke SAT solver to detect and remove flip-flops (with \n");
+ log(" non-constant inputs) that can also be replaced with a constant driver\n");
+ log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
int total_count = 0, total_initdrv = 0;
log_header(design, "Executing OPT_RMDFF pass (remove dff with constant values).\n");
keepdc = false;
+ sat = false;
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++) {
@@ -419,22 +568,28 @@ struct OptRmdffPass : public Pass {
keepdc = true;
continue;
}
+ if (args[argidx] == "-sat") {
+ sat = true;
+ continue;
+ }
break;
}
extra_args(args, argidx, design);
- for (auto module : design->selected_modules())
- {
+ for (auto module : design->selected_modules()) {
pool<SigBit> driven_bits;
dict<SigBit, State> init_bits;
assign_map.set(module);
dff_init_map.set(module);
+ mux_drivers.clear();
+ bit2driver.clear();
+ init_attributes.clear();
for (auto wire : module->wires())
{
- if (wire->attributes.count("\\init") != 0) {
- Const initval = wire->attributes.at("\\init");
+ if (wire->attributes.count(ID(init)) != 0) {
+ Const initval = wire->attributes.at(ID(init));
for (int i = 0; i < GetSize(initval) && i < GetSize(wire); i++)
if (initval[i] == State::S0 || initval[i] == State::S1)
dff_init_map.add(SigBit(wire, i), initval[i]);
@@ -453,40 +608,45 @@ struct OptRmdffPass : public Pass {
driven_bits.insert(bit);
}
}
- mux_drivers.clear();
std::vector<RTLIL::IdString> dff_list;
std::vector<RTLIL::IdString> dffsr_list;
std::vector<RTLIL::IdString> dlatch_list;
for (auto cell : module->cells())
{
- for (auto &conn : cell->connections())
- if (cell->output(conn.first) || !cell->known())
- for (auto bit : assign_map(conn.second))
+ for (auto &conn : cell->connections()) {
+ bool is_output = cell->output(conn.first);
+ if (is_output || !cell->known())
+ for (auto bit : assign_map(conn.second)) {
+ if (is_output)
+ bit2driver[bit] = cell;
driven_bits.insert(bit);
+ }
+ }
- if (cell->type == "$mux" || cell->type == "$pmux") {
- if (cell->getPort("\\A").size() == cell->getPort("\\B").size())
- mux_drivers.insert(assign_map(cell->getPort("\\Y")), cell);
+ if (cell->type.in(ID($mux), ID($pmux))) {
+ if (cell->getPort(ID::A).size() == cell->getPort(ID::B).size())
+ mux_drivers.insert(assign_map(cell->getPort(ID::Y)), cell);
continue;
}
if (!design->selected(module, cell))
continue;
- if (cell->type.in("$_DFFSR_NNN_", "$_DFFSR_NNP_", "$_DFFSR_NPN_", "$_DFFSR_NPP_",
- "$_DFFSR_PNN_", "$_DFFSR_PNP_", "$_DFFSR_PPN_", "$_DFFSR_PPP_", "$dffsr",
- "$_DLATCHSR_NNN_", "$_DLATCHSR_NNP_", "$_DLATCHSR_NPN_", "$_DLATCHSR_NPP_",
- "$_DLATCHSR_PNN_", "$_DLATCHSR_PNP_", "$_DLATCHSR_PPN_", "$_DLATCHSR_PPP_", "$dlatchsr"))
+ if (cell->type.in(ID($_DFFSR_NNN_), ID($_DFFSR_NNP_), ID($_DFFSR_NPN_), ID($_DFFSR_NPP_),
+ ID($_DFFSR_PNN_), ID($_DFFSR_PNP_), ID($_DFFSR_PPN_), ID($_DFFSR_PPP_), ID($dffsr),
+ ID($_DLATCHSR_NNN_), ID($_DLATCHSR_NNP_), ID($_DLATCHSR_NPN_), ID($_DLATCHSR_NPP_),
+ ID($_DLATCHSR_PNN_), ID($_DLATCHSR_PNP_), ID($_DLATCHSR_PPN_), ID($_DLATCHSR_PPP_), ID($dlatchsr)))
dffsr_list.push_back(cell->name);
- if (cell->type.in("$_FF_", "$_DFF_N_", "$_DFF_P_",
- "$_DFF_NN0_", "$_DFF_NN1_", "$_DFF_NP0_", "$_DFF_NP1_",
- "$_DFF_PN0_", "$_DFF_PN1_", "$_DFF_PP0_", "$_DFF_PP1_",
- "$ff", "$dff", "$adff"))
+ if (cell->type.in(ID($_FF_), ID($_DFF_N_), ID($_DFF_P_),
+ ID($_DFF_NN0_), ID($_DFF_NN1_), ID($_DFF_NP0_), ID($_DFF_NP1_),
+ ID($_DFF_PN0_), ID($_DFF_PN1_), ID($_DFF_PP0_), ID($_DFF_PP1_),
+ ID($_DFFE_NN_), ID($_DFFE_NP_), ID($_DFFE_PN_), ID($_DFFE_PP_),
+ ID($ff), ID($dff), ID($dffe), ID($adff)))
dff_list.push_back(cell->name);
- if (cell->type.in("$dlatch", "$_DLATCH_P_", "$_DLATCH_N_"))
+ if (cell->type.in(ID($dlatch), ID($_DLATCH_P_), ID($_DLATCH_N_)))
dlatch_list.push_back(cell->name);
}
@@ -534,6 +694,8 @@ struct OptRmdffPass : public Pass {
assign_map.clear();
mux_drivers.clear();
+ bit2driver.clear();
+ init_attributes.clear();
if (total_count || total_initdrv)
design->scratchpad_set_bool("opt.did_something", true);
diff --git a/passes/opt/opt_share.cc b/passes/opt/opt_share.cc
new file mode 100644
index 000000000..f59f978a6
--- /dev/null
+++ b/passes/opt/opt_share.cc
@@ -0,0 +1,657 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * 2019 Bogdan Vukobratovic <bogdan.vukobratovic@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
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "kernel/log.h"
+#include "kernel/register.h"
+#include "kernel/rtlil.h"
+#include "kernel/sigtools.h"
+#include <algorithm>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+SigMap assign_map;
+
+struct OpMuxConn {
+ RTLIL::SigSpec sig;
+ RTLIL::Cell *mux;
+ RTLIL::Cell *op;
+ int mux_port_id;
+ int mux_port_offset;
+ int op_outsig_offset;
+
+ bool operator<(const OpMuxConn &other) const
+ {
+ if (mux != other.mux)
+ return mux < other.mux;
+
+ if (mux_port_id != other.mux_port_id)
+ return mux_port_id < other.mux_port_id;
+
+ return mux_port_offset < other.mux_port_offset;
+ }
+};
+
+// Helper class to track additiona information about a SigSpec, like whether it is signed and the semantics of the port it is connected to
+struct ExtSigSpec {
+ RTLIL::SigSpec sig;
+ RTLIL::SigSpec sign;
+ bool is_signed;
+ RTLIL::IdString semantics;
+
+ ExtSigSpec() {}
+
+ ExtSigSpec(RTLIL::SigSpec s, RTLIL::SigSpec sign = RTLIL::Const(0, 1), bool is_signed = false, RTLIL::IdString semantics = RTLIL::IdString()) : sig(s), sign(sign), is_signed(is_signed), semantics(semantics) {}
+
+ bool empty() const { return sig.empty(); }
+
+ bool operator<(const ExtSigSpec &other) const
+ {
+ if (sig != other.sig)
+ return sig < other.sig;
+
+ if (sign != other.sign)
+ return sign < other.sign;
+
+ if (is_signed != other.is_signed)
+ return is_signed < other.is_signed;
+
+ return semantics < other.semantics;
+ }
+
+ bool operator==(const RTLIL::SigSpec &other) const { return (sign != RTLIL::Const(0, 1)) ? false : sig == other; }
+ bool operator==(const ExtSigSpec &other) const { return is_signed == other.is_signed && sign == other.sign && sig == other.sig && semantics == other.semantics; }
+};
+
+#define FINE_BITWISE_OPS ID($_AND_), ID($_NAND_), ID($_OR_), ID($_NOR_), ID($_XOR_), ID($_XNOR_), ID($_ANDNOT_), ID($_ORNOT_)
+
+#define BITWISE_OPS FINE_BITWISE_OPS, ID($and), ID($or), ID($xor), ID($xnor)
+
+#define REDUCTION_OPS ID($reduce_and), ID($reduce_or), ID($reduce_xor), ID($reduce_xnor), ID($reduce_bool), ID($reduce_nand)
+
+#define LOGICAL_OPS ID($logic_and), ID($logic_or)
+
+#define SHIFT_OPS ID($shl), ID($shr), ID($sshl), ID($sshr), ID($shift), ID($shiftx)
+
+#define RELATIONAL_OPS ID($lt), ID($le), ID($eq), ID($ne), ID($eqx), ID($nex), ID($ge), ID($gt)
+
+bool cell_supported(RTLIL::Cell *cell)
+{
+ if (cell->type.in(ID($alu))) {
+ RTLIL::SigSpec sig_bi = cell->getPort(ID(BI));
+ RTLIL::SigSpec sig_ci = cell->getPort(ID(CI));
+
+ if (sig_bi.is_fully_const() && sig_ci.is_fully_const() && sig_bi == sig_ci)
+ return true;
+ } else if (cell->type.in(LOGICAL_OPS, SHIFT_OPS, BITWISE_OPS, RELATIONAL_OPS, ID($add), ID($sub), ID($mul), ID($div), ID($mod), ID($concat))) {
+ return true;
+ }
+
+ return false;
+}
+
+std::map<IdString, IdString> mergeable_type_map;
+
+bool mergeable(RTLIL::Cell *a, RTLIL::Cell *b)
+{
+ if (mergeable_type_map.empty()) {
+ mergeable_type_map.insert({ID($sub), ID($add)});
+ }
+ auto a_type = a->type;
+ if (mergeable_type_map.count(a_type))
+ a_type = mergeable_type_map.at(a_type);
+
+ auto b_type = b->type;
+ if (mergeable_type_map.count(b_type))
+ b_type = mergeable_type_map.at(b_type);
+
+ return a_type == b_type;
+}
+
+RTLIL::IdString decode_port_semantics(RTLIL::Cell *cell, RTLIL::IdString port_name)
+{
+ if (cell->type.in(ID($lt), ID($le), ID($ge), ID($gt), ID($div), ID($mod), ID($concat), SHIFT_OPS) && port_name == ID::B)
+ return port_name;
+
+ return "";
+}
+
+RTLIL::SigSpec decode_port_sign(RTLIL::Cell *cell, RTLIL::IdString port_name) {
+
+ if (cell->type == ID($alu) && port_name == ID::B)
+ return cell->getPort(ID(BI));
+ else if (cell->type == ID($sub) && port_name == ID::B)
+ return RTLIL::Const(1, 1);
+
+ return RTLIL::Const(0, 1);
+}
+
+bool decode_port_signed(RTLIL::Cell *cell, RTLIL::IdString port_name)
+{
+ if (cell->type.in(BITWISE_OPS, LOGICAL_OPS))
+ return false;
+
+ if (cell->hasParam(port_name.str() + "_SIGNED"))
+ return cell->getParam(port_name.str() + "_SIGNED").as_bool();
+
+ return false;
+}
+
+ExtSigSpec decode_port(RTLIL::Cell *cell, RTLIL::IdString port_name, SigMap *sigmap)
+{
+ auto sig = (*sigmap)(cell->getPort(port_name));
+
+ RTLIL::SigSpec sign = decode_port_sign(cell, port_name);
+ RTLIL::IdString semantics = decode_port_semantics(cell, port_name);
+
+ bool is_signed = decode_port_signed(cell, port_name);
+
+ return ExtSigSpec(sig, sign, is_signed, semantics);
+}
+
+void merge_operators(RTLIL::Module *module, RTLIL::Cell *mux, const std::vector<OpMuxConn> &ports, const ExtSigSpec &operand)
+{
+ std::vector<ExtSigSpec> muxed_operands;
+ int max_width = 0;
+ for (const auto& p : ports) {
+ auto op = p.op;
+
+ RTLIL::IdString muxed_port_name = ID::A;
+ if (decode_port(op, ID::A, &assign_map) == operand)
+ muxed_port_name = ID::B;
+
+ auto operand = decode_port(op, muxed_port_name, &assign_map);
+ if (operand.sig.size() > max_width)
+ max_width = operand.sig.size();
+
+ muxed_operands.push_back(operand);
+ }
+
+ auto shared_op = ports[0].op;
+
+ if (std::any_of(muxed_operands.begin(), muxed_operands.end(), [&](ExtSigSpec &op) { return op.sign != muxed_operands[0].sign; }))
+ max_width = std::max(max_width, shared_op->getParam(ID(Y_WIDTH)).as_int());
+
+
+ for (auto &operand : muxed_operands)
+ operand.sig.extend_u0(max_width, operand.is_signed);
+
+ for (const auto& p : ports) {
+ auto op = p.op;
+ if (op == shared_op)
+ continue;
+ module->remove(op);
+ }
+
+ for (auto &muxed_op : muxed_operands)
+ if (muxed_op.sign != muxed_operands[0].sign)
+ muxed_op = ExtSigSpec(module->Neg(NEW_ID, muxed_op.sig, muxed_op.is_signed));
+
+ RTLIL::SigSpec mux_y = mux->getPort(ID::Y);
+ RTLIL::SigSpec mux_a = mux->getPort(ID::A);
+ RTLIL::SigSpec mux_b = mux->getPort(ID::B);
+ RTLIL::SigSpec mux_s = mux->getPort(ID(S));
+
+ RTLIL::SigSpec shared_pmux_a = RTLIL::Const(RTLIL::State::Sx, max_width);
+ RTLIL::SigSpec shared_pmux_b;
+ RTLIL::SigSpec shared_pmux_s;
+
+ int conn_width = ports[0].sig.size();
+ int conn_offset = ports[0].mux_port_offset;
+
+ shared_op->setPort(ID::Y, shared_op->getPort(ID::Y).extract(0, conn_width));
+
+ if (mux->type == ID($pmux)) {
+ shared_pmux_s = RTLIL::SigSpec();
+
+ for (const auto &p : ports) {
+ shared_pmux_s.append(mux_s[p.mux_port_id]);
+ mux_b.replace(p.mux_port_id * mux_a.size() + conn_offset, shared_op->getPort(ID::Y));
+ }
+ } else {
+ shared_pmux_s = RTLIL::SigSpec{mux_s, module->Not(NEW_ID, mux_s)};
+ mux_a.replace(conn_offset, shared_op->getPort(ID::Y));
+ mux_b.replace(conn_offset, shared_op->getPort(ID::Y));
+ }
+
+ mux->setPort(ID::A, mux_a);
+ mux->setPort(ID::B, mux_b);
+ mux->setPort(ID::Y, mux_y);
+ mux->setPort(ID(S), mux_s);
+
+ for (const auto &op : muxed_operands)
+ shared_pmux_b.append(op.sig);
+
+ auto mux_to_oper = module->Pmux(NEW_ID, shared_pmux_a, shared_pmux_b, shared_pmux_s);
+
+ if (shared_op->type.in(ID($alu))) {
+ RTLIL::SigSpec alu_x = shared_op->getPort(ID(X));
+ RTLIL::SigSpec alu_co = shared_op->getPort(ID(CO));
+
+ shared_op->setPort(ID(X), alu_x.extract(0, conn_width));
+ shared_op->setPort(ID(CO), alu_co.extract(0, conn_width));
+ }
+
+ bool is_fine = shared_op->type.in(FINE_BITWISE_OPS);
+
+ if (!is_fine)
+ shared_op->setParam(ID(Y_WIDTH), conn_width);
+
+ if (decode_port(shared_op, ID::A, &assign_map) == operand) {
+ shared_op->setPort(ID::B, mux_to_oper);
+ if (!is_fine)
+ shared_op->setParam(ID(B_WIDTH), max_width);
+ } else {
+ shared_op->setPort(ID::A, mux_to_oper);
+ if (!is_fine)
+ shared_op->setParam(ID(A_WIDTH), max_width);
+ }
+}
+
+typedef struct {
+ RTLIL::Cell *mux;
+ std::vector<OpMuxConn> ports;
+ ExtSigSpec shared_operand;
+} merged_op_t;
+
+
+template <typename T> void remove_val(std::vector<T> &v, const std::vector<T> &vals)
+{
+ auto val_iter = vals.rbegin();
+ for (auto i = v.rbegin(); i != v.rend(); ++i)
+ if ((val_iter != vals.rend()) && (*i == *val_iter)) {
+ v.erase(i.base() - 1);
+ ++val_iter;
+ }
+}
+
+void check_muxed_operands(std::vector<const OpMuxConn *> &ports, const ExtSigSpec &shared_operand)
+{
+ auto it = ports.begin();
+ ExtSigSpec seed;
+
+ while (it != ports.end()) {
+ auto p = *it;
+ auto op = p->op;
+
+ RTLIL::IdString muxed_port_name = ID::A;
+ if (decode_port(op, ID::A, &assign_map) == shared_operand) {
+ muxed_port_name = ID::B;
+ }
+
+ auto operand = decode_port(op, muxed_port_name, &assign_map);
+
+ if (seed.empty())
+ seed = operand;
+
+ if (operand.is_signed != seed.is_signed) {
+ ports.erase(it);
+ } else {
+ ++it;
+ }
+ }
+}
+
+ExtSigSpec find_shared_operand(const OpMuxConn* seed, std::vector<const OpMuxConn *> &ports, const std::map<ExtSigSpec, std::set<RTLIL::Cell *>> &operand_to_users)
+{
+ std::set<RTLIL::Cell *> ops_using_operand;
+ std::set<RTLIL::Cell *> ops_set;
+ for(const auto& p: ports)
+ ops_set.insert(p->op);
+
+ ExtSigSpec oper;
+
+ auto op_a = seed->op;
+
+ for (RTLIL::IdString port_name : {ID::A, ID::B}) {
+ oper = decode_port(op_a, port_name, &assign_map);
+ auto operand_users = operand_to_users.at(oper);
+
+ if (operand_users.size() == 1)
+ continue;
+
+ ops_using_operand.clear();
+ for (auto mux_ops: ops_set)
+ if (operand_users.count(mux_ops))
+ ops_using_operand.insert(mux_ops);
+
+ if (ops_using_operand.size() > 1) {
+ ports.erase(std::remove_if(ports.begin(), ports.end(), [&](const OpMuxConn *p) { return !ops_using_operand.count(p->op); }),
+ ports.end());
+ return oper;
+ }
+ }
+
+ return ExtSigSpec();
+}
+
+dict<RTLIL::SigSpec, OpMuxConn> find_valid_op_mux_conns(RTLIL::Module *module, dict<RTLIL::SigBit, RTLIL::SigSpec> &op_outbit_to_outsig,
+ dict<RTLIL::SigSpec, RTLIL::Cell *> outsig_to_operator,
+ dict<RTLIL::SigBit, RTLIL::SigSpec> &op_aux_to_outsig)
+{
+ dict<RTLIL::SigSpec, int> op_outsig_user_track;
+ dict<RTLIL::SigSpec, OpMuxConn> op_mux_conn_map;
+
+ std::function<void(RTLIL::SigSpec)> remove_outsig = [&](RTLIL::SigSpec outsig) {
+ for (auto op_outbit : outsig)
+ op_outbit_to_outsig.erase(op_outbit);
+
+ if (op_mux_conn_map.count(outsig))
+ op_mux_conn_map.erase(outsig);
+ };
+
+ std::function<void(RTLIL::SigBit)> remove_outsig_from_aux_bit = [&](RTLIL::SigBit auxbit) {
+ auto aux_outsig = op_aux_to_outsig.at(auxbit);
+ auto op = outsig_to_operator.at(aux_outsig);
+ auto op_outsig = assign_map(op->getPort(ID::Y));
+ remove_outsig(op_outsig);
+
+ for (auto aux_outbit : aux_outsig)
+ op_aux_to_outsig.erase(aux_outbit);
+ };
+
+ std::function<void(RTLIL::Cell *)> find_op_mux_conns = [&](RTLIL::Cell *mux) {
+ RTLIL::SigSpec sig;
+ int mux_port_size;
+
+ if (mux->type.in(ID($mux), ID($_MUX_))) {
+ mux_port_size = mux->getPort(ID::A).size();
+ sig = RTLIL::SigSpec{mux->getPort(ID::B), mux->getPort(ID::A)};
+ } else {
+ mux_port_size = mux->getPort(ID::A).size();
+ sig = mux->getPort(ID::B);
+ }
+
+ auto mux_insig = assign_map(sig);
+
+ for (int i = 0; i < mux_insig.size(); ++i) {
+ if (op_aux_to_outsig.count(mux_insig[i])) {
+ remove_outsig_from_aux_bit(mux_insig[i]);
+ continue;
+ }
+
+ if (!op_outbit_to_outsig.count(mux_insig[i]))
+ continue;
+
+ auto op_outsig = op_outbit_to_outsig.at(mux_insig[i]);
+
+ if (op_mux_conn_map.count(op_outsig)) {
+ remove_outsig(op_outsig);
+ continue;
+ }
+
+ int mux_port_id = i / mux_port_size;
+ int mux_port_offset = i % mux_port_size;
+
+ int op_outsig_offset;
+ for (op_outsig_offset = 0; op_outsig[op_outsig_offset] != mux_insig[i]; ++op_outsig_offset)
+ ;
+
+ int j = op_outsig_offset;
+ do {
+ if (!op_outbit_to_outsig.count(mux_insig[i]))
+ break;
+
+ if (op_outbit_to_outsig.at(mux_insig[i]) != op_outsig)
+ break;
+
+ ++i;
+ ++j;
+ } while ((i / mux_port_size == mux_port_id) && (j < op_outsig.size()));
+
+ int op_conn_width = j - op_outsig_offset;
+ OpMuxConn inp = {
+ op_outsig.extract(op_outsig_offset, op_conn_width),
+ mux,
+ outsig_to_operator.at(op_outsig),
+ mux_port_id,
+ mux_port_offset,
+ op_outsig_offset,
+ };
+
+ op_mux_conn_map[op_outsig] = inp;
+
+ --i;
+ }
+ };
+
+ std::function<void(RTLIL::SigSpec)> remove_connected_ops = [&](RTLIL::SigSpec sig) {
+ auto mux_insig = assign_map(sig);
+ for (auto outbit : mux_insig) {
+ if (op_aux_to_outsig.count(outbit)) {
+ remove_outsig_from_aux_bit(outbit);
+ continue;
+ }
+
+ if (!op_outbit_to_outsig.count(outbit))
+ continue;
+
+ remove_outsig(op_outbit_to_outsig.at(outbit));
+ }
+ };
+
+ for (auto cell : module->cells()) {
+ if (cell->type.in(ID($mux), ID($_MUX_), ID($pmux))) {
+ remove_connected_ops(cell->getPort(ID(S)));
+ find_op_mux_conns(cell);
+ } else {
+ for (auto &conn : cell->connections())
+ if (cell->input(conn.first))
+ remove_connected_ops(conn.second);
+ }
+ }
+
+ for (auto w : module->wires()) {
+ if (!w->port_output)
+ continue;
+
+ remove_connected_ops(w);
+ }
+
+ return op_mux_conn_map;
+}
+
+struct OptSharePass : public Pass {
+ OptSharePass() : Pass("opt_share", "merge mutually exclusive cells of the same type that share an input signal") {}
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" opt_share [selection]\n");
+ log("\n");
+
+ log("This pass identifies mutually exclusive cells of the same type that:\n");
+ log(" (a) share an input signal,\n");
+ log(" (b) drive the same $mux, $_MUX_, or $pmux multiplexing cell,\n");
+ log("\n");
+ log("allowing the cell to be merged and the multiplexer to be moved from\n");
+ log("multiplexing its output to multiplexing the non-shared input signals.\n");
+ log("\n");
+ }
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+
+ log_header(design, "Executing OPT_SHARE pass.\n");
+
+ extra_args(args, 1, design);
+ for (auto module : design->selected_modules()) {
+ assign_map.clear();
+ assign_map.set(module);
+
+ std::map<ExtSigSpec, std::set<RTLIL::Cell *>> operand_to_users;
+ dict<RTLIL::SigSpec, RTLIL::Cell *> outsig_to_operator;
+ dict<RTLIL::SigBit, RTLIL::SigSpec> op_outbit_to_outsig;
+ dict<RTLIL::SigBit, RTLIL::SigSpec> op_aux_to_outsig;
+ bool any_shared_operands = false;
+ std::vector<ExtSigSpec> op_insigs;
+
+ for (auto cell : module->cells()) {
+ if (!cell_supported(cell))
+ continue;
+
+ if (cell->type == ID($alu)) {
+ for (RTLIL::IdString port_name : {ID(X), ID(CO)}) {
+ auto mux_insig = assign_map(cell->getPort(port_name));
+ outsig_to_operator[mux_insig] = cell;
+ for (auto outbit : mux_insig)
+ op_aux_to_outsig[outbit] = mux_insig;
+ }
+ }
+
+ auto mux_insig = assign_map(cell->getPort(ID::Y));
+ outsig_to_operator[mux_insig] = cell;
+ for (auto outbit : mux_insig)
+ op_outbit_to_outsig[outbit] = mux_insig;
+
+ for (RTLIL::IdString port_name : {ID::A, ID::B}) {
+ auto op_insig = decode_port(cell, port_name, &assign_map);
+ op_insigs.push_back(op_insig);
+ operand_to_users[op_insig].insert(cell);
+ if (operand_to_users[op_insig].size() > 1)
+ any_shared_operands = true;
+ }
+ }
+
+ if (!any_shared_operands)
+ continue;
+
+ // Operator outputs need to be exclusively connected to the $mux inputs in order to be mergeable. Hence we count to
+ // how many points are operator output bits connected.
+ dict<RTLIL::SigSpec, OpMuxConn> op_mux_conn_map =
+ find_valid_op_mux_conns(module, op_outbit_to_outsig, outsig_to_operator, op_aux_to_outsig);
+
+ // Group op connections connected to same ports of the same $mux. Sort them in ascending order of their port offset
+ dict<RTLIL::Cell*, std::vector<std::set<OpMuxConn>>> mux_port_op_conns;
+ for (auto& val: op_mux_conn_map) {
+ OpMuxConn p = val.second;
+ auto& mux_port_conns = mux_port_op_conns[p.mux];
+
+ if (mux_port_conns.size() == 0) {
+ int mux_port_num;
+
+ if (p.mux->type.in(ID($mux), ID($_MUX_)))
+ mux_port_num = 2;
+ else
+ mux_port_num = p.mux->getPort(ID(S)).size();
+
+ mux_port_conns.resize(mux_port_num);
+ }
+
+ mux_port_conns[p.mux_port_id].insert(p);
+ }
+
+ std::vector<merged_op_t> merged_ops;
+ for (auto& val: mux_port_op_conns) {
+
+ RTLIL::Cell* cell = val.first;
+ auto &mux_port_conns = val.second;
+
+ const OpMuxConn *seed = NULL;
+
+ // Look through the bits of the $mux inputs and see which of them are connected to the operator
+ // results. Operator results can be concatenated with other signals before led to the $mux.
+ while (true) {
+
+ // Remove either the merged ports from the last iteration or the seed that failed to yield a merger
+ if (seed != NULL) {
+ mux_port_conns[seed->mux_port_id].erase(*seed);
+ seed = NULL;
+ }
+
+ // For a new merger, find the seed op connection that starts at lowest port offset among port connections
+ for (auto &port_conns : mux_port_conns) {
+ if (!port_conns.size())
+ continue;
+
+ const OpMuxConn *next_p = &(*port_conns.begin());
+
+ if ((seed == NULL) || (seed->mux_port_offset > next_p->mux_port_offset))
+ seed = next_p;
+ }
+
+ // Cannot find the seed -> nothing to do for this $mux anymore
+ if (seed == NULL)
+ break;
+
+ // Find all other op connections that start from the same port offset, and whose ops can be merged with the seed op
+ std::vector<const OpMuxConn *> mergeable_conns;
+ for (auto &port_conns : mux_port_conns) {
+ if (!port_conns.size())
+ continue;
+
+ const OpMuxConn *next_p = &(*port_conns.begin());
+
+ if ((next_p->op_outsig_offset == seed->op_outsig_offset) &&
+ (next_p->mux_port_offset == seed->mux_port_offset) && mergeable(next_p->op, seed->op) &&
+ next_p->sig.size() == seed->sig.size())
+ mergeable_conns.push_back(next_p);
+ }
+
+ // We need at least two mergeable connections for the merger
+ if (mergeable_conns.size() < 2)
+ continue;
+
+ // Filter mergeable connections whose ops share an operand with seed connection's op
+ auto shared_operand = find_shared_operand(seed, mergeable_conns, operand_to_users);
+
+ if (shared_operand.empty())
+ continue;
+
+ check_muxed_operands(mergeable_conns, shared_operand);
+
+ if (mergeable_conns.size() < 2)
+ continue;
+
+ // Remember the combination for the merger
+ std::vector<OpMuxConn> merged_ports;
+ for (auto p : mergeable_conns) {
+ merged_ports.push_back(*p);
+ mux_port_conns[p->mux_port_id].erase(*p);
+ }
+
+ seed = NULL;
+
+ merged_ops.push_back(merged_op_t{cell, merged_ports, shared_operand});
+
+ design->scratchpad_set_bool("opt.did_something", true);
+ }
+
+ }
+
+ for (auto &shared : merged_ops) {
+ log(" Found cells that share an operand and can be merged by moving the %s %s in front "
+ "of "
+ "them:\n",
+ log_id(shared.mux->type), log_id(shared.mux));
+ for (const auto& op : shared.ports)
+ log(" %s\n", log_id(op.op));
+ log("\n");
+
+ merge_operators(module, shared.mux, shared.ports, shared.shared_operand);
+ }
+ }
+ }
+
+} OptSharePass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/opt/pmux2shiftx.cc b/passes/opt/pmux2shiftx.cc
new file mode 100644
index 000000000..92b5794ac
--- /dev/null
+++ b/passes/opt/pmux2shiftx.cc
@@ -0,0 +1,860 @@
+/*
+ * 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 OnehotDatabase
+{
+ Module *module;
+ const SigMap &sigmap;
+ bool verbose = false;
+ bool initialized = false;
+
+ pool<SigBit> init_ones;
+ dict<SigSpec, pool<SigSpec>> sig_sources_db;
+ dict<SigSpec, bool> sig_onehot_cache;
+ pool<SigSpec> recursion_guard;
+
+ OnehotDatabase(Module *module, const SigMap &sigmap) : module(module), sigmap(sigmap)
+ {
+ }
+
+ void initialize()
+ {
+ log_assert(!initialized);
+ initialized = true;
+
+ for (auto wire : module->wires())
+ {
+ auto it = wire->attributes.find(ID(init));
+ if (it == wire->attributes.end())
+ continue;
+
+ auto &val = it->second;
+ int width = std::max(GetSize(wire), GetSize(val));
+
+ for (int i = 0; i < width; i++)
+ if (val[i] == State::S1)
+ init_ones.insert(sigmap(SigBit(wire, i)));
+ }
+
+ for (auto cell : module->cells())
+ {
+ vector<SigSpec> inputs;
+ SigSpec output;
+
+ if (cell->type.in(ID($adff), ID($dff), ID($dffe), ID($dlatch), ID($ff)))
+ {
+ output = cell->getPort(ID(Q));
+ if (cell->type == ID($adff))
+ inputs.push_back(cell->getParam(ID(ARST_VALUE)));
+ inputs.push_back(cell->getPort(ID(D)));
+ }
+
+ if (cell->type.in(ID($mux), ID($pmux)))
+ {
+ output = cell->getPort(ID::Y);
+ inputs.push_back(cell->getPort(ID::A));
+ SigSpec B = cell->getPort(ID::B);
+ for (int i = 0; i < GetSize(B); i += GetSize(output))
+ inputs.push_back(B.extract(i, GetSize(output)));
+ }
+
+ if (!output.empty())
+ {
+ output = sigmap(output);
+ auto &srcs = sig_sources_db[output];
+ for (auto src : inputs) {
+ while (!src.empty() && src[GetSize(src)-1] == State::S0)
+ src.remove(GetSize(src)-1);
+ srcs.insert(sigmap(src));
+ }
+ }
+ }
+ }
+
+ void query_worker(const SigSpec &sig, bool &retval, bool &cache, int indent)
+ {
+ if (verbose)
+ log("%*s %s\n", indent, "", log_signal(sig));
+ log_assert(retval);
+
+ if (recursion_guard.count(sig)) {
+ if (verbose)
+ log("%*s - recursion\n", indent, "");
+ cache = false;
+ return;
+ }
+
+ auto it = sig_onehot_cache.find(sig);
+ if (it != sig_onehot_cache.end()) {
+ if (verbose)
+ log("%*s - cached (%s)\n", indent, "", it->second ? "true" : "false");
+ if (!it->second)
+ retval = false;
+ return;
+ }
+
+ bool found_init_ones = false;
+ for (auto bit : sig) {
+ if (init_ones.count(bit)) {
+ if (found_init_ones) {
+ if (verbose)
+ log("%*s - non-onehot init value\n", indent, "");
+ retval = false;
+ break;
+ }
+ found_init_ones = true;
+ }
+ }
+
+ if (retval)
+ {
+ if (sig.is_fully_const())
+ {
+ bool found_ones = false;
+ for (auto bit : sig) {
+ if (bit == State::S1) {
+ if (found_ones) {
+ if (verbose)
+ log("%*s - non-onehot constant\n", indent, "");
+ retval = false;
+ break;
+ }
+ found_ones = true;
+ }
+ }
+ }
+ else
+ {
+ auto srcs = sig_sources_db.find(sig);
+ if (srcs == sig_sources_db.end()) {
+ if (verbose)
+ log("%*s - no sources for non-const signal\n", indent, "");
+ retval = false;
+ } else {
+ for (auto &src : srcs->second) {
+ bool child_cache = true;
+ recursion_guard.insert(sig);
+ query_worker(src, retval, child_cache, indent+4);
+ recursion_guard.erase(sig);
+ if (!child_cache)
+ cache = false;
+ if (!retval)
+ break;
+ }
+ }
+ }
+ }
+
+ // it is always safe to cache a negative result
+ if (cache || !retval)
+ sig_onehot_cache[sig] = retval;
+ }
+
+ bool query(const SigSpec &sig)
+ {
+ bool retval = true;
+ bool cache = true;
+
+ if (verbose)
+ log("** ONEHOT QUERY START (%s)\n", log_signal(sig));
+
+ if (!initialized)
+ initialize();
+
+ query_worker(sig, retval, cache, 3);
+
+ if (verbose)
+ log("** ONEHOT QUERY RESULT = %s\n", retval ? "true" : "false");
+
+ // it is always safe to cache the root result of a query
+ if (!cache)
+ sig_onehot_cache[sig] = retval;
+
+ return retval;
+ }
+};
+
+struct Pmux2ShiftxPass : public Pass {
+ Pmux2ShiftxPass() : Pass("pmux2shiftx", "transform $pmux cells to $shiftx cells") { }
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" pmux2shiftx [options] [selection]\n");
+ log("\n");
+ log("This pass transforms $pmux cells to $shiftx cells.\n");
+ log("\n");
+ log(" -v, -vv\n");
+ log(" verbose output\n");
+ log("\n");
+ log(" -min_density <percentage>\n");
+ log(" specifies the minimum density for the shifter\n");
+ log(" default: 50\n");
+ log("\n");
+ log(" -min_choices <int>\n");
+ log(" specified the minimum number of choices for a control signal\n");
+ log(" default: 3\n");
+ log("\n");
+ log(" -onehot ignore|pmux|shiftx\n");
+ log(" select strategy for one-hot encoded control signals\n");
+ log(" default: pmux\n");
+ log("\n");
+ log(" -norange\n");
+ log(" disable $sub inference for \"range decoders\"\n");
+ log("\n");
+ }
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ int min_density = 50;
+ int min_choices = 3;
+ bool allow_onehot = false;
+ bool optimize_onehot = true;
+ bool verbose = false;
+ bool verbose_onehot = false;
+ bool norange = false;
+
+ log_header(design, "Executing PMUX2SHIFTX pass.\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ if (args[argidx] == "-min_density" && argidx+1 < args.size()) {
+ min_density = atoi(args[++argidx].c_str());
+ continue;
+ }
+ if (args[argidx] == "-min_choices" && argidx+1 < args.size()) {
+ min_choices = atoi(args[++argidx].c_str());
+ continue;
+ }
+ if (args[argidx] == "-onehot" && argidx+1 < args.size() && args[argidx+1] == "ignore") {
+ argidx++;
+ allow_onehot = false;
+ optimize_onehot = false;
+ continue;
+ }
+ if (args[argidx] == "-onehot" && argidx+1 < args.size() && args[argidx+1] == "pmux") {
+ argidx++;
+ allow_onehot = false;
+ optimize_onehot = true;
+ continue;
+ }
+ if (args[argidx] == "-onehot" && argidx+1 < args.size() && args[argidx+1] == "shiftx") {
+ argidx++;
+ allow_onehot = true;
+ optimize_onehot = false;
+ continue;
+ }
+ if (args[argidx] == "-v") {
+ verbose = true;
+ continue;
+ }
+ if (args[argidx] == "-vv") {
+ verbose = true;
+ verbose_onehot = true;
+ continue;
+ }
+ if (args[argidx] == "-norange") {
+ norange = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto module : design->selected_modules())
+ {
+ SigMap sigmap(module);
+ OnehotDatabase onehot_db(module, sigmap);
+ onehot_db.verbose = verbose_onehot;
+
+ dict<SigBit, pair<SigSpec, Const>> eqdb;
+
+ for (auto cell : module->cells())
+ {
+ if (cell->type == ID($eq))
+ {
+ dict<SigBit, State> bits;
+
+ SigSpec A = sigmap(cell->getPort(ID::A));
+ SigSpec B = sigmap(cell->getPort(ID::B));
+
+ int a_width = cell->getParam(ID(A_WIDTH)).as_int();
+ int b_width = cell->getParam(ID(B_WIDTH)).as_int();
+
+ if (a_width < b_width) {
+ bool a_signed = cell->getParam(ID(A_SIGNED)).as_int();
+ A.extend_u0(b_width, a_signed);
+ }
+
+ if (b_width < a_width) {
+ bool b_signed = cell->getParam(ID(B_SIGNED)).as_int();
+ B.extend_u0(a_width, b_signed);
+ }
+
+ for (int i = 0; i < GetSize(A); i++) {
+ SigBit a_bit = A[i], b_bit = B[i];
+ if (b_bit.wire && !a_bit.wire) {
+ std::swap(a_bit, b_bit);
+ }
+ if (!a_bit.wire || b_bit.wire)
+ goto next_cell;
+ if (bits.count(a_bit))
+ goto next_cell;
+ bits[a_bit] = b_bit.data;
+ }
+
+ if (GetSize(bits) > 20)
+ goto next_cell;
+
+ bits.sort();
+ pair<SigSpec, Const> entry;
+
+ for (auto it : bits) {
+ entry.first.append_bit(it.first);
+ entry.second.bits.push_back(it.second);
+ }
+
+ eqdb[sigmap(cell->getPort(ID::Y)[0])] = entry;
+ goto next_cell;
+ }
+
+ if (cell->type == ID($logic_not))
+ {
+ dict<SigBit, State> bits;
+
+ SigSpec A = sigmap(cell->getPort(ID::A));
+
+ for (int i = 0; i < GetSize(A); i++)
+ bits[A[i]] = State::S0;
+
+ bits.sort();
+ pair<SigSpec, Const> entry;
+
+ for (auto it : bits) {
+ entry.first.append_bit(it.first);
+ entry.second.bits.push_back(it.second);
+ }
+
+ eqdb[sigmap(cell->getPort(ID::Y)[0])] = entry;
+ goto next_cell;
+ }
+ next_cell:;
+ }
+
+ for (auto cell : module->selected_cells())
+ {
+ if (cell->type != ID($pmux))
+ continue;
+
+ string src = cell->get_src_attribute();
+ int width = cell->getParam(ID(WIDTH)).as_int();
+ int width_bits = ceil_log2(width);
+ int extwidth = width;
+
+ while (extwidth & (extwidth-1))
+ extwidth++;
+
+ dict<SigSpec, pool<int>> seldb;
+
+ SigSpec A = cell->getPort(ID::A);
+ SigSpec B = cell->getPort(ID::B);
+ SigSpec S = sigmap(cell->getPort(ID(S)));
+ for (int i = 0; i < GetSize(S); i++)
+ {
+ if (!eqdb.count(S[i]))
+ continue;
+
+ auto &entry = eqdb.at(S[i]);
+ seldb[entry.first].insert(i);
+ }
+
+ if (seldb.empty())
+ continue;
+
+ bool printed_pmux_header = false;
+
+ if (verbose) {
+ printed_pmux_header = true;
+ log("Inspecting $pmux cell %s/%s.\n", log_id(module), log_id(cell));
+ log(" data width: %d (next power-of-2 = %d, log2 = %d)\n", width, extwidth, width_bits);
+ }
+
+ SigSpec updated_S = cell->getPort(ID(S));
+ SigSpec updated_B = cell->getPort(ID::B);
+
+ while (!seldb.empty())
+ {
+ // pick the largest entry in seldb
+ SigSpec sig = seldb.begin()->first;
+ for (auto &it : seldb) {
+ if (GetSize(sig) < GetSize(it.first))
+ sig = it.first;
+ else if (GetSize(seldb.at(sig)) < GetSize(it.second))
+ sig = it.first;
+ }
+
+ // find the relevant choices
+ bool is_onehot = GetSize(sig) > 2;
+ dict<Const, int> choices;
+ for (int i : seldb.at(sig)) {
+ Const val = eqdb.at(S[i]).second;
+ int onebits = 0;
+ for (auto b : val.bits)
+ if (b == State::S1)
+ onebits++;
+ if (onebits > 1)
+ is_onehot = false;
+ choices[val] = i;
+ }
+
+ bool full_pmux = GetSize(choices) == GetSize(S);
+
+ // TBD: also find choices that are using signals that are subsets of the bits in "sig"
+
+ if (!verbose)
+ {
+ if (is_onehot && !allow_onehot && !optimize_onehot) {
+ seldb.erase(sig);
+ continue;
+ }
+
+ if (GetSize(choices) < min_choices) {
+ seldb.erase(sig);
+ continue;
+ }
+ }
+
+ if (!printed_pmux_header) {
+ printed_pmux_header = true;
+ log("Inspecting $pmux cell %s/%s.\n", log_id(module), log_id(cell));
+ log(" data width: %d (next power-of-2 = %d, log2 = %d)\n", width, extwidth, width_bits);
+ }
+
+ log(" checking ctrl signal %s\n", log_signal(sig));
+
+ auto print_choices = [&]() {
+ log(" table of choices:\n");
+ for (auto &it : choices)
+ log(" %3d: %s: %s\n", it.second, log_signal(it.first),
+ log_signal(B.extract(it.second*width, width)));
+ };
+
+ if (verbose)
+ {
+ if (is_onehot && !allow_onehot && !optimize_onehot) {
+ print_choices();
+ log(" ignoring one-hot encoding.\n");
+ seldb.erase(sig);
+ continue;
+ }
+
+ if (GetSize(choices) < min_choices) {
+ print_choices();
+ log(" insufficient choices.\n");
+ seldb.erase(sig);
+ continue;
+ }
+ }
+
+ if (is_onehot && optimize_onehot)
+ {
+ print_choices();
+ if (!onehot_db.query(sig))
+ {
+ log(" failed to detect onehot driver. do not optimize.\n");
+ }
+ else
+ {
+ log(" optimizing one-hot encoding.\n");
+ for (auto &it : choices)
+ {
+ const Const &val = it.first;
+ int index = -1;
+
+ for (int i = 0; i < GetSize(val); i++)
+ if (val[i] == State::S1) {
+ log_assert(index < 0);
+ index = i;
+ }
+
+ if (index < 0) {
+ log(" %3d: zero encoding.\n", it.second);
+ continue;
+ }
+
+ SigBit new_ctrl = sig[index];
+ log(" %3d: new crtl signal is %s.\n", it.second, log_signal(new_ctrl));
+ updated_S[it.second] = new_ctrl;
+ }
+ }
+ seldb.erase(sig);
+ continue;
+ }
+
+ // find the best permutation
+ vector<int> perm_new_from_old(GetSize(sig));
+ Const perm_xormask(State::S0, GetSize(sig));
+ {
+ vector<int> values(GetSize(choices));
+ vector<bool> used_src_columns(GetSize(sig));
+ vector<vector<bool>> columns(GetSize(sig), vector<bool>(GetSize(values)));
+
+ for (int i = 0; i < GetSize(choices); i++) {
+ Const val = choices.element(i)->first;
+ for (int k = 0; k < GetSize(val); k++)
+ if (val[k] == State::S1)
+ columns[k][i] = true;
+ }
+
+ for (int dst_col = GetSize(sig)-1; dst_col >= 0; dst_col--)
+ {
+ int best_src_col = -1;
+ bool best_inv = false;
+ int best_maxval = 0;
+ int best_delta = 0;
+
+ // find best src column for this dst column
+ for (int src_col = 0; src_col < GetSize(sig); src_col++)
+ {
+ if (used_src_columns[src_col])
+ continue;
+
+ int this_maxval = 0;
+ int this_minval = 1 << 30;
+
+ int this_inv_maxval = 0;
+ int this_inv_minval = 1 << 30;
+
+ for (int i = 0; i < GetSize(values); i++)
+ {
+ int val = values[i];
+ int inv_val = val;
+
+ if (columns[src_col][i])
+ val |= 1 << dst_col;
+ else
+ inv_val |= 1 << dst_col;
+
+ this_maxval = std::max(this_maxval, val);
+ this_minval = std::min(this_minval, val);
+
+ this_inv_maxval = std::max(this_inv_maxval, inv_val);
+ this_inv_minval = std::min(this_inv_minval, inv_val);
+ }
+
+ int this_delta = this_maxval - this_minval;
+ int this_inv_delta = this_maxval - this_minval;
+ bool this_inv = false;
+
+ if (!norange && this_delta != this_inv_delta)
+ this_inv = this_inv_delta < this_delta;
+ else if (this_maxval != this_inv_maxval)
+ this_inv = this_inv_maxval < this_maxval;
+
+ if (this_inv) {
+ this_delta = this_inv_delta;
+ this_maxval = this_inv_maxval;
+ this_minval = this_inv_minval;
+ }
+
+ bool this_is_better = false;
+
+ if (best_src_col < 0)
+ this_is_better = true;
+ else if (!norange && this_delta != best_delta)
+ this_is_better = this_delta < best_delta;
+ else if (this_maxval != best_maxval)
+ this_is_better = this_maxval < best_maxval;
+ else
+ this_is_better = sig[best_src_col] < sig[src_col];
+
+ if (this_is_better) {
+ best_src_col = src_col;
+ best_inv = this_inv;
+ best_maxval = this_maxval;
+ best_delta = this_delta;
+ }
+ }
+
+ used_src_columns[best_src_col] = true;
+ perm_new_from_old[dst_col] = best_src_col;
+ perm_xormask[dst_col] = best_inv ? State::S1 : State::S0;
+ }
+ }
+
+ // permutated sig
+ SigSpec perm_sig(State::S0, GetSize(sig));
+ for (int i = 0; i < GetSize(sig); i++)
+ perm_sig[i] = sig[perm_new_from_old[i]];
+
+ log(" best permutation: %s\n", log_signal(perm_sig));
+ log(" best xor mask: %s\n", log_signal(perm_xormask));
+
+ // permutated choices
+ int min_choice = 1 << 30;
+ int max_choice = -1;
+ dict<Const, int> perm_choices;
+
+ for (auto &it : choices)
+ {
+ Const &old_c = it.first;
+ Const new_c(State::S0, GetSize(old_c));
+
+ for (int i = 0; i < GetSize(old_c); i++)
+ new_c[i] = old_c[perm_new_from_old[i]];
+
+ Const new_c_before_xor = new_c;
+ new_c = const_xor(new_c, perm_xormask, false, false, GetSize(new_c));
+
+ perm_choices[new_c] = it.second;
+
+ min_choice = std::min(min_choice, new_c.as_int());
+ max_choice = std::max(max_choice, new_c.as_int());
+
+ log(" %3d: %s -> %s -> %s: %s\n", it.second, log_signal(old_c), log_signal(new_c_before_xor),
+ log_signal(new_c), log_signal(B.extract(it.second*width, width)));
+ }
+
+ int range_density = 100*GetSize(choices) / (max_choice-min_choice+1);
+ int absolute_density = 100*GetSize(choices) / (max_choice+1);
+
+ log(" choices: %d\n", GetSize(choices));
+ log(" min choice: %d\n", min_choice);
+ log(" max choice: %d\n", max_choice);
+ log(" range density: %d%%\n", range_density);
+ log(" absolute density: %d%%\n", absolute_density);
+
+ if (full_pmux) {
+ int full_density = 100*GetSize(choices) / (1 << GetSize(sig));
+ log(" full density: %d%%\n", full_density);
+ if (full_density < min_density) {
+ full_pmux = false;
+ } else {
+ min_choice = 0;
+ max_choice = (1 << GetSize(sig))-1;
+ log(" update to full case.\n");
+ log(" new min choice: %d\n", min_choice);
+ log(" new max choice: %d\n", max_choice);
+ }
+ }
+
+ bool full_case = (min_choice == 0) && (max_choice == (1 << GetSize(sig))-1) && (full_pmux || max_choice+1 == GetSize(choices));
+ log(" full case: %s\n", full_case ? "true" : "false");
+
+ // check density percentages
+ Const offset(State::S0, GetSize(sig));
+ if (!norange && absolute_density < min_density && range_density >= min_density)
+ {
+ offset = Const(min_choice, GetSize(sig));
+ log(" offset: %s\n", log_signal(offset));
+
+ min_choice -= offset.as_int();
+ max_choice -= offset.as_int();
+
+ dict<Const, int> new_perm_choices;
+ for (auto &it : perm_choices)
+ new_perm_choices[const_sub(it.first, offset, false, false, GetSize(sig))] = it.second;
+ perm_choices.swap(new_perm_choices);
+ } else
+ if (absolute_density < min_density) {
+ log(" insufficient density.\n");
+ seldb.erase(sig);
+ continue;
+ }
+
+ // creat cmp signal
+ SigSpec cmp = perm_sig;
+ if (perm_xormask.as_bool())
+ cmp = module->Xor(NEW_ID, cmp, perm_xormask, false, src);
+ if (offset.as_bool())
+ cmp = module->Sub(NEW_ID, cmp, offset, false, src);
+
+ // create enable signal
+ SigBit en = State::S1;
+ if (!full_case) {
+ Const enable_mask(State::S0, max_choice+1);
+ for (auto &it : perm_choices)
+ enable_mask[it.first.as_int()] = State::S1;
+ en = module->addWire(NEW_ID);
+ module->addShift(NEW_ID, enable_mask, cmp, en, false, src);
+ }
+
+ // create data signal
+ SigSpec data(State::Sx, (max_choice+1)*extwidth);
+ if (full_pmux) {
+ for (int i = 0; i <= max_choice; i++)
+ data.replace(i*extwidth, A);
+ }
+ for (auto &it : perm_choices) {
+ int position = it.first.as_int()*extwidth;
+ int data_index = it.second;
+ data.replace(position, B.extract(data_index*width, width));
+ updated_S[data_index] = State::S0;
+ updated_B.replace(data_index*width, SigSpec(State::Sx, width));
+ }
+
+ // create shiftx cell
+ SigSpec shifted_cmp = {cmp, SigSpec(State::S0, width_bits)};
+ SigSpec outsig = module->addWire(NEW_ID, width);
+ Cell *c = module->addShiftx(NEW_ID, data, shifted_cmp, outsig, false, src);
+ updated_S.append(en);
+ updated_B.append(outsig);
+ log(" created $shiftx cell %s.\n", log_id(c));
+
+ // remove this sig and continue with the next block
+ seldb.erase(sig);
+ }
+
+ // update $pmux cell
+ cell->setPort(ID(S), updated_S);
+ cell->setPort(ID::B, updated_B);
+ cell->setParam(ID(S_WIDTH), GetSize(updated_S));
+ }
+ }
+ }
+} Pmux2ShiftxPass;
+
+struct OnehotPass : public Pass {
+ OnehotPass() : Pass("onehot", "optimize $eq cells for onehot signals") { }
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" onehot [options] [selection]\n");
+ log("\n");
+ log("This pass optimizes $eq cells that compare one-hot signals against constants\n");
+ log("\n");
+ log(" -v, -vv\n");
+ log(" verbose output\n");
+ log("\n");
+ }
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ bool verbose = false;
+ bool verbose_onehot = false;
+
+ log_header(design, "Executing ONEHOT pass.\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ if (args[argidx] == "-v") {
+ verbose = true;
+ continue;
+ }
+ if (args[argidx] == "-vv") {
+ verbose = true;
+ verbose_onehot = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto module : design->selected_modules())
+ {
+ SigMap sigmap(module);
+ OnehotDatabase onehot_db(module, sigmap);
+ onehot_db.verbose = verbose_onehot;
+
+ for (auto cell : module->selected_cells())
+ {
+ if (cell->type != ID($eq))
+ continue;
+
+ SigSpec A = sigmap(cell->getPort(ID::A));
+ SigSpec B = sigmap(cell->getPort(ID::B));
+
+ int a_width = cell->getParam(ID(A_WIDTH)).as_int();
+ int b_width = cell->getParam(ID(B_WIDTH)).as_int();
+
+ if (a_width < b_width) {
+ bool a_signed = cell->getParam(ID(A_SIGNED)).as_int();
+ A.extend_u0(b_width, a_signed);
+ }
+
+ if (b_width < a_width) {
+ bool b_signed = cell->getParam(ID(B_SIGNED)).as_int();
+ B.extend_u0(a_width, b_signed);
+ }
+
+ if (A.is_fully_const())
+ std::swap(A, B);
+
+ if (!B.is_fully_const())
+ continue;
+
+ if (verbose)
+ log("Checking $eq(%s, %s) cell %s/%s.\n", log_signal(A), log_signal(B), log_id(module), log_id(cell));
+
+ if (!onehot_db.query(A)) {
+ if (verbose)
+ log(" onehot driver test on %s failed.\n", log_signal(A));
+ continue;
+ }
+
+ int index = -1;
+ bool not_onehot = false;
+
+ for (int i = 0; i < GetSize(B); i++) {
+ if (B[i] != State::S1)
+ continue;
+ if (index >= 0)
+ not_onehot = true;
+ index = i;
+ }
+
+ if (index < 0) {
+ if (verbose)
+ log(" not optimizing the zero pattern.\n");
+ continue;
+ }
+
+ SigSpec Y = cell->getPort(ID::Y);
+
+ if (not_onehot)
+ {
+ if (verbose)
+ log(" replacing with constant 0 driver.\n");
+ else
+ log("Replacing one-hot $eq(%s, %s) cell %s/%s with constant 0 driver.\n", log_signal(A), log_signal(B), log_id(module), log_id(cell));
+ module->connect(Y, SigSpec(1, GetSize(Y)));
+ }
+ else
+ {
+ SigSpec sig = A[index];
+ if (verbose)
+ log(" replacing with signal %s.\n", log_signal(sig));
+ else
+ log("Replacing one-hot $eq(%s, %s) cell %s/%s with signal %s.\n",log_signal(A), log_signal(B), log_id(module), log_id(cell), log_signal(sig));
+ sig.extend_u0(GetSize(Y));
+ module->connect(Y, sig);
+ }
+
+ module->remove(cell);
+ }
+ }
+ }
+} OnehotPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/opt/rmports.cc b/passes/opt/rmports.cc
index 756be7473..32363dd68 100644
--- a/passes/opt/rmports.cc
+++ b/passes/opt/rmports.cc
@@ -28,7 +28,7 @@ PRIVATE_NAMESPACE_BEGIN
struct RmportsPassPass : public Pass {
RmportsPassPass() : Pass("rmports", "remove module ports with no connections") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -39,7 +39,7 @@ struct RmportsPassPass : public Pass {
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
log_header(design, "Executing RMPORTS pass (remove ports with no connections).\n");
@@ -171,7 +171,7 @@ struct RmportsPassPass : public Pass {
wire->port_output = false;
wire->port_id = 0;
}
- log("Removed %zu unused ports.\n", unused_ports.size());
+ log("Removed %d unused ports.\n", GetSize(unused_ports));
// Re-number all of the wires that DO have ports still on them
for(size_t i=0; i<module->ports.size(); i++)
diff --git a/passes/opt/share.cc b/passes/opt/share.cc
index 22914eaa7..92ce3fd11 100644
--- a/passes/opt/share.cc
+++ b/passes/opt/share.cc
@@ -89,8 +89,8 @@ struct ShareWorker
queue_bits.clear();
for (auto &pbit : portbits) {
- if (pbit.cell->type == "$mux" || pbit.cell->type == "$pmux") {
- pool<RTLIL::SigBit> bits = modwalker.sigmap(pbit.cell->getPort("\\S")).to_sigbit_pool();
+ if (pbit.cell->type == ID($mux) || pbit.cell->type == ID($pmux)) {
+ pool<RTLIL::SigBit> bits = modwalker.sigmap(pbit.cell->getPort(ID(S))).to_sigbit_pool();
terminal_bits.insert(bits.begin(), bits.end());
queue_bits.insert(bits.begin(), bits.end());
visited_cells.insert(pbit.cell);
@@ -128,7 +128,7 @@ struct ShareWorker
static int bits_macc(RTLIL::Cell *c)
{
Macc m(c);
- int width = GetSize(c->getPort("\\Y"));
+ int width = GetSize(c->getPort(ID::Y));
return bits_macc(m, width);
}
@@ -242,7 +242,7 @@ struct ShareWorker
{
Macc m1(c1), m2(c2), supermacc;
- int w1 = GetSize(c1->getPort("\\Y")), w2 = GetSize(c2->getPort("\\Y"));
+ int w1 = GetSize(c1->getPort(ID::Y)), w2 = GetSize(c2->getPort(ID::Y));
int width = max(w1, w2);
m1.optimize(w1);
@@ -328,11 +328,11 @@ struct ShareWorker
{
RTLIL::SigSpec sig_y = module->addWire(NEW_ID, width);
- supercell_aux->insert(module->addPos(NEW_ID, sig_y, c1->getPort("\\Y")));
- supercell_aux->insert(module->addPos(NEW_ID, sig_y, c2->getPort("\\Y")));
+ supercell_aux->insert(module->addPos(NEW_ID, sig_y, c1->getPort(ID::Y)));
+ supercell_aux->insert(module->addPos(NEW_ID, sig_y, c2->getPort(ID::Y)));
- supercell->setParam("\\Y_WIDTH", width);
- supercell->setPort("\\Y", sig_y);
+ supercell->setParam(ID(Y_WIDTH), width);
+ supercell->setPort(ID::Y, sig_y);
supermacc.optimize(width);
supermacc.to_cell(supercell);
@@ -368,22 +368,22 @@ struct ShareWorker
continue;
}
- if (cell->type == "$memrd") {
- if (cell->parameters.at("\\CLK_ENABLE").as_bool())
+ if (cell->type == ID($memrd)) {
+ if (cell->parameters.at(ID(CLK_ENABLE)).as_bool())
continue;
- if (config.opt_aggressive || !modwalker.sigmap(cell->getPort("\\ADDR")).is_fully_const())
+ if (config.opt_aggressive || !modwalker.sigmap(cell->getPort(ID(ADDR))).is_fully_const())
shareable_cells.insert(cell);
continue;
}
- if (cell->type == "$mul" || cell->type == "$div" || cell->type == "$mod") {
- if (config.opt_aggressive || cell->parameters.at("\\Y_WIDTH").as_int() >= 4)
+ if (cell->type.in(ID($mul), ID($div), ID($mod))) {
+ if (config.opt_aggressive || cell->parameters.at(ID(Y_WIDTH)).as_int() >= 4)
shareable_cells.insert(cell);
continue;
}
- if (cell->type == "$shl" || cell->type == "$shr" || cell->type == "$sshl" || cell->type == "$sshr") {
- if (config.opt_aggressive || cell->parameters.at("\\Y_WIDTH").as_int() >= 8)
+ if (cell->type.in(ID($shl), ID($shr), ID($sshl), ID($sshr))) {
+ if (config.opt_aggressive || cell->parameters.at(ID(Y_WIDTH)).as_int() >= 8)
shareable_cells.insert(cell);
continue;
}
@@ -401,9 +401,9 @@ struct ShareWorker
if (c1->type != c2->type)
return false;
- if (c1->type == "$memrd")
+ if (c1->type == ID($memrd))
{
- if (c1->parameters.at("\\MEMID").decode_string() != c2->parameters.at("\\MEMID").decode_string())
+ if (c1->parameters.at(ID(MEMID)).decode_string() != c2->parameters.at(ID(MEMID)).decode_string())
return false;
return true;
@@ -413,11 +413,11 @@ struct ShareWorker
{
if (!config.opt_aggressive)
{
- int a1_width = c1->parameters.at("\\A_WIDTH").as_int();
- int y1_width = c1->parameters.at("\\Y_WIDTH").as_int();
+ int a1_width = c1->parameters.at(ID(A_WIDTH)).as_int();
+ int y1_width = c1->parameters.at(ID(Y_WIDTH)).as_int();
- int a2_width = c2->parameters.at("\\A_WIDTH").as_int();
- int y2_width = c2->parameters.at("\\Y_WIDTH").as_int();
+ int a2_width = c2->parameters.at(ID(A_WIDTH)).as_int();
+ int y2_width = c2->parameters.at(ID(Y_WIDTH)).as_int();
if (max(a1_width, a2_width) > 2 * min(a1_width, a2_width)) return false;
if (max(y1_width, y2_width) > 2 * min(y1_width, y2_width)) return false;
@@ -426,17 +426,17 @@ struct ShareWorker
return true;
}
- if (config.generic_bin_ops.count(c1->type) || c1->type == "$alu")
+ if (config.generic_bin_ops.count(c1->type) || c1->type == ID($alu))
{
if (!config.opt_aggressive)
{
- int a1_width = c1->parameters.at("\\A_WIDTH").as_int();
- int b1_width = c1->parameters.at("\\B_WIDTH").as_int();
- int y1_width = c1->parameters.at("\\Y_WIDTH").as_int();
+ int a1_width = c1->parameters.at(ID(A_WIDTH)).as_int();
+ int b1_width = c1->parameters.at(ID(B_WIDTH)).as_int();
+ int y1_width = c1->parameters.at(ID(Y_WIDTH)).as_int();
- int a2_width = c2->parameters.at("\\A_WIDTH").as_int();
- int b2_width = c2->parameters.at("\\B_WIDTH").as_int();
- int y2_width = c2->parameters.at("\\Y_WIDTH").as_int();
+ int a2_width = c2->parameters.at(ID(A_WIDTH)).as_int();
+ int b2_width = c2->parameters.at(ID(B_WIDTH)).as_int();
+ int y2_width = c2->parameters.at(ID(Y_WIDTH)).as_int();
if (max(a1_width, a2_width) > 2 * min(a1_width, a2_width)) return false;
if (max(b1_width, b2_width) > 2 * min(b1_width, b2_width)) return false;
@@ -450,13 +450,13 @@ struct ShareWorker
{
if (!config.opt_aggressive)
{
- int a1_width = c1->parameters.at("\\A_WIDTH").as_int();
- int b1_width = c1->parameters.at("\\B_WIDTH").as_int();
- int y1_width = c1->parameters.at("\\Y_WIDTH").as_int();
+ int a1_width = c1->parameters.at(ID(A_WIDTH)).as_int();
+ int b1_width = c1->parameters.at(ID(B_WIDTH)).as_int();
+ int y1_width = c1->parameters.at(ID(Y_WIDTH)).as_int();
- int a2_width = c2->parameters.at("\\A_WIDTH").as_int();
- int b2_width = c2->parameters.at("\\B_WIDTH").as_int();
- int y2_width = c2->parameters.at("\\Y_WIDTH").as_int();
+ int a2_width = c2->parameters.at(ID(A_WIDTH)).as_int();
+ int b2_width = c2->parameters.at(ID(B_WIDTH)).as_int();
+ int y2_width = c2->parameters.at(ID(Y_WIDTH)).as_int();
int min1_width = min(a1_width, b1_width);
int max1_width = max(a1_width, b1_width);
@@ -472,7 +472,7 @@ struct ShareWorker
return true;
}
- if (c1->type == "$macc")
+ if (c1->type == ID($macc))
{
if (!config.opt_aggressive)
if (share_macc(c1, c2) > 2 * min(bits_macc(c1), bits_macc(c2))) return false;
@@ -510,27 +510,27 @@ struct ShareWorker
if (config.generic_uni_ops.count(c1->type))
{
- if (c1->parameters.at("\\A_SIGNED").as_bool() != c2->parameters.at("\\A_SIGNED").as_bool())
+ if (c1->parameters.at(ID(A_SIGNED)).as_bool() != c2->parameters.at(ID(A_SIGNED)).as_bool())
{
- RTLIL::Cell *unsigned_cell = c1->parameters.at("\\A_SIGNED").as_bool() ? c2 : c1;
- if (unsigned_cell->getPort("\\A").to_sigbit_vector().back() != RTLIL::State::S0) {
- unsigned_cell->parameters.at("\\A_WIDTH") = unsigned_cell->parameters.at("\\A_WIDTH").as_int() + 1;
- RTLIL::SigSpec new_a = unsigned_cell->getPort("\\A");
+ RTLIL::Cell *unsigned_cell = c1->parameters.at(ID(A_SIGNED)).as_bool() ? c2 : c1;
+ if (unsigned_cell->getPort(ID::A).to_sigbit_vector().back() != RTLIL::State::S0) {
+ unsigned_cell->parameters.at(ID(A_WIDTH)) = unsigned_cell->parameters.at(ID(A_WIDTH)).as_int() + 1;
+ RTLIL::SigSpec new_a = unsigned_cell->getPort(ID::A);
new_a.append_bit(RTLIL::State::S0);
- unsigned_cell->setPort("\\A", new_a);
+ unsigned_cell->setPort(ID::A, new_a);
}
- unsigned_cell->parameters.at("\\A_SIGNED") = true;
+ unsigned_cell->parameters.at(ID(A_SIGNED)) = true;
unsigned_cell->check();
}
- bool a_signed = c1->parameters.at("\\A_SIGNED").as_bool();
- log_assert(a_signed == c2->parameters.at("\\A_SIGNED").as_bool());
+ bool a_signed = c1->parameters.at(ID(A_SIGNED)).as_bool();
+ log_assert(a_signed == c2->parameters.at(ID(A_SIGNED)).as_bool());
- RTLIL::SigSpec a1 = c1->getPort("\\A");
- RTLIL::SigSpec y1 = c1->getPort("\\Y");
+ RTLIL::SigSpec a1 = c1->getPort(ID::A);
+ RTLIL::SigSpec y1 = c1->getPort(ID::Y);
- RTLIL::SigSpec a2 = c2->getPort("\\A");
- RTLIL::SigSpec y2 = c2->getPort("\\Y");
+ RTLIL::SigSpec a2 = c2->getPort(ID::A);
+ RTLIL::SigSpec y2 = c2->getPort(ID::Y);
int a_width = max(a1.size(), a2.size());
int y_width = max(y1.size(), y2.size());
@@ -544,11 +544,11 @@ struct ShareWorker
RTLIL::Wire *y = module->addWire(NEW_ID, y_width);
RTLIL::Cell *supercell = module->addCell(NEW_ID, c1->type);
- supercell->parameters["\\A_SIGNED"] = a_signed;
- supercell->parameters["\\A_WIDTH"] = a_width;
- supercell->parameters["\\Y_WIDTH"] = y_width;
- supercell->setPort("\\A", a);
- supercell->setPort("\\Y", y);
+ supercell->parameters[ID(A_SIGNED)] = a_signed;
+ supercell->parameters[ID(A_WIDTH)] = a_width;
+ supercell->parameters[ID(Y_WIDTH)] = y_width;
+ supercell->setPort(ID::A, a);
+ supercell->setPort(ID::Y, y);
supercell_aux.insert(module->addPos(NEW_ID, y, y1));
supercell_aux.insert(module->addPos(NEW_ID, y, y2));
@@ -557,54 +557,54 @@ struct ShareWorker
return supercell;
}
- if (config.generic_bin_ops.count(c1->type) || config.generic_cbin_ops.count(c1->type) || c1->type == "$alu")
+ if (config.generic_bin_ops.count(c1->type) || config.generic_cbin_ops.count(c1->type) || c1->type == ID($alu))
{
bool modified_src_cells = false;
if (config.generic_cbin_ops.count(c1->type))
{
- int score_unflipped = max(c1->parameters.at("\\A_WIDTH").as_int(), c2->parameters.at("\\A_WIDTH").as_int()) +
- max(c1->parameters.at("\\B_WIDTH").as_int(), c2->parameters.at("\\B_WIDTH").as_int());
+ int score_unflipped = max(c1->parameters.at(ID(A_WIDTH)).as_int(), c2->parameters.at(ID(A_WIDTH)).as_int()) +
+ max(c1->parameters.at(ID(B_WIDTH)).as_int(), c2->parameters.at(ID(B_WIDTH)).as_int());
- int score_flipped = max(c1->parameters.at("\\A_WIDTH").as_int(), c2->parameters.at("\\B_WIDTH").as_int()) +
- max(c1->parameters.at("\\B_WIDTH").as_int(), c2->parameters.at("\\A_WIDTH").as_int());
+ int score_flipped = max(c1->parameters.at(ID(A_WIDTH)).as_int(), c2->parameters.at(ID(B_WIDTH)).as_int()) +
+ max(c1->parameters.at(ID(B_WIDTH)).as_int(), c2->parameters.at(ID(A_WIDTH)).as_int());
if (score_flipped < score_unflipped)
{
- RTLIL::SigSpec tmp = c2->getPort("\\A");
- c2->setPort("\\A", c2->getPort("\\B"));
- c2->setPort("\\B", tmp);
+ RTLIL::SigSpec tmp = c2->getPort(ID::A);
+ c2->setPort(ID::A, c2->getPort(ID::B));
+ c2->setPort(ID::B, tmp);
- std::swap(c2->parameters.at("\\A_WIDTH"), c2->parameters.at("\\B_WIDTH"));
- std::swap(c2->parameters.at("\\A_SIGNED"), c2->parameters.at("\\B_SIGNED"));
+ std::swap(c2->parameters.at(ID(A_WIDTH)), c2->parameters.at(ID(B_WIDTH)));
+ std::swap(c2->parameters.at(ID(A_SIGNED)), c2->parameters.at(ID(B_SIGNED)));
modified_src_cells = true;
}
}
- if (c1->parameters.at("\\A_SIGNED").as_bool() != c2->parameters.at("\\A_SIGNED").as_bool())
+ if (c1->parameters.at(ID(A_SIGNED)).as_bool() != c2->parameters.at(ID(A_SIGNED)).as_bool())
{
- RTLIL::Cell *unsigned_cell = c1->parameters.at("\\A_SIGNED").as_bool() ? c2 : c1;
- if (unsigned_cell->getPort("\\A").to_sigbit_vector().back() != RTLIL::State::S0) {
- unsigned_cell->parameters.at("\\A_WIDTH") = unsigned_cell->parameters.at("\\A_WIDTH").as_int() + 1;
- RTLIL::SigSpec new_a = unsigned_cell->getPort("\\A");
+ RTLIL::Cell *unsigned_cell = c1->parameters.at(ID(A_SIGNED)).as_bool() ? c2 : c1;
+ if (unsigned_cell->getPort(ID::A).to_sigbit_vector().back() != RTLIL::State::S0) {
+ unsigned_cell->parameters.at(ID(A_WIDTH)) = unsigned_cell->parameters.at(ID(A_WIDTH)).as_int() + 1;
+ RTLIL::SigSpec new_a = unsigned_cell->getPort(ID::A);
new_a.append_bit(RTLIL::State::S0);
- unsigned_cell->setPort("\\A", new_a);
+ unsigned_cell->setPort(ID::A, new_a);
}
- unsigned_cell->parameters.at("\\A_SIGNED") = true;
+ unsigned_cell->parameters.at(ID(A_SIGNED)) = true;
modified_src_cells = true;
}
- if (c1->parameters.at("\\B_SIGNED").as_bool() != c2->parameters.at("\\B_SIGNED").as_bool())
+ if (c1->parameters.at(ID(B_SIGNED)).as_bool() != c2->parameters.at(ID(B_SIGNED)).as_bool())
{
- RTLIL::Cell *unsigned_cell = c1->parameters.at("\\B_SIGNED").as_bool() ? c2 : c1;
- if (unsigned_cell->getPort("\\B").to_sigbit_vector().back() != RTLIL::State::S0) {
- unsigned_cell->parameters.at("\\B_WIDTH") = unsigned_cell->parameters.at("\\B_WIDTH").as_int() + 1;
- RTLIL::SigSpec new_b = unsigned_cell->getPort("\\B");
+ RTLIL::Cell *unsigned_cell = c1->parameters.at(ID(B_SIGNED)).as_bool() ? c2 : c1;
+ if (unsigned_cell->getPort(ID::B).to_sigbit_vector().back() != RTLIL::State::S0) {
+ unsigned_cell->parameters.at(ID(B_WIDTH)) = unsigned_cell->parameters.at(ID(B_WIDTH)).as_int() + 1;
+ RTLIL::SigSpec new_b = unsigned_cell->getPort(ID::B);
new_b.append_bit(RTLIL::State::S0);
- unsigned_cell->setPort("\\B", new_b);
+ unsigned_cell->setPort(ID::B, new_b);
}
- unsigned_cell->parameters.at("\\B_SIGNED") = true;
+ unsigned_cell->parameters.at(ID(B_SIGNED)) = true;
modified_src_cells = true;
}
@@ -613,28 +613,28 @@ struct ShareWorker
c2->check();
}
- bool a_signed = c1->parameters.at("\\A_SIGNED").as_bool();
- bool b_signed = c1->parameters.at("\\B_SIGNED").as_bool();
+ bool a_signed = c1->parameters.at(ID(A_SIGNED)).as_bool();
+ bool b_signed = c1->parameters.at(ID(B_SIGNED)).as_bool();
- log_assert(a_signed == c2->parameters.at("\\A_SIGNED").as_bool());
- log_assert(b_signed == c2->parameters.at("\\B_SIGNED").as_bool());
+ log_assert(a_signed == c2->parameters.at(ID(A_SIGNED)).as_bool());
+ log_assert(b_signed == c2->parameters.at(ID(B_SIGNED)).as_bool());
- if (c1->type == "$shl" || c1->type == "$shr" || c1->type == "$sshl" || c1->type == "$sshr")
+ if (c1->type == ID($shl) || c1->type == ID($shr) || c1->type == ID($sshl) || c1->type == ID($sshr))
b_signed = false;
- RTLIL::SigSpec a1 = c1->getPort("\\A");
- RTLIL::SigSpec b1 = c1->getPort("\\B");
- RTLIL::SigSpec y1 = c1->getPort("\\Y");
+ RTLIL::SigSpec a1 = c1->getPort(ID::A);
+ RTLIL::SigSpec b1 = c1->getPort(ID::B);
+ RTLIL::SigSpec y1 = c1->getPort(ID::Y);
- RTLIL::SigSpec a2 = c2->getPort("\\A");
- RTLIL::SigSpec b2 = c2->getPort("\\B");
- RTLIL::SigSpec y2 = c2->getPort("\\Y");
+ RTLIL::SigSpec a2 = c2->getPort(ID::A);
+ RTLIL::SigSpec b2 = c2->getPort(ID::B);
+ RTLIL::SigSpec y2 = c2->getPort(ID::Y);
int a_width = max(a1.size(), a2.size());
int b_width = max(b1.size(), b2.size());
int y_width = max(y1.size(), y2.size());
- if (c1->type == "$shr" && a_signed)
+ if (c1->type == ID($shr) && a_signed)
{
a_width = max(y_width, a_width);
@@ -660,43 +660,43 @@ struct ShareWorker
supercell_aux.insert(module->addMux(NEW_ID, b2, b1, act, b));
RTLIL::Wire *y = module->addWire(NEW_ID, y_width);
- RTLIL::Wire *x = c1->type == "$alu" ? module->addWire(NEW_ID, y_width) : nullptr;
- RTLIL::Wire *co = c1->type == "$alu" ? module->addWire(NEW_ID, y_width) : nullptr;
+ RTLIL::Wire *x = c1->type == ID($alu) ? module->addWire(NEW_ID, y_width) : nullptr;
+ RTLIL::Wire *co = c1->type == ID($alu) ? module->addWire(NEW_ID, y_width) : nullptr;
RTLIL::Cell *supercell = module->addCell(NEW_ID, c1->type);
- supercell->parameters["\\A_SIGNED"] = a_signed;
- supercell->parameters["\\B_SIGNED"] = b_signed;
- supercell->parameters["\\A_WIDTH"] = a_width;
- supercell->parameters["\\B_WIDTH"] = b_width;
- supercell->parameters["\\Y_WIDTH"] = y_width;
- supercell->setPort("\\A", a);
- supercell->setPort("\\B", b);
- supercell->setPort("\\Y", y);
- if (c1->type == "$alu") {
+ supercell->parameters[ID(A_SIGNED)] = a_signed;
+ supercell->parameters[ID(B_SIGNED)] = b_signed;
+ supercell->parameters[ID(A_WIDTH)] = a_width;
+ supercell->parameters[ID(B_WIDTH)] = b_width;
+ supercell->parameters[ID(Y_WIDTH)] = y_width;
+ supercell->setPort(ID::A, a);
+ supercell->setPort(ID::B, b);
+ supercell->setPort(ID::Y, y);
+ if (c1->type == ID($alu)) {
RTLIL::Wire *ci = module->addWire(NEW_ID), *bi = module->addWire(NEW_ID);
- supercell_aux.insert(module->addMux(NEW_ID, c2->getPort("\\CI"), c1->getPort("\\CI"), act, ci));
- supercell_aux.insert(module->addMux(NEW_ID, c2->getPort("\\BI"), c1->getPort("\\BI"), act, bi));
- supercell->setPort("\\CI", ci);
- supercell->setPort("\\BI", bi);
- supercell->setPort("\\CO", co);
- supercell->setPort("\\X", x);
+ supercell_aux.insert(module->addMux(NEW_ID, c2->getPort(ID(CI)), c1->getPort(ID(CI)), act, ci));
+ supercell_aux.insert(module->addMux(NEW_ID, c2->getPort(ID(BI)), c1->getPort(ID(BI)), act, bi));
+ supercell->setPort(ID(CI), ci);
+ supercell->setPort(ID(BI), bi);
+ supercell->setPort(ID(CO), co);
+ supercell->setPort(ID(X), x);
}
supercell->check();
supercell_aux.insert(module->addPos(NEW_ID, y, y1));
supercell_aux.insert(module->addPos(NEW_ID, y, y2));
- if (c1->type == "$alu") {
- supercell_aux.insert(module->addPos(NEW_ID, co, c1->getPort("\\CO")));
- supercell_aux.insert(module->addPos(NEW_ID, co, c2->getPort("\\CO")));
- supercell_aux.insert(module->addPos(NEW_ID, x, c1->getPort("\\X")));
- supercell_aux.insert(module->addPos(NEW_ID, x, c2->getPort("\\X")));
+ if (c1->type == ID($alu)) {
+ supercell_aux.insert(module->addPos(NEW_ID, co, c1->getPort(ID(CO))));
+ supercell_aux.insert(module->addPos(NEW_ID, co, c2->getPort(ID(CO))));
+ supercell_aux.insert(module->addPos(NEW_ID, x, c1->getPort(ID(X))));
+ supercell_aux.insert(module->addPos(NEW_ID, x, c2->getPort(ID(X))));
}
supercell_aux.insert(supercell);
return supercell;
}
- if (c1->type == "$macc")
+ if (c1->type == ID($macc))
{
RTLIL::Cell *supercell = module->addCell(NEW_ID, c1->type);
supercell_aux.insert(supercell);
@@ -705,14 +705,18 @@ struct ShareWorker
return supercell;
}
- if (c1->type == "$memrd")
+ if (c1->type == ID($memrd))
{
RTLIL::Cell *supercell = module->addCell(NEW_ID, c1);
- RTLIL::SigSpec addr1 = c1->getPort("\\ADDR");
- RTLIL::SigSpec addr2 = c2->getPort("\\ADDR");
- if (addr1 != addr2)
- supercell->setPort("\\ADDR", module->Mux(NEW_ID, addr2, addr1, act));
- supercell_aux.insert(module->addPos(NEW_ID, supercell->getPort("\\DATA"), c2->getPort("\\DATA")));
+ RTLIL::SigSpec addr1 = c1->getPort(ID(ADDR));
+ RTLIL::SigSpec addr2 = c2->getPort(ID(ADDR));
+ if (GetSize(addr1) < GetSize(addr2))
+ addr1.extend_u0(GetSize(addr2));
+ else
+ addr2.extend_u0(GetSize(addr1));
+ supercell->setPort(ID(ADDR), addr1 != addr2 ? module->Mux(NEW_ID, addr2, addr1, act) : addr1);
+ supercell->parameters[ID(ABITS)] = RTLIL::Const(GetSize(addr1));
+ supercell_aux.insert(module->addPos(NEW_ID, supercell->getPort(ID(DATA)), c2->getPort(ID(DATA))));
supercell_aux.insert(supercell);
return supercell;
}
@@ -743,8 +747,8 @@ struct ShareWorker
modwalker.get_consumers(pbits, modwalker.cell_outputs[cell]);
for (auto &bit : pbits) {
- if ((bit.cell->type == "$mux" || bit.cell->type == "$pmux") && bit.port == "\\S")
- forbidden_controls_cache[cell].insert(bit.cell->getPort("\\S").extract(bit.offset, 1));
+ if ((bit.cell->type == ID($mux) || bit.cell->type == ID($pmux)) && bit.port == ID(S))
+ forbidden_controls_cache[cell].insert(bit.cell->getPort(ID(S)).extract(bit.offset, 1));
consumer_cells.insert(bit.cell);
}
@@ -870,7 +874,7 @@ struct ShareWorker
}
for (auto &pbit : modwalker.signal_consumers[bit]) {
log_assert(fwd_ct.cell_known(pbit.cell->type));
- if ((pbit.cell->type == "$mux" || pbit.cell->type == "$pmux") && (pbit.port == "\\A" || pbit.port == "\\B"))
+ if ((pbit.cell->type == ID($mux) || pbit.cell->type == ID($pmux)) && (pbit.port == ID::A || pbit.port == ID::B))
driven_data_muxes.insert(pbit.cell);
else
driven_cells.insert(pbit.cell);
@@ -886,10 +890,10 @@ struct ShareWorker
bool used_in_a = false;
std::set<int> used_in_b_parts;
- int width = c->parameters.at("\\WIDTH").as_int();
- std::vector<RTLIL::SigBit> sig_a = modwalker.sigmap(c->getPort("\\A"));
- std::vector<RTLIL::SigBit> sig_b = modwalker.sigmap(c->getPort("\\B"));
- std::vector<RTLIL::SigBit> sig_s = modwalker.sigmap(c->getPort("\\S"));
+ int width = c->parameters.at(ID(WIDTH)).as_int();
+ std::vector<RTLIL::SigBit> sig_a = modwalker.sigmap(c->getPort(ID::A));
+ std::vector<RTLIL::SigBit> sig_b = modwalker.sigmap(c->getPort(ID::B));
+ std::vector<RTLIL::SigBit> sig_s = modwalker.sigmap(c->getPort(ID(S)));
for (auto &bit : sig_a)
if (cell_out_bits.count(bit))
@@ -1128,14 +1132,14 @@ struct ShareWorker
fwd_ct.setup_internals();
cone_ct.setup_internals();
- cone_ct.cell_types.erase("$mul");
- cone_ct.cell_types.erase("$mod");
- cone_ct.cell_types.erase("$div");
- cone_ct.cell_types.erase("$pow");
- cone_ct.cell_types.erase("$shl");
- cone_ct.cell_types.erase("$shr");
- cone_ct.cell_types.erase("$sshl");
- cone_ct.cell_types.erase("$sshr");
+ cone_ct.cell_types.erase(ID($mul));
+ cone_ct.cell_types.erase(ID($mod));
+ cone_ct.cell_types.erase(ID($div));
+ cone_ct.cell_types.erase(ID($pow));
+ cone_ct.cell_types.erase(ID($shl));
+ cone_ct.cell_types.erase(ID($shr));
+ cone_ct.cell_types.erase(ID($sshl));
+ cone_ct.cell_types.erase(ID($sshr));
modwalker.setup(design, module);
@@ -1149,9 +1153,9 @@ struct ShareWorker
GetSize(shareable_cells), log_id(module));
for (auto cell : module->cells())
- if (cell->type == "$pmux")
- for (auto bit : cell->getPort("\\S"))
- for (auto other_bit : cell->getPort("\\S"))
+ if (cell->type == ID($pmux))
+ for (auto bit : cell->getPort(ID(S)))
+ for (auto other_bit : cell->getPort(ID(S)))
if (bit < other_bit)
exclusive_ctrls.push_back(std::pair<RTLIL::SigBit, RTLIL::SigBit>(bit, other_bit));
@@ -1421,7 +1425,7 @@ struct ShareWorker
struct SharePass : public Pass {
SharePass() : Pass("share", "perform sat-based resource sharing") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -1453,7 +1457,7 @@ struct SharePass : public Pass {
log(" Only perform the first N merges, then stop. This is useful for debugging.\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
ShareWorkerConfig config;
@@ -1462,43 +1466,43 @@ struct SharePass : public Pass {
config.opt_aggressive = false;
config.opt_fast = false;
- config.generic_uni_ops.insert("$not");
- // config.generic_uni_ops.insert("$pos");
- config.generic_uni_ops.insert("$neg");
-
- config.generic_cbin_ops.insert("$and");
- config.generic_cbin_ops.insert("$or");
- config.generic_cbin_ops.insert("$xor");
- config.generic_cbin_ops.insert("$xnor");
-
- config.generic_bin_ops.insert("$shl");
- config.generic_bin_ops.insert("$shr");
- config.generic_bin_ops.insert("$sshl");
- config.generic_bin_ops.insert("$sshr");
-
- config.generic_bin_ops.insert("$lt");
- config.generic_bin_ops.insert("$le");
- config.generic_bin_ops.insert("$eq");
- config.generic_bin_ops.insert("$ne");
- config.generic_bin_ops.insert("$eqx");
- config.generic_bin_ops.insert("$nex");
- config.generic_bin_ops.insert("$ge");
- config.generic_bin_ops.insert("$gt");
-
- config.generic_cbin_ops.insert("$add");
- config.generic_cbin_ops.insert("$mul");
-
- config.generic_bin_ops.insert("$sub");
- config.generic_bin_ops.insert("$div");
- config.generic_bin_ops.insert("$mod");
- // config.generic_bin_ops.insert("$pow");
-
- config.generic_uni_ops.insert("$logic_not");
- config.generic_cbin_ops.insert("$logic_and");
- config.generic_cbin_ops.insert("$logic_or");
-
- config.generic_other_ops.insert("$alu");
- config.generic_other_ops.insert("$macc");
+ config.generic_uni_ops.insert(ID($not));
+ // config.generic_uni_ops.insert(ID($pos));
+ config.generic_uni_ops.insert(ID($neg));
+
+ config.generic_cbin_ops.insert(ID($and));
+ config.generic_cbin_ops.insert(ID($or));
+ config.generic_cbin_ops.insert(ID($xor));
+ config.generic_cbin_ops.insert(ID($xnor));
+
+ config.generic_bin_ops.insert(ID($shl));
+ config.generic_bin_ops.insert(ID($shr));
+ config.generic_bin_ops.insert(ID($sshl));
+ config.generic_bin_ops.insert(ID($sshr));
+
+ config.generic_bin_ops.insert(ID($lt));
+ config.generic_bin_ops.insert(ID($le));
+ config.generic_bin_ops.insert(ID($eq));
+ config.generic_bin_ops.insert(ID($ne));
+ config.generic_bin_ops.insert(ID($eqx));
+ config.generic_bin_ops.insert(ID($nex));
+ config.generic_bin_ops.insert(ID($ge));
+ config.generic_bin_ops.insert(ID($gt));
+
+ config.generic_cbin_ops.insert(ID($add));
+ config.generic_cbin_ops.insert(ID($mul));
+
+ config.generic_bin_ops.insert(ID($sub));
+ config.generic_bin_ops.insert(ID($div));
+ config.generic_bin_ops.insert(ID($mod));
+ // config.generic_bin_ops.insert(ID($pow));
+
+ config.generic_uni_ops.insert(ID($logic_not));
+ config.generic_cbin_ops.insert(ID($logic_and));
+ config.generic_cbin_ops.insert(ID($logic_or));
+
+ config.generic_other_ops.insert(ID($alu));
+ config.generic_other_ops.insert(ID($macc));
log_header(design, "Executing SHARE pass (SAT-based resource sharing).\n");
diff --git a/passes/opt/wreduce.cc b/passes/opt/wreduce.cc
index 07503fbb3..04b882db9 100644
--- a/passes/opt/wreduce.cc
+++ b/passes/opt/wreduce.cc
@@ -22,23 +22,24 @@
#include "kernel/modtools.h"
USING_YOSYS_NAMESPACE
-using namespace RTLIL;
PRIVATE_NAMESPACE_BEGIN
struct WreduceConfig
{
pool<IdString> supported_cell_types;
+ bool keepdc = false;
WreduceConfig()
{
supported_cell_types = pool<IdString>({
- "$not", "$pos", "$neg",
- "$and", "$or", "$xor", "$xnor",
- "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx",
- "$lt", "$le", "$eq", "$ne", "$eqx", "$nex", "$ge", "$gt",
- "$add", "$sub", "$mul", // "$div", "$mod", "$pow",
- "$mux", "$pmux"
+ ID($not), ID($pos), ID($neg),
+ ID($and), ID($or), ID($xor), ID($xnor),
+ ID($shl), ID($shr), ID($sshl), ID($sshr), ID($shift), ID($shiftx),
+ ID($lt), ID($le), ID($eq), ID($ne), ID($eqx), ID($nex), ID($ge), ID($gt),
+ ID($add), ID($sub), ID($mul), // ID($div), ID($mod), ID($pow),
+ ID($mux), ID($pmux),
+ ID($dff), ID($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) { }
@@ -60,10 +63,10 @@ struct WreduceWorker
{
// Reduce size of MUX if inputs agree on a value for a bit or a output bit is unused
- SigSpec sig_a = mi.sigmap(cell->getPort("\\A"));
- SigSpec sig_b = mi.sigmap(cell->getPort("\\B"));
- SigSpec sig_s = mi.sigmap(cell->getPort("\\S"));
- SigSpec sig_y = mi.sigmap(cell->getPort("\\Y"));
+ SigSpec sig_a = mi.sigmap(cell->getPort(ID::A));
+ SigSpec sig_b = mi.sigmap(cell->getPort(ID::B));
+ SigSpec sig_s = mi.sigmap(cell->getPort(ID(S)));
+ SigSpec sig_y = mi.sigmap(cell->getPort(ID::Y));
std::vector<SigBit> bits_removed;
if (sig_y.has_const())
@@ -73,15 +76,15 @@ struct WreduceWorker
{
auto info = mi.query(sig_y[i]);
if (!info->is_output && GetSize(info->ports) <= 1 && !keep_bits.count(mi.sigmap(sig_y[i]))) {
- bits_removed.push_back(Sx);
+ bits_removed.push_back(State::Sx);
continue;
}
SigBit ref = sig_a[i];
for (int k = 0; k < GetSize(sig_s); k++) {
- if (ref != Sx && sig_b[k*GetSize(sig_a) + i] != Sx && ref != sig_b[k*GetSize(sig_a) + i])
+ if ((config->keepdc || (ref != State::Sx && sig_b[k*GetSize(sig_a) + i] != State::Sx)) && ref != sig_b[k*GetSize(sig_a) + i])
goto no_match_ab;
- if (sig_b[k*GetSize(sig_a) + i] != Sx)
+ if (sig_b[k*GetSize(sig_a) + i] != State::Sx)
ref = sig_b[k*GetSize(sig_a) + i];
}
if (0)
@@ -126,20 +129,113 @@ struct WreduceWorker
for (auto bit : new_work_queue_bits)
work_queue_bits.insert(bit);
- cell->setPort("\\A", new_sig_a);
- cell->setPort("\\B", new_sig_b);
- cell->setPort("\\Y", new_sig_y);
+ cell->setPort(ID::A, new_sig_a);
+ cell->setPort(ID::B, new_sig_b);
+ cell->setPort(ID::Y, new_sig_y);
cell->fixup_parameters();
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(ID(D)));
+ SigSpec sig_q = mi.sigmap(cell->getPort(ID(Q)));
+ bool is_adff = (cell->type == ID($adff));
+ Const initval, arst_value;
+
+ int width_before = GetSize(sig_q);
+
+ if (width_before == 0)
+ return;
+
+ if (cell->parameters.count(ID(ARST_VALUE))) {
+ arst_value = cell->parameters[ID(ARST_VALUE)];
+ }
+
+ bool zero_ext = sig_d[GetSize(sig_d)-1] == State::S0;
+ bool sign_ext = !zero_ext;
+
+ for (int i = 0; i < GetSize(sig_q); i++) {
+ SigBit bit = sig_q[i];
+ if (init_bits.count(bit))
+ initval.bits.push_back(init_bits.at(bit));
+ else
+ initval.bits.push_back(State::Sx);
+ }
+
+ for (int i = GetSize(sig_q)-1; i >= 0; i--)
+ {
+ if (zero_ext && sig_d[i] == State::S0 && (initval[i] == State::S0 || initval[i] == State::Sx) &&
+ (!is_adff || i >= GetSize(arst_value) || arst_value[i] == State::S0 || arst_value[i] == State::Sx)) {
+ 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] &&
+ (!is_adff || i >= GetSize(arst_value) || arst_value[i] == arst_value[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 == nullptr)
+ return;
+ if (!info->is_output && GetSize(info->ports) == 1 && !keep_bits.count(mi.sigmap(sig_q[i]))) {
+ remove_init_bits.insert(sig_q[i]);
+ 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(ID(ARST_VALUE))) {
+ arst_value.bits.resize(GetSize(sig_q));
+ cell->setParam(ID(ARST_VALUE), arst_value);
+ }
+
+ cell->setPort(ID(D), sig_d);
+ cell->setPort(ID(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();
SigSpec sig = mi.sigmap(cell->getPort(stringf("\\%c", port)));
- if (port == 'B' && cell->type.in("$shl", "$shr", "$sshl", "$sshr"))
+ if (port == 'B' && cell->type.in(ID($shl), ID($shr), ID($sshl), ID($sshr)))
port_signed = false;
int bits_removed = 0;
@@ -154,7 +250,7 @@ struct WreduceWorker
while (GetSize(sig) > 1 && sig[GetSize(sig)-1] == sig[GetSize(sig)-2])
work_queue_bits.insert(sig[GetSize(sig)-1]), sig.remove(GetSize(sig)-1), bits_removed++;
} else {
- while (GetSize(sig) > 1 && sig[GetSize(sig)-1] == S0)
+ while (GetSize(sig) > 1 && sig[GetSize(sig)-1] == State::S0)
work_queue_bits.insert(sig[GetSize(sig)-1]), sig.remove(GetSize(sig)-1), bits_removed++;
}
@@ -173,10 +269,13 @@ struct WreduceWorker
if (!cell->type.in(config->supported_cell_types))
return;
- if (cell->type.in("$mux", "$pmux"))
+ if (cell->type.in(ID($mux), ID($pmux)))
return run_cell_mux(cell);
- SigSpec sig = mi.sigmap(cell->getPort("\\Y"));
+ if (cell->type.in(ID($dff), ID($adff)))
+ return run_cell_dff(cell);
+
+ SigSpec sig = mi.sigmap(cell->getPort(ID::Y));
if (sig.has_const())
return;
@@ -184,10 +283,10 @@ struct WreduceWorker
// Reduce size of ports A and B based on constant input bits and size of output port
- int max_port_a_size = cell->hasPort("\\A") ? GetSize(cell->getPort("\\A")) : -1;
- int max_port_b_size = cell->hasPort("\\B") ? GetSize(cell->getPort("\\B")) : -1;
+ int max_port_a_size = cell->hasPort(ID::A) ? GetSize(cell->getPort(ID::A)) : -1;
+ int max_port_b_size = cell->hasPort(ID::B) ? GetSize(cell->getPort(ID::B)) : -1;
- if (cell->type.in("$not", "$pos", "$neg", "$and", "$or", "$xor", "$add", "$sub")) {
+ if (cell->type.in(ID($not), ID($pos), ID($neg), ID($and), ID($or), ID($xor), ID($add), ID($sub))) {
max_port_a_size = min(max_port_a_size, GetSize(sig));
max_port_b_size = min(max_port_b_size, GetSize(sig));
}
@@ -195,32 +294,32 @@ struct WreduceWorker
bool port_a_signed = false;
bool port_b_signed = false;
- if (max_port_a_size >= 0 && cell->type != "$shiftx")
+ if (max_port_a_size >= 0 && cell->type != ID($shiftx))
run_reduce_inport(cell, 'A', max_port_a_size, port_a_signed, did_something);
if (max_port_b_size >= 0)
run_reduce_inport(cell, 'B', max_port_b_size, port_b_signed, did_something);
- if (cell->hasPort("\\A") && cell->hasPort("\\B") && port_a_signed && port_b_signed) {
- SigSpec sig_a = mi.sigmap(cell->getPort("\\A")), sig_b = mi.sigmap(cell->getPort("\\B"));
+ if (cell->hasPort(ID::A) && cell->hasPort(ID::B) && port_a_signed && port_b_signed) {
+ SigSpec sig_a = mi.sigmap(cell->getPort(ID::A)), sig_b = mi.sigmap(cell->getPort(ID::B));
if (GetSize(sig_a) > 0 && sig_a[GetSize(sig_a)-1] == State::S0 &&
GetSize(sig_b) > 0 && sig_b[GetSize(sig_b)-1] == State::S0) {
log("Converting cell %s.%s (%s) from signed to unsigned.\n",
log_id(module), log_id(cell), log_id(cell->type));
- cell->setParam("\\A_SIGNED", 0);
- cell->setParam("\\B_SIGNED", 0);
+ cell->setParam(ID(A_SIGNED), 0);
+ cell->setParam(ID(B_SIGNED), 0);
port_a_signed = false;
port_b_signed = false;
did_something = true;
}
}
- if (cell->hasPort("\\A") && !cell->hasPort("\\B") && port_a_signed) {
- SigSpec sig_a = mi.sigmap(cell->getPort("\\A"));
+ if (cell->hasPort(ID::A) && !cell->hasPort(ID::B) && port_a_signed) {
+ SigSpec sig_a = mi.sigmap(cell->getPort(ID::A));
if (GetSize(sig_a) > 0 && sig_a[GetSize(sig_a)-1] == State::S0) {
log("Converting cell %s.%s (%s) from signed to unsigned.\n",
log_id(module), log_id(cell), log_id(cell->type));
- cell->setParam("\\A_SIGNED", 0);
+ cell->setParam(ID(A_SIGNED), 0);
port_a_signed = false;
did_something = true;
}
@@ -230,13 +329,16 @@ struct WreduceWorker
// Reduce size of port Y based on sizes for A and B and unused bits in Y
int bits_removed = 0;
- if (port_a_signed && cell->type == "$shr") {
+ if (port_a_signed && cell->type == ID($shr)) {
// do not reduce size of output on $shr cells with signed A inputs
} 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;
@@ -245,24 +347,24 @@ struct WreduceWorker
}
}
- if (cell->type.in("$pos", "$add", "$mul", "$and", "$or", "$xor"))
+ if (cell->type.in(ID($pos), ID($add), ID($mul), ID($and), ID($or), ID($xor), ID($sub)))
{
- bool is_signed = cell->getParam("\\A_SIGNED").as_bool();
+ bool is_signed = cell->getParam(ID(A_SIGNED)).as_bool() || cell->type == ID($sub);
int a_size = 0, b_size = 0;
- if (cell->hasPort("\\A")) a_size = GetSize(cell->getPort("\\A"));
- if (cell->hasPort("\\B")) b_size = GetSize(cell->getPort("\\B"));
+ if (cell->hasPort(ID::A)) a_size = GetSize(cell->getPort(ID::A));
+ if (cell->hasPort(ID::B)) b_size = GetSize(cell->getPort(ID::B));
int max_y_size = max(a_size, b_size);
- if (cell->type == "$add")
+ if (cell->type.in(ID($add), ID($sub)))
max_y_size++;
- if (cell->type == "$mul")
+ if (cell->type == ID($mul))
max_y_size = a_size + b_size;
while (GetSize(sig) > 1 && GetSize(sig) > max_y_size) {
- module->connect(sig[GetSize(sig)-1], is_signed ? sig[GetSize(sig)-2] : S0);
+ module->connect(sig[GetSize(sig)-1], is_signed ? sig[GetSize(sig)-2] : State::S0);
sig.remove(GetSize(sig)-1);
bits_removed++;
}
@@ -277,7 +379,7 @@ struct WreduceWorker
if (bits_removed) {
log("Removed top %d bits (of %d) from port Y of cell %s.%s (%s).\n",
bits_removed, GetSize(sig) + bits_removed, log_id(module), log_id(cell), log_id(cell->type));
- cell->setPort("\\Y", sig);
+ cell->setPort(ID::Y, sig);
did_something = true;
}
@@ -290,17 +392,28 @@ struct WreduceWorker
static int count_nontrivial_wire_attrs(RTLIL::Wire *w)
{
int count = w->attributes.size();
- count -= w->attributes.count("\\src");
- count -= w->attributes.count("\\unused_bits");
+ count -= w->attributes.count(ID(src));
+ count -= w->attributes.count(ID(unused_bits));
return count;
}
void run()
{
- for (auto w : module->wires())
- if (w->get_bool_attribute("\\keep"))
+ // 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(ID::keep))
for (auto bit : mi.sigmap(w))
keep_bits.insert(bit);
+ if (w->attributes.count(ID(init))) {
+ Const initval = w->attributes.at(ID(init));
+ SigSpec initsig = init_attr_sigmap(w);
+ int width = std::min(GetSize(initval), GetSize(initsig));
+ for (int i = 0; i < width; i++)
+ init_bits[initsig[i]] = initval[i];
+ }
+ }
for (auto c : module->selected_cells())
work_queue_cells.insert(c);
@@ -348,12 +461,28 @@ struct WreduceWorker
module->connect(nw, SigSpec(w).extract(0, GetSize(nw)));
module->swap_names(w, nw);
}
+
+ if (!remove_init_bits.empty()) {
+ for (auto w : module->wires()) {
+ if (w->attributes.count(ID(init))) {
+ Const initval = w->attributes.at(ID(init));
+ Const new_initval(State::Sx, GetSize(w));
+ SigSpec initsig = init_attr_sigmap(w);
+ int width = std::min(GetSize(initval), GetSize(initsig));
+ for (int i = 0; i < width; i++) {
+ if (!remove_init_bits.count(initsig[i]))
+ new_initval[i] = initval[i];
+ }
+ w->attributes.at(ID(init)) = new_initval;
+ }
+ }
+ }
}
};
struct WreducePass : public Pass {
WreducePass() : Pass("wreduce", "reduce the word size of operations if possible") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -372,8 +501,11 @@ struct WreducePass : public Pass {
log(" Do not change the width of memory address ports. Use this options in\n");
log(" flows that use the 'memory_memx' pass.\n");
log("\n");
+ log(" -keepdc\n");
+ log(" Do not optimize explicit don't-care values.\n");
+ log("\n");
}
- virtual void execute(std::vector<std::string> args, Design *design)
+ void execute(std::vector<std::string> args, Design *design) YS_OVERRIDE
{
WreduceConfig config;
bool opt_memx = false;
@@ -386,6 +518,10 @@ struct WreducePass : public Pass {
opt_memx = true;
continue;
}
+ if (args[argidx] == "-keepdc") {
+ config.keepdc = true;
+ continue;
+ }
break;
}
extra_args(args, argidx, design);
@@ -397,30 +533,66 @@ struct WreducePass : public Pass {
for (auto c : module->selected_cells())
{
- if (c->type.in("$reduce_and", "$reduce_or", "$reduce_xor", "$reduce_xnor", "$reduce_bool",
- "$lt", "$le", "$eq", "$ne", "$eqx", "$nex", "$ge", "$gt",
- "$logic_not", "$logic_and", "$logic_or") && GetSize(c->getPort("\\Y")) > 1) {
- SigSpec sig = c->getPort("\\Y");
+ if (c->type.in(ID($reduce_and), ID($reduce_or), ID($reduce_xor), ID($reduce_xnor), ID($reduce_bool),
+ ID($lt), ID($le), ID($eq), ID($ne), ID($eqx), ID($nex), ID($ge), ID($gt),
+ ID($logic_not), ID($logic_and), ID($logic_or)) && GetSize(c->getPort(ID::Y)) > 1) {
+ SigSpec sig = c->getPort(ID::Y);
if (!sig.has_const()) {
- c->setPort("\\Y", sig[0]);
- c->setParam("\\Y_WIDTH", 1);
+ c->setPort(ID::Y, sig[0]);
+ c->setParam(ID(Y_WIDTH), 1);
sig.remove(0);
module->connect(sig, Const(0, GetSize(sig)));
}
}
- if (!opt_memx && c->type.in("$memrd", "$memwr", "$meminit")) {
- IdString memid = c->getParam("\\MEMID").decode_string();
+
+ if (c->type.in(ID($div), ID($mod), ID($pow)))
+ {
+ SigSpec A = c->getPort(ID::A);
+ int original_a_width = GetSize(A);
+ if (c->getParam(ID(A_SIGNED)).as_bool()) {
+ while (GetSize(A) > 1 && A[GetSize(A)-1] == State::S0 && A[GetSize(A)-2] == State::S0)
+ A.remove(GetSize(A)-1, 1);
+ } else {
+ while (GetSize(A) > 0 && A[GetSize(A)-1] == State::S0)
+ A.remove(GetSize(A)-1, 1);
+ }
+ if (original_a_width != GetSize(A)) {
+ log("Removed top %d bits (of %d) from port A of cell %s.%s (%s).\n",
+ original_a_width-GetSize(A), original_a_width, log_id(module), log_id(c), log_id(c->type));
+ c->setPort(ID::A, A);
+ c->setParam(ID(A_WIDTH), GetSize(A));
+ }
+
+ SigSpec B = c->getPort(ID::B);
+ int original_b_width = GetSize(B);
+ if (c->getParam(ID(B_SIGNED)).as_bool()) {
+ while (GetSize(B) > 1 && B[GetSize(B)-1] == State::S0 && B[GetSize(B)-2] == State::S0)
+ B.remove(GetSize(B)-1, 1);
+ } else {
+ while (GetSize(B) > 0 && B[GetSize(B)-1] == State::S0)
+ B.remove(GetSize(B)-1, 1);
+ }
+ if (original_b_width != GetSize(B)) {
+ log("Removed top %d bits (of %d) from port B of cell %s.%s (%s).\n",
+ original_b_width-GetSize(B), original_b_width, log_id(module), log_id(c), log_id(c->type));
+ c->setPort(ID::B, B);
+ c->setParam(ID(B_WIDTH), GetSize(B));
+ }
+ }
+
+ if (!opt_memx && c->type.in(ID($memrd), ID($memwr), ID($meminit))) {
+ IdString memid = c->getParam(ID(MEMID)).decode_string();
RTLIL::Memory *mem = module->memories.at(memid);
if (mem->start_offset >= 0) {
- int cur_addrbits = c->getParam("\\ABITS").as_int();
+ int cur_addrbits = c->getParam(ID(ABITS)).as_int();
int max_addrbits = ceil_log2(mem->start_offset + mem->size);
if (cur_addrbits > max_addrbits) {
log("Removed top %d address bits (of %d) from memory %s port %s.%s (%s).\n",
cur_addrbits-max_addrbits, cur_addrbits,
- c->type == "$memrd" ? "read" : c->type == "$memwr" ? "write" : "init",
+ c->type == ID($memrd) ? "read" : c->type == ID($memwr) ? "write" : "init",
log_id(module), log_id(c), log_id(memid));
- c->setParam("\\ABITS", max_addrbits);
- c->setPort("\\ADDR", c->getPort("\\ADDR").extract(0, max_addrbits));
+ c->setParam(ID(ABITS), max_addrbits);
+ c->setPort(ID(ADDR), c->getPort(ID(ADDR)).extract(0, max_addrbits));
}
}
}
diff --git a/passes/pmgen/.gitignore b/passes/pmgen/.gitignore
new file mode 100644
index 000000000..e52f3282f
--- /dev/null
+++ b/passes/pmgen/.gitignore
@@ -0,0 +1 @@
+/*_pm.h
diff --git a/passes/pmgen/Makefile.inc b/passes/pmgen/Makefile.inc
new file mode 100644
index 000000000..1a57bef7d
--- /dev/null
+++ b/passes/pmgen/Makefile.inc
@@ -0,0 +1,48 @@
+%_pm.h: passes/pmgen/pmgen.py %.pmg
+ $(P) mkdir -p passes/pmgen && $(PYTHON_EXECUTABLE) $< -o $@ -p $(subst _pm.h,,$(notdir $@)) $(filter-out $<,$^)
+
+# --------------------------------------
+
+OBJS += passes/pmgen/test_pmgen.o
+passes/pmgen/test_pmgen.o: passes/pmgen/test_pmgen_pm.h passes/pmgen/ice40_dsp_pm.h passes/pmgen/peepopt_pm.h passes/pmgen/xilinx_srl_pm.h
+$(eval $(call add_extra_objs,passes/pmgen/test_pmgen_pm.h))
+
+# --------------------------------------
+
+OBJS += passes/pmgen/ice40_dsp.o
+passes/pmgen/ice40_dsp.o: passes/pmgen/ice40_dsp_pm.h
+$(eval $(call add_extra_objs,passes/pmgen/ice40_dsp_pm.h))
+
+# --------------------------------------
+
+OBJS += passes/pmgen/ice40_wrapcarry.o
+passes/pmgen/ice40_wrapcarry.o: passes/pmgen/ice40_wrapcarry_pm.h
+$(eval $(call add_extra_objs,passes/pmgen/ice40_wrapcarry_pm.h))
+
+# --------------------------------------
+
+OBJS += passes/pmgen/xilinx_dsp.o
+passes/pmgen/xilinx_dsp.o: passes/pmgen/xilinx_dsp_pm.h passes/pmgen/xilinx_dsp48a_pm.h passes/pmgen/xilinx_dsp_CREG_pm.h passes/pmgen/xilinx_dsp_cascade_pm.h
+$(eval $(call add_extra_objs,passes/pmgen/xilinx_dsp_pm.h))
+$(eval $(call add_extra_objs,passes/pmgen/xilinx_dsp48a_pm.h))
+$(eval $(call add_extra_objs,passes/pmgen/xilinx_dsp_CREG_pm.h))
+$(eval $(call add_extra_objs,passes/pmgen/xilinx_dsp_cascade_pm.h))
+
+# --------------------------------------
+
+OBJS += passes/pmgen/peepopt.o
+passes/pmgen/peepopt.o: passes/pmgen/peepopt_pm.h
+$(eval $(call add_extra_objs,passes/pmgen/peepopt_pm.h))
+
+PEEPOPT_PATTERN = passes/pmgen/peepopt_shiftmul.pmg
+PEEPOPT_PATTERN += passes/pmgen/peepopt_muldiv.pmg
+PEEPOPT_PATTERN += passes/pmgen/peepopt_dffmux.pmg
+
+passes/pmgen/peepopt_pm.h: passes/pmgen/pmgen.py $(PEEPOPT_PATTERN)
+ $(P) mkdir -p passes/pmgen && $(PYTHON_EXECUTABLE) $< -o $@ -p peepopt $(filter-out $<,$^)
+
+# --------------------------------------
+
+OBJS += passes/pmgen/xilinx_srl.o
+passes/pmgen/xilinx_srl.o: passes/pmgen/xilinx_srl_pm.h
+$(eval $(call add_extra_objs,passes/pmgen/xilinx_srl_pm.h))
diff --git a/passes/pmgen/README.md b/passes/pmgen/README.md
new file mode 100644
index 000000000..39560839f
--- /dev/null
+++ b/passes/pmgen/README.md
@@ -0,0 +1,384 @@
+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_<pattern_name>(callback_function)` method searches for all matches
+for the pattern`<pattern_name>` and calls the callback function for each found
+match:
+
+ pm.run_foobar([&](){
+ 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_<pattern_name>.<state_name>` members. (The `.st_<pattern_name>` member is
+of type `foobar_pm::state_<pattern_name>_t`.)
+
+Similarly the `.pmg` file declares user data variables that become members of
+`.ud_<pattern_name>`, a struct of type `foobar_pm::udata_<pattern_name>_t`.
+
+There are three versions of the `run_<pattern_name>()` method: Without callback,
+callback without arguments, and callback with reference to `pm`. All versions
+of the `run_<pattern_name>()` method return the number of found matches.
+
+
+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 a pattern
+-------------------
+
+A `.pmg` file contains one or more patterns. Each pattern starts with a line
+with the `pattern` keyword followed by the name of the pattern.
+
+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_<pattern_name>(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 automatically initialized to the default constructed value of their type
+when the 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`, `branch`,
+ `finish`, and `subpattern` 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. That is, 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`.
+
+The `semioptional` statement marks matches that must match if at least one
+matching cell exists, but if no matching cell exists it is set to `nullptr`.
+
+Slices and choices
+------------------
+
+Cell matches can contain "slices" and "choices". Slices can be used to
+create matches for different sections of a cell. For example:
+
+ state <int> pmux_slice
+
+ match pmux
+ select pmux->type == $pmux
+ slice idx GetSize(port(pmux, \S))
+ index <SigBit> port(pmux, \S)[idx] === port(eq, \Y)
+ set pmux_slice idx
+ endmatch
+
+The first argument to `slice` is the local variable name used to identify the
+slice. The second argument is the number of slices that should be created for
+this cell. The `set` statement can be used to copy that index into a state
+variable so that later matches and/or code blocks can refer to it.
+
+A similar mechanism is "choices", where a list of options is given as
+second argument, and the matcher will iterate over those options:
+
+ state <SigSpec> foo bar
+ state <IdString> eq_ab eq_ba
+
+ match eq
+ select eq->type == $eq
+ choice <IdString> AB {\A, \B}
+ define <IdString> BA (AB == \A ? \B : \A)
+ index <SigSpec> port(eq, AB) === foo
+ index <SigSpec> port(eq, BA) === bar
+ set eq_ab AB
+ set eq_ba BA
+ generate
+
+Notice how `define` can be used to define additional local variables similar
+to the loop variables defined by `slice` and `choice`.
+
+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 (sub)pattern
+that just rejects.
+
+A `code..finally..endcode` block executes the code after `finally` during
+back-tracking. This is useful for maintaining user data state or printing
+debug messages. For example:
+
+ udata <vector<Cell*>> stack
+
+ code
+ stack.push_back(addAB);
+ ...
+ finally
+ stack.pop_back();
+ endcode
+
+`accept` and `finish` statements can be used inside the `finally` section,
+but not `reject`, `branch`, or `subpattern`.
+
+Declaring a subpattern
+----------------------
+
+A subpattern starts with a line containing the `subpattern` keyword followed
+by the name of the subpattern. Subpatterns can be called from a `code` block
+using a `subpattern(<subpattern_name>);` C statement.
+
+Arguments may be passed to subpattern via state variables. The `subpattern`
+line must be followed by a `arg <arg1> <arg2> ...` line that lists the
+state variables used to pass arguments.
+
+ state <IdString> foobar_type
+ state <bool> foobar_state
+
+ code foobar_type foobar_state
+ foobar_state = false;
+ foobar_type = $add;
+ subpattern(foo);
+ foobar_type = $sub;
+ subpattern(bar);
+ endcode
+
+ subpattern foo
+ arg foobar_type foobar_state
+
+ match addsub
+ index <IdString> addsub->type === foobar_type
+ ...
+ endmatch
+
+ code
+ if (foobar_state) {
+ subpattern(tail);
+ } else {
+ foobar_state = true;
+ subpattern(bar);
+ }
+ endcode
+
+ subpattern bar
+ arg foobar_type foobar_state
+
+ match addsub
+ index <IdString> addsub->type === foobar_type
+ ...
+ endmatch
+
+ code
+ if (foobar_state) {
+ subpattern(tail);
+ } else {
+ foobar_state = true;
+ subpattern(foo);
+ }
+ endcode
+
+ subpattern tail
+ ...
+
+Subpatterns can be called recursively.
+
+If a `subpattern` statement is preceded by a `fallthrough` statement, this is
+equivalent to calling the subpattern at the end of the preceding block.
+
+Generate Blocks
+---------------
+
+Match blocks may contain an optional `generate` section that is used for automatic
+test-case generation. For example:
+
+ match mul
+ ...
+ generate 10 0
+ SigSpec Y = port(ff, \D);
+ SigSpec A = module->addWire(NEW_ID, GetSize(Y) - rng(GetSize(Y)/2));
+ SigSpec B = module->addWire(NEW_ID, GetSize(Y) - rng(GetSize(Y)/2));
+ module->addMul(NEW_ID, A, B, Y, rng(2));
+ endmatch
+
+The expression `rng(n)` returns a non-negative integer less than `n`.
+
+The first argument to `generate` is the chance of this generate block being
+executed when the match block did not match anything, in percent.
+
+The second argument to `generate` is the chance of this generate block being
+executed when the match block did match something, in percent.
+
+The special statement `finish` can be used within generate blocks to terminate
+the current pattern matcher run.
diff --git a/passes/pmgen/generate.h b/passes/pmgen/generate.h
new file mode 100644
index 000000000..354583de5
--- /dev/null
+++ b/passes/pmgen/generate.h
@@ -0,0 +1,140 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#ifndef PMGEN_GENERATE
+#define PMGEN_GENERATE
+
+#define GENERATE_PATTERN(pmclass, pattern) \
+ generate_pattern<pmclass>([](pmclass &pm, std::function<void()> f){ return pm.run_ ## pattern(f); }, #pmclass, #pattern, design)
+
+void pmtest_addports(Module *module)
+{
+ pool<SigBit> driven_bits, used_bits;
+ SigMap sigmap(module);
+ int icnt = 0, ocnt = 0;
+
+ for (auto cell : module->cells())
+ for (auto conn : cell->connections())
+ {
+ if (cell->input(conn.first))
+ for (auto bit : sigmap(conn.second))
+ used_bits.insert(bit);
+ if (cell->output(conn.first))
+ for (auto bit : sigmap(conn.second))
+ driven_bits.insert(bit);
+ }
+
+ for (auto wire : vector<Wire*>(module->wires()))
+ {
+ SigSpec ibits, obits;
+ for (auto bit : sigmap(wire)) {
+ if (!used_bits.count(bit))
+ obits.append(bit);
+ if (!driven_bits.count(bit))
+ ibits.append(bit);
+ }
+ if (!ibits.empty()) {
+ Wire *w = module->addWire(stringf("\\i%d", icnt++), GetSize(ibits));
+ w->port_input = true;
+ module->connect(ibits, w);
+ }
+ if (!obits.empty()) {
+ Wire *w = module->addWire(stringf("\\o%d", ocnt++), GetSize(obits));
+ w->port_output = true;
+ module->connect(w, obits);
+ }
+ }
+
+ module->fixup_ports();
+}
+
+template <class pm>
+void generate_pattern(std::function<void(pm&,std::function<void()>)> run, const char *pmclass, const char *pattern, Design *design)
+{
+ log("Generating \"%s\" patterns for pattern matcher \"%s\".\n", pattern, pmclass);
+
+ int modcnt = 0;
+ int maxmodcnt = 100;
+ int maxsubcnt = 4;
+ int timeout = 0;
+ vector<Module*> mods;
+
+ while (modcnt < maxmodcnt)
+ {
+ int submodcnt = 0, itercnt = 0, cellcnt = 0;
+ Module *mod = design->addModule(NEW_ID);
+
+ while (modcnt < maxmodcnt && submodcnt < maxsubcnt && itercnt++ < 1000)
+ {
+ if (timeout++ > 10000)
+ log_error("pmgen generator is stuck: 10000 iterations with no matching module generated.\n");
+
+ pm matcher(mod, mod->cells());
+
+ matcher.rng(1);
+ matcher.rngseed += modcnt;
+ matcher.rng(1);
+ matcher.rngseed += submodcnt;
+ matcher.rng(1);
+ matcher.rngseed += itercnt;
+ matcher.rng(1);
+ matcher.rngseed += cellcnt;
+ matcher.rng(1);
+
+ if (GetSize(mod->cells()) != cellcnt)
+ {
+ bool found_match = false;
+ run(matcher, [&](){ found_match = true; });
+ cellcnt = GetSize(mod->cells());
+
+ if (found_match) {
+ Module *m = design->addModule(stringf("\\pmtest_%s_%s_%05d",
+ pmclass, pattern, modcnt++));
+ log("Creating module %s with %d cells.\n", log_id(m), cellcnt);
+ mod->cloneInto(m);
+ pmtest_addports(m);
+ mods.push_back(m);
+ submodcnt++;
+ timeout = 0;
+ }
+ }
+
+ matcher.generate_mode = true;
+ run(matcher, [](){});
+ }
+
+ if (submodcnt && maxsubcnt < (1 << 16))
+ maxsubcnt *= 2;
+
+ design->remove(mod);
+ }
+
+ Module *m = design->addModule(stringf("\\pmtest_%s_%s", pmclass, pattern));
+ log("Creating module %s with %d cells.\n", log_id(m), GetSize(mods));
+ for (auto mod : mods) {
+ Cell *c = m->addCell(mod->name, mod->name);
+ for (auto port : mod->ports) {
+ Wire *w = m->addWire(NEW_ID, GetSize(mod->wire(port)));
+ c->setPort(port, w);
+ }
+ }
+ pmtest_addports(m);
+}
+
+#endif
diff --git a/passes/pmgen/ice40_dsp.cc b/passes/pmgen/ice40_dsp.cc
new file mode 100644
index 000000000..c364cd91a
--- /dev/null
+++ b/passes/pmgen/ice40_dsp.cc
@@ -0,0 +1,317 @@
+/*
+ * 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
+
+#include "passes/pmgen/ice40_dsp_pm.h"
+
+void create_ice40_dsp(ice40_dsp_pm &pm)
+{
+ auto &st = pm.st_ice40_dsp;
+
+ log("Checking %s.%s for iCE40 DSP inference.\n", log_id(pm.module), log_id(st.mul));
+
+ log_debug("ffA: %s %s %s\n", log_id(st.ffA, "--"), log_id(st.ffAholdmux, "--"), log_id(st.ffArstmux, "--"));
+ log_debug("ffB: %s %s %s\n", log_id(st.ffB, "--"), log_id(st.ffBholdmux, "--"), log_id(st.ffBrstmux, "--"));
+ log_debug("ffCD: %s %s\n", log_id(st.ffCD, "--"), log_id(st.ffCDholdmux, "--"));
+ log_debug("mul: %s\n", log_id(st.mul, "--"));
+ log_debug("ffFJKG: %s\n", log_id(st.ffFJKG, "--"));
+ log_debug("ffH: %s\n", log_id(st.ffH, "--"));
+ log_debug("add: %s\n", log_id(st.add, "--"));
+ log_debug("mux: %s\n", log_id(st.mux, "--"));
+ log_debug("ffO: %s %s %s\n", log_id(st.ffO, "--"), log_id(st.ffOholdmux, "--"), log_id(st.ffOrstmux, "--"));
+ log_debug("\n");
+
+ if (GetSize(st.sigA) > 16) {
+ log(" input A (%s) is too large (%d > 16).\n", log_signal(st.sigA), GetSize(st.sigA));
+ return;
+ }
+
+ if (GetSize(st.sigB) > 16) {
+ log(" input B (%s) is too large (%d > 16).\n", log_signal(st.sigB), GetSize(st.sigB));
+ return;
+ }
+
+ if (GetSize(st.sigO) > 33) {
+ log(" adder/accumulator (%s) is too large (%d > 33).\n", log_signal(st.sigO), GetSize(st.sigO));
+ return;
+ }
+
+ if (GetSize(st.sigH) > 32) {
+ log(" output (%s) is too large (%d > 32).\n", log_signal(st.sigH), GetSize(st.sigH));
+ return;
+ }
+
+ Cell *cell = st.mul;
+ if (cell->type == ID($mul)) {
+ log(" replacing %s with SB_MAC16 cell.\n", log_id(st.mul->type));
+
+ cell = pm.module->addCell(NEW_ID, ID(SB_MAC16));
+ pm.module->swap_names(cell, st.mul);
+ }
+ else log_assert(cell->type == ID(SB_MAC16));
+
+ // SB_MAC16 Input Interface
+ SigSpec A = st.sigA;
+ A.extend_u0(16, st.mul->parameters.at(ID(A_SIGNED), State::S0).as_bool());
+ log_assert(GetSize(A) == 16);
+
+ SigSpec B = st.sigB;
+ B.extend_u0(16, st.mul->parameters.at(ID(B_SIGNED), State::S0).as_bool());
+ log_assert(GetSize(B) == 16);
+
+ SigSpec CD = st.sigCD;
+ if (CD.empty())
+ CD = RTLIL::Const(0, 32);
+ else
+ log_assert(GetSize(CD) == 32);
+
+ cell->setPort(ID::A, A);
+ cell->setPort(ID::B, B);
+ cell->setPort(ID(C), CD.extract(16, 16));
+ cell->setPort(ID(D), CD.extract(0, 16));
+
+ cell->setParam(ID(A_REG), st.ffA ? State::S1 : State::S0);
+ cell->setParam(ID(B_REG), st.ffB ? State::S1 : State::S0);
+ cell->setParam(ID(C_REG), st.ffCD ? State::S1 : State::S0);
+ cell->setParam(ID(D_REG), st.ffCD ? State::S1 : State::S0);
+
+ SigSpec AHOLD, BHOLD, CDHOLD;
+ if (st.ffAholdmux)
+ AHOLD = st.ffAholdpol ? st.ffAholdmux->getPort(ID(S)) : pm.module->Not(NEW_ID, st.ffAholdmux->getPort(ID(S)));
+ else
+ AHOLD = State::S0;
+ if (st.ffBholdmux)
+ BHOLD = st.ffBholdpol ? st.ffBholdmux->getPort(ID(S)) : pm.module->Not(NEW_ID, st.ffBholdmux->getPort(ID(S)));
+ else
+ BHOLD = State::S0;
+ if (st.ffCDholdmux)
+ CDHOLD = st.ffCDholdpol ? st.ffCDholdmux->getPort(ID(S)) : pm.module->Not(NEW_ID, st.ffCDholdmux->getPort(ID(S)));
+ else
+ CDHOLD = State::S0;
+ cell->setPort(ID(AHOLD), AHOLD);
+ cell->setPort(ID(BHOLD), BHOLD);
+ cell->setPort(ID(CHOLD), CDHOLD);
+ cell->setPort(ID(DHOLD), CDHOLD);
+
+ SigSpec IRSTTOP, IRSTBOT;
+ if (st.ffArstmux)
+ IRSTTOP = st.ffArstpol ? st.ffArstmux->getPort(ID(S)) : pm.module->Not(NEW_ID, st.ffArstmux->getPort(ID(S)));
+ else
+ IRSTTOP = State::S0;
+ if (st.ffBrstmux)
+ IRSTBOT = st.ffBrstpol ? st.ffBrstmux->getPort(ID(S)) : pm.module->Not(NEW_ID, st.ffBrstmux->getPort(ID(S)));
+ else
+ IRSTBOT = State::S0;
+ cell->setPort(ID(IRSTTOP), IRSTTOP);
+ cell->setPort(ID(IRSTBOT), IRSTBOT);
+
+ if (st.clock != SigBit())
+ {
+ cell->setPort(ID(CLK), st.clock);
+ cell->setPort(ID(CE), State::S1);
+ cell->setParam(ID(NEG_TRIGGER), st.clock_pol ? State::S0 : State::S1);
+
+ log(" clock: %s (%s)", log_signal(st.clock), st.clock_pol ? "posedge" : "negedge");
+
+ if (st.ffA)
+ log(" ffA:%s", log_id(st.ffA));
+
+ if (st.ffB)
+ log(" ffB:%s", log_id(st.ffB));
+
+ if (st.ffCD)
+ log(" ffCD:%s", log_id(st.ffCD));
+
+ if (st.ffFJKG)
+ log(" ffFJKG:%s", log_id(st.ffFJKG));
+
+ if (st.ffH)
+ log(" ffH:%s", log_id(st.ffH));
+
+ if (st.ffO)
+ log(" ffO:%s", log_id(st.ffO));
+
+ log("\n");
+ }
+ else
+ {
+ cell->setPort(ID(CLK), State::S0);
+ cell->setPort(ID(CE), State::S0);
+ cell->setParam(ID(NEG_TRIGGER), State::S0);
+ }
+
+ // SB_MAC16 Cascade Interface
+
+ cell->setPort(ID(SIGNEXTIN), State::Sx);
+ cell->setPort(ID(SIGNEXTOUT), pm.module->addWire(NEW_ID));
+
+ cell->setPort(ID(CI), State::Sx);
+
+ cell->setPort(ID(ACCUMCI), State::Sx);
+ cell->setPort(ID(ACCUMCO), pm.module->addWire(NEW_ID));
+
+ // SB_MAC16 Output Interface
+
+ SigSpec O = st.sigO;
+ int O_width = GetSize(O);
+ if (O_width == 33) {
+ log_assert(st.add);
+ // If we have a signed multiply-add, then perform sign extension
+ if (st.add->getParam(ID(A_SIGNED)).as_bool() && st.add->getParam(ID(B_SIGNED)).as_bool())
+ pm.module->connect(O[32], O[31]);
+ else
+ cell->setPort(ID(CO), O[32]);
+ O.remove(O_width-1);
+ }
+ else
+ cell->setPort(ID(CO), pm.module->addWire(NEW_ID));
+ log_assert(GetSize(O) <= 32);
+ if (GetSize(O) < 32)
+ O.append(pm.module->addWire(NEW_ID, 32-GetSize(O)));
+
+ cell->setPort(ID(O), O);
+
+ bool accum = false;
+ if (st.add) {
+ accum = (st.ffO && st.add->getPort(st.addAB == ID::A ? ID::B : ID::A) == st.sigO);
+ if (accum)
+ log(" accumulator %s (%s)\n", log_id(st.add), log_id(st.add->type));
+ else
+ log(" adder %s (%s)\n", log_id(st.add), log_id(st.add->type));
+ cell->setPort(ID(ADDSUBTOP), st.add->type == ID($add) ? State::S0 : State::S1);
+ cell->setPort(ID(ADDSUBBOT), st.add->type == ID($add) ? State::S0 : State::S1);
+ } else {
+ cell->setPort(ID(ADDSUBTOP), State::S0);
+ cell->setPort(ID(ADDSUBBOT), State::S0);
+ }
+
+ SigSpec OHOLD;
+ if (st.ffOholdmux)
+ OHOLD = st.ffOholdpol ? st.ffOholdmux->getPort(ID(S)) : pm.module->Not(NEW_ID, st.ffOholdmux->getPort(ID(S)));
+ else
+ OHOLD = State::S0;
+ cell->setPort(ID(OHOLDTOP), OHOLD);
+ cell->setPort(ID(OHOLDBOT), OHOLD);
+
+ SigSpec ORST;
+ if (st.ffOrstmux)
+ ORST = st.ffOrstpol ? st.ffOrstmux->getPort(ID(S)) : pm.module->Not(NEW_ID, st.ffOrstmux->getPort(ID(S)));
+ else
+ ORST = State::S0;
+ cell->setPort(ID(ORSTTOP), ORST);
+ cell->setPort(ID(ORSTBOT), ORST);
+
+ SigSpec acc_reset = State::S0;
+ if (st.mux) {
+ if (st.muxAB == ID::A)
+ acc_reset = st.mux->getPort(ID(S));
+ else
+ acc_reset = pm.module->Not(NEW_ID, st.mux->getPort(ID(S)));
+ }
+ cell->setPort(ID(OLOADTOP), acc_reset);
+ cell->setPort(ID(OLOADBOT), acc_reset);
+
+ // SB_MAC16 Remaining Parameters
+
+ cell->setParam(ID(TOP_8x8_MULT_REG), st.ffFJKG ? State::S1 : State::S0);
+ cell->setParam(ID(BOT_8x8_MULT_REG), st.ffFJKG ? State::S1 : State::S0);
+ cell->setParam(ID(PIPELINE_16x16_MULT_REG1), st.ffFJKG ? State::S1 : State::S0);
+ cell->setParam(ID(PIPELINE_16x16_MULT_REG2), st.ffH ? State::S1 : State::S0);
+
+ cell->setParam(ID(TOPADDSUB_LOWERINPUT), Const(2, 2));
+ cell->setParam(ID(TOPADDSUB_UPPERINPUT), accum ? State::S0 : State::S1);
+ cell->setParam(ID(TOPADDSUB_CARRYSELECT), Const(3, 2));
+
+ cell->setParam(ID(BOTADDSUB_LOWERINPUT), Const(2, 2));
+ cell->setParam(ID(BOTADDSUB_UPPERINPUT), accum ? State::S0 : State::S1);
+ cell->setParam(ID(BOTADDSUB_CARRYSELECT), Const(0, 2));
+
+ cell->setParam(ID(MODE_8x8), State::S0);
+ cell->setParam(ID(A_SIGNED), st.mul->parameters.at(ID(A_SIGNED), State::S0).as_bool());
+ cell->setParam(ID(B_SIGNED), st.mul->parameters.at(ID(B_SIGNED), State::S0).as_bool());
+
+ if (st.ffO) {
+ if (st.o_lo)
+ cell->setParam(ID(TOPOUTPUT_SELECT), Const(st.add ? 0 : 3, 2));
+ else
+ cell->setParam(ID(TOPOUTPUT_SELECT), Const(1, 2));
+
+ st.ffO->connections_.at(ID(Q)).replace(O, pm.module->addWire(NEW_ID, GetSize(O)));
+ cell->setParam(ID(BOTOUTPUT_SELECT), Const(1, 2));
+ }
+ else {
+ cell->setParam(ID(TOPOUTPUT_SELECT), Const(st.add ? 0 : 3, 2));
+ cell->setParam(ID(BOTOUTPUT_SELECT), Const(st.add ? 0 : 3, 2));
+ }
+
+ if (cell != st.mul)
+ pm.autoremove(st.mul);
+ else
+ pm.blacklist(st.mul);
+ pm.autoremove(st.ffFJKG);
+ pm.autoremove(st.add);
+}
+
+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 ($mul/SB_MAC16) and multiply-accumulate ($mul/SB_MAC16 + $add)\n");
+ log("cells into iCE40 DSP resources.\n");
+ log("Currently, only the 16x16 multiply mode is supported and not the 2 x 8x8 mode.\n");
+ log("\n");
+ log("Pack input registers (A, B, {C,D}; with optional hold), pipeline registers\n");
+ log("({F,J,K,G}, H), output registers (O -- full 32-bits or lower 16-bits only; with\n");
+ log("optional hold), and post-adder into into the SB_MAC16 resource.\n");
+ log("\n");
+ log("Multiply-accumulate operations using the post-adder with feedback on the {C,D}\n");
+ log("input will be folded into the DSP. In this scenario only, resetting the\n");
+ log("the accumulator to an arbitrary value can be inferred to use the {C,D} input.\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_ice40_dsp(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 000000000..9d649cb98
--- /dev/null
+++ b/passes/pmgen/ice40_dsp.pmg
@@ -0,0 +1,581 @@
+pattern ice40_dsp
+
+state <SigBit> clock
+state <bool> clock_pol cd_signed o_lo
+state <SigSpec> sigA sigB sigCD sigH sigO
+state <Cell*> add mux
+state <IdString> addAB muxAB
+
+state <bool> ffAholdpol ffBholdpol ffCDholdpol ffOholdpol
+state <bool> ffArstpol ffBrstpol ffCDrstpol ffOrstpol
+
+state <Cell*> ffA ffAholdmux ffArstmux ffB ffBholdmux ffBrstmux ffCD ffCDholdmux
+state <Cell*> ffFJKG ffH ffO ffOholdmux ffOrstmux
+
+// subpattern
+state <SigSpec> argQ argD
+state <bool> ffholdpol ffrstpol
+state <int> ffoffset
+udata <SigSpec> dffD dffQ
+udata <SigBit> dffclock
+udata <Cell*> dff dffholdmux dffrstmux
+udata <bool> dffholdpol dffrstpol dffclock_pol
+
+match mul
+ select mul->type.in($mul, \SB_MAC16)
+ select GetSize(mul->getPort(\A)) + GetSize(mul->getPort(\B)) > 10
+endmatch
+
+code sigA sigB sigH
+ auto unextend = [](const SigSpec &sig) {
+ int i;
+ for (i = GetSize(sig)-1; i > 0; i--)
+ if (sig[i] != sig[i-1])
+ break;
+ // Do not remove non-const sign bit
+ if (sig[i].wire)
+ ++i;
+ return sig.extract(0, i);
+ };
+ sigA = unextend(port(mul, \A));
+ sigB = unextend(port(mul, \B));
+
+ SigSpec O;
+ if (mul->type == $mul)
+ O = mul->getPort(\Y);
+ else if (mul->type == \SB_MAC16)
+ O = mul->getPort(\O);
+ else log_abort();
+ if (GetSize(O) <= 10)
+ reject;
+
+ // Only care about those bits that are used
+ int i;
+ for (i = 0; i < GetSize(O); i++) {
+ if (nusers(O[i]) <= 1)
+ break;
+ sigH.append(O[i]);
+ }
+ // This sigM could have no users if downstream sinks (e.g. $add) is
+ // narrower than $mul result, for example
+ if (i == 0)
+ reject;
+
+ log_assert(nusers(O.extract_end(i)) <= 1);
+endcode
+
+code argQ ffA ffAholdmux ffArstmux ffAholdpol ffArstpol sigA clock clock_pol
+ if (mul->type != \SB_MAC16 || !param(mul, \A_REG, State::S0).as_bool()) {
+ argQ = sigA;
+ subpattern(in_dffe);
+ if (dff) {
+ ffA = dff;
+ clock = dffclock;
+ clock_pol = dffclock_pol;
+ if (dffrstmux) {
+ ffArstmux = dffrstmux;
+ ffArstpol = dffrstpol;
+ }
+ if (dffholdmux) {
+ ffAholdmux = dffholdmux;
+ ffAholdpol = dffholdpol;
+ }
+ sigA = dffD;
+ }
+ }
+endcode
+
+code argQ ffB ffBholdmux ffBrstmux ffBholdpol ffBrstpol sigB clock clock_pol
+ if (mul->type != \SB_MAC16 || !param(mul, \B_REG, State::S0).as_bool()) {
+ argQ = sigB;
+ subpattern(in_dffe);
+ if (dff) {
+ ffB = dff;
+ clock = dffclock;
+ clock_pol = dffclock_pol;
+ if (dffrstmux) {
+ ffBrstmux = dffrstmux;
+ ffBrstpol = dffrstpol;
+ }
+ if (dffholdmux) {
+ ffBholdmux = dffholdmux;
+ ffBholdpol = dffholdpol;
+ }
+ sigB = dffD;
+ }
+ }
+endcode
+
+code argD ffFJKG sigH clock clock_pol
+ if (nusers(sigH) == 2 &&
+ (mul->type != \SB_MAC16 ||
+ (!param(mul, \TOP_8x8_MULT_REG, State::S0).as_bool() && !param(mul, \BOT_8x8_MULT_REG, State::S0).as_bool() && !param(mul, \PIPELINE_16x16_MULT_REG1, State::S0).as_bool() && !param(mul, \PIPELINE_16x16_MULT_REG1, State::S0).as_bool()))) {
+ argD = sigH;
+ subpattern(out_dffe);
+ if (dff) {
+ // F/J/K/G do not have a CE-like (hold) input
+ if (dffholdmux)
+ goto reject_ffFJKG;
+
+ // Reset signal of F/J (IRSTTOP) and K/G (IRSTBOT)
+ // shared with A and B
+ if ((ffArstmux != NULL) != (dffrstmux != NULL))
+ goto reject_ffFJKG;
+ if ((ffBrstmux != NULL) != (dffrstmux != NULL))
+ goto reject_ffFJKG;
+ if (ffArstmux) {
+ if (port(ffArstmux, \S) != port(dffrstmux, \S))
+ goto reject_ffFJKG;
+ if (ffArstpol != dffrstpol)
+ goto reject_ffFJKG;
+ }
+ if (ffBrstmux) {
+ if (port(ffBrstmux, \S) != port(dffrstmux, \S))
+ goto reject_ffFJKG;
+ if (ffBrstpol != dffrstpol)
+ goto reject_ffFJKG;
+ }
+
+ ffFJKG = dff;
+ clock = dffclock;
+ clock_pol = dffclock_pol;
+ sigH = dffQ;
+
+reject_ffFJKG: ;
+ }
+ }
+endcode
+
+code argD ffH sigH sigO clock clock_pol
+ if (ffFJKG && nusers(sigH) == 2 &&
+ (mul->type != \SB_MAC16 || !param(mul, \PIPELINE_16x16_MULT_REG2, State::S0).as_bool())) {
+ argD = sigH;
+ subpattern(out_dffe);
+ if (dff) {
+ // H does not have a CE-like (hold) input
+ if (dffholdmux)
+ goto reject_ffH;
+
+ // Reset signal of H (IRSTBOT) shared with B
+ if ((ffBrstmux != NULL) != (dffrstmux != NULL))
+ goto reject_ffH;
+ if (ffBrstmux) {
+ if (port(ffBrstmux, \S) != port(dffrstmux, \S))
+ goto reject_ffH;
+ if (ffBrstpol != dffrstpol)
+ goto reject_ffH;
+ }
+
+ ffH = dff;
+ clock = dffclock;
+ clock_pol = dffclock_pol;
+ sigH = dffQ;
+
+reject_ffH: ;
+ }
+ }
+
+ sigO = sigH;
+endcode
+
+match add
+ if mul->type != \SB_MAC16 || (param(mul, \TOPOUTPUT_SELECT, State::S0).as_int() == 3 && param(mul, \BOTOUTPUT_SELECT, State::S0).as_int() == 3)
+
+ select add->type.in($add)
+ choice <IdString> AB {\A, \B}
+ select nusers(port(add, AB)) == 2
+
+ index <SigBit> port(add, AB)[0] === sigH[0]
+ filter GetSize(port(add, AB)) <= GetSize(sigH)
+ filter port(add, AB) == sigH.extract(0, GetSize(port(add, AB)))
+ filter nusers(sigH.extract_end(GetSize(port(add, AB)))) <= 1
+ set addAB AB
+ optional
+endmatch
+
+code sigCD sigO cd_signed
+ if (add) {
+ sigCD = port(add, addAB == \A ? \B : \A);
+ cd_signed = param(add, addAB == \A ? \B_SIGNED : \A_SIGNED).as_bool();
+
+ int natural_mul_width = GetSize(sigA) + GetSize(sigB);
+ int actual_mul_width = GetSize(sigH);
+ int actual_acc_width = GetSize(sigCD);
+
+ if ((actual_acc_width > actual_mul_width) && (natural_mul_width > actual_mul_width))
+ reject;
+ // If accumulator, check adder width and signedness
+ if (sigCD == sigH && (actual_acc_width != actual_mul_width) && (param(mul, \A_SIGNED, State::S0).as_bool() != param(add, \A_SIGNED).as_bool()))
+ reject;
+
+ sigO = port(add, \Y);
+ }
+endcode
+
+match mux
+ select mux->type == $mux
+ choice <IdString> AB {\A, \B}
+ select nusers(port(mux, AB)) == 2
+ index <SigSpec> port(mux, AB) === sigO
+ set muxAB AB
+ optional
+endmatch
+
+code sigO
+ if (mux)
+ sigO = port(mux, \Y);
+endcode
+
+code argD ffO ffOholdmux ffOrstmux ffOholdpol ffOrstpol sigO sigCD clock clock_pol cd_signed o_lo
+ if (mul->type != \SB_MAC16 ||
+ // Ensure that register is not already used
+ ((param(mul, \TOPOUTPUT_SELECT, 0).as_int() != 1 && param(mul, \BOTOUTPUT_SELECT, 0).as_int() != 1) &&
+ // Ensure that OLOADTOP/OLOADBOT is unused or zero
+ (port(mul, \OLOADTOP, State::S0).is_fully_zero() && port(mul, \OLOADBOT, State::S0).is_fully_zero()))) {
+
+ dff = nullptr;
+
+ // First try entire sigO
+ if (nusers(sigO) == 2) {
+ argD = sigO;
+ subpattern(out_dffe);
+ }
+
+ // Otherwise try just its least significant 16 bits
+ if (!dff && GetSize(sigO) > 16) {
+ argD = sigO.extract(0, 16);
+ if (nusers(argD) == 2) {
+ subpattern(out_dffe);
+ o_lo = dff;
+ }
+ }
+
+ if (dff) {
+ ffO = dff;
+ clock = dffclock;
+ clock_pol = dffclock_pol;
+ if (dffrstmux) {
+ ffOrstmux = dffrstmux;
+ ffOrstpol = dffrstpol;
+ }
+ if (dffholdmux) {
+ ffOholdmux = dffholdmux;
+ ffOholdpol = dffholdpol;
+ }
+
+ sigO.replace(sigO.extract(0, GetSize(dffQ)), dffQ);
+ }
+
+ // Loading value into output register is not
+ // supported unless using accumulator
+ if (mux) {
+ if (sigCD != sigO)
+ reject;
+ sigCD = port(mux, muxAB == \B ? \A : \B);
+
+ cd_signed = add && param(add, \A_SIGNED).as_bool() && param(add, \B_SIGNED).as_bool();
+ }
+ }
+endcode
+
+code argQ ffCD ffCDholdmux ffCDholdpol ffCDrstpol sigCD clock clock_pol
+ if (!sigCD.empty() && sigCD != sigO &&
+ (mul->type != \SB_MAC16 || (!param(mul, \C_REG, State::S0).as_bool() && !param(mul, \D_REG, State::S0).as_bool()))) {
+ argQ = sigCD;
+ subpattern(in_dffe);
+ if (dff) {
+ if (dffholdmux) {
+ ffCDholdmux = dffholdmux;
+ ffCDholdpol = dffholdpol;
+ }
+
+ // Reset signal of C (IRSTTOP) and D (IRSTBOT)
+ // shared with A and B
+ if ((ffArstmux != NULL) != (dffrstmux != NULL))
+ goto reject_ffCD;
+ if ((ffBrstmux != NULL) != (dffrstmux != NULL))
+ goto reject_ffCD;
+ if (ffArstmux) {
+ if (port(ffArstmux, \S) != port(dffrstmux, \S))
+ goto reject_ffCD;
+ if (ffArstpol != dffrstpol)
+ goto reject_ffCD;
+ }
+ if (ffBrstmux) {
+ if (port(ffBrstmux, \S) != port(dffrstmux, \S))
+ goto reject_ffCD;
+ if (ffBrstpol != dffrstpol)
+ goto reject_ffCD;
+ }
+
+ ffCD = dff;
+ clock = dffclock;
+ clock_pol = dffclock_pol;
+ sigCD = dffD;
+
+reject_ffCD: ;
+ }
+ }
+endcode
+
+code sigCD
+ sigCD.extend_u0(32, cd_signed);
+endcode
+
+code
+ accept;
+endcode
+
+// #######################
+
+subpattern in_dffe
+arg argD argQ clock clock_pol
+
+code
+ dff = nullptr;
+ if (argQ.empty())
+ reject;
+ for (auto c : argQ.chunks()) {
+ if (!c.wire)
+ reject;
+ if (c.wire->get_bool_attribute(\keep))
+ reject;
+ Const init = c.wire->attributes.at(\init, State::Sx);
+ if (!init.is_fully_undef() && !init.is_fully_zero())
+ reject;
+ }
+endcode
+
+match ff
+ select ff->type.in($dff)
+ // DSP48E1 does not support clock inversion
+ select param(ff, \CLK_POLARITY).as_bool()
+
+ slice offset GetSize(port(ff, \D))
+ index <SigBit> port(ff, \Q)[offset] === argQ[0]
+
+ // Check that the rest of argQ is present
+ filter GetSize(port(ff, \Q)) >= offset + GetSize(argQ)
+ filter port(ff, \Q).extract(offset, GetSize(argQ)) == argQ
+
+ set ffoffset offset
+endmatch
+
+code argQ argD
+{
+ if (clock != SigBit()) {
+ if (port(ff, \CLK) != clock)
+ reject;
+ if (param(ff, \CLK_POLARITY).as_bool() != clock_pol)
+ reject;
+ }
+
+ SigSpec Q = port(ff, \Q);
+ dff = ff;
+ dffclock = port(ff, \CLK);
+ dffclock_pol = param(ff, \CLK_POLARITY).as_bool();
+ dffD = argQ;
+ argD = port(ff, \D);
+ argQ = Q;
+ dffD.replace(argQ, argD);
+ // Only search for ffrstmux if dffD only
+ // has two (ff, ffrstmux) users
+ if (nusers(dffD) > 2)
+ argD = SigSpec();
+}
+endcode
+
+match ffrstmux
+ if false /* TODO: ice40 resets are actually async */
+
+ if !argD.empty()
+ select ffrstmux->type.in($mux)
+ index <SigSpec> port(ffrstmux, \Y) === argD
+
+ choice <IdString> BA {\B, \A}
+ // DSP48E1 only supports reset to zero
+ select port(ffrstmux, BA).is_fully_zero()
+
+ define <bool> pol (BA == \B)
+ set ffrstpol pol
+ semioptional
+endmatch
+
+code argD
+ if (ffrstmux) {
+ dffrstmux = ffrstmux;
+ dffrstpol = ffrstpol;
+ argD = port(ffrstmux, ffrstpol ? \A : \B);
+ dffD.replace(port(ffrstmux, \Y), argD);
+
+ // Only search for ffholdmux if argQ has at
+ // least 3 users (ff, <upstream>, ffrstmux) and
+ // dffD only has two (ff, ffrstmux)
+ if (!(nusers(argQ) >= 3 && nusers(dffD) == 2))
+ argD = SigSpec();
+ }
+ else
+ dffrstmux = nullptr;
+endcode
+
+match ffholdmux
+ if !argD.empty()
+ select ffholdmux->type.in($mux)
+ index <SigSpec> port(ffholdmux, \Y) === argD
+ choice <IdString> BA {\B, \A}
+ index <SigSpec> port(ffholdmux, BA) === argQ
+ define <bool> pol (BA == \B)
+ set ffholdpol pol
+ semioptional
+endmatch
+
+code argD
+ if (ffholdmux) {
+ dffholdmux = ffholdmux;
+ dffholdpol = ffholdpol;
+ argD = port(ffholdmux, ffholdpol ? \A : \B);
+ dffD.replace(port(ffholdmux, \Y), argD);
+ }
+ else
+ dffholdmux = nullptr;
+endcode
+
+// #######################
+
+subpattern out_dffe
+arg argD argQ clock clock_pol
+
+code
+ dff = nullptr;
+ for (auto c : argD.chunks())
+ if (c.wire->get_bool_attribute(\keep))
+ reject;
+endcode
+
+match ffholdmux
+ select ffholdmux->type.in($mux)
+ // ffholdmux output must have two users: ffholdmux and ff.D
+ select nusers(port(ffholdmux, \Y)) == 2
+
+ choice <IdString> BA {\B, \A}
+ // keep-last-value net must have at least three users: ffholdmux, ff, downstream sink(s)
+ select nusers(port(ffholdmux, BA)) >= 3
+
+ slice offset GetSize(port(ffholdmux, \Y))
+ define <IdString> AB (BA == \B ? \A : \B)
+ index <SigBit> port(ffholdmux, AB)[offset] === argD[0]
+
+ // Check that the rest of argD is present
+ filter GetSize(port(ffholdmux, AB)) >= offset + GetSize(argD)
+ filter port(ffholdmux, AB).extract(offset, GetSize(argD)) == argD
+
+ set ffoffset offset
+ define <bool> pol (BA == \B)
+ set ffholdpol pol
+
+ semioptional
+endmatch
+
+code argD argQ
+ dffholdmux = ffholdmux;
+ if (ffholdmux) {
+ SigSpec AB = port(ffholdmux, ffholdpol ? \A : \B);
+ SigSpec Y = port(ffholdmux, \Y);
+ argQ = argD;
+ argD.replace(AB, Y);
+ argQ.replace(AB, port(ffholdmux, ffholdpol ? \B : \A));
+
+ dffholdmux = ffholdmux;
+ dffholdpol = ffholdpol;
+ }
+endcode
+
+match ffrstmux
+ if false /* TODO: ice40 resets are actually async */
+
+ select ffrstmux->type.in($mux)
+ // ffrstmux output must have two users: ffrstmux and ff.D
+ select nusers(port(ffrstmux, \Y)) == 2
+
+ choice <IdString> BA {\B, \A}
+ // DSP48E1 only supports reset to zero
+ select port(ffrstmux, BA).is_fully_zero()
+
+ slice offset GetSize(port(ffrstmux, \Y))
+ define <IdString> AB (BA == \B ? \A : \B)
+ index <SigBit> port(ffrstmux, AB)[offset] === argD[0]
+
+ // Check that offset is consistent
+ filter !ffholdmux || ffoffset == offset
+ // Check that the rest of argD is present
+ filter GetSize(port(ffrstmux, AB)) >= offset + GetSize(argD)
+ filter port(ffrstmux, AB).extract(offset, GetSize(argD)) == argD
+
+ set ffoffset offset
+ define <bool> pol (AB == \A)
+ set ffrstpol pol
+
+ semioptional
+endmatch
+
+code argD argQ
+ dffrstmux = ffrstmux;
+ if (ffrstmux) {
+ SigSpec AB = port(ffrstmux, ffrstpol ? \A : \B);
+ SigSpec Y = port(ffrstmux, \Y);
+ argD.replace(AB, Y);
+
+ dffrstmux = ffrstmux;
+ dffrstpol = ffrstpol;
+ }
+endcode
+
+match ff
+ select ff->type.in($dff)
+ // DSP48E1 does not support clock inversion
+ select param(ff, \CLK_POLARITY).as_bool()
+
+ slice offset GetSize(port(ff, \D))
+ index <SigBit> port(ff, \D)[offset] === argD[0]
+
+ // Check that offset is consistent
+ filter (!ffholdmux && !ffrstmux) || ffoffset == offset
+ // Check that the rest of argD is present
+ filter GetSize(port(ff, \D)) >= offset + GetSize(argD)
+ filter port(ff, \D).extract(offset, GetSize(argD)) == argD
+ // Check that FF.Q is connected to CE-mux
+ filter !ffholdmux || port(ff, \Q).extract(offset, GetSize(argQ)) == argQ
+
+ set ffoffset offset
+endmatch
+
+code argQ
+ if (ff) {
+ if (clock != SigBit()) {
+ if (port(ff, \CLK) != clock)
+ reject;
+ if (param(ff, \CLK_POLARITY).as_bool() != clock_pol)
+ reject;
+ }
+ SigSpec D = port(ff, \D);
+ SigSpec Q = port(ff, \Q);
+ if (!ffholdmux) {
+ argQ = argD;
+ argQ.replace(D, Q);
+ }
+
+ for (auto c : argQ.chunks()) {
+ Const init = c.wire->attributes.at(\init, State::Sx);
+ if (!init.is_fully_undef() && !init.is_fully_zero())
+ reject;
+ }
+
+ dff = ff;
+ dffQ = argQ;
+ dffclock = port(ff, \CLK);
+ dffclock_pol = param(ff, \CLK_POLARITY).as_bool();
+ }
+ // No enable/reset mux possible without flop
+ else if (dffholdmux || dffrstmux)
+ reject;
+endcode
diff --git a/passes/pmgen/ice40_wrapcarry.cc b/passes/pmgen/ice40_wrapcarry.cc
new file mode 100644
index 000000000..0053c8872
--- /dev/null
+++ b/passes/pmgen/ice40_wrapcarry.cc
@@ -0,0 +1,158 @@
+/*
+ * 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
+
+#include "passes/pmgen/ice40_wrapcarry_pm.h"
+
+void create_ice40_wrapcarry(ice40_wrapcarry_pm &pm)
+{
+ auto &st = pm.st_ice40_wrapcarry;
+
+#if 0
+ log("\n");
+ log("carry: %s\n", log_id(st.carry, "--"));
+ log("lut: %s\n", log_id(st.lut, "--"));
+#endif
+
+ log(" replacing SB_LUT + SB_CARRY with $__ICE40_CARRY_WRAPPER cell.\n");
+
+ Cell *cell = pm.module->addCell(NEW_ID, "$__ICE40_CARRY_WRAPPER");
+ pm.module->swap_names(cell, st.carry);
+
+ cell->setPort("\\A", st.carry->getPort("\\I0"));
+ cell->setPort("\\B", st.carry->getPort("\\I1"));
+ auto CI = st.carry->getPort("\\CI");
+ cell->setPort("\\CI", CI);
+ cell->setPort("\\CO", st.carry->getPort("\\CO"));
+
+ cell->setPort("\\I0", st.lut->getPort("\\I0"));
+ auto I3 = st.lut->getPort("\\I3");
+ if (pm.sigmap(CI) == pm.sigmap(I3)) {
+ cell->setParam("\\I3_IS_CI", State::S1);
+ I3 = State::Sx;
+ }
+ else
+ cell->setParam("\\I3_IS_CI", State::S0);
+ cell->setPort("\\I3", I3);
+ cell->setPort("\\O", st.lut->getPort("\\O"));
+ cell->setParam("\\LUT", st.lut->getParam("\\LUT_INIT"));
+
+ for (const auto &a : st.carry->attributes)
+ cell->attributes[stringf("\\SB_CARRY.%s", a.first.c_str())] = a.second;
+ for (const auto &a : st.lut->attributes)
+ cell->attributes[stringf("\\SB_LUT4.%s", a.first.c_str())] = a.second;
+ cell->attributes[ID(SB_LUT4.name)] = Const(st.lut->name.str());
+ if (st.carry->get_bool_attribute(ID::keep) || st.lut->get_bool_attribute(ID::keep))
+ cell->attributes[ID::keep] = true;
+
+ pm.autoremove(st.carry);
+ pm.autoremove(st.lut);
+}
+
+struct Ice40WrapCarryPass : public Pass {
+ Ice40WrapCarryPass() : Pass("ice40_wrapcarry", "iCE40: wrap carries") { }
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" ice40_wrapcarry [selection]\n");
+ log("\n");
+ log("Wrap manually instantiated SB_CARRY cells, along with their associated SB_LUT4s,\n");
+ log("into an internal $__ICE40_CARRY_WRAPPER cell for preservation across technology\n");
+ log("mapping.\n");
+ log("\n");
+ log("Attributes on both cells will have their names prefixed with 'SB_CARRY.' or\n");
+ log("'SB_LUT4.' and attached to the wrapping cell.\n");
+ log("A (* keep *) attribute on either cell will be logically OR-ed together.\n");
+ log("\n");
+ log(" -unwrap\n");
+ log(" unwrap $__ICE40_CARRY_WRAPPER cells back into SB_CARRYs and SB_LUT4s,\n");
+ log(" including restoring their attributes.\n");
+ log("\n");
+ }
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ bool unwrap = false;
+
+ log_header(design, "Executing ICE40_WRAPCARRY pass (wrap carries).\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ if (args[argidx] == "-unwrap") {
+ unwrap = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto module : design->selected_modules()) {
+ if (!unwrap)
+ ice40_wrapcarry_pm(module, module->selected_cells()).run_ice40_wrapcarry(create_ice40_wrapcarry);
+ else {
+ for (auto cell : module->selected_cells()) {
+ if (cell->type != ID($__ICE40_CARRY_WRAPPER))
+ continue;
+
+ auto carry = module->addCell(NEW_ID, ID(SB_CARRY));
+ carry->setPort(ID(I0), cell->getPort(ID(A)));
+ carry->setPort(ID(I1), cell->getPort(ID(B)));
+ carry->setPort(ID(CI), cell->getPort(ID(CI)));
+ carry->setPort(ID(CO), cell->getPort(ID(CO)));
+ module->swap_names(carry, cell);
+ auto lut_name = cell->attributes.at(ID(SB_LUT4.name), Const(NEW_ID.str())).decode_string();
+ auto lut = module->addCell(lut_name, ID($lut));
+ lut->setParam(ID(WIDTH), 4);
+ lut->setParam(ID(LUT), cell->getParam(ID(LUT)));
+ auto I3 = cell->getPort(cell->getParam(ID(I3_IS_CI)).as_bool() ? ID(CI) : ID(I3));
+ lut->setPort(ID(A), { I3, cell->getPort(ID(B)), cell->getPort(ID(A)), cell->getPort(ID(I0)) });
+ lut->setPort(ID(Y), cell->getPort(ID(O)));
+
+ Const src;
+ for (const auto &a : cell->attributes)
+ if (a.first.begins_with("\\SB_CARRY.\\"))
+ carry->attributes[a.first.c_str() + strlen("\\SB_CARRY.")] = a.second;
+ else if (a.first.begins_with("\\SB_LUT4.\\"))
+ lut->attributes[a.first.c_str() + strlen("\\SB_LUT4.")] = a.second;
+ else if (a.first == ID(src))
+ src = a.second;
+ else if (a.first.in(ID(SB_LUT4.name), ID::keep, ID(module_not_derived)))
+ continue;
+ else
+ log_abort();
+
+ if (!src.empty()) {
+ carry->attributes.insert(std::make_pair(ID(src), src));
+ lut->attributes.insert(std::make_pair(ID(src), src));
+ }
+
+ module->remove(cell);
+ }
+ }
+ }
+ }
+} Ice40WrapCarryPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/pmgen/ice40_wrapcarry.pmg b/passes/pmgen/ice40_wrapcarry.pmg
new file mode 100644
index 000000000..bb59edb0c
--- /dev/null
+++ b/passes/pmgen/ice40_wrapcarry.pmg
@@ -0,0 +1,15 @@
+pattern ice40_wrapcarry
+
+match carry
+ select carry->type.in(\SB_CARRY)
+endmatch
+
+match lut
+ select lut->type.in(\SB_LUT4)
+ index <SigSpec> port(lut, \I1) === port(carry, \I0)
+ index <SigSpec> port(lut, \I2) === port(carry, \I1)
+endmatch
+
+code
+ accept;
+endcode
diff --git a/passes/pmgen/peepopt.cc b/passes/pmgen/peepopt.cc
new file mode 100644
index 000000000..2230145df
--- /dev/null
+++ b/passes/pmgen/peepopt.cc
@@ -0,0 +1,131 @@
+/*
+ * 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
+
+bool did_something;
+dict<SigBit, State> initbits;
+pool<SigBit> rminitbits;
+
+#include "passes/pmgen/peepopt_pm.h"
+#include "generate.h"
+
+struct PeepoptPass : public Pass {
+ PeepoptPass() : Pass("peepopt", "collection of peephole optimizers") { }
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" peepopt [options] [selection]\n");
+ log("\n");
+ log("This pass applies a collection of peephole optimizers to the current design.\n");
+ log("\n");
+ }
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ std::string genmode;
+
+ log_header(design, "Executing PEEPOPT pass (run peephole optimizers).\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ if (args[argidx] == "-generate" && argidx+1 < args.size()) {
+ genmode = args[++argidx];
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ if (!genmode.empty())
+ {
+ initbits.clear();
+ rminitbits.clear();
+
+ if (genmode == "shiftmul")
+ GENERATE_PATTERN(peepopt_pm, shiftmul);
+ else if (genmode == "muldiv")
+ GENERATE_PATTERN(peepopt_pm, muldiv);
+ else if (genmode == "dffmux")
+ GENERATE_PATTERN(peepopt_pm, dffmux);
+ else
+ log_abort();
+ return;
+ }
+
+ for (auto module : design->selected_modules())
+ {
+ did_something = true;
+
+ while (did_something)
+ {
+ did_something = false;
+ initbits.clear();
+ rminitbits.clear();
+
+ peepopt_pm pm(module);
+
+ for (auto w : module->wires()) {
+ auto it = w->attributes.find(ID(init));
+ if (it != w->attributes.end()) {
+ SigSpec sig = pm.sigmap(w);
+ Const val = it->second;
+ int len = std::min(GetSize(sig), GetSize(val));
+ for (int i = 0; i < len; i++) {
+ if (sig[i].wire == nullptr)
+ continue;
+ if (val[i] != State::S0 && val[i] != State::S1)
+ continue;
+ initbits[sig[i]] = val[i];
+ }
+ }
+ }
+
+ pm.setup(module->selected_cells());
+
+ pm.run_shiftmul();
+ pm.run_muldiv();
+ pm.run_dffmux();
+
+ for (auto w : module->wires()) {
+ auto it = w->attributes.find(ID(init));
+ if (it != w->attributes.end()) {
+ SigSpec sig = pm.sigmap(w);
+ Const &val = it->second;
+ int len = std::min(GetSize(sig), GetSize(val));
+ for (int i = 0; i < len; i++) {
+ if (rminitbits.count(sig[i]))
+ val[i] = State::Sx;
+ }
+ }
+ }
+
+ initbits.clear();
+ rminitbits.clear();
+ }
+ }
+ }
+} PeepoptPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/pmgen/peepopt_dffmux.pmg b/passes/pmgen/peepopt_dffmux.pmg
new file mode 100644
index 000000000..0069b0570
--- /dev/null
+++ b/passes/pmgen/peepopt_dffmux.pmg
@@ -0,0 +1,171 @@
+pattern dffmux
+
+state <IdString> cemuxAB rstmuxBA
+state <SigSpec> sigD
+
+match dff
+ select dff->type == $dff
+ select GetSize(port(dff, \D)) > 1
+endmatch
+
+code sigD
+ sigD = port(dff, \D);
+endcode
+
+match rstmux
+ select rstmux->type == $mux
+ select GetSize(port(rstmux, \Y)) > 1
+ index <SigSpec> port(rstmux, \Y) === sigD
+ choice <IdString> BA {\B, \A}
+ select port(rstmux, BA).is_fully_const()
+ set rstmuxBA BA
+ semioptional
+endmatch
+
+code sigD
+ if (rstmux)
+ sigD = port(rstmux, rstmuxBA == \B ? \A : \B);
+endcode
+
+match cemux
+ select cemux->type == $mux
+ select GetSize(port(cemux, \Y)) > 1
+ index <SigSpec> port(cemux, \Y) === sigD
+ choice <IdString> AB {\A, \B}
+ index <SigSpec> port(cemux, AB) === port(dff, \Q)
+ set cemuxAB AB
+ semioptional
+endmatch
+
+code
+ if (!cemux && !rstmux)
+ reject;
+endcode
+
+code
+ Const rst;
+ SigSpec D;
+ if (cemux) {
+ D = port(cemux, cemuxAB == \A ? \B : \A);
+ if (rstmux)
+ rst = port(rstmux, rstmuxBA).as_const();
+ else
+ rst = Const(State::Sx, GetSize(D));
+ }
+ else {
+ log_assert(rstmux);
+ D = port(rstmux, rstmuxBA == \B ? \A : \B);
+ rst = port(rstmux, rstmuxBA).as_const();
+ }
+ SigSpec Q = port(dff, \Q);
+ int width = GetSize(D);
+
+ SigSpec dffD = dff->getPort(\D);
+ SigSpec dffQ = dff->getPort(\Q);
+
+ Const initval;
+ for (auto b : Q) {
+ auto it = initbits.find(b);
+ initval.bits.push_back(it == initbits.end() ? State::Sx : it->second);
+ }
+
+ auto cmpx = [=](State lhs, State rhs) {
+ if (lhs == State::Sx || rhs == State::Sx)
+ return true;
+ return lhs == rhs;
+ };
+
+ int i = width-1;
+ while (i > 1) {
+ if (D[i] != D[i-1])
+ break;
+ if (!cmpx(rst[i], rst[i-1]))
+ break;
+ if (!cmpx(initval[i], initval[i-1]))
+ break;
+ if (!cmpx(rst[i], initval[i]))
+ break;
+ rminitbits.insert(Q[i]);
+ module->connect(Q[i], Q[i-1]);
+ i--;
+ }
+ if (i < width-1) {
+ did_something = true;
+ if (cemux) {
+ SigSpec ceA = cemux->getPort(\A);
+ SigSpec ceB = cemux->getPort(\B);
+ SigSpec ceY = cemux->getPort(\Y);
+ ceA.remove(i, width-1-i);
+ ceB.remove(i, width-1-i);
+ ceY.remove(i, width-1-i);
+ cemux->setPort(\A, ceA);
+ cemux->setPort(\B, ceB);
+ cemux->setPort(\Y, ceY);
+ cemux->fixup_parameters();
+ blacklist(cemux);
+ }
+ if (rstmux) {
+ SigSpec rstA = rstmux->getPort(\A);
+ SigSpec rstB = rstmux->getPort(\B);
+ SigSpec rstY = rstmux->getPort(\Y);
+ rstA.remove(i, width-1-i);
+ rstB.remove(i, width-1-i);
+ rstY.remove(i, width-1-i);
+ rstmux->setPort(\A, rstA);
+ rstmux->setPort(\B, rstB);
+ rstmux->setPort(\Y, rstY);
+ rstmux->fixup_parameters();
+ blacklist(rstmux);
+ }
+ dffD.remove(i, width-1-i);
+ dffQ.remove(i, width-1-i);
+ dff->setPort(\D, dffD);
+ dff->setPort(\Q, dffQ);
+ dff->fixup_parameters();
+ blacklist(dff);
+
+ log("dffcemux pattern in %s: dff=%s, cemux=%s, rstmux=%s; removed top %d bits.\n", log_id(module), log_id(dff), log_id(cemux, "n/a"), log_id(rstmux, "n/a"), width-1-i);
+ width = i+1;
+ }
+ if (cemux) {
+ SigSpec ceA = cemux->getPort(\A);
+ SigSpec ceB = cemux->getPort(\B);
+ SigSpec ceY = cemux->getPort(\Y);
+
+ int count = 0;
+ for (int i = width-1; i >= 0; i--) {
+ if (D[i].wire)
+ continue;
+ if (cmpx(rst[i], D[i].data) && cmpx(initval[i], D[i].data)) {
+ count++;
+ rminitbits.insert(Q[i]);
+ module->connect(Q[i], D[i]);
+ ceA.remove(i);
+ ceB.remove(i);
+ ceY.remove(i);
+ dffD.remove(i);
+ dffQ.remove(i);
+ }
+ }
+ if (count > 0)
+ {
+ did_something = true;
+
+ cemux->setPort(\A, ceA);
+ cemux->setPort(\B, ceB);
+ cemux->setPort(\Y, ceY);
+ cemux->fixup_parameters();
+ blacklist(cemux);
+
+ dff->setPort(\D, dffD);
+ dff->setPort(\Q, dffQ);
+ dff->fixup_parameters();
+ blacklist(dff);
+
+ log("dffcemux pattern in %s: dff=%s, cemux=%s, rstmux=%s; removed %d constant bits.\n", log_id(module), log_id(dff), log_id(cemux), log_id(rstmux, "n/a"), count);
+ }
+ }
+
+ if (did_something)
+ accept;
+endcode
diff --git a/passes/pmgen/peepopt_muldiv.pmg b/passes/pmgen/peepopt_muldiv.pmg
new file mode 100644
index 000000000..7cad759d0
--- /dev/null
+++ b/passes/pmgen/peepopt_muldiv.pmg
@@ -0,0 +1,36 @@
+pattern muldiv
+
+state <SigSpec> t x y
+
+match mul
+ select mul->type == $mul
+ select GetSize(port(mul, \A)) + GetSize(port(mul, \B)) <= GetSize(port(mul, \Y))
+endmatch
+
+code t x y
+ t = port(mul, \Y);
+ x = port(mul, \A);
+ y = port(mul, \B);
+ branch;
+ std::swap(x, y);
+endcode
+
+match div
+ select div->type.in($div)
+ index <SigSpec> port(div, \A) === t
+ index <SigSpec> port(div, \B) === x
+endmatch
+
+code
+ SigSpec div_y = port(div, \Y);
+ SigSpec val_y = y;
+
+ if (GetSize(div_y) != GetSize(val_y))
+ val_y.extend_u0(GetSize(div_y), param(div, \A_SIGNED).as_bool());
+
+ did_something = true;
+ log("muldiv pattern in %s: mul=%s, div=%s\n", log_id(module), log_id(mul), log_id(div));
+ module->connect(div_y, val_y);
+ autoremove(div);
+ accept;
+endcode
diff --git a/passes/pmgen/peepopt_shiftmul.pmg b/passes/pmgen/peepopt_shiftmul.pmg
new file mode 100644
index 000000000..d4748ae19
--- /dev/null
+++ b/passes/pmgen/peepopt_shiftmul.pmg
@@ -0,0 +1,96 @@
+pattern shiftmul
+//
+// Optimize mul+shift pairs that result from expressions such as foo[s*W+:W]
+//
+
+state <SigSpec> shamt
+
+match shift
+ select shift->type.in($shift, $shiftx, $shr)
+endmatch
+
+code shamt
+ shamt = port(shift, \B);
+ if (shamt.empty())
+ reject;
+ if (shamt[GetSize(shamt)-1] == State::S0) {
+ do {
+ shamt.remove(GetSize(shamt)-1);
+ if (shamt.empty())
+ reject;
+ } while (shamt[GetSize(shamt)-1] == State::S0);
+ } else
+ if (shift->type.in($shift, $shiftx) && param(shift, \B_SIGNED).as_bool()) {
+ reject;
+ }
+ if (GetSize(shamt) > 20)
+ reject;
+endcode
+
+match mul
+ select mul->type.in($mul)
+ select port(mul, \A).is_fully_const() || port(mul, \B).is_fully_const()
+ index <SigSpec> port(mul, \Y) === shamt
+endmatch
+
+code
+{
+ IdString const_factor_port = port(mul, \A).is_fully_const() ? \A : \B;
+ IdString const_factor_signed = const_factor_port == \A ? \A_SIGNED : \B_SIGNED;
+ Const const_factor_cnst = port(mul, const_factor_port).as_const();
+ int const_factor = const_factor_cnst.as_int();
+
+ if (GetSize(const_factor_cnst) == 0)
+ reject;
+
+ if (const_factor_cnst.bits[GetSize(const_factor_cnst)-1] != State::S0 &&
+ param(mul, const_factor_signed).as_bool())
+ reject;
+
+ if (GetSize(const_factor_cnst) > 20)
+ reject;
+
+ if (GetSize(port(shift, \Y)) > const_factor)
+ reject;
+
+ int factor_bits = ceil_log2(const_factor);
+ SigSpec mul_din = port(mul, const_factor_port == \A ? \B : \A);
+
+ if (GetSize(shamt) < factor_bits+GetSize(mul_din))
+ reject;
+
+ did_something = true;
+ log("shiftmul pattern in %s: shift=%s, mul=%s\n", log_id(module), log_id(shift), log_id(mul));
+
+ int new_const_factor = 1 << factor_bits;
+ SigSpec padding(State::Sx, new_const_factor-const_factor);
+ SigSpec old_a = port(shift, \A), new_a;
+ int trunc = 0;
+
+ if (GetSize(old_a) % const_factor != 0) {
+ trunc = const_factor - GetSize(old_a) % const_factor;
+ old_a.append(SigSpec(State::Sx, trunc));
+ }
+
+ for (int i = 0; i*const_factor < GetSize(old_a); i++) {
+ SigSpec slice = old_a.extract(i*const_factor, const_factor);
+ new_a.append(slice);
+ new_a.append(padding);
+ }
+
+ if (trunc > 0)
+ new_a.remove(GetSize(new_a)-trunc, trunc);
+
+ SigSpec new_b = {mul_din, SigSpec(State::S0, factor_bits)};
+ if (param(shift, \B_SIGNED).as_bool())
+ new_b.append(State::S0);
+
+ shift->setPort(\A, new_a);
+ shift->setParam(\A_WIDTH, GetSize(new_a));
+ shift->setPort(\B, new_b);
+ shift->setParam(\B_WIDTH, GetSize(new_b));
+
+ blacklist(shift);
+ accept;
+}
+endcode
diff --git a/passes/pmgen/pmgen.py b/passes/pmgen/pmgen.py
new file mode 100644
index 000000000..df0ffaff2
--- /dev/null
+++ b/passes/pmgen/pmgen.py
@@ -0,0 +1,797 @@
+#!/usr/bin/env python3
+
+import re
+import sys
+import pprint
+import getopt
+
+pp = pprint.PrettyPrinter(indent=4)
+
+prefix = None
+pmgfiles = list()
+outfile = None
+debug = False
+genhdr = False
+
+opts, args = getopt.getopt(sys.argv[1:], "p:o:dg")
+
+for o, a in opts:
+ if o == "-p":
+ prefix = a
+ elif o == "-o":
+ outfile = a
+ elif o == "-d":
+ debug = True
+ elif o == "-g":
+ genhdr = True
+
+if outfile is None:
+ outfile = "/dev/stdout"
+
+for a in args:
+ assert a.endswith(".pmg")
+ if prefix is None and len(args) == 1:
+ prefix = a[0:-4]
+ prefix = prefix.split('/')[-1]
+ pmgfiles.append(a)
+
+assert prefix is not None
+
+current_pattern = None
+current_subpattern = None
+patterns = dict()
+subpatterns = dict()
+subpattern_args = dict()
+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)
+
+def process_pmgfile(f, filename):
+ linenr = 0
+ global current_pattern
+ global current_subpattern
+ while True:
+ linenr += 1
+ 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 == "pattern":
+ if current_pattern is not None:
+ block = dict()
+ block["type"] = "final"
+ block["pattern"] = (current_pattern, current_subpattern)
+ blocks.append(block)
+ line = line.split()
+ assert len(line) == 2
+ assert line[1] not in patterns
+ current_pattern = line[1]
+ current_subpattern = ""
+ patterns[current_pattern] = len(blocks)
+ subpatterns[(current_pattern, current_subpattern)] = len(blocks)
+ subpattern_args[(current_pattern, current_subpattern)] = list()
+ state_types[current_pattern] = dict()
+ udata_types[current_pattern] = dict()
+ continue
+
+ assert current_pattern is not None
+
+ if cmd == "fallthrough":
+ block = dict()
+ block["type"] = "fallthrough"
+ blocks.append(block)
+ line = line.split()
+ assert len(line) == 1
+ continue
+
+ if cmd == "subpattern":
+ if len(blocks) == 0 or blocks[-1]["type"] != "fallthrough":
+ block = dict()
+ block["type"] = "final"
+ block["pattern"] = (current_pattern, current_subpattern)
+ blocks.append(block)
+ elif len(blocks) and blocks[-1]["type"] == "fallthrough":
+ del blocks[-1]
+ line = line.split()
+ assert len(line) == 2
+ current_subpattern = line[1]
+ subpattern_args[(current_pattern, current_subpattern)] = list()
+ assert (current_pattern, current_subpattern) not in subpatterns
+ subpatterns[(current_pattern, current_subpattern)] = len(blocks)
+ continue
+
+ if cmd == "arg":
+ line = line.split()
+ assert len(line) > 1
+ subpattern_args[(current_pattern, current_subpattern)] += line[1:]
+ continue
+
+ 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[current_pattern]
+ state_types[current_pattern][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[current_pattern]
+ udata_types[current_pattern][s] = type_str
+ continue
+
+ if cmd == "match":
+ block = dict()
+ block["type"] = "match"
+ block["src"] = "%s:%d" % (filename, linenr)
+ block["pattern"] = (current_pattern, current_subpattern)
+
+ block["genargs"] = None
+ block["gencode"] = None
+
+ line = line.split()
+ assert len(line) == 2
+ assert (line[1] not in state_types[current_pattern]) or (state_types[current_pattern][line[1]] == "Cell*")
+ block["cell"] = line[1]
+ state_types[current_pattern][line[1]] = "Cell*";
+
+ block["if"] = list()
+ block["setup"] = list()
+ block["index"] = list()
+ block["filter"] = list()
+ block["sets"] = list()
+ block["optional"] = False
+ block["semioptional"] = False
+
+ while True:
+ linenr += 1
+ 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["setup"].append(("select", rewrite_cpp(b.strip())))
+ continue
+
+ if a[0] == "slice":
+ m = re.match(r"^\s*slice\s+(\S+)\s+(.*?)\s*$", l)
+ block["setup"].append(("slice", m.group(1), rewrite_cpp(m.group(2))))
+ continue
+
+ if a[0] == "choice":
+ m = re.match(r"^\s*choice\s+<(.*?)>\s+(\S+)\s+(.*?)\s*$", l)
+ block["setup"].append(("choice", m.group(1), m.group(2), rewrite_cpp(m.group(3))))
+ continue
+
+ if a[0] == "define":
+ m = re.match(r"^\s*define\s+<(.*?)>\s+(\S+)\s+(.*?)\s*$", l)
+ block["setup"].append(("define", m.group(1), m.group(2), rewrite_cpp(m.group(3))))
+ 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] == "set":
+ m = re.match(r"^\s*set\s+(\S+)\s+(.*?)\s*$", l)
+ block["sets"].append((m.group(1), rewrite_cpp(m.group(2))))
+ continue
+
+ if a[0] == "optional":
+ block["optional"] = True
+ continue
+
+ if a[0] == "semioptional":
+ block["semioptional"] = True
+ continue
+
+ if a[0] == "generate":
+ block["genargs"] = list([int(s) for s in a[1:]])
+ if len(block["genargs"]) == 0: block["genargs"].append(100)
+ if len(block["genargs"]) == 1: block["genargs"].append(0)
+ assert len(block["genargs"]) == 2
+ block["gencode"] = list()
+ while True:
+ linenr += 1
+ l = f.readline()
+ assert l != ""
+ a = l.split()
+ if len(a) == 1 and a[0] == "endmatch": break
+ block["gencode"].append(rewrite_cpp(l.rstrip()))
+ break
+
+ raise RuntimeError("'%s' statement not recognised on line %d" % (a[0], linenr))
+
+ if block["optional"]:
+ assert not block["semioptional"]
+
+ blocks.append(block)
+ continue
+
+ if cmd == "code":
+ block = dict()
+ block["type"] = "code"
+ block["src"] = "%s:%d" % (filename, linenr)
+ block["pattern"] = (current_pattern, current_subpattern)
+
+ block["code"] = list()
+ block["fcode"] = list()
+ block["states"] = set()
+
+ for s in line.split()[1:]:
+ if s not in state_types[current_pattern]:
+ raise RuntimeError("'%s' not in state_types" % s)
+ block["states"].add(s)
+
+ codetype = "code"
+
+ while True:
+ linenr += 1
+ l = f.readline()
+ assert l != ""
+ a = l.split()
+ if len(a) == 0: continue
+ if a[0] == "endcode": break
+
+ if a[0] == "finally":
+ codetype = "fcode"
+ continue
+
+ block[codetype].append(rewrite_cpp(l.rstrip()))
+
+ blocks.append(block)
+ continue
+
+ raise RuntimeError("'%s' command not recognised" % cmd)
+
+for fn in pmgfiles:
+ with open(fn, "r") as f:
+ process_pmgfile(f, fn)
+
+if current_pattern is not None:
+ block = dict()
+ block["type"] = "final"
+ block["pattern"] = (current_pattern, current_subpattern)
+ blocks.append(block)
+
+current_pattern = None
+current_subpattern = None
+
+if debug:
+ pp.pprint(blocks)
+
+with open(outfile, "w") as f:
+ for fn in pmgfiles:
+ print("// Generated by pmgen.py from {}".format(fn), file=f)
+ print("", file=f)
+
+ if genhdr:
+ 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;", file=f)
+ print(" bool setup_done;", file=f)
+ print(" bool generate_mode;", file=f)
+ print(" int accept_cnt;", file=f)
+ print("", file=f)
+
+ print(" uint32_t rngseed;", file=f)
+ print(" int rng(unsigned int n) {", file=f)
+ print(" rngseed ^= rngseed << 13;", file=f)
+ print(" rngseed ^= rngseed >> 17;", file=f)
+ print(" rngseed ^= rngseed << 5;", file=f)
+ print(" return rngseed % n;", file=f)
+ print(" }", 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])
+ value_types = ["Cell*"]
+ for entry in block["setup"]:
+ if entry[0] == "slice":
+ value_types.append("int")
+ if entry[0] == "choice":
+ value_types.append(entry[1])
+ if entry[0] == "define":
+ value_types.append(entry[1])
+ print(" typedef std::tuple<{}> index_{}_key_type;".format(", ".join(index_types), index), file=f)
+ print(" typedef std::tuple<{}> index_{}_value_type;".format(", ".join(value_types), index), file=f)
+ print(" dict<index_{}_key_type, vector<index_{}_value_type>> index_{};".format(index, 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(" dict<Cell*,int> rollback_cache;", file=f)
+ print(" int rollback;", file=f)
+ print("", file=f)
+
+ for current_pattern in sorted(patterns.keys()):
+ print(" struct state_{}_t {{".format(current_pattern), file=f)
+ for s, t in sorted(state_types[current_pattern].items()):
+ print(" {} {};".format(t, s), file=f)
+ print(" }} st_{};".format(current_pattern), file=f)
+ print("", file=f)
+
+ print(" struct udata_{}_t {{".format(current_pattern), file=f)
+ for s, t in sorted(udata_types[current_pattern].items()):
+ print(" {} {};".format(t, s), file=f)
+ print(" }} ud_{};".format(current_pattern), file=f)
+ print("", file=f)
+ current_pattern = None
+
+ 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(" 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 && blacklist_cells.insert(cell).second) {", file=f)
+ print(" auto ptr = rollback_cache.find(cell);", file=f)
+ print(" if (ptr == rollback_cache.end()) return;", file=f)
+ print(" int rb = ptr->second;", file=f)
+ print(" if (rollback == 0 || rollback > rb)", file=f)
+ print(" rollback = rb;", 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(" autoremove_cells.insert(cell);", file=f)
+ print(" blacklist(cell);", file=f)
+ print(" }", file=f)
+ print(" }", file=f)
+ print("", file=f)
+
+ current_pattern = None
+
+ print(" SigSpec port(Cell *cell, IdString portname) {", file=f)
+ print(" return sigmap(cell->getPort(portname));", file=f)
+ print(" }", file=f)
+ print("", file=f)
+ print(" SigSpec port(Cell *cell, IdString portname, const SigSpec& defval) {", file=f)
+ print(" return sigmap(cell->connections_.at(portname, defval));", 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(" Const param(Cell *cell, IdString paramname, const Const& defval) {", file=f)
+ print(" return cell->parameters.at(paramname, defval);", 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), setup_done(false), generate_mode(false), rngseed(12345678) {", file=f)
+ print(" setup(cells);", file=f)
+ print(" }", file=f)
+ print("", file=f)
+
+ print(" {}_pm(Module *module) :".format(prefix), file=f)
+ print(" module(module), sigmap(module), setup_done(false), generate_mode(false), rngseed(12345678) {", file=f)
+ print(" }", file=f)
+ print("", file=f)
+
+ print(" void setup(const vector<Cell*> &cells) {", file=f)
+ for current_pattern in sorted(patterns.keys()):
+ for s, t in sorted(udata_types[current_pattern].items()):
+ if t.endswith("*"):
+ print(" ud_{}.{} = nullptr;".format(current_pattern,s), file=f)
+ else:
+ print(" ud_{}.{} = {}();".format(current_pattern, s, t), file=f)
+ current_pattern = None
+ print(" log_assert(!setup_done);", file=f)
+ print(" setup_done = true;", file=f)
+ print(" for (auto port : module->ports)", file=f)
+ print(" add_siguser(module->wire(port), nullptr);", 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(" 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)
+ print(" index_{}_value_type value;".format(index), file=f)
+ print(" std::get<0>(value) = cell;", file=f)
+ loopcnt = 0
+ valueidx = 1
+ for item in block["setup"]:
+ if item[0] == "select":
+ print(" if (!({})) continue;".format(item[1]), file=f)
+ if item[0] == "slice":
+ print(" int &{} = std::get<{}>(value);".format(item[1], valueidx), file=f)
+ print(" for ({} = 0; {} < {}; {}++) {{".format(item[1], item[1], item[2], item[1]), file=f)
+ valueidx += 1
+ loopcnt += 1
+ if item[0] == "choice":
+ print(" vector<{}> _pmg_choices_{} = {};".format(item[1], item[2], item[3]), file=f)
+ print(" for (const {} &{} : _pmg_choices_{}) {{".format(item[1], item[2], item[2]), file=f)
+ print(" std::get<{}>(value) = {};".format(valueidx, item[2]), file=f)
+ valueidx += 1
+ loopcnt += 1
+ if item[0] == "define":
+ print(" {} &{} = std::get<{}>(value);".format(item[1], item[2], valueidx), file=f)
+ print(" {} = {};".format(item[2], item[3]), file=f)
+ valueidx += 1
+ 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(value);".format(index), file=f)
+ for i in range(loopcnt):
+ print(" }", 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)
+
+ for current_pattern in sorted(patterns.keys()):
+ print(" int run_{}(std::function<void()> on_accept_f) {{".format(current_pattern), file=f)
+ print(" log_assert(setup_done);", file=f)
+ print(" accept_cnt = 0;", file=f)
+ print(" on_accept = on_accept_f;", file=f)
+ print(" rollback = 0;", file=f)
+ for s, t in sorted(state_types[current_pattern].items()):
+ if t.endswith("*"):
+ print(" st_{}.{} = nullptr;".format(current_pattern, s), file=f)
+ else:
+ print(" st_{}.{} = {}();".format(current_pattern, s, t), file=f)
+ print(" block_{}(1);".format(patterns[current_pattern]), file=f)
+ print(" log_assert(rollback_cache.empty());", file=f)
+ print(" return accept_cnt;", file=f)
+ print(" }", file=f)
+ print("", file=f)
+ print(" int run_{}(std::function<void({}_pm&)> on_accept_f) {{".format(current_pattern, prefix), file=f)
+ print(" return run_{}([&](){{on_accept_f(*this);}});".format(current_pattern), file=f)
+ print(" }", file=f)
+ print("", file=f)
+ print(" int run_{}() {{".format(current_pattern), file=f)
+ print(" return run_{}([](){{}});".format(current_pattern, current_pattern), file=f)
+ print(" }", file=f)
+ print("", file=f)
+
+ if len(subpatterns):
+ for p, s in sorted(subpatterns.keys()):
+ print(" void block_subpattern_{}_{}(int recursion) {{ block_{}(recursion); }}".format(p, s, subpatterns[(p, s)]), file=f)
+ print("", file=f)
+
+ current_pattern = None
+ current_subpattern = None
+
+ for index in range(len(blocks)):
+ block = blocks[index]
+
+ if block["type"] in ("match", "code"):
+ print(" // {}".format(block["src"]), file=f)
+
+ print(" void block_{}(int recursion YS_ATTRIBUTE(unused)) {{".format(index), file=f)
+ current_pattern, current_subpattern = block["pattern"]
+
+ if block["type"] == "final":
+ print(" }", file=f)
+ if index+1 != len(blocks):
+ print("", file=f)
+ continue
+
+ const_st = set()
+ nonconst_st = set()
+ restore_st = set()
+
+ for s in subpattern_args[(current_pattern, current_subpattern)]:
+ const_st.add(s)
+
+ for i in range(subpatterns[(current_pattern, current_subpattern)], 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"])
+ for item in blocks[i]["sets"]:
+ const_st.add(item[0])
+ 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)
+ for item in block["sets"]:
+ if item[0] in const_st:
+ const_st.remove(item[0])
+ nonconst_st.add(item[0])
+ else:
+ assert False
+
+ for s in sorted(const_st):
+ t = state_types[current_pattern][s]
+ if t.endswith("*"):
+ print(" {} const &{} YS_ATTRIBUTE(unused) = st_{}.{};".format(t, s, current_pattern, s), file=f)
+ else:
+ print(" const {} &{} YS_ATTRIBUTE(unused) = st_{}.{};".format(t, s, current_pattern, s), file=f)
+
+ for s in sorted(nonconst_st):
+ t = state_types[current_pattern][s]
+ print(" {} &{} YS_ATTRIBUTE(unused) = st_{}.{};".format(t, s, current_pattern, s), file=f)
+
+ for u in sorted(udata_types[current_pattern].keys()):
+ t = udata_types[current_pattern][u]
+ print(" {} &{} YS_ATTRIBUTE(unused) = ud_{}.{};".format(t, u, current_pattern, u), file=f)
+
+ if len(restore_st):
+ print("", file=f)
+ for s in sorted(restore_st):
+ t = state_types[current_pattern][s]
+ print(" {} _pmg_backup_{} = {};".format(t, s, s), file=f)
+
+ if block["type"] == "code":
+ print("", file=f)
+ print("#define reject do { goto rollback_label; } while(0)", file=f)
+ print("#define accept do { accept_cnt++; on_accept(); if (rollback) goto rollback_label; } while(0)", file=f)
+ print("#define finish do { rollback = -1; goto rollback_label; } while(0)", file=f)
+ print("#define branch do {{ block_{}(recursion+1); if (rollback) goto rollback_label; }} while(0)".format(index+1), file=f)
+ print("#define subpattern(pattern_name) do {{ block_subpattern_{}_ ## pattern_name (recursion+1); if (rollback) goto rollback_label; }} while(0)".format(current_pattern), file=f)
+
+ for line in block["code"]:
+ print(" " + line, file=f)
+
+ print("", file=f)
+ print(" block_{}(recursion+1);".format(index+1), file=f)
+
+ print("#undef reject", file=f)
+ print("#undef accept", file=f)
+ print("#undef finish", file=f)
+ print("#undef branch", file=f)
+ print("#undef subpattern", file=f)
+
+ print("", file=f)
+ print("rollback_label:", file=f)
+ print(" YS_ATTRIBUTE(unused);", file=f)
+
+ if len(block["fcode"]):
+ print("#define accept do { accept_cnt++; on_accept(); } while(0)", file=f)
+ print("#define finish do { rollback = -1; goto finish_label; } while(0)", file=f)
+ for line in block["fcode"]:
+ print(" " + line, file=f)
+ print("finish_label:", file=f)
+ print(" YS_ATTRIBUTE(unused);", file=f)
+ print("#undef accept", file=f)
+ print("#undef finish", file=f)
+
+ if len(restore_st) or len(nonconst_st):
+ print("", file=f)
+ for s in sorted(restore_st):
+ t = state_types[current_pattern][s]
+ print(" {} = _pmg_backup_{};".format(s, s), file=f)
+ for s in sorted(nonconst_st):
+ if s not in restore_st:
+ t = state_types[current_pattern][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
+
+ print(" Cell* _pmg_backup_{} = {};".format(block["cell"], block["cell"]), file=f)
+
+ 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_{}(recursion+1);".format(index+1), file=f)
+ print(" {} = _pmg_backup_{};".format(block["cell"], block["cell"]), 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(" auto cells_ptr = index_{}.find(key);".format(index), file=f)
+
+ if block["semioptional"] or block["genargs"] is not None:
+ print(" bool found_any_match = false;", file=f)
+
+ print("", file=f)
+ print(" if (cells_ptr != index_{}.end()) {{".format(index), file=f)
+ print(" const vector<index_{}_value_type> &cells = cells_ptr->second;".format(index), file=f)
+ print(" for (int _pmg_idx = 0; _pmg_idx < GetSize(cells); _pmg_idx++) {", file=f)
+ print(" {} = std::get<0>(cells[_pmg_idx]);".format(block["cell"]), file=f)
+ valueidx = 1
+ for item in block["setup"]:
+ if item[0] == "slice":
+ print(" const int &{} YS_ATTRIBUTE(unused) = std::get<{}>(cells[_pmg_idx]);".format(item[1], valueidx), file=f)
+ valueidx += 1
+ if item[0] == "choice":
+ print(" const {} &{} YS_ATTRIBUTE(unused) = std::get<{}>(cells[_pmg_idx]);".format(item[1], item[2], valueidx), file=f)
+ valueidx += 1
+ if item[0] == "define":
+ print(" const {} &{} YS_ATTRIBUTE(unused) = std::get<{}>(cells[_pmg_idx]);".format(item[1], item[2], valueidx), file=f)
+ valueidx += 1
+ print(" if (blacklist_cells.count({})) continue;".format(block["cell"]), file=f)
+ for expr in block["filter"]:
+ print(" if (!({})) continue;".format(expr), file=f)
+ if block["semioptional"] or block["genargs"] is not None:
+ print(" found_any_match = true;", file=f)
+ for item in block["sets"]:
+ print(" auto _pmg_backup_{} = {};".format(item[0], item[0]), file=f)
+ print(" {} = {};".format(item[0], item[1]), file=f)
+ print(" auto rollback_ptr = rollback_cache.insert(make_pair(std::get<0>(cells[_pmg_idx]), recursion));", file=f)
+ print(" block_{}(recursion+1);".format(index+1), file=f)
+ for item in block["sets"]:
+ print(" {} = _pmg_backup_{};".format(item[0], item[0]), file=f)
+ print(" if (rollback_ptr.second)", file=f)
+ print(" rollback_cache.erase(rollback_ptr.first);", file=f)
+ print(" if (rollback) {", file=f)
+ print(" if (rollback != recursion) {{".format(index+1), file=f)
+ print(" {} = _pmg_backup_{};".format(block["cell"], 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("", file=f)
+ print(" {} = nullptr;".format(block["cell"]), file=f)
+
+ if block["optional"]:
+ print(" block_{}(recursion+1);".format(index+1), file=f)
+
+ if block["semioptional"]:
+ print(" if (!found_any_match) block_{}(recursion+1);".format(index+1), file=f)
+
+ print(" {} = _pmg_backup_{};".format(block["cell"], block["cell"]), file=f)
+
+ if block["genargs"] is not None:
+ print("#define finish do { rollback = -1; return; } while(0)", file=f)
+ print(" if (generate_mode && rng(100) < (found_any_match ? {} : {})) {{".format(block["genargs"][1], block["genargs"][0]), file=f)
+ for line in block["gencode"]:
+ print(" " + line, file=f)
+ print(" }", file=f)
+ print("#undef finish", file=f)
+ else:
+ assert False
+
+ current_pattern = None
+ print(" }", file=f)
+ print("", file=f)
+
+ print("};", file=f)
+
+ if genhdr:
+ print("", file=f)
+ print("YOSYS_NAMESPACE_END", file=f)
diff --git a/passes/pmgen/test_pmgen.cc b/passes/pmgen/test_pmgen.cc
new file mode 100644
index 000000000..72dc18dcc
--- /dev/null
+++ b/passes/pmgen/test_pmgen.cc
@@ -0,0 +1,260 @@
+/*
+ * 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
+
+#include "passes/pmgen/test_pmgen_pm.h"
+#include "passes/pmgen/ice40_dsp_pm.h"
+#include "passes/pmgen/xilinx_srl_pm.h"
+
+#include "generate.h"
+
+void reduce_chain(test_pmgen_pm &pm)
+{
+ auto &st = pm.st_reduce;
+ auto &ud = pm.ud_reduce;
+
+ if (ud.longest_chain.empty())
+ return;
+
+ log("Found chain of length %d (%s):\n", GetSize(ud.longest_chain), log_id(st.first->type));
+
+ SigSpec A;
+ SigSpec Y = ud.longest_chain.front().first->getPort(ID(Y));
+ auto last_cell = ud.longest_chain.back().first;
+
+ for (auto it : ud.longest_chain) {
+ auto cell = it.first;
+ if (cell == last_cell) {
+ A.append(cell->getPort(ID(A)));
+ A.append(cell->getPort(ID(B)));
+ } else {
+ A.append(cell->getPort(it.second == ID(A) ? ID(B) : ID(A)));
+ }
+ log(" %s\n", log_id(cell));
+ pm.autoremove(cell);
+ }
+
+ Cell *c;
+
+ if (last_cell->type == ID($_AND_))
+ c = pm.module->addReduceAnd(NEW_ID, A, Y);
+ else if (last_cell->type == ID($_OR_))
+ c = pm.module->addReduceOr(NEW_ID, A, Y);
+ else if (last_cell->type == ID($_XOR_))
+ c = pm.module->addReduceXor(NEW_ID, A, Y);
+ else
+ log_abort();
+
+ log(" -> %s (%s)\n", log_id(c), log_id(c->type));
+}
+
+void reduce_tree(test_pmgen_pm &pm)
+{
+ auto &st = pm.st_reduce;
+ auto &ud = pm.ud_reduce;
+
+ if (ud.longest_chain.empty())
+ return;
+
+ SigSpec A = ud.leaves;
+ SigSpec Y = st.first->getPort(ID(Y));
+ pm.autoremove(st.first);
+
+ log("Found %s tree with %d leaves for %s (%s).\n", log_id(st.first->type),
+ GetSize(A), log_signal(Y), log_id(st.first));
+
+ Cell *c;
+
+ if (st.first->type == ID($_AND_))
+ c = pm.module->addReduceAnd(NEW_ID, A, Y);
+ else if (st.first->type == ID($_OR_))
+ c = pm.module->addReduceOr(NEW_ID, A, Y);
+ else if (st.first->type == ID($_XOR_))
+ c = pm.module->addReduceXor(NEW_ID, A, Y);
+ else
+ log_abort();
+
+ log(" -> %s (%s)\n", log_id(c), log_id(c->type));
+}
+
+void opt_eqpmux(test_pmgen_pm &pm)
+{
+ auto &st = pm.st_eqpmux;
+
+ SigSpec Y = st.pmux->getPort(ID::Y);
+ int width = GetSize(Y);
+
+ SigSpec EQ = st.pmux->getPort(ID::B).extract(st.pmux_slice_eq*width, width);
+ SigSpec NE = st.pmux->getPort(ID::B).extract(st.pmux_slice_ne*width, width);
+
+ log("Found eqpmux circuit driving %s (eq=%s, ne=%s, pmux=%s).\n",
+ log_signal(Y), log_id(st.eq), log_id(st.ne), log_id(st.pmux));
+
+ pm.autoremove(st.pmux);
+ Cell *c = pm.module->addMux(NEW_ID, NE, EQ, st.eq->getPort(ID::Y), Y);
+ log(" -> %s (%s)\n", log_id(c), log_id(c->type));
+}
+
+struct TestPmgenPass : public Pass {
+ TestPmgenPass() : Pass("test_pmgen", "test pass for pmgen") { }
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" test_pmgen -reduce_chain [options] [selection]\n");
+ log("\n");
+ log("Demo for recursive pmgen patterns. Map chains of AND/OR/XOR to $reduce_*.\n");
+ log("\n");
+
+ log("\n");
+ log(" test_pmgen -reduce_tree [options] [selection]\n");
+ log("\n");
+ log("Demo for recursive pmgen patterns. Map trees of AND/OR/XOR to $reduce_*.\n");
+ log("\n");
+
+ log("\n");
+ log(" test_pmgen -eqpmux [options] [selection]\n");
+ log("\n");
+ log("Demo for recursive pmgen patterns. Optimize EQ/NE/PMUX circuits.\n");
+ log("\n");
+
+ log("\n");
+ log(" test_pmgen -generate [options] <pattern_name>\n");
+ log("\n");
+ log("Create modules that match the specified pattern.\n");
+ log("\n");
+ }
+
+ void execute_reduce_chain(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ log_header(design, "Executing TEST_PMGEN pass (-reduce_chain).\n");
+
+ size_t argidx;
+ for (argidx = 2; argidx < args.size(); argidx++)
+ {
+ // if (args[argidx] == "-singleton") {
+ // singleton_mode = true;
+ // continue;
+ // }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto module : design->selected_modules())
+ while (test_pmgen_pm(module, module->selected_cells()).run_reduce(reduce_chain)) {}
+ }
+
+ void execute_reduce_tree(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ log_header(design, "Executing TEST_PMGEN pass (-reduce_tree).\n");
+
+ size_t argidx;
+ for (argidx = 2; argidx < args.size(); argidx++)
+ {
+ // if (args[argidx] == "-singleton") {
+ // singleton_mode = true;
+ // continue;
+ // }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto module : design->selected_modules())
+ test_pmgen_pm(module, module->selected_cells()).run_reduce(reduce_tree);
+ }
+
+ void execute_eqpmux(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ log_header(design, "Executing TEST_PMGEN pass (-eqpmux).\n");
+
+ size_t argidx;
+ for (argidx = 2; argidx < args.size(); argidx++)
+ {
+ // if (args[argidx] == "-singleton") {
+ // singleton_mode = true;
+ // continue;
+ // }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto module : design->selected_modules())
+ test_pmgen_pm(module, module->selected_cells()).run_eqpmux(opt_eqpmux);
+ }
+
+ void execute_generate(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ log_header(design, "Executing TEST_PMGEN pass (-generate).\n");
+
+ size_t argidx;
+ for (argidx = 2; argidx < args.size(); argidx++)
+ {
+ // if (args[argidx] == "-singleton") {
+ // singleton_mode = true;
+ // continue;
+ // }
+ break;
+ }
+
+ if (argidx+1 != args.size())
+ log_cmd_error("Expected exactly one pattern.\n");
+
+ string pattern = args[argidx];
+
+ if (pattern == "reduce")
+ return GENERATE_PATTERN(test_pmgen_pm, reduce);
+
+ if (pattern == "eqpmux")
+ return GENERATE_PATTERN(test_pmgen_pm, eqpmux);
+
+ if (pattern == "ice40_dsp")
+ return GENERATE_PATTERN(ice40_dsp_pm, ice40_dsp);
+
+ if (pattern == "xilinx_srl.fixed")
+ return GENERATE_PATTERN(xilinx_srl_pm, fixed);
+ if (pattern == "xilinx_srl.variable")
+ return GENERATE_PATTERN(xilinx_srl_pm, variable);
+
+ log_cmd_error("Unknown pattern: %s\n", pattern.c_str());
+ }
+
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ if (GetSize(args) > 1)
+ {
+ if (args[1] == "-reduce_chain")
+ return execute_reduce_chain(args, design);
+ if (args[1] == "-reduce_tree")
+ return execute_reduce_tree(args, design);
+ if (args[1] == "-eqpmux")
+ return execute_eqpmux(args, design);
+ if (args[1] == "-generate")
+ return execute_generate(args, design);
+ }
+ help();
+ log_cmd_error("Missing or unsupported mode parameter.\n");
+ }
+} TestPmgenPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/pmgen/test_pmgen.pmg b/passes/pmgen/test_pmgen.pmg
new file mode 100644
index 000000000..287ed97d8
--- /dev/null
+++ b/passes/pmgen/test_pmgen.pmg
@@ -0,0 +1,189 @@
+pattern reduce
+
+state <IdString> portname
+udata <vector<pair<Cell*, IdString>>> chain longest_chain
+udata <pool<Cell*>> non_first_cells
+udata <SigSpec> leaves
+
+code
+ non_first_cells.clear();
+ subpattern(setup);
+endcode
+
+match first
+ select first->type.in($_AND_, $_OR_, $_XOR_)
+ filter !non_first_cells.count(first)
+generate
+ SigSpec A = module->addWire(NEW_ID);
+ SigSpec B = module->addWire(NEW_ID);
+ SigSpec Y = module->addWire(NEW_ID);
+ switch (rng(3))
+ {
+ case 0:
+ module->addAndGate(NEW_ID, A, B, Y);
+ break;
+ case 1:
+ module->addOrGate(NEW_ID, A, B, Y);
+ break;
+ case 2:
+ module->addXorGate(NEW_ID, A, B, Y);
+ break;
+ }
+endmatch
+
+code
+ leaves = SigSpec();
+ longest_chain.clear();
+ chain.push_back(make_pair(first, \A));
+ subpattern(tail);
+ chain.back().second = \B;
+ subpattern(tail);
+finally
+ chain.pop_back();
+ log_assert(chain.empty());
+ if (GetSize(longest_chain) > 1)
+ accept;
+endcode
+
+// ------------------------------------------------------------------
+
+subpattern setup
+
+match first
+ select first->type.in($_AND_, $_OR_, $_XOR_)
+endmatch
+
+code portname
+ portname = \A;
+ branch;
+ portname = \B;
+endcode
+
+match next
+ select next->type.in($_AND_, $_OR_, $_XOR_)
+ select nusers(port(next, \Y)) == 2
+ index <IdString> next->type === first->type
+ index <SigSpec> port(next, \Y) === port(first, portname)
+endmatch
+
+code
+ non_first_cells.insert(next);
+endcode
+
+// ------------------------------------------------------------------
+
+subpattern tail
+arg first
+
+match next
+ semioptional
+ select next->type.in($_AND_, $_OR_, $_XOR_)
+ select nusers(port(next, \Y)) == 2
+ index <IdString> next->type === chain.back().first->type
+ index <SigSpec> port(next, \Y) === port(chain.back().first, chain.back().second)
+generate 10
+ SigSpec A = module->addWire(NEW_ID);
+ SigSpec B = module->addWire(NEW_ID);
+ SigSpec Y = port(chain.back().first, chain.back().second);
+ Cell *c = module->addAndGate(NEW_ID, A, B, Y);
+ c->type = chain.back().first->type;
+endmatch
+
+code
+ if (next) {
+ chain.push_back(make_pair(next, \A));
+ subpattern(tail);
+ chain.back().second = \B;
+ subpattern(tail);
+ } else {
+ if (GetSize(chain) > GetSize(longest_chain))
+ longest_chain = chain;
+ leaves.append(port(chain.back().first, chain.back().second));
+ }
+finally
+ if (next)
+ chain.pop_back();
+endcode
+
+// ==================================================================
+
+pattern eqpmux
+
+state <bool> eq_ne_signed
+state <SigSpec> eq_inA eq_inB
+state <int> pmux_slice_eq pmux_slice_ne
+
+match eq
+ select eq->type == $eq
+ choice <IdString> AB {\A, \B}
+ define <IdString> BA AB == \A ? \B : \A
+ set eq_inA port(eq, \A)
+ set eq_inB port(eq, \B)
+ set eq_ne_signed param(eq, \A_SIGNED).as_bool()
+generate 100 10
+ SigSpec A = module->addWire(NEW_ID, rng(7)+1);
+ SigSpec B = module->addWire(NEW_ID, rng(7)+1);
+ SigSpec Y = module->addWire(NEW_ID);
+ module->addEq(NEW_ID, A, B, Y, rng(2));
+endmatch
+
+match pmux
+ select pmux->type == $pmux
+ slice idx GetSize(port(pmux, \S))
+ index <SigBit> port(pmux, \S)[idx] === port(eq, \Y)
+ set pmux_slice_eq idx
+generate 100 10
+ int width = rng(7) + 1;
+ int numsel = rng(4) + 1;
+ int idx = rng(numsel);
+
+ SigSpec A = module->addWire(NEW_ID, width);
+ SigSpec Y = module->addWire(NEW_ID, width);
+
+ SigSpec B, S;
+ for (int i = 0; i < numsel; i++) {
+ B.append(module->addWire(NEW_ID, width));
+ S.append(i == idx ? port(eq, \Y) : module->addWire(NEW_ID));
+ }
+
+ module->addPmux(NEW_ID, A, B, S, Y);
+endmatch
+
+match ne
+ select ne->type == $ne
+ choice <IdString> AB {\A, \B}
+ define <IdString> BA (AB == \A ? \B : \A)
+ index <SigSpec> port(ne, AB) === eq_inA
+ index <SigSpec> port(ne, BA) === eq_inB
+ index <int> param(ne, \A_SIGNED).as_bool() === eq_ne_signed
+generate 100 10
+ SigSpec A = eq_inA, B = eq_inB, Y;
+ if (rng(2)) {
+ std::swap(A, B);
+ }
+ if (rng(2)) {
+ for (auto bit : port(pmux, \S)) {
+ if (nusers(bit) < 2)
+ Y.append(bit);
+ }
+ if (GetSize(Y))
+ Y = Y[rng(GetSize(Y))];
+ else
+ Y = module->addWire(NEW_ID);
+ } else {
+ Y = module->addWire(NEW_ID);
+ }
+ module->addNe(NEW_ID, A, B, Y, rng(2));
+endmatch
+
+match pmux2
+ select pmux2->type == $pmux
+ slice idx GetSize(port(pmux2, \S))
+ index <Cell*> pmux2 === pmux
+ index <SigBit> port(pmux2, \S)[idx] === port(ne, \Y)
+ set pmux_slice_ne idx
+endmatch
+
+code
+ accept;
+endcode
diff --git a/passes/pmgen/xilinx_dsp.cc b/passes/pmgen/xilinx_dsp.cc
new file mode 100644
index 000000000..81c3c57c4
--- /dev/null
+++ b/passes/pmgen/xilinx_dsp.cc
@@ -0,0 +1,846 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * 2019 Eddie Hung <eddie@fpgeh.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
+ * 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 <deque>
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+#include "passes/pmgen/xilinx_dsp_pm.h"
+#include "passes/pmgen/xilinx_dsp48a_pm.h"
+#include "passes/pmgen/xilinx_dsp_CREG_pm.h"
+#include "passes/pmgen/xilinx_dsp_cascade_pm.h"
+
+static Cell* addDsp(Module *module) {
+ Cell *cell = module->addCell(NEW_ID, ID(DSP48E1));
+ cell->setParam(ID(ACASCREG), 0);
+ cell->setParam(ID(ADREG), 0);
+ cell->setParam(ID(A_INPUT), Const("DIRECT"));
+ cell->setParam(ID(ALUMODEREG), 0);
+ cell->setParam(ID(AREG), 0);
+ cell->setParam(ID(BCASCREG), 0);
+ cell->setParam(ID(B_INPUT), Const("DIRECT"));
+ cell->setParam(ID(BREG), 0);
+ cell->setParam(ID(CARRYINREG), 0);
+ cell->setParam(ID(CARRYINSELREG), 0);
+ cell->setParam(ID(CREG), 0);
+ cell->setParam(ID(DREG), 0);
+ cell->setParam(ID(INMODEREG), 0);
+ cell->setParam(ID(MREG), 0);
+ cell->setParam(ID(OPMODEREG), 0);
+ cell->setParam(ID(PREG), 0);
+ cell->setParam(ID(USE_MULT), Const("NONE"));
+ cell->setParam(ID(USE_SIMD), Const("ONE48"));
+ cell->setParam(ID(USE_DPORT), Const("FALSE"));
+
+ cell->setPort(ID(D), Const(0, 25));
+ cell->setPort(ID(INMODE), Const(0, 5));
+ cell->setPort(ID(ALUMODE), Const(0, 4));
+ cell->setPort(ID(OPMODE), Const(0, 7));
+ cell->setPort(ID(CARRYINSEL), Const(0, 3));
+ cell->setPort(ID(ACIN), Const(0, 30));
+ cell->setPort(ID(BCIN), Const(0, 18));
+ cell->setPort(ID(PCIN), Const(0, 48));
+ cell->setPort(ID(CARRYIN), Const(0, 1));
+ return cell;
+}
+
+void xilinx_simd_pack(Module *module, const std::vector<Cell*> &selected_cells)
+{
+ std::deque<Cell*> simd12_add, simd12_sub;
+ std::deque<Cell*> simd24_add, simd24_sub;
+
+ for (auto cell : selected_cells) {
+ if (!cell->type.in(ID($add), ID($sub)))
+ continue;
+ SigSpec Y = cell->getPort(ID(Y));
+ if (!Y.is_chunk())
+ continue;
+ if (!Y.as_chunk().wire->get_strpool_attribute(ID(use_dsp)).count("simd"))
+ continue;
+ if (GetSize(Y) > 25)
+ continue;
+ SigSpec A = cell->getPort(ID(A));
+ SigSpec B = cell->getPort(ID(B));
+ if (GetSize(Y) <= 13) {
+ if (GetSize(A) > 12)
+ continue;
+ if (GetSize(B) > 12)
+ continue;
+ if (cell->type == ID($add))
+ simd12_add.push_back(cell);
+ else if (cell->type == ID($sub))
+ simd12_sub.push_back(cell);
+ }
+ else if (GetSize(Y) <= 25) {
+ if (GetSize(A) > 24)
+ continue;
+ if (GetSize(B) > 24)
+ continue;
+ if (cell->type == ID($add))
+ simd24_add.push_back(cell);
+ else if (cell->type == ID($sub))
+ simd24_sub.push_back(cell);
+ }
+ else
+ log_abort();
+ }
+
+ auto f12 = [module](SigSpec &AB, SigSpec &C, SigSpec &P, SigSpec &CARRYOUT, Cell *lane) {
+ SigSpec A = lane->getPort(ID(A));
+ SigSpec B = lane->getPort(ID(B));
+ SigSpec Y = lane->getPort(ID(Y));
+ A.extend_u0(12, lane->getParam(ID(A_SIGNED)).as_bool());
+ B.extend_u0(12, lane->getParam(ID(B_SIGNED)).as_bool());
+ AB.append(A);
+ C.append(B);
+ if (GetSize(Y) < 13)
+ Y.append(module->addWire(NEW_ID, 13-GetSize(Y)));
+ else
+ log_assert(GetSize(Y) == 13);
+ P.append(Y.extract(0, 12));
+ CARRYOUT.append(Y[12]);
+ };
+ auto g12 = [&f12,module](std::deque<Cell*> &simd12) {
+ while (simd12.size() > 1) {
+ SigSpec AB, C, P, CARRYOUT;
+
+ Cell *lane1 = simd12.front();
+ simd12.pop_front();
+ Cell *lane2 = simd12.front();
+ simd12.pop_front();
+ Cell *lane3 = nullptr;
+ Cell *lane4 = nullptr;
+
+ if (!simd12.empty()) {
+ lane3 = simd12.front();
+ simd12.pop_front();
+ if (!simd12.empty()) {
+ lane4 = simd12.front();
+ simd12.pop_front();
+ }
+ }
+
+ log("Analysing %s.%s for Xilinx DSP SIMD12 packing.\n", log_id(module), log_id(lane1));
+
+ Cell *cell = addDsp(module);
+ cell->setParam(ID(USE_SIMD), Const("FOUR12"));
+ // X = A:B
+ // Y = 0
+ // Z = C
+ cell->setPort(ID(OPMODE), Const::from_string("0110011"));
+
+ log_assert(lane1);
+ log_assert(lane2);
+ f12(AB, C, P, CARRYOUT, lane1);
+ f12(AB, C, P, CARRYOUT, lane2);
+ if (lane3) {
+ f12(AB, C, P, CARRYOUT, lane3);
+ if (lane4)
+ f12(AB, C, P, CARRYOUT, lane4);
+ else {
+ AB.append(Const(0, 12));
+ C.append(Const(0, 12));
+ P.append(module->addWire(NEW_ID, 12));
+ CARRYOUT.append(module->addWire(NEW_ID, 1));
+ }
+ }
+ else {
+ AB.append(Const(0, 24));
+ C.append(Const(0, 24));
+ P.append(module->addWire(NEW_ID, 24));
+ CARRYOUT.append(module->addWire(NEW_ID, 2));
+ }
+ log_assert(GetSize(AB) == 48);
+ log_assert(GetSize(C) == 48);
+ log_assert(GetSize(P) == 48);
+ log_assert(GetSize(CARRYOUT) == 4);
+ cell->setPort(ID(A), AB.extract(18, 30));
+ cell->setPort(ID(B), AB.extract(0, 18));
+ cell->setPort(ID(C), C);
+ cell->setPort(ID(P), P);
+ cell->setPort(ID(CARRYOUT), CARRYOUT);
+ if (lane1->type == ID($sub))
+ cell->setPort(ID(ALUMODE), Const::from_string("0011"));
+
+ module->remove(lane1);
+ module->remove(lane2);
+ if (lane3) module->remove(lane3);
+ if (lane4) module->remove(lane4);
+
+ module->design->select(module, cell);
+ }
+ };
+ g12(simd12_add);
+ g12(simd12_sub);
+
+ auto f24 = [module](SigSpec &AB, SigSpec &C, SigSpec &P, SigSpec &CARRYOUT, Cell *lane) {
+ SigSpec A = lane->getPort(ID(A));
+ SigSpec B = lane->getPort(ID(B));
+ SigSpec Y = lane->getPort(ID(Y));
+ A.extend_u0(24, lane->getParam(ID(A_SIGNED)).as_bool());
+ B.extend_u0(24, lane->getParam(ID(B_SIGNED)).as_bool());
+ C.append(A);
+ AB.append(B);
+ if (GetSize(Y) < 25)
+ Y.append(module->addWire(NEW_ID, 25-GetSize(Y)));
+ else
+ log_assert(GetSize(Y) == 25);
+ P.append(Y.extract(0, 24));
+ CARRYOUT.append(module->addWire(NEW_ID)); // TWO24 uses every other bit
+ CARRYOUT.append(Y[24]);
+ };
+ auto g24 = [&f24,module](std::deque<Cell*> &simd24) {
+ while (simd24.size() > 1) {
+ SigSpec AB;
+ SigSpec C;
+ SigSpec P;
+ SigSpec CARRYOUT;
+
+ Cell *lane1 = simd24.front();
+ simd24.pop_front();
+ Cell *lane2 = simd24.front();
+ simd24.pop_front();
+
+ log("Analysing %s.%s for Xilinx DSP SIMD24 packing.\n", log_id(module), log_id(lane1));
+
+ Cell *cell = addDsp(module);
+ cell->setParam(ID(USE_SIMD), Const("TWO24"));
+ // X = A:B
+ // Y = 0
+ // Z = C
+ cell->setPort(ID(OPMODE), Const::from_string("0110011"));
+
+ log_assert(lane1);
+ log_assert(lane2);
+ f24(AB, C, P, CARRYOUT, lane1);
+ f24(AB, C, P, CARRYOUT, lane2);
+ log_assert(GetSize(AB) == 48);
+ log_assert(GetSize(C) == 48);
+ log_assert(GetSize(P) == 48);
+ log_assert(GetSize(CARRYOUT) == 4);
+ cell->setPort(ID(A), AB.extract(18, 30));
+ cell->setPort(ID(B), AB.extract(0, 18));
+ cell->setPort(ID(C), C);
+ cell->setPort(ID(P), P);
+ cell->setPort(ID(CARRYOUT), CARRYOUT);
+ if (lane1->type == ID($sub))
+ cell->setPort(ID(ALUMODE), Const::from_string("0011"));
+
+ module->remove(lane1);
+ module->remove(lane2);
+
+ module->design->select(module, cell);
+ }
+ };
+ g24(simd24_add);
+ g24(simd24_sub);
+}
+
+void xilinx_dsp_pack(xilinx_dsp_pm &pm)
+{
+ auto &st = pm.st_xilinx_dsp_pack;
+
+ log("Analysing %s.%s for Xilinx DSP packing.\n", log_id(pm.module), log_id(st.dsp));
+
+ log_debug("preAdd: %s\n", log_id(st.preAdd, "--"));
+ log_debug("ffAD: %s %s %s\n", log_id(st.ffAD, "--"), log_id(st.ffADcemux, "--"), log_id(st.ffADrstmux, "--"));
+ log_debug("ffA2: %s %s %s\n", log_id(st.ffA2, "--"), log_id(st.ffA2cemux, "--"), log_id(st.ffA2rstmux, "--"));
+ log_debug("ffA1: %s %s %s\n", log_id(st.ffA1, "--"), log_id(st.ffA1cemux, "--"), log_id(st.ffA1rstmux, "--"));
+ log_debug("ffB2: %s %s %s\n", log_id(st.ffB2, "--"), log_id(st.ffB2cemux, "--"), log_id(st.ffB2rstmux, "--"));
+ log_debug("ffB1: %s %s %s\n", log_id(st.ffB1, "--"), log_id(st.ffB1cemux, "--"), log_id(st.ffB1rstmux, "--"));
+ log_debug("ffD: %s %s %s\n", log_id(st.ffD, "--"), log_id(st.ffDcemux, "--"), log_id(st.ffDrstmux, "--"));
+ log_debug("dsp: %s\n", log_id(st.dsp, "--"));
+ log_debug("ffM: %s %s %s\n", log_id(st.ffM, "--"), log_id(st.ffMcemux, "--"), log_id(st.ffMrstmux, "--"));
+ log_debug("postAdd: %s\n", log_id(st.postAdd, "--"));
+ log_debug("postAddMux: %s\n", log_id(st.postAddMux, "--"));
+ log_debug("ffP: %s %s %s\n", log_id(st.ffP, "--"), log_id(st.ffPcemux, "--"), log_id(st.ffPrstmux, "--"));
+ log_debug("overflow: %s\n", log_id(st.overflow, "--"));
+
+ Cell *cell = st.dsp;
+
+ if (st.preAdd) {
+ log(" preadder %s (%s)\n", log_id(st.preAdd), log_id(st.preAdd->type));
+ bool A_SIGNED = st.preAdd->getParam(ID(A_SIGNED)).as_bool();
+ bool D_SIGNED = st.preAdd->getParam(ID(B_SIGNED)).as_bool();
+ if (st.sigA == st.preAdd->getPort(ID(B)))
+ std::swap(A_SIGNED, D_SIGNED);
+ st.sigA.extend_u0(30, A_SIGNED);
+ st.sigD.extend_u0(25, D_SIGNED);
+ cell->setPort(ID(A), st.sigA);
+ cell->setPort(ID(D), st.sigD);
+ cell->setPort(ID(INMODE), Const::from_string("00100"));
+
+ if (st.ffAD) {
+ if (st.ffADcemux) {
+ SigSpec S = st.ffADcemux->getPort(ID(S));
+ cell->setPort(ID(CEAD), st.ffADcepol ? S : pm.module->Not(NEW_ID, S));
+ }
+ else
+ cell->setPort(ID(CEAD), State::S1);
+ cell->setParam(ID(ADREG), 1);
+ }
+
+ cell->setParam(ID(USE_DPORT), Const("TRUE"));
+
+ pm.autoremove(st.preAdd);
+ }
+ if (st.postAdd) {
+ log(" postadder %s (%s)\n", log_id(st.postAdd), log_id(st.postAdd->type));
+
+ SigSpec &opmode = cell->connections_.at(ID(OPMODE));
+ if (st.postAddMux) {
+ log_assert(st.ffP);
+ opmode[4] = st.postAddMux->getPort(ID(S));
+ pm.autoremove(st.postAddMux);
+ }
+ else if (st.ffP && st.sigC == st.sigP)
+ opmode[4] = State::S0;
+ else
+ opmode[4] = State::S1;
+ opmode[6] = State::S0;
+ opmode[5] = State::S1;
+
+ if (opmode[4] != State::S0) {
+ if (st.postAddMuxAB == ID(A))
+ st.sigC.extend_u0(48, st.postAdd->getParam(ID(B_SIGNED)).as_bool());
+ else
+ st.sigC.extend_u0(48, st.postAdd->getParam(ID(A_SIGNED)).as_bool());
+ cell->setPort(ID(C), st.sigC);
+ }
+
+ pm.autoremove(st.postAdd);
+ }
+ if (st.overflow) {
+ log(" overflow %s (%s)\n", log_id(st.overflow), log_id(st.overflow->type));
+ cell->setParam(ID(USE_PATTERN_DETECT), Const("PATDET"));
+ cell->setParam(ID(SEL_PATTERN), Const("PATTERN"));
+ cell->setParam(ID(SEL_MASK), Const("MASK"));
+
+ if (st.overflow->type == ID($ge)) {
+ Const B = st.overflow->getPort(ID(B)).as_const();
+ log_assert(std::count(B.bits.begin(), B.bits.end(), State::S1) == 1);
+ // Since B is an exact power of 2, subtract 1
+ // by inverting all bits up until hitting
+ // that one hi bit
+ for (auto &b : B.bits)
+ if (b == State::S0) b = State::S1;
+ else if (b == State::S1) {
+ b = State::S0;
+ break;
+ }
+ B.extu(48);
+
+ cell->setParam(ID(MASK), B);
+ cell->setParam(ID(PATTERN), Const(0, 48));
+ cell->setPort(ID(OVERFLOW), st.overflow->getPort(ID(Y)));
+ }
+ else log_abort();
+
+ pm.autoremove(st.overflow);
+ }
+
+ if (st.clock != SigBit())
+ {
+ cell->setPort(ID(CLK), st.clock);
+
+ auto f = [&pm,cell](SigSpec &A, Cell* ff, Cell* cemux, bool cepol, IdString ceport, Cell* rstmux, bool rstpol, IdString rstport) {
+ SigSpec D = ff->getPort(ID(D));
+ SigSpec Q = pm.sigmap(ff->getPort(ID(Q)));
+ if (!A.empty())
+ A.replace(Q, D);
+ if (rstmux) {
+ SigSpec Y = rstmux->getPort(ID(Y));
+ SigSpec AB = rstmux->getPort(rstpol ? ID(A) : ID(B));
+ if (!A.empty())
+ A.replace(Y, AB);
+ if (rstport != IdString()) {
+ SigSpec S = rstmux->getPort(ID(S));
+ cell->setPort(rstport, rstpol ? S : pm.module->Not(NEW_ID, S));
+ }
+ }
+ else if (rstport != IdString())
+ cell->setPort(rstport, State::S0);
+ if (cemux) {
+ SigSpec Y = cemux->getPort(ID(Y));
+ SigSpec BA = cemux->getPort(cepol ? ID(B) : ID(A));
+ SigSpec S = cemux->getPort(ID(S));
+ if (!A.empty())
+ A.replace(Y, BA);
+ cell->setPort(ceport, cepol ? S : pm.module->Not(NEW_ID, S));
+ }
+ else
+ cell->setPort(ceport, State::S1);
+
+ for (auto c : Q.chunks()) {
+ auto it = c.wire->attributes.find(ID(init));
+ if (it == c.wire->attributes.end())
+ continue;
+ for (int i = c.offset; i < c.offset+c.width; i++) {
+ log_assert(it->second[i] == State::S0 || it->second[i] == State::Sx);
+ it->second[i] = State::Sx;
+ }
+ }
+ };
+
+ if (st.ffA2) {
+ SigSpec A = cell->getPort(ID(A));
+ f(A, st.ffA2, st.ffA2cemux, st.ffA2cepol, ID(CEA2), st.ffA2rstmux, st.ffArstpol, ID(RSTA));
+ if (st.ffA1) {
+ f(A, st.ffA1, st.ffA1cemux, st.ffA1cepol, ID(CEA1), st.ffA1rstmux, st.ffArstpol, IdString());
+ cell->setParam(ID(AREG), 2);
+ cell->setParam(ID(ACASCREG), 2);
+ }
+ else {
+ cell->setParam(ID(AREG), 1);
+ cell->setParam(ID(ACASCREG), 1);
+ }
+ pm.add_siguser(A, cell);
+ cell->setPort(ID(A), A);
+ }
+ if (st.ffB2) {
+ SigSpec B = cell->getPort(ID(B));
+ f(B, st.ffB2, st.ffB2cemux, st.ffB2cepol, ID(CEB2), st.ffB2rstmux, st.ffBrstpol, ID(RSTB));
+ if (st.ffB1) {
+ f(B, st.ffB1, st.ffB1cemux, st.ffB1cepol, ID(CEB1), st.ffB1rstmux, st.ffBrstpol, IdString());
+ cell->setParam(ID(BREG), 2);
+ cell->setParam(ID(BCASCREG), 2);
+ }
+ else {
+ cell->setParam(ID(BREG), 1);
+ cell->setParam(ID(BCASCREG), 1);
+ }
+ pm.add_siguser(B, cell);
+ cell->setPort(ID(B), B);
+ }
+ if (st.ffD) {
+ SigSpec D = cell->getPort(ID(D));
+ f(D, st.ffD, st.ffDcemux, st.ffDcepol, ID(CED), st.ffDrstmux, st.ffDrstpol, ID(RSTD));
+ pm.add_siguser(D, cell);
+ cell->setPort(ID(D), D);
+ cell->setParam(ID(DREG), 1);
+ }
+ if (st.ffM) {
+ SigSpec M; // unused
+ f(M, st.ffM, st.ffMcemux, st.ffMcepol, ID(CEM), st.ffMrstmux, st.ffMrstpol, ID(RSTM));
+ st.ffM->connections_.at(ID(Q)).replace(st.sigM, pm.module->addWire(NEW_ID, GetSize(st.sigM)));
+ cell->setParam(ID(MREG), State::S1);
+ }
+ if (st.ffP) {
+ SigSpec P; // unused
+ f(P, st.ffP, st.ffPcemux, st.ffPcepol, ID(CEP), st.ffPrstmux, st.ffPrstpol, ID(RSTP));
+ st.ffP->connections_.at(ID(Q)).replace(st.sigP, pm.module->addWire(NEW_ID, GetSize(st.sigP)));
+ cell->setParam(ID(PREG), State::S1);
+ }
+
+ log(" clock: %s (%s)", log_signal(st.clock), "posedge");
+
+ if (st.ffA2) {
+ log(" ffA2:%s", log_id(st.ffA2));
+ if (st.ffA1)
+ log(" ffA1:%s", log_id(st.ffA1));
+ }
+
+ if (st.ffAD)
+ log(" ffAD:%s", log_id(st.ffAD));
+
+ if (st.ffB2) {
+ log(" ffB2:%s", log_id(st.ffB2));
+ if (st.ffB1)
+ log(" ffB1:%s", log_id(st.ffB1));
+ }
+
+ if (st.ffD)
+ log(" ffD:%s", log_id(st.ffD));
+
+ if (st.ffM)
+ log(" ffM:%s", log_id(st.ffM));
+
+ if (st.ffP)
+ log(" ffP:%s", log_id(st.ffP));
+ }
+ log("\n");
+
+ SigSpec P = st.sigP;
+ if (GetSize(P) < 48)
+ P.append(pm.module->addWire(NEW_ID, 48-GetSize(P)));
+ cell->setPort(ID(P), P);
+
+ pm.blacklist(cell);
+}
+
+void xilinx_dsp48a_pack(xilinx_dsp48a_pm &pm)
+{
+ auto &st = pm.st_xilinx_dsp48a_pack;
+
+ log("Analysing %s.%s for Xilinx DSP48A/DSP48A1 packing.\n", log_id(pm.module), log_id(st.dsp));
+
+ log_debug("preAdd: %s\n", log_id(st.preAdd, "--"));
+ log_debug("ffA1: %s %s %s\n", log_id(st.ffA1, "--"), log_id(st.ffA1cemux, "--"), log_id(st.ffA1rstmux, "--"));
+ log_debug("ffA0: %s %s %s\n", log_id(st.ffA0, "--"), log_id(st.ffA0cemux, "--"), log_id(st.ffA0rstmux, "--"));
+ log_debug("ffB1: %s %s %s\n", log_id(st.ffB1, "--"), log_id(st.ffB1cemux, "--"), log_id(st.ffB1rstmux, "--"));
+ log_debug("ffB0: %s %s %s\n", log_id(st.ffB0, "--"), log_id(st.ffB0cemux, "--"), log_id(st.ffB0rstmux, "--"));
+ log_debug("ffD: %s %s %s\n", log_id(st.ffD, "--"), log_id(st.ffDcemux, "--"), log_id(st.ffDrstmux, "--"));
+ log_debug("dsp: %s\n", log_id(st.dsp, "--"));
+ log_debug("ffM: %s %s %s\n", log_id(st.ffM, "--"), log_id(st.ffMcemux, "--"), log_id(st.ffMrstmux, "--"));
+ log_debug("postAdd: %s\n", log_id(st.postAdd, "--"));
+ log_debug("postAddMux: %s\n", log_id(st.postAddMux, "--"));
+ log_debug("ffP: %s %s %s\n", log_id(st.ffP, "--"), log_id(st.ffPcemux, "--"), log_id(st.ffPrstmux, "--"));
+
+ Cell *cell = st.dsp;
+ SigSpec &opmode = cell->connections_.at(ID(OPMODE));
+
+ if (st.preAdd) {
+ log(" preadder %s (%s)\n", log_id(st.preAdd), log_id(st.preAdd->type));
+ bool D_SIGNED = st.preAdd->getParam(ID(A_SIGNED)).as_bool();
+ bool B_SIGNED = st.preAdd->getParam(ID(B_SIGNED)).as_bool();
+ st.sigB.extend_u0(18, B_SIGNED);
+ st.sigD.extend_u0(18, D_SIGNED);
+ cell->setPort(ID(B), st.sigB);
+ cell->setPort(ID(D), st.sigD);
+ opmode[4] = State::S1;
+ if (st.preAdd->type == ID($add))
+ opmode[6] = State::S0;
+ else if (st.preAdd->type == ID($sub))
+ opmode[6] = State::S1;
+ else
+ log_assert(!"strange pre-adder type");
+
+ pm.autoremove(st.preAdd);
+ }
+ if (st.postAdd) {
+ log(" postadder %s (%s)\n", log_id(st.postAdd), log_id(st.postAdd->type));
+
+ if (st.postAddMux) {
+ log_assert(st.ffP);
+ opmode[2] = st.postAddMux->getPort(ID(S));
+ pm.autoremove(st.postAddMux);
+ }
+ else if (st.ffP && st.sigC == st.sigP)
+ opmode[2] = State::S0;
+ else
+ opmode[2] = State::S1;
+ opmode[3] = State::S1;
+
+ if (opmode[2] != State::S0) {
+ if (st.postAddMuxAB == ID(A))
+ st.sigC.extend_u0(48, st.postAdd->getParam(ID(B_SIGNED)).as_bool());
+ else
+ st.sigC.extend_u0(48, st.postAdd->getParam(ID(A_SIGNED)).as_bool());
+ cell->setPort(ID(C), st.sigC);
+ }
+
+ pm.autoremove(st.postAdd);
+ }
+
+ if (st.clock != SigBit())
+ {
+ cell->setPort(ID(CLK), st.clock);
+
+ auto f = [&pm,cell](SigSpec &A, Cell* ff, Cell* cemux, bool cepol, IdString ceport, Cell* rstmux, bool rstpol, IdString rstport) {
+ SigSpec D = ff->getPort(ID(D));
+ SigSpec Q = pm.sigmap(ff->getPort(ID(Q)));
+ if (!A.empty())
+ A.replace(Q, D);
+ if (rstmux) {
+ SigSpec Y = rstmux->getPort(ID(Y));
+ SigSpec AB = rstmux->getPort(rstpol ? ID(A) : ID(B));
+ if (!A.empty())
+ A.replace(Y, AB);
+ if (rstport != IdString()) {
+ SigSpec S = rstmux->getPort(ID(S));
+ cell->setPort(rstport, rstpol ? S : pm.module->Not(NEW_ID, S));
+ }
+ }
+ else if (rstport != IdString())
+ cell->setPort(rstport, State::S0);
+ if (cemux) {
+ SigSpec Y = cemux->getPort(ID(Y));
+ SigSpec BA = cemux->getPort(cepol ? ID(B) : ID(A));
+ SigSpec S = cemux->getPort(ID(S));
+ if (!A.empty())
+ A.replace(Y, BA);
+ cell->setPort(ceport, cepol ? S : pm.module->Not(NEW_ID, S));
+ }
+ else
+ cell->setPort(ceport, State::S1);
+
+ for (auto c : Q.chunks()) {
+ auto it = c.wire->attributes.find(ID(init));
+ if (it == c.wire->attributes.end())
+ continue;
+ for (int i = c.offset; i < c.offset+c.width; i++) {
+ log_assert(it->second[i] == State::S0 || it->second[i] == State::Sx);
+ it->second[i] = State::Sx;
+ }
+ }
+ };
+
+ if (st.ffA0 || st.ffA1) {
+ SigSpec A = cell->getPort(ID(A));
+ if (st.ffA1) {
+ f(A, st.ffA1, st.ffA1cemux, st.ffAcepol, ID(CEA), st.ffA1rstmux, st.ffArstpol, ID(RSTA));
+ cell->setParam(ID(A1REG), 1);
+ }
+ if (st.ffA0) {
+ f(A, st.ffA0, st.ffA0cemux, st.ffAcepol, ID(CEA), st.ffA0rstmux, st.ffArstpol, ID(RSTA));
+ cell->setParam(ID(A0REG), 1);
+ }
+ pm.add_siguser(A, cell);
+ cell->setPort(ID(A), A);
+ }
+ if (st.ffB0 || st.ffB1) {
+ SigSpec B = cell->getPort(ID(B));
+ if (st.ffB1) {
+ f(B, st.ffB1, st.ffB1cemux, st.ffBcepol, ID(CEB), st.ffB1rstmux, st.ffBrstpol, ID(RSTB));
+ cell->setParam(ID(B1REG), 1);
+ }
+ if (st.ffB0) {
+ f(B, st.ffB0, st.ffB0cemux, st.ffBcepol, ID(CEB), st.ffB0rstmux, st.ffBrstpol, ID(RSTB));
+ cell->setParam(ID(B0REG), 1);
+ }
+ pm.add_siguser(B, cell);
+ cell->setPort(ID(B), B);
+ }
+ if (st.ffD) {
+ SigSpec D = cell->getPort(ID(D));
+ f(D, st.ffD, st.ffDcemux, st.ffDcepol, ID(CED), st.ffDrstmux, st.ffDrstpol, ID(RSTD));
+ pm.add_siguser(D, cell);
+ cell->setPort(ID(D), D);
+ cell->setParam(ID(DREG), 1);
+ }
+ if (st.ffM) {
+ SigSpec M; // unused
+ f(M, st.ffM, st.ffMcemux, st.ffMcepol, ID(CEM), st.ffMrstmux, st.ffMrstpol, ID(RSTM));
+ st.ffM->connections_.at(ID(Q)).replace(st.sigM, pm.module->addWire(NEW_ID, GetSize(st.sigM)));
+ cell->setParam(ID(MREG), State::S1);
+ }
+ if (st.ffP) {
+ SigSpec P; // unused
+ f(P, st.ffP, st.ffPcemux, st.ffPcepol, ID(CEP), st.ffPrstmux, st.ffPrstpol, ID(RSTP));
+ st.ffP->connections_.at(ID(Q)).replace(st.sigP, pm.module->addWire(NEW_ID, GetSize(st.sigP)));
+ cell->setParam(ID(PREG), State::S1);
+ }
+
+ log(" clock: %s (%s)", log_signal(st.clock), "posedge");
+
+ if (st.ffA0)
+ log(" ffA0:%s", log_id(st.ffA0));
+ if (st.ffA1)
+ log(" ffA1:%s", log_id(st.ffA1));
+
+ if (st.ffB0)
+ log(" ffB0:%s", log_id(st.ffB0));
+ if (st.ffB1)
+ log(" ffB1:%s", log_id(st.ffB1));
+
+ if (st.ffD)
+ log(" ffD:%s", log_id(st.ffD));
+
+ if (st.ffM)
+ log(" ffM:%s", log_id(st.ffM));
+
+ if (st.ffP)
+ log(" ffP:%s", log_id(st.ffP));
+ }
+ log("\n");
+
+ SigSpec P = st.sigP;
+ if (GetSize(P) < 48)
+ P.append(pm.module->addWire(NEW_ID, 48-GetSize(P)));
+ cell->setPort(ID(P), P);
+
+ pm.blacklist(cell);
+}
+
+void xilinx_dsp_packC(xilinx_dsp_CREG_pm &pm)
+{
+ auto &st = pm.st_xilinx_dsp_packC;
+
+ log_debug("Analysing %s.%s for Xilinx DSP packing (CREG).\n", log_id(pm.module), log_id(st.dsp));
+ log_debug("ffC: %s %s %s\n", log_id(st.ffC, "--"), log_id(st.ffCcemux, "--"), log_id(st.ffCrstmux, "--"));
+
+ Cell *cell = st.dsp;
+
+ if (st.clock != SigBit())
+ {
+ cell->setPort(ID(CLK), st.clock);
+
+ auto f = [&pm,cell](SigSpec &A, Cell* ff, Cell* cemux, bool cepol, IdString ceport, Cell* rstmux, bool rstpol, IdString rstport) {
+ SigSpec D = ff->getPort(ID(D));
+ SigSpec Q = pm.sigmap(ff->getPort(ID(Q)));
+ if (!A.empty())
+ A.replace(Q, D);
+ if (rstmux) {
+ SigSpec Y = rstmux->getPort(ID(Y));
+ SigSpec AB = rstmux->getPort(rstpol ? ID(A) : ID(B));
+ if (!A.empty())
+ A.replace(Y, AB);
+ if (rstport != IdString()) {
+ SigSpec S = rstmux->getPort(ID(S));
+ cell->setPort(rstport, rstpol ? S : pm.module->Not(NEW_ID, S));
+ }
+ }
+ else if (rstport != IdString())
+ cell->setPort(rstport, State::S0);
+ if (cemux) {
+ SigSpec Y = cemux->getPort(ID(Y));
+ SigSpec BA = cemux->getPort(cepol ? ID(B) : ID(A));
+ SigSpec S = cemux->getPort(ID(S));
+ if (!A.empty())
+ A.replace(Y, BA);
+ cell->setPort(ceport, cepol ? S : pm.module->Not(NEW_ID, S));
+ }
+ else
+ cell->setPort(ceport, State::S1);
+
+ for (auto c : Q.chunks()) {
+ auto it = c.wire->attributes.find(ID(init));
+ if (it == c.wire->attributes.end())
+ continue;
+ for (int i = c.offset; i < c.offset+c.width; i++) {
+ log_assert(it->second[i] == State::S0 || it->second[i] == State::Sx);
+ it->second[i] = State::Sx;
+ }
+ }
+ };
+
+ if (st.ffC) {
+ SigSpec C = cell->getPort(ID(C));
+ f(C, st.ffC, st.ffCcemux, st.ffCcepol, ID(CEC), st.ffCrstmux, st.ffCrstpol, ID(RSTC));
+ pm.add_siguser(C, cell);
+ cell->setPort(ID(C), C);
+ cell->setParam(ID(CREG), 1);
+ }
+
+ log(" clock: %s (%s)", log_signal(st.clock), "posedge");
+
+ if (st.ffC)
+ log(" ffC:%s", log_id(st.ffC));
+ log("\n");
+ }
+
+ pm.blacklist(cell);
+}
+
+struct XilinxDspPass : public Pass {
+ XilinxDspPass() : Pass("xilinx_dsp", "Xilinx: pack resources into DSPs") { }
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" xilinx_dsp [options] [selection]\n");
+ log("\n");
+ log("Pack input registers (A2, A1, B2, B1, C, D, AD; with optional enable/reset),\n");
+ log("pipeline registers (M; with optional enable/reset), output registers (P; with\n");
+ log("optional enable/reset), pre-adder and/or post-adder into Xilinx DSP resources.\n");
+ log("\n");
+ log("Multiply-accumulate operations using the post-adder with feedback on the 'C'\n");
+ log("input will be folded into the DSP. In this scenario only, the 'C' input can be\n");
+ log("used to override the current accumulation result with a new value, which will\n");
+ log("be added to the multiplier result to form the next accumulation result.\n");
+ log("\n");
+ log("Use of the dedicated 'PCOUT' -> 'PCIN' cascade path is detected for 'P' -> 'C'\n");
+ log("connections (optionally, where 'P' is right-shifted by 17-bits and used as an\n");
+ log("input to the post-adder -- a pattern common for summing partial products to\n");
+ log("implement wide multipliers). Limited support also exists for similar cascading\n");
+ log("for A and B using '[AB]COUT' -> '[AB]CIN'. Currently, cascade chains are limited\n");
+ log("to a maximum length of 20 cells, corresponding to the smallest Xilinx 7 Series\n");
+ log("device.\n");
+ log("\n");
+ log("\n");
+ log("Experimental feature: addition/subtractions less than 12 or 24 bits with the\n");
+ log("'(* use_dsp=\"simd\" *)' attribute attached to the output wire or attached to\n");
+ log("the add/subtract operator will cause those operations to be implemented using\n");
+ log("the 'SIMD' feature of DSPs.\n");
+ log("\n");
+ log("Experimental feature: the presence of a `$ge' cell attached to the registered\n");
+ log("P output implementing the operation \"(P >= <power-of-2>)\" will be transformed\n");
+ log("into using the DSP48E1's pattern detector feature for overflow detection.\n");
+ log("\n");
+ log(" -family {xcup|xcu|xc7|xc6v|xc5v|xc4v|xc6s|xc3sda}\n");
+ log(" select the family to target\n");
+ log(" default: xc7\n");
+ log("\n");
+ }
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ log_header(design, "Executing XILINX_DSP pass (pack resources into DSPs).\n");
+
+ std::string family = "xc7";
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ if ((args[argidx] == "-family" || args[argidx] == "-arch") && argidx+1 < args.size()) {
+ family = args[++argidx];
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ // Don't bother distinguishing between those.
+ if (family == "xc6v")
+ family = "xc7";
+ if (family == "xcup")
+ family = "xcu";
+
+ for (auto module : design->selected_modules()) {
+ // Experimental feature: pack $add/$sub cells with
+ // (* use_dsp48="simd" *) into DSP48E1's using its
+ // SIMD feature
+ if (family == "xc7")
+ xilinx_simd_pack(module, module->selected_cells());
+
+ // Match for all features ([ABDMP][12]?REG, pre-adder,
+ // post-adder, pattern detector, etc.) except for CREG
+ if (family == "xc7") {
+ xilinx_dsp_pm pm(module, module->selected_cells());
+ pm.run_xilinx_dsp_pack(xilinx_dsp_pack);
+ } else if (family == "xc6s" || family == "xc3sda") {
+ xilinx_dsp48a_pm pm(module, module->selected_cells());
+ pm.run_xilinx_dsp48a_pack(xilinx_dsp48a_pack);
+ }
+ // Separating out CREG packing is necessary since there
+ // is no guarantee that the cell ordering corresponds
+ // to the "expected" case (i.e. the order in which
+ // they appear in the source) thus the possiblity
+ // existed that a register got packed as a CREG into a
+ // downstream DSP that should have otherwise been a
+ // PREG of an upstream DSP that had not been visited
+ // yet
+ {
+ xilinx_dsp_CREG_pm pm(module, module->selected_cells());
+ pm.run_xilinx_dsp_packC(xilinx_dsp_packC);
+ }
+ // Lastly, identify and utilise PCOUT -> PCIN,
+ // ACOUT -> ACIN, and BCOUT-> BCIN dedicated cascade
+ // chains
+ {
+ xilinx_dsp_cascade_pm pm(module, module->selected_cells());
+ pm.run_xilinx_dsp_cascade();
+ }
+ }
+ }
+} XilinxDspPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/pmgen/xilinx_dsp.pmg b/passes/pmgen/xilinx_dsp.pmg
new file mode 100644
index 000000000..af47ab111
--- /dev/null
+++ b/passes/pmgen/xilinx_dsp.pmg
@@ -0,0 +1,727 @@
+// This file describes the main pattern matcher setup (of three total) that
+// forms the `xilinx_dsp` pass described in xilinx_dsp.cc
+// At a high level, it works as follows:
+// ( 1) Starting from a DSP48E1 cell
+// ( 2) Match the driver of the 'A' input to a possible $dff cell (ADREG)
+// (attached to at most two $mux cells that implement clock-enable or
+// reset functionality, using a subpattern discussed below)
+// If ADREG matched, treat 'A' input as input of ADREG
+// ( 3) Match the driver of the 'A' and 'D' inputs for a possible $add cell
+// (pre-adder)
+// ( 4) If pre-adder was present, find match 'A' input for A2REG
+// If pre-adder was not present, move ADREG to A2REG
+// If A2REG, then match 'A' input for A1REG
+// ( 5) Match 'B' input for B2REG
+// If B2REG, then match 'B' input for B1REG
+// ( 6) Match 'D' input for DREG
+// ( 7) Match 'P' output that exclusively drives an MREG
+// ( 8) Match 'P' output that exclusively drives one of two inputs to an $add
+// cell (post-adder).
+// The other input to the adder is assumed to come in from the 'C' input
+// (note: 'P' -> 'C' connections that exist for accumulators are
+// recognised in xilinx_dsp.cc).
+// ( 9) Match 'P' output that exclusively drives a PREG
+// (10) If post-adder and PREG both present, match for a $mux cell driving
+// the 'C' input, where one of the $mux's inputs is the PREG output.
+// This indicates an accumulator situation, and one where a $mux exists
+// to override the accumulated value:
+// +--------------------------------+
+// | ____ |
+// +--| \ |
+// |$mux|-+ |
+// 'C' ---|____/ | |
+// | /-------\ +----+ |
+// +----+ +-| post- |___|PREG|---+ 'P'
+// |MREG|------ | adder | +----+
+// +----+ \-------/
+// (11) If PREG present, match for a greater-than-or-equal $ge cell attached
+// to the 'P' output where it is compared to a constant that is a
+// power-of-2: e.g. `assign overflow = (PREG >= 2**40);`
+// In this scenario, the pattern detector functionality of a DSP48E1 can
+// to implement this function
+// Notes:
+// - The intention of this pattern matcher is for it to be compatible with
+// DSP48E1 cells inferred from multiply operations by Yosys, as well as for
+// user instantiations that may already contain the cells being packed...
+// (though the latter is currently untested)
+// - Since the $dff-with-optional-clock-enable-or-reset-mux pattern is used
+// for each *REG match, it has been factored out into two subpatterns:
+// in_dffe and out_dffe located at the bottom of this file.
+// - Matching for pattern detector features is currently incomplete. For
+// example, matching for underflow as well as overflow detection is
+// possible, as would auto-reset, enabling saturated arithmetic, detecting
+// custom patterns, etc.
+
+pattern xilinx_dsp_pack
+
+state <SigBit> clock
+state <SigSpec> sigA sigB sigC sigD sigM sigP
+state <IdString> postAddAB postAddMuxAB
+state <bool> ffA1cepol ffA2cepol ffADcepol ffB1cepol ffB2cepol ffDcepol ffMcepol ffPcepol
+state <bool> ffArstpol ffADrstpol ffBrstpol ffDrstpol ffMrstpol ffPrstpol
+state <Cell*> ffAD ffADcemux ffADrstmux ffA1 ffA1cemux ffA1rstmux ffA2 ffA2cemux ffA2rstmux
+state <Cell*> ffB1 ffB1cemux ffB1rstmux ffB2 ffB2cemux ffB2rstmux
+state <Cell*> ffD ffDcemux ffDrstmux ffM ffMcemux ffMrstmux ffP ffPcemux ffPrstmux
+
+// Variables used for subpatterns
+state <SigSpec> argQ argD
+state <bool> ffcepol ffrstpol
+state <int> ffoffset
+udata <SigSpec> dffD dffQ
+udata <SigBit> dffclock
+udata <Cell*> dff dffcemux dffrstmux
+udata <bool> dffcepol dffrstpol
+
+// (1) Starting from a DSP48E1 cell
+match dsp
+ select dsp->type.in(\DSP48E1)
+endmatch
+
+code sigA sigB sigC sigD sigM clock
+ auto unextend = [](const SigSpec &sig) {
+ int i;
+ for (i = GetSize(sig)-1; i > 0; i--)
+ if (sig[i] != sig[i-1])
+ break;
+ // Do not remove non-const sign bit
+ if (sig[i].wire)
+ ++i;
+ return sig.extract(0, i);
+ };
+ sigA = unextend(port(dsp, \A));
+ sigB = unextend(port(dsp, \B));
+
+ sigC = port(dsp, \C, SigSpec());
+ sigD = port(dsp, \D, SigSpec());
+
+ SigSpec P = port(dsp, \P);
+ if (param(dsp, \USE_MULT, Const("MULTIPLY")).decode_string() == "MULTIPLY") {
+ // Only care about those bits that are used
+ int i;
+ for (i = GetSize(P)-1; i >= 0; i--)
+ if (nusers(P[i]) > 1)
+ break;
+ i++;
+ log_assert(nusers(P.extract_end(i)) <= 1);
+ // This sigM could have no users if downstream sinks (e.g. $add) is
+ // narrower than $mul result, for example
+ if (i == 0)
+ reject;
+ sigM = P.extract(0, i);
+ }
+ else
+ sigM = P;
+
+ clock = port(dsp, \CLK, SigBit());
+endcode
+
+// (2) Match the driver of the 'A' input to a possible $dff cell (ADREG)
+// (attached to at most two $mux cells that implement clock-enable or
+// reset functionality, using a subpattern discussed above)
+// If matched, treat 'A' input as input of ADREG
+code argQ ffAD ffADcemux ffADrstmux ffADcepol ffADrstpol sigA clock
+ if (param(dsp, \ADREG, 1).as_int() == 0) {
+ argQ = sigA;
+ subpattern(in_dffe);
+ if (dff) {
+ ffAD = dff;
+ clock = dffclock;
+ if (dffrstmux) {
+ ffADrstmux = dffrstmux;
+ ffADrstpol = dffrstpol;
+ }
+ if (dffcemux) {
+ ffADcemux = dffcemux;
+ ffADcepol = dffcepol;
+ }
+ sigA = dffD;
+ }
+ }
+endcode
+
+// (3) Match the driver of the 'A' and 'D' inputs for a possible $add cell
+// (pre-adder)
+match preAdd
+ if sigD.empty() || sigD.is_fully_zero()
+ // Ensure that preAdder not already used
+ if param(dsp, \USE_DPORT, Const("FALSE")).decode_string() == "FALSE"
+ if port(dsp, \INMODE, Const(0, 5)).is_fully_zero()
+
+ select preAdd->type.in($add)
+ // Output has to be 25 bits or less
+ select GetSize(port(preAdd, \Y)) <= 25
+ select nusers(port(preAdd, \Y)) == 2
+ choice <IdString> AB {\A, \B}
+ // A port has to be 30 bits or less
+ select GetSize(port(preAdd, AB)) <= 30
+ define <IdString> BA (AB == \A ? \B : \A)
+ // D port has to be 25 bits or less
+ select GetSize(port(preAdd, BA)) <= 25
+ index <SigSpec> port(preAdd, \Y) === sigA
+
+ optional
+endmatch
+
+code sigA sigD
+ if (preAdd) {
+ sigA = port(preAdd, \A);
+ sigD = port(preAdd, \B);
+ }
+endcode
+
+// (4) If pre-adder was present, find match 'A' input for A2REG
+// If pre-adder was not present, move ADREG to A2REG
+// Then match 'A' input for A1REG
+code argQ ffAD ffADcemux ffADrstmux ffADcepol ffADrstpol sigA clock ffA2 ffA2cemux ffA2rstmux ffA2cepol ffArstpol ffA1 ffA1cemux ffA1rstmux ffA1cepol
+ // Only search for ffA2 if there was a pre-adder
+ // (otherwise ffA2 would have been matched as ffAD)
+ if (preAdd) {
+ if (param(dsp, \AREG, 1).as_int() == 0) {
+ argQ = sigA;
+ subpattern(in_dffe);
+ if (dff) {
+ ffA2 = dff;
+ clock = dffclock;
+ if (dffrstmux) {
+ ffA2rstmux = dffrstmux;
+ ffArstpol = dffrstpol;
+ }
+ if (dffcemux) {
+ ffA2cepol = dffcepol;
+ ffA2cemux = dffcemux;
+ }
+ sigA = dffD;
+ }
+ }
+ }
+ // And if there wasn't a pre-adder,
+ // move AD register to A
+ else if (ffAD) {
+ log_assert(!ffA2 && !ffA2cemux && !ffA2rstmux);
+ std::swap(ffA2, ffAD);
+ std::swap(ffA2cemux, ffADcemux);
+ std::swap(ffA2rstmux, ffADrstmux);
+ ffA2cepol = ffADcepol;
+ ffArstpol = ffADrstpol;
+ }
+
+ // Now attempt to match A1
+ if (ffA2) {
+ argQ = sigA;
+ subpattern(in_dffe);
+ if (dff) {
+ if ((ffA2rstmux != nullptr) ^ (dffrstmux != nullptr))
+ goto ffA1_end;
+ if (dffrstmux) {
+ if (ffArstpol != dffrstpol)
+ goto ffA1_end;
+ if (port(ffA2rstmux, \S) != port(dffrstmux, \S))
+ goto ffA1_end;
+ ffA1rstmux = dffrstmux;
+ }
+
+ ffA1 = dff;
+ clock = dffclock;
+
+ if (dffcemux) {
+ ffA1cemux = dffcemux;
+ ffA1cepol = dffcepol;
+ }
+ sigA = dffD;
+
+ffA1_end: ;
+ }
+ }
+endcode
+
+// (5) Match 'B' input for B2REG
+// If B2REG, then match 'B' input for B1REG
+code argQ ffB2 ffB2cemux ffB2rstmux ffB2cepol ffBrstpol sigB clock ffB1 ffB1cemux ffB1rstmux ffB1cepol
+ if (param(dsp, \BREG, 1).as_int() == 0) {
+ argQ = sigB;
+ subpattern(in_dffe);
+ if (dff) {
+ ffB2 = dff;
+ clock = dffclock;
+ if (dffrstmux) {
+ ffB2rstmux = dffrstmux;
+ ffBrstpol = dffrstpol;
+ }
+ if (dffcemux) {
+ ffB2cemux = dffcemux;
+ ffB2cepol = dffcepol;
+ }
+ sigB = dffD;
+
+ // Now attempt to match B1
+ if (ffB2) {
+ argQ = sigB;
+ subpattern(in_dffe);
+ if (dff) {
+ if ((ffB2rstmux != nullptr) ^ (dffrstmux != nullptr))
+ goto ffB1_end;
+ if (dffrstmux) {
+ if (ffBrstpol != dffrstpol)
+ goto ffB1_end;
+ if (port(ffB2rstmux, \S) != port(dffrstmux, \S))
+ goto ffB1_end;
+ ffB1rstmux = dffrstmux;
+ }
+
+ ffB1 = dff;
+ clock = dffclock;
+
+ if (dffcemux) {
+ ffB1cemux = dffcemux;
+ ffB1cepol = dffcepol;
+ }
+ sigB = dffD;
+
+ffB1_end: ;
+ }
+ }
+
+ }
+ }
+endcode
+
+// (6) Match 'D' input for DREG
+code argQ ffD ffDcemux ffDrstmux ffDcepol ffDrstpol sigD clock
+ if (param(dsp, \DREG, 1).as_int() == 0) {
+ argQ = sigD;
+ subpattern(in_dffe);
+ if (dff) {
+ ffD = dff;
+ clock = dffclock;
+ if (dffrstmux) {
+ ffDrstmux = dffrstmux;
+ ffDrstpol = dffrstpol;
+ }
+ if (dffcemux) {
+ ffDcemux = dffcemux;
+ ffDcepol = dffcepol;
+ }
+ sigD = dffD;
+ }
+ }
+endcode
+
+// (7) Match 'P' output that exclusively drives an MREG
+code argD ffM ffMcemux ffMrstmux ffMcepol ffMrstpol sigM sigP clock
+ if (param(dsp, \MREG, 1).as_int() == 0 && nusers(sigM) == 2) {
+ argD = sigM;
+ subpattern(out_dffe);
+ if (dff) {
+ ffM = dff;
+ clock = dffclock;
+ if (dffrstmux) {
+ ffMrstmux = dffrstmux;
+ ffMrstpol = dffrstpol;
+ }
+ if (dffcemux) {
+ ffMcemux = dffcemux;
+ ffMcepol = dffcepol;
+ }
+ sigM = dffQ;
+ }
+ }
+ sigP = sigM;
+endcode
+
+// (8) Match 'P' output that exclusively drives one of two inputs to an $add
+// cell (post-adder).
+// The other input to the adder is assumed to come in from the 'C' input
+// (note: 'P' -> 'C' connections that exist for accumulators are
+// recognised in xilinx_dsp.cc).
+match postAdd
+ // Ensure that Z mux is not already used
+ if port(dsp, \OPMODE, SigSpec(0, 7)).extract(4,3).is_fully_zero()
+
+ select postAdd->type.in($add)
+ select GetSize(port(postAdd, \Y)) <= 48
+ choice <IdString> AB {\A, \B}
+ select nusers(port(postAdd, AB)) <= 3
+ filter ffMcemux || nusers(port(postAdd, AB)) == 2
+ filter !ffMcemux || nusers(port(postAdd, AB)) == 3
+
+ index <SigBit> port(postAdd, AB)[0] === sigP[0]
+ filter GetSize(port(postAdd, AB)) >= GetSize(sigP)
+ filter port(postAdd, AB).extract(0, GetSize(sigP)) == sigP
+ // Check that remainder of AB is a sign- or zero-extension
+ filter port(postAdd, AB).extract_end(GetSize(sigP)) == SigSpec(sigP[GetSize(sigP)-1], GetSize(port(postAdd, AB))-GetSize(sigP)) || port(postAdd, AB).extract_end(GetSize(sigP)) == SigSpec(State::S0, GetSize(port(postAdd, AB))-GetSize(sigP))
+
+ set postAddAB AB
+ optional
+endmatch
+
+code sigC sigP
+ if (postAdd) {
+ sigC = port(postAdd, postAddAB == \A ? \B : \A);
+ sigP = port(postAdd, \Y);
+ }
+endcode
+
+// (9) Match 'P' output that exclusively drives a PREG
+code argD ffP ffPcemux ffPrstmux ffPcepol ffPrstpol sigP clock
+ if (param(dsp, \PREG, 1).as_int() == 0) {
+ int users = 2;
+ // If ffMcemux and no postAdd new-value net must have three users: ffMcemux, ffM and ffPcemux
+ if (ffMcemux && !postAdd) users++;
+ if (nusers(sigP) == users) {
+ argD = sigP;
+ subpattern(out_dffe);
+ if (dff) {
+ ffP = dff;
+ clock = dffclock;
+ if (dffrstmux) {
+ ffPrstmux = dffrstmux;
+ ffPrstpol = dffrstpol;
+ }
+ if (dffcemux) {
+ ffPcemux = dffcemux;
+ ffPcepol = dffcepol;
+ }
+ sigP = dffQ;
+ }
+ }
+ }
+endcode
+
+// (10) If post-adder and PREG both present, match for a $mux cell driving
+// the 'C' input, where one of the $mux's inputs is the PREG output.
+// This indicates an accumulator situation, and one where a $mux exists
+// to override the accumulated value:
+// +--------------------------------+
+// | ____ |
+// +--| \ |
+// |$mux|-+ |
+// 'C' ---|____/ | |
+// | /-------\ +----+ |
+// +----+ +-| post- |___|PREG|---+ 'P'
+// |MREG|------ | adder | +----+
+// +----+ \-------/
+match postAddMux
+ if postAdd
+ if ffP
+ select postAddMux->type.in($mux)
+ select nusers(port(postAddMux, \Y)) == 2
+ choice <IdString> AB {\A, \B}
+ index <SigSpec> port(postAddMux, AB) === sigP
+ index <SigSpec> port(postAddMux, \Y) === sigC
+ set postAddMuxAB AB
+ optional
+endmatch
+
+code sigC
+ if (postAddMux)
+ sigC = port(postAddMux, postAddMuxAB == \A ? \B : \A);
+endcode
+
+// (11) If PREG present, match for a greater-than-or-equal $ge cell attached to
+// the 'P' output where it is compared to a constant that is a power-of-2:
+// e.g. `assign overflow = (PREG >= 2**40);`
+// In this scenario, the pattern detector functionality of a DSP48E1 can
+// to implement this function
+match overflow
+ if ffP
+ if param(dsp, \USE_PATTERN_DETECT, Const("NO_PATDET")).decode_string() == "NO_PATDET"
+ select overflow->type.in($ge)
+ select GetSize(port(overflow, \Y)) <= 48
+ select port(overflow, \B).is_fully_const()
+ define <Const> B port(overflow, \B).as_const()
+ select std::count(B.bits.begin(), B.bits.end(), State::S1) == 1
+ index <SigSpec> port(overflow, \A) === sigP
+ optional
+endmatch
+
+code
+ accept;
+endcode
+
+// #######################
+
+// Subpattern for matching against input registers, based on knowledge of the
+// 'Q' input. Typically, identifying registers with clock-enable and reset
+// capability would be a task would be handled by other Yosys passes such as
+// dff2dffe, but since DSP inference happens much before this, these patterns
+// have to be manually identified.
+// At a high level:
+// (1) Starting from a $dff cell that (partially or fully) drives the given
+// 'Q' argument
+// (2) Match for a $mux cell implementing synchronous reset semantics ---
+// one that exclusively drives the 'D' input of the $dff, with one of its
+// $mux inputs being fully zero
+// (3) Match for a $mux cell implement clock enable semantics --- one that
+// exclusively drives the 'D' input of the $dff (or the other input of
+// the reset $mux) and where one of this $mux's inputs is connected to
+// the 'Q' output of the $dff
+subpattern in_dffe
+arg argD argQ clock
+
+code
+ dff = nullptr;
+ if (argQ.empty())
+ reject;
+ for (const auto &c : argQ.chunks()) {
+ // Abandon matches when 'Q' is a constant
+ if (!c.wire)
+ reject;
+ // Abandon matches when 'Q' has the keep attribute set
+ if (c.wire->get_bool_attribute(\keep))
+ reject;
+ // Abandon matches when 'Q' has a non-zero init attribute set
+ // (not supported by DSP48E1)
+ Const init = c.wire->attributes.at(\init, Const());
+ if (!init.empty())
+ for (auto b : init.extract(c.offset, c.width))
+ if (b != State::Sx && b != State::S0)
+ reject;
+ }
+endcode
+
+// (1) Starting from a $dff cell that (partially or fully) drives the given
+// 'Q' argument
+match ff
+ select ff->type.in($dff)
+ // DSP48E1 does not support clock inversion
+ select param(ff, \CLK_POLARITY).as_bool()
+
+ slice offset GetSize(port(ff, \D))
+ index <SigBit> port(ff, \Q)[offset] === argQ[0]
+
+ // Check that the rest of argQ is present
+ filter GetSize(port(ff, \Q)) >= offset + GetSize(argQ)
+ filter port(ff, \Q).extract(offset, GetSize(argQ)) == argQ
+
+ filter clock == SigBit() || port(ff, \CLK) == clock
+
+ set ffoffset offset
+endmatch
+
+code argQ argD
+ SigSpec Q = port(ff, \Q);
+ dff = ff;
+ dffclock = port(ff, \CLK);
+ dffD = argQ;
+ argD = port(ff, \D);
+ argQ = Q;
+ dffD.replace(argQ, argD);
+ // Only search for ffrstmux if dffD only
+ // has two (ff, ffrstmux) users
+ if (nusers(dffD) > 2)
+ argD = SigSpec();
+endcode
+
+// (2) Match for a $mux cell implementing synchronous reset semantics ---
+// exclusively drives the 'D' input of the $dff, with one of the $mux
+// inputs being fully zero
+match ffrstmux
+ if !argD.empty()
+ select ffrstmux->type.in($mux)
+ index <SigSpec> port(ffrstmux, \Y) === argD
+
+ choice <IdString> BA {\B, \A}
+ // DSP48E1 only supports reset to zero
+ select port(ffrstmux, BA).is_fully_zero()
+
+ define <bool> pol (BA == \B)
+ set ffrstpol pol
+ semioptional
+endmatch
+
+code argD
+ if (ffrstmux) {
+ dffrstmux = ffrstmux;
+ dffrstpol = ffrstpol;
+ argD = port(ffrstmux, ffrstpol ? \A : \B);
+ dffD.replace(port(ffrstmux, \Y), argD);
+
+ // Only search for ffcemux if argQ has at
+ // least 3 users (ff, <upstream>, ffrstmux) and
+ // dffD only has two (ff, ffrstmux)
+ if (!(nusers(argQ) >= 3 && nusers(dffD) == 2))
+ argD = SigSpec();
+ }
+ else
+ dffrstmux = nullptr;
+endcode
+
+// (3) Match for a $mux cell implement clock enable semantics --- one that
+// exclusively drives the 'D' input of the $dff (or the other input of
+// the reset $mux) and where one of this $mux's inputs is connected to
+// the 'Q' output of the $dff
+match ffcemux
+ if !argD.empty()
+ select ffcemux->type.in($mux)
+ index <SigSpec> port(ffcemux, \Y) === argD
+ choice <IdString> AB {\A, \B}
+ index <SigSpec> port(ffcemux, AB) === argQ
+ define <bool> pol (AB == \A)
+ set ffcepol pol
+ semioptional
+endmatch
+
+code argD
+ if (ffcemux) {
+ dffcemux = ffcemux;
+ dffcepol = ffcepol;
+ argD = port(ffcemux, ffcepol ? \B : \A);
+ dffD.replace(port(ffcemux, \Y), argD);
+ }
+ else
+ dffcemux = nullptr;
+endcode
+
+// #######################
+
+// Subpattern for matching against output registers, based on knowledge of the
+// 'D' input.
+// At a high level:
+// (1) Starting from an optional $mux cell that implements clock enable
+// semantics --- one where the given 'D' argument (partially or fully)
+// drives one of its two inputs
+// (2) Starting from, or continuing onto, another optional $mux cell that
+// implements synchronous reset semantics --- one where the given 'D'
+// argument (or the clock enable $mux output) drives one of its two inputs
+// and where the other input is fully zero
+// (3) Match for a $dff cell (whose 'D' input is the 'D' argument, or the
+// output of the previous clock enable or reset $mux cells)
+subpattern out_dffe
+arg argD argQ clock
+
+code
+ dff = nullptr;
+ for (auto c : argD.chunks())
+ // Abandon matches when 'D' has the keep attribute set
+ if (c.wire->get_bool_attribute(\keep))
+ reject;
+endcode
+
+// (1) Starting from an optional $mux cell that implements clock enable
+// semantics --- one where the given 'D' argument (partially or fully)
+// drives one of its two inputs
+match ffcemux
+ select ffcemux->type.in($mux)
+ // ffcemux output must have two users: ffcemux and ff.D
+ select nusers(port(ffcemux, \Y)) == 2
+
+ choice <IdString> AB {\A, \B}
+ // keep-last-value net must have at least three users: ffcemux, ff, downstream sink(s)
+ select nusers(port(ffcemux, AB)) >= 3
+
+ slice offset GetSize(port(ffcemux, \Y))
+ define <IdString> BA (AB == \A ? \B : \A)
+ index <SigBit> port(ffcemux, BA)[offset] === argD[0]
+
+ // Check that the rest of argD is present
+ filter GetSize(port(ffcemux, BA)) >= offset + GetSize(argD)
+ filter port(ffcemux, BA).extract(offset, GetSize(argD)) == argD
+
+ set ffoffset offset
+ define <bool> pol (AB == \A)
+ set ffcepol pol
+
+ semioptional
+endmatch
+
+code argD argQ
+ dffcemux = ffcemux;
+ if (ffcemux) {
+ SigSpec BA = port(ffcemux, ffcepol ? \B : \A);
+ SigSpec Y = port(ffcemux, \Y);
+ argQ = argD;
+ argD.replace(BA, Y);
+ argQ.replace(BA, port(ffcemux, ffcepol ? \A : \B));
+
+ dffcemux = ffcemux;
+ dffcepol = ffcepol;
+ }
+endcode
+
+// (2) Starting from, or continuing onto, another optional $mux cell that
+// implements synchronous reset semantics --- one where the given 'D'
+// argument (or the clock enable $mux output) drives one of its two inputs
+// and where the other input is fully zero
+match ffrstmux
+ select ffrstmux->type.in($mux)
+ // ffrstmux output must have two users: ffrstmux and ff.D
+ select nusers(port(ffrstmux, \Y)) == 2
+
+ choice <IdString> BA {\B, \A}
+ // DSP48E1 only supports reset to zero
+ select port(ffrstmux, BA).is_fully_zero()
+
+ slice offset GetSize(port(ffrstmux, \Y))
+ define <IdString> AB (BA == \B ? \A : \B)
+ index <SigBit> port(ffrstmux, AB)[offset] === argD[0]
+
+ // Check that offset is consistent
+ filter !ffcemux || ffoffset == offset
+ // Check that the rest of argD is present
+ filter GetSize(port(ffrstmux, AB)) >= offset + GetSize(argD)
+ filter port(ffrstmux, AB).extract(offset, GetSize(argD)) == argD
+
+ set ffoffset offset
+ define <bool> pol (AB == \A)
+ set ffrstpol pol
+
+ semioptional
+endmatch
+
+code argD argQ
+ dffrstmux = ffrstmux;
+ if (ffrstmux) {
+ SigSpec AB = port(ffrstmux, ffrstpol ? \A : \B);
+ SigSpec Y = port(ffrstmux, \Y);
+ argD.replace(AB, Y);
+
+ dffrstmux = ffrstmux;
+ dffrstpol = ffrstpol;
+ }
+endcode
+
+// (3) Match for a $dff cell (whose 'D' input is the 'D' argument, or the
+// output of the previous clock enable or reset $mux cells)
+match ff
+ select ff->type.in($dff)
+ // DSP48E1 does not support clock inversion
+ select param(ff, \CLK_POLARITY).as_bool()
+
+ slice offset GetSize(port(ff, \D))
+ index <SigBit> port(ff, \D)[offset] === argD[0]
+
+ // Check that offset is consistent
+ filter (!ffcemux && !ffrstmux) || ffoffset == offset
+ // Check that the rest of argD is present
+ filter GetSize(port(ff, \D)) >= offset + GetSize(argD)
+ filter port(ff, \D).extract(offset, GetSize(argD)) == argD
+ // Check that FF.Q is connected to CE-mux
+ filter !ffcemux || port(ff, \Q).extract(offset, GetSize(argQ)) == argQ
+
+ filter clock == SigBit() || port(ff, \CLK) == clock
+
+ set ffoffset offset
+endmatch
+
+code argQ
+ SigSpec D = port(ff, \D);
+ SigSpec Q = port(ff, \Q);
+ if (!ffcemux) {
+ argQ = argD;
+ argQ.replace(D, Q);
+ }
+
+ // Abandon matches when 'Q' has a non-zero init attribute set
+ // (not supported by DSP48E1)
+ for (auto c : argQ.chunks()) {
+ Const init = c.wire->attributes.at(\init, Const());
+ if (!init.empty())
+ for (auto b : init.extract(c.offset, c.width))
+ if (b != State::Sx && b != State::S0)
+ reject;
+ }
+
+ dff = ff;
+ dffQ = argQ;
+ dffclock = port(ff, \CLK);
+endcode
diff --git a/passes/pmgen/xilinx_dsp48a.pmg b/passes/pmgen/xilinx_dsp48a.pmg
new file mode 100644
index 000000000..16f5e598d
--- /dev/null
+++ b/passes/pmgen/xilinx_dsp48a.pmg
@@ -0,0 +1,673 @@
+// This file describes the main pattern matcher setup (of three total) that
+// forms the `xilinx_dsp` pass described in xilinx_dsp.cc - version for
+// DSP48A/DSP48A1 (Spartan 3A DSP, Spartan 6).
+// At a high level, it works as follows:
+// ( 1) Starting from a DSP48A/DSP48A1 cell
+// ( 2) Match the driver of the 'B' input to a possible $dff cell (B1REG)
+// (attached to at most two $mux cells that implement clock-enable or
+// reset functionality, using a subpattern discussed below)
+// If B1REG matched, treat 'B' input as input of B1REG
+// ( 3) Match the driver of the 'B' and 'D' inputs for a possible $add cell
+// (pre-adder)
+// ( 4) Match 'B' input for B0REG
+// ( 5) Match 'A' input for A1REG
+// If A1REG, then match 'A' input for A0REG
+// ( 6) Match 'D' input for DREG
+// ( 7) Match 'P' output that exclusively drives an MREG
+// ( 8) Match 'P' output that exclusively drives one of two inputs to an $add
+// cell (post-adder).
+// The other input to the adder is assumed to come in from the 'C' input
+// (note: 'P' -> 'C' connections that exist for accumulators are
+// recognised in xilinx_dsp.cc).
+// ( 9) Match 'P' output that exclusively drives a PREG
+// (10) If post-adder and PREG both present, match for a $mux cell driving
+// the 'C' input, where one of the $mux's inputs is the PREG output.
+// This indicates an accumulator situation, and one where a $mux exists
+// to override the accumulated value:
+// +--------------------------------+
+// | ____ |
+// +--| \ |
+// |$mux|-+ |
+// 'C' ---|____/ | |
+// | /-------\ +----+ |
+// +----+ +-| post- |___|PREG|---+ 'P'
+// |MREG|------ | adder | +----+
+// +----+ \-------/
+// Notes: see the notes in xilinx_dsp.pmg
+
+pattern xilinx_dsp48a_pack
+
+state <SigBit> clock
+state <SigSpec> sigA sigB sigC sigD sigM sigP
+state <IdString> postAddAB postAddMuxAB
+state <bool> ffAcepol ffBcepol ffDcepol ffMcepol ffPcepol
+state <bool> ffArstpol ffBrstpol ffDrstpol ffMrstpol ffPrstpol
+state <Cell*> ffA0 ffA0cemux ffA0rstmux ffA1 ffA1cemux ffA1rstmux
+state <Cell*> ffB0 ffB0cemux ffB0rstmux ffB1 ffB1cemux ffB1rstmux
+state <Cell*> ffD ffDcemux ffDrstmux ffM ffMcemux ffMrstmux ffP ffPcemux ffPrstmux
+
+// Variables used for subpatterns
+state <SigSpec> argQ argD
+state <bool> ffcepol ffrstpol
+state <int> ffoffset
+udata <SigSpec> dffD dffQ
+udata <SigBit> dffclock
+udata <Cell*> dff dffcemux dffrstmux
+udata <bool> dffcepol dffrstpol
+
+// (1) Starting from a DSP48A/DSP48A1 cell
+match dsp
+ select dsp->type.in(\DSP48A, \DSP48A1)
+endmatch
+
+code sigA sigB sigC sigD sigM clock
+ auto unextend = [](const SigSpec &sig) {
+ int i;
+ for (i = GetSize(sig)-1; i > 0; i--)
+ if (sig[i] != sig[i-1])
+ break;
+ // Do not remove non-const sign bit
+ if (sig[i].wire)
+ ++i;
+ return sig.extract(0, i);
+ };
+ sigA = unextend(port(dsp, \A));
+ sigB = unextend(port(dsp, \B));
+
+ sigC = port(dsp, \C, SigSpec());
+ sigD = port(dsp, \D, SigSpec());
+
+ SigSpec P = port(dsp, \P);
+ // Only care about those bits that are used
+ int i;
+ for (i = GetSize(P)-1; i >= 0; i--)
+ if (nusers(P[i]) > 1)
+ break;
+ i++;
+ log_assert(nusers(P.extract_end(i)) <= 1);
+ // This sigM could have no users if downstream sinks (e.g. $add) is
+ // narrower than $mul result, for example
+ if (i == 0)
+ reject;
+ sigM = P.extract(0, i);
+
+ clock = port(dsp, \CLK, SigBit());
+endcode
+
+// (2) Match the driver of the 'B' input to a possible $dff cell (B1REG)
+// (attached to at most two $mux cells that implement clock-enable or
+// reset functionality, using a subpattern discussed above)
+// If matched, treat 'B' input as input of B1REG
+code argQ ffB1 ffB1cemux ffB1rstmux ffBcepol ffBrstpol sigB clock
+ if (param(dsp, \B1REG).as_int() == 0 && param(dsp, \B0REG).as_int() == 0 && port(dsp, \OPMODE, SigSpec()).extract(4, 1).is_fully_zero()) {
+ argQ = sigB;
+ subpattern(in_dffe);
+ if (dff) {
+ ffB1 = dff;
+ clock = dffclock;
+ if (dffrstmux) {
+ ffB1rstmux = dffrstmux;
+ ffBrstpol = dffrstpol;
+ }
+ if (dffcemux) {
+ ffB1cemux = dffcemux;
+ ffBcepol = dffcepol;
+ }
+ sigB = dffD;
+ }
+ }
+endcode
+
+// (3) Match the driver of the 'B' and 'D' inputs for a possible $add cell
+// (pre-adder)
+match preAdd
+ if sigD.empty() || sigD.is_fully_zero()
+ if param(dsp, \B0REG).as_int() == 0
+ // Ensure that preAdder not already used
+ if port(dsp, \OPMODE, SigSpec()).extract(4, 1).is_fully_zero()
+
+ select preAdd->type.in($add, $sub)
+ // Output has to be 18 bits or less
+ select GetSize(port(preAdd, \Y)) <= 18
+ select nusers(port(preAdd, \Y)) == 2
+ // D port has to be 18 bits or less
+ select GetSize(port(preAdd, \A)) <= 18
+ // B port has to be 18 bits or less
+ select GetSize(port(preAdd, \B)) <= 18
+ index <SigSpec> port(preAdd, \Y) === sigB
+
+ optional
+endmatch
+
+code sigB sigD
+ if (preAdd) {
+ sigD = port(preAdd, \A);
+ sigB = port(preAdd, \B);
+ }
+endcode
+
+// (4) Match 'B' input for B0REG
+code argQ ffB0 ffB0cemux ffB0rstmux ffBcepol ffBrstpol sigB clock
+ if (param(dsp, \B0REG).as_int() == 0) {
+ argQ = sigB;
+ subpattern(in_dffe);
+ if (dff) {
+ if (ffB1) {
+ if ((ffB1rstmux != nullptr) ^ (dffrstmux != nullptr))
+ goto ffB0_end;
+ if ((ffB1cemux != nullptr) ^ (dffcemux != nullptr))
+ goto ffB0_end;
+ if (dffrstmux) {
+ if (ffBrstpol != dffrstpol)
+ goto ffB0_end;
+ if (port(ffB1rstmux, \S) != port(dffrstmux, \S))
+ goto ffB0_end;
+ ffB0rstmux = dffrstmux;
+ }
+ if (dffcemux) {
+ if (ffBcepol != dffcepol)
+ goto ffB0_end;
+ if (port(ffB1cemux, \S) != port(dffcemux, \S))
+ goto ffB0_end;
+ ffB0cemux = dffcemux;
+ }
+ }
+ ffB0 = dff;
+ clock = dffclock;
+ if (dffrstmux) {
+ ffB0rstmux = dffrstmux;
+ ffBrstpol = dffrstpol;
+ }
+ if (dffcemux) {
+ ffB0cemux = dffcemux;
+ ffBcepol = dffcepol;
+ }
+ sigB = dffD;
+ }
+ }
+ffB0_end:
+endcode
+
+// (5) Match 'A' input for A1REG
+// If A1REG, then match 'A' input for A0REG
+code argQ ffA1 ffA1cemux ffA1rstmux ffAcepol ffArstpol sigA clock ffA0 ffA0cemux ffA0rstmux
+ if (param(dsp, \A0REG).as_int() == 0 && param(dsp, \A1REG).as_int() == 0) {
+ argQ = sigA;
+ subpattern(in_dffe);
+ if (dff) {
+ ffA1 = dff;
+ clock = dffclock;
+ if (dffrstmux) {
+ ffA1rstmux = dffrstmux;
+ ffArstpol = dffrstpol;
+ }
+ if (dffcemux) {
+ ffA1cemux = dffcemux;
+ ffAcepol = dffcepol;
+ }
+ sigA = dffD;
+
+ // Now attempt to match A0
+ if (ffA1) {
+ argQ = sigA;
+ subpattern(in_dffe);
+ if (dff) {
+ if ((ffA1rstmux != nullptr) ^ (dffrstmux != nullptr))
+ goto ffA0_end;
+ if ((ffA1cemux != nullptr) ^ (dffcemux != nullptr))
+ goto ffA0_end;
+ if (dffrstmux) {
+ if (ffArstpol != dffrstpol)
+ goto ffA0_end;
+ if (port(ffA1rstmux, \S) != port(dffrstmux, \S))
+ goto ffA0_end;
+ ffA0rstmux = dffrstmux;
+ }
+ if (dffcemux) {
+ if (ffAcepol != dffcepol)
+ goto ffA0_end;
+ if (port(ffA1cemux, \S) != port(dffcemux, \S))
+ goto ffA0_end;
+ ffA0cemux = dffcemux;
+ }
+
+ ffA0 = dff;
+ clock = dffclock;
+
+ if (dffcemux) {
+ ffA0cemux = dffcemux;
+ ffAcepol = dffcepol;
+ }
+ sigA = dffD;
+
+ffA0_end: ;
+ }
+ }
+
+ }
+ }
+endcode
+
+// (6) Match 'D' input for DREG
+code argQ ffD ffDcemux ffDrstmux ffDcepol ffDrstpol sigD clock
+ if (param(dsp, \DREG).as_int() == 0) {
+ argQ = sigD;
+ subpattern(in_dffe);
+ if (dff) {
+ ffD = dff;
+ clock = dffclock;
+ if (dffrstmux) {
+ ffDrstmux = dffrstmux;
+ ffDrstpol = dffrstpol;
+ }
+ if (dffcemux) {
+ ffDcemux = dffcemux;
+ ffDcepol = dffcepol;
+ }
+ sigD = dffD;
+ }
+ }
+endcode
+
+// (7) Match 'P' output that exclusively drives an MREG
+code argD ffM ffMcemux ffMrstmux ffMcepol ffMrstpol sigM sigP clock
+ if (param(dsp, \MREG).as_int() == 0 && nusers(sigM) == 2) {
+ argD = sigM;
+ subpattern(out_dffe);
+ if (dff) {
+ ffM = dff;
+ clock = dffclock;
+ if (dffrstmux) {
+ ffMrstmux = dffrstmux;
+ ffMrstpol = dffrstpol;
+ }
+ if (dffcemux) {
+ ffMcemux = dffcemux;
+ ffMcepol = dffcepol;
+ }
+ sigM = dffQ;
+ }
+ }
+ sigP = sigM;
+endcode
+
+// (8) Match 'P' output that exclusively drives one of two inputs to an $add
+// cell (post-adder).
+// The other input to the adder is assumed to come in from the 'C' input
+// (note: 'P' -> 'C' connections that exist for accumulators are
+// recognised in xilinx_dsp.cc).
+match postAdd
+ // Ensure that Z mux is not already used
+ if port(dsp, \OPMODE, SigSpec()).extract(2,2).is_fully_zero()
+
+ select postAdd->type.in($add)
+ select GetSize(port(postAdd, \Y)) <= 48
+ choice <IdString> AB {\A, \B}
+ select nusers(port(postAdd, AB)) <= 3
+ filter ffMcemux || nusers(port(postAdd, AB)) == 2
+ filter !ffMcemux || nusers(port(postAdd, AB)) == 3
+
+ index <SigBit> port(postAdd, AB)[0] === sigP[0]
+ filter GetSize(port(postAdd, AB)) >= GetSize(sigP)
+ filter port(postAdd, AB).extract(0, GetSize(sigP)) == sigP
+ // Check that remainder of AB is a sign- or zero-extension
+ filter port(postAdd, AB).extract_end(GetSize(sigP)) == SigSpec(sigP[GetSize(sigP)-1], GetSize(port(postAdd, AB))-GetSize(sigP)) || port(postAdd, AB).extract_end(GetSize(sigP)) == SigSpec(State::S0, GetSize(port(postAdd, AB))-GetSize(sigP))
+
+ set postAddAB AB
+ optional
+endmatch
+
+code sigC sigP
+ if (postAdd) {
+ sigC = port(postAdd, postAddAB == \A ? \B : \A);
+ sigP = port(postAdd, \Y);
+ }
+endcode
+
+// (9) Match 'P' output that exclusively drives a PREG
+code argD ffP ffPcemux ffPrstmux ffPcepol ffPrstpol sigP clock
+ if (param(dsp, \PREG).as_int() == 0) {
+ int users = 2;
+ // If ffMcemux and no postAdd new-value net must have three users: ffMcemux, ffM and ffPcemux
+ if (ffMcemux && !postAdd) users++;
+ if (nusers(sigP) == users) {
+ argD = sigP;
+ subpattern(out_dffe);
+ if (dff) {
+ ffP = dff;
+ clock = dffclock;
+ if (dffrstmux) {
+ ffPrstmux = dffrstmux;
+ ffPrstpol = dffrstpol;
+ }
+ if (dffcemux) {
+ ffPcemux = dffcemux;
+ ffPcepol = dffcepol;
+ }
+ sigP = dffQ;
+ }
+ }
+ }
+endcode
+
+// (10) If post-adder and PREG both present, match for a $mux cell driving
+// the 'C' input, where one of the $mux's inputs is the PREG output.
+// This indicates an accumulator situation, and one where a $mux exists
+// to override the accumulated value:
+// +--------------------------------+
+// | ____ |
+// +--| \ |
+// |$mux|-+ |
+// 'C' ---|____/ | |
+// | /-------\ +----+ |
+// +----+ +-| post- |___|PREG|---+ 'P'
+// |MREG|------ | adder | +----+
+// +----+ \-------/
+match postAddMux
+ if postAdd
+ if ffP
+ select postAddMux->type.in($mux)
+ select nusers(port(postAddMux, \Y)) == 2
+ choice <IdString> AB {\A, \B}
+ index <SigSpec> port(postAddMux, AB) === sigP
+ index <SigSpec> port(postAddMux, \Y) === sigC
+ set postAddMuxAB AB
+ optional
+endmatch
+
+code sigC
+ if (postAddMux)
+ sigC = port(postAddMux, postAddMuxAB == \A ? \B : \A);
+endcode
+
+code
+ accept;
+endcode
+
+// #######################
+
+// Subpattern for matching against input registers, based on knowledge of the
+// 'Q' input. Typically, identifying registers with clock-enable and reset
+// capability would be a task would be handled by other Yosys passes such as
+// dff2dffe, but since DSP inference happens much before this, these patterns
+// have to be manually identified.
+// At a high level:
+// (1) Starting from a $dff cell that (partially or fully) drives the given
+// 'Q' argument
+// (2) Match for a $mux cell implementing synchronous reset semantics ---
+// one that exclusively drives the 'D' input of the $dff, with one of its
+// $mux inputs being fully zero
+// (3) Match for a $mux cell implement clock enable semantics --- one that
+// exclusively drives the 'D' input of the $dff (or the other input of
+// the reset $mux) and where one of this $mux's inputs is connected to
+// the 'Q' output of the $dff
+subpattern in_dffe
+arg argD argQ clock
+
+code
+ dff = nullptr;
+ if (GetSize(argQ) == 0)
+ reject;
+ for (const auto &c : argQ.chunks()) {
+ // Abandon matches when 'Q' is a constant
+ if (!c.wire)
+ reject;
+ // Abandon matches when 'Q' has the keep attribute set
+ if (c.wire->get_bool_attribute(\keep))
+ reject;
+ // Abandon matches when 'Q' has a non-zero init attribute set
+ // (not supported by DSP48E1)
+ Const init = c.wire->attributes.at(\init, Const());
+ if (!init.empty())
+ for (auto b : init.extract(c.offset, c.width))
+ if (b != State::Sx && b != State::S0)
+ reject;
+ }
+endcode
+
+// (1) Starting from a $dff cell that (partially or fully) drives the given
+// 'Q' argument
+match ff
+ select ff->type.in($dff)
+ // DSP48E1 does not support clock inversion
+ select param(ff, \CLK_POLARITY).as_bool()
+
+ slice offset GetSize(port(ff, \D))
+ index <SigBit> port(ff, \Q)[offset] === argQ[0]
+
+ // Check that the rest of argQ is present
+ filter GetSize(port(ff, \Q)) >= offset + GetSize(argQ)
+ filter port(ff, \Q).extract(offset, GetSize(argQ)) == argQ
+
+ filter clock == SigBit() || port(ff, \CLK) == clock
+
+ set ffoffset offset
+endmatch
+
+code argQ argD
+ SigSpec Q = port(ff, \Q);
+ dff = ff;
+ dffclock = port(ff, \CLK);
+ dffD = argQ;
+ argD = port(ff, \D);
+ argQ = Q;
+ dffD.replace(argQ, argD);
+ // Only search for ffrstmux if dffD only
+ // has two (ff, ffrstmux) users
+ if (nusers(dffD) > 2)
+ argD = SigSpec();
+endcode
+
+// (2) Match for a $mux cell implementing synchronous reset semantics ---
+// exclusively drives the 'D' input of the $dff, with one of the $mux
+// inputs being fully zero
+match ffrstmux
+ if !argD.empty()
+ select ffrstmux->type.in($mux)
+ index <SigSpec> port(ffrstmux, \Y) === argD
+
+ choice <IdString> BA {\B, \A}
+ // DSP48E1 only supports reset to zero
+ select port(ffrstmux, BA).is_fully_zero()
+
+ define <bool> pol (BA == \B)
+ set ffrstpol pol
+ semioptional
+endmatch
+
+code argD
+ if (ffrstmux) {
+ dffrstmux = ffrstmux;
+ dffrstpol = ffrstpol;
+ argD = port(ffrstmux, ffrstpol ? \A : \B);
+ dffD.replace(port(ffrstmux, \Y), argD);
+
+ // Only search for ffcemux if argQ has at
+ // least 3 users (ff, <upstream>, ffrstmux) and
+ // dffD only has two (ff, ffrstmux)
+ if (!(nusers(argQ) >= 3 && nusers(dffD) == 2))
+ argD = SigSpec();
+ }
+ else
+ dffrstmux = nullptr;
+endcode
+
+// (3) Match for a $mux cell implement clock enable semantics --- one that
+// exclusively drives the 'D' input of the $dff (or the other input of
+// the reset $mux) and where one of this $mux's inputs is connected to
+// the 'Q' output of the $dff
+match ffcemux
+ if !argD.empty()
+ select ffcemux->type.in($mux)
+ index <SigSpec> port(ffcemux, \Y) === argD
+ choice <IdString> AB {\A, \B}
+ index <SigSpec> port(ffcemux, AB) === argQ
+ define <bool> pol (AB == \A)
+ set ffcepol pol
+ semioptional
+endmatch
+
+code argD
+ if (ffcemux) {
+ dffcemux = ffcemux;
+ dffcepol = ffcepol;
+ argD = port(ffcemux, ffcepol ? \B : \A);
+ dffD.replace(port(ffcemux, \Y), argD);
+ }
+ else
+ dffcemux = nullptr;
+endcode
+
+// #######################
+
+// Subpattern for matching against output registers, based on knowledge of the
+// 'D' input.
+// At a high level:
+// (1) Starting from an optional $mux cell that implements clock enable
+// semantics --- one where the given 'D' argument (partially or fully)
+// drives one of its two inputs
+// (2) Starting from, or continuing onto, another optional $mux cell that
+// implements synchronous reset semantics --- one where the given 'D'
+// argument (or the clock enable $mux output) drives one of its two inputs
+// and where the other input is fully zero
+// (3) Match for a $dff cell (whose 'D' input is the 'D' argument, or the
+// output of the previous clock enable or reset $mux cells)
+subpattern out_dffe
+arg argD argQ clock
+
+code
+ dff = nullptr;
+ for (auto c : argD.chunks())
+ // Abandon matches when 'D' has the keep attribute set
+ if (c.wire->get_bool_attribute(\keep))
+ reject;
+endcode
+
+// (1) Starting from an optional $mux cell that implements clock enable
+// semantics --- one where the given 'D' argument (partially or fully)
+// drives one of its two inputs
+match ffcemux
+ select ffcemux->type.in($mux)
+ // ffcemux output must have two users: ffcemux and ff.D
+ select nusers(port(ffcemux, \Y)) == 2
+
+ choice <IdString> AB {\A, \B}
+ // keep-last-value net must have at least three users: ffcemux, ff, downstream sink(s)
+ select nusers(port(ffcemux, AB)) >= 3
+
+ slice offset GetSize(port(ffcemux, \Y))
+ define <IdString> BA (AB == \A ? \B : \A)
+ index <SigBit> port(ffcemux, BA)[offset] === argD[0]
+
+ // Check that the rest of argD is present
+ filter GetSize(port(ffcemux, BA)) >= offset + GetSize(argD)
+ filter port(ffcemux, BA).extract(offset, GetSize(argD)) == argD
+
+ set ffoffset offset
+ define <bool> pol (AB == \A)
+ set ffcepol pol
+
+ semioptional
+endmatch
+
+code argD argQ
+ dffcemux = ffcemux;
+ if (ffcemux) {
+ SigSpec BA = port(ffcemux, ffcepol ? \B : \A);
+ SigSpec Y = port(ffcemux, \Y);
+ argQ = argD;
+ argD.replace(BA, Y);
+ argQ.replace(BA, port(ffcemux, ffcepol ? \A : \B));
+
+ dffcemux = ffcemux;
+ dffcepol = ffcepol;
+ }
+endcode
+
+// (2) Starting from, or continuing onto, another optional $mux cell that
+// implements synchronous reset semantics --- one where the given 'D'
+// argument (or the clock enable $mux output) drives one of its two inputs
+// and where the other input is fully zero
+match ffrstmux
+ select ffrstmux->type.in($mux)
+ // ffrstmux output must have two users: ffrstmux and ff.D
+ select nusers(port(ffrstmux, \Y)) == 2
+
+ choice <IdString> BA {\B, \A}
+ // DSP48E1 only supports reset to zero
+ select port(ffrstmux, BA).is_fully_zero()
+
+ slice offset GetSize(port(ffrstmux, \Y))
+ define <IdString> AB (BA == \B ? \A : \B)
+ index <SigBit> port(ffrstmux, AB)[offset] === argD[0]
+
+ // Check that offset is consistent
+ filter !ffcemux || ffoffset == offset
+ // Check that the rest of argD is present
+ filter GetSize(port(ffrstmux, AB)) >= offset + GetSize(argD)
+ filter port(ffrstmux, AB).extract(offset, GetSize(argD)) == argD
+
+ set ffoffset offset
+ define <bool> pol (AB == \A)
+ set ffrstpol pol
+
+ semioptional
+endmatch
+
+code argD argQ
+ dffrstmux = ffrstmux;
+ if (ffrstmux) {
+ SigSpec AB = port(ffrstmux, ffrstpol ? \A : \B);
+ SigSpec Y = port(ffrstmux, \Y);
+ argD.replace(AB, Y);
+
+ dffrstmux = ffrstmux;
+ dffrstpol = ffrstpol;
+ }
+endcode
+
+// (3) Match for a $dff cell (whose 'D' input is the 'D' argument, or the
+// output of the previous clock enable or reset $mux cells)
+match ff
+ select ff->type.in($dff)
+ // DSP48E1 does not support clock inversion
+ select param(ff, \CLK_POLARITY).as_bool()
+
+ slice offset GetSize(port(ff, \D))
+ index <SigBit> port(ff, \D)[offset] === argD[0]
+
+ // Check that offset is consistent
+ filter (!ffcemux && !ffrstmux) || ffoffset == offset
+ // Check that the rest of argD is present
+ filter GetSize(port(ff, \D)) >= offset + GetSize(argD)
+ filter port(ff, \D).extract(offset, GetSize(argD)) == argD
+ // Check that FF.Q is connected to CE-mux
+ filter !ffcemux || port(ff, \Q).extract(offset, GetSize(argQ)) == argQ
+
+ filter clock == SigBit() || port(ff, \CLK) == clock
+
+ set ffoffset offset
+endmatch
+
+code argQ
+ SigSpec D = port(ff, \D);
+ SigSpec Q = port(ff, \Q);
+ if (!ffcemux) {
+ argQ = argD;
+ argQ.replace(D, Q);
+ }
+
+ // Abandon matches when 'Q' has a non-zero init attribute set
+ // (not supported by DSP48E1)
+ for (auto c : argQ.chunks()) {
+ Const init = c.wire->attributes.at(\init, Const());
+ if (!init.empty())
+ for (auto b : init.extract(c.offset, c.width))
+ if (b != State::Sx && b != State::S0)
+ reject;
+ }
+
+ dff = ff;
+ dffQ = argQ;
+ dffclock = port(ff, \CLK);
+endcode
diff --git a/passes/pmgen/xilinx_dsp_CREG.pmg b/passes/pmgen/xilinx_dsp_CREG.pmg
new file mode 100644
index 000000000..b20e4f458
--- /dev/null
+++ b/passes/pmgen/xilinx_dsp_CREG.pmg
@@ -0,0 +1,235 @@
+// This file describes the second of three pattern matcher setups that
+// forms the `xilinx_dsp` pass described in xilinx_dsp.cc
+// At a high level, it works as follows:
+// (1) Starting from a DSP48* cell that (a) doesn't have a CREG already,
+// and (b) uses the 'C' port
+// (2) Match the driver of the 'C' input to a possible $dff cell (CREG)
+// (attached to at most two $mux cells that implement clock-enable or
+// reset functionality, using a subpattern discussed below)
+// Notes:
+// - Running CREG packing after xilinx_dsp_pack is necessary since there is no
+// guarantee that the cell ordering corresponds to the "expected" case (i.e.
+// the order in which they appear in the source) thus the possiblity existed
+// that a register got packed as a CREG into a downstream DSP that should
+// have otherwise been a PREG of an upstream DSP that had not been visited
+// yet
+// - The reason this is separated out from the xilinx_dsp.pmg file is
+// for efficiency --- each *.pmg file creates a class of the same basename,
+// which when constructed, creates a custom database tailored to the
+// pattern(s) contained within. Since the pattern in this file must be
+// executed after the pattern contained in xilinx_dsp.pmg, it is necessary
+// to reconstruct this database. Separating the two patterns into
+// independent files causes two smaller, more specific, databases.
+
+pattern xilinx_dsp_packC
+
+udata <std::function<SigSpec(const SigSpec&)>> unextend
+state <SigBit> clock
+state <SigSpec> sigC sigP
+state <bool> ffCcepol ffCrstpol
+state <Cell*> ffC ffCcemux ffCrstmux
+
+// Variables used for subpatterns
+state <SigSpec> argQ argD
+state <bool> ffcepol ffrstpol
+state <int> ffoffset
+udata <SigSpec> dffD dffQ
+udata <SigBit> dffclock
+udata <Cell*> dff dffcemux dffrstmux
+udata <bool> dffcepol dffrstpol
+
+// (1) Starting from a DSP48* cell that (a) doesn't have a CREG already,
+// and (b) uses the 'C' port
+match dsp
+ select dsp->type.in(\DSP48A, \DSP48A1, \DSP48E1)
+ select param(dsp, \CREG, 1).as_int() == 0
+ select nusers(port(dsp, \C, SigSpec())) > 1
+endmatch
+
+code sigC sigP clock
+ unextend = [](const SigSpec &sig) {
+ int i;
+ for (i = GetSize(sig)-1; i > 0; i--)
+ if (sig[i] != sig[i-1])
+ break;
+ // Do not remove non-const sign bit
+ if (sig[i].wire)
+ ++i;
+ return sig.extract(0, i);
+ };
+ sigC = unextend(port(dsp, \C, SigSpec()));
+
+ SigSpec P = port(dsp, \P);
+ if (!dsp->type.in(\DSP48E1) ||
+ param(dsp, \USE_MULT, Const("MULTIPLY")).decode_string() == "MULTIPLY") {
+ // Only care about those bits that are used
+ int i;
+ for (i = GetSize(P)-1; i >= 0; i--)
+ if (nusers(P[i]) > 1)
+ break;
+ i++;
+ log_assert(nusers(P.extract_end(i)) <= 1);
+ sigP = P.extract(0, i);
+ }
+ else
+ sigP = P;
+
+ clock = port(dsp, \CLK, SigBit());
+endcode
+
+// (2) Match the driver of the 'C' input to a possible $dff cell (CREG)
+// (attached to at most two $mux cells that implement clock-enable or
+// reset functionality, using the in_dffe subpattern)
+code argQ ffC ffCcemux ffCrstmux ffCcepol ffCrstpol sigC clock
+ argQ = sigC;
+ subpattern(in_dffe);
+ if (dff) {
+ ffC = dff;
+ clock = dffclock;
+ if (dffrstmux) {
+ ffCrstmux = dffrstmux;
+ ffCrstpol = dffrstpol;
+ }
+ if (dffcemux) {
+ ffCcemux = dffcemux;
+ ffCcepol = dffcepol;
+ }
+ sigC = dffD;
+ }
+endcode
+
+code
+ if (ffC)
+ accept;
+endcode
+
+// #######################
+
+// Subpattern for matching against input registers, based on knowledge of the
+// 'Q' input. Typically, identifying registers with clock-enable and reset
+// capability would be a task would be handled by other Yosys passes such as
+// dff2dffe, but since DSP inference happens much before this, these patterns
+// have to be manually identified.
+// At a high level:
+// (1) Starting from a $dff cell that (partially or fully) drives the given
+// 'Q' argument
+// (2) Match for a $mux cell implementing synchronous reset semantics ---
+// one that exclusively drives the 'D' input of the $dff, with one of its
+// $mux inputs being fully zero
+// (3) Match for a $mux cell implement clock enable semantics --- one that
+// exclusively drives the 'D' input of the $dff (or the other input of
+// the reset $mux) and where one of this $mux's inputs is connected to
+// the 'Q' output of the $dff
+subpattern in_dffe
+arg argD argQ clock
+
+code
+ dff = nullptr;
+ for (const auto &c : argQ.chunks()) {
+ // Abandon matches when 'Q' is a constant
+ if (!c.wire)
+ reject;
+ // Abandon matches when 'Q' has the keep attribute set
+ if (c.wire->get_bool_attribute(\keep))
+ reject;
+ // Abandon matches when 'Q' has a non-zero init attribute set
+ // (not supported by DSP48E1)
+ Const init = c.wire->attributes.at(\init, Const());
+ for (auto b : init.extract(c.offset, c.width))
+ if (b != State::Sx && b != State::S0)
+ reject;
+ }
+endcode
+
+// (1) Starting from a $dff cell that (partially or fully) drives the given
+// 'Q' argument
+match ff
+ select ff->type.in($dff)
+ // DSP48E1 does not support clock inversion
+ select param(ff, \CLK_POLARITY).as_bool()
+
+ slice offset GetSize(port(ff, \D))
+ index <SigBit> port(ff, \Q)[offset] === argQ[0]
+
+ // Check that the rest of argQ is present
+ filter GetSize(port(ff, \Q)) >= offset + GetSize(argQ)
+ filter port(ff, \Q).extract(offset, GetSize(argQ)) == argQ
+
+ filter clock == SigBit() || port(ff, \CLK) == clock
+
+ set ffoffset offset
+endmatch
+
+code argQ argD
+ SigSpec Q = port(ff, \Q);
+ dff = ff;
+ dffclock = port(ff, \CLK);
+ dffD = argQ;
+ argD = port(ff, \D);
+ argQ = Q;
+ dffD.replace(argQ, argD);
+ // Only search for ffrstmux if dffD only
+ // has two (ff, ffrstmux) users
+ if (nusers(dffD) > 2)
+ argD = SigSpec();
+endcode
+
+// (2) Match for a $mux cell implementing synchronous reset semantics ---
+// exclusively drives the 'D' input of the $dff, with one of the $mux
+// inputs being fully zero
+match ffrstmux
+ if !argD.empty()
+ select ffrstmux->type.in($mux)
+ index <SigSpec> port(ffrstmux, \Y) === argD
+
+ choice <IdString> BA {\B, \A}
+ // DSP48E1 only supports reset to zero
+ select port(ffrstmux, BA).is_fully_zero()
+
+ define <bool> pol (BA == \B)
+ set ffrstpol pol
+ semioptional
+endmatch
+
+code argD
+ if (ffrstmux) {
+ dffrstmux = ffrstmux;
+ dffrstpol = ffrstpol;
+ argD = port(ffrstmux, ffrstpol ? \A : \B);
+ dffD.replace(port(ffrstmux, \Y), argD);
+
+ // Only search for ffcemux if argQ has at
+ // least 3 users (ff, <upstream>, ffrstmux) and
+ // dffD only has two (ff, ffrstmux)
+ if (!(nusers(argQ) >= 3 && nusers(dffD) == 2))
+ argD = SigSpec();
+ }
+ else
+ dffrstmux = nullptr;
+endcode
+
+// (3) Match for a $mux cell implement clock enable semantics --- one that
+// exclusively drives the 'D' input of the $dff (or the other input of
+// the reset $mux) and where one of this $mux's inputs is connected to
+// the 'Q' output of the $dff
+match ffcemux
+ if !argD.empty()
+ select ffcemux->type.in($mux)
+ index <SigSpec> port(ffcemux, \Y) === argD
+ choice <IdString> AB {\A, \B}
+ index <SigSpec> port(ffcemux, AB) === argQ
+ define <bool> pol (AB == \A)
+ set ffcepol pol
+ semioptional
+endmatch
+
+code argD
+ if (ffcemux) {
+ dffcemux = ffcemux;
+ dffcepol = ffcepol;
+ argD = port(ffcemux, ffcepol ? \B : \A);
+ dffD.replace(port(ffcemux, \Y), argD);
+ }
+ else
+ dffcemux = nullptr;
+endcode
diff --git a/passes/pmgen/xilinx_dsp_cascade.pmg b/passes/pmgen/xilinx_dsp_cascade.pmg
new file mode 100644
index 000000000..b14a1ee0a
--- /dev/null
+++ b/passes/pmgen/xilinx_dsp_cascade.pmg
@@ -0,0 +1,486 @@
+// This file describes the third of three pattern matcher setups that
+// forms the `xilinx_dsp` pass described in xilinx_dsp.cc
+// At a high level, it works as follows:
+// (1) Starting from a DSP48E1 cell that (a) has the Z multiplexer
+// (controlled by OPMODE[6:4]) set to zero and (b) doesn't already
+// use the 'PCOUT' port
+// (2.1) Match another DSP48E1 cell that (a) does not have the CREG enabled,
+// (b) has its Z multiplexer output set to the 'C' port, which is
+// driven by the 'P' output of the previous DSP cell, and (c) has its
+// 'PCIN' port unused
+// (2.2) Same as (2.1) but with the 'C' port driven by the 'P' output of the
+// previous DSP cell right-shifted by 17 bits
+// (3) For this subequent DSP48E1 match (i.e. PCOUT -> PCIN cascade exists)
+// if (a) the previous DSP48E1 uses either the A2REG or A1REG, (b) this
+// DSP48 does not use A2REG nor A1REG, (c) this DSP48E1 does not already
+// have an ACOUT -> ACIN cascade, (d) the previous DSP does not already
+// use its ACOUT port, then examine if an ACOUT -> ACIN cascade
+// opportunity exists by matching for a $dff-with-optional-clock-enable-
+// or-reset and checking that the 'D' input of this register is the same
+// as the 'A' input of the previous DSP
+// (4) Same as (3) but for BCOUT -> BCIN cascade
+// (5) Recursively go to (2.1) until no more matches possible, keeping track
+// of the longest possible chain found
+// (6) The longest chain is then divided into chunks of no more than
+// MAX_DSP_CASCADE in length (to prevent long cascades that exceed the
+// height of a DSP column) with each DSP in each chunk being rewritten
+// to use [ABP]COUT -> [ABP]CIN cascading as appropriate
+// Notes:
+// - Currently, [AB]COUT -> [AB]COUT cascades (3 or 4) are only considered
+// if a PCOUT -> PCIN cascade is (2.1 or 2.2) first identified; this need
+// not be the case --- [AB] cascades can exist independently of a P cascade
+// (though all three cascades must come from the same DSP). This situation
+// is not handled currently.
+// - In addition, [AB]COUT -> [AB]COUT cascades (3 or 4) are currently
+// conservative in that they examine the situation where (a) the previous
+// DSP has [AB]2REG or [AB]1REG enabled, (b) that the downstream DSP has no
+// registers enabled, and (c) that there exists only one additional register
+// between the upstream and downstream DSPs. This can certainly be relaxed
+// to identify situations ranging from (i) neither DSP uses any registers,
+// to (ii) upstream DSP has 2 registers, downstream DSP has 2 registers, and
+// there exists a further 2 registers between them. This remains a TODO
+// item.
+
+pattern xilinx_dsp_cascade
+
+udata <std::function<SigSpec(const SigSpec&)>> unextend
+udata <vector<std::tuple<Cell*,int,int,int>>> chain longest_chain
+state <Cell*> next
+state <SigSpec> clock
+state <int> AREG BREG
+
+// Variables used for subpatterns
+state <SigSpec> argQ argD
+state <bool> ffcepol ffrstpol
+state <int> ffoffset
+udata <SigSpec> dffD dffQ
+udata <SigBit> dffclock
+udata <Cell*> dff dffcemux dffrstmux
+udata <bool> dffcepol dffrstpol
+
+code
+#define MAX_DSP_CASCADE 20
+endcode
+
+// (1) Starting from a DSP48* cell that (a) has the Z multiplexer
+// (controlled by OPMODE[3:2] for DSP48A*, by OPMODE[6:4] for DSP48E1)
+// set to zero and (b) doesn't already use the 'PCOUT' port
+match first
+ select (first->type.in(\DSP48A, \DSP48A1) && port(first, \OPMODE, Const(0, 8)).extract(2,2) == Const::from_string("00")) || (first->type.in(\DSP48E1) && port(first, \OPMODE, Const(0, 7)).extract(4,3) == Const::from_string("000"))
+ select nusers(port(first, \PCOUT, SigSpec())) <= 1
+endmatch
+
+// (6) The longest chain is then divided into chunks of no more than
+// MAX_DSP_CASCADE in length (to prevent long cascades that exceed the
+// height of a DSP column) with each DSP in each chunk being rewritten
+// to use [ABP]COUT -> [ABP]CIN cascading as appropriate
+code
+ longest_chain.clear();
+ chain.emplace_back(first, -1, -1, -1);
+ subpattern(tail);
+finally
+ chain.pop_back();
+ log_assert(chain.empty());
+ if (GetSize(longest_chain) > 1) {
+ Cell *dsp = std::get<0>(longest_chain.front());
+
+ Cell *dsp_pcin;
+ int P, AREG, BREG;
+ for (int i = 1; i < GetSize(longest_chain); i++) {
+ std::tie(dsp_pcin,P,AREG,BREG) = longest_chain[i];
+
+ if (i % MAX_DSP_CASCADE > 0) {
+ if (P >= 0) {
+ Wire *cascade = module->addWire(NEW_ID, 48);
+ dsp_pcin->setPort(ID(C), Const(0, 48));
+ dsp_pcin->setPort(ID(PCIN), cascade);
+ dsp->setPort(ID(PCOUT), cascade);
+ add_siguser(cascade, dsp_pcin);
+ add_siguser(cascade, dsp);
+
+ SigSpec opmode = port(dsp_pcin, \OPMODE, Const(0, 7));
+ if (dsp->type.in(\DSP48A, \DSP48A1)) {
+ log_assert(P == 0);
+ opmode[3] = State::S0;
+ opmode[2] = State::S1;
+ }
+ else if (dsp->type.in(\DSP48E1)) {
+ if (P == 17)
+ opmode[6] = State::S1;
+ else if (P == 0)
+ opmode[6] = State::S0;
+ else log_abort();
+
+ opmode[5] = State::S0;
+ opmode[4] = State::S1;
+ }
+ dsp_pcin->setPort(\OPMODE, opmode);
+
+ log_debug("PCOUT -> PCIN cascade for %s -> %s\n", log_id(dsp), log_id(dsp_pcin));
+ }
+ if (AREG >= 0) {
+ Wire *cascade = module->addWire(NEW_ID, 30);
+ dsp_pcin->setPort(ID(A), Const(0, 30));
+ dsp_pcin->setPort(ID(ACIN), cascade);
+ dsp->setPort(ID(ACOUT), cascade);
+ add_siguser(cascade, dsp_pcin);
+ add_siguser(cascade, dsp);
+
+ if (dsp->type.in(\DSP48E1))
+ dsp->setParam(ID(ACASCREG), AREG);
+ dsp_pcin->setParam(ID(A_INPUT), Const("CASCADE"));
+
+ log_debug("ACOUT -> ACIN cascade for %s -> %s\n", log_id(dsp), log_id(dsp_pcin));
+ }
+ if (BREG >= 0) {
+ Wire *cascade = module->addWire(NEW_ID, 18);
+ if (dsp->type.in(\DSP48A, \DSP48A1)) {
+ // According to UG389 p9 [https://www.xilinx.com/support/documentation/user_guides/ug389.pdf]
+ // "The DSP48A1 component uses this input when cascading
+ // BCOUT from an adjacent DSP48A1 slice. The tools then
+ // translate BCOUT cascading to the dedicated BCIN input
+ // and set the B_INPUT attribute for implementation."
+ dsp_pcin->setPort(ID(B), cascade);
+ }
+ else {
+ dsp_pcin->setPort(ID(B), Const(0, 18));
+ dsp_pcin->setPort(ID(BCIN), cascade);
+ }
+ dsp->setPort(ID(BCOUT), cascade);
+ add_siguser(cascade, dsp_pcin);
+ add_siguser(cascade, dsp);
+
+ if (dsp->type.in(\DSP48E1)) {
+ dsp->setParam(ID(BCASCREG), BREG);
+ // According to UG389 p13 [https://www.xilinx.com/support/documentation/user_guides/ug389.pdf]
+ // "The attribute is only used by place and route tools and
+ // is not necessary for the users to set for synthesis. The
+ // attribute is determined by the connection to the B port
+ // of the DSP48A1 slice. If the B port is connected to the
+ // BCOUT of another DSP48A1 slice, then the tools automatically
+ // set the attribute to 'CASCADE', otherwise it is set to
+ // 'DIRECT'".
+ dsp_pcin->setParam(ID(B_INPUT), Const("CASCADE"));
+ }
+
+ log_debug("BCOUT -> BCIN cascade for %s -> %s\n", log_id(dsp), log_id(dsp_pcin));
+ }
+ }
+ else {
+ log_debug(" Blocking %s -> %s cascade (exceeds max: %d)\n", log_id(dsp), log_id(dsp_pcin), MAX_DSP_CASCADE);
+ }
+
+ dsp = dsp_pcin;
+ }
+
+ accept;
+ }
+endcode
+
+// ------------------------------------------------------------------
+
+subpattern tail
+arg first
+arg next
+
+// (2.1) Match another DSP48* cell that (a) does not have the CREG enabled,
+// (b) has its Z multiplexer output set to the 'C' port, which is
+// driven by the 'P' output of the previous DSP cell, and (c) has its
+// 'PCIN' port unused
+match nextP
+ select !param(nextP, \CREG, State::S1).as_bool()
+ select (nextP->type.in(\DSP48A, \DSP48A1) && port(nextP, \OPMODE, Const(0, 8)).extract(2,2) == Const::from_string("11")) || (nextP->type.in(\DSP48E1) && port(nextP, \OPMODE, Const(0, 7)).extract(4,3) == Const::from_string("011"))
+ select nusers(port(nextP, \C, SigSpec())) > 1
+ select nusers(port(nextP, \PCIN, SigSpec())) == 0
+ index <SigBit> port(nextP, \C)[0] === port(std::get<0>(chain.back()), \P)[0]
+ semioptional
+endmatch
+
+// (2.2) For DSP48E1 only, same as (2.1) but with the 'C' port driven
+// by the 'P' output of the previous DSP cell right-shifted by 17 bits
+match nextP_shift17
+ if !nextP
+ select nextP_shift17->type.in(\DSP48E1)
+ select !param(nextP_shift17, \CREG, State::S1).as_bool()
+ select port(nextP_shift17, \OPMODE, Const(0, 7)).extract(4,3) == Const::from_string("011")
+ select nusers(port(nextP_shift17, \C, SigSpec())) > 1
+ select nusers(port(nextP_shift17, \PCIN, SigSpec())) == 0
+ index <SigBit> port(nextP_shift17, \C)[0] === port(std::get<0>(chain.back()), \P)[17]
+ semioptional
+endmatch
+
+code next
+ next = nextP;
+ if (!nextP)
+ next = nextP_shift17;
+ if (next) {
+ if (next->type != first->type)
+ reject;
+ unextend = [](const SigSpec &sig) {
+ int i;
+ for (i = GetSize(sig)-1; i > 0; i--)
+ if (sig[i] != sig[i-1])
+ break;
+ // Do not remove non-const sign bit
+ if (sig[i].wire)
+ ++i;
+ return sig.extract(0, i);
+ };
+ }
+endcode
+
+// (3) For this subequent DSP48E1 match (i.e. PCOUT -> PCIN cascade exists)
+// if (a) this DSP48E1 does not already have an ACOUT -> ACIN cascade,
+// (b) the previous DSP does not already use its ACOUT port, then
+// examine if an ACOUT -> ACIN cascade opportunity exists if
+// (i) A ports are identical, or (ii) separated by a
+// $dff-with-optional-clock-enable-or-reset and checking that the 'D' input
+// of this register is the same as the 'A' input of the previous DSP
+// TODO: Check for two levels of flops, instead of just one
+code argQ clock AREG
+ AREG = -1;
+ if (next && next->type.in(\DSP48E1)) {
+ Cell *prev = std::get<0>(chain.back());
+
+ if (param(next, \A_INPUT, Const("DIRECT")).decode_string() == "DIRECT" &&
+ port(next, \ACIN, SigSpec()).is_fully_zero() &&
+ nusers(port(prev, \ACOUT, SigSpec())) <= 1) {
+ if (param(prev, \AREG, 2) == 0) {
+ if (port(prev, \A) == port(next, \A))
+ AREG = 0;
+ }
+ else {
+ argQ = unextend(port(next, \A));
+ clock = port(prev, \CLK);
+ subpattern(in_dffe);
+ if (dff) {
+ if (!dffrstmux && port(prev, \RSTA, State::S0) != State::S0)
+ goto reject_AREG;
+ if (dffrstmux && port(dffrstmux, \S) != port(prev, \RSTA, State::S0))
+ goto reject_AREG;
+ IdString CEA;
+ if (param(prev, \AREG, 2) == 1)
+ CEA = \CEA2;
+ else if (param(prev, \AREG, 2) == 2)
+ CEA = \CEA1;
+ else log_abort();
+ if (!dffcemux && port(prev, CEA, State::S0) != State::S1)
+ goto reject_AREG;
+ if (dffcemux && port(dffcemux, \S) != port(prev, CEA, State::S0))
+ goto reject_AREG;
+ if (dffD == unextend(port(prev, \A)))
+ AREG = 1;
+ }
+ }
+ }
+reject_AREG: ;
+ }
+endcode
+
+// (4) Same as (3) but for BCOUT -> BCIN cascade
+code argQ clock BREG
+ BREG = -1;
+ if (next) {
+ Cell *prev = std::get<0>(chain.back());
+ if (param(next, \B_INPUT, Const("DIRECT")).decode_string() == "DIRECT" &&
+ port(next, \BCIN, SigSpec()).is_fully_zero() &&
+ nusers(port(prev, \BCOUT, SigSpec())) <= 1) {
+ if ((next->type.in(\DSP48A, \DSP48A1) && param(prev, \B0REG, 0) == 0 && param(prev, \B1REG, 1) == 0) ||
+ (next->type.in(\DSP48E1) && param(prev, \BREG, 2) == 0)) {
+ if (port(prev, \B) == port(next, \B))
+ BREG = 0;
+ }
+ else {
+ argQ = unextend(port(next, \B));
+ clock = port(prev, \CLK);
+ subpattern(in_dffe);
+ if (dff) {
+ if (!dffrstmux && port(prev, \RSTB, State::S0) != State::S0)
+ goto reject_BREG;
+ if (dffrstmux && port(dffrstmux, \S) != port(prev, \RSTB, State::S0))
+ goto reject_BREG;
+ IdString CEB;
+ if (next->type.in(\DSP48A, \DSP48A1))
+ CEB = \CEB;
+ else if (next->type.in(\DSP48E1)) {
+ if (param(prev, \BREG, 2) == 1)
+ CEB = \CEB2;
+ else if (param(prev, \BREG, 2) == 2)
+ CEB = \CEB1;
+ else log_abort();
+ }
+ else log_abort();
+ if (!dffcemux && port(prev, CEB, State::S0) != State::S1)
+ goto reject_BREG;
+ if (dffcemux && port(dffcemux, \S) != port(prev, CEB, State::S0))
+ goto reject_BREG;
+ if (dffD == unextend(port(prev, \B))) {
+ if (next->type.in(\DSP48A, \DSP48A1) && param(prev, \B0REG, 0) != 0)
+ goto reject_BREG;
+ BREG = 1;
+ }
+ }
+ }
+ }
+reject_BREG: ;
+ }
+endcode
+
+// (5) Recursively go to (2.1) until no more matches possible, recording the
+// longest possible chain
+code
+ if (next) {
+ chain.emplace_back(next, nextP_shift17 ? 17 : nextP ? 0 : -1, AREG, BREG);
+
+ SigSpec sigC = unextend(port(next, \C));
+
+ if (nextP_shift17) {
+ if (GetSize(sigC)+17 <= GetSize(port(std::get<0>(chain.back()), \P)) &&
+ port(std::get<0>(chain.back()), \P).extract(17, GetSize(sigC)) != sigC)
+ subpattern(tail);
+ }
+ else {
+ if (GetSize(sigC) <= GetSize(port(std::get<0>(chain.back()), \P)) &&
+ port(std::get<0>(chain.back()), \P).extract(0, GetSize(sigC)) != sigC)
+ subpattern(tail);
+
+ }
+ } else {
+ if (GetSize(chain) > GetSize(longest_chain))
+ longest_chain = chain;
+ }
+finally
+ if (next)
+ chain.pop_back();
+endcode
+
+// #######################
+
+// Subpattern for matching against input registers, based on knowledge of the
+// 'Q' input. Typically, identifying registers with clock-enable and reset
+// capability would be a task would be handled by other Yosys passes such as
+// dff2dffe, but since DSP inference happens much before this, these patterns
+// have to be manually identified.
+// At a high level:
+// (1) Starting from a $dff cell that (partially or fully) drives the given
+// 'Q' argument
+// (2) Match for a $mux cell implementing synchronous reset semantics ---
+// one that exclusively drives the 'D' input of the $dff, with one of its
+// $mux inputs being fully zero
+// (3) Match for a $mux cell implement clock enable semantics --- one that
+// exclusively drives the 'D' input of the $dff (or the other input of
+// the reset $mux) and where one of this $mux's inputs is connected to
+// the 'Q' output of the $dff
+subpattern in_dffe
+arg argD argQ clock
+
+code
+ dff = nullptr;
+ for (const auto &c : argQ.chunks()) {
+ // Abandon matches when 'Q' is a constant
+ if (!c.wire)
+ reject;
+ // Abandon matches when 'Q' has the keep attribute set
+ if (c.wire->get_bool_attribute(\keep))
+ reject;
+ // Abandon matches when 'Q' has a non-zero init attribute set
+ // (not supported by DSP48E1)
+ Const init = c.wire->attributes.at(\init, Const());
+ for (auto b : init.extract(c.offset, c.width))
+ if (b != State::Sx && b != State::S0)
+ reject;
+ }
+endcode
+
+// (1) Starting from a $dff cell that (partially or fully) drives the given
+// 'Q' argument
+match ff
+ select ff->type.in($dff)
+ // DSP48E1 does not support clock inversion
+ select param(ff, \CLK_POLARITY).as_bool()
+
+ slice offset GetSize(port(ff, \D))
+ index <SigBit> port(ff, \Q)[offset] === argQ[0]
+
+ // Check that the rest of argQ is present
+ filter GetSize(port(ff, \Q)) >= offset + GetSize(argQ)
+ filter port(ff, \Q).extract(offset, GetSize(argQ)) == argQ
+
+ filter clock == SigBit() || port(ff, \CLK) == clock
+
+ set ffoffset offset
+endmatch
+
+code argQ argD
+ SigSpec Q = port(ff, \Q);
+ dff = ff;
+ dffclock = port(ff, \CLK);
+ dffD = argQ;
+ argD = port(ff, \D);
+ argQ = Q;
+ dffD.replace(argQ, argD);
+ // Only search for ffrstmux if dffD only
+ // has two (ff, ffrstmux) users
+ if (nusers(dffD) > 2)
+ argD = SigSpec();
+endcode
+
+// (2) Match for a $mux cell implementing synchronous reset semantics ---
+// exclusively drives the 'D' input of the $dff, with one of the $mux
+// inputs being fully zero
+match ffrstmux
+ if !argD.empty()
+ select ffrstmux->type.in($mux)
+ index <SigSpec> port(ffrstmux, \Y) === argD
+
+ choice <IdString> BA {\B, \A}
+ // DSP48E1 only supports reset to zero
+ select port(ffrstmux, BA).is_fully_zero()
+
+ define <bool> pol (BA == \B)
+ set ffrstpol pol
+ semioptional
+endmatch
+
+code argD
+ if (ffrstmux) {
+ dffrstmux = ffrstmux;
+ dffrstpol = ffrstpol;
+ argD = port(ffrstmux, ffrstpol ? \A : \B);
+ dffD.replace(port(ffrstmux, \Y), argD);
+
+ // Only search for ffcemux if argQ has at
+ // least 3 users (ff, <upstream>, ffrstmux) and
+ // dffD only has two (ff, ffrstmux)
+ if (!(nusers(argQ) >= 3 && nusers(dffD) == 2))
+ argD = SigSpec();
+ }
+ else
+ dffrstmux = nullptr;
+endcode
+
+// (3) Match for a $mux cell implement clock enable semantics --- one that
+// exclusively drives the 'D' input of the $dff (or the other input of
+// the reset $mux) and where one of this $mux's inputs is connected to
+// the 'Q' output of the $dff
+match ffcemux
+ if !argD.empty()
+ select ffcemux->type.in($mux)
+ index <SigSpec> port(ffcemux, \Y) === argD
+ choice <IdString> AB {\A, \B}
+ index <SigSpec> port(ffcemux, AB) === argQ
+ define <bool> pol (AB == \A)
+ set ffcepol pol
+ semioptional
+endmatch
+
+code argD
+ if (ffcemux) {
+ dffcemux = ffcemux;
+ dffcepol = ffcepol;
+ argD = port(ffcemux, ffcepol ? \B : \A);
+ dffD.replace(port(ffcemux, \Y), argD);
+ }
+ else
+ dffcemux = nullptr;
+endcode
diff --git a/passes/pmgen/xilinx_srl.cc b/passes/pmgen/xilinx_srl.cc
new file mode 100644
index 000000000..3d264e8d4
--- /dev/null
+++ b/passes/pmgen/xilinx_srl.cc
@@ -0,0 +1,258 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * (C) 2019 Eddie Hung <eddie@fpgeh.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
+ * 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
+
+#include "passes/pmgen/xilinx_srl_pm.h"
+
+void run_fixed(xilinx_srl_pm &pm)
+{
+ auto &st = pm.st_fixed;
+ auto &ud = pm.ud_fixed;
+ log("Found fixed chain of length %d (%s):\n", GetSize(ud.longest_chain), log_id(st.first->type));
+
+ SigSpec initval;
+ for (auto cell : ud.longest_chain) {
+ log_debug(" %s\n", log_id(cell));
+ if (cell->type.in(ID($_DFF_N_), ID($_DFF_P_), ID($_DFFE_NN_), ID($_DFFE_NP_), ID($_DFFE_PN_), ID($_DFFE_PP_))) {
+ SigBit Q = cell->getPort(ID(Q));
+ log_assert(Q.wire);
+ auto it = Q.wire->attributes.find(ID(init));
+ if (it != Q.wire->attributes.end()) {
+ auto &i = it->second[Q.offset];
+ initval.append(i);
+ i = State::Sx;
+ }
+ else
+ initval.append(State::Sx);
+ }
+ else if (cell->type.in(ID(FDRE), ID(FDRE_1))) {
+ if (cell->parameters.at(ID(INIT), State::S0).as_bool())
+ initval.append(State::S1);
+ else
+ initval.append(State::S0);
+ }
+ else
+ log_abort();
+ pm.autoremove(cell);
+ }
+
+ auto first_cell = ud.longest_chain.back();
+ auto last_cell = ud.longest_chain.front();
+ Cell *c = pm.module->addCell(NEW_ID, ID($__XILINX_SHREG_));
+ pm.module->swap_names(c, first_cell);
+
+ if (first_cell->type.in(ID($_DFF_N_), ID($_DFF_P_), ID($_DFFE_NN_), ID($_DFFE_NP_), ID($_DFFE_PN_), ID($_DFFE_PP_), ID(FDRE), ID(FDRE_1))) {
+ c->setParam(ID(DEPTH), GetSize(ud.longest_chain));
+ c->setParam(ID(INIT), initval.as_const());
+ if (first_cell->type.in(ID($_DFF_P_), ID($_DFFE_PN_), ID($_DFFE_PP_)))
+ c->setParam(ID(CLKPOL), 1);
+ else if (first_cell->type.in(ID($_DFF_N_), ID($DFFE_NN_), ID($_DFFE_NP_), ID(FDRE_1)))
+ c->setParam(ID(CLKPOL), 0);
+ else if (first_cell->type.in(ID(FDRE))) {
+ if (!first_cell->parameters.at(ID(IS_C_INVERTED), State::S0).as_bool())
+ c->setParam(ID(CLKPOL), 1);
+ else
+ c->setParam(ID(CLKPOL), 0);
+ }
+ else
+ log_abort();
+ if (first_cell->type.in(ID($_DFFE_NP_), ID($_DFFE_PP_)))
+ c->setParam(ID(ENPOL), 1);
+ else if (first_cell->type.in(ID($_DFFE_NN_), ID($_DFFE_PN_)))
+ c->setParam(ID(ENPOL), 0);
+ else
+ c->setParam(ID(ENPOL), 2);
+
+ c->setPort(ID(C), first_cell->getPort(ID(C)));
+ c->setPort(ID(D), first_cell->getPort(ID(D)));
+ c->setPort(ID(Q), last_cell->getPort(ID(Q)));
+ c->setPort(ID(L), GetSize(ud.longest_chain)-1);
+ if (first_cell->type.in(ID($_DFF_N_), ID($_DFF_P_)))
+ c->setPort(ID(E), State::S1);
+ else if (first_cell->type.in(ID($_DFFE_NN_), ID($_DFFE_NP_), ID($_DFFE_PN_), ID($_DFFE_PP_)))
+ c->setPort(ID(E), first_cell->getPort(ID(E)));
+ else if (first_cell->type.in(ID(FDRE), ID(FDRE_1)))
+ c->setPort(ID(E), first_cell->getPort(ID(CE)));
+ else
+ log_abort();
+ }
+ else
+ log_abort();
+
+ log(" -> %s (%s)\n", log_id(c), log_id(c->type));
+}
+
+void run_variable(xilinx_srl_pm &pm)
+{
+ auto &st = pm.st_variable;
+ auto &ud = pm.ud_variable;
+
+ log("Found variable chain of length %d (%s):\n", GetSize(ud.chain), log_id(st.first->type));
+
+ SigSpec initval;
+ for (const auto &i : ud.chain) {
+ auto cell = i.first;
+ auto slice = i.second;
+ log_debug(" %s\n", log_id(cell));
+ if (cell->type.in(ID($_DFF_N_), ID($_DFF_P_), ID($_DFFE_NN_), ID($_DFFE_NP_), ID($_DFFE_PN_), ID($_DFFE_PP_), ID($dff), ID($dffe))) {
+ SigBit Q = cell->getPort(ID(Q))[slice];
+ log_assert(Q.wire);
+ auto it = Q.wire->attributes.find(ID(init));
+ if (it != Q.wire->attributes.end()) {
+ auto &i = it->second[Q.offset];
+ initval.append(i);
+ i = State::Sx;
+ }
+ else
+ initval.append(State::Sx);
+ }
+ else
+ log_abort();
+ }
+ pm.autoremove(st.shiftx);
+
+ auto first_cell = ud.chain.back().first;
+ auto first_slice = ud.chain.back().second;
+
+ Cell *c = pm.module->addCell(NEW_ID, ID($__XILINX_SHREG_));
+ pm.module->swap_names(c, first_cell);
+
+ if (first_cell->type.in(ID($_DFF_N_), ID($_DFF_P_), ID($_DFFE_NN_), ID($_DFFE_NP_), ID($_DFFE_PN_), ID($_DFFE_PP_), ID($dff), ID($dffe))) {
+ c->setParam(ID(DEPTH), GetSize(ud.chain));
+ c->setParam(ID(INIT), initval.as_const());
+ Const clkpol, enpol;
+ if (first_cell->type.in(ID($_DFF_P_), ID($_DFFE_PN_), ID($_DFFE_PP_)))
+ clkpol = 1;
+ else if (first_cell->type.in(ID($_DFF_N_), ID($DFFE_NN_), ID($_DFFE_NP_)))
+ clkpol = 0;
+ else if (first_cell->type.in(ID($dff), ID($dffe)))
+ clkpol = first_cell->getParam(ID(CLK_POLARITY));
+ else
+ log_abort();
+ if (first_cell->type.in(ID($_DFFE_NP_), ID($_DFFE_PP_)))
+ enpol = 1;
+ else if (first_cell->type.in(ID($_DFFE_NN_), ID($_DFFE_PN_)))
+ enpol = 0;
+ else if (first_cell->type.in(ID($dffe)))
+ enpol = first_cell->getParam(ID(EN_POLARITY));
+ else
+ enpol = 2;
+ c->setParam(ID(CLKPOL), clkpol);
+ c->setParam(ID(ENPOL), enpol);
+
+ if (first_cell->type.in(ID($_DFF_N_), ID($_DFF_P_), ID($_DFFE_NN_), ID($_DFFE_NP_), ID($_DFFE_PN_), ID($_DFFE_PP_)))
+ c->setPort(ID(C), first_cell->getPort(ID(C)));
+ else if (first_cell->type.in(ID($dff), ID($dffe)))
+ c->setPort(ID(C), first_cell->getPort(ID(CLK)));
+ else
+ log_abort();
+ c->setPort(ID(D), first_cell->getPort(ID(D))[first_slice]);
+ c->setPort(ID(Q), st.shiftx->getPort(ID(Y)));
+ c->setPort(ID(L), st.shiftx->getPort(ID(B)));
+ if (first_cell->type.in(ID($_DFF_N_), ID($_DFF_P_), ID($dff)))
+ c->setPort(ID(E), State::S1);
+ else if (first_cell->type.in(ID($_DFFE_NN_), ID($_DFFE_NP_), ID($_DFFE_PN_), ID($_DFFE_PP_)))
+ c->setPort(ID(E), first_cell->getPort(ID(E)));
+ else if (first_cell->type.in(ID($dffe)))
+ c->setPort(ID(E), first_cell->getPort(ID(EN)));
+ else
+ log_abort();
+ }
+ else
+ log_abort();
+
+ log(" -> %s (%s)\n", log_id(c), log_id(c->type));
+}
+
+struct XilinxSrlPass : public Pass {
+ XilinxSrlPass() : Pass("xilinx_srl", "Xilinx shift register extraction") { }
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" xilinx_srl [options] [selection]\n");
+ log("\n");
+ log("This pass converts chains of built-in flops (bit-level: $_DFF_[NP]_, $_DFFE_*\n");
+ log("and word-level: $dff, $dffe) as well as Xilinx flops (FDRE, FDRE_1) into a\n");
+ log("$__XILINX_SHREG cell. Chains must be of the same cell type, clock, clock polarity,\n");
+ log("enable, and enable polarity (where relevant).\n");
+ log("Flops with resets cannot be mapped to Xilinx devices and will not be inferred.");
+ log("\n");
+ log(" -minlen N\n");
+ log(" min length of shift register (default = 3)\n");
+ log("\n");
+ log(" -fixed\n");
+ log(" infer fixed-length shift registers.\n");
+ log("\n");
+ log(" -variable\n");
+ log(" infer variable-length shift registers (i.e. fixed-length shifts where\n");
+ log(" each element also fans-out to a $shiftx cell).\n");
+ log("\n");
+ }
+
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ log_header(design, "Executing XILINX_SRL pass (Xilinx shift register extraction).\n");
+
+ bool fixed = false;
+ bool variable = false;
+ int minlen = 3;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ if (args[argidx] == "-minlen" && argidx+1 < args.size()) {
+ minlen = atoi(args[++argidx].c_str());
+ continue;
+ }
+ if (args[argidx] == "-fixed") {
+ fixed = true;
+ continue;
+ }
+ if (args[argidx] == "-variable") {
+ variable = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ if (!fixed && !variable)
+ log_cmd_error("'-fixed' and/or '-variable' must be specified.\n");
+
+ for (auto module : design->selected_modules()) {
+ auto pm = xilinx_srl_pm(module, module->selected_cells());
+ pm.ud_fixed.minlen = minlen;
+ pm.ud_variable.minlen = minlen;
+
+ if (fixed)
+ pm.run_fixed(run_fixed);
+ if (variable)
+ pm.run_variable(run_variable);
+ }
+ }
+} XilinxSrlPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/pmgen/xilinx_srl.pmg b/passes/pmgen/xilinx_srl.pmg
new file mode 100644
index 000000000..535b3dfdc
--- /dev/null
+++ b/passes/pmgen/xilinx_srl.pmg
@@ -0,0 +1,326 @@
+pattern fixed
+
+state <IdString> clk_port en_port
+udata <vector<Cell*>> chain longest_chain
+udata <pool<Cell*>> non_first_cells
+udata <int> minlen
+
+code
+ non_first_cells.clear();
+ subpattern(setup);
+endcode
+
+match first
+ select first->type.in($_DFF_N_, $_DFF_P_, $_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_, \FDRE, \FDRE_1)
+ select !first->has_keep_attr()
+ select !first->type.in(\FDRE) || !param(first, \IS_R_INVERTED, State::S0).as_bool()
+ select !first->type.in(\FDRE) || !param(first, \IS_D_INVERTED, State::S0).as_bool()
+ select !first->type.in(\FDRE, \FDRE_1) || port(first, \R, State::S0).is_fully_zero()
+ filter !non_first_cells.count(first)
+generate
+ SigSpec C = module->addWire(NEW_ID);
+ SigSpec D = module->addWire(NEW_ID);
+ SigSpec Q = module->addWire(NEW_ID);
+ auto r = rng(8);
+ Cell* cell;
+ switch (r)
+ {
+ case 0:
+ case 1:
+ cell = module->addCell(NEW_ID, \FDRE);
+ cell->setPort(\C, C);
+ cell->setPort(\D, D);
+ cell->setPort(\Q, Q);
+ cell->setPort(\CE, module->addWire(NEW_ID));
+ if (r & 1)
+ cell->setPort(\R, module->addWire(NEW_ID));
+ else {
+ if (rng(2) == 0)
+ cell->setPort(\R, State::S0);
+ }
+ break;
+ case 2:
+ case 3:
+ cell = module->addDffGate(NEW_ID, C, D, Q, r & 1);
+ break;
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ cell = module->addDffeGate(NEW_ID, C, module->addWire(NEW_ID), D, Q, r & 1, r & 2);
+ break;
+ default: log_abort();
+ }
+endmatch
+
+code clk_port en_port
+ if (first->type.in($_DFF_N_, $_DFF_P_, $_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_, \FDRE, \FDRE_1))
+ clk_port = \C;
+ else log_abort();
+ if (first->type.in($_DFF_N_, $_DFF_P_))
+ en_port = IdString();
+ else if (first->type.in($_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_))
+ en_port = \E;
+ else if (first->type.in(\FDRE, \FDRE_1))
+ en_port = \CE;
+ else log_abort();
+
+ longest_chain.clear();
+ chain.push_back(first);
+ subpattern(tail);
+finally
+ chain.pop_back();
+ log_assert(chain.empty());
+ if (GetSize(longest_chain) >= minlen)
+ accept;
+endcode
+
+// ------------------------------------------------------------------
+
+subpattern setup
+arg clk_port
+arg en_port
+
+match first
+ select first->type.in($_DFF_N_, $_DFF_P_, $_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_, \FDRE, \FDRE_1)
+ select !first->has_keep_attr()
+ select !first->type.in(\FDRE) || !param(first, \IS_R_INVERTED, State::S0).as_bool()
+ select !first->type.in(\FDRE) || !param(first, \IS_D_INVERTED, State::S0).as_bool()
+ select !first->type.in(\FDRE, \FDRE_1) || port(first, \R, State::S0).is_fully_zero()
+endmatch
+
+code clk_port en_port
+ if (first->type.in($_DFF_N_, $_DFF_P_, $_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_, \FDRE, \FDRE_1))
+ clk_port = \C;
+ else log_abort();
+ if (first->type.in($_DFF_N_, $_DFF_P_))
+ en_port = IdString();
+ else if (first->type.in($_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_))
+ en_port = \E;
+ else if (first->type.in(\FDRE, \FDRE_1))
+ en_port = \CE;
+ else log_abort();
+endcode
+
+match next
+ select next->type.in($_DFF_N_, $_DFF_P_, $_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_, \FDRE, \FDRE_1)
+ select !next->has_keep_attr()
+ select port(next, \D)[0].wire && !port(next, \D)[0].wire->get_bool_attribute(\keep)
+ select nusers(port(next, \Q)) == 2
+ index <IdString> next->type === first->type
+ index <SigBit> port(next, \Q) === port(first, \D)
+ filter port(next, clk_port) == port(first, clk_port)
+ filter en_port == IdString() || port(next, en_port) == port(first, en_port)
+ filter !first->type.in(\FDRE) || param(next, \IS_C_INVERTED, State::S0).as_bool() == param(first, \IS_C_INVERTED, State::S0).as_bool()
+ filter !first->type.in(\FDRE) || param(next, \IS_D_INVERTED, State::S0).as_bool() == param(first, \IS_D_INVERTED, State::S0).as_bool()
+ filter !first->type.in(\FDRE) || param(next, \IS_R_INVERTED, State::S0).as_bool() == param(first, \IS_R_INVERTED, State::S0).as_bool()
+ filter !first->type.in(\FDRE, \FDRE_1) || port(next, \R, State::S0).is_fully_zero()
+endmatch
+
+code
+ non_first_cells.insert(next);
+endcode
+
+// ------------------------------------------------------------------
+
+subpattern tail
+arg first
+arg clk_port
+arg en_port
+
+match next
+ semioptional
+ select next->type.in($_DFF_N_, $_DFF_P_, $_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_, \FDRE, \FDRE_1)
+ select !next->has_keep_attr()
+ select port(next, \D)[0].wire && !port(next, \D)[0].wire->get_bool_attribute(\keep)
+ select nusers(port(next, \Q)) == 2
+ index <IdString> next->type === chain.back()->type
+ index <SigBit> port(next, \Q) === port(chain.back(), \D)
+ filter port(next, clk_port) == port(first, clk_port)
+ filter en_port == IdString() || port(next, en_port) == port(first, en_port)
+ filter !first->type.in(\FDRE) || param(next, \IS_C_INVERTED, State::S0).as_bool() == param(first, \IS_C_INVERTED, State::S0).as_bool()
+ filter !first->type.in(\FDRE) || param(next, \IS_D_INVERTED, State::S0).as_bool() == param(first, \IS_D_INVERTED, State::S0).as_bool()
+ filter !first->type.in(\FDRE) || param(next, \IS_R_INVERTED, State::S0).as_bool() == param(first, \IS_R_INVERTED, State::S0).as_bool()
+ filter !first->type.in(\FDRE, \FDRE_1) || port(next, \R, State::S0).is_fully_zero()
+generate
+ Cell *cell = module->addCell(NEW_ID, chain.back()->type);
+ cell->setPort(\C, chain.back()->getPort(\C));
+ cell->setPort(\D, module->addWire(NEW_ID));
+ cell->setPort(\Q, chain.back()->getPort(\D));
+ if (cell->type == \FDRE) {
+ if (rng(2) == 0)
+ cell->setPort(\R, port(chain.back(), \R, State::S0));
+ cell->setPort(\CE, chain.back()->getPort(\CE));
+ }
+ else if (cell->type.begins_with("$_DFFE_"))
+ cell->setPort(\E, chain.back()->getPort(\E));
+endmatch
+
+code
+ if (next) {
+ chain.push_back(next);
+ subpattern(tail);
+ } else {
+ if (GetSize(chain) > GetSize(longest_chain))
+ longest_chain = chain;
+ }
+finally
+ if (next)
+ chain.pop_back();
+endcode
+
+// -----------
+
+pattern variable
+
+state <IdString> clk_port en_port
+state <int> shiftx_width
+state <int> slice
+udata <int> minlen
+udata <vector<pair<Cell*,int>>> chain
+udata <pool<SigBit>> chain_bits
+
+code
+ chain_bits.clear();
+endcode
+
+match shiftx
+ select shiftx->type.in($shiftx)
+ select !shiftx->has_keep_attr()
+ select param(shiftx, \Y_WIDTH).as_int() == 1
+ filter param(shiftx, \A_WIDTH).as_int() >= minlen
+generate
+ minlen = 3;
+ module->addShiftx(NEW_ID, module->addWire(NEW_ID, rng(6)+minlen), module->addWire(NEW_ID, 3), module->addWire(NEW_ID));
+endmatch
+
+code shiftx_width
+ shiftx_width = param(shiftx, \A_WIDTH).as_int();
+endcode
+
+match first
+ select first->type.in($_DFF_N_, $_DFF_P_, $_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_, $dff, $dffe)
+ select !first->has_keep_attr()
+ select port(first, \Q)[0].wire && !port(first, \Q)[0].wire->get_bool_attribute(\keep)
+ slice idx GetSize(port(first, \Q))
+ select nusers(port(first, \Q)[idx]) <= 2
+ index <SigBit> port(first, \Q)[idx] === port(shiftx, \A)[shiftx_width-1]
+ set slice idx
+generate
+ SigSpec C = module->addWire(NEW_ID);
+ auto WIDTH = rng(3)+1;
+ SigSpec D = module->addWire(NEW_ID, WIDTH);
+ SigSpec Q = module->addWire(NEW_ID, WIDTH);
+ auto r = rng(8);
+ Cell *cell = nullptr;
+ switch (r)
+ {
+ case 0:
+ case 1:
+ cell = module->addDff(NEW_ID, C, D, Q, r & 1);
+ break;
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ //cell = module->addDffe(NEW_ID, C, module->addWire(NEW_ID), D, Q, r & 1, r & 4);
+ //break;
+ case 6:
+ case 7:
+ WIDTH = 1;
+ cell = module->addDffGate(NEW_ID, C, D[0], Q[0], r & 1);
+ break;
+ default: log_abort();
+ }
+ shiftx->connections_.at(\A)[shiftx_width-1] = port(cell, \Q)[rng(WIDTH)];
+endmatch
+
+code clk_port en_port
+ if (first->type.in($_DFF_N_, $_DFF_P_, $_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_))
+ clk_port = \C;
+ else if (first->type.in($dff, $dffe))
+ clk_port = \CLK;
+ else log_abort();
+ if (first->type.in($_DFF_N_, $_DFF_P_, $dff))
+ en_port = IdString();
+ else if (first->type.in($_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_))
+ en_port = \E;
+ else if (first->type.in($dffe))
+ en_port = \EN;
+ else log_abort();
+
+ chain_bits.insert(port(first, \Q)[slice]);
+ chain.emplace_back(first, slice);
+ subpattern(tail);
+finally
+ if (GetSize(chain) == shiftx_width)
+ accept;
+ chain.clear();
+endcode
+
+// ------------------------------------------------------------------
+
+subpattern tail
+arg first
+arg shiftx
+arg shiftx_width
+arg slice
+arg clk_port
+arg en_port
+
+match next
+ semioptional
+ select next->type.in($_DFF_N_, $_DFF_P_, $_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_, $dff, $dffe)
+ select !next->has_keep_attr()
+ select port(next, \D)[0].wire && !port(next, \D)[0].wire->get_bool_attribute(\keep)
+ slice idx GetSize(port(next, \Q))
+ select nusers(port(next, \Q)[idx]) <= 3
+ index <IdString> next->type === chain.back().first->type
+ index <SigBit> port(next, \Q)[idx] === port(chain.back().first, \D)[chain.back().second]
+ index <SigBit> port(next, \Q)[idx] === port(shiftx, \A)[shiftx_width-1-GetSize(chain)]
+ filter port(next, clk_port) == port(first, clk_port)
+ filter en_port == IdString() || port(next, en_port) == port(first, en_port)
+ filter !next->type.in($dff, $dffe) || param(next, \CLK_POLARITY).as_bool() == param(first, \CLK_POLARITY).as_bool()
+ filter !next->type.in($dffe) || param(next, \EN_POLARITY).as_bool() == param(first, \EN_POLARITY).as_bool()
+ filter !chain_bits.count(port(next, \D)[idx])
+ set slice idx
+generate
+ if (GetSize(chain) < shiftx_width) {
+ auto back = chain.back().first;
+ auto slice = chain.back().second;
+ if (back->type.in($dff, $dffe)) {
+ auto WIDTH = GetSize(port(back, \D));
+ if (rng(2) == 0 && slice < WIDTH-1) {
+ auto new_slice = slice + rng(WIDTH-1-slice);
+ back->connections_.at(\D)[slice] = port(back, \Q)[new_slice];
+ }
+ else {
+ auto D = module->addWire(NEW_ID, WIDTH);
+ if (back->type == $dff)
+ module->addDff(NEW_ID, port(back, \CLK), D, port(back, \D), param(back, \CLK_POLARITY).as_bool());
+ else if (back->type == $dffe)
+ module->addDffe(NEW_ID, port(back, \CLK), port(back, \EN), D, port(back, \D), param(back, \CLK_POLARITY).as_bool(), param(back, \EN_POLARITY).as_bool());
+ else
+ log_abort();
+ }
+ }
+ else if (back->type.begins_with("$_DFF_")) {
+ Cell *cell = module->addCell(NEW_ID, back->type);
+ cell->setPort(\C, back->getPort(\C));
+ cell->setPort(\D, module->addWire(NEW_ID));
+ cell->setPort(\Q, back->getPort(\D));
+ }
+ else
+ log_abort();
+ shiftx->connections_.at(\A)[shiftx_width-1-GetSize(chain)] = port(back, \D)[slice];
+ }
+endmatch
+
+code
+ if (next) {
+ chain_bits.insert(port(next, \Q)[slice]);
+ chain.emplace_back(next, slice);
+ if (GetSize(chain) < shiftx_width)
+ subpattern(tail);
+ }
+endcode
diff --git a/passes/proc/Makefile.inc b/passes/proc/Makefile.inc
index 397fe46a1..4b56979f8 100644
--- a/passes/proc/Makefile.inc
+++ b/passes/proc/Makefile.inc
@@ -1,5 +1,6 @@
OBJS += passes/proc/proc.o
+OBJS += passes/proc/proc_prune.o
OBJS += passes/proc/proc_clean.o
OBJS += passes/proc/proc_rmdead.o
OBJS += passes/proc/proc_init.o
@@ -7,4 +8,3 @@ OBJS += passes/proc/proc_arst.o
OBJS += passes/proc/proc_mux.o
OBJS += passes/proc/proc_dlatch.o
OBJS += passes/proc/proc_dff.o
-
diff --git a/passes/proc/proc.cc b/passes/proc/proc.cc
index d5366f266..a5b4a3112 100644
--- a/passes/proc/proc.cc
+++ b/passes/proc/proc.cc
@@ -27,7 +27,7 @@ PRIVATE_NAMESPACE_BEGIN
struct ProcPass : public Pass {
ProcPass() : Pass("proc", "translate processes to netlists") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -37,6 +37,7 @@ struct ProcPass : public Pass {
log("\n");
log(" proc_clean\n");
log(" proc_rmdead\n");
+ log(" proc_prune\n");
log(" proc_init\n");
log(" proc_arst\n");
log(" proc_mux\n");
@@ -57,7 +58,7 @@ struct ProcPass : public Pass {
log(" executed in -ifx mode.\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
std::string global_arst;
bool ifxmode = false;
@@ -83,6 +84,7 @@ struct ProcPass : public Pass {
Pass::call(design, "proc_clean");
if (!ifxmode)
Pass::call(design, "proc_rmdead");
+ Pass::call(design, "proc_prune");
Pass::call(design, "proc_init");
if (global_arst.empty())
Pass::call(design, "proc_arst");
diff --git a/passes/proc/proc_arst.cc b/passes/proc/proc_arst.cc
index 216b00ddd..c606deb88 100644
--- a/passes/proc/proc_arst.cc
+++ b/passes/proc/proc_arst.cc
@@ -55,7 +55,7 @@ bool check_signal(RTLIL::Module *mod, RTLIL::SigSpec signal, RTLIL::SigSpec ref,
return check_signal(mod, cell->getPort("\\A"), ref, polarity);
}
- if ((cell->type == "$eq" || cell->type == "$eqx") && cell->getPort("\\Y") == signal) {
+ if (cell->type.in("$eq", "$eqx") && cell->getPort("\\Y") == signal) {
if (cell->getPort("\\A").is_fully_const()) {
if (!cell->getPort("\\A").as_bool())
polarity = !polarity;
@@ -68,7 +68,7 @@ bool check_signal(RTLIL::Module *mod, RTLIL::SigSpec signal, RTLIL::SigSpec ref,
}
}
- if ((cell->type == "$ne" || cell->type == "$nex") && cell->getPort("\\Y") == signal) {
+ if (cell->type.in("$ne", "$nex") && cell->getPort("\\Y") == signal) {
if (cell->getPort("\\A").is_fully_const()) {
if (cell->getPort("\\A").as_bool())
polarity = !polarity;
@@ -172,7 +172,7 @@ restart_proc_arst:
sync->type = sync->type == RTLIL::SyncType::STp ? RTLIL::SyncType::ST1 : RTLIL::SyncType::ST0;
}
for (auto &action : sync->actions) {
- RTLIL::SigSpec rspec = action.second;
+ RTLIL::SigSpec rspec = assign_map(action.second);
RTLIL::SigSpec rval = RTLIL::SigSpec(RTLIL::State::Sm, rspec.size());
for (int i = 0; i < GetSize(rspec); i++)
if (rspec[i].wire == NULL)
@@ -203,7 +203,7 @@ restart_proc_arst:
struct ProcArstPass : public Pass {
ProcArstPass() : Pass("proc_arst", "detect asynchronous resets") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -221,7 +221,7 @@ struct ProcArstPass : public Pass {
log(" in the 'init' attribute on the net.\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
std::string global_arst;
bool global_arst_neg = false;
diff --git a/passes/proc/proc_clean.cc b/passes/proc/proc_clean.cc
index 7dbabc211..114c6ab03 100644
--- a/passes/proc/proc_clean.cc
+++ b/passes/proc/proc_clean.cc
@@ -69,26 +69,49 @@ void proc_clean_switch(RTLIL::SwitchRule *sw, RTLIL::CaseRule *parent, bool &did
did_something = true;
for (auto &action : sw->cases[0]->actions)
parent->actions.push_back(action);
- for (auto sw2 : sw->cases[0]->switches)
- parent->switches.push_back(sw2);
+ parent->switches.insert(parent->switches.begin(), sw->cases[0]->switches.begin(), sw->cases[0]->switches.end());
sw->cases[0]->switches.clear();
delete sw->cases[0];
sw->cases.clear();
}
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 +129,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;
@@ -119,7 +142,7 @@ void proc_clean_case(RTLIL::CaseRule *cs, bool &did_something, int &count, int m
YOSYS_NAMESPACE_END
PRIVATE_NAMESPACE_BEGIN
-void proc_clean(RTLIL::Module *mod, RTLIL::Process *proc, int &total_count)
+void proc_clean(RTLIL::Module *mod, RTLIL::Process *proc, int &total_count, bool quiet)
{
int count = 0;
bool did_something = true;
@@ -136,29 +159,43 @@ void proc_clean(RTLIL::Module *mod, RTLIL::Process *proc, int &total_count)
did_something = false;
proc_clean_case(&proc->root_case, did_something, count, -1);
}
- if (count > 0)
+ if (count > 0 && !quiet)
log("Found and cleaned up %d empty switch%s in `%s.%s'.\n", count, count == 1 ? "" : "es", mod->name.c_str(), proc->name.c_str());
total_count += count;
}
struct ProcCleanPass : public Pass {
ProcCleanPass() : Pass("proc_clean", "remove empty parts of processes") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
- log(" proc_clean [selection]\n");
+ log(" proc_clean [options] [selection]\n");
+ log("\n");
+ log(" -quiet\n");
+ log(" do not print any messages.\n");
log("\n");
log("This pass removes empty parts of processes and ultimately removes a process\n");
log("if it contains only empty structures.\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
int total_count = 0;
- log_header(design, "Executing PROC_CLEAN pass (remove empty switches from decision trees).\n");
+ bool quiet = false;
+
+ if (find(args.begin(), args.end(), "-quiet") == args.end())
+ log_header(design, "Executing PROC_CLEAN pass (remove empty switches from decision trees).\n");
- extra_args(args, 1, design);
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ if (args[argidx] == "-quiet") {
+ quiet = true;
+ continue;
+ }
+ }
+ extra_args(args, argidx, design);
for (auto mod : design->modules()) {
std::vector<RTLIL::IdString> delme;
@@ -167,10 +204,11 @@ struct ProcCleanPass : public Pass {
for (auto &proc_it : mod->processes) {
if (!design->selected(mod, proc_it.second))
continue;
- proc_clean(mod, proc_it.second, total_count);
+ proc_clean(mod, proc_it.second, total_count, quiet);
if (proc_it.second->syncs.size() == 0 && proc_it.second->root_case.switches.size() == 0 &&
proc_it.second->root_case.actions.size() == 0) {
- log("Removing empty process `%s.%s'.\n", log_id(mod), proc_it.second->name.c_str());
+ if (!quiet)
+ log("Removing empty process `%s.%s'.\n", log_id(mod), proc_it.second->name.c_str());
delme.push_back(proc_it.first);
}
}
@@ -180,7 +218,8 @@ struct ProcCleanPass : public Pass {
}
}
- log("Cleaned up %d empty switch%s.\n", total_count, total_count == 1 ? "" : "es");
+ if (!quiet)
+ log("Cleaned up %d empty switch%s.\n", total_count, total_count == 1 ? "" : "es");
}
} ProcCleanPass;
diff --git a/passes/proc/proc_dff.cc b/passes/proc/proc_dff.cc
index 98653dc6b..519d35cd6 100644
--- a/passes/proc/proc_dff.cc
+++ b/passes/proc/proc_dff.cc
@@ -322,6 +322,7 @@ void proc_dff(RTLIL::Module *mod, RTLIL::Process *proc, ConstEval &ce)
}
}
+ SigSpec sig_q = sig;
ce.assign_map.apply(insig);
ce.assign_map.apply(rstval);
ce.assign_map.apply(sig);
@@ -350,13 +351,13 @@ void proc_dff(RTLIL::Module *mod, RTLIL::Process *proc, ConstEval &ce)
else if (!rstval.is_fully_const() && !ce.eval(rstval))
{
log_warning("Async reset value `%s' is not constant!\n", log_signal(rstval));
- gen_dffsr(mod, insig, rstval, sig,
+ gen_dffsr(mod, insig, rstval, sig_q,
sync_edge->type == RTLIL::SyncType::STp,
sync_level && sync_level->type == RTLIL::SyncType::ST1,
sync_edge->signal, sync_level->signal, proc);
}
else
- gen_dff(mod, insig, rstval.as_const(), sig,
+ gen_dff(mod, insig, rstval.as_const(), sig_q,
sync_edge && sync_edge->type == RTLIL::SyncType::STp,
sync_level && sync_level->type == RTLIL::SyncType::ST1,
sync_edge ? sync_edge->signal : SigSpec(),
@@ -369,7 +370,7 @@ void proc_dff(RTLIL::Module *mod, RTLIL::Process *proc, ConstEval &ce)
struct ProcDffPass : public Pass {
ProcDffPass() : Pass("proc_dff", "extract flip-flops from processes") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -379,7 +380,7 @@ struct ProcDffPass : public Pass {
log("d-type flip-flop cells.\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
log_header(design, "Executing PROC_DFF pass (convert process syncs to FFs).\n");
diff --git a/passes/proc/proc_dlatch.cc b/passes/proc/proc_dlatch.cc
index 15200ec12..a0c8351b6 100644
--- a/passes/proc/proc_dlatch.cc
+++ b/passes/proc/proc_dlatch.cc
@@ -349,6 +349,10 @@ void proc_dlatch(proc_dlatch_db_t &db, RTLIL::Process *proc)
continue;
}
+ if (proc->get_bool_attribute(ID(always_ff)))
+ log_error("Found non edge/level sensitive event in always_ff process `%s.%s'.\n",
+ db.module->name.c_str(), proc->name.c_str());
+
for (auto ss : sr->actions)
{
db.sigmap.apply(ss.first);
@@ -383,8 +387,12 @@ void proc_dlatch(proc_dlatch_db_t &db, RTLIL::Process *proc)
int offset = 0;
for (auto chunk : nolatches_bits.first.chunks()) {
SigSpec lhs = chunk, rhs = nolatches_bits.second.extract(offset, chunk.width);
- log("No latch inferred for signal `%s.%s' from process `%s.%s'.\n",
- db.module->name.c_str(), log_signal(lhs), db.module->name.c_str(), proc->name.c_str());
+ if (proc->get_bool_attribute(ID(always_latch)))
+ log_error("No latch inferred for signal `%s.%s' from always_latch process `%s.%s'.\n",
+ db.module->name.c_str(), log_signal(lhs), db.module->name.c_str(), proc->name.c_str());
+ else
+ log("No latch inferred for signal `%s.%s' from process `%s.%s'.\n",
+ db.module->name.c_str(), log_signal(lhs), db.module->name.c_str(), proc->name.c_str());
db.module->connect(lhs, rhs);
offset += chunk.width;
}
@@ -410,8 +418,12 @@ void proc_dlatch(proc_dlatch_db_t &db, RTLIL::Process *proc)
cell->set_src_attribute(src);
db.generated_dlatches.insert(cell);
- log("Latch inferred for signal `%s.%s' from process `%s.%s': %s\n",
- db.module->name.c_str(), log_signal(lhs), db.module->name.c_str(), proc->name.c_str(), log_id(cell));
+ if (proc->get_bool_attribute(ID(always_comb)))
+ log_error("Latch inferred for signal `%s.%s' from always_comb process `%s.%s'.\n",
+ db.module->name.c_str(), log_signal(lhs), db.module->name.c_str(), proc->name.c_str());
+ else
+ log("Latch inferred for signal `%s.%s' from process `%s.%s': %s\n",
+ db.module->name.c_str(), log_signal(lhs), db.module->name.c_str(), proc->name.c_str(), log_id(cell));
}
offset += width;
@@ -422,7 +434,7 @@ void proc_dlatch(proc_dlatch_db_t &db, RTLIL::Process *proc)
struct ProcDlatchPass : public Pass {
ProcDlatchPass() : Pass("proc_dlatch", "extract latches from processes") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -432,7 +444,7 @@ struct ProcDlatchPass : public Pass {
log("d-type latches.\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
log_header(design, "Executing PROC_DLATCH pass (convert process syncs to latches).\n");
diff --git a/passes/proc/proc_init.cc b/passes/proc/proc_init.cc
index 0c8fb83dc..462a384b7 100644
--- a/passes/proc/proc_init.cc
+++ b/passes/proc/proc_init.cc
@@ -26,21 +26,7 @@
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
-void proc_get_const(RTLIL::SigSpec &sig, RTLIL::CaseRule &rule)
-{
- log_assert(rule.compare.size() == 0);
-
- while (1) {
- RTLIL::SigSpec tmp = sig;
- for (auto &it : rule.actions)
- tmp.replace(it.first, it.second);
- if (tmp == sig)
- break;
- sig = tmp;
- }
-}
-
-void proc_init(RTLIL::Module *mod, RTLIL::Process *proc)
+void proc_init(RTLIL::Module *mod, SigMap &sigmap, RTLIL::Process *proc)
{
bool found_init = false;
@@ -53,9 +39,7 @@ void proc_init(RTLIL::Module *mod, RTLIL::Process *proc)
for (auto &action : sync->actions)
{
RTLIL::SigSpec lhs = action.first;
- RTLIL::SigSpec rhs = action.second;
-
- proc_get_const(rhs, proc->root_case);
+ RTLIL::SigSpec rhs = sigmap(action.second);
if (!rhs.is_fully_const())
log_cmd_error("Failed to get a constant init value for %s: %s\n", log_signal(lhs), log_signal(rhs));
@@ -102,7 +86,7 @@ void proc_init(RTLIL::Module *mod, RTLIL::Process *proc)
struct ProcInitPass : public Pass {
ProcInitPass() : Pass("proc_init", "convert initial block to init attributes") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -113,17 +97,19 @@ struct ProcInitPass : public Pass {
log("respective wire.\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
log_header(design, "Executing PROC_INIT pass (extract init attributes).\n");
extra_args(args, 1, design);
for (auto mod : design->modules())
- if (design->selected(mod))
+ if (design->selected(mod)) {
+ SigMap sigmap(mod);
for (auto &proc_it : mod->processes)
if (design->selected(mod, proc_it.second))
- proc_init(mod, proc_it.second);
+ proc_init(mod, sigmap, proc_it.second);
+ }
}
} ProcInitPass;
diff --git a/passes/proc/proc_mux.cc b/passes/proc/proc_mux.cc
index 57e131ca5..d029282fd 100644
--- a/passes/proc/proc_mux.cc
+++ b/passes/proc/proc_mux.cc
@@ -108,6 +108,7 @@ struct SigSnippets
struct SnippetSwCache
{
+ dict<RTLIL::SwitchRule*, pool<RTLIL::SigBit>, hash_ptr_ops> full_case_bits_cache;
dict<RTLIL::SwitchRule*, pool<int>, hash_ptr_ops> cache;
const SigSnippets *snippets;
int current_snippet;
@@ -143,7 +144,13 @@ struct SnippetSwCache
}
};
-RTLIL::SigSpec gen_cmp(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::vector<RTLIL::SigSpec> &compare, RTLIL::SwitchRule *sw, bool ifxmode)
+void apply_attrs(RTLIL::Cell *cell, const RTLIL::SwitchRule *sw, const RTLIL::CaseRule *cs)
+{
+ cell->attributes = sw->attributes;
+ cell->add_strpool_attribute("\\src", cs->get_strpool_attribute("\\src"));
+}
+
+RTLIL::SigSpec gen_cmp(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::vector<RTLIL::SigSpec> &compare, RTLIL::SwitchRule *sw, RTLIL::CaseRule *cs, bool ifxmode)
{
std::stringstream sstr;
sstr << "$procmux$" << (autoidx++);
@@ -172,7 +179,7 @@ RTLIL::SigSpec gen_cmp(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const s
{
// create compare cell
RTLIL::Cell *eq_cell = mod->addCell(stringf("%s_CMP%d", sstr.str().c_str(), cmp_wire->width), ifxmode ? "$eqx" : "$eq");
- eq_cell->attributes = sw->attributes;
+ apply_attrs(eq_cell, sw, cs);
eq_cell->parameters["\\A_SIGNED"] = RTLIL::Const(0);
eq_cell->parameters["\\B_SIGNED"] = RTLIL::Const(0);
@@ -198,7 +205,7 @@ RTLIL::SigSpec gen_cmp(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const s
// reduce cmp vector to one logic signal
RTLIL::Cell *any_cell = mod->addCell(sstr.str() + "_ANY", "$reduce_or");
- any_cell->attributes = sw->attributes;
+ apply_attrs(any_cell, sw, cs);
any_cell->parameters["\\A_SIGNED"] = RTLIL::Const(0);
any_cell->parameters["\\A_WIDTH"] = RTLIL::Const(cmp_wire->width);
@@ -211,7 +218,7 @@ RTLIL::SigSpec gen_cmp(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const s
return RTLIL::SigSpec(ctrl_wire);
}
-RTLIL::SigSpec gen_mux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::vector<RTLIL::SigSpec> &compare, RTLIL::SigSpec when_signal, RTLIL::SigSpec else_signal, RTLIL::Cell *&last_mux_cell, RTLIL::SwitchRule *sw, bool ifxmode)
+RTLIL::SigSpec gen_mux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::vector<RTLIL::SigSpec> &compare, RTLIL::SigSpec when_signal, RTLIL::SigSpec else_signal, RTLIL::Cell *&last_mux_cell, RTLIL::SwitchRule *sw, RTLIL::CaseRule *cs, bool ifxmode)
{
log_assert(when_signal.size() == else_signal.size());
@@ -223,7 +230,7 @@ RTLIL::SigSpec gen_mux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const s
return when_signal;
// compare results
- RTLIL::SigSpec ctrl_sig = gen_cmp(mod, signal, compare, sw, ifxmode);
+ RTLIL::SigSpec ctrl_sig = gen_cmp(mod, signal, compare, sw, cs, ifxmode);
if (ctrl_sig.size() == 0)
return when_signal;
log_assert(ctrl_sig.size() == 1);
@@ -233,7 +240,7 @@ RTLIL::SigSpec gen_mux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const s
// create the multiplexer itself
RTLIL::Cell *mux_cell = mod->addCell(sstr.str(), "$mux");
- mux_cell->attributes = sw->attributes;
+ apply_attrs(mux_cell, sw, cs);
mux_cell->parameters["\\WIDTH"] = RTLIL::Const(when_signal.size());
mux_cell->setPort("\\A", else_signal);
@@ -245,7 +252,7 @@ RTLIL::SigSpec gen_mux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const s
return RTLIL::SigSpec(result_wire);
}
-void append_pmux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::vector<RTLIL::SigSpec> &compare, RTLIL::SigSpec when_signal, RTLIL::Cell *last_mux_cell, RTLIL::SwitchRule *sw, bool ifxmode)
+void append_pmux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::vector<RTLIL::SigSpec> &compare, RTLIL::SigSpec when_signal, RTLIL::Cell *last_mux_cell, RTLIL::SwitchRule *sw, RTLIL::CaseRule *cs, bool ifxmode)
{
log_assert(last_mux_cell != NULL);
log_assert(when_signal.size() == last_mux_cell->getPort("\\A").size());
@@ -253,7 +260,7 @@ void append_pmux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::ve
if (when_signal == last_mux_cell->getPort("\\A"))
return;
- RTLIL::SigSpec ctrl_sig = gen_cmp(mod, signal, compare, sw, ifxmode);
+ RTLIL::SigSpec ctrl_sig = gen_cmp(mod, signal, compare, sw, cs, ifxmode);
log_assert(ctrl_sig.size() == 1);
last_mux_cell->type = "$pmux";
@@ -268,6 +275,49 @@ void append_pmux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::ve
last_mux_cell->parameters["\\S_WIDTH"] = last_mux_cell->getPort("\\S").size();
}
+const pool<SigBit> &get_full_case_bits(SnippetSwCache &swcache, RTLIL::SwitchRule *sw)
+{
+ if (!swcache.full_case_bits_cache.count(sw))
+ {
+ pool<SigBit> bits;
+
+ if (sw->get_bool_attribute("\\full_case"))
+ {
+ bool first_case = true;
+
+ for (auto cs : sw->cases)
+ {
+ pool<SigBit> case_bits;
+
+ for (auto it : cs->actions) {
+ for (auto bit : it.first)
+ case_bits.insert(bit);
+ }
+
+ for (auto it : cs->switches) {
+ for (auto bit : get_full_case_bits(swcache, it))
+ case_bits.insert(bit);
+ }
+
+ if (first_case) {
+ first_case = false;
+ bits = case_bits;
+ } else {
+ pool<SigBit> new_bits;
+ for (auto bit : bits)
+ if (case_bits.count(bit))
+ new_bits.insert(bit);
+ bits.swap(new_bits);
+ }
+ }
+ }
+
+ bits.swap(swcache.full_case_bits_cache[sw]);
+ }
+
+ return swcache.full_case_bits_cache.at(sw);
+}
+
RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, SnippetSwCache &swcache, dict<RTLIL::SwitchRule*, bool, hash_ptr_ops> &swpara,
RTLIL::CaseRule *cs, const RTLIL::SigSpec &sig, const RTLIL::SigSpec &defval, bool ifxmode)
{
@@ -337,6 +387,12 @@ RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, SnippetSwCache &swcache, d
}
}
+ // mask default bits that are irrelevant because the output is driven by a full case
+ const pool<SigBit> &full_case_bits = get_full_case_bits(swcache, sw);
+ for (int i = 0; i < GetSize(sig); i++)
+ if (full_case_bits.count(sig[i]))
+ result[i] = State::Sx;
+
// evaluate in reverse order to give the first entry the top priority
RTLIL::SigSpec initial_val = result;
RTLIL::Cell *last_mux_cell = NULL;
@@ -345,9 +401,9 @@ RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, SnippetSwCache &swcache, d
RTLIL::CaseRule *cs2 = sw->cases[case_idx];
RTLIL::SigSpec value = signal_to_mux_tree(mod, swcache, swpara, cs2, sig, initial_val, ifxmode);
if (last_mux_cell && pgroups[case_idx] == pgroups[case_idx+1])
- append_pmux(mod, sw->signal, cs2->compare, value, last_mux_cell, sw, ifxmode);
+ append_pmux(mod, sw->signal, cs2->compare, value, last_mux_cell, sw, cs2, ifxmode);
else
- result = gen_mux(mod, sw->signal, cs2->compare, value, result, last_mux_cell, sw, ifxmode);
+ result = gen_mux(mod, sw->signal, cs2->compare, value, result, last_mux_cell, sw, cs2, ifxmode);
}
}
@@ -382,7 +438,7 @@ void proc_mux(RTLIL::Module *mod, RTLIL::Process *proc, bool ifxmode)
struct ProcMuxPass : public Pass {
ProcMuxPass() : Pass("proc_mux", "convert decision trees to multiplexers") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -396,7 +452,7 @@ struct ProcMuxPass : public Pass {
log(" 'case' expressions and 'if' conditions.\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
bool ifxmode = false;
log_header(design, "Executing PROC_MUX pass (convert decision trees to multiplexers).\n");
diff --git a/passes/proc/proc_prune.cc b/passes/proc/proc_prune.cc
new file mode 100644
index 000000000..d4aee9df0
--- /dev/null
+++ b/passes/proc/proc_prune.cc
@@ -0,0 +1,165 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2019 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"
+#include "kernel/sigtools.h"
+#include "kernel/log.h"
+#include <stdlib.h>
+#include <stdio.h>
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct PruneWorker
+{
+ RTLIL::Module *module;
+ SigMap sigmap;
+
+ int removed_count = 0, promoted_count = 0;
+
+ PruneWorker(RTLIL::Module *mod) : module(mod), sigmap(mod) {}
+
+ pool<RTLIL::SigBit> do_switch(RTLIL::SwitchRule *sw, pool<RTLIL::SigBit> assigned, pool<RTLIL::SigBit> &affected)
+ {
+ pool<RTLIL::SigBit> all_assigned;
+ bool full_case = sw->get_bool_attribute("\\full_case");
+ bool first = true;
+ for (auto it : sw->cases) {
+ if (it->compare.empty())
+ full_case = true;
+ pool<RTLIL::SigBit> case_assigned = do_case(it, assigned, affected);
+ if (first) {
+ first = false;
+ all_assigned = case_assigned;
+ } else {
+ for (auto &bit : all_assigned)
+ if (!case_assigned[bit])
+ all_assigned.erase(bit);
+ }
+ }
+ if (full_case)
+ assigned.insert(all_assigned.begin(), all_assigned.end());
+ return assigned;
+ }
+
+ pool<RTLIL::SigBit> do_case(RTLIL::CaseRule *cs, pool<RTLIL::SigBit> assigned, pool<RTLIL::SigBit> &affected,
+ bool root = false)
+ {
+ for (auto it = cs->switches.rbegin(); it != cs->switches.rend(); ++it) {
+ pool<RTLIL::SigBit> sw_assigned = do_switch((*it), assigned, affected);
+ assigned.insert(sw_assigned.begin(), sw_assigned.end());
+ }
+ for (auto it = cs->actions.rbegin(); it != cs->actions.rend(); ) {
+ RTLIL::SigSpec lhs = sigmap(it->first);
+ bool redundant = true;
+ for (auto &bit : lhs) {
+ if (bit.wire && !assigned[bit]) {
+ redundant = false;
+ break;
+ }
+ }
+ bool remove = false;
+ if (redundant) {
+ removed_count++;
+ remove = true;
+ } else {
+ if (root) {
+ bool promotable = true;
+ for (auto &bit : lhs) {
+ if (bit.wire && affected[bit] && !assigned[bit]) {
+ promotable = false;
+ break;
+ }
+ }
+ if (promotable) {
+ RTLIL::SigSpec rhs = sigmap(it->second);
+ RTLIL::SigSig conn;
+ for (int i = 0; i < GetSize(lhs); i++) {
+ RTLIL::SigBit lhs_bit = lhs[i];
+ if (lhs_bit.wire && !assigned[lhs_bit]) {
+ conn.first.append_bit(lhs_bit);
+ conn.second.append(rhs.extract(i));
+ }
+ }
+ promoted_count++;
+ module->connect(conn);
+ remove = true;
+ }
+ }
+ for (auto &bit : lhs)
+ if (bit.wire)
+ assigned.insert(bit);
+ for (auto &bit : lhs)
+ if (bit.wire)
+ affected.insert(bit);
+ }
+ if (remove)
+ cs->actions.erase((it++).base() - 1);
+ else it++;
+ }
+ return assigned;
+ }
+
+ void do_process(RTLIL::Process *pr)
+ {
+ pool<RTLIL::SigBit> affected;
+ do_case(&pr->root_case, {}, affected, /*root=*/true);
+ }
+};
+
+struct ProcPrunePass : public Pass {
+ ProcPrunePass() : Pass("proc_prune", "remove redundant assignments") { }
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" proc_prune [selection]\n");
+ log("\n");
+ log("This pass identifies assignments in processes that are always overwritten by\n");
+ log("a later assignment to the same signal and removes them.\n");
+ log("\n");
+ }
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ int total_removed_count = 0, total_promoted_count = 0;
+ log_header(design, "Executing PROC_PRUNE pass (remove redundant assignments in processes).\n");
+
+ extra_args(args, 1, design);
+
+ for (auto mod : design->modules()) {
+ if (!design->selected(mod))
+ continue;
+ PruneWorker worker(mod);
+ for (auto &proc_it : mod->processes) {
+ if (!design->selected(mod, proc_it.second))
+ continue;
+ worker.do_process(proc_it.second);
+ }
+ total_removed_count += worker.removed_count;
+ total_promoted_count += worker.promoted_count;
+ }
+
+ log("Removed %d redundant assignment%s.\n",
+ total_removed_count, total_removed_count == 1 ? "" : "s");
+ log("Promoted %d assignment%s to connection%s.\n",
+ total_promoted_count, total_promoted_count == 1 ? "" : "s", total_promoted_count == 1 ? "" : "s");
+ }
+} ProcPrunePass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/proc/proc_rmdead.cc b/passes/proc/proc_rmdead.cc
index 5672fb475..4f40be446 100644
--- a/passes/proc/proc_rmdead.cc
+++ b/passes/proc/proc_rmdead.cc
@@ -28,7 +28,7 @@
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
-void proc_rmdead(RTLIL::SwitchRule *sw, int &counter)
+void proc_rmdead(RTLIL::SwitchRule *sw, int &counter, int &full_case_counter)
{
BitPatternPool pool(sw->signal);
@@ -56,16 +56,21 @@ void proc_rmdead(RTLIL::SwitchRule *sw, int &counter)
}
for (auto switch_it : sw->cases[i]->switches)
- proc_rmdead(switch_it, counter);
+ proc_rmdead(switch_it, counter, full_case_counter);
if (is_default)
pool.take_all();
}
+
+ if (pool.empty() && !sw->get_bool_attribute("\\full_case")) {
+ sw->set_bool_attribute("\\full_case");
+ full_case_counter++;
+ }
}
struct ProcRmdeadPass : public Pass {
ProcRmdeadPass() : Pass("proc_rmdead", "eliminate dead trees in decision trees") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -74,7 +79,7 @@ struct ProcRmdeadPass : public Pass {
log("This pass identifies unreachable branches in decision trees and removes them.\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
log_header(design, "Executing PROC_RMDEAD pass (remove dead branches from decision trees).\n");
@@ -87,12 +92,15 @@ struct ProcRmdeadPass : public Pass {
for (auto &proc_it : mod->processes) {
if (!design->selected(mod, proc_it.second))
continue;
- int counter = 0;
+ int counter = 0, full_case_counter = 0;
for (auto switch_it : proc_it.second->root_case.switches)
- proc_rmdead(switch_it, counter);
+ proc_rmdead(switch_it, counter, full_case_counter);
if (counter > 0)
log("Removed %d dead cases from process %s in module %s.\n", counter,
- proc_it.first.c_str(), log_id(mod));
+ log_id(proc_it.first), log_id(mod));
+ if (full_case_counter > 0)
+ log("Marked %d switch rules as full_case in process %s in module %s.\n",
+ full_case_counter, log_id(proc_it.first), log_id(mod));
total_counter += counter;
}
}
diff --git a/passes/sat/Makefile.inc b/passes/sat/Makefile.inc
index 4fcce2fad..4bb4b0edc 100644
--- a/passes/sat/Makefile.inc
+++ b/passes/sat/Makefile.inc
@@ -7,4 +7,10 @@ OBJS += passes/sat/miter.o
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
+OBJS += passes/sat/fminit.o
diff --git a/passes/sat/assertpmux.cc b/passes/sat/assertpmux.cc
index 63a907671..3b432c461 100644
--- a/passes/sat/assertpmux.cc
+++ b/passes/sat/assertpmux.cc
@@ -180,8 +180,8 @@ struct AssertpmuxWorker
};
struct AssertpmuxPass : public Pass {
- AssertpmuxPass() : Pass("assertpmux", "convert internal signals to module ports") { }
- virtual void help()
+ AssertpmuxPass() : Pass("assertpmux", "adds asserts for parallel muxes") { }
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -195,11 +195,11 @@ struct AssertpmuxPass : public Pass {
log("\n");
log(" -always\n");
log(" usually the $pmux condition is only checked when the $pmux output\n");
- log(" is used be the mux tree it drives. this option will deactivate this\n");
- log(" additional constrained and check the $pmux condition always.\n");
+ log(" is used by the mux tree it drives. this option will deactivate this\n");
+ log(" additional constraint and check the $pmux condition always.\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
bool flag_noinit = false;
bool flag_always = false;
diff --git a/passes/sat/async2sync.cc b/passes/sat/async2sync.cc
new file mode 100644
index 000000000..740248545
--- /dev/null
+++ b/passes/sat/async2sync.cc
@@ -0,0 +1,232 @@
+/*
+ * 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 Async2syncPass : public Pass {
+ Async2syncPass() : Pass("async2sync", "convert async FF inputs to sync circuits") { }
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" async2sync [options] [selection]\n");
+ log("\n");
+ log("This command replaces async FF inputs with sync circuits emulating the same\n");
+ log("behavior for when the async signals are actually synchronized to the clock.\n");
+ log("\n");
+ log("This pass assumes negative hold time for the async FF inputs. For example when\n");
+ log("a reset deasserts with the clock edge, then the FF output will still drive the\n");
+ 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, $dffsr, and $dlatch cells are supported by this pass.\n");
+ log("\n");
+ }
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ // bool flag_noinit = false;
+
+ log_header(design, "Executing ASYNC2SYNC 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);
+ dict<SigBit, State> initbits;
+ pool<SigBit> del_initbits;
+
+ for (auto wire : module->wires())
+ if (wire->attributes.count("\\init") > 0)
+ {
+ Const initval = wire->attributes.at("\\init");
+ SigSpec initsig = sigmap(wire);
+
+ for (int i = 0; i < GetSize(initval) && i < GetSize(initsig); i++)
+ if (initval[i] == State::S0 || initval[i] == State::S1)
+ initbits[initsig[i]] = initval[i];
+ }
+
+ for (auto cell : vector<Cell*>(module->selected_cells()))
+ {
+ if (cell->type.in("$adff"))
+ {
+ // bool clk_pol = cell->parameters["\\CLK_POLARITY"].as_bool();
+ bool arst_pol = cell->parameters["\\ARST_POLARITY"].as_bool();
+ Const arst_val = cell->parameters["\\ARST_VALUE"];
+
+ // SigSpec sig_clk = cell->getPort("\\CLK");
+ SigSpec sig_arst = cell->getPort("\\ARST");
+ SigSpec sig_d = cell->getPort("\\D");
+ SigSpec sig_q = cell->getPort("\\Q");
+
+ log("Replacing %s.%s (%s): ARST=%s, D=%s, Q=%s\n",
+ log_id(module), log_id(cell), log_id(cell->type),
+ log_signal(sig_arst), 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 (arst_pol) {
+ module->addMux(NEW_ID, sig_d, arst_val, sig_arst, new_d);
+ module->addMux(NEW_ID, new_q, arst_val, sig_arst, sig_q);
+ } else {
+ module->addMux(NEW_ID, arst_val, sig_d, sig_arst, new_d);
+ module->addMux(NEW_ID, arst_val, new_q, sig_arst, sig_q);
+ }
+
+ cell->setPort("\\D", new_d);
+ cell->setPort("\\Q", new_q);
+ cell->unsetPort("\\ARST");
+ cell->unsetParam("\\ARST_POLARITY");
+ cell->unsetParam("\\ARST_VALUE");
+ 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;
+ }
+
+ if (cell->type.in("$dlatch"))
+ {
+ bool en_pol = cell->parameters["\\EN_POLARITY"].as_bool();
+
+ SigSpec sig_en = cell->getPort("\\EN");
+ SigSpec sig_d = cell->getPort("\\D");
+ SigSpec sig_q = cell->getPort("\\Q");
+
+ log("Replacing %s.%s (%s): EN=%s, D=%s, Q=%s\n",
+ log_id(module), log_id(cell), log_id(cell->type),
+ log_signal(sig_en), log_signal(sig_d), log_signal(sig_q));
+
+ 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_q = module->addWire(NEW_ID, GetSize(sig_q));
+ new_q->attributes["\\init"] = init_val;
+
+ if (en_pol) {
+ module->addMux(NEW_ID, new_q, sig_d, sig_en, sig_q);
+ } else {
+ module->addMux(NEW_ID, sig_d, new_q, sig_en, sig_q);
+ }
+
+ cell->setPort("\\D", sig_q);
+ cell->setPort("\\Q", new_q);
+ cell->unsetPort("\\EN");
+ cell->unsetParam("\\EN_POLARITY");
+ cell->type = "$ff";
+ continue;
+ }
+ }
+
+ for (auto wire : module->wires())
+ if (wire->attributes.count("\\init") > 0)
+ {
+ bool delete_initattr = true;
+ Const initval = wire->attributes.at("\\init");
+ SigSpec initsig = sigmap(wire);
+
+ for (int i = 0; i < GetSize(initval) && i < GetSize(initsig); i++)
+ if (del_initbits.count(initsig[i]) > 0)
+ initval[i] = State::Sx;
+ else if (initval[i] != State::Sx)
+ delete_initattr = false;
+
+ if (delete_initattr)
+ wire->attributes.erase("\\init");
+ else
+ wire->attributes.at("\\init") = initval;
+ }
+ }
+ }
+} Async2syncPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/sat/clk2fflogic.cc b/passes/sat/clk2fflogic.cc
index ef6d5dd72..4bb4aa047 100644
--- a/passes/sat/clk2fflogic.cc
+++ b/passes/sat/clk2fflogic.cc
@@ -25,7 +25,7 @@ PRIVATE_NAMESPACE_BEGIN
struct Clk2fflogicPass : public Pass {
Clk2fflogicPass() : Pass("clk2fflogic", "convert clocked FFs to generic $ff cells") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -36,7 +36,7 @@ struct Clk2fflogicPass : public Pass {
log("multiple clocks.\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
// bool flag_noinit = false;
@@ -72,7 +72,88 @@ struct Clk2fflogicPass : public Pass {
for (auto cell : vector<Cell*>(module->selected_cells()))
{
- if (cell->type.in("$dlatch"))
+ if (cell->type.in("$mem"))
+ {
+ int abits = cell->getParam("\\ABITS").as_int();
+ int width = cell->getParam("\\WIDTH").as_int();
+ int rd_ports = cell->getParam("\\RD_PORTS").as_int();
+ int wr_ports = cell->getParam("\\WR_PORTS").as_int();
+
+ for (int i = 0; i < rd_ports; i++) {
+ if (cell->getParam("\\RD_CLK_ENABLE").extract(i).as_bool())
+ log_error("Read port %d of memory %s.%s is clocked. This is not supported by \"clk2fflogic\"! "
+ "Call \"memory\" with -nordff to avoid this error.\n", i, log_id(cell), log_id(module));
+ }
+
+ Const wr_clk_en_param = cell->getParam("\\WR_CLK_ENABLE");
+ Const wr_clk_pol_param = cell->getParam("\\WR_CLK_POLARITY");
+
+ SigSpec wr_clk_port = cell->getPort("\\WR_CLK");
+ SigSpec wr_en_port = cell->getPort("\\WR_EN");
+ SigSpec wr_addr_port = cell->getPort("\\WR_ADDR");
+ SigSpec wr_data_port = cell->getPort("\\WR_DATA");
+
+ for (int wport = 0; wport < wr_ports; wport++)
+ {
+ bool clken = wr_clk_en_param[wport] == State::S1;
+ bool clkpol = wr_clk_pol_param[wport] == State::S1;
+
+ if (!clken)
+ continue;
+
+ SigBit clk = wr_clk_port[wport];
+ SigSpec en = wr_en_port.extract(wport*width, width);
+ SigSpec addr = wr_addr_port.extract(wport*abits, abits);
+ SigSpec data = wr_data_port.extract(wport*width, width);
+
+ log("Modifying write port %d on memory %s.%s: CLK=%s, A=%s, D=%s\n",
+ wport, log_id(module), log_id(cell), log_signal(clk),
+ log_signal(addr), log_signal(data));
+
+ Wire *past_clk = module->addWire(NEW_ID);
+ past_clk->attributes["\\init"] = clkpol ? State::S1 : State::S0;
+ module->addFf(NEW_ID, clk, past_clk);
+
+ SigSpec clock_edge_pattern;
+
+ if (clkpol) {
+ clock_edge_pattern.append_bit(State::S0);
+ clock_edge_pattern.append_bit(State::S1);
+ } else {
+ clock_edge_pattern.append_bit(State::S1);
+ clock_edge_pattern.append_bit(State::S0);
+ }
+
+ SigSpec clock_edge = module->Eqx(NEW_ID, {clk, SigSpec(past_clk)}, clock_edge_pattern);
+
+ SigSpec en_q = module->addWire(NEW_ID, GetSize(en));
+ module->addFf(NEW_ID, en, en_q);
+
+ SigSpec addr_q = module->addWire(NEW_ID, GetSize(addr));
+ module->addFf(NEW_ID, addr, addr_q);
+
+ SigSpec data_q = module->addWire(NEW_ID, GetSize(data));
+ module->addFf(NEW_ID, data, data_q);
+
+ wr_clk_port[wport] = State::S0;
+ wr_en_port.replace(wport*width, module->Mux(NEW_ID, Const(0, GetSize(en_q)), en_q, clock_edge));
+ wr_addr_port.replace(wport*abits, addr_q);
+ wr_data_port.replace(wport*width, data_q);
+
+ wr_clk_en_param[wport] = State::S0;
+ wr_clk_pol_param[wport] = State::S0;
+ }
+
+ cell->setParam("\\WR_CLK_ENABLE", wr_clk_en_param);
+ cell->setParam("\\WR_CLK_POLARITY", wr_clk_pol_param);
+
+ cell->setPort("\\WR_CLK", wr_clk_port);
+ cell->setPort("\\WR_EN", wr_en_port);
+ cell->setPort("\\WR_ADDR", wr_addr_port);
+ cell->setPort("\\WR_DATA", wr_data_port);
+ }
+
+ if (cell->type.in("$dlatch", "$dlatchsr"))
{
bool enpol = cell->parameters["\\EN_POLARITY"].as_bool();
@@ -87,10 +168,31 @@ struct Clk2fflogicPass : public Pass {
Wire *past_q = module->addWire(NEW_ID, GetSize(sig_q));
module->addFf(NEW_ID, sig_q, past_q);
- if (enpol)
- module->addMux(NEW_ID, past_q, sig_d, sig_en, sig_q);
+ if (cell->type == "$dlatch")
+ {
+ if (enpol)
+ module->addMux(NEW_ID, past_q, sig_d, sig_en, sig_q);
+ else
+ module->addMux(NEW_ID, sig_d, past_q, sig_en, sig_q);
+ }
else
- module->addMux(NEW_ID, sig_d, past_q, sig_en, sig_q);
+ {
+ SigSpec t;
+ if (enpol)
+ t = module->Mux(NEW_ID, past_q, sig_d, sig_en);
+ else
+ t = module->Mux(NEW_ID, sig_d, past_q, sig_en);
+
+ SigSpec s = cell->getPort("\\SET");
+ if (!cell->parameters["\\SET_POLARITY"].as_bool())
+ s = module->Not(NEW_ID, s);
+ t = module->Or(NEW_ID, t, s);
+
+ SigSpec c = cell->getPort("\\CLR");
+ if (cell->parameters["\\CLR_POLARITY"].as_bool())
+ c = module->Not(NEW_ID, c);
+ module->addAnd(NEW_ID, t, c, sig_q);
+ }
Const initval;
bool assign_initval = false;
@@ -151,6 +253,13 @@ struct Clk2fflogicPass : public Pass {
SigSpec qval = module->Mux(NEW_ID, past_q, past_d, clock_edge);
Const rstval = cell->parameters["\\ARST_VALUE"];
+ Wire *past_arst = module->addWire(NEW_ID);
+ module->addFf(NEW_ID, arst, past_arst);
+ if (cell->parameters["\\ARST_POLARITY"].as_bool())
+ arst = module->LogicOr(NEW_ID, arst, past_arst);
+ else
+ arst = module->LogicAnd(NEW_ID, arst, past_arst);
+
if (cell->parameters["\\ARST_POLARITY"].as_bool())
module->addMux(NEW_ID, qval, rstval, arst, sig_q);
else
diff --git a/passes/sat/cutpoint.cc b/passes/sat/cutpoint.cc
new file mode 100644
index 000000000..b4549bc39
--- /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", "adds formal cut points to the design") { }
+ 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/eval.cc b/passes/sat/eval.cc
index 09f69cc5c..e0bb439f4 100644
--- a/passes/sat/eval.cc
+++ b/passes/sat/eval.cc
@@ -47,8 +47,8 @@ struct BruteForceEquivChecker
{
if (inputs.size() < mod1_inputs.size()) {
RTLIL::SigSpec inputs0 = inputs, inputs1 = inputs;
- inputs0.append(RTLIL::Const(0, 1));
- inputs1.append(RTLIL::Const(1, 1));
+ inputs0.append(State::S0);
+ inputs1.append(State::S1);
run_checker(inputs0);
run_checker(inputs1);
return;
@@ -360,7 +360,7 @@ struct VlogHammerReporter
struct EvalPass : public Pass {
EvalPass() : Pass("eval", "evaluate the circuit given an input") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -383,7 +383,7 @@ struct EvalPass : public Pass {
log(" then all output ports of the current module are used.\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
std::vector<std::pair<std::string, std::string>> sets;
std::vector<std::string> shows, tables;
diff --git a/passes/sat/expose.cc b/passes/sat/expose.cc
index 9427547f3..29dfc7b19 100644
--- a/passes/sat/expose.cc
+++ b/passes/sat/expose.cc
@@ -143,7 +143,7 @@ void create_dff_dq_map(std::map<RTLIL::IdString, dff_map_info_t> &map, RTLIL::De
continue;
}
- if (info.cell->type == "$_DFF_N_" || info.cell->type == "$_DFF_P_") {
+ if (info.cell->type.in("$_DFF_N_", "$_DFF_P_")) {
info.bit_clk = sigmap(info.cell->getPort("\\C")).as_bit();
info.clk_polarity = info.cell->type == "$_DFF_P_";
info.bit_d = sigmap(info.cell->getPort("\\D")).as_bit();
@@ -151,7 +151,7 @@ void create_dff_dq_map(std::map<RTLIL::IdString, dff_map_info_t> &map, RTLIL::De
continue;
}
- if (info.cell->type.size() == 10 && info.cell->type.substr(0, 6) == "$_DFF_") {
+ if (info.cell->type.size() == 10 && info.cell->type.begins_with("$_DFF_")) {
info.bit_clk = sigmap(info.cell->getPort("\\C")).as_bit();
info.bit_arst = sigmap(info.cell->getPort("\\R")).as_bit();
info.clk_polarity = info.cell->type[6] == 'P';
@@ -220,7 +220,7 @@ RTLIL::Wire *add_new_wire(RTLIL::Module *module, RTLIL::IdString name, int width
struct ExposePass : public Pass {
ExposePass() : Pass("expose", "convert internal signals to module ports") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -236,6 +236,10 @@ struct ExposePass : public Pass {
log(" when exposing a wire, create an input/output pair and cut the internal\n");
log(" signal path at that wire.\n");
log("\n");
+ log(" -input\n");
+ log(" when exposing a wire, create an input port and disconnect the internal\n");
+ log(" driver.\n");
+ log("\n");
log(" -shared\n");
log(" only expose those signals that are shared among the selected modules.\n");
log(" this is useful for preparing modules for equivalence checking.\n");
@@ -253,12 +257,13 @@ struct ExposePass : public Pass {
log(" designator for the exposed signal.\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
bool flag_shared = false;
bool flag_evert = false;
bool flag_dff = false;
bool flag_cut = false;
+ bool flag_input = false;
bool flag_evert_dff = false;
std::string sep = ".";
@@ -279,10 +284,14 @@ struct ExposePass : public Pass {
flag_dff = true;
continue;
}
- if (args[argidx] == "-cut") {
+ if (args[argidx] == "-cut" && !flag_input) {
flag_cut = true;
continue;
}
+ if (args[argidx] == "-input" && !flag_cut) {
+ flag_input = true;
+ continue;
+ }
if (args[argidx] == "-evert-dff") {
flag_evert_dff = true;
continue;
@@ -464,16 +473,42 @@ struct ExposePass : public Pass {
continue;
}
- if (!it.second->port_output) {
- it.second->port_output = true;
- log("New module port: %s/%s\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(it.second->name));
+ if (flag_input)
+ {
+ if (!it.second->port_input) {
+ it.second->port_input = true;
+ log("New module port: %s/%s\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(it.second->name));
+ RTLIL::Wire *w = module->addWire(NEW_ID, GetSize(it.second));
+ out_to_in_map.add(it.second, w);
+ }
+ }
+ else
+ {
+ if (!it.second->port_output) {
+ it.second->port_output = true;
+ log("New module port: %s/%s\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(it.second->name));
+ }
+
+ if (flag_cut) {
+ RTLIL::Wire *in_wire = add_new_wire(module, it.second->name.str() + sep + "i", it.second->width);
+ in_wire->port_input = true;
+ out_to_in_map.add(sigmap(it.second), in_wire);
+ }
}
+ }
- if (flag_cut) {
- RTLIL::Wire *in_wire = add_new_wire(module, it.second->name.str() + sep + "i", it.second->width);
- in_wire->port_input = true;
- out_to_in_map.add(sigmap(it.second), in_wire);
+ if (flag_input)
+ {
+ for (auto &it : module->cells_) {
+ if (!ct.cell_known(it.second->type))
+ continue;
+ for (auto &conn : it.second->connections_)
+ if (ct.cell_output(it.second->type, conn.first))
+ conn.second = out_to_in_map(sigmap(conn.second));
}
+
+ for (auto &conn : module->connections_)
+ conn.first = out_to_in_map(conn.first);
}
if (flag_cut)
diff --git a/passes/sat/fmcombine.cc b/passes/sat/fmcombine.cc
new file mode 100644
index 000000000..00c098542
--- /dev/null
+++ b/passes/sat/fmcombine.cc
@@ -0,0 +1,376 @@
+/*
+ * 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 initeq = false;
+ bool anyeq = false;
+ 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;
+ }
+
+ Cell *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));
+
+ return c;
+ }
+
+ 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) {
+ if (opts.anyeq && cell->type.in("$anyseq", "$anyconst")) {
+ Cell *gold = import_prim_cell(cell, "_gold");
+ for (auto &conn : cell->connections())
+ module->connect(import_sig(conn.second, "_gate"), gold->getPort(conn.first));
+ } else {
+ Cell *gold = import_prim_cell(cell, "_gold");
+ Cell *gate = import_prim_cell(cell, "_gate");
+ if (opts.initeq) {
+ if (cell->type.in("$ff", "$dff", "$dffe",
+ "$dffsr", "$adff", "$dlatch", "$dlatchsr")) {
+ SigSpec gold_q = gold->getPort("\\Q");
+ SigSpec gate_q = gate->getPort("\\Q");
+ SigSpec en = module->Initstate(NEW_ID);
+ SigSpec eq = module->Eq(NEW_ID, gold_q, gate_q);
+ module->addAssume(NEW_ID, eq, en);
+ }
+ }
+ }
+ } 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(" -initeq\n");
+ log(" Insert assumptions that initially all FFs in both circuits have the\n");
+ log(" same initial values.\n");
+ log("\n");
+ log(" -anyeq\n");
+ log(" Do not duplicate $anyseq/$anyconst cells.\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] == "-initeq") {
+ opts.initeq = true;
+ continue;
+ }
+ if (args[argidx] == "-anyeq") {
+ opts.anyeq = true;
+ 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("Gate 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("Gate 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/fminit.cc b/passes/sat/fminit.cc
new file mode 100644
index 000000000..f3f00b382
--- /dev/null
+++ b/passes/sat/fminit.cc
@@ -0,0 +1,197 @@
+/*
+ * 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 FminitPass : public Pass {
+ FminitPass() : Pass("fminit", "set init values/sequences for formal") { }
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" fminit [options] <selection>\n");
+ log("\n");
+ log("This pass creates init constraints (for example for reset sequences) in a formal\n");
+ log("model.\n");
+ log("\n");
+ log(" -seq <signal> <sequence>\n");
+ log(" Set sequence using comma-separated list of values, use 'z for\n");
+ log(" unconstrained bits. The last value is used for the remainder of the\n");
+ log(" trace.\n");
+ log("\n");
+ log(" -set <signal> <value>\n");
+ log(" Add constant value constraint\n");
+ log("\n");
+ log(" -posedge <signal>\n");
+ log(" -negedge <signal>\n");
+ log(" Set clock for init sequences\n");
+ log("\n");
+ }
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ vector<pair<string, vector<string>>> initdata;
+ vector<pair<string, string>> setdata;
+ string clocksignal;
+ bool clockedge;
+
+ log_header(design, "Executing FMINIT 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] == "-seq" && argidx+2 < args.size()) {
+ string lhs = args[++argidx];
+ string rhs = args[++argidx];
+ initdata.push_back(make_pair(lhs, split_tokens(rhs, ",")));
+ continue;
+ }
+ if (args[argidx] == "-set" && argidx+2 < args.size()) {
+ string lhs = args[++argidx];
+ string rhs = args[++argidx];
+ setdata.push_back(make_pair(lhs, rhs));
+ continue;
+ }
+ if (args[argidx] == "-posedge" && argidx+1 < args.size()) {
+ clocksignal = args[++argidx];
+ clockedge = true;
+ continue;
+ }
+ if (args[argidx] == "-negedge" && argidx+1 < args.size()) {
+ clocksignal = args[++argidx];
+ clockedge = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ Module *module = nullptr;
+
+ for (auto mod : design->selected_modules()) {
+ if (module != nullptr)
+ log_error("'fminit' requires exactly one module to be selected.\n");
+ module = mod;
+ }
+
+ if (module == nullptr)
+ log_error("'fminit' requires exactly one module to be selected.\n");
+
+ SigSpec clksig;
+ if (!clocksignal.empty()) {
+ if (!SigSpec::parse(clksig, module, clocksignal))
+ log_error("Error parsing expression '%s'.\n", clocksignal.c_str());
+ }
+
+ for (auto &it : setdata)
+ {
+ SigSpec lhs, rhs;
+
+ if (!SigSpec::parse(lhs, module, it.first))
+ log_error("Error parsing expression '%s'.\n", it.first.c_str());
+
+ if (!SigSpec::parse_rhs(lhs, rhs, module, it.second))
+ log_error("Error parsing expression '%s'.\n", it.second.c_str());
+
+ SigSpec final_lhs, final_rhs;
+
+ for (int i = 0; i < GetSize(rhs); i++)
+ if (rhs[i] != State::Sz) {
+ final_lhs.append(lhs[i]);
+ final_rhs.append(rhs[i]);
+ }
+
+ if (!final_lhs.empty()) {
+ SigSpec eq = module->Eq(NEW_ID, final_lhs, final_rhs);
+ module->addAssume(NEW_ID, eq, State::S1);
+ }
+ }
+
+ vector<SigSpec> ctrlsig;
+ vector<SigSpec> ctrlsig_latched;
+
+ for (auto &it : initdata)
+ {
+ SigSpec lhs, rhs;
+
+ if (!SigSpec::parse(lhs, module, it.first))
+ log_error("Error parsing expression '%s'.\n", it.first.c_str());
+
+ for (int i = 0; i < GetSize(it.second); i++)
+ {
+ if (i >= GetSize(ctrlsig))
+ {
+ SigSpec insig = i > 0 ? ctrlsig.at(i-1) : State::S0;
+
+ Wire *outwire = module->addWire(NEW_ID);
+ outwire->attributes[ID(init)] = i > 0 ? State::S0 : State::S1;
+
+ if (clksig.empty())
+ module->addFf(NEW_ID, insig, outwire);
+ else
+ module->addDff(NEW_ID, clksig, insig, outwire, clockedge);
+
+ ctrlsig.push_back(outwire);
+ ctrlsig_latched.push_back(SigSpec());
+ }
+
+ if (i+1 == GetSize(it.second) && ctrlsig_latched[i].empty())
+ {
+ Wire *ffwire = module->addWire(NEW_ID);
+ ffwire->attributes[ID(init)] = State::S0;
+ SigSpec outsig = module->Or(NEW_ID, ffwire, ctrlsig[i]);
+
+ if (clksig.empty())
+ module->addFf(NEW_ID, outsig, ffwire);
+ else
+ module->addDff(NEW_ID, clksig, outsig, ffwire, clockedge);
+
+ ctrlsig_latched[i] = outsig;
+ }
+
+ SigSpec ctrl = i+1 == GetSize(it.second) ? ctrlsig_latched[i] : ctrlsig[i];
+
+ SigSpec final_lhs, final_rhs;
+
+ if (!SigSpec::parse_rhs(lhs, rhs, module, it.second[i]))
+ log_error("Error parsing expression '%s'.\n", it.second[i].c_str());
+
+ for (int i = 0; i < GetSize(rhs); i++)
+ if (rhs[i] != State::Sz) {
+ final_lhs.append(lhs[i]);
+ final_rhs.append(rhs[i]);
+ }
+
+ if (!final_lhs.empty()) {
+ SigSpec eq = module->Eq(NEW_ID, final_lhs, final_rhs);
+ module->addAssume(NEW_ID, eq, ctrl);
+ }
+ }
+ }
+ }
+} FminitPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/sat/freduce.cc b/passes/sat/freduce.cc
index a3028bfce..f29631639 100644
--- a/passes/sat/freduce.cc
+++ b/passes/sat/freduce.cc
@@ -760,7 +760,7 @@ struct FreduceWorker
struct FreducePass : public Pass {
FreducePass() : Pass("freduce", "perform functional reduction") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -791,7 +791,7 @@ struct FreducePass : public Pass {
log("circuit that is analyzed.\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
reduce_counter = 0;
reduce_stop_at = 0;
diff --git a/passes/sat/miter.cc b/passes/sat/miter.cc
index 9e150b60c..49ef40061 100644
--- a/passes/sat/miter.cc
+++ b/passes/sat/miter.cc
@@ -59,7 +59,7 @@ void create_miter_equiv(struct Pass *that, std::vector<std::string> args, RTLIL:
}
break;
}
- if (argidx+3 != args.size() || args[argidx].substr(0, 1) == "-")
+ if (argidx+3 != args.size() || args[argidx].compare(0, 1, "-") == 0)
that->cmd_error(args, argidx, "command argument error");
RTLIL::IdString gold_name = RTLIL::escape_id(args[argidx++]);
@@ -236,7 +236,7 @@ void create_miter_equiv(struct Pass *that, std::vector<std::string> args, RTLIL:
if (flag_make_assert) {
RTLIL::Cell *assert_cell = miter_module->addCell(NEW_ID, "$assert");
assert_cell->setPort("\\A", all_conditions);
- assert_cell->setPort("\\EN", RTLIL::SigSpec(1, 1));
+ assert_cell->setPort("\\EN", State::S1);
}
RTLIL::Wire *w_trigger = miter_module->addWire("\\trigger");
@@ -254,7 +254,7 @@ void create_miter_equiv(struct Pass *that, std::vector<std::string> args, RTLIL:
if (flag_flatten) {
log_push();
- Pass::call_on_module(design, miter_module, "flatten; opt_expr -keepdc -undriven;;");
+ Pass::call_on_module(design, miter_module, "flatten -wb; opt_expr -keepdc -undriven;;");
log_pop();
}
}
@@ -279,7 +279,7 @@ void create_miter_assert(struct Pass *that, std::vector<std::string> args, RTLIL
}
break;
}
- if ((argidx+1 != args.size() && argidx+2 != args.size()) || args[argidx].substr(0, 1) == "-")
+ if ((argidx+1 != args.size() && argidx+2 != args.size()) || args[argidx].compare(0, 1, "-") == 0)
that->cmd_error(args, argidx, "command argument error");
IdString module_name = RTLIL::escape_id(args[argidx++]);
@@ -308,7 +308,7 @@ void create_miter_assert(struct Pass *that, std::vector<std::string> args, RTLIL
if (flag_flatten) {
log_push();
- Pass::call_on_module(design, module, "flatten;;");
+ Pass::call_on_module(design, module, "flatten -wb;;");
log_pop();
}
@@ -358,7 +358,7 @@ void create_miter_assert(struct Pass *that, std::vector<std::string> args, RTLIL
struct MiterPass : public Pass {
MiterPass() : Pass("miter", "automatically create a miter circuit") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -385,7 +385,7 @@ struct MiterPass : public Pass {
log(" also create an 'assert' cell that checks if trigger is always low.\n");
log("\n");
log(" -flatten\n");
- log(" call 'flatten; opt_expr -keepdc -undriven;;' on the miter circuit.\n");
+ log(" call 'flatten -wb; opt_expr -keepdc -undriven;;' on the miter circuit.\n");
log("\n");
log("\n");
log(" miter -assert [options] module [miter_name]\n");
@@ -399,10 +399,10 @@ struct MiterPass : public Pass {
log(" keep module output ports.\n");
log("\n");
log(" -flatten\n");
- log(" call 'flatten; opt_expr -keepdc -undriven;;' on the miter circuit.\n");
+ log(" call 'flatten -wb; opt_expr -keepdc -undriven;;' on the miter circuit.\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
if (args.size() > 1 && args[1] == "-equiv") {
create_miter_equiv(this, args, design);
diff --git a/passes/sat/mutate.cc b/passes/sat/mutate.cc
new file mode 100644
index 000000000..b53bbfeb2
--- /dev/null
+++ b/passes/sat/mutate.cc
@@ -0,0 +1,988 @@
+/*
+ * 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.module.empty())
+ log_cmd_error("Missing -module argument.\n");
+
+ Module *module = design->module(opts.module);
+ if (module == nullptr)
+ log_cmd_error("Module %s not found.\n", log_id(opts.module));
+
+ if (opts.cell.empty())
+ log_cmd_error("Missing -cell argument.\n");
+
+ Cell *cell = module->cell(opts.cell);
+ if (cell == nullptr)
+ log_cmd_error("Cell %s not found in module %s.\n", log_id(opts.cell), log_id(opts.module));
+
+ if (opts.port.empty())
+ log_cmd_error("Missing -port argument.\n");
+
+ if (!cell->hasPort(opts.port))
+ log_cmd_error("Port %s not found on cell %s.%s.\n", log_id(opts.port), log_id(opts.module), log_id(opts.cell));
+
+ if (opts.portbit < 0)
+ log_cmd_error("Missing -portbit argument.\n");
+
+ if (GetSize(cell->getPort(opts.port)) <= opts.portbit)
+ log_cmd_error("Out-of-range -portbit argument for port %s on cell %s.%s.\n", log_id(opts.port), log_id(opts.module), log_id(opts.cell));
+
+ 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.ctrlbit < 0)
+ log_cmd_error("Missing -ctrlbit argument.\n");
+
+ if (GetSize(cell->getPort(opts.port)) <= opts.ctrlbit)
+ log_cmd_error("Out-of-range -ctrlbit argument for port %s on cell %s.%s.\n", log_id(opts.port), log_id(opts.module), log_id(opts.cell));
+
+ 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/sat.cc b/passes/sat/sat.cc
index a6ac7afd4..436ac1b01 100644
--- a/passes/sat/sat.cc
+++ b/passes/sat/sat.cc
@@ -268,8 +268,9 @@ struct SatHelper
RTLIL::SigSpec removed_bits;
for (int i = 0; i < lhs.size(); i++) {
RTLIL::SigSpec bit = lhs.extract(i, 1);
- if (!satgen.initial_state.check_all(bit)) {
- removed_bits.append(bit);
+ if (rhs[i] == State::Sx || !satgen.initial_state.check_all(bit)) {
+ if (rhs[i] != State::Sx)
+ removed_bits.append(bit);
lhs.remove(i, 1);
rhs.remove(i, 1);
i--;
@@ -519,7 +520,7 @@ struct SatHelper
for (auto &p : d->connections()) {
if (d->type == "$dff" && p.first == "\\CLK")
continue;
- if (d->type.substr(0, 6) == "$_DFF_" && p.first == "\\C")
+ if (d->type.begins_with("$_DFF_") && p.first == "\\C")
continue;
queued_signals.add(handled_signals.remove(sigmap(p.second)));
}
@@ -659,6 +660,7 @@ struct SatHelper
void dump_model_to_vcd(std::string vcd_file_name)
{
+ rewrite_filename(vcd_file_name);
FILE *f = fopen(vcd_file_name.c_str(), "w");
if (!f)
log_cmd_error("Can't open output file `%s' for writing: %s\n", vcd_file_name.c_str(), strerror(errno));
@@ -691,7 +693,6 @@ struct SatHelper
// VCD has some limits on internal (non-display) identifier names, so make legal ones
std::map<std::string, std::string> vcdnames;
- fprintf(f, "$timescale 1ns\n"); // arbitrary time scale since actual clock period is unknown/unimportant
fprintf(f, "$scope module %s $end\n", module->name.c_str());
for (auto &info : modelInfo)
{
@@ -762,6 +763,7 @@ struct SatHelper
void dump_model_to_json(std::string json_file_name)
{
+ rewrite_filename(json_file_name);
FILE *f = fopen(json_file_name.c_str(), "w");
if (!f)
log_cmd_error("Can't open output file `%s' for writing: %s\n", json_file_name.c_str(), strerror(errno));
@@ -796,7 +798,7 @@ struct SatHelper
vector<string> data;
string name = wd.first.c_str();
- while (name.substr(0, 1) == "\\")
+ while (name.compare(0, 1, "\\") == 0)
name = name.substr(1);
fprintf(f, " { \"name\": \"%s\", \"wave\": \"", name.c_str());
@@ -891,7 +893,7 @@ void print_qed()
struct SatPass : public Pass {
SatPass() : Pass("sat", "solve a SAT problem in the circuit") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -1058,7 +1060,7 @@ struct SatPass : public Pass {
log(" Like -falsify but do not return an error for timeouts.\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
std::vector<std::pair<std::string, std::string>> sets, sets_init, prove, prove_x;
std::map<int, std::vector<std::pair<std::string, std::string>>> sets_at;
@@ -1170,6 +1172,7 @@ struct SatPass : public Pass {
if (args[argidx] == "-tempinduct-def") {
tempinduct = true;
tempinduct_def = true;
+ enable_undef = true;
continue;
}
if (args[argidx] == "-tempinduct-baseonly") {
@@ -1351,7 +1354,7 @@ struct SatPass : public Pass {
if (show_regs) {
pool<Wire*> reg_wires;
for (auto cell : module->cells()) {
- if (cell->type == "$dff" || cell->type.substr(0, 6) == "$_DFF_")
+ if (cell->type == "$dff" || cell->type.begins_with("$_DFF_"))
for (auto bit : cell->getPort("\\Q"))
if (bit.wire)
reg_wires.insert(bit.wire);
@@ -1505,6 +1508,7 @@ struct SatPass : public Pass {
{
if (!cnf_file_name.empty())
{
+ rewrite_filename(cnf_file_name);
FILE *f = fopen(cnf_file_name.c_str(), "w");
if (!f)
log_cmd_error("Can't open output file `%s' for writing: %s\n", cnf_file_name.c_str(), strerror(errno));
@@ -1608,6 +1612,7 @@ struct SatPass : public Pass {
if (!cnf_file_name.empty())
{
+ rewrite_filename(cnf_file_name);
FILE *f = fopen(cnf_file_name.c_str(), "w");
if (!f)
log_cmd_error("Can't open output file `%s' for writing: %s\n", cnf_file_name.c_str(), strerror(errno));
diff --git a/passes/sat/sim.cc b/passes/sat/sim.cc
index b49c12529..d5634b26d 100644
--- a/passes/sat/sim.cc
+++ b/passes/sat/sim.cc
@@ -88,6 +88,8 @@ struct SimInstance
SimInstance(SimShared *shared, Module *module, Cell *instance = nullptr, SimInstance *parent = nullptr) :
shared(shared), module(module), instance(instance), parent(parent), sigmap(module)
{
+ log_assert(module);
+
if (parent) {
log_assert(parent->children.count(instance) == 0);
parent->children[instance] = this;
@@ -228,7 +230,7 @@ struct SimInstance
bool did_something = false;
sig = sigmap(sig);
- log_assert(GetSize(sig) == GetSize(value));
+ log_assert(GetSize(sig) <= GetSize(value));
for (int i = 0; i < GetSize(sig); i++)
if (state_nets.at(sig[i]) != value[i]) {
@@ -745,7 +747,7 @@ struct SimWorker : SimShared
struct SimPass : public Pass {
SimPass() : Pass("sim", "simulate the circuit") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -778,7 +780,7 @@ struct SimPass : public Pass {
log(" number of cycles to simulate (default: 20)\n");
log("\n");
log(" -a\n");
- log(" include all nets in VCD output, nut just those with public names\n");
+ log(" include all nets in VCD output, not just those with public names\n");
log("\n");
log(" -w\n");
log(" writeback mode: use final simulation state as new init state\n");
@@ -787,7 +789,7 @@ struct SimPass : public Pass {
log(" enable debug output\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
SimWorker worker;
int numcycles = 20;
@@ -848,6 +850,9 @@ struct SimPass : public Pass {
if (design->full_selection()) {
top_mod = design->top_module();
+
+ if (!top_mod)
+ log_cmd_error("Design has no top module, use the 'hierarchy' command to specify one.\n");
} else {
auto mods = design->selected_whole_modules();
if (GetSize(mods) != 1)
diff --git a/passes/sat/supercover.cc b/passes/sat/supercover.cc
new file mode 100644
index 000000000..ba44f02d8
--- /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 140a9f892..369c9de64 100644
--- a/passes/techmap/Makefile.inc
+++ b/passes/techmap/Makefile.inc
@@ -7,13 +7,18 @@ OBJS += passes/techmap/libparse.o
ifeq ($(ENABLE_ABC),1)
OBJS += passes/techmap/abc.o
+OBJS += passes/techmap/abc9.o
+OBJS += passes/techmap/abc9_exe.o
+OBJS += passes/techmap/abc9_ops.o
ifneq ($(ABCEXTERNAL),)
passes/techmap/abc.o: CXXFLAGS += -DABCEXTERNAL='"$(ABCEXTERNAL)"'
+passes/techmap/abc9.o: CXXFLAGS += -DABCEXTERNAL='"$(ABCEXTERNAL)"'
endif
endif
ifneq ($(SMALL),1)
OBJS += passes/techmap/iopadmap.o
+OBJS += passes/techmap/clkbufmap.o
OBJS += passes/techmap/hilomap.o
OBJS += passes/techmap/extract.o
OBJS += passes/techmap/extract_fa.o
@@ -35,6 +40,9 @@ OBJS += passes/techmap/insbuf.o
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
+OBJS += passes/techmap/extractinv.o
endif
GENFILES += passes/techmap/techmap.inc
@@ -57,4 +65,3 @@ yosys-filterlib$(EXE): passes/techmap/filterlib.o
$(Q) mkdir -p $(dir $@)
$(P) $(LD) -o yosys-filterlib$(EXE) $(LDFLAGS) $^ $(LDLIBS)
endif
-
diff --git a/passes/techmap/abc.cc b/passes/techmap/abc.cc
index be86f642a..581652a41 100644
--- a/passes/techmap/abc.cc
+++ b/passes/techmap/abc.cc
@@ -49,6 +49,7 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
+#include <cctype>
#include <cerrno>
#include <sstream>
#include <climits>
@@ -60,6 +61,10 @@
#include "frontends/blif/blifparse.h"
+#ifdef YOSYS_LINK_ABC
+extern "C" int Abc_RealMain(int argc, char *argv[]);
+#endif
+
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
@@ -77,6 +82,7 @@ enum class gate_type_t {
G_ANDNOT,
G_ORNOT,
G_MUX,
+ G_NMUX,
G_AOI3,
G_OAI3,
G_AOI4,
@@ -107,7 +113,7 @@ std::vector<gate_t> signal_list;
std::map<RTLIL::SigBit, int> signal_map;
std::map<RTLIL::SigBit, RTLIL::State> signal_init;
pool<std::string> enabled_gates;
-bool recover_init;
+bool recover_init, cmos_cost;
bool clk_polarity, en_polarity;
RTLIL::SigSpec clk_sig, en_sig;
@@ -160,39 +166,39 @@ void mark_port(RTLIL::SigSpec sig)
void extract_cell(RTLIL::Cell *cell, bool keepff)
{
- if (cell->type == "$_DFF_N_" || cell->type == "$_DFF_P_")
+ if (cell->type.in(ID($_DFF_N_), ID($_DFF_P_)))
{
- if (clk_polarity != (cell->type == "$_DFF_P_"))
+ if (clk_polarity != (cell->type == ID($_DFF_P_)))
return;
- if (clk_sig != assign_map(cell->getPort("\\C")))
+ if (clk_sig != assign_map(cell->getPort(ID(C))))
return;
if (GetSize(en_sig) != 0)
return;
goto matching_dff;
}
- if (cell->type == "$_DFFE_NN_" || cell->type == "$_DFFE_NP_" || cell->type == "$_DFFE_PN_" || cell->type == "$_DFFE_PP_")
+ if (cell->type.in(ID($_DFFE_NN_), ID($_DFFE_NP_), ID($_DFFE_PN_), ID($_DFFE_PP_)))
{
- if (clk_polarity != (cell->type == "$_DFFE_PN_" || cell->type == "$_DFFE_PP_"))
+ if (clk_polarity != cell->type.in(ID($_DFFE_PN_), ID($_DFFE_PP_)))
return;
- if (en_polarity != (cell->type == "$_DFFE_NP_" || cell->type == "$_DFFE_PP_"))
+ if (en_polarity != cell->type.in(ID($_DFFE_NP_), ID($_DFFE_PP_)))
return;
- if (clk_sig != assign_map(cell->getPort("\\C")))
+ if (clk_sig != assign_map(cell->getPort(ID(C))))
return;
- if (en_sig != assign_map(cell->getPort("\\E")))
+ if (en_sig != assign_map(cell->getPort(ID(E))))
return;
goto matching_dff;
}
if (0) {
matching_dff:
- RTLIL::SigSpec sig_d = cell->getPort("\\D");
- RTLIL::SigSpec sig_q = cell->getPort("\\Q");
+ RTLIL::SigSpec sig_d = cell->getPort(ID(D));
+ RTLIL::SigSpec sig_q = cell->getPort(ID(Q));
if (keepff)
for (auto &c : sig_q.chunks())
if (c.wire != NULL)
- c.wire->attributes["\\keep"] = 1;
+ c.wire->attributes[ID::keep] = 1;
assign_map.apply(sig_d);
assign_map.apply(sig_q);
@@ -203,25 +209,25 @@ void extract_cell(RTLIL::Cell *cell, bool keepff)
return;
}
- if (cell->type.in("$_BUF_", "$_NOT_"))
+ if (cell->type.in(ID($_BUF_), ID($_NOT_)))
{
- RTLIL::SigSpec sig_a = cell->getPort("\\A");
- RTLIL::SigSpec sig_y = cell->getPort("\\Y");
+ RTLIL::SigSpec sig_a = cell->getPort(ID::A);
+ RTLIL::SigSpec sig_y = cell->getPort(ID::Y);
assign_map.apply(sig_a);
assign_map.apply(sig_y);
- map_signal(sig_y, cell->type == "$_BUF_" ? G(BUF) : G(NOT), map_signal(sig_a));
+ map_signal(sig_y, cell->type == ID($_BUF_) ? G(BUF) : G(NOT), map_signal(sig_a));
module->remove(cell);
return;
}
- if (cell->type.in("$_AND_", "$_NAND_", "$_OR_", "$_NOR_", "$_XOR_", "$_XNOR_", "$_ANDNOT_", "$_ORNOT_"))
+ if (cell->type.in(ID($_AND_), ID($_NAND_), ID($_OR_), ID($_NOR_), ID($_XOR_), ID($_XNOR_), ID($_ANDNOT_), ID($_ORNOT_)))
{
- RTLIL::SigSpec sig_a = cell->getPort("\\A");
- RTLIL::SigSpec sig_b = cell->getPort("\\B");
- RTLIL::SigSpec sig_y = cell->getPort("\\Y");
+ RTLIL::SigSpec sig_a = cell->getPort(ID::A);
+ RTLIL::SigSpec sig_b = cell->getPort(ID::B);
+ RTLIL::SigSpec sig_y = cell->getPort(ID::Y);
assign_map.apply(sig_a);
assign_map.apply(sig_b);
@@ -230,21 +236,21 @@ void extract_cell(RTLIL::Cell *cell, bool keepff)
int mapped_a = map_signal(sig_a);
int mapped_b = map_signal(sig_b);
- if (cell->type == "$_AND_")
+ if (cell->type == ID($_AND_))
map_signal(sig_y, G(AND), mapped_a, mapped_b);
- else if (cell->type == "$_NAND_")
+ else if (cell->type == ID($_NAND_))
map_signal(sig_y, G(NAND), mapped_a, mapped_b);
- else if (cell->type == "$_OR_")
+ else if (cell->type == ID($_OR_))
map_signal(sig_y, G(OR), mapped_a, mapped_b);
- else if (cell->type == "$_NOR_")
+ else if (cell->type == ID($_NOR_))
map_signal(sig_y, G(NOR), mapped_a, mapped_b);
- else if (cell->type == "$_XOR_")
+ else if (cell->type == ID($_XOR_))
map_signal(sig_y, G(XOR), mapped_a, mapped_b);
- else if (cell->type == "$_XNOR_")
+ else if (cell->type == ID($_XNOR_))
map_signal(sig_y, G(XNOR), mapped_a, mapped_b);
- else if (cell->type == "$_ANDNOT_")
+ else if (cell->type == ID($_ANDNOT_))
map_signal(sig_y, G(ANDNOT), mapped_a, mapped_b);
- else if (cell->type == "$_ORNOT_")
+ else if (cell->type == ID($_ORNOT_))
map_signal(sig_y, G(ORNOT), mapped_a, mapped_b);
else
log_abort();
@@ -253,12 +259,12 @@ void extract_cell(RTLIL::Cell *cell, bool keepff)
return;
}
- if (cell->type == "$_MUX_")
+ if (cell->type.in(ID($_MUX_), ID($_NMUX_)))
{
- RTLIL::SigSpec sig_a = cell->getPort("\\A");
- RTLIL::SigSpec sig_b = cell->getPort("\\B");
- RTLIL::SigSpec sig_s = cell->getPort("\\S");
- RTLIL::SigSpec sig_y = cell->getPort("\\Y");
+ RTLIL::SigSpec sig_a = cell->getPort(ID::A);
+ RTLIL::SigSpec sig_b = cell->getPort(ID::B);
+ RTLIL::SigSpec sig_s = cell->getPort(ID(S));
+ RTLIL::SigSpec sig_y = cell->getPort(ID::Y);
assign_map.apply(sig_a);
assign_map.apply(sig_b);
@@ -269,18 +275,18 @@ void extract_cell(RTLIL::Cell *cell, bool keepff)
int mapped_b = map_signal(sig_b);
int mapped_s = map_signal(sig_s);
- map_signal(sig_y, G(MUX), mapped_a, mapped_b, mapped_s);
+ map_signal(sig_y, cell->type == ID($_MUX_) ? G(MUX) : G(NMUX), mapped_a, mapped_b, mapped_s);
module->remove(cell);
return;
}
- if (cell->type.in("$_AOI3_", "$_OAI3_"))
+ if (cell->type.in(ID($_AOI3_), ID($_OAI3_)))
{
- RTLIL::SigSpec sig_a = cell->getPort("\\A");
- RTLIL::SigSpec sig_b = cell->getPort("\\B");
- RTLIL::SigSpec sig_c = cell->getPort("\\C");
- RTLIL::SigSpec sig_y = cell->getPort("\\Y");
+ RTLIL::SigSpec sig_a = cell->getPort(ID::A);
+ RTLIL::SigSpec sig_b = cell->getPort(ID::B);
+ RTLIL::SigSpec sig_c = cell->getPort(ID(C));
+ RTLIL::SigSpec sig_y = cell->getPort(ID::Y);
assign_map.apply(sig_a);
assign_map.apply(sig_b);
@@ -291,19 +297,19 @@ void extract_cell(RTLIL::Cell *cell, bool keepff)
int mapped_b = map_signal(sig_b);
int mapped_c = map_signal(sig_c);
- map_signal(sig_y, cell->type == "$_AOI3_" ? G(AOI3) : G(OAI3), mapped_a, mapped_b, mapped_c);
+ map_signal(sig_y, cell->type == ID($_AOI3_) ? G(AOI3) : G(OAI3), mapped_a, mapped_b, mapped_c);
module->remove(cell);
return;
}
- if (cell->type.in("$_AOI4_", "$_OAI4_"))
+ if (cell->type.in(ID($_AOI4_), ID($_OAI4_)))
{
- RTLIL::SigSpec sig_a = cell->getPort("\\A");
- RTLIL::SigSpec sig_b = cell->getPort("\\B");
- RTLIL::SigSpec sig_c = cell->getPort("\\C");
- RTLIL::SigSpec sig_d = cell->getPort("\\D");
- RTLIL::SigSpec sig_y = cell->getPort("\\Y");
+ RTLIL::SigSpec sig_a = cell->getPort(ID::A);
+ RTLIL::SigSpec sig_b = cell->getPort(ID::B);
+ RTLIL::SigSpec sig_c = cell->getPort(ID(C));
+ RTLIL::SigSpec sig_d = cell->getPort(ID(D));
+ RTLIL::SigSpec sig_y = cell->getPort(ID::Y);
assign_map.apply(sig_a);
assign_map.apply(sig_b);
@@ -316,18 +322,50 @@ void extract_cell(RTLIL::Cell *cell, bool keepff)
int mapped_c = map_signal(sig_c);
int mapped_d = map_signal(sig_d);
- map_signal(sig_y, cell->type == "$_AOI4_" ? G(AOI4) : G(OAI4), mapped_a, mapped_b, mapped_c, mapped_d);
+ map_signal(sig_y, cell->type == ID($_AOI4_) ? G(AOI4) : G(OAI4), mapped_a, mapped_b, mapped_c, mapped_d);
module->remove(cell);
return;
}
}
-std::string remap_name(RTLIL::IdString abc_name)
+std::string remap_name(RTLIL::IdString abc_name, RTLIL::Wire **orig_wire = nullptr)
{
- std::stringstream sstr;
- sstr << "$abc$" << map_autoidx << "$" << abc_name.substr(1);
- return sstr.str();
+ std::string abc_sname = abc_name.substr(1);
+ bool isnew = false;
+ if (abc_sname.compare(0, 4, "new_") == 0)
+ {
+ abc_sname.erase(0, 4);
+ isnew = true;
+ }
+ if (abc_sname.compare(0, 5, "ys__n") == 0)
+ {
+ abc_sname.erase(0, 5);
+ if (std::isdigit(abc_sname.at(0)))
+ {
+ int sid = std::atoi(abc_sname.c_str());
+ size_t postfix_start = abc_sname.find_first_not_of("0123456789");
+ std::string postfix = postfix_start != std::string::npos ? abc_sname.substr(postfix_start) : "";
+
+ if (sid < GetSize(signal_list))
+ {
+ auto sig = signal_list.at(sid);
+ if (sig.bit.wire != nullptr)
+ {
+ std::string s = stringf("$abc$%d$%s", map_autoidx, sig.bit.wire->name.c_str()+1);
+ if (sig.bit.wire->width != 1)
+ s += stringf("[%d]", sig.bit.offset);
+ if (isnew)
+ s += "_new";
+ s += postfix;
+ if (orig_wire != nullptr)
+ *orig_wire = sig.bit.wire;
+ return s;
+ }
+ }
+ }
+ }
+ return stringf("$abc$%d$%s", map_autoidx, abc_name.c_str()+1);
}
void dump_loop_graph(FILE *f, int &nr, std::map<int, std::set<int>> &edges, std::set<int> &workpool, std::vector<int> &in_counts)
@@ -349,12 +387,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");
}
@@ -546,11 +584,13 @@ std::string replace_tempdir(std::string text, std::string tempdir_name, bool sho
}
std::string selfdir_name = proc_self_dirname();
- while (1) {
- size_t pos = text.find(selfdir_name);
- if (pos == std::string::npos)
- break;
- text = text.substr(0, pos) + "<yosys-exe-dir>/" + text.substr(pos + GetSize(selfdir_name));
+ if (selfdir_name != "/") {
+ while (1) {
+ size_t pos = text.find(selfdir_name);
+ if (pos == std::string::npos)
+ break;
+ text = text.substr(0, pos) + "<yosys-exe-dir>/" + text.substr(pos + GetSize(selfdir_name));
+ }
}
return text;
@@ -618,7 +658,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++;
@@ -722,7 +762,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);
@@ -750,7 +791,7 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin
extract_cell(c, keepff);
for (auto &wire_it : module->wires_) {
- if (wire_it.second->port_id > 0 || wire_it.second->get_bool_attribute("\\keep"))
+ if (wire_it.second->port_id > 0 || wire_it.second->get_bool_attribute(ID::keep))
mark_port(RTLIL::SigSpec(wire_it.second));
}
@@ -778,7 +819,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)
@@ -790,17 +831,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");
}
@@ -809,68 +850,72 @@ 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(NMUX)) {
+ 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, "0-0 1\n");
+ fprintf(f, "-01 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))
@@ -883,51 +928,54 @@ 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");
+ auto &cell_cost = cmos_cost ? CellCosts::cmos_gate_cost() : CellCosts::default_gate_cost();
+
buffer = stringf("%s/stdcells.genlib", tempdir_name.c_str());
f = fopen(buffer.c_str(), "wt");
if (f == NULL)
log_error("Opening %s for writing failed: %s\n", buffer.c_str(), strerror(errno));
fprintf(f, "GATE ZERO 1 Y=CONST0;\n");
fprintf(f, "GATE ONE 1 Y=CONST1;\n");
- fprintf(f, "GATE BUF %d Y=A; PIN * NONINV 1 999 1 0 1 0\n", get_cell_cost("$_BUF_"));
- fprintf(f, "GATE NOT %d Y=!A; PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_NOT_"));
- if (enabled_gates.empty() || enabled_gates.count("AND"))
- fprintf(f, "GATE AND %d Y=A*B; PIN * NONINV 1 999 1 0 1 0\n", get_cell_cost("$_AND_"));
- if (enabled_gates.empty() || enabled_gates.count("NAND"))
- fprintf(f, "GATE NAND %d Y=!(A*B); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_NAND_"));
- if (enabled_gates.empty() || enabled_gates.count("OR"))
- fprintf(f, "GATE OR %d Y=A+B; PIN * NONINV 1 999 1 0 1 0\n", get_cell_cost("$_OR_"));
- if (enabled_gates.empty() || enabled_gates.count("NOR"))
- fprintf(f, "GATE NOR %d Y=!(A+B); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_NOR_"));
- if (enabled_gates.empty() || enabled_gates.count("XOR"))
- fprintf(f, "GATE XOR %d Y=(A*!B)+(!A*B); PIN * UNKNOWN 1 999 1 0 1 0\n", get_cell_cost("$_XOR_"));
- if (enabled_gates.empty() || enabled_gates.count("XNOR"))
- fprintf(f, "GATE XNOR %d Y=(A*B)+(!A*!B); PIN * UNKNOWN 1 999 1 0 1 0\n", get_cell_cost("$_XNOR_"));
- if (enabled_gates.empty() || enabled_gates.count("ANDNOT"))
- fprintf(f, "GATE ANDNOT %d Y=A*!B; PIN * UNKNOWN 1 999 1 0 1 0\n", get_cell_cost("$_ANDNOT_"));
- if (enabled_gates.empty() || enabled_gates.count("ORNOT"))
- fprintf(f, "GATE ORNOT %d Y=A+!B; PIN * UNKNOWN 1 999 1 0 1 0\n", get_cell_cost("$_ORNOT_"));
- if (enabled_gates.empty() || enabled_gates.count("AOI3"))
- fprintf(f, "GATE AOI3 %d Y=!((A*B)+C); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_AOI3_"));
- if (enabled_gates.empty() || enabled_gates.count("OAI3"))
- fprintf(f, "GATE OAI3 %d Y=!((A+B)*C); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_OAI3_"));
- if (enabled_gates.empty() || enabled_gates.count("AOI4"))
- fprintf(f, "GATE AOI4 %d Y=!((A*B)+(C*D)); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_AOI4_"));
- if (enabled_gates.empty() || enabled_gates.count("OAI4"))
- fprintf(f, "GATE OAI4 %d Y=!((A+B)*(C+D)); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_OAI4_"));
- if (enabled_gates.empty() || enabled_gates.count("MUX"))
- fprintf(f, "GATE MUX %d Y=(A*B)+(S*B)+(!S*A); PIN * UNKNOWN 1 999 1 0 1 0\n", get_cell_cost("$_MUX_"));
+ fprintf(f, "GATE BUF %d Y=A; PIN * NONINV 1 999 1 0 1 0\n", cell_cost.at(ID($_BUF_)));
+ fprintf(f, "GATE NOT %d Y=!A; PIN * INV 1 999 1 0 1 0\n", cell_cost.at(ID($_NOT_)));
+ if (enabled_gates.count("AND"))
+ fprintf(f, "GATE AND %d Y=A*B; PIN * NONINV 1 999 1 0 1 0\n", cell_cost.at(ID($_AND_)));
+ if (enabled_gates.count("NAND"))
+ fprintf(f, "GATE NAND %d Y=!(A*B); PIN * INV 1 999 1 0 1 0\n", cell_cost.at(ID($_NAND_)));
+ if (enabled_gates.count("OR"))
+ fprintf(f, "GATE OR %d Y=A+B; PIN * NONINV 1 999 1 0 1 0\n", cell_cost.at(ID($_OR_)));
+ if (enabled_gates.count("NOR"))
+ fprintf(f, "GATE NOR %d Y=!(A+B); PIN * INV 1 999 1 0 1 0\n", cell_cost.at(ID($_NOR_)));
+ if (enabled_gates.count("XOR"))
+ fprintf(f, "GATE XOR %d Y=(A*!B)+(!A*B); PIN * UNKNOWN 1 999 1 0 1 0\n", cell_cost.at(ID($_XOR_)));
+ if (enabled_gates.count("XNOR"))
+ fprintf(f, "GATE XNOR %d Y=(A*B)+(!A*!B); PIN * UNKNOWN 1 999 1 0 1 0\n", cell_cost.at(ID($_XNOR_)));
+ if (enabled_gates.count("ANDNOT"))
+ fprintf(f, "GATE ANDNOT %d Y=A*!B; PIN * UNKNOWN 1 999 1 0 1 0\n", cell_cost.at(ID($_ANDNOT_)));
+ if (enabled_gates.count("ORNOT"))
+ fprintf(f, "GATE ORNOT %d Y=A+!B; PIN * UNKNOWN 1 999 1 0 1 0\n", cell_cost.at(ID($_ORNOT_)));
+ if (enabled_gates.count("AOI3"))
+ fprintf(f, "GATE AOI3 %d Y=!((A*B)+C); PIN * INV 1 999 1 0 1 0\n", cell_cost.at(ID($_AOI3_)));
+ if (enabled_gates.count("OAI3"))
+ fprintf(f, "GATE OAI3 %d Y=!((A+B)*C); PIN * INV 1 999 1 0 1 0\n", cell_cost.at(ID($_OAI3_)));
+ if (enabled_gates.count("AOI4"))
+ fprintf(f, "GATE AOI4 %d Y=!((A*B)+(C*D)); PIN * INV 1 999 1 0 1 0\n", cell_cost.at(ID($_AOI4_)));
+ if (enabled_gates.count("OAI4"))
+ fprintf(f, "GATE OAI4 %d Y=!((A+B)*(C+D)); PIN * INV 1 999 1 0 1 0\n", cell_cost.at(ID($_OAI4_)));
+ if (enabled_gates.count("MUX"))
+ fprintf(f, "GATE MUX %d Y=(A*B)+(S*B)+(!S*A); PIN * UNKNOWN 1 999 1 0 1 0\n", cell_cost.at(ID($_MUX_)));
+ if (enabled_gates.count("NMUX"))
+ fprintf(f, "GATE NMUX %d Y=!((A*B)+(S*B)+(!S*A)); PIN * UNKNOWN 1 999 1 0 1 0\n", cell_cost.at(ID($_NMUX_)));
if (map_mux4)
- fprintf(f, "GATE MUX4 %d Y=(!S*!T*A)+(S*!T*B)+(!S*T*C)+(S*T*D); PIN * UNKNOWN 1 999 1 0 1 0\n", 2*get_cell_cost("$_MUX_"));
+ fprintf(f, "GATE MUX4 %d Y=(!S*!T*A)+(S*!T*B)+(!S*T*C)+(S*T*D); PIN * UNKNOWN 1 999 1 0 1 0\n", 2*cell_cost.at(ID($_MUX_)));
if (map_mux8)
- fprintf(f, "GATE MUX8 %d Y=(!S*!T*!U*A)+(S*!T*!U*B)+(!S*T*!U*C)+(S*T*!U*D)+(!S*!T*U*E)+(S*!T*U*F)+(!S*T*U*G)+(S*T*U*H); PIN * UNKNOWN 1 999 1 0 1 0\n", 4*get_cell_cost("$_MUX_"));
+ fprintf(f, "GATE MUX8 %d Y=(!S*!T*!U*A)+(S*!T*!U*B)+(!S*T*!U*C)+(S*T*!U*D)+(!S*!T*U*E)+(S*!T*U*F)+(!S*T*U*G)+(S*T*U*H); PIN * UNKNOWN 1 999 1 0 1 0\n", 4*cell_cost.at(ID($_MUX_)));
if (map_mux16)
- fprintf(f, "GATE MUX16 %d Y=(!S*!T*!U*!V*A)+(S*!T*!U*!V*B)+(!S*T*!U*!V*C)+(S*T*!U*!V*D)+(!S*!T*U*!V*E)+(S*!T*U*!V*F)+(!S*T*U*!V*G)+(S*T*U*!V*H)+(!S*!T*!U*V*I)+(S*!T*!U*V*J)+(!S*T*!U*V*K)+(S*T*!U*V*L)+(!S*!T*U*V*M)+(S*!T*U*V*N)+(!S*T*U*V*O)+(S*T*U*V*P); PIN * UNKNOWN 1 999 1 0 1 0\n", 8*get_cell_cost("$_MUX_"));
+ fprintf(f, "GATE MUX16 %d Y=(!S*!T*!U*!V*A)+(S*!T*!U*!V*B)+(!S*T*!U*!V*C)+(S*T*!U*!V*D)+(!S*!T*U*!V*E)+(S*!T*U*!V*F)+(!S*T*U*!V*G)+(S*T*U*!V*H)+(!S*!T*!U*V*I)+(S*!T*!U*V*J)+(!S*T*!U*V*K)+(S*T*!U*V*L)+(!S*!T*U*V*M)+(S*!T*U*V*N)+(!S*T*U*V*O)+(S*T*U*V*P); PIN * UNKNOWN 1 999 1 0 1 0\n", 8*cell_cost.at(ID($_MUX_)));
fclose(f);
if (!lut_costs.empty()) {
@@ -943,8 +991,24 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin
buffer = stringf("%s -s -f %s/abc.script 2>&1", exe_file.c_str(), tempdir_name.c_str());
log("Running ABC command: %s\n", replace_tempdir(buffer, tempdir_name, show_tempdir).c_str());
+#ifndef YOSYS_LINK_ABC
abc_output_filter filt(tempdir_name, show_tempdir);
int ret = run_command(buffer, std::bind(&abc_output_filter::next_line, filt, std::placeholders::_1));
+#else
+ // These needs to be mutable, supposedly due to getopt
+ char *abc_argv[5];
+ string tmp_script_name = stringf("%s/abc.script", tempdir_name.c_str());
+ abc_argv[0] = strdup(exe_file.c_str());
+ abc_argv[1] = strdup("-s");
+ abc_argv[2] = strdup("-f");
+ abc_argv[3] = strdup(tmp_script_name.c_str());
+ abc_argv[4] = 0;
+ int ret = Abc_RealMain(4, abc_argv);
+ free(abc_argv[0]);
+ free(abc_argv[1]);
+ free(abc_argv[2]);
+ free(abc_argv[3]);
+#endif
if (ret != 0)
log_error("ABC: execution of command \"%s\" failed: return code %d.\n", buffer.c_str(), ret);
@@ -956,18 +1020,21 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin
bool builtin_lib = liberty_file.empty();
RTLIL::Design *mapped_design = new RTLIL::Design;
- parse_blif(mapped_design, ifs, builtin_lib ? "\\DFF" : "\\_dff_", false, sop_mode);
+ parse_blif(mapped_design, ifs, builtin_lib ? ID(DFF) : ID(_dff_), false, sop_mode);
ifs.close();
log_header(design, "Re-integrating ABC results.\n");
- RTLIL::Module *mapped_mod = mapped_design->modules_["\\netlist"];
+ RTLIL::Module *mapped_mod = mapped_design->modules_[ID(netlist)];
if (mapped_mod == NULL)
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));
- if (markgroups) wire->attributes["\\abcgroup"] = map_autoidx;
+ RTLIL::Wire *orig_wire = nullptr;
+ RTLIL::Wire *wire = module->addWire(remap_name(w->name, &orig_wire));
+ if (orig_wire != nullptr && orig_wire->attributes.count(ID(src)))
+ wire->attributes[ID(src)] = orig_wire->attributes[ID(src)];
+ if (markgroups) wire->attributes[ID(abcgroup)] = map_autoidx;
design->select(module, wire);
}
@@ -977,183 +1044,182 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin
if (builtin_lib)
{
cell_stats[RTLIL::unescape_id(c->type)]++;
- if (c->type == "\\ZERO" || c->type == "\\ONE") {
+ if (c->type.in(ID(ZERO), ID(ONE))) {
RTLIL::SigSig conn;
- conn.first = RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\Y").as_wire()->name)]);
- conn.second = RTLIL::SigSpec(c->type == "\\ZERO" ? 0 : 1, 1);
+ conn.first = RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::Y).as_wire()->name)]);
+ conn.second = RTLIL::SigSpec(c->type == ID(ZERO) ? 0 : 1, 1);
module->connect(conn);
continue;
}
- if (c->type == "\\BUF") {
+ if (c->type == ID(BUF)) {
RTLIL::SigSig conn;
- conn.first = RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\Y").as_wire()->name)]);
- conn.second = RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\A").as_wire()->name)]);
+ conn.first = RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::Y).as_wire()->name)]);
+ conn.second = RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::A).as_wire()->name)]);
module->connect(conn);
continue;
}
- if (c->type == "\\NOT") {
- RTLIL::Cell *cell = module->addCell(remap_name(c->name), "$_NOT_");
- if (markgroups) cell->attributes["\\abcgroup"] = map_autoidx;
- cell->setPort("\\A", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\A").as_wire()->name)]));
- cell->setPort("\\Y", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\Y").as_wire()->name)]));
+ if (c->type == ID(NOT)) {
+ RTLIL::Cell *cell = module->addCell(remap_name(c->name), ID($_NOT_));
+ if (markgroups) cell->attributes[ID(abcgroup)] = map_autoidx;
+ cell->setPort(ID::A, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::A).as_wire()->name)]));
+ cell->setPort(ID::Y, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::Y).as_wire()->name)]));
design->select(module, cell);
continue;
}
- if (c->type == "\\AND" || c->type == "\\OR" || c->type == "\\XOR" || c->type == "\\NAND" || c->type == "\\NOR" ||
- c->type == "\\XNOR" || c->type == "\\ANDNOT" || c->type == "\\ORNOT") {
- RTLIL::Cell *cell = module->addCell(remap_name(c->name), "$_" + c->type.substr(1) + "_");
- if (markgroups) cell->attributes["\\abcgroup"] = map_autoidx;
- cell->setPort("\\A", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\A").as_wire()->name)]));
- cell->setPort("\\B", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\B").as_wire()->name)]));
- cell->setPort("\\Y", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\Y").as_wire()->name)]));
+ if (c->type.in(ID(AND), ID(OR), ID(XOR), ID(NAND), ID(NOR), ID(XNOR), ID(ANDNOT), ID(ORNOT))) {
+ RTLIL::Cell *cell = module->addCell(remap_name(c->name), stringf("$_%s_", c->type.c_str()+1));
+ if (markgroups) cell->attributes[ID(abcgroup)] = map_autoidx;
+ cell->setPort(ID::A, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::A).as_wire()->name)]));
+ cell->setPort(ID::B, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::B).as_wire()->name)]));
+ cell->setPort(ID::Y, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::Y).as_wire()->name)]));
design->select(module, cell);
continue;
}
- if (c->type == "\\MUX") {
- RTLIL::Cell *cell = module->addCell(remap_name(c->name), "$_MUX_");
- if (markgroups) cell->attributes["\\abcgroup"] = map_autoidx;
- cell->setPort("\\A", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\A").as_wire()->name)]));
- cell->setPort("\\B", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\B").as_wire()->name)]));
- cell->setPort("\\S", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\S").as_wire()->name)]));
- cell->setPort("\\Y", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\Y").as_wire()->name)]));
+ if (c->type.in(ID(MUX), ID(NMUX))) {
+ RTLIL::Cell *cell = module->addCell(remap_name(c->name), stringf("$_%s_", c->type.c_str()+1));
+ if (markgroups) cell->attributes[ID(abcgroup)] = map_autoidx;
+ cell->setPort(ID::A, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::A).as_wire()->name)]));
+ cell->setPort(ID::B, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::B).as_wire()->name)]));
+ cell->setPort(ID(S), RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID(S)).as_wire()->name)]));
+ cell->setPort(ID::Y, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::Y).as_wire()->name)]));
design->select(module, cell);
continue;
}
- if (c->type == "\\MUX4") {
- RTLIL::Cell *cell = module->addCell(remap_name(c->name), "$_MUX4_");
- if (markgroups) cell->attributes["\\abcgroup"] = map_autoidx;
- cell->setPort("\\A", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\A").as_wire()->name)]));
- cell->setPort("\\B", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\B").as_wire()->name)]));
- cell->setPort("\\C", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\C").as_wire()->name)]));
- cell->setPort("\\D", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\D").as_wire()->name)]));
- cell->setPort("\\S", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\S").as_wire()->name)]));
- cell->setPort("\\T", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\T").as_wire()->name)]));
- cell->setPort("\\Y", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\Y").as_wire()->name)]));
+ if (c->type == ID(MUX4)) {
+ RTLIL::Cell *cell = module->addCell(remap_name(c->name), ID($_MUX4_));
+ if (markgroups) cell->attributes[ID(abcgroup)] = map_autoidx;
+ cell->setPort(ID::A, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::A).as_wire()->name)]));
+ cell->setPort(ID::B, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::B).as_wire()->name)]));
+ cell->setPort(ID(C), RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID(C)).as_wire()->name)]));
+ cell->setPort(ID(D), RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID(D)).as_wire()->name)]));
+ cell->setPort(ID(S), RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID(S)).as_wire()->name)]));
+ cell->setPort(ID(T), RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID(T)).as_wire()->name)]));
+ cell->setPort(ID::Y, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::Y).as_wire()->name)]));
design->select(module, cell);
continue;
}
- if (c->type == "\\MUX8") {
- RTLIL::Cell *cell = module->addCell(remap_name(c->name), "$_MUX8_");
- if (markgroups) cell->attributes["\\abcgroup"] = map_autoidx;
- cell->setPort("\\A", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\A").as_wire()->name)]));
- cell->setPort("\\B", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\B").as_wire()->name)]));
- cell->setPort("\\C", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\C").as_wire()->name)]));
- cell->setPort("\\D", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\D").as_wire()->name)]));
- cell->setPort("\\E", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\E").as_wire()->name)]));
- cell->setPort("\\F", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\F").as_wire()->name)]));
- cell->setPort("\\G", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\G").as_wire()->name)]));
- cell->setPort("\\H", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\H").as_wire()->name)]));
- cell->setPort("\\S", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\S").as_wire()->name)]));
- cell->setPort("\\T", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\T").as_wire()->name)]));
- cell->setPort("\\U", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\U").as_wire()->name)]));
- cell->setPort("\\Y", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\Y").as_wire()->name)]));
+ if (c->type == ID(MUX8)) {
+ RTLIL::Cell *cell = module->addCell(remap_name(c->name), ID($_MUX8_));
+ if (markgroups) cell->attributes[ID(abcgroup)] = map_autoidx;
+ cell->setPort(ID::A, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::A).as_wire()->name)]));
+ cell->setPort(ID::B, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::B).as_wire()->name)]));
+ cell->setPort(ID(C), RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID(C)).as_wire()->name)]));
+ cell->setPort(ID(D), RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID(D)).as_wire()->name)]));
+ cell->setPort(ID(E), RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID(E)).as_wire()->name)]));
+ cell->setPort(ID(F), RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID(F)).as_wire()->name)]));
+ cell->setPort(ID(G), RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID(G)).as_wire()->name)]));
+ cell->setPort(ID(H), RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID(H)).as_wire()->name)]));
+ cell->setPort(ID(S), RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID(S)).as_wire()->name)]));
+ cell->setPort(ID(T), RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID(T)).as_wire()->name)]));
+ cell->setPort(ID(U), RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID(U)).as_wire()->name)]));
+ cell->setPort(ID::Y, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::Y).as_wire()->name)]));
design->select(module, cell);
continue;
}
- if (c->type == "\\MUX16") {
- RTLIL::Cell *cell = module->addCell(remap_name(c->name), "$_MUX16_");
- if (markgroups) cell->attributes["\\abcgroup"] = map_autoidx;
- cell->setPort("\\A", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\A").as_wire()->name)]));
- cell->setPort("\\B", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\B").as_wire()->name)]));
- cell->setPort("\\C", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\C").as_wire()->name)]));
- cell->setPort("\\D", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\D").as_wire()->name)]));
- cell->setPort("\\E", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\E").as_wire()->name)]));
- cell->setPort("\\F", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\F").as_wire()->name)]));
- cell->setPort("\\G", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\G").as_wire()->name)]));
- cell->setPort("\\H", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\H").as_wire()->name)]));
- cell->setPort("\\I", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\I").as_wire()->name)]));
- cell->setPort("\\J", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\J").as_wire()->name)]));
- cell->setPort("\\K", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\K").as_wire()->name)]));
- cell->setPort("\\L", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\L").as_wire()->name)]));
- cell->setPort("\\M", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\M").as_wire()->name)]));
- cell->setPort("\\N", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\N").as_wire()->name)]));
- cell->setPort("\\O", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\O").as_wire()->name)]));
- cell->setPort("\\P", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\P").as_wire()->name)]));
- cell->setPort("\\S", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\S").as_wire()->name)]));
- cell->setPort("\\T", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\T").as_wire()->name)]));
- cell->setPort("\\U", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\U").as_wire()->name)]));
- cell->setPort("\\V", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\V").as_wire()->name)]));
- cell->setPort("\\Y", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\Y").as_wire()->name)]));
+ if (c->type == ID(MUX16)) {
+ RTLIL::Cell *cell = module->addCell(remap_name(c->name), ID($_MUX16_));
+ if (markgroups) cell->attributes[ID(abcgroup)] = map_autoidx;
+ cell->setPort(ID::A, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::A).as_wire()->name)]));
+ cell->setPort(ID::B, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::B).as_wire()->name)]));
+ cell->setPort(ID(C), RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID(C)).as_wire()->name)]));
+ cell->setPort(ID(D), RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID(D)).as_wire()->name)]));
+ cell->setPort(ID(E), RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID(E)).as_wire()->name)]));
+ cell->setPort(ID(F), RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID(F)).as_wire()->name)]));
+ cell->setPort(ID(G), RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID(G)).as_wire()->name)]));
+ cell->setPort(ID(H), RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID(H)).as_wire()->name)]));
+ cell->setPort(ID(I), RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID(I)).as_wire()->name)]));
+ cell->setPort(ID(J), RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID(J)).as_wire()->name)]));
+ cell->setPort(ID(K), RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID(K)).as_wire()->name)]));
+ cell->setPort(ID(L), RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID(L)).as_wire()->name)]));
+ cell->setPort(ID(M), RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID(M)).as_wire()->name)]));
+ cell->setPort(ID(N), RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID(N)).as_wire()->name)]));
+ cell->setPort(ID(O), RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID(O)).as_wire()->name)]));
+ cell->setPort(ID(P), RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID(P)).as_wire()->name)]));
+ cell->setPort(ID(S), RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID(S)).as_wire()->name)]));
+ cell->setPort(ID(T), RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID(T)).as_wire()->name)]));
+ cell->setPort(ID(U), RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID(U)).as_wire()->name)]));
+ cell->setPort(ID(V), RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID(V)).as_wire()->name)]));
+ cell->setPort(ID::Y, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::Y).as_wire()->name)]));
design->select(module, cell);
continue;
}
- if (c->type == "\\AOI3" || c->type == "\\OAI3") {
- RTLIL::Cell *cell = module->addCell(remap_name(c->name), "$_" + c->type.substr(1) + "_");
- if (markgroups) cell->attributes["\\abcgroup"] = map_autoidx;
- cell->setPort("\\A", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\A").as_wire()->name)]));
- cell->setPort("\\B", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\B").as_wire()->name)]));
- cell->setPort("\\C", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\C").as_wire()->name)]));
- cell->setPort("\\Y", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\Y").as_wire()->name)]));
+ if (c->type.in(ID(AOI3), ID(OAI3))) {
+ RTLIL::Cell *cell = module->addCell(remap_name(c->name), stringf("$_%s_", c->type.c_str()+1));
+ if (markgroups) cell->attributes[ID(abcgroup)] = map_autoidx;
+ cell->setPort(ID::A, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::A).as_wire()->name)]));
+ cell->setPort(ID::B, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::B).as_wire()->name)]));
+ cell->setPort(ID(C), RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID(C)).as_wire()->name)]));
+ cell->setPort(ID::Y, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::Y).as_wire()->name)]));
design->select(module, cell);
continue;
}
- if (c->type == "\\AOI4" || c->type == "\\OAI4") {
- RTLIL::Cell *cell = module->addCell(remap_name(c->name), "$_" + c->type.substr(1) + "_");
- if (markgroups) cell->attributes["\\abcgroup"] = map_autoidx;
- cell->setPort("\\A", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\A").as_wire()->name)]));
- cell->setPort("\\B", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\B").as_wire()->name)]));
- cell->setPort("\\C", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\C").as_wire()->name)]));
- cell->setPort("\\D", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\D").as_wire()->name)]));
- cell->setPort("\\Y", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\Y").as_wire()->name)]));
+ if (c->type.in(ID(AOI4), ID(OAI4))) {
+ RTLIL::Cell *cell = module->addCell(remap_name(c->name), stringf("$_%s_", c->type.c_str()+1));
+ if (markgroups) cell->attributes[ID(abcgroup)] = map_autoidx;
+ cell->setPort(ID::A, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::A).as_wire()->name)]));
+ cell->setPort(ID::B, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::B).as_wire()->name)]));
+ cell->setPort(ID(C), RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID(C)).as_wire()->name)]));
+ cell->setPort(ID(D), RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID(D)).as_wire()->name)]));
+ cell->setPort(ID::Y, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::Y).as_wire()->name)]));
design->select(module, cell);
continue;
}
- if (c->type == "\\DFF") {
+ if (c->type == ID(DFF)) {
log_assert(clk_sig.size() == 1);
RTLIL::Cell *cell;
if (en_sig.size() == 0) {
- cell = module->addCell(remap_name(c->name), clk_polarity ? "$_DFF_P_" : "$_DFF_N_");
+ cell = module->addCell(remap_name(c->name), clk_polarity ? ID($_DFF_P_) : ID($_DFF_N_));
} else {
log_assert(en_sig.size() == 1);
cell = module->addCell(remap_name(c->name), stringf("$_DFFE_%c%c_", clk_polarity ? 'P' : 'N', en_polarity ? 'P' : 'N'));
- cell->setPort("\\E", en_sig);
+ cell->setPort(ID(E), en_sig);
}
- if (markgroups) cell->attributes["\\abcgroup"] = map_autoidx;
- cell->setPort("\\D", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\D").as_wire()->name)]));
- cell->setPort("\\Q", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\Q").as_wire()->name)]));
- cell->setPort("\\C", clk_sig);
+ if (markgroups) cell->attributes[ID(abcgroup)] = map_autoidx;
+ cell->setPort(ID(D), RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID(D)).as_wire()->name)]));
+ cell->setPort(ID(Q), RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID(Q)).as_wire()->name)]));
+ cell->setPort(ID(C), clk_sig);
design->select(module, cell);
continue;
}
}
+ else
+ cell_stats[RTLIL::unescape_id(c->type)]++;
- cell_stats[RTLIL::unescape_id(c->type)]++;
-
- if (c->type == "\\_const0_" || c->type == "\\_const1_") {
+ if (c->type.in(ID(_const0_), ID(_const1_))) {
RTLIL::SigSig conn;
conn.first = RTLIL::SigSpec(module->wires_[remap_name(c->connections().begin()->second.as_wire()->name)]);
- conn.second = RTLIL::SigSpec(c->type == "\\_const0_" ? 0 : 1, 1);
+ conn.second = RTLIL::SigSpec(c->type == ID(_const0_) ? 0 : 1, 1);
module->connect(conn);
continue;
}
- if (c->type == "\\_dff_") {
+ if (c->type == ID(_dff_)) {
log_assert(clk_sig.size() == 1);
RTLIL::Cell *cell;
if (en_sig.size() == 0) {
- cell = module->addCell(remap_name(c->name), clk_polarity ? "$_DFF_P_" : "$_DFF_N_");
+ cell = module->addCell(remap_name(c->name), clk_polarity ? ID($_DFF_P_) : ID($_DFF_N_));
} else {
log_assert(en_sig.size() == 1);
cell = module->addCell(remap_name(c->name), stringf("$_DFFE_%c%c_", clk_polarity ? 'P' : 'N', en_polarity ? 'P' : 'N'));
- cell->setPort("\\E", en_sig);
+ cell->setPort(ID(E), en_sig);
}
- if (markgroups) cell->attributes["\\abcgroup"] = map_autoidx;
- cell->setPort("\\D", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\D").as_wire()->name)]));
- cell->setPort("\\Q", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\Q").as_wire()->name)]));
- cell->setPort("\\C", clk_sig);
+ if (markgroups) cell->attributes[ID(abcgroup)] = map_autoidx;
+ cell->setPort(ID(D), RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID(D)).as_wire()->name)]));
+ cell->setPort(ID(Q), RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID(Q)).as_wire()->name)]));
+ cell->setPort(ID(C), clk_sig);
design->select(module, cell);
continue;
}
- if (c->type == "$lut" && GetSize(c->getPort("\\A")) == 1 && c->getParam("\\LUT").as_int() == 2) {
- SigSpec my_a = module->wires_[remap_name(c->getPort("\\A").as_wire()->name)];
- SigSpec my_y = module->wires_[remap_name(c->getPort("\\Y").as_wire()->name)];
+ if (c->type == ID($lut) && GetSize(c->getPort(ID::A)) == 1 && c->getParam(ID(LUT)).as_int() == 2) {
+ SigSpec my_a = module->wires_[remap_name(c->getPort(ID::A).as_wire()->name)];
+ SigSpec my_y = module->wires_[remap_name(c->getPort(ID::Y).as_wire()->name)];
module->connect(my_y, my_a);
continue;
}
RTLIL::Cell *cell = module->addCell(remap_name(c->name), c->type);
- if (markgroups) cell->attributes["\\abcgroup"] = map_autoidx;
+ if (markgroups) cell->attributes[ID(abcgroup)] = map_autoidx;
cell->parameters = c->parameters;
for (auto &conn : c->connections()) {
RTLIL::SigSpec newsig;
@@ -1178,10 +1244,10 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin
if (recover_init)
for (auto wire : mapped_mod->wires()) {
- if (wire->attributes.count("\\init")) {
+ if (wire->attributes.count(ID(init))) {
Wire *w = module->wires_[remap_name(wire->name)];
- log_assert(w->attributes.count("\\init") == 0);
- w->attributes["\\init"] = wire->attributes.at("\\init");
+ log_assert(w->attributes.count(ID(init)) == 0);
+ w->attributes[ID(init)] = wire->attributes.at(ID(init));
}
}
@@ -1191,7 +1257,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;
@@ -1226,7 +1292,7 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin
struct AbcPass : public Pass {
AbcPass() : Pass("abc", "use ABC for technology mapping") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -1345,20 +1411,27 @@ struct AbcPass : public Pass {
// log("\n");
log(" -g type1,type2,...\n");
log(" Map to the specified list of gate types. Supported gates types are:\n");
- log(" AND, NAND, OR, NOR, XOR, XNOR, ANDNOT, ORNOT, MUX, AOI3, OAI3, AOI4, OAI4.\n");
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log(" AND, NAND, OR, NOR, XOR, XNOR, ANDNOT, ORNOT, MUX,\n");
+ log(" NMUX, AOI3, OAI3, AOI4, OAI4.\n");
log(" (The NOT gate is always added to this list automatically.)\n");
log("\n");
log(" The following aliases can be used to reference common sets of gate types:\n");
log(" simple: AND OR XOR MUX\n");
- log(" cmos2: NAND NOR\n");
- log(" cmos3: NAND NOR AOI3 OAI3\n");
- log(" cmos4: NAND NOR AOI3 OAI3 AOI4 OAI4\n");
- log(" gates: AND NAND OR NOR XOR XNOR ANDNOT ORNOT\n");
- log(" aig: AND NAND OR NOR ANDNOT ORNOT\n");
+ log(" cmos2: NAND NOR\n");
+ log(" cmos3: NAND NOR AOI3 OAI3\n");
+ log(" cmos4: NAND NOR AOI3 OAI3 AOI4 OAI4\n");
+ log(" cmos: NAND NOR AOI3 OAI3 AOI4 OAI4 NMUX MUX XOR XNOR\n");
+ log(" gates: AND NAND OR NOR XOR XNOR ANDNOT ORNOT\n");
+ log(" aig: AND NAND OR NOR ANDNOT ORNOT\n");
+ log("\n");
+ log(" The alias 'all' represent the full set of all gate types.\n");
log("\n");
log(" Prefix a gate type with a '-' to remove it from the list. For example\n");
log(" the arguments 'AND,OR,XOR' and 'simple,-MUX' are equivalent.\n");
log("\n");
+ log(" The default is 'all,-NMUX,-AOI3,-OAI3,-AOI4,-OAI4'.\n");
+ log("\n");
log(" -dff\n");
log(" also pass $_DFF_?_ and $_DFFE_??_ cells through ABC. modules with many\n");
log(" clock domains are automatically partitioned in clock domains and each\n");
@@ -1385,16 +1458,25 @@ 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");
- log("This pass does not operate on modules with unprocessed processes in it.\n");
- log("(I.e. the 'proc' pass should be used first to convert processes to netlists.)\n");
+ log("Note that this is a logic optimization pass within Yosys that is calling ABC\n");
+ log("internally. This is not going to \"run ABC on your design\". It will instead run\n");
+ log("ABC on logic snippets extracted from your design. You will not get any useful\n");
+ log("output when passing an ABC script that writes a file. Instead write your full\n");
+ log("design as BLIF file with write_blif and then load that into ABC externally if\n");
+ log("you want to use ABC to convert your design into another format.\n");
log("\n");
log("[1] http://www.eecs.berkeley.edu/~alanmi/abc/\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
log_header(design, "Executing ABC pass (technology mapping using ABC).\n");
log_push();
@@ -1415,6 +1497,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;
@@ -1422,6 +1505,7 @@ struct AbcPass : public Pass {
map_mux8 = false;
map_mux16 = false;
enabled_gates.clear();
+ cmos_cost = false;
#ifdef _WIN32
#ifndef ABCEXTERNAL
@@ -1430,7 +1514,47 @@ struct AbcPass : public Pass {
#endif
#endif
- size_t argidx;
+ // get arguments from scratchpad first, then override by command arguments
+ std::string lut_arg, luts_arg, g_arg;
+ exe_file = design->scratchpad_get_string("abc.exe", exe_file /* inherit default value if not set */);
+ script_file = design->scratchpad_get_string("abc.script", script_file);
+ liberty_file = design->scratchpad_get_string("abc.liberty", liberty_file);
+ constr_file = design->scratchpad_get_string("abc.constr", constr_file);
+ if (design->scratchpad.count("abc.D")) {
+ delay_target = "-D " + design->scratchpad_get_string("abc.D");
+ }
+ if (design->scratchpad.count("abc.I")) {
+ sop_inputs = "-I " + design->scratchpad_get_string("abc.I");
+ }
+ if (design->scratchpad.count("abc.P")) {
+ sop_products = "-P " + design->scratchpad_get_string("abc.P");
+ }
+ if (design->scratchpad.count("abc.S")) {
+ lutin_shared = "-S " + design->scratchpad_get_string("abc.S");
+ }
+ lut_arg = design->scratchpad_get_string("abc.lut", lut_arg);
+ luts_arg = design->scratchpad_get_string("abc.luts", luts_arg);
+ sop_mode = design->scratchpad_get_bool("abc.sop", sop_mode);
+ map_mux4 = design->scratchpad_get_bool("abc.mux4", map_mux4);
+ map_mux8 = design->scratchpad_get_bool("abc.mux8", map_mux8);
+ map_mux16 = design->scratchpad_get_bool("abc.mux16", map_mux16);
+ abc_dress = design->scratchpad_get_bool("abc.dress", abc_dress);
+ g_arg = design->scratchpad_get_string("abc.g", g_arg);
+
+ fast_mode = design->scratchpad_get_bool("abc.fast", fast_mode);
+ dff_mode = design->scratchpad_get_bool("abc.dff", dff_mode);
+ if (design->scratchpad.count("abc.clk")) {
+ clk_str = design->scratchpad_get_string("abc.clk");
+ dff_mode = true;
+ }
+ keepff = design->scratchpad_get_bool("abc.keepff", keepff);
+ cleanup = !design->scratchpad_get_bool("abc.nocleanup", !cleanup);
+ keepff = design->scratchpad_get_bool("abc.keepff", keepff);
+ show_tempdir = design->scratchpad_get_bool("abc.showtmp", show_tempdir);
+ markgroups = design->scratchpad_get_bool("abc.markgroups", markgroups);
+
+ size_t argidx, g_argidx;
+ bool g_arg_from_cmd = false;
char pwd [PATH_MAX];
if (!getcwd(pwd, sizeof(pwd))) {
log_cmd_error("getcwd failed: %s\n", strerror(errno));
@@ -1444,23 +1568,14 @@ struct AbcPass : public Pass {
}
if (arg == "-script" && argidx+1 < args.size()) {
script_file = args[++argidx];
- rewrite_filename(script_file);
- if (!script_file.empty() && !is_absolute_path(script_file) && script_file[0] != '+')
- script_file = std::string(pwd) + "/" + script_file;
continue;
}
if (arg == "-liberty" && argidx+1 < args.size()) {
liberty_file = args[++argidx];
- rewrite_filename(liberty_file);
- if (!liberty_file.empty() && !is_absolute_path(liberty_file))
- liberty_file = std::string(pwd) + "/" + liberty_file;
continue;
}
if (arg == "-constr" && argidx+1 < args.size()) {
- rewrite_filename(constr_file);
constr_file = args[++argidx];
- if (!constr_file.empty() && !is_absolute_path(constr_file))
- constr_file = std::string(pwd) + "/" + constr_file;
continue;
}
if (arg == "-D" && argidx+1 < args.size()) {
@@ -1480,37 +1595,11 @@ struct AbcPass : public Pass {
continue;
}
if (arg == "-lut" && argidx+1 < args.size()) {
- string arg = args[++argidx];
- size_t pos = arg.find_first_of(':');
- int lut_mode = 0, lut_mode2 = 0;
- if (pos != string::npos) {
- lut_mode = atoi(arg.substr(0, pos).c_str());
- lut_mode2 = atoi(arg.substr(pos+1).c_str());
- } else {
- lut_mode = atoi(arg.c_str());
- lut_mode2 = lut_mode;
- }
- lut_costs.clear();
- for (int i = 0; i < lut_mode; i++)
- lut_costs.push_back(1);
- for (int i = lut_mode; i < lut_mode2; i++)
- lut_costs.push_back(2 << (i - lut_mode));
+ lut_arg = args[++argidx];
continue;
}
if (arg == "-luts" && argidx+1 < args.size()) {
- lut_costs.clear();
- for (auto &tok : split_tokens(args[++argidx], ",")) {
- auto parts = split_tokens(tok, ":");
- if (GetSize(parts) == 0 && !lut_costs.empty())
- lut_costs.push_back(lut_costs.back());
- else if (GetSize(parts) == 1)
- lut_costs.push_back(atoi(parts.at(0).c_str()));
- else if (GetSize(parts) == 2)
- while (GetSize(lut_costs) < atoi(parts.at(0).c_str()))
- lut_costs.push_back(atoi(parts.at(1).c_str()));
- else
- log_cmd_error("Invalid -luts syntax.\n");
- }
+ luts_arg = args[++argidx];
continue;
}
if (arg == "-sop") {
@@ -1529,86 +1618,16 @@ 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;
- bool remove_gates = false;
- if (GetSize(g) > 0 && g[0] == '-') {
- remove_gates = true;
- g = g.substr(1);
- }
- if (g == "AND") goto ok_gate;
- if (g == "NAND") goto ok_gate;
- if (g == "OR") goto ok_gate;
- if (g == "NOR") goto ok_gate;
- if (g == "XOR") goto ok_gate;
- if (g == "XNOR") goto ok_gate;
- if (g == "ANDNOT") goto ok_gate;
- if (g == "ORNOT") goto ok_gate;
- if (g == "MUX") goto ok_gate;
- if (g == "AOI3") goto ok_gate;
- if (g == "OAI3") goto ok_gate;
- if (g == "AOI4") goto ok_gate;
- if (g == "OAI4") goto ok_gate;
- if (g == "simple") {
- gate_list.push_back("AND");
- gate_list.push_back("OR");
- gate_list.push_back("XOR");
- gate_list.push_back("MUX");
- goto ok_alias;
- }
- if (g == "cmos2") {
- gate_list.push_back("NAND");
- gate_list.push_back("NOR");
- goto ok_alias;
- }
- if (g == "cmos3") {
- gate_list.push_back("NAND");
- gate_list.push_back("NOR");
- gate_list.push_back("AOI3");
- gate_list.push_back("OAI3");
- goto ok_alias;
- }
- if (g == "cmos4") {
- gate_list.push_back("NAND");
- gate_list.push_back("NOR");
- gate_list.push_back("AOI3");
- gate_list.push_back("OAI3");
- gate_list.push_back("AOI4");
- gate_list.push_back("OAI4");
- goto ok_alias;
- }
- if (g == "gates") {
- gate_list.push_back("AND");
- gate_list.push_back("NAND");
- gate_list.push_back("OR");
- gate_list.push_back("NOR");
- gate_list.push_back("XOR");
- gate_list.push_back("XNOR");
- gate_list.push_back("ANDNOT");
- gate_list.push_back("ORNOT");
- goto ok_alias;
- }
- if (g == "aig") {
- gate_list.push_back("AND");
- gate_list.push_back("NAND");
- gate_list.push_back("OR");
- gate_list.push_back("NOR");
- gate_list.push_back("ANDNOT");
- gate_list.push_back("ORNOT");
- goto ok_alias;
- }
- cmd_error(args, argidx, stringf("Unsupported gate type: %s", g.c_str()));
- ok_gate:
- gate_list.push_back(g);
- ok_alias:
- for (auto gate : gate_list) {
- if (remove_gates)
- enabled_gates.erase(gate);
- else
- enabled_gates.insert(gate);
- }
- }
+ if (g_arg_from_cmd)
+ log_cmd_error("Can only use -g once. Please combine.");
+ g_arg = args[++argidx];
+ g_argidx = argidx;
+ g_arg_from_cmd = true;
continue;
}
if (arg == "-fast") {
@@ -1644,11 +1663,196 @@ struct AbcPass : public Pass {
}
extra_args(args, argidx, design);
+ rewrite_filename(script_file);
+ if (!script_file.empty() && !is_absolute_path(script_file) && script_file[0] != '+')
+ script_file = std::string(pwd) + "/" + script_file;
+ rewrite_filename(liberty_file);
+ if (!liberty_file.empty() && !is_absolute_path(liberty_file))
+ liberty_file = std::string(pwd) + "/" + liberty_file;
+ rewrite_filename(constr_file);
+ if (!constr_file.empty() && !is_absolute_path(constr_file))
+ constr_file = std::string(pwd) + "/" + constr_file;
+
+ // handle -lut argument
+ if (!lut_arg.empty()) {
+ size_t pos = lut_arg.find_first_of(':');
+ int lut_mode = 0, lut_mode2 = 0;
+ if (pos != string::npos) {
+ lut_mode = atoi(lut_arg.substr(0, pos).c_str());
+ lut_mode2 = atoi(lut_arg.substr(pos+1).c_str());
+ } else {
+ lut_mode = atoi(lut_arg.c_str());
+ lut_mode2 = lut_mode;
+ }
+ lut_costs.clear();
+ for (int i = 0; i < lut_mode; i++)
+ lut_costs.push_back(1);
+ for (int i = lut_mode; i < lut_mode2; i++)
+ lut_costs.push_back(2 << (i - lut_mode));
+ }
+ //handle -luts argument
+ if (!luts_arg.empty()){
+ lut_costs.clear();
+ for (auto &tok : split_tokens(luts_arg, ",")) {
+ auto parts = split_tokens(tok, ":");
+ if (GetSize(parts) == 0 && !lut_costs.empty())
+ lut_costs.push_back(lut_costs.back());
+ else if (GetSize(parts) == 1)
+ lut_costs.push_back(atoi(parts.at(0).c_str()));
+ else if (GetSize(parts) == 2)
+ while (GetSize(lut_costs) < std::atoi(parts.at(0).c_str()))
+ lut_costs.push_back(atoi(parts.at(1).c_str()));
+ else
+ log_cmd_error("Invalid -luts syntax.\n");
+ }
+ }
+
+ // handle -g argument
+ if (!g_arg.empty()){
+ for (auto g : split_tokens(g_arg, ",")) {
+ vector<string> gate_list;
+ bool remove_gates = false;
+ if (GetSize(g) > 0 && g[0] == '-') {
+ remove_gates = true;
+ g = g.substr(1);
+ }
+ if (g == "AND") goto ok_gate;
+ if (g == "NAND") goto ok_gate;
+ if (g == "OR") goto ok_gate;
+ if (g == "NOR") goto ok_gate;
+ if (g == "XOR") goto ok_gate;
+ if (g == "XNOR") goto ok_gate;
+ if (g == "ANDNOT") goto ok_gate;
+ if (g == "ORNOT") goto ok_gate;
+ if (g == "MUX") goto ok_gate;
+ if (g == "NMUX") goto ok_gate;
+ if (g == "AOI3") goto ok_gate;
+ if (g == "OAI3") goto ok_gate;
+ if (g == "AOI4") goto ok_gate;
+ if (g == "OAI4") goto ok_gate;
+ if (g == "simple") {
+ gate_list.push_back("AND");
+ gate_list.push_back("OR");
+ gate_list.push_back("XOR");
+ gate_list.push_back("MUX");
+ goto ok_alias;
+ }
+ if (g == "cmos2") {
+ if (!remove_gates)
+ cmos_cost = true;
+ gate_list.push_back("NAND");
+ gate_list.push_back("NOR");
+ goto ok_alias;
+ }
+ if (g == "cmos3") {
+ if (!remove_gates)
+ cmos_cost = true;
+ gate_list.push_back("NAND");
+ gate_list.push_back("NOR");
+ gate_list.push_back("AOI3");
+ gate_list.push_back("OAI3");
+ goto ok_alias;
+ }
+ if (g == "cmos4") {
+ if (!remove_gates)
+ cmos_cost = true;
+ gate_list.push_back("NAND");
+ gate_list.push_back("NOR");
+ gate_list.push_back("AOI3");
+ gate_list.push_back("OAI3");
+ gate_list.push_back("AOI4");
+ gate_list.push_back("OAI4");
+ goto ok_alias;
+ }
+ if (g == "cmos") {
+ if (!remove_gates)
+ cmos_cost = true;
+ gate_list.push_back("NAND");
+ gate_list.push_back("NOR");
+ gate_list.push_back("AOI3");
+ gate_list.push_back("OAI3");
+ gate_list.push_back("AOI4");
+ gate_list.push_back("OAI4");
+ gate_list.push_back("NMUX");
+ gate_list.push_back("MUX");
+ gate_list.push_back("XOR");
+ gate_list.push_back("XNOR");
+ goto ok_alias;
+ }
+ if (g == "gates") {
+ gate_list.push_back("AND");
+ gate_list.push_back("NAND");
+ gate_list.push_back("OR");
+ gate_list.push_back("NOR");
+ gate_list.push_back("XOR");
+ gate_list.push_back("XNOR");
+ gate_list.push_back("ANDNOT");
+ gate_list.push_back("ORNOT");
+ goto ok_alias;
+ }
+ if (g == "aig") {
+ gate_list.push_back("AND");
+ gate_list.push_back("NAND");
+ gate_list.push_back("OR");
+ gate_list.push_back("NOR");
+ gate_list.push_back("ANDNOT");
+ gate_list.push_back("ORNOT");
+ goto ok_alias;
+ }
+ if (g == "all") {
+ gate_list.push_back("AND");
+ gate_list.push_back("NAND");
+ gate_list.push_back("OR");
+ gate_list.push_back("NOR");
+ gate_list.push_back("XOR");
+ gate_list.push_back("XNOR");
+ gate_list.push_back("ANDNOT");
+ gate_list.push_back("ORNOT");
+ gate_list.push_back("AOI3");
+ gate_list.push_back("OAI3");
+ gate_list.push_back("AOI4");
+ gate_list.push_back("OAI4");
+ gate_list.push_back("MUX");
+ gate_list.push_back("NMUX");
+ }
+ if (g_arg_from_cmd)
+ cmd_error(args, g_argidx, stringf("Unsupported gate type: %s", g.c_str()));
+ else
+ log_cmd_error("Unsupported gate type: %s", g.c_str());
+ ok_gate:
+ gate_list.push_back(g);
+ ok_alias:
+ for (auto gate : gate_list) {
+ if (remove_gates)
+ enabled_gates.erase(gate);
+ else
+ enabled_gates.insert(gate);
+ }
+ }
+ }
+
if (!lut_costs.empty() && !liberty_file.empty())
- log_cmd_error("Got -lut and -liberty! This two options are exclusive.\n");
+ log_cmd_error("Got -lut and -liberty! These two options are exclusive.\n");
if (!constr_file.empty() && liberty_file.empty())
log_cmd_error("Got -constr but no -liberty!\n");
+ if (enabled_gates.empty()) {
+ enabled_gates.insert("AND");
+ enabled_gates.insert("NAND");
+ enabled_gates.insert("OR");
+ enabled_gates.insert("NOR");
+ enabled_gates.insert("XOR");
+ enabled_gates.insert("XNOR");
+ enabled_gates.insert("ANDNOT");
+ enabled_gates.insert("ORNOT");
+ // enabled_gates.insert("AOI3");
+ // enabled_gates.insert("OAI3");
+ // enabled_gates.insert("AOI4");
+ // enabled_gates.insert("OAI4");
+ enabled_gates.insert("MUX");
+ // enabled_gates.insert("NMUX");
+ }
+
for (auto mod : design->selected_modules())
{
if (mod->processes.size() > 0) {
@@ -1660,16 +1864,16 @@ struct AbcPass : public Pass {
signal_init.clear();
for (Wire *wire : mod->wires())
- if (wire->attributes.count("\\init")) {
+ if (wire->attributes.count(ID(init))) {
SigSpec initsig = assign_map(wire);
- Const initval = wire->attributes.at("\\init");
+ Const initval = wire->attributes.at(ID(init));
for (int i = 0; i < GetSize(initsig) && i < GetSize(initval); i++)
switch (initval[i]) {
case State::S0:
signal_init[initsig[i]] = State::S0;
break;
case State::S1:
- signal_init[initsig[i]] = State::S0;
+ signal_init[initsig[i]] = State::S1;
break;
default:
break;
@@ -1678,7 +1882,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;
}
@@ -1719,16 +1923,16 @@ struct AbcPass : public Pass {
}
}
- if (cell->type == "$_DFF_N_" || cell->type == "$_DFF_P_")
+ if (cell->type.in(ID($_DFF_N_), ID($_DFF_P_)))
{
- key = clkdomain_t(cell->type == "$_DFF_P_", assign_map(cell->getPort("\\C")), true, RTLIL::SigSpec());
+ key = clkdomain_t(cell->type == ID($_DFF_P_), assign_map(cell->getPort(ID(C))), true, RTLIL::SigSpec());
}
else
- if (cell->type == "$_DFFE_NN_" || cell->type == "$_DFFE_NP_" || cell->type == "$_DFFE_PN_" || cell->type == "$_DFFE_PP_")
+ if (cell->type.in(ID($_DFFE_NN_), ID($_DFFE_NP_), ID($_DFFE_PN_), ID($_DFFE_PP_)))
{
- bool this_clk_pol = cell->type == "$_DFFE_PN_" || cell->type == "$_DFFE_PP_";
- bool this_en_pol = cell->type == "$_DFFE_NP_" || cell->type == "$_DFFE_PP_";
- key = clkdomain_t(this_clk_pol, assign_map(cell->getPort("\\C")), this_en_pol, assign_map(cell->getPort("\\E")));
+ bool this_clk_pol = cell->type.in(ID($_DFFE_PN_), ID($_DFFE_PP_));
+ bool this_en_pol = cell->type.in(ID($_DFFE_NP_), ID($_DFFE_PP_));
+ key = clkdomain_t(this_clk_pol, assign_map(cell->getPort(ID(C))), this_en_pol, assign_map(cell->getPort(ID(E))));
}
else
continue;
@@ -1823,7 +2027,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/abc9.cc b/passes/techmap/abc9.cc
new file mode 100644
index 000000000..2aeda16d6
--- /dev/null
+++ b/passes/techmap/abc9.cc
@@ -0,0 +1,332 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * (C) 2019 Eddie Hung <eddie@fpgeh.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
+ * 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]] ABC
+// Berkeley Logic Synthesis and Verification Group, ABC: A System for Sequential Synthesis and Verification
+// http://www.eecs.berkeley.edu/~alanmi/abc/
+
+#include "kernel/register.h"
+#include "kernel/celltypes.h"
+#include "kernel/rtlil.h"
+#include "kernel/log.h"
+
+// abc9_exe.cc
+std::string fold_abc9_cmd(std::string str);
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct Abc9Pass : public ScriptPass
+{
+ Abc9Pass() : ScriptPass("abc9", "use ABC9 for technology mapping") { }
+ void on_register() YS_OVERRIDE
+ {
+ RTLIL::constpad["abc9.script.default"] = "+&scorr; &sweep; &dc2; &dch -f; &ps; &if {C} {W} {D} {R} -v; &mfs";
+ RTLIL::constpad["abc9.script.default.area"] = "+&scorr; &sweep; &dc2; &dch -f; &ps; &if {C} {W} {D} {R} -a -v; &mfs";
+ RTLIL::constpad["abc9.script.default.fast"] = "+&if {C} {W} {D} {R} -v";
+ // Based on ABC's &flow
+ RTLIL::constpad["abc9.script.flow"] = "+&scorr; &sweep;" \
+ "&dch -C 500;" \
+ /* Round 1 */ \
+ /* Map 1 */ "&unmap; &if {C} {W} {D} {R} -v; &save; &load; &mfs;" \
+ "&st; &dsdb;" \
+ /* Map 2 */ "&unmap; &if {C} {W} {D} {R} -v; &save; &load; &mfs;" \
+ "&st; &syn2 -m -R 10; &dsdb;" \
+ "&blut -a -K 6;" \
+ /* Map 3 */ "&unmap; &if {C} {W} {D} {R} -v; &save; &load; &mfs;" \
+ /* Round 2 */ \
+ "&st; &sopb;" \
+ /* Map 1 */ "&unmap; &if {C} {W} {D} {R} -v; &save; &load; &mfs;" \
+ "&st; &dsdb;" \
+ /* Map 2 */ "&unmap; &if {C} {W} {D} {R} -v; &save; &load; &mfs;" \
+ "&st; &syn2 -m -R 10; &dsdb;" \
+ "&blut -a -K 6;" \
+ /* Map 3 */ "&unmap; &if {C} {W} {D} {R} -v; &save; &load; &mfs;" \
+ /* Round 3 */ \
+ /* Map 1 */ "&unmap; &if {C} {W} {D} {R} -v; &save; &load; &mfs;" \
+ "&st; &dsdb;" \
+ /* Map 2 */ "&unmap; &if {C} {W} {D} {R} -v; &save; &load; &mfs;" \
+ "&st; &syn2 -m -R 10; &dsdb;" \
+ "&blut -a -K 6;" \
+ /* Map 3 */ "&unmap; &if {C} {W} {D} {R} -v; &save; &load; &mfs;";
+ // Based on ABC's &flow2
+ RTLIL::constpad["abc9.script.flow2"] = "+&scorr; &sweep;" \
+ /* Comm1 */ "&synch2 -K 6 -C 500; &if -m {C} {W} {D} {R} -v; &mfs "/*"-W 4 -M 500 -C 7000"*/"; &save;"\
+ /* Comm2 */ "&dch -C 500; &if -m {C} {W} {D} {R} -v; &mfs "/*"-W 4 -M 500 -C 7000"*/"; &save;"\
+ "&load; &st; &sopb -R 10 -C 4; " \
+ /* Comm3 */ "&synch2 -K 6 -C 500; &if -m "/*"-E 5"*/" {C} {W} {D} {R} -v; &mfs "/*"-W 4 -M 500 -C 7000"*/"; &save;"\
+ /* Comm2 */ "&dch -C 500; &if -m {C} {W} {D} {R} -v; &mfs "/*"-W 4 -M 500 -C 7000"*/"; &save; "\
+ "&load";
+ // Based on ABC's &flow3
+ RTLIL::constpad["abc9.script.flow3"] = "+&scorr; &sweep;" \
+ "&if {C} {W} {D}; &save; &st; &syn2; &if {C} {W} {D} {R} -v; &save; &load;"\
+ "&st; &if {C} -g -K 6; &dch -f; &if {C} {W} {D} {R} -v; &save; &load;"\
+ "&st; &if {C} -g -K 6; &synch2; &if {C} {W} {D} {R} -v; &save; &load;"\
+ "&mfs";
+ }
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" abc9 [options] [selection]\n");
+ log("\n");
+ log("This script pass performs a sequence of commands to facilitate the use of the ABC\n");
+ log("tool [1] for technology mapping of the current design to a target FPGA\n");
+ log("architecture. Only fully-selected modules are supported.\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 'begin', and empty to label is\n");
+ log(" synonymous to the end of the command list.\n");
+ log("\n");
+ log(" -exe <command>\n");
+#ifdef ABCEXTERNAL
+ log(" use the specified command instead of \"" ABCEXTERNAL "\" to execute ABC.\n");
+#else
+ log(" use the specified command instead of \"<yosys-bindir>/yosys-abc\" to execute ABC.\n");
+#endif
+ log(" This can e.g. be used to call a specific version of ABC or a wrapper.\n");
+ log("\n");
+ log(" -script <file>\n");
+ log(" use the specified ABC script file instead of the default script.\n");
+ log("\n");
+ log(" if <file> starts with a plus sign (+), then the rest of the filename\n");
+ log(" string is interpreted as the command string to be passed to ABC. The\n");
+ log(" leading plus sign is removed and all commas (,) in the string are\n");
+ log(" replaced with blanks before the string is passed to ABC.\n");
+ log("\n");
+ log(" if no -script parameter is given, the following scripts are used:\n");
+ log("%s\n", fold_abc9_cmd(RTLIL::constpad.at("abc9.script.default").substr(1,std::string::npos)).c_str());
+ log("\n");
+ log(" -fast\n");
+ log(" use different default scripts that are slightly faster (at the cost\n");
+ log(" of output quality):\n");
+ log("%s\n", fold_abc9_cmd(RTLIL::constpad.at("abc9.script.default.fast").substr(1,std::string::npos)).c_str());
+ log("\n");
+ log(" -D <picoseconds>\n");
+ log(" set delay target. the string {D} in the default scripts above is\n");
+ log(" replaced by this option when used, and an empty string otherwise\n");
+ log(" (indicating best possible delay).\n");
+ log("\n");
+// log(" -S <num>\n");
+// log(" maximum number of LUT inputs shared.\n");
+// log(" (replaces {S} in the default scripts above, default: -S 1)\n");
+// log("\n");
+ log(" -lut <width>\n");
+ log(" generate netlist using luts of (max) the specified width.\n");
+ log("\n");
+ log(" -lut <w1>:<w2>\n");
+ log(" generate netlist using luts of (max) the specified width <w2>. All\n");
+ log(" luts with width <= <w1> have constant cost. for luts larger than <w1>\n");
+ log(" the area cost doubles with each additional input bit. the delay cost\n");
+ log(" is still constant for all lut widths.\n");
+ log("\n");
+ log(" -lut <file>\n");
+ log(" pass this file with lut library to ABC.\n");
+ log("\n");
+ log(" -luts <cost1>,<cost2>,<cost3>,<sizeN>:<cost4-N>,..\n");
+ log(" generate netlist using luts. Use the specified costs for luts with 1,\n");
+ log(" 2, 3, .. inputs.\n");
+ log("\n");
+ log(" -dff\n");
+ log(" also pass $_ABC9_FF_ cells through to ABC. modules with many clock\n");
+ log(" domains are marked as such and automatically partitioned by ABC.\n");
+ log("\n");
+ log(" -nocleanup\n");
+ log(" when this option is used, the temporary files created by this pass\n");
+ log(" are not removed. this is useful for debugging.\n");
+ log("\n");
+ log(" -showtmp\n");
+ log(" print the temp dir name in log. usually this is suppressed so that the\n");
+ log(" command output is identical across runs.\n");
+ log("\n");
+ log(" -box <file>\n");
+ log(" pass this file with box library to ABC.\n");
+ log("\n");
+ log("Note that this is a logic optimization pass within Yosys that is calling ABC\n");
+ log("internally. This is not going to \"run ABC on your design\". It will instead run\n");
+ log("ABC on logic snippets extracted from your design. You will not get any useful\n");
+ log("output when passing an ABC script that writes a file. Instead write your full\n");
+ log("design as an XAIGER file with `write_xaiger' and then load that into ABC\n");
+ log("externally if you want to use ABC to convert your design into another format.\n");
+ log("\n");
+ log("[1] http://www.eecs.berkeley.edu/~alanmi/abc/\n");
+ log("\n");
+ help_script();
+ log("\n");
+ }
+
+ std::stringstream exe_cmd;
+ bool dff_mode, cleanup;
+
+ void clear_flags() YS_OVERRIDE
+ {
+ exe_cmd.str("");
+ exe_cmd << "abc9_exe";
+ dff_mode = false;
+ cleanup = true;
+ }
+
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ std::string run_from, run_to;
+ clear_flags();
+
+ // get arguments from scratchpad first, then override by command arguments
+ dff_mode = design->scratchpad_get_bool("abc9.dff", dff_mode);
+ cleanup = !design->scratchpad_get_bool("abc9.nocleanup", !cleanup);
+
+ if (design->scratchpad_get_bool("abc9.debug")) {
+ cleanup = false;
+ exe_cmd << " -showtmp";
+ }
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ std::string arg = args[argidx];
+ if ((arg == "-exe" || arg == "-script" || arg == "-D" ||
+ /* arg == "-S" || */ arg == "-lut" || arg == "-luts" ||
+ arg == "-box" || arg == "-W") &&
+ argidx+1 < args.size()) {
+ exe_cmd << " " << arg << " " << args[++argidx];
+ continue;
+ }
+ if (arg == "-fast" || /* arg == "-dff" || */
+ /* arg == "-nocleanup" || */ arg == "-showtmp") {
+ exe_cmd << " " << arg;
+ continue;
+ }
+ if (arg == "-dff") {
+ dff_mode = true;
+ exe_cmd << " " << arg;
+ continue;
+ }
+ if (arg == "-nocleanup") {
+ cleanup = false;
+ continue;
+ }
+ if (arg == "-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;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ log_assert(design);
+ if (design->selected_modules().empty()) {
+ log_warning("No modules selected for ABC9 techmapping.\n");
+ return;
+ }
+
+ log_header(design, "Executing ABC9 pass.\n");
+ log_push();
+
+ run_script(design, run_from, run_to);
+
+ log_pop();
+ }
+
+ void script() YS_OVERRIDE
+ {
+ if (check_label("pre")) {
+ run("scc -set_attr abc9_scc_id {}");
+ if (help_mode)
+ run("abc9_ops -mark_scc -prep_xaiger [-dff]", "(option for -dff)");
+ else
+ run("abc9_ops -mark_scc -prep_xaiger" + std::string(dff_mode ? " -dff" : ""), "(option for -dff)");
+ run("select -set abc9_holes A:abc9_holes");
+ run("flatten -wb @abc9_holes");
+ run("techmap @abc9_holes");
+ if (dff_mode || help_mode)
+ run("abc9_ops -prep_dff", "(only if -dff)");
+ run("opt -purge @abc9_holes");
+ run("aigmap");
+ run("wbflip @abc9_holes");
+ }
+
+ if (check_label("map")) {
+ if (help_mode) {
+ run("foreach module in selection");
+ run(" write_xaiger -map <abc-temp-dir>/input.sym <abc-temp-dir>/input.xaig");
+ run(" abc9_exe -cwd <abc-temp-dir> [options]");
+ run(" read_aiger -xaiger -wideports -module_name <module-name>$abc9 -map <abc-temp-dir>/input.sym <abc-temp-dir>/output.aig");
+ run(" abc9_ops -reintegrate");
+ }
+ else {
+ auto selected_modules = active_design->selected_modules();
+ active_design->selection_stack.emplace_back(false);
+
+ for (auto mod : selected_modules) {
+ if (mod->processes.size() > 0) {
+ log("Skipping module %s as it contains processes.\n", log_id(mod));
+ continue;
+ }
+ log_assert(!mod->attributes.count(ID(abc9_box_id)));
+
+ log_push();
+ active_design->selection().select(mod);
+
+ if (!active_design->selected_whole_module(mod))
+ log_error("Can't handle partially selected module %s!\n", log_id(mod));
+
+ std::string tempdir_name = "/tmp/yosys-abc-XXXXXX";
+ if (!cleanup)
+ tempdir_name[0] = tempdir_name[4] = '_';
+ tempdir_name = make_temp_dir(tempdir_name);
+
+ run(stringf("write_xaiger -map %s/input.sym %s/input.xaig", tempdir_name.c_str(), tempdir_name.c_str()));
+
+ int num_outputs = active_design->scratchpad_get_int("write_xaiger.num_outputs");
+
+ log("Extracted %d AND gates and %d wires from module `%s' to a netlist network with %d inputs and %d outputs.\n",
+ active_design->scratchpad_get_int("write_xaiger.num_ands"),
+ active_design->scratchpad_get_int("write_xaiger.num_wires"),
+ log_id(mod),
+ active_design->scratchpad_get_int("write_xaiger.num_inputs"),
+ num_outputs);
+ if (num_outputs) {
+ run(stringf("%s -cwd %s", exe_cmd.str().c_str(), tempdir_name.c_str()));
+ run(stringf("read_aiger -xaiger -wideports -module_name %s$abc9 -map %s/input.sym %s/output.aig", log_id(mod), tempdir_name.c_str(), tempdir_name.c_str()));
+ run("abc9_ops -reintegrate");
+ }
+ else
+ log("Don't call ABC as there is nothing to map.\n");
+
+ if (cleanup) {
+ log("Removing temp directory.\n");
+ remove_directory(tempdir_name);
+ }
+
+ active_design->selection().selected_modules.clear();
+ log_pop();
+ }
+
+ active_design->selection_stack.pop_back();
+ }
+ }
+ }
+} Abc9Pass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/techmap/abc9_exe.cc b/passes/techmap/abc9_exe.cc
new file mode 100644
index 000000000..01bf46539
--- /dev/null
+++ b/passes/techmap/abc9_exe.cc
@@ -0,0 +1,531 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * 2019 Eddie Hung <eddie@fpgeh.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
+ * 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]] ABC
+// Berkeley Logic Synthesis and Verification Group, ABC: A System for Sequential Synthesis and Verification
+// http://www.eecs.berkeley.edu/~alanmi/abc/
+
+#include "kernel/register.h"
+#include "kernel/log.h"
+
+#ifndef _WIN32
+# include <unistd.h>
+# include <dirent.h>
+#endif
+
+#ifdef YOSYS_LINK_ABC
+extern "C" int Abc_RealMain(int argc, char *argv[]);
+#endif
+
+std::string fold_abc9_cmd(std::string str)
+{
+ std::string token, new_str = " ";
+ int char_counter = 10;
+
+ for (size_t i = 0; i <= str.size(); i++) {
+ if (i < str.size())
+ token += str[i];
+ if (i == str.size() || str[i] == ';') {
+ if (char_counter + token.size() > 75)
+ new_str += "\n ", char_counter = 14;
+ new_str += token, char_counter += token.size();
+ token.clear();
+ }
+ }
+
+ return new_str;
+}
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+std::string add_echos_to_abc9_cmd(std::string str)
+{
+ std::string new_str, token;
+ for (size_t i = 0; i < str.size(); i++) {
+ token += str[i];
+ if (str[i] == ';') {
+ while (i+1 < str.size() && str[i+1] == ' ')
+ i++;
+ new_str += "echo + " + token + " " + token + " ";
+ token.clear();
+ }
+ }
+
+ if (!token.empty()) {
+ if (!new_str.empty())
+ new_str += "echo + " + token + "; ";
+ new_str += token;
+ }
+
+ return new_str;
+}
+
+std::string replace_tempdir(std::string text, std::string tempdir_name, bool show_tempdir)
+{
+ if (show_tempdir)
+ return text;
+
+ while (1) {
+ size_t pos = text.find(tempdir_name);
+ if (pos == std::string::npos)
+ break;
+ text = text.substr(0, pos) + "<abc-temp-dir>" + text.substr(pos + GetSize(tempdir_name));
+ }
+
+ std::string selfdir_name = proc_self_dirname();
+ if (selfdir_name != "/") {
+ while (1) {
+ size_t pos = text.find(selfdir_name);
+ if (pos == std::string::npos)
+ break;
+ text = text.substr(0, pos) + "<yosys-exe-dir>/" + text.substr(pos + GetSize(selfdir_name));
+ }
+ }
+
+ return text;
+}
+
+struct abc9_output_filter
+{
+ bool got_cr;
+ int escape_seq_state;
+ std::string linebuf;
+ std::string tempdir_name;
+ bool show_tempdir;
+
+ abc9_output_filter(std::string tempdir_name, bool show_tempdir) : tempdir_name(tempdir_name), show_tempdir(show_tempdir)
+ {
+ got_cr = false;
+ escape_seq_state = 0;
+ }
+
+ void next_char(char ch)
+ {
+ if (escape_seq_state == 0 && ch == '\033') {
+ escape_seq_state = 1;
+ return;
+ }
+ if (escape_seq_state == 1) {
+ escape_seq_state = ch == '[' ? 2 : 0;
+ return;
+ }
+ if (escape_seq_state == 2) {
+ if ((ch < '0' || '9' < ch) && ch != ';')
+ escape_seq_state = 0;
+ return;
+ }
+ escape_seq_state = 0;
+ if (ch == '\r') {
+ got_cr = true;
+ return;
+ }
+ if (ch == '\n') {
+ log("ABC: %s\n", replace_tempdir(linebuf, tempdir_name, show_tempdir).c_str());
+ got_cr = false, linebuf.clear();
+ return;
+ }
+ if (got_cr)
+ got_cr = false, linebuf.clear();
+ linebuf += ch;
+ }
+
+ void next_line(const std::string &line)
+ {
+ //int pi, po;
+ //if (sscanf(line.c_str(), "Start-point = pi%d. End-point = po%d.", &pi, &po) == 2) {
+ // log("ABC: Start-point = pi%d (%s). End-point = po%d (%s).\n",
+ // pi, pi_map.count(pi) ? pi_map.at(pi).c_str() : "???",
+ // po, po_map.count(po) ? po_map.at(po).c_str() : "???");
+ // return;
+ //}
+
+ for (char ch : line)
+ next_char(ch);
+ }
+};
+
+void abc9_module(RTLIL::Design *design, std::string script_file, std::string exe_file,
+ vector<int> lut_costs, bool dff_mode, std::string delay_target, std::string /*lutin_shared*/, bool fast_mode,
+ bool show_tempdir, std::string box_file, std::string lut_file,
+ std::string wire_delay, std::string tempdir_name
+)
+{
+ std::string abc9_script;
+
+ if (!lut_costs.empty())
+ abc9_script += stringf("read_lut %s/lutdefs.txt; ", tempdir_name.c_str());
+ else if (!lut_file.empty())
+ abc9_script += stringf("read_lut %s; ", lut_file.c_str());
+ else
+ log_abort();
+
+ log_assert(!box_file.empty());
+ abc9_script += stringf("read_box %s; ", box_file.c_str());
+ abc9_script += stringf("&read %s/input.xaig; &ps; ", tempdir_name.c_str());
+
+ if (!script_file.empty()) {
+ if (script_file[0] == '+') {
+ for (size_t i = 1; i < script_file.size(); i++)
+ if (script_file[i] == '\'')
+ abc9_script += "'\\''";
+ else if (script_file[i] == ',')
+ abc9_script += " ";
+ else
+ abc9_script += script_file[i];
+ } else
+ abc9_script += stringf("source %s", script_file.c_str());
+ } else if (!lut_costs.empty() || !lut_file.empty()) {
+ abc9_script += fast_mode ? RTLIL::constpad.at("abc9.script.default.fast").substr(1,std::string::npos)
+ : RTLIL::constpad.at("abc9.script.default").substr(1,std::string::npos);
+ } else
+ log_abort();
+
+ for (size_t pos = abc9_script.find("{D}"); pos != std::string::npos; pos = abc9_script.find("{D}", pos))
+ abc9_script = abc9_script.substr(0, pos) + delay_target + abc9_script.substr(pos+3);
+
+ //for (size_t pos = abc9_script.find("{S}"); pos != std::string::npos; pos = abc9_script.find("{S}", pos))
+ // abc9_script = abc9_script.substr(0, pos) + lutin_shared + abc9_script.substr(pos+3);
+
+ for (size_t pos = abc9_script.find("{W}"); pos != std::string::npos; pos = abc9_script.find("{W}", pos))
+ abc9_script = abc9_script.substr(0, pos) + wire_delay + abc9_script.substr(pos+3);
+
+ std::string C;
+ if (design->scratchpad.count("abc9.if.C"))
+ C = "-C " + design->scratchpad_get_string("abc9.if.C");
+ for (size_t pos = abc9_script.find("{C}"); pos != std::string::npos; pos = abc9_script.find("{C}", pos))
+ abc9_script = abc9_script.substr(0, pos) + C + abc9_script.substr(pos+3);
+
+ std::string R;
+ if (design->scratchpad.count("abc9.if.R"))
+ R = "-R " + design->scratchpad_get_string("abc9.if.R");
+ for (size_t pos = abc9_script.find("{R}"); pos != std::string::npos; pos = abc9_script.find("{R}", pos))
+ abc9_script = abc9_script.substr(0, pos) + R + abc9_script.substr(pos+3);
+
+ abc9_script += stringf("; &ps -l; &write -n %s/output.aig", tempdir_name.c_str());
+ if (design->scratchpad_get_bool("abc9.verify")) {
+ if (dff_mode)
+ abc9_script += "; verify -s";
+ else
+ abc9_script += "; verify";
+ }
+ abc9_script += "; time";
+ abc9_script = add_echos_to_abc9_cmd(abc9_script);
+
+ for (size_t i = 0; i+1 < abc9_script.size(); i++)
+ if (abc9_script[i] == ';' && abc9_script[i+1] == ' ')
+ abc9_script[i+1] = '\n';
+
+ FILE *f = fopen(stringf("%s/abc.script", tempdir_name.c_str()).c_str(), "wt");
+ fprintf(f, "%s\n", abc9_script.c_str());
+ fclose(f);
+
+ std::string buffer;
+
+ log_header(design, "Executing ABC9.\n");
+
+ if (!lut_costs.empty()) {
+ buffer = stringf("%s/lutdefs.txt", tempdir_name.c_str());
+ f = fopen(buffer.c_str(), "wt");
+ if (f == NULL)
+ log_error("Opening %s for writing failed: %s\n", buffer.c_str(), strerror(errno));
+ for (int i = 0; i < GetSize(lut_costs); i++)
+ fprintf(f, "%d %d.00 1.00\n", i+1, lut_costs.at(i));
+ fclose(f);
+ }
+
+ buffer = stringf("%s -s -f %s/abc.script 2>&1", exe_file.c_str(), tempdir_name.c_str());
+ log("Running ABC command: %s\n", replace_tempdir(buffer, tempdir_name, show_tempdir).c_str());
+
+#ifndef YOSYS_LINK_ABC
+ abc9_output_filter filt(tempdir_name, show_tempdir);
+ int ret = run_command(buffer, std::bind(&abc9_output_filter::next_line, filt, std::placeholders::_1));
+#else
+ // These needs to be mutable, supposedly due to getopt
+ char *abc9_argv[5];
+ string tmp_script_name = stringf("%s/abc.script", tempdir_name.c_str());
+ abc9_argv[0] = strdup(exe_file.c_str());
+ abc9_argv[1] = strdup("-s");
+ abc9_argv[2] = strdup("-f");
+ abc9_argv[3] = strdup(tmp_script_name.c_str());
+ abc9_argv[4] = 0;
+ int ret = Abc_RealMain(4, abc9_argv);
+ free(abc9_argv[0]);
+ free(abc9_argv[1]);
+ free(abc9_argv[2]);
+ free(abc9_argv[3]);
+#endif
+ if (ret != 0)
+ log_error("ABC: execution of command \"%s\" failed: return code %d.\n", buffer.c_str(), ret);
+}
+
+struct Abc9ExePass : public Pass {
+ Abc9ExePass() : Pass("abc9_exe", "use ABC9 for technology mapping") { }
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" abc9_exe [options]\n");
+ log("\n");
+ log(" \n");
+ log("This pass uses the ABC tool [1] for technology mapping of the top module\n");
+ log("(according to the (* top *) attribute or if only one module is currently selected)\n");
+ log("to a target FPGA architecture.\n");
+ log("\n");
+ log(" -exe <command>\n");
+#ifdef ABCEXTERNAL
+ log(" use the specified command instead of \"" ABCEXTERNAL "\" to execute ABC.\n");
+#else
+ log(" use the specified command instead of \"<yosys-bindir>/yosys-abc\" to execute ABC.\n");
+#endif
+ log(" This can e.g. be used to call a specific version of ABC or a wrapper.\n");
+ log("\n");
+ log(" -script <file>\n");
+ log(" use the specified ABC script file instead of the default script.\n");
+ log("\n");
+ log(" if <file> starts with a plus sign (+), then the rest of the filename\n");
+ log(" string is interpreted as the command string to be passed to ABC. The\n");
+ log(" leading plus sign is removed and all commas (,) in the string are\n");
+ log(" replaced with blanks before the string is passed to ABC.\n");
+ log("\n");
+ log(" if no -script parameter is given, the following scripts are used:\n");
+ log("%s\n", fold_abc9_cmd(RTLIL::constpad.at("abc9.script.default").substr(1,std::string::npos)).c_str());
+ log("\n");
+ log(" -fast\n");
+ log(" use different default scripts that are slightly faster (at the cost\n");
+ log(" of output quality):\n");
+ log("%s\n", fold_abc9_cmd(RTLIL::constpad.at("abc9.script.default.fast").substr(1,std::string::npos)).c_str());
+ log("\n");
+ log(" -D <picoseconds>\n");
+ log(" set delay target. the string {D} in the default scripts above is\n");
+ log(" replaced by this option when used, and an empty string otherwise\n");
+ log(" (indicating best possible delay).\n");
+ log("\n");
+// log(" -S <num>\n");
+// log(" maximum number of LUT inputs shared.\n");
+// log(" (replaces {S} in the default scripts above, default: -S 1)\n");
+// log("\n");
+ log(" -lut <width>\n");
+ log(" generate netlist using luts of (max) the specified width.\n");
+ log("\n");
+ log(" -lut <w1>:<w2>\n");
+ log(" generate netlist using luts of (max) the specified width <w2>. All\n");
+ log(" luts with width <= <w1> have constant cost. for luts larger than <w1>\n");
+ log(" the area cost doubles with each additional input bit. the delay cost\n");
+ log(" is still constant for all lut widths.\n");
+ log("\n");
+ log(" -lut <file>\n");
+ log(" pass this file with lut library to ABC.\n");
+ log("\n");
+ log(" -luts <cost1>,<cost2>,<cost3>,<sizeN>:<cost4-N>,..\n");
+ log(" generate netlist using luts. Use the specified costs for luts with 1,\n");
+ log(" 2, 3, .. inputs.\n");
+ log("\n");
+ log(" -showtmp\n");
+ log(" print the temp dir name in log. usually this is suppressed so that the\n");
+ log(" command output is identical across runs.\n");
+ log("\n");
+ log(" -box <file>\n");
+ log(" pass this file with box library to ABC.\n");
+ log("\n");
+ log(" -cwd <dir>\n");
+ log(" use this as the current working directory, inside which the 'input.xaig'\n");
+ log(" file is expected. temporary files will be created in this directory, and\n");
+ log(" the mapped result will be written to 'output.aig'.\n");
+ log("\n");
+ log("Note that this is a logic optimization pass within Yosys that is calling ABC\n");
+ log("internally. This is not going to \"run ABC on your design\". It will instead run\n");
+ log("ABC on logic snippets extracted from your design. You will not get any useful\n");
+ log("output when passing an ABC script that writes a file. Instead write your full\n");
+ log("design as BLIF file with write_blif and then load that into ABC externally if\n");
+ log("you want to use ABC to convert your design into another format.\n");
+ log("\n");
+ log("[1] http://www.eecs.berkeley.edu/~alanmi/abc/\n");
+ log("\n");
+ }
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ log_header(design, "Executing ABC9_MAP pass (technology mapping using ABC9).\n");
+
+#ifdef ABCEXTERNAL
+ std::string exe_file = ABCEXTERNAL;
+#else
+ std::string exe_file = proc_self_dirname() + "yosys-abc";
+#endif
+ std::string script_file, clk_str, box_file, lut_file;
+ std::string delay_target, lutin_shared = "-S 1", wire_delay;
+ std::string tempdir_name;
+ bool fast_mode = false, dff_mode = false;
+ bool show_tempdir = false;
+ vector<int> lut_costs;
+
+#if 0
+ cleanup = false;
+ show_tempdir = true;
+#endif
+
+#ifdef _WIN32
+#ifndef ABCEXTERNAL
+ if (!check_file_exists(exe_file + ".exe") && check_file_exists(proc_self_dirname() + "..\\yosys-abc.exe"))
+ exe_file = proc_self_dirname() + "..\\yosys-abc";
+#endif
+#endif
+
+ std::string lut_arg, luts_arg;
+ exe_file = design->scratchpad_get_string("abc9.exe", exe_file /* inherit default value if not set */);
+ script_file = design->scratchpad_get_string("abc9.script", script_file);
+ if (design->scratchpad.count("abc9.D")) {
+ delay_target = "-D " + design->scratchpad_get_string("abc9.D");
+ }
+ lut_arg = design->scratchpad_get_string("abc9.lut", lut_arg);
+ luts_arg = design->scratchpad_get_string("abc9.luts", luts_arg);
+ fast_mode = design->scratchpad_get_bool("abc9.fast", fast_mode);
+ dff_mode = design->scratchpad_get_bool("abc9.dff", dff_mode);
+ show_tempdir = design->scratchpad_get_bool("abc9.showtmp", show_tempdir);
+ box_file = design->scratchpad_get_string("abc9.box", box_file);
+ if (design->scratchpad.count("abc9.W")) {
+ wire_delay = "-W " + design->scratchpad_get_string("abc9.W");
+ }
+
+ size_t argidx;
+ char pwd [PATH_MAX];
+ if (!getcwd(pwd, sizeof(pwd))) {
+ log_cmd_error("getcwd failed: %s\n", strerror(errno));
+ log_abort();
+ }
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ std::string arg = args[argidx];
+ if (arg == "-exe" && argidx+1 < args.size()) {
+ exe_file = args[++argidx];
+ continue;
+ }
+ if (arg == "-script" && argidx+1 < args.size()) {
+ script_file = args[++argidx];
+ continue;
+ }
+ if (arg == "-D" && argidx+1 < args.size()) {
+ delay_target = "-D " + args[++argidx];
+ continue;
+ }
+ //if (arg == "-S" && argidx+1 < args.size()) {
+ // lutin_shared = "-S " + args[++argidx];
+ // continue;
+ //}
+ if (arg == "-lut" && argidx+1 < args.size()) {
+ lut_arg = args[++argidx];
+ continue;
+ }
+ if (arg == "-luts" && argidx+1 < args.size()) {
+ lut_arg = args[++argidx];
+ continue;
+ }
+ if (arg == "-fast") {
+ fast_mode = true;
+ continue;
+ }
+ if (arg == "-dff") {
+ dff_mode = true;
+ continue;
+ }
+ if (arg == "-showtmp") {
+ show_tempdir = true;
+ continue;
+ }
+ if (arg == "-box" && argidx+1 < args.size()) {
+ box_file = args[++argidx];
+ continue;
+ }
+ if (arg == "-W" && argidx+1 < args.size()) {
+ wire_delay = "-W " + args[++argidx];
+ continue;
+ }
+ if (arg == "-cwd" && argidx+1 < args.size()) {
+ tempdir_name = args[++argidx];
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ rewrite_filename(script_file);
+ if (!script_file.empty() && !is_absolute_path(script_file) && script_file[0] != '+')
+ script_file = std::string(pwd) + "/" + script_file;
+
+ // handle -lut / -luts args
+ if (!lut_arg.empty()) {
+ string arg = lut_arg;
+ if (arg.find_first_not_of("0123456789:") == std::string::npos) {
+ size_t pos = arg.find_first_of(':');
+ int lut_mode = 0, lut_mode2 = 0;
+ if (pos != string::npos) {
+ lut_mode = atoi(arg.substr(0, pos).c_str());
+ lut_mode2 = atoi(arg.substr(pos+1).c_str());
+ } else {
+ lut_mode = atoi(arg.c_str());
+ lut_mode2 = lut_mode;
+ }
+ lut_costs.clear();
+ for (int i = 0; i < lut_mode; i++)
+ lut_costs.push_back(1);
+ for (int i = lut_mode; i < lut_mode2; i++)
+ lut_costs.push_back(2 << (i - lut_mode));
+ }
+ else {
+ lut_file = arg;
+ rewrite_filename(lut_file);
+ if (!lut_file.empty() && !is_absolute_path(lut_file) && lut_file[0] != '+')
+ lut_file = std::string(pwd) + "/" + lut_file;
+ }
+ }
+ if (!luts_arg.empty()) {
+ lut_costs.clear();
+ for (auto &tok : split_tokens(luts_arg, ",")) {
+ auto parts = split_tokens(tok, ":");
+ if (GetSize(parts) == 0 && !lut_costs.empty())
+ lut_costs.push_back(lut_costs.back());
+ else if (GetSize(parts) == 1)
+ lut_costs.push_back(atoi(parts.at(0).c_str()));
+ else if (GetSize(parts) == 2)
+ while (GetSize(lut_costs) < atoi(parts.at(0).c_str()))
+ lut_costs.push_back(atoi(parts.at(1).c_str()));
+ else
+ log_cmd_error("Invalid -luts syntax.\n");
+ }
+ }
+
+ // ABC expects a box file for XAIG
+ if (box_file.empty())
+ box_file = "+/dummy.box";
+
+ rewrite_filename(box_file);
+ if (!box_file.empty() && !is_absolute_path(box_file) && box_file[0] != '+')
+ box_file = std::string(pwd) + "/" + box_file;
+
+ if (tempdir_name.empty())
+ log_cmd_error("abc9_exe '-cwd' option is mandatory.\n");
+
+
+ abc9_module(design, script_file, exe_file, lut_costs, dff_mode,
+ delay_target, lutin_shared, fast_mode, show_tempdir,
+ box_file, lut_file, wire_delay, tempdir_name);
+ }
+} Abc9ExePass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/techmap/abc9_ops.cc b/passes/techmap/abc9_ops.cc
new file mode 100644
index 000000000..9ad29a8f6
--- /dev/null
+++ b/passes/techmap/abc9_ops.cc
@@ -0,0 +1,825 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * 2019 Eddie Hung <eddie@fpgeh.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
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "kernel/register.h"
+#include "kernel/sigtools.h"
+#include "kernel/utils.h"
+#include "kernel/celltypes.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+int map_autoidx;
+
+inline std::string remap_name(RTLIL::IdString abc9_name)
+{
+ return stringf("$abc$%d$%s", map_autoidx, abc9_name.c_str()+1);
+}
+
+void mark_scc(RTLIL::Module *module)
+{
+ // For every unique SCC found, (arbitrarily) find the first
+ // cell in the component, and convert all wires driven by
+ // its output ports into a new PO, and drive its previous
+ // sinks with a new PI
+ pool<RTLIL::Const> ids_seen;
+ for (auto cell : module->cells()) {
+ auto it = cell->attributes.find(ID(abc9_scc_id));
+ if (it == cell->attributes.end())
+ continue;
+ auto id = it->second;
+ auto r = ids_seen.insert(id);
+ cell->attributes.erase(it);
+ if (!r.second)
+ continue;
+ for (auto &c : cell->connections_) {
+ if (c.second.is_fully_const()) continue;
+ if (cell->output(c.first)) {
+ SigBit b = c.second.as_bit();
+ Wire *w = b.wire;
+ w->set_bool_attribute(ID::keep);
+ w->attributes[ID(abc9_scc_id)] = id.as_int();
+ }
+ }
+ }
+
+ module->fixup_ports();
+}
+
+void prep_dff(RTLIL::Module *module)
+{
+ auto design = module->design;
+ log_assert(design);
+
+ SigMap assign_map(module);
+
+ typedef SigSpec clkdomain_t;
+ dict<clkdomain_t, int> clk_to_mergeability;
+
+ for (auto cell : module->cells()) {
+ if (cell->type != "$__ABC9_FF_")
+ continue;
+
+ Wire *abc9_clock_wire = module->wire(stringf("%s.clock", cell->name.c_str()));
+ if (abc9_clock_wire == NULL)
+ log_error("'%s.clock' is not a wire present in module '%s'.\n", cell->name.c_str(), log_id(module));
+ SigSpec abc9_clock = assign_map(abc9_clock_wire);
+
+ clkdomain_t key(abc9_clock);
+
+ auto r = clk_to_mergeability.insert(std::make_pair(abc9_clock, clk_to_mergeability.size() + 1));
+ auto r2 YS_ATTRIBUTE(unused) = cell->attributes.insert(std::make_pair(ID(abc9_mergeability), r.first->second));
+ log_assert(r2.second);
+
+ Wire *abc9_init_wire = module->wire(stringf("%s.init", cell->name.c_str()));
+ if (abc9_init_wire == NULL)
+ log_error("'%s.init' is not a wire present in module '%s'.\n", cell->name.c_str(), log_id(module));
+ log_assert(GetSize(abc9_init_wire) == 1);
+ SigSpec abc9_init = assign_map(abc9_init_wire);
+ if (!abc9_init.is_fully_const())
+ log_error("'%s.init' is not a constant wire present in module '%s'.\n", cell->name.c_str(), log_id(module));
+ if (abc9_init == State::S1)
+ log_error("'%s.init' in module '%s' has value 1'b1 which is not supported by 'abc9 -dff'.\n", cell->name.c_str(), log_id(module));
+ r2 = cell->attributes.insert(std::make_pair(ID(abc9_init), abc9_init.as_const()));
+ log_assert(r2.second);
+ }
+
+ RTLIL::Module *holes_module = design->module(stringf("%s$holes", module->name.c_str()));
+ if (holes_module) {
+ SigMap sigmap(holes_module);
+
+ dict<SigSpec, SigSpec> replace;
+ for (auto cell : holes_module->cells().to_vector()) {
+ if (!cell->type.in("$_DFF_N_", "$_DFF_NN0_", "$_DFF_NN1_", "$_DFF_NP0_", "$_DFF_NP1_",
+ "$_DFF_P_", "$_DFF_PN0_", "$_DFF_PN1", "$_DFF_PP0_", "$_DFF_PP1_"))
+ continue;
+ SigBit D = cell->getPort("\\D");
+ SigBit Q = cell->getPort("\\Q");
+ // Emulate async control embedded inside $_DFF_* cell with mux in front of D
+ if (cell->type.in("$_DFF_NN0_", "$_DFF_PN0_"))
+ D = holes_module->MuxGate(NEW_ID, State::S0, D, cell->getPort("\\R"));
+ else if (cell->type.in("$_DFF_NN1_", "$_DFF_PN1_"))
+ D = holes_module->MuxGate(NEW_ID, State::S1, D, cell->getPort("\\R"));
+ else if (cell->type.in("$_DFF_NP0_", "$_DFF_PP0_"))
+ D = holes_module->MuxGate(NEW_ID, D, State::S0, cell->getPort("\\R"));
+ else if (cell->type.in("$_DFF_NP1_", "$_DFF_PP1_"))
+ D = holes_module->MuxGate(NEW_ID, D, State::S1, cell->getPort("\\R"));
+ // Remove the $_DFF_* cell from what needs to be a combinatorial box
+ holes_module->remove(cell);
+ Wire *port;
+ if (GetSize(Q.wire) == 1)
+ port = holes_module->wire(stringf("$abc%s", Q.wire->name.c_str()));
+ else
+ port = holes_module->wire(stringf("$abc%s[%d]", Q.wire->name.c_str(), Q.offset));
+ log_assert(port);
+ // Prepare to replace "assign <port> = $_DFF_*.Q;" with "assign <port> = $_DFF_*.D;"
+ // in order to extract just the combinatorial control logic that feeds the box
+ // (i.e. clock enable, synchronous reset, etc.)
+ replace.insert(std::make_pair(Q,D));
+ // Since `flatten` above would have created wires named "<cell>.Q",
+ // extract the pre-techmap cell name
+ auto pos = Q.wire->name.str().rfind(".");
+ log_assert(pos != std::string::npos);
+ IdString driver = Q.wire->name.substr(0, pos);
+ // And drive the signal that was previously driven by "DFF.Q" (typically
+ // used to implement clock-enable functionality) with the "<cell>.$abc9_currQ"
+ // wire (which itself is driven an by input port) we inserted above
+ Wire *currQ = holes_module->wire(stringf("%s.abc9_ff.Q", driver.c_str()));
+ log_assert(currQ);
+ holes_module->connect(Q, currQ);
+ }
+
+ for (auto &conn : holes_module->connections_)
+ conn.second = replace.at(sigmap(conn.second), conn.second);
+ }
+}
+
+void prep_xaiger(RTLIL::Module *module, bool dff)
+{
+ auto design = module->design;
+ log_assert(design);
+
+ SigMap sigmap(module);
+
+ dict<SigBit, pool<IdString>> bit_drivers, bit_users;
+ TopoSort<IdString, RTLIL::sort_by_id_str> toposort;
+ dict<IdString, std::vector<IdString>> box_ports;
+
+ for (auto cell : module->cells()) {
+ if (cell->type == "$__ABC9_FF_")
+ continue;
+ if (cell->has_keep_attr())
+ continue;
+
+ auto inst_module = module->design->module(cell->type);
+ bool abc9_box = inst_module && inst_module->attributes.count("\\abc9_box_id");
+ bool abc9_flop = false;
+ if (abc9_box) {
+ abc9_flop = inst_module->get_bool_attribute("\\abc9_flop");
+ if (abc9_flop && !dff)
+ continue;
+
+ auto r = box_ports.insert(cell->type);
+ if (r.second) {
+ // Make carry in the last PI, and carry out the last PO
+ // since ABC requires it this way
+ IdString carry_in, carry_out;
+ for (const auto &port_name : inst_module->ports) {
+ auto w = inst_module->wire(port_name);
+ log_assert(w);
+ if (w->get_bool_attribute("\\abc9_carry")) {
+ if (w->port_input) {
+ if (carry_in != IdString())
+ log_error("Module '%s' contains more than one 'abc9_carry' input port.\n", log_id(inst_module));
+ carry_in = port_name;
+ }
+ if (w->port_output) {
+ if (carry_out != IdString())
+ log_error("Module '%s' contains more than one 'abc9_carry' output port.\n", log_id(inst_module));
+ carry_out = port_name;
+ }
+ }
+ else
+ r.first->second.push_back(port_name);
+ }
+
+ if (carry_in != IdString() && carry_out == IdString())
+ log_error("Module '%s' contains an 'abc9_carry' input port but no output port.\n", log_id(inst_module));
+ if (carry_in == IdString() && carry_out != IdString())
+ log_error("Module '%s' contains an 'abc9_carry' output port but no input port.\n", log_id(inst_module));
+ if (carry_in != IdString()) {
+ r.first->second.push_back(carry_in);
+ r.first->second.push_back(carry_out);
+ }
+ }
+ }
+ else if (!yosys_celltypes.cell_known(cell->type))
+ continue;
+
+ // TODO: Speed up toposort -- we care about box ordering only
+ for (auto conn : cell->connections()) {
+ if (cell->input(conn.first))
+ for (auto bit : sigmap(conn.second))
+ bit_users[bit].insert(cell->name);
+
+ if (cell->output(conn.first) && !abc9_flop)
+ for (auto bit : sigmap(conn.second))
+ bit_drivers[bit].insert(cell->name);
+ }
+ toposort.node(cell->name);
+ }
+
+ if (box_ports.empty())
+ return;
+
+ for (auto &it : bit_users)
+ if (bit_drivers.count(it.first))
+ for (auto driver_cell : bit_drivers.at(it.first))
+ for (auto user_cell : it.second)
+ toposort.edge(driver_cell, user_cell);
+
+ if (ys_debug(1))
+ toposort.analyze_loops = true;
+
+ bool no_loops YS_ATTRIBUTE(unused) = toposort.sort();
+
+ if (ys_debug(1)) {
+ unsigned i = 0;
+ for (auto &it : toposort.loops) {
+ log(" loop %d\n", i++);
+ for (auto cell_name : it) {
+ auto cell = module->cell(cell_name);
+ log_assert(cell);
+ log("\t%s (%s @ %s)\n", log_id(cell), log_id(cell->type), cell->get_src_attribute().c_str());
+ }
+ }
+ }
+
+ log_assert(no_loops);
+
+ RTLIL::Module *holes_module = design->addModule(stringf("%s$holes", module->name.c_str()));
+ log_assert(holes_module);
+ holes_module->set_bool_attribute("\\abc9_holes");
+
+ dict<IdString, Cell*> cell_cache;
+
+ int port_id = 1, box_count = 0;
+ for (auto cell_name : toposort.sorted) {
+ RTLIL::Cell *cell = module->cell(cell_name);
+ log_assert(cell);
+
+ RTLIL::Module* box_module = design->module(cell->type);
+ if (!box_module || !box_module->attributes.count("\\abc9_box_id"))
+ continue;
+
+ cell->attributes["\\abc9_box_seq"] = box_count++;
+
+ IdString derived_name = box_module->derive(design, cell->parameters);
+ box_module = design->module(derived_name);
+
+ auto r = cell_cache.insert(derived_name);
+ auto &holes_cell = r.first->second;
+ if (r.second) {
+ if (box_module->has_processes())
+ Pass::call_on_module(design, box_module, "proc");
+
+ if (box_module->get_bool_attribute("\\whitebox")) {
+ holes_cell = holes_module->addCell(cell->name, derived_name);
+
+ int box_inputs = 0;
+ for (auto port_name : box_ports.at(cell->type)) {
+ RTLIL::Wire *w = box_module->wire(port_name);
+ log_assert(w);
+ log_assert(!w->port_input || !w->port_output);
+ auto &conn = holes_cell->connections_[port_name];
+ if (w->port_input) {
+ for (int i = 0; i < GetSize(w); i++) {
+ box_inputs++;
+ RTLIL::Wire *holes_wire = holes_module->wire(stringf("\\i%d", box_inputs));
+ if (!holes_wire) {
+ holes_wire = holes_module->addWire(stringf("\\i%d", box_inputs));
+ holes_wire->port_input = true;
+ holes_wire->port_id = port_id++;
+ holes_module->ports.push_back(holes_wire->name);
+ }
+ conn.append(holes_wire);
+ }
+ }
+ else if (w->port_output)
+ conn = holes_module->addWire(stringf("%s.%s", derived_name.c_str(), log_id(port_name)), GetSize(w));
+ }
+
+ // For flops only, create an extra 1-bit input that drives a new wire
+ // called "<cell>.abc9_ff.Q" that is used below
+ if (box_module->get_bool_attribute("\\abc9_flop")) {
+ box_inputs++;
+ Wire *holes_wire = holes_module->wire(stringf("\\i%d", box_inputs));
+ if (!holes_wire) {
+ holes_wire = holes_module->addWire(stringf("\\i%d", box_inputs));
+ holes_wire->port_input = true;
+ holes_wire->port_id = port_id++;
+ holes_module->ports.push_back(holes_wire->name);
+ }
+ Wire *Q = holes_module->addWire(stringf("%s.abc9_ff.Q", cell->name.c_str()));
+ holes_module->connect(Q, holes_wire);
+ }
+ }
+ else // box_module is a blackbox
+ log_assert(holes_cell == nullptr);
+ }
+
+ for (auto port_name : box_ports.at(cell->type)) {
+ RTLIL::Wire *w = box_module->wire(port_name);
+ log_assert(w);
+ if (!w->port_output)
+ continue;
+ Wire *holes_wire = holes_module->addWire(stringf("$abc%s.%s", cell->name.c_str(), log_id(port_name)), GetSize(w));
+ holes_wire->port_output = true;
+ holes_wire->port_id = port_id++;
+ holes_module->ports.push_back(holes_wire->name);
+ if (holes_cell) // whitebox
+ holes_module->connect(holes_wire, holes_cell->getPort(port_name));
+ else // blackbox
+ holes_module->connect(holes_wire, Const(State::S0, GetSize(w)));
+ }
+ }
+}
+
+void reintegrate(RTLIL::Module *module)
+{
+ auto design = module->design;
+ log_assert(design);
+
+ map_autoidx = autoidx++;
+
+ RTLIL::Module *mapped_mod = design->module(stringf("%s$abc9", module->name.c_str()));
+ if (mapped_mod == NULL)
+ log_error("ABC output file does not contain a module `%s$abc'.\n", log_id(module));
+
+ for (auto w : mapped_mod->wires())
+ module->addWire(remap_name(w->name), GetSize(w));
+
+ dict<IdString,std::vector<IdString>> box_ports;
+
+ for (auto m : design->modules()) {
+ if (!m->attributes.count(ID(abc9_box_id)))
+ continue;
+
+ auto r = box_ports.insert(m->name);
+ if (r.second) {
+ // Make carry in the last PI, and carry out the last PO
+ // since ABC requires it this way
+ IdString carry_in, carry_out;
+ for (const auto &port_name : m->ports) {
+ auto w = m->wire(port_name);
+ log_assert(w);
+ if (w->get_bool_attribute("\\abc9_carry")) {
+ if (w->port_input) {
+ if (carry_in != IdString())
+ log_error("Module '%s' contains more than one 'abc9_carry' input port.\n", log_id(m));
+ carry_in = port_name;
+ }
+ if (w->port_output) {
+ if (carry_out != IdString())
+ log_error("Module '%s' contains more than one 'abc9_carry' output port.\n", log_id(m));
+ carry_out = port_name;
+ }
+ }
+ else
+ r.first->second.push_back(port_name);
+ }
+
+ if (carry_in != IdString() && carry_out == IdString())
+ log_error("Module '%s' contains an 'abc9_carry' input port but no output port.\n", log_id(m));
+ if (carry_in == IdString() && carry_out != IdString())
+ log_error("Module '%s' contains an 'abc9_carry' output port but no input port.\n", log_id(m));
+ if (carry_in != IdString()) {
+ r.first->second.push_back(carry_in);
+ r.first->second.push_back(carry_out);
+ }
+ }
+ }
+
+ std::vector<Cell*> boxes;
+ for (auto cell : module->cells().to_vector()) {
+ if (cell->has_keep_attr())
+ continue;
+ if (cell->type.in(ID($_AND_), ID($_NOT_), ID($__ABC9_FF_)))
+ module->remove(cell);
+ else if (cell->attributes.erase("\\abc9_box_seq"))
+ boxes.emplace_back(cell);
+ }
+
+ dict<SigBit, pool<IdString>> bit_drivers, bit_users;
+ TopoSort<IdString, RTLIL::sort_by_id_str> toposort;
+ dict<RTLIL::Cell*,RTLIL::Cell*> not2drivers;
+ dict<SigBit, std::vector<RTLIL::Cell*>> bit2sinks;
+
+ std::map<IdString, int> cell_stats;
+ for (auto mapped_cell : mapped_mod->cells())
+ {
+ // TODO: Speed up toposort -- we care about NOT ordering only
+ toposort.node(mapped_cell->name);
+
+ if (mapped_cell->type == ID($_NOT_)) {
+ RTLIL::SigBit a_bit = mapped_cell->getPort(ID::A);
+ RTLIL::SigBit y_bit = mapped_cell->getPort(ID::Y);
+ bit_users[a_bit].insert(mapped_cell->name);
+ // Ignore inouts for topo ordering
+ if (y_bit.wire && !(y_bit.wire->port_input && y_bit.wire->port_output))
+ bit_drivers[y_bit].insert(mapped_cell->name);
+
+ if (!a_bit.wire) {
+ mapped_cell->setPort(ID::Y, module->addWire(NEW_ID));
+ RTLIL::Wire *wire = module->wire(remap_name(y_bit.wire->name));
+ log_assert(wire);
+ module->connect(RTLIL::SigBit(wire, y_bit.offset), State::S1);
+ }
+ else {
+ RTLIL::Cell* driver_lut = nullptr;
+ // ABC can return NOT gates that drive POs
+ if (!a_bit.wire->port_input) {
+ // If it's not a NOT gate that that comes from a PI directly,
+ // find the driver LUT and clone that to guarantee that we won't
+ // increase the max logic depth
+ // (TODO: Optimise by not cloning unless will increase depth)
+ RTLIL::IdString driver_name;
+ if (GetSize(a_bit.wire) == 1)
+ driver_name = stringf("$lut%s", a_bit.wire->name.c_str());
+ else
+ driver_name = stringf("$lut%s[%d]", a_bit.wire->name.c_str(), a_bit.offset);
+ driver_lut = mapped_mod->cell(driver_name);
+ }
+
+ if (!driver_lut) {
+ // If a driver couldn't be found (could be from PI or box CI)
+ // then implement using a LUT
+ RTLIL::Cell *cell = module->addLut(remap_name(stringf("$lut%s", mapped_cell->name.c_str())),
+ RTLIL::SigBit(module->wires_.at(remap_name(a_bit.wire->name)), a_bit.offset),
+ RTLIL::SigBit(module->wires_.at(remap_name(y_bit.wire->name)), y_bit.offset),
+ RTLIL::Const::from_string("01"));
+ bit2sinks[cell->getPort(ID::A)].push_back(cell);
+ cell_stats[ID($lut)]++;
+ }
+ else
+ not2drivers[mapped_cell] = driver_lut;
+ }
+ continue;
+ }
+
+ if (mapped_cell->type.in(ID($lut), ID($__ABC9_FF_))) {
+ // Convert buffer into direct connection
+ if (mapped_cell->type == ID($lut) &&
+ GetSize(mapped_cell->getPort(ID::A)) == 1 &&
+ mapped_cell->getParam(ID(LUT)) == RTLIL::Const::from_string("01")) {
+ SigSpec my_a = module->wires_.at(remap_name(mapped_cell->getPort(ID::A).as_wire()->name));
+ SigSpec my_y = module->wires_.at(remap_name(mapped_cell->getPort(ID::Y).as_wire()->name));
+ module->connect(my_y, my_a);
+ log_abort();
+ continue;
+ }
+ RTLIL::Cell *cell = module->addCell(remap_name(mapped_cell->name), mapped_cell->type);
+ cell->parameters = mapped_cell->parameters;
+ cell->attributes = mapped_cell->attributes;
+
+ for (auto &mapped_conn : mapped_cell->connections()) {
+ RTLIL::SigSpec newsig;
+ for (auto c : mapped_conn.second.chunks()) {
+ if (c.width == 0)
+ continue;
+ //log_assert(c.width == 1);
+ if (c.wire)
+ c.wire = module->wires_.at(remap_name(c.wire->name));
+ newsig.append(c);
+ }
+ cell->setPort(mapped_conn.first, newsig);
+
+ if (cell->input(mapped_conn.first)) {
+ for (auto i : newsig)
+ bit2sinks[i].push_back(cell);
+ for (auto i : mapped_conn.second)
+ bit_users[i].insert(mapped_cell->name);
+ }
+ if (cell->output(mapped_conn.first))
+ for (auto i : mapped_conn.second)
+ // Ignore inouts for topo ordering
+ if (i.wire && !(i.wire->port_input && i.wire->port_output))
+ bit_drivers[i].insert(mapped_cell->name);
+ }
+ }
+ else {
+ RTLIL::Cell *existing_cell = module->cell(mapped_cell->name);
+ log_assert(existing_cell);
+
+ RTLIL::Module* box_module = design->module(existing_cell->type);
+ auto it = box_module->attributes.find(ID(abc9_box_id));
+ log_assert(it != box_module->attributes.end());
+ log_assert(mapped_cell->type == stringf("$__boxid%d", it->second.as_int()));
+ mapped_cell->type = existing_cell->type;
+
+ RTLIL::Cell *cell = module->addCell(remap_name(mapped_cell->name), mapped_cell->type);
+ cell->parameters = existing_cell->parameters;
+ cell->attributes = existing_cell->attributes;
+ module->swap_names(cell, existing_cell);
+
+ auto jt = mapped_cell->connections_.find("\\i");
+ log_assert(jt != mapped_cell->connections_.end());
+ SigSpec inputs = std::move(jt->second);
+ mapped_cell->connections_.erase(jt);
+ jt = mapped_cell->connections_.find("\\o");
+ log_assert(jt != mapped_cell->connections_.end());
+ SigSpec outputs = std::move(jt->second);
+ mapped_cell->connections_.erase(jt);
+
+ auto abc9_flop = box_module->attributes.count("\\abc9_flop");
+ if (!abc9_flop) {
+ for (const auto &i : inputs)
+ bit_users[i].insert(mapped_cell->name);
+ for (const auto &i : outputs)
+ // Ignore inouts for topo ordering
+ if (i.wire && !(i.wire->port_input && i.wire->port_output))
+ bit_drivers[i].insert(mapped_cell->name);
+ }
+
+ int input_count = 0, output_count = 0;
+ for (const auto &port_name : box_ports.at(cell->type)) {
+ RTLIL::Wire *w = box_module->wire(port_name);
+ log_assert(w);
+
+ SigSpec sig;
+ if (w->port_input) {
+ sig = inputs.extract(input_count, GetSize(w));
+ input_count += GetSize(w);
+ }
+ if (w->port_output) {
+ sig = outputs.extract(output_count, GetSize(w));
+ output_count += GetSize(w);
+ }
+
+ SigSpec newsig;
+ for (auto c : sig.chunks()) {
+ if (c.width == 0)
+ continue;
+ //log_assert(c.width == 1);
+ if (c.wire)
+ c.wire = module->wires_.at(remap_name(c.wire->name));
+ newsig.append(c);
+ }
+ cell->setPort(port_name, newsig);
+
+ if (w->port_input && !abc9_flop)
+ for (const auto &i : newsig)
+ bit2sinks[i].push_back(cell);
+ }
+ }
+
+ cell_stats[mapped_cell->type]++;
+ }
+
+ for (auto cell : boxes)
+ module->remove(cell);
+
+ // Copy connections (and rename) from mapped_mod to module
+ for (auto conn : mapped_mod->connections()) {
+ if (!conn.first.is_fully_const()) {
+ auto chunks = conn.first.chunks();
+ for (auto &c : chunks)
+ c.wire = module->wires_.at(remap_name(c.wire->name));
+ conn.first = std::move(chunks);
+ }
+ if (!conn.second.is_fully_const()) {
+ auto chunks = conn.second.chunks();
+ for (auto &c : chunks)
+ if (c.wire)
+ c.wire = module->wires_.at(remap_name(c.wire->name));
+ conn.second = std::move(chunks);
+ }
+ module->connect(conn);
+ }
+
+ for (auto &it : cell_stats)
+ log("ABC RESULTS: %15s cells: %8d\n", it.first.c_str(), it.second);
+ int in_wires = 0, out_wires = 0;
+
+ // Stitch in mapped_mod's inputs/outputs into module
+ for (auto port : mapped_mod->ports) {
+ RTLIL::Wire *mapped_wire = mapped_mod->wire(port);
+ RTLIL::Wire *wire = module->wire(port);
+ log_assert(wire);
+ if (wire->attributes.erase(ID(abc9_scc_id))) {
+ auto r YS_ATTRIBUTE(unused) = wire->attributes.erase(ID::keep);
+ log_assert(r);
+ }
+ RTLIL::Wire *remap_wire = module->wire(remap_name(port));
+ RTLIL::SigSpec signal(wire, 0, GetSize(remap_wire));
+ log_assert(GetSize(signal) >= GetSize(remap_wire));
+
+ RTLIL::SigSig conn;
+ if (mapped_wire->port_output) {
+ conn.first = signal;
+ conn.second = remap_wire;
+ out_wires++;
+ module->connect(conn);
+ }
+ else if (mapped_wire->port_input) {
+ conn.first = remap_wire;
+ conn.second = signal;
+ in_wires++;
+ module->connect(conn);
+ }
+ }
+
+ // ABC9 will return $_NOT_ gates in its mapping (since they are
+ // treated as being "free"), in particular driving primary
+ // outputs (real primary outputs, or cells treated as blackboxes)
+ // or driving box inputs.
+ // Instead of just mapping those $_NOT_ gates into 2-input $lut-s
+ // at an area and delay cost, see if it is possible to push
+ // this $_NOT_ into the driving LUT, or into all sink LUTs.
+ // When this is not possible, (i.e. this signal drives two primary
+ // outputs, only one of which is complemented) and when the driver
+ // is a LUT, then clone the LUT so that it can be inverted without
+ // increasing depth/delay.
+ for (auto &it : bit_users)
+ if (bit_drivers.count(it.first))
+ for (auto driver_cell : bit_drivers.at(it.first))
+ for (auto user_cell : it.second)
+ toposort.edge(driver_cell, user_cell);
+ bool no_loops YS_ATTRIBUTE(unused) = toposort.sort();
+ log_assert(no_loops);
+
+ for (auto ii = toposort.sorted.rbegin(); ii != toposort.sorted.rend(); ii++) {
+ RTLIL::Cell *not_cell = mapped_mod->cell(*ii);
+ log_assert(not_cell);
+ if (not_cell->type != ID($_NOT_))
+ continue;
+ auto it = not2drivers.find(not_cell);
+ if (it == not2drivers.end())
+ continue;
+ RTLIL::Cell *driver_lut = it->second;
+ RTLIL::SigBit a_bit = not_cell->getPort(ID::A);
+ RTLIL::SigBit y_bit = not_cell->getPort(ID::Y);
+ RTLIL::Const driver_mask;
+
+ a_bit.wire = module->wires_.at(remap_name(a_bit.wire->name));
+ y_bit.wire = module->wires_.at(remap_name(y_bit.wire->name));
+
+ auto jt = bit2sinks.find(a_bit);
+ if (jt == bit2sinks.end())
+ goto clone_lut;
+
+ for (auto sink_cell : jt->second)
+ if (sink_cell->type != ID($lut))
+ goto clone_lut;
+
+ // Push downstream LUTs past inverter
+ for (auto sink_cell : jt->second) {
+ SigSpec A = sink_cell->getPort(ID::A);
+ RTLIL::Const mask = sink_cell->getParam(ID(LUT));
+ int index = 0;
+ for (; index < GetSize(A); index++)
+ if (A[index] == a_bit)
+ break;
+ log_assert(index < GetSize(A));
+ int i = 0;
+ while (i < GetSize(mask)) {
+ for (int j = 0; j < (1 << index); j++)
+ std::swap(mask[i+j], mask[i+j+(1 << index)]);
+ i += 1 << (index+1);
+ }
+ A[index] = y_bit;
+ sink_cell->setPort(ID::A, A);
+ sink_cell->setParam(ID(LUT), mask);
+ }
+
+ // Since we have rewritten all sinks (which we know
+ // to be only LUTs) to be after the inverter, we can
+ // go ahead and clone the LUT with the expectation
+ // that the original driving LUT will become dangling
+ // and get cleaned away
+clone_lut:
+ driver_mask = driver_lut->getParam(ID(LUT));
+ for (auto &b : driver_mask.bits) {
+ if (b == RTLIL::State::S0) b = RTLIL::State::S1;
+ else if (b == RTLIL::State::S1) b = RTLIL::State::S0;
+ }
+ auto cell = module->addLut(NEW_ID,
+ driver_lut->getPort(ID::A),
+ y_bit,
+ driver_mask);
+ for (auto &bit : cell->connections_.at(ID::A)) {
+ bit.wire = module->wires_.at(remap_name(bit.wire->name));
+ bit2sinks[bit].push_back(cell);
+ }
+ }
+
+ //log("ABC RESULTS: internal signals: %8d\n", int(signal_list.size()) - in_wires - out_wires);
+ log("ABC RESULTS: input signals: %8d\n", in_wires);
+ log("ABC RESULTS: output signals: %8d\n", out_wires);
+
+ design->remove(mapped_mod);
+}
+
+struct Abc9OpsPass : public Pass {
+ Abc9OpsPass() : Pass("abc9_ops", "helper functions for ABC9") { }
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" abc9_ops [options] [selection]\n");
+ log("\n");
+ log("This pass contains a set of supporting operations for use during ABC technology\n");
+ log("mapping, and is expected to be called in conjunction with other operations from\n");
+ log("the `abc9' script pass. Only fully-selected modules are supported.\n");
+ log("\n");
+ log(" -mark_scc\n");
+ log(" for an arbitrarily chosen cell in each unique SCC of each selected module\n");
+ log(" (tagged with an (* abc9_scc_id = <int> *) attribute), temporarily mark all\n");
+ log(" wires driven by this cell's outputs with a (* keep *) attribute in order\n");
+ log(" to break the SCC. this temporary attribute will be removed on -reintegrate.\n");
+ log("\n");
+ log(" -prep_xaiger\n");
+ log(" prepare the design for XAIGER output. this includes computing the\n");
+ log(" topological ordering of ABC9 boxes, as well as preparing the\n");
+ log(" '<module-name>$holes' module that contains the logic behaviour of ABC9\n");
+ log(" whiteboxes.\n");
+ log("\n");
+ log(" -dff\n");
+ log(" consider flop cells (those instantiating modules marked with (* abc9_flop *)\n");
+ log(" during -prep_xaiger.\n");
+ log("\n");
+ log(" -prep_dff\n");
+ log(" compute the clock domain and initial value of each flop in the design.\n");
+ log(" process the '$holes' module to support clock-enable functionality.\n");
+ log("\n");
+ log(" -reintegrate\n");
+ log(" for each selected module, re-intergrate the module '<module-name>$abc9'\n");
+ log(" by first recovering ABC9 boxes, and then stitching in the remaining primary\n");
+ log(" inputs and outputs.\n");
+ log("\n");
+ }
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ log_header(design, "Executing ABC9_OPS pass (helper functions for ABC9).\n");
+
+ bool mark_scc_mode = false;
+ bool prep_dff_mode = false;
+ bool prep_xaiger_mode = false;
+ bool reintegrate_mode = false;
+ bool dff_mode = false;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ std::string arg = args[argidx];
+ if (arg == "-mark_scc") {
+ mark_scc_mode = true;
+ continue;
+ }
+ if (arg == "-prep_dff") {
+ prep_dff_mode = true;
+ continue;
+ }
+ if (arg == "-prep_xaiger") {
+ prep_xaiger_mode = true;
+ continue;
+ }
+ if (arg == "-reintegrate") {
+ reintegrate_mode = true;
+ continue;
+ }
+ if (arg == "-dff") {
+ dff_mode = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ if (!(mark_scc_mode || prep_dff_mode || reintegrate_mode))
+ log_cmd_error("At least one of -mark_scc, -prep_{xaiger,dff}, -reintegrate must be specified.\n");
+
+ if (dff_mode && !prep_xaiger_mode)
+ log_cmd_error("'-dff' option is only relevant for -prep_xaiger.\n");
+
+ for (auto mod : design->selected_modules()) {
+ if (mod->get_bool_attribute("\\abc9_holes"))
+ continue;
+
+ if (mod->processes.size() > 0) {
+ log("Skipping module %s as it contains processes.\n", log_id(mod));
+ continue;
+ }
+
+ if (!design->selected_whole_module(mod))
+ log_error("Can't handle partially selected module %s!\n", log_id(mod));
+
+ if (mark_scc_mode)
+ mark_scc(mod);
+ if (prep_dff_mode)
+ prep_dff(mod);
+ if (prep_xaiger_mode)
+ prep_xaiger(mod, dff_mode);
+ if (reintegrate_mode)
+ reintegrate(mod);
+ }
+ }
+} Abc9OpsPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/techmap/aigmap.cc b/passes/techmap/aigmap.cc
index b9ac7aded..2ecb2f35a 100644
--- a/passes/techmap/aigmap.cc
+++ b/passes/techmap/aigmap.cc
@@ -25,8 +25,9 @@ PRIVATE_NAMESPACE_BEGIN
struct AigmapPass : public Pass {
AigmapPass() : Pass("aigmap", "map logic to and-inverter-graph circuit") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
log(" aigmap [options] [selection]\n");
log("\n");
@@ -36,10 +37,15 @@ struct AigmapPass : public Pass {
log(" -nand\n");
log(" Enable creation of $_NAND_ cells\n");
log("\n");
+ log(" -select\n");
+ log(" Overwrite replaced cells in the current selection with new $_AND_,\n");
+ log(" $_NOT_, and $_NAND_, cells\n");
+
+ log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
- bool nand_mode = false;
+ bool nand_mode = false, select_mode = false;
log_header(design, "Executing AIGMAP pass (map logic to AIG).\n");
@@ -50,6 +56,10 @@ struct AigmapPass : public Pass {
nand_mode = true;
continue;
}
+ if (args[argidx] == "-select") {
+ select_mode = true;
+ continue;
+ }
break;
}
extra_args(args, argidx, design);
@@ -62,19 +72,22 @@ struct AigmapPass : public Pass {
dict<IdString, int> stat_not_replaced;
int orig_num_cells = GetSize(module->cells());
+ pool<IdString> new_sel;
for (auto cell : module->selected_cells())
{
Aig aig(cell);
- if (cell->type == "$_AND_" || cell->type == "$_NOT_")
+ if (cell->type.in(ID($_AND_), ID($_NOT_)))
aig.name.clear();
- if (nand_mode && cell->type == "$_NAND_")
+ if (nand_mode && cell->type == ID($_NAND_))
aig.name.clear();
if (aig.name.empty()) {
not_replaced_count++;
stat_not_replaced[cell->type]++;
+ if (select_mode)
+ new_sel.insert(cell->name);
continue;
}
@@ -95,19 +108,33 @@ struct AigmapPass : public Pass {
SigBit A = sigs.at(node.left_parent);
SigBit B = sigs.at(node.right_parent);
if (nand_mode && node.inverter) {
- bit = module->NandGate(NEW_ID, A, B);
+ bit = module->addWire(NEW_ID);
+ auto gate = module->addNandGate(NEW_ID, A, B, bit);
+ if (select_mode)
+ new_sel.insert(gate->name);
+
goto skip_inverter;
} else {
pair<int, int> key(node.left_parent, node.right_parent);
if (and_cache.count(key))
bit = and_cache.at(key);
- else
- bit = module->AndGate(NEW_ID, A, B);
+ else {
+ bit = module->addWire(NEW_ID);
+ auto gate = module->addAndGate(NEW_ID, A, B, bit);
+ if (select_mode)
+ new_sel.insert(gate->name);
+ }
}
}
- if (node.inverter)
- bit = module->NotGate(NEW_ID, bit);
+ if (node.inverter) {
+ SigBit new_bit = module->addWire(NEW_ID);
+ auto gate = module->addNotGate(NEW_ID, bit, new_bit);
+ bit = new_bit;
+ if (select_mode)
+ new_sel.insert(gate->name);
+
+ }
skip_inverter:
for (auto &op : node.outports)
@@ -142,6 +169,13 @@ struct AigmapPass : public Pass {
for (auto cell : replaced_cells)
module->remove(cell);
+
+ if (select_mode) {
+ log_assert(!design->selection_stack.empty());
+ RTLIL::Selection& sel = design->selection_stack.back();
+ sel.selected_members[module->name] = std::move(new_sel);
+ }
+
}
}
} AigmapPass;
diff --git a/passes/techmap/alumacc.cc b/passes/techmap/alumacc.cc
index 95be7ab3b..034731b87 100644
--- a/passes/techmap/alumacc.cc
+++ b/passes/techmap/alumacc.cc
@@ -48,20 +48,31 @@ struct AlumaccWorker
RTLIL::SigSpec cached_cf, cached_of, cached_sf;
RTLIL::SigSpec get_lt() {
- if (GetSize(cached_lt) == 0)
- cached_lt = is_signed ? alu_cell->module->Xor(NEW_ID, get_of(), get_sf()) : get_cf();
+ if (GetSize(cached_lt) == 0) {
+ if (is_signed) {
+ get_of();
+ get_sf();
+ cached_lt = alu_cell->module->Xor(NEW_ID, cached_of, cached_sf);
+ }
+ else
+ cached_lt = get_cf();
+ }
return cached_lt;
}
RTLIL::SigSpec get_gt() {
- if (GetSize(cached_gt) == 0)
- cached_gt = alu_cell->module->Not(NEW_ID, alu_cell->module->Or(NEW_ID, get_lt(), get_eq()), false, alu_cell->get_src_attribute());
+ if (GetSize(cached_gt) == 0) {
+ get_lt();
+ get_eq();
+ SigSpec Or = alu_cell->module->Or(NEW_ID, cached_lt, cached_eq);
+ cached_gt = alu_cell->module->Not(NEW_ID, Or, false, alu_cell->get_src_attribute());
+ }
return cached_gt;
}
RTLIL::SigSpec get_eq() {
if (GetSize(cached_eq) == 0)
- cached_eq = alu_cell->module->ReduceAnd(NEW_ID, alu_cell->getPort("\\X"), false, alu_cell->get_src_attribute());
+ cached_eq = alu_cell->module->ReduceAnd(NEW_ID, alu_cell->getPort(ID(X)), false, alu_cell->get_src_attribute());
return cached_eq;
}
@@ -73,7 +84,7 @@ struct AlumaccWorker
RTLIL::SigSpec get_cf() {
if (GetSize(cached_cf) == 0) {
- cached_cf = alu_cell->getPort("\\CO");
+ cached_cf = alu_cell->getPort(ID(CO));
log_assert(GetSize(cached_cf) >= 1);
cached_cf = alu_cell->module->Not(NEW_ID, cached_cf[GetSize(cached_cf)-1], false, alu_cell->get_src_attribute());
}
@@ -82,7 +93,7 @@ struct AlumaccWorker
RTLIL::SigSpec get_of() {
if (GetSize(cached_of) == 0) {
- cached_of = {alu_cell->getPort("\\CO"), alu_cell->getPort("\\CI")};
+ cached_of = {alu_cell->getPort(ID(CO)), alu_cell->getPort(ID(CI))};
log_assert(GetSize(cached_of) >= 2);
cached_of = alu_cell->module->Xor(NEW_ID, cached_of[GetSize(cached_of)-1], cached_of[GetSize(cached_of)-2]);
}
@@ -91,7 +102,7 @@ struct AlumaccWorker
RTLIL::SigSpec get_sf() {
if (GetSize(cached_sf) == 0) {
- cached_sf = alu_cell->getPort("\\Y");
+ cached_sf = alu_cell->getPort(ID::Y);
cached_sf = cached_sf[GetSize(cached_sf)-1];
}
return cached_sf;
@@ -125,7 +136,7 @@ struct AlumaccWorker
{
for (auto cell : module->selected_cells())
{
- if (!cell->type.in("$pos", "$neg", "$add", "$sub", "$mul"))
+ if (!cell->type.in(ID($pos), ID($neg), ID($add), ID($sub), ID($mul)))
continue;
log(" creating $macc model for %s (%s).\n", log_id(cell), log_id(cell->type));
@@ -134,38 +145,38 @@ struct AlumaccWorker
Macc::port_t new_port;
n->cell = cell;
- n->y = sigmap(cell->getPort("\\Y"));
+ n->y = sigmap(cell->getPort(ID::Y));
n->users = 0;
for (auto bit : n->y)
n->users = max(n->users, bit_users.at(bit) - 1);
- if (cell->type.in("$pos", "$neg"))
+ if (cell->type.in(ID($pos), ID($neg)))
{
- new_port.in_a = sigmap(cell->getPort("\\A"));
- new_port.is_signed = cell->getParam("\\A_SIGNED").as_bool();
- new_port.do_subtract = cell->type == "$neg";
+ new_port.in_a = sigmap(cell->getPort(ID::A));
+ new_port.is_signed = cell->getParam(ID(A_SIGNED)).as_bool();
+ new_port.do_subtract = cell->type == ID($neg);
n->macc.ports.push_back(new_port);
}
- if (cell->type.in("$add", "$sub"))
+ if (cell->type.in(ID($add), ID($sub)))
{
- new_port.in_a = sigmap(cell->getPort("\\A"));
- new_port.is_signed = cell->getParam("\\A_SIGNED").as_bool();
+ new_port.in_a = sigmap(cell->getPort(ID::A));
+ new_port.is_signed = cell->getParam(ID(A_SIGNED)).as_bool();
new_port.do_subtract = false;
n->macc.ports.push_back(new_port);
- new_port.in_a = sigmap(cell->getPort("\\B"));
- new_port.is_signed = cell->getParam("\\B_SIGNED").as_bool();
- new_port.do_subtract = cell->type == "$sub";
+ new_port.in_a = sigmap(cell->getPort(ID::B));
+ new_port.is_signed = cell->getParam(ID(B_SIGNED)).as_bool();
+ new_port.do_subtract = cell->type == ID($sub);
n->macc.ports.push_back(new_port);
}
- if (cell->type.in("$mul"))
+ if (cell->type.in(ID($mul)))
{
- new_port.in_a = sigmap(cell->getPort("\\A"));
- new_port.in_b = sigmap(cell->getPort("\\B"));
- new_port.is_signed = cell->getParam("\\A_SIGNED").as_bool();
+ new_port.in_a = sigmap(cell->getPort(ID::A));
+ new_port.in_b = sigmap(cell->getPort(ID::B));
+ new_port.is_signed = cell->getParam(ID(A_SIGNED)).as_bool();
new_port.do_subtract = false;
n->macc.ports.push_back(new_port);
}
@@ -315,7 +326,7 @@ struct AlumaccWorker
}
if (subtract_b)
- C.append(RTLIL::S1);
+ C.append(State::S1);
if (GetSize(C) > 1)
goto next_macc;
@@ -351,7 +362,7 @@ struct AlumaccWorker
for (auto &it : sig_macc)
{
auto n = it.second;
- auto cell = module->addCell(NEW_ID, "$macc");
+ auto cell = module->addCell(NEW_ID, ID($macc));
macc_counter++;
@@ -361,7 +372,7 @@ struct AlumaccWorker
n->macc.optimize(GetSize(n->y));
n->macc.to_cell(cell);
- cell->setPort("\\Y", n->y);
+ cell->setPort(ID::Y, n->y);
cell->fixup_parameters();
module->remove(n->cell);
delete n;
@@ -376,9 +387,9 @@ struct AlumaccWorker
for (auto cell : module->selected_cells())
{
- if (cell->type.in("$lt", "$le", "$ge", "$gt"))
+ if (cell->type.in(ID($lt), ID($le), ID($ge), ID($gt)))
lge_cells.push_back(cell);
- if (cell->type.in("$eq", "$eqx", "$ne", "$nex"))
+ if (cell->type.in(ID($eq), ID($eqx), ID($ne), ID($nex)))
eq_cells.push_back(cell);
}
@@ -386,13 +397,13 @@ struct AlumaccWorker
{
log(" creating $alu model for %s (%s):", log_id(cell), log_id(cell->type));
- bool cmp_less = cell->type.in("$lt", "$le");
- bool cmp_equal = cell->type.in("$le", "$ge");
- bool is_signed = cell->getParam("\\A_SIGNED").as_bool();
+ bool cmp_less = cell->type.in(ID($lt), ID($le));
+ bool cmp_equal = cell->type.in(ID($le), ID($ge));
+ bool is_signed = cell->getParam(ID(A_SIGNED)).as_bool();
- RTLIL::SigSpec A = sigmap(cell->getPort("\\A"));
- RTLIL::SigSpec B = sigmap(cell->getPort("\\B"));
- RTLIL::SigSpec Y = sigmap(cell->getPort("\\Y"));
+ RTLIL::SigSpec A = sigmap(cell->getPort(ID::A));
+ RTLIL::SigSpec B = sigmap(cell->getPort(ID::B));
+ RTLIL::SigSpec Y = sigmap(cell->getPort(ID::Y));
if (B < A && GetSize(B)) {
cmp_less = !cmp_less;
@@ -402,7 +413,7 @@ struct AlumaccWorker
alunode_t *n = nullptr;
for (auto node : sig_alu[RTLIL::SigSig(A, B)])
- if (node->is_signed == is_signed && node->invert_b && node->c == RTLIL::S1) {
+ if (node->is_signed == is_signed && node->invert_b && node->c == State::S1) {
n = node;
break;
}
@@ -411,7 +422,7 @@ struct AlumaccWorker
n = new alunode_t;
n->a = A;
n->b = B;
- n->c = RTLIL::S1;
+ n->c = State::S1;
n->y = module->addWire(NEW_ID, max(GetSize(A), GetSize(B)));
n->is_signed = is_signed;
n->invert_b = true;
@@ -427,12 +438,12 @@ struct AlumaccWorker
for (auto cell : eq_cells)
{
- bool cmp_equal = cell->type.in("$eq", "$eqx");
- bool is_signed = cell->getParam("\\A_SIGNED").as_bool();
+ bool cmp_equal = cell->type.in(ID($eq), ID($eqx));
+ bool is_signed = cell->getParam(ID(A_SIGNED)).as_bool();
- RTLIL::SigSpec A = sigmap(cell->getPort("\\A"));
- RTLIL::SigSpec B = sigmap(cell->getPort("\\B"));
- RTLIL::SigSpec Y = sigmap(cell->getPort("\\Y"));
+ RTLIL::SigSpec A = sigmap(cell->getPort(ID::A));
+ RTLIL::SigSpec B = sigmap(cell->getPort(ID::B));
+ RTLIL::SigSpec Y = sigmap(cell->getPort(ID::Y));
if (B < A && GetSize(B))
std::swap(A, B);
@@ -440,7 +451,7 @@ struct AlumaccWorker
alunode_t *n = nullptr;
for (auto node : sig_alu[RTLIL::SigSig(A, B)])
- if (node->is_signed == is_signed && node->invert_b && node->c == RTLIL::S1) {
+ if (node->is_signed == is_signed && node->invert_b && node->c == State::S1) {
n = node;
break;
}
@@ -471,7 +482,7 @@ struct AlumaccWorker
goto delete_node;
}
- n->alu_cell = module->addCell(NEW_ID, "$alu");
+ n->alu_cell = module->addCell(NEW_ID, ID($alu));
alu_counter++;
log(" creating $alu cell for ");
@@ -482,13 +493,13 @@ struct AlumaccWorker
if (n->cells.size() > 0)
n->alu_cell->set_src_attribute(n->cells[0]->get_src_attribute());
- n->alu_cell->setPort("\\A", n->a);
- n->alu_cell->setPort("\\B", n->b);
- n->alu_cell->setPort("\\CI", GetSize(n->c) ? n->c : RTLIL::S0);
- n->alu_cell->setPort("\\BI", n->invert_b ? RTLIL::S1 : RTLIL::S0);
- n->alu_cell->setPort("\\Y", n->y);
- n->alu_cell->setPort("\\X", module->addWire(NEW_ID, GetSize(n->y)));
- n->alu_cell->setPort("\\CO", module->addWire(NEW_ID, GetSize(n->y)));
+ n->alu_cell->setPort(ID::A, n->a);
+ n->alu_cell->setPort(ID::B, n->b);
+ n->alu_cell->setPort(ID(CI), GetSize(n->c) ? n->c : State::S0);
+ n->alu_cell->setPort(ID(BI), n->invert_b ? State::S1 : State::S0);
+ n->alu_cell->setPort(ID::Y, n->y);
+ n->alu_cell->setPort(ID(X), module->addWire(NEW_ID, GetSize(n->y)));
+ n->alu_cell->setPort(ID(CO), module->addWire(NEW_ID, GetSize(n->y)));
n->alu_cell->fixup_parameters(n->is_signed, n->is_signed);
for (auto &it : n->cmp)
@@ -539,7 +550,7 @@ struct AlumaccWorker
struct AlumaccPass : public Pass {
AlumaccPass() : Pass("alumacc", "extract ALU and MACC cells") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -549,7 +560,7 @@ struct AlumaccPass : public Pass {
log("and $macc cells.\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
log_header(design, "Executing ALUMACC pass (create $alu and $macc cells).\n");
diff --git a/passes/techmap/attrmap.cc b/passes/techmap/attrmap.cc
index dec81d216..5f30817d4 100644
--- a/passes/techmap/attrmap.cc
+++ b/passes/techmap/attrmap.cc
@@ -81,7 +81,7 @@ struct AttrmapAction {
struct AttrmapTocase : AttrmapAction {
string name;
- virtual bool apply(IdString &id, Const&) {
+ bool apply(IdString &id, Const&) YS_OVERRIDE {
if (match_name(name, id, true))
id = RTLIL::escape_id(name);
return true;
@@ -90,7 +90,7 @@ struct AttrmapTocase : AttrmapAction {
struct AttrmapRename : AttrmapAction {
string old_name, new_name;
- virtual bool apply(IdString &id, Const&) {
+ bool apply(IdString &id, Const&) YS_OVERRIDE {
if (match_name(old_name, id))
id = RTLIL::escape_id(new_name);
return true;
@@ -101,7 +101,7 @@ struct AttrmapMap : AttrmapAction {
bool imap;
string old_name, new_name;
string old_value, new_value;
- virtual bool apply(IdString &id, Const &val) {
+ bool apply(IdString &id, Const &val) YS_OVERRIDE {
if (match_name(old_name, id) && match_value(old_value, val, true)) {
id = RTLIL::escape_id(new_name);
val = make_value(new_value);
@@ -111,9 +111,10 @@ struct AttrmapMap : AttrmapAction {
};
struct AttrmapRemove : AttrmapAction {
+ bool has_value;
string name, value;
- virtual bool apply(IdString &id, Const &val) {
- return !(match_name(name, id) && match_value(value, val));
+ bool apply(IdString &id, Const &val) YS_OVERRIDE {
+ return !(match_name(name, id) && (!has_value || match_value(value, val)));
}
};
@@ -142,33 +143,94 @@ void attrmap_apply(string objname, vector<std::unique_ptr<AttrmapAction>> &actio
attributes.swap(new_attributes);
}
+void log_attrmap_paramap_options()
+{
+ log(" -tocase <name>\n");
+ log(" Match attribute names case-insensitively and set it to the specified\n");
+ log(" name.\n");
+ log("\n");
+ log(" -rename <old_name> <new_name>\n");
+ log(" Rename attributes as specified\n");
+ log("\n");
+ log(" -map <old_name>=<old_value> <new_name>=<new_value>\n");
+ log(" Map key/value pairs as indicated.\n");
+ log("\n");
+ log(" -imap <old_name>=<old_value> <new_name>=<new_value>\n");
+ log(" Like -map, but use case-insensitive match for <old_value> when\n");
+ log(" it is a string value.\n");
+ log("\n");
+ log(" -remove <name>=<value>\n");
+ log(" Remove attributes matching this pattern.\n");
+}
+
+bool parse_attrmap_paramap_options(size_t &argidx, std::vector<std::string> &args, vector<std::unique_ptr<AttrmapAction>> &actions)
+{
+ std::string arg = args[argidx];
+ if (arg == "-tocase" && argidx+1 < args.size()) {
+ auto action = new AttrmapTocase;
+ action->name = args[++argidx];
+ actions.push_back(std::unique_ptr<AttrmapAction>(action));
+ return true;
+ }
+ if (arg == "-rename" && argidx+2 < args.size()) {
+ auto action = new AttrmapRename;
+ action->old_name = args[++argidx];
+ action->new_name = args[++argidx];
+ actions.push_back(std::unique_ptr<AttrmapAction>(action));
+ return true;
+ }
+ if ((arg == "-map" || arg == "-imap") && argidx+2 < args.size()) {
+ string arg1 = args[++argidx];
+ string arg2 = args[++argidx];
+ string val1, val2;
+ size_t p = arg1.find("=");
+ if (p != string::npos) {
+ val1 = arg1.substr(p+1);
+ arg1 = arg1.substr(0, p);
+ }
+ p = arg2.find("=");
+ if (p != string::npos) {
+ val2 = arg2.substr(p+1);
+ arg2 = arg2.substr(0, p);
+ }
+ auto action = new AttrmapMap;
+ action->imap = (arg == "-map");
+ action->old_name = arg1;
+ action->new_name = arg2;
+ action->old_value = val1;
+ action->new_value = val2;
+ actions.push_back(std::unique_ptr<AttrmapAction>(action));
+ return true;
+ }
+ if (arg == "-remove" && argidx+1 < args.size()) {
+ string arg1 = args[++argidx], val1;
+ size_t p = arg1.find("=");
+ if (p != string::npos) {
+ val1 = arg1.substr(p+1);
+ arg1 = arg1.substr(0, p);
+ }
+ auto action = new AttrmapRemove;
+ action->name = arg1;
+ action->has_value = (p != string::npos);
+ action->value = val1;
+ actions.push_back(std::unique_ptr<AttrmapAction>(action));
+ return true;
+ }
+ return false;
+}
+
struct AttrmapPass : public Pass {
AttrmapPass() : Pass("attrmap", "renaming attributes") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
log(" attrmap [options] [selection]\n");
log("\n");
- log("This command renames attributes and/or mapps key/value pairs to\n");
+ log("This command renames attributes and/or maps key/value pairs to\n");
log("other key/value pairs.\n");
log("\n");
- log(" -tocase <name>\n");
- log(" Match attribute names case-insensitively and set it to the specified\n");
- log(" name.\n");
- log("\n");
- log(" -rename <old_name> <new_name>\n");
- log(" Rename attributes as specified\n");
- log("\n");
- log(" -map <old_name>=<old_value> <new_name>=<new_value>\n");
- log(" Map key/value pairs as indicated.\n");
- log("\n");
- log(" -imap <old_name>=<old_value> <new_name>=<new_value>\n");
- log(" Like -map, but use case-insensitive match for <old_value> when\n");
- log(" it is a string value.\n");
- log("\n");
- log(" -remove <name>=<value>\n");
- log(" Remove attributes matching this pattern.\n");
+ log_attrmap_paramap_options();
log("\n");
log(" -modattr\n");
log(" Operate on module attributes instead of attributes on wires and cells.\n");
@@ -179,7 +241,7 @@ struct AttrmapPass : public Pass {
log(" -imap keep=\"false\" keep=0 -remove keep=0\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
log_header(design, "Executing ATTRMAP pass (move or copy attributes).\n");
@@ -189,57 +251,9 @@ struct AttrmapPass : public Pass {
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++)
{
- std::string arg = args[argidx];
- if (arg == "-tocase" && argidx+1 < args.size()) {
- auto action = new AttrmapTocase;
- action->name = args[++argidx];
- actions.push_back(std::unique_ptr<AttrmapAction>(action));
- continue;
- }
- if (arg == "-rename" && argidx+2 < args.size()) {
- auto action = new AttrmapRename;
- action->old_name = args[++argidx];
- action->new_name = args[++argidx];
- actions.push_back(std::unique_ptr<AttrmapAction>(action));
- continue;
- }
- if ((arg == "-map" || arg == "-imap") && argidx+2 < args.size()) {
- string arg1 = args[++argidx];
- string arg2 = args[++argidx];
- string val1, val2;
- size_t p = arg1.find("=");
- if (p != string::npos) {
- val1 = arg1.substr(p+1);
- arg1 = arg1.substr(0, p);
- }
- p = arg2.find("=");
- if (p != string::npos) {
- val2 = arg2.substr(p+1);
- arg2 = arg2.substr(0, p);
- }
- auto action = new AttrmapMap;
- action->imap = (arg == "-map");
- action->old_name = arg1;
- action->new_name = arg2;
- action->old_value = val1;
- action->new_value = val2;
- actions.push_back(std::unique_ptr<AttrmapAction>(action));
- continue;
- }
- if (arg == "-remove" && argidx+1 < args.size()) {
- string arg1 = args[++argidx], val1;
- size_t p = arg1.find("=");
- if (p != string::npos) {
- val1 = arg1.substr(p+1);
- arg1 = arg1.substr(0, p);
- }
- auto action = new AttrmapRemove;
- action->name = arg1;
- action->value = val1;
- actions.push_back(std::unique_ptr<AttrmapAction>(action));
+ if (parse_attrmap_paramap_options(argidx, args, actions))
continue;
- }
- if (arg == "-modattr") {
+ if (args[argidx] == "-modattr") {
modattr_mode = true;
continue;
}
@@ -261,9 +275,67 @@ struct AttrmapPass : public Pass {
for (auto cell : module->selected_cells())
attrmap_apply(stringf("%s.%s", log_id(module), log_id(cell)), actions, cell->attributes);
+
+ for (auto proc : module->processes)
+ {
+ if (!design->selected(module, proc.second))
+ continue;
+ attrmap_apply(stringf("%s.%s", log_id(module), log_id(proc.first)), actions, proc.second->attributes);
+
+ std::vector<RTLIL::CaseRule*> all_cases = {&proc.second->root_case};
+ while (!all_cases.empty()) {
+ RTLIL::CaseRule *cs = all_cases.back();
+ all_cases.pop_back();
+ attrmap_apply(stringf("%s.%s (case)", log_id(module), log_id(proc.first)), actions, cs->attributes);
+
+ for (auto &sw : cs->switches) {
+ attrmap_apply(stringf("%s.%s (switch)", log_id(module), log_id(proc.first)), actions, sw->attributes);
+ all_cases.insert(all_cases.end(), sw->cases.begin(), sw->cases.end());
+ }
+ }
+ }
}
}
}
} AttrmapPass;
+struct ParamapPass : public Pass {
+ ParamapPass() : Pass("paramap", "renaming cell parameters") { }
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" paramap [options] [selection]\n");
+ log("\n");
+ log("This command renames cell parameters and/or maps key/value pairs to\n");
+ log("other key/value pairs.\n");
+ log("\n");
+ log_attrmap_paramap_options();
+ log("\n");
+ log("For example, mapping Diamond-style ECP5 \"init\" attributes to Yosys-style:\n");
+ log("\n");
+ log(" paramap -tocase INIT t:LUT4\n");
+ log("\n");
+ }
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ log_header(design, "Executing PARAMAP pass (move or copy cell parameters).\n");
+
+ vector<std::unique_ptr<AttrmapAction>> actions;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ if (parse_attrmap_paramap_options(argidx, args, actions))
+ continue;
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto module : design->selected_modules())
+ for (auto cell : module->selected_cells())
+ attrmap_apply(stringf("%s.%s", log_id(module), log_id(cell)), actions, cell->parameters);
+ }
+} ParamapPass;
+
PRIVATE_NAMESPACE_END
diff --git a/passes/techmap/attrmvcp.cc b/passes/techmap/attrmvcp.cc
index 1537def00..e59aa6518 100644
--- a/passes/techmap/attrmvcp.cc
+++ b/passes/techmap/attrmvcp.cc
@@ -25,7 +25,7 @@ PRIVATE_NAMESPACE_BEGIN
struct AttrmvcpPass : public Pass {
AttrmvcpPass() : Pass("attrmvcp", "move or copy attributes from wires to driving cells") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
log("\n");
log(" attrmvcp [options] [selection]\n");
@@ -53,7 +53,7 @@ struct AttrmvcpPass : public Pass {
log(" multiple times.\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
log_header(design, "Executing ATTRMVCP pass (move or copy attributes).\n");
diff --git a/passes/techmap/clkbufmap.cc b/passes/techmap/clkbufmap.cc
new file mode 100644
index 000000000..b9cd68883
--- /dev/null
+++ b/passes/techmap/clkbufmap.cc
@@ -0,0 +1,339 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2019 Marcin Kościelnicki <mwk@0x04.net>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "kernel/yosys.h"
+#include "kernel/sigtools.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+void split_portname_pair(std::string &port1, std::string &port2)
+{
+ size_t pos = port1.find_first_of(':');
+ if (pos != std::string::npos) {
+ port2 = port1.substr(pos+1);
+ port1 = port1.substr(0, pos);
+ }
+}
+
+struct ClkbufmapPass : public Pass {
+ ClkbufmapPass() : Pass("clkbufmap", "insert global buffers on clock networks") { }
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" clkbufmap [options] [selection]\n");
+ log("\n");
+ log("Inserts global buffers between nets connected to clock inputs and their drivers.\n");
+ log("\n");
+ log("In the absence of any selection, all wires without the 'clkbuf_inhibit'\n");
+ log("attribute will be considered for global buffer insertion.\n");
+ log("Alternatively, to consider all wires without the 'buffer_type' attribute set to\n");
+ log("'none' or 'bufr' one would specify:\n");
+ log(" 'w:* a:buffer_type=none a:buffer_type=bufr %%u %%d'\n");
+ log("as the selection.\n");
+ log("\n");
+ log(" -buf <celltype> <portname_out>:<portname_in>\n");
+ log(" Specifies the cell type to use for the global buffers\n");
+ log(" and its port names. The first port will be connected to\n");
+ log(" the clock network sinks, and the second will be connected\n");
+ log(" to the actual clock source. This option is required.\n");
+ log("\n");
+ log(" -inpad <celltype> <portname_out>:<portname_in>\n");
+ log(" If specified, a PAD cell of the given type is inserted on\n");
+ log(" clock nets that are also top module's inputs (in addition\n");
+ log(" to the global buffer).\n");
+ log("\n");
+ }
+
+ void module_queue(Design *design, Module *module, std::vector<Module *> &modules_sorted, pool<Module *> &modules_processed) {
+ if (modules_processed.count(module))
+ return;
+ for (auto cell : module->cells()) {
+ Module *submodule = design->module(cell->type);
+ if (!submodule)
+ continue;
+ module_queue(design, submodule, modules_sorted, modules_processed);
+ }
+ modules_sorted.push_back(module);
+ modules_processed.insert(module);
+ }
+
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ log_header(design, "Executing CLKBUFMAP pass (inserting global clock buffers).\n");
+
+ std::string buf_celltype, buf_portname, buf_portname2;
+ std::string inpad_celltype, inpad_portname, inpad_portname2;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ std::string arg = args[argidx];
+ if (arg == "-buf" && argidx+2 < args.size()) {
+ buf_celltype = args[++argidx];
+ buf_portname = args[++argidx];
+ split_portname_pair(buf_portname, buf_portname2);
+ continue;
+ }
+ if (arg == "-inpad" && argidx+2 < args.size()) {
+ inpad_celltype = args[++argidx];
+ inpad_portname = args[++argidx];
+ split_portname_pair(inpad_portname, inpad_portname2);
+ continue;
+ }
+ break;
+ }
+
+ bool select = false;
+ if (argidx < args.size()) {
+ if (args[argidx].compare(0, 1, "-") != 0)
+ select = true;
+ extra_args(args, argidx, design);
+ }
+
+ if (buf_celltype.empty())
+ log_error("The -buf option is required.\n");
+
+ // Cell type, port name, bit index.
+ pool<pair<IdString, pair<IdString, int>>> sink_ports;
+ pool<pair<IdString, pair<IdString, int>>> buf_ports;
+ dict<pair<IdString, pair<IdString, int>>, pair<IdString, int>> inv_ports_out;
+ dict<pair<IdString, pair<IdString, int>>, pair<IdString, int>> inv_ports_in;
+
+ // Process submodules before module using them.
+ std::vector<Module *> modules_sorted;
+ pool<Module *> modules_processed;
+ for (auto module : design->selected_modules())
+ module_queue(design, module, modules_sorted, modules_processed);
+
+ for (auto module : modules_sorted)
+ {
+ if (module->get_blackbox_attribute()) {
+ for (auto port : module->ports) {
+ auto wire = module->wire(port);
+ if (wire->get_bool_attribute("\\clkbuf_driver"))
+ for (int i = 0; i < GetSize(wire); i++)
+ buf_ports.insert(make_pair(module->name, make_pair(wire->name, i)));
+ if (wire->get_bool_attribute("\\clkbuf_sink"))
+ for (int i = 0; i < GetSize(wire); i++)
+ sink_ports.insert(make_pair(module->name, make_pair(wire->name, i)));
+ auto it = wire->attributes.find("\\clkbuf_inv");
+ if (it != wire->attributes.end()) {
+ IdString in_name = RTLIL::escape_id(it->second.decode_string());
+ for (int i = 0; i < GetSize(wire); i++) {
+ inv_ports_out[make_pair(module->name, make_pair(wire->name, i))] = make_pair(in_name, i);
+ inv_ports_in[make_pair(module->name, make_pair(in_name, i))] = make_pair(wire->name, i);
+ }
+ }
+ }
+ continue;
+ }
+ pool<SigBit> sink_wire_bits;
+ pool<SigBit> buf_wire_bits;
+ pool<SigBit> driven_wire_bits;
+ SigMap sigmap(module);
+ // bit -> (buffer, buffer's input)
+ dict<SigBit, pair<Cell *, Wire *>> buffered_bits;
+
+ // First, collect nets that could use a clock buffer.
+ for (auto cell : module->cells())
+ for (auto port : cell->connections())
+ for (int i = 0; i < port.second.size(); i++)
+ if (sink_ports.count(make_pair(cell->type, make_pair(port.first, i))))
+ sink_wire_bits.insert(sigmap(port.second[i]));
+
+ // Second, collect ones that already have a clock buffer.
+ for (auto cell : module->cells())
+ for (auto port : cell->connections())
+ for (int i = 0; i < port.second.size(); i++)
+ if (buf_ports.count(make_pair(cell->type, make_pair(port.first, i))))
+ buf_wire_bits.insert(sigmap(port.second[i]));
+
+ // Third, propagate tags through inverters.
+ bool retry = true;
+ while (retry) {
+ retry = false;
+ for (auto cell : module->cells())
+ for (auto port : cell->connections())
+ for (int i = 0; i < port.second.size(); i++) {
+ auto it = inv_ports_out.find(make_pair(cell->type, make_pair(port.first, i)));
+ auto bit = sigmap(port.second[i]);
+ // If output of an inverter is connected to a sink, mark it as buffered,
+ // and request a buffer on the inverter's input instead.
+ if (it != inv_ports_out.end() && !buf_wire_bits.count(bit) && sink_wire_bits.count(bit)) {
+ buf_wire_bits.insert(bit);
+ auto other_bit = sigmap(cell->getPort(it->second.first)[it->second.second]);
+ sink_wire_bits.insert(other_bit);
+ retry = true;
+ }
+ // If input of an inverter is marked as already-buffered,
+ // mark its output already-buffered as well.
+ auto it2 = inv_ports_in.find(make_pair(cell->type, make_pair(port.first, i)));
+ if (it2 != inv_ports_in.end() && buf_wire_bits.count(bit)) {
+ auto other_bit = sigmap(cell->getPort(it2->second.first)[it2->second.second]);
+ if (!buf_wire_bits.count(other_bit)) {
+ buf_wire_bits.insert(other_bit);
+ retry = true;
+ }
+ }
+
+ }
+ };
+
+ // Collect all driven bits.
+ for (auto cell : module->cells())
+ for (auto port : cell->connections())
+ if (cell->output(port.first))
+ for (int i = 0; i < port.second.size(); i++)
+ driven_wire_bits.insert(port.second[i]);
+
+ // Insert buffers.
+ std::vector<pair<Wire *, Wire *>> input_queue;
+ // Copy current wire list, as we will be adding new ones during iteration.
+ std::vector<Wire *> wires(module->wires());
+ for (auto wire : wires)
+ {
+ // Should not happen.
+ if (wire->port_input && wire->port_output)
+ continue;
+ bool process_wire = module->selected(wire);
+ if (!select && wire->get_bool_attribute("\\clkbuf_inhibit"))
+ process_wire = false;
+ if (!process_wire) {
+ // This wire is supposed to be bypassed, so make sure we don't buffer it in
+ // some buffer higher up in the hierarchy.
+ if (wire->port_output)
+ for (int i = 0; i < GetSize(wire); i++)
+ buf_ports.insert(make_pair(module->name, make_pair(wire->name, i)));
+ continue;
+ }
+
+ pool<int> input_bits;
+
+ for (int i = 0; i < GetSize(wire); i++)
+ {
+ SigBit wire_bit(wire, i);
+ SigBit mapped_wire_bit = sigmap(wire_bit);
+ if (buf_wire_bits.count(mapped_wire_bit)) {
+ // Already buffered downstream. If this is an output, mark it.
+ if (wire->port_output)
+ buf_ports.insert(make_pair(module->name, make_pair(wire->name, i)));
+ } else if (!sink_wire_bits.count(mapped_wire_bit)) {
+ // Nothing to do.
+ } else if (driven_wire_bits.count(wire_bit) || (wire->port_input && module->get_bool_attribute("\\top"))) {
+ // Clock network not yet buffered, driven by one of
+ // our cells or a top-level input -- buffer it.
+
+ log("Inserting %s on %s.%s[%d].\n", buf_celltype.c_str(), log_id(module), log_id(wire), i);
+ RTLIL::Cell *cell = module->addCell(NEW_ID, RTLIL::escape_id(buf_celltype));
+ Wire *iwire = module->addWire(NEW_ID);
+ cell->setPort(RTLIL::escape_id(buf_portname), mapped_wire_bit);
+ cell->setPort(RTLIL::escape_id(buf_portname2), iwire);
+ if (wire->port_input && !inpad_celltype.empty() && module->get_bool_attribute("\\top")) {
+ log("Inserting %s on %s.%s[%d].\n", inpad_celltype.c_str(), log_id(module), log_id(wire), i);
+ RTLIL::Cell *cell2 = module->addCell(NEW_ID, RTLIL::escape_id(inpad_celltype));
+ cell2->setPort(RTLIL::escape_id(inpad_portname), iwire);
+ iwire = module->addWire(NEW_ID);
+ cell2->setPort(RTLIL::escape_id(inpad_portname2), iwire);
+ }
+ buffered_bits[mapped_wire_bit] = make_pair(cell, iwire);
+
+ if (wire->port_input) {
+ input_bits.insert(i);
+ }
+ } else if (wire->port_input) {
+ // A clock input in a submodule -- mark it, let higher level
+ // worry about it.
+ if (wire->port_input)
+ sink_ports.insert(make_pair(module->name, make_pair(wire->name, i)));
+ }
+ }
+ if (!input_bits.empty()) {
+ // This is an input port and some buffers were inserted -- we need
+ // to create a new input wire and transfer attributes.
+ Wire *new_wire = module->addWire(NEW_ID, wire);
+
+ for (int i = 0; i < wire->width; i++) {
+ SigBit wire_bit(wire, i);
+ SigBit mapped_wire_bit = sigmap(wire_bit);
+ auto it = buffered_bits.find(mapped_wire_bit);
+ if (it != buffered_bits.end()) {
+
+ module->connect(it->second.second, SigSpec(new_wire, i));
+ } else {
+ module->connect(SigSpec(wire, i), SigSpec(new_wire, i));
+ }
+ }
+ input_queue.push_back(make_pair(wire, new_wire));
+ }
+ }
+
+ // Mark any newly-buffered output ports as such.
+ for (auto wire : module->selected_wires()) {
+ if (wire->port_input || !wire->port_output)
+ continue;
+ for (int i = 0; i < GetSize(wire); i++)
+ {
+ SigBit wire_bit(wire, i);
+ SigBit mapped_wire_bit = sigmap(wire_bit);
+ if (buffered_bits.count(mapped_wire_bit))
+ buf_ports.insert(make_pair(module->name, make_pair(wire->name, i)));
+ }
+ }
+
+ // Reconnect the drivers to buffer inputs.
+ for (auto cell : module->cells())
+ for (auto port : cell->connections()) {
+ if (!cell->output(port.first))
+ continue;
+ SigSpec sig = port.second;
+ bool newsig = false;
+ for (auto &bit : sig) {
+ const auto it = buffered_bits.find(sigmap(bit));
+ if (it == buffered_bits.end())
+ continue;
+ // Avoid substituting buffer's own output pin.
+ if (cell == it->second.first)
+ continue;
+ bit = it->second.second;
+ newsig = true;
+ }
+ if (newsig)
+ cell->setPort(port.first, sig);
+ }
+
+ // This has to be done last, to avoid upsetting sigmap before the port reconnections.
+ for (auto &it : input_queue) {
+ Wire *wire = it.first;
+ Wire *new_wire = it.second;
+ module->swap_names(new_wire, wire);
+ wire->attributes.clear();
+ wire->port_id = 0;
+ wire->port_input = false;
+ wire->port_output = false;
+ }
+
+ module->fixup_ports();
+ }
+ }
+} ClkbufmapPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/techmap/deminout.cc b/passes/techmap/deminout.cc
index ed4e45762..b976b401b 100644
--- a/passes/techmap/deminout.cc
+++ b/passes/techmap/deminout.cc
@@ -25,7 +25,7 @@ PRIVATE_NAMESPACE_BEGIN
struct DeminoutPass : public Pass {
DeminoutPass() : Pass("deminout", "demote inout ports to input or output") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
log("\n");
log(" deminout [options] [selection]\n");
@@ -33,7 +33,7 @@ struct DeminoutPass : public Pass {
log("\"Demote\" inout ports to input or output ports, if possible.\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
log_header(design, "Executing DEMINOUT pass (demote inout ports to input or output).\n");
@@ -57,7 +57,7 @@ struct DeminoutPass : public Pass {
for (auto module : design->selected_modules())
{
SigMap sigmap(module);
- pool<SigBit> bits_written, bits_used, bits_inout;
+ pool<SigBit> bits_written, bits_used, bits_inout, bits_tribuf;
dict<SigBit, int> bits_numports;
for (auto wire : module->wires())
@@ -82,6 +82,25 @@ struct DeminoutPass : public Pass {
if (cellport_in)
for (auto bit : sigmap(conn.second))
bits_used.insert(bit);
+
+ if (conn.first == ID::Y && cell->type.in(ID($mux), ID($pmux), ID($_MUX_), ID($_TBUF_), ID($tribuf)))
+ {
+ bool tribuf = cell->type.in(ID($_TBUF_), ID($tribuf));
+
+ if (!tribuf) {
+ for (auto &c : cell->connections()) {
+ if (!c.first.in(ID::A, ID::B))
+ continue;
+ for (auto b : sigmap(c.second))
+ if (b == State::Sz)
+ tribuf = true;
+ }
+ }
+
+ if (tribuf)
+ for (auto bit : sigmap(conn.second))
+ bits_tribuf.insert(bit);
+ }
}
for (auto wire : module->selected_wires())
@@ -94,11 +113,17 @@ struct DeminoutPass : public Pass {
{
if (bits_numports[bit] > 1 || bits_inout.count(bit))
new_input = true, new_output = true;
-
- if (bits_written.count(bit))
+ if (bit == State::S0 || bit == State::S1)
+ new_output = true;
+ if (bits_written.count(bit)) {
new_output = true;
- else if (bits_used.count(bit))
- new_input = true;
+ if (bits_tribuf.count(bit))
+ goto tribuf_bit;
+ } else {
+ tribuf_bit:
+ if (bits_used.count(bit))
+ new_input = true;
+ }
}
if (new_input != new_output) {
diff --git a/passes/techmap/dff2dffe.cc b/passes/techmap/dff2dffe.cc
index 1b8920bb7..0242256e5 100644
--- a/passes/techmap/dff2dffe.cc
+++ b/passes/techmap/dff2dffe.cc
@@ -52,13 +52,13 @@ struct Dff2dffeWorker
}
for (auto cell : module->cells()) {
- if (cell->type == "$mux" || cell->type == "$pmux" || cell->type == "$_MUX_") {
- RTLIL::SigSpec sig_y = sigmap(cell->getPort("\\Y"));
+ if (cell->type.in(ID($mux), ID($pmux), ID($_MUX_))) {
+ RTLIL::SigSpec sig_y = sigmap(cell->getPort(ID::Y));
for (int i = 0; i < GetSize(sig_y); i++)
bit2mux[sig_y[i]] = cell_int_t(cell, i);
}
if (direct_dict.empty()) {
- if (cell->type == "$dff" || cell->type == "$_DFF_N_" || cell->type == "$_DFF_P_")
+ if (cell->type.in(ID($dff), ID($_DFF_N_), ID($_DFF_P_)))
dff_cells.push_back(cell);
} else {
if (direct_dict.count(cell->type))
@@ -86,9 +86,9 @@ struct Dff2dffeWorker
return ret;
cell_int_t mux_cell_int = bit2mux.at(d);
- RTLIL::SigSpec sig_a = sigmap(mux_cell_int.first->getPort("\\A"));
- RTLIL::SigSpec sig_b = sigmap(mux_cell_int.first->getPort("\\B"));
- RTLIL::SigSpec sig_s = sigmap(mux_cell_int.first->getPort("\\S"));
+ RTLIL::SigSpec sig_a = sigmap(mux_cell_int.first->getPort(ID::A));
+ RTLIL::SigSpec sig_b = sigmap(mux_cell_int.first->getPort(ID::B));
+ RTLIL::SigSpec sig_s = sigmap(mux_cell_int.first->getPort(ID(S)));
int width = GetSize(sig_a), index = mux_cell_int.second;
for (int i = 0; i < GetSize(sig_s); i++)
@@ -97,9 +97,9 @@ struct Dff2dffeWorker
ret = find_muxtree_feedback_patterns(sig_b[i*width + index], q, path);
if (sig_b[i*width + index] == q) {
- RTLIL::SigSpec s = mux_cell_int.first->getPort("\\B");
+ RTLIL::SigSpec s = mux_cell_int.first->getPort(ID::B);
s[i*width + index] = RTLIL::Sx;
- mux_cell_int.first->setPort("\\B", s);
+ mux_cell_int.first->setPort(ID::B, s);
}
return ret;
@@ -120,9 +120,9 @@ struct Dff2dffeWorker
ret.insert(pat);
if (sig_b[i*width + index] == q) {
- RTLIL::SigSpec s = mux_cell_int.first->getPort("\\B");
+ RTLIL::SigSpec s = mux_cell_int.first->getPort(ID::B);
s[i*width + index] = RTLIL::Sx;
- mux_cell_int.first->setPort("\\B", s);
+ mux_cell_int.first->setPort(ID::B, s);
}
}
@@ -130,9 +130,9 @@ struct Dff2dffeWorker
ret.insert(pat);
if (sig_a[index] == q) {
- RTLIL::SigSpec s = mux_cell_int.first->getPort("\\A");
+ RTLIL::SigSpec s = mux_cell_int.first->getPort(ID::A);
s[index] = RTLIL::Sx;
- mux_cell_int.first->setPort("\\A", s);
+ mux_cell_int.first->setPort(ID::A, s);
}
return ret;
@@ -167,7 +167,7 @@ struct Dff2dffeWorker
}
if (GetSize(or_input) == 0)
- return RTLIL::S1;
+ return State::S1;
if (GetSize(or_input) == 1)
return or_input;
@@ -185,8 +185,8 @@ struct Dff2dffeWorker
void handle_dff_cell(RTLIL::Cell *dff_cell)
{
- RTLIL::SigSpec sig_d = sigmap(dff_cell->getPort("\\D"));
- RTLIL::SigSpec sig_q = sigmap(dff_cell->getPort("\\Q"));
+ RTLIL::SigSpec sig_d = sigmap(dff_cell->getPort(ID(D)));
+ RTLIL::SigSpec sig_q = sigmap(dff_cell->getPort(ID(Q)));
std::map<patterns_t, std::set<int>> grouped_patterns;
std::set<int> remaining_indices;
@@ -208,16 +208,16 @@ struct Dff2dffeWorker
}
if (!direct_dict.empty()) {
log(" converting %s cell %s to %s for %s -> %s.\n", log_id(dff_cell->type), log_id(dff_cell), log_id(direct_dict.at(dff_cell->type)), log_signal(new_sig_d), log_signal(new_sig_q));
- dff_cell->setPort("\\E", make_patterns_logic(it.first, true));
+ dff_cell->setPort(ID(E), make_patterns_logic(it.first, true));
dff_cell->type = direct_dict.at(dff_cell->type);
} else
- if (dff_cell->type == "$dff") {
- RTLIL::Cell *new_cell = module->addDffe(NEW_ID, dff_cell->getPort("\\CLK"), make_patterns_logic(it.first, false),
- new_sig_d, new_sig_q, dff_cell->getParam("\\CLK_POLARITY").as_bool(), true);
+ if (dff_cell->type == ID($dff)) {
+ RTLIL::Cell *new_cell = module->addDffe(NEW_ID, dff_cell->getPort(ID(CLK)), make_patterns_logic(it.first, false),
+ new_sig_d, new_sig_q, dff_cell->getParam(ID(CLK_POLARITY)).as_bool(), true);
log(" created $dffe cell %s for %s -> %s.\n", log_id(new_cell), log_signal(new_sig_d), log_signal(new_sig_q));
} else {
- RTLIL::Cell *new_cell = module->addDffeGate(NEW_ID, dff_cell->getPort("\\C"), make_patterns_logic(it.first, true),
- new_sig_d, new_sig_q, dff_cell->type == "$_DFF_P_", true);
+ RTLIL::Cell *new_cell = module->addDffeGate(NEW_ID, dff_cell->getPort(ID(C)), make_patterns_logic(it.first, true),
+ new_sig_d, new_sig_q, dff_cell->type == ID($_DFF_P_), true);
log(" created %s cell %s for %s -> %s.\n", log_id(new_cell->type), log_id(new_cell), log_signal(new_sig_d), log_signal(new_sig_q));
}
}
@@ -235,9 +235,9 @@ struct Dff2dffeWorker
new_sig_d.append(sig_d[i]);
new_sig_q.append(sig_q[i]);
}
- dff_cell->setPort("\\D", new_sig_d);
- dff_cell->setPort("\\Q", new_sig_q);
- dff_cell->setParam("\\WIDTH", GetSize(remaining_indices));
+ dff_cell->setPort(ID(D), new_sig_d);
+ dff_cell->setPort(ID(Q), new_sig_q);
+ dff_cell->setParam(ID(WIDTH), GetSize(remaining_indices));
}
}
@@ -253,7 +253,7 @@ struct Dff2dffeWorker
struct Dff2dffePass : public Pass {
Dff2dffePass() : Pass("dff2dffe", "transform $dff cells to $dffe cells") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -265,7 +265,11 @@ struct Dff2dffePass : public Pass {
log("\n");
log(" -unmap\n");
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(" of $dff and $mux cells. the options below are ignored 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");
@@ -279,15 +283,17 @@ struct Dff2dffePass : public Pass {
log(" -direct-match <pattern>\n");
log(" like -direct for all DFF cell types matching the expression.\n");
log(" this will use $__DFFE_* as <external_gate_type> matching the\n");
- log(" internal gate type $_DFF_*_, except for $_DFF_[NP]_, which is\n");
- log(" converted to $_DFFE_[NP]_.\n");
+ log(" internal gate type $_DFF_*_, and $__DFFSE_* for those matching\n");
+ log(" $_DFFS_*_, except for $_DFF_[NP]_, which is converted to \n");
+ log(" $_DFFE_[NP]_.\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
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;
@@ -296,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 = atoi(args[++argidx].c_str());
+ 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]);
@@ -305,16 +316,25 @@ struct Dff2dffePass : public Pass {
if (args[argidx] == "-direct-match" && argidx + 1 < args.size()) {
bool found_match = false;
const char *pattern = args[++argidx].c_str();
- if (patmatch(pattern, "$_DFF_P_" )) found_match = true, direct_dict["$_DFF_P_" ] = "$_DFFE_PP_";
- if (patmatch(pattern, "$_DFF_N_" )) found_match = true, direct_dict["$_DFF_N_" ] = "$_DFFE_NP_";
- if (patmatch(pattern, "$_DFF_NN0_")) found_match = true, direct_dict["$_DFF_NN0_"] = "$__DFFE_NN0";
- if (patmatch(pattern, "$_DFF_NN1_")) found_match = true, direct_dict["$_DFF_NN1_"] = "$__DFFE_NN1";
- if (patmatch(pattern, "$_DFF_NP0_")) found_match = true, direct_dict["$_DFF_NP0_"] = "$__DFFE_NP0";
- if (patmatch(pattern, "$_DFF_NP1_")) found_match = true, direct_dict["$_DFF_NP1_"] = "$__DFFE_NP1";
- if (patmatch(pattern, "$_DFF_PN0_")) found_match = true, direct_dict["$_DFF_PN0_"] = "$__DFFE_PN0";
- if (patmatch(pattern, "$_DFF_PN1_")) found_match = true, direct_dict["$_DFF_PN1_"] = "$__DFFE_PN1";
- if (patmatch(pattern, "$_DFF_PP0_")) found_match = true, direct_dict["$_DFF_PP0_"] = "$__DFFE_PP0";
- if (patmatch(pattern, "$_DFF_PP1_")) found_match = true, direct_dict["$_DFF_PP1_"] = "$__DFFE_PP1";
+ if (patmatch(pattern, "$_DFF_P_" )) found_match = true, direct_dict[ID($_DFF_P_) ] = ID($_DFFE_PP_);
+ if (patmatch(pattern, "$_DFF_N_" )) found_match = true, direct_dict[ID($_DFF_N_) ] = ID($_DFFE_NP_);
+ if (patmatch(pattern, "$_DFF_NN0_")) found_match = true, direct_dict[ID($_DFF_NN0_)] = ID($__DFFE_NN0);
+ if (patmatch(pattern, "$_DFF_NN1_")) found_match = true, direct_dict[ID($_DFF_NN1_)] = ID($__DFFE_NN1);
+ if (patmatch(pattern, "$_DFF_NP0_")) found_match = true, direct_dict[ID($_DFF_NP0_)] = ID($__DFFE_NP0);
+ if (patmatch(pattern, "$_DFF_NP1_")) found_match = true, direct_dict[ID($_DFF_NP1_)] = ID($__DFFE_NP1);
+ if (patmatch(pattern, "$_DFF_PN0_")) found_match = true, direct_dict[ID($_DFF_PN0_)] = ID($__DFFE_PN0);
+ if (patmatch(pattern, "$_DFF_PN1_")) found_match = true, direct_dict[ID($_DFF_PN1_)] = ID($__DFFE_PN1);
+ if (patmatch(pattern, "$_DFF_PP0_")) found_match = true, direct_dict[ID($_DFF_PP0_)] = ID($__DFFE_PP0);
+ if (patmatch(pattern, "$_DFF_PP1_")) found_match = true, direct_dict[ID($_DFF_PP1_)] = ID($__DFFE_PP1);
+
+ if (patmatch(pattern, "$__DFFS_NN0_")) found_match = true, direct_dict[ID($__DFFS_NN0_)] = ID($__DFFSE_NN0);
+ if (patmatch(pattern, "$__DFFS_NN1_")) found_match = true, direct_dict[ID($__DFFS_NN1_)] = ID($__DFFSE_NN1);
+ if (patmatch(pattern, "$__DFFS_NP0_")) found_match = true, direct_dict[ID($__DFFS_NP0_)] = ID($__DFFSE_NP0);
+ if (patmatch(pattern, "$__DFFS_NP1_")) found_match = true, direct_dict[ID($__DFFS_NP1_)] = ID($__DFFSE_NP1);
+ if (patmatch(pattern, "$__DFFS_PN0_")) found_match = true, direct_dict[ID($__DFFS_PN0_)] = ID($__DFFSE_PN0);
+ if (patmatch(pattern, "$__DFFS_PN1_")) found_match = true, direct_dict[ID($__DFFS_PN1_)] = ID($__DFFSE_PN1);
+ if (patmatch(pattern, "$__DFFS_PP0_")) found_match = true, direct_dict[ID($__DFFS_PP0_)] = ID($__DFFSE_PP0);
+ if (patmatch(pattern, "$__DFFS_PP1_")) found_match = true, direct_dict[ID($__DFFS_PP1_)] = ID($__DFFSE_PP1);
if (!found_match)
log_cmd_error("No cell types matched pattern '%s'.\n", pattern);
continue;
@@ -333,26 +353,51 @@ 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") {
- 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())
- mod->addMux(NEW_ID, cell->getPort("\\Q"), cell->getPort("\\D"), cell->getPort("\\EN"), tmp);
+ if (cell->type == ID($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(ID(EN))) == sigmap(cell_other->getPort(ID(EN))))
+ ce_use++;
+ }
+ if (ce_use >= min_ce_use)
+ continue;
+ }
+
+ RTLIL::SigSpec tmp = mod->addWire(NEW_ID, GetSize(cell->getPort(ID(D))));
+ mod->addDff(NEW_ID, cell->getPort(ID(CLK)), tmp, cell->getPort(ID(Q)), cell->getParam(ID(CLK_POLARITY)).as_bool());
+ if (cell->getParam(ID(EN_POLARITY)).as_bool())
+ mod->addMux(NEW_ID, cell->getPort(ID(Q)), cell->getPort(ID(D)), cell->getPort(ID(EN)), tmp);
else
- mod->addMux(NEW_ID, cell->getPort("\\D"), cell->getPort("\\Q"), cell->getPort("\\EN"), tmp);
+ mod->addMux(NEW_ID, cell->getPort(ID(D)), cell->getPort(ID(Q)), cell->getPort(ID(EN)), tmp);
mod->remove(cell);
continue;
}
- if (cell->type.substr(0, 7) == "$_DFFE_") {
- bool clk_pol = cell->type.substr(7, 1) == "P";
- bool en_pol = cell->type.substr(8, 1) == "P";
+ if (cell->type.begins_with("$_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(ID(E))) == sigmap(cell_other->getPort(ID(E))))
+ ce_use++;
+ }
+ if (ce_use >= min_ce_use)
+ continue;
+ }
+
+ bool clk_pol = cell->type.compare(7, 1, "P") == 0;
+ bool en_pol = cell->type.compare(8, 1, "P") == 0;
RTLIL::SigSpec tmp = mod->addWire(NEW_ID);
- mod->addDff(NEW_ID, cell->getPort("\\C"), tmp, cell->getPort("\\Q"), clk_pol);
+ mod->addDff(NEW_ID, cell->getPort(ID(C)), tmp, cell->getPort(ID(Q)), clk_pol);
if (en_pol)
- mod->addMux(NEW_ID, cell->getPort("\\Q"), cell->getPort("\\D"), cell->getPort("\\E"), tmp);
+ mod->addMux(NEW_ID, cell->getPort(ID(Q)), cell->getPort(ID(D)), cell->getPort(ID(E)), tmp);
else
- mod->addMux(NEW_ID, cell->getPort("\\D"), cell->getPort("\\Q"), cell->getPort("\\E"), tmp);
+ mod->addMux(NEW_ID, cell->getPort(ID(D)), cell->getPort(ID(Q)), cell->getPort(ID(E)), tmp);
mod->remove(cell);
continue;
}
diff --git a/passes/techmap/dff2dffs.cc b/passes/techmap/dff2dffs.cc
new file mode 100644
index 000000000..3fa1ed5cf
--- /dev/null
+++ b/passes/techmap/dff2dffs.cc
@@ -0,0 +1,165 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2018 David Shah <dave@ds0.me>
+ *
+ * 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 Dff2dffsPass : public Pass {
+ Dff2dffsPass() : Pass("dff2dffs", "process sync set/reset with SR over CE priority") { }
+ void help() YS_OVERRIDE
+ {
+ log("\n");
+ log(" dff2dffs [options] [selection]\n");
+ log("\n");
+ log("Merge synchronous set/reset $_MUX_ cells to create $__DFFS_[NP][NP][01], to be run before\n");
+ log("dff2dffe for SR over CE priority.\n");
+ log("\n");
+ log(" -match-init\n");
+ log(" Disallow merging synchronous set/reset that has polarity opposite of the\n");
+ log(" output wire's init attribute (if any).\n");
+ log("\n");
+ }
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ log_header(design, "Executing dff2dffs pass (merge synchronous set/reset into FF cells).\n");
+
+ bool match_init = false;
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ // if (args[argidx] == "-singleton") {
+ // singleton_mode = true;
+ // continue;
+ // }
+ if (args[argidx] == "-match-init") {
+ match_init = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ pool<IdString> dff_types;
+ dff_types.insert(ID($_DFF_N_));
+ dff_types.insert(ID($_DFF_P_));
+
+ for (auto module : design->selected_modules())
+ {
+ log("Merging set/reset $_MUX_ cells into DFFs in %s.\n", log_id(module));
+
+ SigMap sigmap(module);
+ dict<SigBit, Cell*> sr_muxes;
+ vector<Cell*> ff_cells;
+
+ for (auto cell : module->selected_cells())
+ {
+ if (dff_types.count(cell->type)) {
+ ff_cells.push_back(cell);
+ continue;
+ }
+
+ if (cell->type != ID($_MUX_))
+ continue;
+
+ SigBit bit_a = sigmap(cell->getPort(ID::A));
+ SigBit bit_b = sigmap(cell->getPort(ID::B));
+
+ if (bit_a.wire == nullptr || bit_b.wire == nullptr)
+ sr_muxes[sigmap(cell->getPort(ID::Y))] = cell;
+ }
+
+ for (auto cell : ff_cells)
+ {
+ SigSpec sig_d = cell->getPort(ID(D));
+
+ if (GetSize(sig_d) < 1)
+ continue;
+
+ SigBit bit_d = sigmap(sig_d[0]);
+
+ if (sr_muxes.count(bit_d) == 0)
+ continue;
+
+ Cell *mux_cell = sr_muxes.at(bit_d);
+ SigBit bit_a = sigmap(mux_cell->getPort(ID::A));
+ SigBit bit_b = sigmap(mux_cell->getPort(ID::B));
+ SigBit bit_s = sigmap(mux_cell->getPort(ID(S)));
+
+ SigBit sr_val, sr_sig;
+ bool invert_sr;
+ sr_sig = bit_s;
+ if (bit_a.wire == nullptr) {
+ bit_d = bit_b;
+ sr_val = bit_a;
+ invert_sr = true;
+ } else {
+ log_assert(bit_b.wire == nullptr);
+ bit_d = bit_a;
+ sr_val = bit_b;
+ invert_sr = false;
+ }
+
+ if (match_init) {
+ SigBit bit_q = cell->getPort(ID(Q));
+ if (bit_q.wire) {
+ auto it = bit_q.wire->attributes.find(ID(init));
+ if (it != bit_q.wire->attributes.end()) {
+ auto init_val = it->second[bit_q.offset];
+ if (init_val == State::S1 && sr_val != State::S1)
+ continue;
+ if (init_val == State::S0 && sr_val != State::S0)
+ continue;
+ }
+ }
+ }
+
+ log(" Merging %s (A=%s, B=%s, S=%s) into %s (%s).\n", log_id(mux_cell),
+ log_signal(bit_a), log_signal(bit_b), log_signal(bit_s), log_id(cell), log_id(cell->type));
+
+ if (sr_val == State::S1) {
+ if (cell->type == ID($_DFF_N_)) {
+ if (invert_sr) cell->type = ID($__DFFS_NN1_);
+ else cell->type = ID($__DFFS_NP1_);
+ } else {
+ log_assert(cell->type == ID($_DFF_P_));
+ if (invert_sr) cell->type = ID($__DFFS_PN1_);
+ else cell->type = ID($__DFFS_PP1_);
+ }
+ } else {
+ if (cell->type == ID($_DFF_N_)) {
+ if (invert_sr) cell->type = ID($__DFFS_NN0_);
+ else cell->type = ID($__DFFS_NP0_);
+ } else {
+ log_assert(cell->type == ID($_DFF_P_));
+ if (invert_sr) cell->type = ID($__DFFS_PN0_);
+ else cell->type = ID($__DFFS_PP0_);
+ }
+ }
+ cell->setPort(ID(R), sr_sig);
+ cell->setPort(ID(D), bit_d);
+ }
+ }
+ }
+} Dff2dffsPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/techmap/dffinit.cc b/passes/techmap/dffinit.cc
index d737b3424..cf9301442 100644
--- a/passes/techmap/dffinit.cc
+++ b/passes/techmap/dffinit.cc
@@ -25,7 +25,7 @@ PRIVATE_NAMESPACE_BEGIN
struct DffinitPass : public Pass {
DffinitPass() : Pass("dffinit", "set INIT param on FF cells") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -38,15 +38,44 @@ struct DffinitPass : public Pass {
log(" operate on the specified cell type. this option can be used\n");
log(" multiple times.\n");
log("\n");
+ log(" -highlow\n");
+ log(" use the string values \"high\" and \"low\" 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(" -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");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ 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, 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()) {
IdString cell_name = RTLIL::escape_id(args[++argidx]);
IdString output_port = RTLIL::escape_id(args[++argidx]);
@@ -54,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);
@@ -66,10 +99,11 @@ struct DffinitPass : public Pass {
pool<SigBit> used_bits;
for (auto wire : module->selected_wires()) {
- if (wire->attributes.count("\\init")) {
- Const value = wire->attributes.at("\\init");
+ if (wire->attributes.count(ID(init))) {
+ Const value = wire->attributes.at(ID(init));
for (int i = 0; i < min(GetSize(value), GetSize(wire)); i++)
- init_bits[sigmap(SigBit(wire, i))] = value[i];
+ if (value[i] != State::Sx)
+ init_bits[sigmap(SigBit(wire, i))] = value[i];
}
if (wire->port_output)
for (auto bit : sigmap(wire))
@@ -102,10 +136,24 @@ 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]);
}
+ if (highlow_mode && GetSize(value) != 0) {
+ if (GetSize(value) != 1)
+ 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_string);
+ else
+ 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),
log_id(it.first), log_signal(sig), log_signal(value));
cell->setParam(it.second, value);
@@ -113,8 +161,8 @@ struct DffinitPass : public Pass {
}
for (auto wire : module->selected_wires())
- if (wire->attributes.count("\\init")) {
- Const &value = wire->attributes.at("\\init");
+ if (wire->attributes.count(ID(init))) {
+ Const &value = wire->attributes.at(ID(init));
bool do_cleanup = true;
for (int i = 0; i < min(GetSize(value), GetSize(wire)); i++) {
SigBit bit = sigmap(SigBit(wire, i));
@@ -125,7 +173,7 @@ struct DffinitPass : public Pass {
}
if (do_cleanup) {
log("Removing init attribute from wire %s.%s.\n", log_id(module), log_id(wire));
- wire->attributes.erase("\\init");
+ wire->attributes.erase(ID(init));
}
}
}
diff --git a/passes/techmap/dfflibmap.cc b/passes/techmap/dfflibmap.cc
index 4cb1489a8..b15109cd3 100644
--- a/passes/techmap/dfflibmap.cc
+++ b/passes/techmap/dfflibmap.cc
@@ -27,12 +27,12 @@ USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
struct cell_mapping {
- std::string cell_name;
+ IdString cell_name;
std::map<std::string, char> ports;
};
static std::map<RTLIL::IdString, cell_mapping> cell_mappings;
-static void logmap(std::string dff)
+static void logmap(IdString dff)
{
if (cell_mappings.count(dff) == 0) {
log(" unmapped dff cell: %s\n", dff.c_str());
@@ -54,26 +54,26 @@ static void logmap(std::string dff)
static void logmap_all()
{
- logmap("$_DFF_N_");
- logmap("$_DFF_P_");
-
- logmap("$_DFF_NN0_");
- logmap("$_DFF_NN1_");
- logmap("$_DFF_NP0_");
- logmap("$_DFF_NP1_");
- logmap("$_DFF_PN0_");
- logmap("$_DFF_PN1_");
- logmap("$_DFF_PP0_");
- logmap("$_DFF_PP1_");
-
- logmap("$_DFFSR_NNN_");
- logmap("$_DFFSR_NNP_");
- logmap("$_DFFSR_NPN_");
- logmap("$_DFFSR_NPP_");
- logmap("$_DFFSR_PNN_");
- logmap("$_DFFSR_PNP_");
- logmap("$_DFFSR_PPN_");
- logmap("$_DFFSR_PPP_");
+ logmap(ID($_DFF_N_));
+ logmap(ID($_DFF_P_));
+
+ logmap(ID($_DFF_NN0_));
+ logmap(ID($_DFF_NN1_));
+ logmap(ID($_DFF_NP0_));
+ logmap(ID($_DFF_NP1_));
+ logmap(ID($_DFF_PN0_));
+ logmap(ID($_DFF_PN1_));
+ logmap(ID($_DFF_PP0_));
+ logmap(ID($_DFF_PP1_));
+
+ logmap(ID($_DFFSR_NNN_));
+ logmap(ID($_DFFSR_NNP_));
+ logmap(ID($_DFFSR_NPN_));
+ logmap(ID($_DFFSR_NPP_));
+ logmap(ID($_DFFSR_PNN_));
+ logmap(ID($_DFFSR_PNP_));
+ logmap(ID($_DFFSR_PPN_));
+ logmap(ID($_DFFSR_PPP_));
}
static bool parse_pin(LibertyAst *cell, LibertyAst *attr, std::string &pin_name, bool &pin_pol)
@@ -100,10 +100,22 @@ 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;
}
-static void find_cell(LibertyAst *ast, std::string cell_type, bool clkpol, bool has_reset, bool rstpol, bool rstval, bool prepare_mode)
+static void find_cell(LibertyAst *ast, IdString cell_type, bool clkpol, bool has_reset, bool rstpol, bool rstval, bool prepare_mode)
{
LibertyAst *best_cell = NULL;
std::map<std::string, char> best_cell_ports;
@@ -218,13 +230,13 @@ static void find_cell(LibertyAst *ast, std::string cell_type, bool clkpol, bool
cell_mappings[cell_type].ports["D"] = 'D';
cell_mappings[cell_type].ports["Q"] = 'Q';
} else {
- cell_mappings[cell_type].cell_name = best_cell->args[0];
+ cell_mappings[cell_type].cell_name = RTLIL::escape_id(best_cell->args[0]);
cell_mappings[cell_type].ports = best_cell_ports;
}
}
}
-static void find_cell_sr(LibertyAst *ast, std::string cell_type, bool clkpol, bool setpol, bool clrpol, bool prepare_mode)
+static void find_cell_sr(LibertyAst *ast, IdString cell_type, bool clkpol, bool setpol, bool clrpol, bool prepare_mode)
{
LibertyAst *best_cell = NULL;
std::map<std::string, char> best_cell_ports;
@@ -240,6 +252,10 @@ static void find_cell_sr(LibertyAst *ast, std::string cell_type, bool clkpol, bo
if (cell->id != "cell" || cell->args.size() != 1)
continue;
+ LibertyAst *dn = cell->find("dont_use");
+ if (dn != NULL && dn->value == "true")
+ continue;
+
LibertyAst *ff = cell->find("ff");
if (ff == NULL)
continue;
@@ -331,7 +347,7 @@ static void find_cell_sr(LibertyAst *ast, std::string cell_type, bool clkpol, bo
cell_mappings[cell_type].ports["D"] = 'D';
cell_mappings[cell_type].ports["Q"] = 'Q';
} else {
- cell_mappings[cell_type].cell_name = best_cell->args[0];
+ cell_mappings[cell_type].cell_name = RTLIL::escape_id(best_cell->args[0]);
cell_mappings[cell_type].ports = best_cell_ports;
}
}
@@ -388,7 +404,7 @@ static bool expand_cellmap(std::string pattern, std::string inv)
return return_status;
}
-static void map_sr_to_arst(const char *from, const char *to)
+static void map_sr_to_arst(IdString from, IdString to)
{
if (!cell_mappings.count(from) || cell_mappings.count(to) > 0)
return;
@@ -403,7 +419,7 @@ static void map_sr_to_arst(const char *from, const char *to)
log_assert(from_clk_pol == to_clk_pol);
log_assert(to_rst_pol == from_set_pol && to_rst_pol == from_clr_pol);
- log(" create mapping for %s from mapping for %s.\n", to, from);
+ log(" create mapping for %s from mapping for %s.\n", to.c_str(), from.c_str());
cell_mappings[to].cell_name = cell_mappings[from].cell_name;
cell_mappings[to].ports = cell_mappings[from].ports;
@@ -434,7 +450,7 @@ static void map_sr_to_arst(const char *from, const char *to)
}
}
-static void map_adff_to_dff(const char *from, const char *to)
+static void map_adff_to_dff(IdString from, IdString to)
{
if (!cell_mappings.count(from) || cell_mappings.count(to) > 0)
return;
@@ -445,7 +461,7 @@ static void map_adff_to_dff(const char *from, const char *to)
log_assert(from_clk_pol == to_clk_pol);
- log(" create mapping for %s from mapping for %s.\n", to, from);
+ log(" create mapping for %s from mapping for %s.\n", to.c_str(), from.c_str());
cell_mappings[to].cell_name = cell_mappings[from].cell_name;
cell_mappings[to].ports = cell_mappings[from].ports;
@@ -468,8 +484,8 @@ static void dfflibmap(RTLIL::Design *design, RTLIL::Module *module, bool prepare
for (auto &it : module->cells_) {
if (design->selected(module, it.second) && cell_mappings.count(it.second->type) > 0)
cell_list.push_back(it.second);
- if (it.second->type == "$_NOT_")
- notmap[sigmap(it.second->getPort("\\A"))].insert(it.second);
+ if (it.second->type == ID($_NOT_))
+ notmap[sigmap(it.second->getPort(ID::A))].insert(it.second);
}
std::map<std::string, int> stats;
@@ -483,7 +499,7 @@ static void dfflibmap(RTLIL::Design *design, RTLIL::Module *module, bool prepare
module->remove(cell);
cell_mapping &cm = cell_mappings[cell_type];
- RTLIL::Cell *new_cell = module->addCell(cell_name, prepare_mode ? cm.cell_name : "\\" + cm.cell_name);
+ RTLIL::Cell *new_cell = module->addCell(cell_name, prepare_mode ? cm.cell_name : cm.cell_name);
new_cell->set_src_attribute(src);
@@ -503,8 +519,8 @@ static void dfflibmap(RTLIL::Design *design, RTLIL::Module *module, bool prepare
sig = module->addWire(NEW_ID, GetSize(old_sig));
if (has_q && has_qn) {
for (auto &it : notmap[sigmap(old_sig)]) {
- module->connect(it->getPort("\\Y"), sig);
- it->setPort("\\Y", module->addWire(NEW_ID, GetSize(old_sig)));
+ module->connect(it->getPort(ID::Y), sig);
+ it->setPort(ID::Y, module->addWire(NEW_ID, GetSize(old_sig)));
}
} else {
module->addNotGate(NEW_ID, sig, old_sig);
@@ -533,7 +549,7 @@ static void dfflibmap(RTLIL::Design *design, RTLIL::Module *module, bool prepare
struct DfflibmapPass : public Pass {
DfflibmapPass() : Pass("dfflibmap", "technology mapping of flip-flops") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
log("\n");
log(" dfflibmap [-prepare] -liberty <file> [selection]\n");
@@ -549,7 +565,7 @@ struct DfflibmapPass : public Pass {
log("liberty file.\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
log_header(design, "Executing DFFLIBMAP pass (mapping DFF cells to sequential cells from liberty file).\n");
@@ -583,26 +599,26 @@ struct DfflibmapPass : public Pass {
LibertyParser libparser(f);
f.close();
- find_cell(libparser.ast, "$_DFF_N_", false, false, false, false, prepare_mode);
- find_cell(libparser.ast, "$_DFF_P_", true, false, false, false, prepare_mode);
-
- find_cell(libparser.ast, "$_DFF_NN0_", false, true, false, false, prepare_mode);
- find_cell(libparser.ast, "$_DFF_NN1_", false, true, false, true, prepare_mode);
- find_cell(libparser.ast, "$_DFF_NP0_", false, true, true, false, prepare_mode);
- find_cell(libparser.ast, "$_DFF_NP1_", false, true, true, true, prepare_mode);
- find_cell(libparser.ast, "$_DFF_PN0_", true, true, false, false, prepare_mode);
- find_cell(libparser.ast, "$_DFF_PN1_", true, true, false, true, prepare_mode);
- find_cell(libparser.ast, "$_DFF_PP0_", true, true, true, false, prepare_mode);
- find_cell(libparser.ast, "$_DFF_PP1_", true, true, true, true, prepare_mode);
-
- find_cell_sr(libparser.ast, "$_DFFSR_NNN_", false, false, false, prepare_mode);
- find_cell_sr(libparser.ast, "$_DFFSR_NNP_", false, false, true, prepare_mode);
- find_cell_sr(libparser.ast, "$_DFFSR_NPN_", false, true, false, prepare_mode);
- find_cell_sr(libparser.ast, "$_DFFSR_NPP_", false, true, true, prepare_mode);
- find_cell_sr(libparser.ast, "$_DFFSR_PNN_", true, false, false, prepare_mode);
- find_cell_sr(libparser.ast, "$_DFFSR_PNP_", true, false, true, prepare_mode);
- find_cell_sr(libparser.ast, "$_DFFSR_PPN_", true, true, false, prepare_mode);
- find_cell_sr(libparser.ast, "$_DFFSR_PPP_", true, true, true, prepare_mode);
+ find_cell(libparser.ast, ID($_DFF_N_), false, false, false, false, prepare_mode);
+ find_cell(libparser.ast, ID($_DFF_P_), true, false, false, false, prepare_mode);
+
+ find_cell(libparser.ast, ID($_DFF_NN0_), false, true, false, false, prepare_mode);
+ find_cell(libparser.ast, ID($_DFF_NN1_), false, true, false, true, prepare_mode);
+ find_cell(libparser.ast, ID($_DFF_NP0_), false, true, true, false, prepare_mode);
+ find_cell(libparser.ast, ID($_DFF_NP1_), false, true, true, true, prepare_mode);
+ find_cell(libparser.ast, ID($_DFF_PN0_), true, true, false, false, prepare_mode);
+ find_cell(libparser.ast, ID($_DFF_PN1_), true, true, false, true, prepare_mode);
+ find_cell(libparser.ast, ID($_DFF_PP0_), true, true, true, false, prepare_mode);
+ find_cell(libparser.ast, ID($_DFF_PP1_), true, true, true, true, prepare_mode);
+
+ find_cell_sr(libparser.ast, ID($_DFFSR_NNN_), false, false, false, prepare_mode);
+ find_cell_sr(libparser.ast, ID($_DFFSR_NNP_), false, false, true, prepare_mode);
+ find_cell_sr(libparser.ast, ID($_DFFSR_NPN_), false, true, false, prepare_mode);
+ find_cell_sr(libparser.ast, ID($_DFFSR_NPP_), false, true, true, prepare_mode);
+ find_cell_sr(libparser.ast, ID($_DFFSR_PNN_), true, false, false, prepare_mode);
+ find_cell_sr(libparser.ast, ID($_DFFSR_PNP_), true, false, true, prepare_mode);
+ find_cell_sr(libparser.ast, ID($_DFFSR_PPN_), true, true, false, prepare_mode);
+ find_cell_sr(libparser.ast, ID($_DFFSR_PPP_), true, true, true, prepare_mode);
// try to implement as many cells as possible just by inverting
// the SET and RESET pins. If necessary, implement cell types
@@ -626,29 +642,29 @@ struct DfflibmapPass : public Pass {
break;
}
- map_sr_to_arst("$_DFFSR_NNN_", "$_DFF_NN0_");
- map_sr_to_arst("$_DFFSR_NNN_", "$_DFF_NN1_");
- map_sr_to_arst("$_DFFSR_NPP_", "$_DFF_NP0_");
- map_sr_to_arst("$_DFFSR_NPP_", "$_DFF_NP1_");
- map_sr_to_arst("$_DFFSR_PNN_", "$_DFF_PN0_");
- map_sr_to_arst("$_DFFSR_PNN_", "$_DFF_PN1_");
- map_sr_to_arst("$_DFFSR_PPP_", "$_DFF_PP0_");
- map_sr_to_arst("$_DFFSR_PPP_", "$_DFF_PP1_");
-
- map_adff_to_dff("$_DFF_NN0_", "$_DFF_N_");
- map_adff_to_dff("$_DFF_NN1_", "$_DFF_N_");
- map_adff_to_dff("$_DFF_NP0_", "$_DFF_N_");
- map_adff_to_dff("$_DFF_NP1_", "$_DFF_N_");
- map_adff_to_dff("$_DFF_PN0_", "$_DFF_P_");
- map_adff_to_dff("$_DFF_PN1_", "$_DFF_P_");
- map_adff_to_dff("$_DFF_PP0_", "$_DFF_P_");
- map_adff_to_dff("$_DFF_PP1_", "$_DFF_P_");
-
- log(" final dff cell mappings:\n");
- logmap_all();
+ map_sr_to_arst(ID($_DFFSR_NNN_), ID($_DFF_NN0_));
+ map_sr_to_arst(ID($_DFFSR_NNN_), ID($_DFF_NN1_));
+ map_sr_to_arst(ID($_DFFSR_NPP_), ID($_DFF_NP0_));
+ map_sr_to_arst(ID($_DFFSR_NPP_), ID($_DFF_NP1_));
+ map_sr_to_arst(ID($_DFFSR_PNN_), ID($_DFF_PN0_));
+ map_sr_to_arst(ID($_DFFSR_PNN_), ID($_DFF_PN1_));
+ map_sr_to_arst(ID($_DFFSR_PPP_), ID($_DFF_PP0_));
+ map_sr_to_arst(ID($_DFFSR_PPP_), ID($_DFF_PP1_));
+
+ map_adff_to_dff(ID($_DFF_NN0_), ID($_DFF_N_));
+ map_adff_to_dff(ID($_DFF_NN1_), ID($_DFF_N_));
+ map_adff_to_dff(ID($_DFF_NP0_), ID($_DFF_N_));
+ map_adff_to_dff(ID($_DFF_NP1_), ID($_DFF_N_));
+ map_adff_to_dff(ID($_DFF_PN0_), ID($_DFF_P_));
+ map_adff_to_dff(ID($_DFF_PN1_), ID($_DFF_P_));
+ map_adff_to_dff(ID($_DFF_PP0_), ID($_DFF_P_));
+ map_adff_to_dff(ID($_DFF_PP1_), ID($_DFF_P_));
+
+ log(" final dff cell mappings:\n");
+ logmap_all();
for (auto &it : design->modules_)
- if (design->selected(it.second) && !it.second->get_bool_attribute("\\blackbox"))
+ if (design->selected(it.second) && !it.second->get_blackbox_attribute())
dfflibmap(design, it.second, prepare_mode);
cell_mappings.clear();
diff --git a/passes/techmap/dffsr2dff.cc b/passes/techmap/dffsr2dff.cc
index 0d4d53627..61b06fdc1 100644
--- a/passes/techmap/dffsr2dff.cc
+++ b/passes/techmap/dffsr2dff.cc
@@ -25,17 +25,17 @@ PRIVATE_NAMESPACE_BEGIN
void dffsr_worker(SigMap &sigmap, Module *module, Cell *cell)
{
- if (cell->type == "$dffsr")
+ if (cell->type == ID($dffsr))
{
- int width = cell->getParam("\\WIDTH").as_int();
- bool setpol = cell->getParam("\\SET_POLARITY").as_bool();
- bool clrpol = cell->getParam("\\CLR_POLARITY").as_bool();
+ int width = cell->getParam(ID(WIDTH)).as_int();
+ bool setpol = cell->getParam(ID(SET_POLARITY)).as_bool();
+ bool clrpol = cell->getParam(ID(CLR_POLARITY)).as_bool();
SigBit setunused = setpol ? State::S0 : State::S1;
SigBit clrunused = clrpol ? State::S0 : State::S1;
- SigSpec setsig = sigmap(cell->getPort("\\SET"));
- SigSpec clrsig = sigmap(cell->getPort("\\CLR"));
+ SigSpec setsig = sigmap(cell->getPort(ID(SET)));
+ SigSpec clrsig = sigmap(cell->getPort(ID(CLR)));
Const reset_val;
SigSpec setctrl, clrctrl;
@@ -78,32 +78,32 @@ void dffsr_worker(SigMap &sigmap, Module *module, Cell *cell)
log("Converting %s cell %s.%s to $adff.\n", log_id(cell->type), log_id(module), log_id(cell));
if (GetSize(setctrl) == 1) {
- cell->setPort("\\ARST", setctrl);
- cell->setParam("\\ARST_POLARITY", setpol);
+ cell->setPort(ID(ARST), setctrl);
+ cell->setParam(ID(ARST_POLARITY), setpol);
} else {
- cell->setPort("\\ARST", clrctrl);
- cell->setParam("\\ARST_POLARITY", clrpol);
+ cell->setPort(ID(ARST), clrctrl);
+ cell->setParam(ID(ARST_POLARITY), clrpol);
}
- cell->type = "$adff";
- cell->unsetPort("\\SET");
- cell->unsetPort("\\CLR");
- cell->setParam("\\ARST_VALUE", reset_val);
- cell->unsetParam("\\SET_POLARITY");
- cell->unsetParam("\\CLR_POLARITY");
+ cell->type = ID($adff);
+ cell->unsetPort(ID(SET));
+ cell->unsetPort(ID(CLR));
+ cell->setParam(ID(ARST_VALUE), reset_val);
+ cell->unsetParam(ID(SET_POLARITY));
+ cell->unsetParam(ID(CLR_POLARITY));
return;
}
- if (cell->type.in("$_DFFSR_NNN_", "$_DFFSR_NNP_", "$_DFFSR_NPN_", "$_DFFSR_NPP_",
- "$_DFFSR_PNN_", "$_DFFSR_PNP_", "$_DFFSR_PPN_", "$_DFFSR_PPP_"))
+ if (cell->type.in(ID($_DFFSR_NNN_), ID($_DFFSR_NNP_), ID($_DFFSR_NPN_), ID($_DFFSR_NPP_),
+ ID($_DFFSR_PNN_), ID($_DFFSR_PNP_), ID($_DFFSR_PPN_), ID($_DFFSR_PPP_)))
{
char clkpol = cell->type.c_str()[8];
char setpol = cell->type.c_str()[9];
char clrpol = cell->type.c_str()[10];
- SigBit setbit = sigmap(cell->getPort("\\S"));
- SigBit clrbit = sigmap(cell->getPort("\\R"));
+ SigBit setbit = sigmap(cell->getPort(ID(S)));
+ SigBit clrbit = sigmap(cell->getPort(ID(R)));
SigBit setunused = setpol == 'P' ? State::S0 : State::S1;
SigBit clrunused = clrpol == 'P' ? State::S0 : State::S1;
@@ -112,14 +112,14 @@ void dffsr_worker(SigMap &sigmap, Module *module, Cell *cell)
if (setbit == setunused) {
cell->type = stringf("$_DFF_%c%c0_", clkpol, clrpol);
- cell->unsetPort("\\S");
+ cell->unsetPort(ID(S));
goto converted_gate;
}
if (clrbit == clrunused) {
cell->type = stringf("$_DFF_%c%c1_", clkpol, setpol);
- cell->setPort("\\R", cell->getPort("\\S"));
- cell->unsetPort("\\S");
+ cell->setPort(ID(R), cell->getPort(ID(S)));
+ cell->unsetPort(ID(S));
goto converted_gate;
}
@@ -133,32 +133,32 @@ void dffsr_worker(SigMap &sigmap, Module *module, Cell *cell)
void adff_worker(SigMap &sigmap, Module *module, Cell *cell)
{
- if (cell->type == "$adff")
+ if (cell->type == ID($adff))
{
- bool rstpol = cell->getParam("\\ARST_POLARITY").as_bool();
+ bool rstpol = cell->getParam(ID(ARST_POLARITY)).as_bool();
SigBit rstunused = rstpol ? State::S0 : State::S1;
- SigSpec rstsig = sigmap(cell->getPort("\\ARST"));
+ SigSpec rstsig = sigmap(cell->getPort(ID(ARST)));
if (rstsig != rstunused)
return;
log("Converting %s cell %s.%s to $dff.\n", log_id(cell->type), log_id(module), log_id(cell));
- cell->type = "$dff";
- cell->unsetPort("\\ARST");
- cell->unsetParam("\\ARST_VALUE");
- cell->unsetParam("\\ARST_POLARITY");
+ cell->type = ID($dff);
+ cell->unsetPort(ID(ARST));
+ cell->unsetParam(ID(ARST_VALUE));
+ cell->unsetParam(ID(ARST_POLARITY));
return;
}
- if (cell->type.in("$_DFF_NN0_", "$_DFF_NN1_", "$_DFF_NP0_", "$_DFF_NP1_",
- "$_DFF_PN0_", "$_DFF_PN1_", "$_DFF_PP0_", "$_DFF_PP1_"))
+ if (cell->type.in(ID($_DFF_NN0_), ID($_DFF_NN1_), ID($_DFF_NP0_), ID($_DFF_NP1_),
+ ID($_DFF_PN0_), ID($_DFF_PN1_), ID($_DFF_PP0_), ID($_DFF_PP1_)))
{
char clkpol = cell->type.c_str()[6];
char rstpol = cell->type.c_str()[7];
- SigBit rstbit = sigmap(cell->getPort("\\R"));
+ SigBit rstbit = sigmap(cell->getPort(ID(R)));
SigBit rstunused = rstpol == 'P' ? State::S0 : State::S1;
if (rstbit != rstunused)
@@ -168,7 +168,7 @@ void adff_worker(SigMap &sigmap, Module *module, Cell *cell)
log("Converting %s cell %s.%s to %s.\n", log_id(cell->type), log_id(module), log_id(cell), log_id(newtype));
cell->type = newtype;
- cell->unsetPort("\\R");
+ cell->unsetPort(ID(R));
return;
}
@@ -176,7 +176,7 @@ void adff_worker(SigMap &sigmap, Module *module, Cell *cell)
struct Dffsr2dffPass : public Pass {
Dffsr2dffPass() : Pass("dffsr2dff", "convert DFFSR cells to simpler FF cell types") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -186,7 +186,7 @@ struct Dffsr2dffPass : public Pass {
log("$_DFF_???_) to simpler FF cell types when any of the set/reset inputs is unused.\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
log_header(design, "Executing DFFSR2DFF pass (mapping DFFSR cells to simpler FFs).\n");
diff --git a/passes/techmap/extract.cc b/passes/techmap/extract.cc
index 71e29c60b..f8798eea5 100644
--- a/passes/techmap/extract.cc
+++ b/passes/techmap/extract.cc
@@ -54,40 +54,40 @@ public:
RTLIL::Const unified_param(RTLIL::IdString cell_type, RTLIL::IdString param, RTLIL::Const value)
{
- if (cell_type.substr(0, 1) != "$" || cell_type.substr(0, 2) == "$_")
+ if (!cell_type.begins_with("$") || cell_type.begins_with("$_"))
return value;
#define param_bool(_n) if (param == _n) return value.as_bool();
- param_bool("\\ARST_POLARITY");
- param_bool("\\A_SIGNED");
- param_bool("\\B_SIGNED");
- param_bool("\\CLK_ENABLE");
- param_bool("\\CLK_POLARITY");
- param_bool("\\CLR_POLARITY");
- param_bool("\\EN_POLARITY");
- param_bool("\\SET_POLARITY");
- param_bool("\\TRANSPARENT");
+ param_bool(ID(ARST_POLARITY));
+ param_bool(ID(A_SIGNED));
+ param_bool(ID(B_SIGNED));
+ param_bool(ID(CLK_ENABLE));
+ param_bool(ID(CLK_POLARITY));
+ param_bool(ID(CLR_POLARITY));
+ param_bool(ID(EN_POLARITY));
+ param_bool(ID(SET_POLARITY));
+ param_bool(ID(TRANSPARENT));
#undef param_bool
#define param_int(_n) if (param == _n) return value.as_int();
- param_int("\\ABITS")
- param_int("\\A_WIDTH")
- param_int("\\B_WIDTH")
- param_int("\\CTRL_IN_WIDTH")
- param_int("\\CTRL_OUT_WIDTH")
- param_int("\\OFFSET")
- param_int("\\PRIORITY")
- param_int("\\RD_PORTS")
- param_int("\\SIZE")
- param_int("\\STATE_BITS")
- param_int("\\STATE_NUM")
- param_int("\\STATE_NUM_LOG2")
- param_int("\\STATE_RST")
- param_int("\\S_WIDTH")
- param_int("\\TRANS_NUM")
- param_int("\\WIDTH")
- param_int("\\WR_PORTS")
- param_int("\\Y_WIDTH")
+ param_int(ID(ABITS))
+ param_int(ID(A_WIDTH))
+ param_int(ID(B_WIDTH))
+ param_int(ID(CTRL_IN_WIDTH))
+ param_int(ID(CTRL_OUT_WIDTH))
+ param_int(ID(OFFSET))
+ param_int(ID(PRIORITY))
+ param_int(ID(RD_PORTS))
+ param_int(ID(SIZE))
+ param_int(ID(STATE_BITS))
+ param_int(ID(STATE_NUM))
+ param_int(ID(STATE_NUM_LOG2))
+ param_int(ID(STATE_RST))
+ param_int(ID(S_WIDTH))
+ param_int(ID(TRANS_NUM))
+ param_int(ID(WIDTH))
+ param_int(ID(WR_PORTS))
+ param_int(ID(Y_WIDTH))
#undef param_int
return value;
@@ -203,7 +203,7 @@ bool module2graph(SubCircuit::Graph &graph, RTLIL::Module *mod, bool constports,
continue;
std::string type = cell->type.str();
- if (sel == NULL && type.substr(0, 2) == "\\$")
+ if (sel == NULL && type.compare(0, 2, "\\$") == 0)
type = type.substr(1);
graph.createNode(cell->name.str(), type, (void*)cell);
@@ -341,10 +341,10 @@ RTLIL::Cell *replace(RTLIL::Module *needle, RTLIL::Module *haystack, SubCircuit:
bool compareSortNeedleList(RTLIL::Module *left, RTLIL::Module *right)
{
int left_idx = 0, right_idx = 0;
- if (left->attributes.count("\\extract_order") > 0)
- left_idx = left->attributes.at("\\extract_order").as_int();
- if (right->attributes.count("\\extract_order") > 0)
- right_idx = right->attributes.at("\\extract_order").as_int();
+ if (left->attributes.count(ID(extract_order)) > 0)
+ left_idx = left->attributes.at(ID(extract_order)).as_int();
+ if (right->attributes.count(ID(extract_order)) > 0)
+ right_idx = right->attributes.at(ID(extract_order)).as_int();
if (left_idx != right_idx)
return left_idx < right_idx;
return left->name < right->name;
@@ -352,7 +352,7 @@ bool compareSortNeedleList(RTLIL::Module *left, RTLIL::Module *right)
struct ExtractPass : public Pass {
ExtractPass() : Pass("extract", "find subcircuits and replace them with cells") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -440,7 +440,7 @@ struct ExtractPass : public Pass {
log("See 'help techmap' for a pass that does the opposite thing.\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
log_header(design, "Executing EXTRACT pass (map subcircuits to cells).\n");
log_push();
@@ -594,7 +594,7 @@ struct ExtractPass : public Pass {
map = new RTLIL::Design;
for (auto &filename : map_filenames)
{
- if (filename.substr(0, 1) == "%")
+ if (filename.compare(0, 1, "%") == 0)
{
if (!saved_designs.count(filename.substr(1))) {
delete map;
@@ -613,10 +613,10 @@ struct ExtractPass : public Pass {
delete map;
log_cmd_error("Can't open map file `%s'.\n", filename.c_str());
}
- Frontend::frontend_call(map, &f, filename, (filename.size() > 3 && filename.substr(filename.size()-3) == ".il") ? "ilang" : "verilog");
+ Frontend::frontend_call(map, &f, filename, (filename.size() > 3 && filename.compare(filename.size()-3, std::string::npos, ".il") == 0 ? "ilang" : "verilog"));
f.close();
- if (filename.size() <= 3 || filename.substr(filename.size()-3) != ".il") {
+ if (filename.size() <= 3 || filename.compare(filename.size()-3, std::string::npos, ".il") != 0) {
Pass::call(map, "proc");
Pass::call(map, "opt_clean");
}
diff --git a/passes/techmap/extract_counter.cc b/passes/techmap/extract_counter.cc
index af0eb852a..17a99493d 100644
--- a/passes/techmap/extract_counter.cc
+++ b/passes/techmap/extract_counter.cc
@@ -120,71 +120,71 @@ int counter_tryextract(
//A counter with less than 2 bits makes no sense
//TODO: configurable min threshold
- int a_width = cell->getParam("\\A_WIDTH").as_int();
+ int a_width = cell->getParam(ID(A_WIDTH)).as_int();
extract.width = a_width;
if( (a_width < 2) || (a_width > maxwidth) )
return 1;
//Second input must be a single bit
- int b_width = cell->getParam("\\B_WIDTH").as_int();
+ int b_width = cell->getParam(ID(B_WIDTH)).as_int();
if(b_width != 1)
return 2;
//Both inputs must be unsigned, so don't extract anything with a signed input
- bool a_sign = cell->getParam("\\A_SIGNED").as_bool();
- bool b_sign = cell->getParam("\\B_SIGNED").as_bool();
+ bool a_sign = cell->getParam(ID(A_SIGNED)).as_bool();
+ bool b_sign = cell->getParam(ID(B_SIGNED)).as_bool();
if(a_sign || b_sign)
return 3;
//To be a counter, one input of the ALU must be a constant 1
//TODO: can A or B be swapped in synthesized RTL or is B always the 1?
- const RTLIL::SigSpec b_port = sigmap(cell->getPort("\\B"));
+ const RTLIL::SigSpec b_port = sigmap(cell->getPort(ID::B));
if(!b_port.is_fully_const() || (b_port.as_int() != 1) )
return 4;
//BI and CI must be constant 1 as well
- const RTLIL::SigSpec bi_port = sigmap(cell->getPort("\\BI"));
+ const RTLIL::SigSpec bi_port = sigmap(cell->getPort(ID(BI)));
if(!bi_port.is_fully_const() || (bi_port.as_int() != 1) )
return 5;
- const RTLIL::SigSpec ci_port = sigmap(cell->getPort("\\CI"));
+ const RTLIL::SigSpec ci_port = sigmap(cell->getPort(ID(CI)));
if(!ci_port.is_fully_const() || (ci_port.as_int() != 1) )
return 6;
//CO and X must be unconnected (exactly one connection to each port)
- if(!is_unconnected(sigmap(cell->getPort("\\CO")), index))
+ if(!is_unconnected(sigmap(cell->getPort(ID(CO))), index))
return 7;
- if(!is_unconnected(sigmap(cell->getPort("\\X")), index))
+ if(!is_unconnected(sigmap(cell->getPort(ID(X))), index))
return 8;
//Y must have exactly one connection, and it has to be a $mux cell.
//We must have a direct bus connection from our Y to their A.
- const RTLIL::SigSpec aluy = sigmap(cell->getPort("\\Y"));
+ const RTLIL::SigSpec aluy = sigmap(cell->getPort(ID::Y));
pool<Cell*> y_loads = get_other_cells(aluy, index, cell);
if(y_loads.size() != 1)
return 9;
Cell* count_mux = *y_loads.begin();
extract.count_mux = count_mux;
- if(count_mux->type != "$mux")
+ if(count_mux->type != ID($mux))
return 10;
- if(!is_full_bus(aluy, index, cell, "\\Y", count_mux, "\\A"))
+ if(!is_full_bus(aluy, index, cell, ID::Y, count_mux, ID::A))
return 11;
//B connection of the mux is our underflow value
- const RTLIL::SigSpec underflow = sigmap(count_mux->getPort("\\B"));
+ const RTLIL::SigSpec underflow = sigmap(count_mux->getPort(ID::B));
if(!underflow.is_fully_const())
return 12;
extract.count_value = underflow.as_int();
//S connection of the mux must come from an inverter (need not be the only load)
- const RTLIL::SigSpec muxsel = sigmap(count_mux->getPort("\\S"));
+ const RTLIL::SigSpec muxsel = sigmap(count_mux->getPort(ID(S)));
extract.outsig = muxsel;
pool<Cell*> muxsel_conns = get_other_cells(muxsel, index, count_mux);
Cell* underflow_inv = NULL;
for(auto c : muxsel_conns)
{
- if(c->type != "$logic_not")
+ if(c->type != ID($logic_not))
continue;
- if(!is_full_bus(muxsel, index, c, "\\Y", count_mux, "\\S", true))
+ if(!is_full_bus(muxsel, index, c, ID::Y, count_mux, ID(S), true))
continue;
underflow_inv = c;
@@ -196,7 +196,7 @@ int counter_tryextract(
//Y connection of the mux must have exactly one load, the counter's internal register, if there's no clock enable
//If we have a clock enable, Y drives the B input of a mux. A of that mux must come from our register
- const RTLIL::SigSpec muxy = sigmap(count_mux->getPort("\\Y"));
+ const RTLIL::SigSpec muxy = sigmap(count_mux->getPort(ID::Y));
pool<Cell*> muxy_loads = get_other_cells(muxy, index, count_mux);
if(muxy_loads.size() != 1)
return 14;
@@ -204,12 +204,12 @@ int counter_tryextract(
Cell* count_reg = muxload;
Cell* cemux = NULL;
RTLIL::SigSpec cey;
- if(muxload->type == "$mux")
+ if(muxload->type == ID($mux))
{
//This mux is probably a clock enable mux.
//Find our count register (should be our only load)
cemux = muxload;
- cey = sigmap(cemux->getPort("\\Y"));
+ cey = sigmap(cemux->getPort(ID::Y));
pool<Cell*> cey_loads = get_other_cells(cey, index, cemux);
if(cey_loads.size() != 1)
return 24;
@@ -217,32 +217,32 @@ int counter_tryextract(
//Mux should have A driven by count Q, and B by muxy
//TODO: if A and B are swapped, CE polarity is inverted
- if(sigmap(cemux->getPort("\\B")) != muxy)
+ if(sigmap(cemux->getPort(ID::B)) != muxy)
return 24;
- if(sigmap(cemux->getPort("\\A")) != sigmap(count_reg->getPort("\\Q")))
+ if(sigmap(cemux->getPort(ID::A)) != sigmap(count_reg->getPort(ID(Q))))
return 24;
- if(sigmap(cemux->getPort("\\Y")) != sigmap(count_reg->getPort("\\D")))
+ if(sigmap(cemux->getPort(ID::Y)) != sigmap(count_reg->getPort(ID(D))))
return 24;
//Select of the mux is our clock enable
extract.has_ce = true;
- extract.ce = sigmap(cemux->getPort("\\S"));
+ extract.ce = sigmap(cemux->getPort(ID(S)));
}
else
extract.has_ce = false;
extract.count_reg = count_reg;
- if(count_reg->type == "$dff")
+ if(count_reg->type == ID($dff))
extract.has_reset = false;
- else if(count_reg->type == "$adff")
+ else if(count_reg->type == ID($adff))
{
extract.has_reset = true;
//Check polarity of reset - we may have to add an inverter later on!
- extract.rst_inverted = (count_reg->getParam("\\ARST_POLARITY").as_int() != 1);
+ extract.rst_inverted = (count_reg->getParam(ID(ARST_POLARITY)).as_int() != 1);
//Verify ARST_VALUE is zero or full scale
- int rst_value = count_reg->getParam("\\ARST_VALUE").as_int();
+ int rst_value = count_reg->getParam(ID(ARST_VALUE)).as_int();
if(rst_value == 0)
extract.rst_to_max = false;
else if(rst_value == extract.count_value)
@@ -251,7 +251,7 @@ int counter_tryextract(
return 23;
//Save the reset
- extract.rst = sigmap(count_reg->getPort("\\ARST"));
+ extract.rst = sigmap(count_reg->getPort(ID(ARST)));
}
//TODO: support synchronous reset
else
@@ -260,12 +260,12 @@ int counter_tryextract(
//Sanity check that we use the ALU output properly
if(extract.has_ce)
{
- if(!is_full_bus(muxy, index, count_mux, "\\Y", cemux, "\\B"))
+ if(!is_full_bus(muxy, index, count_mux, ID::Y, cemux, ID::B))
return 16;
- if(!is_full_bus(cey, index, cemux, "\\Y", count_reg, "\\D"))
+ if(!is_full_bus(cey, index, cemux, ID::Y, count_reg, ID(D)))
return 16;
}
- else if(!is_full_bus(muxy, index, count_mux, "\\Y", count_reg, "\\D"))
+ else if(!is_full_bus(muxy, index, count_mux, ID::Y, count_reg, ID(D)))
return 16;
//TODO: Verify count_reg CLK_POLARITY is 1
@@ -273,7 +273,7 @@ int counter_tryextract(
//Register output must have exactly two loads, the inverter and ALU
//(unless we have a parallel output!)
//If we have a clock enable, 3 is OK
- const RTLIL::SigSpec qport = count_reg->getPort("\\Q");
+ const RTLIL::SigSpec qport = count_reg->getPort(ID(Q));
const RTLIL::SigSpec cnout = sigmap(qport);
pool<Cell*> cnout_loads = get_other_cells(cnout, index, count_reg);
unsigned int max_loads = 2;
@@ -312,19 +312,19 @@ int counter_tryextract(
}
}
}
- if(!is_full_bus(cnout, index, count_reg, "\\Q", underflow_inv, "\\A", true))
+ if(!is_full_bus(cnout, index, count_reg, ID(Q), underflow_inv, ID::A, true))
return 18;
- if(!is_full_bus(cnout, index, count_reg, "\\Q", cell, "\\A", true))
+ if(!is_full_bus(cnout, index, count_reg, ID(Q), cell, ID::A, true))
return 19;
//Look up the clock from the register
- extract.clk = sigmap(count_reg->getPort("\\CLK"));
+ extract.clk = sigmap(count_reg->getPort(ID(CLK)));
//Register output net must have an INIT attribute equal to the count value
extract.rwire = cnout.as_wire();
- if(extract.rwire->attributes.find("\\init") == extract.rwire->attributes.end())
+ if(extract.rwire->attributes.find(ID(init)) == extract.rwire->attributes.end())
return 20;
- int rinit = extract.rwire->attributes["\\init"].as_int();
+ int rinit = extract.rwire->attributes[ID(init)].as_int();
if(rinit != extract.count_value)
return 21;
@@ -343,21 +343,21 @@ void counter_worker(
SigMap& sigmap = index.sigmap;
//Core of the counter must be an ALU
- if (cell->type != "$alu")
+ if (cell->type != ID($alu))
return;
//A input is the count value. Check if it has COUNT_EXTRACT set.
//If it's not a wire, don't even try
- auto port = sigmap(cell->getPort("\\A"));
+ auto port = sigmap(cell->getPort(ID::A));
if(!port.is_wire())
return;
RTLIL::Wire* a_wire = port.as_wire();
bool force_extract = false;
bool never_extract = false;
- string count_reg_src = a_wire->attributes["\\src"].decode_string().c_str();
- if(a_wire->attributes.find("\\COUNT_EXTRACT") != a_wire->attributes.end())
+ string count_reg_src = a_wire->attributes[ID(src)].decode_string().c_str();
+ if(a_wire->attributes.find(ID(COUNT_EXTRACT)) != a_wire->attributes.end())
{
- pool<string> sa = a_wire->get_strpool_attribute("\\COUNT_EXTRACT");
+ pool<string> sa = a_wire->get_strpool_attribute(ID(COUNT_EXTRACT));
string extract_value;
if(sa.size() >= 1)
{
@@ -434,66 +434,66 @@ void counter_worker(
string countname = string("$COUNTx$") + log_id(extract.rwire->name.str());
//Wipe all of the old connections to the ALU
- cell->unsetPort("\\A");
- cell->unsetPort("\\B");
- cell->unsetPort("\\BI");
- cell->unsetPort("\\CI");
- cell->unsetPort("\\CO");
- cell->unsetPort("\\X");
- cell->unsetPort("\\Y");
- cell->unsetParam("\\A_SIGNED");
- cell->unsetParam("\\A_WIDTH");
- cell->unsetParam("\\B_SIGNED");
- cell->unsetParam("\\B_WIDTH");
- cell->unsetParam("\\Y_WIDTH");
+ cell->unsetPort(ID::A);
+ cell->unsetPort(ID::B);
+ cell->unsetPort(ID(BI));
+ cell->unsetPort(ID(CI));
+ cell->unsetPort(ID(CO));
+ cell->unsetPort(ID(X));
+ cell->unsetPort(ID::Y);
+ cell->unsetParam(ID(A_SIGNED));
+ cell->unsetParam(ID(A_WIDTH));
+ cell->unsetParam(ID(B_SIGNED));
+ cell->unsetParam(ID(B_WIDTH));
+ cell->unsetParam(ID(Y_WIDTH));
//Change the cell type
- cell->type = "$__COUNT_";
+ cell->type = ID($__COUNT_);
//Hook up resets
if(extract.has_reset)
{
//TODO: support other kinds of reset
- cell->setParam("\\RESET_MODE", RTLIL::Const("LEVEL"));
+ cell->setParam(ID(RESET_MODE), RTLIL::Const("LEVEL"));
//If the reset is active low, infer an inverter ($__COUNT_ cells always have active high reset)
if(extract.rst_inverted)
{
auto realreset = cell->module->addWire(NEW_ID);
cell->module->addNot(NEW_ID, extract.rst, RTLIL::SigSpec(realreset));
- cell->setPort("\\RST", realreset);
+ cell->setPort(ID(RST), realreset);
}
else
- cell->setPort("\\RST", extract.rst);
+ cell->setPort(ID(RST), extract.rst);
}
else
{
- cell->setParam("\\RESET_MODE", RTLIL::Const("RISING"));
- cell->setPort("\\RST", RTLIL::SigSpec(false));
+ cell->setParam(ID(RESET_MODE), RTLIL::Const("RISING"));
+ cell->setPort(ID(RST), RTLIL::SigSpec(false));
}
//Hook up other stuff
- //cell->setParam("\\CLKIN_DIVIDE", RTLIL::Const(1));
- cell->setParam("\\COUNT_TO", RTLIL::Const(extract.count_value));
- cell->setParam("\\WIDTH", RTLIL::Const(extract.width));
- cell->setPort("\\CLK", extract.clk);
- cell->setPort("\\OUT", extract.outsig);
+ //cell->setParam(ID(CLKIN_DIVIDE), RTLIL::Const(1));
+ cell->setParam(ID(COUNT_TO), RTLIL::Const(extract.count_value));
+ cell->setParam(ID(WIDTH), RTLIL::Const(extract.width));
+ cell->setPort(ID(CLK), extract.clk);
+ cell->setPort(ID(OUT), extract.outsig);
//Hook up clock enable
if(extract.has_ce)
{
- cell->setParam("\\HAS_CE", RTLIL::Const(1));
- cell->setPort("\\CE", extract.ce);
+ cell->setParam(ID(HAS_CE), RTLIL::Const(1));
+ cell->setPort(ID(CE), extract.ce);
}
else
- cell->setParam("\\HAS_CE", RTLIL::Const(0));
+ cell->setParam(ID(HAS_CE), RTLIL::Const(0));
//Hook up hard-wired ports (for now up/down are not supported), default to no parallel output
- cell->setParam("\\HAS_POUT", RTLIL::Const(0));
- cell->setParam("\\RESET_TO_MAX", RTLIL::Const(0));
- cell->setParam("\\DIRECTION", RTLIL::Const("DOWN"));
- cell->setPort("\\CE", RTLIL::Const(1));
- cell->setPort("\\UP", RTLIL::Const(0));
+ cell->setParam(ID(HAS_POUT), RTLIL::Const(0));
+ cell->setParam(ID(RESET_TO_MAX), RTLIL::Const(0));
+ cell->setParam(ID(DIRECTION), RTLIL::Const("DOWN"));
+ cell->setPort(ID(CE), RTLIL::Const(1));
+ cell->setPort(ID(UP), RTLIL::Const(0));
//Hook up any parallel outputs
for(auto load : extract.pouts)
@@ -505,8 +505,8 @@ void counter_worker(
//Connect it to our parallel output
//(this is OK to do more than once b/c they all go to the same place)
- cell->setPort("\\POUT", sig);
- cell->setParam("\\HAS_POUT", RTLIL::Const(1));
+ cell->setPort(ID(POUT), sig);
+ cell->setParam(ID(HAS_POUT), RTLIL::Const(1));
}
//Delete the cells we've replaced (let opt_clean handle deleting the now-redundant wires)
@@ -546,7 +546,7 @@ void counter_worker(
int newbits = ceil(log2(extract.count_value));
if(extract.width != newbits)
{
- cell->setParam("\\WIDTH", RTLIL::Const(newbits));
+ cell->setParam(ID(WIDTH), RTLIL::Const(newbits));
log(" Optimizing out %d unused high-order bits (new width is %d)\n",
extract.width - newbits,
newbits);
@@ -559,7 +559,7 @@ void counter_worker(
struct ExtractCounterPass : public Pass {
ExtractCounterPass() : Pass("extract_counter", "Extract GreenPak4 counter cells") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -578,7 +578,7 @@ struct ExtractCounterPass : public Pass {
log("\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
log_header(design, "Executing EXTRACT_COUNTER pass (find counters in netlist).\n");
diff --git a/passes/techmap/extract_fa.cc b/passes/techmap/extract_fa.cc
index a68cc5e2e..9f3bb525b 100644
--- a/passes/techmap/extract_fa.cc
+++ b/passes/techmap/extract_fa.cc
@@ -85,11 +85,11 @@ struct ExtractFaWorker
{
for (auto cell : module->selected_cells())
{
- if (cell->type.in( "$_BUF_", "$_NOT_", "$_AND_", "$_NAND_", "$_OR_", "$_NOR_",
- "$_XOR_", "$_XNOR_", "$_ANDNOT_", "$_ORNOT_", "$_MUX_",
- "$_AOI3_", "$_OAI3_", "$_AOI4_", "$_OAI4_"))
+ if (cell->type.in( ID($_BUF_), ID($_NOT_), ID($_AND_), ID($_NAND_), ID($_OR_), ID($_NOR_),
+ ID($_XOR_), ID($_XNOR_), ID($_ANDNOT_), ID($_ORNOT_), ID($_MUX_), ID($_NMUX_),
+ ID($_AOI3_), ID($_OAI3_), ID($_AOI4_), ID($_OAI4_)))
{
- SigBit y = sigmap(SigBit(cell->getPort("\\Y")));
+ SigBit y = sigmap(SigBit(cell->getPort(ID::Y)));
log_assert(driver.count(y) == 0);
driver[y] = cell;
}
@@ -174,8 +174,10 @@ struct ExtractFaWorker
SigSpec sig = root;
- if (!ce.eval(sig))
- log_abort();
+ if (!ce.eval(sig)) {
+ ce.pop();
+ return;
+ }
if (sig == State::S1)
func |= 1 << i;
@@ -214,8 +216,10 @@ struct ExtractFaWorker
SigSpec sig = root;
- if (!ce.eval(sig))
- log_abort();
+ if (!ce.eval(sig)) {
+ ce.pop();
+ return;
+ }
if (sig == State::S1)
func |= 1 << i;
@@ -258,10 +262,14 @@ struct ExtractFaWorker
pool<SigBit> new_leaves = leaves;
new_leaves.erase(bit);
- if (cell->hasPort("\\A")) new_leaves.insert(sigmap(SigBit(cell->getPort("\\A"))));
- if (cell->hasPort("\\B")) new_leaves.insert(sigmap(SigBit(cell->getPort("\\B"))));
- if (cell->hasPort("\\C")) new_leaves.insert(sigmap(SigBit(cell->getPort("\\C"))));
- if (cell->hasPort("\\D")) new_leaves.insert(sigmap(SigBit(cell->getPort("\\D"))));
+ for (auto port : {ID::A, ID::B, ID(C), ID(D)}) {
+ if (!cell->hasPort(port))
+ continue;
+ auto bit = sigmap(SigBit(cell->getPort(port)));
+ if (!bit.wire)
+ continue;
+ new_leaves.insert(bit);
+ }
if (GetSize(new_leaves) > maxbreadth)
continue;
@@ -273,8 +281,8 @@ struct ExtractFaWorker
void assign_new_driver(SigBit bit, SigBit new_driver)
{
Cell *cell = driver.at(bit);
- if (sigmap(cell->getPort("\\Y")) == bit) {
- cell->setPort("\\Y", module->addWire(NEW_ID));
+ if (sigmap(cell->getPort(ID::Y)) == bit) {
+ cell->setPort(ID::Y, module->addWire(NEW_ID));
module->connect(bit, new_driver);
}
}
@@ -285,7 +293,7 @@ struct ExtractFaWorker
for (auto it : driver)
{
- if (it.second->type.in("$_BUF_", "$_NOT_"))
+ if (it.second->type.in(ID($_BUF_), ID($_NOT_)))
continue;
SigBit root = it.first;
@@ -386,20 +394,20 @@ struct ExtractFaWorker
}
else
{
- Cell *cell = module->addCell(NEW_ID, "$fa");
- cell->setParam("\\WIDTH", 1);
+ Cell *cell = module->addCell(NEW_ID, ID($fa));
+ cell->setParam(ID(WIDTH), 1);
log(" Created $fa cell %s.\n", log_id(cell));
- cell->setPort("\\A", f3i.inv_a ? module->NotGate(NEW_ID, A) : A);
- cell->setPort("\\B", f3i.inv_b ? module->NotGate(NEW_ID, B) : B);
- cell->setPort("\\C", f3i.inv_c ? module->NotGate(NEW_ID, C) : C);
+ cell->setPort(ID::A, f3i.inv_a ? module->NotGate(NEW_ID, A) : A);
+ cell->setPort(ID::B, f3i.inv_b ? module->NotGate(NEW_ID, B) : B);
+ cell->setPort(ID(C), f3i.inv_c ? module->NotGate(NEW_ID, C) : C);
X = module->addWire(NEW_ID);
Y = module->addWire(NEW_ID);
- cell->setPort("\\X", X);
- cell->setPort("\\Y", Y);
+ cell->setPort(ID(X), X);
+ cell->setPort(ID::Y, Y);
facache[fakey] = make_tuple(X, Y, cell);
}
@@ -492,30 +500,30 @@ struct ExtractFaWorker
}
else
{
- Cell *cell = module->addCell(NEW_ID, "$fa");
- cell->setParam("\\WIDTH", 1);
+ Cell *cell = module->addCell(NEW_ID, ID($fa));
+ cell->setParam(ID(WIDTH), 1);
log(" Created $fa cell %s.\n", log_id(cell));
- cell->setPort("\\A", f2i.inv_a ? module->NotGate(NEW_ID, A) : A);
- cell->setPort("\\B", f2i.inv_b ? module->NotGate(NEW_ID, B) : B);
- cell->setPort("\\C", State::S0);
+ cell->setPort(ID::A, f2i.inv_a ? module->NotGate(NEW_ID, A) : A);
+ cell->setPort(ID::B, f2i.inv_b ? module->NotGate(NEW_ID, B) : B);
+ cell->setPort(ID(C), State::S0);
X = module->addWire(NEW_ID);
Y = module->addWire(NEW_ID);
- cell->setPort("\\X", X);
- cell->setPort("\\Y", Y);
+ cell->setPort(ID(X), X);
+ cell->setPort(ID::Y, Y);
}
if (func2.at(key).count(xor2_func)) {
- SigBit YY = invert_xy ? module->NotGate(NEW_ID, Y) : Y;
+ SigBit YY = invert_xy || (f2i.inv_a && !f2i.inv_b) || (!f2i.inv_a && f2i.inv_b) ? module->NotGate(NEW_ID, Y) : Y;
for (auto bit : func2.at(key).at(xor2_func))
assign_new_driver(bit, YY);
}
if (func2.at(key).count(xnor2_func)) {
- SigBit YY = invert_xy ? Y : module->NotGate(NEW_ID, Y);
+ SigBit YY = invert_xy || (f2i.inv_a && !f2i.inv_b) || (!f2i.inv_a && f2i.inv_b) ? Y : module->NotGate(NEW_ID, Y);
for (auto bit : func2.at(key).at(xnor2_func))
assign_new_driver(bit, YY);
}
@@ -531,7 +539,7 @@ struct ExtractFaWorker
struct ExtractFaPass : public Pass {
ExtractFaPass() : Pass("extract_fa", "find and extract full/half adders") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -553,7 +561,7 @@ struct ExtractFaPass : public Pass {
log(" Verbose output\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
ExtractFaConfig config;
diff --git a/passes/techmap/extract_reduce.cc b/passes/techmap/extract_reduce.cc
index cc21c8665..11cfddcd9 100644
--- a/passes/techmap/extract_reduce.cc
+++ b/passes/techmap/extract_reduce.cc
@@ -19,6 +19,7 @@
#include "kernel/yosys.h"
#include "kernel/sigtools.h"
+#include <deque>
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
@@ -33,7 +34,7 @@ struct ExtractReducePass : public Pass
ExtractReducePass() : Pass("extract_reduce", "converts gate chains into $reduce_* cells") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -57,12 +58,12 @@ struct ExtractReducePass : public Pass
inline bool IsRightType(Cell* cell, GateType gt)
{
- return (cell->type == "$_AND_" && gt == GateType::And) ||
- (cell->type == "$_OR_" && gt == GateType::Or) ||
- (cell->type == "$_XOR_" && gt == GateType::Xor);
+ return (cell->type == ID($_AND_) && gt == GateType::And) ||
+ (cell->type == ID($_OR_) && gt == GateType::Or) ||
+ (cell->type == ID($_XOR_) && gt == GateType::Xor);
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
log_header(design, "Executing EXTRACT_REDUCE pass.\n");
log_push();
@@ -123,11 +124,11 @@ struct ExtractReducePass : public Pass
GateType gt;
- if (cell->type == "$_AND_")
+ if (cell->type == ID($_AND_))
gt = GateType::And;
- else if (cell->type == "$_OR_")
+ else if (cell->type == ID($_OR_))
gt = GateType::Or;
- else if (cell->type == "$_XOR_")
+ else if (cell->type == ID($_XOR_))
gt = GateType::Xor;
else
continue;
@@ -147,7 +148,7 @@ struct ExtractReducePass : public Pass
head_cell = x;
- auto y = sigmap(x->getPort("\\Y"));
+ auto y = sigmap(x->getPort(ID::Y));
log_assert(y.size() == 1);
// Should only continue if there is one fanout back into a cell (not to a port)
@@ -165,7 +166,7 @@ struct ExtractReducePass : public Pass
{
//BFS, following all chains until they hit a cell of a different type
//Pick the longest one
- auto y = sigmap(cell->getPort("\\Y"));
+ auto y = sigmap(cell->getPort(ID::Y));
pool<Cell*> current_loads = sig_to_sink[y];
pool<Cell*> next_loads;
@@ -232,7 +233,7 @@ struct ExtractReducePass : public Pass
cur_supercell.insert(x);
- auto a = sigmap(x->getPort("\\A"));
+ auto a = sigmap(x->getPort(ID::A));
log_assert(a.size() == 1);
// Must have only one sink unless we're going off chain
@@ -248,7 +249,7 @@ struct ExtractReducePass : public Pass
}
}
- auto b = sigmap(x->getPort("\\B"));
+ auto b = sigmap(x->getPort(ID::B));
log_assert(b.size() == 1);
// Must have only one sink
@@ -278,26 +279,26 @@ struct ExtractReducePass : public Pass
pool<SigBit> input_pool_intermed;
for (auto x : cur_supercell)
{
- input_pool.insert(sigmap(x->getPort("\\A"))[0]);
- input_pool.insert(sigmap(x->getPort("\\B"))[0]);
- input_pool_intermed.insert(sigmap(x->getPort("\\Y"))[0]);
+ input_pool.insert(sigmap(x->getPort(ID::A))[0]);
+ input_pool.insert(sigmap(x->getPort(ID::B))[0]);
+ input_pool_intermed.insert(sigmap(x->getPort(ID::Y))[0]);
}
SigSpec input;
for (auto b : input_pool)
if (input_pool_intermed.count(b) == 0)
input.append_bit(b);
- SigBit output = sigmap(head_cell->getPort("\\Y")[0]);
+ SigBit output = sigmap(head_cell->getPort(ID::Y)[0]);
auto new_reduce_cell = module->addCell(NEW_ID,
- gt == GateType::And ? "$reduce_and" :
- gt == GateType::Or ? "$reduce_or" :
- gt == GateType::Xor ? "$reduce_xor" : "");
- new_reduce_cell->setParam("\\A_SIGNED", 0);
- new_reduce_cell->setParam("\\A_WIDTH", input.size());
- new_reduce_cell->setParam("\\Y_WIDTH", 1);
- new_reduce_cell->setPort("\\A", input);
- new_reduce_cell->setPort("\\Y", output);
+ gt == GateType::And ? ID($reduce_and) :
+ gt == GateType::Or ? ID($reduce_or) :
+ gt == GateType::Xor ? ID($reduce_xor) : "");
+ new_reduce_cell->setParam(ID(A_SIGNED), 0);
+ new_reduce_cell->setParam(ID(A_WIDTH), input.size());
+ new_reduce_cell->setParam(ID(Y_WIDTH), 1);
+ new_reduce_cell->setPort(ID::A, input);
+ new_reduce_cell->setPort(ID::Y, output);
if(allow_off_chain)
consumed_cells.insert(head_cell);
diff --git a/passes/techmap/extractinv.cc b/passes/techmap/extractinv.cc
new file mode 100644
index 000000000..dda71f12a
--- /dev/null
+++ b/passes/techmap/extractinv.cc
@@ -0,0 +1,123 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2019 Marcin Kościelnicki <mwk@0x04.net>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "kernel/yosys.h"
+#include "kernel/sigtools.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+void split_portname_pair(std::string &port1, std::string &port2)
+{
+ size_t pos = port1.find_first_of(':');
+ if (pos != std::string::npos) {
+ port2 = port1.substr(pos+1);
+ port1 = port1.substr(0, pos);
+ }
+}
+
+struct ExtractinvPass : public Pass {
+ ExtractinvPass() : Pass("extractinv", "extract explicit inverter cells for invertible cell pins") { }
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" extractinv [options] [selection]\n");
+ log("\n");
+ log("Searches the design for all cells with invertible pins controlled by a cell\n");
+ log("parameter (eg. IS_CLK_INVERTED on many Xilinx cells) and removes the parameter.\n");
+ log("If the parameter was set to 1, inserts an explicit inverter cell in front of\n");
+ log("the pin instead. Normally used for output to ISE, which does not support the\n");
+ log("inversion parameters.\n");
+ log("\n");
+ log("To mark a cell port as invertible, use (* invertible_pin = \"param_name\" *)\n");
+ log("on the wire in the blackbox module. The parameter value should have\n");
+ log("the same width as the port, and will be effectively XORed with it.\n");
+ log("\n");
+ log(" -inv <celltype> <portname_out>:<portname_in>\n");
+ log(" Specifies the cell type to use for the inverters and its port names.\n");
+ log(" This option is required.\n");
+ log("\n");
+ }
+
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ log_header(design, "Executing EXTRACTINV pass (extracting pin inverters).\n");
+
+ std::string inv_celltype, inv_portname, inv_portname2;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ std::string arg = args[argidx];
+ if (arg == "-inv" && argidx+2 < args.size()) {
+ inv_celltype = args[++argidx];
+ inv_portname = args[++argidx];
+ split_portname_pair(inv_portname, inv_portname2);
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ if (inv_celltype.empty())
+ log_error("The -inv option is required.\n");
+
+ for (auto module : design->selected_modules())
+ {
+ for (auto cell : module->selected_cells())
+ for (auto port : cell->connections()) {
+ auto cell_module = design->module(cell->type);
+ if (!cell_module)
+ continue;
+ auto cell_wire = cell_module->wire(port.first);
+ if (!cell_wire)
+ continue;
+ auto it = cell_wire->attributes.find("\\invertible_pin");
+ if (it == cell_wire->attributes.end())
+ continue;
+ IdString param_name = RTLIL::escape_id(it->second.decode_string());
+ auto it2 = cell->parameters.find(param_name);
+ // Inversion not used -- skip.
+ if (it2 == cell->parameters.end())
+ continue;
+ SigSpec sig = port.second;
+ if (it2->second.size() != sig.size())
+ log_error("The inversion parameter needs to be the same width as the port (%s.%s port %s parameter %s)", log_id(module->name), log_id(cell->type), log_id(port.first), log_id(param_name));
+ RTLIL::Const invmask = it2->second;
+ cell->parameters.erase(param_name);
+ if (invmask.is_fully_zero())
+ continue;
+ Wire *iwire = module->addWire(NEW_ID, sig.size());
+ for (int i = 0; i < sig.size(); i++)
+ if (invmask[i] == State::S1) {
+ RTLIL::Cell *icell = module->addCell(NEW_ID, RTLIL::escape_id(inv_celltype));
+ icell->setPort(RTLIL::escape_id(inv_portname), SigSpec(iwire, i));
+ icell->setPort(RTLIL::escape_id(inv_portname2), sig[i]);
+ log("Inserting %s on %s.%s.%s[%d].\n", inv_celltype.c_str(), log_id(module), log_id(cell->type), log_id(port.first), i);
+ sig[i] = SigBit(iwire, i);
+ }
+ cell->setPort(port.first, sig);
+ }
+ }
+ }
+} ExtractinvPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/techmap/flowmap.cc b/passes/techmap/flowmap.cc
new file mode 100644
index 000000000..a2ad87f7d
--- /dev/null
+++ b/passes/techmap/flowmap.cc
@@ -0,0 +1,1614 @@
+/*
+ * 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 = {source}, xi; // X and XÌ… in the paper
+
+ NodePrime source_prime = {source, true};
+ 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[source] && !xi[source]);
+ 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(ID($flowmap_level)))
+ labels[input] = input.wire->attributes[ID($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 %d LUTs with maximum depth %d.\n", GetSize(lut_nodes), 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)
+ {
+ int initial_count = GetSize(lut_nodes);
+
+ 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 %d (+%d) LUTs.\n", GetSize(lut_nodes), GetSize(lut_nodes) - 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));
+ unsigned const mask = 1 << input_nodes.size();
+ for (unsigned i = 0; i < mask; 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(ID(src), gate_origin.cell->get_strpool_attribute(ID(src)));
+ packed_count++;
+ }
+ lut_count++;
+ lut_area += lut_table.size();
+
+ if ((int)input_nodes.size() >= minlut)
+ log(" Packed into a %d-LUT %s.%s.\n", GetSize(input_nodes), log_id(module), log_id(lut));
+ else
+ log(" Packed into a %d-LUT %s.%s (implemented as %d-LUT).\n", GetSize(input_nodes), 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 = {ID($_NOT_), ID($_AND_), ID($_OR_), ID($_XOR_), ID($_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/hilomap.cc b/passes/techmap/hilomap.cc
index 82cecac26..9ec651aef 100644
--- a/passes/techmap/hilomap.cc
+++ b/passes/techmap/hilomap.cc
@@ -55,7 +55,7 @@ void hilomap_worker(RTLIL::SigSpec &sig)
struct HilomapPass : public Pass {
HilomapPass() : Pass("hilomap", "technology mapping of constant hi- and/or lo-drivers") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
log("\n");
log(" hilomap [options] [selection]\n");
@@ -74,7 +74,7 @@ struct HilomapPass : public Pass {
log(" each constant bit.\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
log_header(design, "Executing HILOMAP pass (mapping to constant drivers).\n");
diff --git a/passes/techmap/insbuf.cc b/passes/techmap/insbuf.cc
index aa81468dc..2173049b4 100644
--- a/passes/techmap/insbuf.cc
+++ b/passes/techmap/insbuf.cc
@@ -25,7 +25,7 @@ PRIVATE_NAMESPACE_BEGIN
struct InsbufPass : public Pass {
InsbufPass() : Pass("insbuf", "insert buffer cells for connected wires") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
log("\n");
log(" insbuf [options] [selection]\n");
@@ -37,7 +37,7 @@ struct InsbufPass : public Pass {
log(" call to \"clean\" will remove all $_BUF_ in the design.)\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
log_header(design, "Executing INSBUF pass (insert buffer cells for connected wires).\n");
diff --git a/passes/techmap/iopadmap.cc b/passes/techmap/iopadmap.cc
index 4acbf7c0d..531ac2b99 100644
--- a/passes/techmap/iopadmap.cc
+++ b/passes/techmap/iopadmap.cc
@@ -34,7 +34,7 @@ void split_portname_pair(std::string &port1, std::string &port2)
struct IopadmapPass : public Pass {
IopadmapPass() : Pass("iopadmap", "technology mapping of i/o pads (or buffers)") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
log("\n");
log(" iopadmap [options] [selection]\n");
@@ -64,6 +64,11 @@ struct IopadmapPass : public Pass {
log(" of the tristate driver and the 2nd portname is the internal output\n");
log(" buffering the external signal.\n");
log("\n");
+ log(" -ignore <celltype> <portname>[:<portname>]*\n");
+ log(" Skips mapping inputs/outputs that are already connected to given\n");
+ log(" ports of the given cell. Can be used multiple times. This is in\n");
+ log(" addition to the cells specified as mapping targets.\n");
+ log("\n");
log(" -widthparam <param_name>\n");
log(" Use the specified parameter name to set the port width.\n");
log("\n");
@@ -78,16 +83,17 @@ struct IopadmapPass : public Pass {
log("Tristate PADS (-toutpad, -tinoutpad) always operate in -bits mode.\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
log_header(design, "Executing IOPADMAP pass (mapping inputs/outputs to IO-PAD cells).\n");
- std::string inpad_celltype, inpad_portname, inpad_portname2;
- std::string outpad_celltype, outpad_portname, outpad_portname2;
- std::string inoutpad_celltype, inoutpad_portname, inoutpad_portname2;
- std::string toutpad_celltype, toutpad_portname, toutpad_portname2, toutpad_portname3;
- std::string tinoutpad_celltype, tinoutpad_portname, tinoutpad_portname2, tinoutpad_portname3, tinoutpad_portname4;
+ std::string inpad_celltype, inpad_portname_o, inpad_portname_pad;
+ std::string outpad_celltype, outpad_portname_i, outpad_portname_pad;
+ std::string inoutpad_celltype, inoutpad_portname_io, inoutpad_portname_pad;
+ std::string toutpad_celltype, toutpad_portname_oe, toutpad_portname_i, toutpad_portname_pad;
+ std::string tinoutpad_celltype, tinoutpad_portname_oe, tinoutpad_portname_o, tinoutpad_portname_i, tinoutpad_portname_pad;
std::string widthparam, nameparam;
+ pool<pair<IdString, IdString>> ignore;
bool flag_bits = false;
size_t argidx;
@@ -96,35 +102,47 @@ struct IopadmapPass : public Pass {
std::string arg = args[argidx];
if (arg == "-inpad" && argidx+2 < args.size()) {
inpad_celltype = args[++argidx];
- inpad_portname = args[++argidx];
- split_portname_pair(inpad_portname, inpad_portname2);
+ inpad_portname_o = args[++argidx];
+ split_portname_pair(inpad_portname_o, inpad_portname_pad);
continue;
}
if (arg == "-outpad" && argidx+2 < args.size()) {
outpad_celltype = args[++argidx];
- outpad_portname = args[++argidx];
- split_portname_pair(outpad_portname, outpad_portname2);
+ outpad_portname_i = args[++argidx];
+ split_portname_pair(outpad_portname_i, outpad_portname_pad);
continue;
}
if (arg == "-inoutpad" && argidx+2 < args.size()) {
inoutpad_celltype = args[++argidx];
- inoutpad_portname = args[++argidx];
- split_portname_pair(inoutpad_portname, inoutpad_portname2);
+ inoutpad_portname_io = args[++argidx];
+ split_portname_pair(inoutpad_portname_io, inoutpad_portname_pad);
continue;
}
if (arg == "-toutpad" && argidx+2 < args.size()) {
toutpad_celltype = args[++argidx];
- toutpad_portname = args[++argidx];
- split_portname_pair(toutpad_portname, toutpad_portname2);
- split_portname_pair(toutpad_portname2, toutpad_portname3);
+ toutpad_portname_oe = args[++argidx];
+ split_portname_pair(toutpad_portname_oe, toutpad_portname_i);
+ split_portname_pair(toutpad_portname_i, toutpad_portname_pad);
continue;
}
if (arg == "-tinoutpad" && argidx+2 < args.size()) {
tinoutpad_celltype = args[++argidx];
- tinoutpad_portname = args[++argidx];
- split_portname_pair(tinoutpad_portname, tinoutpad_portname2);
- split_portname_pair(tinoutpad_portname2, tinoutpad_portname3);
- split_portname_pair(tinoutpad_portname3, tinoutpad_portname4);
+ tinoutpad_portname_oe = args[++argidx];
+ split_portname_pair(tinoutpad_portname_oe, tinoutpad_portname_o);
+ split_portname_pair(tinoutpad_portname_o, tinoutpad_portname_i);
+ split_portname_pair(tinoutpad_portname_i, tinoutpad_portname_pad);
+ continue;
+ }
+ if (arg == "-ignore" && argidx+2 < args.size()) {
+ std::string ignore_celltype = args[++argidx];
+ std::string ignore_portname = args[++argidx];
+ std::string ignore_portname2;
+ while (!ignore_portname.empty()) {
+ split_portname_pair(ignore_portname, ignore_portname2);
+ ignore.insert(make_pair(RTLIL::escape_id(ignore_celltype), RTLIL::escape_id(ignore_portname)));
+
+ ignore_portname = ignore_portname2;
+ }
continue;
}
if (arg == "-widthparam" && argidx+1 < args.size()) {
@@ -143,116 +161,146 @@ struct IopadmapPass : public Pass {
}
extra_args(args, argidx, design);
+ if (!inpad_portname_pad.empty())
+ ignore.insert(make_pair(RTLIL::escape_id(inpad_celltype), RTLIL::escape_id(inpad_portname_pad)));
+ if (!outpad_portname_pad.empty())
+ ignore.insert(make_pair(RTLIL::escape_id(outpad_celltype), RTLIL::escape_id(outpad_portname_pad)));
+ if (!inoutpad_portname_pad.empty())
+ ignore.insert(make_pair(RTLIL::escape_id(inoutpad_celltype), RTLIL::escape_id(inoutpad_portname_pad)));
+ if (!toutpad_portname_pad.empty())
+ ignore.insert(make_pair(RTLIL::escape_id(toutpad_celltype), RTLIL::escape_id(toutpad_portname_pad)));
+ if (!tinoutpad_portname_pad.empty())
+ ignore.insert(make_pair(RTLIL::escape_id(tinoutpad_celltype), RTLIL::escape_id(tinoutpad_portname_pad)));
+
+ for (auto module : design->modules())
+ if (module->get_blackbox_attribute())
+ for (auto wire : module->wires())
+ if (wire->get_bool_attribute("\\iopad_external_pin"))
+ ignore.insert(make_pair(module->name, wire->name));
+
for (auto module : design->selected_modules())
{
- dict<IdString, pool<int>> skip_wires;
+ pool<SigBit> skip_wire_bits;
+ dict<Wire *, dict<int, pair<Cell *, IdString>>> rewrite_bits;
+
+ for (auto cell : module->cells())
+ for (auto port : cell->connections())
+ if (ignore.count(make_pair(cell->type, port.first)))
+ for (auto bit : port.second)
+ skip_wire_bits.insert(bit);
if (!toutpad_celltype.empty() || !tinoutpad_celltype.empty())
{
- SigMap sigmap(module);
- dict<SigBit, pair<IdString, pool<IdString>>> tbuf_bits;
+ dict<SigBit, Cell *> tbuf_bits;
+ pool<SigBit> driven_bits;
+ // Gather tristate buffers and always-on drivers.
for (auto cell : module->cells())
- if (cell->type == "$_TBUF_") {
- SigBit bit = sigmap(cell->getPort("\\Y").as_bit());
- tbuf_bits[bit].first = cell->name;
+ if (cell->type == ID($_TBUF_)) {
+ SigBit bit = cell->getPort(ID::Y).as_bit();
+ tbuf_bits[bit] = cell;
+ } else {
+ for (auto port : cell->connections())
+ if (!cell->known() || cell->output(port.first))
+ for (auto bit : port.second)
+ driven_bits.insert(bit);
}
- for (auto cell : module->cells())
- for (auto port : cell->connections())
- for (auto bit : sigmap(port.second))
- if (tbuf_bits.count(bit))
- tbuf_bits.at(bit).second.insert(cell->name);
+ // If a wire is a target of an assignment, it is driven, unless the source is 'z.
+ for (auto &conn : module->connections())
+ for (int i = 0; i < GetSize(conn.first); i++) {
+ SigBit dstbit = conn.first[i];
+ SigBit srcbit = conn.second[i];
+ if (!srcbit.wire && srcbit.data == State::Sz)
+ continue;
+ driven_bits.insert(dstbit);
+ }
for (auto wire : module->selected_wires())
{
if (!wire->port_output)
continue;
+ // Don't handle inout ports if we have no suitable buffer type.
+ if (wire->port_input && tinoutpad_celltype.empty())
+ continue;
+
+ // likewise for output ports.
+ if (!wire->port_input && toutpad_celltype.empty())
+ continue;
+
for (int i = 0; i < GetSize(wire); i++)
{
SigBit wire_bit(wire, i);
- SigBit mapped_wire_bit = sigmap(wire_bit);
-
- if (tbuf_bits.count(mapped_wire_bit) == 0)
- continue;
+ Cell *tbuf_cell = nullptr;
- auto &tbuf_cache = tbuf_bits.at(mapped_wire_bit);
- Cell *tbuf_cell = module->cell(tbuf_cache.first);
-
- if (tbuf_cell == nullptr)
+ if (skip_wire_bits.count(wire_bit))
continue;
- SigBit en_sig = tbuf_cell->getPort("\\E").as_bit();
- SigBit data_sig = tbuf_cell->getPort("\\A").as_bit();
+ if (tbuf_bits.count(wire_bit))
+ tbuf_cell = tbuf_bits.at(wire_bit);
+
+ SigBit en_sig;
+ SigBit data_sig;
+ bool is_driven = driven_bits.count(wire_bit);
+
+ if (tbuf_cell != nullptr) {
+ // Found a tristate buffer — use it.
+ en_sig = tbuf_cell->getPort(ID(E)).as_bit();
+ data_sig = tbuf_cell->getPort(ID::A).as_bit();
+ } else if (is_driven) {
+ // No tristate buffer, but an always-on driver is present.
+ // If this is an inout port, we're creating a tinoutpad
+ // anyway, just with a constant 1 as enable.
+ if (!wire->port_input)
+ continue;
+ en_sig = SigBit(State::S1);
+ data_sig = wire_bit;
+ } else {
+ // No driver on a wire. Create a tristate pad with always-0
+ // enable.
+ en_sig = SigBit(State::S0);
+ data_sig = SigBit(State::Sx);
+ }
- if (wire->port_input && !tinoutpad_celltype.empty())
+ if (wire->port_input)
{
log("Mapping port %s.%s[%d] using %s.\n", log_id(module), log_id(wire), i, tinoutpad_celltype.c_str());
Cell *cell = module->addCell(NEW_ID, RTLIL::escape_id(tinoutpad_celltype));
- Wire *owire = module->addWire(NEW_ID);
-
- cell->setPort(RTLIL::escape_id(tinoutpad_portname), en_sig);
- cell->setPort(RTLIL::escape_id(tinoutpad_portname2), owire);
- cell->setPort(RTLIL::escape_id(tinoutpad_portname3), data_sig);
- cell->setPort(RTLIL::escape_id(tinoutpad_portname4), wire_bit);
- cell->attributes["\\keep"] = RTLIL::Const(1);
-
- for (auto cn : tbuf_cache.second) {
- auto c = module->cell(cn);
- if (c == nullptr)
- continue;
- for (auto port : c->connections()) {
- SigSpec sig = port.second;
- bool newsig = false;
- for (auto &bit : sig)
- if (sigmap(bit) == mapped_wire_bit) {
- bit = owire;
- newsig = true;
- }
- if (newsig)
- c->setPort(port.first, sig);
- }
- }
-
- module->remove(tbuf_cell);
- skip_wires[wire->name].insert(i);
- continue;
- }
-
- if (!wire->port_input && !toutpad_celltype.empty())
- {
+ cell->setPort(RTLIL::escape_id(tinoutpad_portname_oe), en_sig);
+ cell->attributes[ID::keep] = RTLIL::Const(1);
+
+ if (tbuf_cell) {
+ module->remove(tbuf_cell);
+ cell->setPort(RTLIL::escape_id(tinoutpad_portname_o), wire_bit);
+ cell->setPort(RTLIL::escape_id(tinoutpad_portname_i), data_sig);
+ } else if (is_driven) {
+ cell->setPort(RTLIL::escape_id(tinoutpad_portname_i), wire_bit);
+ } else {
+ cell->setPort(RTLIL::escape_id(tinoutpad_portname_o), wire_bit);
+ cell->setPort(RTLIL::escape_id(tinoutpad_portname_i), data_sig);
+ }
+ skip_wire_bits.insert(wire_bit);
+ if (!tinoutpad_portname_pad.empty())
+ rewrite_bits[wire][i] = make_pair(cell, RTLIL::escape_id(tinoutpad_portname_pad));
+ } else {
log("Mapping port %s.%s[%d] using %s.\n", log_id(module), log_id(wire), i, toutpad_celltype.c_str());
Cell *cell = module->addCell(NEW_ID, RTLIL::escape_id(toutpad_celltype));
- cell->setPort(RTLIL::escape_id(toutpad_portname), en_sig);
- cell->setPort(RTLIL::escape_id(toutpad_portname2), data_sig);
- cell->setPort(RTLIL::escape_id(toutpad_portname3), wire_bit);
- cell->attributes["\\keep"] = RTLIL::Const(1);
-
- for (auto cn : tbuf_cache.second) {
- auto c = module->cell(cn);
- if (c == nullptr)
- continue;
- for (auto port : c->connections()) {
- SigSpec sig = port.second;
- bool newsig = false;
- for (auto &bit : sig)
- if (sigmap(bit) == mapped_wire_bit) {
- bit = data_sig;
- newsig = true;
- }
- if (newsig)
- c->setPort(port.first, sig);
- }
- }
+ cell->setPort(RTLIL::escape_id(toutpad_portname_oe), en_sig);
+ cell->setPort(RTLIL::escape_id(toutpad_portname_i), data_sig);
+ cell->attributes[ID::keep] = RTLIL::Const(1);
- module->remove(tbuf_cell);
- skip_wires[wire->name].insert(i);
- continue;
+ if (tbuf_cell) {
+ module->remove(tbuf_cell);
+ module->connect(wire_bit, data_sig);
+ }
+ skip_wire_bits.insert(wire_bit);
+ if (!toutpad_portname_pad.empty())
+ rewrite_bits[wire][i] = make_pair(cell, RTLIL::escape_id(toutpad_portname_pad));
}
}
}
@@ -263,14 +311,15 @@ struct IopadmapPass : public Pass {
if (!wire->port_id)
continue;
- std::string celltype, portname, portname2;
+ std::string celltype, portname_int, portname_pad;
pool<int> skip_bit_indices;
- if (skip_wires.count(wire->name)) {
- if (!flag_bits)
- continue;
- skip_bit_indices = skip_wires.at(wire->name);
- }
+ for (int i = 0; i < GetSize(wire); i++)
+ if (skip_wire_bits.count(SigBit(wire, i)))
+ skip_bit_indices.insert(i);
+
+ if (GetSize(wire) == GetSize(skip_bit_indices))
+ continue;
if (wire->port_input && !wire->port_output) {
if (inpad_celltype.empty()) {
@@ -278,8 +327,8 @@ struct IopadmapPass : public Pass {
continue;
}
celltype = inpad_celltype;
- portname = inpad_portname;
- portname2 = inpad_portname2;
+ portname_int = inpad_portname_o;
+ portname_pad = inpad_portname_pad;
} else
if (!wire->port_input && wire->port_output) {
if (outpad_celltype.empty()) {
@@ -287,8 +336,8 @@ struct IopadmapPass : public Pass {
continue;
}
celltype = outpad_celltype;
- portname = outpad_portname;
- portname2 = outpad_portname2;
+ portname_int = outpad_portname_i;
+ portname_pad = outpad_portname_pad;
} else
if (wire->port_input && wire->port_output) {
if (inoutpad_celltype.empty()) {
@@ -296,8 +345,8 @@ struct IopadmapPass : public Pass {
continue;
}
celltype = inoutpad_celltype;
- portname = inoutpad_portname;
- portname2 = inoutpad_portname2;
+ portname_int = inoutpad_portname_io;
+ portname_pad = inoutpad_portname_pad;
} else
log_abort();
@@ -308,47 +357,70 @@ struct IopadmapPass : public Pass {
log("Mapping port %s.%s using %s.\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(wire->name), celltype.c_str());
- RTLIL::Wire *new_wire = NULL;
- if (!portname2.empty()) {
- new_wire = module->addWire(NEW_ID, wire);
- module->swap_names(new_wire, wire);
- wire->attributes.clear();
- }
-
if (flag_bits)
{
for (int i = 0; i < wire->width; i++)
{
- if (skip_bit_indices.count(i)) {
- if (wire->port_output)
- module->connect(SigSpec(new_wire, i), SigSpec(wire, i));
- else
- module->connect(SigSpec(wire, i), SigSpec(new_wire, i));
+ if (skip_bit_indices.count(i))
continue;
- }
+
+ SigBit wire_bit(wire, i);
RTLIL::Cell *cell = module->addCell(NEW_ID, RTLIL::escape_id(celltype));
- cell->setPort(RTLIL::escape_id(portname), RTLIL::SigSpec(wire, i));
- if (!portname2.empty())
- cell->setPort(RTLIL::escape_id(portname2), RTLIL::SigSpec(new_wire, i));
+ cell->setPort(RTLIL::escape_id(portname_int), wire_bit);
+
+ if (!portname_pad.empty())
+ rewrite_bits[wire][i] = make_pair(cell, RTLIL::escape_id(portname_pad));
if (!widthparam.empty())
cell->parameters[RTLIL::escape_id(widthparam)] = RTLIL::Const(1);
if (!nameparam.empty())
cell->parameters[RTLIL::escape_id(nameparam)] = RTLIL::Const(stringf("%s[%d]", RTLIL::id2cstr(wire->name), i));
- cell->attributes["\\keep"] = RTLIL::Const(1);
+ cell->attributes[ID::keep] = RTLIL::Const(1);
}
}
else
{
RTLIL::Cell *cell = module->addCell(NEW_ID, RTLIL::escape_id(celltype));
- cell->setPort(RTLIL::escape_id(portname), RTLIL::SigSpec(wire));
- if (!portname2.empty())
- cell->setPort(RTLIL::escape_id(portname2), RTLIL::SigSpec(new_wire));
+ cell->setPort(RTLIL::escape_id(portname_int), RTLIL::SigSpec(wire));
+
+ if (!portname_pad.empty()) {
+ RTLIL::Wire *new_wire = NULL;
+ new_wire = module->addWire(NEW_ID, wire);
+ module->swap_names(new_wire, wire);
+ wire->attributes.clear();
+ cell->setPort(RTLIL::escape_id(portname_pad), RTLIL::SigSpec(new_wire));
+ }
if (!widthparam.empty())
cell->parameters[RTLIL::escape_id(widthparam)] = RTLIL::Const(wire->width);
if (!nameparam.empty())
cell->parameters[RTLIL::escape_id(nameparam)] = RTLIL::Const(RTLIL::id2cstr(wire->name));
- cell->attributes["\\keep"] = RTLIL::Const(1);
+ cell->attributes[ID::keep] = RTLIL::Const(1);
+ }
+
+ if (!rewrite_bits.count(wire)) {
+ wire->port_id = 0;
+ wire->port_input = false;
+ wire->port_output = false;
+ }
+ }
+
+ for (auto &it : rewrite_bits) {
+ RTLIL::Wire *wire = it.first;
+ RTLIL::Wire *new_wire = module->addWire(NEW_ID, wire);
+ module->swap_names(new_wire, wire);
+ wire->attributes.clear();
+ for (int i = 0; i < wire->width; i++)
+ {
+ SigBit wire_bit(wire, i);
+ if (!it.second.count(i)) {
+ if (wire->port_output)
+ module->connect(SigSpec(new_wire, i), SigSpec(wire, i));
+ else
+ module->connect(SigSpec(wire, i), SigSpec(new_wire, i));
+ } else {
+ auto &new_conn = it.second.at(i);
+ new_conn.first->setPort(new_conn.second, RTLIL::SigSpec(new_wire, i));
+ }
}
wire->port_id = 0;
diff --git a/passes/techmap/libparse.cc b/passes/techmap/libparse.cc
index d5254c029..349ccc115 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,12 +87,14 @@ 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;
+ str = static_cast<char>(c);
while (1) {
c = f.get();
if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') || c == '_' || c == '-' || c == '+' || c == '.')
@@ -100,10 +103,19 @@ int LibertyParser::lexer(std::string &str)
break;
}
f.unget();
- // fprintf(stderr, "LEX: identifier >>%s<<\n", str.c_str());
- return 'v';
+ if (str == "+" || str == "-") {
+ /* Single operator is not an identifier */
+ // fprintf(stderr, "LEX: char >>%s<<\n", str.c_str());
+ return str[0];
+ }
+ else {
+ // fprintf(stderr, "LEX: identifier >>%s<<\n", str.c_str());
+ return 'v';
+ }
}
+ // if it wasn't an identifer, number of array range,
+ // maybe it's a string?
if (c == '"') {
str = "";
while (1) {
@@ -118,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;
@@ -129,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++;
@@ -137,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
@@ -168,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;
@@ -184,13 +229,33 @@ 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()) {
tok = lexer(ast->value);
if (tok != 'v')
error();
+ tok = lexer(str);
+ while (tok == '+' || tok == '-' || tok == '*' || tok == '/') {
+ ast->value += tok;
+ tok = lexer(str);
+ if (tok != 'v')
+ error();
+ ast->value += str;
+ tok = lexer(str);
+ }
+
+ // 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();
continue;
}
@@ -202,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;
@@ -229,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);
}
@@ -244,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 cf6325570..c9ebd06c5 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 2bb0bd8b4..c6618fc9d 100644
--- a/passes/techmap/lut2mux.cc
+++ b/passes/techmap/lut2mux.cc
@@ -25,14 +25,14 @@ PRIVATE_NAMESPACE_BEGIN
int lut2mux(Cell *cell)
{
- SigSpec sig_a = cell->getPort("\\A");
- SigSpec sig_y = cell->getPort("\\Y");
- Const lut = cell->getParam("\\LUT");
+ SigSpec sig_a = cell->getPort(ID::A);
+ SigSpec sig_y = cell->getPort(ID::Y);
+ Const lut = cell->getParam(ID(LUT));
int count = 1;
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
{
@@ -56,7 +56,7 @@ int lut2mux(Cell *cell)
struct Lut2muxPass : public Pass {
Lut2muxPass() : Pass("lut2mux", "convert $lut to $_MUX_") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -65,7 +65,7 @@ struct Lut2muxPass : public Pass {
log("This pass converts $lut cells to $_MUX_ gates.\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
log_header(design, "Executing LUT2MUX pass (convert $lut to $_MUX_).\n");
@@ -81,7 +81,7 @@ struct Lut2muxPass : public Pass {
for (auto module : design->selected_modules())
for (auto cell : module->selected_cells()) {
- if (cell->type == "$lut") {
+ if (cell->type == ID($lut)) {
IdString cell_name = cell->name;
int count = lut2mux(cell);
log("Converted %s.%s to %d MUX cells.\n", log_id(module), log_id(cell_name), count);
diff --git a/passes/techmap/maccmap.cc b/passes/techmap/maccmap.cc
index 32569d076..09f61927c 100644
--- a/passes/techmap/maccmap.cc
+++ b/passes/techmap/maccmap.cc
@@ -36,7 +36,7 @@ struct MaccmapWorker
void add(RTLIL::SigBit bit, int position)
{
- if (position >= width || bit == RTLIL::S0)
+ if (position >= width || bit == State::S0)
return;
if (bits.at(position).count(bit)) {
@@ -53,7 +53,7 @@ struct MaccmapWorker
if (do_subtract) {
a = module->Not(NEW_ID, a);
- add(RTLIL::S1, 0);
+ add(State::S1, 0);
}
for (int i = 0; i < width; i++)
@@ -80,7 +80,7 @@ struct MaccmapWorker
else
{
add(module->And(NEW_ID, a, RTLIL::SigSpec(b[i], width)), false, do_subtract);
- a = {a.extract(0, width-1), RTLIL::S0};
+ a = {a.extract(0, width-1), State::S0};
}
}
@@ -88,10 +88,10 @@ struct MaccmapWorker
{
int start_index = 0, stop_index = GetSize(in1);
- while (start_index < stop_index && in1[start_index] == RTLIL::S0 && in2[start_index] == RTLIL::S0 && in3[start_index] == RTLIL::S0)
+ while (start_index < stop_index && in1[start_index] == State::S0 && in2[start_index] == RTLIL::S0 && in3[start_index] == RTLIL::S0)
start_index++;
- while (start_index < stop_index && in1[stop_index-1] == RTLIL::S0 && in2[stop_index-1] == RTLIL::S0 && in3[stop_index-1] == RTLIL::S0)
+ while (start_index < stop_index && in1[stop_index-1] == State::S0 && in2[stop_index-1] == RTLIL::S0 && in3[stop_index-1] == RTLIL::S0)
stop_index--;
if (start_index == stop_index)
@@ -111,13 +111,13 @@ struct MaccmapWorker
RTLIL::Wire *w1 = module->addWire(NEW_ID, width);
RTLIL::Wire *w2 = module->addWire(NEW_ID, width);
- RTLIL::Cell *cell = module->addCell(NEW_ID, "$fa");
- cell->setParam("\\WIDTH", width);
- cell->setPort("\\A", in1);
- cell->setPort("\\B", in2);
- cell->setPort("\\C", in3);
- cell->setPort("\\Y", w1);
- cell->setPort("\\X", w2);
+ RTLIL::Cell *cell = module->addCell(NEW_ID, ID($fa));
+ cell->setParam(ID(WIDTH), width);
+ cell->setPort(ID::A, in1);
+ cell->setPort(ID::B, in2);
+ cell->setPort(ID(C), in3);
+ cell->setPort(ID::Y, w1);
+ cell->setPort(ID(X), w2);
out1 = {out_zeros_msb, w1, out_zeros_lsb};
out2 = {out_zeros_msb, w2, out_zeros_lsb};
@@ -222,7 +222,7 @@ struct MaccmapWorker
RTLIL::SigSpec in3 = summands[i+2];
RTLIL::SigSpec out1, out2;
fulladd(in1, in2, in3, out1, out2);
- RTLIL::SigBit extra_bit = RTLIL::S0;
+ RTLIL::SigBit extra_bit = State::S0;
if (!tree_sum_bits.empty()) {
extra_bit = tree_sum_bits.back();
tree_sum_bits.pop_back();
@@ -237,23 +237,23 @@ struct MaccmapWorker
}
- RTLIL::Cell *c = module->addCell(NEW_ID, "$alu");
- c->setPort("\\A", summands.front());
- c->setPort("\\B", summands.back());
- c->setPort("\\CI", RTLIL::S0);
- c->setPort("\\BI", RTLIL::S0);
- c->setPort("\\Y", module->addWire(NEW_ID, width));
- c->setPort("\\X", module->addWire(NEW_ID, width));
- c->setPort("\\CO", module->addWire(NEW_ID, width));
+ RTLIL::Cell *c = module->addCell(NEW_ID, ID($alu));
+ c->setPort(ID::A, summands.front());
+ c->setPort(ID::B, summands.back());
+ c->setPort(ID(CI), State::S0);
+ c->setPort(ID(BI), State::S0);
+ c->setPort(ID::Y, module->addWire(NEW_ID, width));
+ c->setPort(ID(X), module->addWire(NEW_ID, width));
+ c->setPort(ID(CO), module->addWire(NEW_ID, width));
c->fixup_parameters();
if (!tree_sum_bits.empty()) {
- c->setPort("\\CI", tree_sum_bits.back());
+ c->setPort(ID(CI), tree_sum_bits.back());
tree_sum_bits.pop_back();
}
log_assert(tree_sum_bits.empty());
- return c->getPort("\\Y");
+ return c->getPort(ID::Y);
}
};
@@ -264,17 +264,17 @@ extern void maccmap(RTLIL::Module *module, RTLIL::Cell *cell, bool unmap = false
void maccmap(RTLIL::Module *module, RTLIL::Cell *cell, bool unmap)
{
- int width = GetSize(cell->getPort("\\Y"));
+ int width = GetSize(cell->getPort(ID::Y));
Macc macc;
macc.from_cell(cell);
RTLIL::SigSpec all_input_bits;
- all_input_bits.append(cell->getPort("\\A"));
- all_input_bits.append(cell->getPort("\\B"));
+ all_input_bits.append(cell->getPort(ID::A));
+ all_input_bits.append(cell->getPort(ID::B));
if (all_input_bits.to_sigbit_set().count(RTLIL::Sx)) {
- module->connect(cell->getPort("\\Y"), RTLIL::SigSpec(RTLIL::Sx, width));
+ module->connect(cell->getPort(ID::Y), RTLIL::SigSpec(RTLIL::Sx, width));
return;
}
@@ -339,9 +339,9 @@ void maccmap(RTLIL::Module *module, RTLIL::Cell *cell, bool unmap)
}
if (summands.front().second)
- module->addNeg(NEW_ID, summands.front().first, cell->getPort("\\Y"));
+ module->addNeg(NEW_ID, summands.front().first, cell->getPort(ID::Y));
else
- module->connect(cell->getPort("\\Y"), summands.front().first);
+ module->connect(cell->getPort(ID::Y), summands.front().first);
}
else
{
@@ -356,7 +356,7 @@ void maccmap(RTLIL::Module *module, RTLIL::Cell *cell, bool unmap)
for (auto &bit : macc.bit_ports)
worker.add(bit, 0);
- module->connect(cell->getPort("\\Y"), worker.synth());
+ module->connect(cell->getPort(ID::Y), worker.synth());
}
}
@@ -365,7 +365,7 @@ PRIVATE_NAMESPACE_BEGIN
struct MaccmapPass : public Pass {
MaccmapPass() : Pass("maccmap", "mapping macc cells") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -375,7 +375,7 @@ struct MaccmapPass : public Pass {
log("is used then the $macc cell is mapped to $add, $sub, etc. cells instead.\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
bool unmap_mode = false;
@@ -393,7 +393,7 @@ struct MaccmapPass : public Pass {
for (auto mod : design->selected_modules())
for (auto cell : mod->selected_cells())
- if (cell->type == "$macc") {
+ if (cell->type == ID($macc)) {
log("Mapping %s.%s (%s).\n", log_id(mod), log_id(cell), log_id(cell->type));
maccmap(mod, cell, unmap_mode);
mod->remove(cell);
diff --git a/passes/techmap/muxcover.cc b/passes/techmap/muxcover.cc
index 1dc649587..5541b6122 100644
--- a/passes/techmap/muxcover.cc
+++ b/passes/techmap/muxcover.cc
@@ -23,6 +23,7 @@
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
+#define COST_DMUX 90
#define COST_MUX2 100
#define COST_MUX4 220
#define COST_MUX8 460
@@ -57,6 +58,13 @@ struct MuxcoverWorker
bool use_mux8;
bool use_mux16;
bool nodecode;
+ bool nopartial;
+
+ int cost_dmux;
+ int cost_mux2;
+ int cost_mux4;
+ int cost_mux8;
+ int cost_mux16;
MuxcoverWorker(Module *module) : module(module), sigmap(module)
{
@@ -64,9 +72,32 @@ struct MuxcoverWorker
use_mux8 = false;
use_mux16 = false;
nodecode = false;
+ nopartial = false;
+ cost_dmux = COST_DMUX;
+ cost_mux2 = COST_MUX2;
+ cost_mux4 = COST_MUX4;
+ cost_mux8 = COST_MUX8;
+ cost_mux16 = COST_MUX16;
decode_mux_counter = 0;
}
+ bool xcmp(std::initializer_list<SigBit> list)
+ {
+ auto cursor = list.begin(), end = list.end();
+ log_assert(cursor != end);
+ SigBit tmp = *(cursor++);
+ while (cursor != end) {
+ SigBit bit = *(cursor++);
+ if (bit == State::Sx)
+ continue;
+ if (tmp == State::Sx)
+ tmp = bit;
+ if (bit != tmp)
+ return false;
+ }
+ return true;
+ }
+
void treeify()
{
pool<SigBit> roots;
@@ -85,13 +116,13 @@ struct MuxcoverWorker
if (!cell->input(conn.first))
continue;
for (auto bit : sigmap(conn.second)) {
- if (used_once.count(bit) || cell->type != "$_MUX_" || conn.first == "\\S")
+ if (used_once.count(bit) || cell->type != ID($_MUX_) || conn.first == ID(S))
roots.insert(bit);
used_once.insert(bit);
}
}
- if (cell->type == "$_MUX_")
- sig_to_mux[sigmap(cell->getPort("\\Y"))] = cell;
+ if (cell->type == ID($_MUX_))
+ sig_to_mux[sigmap(cell->getPort(ID::Y))] = cell;
}
log(" Treeifying %d MUXes:\n", GetSize(sig_to_mux));
@@ -110,8 +141,8 @@ struct MuxcoverWorker
if (sig_to_mux.count(bit) && (bit == rootsig || !roots.count(bit))) {
Cell *c = sig_to_mux.at(bit);
tree.muxes[bit] = c;
- wavefront.insert(sigmap(c->getPort("\\A")));
- wavefront.insert(sigmap(c->getPort("\\B")));
+ wavefront.insert(sigmap(c->getPort(ID::A)));
+ wavefront.insert(sigmap(c->getPort(ID::B)));
}
}
@@ -124,13 +155,22 @@ struct MuxcoverWorker
log(" Finished treeification: Found %d trees.\n", GetSize(tree_list));
}
- bool follow_muxtree(SigBit &ret_bit, tree_t &tree, SigBit bit, const char *path)
+ bool follow_muxtree(SigBit &ret_bit, tree_t &tree, SigBit bit, const char *path, bool first_layer = true)
{
if (*path) {
- if (tree.muxes.count(bit) == 0)
- return false;
+ if (tree.muxes.count(bit) == 0) {
+ if (first_layer || nopartial)
+ return false;
+ while (path[0] && path[1])
+ path++;
+ if (path[0] == 'S')
+ ret_bit = State::Sx;
+ else
+ ret_bit = bit;
+ return true;
+ }
char port_name[3] = {'\\', *path, 0};
- return follow_muxtree(ret_bit, tree, sigmap(tree.muxes.at(bit)->getPort(port_name)), path+1);
+ return follow_muxtree(ret_bit, tree, sigmap(tree.muxes.at(bit)->getPort(port_name)), path+1, false);
} else {
ret_bit = bit;
return true;
@@ -139,7 +179,7 @@ struct MuxcoverWorker
int prepare_decode_mux(SigBit &A, SigBit B, SigBit sel, SigBit bit)
{
- if (A == B)
+ if (A == B || sel == State::Sx)
return 0;
tuple<SigBit, SigBit, SigBit> key(A, B, sel);
@@ -157,7 +197,10 @@ struct MuxcoverWorker
if (std::get<2>(entry))
return 0;
- return COST_MUX2 / GetSize(std::get<1>(entry));
+ if (A == State::Sx || B == State::Sx)
+ return 0;
+
+ return cost_dmux / GetSize(std::get<1>(entry));
}
void implement_decode_mux(SigBit ctrl_bit)
@@ -174,9 +217,32 @@ struct MuxcoverWorker
implement_decode_mux(std::get<0>(key));
implement_decode_mux(std::get<1>(key));
- module->addMuxGate(NEW_ID, std::get<0>(key), std::get<1>(key), std::get<2>(key), ctrl_bit);
+ if (std::get<0>(key) == State::Sx) {
+ module->addBufGate(NEW_ID, std::get<1>(key), ctrl_bit);
+ } else if (std::get<1>(key) == State::Sx) {
+ module->addBufGate(NEW_ID, std::get<0>(key), ctrl_bit);
+ } else {
+ module->addMuxGate(NEW_ID, std::get<0>(key), std::get<1>(key), std::get<2>(key), ctrl_bit);
+ decode_mux_counter++;
+ }
std::get<2>(entry) = true;
- decode_mux_counter++;
+ }
+
+ void find_best_covers(tree_t &tree, const vector<SigBit> &bits)
+ {
+ for (auto bit : bits)
+ find_best_cover(tree, bit);
+ }
+
+ int sum_best_covers(tree_t &tree, const vector<SigBit> &bits)
+ {
+ int sum = 0;
+ for (auto bit : pool<SigBit>(bits.begin(), bits.end())) {
+ int cost = tree.newmuxes.at(bit).cost;
+ log_debug(" Best cost for %s: %d\n", log_signal(bit), cost);
+ sum += cost;
+ }
+ return sum;
}
int find_best_cover(tree_t &tree, SigBit bit)
@@ -209,9 +275,13 @@ struct MuxcoverWorker
mux.inputs.push_back(B);
mux.selects.push_back(S1);
- mux.cost += COST_MUX2;
- mux.cost += find_best_cover(tree, A);
- mux.cost += find_best_cover(tree, B);
+ find_best_covers(tree, mux.inputs);
+ log_debug(" Decode cost for mux2 at %s: %d\n", log_signal(bit), mux.cost);
+
+ mux.cost += cost_mux2;
+ mux.cost += sum_best_covers(tree, mux.inputs);
+
+ log_debug(" Cost of mux2 at %s: %d\n", log_signal(bit), mux.cost);
best_mux = mux;
}
@@ -229,7 +299,7 @@ struct MuxcoverWorker
ok = ok && follow_muxtree(S2, tree, bit, "BS");
if (nodecode)
- ok = ok && S1 == S2;
+ ok = ok && xcmp({S1, S2});
ok = ok && follow_muxtree(T1, tree, bit, "S");
@@ -247,13 +317,15 @@ struct MuxcoverWorker
mux.selects.push_back(S1);
mux.selects.push_back(T1);
- mux.cost += COST_MUX4;
- mux.cost += find_best_cover(tree, A);
- mux.cost += find_best_cover(tree, B);
- mux.cost += find_best_cover(tree, C);
- mux.cost += find_best_cover(tree, D);
+ find_best_covers(tree, mux.inputs);
+ log_debug(" Decode cost for mux4 at %s: %d\n", log_signal(bit), mux.cost);
- if (best_mux.cost > mux.cost)
+ mux.cost += cost_mux4;
+ mux.cost += sum_best_covers(tree, mux.inputs);
+
+ log_debug(" Cost of mux4 at %s: %d\n", log_signal(bit), mux.cost);
+
+ if (best_mux.cost >= mux.cost)
best_mux = mux;
}
}
@@ -277,13 +349,13 @@ struct MuxcoverWorker
ok = ok && follow_muxtree(S4, tree, bit, "BBS");
if (nodecode)
- ok = ok && S1 == S2 && S2 == S3 && S3 == S4;
+ ok = ok && xcmp({S1, S2, S3, S4});
ok = ok && follow_muxtree(T1, tree, bit, "AS");
ok = ok && follow_muxtree(T2, tree, bit, "BS");
if (nodecode)
- ok = ok && T1 == T2;
+ ok = ok && xcmp({T1, T2});
ok = ok && follow_muxtree(U1, tree, bit, "S");
@@ -310,17 +382,15 @@ struct MuxcoverWorker
mux.selects.push_back(T1);
mux.selects.push_back(U1);
- mux.cost += COST_MUX8;
- mux.cost += find_best_cover(tree, A);
- mux.cost += find_best_cover(tree, B);
- mux.cost += find_best_cover(tree, C);
- mux.cost += find_best_cover(tree, D);
- mux.cost += find_best_cover(tree, E);
- mux.cost += find_best_cover(tree, F);
- mux.cost += find_best_cover(tree, G);
- mux.cost += find_best_cover(tree, H);
-
- if (best_mux.cost > mux.cost)
+ find_best_covers(tree, mux.inputs);
+ log_debug(" Decode cost for mux8 at %s: %d\n", log_signal(bit), mux.cost);
+
+ mux.cost += cost_mux8;
+ mux.cost += sum_best_covers(tree, mux.inputs);
+
+ log_debug(" Cost of mux8 at %s: %d\n", log_signal(bit), mux.cost);
+
+ if (best_mux.cost >= mux.cost)
best_mux = mux;
}
}
@@ -356,7 +426,7 @@ struct MuxcoverWorker
ok = ok && follow_muxtree(S8, tree, bit, "BBBS");
if (nodecode)
- ok = ok && S1 == S2 && S2 == S3 && S3 == S4 && S4 == S5 && S5 == S6 && S6 == S7 && S7 == S8;
+ ok = ok && xcmp({S1, S2, S3, S4, S5, S6, S7, S8});
ok = ok && follow_muxtree(T1, tree, bit, "AAS");
ok = ok && follow_muxtree(T2, tree, bit, "ABS");
@@ -364,13 +434,13 @@ struct MuxcoverWorker
ok = ok && follow_muxtree(T4, tree, bit, "BBS");
if (nodecode)
- ok = ok && T1 == T2 && T2 == T3 && T3 == T4;
+ ok = ok && xcmp({T1, T2, T3, T4});
ok = ok && follow_muxtree(U1, tree, bit, "AS");
ok = ok && follow_muxtree(U2, tree, bit, "BS");
if (nodecode)
- ok = ok && U1 == U2;
+ ok = ok && xcmp({U1, U2});
ok = ok && follow_muxtree(V1, tree, bit, "S");
@@ -414,25 +484,15 @@ struct MuxcoverWorker
mux.selects.push_back(U1);
mux.selects.push_back(V1);
- mux.cost += COST_MUX16;
- mux.cost += find_best_cover(tree, A);
- mux.cost += find_best_cover(tree, B);
- mux.cost += find_best_cover(tree, C);
- mux.cost += find_best_cover(tree, D);
- mux.cost += find_best_cover(tree, E);
- mux.cost += find_best_cover(tree, F);
- mux.cost += find_best_cover(tree, G);
- mux.cost += find_best_cover(tree, H);
- mux.cost += find_best_cover(tree, I);
- mux.cost += find_best_cover(tree, J);
- mux.cost += find_best_cover(tree, K);
- mux.cost += find_best_cover(tree, L);
- mux.cost += find_best_cover(tree, M);
- mux.cost += find_best_cover(tree, N);
- mux.cost += find_best_cover(tree, O);
- mux.cost += find_best_cover(tree, P);
-
- if (best_mux.cost > mux.cost)
+ find_best_covers(tree, mux.inputs);
+ log_debug(" Decode cost for mux16 at %s: %d\n", log_signal(bit), mux.cost);
+
+ mux.cost += cost_mux16;
+ mux.cost += sum_best_covers(tree, mux.inputs);
+
+ log_debug(" Cost of mux16 at %s: %d\n", log_signal(bit), mux.cost);
+
+ if (best_mux.cost >= mux.cost)
best_mux = mux;
}
}
@@ -456,69 +516,69 @@ struct MuxcoverWorker
if (GetSize(mux.inputs) == 2) {
count_muxes_by_type[0]++;
- Cell *cell = module->addCell(NEW_ID, "$_MUX_");
- cell->setPort("\\A", mux.inputs[0]);
- cell->setPort("\\B", mux.inputs[1]);
- cell->setPort("\\S", mux.selects[0]);
- cell->setPort("\\Y", bit);
+ Cell *cell = module->addCell(NEW_ID, ID($_MUX_));
+ cell->setPort(ID::A, mux.inputs[0]);
+ cell->setPort(ID::B, mux.inputs[1]);
+ cell->setPort(ID(S), mux.selects[0]);
+ cell->setPort(ID::Y, bit);
return;
}
if (GetSize(mux.inputs) == 4) {
count_muxes_by_type[1]++;
- Cell *cell = module->addCell(NEW_ID, "$_MUX4_");
- cell->setPort("\\A", mux.inputs[0]);
- cell->setPort("\\B", mux.inputs[1]);
- cell->setPort("\\C", mux.inputs[2]);
- cell->setPort("\\D", mux.inputs[3]);
- cell->setPort("\\S", mux.selects[0]);
- cell->setPort("\\T", mux.selects[1]);
- cell->setPort("\\Y", bit);
+ Cell *cell = module->addCell(NEW_ID, ID($_MUX4_));
+ cell->setPort(ID::A, mux.inputs[0]);
+ cell->setPort(ID::B, mux.inputs[1]);
+ cell->setPort(ID(C), mux.inputs[2]);
+ cell->setPort(ID(D), mux.inputs[3]);
+ cell->setPort(ID(S), mux.selects[0]);
+ cell->setPort(ID(T), mux.selects[1]);
+ cell->setPort(ID::Y, bit);
return;
}
if (GetSize(mux.inputs) == 8) {
count_muxes_by_type[2]++;
- Cell *cell = module->addCell(NEW_ID, "$_MUX8_");
- cell->setPort("\\A", mux.inputs[0]);
- cell->setPort("\\B", mux.inputs[1]);
- cell->setPort("\\C", mux.inputs[2]);
- cell->setPort("\\D", mux.inputs[3]);
- cell->setPort("\\E", mux.inputs[4]);
- cell->setPort("\\F", mux.inputs[5]);
- cell->setPort("\\G", mux.inputs[6]);
- cell->setPort("\\H", mux.inputs[7]);
- cell->setPort("\\S", mux.selects[0]);
- cell->setPort("\\T", mux.selects[1]);
- cell->setPort("\\U", mux.selects[2]);
- cell->setPort("\\Y", bit);
+ Cell *cell = module->addCell(NEW_ID, ID($_MUX8_));
+ cell->setPort(ID::A, mux.inputs[0]);
+ cell->setPort(ID::B, mux.inputs[1]);
+ cell->setPort(ID(C), mux.inputs[2]);
+ cell->setPort(ID(D), mux.inputs[3]);
+ cell->setPort(ID(E), mux.inputs[4]);
+ cell->setPort(ID(F), mux.inputs[5]);
+ cell->setPort(ID(G), mux.inputs[6]);
+ cell->setPort(ID(H), mux.inputs[7]);
+ cell->setPort(ID(S), mux.selects[0]);
+ cell->setPort(ID(T), mux.selects[1]);
+ cell->setPort(ID(U), mux.selects[2]);
+ cell->setPort(ID::Y, bit);
return;
}
if (GetSize(mux.inputs) == 16) {
count_muxes_by_type[3]++;
- Cell *cell = module->addCell(NEW_ID, "$_MUX16_");
- cell->setPort("\\A", mux.inputs[0]);
- cell->setPort("\\B", mux.inputs[1]);
- cell->setPort("\\C", mux.inputs[2]);
- cell->setPort("\\D", mux.inputs[3]);
- cell->setPort("\\E", mux.inputs[4]);
- cell->setPort("\\F", mux.inputs[5]);
- cell->setPort("\\G", mux.inputs[6]);
- cell->setPort("\\H", mux.inputs[7]);
- cell->setPort("\\I", mux.inputs[8]);
- cell->setPort("\\J", mux.inputs[9]);
- cell->setPort("\\K", mux.inputs[10]);
- cell->setPort("\\L", mux.inputs[11]);
- cell->setPort("\\M", mux.inputs[12]);
- cell->setPort("\\N", mux.inputs[13]);
- cell->setPort("\\O", mux.inputs[14]);
- cell->setPort("\\P", mux.inputs[15]);
- cell->setPort("\\S", mux.selects[0]);
- cell->setPort("\\T", mux.selects[1]);
- cell->setPort("\\U", mux.selects[2]);
- cell->setPort("\\V", mux.selects[3]);
- cell->setPort("\\Y", bit);
+ Cell *cell = module->addCell(NEW_ID, ID($_MUX16_));
+ cell->setPort(ID::A, mux.inputs[0]);
+ cell->setPort(ID::B, mux.inputs[1]);
+ cell->setPort(ID(C), mux.inputs[2]);
+ cell->setPort(ID(D), mux.inputs[3]);
+ cell->setPort(ID(E), mux.inputs[4]);
+ cell->setPort(ID(F), mux.inputs[5]);
+ cell->setPort(ID(G), mux.inputs[6]);
+ cell->setPort(ID(H), mux.inputs[7]);
+ cell->setPort(ID(I), mux.inputs[8]);
+ cell->setPort(ID(J), mux.inputs[9]);
+ cell->setPort(ID(K), mux.inputs[10]);
+ cell->setPort(ID(L), mux.inputs[11]);
+ cell->setPort(ID(M), mux.inputs[12]);
+ cell->setPort(ID(N), mux.inputs[13]);
+ cell->setPort(ID(O), mux.inputs[14]);
+ cell->setPort(ID(P), mux.inputs[15]);
+ cell->setPort(ID(S), mux.selects[0]);
+ cell->setPort(ID(T), mux.selects[1]);
+ cell->setPort(ID(U), mux.selects[2]);
+ cell->setPort(ID(V), mux.selects[3]);
+ cell->setPort(ID::Y, bit);
return;
}
@@ -528,6 +588,7 @@ struct MuxcoverWorker
void treecover(tree_t &tree)
{
int count_muxes_by_type[4] = {0, 0, 0, 0};
+ log_debug(" Searching for best cover for tree at %s.\n", log_signal(tree.root));
find_best_cover(tree, tree.root);
implement_best_cover(tree, tree.root, count_muxes_by_type);
log(" Replaced tree at %s: %d MUX2, %d MUX4, %d MUX8, %d MUX16\n", log_signal(tree.root),
@@ -544,12 +605,13 @@ struct MuxcoverWorker
log(" Covering trees:\n");
- // pre-fill cache of decoder muxes
- if (!nodecode)
+ if (!nodecode) {
+ log_debug(" Populating cache of decoder muxes.\n");
for (auto &tree : tree_list) {
find_best_cover(tree, tree.root);
tree.newmuxes.clear();
}
+ }
for (auto &tree : tree_list)
treecover(tree);
@@ -561,7 +623,7 @@ struct MuxcoverWorker
struct MuxcoverPass : public Pass {
MuxcoverPass() : Pass("muxcover", "cover trees of MUX cells with wider MUXes") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -569,17 +631,32 @@ struct MuxcoverPass : public Pass {
log("\n");
log("Cover trees of $_MUX_ cells with $_MUX{4,8,16}_ cells\n");
log("\n");
- log(" -mux4, -mux8, -mux16\n");
- log(" Use the specified types of MUXes. If none of those options are used,\n");
- log(" the effect is the same as if all of them where used.\n");
+ log(" -mux4[=cost], -mux8[=cost], -mux16[=cost]\n");
+ log(" Cover $_MUX_ trees using the specified types of MUXes (with optional\n");
+ log(" integer costs). If none of these options are given, the effect is the\n");
+ log(" same as if all of them are.\n");
+ log(" Default costs: $_MUX4_ = %d, $_MUX8_ = %d, \n", COST_MUX4, COST_MUX8);
+ log(" $_MUX16_ = %d\n", COST_MUX16);
+ log("\n");
+ log(" -mux2=cost\n");
+ log(" Use the specified cost for $_MUX_ cells when making covering decisions.\n");
+ log(" Default cost: $_MUX_ = %d\n", COST_MUX2);
+ log("\n");
+ log(" -dmux=cost\n");
+ log(" Use the specified cost for $_MUX_ cells used in decoders.\n");
+ log(" Default cost: %d\n", COST_DMUX);
log("\n");
log(" -nodecode\n");
log(" Do not insert decoder logic. This reduces the number of possible\n");
log(" substitutions, but guarantees that the resulting circuit is not\n");
log(" less efficient than the original circuit.\n");
log("\n");
+ log(" -nopartial\n");
+ log(" Do not consider mappings that use $_MUX<N>_ to select from less\n");
+ log(" than <N> different signals.\n");
+ log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
log_header(design, "Executing MUXCOVER pass (mapping to wider MUXes).\n");
@@ -587,26 +664,57 @@ struct MuxcoverPass : public Pass {
bool use_mux8 = false;
bool use_mux16 = false;
bool nodecode = false;
+ bool nopartial = false;
+ int cost_dmux = COST_DMUX;
+ int cost_mux2 = COST_MUX2;
+ int cost_mux4 = COST_MUX4;
+ int cost_mux8 = COST_MUX8;
+ int cost_mux16 = COST_MUX16;
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++)
{
- if (args[argidx] == "-mux4") {
+ const auto &arg = args[argidx];
+ if (arg.size() >= 6 && arg.compare(0,6,"-mux2=") == 0) {
+ cost_mux2 = atoi(arg.substr(6).c_str());
+ continue;
+ }
+ if (arg.size() >= 5 && arg.compare(0,5,"-mux4") == 0) {
use_mux4 = true;
+ if (arg.size() > 5) {
+ if (arg[5] != '=') break;
+ cost_mux4 = atoi(arg.substr(6).c_str());
+ }
continue;
}
- if (args[argidx] == "-mux8") {
+ if (arg.size() >= 5 && arg.compare(0,5,"-mux8") == 0) {
use_mux8 = true;
+ if (arg.size() > 5) {
+ if (arg[5] != '=') break;
+ cost_mux8 = atoi(arg.substr(6).c_str());
+ }
continue;
}
- if (args[argidx] == "-mux16") {
+ if (arg.size() >= 6 && arg.compare(0,6,"-mux16") == 0) {
use_mux16 = true;
+ if (arg.size() > 6) {
+ if (arg[6] != '=') break;
+ cost_mux16 = atoi(arg.substr(7).c_str());
+ }
+ continue;
+ }
+ if (arg.size() >= 6 && arg.compare(0,6,"-dmux=") == 0) {
+ cost_dmux = atoi(arg.substr(6).c_str());
continue;
}
- if (args[argidx] == "-nodecode") {
+ if (arg == "-nodecode") {
nodecode = true;
continue;
}
+ if (arg == "-nopartial") {
+ nopartial = true;
+ continue;
+ }
break;
}
extra_args(args, argidx, design);
@@ -623,7 +731,13 @@ struct MuxcoverPass : public Pass {
worker.use_mux4 = use_mux4;
worker.use_mux8 = use_mux8;
worker.use_mux16 = use_mux16;
+ worker.cost_dmux = cost_dmux;
+ worker.cost_mux2 = cost_mux2;
+ worker.cost_mux4 = cost_mux4;
+ worker.cost_mux8 = cost_mux8;
+ worker.cost_mux16 = cost_mux16;
worker.nodecode = nodecode;
+ worker.nopartial = nopartial;
worker.run();
}
}
diff --git a/passes/techmap/nlutmap.cc b/passes/techmap/nlutmap.cc
index f1a41cc3e..798d82248 100644
--- a/passes/techmap/nlutmap.cc
+++ b/passes/techmap/nlutmap.cc
@@ -82,10 +82,10 @@ struct NlutmapWorker
for (auto cell : module->cells())
{
- if (cell->type != "$lut" || mapped_cells.count(cell))
+ if (cell->type != ID($lut) || mapped_cells.count(cell))
continue;
- if (GetSize(cell->getPort("\\A")) == lut_size || lut_size == 2)
+ if (GetSize(cell->getPort(ID::A)) == lut_size || lut_size == 2)
candidate_ratings[cell] = 0;
for (auto &conn : cell->connections())
@@ -119,7 +119,7 @@ struct NlutmapWorker
if (config.assert_mode) {
for (auto cell : module->cells())
- if (cell->type == "$lut" && !mapped_cells.count(cell))
+ if (cell->type == ID($lut) && !mapped_cells.count(cell))
log_error("Insufficient number of LUTs to map all logic cells!\n");
}
@@ -129,7 +129,7 @@ struct NlutmapWorker
struct NlutmapPass : public Pass {
NlutmapPass() : Pass("nlutmap", "map to LUTs of different sizes") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -149,7 +149,7 @@ struct NlutmapPass : public Pass {
log("to generic logic gates ($_AND_, etc.).\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
NlutmapConfig config;
diff --git a/passes/techmap/pmuxtree.cc b/passes/techmap/pmuxtree.cc
index c626dbcc5..31ab13cec 100644
--- a/passes/techmap/pmuxtree.cc
+++ b/passes/techmap/pmuxtree.cc
@@ -67,16 +67,16 @@ static SigSpec recursive_mux_generator(Module *module, const SigSpec &sig_data,
struct PmuxtreePass : public Pass {
PmuxtreePass() : Pass("pmuxtree", "transform $pmux cells to trees of $mux cells") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
- log(" pmuxtree [options] [selection]\n");
+ log(" pmuxtree [selection]\n");
log("\n");
- log("This pass transforms $pmux cells to a trees of $mux cells.\n");
+ log("This pass transforms $pmux cells to trees of $mux cells.\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
log_header(design, "Executing PMUXTREE pass.\n");
@@ -89,21 +89,21 @@ struct PmuxtreePass : public Pass {
for (auto module : design->selected_modules())
for (auto cell : module->selected_cells())
{
- if (cell->type != "$pmux")
+ if (cell->type != ID($pmux))
continue;
- SigSpec sig_data = cell->getPort("\\B");
- SigSpec sig_sel = cell->getPort("\\S");
+ SigSpec sig_data = cell->getPort(ID::B);
+ SigSpec sig_sel = cell->getPort(ID(S));
- if (!cell->getPort("\\A").is_fully_undef()) {
- sig_data.append(cell->getPort("\\A"));
+ if (!cell->getPort(ID::A).is_fully_undef()) {
+ sig_data.append(cell->getPort(ID::A));
SigSpec sig_sel_or = module->ReduceOr(NEW_ID, sig_sel);
sig_sel.append(module->Not(NEW_ID, sig_sel_or));
}
SigSpec result, result_or;
result = recursive_mux_generator(module, sig_data, sig_sel, result_or);
- module->connect(cell->getPort("\\Y"), result);
+ module->connect(cell->getPort(ID::Y), result);
module->remove(cell);
}
}
diff --git a/passes/techmap/shregmap.cc b/passes/techmap/shregmap.cc
index 6936b499e..be00e5030 100644
--- a/passes/techmap/shregmap.cc
+++ b/passes/techmap/shregmap.cc
@@ -71,22 +71,22 @@ struct ShregmapTechGreenpak4 : ShregmapTech
bool fixup(Cell *cell, dict<int, SigBit> &taps)
{
- auto D = cell->getPort("\\D");
- auto C = cell->getPort("\\C");
+ auto D = cell->getPort(ID(D));
+ auto C = cell->getPort(ID(C));
- auto newcell = cell->module->addCell(NEW_ID, "\\GP_SHREG");
- newcell->setPort("\\nRST", State::S1);
- newcell->setPort("\\CLK", C);
- newcell->setPort("\\IN", D);
+ auto newcell = cell->module->addCell(NEW_ID, ID(GP_SHREG));
+ newcell->setPort(ID(nRST), State::S1);
+ newcell->setPort(ID(CLK), C);
+ newcell->setPort(ID(IN), D);
int i = 0;
for (auto tap : taps) {
- newcell->setPort(i ? "\\OUTB" : "\\OUTA", tap.second);
- newcell->setParam(i ? "\\OUTB_TAP" : "\\OUTA_TAP", tap.first + 1);
+ newcell->setPort(i ? ID(OUTB) : ID(OUTA), tap.second);
+ newcell->setParam(i ? ID(OUTB_TAP) : ID(OUTA_TAP), tap.first + 1);
i++;
}
- cell->setParam("\\OUTA_INVERT", 0);
+ cell->setParam(ID(OUTA_INVERT), 0);
return false;
}
};
@@ -112,14 +112,14 @@ struct ShregmapWorker
{
for (auto wire : module->wires())
{
- if (wire->port_output || wire->get_bool_attribute("\\keep")) {
+ if (wire->port_output || wire->get_bool_attribute(ID::keep)) {
for (auto bit : sigmap(wire))
sigbit_with_non_chain_users.insert(bit);
}
- if (wire->attributes.count("\\init")) {
+ if (wire->attributes.count(ID(init))) {
SigSpec initsig = sigmap(wire);
- Const initval = wire->attributes.at("\\init");
+ Const initval = wire->attributes.at(ID(init));
for (int i = 0; i < GetSize(initsig) && i < GetSize(initval); i++)
if (initval[i] == State::S0 && !opts.zinit)
sigbit_init[initsig[i]] = false;
@@ -130,7 +130,7 @@ struct ShregmapWorker
for (auto cell : module->cells())
{
- if (opts.ffcells.count(cell->type) && !cell->get_bool_attribute("\\keep"))
+ if (opts.ffcells.count(cell->type) && !cell->get_bool_attribute(ID::keep))
{
IdString d_port = opts.ffcells.at(cell->type).first;
IdString q_port = opts.ffcells.at(cell->type).second;
@@ -140,10 +140,22 @@ struct ShregmapWorker
if (opts.init || sigbit_init.count(q_bit) == 0)
{
- if (sigbit_chain_next.count(d_bit)) {
+ auto r = sigbit_chain_next.insert(std::make_pair(d_bit, cell));
+ if (!r.second) {
+ // Insertion not successful means that d_bit is already
+ // connected to another register, thus mark it as a
+ // non chain user ...
sigbit_with_non_chain_users.insert(d_bit);
- } else
- sigbit_chain_next[d_bit] = cell;
+ // ... and clone d_bit into another wire, and use that
+ // wire as a different key in the d_bit-to-cell dictionary
+ // so that it can be identified as another chain
+ // (omitting this common flop)
+ // Link: https://github.com/YosysHQ/yosys/pull/1085
+ Wire *wire = module->addWire(NEW_ID);
+ module->connect(wire, d_bit);
+ sigmap.add(wire, d_bit);
+ sigbit_chain_next.insert(std::make_pair(wire, cell));
+ }
sigbit_chain_prev[q_bit] = cell;
continue;
@@ -179,7 +191,7 @@ struct ShregmapWorker
IdString q_port = opts.ffcells.at(c1->type).second;
auto c1_conn = c1->connections();
- auto c2_conn = c1->connections();
+ auto c2_conn = c2->connections();
c1_conn.erase(d_port);
c1_conn.erase(q_port);
@@ -307,7 +319,7 @@ struct ShregmapWorker
initval.push_back(State::S0);
remove_init.insert(bit);
}
- first_cell->setParam("\\INIT", initval);
+ first_cell->setParam(ID(INIT), initval);
}
if (opts.zinit)
@@ -321,22 +333,22 @@ struct ShregmapWorker
int param_clkpol = -1;
int param_enpol = 2;
- if (first_cell->type == "$_DFF_N_") param_clkpol = 0;
- if (first_cell->type == "$_DFF_P_") param_clkpol = 1;
+ if (first_cell->type == ID($_DFF_N_)) param_clkpol = 0;
+ if (first_cell->type == ID($_DFF_P_)) param_clkpol = 1;
- if (first_cell->type == "$_DFFE_NN_") param_clkpol = 0, param_enpol = 0;
- if (first_cell->type == "$_DFFE_NP_") param_clkpol = 0, param_enpol = 1;
- if (first_cell->type == "$_DFFE_PN_") param_clkpol = 1, param_enpol = 0;
- if (first_cell->type == "$_DFFE_PP_") param_clkpol = 1, param_enpol = 1;
+ if (first_cell->type == ID($_DFFE_NN_)) param_clkpol = 0, param_enpol = 0;
+ if (first_cell->type == ID($_DFFE_NP_)) param_clkpol = 0, param_enpol = 1;
+ if (first_cell->type == ID($_DFFE_PN_)) param_clkpol = 1, param_enpol = 0;
+ if (first_cell->type == ID($_DFFE_PP_)) param_clkpol = 1, param_enpol = 1;
log_assert(param_clkpol >= 0);
- first_cell->setParam("\\CLKPOL", param_clkpol);
- if (opts.ffe) first_cell->setParam("\\ENPOL", param_enpol);
+ first_cell->setParam(ID(CLKPOL), param_clkpol);
+ if (opts.ffe) first_cell->setParam(ID(ENPOL), param_enpol);
}
first_cell->type = shreg_cell_type_str;
first_cell->setPort(q_port, last_cell->getPort(q_port));
- first_cell->setParam("\\DEPTH", depth);
+ first_cell->setParam(ID(DEPTH), depth);
if (opts.tech != nullptr && !opts.tech->fixup(first_cell, taps_dict))
remove_cells.insert(first_cell);
@@ -354,18 +366,18 @@ struct ShregmapWorker
for (auto wire : module->wires())
{
- if (wire->attributes.count("\\init") == 0)
+ if (wire->attributes.count(ID(init)) == 0)
continue;
SigSpec initsig = sigmap(wire);
- Const &initval = wire->attributes.at("\\init");
+ Const &initval = wire->attributes.at(ID(init));
for (int i = 0; i < GetSize(initsig) && i < GetSize(initval); i++)
if (remove_init.count(initsig[i]))
initval[i] = State::Sx;
if (SigSpec(initval).is_fully_undef())
- wire->attributes.erase("\\init");
+ wire->attributes.erase(ID(init));
}
remove_cells.clear();
@@ -391,7 +403,7 @@ struct ShregmapWorker
struct ShregmapPass : public Pass {
ShregmapPass() : Pass("shregmap", "map shift registers") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -449,7 +461,7 @@ struct ShregmapPass : public Pass {
log(" map to greenpak4 shift registers.\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
ShregmapOptions opts;
string clkpol, enpol;
@@ -536,19 +548,19 @@ struct ShregmapPass : public Pass {
bool en_neg = enpol == "neg" || enpol == "any" || enpol == "any_or_none";
if (clk_pos && en_none)
- opts.ffcells["$_DFF_P_"] = make_pair(IdString("\\D"), IdString("\\Q"));
+ opts.ffcells[ID($_DFF_P_)] = make_pair(IdString(ID(D)), IdString(ID(Q)));
if (clk_neg && en_none)
- opts.ffcells["$_DFF_N_"] = make_pair(IdString("\\D"), IdString("\\Q"));
+ opts.ffcells[ID($_DFF_N_)] = make_pair(IdString(ID(D)), IdString(ID(Q)));
if (clk_pos && en_pos)
- opts.ffcells["$_DFFE_PP_"] = make_pair(IdString("\\D"), IdString("\\Q"));
+ opts.ffcells[ID($_DFFE_PP_)] = make_pair(IdString(ID(D)), IdString(ID(Q)));
if (clk_pos && en_neg)
- opts.ffcells["$_DFFE_PN_"] = make_pair(IdString("\\D"), IdString("\\Q"));
+ opts.ffcells[ID($_DFFE_PN_)] = make_pair(IdString(ID(D)), IdString(ID(Q)));
if (clk_neg && en_pos)
- opts.ffcells["$_DFFE_NP_"] = make_pair(IdString("\\D"), IdString("\\Q"));
+ opts.ffcells[ID($_DFFE_NP_)] = make_pair(IdString(ID(D)), IdString(ID(Q)));
if (clk_neg && en_neg)
- opts.ffcells["$_DFFE_NN_"] = make_pair(IdString("\\D"), IdString("\\Q"));
+ opts.ffcells[ID($_DFFE_NN_)] = make_pair(IdString(ID(D)), IdString(ID(Q)));
if (en_pos || en_neg)
opts.ffe = true;
diff --git a/passes/techmap/simplemap.cc b/passes/techmap/simplemap.cc
index c6b932bdc..91574f3c6 100644
--- a/passes/techmap/simplemap.cc
+++ b/passes/techmap/simplemap.cc
@@ -28,82 +28,82 @@ YOSYS_NAMESPACE_BEGIN
void simplemap_not(RTLIL::Module *module, RTLIL::Cell *cell)
{
- RTLIL::SigSpec sig_a = cell->getPort("\\A");
- RTLIL::SigSpec sig_y = cell->getPort("\\Y");
+ RTLIL::SigSpec sig_a = cell->getPort(ID::A);
+ RTLIL::SigSpec sig_y = cell->getPort(ID::Y);
- sig_a.extend_u0(GetSize(sig_y), cell->parameters.at("\\A_SIGNED").as_bool());
+ sig_a.extend_u0(GetSize(sig_y), cell->parameters.at(ID(A_SIGNED)).as_bool());
for (int i = 0; i < GetSize(sig_y); i++) {
- RTLIL::Cell *gate = module->addCell(NEW_ID, "$_NOT_");
- gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src"));
- gate->setPort("\\A", sig_a[i]);
- gate->setPort("\\Y", sig_y[i]);
+ RTLIL::Cell *gate = module->addCell(NEW_ID, ID($_NOT_));
+ gate->add_strpool_attribute(ID(src), cell->get_strpool_attribute(ID(src)));
+ gate->setPort(ID::A, sig_a[i]);
+ gate->setPort(ID::Y, sig_y[i]);
}
}
void simplemap_pos(RTLIL::Module *module, RTLIL::Cell *cell)
{
- RTLIL::SigSpec sig_a = cell->getPort("\\A");
- RTLIL::SigSpec sig_y = cell->getPort("\\Y");
+ RTLIL::SigSpec sig_a = cell->getPort(ID::A);
+ RTLIL::SigSpec sig_y = cell->getPort(ID::Y);
- sig_a.extend_u0(GetSize(sig_y), cell->parameters.at("\\A_SIGNED").as_bool());
+ sig_a.extend_u0(GetSize(sig_y), cell->parameters.at(ID(A_SIGNED)).as_bool());
module->connect(RTLIL::SigSig(sig_y, sig_a));
}
void simplemap_bitop(RTLIL::Module *module, RTLIL::Cell *cell)
{
- RTLIL::SigSpec sig_a = cell->getPort("\\A");
- RTLIL::SigSpec sig_b = cell->getPort("\\B");
- RTLIL::SigSpec sig_y = cell->getPort("\\Y");
+ RTLIL::SigSpec sig_a = cell->getPort(ID::A);
+ RTLIL::SigSpec sig_b = cell->getPort(ID::B);
+ RTLIL::SigSpec sig_y = cell->getPort(ID::Y);
- sig_a.extend_u0(GetSize(sig_y), cell->parameters.at("\\A_SIGNED").as_bool());
- sig_b.extend_u0(GetSize(sig_y), cell->parameters.at("\\B_SIGNED").as_bool());
+ sig_a.extend_u0(GetSize(sig_y), cell->parameters.at(ID(A_SIGNED)).as_bool());
+ sig_b.extend_u0(GetSize(sig_y), cell->parameters.at(ID(B_SIGNED)).as_bool());
- if (cell->type == "$xnor")
+ if (cell->type == ID($xnor))
{
RTLIL::SigSpec sig_t = module->addWire(NEW_ID, GetSize(sig_y));
for (int i = 0; i < GetSize(sig_y); i++) {
- RTLIL::Cell *gate = module->addCell(NEW_ID, "$_NOT_");
- gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src"));
- gate->setPort("\\A", sig_t[i]);
- gate->setPort("\\Y", sig_y[i]);
+ RTLIL::Cell *gate = module->addCell(NEW_ID, ID($_NOT_));
+ gate->add_strpool_attribute(ID(src), cell->get_strpool_attribute(ID(src)));
+ gate->setPort(ID::A, sig_t[i]);
+ gate->setPort(ID::Y, sig_y[i]);
}
sig_y = sig_t;
}
- std::string gate_type;
- if (cell->type == "$and") gate_type = "$_AND_";
- if (cell->type == "$or") gate_type = "$_OR_";
- if (cell->type == "$xor") gate_type = "$_XOR_";
- if (cell->type == "$xnor") gate_type = "$_XOR_";
+ IdString gate_type;
+ if (cell->type == ID($and)) gate_type = ID($_AND_);
+ if (cell->type == ID($or)) gate_type = ID($_OR_);
+ if (cell->type == ID($xor)) gate_type = ID($_XOR_);
+ if (cell->type == ID($xnor)) gate_type = ID($_XOR_);
log_assert(!gate_type.empty());
for (int i = 0; i < GetSize(sig_y); i++) {
RTLIL::Cell *gate = module->addCell(NEW_ID, gate_type);
- gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src"));
- gate->setPort("\\A", sig_a[i]);
- gate->setPort("\\B", sig_b[i]);
- gate->setPort("\\Y", sig_y[i]);
+ gate->add_strpool_attribute(ID(src), cell->get_strpool_attribute(ID(src)));
+ gate->setPort(ID::A, sig_a[i]);
+ gate->setPort(ID::B, sig_b[i]);
+ gate->setPort(ID::Y, sig_y[i]);
}
}
void simplemap_reduce(RTLIL::Module *module, RTLIL::Cell *cell)
{
- RTLIL::SigSpec sig_a = cell->getPort("\\A");
- RTLIL::SigSpec sig_y = cell->getPort("\\Y");
+ RTLIL::SigSpec sig_a = cell->getPort(ID::A);
+ RTLIL::SigSpec sig_y = cell->getPort(ID::Y);
if (sig_y.size() == 0)
return;
if (sig_a.size() == 0) {
- if (cell->type == "$reduce_and") module->connect(RTLIL::SigSig(sig_y, RTLIL::SigSpec(1, sig_y.size())));
- if (cell->type == "$reduce_or") module->connect(RTLIL::SigSig(sig_y, RTLIL::SigSpec(0, sig_y.size())));
- if (cell->type == "$reduce_xor") module->connect(RTLIL::SigSig(sig_y, RTLIL::SigSpec(0, sig_y.size())));
- if (cell->type == "$reduce_xnor") module->connect(RTLIL::SigSig(sig_y, RTLIL::SigSpec(1, sig_y.size())));
- if (cell->type == "$reduce_bool") module->connect(RTLIL::SigSig(sig_y, RTLIL::SigSpec(0, sig_y.size())));
+ if (cell->type == ID($reduce_and)) module->connect(RTLIL::SigSig(sig_y, RTLIL::SigSpec(1, sig_y.size())));
+ if (cell->type == ID($reduce_or)) module->connect(RTLIL::SigSig(sig_y, RTLIL::SigSpec(0, sig_y.size())));
+ if (cell->type == ID($reduce_xor)) module->connect(RTLIL::SigSig(sig_y, RTLIL::SigSpec(0, sig_y.size())));
+ if (cell->type == ID($reduce_xnor)) module->connect(RTLIL::SigSig(sig_y, RTLIL::SigSpec(1, sig_y.size())));
+ if (cell->type == ID($reduce_bool)) module->connect(RTLIL::SigSig(sig_y, RTLIL::SigSpec(0, sig_y.size())));
return;
}
@@ -112,12 +112,12 @@ void simplemap_reduce(RTLIL::Module *module, RTLIL::Cell *cell)
sig_y = sig_y.extract(0, 1);
}
- std::string gate_type;
- if (cell->type == "$reduce_and") gate_type = "$_AND_";
- if (cell->type == "$reduce_or") gate_type = "$_OR_";
- if (cell->type == "$reduce_xor") gate_type = "$_XOR_";
- if (cell->type == "$reduce_xnor") gate_type = "$_XOR_";
- if (cell->type == "$reduce_bool") gate_type = "$_OR_";
+ IdString gate_type;
+ if (cell->type == ID($reduce_and)) gate_type = ID($_AND_);
+ if (cell->type == ID($reduce_or)) gate_type = ID($_OR_);
+ if (cell->type == ID($reduce_xor)) gate_type = ID($_XOR_);
+ if (cell->type == ID($reduce_xnor)) gate_type = ID($_XOR_);
+ if (cell->type == ID($reduce_bool)) gate_type = ID($_OR_);
log_assert(!gate_type.empty());
RTLIL::Cell *last_output_cell = NULL;
@@ -134,22 +134,22 @@ void simplemap_reduce(RTLIL::Module *module, RTLIL::Cell *cell)
}
RTLIL::Cell *gate = module->addCell(NEW_ID, gate_type);
- gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src"));
- gate->setPort("\\A", sig_a[i]);
- gate->setPort("\\B", sig_a[i+1]);
- gate->setPort("\\Y", sig_t[i/2]);
+ gate->add_strpool_attribute(ID(src), cell->get_strpool_attribute(ID(src)));
+ gate->setPort(ID::A, sig_a[i]);
+ gate->setPort(ID::B, sig_a[i+1]);
+ gate->setPort(ID::Y, sig_t[i/2]);
last_output_cell = gate;
}
sig_a = sig_t;
}
- if (cell->type == "$reduce_xnor") {
+ if (cell->type == ID($reduce_xnor)) {
RTLIL::SigSpec sig_t = module->addWire(NEW_ID);
- RTLIL::Cell *gate = module->addCell(NEW_ID, "$_NOT_");
- gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src"));
- gate->setPort("\\A", sig_a);
- gate->setPort("\\Y", sig_t);
+ RTLIL::Cell *gate = module->addCell(NEW_ID, ID($_NOT_));
+ gate->add_strpool_attribute(ID(src), cell->get_strpool_attribute(ID(src)));
+ gate->setPort(ID::A, sig_a);
+ gate->setPort(ID::Y, sig_t);
last_output_cell = gate;
sig_a = sig_t;
}
@@ -157,7 +157,7 @@ void simplemap_reduce(RTLIL::Module *module, RTLIL::Cell *cell)
if (last_output_cell == NULL) {
module->connect(RTLIL::SigSig(sig_y, sig_a));
} else {
- last_output_cell->setPort("\\Y", sig_y);
+ last_output_cell->setPort(ID::Y, sig_y);
}
}
@@ -174,26 +174,26 @@ static void logic_reduce(RTLIL::Module *module, RTLIL::SigSpec &sig, RTLIL::Cell
continue;
}
- RTLIL::Cell *gate = module->addCell(NEW_ID, "$_OR_");
- gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src"));
- gate->setPort("\\A", sig[i]);
- gate->setPort("\\B", sig[i+1]);
- gate->setPort("\\Y", sig_t[i/2]);
+ RTLIL::Cell *gate = module->addCell(NEW_ID, ID($_OR_));
+ gate->add_strpool_attribute(ID(src), cell->get_strpool_attribute(ID(src)));
+ gate->setPort(ID::A, sig[i]);
+ gate->setPort(ID::B, sig[i+1]);
+ gate->setPort(ID::Y, sig_t[i/2]);
}
sig = sig_t;
}
if (sig.size() == 0)
- sig = RTLIL::SigSpec(0, 1);
+ sig = State::S0;
}
void simplemap_lognot(RTLIL::Module *module, RTLIL::Cell *cell)
{
- RTLIL::SigSpec sig_a = cell->getPort("\\A");
+ RTLIL::SigSpec sig_a = cell->getPort(ID::A);
logic_reduce(module, sig_a, cell);
- RTLIL::SigSpec sig_y = cell->getPort("\\Y");
+ RTLIL::SigSpec sig_y = cell->getPort(ID::Y);
if (sig_y.size() == 0)
return;
@@ -203,21 +203,21 @@ void simplemap_lognot(RTLIL::Module *module, RTLIL::Cell *cell)
sig_y = sig_y.extract(0, 1);
}
- RTLIL::Cell *gate = module->addCell(NEW_ID, "$_NOT_");
- gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src"));
- gate->setPort("\\A", sig_a);
- gate->setPort("\\Y", sig_y);
+ RTLIL::Cell *gate = module->addCell(NEW_ID, ID($_NOT_));
+ gate->add_strpool_attribute(ID(src), cell->get_strpool_attribute(ID(src)));
+ gate->setPort(ID::A, sig_a);
+ gate->setPort(ID::Y, sig_y);
}
void simplemap_logbin(RTLIL::Module *module, RTLIL::Cell *cell)
{
- RTLIL::SigSpec sig_a = cell->getPort("\\A");
+ RTLIL::SigSpec sig_a = cell->getPort(ID::A);
logic_reduce(module, sig_a, cell);
- RTLIL::SigSpec sig_b = cell->getPort("\\B");
+ RTLIL::SigSpec sig_b = cell->getPort(ID::B);
logic_reduce(module, sig_b, cell);
- RTLIL::SigSpec sig_y = cell->getPort("\\Y");
+ RTLIL::SigSpec sig_y = cell->getPort(ID::Y);
if (sig_y.size() == 0)
return;
@@ -227,41 +227,41 @@ void simplemap_logbin(RTLIL::Module *module, RTLIL::Cell *cell)
sig_y = sig_y.extract(0, 1);
}
- std::string gate_type;
- if (cell->type == "$logic_and") gate_type = "$_AND_";
- if (cell->type == "$logic_or") gate_type = "$_OR_";
+ IdString gate_type;
+ if (cell->type == ID($logic_and)) gate_type = ID($_AND_);
+ if (cell->type == ID($logic_or)) gate_type = ID($_OR_);
log_assert(!gate_type.empty());
RTLIL::Cell *gate = module->addCell(NEW_ID, gate_type);
- gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src"));
- gate->setPort("\\A", sig_a);
- gate->setPort("\\B", sig_b);
- gate->setPort("\\Y", sig_y);
+ gate->add_strpool_attribute(ID(src), cell->get_strpool_attribute(ID(src)));
+ gate->setPort(ID::A, sig_a);
+ gate->setPort(ID::B, sig_b);
+ gate->setPort(ID::Y, sig_y);
}
void simplemap_eqne(RTLIL::Module *module, RTLIL::Cell *cell)
{
- RTLIL::SigSpec sig_a = cell->getPort("\\A");
- RTLIL::SigSpec sig_b = cell->getPort("\\B");
- RTLIL::SigSpec sig_y = cell->getPort("\\Y");
- bool is_signed = cell->parameters.at("\\A_SIGNED").as_bool();
- bool is_ne = cell->type == "$ne" || cell->type == "$nex";
+ RTLIL::SigSpec sig_a = cell->getPort(ID::A);
+ RTLIL::SigSpec sig_b = cell->getPort(ID::B);
+ RTLIL::SigSpec sig_y = cell->getPort(ID::Y);
+ bool is_signed = cell->parameters.at(ID(A_SIGNED)).as_bool();
+ bool is_ne = cell->type.in(ID($ne), ID($nex));
RTLIL::SigSpec xor_out = module->addWire(NEW_ID, max(GetSize(sig_a), GetSize(sig_b)));
RTLIL::Cell *xor_cell = module->addXor(NEW_ID, sig_a, sig_b, xor_out, is_signed);
- xor_cell->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src"));
+ xor_cell->add_strpool_attribute(ID(src), cell->get_strpool_attribute(ID(src)));
simplemap_bitop(module, xor_cell);
module->remove(xor_cell);
RTLIL::SigSpec reduce_out = is_ne ? sig_y : module->addWire(NEW_ID);
RTLIL::Cell *reduce_cell = module->addReduceOr(NEW_ID, xor_out, reduce_out);
- reduce_cell->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src"));
+ reduce_cell->add_strpool_attribute(ID(src), cell->get_strpool_attribute(ID(src)));
simplemap_reduce(module, reduce_cell);
module->remove(reduce_cell);
if (!is_ne) {
RTLIL::Cell *not_cell = module->addLogicNot(NEW_ID, reduce_out, sig_y);
- not_cell->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src"));
+ not_cell->add_strpool_attribute(ID(src), cell->get_strpool_attribute(ID(src)));
simplemap_lognot(module, not_cell);
module->remove(not_cell);
}
@@ -269,65 +269,65 @@ void simplemap_eqne(RTLIL::Module *module, RTLIL::Cell *cell)
void simplemap_mux(RTLIL::Module *module, RTLIL::Cell *cell)
{
- RTLIL::SigSpec sig_a = cell->getPort("\\A");
- RTLIL::SigSpec sig_b = cell->getPort("\\B");
- RTLIL::SigSpec sig_y = cell->getPort("\\Y");
+ RTLIL::SigSpec sig_a = cell->getPort(ID::A);
+ RTLIL::SigSpec sig_b = cell->getPort(ID::B);
+ RTLIL::SigSpec sig_y = cell->getPort(ID::Y);
for (int i = 0; i < GetSize(sig_y); i++) {
- RTLIL::Cell *gate = module->addCell(NEW_ID, "$_MUX_");
- gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src"));
- gate->setPort("\\A", sig_a[i]);
- gate->setPort("\\B", sig_b[i]);
- gate->setPort("\\S", cell->getPort("\\S"));
- gate->setPort("\\Y", sig_y[i]);
+ RTLIL::Cell *gate = module->addCell(NEW_ID, ID($_MUX_));
+ gate->add_strpool_attribute(ID(src), cell->get_strpool_attribute(ID(src)));
+ gate->setPort(ID::A, sig_a[i]);
+ gate->setPort(ID::B, sig_b[i]);
+ gate->setPort(ID(S), cell->getPort(ID(S)));
+ gate->setPort(ID::Y, sig_y[i]);
}
}
void simplemap_tribuf(RTLIL::Module *module, RTLIL::Cell *cell)
{
- RTLIL::SigSpec sig_a = cell->getPort("\\A");
- RTLIL::SigSpec sig_e = cell->getPort("\\EN");
- RTLIL::SigSpec sig_y = cell->getPort("\\Y");
+ RTLIL::SigSpec sig_a = cell->getPort(ID::A);
+ RTLIL::SigSpec sig_e = cell->getPort(ID(EN));
+ RTLIL::SigSpec sig_y = cell->getPort(ID::Y);
for (int i = 0; i < GetSize(sig_y); i++) {
- RTLIL::Cell *gate = module->addCell(NEW_ID, "$_TBUF_");
- gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src"));
- gate->setPort("\\A", sig_a[i]);
- gate->setPort("\\E", sig_e);
- gate->setPort("\\Y", sig_y[i]);
+ RTLIL::Cell *gate = module->addCell(NEW_ID, ID($_TBUF_));
+ gate->add_strpool_attribute(ID(src), cell->get_strpool_attribute(ID(src)));
+ gate->setPort(ID::A, sig_a[i]);
+ gate->setPort(ID(E), sig_e);
+ gate->setPort(ID::Y, sig_y[i]);
}
}
void simplemap_lut(RTLIL::Module *module, RTLIL::Cell *cell)
{
- SigSpec lut_ctrl = cell->getPort("\\A");
- SigSpec lut_data = cell->getParam("\\LUT");
- lut_data.extend_u0(1 << cell->getParam("\\WIDTH").as_int());
+ SigSpec lut_ctrl = cell->getPort(ID::A);
+ SigSpec lut_data = cell->getParam(ID(LUT));
+ lut_data.extend_u0(1 << cell->getParam(ID(WIDTH)).as_int());
for (int idx = 0; GetSize(lut_data) > 1; idx++) {
SigSpec sig_s = lut_ctrl[idx];
SigSpec new_lut_data = module->addWire(NEW_ID, GetSize(lut_data)/2);
for (int i = 0; i < GetSize(lut_data); i += 2) {
- RTLIL::Cell *gate = module->addCell(NEW_ID, "$_MUX_");
- gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src"));
- gate->setPort("\\A", lut_data[i]);
- gate->setPort("\\B", lut_data[i+1]);
- gate->setPort("\\S", lut_ctrl[idx]);
- gate->setPort("\\Y", new_lut_data[i/2]);
+ RTLIL::Cell *gate = module->addCell(NEW_ID, ID($_MUX_));
+ gate->add_strpool_attribute(ID(src), cell->get_strpool_attribute(ID(src)));
+ gate->setPort(ID::A, lut_data[i]);
+ gate->setPort(ID::B, lut_data[i+1]);
+ gate->setPort(ID(S), lut_ctrl[idx]);
+ gate->setPort(ID::Y, new_lut_data[i/2]);
}
lut_data = new_lut_data;
}
- module->connect(cell->getPort("\\Y"), lut_data);
+ module->connect(cell->getPort(ID::Y), lut_data);
}
void simplemap_sop(RTLIL::Module *module, RTLIL::Cell *cell)
{
- SigSpec ctrl = cell->getPort("\\A");
- SigSpec table = cell->getParam("\\TABLE");
+ SigSpec ctrl = cell->getPort(ID::A);
+ SigSpec table = cell->getParam(ID(TABLE));
- int width = cell->getParam("\\WIDTH").as_int();
- int depth = cell->getParam("\\DEPTH").as_int();
+ int width = cell->getParam(ID(WIDTH)).as_int();
+ int depth = cell->getParam(ID(DEPTH)).as_int();
table.extend_u0(2 * width * depth);
SigSpec products;
@@ -348,213 +348,213 @@ void simplemap_sop(RTLIL::Module *module, RTLIL::Cell *cell)
products.append(GetSize(in) > 0 ? module->Eq(NEW_ID, in, pat) : State::S1);
}
- module->connect(cell->getPort("\\Y"), module->ReduceOr(NEW_ID, products));
+ module->connect(cell->getPort(ID::Y), module->ReduceOr(NEW_ID, products));
}
void simplemap_slice(RTLIL::Module *module, RTLIL::Cell *cell)
{
- int offset = cell->parameters.at("\\OFFSET").as_int();
- RTLIL::SigSpec sig_a = cell->getPort("\\A");
- RTLIL::SigSpec sig_y = cell->getPort("\\Y");
+ int offset = cell->parameters.at(ID(OFFSET)).as_int();
+ RTLIL::SigSpec sig_a = cell->getPort(ID::A);
+ RTLIL::SigSpec sig_y = cell->getPort(ID::Y);
module->connect(RTLIL::SigSig(sig_y, sig_a.extract(offset, sig_y.size())));
}
void simplemap_concat(RTLIL::Module *module, RTLIL::Cell *cell)
{
- RTLIL::SigSpec sig_ab = cell->getPort("\\A");
- sig_ab.append(cell->getPort("\\B"));
- RTLIL::SigSpec sig_y = cell->getPort("\\Y");
+ RTLIL::SigSpec sig_ab = cell->getPort(ID::A);
+ sig_ab.append(cell->getPort(ID::B));
+ RTLIL::SigSpec sig_y = cell->getPort(ID::Y);
module->connect(RTLIL::SigSig(sig_y, sig_ab));
}
void simplemap_sr(RTLIL::Module *module, RTLIL::Cell *cell)
{
- int width = cell->parameters.at("\\WIDTH").as_int();
- char set_pol = cell->parameters.at("\\SET_POLARITY").as_bool() ? 'P' : 'N';
- char clr_pol = cell->parameters.at("\\CLR_POLARITY").as_bool() ? 'P' : 'N';
+ int width = cell->parameters.at(ID(WIDTH)).as_int();
+ char set_pol = cell->parameters.at(ID(SET_POLARITY)).as_bool() ? 'P' : 'N';
+ char clr_pol = cell->parameters.at(ID(CLR_POLARITY)).as_bool() ? 'P' : 'N';
- RTLIL::SigSpec sig_s = cell->getPort("\\SET");
- RTLIL::SigSpec sig_r = cell->getPort("\\CLR");
- RTLIL::SigSpec sig_q = cell->getPort("\\Q");
+ RTLIL::SigSpec sig_s = cell->getPort(ID(SET));
+ RTLIL::SigSpec sig_r = cell->getPort(ID(CLR));
+ RTLIL::SigSpec sig_q = cell->getPort(ID(Q));
std::string gate_type = stringf("$_SR_%c%c_", set_pol, clr_pol);
for (int i = 0; i < width; i++) {
RTLIL::Cell *gate = module->addCell(NEW_ID, gate_type);
- gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src"));
- gate->setPort("\\S", sig_s[i]);
- gate->setPort("\\R", sig_r[i]);
- gate->setPort("\\Q", sig_q[i]);
+ gate->add_strpool_attribute(ID(src), cell->get_strpool_attribute(ID(src)));
+ gate->setPort(ID(S), sig_s[i]);
+ gate->setPort(ID(R), sig_r[i]);
+ gate->setPort(ID(Q), sig_q[i]);
}
}
void simplemap_ff(RTLIL::Module *module, RTLIL::Cell *cell)
{
- int width = cell->parameters.at("\\WIDTH").as_int();
+ int width = cell->parameters.at(ID(WIDTH)).as_int();
- RTLIL::SigSpec sig_d = cell->getPort("\\D");
- RTLIL::SigSpec sig_q = cell->getPort("\\Q");
+ RTLIL::SigSpec sig_d = cell->getPort(ID(D));
+ RTLIL::SigSpec sig_q = cell->getPort(ID(Q));
- std::string gate_type = "$_FF_";
+ IdString gate_type = ID($_FF_);
for (int i = 0; i < width; i++) {
RTLIL::Cell *gate = module->addCell(NEW_ID, gate_type);
- gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src"));
- gate->setPort("\\D", sig_d[i]);
- gate->setPort("\\Q", sig_q[i]);
+ gate->add_strpool_attribute(ID(src), cell->get_strpool_attribute(ID(src)));
+ gate->setPort(ID(D), sig_d[i]);
+ gate->setPort(ID(Q), sig_q[i]);
}
}
void simplemap_dff(RTLIL::Module *module, RTLIL::Cell *cell)
{
- int width = cell->parameters.at("\\WIDTH").as_int();
- char clk_pol = cell->parameters.at("\\CLK_POLARITY").as_bool() ? 'P' : 'N';
+ int width = cell->parameters.at(ID(WIDTH)).as_int();
+ char clk_pol = cell->parameters.at(ID(CLK_POLARITY)).as_bool() ? 'P' : 'N';
- RTLIL::SigSpec sig_clk = cell->getPort("\\CLK");
- RTLIL::SigSpec sig_d = cell->getPort("\\D");
- RTLIL::SigSpec sig_q = cell->getPort("\\Q");
+ RTLIL::SigSpec sig_clk = cell->getPort(ID(CLK));
+ RTLIL::SigSpec sig_d = cell->getPort(ID(D));
+ RTLIL::SigSpec sig_q = cell->getPort(ID(Q));
- std::string gate_type = stringf("$_DFF_%c_", clk_pol);
+ IdString gate_type = stringf("$_DFF_%c_", clk_pol);
for (int i = 0; i < width; i++) {
RTLIL::Cell *gate = module->addCell(NEW_ID, gate_type);
- gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src"));
- gate->setPort("\\C", sig_clk);
- gate->setPort("\\D", sig_d[i]);
- gate->setPort("\\Q", sig_q[i]);
+ gate->add_strpool_attribute(ID(src), cell->get_strpool_attribute(ID(src)));
+ gate->setPort(ID(C), sig_clk);
+ gate->setPort(ID(D), sig_d[i]);
+ gate->setPort(ID(Q), sig_q[i]);
}
}
void simplemap_dffe(RTLIL::Module *module, RTLIL::Cell *cell)
{
- int width = cell->parameters.at("\\WIDTH").as_int();
- char clk_pol = cell->parameters.at("\\CLK_POLARITY").as_bool() ? 'P' : 'N';
- char en_pol = cell->parameters.at("\\EN_POLARITY").as_bool() ? 'P' : 'N';
+ int width = cell->parameters.at(ID(WIDTH)).as_int();
+ char clk_pol = cell->parameters.at(ID(CLK_POLARITY)).as_bool() ? 'P' : 'N';
+ char en_pol = cell->parameters.at(ID(EN_POLARITY)).as_bool() ? 'P' : 'N';
- RTLIL::SigSpec sig_clk = cell->getPort("\\CLK");
- RTLIL::SigSpec sig_en = cell->getPort("\\EN");
- RTLIL::SigSpec sig_d = cell->getPort("\\D");
- RTLIL::SigSpec sig_q = cell->getPort("\\Q");
+ RTLIL::SigSpec sig_clk = cell->getPort(ID(CLK));
+ RTLIL::SigSpec sig_en = cell->getPort(ID(EN));
+ RTLIL::SigSpec sig_d = cell->getPort(ID(D));
+ RTLIL::SigSpec sig_q = cell->getPort(ID(Q));
- std::string gate_type = stringf("$_DFFE_%c%c_", clk_pol, en_pol);
+ IdString gate_type = stringf("$_DFFE_%c%c_", clk_pol, en_pol);
for (int i = 0; i < width; i++) {
RTLIL::Cell *gate = module->addCell(NEW_ID, gate_type);
- gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src"));
- gate->setPort("\\C", sig_clk);
- gate->setPort("\\E", sig_en);
- gate->setPort("\\D", sig_d[i]);
- gate->setPort("\\Q", sig_q[i]);
+ gate->add_strpool_attribute(ID(src), cell->get_strpool_attribute(ID(src)));
+ gate->setPort(ID(C), sig_clk);
+ gate->setPort(ID(E), sig_en);
+ gate->setPort(ID(D), sig_d[i]);
+ gate->setPort(ID(Q), sig_q[i]);
}
}
void simplemap_dffsr(RTLIL::Module *module, RTLIL::Cell *cell)
{
- int width = cell->parameters.at("\\WIDTH").as_int();
- char clk_pol = cell->parameters.at("\\CLK_POLARITY").as_bool() ? 'P' : 'N';
- char set_pol = cell->parameters.at("\\SET_POLARITY").as_bool() ? 'P' : 'N';
- char clr_pol = cell->parameters.at("\\CLR_POLARITY").as_bool() ? 'P' : 'N';
+ int width = cell->parameters.at(ID(WIDTH)).as_int();
+ char clk_pol = cell->parameters.at(ID(CLK_POLARITY)).as_bool() ? 'P' : 'N';
+ char set_pol = cell->parameters.at(ID(SET_POLARITY)).as_bool() ? 'P' : 'N';
+ char clr_pol = cell->parameters.at(ID(CLR_POLARITY)).as_bool() ? 'P' : 'N';
- RTLIL::SigSpec sig_clk = cell->getPort("\\CLK");
- RTLIL::SigSpec sig_s = cell->getPort("\\SET");
- RTLIL::SigSpec sig_r = cell->getPort("\\CLR");
- RTLIL::SigSpec sig_d = cell->getPort("\\D");
- RTLIL::SigSpec sig_q = cell->getPort("\\Q");
+ RTLIL::SigSpec sig_clk = cell->getPort(ID(CLK));
+ RTLIL::SigSpec sig_s = cell->getPort(ID(SET));
+ RTLIL::SigSpec sig_r = cell->getPort(ID(CLR));
+ RTLIL::SigSpec sig_d = cell->getPort(ID(D));
+ RTLIL::SigSpec sig_q = cell->getPort(ID(Q));
- std::string gate_type = stringf("$_DFFSR_%c%c%c_", clk_pol, set_pol, clr_pol);
+ IdString gate_type = stringf("$_DFFSR_%c%c%c_", clk_pol, set_pol, clr_pol);
for (int i = 0; i < width; i++) {
RTLIL::Cell *gate = module->addCell(NEW_ID, gate_type);
- gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src"));
- gate->setPort("\\C", sig_clk);
- gate->setPort("\\S", sig_s[i]);
- gate->setPort("\\R", sig_r[i]);
- gate->setPort("\\D", sig_d[i]);
- gate->setPort("\\Q", sig_q[i]);
+ gate->add_strpool_attribute(ID(src), cell->get_strpool_attribute(ID(src)));
+ gate->setPort(ID(C), sig_clk);
+ gate->setPort(ID(S), sig_s[i]);
+ gate->setPort(ID(R), sig_r[i]);
+ gate->setPort(ID(D), sig_d[i]);
+ gate->setPort(ID(Q), sig_q[i]);
}
}
void simplemap_adff(RTLIL::Module *module, RTLIL::Cell *cell)
{
- int width = cell->parameters.at("\\WIDTH").as_int();
- char clk_pol = cell->parameters.at("\\CLK_POLARITY").as_bool() ? 'P' : 'N';
- char rst_pol = cell->parameters.at("\\ARST_POLARITY").as_bool() ? 'P' : 'N';
+ int width = cell->parameters.at(ID(WIDTH)).as_int();
+ char clk_pol = cell->parameters.at(ID(CLK_POLARITY)).as_bool() ? 'P' : 'N';
+ char rst_pol = cell->parameters.at(ID(ARST_POLARITY)).as_bool() ? 'P' : 'N';
- std::vector<RTLIL::State> rst_val = cell->parameters.at("\\ARST_VALUE").bits;
+ std::vector<RTLIL::State> rst_val = cell->parameters.at(ID(ARST_VALUE)).bits;
while (int(rst_val.size()) < width)
rst_val.push_back(RTLIL::State::S0);
- RTLIL::SigSpec sig_clk = cell->getPort("\\CLK");
- RTLIL::SigSpec sig_rst = cell->getPort("\\ARST");
- RTLIL::SigSpec sig_d = cell->getPort("\\D");
- RTLIL::SigSpec sig_q = cell->getPort("\\Q");
+ RTLIL::SigSpec sig_clk = cell->getPort(ID(CLK));
+ RTLIL::SigSpec sig_rst = cell->getPort(ID(ARST));
+ RTLIL::SigSpec sig_d = cell->getPort(ID(D));
+ RTLIL::SigSpec sig_q = cell->getPort(ID(Q));
- std::string gate_type_0 = stringf("$_DFF_%c%c0_", clk_pol, rst_pol);
- std::string gate_type_1 = stringf("$_DFF_%c%c1_", clk_pol, rst_pol);
+ IdString gate_type_0 = stringf("$_DFF_%c%c0_", clk_pol, rst_pol);
+ IdString gate_type_1 = stringf("$_DFF_%c%c1_", clk_pol, rst_pol);
for (int i = 0; i < width; i++) {
RTLIL::Cell *gate = module->addCell(NEW_ID, rst_val.at(i) == RTLIL::State::S1 ? gate_type_1 : gate_type_0);
- gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src"));
- gate->setPort("\\C", sig_clk);
- gate->setPort("\\R", sig_rst);
- gate->setPort("\\D", sig_d[i]);
- gate->setPort("\\Q", sig_q[i]);
+ gate->add_strpool_attribute(ID(src), cell->get_strpool_attribute(ID(src)));
+ gate->setPort(ID(C), sig_clk);
+ gate->setPort(ID(R), sig_rst);
+ gate->setPort(ID(D), sig_d[i]);
+ gate->setPort(ID(Q), sig_q[i]);
}
}
void simplemap_dlatch(RTLIL::Module *module, RTLIL::Cell *cell)
{
- int width = cell->parameters.at("\\WIDTH").as_int();
- char en_pol = cell->parameters.at("\\EN_POLARITY").as_bool() ? 'P' : 'N';
+ int width = cell->parameters.at(ID(WIDTH)).as_int();
+ char en_pol = cell->parameters.at(ID(EN_POLARITY)).as_bool() ? 'P' : 'N';
- RTLIL::SigSpec sig_en = cell->getPort("\\EN");
- RTLIL::SigSpec sig_d = cell->getPort("\\D");
- RTLIL::SigSpec sig_q = cell->getPort("\\Q");
+ RTLIL::SigSpec sig_en = cell->getPort(ID(EN));
+ RTLIL::SigSpec sig_d = cell->getPort(ID(D));
+ RTLIL::SigSpec sig_q = cell->getPort(ID(Q));
- std::string gate_type = stringf("$_DLATCH_%c_", en_pol);
+ IdString gate_type = stringf("$_DLATCH_%c_", en_pol);
for (int i = 0; i < width; i++) {
RTLIL::Cell *gate = module->addCell(NEW_ID, gate_type);
- gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src"));
- gate->setPort("\\E", sig_en);
- gate->setPort("\\D", sig_d[i]);
- gate->setPort("\\Q", sig_q[i]);
+ gate->add_strpool_attribute(ID(src), cell->get_strpool_attribute(ID(src)));
+ gate->setPort(ID(E), sig_en);
+ gate->setPort(ID(D), sig_d[i]);
+ gate->setPort(ID(Q), sig_q[i]);
}
}
void simplemap_get_mappers(std::map<RTLIL::IdString, void(*)(RTLIL::Module*, RTLIL::Cell*)> &mappers)
{
- mappers["$not"] = simplemap_not;
- mappers["$pos"] = simplemap_pos;
- mappers["$and"] = simplemap_bitop;
- mappers["$or"] = simplemap_bitop;
- mappers["$xor"] = simplemap_bitop;
- mappers["$xnor"] = simplemap_bitop;
- mappers["$reduce_and"] = simplemap_reduce;
- mappers["$reduce_or"] = simplemap_reduce;
- mappers["$reduce_xor"] = simplemap_reduce;
- mappers["$reduce_xnor"] = simplemap_reduce;
- mappers["$reduce_bool"] = simplemap_reduce;
- mappers["$logic_not"] = simplemap_lognot;
- mappers["$logic_and"] = simplemap_logbin;
- mappers["$logic_or"] = simplemap_logbin;
- mappers["$eq"] = simplemap_eqne;
- mappers["$eqx"] = simplemap_eqne;
- mappers["$ne"] = simplemap_eqne;
- mappers["$nex"] = simplemap_eqne;
- mappers["$mux"] = simplemap_mux;
- mappers["$tribuf"] = simplemap_tribuf;
- mappers["$lut"] = simplemap_lut;
- mappers["$sop"] = simplemap_sop;
- mappers["$slice"] = simplemap_slice;
- mappers["$concat"] = simplemap_concat;
- mappers["$sr"] = simplemap_sr;
- mappers["$ff"] = simplemap_ff;
- mappers["$dff"] = simplemap_dff;
- mappers["$dffe"] = simplemap_dffe;
- mappers["$dffsr"] = simplemap_dffsr;
- mappers["$adff"] = simplemap_adff;
- mappers["$dlatch"] = simplemap_dlatch;
+ mappers[ID($not)] = simplemap_not;
+ mappers[ID($pos)] = simplemap_pos;
+ mappers[ID($and)] = simplemap_bitop;
+ mappers[ID($or)] = simplemap_bitop;
+ mappers[ID($xor)] = simplemap_bitop;
+ mappers[ID($xnor)] = simplemap_bitop;
+ mappers[ID($reduce_and)] = simplemap_reduce;
+ mappers[ID($reduce_or)] = simplemap_reduce;
+ mappers[ID($reduce_xor)] = simplemap_reduce;
+ mappers[ID($reduce_xnor)] = simplemap_reduce;
+ mappers[ID($reduce_bool)] = simplemap_reduce;
+ mappers[ID($logic_not)] = simplemap_lognot;
+ mappers[ID($logic_and)] = simplemap_logbin;
+ mappers[ID($logic_or)] = simplemap_logbin;
+ mappers[ID($eq)] = simplemap_eqne;
+ mappers[ID($eqx)] = simplemap_eqne;
+ mappers[ID($ne)] = simplemap_eqne;
+ mappers[ID($nex)] = simplemap_eqne;
+ mappers[ID($mux)] = simplemap_mux;
+ mappers[ID($tribuf)] = simplemap_tribuf;
+ mappers[ID($lut)] = simplemap_lut;
+ mappers[ID($sop)] = simplemap_sop;
+ mappers[ID($slice)] = simplemap_slice;
+ mappers[ID($concat)] = simplemap_concat;
+ mappers[ID($sr)] = simplemap_sr;
+ mappers[ID($ff)] = simplemap_ff;
+ mappers[ID($dff)] = simplemap_dff;
+ mappers[ID($dffe)] = simplemap_dffe;
+ mappers[ID($dffsr)] = simplemap_dffsr;
+ mappers[ID($adff)] = simplemap_adff;
+ mappers[ID($dlatch)] = simplemap_dlatch;
}
void simplemap(RTLIL::Module *module, RTLIL::Cell *cell)
@@ -575,7 +575,7 @@ PRIVATE_NAMESPACE_BEGIN
struct SimplemapPass : public Pass {
SimplemapPass() : Pass("simplemap", "mapping simple coarse-grain cells") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -590,7 +590,7 @@ struct SimplemapPass : public Pass {
log(" $sr, $ff, $dff, $dffsr, $adff, $dlatch\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
log_header(design, "Executing SIMPLEMAP pass (map simple cells to gate primitives).\n");
extra_args(args, 1, design);
@@ -599,7 +599,7 @@ struct SimplemapPass : public Pass {
simplemap_get_mappers(mappers);
for (auto mod : design->modules()) {
- if (!design->selected(mod))
+ if (!design->selected(mod) || mod->get_blackbox_attribute())
continue;
std::vector<RTLIL::Cell*> cells = mod->cells();
for (auto cell : cells) {
diff --git a/passes/techmap/techmap.cc b/passes/techmap/techmap.cc
index d9e81e808..0c57733d4 100644
--- a/passes/techmap/techmap.cc
+++ b/passes/techmap/techmap.cc
@@ -39,20 +39,20 @@ YOSYS_NAMESPACE_END
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
-void apply_prefix(std::string prefix, std::string &id)
+void apply_prefix(IdString prefix, IdString &id)
{
if (id[0] == '\\')
- id = prefix + "." + id.substr(1);
+ id = stringf("%s.%s", prefix.c_str(), id.c_str()+1);
else
- id = "$techmap" + prefix + "." + id;
+ id = stringf("$techmap%s.%s", prefix.c_str(), id.c_str());
}
-void apply_prefix(std::string prefix, RTLIL::SigSpec &sig, RTLIL::Module *module)
+void apply_prefix(IdString prefix, RTLIL::SigSpec &sig, RTLIL::Module *module)
{
vector<SigChunk> chunks = sig;
for (auto &chunk : chunks)
if (chunk.wire != NULL) {
- std::string wire_name = chunk.wire->name.str();
+ IdString wire_name = chunk.wire->name;
apply_prefix(prefix, wire_name);
log_assert(module->wires_.count(wire_name) > 0);
chunk.wire = module->wires_[wire_name];
@@ -72,6 +72,8 @@ struct TechmapWorker
pool<IdString> flatten_done_list;
pool<Cell*> flatten_keep_list;
+ pool<string> log_msg_cache;
+
struct TechmapWireData {
RTLIL::Wire *wire;
RTLIL::SigSpec value;
@@ -84,6 +86,7 @@ struct TechmapWorker
bool flatten_mode;
bool recursive_mode;
bool autoproc_mode;
+ bool ignore_wb;
TechmapWorker()
{
@@ -92,6 +95,7 @@ struct TechmapWorker
flatten_mode = false;
recursive_mode = false;
autoproc_mode = false;
+ ignore_wb = false;
}
std::string constmap_tpl_name(SigMap &sigmap, RTLIL::Module *tpl, RTLIL::Cell *cell, bool verbose)
@@ -141,8 +145,8 @@ struct TechmapWorker
record.wire = it.second;
record.value = it.second;
result[p].push_back(record);
- it.second->attributes["\\keep"] = RTLIL::Const(1);
- it.second->attributes["\\_techmap_special_"] = RTLIL::Const(1);
+ it.second->attributes[ID::keep] = RTLIL::Const(1);
+ it.second->attributes[ID(_techmap_special_)] = RTLIL::Const(1);
}
}
@@ -171,54 +175,94 @@ struct TechmapWorker
}
std::string orig_cell_name;
- pool<string> extra_src_attrs;
+ pool<string> extra_src_attrs = cell->get_strpool_attribute(ID(src));
- if (!flatten_mode)
- {
+ if (!flatten_mode) {
for (auto &it : tpl->cells_)
- if (it.first == "\\_TECHMAP_REPLACE_") {
+ if (it.first == ID(_TECHMAP_REPLACE_)) {
orig_cell_name = cell->name.str();
module->rename(cell, stringf("$techmap%d", autoidx++) + cell->name.str());
break;
}
-
- extra_src_attrs = cell->get_strpool_attribute("\\src");
}
dict<IdString, IdString> memory_renames;
for (auto &it : tpl->memories) {
- std::string m_name = it.first.str();
- apply_prefix(cell->name.str(), m_name);
+ IdString m_name = it.first;
+ apply_prefix(cell->name, m_name);
RTLIL::Memory *m = new RTLIL::Memory;
m->name = m_name;
m->width = it.second->width;
m->start_offset = it.second->start_offset;
m->size = it.second->size;
m->attributes = it.second->attributes;
- if (m->attributes.count("\\src"))
- m->add_strpool_attribute("\\src", extra_src_attrs);
+ if (m->attributes.count(ID(src)))
+ m->add_strpool_attribute(ID(src), extra_src_attrs);
module->memories[m->name] = m;
memory_renames[it.first] = m->name;
design->select(module, m);
}
std::map<RTLIL::IdString, RTLIL::IdString> positional_ports;
+ dict<Wire*, IdString> temp_renamed_wires;
+ pool<SigBit> autopurge_tpl_bits;
- for (auto &it : tpl->wires_) {
+ for (auto &it : tpl->wires_)
+ {
if (it.second->port_id > 0)
- positional_ports[stringf("$%d", it.second->port_id)] = it.first;
- std::string w_name = it.second->name.str();
- apply_prefix(cell->name.str(), w_name);
- RTLIL::Wire *w = module->addWire(w_name, it.second);
- w->port_input = false;
- w->port_output = false;
- w->port_id = 0;
- if (it.second->get_bool_attribute("\\_techmap_special_"))
- w->attributes.clear();
- if (w->attributes.count("\\src"))
- w->add_strpool_attribute("\\src", extra_src_attrs);
+ {
+ IdString posportname = stringf("$%d", it.second->port_id);
+ positional_ports[posportname] = it.first;
+
+ if (!flatten_mode && it.second->get_bool_attribute(ID(techmap_autopurge)) &&
+ (!cell->hasPort(it.second->name) || !GetSize(cell->getPort(it.second->name))) &&
+ (!cell->hasPort(posportname) || !GetSize(cell->getPort(posportname))))
+ {
+ if (sigmaps.count(tpl) == 0)
+ sigmaps[tpl].set(tpl);
+
+ for (auto bit : sigmaps.at(tpl)(it.second))
+ if (bit.wire != nullptr)
+ autopurge_tpl_bits.insert(bit);
+ }
+ }
+ IdString w_name = it.second->name;
+ apply_prefix(cell->name, w_name);
+ RTLIL::Wire *w = module->wire(w_name);
+ if (w != nullptr) {
+ if (!flatten_mode || !w->get_bool_attribute(ID(hierconn))) {
+ temp_renamed_wires[w] = w->name;
+ module->rename(w, NEW_ID);
+ w = nullptr;
+ } else {
+ w->attributes.erase(ID(hierconn));
+ if (GetSize(w) < GetSize(it.second)) {
+ log_warning("Widening signal %s.%s to match size of %s.%s (via %s.%s).\n", log_id(module), log_id(w),
+ log_id(tpl), log_id(it.second), log_id(module), log_id(cell));
+ w->width = GetSize(it.second);
+ }
+ }
+ }
+ if (w == nullptr) {
+ w = module->addWire(w_name, it.second);
+ w->port_input = false;
+ w->port_output = false;
+ w->port_id = 0;
+ if (!flatten_mode)
+ w->attributes.erase(ID(techmap_autopurge));
+ if (it.second->get_bool_attribute(ID(_techmap_special_)))
+ w->attributes.clear();
+ if (w->attributes.count(ID(src)))
+ w->add_strpool_attribute(ID(src), extra_src_attrs);
+ }
design->select(module, w);
+
+ if (it.second->name.begins_with("\\_TECHMAP_REPLACE_.")) {
+ IdString replace_name = stringf("%s%s", orig_cell_name.c_str(), it.second->name.c_str() + strlen("\\_TECHMAP_REPLACE_"));
+ Wire *replace_w = module->addWire(replace_name, it.second);
+ module->connect(replace_w, w);
+ }
}
SigMap tpl_sigmap(tpl);
@@ -242,7 +286,7 @@ struct TechmapWorker
if (positional_ports.count(portname) > 0)
portname = positional_ports.at(portname);
if (tpl->wires_.count(portname) == 0 || tpl->wires_.at(portname)->port_id == 0) {
- if (portname.substr(0, 1) == "$")
+ if (portname.begins_with("$"))
log_error("Can't map port `%s' of cell `%s' to template `%s'!\n", portname.c_str(), cell->name.c_str(), tpl->name.c_str());
continue;
}
@@ -256,18 +300,18 @@ struct TechmapWorker
if (w->port_output && !w->port_input) {
c.first = it.second;
c.second = RTLIL::SigSpec(w);
- apply_prefix(cell->name.str(), c.second, module);
+ apply_prefix(cell->name, c.second, module);
extra_connect.first = c.second;
extra_connect.second = c.first;
} else if (!w->port_output && w->port_input) {
c.first = RTLIL::SigSpec(w);
c.second = it.second;
- apply_prefix(cell->name.str(), c.first, module);
+ apply_prefix(cell->name, c.first, module);
extra_connect.first = c.first;
extra_connect.second = c.second;
} else {
SigSpec sig_tpl = w, sig_tpl_pf = w, sig_mod = it.second;
- apply_prefix(cell->name.str(), sig_tpl_pf, module);
+ apply_prefix(cell->name, sig_tpl_pf, module);
for (int i = 0; i < GetSize(sig_tpl) && i < GetSize(sig_mod); i++) {
if (tpl_written_bits.count(tpl_sigmap(sig_tpl[i]))) {
c.first.append(sig_mod[i]);
@@ -319,8 +363,14 @@ struct TechmapWorker
}
for (auto &attr : w->attributes) {
- if (attr.first == "\\src")
+ if (attr.first == ID(src))
continue;
+ auto lhs = GetSize(extra_connect.first);
+ auto rhs = GetSize(extra_connect.second);
+ if (lhs > rhs)
+ extra_connect.first.remove(rhs, lhs-rhs);
+ else if (rhs > lhs)
+ extra_connect.second.remove(lhs, rhs-lhs);
module->connect(extra_connect);
break;
}
@@ -329,41 +379,61 @@ struct TechmapWorker
for (auto &it : tpl->cells_)
{
- std::string c_name = it.second->name.str();
- bool techmap_replace_cell = (!flatten_mode) && (c_name == "\\_TECHMAP_REPLACE_");
+ IdString c_name = it.second->name.str();
+ bool techmap_replace_cell = (!flatten_mode) && (c_name == ID(_TECHMAP_REPLACE_));
if (techmap_replace_cell)
c_name = orig_cell_name;
+ else if (it.second->name.begins_with("\\_TECHMAP_REPLACE_."))
+ c_name = stringf("%s%s", orig_cell_name.c_str(), c_name.c_str() + strlen("\\_TECHMAP_REPLACE_"));
else
- apply_prefix(cell->name.str(), c_name);
+ apply_prefix(cell->name, c_name);
RTLIL::Cell *c = module->addCell(c_name, it.second);
design->select(module, c);
- c->set_src_attribute(cell->get_src_attribute());
-
- if (!flatten_mode && c->type.substr(0, 2) == "\\$")
+ if (!flatten_mode && c->type.begins_with("\\$"))
c->type = c->type.substr(1);
- for (auto &it2 : c->connections_) {
- apply_prefix(cell->name.str(), it2.second, module);
- port_signal_map.apply(it2.second);
+ vector<IdString> autopurge_ports;
+
+ for (auto &it2 : c->connections_)
+ {
+ bool autopurge = false;
+ if (!autopurge_tpl_bits.empty()) {
+ autopurge = GetSize(it2.second) != 0;
+ for (auto &bit : sigmaps.at(tpl)(it2.second))
+ if (!autopurge_tpl_bits.count(bit)) {
+ autopurge = false;
+ break;
+ }
+ }
+
+ if (autopurge) {
+ autopurge_ports.push_back(it2.first);
+ } else {
+ apply_prefix(cell->name, it2.second, module);
+ port_signal_map.apply(it2.second);
+ }
}
- if (c->type == "$memrd" || c->type == "$memwr" || c->type == "$meminit") {
- IdString memid = c->getParam("\\MEMID").decode_string();
+ for (auto &it2 : autopurge_ports)
+ c->unsetPort(it2);
+
+ if (c->type.in(ID($memrd), ID($memwr), ID($meminit))) {
+ IdString memid = c->getParam(ID(MEMID)).decode_string();
log_assert(memory_renames.count(memid) != 0);
- c->setParam("\\MEMID", Const(memory_renames[memid].str()));
+ c->setParam(ID(MEMID), Const(memory_renames[memid].str()));
}
- if (c->type == "$mem") {
- string memid = c->getParam("\\MEMID").decode_string();
- apply_prefix(cell->name.str(), memid);
- c->setParam("\\MEMID", Const(memid));
+ if (c->type == ID($mem)) {
+ IdString memid = c->getParam(ID(MEMID)).decode_string();
+ apply_prefix(cell->name, memid);
+ c->setParam(ID(MEMID), Const(memid.c_str()));
}
- if (c->attributes.count("\\src"))
- c->add_strpool_attribute("\\src", extra_src_attrs);
+ if (c->attributes.count(ID(src)))
+ c->add_strpool_attribute(ID(src), extra_src_attrs);
if (techmap_replace_cell)
for (auto attr : cell->attributes)
@@ -381,6 +451,16 @@ struct TechmapWorker
}
module->remove(cell);
+
+ for (auto &it : temp_renamed_wires)
+ {
+ Wire *w = it.first;
+ IdString name = it.second;
+ IdString altname = module->uniquify(name);
+ Wire *other_w = module->wire(name);
+ module->rename(other_w, altname);
+ module->rename(w, name);
+ }
}
bool techmap_module(RTLIL::Design *design, RTLIL::Module *module, RTLIL::Design *map, std::set<RTLIL::Cell*> &handled_cells,
@@ -388,14 +468,27 @@ struct TechmapWorker
{
std::string mapmsg_prefix = in_recursion ? "Recursively mapping" : "Mapping";
- if (!design->selected(module))
+ if (!design->selected(module) || module->get_blackbox_attribute(ignore_wb))
return false;
bool log_continue = false;
bool did_something = false;
+ LogMakeDebugHdl mkdebug;
SigMap sigmap(module);
+ dict<SigBit, State> init_bits;
+ pool<SigBit> remove_init_bits;
+
+ for (auto wire : module->wires()) {
+ if (wire->attributes.count("\\init")) {
+ Const value = wire->attributes.at("\\init");
+ for (int i = 0; i < min(GetSize(value), GetSize(wire)); i++)
+ if (value[i] != State::Sx)
+ init_bits[sigmap(SigBit(wire, i))] = value[i];
+ }
+ }
+
TopoSort<RTLIL::Cell*, RTLIL::IdString::compare_ptr_by_name<RTLIL::Cell>> cells;
std::map<RTLIL::Cell*, std::set<RTLIL::SigBit>> cell_to_inbit;
std::map<RTLIL::SigBit, std::set<RTLIL::Cell*>> outbit_to_cell;
@@ -406,7 +499,7 @@ struct TechmapWorker
continue;
std::string cell_type = cell->type.str();
- if (in_recursion && cell_type.substr(0, 2) == "\\$")
+ if (in_recursion && cell->type.begins_with("\\$"))
cell_type = cell_type.substr(1);
if (celltypeMap.count(cell_type) == 0) {
@@ -416,9 +509,9 @@ struct TechmapWorker
}
if (flatten_mode) {
- bool keepit = cell->get_bool_attribute("\\keep_hierarchy");
+ bool keepit = cell->get_bool_attribute(ID(keep_hierarchy));
for (auto &tpl_name : celltypeMap.at(cell_type))
- if (map->modules_[tpl_name]->get_bool_attribute("\\keep_hierarchy"))
+ if (map->modules_[tpl_name]->get_bool_attribute(ID(keep_hierarchy)))
keepit = true;
if (keepit) {
if (!flatten_keep_list[cell]) {
@@ -468,7 +561,7 @@ struct TechmapWorker
std::string cell_type = cell->type.str();
- if (in_recursion && cell_type.substr(0, 2) == "\\$")
+ if (in_recursion && cell->type.begins_with("\\$"))
cell_type = cell_type.substr(1);
for (auto &tpl_name : celltypeMap.at(cell_type))
@@ -477,20 +570,20 @@ struct TechmapWorker
RTLIL::Module *tpl = map->modules_[tpl_name];
std::map<RTLIL::IdString, RTLIL::Const> parameters(cell->parameters.begin(), cell->parameters.end());
- if (tpl->get_bool_attribute("\\blackbox"))
+ if (tpl->get_blackbox_attribute(ignore_wb))
continue;
if (!flatten_mode)
{
std::string extmapper_name;
- if (tpl->get_bool_attribute("\\techmap_simplemap"))
+ if (tpl->get_bool_attribute(ID(techmap_simplemap)))
extmapper_name = "simplemap";
- if (tpl->get_bool_attribute("\\techmap_maccmap"))
+ if (tpl->get_bool_attribute(ID(techmap_maccmap)))
extmapper_name = "maccmap";
- if (tpl->attributes.count("\\techmap_wrap"))
+ if (tpl->attributes.count(ID(techmap_wrap)))
extmapper_name = "wrap";
if (!extmapper_name.empty())
@@ -505,7 +598,7 @@ struct TechmapWorker
m_name += stringf(":%s=%s", log_id(c.first), log_signal(c.second));
if (extmapper_name == "wrap")
- m_name += ":" + sha1(tpl->attributes.at("\\techmap_wrap").decode_string());
+ m_name += ":" + sha1(tpl->attributes.at(ID(techmap_wrap)).decode_string());
RTLIL::Design *extmapper_design = extern_mode && !in_recursion ? design : tpl->design;
RTLIL::Module *extmapper_module = extmapper_design->module(m_name);
@@ -520,7 +613,7 @@ struct TechmapWorker
int port_counter = 1;
for (auto &c : extmapper_cell->connections_) {
RTLIL::Wire *w = extmapper_module->addWire(c.first, GetSize(c.second));
- if (w->name == "\\Y" || w->name == "\\Q")
+ if (w->name.in(ID::Y, ID(Q)))
w->port_output = true;
else
w->port_input = true;
@@ -541,15 +634,16 @@ struct TechmapWorker
if (extmapper_name == "maccmap") {
log("Creating %s with maccmap.\n", log_id(extmapper_module));
- if (extmapper_cell->type != "$macc")
+ if (extmapper_cell->type != ID($macc))
log_error("The maccmap mapper can only map $macc (not %s) cells!\n", log_id(extmapper_cell->type));
maccmap(extmapper_module, extmapper_cell);
extmapper_module->remove(extmapper_cell);
}
if (extmapper_name == "wrap") {
- std::string cmd_string = tpl->attributes.at("\\techmap_wrap").decode_string();
+ std::string cmd_string = tpl->attributes.at(ID(techmap_wrap)).decode_string();
log("Running \"%s\" on wrapper %s.\n", cmd_string.c_str(), log_id(extmapper_module));
+ mkdebug.on();
Pass::call_on_module(extmapper_design, extmapper_module, cmd_string);
log_continue = true;
}
@@ -563,11 +657,21 @@ struct TechmapWorker
goto use_wrapper_tpl;
}
- log("%s %s.%s (%s) to %s.\n", mapmsg_prefix.c_str(), log_id(module), log_id(cell), log_id(cell->type), log_id(extmapper_module));
+ auto msg = stringf("Using extmapper %s for cells of type %s.", log_id(extmapper_module), log_id(cell->type));
+ if (!log_msg_cache.count(msg)) {
+ log_msg_cache.insert(msg);
+ log("%s\n", msg.c_str());
+ }
+ log_debug("%s %s.%s (%s) to %s.\n", mapmsg_prefix.c_str(), log_id(module), log_id(cell), log_id(cell->type), log_id(extmapper_module));
}
else
{
- log("%s %s.%s (%s) with %s.\n", mapmsg_prefix.c_str(), log_id(module), log_id(cell), log_id(cell->type), extmapper_name.c_str());
+ auto msg = stringf("Using extmapper %s for cells of type %s.", extmapper_name.c_str(), log_id(cell->type));
+ if (!log_msg_cache.count(msg)) {
+ log_msg_cache.insert(msg);
+ log("%s\n", msg.c_str());
+ }
+ log_debug("%s %s.%s (%s) with %s.\n", mapmsg_prefix.c_str(), log_id(module), log_id(cell), log_id(cell->type), extmapper_name.c_str());
if (extmapper_name == "simplemap") {
if (simplemap_mappers.count(cell->type) == 0)
@@ -576,7 +680,7 @@ struct TechmapWorker
}
if (extmapper_name == "maccmap") {
- if (cell->type != "$macc")
+ if (cell->type != ID($macc))
log_error("The maccmap mapper can only map $macc (not %s) cells!\n", log_id(cell->type));
maccmap(module, cell);
}
@@ -591,7 +695,7 @@ struct TechmapWorker
}
for (auto conn : cell->connections()) {
- if (conn.first.substr(0, 1) == "$")
+ if (conn.first.begins_with("$"))
continue;
if (tpl->wires_.count(conn.first) > 0 && tpl->wires_.at(conn.first)->port_id > 0)
continue;
@@ -605,8 +709,8 @@ struct TechmapWorker
continue;
}
- if (tpl->avail_parameters.count("\\_TECHMAP_CELLTYPE_") != 0)
- parameters["\\_TECHMAP_CELLTYPE_"] = RTLIL::unescape_id(cell->type);
+ if (tpl->avail_parameters.count(ID(_TECHMAP_CELLTYPE_)) != 0)
+ parameters[ID(_TECHMAP_CELLTYPE_)] = RTLIL::unescape_id(cell->type);
for (auto conn : cell->connections()) {
if (tpl->avail_parameters.count(stringf("\\_TECHMAP_CONSTMSK_%s_", RTLIL::id2cstr(conn.first))) != 0) {
@@ -622,6 +726,17 @@ struct TechmapWorker
bit = RTLIL::SigBit(RTLIL::State::Sx);
parameters[stringf("\\_TECHMAP_CONSTVAL_%s_", RTLIL::id2cstr(conn.first))] = RTLIL::SigSpec(v).as_const();
}
+ if (tpl->avail_parameters.count(stringf("\\_TECHMAP_WIREINIT_%s_", RTLIL::id2cstr(conn.first))) != 0) {
+ auto sig = sigmap(conn.second);
+ RTLIL::Const value(State::Sx, sig.size());
+ for (int i = 0; i < sig.size(); i++) {
+ auto it = init_bits.find(sig[i]);
+ if (it != init_bits.end()) {
+ value[i] = it->second;
+ }
+ }
+ parameters[stringf("\\_TECHMAP_WIREINIT_%s_", RTLIL::id2cstr(conn.first))] = value;
+ }
}
int unique_bit_id_counter = 0;
@@ -638,12 +753,15 @@ struct TechmapWorker
unique_bit_id[bit] = unique_bit_id_counter++;
}
+ // Find highest bit set
int bits = 0;
for (int i = 0; i < 32; i++)
if (((unique_bit_id_counter-1) & (1 << i)) != 0)
bits = i;
- if (tpl->avail_parameters.count("\\_TECHMAP_BITS_CONNMAP_"))
- parameters["\\_TECHMAP_BITS_CONNMAP_"] = bits;
+ // Increment index by one to get number of bits
+ bits++;
+ if (tpl->avail_parameters.count(ID(_TECHMAP_BITS_CONNMAP_)))
+ parameters[ID(_TECHMAP_BITS_CONNMAP_)] = bits;
for (auto conn : cell->connections())
if (tpl->avail_parameters.count(stringf("\\_TECHMAP_CONNMAP_%s_", RTLIL::id2cstr(conn.first))) != 0) {
@@ -665,6 +783,7 @@ struct TechmapWorker
tpl = techmap_cache[key];
} else {
if (parameters.size() != 0) {
+ mkdebug.on();
derived_name = tpl->derive(map, dict<RTLIL::IdString, RTLIL::Const>(parameters.begin(), parameters.end()));
tpl = map->module(derived_name);
log_continue = true;
@@ -710,7 +829,7 @@ struct TechmapWorker
for (auto &it : twd)
{
- if (it.first.substr(0, 12) != "_TECHMAP_DO_" || it.second.empty())
+ if (it.first.compare(0, 12, "_TECHMAP_DO_") != 0 || it.second.empty())
continue;
auto &data = it.second.front();
@@ -818,7 +937,7 @@ struct TechmapWorker
TechmapWires twd = techmap_find_special_wires(tpl);
for (auto &it : twd) {
- if (it.first != "_TECHMAP_FAIL_" && it.first.substr(0, 12) != "_TECHMAP_DO_" && it.first.substr(0, 14) != "_TECHMAP_DONE_")
+ if (it.first != "_TECHMAP_FAIL_" && (it.first.substr(0, 20) != "_TECHMAP_REMOVEINIT_" || it.first[it.first.size()-1] != '_') && it.first.substr(0, 12) != "_TECHMAP_DO_" && it.first.substr(0, 14) != "_TECHMAP_DONE_")
log_error("Techmap yielded unknown config wire %s.\n", it.first.c_str());
if (techmap_do_cache[tpl])
for (auto &it2 : it.second)
@@ -834,6 +953,7 @@ struct TechmapWorker
if (log_continue) {
log_header(design, "Continuing TECHMAP pass.\n");
log_continue = false;
+ mkdebug.off();
}
while (techmap_module(map, tpl, map, handled_cells, celltypeMap, true)) { }
}
@@ -845,6 +965,24 @@ struct TechmapWorker
if (log_continue) {
log_header(design, "Continuing TECHMAP pass.\n");
log_continue = false;
+ mkdebug.off();
+ }
+
+ TechmapWires twd = techmap_find_special_wires(tpl);
+ for (auto &it : twd) {
+ if (it.first.substr(0, 20) == "_TECHMAP_REMOVEINIT_") {
+ for (auto &it2 : it.second) {
+ auto val = it2.value.as_const();
+ auto wirename = RTLIL::escape_id(it.first.substr(20, it.first.size() - 20 - 1));
+ auto it = cell->connections().find(wirename);
+ if (it != cell->connections().end()) {
+ auto sig = sigmap(it->second);
+ for (int i = 0; i < sig.size(); i++)
+ if (val[i] == State::S1)
+ remove_init_bits.insert(sig[i]);
+ }
+ }
+ }
}
if (extern_mode && !in_recursion)
@@ -857,20 +995,25 @@ struct TechmapWorker
tpl->cloneInto(m);
for (auto cell : m->cells()) {
- if (cell->type.substr(0, 2) == "\\$")
+ if (cell->type.begins_with("\\$"))
cell->type = cell->type.substr(1);
}
module_queue.insert(m);
}
- log("%s %s.%s to imported %s.\n", mapmsg_prefix.c_str(), log_id(module), log_id(cell), log_id(m_name));
+ log_debug("%s %s.%s to imported %s.\n", mapmsg_prefix.c_str(), log_id(module), log_id(cell), log_id(m_name));
cell->type = m_name;
cell->parameters.clear();
}
else
{
- log("%s %s.%s using %s.\n", mapmsg_prefix.c_str(), log_id(module), log_id(cell), log_id(tpl));
+ auto msg = stringf("Using template %s for cells of type %s.", log_id(tpl), log_id(cell->type));
+ if (!log_msg_cache.count(msg)) {
+ log_msg_cache.insert(msg);
+ log("%s\n", msg.c_str());
+ }
+ log_debug("%s %s.%s (%s) using %s.\n", mapmsg_prefix.c_str(), log_id(module), log_id(cell), log_id(cell->type), log_id(tpl));
techmap_module_worker(design, module, cell, tpl);
cell = NULL;
}
@@ -885,9 +1028,29 @@ struct TechmapWorker
handled_cells.insert(cell);
}
+ if (!remove_init_bits.empty()) {
+ for (auto wire : module->wires())
+ if (wire->attributes.count("\\init")) {
+ Const &value = wire->attributes.at("\\init");
+ bool do_cleanup = true;
+ for (int i = 0; i < min(GetSize(value), GetSize(wire)); i++) {
+ SigBit bit = sigmap(SigBit(wire, i));
+ if (remove_init_bits.count(bit))
+ value[i] = State::Sx;
+ else if (value[i] != State::Sx)
+ do_cleanup = false;
+ }
+ if (do_cleanup) {
+ log("Removing init attribute from wire %s.%s.\n", log_id(module), log_id(wire));
+ wire->attributes.erase("\\init");
+ }
+ }
+ }
+
if (log_continue) {
log_header(design, "Continuing TECHMAP pass.\n");
log_continue = false;
+ mkdebug.off();
}
return did_something;
@@ -896,7 +1059,7 @@ struct TechmapWorker
struct TechmapPass : public Pass {
TechmapPass() : Pass("techmap", "generic technology mapper") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -920,7 +1083,8 @@ struct TechmapPass : public Pass {
log(" instead of inlining them.\n");
log("\n");
log(" -max_iter <number>\n");
- log(" only run the specified number of iterations.\n");
+ log(" only run the specified number of iterations on each module.\n");
+ log(" default: unlimited\n");
log("\n");
log(" -recursive\n");
log(" instead of the iterative breadth-first algorithm use a recursive\n");
@@ -930,6 +1094,9 @@ struct TechmapPass : public Pass {
log(" -autoproc\n");
log(" Automatically call \"proc\" on implementations that contain processes.\n");
log("\n");
+ log(" -wb\n");
+ log(" Ignore the 'whitebox' attribute on cell implementations.\n");
+ log("\n");
log(" -assert\n");
log(" this option will cause techmap to exit with an error if it can't map\n");
log(" a selected cell. only cell types that end on an underscore are accepted\n");
@@ -938,7 +1105,7 @@ struct TechmapPass : public Pass {
log(" -D <define>, -I <incdir>\n");
log(" this options are passed as-is to the Verilog frontend for loading the\n");
log(" map file. Note that the Verilog frontend is also called with the\n");
- log(" '-ignore_redef' option set.\n");
+ log(" '-nooverwrite' option set.\n");
log("\n");
log("When a module in the map file has the 'techmap_celltype' attribute set, it will\n");
log("match cells with a type that match the text value of this attribute. Otherwise\n");
@@ -954,6 +1121,11 @@ struct TechmapPass : public Pass {
log("will create a wrapper for the cell and then run the command string that the\n");
log("attribute is set to on the wrapper module.\n");
log("\n");
+ log("When a port on a module in the map file has the 'techmap_autopurge' attribute\n");
+ log("set, and that port is not connected in the instantiation that is mapped, then\n");
+ log("then a cell port connected only to such wires will be omitted in the mapped\n");
+ log("version of the circuit.\n");
+ log("\n");
log("All wires in the modules from the map file matching the pattern _TECHMAP_*\n");
log("or *._TECHMAP_* are special wires that are used to pass instructions from\n");
log("the mapping module to the techmap command. At the moment the following special\n");
@@ -992,6 +1164,13 @@ struct TechmapPass : public Pass {
log("\n");
log(" It is possible to combine both prefixes to 'RECURSION; CONSTMAP; '.\n");
log("\n");
+ log(" _TECHMAP_REMOVEINIT_<port-name>_\n");
+ log(" When this wire is set to a constant value, the init attribute of the wire(s)\n");
+ log(" connected to this port will be consumed. This wire must have the same\n");
+ log(" width as the given port, and for every bit that is set to 1 in the value,\n");
+ log(" the corresponding init attribute bit will be changed to 1'bx. If all\n");
+ log(" bits of an init attribute are left as x, it will be removed.\n");
+ log("\n");
log("In addition to this special wires, techmap also supports special parameters in\n");
log("modules in the map file:\n");
log("\n");
@@ -1005,6 +1184,13 @@ struct TechmapPass : public Pass {
log(" former has a 1-bit for each constant input bit and the latter has the\n");
log(" value for this bit. The unused bits of the latter are set to undef (x).\n");
log("\n");
+ log(" _TECHMAP_WIREINIT_<port-name>_\n");
+ log(" When a parameter with this name exists, it will be set to the initial\n");
+ log(" value of the wire(s) connected to the given port, as specified by the init\n");
+ log(" attribute. If the attribute doesn't exist, x will be filled for the\n");
+ log(" missing bits. To remove the init attribute bits used, use the\n");
+ log(" _TECHMAP_REMOVEINIT_*_ wires.\n");
+ log("\n");
log(" _TECHMAP_BITS_CONNMAP_\n");
log(" _TECHMAP_CONNMAP_<port-name>_\n");
log(" For an N-bit port, the _TECHMAP_CONNMAP_<port-name>_ parameter, if it\n");
@@ -1020,6 +1206,12 @@ struct TechmapPass : public Pass {
log("\n");
log("A cell with the name _TECHMAP_REPLACE_ in the map file will inherit the name\n");
log("and attributes of the cell that is being replaced.\n");
+ log("A cell with a name of the form `_TECHMAP_REPLACE_.<suffix>` in the map file will\n");
+ log("be named thus but with the `_TECHMAP_REPLACE_' prefix substituted with the name\n");
+ log("of the cell being replaced.\n");
+ log("Similarly, a wire named in the form `_TECHMAP_REPLACE_.<suffix>` will cause a\n");
+ log("new wire alias to be created and named as above but with the `_TECHMAP_REPLACE_'\n");
+ log("prefix also substituted.\n");
log("\n");
log("See 'help extract' for a pass that does the opposite thing.\n");
log("\n");
@@ -1027,7 +1219,7 @@ struct TechmapPass : public Pass {
log("essentially techmap but using the design itself as map library).\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
log_header(design, "Executing TECHMAP pass (map to technology primitives).\n");
log_push();
@@ -1036,7 +1228,7 @@ struct TechmapPass : public Pass {
simplemap_get_mappers(worker.simplemap_mappers);
std::vector<std::string> map_files;
- std::string verilog_frontend = "verilog -ignore_redef";
+ std::string verilog_frontend = "verilog -nooverwrite -noblackbox";
int max_iter = -1;
size_t argidx;
@@ -1073,6 +1265,10 @@ struct TechmapPass : public Pass {
worker.autoproc_mode = true;
continue;
}
+ if (args[argidx] == "-wb") {
+ worker.ignore_wb = true;
+ continue;
+ }
break;
}
extra_args(args, argidx, design);
@@ -1081,9 +1277,9 @@ struct TechmapPass : public Pass {
if (map_files.empty()) {
std::istringstream f(stdcells_code);
Frontend::frontend_call(map, &f, "<techmap.v>", verilog_frontend);
- } else
+ } else {
for (auto &fn : map_files)
- if (fn.substr(0, 1) == "%") {
+ if (fn.compare(0, 1, "%") == 0) {
if (!saved_designs.count(fn.substr(1))) {
delete map;
log_cmd_error("Can't saved design `%s'.\n", fn.c_str()+1);
@@ -1095,21 +1291,25 @@ struct TechmapPass : public Pass {
std::ifstream f;
rewrite_filename(fn);
f.open(fn.c_str());
+ yosys_input_files.insert(fn);
if (f.fail())
log_cmd_error("Can't open map file `%s'\n", fn.c_str());
- Frontend::frontend_call(map, &f, fn, (fn.size() > 3 && fn.substr(fn.size()-3) == ".il") ? "ilang" : verilog_frontend);
+ Frontend::frontend_call(map, &f, fn, (fn.size() > 3 && fn.compare(fn.size()-3, std::string::npos, ".il") == 0 ? "ilang" : verilog_frontend));
}
+ }
+
+ log_header(design, "Continuing TECHMAP pass.\n");
std::map<RTLIL::IdString, std::set<RTLIL::IdString, RTLIL::sort_by_id_str>> celltypeMap;
for (auto &it : map->modules_) {
- if (it.second->attributes.count("\\techmap_celltype") && !it.second->attributes.at("\\techmap_celltype").bits.empty()) {
- char *p = strdup(it.second->attributes.at("\\techmap_celltype").decode_string().c_str());
+ if (it.second->attributes.count(ID(techmap_celltype)) && !it.second->attributes.at(ID(techmap_celltype)).bits.empty()) {
+ char *p = strdup(it.second->attributes.at(ID(techmap_celltype)).decode_string().c_str());
for (char *q = strtok(p, " \t\r\n"); q; q = strtok(NULL, " \t\r\n"))
celltypeMap[RTLIL::escape_id(q)].insert(it.first);
free(p);
} else {
string module_name = it.first.str();
- if (module_name.substr(0, 2) == "\\$")
+ if (it.first.begins_with("\\$"))
module_name = module_name.substr(1);
celltypeMap[module_name].insert(it.first);
}
@@ -1123,15 +1323,16 @@ struct TechmapPass : public Pass {
RTLIL::Module *module = *worker.module_queue.begin();
worker.module_queue.erase(module);
+ int module_max_iter = max_iter;
bool did_something = true;
std::set<RTLIL::Cell*> handled_cells;
while (did_something) {
did_something = false;
- if (worker.techmap_module(design, module, map, handled_cells, celltypeMap, false))
- did_something = true;
+ if (worker.techmap_module(design, module, map, handled_cells, celltypeMap, false))
+ did_something = true;
if (did_something)
module->check();
- if (max_iter > 0 && --max_iter == 0)
+ if (module_max_iter > 0 && --module_max_iter == 0)
break;
}
}
@@ -1145,11 +1346,11 @@ struct TechmapPass : public Pass {
struct FlattenPass : public Pass {
FlattenPass() : Pass("flatten", "flatten design") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
- log(" flatten [selection]\n");
+ log(" flatten [options] [selection]\n");
log("\n");
log("This pass flattens the design by replacing cells by their implementation. This\n");
log("pass is very similar to the 'techmap' pass. The only difference is that this\n");
@@ -1158,17 +1359,29 @@ struct FlattenPass : public Pass {
log("Cells and/or modules with the 'keep_hierarchy' attribute set will not be\n");
log("flattened by this command.\n");
log("\n");
+ log(" -wb\n");
+ log(" Ignore the 'whitebox' attribute on cell implementations.\n");
+ log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
log_header(design, "Executing FLATTEN pass (flatten design).\n");
log_push();
- extra_args(args, 1, design);
-
TechmapWorker worker;
worker.flatten_mode = true;
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ if (args[argidx] == "-wb") {
+ worker.ignore_wb = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+
std::map<RTLIL::IdString, std::set<RTLIL::IdString, RTLIL::sort_by_id_str>> celltypeMap;
for (auto module : design->modules())
celltypeMap[module->name].insert(module->name);
@@ -1176,7 +1389,7 @@ struct FlattenPass : public Pass {
RTLIL::Module *top_mod = NULL;
if (design->full_selection())
for (auto mod : design->modules())
- if (mod->get_bool_attribute("\\top"))
+ if (mod->get_bool_attribute(ID(top)))
top_mod = mod;
std::set<RTLIL::Cell*> handled_cells;
@@ -1194,6 +1407,7 @@ struct FlattenPass : public Pass {
}
}
+ log_suppressed();
log("No more expansions possible.\n");
if (top_mod != NULL)
@@ -1213,7 +1427,7 @@ struct FlattenPass : public Pass {
dict<RTLIL::IdString, RTLIL::Module*> new_modules;
for (auto mod : vector<Module*>(design->modules()))
- if (used_modules[mod->name] || mod->get_bool_attribute("\\blackbox")) {
+ if (used_modules[mod->name] || mod->get_blackbox_attribute(worker.ignore_wb)) {
new_modules[mod->name] = mod;
} else {
log("Deleting now unused module %s.\n", log_id(mod));
diff --git a/passes/techmap/tribuf.cc b/passes/techmap/tribuf.cc
index 03629082c..decf9a202 100644
--- a/passes/techmap/tribuf.cc
+++ b/passes/techmap/tribuf.cc
@@ -63,38 +63,40 @@ struct TribufWorker {
for (auto cell : module->selected_cells())
{
- if (cell->type == "$tribuf")
- tribuf_cells[sigmap(cell->getPort("\\Y"))].push_back(cell);
+ if (cell->type == ID($tribuf))
+ tribuf_cells[sigmap(cell->getPort(ID::Y))].push_back(cell);
- if (cell->type == "$_TBUF_")
- tribuf_cells[sigmap(cell->getPort("\\Y"))].push_back(cell);
+ if (cell->type == ID($_TBUF_))
+ tribuf_cells[sigmap(cell->getPort(ID::Y))].push_back(cell);
- if (cell->type.in("$mux", "$_MUX_"))
+ if (cell->type.in(ID($mux), ID($_MUX_)))
{
- IdString en_port = cell->type == "$mux" ? "\\EN" : "\\E";
- IdString tri_type = cell->type == "$mux" ? "$tribuf" : "$_TBUF_";
+ IdString en_port = cell->type == ID($mux) ? ID(EN) : ID(E);
+ IdString tri_type = cell->type == ID($mux) ? ID($tribuf) : ID($_TBUF_);
- if (is_all_z(cell->getPort("\\A")) && is_all_z(cell->getPort("\\B"))) {
+ if (is_all_z(cell->getPort(ID::A)) && is_all_z(cell->getPort(ID::B))) {
module->remove(cell);
continue;
}
- if (is_all_z(cell->getPort("\\A"))) {
- cell->setPort("\\A", cell->getPort("\\B"));
- cell->setPort(en_port, cell->getPort("\\S"));
- cell->unsetPort("\\B");
- cell->unsetPort("\\S");
+ if (is_all_z(cell->getPort(ID::A))) {
+ cell->setPort(ID::A, cell->getPort(ID::B));
+ cell->setPort(en_port, cell->getPort(ID(S)));
+ cell->unsetPort(ID::B);
+ cell->unsetPort(ID(S));
cell->type = tri_type;
- tribuf_cells[sigmap(cell->getPort("\\Y"))].push_back(cell);
+ tribuf_cells[sigmap(cell->getPort(ID::Y))].push_back(cell);
+ module->design->scratchpad_set_bool("tribuf.added_something", true);
continue;
}
- if (is_all_z(cell->getPort("\\B"))) {
- cell->setPort(en_port, module->Not(NEW_ID, cell->getPort("\\S")));
- cell->unsetPort("\\B");
- cell->unsetPort("\\S");
+ if (is_all_z(cell->getPort(ID::B))) {
+ cell->setPort(en_port, module->Not(NEW_ID, cell->getPort(ID(S))));
+ cell->unsetPort(ID::B);
+ cell->unsetPort(ID(S));
cell->type = tri_type;
- tribuf_cells[sigmap(cell->getPort("\\Y"))].push_back(cell);
+ tribuf_cells[sigmap(cell->getPort(ID::Y))].push_back(cell);
+ module->design->scratchpad_set_bool("tribuf.added_something", true);
continue;
}
}
@@ -118,11 +120,11 @@ struct TribufWorker {
SigSpec pmux_b, pmux_s;
for (auto cell : it.second) {
- if (cell->type == "$tribuf")
- pmux_s.append(cell->getPort("\\EN"));
+ if (cell->type == ID($tribuf))
+ pmux_s.append(cell->getPort(ID(EN)));
else
- pmux_s.append(cell->getPort("\\E"));
- pmux_b.append(cell->getPort("\\A"));
+ pmux_s.append(cell->getPort(ID(E)));
+ pmux_b.append(cell->getPort(ID::A));
module->remove(cell);
}
@@ -130,8 +132,10 @@ struct TribufWorker {
if (no_tribuf)
module->connect(it.first, muxout);
- else
+ else {
module->addTribuf(NEW_ID, muxout, module->ReduceOr(NEW_ID, pmux_s), it.first);
+ module->design->scratchpad_set_bool("tribuf.added_something", true);
+ }
}
}
}
@@ -139,7 +143,7 @@ struct TribufWorker {
struct TribufPass : public Pass {
TribufPass() : Pass("tribuf", "infer tri-state buffers") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -156,7 +160,7 @@ struct TribufPass : public Pass {
log(" to non-tristate logic. this option implies -merge.\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
TribufConfig config;
diff --git a/passes/techmap/zinit.cc b/passes/techmap/zinit.cc
index a577e1235..ac3d4ed4a 100644
--- a/passes/techmap/zinit.cc
+++ b/passes/techmap/zinit.cc
@@ -25,7 +25,7 @@ PRIVATE_NAMESPACE_BEGIN
struct ZinitPass : public Pass {
ZinitPass() : Pass("zinit", "add inverters so all FF are zero-initialized") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -37,7 +37,7 @@ struct ZinitPass : public Pass {
log(" also add zero initialization to uninitialized FFs\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
bool all_mode = false;
@@ -46,7 +46,7 @@ struct ZinitPass : public Pass {
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++)
{
- if (args[argidx] == "-singleton") {
+ if (args[argidx] == "-all") {
all_mode = true;
continue;
}
@@ -62,12 +62,12 @@ struct ZinitPass : public Pass {
for (auto wire : module->selected_wires())
{
- if (wire->attributes.count("\\init") == 0)
+ if (wire->attributes.count(ID(init)) == 0)
continue;
SigSpec wirebits = sigmap(wire);
- Const initval = wire->attributes.at("\\init");
- wire->attributes.erase("\\init");
+ Const initval = wire->attributes.at(ID(init));
+ wire->attributes.erase(ID(init));
for (int i = 0; i < GetSize(wirebits) && i < GetSize(initval); i++)
{
@@ -90,12 +90,12 @@ struct ZinitPass : public Pass {
}
pool<IdString> dff_types = {
- "$ff", "$dff", "$dffe", "$dffsr", "$adff",
- "$_FF_", "$_DFFE_NN_", "$_DFFE_NP_", "$_DFFE_PN_", "$_DFFE_PP_",
- "$_DFFSR_NNN_", "$_DFFSR_NNP_", "$_DFFSR_NPN_", "$_DFFSR_NPP_",
- "$_DFFSR_PNN_", "$_DFFSR_PNP_", "$_DFFSR_PPN_", "$_DFFSR_PPP_",
- "$_DFF_N_", "$_DFF_NN0_", "$_DFF_NN1_", "$_DFF_NP0_", "$_DFF_NP1_",
- "$_DFF_P_", "$_DFF_PN0_", "$_DFF_PN1_", "$_DFF_PP0_", "$_DFF_PP1_"
+ ID($ff), ID($dff), ID($dffe), ID($dffsr), ID($adff),
+ ID($_FF_), ID($_DFFE_NN_), ID($_DFFE_NP_), ID($_DFFE_PN_), ID($_DFFE_PP_),
+ ID($_DFFSR_NNN_), ID($_DFFSR_NNP_), ID($_DFFSR_NPN_), ID($_DFFSR_NPP_),
+ ID($_DFFSR_PNN_), ID($_DFFSR_PNP_), ID($_DFFSR_PPN_), ID($_DFFSR_PPP_),
+ ID($_DFF_N_), ID($_DFF_NN0_), ID($_DFF_NN1_), ID($_DFF_NP0_), ID($_DFF_NP1_),
+ ID($_DFF_P_), ID($_DFF_PN0_), ID($_DFF_PN1_), ID($_DFF_PP0_), ID($_DFF_PP1_)
};
for (auto cell : module->selected_cells())
@@ -103,8 +103,8 @@ struct ZinitPass : public Pass {
if (!dff_types.count(cell->type))
continue;
- SigSpec sig_d = sigmap(cell->getPort("\\D"));
- SigSpec sig_q = sigmap(cell->getPort("\\Q"));
+ SigSpec sig_d = sigmap(cell->getPort(ID(D)));
+ SigSpec sig_q = sigmap(cell->getPort(ID(Q)));
if (GetSize(sig_d) < 1 || GetSize(sig_q) < 1)
continue;
@@ -120,14 +120,14 @@ struct ZinitPass : public Pass {
}
Wire *initwire = module->addWire(NEW_ID, GetSize(initval));
- initwire->attributes["\\init"] = initval;
+ initwire->attributes[ID(init)] = initval;
for (int i = 0; i < GetSize(initwire); i++)
if (initval.bits.at(i) == State::S1)
{
sig_d[i] = module->NotGate(NEW_ID, sig_d[i]);
module->addNotGate(NEW_ID, SigSpec(initwire, i), sig_q[i]);
- initwire->attributes["\\init"].bits.at(i) = State::S0;
+ initwire->attributes[ID(init)].bits.at(i) = State::S0;
}
else
{
@@ -137,8 +137,8 @@ struct ZinitPass : public Pass {
log("FF init value for cell %s (%s): %s = %s\n", log_id(cell), log_id(cell->type),
log_signal(sig_q), log_signal(initval));
- cell->setPort("\\D", sig_d);
- cell->setPort("\\Q", initwire);
+ cell->setPort(ID(D), sig_d);
+ cell->setPort(ID(Q), initwire);
}
for (auto &it : initbits)
diff --git a/passes/tests/flowmap/flow.v b/passes/tests/flowmap/flow.v
new file mode 100644
index 000000000..297ef910e
--- /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 000000000..2fb40ffa4
--- /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 000000000..9454edf3c
--- /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 000000000..fdb278833
--- /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 000000000..445e4afb0
--- /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 000000000..d4b41733d
--- /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 000000000..06147a1aa
--- /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 000000000..bc6ac1757
--- /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
diff --git a/passes/tests/test_abcloop.cc b/passes/tests/test_abcloop.cc
index 09cb41954..5d5466afe 100644
--- a/passes/tests/test_abcloop.cc
+++ b/passes/tests/test_abcloop.cc
@@ -244,7 +244,7 @@ static void test_abcloop()
struct TestAbcloopPass : public Pass {
TestAbcloopPass() : Pass("test_abcloop", "automatically test handling of loops in abc command") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -259,7 +259,7 @@ struct TestAbcloopPass : public Pass {
log(" use this value as rng seed value (default = unix time).\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design*)
+ void execute(std::vector<std::string> args, RTLIL::Design*) YS_OVERRIDE
{
int num_iter = 100;
xorshift32_state = 0;
diff --git a/passes/tests/test_autotb.cc b/passes/tests/test_autotb.cc
index cb31056f4..2b6a86c25 100644
--- a/passes/tests/test_autotb.cc
+++ b/passes/tests/test_autotb.cc
@@ -324,7 +324,7 @@ static void autotest(std::ostream &f, RTLIL::Design *design, int num_iter, int s
struct TestAutotbBackend : public Backend {
TestAutotbBackend() : Backend("=test_autotb", "generate simple test benches") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -345,11 +345,19 @@ struct TestAutotbBackend : public Backend {
log("value after initialization. This can e.g. be used to force a reset signal\n");
log("low in order to explore more inner states in a state machine.\n");
log("\n");
+ log("The attribute 'gentb_skip' can be attached to modules to suppress testbench\n");
+ log("generation.\n");
+ log("\n");
log(" -n <int>\n");
log(" number of iterations the test bench should run (default = 1000)\n");
log("\n");
+ log(" -seed <int>\n");
+ log(" seed used for pseudo-random number generation (default = 0).\n");
+ log(" a value of 0 will cause an arbitrary seed to be chosen, based on\n");
+ log(" the current system time.\n");
+ log("\n");
}
- virtual void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
int num_iter = 1000;
int seed = 0;
diff --git a/passes/tests/test_cell.cc b/passes/tests/test_cell.cc
index 47b6bdf23..88116eeec 100644
--- a/passes/tests/test_cell.cc
+++ b/passes/tests/test_cell.cc
@@ -43,7 +43,7 @@ static void create_gold_module(RTLIL::Design *design, RTLIL::IdString cell_type,
RTLIL::Cell *cell = module->addCell("\\UUT", cell_type);
RTLIL::Wire *wire;
- if (cell_type == "$mux" || cell_type == "$pmux")
+ if (cell_type.in("$mux", "$pmux"))
{
int width = 1 + xorshift32(8);
int swidth = cell_type == "$mux" ? 1 : 1 + xorshift32(8);
@@ -186,7 +186,7 @@ static void create_gold_module(RTLIL::Design *design, RTLIL::IdString cell_type,
RTLIL::SigSpec config;
for (int i = 0; i < (1 << width); i++)
- config.append(xorshift32(2) ? RTLIL::S1 : RTLIL::S0);
+ config.append(xorshift32(2) ? State::S1 : State::S0);
cell->setParam("\\LUT", config.as_const());
}
@@ -209,16 +209,16 @@ static void create_gold_module(RTLIL::Design *design, RTLIL::IdString cell_type,
for (int i = 0; i < width*depth; i++)
switch (xorshift32(3)) {
case 0:
- config.append(RTLIL::S1);
- config.append(RTLIL::S0);
+ config.append(State::S1);
+ config.append(State::S0);
break;
case 1:
- config.append(RTLIL::S0);
- config.append(RTLIL::S1);
+ config.append(State::S0);
+ config.append(State::S1);
break;
case 2:
- config.append(RTLIL::S0);
- config.append(RTLIL::S0);
+ config.append(State::S0);
+ config.append(State::S0);
break;
}
@@ -264,7 +264,7 @@ static void create_gold_module(RTLIL::Design *design, RTLIL::IdString cell_type,
cell->setPort("\\Y", wire);
}
- if (muxdiv && (cell_type == "$div" || cell_type == "$mod")) {
+ if (muxdiv && cell_type.in("$div", "$mod")) {
auto b_not_zero = module->ReduceBool(NEW_ID, cell->getPort("\\B"));
auto div_out = module->addWire(NEW_ID, GetSize(cell->getPort("\\Y")));
module->addMux(NEW_ID, RTLIL::SigSpec(0, GetSize(div_out)), div_out, b_not_zero, cell->getPort("\\Y"));
@@ -308,18 +308,18 @@ static void create_gold_module(RTLIL::Design *design, RTLIL::IdString cell_type,
case 0:
n = xorshift32(GetSize(sig) + 1);
for (int i = 0; i < n; i++)
- sig[i] = xorshift32(2) == 1 ? RTLIL::S1 : RTLIL::S0;
+ sig[i] = xorshift32(2) == 1 ? State::S1 : State::S0;
break;
case 1:
n = xorshift32(GetSize(sig) + 1);
for (int i = n; i < GetSize(sig); i++)
- sig[i] = xorshift32(2) == 1 ? RTLIL::S1 : RTLIL::S0;
+ sig[i] = xorshift32(2) == 1 ? State::S1 : State::S0;
break;
case 2:
n = xorshift32(GetSize(sig));
m = xorshift32(GetSize(sig));
for (int i = min(n, m); i < max(n, m); i++)
- sig[i] = xorshift32(2) == 1 ? RTLIL::S1 : RTLIL::S0;
+ sig[i] = xorshift32(2) == 1 ? State::S1 : State::S0;
break;
}
@@ -491,7 +491,7 @@ static void run_eval_test(RTLIL::Design *design, bool verbose, bool nosat, std::
RTLIL::Const in_value;
for (int i = 0; i < GetSize(gold_wire); i++)
- in_value.bits.push_back(xorshift32(2) ? RTLIL::S1 : RTLIL::S0);
+ in_value.bits.push_back(xorshift32(2) ? State::S1 : State::S0);
if (xorshift32(4) == 0) {
int inv_chance = 1 + xorshift32(8);
@@ -591,11 +591,11 @@ static void run_eval_test(RTLIL::Design *design, bool verbose, bool nosat, std::
}
for (int i = 0; i < GetSize(out_sig); i++) {
- if (out_val[i] != RTLIL::S0 && out_val[i] != RTLIL::S1)
+ if (out_val[i] != State::S0 && out_val[i] != State::S1)
continue;
- if (out_val[i] == RTLIL::S0 && sat1_model_value.at(i) == false)
+ if (out_val[i] == State::S0 && sat1_model_value.at(i) == false)
continue;
- if (out_val[i] == RTLIL::S1 && sat1_model_value.at(i) == true)
+ if (out_val[i] == State::S1 && sat1_model_value.at(i) == true)
continue;
log_error("Mismatch in sat model 1 (no undef modeling) output!\n");
}
@@ -627,12 +627,12 @@ static void run_eval_test(RTLIL::Design *design, bool verbose, bool nosat, std::
for (int i = 0; i < GetSize(out_sig); i++) {
if (sat2_model_value.at(GetSize(out_sig) + i)) {
- if (out_val[i] != RTLIL::S0 && out_val[i] != RTLIL::S1)
+ if (out_val[i] != State::S0 && out_val[i] != State::S1)
continue;
} else {
- if (out_val[i] == RTLIL::S0 && sat2_model_value.at(i) == false)
+ if (out_val[i] == State::S0 && sat2_model_value.at(i) == false)
continue;
- if (out_val[i] == RTLIL::S1 && sat2_model_value.at(i) == true)
+ if (out_val[i] == State::S1 && sat2_model_value.at(i) == true)
continue;
}
log_error("Mismatch in sat model 2 (undef modeling) output!\n");
@@ -652,7 +652,7 @@ static void run_eval_test(RTLIL::Design *design, bool verbose, bool nosat, std::
struct TestCellPass : public Pass {
TestCellPass() : Pass("test_cell", "automatically test the implementation of a cell type") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -712,7 +712,7 @@ struct TestCellPass : public Pass {
log(" create a Verilog test bench to test simlib and write_verilog\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design*)
+ void execute(std::vector<std::string> args, RTLIL::Design*) YS_OVERRIDE
{
int num_iter = 100;
std::string techmap_cmd = "techmap -assert";
@@ -872,7 +872,7 @@ struct TestCellPass : public Pass {
continue;
}
- if (args[argidx].substr(0, 1) == "/") {
+ if (args[argidx].compare(0, 1, "/") == 0) {
std::vector<std::string> new_selected_cell_types;
for (auto it : selected_cell_types)
if (it != args[argidx].substr(1))