当前位置:   article > 正文

【【IIC模块Verilog实现---用IIC协议从FPGA端读取E2PROM】】_e2prom fpga verilog

e2prom fpga verilog

IIC模块Verilog实现–用IIC协议从FPGA端读取E2PROM

下面是 design 设计

I2C_dri.v

module IIC_CONTROL #(
    parameter             SLAVE_ADDR = 7'b1010000         , // E2PROM 从机地址
    parameter             CLK_FREQ   = 26'd50_000_000     , // 50MHz 的时钟频率
    parameter             I2C_FREQ   = 18'd250_000          // SCL 的时钟频率
  )
  (
    input                            clk                  ,
    input                            rst_n                ,
    //  ----------------------------------------------  //
    input            [15 : 0]         i2c_addr            ,    // 地址
    input            [7  : 0]         i2c_data_w          ,    // 数据
    input                             i2c_rh_wl           ,    // 判断 是 read or write
    input                             bit_control         ,    // 1是 16位 0 是 8位
    input                             i2c_exec            ,

    // ------------------------------------------------    //
    output   reg                      dri_clk             ,
    output   reg     [7 : 0]          i2c_data_r          ,
    output   reg                      i2c_ack             ,
    output   reg                      i2c_done            ,

    // -------------------------------------------------- //
    output   reg                      scl                 ,
    inout                             sda
  );


  // --------------------------------------------------------//
  //    next is  define                                       //
  // --------------------------------------------------------//
  reg         [9 : 0]             clk_cnt              ;
  wire        [8 : 0]             dri_cnt              ;
  reg         [2 : 0]             state                ;
  reg         [2 : 0]             next_state           ;
  reg                             st_done              ; // 在 状态机里面用来提示数据完成可以跳转
  reg                             sda_dir              ; // sda方向控制器
  reg                             sda_out              ; // 选择FPGA输入模式之后赋予sda线上
  wire                            sda_in               ; // sda输入信号
  reg         [6 : 0]             cnt                  ; // 我们为了第三部分状态机而准备的

  reg         [15: 0]             addr_save            ; // 地址存储
  reg         [7 : 0]             data_w_save          ; // 数据写的暂存
  reg                             wr_flag              ; // 0 是 写 1 是 读
  // 这三个是 暂存的方便调度的

  reg         [7 : 0]             data_r_save          ; // 读到的数据存储方便整合

  // --------------------------------------------------------- //
  //  parameter define                                         //
  parameter                 st_idle      =  3'b000        ;  // 空闲状态
  parameter                 st_sladdr    =  3'b001        ;  // 发送器件地址
  parameter                 st_addr16    =  3'b010        ;  // 发送高八位地址
  parameter                 st_addr8     =  3'b011        ;  // 发送低八位地址
  parameter                 st_data_wr   =  3'b100        ;  // 写数据
  parameter                 st_addr_rd   =  3'b101        ;  // 再次发送器件地址读
  parameter                 st_data_rd   =  3'b110        ;  // 读数据
  parameter                 st_stop      =  3'b111        ;  // 结束操作停止位


  //    ---------------------------------------------------- //
  //    next is main  code                                   //
  // -------------------------------------------------------//
  assign dri_cnt = (CLK_FREQ/I2C_FREQ ) >> 2                ;

  always@(posedge clk or negedge rst_n )
  begin
    if(rst_n == 0)
    begin
      dri_clk   <=  0         ;
      clk_cnt   <=  0         ;
    end
    else if( clk_cnt == dri_cnt[8:1] - 1)
    begin
      clk_cnt   <=  0         ;
      dri_clk   <=  ~dri_clk  ;
    end
    else
    begin
      dri_clk   <=  dri_clk     ;
      clk_cnt   <=  clk_cnt + 1 ;
    end
  end

  // 下面开始状态机的叙述




  // 同步时序描述状态转移
  always@(posedge dri_clk or negedge rst_n)
  begin
    if(rst_n == 0)
    begin
      state <= st_idle ;
    end // 处于空闲状态
    else
    begin
      state <= next_state ;
    end
  end



  // 组合逻辑判断状态转移条件
  always@(*)
  begin
    next_state <= st_idle ;
    case(state)

      st_idle :
      begin
        if(i2c_exec == 1)
        begin
          next_state      <= st_sladdr ;
        end
        else
        begin
          next_state      <= st_idle   ;
        end
      end
      // 当触发了i2c_exec 时候 可以由 空闲状态转移到

      st_sladdr :
      begin
        if(st_done == 1)
        begin
          if(bit_control == 1)
            next_state <= st_addr16 ;
          else
            next_state <= st_addr8  ;
        end
        else
        begin
          next_state <= st_sladdr ;
        end
      end

      // 当 触发了 st_done 之后 通过 bit_control 选择是低八位 还是高八位的传输

      st_addr16 :
      begin
        if(st_done == 1)
        begin
          next_state <= st_addr8 ;
        end
        else
        begin
          next_state <= st_addr16 ;
        end
      end

      // 高位 用完 轮到 低位的 传输

      st_addr8  :
      begin
        if(st_done == 1)
        begin
          if(wr_flag == 0)
            next_state <= st_data_wr ;
          else
            next_state <= st_addr_rd ;
        end
        else
        begin
          next_state <= st_addr8 ;
        end
      end

      // 先来判断 写数据的 st_data_wr 数据代号是 4
      st_data_wr :
      begin
        if(st_done == 1)
        begin
          next_state <= st_stop ;
        end
        else
        begin
          next_state <= st_data_wr ;
        end
      end

      //

      st_addr_rd :
      begin
        if(st_done == 1)
        begin
          next_state <= st_data_rd ;
        end
        else
        begin
          next_state <= st_addr_rd ;
        end
      end

      //

      st_data_rd :
      begin
        if(st_done == 1)
        begin
          next_state <= st_stop ;
        end
        else
        begin
          next_state <= st_data_rd ;
        end
      end

      //

      st_stop :
      begin
        if(st_done == 1)
        begin
          next_state <= st_idle ;
        end
        else
        begin
          next_state <= st_stop ;
        end
      end

      default:
        next_state <= st_idle ;
    endcase
  end

  / 下面来考虑另一个状态机的第三部分   --- 时序电路描述状态输出
  // 设置一个变量 来控制 SDA的朝向
  assign     sda    = sda_dir ? sda_out : 1'bz   ;  // sda_dir 为1 FPGA控制
  assign     sda_in = sda                        ;  // 把sda当成了输出


  always@(posedge dri_clk or negedge rst_n )
  begin
    if( rst_n == 0)
    begin
      //首先根据输入输出 来判断 SCL 与 SDA 必须都为高
      scl         <=    1 ;
      sda_dir     <=    1 ;
      sda_out     <=    1 ;
      // 剩下的输出 i2c_data_r(输出) == data_r_save
      i2c_data_r  <=    0 ;
      data_r_save <=    0 ;
      // 下面是端口的另外两个输出 i2c_ack 和 i2c_done
      i2c_ack     <=    0 ;
      i2c_done    <=    0 ;

      // 接下里是 内部信号的调节  这两个一个是内部后续的计数 还有一个本次case完成的结束信号
      cnt         <=    0 ;
      st_done     <=    0 ;

      // 下面是三个暂存信号一个是 读写标志位 还有 传入的地址暂存 传入的数据暂存
      wr_flag     <=    0 ;
      addr_save   <=    0 ;
      data_w_save <=    0 ;
    end
    else
    begin
      st_done   <=    0    ;    // 脉冲信号
      cnt       <= cnt + 1 ;
      //这里写在了 case之前就代表了 不用刻意在内部去调配 st_done 或是cnt
      case(state)
        st_idle :
        begin
          scl         <=    1 ;
          sda_dir     <=    1 ;
          sda_out     <=    1 ;
          //这两个写不写不所谓 因为根本没用到
          i2c_data_r  <=    0 ;
          data_r_save <=    0 ;
          i2c_done    <=    0 ;
          //
          cnt         <=    0 ;
          st_done     <=    0 ;
          // 开始
          if( i2c_exec == 1)
          begin
            wr_flag     <=    i2c_rh_wl  ;
            addr_save   <=    i2c_addr   ;
            data_w_save <=    i2c_data_w ;
            i2c_ack     <=    0 ;
          end
        end
        // 这里先传递的是
        st_sladdr :
        begin
          case(cnt)
            7'd1  :
              sda_out <=  0             ;
            7'd3  :
              scl     <=  0             ;
            7'd4  :
              sda_out <=  SLAVE_ADDR[6] ;

            7'd5  :
              scl     <=  1'b1          ;
            7'd7  :
              scl     <=  1'b0          ;
            7'd8  :
              sda_out <=  SLAVE_ADDR[5] ;

            7'd9  :
              scl     <=  1'b1          ;
            7'd11 :
              scl     <=  1'b0          ;
            7'd12 :
              sda_out <=  SLAVE_ADDR[4] ;

            7'd13 :
              scl     <=  1'b1          ;
            7'd15 :
              scl     <=  1'b0          ;
            7'd16 :
              sda_out <=  SLAVE_ADDR[3] ;

            7'd17 :
              scl     <=  1'b1          ;
            7'd19 :
              scl     <=  1'b0          ;
            7'd20 :
              sda_out <=  SLAVE_ADDR[2] ;

            7'd21 :
              scl     <=  1'b1          ;
            7'd23 :
              scl     <=  1'b0          ;
            7'd24 :
              sda_out <=  SLAVE_ADDR[1] ;

            7'd25 :
              scl     <=  1'b1          ;
            7'd27 :
              scl     <=  1'b0          ;
            7'd28 :
              sda_out <=  SLAVE_ADDR[0] ;

            7'd29 :
              scl     <=  1'b1          ;
            7'd31 :
              scl     <=  1'b0          ;
            7'd32 :
              sda_out <=  1'b0          ;
            // 此处完成了 数据的传递 接下来的任务是 反馈

            7'd33 :
              scl     <=  1'b1          ;
            7'd35 :
              scl     <=  1'b0          ;
            7'd36 :
              sda_dir <=  1'b0          ;   // 下放控制权给从机端口

            7'd37 :
              scl     <=  1'b1          ;
            // 下一时刻判断是否 有正确的反馈拉低 并确定 st_done = 1
            7'd38 :
            begin
              st_done         <=  1'b1          ;
              if( sda_in == 1)
                i2c_ack         <=  1'b1          ;
            end

            7'd39 :
            begin
              scl             <=  1'b0          ;
              cnt             <=  7'b0          ;
            end
            default :
              ;
          endcase
        end


        //发送高8位字节
        st_addr16 :
        begin
          case(cnt)
            7'd0 :
            begin   // 39之后移动一格就是0 0 此处即可以开始
              //把使能交还给FPGA端
              sda_dir     <=    1'b1              ;
              sda_out     <=    addr_save[15]     ;
            end

            // 第一个转换有点时序差距 后面都是 每隔4 sda变化一次

            7'd1   :
              scl         <=   1'b1             ;
            7'd3   :
              scl         <=   1'b0             ;
            7'd4   :
              sda_out     <=   addr_save[14]    ;

            7'd5   :
              scl         <=   1'b1             ;
            7'd7   :
              scl         <=   1'b0             ;
            7'd8   :
              sda_out     <=   addr_save[13]    ;

            7'd9   :
              scl         <=   1'b1             ;
            7'd11  :
              scl         <=   1'b0             ;
            7'd12  :
              sda_out     <=   addr_save[12]    ;

            7'd13  :
              scl         <=   1'b1             ;
            7'd15  :
              scl         <=   1'b0             ;
            7'd16  :
              sda_out     <=   addr_save[11]    ;

            7'd17  :
              scl         <=   1'b1             ;
            7'd19  :
              scl         <=   1'b0             ;
            7'd20  :
              sda_out     <=   addr_save[10]    ;

            7'd21  :
              scl         <=   1'b1             ;
            7'd23  :
              scl         <=   1'b0             ;
            7'd24  :
              sda_out     <=   addr_save[9]     ;

            7'd25  :
              scl         <=   1'b1             ;
            7'd27  :
              scl         <=   1'b0             ;
            7'd28  :
              sda_out     <=   addr_save[8]     ;
            // 29 拉升 31下降 32放控制权 33拉升 34结束并作判断 35 拉低 cnt归零为下一状态准备

            7'd29  :
              scl         <=   1'b1             ;
            7'd31  :
              scl         <=   1'b0             ;
            7'd32  :
              sda_dir     <=   1'b0             ;
            7'd33  :
              scl         <=   1'b1             ;

            7'd34  :
            begin
              st_done     <=   1'b1             ; //完成
              if(sda_in == 1)
                i2c_ack     <=   1'b1             ; // scl拉高时 反馈 i2c_ack = 1 表示有错误
            end

            7'd35  :
            begin
              scl         <=   1'b0             ;
              cnt         <=   7'b0             ;
            end
            default :
              ;
          endcase
        end



        //发送低8位字节
        st_addr8 :
        begin
          // 和上面这个写法是一样的 对于cnt = 0 sda_dir 交回FPGA控制权 并立刻赋值
          case(cnt)
            7'd0:
            begin
              sda_dir <= 1'b1 ;
              sda_out <= addr_save[7];         //字地址
            end
            7'd1  :
              scl     <= 1'b1;
            7'd3  :
              scl     <= 1'b0;
            7'd4  :
              sda_out <= addr_save[6];

            7'd5  :
              scl <= 1'b1;
            7'd7  :
              scl <= 1'b0;
            7'd8  :
              sda_out <= addr_save[5];

            7'd9  :
              scl <= 1'b1;
            7'd11 :
              scl <= 1'b0;
            7'd12 :
              sda_out <= addr_save[4];

            7'd13 :
              scl <= 1'b1;
            7'd15 :
              scl <= 1'b0;
            7'd16 :
              sda_out <= addr_save[3];

            7'd17 :
              scl <= 1'b1;
            7'd19 :
              scl <= 1'b0;
            7'd20 :
              sda_out <= addr_save[2];

            7'd21 :
              scl <= 1'b1;
            7'd23 :
              scl <= 1'b0;
            7'd24 :
              sda_out <= addr_save[1];

            7'd25 :
              scl <= 1'b1;
            7'd27 :
              scl <= 1'b0;
            7'd28 :
              sda_out <= addr_save[0];



            7'd29  :
              scl         <=   1'b1             ;
            7'd31  :
              scl         <=   1'b0             ;
            7'd32  :
              sda_dir     <=   1'b0             ;
            7'd33  :
              scl         <=   1'b1             ;

            7'd34  :
            begin
              st_done     <=   1'b1             ; //完成
              if(sda_in == 1)
                i2c_ack     <=   1'b1             ; // scl拉高时 反馈 i2c_ack = 1 表示有错误
            end

            7'd35  :
            begin
              scl         <=   1'b0             ;
              cnt         <=   7'b0             ;
            end
            default :
              ;
          endcase
        end



        //
        st_data_wr :
        begin
          // 和上面这个写法是一样的 对于cnt = 0 sda_dir 交回FPGA控制权 并立刻赋值
          case(cnt)
            7'd0:
            begin
              sda_dir <= 1'b1 ;
              sda_out <= data_w_save[7];         //字地址
            end
            7'd1  :
              scl     <= 1'b1;
            7'd3  :
              scl     <= 1'b0;
            7'd4  :
              sda_out <= data_w_save[6];

            7'd5  :
              scl <= 1'b1;
            7'd7  :
              scl <= 1'b0;
            7'd8  :
              sda_out <= data_w_save[5];

            7'd9  :
              scl <= 1'b1;
            7'd11 :
              scl <= 1'b0;
            7'd12 :
              sda_out <= data_w_save[4];

            7'd13 :
              scl <= 1'b1;
            7'd15 :
              scl <= 1'b0;
            7'd16 :
              sda_out <= data_w_save[3];

            7'd17 :
              scl <= 1'b1;
            7'd19 :
              scl <= 1'b0;
            7'd20 :
              sda_out <= data_w_save[2];

            7'd21 :
              scl <= 1'b1;
            7'd23 :
              scl <= 1'b0;
            7'd24 :
              sda_out <= data_w_save[1];

            7'd25 :
              scl <= 1'b1;
            7'd27 :
              scl <= 1'b0;
            7'd28 :
              sda_out <= data_w_save[0];

            // 29 拉升 31下降 32放控制权 33拉升 34结束并作判断 35 拉低 cnt归零为下一状态准备

            7'd29  :
              scl         <=   1'b1             ;
            7'd31  :
              scl         <=   1'b0             ;
            7'd32  :
              sda_dir     <=   1'b0             ;
            7'd33  :
              scl         <=   1'b1             ;

            7'd34  :
            begin
              st_done     <=   1'b1             ; //完成
              if(sda_in == 1)
                i2c_ack     <=   1'b1             ; // scl拉高时 反馈 i2c_ack = 1 表示有错误
            end

            7'd35  :
            begin
              scl         <=   1'b0             ;
              cnt         <=   7'b0             ;
            end
            default :
              ;
          endcase
        end


        // 读控制信号 可以开始读了
        st_addr_rd :
        begin
          // 这里的过程应该和上面的那个 st_sladdr一样 先写地址
          //  一样又不太一样
          case(cnt)
            7'd0 :
            begin
              sda_dir <= 1'b1;
              sda_out <= 1'b1;
            end
            7'd1 :
              scl <= 1'b1;
            7'd2 :
              sda_out <= 1'b0;          //重新开始
            7'd3 :
              scl <= 1'b0;
            7'd4 :
              sda_out <= SLAVE_ADDR[6]; //传送器件地址
            7'd5 :
              scl <= 1'b1;
            7'd7 :
              scl <= 1'b0;
            7'd8 :
              sda_out <= SLAVE_ADDR[5];
            7'd9 :
              scl <= 1'b1;
            7'd11:
              scl <= 1'b0;
            7'd12:
              sda_out <= SLAVE_ADDR[4];
            7'd13:
              scl <= 1'b1;
            7'd15:
              scl <= 1'b0;
            7'd16:
              sda_out <= SLAVE_ADDR[3];
            7'd17:
              scl <= 1'b1;
            7'd19:
              scl <= 1'b0;
            7'd20:
              sda_out <= SLAVE_ADDR[2];
            7'd21:
              scl <= 1'b1;
            7'd23:
              scl <= 1'b0;
            7'd24:
              sda_out <= SLAVE_ADDR[1];
            7'd25:
              scl <= 1'b1;
            7'd27:
              scl <= 1'b0;
            7'd28:
              sda_out <= SLAVE_ADDR[0];
            7'd29:
              scl <= 1'b1;
            7'd31:
              scl <= 1'b0;
            7'd32:
              sda_out <= 1'b1;          //1:读
            7'd33:
              scl <= 1'b1;
            7'd35:
              scl <= 1'b0;
            7'd36:
            begin
              sda_dir <= 1'b0;
              sda_out <= 1'b1;
            end
            7'd37:
              scl     <= 1'b1;
            7'd38:
            begin                     //从机应答
              st_done <= 1'b1;
              if(sda_in == 1'b1)           //高电平表示未应答
                i2c_ack <= 1'b1;         //拉高应答标志位
            end
            7'd39:
            begin
              scl <= 1'b0;
              cnt <= 7'b0;
            end
            default :
              ;
          endcase
        end

        st_data_rd :
        begin                        //读取数据(8 bit)
          case(cnt)
            7'd0:
              sda_dir <= 1'b0;
            7'd1:
              scl       <= 1'b1;
            7'd2 :
              data_r_save[7] <= sda_in;


            7'd3:
              scl  <= 1'b0;
            7'd5:
              scl       <= 1'b1   ;
            7'd6:
              data_r_save[6] <= sda_in ;


            7'd7:
              scl  <= 1'b0;
            7'd9:
              scl       <= 1'b1  ;
            7'd10 :
              data_r_save[5] <= sda_in;


            7'd11:
              scl  <= 1'b0;
            7'd13:
              scl       <= 1'b1  ;
            7'd14:
              data_r_save[4] <= sda_in;


            7'd15:
              scl  <= 1'b0;
            7'd17:
              scl       <= 1'b1  ;
            7'd18:
              data_r_save[3] <= sda_in;


            7'd19:
              scl  <= 1'b0;
            7'd21:
              scl       <= 1'b1  ;
            7'd22:
              data_r_save[2] <= sda_in;


            7'd23:
              scl  <= 1'b0;
            7'd25:
              scl       <= 1'b1  ;
            7'd26:
              data_r_save[1] <= sda_in;


            7'd27:
              scl  <= 1'b0;
            7'd29:
              scl       <= 1'b1  ;
            7'd30:
              data_r_save[0] <= sda_in;




            7'd31:
              scl  <= 1'b0;
            7'd32:
            begin
              sda_dir <= 1'b1;
              sda_out <= 1'b1;
            end
            7'd33:
              scl     <= 1'b1;
            7'd34:
              st_done <= 1'b1;          //非应答
            7'd35:
            begin
              scl <= 1'b0;
              cnt <= 7'b0;
              i2c_data_r <= data_r_save;
            end
            default  :
              ;
          endcase
        end


        st_stop:
        begin                           //结束I2C操作
          case(cnt)
            7'd0:
            begin
              sda_dir <= 1'b1;             //结束I2C
              sda_out <= 1'b0;
            end
            7'd1 :
              scl     <= 1'b1;
            7'd3 :
              sda_out <= 1'b1;
            7'd15:
              st_done <= 1'b1;
            7'd16:
            begin
              cnt      <= 7'b0;
              i2c_done <= 1'b1;            //向上层模块传递I2C结束信号
            end
            default  :
              ;
          endcase
        end
      endcase
    end
  end

endmodule
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • 310
  • 311
  • 312
  • 313
  • 314
  • 315
  • 316
  • 317
  • 318
  • 319
  • 320
  • 321
  • 322
  • 323
  • 324
  • 325
  • 326
  • 327
  • 328
  • 329
  • 330
  • 331
  • 332
  • 333
  • 334
  • 335
  • 336
  • 337
  • 338
  • 339
  • 340
  • 341
  • 342
  • 343
  • 344
  • 345
  • 346
  • 347
  • 348
  • 349
  • 350
  • 351
  • 352
  • 353
  • 354
  • 355
  • 356
  • 357
  • 358
  • 359
  • 360
  • 361
  • 362
  • 363
  • 364
  • 365
  • 366
  • 367
  • 368
  • 369
  • 370
  • 371
  • 372
  • 373
  • 374
  • 375
  • 376
  • 377
  • 378
  • 379
  • 380
  • 381
  • 382
  • 383
  • 384
  • 385
  • 386
  • 387
  • 388
  • 389
  • 390
  • 391
  • 392
  • 393
  • 394
  • 395
  • 396
  • 397
  • 398
  • 399
  • 400
  • 401
  • 402
  • 403
  • 404
  • 405
  • 406
  • 407
  • 408
  • 409
  • 410
  • 411
  • 412
  • 413
  • 414
  • 415
  • 416
  • 417
  • 418
  • 419
  • 420
  • 421
  • 422
  • 423
  • 424
  • 425
  • 426
  • 427
  • 428
  • 429
  • 430
  • 431
  • 432
  • 433
  • 434
  • 435
  • 436
  • 437
  • 438
  • 439
  • 440
  • 441
  • 442
  • 443
  • 444
  • 445
  • 446
  • 447
  • 448
  • 449
  • 450
  • 451
  • 452
  • 453
  • 454
  • 455
  • 456
  • 457
  • 458
  • 459
  • 460
  • 461
  • 462
  • 463
  • 464
  • 465
  • 466
  • 467
  • 468
  • 469
  • 470
  • 471
  • 472
  • 473
  • 474
  • 475
  • 476
  • 477
  • 478
  • 479
  • 480
  • 481
  • 482
  • 483
  • 484
  • 485
  • 486
  • 487
  • 488
  • 489
  • 490
  • 491
  • 492
  • 493
  • 494
  • 495
  • 496
  • 497
  • 498
  • 499
  • 500
  • 501
  • 502
  • 503
  • 504
  • 505
  • 506
  • 507
  • 508
  • 509
  • 510
  • 511
  • 512
  • 513
  • 514
  • 515
  • 516
  • 517
  • 518
  • 519
  • 520
  • 521
  • 522
  • 523
  • 524
  • 525
  • 526
  • 527
  • 528
  • 529
  • 530
  • 531
  • 532
  • 533
  • 534
  • 535
  • 536
  • 537
  • 538
  • 539
  • 540
  • 541
  • 542
  • 543
  • 544
  • 545
  • 546
  • 547
  • 548
  • 549
  • 550
  • 551
  • 552
  • 553
  • 554
  • 555
  • 556
  • 557
  • 558
  • 559
  • 560
  • 561
  • 562
  • 563
  • 564
  • 565
  • 566
  • 567
  • 568
  • 569
  • 570
  • 571
  • 572
  • 573
  • 574
  • 575
  • 576
  • 577
  • 578
  • 579
  • 580
  • 581
  • 582
  • 583
  • 584
  • 585
  • 586
  • 587
  • 588
  • 589
  • 590
  • 591
  • 592
  • 593
  • 594
  • 595
  • 596
  • 597
  • 598
  • 599
  • 600
  • 601
  • 602
  • 603
  • 604
  • 605
  • 606
  • 607
  • 608
  • 609
  • 610
  • 611
  • 612
  • 613
  • 614
  • 615
  • 616
  • 617
  • 618
  • 619
  • 620
  • 621
  • 622
  • 623
  • 624
  • 625
  • 626
  • 627
  • 628
  • 629
  • 630
  • 631
  • 632
  • 633
  • 634
  • 635
  • 636
  • 637
  • 638
  • 639
  • 640
  • 641
  • 642
  • 643
  • 644
  • 645
  • 646
  • 647
  • 648
  • 649
  • 650
  • 651
  • 652
  • 653
  • 654
  • 655
  • 656
  • 657
  • 658
  • 659
  • 660
  • 661
  • 662
  • 663
  • 664
  • 665
  • 666
  • 667
  • 668
  • 669
  • 670
  • 671
  • 672
  • 673
  • 674
  • 675
  • 676
  • 677
  • 678
  • 679
  • 680
  • 681
  • 682
  • 683
  • 684
  • 685
  • 686
  • 687
  • 688
  • 689
  • 690
  • 691
  • 692
  • 693
  • 694
  • 695
  • 696
  • 697
  • 698
  • 699
  • 700
  • 701
  • 702
  • 703
  • 704
  • 705
  • 706
  • 707
  • 708
  • 709
  • 710
  • 711
  • 712
  • 713
  • 714
  • 715
  • 716
  • 717
  • 718
  • 719
  • 720
  • 721
  • 722
  • 723
  • 724
  • 725
  • 726
  • 727
  • 728
  • 729
  • 730
  • 731
  • 732
  • 733
  • 734
  • 735
  • 736
  • 737
  • 738
  • 739
  • 740
  • 741
  • 742
  • 743
  • 744
  • 745
  • 746
  • 747
  • 748
  • 749
  • 750
  • 751
  • 752
  • 753
  • 754
  • 755
  • 756
  • 757
  • 758
  • 759
  • 760
  • 761
  • 762
  • 763
  • 764
  • 765
  • 766
  • 767
  • 768
  • 769
  • 770
  • 771
  • 772
  • 773
  • 774
  • 775
  • 776
  • 777
  • 778
  • 779
  • 780
  • 781
  • 782
  • 783
  • 784
  • 785
  • 786
  • 787
  • 788
  • 789
  • 790
  • 791
  • 792
  • 793
  • 794
  • 795
  • 796
  • 797
  • 798
  • 799
  • 800
  • 801
  • 802
  • 803
  • 804
  • 805
  • 806
  • 807
  • 808
  • 809
  • 810
  • 811
  • 812
  • 813
  • 814
  • 815
  • 816
  • 817
  • 818
  • 819
  • 820
  • 821
  • 822
  • 823
  • 824
  • 825
  • 826
  • 827
  • 828
  • 829
  • 830
  • 831
  • 832
  • 833
  • 834
  • 835
  • 836
  • 837
  • 838
  • 839
  • 840
  • 841
  • 842
  • 843
  • 844
  • 845
  • 846
  • 847
  • 848
  • 849
  • 850

E2PROM.v

module E2PROM #(
// //EEPROM写数据需要添加间隔时间,读数据则不需要
parameter      WR_WAIT_TIME = 14'd5000, //写入间隔时间
parameter      MAX_BYTE     = 16'd256  //读写测试的字节个数
) (
    input                              clk         ,
    input                              rst_n       ,
    
    // from I2C-control 
    input           [7 : 0]            i2c_data_r  ,  // 读出来的数据
    input                              i2c_ack     ,  // 应答
    input                              i2c_done    ,  // i2c完成信号   

    // give to i2c
    output   reg                       i2c_exec    ,
    output   reg                       i2c_rh_wl   ,
    output   reg    [15 : 0]           i2c_addr    ,
    output   reg    [7 : 0]            i2c_data_w  ,

    // gvie it to led
    output   reg                       rw_done     ,  // e2prom 读写测试完成
    output   reg                       rw_result      // e2prom 的结果 0 : 失败 1 :成功   
);
   



