基于 FPGA 实现 IIC(I2C) 协议控制 EEPROM 读写操作

目录

  • 一、源码
    • 1.1 顶层模块
    • 1.2 按键模块
    • 1.3 IIC模块
    • 1.4 数码管模块
  • 二、仿真文件
  • 三、管脚配置文件
  • 四、验证结果

本文内容:FPGA 作为主机,控制 EEPROM 芯片,进行数据读写,同时将写入或读出的数据和地址显示在数码管上,并有标记

  • 前一篇文章:基于 FPGA 使用 Verilog 实现 IIC(I2C) 协议回环数据传输 的 FPGA 作为从机,PC 作为主机,所以 FPGA 是以从机的形式写的源码
  • 本篇文章,是以 FPGA 作为主机,控制 EEPROM 芯片数据读写,区别也不是很大,协议也完全相同,原理就不介绍了,直接上源代码

一、源码

1.1 顶层模块

IIC_top.v

/*========================================*\
    filename        : IIC_top.v
    description     : IIC顶层模块
    up file         : 
    reversion       : 
        v1.0 : 2022-8-22 16:15:32
    author          : 张某某
\*========================================*/

module IIC_top (
    input                               clk             ,
    input                               rst_n           ,
    input           [ 1:0]              key_in          ,

    inout                               SDA             ,

    output                              SCL             ,
    output          [ 5:0]              SEL             ,
    output          [ 7:0]              DIG
);

// Parameter definition
    wire            [ 1:0]              press           ;
    wire                                SDA_in          ;        
    wire                                SDA_out         ;
    wire                                SDA_oe          ;
    wire            [ 7:0]              data            ;
    wire            [ 7:0]              address         ;
    wire            [ 1:0]              eeprom_state    ;

// Signal definition


// Module calls
    key_filter          U_key_filter(
        /*input                 */  .clk            (clk            ),
        /*input                 */  .rst_n          (rst_n          ),
        /*input           [ 1:0]*/  .key_in         (key_in         ),
        /*output  reg     [ 1:0]*/  .press          (press          )
    );

    iic_eeprom          U_iic_eeprom(
        /*input                 */  .clk            (clk            ),
        /*input                 */  .rst_n          (rst_n          ),
        /*input           [ 1:0]*/  .press          (press          ),
        /*input                 */  .SDA_in         (SDA_in         ),
        /*output  reg           */  .SCL            (SCL            ),
        /*output  reg           */  .SDA_out        (SDA_out        ),
        /*output  reg           */  .SDA_oe         (SDA_oe         ),
        /*output  reg     [ 7:0]*/  .data_out       (data           ),
        /*output  reg     [ 7:0]*/  .address_out    (address        ),
        /*output  reg     [ 1:0]*/  .state_out      (eeprom_state   )  // 0表示空闲,1表示写,2表示读
    );

    display             U_display(
        /*input                 */  .clk            (clk            ), // 50MHz
        /*input                 */  .rst_n          (rst_n          ), // 复位信号
        /*input           [ 1:0]*/  .eeprom_state   (eeprom_state   ),
        /*input           [ 7:0]*/  .data           (data           ),
        /*input           [ 7:0]*/  .address        (address        ),
        /*output  reg     [ 5:0]*/  .SEL            (SEL            ), // SEL信号
        /*output  reg     [ 7:0]*/  .DIG            (DIG            )  // DIG信号
    );

// Logic description
    assign SDA_in = SDA;
    assign SDA = SDA_oe ? SDA_out : 1'bz;

endmodule

1.2 按键模块

key_filter.v

/*========================================*\
    filename        : key_filter.v
    description     : 按键消抖模块
    up file         : IIC_top.v
    reversion       : 
        v1.0 : 2022-8-22 16:15:09
    author          : 张某某
\*========================================*/

module key_filter #(parameter MS_20 = 20'd1000_000)(
    input                               clk         ,
    input                               rst_n       ,
    input           [ 1:0]              key_in      ,
  
    output  reg     [ 1:0]              press
);
// 全局变量定义

