Added opt_expr support for div/mod by power-of-two
authorClifford Wolf <clifford@clifford.at>
Sun, 29 May 2016 10:17:36 +0000 (12:17 +0200)
committerClifford Wolf <clifford@clifford.at>
Sun, 29 May 2016 10:17:36 +0000 (12:17 +0200)
passes/opt/opt_expr.cc
tests/simple/constmuldivmod.v [new file with mode: 0644]

index a18ebb901763b3ef542ce9663bf886ff4d5c0b0a..b62eae285c6b662e0335470cdd2f0c86fe02e4e2 100644 (file)
@@ -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 (file)
index 0000000..d1d8be8
--- /dev/null
@@ -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