From 243d8317a5c3c900c35fb9e0cdb1a4334fc797f6 Mon Sep 17 00:00:00 2001 From: Akash Levy Date: Mon, 26 Aug 2024 22:36:47 -0700 Subject: [PATCH] SMALL mode with first pass of opt_balance_tree --- Makefile | 4 +- passes/opt/Makefile.inc | 6 +- passes/opt/opt_balance_tree.cc | 276 +++++++++++++++++++++++++++++++++ 3 files changed, 283 insertions(+), 3 deletions(-) create mode 100644 passes/opt/opt_balance_tree.cc diff --git a/Makefile b/Makefile index d758ed4ba..4370a8125 100644 --- a/Makefile +++ b/Makefile @@ -87,7 +87,7 @@ EXTRA_TARGETS = TARGETS = $(PROGRAM_PREFIX)yosys$(EXE) $(PROGRAM_PREFIX)yosys-config PRETTY = 1 -SMALL = 0 +SMALL = 1 # Unit test UNITESTPATH := tests/unit @@ -710,6 +710,8 @@ OBJS += passes/cmds/cover.o OBJS += passes/cmds/design.o OBJS += passes/cmds/plugin.o +OBJS += passes/cmds/splitfanout.o + include $(YOSYS_SRC)/passes/proc/Makefile.inc include $(YOSYS_SRC)/passes/opt/Makefile.inc include $(YOSYS_SRC)/passes/techmap/Makefile.inc diff --git a/passes/opt/Makefile.inc b/passes/opt/Makefile.inc index 76bf8a84e..312149301 100644 --- a/passes/opt/Makefile.inc +++ b/passes/opt/Makefile.inc @@ -12,14 +12,16 @@ OBJS += passes/opt/opt_share.o OBJS += passes/opt/opt_clean.o OBJS += passes/opt/opt_expr.o +OBJS += passes/opt/opt_balance_tree.o +OBJS += passes/opt/muxpack.o +OBJS += passes/opt/wreduce.o + ifneq ($(SMALL),1) OBJS += passes/opt/share.o -OBJS += passes/opt/wreduce.o OBJS += passes/opt/opt_demorgan.o OBJS += passes/opt/rmports.o OBJS += passes/opt/opt_lut.o OBJS += passes/opt/opt_lut_ins.o OBJS += passes/opt/opt_ffinv.o OBJS += passes/opt/pmux2shiftx.o -OBJS += passes/opt/muxpack.o endif diff --git a/passes/opt/opt_balance_tree.cc b/passes/opt/opt_balance_tree.cc new file mode 100644 index 000000000..2edd5d5d9 --- /dev/null +++ b/passes/opt/opt_balance_tree.cc @@ -0,0 +1,276 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Claire Xenia Wolf + * 2019 Eddie Hung + * 2024 Akash Levy + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/yosys.h" +#include "kernel/sigtools.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + + +struct OptBalanceTreeWorker { + Module *module; + SigMap sigmap; + + dict cell_count; + + pool remove_cells; + + dict sig_chain_next; + dict sig_chain_prev; + pool sigbit_with_non_chain_users; + pool chain_start_cells; + pool candidate_cells; + + void make_sig_chain_next_prev(IdString cell_type) { + // Mark all wires with keep attribute as having non-chain users + for (auto wire : module->wires()) { + if (wire->get_bool_attribute(ID::keep)) { + for (auto bit : sigmap(wire)) + sigbit_with_non_chain_users.insert(bit); + } + } + + // Iterate over all cells in module + for (auto cell : module->cells()) { + // If cell matches and not marked as keep + if (cell->type == cell_type && !cell->get_bool_attribute(ID::keep)) { + // Get signals for cell ports + SigSpec a_sig = sigmap(cell->getPort(ID::A)); + SigSpec b_sig = sigmap(cell->getPort(ID::B)); + SigSpec y_sig = sigmap(cell->getPort(ID::Y)); + + // If a_sig already has a chain user, mark its bits as having non-chain users + if (sig_chain_next.count(a_sig) && !a_sig.is_fully_const()) // also ok if a_sig is fully const + for (auto a_bit : a_sig.bits()) + sigbit_with_non_chain_users.insert(a_bit); + // Otherwise, mark cell as the next in the chain relative to a_sig + else { + sig_chain_next[a_sig] = cell; + candidate_cells.insert(cell); + } + + if (!b_sig.empty()) { + // If b_sig already has a chain user, mark its bits as having non-chain users + if (sig_chain_next.count(b_sig) && !b_sig.is_fully_const()) // also ok if b_sig is fully const + for (auto b_bit : b_sig.bits()) + sigbit_with_non_chain_users.insert(b_bit); + // Otherwise, mark cell as the next in the chain relative to b_sig + else { + sig_chain_next[b_sig] = cell; + candidate_cells.insert(cell); + } + } + + // Mark cell as the previous in the chain relative to y_sig + sig_chain_prev[y_sig] = cell; + } + // If cell is not matching type, mark all cell input signals as being non-chain users + else { + for (auto conn : cell->connections()) + if (cell->input(conn.first)) + for (auto bit : sigmap(conn.second)) + sigbit_with_non_chain_users.insert(bit); + } + } + } + + void find_chain_start_cells() { + for (auto cell : candidate_cells) { + // Log candidate cell + log("Considering %s (%s)\n", log_id(cell), log_id(cell->type)); + + // Get signals for cell ports + SigSpec a_sig = sigmap(cell->getPort(ID::A)); + SigSpec b_sig = sigmap(cell->getPort(ID::B)); + SigSpec prev_sig = sig_chain_prev.count(a_sig) ? a_sig : b_sig; + + // This is a start cell if there was no previous cell in the chain for a_sig or b_sig + if (sig_chain_prev.count(a_sig) + sig_chain_prev.count(b_sig) != 1) { + chain_start_cells.insert(cell); + continue; + } + + // If any bits in previous cell signal have non-chain users, this is a start cell + for (auto bit : prev_sig.bits()) + if (sigbit_with_non_chain_users.count(bit)) { + chain_start_cells.insert(cell); + continue; + } + } + } + + vector create_chain(Cell *start_cell) { + // Chain of cells + vector chain; + + // Current cell + Cell *c = start_cell; + + // Iterate over cells and add to chain + while (c != nullptr) { + chain.push_back(c); + + SigSpec y_sig = sigmap(c->getPort(ID::Y)); + + if (sig_chain_next.count(y_sig) == 0) + break; + + c = sig_chain_next.at(y_sig); + if (chain_start_cells.count(c) != 0) + break; + } + + // Return chain of cells + return chain; + } + + void process_chain(vector &chain) { + // If chain size is less than 3, no balancing needed + if (GetSize(chain) < 3) + return; + + // Get mid, midnext (at index mid+1) and end of chain + Cell *mid_cell = chain[GetSize(chain) / 2]; + Cell *midnext_cell = chain[GetSize(chain) / 2 + 1]; + Cell *end_cell = chain.back(); + + // Get mid signals + SigSpec mid_a_sig = sigmap(mid_cell->getPort(ID::A)); + SigSpec mid_b_sig = sigmap(mid_cell->getPort(ID::B)); + SigSpec mid_non_chain_sig = sig_chain_prev.count(mid_a_sig) ? mid_b_sig : mid_a_sig; + IdString mid_non_chain_port = sig_chain_prev.count(mid_a_sig) ? ID::B : ID::A; + + // Get midnext signals + SigSpec midnext_a_sig = sigmap(midnext_cell->getPort(ID::A)); + SigSpec midnext_b_sig = sigmap(midnext_cell->getPort(ID::B)); + IdString midnext_chain_port = sig_chain_next.count(midnext_a_sig) ? ID::A : ID::B; + + // Get output signal + SigSpec end_y_sig = sigmap(end_cell->getPort(ID::Y)); + + // Unset ports involved in rotation + mid_cell->unsetPort(mid_non_chain_port); + mid_cell->unsetPort(ID::Y); + midnext_cell->unsetPort(midnext_chain_port); + end_cell->unsetPort(ID::Y); + + // Perform rotation + mid_cell->setPort(ID::Y, end_y_sig); + midnext_cell->setPort(midnext_chain_port, mid_non_chain_sig); + end_cell->setPort(ID::Y, mid_cell->getPort(mid_non_chain_port)); + + // Recurse on subtrees + vector left_chain(chain.begin(), chain.begin() + GetSize(chain) / 2); + vector right_chain(chain.begin() + GetSize(chain) / 2 + 1, chain.end()); + process_chain(left_chain); + process_chain(right_chain); + } + + void cleanup() { + // Remove cells + for (auto cell : remove_cells) + module->remove(cell); + + // Fix ports + module->fixup_ports(); + + // Clear data structures + remove_cells.clear(); + sig_chain_next.clear(); + sig_chain_prev.clear(); + sigbit_with_non_chain_users.clear(); + chain_start_cells.clear(); + candidate_cells.clear(); + } + + OptBalanceTreeWorker(Module *module, const vector cell_types) : module(module), sigmap(module) { + // Do for each cell type + for (auto cell_type : cell_types) { + // Find chains of ops + make_sig_chain_next_prev(cell_type); + find_chain_start_cells(); + + // For each chain, if len >= 3, convert to tree via "rotation" and recurse on subtrees + for (auto c : chain_start_cells) { + vector chain = create_chain(c); + process_chain(chain); + } + + // Clean up + cleanup(); + } + } +}; + +struct OptBalanceTreePass : public Pass { + OptBalanceTreePass() : Pass("opt_balance_tree", "$and/$or/$xor/$xnor/$add/$mul cascades to trees") { } + void help() override { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" opt_balance_tree [options] [selection]\n"); + log("\n"); + log("This pass converts cascaded chains of $and/$or/$xor/$xnor/$add/$mul cells into\n"); + log("trees of cells to improve timing.\n"); + log("\n"); + log(" -splitfanout\n"); + log(" run splitfanout pass first\n"); + log("\n"); + } + void execute(std::vector args, RTLIL::Design *design) override { + bool splitfanout = false; + + log_header(design, "Executing OPT_BALANCE_TREE pass (cell cascades to trees).\n"); + + // Handle arguments + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-splitfanout") { + splitfanout = true; + continue; + } + break; + } + extra_args(args, argidx, design); + + // Run splitfanout pass first + if (splitfanout) { + string cmd = "splitfanout t:$and t:$or t:$xor t:$add t:$mul"; + Pass::call(design, cmd); + } + + // Count of all cells that were packed + dict cell_count; + const vector cell_types = {ID($and), ID($or), ID($xor), ID($xnor), ID($add), ID($mul)}; + for (auto module : design->selected_modules()) { + OptBalanceTreeWorker worker(module, cell_types); + for (auto cell : worker.cell_count) { + cell_count[cell.first] += cell.second; + } + } + + // Log stats + for (auto cell_type : cell_types) + log("Converted %d %s cells into %s trees.\n", cell_count[cell_type], log_id(cell_type), log_id(cell_type.str() + "_tree")); + } +} OptBalanceTreePass; + +PRIVATE_NAMESPACE_END