// 信号定义
    reg             [ 1:0]              key_0       ; // 按键信号当前时钟周期电平
    reg             [ 1:0]              key_1       ; // 按键信号下一个时钟周期电平

    wire            [ 1:0]              key_nedge   ; // 下降沿使能信号
    reg                                 add_flag    ; // 计数使能信号
    reg             [19:0]              delay_cnt   ; // 延时计数器

// 模块功能
    //打拍器
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            key_0 <= 'b1;
            key_1 <= 'b1;
        end
        else begin
            key_0 <= key_in;
            key_1 <= key_0;
        end
    end

    // 检测下降沿
    assign key_nedge = ~key_0 & key_1;

    // 计数使能信号
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            add_flag <= 'b0;
        end
        else if (key_nedge) begin
            add_flag <= 'b1;
        end
        else if (delay_cnt >= MS_20 - 1) begin
            add_flag <= 'b0;
        end
        else begin
            add_flag <= add_flag;
        end
    end

    // 计数20ms
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            delay_cnt <= 20'd0;
        end
        else if (add_flag) begin
            if (delay_cnt >= MS_20 - 1) begin
                delay_cnt <= 20'd0;
            end
            else begin
                delay_cnt <= delay_cnt + 1;
            end
        end
        else begin
            delay_cnt <= 20'd0;
        end
    end

    // 输出
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            press <= 'd0;
        end
        else if (delay_cnt >= MS_20 - 1) begin
            press <= ~key_in;
        end
        else begin
            press <= 'd0;
        end
    end

endmodule

1.3 IIC模块

iic_eeprom.v

/*========================================*\
    filename        : iic_eeprom.v
    description     : IIC控制EEPROM
    up file         : IIC_top.v
    reversion       : 
        v1.0 : 2022-8-22 16:14:06
    author          : 张某某
\*========================================*/

module iic_eeprom (
    input                               clk                 ,
    input                               rst_n               ,
    input           [ 1:0]              press               ,
    input                               SDA_in              ,

    output  reg                         SCL                 ,
    output  reg                         SDA_out             ,
    output  reg                         SDA_oe              ,
    output  reg     [ 7:0]              data_out            ,
    output  reg     [ 7:0]              address_out         ,
    output  reg     [ 1:0]              state_out             // 0表示空闲,1表示写,2表示读
);

// Parameter definition
    parameter       IDLE        =       7'b000_0001         ,
                    START       =       7'b000_0010         ,
                    WR_DATA     =       7'b000_0100         ,
                    RD_DATA     =       7'b000_1000         ,
                    CHECK_ACK   =       7'b001_0000         ,
                    SEND_NACK   =       7'b010_0000         ,
                    STOP        =       7'b100_0000         ;

// Signal definition    
    reg             [ 6:0]              state_c             ;
    reg             [ 6:0]              state_n             ;

    wire                                idle2start          ;
    wire                                start2wr_data       ;
    wire                                wr_data2check_ack   ;
    wire                                check_ack2start     ;
    wire                                check_ack2wr_data   ;
    wire                                check_ack2rd_data   ;
    wire                                rd_data2send_nack   ;
    wire                                check_ack2stop      ;
    wire                                send_nack2stop      ;
    wire                                stop2idle           ;

    reg             [ 6:0]              cnt_400KHz          ; // SCL频率计数器
    wire                                add_cnt_400KHz      ;
    wire                                end_cnt_400KHz      ;

    reg                                 SCL_0               ; // SCL打拍器
    wire                                SCL_podge           ; // SCL上升沿
    wire                                SCL_nedge           ; // SCL下降沿

    reg             [ 2:0]              cnt_bit             ; // 比特计数器
    wire                                add_cnt_bit         ;
    wire                                end_cnt_bit         ;

    reg             [ 1:0]              cnt_byte            ; // 字节计数器
    wire                                add_cnt_byte        ;
    wire                                end_cnt_byte        ;

    reg             [ 1:0]              eeprom_state        ; // 0表示空闲,1表示写,2表示读
    reg             [ 7:0]              data_buffer         ; // 数据缓冲器
    reg             [ 7:0]              pointer_wr          ; // 写数据指针
    reg             [ 7:0]              pointer_rd          ; // 读数据指针

    reg             [ 7:0]              data_generate       ; // 生成写入EEPROM的数据
    wire                                add_data_generate   ;
    wire                                end_data_generate   ;

