module anulador1_32bit

#(
	parameter WIDTH = 32,
	parameter ENDMEMBER = 21
)

(
	// Input Ports
	input clk,rst,
	input [(WIDTH*ENDMEMBER-1):0] fila_nueva,fila_ref,
	input req,
	input [4:0] i,

	// Output Ports
	output [(WIDTH*ENDMEMBER-1):0] fila_sig   ,
	output ack

);

genvar i_genvar;

wire [WIDTH-1:0]	aij, aii,a_temp,abs_aij,abs_aii,abs_a_temp;
wire [4:0]log_aii,log_aij,log_a_temp;
reg [5:0]k_next,k_reg;
reg [(WIDTH*ENDMEMBER-1):0] fila_reg_ref,fila_reg;
wire [(WIDTH*ENDMEMBER-1):0] fila_temp;
reg en_k,en_fila,nuevo,sum_res_fliflop;
reg [(WIDTH*ENDMEMBER-1):0]fila_temp_op;

reg [4:0]state_reg,state_next;
wire cero;
reg ack_reg,ack_next;
wire igual,mayor;
reg sel_k;

localparam 	state_idle 		= 	5'h01,
				state_register	=	5'h02,
				state_calk		=	5'h04,
				state_op			=	5'h08,
				state_fin		=	5'h10;

mux5bits #(.WIDTH(WIDTH)) stage_mux_aij (.data_in(fila_reg),.sel(i),.data_out(aij));
mux5bits #(.WIDTH(WIDTH)) stage_mux_aii (.data_in(fila_reg_ref),.sel(i),.data_out(aii));
mux5bits #(.WIDTH(WIDTH)) stage_mux_temp (.data_in(fila_temp),.sel(i),.data_out(a_temp));

abs #(.WIDTH(WIDTH)) stage_abs_aii (.number(aii),.result(abs_aii));
abs #(.WIDTH(WIDTH)) stage_abs_aij (.number(aij),.result(abs_aij));
abs #(.WIDTH(WIDTH)) stage_abs_a_temp (.number(a_temp),.result(abs_a_temp));

log_32bits_sin_decimal #(.WIDTH(WIDTH)) stage_log_aii(.number(abs_aii),.result(log_aii));
log_32bits_sin_decimal #(.WIDTH(WIDTH)) stage_log_aij(.number(abs_aij),.result(log_aij));
log_32bits_sin_decimal #(.WIDTH(WIDTH)) stage_log_a_temp(.number(abs_a_temp),.result(log_a_temp));

assign igual=(log_a_temp==log_aij)?1'b1:1'b0;
assign mayor=(log_a_temp>log_aij)?1'b1:1'b0;

always@(*)
begin
	if (sel_k)
		if (mayor)
			k_next = k_reg-1;
		else
			k_next = k_reg+1;
	else
		k_next =   {1'b0,log_aij} - {1'b0,log_aii};
end

always@(posedge clk,posedge rst)
begin
	if (rst)
		k_reg <= 5'h00;
	else
		if (en_k)
			k_reg <= k_next;
end

generate
for (i_genvar=0;i_genvar<=ENDMEMBER-1;i_genvar=i_genvar+1) begin :generate_filas
	shift_32bit #(.WIDTH(WIDTH)) stage_shift(.number(fila_reg_ref[(i_genvar+1)*WIDTH-1:i_genvar*WIDTH]),.k(k_reg),.result(fila_temp[(i_genvar+1)*WIDTH-1:i_genvar*WIDTH]));

	always@(*)
	begin
		if (sum_res_fliflop)
			fila_temp_op[(i_genvar+1)*WIDTH-1:i_genvar*WIDTH]=fila_reg[(i_genvar+1)*WIDTH-1:i_genvar*WIDTH]+fila_temp[(i_genvar+1)*WIDTH-1:i_genvar*WIDTH];
		else
			fila_temp_op[(i_genvar+1)*WIDTH-1:i_genvar*WIDTH]=fila_reg[(i_genvar+1)*WIDTH-1:i_genvar*WIDTH]-fila_temp[(i_genvar+1)*WIDTH-1:i_genvar*WIDTH];
	end
	
end
endgenerate

always@(posedge clk,posedge rst)
begin
if (rst)
	sum_res_fliflop=1'b0;
else if(en_k)
	sum_res_fliflop=aii[WIDTH-1]^aij[WIDTH-1];
end

always@(posedge clk,posedge rst)
begin
	if (rst)
		fila_reg <= 3'b000;
	else
		if (en_fila)
			if (nuevo)
				fila_reg <= fila_nueva;
			else
				fila_reg <= fila_temp_op;
end

always@(posedge clk,posedge rst)
begin
	if (rst)
		fila_reg_ref <= 3'b000;
	else
		if ((en_fila)&(nuevo))
				fila_reg_ref <= fila_ref;
end

assign cero=(aij==0)?1'b1:1'b0;
			
//Registro
always@(posedge clk,posedge rst)
begin
	if (rst)
		state_reg <= 5'b000;
	else
		state_reg <= state_next;
end

//Logica del estado siguiente
always@(*)
begin
	state_next = state_reg;
	casez(state_reg)
	state_idle:
	begin
		if (req)
			state_next=state_register;
	end
	state_register:
	begin
		state_next=state_calk;
	end
	state_calk:
	begin
		if (cero)
			state_next=state_fin;
		else
			state_next=state_op;
	end
	state_op:
	begin
		if (igual==1'b1)
			state_next=state_calk;
		else
			state_next=state_op;
	end
	state_fin:
	begin
		if (req)
			state_next=state_fin;
		else
			state_next=state_idle;
	end
	default:state_next = state_idle;
	endcase
end

always@(posedge clk,posedge rst)
begin
if (rst)
	ack_reg <= 1'b0;
else
	ack_reg <= ack_next;
end

assign ack=ack_reg;

//Logica de salida
always@(*)
begin
	nuevo	=	1'b0;
	en_fila		=	1'b0;
	en_k			=	1'b0;
	ack_next		=	1'b0;
	sel_k=1'b0;
	casez(state_reg)
	state_idle:
	begin
		nuevo	=	1'b0;
		en_fila		=	1'b0;
		en_k			=	1'b0;
		ack_next		=	1'b0;		
	end
	state_register:
	begin
		nuevo	=	1'b1;
		en_fila		=	1'b1;
		en_k			=	1'b0;
		ack_next		=	1'b0;
	end
	state_calk:
	begin
		nuevo			=	1'b0;
		en_fila		=	1'b0;
		en_k			=	1'b1;
		ack_next		=	1'b0;
		if (cero)
			ack_next = 1'b1;
	end
	state_op:
	begin
		if (igual==1'b1)
		begin
			nuevo			=	1'b0;
			en_fila		=	1'b1;
			en_k			=	1'b0;
			ack_next		=	1'b0;
		end
		else
		begin
		   sel_k			=	1'b1;
			nuevo			=	1'b0;
			en_fila		=	1'b0;
			en_k			=	1'b1;
			ack_next		=	1'b0;
		end
	end
	state_fin:
	begin
		nuevo			=	1'b0;
		en_fila		=	1'b0;
		en_k			=	1'b0;
		ack_next		=	1'b1;
		if (~req)
			ack_next =	1'b0;
	end
	default:
	begin
		nuevo			=	1'b0;
		en_fila		=	1'b0;
		en_k			=	1'b0;
		ack_next		=	1'b0;
	end
	endcase
end

assign fila_sig=fila_reg;

endmodule
