Add flooring division operator
authorXiretza <xiretza@xiretza.xyz>
Tue, 21 Apr 2020 10:51:58 +0000 (12:51 +0200)
committerXiretza <xiretza@xiretza.xyz>
Thu, 28 May 2020 20:59:04 +0000 (22:59 +0200)
The $div and $mod cells use truncating division semantics (rounding
towards 0), as defined by e.g. Verilog. Another rounding mode, flooring
(rounding towards negative infinity), can be used in e.g. VHDL. The
new $divfloor cell provides this flooring division.

This commit also fixes the handling of $div in opt_expr, which was
previously optimized as if it was $divfloor.

19 files changed:
backends/btor/test_cells.sh
backends/smv/test_cells.sh
backends/verilog/verilog_backend.cc
kernel/calc.cc
kernel/celledges.cc
kernel/celltypes.h
kernel/rtlil.cc
kernel/rtlil.h
kernel/satgen.h
manual/PRESENTATION_Prog.tex
passes/cmds/stat.cc
passes/memory/memory_share.cc
passes/opt/opt_expr.cc
passes/opt/opt_share.cc
passes/opt/share.cc
passes/opt/wreduce.cc
passes/tests/test_cell.cc
techlibs/common/simlib.v
techlibs/common/techmap.v

index e8d35acf8b1a2c78a0929a8d713f3b955bbf8eb4..3f077201a0e1951434f38e4e93a66b485f5e3a40 100644 (file)
@@ -6,7 +6,7 @@ rm -rf test_cells.tmp
 mkdir -p test_cells.tmp
 cd test_cells.tmp
 
-../../../yosys -p 'test_cell -n 5 -w test all /$alu /$fa /$lcu /$lut /$sop /$macc /$mul /$div /$mod /$modfloor'
+../../../yosys -p 'test_cell -n 5 -w test all /$alu /$fa /$lcu /$lut /$sop /$macc /$mul /$div /$mod /$divfloor /$modfloor'
 
 for fn in test_*.il; do
        ../../../yosys -p "
index ae832ce0080bce0e9ab5b2fede69589f2a7d2881..145b9c33b92fc7a315ba0204a4016fd2d5220af2 100644 (file)
@@ -7,8 +7,8 @@ mkdir -p test_cells.tmp
 cd test_cells.tmp
 
 # don't test $mul to reduce runtime
-# don't test $div/$mod/$modfloor to reduce runtime and avoid "div by zero" message
-../../../yosys -p 'test_cell -n 5 -w test all /$alu /$fa /$lcu /$lut /$macc /$mul /$div /$mod /$modfloor'
+# don't test $div/$mod/$divfloor/$modfloor to reduce runtime and avoid "div by zero" message
+../../../yosys -p 'test_cell -n 5 -w test all /$alu /$fa /$lcu /$lut /$macc /$mul /$div /$mod /$divfloor /$modfloor'
 
 cat > template.txt << "EOT"
 %module main
index 368b76793b075ee7b3f1437cbfd5ac17dd17c58a..4f44a053a80ed19b935d0cbef0a246ce5df06aec 100644 (file)
@@ -740,6 +740,61 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
 #undef HANDLE_UNIOP
 #undef HANDLE_BINOP
 