// Logic description
    /*************************************
        第一段 状态转移
    *************************************/
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            state_c <= IDLE;
        end
        else begin
            state_c <= state_n;
        end
    end

    /*************************************
        第二段 状态转移规律
    *************************************/
    always @(*) begin
        case (state_c)
            IDLE : 
                begin
                    if (idle2start) begin
                        state_n = START;
                    end
                    else begin
                        state_n = state_c;
                    end
                end
            START :
                begin
                    if (start2wr_data) begin
                        state_n = WR_DATA;
                    end
                    else begin
                        state_n = state_c;
                    end
                end
            WR_DATA :
                begin
                    if (wr_data2check_ack) begin
                        state_n = CHECK_ACK;
                    end
                    else begin
                        state_n = state_c;
                    end
                end
            RD_DATA :
                begin
                    if (rd_data2send_nack) begin
                        state_n = SEND_NACK;
                    end
                    else begin
                        state_n = state_c;
                    end
                end
            CHECK_ACK :
                begin
                    if (check_ack2start) begin
                        state_n = START;
                    end
                    else if (check_ack2rd_data) begin
                        state_n = RD_DATA;
                    end
                    else if (check_ack2wr_data) begin
                        state_n = WR_DATA;
                    end
                    else if (check_ack2stop) begin
                        state_n = STOP;
                    end
                    else begin
                        state_n = state_c;
                    end
                end
            SEND_NACK :
                begin
                    if (send_nack2stop) begin
                        state_n = STOP;
                    end
                    else begin
                        state_n = state_c;
                    end
                end
            STOP :
                begin
                    if (stop2idle) begin
                        state_n = IDLE;
                    end
                    else begin
                        state_n = state_c;
                    end
                end
        endcase
    end

    assign idle2start           = state_c == IDLE && press;
    assign start2wr_data        = state_c == START && SCL_nedge;
    assign wr_data2check_ack    = state_c == WR_DATA && end_cnt_bit && cnt_byte <= 'd3;
    assign check_ack2start      = state_c == CHECK_ACK && cnt_byte == 'd1 && SCL_nedge && eeprom_state == 'd2;
    assign check_ack2wr_data    = state_c == CHECK_ACK && SCL_nedge && cnt_byte < 'd2;
    assign check_ack2rd_data    = state_c == CHECK_ACK && cnt_byte == 'd2 && SCL_nedge && eeprom_state == 'd2;
    assign rd_data2send_nack    = state_c == RD_DATA && end_cnt_bit && cnt_byte <= 'd3;
    assign check_ack2stop       = state_c == CHECK_ACK && cnt_byte == 'd2 && SCL_nedge && eeprom_state == 'd1;
    assign send_nack2stop       = state_c == SEND_NACK && cnt_byte == 'd3 && SCL_nedge;
    assign stop2idle            = state_c == STOP && cnt_400KHz == 'd57;

    /*************************************
        第三段 描述输出
    *************************************/
    // 按键控制EEPROM读写
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            eeprom_state <= 'd0;
        end
        else if (stop2idle) begin
            eeprom_state <= 'd0;
        end
        else if (state_c == IDLE) begin
            case (press)
                2'b10 : eeprom_state <= 'd1; // 写状态
                2'b01 : eeprom_state <= 'd2; // 读状态
                default: eeprom_state <= eeprom_state;
            endcase
        end
        else begin
            eeprom_state <= eeprom_state;
        end
    end

    // 按键控制指针
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            pointer_wr <= 'd0;
            pointer_rd <= 'd0;
        end
        else if (stop2idle) begin
            case (eeprom_state)
                'd1 : pointer_wr <= pointer_wr + 'd1;
                'd2 : pointer_rd <= pointer_rd + 'd1;
                default: begin pointer_wr <= pointer_wr; pointer_rd <= pointer_rd;end
            endcase
        end
        else begin
            pointer_wr <= pointer_wr;
            pointer_rd <= pointer_rd;
        end
    end

    // SCL打拍器
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            SCL_0 <= 'd1;
        end
        else begin
            SCL_0 <= SCL;
        end
    end

    assign SCL_podge = SCL & ~SCL_0; // SCL上升沿
    assign SCL_nedge = ~SCL & SCL_0; // SCL下降沿

    // SCL计数器
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            cnt_400KHz <= 'd0;
        end
        else if (add_cnt_400KHz) begin
            if (end_cnt_400KHz) begin
                cnt_400KHz <= 'd0;
            end
            else begin
                cnt_400KHz <= cnt_400KHz + 'd1;
            end
        end
        else begin
            cnt_400KHz <= 'd0;
        end
    end
    assign add_cnt_400KHz = state_c != IDLE;
    assign end_cnt_400KHz = add_cnt_400KHz && cnt_400KHz >= 'd124;

    // SCL输出
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            SCL <= 'd1;
        end
        else begin
            case (cnt_400KHz)
                'd0  : SCL <= 'd1;
                'd59 : SCL <= 'd0;
                default: SCL <= SCL;
            endcase
        end
    end

    // 比特计数器
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            cnt_bit <= 'd0;
        end
        else if (add_cnt_bit) begin
            if (end_cnt_bit) begin
                cnt_bit <= 'd0;
            end
            else begin
                cnt_bit <= cnt_bit + 'd1;
            end
        end
        else begin
            cnt_bit <= cnt_bit;
        end
    end
    assign add_cnt_bit = (state_c == WR_DATA || state_c == RD_DATA) && SCL_nedge;
    assign end_cnt_bit = add_cnt_bit && cnt_bit >= 'd7;

    // 字节计数器
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            cnt_byte <= 'd0;
        end
        else if (check_ack2stop || send_nack2stop) begin
            cnt_byte <= 'd0;
        end
        else if (add_cnt_byte) begin
            if (end_cnt_byte) begin
                cnt_byte <= 'd0;
            end
            else begin
                cnt_byte <= cnt_byte + 'd1;
            end
        end
        else begin
            cnt_byte <= cnt_byte;
        end
    end
    assign add_cnt_byte = check_ack2wr_data || check_ack2rd_data;
    assign end_cnt_byte = add_cnt_byte && cnt_byte >= 'd3;

    // 生成顺序数据
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            data_generate <= 'd0;
        end
        else if (add_data_generate) begin
            if (end_data_generate) begin
                data_generate <= 'd1;
            end
            else begin
                data_generate <= data_generate + 'd1;
            end
        end
        else begin
            data_generate <= data_generate;
        end
    end
    assign add_data_generate = state_c == IDLE && press == 2'b10;
    assign end_data_generate = add_data_generate && data_generate >= 8'hff;

    // 数据缓冲器
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            data_buffer <= 'd0;
        end
        else if (state_c == WR_DATA || state_c == RD_DATA) begin
            case (eeprom_state)
                'd1 : // 1表示写数据到EEPROM中
                    begin
                        case (cnt_byte)
                            'd0 : data_buffer <= 8'ha0;
                            'd1 : data_buffer <= pointer_wr;
                            'd2 : data_buffer <= data_generate;
                            default: data_buffer <= 'd0;
                        endcase
                    end
                'd2 : // 2表示从EEPROM中读数据
                    begin
                        case (cnt_byte)
                            'd0 : data_buffer <= 8'ha0;
                            'd1 : data_buffer <= pointer_rd;
                            'd2 : data_buffer <= 8'ha1;
                            'd3 : 
                                begin
                                    if (SCL) begin
                                        data_buffer[7 - cnt_bit] <= SDA_in;
                                    end
                                    else begin
                                        data_buffer <= data_buffer;
                                    end
                                end
                            default: data_buffer <= 'd0;
                        endcase
                    end
                default: data_buffer <= 'd0;
            endcase
        end
        else begin
            data_buffer <= 'd0;
        end
    end

    // SDA_out输出
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            SDA_oe <= 'd1;
            SDA_out <= 'd1;
        end
        else begin
            case (state_c)
                START : 
                    begin
                        if (cnt_400KHz == 'd29) begin
                            SDA_oe <= 'd1;
                            SDA_out <= 'd0;
                        end
                        else if (cnt_400KHz == 'd69) begin
                            SDA_oe <= 'd1;
                            SDA_out <= 'd1;
                        end
                        else begin
                            SDA_oe <= SDA_oe;
                            SDA_out <= SDA_out;
                        end
                    end
                WR_DATA :
                    begin
                        if (cnt_400KHz == 'd69) begin
                            SDA_oe <= 'd1;
                            SDA_out <= data_buffer[7 - cnt_bit];
                        end
                        else begin
                            SDA_oe <= SDA_oe;
                            SDA_out <= SDA_out;
                        end
                    end
                RD_DATA :
                    begin
                        SDA_oe <= 'd0;
                        SDA_out <= 'd0;
                    end
                CHECK_ACK :
                    begin
                        if (cnt_400KHz == 'd69) begin
                            SDA_oe <= 'd0;
                            SDA_out <= 'd0;
                        end
                        else begin
                            SDA_oe <= SDA_oe;
                            SDA_out <= SDA_out;
                        end
                    end
                SEND_NACK :
                    begin
                        if (cnt_400KHz == 'd69) begin
                            SDA_oe <= 'd1;
                            SDA_out <= 'd1;
                        end
                        else begin
                            SDA_oe <= SDA_oe;
                            SDA_out <= SDA_out;
                        end
                    end
                STOP :
                    begin
                        if (cnt_400KHz == 'd69) begin
                            SDA_oe <= 'd1;
                            SDA_out <= 'd0;
                        end
                        else if (cnt_400KHz == 'd29) begin
                            SDA_oe <= 'd1;
                            SDA_out <= 'd1;
                        end
                        else begin
                            SDA_oe <= SDA_oe;
                            SDA_out <= SDA_out;
                        end
                    end
                default: 
                    begin
                        SDA_oe <= 'd1;
                        SDA_out <= 'd1;
                    end
            endcase
        end
    end

    // 输出数据给数码管显示
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            data_out <= 'd0;
            address_out <= 'd0;
            state_out <= 'd0;
        end
        else if (eeprom_state == 'd1 && wr_data2check_ack && cnt_byte == 'd2) begin
            data_out <= data_buffer;
            address_out <= pointer_wr;
            state_out <= 'd1;
        end
        else if (rd_data2send_nack) begin
            data_out <= data_buffer;
            address_out <= pointer_rd;
            state_out <= 'd2;
        end
        else begin
            data_out <= data_out;
            address_out <= address_out;
            state_out <= state_out;
        end
    end

endmodule

1.4 数码管模块

display.v

/*========================================*\
  filename        : display.v
  description     : 滚动显示输入的数据
  up file         : 
  reversion       : 
      v1.0 : 2022-7-27 18:49:34
  author          : 张某某
\*========================================*/

module display #(parameter  MS_1= 16'd50000)(
    input                               clk                 , // 50MHz
    input                               rst_n               , // 复位信号
    input           [ 1:0]              eeprom_state        ,
    input           [ 7:0]              data                ,
    input           [ 7:0]              address             ,

    output  reg     [ 5:0]              SEL                 , // SEL信号
    output  reg     [ 7:0]              DIG                   // DIG信号
);

// 信号定义
    reg             [15:0]              cnt_flicker         ; // 计数1ms
    wire                                SEL_change          ; // cnt_flicker计满使能信号
    reg             [ 4:0]              tmp_data            ; // 当前DIG的值

    wire            [ 3:0]              num_2               ; // 百位
    wire            [ 3:0]              num_1               ; // 十位
    wire            [ 3:0]              num_0               ; // 个位

    reg                                 pointer             ;
    
// 逻辑描述
    // 闪烁频率计数器
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            cnt_flicker <= 'd0;
        end
        else if (SEL_change) begin
            cnt_flicker <= 'd0;
        end
        else begin
            cnt_flicker <= cnt_flicker + 'd1; 
        end
    end
    assign SEL_change = cnt_flicker >= MS_1 - 'd1 ? 1'b1 : 1'b0;

    // SEL信号输出
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            SEL <= 6'b011_111;
        end
        else if (SEL_change) begin
            SEL <= {SEL[4:0], SEL[5]};
        end
        else begin
            SEL <= SEL;
        end
    end

    // 获取地址的百位、十位、个位
    assign num_2 = address / 100;
    assign num_1 = address % 100 / 10;
    assign num_0 = address % 100 % 10;

    // tmp_data当前SEL位选所对应的DIG十进制值
    always @(*) begin
        case (SEL)
            6'b011_111 : begin tmp_data = data[3:0]; pointer = 'd1;end
            6'b101_111 : begin tmp_data = data[7:4]; pointer = 'd1;end
            6'b110_111 : begin tmp_data = num_0; pointer = 'd0;end
            6'b111_011 :
                begin
                    pointer = 'd1;
                    if (num_2 == 'd0 && num_1 == 'd0) begin
                        tmp_data = 'd16;
                    end
                    else begin
                        tmp_data = num_1;
                    end
                end
            6'b111_101 : 
                begin
                    pointer = 'd1;
                    if (num_2) begin
                        tmp_data = num_2;
                    end
                    else begin
                        tmp_data = 'd16;
                    end
                end
            6'b111_110 : 
                begin
                    pointer = 'd1;
                    case (eeprom_state)
                        'd1 : tmp_data = 'd17;
                        'd2 : tmp_data = 'd18;
                        default: tmp_data = 'd16;
                    endcase
                end
            default: begin tmp_data = 'd0; pointer = 'd1;end
        endcase
    end

    // DIG输出各数字对应的二进制
    always @(*) begin
        case (tmp_data)
            5'd0 : DIG = {pointer, 7'b100_0000};
            5'd1 : DIG = {pointer, 7'b111_1001};
            5'd2 : DIG = {pointer, 7'b010_0100};
            5'd3 : DIG = {pointer, 7'b011_0000};
            5'd4 : DIG = {pointer, 7'b001_1001};
            5'd5 : DIG = {pointer, 7'b001_0010};
            5'd6 : DIG = {pointer, 7'b000_0010};
            5'd7 : DIG = {pointer, 7'b111_1000};
            5'd8 : DIG = {pointer, 7'b000_0000};
            5'd9 : DIG = {pointer, 7'b001_0000};
            5'd10: DIG = {pointer, 7'b000_1000};
            5'd11: DIG = {pointer, 7'b000_0011};
            5'd12: DIG = {pointer, 7'b100_0110};
            5'd13: DIG = {pointer, 7'b010_0001};
            5'd14: DIG = {pointer, 7'b000_0110};
            5'd15: DIG = {pointer, 7'b000_1110};
            5'd16: DIG = {pointer, 7'b111_1111}; // 全黑
            5'd17: DIG = {pointer, 7'b001_1101}; // 写数据标识符
            5'd18: DIG = {pointer, 7'b010_1011}; // 读数据标识符
            default : DIG = 8'b1111_1111;
        endcase
    end

