aboutsummaryrefslogtreecommitdiffstats
path: root/passes
diff options
context:
space:
mode:
Diffstat (limited to 'passes')
-rw-r--r--passes/cmds/add.cc2
-rw-r--r--passes/cmds/bugpoint.cc14
-rw-r--r--passes/cmds/cover.cc10
-rw-r--r--passes/cmds/plugin.cc50
-rw-r--r--passes/cmds/qwp.cc2
-rw-r--r--passes/cmds/rename.cc32
-rw-r--r--passes/cmds/setattr.cc39
-rw-r--r--passes/cmds/setundef.cc118
-rw-r--r--passes/cmds/show.cc27
-rw-r--r--passes/cmds/stat.cc76
-rw-r--r--passes/cmds/tee.cc5
-rw-r--r--passes/cmds/trace.cc34
-rw-r--r--passes/equiv/equiv_opt.cc21
-rw-r--r--passes/hierarchy/hierarchy.cc275
-rw-r--r--passes/hierarchy/uniquify.cc4
-rw-r--r--passes/memory/memory_bram.cc31
-rw-r--r--passes/opt/Makefile.inc1
-rw-r--r--passes/opt/opt_clean.cc210
-rw-r--r--passes/opt/opt_expr.cc132
-rw-r--r--passes/opt/opt_merge.cc8
-rw-r--r--passes/opt/opt_muxtree.cc20
-rw-r--r--passes/opt/opt_rmdff.cc72
-rw-r--r--passes/opt/pmux2shiftx.cc852
-rw-r--r--passes/opt/wreduce.cc100
-rw-r--r--passes/pmgen/.gitignore1
-rw-r--r--passes/pmgen/Makefile.inc19
-rw-r--r--passes/pmgen/README.md36
-rw-r--r--passes/pmgen/ice40_dsp.cc117
-rw-r--r--passes/pmgen/ice40_dsp.pmg2
-rw-r--r--passes/pmgen/peepopt.cc68
-rw-r--r--passes/pmgen/peepopt_muldiv.pmg36
-rw-r--r--passes/pmgen/peepopt_shiftmul.pmg94
-rw-r--r--passes/pmgen/pmgen.py261
-rw-r--r--passes/proc/proc_mux.cc50
-rw-r--r--passes/proc/proc_rmdead.cc18
-rw-r--r--passes/sat/Makefile.inc4
-rw-r--r--passes/sat/async2sync.cc53
-rw-r--r--passes/sat/cutpoint.cc168
-rw-r--r--passes/sat/expose.cc2
-rw-r--r--passes/sat/fmcombine.cc376
-rw-r--r--passes/sat/miter.cc8
-rw-r--r--passes/sat/mutate.cc988
-rw-r--r--passes/sat/sat.cc1
-rw-r--r--passes/sat/sim.cc5
-rw-r--r--passes/sat/supercover.cc92
-rw-r--r--passes/techmap/abc.cc73
-rw-r--r--passes/techmap/attrmap.cc4
-rw-r--r--passes/techmap/dffinit.cc3
-rw-r--r--passes/techmap/dfflibmap.cc2
-rw-r--r--passes/techmap/flowmap.cc4
-rw-r--r--passes/techmap/libparse.cc179
-rw-r--r--passes/techmap/libparse.h10
-rw-r--r--passes/techmap/muxcover.cc54
-rw-r--r--passes/techmap/pmuxtree.cc4
-rw-r--r--passes/techmap/shregmap.cc177
-rw-r--r--passes/techmap/simplemap.cc2
-rw-r--r--passes/techmap/techmap.cc72
-rw-r--r--passes/techmap/zinit.cc2
58 files changed, 4609 insertions, 511 deletions
diff --git a/passes/cmds/add.cc b/passes/cmds/add.cc
index cfccca966..af6f7043d 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;
diff --git a/passes/cmds/bugpoint.cc b/passes/cmds/bugpoint.cc
index 606276e64..038ab7c7c 100644
--- a/passes/cmds/bugpoint.cc
+++ b/passes/cmds/bugpoint.cc
@@ -128,7 +128,7 @@ struct BugpointPass : public Pass {
{
for (auto &it : design_copy->modules_)
{
- if (it.second->get_bool_attribute("\\blackbox"))
+ if (it.second->get_blackbox_attribute())
continue;
if (index++ == seed)
@@ -143,7 +143,7 @@ struct BugpointPass : public Pass {
{
for (auto mod : design_copy->modules())
{
- if (mod->get_bool_attribute("\\blackbox"))
+ if (mod->get_blackbox_attribute())
continue;
for (auto wire : mod->wires())
@@ -168,7 +168,7 @@ struct BugpointPass : public Pass {
{
for (auto mod : design_copy->modules())
{
- if (mod->get_bool_attribute("\\blackbox"))
+ if (mod->get_blackbox_attribute())
continue;
for (auto &it : mod->cells_)
@@ -186,7 +186,7 @@ struct BugpointPass : public Pass {
{
for (auto mod : design_copy->modules())
{
- if (mod->get_bool_attribute("\\blackbox"))
+ if (mod->get_blackbox_attribute())
continue;
for (auto cell : mod->cells())
@@ -281,6 +281,9 @@ struct BugpointPass : public Pass {
}
extra_args(args, argidx, design);
+ if (script.empty())
+ log_cmd_error("Missing -script option.\n");
+
if (!has_part)
{
modules = true;
@@ -298,7 +301,7 @@ struct BugpointPass : public Pass {
if (!check_logfile(grep))
log_cmd_error("The provided grep string is not found in the log file!\n");
- int seed = 0, crashing_seed = seed;
+ int seed = 0;
bool found_something = false, stage2 = false;
while (true)
{
@@ -324,7 +327,6 @@ struct BugpointPass : public Pass {
if (crashing_design != design)
delete crashing_design;
crashing_design = simplified;
- crashing_seed = seed;
found_something = true;
}
else
diff --git a/passes/cmds/cover.cc b/passes/cmds/cover.cc
index 0ec747671..1128116b4 100644
--- a/passes/cmds/cover.cc
+++ b/passes/cmds/cover.cc
@@ -98,21 +98,23 @@ 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;
diff --git a/passes/cmds/plugin.cc b/passes/cmds/plugin.cc
index aa6d5b6cc..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)
@@ -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/qwp.cc b/passes/cmds/qwp.cc
index 1c64a7b77..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
diff --git a/passes/cmds/rename.cc b/passes/cmds/rename.cc
index 698ce7235..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;
@@ -108,15 +113,26 @@ struct RenamePass : public Pass {
log("Rename the specified object. Note that selection patterns are not supported\n");
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");
@@ -124,11 +140,13 @@ struct RenamePass : public Pass {
log("The character %% in the pattern is replaced with a integer number. The default\n");
log("pattern is '_%%_'.\n");
log("\n");
+ log("\n");
log(" rename -hide [selection]\n");
log("\n");
log("Assign private names (the ones with $-prefix) to all selected wires and cells\n");
log("with public names. This ignores all selected ports.\n");
log("\n");
+ log("\n");
log(" rename -top new_name\n");
log("\n");
log("Rename top module.\n");
@@ -142,6 +160,7 @@ struct RenamePass : public Pass {
bool flag_enumerate = false;
bool flag_hide = false;
bool flag_top = false;
+ bool flag_output = false;
bool got_mode = false;
size_t argidx;
@@ -153,6 +172,11 @@ struct RenamePass : public Pass {
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;
@@ -322,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/setattr.cc b/passes/cmds/setattr.cc
index d38a6b3da..b9fcc3e7a 100644
--- a/passes/cmds/setattr.cc
+++ b/passes/cmds/setattr.cc
@@ -128,6 +128,45 @@ 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") { }
void help() YS_OVERRIDE
diff --git a/passes/cmds/setundef.cc b/passes/cmds/setundef.cc
index f6949c820..3eedc86b8 100644
--- a/passes/cmds/setundef.cc
+++ b/passes/cmds/setundef.cc
@@ -393,44 +393,112 @@ 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 bit : sigmap(wire))
- ffbits.erase(bit);
+ for (auto wire : initwires)
+ {
+ int weight = 0;
- initwires.insert(wire);
- }
+ for (auto bit : sigmap(wire))
+ weight += ffbits.count(bit) ? +1 : -1;
+
+ 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;
+ }
- initwires.insert(wire);
+ for (int i = 0; i < GetSize(wire); i++)
+ if (initval[i] != State::Sx)
+ ffbits.erase(sigmap(SigBit(wire, i)));
+
+ 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 (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();
+ for (auto bit : sigmap(wire))
+ if (!ffbits.count(bit))
+ goto next_wire;
+
+ 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);
diff --git a/passes/cmds/show.cc b/passes/cmds/show.cc
index 58acd302d..cf729215f 100644
--- a/passes/cmds/show.cc
+++ b/passes/cmds/show.cc
@@ -237,15 +237,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));
@@ -555,7 +574,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
@@ -771,7 +790,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;
diff --git a/passes/cmds/stat.cc b/passes/cmds/stat.cc
index 54f4ea817..d22685b62 100644
--- a/passes/cmds/stat.cc
+++ b/passes/cmds/stat.cc
@@ -37,7 +37,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 +72,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
@@ -153,7 +157,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");
@@ -165,6 +170,59 @@ struct statdata_t
log("\n");
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);
+ }
}
};
@@ -226,6 +284,10 @@ 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. Corrently supported\n");
+ log(" calues for <technology>: xilinx\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");
@@ -239,6 +301,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++)
@@ -253,6 +316,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());
@@ -263,13 +330,16 @@ struct StatPass : public Pass {
}
extra_args(args, argidx, design);
+ if (techname != "" && techname != "xilinx")
+ 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");
diff --git a/passes/cmds/tee.cc b/passes/cmds/tee.cc
index ee96ace86..1a44bdaec 100644
--- a/passes/cmds/tee.cc
+++ b/passes/cmds/tee.cc
@@ -52,7 +52,9 @@ struct TeePass : public Pass {
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,6 +62,7 @@ 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()) {
@@ -89,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;
}
@@ -97,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/trace.cc b/passes/cmds/trace.cc
index f5305cde9..cf3e46ace 100644
--- a/passes/cmds/trace.cc
+++ b/passes/cmds/trace.cc
@@ -94,4 +94,38 @@ struct TracePass : public Pass {
}
} TracePass;
+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/equiv/equiv_opt.cc b/passes/equiv/equiv_opt.cc
index 86550a69b..3596dfd7b 100644
--- a/passes/equiv/equiv_opt.cc
+++ b/passes/equiv/equiv_opt.cc
@@ -44,7 +44,10 @@ struct EquivOptPass:public ScriptPass
log(" useful for handling architecture-specific primitives.\n");
log("\n");
log(" -assert\n");
- log(" produce an error if the circuits are not equivalent\n");
+ log(" produce an error if the circuits are not equivalent.\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();
@@ -52,13 +55,14 @@ struct EquivOptPass:public ScriptPass
}
std::string command, techmap_opts;
- bool assert;
+ bool assert, undef;
void clear_flags() YS_OVERRIDE
{
command = "";
techmap_opts = "";
assert = false;
+ undef = false;
}
void execute(std::vector < std::string > args, RTLIL::Design * design) YS_OVERRIDE
@@ -84,6 +88,10 @@ struct EquivOptPass:public ScriptPass
assert = true;
continue;
}
+ if (args[argidx] == "-undef") {
+ undef = true;
+ continue;
+ }
break;
}
@@ -134,12 +142,17 @@ struct EquivOptPass:public ScriptPass
opts = " -map <filename> ...";
else
opts = techmap_opts;
- run("techmap -D EQUIV -autoproc" + opts);
+ run("techmap -wb -D EQUIV -autoproc" + opts);
}
if (check_label("prove")) {
run("equiv_make gold gate equiv");
- run("equiv_induct 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)
diff --git a/passes/hierarchy/hierarchy.cc b/passes/hierarchy/hierarchy.cc
index 2d8edebb5..24e64a9b2 100644
--- a/passes/hierarchy/hierarchy.cc
+++ b/passes/hierarchy/hierarchy.cc
@@ -346,9 +346,9 @@ bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check
}
RTLIL::Module *mod = design->modules_[cell->type];
- if (design->modules_.at(cell->type)->get_bool_attribute("\\blackbox")) {
+ 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 module.\n",
+ 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;
}
@@ -451,7 +451,7 @@ 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);
@@ -491,7 +491,7 @@ void hierarchy_clean(RTLIL::Design *design, RTLIL::Module *top, bool purge_lib)
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);
@@ -562,7 +562,8 @@ 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");
@@ -570,7 +571,7 @@ struct HierarchyPass : public Pass {
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(" 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");
@@ -583,20 +584,20 @@ 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(" -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");
@@ -606,6 +607,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");
@@ -641,6 +648,7 @@ struct HierarchyPass : public Pass {
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++)
@@ -715,28 +723,61 @@ 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)
- load_top_mod = args[argidx];
+ 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()) {
+ 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, load_top_mod);
+ verific_import(design, parameters, load_top_mod);
top_mod = design->module(RTLIL::escape_id(load_top_mod));
}
#endif
@@ -745,7 +786,7 @@ struct HierarchyPass : public Pass {
} else {
#ifdef YOSYS_ENABLE_VERIFIC
if (verific_import_pending)
- verific_import(design);
+ verific_import(design, parameters);
#endif
}
@@ -846,7 +887,7 @@ struct HierarchyPass : public Pass {
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");
}
}
@@ -903,62 +944,178 @@ struct HierarchyPass : public Pass {
std::vector<Module*> design_modules = design->modules();
for (auto module : design_modules)
- for (auto cell : module->cells())
{
- Module *m = design->module(cell->type);
+ pool<Wire*> wand_wor_index;
+ dict<Wire*, SigSpec> wand_map, wor_map;
+ vector<SigSig> new_connections;
- if (m == nullptr)
- continue;
+ for (auto wire : module->wires())
+ {
+ 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);
+ }
+ }
+
+ for (auto &conn : module->connections())
+ {
+ SigSig new_conn;
+ int cursor = 0;
- if (m->get_bool_attribute("\\blackbox") && !cell->parameters.empty()) {
- IdString new_m_name = m->derive(design, cell->parameters, true);
- if (new_m_name.empty())
+ 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);
+
+ for (auto cell : module->cells())
+ {
+ if (!cell->known())
continue;
- if (new_m_name != m->name) {
- m = design->module(new_m_name);
- blackbox_derivatives.insert(m);
+
+ for (auto &conn : cell->connections())
+ {
+ if (!cell->output(conn.first))
+ continue;
+
+ SigSpec new_sig;
+ bool update_port = false;
+
+ for (auto c : conn.second.chunks())
+ {
+ 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 &conn : cell->connections())
+ for (auto w : wand_wor_index)
{
- Wire *w = m->wire(conn.first);
+ bool wand = wand_map.count(w);
+ SigSpec sigs = wand ? wand_map.at(w) : wor_map.at(w);
- if (w == nullptr || w->port_id == 0)
+ if (GetSize(sigs) == 0)
continue;
- if (GetSize(conn.second) == 0)
+ 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);
+ }
- SigSpec sig = conn.second;
+ for (auto cell : module->cells())
+ {
+ Module *m = design->module(cell->type);
- if (!keep_portwidths && GetSize(w) != GetSize(conn.second))
- {
- 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);
+ 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);
}
- else
+ }
+
+ 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));
}
}
diff --git a/passes/hierarchy/uniquify.cc b/passes/hierarchy/uniquify.cc
index c88ecd82e..ad3220918 100644
--- a/passes/hierarchy/uniquify.cc
+++ b/passes/hierarchy/uniquify.cc
@@ -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/memory_bram.cc b/passes/memory/memory_bram.cc
index cf4095d06..ddc56d9b5 100644
--- a/passes/memory/memory_bram.cc
+++ b/passes/memory/memory_bram.cc
@@ -542,7 +542,7 @@ bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram,
}
// 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;
@@ -552,7 +552,7 @@ bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram,
pair<SigBit, bool> clkdom(clksig, clkpol);
if (!clken)
clkdom = pair<SigBit, bool>(State::S1, false);
-
+ wr_clkdom = clkdom;
log(" Write port #%d is in clock domain %s%s.\n",
cell_port_i, clkdom.second ? "" : "!",
clken ? log_signal(clkdom.first) : "~async~");
@@ -641,6 +641,7 @@ grow_read_ports:;
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) {
@@ -718,7 +719,13 @@ grow_read_ports:;
if (read_transp.count(pi.transp) && read_transp.at(pi.transp) != transp) {
if (match.make_transp && wr_ports <= 1) {
pi.make_transp = true;
- enable_make_transp = true;
+ if (pi.clocks != 0) {
+ if (wr_ports == 1 && wr_clkdom != clkdom) {
+ log(" Bram port %c%d.%d cannot have soft transparency logic added as read and write clock domains differ.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
+ goto skip_bram_rport;
+ }
+ enable_make_transp = true;
+ }
} else {
log(" Bram port %c%d.%d has incompatible read transparency.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
goto skip_bram_rport;
@@ -737,7 +744,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;
@@ -913,17 +921,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),
@@ -949,6 +958,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);
}
diff --git a/passes/opt/Makefile.inc b/passes/opt/Makefile.inc
index c3e0a2a40..337fee9e4 100644
--- a/passes/opt/Makefile.inc
+++ b/passes/opt/Makefile.inc
@@ -13,5 +13,6 @@ 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
endif
diff --git a/passes/opt/opt_clean.cc b/passes/opt/opt_clean.cc
index c3b13acaf..cfb0f788a 100644
--- a/passes/opt/opt_clean.cc
+++ b/passes/opt/opt_clean.cc
@@ -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("$memwr", "$meminit", "$assert", "$assume", "$live", "$fair", "$cover", "$specify2", "$specify3", "$specrule"))
return true;
if (cell->has_keep_attr())
@@ -85,22 +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 raw_bit : it2.second) {
- if (raw_bit.wire == nullptr)
- continue;
- auto bit = sigmap(raw_bit);
- if (bit.wire == nullptr)
- log_warning("Driver-driver conflict for %s between cell %s.%s and constant %s in %s: Resolved using constant.\n",
- 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 (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);
@@ -114,6 +126,8 @@ void rmunused_module_cells(Module *module, bool verbose)
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));
}
}
@@ -137,11 +151,27 @@ 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)
@@ -202,7 +232,7 @@ bool check_public_name(RTLIL::IdString id)
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;
@@ -245,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);
@@ -259,6 +291,7 @@ 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)
@@ -271,72 +304,103 @@ void rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos
}
}
- 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("\\init"))
+ initval = wire->attributes.at("\\init");
+ if (GetSize(initval) != GetSize(wire))
+ initval.bits.resize(GetSize(wire), State::Sx);
+ if (initval.is_fully_undef())
+ wire->attributes.erase("\\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("\\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)) {
+ // do not get rid of public names unless in purge mode
+ } 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("\\init");
+ else
+ wire->attributes.at("\\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)
+ 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");
- 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)
@@ -399,7 +463,7 @@ bool rmunused_module_init(RTLIL::Module *module, bool purge_mode, bool verbose)
}
if (verbose)
- log(" removing redundant init attribute on %s.\n", log_id(wire));
+ log_debug(" removing redundant init attribute on %s.\n", log_id(wire));
wire->attributes.erase("\\init");
did_something = true;
@@ -426,7 +490,7 @@ void rmunused_module(RTLIL::Module *module, bool purge_mode, bool verbose, bool
}
for (auto cell : delcells) {
if (verbose)
- log(" removing buffer cell `%s': %s = %s\n", cell->name.c_str(),
+ log_debug(" removing buffer cell `%s': %s = %s\n", cell->name.c_str(),
log_signal(cell->getPort("\\Y")), log_signal(cell->getPort("\\A")));
module->remove(cell);
}
@@ -434,10 +498,10 @@ void rmunused_module(RTLIL::Module *module, bool purge_mode, bool verbose, bool
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 {
@@ -483,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;
@@ -548,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_expr.cc b/passes/opt/opt_expr.cc
index 26a3ca7bc..512ef0cbf 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("\\init")) {
+ SigSpec sig = sigmap(wire);
+ Const initval = wire->attributes.at("\\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("\\keep"))
used_signals.add(sigmap(wire));
all_signals.add(sigmap(wire));
}
@@ -67,10 +78,43 @@ 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("\\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("\\init");
+ did_something = true;
+ } else if (initval != wire->attributes.at("\\init")) {
+ log_debug("Updating init attribute on %s/%s: %s\n", log_id(module), log_id(wire), log_signal(initval));
+ wire->attributes["\\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)
@@ -78,7 +122,7 @@ void replace_cell(SigMap &assign_map, RTLIL::Module *module, RTLIL::Cell *cell,
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);
@@ -134,7 +178,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,6 +199,13 @@ bool group_cell_inputs(RTLIL::Module *module, RTLIL::Cell *cell, bool commutativ
new_b.append_bit(it.first.second);
}
+ if (cell->type.in("$and", "$or") && i == GRP_CONST_A) {
+ log_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);
@@ -173,10 +224,10 @@ bool group_cell_inputs(RTLIL::Module *module, RTLIL::Cell *cell, bool commutativ
module->connect(new_conn);
- log(" New cell `%s': A=%s", log_id(c), log_signal(new_a));
+ log_debug(" 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(", 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 +241,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 +270,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)));
@@ -448,9 +499,10 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
{
if (cell->type == "$reduce_xnor") {
cover("opt.opt_expr.reduce_xnor_not");
- log("Replacing %s cell `%s' in module `%s' with $not cell.\n",
+ log_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";
+ did_something = true;
} else {
cover("opt.opt_expr.unary_buffer");
replace_cell(assign_map, module, cell, "unary_buffer", "\\Y", cell->getPort("\\A"));
@@ -481,7 +533,7 @@ 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);
@@ -504,7 +556,7 @@ 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);
@@ -530,7 +582,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
if (new_a != RTLIL::State::Sm && RTLIL::SigSpec(new_a) != sig_a) {
cover("opt.opt_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;
@@ -556,7 +608,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
if (new_a != RTLIL::State::Sm && RTLIL::SigSpec(new_a) != sig_a) {
cover_list("opt.opt_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;
@@ -582,7 +634,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
if (new_b != RTLIL::State::Sm && RTLIL::SigSpec(new_b) != sig_b) {
cover_list("opt.opt_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;
@@ -633,7 +685,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
if ((cell->type == "$_MUX_" || cell->type == "$mux") && invert_map.count(assign_map(cell->getPort("\\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));
+ 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("\\A");
cell->setPort("\\A", cell->getPort("\\B"));
cell->setPort("\\B", tmp);
@@ -743,7 +795,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
ACTION_DO("\\Y", cell->getPort("\\A"));
if (input == State::S0 && !a.is_fully_undef()) {
cover("opt.opt_expr.action_" S__LINE__);
- log("Replacing data input of %s cell `%s' in module `%s' with constant undef.\n",
+ 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("\\A", SigSpec(State::Sx, GetSize(a)));
did_something = true;
@@ -815,7 +867,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
ACTION_DO("\\Y", cell->getPort("\\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));
+ log_debug("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");
@@ -830,7 +882,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
(assign_map(cell->getPort("\\A")).is_fully_zero() || assign_map(cell->getPort("\\B")).is_fully_zero()))
{
cover_list("opt.opt_expr.eqneq.cmpzero", "$eq", "$ne", cell->type.str());
- log("Replacing %s cell `%s' in module `%s' with %s.\n", log_id(cell->type), log_id(cell),
+ log_debug("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()) {
@@ -869,7 +921,7 @@ 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_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("\\B"))), shift_bits, log_id(module), log_signal(sig_y));
module->connect(cell->getPort("\\Y"), sig_y);
@@ -932,7 +984,7 @@ 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) {
@@ -962,7 +1014,7 @@ 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(1, 1) && cell->getPort("\\B") == RTLIL::SigSpec(0, 1)) {
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));
+ log_debug("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");
@@ -981,7 +1033,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
if (consume_x && mux_bool && (cell->type == "$mux" || cell->type == "$_MUX_") && cell->getPort("\\A") == RTLIL::SigSpec(0, 1)) {
cover_list("opt.opt_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));
+ 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("\\A", cell->getPort("\\S"));
cell->unsetPort("\\S");
if (cell->type == "$mux") {
@@ -1001,7 +1053,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
if (consume_x && mux_bool && (cell->type == "$mux" || cell->type == "$_MUX_") && cell->getPort("\\B") == RTLIL::SigSpec(1, 1)) {
cover_list("opt.opt_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));
+ 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("\\B", cell->getPort("\\S"));
cell->unsetPort("\\S");
if (cell->type == "$mux") {
@@ -1054,7 +1106,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
}
if (cell->getPort("\\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",
+ log_debug("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);
@@ -1172,7 +1224,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())));
@@ -1190,7 +1242,7 @@ 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) {
@@ -1230,7 +1282,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())));
@@ -1247,7 +1299,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
{
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);
@@ -1265,7 +1317,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
{
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);
@@ -1335,7 +1387,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
SigSpec y_sig = cell->getPort("\\Y");
Const y_value(cell->type.in("$eq", "$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);
@@ -1347,7 +1399,7 @@ 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);
@@ -1486,7 +1538,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
if (replace || remove)
{
- log("Replacing %s cell `%s' (implementing %s) with %s.\n",
+ 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("\\Y"), replace_sig);
@@ -1592,8 +1644,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 {
@@ -1603,7 +1661,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_merge.cc b/passes/opt/opt_merge.cc
index eedf88904..7567d4657 100644
--- a/passes/opt/opt_merge.cc
+++ b/passes/opt/opt_merge.cc
@@ -315,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
@@ -336,6 +336,8 @@ struct OptMergeWorker
}
}
}
+
+ log_suppressed();
}
};
diff --git a/passes/opt/opt_muxtree.cc b/passes/opt/opt_muxtree.cc
index 375697dc8..6511e091b 100644
--- a/passes/opt/opt_muxtree.cc
+++ b/passes/opt/opt_muxtree.cc
@@ -181,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)
{
@@ -326,7 +335,7 @@ struct OptMuxtreeWorker
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
@@ -397,10 +406,8 @@ struct OptMuxtreeWorker
void eval_mux(knowledge_t &knowledge, int mux_idx, bool do_replace_known, bool do_enable_ports, int abort_count)
{
- if (glob_abort_cnt == 0) {
- log(" Giving up (too many iterations)\n");
+ if (glob_abort_cnt == 0)
return;
- }
glob_abort_cnt--;
muxinfo_t &muxinfo = mux2info[mux_idx];
@@ -454,6 +461,7 @@ struct OptMuxtreeWorker
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));
diff --git a/passes/opt/opt_rmdff.cc b/passes/opt/opt_rmdff.cc
index e8570f0eb..eeb992a3e 100644
--- a/passes/opt/opt_rmdff.cc
+++ b/passes/opt/opt_rmdff.cc
@@ -260,8 +260,8 @@ delete_dlatch:
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");
@@ -285,6 +285,16 @@ bool handle_dff(RTLIL::Module *mod, RTLIL::Cell *dff)
val_rp = RTLIL::Const(dff->type[7] == 'P', 1);
val_rv = RTLIL::Const(dff->type[8] == '1', 1);
}
+ else if (dff->type.substr(0,7) == "$_DFFE_" && dff->type.substr(9) == "_" &&
+ (dff->type[7] == 'N' || dff->type[7] == 'P') &&
+ (dff->type[8] == 'N' || dff->type[8] == 'P')) {
+ sig_d = dff->getPort("\\D");
+ sig_q = dff->getPort("\\Q");
+ sig_c = dff->getPort("\\C");
+ sig_e = dff->getPort("\\E");
+ val_cp = RTLIL::Const(dff->type[7] == 'P', 1);
+ val_ep = RTLIL::Const(dff->type[8] == 'P', 1);
+ }
else if (dff->type == "$ff") {
sig_d = dff->getPort("\\D");
sig_q = dff->getPort("\\Q");
@@ -295,6 +305,14 @@ bool handle_dff(RTLIL::Module *mod, RTLIL::Cell *dff)
sig_c = dff->getPort("\\CLK");
val_cp = RTLIL::Const(dff->parameters["\\CLK_POLARITY"].as_bool(), 1);
}
+ else if (dff->type == "$dffe") {
+ sig_e = dff->getPort("\\EN");
+ 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);
+ val_ep = RTLIL::Const(dff->parameters["\\EN_POLARITY"].as_bool(), 1);
+ }
else if (dff->type == "$adff") {
sig_d = dff->getPort("\\D");
sig_q = dff->getPort("\\Q");
@@ -337,39 +355,60 @@ 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;
}
@@ -389,6 +428,30 @@ bool handle_dff(RTLIL::Module *mod, RTLIL::Cell *dff)
dff->unsetPort("\\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 == "$dffe") {
+ dff->type = "$dff";
+ dff->unsetPort("\\EN");
+ dff->unsetParam("\\EN_POLARITY");
+ return true;
+ }
+
+ log_assert(dff->type.substr(0,7) == "$_DFFE_");
+ dff->type = stringf("$_DFF_%c_", + dff->type[7]);
+ dff->unsetPort("\\E");
+ }
+
return false;
delete_dff:
@@ -489,7 +552,8 @@ struct OptRmdffPass : public Pass {
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"))
+ "$_DFFE_NN_", "$_DFFE_NP_", "$_DFFE_PN_", "$_DFFE_PP_",
+ "$ff", "$dff", "$dffe", "$adff"))
dff_list.push_back(cell->name);
if (cell->type.in("$dlatch", "$_DLATCH_P_", "$_DLATCH_N_"))
diff --git a/passes/opt/pmux2shiftx.cc b/passes/opt/pmux2shiftx.cc
new file mode 100644
index 000000000..29870f510
--- /dev/null
+++ b/passes/opt/pmux2shiftx.cc
@@ -0,0 +1,852 @@
+/*
+ * 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("\\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("$adff", "$dff", "$dffe", "$dlatch", "$ff"))
+ {
+ output = cell->getPort("\\Q");
+ if (cell->type == "$adff")
+ inputs.push_back(cell->getParam("\\ARST_VALUE"));
+ inputs.push_back(cell->getPort("\\D"));
+ }
+
+ if (cell->type.in("$mux", "$pmux"))
+ {
+ output = cell->getPort("\\Y");
+ inputs.push_back(cell->getPort("\\A"));
+ SigSpec B = cell->getPort("\\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");
+ }
+ 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;
+
+ 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;
+ }
+ 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 == "$eq")
+ {
+ dict<SigBit, State> bits;
+
+ SigSpec A = sigmap(cell->getPort("\\A"));
+ SigSpec B = sigmap(cell->getPort("\\B"));
+
+ int a_width = cell->getParam("\\A_WIDTH").as_int();
+ int b_width = cell->getParam("\\B_WIDTH").as_int();
+
+ if (a_width < b_width) {
+ bool a_signed = cell->getParam("\\A_SIGNED").as_int();
+ A.extend_u0(b_width, a_signed);
+ }
+
+ if (b_width < a_width) {
+ bool b_signed = cell->getParam("\\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("\\Y")[0])] = entry;
+ goto next_cell;
+ }
+
+ if (cell->type == "$logic_not")
+ {
+ dict<SigBit, State> bits;
+
+ SigSpec A = sigmap(cell->getPort("\\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("\\Y")[0])] = entry;
+ goto next_cell;
+ }
+ next_cell:;
+ }
+
+ for (auto cell : module->selected_cells())
+ {
+ if (cell->type != "$pmux")
+ continue;
+
+ string src = cell->get_src_attribute();
+ int width = cell->getParam("\\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("\\A");
+ SigSpec B = cell->getPort("\\B");
+ SigSpec S = sigmap(cell->getPort("\\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("\\S");
+ SigSpec updated_B = cell->getPort("\\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 (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 (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 (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("\\S", updated_S);
+ cell->setPort("\\B", updated_B);
+ cell->setParam("\\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 != "$eq")
+ continue;
+
+ SigSpec A = sigmap(cell->getPort("\\A"));
+ SigSpec B = sigmap(cell->getPort("\\B"));
+
+ int a_width = cell->getParam("\\A_WIDTH").as_int();
+ int b_width = cell->getParam("\\B_WIDTH").as_int();
+
+ if (a_width < b_width) {
+ bool a_signed = cell->getParam("\\A_SIGNED").as_int();
+ A.extend_u0(b_width, a_signed);
+ }
+
+ if (b_width < a_width) {
+ bool b_signed = cell->getParam("\\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("\\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/wreduce.cc b/passes/opt/wreduce.cc
index 09983bc67..1fbc41082 100644
--- a/passes/opt/wreduce.cc
+++ b/passes/opt/wreduce.cc
@@ -29,6 +29,7 @@ PRIVATE_NAMESPACE_BEGIN
struct WreduceConfig
{
pool<IdString> supported_cell_types;
+ bool keepdc = false;
WreduceConfig()
{
@@ -53,6 +54,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) { }
@@ -80,7 +83,7 @@ struct WreduceWorker
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 != Sx && sig_b[k*GetSize(sig_a) + i] != Sx)) && ref != sig_b[k*GetSize(sig_a) + i])
goto no_match_ab;
if (sig_b[k*GetSize(sig_a) + i] != Sx)
ref = sig_b[k*GetSize(sig_a) + i];
@@ -141,6 +144,7 @@ struct WreduceWorker
SigSpec sig_d = mi.sigmap(cell->getPort("\\D"));
SigSpec sig_q = mi.sigmap(cell->getPort("\\Q"));
+ Const initval;
int width_before = GetSize(sig_q);
@@ -150,24 +154,37 @@ struct WreduceWorker
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) {
+ if (zero_ext && sig_d[i] == State::S0 && (initval[i] == State::S0 || initval[i] == State::Sx)) {
module->connect(sig_q[i], State::S0);
+ remove_init_bits.insert(sig_q[i]);
sig_d.remove(i);
sig_q.remove(i);
continue;
}
- if (sign_ext && i > 0 && sig_d[i] == sig_d[i-1]) {
+ if (sign_ext && i > 0 && sig_d[i] == sig_d[i-1] && initval[i] == initval[i-1]) {
module->connect(sig_q[i], sig_q[i-1]);
+ remove_init_bits.insert(sig_q[i]);
sig_d.remove(i);
sig_q.remove(i);
continue;
}
auto info = mi.query(sig_q[i]);
- if (!info->is_output && GetSize(info->ports) <= 1 && !keep_bits.count(mi.sigmap(sig_q[i]))) {
+ 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;
@@ -183,10 +200,11 @@ struct WreduceWorker
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 mux cell %s.%s (%s).\n", width_before - GetSize(sig_q), width_before,
+ 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)
@@ -376,10 +394,21 @@ struct WreduceWorker
void run()
{
- for (auto w : module->wires())
+ // create a copy as mi.sigmap will be updated as we process the module
+ SigMap init_attr_sigmap = mi.sigmap;
+
+ for (auto w : module->wires()) {
if (w->get_bool_attribute("\\keep"))
for (auto bit : mi.sigmap(w))
keep_bits.insert(bit);
+ if (w->attributes.count("\\init")) {
+ Const initval = w->attributes.at("\\init");
+ SigSpec initsig = init_attr_sigmap(w);
+ int width = std::min(GetSize(initval), GetSize(initsig));
+ for (int i = 0; i < width; i++)
+ init_bits[initsig[i]] = initval[i];
+ }
+ }
for (auto c : module->selected_cells())
work_queue_cells.insert(c);
@@ -427,6 +456,22 @@ struct WreduceWorker
module->connect(nw, SigSpec(w).extract(0, GetSize(nw)));
module->swap_names(w, nw);
}
+
+ if (!remove_init_bits.empty()) {
+ for (auto w : module->wires()) {
+ if (w->attributes.count("\\init")) {
+ Const initval = w->attributes.at("\\init");
+ Const new_initval(State::Sx, GetSize(w));
+ SigSpec initsig = init_attr_sigmap(w);
+ int width = std::min(GetSize(initval), GetSize(initsig));
+ for (int i = 0; i < width; i++) {
+ if (!remove_init_bits.count(initsig[i]))
+ new_initval[i] = initval[i];
+ }
+ w->attributes.at("\\init") = new_initval;
+ }
+ }
+ }
}
};
@@ -451,6 +496,9 @@ 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");
}
void execute(std::vector<std::string> args, Design *design) YS_OVERRIDE
{
@@ -465,6 +513,10 @@ struct WreducePass : public Pass {
opt_memx = true;
continue;
}
+ if (args[argidx] == "-keepdc") {
+ config.keepdc = true;
+ continue;
+ }
break;
}
extra_args(args, argidx, design);
@@ -487,6 +539,42 @@ struct WreducePass : public Pass {
module->connect(sig, Const(0, GetSize(sig)));
}
}
+
+ if (c->type.in("$div", "$mod", "$pow"))
+ {
+ SigSpec A = c->getPort("\\A");
+ int original_a_width = GetSize(A);
+ if (c->getParam("\\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("\\A", A);
+ c->setParam("\\A_WIDTH", GetSize(A));
+ }
+
+ SigSpec B = c->getPort("\\B");
+ int original_b_width = GetSize(B);
+ if (c->getParam("\\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("\\B", B);
+ c->setParam("\\B_WIDTH", GetSize(B));
+ }
+ }
+
if (!opt_memx && c->type.in("$memrd", "$memwr", "$meminit")) {
IdString memid = c->getParam("\\MEMID").decode_string();
RTLIL::Memory *mem = module->memories.at(memid);
diff --git a/passes/pmgen/.gitignore b/passes/pmgen/.gitignore
index c9263057e..0ad36ea2c 100644
--- a/passes/pmgen/.gitignore
+++ b/passes/pmgen/.gitignore
@@ -1 +1,2 @@
/ice40_dsp_pm.h
+/peepopt_pm.h
diff --git a/passes/pmgen/Makefile.inc b/passes/pmgen/Makefile.inc
index 33baaca30..7911132db 100644
--- a/passes/pmgen/Makefile.inc
+++ b/passes/pmgen/Makefile.inc
@@ -1,8 +1,23 @@
OBJS += passes/pmgen/ice40_dsp.o
+OBJS += passes/pmgen/peepopt.o
+
+# --------------------------------------
passes/pmgen/ice40_dsp.o: passes/pmgen/ice40_dsp_pm.h
EXTRA_OBJS += passes/pmgen/ice40_dsp_pm.h
.SECONDARY: passes/pmgen/ice40_dsp_pm.h
-passes/pmgen/ice40_dsp_pm.h: passes/pmgen/ice40_dsp.pmg passes/pmgen/pmgen.py
- $(P) cd passes/pmgen && python3 pmgen.py ice40_dsp
+passes/pmgen/ice40_dsp_pm.h: passes/pmgen/pmgen.py passes/pmgen/ice40_dsp.pmg
+ $(P) mkdir -p passes/pmgen && python3 $< -o $@ -p ice40_dsp $(filter-out $<,$^)
+
+# --------------------------------------
+
+passes/pmgen/peepopt.o: passes/pmgen/peepopt_pm.h
+EXTRA_OBJS += passes/pmgen/peepopt_pm.h
+.SECONDARY: passes/pmgen/peepopt_pm.h
+
+PEEPOPT_PATTERN = passes/pmgen/peepopt_shiftmul.pmg
+PEEPOPT_PATTERN += passes/pmgen/peepopt_muldiv.pmg
+
+passes/pmgen/peepopt_pm.h: passes/pmgen/pmgen.py $(PEEPOPT_PATTERN)
+ $(P) mkdir -p passes/pmgen && python3 $< -o $@ -p peepopt $(filter-out $<,$^)
diff --git a/passes/pmgen/README.md b/passes/pmgen/README.md
index a1007dc62..2f0b1fd5a 100644
--- a/passes/pmgen/README.md
+++ b/passes/pmgen/README.md
@@ -16,7 +16,7 @@ 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 instanciated with an RTLIL module and a
+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());
@@ -29,19 +29,25 @@ up in any future matches:
pm.blacklist(some_cell);
-The `.run(callback_function)` method searches for all matches and calls the
-callback function for each found match:
+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([&](){
+ 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.<state_name>` members. (The `.st` member is of type `foobar_pm::state_t`.)
+`.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`, a struct of type `foobar_pm::udata_t`.
+`.ud_<pattern_name>`, a struct of type `foobar_pm::udata_<pattern_name>_t`.
+
+There are four versions of the `run_<pattern_name>()` method: Without callback,
+callback without arguments, callback with reference to `pm`, and callback with
+reference to `pm.st_<pattern_name>`.
The .pmg File Format
@@ -52,6 +58,12 @@ 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
-------------------------
@@ -66,7 +78,7 @@ State variables are automatically managed by the generated backtracking algorith
and saved and restored as needed.
They are automatically initialized to the default constructed value of their type
-when `.run(callback_function)` is called.
+when `.run_<pattern_name>(callback_function)` is called.
Declaring udata variables
-------------------------
@@ -83,8 +95,8 @@ They are declared like state variables, just using the `udata` statement:
udata <int> min_data_width max_data_width
udata <IdString> data_port_name
-They are atomatically initialzed to the default constructed value of their type
-when ther pattern matcher object is constructed.
+They are automatically initialized to the default constructed value of their type
+when the pattern matcher object is constructed.
Embedded C++ code
-----------------
@@ -142,7 +154,7 @@ 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
-`select` lines.
+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
@@ -158,7 +170,7 @@ Finally, `filter <expression>` narrows down the remaining list of cells. For
performance reasons `filter` statements should only be used for things that
can't be done using `select` and `index`.
-The `optional` statement marks optional matches. I.e. the matcher will also
+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`.
@@ -220,5 +232,5 @@ But in some cases it is more natural to utilize the implicit branch statement:
portAB = \B;
endcode
-There is an implicit `code..endcode` block at the end of each `.pgm` file
+There is an implicit `code..endcode` block at the end of each `.pmg` file
that just accepts everything that gets all the way there.
diff --git a/passes/pmgen/ice40_dsp.cc b/passes/pmgen/ice40_dsp.cc
index 3a054a463..39d033a04 100644
--- a/passes/pmgen/ice40_dsp.cc
+++ b/passes/pmgen/ice40_dsp.cc
@@ -19,47 +19,50 @@
#include "kernel/yosys.h"
#include "kernel/sigtools.h"
-#include "passes/pmgen/ice40_dsp_pm.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;
+
#if 0
log("\n");
- log("ffA: %s\n", log_id(pm.st.ffA, "--"));
- log("ffB: %s\n", log_id(pm.st.ffB, "--"));
- log("mul: %s\n", log_id(pm.st.mul, "--"));
- log("ffY: %s\n", log_id(pm.st.ffY, "--"));
- log("addAB: %s\n", log_id(pm.st.addAB, "--"));
- log("muxAB: %s\n", log_id(pm.st.muxAB, "--"));
- log("ffS: %s\n", log_id(pm.st.ffS, "--"));
+ log("ffA: %s\n", log_id(st.ffA, "--"));
+ log("ffB: %s\n", log_id(st.ffB, "--"));
+ log("mul: %s\n", log_id(st.mul, "--"));
+ log("ffY: %s\n", log_id(st.ffY, "--"));
+ log("addAB: %s\n", log_id(st.addAB, "--"));
+ log("muxAB: %s\n", log_id(st.muxAB, "--"));
+ log("ffS: %s\n", log_id(st.ffS, "--"));
#endif
- log("Checking %s.%s for iCE40 DSP inference.\n", log_id(pm.module), log_id(pm.st.mul));
+ log("Checking %s.%s for iCE40 DSP inference.\n", log_id(pm.module), log_id(st.mul));
- if (GetSize(pm.st.sigA) > 16) {
- log(" input A (%s) is too large (%d > 16).\n", log_signal(pm.st.sigA), GetSize(pm.st.sigA));
+ 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(pm.st.sigB) > 16) {
- log(" input B (%s) is too large (%d > 16).\n", log_signal(pm.st.sigB), GetSize(pm.st.sigB));
+ 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(pm.st.sigS) > 32) {
- log(" accumulator (%s) is too large (%d > 32).\n", log_signal(pm.st.sigS), GetSize(pm.st.sigS));
+ if (GetSize(st.sigS) > 32) {
+ log(" accumulator (%s) is too large (%d > 32).\n", log_signal(st.sigS), GetSize(st.sigS));
return;
}
- if (GetSize(pm.st.sigY) > 32) {
- log(" output (%s) is too large (%d > 32).\n", log_signal(pm.st.sigY), GetSize(pm.st.sigY));
+ if (GetSize(st.sigY) > 32) {
+ log(" output (%s) is too large (%d > 32).\n", log_signal(st.sigY), GetSize(st.sigY));
return;
}
- bool mul_signed = pm.st.mul->getParam("\\A_SIGNED").as_bool();
+ bool mul_signed = st.mul->getParam("\\A_SIGNED").as_bool();
if (mul_signed) {
log(" inference of signed iCE40 DSP arithmetic is currently not supported.\n");
@@ -69,21 +72,21 @@ void create_ice40_dsp(ice40_dsp_pm &pm)
log(" replacing $mul with SB_MAC16 cell.\n");
Cell *cell = pm.module->addCell(NEW_ID, "\\SB_MAC16");
- pm.module->swap_names(cell, pm.st.mul);
+ pm.module->swap_names(cell, st.mul);
// SB_MAC16 Input Interface
- SigSpec A = pm.st.sigA;
+ SigSpec A = st.sigA;
A.extend_u0(16, mul_signed);
- SigSpec B = pm.st.sigB;
+ SigSpec B = st.sigB;
B.extend_u0(16, mul_signed);
SigSpec CD;
- if (pm.st.muxA)
- CD = pm.st.muxA->getPort("\\B");
- if (pm.st.muxB)
- CD = pm.st.muxB->getPort("\\A");
+ if (st.muxA)
+ CD = st.muxA->getPort("\\B");
+ if (st.muxB)
+ CD = st.muxB->getPort("\\A");
CD.extend_u0(32, mul_signed);
cell->setPort("\\A", A);
@@ -91,8 +94,8 @@ void create_ice40_dsp(ice40_dsp_pm &pm)
cell->setPort("\\C", CD.extract(0, 16));
cell->setPort("\\D", CD.extract(16, 16));
- cell->setParam("\\A_REG", pm.st.ffA ? State::S1 : State::S0);
- cell->setParam("\\B_REG", pm.st.ffB ? State::S1 : State::S0);
+ cell->setParam("\\A_REG", st.ffA ? State::S1 : State::S0);
+ cell->setParam("\\B_REG", st.ffB ? State::S1 : State::S0);
cell->setPort("\\AHOLD", State::S0);
cell->setPort("\\BHOLD", State::S0);
@@ -102,25 +105,25 @@ void create_ice40_dsp(ice40_dsp_pm &pm)
cell->setPort("\\IRSTTOP", State::S0);
cell->setPort("\\IRSTBOT", State::S0);
- if (pm.st.clock_vld)
+ if (st.clock_vld)
{
- cell->setPort("\\CLK", pm.st.clock);
+ cell->setPort("\\CLK", st.clock);
cell->setPort("\\CE", State::S1);
- cell->setParam("\\NEG_TRIGGER", pm.st.clock_pol ? State::S0 : State::S1);
+ cell->setParam("\\NEG_TRIGGER", st.clock_pol ? State::S0 : State::S1);
- log(" clock: %s (%s)", log_signal(pm.st.clock), pm.st.clock_pol ? "posedge" : "negedge");
+ log(" clock: %s (%s)", log_signal(st.clock), st.clock_pol ? "posedge" : "negedge");
- if (pm.st.ffA)
- log(" ffA:%s", log_id(pm.st.ffA));
+ if (st.ffA)
+ log(" ffA:%s", log_id(st.ffA));
- if (pm.st.ffB)
- log(" ffB:%s", log_id(pm.st.ffB));
+ if (st.ffB)
+ log(" ffB:%s", log_id(st.ffB));
- if (pm.st.ffY)
- log(" ffY:%s", log_id(pm.st.ffY));
+ if (st.ffY)
+ log(" ffY:%s", log_id(st.ffY));
- if (pm.st.ffS)
- log(" ffS:%s", log_id(pm.st.ffS));
+ if (st.ffS)
+ log(" ffS:%s", log_id(st.ffS));
log("\n");
}
@@ -144,16 +147,16 @@ void create_ice40_dsp(ice40_dsp_pm &pm)
// SB_MAC16 Output Interface
- SigSpec O = pm.st.ffS ? pm.st.sigS : pm.st.sigY;
+ SigSpec O = st.ffS ? st.sigS : st.sigY;
if (GetSize(O) < 32)
O.append(pm.module->addWire(NEW_ID, 32-GetSize(O)));
cell->setPort("\\O", O);
- if (pm.st.addAB) {
- log(" accumulator %s (%s)\n", log_id(pm.st.addAB), log_id(pm.st.addAB->type));
- cell->setPort("\\ADDSUBTOP", pm.st.addAB->type == "$add" ? State::S0 : State::S1);
- cell->setPort("\\ADDSUBBOT", pm.st.addAB->type == "$add" ? State::S0 : State::S1);
+ if (st.addAB) {
+ log(" accumulator %s (%s)\n", log_id(st.addAB), log_id(st.addAB->type));
+ cell->setPort("\\ADDSUBTOP", st.addAB->type == "$add" ? State::S0 : State::S1);
+ cell->setPort("\\ADDSUBBOT", st.addAB->type == "$add" ? State::S0 : State::S1);
} else {
cell->setPort("\\ADDSUBTOP", State::S0);
cell->setPort("\\ADDSUBBOT", State::S0);
@@ -166,10 +169,10 @@ void create_ice40_dsp(ice40_dsp_pm &pm)
cell->setPort("\\OHOLDBOT", State::S0);
SigSpec acc_reset = State::S0;
- if (pm.st.muxA)
- acc_reset = pm.st.muxA->getPort("\\S");
- if (pm.st.muxB)
- acc_reset = pm.module->Not(NEW_ID, pm.st.muxB->getPort("\\S"));
+ if (st.muxA)
+ acc_reset = st.muxA->getPort("\\S");
+ if (st.muxB)
+ acc_reset = pm.module->Not(NEW_ID, st.muxB->getPort("\\S"));
cell->setPort("\\OLOADTOP", acc_reset);
cell->setPort("\\OLOADBOT", acc_reset);
@@ -179,17 +182,17 @@ void create_ice40_dsp(ice40_dsp_pm &pm)
cell->setParam("\\C_REG", State::S0);
cell->setParam("\\D_REG", State::S0);
- cell->setParam("\\TOP_8x8_MULT_REG", pm.st.ffY ? State::S1 : State::S0);
- cell->setParam("\\BOT_8x8_MULT_REG", pm.st.ffY ? State::S1 : State::S0);
- cell->setParam("\\PIPELINE_16x16_MULT_REG1", pm.st.ffY ? State::S1 : State::S0);
+ cell->setParam("\\TOP_8x8_MULT_REG", st.ffY ? State::S1 : State::S0);
+ cell->setParam("\\BOT_8x8_MULT_REG", st.ffY ? State::S1 : State::S0);
+ cell->setParam("\\PIPELINE_16x16_MULT_REG1", st.ffY ? State::S1 : State::S0);
cell->setParam("\\PIPELINE_16x16_MULT_REG2", State::S0);
- cell->setParam("\\TOPOUTPUT_SELECT", Const(pm.st.ffS ? 1 : 3, 2));
+ cell->setParam("\\TOPOUTPUT_SELECT", Const(st.ffS ? 1 : 3, 2));
cell->setParam("\\TOPADDSUB_LOWERINPUT", Const(2, 2));
cell->setParam("\\TOPADDSUB_UPPERINPUT", State::S0);
cell->setParam("\\TOPADDSUB_CARRYSELECT", Const(3, 2));
- cell->setParam("\\BOTOUTPUT_SELECT", Const(pm.st.ffS ? 1 : 3, 2));
+ cell->setParam("\\BOTOUTPUT_SELECT", Const(st.ffS ? 1 : 3, 2));
cell->setParam("\\BOTADDSUB_LOWERINPUT", Const(2, 2));
cell->setParam("\\BOTADDSUB_UPPERINPUT", State::S0);
cell->setParam("\\BOTADDSUB_CARRYSELECT", Const(0, 2));
@@ -198,9 +201,9 @@ void create_ice40_dsp(ice40_dsp_pm &pm)
cell->setParam("\\A_SIGNED", mul_signed ? State::S1 : State::S0);
cell->setParam("\\B_SIGNED", mul_signed ? State::S1 : State::S0);
- pm.autoremove(pm.st.mul);
- pm.autoremove(pm.st.ffY);
- pm.autoremove(pm.st.ffS);
+ pm.autoremove(st.mul);
+ pm.autoremove(st.ffY);
+ pm.autoremove(st.ffS);
}
struct Ice40DspPass : public Pass {
@@ -230,7 +233,7 @@ struct Ice40DspPass : public Pass {
extra_args(args, argidx, design);
for (auto module : design->selected_modules())
- ice40_dsp_pm(module, module->selected_cells()).run(create_ice40_dsp);
+ ice40_dsp_pm(module, module->selected_cells()).run_ice40_dsp(create_ice40_dsp);
}
} Ice40DspPass;
diff --git a/passes/pmgen/ice40_dsp.pmg b/passes/pmgen/ice40_dsp.pmg
index 96c62e313..1f3590d4e 100644
--- a/passes/pmgen/ice40_dsp.pmg
+++ b/passes/pmgen/ice40_dsp.pmg
@@ -1,3 +1,5 @@
+pattern ice40_dsp
+
state <SigBit> clock
state <bool> clock_pol clock_vld
state <SigSpec> sigA sigB sigY sigS
diff --git a/passes/pmgen/peepopt.cc b/passes/pmgen/peepopt.cc
new file mode 100644
index 000000000..e7f95cf85
--- /dev/null
+++ b/passes/pmgen/peepopt.cc
@@ -0,0 +1,68 @@
+/*
+ * 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;
+
+#include "passes/pmgen/peepopt_pm.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
+ {
+ log_header(design, "Executing PEEPOPT pass (run peephole optimizers).\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()) {
+ did_something = true;
+ while (did_something) {
+ did_something = false;
+ peepopt_pm pm(module, module->selected_cells());
+ pm.run_shiftmul();
+ pm.run_muldiv();
+ }
+ }
+ }
+} PeepoptPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/pmgen/peepopt_muldiv.pmg b/passes/pmgen/peepopt_muldiv.pmg
new file mode 100644
index 000000000..06c275834
--- /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);
+ reject;
+endcode
diff --git a/passes/pmgen/peepopt_shiftmul.pmg b/passes/pmgen/peepopt_shiftmul.pmg
new file mode 100644
index 000000000..6adab4e5f
--- /dev/null
+++ b/passes/pmgen/peepopt_shiftmul.pmg
@@ -0,0 +1,94 @@
+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);
+ reject;
+endcode
diff --git a/passes/pmgen/pmgen.py b/passes/pmgen/pmgen.py
index e688a4567..81052afce 100644
--- a/passes/pmgen/pmgen.py
+++ b/passes/pmgen/pmgen.py
@@ -3,11 +3,42 @@
import re
import sys
import pprint
+import getopt
pp = pprint.PrettyPrinter(indent=4)
-prefix = sys.argv[1]
-
+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
+patterns = dict()
state_types = dict()
udata_types = dict()
blocks = list()
@@ -73,7 +104,8 @@ def rewrite_cpp(s):
return "".join(t)
-with open("%s.pmg" % prefix, "r") as f:
+def process_pmgfile(f):
+ global current_pattern
while True:
line = f.readline()
if line == "": break
@@ -82,15 +114,32 @@ with open("%s.pmg" % prefix, "r") as f:
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
+ blocks.append(block)
+ line = line.split()
+ assert len(line) == 2
+ assert line[1] not in patterns
+ current_pattern = line[1]
+ patterns[current_pattern] = len(blocks)
+ state_types[current_pattern] = dict()
+ udata_types[current_pattern] = dict()
+ continue
+
+ assert current_pattern is not None
+
if cmd == "state":
m = re.match(r"^state\s+<(.*?)>\s+(([A-Za-z_][A-Za-z_0-9]*\s+)*[A-Za-z_][A-Za-z_0-9]*)\s*$", line)
assert m
type_str = m.group(1)
states_str = m.group(2)
for s in re.split(r"\s+", states_str):
- assert s not in state_types
- state_types[s] = type_str
+ assert s not in state_types[current_pattern]
+ state_types[current_pattern][s] = type_str
continue
if cmd == "udata":
@@ -99,19 +148,20 @@ with open("%s.pmg" % prefix, "r") as f:
type_str = m.group(1)
udatas_str = m.group(2)
for s in re.split(r"\s+", udatas_str):
- assert s not in udata_types
- udata_types[s] = type_str
+ 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["pattern"] = current_pattern
line = line.split()
assert len(line) == 2
- assert line[1] not in state_types
+ assert line[1] not in state_types[current_pattern]
block["cell"] = line[1]
- state_types[line[1]] = "Cell*";
+ state_types[current_pattern][line[1]] = "Cell*";
block["if"] = list()
block["select"] = list()
@@ -154,15 +204,18 @@ with open("%s.pmg" % prefix, "r") as f:
assert False
blocks.append(block)
+ continue
if cmd == "code":
block = dict()
block["type"] = "code"
+ block["pattern"] = current_pattern
+
block["code"] = list()
block["states"] = set()
for s in line.split()[1:]:
- assert s in state_types
+ assert s in state_types[current_pattern]
block["states"].add(s)
while True:
@@ -175,18 +228,37 @@ with open("%s.pmg" % prefix, "r") as f:
block["code"].append(rewrite_cpp(l.rstrip()))
blocks.append(block)
+ continue
-with open("%s_pm.h" % prefix, "w") as f:
- print("// Generated by pmgen.py from {}.pgm".format(prefix), file=f)
- print("", file=f)
+ assert False
- print("#include \"kernel/yosys.h\"", file=f)
- print("#include \"kernel/sigtools.h\"", file=f)
- print("", file=f)
+for fn in pmgfiles:
+ with open(fn, "r") as f:
+ process_pmgfile(f)
+
+if current_pattern is not None:
+ block = dict()
+ block["type"] = "final"
+ block["pattern"] = current_pattern
+ blocks.append(block)
+
+current_pattern = None
+
+if debug:
+ pp.pprint(blocks)
- print("YOSYS_NAMESPACE_BEGIN", file=f)
+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)
@@ -208,17 +280,19 @@ with open("%s_pm.h" % prefix, "w") as f:
print(" int rollback;", file=f)
print("", file=f)
- print(" struct state_t {", file=f)
- for s, t in sorted(state_types.items()):
- print(" {} {};".format(t, s), file=f)
- print(" } st;", file=f)
- print("", file=f)
+ 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 {", file=f)
- for s, t in sorted(udata_types.items()):
- print(" {} {};".format(t, s), file=f)
- print(" } ud;", 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] == "\\":
@@ -254,20 +328,24 @@ with open("%s_pm.h" % prefix, "w") as f:
print(" }", file=f)
print("", file=f)
- print(" void check_blacklist() {", file=f)
- print(" if (!blacklist_dirty)", file=f)
- print(" return;", file=f)
- print(" blacklist_dirty = false;", file=f)
- for index in range(len(blocks)):
- block = blocks[index]
- if block["type"] == "match":
- print(" if (st.{} != nullptr && blacklist_cells.count(st.{})) {{".format(block["cell"], block["cell"]), file=f)
- print(" rollback = {};".format(index+1), file=f)
- print(" return;", file=f)
- print(" }", file=f)
- print(" rollback = 0;", file=f)
- print(" }", file=f)
- print("", file=f)
+ for current_pattern in sorted(patterns.keys()):
+ print(" void check_blacklist_{}() {{".format(current_pattern), file=f)
+ print(" if (!blacklist_dirty)", file=f)
+ print(" return;", file=f)
+ print(" blacklist_dirty = false;", file=f)
+ for index in range(len(blocks)):
+ block = blocks[index]
+ if block["pattern"] != current_pattern:
+ continue
+ if block["type"] == "match":
+ print(" if (st_{}.{} != nullptr && blacklist_cells.count(st_{}.{})) {{".format(current_pattern, block["cell"], current_pattern, block["cell"]), file=f)
+ print(" rollback = {};".format(index+1), file=f)
+ print(" return;", file=f)
+ print(" }", file=f)
+ print(" rollback = 0;", file=f)
+ print(" }", file=f)
+ print("", file=f)
+ current_pattern = None
print(" SigSpec port(Cell *cell, IdString portname) {", file=f)
print(" return sigmap(cell->getPort(portname));", file=f)
@@ -290,11 +368,13 @@ with open("%s_pm.h" % prefix, "w") as f:
print(" {}_pm(Module *module, const vector<Cell*> &cells) :".format(prefix), file=f)
print(" module(module), sigmap(module) {", file=f)
- for s, t in sorted(udata_types.items()):
- if t.endswith("*"):
- print(" ud.{} = nullptr;".format(s), file=f)
- else:
- print(" ud.{} = {}();".format(s, t), file=f)
+ 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(" for (auto cell : module->cells()) {", file=f)
print(" for (auto &conn : cell->connections())", file=f)
print(" add_siguser(conn.second, cell);", file=f)
@@ -324,34 +404,52 @@ with open("%s_pm.h" % prefix, "w") as f:
print(" }", file=f)
print("", file=f)
- print(" void run(std::function<void()> on_accept_f) {", file=f)
- print(" on_accept = on_accept_f;", file=f)
- print(" rollback = 0;", file=f)
- print(" blacklist_dirty = false;", file=f)
- for s, t in sorted(state_types.items()):
- if t.endswith("*"):
- print(" st.{} = nullptr;".format(s), file=f)
- else:
- print(" st.{} = {}();".format(s, t), file=f)
- print(" block_0();", file=f)
- print(" }", file=f)
- print("", file=f)
-
- print(" void run(std::function<void({}_pm&)> on_accept_f) {{".format(prefix), file=f)
- print(" run([&](){on_accept_f(*this);});", file=f)
- print(" }", file=f)
- print("", file=f)
+ for current_pattern in sorted(patterns.keys()):
+ print(" void run_{}(std::function<void()> on_accept_f) {{".format(current_pattern), file=f)
+ print(" on_accept = on_accept_f;", file=f)
+ print(" rollback = 0;", file=f)
+ print(" blacklist_dirty = false;", file=f)
+ for s, t in sorted(state_types[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_{}();".format(patterns[current_pattern]), file=f)
+ print(" }", file=f)
+ print("", file=f)
+ print(" void run_{}(std::function<void({}_pm&)> on_accept_f) {{".format(current_pattern, prefix), file=f)
+ print(" run_{}([&](){{on_accept_f(*this);}});".format(current_pattern), file=f)
+ print(" }", file=f)
+ print("", file=f)
+ print(" void run_{}(std::function<void(state_{}_t&)> on_accept_f) {{".format(current_pattern, current_pattern), file=f)
+ print(" run_{}([&](){{on_accept_f(st_{});}});".format(current_pattern, current_pattern), file=f)
+ print(" }", file=f)
+ print("", file=f)
+ print(" void run_{}() {{".format(current_pattern), file=f)
+ print(" run_{}([](){{}});".format(current_pattern, current_pattern), file=f)
+ print(" }", file=f)
+ print("", file=f)
+ current_pattern = None
for index in range(len(blocks)):
block = blocks[index]
print(" void block_{}() {{".format(index), file=f)
+ current_pattern = block["pattern"]
+
+ if block["type"] == "final":
+ print(" on_accept();", file=f)
+ print(" check_blacklist_{}();".format(current_pattern), file=f)
+ print(" }", file=f)
+ if index+1 != len(blocks):
+ print("", file=f)
+ continue
const_st = set()
nonconst_st = set()
restore_st = set()
- for i in range(index):
+ for i in range(patterns[current_pattern], index):
if blocks[i]["type"] == "code":
for s in blocks[i]["states"]:
const_st.add(s)
@@ -374,27 +472,27 @@ with open("%s_pm.h" % prefix, "w") as f:
assert False
for s in sorted(const_st):
- t = state_types[s]
+ t = state_types[current_pattern][s]
if t.endswith("*"):
- print(" {} const &{} YS_ATTRIBUTE(unused) = st.{};".format(t, s, s), file=f)
+ print(" {} const &{} YS_ATTRIBUTE(unused) = st_{}.{};".format(t, s, current_pattern, s), file=f)
else:
- print(" const {} &{} YS_ATTRIBUTE(unused) = st.{};".format(t, s, s), file=f)
+ print(" const {} &{} YS_ATTRIBUTE(unused) = st_{}.{};".format(t, s, current_pattern, s), file=f)
for s in sorted(nonconst_st):
- t = state_types[s]
- print(" {} &{} YS_ATTRIBUTE(unused) = st.{};".format(t, s, s), file=f)
+ t = state_types[current_pattern][s]
+ print(" {} &{} YS_ATTRIBUTE(unused) = st_{}.{};".format(t, s, current_pattern, s), file=f)
if len(restore_st):
print("", file=f)
for s in sorted(restore_st):
- t = state_types[s]
+ t = state_types[current_pattern][s]
print(" {} backup_{} = {};".format(t, s, s), file=f)
if block["type"] == "code":
print("", file=f)
print(" do {", file=f)
- print("#define reject do { check_blacklist(); goto rollback_label; } while(0)", file=f)
- print("#define accept do { on_accept(); check_blacklist(); if (rollback) goto rollback_label; } while(0)", file=f)
+ print("#define reject do {{ check_blacklist_{}(); goto rollback_label; }} while(0)".format(current_pattern), file=f)
+ print("#define accept do {{ on_accept(); check_blacklist_{}(); if (rollback) goto rollback_label; }} while(0)".format(current_pattern), file=f)
print("#define branch do {{ block_{}(); if (rollback) goto rollback_label; }} while(0)".format(index+1), file=f)
for line in block["code"]:
@@ -413,11 +511,11 @@ with open("%s_pm.h" % prefix, "w") as f:
if len(restore_st) or len(nonconst_st):
print("", file=f)
for s in sorted(restore_st):
- t = state_types[s]
+ t = state_types[current_pattern][s]
print(" {} = backup_{};".format(s, s), file=f)
for s in sorted(nonconst_st):
if s not in restore_st:
- t = state_types[s]
+ t = state_types[current_pattern][s]
if t.endswith("*"):
print(" {} = nullptr;".format(s), file=f)
else:
@@ -466,17 +564,12 @@ with open("%s_pm.h" % prefix, "w") as f:
else:
assert False
-
+ current_pattern = None
print(" }", file=f)
print("", file=f)
- print(" void block_{}() {{".format(len(blocks)), file=f)
- print(" on_accept();", file=f)
- print(" check_blacklist();", file=f)
- print(" }", file=f)
print("};", file=f)
- print("", file=f)
- print("YOSYS_NAMESPACE_END", file=f)
-
-# pp.pprint(blocks)
+ if genhdr:
+ print("", file=f)
+ print("YOSYS_NAMESPACE_END", file=f)
diff --git a/passes/proc/proc_mux.cc b/passes/proc/proc_mux.cc
index 1329c1fef..aac0b121c 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;
@@ -268,6 +269,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 +381,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;
diff --git a/passes/proc/proc_rmdead.cc b/passes/proc/proc_rmdead.cc
index 7c334e661..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,11 +56,16 @@ 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 {
@@ -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 8ab0280c0..fc3ac879e 100644
--- a/passes/sat/Makefile.inc
+++ b/passes/sat/Makefile.inc
@@ -8,4 +8,8 @@ OBJS += passes/sat/expose.o
OBJS += passes/sat/assertpmux.o
OBJS += passes/sat/clk2fflogic.o
OBJS += passes/sat/async2sync.o
+OBJS += passes/sat/supercover.o
+OBJS += passes/sat/fmcombine.o
+OBJS += passes/sat/mutate.o
+OBJS += passes/sat/cutpoint.o
diff --git a/passes/sat/async2sync.cc b/passes/sat/async2sync.cc
index c92db7118..d045d0dcb 100644
--- a/passes/sat/async2sync.cc
+++ b/passes/sat/async2sync.cc
@@ -39,7 +39,7 @@ struct Async2syncPass : public Pass {
log("reset value in the next cycle regardless of the data-in value at the time of\n");
log("the clock edge.\n");
log("\n");
- log("Currently only $adff cells are supported by this pass.\n");
+ log("Currently only $adff and $dffsr cells are supported by this pass.\n");
log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
@@ -84,7 +84,7 @@ struct Async2syncPass : public Pass {
bool arst_pol = cell->parameters["\\ARST_POLARITY"].as_bool();
Const arst_val = cell->parameters["\\ARST_VALUE"];
- SigSpec sig_clk = cell->getPort("\\CLK");
+ // SigSpec sig_clk = cell->getPort("\\CLK");
SigSpec sig_arst = cell->getPort("\\ARST");
SigSpec sig_d = cell->getPort("\\D");
SigSpec sig_q = cell->getPort("\\Q");
@@ -120,6 +120,55 @@ struct Async2syncPass : public Pass {
cell->type = "$dff";
continue;
}
+
+ if (cell->type.in("$dffsr"))
+ {
+ // bool clk_pol = cell->parameters["\\CLK_POLARITY"].as_bool();
+ bool set_pol = cell->parameters["\\SET_POLARITY"].as_bool();
+ bool clr_pol = cell->parameters["\\CLR_POLARITY"].as_bool();
+
+ // SigSpec sig_clk = cell->getPort("\\CLK");
+ SigSpec sig_set = cell->getPort("\\SET");
+ SigSpec sig_clr = cell->getPort("\\CLR");
+ SigSpec sig_d = cell->getPort("\\D");
+ SigSpec sig_q = cell->getPort("\\Q");
+
+ log("Replacing %s.%s (%s): SET=%s, CLR=%s, D=%s, Q=%s\n",
+ log_id(module), log_id(cell), log_id(cell->type),
+ log_signal(sig_set), log_signal(sig_clr), log_signal(sig_d), log_signal(sig_q));
+
+ Const init_val;
+ for (int i = 0; i < GetSize(sig_q); i++) {
+ SigBit bit = sigmap(sig_q[i]);
+ init_val.bits.push_back(initbits.count(bit) ? initbits.at(bit) : State::Sx);
+ del_initbits.insert(bit);
+ }
+
+ Wire *new_d = module->addWire(NEW_ID, GetSize(sig_d));
+ Wire *new_q = module->addWire(NEW_ID, GetSize(sig_q));
+ new_q->attributes["\\init"] = init_val;
+
+ if (!set_pol)
+ sig_set = module->Not(NEW_ID, sig_set);
+
+ if (clr_pol)
+ sig_clr = module->Not(NEW_ID, sig_clr);
+
+ SigSpec tmp = module->Or(NEW_ID, sig_d, sig_set);
+ module->addAnd(NEW_ID, tmp, sig_clr, new_d);
+
+ tmp = module->Or(NEW_ID, new_q, sig_set);
+ module->addAnd(NEW_ID, tmp, sig_clr, sig_q);
+
+ cell->setPort("\\D", new_d);
+ cell->setPort("\\Q", new_q);
+ cell->unsetPort("\\SET");
+ cell->unsetPort("\\CLR");
+ cell->unsetParam("\\SET_POLARITY");
+ cell->unsetParam("\\CLR_POLARITY");
+ cell->type = "$dff";
+ continue;
+ }
}
for (auto wire : module->wires())
diff --git a/passes/sat/cutpoint.cc b/passes/sat/cutpoint.cc
new file mode 100644
index 000000000..048aec7f3
--- /dev/null
+++ b/passes/sat/cutpoint.cc
@@ -0,0 +1,168 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "kernel/yosys.h"
+#include "kernel/sigtools.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct CutpointPass : public Pass {
+ CutpointPass() : Pass("cutpoint", "add hi/lo cover cells for each wire bit") { }
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" cutpoint [options] [selection]\n");
+ log("\n");
+ log("This command adds formal cut points to the design.\n");
+ log("\n");
+ log(" -undef\n");
+ log(" set cupoint nets to undef (x). the default behavior is to create a\n");
+ log(" $anyseq cell and drive the cutpoint net from that\n");
+ log("\n");
+ }
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ bool flag_undef = false;
+
+ log_header(design, "Executing CUTPOINT pass.\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ if (args[argidx] == "-undef") {
+ flag_undef = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto module : design->selected_modules())
+ {
+ if (design->selected_whole_module(module->name)) {
+ log("Making all outputs of module %s cut points, removing module contents.\n", log_id(module));
+ module->new_connections(std::vector<RTLIL::SigSig>());
+ for (auto cell : vector<Cell*>(module->cells()))
+ module->remove(cell);
+ vector<Wire*> output_wires;
+ for (auto wire : module->wires())
+ if (wire->port_output)
+ output_wires.push_back(wire);
+ for (auto wire : output_wires)
+ module->connect(wire, flag_undef ? Const(State::Sx, GetSize(wire)) : module->Anyseq(NEW_ID, GetSize(wire)));
+ continue;
+ }
+
+ SigMap sigmap(module);
+ pool<SigBit> cutpoint_bits;
+
+ for (auto cell : module->selected_cells()) {
+ if (cell->type == "$anyseq")
+ continue;
+ log("Removing cell %s.%s, making all cell outputs cutpoints.\n", log_id(module), log_id(cell));
+ for (auto &conn : cell->connections()) {
+ if (cell->output(conn.first))
+ module->connect(conn.second, flag_undef ? Const(State::Sx, GetSize(conn.second)) : module->Anyseq(NEW_ID, GetSize(conn.second)));
+ }
+ module->remove(cell);
+ }
+
+ for (auto wire : module->selected_wires()) {
+ if (wire->port_output) {
+ log("Making output wire %s.%s a cutpoint.\n", log_id(module), log_id(wire));
+ Wire *new_wire = module->addWire(NEW_ID, wire);
+ module->swap_names(wire, new_wire);
+ module->connect(new_wire, flag_undef ? Const(State::Sx, GetSize(new_wire)) : module->Anyseq(NEW_ID, GetSize(new_wire)));
+ wire->port_id = 0;
+ wire->port_input = false;
+ wire->port_output = false;
+ continue;
+ }
+ log("Making wire %s.%s a cutpoint.\n", log_id(module), log_id(wire));
+ for (auto bit : sigmap(wire))
+ cutpoint_bits.insert(bit);
+ }
+
+ if (!cutpoint_bits.empty())
+ {
+ for (auto cell : module->cells()) {
+ for (auto &conn : cell->connections()) {
+ if (!cell->output(conn.first))
+ continue;
+ SigSpec sig = sigmap(conn.second);
+ int bit_count = 0;
+ for (auto &bit : sig) {
+ if (cutpoint_bits.count(bit))
+ bit_count++;
+ }
+ if (bit_count == 0)
+ continue;
+ SigSpec dummy = module->addWire(NEW_ID, bit_count);
+ bit_count = 0;
+ for (auto &bit : sig) {
+ if (cutpoint_bits.count(bit))
+ bit = dummy[bit_count++];
+ }
+ cell->setPort(conn.first, sig);
+ }
+ }
+
+ vector<Wire*> rewrite_wires;
+ for (auto wire : module->wires()) {
+ if (!wire->port_input)
+ continue;
+ int bit_count = 0;
+ for (auto &bit : sigmap(wire))
+ if (cutpoint_bits.count(bit))
+ bit_count++;
+ if (bit_count)
+ rewrite_wires.push_back(wire);
+ }
+
+ for (auto wire : rewrite_wires) {
+ Wire *new_wire = module->addWire(NEW_ID, wire);
+ SigSpec lhs, rhs, sig = sigmap(wire);
+ for (int i = 0; i < GetSize(sig); i++)
+ if (!cutpoint_bits.count(sig[i])) {
+ lhs.append(SigBit(wire, i));
+ rhs.append(SigBit(new_wire, i));
+ }
+ if (GetSize(lhs))
+ module->connect(lhs, rhs);
+ module->swap_names(wire, new_wire);
+ wire->port_id = 0;
+ wire->port_input = false;
+ wire->port_output = false;
+ }
+
+ SigSpec sig(cutpoint_bits);
+ sig.sort_and_unify();
+
+ for (auto chunk : sig.chunks()) {
+ SigSpec s(chunk);
+ module->connect(s, flag_undef ? Const(State::Sx, GetSize(s)) : module->Anyseq(NEW_ID, GetSize(s)));
+ }
+ }
+ }
+ }
+} CutpointPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/sat/expose.cc b/passes/sat/expose.cc
index 809345486..71ce1683d 100644
--- a/passes/sat/expose.cc
+++ b/passes/sat/expose.cc
@@ -508,7 +508,7 @@ struct ExposePass : public Pass {
}
for (auto &conn : module->connections_)
- conn.first = out_to_in_map(sigmap(conn.first));
+ 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/miter.cc b/passes/sat/miter.cc
index d37f1b126..1a886af70 100644
--- a/passes/sat/miter.cc
+++ b/passes/sat/miter.cc
@@ -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();
}
}
@@ -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();
}
@@ -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,7 +399,7 @@ 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");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
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 695a03e15..cbba738f0 100644
--- a/passes/sat/sat.cc
+++ b/passes/sat/sat.cc
@@ -1169,6 +1169,7 @@ struct SatPass : public Pass {
if (args[argidx] == "-tempinduct-def") {
tempinduct = true;
tempinduct_def = true;
+ enable_undef = true;
continue;
}
if (args[argidx] == "-tempinduct-baseonly") {
diff --git a/passes/sat/sim.cc b/passes/sat/sim.cc
index 53e248adf..4c3022c70 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;
@@ -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/abc.cc b/passes/techmap/abc.cc
index 21b70f492..5b19d84fb 100644
--- a/passes/techmap/abc.cc
+++ b/passes/techmap/abc.cc
@@ -29,17 +29,17 @@
// Kahn, Arthur B. (1962), "Topological sorting of large networks", Communications of the ACM 5 (11): 558-562, doi:10.1145/368996.369025
// http://en.wikipedia.org/wiki/Topological_sorting
-#define ABC_COMMAND_LIB "strash; ifraig; scorr; dc2; dretime; strash; &get -n; &dch -f; &nf {D}; &put"
-#define ABC_COMMAND_CTR "strash; ifraig; scorr; dc2; dretime; strash; &get -n; &dch -f; &nf {D}; &put; buffer; upsize {D}; dnsize {D}; stime -p"
-#define ABC_COMMAND_LUT "strash; ifraig; scorr; dc2; dretime; strash; dch -f; if; mfs2"
-#define ABC_COMMAND_SOP "strash; ifraig; scorr; dc2; dretime; strash; dch -f; cover {I} {P}"
-#define ABC_COMMAND_DFL "strash; ifraig; scorr; dc2; dretime; strash; &get -n; &dch -f; &nf {D}; &put"
-
-#define ABC_FAST_COMMAND_LIB "strash; dretime; map {D}"
-#define ABC_FAST_COMMAND_CTR "strash; dretime; map {D}; buffer; upsize {D}; dnsize {D}; stime -p"
-#define ABC_FAST_COMMAND_LUT "strash; dretime; if"
-#define ABC_FAST_COMMAND_SOP "strash; dretime; cover -I {I} -P {P}"
-#define ABC_FAST_COMMAND_DFL "strash; dretime; map"
+#define ABC_COMMAND_LIB "strash; ifraig; scorr; dc2; dretime; retime {D}; strash; &get -n; &dch -f; &nf {D}; &put"
+#define ABC_COMMAND_CTR "strash; ifraig; scorr; dc2; dretime; retime {D}; strash; &get -n; &dch -f; &nf {D}; &put; buffer; upsize {D}; dnsize {D}; stime -p"
+#define ABC_COMMAND_LUT "strash; ifraig; scorr; dc2; dretime; retime {D}; strash; dch -f; if; mfs2"
+#define ABC_COMMAND_SOP "strash; ifraig; scorr; dc2; dretime; retime {D}; strash; dch -f; cover {I} {P}"
+#define ABC_COMMAND_DFL "strash; ifraig; scorr; dc2; dretime; retime {D}; strash; &get -n; &dch -f; &nf {D}; &put"
+
+#define ABC_FAST_COMMAND_LIB "strash; dretime; retime {D}; map {D}"
+#define ABC_FAST_COMMAND_CTR "strash; dretime; retime {D}; map {D}; buffer; upsize {D}; dnsize {D}; stime -p"
+#define ABC_FAST_COMMAND_LUT "strash; dretime; retime {D}; if"
+#define ABC_FAST_COMMAND_SOP "strash; dretime; retime {D}; cover -I {I} -P {P}"
+#define ABC_FAST_COMMAND_DFL "strash; dretime; retime {D}; map"
#include "kernel/register.h"
#include "kernel/sigtools.h"
@@ -330,20 +330,37 @@ void extract_cell(RTLIL::Cell *cell, bool keepff)
std::string remap_name(RTLIL::IdString abc_name, RTLIL::Wire **orig_wire = nullptr)
{
std::string abc_sname = abc_name.substr(1);
- if (abc_sname.substr(0, 5) == "ys__n") {
- int sid = std::stoi(abc_sname.substr(5));
- bool inv = abc_sname.back() == 'v';
- for (auto sig : signal_list) {
- if (sig.id == sid && sig.bit.wire != nullptr) {
- std::stringstream sstr;
- sstr << "$abc$" << map_autoidx << "$" << sig.bit.wire->name.substr(1);
- if (sig.bit.wire->width != 1)
- sstr << "[" << sig.bit.offset << "]";
- if (inv)
- sstr << "_inv";
- if (orig_wire != nullptr)
- *orig_wire = sig.bit.wire;
- return sstr.str();
+ bool isnew = false;
+ if (abc_sname.substr(0, 4) == "new_")
+ {
+ abc_sname.erase(0, 4);
+ isnew = true;
+ }
+ if (abc_sname.substr(0, 5) == "ys__n")
+ {
+ abc_sname.erase(0, 5);
+ if (std::isdigit(abc_sname.at(0)))
+ {
+ int sid = std::stoi(abc_sname);
+ 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::stringstream sstr;
+ sstr << "$abc$" << map_autoidx << "$" << sig.bit.wire->name.substr(1);
+ if (sig.bit.wire->width != 1)
+ sstr << "[" << sig.bit.offset << "]";
+ if (isnew)
+ sstr << "_new";
+ sstr << postfix;
+ if (orig_wire != nullptr)
+ *orig_wire = sig.bit.wire;
+ return sstr.str();
+ }
}
}
}
@@ -731,10 +748,6 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin
else
abc_script += fast_mode ? ABC_FAST_COMMAND_DFL : ABC_COMMAND_DFL;
- if (script_file.empty() && !delay_target.empty())
- for (size_t pos = abc_script.find("dretime;"); pos != std::string::npos; pos = abc_script.find("dretime;", pos+1))
- abc_script = abc_script.substr(0, pos) + "dretime; retime -o {D};" + abc_script.substr(pos+8);
-
for (size_t pos = abc_script.find("{D}"); pos != std::string::npos; pos = abc_script.find("{D}", pos))
abc_script = abc_script.substr(0, pos) + delay_target + abc_script.substr(pos+3);
@@ -1726,7 +1739,7 @@ struct AbcPass : public Pass {
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;
diff --git a/passes/techmap/attrmap.cc b/passes/techmap/attrmap.cc
index 0b5576b06..aa48e1125 100644
--- a/passes/techmap/attrmap.cc
+++ b/passes/techmap/attrmap.cc
@@ -111,9 +111,10 @@ struct AttrmapMap : AttrmapAction {
};
struct AttrmapRemove : AttrmapAction {
+ bool has_value;
string name, value;
bool apply(IdString &id, Const &val) YS_OVERRIDE {
- return !(match_name(name, id) && match_value(value, val));
+ return !(match_name(name, id) && (!has_value || match_value(value, val)));
}
};
@@ -235,6 +236,7 @@ struct AttrmapPass : public Pass {
}
auto action = new AttrmapRemove;
action->name = arg1;
+ action->has_value = (p != string::npos);
action->value = val1;
actions.push_back(std::unique_ptr<AttrmapAction>(action));
continue;
diff --git a/passes/techmap/dffinit.cc b/passes/techmap/dffinit.cc
index 48390488e..0ad33dc0e 100644
--- a/passes/techmap/dffinit.cc
+++ b/passes/techmap/dffinit.cc
@@ -102,7 +102,8 @@ struct DffinitPass : public Pass {
if (wire->attributes.count("\\init")) {
Const value = wire->attributes.at("\\init");
for (int i = 0; i < min(GetSize(value), GetSize(wire)); i++)
- init_bits[sigmap(SigBit(wire, i))] = value[i];
+ if (value[i] != State::Sx)
+ init_bits[sigmap(SigBit(wire, i))] = value[i];
}
if (wire->port_output)
for (auto bit : sigmap(wire))
diff --git a/passes/techmap/dfflibmap.cc b/passes/techmap/dfflibmap.cc
index 274177a68..b5c0498d0 100644
--- a/passes/techmap/dfflibmap.cc
+++ b/passes/techmap/dfflibmap.cc
@@ -664,7 +664,7 @@ struct DfflibmapPass : public Pass {
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/flowmap.cc b/passes/techmap/flowmap.cc
index 0b7931e48..f5892a60e 100644
--- a/passes/techmap/flowmap.cc
+++ b/passes/techmap/flowmap.cc
@@ -397,7 +397,6 @@ struct FlowGraph
pool<RTLIL::SigBit> x, xi;
NodePrime source_prime = {source, true};
- NodePrime sink_prime = {sink, false};
pool<NodePrime> visited;
vector<NodePrime> worklist = {source_prime};
while (!worklist.empty())
@@ -1382,7 +1381,8 @@ struct FlowmapWorker
vector<RTLIL::SigBit> input_nodes(lut_edges_bw[node].begin(), lut_edges_bw[node].end());
RTLIL::Const lut_table(State::Sx, max(1 << input_nodes.size(), 1 << minlut));
- for (unsigned i = 0; i < (1 << input_nodes.size()); i++)
+ 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++)
diff --git a/passes/techmap/libparse.cc b/passes/techmap/libparse.cc
index 3927a657b..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,15 +87,17 @@ int LibertyParser::lexer(std::string &str)
{
int c;
+ // eat whitespace
do {
c = f.get();
} while (c == ' ' || c == '\t' || c == '\r');
- if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') || c == '_' || c == '-' || c == '+' || c == '.' || c == '[' || c == ']') {
- str = c;
+ // 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 = 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 == '.' || c == '[' || c == ']')
+ if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') || c == '_' || c == '-' || c == '+' || c == '.')
str += c;
else
break;
@@ -111,6 +114,8 @@ int LibertyParser::lexer(std::string &str)
}
}
+ // if it wasn't an identifer, number of array range,
+ // maybe it's a string?
if (c == '"') {
str = "";
while (1) {
@@ -125,9 +130,10 @@ int LibertyParser::lexer(std::string &str)
return 'v';
}
+ // if it wasn't a string, perhaps it's a comment or a forward slash?
if (c == '/') {
c = f.get();
- if (c == '*') {
+ if (c == '*') { // start of '/*' block comment
int last_c = 0;
while (c > 0 && (last_c != '*' || c != '/')) {
last_c = c;
@@ -136,7 +142,7 @@ int LibertyParser::lexer(std::string &str)
line++;
}
return lexer(str);
- } else if (c == '/') {
+ } else if (c == '/') { // start of '//' line comment
while (c > 0 && c != '\n')
c = f.get();
line++;
@@ -144,24 +150,31 @@ int LibertyParser::lexer(std::string &str)
}
f.unget();
// fprintf(stderr, "LEX: char >>/<<\n");
- return '/';
+ return '/'; // a single '/' charater.
}
+ // check for a backslash
if (c == '\\') {
- c = f.get();
+ c = f.get();
if (c == '\r')
c = f.get();
- if (c == '\n')
+ if (c == '\n') {
+ line++;
return lexer(str);
+ }
f.unget();
return '\\';
}
+ // check for a new line
if (c == '\n') {
line++;
return 'n';
}
+ // anything else, such as ';' will get passed
+ // through as literal items.
+
// if (c >= 32 && c < 255)
// fprintf(stderr, "LEX: char >>%c<<\n", c);
// else
@@ -175,14 +188,39 @@ LibertyAst *LibertyParser::parse()
int tok = lexer(str);
- while (tok == 'n')
+ // there are liberty files in the wild that
+ // have superfluous ';' at the end of
+ // a { ... }. We simply ignore a ';' here.
+ // and get to the next statement.
+
+ while ((tok == 'n') || (tok == ';'))
tok = lexer(str);
if (tok == '}' || tok < 0)
return NULL;
- if (tok != 'v')
- error();
+ if (tok != 'v') {
+ std::string eReport;
+ switch(tok)
+ {
+ case 'n':
+ error("Unexpected newline.");
+ break;
+ case '[':
+ case ']':
+ case '}':
+ case '{':
+ case '\"':
+ case ':':
+ eReport = "Unexpected '";
+ eReport += static_cast<char>(tok);
+ eReport += "'.";
+ error(eReport);
+ break;
+ default:
+ error();
+ }
+ }
LibertyAst *ast = new LibertyAst;
ast->id = str;
@@ -191,12 +229,11 @@ LibertyAst *LibertyParser::parse()
{
tok = lexer(str);
- if (tok == ';')
+ // allow both ';' and new lines to
+ // terminate a statement.
+ if ((tok == ';') || (tok == 'n'))
break;
- if (tok == 'n')
- continue;
-
if (tok == ':' && ast->value.empty()) {
tok = lexer(ast->value);
if (tok != 'v')
@@ -210,7 +247,12 @@ LibertyAst *LibertyParser::parse()
ast->value += str;
tok = lexer(str);
}
- if (tok == ';')
+
+ // In a liberty file, all key : value pairs should end in ';'
+ // However, there are some liberty files in the wild that
+ // just have a newline. We'll be kind and accept a newline
+ // instead of the ';' too..
+ if ((tok == ';') || (tok == 'n'))
break;
else
error();
@@ -225,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;
@@ -255,6 +359,14 @@ void LibertyParser::error()
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()
@@ -263,25 +375,34 @@ void LibertyParser::error()
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);
+}
+
/**** BEGIN: http://svn.clifford.at/tools/trunk/examples/check.h ****/
#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/muxcover.cc b/passes/techmap/muxcover.cc
index 12da9ed0c..32102436d 100644
--- a/passes/techmap/muxcover.cc
+++ b/passes/techmap/muxcover.cc
@@ -58,12 +58,21 @@ struct MuxcoverWorker
bool use_mux16;
bool nodecode;
+ int cost_mux2;
+ int cost_mux4;
+ int cost_mux8;
+ int cost_mux16;
+
MuxcoverWorker(Module *module) : module(module), sigmap(module)
{
use_mux4 = false;
use_mux8 = false;
use_mux16 = false;
nodecode = false;
+ cost_mux2 = COST_MUX2;
+ cost_mux4 = COST_MUX4;
+ cost_mux8 = COST_MUX8;
+ cost_mux16 = COST_MUX16;
decode_mux_counter = 0;
}
@@ -157,7 +166,7 @@ struct MuxcoverWorker
if (std::get<2>(entry))
return 0;
- return COST_MUX2 / GetSize(std::get<1>(entry));
+ return cost_mux2 / GetSize(std::get<1>(entry));
}
void implement_decode_mux(SigBit ctrl_bit)
@@ -209,7 +218,7 @@ struct MuxcoverWorker
mux.inputs.push_back(B);
mux.selects.push_back(S1);
- mux.cost += COST_MUX2;
+ mux.cost += cost_mux2;
mux.cost += find_best_cover(tree, A);
mux.cost += find_best_cover(tree, B);
@@ -247,7 +256,7 @@ struct MuxcoverWorker
mux.selects.push_back(S1);
mux.selects.push_back(T1);
- mux.cost += COST_MUX4;
+ 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);
@@ -310,7 +319,7 @@ struct MuxcoverWorker
mux.selects.push_back(T1);
mux.selects.push_back(U1);
- mux.cost += COST_MUX8;
+ 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);
@@ -414,7 +423,7 @@ struct MuxcoverWorker
mux.selects.push_back(U1);
mux.selects.push_back(V1);
- mux.cost += COST_MUX16;
+ 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);
@@ -569,9 +578,11 @@ 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(" Use the specified types of MUXes (with optional integer costs). If none\n");
+ log(" of these options are given, the effect is the same as if all of them are.\n");
+ log(" Default costs: $_MUX_ = %d, $_MUX4_ = %d,\n", COST_MUX2, COST_MUX4);
+ log(" $_MUX8_ = %d, $_MUX16_ = %d\n", COST_MUX8, COST_MUX16);
log("\n");
log(" -nodecode\n");
log(" Do not insert decoder logic. This reduces the number of possible\n");
@@ -587,23 +598,39 @@ struct MuxcoverPass : public Pass {
bool use_mux8 = false;
bool use_mux16 = false;
bool nodecode = false;
+ 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() >= 5 && arg.substr(0,5) == "-mux4") {
use_mux4 = true;
+ if (arg.size() > 5) {
+ if (arg[5] != '=') break;
+ cost_mux4 = atoi(arg.substr(5).c_str());
+ }
continue;
}
- if (args[argidx] == "-mux8") {
+ if (arg.size() >= 5 && arg.substr(0,5) == "-mux8") {
use_mux8 = true;
+ if (arg.size() > 5) {
+ if (arg[5] != '=') break;
+ cost_mux8 = atoi(arg.substr(5).c_str());
+ }
continue;
}
- if (args[argidx] == "-mux16") {
+ if (arg.size() >= 6 && arg.substr(0,6) == "-mux16") {
use_mux16 = true;
+ if (arg.size() > 6) {
+ if (arg[6] != '=') break;
+ cost_mux16 = atoi(arg.substr(6).c_str());
+ }
continue;
}
- if (args[argidx] == "-nodecode") {
+ if (arg == "-nodecode") {
nodecode = true;
continue;
}
@@ -623,6 +650,9 @@ struct MuxcoverPass : public Pass {
worker.use_mux4 = use_mux4;
worker.use_mux8 = use_mux8;
worker.use_mux16 = use_mux16;
+ worker.cost_mux4 = cost_mux4;
+ worker.cost_mux8 = cost_mux8;
+ worker.cost_mux16 = cost_mux16;
worker.nodecode = nodecode;
worker.run();
}
diff --git a/passes/techmap/pmuxtree.cc b/passes/techmap/pmuxtree.cc
index b7a22dc3b..6a923f481 100644
--- a/passes/techmap/pmuxtree.cc
+++ b/passes/techmap/pmuxtree.cc
@@ -71,9 +71,9 @@ struct PmuxtreePass : public Pass {
{
// |---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");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
diff --git a/passes/techmap/shregmap.cc b/passes/techmap/shregmap.cc
index f20863ba0..21dfe9619 100644
--- a/passes/techmap/shregmap.cc
+++ b/passes/techmap/shregmap.cc
@@ -26,7 +26,9 @@ PRIVATE_NAMESPACE_BEGIN
struct ShregmapTech
{
virtual ~ShregmapTech() { }
- virtual bool analyze(vector<int> &taps) = 0;
+ virtual void init(const Module * /*module*/, const SigMap &/*sigmap*/) {}
+ virtual void non_chain_user(const SigBit &/*bit*/, const Cell* /*cell*/, IdString /*port*/) {}
+ virtual bool analyze(vector<int> &taps, const vector<SigBit> &qbits) = 0;
virtual bool fixup(Cell *cell, dict<int, SigBit> &taps) = 0;
};
@@ -54,7 +56,7 @@ struct ShregmapOptions
struct ShregmapTechGreenpak4 : ShregmapTech
{
- bool analyze(vector<int> &taps)
+ bool analyze(vector<int> &taps, const vector<SigBit> &/*qbits*/)
{
if (GetSize(taps) > 2 && taps[0] == 0 && taps[2] < 17) {
taps.clear();
@@ -91,6 +93,155 @@ struct ShregmapTechGreenpak4 : ShregmapTech
}
};
+struct ShregmapTechXilinx7 : ShregmapTech
+{
+ dict<SigBit, std::tuple<Cell*,int,int>> sigbit_to_shiftx_offset;
+ const ShregmapOptions &opts;
+
+ ShregmapTechXilinx7(const ShregmapOptions &opts) : opts(opts) {}
+
+ virtual void init(const Module* module, const SigMap &sigmap) override
+ {
+ for (const auto &i : module->cells_) {
+ auto cell = i.second;
+ if (cell->type == "$shiftx") {
+ if (cell->getParam("\\Y_WIDTH") != 1) continue;
+ int j = 0;
+ for (auto bit : sigmap(cell->getPort("\\A")))
+ sigbit_to_shiftx_offset[bit] = std::make_tuple(cell, j++, 0);
+ log_assert(j == cell->getParam("\\A_WIDTH").as_int());
+ }
+ else if (cell->type == "$mux") {
+ int j = 0;
+ for (auto bit : sigmap(cell->getPort("\\A")))
+ sigbit_to_shiftx_offset[bit] = std::make_tuple(cell, 0, j++);
+ j = 0;
+ for (auto bit : sigmap(cell->getPort("\\B")))
+ sigbit_to_shiftx_offset[bit] = std::make_tuple(cell, 1, j++);
+ }
+ }
+ }
+
+ virtual void non_chain_user(const SigBit &bit, const Cell *cell, IdString port) override
+ {
+ auto it = sigbit_to_shiftx_offset.find(bit);
+ if (it == sigbit_to_shiftx_offset.end())
+ return;
+ if (cell) {
+ if (cell->type == "$shiftx" && port == "\\A")
+ return;
+ if (cell->type == "$mux" && (port == "\\A" || port == "\\B"))
+ return;
+ }
+ sigbit_to_shiftx_offset.erase(it);
+ }
+
+ virtual bool analyze(vector<int> &taps, const vector<SigBit> &qbits) override
+ {
+ if (GetSize(taps) == 1)
+ return taps[0] >= opts.minlen-1 && sigbit_to_shiftx_offset.count(qbits[0]);
+
+ if (taps.back() < opts.minlen-1)
+ return false;
+
+ Cell *shiftx = nullptr;
+ int group = 0;
+ for (int i = 0; i < GetSize(taps); ++i) {
+ auto it = sigbit_to_shiftx_offset.find(qbits[i]);
+ if (it == sigbit_to_shiftx_offset.end())
+ return false;
+
+ // Check taps are sequential
+ if (i != taps[i])
+ return false;
+ // Check taps are not connected to a shift register,
+ // or sequential to the same shift register
+ if (i == 0) {
+ int offset;
+ std::tie(shiftx,offset,group) = it->second;
+ if (offset != i)
+ return false;
+ }
+ else {
+ Cell *shiftx_ = std::get<0>(it->second);
+ if (shiftx_ != shiftx)
+ return false;
+ int offset = std::get<1>(it->second);
+ if (offset != i)
+ return false;
+ int group_ = std::get<2>(it->second);
+ if (group_ != group)
+ return false;
+ }
+ }
+ log_assert(shiftx);
+
+ // Only map if $shiftx exclusively covers the shift register
+ if (shiftx->type == "$shiftx") {
+ if (GetSize(taps) > shiftx->getParam("\\A_WIDTH").as_int())
+ return false;
+ // Due to padding the most significant bits of A may be 1'bx,
+ // and if so, discount them
+ if (GetSize(taps) < shiftx->getParam("\\A_WIDTH").as_int()) {
+ const SigSpec A = shiftx->getPort("\\A");
+ const int A_width = shiftx->getParam("\\A_WIDTH").as_int();
+ for (int i = GetSize(taps); i < A_width; ++i)
+ if (A[i] != RTLIL::Sx) return false;
+ }
+ else if (GetSize(taps) != shiftx->getParam("\\A_WIDTH").as_int())
+ return false;
+ }
+ else if (shiftx->type == "$mux") {
+ if (GetSize(taps) != 2)
+ return false;
+ }
+ else log_abort();
+
+ return true;
+ }
+
+ virtual bool fixup(Cell *cell, dict<int, SigBit> &taps) override
+ {
+ const auto &tap = *taps.begin();
+ auto bit = tap.second;
+
+ auto it = sigbit_to_shiftx_offset.find(bit);
+ log_assert(it != sigbit_to_shiftx_offset.end());
+
+ auto newcell = cell->module->addCell(NEW_ID, "$__XILINX_SHREG_");
+ newcell->set_src_attribute(cell->get_src_attribute());
+ newcell->setParam("\\DEPTH", cell->getParam("\\DEPTH"));
+ newcell->setParam("\\INIT", cell->getParam("\\INIT"));
+ newcell->setParam("\\CLKPOL", cell->getParam("\\CLKPOL"));
+ newcell->setParam("\\ENPOL", cell->getParam("\\ENPOL"));
+
+ newcell->setPort("\\C", cell->getPort("\\C"));
+ newcell->setPort("\\D", cell->getPort("\\D"));
+ if (cell->hasPort("\\E"))
+ newcell->setPort("\\E", cell->getPort("\\E"));
+
+ Cell* shiftx = std::get<0>(it->second);
+ RTLIL::SigSpec l_wire, q_wire;
+ if (shiftx->type == "$shiftx") {
+ l_wire = shiftx->getPort("\\B");
+ q_wire = shiftx->getPort("\\Y");
+ shiftx->setPort("\\Y", cell->module->addWire(NEW_ID));
+ }
+ else if (shiftx->type == "$mux") {
+ l_wire = shiftx->getPort("\\S");
+ q_wire = shiftx->getPort("\\Y");
+ shiftx->setPort("\\Y", cell->module->addWire(NEW_ID));
+ }
+ else log_abort();
+
+ newcell->setPort("\\Q", q_wire);
+ newcell->setPort("\\L", l_wire);
+
+ return false;
+ }
+};
+
+
struct ShregmapWorker
{
Module *module;
@@ -113,8 +264,10 @@ struct ShregmapWorker
for (auto wire : module->wires())
{
if (wire->port_output || wire->get_bool_attribute("\\keep")) {
- for (auto bit : sigmap(wire))
+ for (auto bit : sigmap(wire)) {
sigbit_with_non_chain_users.insert(bit);
+ if (opts.tech) opts.tech->non_chain_user(bit, nullptr, {});
+ }
}
if (wire->attributes.count("\\init")) {
@@ -152,8 +305,10 @@ struct ShregmapWorker
for (auto conn : cell->connections())
if (cell->input(conn.first))
- for (auto bit : sigmap(conn.second))
+ for (auto bit : sigmap(conn.second)) {
sigbit_with_non_chain_users.insert(bit);
+ if (opts.tech) opts.tech->non_chain_user(bit, cell, conn.first);
+ }
}
}
@@ -258,7 +413,7 @@ struct ShregmapWorker
if (taps.empty() || taps.back() < depth-1)
taps.push_back(depth-1);
- if (opts.tech->analyze(taps))
+ if (opts.tech->analyze(taps, qbits))
break;
taps.pop_back();
@@ -377,6 +532,9 @@ struct ShregmapWorker
ShregmapWorker(Module *module, const ShregmapOptions &opts) :
module(module), sigmap(module), opts(opts), dff_count(0), shreg_count(0)
{
+ if (opts.tech)
+ opts.tech->init(module, sigmap);
+
make_sigbit_chain_next_prev();
find_chain_start_cells();
@@ -448,6 +606,9 @@ struct ShregmapPass : public Pass {
log(" -tech greenpak4\n");
log(" map to greenpak4 shift registers.\n");
log("\n");
+ log(" -tech xilinx\n");
+ log(" map to xilinx dynamic-length shift registers.\n");
+ log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
@@ -501,6 +662,12 @@ struct ShregmapPass : public Pass {
clkpol = "pos";
opts.zinit = true;
opts.tech = new ShregmapTechGreenpak4;
+ }
+ else if (tech == "xilinx") {
+ opts.init = true;
+ opts.params = true;
+ enpol = "any_or_none";
+ opts.tech = new ShregmapTechXilinx7(opts);
} else {
argidx--;
break;
diff --git a/passes/techmap/simplemap.cc b/passes/techmap/simplemap.cc
index 660b60601..f3da80c66 100644
--- a/passes/techmap/simplemap.cc
+++ b/passes/techmap/simplemap.cc
@@ -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 d0e5e2236..ab0bd3b54 100644
--- a/passes/techmap/techmap.cc
+++ b/passes/techmap/techmap.cc
@@ -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)
@@ -383,11 +387,12 @@ 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);
@@ -472,7 +477,7 @@ 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)
@@ -545,6 +550,7 @@ struct TechmapWorker
if (extmapper_name == "wrap") {
std::string cmd_string = tpl->attributes.at("\\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;
}
@@ -558,11 +564,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)
@@ -660,6 +676,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;
@@ -829,6 +846,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)) { }
}
@@ -840,6 +858,7 @@ struct TechmapWorker
if (log_continue) {
log_header(design, "Continuing TECHMAP pass.\n");
log_continue = false;
+ mkdebug.off();
}
if (extern_mode && !in_recursion)
@@ -859,13 +878,18 @@ struct TechmapWorker
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;
}
@@ -883,6 +907,7 @@ struct TechmapWorker
if (log_continue) {
log_header(design, "Continuing TECHMAP pass.\n");
log_continue = false;
+ mkdebug.off();
}
return did_something;
@@ -925,6 +950,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");
@@ -1031,7 +1059,7 @@ struct TechmapPass : public Pass {
simplemap_get_mappers(worker.simplemap_mappers);
std::vector<std::string> map_files;
- std::string verilog_frontend = "verilog -nooverwrite";
+ std::string verilog_frontend = "verilog -nooverwrite -noblackbox";
int max_iter = -1;
size_t argidx;
@@ -1068,6 +1096,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);
@@ -1076,7 +1108,7 @@ 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 (!saved_designs.count(fn.substr(1))) {
@@ -1095,6 +1127,9 @@ struct TechmapPass : public Pass {
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);
}
+ }
+
+ 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_) {
@@ -1145,7 +1180,7 @@ struct FlattenPass : public Pass {
{
// |---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");
@@ -1154,17 +1189,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");
}
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);
@@ -1190,6 +1237,7 @@ struct FlattenPass : public Pass {
}
}
+ log_suppressed();
log("No more expansions possible.\n");
if (top_mod != NULL)
@@ -1209,7 +1257,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/zinit.cc b/passes/techmap/zinit.cc
index b46147fb9..2aefc091d 100644
--- a/passes/techmap/zinit.cc
+++ b/passes/techmap/zinit.cc
@@ -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;
}