module array_fifo_64 (
	clk,
	reset,
	write_in_control,
	write_array,
	address_write_array,
	writedata_array,
	write_out_control,
	read_array,
	address_read_array,
	writedata_in_control,
	writedata_out_control,
	
	
	wait_write_in_control,
	wait_write_array,
	wait_write_out_control,
	readdata_array,
	wait_read_array
	);
parameter	MEMORY_SIZE=64, 		// The number of words in each FIFO buffer slot
			FIFO_DEPTH_LOG=2,	// Number of buffer slots in the FIFO (i.e. FIFO depth)
			DATA_WIDTH=32,		// Width of words stored in the buffer
			ADDR_WIDTH=6;		// Width of the address bus (has to be == log (MEMORY_SIZE))

input					clk;
input					reset;
input					write_in_control;
input					write_array;
input [ADDR_WIDTH-1:0]	address_write_array;
input [DATA_WIDTH-1:0]	writedata_array;
input					write_out_control;
input					read_array;
input [ADDR_WIDTH-1:0]	address_read_array;
input [7:0]				writedata_in_control;
input [7:0]				writedata_out_control;

output					wait_write_in_control;
output					wait_write_array;
output					wait_write_out_control;
output [DATA_WIDTH-1:0]	readdata_array;
output					wait_read_array;

reg [FIFO_DEPTH_LOG:0]	read_ptr;				// Will contain index of the memory module currently used for reading
reg [FIFO_DEPTH_LOG:0]	write_ptr;				// Will contain index of the memory module currently used for reading
// Both pointers declared above this line have one extra bit, which is used to distinguish between the buffer being full or empty

wire buffer_full;
wire buffer_empty;
wire [(2**FIFO_DEPTH_LOG)-1:0] write;

wire [DATA_WIDTH*(2**FIFO_DEPTH_LOG)-1:0] data_read;

genvar i;

	assign buffer_empty = (read_ptr == write_ptr);
	assign buffer_full = (read_ptr[FIFO_DEPTH_LOG-1:0] == write_ptr[FIFO_DEPTH_LOG-1:0]) && (read_ptr[FIFO_DEPTH_LOG] != write_ptr[FIFO_DEPTH_LOG]);


	assign wait_write_array = write_array & buffer_full;
	assign wait_read_array = read_array & buffer_empty;

	assign wait_write_in_control = write_in_control & buffer_full;
	assign wait_write_out_control = write_out_control & buffer_empty;

	parameterized_mux the_mux (.data (data_read),
			.sel (read_ptr[FIFO_DEPTH_LOG-1:0]),
			.result(readdata_array)
			);
	defparam 	the_mux.LPM_WIDTH = DATA_WIDTH,
				the_mux.LPM_SIZE = (2**FIFO_DEPTH_LOG),
				the_mux.LPM_WIDTHS = FIFO_DEPTH_LOG;


	always @(posedge clk or posedge reset)
	begin
		if (reset == 1'b1)
		begin
			read_ptr = {(FIFO_DEPTH_LOG+1){1'b0}};		// Initially the memory with index 0 will be used for reading
		end
		else if (write_out_control & ~wait_write_out_control)
		begin
			read_ptr = read_ptr + 1'b1;
		end
	end


	always @(posedge clk or posedge reset)
	begin
		if (reset == 1'b1)
		begin
			write_ptr = {(FIFO_DEPTH_LOG+1){1'b0}};		// Initially the memory with index 0 will be used for writing
		end
		else if (write_in_control & ~wait_write_in_control)
		begin
			write_ptr = write_ptr + 1'b1;
		end
	end

generate 
	for(i = 0; i<(2**FIFO_DEPTH_LOG); i = i+1) 
	begin : memory_block
		altsyncram	memory (
					.wren_a (write[i]),
					.clock0 (clk),
					.address_a (address_write_array),
					.address_b (address_read_array),
					.data_a (writedata_array),
					.q_b (data_read[DATA_WIDTH*(i+1)-1:DATA_WIDTH*i]),
					.aclr0 (1'b0),
					.aclr1 (1'b0),
					.addressstall_a (1'b0),
					.addressstall_b (1'b0),
					.byteena_a (1'b1),
					.byteena_b (1'b1),
					.clock1 (1'b1),
					.clocken0 (1'b1),
					.clocken1 (1'b1),
					.clocken2 (1'b1),
					.clocken3 (1'b1),
					.data_b ({32{1'b1}}),
					.eccstatus (),
					.q_a (),
					.rden_a (1'b1),
					.rden_b (1'b1),
					.wren_b (1'b0));
		defparam
			memory.address_reg_b = "CLOCK0",
			memory.clock_enable_input_a = "BYPASS",
			memory.clock_enable_input_b = "BYPASS",
			memory.clock_enable_output_a = "BYPASS",
			memory.clock_enable_output_b = "BYPASS",
			memory.lpm_type = "altsyncram",
			memory.operation_mode = "DUAL_PORT",
			memory.outdata_aclr_b = "NONE",
			memory.outdata_reg_b = "UNREGISTERED",
			memory.power_up_uninitialized = "FALSE",
			memory.read_during_write_mode_mixed_ports = "DONT_CARE",
			memory.widthad_a = ADDR_WIDTH,
			memory.widthad_b = ADDR_WIDTH,
			memory.width_a = DATA_WIDTH,
			memory.width_b = DATA_WIDTH,
			memory.width_byteena_a = 1;
	assign write[i] = write_array & (write_ptr[FIFO_DEPTH_LOG-1:0] == i) & ~wait_write_array;
	
	end
endgenerate

endmodule
