fpga配置adv7611

请问有fpga配置adv7611的i2c例程吗,最好是附带注释。

如果刚开始学习,请仿照这个链接:https://zhuanlan.zhihu.com/p/543472995

基于FPGA的I2C协议——以EEPROM为例
可以借鉴下
https://blog.csdn.net/weixin_52761825/article/details/125058946

引用chatGPT作答,以下是一个使用FPGA配置ADV7611的I2C例程。这个例程基于Verilog HDL,使用一个I2C总线控制器(如AXI IIC)与ADV7611进行通信。注释已经添加在代码中以帮助您更好地理解代码的功能和工作原理。

/********************************************
*   ADV7611 I2C Configuration Example
*   Author: ChatGPT
*   Date: 2023-04-27
********************************************/

/*********** I2C Registers ************/
// ADV7611 I2C地址
`define ADV7611_I2C_ADDR 8'b100_1010

// ADV7611芯片ID寄存器
`define ADV7611_CHIP_ID_REG 8'h00
// ADV7611芯片ID值
`define ADV7611_CHIP_ID_VAL 8'h20

// ADV7611输入映射寄存器
`define ADV7611_INPUT_MAP_REG 8'h04
// ADV7611输入映射值
`define ADV7611_INPUT_MAP_VAL 8'h00

// ADV7611输入控制寄存器
`define ADV7611_INPUT_CTRL_REG 8'h05
// ADV7611输入控制值
`define ADV7611_INPUT_CTRL_VAL 8'h1f

// ADV7611CEC配置寄存器
`define ADV7611_CEC_CONFIG_REG 8'h15
// ADV7611CEC配置值
`define ADV7611_CEC_CONFIG_VAL 8'h9c

// ADV7611输入信号检测寄存器
`define ADV7611_INPUT_DETECT_REG 8'h42
// ADV7611输入信号检测值
`define ADV7611_INPUT_DETECT_VAL 8'ha3

/*********** I2C Controller ************/
// AXI IIC控制器地址
`define AXI_IIC_ADDR 32'h40000000

// IIC控制器数据寄存器地址
`define IIC_DATA_ADDR 8'h00
// IIC控制器状态寄存器地址
`define IIC_STATUS_ADDR 8'h04
// IIC控制器控制寄存器地址
`define IIC_CONTROL_ADDR 8'h08

// IIC控制器读命令
`define IIC_READ_CMD 8'h01
// IIC控制器写命令
`define IIC_WRITE_CMD 8'h00

/*********** I2C Functions ************/
// 写一个字节到ADV7611的I2C寄存器
function automatic void adv7611_i2c_write(
    input iic_addr, // I2C地址
    input reg_addr, // 寄存器地址
    input data      // 数据
);
    reg [7:0] tx_data;

    // 将寄存器地址和数据组合成一个字节
    tx_data = {reg_addr, data};

    // 向IIC控制器写入数据
    i2c_write(iic_addr, tx_data);
endfunction

// 从ADV7611的I2C寄存器读取一个字节
function automatic byte adv7611_i2c_read(
    input iic_addr, // I2C地址
    input reg_addr  // 寄存器地址
) returns (byte);

    reg [7:0] tx_data;
reg [7:0] rx_data;
// 将寄存器地址转换为单字节
tx_data = {reg_addr};

// 向IIC控制器写入寄存器地址
i2c_write(iic_addr, tx_data);

// 从IIC控制器读取一个字节的数据
rx_data = i2c_read(iic_addr, 1);

// 返回读取的数据
return rx_data;
endfunction

/*********** Main Function ************/
// ADV7611 I2C配置例程
module adv7611_i2c_config(
input wire i2c_scl, // I2C时钟线
input wire i2c_sda // I2C数据线
);

// 设置IIC控制器寄存器
reg [31:0] iic_ctrl = 0;

// 初始化I2C控制器
initial begin
    iic_ctrl[IIC_CONTROL_ADDR] = 8'h80; // 使能IIC控制器
    iic_ctrl[IIC_STATUS_ADDR] = 8'h7;   // 复位IIC状态
end

// 写入芯片ID寄存器
adv7611_i2c_write(`ADV7611_I2C_ADDR, `ADV7611_CHIP_ID_REG, `ADV7611_CHIP_ID_VAL);

