diff options
author | Eddie Hung <eddie@fpgeh.com> | 2019-04-22 11:19:52 -0700 |
---|---|---|
committer | Eddie Hung <eddie@fpgeh.com> | 2019-04-22 11:19:52 -0700 |
commit | 4883391b6331e62226c46e797f82a31ef9ef81a3 (patch) | |
tree | 3779d9d3c226602b96eb6f72e2c780e02c64df3e /passes | |
parent | d06d4f35c376672ad1042b46bb29d7bd2bfa5243 (diff) | |
parent | bc98a463a433e5b1553b307301e67e641a148d3c (diff) | |
download | yosys-4883391b6331e62226c46e797f82a31ef9ef81a3.tar.gz yosys-4883391b6331e62226c46e797f82a31ef9ef81a3.tar.bz2 yosys-4883391b6331e62226c46e797f82a31ef9ef81a3.zip |
Merge remote-tracking branch 'origin/master' into xaig
Diffstat (limited to 'passes')
-rw-r--r-- | passes/cmds/plugin.cc | 50 | ||||
-rw-r--r-- | passes/cmds/trace.cc | 34 | ||||
-rw-r--r-- | passes/opt/Makefile.inc | 1 | ||||
-rw-r--r-- | passes/opt/opt_clean.cc | 11 | ||||
-rw-r--r-- | passes/opt/opt_expr.cc | 79 | ||||
-rw-r--r-- | passes/opt/opt_merge.cc | 8 | ||||
-rw-r--r-- | passes/opt/opt_muxtree.cc | 6 | ||||
-rw-r--r-- | passes/opt/pmux2shiftx.cc | 852 | ||||
-rw-r--r-- | passes/techmap/attrmap.cc | 4 | ||||
-rw-r--r-- | passes/techmap/techmap.cc | 37 |
10 files changed, 1031 insertions, 51 deletions
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/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/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..c38e9df5e 100644 --- a/passes/opt/opt_clean.cc +++ b/passes/opt/opt_clean.cc @@ -137,7 +137,7 @@ 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++; @@ -326,7 +326,7 @@ void rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos 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()); + log_debug(" removing unused non-port wire %s.\n", wire->name.c_str()); } del_wires.insert(wire); del_wires_count++; @@ -336,7 +336,7 @@ void rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos count_rm_wires += del_wires.size(); if (verbose && del_wires_count > 0) - log(" removed %d unused temporary wires.\n", del_wires_count); + log_debug(" removed %d unused temporary wires.\n", del_wires_count); } bool rmunused_module_init(RTLIL::Module *module, bool purge_mode, bool verbose) @@ -399,7 +399,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 +426,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); } @@ -551,6 +551,7 @@ struct CleanPass : public Pass { rmunused_module(module, purge_mode, false, 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 a05db2a4f..af6d352af 100644 --- a/passes/opt/opt_expr.cc +++ b/passes/opt/opt_expr.cc @@ -67,7 +67,7 @@ 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)); + log_debug("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))); did_something = true; } @@ -78,7 +78,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 +134,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++) @@ -156,7 +156,7 @@ bool group_cell_inputs(RTLIL::Module *module, RTLIL::Cell *cell, bool commutativ } if (cell->type.in("$and", "$or") && i == GRP_CONST_A) { - log(" Direct Connection: %s (%s with %s)\n", log_signal(new_b), log_id(cell->type), log_signal(new_a)); + 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; @@ -180,10 +180,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()); @@ -197,7 +197,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))); @@ -226,7 +226,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))); @@ -455,9 +455,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")); @@ -488,7 +489,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); @@ -511,7 +512,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); @@ -537,7 +538,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; @@ -563,7 +564,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; @@ -589,7 +590,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; @@ -640,7 +641,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); @@ -750,7 +751,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; @@ -822,7 +823,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"); @@ -837,7 +838,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()) { @@ -876,7 +877,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); @@ -939,7 +940,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) { @@ -969,7 +970,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"); @@ -988,7 +989,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") { @@ -1008,7 +1009,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") { @@ -1061,7 +1062,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); @@ -1179,7 +1180,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()))); @@ -1197,7 +1198,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) { @@ -1237,7 +1238,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()))); @@ -1254,7 +1255,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); @@ -1272,7 +1273,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); @@ -1342,7 +1343,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); @@ -1354,7 +1355,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); @@ -1493,7 +1494,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); @@ -1599,8 +1600,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 { @@ -1610,7 +1617,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..dbebf21e0 100644 --- a/passes/opt/opt_muxtree.cc +++ b/passes/opt/opt_muxtree.cc @@ -181,14 +181,14 @@ 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); } 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); @@ -326,7 +326,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 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/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/techmap.cc b/passes/techmap/techmap.cc index 1a4318460..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; @@ -390,6 +392,7 @@ struct TechmapWorker bool log_continue = false; bool did_something = false; + LogMakeDebugHdl mkdebug; SigMap sigmap(module); @@ -547,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; } @@ -560,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) @@ -662,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; @@ -831,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)) { } } @@ -842,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) @@ -861,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; } @@ -885,6 +907,7 @@ struct TechmapWorker if (log_continue) { log_header(design, "Continuing TECHMAP pass.\n"); log_continue = false; + mkdebug.off(); } return did_something; @@ -1085,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))) { @@ -1104,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_) { @@ -1211,6 +1237,7 @@ struct FlattenPass : public Pass { } } + log_suppressed(); log("No more expansions possible.\n"); if (top_mod != NULL) |