3
0
Fork 0
mirror of https://github.com/YosysHQ/yosys synced 2025-06-13 01:16:16 +00:00

Add flooring modulo operator

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 $modfloor cell provides this flooring modulo (also known as "remainder"
in several languages, but this name is ambiguous).

This commit also fixes the handling of $mod in opt_expr, which was
previously optimized as if it was $modfloor.
This commit is contained in:
Xiretza 2020-04-08 19:30:47 +02:00
parent 0d99522b3c
commit 17163cf43a
No known key found for this signature in database
GPG key ID: 17B78226F7139993
23 changed files with 280 additions and 37 deletions

View file

@ -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);

View 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'
../../../yosys -p 'test_cell -n 5 -w test all /$alu /$fa /$lcu /$lut /$sop /$macc /$mul /$div /$mod /$modfloor'
for fn in test_*.il; do
../../../yosys -p "

View file

@ -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_))) {

View file

@ -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))) {

View file

@ -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())
{

View 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 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/$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'
cat > template.txt << "EOT"
%module main

View file

@ -740,6 +740,40 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
#undef HANDLE_UNIOP
#undef HANDLE_BINOP
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());