mirror of https://github.com/YosysHQ/yosys.git
Merge pull request #1648 from YosysHQ/eddie/cmp2lcu
"techmap -map +/cmp2lcu.v" for decomposing arithmetic compares to $lcu
This commit is contained in:
commit
d61a6b81fc
|
@ -30,3 +30,4 @@ $(eval $(call add_share_file,share,techlibs/common/cmp2lut.v))
|
||||||
$(eval $(call add_share_file,share,techlibs/common/cells.lib))
|
$(eval $(call add_share_file,share,techlibs/common/cells.lib))
|
||||||
$(eval $(call add_share_file,share,techlibs/common/mul2dsp.v))
|
$(eval $(call add_share_file,share,techlibs/common/mul2dsp.v))
|
||||||
$(eval $(call add_share_file,share,techlibs/common/abc9_model.v))
|
$(eval $(call add_share_file,share,techlibs/common/abc9_model.v))
|
||||||
|
$(eval $(call add_share_file,share,techlibs/common/cmp2lcu.v))
|
||||||
|
|
|
@ -0,0 +1,116 @@
|
||||||
|
// This pass performs an optimisation that decomposes wide arithmetic
|
||||||
|
// comparisons into LUT-size chunks (as guided by the `LUT_WIDTH
|
||||||
|
// macro) connected to a single lookahead-carry-unit $lcu cell,
|
||||||
|
// which is typically mapped to dedicated (and fast) FPGA
|
||||||
|
// carry-chains.
|
||||||
|
(* techmap_celltype = "$lt $le $gt $ge" *)
|
||||||
|
module _80_lcu_cmp_ (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;
|
||||||
|
|
||||||
|
parameter _TECHMAP_CELLTYPE_ = "";
|
||||||
|
|
||||||
|
generate
|
||||||
|
if (_TECHMAP_CELLTYPE_ == "" || `LUT_WIDTH < 2)
|
||||||
|
wire _TECHMAP_FAIL_ = 1;
|
||||||
|
else if (_TECHMAP_CELLTYPE_ == "$lt") begin
|
||||||
|
// Transform $lt into $gt by swapping A and B
|
||||||
|
$gt #(.A_SIGNED(B_SIGNED), .B_SIGNED(A_SIGNED), .A_WIDTH(B_WIDTH), .B_WIDTH(A_WIDTH), .Y_WIDTH(Y_WIDTH)) _TECHMAP_REPLACE_ (.A(B), .B(A), .Y(Y));
|
||||||
|
end
|
||||||
|
else if (_TECHMAP_CELLTYPE_ == "$le") begin
|
||||||
|
// Transform $le into $ge by swapping A and B
|
||||||
|
$ge #(.A_SIGNED(B_SIGNED), .B_SIGNED(A_SIGNED), .A_WIDTH(B_WIDTH), .B_WIDTH(A_WIDTH), .Y_WIDTH(Y_WIDTH)) _TECHMAP_REPLACE_ (.A(B), .B(A), .Y(Y));
|
||||||
|
end
|
||||||
|
else begin
|
||||||
|
// Perform sign extension on A and B
|
||||||
|
localparam WIDTH = A_WIDTH > B_WIDTH ? A_WIDTH : B_WIDTH;
|
||||||
|
wire [WIDTH-1:0] AA = {{(WIDTH-A_WIDTH){A_SIGNED ? A[A_WIDTH-1] : 1'b0}}, A};
|
||||||
|
wire [WIDTH-1:0] BB = {{(WIDTH-B_WIDTH){B_SIGNED ? B[B_WIDTH-1] : 1'b0}}, B};
|
||||||
|
// For $ge operation, start with the assumption that A and B are
|
||||||
|
// equal (propagating this equality if A and B turn out to be so)
|
||||||
|
if (_TECHMAP_CELLTYPE_ == "$ge")
|
||||||
|
localparam CI = 1'b1;
|
||||||
|
else
|
||||||
|
localparam CI = 1'b0;
|
||||||
|
$__CMP2LCU #(.AB_WIDTH(WIDTH), .AB_SIGNED(A_SIGNED && B_SIGNED), .LCU_WIDTH(1), .BUDGET(`LUT_WIDTH), .CI(CI))
|
||||||
|
_TECHMAP_REPLACE_ (.A(AA), .B(BB), .P(1'b1), .G(1'b0), .Y(Y));
|
||||||
|
end
|
||||||
|
endgenerate
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
module $__CMP2LCU (A, B, P, G, Y);
|
||||||
|
|
||||||
|
parameter AB_WIDTH = 0;
|
||||||
|
parameter AB_SIGNED = 0;
|
||||||
|
parameter LCU_WIDTH = 1;
|
||||||
|
parameter BUDGET = 0;
|
||||||
|
parameter CI = 0;
|
||||||
|
|
||||||
|
input [AB_WIDTH-1:0] A; // A from original $gt/$ge
|
||||||
|
input [AB_WIDTH-1:0] B; // B from original $gt/$ge
|
||||||
|
input [LCU_WIDTH-1:0] P; // P of $lcu
|
||||||
|
input [LCU_WIDTH-1:0] G; // G of $lcu
|
||||||
|
output Y;
|
||||||
|
|
||||||
|
parameter [AB_WIDTH-1:0] _TECHMAP_CONSTMSK_A_ = 0;
|
||||||
|
parameter [AB_WIDTH-1:0] _TECHMAP_CONSTMSK_B_ = 0;
|
||||||
|
parameter [LCU_WIDTH-1:0] _TECHMAP_CONSTMSK_P_ = 0;
|
||||||
|
|
||||||
|
generate
|
||||||
|
if (AB_WIDTH == 0) begin
|
||||||
|
wire [LCU_WIDTH-1:0] CO;
|
||||||
|
$lcu #(.WIDTH(LCU_WIDTH)) _TECHMAP_REPLACE_ (.P(P), .G(G), .CI(CI), .CO(CO));
|
||||||
|
assign Y = CO[LCU_WIDTH-1];
|
||||||
|
end
|
||||||
|
else begin
|
||||||
|
if (_TECHMAP_CONSTMSK_A_[AB_WIDTH-1:0] && _TECHMAP_CONSTMSK_B_[AB_WIDTH-1:0])
|
||||||
|
localparam COST = 0;
|
||||||
|
else if (_TECHMAP_CONSTMSK_A_[AB_WIDTH-1:0] || _TECHMAP_CONSTMSK_B_[AB_WIDTH-1:0])
|
||||||
|
localparam COST = 1;
|
||||||
|
else
|
||||||
|
localparam COST = 2;
|
||||||
|
|
||||||
|
if (BUDGET < COST)
|
||||||
|
$__CMP2LCU #(.AB_WIDTH(AB_WIDTH), .AB_SIGNED(AB_SIGNED), .LCU_WIDTH(LCU_WIDTH+1), .BUDGET(`LUT_WIDTH), .CI(CI))
|
||||||
|
_TECHMAP_REPLACE_ (.A(A), .B(B), .P({P, 1'b1}), .G({G, 1'b0}), .Y(Y));
|
||||||
|
else begin
|
||||||
|
wire PP, GG;
|
||||||
|
// Bit-wise equality (xnor) of A and B
|
||||||
|
assign PP = A[AB_WIDTH-1] ^~ B[AB_WIDTH-1];
|
||||||
|
if (AB_SIGNED)
|
||||||
|
assign GG = ~A[AB_WIDTH-1] & B[AB_WIDTH-1];
|
||||||
|
else if (_TECHMAP_CONSTMSK_P_[LCU_WIDTH-1]) // First compare for LUT if P (and G) is constant
|
||||||
|
assign GG = A[AB_WIDTH-1] & ~B[AB_WIDTH-1];
|
||||||
|
else
|
||||||
|
// Priority "encoder" that checks A[i] == 1'b1 && B[i] == 1'b0
|
||||||
|
// from MSB down, deferring to less significant bits if the
|
||||||
|
// MSBs are equal
|
||||||
|
assign GG = P[0] & (A[AB_WIDTH-1] & ~B[AB_WIDTH-1]);
|
||||||
|
if (LCU_WIDTH == 1) begin
|
||||||
|
// Propagate only if all pairs are equal
|
||||||
|
// (inconclusive evidence to say A >= B)
|
||||||
|
wire P_ = P[0] & PP;
|
||||||
|
// Generate if any comparisons call for it
|
||||||
|
wire G_ = G[0] | GG;
|
||||||
|
end
|
||||||
|
else begin
|
||||||
|
// Propagate only if all pairs are equal
|
||||||
|
// (inconclusive evidence to say A >= B)
|
||||||
|
wire [LCU_WIDTH-1:0] P_ = {P[LCU_WIDTH-1:1], P[0] & PP};
|
||||||
|
// Generate if any comparisons call for it
|
||||||
|
wire [LCU_WIDTH-1:0] G_ = {G[LCU_WIDTH-1:1], G[0] | GG};
|
||||||
|
end
|
||||||
|
$__CMP2LCU #(.AB_WIDTH(AB_WIDTH-1), .AB_SIGNED(1'b0), .LCU_WIDTH(LCU_WIDTH), .BUDGET(BUDGET-COST), .CI(CI))
|
||||||
|
_TECHMAP_REPLACE_ (.A(A[AB_WIDTH-2:0]), .B(B[AB_WIDTH-2:0]), .P(P_), .G(G_), .Y(Y));
|
||||||
|
end
|
||||||
|
end
|
||||||
|
endgenerate
|
||||||
|
endmodule
|
|
@ -57,10 +57,6 @@ function automatic [(1 << `LUT_WIDTH)-1:0] gen_lut;
|
||||||
o_bit = (lhs > rhs);
|
o_bit = (lhs > rhs);
|
||||||
if (operation == 3)
|
if (operation == 3)
|
||||||
o_bit = (lhs >= rhs);
|
o_bit = (lhs >= rhs);
|
||||||
if (operation == 4)
|
|
||||||
o_bit = (lhs == rhs);
|
|
||||||
if (operation == 5)
|
|
||||||
o_bit = (lhs != rhs);
|
|
||||||
gen_lut = gen_lut | (o_bit << n);
|
gen_lut = gen_lut | (o_bit << n);
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -75,10 +71,6 @@ generate
|
||||||
localparam operation = 2;
|
localparam operation = 2;
|
||||||
if (_TECHMAP_CELLTYPE_ == "$ge")
|
if (_TECHMAP_CELLTYPE_ == "$ge")
|
||||||
localparam operation = 3;
|
localparam operation = 3;
|
||||||
if (_TECHMAP_CELLTYPE_ == "$eq")
|
|
||||||
localparam operation = 4;
|
|
||||||
if (_TECHMAP_CELLTYPE_ == "$ne")
|
|
||||||
localparam operation = 5;
|
|
||||||
|
|
||||||
if (A_WIDTH > `LUT_WIDTH || B_WIDTH > `LUT_WIDTH || Y_WIDTH != 1)
|
if (A_WIDTH > `LUT_WIDTH || B_WIDTH > `LUT_WIDTH || Y_WIDTH != 1)
|
||||||
wire _TECHMAP_FAIL_ = 1;
|
wire _TECHMAP_FAIL_ = 1;
|
||||||
|
|
|
@ -225,9 +225,9 @@ struct SynthPass : public ScriptPass
|
||||||
run("peepopt");
|
run("peepopt");
|
||||||
run("opt_clean");
|
run("opt_clean");
|
||||||
if (help_mode)
|
if (help_mode)
|
||||||
run("techmap -map +/cmp2lut.v", " (if -lut)");
|
run("techmap -map +/cmp2lut.v -map +/cmp2lcu.v", " (if -lut)");
|
||||||
else
|
else if (lut)
|
||||||
run(stringf("techmap -map +/cmp2lut.v -D LUT_WIDTH=%d", lut));
|
run(stringf("techmap -map +/cmp2lut.v -map +/cmp2lcu.v -D LUT_WIDTH=%d", lut));
|
||||||
if (!noalumacc)
|
if (!noalumacc)
|
||||||
run("alumacc", " (unless -noalumacc)");
|
run("alumacc", " (unless -noalumacc)");
|
||||||
if (!noshare)
|
if (!noshare)
|
||||||
|
|
|
@ -393,8 +393,6 @@ struct SynthXilinxPass : public ScriptPass
|
||||||
run("pmux2shiftx", "(skip if '-nosrl' and '-widemux=0')");
|
run("pmux2shiftx", "(skip if '-nosrl' and '-widemux=0')");
|
||||||
run("clean", " (skip if '-nosrl' and '-widemux=0')");
|
run("clean", " (skip if '-nosrl' and '-widemux=0')");
|
||||||
}
|
}
|
||||||
|
|
||||||
run("techmap -map +/cmp2lut.v -D LUT_WIDTH=" + lut_size_s);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (check_label("map_dsp", "(skip if '-nodsp')")) {
|
if (check_label("map_dsp", "(skip if '-nodsp')")) {
|
||||||
|
@ -460,6 +458,7 @@ struct SynthXilinxPass : public ScriptPass
|
||||||
}
|
}
|
||||||
|
|
||||||
if (check_label("coarse")) {
|
if (check_label("coarse")) {
|
||||||
|
run("techmap -map +/cmp2lut.v -map +/cmp2lcu.v -D LUT_WIDTH=" + lut_size_s);
|
||||||
run("alumacc");
|
run("alumacc");
|
||||||
run("share");
|
run("share");
|
||||||
run("opt");
|
run("opt");
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
read_verilog <<EOT
|
||||||
|
module top(input [12:0] a, b, output gtu, gts, ltu, lts, geu, ges, leu, les);
|
||||||
|
assign gtu = a > b;
|
||||||
|
assign gts = $signed(a) > $signed(b);
|
||||||
|
assign ltu = a < b;
|
||||||
|
assign lts = $signed(a) < $signed(b);
|
||||||
|
assign geu = a >= b;
|
||||||
|
assign ges = $signed(a) >= $signed(b);
|
||||||
|
assign leu = a <= b;
|
||||||
|
assign les = $signed(a) <= $signed(b);
|
||||||
|
endmodule
|
||||||
|
EOT
|
||||||
|
|
||||||
|
equiv_opt -assert techmap -map +/cmp2lcu.v -D LUT_WIDTH=6
|
||||||
|
design -load postopt
|
||||||
|
select -assert-count 8 t:$lcu r:WIDTH=5 %i
|
||||||
|
select -assert-none t:$gt t:$ge t:$lt t:$le
|
||||||
|
|
||||||
|
design -load preopt
|
||||||
|
equiv_opt -assert techmap -map +/cmp2lcu.v -D LUT_WIDTH=4
|
||||||
|
design -load postopt
|
||||||
|
select -assert-count 8 t:$lcu r:WIDTH=7 %i
|
||||||
|
select -assert-none t:$gt t:$ge t:$lt t:$le
|
||||||
|
|
||||||
|
|
||||||
|
design -reset
|
||||||
|
read_verilog <<EOT
|
||||||
|
module top(input [8:0] a, b, output gtu, gts, ltu, lts, geu, ges, leu, les);
|
||||||
|
wire [13:0] c = {a[8:6], 3'b101, a[5:4], 2'b11, a[3:0]};
|
||||||
|
wire [13:0] d = {b[8], 3'b101, b[7:4], 2'b01, b[3:0]};
|
||||||
|
assign gtu = c > d;
|
||||||
|
assign gts = $signed(c) > $signed(d);
|
||||||
|
assign ltu = c < d;
|
||||||
|
assign lts = $signed(c) < $signed(d);
|
||||||
|
assign geu = c >= d;
|
||||||
|
assign ges = $signed(c) >= $signed(d);
|
||||||
|
assign leu = c <= d;
|
||||||
|
assign les = $signed(c) <= $signed(d);
|
||||||
|
endmodule
|
||||||
|
EOT
|
||||||
|
design -save gold
|
||||||
|
|
||||||
|
equiv_opt -assert techmap -map +/cmp2lcu.v -D LUT_WIDTH=5
|
||||||
|
design -load postopt
|
||||||
|
select -assert-count 8 t:$lcu r:WIDTH=2 %i
|
||||||
|
select -assert-none t:$gt t:$ge t:$lt t:$le
|
||||||
|
|
||||||
|
design -load preopt
|
||||||
|
equiv_opt -assert techmap -map +/cmp2lcu.v -D LUT_WIDTH=3
|
||||||
|
design -load postopt
|
||||||
|
select -assert-count 8 t:$lcu r:WIDTH=4 %i
|
||||||
|
select -assert-none t:$gt t:$ge t:$lt t:$le
|
Loading…
Reference in New Issue