diff options
Diffstat (limited to 'passes')
-rw-r--r-- | passes/cmds/rename.cc | 50 | ||||
-rw-r--r-- | passes/equiv/Makefile.inc | 2 | ||||
-rw-r--r-- | passes/equiv/equiv_make.cc | 27 | ||||
-rw-r--r-- | passes/equiv/equiv_opt.cc | 157 | ||||
-rw-r--r-- | passes/memory/memory_bram.cc | 17 | ||||
-rw-r--r-- | passes/opt/Makefile.inc | 1 | ||||
-rw-r--r-- | passes/opt/opt_expr.cc | 2 | ||||
-rw-r--r-- | passes/opt/opt_lut.cc | 503 | ||||
-rw-r--r-- | passes/opt/opt_muxtree.cc | 18 | ||||
-rw-r--r-- | passes/techmap/abc.cc | 80 | ||||
-rw-r--r-- | passes/techmap/deminout.cc | 7 | ||||
-rw-r--r-- | passes/techmap/dff2dffe.cc | 35 | ||||
-rw-r--r-- | passes/techmap/lut2mux.cc | 2 |
13 files changed, 855 insertions, 46 deletions
diff --git a/passes/cmds/rename.cc b/passes/cmds/rename.cc index dce576fdf..4b4af0a40 100644 --- a/passes/cmds/rename.cc +++ b/passes/cmds/rename.cc @@ -52,6 +52,15 @@ static void rename_in_module(RTLIL::Module *module, std::string from_name, std:: log_cmd_error("Object `%s' not found!\n", from_name.c_str()); } +static std::string derive_name_from_src(const std::string &src, int counter) +{ + std::string src_base = src.substr(0, src.find('|')); + if (src_base.empty()) + return stringf("$%d", counter); + else + return stringf("\\%s$%d", src_base.c_str(), counter); +} + struct RenamePass : public Pass { RenamePass() : Pass("rename", "rename object in the design") { } void help() YS_OVERRIDE @@ -63,6 +72,10 @@ 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(" 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(" rename -enumerate [-pattern <pattern>] [selection]\n"); log("\n"); @@ -84,6 +97,7 @@ struct RenamePass : public Pass { void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { std::string pattern_prefix = "_", pattern_suffix = "_"; + bool flag_src = false; bool flag_enumerate = false; bool flag_hide = false; bool flag_top = false; @@ -93,6 +107,11 @@ struct RenamePass : public Pass { for (argidx = 1; argidx < args.size(); argidx++) { std::string arg = args[argidx]; + if (arg == "-src" && !got_mode) { + flag_src = true; + got_mode = true; + continue; + } if (arg == "-enumerate" && !got_mode) { flag_enumerate = true; got_mode = true; @@ -117,6 +136,37 @@ struct RenamePass : public Pass { break; } + if (flag_src) + { + extra_args(args, argidx, design); + + for (auto &mod : design->modules_) + { + int counter = 0; + + RTLIL::Module *module = mod.second; + if (!design->selected(module)) + continue; + + dict<RTLIL::IdString, RTLIL::Wire*> new_wires; + for (auto &it : module->wires_) { + if (it.first[0] == '$' && design->selected(module, it.second)) + it.second->name = derive_name_from_src(it.second->get_src_attribute(), counter++); + new_wires[it.second->name] = it.second; + } + module->wires_.swap(new_wires); + module->fixup_ports(); + + dict<RTLIL::IdString, RTLIL::Cell*> new_cells; + for (auto &it : module->cells_) { + if (it.first[0] == '$' && design->selected(module, it.second)) + it.second->name = derive_name_from_src(it.second->get_src_attribute(), counter++); + new_cells[it.second->name] = it.second; + } + module->cells_.swap(new_cells); + } + } + else if (flag_enumerate) { extra_args(args, argidx, design); diff --git a/passes/equiv/Makefile.inc b/passes/equiv/Makefile.inc index dd7b3be02..27ea54b22 100644 --- a/passes/equiv/Makefile.inc +++ b/passes/equiv/Makefile.inc @@ -9,4 +9,4 @@ OBJS += passes/equiv/equiv_induct.o OBJS += passes/equiv/equiv_struct.o OBJS += passes/equiv/equiv_purge.o OBJS += passes/equiv/equiv_mark.o - +OBJS += passes/equiv/equiv_opt.o diff --git a/passes/equiv/equiv_make.cc b/passes/equiv/equiv_make.cc index 66ee28aff..dbd8682e6 100644 --- a/passes/equiv/equiv_make.cc +++ b/passes/equiv/equiv_make.cc @@ -290,22 +290,29 @@ struct EquivMakeWorker init_bit2driven(); - pool<Cell*> visited_cells; + pool<Cell*> visited_cells; for (auto c : cells_list) for (auto &conn : c->connections()) if (!ct.cell_output(c->type, conn.first)) { SigSpec old_sig = assign_map(conn.second); SigSpec new_sig = rd_signal_map(old_sig); - - visited_cells.clear(); - if (old_sig != new_sig) { - if (check_signal_in_fanout(visited_cells, old_sig, new_sig)) - continue; - log("Changing input %s of cell %s (%s): %s -> %s\n", - log_id(conn.first), log_id(c), log_id(c->type), - log_signal(old_sig), log_signal(new_sig)); - c->setPort(conn.first, new_sig); + if(old_sig != new_sig) { + SigSpec tmp_sig = old_sig; + for (int i = 0; i < GetSize(old_sig); i++) { + SigBit old_bit = old_sig[i], new_bit = new_sig[i]; + + visited_cells.clear(); + if (check_signal_in_fanout(visited_cells, old_bit, new_bit)) + continue; + + log("Changing input %s of cell %s (%s): %s -> %s\n", + log_id(conn.first), log_id(c), log_id(c->type), + log_signal(old_bit), log_signal(new_bit)); + + tmp_sig[i] = new_bit; + } + c->setPort(conn.first, tmp_sig); } } diff --git a/passes/equiv/equiv_opt.cc b/passes/equiv/equiv_opt.cc new file mode 100644 index 000000000..86550a69b --- /dev/null +++ b/passes/equiv/equiv_opt.cc @@ -0,0 +1,157 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2018 whitequark <whitequark@whitequark.org> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/register.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct EquivOptPass:public ScriptPass +{ + EquivOptPass() : ScriptPass("equiv_opt", "prove equivalence for optimized circuit") { } + + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" equiv_opt [options] [command]\n"); + log("\n"); + log("This command checks circuit equivalence before and after an optimization pass.\n"); + log("\n"); + log(" -run <from_label>:<to_label>\n"); + log(" only run the commands between the labels (see below). an empty\n"); + log(" from label is synonymous to the start of the command list, and empty to\n"); + log(" label is synonymous to the end of the command list.\n"); + log("\n"); + log(" -map <filename>\n"); + log(" expand the modules in this file before proving equivalence. this is\n"); + log(" useful for handling architecture-specific primitives.\n"); + log("\n"); + log(" -assert\n"); + log(" produce an error if the circuits are not equivalent\n"); + log("\n"); + log("The following commands are executed by this verification command:\n"); + help_script(); + log("\n"); + } + + std::string command, techmap_opts; + bool assert; + + void clear_flags() YS_OVERRIDE + { + command = ""; + techmap_opts = ""; + assert = false; + } + + void execute(std::vector < std::string > args, RTLIL::Design * design) YS_OVERRIDE + { + string run_from, run_to; + clear_flags(); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-run" && argidx + 1 < args.size()) { + size_t pos = args[argidx + 1].find(':'); + if (pos == std::string::npos) + break; + run_from = args[++argidx].substr(0, pos); + run_to = args[argidx].substr(pos + 1); + continue; + } + if (args[argidx] == "-map" && argidx + 1 < args.size()) { + techmap_opts += " -map " + args[++argidx]; + continue; + } + if (args[argidx] == "-assert") { + assert = true; + continue; + } + break; + } + + for (; argidx < args.size(); argidx++) { + if (command.empty()) { + if (args[argidx].substr(0, 1) == "-") + cmd_error(args, argidx, "Unknown option."); + } else { + command += " "; + } + command += args[argidx]; + } + + if (command.empty()) + log_cmd_error("No optimization pass specified!\n"); + + if (!design->full_selection()) + log_cmd_error("This command only operates on fully selected designs!\n"); + + log_header(design, "Executing EQUIV_OPT pass.\n"); + log_push(); + + run_script(design, run_from, run_to); + + log_pop(); + } + + void script() YS_OVERRIDE + { + if (check_label("run_pass")) { + run("hierarchy -auto-top"); + run("design -save preopt"); + if (help_mode) + run("[command]"); + else + run(command); + run("design -stash postopt"); + } + + if (check_label("prepare")) { + run("design -copy-from preopt -as gold A:top"); + run("design -copy-from postopt -as gate A:top"); + } + + if ((!techmap_opts.empty() || help_mode) && check_label("techmap", "(only with -map)")) { + string opts; + if (help_mode) + opts = " -map <filename> ..."; + else + opts = techmap_opts; + run("techmap -D EQUIV -autoproc" + opts); + } + + if (check_label("prove")) { + run("equiv_make gold gate equiv"); + run("equiv_induct equiv"); + if (help_mode) + run("equiv_status [-assert] equiv"); + else if (assert) + run("equiv_status -assert equiv"); + else + run("equiv_status equiv"); + } + + if (check_label("restore")) { + run("design -load preopt"); + } + } +} EquivOptPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/memory/memory_bram.cc b/passes/memory/memory_bram.cc index 8740042c4..cf4095d06 100644 --- a/passes/memory/memory_bram.cc +++ b/passes/memory/memory_bram.cc @@ -472,8 +472,12 @@ bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram, std::vector<SigSpec> new_wr_en(GetSize(old_wr_en)); std::vector<SigSpec> new_wr_data(GetSize(old_wr_data)); std::vector<SigSpec> new_rd_data(GetSize(old_rd_data)); + std::vector<std::vector<State>> new_initdata; std::vector<int> shuffle_map; + if (cell_init) + new_initdata.resize(mem_size); + for (auto &it : en_order) { auto &bits = bits_wr_en.at(it); @@ -489,6 +493,10 @@ bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram, } for (int j = 0; j < rd_ports; j++) new_rd_data[j].append(old_rd_data[j][bits[i]]); + if (cell_init) { + for (int j = 0; j < mem_size; j++) + new_initdata[j].push_back(initdata[j][bits[i]]); + } shuffle_map.push_back(bits[i]); } @@ -499,6 +507,10 @@ bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram, } for (int j = 0; j < rd_ports; j++) new_rd_data[j].append(State::Sx); + if (cell_init) { + for (int j = 0; j < mem_size; j++) + new_initdata[j].push_back(State::Sx); + } shuffle_map.push_back(-1); } } @@ -522,6 +534,11 @@ bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram, for (int i = 0; i < rd_ports; i++) rd_data.replace(i*mem_width, new_rd_data[i]); + + if (cell_init) { + for (int i = 0; i < mem_size; i++) + initdata[i] = Const(new_initdata[i]); + } } // assign write ports diff --git a/passes/opt/Makefile.inc b/passes/opt/Makefile.inc index 0d01e9d35..0f596b1f4 100644 --- a/passes/opt/Makefile.inc +++ b/passes/opt/Makefile.inc @@ -6,6 +6,7 @@ OBJS += passes/opt/opt_reduce.o OBJS += passes/opt/opt_rmdff.o OBJS += passes/opt/opt_clean.o OBJS += passes/opt/opt_expr.o +OBJS += passes/opt/opt_lut.o ifneq ($(SMALL),1) OBJS += passes/opt/share.o diff --git a/passes/opt/opt_expr.cc b/passes/opt/opt_expr.cc index 0ba233c62..610edc5e9 100644 --- a/passes/opt/opt_expr.cc +++ b/passes/opt/opt_expr.cc @@ -1406,7 +1406,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons if (sigConst.is_fully_const() && sigConst.is_fully_def() && var_signed == false) { if (sigConst.is_fully_zero()) { - RTLIL::SigSpec a_prime(RTLIL::State::S0, 1); + RTLIL::SigSpec a_prime(RTLIL::State::S0, GetSize(cell->getPort("\\Y"))); if (is_lt) { log("Replacing %s cell `%s' (implementing unsigned X<0) with constant false.\n", log_id(cell->type), log_id(cell)); diff --git a/passes/opt/opt_lut.cc b/passes/opt/opt_lut.cc new file mode 100644 index 000000000..be050c713 --- /dev/null +++ b/passes/opt/opt_lut.cc @@ -0,0 +1,503 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2018 whitequark <whitequark@whitequark.org> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/yosys.h" +#include "kernel/sigtools.h" +#include "kernel/modtools.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct OptLutWorker +{ + dict<IdString, dict<int, IdString>> &dlogic; + RTLIL::Module *module; + ModIndex index; + SigMap sigmap; + + pool<RTLIL::Cell*> luts; + dict<RTLIL::Cell*, int> luts_arity; + dict<RTLIL::Cell*, pool<RTLIL::Cell*>> luts_dlogics; + dict<RTLIL::Cell*, pool<int>> luts_dlogic_inputs; + + int combined_count = 0; + + bool evaluate_lut(RTLIL::Cell *lut, dict<SigBit, bool> inputs) + { + SigSpec lut_input = sigmap(lut->getPort("\\A")); + int lut_width = lut->getParam("\\WIDTH").as_int(); + Const lut_table = lut->getParam("\\LUT"); + int lut_index = 0; + + for (int i = 0; i < lut_width; i++) + { + SigBit input = sigmap(lut_input[i]); + if (inputs.count(input)) + { + lut_index |= inputs[input] << i; + } + else + { + lut_index |= SigSpec(lut_input[i]).as_bool() << i; + } + } + + return lut_table.extract(lut_index).as_bool(); + } + + void show_stats_by_arity() + { + dict<int, int> arity_counts; + dict<IdString, int> dlogic_counts; + int max_arity = 0; + + for (auto lut_arity : luts_arity) + { + max_arity = max(max_arity, lut_arity.second); + arity_counts[lut_arity.second]++; + } + + for (auto &lut_dlogics : luts_dlogics) + { + for (auto &lut_dlogic : lut_dlogics.second) + { + dlogic_counts[lut_dlogic->type]++; + } + } + + log("Number of LUTs: %8zu\n", luts.size()); + for (int arity = 1; arity <= max_arity; arity++) + { + if (arity_counts[arity]) + log(" %d-LUT %16d\n", arity, arity_counts[arity]); + } + for (auto &dlogic_count : dlogic_counts) + { + log(" with %-12s %4d\n", dlogic_count.first.c_str(), dlogic_count.second); + } + } + + OptLutWorker(dict<IdString, dict<int, IdString>> &dlogic, RTLIL::Module *module, int limit) : + dlogic(dlogic), module(module), index(module), sigmap(module) + { + log("Discovering LUTs.\n"); + for (auto cell : module->selected_cells()) + { + if (cell->type == "$lut") + { + int lut_width = cell->getParam("\\WIDTH").as_int(); + SigSpec lut_input = cell->getPort("\\A"); + int lut_arity = 0; + + log("Found $lut\\WIDTH=%d cell %s.%s.\n", lut_width, log_id(module), log_id(cell)); + luts.insert(cell); + + // First, find all dedicated logic we're connected to. This results in an overapproximation + // of such connections. + pool<RTLIL::Cell*> lut_all_dlogics; + for (int i = 0; i < lut_width; i++) + { + SigBit bit = lut_input[i]; + for (auto &port : index.query_ports(bit)) + { + if (dlogic.count(port.cell->type)) + { + auto &dlogic_map = dlogic[port.cell->type]; + if (dlogic_map.count(i)) + { + if (port.port == dlogic_map[i]) + { + lut_all_dlogics.insert(port.cell); + } + } + } + } + } + + // Second, make sure that the connection to dedicated logic is legal. If it is not legal, + // it means one of the two things: + // * The connection is spurious. I.e. this is dedicated logic that will be packed + // with some other LUT, and it just happens to be conected to this LUT as well. + // * The connection is illegal. + // In either of these cases, we don't need to concern ourselves with preserving the connection + // between this LUT and this dedicated logic cell. + pool<RTLIL::Cell*> lut_legal_dlogics; + pool<int> lut_dlogic_inputs; + for (auto lut_dlogic : lut_all_dlogics) + { + auto &dlogic_map = dlogic[lut_dlogic->type]; + bool legal = true; + for (auto &dlogic_conn : dlogic_map) + { + if (lut_width <= dlogic_conn.first) + { + log(" LUT has illegal connection to %s cell %s.%s.\n", lut_dlogic->type.c_str(), log_id(module), log_id(lut_dlogic)); + log(" LUT input A[%d] not present.\n", dlogic_conn.first); + legal = false; + break; + } + if (sigmap(lut_input[dlogic_conn.first]) != sigmap(lut_dlogic->getPort(dlogic_conn.second))) + { + log(" LUT has illegal connection to %s cell %s.%s.\n", lut_dlogic->type.c_str(), log_id(module), log_id(lut_dlogic)); + log(" LUT input A[%d] (wire %s) not connected to %s port %s (wire %s).\n", dlogic_conn.first, log_signal(lut_input[dlogic_conn.first]), lut_dlogic->type.c_str(), dlogic_conn.second.c_str(), log_signal(lut_dlogic->getPort(dlogic_conn.second))); + legal = false; + break; + } + } + + if (legal) + { + log(" LUT has legal connection to %s cell %s.%s.\n", lut_dlogic->type.c_str(), log_id(module), log_id(lut_dlogic)); + lut_legal_dlogics.insert(lut_dlogic); + for (auto &dlogic_conn : dlogic_map) + lut_dlogic_inputs.insert(dlogic_conn.first); + } + } + + // Third, determine LUT arity. An n-wide LUT that has k constant inputs and m inputs shared with dedicated + // logic implements an (n-k-m)-ary function. + for (int i = 0; i < lut_width; i++) + { + SigBit bit = lut_input[i]; + if (bit.wire || lut_dlogic_inputs.count(i)) + lut_arity++; + } + + log(" Cell implements a %d-LUT.\n", lut_arity); + luts_arity[cell] = lut_arity; + luts_dlogics[cell] = lut_legal_dlogics; + luts_dlogic_inputs[cell] = lut_dlogic_inputs; + } + } + show_stats_by_arity(); + + log("\n"); + log("Combining LUTs.\n"); + pool<RTLIL::Cell*> worklist = luts; + while (worklist.size()) + { + if (limit == 0) + { + log("Limit reached.\n"); + break; + } + + auto lutA = worklist.pop(); + SigSpec lutA_input = sigmap(lutA->getPort("\\A")); + SigSpec lutA_output = sigmap(lutA->getPort("\\Y")[0]); + int lutA_width = lutA->getParam("\\WIDTH").as_int(); + int lutA_arity = luts_arity[lutA]; + pool<int> &lutA_dlogic_inputs = luts_dlogic_inputs[lutA]; + + auto lutA_output_ports = index.query_ports(lutA->getPort("\\Y")); + if (lutA_output_ports.size() != 2) + continue; + + for (auto &port : lutA_output_ports) + { + if (port.cell == lutA) + continue; + + if (luts.count(port.cell)) + { + auto lutB = port.cell; + SigSpec lutB_input = sigmap(lutB->getPort("\\A")); + SigSpec lutB_output = sigmap(lutB->getPort("\\Y")[0]); + int lutB_width = lutB->getParam("\\WIDTH").as_int(); + int lutB_arity = luts_arity[lutB]; + pool<int> &lutB_dlogic_inputs = luts_dlogic_inputs[lutB]; + + log("Found %s.%s (cell A) feeding %s.%s (cell B).\n", log_id(module), log_id(lutA), log_id(module), log_id(lutB)); + + if (index.query_is_output(lutA->getPort("\\Y"))) + { + log(" Not combining LUTs (cascade connection feeds module output).\n"); + continue; + } + + pool<SigBit> lutA_inputs; + pool<SigBit> lutB_inputs; + for (auto &bit : lutA_input) + { + if (bit.wire) + lutA_inputs.insert(sigmap(bit)); + } + for (auto &bit : lutB_input) + { + if (bit.wire) + lutB_inputs.insert(sigmap(bit)); + } + + pool<SigBit> common_inputs; + for (auto &bit : lutA_inputs) + { + if (lutB_inputs.count(bit)) + common_inputs.insert(bit); + } + + int lutM_arity = lutA_arity + lutB_arity - 1 - common_inputs.size(); + if (lutA_dlogic_inputs.size()) + log(" Cell A is a %d-LUT with %zu dedicated connections. ", lutA_arity, lutA_dlogic_inputs.size()); + else + log(" Cell A is a %d-LUT. ", lutA_arity); + if (lutB_dlogic_inputs.size()) + log("Cell B is a %d-LUT with %zu dedicated connections.\n", lutB_arity, lutB_dlogic_inputs.size()); + else + log("Cell B is a %d-LUT.\n", lutB_arity); + log(" Cells share %zu input(s) and can be merged into one %d-LUT.\n", common_inputs.size(), lutM_arity); + + const int COMBINE_A = 1, COMBINE_B = 2, COMBINE_EITHER = COMBINE_A | COMBINE_B; + int combine_mask = 0; + if (lutM_arity > lutA_width) + { + log(" Not combining LUTs into cell A (combined LUT wider than cell A).\n"); + } + else if (lutB_dlogic_inputs.size() > 0) + { + log(" Not combining LUTs into cell A (cell B is connected to dedicated logic).\n"); + } + else if (lutB->get_bool_attribute("\\lut_keep")) + { + log(" Not combining LUTs into cell A (cell B has attribute \\lut_keep).\n"); + } + else + { + combine_mask |= COMBINE_A; + } + if (lutM_arity > lutB_width) + { + log(" Not combining LUTs into cell B (combined LUT wider than cell B).\n"); + } + else if (lutA_dlogic_inputs.size() > 0) + { + log(" Not combining LUTs into cell B (cell A is connected to dedicated logic).\n"); + } + else if (lutA->get_bool_attribute("\\lut_keep")) + { + log(" Not combining LUTs into cell B (cell A has attribute \\lut_keep).\n"); + } + else + { + combine_mask |= COMBINE_B; + } + + int combine = combine_mask; + if (combine == COMBINE_EITHER) + { + log(" Can combine into either cell.\n"); + if (lutA_arity == 1) + { + log(" Cell A is a buffer or inverter, combining into cell B.\n"); + combine = COMBINE_B; + } + else if (lutB_arity == 1) + { + log(" Cell B is a buffer or inverter, combining into cell A.\n"); + combine = COMBINE_A; + } + else + { + log(" Arbitrarily combining into cell A.\n"); + combine = COMBINE_A; + } + } + + RTLIL::Cell *lutM, *lutR; + pool<SigBit> lutM_inputs, lutR_inputs; + pool<int> lutM_dlogic_inputs; + if (combine == COMBINE_A) + { + log(" Combining LUTs into cell A.\n"); + lutM = lutA; + lutM_inputs = lutA_inputs; + lutM_dlogic_inputs = lutA_dlogic_inputs; + lutR = lutB; + lutR_inputs = lutB_inputs; + } + else if (combine == COMBINE_B) + { + log(" Combining LUTs into cell B.\n"); + lutM = lutB; + lutM_inputs = lutB_inputs; + lutM_dlogic_inputs = lutB_dlogic_inputs; + lutR = lutA; + lutR_inputs = lutA_inputs; + } + else + { + log(" Cannot combine LUTs.\n"); + continue; + } + + pool<SigBit> lutR_unique; + for (auto &bit : lutR_inputs) + { + if (!common_inputs.count(bit) && bit != lutA_output) + lutR_unique.insert(bit); + } + + int lutM_width = lutM->getParam("\\WIDTH").as_int(); + SigSpec lutM_input = sigmap(lutM->getPort("\\A")); + std::vector<SigBit> lutM_new_inputs; + for (int i = 0; i < lutM_width; i++) + { + bool input_unused = false; + if (sigmap(lutM_input[i]) == lutA_output) + input_unused = true; + if (!lutM_input[i].wire && !lutM_dlogic_inputs.count(i)) + input_unused = true; + + if (input_unused && lutR_unique.size()) + { + SigBit new_input = lutR_unique.pop(); + log(" Connecting input %d as %s.\n", i, log_signal(new_input)); + lutM_new_inputs.push_back(new_input); + } + else if (sigmap(lutM_input[i]) == lutA_output) + { + log(" Disconnecting cascade input %d.\n", i); + lutM_new_inputs.push_back(SigBit()); + } + else + { + log(" Leaving input %d as %s.\n", i, log_signal(lutM_input[i])); + lutM_new_inputs.push_back(lutM_input[i]); + } + } + log_assert(lutR_unique.size() == 0); + + RTLIL::Const lutM_new_table(State::Sx, 1 << lutM_width); + for (int eval = 0; eval < 1 << lutM_width; eval++) + { + dict<SigBit, bool> eval_inputs; + for (size_t i = 0; i < lutM_new_inputs.size(); i++) + { + eval_inputs[lutM_new_inputs[i]] = (eval >> i) & 1; + } + eval_inputs[lutA_output] = evaluate_lut(lutA, eval_inputs); + lutM_new_table[eval] = (RTLIL::State) evaluate_lut(lutB, eval_inputs); + } + + log(" Cell A truth table: %s.\n", lutA->getParam("\\LUT").as_string().c_str()); + log(" Cell B truth table: %s.\n", lutB->getParam("\\LUT").as_string().c_str()); + log(" Merged truth table: %s.\n", lutM_new_table.as_string().c_str()); + + lutM->setParam("\\LUT", lutM_new_table); + lutM->setPort("\\A", lutM_new_inputs); + lutM->setPort("\\Y", lutB_output); + + luts_arity[lutM] = lutM_arity; + luts.erase(lutR); + luts_arity.erase(lutR); + lutR->module->remove(lutR); + + worklist.insert(lutM); + worklist.erase(lutR); + + combined_count++; + if (limit > 0) + limit--; + } + } + } + show_stats_by_arity(); + } +}; + +static void split(std::vector<std::string> &tokens, const std::string &text, char sep) +{ + size_t start = 0, end = 0; + while ((end = text.find(sep, start)) != std::string::npos) { + tokens.push_back(text.substr(start, end - start)); + start = end + 1; + } + tokens.push_back(text.substr(start)); +} + +struct OptLutPass : public Pass { + OptLutPass() : Pass("opt_lut", "optimize LUT cells") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" opt_lut [options] [selection]\n"); + log("\n"); + log("This pass combines cascaded $lut cells with unused inputs.\n"); + log("\n"); + log(" -dlogic <type>:<cell-port>=<LUT-input>[:<cell-port>=<LUT-input>...]\n"); + log(" preserve connections to dedicated logic cell <type> that has ports\n"); + log(" <cell-port> connected to LUT inputs <LUT-input>. this includes\n"); + log(" the case where both LUT and dedicated logic input are connected to\n"); + log(" the same constant.\n"); + log("\n"); + log(" -limit N\n"); + log(" only perform the first N combines, then stop. useful for debugging.\n"); + log("\n"); + } + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + { + log_header(design, "Executing OPT_LUT pass (optimize LUTs).\n"); + + dict<IdString, dict<int, IdString>> dlogic; + int limit = -1; + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + if (args[argidx] == "-dlogic" && argidx+1 < args.size()) + { + std::vector<std::string> tokens; + split(tokens, args[++argidx], ':'); + if (tokens.size() < 2) + log_cmd_error("The -dlogic option requires at least one connection.\n"); + IdString type = "\\" + tokens[0]; + for (auto it = tokens.begin() + 1; it != tokens.end(); ++it) { + std::vector<std::string> conn_tokens; + split(conn_tokens, *it, '='); + if (conn_tokens.size() != 2) + log_cmd_error("Invalid format of -dlogic signal mapping.\n"); + IdString logic_port = "\\" + conn_tokens[0]; + int lut_input = atoi(conn_tokens[1].c_str()); + dlogic[type][lut_input] = logic_port; + } + continue; + } + if (args[argidx] == "-limit" && argidx + 1 < args.size()) + { + limit = atoi(args[++argidx].c_str()); + continue; + } + break; + } + extra_args(args, argidx, design); + + int total_count = 0; + for (auto module : design->selected_modules()) + { + OptLutWorker worker(dlogic, module, limit - total_count); + total_count += worker.combined_count; + } + if (total_count) + design->scratchpad_set_bool("opt.did_something", true); + log("\n"); + log("Combined %d LUTs.\n", total_count); + } +} OptLutPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/opt/opt_muxtree.cc b/passes/opt/opt_muxtree.cc index 87c7ce9b2..375697dc8 100644 --- a/passes/opt/opt_muxtree.cc +++ b/passes/opt/opt_muxtree.cc @@ -36,6 +36,7 @@ struct OptMuxtreeWorker RTLIL::Module *module; SigMap assign_map; int removed_count; + int glob_abort_cnt = 100000; struct bitinfo_t { bool seen_non_mux; @@ -293,6 +294,9 @@ struct OptMuxtreeWorker void eval_mux_port(knowledge_t &knowledge, int mux_idx, int port_idx, bool do_replace_known, bool do_enable_ports, int abort_count) { + if (glob_abort_cnt == 0) + return; + muxinfo_t &muxinfo = mux2info[mux_idx]; if (do_enable_ports) @@ -315,7 +319,7 @@ struct OptMuxtreeWorker knowledge.visited_muxes[m] = true; parent_muxes.push_back(m); } - for (int m : parent_muxes) + for (int m : parent_muxes) { if (root_enable_muxes.at(m)) continue; else if (root_muxes.at(m)) { @@ -327,6 +331,9 @@ struct OptMuxtreeWorker eval_mux(knowledge, m, false, do_enable_ports, abort_count - 1); } else eval_mux(knowledge, m, do_replace_known, do_enable_ports, abort_count); + if (glob_abort_cnt == 0) + return; + } for (int m : parent_muxes) knowledge.visited_muxes[m] = false; @@ -390,6 +397,12 @@ struct OptMuxtreeWorker void eval_mux(knowledge_t &knowledge, int mux_idx, bool do_replace_known, bool do_enable_ports, int abort_count) { + if (glob_abort_cnt == 0) { + log(" Giving up (too many iterations)\n"); + return; + } + glob_abort_cnt--; + muxinfo_t &muxinfo = mux2info[mux_idx]; // set input ports to constants if we find known active or inactive signals @@ -433,6 +446,9 @@ struct OptMuxtreeWorker if (knowledge.known_inactive.at(portinfo.ctrl_sig)) continue; eval_mux_port(knowledge, mux_idx, port_idx, do_replace_known, do_enable_ports, abort_count); + + if (glob_abort_cnt == 0) + return; } } diff --git a/passes/techmap/abc.cc b/passes/techmap/abc.cc index d2d15a4a9..d3bdd555b 100644 --- a/passes/techmap/abc.cc +++ b/passes/techmap/abc.cc @@ -329,6 +329,18 @@ void extract_cell(RTLIL::Cell *cell, bool keepff) std::string remap_name(RTLIL::IdString abc_name) { + 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) { + std::stringstream sstr; + sstr << "$abc$" << map_autoidx << "$" << log_signal(sig.bit) << (inv ? "_inv" : ""); + return sstr.str(); + } + } + } std::stringstream sstr; sstr << "$abc$" << map_autoidx << "$" << abc_name.substr(1); return sstr.str(); @@ -353,12 +365,12 @@ void dump_loop_graph(FILE *f, int &nr, std::map<int, std::set<int>> &edges, std: } for (auto n : nodes) - fprintf(f, " n%d [label=\"%s\\nid=%d, count=%d\"%s];\n", n, log_signal(signal_list[n].bit), + fprintf(f, " ys__n%d [label=\"%s\\nid=%d, count=%d\"%s];\n", n, log_signal(signal_list[n].bit), n, in_counts[n], workpool.count(n) ? ", shape=box" : ""); for (auto &e : edges) for (auto n : e.second) - fprintf(f, " n%d -> n%d;\n", e.first, n); + fprintf(f, " ys__n%d -> ys__n%d;\n", e.first, n); fprintf(f, "}\n"); } @@ -624,7 +636,7 @@ struct abc_output_filter void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::string script_file, std::string exe_file, std::string liberty_file, std::string constr_file, bool cleanup, vector<int> lut_costs, bool dff_mode, std::string clk_str, bool keepff, std::string delay_target, std::string sop_inputs, std::string sop_products, std::string lutin_shared, bool fast_mode, - const std::vector<RTLIL::Cell*> &cells, bool show_tempdir, bool sop_mode) + const std::vector<RTLIL::Cell*> &cells, bool show_tempdir, bool sop_mode, bool abc_dress) { module = current_module; map_autoidx = autoidx++; @@ -728,7 +740,8 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin for (size_t pos = abc_script.find("{S}"); pos != std::string::npos; pos = abc_script.find("{S}", pos)) abc_script = abc_script.substr(0, pos) + lutin_shared + abc_script.substr(pos+3); - + if (abc_dress) + abc_script += "; dress"; abc_script += stringf("; write_blif %s/output.blif", tempdir_name.c_str()); abc_script = add_echos_to_abc_cmd(abc_script); @@ -784,7 +797,7 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin for (auto &si : signal_list) { if (!si.is_port || si.type != G(NONE)) continue; - fprintf(f, " n%d", si.id); + fprintf(f, " ys__n%d", si.id); pi_map[count_input++] = log_signal(si.bit); } if (count_input == 0) @@ -796,17 +809,17 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin for (auto &si : signal_list) { if (!si.is_port || si.type == G(NONE)) continue; - fprintf(f, " n%d", si.id); + fprintf(f, " ys__n%d", si.id); po_map[count_output++] = log_signal(si.bit); } fprintf(f, "\n"); for (auto &si : signal_list) - fprintf(f, "# n%-5d %s\n", si.id, log_signal(si.bit)); + fprintf(f, "# ys__n%-5d %s\n", si.id, log_signal(si.bit)); for (auto &si : signal_list) { if (si.bit.wire == NULL) { - fprintf(f, ".names n%d\n", si.id); + fprintf(f, ".names ys__n%d\n", si.id); if (si.bit == RTLIL::State::S1) fprintf(f, "1\n"); } @@ -815,68 +828,68 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin int count_gates = 0; for (auto &si : signal_list) { if (si.type == G(BUF)) { - fprintf(f, ".names n%d n%d\n", si.in1, si.id); + fprintf(f, ".names ys__n%d ys__n%d\n", si.in1, si.id); fprintf(f, "1 1\n"); } else if (si.type == G(NOT)) { - fprintf(f, ".names n%d n%d\n", si.in1, si.id); + fprintf(f, ".names ys__n%d ys__n%d\n", si.in1, si.id); fprintf(f, "0 1\n"); } else if (si.type == G(AND)) { - fprintf(f, ".names n%d n%d n%d\n", si.in1, si.in2, si.id); + fprintf(f, ".names ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.id); fprintf(f, "11 1\n"); } else if (si.type == G(NAND)) { - fprintf(f, ".names n%d n%d n%d\n", si.in1, si.in2, si.id); + fprintf(f, ".names ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.id); fprintf(f, "0- 1\n"); fprintf(f, "-0 1\n"); } else if (si.type == G(OR)) { - fprintf(f, ".names n%d n%d n%d\n", si.in1, si.in2, si.id); + fprintf(f, ".names ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.id); fprintf(f, "-1 1\n"); fprintf(f, "1- 1\n"); } else if (si.type == G(NOR)) { - fprintf(f, ".names n%d n%d n%d\n", si.in1, si.in2, si.id); + fprintf(f, ".names ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.id); fprintf(f, "00 1\n"); } else if (si.type == G(XOR)) { - fprintf(f, ".names n%d n%d n%d\n", si.in1, si.in2, si.id); + fprintf(f, ".names ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.id); fprintf(f, "01 1\n"); fprintf(f, "10 1\n"); } else if (si.type == G(XNOR)) { - fprintf(f, ".names n%d n%d n%d\n", si.in1, si.in2, si.id); + fprintf(f, ".names ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.id); fprintf(f, "00 1\n"); fprintf(f, "11 1\n"); } else if (si.type == G(ANDNOT)) { - fprintf(f, ".names n%d n%d n%d\n", si.in1, si.in2, si.id); + fprintf(f, ".names ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.id); fprintf(f, "10 1\n"); } else if (si.type == G(ORNOT)) { - fprintf(f, ".names n%d n%d n%d\n", si.in1, si.in2, si.id); + fprintf(f, ".names ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.id); fprintf(f, "1- 1\n"); fprintf(f, "-0 1\n"); } else if (si.type == G(MUX)) { - fprintf(f, ".names n%d n%d n%d n%d\n", si.in1, si.in2, si.in3, si.id); + fprintf(f, ".names ys__n%d ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.in3, si.id); fprintf(f, "1-0 1\n"); fprintf(f, "-11 1\n"); } else if (si.type == G(AOI3)) { - fprintf(f, ".names n%d n%d n%d n%d\n", si.in1, si.in2, si.in3, si.id); + fprintf(f, ".names ys__n%d ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.in3, si.id); fprintf(f, "-00 1\n"); fprintf(f, "0-0 1\n"); } else if (si.type == G(OAI3)) { - fprintf(f, ".names n%d n%d n%d n%d\n", si.in1, si.in2, si.in3, si.id); + fprintf(f, ".names ys__n%d ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.in3, si.id); fprintf(f, "00- 1\n"); fprintf(f, "--0 1\n"); } else if (si.type == G(AOI4)) { - fprintf(f, ".names n%d n%d n%d n%d n%d\n", si.in1, si.in2, si.in3, si.in4, si.id); + fprintf(f, ".names ys__n%d ys__n%d ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.in3, si.in4, si.id); fprintf(f, "-0-0 1\n"); fprintf(f, "-00- 1\n"); fprintf(f, "0--0 1\n"); fprintf(f, "0-0- 1\n"); } else if (si.type == G(OAI4)) { - fprintf(f, ".names n%d n%d n%d n%d n%d\n", si.in1, si.in2, si.in3, si.in4, si.id); + fprintf(f, ".names ys__n%d ys__n%d ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.in3, si.in4, si.id); fprintf(f, "00-- 1\n"); fprintf(f, "--00 1\n"); } else if (si.type == G(FF)) { if (si.init == State::S0 || si.init == State::S1) { - fprintf(f, ".latch n%d n%d %d\n", si.in1, si.id, si.init == State::S1 ? 1 : 0); + fprintf(f, ".latch ys__n%d ys__n%d %d\n", si.in1, si.id, si.init == State::S1 ? 1 : 0); recover_init = true; } else - fprintf(f, ".latch n%d n%d 2\n", si.in1, si.id); + fprintf(f, ".latch ys__n%d ys__n%d 2\n", si.in1, si.id); } else if (si.type != G(NONE)) log_abort(); if (si.type != G(NONE)) @@ -889,7 +902,6 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin log("Extracted %d gates and %d wires to a netlist network with %d inputs and %d outputs.\n", count_gates, GetSize(signal_list), count_input, count_output); log_push(); - if (count_output > 0) { log_header(design, "Executing ABC.\n"); @@ -1213,7 +1225,7 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin for (auto &si : signal_list) if (si.is_port) { char buffer[100]; - snprintf(buffer, 100, "\\n%d", si.id); + snprintf(buffer, 100, "\\ys__n%d", si.id); RTLIL::SigSig conn; if (si.type != G(NONE)) { conn.first = si.bit; @@ -1407,6 +1419,11 @@ struct AbcPass : public Pass { log(" this attribute is a unique integer for each ABC process started. This\n"); log(" is useful for debugging the partitioning of clock domains.\n"); log("\n"); + log(" -dress\n"); + log(" run the 'dress' command after all other ABC commands. This aims to\n"); + log(" preserve naming by an equivalence check between the original and post-ABC\n"); + log(" netlists (experimental).\n"); + log("\n"); log("When neither -liberty nor -lut is used, the Yosys standard cell library is\n"); log("loaded into ABC before the ABC script is executed.\n"); log("\n"); @@ -1441,6 +1458,7 @@ struct AbcPass : public Pass { std::string delay_target, sop_inputs, sop_products, lutin_shared = "-S 1"; bool fast_mode = false, dff_mode = false, keepff = false, cleanup = true; bool show_tempdir = false, sop_mode = false; + bool abc_dress = false; vector<int> lut_costs; markgroups = false; @@ -1555,6 +1573,10 @@ struct AbcPass : public Pass { map_mux16 = true; continue; } + if (arg == "-dress") { + abc_dress = true; + continue; + } if (arg == "-g" && argidx+1 < args.size()) { for (auto g : split_tokens(args[++argidx], ",")) { vector<string> gate_list; @@ -1704,7 +1726,7 @@ struct AbcPass : public Pass { if (!dff_mode || !clk_str.empty()) { abc_module(design, mod, script_file, exe_file, liberty_file, constr_file, cleanup, lut_costs, dff_mode, clk_str, keepff, - delay_target, sop_inputs, sop_products, lutin_shared, fast_mode, mod->selected_cells(), show_tempdir, sop_mode); + delay_target, sop_inputs, sop_products, lutin_shared, fast_mode, mod->selected_cells(), show_tempdir, sop_mode, abc_dress); continue; } @@ -1849,7 +1871,7 @@ struct AbcPass : public Pass { en_polarity = std::get<2>(it.first); en_sig = assign_map(std::get<3>(it.first)); abc_module(design, mod, script_file, exe_file, liberty_file, constr_file, cleanup, lut_costs, !clk_sig.empty(), "$", - keepff, delay_target, sop_inputs, sop_products, lutin_shared, fast_mode, it.second, show_tempdir, sop_mode); + keepff, delay_target, sop_inputs, sop_products, lutin_shared, fast_mode, it.second, show_tempdir, sop_mode, abc_dress); assign_map.set(mod); } } diff --git a/passes/techmap/deminout.cc b/passes/techmap/deminout.cc index 9f0c7bf67..47d0ff416 100644 --- a/passes/techmap/deminout.cc +++ b/passes/techmap/deminout.cc @@ -83,9 +83,9 @@ struct DeminoutPass : public Pass { for (auto bit : sigmap(conn.second)) bits_used.insert(bit); - if (conn.first == "\\Y" && cell->type.in("$mux", "$pmux", "$_MUX_", "$_TBUF_")) + if (conn.first == "\\Y" && cell->type.in("$mux", "$pmux", "$_MUX_", "$_TBUF_", "$tribuf")) { - bool tribuf = (cell->type == "$_TBUF_"); + bool tribuf = (cell->type == "$_TBUF_" || cell->type == "$tribuf"); if (!tribuf) { for (auto &c : cell->connections()) { @@ -113,7 +113,8 @@ struct DeminoutPass : public Pass { { if (bits_numports[bit] > 1 || bits_inout.count(bit)) new_input = true, new_output = true; - + if (bit == State::S0 || bit == State::S1) + new_output = true; if (bits_written.count(bit)) { new_output = true; if (bits_tribuf.count(bit)) diff --git a/passes/techmap/dff2dffe.cc b/passes/techmap/dff2dffe.cc index 3291f5a4a..7e1040963 100644 --- a/passes/techmap/dff2dffe.cc +++ b/passes/techmap/dff2dffe.cc @@ -267,6 +267,10 @@ struct Dff2dffePass : public Pass { log(" operate in the opposite direction: replace $dffe cells with combinations\n"); log(" of $dff and $mux cells. the options below are ignore in unmap mode.\n"); log("\n"); + log(" -unmap-mince N\n"); + log(" Same as -unmap but only unmap $dffe where the clock enable port\n"); + log(" signal is used by less $dffe than the specified number\n"); + log("\n"); log(" -direct <internal_gate_type> <external_gate_type>\n"); log(" map directly to external gate type. <internal_gate_type> can\n"); log(" be any internal gate-level FF cell (except $_DFFE_??_). the\n"); @@ -289,6 +293,7 @@ struct Dff2dffePass : public Pass { log_header(design, "Executing DFF2DFFE pass (transform $dff to $dffe where applicable).\n"); bool unmap_mode = false; + int min_ce_use = -1; dict<IdString, IdString> direct_dict; size_t argidx; @@ -297,6 +302,11 @@ struct Dff2dffePass : public Pass { unmap_mode = true; continue; } + if (args[argidx] == "-unmap-mince" && argidx + 1 < args.size()) { + unmap_mode = true; + min_ce_use = std::stoi(args[++argidx]); + continue; + } if (args[argidx] == "-direct" && argidx + 2 < args.size()) { string direct_from = RTLIL::escape_id(args[++argidx]); string direct_to = RTLIL::escape_id(args[++argidx]); @@ -343,8 +353,21 @@ struct Dff2dffePass : public Pass { if (!mod->has_processes_warn()) { if (unmap_mode) { + SigMap sigmap(mod); for (auto cell : mod->selected_cells()) { if (cell->type == "$dffe") { + if (min_ce_use >= 0) { + int ce_use = 0; + for (auto cell_other : mod->selected_cells()) { + if (cell_other->type != cell->type) + continue; + if (sigmap(cell->getPort("\\EN")) == sigmap(cell_other->getPort("\\EN"))) + ce_use++; + } + if (ce_use >= min_ce_use) + continue; + } + RTLIL::SigSpec tmp = mod->addWire(NEW_ID, GetSize(cell->getPort("\\D"))); mod->addDff(NEW_ID, cell->getPort("\\CLK"), tmp, cell->getPort("\\Q"), cell->getParam("\\CLK_POLARITY").as_bool()); if (cell->getParam("\\EN_POLARITY").as_bool()) @@ -355,6 +378,18 @@ struct Dff2dffePass : public Pass { continue; } if (cell->type.substr(0, 7) == "$_DFFE_") { + if (min_ce_use >= 0) { + int ce_use = 0; + for (auto cell_other : mod->selected_cells()) { + if (cell_other->type != cell->type) + continue; + if (sigmap(cell->getPort("\\E")) == sigmap(cell_other->getPort("\\E"))) + ce_use++; + } + if (ce_use >= min_ce_use) + continue; + } + bool clk_pol = cell->type.substr(7, 1) == "P"; bool en_pol = cell->type.substr(8, 1) == "P"; RTLIL::SigSpec tmp = mod->addWire(NEW_ID); diff --git a/passes/techmap/lut2mux.cc b/passes/techmap/lut2mux.cc index d32bbff14..a4ed79550 100644 --- a/passes/techmap/lut2mux.cc +++ b/passes/techmap/lut2mux.cc @@ -32,7 +32,7 @@ int lut2mux(Cell *cell) if (GetSize(sig_a) == 1) { - cell->module->addMuxGate(NEW_ID, lut[0], lut[1], sig_a, sig_y); + cell->module->addMuxGate(NEW_ID, lut.extract(0)[0], lut.extract(1)[0], sig_a, sig_y); } else { |