diff options
Diffstat (limited to 'techlibs')
| -rw-r--r-- | techlibs/common/Makefile.inc | 1 | ||||
| -rw-r--r-- | techlibs/common/mul2dsp.v | 326 | ||||
| -rw-r--r-- | techlibs/ecp5/Makefile.inc | 1 | ||||
| -rw-r--r-- | techlibs/ecp5/dsp_map.v | 17 | ||||
| -rw-r--r-- | techlibs/ecp5/synth_ecp5.cc | 34 | ||||
| -rw-r--r-- | techlibs/ice40/Makefile.inc | 1 | ||||
| -rw-r--r-- | techlibs/ice40/dsp_map.v | 34 | ||||
| -rw-r--r-- | techlibs/ice40/synth_ice40.cc | 9 | ||||
| -rw-r--r-- | techlibs/xilinx/Makefile.inc | 1 | ||||
| -rw-r--r-- | techlibs/xilinx/cells_sim.v | 409 | ||||
| -rw-r--r-- | techlibs/xilinx/cells_xtra.v | 83 | ||||
| -rw-r--r-- | techlibs/xilinx/dsp_map.v | 48 | ||||
| -rw-r--r-- | techlibs/xilinx/synth_xilinx.cc | 27 | ||||
| -rw-r--r-- | techlibs/xilinx/tests/.gitignore | 5 | ||||
| -rw-r--r-- | techlibs/xilinx/tests/test_dsp_model.sh | 14 | ||||
| -rw-r--r-- | techlibs/xilinx/tests/test_dsp_model.v | 597 | 
16 files changed, 1516 insertions, 91 deletions
| diff --git a/techlibs/common/Makefile.inc b/techlibs/common/Makefile.inc index de94798af..6c0a4fe66 100644 --- a/techlibs/common/Makefile.inc +++ b/techlibs/common/Makefile.inc @@ -28,4 +28,5 @@ $(eval $(call add_share_file,share,techlibs/common/dff2ff.v))  $(eval $(call add_share_file,share,techlibs/common/gate2lut.v))  $(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/mul2dsp.v))  $(eval $(call add_share_file,share,techlibs/common/dummy.box)) diff --git a/techlibs/common/mul2dsp.v b/techlibs/common/mul2dsp.v new file mode 100644 index 000000000..f2b44222e --- /dev/null +++ b/techlibs/common/mul2dsp.v @@ -0,0 +1,326 @@ +/*
 + *  yosys -- Yosys Open SYnthesis Suite
 + *
 + *  Copyright (C) 2012  Clifford Wolf <clifford@clifford.at>
 + *                2019  Eddie Hung    <eddie@fpgeh.com>
 + *                2019  David Shah    <dave@ds0.me>
 + *
 + *  Permission to use, copy, modify, and/or distribute this software for any
 + *  purpose with or without fee is hereby granted, provided that the above
 + *  copyright notice and this permission notice appear in all copies.
 + *
 + *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 + *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 + *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 + *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 + *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 + *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 + *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 + *
 + *  ---
 + *
 + *  Tech-mapping rules for decomposing arbitrarily-sized $mul cells
 + *  into an equivalent collection of smaller `DSP_NAME cells (with the 
 + *  same interface as $mul) no larger than `DSP_[AB]_MAXWIDTH, attached 
 + *  to $shl and $add cells.
 + *
 + */
 +
 +`ifndef DSP_A_MAXWIDTH
 +$fatal(1, "Macro DSP_A_MAXWIDTH must be defined");
 +`endif
 +`ifndef DSP_B_MAXWIDTH
 +$fatal(1, "Macro DSP_B_MAXWIDTH must be defined");
 +`endif
 +`ifndef DSP_B_MAXWIDTH
 +$fatal(1, "Macro DSP_B_MAXWIDTH must be defined");
 +`endif
 +`ifndef DSP_A_MAXWIDTH_PARTIAL
 +`define DSP_A_MAXWIDTH_PARTIAL `DSP_A_MAXWIDTH
 +`endif
 +`ifndef DSP_B_MAXWIDTH_PARTIAL
 +`define DSP_B_MAXWIDTH_PARTIAL `DSP_B_MAXWIDTH
 +`endif
 +
 +`ifndef DSP_NAME
 +$fatal(1, "Macro DSP_NAME must be defined");
 +`endif
 +
 +`define MAX(a,b) (a > b ? a : b)
 +`define MIN(a,b) (a < b ? a : b)
 +
 +(* techmap_celltype = "$mul $__mul" *)
 +module _80_mul (A, B, Y);
 +	parameter A_SIGNED = 0;
 +	parameter B_SIGNED = 0;
 +	parameter A_WIDTH = 1;
 +	parameter B_WIDTH = 1;
 +	parameter Y_WIDTH = 1;
 +
 +	input [A_WIDTH-1:0] A;
 +	input [B_WIDTH-1:0] B;
 +	output [Y_WIDTH-1:0] Y;
 +
 +	parameter _TECHMAP_CELLTYPE_ = "";
 +
 +	generate
 +	if (0) begin end
 +`ifdef DSP_A_MINWIDTH
 +	else if (A_WIDTH < `DSP_A_MINWIDTH)
 +		wire _TECHMAP_FAIL_ = 1;
 +`endif
 +`ifdef DSP_B_MINWIDTH
 +	else if (B_WIDTH < `DSP_B_MINWIDTH)
 +		wire _TECHMAP_FAIL_ = 1;
 +`endif
 +`ifdef DSP_Y_MINWIDTH
 +	else if (Y_WIDTH < `DSP_Y_MINWIDTH)
 +		wire _TECHMAP_FAIL_ = 1;
 +`endif
 +	else if (_TECHMAP_CELLTYPE_ == "$mul" && A_SIGNED != B_SIGNED)
 +		wire _TECHMAP_FAIL_ = 1;
 +`ifdef DSP_SIGNEDONLY
 +	else if (_TECHMAP_CELLTYPE_ == "$mul" && !A_SIGNED)
 +		\$mul #(
 +			.A_SIGNED(1),
 +			.B_SIGNED(1),
 +			.A_WIDTH(A_WIDTH + 1),
 +			.B_WIDTH(B_WIDTH + 1),
 +			.Y_WIDTH(Y_WIDTH)
 +		) _TECHMAP_REPLACE_ (
 +			.A({1'b0, A}),
 +			.B({1'b0, B}),
 +			.Y(Y)
 +		);
 +`endif
 +	else if (_TECHMAP_CELLTYPE_ == "$mul" && A_WIDTH < B_WIDTH)
 +		\$mul #(
 +			.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)
 +		);
 +	else begin
 +		wire [1023:0] _TECHMAP_DO_ = "proc; clean";
 +
 +`ifdef DSP_SIGNEDONLY
 +		localparam sign_headroom = 1;
 +`else
 +		localparam sign_headroom = 0;
 +`endif
 +
 +		genvar i;
 +		if (A_WIDTH > `DSP_A_MAXWIDTH) begin
 +			localparam n = (A_WIDTH-`DSP_A_MAXWIDTH+`DSP_A_MAXWIDTH_PARTIAL-sign_headroom-1) / (`DSP_A_MAXWIDTH_PARTIAL-sign_headroom);
 +			localparam partial_Y_WIDTH = `MIN(Y_WIDTH, B_WIDTH+`DSP_A_MAXWIDTH_PARTIAL);
 +			localparam last_A_WIDTH = A_WIDTH-n*(`DSP_A_MAXWIDTH_PARTIAL-sign_headroom);
 +			localparam last_Y_WIDTH = B_WIDTH+last_A_WIDTH;
 +			if (A_SIGNED && B_SIGNED) begin
 +				wire signed [partial_Y_WIDTH-1:0] partial [n-1:0];
 +				wire signed [last_Y_WIDTH-1:0] last_partial;
 +				wire signed [Y_WIDTH-1:0] partial_sum [n:0];
 +			end
 +			else begin
 +				wire [partial_Y_WIDTH-1:0] partial [n-1:0];
 +				wire [last_Y_WIDTH-1:0] last_partial;
 +				wire [Y_WIDTH-1:0] partial_sum [n:0];
 +			end
 +
 +			for (i = 0; i < n; i=i+1) begin:slice
 +				\$__mul #(
 +					.A_SIGNED(sign_headroom),
 +					.B_SIGNED(B_SIGNED),
 +					.A_WIDTH(`DSP_A_MAXWIDTH_PARTIAL),
 +					.B_WIDTH(B_WIDTH),
 +					.Y_WIDTH(partial_Y_WIDTH)
 +				) mul_slice (
 +					.A({{sign_headroom{1'b0}}, A[i*(`DSP_A_MAXWIDTH_PARTIAL-sign_headroom) +: `DSP_A_MAXWIDTH_PARTIAL-sign_headroom]}),
 +					.B(B),
 +					.Y(partial[i])
 +				);
 +				// TODO: Currently a 'cascade' approach to summing the partial
 +				//       products is taken here, but a more efficient 'binary
 +				//       reduction' approach also exists...
 +				if (i == 0)
 +					assign partial_sum[i] = partial[i];
 +				else begin
 +					// Rewrite the following statement explicitly in order
 +					//   to save on a call to 'opt_expr -fine' which would
 +					//   optimise away the '<<' op and trim size of adder
 +					//assign partial_sum[i] = (partial[i] << i*(`DSP_A_MAXWIDTH_PARTIAL-sign_headroom)) + partial_sum[i-1];
 +					if (A_SIGNED && B_SIGNED)
 +						assign partial_sum[i][Y_WIDTH-1:i*(`DSP_A_MAXWIDTH_PARTIAL-sign_headroom)] = partial[i] + $signed(partial_sum[i-1][Y_WIDTH-1:i*(`DSP_A_MAXWIDTH_PARTIAL-sign_headroom)]);
 +					else
 +						assign partial_sum[i][Y_WIDTH-1:i*(`DSP_A_MAXWIDTH_PARTIAL-sign_headroom)] = partial[i] + partial_sum[i-1][Y_WIDTH-1:i*(`DSP_A_MAXWIDTH_PARTIAL-sign_headroom)];
 +					assign partial_sum[i][i*(`DSP_A_MAXWIDTH_PARTIAL-sign_headroom)-1:0] = partial_sum[i-1][i*(`DSP_A_MAXWIDTH_PARTIAL-sign_headroom)-1:0];
 +				end
 +			end
 +
 +			\$__mul #(
 +				.A_SIGNED(A_SIGNED),
 +				.B_SIGNED(B_SIGNED),
 +				.A_WIDTH(last_A_WIDTH),
 +				.B_WIDTH(B_WIDTH),
 +				.Y_WIDTH(last_Y_WIDTH)
 +			) mul_slice_last (
 +				.A(A[A_WIDTH-1 -: last_A_WIDTH]),
 +				.B(B),
 +				.Y(last_partial)
 +			);
 +			//assign partial_sum[n] = (last_partial << n*(`DSP_A_MAXWIDTH_PARTIAL-sign_headroom)) + partial_sum[n-1];
 +			if (A_SIGNED && B_SIGNED)
 +				assign partial_sum[n][Y_WIDTH-1:n*(`DSP_A_MAXWIDTH_PARTIAL-sign_headroom)] = last_partial + $signed(partial_sum[n-1][Y_WIDTH-1:n*(`DSP_A_MAXWIDTH_PARTIAL-sign_headroom)]);
 +			else
 +				assign partial_sum[n][Y_WIDTH-1:n*(`DSP_A_MAXWIDTH_PARTIAL-sign_headroom)] = last_partial + partial_sum[n-1][Y_WIDTH-1:n*(`DSP_A_MAXWIDTH_PARTIAL-sign_headroom)];
 +			assign partial_sum[n][n*(`DSP_A_MAXWIDTH_PARTIAL-sign_headroom)-1:0] = partial_sum[n-1][n*(`DSP_A_MAXWIDTH_PARTIAL-sign_headroom)-1:0];
 +			assign Y = partial_sum[n];
 +		end
 +		else if (B_WIDTH > `DSP_B_MAXWIDTH) begin
 +			localparam n = (B_WIDTH-`DSP_B_MAXWIDTH+`DSP_B_MAXWIDTH_PARTIAL-sign_headroom-1) / (`DSP_B_MAXWIDTH_PARTIAL-sign_headroom);
 +			localparam partial_Y_WIDTH = `MIN(Y_WIDTH, A_WIDTH+`DSP_B_MAXWIDTH_PARTIAL);
 +			localparam last_B_WIDTH = B_WIDTH-n*(`DSP_B_MAXWIDTH_PARTIAL-sign_headroom);
 +			localparam last_Y_WIDTH = A_WIDTH+last_B_WIDTH;
 +			if (A_SIGNED && B_SIGNED) begin
 +				wire signed [partial_Y_WIDTH-1:0] partial [n-1:0];
 +				wire signed [last_Y_WIDTH-1:0] last_partial;
 +				wire signed [Y_WIDTH-1:0] partial_sum [n:0];
 +			end
 +			else begin
 +				wire [partial_Y_WIDTH-1:0] partial [n-1:0];
 +				wire [last_Y_WIDTH-1:0] last_partial;
 +				wire [Y_WIDTH-1:0] partial_sum [n:0];
 +			end
 +
 +			for (i = 0; i < n; i=i+1) begin:slice
 +				\$__mul #(
 +					.A_SIGNED(A_SIGNED),
 +					.B_SIGNED(sign_headroom),
 +					.A_WIDTH(A_WIDTH),
 +					.B_WIDTH(`DSP_B_MAXWIDTH_PARTIAL),
 +					.Y_WIDTH(partial_Y_WIDTH)
 +				) mul (
 +					.A(A),
 +					.B({{sign_headroom{1'b0}}, B[i*(`DSP_B_MAXWIDTH_PARTIAL-sign_headroom) +: `DSP_B_MAXWIDTH_PARTIAL-sign_headroom]}),
 +					.Y(partial[i])
 +				);
 +				// TODO: Currently a 'cascade' approach to summing the partial
 +				//       products is taken here, but a more efficient 'binary
 +				//       reduction' approach also exists...
 +				if (i == 0)
 +					assign partial_sum[i] = partial[i];
 +				else begin
 +					// Rewrite the following statement explicitly in order
 +					//   to save on a call to 'opt_expr -fine' which would
 +					//   optimise away the '<<' op and trim size of adder
 +					//assign partial_sum[i] = (partial[i] << i*(`DSP_B_MAXWIDTH_PARTIAL-sign_headroom)) + partial_sum[i-1];
 +					if (A_SIGNED && B_SIGNED)
 +						assign partial_sum[i][Y_WIDTH-1:i*(`DSP_B_MAXWIDTH_PARTIAL-sign_headroom)] = partial[i] + $signed(partial_sum[i-1][Y_WIDTH-1:i*(`DSP_B_MAXWIDTH_PARTIAL-sign_headroom)]);
 +					else
 +						assign partial_sum[i][Y_WIDTH-1:i*(`DSP_B_MAXWIDTH_PARTIAL-sign_headroom)] = partial[i] + partial_sum[i-1][Y_WIDTH-1:i*(`DSP_B_MAXWIDTH_PARTIAL-sign_headroom)];
 +					assign partial_sum[i][i*(`DSP_B_MAXWIDTH_PARTIAL-sign_headroom)-1:0] = partial_sum[i-1][i*(`DSP_B_MAXWIDTH_PARTIAL-sign_headroom)-1:0];
 +				end
 +			end
 +
 +			\$__mul #(
 +				.A_SIGNED(A_SIGNED),
 +				.B_SIGNED(B_SIGNED),
 +				.A_WIDTH(A_WIDTH),
 +				.B_WIDTH(last_B_WIDTH),
 +				.Y_WIDTH(last_Y_WIDTH)
 +			) mul_last (
 +				.A(A),
 +				.B(B[B_WIDTH-1 -: last_B_WIDTH]),
 +				.Y(last_partial)
 +			);
 +			//assign partial_sum[n] = (last_partial << n*(`DSP_B_MAXWIDTH_PARTIAL-sign_headroom)) + partial_sum[n-1];
 +			if (A_SIGNED && B_SIGNED)
 +				assign partial_sum[n][Y_WIDTH-1:n*(`DSP_B_MAXWIDTH_PARTIAL-sign_headroom)] = last_partial + partial_sum[n-1][Y_WIDTH-1:n*(`DSP_B_MAXWIDTH_PARTIAL-sign_headroom)];
 +			else
 +				assign partial_sum[n][Y_WIDTH-1:n*(`DSP_B_MAXWIDTH_PARTIAL-sign_headroom)] = last_partial + $signed(partial_sum[n-1][Y_WIDTH-1:n*(`DSP_B_MAXWIDTH_PARTIAL-sign_headroom)]);
 +			assign partial_sum[n][n*(`DSP_B_MAXWIDTH_PARTIAL-sign_headroom)-1:0] = partial_sum[n-1][n*(`DSP_B_MAXWIDTH_PARTIAL-sign_headroom)-1:0];
 +			assign Y = partial_sum[n];
 +		end
 +		else begin
 +			if (A_SIGNED)
 +				wire signed [`DSP_A_MAXWIDTH-1:0] Aext = $signed(A);
 +			else
 +				wire [`DSP_A_MAXWIDTH-1:0] Aext = A;
 +			if (B_SIGNED)
 +				wire signed [`DSP_B_MAXWIDTH-1:0] Bext = $signed(B);
 +			else
 +				wire [`DSP_B_MAXWIDTH-1:0] Bext = B;
 +
 +			`DSP_NAME #(
 +				.A_SIGNED(A_SIGNED),
 +				.B_SIGNED(B_SIGNED),
 +				.A_WIDTH(`DSP_A_MAXWIDTH),
 +				.B_WIDTH(`DSP_B_MAXWIDTH),
 +				.Y_WIDTH(`MIN(Y_WIDTH,`DSP_A_MAXWIDTH+`DSP_B_MAXWIDTH)),
 +			) _TECHMAP_REPLACE_ (
 +				.A(Aext),
 +				.B(Bext),
 +				.Y(Y)
 +			);
 +		end
 +	end
 +	endgenerate
 +endmodule
 +
 +(* techmap_celltype = "$mul $__mul" *)
 +module _90_soft_mul (A, B, Y);
 +	parameter A_SIGNED = 0;
 +	parameter B_SIGNED = 0;
 +	parameter A_WIDTH = 1;
 +	parameter B_WIDTH = 1;
 +	parameter Y_WIDTH = 1;
 +
 +	input [A_WIDTH-1:0] A;
 +	input [B_WIDTH-1:0] B;
 +	output [Y_WIDTH-1:0] Y;
 +
 +	// Indirection necessary since mapping
 +	//   back to $mul will cause recursion
 +	generate
 +	if (A_SIGNED && !B_SIGNED)
 +		\$__soft_mul #(
 +			.A_SIGNED(A_SIGNED),
 +			.B_SIGNED(1),
 +			.A_WIDTH(A_WIDTH),
 +			.B_WIDTH(B_WIDTH+1),
 +			.Y_WIDTH(Y_WIDTH)
 +		) _TECHMAP_REPLACE_ (
 +			.A(A),
 +			.B({1'b0,B}),
 +			.Y(Y)
 +		);
 +	else if (!A_SIGNED && B_SIGNED)
 +		\$__soft_mul #(
 +			.A_SIGNED(1),
 +			.B_SIGNED(B_SIGNED),
 +			.A_WIDTH(A_WIDTH+1),
 +			.B_WIDTH(B_WIDTH),
 +			.Y_WIDTH(Y_WIDTH)
 +		) _TECHMAP_REPLACE_ (
 +			.A({1'b0,A}),
 +			.B(B),
 +			.Y(Y)
 +		);
 +	else
 +		\$__soft_mul #(
 +			.A_SIGNED(A_SIGNED),
 +			.B_SIGNED(B_SIGNED),
 +			.A_WIDTH(A_WIDTH),
 +			.B_WIDTH(B_WIDTH),
 +			.Y_WIDTH(Y_WIDTH)
 +		) _TECHMAP_REPLACE_ (
 +			.A(A),
 +			.B(B),
 +			.Y(Y)
 +		);
 +	endgenerate
 +endmodule
 diff --git a/techlibs/ecp5/Makefile.inc b/techlibs/ecp5/Makefile.inc index 9efb6347f..80eee5004 100644 --- a/techlibs/ecp5/Makefile.inc +++ b/techlibs/ecp5/Makefile.inc @@ -13,6 +13,7 @@ $(eval $(call add_share_file,share/ecp5,techlibs/ecp5/brams_map.v))  $(eval $(call add_share_file,share/ecp5,techlibs/ecp5/bram.txt))  $(eval $(call add_share_file,share/ecp5,techlibs/ecp5/arith_map.v))  $(eval $(call add_share_file,share/ecp5,techlibs/ecp5/latches_map.v)) +$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/dsp_map.v))  $(eval $(call add_share_file,share/ecp5,techlibs/ecp5/abc_map.v))  $(eval $(call add_share_file,share/ecp5,techlibs/ecp5/abc_unmap.v)) diff --git a/techlibs/ecp5/dsp_map.v b/techlibs/ecp5/dsp_map.v new file mode 100644 index 000000000..cb95ddb1c --- /dev/null +++ b/techlibs/ecp5/dsp_map.v @@ -0,0 +1,17 @@ +module \$__MUL18X18 (input [17:0] A, input [17:0] B, output [35:0] Y); + +	parameter A_WIDTH = 18; +	parameter B_WIDTH = 18; +	parameter Y_WIDTH = 36; +	parameter A_SIGNED = 0; +	parameter B_SIGNED = 0; + +	MULT18X18D _TECHMAP_REPLACE_ ( +		.A0(A[0]), .A1(A[1]), .A2(A[2]), .A3(A[3]), .A4(A[4]), .A5(A[5]), .A6(A[6]), .A7(A[7]), .A8(A[8]), .A9(A[9]), .A10(A[10]), .A11(A[11]), .A12(A[12]), .A13(A[13]), .A14(A[14]), .A15(A[15]), .A16(A[16]), .A17(A[17]), +		.B0(B[0]), .B1(B[1]), .B2(B[2]), .B3(B[3]), .B4(B[4]), .B5(B[5]), .B6(B[6]), .B7(B[7]), .B8(B[8]), .B9(B[9]), .B10(B[10]), .B11(B[11]), .B12(B[12]), .B13(B[13]), .B14(B[14]), .B15(B[15]), .B16(B[16]), .B17(B[17]), +		.C17(1'b0), .C16(1'b0), .C15(1'b0), .C14(1'b0), .C13(1'b0), .C12(1'b0), .C11(1'b0), .C10(1'b0), .C9(1'b0), .C8(1'b0), .C7(1'b0), .C6(1'b0), .C5(1'b0), .C4(1'b0), .C3(1'b0), .C2(1'b0), .C1(1'b0), .C0(1'b0), +		.SIGNEDA(A_SIGNED), .SIGNEDB(B_SIGNED), .SOURCEA(1'b0), .SOURCEB(1'b0), + +		.P0(Y[0]), .P1(Y[1]), .P2(Y[2]), .P3(Y[3]), .P4(Y[4]), .P5(Y[5]), .P6(Y[6]), .P7(Y[7]), .P8(Y[8]), .P9(Y[9]), .P10(Y[10]), .P11(Y[11]), .P12(Y[12]), .P13(Y[13]), .P14(Y[14]), .P15(Y[15]), .P16(Y[16]), .P17(Y[17]), .P18(Y[18]), .P19(Y[19]), .P20(Y[20]), .P21(Y[21]), .P22(Y[22]), .P23(Y[23]), .P24(Y[24]), .P25(Y[25]), .P26(Y[26]), .P27(Y[27]), .P28(Y[28]), .P29(Y[29]), .P30(Y[30]), .P31(Y[31]), .P32(Y[32]), .P33(Y[33]), .P34(Y[34]), .P35(Y[35]) +	); +endmodule diff --git a/techlibs/ecp5/synth_ecp5.cc b/techlibs/ecp5/synth_ecp5.cc index 2593546e0..0a3dcc62c 100644 --- a/techlibs/ecp5/synth_ecp5.cc +++ b/techlibs/ecp5/synth_ecp5.cc @@ -89,6 +89,9 @@ struct SynthEcp5Pass : public ScriptPass  		log("        generate an output netlist (and BLIF file) suitable for VPR\n");  		log("        (this feature is experimental and incomplete)\n");  		log("\n"); +		log("    -nodsp\n"); +		log("        do not map multipliers to MULT18X18D\n"); +		log("\n");  		log("\n");  		log("The following commands are executed by this synthesis command:\n");  		help_script(); @@ -96,7 +99,7 @@ struct SynthEcp5Pass : public ScriptPass  	}  	string top_opt, blif_file, edif_file, json_file; -	bool noccu2, nodffe, nobram, nolutram, nowidelut, flatten, retime, abc2, abc9, vpr; +	bool noccu2, nodffe, nobram, nolutram, nowidelut, flatten, retime, abc2, abc9, nodsp, vpr;  	void clear_flags() YS_OVERRIDE  	{ @@ -114,6 +117,7 @@ struct SynthEcp5Pass : public ScriptPass  		abc2 = false;  		vpr = false;  		abc9 = false; +		nodsp = false;  	}  	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE @@ -192,6 +196,10 @@ struct SynthEcp5Pass : public ScriptPass  				abc9 = true;  				continue;  			} +			if (args[argidx] == "-nodsp") { +				nodsp = true; +				continue; +			}  			break;  		}  		extra_args(args, argidx, design); @@ -228,7 +236,29 @@ struct SynthEcp5Pass : public ScriptPass  		if (check_label("coarse"))  		{ -			run("synth -run coarse"); +			run("opt_expr"); +			run("opt_clean"); +			run("check"); +			run("opt"); +			run("wreduce"); +			run("peepopt"); +			run("opt_clean"); +			run("share"); +			run("techmap -map +/cmp2lut.v -D LUT_WIDTH=4"); +			run("opt_expr"); +			run("opt_clean"); +			if (!nodsp) { +				run("techmap -map +/mul2dsp.v -D DSP_A_MAXWIDTH=18 -D DSP_B_MAXWIDTH=18  -D DSP_A_MINWIDTH=2 -D DSP_B_MINWIDTH=2  -D DSP_NAME=$__MUL18X18", "(unless -nodsp)"); +				run("clean", "(unless -nodsp)"); +				run("techmap -map +/ecp5/dsp_map.v", "(unless -nodsp)"); +				run("chtype -set $mul t:$__soft_mul", "(unless -nodsp)"); +			} +			run("alumacc"); +			run("opt"); +			run("fsm"); +			run("opt -fast"); +			run("memory -nomap"); +			run("opt_clean");  		}  		if (!nobram && check_label("map_bram", "(skip if -nobram)")) diff --git a/techlibs/ice40/Makefile.inc b/techlibs/ice40/Makefile.inc index 76a89b107..92a9956ea 100644 --- a/techlibs/ice40/Makefile.inc +++ b/techlibs/ice40/Makefile.inc @@ -27,6 +27,7 @@ $(eval $(call add_share_file,share/ice40,techlibs/ice40/cells_sim.v))  $(eval $(call add_share_file,share/ice40,techlibs/ice40/latches_map.v))  $(eval $(call add_share_file,share/ice40,techlibs/ice40/brams.txt))  $(eval $(call add_share_file,share/ice40,techlibs/ice40/brams_map.v)) +$(eval $(call add_share_file,share/ice40,techlibs/ice40/dsp_map.v))  $(eval $(call add_share_file,share/ice40,techlibs/ice40/abc_hx.box))  $(eval $(call add_share_file,share/ice40,techlibs/ice40/abc_hx.lut))  $(eval $(call add_share_file,share/ice40,techlibs/ice40/abc_lp.box)) diff --git a/techlibs/ice40/dsp_map.v b/techlibs/ice40/dsp_map.v new file mode 100644 index 000000000..06fa73956 --- /dev/null +++ b/techlibs/ice40/dsp_map.v @@ -0,0 +1,34 @@ +module \$__MUL16X16 (input [15:0] A, input [15:0] B, output [31:0] Y); +	parameter A_SIGNED = 0; +	parameter B_SIGNED = 0; +	parameter A_WIDTH = 0; +	parameter B_WIDTH = 0; +	parameter Y_WIDTH = 0; + +	SB_MAC16 #( +		.NEG_TRIGGER(1'b0), +		.C_REG(1'b0), +		.A_REG(1'b0), +		.B_REG(1'b0), +		.D_REG(1'b0), +		.TOP_8x8_MULT_REG(1'b0), +		.BOT_8x8_MULT_REG(1'b0), +		.PIPELINE_16x16_MULT_REG1(1'b0), +		.PIPELINE_16x16_MULT_REG2(1'b0), +		.TOPOUTPUT_SELECT(2'b11), +		.TOPADDSUB_LOWERINPUT(2'b0), +		.TOPADDSUB_UPPERINPUT(1'b0), +		.TOPADDSUB_CARRYSELECT(2'b0), +		.BOTOUTPUT_SELECT(2'b11), +		.BOTADDSUB_LOWERINPUT(2'b0), +		.BOTADDSUB_UPPERINPUT(1'b0), +		.BOTADDSUB_CARRYSELECT(2'b0), +		.MODE_8x8(1'b0), +		.A_SIGNED(A_SIGNED), +		.B_SIGNED(B_SIGNED) +	) _TECHMAP_REPLACE_ ( +		.A(A), +		.B(B), +		.O(Y), +	); +endmodule diff --git a/techlibs/ice40/synth_ice40.cc b/techlibs/ice40/synth_ice40.cc index a3890268a..55aa72aa7 100644 --- a/techlibs/ice40/synth_ice40.cc +++ b/techlibs/ice40/synth_ice40.cc @@ -272,8 +272,13 @@ struct SynthIce40Pass : public ScriptPass  			run("techmap -map +/cmp2lut.v -D LUT_WIDTH=4");  			run("opt_expr");  			run("opt_clean"); -			if (help_mode || dsp) -				run("ice40_dsp", "(if -dsp)"); +			if (help_mode || dsp) { +				run("techmap -map +/mul2dsp.v -map +/ice40/dsp_map.v -D DSP_A_MAXWIDTH=16 -D DSP_B_MAXWIDTH=16 -D DSP_A_MINWIDTH=2 -D DSP_B_MINWIDTH=2 -D DSP_Y_MINWIDTH=11 -D DSP_NAME=$__MUL16X16", "(if -dsp)"); +				run("opt_expr -fine", "               (if -dsp)"); +				run("wreduce", "                      (if -dsp)"); +				run("ice40_dsp", "                    (if -dsp)"); +				run("chtype -set $mul t:$__soft_mul","(if -dsp)"); +			}  			run("alumacc");  			run("opt");  			run("fsm"); diff --git a/techlibs/xilinx/Makefile.inc b/techlibs/xilinx/Makefile.inc index b5e81a79d..5f5aa5518 100644 --- a/techlibs/xilinx/Makefile.inc +++ b/techlibs/xilinx/Makefile.inc @@ -39,6 +39,7 @@ $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/xc6s_ff_map.v))  $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/xc7_ff_map.v))  $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/lut_map.v))  $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/mux_map.v)) +$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/dsp_map.v))  $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/abc_map.v))  $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/abc_unmap.v)) diff --git a/techlibs/xilinx/cells_sim.v b/techlibs/xilinx/cells_sim.v index 6e8729256..ed421f85e 100644 --- a/techlibs/xilinx/cells_sim.v +++ b/techlibs/xilinx/cells_sim.v @@ -489,3 +489,412 @@ module SRLC32E (        always @(posedge CLK) if (CE) r <= { r[30:0], D };    endgenerate  endmodule + +module DSP48E1 ( +    output [29:0] ACOUT, +    output [17:0] BCOUT, +    output reg CARRYCASCOUT, +    output reg [3:0] CARRYOUT, +    output reg MULTSIGNOUT, +    output OVERFLOW, +    output reg signed [47:0] P, +    output PATTERNBDETECT, +    output PATTERNDETECT, +    output [47:0] PCOUT, +    output UNDERFLOW, +    input signed [29:0] A, +    input [29:0] ACIN, +    input [3:0] ALUMODE, +    input signed [17:0] B, +    input [17:0] BCIN, +    input [47:0] C, +    input CARRYCASCIN, +    input CARRYIN, +    input [2:0] CARRYINSEL, +    input CEA1, +    input CEA2, +    input CEAD, +    input CEALUMODE, +    input CEB1, +    input CEB2, +    input CEC, +    input CECARRYIN, +    input CECTRL, +    input CED, +    input CEINMODE, +    input CEM, +    input CEP, +    (* clkbuf_sink *) input CLK, +    input [24:0] D, +    input [4:0] INMODE, +    input MULTSIGNIN, +    input [6:0] OPMODE, +    input [47:0] PCIN, +    input RSTA, +    input RSTALLCARRYIN, +    input RSTALUMODE, +    input RSTB, +    input RSTC, +    input RSTCTRL, +    input RSTD, +    input RSTINMODE, +    input RSTM, +    input RSTP +); +    parameter integer ACASCREG = 1; +    parameter integer ADREG = 1; +    parameter integer ALUMODEREG = 1; +    parameter integer AREG = 1; +    parameter AUTORESET_PATDET = "NO_RESET"; +    parameter A_INPUT = "DIRECT"; +    parameter integer BCASCREG = 1; +    parameter integer BREG = 1; +    parameter B_INPUT = "DIRECT"; +    parameter integer CARRYINREG = 1; +    parameter integer CARRYINSELREG = 1; +    parameter integer CREG = 1; +    parameter integer DREG = 1; +    parameter integer INMODEREG = 1; +    parameter integer MREG = 1; +    parameter integer OPMODEREG = 1; +    parameter integer PREG = 1; +    parameter SEL_MASK = "MASK"; +    parameter SEL_PATTERN = "PATTERN"; +    parameter USE_DPORT = "FALSE"; +    parameter USE_MULT = "MULTIPLY"; +    parameter USE_PATTERN_DETECT = "NO_PATDET"; +    parameter USE_SIMD = "ONE48"; +    parameter [47:0] MASK = 48'h3FFFFFFFFFFF; +    parameter [47:0] PATTERN = 48'h000000000000; +    parameter [3:0] IS_ALUMODE_INVERTED = 4'b0; +    parameter [0:0] IS_CARRYIN_INVERTED = 1'b0; +    parameter [0:0] IS_CLK_INVERTED = 1'b0; +    parameter [4:0] IS_INMODE_INVERTED = 5'b0; +    parameter [6:0] IS_OPMODE_INVERTED = 7'b0; + +    initial begin +`ifdef __ICARUS__ +        if (AUTORESET_PATDET != "NO_RESET") $fatal(1, "Unsupported AUTORESET_PATDET value"); +        //if (PREG != 0)              $fatal(1, "Unsupported PREG value"); +        if (SEL_MASK != "MASK")     $fatal(1, "Unsupported SEL_MASK value"); +        if (SEL_PATTERN != "PATTERN") $fatal(1, "Unsupported SEL_PATTERN value"); +        if (USE_PATTERN_DETECT != "NO_PATDET") $fatal(1, "Unsupported USE_PATTERN_DETECT value"); +        if (USE_SIMD != "ONE48" && USE_SIMD != "TWO24" && USE_SIMD != "FOUR12")    $fatal(1, "Unsupported USE_SIMD value"); +        if (IS_ALUMODE_INVERTED != 4'b0) $fatal(1, "Unsupported IS_ALUMODE_INVERTED value"); +        if (IS_CARRYIN_INVERTED != 1'b0) $fatal(1, "Unsupported IS_CARRYIN_INVERTED value"); +        if (IS_CLK_INVERTED != 1'b0) $fatal(1, "Unsupported IS_CLK_INVERTED value"); +        if (IS_INMODE_INVERTED != 5'b0) $fatal(1, "Unsupported IS_INMODE_INVERTED value"); +        if (IS_OPMODE_INVERTED != 7'b0) $fatal(1, "Unsupported IS_OPMODE_INVERTED value"); +`endif +    end + +    wire signed [29:0] A_muxed; +    wire signed [17:0] B_muxed; + +    generate +        if (A_INPUT == "CASCADE") assign A_muxed = ACIN; +        else assign A_muxed = A; + +        if (B_INPUT == "CASCADE") assign B_muxed = BCIN; +        else assign B_muxed = B; +    endgenerate + +    reg signed [29:0] Ar1 = 30'b0, Ar2 = 30'b0; +    reg signed [24:0] Dr = 25'b0; +    reg signed [17:0] Br1 = 18'b0, Br2 = 18'b0; +    reg signed [47:0] Cr = 48'b0; +    reg        [4:0]  INMODEr = 5'b0; +    reg        [6:0]  OPMODEr = 7'b0; +    reg        [3:0]  ALUMODEr = 4'b0; +    reg        [2:0]  CARRYINSELr = 3'b0; + +    generate +        // Configurable A register +        if (AREG == 2) begin +            always @(posedge CLK) +                if (RSTA) begin +                    Ar1 <= 30'b0; +                    Ar2 <= 30'b0; +                end else begin +                    if (CEA1) Ar1 <= A_muxed; +                    if (CEA2) Ar2 <= Ar1; +                end +        end else if (AREG == 1) begin +            always @(posedge CLK) +                if (RSTA) begin +                    Ar1 <= 30'b0; +                    Ar2 <= 30'b0; +                end else begin +                    if (CEA1) Ar1 <= A_muxed; +                    if (CEA2) Ar2 <= A_muxed; +                end +        end else begin +            always @* Ar1 <= A_muxed; +            always @* Ar2 <= A_muxed; +        end + +        // Configurable B register +        if (BREG == 2) begin +            always @(posedge CLK) +                if (RSTB) begin +                    Br1 <= 18'b0; +                    Br2 <= 18'b0; +                end else begin +                    if (CEB1) Br1 <= B_muxed; +                    if (CEB2) Br2 <= Br1; +                end +        end else if (BREG == 1) begin +            always @(posedge CLK) +                if (RSTB) begin +                    Br1 <= 18'b0; +                    Br2 <= 18'b0; +                end else begin +                    if (CEB1) Br1 <= B_muxed; +                    if (CEB2) Br2 <= B_muxed; +                end +        end else begin +            always @* Br1 <= B_muxed; +            always @* Br2 <= B_muxed; +        end + +        // C and D registers +        if (CREG == 1) begin always @(posedge CLK) if (RSTC) Cr <= 48'b0; else if (CEC) Cr <= C; end +        else           always @* Cr <= C; + +        if (DREG == 1) begin always @(posedge CLK) if (RSTD) Dr <= 25'b0; else if (CED) Dr <= D; end +        else           always @* Dr <= D; + +        // Control registers +        if (INMODEREG == 1) begin always @(posedge CLK) if (RSTINMODE) INMODEr <= 5'b0; else if (CEINMODE) INMODEr <= INMODE; end +        else           always @* INMODEr <= INMODE; +        if (OPMODEREG == 1) begin always @(posedge CLK) if (RSTCTRL) OPMODEr <= 7'b0; else if (CECTRL) OPMODEr <= OPMODE; end +        else           always @* OPMODEr <= OPMODE; +        if (ALUMODEREG == 1) begin always @(posedge CLK) if (RSTALUMODE) ALUMODEr <= 4'b0; else if (CEALUMODE) ALUMODEr <= ALUMODE; end +        else           always @* ALUMODEr <= ALUMODE; +        if (CARRYINSELREG == 1) begin always @(posedge CLK) if (RSTCTRL) CARRYINSELr <= 3'b0; else if (CECTRL) CARRYINSELr <= CARRYINSEL; end +        else           always @* CARRYINSELr <= CARRYINSEL; +    endgenerate + +    // A and B cascsde +    generate +        if (ACASCREG == 1 && AREG == 2) assign ACOUT = Ar1; +        else assign ACOUT = Ar2; +        if (BCASCREG == 1 && BREG == 2) assign BCOUT = Br1; +        else assign BCOUT = Br2; +    endgenerate + +    // A/D input selection and pre-adder +    wire signed [29:0] Ar12_muxed = INMODEr[0] ? Ar1 : Ar2; +    wire signed [24:0] Ar12_gated = INMODEr[1] ? 25'b0 : Ar12_muxed; +    wire signed [24:0] Dr_gated   = INMODEr[2] ? Dr : 25'b0; +    wire signed [24:0] AD_result  = INMODEr[3] ? (Dr_gated - Ar12_gated) : (Dr_gated + Ar12_gated); +    reg  signed [24:0] ADr = 25'b0; + +    generate +        if (ADREG == 1) begin always @(posedge CLK) if (RSTD) ADr <= 25'b0; else if (CEAD) ADr <= AD_result; end +        else            always @* ADr <= AD_result; +    endgenerate + +    // 25x18 multiplier +    wire signed [24:0] A_MULT; +    wire signed [17:0] B_MULT = INMODEr[4] ? Br1 : Br2; +    generate +        if (USE_DPORT == "TRUE") assign A_MULT = ADr; +        else assign A_MULT = Ar12_gated; +    endgenerate + +    wire signed [42:0] M = A_MULT * B_MULT; +    wire signed [42:0] Mx = (CARRYINSEL == 3'b010) ? 43'bx : M; +    reg  signed [42:0] Mr = 43'b0; + +    // Multiplier result register +    generate +        if (MREG == 1) begin always @(posedge CLK) if (RSTM) Mr <= 43'b0; else if (CEM) Mr <= Mx; end +        else           always @* Mr <= Mx; +    endgenerate + +    wire signed [42:0] Mrx = (CARRYINSELr == 3'b010) ? 43'bx : Mr; + +    // X, Y and Z ALU inputs +    reg signed [47:0] X, Y, Z; + +    always @* begin +        // X multiplexer +        case (OPMODEr[1:0]) +            2'b00: X = 48'b0; +            2'b01: begin X = $signed(Mrx); +`ifdef __ICARUS__ +                if (OPMODEr[3:2] != 2'b01) $fatal(1, "OPMODEr[3:2] must be 2'b01 when OPMODEr[1:0] is 2'b01"); +`endif +            end +            2'b10: begin X = P; +`ifdef __ICARUS__ +                if (PREG != 1) $fatal(1, "PREG must be 1 when OPMODEr[1:0] is 2'b10"); +`endif +            end +            2'b11: X = $signed({Ar2, Br2}); +            default: X = 48'bx; +        endcase + +        // Y multiplexer +        case (OPMODEr[3:2]) +            2'b00: Y = 48'b0; +            2'b01: begin Y = 48'b0; // FIXME: more accurate partial product modelling? +`ifdef __ICARUS__ +                if (OPMODEr[1:0] != 2'b01) $fatal(1, "OPMODEr[1:0] must be 2'b01 when OPMODEr[3:2] is 2'b01"); +`endif +            end +            2'b10: Y = {48{1'b1}}; +            2'b11: Y = Cr; +            default: Y = 48'bx; +        endcase + +        // Z multiplexer +        case (OPMODEr[6:4]) +            3'b000: Z = 48'b0; +            3'b001: Z = PCIN; +            3'b010: begin Z = P; +`ifdef __ICARUS__ +                if (PREG != 1) $fatal(1, "PREG must be 1 when OPMODEr[6:4] i0s 3'b010"); +`endif +            end +            3'b011: Z = Cr; +            3'b100: begin Z = P; +`ifdef __ICARUS__ +                if (PREG != 1) $fatal(1, "PREG must be 1 when OPMODEr[6:4] is 3'b100"); +                if (OPMODEr[3:0] != 4'b1000) $fatal(1, "OPMODEr[3:0] must be 4'b1000 when OPMODEr[6:4] i0s 3'b100"); +`endif +            end +            3'b101: Z = $signed(PCIN[47:17]); +            3'b110: Z = $signed(P[47:17]); +            default: Z = 48'bx; +        endcase +    end + +    // Carry in +    wire A24_xnor_B17d = A_MULT[24] ~^ B_MULT[17]; +    reg CARRYINr = 1'b0, A24_xnor_B17 = 1'b0; +    generate +        if (CARRYINREG == 1) begin always @(posedge CLK) if (RSTALLCARRYIN) CARRYINr <= 1'b0; else if (CECARRYIN) CARRYINr <= CARRYIN; end +        else                 always @* CARRYINr = CARRYIN; + +        if (MREG == 1) begin always @(posedge CLK) if (RSTALLCARRYIN) A24_xnor_B17 <= 1'b0; else if (CEM) A24_xnor_B17 <= A24_xnor_B17d; end +        else                 always @* A24_xnor_B17 = A24_xnor_B17d; +    endgenerate + +    reg cin_muxed; + +    always @(*) begin +        case (CARRYINSELr) +            3'b000: cin_muxed = CARRYINr; +            3'b001: cin_muxed = ~PCIN[47]; +            3'b010: cin_muxed = CARRYCASCIN; +            3'b011: cin_muxed = PCIN[47]; +            3'b100: cin_muxed = CARRYCASCOUT; +            3'b101: cin_muxed = ~P[47]; +            3'b110: cin_muxed = A24_xnor_B17; +            3'b111: cin_muxed = P[47]; +            default: cin_muxed = 1'bx; +        endcase +    end + +    wire alu_cin = (ALUMODEr[3] || ALUMODEr[2]) ? 1'b0 : cin_muxed; + +    // ALU core +    wire [47:0] Z_muxinv = ALUMODEr[0] ? ~Z : Z; +    wire [47:0] xor_xyz = X ^ Y ^ Z_muxinv; +    wire [47:0] maj_xyz = (X & Y) | (X & Z_muxinv) | (Y & Z_muxinv); + +    wire [47:0] xor_xyz_muxed = ALUMODEr[3] ? maj_xyz : xor_xyz; +    wire [47:0] maj_xyz_gated = ALUMODEr[2] ? 48'b0 :  maj_xyz; + +    wire [48:0] maj_xyz_simd_gated; +    wire [3:0] int_carry_in, int_carry_out, ext_carry_out; +    wire [47:0] alu_sum; +    assign int_carry_in[0] = 1'b0; +    wire [3:0] carryout_reset; + +    generate +        if (USE_SIMD == "FOUR12") begin +            assign maj_xyz_simd_gated = { +                    maj_xyz_gated[47:36], +                    1'b0, maj_xyz_gated[34:24], +                    1'b0, maj_xyz_gated[22:12], +                    1'b0, maj_xyz_gated[10:0], +                    alu_cin +                }; +            assign int_carry_in[3:1] = 3'b000; +            assign ext_carry_out = { +                    int_carry_out[3], +                    maj_xyz_gated[35] ^ int_carry_out[2], +                    maj_xyz_gated[23] ^ int_carry_out[1], +                    maj_xyz_gated[11] ^ int_carry_out[0] +                }; +            assign carryout_reset = 4'b0000; +        end else if (USE_SIMD == "TWO24") begin +            assign maj_xyz_simd_gated = { +                    maj_xyz_gated[47:24], +                    1'b0, maj_xyz_gated[22:0], +                    alu_cin +                }; +            assign int_carry_in[3:1] = {int_carry_out[2], 1'b0, int_carry_out[0]}; +            assign ext_carry_out = { +                    int_carry_out[3], +                    1'bx, +                    maj_xyz_gated[23] ^ int_carry_out[1], +                    1'bx +                }; +            assign carryout_reset = 4'b0x0x; +        end else begin +            assign maj_xyz_simd_gated = {maj_xyz_gated, alu_cin}; +            assign int_carry_in[3:1] = int_carry_out[2:0]; +            assign ext_carry_out = { +                    int_carry_out[3], +                    3'bxxx +                }; +            assign carryout_reset = 4'b0xxx; +        end + +        genvar i; +        for (i = 0; i < 4; i = i + 1) +            assign {int_carry_out[i], alu_sum[i*12 +: 12]} = {1'b0, maj_xyz_simd_gated[i*12 +: ((i == 3) ? 13 : 12)]} +                                                              + xor_xyz_muxed[i*12 +: 12] + int_carry_in[i]; +    endgenerate + +    wire signed [47:0] Pd = ALUMODEr[1] ? ~alu_sum : alu_sum; +    initial P = 48'b0; +    initial CARRYOUT = carryout_reset; +    initial CARRYCASCOUT = 1'b0; +    initial MULTSIGNOUT = 1'b0; +    wire [3:0] CARRYOUTd = (OPMODEr[3:0] == 4'b0101 || ALUMODEr[3:2] != 2'b00) ? 4'bxxxx : +                           ((ALUMODEr[0] & ALUMODEr[1]) ? ~ext_carry_out : ext_carry_out); +    wire CARRYCASCOUTd = ext_carry_out[3]; +    wire MULTSIGNOUTd = Mrx[42]; + +    generate +        if (PREG == 1) begin +            always @(posedge CLK) +                if (RSTP) begin +                    P <= 48'b0; +                    CARRYOUT <= carryout_reset; +                    CARRYCASCOUT <= 1'b0; +                    MULTSIGNOUT <= 1'b0; +                end else if (CEP) begin +                    P <= Pd; +                    CARRYOUT <= CARRYOUTd; +                    CARRYCASCOUT <= CARRYCASCOUTd; +                    MULTSIGNOUT <= MULTSIGNOUTd; +                end +        end else begin +            always @* begin +                P = Pd; +                CARRYOUT = CARRYOUTd; +                CARRYCASCOUT = CARRYCASCOUTd; +                MULTSIGNOUT = MULTSIGNOUTd; +            end +        end +    endgenerate + +    assign PCOUT = P; + +endmodule diff --git a/techlibs/xilinx/cells_xtra.v b/techlibs/xilinx/cells_xtra.v index a6669b872..b8abdda64 100644 --- a/techlibs/xilinx/cells_xtra.v +++ b/techlibs/xilinx/cells_xtra.v @@ -137,89 +137,6 @@ module DNA_PORT (...);      input SHIFT;  endmodule -module DSP48E1 (...); -    parameter integer ACASCREG = 1; -    parameter integer ADREG = 1; -    parameter integer ALUMODEREG = 1; -    parameter integer AREG = 1; -    parameter AUTORESET_PATDET = "NO_RESET"; -    parameter A_INPUT = "DIRECT"; -    parameter integer BCASCREG = 1; -    parameter integer BREG = 1; -    parameter B_INPUT = "DIRECT"; -    parameter integer CARRYINREG = 1; -    parameter integer CARRYINSELREG = 1; -    parameter integer CREG = 1; -    parameter integer DREG = 1; -    parameter integer INMODEREG = 1; -    parameter integer MREG = 1; -    parameter integer OPMODEREG = 1; -    parameter integer PREG = 1; -    parameter SEL_MASK = "MASK"; -    parameter SEL_PATTERN = "PATTERN"; -    parameter USE_DPORT = "FALSE"; -    parameter USE_MULT = "MULTIPLY"; -    parameter USE_PATTERN_DETECT = "NO_PATDET"; -    parameter USE_SIMD = "ONE48"; -    parameter [47:0] MASK = 48'h3FFFFFFFFFFF; -    parameter [47:0] PATTERN = 48'h000000000000; -    parameter [3:0] IS_ALUMODE_INVERTED = 4'b0; -    parameter [0:0] IS_CARRYIN_INVERTED = 1'b0; -    parameter [0:0] IS_CLK_INVERTED = 1'b0; -    parameter [4:0] IS_INMODE_INVERTED = 5'b0; -    parameter [6:0] IS_OPMODE_INVERTED = 7'b0; -    output [29:0] ACOUT; -    output [17:0] BCOUT; -    output CARRYCASCOUT; -    output [3:0] CARRYOUT; -    output MULTSIGNOUT; -    output OVERFLOW; -    output [47:0] P; -    output PATTERNBDETECT; -    output PATTERNDETECT; -    output [47:0] PCOUT; -    output UNDERFLOW; -    input [29:0] A; -    input [29:0] ACIN; -    input [3:0] ALUMODE; -    input [17:0] B; -    input [17:0] BCIN; -    input [47:0] C; -    input CARRYCASCIN; -    input CARRYIN; -    input [2:0] CARRYINSEL; -    input CEA1; -    input CEA2; -    input CEAD; -    input CEALUMODE; -    input CEB1; -    input CEB2; -    input CEC; -    input CECARRYIN; -    input CECTRL; -    input CED; -    input CEINMODE; -    input CEM; -    input CEP; -    (* clkbuf_sink *) -    input CLK; -    input [24:0] D; -    input [4:0] INMODE; -    input MULTSIGNIN; -    input [6:0] OPMODE; -    input [47:0] PCIN; -    input RSTA; -    input RSTALLCARRYIN; -    input RSTALUMODE; -    input RSTB; -    input RSTC; -    input RSTCTRL; -    input RSTD; -    input RSTINMODE; -    input RSTM; -    input RSTP; -endmodule -  module EFUSE_USR (...);      parameter [31:0] SIM_EFUSE_VALUE = 32'h00000000;      output [31:0] EFUSEUSR; diff --git a/techlibs/xilinx/dsp_map.v b/techlibs/xilinx/dsp_map.v new file mode 100644 index 000000000..cc37f0085 --- /dev/null +++ b/techlibs/xilinx/dsp_map.v @@ -0,0 +1,48 @@ +module \$__MUL25X18 (input [24:0] A, input [17:0] B, output [42:0] Y); +	parameter A_SIGNED = 0; +	parameter B_SIGNED = 0; +	parameter A_WIDTH = 0; +	parameter B_WIDTH = 0; +	parameter Y_WIDTH = 0; + +	wire [47:0] P_48; +	DSP48E1 #( +		// Disable all registers +		.ACASCREG(0), +		.ADREG(0), +		.A_INPUT("DIRECT"), +		.ALUMODEREG(0), +		.AREG(0), +		.BCASCREG(0), +		.B_INPUT("DIRECT"), +		.BREG(0), +		.CARRYINREG(0), +		.CARRYINSELREG(0), +		.CREG(0), +		.DREG(0), +		.INMODEREG(0), +		.MREG(0), +		.OPMODEREG(0), +		.PREG(0), +		.USE_MULT("MULTIPLY"), +		.USE_SIMD("ONE48") +	) _TECHMAP_REPLACE_ ( +		//Data path +		.A({{5{A[24]}}, A}), +		.B(B), +		.C(48'b0), +		.D(24'b0), +		.P(P_48), + +		.INMODE(5'b00000), +		.ALUMODE(4'b0000), +		.OPMODE(7'b000101), +		.CARRYINSEL(3'b000), + +		.ACIN(30'b0), +		.BCIN(18'b0), +		.PCIN(48'b0), +		.CARRYIN(1'b0) +	); +	assign Y = P_48; +endmodule diff --git a/techlibs/xilinx/synth_xilinx.cc b/techlibs/xilinx/synth_xilinx.cc index e0e81ef1d..3d92c3e2c 100644 --- a/techlibs/xilinx/synth_xilinx.cc +++ b/techlibs/xilinx/synth_xilinx.cc @@ -81,6 +81,8 @@ struct SynthXilinxPass : public ScriptPass  		log("    -nowidelut\n");  		log("        do not use MUXF[78] resources to implement LUTs larger than LUT6s\n");  		log("\n"); +		log("    -nodsp\n"); +		log("        do not use DSP48E1s to implement multipliers and associated logic\n");  		log("    -iopad\n");  		log("        enable I/O buffer insertion (selected automatically by -ise)\n");  		log("\n"); @@ -116,7 +118,7 @@ struct SynthXilinxPass : public ScriptPass  	}  	std::string top_opt, edif_file, blif_file, family; -	bool flatten, retime, vpr, ise, iopad, noiopad, noclkbuf, nobram, nolutram, nosrl, nocarry, nowidelut, abc9; +	bool flatten, retime, vpr, ise, iopad, noiopad, noclkbuf, nobram, nolutram, nosrl, nocarry, nowidelut, nodsp, abc9;  	bool flatten_before_abc;  	int widemux; @@ -139,6 +141,7 @@ struct SynthXilinxPass : public ScriptPass  		nosrl = false;  		nocarry = false;  		nowidelut = false; +		nodsp = false;  		abc9 = false;  		flatten_before_abc = false;  		widemux = 0; @@ -240,6 +243,10 @@ struct SynthXilinxPass : public ScriptPass  				abc9 = true;  				continue;  			} +			if (args[argidx] == "-nodsp") { +				nodsp = true; +				continue; +			}  			break;  		}  		extra_args(args, argidx, design); @@ -293,10 +300,10 @@ struct SynthXilinxPass : public ScriptPass  			run(stringf("hierarchy -check %s", top_opt.c_str()));  		} -		if (check_label("coarse")) { +		if (check_label("prepare")) {  			run("proc"); -			if (help_mode || flatten) -				run("flatten", "(if -flatten)"); +			if (flatten || help_mode) +				run("flatten", "(with '-flatten')");  			run("opt_expr");  			run("opt_clean");  			run("check"); @@ -320,6 +327,18 @@ struct SynthXilinxPass : public ScriptPass  			}  			run("techmap -map +/cmp2lut.v -D LUT_WIDTH=6"); +		} + +		if (check_label("map_dsp"), "(skip if '-nodsp')") { +			if (!nodsp || help_mode) { +				// NB: Xilinx multipliers are signed only +				run("techmap -map +/mul2dsp.v -map +/xilinx/dsp_map.v -D DSP_A_MAXWIDTH=25 -D DSP_A_MAXWIDTH_PARTIAL=18 -D DSP_B_MAXWIDTH=18 -D DSP_SIGNEDONLY=1 -D DSP_NAME=$__MUL25X18"); +				run("xilinx_dsp"); +				run("chtype -set $mul t:$__soft_mul"); +			} +		} + +		if (check_label("coarse")) {  			run("alumacc");  			run("share");  			run("opt"); diff --git a/techlibs/xilinx/tests/.gitignore b/techlibs/xilinx/tests/.gitignore index 496b87461..ef3699bd2 100644 --- a/techlibs/xilinx/tests/.gitignore +++ b/techlibs/xilinx/tests/.gitignore @@ -4,3 +4,8 @@ bram1_[0-9]*/  bram2.log  bram2_syn.v  bram2_tb +dsp_work*/ +test_dsp_model_ref.v +test_dsp_model_uut.v +test_dsp_model +*.vcd diff --git a/techlibs/xilinx/tests/test_dsp_model.sh b/techlibs/xilinx/tests/test_dsp_model.sh new file mode 100644 index 000000000..2acd97eb4 --- /dev/null +++ b/techlibs/xilinx/tests/test_dsp_model.sh @@ -0,0 +1,14 @@ +#!/bin/bash +set -ex +sed 's/DSP48E1/DSP48E1_UUT/; /DSP48E1_UUT/,/endmodule/ p; d;' < ../cells_sim.v > test_dsp_model_uut.v +if [ ! -f "test_dsp_model_ref.v" ]; then +	cat /opt/Xilinx/Vivado/2019.1/data/verilog/src/unisims/DSP48E1.v > test_dsp_model_ref.v +fi +for tb in simd24_preadd_noreg_nocasc simd12_preadd_noreg_nocasc \ +    mult_allreg_nopreadd_nocasc mult_noreg_nopreadd_nocasc  \ +	mult_allreg_preadd_nocasc mult_noreg_preadd_nocasc mult_inreg_preadd_nocasc \ +	 +do +	iverilog -s $tb -s glbl -o test_dsp_model test_dsp_model.v test_dsp_model_uut.v test_dsp_model_ref.v /opt/Xilinx/Vivado/2019.1/data/verilog/src/glbl.v +	vvp -N ./test_dsp_model +done diff --git a/techlibs/xilinx/tests/test_dsp_model.v b/techlibs/xilinx/tests/test_dsp_model.v new file mode 100644 index 000000000..04d5b26ab --- /dev/null +++ b/techlibs/xilinx/tests/test_dsp_model.v @@ -0,0 +1,597 @@ +`timescale 1ns / 1ps + +module testbench; +    parameter integer ACASCREG = 1; +    parameter integer ADREG = 1; +    parameter integer ALUMODEREG = 1; +    parameter integer AREG = 1; +    parameter AUTORESET_PATDET = "NO_RESET"; +    parameter A_INPUT = "DIRECT"; +    parameter integer BCASCREG = 1; +    parameter integer BREG = 1; +    parameter B_INPUT = "DIRECT"; +    parameter integer CARRYINREG = 1; +    parameter integer CARRYINSELREG = 1; +    parameter integer CREG = 1; +    parameter integer DREG = 1; +    parameter integer INMODEREG = 1; +    parameter integer MREG = 1; +    parameter integer OPMODEREG = 1; +    parameter integer PREG = 1; +    parameter SEL_MASK = "MASK"; +    parameter SEL_PATTERN = "PATTERN"; +    parameter USE_DPORT = "FALSE"; +    parameter USE_MULT = "MULTIPLY"; +    parameter USE_PATTERN_DETECT = "NO_PATDET"; +    parameter USE_SIMD = "ONE48"; +    parameter [47:0] MASK = 48'h3FFFFFFFFFFF; +    parameter [47:0] PATTERN = 48'h000000000000; +    parameter [3:0] IS_ALUMODE_INVERTED = 4'b0; +    parameter [0:0] IS_CARRYIN_INVERTED = 1'b0; +    parameter [0:0] IS_CLK_INVERTED = 1'b0; +    parameter [4:0] IS_INMODE_INVERTED = 5'b0; +    parameter [6:0] IS_OPMODE_INVERTED = 7'b0; + +	reg CLK; +	reg CEA1, CEA2, CEAD, CEALUMODE, CEB1, CEB2, CEC, CECARRYIN, CECTRL; +	reg CED, CEINMODE, CEM, CEP; +	reg RSTA, RSTALLCARRYIN, RSTALUMODE, RSTB, RSTC, RSTCTRL, RSTD, RSTINMODE, RSTM, RSTP; +	reg [29:0] A, ACIN; +	reg [17:0] B, BCIN; +	reg [47:0] C; +	reg [24:0] D; +	reg [47:0] PCIN; +	reg [3:0] ALUMODE; +	reg [2:0] CARRYINSEL; +	reg [4:0] INMODE; +	reg [6:0] OPMODE; +	reg CARRYCASCIN, CARRYIN, MULTSIGNIN; + +    output [29:0] ACOUT, REF_ACOUT; +    output [17:0] BCOUT, REF_BCOUT; +    output CARRYCASCOUT, REF_CARRYCASCOUT; +    output [3:0] CARRYOUT, REF_CARRYOUT; +    output MULTSIGNOUT, REF_MULTSIGNOUT; +    output OVERFLOW, REF_OVERFLOW; +    output [47:0] P, REF_P; +    output PATTERNBDETECT, REF_PATTERNBDETECT; +    output PATTERNDETECT, REF_PATTERNDETECT; +    output [47:0] PCOUT, REF_PCOUT; +    output UNDERFLOW, REF_UNDERFLOW; + +	integer errcount = 0; + +	reg ERROR_FLAG = 0; + +	task clkcycle; +		begin +			#5; +			CLK = ~CLK; +			#10; +			CLK = ~CLK; +			#2; +			ERROR_FLAG = 0; +			if (REF_P !== P) begin +				$display("ERROR at %1t: REF_P=%b UUT_P=%b DIFF=%b", $time, REF_P, P, REF_P ^ P); +				errcount = errcount + 1; +				ERROR_FLAG = 1; +			end +			if (REF_CARRYOUT !== CARRYOUT) begin +				$display("ERROR at %1t: REF_CARRYOUT=%b UUT_CARRYOUT=%b", $time, REF_CARRYOUT, CARRYOUT); +				errcount = errcount + 1; +				ERROR_FLAG = 1; +			end +			#3; +		end +	endtask + +	reg config_valid = 0; +	task drc; +		begin +			config_valid = 1; +			if (AREG != 2 && INMODE[0]) config_valid = 0; +			if (BREG != 2 && INMODE[4]) config_valid = 0; + +			if (USE_SIMD != "ONE48" && OPMODE[3:0] == 4'b0101) config_valid = 0; + +			if (OPMODE[1:0] == 2'b10 && PREG != 1) config_valid = 0; +			if ((OPMODE[3:2] == 2'b01) ^ (OPMODE[1:0] == 2'b01) == 1'b1) config_valid = 0; +			if ((OPMODE[6:4] == 3'b010 || OPMODE[6:4] == 3'b110) && PREG != 1) config_valid = 0; +			if ((OPMODE[6:4] == 3'b100) && (PREG != 1 || OPMODE[3:0] != 4'b1000 || ALUMODE[3:2] == 2'b01 || ALUMODE[3:2] == 2'b11)) config_valid = 0; +			if ((CARRYINSEL == 3'b100 || CARRYINSEL == 3'b101 || CARRYINSEL == 3'b111) && (PREG != 1)) config_valid = 0; +			if (OPMODE[6:4] == 3'b111) config_valid = 0; +			if ((OPMODE[3:0] == 4'b0101) && CARRYINSEL == 3'b010) config_valid = 0; +			if (CARRYINSEL == 3'b000 && OPMODE == 7'b1001000) config_valid = 0; + +			if ((ALUMODE[3:2] == 2'b01 || ALUMODE[3:2] == 2'b11) && OPMODE[3:2] != 2'b00 && OPMODE[3:2] != 2'b10) config_valid = 0; + + +		end +	endtask + +	initial begin +		$dumpfile("test_dsp_model.vcd"); +		$dumpvars(0, testbench); + +		#2; +		CLK = 1'b0; +		{CEA1, CEA2, CEAD, CEALUMODE, CEB1, CEB2, CEC, CECARRYIN, CECTRL} = 9'b111111111; +		{CED, CEINMODE, CEM, CEP} = 4'b1111; + +		{A, B, C, D} = 0; +		{ACIN, BCIN, PCIN} = 0; +		{ALUMODE, CARRYINSEL, INMODE} = 0; +		{OPMODE, CARRYCASCIN, CARRYIN, MULTSIGNIN} = 0; + +		{RSTA, RSTALLCARRYIN, RSTALUMODE, RSTB, RSTC, RSTCTRL, RSTD, RSTINMODE, RSTM, RSTP} = ~0; +		repeat (10) begin +			#10; +			CLK = 1'b1; +			#10; +			CLK = 1'b0; +			#10; +			CLK = 1'b1; +			#10; +			CLK = 1'b0; +		end +		{RSTA, RSTALLCARRYIN, RSTALUMODE, RSTB, RSTC, RSTCTRL, RSTD, RSTINMODE, RSTM, RSTP} = 0; + +		repeat (10000) begin +			clkcycle; +			config_valid = 0; +			while (!config_valid) begin +				A = $urandom; +				ACIN = $urandom; +				B = $urandom; +				BCIN = $urandom; +				C = {$urandom, $urandom}; +				D = $urandom; +				PCIN = {$urandom, $urandom}; + +				{CEA1, CEA2, CEAD, CEALUMODE, CEB1, CEB2, CEC, CECARRYIN, CECTRL} = $urandom | $urandom | $urandom; +				{CED, CEINMODE, CEM, CEP} = $urandom | $urandom | $urandom | $urandom; + +				// Otherwise we can accidentally create illegal configs +				CEINMODE = CECTRL; +				CEALUMODE = CECTRL; + +				{RSTA, RSTALLCARRYIN, RSTALUMODE, RSTB, RSTC, RSTCTRL, RSTD, RSTINMODE, RSTM, RSTP} = $urandom & $urandom & $urandom & $urandom & $urandom & $urandom; +				{ALUMODE, INMODE} = $urandom; +				CARRYINSEL = $urandom & $urandom & $urandom; +				OPMODE = $urandom;  +				if ($urandom & 1'b1) +					OPMODE[3:0] = 4'b0101; // test multiply more than other modes +				{CARRYCASCIN, CARRYIN, MULTSIGNIN} = $urandom; + +				// So few valid options in these modes, just force one valid option +				if (CARRYINSEL == 3'b001) OPMODE = 7'b1010101; +				if (CARRYINSEL == 3'b010) OPMODE = 7'b0001010; +				if (CARRYINSEL == 3'b011) OPMODE = 7'b0011011; +				if (CARRYINSEL == 3'b100) OPMODE = 7'b0110011; +				if (CARRYINSEL == 3'b101) OPMODE = 7'b0011010; +				if (CARRYINSEL == 3'b110) OPMODE = 7'b0010101; +				if (CARRYINSEL == 3'b111) OPMODE = 7'b0100011; + +				drc; +			end +		end + +		if (errcount == 0) begin +			$display("All tests passed."); +			$finish; +		end else begin +			$display("Caught %1d errors.", errcount); +			$stop; +		end +	end + +	DSP48E1 #( +		.ACASCREG           (ACASCREG), +		.ADREG              (ADREG), +		.ALUMODEREG         (ALUMODEREG), +		.AREG               (AREG), +		.AUTORESET_PATDET   (AUTORESET_PATDET), +		.A_INPUT            (A_INPUT), +		.BCASCREG           (BCASCREG), +		.BREG               (BREG), +		.B_INPUT            (B_INPUT), +		.CARRYINREG         (CARRYINREG), +		.CARRYINSELREG      (CARRYINSELREG), +		.CREG               (CREG), +		.DREG               (DREG), +		.INMODEREG          (INMODEREG), +		.MREG               (MREG), +		.OPMODEREG          (OPMODEREG), +		.PREG               (PREG), +		.SEL_MASK           (SEL_MASK), +		.SEL_PATTERN        (SEL_PATTERN), +		.USE_DPORT          (USE_DPORT), +		.USE_MULT           (USE_MULT), +		.USE_PATTERN_DETECT (USE_PATTERN_DETECT), +		.USE_SIMD           (USE_SIMD), +		.MASK               (MASK), +		.PATTERN            (PATTERN), +		.IS_ALUMODE_INVERTED(IS_ALUMODE_INVERTED), +		.IS_CARRYIN_INVERTED(IS_CARRYIN_INVERTED), +		.IS_CLK_INVERTED    (IS_CLK_INVERTED), +		.IS_INMODE_INVERTED (IS_INMODE_INVERTED), +		.IS_OPMODE_INVERTED (IS_OPMODE_INVERTED) +	) ref ( +		.ACOUT         (REF_ACOUT), +		.BCOUT         (REF_BCOUT), +		.CARRYCASCOUT  (REF_CARRYCASCOUT), +		.CARRYOUT      (REF_CARRYOUT), +		.MULTSIGNOUT   (REF_MULTSIGNOUT), +		.OVERFLOW      (REF_OVERFLOW), +		.P             (REF_P), +		.PATTERNBDETECT(REF_PATTERNBDETECT), +		.PATTERNDETECT (REF_PATTERNDETECT), +		.PCOUT         (REF_PCOUT), +		.UNDERFLOW     (REF_UNDERFLOW), +		.A             (A), +		.ACIN          (ACIN), +		.ALUMODE       (ALUMODE), +		.B             (B), +		.BCIN          (BCIN), +		.C             (C), +		.CARRYCASCIN   (CARRYCASCIN), +		.CARRYINSEL    (CARRYINSEL), +		.CEA1          (CEA1), +		.CEA2          (CEA2), +		.CEAD          (CEAD), +		.CEALUMODE     (CEALUMODE), +		.CEB1          (CEB1), +		.CEB2          (CEB2), +		.CEC           (CEC), +		.CECARRYIN     (CECARRYIN), +		.CECTRL        (CECTRL), +		.CED           (CED), +		.CEINMODE      (CEINMODE), +		.CEM           (CEM), +		.CEP           (CEP), +		.CLK           (CLK), +		.D             (D), +		.INMODE        (INMODE), +		.MULTSIGNIN    (MULTSIGNIN), +		.OPMODE        (OPMODE), +		.PCIN          (PCIN), +		.RSTA          (RSTA), +		.RSTALLCARRYIN (RSTALLCARRYIN), +		.RSTALUMODE    (RSTALUMODE), +		.RSTB          (RSTB), +		.RSTC          (RSTC), +		.RSTCTRL       (RSTCTRL), +		.RSTD          (RSTD), +		.RSTINMODE     (RSTINMODE), +		.RSTM          (RSTM), +		.RSTP          (RSTP) +	); + +	DSP48E1_UUT #( +		.ACASCREG           (ACASCREG), +		.ADREG              (ADREG), +		.ALUMODEREG         (ALUMODEREG), +		.AREG               (AREG), +		.AUTORESET_PATDET   (AUTORESET_PATDET), +		.A_INPUT            (A_INPUT), +		.BCASCREG           (BCASCREG), +		.BREG               (BREG), +		.B_INPUT            (B_INPUT), +		.CARRYINREG         (CARRYINREG), +		.CARRYINSELREG      (CARRYINSELREG), +		.CREG               (CREG), +		.DREG               (DREG), +		.INMODEREG          (INMODEREG), +		.MREG               (MREG), +		.OPMODEREG          (OPMODEREG), +		.PREG               (PREG), +		.SEL_MASK           (SEL_MASK), +		.SEL_PATTERN        (SEL_PATTERN), +		.USE_DPORT          (USE_DPORT), +		.USE_MULT           (USE_MULT), +		.USE_PATTERN_DETECT (USE_PATTERN_DETECT), +		.USE_SIMD           (USE_SIMD), +		.MASK               (MASK), +		.PATTERN            (PATTERN), +		.IS_ALUMODE_INVERTED(IS_ALUMODE_INVERTED), +		.IS_CARRYIN_INVERTED(IS_CARRYIN_INVERTED), +		.IS_CLK_INVERTED    (IS_CLK_INVERTED), +		.IS_INMODE_INVERTED (IS_INMODE_INVERTED), +		.IS_OPMODE_INVERTED (IS_OPMODE_INVERTED) +	) uut ( +		.ACOUT         (ACOUT), +		.BCOUT         (BCOUT), +		.CARRYCASCOUT  (CARRYCASCOUT), +		.CARRYOUT      (CARRYOUT), +		.MULTSIGNOUT   (MULTSIGNOUT), +		.OVERFLOW      (OVERFLOW), +		.P             (P), +		.PATTERNBDETECT(PATTERNBDETECT), +		.PATTERNDETECT (PATTERNDETECT), +		.PCOUT         (PCOUT), +		.UNDERFLOW     (UNDERFLOW), +		.A             (A), +		.ACIN          (ACIN), +		.ALUMODE       (ALUMODE), +		.B             (B), +		.BCIN          (BCIN), +		.C             (C), +		.CARRYCASCIN   (CARRYCASCIN), +		.CARRYINSEL    (CARRYINSEL), +		.CEA1          (CEA1), +		.CEA2          (CEA2), +		.CEAD          (CEAD), +		.CEALUMODE     (CEALUMODE), +		.CEB1          (CEB1), +		.CEB2          (CEB2), +		.CEC           (CEC), +		.CECARRYIN     (CECARRYIN), +		.CECTRL        (CECTRL), +		.CED           (CED), +		.CEINMODE      (CEINMODE), +		.CEM           (CEM), +		.CEP           (CEP), +		.CLK           (CLK), +		.D             (D), +		.INMODE        (INMODE), +		.MULTSIGNIN    (MULTSIGNIN), +		.OPMODE        (OPMODE), +		.PCIN          (PCIN), +		.RSTA          (RSTA), +		.RSTALLCARRYIN (RSTALLCARRYIN), +		.RSTALUMODE    (RSTALUMODE), +		.RSTB          (RSTB), +		.RSTC          (RSTC), +		.RSTCTRL       (RSTCTRL), +		.RSTD          (RSTD), +		.RSTINMODE     (RSTINMODE), +		.RSTM          (RSTM), +		.RSTP          (RSTP) +	); +endmodule + +module mult_noreg_nopreadd_nocasc; +	testbench #( +		.ACASCREG           (0), +		.ADREG              (0), +		.ALUMODEREG         (0), +		.AREG               (0), +		.AUTORESET_PATDET   ("NO_RESET"), +		.A_INPUT            ("DIRECT"), +		.BCASCREG           (0), +		.BREG               (0), +		.B_INPUT            ("DIRECT"), +		.CARRYINREG         (0), +		.CARRYINSELREG      (0), +		.CREG               (0), +		.DREG               (0), +		.INMODEREG          (0), +		.MREG               (0), +		.OPMODEREG          (0), +		.PREG               (0), +		.SEL_MASK           ("MASK"), +		.SEL_PATTERN        ("PATTERN"), +		.USE_DPORT          ("FALSE"), +		.USE_MULT           ("DYNAMIC"), +		.USE_PATTERN_DETECT ("NO_PATDET"), +		.USE_SIMD           ("ONE48"), +		.MASK               (48'h3FFFFFFFFFFF), +		.PATTERN            (48'h000000000000), +		.IS_ALUMODE_INVERTED(4'b0), +		.IS_CARRYIN_INVERTED(1'b0), +		.IS_CLK_INVERTED    (1'b0), +		.IS_INMODE_INVERTED (5'b0), +		.IS_OPMODE_INVERTED (7'b0) +	) testbench (); +endmodule + +module mult_allreg_nopreadd_nocasc; +	testbench #( +		.ACASCREG           (1), +		.ADREG              (1), +		.ALUMODEREG         (1), +		.AREG               (2), +		.AUTORESET_PATDET   ("NO_RESET"), +		.A_INPUT            ("DIRECT"), +		.BCASCREG           (1), +		.BREG               (2), +		.B_INPUT            ("DIRECT"), +		.CARRYINREG         (1), +		.CARRYINSELREG      (1), +		.CREG               (1), +		.DREG               (1), +		.INMODEREG          (1), +		.MREG               (1), +		.OPMODEREG          (1), +		.PREG               (1), +		.SEL_MASK           ("MASK"), +		.SEL_PATTERN        ("PATTERN"), +		.USE_DPORT          ("FALSE"), +		.USE_MULT           ("DYNAMIC"), +		.USE_PATTERN_DETECT ("NO_PATDET"), +		.USE_SIMD           ("ONE48"), +		.MASK               (48'h3FFFFFFFFFFF), +		.PATTERN            (48'h000000000000), +		.IS_ALUMODE_INVERTED(4'b0), +		.IS_CARRYIN_INVERTED(1'b0), +		.IS_CLK_INVERTED    (1'b0), +		.IS_INMODE_INVERTED (5'b0), +		.IS_OPMODE_INVERTED (7'b0) +	) testbench (); +endmodule + +module mult_noreg_preadd_nocasc; +	testbench #( +		.ACASCREG           (0), +		.ADREG              (0), +		.ALUMODEREG         (0), +		.AREG               (0), +		.AUTORESET_PATDET   ("NO_RESET"), +		.A_INPUT            ("DIRECT"), +		.BCASCREG           (0), +		.BREG               (0), +		.B_INPUT            ("DIRECT"), +		.CARRYINREG         (0), +		.CARRYINSELREG      (0), +		.CREG               (0), +		.DREG               (0), +		.INMODEREG          (0), +		.MREG               (0), +		.OPMODEREG          (0), +		.PREG               (0), +		.SEL_MASK           ("MASK"), +		.SEL_PATTERN        ("PATTERN"), +		.USE_DPORT          ("TRUE"), +		.USE_MULT           ("DYNAMIC"), +		.USE_PATTERN_DETECT ("NO_PATDET"), +		.USE_SIMD           ("ONE48"), +		.MASK               (48'h3FFFFFFFFFFF), +		.PATTERN            (48'h000000000000), +		.IS_ALUMODE_INVERTED(4'b0), +		.IS_CARRYIN_INVERTED(1'b0), +		.IS_CLK_INVERTED    (1'b0), +		.IS_INMODE_INVERTED (5'b0), +		.IS_OPMODE_INVERTED (7'b0) +	) testbench (); +endmodule + +module mult_allreg_preadd_nocasc; +	testbench #( +		.ACASCREG           (1), +		.ADREG              (1), +		.ALUMODEREG         (1), +		.AREG               (2), +		.AUTORESET_PATDET   ("NO_RESET"), +		.A_INPUT            ("DIRECT"), +		.BCASCREG           (1), +		.BREG               (2), +		.B_INPUT            ("DIRECT"), +		.CARRYINREG         (1), +		.CARRYINSELREG      (1), +		.CREG               (1), +		.DREG               (1), +		.INMODEREG          (1), +		.MREG               (1), +		.OPMODEREG          (1), +		.PREG               (1), +		.SEL_MASK           ("MASK"), +		.SEL_PATTERN        ("PATTERN"), +		.USE_DPORT          ("TRUE"), +		.USE_MULT           ("DYNAMIC"), +		.USE_PATTERN_DETECT ("NO_PATDET"), +		.USE_SIMD           ("ONE48"), +		.MASK               (48'h3FFFFFFFFFFF), +		.PATTERN            (48'h000000000000), +		.IS_ALUMODE_INVERTED(4'b0), +		.IS_CARRYIN_INVERTED(1'b0), +		.IS_CLK_INVERTED    (1'b0), +		.IS_INMODE_INVERTED (5'b0), +		.IS_OPMODE_INVERTED (7'b0) +	) testbench (); +endmodule + +module mult_inreg_preadd_nocasc; +	testbench #( +		.ACASCREG           (1), +		.ADREG              (0), +		.ALUMODEREG         (0), +		.AREG               (1), +		.AUTORESET_PATDET   ("NO_RESET"), +		.A_INPUT            ("DIRECT"), +		.BCASCREG           (1), +		.BREG               (1), +		.B_INPUT            ("DIRECT"), +		.CARRYINREG         (0), +		.CARRYINSELREG      (0), +		.CREG               (1), +		.DREG               (1), +		.INMODEREG          (0), +		.MREG               (0), +		.OPMODEREG          (0), +		.PREG               (0), +		.SEL_MASK           ("MASK"), +		.SEL_PATTERN        ("PATTERN"), +		.USE_DPORT          ("TRUE"), +		.USE_MULT           ("DYNAMIC"), +		.USE_PATTERN_DETECT ("NO_PATDET"), +		.USE_SIMD           ("ONE48"), +		.MASK               (48'h3FFFFFFFFFFF), +		.PATTERN            (48'h000000000000), +		.IS_ALUMODE_INVERTED(4'b0), +		.IS_CARRYIN_INVERTED(1'b0), +		.IS_CLK_INVERTED    (1'b0), +		.IS_INMODE_INVERTED (5'b0), +		.IS_OPMODE_INVERTED (7'b0) +	) testbench (); +endmodule + +module simd12_preadd_noreg_nocasc; +	testbench #( +		.ACASCREG           (0), +		.ADREG              (0), +		.ALUMODEREG         (0), +		.AREG               (0), +		.AUTORESET_PATDET   ("NO_RESET"), +		.A_INPUT            ("DIRECT"), +		.BCASCREG           (0), +		.BREG               (0), +		.B_INPUT            ("DIRECT"), +		.CARRYINREG         (0), +		.CARRYINSELREG      (0), +		.CREG               (0), +		.DREG               (0), +		.INMODEREG          (0), +		.MREG               (0), +		.OPMODEREG          (0), +		.PREG               (0), +		.SEL_MASK           ("MASK"), +		.SEL_PATTERN        ("PATTERN"), +		.USE_DPORT          ("TRUE"), +		.USE_MULT           ("DYNAMIC"), +		.USE_PATTERN_DETECT ("NO_PATDET"), +		.USE_SIMD           ("FOUR12"), +		.MASK               (48'h3FFFFFFFFFFF), +		.PATTERN            (48'h000000000000), +		.IS_ALUMODE_INVERTED(4'b0), +		.IS_CARRYIN_INVERTED(1'b0), +		.IS_CLK_INVERTED    (1'b0), +		.IS_INMODE_INVERTED (5'b0), +		.IS_OPMODE_INVERTED (7'b0) +	) testbench (); +endmodule + + +module simd24_preadd_noreg_nocasc; +	testbench #( +		.ACASCREG           (0), +		.ADREG              (0), +		.ALUMODEREG         (0), +		.AREG               (0), +		.AUTORESET_PATDET   ("NO_RESET"), +		.A_INPUT            ("DIRECT"), +		.BCASCREG           (0), +		.BREG               (0), +		.B_INPUT            ("DIRECT"), +		.CARRYINREG         (0), +		.CARRYINSELREG      (0), +		.CREG               (0), +		.DREG               (0), +		.INMODEREG          (0), +		.MREG               (0), +		.OPMODEREG          (0), +		.PREG               (0), +		.SEL_MASK           ("MASK"), +		.SEL_PATTERN        ("PATTERN"), +		.USE_DPORT          ("TRUE"), +		.USE_MULT           ("DYNAMIC"), +		.USE_PATTERN_DETECT ("NO_PATDET"), +		.USE_SIMD           ("TWO24"), +		.MASK               (48'h3FFFFFFFFFFF), +		.PATTERN            (48'h000000000000), +		.IS_ALUMODE_INVERTED(4'b0), +		.IS_CARRYIN_INVERTED(1'b0), +		.IS_CLK_INVERTED    (1'b0), +		.IS_INMODE_INVERTED (5'b0), +		.IS_OPMODE_INVERTED (7'b0) +	) testbench (); +endmodule
\ No newline at end of file | 
