diff --git a/passes/opt/opt_expr.cc b/passes/opt/opt_expr.cc index 62a0ffc48..9967c7753 100644 --- a/passes/opt/opt_expr.cc +++ b/passes/opt/opt_expr.cc @@ -1690,7 +1690,38 @@ skip_identity: else if (inA == inB) ACTION_DO(ID::Y, cell->getPort(ID::A)); } + if (cell->type == ID($pow) && cell->getPort(ID::A).is_fully_const() && !cell->parameters[ID::B_SIGNED].as_bool()) { + SigSpec sig_a = assign_map(cell->getPort(ID::A)); + SigSpec sig_y = assign_map(cell->getPort(ID::Y)); + int y_size = GetSize(sig_y); + int bit_idx; + const auto onehot = sig_a.is_onehot(&bit_idx); + + if (onehot) { + if (bit_idx == 1) { + log_debug("Replacing pow cell `%s' in module `%s' with left-shift\n", + cell->name.c_str(), module->name.c_str()); + cell->type = ID($shl); + cell->parameters[ID::A_WIDTH] = 1; + cell->setPort(ID::A, Const(State::S1, 1)); + } + else { + log_debug("Replacing pow cell `%s' in module `%s' with multiply and left-shift\n", + cell->name.c_str(), module->name.c_str()); + cell->type = ID($mul); + cell->parameters[ID::A_SIGNED] = 0; + cell->setPort(ID::A, Const(bit_idx, cell->parameters[ID::A_WIDTH].as_int())); + + SigSpec y_wire = module->addWire(NEW_ID, y_size); + cell->setPort(ID::Y, y_wire); + + module->addShl(NEW_ID, Const(State::S1, 1), y_wire, sig_y); + } + did_something = true; + goto next_cell; + } + } if (!keepdc && cell->type == ID($mul)) { bool a_signed = cell->parameters[ID::A_SIGNED].as_bool(); diff --git a/tests/opt/opt_pow.ys b/tests/opt/opt_pow.ys new file mode 100644 index 000000000..0fa2f88c7 --- /dev/null +++ b/tests/opt/opt_pow.ys @@ -0,0 +1,89 @@ +# Default power of two + +design -reset + +read_rtlil << EOT +autoidx 3 +attribute \cells_not_processed 1 +attribute \src "<stdin>:1.1-3.10" +module \top + attribute \src "<stdin>:2.17-2.20" + wire width 32 $add$<stdin>:2$1_Y + attribute \src "<stdin>:2.12-2.21" + wire width 32 signed $pow$<stdin>:2$2_Y + attribute \src "<stdin>:1.29-1.30" + wire width 15 input 1 \a + attribute \src "<stdin>:1.51-1.52" + wire width 32 output 2 \b + attribute \src "<stdin>:2.17-2.20" + cell $add $add$<stdin>:2$1 + parameter \A_SIGNED 0 + parameter \A_WIDTH 15 + parameter \B_SIGNED 0 + parameter \B_WIDTH 32 + parameter \Y_WIDTH 32 + connect \A \a + connect \B 2 + connect \Y $add$<stdin>:2$1_Y + end + attribute \src "<stdin>:2.12-2.21" + cell $pow $pow$<stdin>:2$2 + parameter \A_SIGNED 0 + parameter \A_WIDTH 32 + parameter \B_SIGNED 0 + parameter \B_WIDTH 32 + parameter \Y_WIDTH 32 + connect \A 2 + connect \B $add$<stdin>:2$1_Y + connect \Y $pow$<stdin>:2$2_Y + end + connect \b $pow$<stdin>:2$2_Y +end +EOT + +select -assert-count 1 t:$pow +select -assert-none t:$shl +opt_expr +select -assert-none t:$pow +select -assert-count 1 t:$shl + +read_verilog << EOT +module ref(input wire [14:0] a, output wire [31:0] b); +assign b = 1 << (a+2); +endmodule +EOT + +equiv_make top ref equiv +select -assert-any -module equiv t:$equiv +equiv_induct +equiv_status -assert + +# Other power of 2 value + +design -reset + +read_verilog <<EOT +module top(input wire [14:0] a, output wire [31:0] b); +assign b = 128**(a+2); +endmodule +EOT + +# Check the cell counts have changed correctly +select -assert-count 1 t:$pow +select -assert-none t:$shl +select -assert-none t:$mul +opt_expr +select -assert-none t:$pow +select -assert-count 1 t:$shl +select -assert-count 1 t:$mul + +read_verilog <<EOT +module ref(input wire [14:0] a, output wire [31:0] b); +assign b = 1 << (7 * (a+2)); +endmodule +EOT + +equiv_make top ref equiv +select -assert-any -module equiv t:$equiv +equiv_induct +equiv_status -assert