// 验证芯片ID
if (adv7611_i2c_read(`ADV7611_I2C_ADDR, `ADV7611_CHIP_ID_REG) != `ADV7611_CHIP_ID_VAL) begin
    $display("Error: ADV7611 Chip ID mismatch!");
    $finish;
end

// 配置输入映射寄存器
adv7611_i2c_write(`ADV7611_I2C_ADDR, `ADV7611_INPUT_MAP_REG, `ADV7611_INPUT_MAP_VAL);

// 配置输入控制寄存器
adv7611_i2c_write(`ADV7611_I2C_ADDR, `ADV7611_INPUT_CTRL_REG, `ADV7611_INPUT_CTRL_VAL);

// 配置CEC配置寄存器
adv7611_i2c_write(`ADV7611_I2C_ADDR, `ADV7611_CEC_CONFIG_REG, `ADV7611_CEC_CONFIG_VAL);

// 配置输入信号检测寄存器
adv7611_i2c_write(`ADV7611_I2C_ADDR, `ADV7611_INPUT_DETECT_REG, `ADV7611_INPUT_DETECT_VAL);

$display("ADV7611 I2C Configuration Successful!");

// 等待1秒
#1000000000;

// 关闭I2C控制器
iic_ctrl[IIC_CONTROL_ADDR] = 8'h00;
endmodule

// I2C写函数
function automatic void i2c_write(
input iic_addr, // I2C地址
input byte data // 数据
);
// 在I2C总线上发送起始信号
i2c_start();

// 发送I2C地址和写命令
i2c_send_byte(iic_addr << 1 | `IIC_WRITE_CMD);

// 发送数据
i2c_send_byte(data);

// 在I2C总线上发送停止信号
i2c_stop();
endfunction

// I2C读函数
function automatic byte i2c_read(
input iic_addr, // I2C地址
input int num_bytes // 要读取的字节数
) returns (byte);

reg [7:0] rx_data;
reg [7:0] tx_data;

// 在I2C总线上发送起始信号
i2c_start();

// 发送I2C地址和读命令
i2c_send_byte(iic_addr << 1 | `IIC_READ_CMD);

// 读取数据
for (int i = 0; i < num_bytes; i++) begin
    if (i == num_bytes - 1) begin
        // 最后一个字节,不发送ACK
        rx_data[i] = i2c_receive_byte(`IIC_NACK);
    end else begin
        // 发送ACK,继续读取下一个字节
        rx_data[i] = i2c_receive_byte(`IIC_ACK);
    end
end

// 在I2C总线上发送停止信号
i2c_stop();

// 返回读取的数据
return rx_data;
endfunction

// I2C总线控制函数
// 发送起始信号
task automatic i2c_start();
// 设置SDA为高电平
assign i2c_sda = 1'b1;
// 在SCL为高电平时设置SDA为低电平
always @(posedge i2c_scl) begin
    if (i2c_sda == 1'b1) begin
        i2c_sda <= 1'b0;
    end
end
endtask

// 发送停止信号
task automatic i2c_stop();
// 设置SDA为低电平
assign i2c_sda = 1'b0;
// 在SCL为高电平时设置SDA为高电平
always @(posedge i2c_scl) begin
    if (i2c_sda == 1'b0) begin
        i2c_sda <= 1'b1;
    end
end
endtask

// 发送一个字节的数据,并返回ACK信号
function automatic bit i2c_send_byte(
input byte data // 要发送的数据
) returns (bit);
// 发送数据
for (int i = 7; i >= 0; i--) begin
    assign i2c_sda = data[i];

    // 在SCL的上升沿时更新SDA的值
    always @(posedge i2c_scl) begin
        if (i2c_sda != data[i]) begin
            $display("Error: SDA value changed during SCL rising edge!");
            $finish;
        end
    end
end

// 接收ACK
assign i2c_sda = 1'b1;
always @(posedge i2c_scl) begin
    if (i2c_sda == 1'b0) begin
        i2c_sda <= 1'b1;
    end
end

// 返回ACK信号
return !i2c_sda;

endfunction

// 接收一个字节的数据,并发送ACK或NACK信号
function automatic byte i2c_receive_byte(
input bitack // 是否发送ACK信号
) returns (byte);

reg [7:0] data;

// 接收数据
for (int i = 7; i >= 0; i--) begin
    // 在SCL的上升沿时读取SDA的值
    always @(posedge i2c_scl) begin
        data[i] <= i2c_sda;
    end
end

// 发送ACK或NACK信号
assign i2c_sda = ~ack;

// 在SCL的上升沿时更新SDA的值
always @(posedge i2c_scl) begin
    if (i2c_sda != ~ack) begin
        $display("Error: SDA value changed during SCL rising edge!");
        $finish;
    end
end

// 返回接收到的数据
return data;

endfunction

// 主函数
module tb;
reg [7:0] reg_data;

// 初始化I2C总线
initial begin
    i2c_scl = 1'b1;
    i2c_sda = 1'b1;
    #10;
    i2c_stop();
    #10;
end

// 从ADV7611的寄存器读取数据
initial begin
    reg_data = adv7611_i2c_read(`ADV7611_I2C_ADDR, `ADV7611_REG_ADDR);
    $display("Data read from ADV7611: %h", reg_data);
end

// 向ADV7611的寄存器写入数据
initial begin
    reg_data = `ADV7611_REG_DATA;
    adv7611_i2c_write(`ADV7611_I2C_ADDR, `ADV7611_REG_ADDR, reg_data);
    $display("Data written to ADV7611: %h", reg_data);
end

// 从I2C设备读取数据
initial begin
    reg_data = i2c_read(`I2C_ADDR, `NUM_BYTES);
    $display("Data read from I2C device: %h", reg_data);
end

// 向I2C设备写入数据
initial begin
    reg_data = `I2C_DATA;
    i2c_write(`I2C_ADDR, reg_data);
    $display("Data written to I2C device: %h", reg_data);
