diff options
Diffstat (limited to 'passes')
-rw-r--r-- | passes/cmds/write_file.cc | 2 | ||||
-rw-r--r-- | passes/memory/memory_dff.cc | 16 | ||||
-rw-r--r-- | passes/opt/Makefile.inc | 1 | ||||
-rw-r--r-- | passes/opt/muxpack.cc | 368 | ||||
-rw-r--r-- | passes/opt/opt_clean.cc | 6 | ||||
-rw-r--r-- | passes/sat/sat.cc | 4 | ||||
-rw-r--r-- | passes/techmap/muxcover.cc | 144 | ||||
-rw-r--r-- | passes/techmap/shregmap.cc | 20 |
8 files changed, 504 insertions, 57 deletions
diff --git a/passes/cmds/write_file.cc b/passes/cmds/write_file.cc index 9613b462b..64a762d7c 100644 --- a/passes/cmds/write_file.cc +++ b/passes/cmds/write_file.cc @@ -62,7 +62,7 @@ struct WriteFileFrontend : public Frontend { if (argidx < args.size() && args[argidx].rfind("-", 0) != 0) output_filename = args[argidx++]; else - log_cmd_error("Missing putput filename.\n"); + log_cmd_error("Missing output filename.\n"); extra_args(f, filename, args, argidx); diff --git a/passes/memory/memory_dff.cc b/passes/memory/memory_dff.cc index 220d29295..5215cce44 100644 --- a/passes/memory/memory_dff.cc +++ b/passes/memory/memory_dff.cc @@ -182,11 +182,17 @@ struct MemoryDffWorker if (mux_cells_a.count(sig_data) || mux_cells_b.count(sig_data)) { - bool enable_invert = mux_cells_a.count(sig_data) != 0; - Cell *mux = enable_invert ? mux_cells_a.at(sig_data) : mux_cells_b.at(sig_data); - SigSpec check_q = sigmap(mux->getPort(enable_invert ? "\\B" : "\\A")); + RTLIL::SigSpec en; + RTLIL::SigSpec check_q; + + do { + bool enable_invert = mux_cells_a.count(sig_data) != 0; + Cell *mux = enable_invert ? mux_cells_a.at(sig_data) : mux_cells_b.at(sig_data); + check_q = sigmap(mux->getPort(enable_invert ? "\\B" : "\\A")); + sig_data = sigmap(mux->getPort("\\Y")); + en.append(enable_invert ? module->LogicNot(NEW_ID, mux->getPort("\\S")) : mux->getPort("\\S")); + } while (mux_cells_a.count(sig_data) || mux_cells_b.count(sig_data)); - sig_data = sigmap(mux->getPort("\\Y")); for (auto bit : sig_data) if (sigbit_users_count[bit] > 1) goto skip_ff_after_read_merging; @@ -195,7 +201,7 @@ struct MemoryDffWorker { disconnect_dff(sig_data); cell->setPort("\\CLK", clk_data); - cell->setPort("\\EN", enable_invert ? module->LogicNot(NEW_ID, mux->getPort("\\S")) : mux->getPort("\\S")); + cell->setPort("\\EN", en.size() > 1 ? module->ReduceAnd(NEW_ID, en) : en); cell->setPort("\\DATA", sig_data); cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(1); cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity); diff --git a/passes/opt/Makefile.inc b/passes/opt/Makefile.inc index 337fee9e4..ea3646330 100644 --- a/passes/opt/Makefile.inc +++ b/passes/opt/Makefile.inc @@ -14,5 +14,6 @@ OBJS += passes/opt/opt_demorgan.o OBJS += passes/opt/rmports.o OBJS += passes/opt/opt_lut.o OBJS += passes/opt/pmux2shiftx.o +OBJS += passes/opt/muxpack.o endif diff --git a/passes/opt/muxpack.cc b/passes/opt/muxpack.cc new file mode 100644 index 000000000..6697d6ca1 --- /dev/null +++ b/passes/opt/muxpack.cc @@ -0,0 +1,368 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * 2019 Eddie Hung <eddie@fpgeh.com> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/yosys.h" +#include "kernel/sigtools.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct ExclusiveDatabase +{ + Module *module; + const SigMap &sigmap; + + dict<SigBit, std::pair<SigSpec,std::vector<Const>>> sig_cmp_prev; + + ExclusiveDatabase(Module *module, const SigMap &sigmap) : module(module), sigmap(sigmap) + { + SigSpec const_sig, nonconst_sig; + SigBit y_port; + pool<Cell*> reduce_or; + for (auto cell : module->cells()) { + if (cell->type == "$eq") { + nonconst_sig = sigmap(cell->getPort("\\A")); + const_sig = sigmap(cell->getPort("\\B")); + if (!const_sig.is_fully_const()) { + if (!nonconst_sig.is_fully_const()) + continue; + std::swap(nonconst_sig, const_sig); + } + y_port = sigmap(cell->getPort("\\Y")); + } + else if (cell->type == "$logic_not") { + nonconst_sig = sigmap(cell->getPort("\\A")); + const_sig = Const(RTLIL::S0, GetSize(nonconst_sig)); + y_port = sigmap(cell->getPort("\\Y")); + } + else if (cell->type == "$reduce_or") { + reduce_or.insert(cell); + continue; + } + else continue; + + log_assert(!nonconst_sig.empty()); + log_assert(!const_sig.empty()); + sig_cmp_prev[y_port] = std::make_pair(nonconst_sig,std::vector<Const>{const_sig.as_const()}); + } + + for (auto cell : reduce_or) { + nonconst_sig = SigSpec(); + std::vector<Const> values; + SigSpec a_port = sigmap(cell->getPort("\\A")); + for (auto bit : a_port) { + auto it = sig_cmp_prev.find(bit); + if (it == sig_cmp_prev.end()) { + nonconst_sig = SigSpec(); + break; + } + if (nonconst_sig.empty()) + nonconst_sig = it->second.first; + else if (nonconst_sig != it->second.first) { + nonconst_sig = SigSpec(); + break; + } + for (auto value : it->second.second) + values.push_back(value); + } + if (nonconst_sig.empty()) + continue; + y_port = sigmap(cell->getPort("\\Y")); + sig_cmp_prev[y_port] = std::make_pair(nonconst_sig,std::move(values)); + } + } + + bool query(const SigSpec &sig) const + { + SigSpec nonconst_sig; + pool<Const> const_values; + + for (auto bit : sig.bits()) { + auto it = sig_cmp_prev.find(bit); + if (it == sig_cmp_prev.end()) + return false; + + if (nonconst_sig.empty()) + nonconst_sig = it->second.first; + else if (nonconst_sig != it->second.first) + return false; + + for (auto value : it->second.second) + if (!const_values.insert(value).second) + return false; + } + + return true; + } +}; + + +struct MuxpackWorker +{ + Module *module; + SigMap sigmap; + + int mux_count, pmux_count; + + pool<Cell*> remove_cells; + + dict<SigSpec, Cell*> sig_chain_next; + dict<SigSpec, Cell*> sig_chain_prev; + pool<SigBit> sigbit_with_non_chain_users; + pool<Cell*> chain_start_cells; + pool<Cell*> candidate_cells; + + ExclusiveDatabase excl_db; + + void make_sig_chain_next_prev() + { + for (auto wire : module->wires()) + { + if (wire->port_output || wire->get_bool_attribute("\\keep")) { + for (auto bit : sigmap(wire)) + sigbit_with_non_chain_users.insert(bit); + } + } + + for (auto cell : module->cells()) + { + if (cell->type.in("$mux", "$pmux") && !cell->get_bool_attribute("\\keep")) + { + SigSpec a_sig = sigmap(cell->getPort("\\A")); + SigSpec b_sig; + if (cell->type == "$mux") + b_sig = sigmap(cell->getPort("\\B")); + SigSpec y_sig = sigmap(cell->getPort("\\Y")); + + if (sig_chain_next.count(a_sig)) + for (auto a_bit : a_sig.bits()) + sigbit_with_non_chain_users.insert(a_bit); + else { + sig_chain_next[a_sig] = cell; + candidate_cells.insert(cell); + } + + if (!b_sig.empty()) { + if (sig_chain_next.count(b_sig)) + for (auto b_bit : b_sig.bits()) + sigbit_with_non_chain_users.insert(b_bit); + else { + sig_chain_next[b_sig] = cell; + candidate_cells.insert(cell); + } + } + + sig_chain_prev[y_sig] = cell; + continue; + } + + for (auto conn : cell->connections()) + if (cell->input(conn.first)) + for (auto bit : sigmap(conn.second)) + sigbit_with_non_chain_users.insert(bit); + } + } + + void find_chain_start_cells() + { + for (auto cell : candidate_cells) + { + log_debug("Considering %s (%s)\n", log_id(cell), log_id(cell->type)); + + SigSpec a_sig = sigmap(cell->getPort("\\A")); + if (cell->type == "$mux") { + SigSpec b_sig = sigmap(cell->getPort("\\B")); + if (sig_chain_prev.count(a_sig) + sig_chain_prev.count(b_sig) != 1) + goto start_cell; + + if (!sig_chain_prev.count(a_sig)) + a_sig = b_sig; + } + else if (cell->type == "$pmux") { + if (!sig_chain_prev.count(a_sig)) + goto start_cell; + } + else log_abort(); + + for (auto bit : a_sig.bits()) + if (sigbit_with_non_chain_users.count(bit)) + goto start_cell; + + { + Cell *prev_cell = sig_chain_prev.at(a_sig); + log_assert(prev_cell); + SigSpec s_sig = sigmap(cell->getPort("\\S")); + s_sig.append(sigmap(prev_cell->getPort("\\S"))); + if (!excl_db.query(s_sig)) + goto start_cell; + } + + continue; + + start_cell: + chain_start_cells.insert(cell); + } + } + + vector<Cell*> create_chain(Cell *start_cell) + { + vector<Cell*> chain; + + Cell *c = start_cell; + while (c != nullptr) + { + chain.push_back(c); + + SigSpec y_sig = sigmap(c->getPort("\\Y")); + + if (sig_chain_next.count(y_sig) == 0) + break; + + c = sig_chain_next.at(y_sig); + if (chain_start_cells.count(c) != 0) + break; + } + + return chain; + } + + void process_chain(vector<Cell*> &chain) + { + if (GetSize(chain) < 2) + return; + + int cursor = 0; + while (cursor < GetSize(chain)) + { + int cases = GetSize(chain) - cursor; + + Cell *first_cell = chain[cursor]; + dict<int, SigBit> taps_dict; + + if (cases < 2) { + cursor++; + continue; + } + + Cell *last_cell = chain[cursor+cases-1]; + + log("Converting %s.%s ... %s.%s to a pmux with %d cases.\n", + log_id(module), log_id(first_cell), log_id(module), log_id(last_cell), cases); + + mux_count += cases; + pmux_count += 1; + + first_cell->type = "$pmux"; + SigSpec b_sig = first_cell->getPort("\\B"); + SigSpec s_sig = first_cell->getPort("\\S"); + + for (int i = 1; i < cases; i++) { + Cell* prev_cell = chain[cursor+i-1]; + Cell* cursor_cell = chain[cursor+i]; + if (sigmap(prev_cell->getPort("\\Y")) == sigmap(cursor_cell->getPort("\\A"))) { + b_sig.append(cursor_cell->getPort("\\B")); + s_sig.append(cursor_cell->getPort("\\S")); + } + else { + log_assert(cursor_cell->type == "$mux"); + b_sig.append(cursor_cell->getPort("\\A")); + s_sig.append(module->LogicNot(NEW_ID, cursor_cell->getPort("\\S"))); + } + remove_cells.insert(cursor_cell); + } + + first_cell->setPort("\\B", b_sig); + first_cell->setPort("\\S", s_sig); + first_cell->setParam("\\S_WIDTH", GetSize(s_sig)); + first_cell->setPort("\\Y", last_cell->getPort("\\Y")); + + cursor += cases; + } + } + + void cleanup() + { + for (auto cell : remove_cells) + module->remove(cell); + + remove_cells.clear(); + sig_chain_next.clear(); + sig_chain_prev.clear(); + chain_start_cells.clear(); + candidate_cells.clear(); + } + + MuxpackWorker(Module *module) : + module(module), sigmap(module), mux_count(0), pmux_count(0), excl_db(module, sigmap) + { + make_sig_chain_next_prev(); + find_chain_start_cells(); + + for (auto c : chain_start_cells) { + vector<Cell*> chain = create_chain(c); + process_chain(chain); + } + + cleanup(); + } +}; + +struct MuxpackPass : public Pass { + MuxpackPass() : Pass("muxpack", "$mux/$pmux cascades to $pmux") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" muxpack [selection]\n"); + log("\n"); + log("This pass converts cascaded chains of $pmux cells (e.g. those create from case\n"); + log("constructs) and $mux cells (e.g. those created by if-else constructs) into\n"); + log("$pmux cells.\n"); + log("\n"); + log("This optimisation is conservative --- it will only pack $mux or $pmux cells\n"); + log("whose select lines are driven by '$eq' cells with other such cells if it can be\n"); + log("certain that their select inputs are mutually exclusive.\n"); + log("\n"); + } + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + { + log_header(design, "Executing MUXPACK pass ($mux cell cascades to $pmux).\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + break; + } + extra_args(args, argidx, design); + + int mux_count = 0; + int pmux_count = 0; + + for (auto module : design->selected_modules()) { + MuxpackWorker worker(module); + mux_count += worker.mux_count; + pmux_count += worker.pmux_count; + } + + log("Converted %d (p)mux cells into %d pmux cells.\n", mux_count, pmux_count); + } +} MuxpackPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/opt/opt_clean.cc b/passes/opt/opt_clean.cc index cfb0f788a..a8a8e0bc7 100644 --- a/passes/opt/opt_clean.cc +++ b/passes/opt/opt_clean.cc @@ -326,8 +326,8 @@ bool rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos if (wire->port_id != 0 || wire->get_bool_attribute("\\keep") || !initval.is_fully_undef()) { // do not delete anything with "keep" or module ports or initialized wires } else - if (!purge_mode && check_public_name(wire->name)) { - // do not get rid of public names unless in purge mode + if (!purge_mode && check_public_name(wire->name) && (raw_used_signals.check_any(s1) || used_signals.check_any(s2) || s1 != s2)) { + // do not get rid of public names unless in purge mode or if the wire is entirely unused, not even aliased } else if (!raw_used_signals.check_any(s1)) { // delete wires that aren't used by anything directly @@ -480,7 +480,7 @@ void rmunused_module(RTLIL::Module *module, bool purge_mode, bool verbose, bool std::vector<RTLIL::Cell*> delcells; for (auto cell : module->cells()) - if (cell->type.in("$pos", "$_BUF_")) { + if (cell->type.in("$pos", "$_BUF_") && !cell->has_keep_attr()) { bool is_signed = cell->type == "$pos" && cell->getParam("\\A_SIGNED").as_bool(); RTLIL::SigSpec a = cell->getPort("\\A"); RTLIL::SigSpec y = cell->getPort("\\Y"); diff --git a/passes/sat/sat.cc b/passes/sat/sat.cc index cbba738f0..e4654d835 100644 --- a/passes/sat/sat.cc +++ b/passes/sat/sat.cc @@ -659,6 +659,7 @@ struct SatHelper void dump_model_to_vcd(std::string vcd_file_name) { + rewrite_filename(vcd_file_name); FILE *f = fopen(vcd_file_name.c_str(), "w"); if (!f) log_cmd_error("Can't open output file `%s' for writing: %s\n", vcd_file_name.c_str(), strerror(errno)); @@ -761,6 +762,7 @@ struct SatHelper void dump_model_to_json(std::string json_file_name) { + rewrite_filename(json_file_name); FILE *f = fopen(json_file_name.c_str(), "w"); if (!f) log_cmd_error("Can't open output file `%s' for writing: %s\n", json_file_name.c_str(), strerror(errno)); @@ -1505,6 +1507,7 @@ struct SatPass : public Pass { { if (!cnf_file_name.empty()) { + rewrite_filename(cnf_file_name); FILE *f = fopen(cnf_file_name.c_str(), "w"); if (!f) log_cmd_error("Can't open output file `%s' for writing: %s\n", cnf_file_name.c_str(), strerror(errno)); @@ -1608,6 +1611,7 @@ struct SatPass : public Pass { if (!cnf_file_name.empty()) { + rewrite_filename(cnf_file_name); FILE *f = fopen(cnf_file_name.c_str(), "w"); if (!f) log_cmd_error("Can't open output file `%s' for writing: %s\n", cnf_file_name.c_str(), strerror(errno)); diff --git a/passes/techmap/muxcover.cc b/passes/techmap/muxcover.cc index 8e44be148..b0722134e 100644 --- a/passes/techmap/muxcover.cc +++ b/passes/techmap/muxcover.cc @@ -23,6 +23,7 @@ USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN +#define COST_DMUX 90 #define COST_MUX2 100 #define COST_MUX4 220 #define COST_MUX8 460 @@ -57,7 +58,9 @@ struct MuxcoverWorker bool use_mux8; bool use_mux16; bool nodecode; + bool nopartial; + int cost_dmux; int cost_mux2; int cost_mux4; int cost_mux8; @@ -69,6 +72,8 @@ struct MuxcoverWorker use_mux8 = false; use_mux16 = false; nodecode = false; + nopartial = false; + cost_dmux = COST_DMUX; cost_mux2 = COST_MUX2; cost_mux4 = COST_MUX4; cost_mux8 = COST_MUX8; @@ -133,13 +138,20 @@ struct MuxcoverWorker log(" Finished treeification: Found %d trees.\n", GetSize(tree_list)); } - bool follow_muxtree(SigBit &ret_bit, tree_t &tree, SigBit bit, const char *path) + bool follow_muxtree(SigBit &ret_bit, tree_t &tree, SigBit bit, const char *path, bool first_layer = true) { if (*path) { - if (tree.muxes.count(bit) == 0) - return false; + if (tree.muxes.count(bit) == 0) { + if (first_layer || nopartial) + return false; + if (path[0] == 'S') + ret_bit = State::Sx; + else + ret_bit = bit; + return true; + } char port_name[3] = {'\\', *path, 0}; - return follow_muxtree(ret_bit, tree, sigmap(tree.muxes.at(bit)->getPort(port_name)), path+1); + return follow_muxtree(ret_bit, tree, sigmap(tree.muxes.at(bit)->getPort(port_name)), path+1, false); } else { ret_bit = bit; return true; @@ -148,7 +160,7 @@ struct MuxcoverWorker int prepare_decode_mux(SigBit &A, SigBit B, SigBit sel, SigBit bit) { - if (A == B) + if (A == B || sel == State::Sx) return 0; tuple<SigBit, SigBit, SigBit> key(A, B, sel); @@ -166,7 +178,10 @@ struct MuxcoverWorker if (std::get<2>(entry)) return 0; - return cost_mux2 / GetSize(std::get<1>(entry)); + if (A == State::Sx || B == State::Sx) + return 0; + + return cost_dmux / GetSize(std::get<1>(entry)); } void implement_decode_mux(SigBit ctrl_bit) @@ -183,9 +198,32 @@ struct MuxcoverWorker implement_decode_mux(std::get<0>(key)); implement_decode_mux(std::get<1>(key)); - module->addMuxGate(NEW_ID, std::get<0>(key), std::get<1>(key), std::get<2>(key), ctrl_bit); + if (std::get<0>(key) == State::Sx) { + module->addBufGate(NEW_ID, std::get<1>(key), ctrl_bit); + } else if (std::get<1>(key) == State::Sx) { + module->addBufGate(NEW_ID, std::get<0>(key), ctrl_bit); + } else { + module->addMuxGate(NEW_ID, std::get<0>(key), std::get<1>(key), std::get<2>(key), ctrl_bit); + decode_mux_counter++; + } std::get<2>(entry) = true; - decode_mux_counter++; + } + + void find_best_covers(tree_t &tree, const vector<SigBit> &bits) + { + for (auto bit : bits) + find_best_cover(tree, bit); + } + + int sum_best_covers(tree_t &tree, const vector<SigBit> &bits) + { + int sum = 0; + for (auto bit : pool<SigBit>(bits.begin(), bits.end())) { + int cost = tree.newmuxes.at(bit).cost; + log_debug(" Best cost for %s: %d\n", log_signal(bit), cost); + sum += cost; + } + return sum; } int find_best_cover(tree_t &tree, SigBit bit) @@ -218,9 +256,13 @@ struct MuxcoverWorker mux.inputs.push_back(B); mux.selects.push_back(S1); + find_best_covers(tree, mux.inputs); + log_debug(" Decode cost for mux2 at %s: %d\n", log_signal(bit), mux.cost); + mux.cost += cost_mux2; - mux.cost += find_best_cover(tree, A); - mux.cost += find_best_cover(tree, B); + mux.cost += sum_best_covers(tree, mux.inputs); + + log_debug(" Cost of mux2 at %s: %d\n", log_signal(bit), mux.cost); best_mux = mux; } @@ -256,13 +298,15 @@ struct MuxcoverWorker mux.selects.push_back(S1); mux.selects.push_back(T1); + find_best_covers(tree, mux.inputs); + log_debug(" Decode cost for mux4 at %s: %d\n", log_signal(bit), mux.cost); + mux.cost += cost_mux4; - mux.cost += find_best_cover(tree, A); - mux.cost += find_best_cover(tree, B); - mux.cost += find_best_cover(tree, C); - mux.cost += find_best_cover(tree, D); + mux.cost += sum_best_covers(tree, mux.inputs); - if (best_mux.cost > mux.cost) + log_debug(" Cost of mux4 at %s: %d\n", log_signal(bit), mux.cost); + + if (best_mux.cost >= mux.cost) best_mux = mux; } } @@ -319,17 +363,15 @@ struct MuxcoverWorker mux.selects.push_back(T1); mux.selects.push_back(U1); + find_best_covers(tree, mux.inputs); + log_debug(" Decode cost for mux8 at %s: %d\n", log_signal(bit), mux.cost); + mux.cost += cost_mux8; - mux.cost += find_best_cover(tree, A); - mux.cost += find_best_cover(tree, B); - mux.cost += find_best_cover(tree, C); - mux.cost += find_best_cover(tree, D); - mux.cost += find_best_cover(tree, E); - mux.cost += find_best_cover(tree, F); - mux.cost += find_best_cover(tree, G); - mux.cost += find_best_cover(tree, H); - - if (best_mux.cost > mux.cost) + mux.cost += sum_best_covers(tree, mux.inputs); + + log_debug(" Cost of mux8 at %s: %d\n", log_signal(bit), mux.cost); + + if (best_mux.cost >= mux.cost) best_mux = mux; } } @@ -423,25 +465,15 @@ struct MuxcoverWorker mux.selects.push_back(U1); mux.selects.push_back(V1); + find_best_covers(tree, mux.inputs); + log_debug(" Decode cost for mux16 at %s: %d\n", log_signal(bit), mux.cost); + mux.cost += cost_mux16; - mux.cost += find_best_cover(tree, A); - mux.cost += find_best_cover(tree, B); - mux.cost += find_best_cover(tree, C); - mux.cost += find_best_cover(tree, D); - mux.cost += find_best_cover(tree, E); - mux.cost += find_best_cover(tree, F); - mux.cost += find_best_cover(tree, G); - mux.cost += find_best_cover(tree, H); - mux.cost += find_best_cover(tree, I); - mux.cost += find_best_cover(tree, J); - mux.cost += find_best_cover(tree, K); - mux.cost += find_best_cover(tree, L); - mux.cost += find_best_cover(tree, M); - mux.cost += find_best_cover(tree, N); - mux.cost += find_best_cover(tree, O); - mux.cost += find_best_cover(tree, P); - - if (best_mux.cost > mux.cost) + mux.cost += sum_best_covers(tree, mux.inputs); + + log_debug(" Cost of mux16 at %s: %d\n", log_signal(bit), mux.cost); + + if (best_mux.cost >= mux.cost) best_mux = mux; } } @@ -537,6 +569,7 @@ struct MuxcoverWorker void treecover(tree_t &tree) { int count_muxes_by_type[4] = {0, 0, 0, 0}; + log_debug(" Searching for best cover for tree at %s.\n", log_signal(tree.root)); find_best_cover(tree, tree.root); implement_best_cover(tree, tree.root, count_muxes_by_type); log(" Replaced tree at %s: %d MUX2, %d MUX4, %d MUX8, %d MUX16\n", log_signal(tree.root), @@ -553,12 +586,13 @@ struct MuxcoverWorker log(" Covering trees:\n"); - // pre-fill cache of decoder muxes - if (!nodecode) + if (!nodecode) { + log_debug(" Populating cache of decoder muxes.\n"); for (auto &tree : tree_list) { find_best_cover(tree, tree.root); tree.newmuxes.clear(); } + } for (auto &tree : tree_list) treecover(tree); @@ -584,11 +618,19 @@ struct MuxcoverPass : public Pass { log(" Default costs: $_MUX_ = %d, $_MUX4_ = %d,\n", COST_MUX2, COST_MUX4); log(" $_MUX8_ = %d, $_MUX16_ = %d\n", COST_MUX8, COST_MUX16); log("\n"); + log(" -dmux=cost\n"); + log(" Use the specified cost for $_MUX_ cells used in decoders.\n"); + log(" Default cost: %d\n", COST_DMUX); + log("\n"); log(" -nodecode\n"); log(" Do not insert decoder logic. This reduces the number of possible\n"); log(" substitutions, but guarantees that the resulting circuit is not\n"); log(" less efficient than the original circuit.\n"); log("\n"); + log(" -nopartial\n"); + log(" Do not consider mappings that use $_MUX<N>_ to select from less\n"); + log(" than <N> different signals.\n"); + log("\n"); } void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { @@ -598,6 +640,8 @@ struct MuxcoverPass : public Pass { bool use_mux8 = false; bool use_mux16 = false; bool nodecode = false; + bool nopartial = false; + int cost_dmux = COST_DMUX; int cost_mux4 = COST_MUX4; int cost_mux8 = COST_MUX8; int cost_mux16 = COST_MUX16; @@ -630,10 +674,18 @@ struct MuxcoverPass : public Pass { } continue; } + if (arg.size() >= 6 && arg.substr(0,6) == "-dmux=") { + cost_dmux = atoi(arg.substr(6).c_str()); + continue; + } if (arg == "-nodecode") { nodecode = true; continue; } + if (arg == "-nopartial") { + nopartial = true; + continue; + } break; } extra_args(args, argidx, design); @@ -650,10 +702,12 @@ struct MuxcoverPass : public Pass { worker.use_mux4 = use_mux4; worker.use_mux8 = use_mux8; worker.use_mux16 = use_mux16; + worker.cost_dmux = cost_dmux; worker.cost_mux4 = cost_mux4; worker.cost_mux8 = cost_mux8; worker.cost_mux16 = cost_mux16; worker.nodecode = nodecode; + worker.nopartial = nopartial; worker.run(); } } diff --git a/passes/techmap/shregmap.cc b/passes/techmap/shregmap.cc index 21dfe9619..004ab1eb9 100644 --- a/passes/techmap/shregmap.cc +++ b/passes/techmap/shregmap.cc @@ -293,10 +293,22 @@ struct ShregmapWorker if (opts.init || sigbit_init.count(q_bit) == 0) { - if (sigbit_chain_next.count(d_bit)) { + auto r = sigbit_chain_next.insert(std::make_pair(d_bit, cell)); + if (!r.second) { + // Insertion not successful means that d_bit is already + // connected to another register, thus mark it as a + // non chain user ... sigbit_with_non_chain_users.insert(d_bit); - } else - sigbit_chain_next[d_bit] = cell; + // ... and clone d_bit into another wire, and use that + // wire as a different key in the d_bit-to-cell dictionary + // so that it can be identified as another chain + // (omitting this common flop) + // Link: https://github.com/YosysHQ/yosys/pull/1085 + Wire *wire = module->addWire(NEW_ID); + module->connect(wire, d_bit); + sigmap.add(wire, d_bit); + sigbit_chain_next.insert(std::make_pair(wire, cell)); + } sigbit_chain_prev[q_bit] = cell; continue; @@ -605,9 +617,11 @@ struct ShregmapPass : public Pass { log("\n"); log(" -tech greenpak4\n"); log(" map to greenpak4 shift registers.\n"); + log(" this option also implies -clkpol pos -zinit\n"); log("\n"); log(" -tech xilinx\n"); log(" map to xilinx dynamic-length shift registers.\n"); + log(" this option also implies -params -init\n"); log("\n"); } void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE |