mirror of https://github.com/YosysHQ/yosys.git
Merge pull request #1885 from Xiretza/mod-rem-cells
Fix modulo/remainder semantics
This commit is contained in:
commit
94c1035389
|
@ -66,6 +66,7 @@ Yosys 0.9 .. Yosys 0.9-dev
|
||||||
- Added "design -delete"
|
- Added "design -delete"
|
||||||
- Added "select -unset"
|
- Added "select -unset"
|
||||||
- Use YosysHQ/abc instead of upstream berkeley-abc/abc
|
- Use YosysHQ/abc instead of upstream berkeley-abc/abc
|
||||||
|
- Added $divfloor and $modfloor cells
|
||||||
|
|
||||||
Yosys 0.8 .. Yosys 0.9
|
Yosys 0.8 .. Yosys 0.9
|
||||||
----------------------
|
----------------------
|
||||||
|
|
|
@ -266,20 +266,26 @@ struct BtorWorker
|
||||||
goto okay;
|
goto okay;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cell->type.in(ID($div), ID($mod)))
|
if (cell->type.in(ID($div), ID($mod), ID($modfloor)))
|
||||||
{
|
{
|
||||||
|
bool a_signed = cell->hasParam(ID::A_SIGNED) ? cell->getParam(ID::A_SIGNED).as_bool() : false;
|
||||||
|
bool b_signed = cell->hasParam(ID::B_SIGNED) ? cell->getParam(ID::B_SIGNED).as_bool() : false;
|
||||||
|
|
||||||
string btor_op;
|
string btor_op;
|
||||||
if (cell->type == ID($div)) btor_op = "div";
|
if (cell->type == ID($div)) btor_op = "div";
|
||||||
|
// "rem" = truncating modulo
|
||||||
if (cell->type == ID($mod)) btor_op = "rem";
|
if (cell->type == ID($mod)) btor_op = "rem";
|
||||||
|
// "mod" = flooring modulo
|
||||||
|
if (cell->type == ID($modfloor)) {
|
||||||
|
// "umod" doesn't exist because it's the same as "urem"
|
||||||
|
btor_op = a_signed || b_signed ? "mod" : "rem";
|
||||||
|
}
|
||||||
log_assert(!btor_op.empty());
|
log_assert(!btor_op.empty());
|
||||||
|
|
||||||
int width = GetSize(cell->getPort(ID::Y));
|
int width = GetSize(cell->getPort(ID::Y));
|
||||||
width = std::max(width, GetSize(cell->getPort(ID::A)));
|
width = std::max(width, GetSize(cell->getPort(ID::A)));
|
||||||
width = std::max(width, GetSize(cell->getPort(ID::B)));
|
width = std::max(width, GetSize(cell->getPort(ID::B)));
|
||||||
|
|
||||||
bool a_signed = cell->hasParam(ID::A_SIGNED) ? cell->getParam(ID::A_SIGNED).as_bool() : false;
|
|
||||||
bool b_signed = cell->hasParam(ID::B_SIGNED) ? cell->getParam(ID::B_SIGNED).as_bool() : false;
|
|
||||||
|
|
||||||
int nid_a = get_sig_nid(cell->getPort(ID::A), width, a_signed);
|
int nid_a = get_sig_nid(cell->getPort(ID::A), width, a_signed);
|
||||||
int nid_b = get_sig_nid(cell->getPort(ID::B), width, b_signed);
|
int nid_b = get_sig_nid(cell->getPort(ID::B), width, b_signed);
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ rm -rf test_cells.tmp
|
||||||
mkdir -p test_cells.tmp
|
mkdir -p test_cells.tmp
|
||||||
cd 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'
|
../../../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
|
for fn in test_*.il; do
|
||||||
../../../yosys -p "
|
../../../yosys -p "
|
||||||
|
|
|
@ -585,6 +585,7 @@ struct FirrtlWorker
|
||||||
firrtl_is_signed = a_signed | b_signed;
|
firrtl_is_signed = a_signed | b_signed;
|
||||||
firrtl_width = a_width;
|
firrtl_width = a_width;
|
||||||
} else if (cell->type == ID($mod)) {
|
} else if (cell->type == ID($mod)) {
|
||||||
|
// "rem" = truncating modulo
|
||||||
primop = "rem";
|
primop = "rem";
|
||||||
firrtl_width = min(a_width, b_width);
|
firrtl_width = min(a_width, b_width);
|
||||||
} else if (cell->type.in(ID($and), ID($_AND_))) {
|
} else if (cell->type.in(ID($and), ID($_AND_))) {
|
||||||
|
|
|
@ -590,7 +590,17 @@ struct Smt2Worker
|
||||||
if (cell->type == ID($sub)) return export_bvop(cell, "(bvsub A B)");
|
if (cell->type == ID($sub)) return export_bvop(cell, "(bvsub A B)");
|
||||||
if (cell->type == ID($mul)) return export_bvop(cell, "(bvmul A B)");
|
if (cell->type == ID($mul)) return export_bvop(cell, "(bvmul A B)");
|
||||||
if (cell->type == ID($div)) return export_bvop(cell, "(bvUdiv A B)", 'd');
|
if (cell->type == ID($div)) return export_bvop(cell, "(bvUdiv A B)", 'd');
|
||||||
|
// "rem" = truncating modulo
|
||||||
if (cell->type == ID($mod)) return export_bvop(cell, "(bvUrem A B)", 'd');
|
if (cell->type == ID($mod)) return export_bvop(cell, "(bvUrem A B)", 'd');
|
||||||
|
// "mod" = flooring modulo
|
||||||
|
if (cell->type == ID($modfloor)) {
|
||||||
|
// bvumod doesn't exist because it's the same as bvurem
|
||||||
|
if (cell->getParam(ID::A_SIGNED).as_bool()) {
|
||||||
|
return export_bvop(cell, "(bvsmod A B)", 'd');
|
||||||
|
} else {
|
||||||
|
return export_bvop(cell, "(bvurem A B)", 'd');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (cell->type.in(ID($reduce_and), ID($reduce_or), ID($reduce_bool)) &&
|
if (cell->type.in(ID($reduce_and), ID($reduce_or), ID($reduce_bool)) &&
|
||||||
2*GetSize(cell->getPort(ID::A).chunks()) < GetSize(cell->getPort(ID::A))) {
|
2*GetSize(cell->getPort(ID::A).chunks()) < GetSize(cell->getPort(ID::A))) {
|
||||||
|
|
|
@ -358,7 +358,8 @@ struct SmvWorker
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cell->type.in(ID($div), ID($mod)))
|
// SMV has a "mod" operator, but its semantics don't seem to be well-defined - to be safe, don't generate it at all
|
||||||
|
if (cell->type.in(ID($div)/*, ID($mod), ID($modfloor)*/))
|
||||||
{
|
{
|
||||||
int width_y = GetSize(cell->getPort(ID::Y));
|
int width_y = GetSize(cell->getPort(ID::Y));
|
||||||
int width = max(width_y, GetSize(cell->getPort(ID::A)));
|
int width = max(width_y, GetSize(cell->getPort(ID::A)));
|
||||||
|
@ -366,7 +367,7 @@ struct SmvWorker
|
||||||
string expr_a, expr_b, op;
|
string expr_a, expr_b, op;
|
||||||
|
|
||||||
if (cell->type == ID($div)) op = "/";
|
if (cell->type == ID($div)) op = "/";
|
||||||
if (cell->type == ID($mod)) op = "mod";
|
//if (cell->type == ID($mod)) op = "mod";
|
||||||
|
|
||||||
if (cell->getParam(ID::A_SIGNED).as_bool())
|
if (cell->getParam(ID::A_SIGNED).as_bool())
|
||||||
{
|
{
|
||||||
|
|
|
@ -7,8 +7,8 @@ mkdir -p test_cells.tmp
|
||||||
cd test_cells.tmp
|
cd test_cells.tmp
|
||||||
|
|
||||||
# don't test $mul to reduce runtime
|
# don't test $mul to reduce runtime
|
||||||
# don't test $div and $mod to reduce runtime and avoid "div by zero" message
|
# 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'
|
../../../yosys -p 'test_cell -n 5 -w test all /$alu /$fa /$lcu /$lut /$macc /$mul /$div /$mod /$divfloor /$modfloor'
|
||||||
|
|
||||||
cat > template.txt << "EOT"
|
cat > template.txt << "EOT"
|
||||||
%module main
|
%module main
|
||||||
|
|
|
@ -740,6 +740,95 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
|
||||||
#undef HANDLE_UNIOP
|
#undef HANDLE_UNIOP
|
||||||
#undef HANDLE_BINOP
|
#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);
|
||||||
|
// assign Y = (A[-1] == B[-1]) || truncated == 0 ? truncated : $signed(B) + $signed(truncated);
|
||||||
|
|
||||||
|
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 temp_id = next_auto_id();
|
||||||
|
f << stringf("%s" "wire [%d:0] %s = ", indent.c_str(), GetSize(cell->getPort(ID::A))-1, temp_id.c_str());
|
||||||
|
dump_cell_expr_port(f, cell, "A", true);
|
||||||
|
f << stringf(" %% ");
|
||||||
|
dump_attributes(f, "", cell->attributes, ' ');
|
||||||
|
dump_cell_expr_port(f, cell, "B", true);
|
||||||
|
f << stringf(";\n");
|
||||||
|
|
||||||
|
f << stringf("%s" "assign ", indent.c_str());
|
||||||
|
dump_sigspec(f, cell->getPort(ID::Y));
|
||||||
|
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(") || %s == 0 ? %s : ", temp_id.c_str(), temp_id.c_str());
|
||||||
|
dump_cell_expr_port(f, cell, "B", true);
|
||||||
|
f << stringf(" + $signed(%s);\n", temp_id.c_str());
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
// same as truncating modulo
|
||||||
|
dump_cell_expr_binop(f, indent, cell, "%");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (cell->type == ID($shift))
|
if (cell->type == ID($shift))
|
||||||
{
|
{
|
||||||
f << stringf("%s" "assign ", indent.c_str());
|
f << stringf("%s" "assign ", indent.c_str());
|
||||||
|
|
|
@ -489,6 +489,7 @@ RTLIL::Const RTLIL::const_mul(const RTLIL::Const &arg1, const RTLIL::Const &arg2
|
||||||
return big2const(y, result_len >= 0 ? result_len : max(arg1.bits.size(), arg2.bits.size()), min(undef_bit_pos, 0));
|
return big2const(y, result_len >= 0 ? result_len : max(arg1.bits.size(), arg2.bits.size()), min(undef_bit_pos, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// truncating division
|
||||||
RTLIL::Const RTLIL::const_div(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len)
|
RTLIL::Const RTLIL::const_div(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len)
|
||||||
{
|
{
|
||||||
int undef_bit_pos = -1;
|
int undef_bit_pos = -1;
|
||||||
|
@ -502,6 +503,7 @@ RTLIL::Const RTLIL::const_div(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));
|
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// truncating modulo
|
||||||
RTLIL::Const RTLIL::const_mod(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len)
|
RTLIL::Const RTLIL::const_mod(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len)
|
||||||
{
|
{
|
||||||
int undef_bit_pos = -1;
|
int undef_bit_pos = -1;
|
||||||
|
@ -515,6 +517,51 @@ 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));
|
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;
|
||||||
|
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);
|
||||||
|
|
||||||
|
BigInteger::Sign a_sign = a.getSign();
|
||||||
|
BigInteger::Sign b_sign = b.getSign();
|
||||||
|
a = a_sign == BigInteger::negative ? -a : a;
|
||||||
|
b = b_sign == BigInteger::negative ? -b : b;
|
||||||
|
BigInteger truncated = a_sign == BigInteger::negative ? -(a % b) : (a % b);
|
||||||
|
BigInteger modulo;
|
||||||
|
|
||||||
|
if (truncated == 0 || (a_sign == b_sign)) {
|
||||||
|
modulo = truncated;
|
||||||
|
} else {
|
||||||
|
modulo = b_sign == BigInteger::negative ? truncated - b : truncated + b;
|
||||||
|
}
|
||||||
|
return big2const(modulo, result_len >= 0 ? result_len : max(arg1.bits.size(), arg2.bits.size()), min(undef_bit_pos, 0));
|
||||||
|
}
|
||||||
|
|
||||||
RTLIL::Const RTLIL::const_pow(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len)
|
RTLIL::Const RTLIL::const_pow(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len)
|
||||||
{
|
{
|
||||||
int undef_bit_pos = -1;
|
int undef_bit_pos = -1;
|
||||||
|
|
|
@ -187,7 +187,7 @@ bool YOSYS_NAMESPACE_PREFIX AbstractCellEdgesDatabase::add_edges_from_cell(RTLIL
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: $mul $div $mod $slice $concat
|
// FIXME: $mul $div $mod $divfloor $modfloor $slice $concat
|
||||||
// FIXME: $lut $sop $alu $lcu $macc $fa
|
// FIXME: $lut $sop $alu $lcu $macc $fa
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -114,7 +114,7 @@ struct CellTypes
|
||||||
ID($and), ID($or), ID($xor), ID($xnor),
|
ID($and), ID($or), ID($xor), ID($xnor),
|
||||||
ID($shl), ID($shr), ID($sshl), ID($sshr), ID($shift), ID($shiftx),
|
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($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($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)
|
ID($logic_and), ID($logic_or), ID($concat), ID($macc)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -304,6 +304,8 @@ struct CellTypes
|
||||||
HANDLE_CELL_TYPE(mul)
|
HANDLE_CELL_TYPE(mul)
|
||||||
HANDLE_CELL_TYPE(div)
|
HANDLE_CELL_TYPE(div)
|
||||||
HANDLE_CELL_TYPE(mod)
|
HANDLE_CELL_TYPE(mod)
|
||||||
|
HANDLE_CELL_TYPE(divfloor)
|
||||||
|
HANDLE_CELL_TYPE(modfloor)
|
||||||
HANDLE_CELL_TYPE(pow)
|
HANDLE_CELL_TYPE(pow)
|
||||||
HANDLE_CELL_TYPE(pos)
|
HANDLE_CELL_TYPE(pos)
|
||||||
HANDLE_CELL_TYPE(neg)
|
HANDLE_CELL_TYPE(neg)
|
||||||
|
|
|
@ -948,7 +948,7 @@ namespace {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cell->type.in(ID($add), ID($sub), ID($mul), ID($div), ID($mod), 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::A_SIGNED);
|
||||||
param_bool(ID::B_SIGNED);
|
param_bool(ID::B_SIGNED);
|
||||||
port(ID::A, param(ID::A_WIDTH));
|
port(ID::A, param(ID::A_WIDTH));
|
||||||
|
@ -1949,6 +1949,8 @@ 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(Mul, max(sig_a.size(), sig_b.size()), ID($mul))
|
||||||
DEF_METHOD(Div, max(sig_a.size(), sig_b.size()), ID($div))
|
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(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(LogicAnd, 1, ID($logic_and))
|
||||||
DEF_METHOD(LogicOr, 1, ID($logic_or))
|
DEF_METHOD(LogicOr, 1, ID($logic_or))
|
||||||
#undef DEF_METHOD
|
#undef DEF_METHOD
|
||||||
|
|
|
@ -468,6 +468,8 @@ namespace RTLIL
|
||||||
RTLIL::Const const_sub (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
|
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_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_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_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);
|
RTLIL::Const const_pow (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
|
||||||
|
|
||||||
|
@ -1204,8 +1206,12 @@ public:
|
||||||
RTLIL::Cell* addAdd (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* addAdd (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* addSub (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* addSub (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* 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* 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 = "");
|
||||||
|
// truncating division
|
||||||
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* 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 = "");
|
||||||
|
// truncating modulo
|
||||||
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* 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 = "");
|
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 = "");
|
||||||
|
|
||||||
RTLIL::Cell* addLogicNot (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_y, bool is_signed = false, const std::string &src = "");
|
RTLIL::Cell* addLogicNot (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_y, bool is_signed = false, const std::string &src = "");
|
||||||
|
@ -1303,8 +1309,12 @@ public:
|
||||||
RTLIL::SigSpec Add (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, bool is_signed = false, const std::string &src = "");
|
RTLIL::SigSpec Add (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, bool is_signed = false, const std::string &src = "");
|
||||||
RTLIL::SigSpec Sub (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, bool is_signed = false, const std::string &src = "");
|
RTLIL::SigSpec Sub (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, bool is_signed = false, const std::string &src = "");
|
||||||
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 Mul (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, bool is_signed = false, const std::string &src = "");
|
||||||
|
// truncating division
|
||||||
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 Div (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, bool is_signed = false, const std::string &src = "");
|
||||||
|
// truncating modulo
|
||||||
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 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 = "");
|
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 = "");
|
||||||
|
|
||||||
RTLIL::SigSpec LogicNot (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, bool is_signed = false, const std::string &src = "");
|
RTLIL::SigSpec LogicNot (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, bool is_signed = false, const std::string &src = "");
|
||||||
|
|
|
@ -279,7 +279,7 @@ struct SatGen
|
||||||
bool arith_undef_handled = false;
|
bool arith_undef_handled = false;
|
||||||
bool is_arith_compare = cell->type.in(ID($lt), ID($le), ID($ge), ID($gt));
|
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)) || 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_a = importUndefSigSpec(cell->getPort(ID::A), timestep);
|
||||||
std::vector<int> undef_b = importUndefSigSpec(cell->getPort(ID::B), 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_any_b = ez->expression(ezSAT::OpOr, undef_b);
|
||||||
int undef_y_bit = ez->OR(undef_any_a, undef_any_b);
|
int undef_y_bit = ez->OR(undef_any_a, undef_any_b);
|
||||||
|
|
||||||
if (cell->type.in(ID($div), ID($mod))) {
|
if (cell->type.in(ID($div), ID($mod), ID($divfloor), ID($modfloor))) {
|
||||||
std::vector<int> b = importSigSpec(cell->getPort(ID::B), timestep);
|
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)));
|
undef_y_bit = ez->OR(undef_y_bit, ez->NOT(ez->expression(ezSAT::OpOr, b)));
|
||||||
}
|
}
|
||||||
|
@ -935,7 +935,7 @@ struct SatGen
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cell->type.in(ID($div), ID($mod)))
|
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> a = importDefSigSpec(cell->getPort(ID::A), timestep);
|
||||||
std::vector<int> b = importDefSigSpec(cell->getPort(ID::B), timestep);
|
std::vector<int> b = importDefSigSpec(cell->getPort(ID::B), timestep);
|
||||||
|
@ -970,23 +970,48 @@ struct SatGen
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<int> y_tmp = ignore_div_by_zero ? yy : ez->vec_var(y.size());
|
std::vector<int> y_tmp = ignore_div_by_zero ? yy : ez->vec_var(y.size());
|
||||||
|
|
||||||
|
// modulo calculation
|
||||||
|
std::vector<int> modulo_trunc;
|
||||||
|
int floored_eq_trunc;
|
||||||
|
if (cell->parameters[ID::A_SIGNED].as_bool() && cell->parameters[ID::B_SIGNED].as_bool()) {
|
||||||
|
modulo_trunc = ez->vec_ite(a.back(), ez->vec_neg(chain_buf), chain_buf);
|
||||||
|
// floor == trunc when sgn(a) == sgn(b) or trunc == 0
|
||||||
|
floored_eq_trunc = ez->OR(ez->IFF(a.back(), b.back()), ez->NOT(ez->expression(ezSAT::OpOr, modulo_trunc)));
|
||||||
|
} else {
|
||||||
|
modulo_trunc = chain_buf;
|
||||||
|
floored_eq_trunc = ez->CONST_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
if (cell->type == ID($div)) {
|
if (cell->type == ID($div)) {
|
||||||
if (cell->parameters[ID::A_SIGNED].as_bool() && cell->parameters[ID::B_SIGNED].as_bool())
|
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(y_u), y_u)));
|
ez->assume(ez->vec_eq(y_tmp, ez->vec_ite(ez->XOR(a.back(), b.back()), ez->vec_neg(y_u), y_u)));
|
||||||
else
|
else
|
||||||
ez->assume(ez->vec_eq(y_tmp, y_u));
|
ez->assume(ez->vec_eq(y_tmp, y_u));
|
||||||
} else {
|
} 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())
|
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(a.back(), ez->vec_neg(chain_buf), chain_buf)));
|
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
|
else
|
||||||
ez->assume(ez->vec_eq(y_tmp, chain_buf));
|
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))));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ignore_div_by_zero) {
|
if (ignore_div_by_zero) {
|
||||||
ez->assume(ez->expression(ezSAT::OpOr, b));
|
ez->assume(ez->expression(ezSAT::OpOr, b));
|
||||||
} else {
|
} else {
|
||||||
std::vector<int> div_zero_result;
|
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()) {
|
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> all_ones(y.size(), ez->CONST_TRUE);
|
||||||
std::vector<int> only_first_one(y.size(), ez->CONST_FALSE);
|
std::vector<int> only_first_one(y.size(), ez->CONST_FALSE);
|
||||||
|
@ -996,7 +1021,8 @@ struct SatGen
|
||||||
div_zero_result.insert(div_zero_result.end(), cell->getPort(ID::A).size(), ez->CONST_TRUE);
|
div_zero_result.insert(div_zero_result.end(), cell->getPort(ID::A).size(), ez->CONST_TRUE);
|
||||||
div_zero_result.insert(div_zero_result.end(), y.size() - div_zero_result.size(), ez->CONST_FALSE);
|
div_zero_result.insert(div_zero_result.end(), y.size() - div_zero_result.size(), ez->CONST_FALSE);
|
||||||
}
|
}
|
||||||
} else {
|
} else if (cell->type.in(ID($mod), ID($modfloor))) {
|
||||||
|
// a mod 0 = a
|
||||||
int copy_a_bits = min(cell->getPort(ID::A).size(), cell->getPort(ID::B).size());
|
int copy_a_bits = min(cell->getPort(ID::A).size(), cell->getPort(ID::B).size());
|
||||||
div_zero_result.insert(div_zero_result.end(), a.begin(), a.begin() + copy_a_bits);
|
div_zero_result.insert(div_zero_result.end(), a.begin(), a.begin() + copy_a_bits);
|
||||||
if (cell->parameters[ID::A_SIGNED].as_bool() && cell->parameters[ID::B_SIGNED].as_bool())
|
if (cell->parameters[ID::A_SIGNED].as_bool() && cell->parameters[ID::B_SIGNED].as_bool())
|
||||||
|
|
|
@ -139,6 +139,8 @@ Verilog & Cell Type \\
|
||||||
\lstinline[language=Verilog]; Y = A * B; & {\tt \$mul} \\
|
\lstinline[language=Verilog]; Y = A * B; & {\tt \$mul} \\
|
||||||
\lstinline[language=Verilog]; Y = A / B; & {\tt \$div} \\
|
\lstinline[language=Verilog]; Y = A / B; & {\tt \$div} \\
|
||||||
\lstinline[language=Verilog]; Y = A % B; & {\tt \$mod} \\
|
\lstinline[language=Verilog]; Y = A % B; & {\tt \$mod} \\
|
||||||
|
\multicolumn{1}{c}{\tt [N/A]} & {\tt \$divfloor} \\
|
||||||
|
\multicolumn{1}{c}{\tt [N/A]} & {\tt \$modfoor} \\
|
||||||
\lstinline[language=Verilog]; Y = A ** B; & {\tt \$pow} \\
|
\lstinline[language=Verilog]; Y = A ** B; & {\tt \$pow} \\
|
||||||
\end{tabular}
|
\end{tabular}
|
||||||
\caption{Cell types for binary operators with their corresponding Verilog expressions.}
|
\caption{Cell types for binary operators with their corresponding Verilog expressions.}
|
||||||
|
@ -161,6 +163,27 @@ For the binary cells that output a logical value ({\tt \$logic\_and}, {\tt \$log
|
||||||
{\tt \$gt}), when the \B{Y\_WIDTH} parameter is greater than 1, the output is zero-extended,
|
{\tt \$gt}), when the \B{Y\_WIDTH} parameter is greater than 1, the output is zero-extended,
|
||||||
and only the least significant bit varies.
|
and only the least significant bit varies.
|
||||||
|
|
||||||
|
Division and modulo cells are available in two rounding modes. The original {\tt \$div} and {\tt \$mod}
|
||||||
|
cells are based on truncating division, and correspond to the semantics of the verilog {\tt /} and
|
||||||
|
{\tt \%} operators. The {\tt \$divfloor} and {\tt \$modfloor} cells represent flooring division and
|
||||||
|
flooring modulo, the latter of which is also known as ``remainder'' in several languages. See
|
||||||
|
table~\ref{tab:CellLib_divmod} for a side-by-side comparison between the different semantics.
|
||||||
|
|
||||||
|
\begin{table}[h]
|
||||||
|
\hfil
|
||||||
|
\begin{tabular}{lr|rr|rr}
|
||||||
|
\multirow{2}{*}{Division} & \multirow{2}{*}{Result} & \multicolumn{2}{c|}{Truncating} & \multicolumn{2}{c}{Flooring} \\
|
||||||
|
& & {\tt \$div} & {\tt \$mod} & {\tt \$divfloor} & {\tt \$modfloor} \\
|
||||||
|
\hline
|
||||||
|
{\tt -10 / 3} & {\tt -3.3} & {\tt -3} & {\tt -1} & {\tt -4} & {\tt 2} \\
|
||||||
|
{\tt 10 / -3} & {\tt -3.3} & {\tt -3} & {\tt 1} & {\tt -4} & {\tt -2} \\
|
||||||
|
{\tt -10 / -3} & {\tt 3.3} & {\tt 3} & {\tt -1} & {\tt 3} & {\tt -1} \\
|
||||||
|
{\tt 10 / 3} & {\tt 3.3} & {\tt 3} & {\tt 1} & {\tt 3} & {\tt 1} \\
|
||||||
|
\end{tabular}
|
||||||
|
\caption{Comparison between different rounding modes for division and modulo cells.}
|
||||||
|
\label{tab:CellLib_divmod}
|
||||||
|
\end{table}
|
||||||
|
|
||||||
\subsection{Multiplexers}
|
\subsection{Multiplexers}
|
||||||
|
|
||||||
Multiplexers are generated by the Verilog HDL frontend for {\tt
|
Multiplexers are generated by the Verilog HDL frontend for {\tt
|
||||||
|
|
|
@ -307,7 +307,7 @@ cell name from the internal cell library:
|
||||||
\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{6pt}{7pt}\selectfont]
|
\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
|
$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
|
$reduce_bool $shl $shr $sshl $sshr $lt $le $eq $ne $eqx $nex $ge $gt $add $sub $mul $div $mod
|
||||||
$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_
|
$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_
|
$_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_
|
$_DFF_PN1_ $_DFF_PP0_ $_DFF_PP1_ $_DFFSR_NNN_ $_DFFSR_NNP_ $_DFFSR_NPN_ $_DFFSR_NPP_ $_DFFSR_PNN_
|
||||||
|
|
|
@ -109,7 +109,7 @@ struct statdata_t
|
||||||
ID($lut), ID($and), ID($or), ID($xor), ID($xnor),
|
ID($lut), ID($and), ID($or), ID($xor), ID($xnor),
|
||||||
ID($shl), ID($shr), ID($sshl), ID($sshr), ID($shift), ID($shiftx),
|
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($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($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_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_b = cell->hasPort(ID::B) ? GetSize(cell->getPort(ID::B)) : 0;
|
||||||
int width_y = cell->hasPort(ID::Y) ? GetSize(cell->getPort(ID::Y)) : 0;
|
int width_y = cell->hasPort(ID::Y) ? GetSize(cell->getPort(ID::Y)) : 0;
|
||||||
|
|
|
@ -715,6 +715,8 @@ struct MemoryShareWorker
|
||||||
cone_ct.cell_types.erase(ID($mul));
|
cone_ct.cell_types.erase(ID($mul));
|
||||||
cone_ct.cell_types.erase(ID($mod));
|
cone_ct.cell_types.erase(ID($mod));
|
||||||
cone_ct.cell_types.erase(ID($div));
|
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($pow));
|
||||||
cone_ct.cell_types.erase(ID($shl));
|
cone_ct.cell_types.erase(ID($shl));
|
||||||
cone_ct.cell_types.erase(ID($shr));
|
cone_ct.cell_types.erase(ID($shr));
|
||||||
|
|
|
@ -864,7 +864,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
|
||||||
skip_fine_alu:
|
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),
|
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($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_a = assign_map(cell->getPort(ID::A));
|
||||||
RTLIL::SigSpec sig_b = cell->hasPort(ID::B) ? assign_map(cell->getPort(ID::B)) : RTLIL::SigSpec();
|
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) {
|
if (0) {
|
||||||
found_the_x_bit:
|
found_the_x_bit:
|
||||||
cover_list("opt.opt_expr.xbit", "$reduce_xor", "$reduce_xnor", "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx",
|
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", "$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)))
|
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);
|
replace_cell(assign_map, module, cell, "x-bit in input", ID::Y, RTLIL::State::Sx);
|
||||||
else
|
else
|
||||||
|
@ -1469,6 +1469,8 @@ skip_identity:
|
||||||
FOLD_2ARG_CELL(mul)
|
FOLD_2ARG_CELL(mul)
|
||||||
FOLD_2ARG_CELL(div)
|
FOLD_2ARG_CELL(div)
|
||||||
FOLD_2ARG_CELL(mod)
|
FOLD_2ARG_CELL(mod)
|
||||||
|
FOLD_2ARG_CELL(divfloor)
|
||||||
|
FOLD_2ARG_CELL(modfloor)
|
||||||
FOLD_2ARG_CELL(pow)
|
FOLD_2ARG_CELL(pow)
|
||||||
|
|
||||||
FOLD_1ARG_CELL(pos)
|
FOLD_1ARG_CELL(pos)
|
||||||
|
@ -1583,9 +1585,11 @@ skip_identity:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!keepdc && cell->type.in(ID($div), ID($mod)))
|
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();
|
bool b_signed = cell->parameters[ID::B_SIGNED].as_bool();
|
||||||
|
SigSpec sig_a = assign_map(cell->getPort(ID::A));
|
||||||
SigSpec sig_b = assign_map(cell->getPort(ID::B));
|
SigSpec sig_b = assign_map(cell->getPort(ID::B));
|
||||||
SigSpec sig_y = assign_map(cell->getPort(ID::Y));
|
SigSpec sig_y = assign_map(cell->getPort(ID::Y));
|
||||||
|
|
||||||
|
@ -1610,11 +1614,13 @@ skip_identity:
|
||||||
for (int i = 1; i < (b_signed ? sig_b.size()-1 : sig_b.size()); i++)
|
for (int i = 1; i < (b_signed ? sig_b.size()-1 : sig_b.size()); i++)
|
||||||
if (b_val == (1 << 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");
|
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);
|
b_val, cell->name.c_str(), module->name.c_str(), i);
|
||||||
|
|
||||||
std::vector<RTLIL::SigBit> new_b = RTLIL::SigSpec(i, 6);
|
std::vector<RTLIL::SigBit> new_b = RTLIL::SigSpec(i, 6);
|
||||||
|
@ -1622,17 +1628,35 @@ skip_identity:
|
||||||
while (GetSize(new_b) > 1 && new_b.back() == RTLIL::State::S0)
|
while (GetSize(new_b) > 1 && new_b.back() == RTLIL::State::S0)
|
||||||
new_b.pop_back();
|
new_b.pop_back();
|
||||||
|
|
||||||
cell->type = ID($shr);
|
cell->type = ID($sshr);
|
||||||
cell->parameters[ID::B_WIDTH] = GetSize(new_b);
|
cell->parameters[ID::B_WIDTH] = GetSize(new_b);
|
||||||
cell->parameters[ID::B_SIGNED] = false;
|
cell->parameters[ID::B_SIGNED] = false;
|
||||||
cell->setPort(ID::B, new_b);
|
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();
|
cell->check();
|
||||||
}
|
}
|
||||||
else
|
else if (cell->type.in(ID($mod), ID($modfloor)))
|
||||||
{
|
{
|
||||||
cover("opt.opt_expr.mod_mask");
|
cover("opt.opt_expr.mod_mask");
|
||||||
|
|
||||||
log_debug("Replacing modulo-by-%d cell `%s' in module `%s' with bitmask.\n",
|
bool is_truncating = cell->type == ID($mod);
|
||||||
|
log_debug("Replacing %s-modulo-by-%d cell `%s' in module `%s' with bitmask.\n",
|
||||||
|
is_truncating ? "truncating" : "flooring",
|
||||||
b_val, cell->name.c_str(), module->name.c_str());
|
b_val, cell->name.c_str(), module->name.c_str());
|
||||||
|
|
||||||
std::vector<RTLIL::SigBit> new_b = RTLIL::SigSpec(State::S1, i);
|
std::vector<RTLIL::SigBit> new_b = RTLIL::SigSpec(State::S1, i);
|
||||||
|
@ -1643,6 +1667,24 @@ skip_identity:
|
||||||
cell->type = ID($and);
|
cell->type = ID($and);
|
||||||
cell->parameters[ID::B_WIDTH] = GetSize(new_b);
|
cell->parameters[ID::B_WIDTH] = GetSize(new_b);
|
||||||
cell->setPort(ID::B, new_b);
|
cell->setPort(ID::B, new_b);
|
||||||
|
|
||||||
|
// truncating modulo has the same masked bits as flooring modulo, but
|
||||||
|
// the sign bits are those of A (except when R=0)
|
||||||
|
if (is_truncating && a_signed) {
|
||||||
|
Wire *flooring = module->addWire(NEW_ID, sig_y.size());
|
||||||
|
cell->setPort(ID::Y, flooring);
|
||||||
|
SigSpec truncating = SigSpec(flooring).extract(0, i);
|
||||||
|
|
||||||
|
Wire *rem_nonzero = module->addWire(NEW_ID);
|
||||||
|
module->addReduceOr(NEW_ID, truncating, rem_nonzero);
|
||||||
|
SigSpec a_sign = sig_a[sig_a.size()-1];
|
||||||
|
Wire *extend_bit = module->addWire(NEW_ID);
|
||||||
|
module->addAnd(NEW_ID, a_sign, rem_nonzero, extend_bit);
|
||||||
|
|
||||||
|
truncating.append(extend_bit);
|
||||||
|
module->addPos(NEW_ID, truncating, sig_y, true);
|
||||||
|
}
|
||||||
|
|
||||||
cell->check();
|
cell->check();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
if (sig_bi.is_fully_const() && sig_ci.is_fully_const() && sig_bi == sig_ci)
|
||||||
return true;
|
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($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;
|
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)
|
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($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 port_name;
|
||||||
|
|
||||||
return "";
|
return "";
|
||||||
|
|
|
@ -376,7 +376,7 @@ struct ShareWorker
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cell->type.in(ID($mul), ID($div), ID($mod))) {
|
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)
|
if (config.opt_aggressive || cell->parameters.at(ID::Y_WIDTH).as_int() >= 4)
|
||||||
shareable_cells.insert(cell);
|
shareable_cells.insert(cell);
|
||||||
continue;
|
continue;
|
||||||
|
@ -1133,6 +1133,8 @@ struct ShareWorker
|
||||||
cone_ct.cell_types.erase(ID($mul));
|
cone_ct.cell_types.erase(ID($mul));
|
||||||
cone_ct.cell_types.erase(ID($mod));
|
cone_ct.cell_types.erase(ID($mod));
|
||||||
cone_ct.cell_types.erase(ID($div));
|
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($pow));
|
||||||
cone_ct.cell_types.erase(ID($shl));
|
cone_ct.cell_types.erase(ID($shl));
|
||||||
cone_ct.cell_types.erase(ID($shr));
|
cone_ct.cell_types.erase(ID($shr));
|
||||||
|
@ -1512,6 +1514,8 @@ struct SharePass : public Pass {
|
||||||
config.generic_bin_ops.insert(ID($sub));
|
config.generic_bin_ops.insert(ID($sub));
|
||||||
config.generic_bin_ops.insert(ID($div));
|
config.generic_bin_ops.insert(ID($div));
|
||||||
config.generic_bin_ops.insert(ID($mod));
|
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));
|
// config.generic_bin_ops.insert(ID($pow));
|
||||||
|
|
||||||
config.generic_uni_ops.insert(ID($logic_not));
|
config.generic_uni_ops.insert(ID($logic_not));
|
||||||
|
|
|
@ -37,7 +37,7 @@ struct WreduceConfig
|
||||||
ID($and), ID($or), ID($xor), ID($xnor),
|
ID($and), ID($or), ID($xor), ID($xnor),
|
||||||
ID($shl), ID($shr), ID($sshl), ID($sshr), ID($shift), ID($shiftx),
|
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($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($pow),
|
ID($add), ID($sub), ID($mul), // ID($div), ID($mod), ID($divfloor), ID($modfloor), ID($pow),
|
||||||
ID($mux), ID($pmux),
|
ID($mux), ID($pmux),
|
||||||
ID($dff), ID($adff)
|
ID($dff), ID($adff)
|
||||||
});
|
});
|
||||||
|
@ -545,7 +545,7 @@ struct WreducePass : public Pass {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (c->type.in(ID($div), ID($mod), ID($pow)))
|
if (c->type.in(ID($div), ID($mod), ID($divfloor), ID($modfloor), ID($pow)))
|
||||||
{
|
{
|
||||||
SigSpec A = c->getPort(ID::A);
|
SigSpec A = c->getPort(ID::A);
|
||||||
int original_a_width = GetSize(A);
|
int original_a_width = GetSize(A);
|
||||||
|
|
|
@ -264,7 +264,7 @@ static void create_gold_module(RTLIL::Design *design, RTLIL::IdString cell_type,
|
||||||
cell->setPort(ID::Y, wire);
|
cell->setPort(ID::Y, wire);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (muxdiv && cell_type.in(ID($div), ID($mod))) {
|
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 b_not_zero = module->ReduceBool(NEW_ID, cell->getPort(ID::B));
|
||||||
auto div_out = module->addWire(NEW_ID, GetSize(cell->getPort(ID::Y)));
|
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));
|
module->addMux(NEW_ID, RTLIL::SigSpec(0, GetSize(div_out)), div_out, b_not_zero, cell->getPort(ID::Y));
|
||||||
|
@ -839,6 +839,8 @@ struct TestCellPass : public Pass {
|
||||||
cell_types[ID($mul)] = "ABSY";
|
cell_types[ID($mul)] = "ABSY";
|
||||||
cell_types[ID($div)] = "ABSY";
|
cell_types[ID($div)] = "ABSY";
|
||||||
cell_types[ID($mod)] = "ABSY";
|
cell_types[ID($mod)] = "ABSY";
|
||||||
|
cell_types[ID($divfloor)] = "ABSY";
|
||||||
|
cell_types[ID($modfloor)] = "ABSY";
|
||||||
// cell_types[ID($pow)] = "ABsY";
|
// cell_types[ID($pow)] = "ABsY";
|
||||||
|
|
||||||
cell_types[ID($logic_not)] = "ASY";
|
cell_types[ID($logic_not)] = "ASY";
|
||||||
|
|
|
@ -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);
|
module \$div (A, B, Y);
|
||||||
|
|
||||||
parameter A_SIGNED = 0;
|
parameter A_SIGNED = 0;
|
||||||
|
@ -1021,6 +1027,14 @@ endmodule
|
||||||
|
|
||||||
// --------------------------------------------------------
|
// --------------------------------------------------------
|
||||||
|
|
||||||
|
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||||
|
//-
|
||||||
|
//- $mod (A, B, Y)
|
||||||
|
//-
|
||||||
|
//- Modulo/remainder of division with truncated result (rounded towards 0).
|
||||||
|
//-
|
||||||
|
//- Invariant: $div(A, B) * B + $mod(A, B) == A
|
||||||
|
//-
|
||||||
module \$mod (A, B, Y);
|
module \$mod (A, B, Y);
|
||||||
|
|
||||||
parameter A_SIGNED = 0;
|
parameter A_SIGNED = 0;
|
||||||
|
@ -1043,6 +1057,83 @@ endgenerate
|
||||||
|
|
||||||
endmodule
|
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)
|
||||||
|
//-
|
||||||
|
//- Modulo/remainder of division with floored result (rounded towards negative infinity).
|
||||||
|
//-
|
||||||
|
//- Invariant: $divfloor(A, B) * B + $modfloor(A, B) == A
|
||||||
|
//-
|
||||||
|
module \$modfloor (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 = B_WIDTH >= Y_WIDTH ? B_WIDTH : Y_WIDTH;
|
||||||
|
wire [WIDTH-1:0] B_buf, Y_trunc;
|
||||||
|
assign B_buf = $signed(B);
|
||||||
|
assign Y_trunc = $signed(A) % $signed(B);
|
||||||
|
// flooring mod is the same as truncating mod for positive division results (A and B have
|
||||||
|
// the same sign), as well as when there's no remainder.
|
||||||
|
// For all other cases, they behave as `floor - trunc = B`
|
||||||
|
assign Y = (A[A_WIDTH-1] == B[B_WIDTH-1]) || Y_trunc == 0 ? Y_trunc : $signed(B_buf) + $signed(Y_trunc);
|
||||||
|
end else begin:BLOCK2
|
||||||
|
// no difference between truncating and flooring for unsigned
|
||||||
|
assign Y = A % B;
|
||||||
|
end
|
||||||
|
endgenerate
|
||||||
|
|
||||||
|
endmodule
|
||||||
|
|
||||||
// --------------------------------------------------------
|
// --------------------------------------------------------
|
||||||
`ifndef SIMLIB_NOPOW
|
`ifndef SIMLIB_NOPOW
|
||||||
|
|
||||||
|
|
|
@ -364,7 +364,8 @@ module \$__div_mod_u (A, B, Y, R);
|
||||||
end endgenerate
|
end endgenerate
|
||||||
endmodule
|
endmodule
|
||||||
|
|
||||||
module \$__div_mod (A, B, Y, R);
|
// truncating signed division/modulo
|
||||||
|
module \$__div_mod_trunc (A, B, Y, R);
|
||||||
parameter A_SIGNED = 0;
|
parameter A_SIGNED = 0;
|
||||||
parameter B_SIGNED = 0;
|
parameter B_SIGNED = 0;
|
||||||
parameter A_WIDTH = 1;
|
parameter A_WIDTH = 1;
|
||||||
|
@ -420,7 +421,7 @@ module _90_div (A, B, Y);
|
||||||
(* force_downto *)
|
(* force_downto *)
|
||||||
output [Y_WIDTH-1:0] Y;
|
output [Y_WIDTH-1:0] Y;
|
||||||
|
|
||||||
\$__div_mod #(
|
\$__div_mod_trunc #(
|
||||||
.A_SIGNED(A_SIGNED),
|
.A_SIGNED(A_SIGNED),
|
||||||
.B_SIGNED(B_SIGNED),
|
.B_SIGNED(B_SIGNED),
|
||||||
.A_WIDTH(A_WIDTH),
|
.A_WIDTH(A_WIDTH),
|
||||||
|
@ -448,7 +449,107 @@ module _90_mod (A, B, Y);
|
||||||
(* force_downto *)
|
(* force_downto *)
|
||||||
output [Y_WIDTH-1:0] Y;
|
output [Y_WIDTH-1:0] Y;
|
||||||
|
|
||||||
\$__div_mod #(
|
\$__div_mod_trunc #(
|
||||||
|
.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),
|
||||||
|
.R(Y)
|
||||||
|
);
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
// flooring signed division/modulo
|
||||||
|
module \$__div_mod_floor (A, B, Y, R);
|
||||||
|
parameter A_SIGNED = 0;
|
||||||
|
parameter B_SIGNED = 0;
|
||||||
|
parameter A_WIDTH = 1;
|
||||||
|
parameter B_WIDTH = 1;
|
||||||
|
parameter Y_WIDTH = 1;
|
||||||
|
|
||||||
|
localparam WIDTH =
|
||||||
|
A_WIDTH >= B_WIDTH && A_WIDTH >= Y_WIDTH ? A_WIDTH :
|
||||||
|
B_WIDTH >= A_WIDTH && B_WIDTH >= Y_WIDTH ? B_WIDTH : Y_WIDTH;
|
||||||
|
|
||||||
|
input [A_WIDTH-1:0] A;
|
||||||
|
input [B_WIDTH-1:0] B;
|
||||||
|
output [Y_WIDTH-1:0] Y, R;
|
||||||
|
|
||||||
|
wire [WIDTH-1:0] A_buf, B_buf;
|
||||||
|
\$pos #(.A_SIGNED(A_SIGNED), .A_WIDTH(A_WIDTH), .Y_WIDTH(WIDTH)) A_conv (.A(A), .Y(A_buf));
|
||||||
|
\$pos #(.A_SIGNED(B_SIGNED), .A_WIDTH(B_WIDTH), .Y_WIDTH(WIDTH)) B_conv (.A(B), .Y(B_buf));
|
||||||
|
|
||||||
|
wire [WIDTH-1:0] A_buf_u, B_buf_u, Y_u, R_u, R_s;
|
||||||
|
assign A_buf_u = A_SIGNED && A_buf[WIDTH-1] ? -A_buf : A_buf;
|
||||||
|
assign B_buf_u = B_SIGNED && B_buf[WIDTH-1] ? -B_buf : B_buf;
|
||||||
|
|
||||||
|
\$__div_mod_u #(
|
||||||
|
.WIDTH(WIDTH)
|
||||||
|
) div_mod_u (
|
||||||
|
.A(A_buf_u),
|
||||||
|
.B(B_buf_u),
|
||||||
|
.Y(Y_u),
|
||||||
|
.R(R_u)
|
||||||
|
);
|
||||||
|
|
||||||
|
// For negative results, if there was a remainder, subtract one to turn
|
||||||
|
// the round towards 0 into a round towards -inf
|
||||||
|
assign Y = A_SIGNED && B_SIGNED && (A_buf[WIDTH-1] != B_buf[WIDTH-1]) ? (R_u == 0 ? -Y_u : -Y_u-1) : Y_u;
|
||||||
|
|
||||||
|
// truncating modulo
|
||||||
|
assign R_s = A_SIGNED && B_SIGNED && A_buf[WIDTH-1] ? -R_u : R_u;
|
||||||
|
// Flooring modulo differs from truncating modulo only if it is nonzero and
|
||||||
|
// A and B have different signs - then `floor - trunc = B`
|
||||||
|
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;
|
||||||
|
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),
|
.A_SIGNED(A_SIGNED),
|
||||||
.B_SIGNED(B_SIGNED),
|
.B_SIGNED(B_SIGNED),
|
||||||
.A_WIDTH(A_WIDTH),
|
.A_WIDTH(A_WIDTH),
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
module constmuldivmod(input [7:0] A, input [2:0] mode, output reg [7:0] Y);
|
module constmuldivmod(input [7:0] A, input [5:0] mode, output reg [7:0] Y);
|
||||||
always @* begin
|
always @* begin
|
||||||
case (mode)
|
case (mode)
|
||||||
0: Y = A / 8'd0;
|
0: Y = A / 8'd0;
|
||||||
|
@ -21,6 +21,46 @@ module constmuldivmod(input [7:0] A, input [2:0] mode, output reg [7:0] Y);
|
||||||
13: Y = A % 8'd8;
|
13: Y = A % 8'd8;
|
||||||
14: Y = A * 8'd8;
|
14: Y = A * 8'd8;
|
||||||
|
|
||||||
|
15: Y = $signed(A) / $signed(8'd0);
|
||||||
|
16: Y = $signed(A) % $signed(8'd0);
|
||||||
|
17: Y = $signed(A) * $signed(8'd0);
|
||||||
|
|
||||||
|
18: Y = $signed(A) / $signed(8'd1);
|
||||||
|
19: Y = $signed(A) % $signed(8'd1);
|
||||||
|
20: Y = $signed(A) * $signed(8'd1);
|
||||||
|
|
||||||
|
21: Y = $signed(A) / $signed(8'd2);
|
||||||
|
22: Y = $signed(A) % $signed(8'd2);
|
||||||
|
23: Y = $signed(A) * $signed(8'd2);
|
||||||
|
|
||||||
|
24: Y = $signed(A) / $signed(8'd4);
|
||||||
|
25: Y = $signed(A) % $signed(8'd4);
|
||||||
|
26: Y = $signed(A) * $signed(8'd4);
|
||||||
|
|
||||||
|
27: Y = $signed(A) / $signed(8'd8);
|
||||||
|
28: Y = $signed(A) % $signed(8'd8);
|
||||||
|
29: Y = $signed(A) * $signed(8'd8);
|
||||||
|
|
||||||
|
30: Y = $signed(A) / $signed(-8'd0);
|
||||||
|
31: Y = $signed(A) % $signed(-8'd0);
|
||||||
|
32: Y = $signed(A) * $signed(-8'd0);
|
||||||
|
|
||||||
|
33: Y = $signed(A) / $signed(-8'd1);
|
||||||
|
34: Y = $signed(A) % $signed(-8'd1);
|
||||||
|
35: Y = $signed(A) * $signed(-8'd1);
|
||||||
|
|
||||||
|
36: Y = $signed(A) / $signed(-8'd2);
|
||||||
|
37: Y = $signed(A) % $signed(-8'd2);
|
||||||
|
38: Y = $signed(A) * $signed(-8'd2);
|
||||||
|
|
||||||
|
39: Y = $signed(A) / $signed(-8'd4);
|
||||||
|
40: Y = $signed(A) % $signed(-8'd4);
|
||||||
|
41: Y = $signed(A) * $signed(-8'd4);
|
||||||
|
|
||||||
|
42: Y = $signed(A) / $signed(-8'd8);
|
||||||
|
43: Y = $signed(A) % $signed(-8'd8);
|
||||||
|
44: Y = $signed(A) * $signed(-8'd8);
|
||||||
|
|
||||||
default: Y = 8'd16 * A;
|
default: Y = 8'd16 * A;
|
||||||
endcase
|
endcase
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue