diff options
Diffstat (limited to 'passes/opt')
-rw-r--r-- | passes/opt/Makefile.inc | 6 | ||||
-rw-r--r-- | passes/opt/opt.cc | 65 | ||||
-rw-r--r-- | passes/opt/opt_clean.cc | 115 | ||||
-rw-r--r-- | passes/opt/opt_expr.cc (renamed from passes/opt/opt_const.cc) | 372 | ||||
-rw-r--r-- | passes/opt/opt_merge.cc (renamed from passes/opt/opt_share.cc) | 103 | ||||
-rw-r--r-- | passes/opt/opt_muxtree.cc | 12 | ||||
-rw-r--r-- | passes/opt/opt_reduce.cc | 10 | ||||
-rw-r--r-- | passes/opt/opt_rmdff.cc | 106 | ||||
-rw-r--r-- | passes/opt/share.cc | 159 | ||||
-rw-r--r-- | passes/opt/wreduce.cc | 93 |
10 files changed, 788 insertions, 253 deletions
diff --git a/passes/opt/Makefile.inc b/passes/opt/Makefile.inc index 6b075cd9a..a8b1537bb 100644 --- a/passes/opt/Makefile.inc +++ b/passes/opt/Makefile.inc @@ -1,11 +1,11 @@ OBJS += passes/opt/opt.o -OBJS += passes/opt/opt_share.o +OBJS += passes/opt/opt_merge.o OBJS += passes/opt/opt_muxtree.o OBJS += passes/opt/opt_reduce.o OBJS += passes/opt/opt_rmdff.o -OBJS += passes/opt/opt_clean.o -OBJS += passes/opt/opt_const.o +OBJS += passes/opt/opt_clean.o +OBJS += passes/opt/opt_expr.o ifneq ($(SMALL),1) OBJS += passes/opt/share.o diff --git a/passes/opt/opt.cc b/passes/opt/opt.cc index 25419375e..13ea5469b 100644 --- a/passes/opt/opt.cc +++ b/passes/opt/opt.cc @@ -2,11 +2,11 @@ * 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 @@ -37,23 +37,23 @@ struct OptPass : public Pass { log("a series of trivial optimizations and cleanups. This pass executes the other\n"); log("passes in the following order:\n"); log("\n"); - log(" opt_const [-mux_undef] [-mux_bool] [-undriven] [-fine] [-full] [-keepdc]\n"); - log(" opt_share -nomux\n"); + log(" opt_expr [-mux_undef] [-mux_bool] [-undriven] [-clkinv] [-fine] [-full] [-keepdc]\n"); + log(" opt_merge [-share_all] -nomux\n"); log("\n"); log(" do\n"); log(" opt_muxtree\n"); log(" opt_reduce [-fine] [-full]\n"); - log(" opt_share\n"); + log(" opt_merge [-share_all]\n"); log(" opt_rmdff\n"); log(" opt_clean [-purge]\n"); - log(" opt_const [-mux_undef] [-mux_bool] [-undriven] [-fine] [-full] [-keepdc]\n"); + log(" opt_expr [-mux_undef] [-mux_bool] [-undriven] [-clkinv] [-fine] [-full] [-keepdc]\n"); log(" while <changed design>\n"); log("\n"); log("When called with -fast the following script is used instead:\n"); log("\n"); log(" do\n"); - log(" opt_const [-mux_undef] [-mux_bool] [-undriven] [-fine] [-full] [-keepdc]\n"); - log(" opt_share\n"); + log(" opt_expr [-mux_undef] [-mux_bool] [-undriven] [-clkinv] [-fine] [-full] [-keepdc]\n"); + log(" opt_merge [-share_all]\n"); log(" opt_rmdff\n"); log(" opt_clean [-purge]\n"); log(" while <changed design in opt_rmdff>\n"); @@ -66,11 +66,12 @@ struct OptPass : public Pass { virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { std::string opt_clean_args; - std::string opt_const_args; + std::string opt_expr_args; std::string opt_reduce_args; + std::string opt_merge_args; bool fast_mode = false; - log_header("Executing OPT pass (performing simple optimizations).\n"); + log_header(design, "Executing OPT pass (performing simple optimizations).\n"); log_push(); size_t argidx; @@ -80,29 +81,37 @@ struct OptPass : public Pass { continue; } if (args[argidx] == "-mux_undef") { - opt_const_args += " -mux_undef"; + opt_expr_args += " -mux_undef"; continue; } if (args[argidx] == "-mux_bool") { - opt_const_args += " -mux_bool"; + opt_expr_args += " -mux_bool"; continue; } if (args[argidx] == "-undriven") { - opt_const_args += " -undriven"; + opt_expr_args += " -undriven"; + continue; + } + if (args[argidx] == "-clkinv") { + opt_expr_args += " -clkinv"; continue; } if (args[argidx] == "-fine") { - opt_const_args += " -fine"; + opt_expr_args += " -fine"; opt_reduce_args += " -fine"; continue; } if (args[argidx] == "-full") { - opt_const_args += " -full"; + opt_expr_args += " -full"; opt_reduce_args += " -full"; continue; } if (args[argidx] == "-keepdc") { - opt_const_args += " -keepdc"; + opt_expr_args += " -keepdc"; + continue; + } + if (args[argidx] == "-share_all") { + opt_merge_args += " -share_all"; continue; } if (args[argidx] == "-fast") { @@ -116,38 +125,42 @@ struct OptPass : public Pass { if (fast_mode) { while (1) { - Pass::call(design, "opt_const" + opt_const_args); - Pass::call(design, "opt_share"); + Pass::call(design, "opt_expr" + opt_expr_args); + Pass::call(design, "opt_merge" + opt_merge_args); design->scratchpad_unset("opt.did_something"); Pass::call(design, "opt_rmdff"); if (design->scratchpad_get_bool("opt.did_something") == false) break; Pass::call(design, "opt_clean" + opt_clean_args); - log_header("Rerunning OPT passes. (Removed registers in this run.)\n"); + log_header(design, "Rerunning OPT passes. (Removed registers in this run.)\n"); } Pass::call(design, "opt_clean" + opt_clean_args); } else { - Pass::call(design, "opt_const" + opt_const_args); - Pass::call(design, "opt_share -nomux"); + Pass::call(design, "opt_expr" + opt_expr_args); + Pass::call(design, "opt_merge -nomux" + opt_merge_args); while (1) { design->scratchpad_unset("opt.did_something"); Pass::call(design, "opt_muxtree"); Pass::call(design, "opt_reduce" + opt_reduce_args); - Pass::call(design, "opt_share"); + Pass::call(design, "opt_merge" + opt_merge_args); Pass::call(design, "opt_rmdff"); Pass::call(design, "opt_clean" + opt_clean_args); - Pass::call(design, "opt_const" + opt_const_args); + Pass::call(design, "opt_expr" + opt_expr_args); if (design->scratchpad_get_bool("opt.did_something") == false) break; - log_header("Rerunning OPT passes. (Maybe there is more to do..)\n"); + log_header(design, "Rerunning OPT passes. (Maybe there is more to do..)\n"); } } - log_header(fast_mode ? "Finished fast OPT passes.\n" : "Finished OPT passes. (There is nothing left to do.)\n"); + design->optimize(); + design->sort(); + design->check(); + + log_header(design, fast_mode ? "Finished fast OPT passes.\n" : "Finished OPT passes. (There is nothing left to do.)\n"); log_pop(); } } OptPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/opt/opt_clean.cc b/passes/opt/opt_clean.cc index 6a7e6051d..6600ffa25 100644 --- a/passes/opt/opt_clean.cc +++ b/passes/opt/opt_clean.cc @@ -2,11 +2,11 @@ * 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 @@ -30,7 +30,55 @@ PRIVATE_NAMESPACE_BEGIN using RTLIL::id2cstr; -CellTypes ct, ct_reg, ct_all; +struct keep_cache_t +{ + Design *design; + dict<Module*, bool> cache; + + void reset(Design *design = nullptr) + { + this->design = design; + cache.clear(); + } + + bool query(Module *module) + { + log_assert(design != nullptr); + + if (module == nullptr) + return false; + + if (cache.count(module)) + return cache.at(module); + + cache[module] = true; + if (!module->get_bool_attribute("\\keep")) { + bool found_keep = false; + for (auto cell : module->cells()) + if (query(cell)) found_keep = true; + cache[module] = found_keep; + } + + return cache[module]; + } + + bool query(Cell *cell) + { + if (cell->type.in("$memwr", "$meminit", "$assert", "$assume")) + return true; + + if (cell->has_keep_attr()) + return true; + + if (cell->module && cell->module->design) + return query(cell->module->design->module(cell->type)); + + return false; + } +}; + +keep_cache_t keep_cache; +CellTypes ct_reg, ct_all; int count_rm_cells, count_rm_wires; void rmunused_module_cells(Module *module, bool verbose) @@ -42,12 +90,12 @@ void rmunused_module_cells(Module *module, bool verbose) for (auto &it : module->cells_) { Cell *cell = it.second; for (auto &it2 : cell->connections()) { - if (!ct.cell_input(cell->type, it2.first)) + if (!ct_all.cell_known(cell->type) || ct_all.cell_output(cell->type, it2.first)) for (auto bit : sigmap(it2.second)) if (bit.wire != nullptr) wire2driver[bit].insert(cell); } - if (cell->type == "$memwr" || cell->type == "$assert" || cell->has_keep_attr()) + if (keep_cache.query(cell)) queue.insert(cell); else unused.insert(cell); @@ -67,7 +115,7 @@ void rmunused_module_cells(Module *module, bool verbose) pool<SigBit> bits; for (auto cell : queue) for (auto &it : cell->connections()) - if (!ct.cell_output(cell->type, it.first)) + if (!ct_all.cell_known(cell->type) || ct_all.cell_input(cell->type, it.first)) for (auto bit : sigmap(it.second)) bits.insert(bit); @@ -108,6 +156,9 @@ bool compare_signals(RTLIL::SigBit &s1, RTLIL::SigBit &s2, SigPool ®s, SigPoo if (w1->port_input != w2->port_input) return w2->port_input; + if ((w1->port_input && w1->port_output) != (w2->port_input && w2->port_output)) + return !(w2->port_input && w2->port_output); + if (w1->name[0] == '\\' && w2->name[0] == '\\') { if (regs.check_any(s1) != regs.check_any(s2)) return regs.check_any(s2); @@ -129,7 +180,7 @@ bool compare_signals(RTLIL::SigBit &s1, RTLIL::SigBit &s2, SigPool ®s, SigPoo if (attrs1 != attrs2) return attrs2 > attrs1; - return w2->name < w1->name; + return strcmp(w2->name.c_str(), w1->name.c_str()) < 0; } bool check_public_name(RTLIL::IdString id) @@ -159,7 +210,7 @@ void rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos for (auto &it2 : cell->connections()) connected_signals.add(it2.second); } - + SigMap assign_map(module); pool<RTLIL::SigSpec> direct_sigs; pool<RTLIL::Wire*> direct_wires; @@ -193,7 +244,7 @@ void rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos for (auto &it2 : cell->connections_) { assign_map.apply(it2.second); used_signals.add(it2.second); - if (!ct.cell_output(cell->type, it2.first)) + if (!ct_all.cell_output(cell->type, it2.first)) used_signals_nodrivers.add(it2.second); } } @@ -216,7 +267,7 @@ void rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos std::vector<RTLIL::Wire*> maybe_del_wires; for (auto wire : module->wires()) { - if ((!purge_mode && check_public_name(wire->name)) || wire->port_id != 0 || wire->get_bool_attribute("\\keep")) { + if ((!purge_mode && check_public_name(wire->name)) || wire->port_id != 0 || wire->get_bool_attribute("\\keep") || wire->attributes.count("\\init")) { RTLIL::SigSpec s1 = RTLIL::SigSpec(wire), s2 = s1; assign_map.apply(s2); if (!used_signals.check_any(s2) && wire->port_id == 0 && !wire->get_bool_attribute("\\keep")) { @@ -296,8 +347,14 @@ void rmunused_module(RTLIL::Module *module, bool purge_mode, bool verbose) module->connect(y, a); delcells.push_back(cell); } - for (auto cell : delcells) + for (auto cell : delcells) { + if (verbose) + log(" removing buffer cell `%s': %s = %s\n", cell->name.c_str(), + log_signal(cell->getPort("\\Y")), log_signal(cell->getPort("\\A"))); module->remove(cell); + } + if (!delcells.empty()) + module->design->scratchpad_set_bool("opt.did_something", true); rmunused_module_cells(module, verbose); rmunused_module_signals(module, purge_mode, verbose); @@ -326,7 +383,7 @@ struct OptCleanPass : public Pass { { bool purge_mode = false; - log_header("Executing OPT_CLEAN pass (remove unused cells and wires).\n"); + log_header(design, "Executing OPT_CLEAN pass (remove unused cells and wires).\n"); log_push(); size_t argidx; @@ -339,26 +396,30 @@ struct OptCleanPass : public Pass { } extra_args(args, argidx, design); - ct.setup_internals(); - ct.setup_internals_mem(); - ct.setup_stdcells(); - ct.setup_stdcells_mem(); + keep_cache.reset(design); ct_reg.setup_internals_mem(); ct_reg.setup_stdcells_mem(); + ct_all.setup(design); + for (auto module : design->selected_whole_modules_warn()) { if (module->has_processes_warn()) continue; rmunused_module(module, purge_mode, true); } - ct.clear(); + design->optimize(); + design->sort(); + design->check(); + + keep_cache.reset(); ct_reg.clear(); + ct_all.clear(); log_pop(); } } OptCleanPass; - + struct CleanPass : public Pass { CleanPass() : Pass("clean", "remove unused cells and wires") { } virtual void help() @@ -391,10 +452,7 @@ struct CleanPass : public Pass { if (argidx < args.size()) extra_args(args, argidx, design); - ct.setup_internals(); - ct.setup_internals_mem(); - ct.setup_stdcells(); - ct.setup_stdcells_mem(); + keep_cache.reset(design); ct_reg.setup_internals_mem(); ct_reg.setup_stdcells_mem(); @@ -404,13 +462,10 @@ struct CleanPass : public Pass { count_rm_cells = 0; count_rm_wires = 0; - for (auto mod : design->selected_whole_modules()) { - if (mod->has_processes()) + for (auto module : design->selected_whole_modules()) { + if (module->has_processes()) continue; - do { - design->scratchpad_unset("opt.did_something"); - rmunused_module(mod, purge_mode, false); - } while (design->scratchpad_get_bool("opt.did_something")); + rmunused_module(module, purge_mode, false); } if (count_rm_cells > 0 || count_rm_wires > 0) @@ -420,10 +475,10 @@ struct CleanPass : public Pass { design->sort(); design->check(); - ct.clear(); + keep_cache.reset(); ct_reg.clear(); ct_all.clear(); } } CleanPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/opt/opt_const.cc b/passes/opt/opt_expr.cc index 1758a34fa..b62eae285 100644 --- a/passes/opt/opt_const.cc +++ b/passes/opt/opt_expr.cc @@ -2,11 +2,11 @@ * 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 @@ -179,14 +179,87 @@ bool group_cell_inputs(RTLIL::Module *module, RTLIL::Cell *cell, bool commutativ log("\n"); } - cover_list("opt.opt_const.fine.group", "$not", "$pos", "$and", "$or", "$xor", "$xnor", cell->type.str()); + cover_list("opt.opt_expr.fine.group", "$not", "$pos", "$and", "$or", "$xor", "$xnor", cell->type.str()); module->remove(cell); did_something = true; return true; } -void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool consume_x, bool mux_undef, bool mux_bool, bool do_fine, bool keepdc) +void handle_polarity_inv(Cell *cell, IdString port, IdString param, const SigMap &assign_map, const dict<RTLIL::SigSpec, RTLIL::SigSpec> &invert_map) +{ + 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_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))); + cell->setParam(param, !cell->getParam(param).as_bool()); + } +} + +void handle_clkpol_celltype_swap(Cell *cell, string type1, string type2, IdString port, const SigMap &assign_map, const dict<RTLIL::SigSpec, RTLIL::SigSpec> &invert_map) +{ + log_assert(GetSize(type1) == GetSize(type2)); + string cell_type = cell->type.str(); + + if (GetSize(type1) != GetSize(cell_type)) + return; + + for (int i = 0; i < GetSize(type1); i++) { + log_assert((type1[i] == '?') == (type2[i] == '?')); + if (type1[i] == '?') { + if (cell_type[i] != '0' && cell_type[i] != '1' && cell_type[i] != 'N' && cell_type[i] != 'P') + return; + type1[i] = cell_type[i]; + type2[i] = cell_type[i]; + } + } + + 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_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))); + cell->type = cell->type == type1 ? type2 : type1; + } + } +} + +bool is_one_or_minus_one(const Const &value, bool is_signed, bool &is_negative) +{ + bool all_bits_one = true; + bool last_bit_one = true; + + if (GetSize(value.bits) < 1) + return false; + + if (GetSize(value.bits) == 1) { + if (value.bits[0] != State::S1) + return false; + if (is_signed) + is_negative = true; + return true; + } + + for (int i = 0; i < GetSize(value.bits); i++) { + if (value.bits[i] != State::S1) + all_bits_one = false; + if (value.bits[i] != (i ? State::S0 : State::S1)) + last_bit_one = false; + } + + if (all_bits_one && is_signed) { + is_negative = true; + return true; + } + + return last_bit_one; +} + +void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool consume_x, bool mux_undef, bool mux_bool, bool do_fine, bool keepdc, bool clkinv) { if (!design->selected(module)) return; @@ -207,6 +280,8 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons if ((cell->type == "$_NOT_" || cell->type == "$not" || cell->type == "$logic_not") && cell->getPort("\\A").size() == 1 && cell->getPort("\\Y").size() == 1) invert_map[assign_map(cell->getPort("\\Y"))] = assign_map(cell->getPort("\\A")); + if ((cell->type == "$mux" || cell->type == "$_MUX_") && cell->getPort("\\A") == SigSpec(State::S1) && cell->getPort("\\B") == SigSpec(State::S0)) + invert_map[assign_map(cell->getPort("\\Y"))] = assign_map(cell->getPort("\\S")); if (ct_combinational.cell_known(cell->type)) for (auto &conn : cell->connections()) { RTLIL::SigSpec sig = assign_map(conn.second); @@ -229,9 +304,106 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons for (auto cell : cells.sorted) { -#define ACTION_DO(_p_, _s_) do { cover("opt.opt_const.action_" S__LINE__); replace_cell(assign_map, module, cell, input.as_string(), _p_, _s_); goto next_cell; } while (0) +#define ACTION_DO(_p_, _s_) do { cover("opt.opt_expr.action_" S__LINE__); replace_cell(assign_map, module, cell, input.as_string(), _p_, _s_); goto next_cell; } while (0) #define ACTION_DO_Y(_v_) ACTION_DO("\\Y", RTLIL::SigSpec(RTLIL::State::S ## _v_)) + if (clkinv) + { + if (cell->type.in("$dff", "$dffe", "$dffsr", "$adff", "$fsm", "$memrd", "$memwr")) + handle_polarity_inv(cell, "\\CLK", "\\CLK_POLARITY", assign_map, invert_map); + + if (cell->type.in("$sr", "$dffsr", "$dlatchsr")) { + handle_polarity_inv(cell, "\\SET", "\\SET_POLARITY", assign_map, invert_map); + handle_polarity_inv(cell, "\\CLR", "\\CLR_POLARITY", assign_map, invert_map); + } + + if (cell->type.in("$dffe", "$dlatch", "$dlatchsr")) + handle_polarity_inv(cell, "\\EN", "\\EN_POLARITY", assign_map, invert_map); + + handle_clkpol_celltype_swap(cell, "$_SR_N?_", "$_SR_P?_", "\\S", assign_map, invert_map); + handle_clkpol_celltype_swap(cell, "$_SR_?N_", "$_SR_?P_", "\\R", assign_map, invert_map); + + handle_clkpol_celltype_swap(cell, "$_DFF_N_", "$_DFF_P_", "\\C", assign_map, invert_map); + + handle_clkpol_celltype_swap(cell, "$_DFFE_N?_", "$_DFFE_P?_", "\\C", assign_map, invert_map); + handle_clkpol_celltype_swap(cell, "$_DFFE_?N_", "$_DFFE_?P_", "\\E", assign_map, invert_map); + + handle_clkpol_celltype_swap(cell, "$_DFF_N??_", "$_DFF_P??_", "\\C", assign_map, invert_map); + handle_clkpol_celltype_swap(cell, "$_DFF_?N?_", "$_DFF_?P?_", "\\R", assign_map, invert_map); + + handle_clkpol_celltype_swap(cell, "$_DFFSR_N??_", "$_DFFSR_P??_", "\\C", assign_map, invert_map); + handle_clkpol_celltype_swap(cell, "$_DFFSR_?N?_", "$_DFFSR_?P?_", "\\S", assign_map, invert_map); + handle_clkpol_celltype_swap(cell, "$_DFFSR_??N_", "$_DFFSR_??P_", "\\R", assign_map, invert_map); + + handle_clkpol_celltype_swap(cell, "$_DLATCH_N_", "$_DLATCH_P_", "\\E", assign_map, invert_map); + + handle_clkpol_celltype_swap(cell, "$_DLATCHSR_N??_", "$_DLATCHSR_P??_", "\\E", assign_map, invert_map); + handle_clkpol_celltype_swap(cell, "$_DLATCHSR_?N?_", "$_DLATCHSR_?P?_", "\\S", assign_map, invert_map); + handle_clkpol_celltype_swap(cell, "$_DLATCHSR_??N_", "$_DLATCHSR_??P_", "\\R", assign_map, invert_map); + } + + bool detect_const_and = false; + bool detect_const_or = false; + + if (cell->type.in("$reduce_and", "$_AND_")) + detect_const_and = true; + + if (cell->type.in("$and", "$logic_and") && GetSize(cell->getPort("\\A")) == 1 && GetSize(cell->getPort("\\B")) == 1) + detect_const_and = true; + + if (cell->type.in("$reduce_or", "$reduce_bool", "$_OR_")) + detect_const_or = true; + + if (cell->type.in("$or", "$logic_or") && GetSize(cell->getPort("\\A")) == 1 && GetSize(cell->getPort("\\B")) == 1) + detect_const_or = true; + + if (detect_const_and || detect_const_or) + { + pool<SigBit> input_bits = assign_map(cell->getPort("\\A")).to_sigbit_pool(); + bool found_zero = false, found_one = false, found_inv = false; + + if (cell->hasPort("\\B")) { + vector<SigBit> more_bits = assign_map(cell->getPort("\\B")).to_sigbit_vector(); + input_bits.insert(more_bits.begin(), more_bits.end()); + } + + for (auto bit : input_bits) { + if (bit == State::S0) + found_zero = true; + if (bit == State::S1) + found_one = true; + if (invert_map.count(bit) && input_bits.count(invert_map.at(bit))) + found_inv = true; + } + + if (detect_const_and && (found_zero || found_inv)) { + cover("opt.opt_expr.const_and"); + replace_cell(assign_map, module, cell, "const_and", "\\Y", RTLIL::State::S0); + goto next_cell; + } + + if (detect_const_or && (found_one || found_inv)) { + cover("opt.opt_expr.const_or"); + replace_cell(assign_map, module, cell, "const_or", "\\Y", RTLIL::State::S1); + goto next_cell; + } + } + + if (cell->type.in("$reduce_and", "$reduce_or", "$reduce_bool", "$reduce_xor", "$reduce_xnor", "$neg") && + GetSize(cell->getPort("\\A")) == 1 && GetSize(cell->getPort("\\Y")) == 1) + { + if (cell->type == "$reduce_xnor") { + cover("opt.opt_expr.reduce_xnor_not"); + log("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"; + } else { + cover("opt.opt_expr.unary_buffer"); + replace_cell(assign_map, module, cell, "unary_buffer", "\\Y", cell->getPort("\\A")); + } + goto next_cell; + } + if (do_fine) { if (cell->type == "$not" || cell->type == "$pos" || @@ -256,7 +428,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_const.fine.$reduce_and"); + cover("opt.opt_expr.fine.$reduce_and"); log("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); @@ -282,7 +454,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_const.fine.A", "$logic_not", "$logic_and", "$logic_or", "$reduce_or", "$reduce_bool", cell->type.str()); + 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", 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); @@ -308,7 +480,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_const.fine.B", "$logic_and", "$logic_or", cell->type.str()); + 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", 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); @@ -318,18 +490,6 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons } } - if (cell->type == "$logic_or" && (assign_map(cell->getPort("\\A")) == RTLIL::State::S1 || assign_map(cell->getPort("\\B")) == RTLIL::State::S1)) { - cover("opt.opt_const.one_high"); - replace_cell(assign_map, module, cell, "one high", "\\Y", RTLIL::State::S1); - goto next_cell; - } - - if (cell->type == "$logic_and" && (assign_map(cell->getPort("\\A")) == RTLIL::State::S0 || assign_map(cell->getPort("\\B")) == RTLIL::State::S0)) { - cover("opt.opt_const.one_low"); - replace_cell(assign_map, module, cell, "one low", "\\Y", RTLIL::State::S0); - goto next_cell; - } - if (cell->type == "$reduce_xor" || cell->type == "$reduce_xnor" || cell->type == "$shift" || cell->type == "$shiftx" || cell->type == "$shl" || cell->type == "$shr" || cell->type == "$sshl" || cell->type == "$sshr" || cell->type == "$lt" || cell->type == "$le" || cell->type == "$ge" || cell->type == "$gt" || @@ -352,7 +512,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons if (0) { found_the_x_bit: - cover_list("opt.opt_const.xbit", "$reduce_xor", "$reduce_xnor", "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx", + cover_list("opt.opt_expr.xbit", "$reduce_xor", "$reduce_xnor", "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx", "$lt", "$le", "$ge", "$gt", "$neg", "$add", "$sub", "$mul", "$div", "$mod", "$pow", cell->type.str()); if (cell->type == "$reduce_xor" || cell->type == "$reduce_xnor" || cell->type == "$lt" || cell->type == "$le" || cell->type == "$ge" || cell->type == "$gt") @@ -365,13 +525,13 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons if ((cell->type == "$_NOT_" || cell->type == "$not" || cell->type == "$logic_not") && cell->getPort("\\Y").size() == 1 && invert_map.count(assign_map(cell->getPort("\\A"))) != 0) { - cover_list("opt.opt_const.invert.double", "$_NOT_", "$not", "$logic_not", cell->type.str()); + cover_list("opt.opt_expr.invert.double", "$_NOT_", "$not", "$logic_not", cell->type.str()); replace_cell(assign_map, module, cell, "double_invert", "\\Y", invert_map.at(assign_map(cell->getPort("\\A")))); goto next_cell; } if ((cell->type == "$_MUX_" || cell->type == "$mux") && invert_map.count(assign_map(cell->getPort("\\S"))) != 0) { - cover_list("opt.opt_const.invert.muxsel", "$_MUX_", "$mux", cell->type.str()); + cover_list("opt.opt_expr.invert.muxsel", "$_MUX_", "$mux", cell->type.str()); log("Optimizing away select inverter for %s cell `%s' in module `%s'.\n", log_id(cell->type), log_id(cell), log_id(module)); RTLIL::SigSpec tmp = cell->getPort("\\A"); cell->setPort("\\A", cell->getPort("\\B")); @@ -454,7 +614,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons if (input.match(" 1")) ACTION_DO("\\Y", input.extract(1, 1)); if (input.match("01 ")) ACTION_DO("\\Y", input.extract(0, 1)); if (input.match("10 ")) { - cover("opt.opt_const.mux_to_inv"); + cover("opt.opt_expr.mux_to_inv"); cell->type = "$_NOT_"; cell->setPort("\\A", input.extract(0, 1)); cell->unsetPort("\\B"); @@ -479,7 +639,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons RTLIL::SigSpec b = cell->getPort("\\B"); if (cell->parameters["\\A_WIDTH"].as_int() != cell->parameters["\\B_WIDTH"].as_int()) { - int width = std::max(cell->parameters["\\A_WIDTH"].as_int(), cell->parameters["\\B_WIDTH"].as_int()); + int width = max(cell->parameters["\\A_WIDTH"].as_int(), cell->parameters["\\B_WIDTH"].as_int()); a.extend_u0(width, cell->parameters["\\A_SIGNED"].as_bool() && cell->parameters["\\B_SIGNED"].as_bool()); b.extend_u0(width, cell->parameters["\\A_SIGNED"].as_bool() && cell->parameters["\\B_SIGNED"].as_bool()); } @@ -489,7 +649,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons log_assert(GetSize(a) == GetSize(b)); for (int i = 0; i < GetSize(a); i++) { if (a[i].wire == NULL && b[i].wire == NULL && a[i] != b[i] && a[i].data <= RTLIL::State::S1 && b[i].data <= RTLIL::State::S1) { - cover_list("opt.opt_const.eqneq.isneq", "$eq", "$ne", "$eqx", "$nex", cell->type.str()); + cover_list("opt.opt_expr.eqneq.isneq", "$eq", "$ne", "$eqx", "$nex", cell->type.str()); RTLIL::SigSpec new_y = RTLIL::SigSpec((cell->type == "$eq" || cell->type == "$eqx") ? RTLIL::State::S0 : RTLIL::State::S1); new_y.extend_u0(cell->parameters["\\Y_WIDTH"].as_int(), false); replace_cell(assign_map, module, cell, "isneq", "\\Y", new_y); @@ -502,7 +662,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons } if (new_a.size() == 0) { - cover_list("opt.opt_const.eqneq.empty", "$eq", "$ne", "$eqx", "$nex", cell->type.str()); + cover_list("opt.opt_expr.eqneq.empty", "$eq", "$ne", "$eqx", "$nex", cell->type.str()); RTLIL::SigSpec new_y = RTLIL::SigSpec((cell->type == "$eq" || cell->type == "$eqx") ? RTLIL::State::S1 : RTLIL::State::S0); new_y.extend_u0(cell->parameters["\\Y_WIDTH"].as_int(), false); replace_cell(assign_map, module, cell, "empty", "\\Y", new_y); @@ -510,7 +670,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons } if (new_a.size() < a.size() || new_b.size() < b.size()) { - cover_list("opt.opt_const.eqneq.resize", "$eq", "$ne", "$eqx", "$nex", cell->type.str()); + cover_list("opt.opt_expr.eqneq.resize", "$eq", "$ne", "$eqx", "$nex", cell->type.str()); cell->setPort("\\A", new_a); cell->setPort("\\B", new_b); cell->parameters["\\A_WIDTH"] = new_a.size(); @@ -525,7 +685,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons RTLIL::SigSpec b = assign_map(cell->getPort("\\B")); if (a.is_fully_const() && !b.is_fully_const()) { - cover_list("opt.opt_const.eqneq.swapconst", "$eq", "$ne", cell->type.str()); + cover_list("opt.opt_expr.eqneq.swapconst", "$eq", "$ne", cell->type.str()); cell->setPort("\\A", b); cell->setPort("\\B", a); std::swap(a, b); @@ -536,7 +696,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons RTLIL::SigSpec input = b; ACTION_DO("\\Y", cell->getPort("\\A")); } else { - cover_list("opt.opt_const.eqneq.isnot", "$eq", "$ne", cell->type.str()); + cover_list("opt.opt_expr.eqneq.isnot", "$eq", "$ne", cell->type.str()); log("Replacing %s cell `%s' in module `%s' with inverter.\n", log_id(cell->type), log_id(cell), log_id(module)); cell->type = "$not"; cell->parameters.erase("\\B_WIDTH"); @@ -548,6 +708,25 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons } } + if ((cell->type == "$eq" || cell->type == "$ne") && + (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_id(module), "$eq" ? "$logic_not" : "$reduce_bool"); + cell->type = cell->type == "$eq" ? "$logic_not" : "$reduce_bool"; + if (assign_map(cell->getPort("\\A")).is_fully_zero()) { + cell->setPort("\\A", cell->getPort("\\B")); + cell->setParam("\\A_SIGNED", cell->getParam("\\B_SIGNED")); + cell->setParam("\\A_WIDTH", cell->getParam("\\B_WIDTH")); + } + cell->unsetPort("\\B"); + cell->unsetParam("\\B_SIGNED"); + cell->unsetParam("\\B_WIDTH"); + did_something = true; + goto next_cell; + } + if (cell->type.in("$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx") && assign_map(cell->getPort("\\B")).is_fully_const()) { bool sign_ext = cell->type == "$sshr" && cell->getParam("\\A_SIGNED").as_bool(); @@ -570,7 +749,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons sig_y[i] = sig_a[GetSize(sig_a)-1]; } - cover_list("opt.opt_const.constshift", "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx", cell->type.str()); + cover_list("opt.opt_expr.constshift", "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx", cell->type.str()); log("Replacing %s cell `%s' (B=%s, SHR=%d) in module `%s' with fixed wiring: %s\n", log_id(cell->type), log_id(cell), log_signal(assign_map(cell->getPort("\\B"))), shift_bits, log_id(module), log_signal(sig_y)); @@ -586,6 +765,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons { bool identity_wrt_a = false; bool identity_wrt_b = false; + bool arith_inverse = false; if (cell->type == "$add" || cell->type == "$sub" || cell->type == "$or" || cell->type == "$xor") { @@ -612,10 +792,10 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons RTLIL::SigSpec a = assign_map(cell->getPort("\\A")); RTLIL::SigSpec b = assign_map(cell->getPort("\\B")); - if (a.is_fully_const() && a.size() <= 32 && a.as_int() == 1) + if (a.is_fully_const() && is_one_or_minus_one(a.as_const(), cell->getParam("\\A_SIGNED").as_bool(), arith_inverse)) identity_wrt_b = true; - - if (b.is_fully_const() && b.size() <= 32 && b.as_int() == 1) + else + if (b.is_fully_const() && is_one_or_minus_one(b.as_const(), cell->getParam("\\B_SIGNED").as_bool(), arith_inverse)) identity_wrt_a = true; } @@ -630,9 +810,9 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons if (identity_wrt_a || identity_wrt_b) { if (identity_wrt_a) - cover_list("opt.opt_const.identwrt.a", "$add", "$sub", "$or", "$xor", "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx", "$mul", "$div", cell->type.str()); + cover_list("opt.opt_expr.identwrt.a", "$add", "$sub", "$or", "$xor", "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx", "$mul", "$div", cell->type.str()); if (identity_wrt_b) - cover_list("opt.opt_const.identwrt.b", "$add", "$sub", "$or", "$xor", "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx", "$mul", "$div", cell->type.str()); + 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", cell->type.c_str(), cell->name.c_str(), module->name.c_str(), identity_wrt_a ? 'A' : 'B'); @@ -643,7 +823,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons cell->parameters.at("\\A_SIGNED") = cell->parameters.at("\\B_SIGNED"); } - cell->type = "$pos"; + cell->type = arith_inverse ? "$neg" : "$pos"; cell->unsetPort("\\B"); cell->parameters.erase("\\B_WIDTH"); cell->parameters.erase("\\B_SIGNED"); @@ -656,14 +836,14 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons if (mux_bool && (cell->type == "$mux" || cell->type == "$_MUX_") && cell->getPort("\\A") == RTLIL::SigSpec(0, 1) && cell->getPort("\\B") == RTLIL::SigSpec(1, 1)) { - cover_list("opt.opt_const.mux_bool", "$mux", "$_MUX_", cell->type.str()); + cover_list("opt.opt_expr.mux_bool", "$mux", "$_MUX_", cell->type.str()); replace_cell(assign_map, module, cell, "mux_bool", "\\Y", cell->getPort("\\S")); goto next_cell; } if (mux_bool && (cell->type == "$mux" || cell->type == "$_MUX_") && cell->getPort("\\A") == RTLIL::SigSpec(1, 1) && cell->getPort("\\B") == RTLIL::SigSpec(0, 1)) { - cover_list("opt.opt_const.mux_invert", "$mux", "$_MUX_", cell->type.str()); + cover_list("opt.opt_expr.mux_invert", "$mux", "$_MUX_", cell->type.str()); log("Replacing %s cell `%s' in module `%s' with inverter.\n", log_id(cell->type), log_id(cell), log_id(module)); cell->setPort("\\A", cell->getPort("\\S")); cell->unsetPort("\\B"); @@ -682,7 +862,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_const.mux_and", "$mux", "$_MUX_", cell->type.str()); + cover_list("opt.opt_expr.mux_and", "$mux", "$_MUX_", cell->type.str()); log("Replacing %s cell `%s' in module `%s' with and-gate.\n", log_id(cell->type), log_id(cell), log_id(module)); cell->setPort("\\A", cell->getPort("\\S")); cell->unsetPort("\\S"); @@ -702,7 +882,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_const.mux_or", "$mux", "$_MUX_", cell->type.str()); + cover_list("opt.opt_expr.mux_or", "$mux", "$_MUX_", cell->type.str()); log("Replacing %s cell `%s' in module `%s' with or-gate.\n", log_id(cell->type), log_id(cell), log_id(module)); cell->setPort("\\B", cell->getPort("\\S")); cell->unsetPort("\\S"); @@ -726,7 +906,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons int width = cell->getPort("\\A").size(); if ((cell->getPort("\\A").is_fully_undef() && cell->getPort("\\B").is_fully_undef()) || cell->getPort("\\S").is_fully_undef()) { - cover_list("opt.opt_const.mux_undef", "$mux", "$pmux", cell->type.str()); + cover_list("opt.opt_expr.mux_undef", "$mux", "$pmux", cell->type.str()); replace_cell(assign_map, module, cell, "mux_undef", "\\Y", cell->getPort("\\A")); goto next_cell; } @@ -745,17 +925,17 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons new_s = new_s.extract(0, new_s.size()-1); } if (new_s.size() == 0) { - cover_list("opt.opt_const.mux_empty", "$mux", "$pmux", cell->type.str()); + cover_list("opt.opt_expr.mux_empty", "$mux", "$pmux", cell->type.str()); replace_cell(assign_map, module, cell, "mux_empty", "\\Y", new_a); goto next_cell; } if (new_a == RTLIL::SigSpec(RTLIL::State::S0) && new_b == RTLIL::SigSpec(RTLIL::State::S1)) { - cover_list("opt.opt_const.mux_sel01", "$mux", "$pmux", cell->type.str()); + cover_list("opt.opt_expr.mux_sel01", "$mux", "$pmux", cell->type.str()); replace_cell(assign_map, module, cell, "mux_sel01", "\\Y", new_s); goto next_cell; } if (cell->getPort("\\S").size() != new_s.size()) { - cover_list("opt.opt_const.mux_reduce", "$mux", "$pmux", cell->type.str()); + cover_list("opt.opt_expr.mux_reduce", "$mux", "$pmux", cell->type.str()); log("Optimized away %d select inputs of %s cell `%s' in module `%s'.\n", GetSize(cell->getPort("\\S")) - GetSize(new_s), log_id(cell->type), log_id(cell), log_id(module)); cell->setPort("\\A", new_a); @@ -781,7 +961,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons RTLIL::SigSpec y(RTLIL::const_ ## _t(a.as_const(), dummy_arg, \ cell->parameters["\\A_SIGNED"].as_bool(), false, \ cell->parameters["\\Y_WIDTH"].as_int())); \ - cover("opt.opt_const.const.$" #_t); \ + cover("opt.opt_expr.const.$" #_t); \ replace_cell(assign_map, module, cell, stringf("%s", log_signal(a)), "\\Y", y); \ goto next_cell; \ } \ @@ -796,7 +976,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons cell->parameters["\\A_SIGNED"].as_bool(), \ cell->parameters["\\B_SIGNED"].as_bool(), \ cell->parameters["\\Y_WIDTH"].as_int())); \ - cover("opt.opt_const.const.$" #_t); \ + cover("opt.opt_expr.const.$" #_t); \ replace_cell(assign_map, module, cell, stringf("%s, %s", log_signal(a), log_signal(b)), "\\Y", y); \ goto next_cell; \ } \ @@ -872,7 +1052,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons if (a_val == 0) { - cover("opt.opt_const.mul_shift.zero"); + cover("opt.opt_expr.mul_shift.zero"); log("Replacing multiply-by-zero cell `%s' in module `%s' with zero-driver.\n", cell->name.c_str(), module->name.c_str()); @@ -888,9 +1068,9 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons if (a_val == (1 << i)) { if (swapped_ab) - cover("opt.opt_const.mul_shift.swapped"); + cover("opt.opt_expr.mul_shift.swapped"); else - cover("opt.opt_const.mul_shift.unswapped"); + cover("opt.opt_expr.mul_shift.unswapped"); log("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); @@ -918,6 +1098,75 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons } } + if (!keepdc && cell->type.in("$div", "$mod")) + { + bool b_signed = cell->parameters["\\B_SIGNED"].as_bool(); + SigSpec sig_b = assign_map(cell->getPort("\\B")); + SigSpec sig_y = assign_map(cell->getPort("\\Y")); + + if (sig_b.is_fully_def() && sig_b.size() <= 32) + { + int b_val = sig_b.as_int(); + + if (b_val == 0) + { + cover("opt.opt_expr.divmod_zero"); + + log("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()))); + module->remove(cell); + + did_something = true; + goto next_cell; + } + + for (int i = 1; i < (b_signed ? sig_b.size()-1 : sig_b.size()); i++) + if (b_val == (1 << i)) + { + if (cell->type == "$div") + { + cover("opt.opt_expr.div_shift"); + + log("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); + + while (GetSize(new_b) > 1 && new_b.back() == RTLIL::State::S0) + new_b.pop_back(); + + cell->type = "$shr"; + cell->parameters["\\B_WIDTH"] = GetSize(new_b); + cell->parameters["\\B_SIGNED"] = false; + cell->setPort("\\B", new_b); + cell->check(); + } + else + { + cover("opt.opt_expr.mod_mask"); + + log("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); + + if (b_signed) + new_b.push_back(State::S0); + + cell->type = "$and"; + cell->parameters["\\B_WIDTH"] = GetSize(new_b); + cell->setPort("\\B", new_b); + cell->check(); + } + + did_something = true; + goto next_cell; + } + } + } + next_cell:; #undef ACTION_DO #undef ACTION_DO_Y @@ -926,15 +1175,16 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons } } -struct OptConstPass : public Pass { - OptConstPass() : Pass("opt_const", "perform const folding") { } +struct OptExprPass : public Pass { + OptExprPass() : Pass("opt_expr", "perform const folding and simple expression rewriting") { } virtual void help() { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); - log(" opt_const [options] [selection]\n"); + log(" opt_expr [options] [selection]\n"); log("\n"); log("This pass performs const folding on internal cell types with constant inputs.\n"); + log("It also performs some simple expression rewritring.\n"); log("\n"); log(" -mux_undef\n"); log(" remove 'undef' inputs from $mux, $pmux and $_MUX_ cells\n"); @@ -945,6 +1195,9 @@ struct OptConstPass : public Pass { log(" -undriven\n"); log(" replace undriven nets with undef (x) constants\n"); log("\n"); + log(" -clkinv\n"); + log(" optimize clock inverters by changing FF types\n"); + log("\n"); log(" -fine\n"); log(" perform fine-grain optimizations\n"); log("\n"); @@ -963,10 +1216,11 @@ struct OptConstPass : public Pass { bool mux_undef = false; bool mux_bool = false; bool undriven = false; + bool clkinv = false; bool do_fine = false; bool keepdc = false; - log_header("Executing OPT_CONST pass (perform const folding).\n"); + log_header(design, "Executing OPT_EXPR pass (perform const folding).\n"); log_push(); size_t argidx; @@ -983,6 +1237,10 @@ struct OptConstPass : public Pass { undriven = true; continue; } + if (args[argidx] == "-clkinv") { + clkinv = true; + continue; + } if (args[argidx] == "-fine") { do_fine = true; continue; @@ -1010,16 +1268,16 @@ struct OptConstPass : public Pass { do { do { did_something = false; - replace_const_cells(design, module, false, mux_undef, mux_bool, do_fine, keepdc); + replace_const_cells(design, module, false, mux_undef, mux_bool, do_fine, keepdc, clkinv); if (did_something) design->scratchpad_set_bool("opt.did_something", true); } while (did_something); - replace_const_cells(design, module, true, mux_undef, mux_bool, do_fine, keepdc); + replace_const_cells(design, module, true, mux_undef, mux_bool, do_fine, keepdc, clkinv); } while (did_something); } log_pop(); } -} OptConstPass; +} OptExprPass; PRIVATE_NAMESPACE_END diff --git a/passes/opt/opt_share.cc b/passes/opt/opt_merge.cc index cce97d65b..97989d271 100644 --- a/passes/opt/opt_share.cc +++ b/passes/opt/opt_merge.cc @@ -2,11 +2,11 @@ * 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 @@ -31,12 +31,13 @@ USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN -struct OptShareWorker +struct OptMergeWorker { RTLIL::Design *design; RTLIL::Module *module; SigMap assign_map; SigMap dff_init_map; + bool mode_share_all; CellTypes ct; int total_count; @@ -44,6 +45,29 @@ struct OptShareWorker dict<const RTLIL::Cell*, std::string> cell_hash_cache; #endif + static void sort_pmux_conn(dict<RTLIL::IdString, RTLIL::SigSpec> &conn) + { + SigSpec sig_s = conn.at("\\S"); + SigSpec sig_b = conn.at("\\B"); + + int s_width = GetSize(sig_s); + int width = GetSize(sig_b) / s_width; + + vector<pair<SigBit, SigSpec>> sb_pairs; + for (int i = 0; i < s_width; i++) + sb_pairs.push_back(pair<SigBit, SigSpec>(sig_s[i], sig_b.extract(i*width, width))); + + std::sort(sb_pairs.begin(), sb_pairs.end()); + + conn["\\S"] = SigSpec(); + conn["\\B"] = SigSpec(); + + for (auto &it : sb_pairs) { + conn["\\S"].append(it.first); + conn["\\B"].append(it.second); + } + } + #ifdef USE_CELL_HASH_CACHE std::string int_to_hash_string(unsigned int v) { @@ -90,25 +114,40 @@ struct OptShareWorker assign_map.apply(alt_conn.at("\\A")); alt_conn.at("\\A").sort_and_unify(); conn = &alt_conn; + } else + if (cell->type == "$pmux") { + alt_conn = *conn; + assign_map.apply(alt_conn.at("\\A")); + assign_map.apply(alt_conn.at("\\B")); + assign_map.apply(alt_conn.at("\\S")); + sort_pmux_conn(alt_conn); + conn = &alt_conn; } + vector<string> hash_conn_strings; + for (auto &it : *conn) { - if (ct.cell_output(cell->type, it.first)) + if (cell->output(it.first)) continue; RTLIL::SigSpec sig = it.second; assign_map.apply(sig); - hash_string += "C " + it.first.str() + "="; + string s = "C " + it.first.str() + "="; for (auto &chunk : sig.chunks()) { if (chunk.wire) - hash_string += "{" + chunk.wire->name.str() + " " + + s += "{" + chunk.wire->name.str() + " " + int_to_hash_string(chunk.offset) + " " + int_to_hash_string(chunk.width) + "}"; else - hash_string += RTLIL::Const(chunk.data).as_string(); + s += RTLIL::Const(chunk.data).as_string(); } - hash_string += "\n"; + hash_conn_strings.push_back(s + "\n"); } + std::sort(hash_conn_strings.begin(), hash_conn_strings.end()); + + for (auto it : hash_conn_strings) + hash_string += it; + cell_hash_cache[cell] = sha1(hash_string); return cell_hash_cache[cell]; } @@ -137,14 +176,14 @@ struct OptShareWorker dict<RTLIL::IdString, RTLIL::SigSpec> conn2 = cell2->connections(); for (auto &it : conn1) { - if (ct.cell_output(cell1->type, it.first)) + if (cell1->output(it.first)) it.second = RTLIL::SigSpec(); else assign_map.apply(it.second); } for (auto &it : conn2) { - if (ct.cell_output(cell2->type, it.first)) + if (cell2->output(it.first)) it.second = RTLIL::SigSpec(); else assign_map.apply(it.second); @@ -170,6 +209,10 @@ struct OptShareWorker if (cell1->type == "$reduce_and" || cell1->type == "$reduce_or" || cell1->type == "$reduce_bool") { conn1["\\A"].sort_and_unify(); conn2["\\A"].sort_and_unify(); + } else + if (cell1->type == "$pmux") { + sort_pmux_conn(conn1); + sort_pmux_conn(conn2); } if (conn1 != conn2) { @@ -197,7 +240,7 @@ struct OptShareWorker if (cell1->type != cell2->type) return cell1->type < cell2->type; - if (!ct.cell_known(cell1->type)) + if ((!mode_share_all && !ct.cell_known(cell1->type)) || !cell1->known()) return cell1 < cell2; if (cell1->has_keep_attr() || cell2->has_keep_attr()) @@ -211,15 +254,15 @@ struct OptShareWorker } struct CompareCells { - OptShareWorker *that; - CompareCells(OptShareWorker *that) : that(that) {} + OptMergeWorker *that; + CompareCells(OptMergeWorker *that) : that(that) {} bool operator()(const RTLIL::Cell *cell1, const RTLIL::Cell *cell2) const { return that->compare_cells(cell1, cell2); } }; - OptShareWorker(RTLIL::Design *design, RTLIL::Module *module, bool mode_nomux) : - design(design), module(module), assign_map(module) + OptMergeWorker(RTLIL::Design *design, RTLIL::Module *module, bool mode_nomux, bool mode_share_all) : + design(design), module(module), assign_map(module), mode_share_all(mode_share_all) { total_count = 0; ct.setup_internals(); @@ -249,7 +292,9 @@ struct OptShareWorker std::vector<RTLIL::Cell*> cells; cells.reserve(module->cells_.size()); for (auto &it : module->cells_) { - if (ct.cell_known(it.second->type) && design->selected(module, it.second)) + if (!design->selected(module, it.second)) + continue; + if (ct.cell_known(it.second->type) || (mode_share_all && it.second->known())) cells.push_back(it.second); } @@ -261,7 +306,7 @@ struct OptShareWorker did_something = true; log(" Cell `%s' is identical to cell `%s'.\n", cell->name.c_str(), sharemap[cell]->name.c_str()); for (auto &it : cell->connections()) { - if (ct.cell_output(cell->type, it.first)) { + 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_signal(it.second), log_signal(other_sig)); @@ -270,7 +315,9 @@ struct OptShareWorker } } log(" 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 module->remove(cell); total_count++; } else { @@ -281,13 +328,13 @@ struct OptShareWorker } }; -struct OptSharePass : public Pass { - OptSharePass() : Pass("opt_share", "consolidate identical cells") { } +struct OptMergePass : public Pass { + OptMergePass() : Pass("opt_merge", "consolidate identical cells") { } virtual void help() { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); - log(" opt_share [-nomux] [selection]\n"); + log(" opt_merge [options] [selection]\n"); log("\n"); log("This pass identifies cells with identical type and input signals. Such cells\n"); log("are then merged to one cell.\n"); @@ -295,12 +342,16 @@ struct OptSharePass : public Pass { log(" -nomux\n"); log(" Do not merge MUX cells.\n"); log("\n"); + log(" -share_all\n"); + log(" Operate on all cell types, not just built-in types.\n"); + log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing OPT_SHARE pass (detect identical cells).\n"); + log_header(design, "Executing OPT_MERGE pass (detect identical cells).\n"); bool mode_nomux = false; + bool mode_share_all = false; size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { @@ -309,13 +360,17 @@ struct OptSharePass : public Pass { mode_nomux = true; continue; } + if (arg == "-share_all") { + mode_share_all = true; + continue; + } break; } extra_args(args, argidx, design); int total_count = 0; for (auto module : design->selected_modules()) { - OptShareWorker worker(design, module, mode_nomux); + OptMergeWorker worker(design, module, mode_nomux, mode_share_all); total_count += worker.total_count; } @@ -323,6 +378,6 @@ struct OptSharePass : public Pass { design->scratchpad_set_bool("opt.did_something", true); log("Removed a total of %d cells.\n", total_count); } -} OptSharePass; - +} OptMergePass; + PRIVATE_NAMESPACE_END diff --git a/passes/opt/opt_muxtree.cc b/passes/opt/opt_muxtree.cc index 982870745..f5ddc2af9 100644 --- a/passes/opt/opt_muxtree.cc +++ b/passes/opt/opt_muxtree.cc @@ -2,11 +2,11 @@ * 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 @@ -68,7 +68,7 @@ struct OptMuxtreeWorker OptMuxtreeWorker(RTLIL::Design *design, RTLIL::Module *module) : design(design), module(module), assign_map(module), removed_count(0) { - log("Running muxtree optimizier on module %s..\n", module->name.c_str()); + log("Running muxtree optimizer on module %s..\n", module->name.c_str()); log(" Creating internal representation of mux trees.\n"); @@ -136,7 +136,7 @@ struct OptMuxtreeWorker } } for (auto wire : module->wires()) { - if (wire->port_output) + if (wire->port_output || wire->get_bool_attribute("\\keep")) for (int idx : sig2bits(RTLIL::SigSpec(wire))) bit2info[idx].seen_non_mux = true; } @@ -464,7 +464,7 @@ struct OptMuxtreePass : public Pass { } virtual void execute(vector<std::string> args, RTLIL::Design *design) { - log_header("Executing OPT_MUXTREE pass (detect dead branches in mux trees).\n"); + log_header(design, "Executing OPT_MUXTREE pass (detect dead branches in mux trees).\n"); extra_args(args, 1, design); int total_count = 0; @@ -479,5 +479,5 @@ struct OptMuxtreePass : public Pass { log("Removed %d multiplexer ports.\n", total_count); } } OptMuxtreePass; - + PRIVATE_NAMESPACE_END diff --git a/passes/opt/opt_reduce.cc b/passes/opt/opt_reduce.cc index 5c36eb26b..eb9d02ad5 100644 --- a/passes/opt/opt_reduce.cc +++ b/passes/opt/opt_reduce.cc @@ -2,11 +2,11 @@ * 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 @@ -284,7 +284,7 @@ struct OptReduceWorker did_something = false; // merge trees of reduce_* cells to one single cell and unify input vectors - // (only handle recduce_and and reduce_or for various reasons) + // (only handle reduce_and and reduce_or for various reasons) const char *type_list[] = { "$reduce_or", "$reduce_and" }; for (auto type : type_list) @@ -354,7 +354,7 @@ struct OptReducePass : public Pass { { bool do_fine = false; - log_header("Executing OPT_REDUCE pass (consolidate $*mux and $reduce_* inputs).\n"); + log_header(design, "Executing OPT_REDUCE pass (consolidate $*mux and $reduce_* inputs).\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { @@ -384,5 +384,5 @@ struct OptReducePass : public Pass { log("Performed a total of %d changes.\n", total_count); } } OptReducePass; - + PRIVATE_NAMESPACE_END diff --git a/passes/opt/opt_rmdff.cc b/passes/opt/opt_rmdff.cc index 5f52bb8d8..1711d0f45 100644 --- a/passes/opt/opt_rmdff.cc +++ b/passes/opt/opt_rmdff.cc @@ -2,11 +2,11 @@ * 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 @@ -28,6 +28,43 @@ PRIVATE_NAMESPACE_BEGIN SigMap assign_map, dff_init_map; SigSet<RTLIL::Cell*> mux_drivers; +dict<SigBit, pool<SigBit>> init_attributes; + +void remove_init_attr(SigSpec sig) +{ + for (auto bit : assign_map(sig)) + if (init_attributes.count(bit)) + for (auto wbit : init_attributes.at(bit)) + wbit.wire->attributes.at("\\init")[wbit.offset] = State::Sx; +} + +bool handle_dlatch(RTLIL::Module *mod, RTLIL::Cell *dlatch) +{ + SigSpec sig_e = dlatch->getPort("\\EN"); + + if (sig_e == State::S0) + { + RTLIL::Const val_init; + for (auto bit : dff_init_map(dlatch->getPort("\\Q"))) + val_init.bits.push_back(bit.wire == NULL ? bit.data : State::Sx); + mod->connect(dlatch->getPort("\\Q"), val_init); + goto delete_dlatch; + } + + if (sig_e == State::S1) + { + mod->connect(dlatch->getPort("\\Q"), dlatch->getPort("\\D")); + goto delete_dlatch; + } + + return false; + +delete_dlatch: + log("Removing %s (%s) from module %s.\n", dlatch->name.c_str(), dlatch->type.c_str(), mod->name.c_str()); + remove_init_attr(dlatch->getPort("\\Q")); + mod->remove(dlatch); + return true; +} bool handle_dff(RTLIL::Module *mod, RTLIL::Cell *dff) { @@ -83,60 +120,50 @@ bool handle_dff(RTLIL::Module *mod, RTLIL::Cell *dff) val_init.bits.push_back(bit.wire == NULL ? bit.data : RTLIL::State::Sx); } - if (dff->type == "$dff" && mux_drivers.has(sig_d) && !has_init) { + if (dff->type == "$dff" && mux_drivers.has(sig_d)) { std::set<RTLIL::Cell*> muxes; mux_drivers.find(sig_d, muxes); for (auto mux : muxes) { RTLIL::SigSpec sig_a = assign_map(mux->getPort("\\A")); RTLIL::SigSpec sig_b = assign_map(mux->getPort("\\B")); - if (sig_a == sig_q && sig_b.is_fully_const()) { - RTLIL::SigSig conn(sig_q, sig_b); - mod->connect(conn); + if (sig_a == sig_q && sig_b.is_fully_const() && (!has_init || val_init == sig_b.as_const())) { + mod->connect(sig_q, sig_b); goto delete_dff; } - if (sig_b == sig_q && sig_a.is_fully_const()) { - RTLIL::SigSig conn(sig_q, sig_a); - mod->connect(conn); + if (sig_b == sig_q && sig_a.is_fully_const() && (!has_init || val_init == sig_a.as_const())) { + mod->connect(sig_q, sig_a); goto delete_dff; } } } - if (sig_c.is_fully_const() && (!sig_r.size() || !has_init)) { + if (sig_c.is_fully_const() && (!sig_r.size() || !has_init || val_init == val_rv)) { if (val_rv.bits.size() == 0) val_rv = val_init; - RTLIL::SigSig conn(sig_q, val_rv); - mod->connect(conn); + mod->connect(sig_q, val_rv); goto delete_dff; } - if (sig_d.is_fully_undef() && sig_r.size() && !has_init) { - RTLIL::SigSig conn(sig_q, val_rv); - mod->connect(conn); + if (sig_d.is_fully_undef() && sig_r.size() && (!has_init || val_init == val_rv)) { + mod->connect(sig_q, val_rv); goto delete_dff; } if (sig_d.is_fully_undef() && !sig_r.size() && has_init) { - RTLIL::SigSig conn(sig_q, val_init); - mod->connect(conn); + mod->connect(sig_q, val_init); goto delete_dff; } - if (sig_d.is_fully_const() && !sig_r.size() && !has_init) { - RTLIL::SigSig conn(sig_q, sig_d); - mod->connect(conn); + if (sig_d.is_fully_const() && (!sig_r.size() || val_rv == sig_d.as_const()) && (!has_init || val_init == sig_d.as_const())) { + mod->connect(sig_q, sig_d); goto delete_dff; } - if (sig_d == sig_q && !(sig_r.size() && has_init)) { - if (sig_r.size()) { - RTLIL::SigSig conn(sig_q, val_rv); - mod->connect(conn); - } - if (has_init) { - RTLIL::SigSig conn(sig_q, val_init); - mod->connect(conn); - } + if (sig_d == sig_q && (!sig_r.size() || !has_init || val_init == val_rv)) { + if (sig_r.size()) + mod->connect(sig_q, val_rv); + if (has_init) + mod->connect(sig_q, val_init); goto delete_dff; } @@ -144,6 +171,7 @@ bool handle_dff(RTLIL::Module *mod, RTLIL::Cell *dff) delete_dff: log("Removing %s (%s) from module %s.\n", dff->name.c_str(), dff->type.c_str(), mod->name.c_str()); + remove_init_attr(dff->getPort("\\Q")); mod->remove(dff); return true; } @@ -163,7 +191,7 @@ struct OptRmdffPass : public Pass { virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { int total_count = 0; - log_header("Executing OPT_RMDFF pass (remove dff with constant values).\n"); + log_header(design, "Executing OPT_RMDFF pass (remove dff with constant values).\n"); extra_args(args, 1, design); @@ -175,11 +203,18 @@ struct OptRmdffPass : public Pass { assign_map.set(mod_it.second); dff_init_map.set(mod_it.second); for (auto &it : mod_it.second->wires_) - if (it.second->attributes.count("\\init") != 0) + if (it.second->attributes.count("\\init") != 0) { dff_init_map.add(it.second, it.second->attributes.at("\\init")); + for (int i = 0; i < GetSize(it.second); i++) { + SigBit wire_bit(it.second, i), mapped_bit = assign_map(wire_bit); + if (mapped_bit.wire) + init_attributes[mapped_bit].insert(wire_bit); + } + } mux_drivers.clear(); std::vector<RTLIL::IdString> dff_list; + std::vector<RTLIL::IdString> dlatch_list; for (auto &it : mod_it.second->cells_) { if (it.second->type == "$mux" || it.second->type == "$pmux") { if (it.second->getPort("\\A").size() == it.second->getPort("\\B").size()) @@ -200,6 +235,7 @@ struct OptRmdffPass : public Pass { if (it.second->type == "$_DFF_PP1_") dff_list.push_back(it.first); if (it.second->type == "$dff") dff_list.push_back(it.first); if (it.second->type == "$adff") dff_list.push_back(it.first); + if (it.second->type == "$dlatch") dlatch_list.push_back(it.first); } for (auto &id : dff_list) { @@ -207,6 +243,12 @@ struct OptRmdffPass : public Pass { handle_dff(mod_it.second, mod_it.second->cells_[id])) total_count++; } + + for (auto &id : dlatch_list) { + if (mod_it.second->cells_.count(id) > 0 && + handle_dlatch(mod_it.second, mod_it.second->cells_[id])) + total_count++; + } } assign_map.clear(); @@ -217,5 +259,5 @@ struct OptRmdffPass : public Pass { log("Replaced %d DFF cells.\n", total_count); } } OptRmdffPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/opt/share.cc b/passes/opt/share.cc index 3133cb2a6..22914eaa7 100644 --- a/passes/opt/share.cc +++ b/passes/opt/share.cc @@ -2,11 +2,11 @@ * 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 @@ -113,8 +113,8 @@ struct ShareWorker static int bits_macc_port(const Macc::port_t &p, int width) { if (GetSize(p.in_a) == 0 || GetSize(p.in_b) == 0) - return std::min(std::max(GetSize(p.in_a), GetSize(p.in_b)), width); - return std::min(GetSize(p.in_a), width) * std::min(GetSize(p.in_b), width) / 2; + return min(max(GetSize(p.in_a), GetSize(p.in_b)), width); + return min(GetSize(p.in_a), width) * min(GetSize(p.in_b), width) / 2; } static int bits_macc(const Macc &m, int width) @@ -224,13 +224,13 @@ struct ShareWorker supermacc->ports.push_back(p); } - int score = 1000 + abs(GetSize(p1.in_a) - GetSize(p2.in_a)) * std::max(abs(GetSize(p1.in_b) - GetSize(p2.in_b)), 1); + int score = 1000 + abs(GetSize(p1.in_a) - GetSize(p2.in_a)) * max(abs(GetSize(p1.in_b) - GetSize(p2.in_b)), 1); - for (int i = 0; i < std::min(GetSize(p1.in_a), GetSize(p2.in_a)); i++) + for (int i = 0; i < min(GetSize(p1.in_a), GetSize(p2.in_a)); i++) if (p1.in_a[i] == p2.in_a[i] && score > 0) score--; - for (int i = 0; i < std::min(GetSize(p1.in_b), GetSize(p2.in_b)); i++) + for (int i = 0; i < min(GetSize(p1.in_b), GetSize(p2.in_b)); i++) if (p1.in_b[i] == p2.in_b[i] && score > 0) score--; @@ -243,7 +243,7 @@ struct ShareWorker Macc m1(c1), m2(c2), supermacc; int w1 = GetSize(c1->getPort("\\Y")), w2 = GetSize(c2->getPort("\\Y")); - int width = std::max(w1, w2); + int width = max(w1, w2); m1.optimize(w1); m2.optimize(w2); @@ -369,7 +369,9 @@ struct ShareWorker } if (cell->type == "$memrd") { - if (!cell->parameters.at("\\CLK_ENABLE").as_bool()) + if (cell->parameters.at("\\CLK_ENABLE").as_bool()) + continue; + if (config.opt_aggressive || !modwalker.sigmap(cell->getPort("\\ADDR")).is_fully_const()) shareable_cells.insert(cell); continue; } @@ -387,7 +389,7 @@ struct ShareWorker } if (generic_ops.count(cell->type)) { - if (config.opt_aggressive || cell->parameters.at("\\Y_WIDTH").as_int() >= 10) + if (config.opt_aggressive) shareable_cells.insert(cell); continue; } @@ -417,8 +419,8 @@ struct ShareWorker int a2_width = c2->parameters.at("\\A_WIDTH").as_int(); int y2_width = c2->parameters.at("\\Y_WIDTH").as_int(); - if (std::max(a1_width, a2_width) > 2 * std::min(a1_width, a2_width)) return false; - if (std::max(y1_width, y2_width) > 2 * std::min(y1_width, y2_width)) return false; + if (max(a1_width, a2_width) > 2 * min(a1_width, a2_width)) return false; + if (max(y1_width, y2_width) > 2 * min(y1_width, y2_width)) return false; } return true; @@ -436,9 +438,9 @@ struct ShareWorker int b2_width = c2->parameters.at("\\B_WIDTH").as_int(); int y2_width = c2->parameters.at("\\Y_WIDTH").as_int(); - if (std::max(a1_width, a2_width) > 2 * std::min(a1_width, a2_width)) return false; - if (std::max(b1_width, b2_width) > 2 * std::min(b1_width, b2_width)) return false; - if (std::max(y1_width, y2_width) > 2 * std::min(y1_width, y2_width)) return false; + if (max(a1_width, a2_width) > 2 * min(a1_width, a2_width)) return false; + if (max(b1_width, b2_width) > 2 * min(b1_width, b2_width)) return false; + if (max(y1_width, y2_width) > 2 * min(y1_width, y2_width)) return false; } return true; @@ -456,15 +458,15 @@ struct ShareWorker int b2_width = c2->parameters.at("\\B_WIDTH").as_int(); int y2_width = c2->parameters.at("\\Y_WIDTH").as_int(); - int min1_width = std::min(a1_width, b1_width); - int max1_width = std::max(a1_width, b1_width); + int min1_width = min(a1_width, b1_width); + int max1_width = max(a1_width, b1_width); - int min2_width = std::min(a2_width, b2_width); - int max2_width = std::max(a2_width, b2_width); + int min2_width = min(a2_width, b2_width); + int max2_width = max(a2_width, b2_width); - if (std::max(min1_width, min2_width) > 2 * std::min(min1_width, min2_width)) return false; - if (std::max(max1_width, max2_width) > 2 * std::min(max1_width, max2_width)) return false; - if (std::max(y1_width, y2_width) > 2 * std::min(y1_width, y2_width)) return false; + if (max(min1_width, min2_width) > 2 * min(min1_width, min2_width)) return false; + if (max(max1_width, max2_width) > 2 * min(max1_width, max2_width)) return false; + if (max(y1_width, y2_width) > 2 * min(y1_width, y2_width)) return false; } return true; @@ -473,7 +475,7 @@ struct ShareWorker if (c1->type == "$macc") { if (!config.opt_aggressive) - if (share_macc(c1, c2) > 2 * std::min(bits_macc(c1), bits_macc(c2))) return false; + if (share_macc(c1, c2) > 2 * min(bits_macc(c1), bits_macc(c2))) return false; return true; } @@ -530,8 +532,8 @@ struct ShareWorker RTLIL::SigSpec a2 = c2->getPort("\\A"); RTLIL::SigSpec y2 = c2->getPort("\\Y"); - int a_width = std::max(a1.size(), a2.size()); - int y_width = std::max(y1.size(), y2.size()); + int a_width = max(a1.size(), a2.size()); + int y_width = max(y1.size(), y2.size()); a1.extend_u0(a_width, a_signed); a2.extend_u0(a_width, a_signed); @@ -561,11 +563,11 @@ struct ShareWorker if (config.generic_cbin_ops.count(c1->type)) { - int score_unflipped = std::max(c1->parameters.at("\\A_WIDTH").as_int(), c2->parameters.at("\\A_WIDTH").as_int()) + - std::max(c1->parameters.at("\\B_WIDTH").as_int(), c2->parameters.at("\\B_WIDTH").as_int()); + int score_unflipped = max(c1->parameters.at("\\A_WIDTH").as_int(), c2->parameters.at("\\A_WIDTH").as_int()) + + max(c1->parameters.at("\\B_WIDTH").as_int(), c2->parameters.at("\\B_WIDTH").as_int()); - int score_flipped = std::max(c1->parameters.at("\\A_WIDTH").as_int(), c2->parameters.at("\\B_WIDTH").as_int()) + - std::max(c1->parameters.at("\\B_WIDTH").as_int(), c2->parameters.at("\\A_WIDTH").as_int()); + int score_flipped = max(c1->parameters.at("\\A_WIDTH").as_int(), c2->parameters.at("\\B_WIDTH").as_int()) + + max(c1->parameters.at("\\B_WIDTH").as_int(), c2->parameters.at("\\A_WIDTH").as_int()); if (score_flipped < score_unflipped) { @@ -628,13 +630,13 @@ struct ShareWorker RTLIL::SigSpec b2 = c2->getPort("\\B"); RTLIL::SigSpec y2 = c2->getPort("\\Y"); - int a_width = std::max(a1.size(), a2.size()); - int b_width = std::max(b1.size(), b2.size()); - int y_width = std::max(y1.size(), y2.size()); + int a_width = max(a1.size(), a2.size()); + int b_width = max(b1.size(), b2.size()); + int y_width = max(y1.size(), y2.size()); if (c1->type == "$shr" && a_signed) { - a_width = std::max(y_width, a_width); + a_width = max(y_width, a_width); if (a1.size() < y1.size()) a1.extend_u0(y1.size(), true); if (a2.size() < y2.size()) a2.extend_u0(y2.size(), true); @@ -706,6 +708,10 @@ struct ShareWorker if (c1->type == "$memrd") { RTLIL::Cell *supercell = module->addCell(NEW_ID, c1); + RTLIL::SigSpec addr1 = c1->getPort("\\ADDR"); + RTLIL::SigSpec addr2 = c2->getPort("\\ADDR"); + if (addr1 != addr2) + supercell->setPort("\\ADDR", module->Mux(NEW_ID, addr2, addr1, act)); supercell_aux.insert(module->addPos(NEW_ID, supercell->getPort("\\DATA"), c2->getPort("\\DATA"))); supercell_aux.insert(supercell); return supercell; @@ -787,10 +793,59 @@ struct ShareWorker return true; } - void optimize_activation_patterns(pool<ssc_pair_t> & /* patterns */) + void optimize_activation_patterns(pool<ssc_pair_t> &patterns) { // TODO: Remove patterns that are contained in other patterns - // TODO: Consolidate pairs of patterns that only differ in the value for one signal bit + + dict<SigSpec, pool<Const>> db; + bool did_something = false; + + for (auto const &p : patterns) + { + auto &sig = p.first; + auto &val = p.second; + int len = GetSize(sig); + + for (int i = 0; i < len; i++) + { + auto otherval = val; + + if (otherval.bits[i] == State::S0) + otherval.bits[i] = State::S1; + else if (otherval.bits[i] == State::S1) + otherval.bits[i] = State::S0; + else + continue; + + if (db[sig].count(otherval)) + { + auto newsig = sig; + newsig.remove(i); + + auto newval = val; + newval.bits.erase(newval.bits.begin() + i); + + db[newsig].insert(newval); + db[sig].erase(otherval); + + did_something = true; + goto next_pattern; + } + } + + db[sig].insert(val); + next_pattern:; + } + + if (!did_something) + return; + + patterns.clear(); + for (auto &it : db) + for (auto &val : it.second) + patterns.insert(make_pair(it.first, val)); + + optimize_activation_patterns(patterns); } const pool<ssc_pair_t> &find_cell_activation_patterns(RTLIL::Cell *cell, const char *indent) @@ -1105,7 +1160,7 @@ struct ShareWorker RTLIL::Cell *cell = *shareable_cells.begin(); shareable_cells.erase(cell); - log(" Analyzing resource sharing options for %s:\n", log_id(cell)); + log(" Analyzing resource sharing options for %s (%s):\n", log_id(cell), log_id(cell->type)); const pool<ssc_pair_t> &cell_activation_patterns = find_cell_activation_patterns(cell, " "); RTLIL::SigSpec cell_activation_signals = bits_from_activation_patterns(cell_activation_patterns); @@ -1138,7 +1193,7 @@ struct ShareWorker for (auto other_cell : candidates) { - log(" Analyzing resource sharing with %s:\n", log_id(other_cell)); + log(" Analyzing resource sharing with %s (%s):\n", log_id(other_cell), log_id(other_cell->type)); const pool<ssc_pair_t> &other_cell_activation_patterns = find_cell_activation_patterns(other_cell, " "); RTLIL::SigSpec other_cell_activation_signals = bits_from_activation_patterns(other_cell_activation_patterns); @@ -1178,8 +1233,8 @@ struct ShareWorker optimize_activation_patterns(filtered_cell_activation_patterns); optimize_activation_patterns(filtered_other_cell_activation_patterns); - ezDefaultSAT ez; - SatGen satgen(&ez, &modwalker.sigmap); + ezSatPtr ez; + SatGen satgen(ez.get(), &modwalker.sigmap); pool<RTLIL::Cell*> sat_cells; std::set<RTLIL::SigBit> bits_queue; @@ -1189,13 +1244,13 @@ struct ShareWorker for (auto &p : filtered_cell_activation_patterns) { log(" Activation pattern for cell %s: %s = %s\n", log_id(cell), log_signal(p.first), log_signal(p.second)); - cell_active.push_back(ez.vec_eq(satgen.importSigSpec(p.first), satgen.importSigSpec(p.second))); + cell_active.push_back(ez->vec_eq(satgen.importSigSpec(p.first), satgen.importSigSpec(p.second))); all_ctrl_signals.append(p.first); } for (auto &p : filtered_other_cell_activation_patterns) { log(" Activation pattern for cell %s: %s = %s\n", log_id(other_cell), log_signal(p.first), log_signal(p.second)); - other_cell_active.push_back(ez.vec_eq(satgen.importSigSpec(p.first), satgen.importSigSpec(p.second))); + other_cell_active.push_back(ez->vec_eq(satgen.importSigSpec(p.first), satgen.importSigSpec(p.second))); all_ctrl_signals.append(p.first); } @@ -1230,36 +1285,36 @@ struct ShareWorker log(" Adding exclusive control bits: %s vs. %s\n", log_signal(it.first), log_signal(it.second)); int sub1 = satgen.importSigBit(it.first); int sub2 = satgen.importSigBit(it.second); - ez.assume(ez.NOT(ez.AND(sub1, sub2))); + ez->assume(ez->NOT(ez->AND(sub1, sub2))); } - if (!ez.solve(ez.expression(ez.OpOr, cell_active))) { + if (!ez->solve(ez->expression(ez->OpOr, cell_active))) { log(" According to the SAT solver the cell %s is never active. Sharing is pointless, we simply remove it.\n", log_id(cell)); cells_to_remove.insert(cell); break; } - if (!ez.solve(ez.expression(ez.OpOr, other_cell_active))) { + if (!ez->solve(ez->expression(ez->OpOr, other_cell_active))) { log(" According to the SAT solver the cell %s is never active. Sharing is pointless, we simply remove it.\n", log_id(other_cell)); cells_to_remove.insert(other_cell); shareable_cells.erase(other_cell); continue; } - ez.non_incremental(); + ez->non_incremental(); all_ctrl_signals.sort_and_unify(); std::vector<int> sat_model = satgen.importSigSpec(all_ctrl_signals); std::vector<bool> sat_model_values; - int sub1 = ez.expression(ez.OpOr, cell_active); - int sub2 = ez.expression(ez.OpOr, other_cell_active); - ez.assume(ez.AND(sub1, sub2)); + int sub1 = ez->expression(ez->OpOr, cell_active); + int sub2 = ez->expression(ez->OpOr, other_cell_active); + ez->assume(ez->AND(sub1, sub2)); log(" Size of SAT problem: %d cells, %d variables, %d clauses\n", - GetSize(sat_cells), ez.numCnfVariables(), ez.numCnfClauses()); + GetSize(sat_cells), ez->numCnfVariables(), ez->numCnfClauses()); - if (ez.solve(sat_model, sat_model_values)) { + if (ez->solve(sat_model, sat_model_values)) { log(" According to the SAT solver this pair of cells can not be shared.\n"); log(" Model from SAT solver: %s = %d'", log_signal(all_ctrl_signals), GetSize(sat_model_values)); for (int i = GetSize(sat_model_values)-1; i >= 0; i--) @@ -1391,7 +1446,7 @@ struct SharePass : public Pass { log("\n"); log(" -fast\n"); log(" Only consider the simple part of the control logic in SAT solving, resulting\n"); - log(" in much easier SAT problems at the cost of maybe missing some oportunities\n"); + log(" in much easier SAT problems at the cost of maybe missing some opportunities\n"); log(" for resource sharing.\n"); log("\n"); log(" -limit N\n"); @@ -1445,7 +1500,7 @@ struct SharePass : public Pass { config.generic_other_ops.insert("$alu"); config.generic_other_ops.insert("$macc"); - log_header("Executing SHARE pass (SAT-based resource sharing).\n"); + log_header(design, "Executing SHARE pass (SAT-based resource sharing).\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { diff --git a/passes/opt/wreduce.cc b/passes/opt/wreduce.cc index 1609a8be7..333541eab 100644 --- a/passes/opt/wreduce.cc +++ b/passes/opt/wreduce.cc @@ -2,11 +2,11 @@ * 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 @@ -37,7 +37,7 @@ struct WreduceConfig "$and", "$or", "$xor", "$xnor", "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx", "$lt", "$le", "$eq", "$ne", "$eqx", "$nex", "$ge", "$gt", - "$add", "$sub", // "$mul", "$div", "$mod", "$pow", + "$add", "$sub", "$mul", // "$div", "$mod", "$pow", "$mux", "$pmux" }); } @@ -51,6 +51,7 @@ struct WreduceWorker std::set<Cell*, IdString::compare_ptr_by_name<Cell>> work_queue_cells; std::set<SigBit> work_queue_bits; + pool<SigBit> keep_bits; WreduceWorker(WreduceConfig *config, Module *module) : config(config), module(module), mi(module) { } @@ -65,10 +66,13 @@ struct WreduceWorker SigSpec sig_y = mi.sigmap(cell->getPort("\\Y")); std::vector<SigBit> bits_removed; + if (sig_y.has_const()) + return; + for (int i = GetSize(sig_y)-1; i >= 0; i--) { auto info = mi.query(sig_y[i]); - if (!info->is_output && GetSize(info->ports) <= 1) { + if (!info->is_output && GetSize(info->ports) <= 1 && !keep_bits.count(mi.sigmap(sig_y[i]))) { bits_removed.push_back(Sx); continue; } @@ -172,6 +176,11 @@ struct WreduceWorker if (cell->type.in("$mux", "$pmux")) return run_cell_mux(cell); + SigSpec sig = mi.sigmap(cell->getPort("\\Y")); + + if (sig.has_const()) + return; + // Reduce size of ports A and B based on constant input bits and size of output port @@ -179,8 +188,8 @@ struct WreduceWorker int max_port_b_size = cell->hasPort("\\B") ? GetSize(cell->getPort("\\B")) : -1; if (cell->type.in("$not", "$pos", "$neg", "$and", "$or", "$xor", "$add", "$sub")) { - max_port_a_size = std::min(max_port_a_size, GetSize(cell->getPort("\\Y"))); - max_port_b_size = std::min(max_port_b_size, GetSize(cell->getPort("\\Y"))); + max_port_a_size = min(max_port_a_size, GetSize(sig)); + max_port_b_size = min(max_port_b_size, GetSize(sig)); } bool port_a_signed = false; @@ -192,10 +201,33 @@ struct WreduceWorker if (max_port_b_size >= 0) run_reduce_inport(cell, 'B', max_port_b_size, port_b_signed, did_something); + if (cell->hasPort("\\A") && cell->hasPort("\\B") && port_a_signed && port_b_signed) { + SigSpec sig_a = mi.sigmap(cell->getPort("\\A")), sig_b = mi.sigmap(cell->getPort("\\B")); + if (GetSize(sig_a) > 0 && sig_a[GetSize(sig_a)-1] == State::S0 && + GetSize(sig_b) > 0 && sig_b[GetSize(sig_b)-1] == State::S0) { + log("Converting cell %s.%s (%s) from signed to unsigned.\n", + log_id(module), log_id(cell), log_id(cell->type)); + cell->setParam("\\A_SIGNED", 0); + cell->setParam("\\B_SIGNED", 0); + port_a_signed = false; + port_b_signed = false; + did_something = true; + } + } - // Reduce size of port Y based on sizes for A and B and unused bits in Y + if (cell->hasPort("\\A") && !cell->hasPort("\\B") && port_a_signed) { + SigSpec sig_a = mi.sigmap(cell->getPort("\\A")); + if (GetSize(sig_a) > 0 && sig_a[GetSize(sig_a)-1] == State::S0) { + log("Converting cell %s.%s (%s) from signed to unsigned.\n", + log_id(module), log_id(cell), log_id(cell->type)); + cell->setParam("\\A_SIGNED", 0); + port_a_signed = false; + did_something = true; + } + } - SigSpec sig = mi.sigmap(cell->getPort("\\Y")); + + // Reduce size of port Y based on sizes for A and B and unused bits in Y int bits_removed = 0; if (port_a_signed && cell->type == "$shr") { @@ -221,7 +253,7 @@ struct WreduceWorker if (cell->hasPort("\\A")) a_size = GetSize(cell->getPort("\\A")); if (cell->hasPort("\\B")) b_size = GetSize(cell->getPort("\\B")); - int max_y_size = std::max(a_size, b_size); + int max_y_size = max(a_size, b_size); if (cell->type == "$add") max_y_size++; @@ -265,6 +297,11 @@ struct WreduceWorker void run() { + for (auto w : module->wires()) + if (w->get_bool_attribute("\\keep")) + for (auto bit : mi.sigmap(w)) + keep_bits.insert(bit); + for (auto c : module->selected_cells()) work_queue_cells.insert(c); @@ -281,6 +318,10 @@ struct WreduceWorker work_queue_cells.insert(port.cell); } + pool<SigSpec> complete_wires; + for (auto w : module->wires()) + complete_wires.insert(mi.sigmap(w)); + for (auto w : module->selected_wires()) { int unused_top_bits = 0; @@ -296,19 +337,22 @@ struct WreduceWorker unused_top_bits++; } - if (0 < unused_top_bits && unused_top_bits < GetSize(w)) { - log("Removed top %d bits (of %d) from wire %s.%s.\n", unused_top_bits, GetSize(w), log_id(module), log_id(w)); - Wire *nw = module->addWire(NEW_ID, w); - nw->width = GetSize(w) - unused_top_bits; - module->connect(nw, SigSpec(w).extract(0, GetSize(nw))); - module->swap_names(w, nw); - } + if (unused_top_bits == 0 || unused_top_bits == GetSize(w)) + continue; + + if (complete_wires[mi.sigmap(w).extract(0, GetSize(w) - unused_top_bits)]) + continue; + + log("Removed top %d bits (of %d) from wire %s.%s.\n", unused_top_bits, GetSize(w), log_id(module), log_id(w)); + Wire *nw = module->addWire(NEW_ID, GetSize(w) - unused_top_bits); + module->connect(nw, SigSpec(w).extract(0, GetSize(nw))); + module->swap_names(w, nw); } } }; struct WreducePass : public Pass { - WreducePass() : Pass("wreduce", "reduce the word size of operations is possible") { } + WreducePass() : Pass("wreduce", "reduce the word size of operations if possible") { } virtual void help() { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| @@ -327,7 +371,7 @@ struct WreducePass : public Pass { { WreduceConfig config; - log_header("Executing WREDUCE pass (reducing word size of cells).\n"); + log_header(design, "Executing WREDUCE pass (reducing word size of cells).\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { @@ -340,6 +384,19 @@ struct WreducePass : public Pass { if (module->has_processes_warn()) continue; + for (auto c : module->selected_cells()) + if (c->type.in("$reduce_and", "$reduce_or", "$reduce_xor", "$reduce_xnor", "$reduce_bool", + "$lt", "$le", "$eq", "$ne", "$eqx", "$nex", "$ge", "$gt", + "$logic_not", "$logic_and", "$logic_or") && GetSize(c->getPort("\\Y")) > 1) { + SigSpec sig = c->getPort("\\Y"); + if (!sig.has_const()) { + c->setPort("\\Y", sig[0]); + c->setParam("\\Y_WIDTH", 1); + sig.remove(0); + module->connect(sig, Const(0, GetSize(sig))); + } + } + WreduceWorker worker(&config, module); worker.run(); } |