mirror of
https://github.com/YosysHQ/yosys
synced 2025-04-24 01:25:33 +00:00
Merge pull request #1885 from Xiretza/mod-rem-cells
Fix modulo/remainder semantics
This commit is contained in:
commit
94c1035389
26 changed files with 540 additions and 40 deletions
|
@ -266,20 +266,26 @@ struct BtorWorker
|
|||
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;
|
||||
if (cell->type == ID($div)) btor_op = "div";
|
||||
// "rem" = truncating modulo
|
||||
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());
|
||||
|
||||
int width = GetSize(cell->getPort(ID::Y));
|
||||
width = std::max(width, GetSize(cell->getPort(ID::A)));
|
||||
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_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
|
||||
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
|
||||
../../../yosys -p "
|
||||
|
|
|
@ -585,6 +585,7 @@ struct FirrtlWorker
|
|||
firrtl_is_signed = a_signed | b_signed;
|
||||
firrtl_width = a_width;
|
||||
} else if (cell->type == ID($mod)) {
|
||||
// "rem" = truncating modulo
|
||||
primop = "rem";
|
||||
firrtl_width = min(a_width, b_width);
|
||||
} 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($mul)) return export_bvop(cell, "(bvmul A B)");
|
||||
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');
|
||||
// "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)) &&
|
||||
2*GetSize(cell->getPort(ID::A).chunks()) < GetSize(cell->getPort(ID::A))) {
|
||||
|
|
|
@ -358,7 +358,8 @@ struct SmvWorker
|
|||
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 = max(width_y, GetSize(cell->getPort(ID::A)));
|
||||
|
@ -366,7 +367,7 @@ struct SmvWorker
|
|||
string expr_a, expr_b, 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())
|
||||
{
|
||||
|
|
|
@ -7,8 +7,8 @@ mkdir -p test_cells.tmp
|
|||
cd test_cells.tmp
|
||||
|
||||
# don't test $mul to reduce runtime
|
||||
# don't test $div and $mod 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'
|
||||
# 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
|
||||
|
|
|
@ -740,6 +740,95 @@ 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);
|
||||
// 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))
|
||||
{
|
||||
f << stringf("%s" "assign ", indent.c_str());
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue