aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--passes/opt/opt_expr.cc69
-rw-r--r--tests/simple/constmuldivmod.v27
2 files changed, 96 insertions, 0 deletions
diff --git a/passes/opt/opt_expr.cc b/passes/opt/opt_expr.cc
index a18ebb901..b62eae285 100644
--- a/passes/opt/opt_expr.cc
+++ b/passes/opt/opt_expr.cc
@@ -1098,6 +1098,75 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
}
}
+ if (!keepdc && cell->type.in("$div", "$mod"))
+ {
+ bool b_signed = cell->parameters["\\B_SIGNED"].as_bool();
+ SigSpec sig_b = assign_map(cell->getPort("\\B"));
+ SigSpec sig_y = assign_map(cell->getPort("\\Y"));
+
+ if (sig_b.is_fully_def() && sig_b.size() <= 32)
+ {
+ int b_val = sig_b.as_int();
+
+ if (b_val == 0)
+ {
+ cover("opt.opt_expr.divmod_zero");
+
+ log("Replacing divide-by-zero cell `%s' in module `%s' with undef-driver.\n",
+ cell->name.c_str(), module->name.c_str());
+
+ module->connect(RTLIL::SigSig(sig_y, RTLIL::SigSpec(State::Sx, sig_y.size())));
+ module->remove(cell);
+
+ did_something = true;
+ goto next_cell;
+ }
+
+ for (int i = 1; i < (b_signed ? sig_b.size()-1 : sig_b.size()); i++)
+ if (b_val == (1 << i))
+ {
+ if (cell->type == "$div")
+ {
+ cover("opt.opt_expr.div_shift");
+
+ log("Replacing divide-by-%d cell `%s' in module `%s' with shift-by-%d.\n",
+ b_val, cell->name.c_str(), module->name.c_str(), i);
+
+ std::vector<RTLIL::SigBit> new_b = RTLIL::SigSpec(i, 6);
+
+ while (GetSize(new_b) > 1 && new_b.back() == RTLIL::State::S0)
+ new_b.pop_back();
+
+ cell->type = "$shr";
+ cell->parameters["\\B_WIDTH"] = GetSize(new_b);
+ cell->parameters["\\B_SIGNED"] = false;
+ cell->setPort("\\B", new_b);
+ cell->check();
+ }
+ else
+ {
+ cover("opt.opt_expr.mod_mask");
+
+ log("Replacing modulo-by-%d cell `%s' in module `%s' with bitmask.\n",
+ b_val, cell->name.c_str(), module->name.c_str());
+
+ std::vector<RTLIL::SigBit> new_b = RTLIL::SigSpec(State::S1, i);
+
+ if (b_signed)
+ new_b.push_back(State::S0);
+
+ cell->type = "$and";
+ cell->parameters["\\B_WIDTH"] = GetSize(new_b);
+ cell->setPort("\\B", new_b);
+ cell->check();
+ }
+
+ did_something = true;
+ goto next_cell;
+ }
+ }
+ }
+
next_cell:;
#undef ACTION_DO
#undef ACTION_DO_Y
diff --git a/tests/simple/constmuldivmod.v b/tests/simple/constmuldivmod.v
new file mode 100644
index 000000000..d1d8be862
--- /dev/null
+++ b/tests/simple/constmuldivmod.v
@@ -0,0 +1,27 @@
+module constmuldivmod(input [7:0] A, input [2:0] mode, output reg [7:0] Y);
+ always @* begin
+ case (mode)
+ 0: Y = A / 8'd0;
+ 1: Y = A % 8'd0;
+ 2: Y = A * 8'd0;
+
+ 3: Y = A / 8'd1;
+ 4: Y = A % 8'd1;
+ 5: Y = A * 8'd1;
+
+ 6: Y = A / 8'd2;
+ 7: Y = A % 8'd2;
+ 8: Y = A * 8'd2;
+
+ 9: Y = A / 8'd4;
+ 10: Y = A % 8'd4;
+ 11: Y = A * 8'd4;
+
+ 12: Y = A / 8'd8;
+ 13: Y = A % 8'd8;
+ 14: Y = A * 8'd8;
+
+ default: Y = 8'd16 * A;
+ endcase
+ end
+endmodule