////////////////////////////////////////////////////////////////////////////////////////////////////////
// I2C_Interface.v
//
// Author:			Thanh Tien Truong
//
// Description:
// ------------
// SCCB interface to communicate with OV5647
//  - Implementing two-wire data transmission of SCCB protocol
//	- Using 3-phase write transmission cycle of SCCB protocol
//
//////////////////////////////////////////////////////////////////////////////////////////////////////

module I2C_Interface #(parameter SID = 8'hC0) 
( 
    input resend,                               // = reset
    input clk,                                  // 24Mhz clock signal
    output sioc,                                // Clock signal for SCCB
    output siod,                                 // Data signal for SCCB
    output siod_ioen,                           // Output enable (active low) for data signal
    output taken,                               // Flag to go to next address of LUT
    input send,                                 // Flag to indicate if configuration is finshed
    input waitnull,                             // wait signal. 1 wait,don't send
    input [7:0] regah,                          // Resgister address high 8 bit
    input [7:0] regal,                          // Resgister address low 8 bit
    input [7:0] value,							// Data to write into a regsiter address
	output busy);                         		// I2C interface is currently sending data (1) or idle (0)
                            
    
    // Internal signals
    reg [7:0] divider ;
    reg [40:0] busy_sr;
    reg [40:0] data_sr/*synthesis syn_keep=1*/;
    reg sioc_temp/*synthesis syn_keep=1*/;
    reg siod_temp/*synthesis syn_keep=1*/;
    reg siod_ioen_temp/*synthesis syn_keep=1*/;
    reg taken_temp;
    
    // ID of an OC0TA1B for SCCB protocol
    localparam id = SID;
    
    // Assign value for outputs
    assign siod = siod_temp;
    assign sioc = sioc_temp;
    assign siod_ioen = siod_ioen_temp;
    assign taken = taken_temp;

	assign busy = busy_sr[40];
    
    always @ (posedge clk) 
    begin
        // when the bus is idle SIOD must be tri-state
        if(busy_sr[11:10] == 2'b10 || busy_sr[20:19] == 2'b10 || busy_sr[29:28] == 2'b10 || busy_sr[38:37] == 2'b10) begin
            // siod_temp <= 1'bZ;
            siod_ioen_temp = 1'b1;
        end
        // else SIOD will be driven my master (FPGA board)
        else begin
            //siod_temp <= data_sr[40];
            siod_ioen_temp = 1'b0;
        end

        siod_temp <= data_sr[40];
    end
        
    always @ (posedge clk  or posedge resend) 
    begin
    	if(resend)
    		begin
        		taken_temp <= 1'b0;
        		busy_sr <= {41{1'b0}};
				data_sr <= {41{1'b1}};
				divider <= 8'd0;
        	end
        
        // If all 31 bits are transmitted 
        else if(busy_sr[40] == 0) 
        	begin
        	    // Assert SIOC high for starting new transmission
        	    sioc_temp <= 1;
        	    
        	    // If New data is arrived from LUT 
        	    if(send == 1 && waitnull == 1'b0) begin
        	        if(divider == 0) begin
        	            // Create an data to send through the data signal of the SCCB
        	            // The data is created using 3-phase write transmission cycle of SCCB protocol
        	            //
        	            // Data:  
        	            // 3'b100 --> SIOD will go from 1 to 0 to indicate a start transmission
        	            //            the last bit is an don't care bit
        	            // id     --> the ID of a slave (8'h42). The last bit of the slave is 0 inidicate a write transaction
        	            // 1'b0   --> don't care bit to seperate phases
        	            // rega   --> register address that want to write into
        	            // 1'b0   --> don't care bit to seperate phases
        	            // value  --> data to write into the register address
        	            // 1'b0   --> don't care bit to seperate phases
        	            // 2'b01  --> SIOD will go from 0 to 1 to indicate a stop tranmission
        	            
        	            data_sr <= {3'b100, id, 1'b0,     regah, 1'b0,  regal, 1'b0,  value, 1'b0,  2'b01};
        	            busy_sr <= {3'b111, 9'b111111111, 9'b111111111, 9'b111111111, 9'b111111111, 2'b11};
        	            taken_temp <= 1'b1;
        	        end
        	        else begin
        	            divider <= divider + 2'd1;
        	            taken_temp <= 1'b0;
        	        end
        	    end
        	    
        	end
        
        // Implement two-write data transmission of SCCB protocol
        else 
        	begin
        	    case ({busy_sr[40:38],busy_sr[2:0]})        // Checking for the start and stop condition
        	        // For START condition
        	        6'b111_111: begin                       // bit 31th of data_sr is transmitted, SIOC must be high
        	            case (divider[7:6])                 // --> SIOD goes from tri-state to high             
        	                2'b00: sioc_temp <= 1;          
        	                2'b01: sioc_temp <= 1;          
        	                2'b10: sioc_temp <= 1;          
        	                default: sioc_temp <= 1;
        	            endcase
        	        end
        	        
        	        6'b111_110: begin                       // bit 30th of data_sr is transmitted
        	            case (divider[7:6])                 // --> SIOD goes from high to low, SIOC must be high
        	                2'b00: sioc_temp <= 1;          // --> complete START condition
        	                2'b01: sioc_temp <= 1;
        	                2'b10: sioc_temp <= 1;
        	                default: sioc_temp <= 1;
        	            endcase
        	        end
        	    
        	        6'b111_100: begin                       // bit 29th of data_sr is transmitted (don't care bit)
        	            case (divider[7:6])                 // --> SIOD goes from tri-state to high, then high to low 
        	                2'b00: sioc_temp <= 0;          //     after SIOC goes from high to low 
        	                2'b01: sioc_temp <= 0;          // --> Ready for first transmission from Master to Slave
        	                2'b10: sioc_temp <= 0;
        	                default: sioc_temp <= 0;
        	            endcase
        	        end
        	    
        	        // For STOP condition
        	        6'b110_000: begin                       // bit 2nd of data_sr is transmitted (don't care bit)
        	            case (divider[7:6])                 // SIOC waits for 1 clock cyle of 200Khz then go high    
        	                2'b00: sioc_temp <= 0;
        	                2'b01: sioc_temp <= 1;
        	                2'b10: sioc_temp <= 1;
        	                default: sioc_temp <= 1;
        	            endcase
        	        end
        	    
        	        6'b100_000: begin                       // bit 1st of data_sr is transmitted
        	            case (divider[7:6])                 // SIOD is low
        	                2'b00: sioc_temp <= 1;          // SIOC must be high
        	                2'b01: sioc_temp <= 1;
        	                2'b10: sioc_temp <= 1;
        	                default: sioc_temp <= 1;
        	            endcase
        	        end
        	    
        	        6'b000_000: begin                       // bit 0th of data_sr is transmitted
        	            case (divider[7:6])                 // SIOD is high
        	                2'b00: sioc_temp <= 1;          // --> SIOD goes from low to high while SIOC is high
        	                2'b01: sioc_temp <= 1;          // --> complete STOP condition for SCCB protocol    
        	                2'b10: sioc_temp <= 1;
        	                default: sioc_temp <= 1;
        	            endcase
        	        end
        	        
        	        // Between START and STOP condition
        	        // SIOC is high on 2 cycles of 400Khz and low on 2 cycle of 400Khz
        	        // --> SIOC is 200Khz
        	        default: begin                          
        	            case (divider[7:6])
        	                2'b00: sioc_temp <= 0;          
        	                2'b01: sioc_temp <= 1;          
        	                2'b10: sioc_temp <= 1;
        	                default: sioc_temp <= 0;
        	            endcase
        	        end
        	    endcase
        	
        	    // Create a frequency for SCCB with is 200KHz
        	    if (divider == 8'b11111111) 
        	    	begin
        	    		busy_sr <= {busy_sr[39:0], 1'b0};        // Update number of bit that get transmitted
        	    		data_sr <= {data_sr[39:0], 1'b1};        // Update new bit 31th by bit 30th
        	    		divider <= 8'd0;                         // Reset counter for clock divider
        	    	end
        	    else 
        	    	begin
        	    		busy_sr <= busy_sr;
						data_sr <= data_sr;
        	    		divider <= divider + 2'd1;
        	    	end
        	    
        	    taken_temp <= 1'b0;
        	
        	end
    end
endmodule