i2c协议简介,用FPGA实现I2c总线协议,i2c读写EEPROM工程示例。

本实验采用Xilinx artix 7芯片,型号为xc7a35t_2fgg484。

任务目标:设计一个可进行读写操作的 I2C 控制器,实现 FPGA对 EEPROM 存储器的数据写入和数据读取操作。

1 、I2c简介

I2C(Inter-Integrated Circuit)总线协议是一种用于在集成电路(IC)之间进行通信的串行通信协议。它由飞利浦(Philips)公司于上世纪80年代开发,并成为一种广泛应用于各种电子设备中的通信协议。

1.1 基本原理

I2C总线协议使用两根线(SDA和SCL)进行通信。SDA(Serial Data Line)是数据线,用于传输数据,而SCL(Serial Clock Line)是时钟线,用于同步数据传输。这种双线制的结构使得多个IC可以在同一总线上进行通信。

1、主从结构:I2C总线协议采用主从结构。主设备(Master)负责发起通信并控制总线上的时序,而从设备(Slave)则被动地响应主设备的请求。主设备可以与多个从设备进行通信,每个从设备都有一个唯一的地址。

2、数据传输:数据传输通过起始位(2)、地址/数据(3)和停止位(4)来完成。空闲时(1),主设备发起通信时,发送一个起始位(Start)信号,然后发送从设备的地址和读/写指令位。接下来,主设备发送读/写地址并等待从设备响应,接着发送或接收数据,并等待从设备的确认信号。通信结束时,主设备发送停止位(Stop)信号,如图1所示。具体来说,它有4种传输模式:单字节写、多字节写、单字节读、多字节读。值得注意的是,每种读写模式里面须根据从设备存储空间大小来确定WORD ADDRESS的长度,对于一些大容量设备,这里的长度可以为双字节(16bit),每字节地址都需要一个ACK信号。地址和数据都是高位在前、低位在后。

图1  i2c总线协议
图2 单字节写 时序图
图3 多字节写(顺序写) 时序图(两字节地址)

 注意,读操作与写操作的不同点在于,主机写完存储地址之后,再次给个start信号,其后再给一次设备地址+读指令位,响应后开始接收读数据。

图4 单字节读 时序图
图5 多字节读(顺序读)时序图

 3、速度和可靠性:I2C总线协议支持多种传输速率,通常有标准模式(100 kbps)和快速模式(400 kbps)。此外,还有高速模式(3.4 Mbps)和超高速模式(5 Mbps)等。总线上的设备可以根据自身的能力选择适当的速率。

1.2 常用场景

I2C总线协议在许多电子设备中得到广泛应用。一些常见的场景包括:

  • 传感器和微控制器之间的通信:I2C总线协议可用于传感器与微控制器之间的数据传输,例如温度传感器、湿度传感器等。
  • 存储器和微控制器之间的通信:I2C总线协议可用于存储器(如EEPROM)与微控制器之间的数据读写操作。
  • 外围设备控制:I2C总线协议可用于控制外围设备,如LCD显示屏、触摸屏、电子芯片等。
  • 数字信号处理器(DSP)和外设之间的通信:I2C总线协议可用于DSP与外设(如音频编解码器、ADC、DAC等)之间的数据传输。

2、 工程示例

2.1 模块设计

工程主要由3个子模块构成,包括按键消抖模块(通过按键控制读/写)、读写数据模块、i2c驱动模块。整个工程的模块划分如下图所示:

图6 模块划分

按下数据写操作按键,写触发信号传入按键消抖模块(key_filter),经消抖处理后的写触发信号传入数据收发模块(i2c_rw_data),模块接收到有效的写触发信号后,生成写使能信号、待写入数据、数据地址传入 I2C 驱动模块(i2c_ctrl), I2C 驱动模块按照 I2C 协议将数据写入 EEPROM 存储芯片;
数据写入完成后,按下数据读操作按键,读触发信号传入按键消抖模块(key_filter),经消抖处理后的读触发信号传入数据收发模块(i2c_rw_data),模块接收到有效的读触发信号后,生成读使能信号、数据地址传入 I2C 驱动模块(i2c_ctrl), I2C 驱动模块自 EEPROM存储芯片读取数据,将读取到的数据回传给数据收发模块(i2c_rw_data),数据收发模块将数据暂存,待所有数据均读取完成后,将数据输出。


2.2 i2c驱动模块

I2C 驱动模块的主要功能是按照 I2C 协议对 EERPROM 存储芯片执行数据读写操作。

图7 状态转移图
module i2c_ctrl #(
  parameter sys_clk_freq = 50_000_000,
            i2c_clk_freq = 1_000_000 ,
            i2c_scl_freq = 250_000,
  parameter device_addr_w = 8'b1010_0110, 
            device_addr_r = 8'b1010_0111             
)
(
input wire             sys_clk         ,
input wire             sys_rst_n       ,
input wire             i2c_start       ,
input wire             wr_en           ,
input wire             rd_en           ,
input wire [15:0]      address         ,
input wire [7:0]       wr_data         ,
input wire             addr_num        ,

inout  wire            i2c_sda         ,
    
output reg            i2c_scl          ,
output reg            i2c_clk          ,
output reg [7:0]       rd_data         ,
output reg             i2c_end         
    );


localparam IDLE             = 5'd1 , 
           START            = 5'd2 , 
           SEND_DEV_ADDR    = 5'd3 ,
           ACK_1            = 5'd4 , 
           SEND_H           = 5'd5 , 
           ACK_2            = 5'd6 ,
           SEND_B_L         = 5'd7 ,
           ACK_3            = 5'd8 , 
           WR_DAT           = 5'd9 ,
           ACK_4            = 5'd10, 
           STOP             = 5'd11;
localparam START_2          = 5'd12, 
           SEND_RD_A        = 5'd13, 
           ACK_5            = 5'd14, 
           RD_DAT           = 5'd15,
           NO_ACK           = 5'd16;
localparam cnt_sys_clk_max  = 5'd24;

reg [4:0]  cnt_sys_clk        ;
reg        cnt_i2c_clk_en     ;
reg [1:0]  cnt_i2c_clk        ;
(*mark_debug="true"*)reg [4:0]  state              ;
(*mark_debug="true"*)reg [2:0]  cnt_bit            ;
reg       ack                ;
reg        i2c_sda_out        ;
(*mark_debug="true"*)wire       i2c_sda_in         ;      
(*mark_debug="true"*)wire       sda_en             ;
reg [7:0]  rd_data_reg        ;

wire                ack_flag;

