mirror of
				https://github.com/YosysHQ/yosys
				synced 2025-10-31 03:32:29 +00:00 
			
		
		
		
	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
					
				
					 6 changed files with 173 additions and 13 deletions
				
			
		|  | @ -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)) | ||||||
|  |  | ||||||
							
								
								
									
										116
									
								
								techlibs/common/cmp2lcu.v
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								techlibs/common/cmp2lcu.v
									
										
									
									
									
										Normal file
									
								
							|  | @ -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"); | ||||||
|  |  | ||||||
							
								
								
									
										52
									
								
								tests/techmap/cmp2lcu.ys
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								tests/techmap/cmp2lcu.ys
									
										
									
									
									
										Normal file
									
								
							|  | @ -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…
	
	Add table
		Add a link
		
	
		Reference in a new issue