//+FHDR--------------------------------------------------------------------------------------------------------
//  Company: 
//-----------------------------------------------------------------------------------------------------------------
//  File Name             :    IIR_Filter.v
//  Department            :    
//  Author                :    thfu
//  Author's Tel          :     
//-----------------------------------------------------------------------------------------------------------------
//  Relese History
//  Version     Date            Author          Description
//  0.4         2024-05-28      thfu
//2024-05-28 10:22:49 
//-----------------------------------------------------------------------------------------------------------------
//  Keywords            :       
//
//-----------------------------------------------------------------------------------------------------------------
//  Parameter
//
//-----------------------------------------------------------------------------------------------------------------
//  Purpose                 :
//                      
//-----------------------------------------------------------------------------------------------------------------
//  Target Device:        
//  Tool versions:        
//-----------------------------------------------------------------------------------------------------------------
//  Reuse Issues
//  Reset Strategy: 
//  Clock Domains: 
//  Critical Timing:
//  Asynchronous I/F:
//  Synthesizable (y/n): 
//  Other:
//-FHDR--------------------------------------------------------------------------------------------------------
module  IIR_Filter #(
 parameter  data_in_width       = 16 
,parameter  coef_width          = 32 
,parameter  frac_data_out_width = 20//X for in,5
,parameter  frac_coef_width     = 31//division
)
(
 input	 rstn
,input	 clk
,input	 en
,input   signed	[data_in_width-1:0]   din_re
,input   signed	[data_in_width-1:0]   din_im
,input   signed	[coef_width-1   :0]   a_re
,input   signed	[coef_width-1   :0]   a_im
,input   signed	[coef_width-1   :0]   b_re
,input   signed	[coef_width-1   :0]   b_im

,output  signed	[data_in_width-1:0]   dout
);


wire	signed	[data_in_width+frac_data_out_width:0] 	 x1_re;
wire	signed	[data_in_width+frac_data_out_width:0]	 x1_im;
mult_C
#(
 .A_width(data_in_width) 
,.B_width(data_in_width) 
,.C_width(coef_width+frac_data_out_width) 
,.D_width(coef_width+frac_data_out_width) 
,.frac_coef_width(frac_coef_width) 
)
inst_c1         (
                                            .clk	(clk    	),
                                            .rstn	(rstn   	),
                                            .en		(en     	),
                                            .a		(din_re		),
                                            .b		(din_im		),
                                            .c		({a_re,{frac_data_out_width{1'b0}}}),
                                            .d		({a_im,{frac_data_out_width{1'b0}}}),
                                            .Re		(x1_re   	),//a*x(n)
                                            .Im		(x1_im   	)
	        );
wire	signed	[data_in_width+frac_data_out_width+1:0]  x2_re;
wire	signed	[data_in_width+frac_data_out_width+1:0]	 x2_im;
mult_C	
#(
 .A_width(data_in_width+frac_data_out_width+1) 
,.B_width(data_in_width+frac_data_out_width+1) 
,.C_width(coef_width) 
,.D_width(coef_width) 
,.frac_coef_width(frac_coef_width) 
)
inst_c2         (
                                            .clk	(clk    	),
                                            .rstn	(rstn   	),
                                            .en		(en     	),
                                            .a		(x1_re		),//a*x(n)
                                            .b		(x1_im		),
                                            .c		(b_re	  	),
                                            .d		(b_im	  	),
                                            .Re		(x2_re     	),//a*b*x(n-1)
                                            .Im		(x2_im     	)
		);
wire	signed	[data_in_width+frac_data_out_width+2:0]  v_re;
wire	signed	[data_in_width+frac_data_out_width+2:0]	 v_im;

assign	v_re       =   x1_re + x2_re;
assign	v_im       =   x1_im + x2_im;

reg	signed	[data_in_width+frac_data_out_width+2:0]  v1_re;
reg	signed	[data_in_width+frac_data_out_width+2:0]	 v1_im;

always @(posedge clk or negedge rstn) 
  	if (!rstn)
		 begin
                v1_re    <=    'h0;
                v1_im    <=    'h0;
 		 end 
	 else if(en)
		 begin
                v1_re    <=    v_re;
                v1_im    <=    v_im;
		  end
	 else
		 begin
                v1_re    <=    v1_re;
                v1_im    <=    v1_im;
		  end

wire    signed	[data_in_width+frac_data_out_width+1:0]	 y_re;
wire    signed	[data_in_width+frac_data_out_width+1:0]	 y_im;
wire    signed	[data_in_width+frac_data_out_width+2:0]	 y1_re;
wire    signed	[data_in_width+frac_data_out_width+2:0]	 y1_im;
wire    signed	[data_in_width+frac_data_out_width+3:0]	 y2_re;
wire    signed	[data_in_width+frac_data_out_width+3:0]	 y2_im;

reg 	signed	[data_in_width-1:0]   dout_re;

mult_C
#(
 .A_width(data_in_width+frac_data_out_width+2) 
,.B_width(data_in_width+frac_data_out_width+2) 
,.C_width(coef_width) 
,.D_width(coef_width) 
,.frac_coef_width(frac_coef_width) 
)
inst_c3         (
                                            .clk	(clk    	),
                                            .rstn	(rstn   	),
                                            .en		(en     	),
                                            .a		(y_re		),//y(n)=a*x(n)+a*b*x(n-1)+b^2*y(n-2)
                                            .b		(y_im		),
                                            .c		(b_re	  	),
                                            .d		(b_im	  	),
                                            .Re		(y1_re     	),//b*y(n-1)
                                            .Im		(y1_im     	)
		);

mult_C	
#(
 .A_width(data_in_width+frac_data_out_width+3) 
,.B_width(data_in_width+frac_data_out_width+3) 
,.C_width(coef_width) 
,.D_width(coef_width) 
,.frac_coef_width(frac_coef_width) 
)
inst_c4         (
                                            .clk	(clk    	),
                                            .rstn	(rstn   	),
                                            .en		(en     	),
                                            .a		(y1_re		),
                                            .b		(y1_im		),
                                            .c		(b_re	  	),
                                            .d		(b_im	  	),
                                            .Re		(y2_re     	),//b^2*y(n-2)
                                            .Im		(y2_im     	)
		);

assign	y_re       =   v1_re + y2_re;
assign	y_im       =   v1_im + y2_im;

reg signed	[data_in_width+frac_data_out_width+1:0] dout_round;

FixRound #(data_in_width+frac_data_out_width+2,frac_data_out_width) u_round1 (clk, rstn, en, y_re, dout_round);

always @(posedge clk or negedge rstn) 
  	if (!rstn)
		 begin
		    dout_re  <= 'h0;
		 end 
	 else if(en)
		 begin
		    dout_re  <= dout_round[frac_data_out_width+15:frac_data_out_width];
		  end
	 else
		 begin
		    dout_re  <= dout_re;
		  end

reg 	signed	[data_in_width-1:0]   dout_clip;

always @(posedge clk or negedge rstn) 
  	if (!rstn)
		 begin
		    dout_clip <= 'h0;
		 end 
	 else if(en)
		 begin
	         if(dout_round[frac_data_out_width+16:frac_data_out_width+15]==2'b01)
			dout_clip	<=	16'd32767;
		 else if(dout_round[frac_data_out_width+16:frac_data_out_width+15]==2'b10)
			dout_clip	<=	-16'd32768;
		 else
			dout_clip	<=	dout_re;		 
		 end
	 else
		 begin
		    dout_clip  <= dout_clip;
		  end

assign	dout = dout_clip;

endmodule
