module mul #(
	parameter DW = `MUL_EXEPARAM_DW 
)
(
	input mul_exeparam_valid,
	output mul_execute_ready,
	input [DW-1:0] mul_exeparam,

	output mul_writeback_valid,
	output [63:0] mul_res_qout,
	output [(5+`RB)-1:0] mul_rd0_qout,

	//from regFile
	input [(64*`RP*32)-1:0] regFileX_read,

	input flush,
	input CLK,
	input RSTn		
);



	wire rv64m_mul, rv64m_mulh, rv64m_mullhsu, rv64m_mulhu, rv64m_div, rv64m_divu, rv64m_rem, rv64m_remu, rv64m_mulw, rv64m_divw, rv64m_divuw, rv64_remw, rv64m_remuw;

	wire [(5+`RB)-1:0] mul_rs1;
	wire [(5+`RB)-1:0] mul_rs2;
	wire [(5+`RB)-1:0] mul_rd0_dnxt;


	assign {
			rv64m_mul,
			rv64m_mulh,
			rv64m_mullhsu,
			rv64m_mulhu,
			rv64m_div,
			rv64m_divu,
			rv64m_rem,
			rv64m_remu,
			rv64m_mulw,
			rv64m_divw,
			rv64m_divuw,
			rv64_remw,
			rv64m_remuw,

			mul_rd0_dnxt,
			mul_rs1,
			mul_rs2

			} = mul_exeparam;


	wire [63:0] src1 = regFileX_read[ 64*mul_rs1 +: 64];
	wire [63:0] src2 = regFileX_read[ 64*mul_rs2 +: 64];

	initial $warning("only can syn in FPGA in this verison");

	wire mul_fun = rv64m_mul | rv64m_mulh | rv64m_mullhsu | rv64m_mulhu | rv64m_mulw;



	wire mul_op1_sign = (rv64m_mul | rv64m_mulh | rv64m_mullhsu | rv64m_mulw) & ( rv64m_mulw ? src1[31] : src1[63]);
	wire mul_op2_sign = (rv64m_mul | rv64m_mulh | rv64m_mulw) & ( rv64m_mulw ? src2[31] : src2[63]);

	wire [64:0] mul_op1 = rv64m_mulw ? { {33{mul_op1_sign}}, src1[31:0] } : { mul_op1_sign, src1 };
	wire [64:0] mul_op2 = rv64m_mulw ? { {33{mul_op2_sign}}, src2[31:0] } : { mul_op2_sign, src2 };

	wire signed [127:0] muls = $signed(mul_op1) * $signed(mul_op2);







	//div
	wire div_fun = 
		rv64m_div
		| rv64m_divu
		| rv64m_rem
		| rv64m_remu
		| rv64m_divw
		| rv64m_divuw
		| rv64_remw
		| rv64m_remuw;

	wire is32w = rv64m_divw | rv64m_divuw | rv64_remw | rv64m_remuw;
	wire isUsi = rv64m_divu | rv64m_remu | rv64m_divuw | rv64m_remuw;


	wire [127:0] dividend_dnxt, dividend_qout;
	wire [63:0] divisor_dnxt, divisor_qout;
	wire [63:0] div_res_dnxt, div_res_qout;
	wire [6:0] div_cnt_dnxt, div_cnt_qout;

	wire [63:0] dividend_load = isUsi ? 
									src1 : 
									( 
										is32w ?  
											{ 32'b0, (src1[31] ? $signed(-src1[31:0]) : src1[31:0])} : 
											(src1[63] ? $signed(-src1) : src1)
									);

	wire [63:0] divisor_load = isUsi ? 
									src2 : 
									( 
										is32w ?  
										{ {32'b0}, (src2[31] ? $signed(-src2[31:0]) : src2[31:0])} : 
											(src2[63] ? $signed(-src2) : src2)
									);


	wire div_cmp;
	wire [127:0] dividend_shift;
	wire [127:0] divided;

	assign dividend_dnxt = 
		(mul_exeparam_valid & div_fun) ? 
			{64'd0, dividend_load} :
			((div_cnt_qout == 7'd64) ? dividend_qout : divided)
			;

	assign divisor_dnxt = (mul_exeparam_valid & div_fun) ? 
						divisor_load :
						divisor_qout;


	assign div_cnt_dnxt = (mul_exeparam_valid & div_fun) ? 
							7'd0 :
							(div_cnt_qout == 7'd65 ? div_cnt_qout : div_cnt_qout + 1);


	assign dividend_shift = dividend_qout << 1;
	assign div_cmp = dividend_shift[127:64] >= divisor_qout;
	assign divided = div_cmp ? 
						{(dividend_shift[127:64] - divisor_qout), dividend_shift[63:1], 1'b1} :
						dividend_shift;



	wire dividend_sign = isUsi ? 1'b0 : (is32w ? src1[31] : src1[63]);
	wire divisor_sign  = isUsi ? 1'b0 : (is32w ? src2[31] : src2[63]);
	wire div_by_zero = (src2 == 64'd0);
	wire div_overflow = ~isUsi & 
							(
								(is32w & (src1[31] & ~src1[30:0]) & (&src2[31:0]))
								|
								(~is32w & (src1[63] & ~src1[62:0]) & (&src2[63:0]))								
							);



	wire [63:0] quot = 
		({64{div_by_zero}} & {64{1'b1}})
		|
		({64{div_overflow}} & (is32w ? { {33{1'b1}}, 31'b0 } : {1'b1, 63'b0}))
		|
		(
			{64{(~div_by_zero)&(~div_overflow)}} &
				((dividend_sign^divisor_sign) ? $signed(-dividend_qout[63:0]) : dividend_qout[63:0])
		);

	wire [63:0] rema = 
		({64{div_by_zero}} & (is32w ? { {32{src1[31]}}, src1} : src1 ) )
		|
		({64{div_overflow}} & 64'd0 )
		|
		(
			{64{(~div_by_zero)&(~div_overflow)}} &
				((dividend_sign) ? $signed(-dividend_qout[127:64]): dividend_qout[127:64])
		);
		

	gen_dffr # (.DW(128)) dividend ( .dnxt(dividend_dnxt), .qout(dividend_qout), .CLK(CLK), .RSTn(RSTn));
	gen_dffr # (.DW(64)) divisor ( .dnxt(divisor_dnxt), .qout(divisor_qout), .CLK(CLK), .RSTn(RSTn));
	gen_dffr # (.DW(7)) div_cnt ( .dnxt(div_cnt_dnxt), .qout(div_cnt_qout), .CLK(CLK), .RSTn(RSTn));









	wire [63:0] mul_res_dnxt = {64{valid_dnxt}} & 
		(
			( {64{rv64m_mul}} & muls[63:0] )
			| ({64{rv64m_mulh}} & muls[127:64] )
			| ({64{rv64m_mullhsu}} & muls[127:64] )
			| ({64{rv64m_mulhu}} & muls[127:64] )
			| ({64{rv64m_div}} & quot)
			| ({64{rv64m_divu}} & quot)
			| ({64{rv64m_rem}} & rema)
			| ({64{rv64m_remu}} & rema)
			| ({64{rv64m_mulw}} & {{32{muls[31]}},muls[31:0]} )
			| ({64{rv64m_divw}} & {{32{quot[31]}}, quot[31:0]} )
			| ({64{rv64m_divuw}} & {{32{quot[31]}}, quot[31:0]} )
			| ({64{rv64_remw}} & { {32{rema[31]}}, rema[31:0] } )
			| ({64{rv64m_remuw}} & { {32{rema[31]}}, rema[31:0]})
		);



	wire valid_dnxt;

	wire mul_valid;
	wire div_valid;

	assign mul_valid = mul_exeparam_valid & mul_fun;
	assign div_valid = div_fun & 
						(
							(mul_exeparam_valid & (div_by_zero | div_overflow))
							|
							(div_cnt_qout == 7'd64 & ~mul_execute_ready)
						);


	assign valid_dnxt = (~flush) & (mul_valid | div_valid);


	wire ready_set;
	wire ready_rst;

	assign ready_set = (mul_valid | div_valid) | flush;
	assign ready_rst = ~ready_set & mul_exeparam_valid & ~flush;

	// assign ready_dnxt = (mul_exeparam_valid & 1'b0)
	// 					|
	// 					(valid_dnxt & 1'b1)
	// 					|
	// 					(~mul_exeparam_valid & ~valid_dnxt & mul_execute_ready);
	// 					|
	// 					flush


	gen_dffr # (.DW((5+`RB))) mul_rd0 ( .dnxt(mul_rd0_dnxt), .qout(mul_rd0_qout), .CLK(CLK), .RSTn(RSTn));
	gen_dffr # (.DW(64)) mul_res ( .dnxt(mul_res_dnxt), .qout(mul_res_qout), .CLK(CLK), .RSTn(RSTn));
	gen_dffr # (.DW(1)) valid ( .dnxt(valid_dnxt), .qout(mul_writeback_valid), .CLK(CLK), .RSTn(RSTn));
	gen_rsffr # (.DW(1), .rstValue(1'b1)) ready_rsffr ( .set_in(ready_set), .rst_in(ready_rst), .qout(mul_execute_ready), .CLK(CLK), .RSTn(RSTn));




endmodule