endmodule

二、仿真文件

这里只需要对 iic_eeprom.v 文件进行仿真即可
tb_iic_eeprom.v

/*========================================*\
    filename        : tb_iic_eeprom.v
    description     : iic_eeprom仿真
    up file         : 
    reversion       : 
        v1.0 : 2022-8-22 16:13:37
    author          : 张某某
\*========================================*/

`timescale 1ns/1ns

module tb_iic_eeprom;

// Parameter definition
    parameter       CYC_CLK             = 20            ;

// Drive signal
    reg                                 tb_clk          ;
    reg                                 tb_rst_n        ;
    reg             [ 1:0]              tb_press        ;
    reg                                 tb_SDA_in       ;

// Observation signal
    wire                                tb_SCL          ;
    wire                                tb_SDA_out      ;
    wire                                tb_SDA_oe       ;
    wire            [ 7:0]              tb_data_out     ;

// Module calls
    iic_eeprom              U_iic_eeprom(
        /*input                 */  .clk            (tb_clk         ),
        /*input                 */  .rst_n          (tb_rst_n       ),
        /*input           [ 1:0]*/  .press          (tb_press       ),
        /*input                 */  .SDA_in         (tb_SDA_in      ),
        /*output  reg           */  .SCL            (tb_SCL         ),
        /*output  reg           */  .SDA_out        (tb_SDA_out     ),
        /*output  reg           */  .SDA_oe         (tb_SDA_oe      ),
        /*output  reg     [ 7:0]*/  .data_out       (tb_data_out    ),
        /*output  reg     [ 7:0]*/  .address_out    (tb_address_out ),
        /*output  reg     [ 7:0]*/  .state_out      (tb_state_out   )
    );

// System initialization
    initial begin
        tb_clk = 1'b1;
        tb_rst_n = 1'b0;
        #(CYC_CLK) tb_rst_n = 1'b1;
    end
    always #(CYC_CLK >> 1) tb_clk = ~tb_clk;

    initial begin
        tb_press = 2'b00;
        tb_SDA_in = 1'b0;

        #(1000 * CYC_CLK);

        // 写数据
        tb_press = 2'b10;
        #(CYC_CLK) tb_press = 2'b00;

        #(5000 * CYC_CLK);

        // 读数据
        tb_press = 2'b01;
        #(CYC_CLK) tb_press = 2'b00;

        #(10000 * CYC_CLK);

        $stop;
    end
endmodule

三、管脚配置文件

  • 根据开发板原理图可以知道,EEPROM 采用 24LC04 芯片,再在原理图中找到对应管脚即可
    基于 FPGA 实现 IIC(I2C) 协议控制 EEPROM 读写操作_第1张图片

tcl 文件

set_location_assignment PIN_E1  -to clk
set_location_assignment PIN_E15 -to rst_n
set_location_assignment PIN_E16 -to key_in[0]
set_location_assignment PIN_M15 -to key_in[1]
set_location_assignment PIN_L1  -to SCL
set_location_assignment PIN_L2  -to SDA
set_location_assignment PIN_A4  -to SEL[0]
set_location_assignment PIN_B4  -to SEL[1]
set_location_assignment PIN_A3  -to SEL[2]
set_location_assignment PIN_B3  -to SEL[3]
set_location_assignment PIN_A2  -to SEL[4]
set_location_assignment PIN_B1  -to SEL[5]
set_location_assignment PIN_B7  -to DIG[0]
set_location_assignment PIN_A8  -to DIG[1]
set_location_assignment PIN_A6  -to DIG[2]
set_location_assignment PIN_B5  -to DIG[3]
set_location_assignment PIN_B6  -to DIG[4]
set_location_assignment PIN_A7  -to DIG[5]
set_location_assignment PIN_B8  -to DIG[6]
set_location_assignment PIN_A5  -to DIG[7]

四、验证结果

  • 仿真结果,前面一部分是写的波形,后面一部分是读的波形
    基于 FPGA 实现 IIC(I2C) 协议控制 EEPROM 读写操作_第2张图片
  • 板上结果

IIC控制EEPROM

  • 按左边的按键表示写操作,按右边按键表示读操作
  • 最左边的符号,“u” 表示写数据到 EEPROM 中,“n” 表示从 EEPROM 中读出写入的数据
  • 小数点左边的数字表示当前操作的 EEPROM 地址
  • 小数点右边的数字表示写入或读出的数据

你可能感兴趣的:(FPGA小项目,fpga开发,IIC)