From 3af871f969f7f5bd5201bac17544559671312a6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcelina=20Ko=C5=9Bcielnicka?= Date: Tue, 9 Mar 2021 21:32:16 +0100 Subject: opt_clean: Remove init attribute bits together with removed DFFs. Fixes #2546. --- passes/opt/opt_clean.cc | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'passes/opt') diff --git a/passes/opt/opt_clean.cc b/passes/opt/opt_clean.cc index 883374cf6..c66f45308 100644 --- a/passes/opt/opt_clean.cc +++ b/passes/opt/opt_clean.cc @@ -21,6 +21,7 @@ #include "kernel/sigtools.h" #include "kernel/log.h" #include "kernel/celltypes.h" +#include "kernel/ffinit.h" #include #include #include @@ -101,6 +102,7 @@ void rmunused_module_cells(Module *module, bool verbose) pool used_raw_bits; dict> wire2driver; dict> driver_driver_logs; + FfInitVals ffinit(&sigmap, module); SigMap raw_sigmap; for (auto &it : module->connections_) { @@ -193,6 +195,8 @@ void rmunused_module_cells(Module *module, bool verbose) if (verbose) log_debug(" removing unused `%s' cell `%s'.\n", cell->type.c_str(), cell->name.c_str()); module->design->scratchpad_set_bool("opt.did_something", true); + if (RTLIL::builtin_ff_cell_types().count(cell->type)) + ffinit.remove_init(cell->getPort(ID::Q)); module->remove(cell); count_rm_cells++; } -- cgit v1.2.3 From 5c1e6a0e201f1bb623c3fc7d2c8ee13c783c9c07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcelina=20Ko=C5=9Bcielnicka?= Date: Tue, 4 May 2021 19:14:24 +0200 Subject: opt_dff: Fix NOT gates wired in reverse. --- passes/opt/opt_dff.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'passes/opt') diff --git a/passes/opt/opt_dff.cc b/passes/opt/opt_dff.cc index a47071a30..c87ac3163 100644 --- a/passes/opt/opt_dff.cc +++ b/passes/opt/opt_dff.cc @@ -318,9 +318,9 @@ struct OptDffWorker if (!ff.pol_clr) { module->connect(ff.sig_q[i], ff.sig_clr[i]); } else if (ff.is_fine) { - module->addNotGate(NEW_ID, ff.sig_q[i], ff.sig_clr[i]); + module->addNotGate(NEW_ID, ff.sig_clr[i], ff.sig_q[i]); } else { - module->addNot(NEW_ID, ff.sig_q[i], ff.sig_clr[i]); + module->addNot(NEW_ID, ff.sig_clr[i], ff.sig_q[i]); } log("Handling always-active SET at position %d on %s (%s) from module %s (changing to combinatorial circuit).\n", i, log_id(cell), log_id(cell->type), log_id(module)); -- cgit v1.2.3 From a23d9409e7d04fcfa31a139d0cf6169be4c46fca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcelina=20Ko=C5=9Bcielnicka?= Date: Sat, 22 May 2021 20:27:51 +0200 Subject: opt_mem: Remove write ports with const-0 EN. Fixes #2765. --- passes/opt/opt_mem.cc | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'passes/opt') diff --git a/passes/opt/opt_mem.cc b/passes/opt/opt_mem.cc index 49a0ac51a..0409fb736 100644 --- a/passes/opt/opt_mem.cc +++ b/passes/opt/opt_mem.cc @@ -52,6 +52,18 @@ struct OptMemPass : public Pass { int total_count = 0; for (auto module : design->selected_modules()) { for (auto &mem : Mem::get_selected_memories(module)) { + bool changed = false; + for (auto &port : mem.wr_ports) { + if (port.en.is_fully_zero()) { + port.removed = true; + changed = true; + total_count++; + } + } + if (changed) { + mem.emit(); + } + if (mem.wr_ports.empty() && mem.inits.empty()) { mem.remove(); total_count++; -- cgit v1.2.3 From d905990d0194decb9f673ccf10aa488820816b87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcelina=20Ko=C5=9Bcielnicka?= Date: Sun, 23 May 2021 15:42:51 +0200 Subject: memory_share: Split off feedback path finding as a separate pass. memory_share is actually three passes in a trenchcoat. Split off the one that has the least in common with the other two as a separate pass. --- passes/opt/Makefile.inc | 1 + passes/opt/opt_mem_feedback.cc | 333 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 334 insertions(+) create mode 100644 passes/opt/opt_mem_feedback.cc (limited to 'passes/opt') diff --git a/passes/opt/Makefile.inc b/passes/opt/Makefile.inc index 4ae9b8895..b0192235b 100644 --- a/passes/opt/Makefile.inc +++ b/passes/opt/Makefile.inc @@ -2,6 +2,7 @@ OBJS += passes/opt/opt.o OBJS += passes/opt/opt_merge.o OBJS += passes/opt/opt_mem.o +OBJS += passes/opt/opt_mem_feedback.o OBJS += passes/opt/opt_muxtree.o OBJS += passes/opt/opt_reduce.o OBJS += passes/opt/opt_dff.o diff --git a/passes/opt/opt_mem_feedback.cc b/passes/opt/opt_mem_feedback.cc new file mode 100644 index 000000000..75e244514 --- /dev/null +++ b/passes/opt/opt_mem_feedback.cc @@ -0,0 +1,333 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * 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/satgen.h" +#include "kernel/sigtools.h" +#include "kernel/modtools.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +bool memrd_cmp(RTLIL::Cell *a, RTLIL::Cell *b) +{ + return a->name < b->name; +} + +bool memwr_cmp(RTLIL::Cell *a, RTLIL::Cell *b) +{ + return a->parameters.at(ID::PRIORITY).as_int() < b->parameters.at(ID::PRIORITY).as_int(); +} + +struct OptMemFeedbackWorker +{ + RTLIL::Design *design; + RTLIL::Module *module; + SigMap sigmap, sigmap_xmux; + + std::map> sig_to_mux; + std::map>, SigBit>, SigBit> conditions_logic_cache; + + + // ----------------------------------------------------------------- + // Converting feedbacks to async read ports to proper enable signals + // ----------------------------------------------------------------- + + bool find_data_feedback(const std::set &async_rd_bits, RTLIL::SigBit sig, + std::map &state, std::set> &conditions) + { + if (async_rd_bits.count(sig)) { + conditions.insert(state); + return true; + } + + if (sig_to_mux.count(sig) == 0) + return false; + + RTLIL::Cell *cell = sig_to_mux.at(sig).first; + int bit_idx = sig_to_mux.at(sig).second; + + std::vector sig_a = sigmap(cell->getPort(ID::A)); + std::vector sig_b = sigmap(cell->getPort(ID::B)); + std::vector sig_s = sigmap(cell->getPort(ID::S)); + std::vector sig_y = sigmap(cell->getPort(ID::Y)); + log_assert(sig_y.at(bit_idx) == sig); + + for (int i = 0; i < int(sig_s.size()); i++) + if (state.count(sig_s[i]) && state.at(sig_s[i]) == true) { + if (find_data_feedback(async_rd_bits, sig_b.at(bit_idx + i*sig_y.size()), state, conditions)) { + RTLIL::SigSpec new_b = cell->getPort(ID::B); + new_b.replace(bit_idx + i*sig_y.size(), RTLIL::State::Sx); + cell->setPort(ID::B, new_b); + } + return false; + } + + + for (int i = 0; i < int(sig_s.size()); i++) + { + if (state.count(sig_s[i]) && state.at(sig_s[i]) == false) + continue; + + std::map new_state = state; + new_state[sig_s[i]] = true; + + if (find_data_feedback(async_rd_bits, sig_b.at(bit_idx + i*sig_y.size()), new_state, conditions)) { + RTLIL::SigSpec new_b = cell->getPort(ID::B); + new_b.replace(bit_idx + i*sig_y.size(), RTLIL::State::Sx); + cell->setPort(ID::B, new_b); + } + } + + std::map new_state = state; + for (int i = 0; i < int(sig_s.size()); i++) + new_state[sig_s[i]] = false; + + if (find_data_feedback(async_rd_bits, sig_a.at(bit_idx), new_state, conditions)) { + RTLIL::SigSpec new_a = cell->getPort(ID::A); + new_a.replace(bit_idx, RTLIL::State::Sx); + cell->setPort(ID::A, new_a); + } + + return false; + } + + RTLIL::SigBit conditions_to_logic(std::set> &conditions, SigBit olden, int &created_conditions) + { + auto key = make_pair(conditions, olden); + + if (conditions_logic_cache.count(key)) + return conditions_logic_cache.at(key); + + RTLIL::SigSpec terms; + for (auto &cond : conditions) { + RTLIL::SigSpec sig1, sig2; + for (auto &it : cond) { + sig1.append(it.first); + sig2.append(it.second ? RTLIL::State::S1 : RTLIL::State::S0); + } + terms.append(module->Ne(NEW_ID, sig1, sig2)); + created_conditions++; + } + + if (olden.wire != nullptr || olden != State::S1) + terms.append(olden); + + if (GetSize(terms) == 0) + terms = State::S1; + + if (GetSize(terms) > 1) + terms = module->ReduceAnd(NEW_ID, terms); + + return conditions_logic_cache[key] = terms; + } + + void translate_rd_feedback_to_en(std::string memid, std::vector &rd_ports, std::vector &wr_ports) + { + std::map>> async_rd_bits; + std::map> muxtree_upstream_map; + std::set non_feedback_nets; + + for (auto wire : module->wires()) + if (wire->port_output) { + std::vector bits = sigmap(wire); + non_feedback_nets.insert(bits.begin(), bits.end()); + } + + for (auto cell : module->cells()) + { + bool ignore_data_port = false; + + if (cell->type.in(ID($mux), ID($pmux))) + { + std::vector sig_a = sigmap(cell->getPort(ID::A)); + std::vector sig_b = sigmap(cell->getPort(ID::B)); + std::vector sig_s = sigmap(cell->getPort(ID::S)); + std::vector sig_y = sigmap(cell->getPort(ID::Y)); + + non_feedback_nets.insert(sig_s.begin(), sig_s.end()); + + for (int i = 0; i < int(sig_y.size()); i++) { + muxtree_upstream_map[sig_y[i]].insert(sig_a[i]); + for (int j = 0; j < int(sig_s.size()); j++) + muxtree_upstream_map[sig_y[i]].insert(sig_b[i + j*sig_y.size()]); + } + + continue; + } + + if (cell->type.in(ID($memwr), ID($memrd)) && + cell->parameters.at(ID::MEMID).decode_string() == memid) + ignore_data_port = true; + + for (auto conn : cell->connections()) + { + if (ignore_data_port && conn.first == ID::DATA) + continue; + std::vector bits = sigmap(conn.second); + non_feedback_nets.insert(bits.begin(), bits.end()); + } + } + + std::set expand_non_feedback_nets = non_feedback_nets; + while (!expand_non_feedback_nets.empty()) + { + std::set new_expand_non_feedback_nets; + + for (auto &bit : expand_non_feedback_nets) + if (muxtree_upstream_map.count(bit)) + for (auto &new_bit : muxtree_upstream_map.at(bit)) + if (!non_feedback_nets.count(new_bit)) { + non_feedback_nets.insert(new_bit); + new_expand_non_feedback_nets.insert(new_bit); + } + + expand_non_feedback_nets.swap(new_expand_non_feedback_nets); + } + + for (auto cell : rd_ports) + { + if (cell->parameters.at(ID::CLK_ENABLE).as_bool()) + continue; + + RTLIL::SigSpec sig_addr = sigmap(cell->getPort(ID::ADDR)); + std::vector sig_data = sigmap(cell->getPort(ID::DATA)); + + for (int i = 0; i < int(sig_data.size()); i++) + if (non_feedback_nets.count(sig_data[i])) + goto not_pure_feedback_port; + + async_rd_bits[sig_addr].resize(max(async_rd_bits.size(), sig_data.size())); + for (int i = 0; i < int(sig_data.size()); i++) + async_rd_bits[sig_addr][i].insert(sig_data[i]); + + not_pure_feedback_port:; + } + + if (async_rd_bits.empty()) + return; + + log("Populating enable bits on write ports of memory %s.%s with aync read feedback:\n", log_id(module), log_id(memid)); + + for (auto cell : wr_ports) + { + RTLIL::SigSpec sig_addr = sigmap_xmux(cell->getPort(ID::ADDR)); + if (!async_rd_bits.count(sig_addr)) + continue; + + log(" Analyzing write port %s.\n", log_id(cell)); + + std::vector cell_data = cell->getPort(ID::DATA); + std::vector cell_en = cell->getPort(ID::EN); + + int created_conditions = 0; + for (int i = 0; i < int(cell_data.size()); i++) + if (cell_en[i] != RTLIL::SigBit(RTLIL::State::S0)) + { + std::map state; + std::set> conditions; + + find_data_feedback(async_rd_bits.at(sig_addr).at(i), cell_data[i], state, conditions); + cell_en[i] = conditions_to_logic(conditions, cell_en[i], created_conditions); + } + + if (created_conditions) { + log(" Added enable logic for %d different cases.\n", created_conditions); + cell->setPort(ID::EN, cell_en); + } + } + } + + // ------------- + // Setup and run + // ------------- + + OptMemFeedbackWorker(RTLIL::Design *design) : design(design) {} + + void operator()(RTLIL::Module* module) + { + std::map, std::vector>> memindex; + + this->module = module; + sigmap.set(module); + sig_to_mux.clear(); + conditions_logic_cache.clear(); + + sigmap_xmux = sigmap; + for (auto cell : module->cells()) + { + if (cell->type == ID($memrd)) + memindex[cell->parameters.at(ID::MEMID).decode_string()].first.push_back(cell); + + if (cell->type == ID($memwr)) + memindex[cell->parameters.at(ID::MEMID).decode_string()].second.push_back(cell); + + if (cell->type == ID($mux)) + { + RTLIL::SigSpec sig_a = sigmap_xmux(cell->getPort(ID::A)); + RTLIL::SigSpec sig_b = sigmap_xmux(cell->getPort(ID::B)); + + if (sig_a.is_fully_undef()) + sigmap_xmux.add(cell->getPort(ID::Y), sig_b); + else if (sig_b.is_fully_undef()) + sigmap_xmux.add(cell->getPort(ID::Y), sig_a); + } + + if (cell->type.in(ID($mux), ID($pmux))) + { + std::vector sig_y = sigmap(cell->getPort(ID::Y)); + for (int i = 0; i < int(sig_y.size()); i++) + sig_to_mux[sig_y[i]] = std::pair(cell, i); + } + } + + for (auto &it : memindex) { + std::sort(it.second.first.begin(), it.second.first.end(), memrd_cmp); + std::sort(it.second.second.begin(), it.second.second.end(), memwr_cmp); + translate_rd_feedback_to_en(it.first, it.second.first, it.second.second); + } + } +}; + +struct OptMemFeedbackPass : public Pass { + OptMemFeedbackPass() : Pass("opt_mem_feedback", "convert memory read-to-write port feedback paths to write enables") { } + void help() override + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" opt_mem_feedback [selection]\n"); + log("\n"); + log("This pass detects cases where an asynchronous read port is connected via\n"); + log("a mux tree to a write port with the same address. When such a path is\n"); + log("found, it is replaced with a new condition on an enable signal, possibly\n"); + log("allowing for removal of the read port.\n"); + log("\n"); + } + void execute(std::vector args, RTLIL::Design *design) override { + log_header(design, "Executing OPT_MEM_FEEDBACK pass (finding memory read-to-write feedback paths).\n"); + extra_args(args, 1, design); + OptMemFeedbackWorker worker(design); + + for (auto module : design->selected_modules()) + worker(module); + } +} OptMemFeedbackPass; + +PRIVATE_NAMESPACE_END + -- cgit v1.2.3 From b706adb809f17ea897e8534a7ee3ae833b243d2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcelina=20Ko=C5=9Bcielnicka?= Date: Sun, 23 May 2021 17:20:55 +0200 Subject: opt_mem_feedback: Convert to Mem helpers. --- passes/opt/opt_mem_feedback.cc | 77 +++++++++++++++--------------------------- 1 file changed, 28 insertions(+), 49 deletions(-) (limited to 'passes/opt') diff --git a/passes/opt/opt_mem_feedback.cc b/passes/opt/opt_mem_feedback.cc index 75e244514..63917c23a 100644 --- a/passes/opt/opt_mem_feedback.cc +++ b/passes/opt/opt_mem_feedback.cc @@ -18,23 +18,12 @@ */ #include "kernel/yosys.h" -#include "kernel/satgen.h" #include "kernel/sigtools.h" -#include "kernel/modtools.h" +#include "kernel/mem.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN -bool memrd_cmp(RTLIL::Cell *a, RTLIL::Cell *b) -{ - return a->name < b->name; -} - -bool memwr_cmp(RTLIL::Cell *a, RTLIL::Cell *b) -{ - return a->parameters.at(ID::PRIORITY).as_int() < b->parameters.at(ID::PRIORITY).as_int(); -} - struct OptMemFeedbackWorker { RTLIL::Design *design; @@ -138,7 +127,7 @@ struct OptMemFeedbackWorker return conditions_logic_cache[key] = terms; } - void translate_rd_feedback_to_en(std::string memid, std::vector &rd_ports, std::vector &wr_ports) + void translate_rd_feedback_to_en(Mem &mem) { std::map>> async_rd_bits; std::map> muxtree_upstream_map; @@ -173,7 +162,7 @@ struct OptMemFeedbackWorker } if (cell->type.in(ID($memwr), ID($memrd)) && - cell->parameters.at(ID::MEMID).decode_string() == memid) + IdString(cell->parameters.at(ID::MEMID).decode_string()) == mem.memid) ignore_data_port = true; for (auto conn : cell->connections()) @@ -201,21 +190,18 @@ struct OptMemFeedbackWorker expand_non_feedback_nets.swap(new_expand_non_feedback_nets); } - for (auto cell : rd_ports) + for (auto &port : mem.rd_ports) { - if (cell->parameters.at(ID::CLK_ENABLE).as_bool()) + if (port.clk_enable) continue; - RTLIL::SigSpec sig_addr = sigmap(cell->getPort(ID::ADDR)); - std::vector sig_data = sigmap(cell->getPort(ID::DATA)); - - for (int i = 0; i < int(sig_data.size()); i++) - if (non_feedback_nets.count(sig_data[i])) + for (auto &bit : port.data) + if (non_feedback_nets.count(bit)) goto not_pure_feedback_port; - async_rd_bits[sig_addr].resize(max(async_rd_bits.size(), sig_data.size())); - for (int i = 0; i < int(sig_data.size()); i++) - async_rd_bits[sig_addr][i].insert(sig_data[i]); + async_rd_bits[port.addr].resize(max(GetSize(async_rd_bits), GetSize(port.data))); + for (int i = 0; i < GetSize(port.data); i++) + async_rd_bits[port.addr][i].insert(port.data[i]); not_pure_feedback_port:; } @@ -223,35 +209,37 @@ struct OptMemFeedbackWorker if (async_rd_bits.empty()) return; - log("Populating enable bits on write ports of memory %s.%s with aync read feedback:\n", log_id(module), log_id(memid)); + bool changed = false; + log("Populating enable bits on write ports of memory %s.%s with aync read feedback:\n", log_id(module), log_id(mem.memid)); - for (auto cell : wr_ports) + for (int i = 0; i < GetSize(mem.wr_ports); i++) { - RTLIL::SigSpec sig_addr = sigmap_xmux(cell->getPort(ID::ADDR)); - if (!async_rd_bits.count(sig_addr)) - continue; + auto &port = mem.wr_ports[i]; - log(" Analyzing write port %s.\n", log_id(cell)); + if (!async_rd_bits.count(port.addr)) + continue; - std::vector cell_data = cell->getPort(ID::DATA); - std::vector cell_en = cell->getPort(ID::EN); + log(" Analyzing write port %d.\n", i); int created_conditions = 0; - for (int i = 0; i < int(cell_data.size()); i++) - if (cell_en[i] != RTLIL::SigBit(RTLIL::State::S0)) + for (int j = 0; j < GetSize(port.data); j++) + if (port.en[j] != RTLIL::SigBit(RTLIL::State::S0)) { std::map state; std::set> conditions; - find_data_feedback(async_rd_bits.at(sig_addr).at(i), cell_data[i], state, conditions); - cell_en[i] = conditions_to_logic(conditions, cell_en[i], created_conditions); + find_data_feedback(async_rd_bits.at(port.addr).at(j), port.data[j], state, conditions); + port.en[j] = conditions_to_logic(conditions, port.en[j], created_conditions); } if (created_conditions) { log(" Added enable logic for %d different cases.\n", created_conditions); - cell->setPort(ID::EN, cell_en); + changed = true; } } + + if (changed) + mem.emit(); } // ------------- @@ -262,7 +250,7 @@ struct OptMemFeedbackWorker void operator()(RTLIL::Module* module) { - std::map, std::vector>> memindex; + std::vector memories = Mem::get_selected_memories(module); this->module = module; sigmap.set(module); @@ -272,12 +260,6 @@ struct OptMemFeedbackWorker sigmap_xmux = sigmap; for (auto cell : module->cells()) { - if (cell->type == ID($memrd)) - memindex[cell->parameters.at(ID::MEMID).decode_string()].first.push_back(cell); - - if (cell->type == ID($memwr)) - memindex[cell->parameters.at(ID::MEMID).decode_string()].second.push_back(cell); - if (cell->type == ID($mux)) { RTLIL::SigSpec sig_a = sigmap_xmux(cell->getPort(ID::A)); @@ -297,11 +279,8 @@ struct OptMemFeedbackWorker } } - for (auto &it : memindex) { - std::sort(it.second.first.begin(), it.second.first.end(), memrd_cmp); - std::sort(it.second.second.begin(), it.second.second.end(), memwr_cmp); - translate_rd_feedback_to_en(it.first, it.second.first, it.second.second); - } + for (auto &mem : memories) + translate_rd_feedback_to_en(mem); } }; -- cgit v1.2.3 From 835688bf80eb9db7241c1aa767b7e97dad1c0eeb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcelina=20Ko=C5=9Bcielnicka?= Date: Mon, 24 May 2021 21:21:51 +0200 Subject: opt_mem_feedback: Rewrite feedback path finding logic. Fixes #2766. --- passes/opt/opt_mem_feedback.cc | 245 ++++++++++++++++++++++------------------- 1 file changed, 130 insertions(+), 115 deletions(-) (limited to 'passes/opt') diff --git a/passes/opt/opt_mem_feedback.cc b/passes/opt/opt_mem_feedback.cc index 63917c23a..f186d845d 100644 --- a/passes/opt/opt_mem_feedback.cc +++ b/passes/opt/opt_mem_feedback.cc @@ -24,30 +24,52 @@ USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN +// Describes found feedback path. +struct FeedbackPath { + // Which write port it is. + int wrport_idx; + // Which data bit of that write port it is. + int data_bit_idx; + // Values of all mux select signals that need to be set to select this path. + dict condition; + // The exact feedback bit used (used to match read port). + SigBit feedback_bit; + + FeedbackPath(int wrport_idx, int data_bit_idx, dict condition, SigBit feedback_bit) : wrport_idx(wrport_idx), data_bit_idx(data_bit_idx), condition(condition), feedback_bit(feedback_bit) {} +}; + struct OptMemFeedbackWorker { RTLIL::Design *design; RTLIL::Module *module; SigMap sigmap, sigmap_xmux; - std::map> sig_to_mux; - std::map>, SigBit>, SigBit> conditions_logic_cache; + dict> sig_to_mux; + dict sig_users_count; + dict>, SigBit>, SigBit> conditions_logic_cache; // ----------------------------------------------------------------- // Converting feedbacks to async read ports to proper enable signals // ----------------------------------------------------------------- - bool find_data_feedback(const std::set &async_rd_bits, RTLIL::SigBit sig, - std::map &state, std::set> &conditions) + void find_data_feedback(const pool &async_rd_bits, RTLIL::SigBit sig, + const dict &state, + int wrport_idx, int data_bit_idx, + std::vector &paths) { if (async_rd_bits.count(sig)) { - conditions.insert(state); - return true; + paths.push_back(FeedbackPath(wrport_idx, data_bit_idx, state, sig)); + return; + } + + if (sig_users_count[sig] != 1) { + // Only descend into muxes if we're the only user. + return; } if (sig_to_mux.count(sig) == 0) - return false; + return; RTLIL::Cell *cell = sig_to_mux.at(sig).first; int bit_idx = sig_to_mux.at(sig).second; @@ -58,46 +80,32 @@ struct OptMemFeedbackWorker std::vector sig_y = sigmap(cell->getPort(ID::Y)); log_assert(sig_y.at(bit_idx) == sig); - for (int i = 0; i < int(sig_s.size()); i++) + for (int i = 0; i < GetSize(sig_s); i++) if (state.count(sig_s[i]) && state.at(sig_s[i]) == true) { - if (find_data_feedback(async_rd_bits, sig_b.at(bit_idx + i*sig_y.size()), state, conditions)) { - RTLIL::SigSpec new_b = cell->getPort(ID::B); - new_b.replace(bit_idx + i*sig_y.size(), RTLIL::State::Sx); - cell->setPort(ID::B, new_b); - } - return false; + find_data_feedback(async_rd_bits, sig_b.at(bit_idx + i*sig_y.size()), state, wrport_idx, data_bit_idx, paths); + return; } - for (int i = 0; i < int(sig_s.size()); i++) + for (int i = 0; i < GetSize(sig_s); i++) { if (state.count(sig_s[i]) && state.at(sig_s[i]) == false) continue; - std::map new_state = state; + dict new_state = state; new_state[sig_s[i]] = true; - if (find_data_feedback(async_rd_bits, sig_b.at(bit_idx + i*sig_y.size()), new_state, conditions)) { - RTLIL::SigSpec new_b = cell->getPort(ID::B); - new_b.replace(bit_idx + i*sig_y.size(), RTLIL::State::Sx); - cell->setPort(ID::B, new_b); - } + find_data_feedback(async_rd_bits, sig_b.at(bit_idx + i*sig_y.size()), new_state, wrport_idx, data_bit_idx, paths); } - std::map new_state = state; - for (int i = 0; i < int(sig_s.size()); i++) - new_state[sig_s[i]] = false; - - if (find_data_feedback(async_rd_bits, sig_a.at(bit_idx), new_state, conditions)) { - RTLIL::SigSpec new_a = cell->getPort(ID::A); - new_a.replace(bit_idx, RTLIL::State::Sx); - cell->setPort(ID::A, new_a); - } + dict new_state = state; + for (auto bit : sig_s) + new_state[bit] = false; - return false; + find_data_feedback(async_rd_bits, sig_a.at(bit_idx), new_state, wrport_idx, data_bit_idx, paths); } - RTLIL::SigBit conditions_to_logic(std::set> &conditions, SigBit olden, int &created_conditions) + RTLIL::SigBit conditions_to_logic(pool> &conditions, SigBit olden) { auto key = make_pair(conditions, olden); @@ -112,10 +120,9 @@ struct OptMemFeedbackWorker sig2.append(it.second ? RTLIL::State::S1 : RTLIL::State::S0); } terms.append(module->Ne(NEW_ID, sig1, sig2)); - created_conditions++; } - if (olden.wire != nullptr || olden != State::S1) + if (olden != State::S1) terms.append(olden); if (GetSize(terms) == 0) @@ -129,117 +136,113 @@ struct OptMemFeedbackWorker void translate_rd_feedback_to_en(Mem &mem) { - std::map>> async_rd_bits; - std::map> muxtree_upstream_map; - std::set non_feedback_nets; - - for (auto wire : module->wires()) - if (wire->port_output) { - std::vector bits = sigmap(wire); - non_feedback_nets.insert(bits.begin(), bits.end()); - } + // Look for async read ports that may be suitable for feedback paths. + dict>> async_rd_bits; - for (auto cell : module->cells()) + for (auto &port : mem.rd_ports) { - bool ignore_data_port = false; + if (port.clk_enable) + continue; - if (cell->type.in(ID($mux), ID($pmux))) - { - std::vector sig_a = sigmap(cell->getPort(ID::A)); - std::vector sig_b = sigmap(cell->getPort(ID::B)); - std::vector sig_s = sigmap(cell->getPort(ID::S)); - std::vector sig_y = sigmap(cell->getPort(ID::Y)); + SigSpec addr = sigmap_xmux(port.addr); - non_feedback_nets.insert(sig_s.begin(), sig_s.end()); + async_rd_bits[addr].resize(mem.width); + for (int i = 0; i < mem.width; i++) + async_rd_bits[addr][i].insert(sigmap(port.data[i])); + } + + if (async_rd_bits.empty()) + return; + + // Look for actual feedback paths. + std::vector paths; + + for (int i = 0; i < GetSize(mem.wr_ports); i++) + { + auto &port = mem.wr_ports[i]; - for (int i = 0; i < int(sig_y.size()); i++) { - muxtree_upstream_map[sig_y[i]].insert(sig_a[i]); - for (int j = 0; j < int(sig_s.size()); j++) - muxtree_upstream_map[sig_y[i]].insert(sig_b[i + j*sig_y.size()]); - } + SigSpec addr = sigmap_xmux(port.addr); + if (!async_rd_bits.count(addr)) continue; - } - if (cell->type.in(ID($memwr), ID($memrd)) && - IdString(cell->parameters.at(ID::MEMID).decode_string()) == mem.memid) - ignore_data_port = true; + log(" Analyzing %s.%s write port %d.\n", log_id(module), log_id(mem.memid), i); - for (auto conn : cell->connections()) + for (int j = 0; j < GetSize(port.data); j++) { - if (ignore_data_port && conn.first == ID::DATA) + if (port.en[j] == State::S0) continue; - std::vector bits = sigmap(conn.second); - non_feedback_nets.insert(bits.begin(), bits.end()); + + dict state; + + find_data_feedback(async_rd_bits.at(addr).at(j), sigmap(port.data[j]), state, i, j, paths); } } - std::set expand_non_feedback_nets = non_feedback_nets; - while (!expand_non_feedback_nets.empty()) - { - std::set new_expand_non_feedback_nets; + if (paths.empty()) + return; - for (auto &bit : expand_non_feedback_nets) - if (muxtree_upstream_map.count(bit)) - for (auto &new_bit : muxtree_upstream_map.at(bit)) - if (!non_feedback_nets.count(new_bit)) { - non_feedback_nets.insert(new_bit); - new_expand_non_feedback_nets.insert(new_bit); - } + // Now determine which read ports are actually used only for + // feedback paths, and can be removed. - expand_non_feedback_nets.swap(new_expand_non_feedback_nets); - } + dict feedback_users_count; + for (auto &path : paths) + feedback_users_count[path.feedback_bit]++; + pool feedback_ok; for (auto &port : mem.rd_ports) { if (port.clk_enable) continue; - for (auto &bit : port.data) - if (non_feedback_nets.count(bit)) - goto not_pure_feedback_port; + bool ok = true; + for (auto bit : sigmap(port.data)) + if (sig_users_count[bit] != feedback_users_count[bit]) + ok = false; - async_rd_bits[port.addr].resize(max(GetSize(async_rd_bits), GetSize(port.data))); - for (int i = 0; i < GetSize(port.data); i++) - async_rd_bits[port.addr][i].insert(port.data[i]); + if (ok) + { + // This port is going bye-bye. + for (auto bit : sigmap(port.data)) + feedback_ok.insert(bit); - not_pure_feedback_port:; + port.removed = true; + } } - if (async_rd_bits.empty()) + if (feedback_ok.empty()) return; - bool changed = false; - log("Populating enable bits on write ports of memory %s.%s with aync read feedback:\n", log_id(module), log_id(mem.memid)); + // Prepare a feedback condition list grouped by port bits. - for (int i = 0; i < GetSize(mem.wr_ports); i++) - { - auto &port = mem.wr_ports[i]; + dict, pool>> portbit_conds; + for (auto &path : paths) + if (feedback_ok.count(path.feedback_bit)) + portbit_conds[std::make_pair(path.wrport_idx, path.data_bit_idx)].insert(path.condition); - if (!async_rd_bits.count(port.addr)) - continue; + if (portbit_conds.empty()) + return; - log(" Analyzing write port %d.\n", i); + // Okay, let's do it. - int created_conditions = 0; - for (int j = 0; j < GetSize(port.data); j++) - if (port.en[j] != RTLIL::SigBit(RTLIL::State::S0)) - { - std::map state; - std::set> conditions; - - find_data_feedback(async_rd_bits.at(port.addr).at(j), port.data[j], state, conditions); - port.en[j] = conditions_to_logic(conditions, port.en[j], created_conditions); - } - - if (created_conditions) { - log(" Added enable logic for %d different cases.\n", created_conditions); - changed = true; - } + log("Populating enable bits on write ports of memory %s.%s with async read feedback:\n", log_id(module), log_id(mem.memid)); + + for (auto &it : portbit_conds) + { + int wrport_idx = it.first.first; + int bit = it.first.second; + auto &port = mem.wr_ports[wrport_idx]; + + port.en[bit] = conditions_to_logic(it.second, port.en[bit]); + log(" Port %d bit %d: added enable logic for %d different cases.\n", wrport_idx, bit, GetSize(it.second)); } - if (changed) - mem.emit(); + mem.emit(); + + for (auto bit : feedback_ok) + module->connect(bit, State::Sx); + + design->scratchpad_set_bool("opt.did_something", true); } // ------------- @@ -258,6 +261,13 @@ struct OptMemFeedbackWorker conditions_logic_cache.clear(); sigmap_xmux = sigmap; + + for (auto wire : module->wires()) { + if (wire->port_output) + for (auto bit : sigmap(wire)) + sig_users_count[bit]++; + } + for (auto cell : module->cells()) { if (cell->type == ID($mux)) @@ -277,6 +287,11 @@ struct OptMemFeedbackWorker for (int i = 0; i < int(sig_y.size()); i++) sig_to_mux[sig_y[i]] = std::pair(cell, i); } + + for (auto &conn : cell->connections()) + if (!cell->known() || cell->input(conn.first)) + for (auto bit : sigmap(conn.second)) + sig_users_count[bit]++; } for (auto &mem : memories) @@ -292,10 +307,10 @@ struct OptMemFeedbackPass : public Pass { log("\n"); log(" opt_mem_feedback [selection]\n"); log("\n"); - log("This pass detects cases where an asynchronous read port is connected via\n"); - log("a mux tree to a write port with the same address. When such a path is\n"); - log("found, it is replaced with a new condition on an enable signal, possibly\n"); - log("allowing for removal of the read port.\n"); + log("This pass detects cases where an asynchronous read port is only connected via\n"); + log("a mux tree to a write port with the same address. When such a connection is\n"); + log("found, it is replaced with a new condition on an enable signal, allowing\n"); + log("for removal of the read port.\n"); log("\n"); } void execute(std::vector args, RTLIL::Design *design) override { -- cgit v1.2.3 From 9d5d5a48b14832b3cc38d78e7e1960b14269ff4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcelina=20Ko=C5=9Bcielnicka?= Date: Tue, 25 May 2021 01:52:52 +0200 Subject: opt_mem_feedback: Add wide port support. --- passes/opt/opt_mem_feedback.cc | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-) (limited to 'passes/opt') diff --git a/passes/opt/opt_mem_feedback.cc b/passes/opt/opt_mem_feedback.cc index f186d845d..90e5cea9b 100644 --- a/passes/opt/opt_mem_feedback.cc +++ b/passes/opt/opt_mem_feedback.cc @@ -144,11 +144,14 @@ struct OptMemFeedbackWorker if (port.clk_enable) continue; - SigSpec addr = sigmap_xmux(port.addr); - - async_rd_bits[addr].resize(mem.width); - for (int i = 0; i < mem.width; i++) - async_rd_bits[addr][i].insert(sigmap(port.data[i])); + for (int sub = 0; sub < (1 << port.wide_log2); sub++) { + SigSpec addr = sigmap_xmux(port.addr); + for (int i = 0; i < port.wide_log2; i++) + addr[i] = State(sub >> i & 1); + async_rd_bits[addr].resize(mem.width); + for (int i = 0; i < mem.width; i++) + async_rd_bits[addr][i].insert(sigmap(port.data[i + sub * mem.width])); + } } if (async_rd_bits.empty()) @@ -161,21 +164,28 @@ struct OptMemFeedbackWorker { auto &port = mem.wr_ports[i]; - SigSpec addr = sigmap_xmux(port.addr); - - if (!async_rd_bits.count(addr)) - continue; - log(" Analyzing %s.%s write port %d.\n", log_id(module), log_id(mem.memid), i); - for (int j = 0; j < GetSize(port.data); j++) + for (int sub = 0; sub < (1 << port.wide_log2); sub++) { - if (port.en[j] == State::S0) + SigSpec addr = sigmap_xmux(port.addr); + for (int k = 0; k < port.wide_log2; k++) + addr[k] = State(sub >> k & 1); + + if (!async_rd_bits.count(addr)) continue; - dict state; + for (int j = 0; j < mem.width; j++) + { + int bit_idx = sub * mem.width + j; + + if (port.en[bit_idx] == State::S0) + continue; + + dict state; - find_data_feedback(async_rd_bits.at(addr).at(j), sigmap(port.data[j]), state, i, j, paths); + find_data_feedback(async_rd_bits.at(addr).at(j), sigmap(port.data[bit_idx]), state, i, bit_idx, paths); + } } } -- cgit v1.2.3 From 5628f5a88fa49c126af0149e302a8292229ab9df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcelina=20Ko=C5=9Bcielnicka?= Date: Tue, 25 May 2021 15:17:29 +0200 Subject: opt_mem_feedback: Respect write port priority. --- passes/opt/opt_mem_feedback.cc | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'passes/opt') diff --git a/passes/opt/opt_mem_feedback.cc b/passes/opt/opt_mem_feedback.cc index 90e5cea9b..8e93c93b9 100644 --- a/passes/opt/opt_mem_feedback.cc +++ b/passes/opt/opt_mem_feedback.cc @@ -237,6 +237,21 @@ struct OptMemFeedbackWorker log("Populating enable bits on write ports of memory %s.%s with async read feedback:\n", log_id(module), log_id(mem.memid)); + // If a write port has a feedback path that we're about to bypass, + // but also has priority over some other write port, the feedback + // path is not necessarily a NOP — it may overwrite the other port. + // Emulate this effect by converting the priority to soft logic + // (this will affect the other port's enable signal). + for (auto &it : portbit_conds) + { + int wrport_idx = it.first.first; + auto &port = mem.wr_ports[wrport_idx]; + + for (int i = 0; i < wrport_idx; i++) + if (port.priority_mask[i]) + mem.emulate_priority(i, wrport_idx); + } + for (auto &it : portbit_conds) { int wrport_idx = it.first.first; -- cgit v1.2.3 From e6b078d156f8690ab06d342da9be9af02cbcc3aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcelina=20Ko=C5=9Bcielnicka?= Date: Tue, 25 May 2021 18:49:17 +0200 Subject: opt_mem: Add reset/init value support. --- passes/opt/opt_mem.cc | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'passes/opt') diff --git a/passes/opt/opt_mem.cc b/passes/opt/opt_mem.cc index 0409fb736..2788648ec 100644 --- a/passes/opt/opt_mem.cc +++ b/passes/opt/opt_mem.cc @@ -51,6 +51,8 @@ struct OptMemPass : public Pass { int total_count = 0; for (auto module : design->selected_modules()) { + SigMap sigmap(module); + FfInitVals initvals(&sigmap, module); for (auto &mem : Mem::get_selected_memories(module)) { bool changed = false; for (auto &port : mem.wr_ports) { @@ -65,6 +67,16 @@ struct OptMemPass : public Pass { } if (mem.wr_ports.empty() && mem.inits.empty()) { + // The whole memory array will contain + // only State::Sx, but the embedded read + // registers could have reset or init values. + // They will probably be optimized away by + // opt_dff later. + for (int i = 0; i < GetSize(mem.rd_ports); i++) { + mem.extract_rdff(i, &initvals); + auto &port = mem.rd_ports[i]; + module->connect(port.data, Const(State::Sx, GetSize(port.data))); + } mem.remove(); total_count++; } -- cgit v1.2.3 From 83a218141c1a333b582c257c316ddea63e1ca519 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcelina=20Ko=C5=9Bcielnicka?= Date: Wed, 26 May 2021 02:49:50 +0200 Subject: kernel/mem: Add sub_addr helpers. --- passes/opt/opt_mem_feedback.cc | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'passes/opt') diff --git a/passes/opt/opt_mem_feedback.cc b/passes/opt/opt_mem_feedback.cc index 8e93c93b9..131103843 100644 --- a/passes/opt/opt_mem_feedback.cc +++ b/passes/opt/opt_mem_feedback.cc @@ -145,9 +145,7 @@ struct OptMemFeedbackWorker continue; for (int sub = 0; sub < (1 << port.wide_log2); sub++) { - SigSpec addr = sigmap_xmux(port.addr); - for (int i = 0; i < port.wide_log2; i++) - addr[i] = State(sub >> i & 1); + SigSpec addr = sigmap_xmux(port.sub_addr(sub)); async_rd_bits[addr].resize(mem.width); for (int i = 0; i < mem.width; i++) async_rd_bits[addr][i].insert(sigmap(port.data[i + sub * mem.width])); @@ -168,9 +166,7 @@ struct OptMemFeedbackWorker for (int sub = 0; sub < (1 << port.wide_log2); sub++) { - SigSpec addr = sigmap_xmux(port.addr); - for (int k = 0; k < port.wide_log2; k++) - addr[k] = State(sub >> k & 1); + SigSpec addr = sigmap_xmux(port.sub_addr(sub)); if (!async_rd_bits.count(addr)) continue; -- cgit v1.2.3 From 72787f52fc31954e4b7dc3dc34d86705fc4e9dd1 Mon Sep 17 00:00:00 2001 From: Claire Xenia Wolf Date: Tue, 8 Jun 2021 00:39:36 +0200 Subject: Fixing old e-mail addresses and deadnames s/((Claire|Xen|Xenia|Clifford)\s+)+(Wolf|Xen)\s+<(claire|clifford)@(symbioticeda.com|clifford.at|yosyshq.com)>/Claire Xenia Wolf /gi; s/((Nina|Nak|N\.)\s+)+Engelhardt\s+/N. Engelhardt /gi; s/((David)\s+)+Shah\s+<(dave|david)@(symbioticeda.com|yosyshq.com|ds0.me)>/David Shah /gi; s/((Miodrag)\s+)+Milanovic\s+<(miodrag|micko)@(symbioticeda.com|yosyshq.com)>/Miodrag Milanovic /gi; s,https?://www.clifford.at/yosys/,http://yosyshq.net/yosys/,g; --- passes/opt/muxpack.cc | 2 +- passes/opt/opt.cc | 2 +- passes/opt/opt_clean.cc | 2 +- passes/opt/opt_demorgan.cc | 2 +- passes/opt/opt_dff.cc | 2 +- passes/opt/opt_expr.cc | 2 +- passes/opt/opt_lut_ins.cc | 2 +- passes/opt/opt_mem.cc | 2 +- passes/opt/opt_mem_feedback.cc | 2 +- passes/opt/opt_merge.cc | 2 +- passes/opt/opt_muxtree.cc | 2 +- passes/opt/opt_reduce.cc | 2 +- passes/opt/opt_share.cc | 2 +- passes/opt/pmux2shiftx.cc | 2 +- passes/opt/rmports.cc | 2 +- passes/opt/share.cc | 2 +- passes/opt/wreduce.cc | 2 +- 17 files changed, 17 insertions(+), 17 deletions(-) (limited to 'passes/opt') diff --git a/passes/opt/muxpack.cc b/passes/opt/muxpack.cc index aa5f82437..b5e151098 100644 --- a/passes/opt/muxpack.cc +++ b/passes/opt/muxpack.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * 2019 Eddie Hung * * Permission to use, copy, modify, and/or distribute this software for any diff --git a/passes/opt/opt.cc b/passes/opt/opt.cc index 4b052d9a2..c3e418c07 100644 --- a/passes/opt/opt.cc +++ b/passes/opt/opt.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/opt/opt_clean.cc b/passes/opt/opt_clean.cc index c66f45308..699fd4f80 100644 --- a/passes/opt/opt_clean.cc +++ b/passes/opt/opt_clean.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/opt/opt_demorgan.cc b/passes/opt/opt_demorgan.cc index f0fa86f42..1464c4177 100644 --- a/passes/opt/opt_demorgan.cc +++ b/passes/opt/opt_demorgan.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2017 Clifford Wolf + * Copyright (C) 2017 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/opt/opt_dff.cc b/passes/opt/opt_dff.cc index c87ac3163..94d6d5443 100644 --- a/passes/opt/opt_dff.cc +++ b/passes/opt/opt_dff.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * Copyright (C) 2020 Marcelina Kościelnicka * * Permission to use, copy, modify, and/or distribute this software for any diff --git a/passes/opt/opt_expr.cc b/passes/opt/opt_expr.cc index e36e4419d..84f07c8a9 100644 --- a/passes/opt/opt_expr.cc +++ b/passes/opt/opt_expr.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/opt/opt_lut_ins.cc b/passes/opt/opt_lut_ins.cc index bb40e1e55..99043ef7e 100644 --- a/passes/opt/opt_lut_ins.cc +++ b/passes/opt/opt_lut_ins.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/opt/opt_mem.cc b/passes/opt/opt_mem.cc index 2788648ec..edadf2c7b 100644 --- a/passes/opt/opt_mem.cc +++ b/passes/opt/opt_mem.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/opt/opt_mem_feedback.cc b/passes/opt/opt_mem_feedback.cc index 131103843..9e04772b4 100644 --- a/passes/opt/opt_mem_feedback.cc +++ b/passes/opt/opt_mem_feedback.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/opt/opt_merge.cc b/passes/opt/opt_merge.cc index 9086943dc..f27277574 100644 --- a/passes/opt/opt_merge.cc +++ b/passes/opt/opt_merge.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/opt/opt_muxtree.cc b/passes/opt/opt_muxtree.cc index 67b283e11..8ef54cdda 100644 --- a/passes/opt/opt_muxtree.cc +++ b/passes/opt/opt_muxtree.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/opt/opt_reduce.cc b/passes/opt/opt_reduce.cc index 28de9ceb6..15b2772c7 100644 --- a/passes/opt/opt_reduce.cc +++ b/passes/opt/opt_reduce.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/opt/opt_share.cc b/passes/opt/opt_share.cc index 62a478673..ba85df975 100644 --- a/passes/opt/opt_share.cc +++ b/passes/opt/opt_share.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * 2019 Bogdan Vukobratovic * * Permission to use, copy, modify, and/or distribute this software for any diff --git a/passes/opt/pmux2shiftx.cc b/passes/opt/pmux2shiftx.cc index f3b1fd377..90ddf8dd7 100644 --- a/passes/opt/pmux2shiftx.cc +++ b/passes/opt/pmux2shiftx.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/opt/rmports.cc b/passes/opt/rmports.cc index 99a2a61c8..9fa9f5c2d 100644 --- a/passes/opt/rmports.cc +++ b/passes/opt/rmports.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/opt/share.cc b/passes/opt/share.cc index f7848e01d..88c4dee8b 100644 --- a/passes/opt/share.cc +++ b/passes/opt/share.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/opt/wreduce.cc b/passes/opt/wreduce.cc index a216f36d4..0cdabcc1a 100644 --- a/passes/opt/wreduce.cc +++ b/passes/opt/wreduce.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above -- cgit v1.2.3 From 12b3a9765dafeb8766265c82dee9e06435343e66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcelina=20Ko=C5=9Bcielnicka?= Date: Wed, 9 Jun 2021 16:14:16 +0200 Subject: opt_expr: Optimize div/mod by const 1. Turns out the code for div by a power of 2 is already almost capable of optimizing this to a shift-by-0 or and-with-0, which will be further folded into nothingness; let's beef it up to handle div by 1 as well. Fixes #2820. --- passes/opt/opt_expr.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'passes/opt') diff --git a/passes/opt/opt_expr.cc b/passes/opt/opt_expr.cc index 84f07c8a9..0230a5c40 100644 --- a/passes/opt/opt_expr.cc +++ b/passes/opt/opt_expr.cc @@ -1648,7 +1648,7 @@ skip_identity: goto next_cell; } - for (int i = 1; i < (b_signed ? sig_b.size()-1 : sig_b.size()); i++) + for (int i = 0; i < (b_signed ? sig_b.size()-1 : sig_b.size()); i++) if (b_val == (1 << i)) { if (cell->type.in(ID($div), ID($divfloor))) @@ -1672,7 +1672,7 @@ skip_identity: // Truncating division is the same as flooring division, except when // the result is negative and there is a remainder - then trunc = floor + 1 - if (is_truncating && a_signed) { + if (is_truncating && a_signed && i != 0) { Wire *flooring = module->addWire(NEW_ID, sig_y.size()); cell->setPort(ID::Y, flooring); @@ -1698,7 +1698,7 @@ skip_identity: std::vector new_b = RTLIL::SigSpec(State::S1, i); - if (b_signed) + if (b_signed || i == 0) new_b.push_back(State::S0); cell->type = ID($and); @@ -1707,7 +1707,7 @@ skip_identity: // truncating modulo has the same masked bits as flooring modulo, but // the sign bits are those of A (except when R=0) - if (is_truncating && a_signed) { + if (is_truncating && a_signed && i != 0) { Wire *flooring = module->addWire(NEW_ID, sig_y.size()); cell->setPort(ID::Y, flooring); SigSpec truncating = SigSpec(flooring).extract(0, i); -- cgit v1.2.3 From 1667ad658b3aefd3b5418dace6403d3990029fb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcelina=20Ko=C5=9Bcielnicka?= Date: Wed, 9 Jun 2021 18:41:57 +0200 Subject: opt_expr: Fix mul/div/mod by POT patterns to support >= 32 bits. The previous code, in addition to being needlessly limitted to 32 bits in the first place, also had UB for the 31th bit (doing 1 << 31). --- passes/opt/opt_expr.cc | 207 ++++++++++++++++++++----------------------------- 1 file changed, 85 insertions(+), 122 deletions(-) (limited to 'passes/opt') diff --git a/passes/opt/opt_expr.cc b/passes/opt/opt_expr.cc index 0230a5c40..709cb6020 100644 --- a/passes/opt/opt_expr.cc +++ b/passes/opt/opt_expr.cc @@ -393,29 +393,6 @@ int get_highest_hot_index(RTLIL::SigSpec signal) return -1; } -// if the signal has only one bit set, return the index of that bit. -// otherwise return -1 -int get_onehot_bit_index(RTLIL::SigSpec signal) -{ - int bit_index = -1; - - for (int i = 0; i < GetSize(signal); i++) - { - if (signal[i] == RTLIL::State::S0) - continue; - - if (signal[i] != RTLIL::State::S1) - return -1; - - if (bit_index != -1) - return -1; - - bit_index = i; - } - - return bit_index; -} - 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 noclkinv) { if (!design->selected(module)) @@ -1526,14 +1503,12 @@ skip_identity: RTLIL::SigSpec sig_b = assign_map(cell->getPort(ID::B)); RTLIL::SigSpec sig_y = assign_map(cell->getPort(ID::Y)); - if (sig_b.is_fully_const() && sig_b.size() <= 32) + if (sig_b.is_fully_const()) std::swap(sig_a, sig_b), std::swap(a_signed, b_signed), swapped_ab = true; - if (sig_a.is_fully_def() && sig_a.size() <= 32) + if (sig_a.is_fully_def()) { - int a_val = sig_a.as_int(); - - if (a_val == 0) + if (sig_a.is_fully_zero()) { cover("opt.opt_expr.mul_shift.zero"); @@ -1547,37 +1522,34 @@ skip_identity: goto next_cell; } - for (int i = 1; i < (a_signed ? sig_a.size()-1 : sig_a.size()); i++) - if (a_val == (1 << i)) - { - if (swapped_ab) - cover("opt.opt_expr.mul_shift.swapped"); - else - cover("opt.opt_expr.mul_shift.unswapped"); - - log_debug("Replacing multiply-by-%d cell `%s' in module `%s' with shift-by-%d.\n", - a_val, cell->name.c_str(), module->name.c_str(), i); + int exp; + if (sig_a.is_onehot(&exp) && !(a_signed && exp == GetSize(sig_a) - 1)) + { + if (swapped_ab) + cover("opt.opt_expr.mul_shift.swapped"); + else + cover("opt.opt_expr.mul_shift.unswapped"); - if (!swapped_ab) { - cell->setPort(ID::A, cell->getPort(ID::B)); - cell->parameters.at(ID::A_WIDTH) = cell->parameters.at(ID::B_WIDTH); - cell->parameters.at(ID::A_SIGNED) = cell->parameters.at(ID::B_SIGNED); - } + log_debug("Replacing multiply-by-%s cell `%s' in module `%s' with shift-by-%d.\n", + log_signal(sig_a), cell->name.c_str(), module->name.c_str(), exp); - std::vector new_b = RTLIL::SigSpec(i, 6); + if (!swapped_ab) { + cell->setPort(ID::A, cell->getPort(ID::B)); + cell->parameters.at(ID::A_WIDTH) = cell->parameters.at(ID::B_WIDTH); + cell->parameters.at(ID::A_SIGNED) = cell->parameters.at(ID::B_SIGNED); + } - while (GetSize(new_b) > 1 && new_b.back() == RTLIL::State::S0) - new_b.pop_back(); + Const new_b = exp; - cell->type = ID($shl); - cell->parameters[ID::B_WIDTH] = GetSize(new_b); - cell->parameters[ID::B_SIGNED] = false; - cell->setPort(ID::B, new_b); - cell->check(); + cell->type = ID($shl); + cell->parameters[ID::B_WIDTH] = GetSize(new_b); + cell->parameters[ID::B_SIGNED] = false; + cell->setPort(ID::B, new_b); + cell->check(); - did_something = true; - goto next_cell; - } + did_something = true; + goto next_cell; + } } sig_a = assign_map(cell->getPort(ID::A)); @@ -1622,7 +1594,7 @@ skip_identity: } } - if (!keepdc && cell->type.in(ID($div), ID($mod), ID($divfloor), ID($modfloor))) + if (cell->type.in(ID($div), ID($mod), ID($divfloor), ID($modfloor))) { bool a_signed = cell->parameters[ID::A_SIGNED].as_bool(); bool b_signed = cell->parameters[ID::B_SIGNED].as_bool(); @@ -1630,11 +1602,9 @@ skip_identity: SigSpec sig_b = assign_map(cell->getPort(ID::B)); SigSpec sig_y = assign_map(cell->getPort(ID::Y)); - if (sig_b.is_fully_def() && sig_b.size() <= 32) + if (sig_b.is_fully_def()) { - int b_val = sig_b.as_int(); - - if (b_val == 0) + if (sig_b.is_fully_zero()) { cover("opt.opt_expr.divmod_zero"); @@ -1648,86 +1618,79 @@ skip_identity: goto next_cell; } - for (int i = 0; i < (b_signed ? sig_b.size()-1 : sig_b.size()); i++) - if (b_val == (1 << i)) + int exp; + if (!keepdc && sig_b.is_onehot(&exp) && !(b_signed && exp == GetSize(sig_b) - 1)) + { + if (cell->type.in(ID($div), ID($divfloor))) { - if (cell->type.in(ID($div), ID($divfloor))) - { - cover("opt.opt_expr.div_shift"); - - bool is_truncating = cell->type == ID($div); - log_debug("Replacing %s-divide-by-%d cell `%s' in module `%s' with shift-by-%d.\n", - is_truncating ? "truncating" : "flooring", - b_val, cell->name.c_str(), module->name.c_str(), i); + cover("opt.opt_expr.div_shift"); - std::vector new_b = RTLIL::SigSpec(i, 6); + bool is_truncating = cell->type == ID($div); + log_debug("Replacing %s-divide-by-%s cell `%s' in module `%s' with shift-by-%d.\n", + is_truncating ? "truncating" : "flooring", + log_signal(sig_b), cell->name.c_str(), module->name.c_str(), exp); - while (GetSize(new_b) > 1 && new_b.back() == RTLIL::State::S0) - new_b.pop_back(); + Const new_b = exp; - cell->type = ID($sshr); - cell->parameters[ID::B_WIDTH] = GetSize(new_b); - cell->parameters[ID::B_SIGNED] = false; - cell->setPort(ID::B, new_b); + cell->type = ID($sshr); + cell->parameters[ID::B_WIDTH] = GetSize(new_b); + cell->parameters[ID::B_SIGNED] = false; + cell->setPort(ID::B, new_b); - // Truncating division is the same as flooring division, except when - // the result is negative and there is a remainder - then trunc = floor + 1 - if (is_truncating && a_signed && i != 0) { - Wire *flooring = module->addWire(NEW_ID, sig_y.size()); - cell->setPort(ID::Y, flooring); - - Wire *result_neg = module->addWire(NEW_ID); - module->addXor(NEW_ID, sig_a[sig_a.size()-1], sig_b[sig_b.size()-1], result_neg); - Wire *rem_nonzero = module->addWire(NEW_ID); - module->addReduceOr(NEW_ID, sig_a.extract(0, i), rem_nonzero); - Wire *should_add = module->addWire(NEW_ID); - module->addAnd(NEW_ID, result_neg, rem_nonzero, should_add); - module->addAdd(NEW_ID, flooring, should_add, sig_y); - } + // Truncating division is the same as flooring division, except when + // the result is negative and there is a remainder - then trunc = floor + 1 + if (is_truncating && a_signed && GetSize(sig_a) != 0 && exp != 0) { + Wire *flooring = module->addWire(NEW_ID, sig_y.size()); + cell->setPort(ID::Y, flooring); - cell->check(); + SigSpec a_sign = sig_a[sig_a.size()-1]; + SigSpec rem_nonzero = module->ReduceOr(NEW_ID, sig_a.extract(0, exp)); + SigSpec should_add = module->And(NEW_ID, a_sign, rem_nonzero); + module->addAdd(NEW_ID, flooring, should_add, sig_y); } - else if (cell->type.in(ID($mod), ID($modfloor))) + + cell->check(); + } + else if (cell->type.in(ID($mod), ID($modfloor))) + { + cover("opt.opt_expr.mod_mask"); + + bool is_truncating = cell->type == ID($mod); + log_debug("Replacing %s-modulo-by-%s cell `%s' in module `%s' with bitmask.\n", + is_truncating ? "truncating" : "flooring", + log_signal(sig_b), cell->name.c_str(), module->name.c_str()); + + // truncating modulo has the same masked bits as flooring modulo, but + // the sign bits are those of A (except when R=0) + if (is_truncating && a_signed && GetSize(sig_a) != 0 && exp != 0) { - cover("opt.opt_expr.mod_mask"); + module->remove(cell); + SigSpec truncating = sig_a.extract(0, exp); - bool is_truncating = cell->type == ID($mod); - log_debug("Replacing %s-modulo-by-%d cell `%s' in module `%s' with bitmask.\n", - is_truncating ? "truncating" : "flooring", - b_val, cell->name.c_str(), module->name.c_str()); + SigSpec a_sign = sig_a[sig_a.size()-1]; + SigSpec rem_nonzero = module->ReduceOr(NEW_ID, sig_a.extract(0, exp)); + SigSpec extend_bit = module->And(NEW_ID, a_sign, rem_nonzero); - std::vector new_b = RTLIL::SigSpec(State::S1, i); + truncating.append(extend_bit); + module->addPos(NEW_ID, truncating, sig_y, true); + } + else + { + std::vector new_b = RTLIL::SigSpec(State::S1, exp); - if (b_signed || i == 0) + if (b_signed || exp == 0) new_b.push_back(State::S0); cell->type = ID($and); cell->parameters[ID::B_WIDTH] = GetSize(new_b); cell->setPort(ID::B, new_b); - - // truncating modulo has the same masked bits as flooring modulo, but - // the sign bits are those of A (except when R=0) - if (is_truncating && a_signed && i != 0) { - Wire *flooring = module->addWire(NEW_ID, sig_y.size()); - cell->setPort(ID::Y, flooring); - SigSpec truncating = SigSpec(flooring).extract(0, i); - - Wire *rem_nonzero = module->addWire(NEW_ID); - module->addReduceOr(NEW_ID, truncating, rem_nonzero); - SigSpec a_sign = sig_a[sig_a.size()-1]; - Wire *extend_bit = module->addWire(NEW_ID); - module->addAnd(NEW_ID, a_sign, rem_nonzero, extend_bit); - - truncating.append(extend_bit); - module->addPos(NEW_ID, truncating, sig_y, true); - } - cell->check(); } - - did_something = true; - goto next_cell; } + + did_something = true; + goto next_cell; + } } } @@ -1957,8 +1920,8 @@ skip_alu_split: replace = true; } - int const_bit_hot = get_onehot_bit_index(const_sig); - if (const_bit_hot >= 0 && const_bit_hot < var_width) + int const_bit_hot; + if (const_sig.is_onehot(&const_bit_hot) && const_bit_hot < var_width) { RTLIL::SigSpec var_high_sig(RTLIL::State::S0, var_width - const_bit_hot); for (int i = const_bit_hot; i < var_width; i++) { -- cgit v1.2.3 From 6a6d049f1cb6a96eb2af83441a0fb156efbd9cc3 Mon Sep 17 00:00:00 2001 From: gatecat Date: Fri, 11 Jun 2021 11:11:12 +0100 Subject: opt_muxtree: Update port_off and port_idx even for constant bits Signed-off-by: gatecat --- passes/opt/opt_muxtree.cc | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) (limited to 'passes/opt') diff --git a/passes/opt/opt_muxtree.cc b/passes/opt/opt_muxtree.cc index 8ef54cdda..100b1b495 100644 --- a/passes/opt/opt_muxtree.cc +++ b/passes/opt/opt_muxtree.cc @@ -372,29 +372,28 @@ struct OptMuxtreeWorker int port_idx = 0, port_off = 0; vector bits = sig2bits(sig, false); for (int i = 0; i < GetSize(bits); i++) { - if (bits[i] < 0) - continue; - if (knowledge.known_inactive.at(bits[i])) { - sig[i] = State::S0; - did_something = true; - } else - if (knowledge.known_active.at(bits[i])) { - sig[i] = State::S1; - did_something = true; - } - if (width) { - if (ctrl_bits.count(bits[i])) { - sig[i] = ctrl_bits.at(bits[i]) == port_idx ? State::S1 : State::S0; + if (bits[i] >= 0) { + if (knowledge.known_inactive.at(bits[i])) { + sig[i] = State::S0; + did_something = true; + } else + if (knowledge.known_active.at(bits[i])) { + sig[i] = State::S1; did_something = true; } - if (++port_off == width) - port_idx++, port_off=0; - } else { if (ctrl_bits.count(bits[i])) { - sig[i] = State::S0; + if (width) { + sig[i] = ctrl_bits.at(bits[i]) == port_idx ? State::S1 : State::S0; + } else { + sig[i] = State::S0; + } did_something = true; } } + if (width) { + if (++port_off == width) + port_idx++, port_off=0; + } } if (did_something) { -- cgit v1.2.3 From 436d42c00c2bf1b2eaf84ada388d8aaab65da086 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcelina=20Ko=C5=9Bcielnicka?= Date: Tue, 27 Jul 2021 15:24:48 +0200 Subject: opt_expr: Propagate constants to port connections. This adds one simple piece of functionality to opt_expr: when a cell port is connected to a fully-constant signal (as determined by sigmap), the port is reconnected directly to the constant value. This is just enough optimization to fix the "non-constant $meminit input" problem without requiring a full opt_clean or a separate pass. --- passes/opt/opt_expr.cc | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) (limited to 'passes/opt') diff --git a/passes/opt/opt_expr.cc b/passes/opt/opt_expr.cc index 709cb6020..b7bbb2adf 100644 --- a/passes/opt/opt_expr.cc +++ b/passes/opt/opt_expr.cc @@ -395,9 +395,6 @@ int get_highest_hot_index(RTLIL::SigSpec signal) 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 noclkinv) { - if (!design->selected(module)) - return; - CellTypes ct_combinational; ct_combinational.setup_internals(); ct_combinational.setup_stdcells(); @@ -2007,6 +2004,23 @@ skip_alu_split: } } +void replace_const_connections(RTLIL::Module *module) { + SigMap assign_map(module); + for (auto cell : module->selected_cells()) + { + std::vector> changes; + for (auto &conn : cell->connections()) { + SigSpec mapped = assign_map(conn.second); + if (conn.second != mapped && mapped.is_fully_const()) + changes.push_back({conn.first, mapped}); + } + if (!changes.empty()) + did_something = true; + for (auto &it : changes) + cell->setPort(it.first, it.second); + } +} + struct OptExprPass : public Pass { OptExprPass() : Pass("opt_expr", "perform const folding and simple expression rewriting") { } void help() override @@ -2117,6 +2131,11 @@ struct OptExprPass : public Pass { design->scratchpad_set_bool("opt.did_something", true); } while (did_something); + did_something = false; + replace_const_connections(module); + if (did_something) + design->scratchpad_set_bool("opt.did_something", true); + log_suppressed(); } -- cgit v1.2.3 From 19720b970dff017c47805e37745b9fcf29843c45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcelina=20Ko=C5=9Bcielnicka?= Date: Fri, 21 May 2021 02:26:52 +0200 Subject: memory: Introduce $meminit_v2 cell, with EN input. --- passes/opt/opt_clean.cc | 2 +- passes/opt/wreduce.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'passes/opt') diff --git a/passes/opt/opt_clean.cc b/passes/opt/opt_clean.cc index 699fd4f80..c3a0928ef 100644 --- a/passes/opt/opt_clean.cc +++ b/passes/opt/opt_clean.cc @@ -117,7 +117,7 @@ void rmunused_module_cells(Module *module, bool verbose) } for (Cell *cell : module->cells()) { - if (cell->type.in(ID($memwr), ID($meminit))) { + if (cell->type.in(ID($memwr), ID($meminit), ID($meminit_v2))) { IdString mem_id = cell->getParam(ID::MEMID).decode_string(); mem2cells[mem_id].insert(cell); } diff --git a/passes/opt/wreduce.cc b/passes/opt/wreduce.cc index 0cdabcc1a..f6bf8b51a 100644 --- a/passes/opt/wreduce.cc +++ b/passes/opt/wreduce.cc @@ -558,7 +558,7 @@ struct WreducePass : public Pass { } } - if (!opt_memx && c->type.in(ID($memrd), ID($memwr), ID($meminit))) { + if (!opt_memx && c->type.in(ID($memrd), ID($memwr), ID($meminit), ID($meminit_v2))) { IdString memid = c->getParam(ID::MEMID).decode_string(); RTLIL::Memory *mem = module->memories.at(memid); if (mem->start_offset >= 0) { -- cgit v1.2.3 From 54e75129e57f29df1099f5cd45cec633d80e3841 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcelina=20Ko=C5=9Bcielnicka?= Date: Thu, 29 Jul 2021 16:55:15 +0200 Subject: opt_lut: Allow more than one -dlogic per cell type. Fixes #2061. --- passes/opt/opt_lut.cc | 53 +++++++++++++++++++++++++++++---------------------- 1 file changed, 30 insertions(+), 23 deletions(-) (limited to 'passes/opt') diff --git a/passes/opt/opt_lut.cc b/passes/opt/opt_lut.cc index 623101016..3b079d964 100644 --- a/passes/opt/opt_lut.cc +++ b/passes/opt/opt_lut.cc @@ -24,16 +24,22 @@ USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN +struct dlogic_t { + IdString cell_type; + // LUT input idx -> hard cell's port name + dict lut_input_port; +}; + struct OptLutWorker { - dict> &dlogic; + const std::vector &dlogic; RTLIL::Module *module; ModIndex index; SigMap sigmap; pool luts; dict luts_arity; - dict> luts_dlogics; + dict>> luts_dlogics; dict> luts_dlogic_inputs; int eliminated_count = 0, combined_count = 0; @@ -64,7 +70,7 @@ struct OptLutWorker void show_stats_by_arity() { dict arity_counts; - dict dlogic_counts; + std::vector dlogic_counts(dlogic.size()); int max_arity = 0; for (auto lut_arity : luts_arity) @@ -77,7 +83,7 @@ struct OptLutWorker { for (auto &lut_dlogic : lut_dlogics.second) { - dlogic_counts[lut_dlogic->type]++; + dlogic_counts[lut_dlogic.first]++; } } @@ -87,13 +93,13 @@ struct OptLutWorker if (arity_counts[arity]) log(" %d-LUT %16d\n", arity, arity_counts[arity]); } - for (auto &dlogic_count : dlogic_counts) + for (int i = 0; i < GetSize(dlogic); i++) { - log(" with %-12s %4d\n", dlogic_count.first.c_str(), dlogic_count.second); + log(" with %-12s (#%d) %4d\n", dlogic[i].cell_type.c_str(), i, dlogic_counts[i]); } } - OptLutWorker(dict> &dlogic, RTLIL::Module *module, int limit) : + OptLutWorker(const std::vector &dlogic, RTLIL::Module *module, int limit) : dlogic(dlogic), module(module), index(module), sigmap(module) { log("Discovering LUTs.\n"); @@ -116,20 +122,19 @@ struct OptLutWorker // First, find all dedicated logic we're connected to. This results in an overapproximation // of such connections. - pool lut_all_dlogics; + pool> lut_all_dlogics; for (int i = 0; i < lut_width; i++) { SigBit bit = lut_input[i]; for (auto &port : index.query_ports(bit)) { - if (dlogic.count(port.cell->type)) + for (int j = 0; j < GetSize(dlogic); j++) { - auto &dlogic_map = dlogic[port.cell->type]; - if (dlogic_map.count(i)) + if (dlogic[j].cell_type == port.cell->type) { - if (port.port == dlogic_map[i]) + if (port.port == dlogic[j].lut_input_port.at(i, IdString())) { - lut_all_dlogics.insert(port.cell); + lut_all_dlogics.insert({j, port.cell}); } } } @@ -143,25 +148,25 @@ struct OptLutWorker // * The connection is illegal. // In either of these cases, we don't need to concern ourselves with preserving the connection // between this LUT and this dedicated logic cell. - pool lut_legal_dlogics; + pool> lut_legal_dlogics; pool lut_dlogic_inputs; for (auto lut_dlogic : lut_all_dlogics) { - auto &dlogic_map = dlogic[lut_dlogic->type]; + auto &dlogic_map = dlogic[lut_dlogic.first].lut_input_port; bool legal = true; for (auto &dlogic_conn : dlogic_map) { if (lut_width <= dlogic_conn.first) { - log_debug(" LUT has illegal connection to %s cell %s.%s.\n", lut_dlogic->type.c_str(), log_id(module), log_id(lut_dlogic)); + log_debug(" LUT has illegal connection to %s cell %s.%s.\n", lut_dlogic.second->type.c_str(), log_id(module), log_id(lut_dlogic.second)); log_debug(" LUT input A[%d] not present.\n", dlogic_conn.first); legal = false; break; } - if (sigmap(lut_input[dlogic_conn.first]) != sigmap(lut_dlogic->getPort(dlogic_conn.second))) + if (sigmap(lut_input[dlogic_conn.first]) != sigmap(lut_dlogic.second->getPort(dlogic_conn.second))) { - log_debug(" LUT has illegal connection to %s cell %s.%s.\n", lut_dlogic->type.c_str(), log_id(module), log_id(lut_dlogic)); - log_debug(" LUT input A[%d] (wire %s) not connected to %s port %s (wire %s).\n", dlogic_conn.first, log_signal(lut_input[dlogic_conn.first]), lut_dlogic->type.c_str(), dlogic_conn.second.c_str(), log_signal(lut_dlogic->getPort(dlogic_conn.second))); + log_debug(" LUT has illegal connection to %s cell %s.%s.\n", lut_dlogic.second->type.c_str(), log_id(module), log_id(lut_dlogic.second)); + log_debug(" LUT input A[%d] (wire %s) not connected to %s port %s (wire %s).\n", dlogic_conn.first, log_signal(lut_input[dlogic_conn.first]), lut_dlogic.second->type.c_str(), dlogic_conn.second.c_str(), log_signal(lut_dlogic.second->getPort(dlogic_conn.second))); legal = false; break; } @@ -169,7 +174,7 @@ struct OptLutWorker if (legal) { - log_debug(" LUT has legal connection to %s cell %s.%s.\n", lut_dlogic->type.c_str(), log_id(module), log_id(lut_dlogic)); + log_debug(" LUT has legal connection to %s cell %s.%s.\n", lut_dlogic.second->type.c_str(), log_id(module), log_id(lut_dlogic.second)); lut_legal_dlogics.insert(lut_dlogic); for (auto &dlogic_conn : dlogic_map) lut_dlogic_inputs.insert(dlogic_conn.first); @@ -544,7 +549,7 @@ struct OptLutPass : public Pass { { log_header(design, "Executing OPT_LUT pass (optimize LUTs).\n"); - dict> dlogic; + std::vector dlogic; int limit = -1; size_t argidx; @@ -556,7 +561,8 @@ struct OptLutPass : public Pass { split(tokens, args[++argidx], ':'); if (tokens.size() < 2) log_cmd_error("The -dlogic option requires at least one connection.\n"); - IdString type = "\\" + tokens[0]; + dlogic_t entry; + entry.cell_type = "\\" + tokens[0]; for (auto it = tokens.begin() + 1; it != tokens.end(); ++it) { std::vector conn_tokens; split(conn_tokens, *it, '='); @@ -564,8 +570,9 @@ struct OptLutPass : public Pass { log_cmd_error("Invalid format of -dlogic signal mapping.\n"); IdString logic_port = "\\" + conn_tokens[0]; int lut_input = atoi(conn_tokens[1].c_str()); - dlogic[type][lut_input] = logic_port; + entry.lut_input_port[lut_input] = logic_port; } + dlogic.push_back(entry); continue; } if (args[argidx] == "-limit" && argidx + 1 < args.size()) -- cgit v1.2.3 From 98003430d672af05cff7d950e08a42526e766f74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcelina=20Ko=C5=9Bcielnicka?= Date: Sun, 8 Aug 2021 00:33:31 +0200 Subject: opt_merge: Use FfInitVals. Partial #2920 fix. --- passes/opt/opt_merge.cc | 35 ++++++++--------------------------- 1 file changed, 8 insertions(+), 27 deletions(-) (limited to 'passes/opt') diff --git a/passes/opt/opt_merge.cc b/passes/opt/opt_merge.cc index f27277574..d9861f49b 100644 --- a/passes/opt/opt_merge.cc +++ b/passes/opt/opt_merge.cc @@ -18,6 +18,7 @@ */ #include "kernel/register.h" +#include "kernel/ffinit.h" #include "kernel/sigtools.h" #include "kernel/log.h" #include "kernel/celltypes.h" @@ -35,7 +36,7 @@ struct OptMergeWorker RTLIL::Design *design; RTLIL::Module *module; SigMap assign_map; - SigMap dff_init_map; + FfInitVals initvals; bool mode_share_all; CellTypes ct; @@ -121,8 +122,7 @@ struct OptMergeWorker if (it.first == ID::Q && RTLIL::builtin_ff_cell_types().count(cell->type)) { // For the 'Q' output of state elements, // use its (* init *) attribute value - for (const auto &b : dff_init_map(it.second)) - sig.append(b.wire ? State::Sx : b); + sig = initvals(it.second); } else continue; @@ -176,12 +176,8 @@ struct OptMergeWorker if (it.first == ID::Q && RTLIL::builtin_ff_cell_types().count(cell1->type)) { // For the 'Q' output of state elements, // use the (* init *) attribute value - auto &sig1 = conn1[it.first]; - for (const auto &b : dff_init_map(it.second)) - sig1.append(b.wire ? State::Sx : b); - auto &sig2 = conn2[it.first]; - for (const auto &b : dff_init_map(cell2->getPort(it.first))) - sig2.append(b.wire ? State::Sx : b); + conn1[it.first] = initvals(it.second); + conn2[it.first] = initvals(cell2->getPort(it.first)); } else { conn1[it.first] = RTLIL::SigSpec(); @@ -247,14 +243,7 @@ struct OptMergeWorker log("Finding identical cells in module `%s'.\n", module->name.c_str()); assign_map.set(module); - dff_init_map.set(module); - for (auto &it : module->wires_) - if (it.second->attributes.count(ID::init) != 0) { - Const initval = it.second->attributes.at(ID::init); - for (int i = 0; i < GetSize(initval) && i < GetSize(it.second); i++) - if (initval[i] == State::S0 || initval[i] == State::S1) - dff_init_map.add(SigBit(it.second, i), initval[i]); - } + initvals.set(&assign_map, module); bool did_something = true; while (did_something) @@ -296,16 +285,8 @@ struct OptMergeWorker module->connect(RTLIL::SigSig(it.second, other_sig)); assign_map.add(it.second, other_sig); - if (it.first == ID::Q && RTLIL::builtin_ff_cell_types().count(cell->type)) { - for (auto c : it.second.chunks()) { - auto jt = c.wire->attributes.find(ID::init); - if (jt == c.wire->attributes.end()) - continue; - for (int i = c.offset; i < c.offset + c.width; i++) - jt->second[i] = State::Sx; - } - dff_init_map.add(it.second, Const(State::Sx, GetSize(it.second))); - } + if (it.first == ID::Q && RTLIL::builtin_ff_cell_types().count(cell->type)) + initvals.remove_init(it.second); } } log_debug(" Removing %s cell `%s' from module `%s'.\n", cell->type.c_str(), cell->name.c_str(), module->name.c_str()); -- cgit v1.2.3 From d25b9088c83ba68b938ef9f0d97793a08001a9fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcelina=20Ko=C5=9Bcielnicka?= Date: Wed, 4 Aug 2021 00:02:16 +0200 Subject: Refactor common parts of SAT-using optimizations into a helper. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This also aligns the functionality: - in all cases, the onehot attribute is used to create appropriate constraints (previously, opt_dff didn't do it at all, and share created one-hot constraints based on $pmux presence alone, which is unsound) - in all cases, shift and mul/div/pow cells are now skipped when importing the SAT problem (previously only memory_share did this) — this avoids creating clauses for hard cells that are unlikely to help with proving the UNSATness needed for optimization --- passes/opt/opt_dff.cc | 44 +++++++++--------------------- passes/opt/share.cc | 75 +++++++++++++-------------------------------------- 2 files changed, 30 insertions(+), 89 deletions(-) (limited to 'passes/opt') diff --git a/passes/opt/opt_dff.cc b/passes/opt/opt_dff.cc index 94d6d5443..ddf08392b 100644 --- a/passes/opt/opt_dff.cc +++ b/passes/opt/opt_dff.cc @@ -21,7 +21,8 @@ #include "kernel/log.h" #include "kernel/register.h" #include "kernel/rtlil.h" -#include "kernel/satgen.h" +#include "kernel/qcsat.h" +#include "kernel/modtools.h" #include "kernel/sigtools.h" #include "kernel/ffinit.h" #include "kernel/ff.h" @@ -51,26 +52,23 @@ struct OptDffWorker FfInitVals initvals; dict bitusers; dict bit2mux; - dict bit2driver; typedef std::map pattern_t; typedef std::set patterns_t; typedef std::pair ctrl_t; typedef std::set ctrls_t; - ezSatPtr ez; - SatGen satgen; - pool sat_cells; + ModWalker modwalker; + QuickConeSat qcsat; // Used as a queue. std::vector dff_cells; - OptDffWorker(const OptDffOptions &opt, Module *mod) : opt(opt), module(mod), sigmap(mod), initvals(&sigmap, mod), ez(), satgen(ez.get(), &sigmap) { - // Gathering three kinds of information here for every sigmapped SigBit: + OptDffWorker(const OptDffOptions &opt, Module *mod) : opt(opt), module(mod), sigmap(mod), initvals(&sigmap, mod), modwalker(module->design, module), qcsat(modwalker) { + // Gathering two kinds of information here for every sigmapped SigBit: // // - bitusers: how many users it has (muxes will only be merged into FFs if this is 1, making the FF the only user) // - bit2mux: the mux cell and bit index that drives it, if any - // - bit2driver: the cell driving it, if any for (auto wire : module->wires()) { @@ -88,10 +86,6 @@ struct OptDffWorker for (auto conn : cell->connections()) { bool is_output = cell->output(conn.first); - if (is_output) { - for (auto bit : sigmap(conn.second)) - bit2driver[bit] = cell; - } if (!is_output || !cell->known()) { for (auto bit : sigmap(conn.second)) bitusers[bit]++; @@ -104,20 +98,6 @@ struct OptDffWorker } - std::function sat_import_cell = [&](Cell *c) { - if (!sat_cells.insert(c).second) - return; - if (!satgen.importCell(c)) - return; - for (auto &conn : c->connections()) { - if (!c->input(conn.first)) - continue; - for (auto bit : sigmap(conn.second)) - if (bit2driver.count(bit)) - sat_import_cell(bit2driver.at(bit)); - } - }; - State combine_const(State a, State b) { if (a == State::Sx && !opt.keepdc) return b; @@ -594,19 +574,19 @@ struct OptDffWorker if (!opt.sat) continue; // For each register bit, try to prove that it cannot change from the initial value. If so, remove it - if (!bit2driver.count(ff.sig_d[i])) + if (!modwalker.has_drivers(ff.sig_d.extract(i))) continue; if (val != State::S0 && val != State::S1) continue; - sat_import_cell(bit2driver.at(ff.sig_d[i])); + int init_sat_pi = qcsat.importSigBit(val); + int q_sat_pi = qcsat.importSigBit(ff.sig_q[i]); + int d_sat_pi = qcsat.importSigBit(ff.sig_d[i]); - int init_sat_pi = satgen.importSigSpec(val).front(); - int q_sat_pi = satgen.importSigBit(ff.sig_q[i]); - int d_sat_pi = satgen.importSigBit(ff.sig_d[i]); + qcsat.prepare(); // Try to find out whether the register bit can change under some circumstances - bool counter_example_found = ez->solve(ez->IFF(q_sat_pi, init_sat_pi), ez->NOT(ez->IFF(d_sat_pi, init_sat_pi))); + bool counter_example_found = qcsat.ez->solve(qcsat.ez->IFF(q_sat_pi, init_sat_pi), qcsat.ez->NOT(qcsat.ez->IFF(d_sat_pi, init_sat_pi))); // If the register bit cannot change, we can replace it with a constant if (counter_example_found) diff --git a/passes/opt/share.cc b/passes/opt/share.cc index 88c4dee8b..ee1acfb7f 100644 --- a/passes/opt/share.cc +++ b/passes/opt/share.cc @@ -18,7 +18,7 @@ */ #include "kernel/yosys.h" -#include "kernel/satgen.h" +#include "kernel/qcsat.h" #include "kernel/sigtools.h" #include "kernel/modtools.h" #include "kernel/utils.h" @@ -58,8 +58,6 @@ struct ShareWorker std::map, cell_ptr_cmp> topo_cell_drivers; std::map> topo_bit_drivers; - std::vector> exclusive_ctrls; - // ------------------------------------------------------------------------------ // Find terminal bits -- i.e. bits that do not (exclusively) feed into a mux tree @@ -1156,7 +1154,6 @@ struct ShareWorker recursion_state.clear(); topo_cell_drivers.clear(); topo_bit_drivers.clear(); - exclusive_ctrls.clear(); terminal_bits.clear(); shareable_cells.clear(); forbidden_controls_cache.clear(); @@ -1171,13 +1168,6 @@ struct ShareWorker log("Found %d cells in module %s that may be considered for resource sharing.\n", GetSize(shareable_cells), log_id(module)); - for (auto cell : module->cells()) - if (cell->type == ID($pmux)) - for (auto bit : cell->getPort(ID::S)) - for (auto other_bit : cell->getPort(ID::S)) - if (bit < other_bit) - exclusive_ctrls.push_back(std::pair(bit, other_bit)); - while (!shareable_cells.empty() && config.limit != 0) { RTLIL::Cell *cell = *shareable_cells.begin(); @@ -1256,8 +1246,11 @@ struct ShareWorker optimize_activation_patterns(filtered_cell_activation_patterns); optimize_activation_patterns(filtered_other_cell_activation_patterns); - ezSatPtr ez; - SatGen satgen(ez.get(), &modwalker.sigmap); + QuickConeSat qcsat(modwalker); + if (config.opt_fast) { + qcsat.max_cell_outs = 3; + qcsat.max_cell_count = 100; + } pool sat_cells; std::set bits_queue; @@ -1267,77 +1260,45 @@ 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(qcsat.ez->vec_eq(qcsat.importSig(p.first), qcsat.importSig(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(qcsat.ez->vec_eq(qcsat.importSig(p.first), qcsat.importSig(p.second))); all_ctrl_signals.append(p.first); } - for (auto &bit : cell_activation_signals.to_sigbit_vector()) - bits_queue.insert(bit); - - for (auto &bit : other_cell_activation_signals.to_sigbit_vector()) - bits_queue.insert(bit); - - while (!bits_queue.empty()) - { - pool portbits; - modwalker.get_drivers(portbits, bits_queue); - bits_queue.clear(); - - for (auto &pbit : portbits) - if (sat_cells.count(pbit.cell) == 0 && cone_ct.cell_known(pbit.cell->type)) { - if (config.opt_fast && modwalker.cell_outputs[pbit.cell].size() >= 4) - continue; - // log(" Adding cell %s (%s) to SAT problem.\n", log_id(pbit.cell), log_id(pbit.cell->type)); - bits_queue.insert(modwalker.cell_inputs[pbit.cell].begin(), modwalker.cell_inputs[pbit.cell].end()); - satgen.importCell(pbit.cell); - sat_cells.insert(pbit.cell); - } - - if (config.opt_fast && sat_cells.size() > 100) - break; - } - - for (auto it : exclusive_ctrls) - if (satgen.importedSigBit(it.first) && satgen.importedSigBit(it.second)) { - 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))); - } + qcsat.prepare(); - if (!ez->solve(ez->expression(ez->OpOr, cell_active))) { + int sub1 = qcsat.ez->expression(qcsat.ez->OpOr, cell_active); + if (!qcsat.ez->solve(sub1)) { 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))) { + int sub2 = qcsat.ez->expression(qcsat.ez->OpOr, other_cell_active); + if (!qcsat.ez->solve(sub2)) { 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(); + qcsat.ez->non_incremental(); all_ctrl_signals.sort_and_unify(); - std::vector sat_model = satgen.importSigSpec(all_ctrl_signals); + std::vector sat_model = qcsat.importSig(all_ctrl_signals); std::vector 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)); + qcsat.ez->assume(qcsat.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), qcsat.ez->numCnfVariables(), qcsat.ez->numCnfClauses()); - if (ez->solve(sat_model, sat_model_values)) { + if (qcsat.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--) -- cgit v1.2.3 From e6f3d1c225abecf736782f43af4f36526c63f4c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcelina=20Ko=C5=9Bcielnicka?= Date: Sat, 31 Jul 2021 23:21:37 +0200 Subject: kernel/mem: Introduce transparency masks. --- passes/opt/opt_mem_feedback.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'passes/opt') diff --git a/passes/opt/opt_mem_feedback.cc b/passes/opt/opt_mem_feedback.cc index 9e04772b4..20a2a79ed 100644 --- a/passes/opt/opt_mem_feedback.cc +++ b/passes/opt/opt_mem_feedback.cc @@ -43,6 +43,7 @@ struct OptMemFeedbackWorker RTLIL::Design *design; RTLIL::Module *module; SigMap sigmap, sigmap_xmux; + FfInitVals initvals; dict> sig_to_mux; dict sig_users_count; @@ -245,7 +246,7 @@ struct OptMemFeedbackWorker for (int i = 0; i < wrport_idx; i++) if (port.priority_mask[i]) - mem.emulate_priority(i, wrport_idx); + mem.emulate_priority(i, wrport_idx, &initvals); } for (auto &it : portbit_conds) @@ -278,6 +279,7 @@ struct OptMemFeedbackWorker this->module = module; sigmap.set(module); + initvals.set(&sigmap, module); sig_to_mux.clear(); conditions_logic_cache.clear(); -- cgit v1.2.3 From fd7921776387a05edadcc90d1300670d49a73d68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcelina=20Ko=C5=9Bcielnicka?= Date: Thu, 27 May 2021 20:54:29 +0200 Subject: Add v2 memory cells. --- passes/opt/opt_clean.cc | 4 ++-- passes/opt/opt_expr.cc | 2 +- passes/opt/opt_reduce.cc | 4 ++-- passes/opt/share.cc | 9 ++++++--- passes/opt/wreduce.cc | 2 +- 5 files changed, 12 insertions(+), 9 deletions(-) (limited to 'passes/opt') diff --git a/passes/opt/opt_clean.cc b/passes/opt/opt_clean.cc index c3a0928ef..08e9d6b79 100644 --- a/passes/opt/opt_clean.cc +++ b/passes/opt/opt_clean.cc @@ -117,7 +117,7 @@ void rmunused_module_cells(Module *module, bool verbose) } for (Cell *cell : module->cells()) { - if (cell->type.in(ID($memwr), ID($meminit), ID($meminit_v2))) { + if (cell->type.in(ID($memwr), ID($memwr_v2), ID($meminit), ID($meminit_v2))) { IdString mem_id = cell->getParam(ID::MEMID).decode_string(); mem2cells[mem_id].insert(cell); } @@ -167,7 +167,7 @@ void rmunused_module_cells(Module *module, bool verbose) for (auto bit : sigmap(it.second)) bits.insert(bit); - if (cell->type == ID($memrd)) { + if (cell->type.in(ID($memrd), ID($memrd_v2))) { IdString mem_id = cell->getParam(ID::MEMID).decode_string(); if (mem_unused.count(mem_id)) { mem_unused.erase(mem_id); diff --git a/passes/opt/opt_expr.cc b/passes/opt/opt_expr.cc index b7bbb2adf..cdd821c52 100644 --- a/passes/opt/opt_expr.cc +++ b/passes/opt/opt_expr.cc @@ -441,7 +441,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons if (!noclkinv) { - if (cell->type.in(ID($dff), ID($dffe), ID($dffsr), ID($dffsre), ID($adff), ID($adffe), ID($sdff), ID($sdffe), ID($sdffce), ID($fsm), ID($memrd), ID($memwr))) + if (cell->type.in(ID($dff), ID($dffe), ID($dffsr), ID($dffsre), ID($adff), ID($adffe), ID($sdff), ID($sdffe), ID($sdffce), ID($fsm), ID($memrd), ID($memrd_v2), ID($memwr), ID($memwr_v2))) handle_polarity_inv(cell, ID::CLK, ID::CLK_POLARITY, assign_map, invert_map); if (cell->type.in(ID($sr), ID($dffsr), ID($dffsre), ID($dlatchsr))) { diff --git a/passes/opt/opt_reduce.cc b/passes/opt/opt_reduce.cc index 15b2772c7..b558f547e 100644 --- a/passes/opt/opt_reduce.cc +++ b/passes/opt/opt_reduce.cc @@ -254,9 +254,9 @@ struct OptReduceWorker SigPool mem_wren_sigs; for (auto &cell_it : module->cells_) { RTLIL::Cell *cell = cell_it.second; - if (cell->type == ID($mem)) + if (cell->type.in(ID($mem), ID($mem_v2))) mem_wren_sigs.add(assign_map(cell->getPort(ID::WR_EN))); - if (cell->type == ID($memwr)) + if (cell->type.in(ID($memwr), ID($memwr_v2))) mem_wren_sigs.add(assign_map(cell->getPort(ID::EN))); } for (auto &cell_it : module->cells_) { diff --git a/passes/opt/share.cc b/passes/opt/share.cc index ee1acfb7f..abef71937 100644 --- a/passes/opt/share.cc +++ b/passes/opt/share.cc @@ -366,7 +366,7 @@ struct ShareWorker continue; } - if (cell->type == ID($memrd)) { + if (cell->type.in(ID($memrd), ID($memrd_v2))) { if (cell->parameters.at(ID::CLK_ENABLE).as_bool()) continue; if (config.opt_aggressive || !modwalker.sigmap(cell->getPort(ID::ADDR)).is_fully_const()) @@ -399,11 +399,14 @@ struct ShareWorker if (c1->type != c2->type) return false; - if (c1->type == ID($memrd)) + if (c1->type.in(ID($memrd), ID($memrd_v2))) { if (c1->parameters.at(ID::MEMID).decode_string() != c2->parameters.at(ID::MEMID).decode_string()) return false; + if (c1->parameters.at(ID::WIDTH) != c2->parameters.at(ID::WIDTH)) + return false; + return true; } @@ -703,7 +706,7 @@ struct ShareWorker return supercell; } - if (c1->type == ID($memrd)) + if (c1->type.in(ID($memrd), ID($memrd_v2))) { RTLIL::Cell *supercell = module->addCell(NEW_ID, c1); RTLIL::SigSpec addr1 = c1->getPort(ID::ADDR); diff --git a/passes/opt/wreduce.cc b/passes/opt/wreduce.cc index f6bf8b51a..aaad28ef0 100644 --- a/passes/opt/wreduce.cc +++ b/passes/opt/wreduce.cc @@ -558,7 +558,7 @@ struct WreducePass : public Pass { } } - if (!opt_memx && c->type.in(ID($memrd), ID($memwr), ID($meminit), ID($meminit_v2))) { + if (!opt_memx && c->type.in(ID($memrd), ID($memrd_v2), ID($memwr), ID($memwr_v2), ID($meminit), ID($meminit_v2))) { IdString memid = c->getParam(ID::MEMID).decode_string(); RTLIL::Memory *mem = module->memories.at(memid); if (mem->start_offset >= 0) { -- cgit v1.2.3 From 616ace2d9299eee2006650ed3f13e9241664ad20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcelina=20Ko=C5=9Bcielnicka?= Date: Thu, 12 Aug 2021 03:31:56 +0200 Subject: Add new opt_mem_priority pass. --- passes/opt/Makefile.inc | 1 + passes/opt/opt_mem_priority.cc | 109 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 110 insertions(+) create mode 100644 passes/opt/opt_mem_priority.cc (limited to 'passes/opt') diff --git a/passes/opt/Makefile.inc b/passes/opt/Makefile.inc index b0192235b..d8eb2f0b9 100644 --- a/passes/opt/Makefile.inc +++ b/passes/opt/Makefile.inc @@ -3,6 +3,7 @@ OBJS += passes/opt/opt.o OBJS += passes/opt/opt_merge.o OBJS += passes/opt/opt_mem.o OBJS += passes/opt/opt_mem_feedback.o +OBJS += passes/opt/opt_mem_priority.o OBJS += passes/opt/opt_muxtree.o OBJS += passes/opt/opt_reduce.o OBJS += passes/opt/opt_dff.o diff --git a/passes/opt/opt_mem_priority.cc b/passes/opt/opt_mem_priority.cc new file mode 100644 index 000000000..49ece570b --- /dev/null +++ b/passes/opt/opt_mem_priority.cc @@ -0,0 +1,109 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2021 Marcelina Kościelnicka + * + * 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/modtools.h" +#include "kernel/qcsat.h" +#include "kernel/mem.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct OptMemPriorityPass : public Pass { + OptMemPriorityPass() : Pass("opt_mem_priority", "remove priority relations between write ports that can never collide") { } + void help() override + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" opt_mem_priority [selection]\n"); + log("\n"); + log("This pass detects cases where one memory write port has priority over another\n"); + log("even though they can never collide with each other — ie. there can never be\n"); + log("a situation where a given memory bit is written by both ports at the same\n"); + log("time, for example because of always-different addresses, or mutually exclusive\n"); + log("enable signals. In such cases, the priority relation is removed.\n"); + log("\n"); + } + void execute(std::vector args, RTLIL::Design *design) override { + log_header(design, "Executing OPT_MEM_PRIORITY pass (removing unnecessary memory write priority relations).\n"); + extra_args(args, 1, design); + + ModWalker modwalker(design); + + int total_count = 0; + for (auto module : design->selected_modules()) { + modwalker.setup(module); + for (auto &mem : Mem::get_selected_memories(module)) { + bool mem_changed = false; + QuickConeSat qcsat(modwalker); + for (int i = 0; i < GetSize(mem.wr_ports); i++) { + auto &wport1 = mem.wr_ports[i]; + for (int j = 0; j < GetSize(mem.wr_ports); j++) { + auto &wport2 = mem.wr_ports[j]; + if (!wport1.priority_mask[j]) + continue; + // No mixed width support — we could do it, but + // that would complicate code and wouldn't help + // anything since we run this pass before + // wide ports are created in normal flow. + if (wport1.wide_log2 != wport2.wide_log2) + continue; + // Two ports with priority, let's go. + pool> checked; + SigSpec addr1 = wport1.addr; + SigSpec addr2 = wport2.addr; + int abits = std::max(GetSize(addr1), GetSize(addr2)); + addr1.extend_u0(abits); + addr2.extend_u0(abits); + int addr_eq = qcsat.ez->vec_eq(qcsat.importSig(addr1), qcsat.importSig(addr2)); + bool ok = true; + for (int k = 0; k < GetSize(wport1.data); k++) { + SigBit wen1 = wport1.en[k]; + SigBit wen2 = wport2.en[k]; + if (checked.count({wen1, wen2})) + continue; + int wen1_sat = qcsat.importSigBit(wen1); + int wen2_sat = qcsat.importSigBit(wen2); + qcsat.prepare(); + if (qcsat.ez->solve(wen1_sat, wen2_sat, addr_eq)) { + ok = false; + break; + } + checked.insert({wen1, wen2}); + } + if (ok) { + total_count++; + mem_changed = true; + wport1.priority_mask[j] = false; + } + } + } + if (mem_changed) + mem.emit(); + } + } + + if (total_count) + design->scratchpad_set_bool("opt.did_something", true); + log("Performed a total of %d transformations.\n", total_count); + } +} OptMemPriorityPass; + +PRIVATE_NAMESPACE_END + -- cgit v1.2.3 From f7913285067ed30bf5087f265db7e0bd523af2b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcelina=20Ko=C5=9Bcielnicka?= Date: Fri, 13 Aug 2021 00:43:15 +0200 Subject: Add opt_mem_widen pass. If all of us are wide, then none of us are! --- passes/opt/Makefile.inc | 1 + passes/opt/opt_mem_widen.cc | 107 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+) create mode 100644 passes/opt/opt_mem_widen.cc (limited to 'passes/opt') diff --git a/passes/opt/Makefile.inc b/passes/opt/Makefile.inc index d8eb2f0b9..4e52ad8da 100644 --- a/passes/opt/Makefile.inc +++ b/passes/opt/Makefile.inc @@ -4,6 +4,7 @@ OBJS += passes/opt/opt_merge.o OBJS += passes/opt/opt_mem.o OBJS += passes/opt/opt_mem_feedback.o OBJS += passes/opt/opt_mem_priority.o +OBJS += passes/opt/opt_mem_widen.o OBJS += passes/opt/opt_muxtree.o OBJS += passes/opt/opt_reduce.o OBJS += passes/opt/opt_dff.o diff --git a/passes/opt/opt_mem_widen.cc b/passes/opt/opt_mem_widen.cc new file mode 100644 index 000000000..95e01088c --- /dev/null +++ b/passes/opt/opt_mem_widen.cc @@ -0,0 +1,107 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2021 Marcelina Kościelnicka + * + * 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/mem.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct OptMemWidenPass : public Pass { + OptMemWidenPass() : Pass("opt_mem_widen", "optimize memories where all ports are wide") { } + void help() override + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" opt_mem_widen [options] [selection]\n"); + log("\n"); + log("This pass looks for memories where all ports are wide and adjusts the base\n"); + log("memory width up until that stops being the case.\n"); + log("\n"); + } + void execute(std::vector args, RTLIL::Design *design) override + { + log_header(design, "Executing OPT_MEM_WIDEN pass (optimize memories where all ports are wide).\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + // if (args[argidx] == "-nomux") { + // mode_nomux = true; + // continue; + // } + break; + } + extra_args(args, argidx, design); + + int total_count = 0; + for (auto module : design->selected_modules()) { + for (auto &mem : Mem::get_selected_memories(module)) { + // If the memory has no read ports, opt_clean will remove it + // instead. + if (mem.rd_ports.empty()) + continue; + int factor_log2 = mem.rd_ports[0].wide_log2; + for (auto &port : mem.rd_ports) + if (port.wide_log2 < factor_log2) + factor_log2 = port.wide_log2; + for (auto &port : mem.wr_ports) + if (port.wide_log2 < factor_log2) + factor_log2 = port.wide_log2; + if (factor_log2 == 0) + continue; + log("Widening base width of memory %s in module %s by factor %d.\n", log_id(mem.memid), log_id(module->name), 1 << factor_log2); + total_count++; + // The inits are too messy to expand one-by-one, for they may + // collide with one another after expansion. Just hit it with + // a hammer. + bool has_init = !mem.inits.empty(); + Const init_data; + if (has_init) { + init_data = mem.get_init_data(); + mem.clear_inits(); + } + mem.width <<= factor_log2; + mem.size >>= factor_log2; + mem.start_offset >>= factor_log2; + if (has_init) { + MemInit new_init; + new_init.addr = mem.start_offset; + new_init.data = init_data; + new_init.en = Const(State::S1, mem.width); + mem.inits.push_back(new_init); + } + for (auto &port : mem.rd_ports) { + port.wide_log2 -= factor_log2; + port.addr = port.addr.extract_end(factor_log2); + } + for (auto &port : mem.wr_ports) { + port.wide_log2 -= factor_log2; + port.addr = port.addr.extract_end(factor_log2); + } + mem.emit(); + } + } + + if (total_count) + design->scratchpad_set_bool("opt.did_something", true); + log("Performed a total of %d transformations.\n", total_count); + } +} OptMemWidenPass; + +PRIVATE_NAMESPACE_END -- cgit v1.2.3 From 62d41d46397a93d1efa2b8282203d192b256d824 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcelina=20Ko=C5=9Bcielnicka?= Date: Sat, 21 Aug 2021 23:36:00 +0200 Subject: opt_clean: Make the init attribute follow the FF's Q. Previously, opt_clean would reconnect all ports (including FF Q ports) to a "canonical" SigBit chosen by complex rules, but would leave the init attribute on the old wire. This change applies the same canonicalization rules to the init attributes, ensuring that init moves to wherever the Q port moved. Part of another jab at #2920. --- passes/opt/opt_clean.cc | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'passes/opt') diff --git a/passes/opt/opt_clean.cc b/passes/opt/opt_clean.cc index 08e9d6b79..cb2c261c4 100644 --- a/passes/opt/opt_clean.cc +++ b/passes/opt/opt_clean.cc @@ -339,6 +339,7 @@ bool rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos used_signals_nodrivers.add(it2.second); } } + dict init_bits; for (auto &it : module->wires_) { RTLIL::Wire *wire = it.second; if (wire->port_id > 0) { @@ -354,6 +355,29 @@ bool rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos assign_map.apply(sig); used_signals.add(sig); } + auto it2 = wire->attributes.find(ID::init); + if (it2 != wire->attributes.end()) { + RTLIL::Const &val = it2->second; + SigSpec sig = assign_map(wire); + for (int i = 0; i < GetSize(val) && i < GetSize(sig); i++) + if (val.bits[i] != State::Sx) + init_bits[sig[i]] = val.bits[i]; + wire->attributes.erase(it2); + } + } + + for (auto wire : module->wires()) { + bool found = false; + Const val(State::Sx, wire->width); + for (int i = 0; i < wire->width; i++) { + auto it = init_bits.find(RTLIL::SigBit(wire, i)); + if (it != init_bits.end()) { + val.bits[i] = it->second; + found = true; + } + } + if (found) + wire->attributes[ID::init] = val; } pool del_wires_queue; -- cgit v1.2.3 From 9cbff3a4a972f359d3842b689e135d4f906d763b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcelina=20Ko=C5=9Bcielnicka?= Date: Sun, 22 Aug 2021 17:01:58 +0200 Subject: opt_merge: Remove and reinsert init when connecting nets. Mutating the SigMap by adding a new connection will throw off FfInitVals index. Work around this by removing the relevant init values from index whenever we connect nets, then re-add the new init value. Should fix #2920. --- passes/opt/opt_merge.cc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'passes/opt') diff --git a/passes/opt/opt_merge.cc b/passes/opt/opt_merge.cc index d9861f49b..115eb97a9 100644 --- a/passes/opt/opt_merge.cc +++ b/passes/opt/opt_merge.cc @@ -282,11 +282,12 @@ struct OptMergeWorker RTLIL::SigSpec other_sig = r.first->second->getPort(it.first); log_debug(" Redirecting output %s: %s = %s\n", it.first.c_str(), log_signal(it.second), log_signal(other_sig)); + Const init = initvals(other_sig); + initvals.remove_init(it.second); + initvals.remove_init(other_sig); module->connect(RTLIL::SigSig(it.second, other_sig)); assign_map.add(it.second, other_sig); - - if (it.first == ID::Q && RTLIL::builtin_ff_cell_types().count(cell->type)) - initvals.remove_init(it.second); + initvals.set_init(other_sig, init); } } log_debug(" Removing %s cell `%s' from module `%s'.\n", cell->type.c_str(), cell->name.c_str(), module->name.c_str()); -- cgit v1.2.3 From 63b9df8693840d17def8abcb0e848112283b0231 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcelina=20Ko=C5=9Bcielnicka?= Date: Fri, 1 Oct 2021 23:50:48 +0200 Subject: kernel/ff: Refactor FfData to enable FFs with async load. - *_en is split into *_ce (clock enable) and *_aload (async load aka latch gate enable), so both can be present at once - has_d is removed - has_gclk is added (to have a clear marker for $ff) - d_is_const and val_d leftovers are removed - async2sync, clk2fflogic, opt_dff are updated to operate correctly on FFs with async load --- passes/opt/opt_dff.cc | 229 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 139 insertions(+), 90 deletions(-) (limited to 'passes/opt') diff --git a/passes/opt/opt_dff.cc b/passes/opt/opt_dff.cc index ddf08392b..0e25484b8 100644 --- a/passes/opt/opt_dff.cc +++ b/passes/opt/opt_dff.cc @@ -382,6 +382,69 @@ struct OptDffWorker } } + if (ff.has_aload) { + if (ff.sig_aload == (ff.pol_aload ? State::S0 : State::S1) || (!opt.keepdc && ff.sig_aload == State::Sx)) { + // Always-inactive enable — remove. + log("Removing never-active async load on %s (%s) from module %s.\n", + log_id(cell), log_id(cell->type), log_id(module)); + ff.has_aload = false; + changed = true; + } else if (ff.sig_aload == (ff.pol_aload ? State::S1 : State::S0)) { + // Always-active enable. Make a comb circuit, nuke the FF/latch. + log("Handling always-active async load on %s (%s) from module %s (changing to combinatorial circuit).\n", + log_id(cell), log_id(cell->type), log_id(module)); + initvals.remove_init(ff.sig_q); + module->remove(cell); + if (ff.has_sr) { + SigSpec tmp; + if (ff.is_fine) { + if (ff.pol_set) + tmp = module->MuxGate(NEW_ID, ff.sig_ad, State::S1, ff.sig_set); + else + tmp = module->MuxGate(NEW_ID, State::S1, ff.sig_ad, ff.sig_set); + if (ff.pol_clr) + module->addMuxGate(NEW_ID, tmp, State::S0, ff.sig_clr, ff.sig_q); + else + module->addMuxGate(NEW_ID, State::S0, tmp, ff.sig_clr, ff.sig_q); + } else { + if (ff.pol_set) + tmp = module->Or(NEW_ID, ff.sig_ad, ff.sig_set); + else + tmp = module->Or(NEW_ID, ff.sig_ad, module->Not(NEW_ID, ff.sig_set)); + if (ff.pol_clr) + module->addAnd(NEW_ID, tmp, module->Not(NEW_ID, ff.sig_clr), ff.sig_q); + else + module->addAnd(NEW_ID, tmp, ff.sig_clr, ff.sig_q); + } + } else if (ff.has_arst) { + if (ff.is_fine) { + if (ff.pol_arst) + module->addMuxGate(NEW_ID, ff.sig_ad, ff.val_arst[0], ff.sig_arst, ff.sig_q); + else + module->addMuxGate(NEW_ID, ff.val_arst[0], ff.sig_ad, ff.sig_arst, ff.sig_q); + } else { + if (ff.pol_arst) + module->addMux(NEW_ID, ff.sig_ad, ff.val_arst, ff.sig_arst, ff.sig_q); + else + module->addMux(NEW_ID, ff.val_arst, ff.sig_ad, ff.sig_arst, ff.sig_q); + } + } else { + module->connect(ff.sig_q, ff.sig_ad); + } + did_something = true; + continue; + } else if (ff.sig_ad.is_fully_const() && !ff.has_arst && !ff.has_sr) { + log("Changing const-value async load to async reset on %s (%s) from module %s.\n", + log_id(cell), log_id(cell->type), log_id(module)); + ff.has_arst = true; + ff.has_aload = false; + ff.sig_arst = ff.sig_aload; + ff.pol_arst = ff.pol_aload; + ff.val_arst = ff.sig_ad.as_const(); + changed = true; + } + } + if (ff.has_arst) { if (ff.sig_arst == (ff.pol_arst ? State::S0 : State::S1)) { // Always-inactive reset — remove. @@ -414,111 +477,63 @@ struct OptDffWorker log_id(cell), log_id(cell->type), log_id(module)); ff.has_srst = false; if (!ff.ce_over_srst) - ff.has_en = false; - ff.sig_d = ff.val_d = ff.val_srst; - ff.d_is_const = true; + ff.has_ce = false; + ff.sig_d = ff.val_srst; changed = true; } } - if (ff.has_en) { - if (ff.sig_en == (ff.pol_en ? State::S0 : State::S1) || (!opt.keepdc && ff.sig_en == State::Sx)) { + if (ff.has_ce) { + if (ff.sig_ce == (ff.pol_ce ? State::S0 : State::S1) || (!opt.keepdc && ff.sig_ce == State::Sx)) { // Always-inactive enable — remove. - if (ff.has_clk && ff.has_srst && !ff.ce_over_srst) { + if (ff.has_srst && !ff.ce_over_srst) { log("Handling never-active EN on %s (%s) from module %s (connecting SRST instead).\n", log_id(cell), log_id(cell->type), log_id(module)); // FF with sync reset — connect the sync reset to D instead. - ff.pol_en = ff.pol_srst; - ff.sig_en = ff.sig_srst; + ff.pol_ce = ff.pol_srst; + ff.sig_ce = ff.sig_srst; ff.has_srst = false; - ff.sig_d = ff.val_d = ff.val_srst; - ff.d_is_const = true; + ff.sig_d = ff.val_srst; changed = true; } else { log("Handling never-active EN on %s (%s) from module %s (removing D path).\n", log_id(cell), log_id(cell->type), log_id(module)); - // The D input path is effectively useless, so remove it (this will be a const-input D latch, SR latch, or a const driver). - ff.has_d = ff.has_en = ff.has_clk = false; + // The D input path is effectively useless, so remove it (this will be a D latch, SR latch, or a const driver). + ff.has_ce = ff.has_clk = ff.has_srst = false; changed = true; } - } else if (ff.sig_en == (ff.pol_en ? State::S1 : State::S0)) { - // Always-active enable. - if (ff.has_clk) { - // For FF, just remove the useless enable. - log("Removing always-active EN on %s (%s) from module %s.\n", - log_id(cell), log_id(cell->type), log_id(module)); - ff.has_en = false; - changed = true; - } else { - // For latches, make a comb circuit, nuke the latch. - log("Handling always-active EN on %s (%s) from module %s (changing to combinatorial circuit).\n", - log_id(cell), log_id(cell->type), log_id(module)); - initvals.remove_init(ff.sig_q); - module->remove(cell); - if (ff.has_sr) { - SigSpec tmp; - if (ff.is_fine) { - if (ff.pol_set) - tmp = module->MuxGate(NEW_ID, ff.sig_d, State::S1, ff.sig_set); - else - tmp = module->MuxGate(NEW_ID, State::S1, ff.sig_d, ff.sig_set); - if (ff.pol_clr) - module->addMuxGate(NEW_ID, tmp, State::S0, ff.sig_clr, ff.sig_q); - else - module->addMuxGate(NEW_ID, State::S0, tmp, ff.sig_clr, ff.sig_q); - } else { - if (ff.pol_set) - tmp = module->Or(NEW_ID, ff.sig_d, ff.sig_set); - else - tmp = module->Or(NEW_ID, ff.sig_d, module->Not(NEW_ID, ff.sig_set)); - if (ff.pol_clr) - module->addAnd(NEW_ID, tmp, module->Not(NEW_ID, ff.sig_clr), ff.sig_q); - else - module->addAnd(NEW_ID, tmp, ff.sig_clr, ff.sig_q); - } - } else if (ff.has_arst) { - if (ff.is_fine) { - if (ff.pol_arst) - module->addMuxGate(NEW_ID, ff.sig_d, ff.val_arst[0], ff.sig_arst, ff.sig_q); - else - module->addMuxGate(NEW_ID, ff.val_arst[0], ff.sig_d, ff.sig_arst, ff.sig_q); - } else { - if (ff.pol_arst) - module->addMux(NEW_ID, ff.sig_d, ff.val_arst, ff.sig_arst, ff.sig_q); - else - module->addMux(NEW_ID, ff.val_arst, ff.sig_d, ff.sig_arst, ff.sig_q); - } - } else { - module->connect(ff.sig_q, ff.sig_d); - } - did_something = true; - continue; - } + } else if (ff.sig_ce == (ff.pol_ce ? State::S1 : State::S0)) { + // Always-active enable. Just remove it. + // For FF, just remove the useless enable. + log("Removing always-active EN on %s (%s) from module %s.\n", + log_id(cell), log_id(cell->type), log_id(module)); + ff.has_ce = false; + changed = true; } } if (ff.has_clk) { if (ff.sig_clk.is_fully_const()) { - // Const clock — the D input path is effectively useless, so remove it (this will be a const-input D latch, SR latch, or a const driver). + // Const clock — the D input path is effectively useless, so remove it (this will be a D latch, SR latch, or a const driver). log("Handling const CLK on %s (%s) from module %s (removing D path).\n", log_id(cell), log_id(cell->type), log_id(module)); - ff.has_d = ff.has_en = ff.has_clk = ff.has_srst = false; + ff.has_ce = ff.has_clk = ff.has_srst = false; changed = true; } } - if (ff.has_d && ff.sig_d == ff.sig_q) { + if ((ff.has_clk || ff.has_gclk) && ff.sig_d == ff.sig_q) { // Q wrapped back to D, can be removed. if (ff.has_clk && ff.has_srst) { // FF with sync reset — connect the sync reset to D instead. log("Handling D = Q on %s (%s) from module %s (conecting SRST instead).\n", log_id(cell), log_id(cell->type), log_id(module)); - if (ff.has_en && ff.ce_over_srst) { - if (!ff.pol_en) { + if (ff.has_ce && ff.ce_over_srst) { + if (!ff.pol_ce) { if (ff.is_fine) - ff.sig_en = module->NotGate(NEW_ID, ff.sig_en); + ff.sig_ce = module->NotGate(NEW_ID, ff.sig_ce); else - ff.sig_en = module->Not(NEW_ID, ff.sig_en); + ff.sig_ce = module->Not(NEW_ID, ff.sig_ce); } if (!ff.pol_srst) { if (ff.is_fine) @@ -527,28 +542,34 @@ struct OptDffWorker ff.sig_srst = module->Not(NEW_ID, ff.sig_srst); } if (ff.is_fine) - ff.sig_en = module->AndGate(NEW_ID, ff.sig_en, ff.sig_srst); + ff.sig_ce = module->AndGate(NEW_ID, ff.sig_ce, ff.sig_srst); else - ff.sig_en = module->And(NEW_ID, ff.sig_en, ff.sig_srst); - ff.pol_en = true; + ff.sig_ce = module->And(NEW_ID, ff.sig_ce, ff.sig_srst); + ff.pol_ce = true; } else { - ff.pol_en = ff.pol_srst; - ff.sig_en = ff.sig_srst; + ff.pol_ce = ff.pol_srst; + ff.sig_ce = ff.sig_srst; } - ff.has_en = true; + ff.has_ce = true; ff.has_srst = false; - ff.sig_d = ff.val_d = ff.val_srst; - ff.d_is_const = true; + ff.sig_d = ff.val_srst; changed = true; } else { // The D input path is effectively useless, so remove it (this will be a const-input D latch, SR latch, or a const driver). log("Handling D = Q on %s (%s) from module %s (removing D path).\n", log_id(cell), log_id(cell->type), log_id(module)); - ff.has_d = ff.has_en = ff.has_clk = false; + ff.has_clk = ff.has_ce = ff.has_clk = false; changed = true; } } + if (ff.has_aload && !ff.has_clk && ff.sig_ad == ff.sig_q) { + log("Handling AD = Q on %s (%s) from module %s (removing async load path).\n", + log_id(cell), log_id(cell->type), log_id(module)); + ff.has_aload = false; + changed = true; + } + // Now check if any bit can be replaced by a constant. pool removed_sigbits; for (int i = 0; i < ff.width; i++) { @@ -565,7 +586,7 @@ struct OptDffWorker } if (val == State::Sm) continue; - if (ff.has_d) { + if (ff.has_clk || ff.has_gclk) { if (!ff.sig_d[i].wire) { val = combine_const(val, ff.sig_d[i].data); if (val == State::Sm) @@ -593,6 +614,34 @@ struct OptDffWorker continue; } } + if (ff.has_aload) { + if (!ff.sig_ad[i].wire) { + val = combine_const(val, ff.sig_ad[i].data); + if (val == State::Sm) + continue; + } else { + if (!opt.sat) + continue; + // For each register bit, try to prove that it cannot change from the initial value. If so, remove it + if (!modwalker.has_drivers(ff.sig_ad.extract(i))) + continue; + if (val != State::S0 && val != State::S1) + continue; + + int init_sat_pi = qcsat.importSigBit(val); + int q_sat_pi = qcsat.importSigBit(ff.sig_q[i]); + int d_sat_pi = qcsat.importSigBit(ff.sig_ad[i]); + + qcsat.prepare(); + + // Try to find out whether the register bit can change under some circumstances + bool counter_example_found = qcsat.ez->solve(qcsat.ez->IFF(q_sat_pi, init_sat_pi), qcsat.ez->NOT(qcsat.ez->IFF(d_sat_pi, init_sat_pi))); + + // If the register bit cannot change, we can replace it with a constant + if (counter_example_found) + continue; + } + } log("Setting constant %d-bit at position %d on %s (%s) from module %s.\n", val ? 1 : 0, i, log_id(cell), log_id(cell->type), log_id(module)); @@ -616,7 +665,7 @@ struct OptDffWorker // The cell has been simplified as much as possible already. Now try to spice it up with enables / sync resets. if (ff.has_clk) { - if (!ff.has_arst && !ff.has_sr && (!ff.has_srst || !ff.has_en || ff.ce_over_srst) && !opt.nosdff) { + if (!ff.has_arst && !ff.has_sr && (!ff.has_srst || !ff.has_ce || ff.ce_over_srst) && !opt.nosdff) { // Try to merge sync resets. std::map> groups; std::vector remaining_indices; @@ -677,7 +726,7 @@ struct OptDffWorker new_ff.has_srst = true; new_ff.sig_srst = srst.first; new_ff.pol_srst = srst.second; - if (new_ff.has_en) + if (new_ff.has_ce) new_ff.ce_over_srst = true; Cell *new_cell = new_ff.emit(module, NEW_ID); if (new_cell) @@ -695,7 +744,7 @@ struct OptDffWorker changed = true; } } - if ((!ff.has_srst || !ff.has_en || !ff.ce_over_srst) && !opt.nodffe) { + if ((!ff.has_srst || !ff.has_ce || !ff.ce_over_srst) && !opt.nodffe) { // Try to merge enables. std::map, std::vector> groups; std::vector remaining_indices; @@ -725,8 +774,8 @@ struct OptDffWorker if (!opt.simple_dffe) patterns = find_muxtree_feedback_patterns(ff.sig_d[i], ff.sig_q[i], pattern_t()); if (!patterns.empty() || !enables.empty()) { - if (ff.has_en) - enables.insert(ctrl_t(ff.sig_en, ff.pol_en)); + if (ff.has_ce) + enables.insert(ctrl_t(ff.sig_ce, ff.pol_ce)); simplify_patterns(patterns); groups[std::make_pair(patterns, enables)].push_back(i); } else @@ -737,9 +786,9 @@ struct OptDffWorker FfData new_ff = ff.slice(it.second); ctrl_t en = make_patterns_logic(it.first.first, it.first.second, ff.is_fine); - new_ff.has_en = true; - new_ff.sig_en = en.first; - new_ff.pol_en = en.second; + new_ff.has_ce = true; + new_ff.sig_ce = en.first; + new_ff.pol_ce = en.second; new_ff.ce_over_srst = false; Cell *new_cell = new_ff.emit(module, NEW_ID); if (new_cell) -- cgit v1.2.3 From e7d89e653c9d295d3cc9547b83660658e4d1c95b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcelina=20Ko=C5=9Bcielnicka?= Date: Sat, 2 Oct 2021 01:23:43 +0200 Subject: Hook up $aldff support in various passes. --- passes/opt/opt_expr.cc | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'passes/opt') diff --git a/passes/opt/opt_expr.cc b/passes/opt/opt_expr.cc index cdd821c52..be0cd470b 100644 --- a/passes/opt/opt_expr.cc +++ b/passes/opt/opt_expr.cc @@ -441,7 +441,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons if (!noclkinv) { - if (cell->type.in(ID($dff), ID($dffe), ID($dffsr), ID($dffsre), ID($adff), ID($adffe), ID($sdff), ID($sdffe), ID($sdffce), ID($fsm), ID($memrd), ID($memrd_v2), ID($memwr), ID($memwr_v2))) + if (cell->type.in(ID($dff), ID($dffe), ID($dffsr), ID($dffsre), ID($adff), ID($adffe), ID($aldff), ID($aldffe), ID($sdff), ID($sdffe), ID($sdffce), ID($fsm), ID($memrd), ID($memrd_v2), ID($memwr), ID($memwr_v2))) handle_polarity_inv(cell, ID::CLK, ID::CLK_POLARITY, assign_map, invert_map); if (cell->type.in(ID($sr), ID($dffsr), ID($dffsre), ID($dlatchsr))) { @@ -452,10 +452,13 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons if (cell->type.in(ID($adff), ID($adffe), ID($adlatch))) handle_polarity_inv(cell, ID::ARST, ID::ARST_POLARITY, assign_map, invert_map); + if (cell->type.in(ID($aldff), ID($aldffe))) + handle_polarity_inv(cell, ID::ALOAD, ID::ALOAD_POLARITY, assign_map, invert_map); + if (cell->type.in(ID($sdff), ID($sdffe), ID($sdffce))) handle_polarity_inv(cell, ID::SRST, ID::SRST_POLARITY, assign_map, invert_map); - if (cell->type.in(ID($dffe), ID($adffe), ID($sdffe), ID($sdffce), ID($dffsre), ID($dlatch), ID($adlatch), ID($dlatchsr))) + if (cell->type.in(ID($dffe), ID($adffe), ID($aldffe), ID($sdffe), ID($sdffce), ID($dffsre), ID($dlatch), ID($adlatch), ID($dlatchsr))) handle_polarity_inv(cell, ID::EN, ID::EN_POLARITY, assign_map, invert_map); handle_clkpol_celltype_swap(cell, "$_SR_N?_", "$_SR_P?_", ID::S, assign_map, invert_map); @@ -484,6 +487,13 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons handle_clkpol_celltype_swap(cell, "$_SDFFCE_?N??_", "$_SDFFCE_?P??_", ID::R, assign_map, invert_map); handle_clkpol_celltype_swap(cell, "$_SDFFCE_???N_", "$_SDFFCE_???P_", ID::E, assign_map, invert_map); + handle_clkpol_celltype_swap(cell, "$_ALDFF_N?_", "$_ALDFF_P?_", ID::C, assign_map, invert_map); + handle_clkpol_celltype_swap(cell, "$_ALDFF_?N_", "$_ALDFF_?P_", ID::L, assign_map, invert_map); + + handle_clkpol_celltype_swap(cell, "$_ALDFFE_N??_", "$_ALDFFE_P??_", ID::C, assign_map, invert_map); + handle_clkpol_celltype_swap(cell, "$_ALDFFE_?N?_", "$_ALDFFE_?P?_", ID::L, assign_map, invert_map); + handle_clkpol_celltype_swap(cell, "$_ALDFFE_??N_", "$_ALDFFE_??P_", ID::E, assign_map, invert_map); + handle_clkpol_celltype_swap(cell, "$_DFFSR_N??_", "$_DFFSR_P??_", ID::C, assign_map, invert_map); handle_clkpol_celltype_swap(cell, "$_DFFSR_?N?_", "$_DFFSR_?P?_", ID::S, assign_map, invert_map); handle_clkpol_celltype_swap(cell, "$_DFFSR_??N_", "$_DFFSR_??P_", ID::R, assign_map, invert_map); -- cgit v1.2.3 From 4e70c3077562e511d6f840c91dd30ade87d66517 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcelina=20Ko=C5=9Bcielnicka?= Date: Wed, 6 Oct 2021 22:16:55 +0200 Subject: FfData: some refactoring. - FfData now keeps track of the module and underlying cell, if any (so calling emit on FfData created from a cell will replace the existing cell) - FfData implementation is split off to its own .cc file for faster compilation - the "flip FF data sense by inserting inverters in front and after" functionality that zinit uses is moved onto FfData class and beefed up to have dffsr support, to support more use cases --- passes/opt/opt_dff.cc | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'passes/opt') diff --git a/passes/opt/opt_dff.cc b/passes/opt/opt_dff.cc index 0e25484b8..38faba15a 100644 --- a/passes/opt/opt_dff.cc +++ b/passes/opt/opt_dff.cc @@ -275,7 +275,7 @@ struct OptDffWorker bool changed = false; if (!ff.width) { - module->remove(cell); + ff.remove(); did_something = true; continue; } @@ -316,6 +316,7 @@ struct OptDffWorker continue; } ff = ff.slice(keep_bits); + ff.cell = cell; changed = true; } @@ -393,8 +394,7 @@ struct OptDffWorker // Always-active enable. Make a comb circuit, nuke the FF/latch. log("Handling always-active async load on %s (%s) from module %s (changing to combinatorial circuit).\n", log_id(cell), log_id(cell->type), log_id(module)); - initvals.remove_init(ff.sig_q); - module->remove(cell); + ff.remove(); if (ff.has_sr) { SigSpec tmp; if (ff.is_fine) { @@ -456,8 +456,7 @@ struct OptDffWorker // Always-active async reset — change to const driver. log("Handling always-active ARST on %s (%s) from module %s (changing to const driver).\n", log_id(cell), log_id(cell->type), log_id(module)); - initvals.remove_init(ff.sig_q); - module->remove(cell); + ff.remove(); module->connect(ff.sig_q, ff.val_arst); did_something = true; continue; @@ -660,6 +659,7 @@ struct OptDffWorker continue; } ff = ff.slice(keep_bits); + ff.cell = cell; changed = true; } @@ -728,7 +728,7 @@ struct OptDffWorker new_ff.pol_srst = srst.second; if (new_ff.has_ce) new_ff.ce_over_srst = true; - Cell *new_cell = new_ff.emit(module, NEW_ID); + Cell *new_cell = new_ff.emit(); if (new_cell) dff_cells.push_back(new_cell); log("Adding SRST signal on %s (%s) from module %s (D = %s, Q = %s, rval = %s).\n", @@ -741,6 +741,7 @@ struct OptDffWorker continue; } else if (GetSize(remaining_indices) != ff.width) { ff = ff.slice(remaining_indices); + ff.cell = cell; changed = true; } } @@ -790,7 +791,7 @@ struct OptDffWorker new_ff.sig_ce = en.first; new_ff.pol_ce = en.second; new_ff.ce_over_srst = false; - Cell *new_cell = new_ff.emit(module, NEW_ID); + Cell *new_cell = new_ff.emit(); if (new_cell) dff_cells.push_back(new_cell); log("Adding EN signal on %s (%s) from module %s (D = %s, Q = %s).\n", @@ -803,6 +804,7 @@ struct OptDffWorker continue; } else if (GetSize(remaining_indices) != ff.width) { ff = ff.slice(remaining_indices); + ff.cell = cell; changed = true; } } @@ -810,9 +812,7 @@ struct OptDffWorker if (changed) { // Rebuild the FF. - IdString name = cell->name; - module->remove(cell); - ff.emit(module, name); + ff.emit(); did_something = true; } } -- cgit v1.2.3 From 0c7461fe5e7ff8deacf4a16fa0e67e6666a17441 Mon Sep 17 00:00:00 2001 From: Pepijn de Vos Date: Sat, 6 Nov 2021 16:09:30 +0100 Subject: gowin: widelut support (#3042) --- passes/opt/opt_lut_ins.cc | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'passes/opt') diff --git a/passes/opt/opt_lut_ins.cc b/passes/opt/opt_lut_ins.cc index 99043ef7e..2f7c392b2 100644 --- a/passes/opt/opt_lut_ins.cc +++ b/passes/opt/opt_lut_ins.cc @@ -193,6 +193,12 @@ struct OptLutInsPass : public Pass { swz += extra; } } + if (techname == "gowin") { + // Pad the LUT to 1 input, adding consts from the front. + if (new_inputs.empty()) { + new_inputs.insert(new_inputs.begin(), State::S0); + } + } Const new_lut(0, 1 << GetSize(new_inputs)); for (int i = 0; i < GetSize(new_lut); i++) { int lidx = 0; @@ -209,9 +215,9 @@ struct OptLutInsPass : public Pass { } new_lut[i] = lut[lidx]; } - // For ecp5, do not replace with a const driver — the nextpnr + // For ecp5, and gowin do not replace with a const driver — the nextpnr // packer requires a complete set of LUTs for wide LUT muxes. - if (new_inputs.empty() && techname != "ecp5") { + if (new_inputs.empty() && techname != "ecp5" && techname != "gowin") { // const driver. remove_cells.push_back(cell); module->connect(output, new_lut[0]); -- cgit v1.2.3 From 1184a7f3b41f9044b603406c914bf43ab1808b28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcelina=20Ko=C5=9Bcielnicka?= Date: Wed, 8 Dec 2021 23:23:03 +0100 Subject: opt_mem_priority: Fix non-ascii char in help message. This is a fixed version of #3072. --- passes/opt/opt_mem_priority.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'passes/opt') diff --git a/passes/opt/opt_mem_priority.cc b/passes/opt/opt_mem_priority.cc index 49ece570b..a9b145bea 100644 --- a/passes/opt/opt_mem_priority.cc +++ b/passes/opt/opt_mem_priority.cc @@ -34,7 +34,7 @@ struct OptMemPriorityPass : public Pass { log(" opt_mem_priority [selection]\n"); log("\n"); log("This pass detects cases where one memory write port has priority over another\n"); - log("even though they can never collide with each other — ie. there can never be\n"); + log("even though they can never collide with each other -- ie. there can never be\n"); log("a situation where a given memory bit is written by both ports at the same\n"); log("time, for example because of always-different addresses, or mutually exclusive\n"); log("enable signals. In such cases, the priority relation is removed.\n"); -- cgit v1.2.3 From b022fe61a7e0cbe93124a5db243083e65f51fd85 Mon Sep 17 00:00:00 2001 From: Austin Seipp Date: Tue, 4 Jan 2022 10:49:54 -0600 Subject: opt_dff: fix sequence point copy paste bug MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Newer GCCs emit the following warning for opt_dff: passes/opt/opt_dff.cc:560:17: warning: operation on ‘ff.Yosys::FfData::has_clk’ may be undefined [-Wsequence-point] 560 | ff.has_clk = ff.has_ce = ff.has_clk = false; | ~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Which is correct: the order of whether the read or write of has_clk occurs first is undefined since there is no sequence point between them. This is almost certainly just a typo/copy paste error and objectively wrong, so just fix it. Signed-off-by: Austin Seipp --- passes/opt/opt_dff.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'passes/opt') diff --git a/passes/opt/opt_dff.cc b/passes/opt/opt_dff.cc index 38faba15a..98b66dee3 100644 --- a/passes/opt/opt_dff.cc +++ b/passes/opt/opt_dff.cc @@ -557,7 +557,7 @@ struct OptDffWorker // The D input path is effectively useless, so remove it (this will be a const-input D latch, SR latch, or a const driver). log("Handling D = Q on %s (%s) from module %s (removing D path).\n", log_id(cell), log_id(cell->type), log_id(module)); - ff.has_clk = ff.has_ce = ff.has_clk = false; + ff.has_clk = ff.has_ce = false; changed = true; } } -- cgit v1.2.3 From db33b1e535f5ee93dba9ee1cc181b91c482a4dee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcelina=20Ko=C5=9Bcielnicka?= Date: Thu, 27 Jan 2022 17:06:57 +0100 Subject: opt_dff: Don't mutate muxes while ModWalker is active. --- passes/opt/opt_dff.cc | 210 +++++++++++++++++++++++++++----------------------- 1 file changed, 112 insertions(+), 98 deletions(-) (limited to 'passes/opt') diff --git a/passes/opt/opt_dff.cc b/passes/opt/opt_dff.cc index 98b66dee3..73d674c8d 100644 --- a/passes/opt/opt_dff.cc +++ b/passes/opt/opt_dff.cc @@ -58,13 +58,10 @@ struct OptDffWorker typedef std::pair ctrl_t; typedef std::set ctrls_t; - ModWalker modwalker; - QuickConeSat qcsat; - // Used as a queue. std::vector dff_cells; - OptDffWorker(const OptDffOptions &opt, Module *mod) : opt(opt), module(mod), sigmap(mod), initvals(&sigmap, mod), modwalker(module->design, module), qcsat(modwalker) { + OptDffWorker(const OptDffOptions &opt, Module *mod) : opt(opt), module(mod), sigmap(mod), initvals(&sigmap, mod) { // Gathering two kinds of information here for every sigmapped SigBit: // // - bitusers: how many users it has (muxes will only be merged into FFs if this is 1, making the FF the only user) @@ -569,100 +566,6 @@ struct OptDffWorker changed = true; } - // Now check if any bit can be replaced by a constant. - pool removed_sigbits; - for (int i = 0; i < ff.width; i++) { - State val = ff.val_init[i]; - if (ff.has_arst) - val = combine_const(val, ff.val_arst[i]); - if (ff.has_srst) - val = combine_const(val, ff.val_srst[i]); - if (ff.has_sr) { - if (ff.sig_clr[i] != (ff.pol_clr ? State::S0 : State::S1)) - val = combine_const(val, State::S0); - if (ff.sig_set[i] != (ff.pol_set ? State::S0 : State::S1)) - val = combine_const(val, State::S1); - } - if (val == State::Sm) - continue; - if (ff.has_clk || ff.has_gclk) { - if (!ff.sig_d[i].wire) { - val = combine_const(val, ff.sig_d[i].data); - if (val == State::Sm) - continue; - } else { - if (!opt.sat) - continue; - // For each register bit, try to prove that it cannot change from the initial value. If so, remove it - if (!modwalker.has_drivers(ff.sig_d.extract(i))) - continue; - if (val != State::S0 && val != State::S1) - continue; - - int init_sat_pi = qcsat.importSigBit(val); - int q_sat_pi = qcsat.importSigBit(ff.sig_q[i]); - int d_sat_pi = qcsat.importSigBit(ff.sig_d[i]); - - qcsat.prepare(); - - // Try to find out whether the register bit can change under some circumstances - bool counter_example_found = qcsat.ez->solve(qcsat.ez->IFF(q_sat_pi, init_sat_pi), qcsat.ez->NOT(qcsat.ez->IFF(d_sat_pi, init_sat_pi))); - - // If the register bit cannot change, we can replace it with a constant - if (counter_example_found) - continue; - } - } - if (ff.has_aload) { - if (!ff.sig_ad[i].wire) { - val = combine_const(val, ff.sig_ad[i].data); - if (val == State::Sm) - continue; - } else { - if (!opt.sat) - continue; - // For each register bit, try to prove that it cannot change from the initial value. If so, remove it - if (!modwalker.has_drivers(ff.sig_ad.extract(i))) - continue; - if (val != State::S0 && val != State::S1) - continue; - - int init_sat_pi = qcsat.importSigBit(val); - int q_sat_pi = qcsat.importSigBit(ff.sig_q[i]); - int d_sat_pi = qcsat.importSigBit(ff.sig_ad[i]); - - qcsat.prepare(); - - // Try to find out whether the register bit can change under some circumstances - bool counter_example_found = qcsat.ez->solve(qcsat.ez->IFF(q_sat_pi, init_sat_pi), qcsat.ez->NOT(qcsat.ez->IFF(d_sat_pi, init_sat_pi))); - - // If the register bit cannot change, we can replace it with a constant - if (counter_example_found) - continue; - } - } - log("Setting constant %d-bit at position %d on %s (%s) from module %s.\n", val ? 1 : 0, - i, log_id(cell), log_id(cell->type), log_id(module)); - - initvals.remove_init(ff.sig_q[i]); - module->connect(ff.sig_q[i], val); - removed_sigbits.insert(i); - } - if (!removed_sigbits.empty()) { - std::vector keep_bits; - for (int i = 0; i < ff.width; i++) - if (!removed_sigbits.count(i)) - keep_bits.push_back(i); - if (keep_bits.empty()) { - module->remove(cell); - did_something = true; - continue; - } - ff = ff.slice(keep_bits); - ff.cell = cell; - changed = true; - } - // The cell has been simplified as much as possible already. Now try to spice it up with enables / sync resets. if (ff.has_clk) { if (!ff.has_arst && !ff.has_sr && (!ff.has_srst || !ff.has_ce || ff.ce_over_srst) && !opt.nosdff) { @@ -818,6 +721,115 @@ struct OptDffWorker } return did_something; } + + bool run_constbits() { + ModWalker modwalker(module->design, module); + QuickConeSat qcsat(modwalker); + + // Run as a separate sub-pass, so that we don't mutate (non-FF) cells under ModWalker. + bool did_something = false; + for (auto cell : module->selected_cells()) { + if (!RTLIL::builtin_ff_cell_types().count(cell->type)) + continue; + FfData ff(&initvals, cell); + + // Now check if any bit can be replaced by a constant. + pool removed_sigbits; + for (int i = 0; i < ff.width; i++) { + State val = ff.val_init[i]; + if (ff.has_arst) + val = combine_const(val, ff.val_arst[i]); + if (ff.has_srst) + val = combine_const(val, ff.val_srst[i]); + if (ff.has_sr) { + if (ff.sig_clr[i] != (ff.pol_clr ? State::S0 : State::S1)) + val = combine_const(val, State::S0); + if (ff.sig_set[i] != (ff.pol_set ? State::S0 : State::S1)) + val = combine_const(val, State::S1); + } + if (val == State::Sm) + continue; + if (ff.has_clk || ff.has_gclk) { + if (!ff.sig_d[i].wire) { + val = combine_const(val, ff.sig_d[i].data); + if (val == State::Sm) + continue; + } else { + if (!opt.sat) + continue; + // For each register bit, try to prove that it cannot change from the initial value. If so, remove it + if (!modwalker.has_drivers(ff.sig_d.extract(i))) + continue; + if (val != State::S0 && val != State::S1) + continue; + + int init_sat_pi = qcsat.importSigBit(val); + int q_sat_pi = qcsat.importSigBit(ff.sig_q[i]); + int d_sat_pi = qcsat.importSigBit(ff.sig_d[i]); + + qcsat.prepare(); + + // Try to find out whether the register bit can change under some circumstances + bool counter_example_found = qcsat.ez->solve(qcsat.ez->IFF(q_sat_pi, init_sat_pi), qcsat.ez->NOT(qcsat.ez->IFF(d_sat_pi, init_sat_pi))); + + // If the register bit cannot change, we can replace it with a constant + if (counter_example_found) + continue; + } + } + if (ff.has_aload) { + if (!ff.sig_ad[i].wire) { + val = combine_const(val, ff.sig_ad[i].data); + if (val == State::Sm) + continue; + } else { + if (!opt.sat) + continue; + // For each register bit, try to prove that it cannot change from the initial value. If so, remove it + if (!modwalker.has_drivers(ff.sig_ad.extract(i))) + continue; + if (val != State::S0 && val != State::S1) + continue; + + int init_sat_pi = qcsat.importSigBit(val); + int q_sat_pi = qcsat.importSigBit(ff.sig_q[i]); + int d_sat_pi = qcsat.importSigBit(ff.sig_ad[i]); + + qcsat.prepare(); + + // Try to find out whether the register bit can change under some circumstances + bool counter_example_found = qcsat.ez->solve(qcsat.ez->IFF(q_sat_pi, init_sat_pi), qcsat.ez->NOT(qcsat.ez->IFF(d_sat_pi, init_sat_pi))); + + // If the register bit cannot change, we can replace it with a constant + if (counter_example_found) + continue; + } + } + log("Setting constant %d-bit at position %d on %s (%s) from module %s.\n", val ? 1 : 0, + i, log_id(cell), log_id(cell->type), log_id(module)); + + initvals.remove_init(ff.sig_q[i]); + module->connect(ff.sig_q[i], val); + removed_sigbits.insert(i); + } + if (!removed_sigbits.empty()) { + std::vector keep_bits; + for (int i = 0; i < ff.width; i++) + if (!removed_sigbits.count(i)) + keep_bits.push_back(i); + if (keep_bits.empty()) { + module->remove(cell); + did_something = true; + continue; + } + ff = ff.slice(keep_bits); + ff.cell = cell; + ff.emit(); + did_something = true; + } + } + return did_something; + } }; struct OptDffPass : public Pass { @@ -894,6 +906,8 @@ struct OptDffPass : public Pass { OptDffWorker worker(opt, mod); if (worker.run()) did_something = true; + if (worker.run_constbits()) + did_something = true; } if (did_something) -- cgit v1.2.3 From 07a657fb0ca08012af3de410520458af255b1097 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcelina=20Ko=C5=9Bcielnicka?= Date: Sat, 29 Jan 2022 01:01:21 +0100 Subject: opt_reduce: Add $bmux and $demux optimization patterns. --- passes/opt/opt_reduce.cc | 397 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 337 insertions(+), 60 deletions(-) (limited to 'passes/opt') diff --git a/passes/opt/opt_reduce.cc b/passes/opt/opt_reduce.cc index b558f547e..1a7c93fbd 100644 --- a/passes/opt/opt_reduce.cc +++ b/passes/opt/opt_reduce.cc @@ -100,7 +100,7 @@ struct OptReduceWorker return; } - void opt_mux(RTLIL::Cell *cell) + void opt_pmux(RTLIL::Cell *cell) { RTLIL::SigSpec sig_a = assign_map(cell->getPort(ID::A)); RTLIL::SigSpec sig_b = assign_map(cell->getPort(ID::B)); @@ -141,20 +141,20 @@ struct OptReduceWorker handled_sig.insert(this_b); } - if (new_sig_s.size() != sig_s.size()) { - log(" New ctrl vector for %s cell %s: %s\n", cell->type.c_str(), cell->name.c_str(), log_signal(new_sig_s)); - did_something = true; - total_count++; - } - if (new_sig_s.size() == 0) { - module->connect(RTLIL::SigSig(cell->getPort(ID::Y), cell->getPort(ID::A))); + module->connect(cell->getPort(ID::Y), cell->getPort(ID::A)); assign_map.add(cell->getPort(ID::Y), cell->getPort(ID::A)); module->remove(cell); + did_something = true; + total_count++; + return; } - else - { + + if (new_sig_s.size() != sig_s.size() || (new_sig_s.size() == 1 && cell->type == ID($pmux))) { + log(" New ctrl vector for %s cell %s: %s\n", cell->type.c_str(), cell->name.c_str(), log_signal(new_sig_s)); + did_something = true; + total_count++; cell->setPort(ID::B, new_sig_b); cell->setPort(ID::S, new_sig_s); if (new_sig_s.size() > 1) { @@ -166,81 +166,347 @@ struct OptReduceWorker } } - void opt_mux_bits(RTLIL::Cell *cell) + void opt_bmux(RTLIL::Cell *cell) { - std::vector sig_a = assign_map(cell->getPort(ID::A)).to_sigbit_vector(); - std::vector sig_b = assign_map(cell->getPort(ID::B)).to_sigbit_vector(); - std::vector sig_y = assign_map(cell->getPort(ID::Y)).to_sigbit_vector(); + RTLIL::SigSpec sig_a = assign_map(cell->getPort(ID::A)); + RTLIL::SigSpec sig_s = assign_map(cell->getPort(ID::S)); + int width = cell->getParam(ID::WIDTH).as_int(); + + RTLIL::SigSpec new_sig_a, new_sig_s; + dict handled_bits; + + // 0 and up: index of new_sig_s bit + // -1: const 0 + // -2: const 1 + std::vector swizzle; + + for (int i = 0; i < sig_s.size(); i++) + { + SigBit bit = sig_s[i]; + if (bit == State::S0) { + swizzle.push_back(-1); + } else if (bit == State::S1) { + swizzle.push_back(-2); + } else { + auto it = handled_bits.find(bit); + if (it == handled_bits.end()) { + int new_idx = GetSize(new_sig_s); + new_sig_s.append(bit); + handled_bits[bit] = new_idx; + swizzle.push_back(new_idx); + } else { + swizzle.push_back(it->second); + } + } + } + + for (int i = 0; i < (1 << GetSize(new_sig_s)); i++) { + int idx = 0; + for (int j = 0; j < GetSize(sig_s); j++) { + if (swizzle[j] == -1) { + // const 0. + } else if (swizzle[j] == -2) { + // const 1. + idx |= 1 << j; + } else { + if (i & 1 << swizzle[j]) + idx |= 1 << j; + } + } + new_sig_a.append(sig_a.extract(idx * width, width)); + } + + if (new_sig_s.size() == 0) + { + module->connect(cell->getPort(ID::Y), new_sig_a); + assign_map.add(cell->getPort(ID::Y), new_sig_a); + module->remove(cell); + did_something = true; + total_count++; + return; + } + + if (new_sig_s.size() == 1) + { + cell->type = ID($mux); + cell->setPort(ID::A, new_sig_a.extract(0, width)); + cell->setPort(ID::B, new_sig_a.extract(width, width)); + cell->setPort(ID::S, new_sig_s); + cell->parameters.erase(ID::S_WIDTH); + did_something = true; + total_count++; + return; + } + + if (new_sig_s.size() != sig_s.size()) { + log(" New ctrl vector for %s cell %s: %s\n", cell->type.c_str(), cell->name.c_str(), log_signal(new_sig_s)); + did_something = true; + total_count++; + cell->setPort(ID::A, new_sig_a); + cell->setPort(ID::S, new_sig_s); + cell->parameters[ID::S_WIDTH] = RTLIL::Const(new_sig_s.size()); + } + } + + void opt_demux(RTLIL::Cell *cell) + { + RTLIL::SigSpec sig_y = assign_map(cell->getPort(ID::Y)); + RTLIL::SigSpec sig_s = assign_map(cell->getPort(ID::S)); + int width = cell->getParam(ID::WIDTH).as_int(); + + RTLIL::SigSpec new_sig_y, new_sig_s; + dict handled_bits; + + // 0 and up: index of new_sig_s bit + // -1: const 0 + // -2: const 1 + std::vector swizzle; + + for (int i = 0; i < sig_s.size(); i++) + { + SigBit bit = sig_s[i]; + if (bit == State::S0) { + swizzle.push_back(-1); + } else if (bit == State::S1) { + swizzle.push_back(-2); + } else { + auto it = handled_bits.find(bit); + if (it == handled_bits.end()) { + int new_idx = GetSize(new_sig_s); + new_sig_s.append(bit); + handled_bits[bit] = new_idx; + swizzle.push_back(new_idx); + } else { + swizzle.push_back(it->second); + } + } + } + + pool nonzero_idx; + + for (int i = 0; i < (1 << GetSize(new_sig_s)); i++) { + int idx = 0; + for (int j = 0; j < GetSize(sig_s); j++) { + if (swizzle[j] == -1) { + // const 0. + } else if (swizzle[j] == -2) { + // const 1. + idx |= 1 << j; + } else { + if (i & 1 << swizzle[j]) + idx |= 1 << j; + } + } + log_assert(!nonzero_idx.count(idx)); + nonzero_idx.insert(idx); + new_sig_y.append(sig_y.extract(idx * width, width)); + } + + if (new_sig_s.size() == sig_s.size() && sig_s.size() > 0) + return; + + log(" New ctrl vector for %s cell %s: %s\n", cell->type.c_str(), cell->name.c_str(), log_signal(new_sig_s)); + did_something = true; + total_count++; + + for (int i = 0; i < (1 << GetSize(sig_s)); i++) { + if (!nonzero_idx.count(i)) { + SigSpec slice = sig_y.extract(i * width, width); + module->connect(slice, Const(State::S0, width)); + assign_map.add(slice, Const(State::S0, width)); + } + } + + if (new_sig_s.size() == 0) + { + module->connect(new_sig_y, cell->getPort(ID::A)); + assign_map.add(new_sig_y, cell->getPort(ID::A)); + module->remove(cell); + } + else + { + cell->setPort(ID::S, new_sig_s); + cell->setPort(ID::Y, new_sig_y); + cell->parameters[ID::S_WIDTH] = RTLIL::Const(new_sig_s.size()); + } + } + + bool opt_mux_bits(RTLIL::Cell *cell) + { + SigSpec sig_a = assign_map(cell->getPort(ID::A)); + SigSpec sig_b; + SigSpec sig_y = assign_map(cell->getPort(ID::Y)); + int width = GetSize(sig_y); + + if (cell->type != ID($bmux)) + sig_b = assign_map(cell->getPort(ID::B)); - std::vector new_sig_y; RTLIL::SigSig old_sig_conn; - std::vector> consolidated_in_tuples; - std::map, RTLIL::SigBit> consolidated_in_tuples_map; + dict consolidated_in_tuples; + std::vector swizzle; - for (int i = 0; i < int(sig_y.size()); i++) + for (int i = 0; i < width; i++) { - std::vector in_tuple; + SigSpec in_tuple; bool all_tuple_bits_same = true; - in_tuple.push_back(sig_a.at(i)); - for (int j = i; j < int(sig_b.size()); j += int(sig_a.size())) { - if (sig_b.at(j) != sig_a.at(i)) + in_tuple.append(sig_a[i]); + for (int j = i; j < GetSize(sig_a); j += width) { + in_tuple.append(sig_a[j]); + if (sig_a[j] != in_tuple[0]) + all_tuple_bits_same = false; + } + for (int j = i; j < GetSize(sig_b); j += width) { + in_tuple.append(sig_b[j]); + if (sig_b[j] != in_tuple[0]) all_tuple_bits_same = false; - in_tuple.push_back(sig_b.at(j)); } if (all_tuple_bits_same) { - old_sig_conn.first.append(sig_y.at(i)); - old_sig_conn.second.append(sig_a.at(i)); + old_sig_conn.first.append(sig_y[i]); + old_sig_conn.second.append(sig_a[i]); + continue; } - else if (consolidated_in_tuples_map.count(in_tuple)) + + auto it = consolidated_in_tuples.find(in_tuple); + if (it == consolidated_in_tuples.end()) { - old_sig_conn.first.append(sig_y.at(i)); - old_sig_conn.second.append(consolidated_in_tuples_map.at(in_tuple)); + consolidated_in_tuples[in_tuple] = sig_y[i]; + swizzle.push_back(i); } else { - consolidated_in_tuples_map[in_tuple] = sig_y.at(i); - consolidated_in_tuples.push_back(in_tuple); - new_sig_y.push_back(sig_y.at(i)); + old_sig_conn.first.append(sig_y[i]); + old_sig_conn.second.append(it->second); } } - if (new_sig_y.size() != sig_y.size()) + if (GetSize(swizzle) != width) { log(" Consolidated identical input bits for %s cell %s:\n", cell->type.c_str(), cell->name.c_str()); - log(" Old ports: A=%s, B=%s, Y=%s\n", log_signal(cell->getPort(ID::A)), - log_signal(cell->getPort(ID::B)), log_signal(cell->getPort(ID::Y))); - - cell->setPort(ID::A, RTLIL::SigSpec()); - for (auto &in_tuple : consolidated_in_tuples) { - RTLIL::SigSpec new_a = cell->getPort(ID::A); - new_a.append(in_tuple.at(0)); - cell->setPort(ID::A, new_a); + if (cell->type != ID($bmux)) { + log(" Old ports: A=%s, B=%s, Y=%s\n", log_signal(cell->getPort(ID::A)), + log_signal(cell->getPort(ID::B)), log_signal(cell->getPort(ID::Y))); + } else { + log(" Old ports: A=%s, Y=%s\n", log_signal(cell->getPort(ID::A)), + log_signal(cell->getPort(ID::Y))); } - cell->setPort(ID::B, RTLIL::SigSpec()); - for (int i = 1; i <= cell->getPort(ID::S).size(); i++) - for (auto &in_tuple : consolidated_in_tuples) { - RTLIL::SigSpec new_b = cell->getPort(ID::B); - new_b.append(in_tuple.at(i)); - cell->setPort(ID::B, new_b); + if (swizzle.empty()) { + module->remove(cell); + } else { + SigSpec new_sig_a; + for (int i = 0; i < GetSize(sig_a); i += width) + for (int j: swizzle) + new_sig_a.append(sig_a[i+j]); + cell->setPort(ID::A, new_sig_a); + + if (cell->type != ID($bmux)) { + SigSpec new_sig_b; + for (int i = 0; i < GetSize(sig_b); i += width) + for (int j: swizzle) + new_sig_b.append(sig_b[i+j]); + cell->setPort(ID::B, new_sig_b); } - cell->parameters[ID::WIDTH] = RTLIL::Const(new_sig_y.size()); - cell->setPort(ID::Y, new_sig_y); + SigSpec new_sig_y; + for (int j: swizzle) + new_sig_y.append(sig_y[j]); + cell->setPort(ID::Y, new_sig_y); + + cell->parameters[ID::WIDTH] = RTLIL::Const(GetSize(swizzle)); + + if (cell->type != ID($bmux)) { + log(" New ports: A=%s, B=%s, Y=%s\n", log_signal(cell->getPort(ID::A)), + log_signal(cell->getPort(ID::B)), log_signal(cell->getPort(ID::Y))); + } else { + log(" New ports: A=%s, Y=%s\n", log_signal(cell->getPort(ID::A)), + log_signal(cell->getPort(ID::Y))); + } + } - log(" New ports: A=%s, B=%s, Y=%s\n", log_signal(cell->getPort(ID::A)), - log_signal(cell->getPort(ID::B)), log_signal(cell->getPort(ID::Y))); log(" New connections: %s = %s\n", log_signal(old_sig_conn.first), log_signal(old_sig_conn.second)); + module->connect(old_sig_conn); + + did_something = true; + total_count++; + } + return swizzle.empty(); + } + + bool opt_demux_bits(RTLIL::Cell *cell) { + SigSpec sig_a = assign_map(cell->getPort(ID::A)); + SigSpec sig_y = assign_map(cell->getPort(ID::Y)); + int width = GetSize(sig_a); + + RTLIL::SigSig old_sig_conn; + + dict handled_bits; + std::vector swizzle; + + for (int i = 0; i < width; i++) + { + if (sig_a[i] == State::S0) + { + for (int j = i; j < GetSize(sig_y); j += width) + { + old_sig_conn.first.append(sig_y[j]); + old_sig_conn.second.append(State::S0); + } + continue; + } + auto it = handled_bits.find(sig_a[i]); + if (it == handled_bits.end()) + { + handled_bits[sig_a[i]] = i; + swizzle.push_back(i); + } + else + { + for (int j = 0; j < GetSize(sig_y); j += width) + { + old_sig_conn.first.append(sig_y[i+j]); + old_sig_conn.second.append(sig_y[it->second+j]); + } + } + } + + if (GetSize(swizzle) != width) + { + log(" Consolidated identical input bits for %s cell %s:\n", cell->type.c_str(), cell->name.c_str()); + log(" Old ports: A=%s, Y=%s\n", log_signal(cell->getPort(ID::A)), + log_signal(cell->getPort(ID::Y))); + + if (swizzle.empty()) { + module->remove(cell); + } else { + SigSpec new_sig_a; + for (int j: swizzle) + new_sig_a.append(sig_a[j]); + cell->setPort(ID::A, new_sig_a); + + SigSpec new_sig_y; + for (int i = 0; i < GetSize(sig_y); i += width) + for (int j: swizzle) + new_sig_y.append(sig_y[i+j]); + cell->setPort(ID::Y, new_sig_y); + + cell->parameters[ID::WIDTH] = RTLIL::Const(GetSize(swizzle)); + + log(" New ports: A=%s, Y=%s\n", log_signal(cell->getPort(ID::A)), + log_signal(cell->getPort(ID::Y))); + } + + log(" New connections: %s = %s\n", log_signal(old_sig_conn.first), log_signal(old_sig_conn.second)); module->connect(old_sig_conn); did_something = true; total_count++; } + return swizzle.empty(); } OptReduceWorker(RTLIL::Design *design, RTLIL::Module *module, bool do_fine) : @@ -309,20 +575,31 @@ struct OptReduceWorker // merge identical inputs on $mux and $pmux cells - std::vector cells; - - for (auto &it : module->cells_) - if ((it.second->type == ID($mux) || it.second->type == ID($pmux)) && design->selected(module, it.second)) - cells.push_back(it.second); - - for (auto cell : cells) + for (auto cell : module->selected_cells()) { + if (!cell->type.in(ID($mux), ID($pmux), ID($bmux), ID($demux))) + continue; + // this optimization is to aggressive for most coarse-grain applications. // but we always want it for multiplexers driving write enable ports. - if (do_fine || mem_wren_sigs.check_any(assign_map(cell->getPort(ID::Y)))) - opt_mux_bits(cell); + if (do_fine || mem_wren_sigs.check_any(assign_map(cell->getPort(ID::Y)))) { + if (cell->type == ID($demux)) { + if (opt_demux_bits(cell)) + continue; + } else { + if (opt_mux_bits(cell)) + continue; + } + } + + if (cell->type.in(ID($mux), ID($pmux))) + opt_pmux(cell); + + if (cell->type == ID($bmux)) + opt_bmux(cell); - opt_mux(cell); + if (cell->type == ID($demux)) + opt_demux(cell); } } -- cgit v1.2.3