3
0
Fork 0
mirror of https://github.com/YosysHQ/yosys synced 2025-06-20 12:53:39 +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

@ -1021,6 +1021,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);
parameter A_SIGNED = 0;
@ -1043,6 +1051,46 @@ 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