	/*
 * Circular Buffer
 *
 * Embedded Systems Project
 * Columbia University
 */

module circ_buffer(
			input logic				clk,
			input logic				reset,		
			input logic [31:0]	data_b,
			input logic [8:0]		address,
			input						chipselect,
			input logic				read,
			input logic				write,
			output logic [31:0]	readdata);

	logic [7:0]				data_a;
	logic [10:0]			address_a;
	logic [8:0]				address_b;
	logic [7:0]				q_a;
	logic [31:0]			q_b;

	logic						write_ready; //lets the device driver know when to write into RAM
	logic						read_ready; // lets the device driver know when to read from RAM
	
	logic [1:0]				active_row; // informs the algorithm which row to work with
	logic [1:0]				top_row; // informs the algorithm which row to work with
	logic						data_ready;
	logic [1:0]				read_row;
	logic [1:0]				write_row;

   logic						wren_a;
   logic						wren_b;

	//logic						row_filled0, row_filled1, row_filled2;
	logic						row_filled[2:0];
	logic						row_processed0, row_processed1, row_processed2;
	logic						reading_row0, reading_row1, reading_row2;
	logic						row_processed[2:0];
	logic						read_toggle;

	logic						mode1;
	logic						mode2;
	logic						image_transferred;
	logic [7:0]				max;
	logic						LUT_complete;
	
	logic [7:0]				LUT_add, LUT_val;
	
   parameter ROWADDRESSOFFSET      = 9'd160;

   RAM_cc RAM_cc_instance(
			.clock(clk),
			.aclr(reset),
			.address_a(address_a),
			.address_b(address_b),
			.data_a(data_a),
			.data_b(data_b),
			.wren_a(wren_a),
			.wren_b(wren_b),
			.q_a(q_a),
			.q_b(q_b) );
	

	ccl ccl_instance(
			.clk(clk), 
			.data_ready(data_ready),
			.ACTIVE_ROW(active_row),
			.TOP_ROW(top_row),
			.mode1(mode1),
			.mode2(mode2),
			.row_processed0(row_processed0),
			.row_processed1(row_processed1),
			.row_processed2(row_processed2),
			.reading_row0(reading_row0),
			.reading_row1(reading_row1),
			.reading_row2(reading_row2),
			.LUT_COMPLETE(LUT_complete),
			.image_transferred(image_transferred), 
			.max(max),
			.q_a(q_a), //RAM
			.address_a(address_a), //RAM
			.data_a(data_a), //RAM
			.wren_a(wren_a),
			.LUT_add(LUT_add), 
			.LUT_val(LUT_val)); //RAM

	

	// Initial assignments
	initial begin
		active_row = 2'd1; // initially, there's no filled row to process
		top_row = 2'd0; // initially, the third row will serve as the zero-padded top row
		//data_ready = 1'b0;
		write_row = 2'd0; // initially, begin by writing into the first row
		read_row = 2'd0; // initially, there's no row to read

		row_filled = '{1'b0, 1'b0, 1'b0};
		//row_processed = '{1'b0, 1'b0, 1'b0};
		
		reading_row0 = 1'b0;
		reading_row1 = 1'b0;
		reading_row2 = 1'b0;
		
		//write_ready = 1'b1;
		//read_ready = 1'b0;
		
		mode1 = 1'b0;
		mode2 = 1'b0;
		read_toggle = 1'b0;
		image_transferred = 1'b0;
		//LUT_complete = 1'b0;
	end
	
	// 
	always_comb begin
		LUT_add = 0;
		readdata = q_b;
		wren_b = write && chipselect;
		address_b = 9'b111111111;
		
		row_processed[0] = row_processed0;
		row_processed[1] = row_processed1;
		row_processed[2] = row_processed2;
			
		read_ready = (row_processed[0] || row_processed[1] || row_processed[2]);

		write_ready = ~(row_filled[0] && row_filled[1] && row_filled[2]);
		
		data_ready = (row_filled[active_row] && row_filled[top_row] && (~row_processed[active_row]));
	
		if(address < ROWADDRESSOFFSET) begin
		   if(read) begin
				readdata = q_b;
				address_b = read_row * ROWADDRESSOFFSET + address;
			end
			if(write) begin
				address_b = write_row * ROWADDRESSOFFSET + address;
			end
		end else if (address == 9'd184) begin
			if(read) begin
				readdata = {31'd0, write_ready};
			end
		end else if (address == 9'd185) begin
			if(read) begin
				readdata = {31'd0, read_ready};
			end
		end else if (address == 9'd186) begin
			if(read) begin
				readdata = {31'd0, LUT_complete};
			end
		end else if (address == 9'd187) begin
			if(read) begin
				readdata = {30'd0, write_row};
			end
		end else if (address == 9'd188) begin
			if(read) begin
				readdata = {30'd0, read_row};
			end
		end else if (address == 9'd189) begin
			if(read) begin
				readdata = {31'd0, row_filled[0]};
			end
		end else if (address == 9'd190) begin
			if(read) begin
				readdata = {31'd0, row_filled[1]};
			end
		end else if (address == 9'd191) begin
			if(read) begin
				readdata = {31'd0, row_filled[2]};
			end
		end else if (address == 9'd192) begin
			if(read) begin
				readdata = {31'd0, row_processed[0]};
			end
		end else if (address == 9'd193) begin
			if(read) begin
				readdata = {31'd0, row_processed[1]};
			end
		end else if (address == 9'd194) begin
			if(read) begin
				readdata = {31'd0, row_processed[2]};
			end
		end else if (address == 9'd195) begin
			if(read) begin
				readdata = {31'd0, reading_row0};
			end
		end else if (address == 9'd196) begin
			if(read) begin
				readdata = {31'd0, reading_row1};
			end
		end else if (address == 9'd197) begin
			if(read) begin
				readdata = {31'd0, reading_row2};
			end
		end else if (address == 9'd198) begin
			if(read) begin
				readdata = {31'd0, data_ready};
			end
		end else if (address == 9'd199) begin
			if(read) begin
				readdata = {30'd0, active_row};
			end
		end else if (address == 9'd200) begin
			if(read) begin
				readdata = {30'd0, top_row};
			end
		end else if (address == 9'd201) begin
			if(read) begin
				readdata = {24'd0, max};
			end
		end else if (address > 9'd255 && address <= 9'd511) begin
			if(read) begin
				LUT_add = address[7:0];
				readdata = {24'd0, LUT_val};
			end
		end
	end
	
	always_ff @(posedge clk) begin
	
		if (row_processed[top_row]) begin
			if (active_row == 2'd0) begin
				active_row <= 2'd1;
				top_row <= 2'd0;
			end else if(active_row == 2'd1) begin
				active_row <= 2'd2;
				top_row <= 2'd1;
			end else if (active_row == 2'd2) begin
				active_row <= 2'd0;
				top_row <= 2'd2;
			end
		end else begin
			active_row <= active_row;
			top_row <= top_row;
		end
		
		if(address == 9'd180) begin
			if(write) begin
				mode1 <= data_b[0];
				active_row <= 2'd1; // initially, there's no filled row to process
				top_row <= 2'd0; // initially, the third row will serve as the zero-padded top row
				//data_ready <= 1'b0;
				write_row <= 2'd0; // initially, begin by writing into the first row
				read_row <= 2'd0;
				row_filled <= '{1'b0, 1'b0, 1'b0};
				//row_processed <= '{1'b0, 1'b0, 1'b0};
				read_toggle <= 1'b0;
				image_transferred <= 1'b0;
				reading_row0 <= 1'b0;
				reading_row1 <= 1'b0;
				reading_row2 <= 1'b0;
				//LUT_complete <= 1'b0;
			end
		end else if (address == 9'd181) begin
			if(write) begin
				mode2 <= data_b[0];
				active_row <= 2'd1; // initially, there's no filled row to process
				top_row <= 2'd0; // initially, the third row will serve as the zero-padded top row
				//data_ready <= 1'b0;
				write_row <= 2'd0; // initially, begin by writing into the first row
				read_row <= 2'd0;
				row_filled <= '{1'b0, 1'b0, 1'b0};
				//row_processed <= '{1'b0, 1'b0, 1'b0};
				read_toggle <= 1'b0;
				image_transferred <= 1'b0;
				reading_row0 <= 1'b0;
				reading_row1 <= 1'b0;
				reading_row2 <= 1'b0;
			end
		end else if (address == 9'd182) begin
			if(write) begin
				image_transferred <= data_b[0];
			end
		end
	
		if(write) begin
			if(address == ROWADDRESSOFFSET - 9'b1) begin
				row_filled[write_row] <= 1'b1;
				if (write_row == 2'd0) begin
					write_row <= 2'd1;
				end else if(write_row == 2'd1) begin
					write_row <= 2'd2;
				end else if (write_row == 2'd2) begin
					write_row <= 2'd0;
				end					
			end
		end
		
		if (read && ~read_toggle) begin
			if(address == 0) begin
				row_filled[read_row] <= 1'b0;
				if(read_row == 2'd0) begin
					reading_row0 <= 1'b1;
					reading_row1 <= 1'b0;
					reading_row2 <= 1'b0;
				end else if(read_row == 2'd1) begin
					reading_row0 <= 1'b0;
					reading_row1 <= 1'b1;
					reading_row2 <= 1'b0;
				end else if(read_row == 2'd2) begin
					reading_row0 <= 1'b0;
					reading_row1 <= 1'b0;
					reading_row2 <= 1'b1;
				end
			end else if(address == ROWADDRESSOFFSET - 9'b1) begin
				read_toggle <= 1'b1;
				reading_row0 <= 1'b0;
				reading_row1 <= 1'b0;
				reading_row2 <= 1'b0;
				//row_processed[read_row] <= 1'b0;
				if (read_row == 2'd0) begin
					read_row <= 2'd1;
				end else if(read_row == 2'd1) begin
					read_row <= 2'd2;
				end else if (read_row == 2'd2) begin
					read_row <= 2'd0;
				end
			end
		end
		
		if(~read) begin
			read_toggle <= 1'b0;
		end
	end
endmodule