// reg define 
reg   [1:0]    flow_cnt  ; //状态流控制
reg   [13:0]   wait_cnt  ; //延时计数器





always@(posedge clk or negedge rst_n) begin 
    if(rst_n == 0) begin 
        i2c_exec       <=     0   ;
        i2c_rh_wl      <=     0   ;
        i2c_addr       <=     0   ;
        i2c_data_w     <=     0   ;

        rw_done        <=     0   ;
        rw_result      <=     0   ;

        flow_cnt       <=     0   ;
        wait_cnt       <=     0   ; end
        else  begin 
            i2c_exec   <=     0   ;   // 把 i2c_ecec 看成是一个脉冲信号
            case(flow_cnt) 
            2'd0 : begin 
           if(wait_cnt == (WR_WAIT_TIME - 1) ) begin 
            wait_cnt <= 0 ; 
             if(i2c_addr == MAX_BYTE) begin   // 表示256个数据写入其中 
                i2c_addr  <= 16'b0;
                i2c_rh_wl <= 1'b1;
                flow_cnt  <= 2'd2;  end  
                else begin
                    flow_cnt <= flow_cnt + 2'b1;
                    i2c_exec <= 1'b1;
                end  
                end
                else begin 
                    wait_cnt <= wait_cnt + 1 ; end
                end

                2'd1 : begin 
                    if(i2c_done == 1'b1) begin                  //EEPROM单次写入完成
                        flow_cnt   <= 2'd0;
                        i2c_addr   <= i2c_addr + 16'b1;           //地址0~255分别写入
                        i2c_data_w <= i2c_data_w + 8'b1;         //数据0~255
                    end    
                    else begin 
                        flow_cnt    <= flow_cnt    ;
                        i2c_addr    <= i2c_addr    ;
                        i2c_data_w  <= i2c_data_w  ; end
                    end
                    
                2'd2 : begin 
                    flow_cnt <= flow_cnt + 2'b1;
                    i2c_exec <= 1'b1;   end
                
                2'd3 : begin 
                    if(i2c_done == 1'b1) begin                 //EEPROM单次读出完成
                        //读出的值错误或者I2C未应答,读写测试失败
                        if((i2c_addr[7:0] != i2c_data_r) || (i2c_ack == 1'b1)) begin
                            rw_done <= 1'b1;
                            rw_result <= 1'b0;
                        end
                        else if(i2c_addr == (MAX_BYTE - 16'b1))begin //读写测试成功
                            rw_done   <= 1'b1;
                            rw_result <= 1'b1;
                        end    
                        else begin
                            flow_cnt <= 2'd2;
                            i2c_addr <= i2c_addr + 16'b1;
                        end
                    end                 
                end
                default : ;
            endcase    
        end
    end    
    
    endmodule    
             
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106

led.v

module LED 
    #(parameter L_TIME = 17'd125_000
    )
    (
    input        clk       ,  //时钟信号
    input        rst_n     ,  //复位信号
                 
    input        rw_done   ,  //错误标志
    input        rw_result ,  //E2PROM读写测试完成
    output  reg  led          //E2PROM读写测试结果 0:失败 1:成功
);

//reg define
reg          rw_done_flag;    //读写测试完成标志
reg  [16:0]  led_cnt     ;    //led计数

//*****************************************************
//**                    main code
//*****************************************************

//读写测试完成标志
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        rw_done_flag <= 1'b0;
    else if(rw_done)
        rw_done_flag <= 1'b1;
end        

//错误标志为1时PL_LED0闪烁,否则PL_LED0常亮
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        led_cnt <= 17'd0;
        led <= 1'b0;
    end
    else begin
        if(rw_done_flag) begin
            if(rw_result)                          //读写测试正确
                led <= 1'b1;                       //led灯常亮
            else begin                             //读写测试错误
                led_cnt <= led_cnt + 17'd1;
                if(led_cnt == (L_TIME - 17'b1)) begin
                    led_cnt <= 17'd0;
                    led <= ~led;                   //led灯闪烁
                end
                else
                    led <= led;
            end
        end
        else
            led <= 1'b0;                           //读写测试完成之前,led灯熄灭
    end    
end

endmodule
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54

top.v

module IIC_top  #(
    parameter    SLAVE_ADDR = 7'b1010000     , //器件地址(SLAVE_ADDR)
    parameter    BIT_CTRL   = 1'b1           , //字地址位控制参数(16b/8b)
    parameter    CLK_FREQ   = 26'd50_000_000 , //i2c_dri模块的驱动时钟频率(CLK_FREQ)
    parameter    I2C_FREQ   = 18'd250_000    , //I2C的SCL时钟频率
    parameter    L_TIME     = 17'd125_000    , //led闪烁时间参数
    parameter    MAX_BYTE   = 16'd256         //读写测试的字节个数
    )(
    input                            sys_clk      ,
    input                            sys_rst_n    ,
    // i2c interface 
    output                           i2c_scl      ,
    inout                            i2c_sda      ,
    // led
    output                           led   
);
wire           dri_clk   ; //I2C操作时钟
wire           i2c_exec  ; //I2C触发控制
wire   [15:0]  i2c_addr  ; //I2C操作地址
wire   [ 7:0]  i2c_data_w; //I2C写入的数据
wire           i2c_done  ; //I2C操作结束标志
wire           i2c_ack   ; //I2C应答标志 0:应答 1:未应答
wire           i2c_rh_wl ; //I2C读写控制
wire   [ 7:0]  i2c_data_r; //I2C读出的数据
wire           rw_done   ; //E2PROM读写测试完成
wire           rw_result ; //E2PROM读写测试结果 0:失败 1:成功 


E2PROM#(
    .WR_WAIT_TIME ( 14'd5000 ),
    .MAX_BYTE     ( MAX_BYTE )
)u_E2PROM(
    .clk          ( dri_clk          ),
    .rst_n        ( sys_rst_n        ),
    .i2c_data_r   ( i2c_data_r   ),
    .i2c_ack      ( i2c_ack      ),
    .i2c_done     ( i2c_done     ),
    .i2c_exec     ( i2c_exec     ),
    .i2c_rh_wl    ( i2c_rh_wl    ),
    .i2c_addr     ( i2c_addr     ),
    .i2c_data_w   ( i2c_data_w   ),
    .rw_done      ( rw_done      ),
    .rw_result    ( rw_result    )
);

IIC_CONTROL#(
    .SLAVE_ADDR   ( SLAVE_ADDR ),
    .CLK_FREQ     ( CLK_FREQ ),
    .I2C_FREQ     ( I2C_FREQ )
)u_IIC_CONTROL(
    .clk          ( sys_clk          ),
    .rst_n        ( sys_rst_n        ),
    .i2c_addr     ( i2c_addr     ),
    .i2c_data_w   ( i2c_data_w   ),
    .i2c_rh_wl    ( i2c_rh_wl    ),
    .bit_control  ( BIT_CTRL  ),
    .i2c_exec     ( i2c_exec     ),
    .dri_clk      ( dri_clk      ),
    .i2c_data_r   ( i2c_data_r   ),
    .i2c_ack      ( i2c_ack      ),
    .i2c_done     ( i2c_done     ),
    .scl          ( i2c_scl          ),
    .sda          ( i2c_sda    )
);


LED#(
    .L_TIME     ( L_TIME )
)u_LED(
    .clk        ( dri_clk        ),
    .rst_n      ( sys_rst_n      ),
    .rw_done    ( rw_done    ),
    .rw_result  ( rw_result  ),
    .led        ( led        )
);

endmodule 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77

下面是testbench

EEPROM_AT24C64.v

`timescale 1ns/1ns
`define timeslice 1250
module EEPROM_AT24C64(
scl,
sda
);
input scl; 
inout sda; 
reg out_flag; 
reg[7:0] memory[8191:0]; 
reg[12:0]address; 
reg[7:0]memory_buf; 
reg[7:0]sda_buf; 
reg[7:0]shift; 
reg[7:0]addr_byte_h; 
reg[7:0]addr_byte_l; 
reg[7:0]ctrl_byte; 
reg[1:0]State;
integer i;
//---------------------------
parameter
r7 = 8'b1010_1111, w7 = 8'b1010_1110, //main7
r6 = 8'b1010_1101, w6 = 8'b1010_1100, //main6
r5 = 8'b1010_1011, w5 = 8'b1010_1010, //main5
r4 = 8'b1010_1001, w4 = 8'b1010_1000, //main4
r3 = 8'b1010_0111, w3 = 8'b1010_0110, //main3
r2 = 8'b1010_0101, w2 = 8'b1010_0100, //main2
r1 = 8'b1010_0011, w1 = 8'b1010_0010, //main1
r0 = 8'b1010_0001, w0 = 8'b1010_0000; //main0
assign sda = (out_flag == 1) ? sda_buf[7] : 1'bz;

initial
begin
addr_byte_h = 0;
addr_byte_l = 0;
ctrl_byte = 0;
out_flag = 0;
sda_buf = 0;
State = 2'b00;
memory_buf = 0;
address = 0;
shift = 0;
for(i=0;i<=8191;i=i+1)
memory[i] = 0;
end
always@(negedge sda)
begin
if(scl == 1)
begin
State = State + 1;
if(State == 2'b11)
disable write_to_eeprom;
end
end

always@(posedge sda)
begin
if(scl == 1) 
stop_W_R;
else
begin
casex(State)
2'b01:begin
read_in;
if(ctrl_byte == w7 || ctrl_byte == w6
|| ctrl_byte == w5 || ctrl_byte == w4
|| ctrl_byte == w3 || ctrl_byte == w2
|| ctrl_byte == w1 || ctrl_byte == w0)
begin
State = 2'b10;
write_to_eeprom; 
end
else
State = 2'b00;
end
2'b11:
read_from_eeprom;
default:
State = 2'b00;
endcase
end
end 
task stop_W_R;
begin
State = 2'b00;
addr_byte_h = 0;
addr_byte_l = 0;
ctrl_byte = 0;
out_flag = 0;
sda_buf = 0;
end
endtask

task read_in;
begin
shift_in(ctrl_byte);
shift_in(addr_byte_h);
shift_in(addr_byte_l);
end
endtask

task write_to_eeprom;
begin
shift_in(memory_buf);
address = {addr_byte_h[4:0], addr_byte_l};
memory[address] = memory_buf;
State = 2'b00;
end
endtask

task read_from_eeprom;
begin
shift_in(ctrl_byte);
if(ctrl_byte == r7 || ctrl_byte == w6
|| ctrl_byte == r5 || ctrl_byte == r4
|| ctrl_byte == r3 || ctrl_byte == r2
|| ctrl_byte == r1 || ctrl_byte == r0)
begin
address = {addr_byte_h[4:0], addr_byte_l};
sda_buf = memory[address];
shift_out;
State = 2'b00;
end
end
endtask
task shift_in;
output[7:0]shift;
begin
@(posedge scl) shift[7] = sda;
@(posedge scl) shift[6] = sda;
@(posedge scl) shift[5] = sda;
@(posedge scl) shift[4] = sda;
@(posedge scl) shift[3] = sda;
@(posedge scl) shift[2] = sda;
@(posedge scl) shift[1] = sda;
@(posedge scl) shift[0] = sda;
@(negedge scl)
begin
#(`timeslice);
out_flag = 1;
sda_buf = 0;
end
@(negedge scl)
begin
#(`timeslice-250);
out_flag = 0;
end
end
endtask
task shift_out;
begin
out_flag = 1;
for(i=6; i>=0; i=i-1)
begin
@(negedge scl);
#`timeslice;
sda_buf = sda_buf << 1;
end
@(negedge scl) #`timeslice sda_buf[7] = 1;
@(negedge scl) #`timeslice out_flag = 0;
end
endtask
endmodule
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163

e2prom_tb.v

module  e2prom_tb;              

//parameter  define
parameter  T = 20                          ; //时钟周期为20ns
parameter  SLAVE_ADDR     = 7'b1010000     ; //器件地址(SLAVE_ADDR)
parameter  BIT_CTRL       = 1'b1           ; //字地址位控制参数(16b/8b)
parameter  CLK_FREQ       = 26'd50_000_000 ; //i2c_dri模块的驱动时钟频率(CLK_FREQ)
parameter  I2C_FREQ       = 18'd250_000    ; //I2C的SCL时钟频率
parameter  L_TIME         = 17'd1          ; //led闪烁时间参数
parameter  MAX_BYTE       = 16'd3          ; //读写测试的字节个数

//reg define
reg          sys_clk  ;                 //时钟信号
reg          sys_rst_n;                 //复位信号

//wire define
wire         iic_scl;
wire         iic_sda;
wire         led    ;

//*****************************************************
//**                    main code
//*****************************************************

//给输入信号初始值
initial begin
    sys_clk            = 1'b0;
    sys_rst_n          = 1'b0;     //复位
    #(T+1)  sys_rst_n  = 1'b1;     //在第21ns的时候复位信号信号拉高
end

//50Mhz的时钟,周期则为1/50Mhz=20ns,所以每10ns,电平取反一次
always #(T/2) sys_clk = ~sys_clk;

//将SDA数据线上拉
pullup(iic_sda);

IIC_top#(
    .SLAVE_ADDR     ( 7'b1010000 ),
    .BIT_CTRL       ( 1'b1 ),
    .CLK_FREQ       ( 26'd50_000_000 ),
    .I2C_FREQ       ( 18'd250_000 ),
    .L_TIME         ( 17'd125_000 ),
    .MAX_BYTE       ( 16'd256 )
)u_IIC_top(
    .sys_clk        ( sys_clk        ),
    .sys_rst_n      ( sys_rst_n      ),
    .i2c_scl        ( iic_scl        ),
    .i2c_sda        ( iic_sda        ),
    .led            ( led            )
);


//例化e2prom仿真模型
EEPROM_AT24C64 u_EEPROM_AT24C64(
    .scl         (iic_scl),
    .sda         (iic_sda)
    );

endmodule
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60

下面是注意事项

README.md

I2C 转换接口的设计思路

因为时钟的不同 我们先设计出本次时钟所需要的dri_clk
在配置完dri_clk 之后 我们需要做的是对整个I2C结构 进行状态机的 书写
建议 写成经典的三段状态机的形式

  1. 同步时序描述状态转移
  2. 组合逻辑判断状态转移条件
  3. 时序电路描述状态输出

前两部分是相对来说好处理的 后面第三部分的 时序逻辑电路描述状态有些复杂

代码第639行 和之前的存储地址一样又 不太一样

需要先 交还给 FPGA端控制权 再执行
为什么在之前的传递数据在cnt = 0 的时候 交还了控制权 直接赋值呢
原因是因为 它们并不是传递地址 只有首次传递地址的时候 需要保证在SCL在拉高的情况下 SDA先拉底
我们采用倒推法

cnt3501234
SCL1->000->111->00

实验效果 在 35 这一时刻来自于上一时刻
在0时刻 SCL处于中间低处 此时 我们对于 SDA交还控制权给 FPGA 再 给赋值 sda_out = 1
因为我们SCL总是 dri_clk 的四倍 我们在35时刻拉低 就在1时刻拉高
在稳定的 2时刻 拉低 sda_out 即 传输器件地址需要先拉低 sda
最后对于 1拉高 3拉低 4处于低的洼地 -> 可以使得 sda_out 变化

完整介绍一下模块的设计

首先本次实验完成的是 IIC转化的串口形式
在执行处理的时候,把整个元件当成了一个黑盒模块
在这里插入图片描述

就像之前的AXI-Stream转Native接口的RTL模块一样
现在所做的更像是把 Native模块 转化成IIC协议的接口形式
通俗易懂来讲 别的模块我根本不在乎 这个东西 就是将地址,数据等信息传递进入这个转换模块
转换模块通过内部的调度与适配 传输成符合标准与要求的 SCL与SDA的形式
(我个人觉得 IIC要比 AXI-Stream难 10倍 UART 最简单浅显易懂)

这是I2C的读写流程图
在这里插入图片描述

我们本次实验主要在这个图上编写状态机

这里模块内部时钟的编辑
通常使用的时钟频率是50MHz = 26’d50_000_000
这个50MHz 的意思 就是 1s 有 50_000_000个时钟周期 即 每个时钟周期的时长为 20ns

彼时 需要使用一个新的时钟频率 假如是 18’d250_000 的时钟频率 即每秒有250000个周期间隔
我们可以通过number = 50_000_000 / 250_000 求得计数
注意在用cnt 计数时 除以2 再减 1 因为时钟总是在一半的时候进行翻转

完整的画一下 单次读 与 单次写的 时序流程图

在这里插入图片描述

下面是单次写的时序图

在这里插入图片描述

我觉得有一个不妥当的地方是为什么它非要在上升沿采样数据呢 为什么不等高电平 数据稳定的时候采样

第278行 我将 i2c_exec删除了 我觉得没问题

最后上板验证也未出现问题

         // if( i2c_exec == 1) begin
          wr_flag     <=    i2c_rh_wl  ;
          addr_save   <=    i2c_addr   ;
          data_w_save <=    i2c_data_w ;
          i2c_ack     <=    0 ;
         //  end
         ```

# 第734行 它的做法总是在上升沿触发 我吐槽过了 我觉得电平触发更加稳定 
它的做法
```verilog
        case(cnt)
            7'd0:
              sda_dir <= 1'b0;
            7'd1:
            begin
              data_r_save[7] <= sda_in;
              scl       <= 1'b1;
            end
            7'd3:
              scl  <= 1'b0;
            7'd5:
            begin
              data_r_save[6] <= sda_in ;
              scl       <= 1'b1   ;
            end
            7'd7:
              scl  <= 1'b0;
            7'd9:
            begin
              data_r_save[5] <= sda_in;
              scl       <= 1'b1  ;
            end
            7'd11:
              scl  <= 1'b0;
            7'd13:
            begin
              data_r_save[4] <= sda_in;
              scl       <= 1'b1  ;
            end
            7'd15:
              scl  <= 1'b0;
            7'd17:
            begin
              data_r_save[3] <= sda_in;
              scl       <= 1'b1  ;
            end
            7'd19:
              scl  <= 1'b0;
            7'd21:
            begin
              data_r_save[2] <= sda_in;
              scl       <= 1'b1  ;
            end
            7'd23:
              scl  <= 1'b0;
            7'd25:
            begin
              data_r_save[1] <= sda_in;
              scl       <= 1'b1  ;
            end
            7'd27:
              scl  <= 1'b0;
            7'd29:
            begin
              data_r_save[0] <= sda_in;
              scl       <= 1'b1  ;
            end
            ```
上面是别人的对的 
我自己改成高电平触发 也是正确的 
```verilog
case(cnt)
            7'd0:
              sda_dir <= 1'b0;
            7'd1:
              scl       <= 1'b1;
            7'd2 :
              data_r_save[7] <= sda_in;


            7'd3:
              scl  <= 1'b0;
            7'd5:
              scl       <= 1'b1   ;
            7'd6:
              data_r_save[6] <= sda_in ;


            7'd7:
              scl  <= 1'b0;
            7'd9:
              scl       <= 1'b1  ;
            7'd10 :
              data_r_save[5] <= sda_in;


            7'd11:
              scl  <= 1'b0;
            7'd13:
              scl       <= 1'b1  ;
            7'd14:
              data_r_save[4] <= sda_in;


            7'd15:
              scl  <= 1'b0;
            7'd17:
              scl       <= 1'b1  ;
            7'd18:
              data_r_save[3] <= sda_in;


            7'd19:
              scl  <= 1'b0;
            7'd21:
              scl       <= 1'b1  ;
            7'd22:
              data_r_save[2] <= sda_in;


            7'd23:
              scl  <= 1'b0;
            7'd25:
              scl       <= 1'b1  ;
            7'd26:
              data_r_save[1] <= sda_in;


            7'd27:
              scl  <= 1'b0;
            7'd29:
              scl       <= 1'b1  ;
            7'd30:
              data_r_save[0] <= sda_in;




            7'd31:
              scl  <= 1'b0;
            7'd32:
            begin
              sda_dir <= 1'b1;
              sda_out <= 1'b1;
            end
            7'd33:
              scl     <= 1'b1;
            7'd34:
              st_done <= 1'b1;          //非应答
            7'd35:
            begin
              scl <= 1'b0;
              cnt <= 7'b0;
              i2c_data_r <= data_r_save;
            end
            default  :
              ;
          endcase
        end
        ```

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/665386
推荐阅读
相关标签
  

闽ICP备14008679号