本实验采用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信号。地址和数据都是高位在前、低位在后。



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


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驱动模块。整个工程的模块划分如下图所示:

按下数据写操作按键,写触发信号传入按键消抖模块(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 存储芯片执行数据读写操作。

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