// CSEE 4840L Connected component labeling

//Top Module: CCL - Communicates with the memory buffer and connects the submodules. 
module ccl (input logic clk, 
				input data_ready, 
				input logic [1:0] ACTIVE_ROW, 
				input logic [1:0] TOP_ROW,
				input logic mode1, /**for pass 1 operation**/
				input logic mode2, /**for pass 2 operation**/
				input logic reading_row0, /**set when row 0 is being read by software**/
				input logic reading_row1,
				input logic reading_row2,
				output logic row_processed0, /**set row 0 has finished pass 1 or pass 2**/
				output logic row_processed1,
				output logic row_processed2,				
				output logic LUT_COMPLETE, /**set when LUT equivalences have been resolved**/
				input image_transferred, /**set when all image rows have been written on buffer**/
				output logic [7:0] max, 
				input logic [7:0] q_a, /**value in RAM**/ 
				output logic [10:0] address_a, /**address of RAM**/
				output logic [7:0] data_a, /**data to be written on RAM**/
				output logic wren_a, /**write enable for RAM**/ 
				input logic [7:0] LUT_add,
				output logic [7:0] LUT_val
				);
				
				logic [7:0] label_no; /**Starts from 1, total 255 possible labels**/

				logic [10:0] OFFSET; 
				assign OFFSET = 11'd640; /**Row offset**/

				logic c_en, c_reset, nbd_ready, resolve_en, p2ready; 
				logic [10:0] count; /**pixel counter**/
				
				logic [7:0] curr, curr_l, top, top_l; /**pixel values for neighboring and current pixel**/
				
				logic [7:0]  index, din, dout, p1index, p2index, lindex, lutrout;
				logic we, p1we, lwe;
				logic [7:0] p1din, ldin;
				
				logic [10:0] p1address_a, p2address_a;
				logic [7:0] p1data_a, p2data_a;
				logic p1wren_a, p2wren_a, wren_default;
				
				initial begin
					c_en = 1'b0; //for count enable
					c_reset = 1'b1; //for count reset
					resolve_en = 1'b0; //for LUT resolve
					row_processed0 = 1'b0;
					row_processed1 = 1'b0;
					row_processed2 = 1'b0;
					max = 1'b0;
				end
				
				always_ff @(posedge clk) begin
					if (count == 11'd639 && (nbd_ready || p2ready)) begin
						c_reset <=1'b1; 
						if(TOP_ROW == 2'd0) row_processed0 <= 1'b1; /**Row processed set when counter reaches 639**/
						else if(TOP_ROW == 2'd1) row_processed1 <= 1'b1;
						else if(TOP_ROW == 2'd2) row_processed2 <= 1'b1;
						else begin
							row_processed0 <= row_processed0;
							row_processed1 <= row_processed1;
							row_processed2 <= row_processed2;
						end
					end else if (data_ready) begin
						c_reset <= 1'b0; 
					end else begin		
						c_reset <= 1'b1;
					end
					
					if (reading_row0 == 1'b1) row_processed0 <= 1'b0; /**When software starts reading row0, row processed is reset**/
					else if (reading_row1 == 1'b1) row_processed1 <= 1'b0;
					else if (reading_row2 == 1'b1) row_processed2 <= 1'b0;
					
					if (nbd_ready || p2ready) c_en <= 1'b1;
					else c_en <=1'b0;
					
					if (image_transferred && (~data_ready)) begin 
						max = label_no - 8'd1;
						resolve_en = 1'b1; /**Set when LUT is ready to be resolved**/ 
						if(TOP_ROW == 2'd0) row_processed0 <= 1'b1;
						else if(TOP_ROW == 2'd1) row_processed1 <= 1'b1;
						else if(TOP_ROW == 2'd2) row_processed2 <= 1'b1;
					end
					if (LUT_COMPLETE) begin
						resolve_en = 1'b0;
					end
				end
				
				always_comb
				begin
					if (mode1 && data_ready) begin
						index = p1index;
						we = p1we;
						din = p1din;
					end
					else if (resolve_en) begin
						index = lindex;
						we = lwe;
						din = ldin;
					end
					else if (mode2 && data_ready) begin
						index = p2index;
						we = 1'b0;
						din = 8'd0;
					end
					else begin
						index = LUT_add; /**To print LUT**/
						we = 1'b0;
						din = 8'd0;
					end
					
					LUT_val = dout; /**To print LUT**/
					
					if (mode1 && data_ready)begin
						address_a = p1address_a;
						data_a = p1data_a;
						wren_a = p1wren_a; 
					end
					else if(mode2 && data_ready) begin
						address_a = p2address_a;
						data_a = p2data_a;
						wren_a = p2wren_a; 
					end else begin
						wren_a = 1'b0;	
						address_a = 11'd2047; 
						data_a = 8'd0;
					end
				end
				
				counter c(.*);
				pass1 p1(.*);
				lookup l(.*);
				lut_resolve r(.*);
				pass2 p2(.*);
endmodule

// - MODULE - COUNTER: pixel index counter
module counter (input logic clk, input logic c_en, input logic c_reset, output logic [10:0] count);

	initial begin
		count = 11'd0;
	end

	always_ff @(posedge clk) begin
		if (c_reset) begin
			count <= 11'd0;
		end
		else if(c_en) begin
				count <= count + 11'd1;  
		end
	end

endmodule

// MODULE - PASS1: Neighborhood + Threshold + Label 1
module pass1(input logic clk, 
			  input logic [10:0] OFFSET, 
			  input logic [1:0] ACTIVE_ROW, 
			  input logic [1:0] TOP_ROW,
			  input logic [10:0] count,
			  input logic mode1,
			  input logic c_reset,
			  input logic [7:0] q_a, 
			  output logic [10:0] p1address_a, /**wire connecting to RAM address**/
			  output logic [7:0] p1data_a, 
			  output logic p1wren_a, 
			  output logic [7:0] label_no,
			  output logic [7:0]  p1index, p1din, /**LUT module inputs**/
	        output logic p1we, /**LUT write enable**/
			  output logic nbd_ready /**set when all four neighboring pixels are assigned values**/
			  );
			
		logic [2:0] i; 
		logic [7:0] curr, curr_l, top, top_l;
		
		initial begin
			i = 3'd0;
			label_no = 8'd1;
			nbd_ready = 1'b0;
			p1wren_a = 1'b0;
			p1we = 1'b0;
			curr = 8'd0;
			top = 8'd0;
			curr_l = 8'd0;
			top_l = 8'd0;
		end
		
		always_ff @(posedge clk) begin
			if (mode1 && ~c_reset) begin /**Enables pass 1 operations**/
				if (i == 3'd0) begin
					p1we = 1'b0;
					p1wren_a = 1'b0;
					p1address_a = ACTIVE_ROW*OFFSET + count;
					curr_l = curr;
					top_l = top;
					i++;
				end
				
				else if (i == 3'd1) begin
					p1address_a = TOP_ROW*OFFSET + count;
					i++;
				end
				
				else if (i == 3'd2) begin
					i++;
				end
				
				else if (i == 3'd3) begin
					curr = q_a;
					nbd_ready = 1'b1;
					i++;
				end
				
				else if (i == 3'd4) begin
					i++;
					top = q_a;
					/**Threshold = 128**/
					if (curr[7] == 1'b1) curr = 8'd255; /**Foreground**/
					else curr = 8'd0; /**Background**/
					p1address_a = ACTIVE_ROW*OFFSET + count;
					nbd_ready = 1'b0;
				end
				
				else begin 
					i = 3'b0;
					if (curr == 8'd255) begin 
						if (top_l != 8'd0) curr = top_l;
						else if (top != 8'd0 && top_l == 8'd0 && curr_l == 8'd0) curr = top;
						else if (curr_l != 8'd0 && top_l == 8'd0 && top == 8'd0 ) curr = curr_l;  
						else if (top_l == 8'd0 && top != 8'd0 && curr_l != 8'd0 ) begin
						   /**For LUT module using approach 1**/
							curr = top;
						   p1index = curr_l;
						   p1din = top;
							p1we = 1'b1; /**Write in LUT at p1index to update label **/
							
						  /**For LUT module using approach 2**/
						  /*if (top < curr_l) begin
								 curr = top;
								 p1index = curr_l;
								 p1din = top;
								 p1we = 1'b1; //write din in LUT at p1index
						  /*end
						  else if (curr_l < top) begin
								 curr = curr_l;
								 p1index = top;
								 p1din = curr_l;
								 p1we = 1'b1; //write din in LUT at p1index
						  end	 
						  else curr = top;*/
						end
						else begin
							  curr = label_no;
							  p1index = label_no;
							  p1din = label_no;
							  p1we = 1'b1;
							  label_no++;  
						end
						p1data_a = curr; /**Value to write in RAM for current pixel**/
						p1wren_a = 1'b1; /**RAM write enabled**/
					end
					else begin 
						p1data_a = curr;
						p1wren_a = 1'b1;
					end
				end
			end 
			else if (mode1) begin
				i = 3'd0;
				nbd_ready= 1'b0;
				p1wren_a = 1'b0;
				p1we = 1'b0;
			end
			
			else begin /**Reset condition**/
				i = 3'd0;
				label_no = 8'd1;
				nbd_ready = 1'b0;
				p1wren_a = 1'b0;
				p1we = 1'b0;
			end
		end

endmodule

//MODULE - LOOK-UP: Stores equivalences encountered during pass 1
module lookup(input logic  clk,
	      input logic [7:0]  index,
	      input logic [7:0]  din,
	      input logic 	 we,
	      output logic [7:0] dout); 
   
   logic [7:0] LUT[255:0]; /**LUT memory**/

	initial begin
		for(int i =0; i < 256; i++) begin
			LUT[i] = 8'b0;
		end
	end
	
   always_ff @(posedge clk) begin
      if (we) LUT[index] <= din;
      dout <= LUT[index];
   end
endmodule

//MODULE - LUT_RESOLVE: Resolving equvalences
module lut_resolve (input logic clk,
			input logic mode1,
			input logic [7:0] max,
			input resolve_en,
	      input logic [7:0] dout, 
			output logic [7:0]  lindex,
			output logic LUT_COMPLETE,
			output logic lwe,
			output logic [7:0] ldin);
			
		logic [7:0] loopindex, label_index;
		logic [4:0] c;
		
		initial begin
			lwe = 1'b0;
			ldin = 8'd0;
			LUT_COMPLETE = 1'b0;
			lindex = 8'd0;
			loopindex = 8'd1;
			c = 3'd0;
			label_index = 8'd0;
		end
		
		/**Iterate through the LUT after pass1 one to merge equivalent labels**/
		
		always_ff @(posedge clk) begin
		
		   /**The module is run when resolve_en is set and the program has exited mode 1**/
			/**Iterates through the lookup table to find the final label**/
			if(resolve_en && loopindex <= max && (~mode1)) begin
				if(c == 3'd0) begin
					lwe = 1'b0;
					lindex = loopindex;
					c = 3'd1;
				end else if(c == 3'd1) begin
					c = 3'd2;
				end else if(c == 3'd2) begin
					if(dout == lindex) begin
						ldin = dout;
						lindex = loopindex;
						lwe = 1'b1;
						loopindex = loopindex + 1'b1;
						c = 3'd0;
					end else begin
						lindex = dout;
						c = 3'd1;
					end
				end
			end
			/**Set LUT_COMPLETE once all equivalences have been resolved**/
			else if (resolve_en && (~mode1)) begin
				LUT_COMPLETE = 1'b1;
				lwe = 1'b0;
				ldin = 8'd0;
			end
			else if (mode1) begin
				LUT_COMPLETE = 1'b0;
				lwe = 1'b0;
				ldin = 8'd0;
				loopindex = 8'd1;
				lindex = 8'd0;
				c = 3'd0;
			end
			
		end
endmodule


// MODULE - PASS2
module pass2 (input logic clk,
				input logic mode2,
				input logic [10:0]	OFFSET,
				input logic [1:0] ACTIVE_ROW,
			   input logic [10:0] count,
			   input logic c_reset,
			   output logic [10:0] p2address_a,
			   input logic [7:0] q_a, 
			   output logic [7:0] p2data_a, 
			   output logic p2wren_a,
			   output logic [7:0]  p2index, /**LUT index**/
				input logic [7:0] dout, /**LUT output**/
				output logic p2ready /**Triggers c_en for count**/
	         );

				logic [2:0] i;
				
				initial begin
					i = 3'd0;
					p2wren_a = 1'b0;
					p2address_a = 11'd0;
					p2data_a = 8'd0;
					p2ready = 1'b0;
					p2index = 8'd0;
				end
				
				always_ff @(posedge clk)
				begin
					if (mode2 & ~c_reset) begin
						if (i == 3'd0) begin
							p2address_a <= ACTIVE_ROW*OFFSET + count;
							i <= 3'd1;
							p2wren_a <= 1'b0;
						end
						
						else if (i == 3'd1) begin
							i <= 3'd2;
						end
						
						else if (i == 3'd2) begin
							i <= 3'd3;
						end
						
						else if (i == 3'd3) begin
							i <= 3'd4;
							p2index <= q_a; /**Assigns the pixel value in RAM to LUT index**/
							p2ready <= 1'b1;
						end
						
						else if (i == 3'd4) begin
							i <= 3'd5;
							p2ready <= 1'b0;
						end
						
						else if (i == 3'd5) begin
							if (dout != 8'd0) begin
								p2data_a <= dout;
							end
							else begin
								p2data_a <= p2index; /**Assigns the LUT index to pixel value in RAM**/
							end
							p2wren_a <= 1'b1;
							i <= 3'd0;
						end
					end else begin
						i = 3'd0;
						p2wren_a = 1'b0;
						p2address_a = 11'd0;
						p2data_a = 8'd0;
						p2ready = 1'b0;
						p2index = 8'd0;
					end
				end
endmodule
