njithjw的个人空间 https://blog.eetop.cn/505866 [收藏] [复制] [分享] [RSS]

空间首页 动态 记录 日志 相册 主题 分享 留言板 个人资料

日志

i2c_intf

已有 812 次阅读| 2017-11-1 11:18 |个人分类:FPGA_Verilog|系统分类:硬件设计

//-----------------------------------------------------------------------------------------------//
//PURPOSE  :
//process i2c interface to local ram interface
//-----------------------------------------------------------------------------------------------//
//Description:
///////////////////////////////////////////////////////////////////////////////////////////////////
//////--------------------------------------------------------+
//////|Master|start|dev_addr+w|   |byte_addr|   |data| |stop|
//////+-------------------------------------------------------+
//////|Slave |    |          |ack|         |ack| |ack|    |
//////--------------------------------------------------------+
///////////////////////////////////////////////////////////////////////////////////////////////////
//////--------------------------------------------------------+--------+
//////|Master|start|dev_addr+w|   |byte_addr|   |data| |data|   |stop|
//////+-------------------------------------------------------+--------+
//////|Slave |    |          |ack|         |ack| |ack|    |ack|    |
//////--------------------------------------------------------+---------
///////////////////////////////////////////////////////////////////////////////////////////////////
//////-------------------------------------------------------------------------------+
//////|Master|start|dev_addr+w|   |byte_addr| |start|dev_addr+r| |    |nmack|stop|
//////-------------------------------------------------------------------------------+
//////|Slave |    |   |ack|     |ack|   |          |ack|data|     |    |
//////-------------------------------------------------------------------------------+
///////////////////////////////////////////////////////////////////////////////////////////////////
//////-----------------------------------------------------------------------------------------+
//////|Master|start|dev_addr+w|   |byte_addr| |start|dev_addr+r| |    |mack|    |nmack|stop|
//////-----------------------------------------------------------------------------------------+
//////|Slave |    |   |ack|     |ack|   |          |ack|data|    |data|     |    |
//////-----------------------------------------------------------------------------------------+
///////////////////////////////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------------------------//
`timescale 1ns/1ps
module i2c_intf
    #(
    parameter C_CLK_FREQ = 133_000_000, //时钟频率
    parameter C_IIC_FREQ = 400_000      //IIC 总线速率
    )
    (
    input                               clk,            //时钟
    input                               rst,            //异步复位,高有效

    input       [6:0]                   i2c_dev_addr,   //IIC器件地址
    input       [6:0]                   i2c_sub_addr,   //器件内存储空间地址

    output  reg [6:0]                   i2c_wdata_addr,
    output  reg                         i2c_wdata_en,
    input       [7:0]                   i2c_wdata,      //待写数据

    input                               i2c_data_wr,      //写使能,脉冲,高有效
    input       [6:0]                   i2c_data_wr_len,  //待写数据长度,单位Byte

    input                               i2c_data_rd,      //读使能,脉冲,高有效
    input       [6:0]                   i2c_data_rd_len,  //读数据长度,单位Byte,

    output  reg [7:0]                   i2c_rdata,      //读出数据
    output  reg                         i2c_rdata_rd_v, //读出数据有效指示,脉冲,高有效
    output  reg [6:0]                   i2c_rddata_addr,

    output  reg                         i2c_ready,    //IIC总线空闲,高有效,空闲时可发送I_data_we或I_data_rd信号

    output  reg                         i2c_scl,      //IIC时钟信号
    inout                               i2c_sda      //IIC数据信号
    );

localparam  C_BIT_PERIOD = (C_CLK_FREQ/C_IIC_FREQ)/2-1;
localparam  C_BIT_HALF_PERIOD = C_CLK_FREQ/C_IIC_FREQ/4-1;
localparam  C_BIT_QUAR_PERIOD = C_CLK_FREQ/C_IIC_FREQ/8-1;
localparam  C_PERIOD_WIDTH = F_width(C_BIT_PERIOD)+1;

localparam  ST_IDLE = 4'd0;
localparam  ST_START = 4'd1;
localparam  ST_DEV_ADDR = 4'd2;
localparam  ST_RCV_ACK = 4'd3;
localparam  ST_BYTE_ADDR = 4'd4;
localparam  ST_WDATA = 4'd5;
localparam  ST_RDATA = 4'd6;
localparam  ST_SEND_ACK = 4'd7;
localparam  ST_STOP = 4'd8;
localparam  ST_PRO = 4'd9;


localparam  C_PRO_LEN = 128;
localparam  C_PRO_WIDTH = 8;

reg     [C_PERIOD_WIDTH-1:0]    i2c_clk_cnt;
reg                             i2c_scl_dly;
reg                             i2c_clk_rise;
reg                             i2c_clk_fall;
reg     [2:0]                   i2c_byte_cnt;
reg                             byte_over;
reg                             s_ack;
reg     [6:0]                   rdata_byte_cnt;
reg                             rd_stop_id;
reg     [3:0]                   cur_state;
reg     [3:0]                   next_state;
reg     [3:0]                   cur_state_dly;
reg                             s_iic_sda_v;
reg                             s_iic_sda;
reg     [7:0]                   dev_addr_shift;
reg                             s_iic_stop_id;
reg     [C_PRO_WIDTH-1:0]       s_pro_cnt;
reg                             s_pro_over;
reg                             s_iic_start_id;
reg                             i2c_clk_fall_dly1;
reg                             i2c_clk_fall_dly2;
reg                 wr_rd_id;//1, read; 0, write;
reg     [6:0]       sack_cnt;
reg     [7:0]       wr_data_shift;
reg                 wr_finish;
reg                 i2c_data_wr_lat;
reg                 i2c_data_rd_lat;

//----------------------------------------------------------------------------//
always @ (posedge clk or posedge rst)
begin
    if (rst == 1'b1)
        cur_state <= ST_IDLE;
    else
        cur_state <= next_state;
end

always @(posedge clk or posedge rst)
begin
    if (rst == 1'b1)
        cur_state_dly <= 4'd0;
    else
        cur_state_dly <= cur_state;
end

always @(*)
begin
    case(cur_state)
        ST_IDLE:        //0
            if ((i2c_data_wr || i2c_data_rd) && i2c_ready)//write enable, read enable,
                next_state = ST_START;
            else
                next_state = ST_IDLE;

        ST_START:       //1
            if (i2c_clk_fall_dly2 == 1'b1)
                next_state = ST_DEV_ADDR;
            else
                next_state = ST_START;

        ST_DEV_ADDR:    //2
            if (byte_over == 1'b1)
                next_state = ST_RCV_ACK;
            else
                next_state = ST_DEV_ADDR;

        ST_RCV_ACK:     //3
            if(i2c_clk_fall_dly2)
            begin
                if(s_ack == 1'b1)                   //no ack,
                    next_state = ST_IDLE;
                else if (sack_cnt == 6'd1)
                    next_state = ST_BYTE_ADDR;
                else if (sack_cnt == 6'd2)
                begin
                    if (wr_rd_id == 1'b0)//write
                        next_state = ST_WDATA;
                    else
                        next_state = ST_START;
                end
                else if (sack_cnt >= 6'd3)
                begin
                    if (wr_rd_id == 1'b0)//write
                    begin
                        if (wr_finish == 1'b1)
                            next_state = ST_STOP;
                        else
                            next_state = ST_WDATA;
                    end
                    else
                        next_state = ST_RDATA;
                end
                else
                    next_state = ST_IDLE;//2017/8/29 16:02:28, 
            end
            else
                next_state = ST_RCV_ACK;

        ST_BYTE_ADDR:   //4
            if(byte_over)                     //rec 1 Byte dat
                next_state = ST_RCV_ACK;
            else
                next_state = ST_BYTE_ADDR;

        ST_WDATA:       //5
            if(byte_over)
                next_state = ST_RCV_ACK;
            else
                next_state = ST_WDATA;

        ST_RDATA:       //6
            if(byte_over)
                next_state = ST_SEND_ACK;
            else
                next_state = ST_RDATA;

        ST_SEND_ACK:    //7
            if(i2c_clk_fall_dly2)
            begin
                if (rd_stop_id == 1'b1)
                    next_state = ST_STOP;
                else
                    next_state = ST_RDATA;
            end
            else
                next_state = ST_SEND_ACK;

        ST_STOP:        //8
            if(s_iic_stop_id)
                next_state = ST_PRO;
            else
                next_state = ST_STOP;

        ST_PRO:
            if(s_pro_over)
                next_state = ST_IDLE;
            else
                next_state = ST_PRO;

        default: next_state = ST_IDLE;
    endcase
end
//----------------------------------------------------------------------------//

always @ (posedge clk or posedge rst)
begin
    if (rst == 1'b1)
        s_ack <= 1'b0;
    else if(i2c_clk_rise)
        s_ack <= i2c_sda;
    else ;
end

//-----------------------------------------------------------------------------------------------//
//i2c clock
always @ (posedge clk or posedge rst)
begin
    if (rst == 1'b1)
        i2c_clk_cnt <= 'd0;
    else if (cur_state == ST_IDLE || i2c_clk_cnt == C_BIT_PERIOD)
        i2c_clk_cnt <= 'd0;
    else
        i2c_clk_cnt <= i2c_clk_cnt + 'd1;
end

always @ (posedge clk or posedge rst)
begin
    if (rst == 1'b1)
        i2c_scl <= 1'b1;
    else if(cur_state == ST_IDLE)
        i2c_scl <= 1'b1;
    else if(i2c_clk_cnt == C_BIT_HALF_PERIOD)
        i2c_scl <= ~i2c_scl;
    else ;
end

//-----------------------------------------------------------------------------------------------//
always @ (posedge clk or posedge rst)
begin
    if (rst == 1'b1)
        i2c_scl_dly <= 1'b0;
    else
        i2c_scl_dly <= i2c_scl;
end

always @ (posedge clk or posedge rst)
begin
    if (rst == 1'b1)
    begin
        i2c_clk_rise <= 1'b0;
        i2c_clk_fall <= 1'b0;
    end
    else
    begin
        i2c_clk_rise <= i2c_scl && (~i2c_scl_dly);
        i2c_clk_fall <= i2c_scl_dly && (~i2c_scl);
    end
end

always @ (posedge clk or posedge rst)
begin
    if (rst == 1'b1)
    begin
        i2c_clk_fall_dly1 <= 1'b0;
        i2c_clk_fall_dly2 <= 1'b0;
    end
    else
    begin
        i2c_clk_fall_dly1 <= i2c_clk_fall;
        i2c_clk_fall_dly2 <= i2c_clk_fall_dly1;
    end
end
//-----------------------------------------------------------------------------------------------//
always @ (posedge clk or posedge rst)
begin
    if (rst == 1'b1)
        i2c_byte_cnt <= 3'd0;
    else if (i2c_clk_fall_dly2 == 1'b1)
    begin
        if (cur_state == ST_DEV_ADDR || cur_state == ST_BYTE_ADDR ||
            cur_state == ST_WDATA || cur_state == ST_RDATA)
            i2c_byte_cnt <= i2c_byte_cnt + 3'd1;
        else
            i2c_byte_cnt <= 3'd0;
    end
    else ;
end

always @ ( * )
begin
    if (rst == 1'b1)
        byte_over = 1'b0;
    else if ((i2c_byte_cnt == 3'd7) && (i2c_clk_fall_dly2 == 1'b1))
        byte_over = 1'b1;
    else
        byte_over = 1'b0;
end

always @(posedge clk or posedge rst)
begin
    if (rst == 1'b1)
        s_iic_stop_id <= 1'b0;
    else
        s_iic_stop_id <= i2c_scl && (i2c_clk_cnt == C_BIT_PERIOD) && (cur_state == ST_STOP);
end

always @ (posedge clk or posedge rst)
begin
    if (rst == 1'b1)
        s_iic_start_id <= 1'b0;
    else if(i2c_scl && (i2c_clk_cnt == C_BIT_QUAR_PERIOD) && (cur_state == ST_START))
        s_iic_start_id <= 1'b1;
    else if(cur_state != ST_START)
        s_iic_start_id <= 1'b0;
    else ;
end

always @ (posedge clk or posedge rst)
begin
    if (rst == 1'b1)
        rdata_byte_cnt <= 7'd0;
    else if(cur_state == ST_IDLE)
        rdata_byte_cnt <= 7'd0;
    else if(cur_state == ST_RDATA && cur_state_dly != ST_RDATA)
        rdata_byte_cnt <= rdata_byte_cnt + 7'd1;
    else ;
end

reg     [7:0]       rd_data_shift;
always @ (posedge clk or posedge rst)
begin
    if (rst == 1'b1)
        rd_data_shift <= 8'd0;
    else if (cur_state == ST_IDLE)
        rd_data_shift <= 8'd0;
    else if (cur_state == ST_RDATA && i2c_clk_rise)
        rd_data_shift <= {rd_data_shift[6:0],i2c_sda};
    else ;
end

always @ (posedge clk or posedge rst)
begin
    if (rst == 1'b1)
        i2c_rdata <= 8'd0;
    else if (cur_state == ST_IDLE)
        i2c_rdata <= 8'd0;
    else if (cur_state == ST_SEND_ACK)
        i2c_rdata <= rd_data_shift;
    else ;
end

always @ (posedge clk or posedge rst)
begin
    if (rst == 1'b1)
        i2c_rdata_rd_v <= 1'b0;
    else if (cur_state == ST_SEND_ACK && i2c_clk_rise)
        i2c_rdata_rd_v <= 1'b1;
    else
        i2c_rdata_rd_v <= 1'b0;
end

always @ (posedge clk or posedge rst)
begin
    if (rst == 1'b1)
        i2c_rddata_addr <= 7'd0;
    else if (cur_state == ST_IDLE)
        i2c_rddata_addr <= i2c_sub_addr - 7'd1;
    else if (cur_state == ST_SEND_ACK && i2c_clk_rise)
        i2c_rddata_addr <= i2c_rddata_addr + 7'd1;
    else ;
end

always @ (*)
begin
    if (rdata_byte_cnt == i2c_data_rd_len)
        rd_stop_id = 1'b1;
    else
        rd_stop_id = 1'b0;
end

always @ (posedge clk or posedge rst)
begin
    if (rst == 1'b1)
        i2c_ready <= 1'b0;
    else if(i2c_data_wr || i2c_data_rd)
        i2c_ready <= 1'b0;
    else if(cur_state == ST_IDLE)
        i2c_ready <= 1'b1;
    else ;
end

always @ (posedge clk or posedge rst)
begin
    if (rst == 1'b1)
        s_iic_sda_v <= 1'b0;
    else if (((cur_state_dly == ST_DEV_ADDR) || (cur_state_dly == ST_BYTE_ADDR) ||
              (cur_state_dly == ST_WDATA) || (cur_state_dly == ST_SEND_ACK)) &&
              (s_iic_sda == 1'b0) || (cur_state == ST_STOP) || (s_iic_start_id == 1'b1))
        s_iic_sda_v <= 1'b1;
    else
        s_iic_sda_v <= 1'b0;
end

reg                 i2c_data_rd_latch;
always @ (posedge clk or posedge rst)
begin
    if (rst == 1'b1)
        i2c_data_rd_latch <= 1'b0;
    else if (cur_state == ST_IDLE)
        i2c_data_rd_latch <= i2c_data_rd;
    else ;
end

always @ (posedge clk or posedge rst)
begin
    if (rst == 1'b1)
        wr_rd_id <= 1'b0;
    else if (cur_state == ST_IDLE)
        wr_rd_id <= 1'b0;
    else if (cur_state == ST_BYTE_ADDR)
        wr_rd_id <= i2c_data_rd_latch;
    else ;
end

always @ (posedge clk or posedge rst)
begin
    if (rst == 1'b1)
        dev_addr_shift <= 8'd0;
    else if(cur_state == ST_START)
        dev_addr_shift <= {i2c_dev_addr,wr_rd_id};
    else if(cur_state == ST_DEV_ADDR && i2c_clk_fall_dly2)
        dev_addr_shift <= dev_addr_shift << 1;
    else ;
end

always @ (posedge clk or posedge rst)
begin
    if (rst == 1'b1)
        sack_cnt <= 6'd0;
    else if (cur_state == ST_IDLE)
        sack_cnt <= 6'd0;
    else if (cur_state == ST_RCV_ACK && cur_state_dly != ST_RCV_ACK)
        sack_cnt <= sack_cnt + 6'd1;
    else ;
end

reg     [7:0]       byte_addr_shift;
reg                 i2c_wr_mode;//1 burst mode; 0, single mode
always @ (*)
begin
    if ((i2c_data_wr_lat == 1'b1) && (i2c_data_wr_len > 7'd1))
        i2c_wr_mode = 1'b1;
    else if ((i2c_data_rd_lat == 1'b1) && (i2c_data_rd_len > 7'd1))
        i2c_wr_mode = 1'b1;
    else
        i2c_wr_mode = 1'b0;
end

always @ (posedge clk or posedge rst)
begin
    if (rst == 1'b1)
        byte_addr_shift <= 'd0;
    else if(cur_state == ST_START)
        byte_addr_shift <= {i2c_wr_mode,i2c_sub_addr};
    else if(cur_state == ST_BYTE_ADDR && i2c_clk_fall_dly2)
        byte_addr_shift <= byte_addr_shift << 1;
    else ;
end

always @ (posedge clk or posedge rst)
begin
    if (rst == 1'b1)
        s_iic_sda <= 1'b0;
    else if(cur_state == ST_DEV_ADDR)
        s_iic_sda <= dev_addr_shift[7];
    else if(cur_state == ST_BYTE_ADDR)
        s_iic_sda <= byte_addr_shift[7];
    else if(cur_state == ST_WDATA)
        s_iic_sda <= wr_data_shift[7];
    else if(cur_state == ST_SEND_ACK)
    begin
        if (rd_stop_id == 1'b0)
            s_iic_sda <= 1'b0;
        else
            s_iic_sda <= 1'b1;
    end
    else ;
end

always @ (posedge clk or posedge rst)
begin
    if (rst == 1'b1)
        wr_data_shift <= 8'd0;
    else if (cur_state == ST_RCV_ACK)
        wr_data_shift <= i2c_wdata;
    else if (cur_state == ST_WDATA && i2c_clk_fall_dly2)
        wr_data_shift <= wr_data_shift << 1;
    else ;
end

always @ (posedge clk or posedge rst)
begin
    if (rst == 1'b1)
        i2c_data_wr_lat <= 1'b0;
    else if (cur_state == ST_STOP)
        i2c_data_wr_lat <= 1'b0;
    else if (i2c_data_wr == 1'b1)
        i2c_data_wr_lat <= 1'b1;
    else ;
end

always @ (posedge clk or posedge rst)
begin
    if (rst == 1'b1)
        i2c_data_rd_lat <= 1'b0;
    else if (cur_state == ST_IDLE)
        i2c_data_rd_lat <= 1'b0;
    else if (i2c_data_rd == 1'b1)
        i2c_data_rd_lat <= 1'b1;
    else ;
end

always @ (posedge clk or posedge rst)
begin
    if (rst == 1'b1)
    begin
        i2c_wdata_addr <= 7'd0;
        i2c_wdata_en <= 1'b0;
    end
    else if (cur_state == ST_RCV_ACK && i2c_clk_rise && i2c_data_wr_lat == 1'b1)//5
    begin
        if (sack_cnt == 6'd2)
        begin
            i2c_wdata_addr <= i2c_sub_addr;
            i2c_wdata_en <= 1'b1;
        end
        else if ((i2c_wdata_addr < (i2c_sub_addr + i2c_data_wr_len - 7'd1)) && (sack_cnt > 6'd2))
        begin
            i2c_wdata_addr <= i2c_wdata_addr + 7'd1;
            i2c_wdata_en <= 1'b1;
        end
        else
            i2c_wdata_en <= 1'b0;
    end
    else
        i2c_wdata_en <= 1'b0;
end

always @ (posedge clk or posedge rst)
begin
    if (rst == 1'b1)
        wr_finish <= 1'b0;
    else if (cur_state == ST_IDLE)
        wr_finish <= 1'b0;
    else if (cur_state == ST_WDATA && i2c_clk_fall_dly2)//5
    begin
        if (i2c_wdata_addr >= (i2c_sub_addr + i2c_data_wr_len - 7'd1))
            wr_finish <= 1'b1;
        else
            wr_finish <= 1'b0;
    end
    else ;
end

always @ (posedge clk or posedge rst)
begin
    if (rst == 1'b1)
        s_pro_cnt <= 'd0;
    else if(cur_state == ST_PRO)
        s_pro_cnt <= s_pro_cnt + 'd1;
    else
        s_pro_cnt <= 'd0;
end

always @(posedge clk or posedge rst)
begin
    if (rst == 1'b1)
        s_pro_over <= 1'b0;
    else
        s_pro_over <= (s_pro_cnt == C_PRO_LEN);
end

assign i2c_sda = s_iic_sda_v ? 1'b0 : 1'bz;

function integer F_width;
input integer I_num;
integer i;
begin
    for(i=0;2**i<=I_num;i=i+1)
        F_width = i;
end
endfunction

endmodule


点赞

评论 (0 个评论)

facelist

您需要登录后才可以评论 登录 | 注册

  • 关注TA
  • 加好友
  • 联系TA
  • 0

    周排名
  • 0

    月排名
  • 0

    总排名
  • 0

    关注
  • 2

    粉丝
  • 0

    好友
  • 0

    获赞
  • 17

    评论
  • 3512

    访问数
关闭

站长推荐 上一条 /2 下一条

小黑屋| 关于我们| 联系我们| 在线咨询| 隐私声明| EETOP 创芯网
( 京ICP备:10050787号 京公网安备:11010502037710 )

GMT+8, 2024-4-19 08:54 , Processed in 0.019283 second(s), 8 queries , Gzip On, Redis On.

eetop公众号 创芯大讲堂 创芯人才网
返回顶部