diff --git a/passes/techmap/Makefile.inc b/passes/techmap/Makefile.inc index 91b3b563a..32912d979 100644 --- a/passes/techmap/Makefile.inc +++ b/passes/techmap/Makefile.inc @@ -29,6 +29,7 @@ OBJS += passes/techmap/extract.o OBJS += passes/techmap/extract_fa.o OBJS += passes/techmap/extract_counter.o OBJS += passes/techmap/extract_reduce.o +OBJS += passes/techmap/breakreduce.o OBJS += passes/techmap/alumacc.o OBJS += passes/techmap/dffinit.o OBJS += passes/techmap/pmuxtree.o diff --git a/passes/techmap/breakreduce.cc b/passes/techmap/breakreduce.cc new file mode 100644 index 000000000..8838dce11 --- /dev/null +++ b/passes/techmap/breakreduce.cc @@ -0,0 +1,241 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Claire Xenia Wolf + * Copyright (C) 2025 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 + +static void logic_reduce(RTLIL::Module *module, RTLIL::SigSpec &sig, RTLIL::Cell *cell) +{ + while (sig.size() > 1) + { + RTLIL::SigSpec sig_t = module->addWire(NEW_ID_SUFFIX("t"), sig.size() / 2); + + for (int i = 0; i < sig.size(); i += 2) + { + if (i+1 == sig.size()) { + sig_t.append(sig[i]); + continue; + } + + RTLIL::Cell *gate = module->addCell(NEW_ID, ID($or)); + gate->attributes = cell->attributes; + gate->setPort(ID::A, sig[i]); + gate->setPort(ID::B, sig[i+1]); + gate->setPort(ID::Y, sig_t[i/2]); + gate->fixup_parameters(); + } + + sig = sig_t; + } + + if (sig.size() == 0) + sig = State::S0; +} + +void breakreduce(RTLIL::Module *module, RTLIL::Cell *cell) +{ + RTLIL::SigSpec sig_a = cell->getPort(ID::A); + RTLIL::SigSpec sig_y = cell->getPort(ID::Y); + + if (sig_y.size() == 0) + return; + + if (sig_a.size() == 0) { + if (cell->type == ID($reduce_and)) module->connect(RTLIL::SigSig(sig_y, RTLIL::SigSpec(1, sig_y.size()))); + if (cell->type == ID($reduce_or)) module->connect(RTLIL::SigSig(sig_y, RTLIL::SigSpec(0, sig_y.size()))); + if (cell->type == ID($reduce_xor)) module->connect(RTLIL::SigSig(sig_y, RTLIL::SigSpec(0, sig_y.size()))); + if (cell->type == ID($reduce_xnor)) module->connect(RTLIL::SigSig(sig_y, RTLIL::SigSpec(1, sig_y.size()))); + if (cell->type == ID($reduce_bool)) module->connect(RTLIL::SigSig(sig_y, RTLIL::SigSpec(0, sig_y.size()))); + return; + } + + if (sig_y.size() > 1) { + module->connect(RTLIL::SigSig(sig_y.extract(1, sig_y.size()-1), RTLIL::SigSpec(0, sig_y.size()-1))); + sig_y = sig_y.extract(0, 1); + } + + IdString gate_type; + if (cell->type == ID($reduce_and)) gate_type = ID($and); + if (cell->type == ID($reduce_or)) gate_type = ID($or); + if (cell->type == ID($reduce_xor)) gate_type = ID($xor); + if (cell->type == ID($reduce_xnor)) gate_type = ID($xor); + if (cell->type == ID($reduce_bool)) gate_type = ID($or); + log_assert(!gate_type.empty()); + + RTLIL::Cell *last_output_cell = NULL; + + while (sig_a.size() > 1) + { + RTLIL::SigSpec sig_t = module->addWire(NEW_ID_SUFFIX("t"), sig_a.size() / 2); + + for (int i = 0; i < sig_a.size(); i += 2) + { + if (i+1 == sig_a.size()) { + sig_t.append(sig_a[i]); + continue; + } + + RTLIL::Cell *gate = module->addCell(NEW_ID, gate_type); + gate->attributes = cell->attributes; + gate->setPort(ID::A, sig_a[i]); + gate->setPort(ID::B, sig_a[i+1]); + gate->setPort(ID::Y, sig_t[i/2]); + gate->fixup_parameters(); + last_output_cell = gate; + } + + sig_a = sig_t; + } + + if (cell->type == ID($reduce_xnor)) { + RTLIL::SigSpec sig_t = module->addWire(NEW_ID_SUFFIX("t")); + RTLIL::Cell *gate = module->addCell(NEW_ID, ID($not)); + gate->attributes = cell->attributes; + gate->setPort(ID::A, sig_a); + gate->setPort(ID::Y, sig_t); + gate->fixup_parameters(); + last_output_cell = gate; + sig_a = sig_t; + } + + if (last_output_cell == NULL) { + module->connect(RTLIL::SigSig(sig_y, sig_a)); + } else { + last_output_cell->setPort(ID::Y, sig_y); + } + + module->remove(cell); +} + +void breaklognot(RTLIL::Module *module, RTLIL::Cell *cell) +{ + RTLIL::SigSpec sig_a = cell->getPort(ID::A); + logic_reduce(module, sig_a, cell); + + RTLIL::SigSpec sig_y = cell->getPort(ID::Y); + + if (sig_y.size() == 0) + return; + + if (sig_y.size() > 1) { + module->connect(RTLIL::SigSig(sig_y.extract(1, sig_y.size()-1), RTLIL::SigSpec(0, sig_y.size()-1))); + sig_y = sig_y.extract(0, 1); + } + + RTLIL::Cell *gate = module->addCell(NEW_ID, ID($not)); + gate->attributes = cell->attributes; + gate->setPort(ID::A, sig_a); + gate->setPort(ID::Y, sig_y); + gate->fixup_parameters(); + + module->remove(cell); +} + +void breaklogbin(RTLIL::Module *module, RTLIL::Cell *cell) +{ + RTLIL::SigSpec sig_a = cell->getPort(ID::A); + logic_reduce(module, sig_a, cell); + + RTLIL::SigSpec sig_b = cell->getPort(ID::B); + logic_reduce(module, sig_b, cell); + + RTLIL::SigSpec sig_y = cell->getPort(ID::Y); + + if (sig_y.size() == 0) + return; + + if (sig_y.size() > 1) { + module->connect(RTLIL::SigSig(sig_y.extract(1, sig_y.size()-1), RTLIL::SigSpec(0, sig_y.size()-1))); + sig_y = sig_y.extract(0, 1); + } + + IdString gate_type; + if (cell->type == ID($logic_and)) gate_type = ID($and); + if (cell->type == ID($logic_or)) gate_type = ID($or); + log_assert(!gate_type.empty()); + + RTLIL::Cell *gate = module->addCell(NEW_ID, gate_type); + gate->attributes = cell->attributes; + gate->setPort(ID::A, sig_a); + gate->setPort(ID::B, sig_b); + gate->setPort(ID::Y, sig_y); + gate->fixup_parameters(); + + module->remove(cell); +} + +void breakeqne(RTLIL::Module *module, RTLIL::Cell *cell) +{ + RTLIL::SigSpec sig_a = cell->getPort(ID::A); + RTLIL::SigSpec sig_b = cell->getPort(ID::B); + RTLIL::SigSpec sig_y = cell->getPort(ID::Y); + bool is_signed = cell->parameters.at(ID::A_SIGNED).as_bool(); + bool is_ne = cell->type.in(ID($ne), ID($nex)); + + RTLIL::SigSpec xor_out = module->addWire(NEW_ID_SUFFIX("xor"), max(GetSize(sig_a), GetSize(sig_b))); + RTLIL::Cell *xor_cell = module->addXor(NEW_ID, sig_a, sig_b, xor_out, is_signed, cell->get_src_attribute()); + xor_cell->attributes = cell->attributes; + + RTLIL::SigSpec reduce_out = is_ne ? sig_y : module->addWire(NEW_ID_SUFFIX("reduce_out")); + RTLIL::Cell *reduce_cell = module->addReduceOr(NEW_ID_SUFFIX("reduce_or"), xor_out, reduce_out, false, cell->get_src_attribute()); + reduce_cell->attributes = cell->attributes; + breakreduce(module, reduce_cell); + + if (!is_ne) { + RTLIL::Cell *not_cell = module->addLogicNot(NEW_ID_SUFFIX("not"), reduce_out, sig_y, false, cell->get_src_attribute()); + not_cell->attributes = cell->attributes; + breaklognot(module, not_cell); + } + + module->remove(cell); +} + +struct BreakReducePass : public Pass { + BreakReducePass() : Pass("breakreduce", "break reduce-style cells into trees of primitives") { } + void help() override + { + log("\n"); + log(" breakreduce [selection]\n"); + log("\n"); + log("Break reduce-style ($reduce_*/$logic_*/$*eq*) cells into trees of primitives.\n"); + log("\n"); + } + void execute(std::vector args, RTLIL::Design *design) override + { + log_header(design, "Executing BREAKREDUCE pass (break reduce-style cells into trees of primitives).\n"); + extra_args(args, 1, design); + + for (auto module : design->selected_modules()) + for (auto cell : module->selected_cells()) + if (cell->type.in("$reduce_and", "$reduce_or", "$reduce_xor", "$reduce_xnor", "$reduce_bool")) + breakreduce(module, cell); + else if (cell->type.in("$logic_and", "$logic_or")) + breaklogbin(module, cell); + else if (cell->type.in("$logic_not")) + breaklognot(module, cell); + else if (cell->type.in("$eq", "$ne", "$eqx", "$nex")) + breakeqne(module, cell); + } +} BreakReducePass; + +PRIVATE_NAMESPACE_END diff --git a/tests/techmap/breakreduce.ys b/tests/techmap/breakreduce.ys new file mode 100644 index 000000000..c43e045dd Binary files /dev/null and b/tests/techmap/breakreduce.ys differ