end

endmodule


给你贴一份 米联客-Artix7-MA703-35T/100T 开发板中的 ADV7611 I2C 配置的代码

img

img

例化代码


IIC_ADV7611_Config    IIC_ADV7611_Config_inst
(
    .ADV_CLK    (clk_100m),            // input wire ADV_CLK
    .ADV_RST    (v_rstn),            // input wire ADV_RST
    .ADV_SCLK    (iic_scl),            // output wire ADV_SCLK
    .ADV_SDAT    (iic_sda)            // inout wire ADV_SDAT
);


IIC_ADV7611_Config.v

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2019/03/07 17:22:20
// Design Name: 
// Module Name: IIC_ADV7611_Config
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////


module IIC_ADV7611_Config #(
    parameter    ADV_CLK_FREQ    =    100_000000,    //100 MHz
    parameter    ADV_I2C_FREQ    =    10_000        //10 KHz(< 400KHz)
)
(
 input ADV_CLK,
 input ADV_RST,//低电平复位
 output ADV_SCLK,
 inout ADV_SDAT
);
    
    
    wire I2C_EN;      
//----------------------------------------------
    //i2c timing controller module
    wire    [9:0]    i2c_config_index;
    wire    [23:0]i2c_config_data;
    wire    [9:0]    i2c_config_size;
    wire            i2c_config_done;
    wire        i2c_RW_flag;
    wire    [7:0]    i2c_rdata;        //i2c register data   
    
i2c_timing_ctrl
 #(
.CLK_FREQ    (ADV_CLK_FREQ),    //100 MHz
.I2C_FREQ    (ADV_I2C_FREQ)        //100 kHz(<= 400KHz)
  )
 u_i2c_timing_ctrl
  (
 //global clock
 .clk                (ADV_CLK),        //100MHz
 .rst_n                (I2C_EN),    //system reset
                               
//i2c interface
 .i2c_sclk            (ADV_SCLK),    //i2c clock
 .i2c_sdat            (ADV_SDAT),    //i2c data for bidirection
                   
//i2c config data
 .i2c_RW_flag(i2c_RW_flag),
 .i2c_config_index    (i2c_config_index),    //i2c config reg index, read 2 reg and write xx reg
 .i2c_config_data    (i2c_config_data),    //i2c config data
 .i2c_config_size    (i2c_config_size),    //i2c config data counte
 .i2c_config_done    (i2c_config_done),    //i2c config timing complete
 .i2c_rdata            (i2c_rdata)            //i2c register data while read i2c slave
 );
                   
