//
//state machine converts USB control data stream into control sequence
//for Wishbone compatible I2C master core
//
// jb/2014 v1.0
//

`timescale 1ns / 10ps

module USB_I2C_state_machine
(
	// I2C Ports
	input            clk,
	input            rst,

	output reg [2:0] i2c_reg_adr,
	output reg [7:0] data_to_i2c,
	input      [7:0] data_from_i2c,
	output reg       i2c_we, 
	output reg       i2c_stb, 
	output reg       i2c_cyc, 
	input            wb_ack_o,

	//USB command stream
	input             i2c_operation_start,
	input      [255:0]USB_cntr_in,
	output reg [255:0]USB_data_out,
	output reg			i2c_queue_done,
	output reg        last_i2c_ack,
   output            usb_tst1
);


//I2C Wishbone registers 
	parameter PRER_LO = 3'b000;
	parameter PRER_HI = 3'b001;
	parameter CTR     = 3'b010;
	parameter RXR     = 3'b011;
	parameter TXR     = 3'b011;
	parameter CR      = 3'b100;
	parameter SR      = 3'b100;
	
	
reg [255:0] USB_shift_reg;
reg         next_I2C_byte;
reg         i2c_operation_start1, i2c_operation_start2, STP;

reg         I2C_whishbone_init, 
            I2C_write, I2C_read, I2C_err, I2C_delay;
reg         next_USB_operation;
reg   [3:0] I2C_n_bytes, byte_cnt;
reg         STOP_flag, ACK_flag;
reg   [12:0]delay_reg, delay_cnt;	

wire [7:0]	I2C_data_byte;
wire  		tip_bit_zero;

//
// USB control data format
//
// {cw1}, {cw2}, ...{8{1'b0}}
// control word cw: {op_code_byte, I2C_address_byte, data_byte_1, data_byte_2, ... data_byte_n}
//
// operation code byte
//      bits[7:5]   
//           000    nop
//           001    I2C_whishbone_init
//           010    I2C_write
//           100    I2C_read
//
//      bit [4]     stop condition
//      bits[3:0]   number of data bytes to be transferred 
//
	always @(posedge clk)
	  if (rst)
	    begin
				I2C_whishbone_init <= 1'b0;
				I2C_write          <= 1'b0;
				I2C_read           <= 1'b0;
				I2C_err            <= 1'b0;
				I2C_delay          <= 1'b0;
				I2C_n_bytes        <= 4'b0001;
				STP                <= 1'b1;
				delay_reg          <= 12'b111100000000;
 	    end
	  else
    if (i2c_operation_start1)
	    begin
				I2C_whishbone_init <= USB_shift_reg[255:253]==3'b001;
				I2C_write          <= USB_shift_reg[255:253]==3'b010;
				I2C_read           <= USB_shift_reg[255:253]==3'b100;
				I2C_err            <= USB_shift_reg[255:253]==3'b011;
				I2C_delay          <= USB_shift_reg[255:253]==3'b111;
				I2C_n_bytes        <= USB_shift_reg[251:248];
				STP                <= USB_shift_reg[252];
				delay_reg          <= {USB_shift_reg[251:248],8'h00};
      end
      

assign  I2C_data_byte =  USB_shift_reg[255:248];



//************************************************************
//                      main state machine                   *
//************************************************************

// machine state coding
  parameter[3:0]
						 IDLE               =  0,
	                LOAD_PRER_LO       =  1,
	                LOAD_PRER_HI       =  2,
	                LOAD_CTR           =  3,
	
	                I2C_WR_TXR_first   =  4,
	                I2C_WR_CR_first    =  5,
	                I2C_WR_TBT_first   =  6,
	                	                
                   I2C_WR_stop_flag   =  7,
	                I2C_WR_TXR_last    =  8,
	                I2C_WR_CR_last     =  9,
	                I2C_WR_TBT_last    =  10,
	                	                
                   I2C_RD_stop_flag   =  11,
	                I2C_RD_CR_last     =  12,
	                I2C_RD_TBT_last    =  13,
	                I2C_get_last_byte  =  14,
	                
	                DELAY              =  15;


	                
reg [3:0] c_state;

assign usb_tst1     = c_state==IDLE;

//test if RxACK from slave received
	always @(posedge clk) 
	begin
		if (rst) last_i2c_ack <= 1'b1;
		else if (wb_ack_o & (i2c_reg_adr==SR) & i2c_we==1'b0) last_i2c_ack <= data_from_i2c[7];
		else     last_i2c_ack <= last_i2c_ack;
	end

	
	always @(posedge clk)
	  if (rst)
	    begin
	        c_state				<= #1 IDLE;
			  next_I2C_byte      <= #1 1'b0;
			  next_USB_operation <= #1 1'b0;
			  i2c_we             <= #1 1'b0;
			  i2c_stb            <= #1 1'b0;
			  i2c_cyc            <= #1 1'b0;
			  i2c_reg_adr        <= #1 SR;
			  data_to_i2c        <= #1 8'h00;
			  byte_cnt           <= #1 4'h0;
			  STOP_flag          <= #1 1'b0;
			  delay_cnt          <= #1 12'h000;
			  i2c_queue_done		<= #1 1'b1;
	    end
	  else
	  begin
	      // initially reset these signals
			  next_I2C_byte      <= #1 1'b0;
			  i2c_we             <= #1 1'b0;
			  i2c_stb            <= #1 1'b0;
			  i2c_cyc            <= #1 1'b0;
			  next_USB_operation <= #1 1'b0;
			  data_to_i2c        <= #1 8'h00;		
          			  			  	
	    case (c_state)
	        IDLE:
			  begin
	          if (i2c_operation_start2)
	            begin
	              byte_cnt  <= #1 I2C_n_bytes;
					  delay_cnt <= #1 delay_reg;
					  
	              if (I2C_whishbone_init)        c_state <= #1 LOAD_PRER_LO;
	              else if (I2C_write | I2C_read | I2C_err) 
	                                             c_state <= #1 I2C_WR_TXR_first;
	              else if (I2C_delay)            c_state <= #1 DELAY;
	              else                           c_state <= #1 IDLE;
					end
	
				  
				 if(USB_shift_reg[255:253]==3'b000)i2c_queue_done <= 1'b1;
				 else if (i2c_operation_start2)    i2c_queue_done <= 1'b0;
			  end
	
					
//************************************************************
//                I2C_whishbone_init command                 *
//************************************************************

	        LOAD_PRER_LO: //load prescaler lo-byte counter
              begin	         
	               if (wb_ack_o) 
	                 begin
									c_state       <= #1 LOAD_PRER_HI;
						         next_I2C_byte <= #1 1'b1;
						         byte_cnt      <= byte_cnt - 4'b0001;
	                 end
	               else          
	                 begin
						        c_state     <= #1 LOAD_PRER_LO;	                 
						        i2c_we      <= #1 1'b1;           // write
						        i2c_stb     <= #1 1'b1;
						        i2c_cyc     <= #1 1'b1;
						        i2c_reg_adr <= #1 PRER_LO;        // prescaler lo-byte address
						        data_to_i2c <= #1 I2C_data_byte;  // prescaler lo-byte data
					         end
	            end

	        LOAD_PRER_HI: //load prescaler hi-byte counter
	            begin	              
	               if (wb_ack_o) 
	                 begin
									c_state       <= #1 LOAD_CTR;
						         next_I2C_byte <= #1 1'b1;
						         byte_cnt      <= byte_cnt - 4'b0001;
	                 end
	               else if (next_I2C_byte)c_state  <= #1 LOAD_PRER_HI;
	               else
	                begin          
						        c_state     <= #1 LOAD_PRER_HI;	               
						        i2c_we      <= #1 1'b1;           // write
						        i2c_stb     <= #1 1'b1;
						        i2c_cyc     <= #1 1'b1;
						        i2c_reg_adr <= #1 PRER_HI;        // prescaler hi-byte address
						        data_to_i2c <= #1 I2C_data_byte;  // prescaler hi-byte data
					         end
				      end					

	        LOAD_CTR: //load CTR register
	            begin	              
	               if (wb_ack_o)          
								begin
									c_state            <= #1 IDLE;
									i2c_reg_adr        <= #1 SR;
									i2c_we      <= #1 1'b0;     //read
						         i2c_stb     <= #1 1'b1;
						         i2c_cyc     <= #1 1'b1;
									next_USB_operation <= #1 1'b1; 
									next_I2C_byte      <= #1 1'b1;
									byte_cnt           <= byte_cnt - 4'b0001;
								end
	               else if (next_I2C_byte)c_state  <= #1 LOAD_CTR;
						     else
								begin          
						        c_state     <= #1 LOAD_CTR;	               
						        i2c_we      <= #1 1'b1;
						        i2c_stb     <= #1 1'b1;
						        i2c_cyc     <= #1 1'b1;
						        i2c_reg_adr <= #1 CTR;            // CTR address
						        data_to_i2c <= #1 I2C_data_byte;  // CRT data
					         end
				      end					

						
//************************************************************
//            generate first part of I2C transfer            *
//************************************************************

// generate first I2C transfer 
	        I2C_WR_TXR_first:  //write TXR; present slave address, set write-bit
	            begin	         
	               if (wb_ack_o) 
	                 begin
	                   c_state            <= #1 I2C_WR_CR_first;
						         next_I2C_byte <= #1 1'b1;
						         byte_cnt      <= byte_cnt - 4'b0001;
	                 end
	               else          
	                 begin
						        c_state     <= #1 I2C_WR_TXR_first;	                 
						        i2c_we      <= #1 1'b1;
						        i2c_stb     <= #1 1'b1;
						        i2c_cyc     <= #1 1'b1;
						        i2c_reg_adr <= #1 TXR;              // TXR register
						        data_to_i2c <= #1 {I2C_data_byte};  // slave addr with RD/WR bit set
					         end

	            end

	        I2C_WR_CR_first:  //write CR;  set command (start, write) 
	            begin	         
	               if (wb_ack_o) c_state <= #1 I2C_WR_TBT_first;
	               else          
	                 begin
						        c_state     <= #1 I2C_WR_CR_first;	                 
						        i2c_we      <= #1 1'b1;
						        i2c_stb     <= #1 1'b1;
						        i2c_cyc     <= #1 1'b1;
						        i2c_reg_adr <= #1 CR;       // CR register
						        data_to_i2c <= #1 {1'b1,I2C_err,6'b010000};  // set command (start, stop, write) 
					         end
	            end

	        I2C_WR_TBT_first:  //read CR; check tip bit, poll it until it is zero
	            begin	         
	               if      (wb_ack_o & tip_bit_zero & I2C_write)    c_state <= #1 I2C_WR_stop_flag;
	               else if (wb_ack_o & tip_bit_zero & I2C_read)     c_state <= #1 I2C_RD_stop_flag;
	               else if (wb_ack_o & tip_bit_zero & I2C_err)  
	                    begin
	                     c_state            <= #1 IDLE;
								i2c_reg_adr        <= #1 SR;
								i2c_we      <= #1 1'b0;     //read
						      i2c_stb     <= #1 1'b1;
						      i2c_cyc     <= #1 1'b1;
	                     next_USB_operation <= #1 1'b1; 
	                    end	                 
	               else          
	                 begin
						        c_state     <= #1 I2C_WR_TBT_first;	                 
						        i2c_we      <= #1 1'b0;     //read
						        i2c_stb     <= #1 1'b1;
						        i2c_cyc     <= #1 1'b1;
						        i2c_reg_adr <= #1 CR;       // CR register 
					         end
	            end

//************************************************************
//                   I2C_write command                       *
//************************************************************
	            
//generate last I2C transfer (write command)
	        I2C_WR_stop_flag: 
            begin
	                   c_state <= #1 I2C_WR_TXR_last; 
	                   if (byte_cnt>4'b0001) STOP_flag <= #1 1'b0;
	                   else                  STOP_flag <= #1 STP;	                 
            end


	        I2C_WR_TXR_last:  //write TXR; present slave address, set write-bit
	            begin	         
	               if (wb_ack_o) c_state   <= #1 I2C_WR_CR_last;
	               else          
	                 begin
						        c_state     <= #1 I2C_WR_TXR_last;	                 
						        i2c_we      <= #1 1'b1;           //write
						        i2c_stb     <= #1 1'b1;
						        i2c_cyc     <= #1 1'b1;
						        i2c_reg_adr <= #1 TXR;            // TXR register
						        data_to_i2c <= #1 I2C_data_byte;  // data to be written
					         end
	            end

	        I2C_WR_CR_last:  //write CR;  set command (stop, write)
	            begin	         
	               if (wb_ack_o) c_state <= #1 I2C_WR_TBT_last;
	               else          
	                 begin
						        c_state     <= #1 I2C_WR_CR_last;	                 
						        i2c_we      <= #1 1'b1;     //write
						        i2c_stb     <= #1 1'b1;
						        i2c_cyc     <= #1 1'b1;
						        i2c_reg_adr <= #1 CR;       // CR register
						        data_to_i2c <= #1 {1'b0,STOP_flag,6'b010000};  // set command (stop, write)
					         end
	            end

	        I2C_WR_TBT_last:  //read CR; check tip bit, poll it until it is zero
	            begin	         
	               if (wb_ack_o & tip_bit_zero & (byte_cnt>4'b0001)) 
	                 begin
	                        c_state            <= #1 I2C_WR_stop_flag;
						         next_I2C_byte      <= #1 1'b1;
						         byte_cnt           <= byte_cnt - 4'b0001;
	                 end
	               else if (wb_ack_o & tip_bit_zero ) 
	                 begin
	                   c_state            <= #1 IDLE;
							 i2c_reg_adr        <= #1 SR;
							 i2c_we      <= #1 1'b0;     //read
						    i2c_stb     <= #1 1'b1;
						    i2c_cyc     <= #1 1'b1;
	                   next_USB_operation <= #1 1'b1; 
						    next_I2C_byte      <= #1 1'b1;
						    byte_cnt           <= byte_cnt - 4'b0001;
	                 end	                 
	               else          
	                 begin
						        c_state     <= #1 I2C_WR_TBT_last;	                 
						        i2c_we      <= #1 1'b0;     //read
						        i2c_stb     <= #1 1'b1;
						        i2c_cyc     <= #1 1'b1;
						        i2c_reg_adr <= #1 CR;       // CR register
					         end
	            end


//************************************************************
//                     I2C_read command                      *
//************************************************************

//generate last I2C transfer (read command)
	        I2C_RD_stop_flag: 
            begin
	                   c_state <= #1 I2C_RD_CR_last;   
	                   if (byte_cnt>4'b0001) 
	                     begin
	                       STOP_flag <= #1 1'b0;
	                       ACK_flag  <= #1 1'b0;//ACK="0"
	                     end
	                   else                
	                     begin
	                       STOP_flag <= #1 1'b1;
	                       ACK_flag  <= #1 1'b1;//ACK="1", NACK
	                     end
            end

	        I2C_RD_CR_last:  //write CR;  set command (stop, read)
	            begin	         
	               if (wb_ack_o) c_state <= #1 I2C_RD_TBT_last;
	               else          
	                 begin
						        c_state     <= #1 I2C_RD_CR_last;	                 
						        i2c_we      <= #1 1'b1;     //write
						        i2c_stb     <= #1 1'b1;
						        i2c_cyc     <= #1 1'b1;
						        i2c_reg_adr <= #1 CR;       // CR register
						        data_to_i2c <= #1 {1'b0,STOP_flag,2'b10,ACK_flag,3'b000};  // set command (stop, read)
					         end
	            end

	        I2C_RD_TBT_last:  //read CR; check tip bit, poll it until it is zero
	            begin	         
	               if (wb_ack_o & tip_bit_zero) 
	                 begin
	                   c_state       <= #1 I2C_get_last_byte;
	                 end
	               else          
	                 begin
						        c_state     <= #1 I2C_RD_TBT_last;	                 
						        i2c_we      <= #1 1'b0;     //read
						        i2c_stb     <= #1 1'b1;
						        i2c_cyc     <= #1 1'b1;
						        i2c_reg_adr <= #1 CR;       // CR register
					         end
	            end

	        I2C_get_last_byte:  //get the last byte received via I2C
	            begin	         
	               if      (wb_ack_o & (byte_cnt>4'b0001)) 
	                 begin
	                   c_state            <= #1 I2C_RD_stop_flag;
						         byte_cnt           <= byte_cnt - 4'b0001;
	                 end	               
	               else if (wb_ack_o ) 
	                 begin
	                   c_state            <= #1 IDLE;
							 i2c_reg_adr        <= #1 SR;
						        i2c_we      <= #1 1'b0;     //read
						        i2c_stb     <= #1 1'b1;
						        i2c_cyc     <= #1 1'b1;
	                   next_USB_operation <= #1 1'b1; 
						    byte_cnt           <= #1 byte_cnt - 4'b0001;
	                 end	                 
	               else          
	                 begin
						        c_state     <= #1 I2C_get_last_byte;	                 
						        i2c_we      <= #1 1'b0;     //read
						        i2c_stb     <= #1 1'b1;
						        i2c_cyc     <= #1 1'b1;
						        i2c_reg_adr <= #1 RXR;       // RXR register
					         end
	            end

	        DELAY:  //insert the delay into the command stream 
	            begin	
	              if(delay_cnt==12'h000)
	                begin
	                 next_USB_operation <= #1 1'b1;
	                 c_state            <= #1 IDLE;
						        i2c_we      <= #1 1'b0;     //read
						        i2c_stb     <= #1 1'b1;
						        i2c_cyc     <= #1 1'b1;
						  i2c_reg_adr        <= #1 SR; 
	                end
	               else
	                begin
	                 c_state   <= #1 DELAY;
	                 delay_cnt <= #1 delay_cnt - 12'h001;
	                end              
	            end



	    endcase
	  end	


//************************************************************
//                     operational part                      *
//************************************************************

assign  tip_bit_zero = data_from_i2c[1]==1'b0; //test if the tip bit is zero

always @ (posedge clk)
   begin
     
    //start sequence
    i2c_operation_start1 <= i2c_operation_start | next_USB_operation;
    i2c_operation_start2 <= i2c_operation_start1;

	  //USB input shift register (control data)
 	  if (rst==1'b1)                      USB_shift_reg <= {256{1'b0}};
	  else if (i2c_operation_start==1'b1) USB_shift_reg <= USB_cntr_in;
	  else if ((next_I2C_byte==1'b1) | (i2c_operation_start1==1'b1))       
	                                      USB_shift_reg <= { USB_shift_reg[247:0], 8'b00000000};
	  
	  //USB output shift register (data from I2C interface of Nevis13)
 	  if (rst==1'b1)                      USB_data_out <= {256{1'b0}};
	  else if ((c_state==I2C_get_last_byte) & (wb_ack_o))
	                                      USB_data_out <= { USB_data_out[247:0], data_from_i2c };
	 end


endmodule
