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