module cordic_exp_rtl
#(parameter	DATA_WIDTH = 32,    // Êý¾ÝÎ»¿í
  parameter	FRAC_WIDTH = 16,	// Ð¡Êý²¿·Ö
  parameter	EPSILON = 16,		// ÊÕÁ²ãÐÖµ
  parameter ITERATION = 8,  	// µü´ú´ÎÊý
  parameter ROM_LATENCY = 2,	// romµÄIPºË¶ÁÈ¡ÐèÒªÑÓÊ±
  parameter	DATA_UNIT = {{(DATA_WIDTH-FRAC_WIDTH-1){1'B0}}, 1'B1, {FRAC_WIDTH{1'B0}}}, // ¹Ì¶¨µÄµ¥Î»1 
  parameter	DATA_ZERO = {DATA_WIDTH{1'B0}},	// ¹Ì¶¨µÄ0Öµ
  parameter	DATA_LOF = (FRAC_WIDTH*11)<<(FRAC_WIDTH-4),	// ÏÂÒç ==> Ö±½Ó·µ»Ø0  e^-11 ==> 2^-16, 
  parameter	DATA_UOF = ((DATA_WIDTH-FRAC_WIDTH-1)*11)<<(FRAC_WIDTH-4)	// ÉÏÒç ==> Ö±½Ó·µ»Ø(2**31-1)  e^10.39 ==> 2^15, 
)
(
	input	wire								sys_clk, sys_rst_n,
	input	wire	signed	[DATA_WIDTH-1:0]	src_x,
	
	output	reg		signed	[DATA_WIDTH-1:0]	rho
);

	// ´æ´¢KnµÄÏµÊý±í£¬Ê¹ÓÃPython½Å±¾À´Éú³ÉÏàÓ¦µÄÊýÖµ
	wire	[DATA_WIDTH-1:0]	Kn_THETAn_address;
	wire	[DATA_WIDTH-1:0]	Kn;
	wire	[DATA_WIDTH-1:0]	THETAn;
	cordic_factor_exp_rom_ip 	exp_cordic_rom_ip_core	(.address(Kn_THETAn_address),.clock(sys_clk),.q({Kn, THETAn}));
	 //////////////////////////////////

	// ÒòÎªÒªÐ´³ÉÁ÷Ë®ÏßÐÍµÄCORDICÔËËã
	// ËùÒÔÐèÒª½¨Á¢Ò»¸ö¾Þ´óµÄregÕóÁÐ
	reg		signed 	[DATA_WIDTH-1:0]	Xn	[0:ITERATION-1];	// ÒòÎªÊÇÁ÷Ë®Ïß£¬ËùÒÔÐèÒª²»¶ÏµØ¡°ÒÆÎ»¡±µü´úÊäÈëµÄx(Ð¡Êý²¿·Ö)
	reg		signed 	[DATA_WIDTH-1:0]	In	[0:ITERATION-1];	// ÒòÎªÊÇÁ÷Ë®Ïß£¬ËùÒÔÐèÒª²»¶ÏµØ¡°ÒÆÎ»¡±µü´úÊäÈëµÄx(ÕûÊý²¿·Ö)
	reg		signed 	[DATA_WIDTH-1:0]	Zn	[0:ITERATION-1];
	reg									LOFn[0:ITERATION-1];	// ÏÂÒç
	reg									UOFn[0:ITERATION+2];	// ÉÏÒç
	// µü´ú´ÎÊý£¬ÒòÎªZnÊÇ¿ÉÄÜÖÐÍ¾ÊÕÁ²µÄ£¬ÐèÒª±ê×¢Ê²Ã´Ê±ºòÊÕÁ²ÁË
	reg				[DATA_WIDTH-1:0]	Nn	[0:ITERATION+1];	// Ò»¸ö³ÌÐòµÄbug£¬ÕâÀïµÄNnÓ¦¸Ã¼ÌÐø´«µÝ
	// »¹Òª¼ÇÂ¼Ðý×ªµÄ½Ç¶È
	reg		signed 	[DATA_WIDTH-1:0]	Tn	[0:ITERATION-1];
	reg		signed 	[DATA_WIDTH-1:0]	T0n	[0:ITERATION-1];	// ÕâÊÇÒª´ÓROMÀïÃæ¼ÓÔØµÄ
	reg		signed 	[DATA_WIDTH-1:0]	K0n	[0:ITERATION-1];	// ÕâÊÇÒª´ÓROMÀïÃæ¼ÓÔØµÄ
	// 
	reg		[4:0]	cstate;	// ×´Ì¬¼ÆÊýÆ÷
	parameter		IDLE = 0;	// ÏÐÖÃ×´Ì¬
	parameter		LOAD = 1;	// ¼ÓÔØROMÖÐµÄÊý¾Ý
	parameter		COMP = 2;	// Õý³£µÄ¹¤×÷/ÔËËã½×¶Î
	reg		[DATA_WIDTH-1:0]	timer_in_state;	// Ã¿¸ö½×¶ÎµÄ¼ÆÊýÆ÷
	reg		[DATA_WIDTH-1:0]	rom_address;	// ¶ÁÈ¡ROMµÄµØÖ·¼ÆÊýÆ÷
	// cordic µü´úÔËËã + Êý¾ÝÊäÈë&1/4ÏóÏÞÐ£Õý´¦Àí
	always @(posedge sys_clk)
		// ³õÊ¼»¯
		if(!sys_rst_n)
			init_system_task;
		// ·ñÔò¾ÍÊÇÕý³£µÄµü´ú¼ÆËã
		else
		begin
			case(cstate)
				IDLE:		prepare_load_task;
				LOAD:		execute_load_task;
				COMP:		execute_comp_task;	
				default:	init_system_task;
			endcase
		end
///////////////////////////////
// ÏÂÃæÊÇ¾ßÌåµÄtaskµÄÃèÊö
integer	n;
// Ê×ÏÈÊÇÏµÍ³³õÊ¼»¯µÄÃèÊö
task init_system_task;
begin
	cstate <= IDLE;	// Ê×ÏÈÇÐ»»µ½ÏÐÖÃ×´Ì¬
	// È»ºó¸´Î»ËùÓÐµÄ¼Ä´æÆ÷
	for(n=0; n<ITERATION; n=n+1)
	begin
		Xn[n] <= DATA_ZERO;
		Zn[n] <= DATA_ZERO;
		Nn[n] <= DATA_ZERO;
		Tn[n] <= DATA_ZERO;
		//
		LOFn[n] <= 0;
		UOFn[n] <= 0;
	end
	// ¼ÆÊýÆ÷¸´Î»
	timer_in_state <= DATA_ZERO;
	// ¶ÁÈ¡romµÄµØÖ·¼ÆÊýÆ÷ÇåÁã
	rom_address <= 0;
end
endtask
////////////////////
// È»ºóÊÇIDLE½×¶Î£¬²»Ö±½Ó½øÈëload½×¶Î£¬
// Ö÷ÒªÊÇÒòÎªromµÄ¶ÁÈ¡ÊÇÓÐlatencyÊ±¼äµÄ
task prepare_load_task;
begin	
	// Èç¹ûµÈ´ý¹»ÁË¾ÍÒªÌø³ö£¬½øÈëromÊý¾Ý¼ÓÔØ½×¶Î
	if(timer_in_state>=(ROM_LATENCY-1))
	begin
		cstate <= LOAD;
		// ¼ÆÊýÆ÷¸´Î»
		timer_in_state <= DATA_ZERO;
	end
	// ·ñÔò£¬¾ÍÒª¼ÌÐø¼ÓÔØROMÊý¾Ý
	else
	begin
		timer_in_state <= timer_in_state+1;
	end
	// rom¶ÁÈ¡²»ÒªÍ£
	rom_address <= rom_address+1;
end
endtask
// È»ºóÊÇLOAD½×¶Î£¬Ö´ÐÐloadÖ¸Áî
task execute_load_task;
begin	
	// ¼ÓÔØ¹»ÁË£¬¾ÍÒªÌø³ö£¬¿ÉÒÔ¿ªÊ¼¼ÆËãÁË
	if(timer_in_state>=(ITERATION))
	begin
		cstate <= COMP;
		// ¼ÆÊýÆ÷¸´Î»
		timer_in_state <= DATA_ZERO;
	end
	else
	begin
		// ·ñÔò£¬¼ÓÔØromÀïÃæµÄÊý¾Ý
		T0n[timer_in_state] <= THETAn;
		K0n[timer_in_state] <= Kn;
		rom_address <= rom_address+1;
		timer_in_state <= timer_in_state+1;
	end
end
endtask
// ÏÖÔÚÊÇÖØÍ·Ï·£¬¾ÍÊÇÕû¸öcoedicµü´ú¹ý³ÌÁË
task execute_comp_task;
begin
	// Ê×ÏÈÊÇÊäÈëÊý¾Ý
	// ·Ö¸îÊý¾ÝµÄÕûÊý - Ð¡Êý²¿·Ö
	Xn[0] <= {{(DATA_WIDTH-FRAC_WIDTH){1'B0}}, src_x[FRAC_WIDTH-1:0]};
	In[0] <= (src_x >>> FRAC_WIDTH);
	// ÉÏÒç/ÏÂÒçÅÐ±ð
	LOFn[0] <= src_x < (-DATA_LOF);
	UOFn[0] <= src_x >= (DATA_UOF);
	// ³õÊ¼»¯Z = 1
	Zn[0] <= DATA_UNIT;
	// È»ºóN = 0
	Nn[0] <= DATA_ZERO;
	// T=0
	Tn[0] <= DATA_ZERO;
	// È»ºóÊÇcordicµü´úµÄ¹ý³Ì£¬ÕâÀïÊ¹ÓÃforÑ­»·£¬·½±ãÐ´³ÌÐò£¬×¢Òâ×ÛºÏ½á¹û
	for(n=ITERATION-1; n>=1; n=n-1)
	begin
		// ÊäÈë X & I ÐèÒª²»¶ÏµØÒÆÎ»ÏÂÈ¥
		Xn[n] <= Xn[n-1];
		In[n] <= In[n-1];
		// Èç¹ûTn[n-1]>Xn[n-1]£¬ÄÇÃ´Ë³Ê±ÕëÐý×ª
		if(Tn[n-1]>(Xn[n-1]+EPSILON))
		begin
			Zn[n] <= Zn[n-1] - (Zn[n-1]>>>(n));
			Nn[n] <= Nn[n-1] + 1;	// ¼ÌÐøµü´ú£¬µü´ú´ÎÊý+1
			Tn[n] <= Tn[n-1] - T0n[n-1];	// ÐÞ¸Ä½Ç¶ÈÖµ
		end
		// Èç¹ûTn[n-1]<Xn[n-1]£¬ÄÇÃ´ÄæÊ±ÕëÐý×ª
		else if(Xn[n-1]>(Tn[n-1]+EPSILON))
		begin
			Zn[n] <= Zn[n-1] + (Zn[n-1]>>>(n));
			Nn[n] <= Nn[n-1] + 1;	// ¼ÌÐøµü´ú£¬µü´ú´ÎÊý+1
			Tn[n] <= Tn[n-1] + T0n[n-1];	// ÐÞ¸Ä½Ç¶ÈÖµ
		end
		// ·ñÔò¾ÍËµÃ÷ÊÕÁ²ÁË£¬Í£Ö¹µü´ú¹ý³Ì
		else
		begin
			Zn[n] <= Zn[n-1];
			Nn[n] <= Nn[n-1];
			Tn[n] <= Tn[n-1];
		end
		//
		LOFn[n] <= LOFn[n-1];
		UOFn[n] <= UOFn[n-1];
	end
	// Nn»¹ÔÚ´«µÝ
	Nn[ITERATION] <= Nn[ITERATION-1];
	Nn[ITERATION+1] <= Nn[ITERATION];
	// ÉÏÒçÖ¸±ê»¹ÔÚ´«µÝ
	UOFn[ITERATION] <= UOFn[ITERATION-1];
	UOFn[ITERATION+1] <= UOFn[ITERATION];
	UOFn[ITERATION+2] <= UOFn[ITERATION+1];
end
endtask
	//////////////////////////////////////////////////
	// ×îºó£¬¿¼ÂÇµ½romµÄipºË¶ÁÈ¡£¬ÐèÒª¸ø³öROMµÄ¶ÁÈ¡µØÖ·
	assign	Kn_THETAn_address = rom_address;
	// ÓÉÓÚÔÚKn[]ÀïÃæÑ°Ö· + Êä³öÐ£ÕýµÄ³Ë·¨ÔËËã£¬Ê®·ÖÏûºÄ×ÊÔ´£¬Ó°ÏìÊ±Ðò
	// ËùÒÔÕâÀïÏÈ°Ñ¸÷¸öÊý¾Ý¶¼¼Ä´æÒ»ÏÂ£¬ÄÇÃ´ºóÃæµÄ¾ÍÖ»ÊÇ³Ë·¨Ó°ÏìÊ±ÐòÁË
	reg		[DATA_WIDTH-1:0]	Kn_res [0:1];	// ÊÂÊµÖ¤Ã÷£¬Õâ¸öK0n»¹²»ÈçÖ±½Ó±ä³Éram£¡
	reg		[DATA_WIDTH-1:0]	Zn_res [0:1];
	reg		[DATA_WIDTH-1:0]	Xn_res [0:1];
	reg		[DATA_WIDTH-1:0]	In_res [0:1];
	always @(posedge sys_clk)
	begin
		Kn_res[0] <= K0n[Nn[ITERATION-1]-1];
		Zn_res[0] <= LOFn[ITERATION-1]? DATA_ZERO : UOFn[ITERATION-1]? {1'B0, {(DATA_WIDTH-1){1'B1}}} : Zn[ITERATION-1];	// Ôö¼ÓÉÏÒç/ÏÂÒçÖ¸Ê¾
		Xn_res[0] <= Xn[ITERATION-1];
		In_res[0] <= In[ITERATION-1];
		// ÎªÁËÊ±ÐòÒ²ÊÇÆ´ÁË
		Kn_res[1] <= Kn_res[0];
		Zn_res[1] <= Zn_res[0];
		Xn_res[1] <= Xn_res[0];
		In_res[1] <= In_res[0];
	end
	// È»ºóÊÇÐ£Õýrho£¬ÐèÒªKnÏµÊý
	// ×¢Òâµ½ÕâÀïµÄKn¶¼ÊÇ0~1µÄÏµÊý£¬ËùÒÔÎÒÃÇ´æµÄÊ±ºò£¬¾¡¹ÜÊÇ32bitµÄsigned±äÁ¿£¬Êµ¼ÊÖÐÓÃµ½ÁË(FRAC_WIDTH)-bit
	// Òò´Ë£¬×îºóÊä³öµÄÊ±ºò£¬ÐèÒª×öÒ»Ð©ÅÐ¶ÏµÄ
	reg		[2*DATA_WIDTH-1:0]	rho_reg [0:1];	// ÕâÊÇÐ£ÕýÏòÁ¿Ä£Êä³öµÄÊ±ºòµÄ¡°ÔÝ´æ±äÁ¿¡±£¬ÊÇ64-bitµÄ£¬×îºóÒª½ØµôLSB
	reg		[DATA_WIDTH-1:0]	x_reg [0:1];		// ÊäÈëÏòÁ¿µÄÒÆÎ»»º´æ£¬ÎªÁËÄÜ¹»ºÍÊä³örho½øÐÐ±È¶Ô
	// [0] ÊÇÃ»ÓÐÕûÊý²¿·ÖÊ±ºòµÄÊý¾ÝÖµ£¬ [1]ÊÇÓÐÕûÊý²¿·ÖÒÔºóµÄÊý¾ÝÖµ
	wire	[DATA_WIDTH-1:0]	int_part_exp_val;	// ÕûÊý²¿·ÖµÄ exp ÔËËã½á¹ûÊý¾ÝÖµ
	reg		[DATA_WIDTH-1:0]	x;			
	always @(posedge sys_clk)
	begin
		// Èç¹ûÃ»ÓÐÏòÉÏÒç³ö
		if(UOFn[ITERATION+1]==0)
		begin
			if(Nn[ITERATION+1]==0)
				rho_reg[0] <= Zn_res[1]*DATA_UNIT;			
			else
				rho_reg[0] <= Zn_res[1]*Kn_res[1];	// ×¢Òâ£¡ ÕâÀïÊÇÊ±Ðò·Ç³£²îµÄµØ·½£¬ÒªÏë°ì·¨µ÷Õû
		end
		// Èç¹ûÏòÉÏÒç³ö
		else 
			rho_reg[0] <= {1'B0, {(2*DATA_WIDTH-1){1'B1}}};
		// 
		x_reg[0] <= Xn_res[1]|(In_res[1]<<<FRAC_WIDTH);
		////////
		// ¼ÓÈëÕûÊý²¿·ÖµÄÐ£Õý
		x_reg[1] <= x_reg[0];
		// Èç¹ûÃ»ÓÐÏòÉÏÒç³ö
		if(UOFn[ITERATION+2]==0)
			rho_reg[1] <= rho_reg[0][2*DATA_WIDTH-1:FRAC_WIDTH]*int_part_exp_val;
		else
			rho_reg[1] <= (1<<(DATA_WIDTH+FRAC_WIDTH-1))-1;
		////////
		rho <= rho_reg[1][DATA_WIDTH+FRAC_WIDTH-1:FRAC_WIDTH];
		x <= x_reg[1];
	end
	// ´ÓROMÀïÃæ¶Á³öÕûÊý²¿·ÖµÄÖ¸ÊýÔËËã½á¹û	
	cordic_int_part_exp_rom_ip	int_part_mdl(.address(In_res[0]+128), .clock(sys_clk), .q(int_part_exp_val));
endmodule