diff --git a/passes/silimate/Makefile.inc b/passes/silimate/Makefile.inc index 7f94710b9..9ecc6128b 100644 --- a/passes/silimate/Makefile.inc +++ b/passes/silimate/Makefile.inc @@ -28,6 +28,17 @@ PEEPOPT_PATTERN = passes/silimate/peepopt_expand.pmg passes/silimate/peepopt_expand.h: passes/pmgen/pmgen.py $(PEEPOPT_PATTERN) $(P) mkdir -p $(dir $@) && $(PYTHON_EXECUTABLE) $< -o $@ -p peepopt $(filter-out $<,$^) +OBJS += passes/silimate/opt_shift.o +GENFILES += passes/silimate/peepopt_shift.h +passes/silimate/opt_shift.o: passes/silimate/peepopt_shift.h +$(eval $(call add_extra_objs,passes/silimate/peepopt_shift.h)) + +PEEPOPT_PATTERN = passes/silimate/peepopt_combine_shifts.pmg +PEEPOPT_PATTERN += passes/silimate/peepopt_expand_shifts.pmg + +passes/silimate/peepopt_shift.h: passes/pmgen/pmgen.py $(PEEPOPT_PATTERN) + $(P) mkdir -p $(dir $@) && $(PYTHON_EXECUTABLE) $< -o $@ -p peepopt $(filter-out $<,$^) + OBJS += passes/silimate/muxmode.o GENFILES += passes/silimate/peepopt_muxmode.h passes/silimate/muxmode.o: passes/silimate/peepopt_muxmode.h diff --git a/passes/silimate/opt_shift.cc b/passes/silimate/opt_shift.cc new file mode 100644 index 000000000..dbe6f5d99 --- /dev/null +++ b/passes/silimate/opt_shift.cc @@ -0,0 +1,109 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Claire Xenia Wolf + * 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 + +bool did_something; + +#include "passes/silimate/peepopt_shift.h" + +struct OptShiftPass : public Pass { + OptShiftPass() : Pass("opt_shift", "shift optimizations: combine and expand") { } + void help() override + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" opt_shift [options] [selection]\n"); + log("\n"); + log("This pass performs shift optimizations.\n"); + log("\n"); + log(" -combine\n"); + log(" Combine nested shift operations (works with all\n"); + log(" combinations of $shl/$sshl and $shr/$sshr):\n"); + log(" (a < a <>>/>> b) >>>/>> c ===> a >>>/>> (b + c)\n"); + log(" (a <>>/>> c ===> a <>>/>> b) < a >>>/>> (b - c)\n"); + log(" Result uses the inner shift's type.\n"); + log("\n"); + log(" -expand\n"); + log(" Expand shifts across binary operations:\n"); + log(" (a OP b) << c ===> (a << c) OP (b << c)\n"); + log(" (a OP b) >> c ===> (a >> c) OP (b >> c)\n"); + log(" where OP in {$and, $or, $xor, $add, $sub}\n"); + log("\n"); + log(" -max_iters n\n"); + log(" max number of pass iterations to run.\n"); + log("\n"); + log("If neither -combine nor -expand is given, both are run.\n"); + log("\n"); + } + void execute(std::vector args, RTLIL::Design *design) override + { + log_header(design, "Executing OPT_SHIFT pass (shift optimizations).\n"); + + bool run_combine = false; + bool run_expand = false; + int max_iters = 10000; + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-combine") { + run_combine = true; + continue; + } + if (args[argidx] == "-expand") { + run_expand = true; + continue; + } + if (args[argidx] == "-max_iters" && argidx + 1 < args.size()) { + max_iters = std::stoi(args[++argidx]); + continue; + } + break; + } + extra_args(args, argidx, design); + + if (!run_combine && !run_expand) { + run_combine = true; + run_expand = true; + } + + for (auto module : design->selected_modules()) + { + did_something = true; + for (int i = 0; did_something && i < max_iters; i++) + { + did_something = false; + peepopt_pm pm(module); + pm.setup(module->selected_cells()); + if (run_combine) + pm.run_combine_shifts(); + if (run_expand) + pm.run_expand_shifts(); + } + } + } +} OptShiftPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/silimate/peepopt_combine_shifts.pmg b/passes/silimate/peepopt_combine_shifts.pmg new file mode 100644 index 000000000..3dd347803 --- /dev/null +++ b/passes/silimate/peepopt_combine_shifts.pmg @@ -0,0 +1,91 @@ +pattern combine_shifts +// +// Authored by Akash Levy of Silimate, Inc. under ISC license. +// +// Combine nested shift operations +// +// Same direction: +// (a << b) << c ===> a << (b + c) +// (a >> b) >> c ===> a >> (b + c) +// (a <<< b) <<< c ===> a <<< (b + c) +// (a >>> b) >>> c ===> a >>> (b + c) +// (a << b) <<< c ===> a << (b + c) +// (a <<< b) << c ===> a <<< (b + c) +// (a >> b) >>> c ===> a >> (b + c) +// (a >>> b) >> c ===> a >>> (b + c) +// +// Mixed direction: +// (a <>>/>> c ===> a <>>/>> b) < a >>>/>> (b - c) +// + +state inner_a inner_b inner_y outer_b outer_y + +match inner_shift + select inner_shift->type.in($shl, $shr, $sshl, $sshr) + set inner_a port(inner_shift, \A) + set inner_b port(inner_shift, \B) + set inner_y port(inner_shift, \Y) +endmatch + +code + if (nusers(inner_y) != 2) + reject; +endcode + +match outer_shift + select outer_shift->type.in($shl, $shr, $sshl, $sshr) + index port(outer_shift, \A) === inner_y + set outer_b port(outer_shift, \B) + set outer_y port(outer_shift, \Y) +endmatch + +code inner_a inner_b inner_y outer_b outer_y + bool inner_is_left = inner_shift->type.in($shl, $sshl); + bool outer_is_left = outer_shift->type.in($shl, $sshl); + bool same_direction = (inner_is_left == outer_is_left); + + // Unset all ports + inner_shift->unsetPort(\A); + inner_shift->unsetPort(\B); + inner_shift->unsetPort(\Y); + outer_shift->unsetPort(\A); + outer_shift->unsetPort(\B); + outer_shift->unsetPort(\Y); + + Cell *cell = outer_shift; + + // Create combined shift amount + int combined_width = max(GetSize(inner_b), GetSize(outer_b)) + 1; + Wire *combined_amt = module->addWire(NEW_ID2, combined_width); + + if (same_direction) { + // Same direction: b + c + module->addAdd(NEW_ID2, inner_b, outer_b, combined_amt, + false, cell->get_src_attribute()); + } else { + // Mixed direction: b - c + module->addSub(NEW_ID2, inner_b, outer_b, combined_amt, + false, cell->get_src_attribute()); + } + + // Reuse outer shift with inner shift's type, original data, and combined amount + outer_shift->type = inner_shift->type; + outer_shift->setPort(\A, inner_a); + outer_shift->setPort(\B, combined_amt); + outer_shift->setPort(\Y, outer_y); + outer_shift->setParam(\A_SIGNED, inner_shift->getParam(\A_SIGNED)); + outer_shift->fixup_parameters(); + + // Rename for formal + module->rename(outer_shift, NEW_ID2); + + // Remove inner shift + autoremove(inner_shift); + + log("combine_shifts pattern in %s: inner=%s (%s), outer=%s (%s)\n", + log_id(module), log_id(inner_shift), log_id(inner_shift->type), + log_id(outer_shift), log_id(outer_shift->type)); + did_something = true; + accept; +endcode diff --git a/passes/silimate/peepopt_expand_shifts.pmg b/passes/silimate/peepopt_expand_shifts.pmg new file mode 100644 index 000000000..7c512b5cb --- /dev/null +++ b/passes/silimate/peepopt_expand_shifts.pmg @@ -0,0 +1,93 @@ +pattern expand_shifts +// +// Authored by Akash Levy of Silimate, Inc. under ISC license. +// +// Expand shifts across binary operations +// +// y = (a OP b) << c ===> y = (a << c) OP (b << c) +// y = (a OP b) >> c ===> y = (a >> c) OP (b >> c) +// y = (a OP b) <<< c ===> y = (a <<< c) OP (b <<< c) +// y = (a OP b) >>> c ===> y = (a >>> c) OP (b >>> c) +// +// where OP in {$and, $or, $xor, $add, $sub} +// + +state op_a op_b op_y shift_amt shift_y + +match op_gate + // Select inner binary operation gate + select op_gate->type.in($and, $or, $xor, $add, $sub) + set op_a port(op_gate, \A) + set op_b port(op_gate, \B) + set op_y port(op_gate, \Y) +endmatch + +code + // Fanout of each OP gate Y bit should be 1 (no bit-split) + if (nusers(op_y) != 2) + reject; +endcode + +match shift_gate + // Select shift gate consuming the OP gate's output + select shift_gate->type.in($shl, $shr, $sshl, $sshr) + + // Connection: shift gate's A input is the OP gate's output + index port(shift_gate, \A) === op_y + + set shift_amt port(shift_gate, \B) + set shift_y port(shift_gate, \Y) +endmatch + +code op_a op_b op_y shift_amt shift_y + // Unset all ports + shift_gate->unsetPort(\A); + shift_gate->unsetPort(\B); + shift_gate->unsetPort(\Y); + op_gate->unsetPort(\A); + op_gate->unsetPort(\B); + op_gate->unsetPort(\Y); + + Cell *cell = shift_gate; + + // Create new intermediate wires + Wire *new_op_a = module->addWire(NEW_ID2, GetSize(shift_y)); + Wire *new_op_b = module->addWire(NEW_ID2, GetSize(shift_y)); + + // Create new shift gates: (a SHIFT c) and (b SHIFT c) + Cell *new_shift_a = module->addCell(NEW_ID2, shift_gate->type); + new_shift_a->setPort(\A, op_a); + new_shift_a->setPort(\B, shift_amt); + new_shift_a->setPort(\Y, new_op_a); + new_shift_a->setParam(\A_SIGNED, shift_gate->getParam(\A_SIGNED)); + new_shift_a->setParam(\B_SIGNED, shift_gate->getParam(\B_SIGNED)); + new_shift_a->fixup_parameters(); + new_shift_a->set_src_attribute(cell->get_src_attribute()); + + Cell *new_shift_b = module->addCell(NEW_ID2, shift_gate->type); + new_shift_b->setPort(\A, op_b); + new_shift_b->setPort(\B, shift_amt); + new_shift_b->setPort(\Y, new_op_b); + new_shift_b->setParam(\A_SIGNED, shift_gate->getParam(\A_SIGNED)); + new_shift_b->setParam(\B_SIGNED, shift_gate->getParam(\B_SIGNED)); + new_shift_b->fixup_parameters(); + new_shift_b->set_src_attribute(cell->get_src_attribute()); + + // Update OP gate to take shifted inputs and produce final output + op_gate->setPort(\A, new_op_a); + op_gate->setPort(\B, new_op_b); + op_gate->setPort(\Y, shift_y); + op_gate->fixup_parameters(); + + // Rename OP gate for formal + cell = op_gate; + module->rename(op_gate, NEW_ID2); + + // Remove original shift gate + autoremove(shift_gate); + + // Log, fixup, accept + log("expand_shifts pattern in %s: op=%s (%s), shift=%s (%s)\n", log_id(module), log_id(op_gate), log_id(op_gate->type), log_id(shift_gate), log_id(shift_gate->type)); + did_something = true; + accept; +endcode diff --git a/tests/silimate/opt_combine_shifts.ys b/tests/silimate/opt_combine_shifts.ys new file mode 100644 index 000000000..833580f6f --- /dev/null +++ b/tests/silimate/opt_combine_shifts.ys @@ -0,0 +1,377 @@ +log -header "Same direction SHL: (a << b) << c" +log -push +design -reset +read_verilog <> b) >> c" +log -push +design -reset +read_verilog <> b) >> c; +endmodule +EOF +check -assert +equiv_opt -assert opt_shift -combine +design -load postopt +select -assert-count 1 t:$shr +select -assert-count 1 t:$add +design -reset +log -pop + + + +log -header "Same direction SSHR: (a >>> b) >>> c (signed arithmetic)" +log -push +design -reset +read_verilog <>> b) >>> c; +endmodule +EOF +check -assert +equiv_opt -assert opt_shift -combine +design -load postopt +select -assert-count 1 t:$sshr +select -assert-count 1 t:$add +design -reset +log -pop + + + +log -header "Negative case: fanout from intermediate wire prevents combining" +log -push +design -reset +read_verilog <> c" +log -push +design -reset +read_verilog <> c; +endmodule +EOF +check -assert +opt_shift -combine +select -assert-count 1 t:$shl +select -assert-count 0 t:$shr +select -assert-count 1 t:$sub +design -reset +log -pop + + + +log -header "Mixed direction: (a >> b) << c" +log -push +design -reset +read_verilog <> b) << c; +endmodule +EOF +check -assert +opt_shift -combine +select -assert-count 0 t:$shl +select -assert-count 1 t:$shr +select -assert-count 1 t:$sub +design -reset +log -pop + + + +log -header "Cross-type same direction: $shr then $sshr" +log -push +design -reset +read_rtlil <> c" +log -push +design -reset +read_verilog <> c; +endmodule +EOF +check -assert +equiv_opt -assert opt_shift -expand +design -load postopt +select -assert-count 1 t:$and +select -assert-count 2 t:$shr +design -reset +log -pop + + + +log -header "SHR across XOR: (a ^ b) >> c" +log -push +design -reset +read_verilog <> c; +endmodule +EOF +check -assert +equiv_opt -assert opt_shift -expand +design -load postopt +select -assert-count 1 t:$xor +select -assert-count 2 t:$shr +design -reset +log -pop + + + +log -header "SSHR (arithmetic right shift) across AND: signed (a & b) >>> c" +log -push +design -reset +read_verilog <>> c; +endmodule +EOF +check -assert +equiv_opt -assert opt_shift -expand +design -load postopt +select -assert-count 1 t:$and +select -assert-count 2 t:$sshr +design -reset +log -pop + + + +log -header "Negative case: fanout from intermediate wire prevents expansion" +log -push +design -reset +read_verilog <