//----------------------------------------------
 //I2C Configure Data of OV7725
//I2C_OV7725_RGB565_Config    u_I2C_OV7725_RGB565_Config
I2C_ADV7611_Config    u_I2C_ADV7611_Config
(
.clk             (ADV_CLK),
.rst_n           (ADV_RST),
.I2C_EN           (I2C_EN),
.I2C_RW_flag    (i2c_RW_flag),
.I2C_rdata        (i2c_rdata),
.I2C_config_done(i2c_config_done),
.LUT_INDEX        (i2c_config_index),
.LUT_DATA        (i2c_config_data),
.LUT_SIZE        (i2c_config_size)
);   
endmodule


i2c_timing_ctrl.v

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2019/03/07 17:17:54
// Design Name: 
// Module Name: i2c_timing_ctrl
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////


`timescale 1ns/1ns
module    i2c_timing_ctrl
#(
    parameter    CLK_FREQ    =    100_000000,    //100 MHz
    parameter    I2C_FREQ    =    10_000        //10 KHz(< 400KHz)
)
(
    //global clock
    input                clk,        //100MHz
    input                rst_n,        //system reset
    
    //i2c interface
    output                i2c_sclk,    //i2c clock
    inout                i2c_sdat,    //i2c data for bidirection

    //user interface
    input          i2c_RW_flag,
    input        [9:0]    i2c_config_size,    //i2c config data counte
    output    reg    [9:0]    i2c_config_index,    //i2c config reg index, read 2 reg and write xx reg
    input        [23:0]    i2c_config_data,    //i2c config data
    output                i2c_config_done,    //i2c config timing complete
    output    reg    [7:0]    i2c_rdata            //i2c register data while read i2c slave
);

//----------------------------------------
//Delay xxus until i2c slave is steady
reg    [19:0]    delay_cnt;
localparam    DELAY_TOP = CLK_FREQ/1000;    //1ms Setting time after software/hardware reset
//localparam    DELAY_TOP = 17'hff;            //Just for test
always@(posedge clk or negedge rst_n)
begin
    if(!rst_n)
        delay_cnt <= 0;
    else if(delay_cnt < DELAY_TOP - 1'b1)
        delay_cnt <= delay_cnt + 1'b1;
    else
        delay_cnt <= delay_cnt;
end
wire    delay_done = (delay_cnt == DELAY_TOP - 1'b1) ? 1'b1 : 1'b0;    //81us delay


//----------------------------------------
//I2C Control Clock generate
reg    [15:0]    clk_cnt;    //divide for i2c clock
/******************************************
             _______          _______
SCLK    ____|        |________|         |
         ________________ ______________
SDAT    |________________|______________
         _                  _
CLK_EN    | |______________| |____________
                _                   _
CAP_EN    _______| |______________| |_____
*******************************************/
reg    i2c_ctrl_clk;        //i2c control clock, H: valid; L: valid
reg    i2c_transfer_en;    //send i2c data    before, make sure that sdat is steady when i2c_sclk is valid
reg    i2c_capture_en;        //capture i2c data    while sdat is steady from cmos                 
always@(posedge clk or negedge rst_n)
begin
    if(!rst_n)
        begin
        clk_cnt <= 0;
        i2c_ctrl_clk <= 0;
        i2c_transfer_en <= 0;
        i2c_capture_en <= 0;
        end
    else if(delay_done)
        begin
        if(clk_cnt < (CLK_FREQ/I2C_FREQ) - 1'b1)
            clk_cnt <= clk_cnt + 1'd1;
        else
            clk_cnt <= 0;
        //i2c control clock, H: valid; L: valid
        i2c_ctrl_clk <= ((clk_cnt >= (CLK_FREQ/I2C_FREQ)/4 + 1'b1) &&
                        (clk_cnt < (3*CLK_FREQ/I2C_FREQ)/4 + 1'b1)) ? 1'b1 : 1'b0;
        //send i2c data    before, make sure that sdat is steady when i2c_sclk is valid
        i2c_transfer_en <= (clk_cnt == 16'd0) ? 1'b1 : 1'b0;
        //capture i2c data    while sdat is steady from cmos                     
        i2c_capture_en <= (clk_cnt == (2*CLK_FREQ/I2C_FREQ)/4 - 1'b1) ? 1'b1 : 1'b0;
        end
    else
        begin
        clk_cnt <= 0;
        i2c_ctrl_clk <= 0;
        i2c_transfer_en <= 0;
        i2c_capture_en <= 0;
        end
end

//-----------------------------------------
//I2C Timing state Parameter
localparam    I2C_IDLE        =    5'd0;
//Write I2C: {ID_Address, REG_Address, W_REG_Data}
localparam    I2C_WR_START    =    5'd1;
localparam    I2C_WR_IDADDR    =    5'd2;
localparam    I2C_WR_ACK1        =    5'd3;
localparam    I2C_WR_REGADDR    =    5'd4;
localparam    I2C_WR_ACK2        =    5'd5;
localparam    I2C_WR_REGDATA    =    5'd6;
localparam    I2C_WR_ACK3        =    5'd7;
localparam    I2C_WR_STOP        =    5'd8;
//I2C Read: {ID_Address + REG_Address} + {ID_Address + R_REG_Data}
localparam    I2C_RD_START1    =    5'd9;        
localparam    I2C_RD_IDADDR1    =    5'd10;
localparam    I2C_RD_ACK1        =    5'd11;
localparam    I2C_RD_REGADDR    =    5'd12;
localparam    I2C_RD_ACK2        =    5'd13;
localparam    I2C_RD_STOP1    =    5'd14;
localparam    I2C_RD_IDLE        =    5'd15;
localparam    I2C_RD_START2    =    5'd16;
localparam    I2C_RD_IDADDR2    =    5'd17;
localparam    I2C_RD_ACK3        =    5'd18;
localparam    I2C_RD_REGDATA    =    5'd19;
localparam    I2C_RD_NACK        =    5'd20;
localparam    I2C_RD_STOP2    =    5'd21;


//-----------------------------------------
// FSM: always1
reg    [4:0]    current_state, next_state; //i2c write and read state  
always@(posedge clk or negedge rst_n)
begin
    if(!rst_n)
        current_state <= I2C_IDLE;
    else if(i2c_transfer_en)
        current_state <= next_state;
end

//-----------------------------------------
wire    i2c_transfer_end = (current_state == I2C_WR_STOP || current_state == I2C_RD_STOP2) ? 1'b1 : 1'b0;
reg        i2c_ack;    //i2c slave renpose successed
always@(posedge clk or negedge rst_n)
begin
    if(!rst_n)
        i2c_config_index <= 0;
    else if(i2c_transfer_en)
        begin
        if(i2c_transfer_end & ~i2c_ack)
//        if(i2c_transfer_end /*& ~i2c_ack*/)                                            //Just for test
            begin
            if(i2c_config_index < i2c_config_size)    
                i2c_config_index <= i2c_config_index + 1'b1;
//                i2c_config_index <= {i2c_config_index[7:1], ~i2c_config_index[0]};    //Just for test
            else
                i2c_config_index <= i2c_config_size;
            end
        else
            i2c_config_index <= i2c_config_index;
        end
    else
        i2c_config_index <= i2c_config_index;
end
assign    i2c_config_done = (i2c_config_index == i2c_config_size) ? 1'b1 : 1'b0;


//-----------------------------------------
// FSM: always2
reg    [3:0]    i2c_stream_cnt;    //i2c data bit stream count
always@(*)
begin
    next_state = I2C_IDLE;     //state initialization
    case(current_state)
    I2C_IDLE:        //5'd0
        begin
        if(delay_done)    //1ms Setting time after software/hardware reset    
            begin
            if(i2c_transfer_en)
                begin
                //if(i2c_config_index < 8'd2)
                //     next_state = I2C_RD_START1;    //Read I2C Slave ID
                //else 
                if(i2c_config_index < i2c_config_size)
                  if(!i2c_RW_flag)
                      next_state = I2C_WR_START;    //Write Data to I2C
                  else  next_state = I2C_RD_START1;
                else// if(i2c_config_index >= i2c_config_size)
                    next_state = I2C_IDLE;        //Config I2C Complete
                end
            else
                next_state = next_state;
            end
        else
                next_state = I2C_IDLE;        //Wait I2C Bus is steady
        end
    //Write I2C: {ID_Address, REG_Address, W_REG_Data}
    I2C_WR_START:    //5'd1
        begin
        if(i2c_transfer_en)    next_state = I2C_WR_IDADDR;
        else                next_state = I2C_WR_START;
        end
    I2C_WR_IDADDR:    //5'd2
        if(i2c_transfer_en == 1'b1 && i2c_stream_cnt == 4'd8)    
                            next_state = I2C_WR_ACK1;
        else                next_state = I2C_WR_IDADDR;
    I2C_WR_ACK1:    //5'd3
        if(i2c_transfer_en)    next_state = I2C_WR_REGADDR;
        else                next_state = I2C_WR_ACK1;
    I2C_WR_REGADDR:    //5'd4
        if(i2c_transfer_en == 1'b1 && i2c_stream_cnt == 4'd8)    
                            next_state = I2C_WR_ACK2;
        else                next_state = I2C_WR_REGADDR;
    I2C_WR_ACK2:    //5'd5
        if(i2c_transfer_en)    next_state = I2C_WR_REGDATA;
        else                next_state = I2C_WR_ACK2;
    I2C_WR_REGDATA://5'd6
        if(i2c_transfer_en == 1'b1 && i2c_stream_cnt == 4'd8)    
                            next_state = I2C_WR_ACK3;
        else                next_state = I2C_WR_REGDATA;
    I2C_WR_ACK3:    //5'd7
        if(i2c_transfer_en)    next_state = I2C_WR_STOP;
        else                next_state = I2C_WR_ACK3;
    I2C_WR_STOP:    //5'd8
        if(i2c_transfer_en)    next_state = I2C_IDLE;
        else                next_state = I2C_WR_STOP;
    //I2C Read: {ID_Address + REG_Address} + {ID_Address + R_REG_Data}
    I2C_RD_START1:    //5'd9
        if(i2c_transfer_en)    next_state = I2C_RD_IDADDR1;
        else                next_state = I2C_RD_START1;
    I2C_RD_IDADDR1://5'd10
        if(i2c_transfer_en == 1'b1 && i2c_stream_cnt == 4'd8)    
                            next_state = I2C_RD_ACK1;
        else                next_state = I2C_RD_IDADDR1;
    I2C_RD_ACK1:    //5'd11
        if(i2c_transfer_en)    next_state = I2C_RD_REGADDR;
        else                next_state = I2C_RD_ACK1;
    I2C_RD_REGADDR://5'd12
        if(i2c_transfer_en == 1'b1 && i2c_stream_cnt == 4'd8)    
                            next_state = I2C_RD_ACK2;
        else                next_state = I2C_RD_REGADDR;
    I2C_RD_ACK2:    //5'd13
        if(i2c_transfer_en)    next_state = I2C_RD_STOP1;
        else                next_state = I2C_RD_ACK2;
    I2C_RD_STOP1:    //5'd14
        if(i2c_transfer_en)    next_state = I2C_RD_IDLE;
        else                next_state = I2C_RD_STOP1;
    I2C_RD_IDLE:    //5'd15
        if(i2c_transfer_en)    next_state = I2C_RD_START2;
        else                next_state = I2C_RD_IDLE;
    I2C_RD_START2:    //5'd16
        if(i2c_transfer_en)    next_state = I2C_RD_IDADDR2;
        else                next_state = I2C_RD_START2;
    I2C_RD_IDADDR2:    //5'd17
        if(i2c_transfer_en == 1'b1 && i2c_stream_cnt == 4'd8)    
                            next_state = I2C_RD_ACK3;
        else                next_state = I2C_RD_IDADDR2;
    I2C_RD_ACK3:    //5'd18
        if(i2c_transfer_en)    next_state = I2C_RD_REGDATA;
        else                next_state = I2C_RD_ACK3;
    I2C_RD_REGDATA:    //5'd19
        if(i2c_transfer_en == 1'b1 && i2c_stream_cnt == 4'd8)    
                            next_state = I2C_RD_NACK;
        else                next_state = I2C_RD_REGDATA;
    I2C_RD_NACK:    //5'd20
        if(i2c_transfer_en)    next_state = I2C_RD_STOP2;
        else                next_state = I2C_RD_NACK;
    I2C_RD_STOP2:    //5'd21
        if(i2c_transfer_en)    next_state = I2C_IDLE;
        else                next_state = I2C_RD_STOP2;
    default:;    //default vaule        
    endcase
end

//-----------------------------------------
// FSM: always3
//reg    i2c_write_flag, i2c_read_flag;
reg    i2c_sdat_out;        //i2c data output
//reg    [3:0]    i2c_stream_cnt;    //i2c data bit stream count
reg    [7:0]    i2c_wdata;    //i2c data prepared to transfer
always@(posedge clk or negedge rst_n)
begin
    if(!rst_n)
        begin
        i2c_sdat_out <= 1'b1;
        i2c_stream_cnt <= 0;
        i2c_wdata <= 0;
        end
    else if(i2c_transfer_en) 
        begin
        case(next_state)
        I2C_IDLE:    //5'd0
            begin
            i2c_sdat_out <= 1'b1;        //idle state
            i2c_stream_cnt <= 0;
            i2c_wdata <= 0;
            end
        //Write I2C: {ID_Address, REG_Address, W_REG_Data}
        I2C_WR_START:    //5'd1
            begin
            i2c_sdat_out <= 1'b0;
            i2c_stream_cnt <= 0;
            i2c_wdata <= i2c_config_data[23:16];    //ID_Address
            end
        I2C_WR_IDADDR:    //5'd2
            begin
            i2c_stream_cnt <= i2c_stream_cnt + 1'b1;
            i2c_sdat_out <= i2c_wdata[3'd7 - i2c_stream_cnt];
            end
        I2C_WR_ACK1:    //5'd3
            begin
            i2c_stream_cnt <= 0;
            i2c_wdata <= i2c_config_data[15:8];        //REG_Address
            end
        I2C_WR_REGADDR:    //5'd4
            begin
            i2c_stream_cnt <= i2c_stream_cnt + 1'b1;
            i2c_sdat_out <= i2c_wdata[3'd7 - i2c_stream_cnt];
            end
        I2C_WR_ACK2:    //5'd5
            begin
            i2c_stream_cnt <= 0;
            i2c_wdata <= i2c_config_data[7:0];        //W_REG_Data
            end
        I2C_WR_REGDATA:    //5'd6
            begin
            i2c_stream_cnt <= i2c_stream_cnt + 1'b1;
            i2c_sdat_out <= i2c_wdata[3'd7 - i2c_stream_cnt];
            end
        I2C_WR_ACK3:    //5'd7
            i2c_stream_cnt <= 0;
        I2C_WR_STOP:    //5'd8
            i2c_sdat_out <= 1'b0;
        //I2C Read: {ID_Address + REG_Address} + {ID_Address + R_REG_Data}
        I2C_RD_START1:    //5'd10
            begin
            i2c_sdat_out <= 1'b0;
            i2c_stream_cnt <= 0;
            i2c_wdata <= i2c_config_data[23:16];
            end
        I2C_RD_IDADDR1:    //5'd11
            begin
            i2c_stream_cnt <= i2c_stream_cnt + 1'b1;
            i2c_sdat_out <= i2c_wdata[3'd7 - i2c_stream_cnt];
            end
        I2C_RD_ACK1:    //5'd11
            begin
            i2c_stream_cnt <= 0;
            i2c_wdata <= i2c_config_data[15:8];
            end
        I2C_RD_REGADDR:    //5'd12
            begin
            i2c_stream_cnt <= i2c_stream_cnt + 1'b1;
            i2c_sdat_out <= i2c_wdata[3'd7 - i2c_stream_cnt];    
            end
        I2C_RD_ACK2:    //5'd13
            i2c_stream_cnt <= 0;
        I2C_RD_STOP1:    //5'd14
            i2c_sdat_out <= 1'b0;
        I2C_RD_IDLE:    //5'd15
            i2c_sdat_out <= 1'b1;        //idle state
        //-------------------------
        I2C_RD_START2:    //5'd16
            begin
            i2c_sdat_out <= 1'b0;
            i2c_stream_cnt <= 0;
            i2c_wdata <= {i2c_config_data[23:17],1'b1};    
            end
        I2C_RD_IDADDR2:    //5'd17
            begin
            i2c_stream_cnt <= i2c_stream_cnt + 1'b1;
            if(i2c_stream_cnt < 5'd7)
                i2c_sdat_out <= i2c_wdata[3'd7 - i2c_stream_cnt];
            else
                i2c_sdat_out <= 1'b1;    //Read flag for I2C Timing
            end
        I2C_RD_ACK3:    //5'd18
            i2c_stream_cnt <= 0;
        I2C_RD_REGDATA:    //5'd19
            i2c_stream_cnt <= i2c_stream_cnt + 1'b1;
        I2C_RD_NACK:    //5'd20
            i2c_sdat_out <= 1'b1;    //NACK
        I2C_RD_STOP2:    //5'd21
            i2c_sdat_out <= 1'b0;
        endcase
        end
    else
        begin
        i2c_stream_cnt <= i2c_stream_cnt;
        i2c_sdat_out <= i2c_sdat_out;
        end
end

//---------------------------------------------
//respone from slave for i2c data transfer
reg    i2c_ack1, i2c_ack2, i2c_ack3;
//reg    i2c_ack;
//reg    [7:0]    i2c_rdata;
always@(posedge clk or negedge rst_n)
begin
    if(!rst_n)
        begin
        {i2c_ack1, i2c_ack2, i2c_ack3} <= 3'b111;
        i2c_ack <= 1'b1;
        i2c_rdata <= 0;
        end
    else if(i2c_capture_en)
        begin
        case(next_state)
        I2C_IDLE:
            begin
        {i2c_ack1, i2c_ack2, i2c_ack3} <= 3'b111;
        i2c_ack <= 1'b1;
            end
        //Write I2C: {ID_Address, REG_Address, W_REG_Data}
        I2C_WR_ACK1:    i2c_ack1 <= i2c_sdat;
        I2C_WR_ACK2:    i2c_ack2 <= i2c_sdat;
        I2C_WR_ACK3:    i2c_ack3 <= i2c_sdat;
        I2C_WR_STOP:    i2c_ack <= (i2c_ack1 | i2c_ack2 | i2c_ack3);
        //I2C Read: {ID_Address + REG_Address} + {ID_Address + R_REG_Data}
        I2C_RD_ACK1:    i2c_ack1 <= i2c_sdat;
        I2C_RD_ACK2:    i2c_ack2 <= i2c_sdat;
        I2C_RD_ACK3:    i2c_ack3 <= i2c_sdat;
        I2C_RD_STOP2:    i2c_ack <= (i2c_ack1 | i2c_ack2 | i2c_ack3);
        I2C_RD_REGDATA:    i2c_rdata <= {i2c_rdata[6:0], i2c_sdat};
        endcase
        end
    else
        begin
        {i2c_ack1, i2c_ack2, i2c_ack3} <= {i2c_ack1, i2c_ack2, i2c_ack3};
        i2c_ack <= i2c_ack;
        end
end

//---------------------------------------------------
wire    bir_en =(    current_state == I2C_WR_ACK1 || current_state == I2C_WR_ACK2 || current_state == I2C_WR_ACK3 ||
                    current_state == I2C_RD_ACK1 || current_state == I2C_RD_ACK2 || current_state == I2C_RD_ACK3 ||
                    current_state == I2C_RD_REGDATA) ? 1'b1 : 1'b0;
assign    i2c_sclk = (current_state >= I2C_WR_IDADDR && current_state <= I2C_WR_ACK3 ||
                    current_state >= I2C_RD_IDADDR1 && current_state <= I2C_RD_ACK2 ||
                    current_state >= I2C_RD_IDADDR2 && current_state <= I2C_RD_NACK) ? 
                    i2c_ctrl_clk : 1'b1;
assign    i2c_sdat = (~bir_en) ? i2c_sdat_out : 1'bz;

endmodule