mirror of
				https://github.com/YosysHQ/yosys
				synced 2025-10-31 03:32:29 +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
				
			
		|  | @ -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…
	
	Add table
		Add a link
		
	
		Reference in a new issue