//////////////////////////////////////////////////////////////////////////////////
// Company: 武汉芯路恒科技有限公司
// Engineer: www.corecourse.cn
// 
// Create Date: 2019/05/01 00:00:00
// Design Name: 
// Module Name: I2C
// Project Name: I2C
// Target Devices: XC7A35T-2FGG484I
// Tool Versions: Vivado 2018.3
// Description: I2C控制器，兼容1字节和2字节地址段器件
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////

module I2C
(
	clk,
	reset_n,
	
	addr_byte_num,	

	device_addr,	
  burst_len,
  mem_addr,

	wr_en,
	wr_data,
	wr_data_req,

	rd_en,
	rd_data,
	rd_data_valid,

  pro_state,
  one_pro_done,
	
	Scl,
	Sda,
  sda_debug
);

//系统时钟采用50MHz
parameter SYS_CLOCK = 50_000_000;
//SCL总线时钟采用400kHz
parameter SCL_CLOCK = 200_000;
//产生时钟SCL计数器最大值
localparam SCL_CNT_M = SYS_CLOCK/SCL_CLOCK;

	input             clk;           //系统时钟
	input             reset_n;       //系统复位信号

	input     [1:0]   addr_byte_num; //I2C器件数据地址字节数

  input     [6:0]   device_addr;   //待访问的I2C器件地址
	input     [5:0]   burst_len;     //I2C总线连续写/读数据字节个数	
	input     [15:0]  mem_addr;      //I2C器件内部存储地址

	input             wr_en;         //I2C器件写使能
	input     [7:0]   wr_data;       //I2C器件写数据
	output reg        wr_data_req;   //I2C器件写数据请求标志

	input             rd_en;         //I2C器件读使能
	output reg[7:0]   rd_data;       //I2C器件读数据
	output reg        rd_data_valid; //I2C器件读数据有效标志位
  
  output            pro_state;     //对I2C器件操作状态，1：忙，0：空闲
	output reg        one_pro_done;  //对I2C器件一次写/读完成标识 

	output reg        Scl;           //I2C时钟线
	inout             Sda;           //I2C数据线
	output            sda_debug;     //I2C数据线调试信号

	//主状态机状态
	localparam  IDLE         = 10'b00_0000_0001,//空闲状态
              WR_START     = 10'b00_0000_0010,//写开始状态
              WR_CTRL      = 10'b00_0000_0100,//写控制状态
              WR_MEM_ADDR1 = 10'b00_0000_1000,//写寄存器地址，第1字节地址
              WR_MEM_ADDR2 = 10'b00_0001_0000,//写寄存器地址，第2字节地址
              WR_DATA      = 10'b00_0010_0000,//写数据状态
              RD_START     = 10'b00_0100_0000,//读开始状态
              RD_CTRL      = 10'b00_1000_0000,//读控制状态
              RD_DATA      = 10'b01_0000_0000,//读数据状态
              STOP         = 10'b10_0000_0000;//停止状态

  reg [15:0]mem_addr_reg;    //存储器地址寄存器
	wire[7:0] wr_ctrl_word;    //写控制数据寄存器
	wire[7:0] rd_ctrl_word;    //读控制数据寄存器
  reg       wr_state;        //I2C写操作状态标志
  reg       rd_state;        //I2C读操作状态标志
  reg [15:0]scl_cnt;         //SCL时钟计数器
	reg       scl_high;        //SCL时钟高电平中部标志位
	reg       scl_low;         //SCL时钟低电平中部标志位
  reg [9:0] main_state;	     //主状态机状态寄存器
	reg       sda_en;          //sda数据总线控制位
	reg       sda_reg;         //sda数据输出寄存器
  reg [7:0] sda_data_out;    //待输出SDA串行数据
	reg [7:0] sda_data_in;     //SDA串行输入后数据
	reg [7:0] wdata_cnt;       //写数据字节数计数器
	reg [7:0] rdata_cnt;       //读数据字节数计数器
	reg       FF;              //串行输出输入任务执行标志位
  reg       ack_time;  
  reg [3:0] bit_cnt;         //串行数据传输计数器

	//读写控制字
	assign wr_ctrl_word = {device_addr,1'b0};
	assign rd_ctrl_word = {device_addr,1'b1};

	//I2C数据线采用三态门传输
	//assign Sda = sda_en ? sda_reg : 1'bz;
  
  //改进后代码
  assign Sda = sda_reg ? 1'bz : 1'b0;

  assign sda_debug = Sda;

	//I2C操作状态信号的产生，1：忙，0：空闲，无操作
  assign pro_state = wr_state | rd_state;
  
  //1、对读/写寄存器地址进行寄存，仿真读写过程中变化
  //2、对2字节存储器地址的高低字节互换，协议上先传高字节，后传低字节
	always@(posedge clk or negedge reset_n)
	begin
		if(!reset_n)
			mem_addr_reg <= 16'd0;
		else if(wr_en || rd_en)
      if(addr_byte_num != 2'd1)
        mem_addr_reg <= {mem_addr[7:0],mem_addr[15:8]};
      else
        mem_addr_reg <= mem_addr;
		else
			mem_addr_reg <= mem_addr_reg;
	end	
 
  //I2C写操作状态标志信号
	always@(posedge clk or negedge reset_n)
	begin
		if(!reset_n)
			wr_state <= 1'b0;
		else if(wr_en)
			wr_state <= 1'b1;
		else if(one_pro_done)
			wr_state <= 1'b0;
		else
			wr_state <= wr_state;
	end

  //I2C读操作状态标志信号
	always@(posedge clk or negedge reset_n)
	begin
		if(!reset_n)
			rd_state <= 1'b0;
		else if(rd_en)
			rd_state <= 1'b1;
		else if(one_pro_done)
			rd_state <= 1'b0;
		else
			rd_state <= rd_state;
	end

	//scl时钟计数器
	always@(posedge clk or negedge reset_n)
	begin
		if(!reset_n)
			scl_cnt <= 16'd0;
		else if(pro_state)begin
			if(scl_cnt == SCL_CNT_M - 1)
				scl_cnt <= 16'd0;
			else
				scl_cnt <= scl_cnt + 16'd1;
		end
		else
			scl_cnt <= 16'd0;
	end

	//scl时钟,在计数器值到达最大值一半和0时翻转
	always@(posedge clk or negedge reset_n)
	begin
		if(!reset_n)
			Scl <= 1'b1;
		else if(scl_cnt == SCL_CNT_M >>1)
			Scl <= 1'b0;
		else if(scl_cnt == 16'd0)
			Scl <= 1'b1;
		else
			Scl <= Scl;
	end	

	//scl时钟高低电平中部标志位
	always@(posedge clk or negedge reset_n)
	begin
		if(!reset_n)
			scl_high <= 1'b0;
		else if(scl_cnt == (SCL_CNT_M>>2))
			scl_high <= 1'b1;
		else
			scl_high <= 1'b0;
	end
	//scl时钟低电平中部标志位
	always@(posedge clk or negedge reset_n)
	begin
		if(!reset_n)
			scl_low <= 1'b0;
		else if(scl_cnt == (SCL_CNT_M>>1)+(SCL_CNT_M>>2))
			scl_low <= 1'b1;
		else
			scl_low <= 1'b0;
	end	

	//主状态机
	always@(posedge clk or negedge reset_n)
	begin
		if(!reset_n)
    begin
			main_state <= IDLE;
			sda_reg    <= 1'b1;
      sda_en     <= 1'b0;
      sda_data_out<= 8'd0;
      sda_data_in<= 8'd0;
			wdata_cnt  <= 8'd1;
			rdata_cnt  <= 8'd1;
      FF         <= 1'b1;
		end
		else 
    begin
			case(main_state)
				IDLE:
        begin
					sda_reg   <= 1'b1;
          sda_en    <= 1'b0;
          sda_data_out<= 8'd0;
          sda_data_in<= 8'd0;
					wdata_cnt <= 8'd1;
					rdata_cnt <= 8'd1;
          FF        <= 1'b1;
					if(wr_en | rd_en)
						main_state <= WR_START;
					else
						main_state <= IDLE;
				end

				WR_START:
        begin
					if(scl_high)
          begin
						main_state   <= WR_CTRL;
            sda_en       <= 1'b1;
            sda_reg      <= 1'b0;
						sda_data_out <= wr_ctrl_word;
						FF           <= 1'b0;
					end
					else
						main_state <= WR_START;
				end

				WR_CTRL:
        begin
          if(FF == 1'b0)
            send_8bit_data;
          else if(ack_time && scl_high && !Sda)//响应时刻收到响应
          begin
            main_state   <= WR_MEM_ADDR1;
            sda_data_out <= mem_addr_reg[7:0];
            FF           <= 1'b0;
          end
          else if(ack_time && scl_high && Sda)//响应时刻未收到响应
            main_state <= IDLE;
          else
            main_state   <= WR_CTRL;
        end

				WR_MEM_ADDR1:
        begin
          if(FF == 1'b0)
            send_8bit_data;
          else if(ack_time && scl_high && !Sda)//响应时刻收到响应
            if(addr_byte_num != 1'b1)
            begin
              main_state   <= WR_MEM_ADDR2;
              sda_data_out <= mem_addr_reg[15:8];
              FF           <= 1'b0;
            end
            else if(wr_state)
            begin
              main_state   <= WR_DATA;
              sda_data_out <= wr_data;
              FF           <= 1'b0;
            end
            else if(rd_state)
              main_state   <= RD_START;        
          else if(ack_time && scl_high && Sda)//响应时刻未收到响应
            main_state <= IDLE;
          else
            main_state   <= WR_MEM_ADDR1;
        end

				WR_MEM_ADDR2:     //2字节寄存器地址才会经过这个状态
        begin
          if(FF == 1'b0)
            send_8bit_data;
          else if(ack_time && scl_high && !Sda)//响应时刻收到响应
            if(wr_state)
            begin
              main_state   <= WR_DATA;
              sda_data_out <= wr_data;
              FF           <= 1'b0;
            end
            else if(rd_state)
              main_state   <= RD_START;        
          else if(ack_time && scl_high && Sda)//响应时刻未收到响应
            main_state <= IDLE;
          else
            main_state   <= WR_MEM_ADDR2; 
        end            

				WR_DATA:
        begin
					if(FF == 1'b0)
						send_8bit_data;
          else if(ack_time && scl_high && !Sda)//响应时刻收到响应
            if(wdata_cnt == burst_len)
              main_state <= STOP;
            else
            begin
              wdata_cnt    <= wdata_cnt + 1'd1;
						  main_state   <= WR_DATA;
							sda_data_out <= wr_data;
							FF           <= 1'b0;
            end
          else if(ack_time && scl_high && Sda)//响应时刻未收到响应
            main_state <= IDLE;
          else
            main_state   <= WR_DATA;
        end
        
        RD_START:
        begin
					if(scl_high)
          begin
						main_state   <= RD_CTRL;
            sda_en       <= 1'b1;
            sda_reg      <= 1'b0;
						sda_data_out <= rd_ctrl_word;
						FF           <= 1'b0;
					end
					else
						main_state <= RD_START;
				end
        
        RD_CTRL:
        begin
          if(FF == 1'b0)
            send_8bit_data;
          else if(ack_time && scl_high && !Sda)//响应时刻收到响应
          begin
            main_state <= RD_DATA;
            FF         <= 1'b0;
          end
          else if(ack_time && scl_high && Sda)//响应时刻未收到响应
            main_state <= IDLE;
          else
            main_state <= RD_CTRL;
				end
				
				RD_DATA:
        begin
					if(FF == 1'b0)
						receive_8bit_data;
					else if(rdata_cnt == burst_len)//接收完1byte数据，根据读取数据量发送ACK或NACK
          begin
            if(scl_low)
              main_state <= STOP;
            else
              main_state <= RD_DATA;
          end
          else
          begin
            if(scl_low)
            begin
              main_state <= RD_DATA;
              rdata_cnt  <= rdata_cnt + 1'b1;
              FF         <= 1'b0;
            end
            else
            begin
              main_state <= RD_DATA;
              sda_en     <= 1'b1;
              sda_reg    <= 1'b0;
            end
          end
        end          

				STOP:
        begin
          sda_en  <= 1'b1;
          sda_reg <= 1'b0;          
					if(scl_high)begin
						sda_reg    <= 1'b1;
						main_state <= IDLE;
					end
					else
						main_state <= STOP;
				end
				
				default: main_state <= IDLE;
			endcase
		end
  end
		
	//sda串行发送时bit计数器
	always@(posedge clk or negedge reset_n)
	begin
		if(!reset_n)
			bit_cnt <= 1'd0;
		else if((main_state == WR_CTRL)||(main_state == RD_CTRL)||
            (main_state == WR_MEM_ADDR1)||(main_state == WR_MEM_ADDR2)||
				    (main_state == WR_DATA)||(main_state == RD_DATA))
    begin
			if(scl_low && bit_cnt == 4'd8)
				bit_cnt <= 4'd0;
			else if(scl_low)
        bit_cnt <= bit_cnt + 1'd1;
			else
				bit_cnt <= bit_cnt;
		end
		else
			bit_cnt <= 1'd0;
	end

  //输出串行数据任务
	task send_8bit_data;
  begin    
    if(scl_low && (bit_cnt == 4'd8))
    begin
      FF     <= 1'b1;
      sda_en <= 1'b0;
      sda_reg<= 1'b1;
    end    
    else if(scl_low)
    begin    
      sda_en  <= 1'b1;
      sda_reg <= sda_data_out[7];      
      sda_data_out <= {sda_data_out[6:0],1'b0};      
    end
    else
      sda_data_out <= sda_data_out;
  end
	endtask
	
	//串行数据输入任务
	task receive_8bit_data;
  begin
    sda_en <= 1'b0;
    sda_reg<= 1'b1;
    if(scl_low && (bit_cnt == 4'd8))
      FF <= 1'b1;
    else if(scl_high && bit_cnt <= 4'd8)
      sda_data_in <= {sda_data_in[6:0],Sda};
    else
      sda_data_in <= sda_data_in;
  end
	endtask

	//数据接收方对发送的响应检测标志位
	always@(posedge clk or negedge reset_n)
	begin
		if(!reset_n)
			ack_time <= 1'b0;
    else if((bit_cnt == 4'd0) && scl_high)
      ack_time <= 1'b0;
		else if((bit_cnt == 4'd8) && scl_low)
			ack_time <= 1'b1;
		else
			ack_time <= ack_time;
	end	

	//写数据有效标志位
  always@(posedge clk or negedge reset_n)
	begin
    if(!reset_n)
      wr_data_req <= 1'b0;
    else if((main_state == WR_DATA)&& bit_cnt == 1'b0 && scl_low)
      wr_data_req <= 1'b1;
    else
      wr_data_req <= 1'b0;
	end

  //读出数据有效标志位
	always@(posedge clk or negedge reset_n)
	begin
    if(!reset_n)
      rd_data_valid <= 1'b0;
    else if((main_state == RD_DATA)&&(bit_cnt == 4'd8)&& scl_low)
      rd_data_valid <= 1'b1;
    else
      rd_data_valid <= 1'b0;
	end

	//读出的有效数据
	always@(posedge clk or negedge reset_n)
	begin
    if(!reset_n)
      rd_data <= 8'd0;
    else if((main_state == RD_DATA)&&(bit_cnt == 4'd8)&& scl_low)
      rd_data <= sda_data_in;
    else
      rd_data <= rd_data;
	end
  
 always@(posedge clk or negedge reset_n)
	begin
		if(!reset_n)
			one_pro_done <= 1'b0;
		else if((main_state == STOP)&& scl_high)
			one_pro_done <= 1'b1;
		else
			one_pro_done <= 1'b0;
	end	

endmodule 