always @(posedge sys_clk or negedge sys_rst_n)  //生成i2c_clk时钟
    if (sys_rst_n == 1'b0) begin
        cnt_sys_clk <= 5'b0;
        i2c_clk <= 1'b0;
    end
    else if (cnt_sys_clk == cnt_sys_clk_max) begin
        cnt_sys_clk <= 5'd0;
        i2c_clk <= ~i2c_clk ;
    end
    else
        cnt_sys_clk <= cnt_sys_clk + 5'd1;

always @(posedge i2c_clk or negedge sys_rst_n) //i2c_clk时钟计数起始信号
    if (sys_rst_n == 1'b0)
       cnt_i2c_clk_en <= 1'b0;
    else if (state == STOP && cnt_bit == 3'd3 && cnt_i2c_clk == 2'd3)
       cnt_i2c_clk_en <= 1'b0;   
    else if (i2c_start == 1'b1)
       cnt_i2c_clk_en <= 1'b1;
    else
       cnt_i2c_clk_en <= cnt_i2c_clk_en;

always @(posedge i2c_clk or negedge sys_rst_n) //i2c_clk计数生成cnt_bit控制信号
    if (sys_rst_n == 1'b0)
        cnt_i2c_clk <= 2'd0;
    else if (cnt_i2c_clk_en == 1'b1) begin
          if (cnt_i2c_clk == 2'd3)
             cnt_i2c_clk <= 2'd0;
          else 
             cnt_i2c_clk <= cnt_i2c_clk + 2'd1;
    end
    else 
        cnt_i2c_clk <= 2'd0;

always@(*)  //i2c_scl ?
    case (state)
    IDLE:
    i2c_scl <= 1'b1;
    START:if(cnt_i2c_clk == 2'd3)
             i2c_scl <= 1'b0;
            else
             i2c_scl <= 1'b1;
    SEND_DEV_ADDR,ACK_1,SEND_H,ACK_2,SEND_B_L,
    ACK_3,WR_DAT,ACK_4,START_2,SEND_RD_A,ACK_5,RD_DAT,NO_ACK:
            if((cnt_i2c_clk == 2'd1) || (cnt_i2c_clk == 2'd2))
             i2c_scl <= 1'b1;
            else
             i2c_scl <= 1'b0;
    STOP:
            if((cnt_bit == 3'd0) &&(cnt_i2c_clk == 2'd0))
             i2c_scl <= 1'b0;
            else
             i2c_scl <= 1'b1;
    default: i2c_scl <= 1'b1;
    endcase

always @(posedge i2c_clk or negedge sys_rst_n) //收发字节的计 ?
    if (sys_rst_n == 1'b0)
      cnt_bit <= 3'b0;
    else if ((cnt_bit == 3'd7&&cnt_i2c_clk == 2'd3) || (state == ACK_1 || state == ACK_2 || state == ACK_3
             || state == ACK_4 ||state == ACK_5||state == IDLE||state == NO_ACK||state == START_2||state == START)
             ||(state == STOP && cnt_bit == 3'd3&&cnt_i2c_clk == 2'd3))
      cnt_bit <= 3'b0;
    else if ((cnt_i2c_clk == 2'd3) && (state != IDLE))
      cnt_bit <= cnt_bit + 1'd1;  
    else 
      cnt_bit <= cnt_bit;

always @(posedge i2c_clk or negedge sys_rst_n) //状 ? 机的跳 ?
    if(sys_rst_n == 1'b0)
      state <= IDLE;
    else case(state)
         IDLE:          
                        if (i2c_start == 1'b1)
                         state <= START;
                        else
                         state <= IDLE;
         START:         if (cnt_i2c_clk == 2'd3)
                         state <= SEND_DEV_ADDR;
                        else
                         state <= START;
         SEND_DEV_ADDR: if(cnt_bit == 3'd7 && cnt_i2c_clk==2'd3)
                         state <= ACK_1;
                        else
                         ;
         ACK_1:         if(ack == 1'b0 && cnt_i2c_clk==2'd3)begin
                             if(addr_num == 1'b0)
                              state <= SEND_B_L;
                             else
                              state <= SEND_H;
                        end
                        else
                        ;
         SEND_H:      if(cnt_bit == 3'd7 && cnt_i2c_clk==2'd3)
                         state <= ACK_2;
                        else
                        ;
         ACK_2:         if(ack == 1'b0 && cnt_i2c_clk==2'd3)
                         state <= SEND_B_L;
                        else
                        ;
         SEND_B_L:      if(cnt_bit == 3'd7 && cnt_i2c_clk==2'd3)
                         state <= ACK_3;
                        else
                        ;
         ACK_3:         if(ack == 1'b0 && cnt_i2c_clk==2'd3)begin
                           if(rd_en == 1'b1)
                            state <= START_2;
                           else if(wr_en == 1'b1)
                            state <= WR_DAT;
                           else
                           ;
                           end
                        else
                         ;
         WR_DAT:        if(cnt_bit == 3'd7 && cnt_i2c_clk==2'd3)
                         state <= ACK_4;
                        else
                        ;
         ACK_4:         if(ack == 1'b0 && cnt_i2c_clk==2'd3)
                         state <= STOP;
                        else
                        ;
         STOP:          if(cnt_bit == 3'd3 && cnt_i2c_clk == 2'd3)
                         state <= IDLE;
                        else
                        ;
         START_2:       if(cnt_i2c_clk == 2'd3)
                         state <= SEND_RD_A;
                        else
                        ;   
         SEND_RD_A:     if(cnt_bit == 3'd7 && cnt_i2c_clk == 2'd3)
                         state <= ACK_5;
                        else
                        ;
         ACK_5:         if(ack == 1'b0 && cnt_i2c_clk == 2'd3)
                         state <= RD_DAT;
                        else
                        ;
         RD_DAT:        if(cnt_bit == 3'd7 && cnt_i2c_clk == 2'd3)
                         state <= NO_ACK;
                        else
                        ;
         NO_ACK:        if(i2c_sda_out == 1'b1 && cnt_i2c_clk == 2'd3)
                         state <= STOP;
                        else
                        ;
         default:      state <= IDLE;
    endcase

always @(posedge i2c_clk or negedge sys_rst_n) //i2c的结束信 ?
       if(sys_rst_n == 1'b0)
         i2c_end <= 1'b0;
       else if(state == STOP && cnt_bit == 3'd3 && cnt_i2c_clk ==2'd3)
         i2c_end <= 1'b1;
       else
         i2c_end <= 1'b0;

/*always @(posedge i2c_clk or negedge sys_rst_n) //使用时序逻辑给sda线输出时的数据赋 ?
   if(sys_rst_n == 1'b0)
      i2c_sda_out = 1'b1;
   else if(state == START)
      i2c_sda_out = 1'b0;
   else if(state == START && cnt_i2c_clk == 2'd3) 
      i2c_sda_out <= device_addr_w[7];
   else if(state == SEND_DEV_ADDR) begin
    if (cnt_i2c_clk == 2'd3) begin
      case(cnt_bit)
        0: i2c_sda_out <= device_addr_w[6];
        1: i2c_sda_out <= device_addr_w[5];
        2: i2c_sda_out <= device_addr_w[4];
        3: i2c_sda_out <= device_addr_w[3];
        4: i2c_sda_out <= device_addr_w[2];
        5: i2c_sda_out <= device_addr_w[1];
        6: i2c_sda_out <= device_addr_w[0];
        default: i2c_sda_out <= 1'b1;        
      endcase
    end
    else
      ;
    end
   else if(state == ACK_1 && cnt_i2c_clk == 2'd3)
       i2c_sda_out <= address[15];
   else if(state == SEND_H) begin
    if (cnt_i2c_clk == 2'd3) begin
      case(cnt_bit)
        0: i2c_sda_out <= address[14];
        1: i2c_sda_out <= address[13];
        2: i2c_sda_out <= address[12];
        3: i2c_sda_out <= address[11];
        4: i2c_sda_out <= address[10];
        5: i2c_sda_out <= address[9];
        6: i2c_sda_out <= address[8];
        default: i2c_sda_out <= 1'b1;        
      endcase
    end
    else
      ;
    end
   else if(state == ACK_2 && cnt_i2c_clk == 2'd3)
       i2c_sda_out <= address[7];
   else if(state == SEND_B_L) begin
    if (cnt_i2c_clk == 2'd3) begin
      case(cnt_bit)
        0: i2c_sda_out <= address[6];
        1: i2c_sda_out <= address[5];
        2: i2c_sda_out <= address[4];
        3: i2c_sda_out <= address[3];
        4: i2c_sda_out <= address[2];
        5: i2c_sda_out <= address[1];
        6: i2c_sda_out <= address[0];
        default: i2c_sda_out <= 1'b1 ;  
      endcase
    end
    else
      ;
    end
   else if(state == ACK_3 && cnt_i2c_clk == 2'd3)
       i2c_sda_out <= wr_data[7];
   else if(state == WR_DAT) begin
    if (cnt_i2c_clk == 2'd3) begin
      case(cnt_bit)
        0: i2c_sda_out <= wr_data[6];
        1: i2c_sda_out <= wr_data[5];
        2: i2c_sda_out <= wr_data[4];
        3: i2c_sda_out <= wr_data[3];
        4: i2c_sda_out <= wr_data[2];
        5: i2c_sda_out <= wr_data[1];
        6: i2c_sda_out <= wr_data[0];
        default: i2c_sda_out <= 1'b1 ;  
      endcase
    end
    else
      ;
    end
   else if(state == ACK_4 && cnt_i2c_clk == 2'd3)
       i2c_sda_out <= 1'b0;
   else if(state == START_2 && cnt_i2c_clk == 2'd1) //随机写操作的 ? ?
       i2c_sda_out <= 1'b0;
   else if(state == START_2 && cnt_i2c_clk == 2'd3)
       i2c_sda_out <= device_addr_r[7];
   else if(state == SEND_RD_A) begin
    if (cnt_i2c_clk == 2'd3) begin
      case(cnt_bit)
        0: i2c_sda_out <= device_addr_r[6];
        1: i2c_sda_out <= device_addr_r[5];
        2: i2c_sda_out <= device_addr_r[4];
        3: i2c_sda_out <= device_addr_r[3];
        4: i2c_sda_out <= device_addr_r[2];
        5: i2c_sda_out <= device_addr_r[1];
        6: i2c_sda_out <= device_addr_r[0];
        default: i2c_sda_out <= 1'b1 ;  
      endcase
    end
    else
      ;
    end
   else if(state == NO_ACK && cnt_i2c_clk == 2'd3)
       i2c_sda_out <= 1'b0;
   else if(state == STOP && cnt_i2c_clk == 2'd2)
       i2c_sda_out <= 1'b1;    
   else if(state == IDLE)
       i2c_sda_out <= 1'b1;
   else
      i2c_sda_out <= i2c_sda_out;
always @(posedge i2c_scl or negedge sys_rst_n) //sda作为输入时,接收到的数据串转 ?
  if(sys_rst_n == 1'b0)
    rd_data <= 8'b0;
  else if(state == RD_DAT && cnt_i2c_clk == 2'd2) begin
    rd_data= {rd_data[6:0],i2c_sda_in};
  end
  else if(state == IDLE)
    rd_data <= 8'b0;
  else
    ;
*/

always @(*)
  case(state)
  IDLE,START,SEND_DEV_ADDR,SEND_H,SEND_B_L,WR_DAT,STOP,START_2,SEND_RD_A,RD_DAT,NO_ACK:
          ack <= 1'b1;
  ACK_1,ACK_2,ACK_3,ACK_4,ACK_5:
       if(cnt_i2c_clk == 2'd0)
          ack <= i2c_sda;
      else
          ack <= ack;
  default : ack <= 1'b1;
  endcase 

always @(*)  //使用组合逻辑对sda线的输出和读取寄存器赋 ??,更方 ?
  case(state)
   IDLE: 
      begin
        i2c_sda_out <= 1'b1;
        rd_data_reg <= 8'd0;
      end
   START:
        i2c_sda_out <= 1'b0;
    SEND_DEV_ADDR:
      if(cnt_bit <= 3'd7)
        i2c_sda_out <= device_addr_w[7 - cnt_bit];
      else
        ;
    ACK_1:
      i2c_sda_out <= 1'b1;
    SEND_H:
      i2c_sda_out <= address[15-cnt_bit];
    ACK_2:
      i2c_sda_out <= 1'b1;
    SEND_B_L:
      i2c_sda_out <= address[7-cnt_bit];
    ACK_3:
      i2c_sda_out <= 1'b1;
    WR_DAT:
      i2c_sda_out <= wr_data[7-cnt_bit];
    ACK_4:
      i2c_sda_out <= 1'b1;
    STOP:
      if(cnt_bit == 3'd0 && cnt_i2c_clk < 2'd3) 
        i2c_sda_out <= 1'b0;
       else
        i2c_sda_out <= 1'b1;
      
    START_2:
      if(cnt_i2c_clk == 2'd0)
       i2c_sda_out <= 1'b1;
      else if(cnt_i2c_clk == 2'd2)
       i2c_sda_out <= 1'b0;
      else
      ;
    SEND_RD_A:
      i2c_sda_out <= device_addr_r[7-cnt_bit];
    ACK_5:
      i2c_sda_out <= 1'b1;
    RD_DAT:
      if(cnt_i2c_clk == 2'd2)
       rd_data_reg[7-cnt_bit] <= i2c_sda_in;
      else
       rd_data_reg <= rd_data_reg; 
    default: begin
      i2c_sda_out <= 1'b1;
      rd_data_reg  <= rd_data_reg;
    end
   endcase
always @(posedge i2c_clk or negedge sys_rst_n) //读数据输出赋 ?
  if(sys_rst_n == 1'b0)
   rd_data <= 8'd0;
  else if(state == RD_DAT && cnt_bit == 3'd7 && cnt_i2c_clk == 2'd3)
   rd_data <= rd_data_reg;  //在读数据寄存器收集数据完成后赋 ??
  else
  ;

//assign ack     = ((state == ACK_1) || (state == ACK_2) ||(state == ACK_3) || (state == ACK_4) || (state == ACK_5)) ?  i2c_sda_in: 1'b1;
assign i2c_sda_in = i2c_sda; //sda作为输入时,接收到的数据  
assign sda_en  = ( (state == ACK_1) || (state == ACK_2) ||(state == ACK_3) || (state == ACK_4) || (state == ACK_5)|| (state == RD_DAT)) ? 1'b0 : 1'b1;
assign i2c_sda = (sda_en == 1'b1) ? i2c_sda_out : 1'bz;   //数据使能时作为输出,否则输出为高阻 ??


//IOBUF #(
//      .DRIVE(12), // Specify the output drive strength
//      .IBUF_LOW_PWR("FALSE"),  // Low Power - "TRUE", High Performance = "FALSE" 
//      .IOSTANDARD("DEFAULT"), // Specify the I/O standard
//      .SLEW("SLOW") // Specify the output slew rate
//   ) IOBUF_i2c(
//      .O(i2c_sda_in),     // Buffer output
//      .IO(i2c_sda),   // Buffer inout port (connect directly to top-level port)
//      .I(i2c_sda_out),     // Buffer input
//      .T(~sda_en)      // 3-state enable input, high=input, low=output
//   ); 

endmodule

2.3 数据收发模块

数据收发模块的主要功能是:为 I2C 驱动模块提供读/写数据存储地址、待写入数据以及作为 EEPROM 读出数据缓存。

module i2c_rw_data(
input wire        sys_clk      ,
input wire        sys_rst_n    ,
input wire        write        ,
input wire        read         ,
input wire        i2c_clk      ,
input wire [7:0]  rd_data      ,
input wire        i2c_end      ,

output reg        i2c_start    ,
output reg        wr_en        ,
output reg        rd_en        ,
output reg [15:0] byte_addr    ,
output reg [7:0]  wr_data      ,
output wire[7:0]  fifo_rd_data         
    );
parameter cnt_sys_clk_m = 8'd200, cnt_wr_m = 4'd10,
          cnt_start_m   = 32'd4999,cnt_fifo_wait_m=28'd499999;


reg [7:0]       cnt_wr_valid;
reg [7:0]       cnt_rd_valid;
reg             wr_valid    ;
reg             rd_valid    ;

reg [31:0]      cnt_i2c_start;   //每次“开始 ? 信号的间隔时间
reg [3:0]       cnt_wr       ;   //发 ? 数据个数计 ?
reg [3:0]       cnt_rd       ;   //读取数据个数计数


reg             fifo_rd_valid;   //fifo读取有效信号
reg [27:0]      cnt_fifo_wait;   //每次从FIFO读出数据的时间间隔,方便观察
reg             fifo_rd_en   ;   //从fifo读数据的使能信号
reg [3:0]       rd_fifo_num  ;   //从fifo读出数据的个 ?
wire [7:0]       fifo_num     ;   //fifo中的数据个数
wire             full         ;
wire             empty        ;

always @(posedge sys_clk or negedge sys_rst_n) //信号从高频到低频跨时钟域,延 ?
  if(sys_rst_n == 1'b0) begin
    cnt_wr_valid <= 8'd0;
    wr_valid <= 1'b0;
    cnt_rd_valid <= 8'd0;
    rd_valid <= 1'b0;
  end
  else if (cnt_wr_valid == cnt_sys_clk_m) begin
    cnt_wr_valid <= 8'd0;
    wr_valid <= 1'b0;
  end
  else if (write == 1'b1)
    wr_valid <= 1'b1;
  else if (wr_valid == 1'b1)
    cnt_wr_valid <= cnt_wr_valid + 8'd1;
  else if (cnt_rd_valid == cnt_sys_clk_m)begin
    cnt_rd_valid <= 8'd0;
    rd_valid <= 1'b0;
  end
  else if(read == 1'b1)
    rd_valid <= 1'b1;
  else if(rd_valid == 1'b1)
    cnt_rd_valid <= cnt_rd_valid + 8'd1;
  else begin
    wr_valid <= wr_valid;
    rd_valid <= rd_valid;
  end

always @(posedge i2c_clk or negedge sys_rst_n)  //i2c的读写使能信 ?
  if(sys_rst_n == 1'b0) begin
    wr_en <= 1'b0;
    rd_en <= 1'b0;
  end
  else if(wr_valid == 1'b1)
    wr_en <= 1'b1;
  else if (rd_valid == 1'b1)
    rd_en <= 1'b1;
  else if (cnt_wr == cnt_wr_m - 1 && i2c_end == 1'b1)
    wr_en <= 1'b0;
  else if (cnt_rd == cnt_wr_m - 1 && i2c_end == 1'b1)
    rd_en <= 1'b0;
  else
  ;

always @(posedge i2c_clk or negedge sys_rst_n)   //i2c的开始信 ?
  if(sys_rst_n == 1'b0) 
   cnt_i2c_start <= 32'd0;
   else if(cnt_i2c_start == cnt_start_m)
   cnt_i2c_start <= 32'd0;
  else if(wr_en == 1'b0 && rd_en == 1'b0)
   cnt_i2c_start <= 32'd0;
  else if(wr_en == 1'b1 || rd_en == 1'b1)
   cnt_i2c_start <= cnt_i2c_start + 32'd1;
  else
   ;

always @(posedge i2c_clk or negedge sys_rst_n)   //i2c的开始信 ?
  if(sys_rst_n == 1'b0)
   i2c_start <= 1'b0;
  else if(cnt_i2c_start == cnt_start_m)
   i2c_start <= 1'b1;
  else
   i2c_start <= 1'b0;

always @(posedge i2c_clk or negedge sys_rst_n) //i2c写 ? 读的次数计 ?
  if(sys_rst_n == 1'b0) 
   cnt_wr <= 4'd0;
  else if(wr_en == 1'b0)
   cnt_wr <= 4'd0;
  else if((i2c_end == 1'b1) && (wr_en == 1'b1))
   cnt_wr <= cnt_wr + 4'd1;
  else
   ;

always @(posedge i2c_clk or negedge sys_rst_n) //i2c写 ? 读的次数计 ?
  if(sys_rst_n == 1'b0) 
   cnt_rd <= 4'd0;
  else if(rd_en == 1'b0)
   cnt_rd <= 4'd0; 
  else if((i2c_end == 1'b1) && (rd_en == 1'b1))
   cnt_rd <= cnt_rd + 4'd1;
  else
   ;

always @(posedge i2c_clk or negedge sys_rst_n) //fifo读有效信 ?
  if(sys_rst_n == 1'b0)
    fifo_rd_valid <= 1'b0;
  else if (cnt_rd == 4'd10)
    fifo_rd_valid <= 1'b1;
  else if(rd_fifo_num == 8'd10 && cnt_fifo_wait == cnt_fifo_wait_m)
    fifo_rd_valid <= 1'b0;
  else 
  ;
always @(posedge i2c_clk or negedge sys_rst_n) //每次从fifo读数据的等待时间
  if(sys_rst_n == 1'b0)
    cnt_fifo_wait <= 28'b0;
  else if(cnt_fifo_wait == cnt_fifo_wait_m)
    cnt_fifo_wait <= 28'd0;  
  else if(fifo_rd_valid == 1'b1)
    cnt_fifo_wait <= cnt_fifo_wait + 28'd1;
  else if(fifo_rd_valid == 1'b0)
    cnt_fifo_wait <= 28'd0;
  else
    ;
always @(posedge i2c_clk or negedge sys_rst_n) //fifo读使能信 ?
   if(sys_rst_n == 1'b0)
     fifo_rd_en <= 1'b0;
   else if(fifo_rd_valid == 1'b1 && (cnt_fifo_wait==cnt_fifo_wait_m))
     fifo_rd_en <= 1'b1;
   else
     fifo_rd_en <= 1'b0;
always @(posedge i2c_clk or negedge sys_rst_n)  //从FIFO中读取的次数
   if(sys_rst_n == 1'b0)
     rd_fifo_num <= 4'd0;
   else if(fifo_rd_en == 1'b1)
     rd_fifo_num <= rd_fifo_num + 4'd1;
   else if(fifo_rd_valid == 1'b0)
     rd_fifo_num <= 4'd0;
   else
     ;

always @(posedge i2c_clk or negedge sys_rst_n)  //i2c写入的eeprom存储地址和数 ?
 if(sys_rst_n == 1'b0 ) begin
  byte_addr <= 16'h005a;
  wr_data   <= 8'h01;
 end
  else if(((cnt_wr == 4'd9)||(cnt_rd == 4'd9)) && i2c_end == 1'b1)begin
  byte_addr <= 16'h005a;
  wr_data   <= 8'h01;
 end
 else if((rd_en == 1'b1) && (i2c_end == 1'b1)&&(wr_en == 1'b0)) 
  byte_addr <= byte_addr + 16'h1;
 else if((wr_en == 1'b1) && (i2c_end == 1'b1))  begin
  wr_data   <= wr_data + 8'h1;
  byte_addr <= byte_addr + 16'h1;
  end
 else 
  ;

fifo_i2c_read fifo_i2c_read_i2c(
  .clk(i2c_clk               ),                // input wire clk
  .din(rd_data               ),                // input wire [7 : 0] din
  .wr_en(rd_en && i2c_end    ),            // input wire wr_en
  .rd_en(fifo_rd_en          ),            // input wire rd_en
  .dout(fifo_rd_data         ),              // output wire [7 : 0] dout
  .full(full                 ),              // output wire full
  .empty(empty               ),            // output wire empty
  .data_count(fifo_num       )  // output wire [7 : 0] data_count
);


endmodule

2.4 按键消抖模块

module key_filter
 #(
 parameter cnt_max=20'd999_999
)
(
input clk,
input rst_n,
input key_n,

output key_flag
    );
    reg [19:0] cnt_20ms;
    reg key_flag1;
always @(posedge clk or negedge rst_n)
    if(rst_n == 1'b0)
      cnt_20ms <= 20'd0;
    else if(cnt_20ms == cnt_max)
      cnt_20ms <= cnt_max;
    else if(key_n == 1'b1)
      cnt_20ms <= 20'd0;
    else
      cnt_20ms <= cnt_20ms+1'd1;
      
always @(posedge clk or negedge rst_n)
    if(rst_n == 1'b0)
      key_flag1 <= 1'b0;
    else if (cnt_20ms == cnt_max - 20'd1)
      key_flag1 <= 1'b1;
    else 
      key_flag1 <= 1'b0;
      
assign key_flag = key_flag1;
endmodule

2.5 顶层模块

module i2c_eeprom(
input             sys_clk     ,
input             sys_rst_n   ,
input      [1:0]  key         ,     

output reg     [2:0]  led         ,
output            i2c_scl     ,
output            i2c_sda                 
    );
wire          write;
wire          read ;

wire          i2c_clk;
wire          i2c_end;
wire [7:0]    i2c_rd_data;
wire          i2c_start   ;
wire          wr_en       ;
wire          rd_en       ;
wire [15:0]   byte_addr   ;
wire [7:0]    wr_data     ;
wire [7:0]    fifo_rd_data;

always @(*)
   if(sys_rst_n == 1'b0)
      led   <= 3'b000;
   else if(wr_data == 8'h0a)
      led <= 3'b001;
   else if (i2c_rd_data == 8'h0a)
      led <= 3'b011;
   else if (fifo_rd_data == 8'h0a)
      led <= 3'b111;
   else
   ;
   
key_filter
 #(
 .cnt_max (20'd999_999)
)key_filter_a
(
.clk          (  sys_clk          ),
.rst_n        (  sys_rst_n        ),
.key_n        (  key[0]           ),

.key_flag     (  write            )
    );
    
key_filter
 #(
 .cnt_max (20'd999_999)
)key_filter_b
(
.clk          (  sys_clk          ),
.rst_n        (  sys_rst_n        ),
.key_n        (  key[1]           ),

.key_flag     (  read             )
    );  

i2c_rw_data i2c_rw_data_inst(
.sys_clk           (  sys_clk           ),
.sys_rst_n         (  sys_rst_n         ),
.write             (  write             ),
.read              (  read              ),
.i2c_clk           (  i2c_clk           ),
.rd_data           (  i2c_rd_data       ),
.i2c_end           (  i2c_end           ),

.i2c_start         (  i2c_start         ),
.wr_en             (  wr_en             ),
.rd_en             (  rd_en             ),
.byte_addr         (  byte_addr         ),
.wr_data           (  wr_data           ),
.fifo_rd_data      (  fifo_rd_data      )        
    );
        
 i2c_ctrl #(
.sys_clk_freq (50_000_000),
.i2c_clk_freq (1_000_000 ),
.i2c_scl_freq (250_000   ),
.device_addr_w (8'b1010_0110), 
.device_addr_r (8'b1010_0111) 
)i2c_ctrl_top
(
.sys_clk       (  sys_clk          ),
.sys_rst_n     (  sys_rst_n        ),
.i2c_start     (  i2c_start        ),
.wr_en         (  wr_en            ),
.rd_en         (  rd_en            ),
.address       (  byte_addr        ),
.wr_data       (  wr_data          ),
.addr_num      (  1'b1             ), //    i2c  ַ ֽ   

.i2c_sda       (  i2c_sda          ),

.i2c_scl       (  i2c_scl          ),
.i2c_clk       (  i2c_clk          ),
.rd_data       (  i2c_rd_data      ),
.i2c_end       (  i2c_end          )
    );
    
ila_i2c_eeprom ila_i2c_eeprom_inst(
	.clk(sys_clk), // input wire clk


	.probe0(sys_rst_n), // input wire [0:0]  probe0  
	.probe1(key), // input wire [1:0]  probe1 
	.probe2(i2c_clk), // input wire [0:0]  probe2 
	.probe3(i2c_rd_data), // input wire [7:0]  probe3 
	.probe4(wr_data), // input wire [7:0]  probe4 
	.probe5(i2c_end), // input wire [0:0]  probe5 
	.probe6(i2c_scl), // input wire [0:0]  probe6 
	.probe7(i2c_sda), // input wire [0:0]  probe7 
	.probe8(rd_en) // input wire [0:0]  probe8
); 
 
endmodule

3、 testbench仿真文件

module i2c_eeprom_tb(   );

reg          sys_clk       ;
reg          sys_rst_n     ;
reg [1:0]   key            ;

wire [2:0]  led             ;
wire         i2c_scl       ;
wire         i2c_sda       ;

initial
  begin
  sys_clk <= 1'b1;
  sys_rst_n <= 1'b0;
  key <= 2'b11;
  #200
  sys_rst_n <= 1'b1;
  #2000
  key <= 2'b10;
  #400
  key <= 2'b11;
  #51_000_000
  key <= 2'b01;
  #400
  key <= 2'b11;
  end



always #10  sys_clk = ~sys_clk;

defparam i2c_eeprom_testbench.key_filter_a.cnt_max = 5;
defparam i2c_eeprom_testbench.key_filter_b.cnt_max = 5;
defparam i2c_eeprom_testbench.i2c_rw_data_inst.cnt_fifo_wait_m = 28'd1000;
defparam i2c_eeprom_testbench.i2c_ctrl_top.device_addr_w = 8'b1010_0000;
defparam i2c_eeprom_testbench.i2c_ctrl_top.device_addr_r = 8'b1010_0001;

i2c_eeprom i2c_eeprom_testbench(
.sys_clk     (sys_clk          ),
.sys_rst_n   (sys_rst_n        ),
.key         (key              ),     

.led         (    led          ),
.i2c_scl     (i2c_scl          ),
.i2c_sda     (i2c_sda          )            
    );

M24LC64 M24LC64_inst(
.A0             (  1'b0             ), //      ַ
.A1             (  1'b0             ), //      ַ
.A2             (  1'b0             ), //      ַ
.WP             (  1'b0             ), //д     źţ  ߵ ƽ  Ч
.SDA            (  i2c_sda            ), 
.SCL            (  i2c_scl            ), 
.RESET          (  ~sys_rst_n          )
);

endmodule

其中,M24LC64为EEPROM器件的fpga实现,相关v文件在网上查得,供参考。

`timescale 1ns/10ps

module M24LC64 (A0, A1, A2, WP, SDA, SCL, RESET);

   input                A0;                             // chip select bit
   input                A1;                             // chip select bit
   input                A2;                             // chip select bit

   input                WP;                             // write protect pin

   inout                SDA;                            // serial data I/O
   input                SCL;                            // serial data clock

   input                RESET;                          // system reset


// *******************************************************************************************************
// **   DECLARATIONS                                                                                    **
// *******************************************************************************************************

   reg                  SDA_DO;                         // serial data - output
   reg                  SDA_OE;                         // serial data - output enable

   wire                 SDA_DriveEnable;                // serial data output enable
   reg                  SDA_DriveEnableDlyd;            // serial data output enable - delayed

   wire [02:00]         ChipAddress;                    // hardwired chip address

   reg  [03:00]         BitCounter;                     // serial bit counter

   reg                  START_Rcvd;                     // START bit received flag
   reg                  STOP_Rcvd;                      // ACK_4 bit received flag
   reg                  CTRL_Rcvd;                      // control byte received flag
   reg                  ADHI_Rcvd;                      // byte address hi received flag
   reg                  ADLO_Rcvd;                      // byte address lo received flag
   reg                  MACK_Rcvd;                      // master acknowledge received flag

   reg                  WrCycle;                        // memory write cycle
   reg                  RdCycle;                        // memory read cycle

   reg  [07:00]         ShiftRegister;                  // input data shift register

   reg  [07:00]         ControlByte;                    // control byte register
   wire                 RdWrBit;                        // read/write control bit

   reg  [12:00]         StartAddress;                   // memory access starting address
   reg  [04:00]         PageAddress;                    // memory page address

   reg  [07:00]         WrDataByte [0:31];              // memory write data buffer
   wire [07:00]         RdDataByte;                     // memory read data

   reg  [15:00]         WrCounter;                      // write buffer counter

   reg  [04:00]         WrPointer;                      // write buffer pointer
   reg  [12:00]         RdPointer;                      // read address pointer

   reg                  WriteActive;                    // memory write cycle active

   reg  [07:00]         MemoryBlock [0:8191];           // EEPROM data memory array

   integer              LoopIndex;                      // iterative loop index

   integer              tAA;                            // timing parameter
   integer              tWC;                            // timing parameter


// *******************************************************************************************************
// **   INITIALIZATION                                                                                  **
// *******************************************************************************************************

//----------------------------
//------写数据间隔改 ?----------
   initial tAA = 900;                                   // SCL to SDA output delay
   initial tWC = 500;                                   // memory write cycle time

//   initial tAA = 900;                                   // SCL to SDA output delay
//   initial tWC = 5000000;                               // memory write cycle time
	
	
   initial begin
      SDA_DO = 0;
      SDA_OE = 0;
   end

   initial begin
      START_Rcvd = 0;
      STOP_Rcvd  = 0;
      CTRL_Rcvd  = 0;
      ADHI_Rcvd  = 0;
      ADLO_Rcvd  = 0;
      MACK_Rcvd  = 0;
   end

   initial begin
      BitCounter  = 0;
      ControlByte = 0;
   end

   initial begin
      WrCycle = 0;
      RdCycle = 0;

      WriteActive = 0;
   end

   assign ChipAddress = {A2,A1,A0};


// *******************************************************************************************************
// **   CORE LOGIC                                                                                      **
// *******************************************************************************************************
// -------------------------------------------------------------------------------------------------------
//      1.01:  START Bit Detection
// -------------------------------------------------------------------------------------------------------

   always @(negedge SDA) begin
      if (SCL == 1) begin
         START_Rcvd <= 1;
         STOP_Rcvd  <= 0;
         CTRL_Rcvd  <= 0;
         ADHI_Rcvd  <= 0;
         ADLO_Rcvd  <= 0;
         MACK_Rcvd  <= 0;

         WrCycle <= #1 0;
         RdCycle <= #1 0;

         BitCounter <= 0;
      end
   end

// -------------------------------------------------------------------------------------------------------
//      1.02:  ACK_4 Bit Detection
// -------------------------------------------------------------------------------------------------------

   always @(posedge SDA) begin
      if (SCL == 1) begin
         START_Rcvd <= 0;
         STOP_Rcvd  <= 1;
         CTRL_Rcvd  <= 0;
         ADHI_Rcvd  <= 0;
         ADLO_Rcvd  <= 0;
         MACK_Rcvd  <= 0;

         WrCycle <= #1 0;
         RdCycle <= #1 0;

         BitCounter <= 10;
      end
   end

// -------------------------------------------------------------------------------------------------------
//      1.03:  Input Shift Register
// -------------------------------------------------------------------------------------------------------

   always @(posedge SCL) begin
      ShiftRegister[00] <= SDA;
      ShiftRegister[01] <= ShiftRegister[00];
      ShiftRegister[02] <= ShiftRegister[01];
      ShiftRegister[03] <= ShiftRegister[02];
      ShiftRegister[04] <= ShiftRegister[03];
      ShiftRegister[05] <= ShiftRegister[04];
      ShiftRegister[06] <= ShiftRegister[05];
      ShiftRegister[07] <= ShiftRegister[06];
   end

// -------------------------------------------------------------------------------------------------------
//      1.04:  Input Bit Counter
// -------------------------------------------------------------------------------------------------------

   always @(posedge SCL) begin
      if (BitCounter < 10) BitCounter <= BitCounter + 1;
   end

// -------------------------------------------------------------------------------------------------------
//      1.05:  Control Byte Register
// -------------------------------------------------------------------------------------------------------

   always @(negedge SCL) begin
      if (START_Rcvd & (BitCounter == 8)) begin
         if (!WriteActive & (ShiftRegister[07:01] == {4'b1010,ChipAddress[02:00]})) begin
            if (ShiftRegister[00] == 0) WrCycle <= 1;
            if (ShiftRegister[00] == 1) RdCycle <= 1;

            ControlByte <= ShiftRegister[07:00];

            CTRL_Rcvd <= 1;
         end

         START_Rcvd <= 0;
      end
   end

   assign RdWrBit = ControlByte[00];

// -------------------------------------------------------------------------------------------------------
//      1.06:  Byte Address Register
// -------------------------------------------------------------------------------------------------------

   always @(negedge SCL) begin
      if (CTRL_Rcvd & (BitCounter == 8)) begin
         if (RdWrBit == 0) begin
            StartAddress[12:08] <= ShiftRegister[04:00];
            RdPointer[12:08]    <= ShiftRegister[04:00];

            ADHI_Rcvd <= 1;
         end

         WrCounter <= 0;
         WrPointer <= 0;

         CTRL_Rcvd <= 0;
      end
   end

   always @(negedge SCL) begin
      if (ADHI_Rcvd & (BitCounter == 8)) begin
         if (RdWrBit == 0) begin
            StartAddress[07:00] <= ShiftRegister[07:00];
            RdPointer[07:00]    <= ShiftRegister[07:00];

            ADLO_Rcvd <= 1;
         end

         WrCounter <= 0;
         WrPointer <= 0;

         ADHI_Rcvd <= 0;
      end
   end

// -------------------------------------------------------------------------------------------------------
//      1.07:  Write Data Buffer
// -------------------------------------------------------------------------------------------------------

   always @(negedge SCL) begin
      if (ADLO_Rcvd & (BitCounter == 8)) begin
         if (RdWrBit == 0) begin
            WrDataByte[WrPointer] <= ShiftRegister[07:00];

            WrCounter <= WrCounter + 1;
            WrPointer <= WrPointer + 1;
         end
      end
   end

// -------------------------------------------------------------------------------------------------------
//      1.08:  Acknowledge Generator
// -------------------------------------------------------------------------------------------------------

   always @(negedge SCL) begin
      if (!WriteActive) begin
         if (BitCounter == 8) begin
            if (WrCycle | (START_Rcvd & (ShiftRegister[07:01] == {4'b1010,ChipAddress[02:00]}))) begin
               SDA_DO <= 0;
               SDA_OE <= 1;
            end 
         end
         if (BitCounter == 9) begin
            BitCounter <= 0;

            if (!RdCycle) begin
               SDA_DO <= 0;
               SDA_OE <= 0;
            end
         end
      end
   end 

// -------------------------------------------------------------------------------------------------------
//      1.09:  Acknowledge Detect
// -------------------------------------------------------------------------------------------------------

   always @(posedge SCL) begin
      if (RdCycle & (BitCounter == 8)) begin
         if ((SDA == 0) & (SDA_OE == 0)) MACK_Rcvd <= 1;
      end
   end

   always @(negedge SCL) MACK_Rcvd <= 0;

// -------------------------------------------------------------------------------------------------------
//      1.10:  Write Cycle Timer
// -------------------------------------------------------------------------------------------------------

   always @(posedge STOP_Rcvd) begin
      if (WrCycle & (WP == 0) & (WrCounter > 0)) begin
         WriteActive = 1;
         #(tWC);
         WriteActive = 0;
      end
   end

   always @(posedge STOP_Rcvd) begin
      #(1.0);
      STOP_Rcvd = 0;
   end

// -------------------------------------------------------------------------------------------------------
//      1.11:  Write Cycle Processor
// -------------------------------------------------------------------------------------------------------

   always @(negedge WriteActive) begin
      for (LoopIndex = 0; LoopIndex < WrCounter; LoopIndex = LoopIndex + 1) begin
         PageAddress = StartAddress[04:00] + LoopIndex;

         MemoryBlock[{StartAddress[12:05],PageAddress[04:00]}] = WrDataByte[LoopIndex[04:00]];
      end
   end

// -------------------------------------------------------------------------------------------------------
//      1.12:  Read Data Multiplexor
// -------------------------------------------------------------------------------------------------------

   always @(negedge SCL) begin
      if (BitCounter == 8) begin
         if (WrCycle & ADLO_Rcvd) begin
            RdPointer <= StartAddress + WrPointer + 1;
         end
         if (RdCycle) begin
            RdPointer <= RdPointer + 1;
         end
      end
   end

   assign RdDataByte = MemoryBlock[RdPointer[12:00]];

// -------------------------------------------------------------------------------------------------------
//      1.13:  Read Data Processor
// -------------------------------------------------------------------------------------------------------

   always @(negedge SCL) begin
      if (RdCycle) begin
         if (BitCounter == 8) begin
            SDA_DO <= 0;
            SDA_OE <= 0;
         end
         else if (BitCounter == 9) begin
            SDA_DO <= RdDataByte[07];

            if (MACK_Rcvd) SDA_OE <= 1;
         end
         else begin
            SDA_DO <= RdDataByte[7-BitCounter];
         end
      end
   end

// -------------------------------------------------------------------------------------------------------
//      1.14:  SDA Data I/O Buffer
// -------------------------------------------------------------------------------------------------------

   bufif1 (SDA, 1'b0, SDA_DriveEnableDlyd);

   assign SDA_DriveEnable = !SDA_DO & SDA_OE;
   always @(SDA_DriveEnable) SDA_DriveEnableDlyd <= #(tAA) SDA_DriveEnable;


// *******************************************************************************************************
// **   DEBUG LOGIC                                                                                     **
// *******************************************************************************************************
// -------------------------------------------------------------------------------------------------------
//      2.01:  Memory Data Bytes
// -------------------------------------------------------------------------------------------------------

   wire [07:00] MemoryByte_000 = MemoryBlock[00];
   wire [07:00] MemoryByte_001 = MemoryBlock[01];
   wire [07:00] MemoryByte_002 = MemoryBlock[02];
   wire [07:00] MemoryByte_003 = MemoryBlock[03];
   wire [07:00] MemoryByte_004 = MemoryBlock[04];
   wire [07:00] MemoryByte_005 = MemoryBlock[05];
   wire [07:00] MemoryByte_006 = MemoryBlock[06];
   wire [07:00] MemoryByte_007 = MemoryBlock[07];
   wire [07:00] MemoryByte_008 = MemoryBlock[08];
   wire [07:00] MemoryByte_009 = MemoryBlock[09];
   wire [07:00] MemoryByte_00A = MemoryBlock[10];
   wire [07:00] MemoryByte_00B = MemoryBlock[11];
   wire [07:00] MemoryByte_00C = MemoryBlock[12];
   wire [07:00] MemoryByte_00D = MemoryBlock[13];
   wire [07:00] MemoryByte_00E = MemoryBlock[14];
   wire [07:00] MemoryByte_00F = MemoryBlock[15];

// -------------------------------------------------------------------------------------------------------
//      2.02:  Write Data Buffer
// -------------------------------------------------------------------------------------------------------

   wire [07:00] WriteData_00 = WrDataByte[00];
   wire [07:00] WriteData_01 = WrDataByte[01];
   wire [07:00] WriteData_02 = WrDataByte[02];
   wire [07:00] WriteData_03 = WrDataByte[03];
   wire [07:00] WriteData_04 = WrDataByte[04];
   wire [07:00] WriteData_05 = WrDataByte[05];
   wire [07:00] WriteData_06 = WrDataByte[06];
   wire [07:00] WriteData_07 = WrDataByte[07];
   wire [07:00] WriteData_08 = WrDataByte[08];
   wire [07:00] WriteData_09 = WrDataByte[09];
   wire [07:00] WriteData_0A = WrDataByte[10];
   wire [07:00] WriteData_0B = WrDataByte[11];
   wire [07:00] WriteData_0C = WrDataByte[12];
   wire [07:00] WriteData_0D = WrDataByte[13];
   wire [07:00] WriteData_0E = WrDataByte[14];
   wire [07:00] WriteData_0F = WrDataByte[15];

   wire [07:00] WriteData_10 = WrDataByte[16];
   wire [07:00] WriteData_11 = WrDataByte[17];
   wire [07:00] WriteData_12 = WrDataByte[18];
   wire [07:00] WriteData_13 = WrDataByte[19];
   wire [07:00] WriteData_14 = WrDataByte[20];
   wire [07:00] WriteData_15 = WrDataByte[21];
   wire [07:00] WriteData_16 = WrDataByte[22];
   wire [07:00] WriteData_17 = WrDataByte[23];
   wire [07:00] WriteData_18 = WrDataByte[24];
   wire [07:00] WriteData_19 = WrDataByte[25];
   wire [07:00] WriteData_1A = WrDataByte[26];
   wire [07:00] WriteData_1B = WrDataByte[27];
   wire [07:00] WriteData_1C = WrDataByte[28];
   wire [07:00] WriteData_1D = WrDataByte[29];
   wire [07:00] WriteData_1E = WrDataByte[30];
   wire [07:00] WriteData_1F = WrDataByte[31];


// *******************************************************************************************************
// **   TIMING CHECKS                                                                                   **
// *******************************************************************************************************

   wire TimingCheckEnable = (RESET == 0) & (SDA_OE == 0);
   wire StopTimingCheckEnable = TimingCheckEnable && SCL;
	
//--------------------------------
//-------仿真时时序约束需改动--------
//--------------------------------
   specify
      specparam
         tHI = 600,                                     // SCL pulse width - high
//         tLO = 1300,                                    // SCL pulse width - low
         tLO = 600, 
			tSU_STA = 600,                                 // SCL to SDA setup time
         tHD_STA = 600,                                 // SCL to SDA hold time
         tSU_DAT = 100,                                 // SDA to SCL setup time
         tSU_STO = 600,                                 // SCL to SDA setup time
         tSU_WP = 600,                                  // WP to SDA setup time
         tHD_WP = 1300,                                 // WP to SDA hold time
//         tBUF = 1300;                                   // Bus free time
         tBUF = 600; 
			
      $width (posedge SCL, tHI);
      $width (negedge SCL, tLO);

      $width (posedge SDA &&& SCL, tBUF);

      $setup (posedge SCL, negedge SDA &&& TimingCheckEnable, tSU_STA);
      $setup (SDA, posedge SCL &&& TimingCheckEnable, tSU_DAT);
      $setup (posedge SCL, posedge SDA &&& TimingCheckEnable, tSU_STO);
      $setup (WP, posedge SDA &&& StopTimingCheckEnable, tSU_WP);

      $hold  (negedge SDA &&& TimingCheckEnable, negedge SCL, tHD_STA);
      $hold  (posedge SDA &&& StopTimingCheckEnable, WP, tHD_WP);
   endspecify

endmodule

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值