+       if (cell->type == ID($divfloor))
+       {
+               // wire [MAXLEN+1:0] _0_, _1_, _2_;
+               // assign _0_ = $signed(A);
+               // assign _1_ = $signed(B);
+               // assign _2_ = (A[-1] == B[-1]) || A == 0 ? _0_ : $signed(_0_ - (B[-1] ? _1_ + 1 : _1_ - 1));
+               // assign Y = $signed(_2_) / $signed(_1_);
+
+               if (cell->getParam(ID::A_SIGNED).as_bool() && cell->getParam(ID::B_SIGNED).as_bool()) {
+                       SigSpec sig_a = cell->getPort(ID::A);
+                       SigSpec sig_b = cell->getPort(ID::B);
+
+                       std::string buf_a = next_auto_id();
+                       std::string buf_b = next_auto_id();
+                       std::string buf_num = next_auto_id();
+                       int size_a = GetSize(sig_a);
+                       int size_b = GetSize(sig_b);
+                       int size_y = GetSize(cell->getPort(ID::Y));
+                       int size_max = std::max(size_a, std::max(size_b, size_y));
+
+                       // intentionally one wider than maximum width
+                       f << stringf("%s" "wire [%d:0] %s, %s, %s;\n", indent.c_str(), size_max, buf_a.c_str(), buf_b.c_str(), buf_num.c_str());
+                       f << stringf("%s" "assign %s = ", indent.c_str(), buf_a.c_str());
+                       dump_cell_expr_port(f, cell, "A", true);
+                       f << stringf(";\n");
+                       f << stringf("%s" "assign %s = ", indent.c_str(), buf_b.c_str());
+                       dump_cell_expr_port(f, cell, "B", true);
+                       f << stringf(";\n");
+
+                       f << stringf("%s" "assign %s = ", indent.c_str(), buf_num.c_str());
+                       f << stringf("(");
+                       dump_sigspec(f, sig_a.extract(sig_a.size()-1));
+                       f << stringf(" == ");
+                       dump_sigspec(f, sig_b.extract(sig_b.size()-1));
+                       f << stringf(") || ");
+                       dump_sigspec(f, sig_a);
+                       f << stringf(" == 0 ? %s : ", buf_a.c_str());
+                       f << stringf("$signed(%s - (", buf_a.c_str());
+                       dump_sigspec(f, sig_b.extract(sig_b.size()-1));
+                       f << stringf(" ? %s + 1 : %s - 1));\n", buf_b.c_str(), buf_b.c_str());
+
+
+                       f << stringf("%s" "assign ", indent.c_str());
+                       dump_sigspec(f, cell->getPort(ID::Y));
+                       f << stringf(" = $signed(%s) / ", buf_num.c_str());
+                       dump_attributes(f, "", cell->attributes, ' ');
+                       f << stringf("$signed(%s);\n", buf_b.c_str());
+                       return true;
+               } else {
+                       // same as truncating division
+                       dump_cell_expr_binop(f, indent, cell, "/");
+                       return true;
+               }
+       }
+
        if (cell->type == ID($modfloor))
        {
                // wire truncated = $signed(A) % $signed(B);
index 38a529128eccb320ffcd1ec1b5f1361daf839251..ae18809d3f62cdc20e9858f09475fd3c2c0af497 100644 (file)
@@ -517,6 +517,28 @@ RTLIL::Const RTLIL::const_mod(const RTLIL::Const &arg1, const RTLIL::Const &arg2
        return big2const(result_neg ? -(a % b) : (a % b), result_len >= 0 ? result_len : max(arg1.bits.size(), arg2.bits.size()), min(undef_bit_pos, 0));
 }
 
+RTLIL::Const RTLIL::const_divfloor(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len)
+{
+       int undef_bit_pos = -1;
+       BigInteger a = const2big(arg1, signed1, undef_bit_pos);
+       BigInteger b = const2big(arg2, signed2, undef_bit_pos);
+       if (b.isZero())
+               return RTLIL::Const(RTLIL::State::Sx, result_len);
+
+       bool result_pos = (a.getSign() == BigInteger::negative) == (b.getSign() == BigInteger::negative);
+       a = a.getSign() == BigInteger::negative ? -a : a;
+       b = b.getSign() == BigInteger::negative ? -b : b;
+       BigInteger result;
+
+       if (result_pos || a == 0) {
+               result = a / b;
+       } else {
+               // bigint division with negative numbers is wonky, make sure we only negate at the very end
+               result = -((a + b - 1) / b);
+       }
+       return big2const(result, result_len >= 0 ? result_len : max(arg1.bits.size(), arg2.bits.size()), min(undef_bit_pos, 0));
+}
+
 RTLIL::Const RTLIL::const_modfloor(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len)
 {
        int undef_bit_pos = -1;
index 488ee9d02313d16d316af88ab630e3bcdeac7c23..314e7c77e76c289798be74513f5e5fcb5361c9a9 100644 (file)
@@ -187,7 +187,7 @@ bool YOSYS_NAMESPACE_PREFIX AbstractCellEdgesDatabase::add_edges_from_cell(RTLIL
                return true;
        }
 
-       // FIXME: $mul $div $mod $modfloor $slice $concat
+       // FIXME: $mul $div $mod $divfloor $modfloor $slice $concat
        // FIXME: $lut $sop $alu $lcu $macc $fa
 
        return false;
index 37c251f7ef899aebd09ac1ababec67a09f263bd7..db54436cba6f5b3e23d7120dd221d9634aee1892 100644 (file)
@@ -114,7 +114,7 @@ struct CellTypes
                        ID($and), ID($or), ID($xor), ID($xnor),
                        ID($shl), ID($shr), ID($sshl), ID($sshr), ID($shift), ID($shiftx),
                        ID($lt), ID($le), ID($eq), ID($ne), ID($eqx), ID($nex), ID($ge), ID($gt),
-                       ID($add), ID($sub), ID($mul), ID($div), ID($mod), ID($modfloor), ID($pow),
+                       ID($add), ID($sub), ID($mul), ID($div), ID($mod), ID($divfloor), ID($modfloor), ID($pow),
                        ID($logic_and), ID($logic_or), ID($concat), ID($macc)
                };
 
@@ -304,6 +304,7 @@ struct CellTypes
                HANDLE_CELL_TYPE(mul)
                HANDLE_CELL_TYPE(div)
                HANDLE_CELL_TYPE(mod)
+               HANDLE_CELL_TYPE(divfloor)
                HANDLE_CELL_TYPE(modfloor)
                HANDLE_CELL_TYPE(pow)
                HANDLE_CELL_TYPE(pos)
index f2480ba5a33e1280e54075b613188a5f220079b2..ca4201b532b0eace12b6a4608e61dbe298679c29 100644 (file)
@@ -948,7 +948,7 @@ namespace {
                                return;
                        }
 
-                       if (cell->type.in(ID($add), ID($sub), ID($mul), ID($div), ID($mod), ID($modfloor), ID($pow))) {
+                       if (cell->type.in(ID($add), ID($sub), ID($mul), ID($div), ID($mod), ID($divfloor), ID($modfloor), ID($pow))) {
                                param_bool(ID::A_SIGNED);
                                param_bool(ID::B_SIGNED);
                                port(ID::A, param(ID::A_WIDTH));
@@ -1949,6 +1949,7 @@ DEF_METHOD(Sub,      max(sig_a.size(), sig_b.size()), ID($sub))
 DEF_METHOD(Mul,      max(sig_a.size(), sig_b.size()), ID($mul))
 DEF_METHOD(Div,      max(sig_a.size(), sig_b.size()), ID($div))
 DEF_METHOD(Mod,      max(sig_a.size(), sig_b.size()), ID($mod))
+DEF_METHOD(DivFloor, max(sig_a.size(), sig_b.size()), ID($divfloor))
 DEF_METHOD(ModFloor, max(sig_a.size(), sig_b.size()), ID($modfloor))
 DEF_METHOD(LogicAnd, 1, ID($logic_and))
 DEF_METHOD(LogicOr,  1, ID($logic_or))
index 6bb7599280144322496859d32ebdb94723d6b80b..1408133262048725e242fa2208a3a6d19564bf96 100644 (file)
@@ -466,6 +466,7 @@ namespace RTLIL
        RTLIL::Const const_sub         (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
        RTLIL::Const const_mul         (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
        RTLIL::Const const_div         (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
+       RTLIL::Const const_divfloor    (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
        RTLIL::Const const_modfloor    (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
        RTLIL::Const const_mod         (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
        RTLIL::Const const_pow         (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
@@ -1205,6 +1206,7 @@ public:
        RTLIL::Cell* addMul (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, const RTLIL::SigSpec &sig_y, bool is_signed = false, const std::string &src = "");
        RTLIL::Cell* addDiv (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, const RTLIL::SigSpec &sig_y, bool is_signed = false, const std::string &src = "");
        RTLIL::Cell* addMod (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, const RTLIL::SigSpec &sig_y, bool is_signed = false, const std::string &src = "");
+       RTLIL::Cell* addDivFloor (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, const RTLIL::SigSpec &sig_y, bool is_signed = false, const std::string &src = "");
        RTLIL::Cell* addModFloor (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, const RTLIL::SigSpec &sig_y, bool is_signed = false, const std::string &src = "");
        RTLIL::Cell* addPow (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, const RTLIL::SigSpec &sig_y, bool a_signed = false, bool b_signed = false, const std::string &src = "");
 
@@ -1305,6 +1307,7 @@ public:
        RTLIL::SigSpec Mul (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, bool is_signed = false, const std::string &src = "");
        RTLIL::SigSpec Div (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, bool is_signed = false, const std::string &src = "");
        RTLIL::SigSpec Mod (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, bool is_signed = false, const std::string &src = "");
+       RTLIL::SigSpec DivFloor (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, bool is_signed = false, const std::string &src = "");
        RTLIL::SigSpec ModFloor (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, bool is_signed = false, const std::string &src = "");
        RTLIL::SigSpec Pow (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, bool a_signed = false, bool b_signed = false, const std::string &src = "");
 
index 1d257aa2cdcd428f43c174b758cce32fcaa919e7..3929a87081f46e1911f3591bb2e12896740d768a 100644 (file)
@@ -279,7 +279,7 @@ struct SatGen
                bool arith_undef_handled = false;
                bool is_arith_compare = cell->type.in(ID($lt), ID($le), ID($ge), ID($gt));
 
-               if (model_undef && (cell->type.in(ID($add), ID($sub), ID($mul), ID($div), ID($mod), ID($modfloor)) || is_arith_compare))
+               if (model_undef && (cell->type.in(ID($add), ID($sub), ID($mul), ID($div), ID($mod), ID($divfloor), ID($modfloor)) || is_arith_compare))
                {
                        std::vector<int> undef_a = importUndefSigSpec(cell->getPort(ID::A), timestep);
                        std::vector<int> undef_b = importUndefSigSpec(cell->getPort(ID::B), timestep);
@@ -293,7 +293,7 @@ struct SatGen
                        int undef_any_b = ez->expression(ezSAT::OpOr, undef_b);
                        int undef_y_bit = ez->OR(undef_any_a, undef_any_b);
 
-                       if (cell->type.in(ID($div), ID($mod), ID($modfloor))) {
+                       if (cell->type.in(ID($div), ID($mod), ID($divfloor), ID($modfloor))) {
                                std::vector<int> b = importSigSpec(cell->getPort(ID::B), timestep);
                                undef_y_bit = ez->OR(undef_y_bit, ez->NOT(ez->expression(ezSAT::OpOr, b)));
                        }
@@ -935,7 +935,7 @@ struct SatGen
                        return true;
                }
 
-               if (cell->type.in(ID($div), ID($mod), ID($modfloor)))
+               if (cell->type.in(ID($div), ID($mod), ID($divfloor), ID($modfloor)))
                {
                        std::vector<int> a = importDefSigSpec(cell->getPort(ID::A), timestep);
                        std::vector<int> b = importDefSigSpec(cell->getPort(ID::B), timestep);
@@ -990,6 +990,19 @@ struct SatGen
                                        ez->assume(ez->vec_eq(y_tmp, y_u));
                        } else if (cell->type == ID($mod)) {
                                ez->assume(ez->vec_eq(y_tmp, modulo_trunc));
+                       } else if (cell->type == ID($divfloor)) {
+                               if (cell->parameters[ID::A_SIGNED].as_bool() && cell->parameters[ID::B_SIGNED].as_bool())
+                                       ez->assume(ez->vec_eq(y_tmp, ez->vec_ite(
+                                               ez->XOR(a.back(), b.back()),
+                                               ez->vec_neg(ez->vec_ite(
+                                                       ez->vec_reduce_or(modulo_trunc),
+                                                       ez->vec_add(y_u, ez->vec_const_unsigned(1, y_u.size())),
+                                                       y_u
+                                               )),
+                                               y_u
+                                       )));
+                               else
+                                       ez->assume(ez->vec_eq(y_tmp, y_u));
                        } else if (cell->type == ID($modfloor)) {
                                ez->assume(ez->vec_eq(y_tmp, ez->vec_ite(floored_eq_trunc, modulo_trunc, ez->vec_add(modulo_trunc, b))));
                        }
@@ -998,7 +1011,7 @@ struct SatGen
                                ez->assume(ez->expression(ezSAT::OpOr, b));
                        } else {
                                std::vector<int> div_zero_result;
-                               if (cell->type == ID($div)) {
+                               if (cell->type.in(ID($div), ID($divfloor))) {
                                        if (cell->parameters[ID::A_SIGNED].as_bool() && cell->parameters[ID::B_SIGNED].as_bool()) {
                                                std::vector<int> all_ones(y.size(), ez->CONST_TRUE);
                                                std::vector<int> only_first_one(y.size(), ez->CONST_FALSE);
index 4652731e93b718cf6dafc775efa8c8b320389805..a9416f82a4aca72908c936e28e6c329756656b62 100644 (file)
@@ -307,7 +307,7 @@ cell name from the internal cell library:
 \begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{6pt}{7pt}\selectfont]
 $not $pos $neg $and $or $xor $xnor $reduce_and $reduce_or $reduce_xor $reduce_xnor
 $reduce_bool $shl $shr $sshl $sshr $lt $le $eq $ne $eqx $nex $ge $gt $add $sub $mul $div $mod
-$modfloor $pow $logic_not $logic_and $logic_or $mux $pmux $slice $concat $lut $assert $sr $dff
+$divfloor $modfloor $pow $logic_not $logic_and $logic_or $mux $pmux $slice $concat $lut $assert $sr $dff
 $dffsr $adff $dlatch $dlatchsr $memrd $memwr $mem $fsm $_NOT_ $_AND_ $_OR_ $_XOR_ $_MUX_ $_SR_NN_
 $_SR_NP_ $_SR_PN_ $_SR_PP_ $_DFF_N_ $_DFF_P_ $_DFF_NN0_ $_DFF_NN1_ $_DFF_NP0_ $_DFF_NP1_ $_DFF_PN0_
 $_DFF_PN1_ $_DFF_PP0_ $_DFF_PP1_ $_DFFSR_NNN_ $_DFFSR_NNP_ $_DFFSR_NPN_ $_DFFSR_NPP_ $_DFFSR_PNN_
index e979685e09ae4bfdc62c08dbf21de4790e28a548..30436d8296be6a791367e81a41228a5f9d17f58b 100644 (file)
@@ -109,7 +109,7 @@ struct statdata_t
                                                ID($lut), ID($and), ID($or), ID($xor), ID($xnor),
                                                ID($shl), ID($shr), ID($sshl), ID($sshr), ID($shift), ID($shiftx),
                                                ID($lt), ID($le), ID($eq), ID($ne), ID($eqx), ID($nex), ID($ge), ID($gt),
-                                               ID($add), ID($sub), ID($mul), ID($div), ID($mod), ID($modfloor), ID($pow), ID($alu))) {
+                                               ID($add), ID($sub), ID($mul), ID($div), ID($mod), ID($divfloor), ID($modfloor), ID($pow), ID($alu))) {
                                        int width_a = cell->hasPort(ID::A) ? GetSize(cell->getPort(ID::A)) : 0;
                                        int width_b = cell->hasPort(ID::B) ? GetSize(cell->getPort(ID::B)) : 0;
                                        int width_y = cell->hasPort(ID::Y) ? GetSize(cell->getPort(ID::Y)) : 0;
index c9ac03a6ad4429664cdda553b902e0495df43ed4..e11958bd68e2f91b66586aef79d552e6902d7ddb 100644 (file)
@@ -716,6 +716,7 @@ struct MemoryShareWorker
                cone_ct.cell_types.erase(ID($mod));
                cone_ct.cell_types.erase(ID($div));
                cone_ct.cell_types.erase(ID($modfloor));
+               cone_ct.cell_types.erase(ID($divfloor));
                cone_ct.cell_types.erase(ID($pow));
                cone_ct.cell_types.erase(ID($shl));
                cone_ct.cell_types.erase(ID($shr));
index 1b33435b15acde115e610b6f2ce04a171d041934..e5b8bda956b9b307a65025add3cfe337a7f8eed1 100644 (file)
@@ -864,7 +864,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
 skip_fine_alu:
 
                if (cell->type.in(ID($reduce_xor), ID($reduce_xnor), ID($shift), ID($shiftx), ID($shl), ID($shr), ID($sshl), ID($sshr),
-                                       ID($lt), ID($le), ID($ge), ID($gt), ID($neg), ID($add), ID($sub), ID($mul), ID($div), ID($mod), ID($modfloor), ID($pow)))
+                                       ID($lt), ID($le), ID($ge), ID($gt), ID($neg), ID($add), ID($sub), ID($mul), ID($div), ID($mod), ID($divfloor), ID($modfloor), ID($pow)))
                {
                        RTLIL::SigSpec sig_a = assign_map(cell->getPort(ID::A));
                        RTLIL::SigSpec sig_b = cell->hasPort(ID::B) ? assign_map(cell->getPort(ID::B)) : RTLIL::SigSpec();
@@ -883,7 +883,7 @@ skip_fine_alu:
                        if (0) {
                found_the_x_bit:
                                cover_list("opt.opt_expr.xbit", "$reduce_xor", "$reduce_xnor", "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx",
-                                               "$lt", "$le", "$ge", "$gt", "$neg", "$add", "$sub", "$mul", "$div", "$mod", "$modfloor", "$pow", cell->type.str());
+                                               "$lt", "$le", "$ge", "$gt", "$neg", "$add", "$sub", "$mul", "$div", "$mod", "$divfloor", "$modfloor", "$pow", cell->type.str());
                                if (cell->type.in(ID($reduce_xor), ID($reduce_xnor), ID($lt), ID($le), ID($ge), ID($gt)))
                                        replace_cell(assign_map, module, cell, "x-bit in input", ID::Y, RTLIL::State::Sx);
                                else
@@ -1469,6 +1469,7 @@ skip_identity:
                FOLD_2ARG_CELL(mul)
                FOLD_2ARG_CELL(div)
                FOLD_2ARG_CELL(mod)
+               FOLD_2ARG_CELL(divfloor)
                FOLD_2ARG_CELL(modfloor)
                FOLD_2ARG_CELL(pow)
 
@@ -1584,7 +1585,7 @@ skip_identity:
                        }
                }
 
-               if (!keepdc && cell->type.in(ID($div), ID($mod), ID($modfloor)))
+               if (!keepdc && cell->type.in(ID($div), ID($mod), ID($divfloor), ID($modfloor)))
                {
                        bool a_signed = cell->parameters[ID::A_SIGNED].as_bool();
                        bool b_signed = cell->parameters[ID::B_SIGNED].as_bool();
@@ -1613,11 +1614,13 @@ skip_identity:
                                for (int i = 1; i < (b_signed ? sig_b.size()-1 : sig_b.size()); i++)
                                        if (b_val == (1 << i))
                                        {
-                                               if (cell->type == ID($div))
+                                               if (cell->type.in(ID($div), ID($divfloor)))
                                                {
                                                        cover("opt.opt_expr.div_shift");
 
-                                                       log_debug("Replacing divide-by-%d cell `%s' in module `%s' with shift-by-%d.\n",
+                                                       bool is_truncating = cell->type == ID($div);
+                                                       log_debug("Replacing %s-divide-by-%d cell `%s' in module `%s' with shift-by-%d.\n",
+                                                                       is_truncating ? "truncating" : "flooring",
                                                                        b_val, cell->name.c_str(), module->name.c_str(), i);
 
                                                        std::vector<RTLIL::SigBit> new_b = RTLIL::SigSpec(i, 6);
@@ -1625,10 +1628,26 @@ skip_identity:
                                                        while (GetSize(new_b) > 1 && new_b.back() == RTLIL::State::S0)
                                                                new_b.pop_back();
 
-                                                       cell->type = ID($shr);
+                                                       cell->type = ID($sshr);
                                                        cell->parameters[ID::B_WIDTH] = GetSize(new_b);
                                                        cell->parameters[ID::B_SIGNED] = false;
                                                        cell->setPort(ID::B, new_b);
+
+                                                       // Truncating division is the same as flooring division, except when
+                                                       // the result is negative and there is a remainder - then trunc = floor + 1
+                                                       if (is_truncating && a_signed) {
+                                                               Wire *flooring = module->addWire(NEW_ID, sig_y.size());
+                                                               cell->setPort(ID::Y, flooring);
+
+                                                               Wire *result_neg = module->addWire(NEW_ID);
+                                                               module->addXor(NEW_ID, sig_a[sig_a.size()-1], sig_b[sig_b.size()-1], result_neg);
+                                                               Wire *rem_nonzero = module->addWire(NEW_ID);
+                                                               module->addReduceOr(NEW_ID, sig_a.extract(0, i), rem_nonzero);
+                                                               Wire *should_add = module->addWire(NEW_ID);
+                                                               module->addAnd(NEW_ID, result_neg, rem_nonzero, should_add);
+                                                               module->addAdd(NEW_ID, flooring, should_add, sig_y);
+                                                       }
+
                                                        cell->check();
                                                }
                                                else if (cell->type.in(ID($mod), ID($modfloor)))
index b08a8172f7a46687c409fe446b997f8bb9413c38..cbace7bac4b9ca8e6ae144e2e6604d47f5446a7e 100644 (file)
@@ -103,7 +103,7 @@ bool cell_supported(RTLIL::Cell *cell)
 
                if (sig_bi.is_fully_const() && sig_ci.is_fully_const() && sig_bi == sig_ci)
                        return true;
-       } else if (cell->type.in(LOGICAL_OPS, SHIFT_OPS, BITWISE_OPS, RELATIONAL_OPS, ID($add), ID($sub), ID($mul), ID($div), ID($mod), ID($modfloor), ID($concat))) {
+       } else if (cell->type.in(LOGICAL_OPS, SHIFT_OPS, BITWISE_OPS, RELATIONAL_OPS, ID($add), ID($sub), ID($mul), ID($div), ID($mod), ID($divfloor), ID($modfloor), ID($concat))) {
                return true;
        }
 
@@ -130,7 +130,7 @@ bool mergeable(RTLIL::Cell *a, RTLIL::Cell *b)
 
 RTLIL::IdString decode_port_semantics(RTLIL::Cell *cell, RTLIL::IdString port_name)
 {
-       if (cell->type.in(ID($lt), ID($le), ID($ge), ID($gt), ID($div), ID($mod), ID($modfloor), ID($concat), SHIFT_OPS) && port_name == ID::B)
+       if (cell->type.in(ID($lt), ID($le), ID($ge), ID($gt), ID($div), ID($mod), ID($divfloor), ID($modfloor), ID($concat), SHIFT_OPS) && port_name == ID::B)
                return port_name;
 
        return "";
index 1c8942df0d9866f4f9be66dca8120985fff2261c..988253edf0df65a55dbeaa755932368677fa37e8 100644 (file)
@@ -376,7 +376,7 @@ struct ShareWorker
                                continue;
                        }
 
-                       if (cell->type.in(ID($mul), ID($div), ID($mod), ID($modfloor))) {
+                       if (cell->type.in(ID($mul), ID($div), ID($mod), ID($divfloor), ID($modfloor))) {
                                if (config.opt_aggressive || cell->parameters.at(ID::Y_WIDTH).as_int() >= 4)
                                        shareable_cells.insert(cell);
                                continue;
@@ -1134,6 +1134,7 @@ struct ShareWorker
                cone_ct.cell_types.erase(ID($mod));
                cone_ct.cell_types.erase(ID($div));
                cone_ct.cell_types.erase(ID($modfloor));
+               cone_ct.cell_types.erase(ID($divfloor));
                cone_ct.cell_types.erase(ID($pow));
                cone_ct.cell_types.erase(ID($shl));
                cone_ct.cell_types.erase(ID($shr));
@@ -1513,6 +1514,7 @@ struct SharePass : public Pass {
                config.generic_bin_ops.insert(ID($sub));
                config.generic_bin_ops.insert(ID($div));
                config.generic_bin_ops.insert(ID($mod));
+               config.generic_bin_ops.insert(ID($divfloor));
                config.generic_bin_ops.insert(ID($modfloor));
                // config.generic_bin_ops.insert(ID($pow));
 
index 17b957d23a236bf4533217a3c0b665a1f08d1e37..f60f2f8a848f69dd8fe468d46f219992e171eadd 100644 (file)
@@ -37,7 +37,7 @@ struct WreduceConfig
                        ID($and), ID($or), ID($xor), ID($xnor),
                        ID($shl), ID($shr), ID($sshl), ID($sshr), ID($shift), ID($shiftx),
                        ID($lt), ID($le), ID($eq), ID($ne), ID($eqx), ID($nex), ID($ge), ID($gt),
-                       ID($add), ID($sub), ID($mul), // ID($div), ID($mod), ID($modfloor), ID($pow),
+                       ID($add), ID($sub), ID($mul), // ID($div), ID($mod), ID($divfloor), ID($modfloor), ID($pow),
                        ID($mux), ID($pmux),
                        ID($dff), ID($adff)
                });
@@ -545,7 +545,7 @@ struct WreducePass : public Pass {
                                        }
                                }
 
-                               if (c->type.in(ID($div), ID($mod), ID($modfloor), ID($pow)))
+                               if (c->type.in(ID($div), ID($mod), ID($divfloor), ID($modfloor), ID($pow)))
                                {
                                        SigSpec A = c->getPort(ID::A);
                                        int original_a_width = GetSize(A);
index bc5ff598ee38ec53ae3d42cb20e136baeca08ef2..c6801007d642b5c8e3a9c893a2b60db85b4b8559 100644 (file)
@@ -264,7 +264,7 @@ static void create_gold_module(RTLIL::Design *design, RTLIL::IdString cell_type,
                cell->setPort(ID::Y, wire);
        }
 
-       if (muxdiv && cell_type.in(ID($div), ID($mod), ID($modfloor))) {
+       if (muxdiv && cell_type.in(ID($div), ID($mod), ID($divfloor), ID($modfloor))) {
                auto b_not_zero = module->ReduceBool(NEW_ID, cell->getPort(ID::B));
                auto div_out = module->addWire(NEW_ID, GetSize(cell->getPort(ID::Y)));
                module->addMux(NEW_ID, RTLIL::SigSpec(0, GetSize(div_out)), div_out, b_not_zero, cell->getPort(ID::Y));
@@ -839,6 +839,7 @@ struct TestCellPass : public Pass {
                cell_types[ID($mul)] = "ABSY";
                cell_types[ID($div)] = "ABSY";
                cell_types[ID($mod)] = "ABSY";
+               cell_types[ID($divfloor)] = "ABSY";
                cell_types[ID($modfloor)] = "ABSY";
                // cell_types[ID($pow)] = "ABsY";
 
index 57fc07caa8045a1852cdf8cf3074ab0169fc4126..125b8e0135b8c1e89d4945056a5bc4347012f399 100644 (file)
@@ -997,6 +997,12 @@ endmodule
 
 // --------------------------------------------------------
 
+//  |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+//-
+//-     $div (A, B, Y)
+//-
+//- Division with truncated result (rounded towards 0).
+//-
 module \$div (A, B, Y);
 
 parameter A_SIGNED = 0;
@@ -1053,6 +1059,43 @@ endmodule
 
 // --------------------------------------------------------
 
+//  |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+//-
+//-     $divfloor (A, B, Y)
+//-
+//- Division with floored result (rounded towards negative infinity).
+//-
+module \$divfloor (A, B, Y);
+
+parameter A_SIGNED = 0;
+parameter B_SIGNED = 0;
+parameter A_WIDTH = 0;
+parameter B_WIDTH = 0;
+parameter Y_WIDTH = 0;
+
+input [A_WIDTH-1:0] A;
+input [B_WIDTH-1:0] B;
+output [Y_WIDTH-1:0] Y;
+
+generate
+       if (A_SIGNED && B_SIGNED) begin:BLOCK1
+               localparam WIDTH =
+                               A_WIDTH >= B_WIDTH && A_WIDTH >= Y_WIDTH ? A_WIDTH :
+                               B_WIDTH >= A_WIDTH && B_WIDTH >= Y_WIDTH ? B_WIDTH : Y_WIDTH;
+               wire [WIDTH:0] A_buf, B_buf, N_buf;
+               assign A_buf = $signed(A);
+               assign B_buf = $signed(B);
+               assign N_buf = (A[A_WIDTH-1] == B[B_WIDTH-1]) || A == 0 ? A_buf : $signed(A_buf - (B[B_WIDTH-1] ? B_buf+1 : B_buf-1));
+               assign Y = $signed(N_buf) / $signed(B_buf);
+       end else begin:BLOCK2
+               assign Y = A / B;
+       end
+endgenerate
+
+endmodule
+
+// --------------------------------------------------------
+
 //  |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
 //-
 //-     $modfloor (A, B, Y)
index 95223180de355b6ce9b1ed53e7d2e54eabf9314d..eafe8d4da43d243458c7ea45c3431a4a6ea75e5e 100644 (file)
@@ -506,6 +506,34 @@ module \$__div_mod_floor (A, B, Y, R);
        assign R = (R_s != 0) && A_SIGNED && B_SIGNED && (A_buf[WIDTH-1] != B_buf[WIDTH-1]) ? $signed(B_buf) + $signed(R_s) : R_s;
 endmodule
 
+(* techmap_celltype = "$divfloor" *)
+module _90_divfloor (A, B, Y);
+       parameter A_SIGNED = 0;
+       parameter B_SIGNED = 0;
+       parameter A_WIDTH = 1;
+       parameter B_WIDTH = 1;
+       parameter Y_WIDTH = 1;
+
+       (* force_downto *)
+       input [A_WIDTH-1:0] A;
+       (* force_downto *)
+       input [B_WIDTH-1:0] B;
+       (* force_downto *)
+       output [Y_WIDTH-1:0] Y;
+
+       \$__div_mod_floor #(
+               .A_SIGNED(A_SIGNED),
+               .B_SIGNED(B_SIGNED),
+               .A_WIDTH(A_WIDTH),
+               .B_WIDTH(B_WIDTH),
+               .Y_WIDTH(Y_WIDTH)
+       ) div_mod (
+               .A(A),
+               .B(B),
+               .Y(Y)
+       );
+endmodule
+
 (* techmap_celltype = "$modfloor" *)
 module _90_modfloor (A, B, Y);
        parameter A_SIGNED = 0;