diff options
| author | Xiretza <xiretza@xiretza.xyz> | 2020-04-21 12:51:58 +0200 | 
|---|---|---|
| committer | Xiretza <xiretza@xiretza.xyz> | 2020-05-28 22:59:04 +0200 | 
| commit | edd8ff2c0778d97808869488cc7394151456c4ca (patch) | |
| tree | 797418b87588ae7a69992b7f107dfd5cdfdec08d /backends/verilog | |
| parent | 17163cf43a6b6eec9aac44f6a4463dda54b8ed68 (diff) | |
| download | yosys-edd8ff2c0778d97808869488cc7394151456c4ca.tar.gz yosys-edd8ff2c0778d97808869488cc7394151456c4ca.tar.bz2 yosys-edd8ff2c0778d97808869488cc7394151456c4ca.zip  | |
Add flooring division 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 $divfloor cell provides this flooring division.
This commit also fixes the handling of $div in opt_expr, which was
previously optimized as if it was $divfloor.
Diffstat (limited to 'backends/verilog')
| -rw-r--r-- | backends/verilog/verilog_backend.cc | 55 | 
1 files changed, 55 insertions, 0 deletions
diff --git a/backends/verilog/verilog_backend.cc b/backends/verilog/verilog_backend.cc index 368b76793..4f44a053a 100644 --- a/backends/verilog/verilog_backend.cc +++ b/backends/verilog/verilog_backend.cc @@ -740,6 +740,61 @@ 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);  | 
