1.原理
IIC(Inter-Integrated Circuit)协议是一种串行通信协议,它具有以下特点:
- 双线通信:IIC协议使用两根信号线进行通信,分别是串行数据线SDA和串行时钟线SCL。
- 多主从架构:IIC支持多主多从的通信方式,允许多个设备连接到同一总线上,并且每个设备都有唯一的地址。
- 开漏输出:所有设备的SDA和SCL引脚都采用开漏输出结构,通过外接上拉电阻实现线与逻辑关系,防止数据冲突。
- 自动寻址:总线上的所有设备都可以通过软件寻址,具有唯一的7位或10位地址。
- 支持仲裁机制:在多主机系统中,通过冲突检测和仲裁机制防止多个主机同时发起数据传输时存在的冲突。
- 自动应答:IIC总线上所有器件都具有自动应答功能,保证数据传输的正确性。
- 传输速率:IIC支持不同的传输速率,包括标准模式(100kb/s)、快速模式(400kb/s)、增强快速模式(1Mb/s)、高速模式(3.4Mb/s)和极速模式(5Mb/s。
- 总线电容限制:IIC总线允许挂载的设备数量取决于总线上最大电容值,一般为400pf(高速模式100pf。
- 简单可靠:IIC协议结构简单,可靠性高,适用于低速外围设备的数据传输。
- 广泛应用:由于其简单性和可靠性,IIC协议被广泛应用于各种嵌入式系统中,如存储器、LED及LCD驱动器、A/D及D/A转换器等。
IIC协议的这些特点使其在嵌入式系统和集成电路间的通信中非常流行,特别是在对硬件资源要求较低、需要连接多个低速设备的应用场景中。
A2A1A3地址表示可以连接8个EEPROM设备,当读写控制位为0表示主机要对从机进行数据写入操作,1表示主机要对从机进行数据读出操作。当主机把器件地址发送到总线上,所有的从机都会收到,然后和自身的器件地址做对比,如果和总线上的地址相同,那么从机设备就会给主机发送一个应答信号。
内存地址只是一个编号,代表一个内存空间;内存地址所执行的内存单元大小就是1字节,跟内存地址位数无关
1.写操作
以上是单字节写
以上是页写
所有的i2c设备都支持单字节写入,但只有一部分I2C设备支持页写操作。并且支持页写操作的设备一次性写入的数据量不能超过这个设备单页所包含的存储单元数。而24c64单页的存储单元个数是32,也就是一次页写操作最多写入32个字节的数据。
2.读操作
以上是随机读操作
以上是顺序读
2.实践
任务
使用写控制按键向EEPROM写入1-10共10个字节的数据,再使用读控制按键读出数据并在数码管上显示出来。
64Kbit=8KB,EEPROM每个存储空间只能存储1字节的数据,24C64需要13位宽的地址。
以上是数据写的状态转移图
以上是读和写的状态转移图
系统时钟是50MHZ,i2c_clk是1mhz,所以i2c_clk信号经过系统时钟50分频。
时钟分频的时序图
i2c_scl的时钟频率是250K,250K相当于i2c_clk的4分频
以上是写操作的时序图
以上是读操作的时序图
2.1 i2c_ctrl.v
module i2c_ctrl
#(
parameter DEVICE_ADDR = 7'b1010_011,
parameter SYS_CLK_FREQ = 'd50_000_000,
parameter SCL_FREQ = 'd250_000
)
(
input wire sys_clk ,
input wire sys_rst_n ,
input wire i2c_start ,
input wire wr_en ,
input wire [15:0] byte_addr ,
input wire [7:0] wr_data ,
input wire rd_en ,
input wire addr_num ,
output reg i2c_scl ,
inout wire i2c_sda ,
output reg [7:0] rd_data ,
output reg i2c_end ,
output reg i2c_clk
);
parameter CNT_CLK_MAX = (SYS_CLK_FREQ / SCL_FREQ) >> 3 ;
parameter IDLE = 4'd00,
START = 4'd01,
SEND_D_A = 4'd02,
ACK_1 = 4'd03,
SEND_B_H = 4'd04,
ACK_2 = 4'd05,
SEND_B_L = 4'd06,
ACK_3 = 4'd07,
WR_DATA = 4'd08,
ACK_4 = 4'd09,
START_2 = 4'd10,
SEND_R_A = 4'd11,
ACK_5 = 4'd12,
RD_DATA = 4'd13,
N_ACK = 4'd14,
STOP = 4'd15;
wire sda_en ;
wire sda_in ;
reg [7:0] cnt_clk ;
reg [3:0] state ;
reg [1:0] cnt_i2c_clk ;
reg cnt_i2c_clk_en ;
reg [2:0] cnt_bit ;
reg sda_out ;
reg ack ;
reg [7:0] rd_data_reg ;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_clk <= 8'd0;
else if(cnt_clk == CNT_CLK_MAX - 1)
cnt_clk <= 8'd0;
else
cnt_clk <= cnt_clk + 1'b1;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
i2c_clk <= 1'b1;
else if(cnt_clk == CNT_CLK_MAX - 1)
i2c_clk <= ~i2c_clk;
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 <= state;
START:
if(cnt_i2c_clk == 2'd3)
state <= SEND_D_A;
else
state <= state;
SEND_D_A:
if((cnt_bit == 3'd7) && (cnt_i2c_clk == 2'd3))
state <= ACK_1;
else
state <= state;
ACK_1:
if((cnt_i2c_clk == 2'd3) && (ack == 1'b0))
begin
if(addr_num == 1'b1)
state <= SEND_B_H;
else
state <= SEND_B_L;
end
else
state <= state;
SEND_B_H:
if((cnt_bit == 3'd7) && (cnt_i2c_clk == 2'd3))
state <= ACK_2;
else
state <= state;
ACK_2:
if((cnt_i2c_clk == 2'd3) && (ack == 1'b0))
state <= SEND_B_L;
else
state <= state;
SEND_B_L:
if((cnt_bit == 3'd7) && (cnt_i2c_clk == 2'd3))
state <= ACK_3;
else
state <= state;
ACK_3:
if((cnt_i2c_clk == 2'd3) && (ack == 1'b0))
begin
if(wr_en == 1'b1)
state <= WR_DATA;
else if(rd_en == 1'b1)
state <= START_2;
else
state <= state;
end
else
state <= state;
WR_DATA:
if((cnt_bit == 3'd7) && (cnt_i2c_clk == 2'd3))
state <= ACK_4;
else
state <= state;
ACK_4:
if((cnt_i2c_clk == 2'd3) && (ack == 1'b0))
state <= STOP;
else
state <= state;
START_2:
if(cnt_i2c_clk == 2'd3)
state <= SEND_R_A;
else
state <= state;
SEND_R_A:
if((cnt_bit == 3'd7) && (cnt_i2c_clk == 2'd3))
state <= ACK_5;
else
state <= state;
ACK_5:
if((cnt_i2c_clk == 2'd3) && (ack == 1'b0))
state <= RD_DATA;
else
state <= state;
RD_DATA:
if((cnt_bit == 3'd7) && (cnt_i2c_clk == 2'd3))
state <= N_ACK;
else
state <= state;
N_ACK:
if(cnt_i2c_clk == 2'd3)
state <= STOP;
else
state <= state;
STOP:
if((cnt_bit == 3'd3) && (cnt_i2c_clk == 2'd3))
state <= IDLE;
else
state <= state;
default:state <= IDLE;
endcase
always@(posedge i2c_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_i2c_clk <= 2'd0;
else if(cnt_i2c_clk_en == 1'b1)
cnt_i2c_clk <= cnt_i2c_clk + 1'b1;
always@(posedge i2c_clk or negedge sys_rst_n)
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;
always@(posedge i2c_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_bit <= 3'd0;
else if((state == IDLE) || (state == START) || (state == ACK_1)
||(state == ACK_2) || (state == ACK_3) ||(state == ACK_4)
|| (state == ACK_5) ||(state == START_2) || (state == N_ACK))
cnt_bit <= 3'd0;
else if((cnt_bit == 3'd7) && (cnt_i2c_clk == 2'd3))
cnt_bit <= 3'd0;
else if((cnt_i2c_clk == 2'd3)&& (state != IDLE))
cnt_bit <= cnt_bit + 1'b1;
always@(*)
case(state)
IDLE:
sda_out <= 1'b1;
START:
if(cnt_i2c_clk == 2'd0)
sda_out <= 1'b1;
else
sda_out <= 1'b0;
SEND_D_A:
if(cnt_bit <= 3'd6)
sda_out <= DEVICE_ADDR[6 - cnt_bit];
else
sda_out <= 1'b0;
ACK_1:
sda_out <= 1'b1;
SEND_B_H:
sda_out <= byte_addr[15 - cnt_bit];
ACK_2:
sda_out <= 1'b1;
SEND_B_L:
sda_out <= byte_addr[7 - cnt_bit];
ACK_3:
sda_out <= 1'b1;
WR_DATA:
sda_out <= wr_data[7 - cnt_bit];
ACK_4:
sda_out <= 1'b1;
START_2:
if(cnt_i2c_clk <= 2'd1)
sda_out <= 1'b1;
else
sda_out <= 1'b0;
SEND_R_A:
if(cnt_bit <= 3'd6)
sda_out <= DEVICE_ADDR[6 - cnt_bit];
else
sda_out <= 1'b1;
ACK_5:
sda_out <= 1'b1;
RD_DATA:
sda_out <= 1'b1;
N_ACK:
sda_out <= 1'b1;
STOP:
if((cnt_bit == 3'd0) && (cnt_i2c_clk < 2'd3))
sda_out <= 1'b0;
else
sda_out <= 1'b1;
default:sda_out <= 1'b1;
endcase
assign sda_en = ((state == RD_DATA) || (state == ACK_1) || (state == ACK_2) || (state == ACK_3) || (state == ACK_4) || (state == ACK_5)) ? 1'b0 : 1'b1;
always@(*)
case(state)
ACK_1,ACK_2,ACK_3,ACK_4,ACK_5:
if(cnt_i2c_clk == 2'd0)
ack <= sda_in;//1'b0;//
else
ack <= ack;
default:ack <= 1'b1;
endcase
assign sda_in = i2c_sda;
always@(*)
case(state)
IDLE:
rd_data_reg <= 8'd0;
RD_DATA:
if(cnt_i2c_clk == 2'd2)
rd_data_reg[7-cnt_bit] <= sda_in;
else
rd_data_reg <= rd_data_reg;
default:rd_data_reg <= rd_data_reg;
endcase
always@(*)
case(state)
IDLE:
i2c_scl <= 1'b1;
START:
if(cnt_i2c_clk == 2'd3)
i2c_scl <= 1'b0;
else
i2c_scl <= 1'b1;
SEND_D_A,ACK_1,SEND_B_H,ACK_2,SEND_B_L,ACK_3,
WR_DATA,ACK_4,START_2,SEND_R_A,ACK_5,RD_DATA,N_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
assign i2c_sda = (sda_en == 1'b1) ? sda_out : 1'bz;
always@(posedge i2c_clk or negedge sys_rst_n)
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)
if(sys_rst_n == 1'b0)
rd_data <= 8'd0;
else if((state == RD_DATA) && (cnt_bit == 3'd7) && (cnt_i2c_clk == 2'd3))
rd_data <= rd_data_reg;
endmodule
2.2 i2c_rw_data.v
module i2c_rw_data
(
input wire sys_clk ,
input wire sys_rst_n ,
input wire write ,
input wire read ,
input wire [7:0] rd_data ,
input wire i2c_end ,
input wire i2c_clk ,
output reg i2c_start ,
output reg wr_en ,
output reg [15:0] byte_addr ,
output reg [7:0] wr_data ,
output reg rd_en ,
output wire [7:0] fifo_data
);
parameter CNT_WR_RD_MAX = 8'd200;
parameter CNT_START_MAX = 16'd50_000;
parameter CNT_DATA_NUM = 8'd10;
parameter CNT_WAIT_MAX = 20'd500_000;
wire [7:0] data_num ;
reg wr_valid ;
reg [7:0] cnt_wr ;
reg rd_valid ;
reg [7:0] cnt_rd ;
reg [15:0] cnt_start ;
reg [7:0] cnt_wr_num ;
reg [7:0] cnt_rd_num ;
reg fifo_rd_valid;
reg fifo_rd_en ;
reg [19:0] cnt_wait ;
reg [7:0] cnt_rd_fifo_num ;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
wr_valid <= 1'b0;
else if(cnt_wr == CNT_WR_RD_MAX - 1)
wr_valid <= 1'b0;
else if(write == 1'b1)
wr_valid <= 1'b1;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_wr <= 8'd0;
else if(wr_valid == 1'b0)
cnt_wr <= 8'd0;
else if(wr_valid == 1'b1)
cnt_wr <= cnt_wr + 1'b1;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
rd_valid <= 1'b0;
else if(cnt_rd == CNT_WR_RD_MAX - 1)
rd_valid <= 1'b0;
else if(read == 1'b1)
rd_valid <= 1'b1;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_rd <= 8'd0;
else if(rd_valid == 1'b0)
cnt_rd <= 8'd0;
else if(rd_valid == 1'b1)
cnt_rd <= cnt_rd + 1'b1;
always@(posedge i2c_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_start <= 16'd0;
else if((wr_en == 1'b0) && (rd_en == 1'b0))
cnt_start <= 16'd0;
else if(cnt_start == CNT_START_MAX - 1'b1)
cnt_start <= 16'd0;
else if((wr_en == 1'b1) || (rd_en == 1'b1))
cnt_start <= cnt_start + 1'b1;
always@(posedge i2c_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_wr_num <= 8'd0;
else if(wr_en == 1'b0)
cnt_wr_num <= 8'd0;
else if((wr_en == 1'b1) && (i2c_end == 1'b1))
cnt_wr_num <= cnt_wr_num + 1'b1;
always@(posedge i2c_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_rd_num <= 8'd0;
else if(rd_en == 1'b0)
cnt_rd_num <= 8'd0;
else if((rd_en == 1'b1) && (i2c_end == 1'b1))
cnt_rd_num <= cnt_rd_num + 1'b1;
always@(posedge i2c_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
fifo_rd_valid <= 1'b0;
else if((cnt_rd_fifo_num == CNT_DATA_NUM)
&& (cnt_wait == CNT_WAIT_MAX - 1'b1))
fifo_rd_valid <= 1'b0;
else if(data_num == CNT_DATA_NUM)
fifo_rd_valid <= 1'b1;
always@(posedge i2c_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
fifo_rd_en <= 1'b0;
else if((cnt_rd_fifo_num < CNT_DATA_NUM)
&& (cnt_wait == CNT_WAIT_MAX - 1'b1))
fifo_rd_en <= 1'b1;
else
fifo_rd_en <= 1'b0;
always@(posedge i2c_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_wait <= 20'd0;
else if(fifo_rd_valid == 1'b0)
cnt_wait <= 20'd0;
else if(cnt_wait == CNT_WAIT_MAX - 1'b1)
cnt_wait <= 20'd0;
else if(fifo_rd_valid == 1'b1)
cnt_wait <= cnt_wait + 1'b1;
always@(posedge i2c_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_rd_fifo_num <= 8'd0;
else if(fifo_rd_valid == 1'b0)
cnt_rd_fifo_num <= 8'd0;
else if(fifo_rd_en == 1'b1)
cnt_rd_fifo_num <= cnt_rd_fifo_num + 1'b1;
always@(posedge i2c_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
wr_en <= 1'b0;
else if((cnt_wr_num == CNT_DATA_NUM - 1'b1)
&& (i2c_end == 1'b1) && (wr_en == 1'b1))
wr_en <= 1'b0;
else if(wr_valid == 1'b1)
wr_en <= 1'b1;
always@(posedge i2c_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
rd_en <= 1'b0;
else if((cnt_rd_num == CNT_DATA_NUM - 1'b1)
&& (i2c_end == 1'b1) && (rd_en == 1'b1))
rd_en <= 1'b0;
else if(rd_valid == 1'b1)
rd_en <= 1'b1;
always@(posedge i2c_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
i2c_start <= 1'b0;
else if(cnt_start == CNT_START_MAX - 1'b1)
i2c_start <= 1'b1;
else
i2c_start <= 1'b0;
always@(posedge i2c_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
byte_addr <= 16'h00_99;
else if((wr_en == 1'b0) && (rd_en == 1'b0))
byte_addr <= 16'h00_99;
else if(((wr_en == 1'b1) || (rd_en == 1'b1)) && (i2c_end == 1'b1))
byte_addr <= byte_addr + 1'b1;
always@(posedge i2c_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
wr_data <= 8'h01;
else if(wr_en == 1'b0)
wr_data <= 8'h01;
else if((wr_en == 1'b1) && (i2c_end == 1'b1))
wr_data <= wr_data + 1'b1;
fifo_data fifo_data_inst
(
.clock (i2c_clk),
.data (rd_data),
.rdreq (fifo_rd_en),
.wrreq (i2c_end && rd_en),
.q (fifo_data),
.usedw (data_num)
);
endmodule
i2c_clk的频率是1mhz,去采集sys_clk的50mhz,会出现问题采集不正确的情况,所以就要做一个跨时钟域的处理。需要将读写使能信号同步到i2c_clk时钟下。要保证在i2c_clk信号下,能正确采集到读写使能信号。
跨时钟域处理:
多比特数据:fifo,ram IP核,写入数据缓存一下,写入时钟和读出时钟使用不同的时钟就可以实现跨时钟域的处理。
单比特数据:如果两个时钟信号同频不同相,采用打拍的方式处理。但是在此处,两个时钟不同频,而且是从高速时钟域到低俗时钟域,我们要延长触发信号的保持时间来处理跨时钟域信号。
增加有效信号大于i2c_clk两个时钟周期的长度。50mhz和1mhz存在50倍的关系,也就是1个i2c_clk时钟的时钟周期等于50个sys_clk的时钟周期,因此有效信号的高电平保持时间至少要大于100个sys_clk的时钟周期,为了保险起见这里选择200个sys_clk的时钟周期。
两个字节的写入,中间的间隔要大于5ms。以上是24c64的数据手册。i2c_clk是1mhz,1mhz=1000_00hz=1/1000_000s=0.000_001s=1us。5ms=5000us。5000us/1us=5000也就是至少要间隔5000个i2c_clk时钟周期。
2.3 i2c_eeprom.v
module i2c_eeprom
(
input wire sys_clk ,
input wire sys_rst_n ,
input wire key_wr ,
input wire key_rd ,
output wire ds ,
output wire oe ,
output wire shcp ,
output wire stcp ,
output wire i2c_scl ,
inout wire i2c_sda
);
parameter CNT_MAX = 20'd999_999;
wire write ;
wire read ;
wire i2c_start;
wire wr_en ;
wire [15:0] byte_addr;
wire [7:0] wr_data ;
wire rd_en ;
wire [7:0] fifo_data;
wire [7:0] rd_data ;
wire i2c_end;
wire i2c_clk;
key_filter
#(
.CNT_MAX (CNT_MAX)
)
key_filter_wr
(
.sys_clk (sys_clk),
.sys_rst_n (sys_rst_n),
.key_in (key_wr),
.key_flag (write)
);
key_filter
#(
.CNT_MAX (CNT_MAX)
)
key_filter_rd
(
.sys_clk (sys_clk),
.sys_rst_n (sys_rst_n),
.key_in (key_rd),
.key_flag (read)
);
i2c_rw_data i2c_rw_data_inst
(
.sys_clk (sys_clk),
.sys_rst_n (sys_rst_n),
.write (write),
.read (read),
.rd_data (rd_data),
.i2c_end (i2c_end),
.i2c_clk (i2c_clk),
.i2c_start (i2c_start),
.wr_en (wr_en),
.byte_addr (byte_addr),
.wr_data (wr_data),
.rd_en (rd_en),
.fifo_data (fifo_data)
);
i2c_ctrl
#(
.DEVICE_ADDR (7'b1010_011),
.SYS_CLK_FREQ ('d50_000_000),
.SCL_FREQ ('d250_000)
)
i2c_ctrl_inst
(
.sys_clk (sys_clk),
.sys_rst_n (sys_rst_n),
.i2c_start (i2c_start),
.wr_en (wr_en),
.byte_addr (byte_addr),
.wr_data (wr_data),
.rd_en (rd_en),
.addr_num (1'b1),
.i2c_scl (i2c_scl),
.i2c_sda (i2c_sda),
.rd_data (rd_data),
.i2c_end (i2c_end),
.i2c_clk (i2c_clk)
);
seg_595_dynamic seg_595_dynamic_inst
(
.sys_clk (sys_clk),
.sys_rst_n (sys_rst_n),
.data (fifo_data ),
.point ( ),
.sign ( ),
.seg_en (1'b1),
.ds (ds ),
.oe (oe ),
.shcp (shcp),
.stcp (stcp)
);
endmodule
2.4 tb_i2c_eeprom.v
`timescale 1ns/1ns
module tb_i2c_eeprom();
wire ds ;
wire oe ;
wire shcp ;
wire stcp ;
wire i2c_scl;
wire i2c_sda;
reg sys_clk ;
reg sys_rst_n ;
reg key_wr ;
reg key_rd ;
initial
begin
sys_clk = 1'b1;
sys_rst_n <= 1'b0;
key_wr <= 1'b1;
key_rd <= 1'b1;
#200
sys_rst_n <= 1'b1;
#1000
key_wr <= 1'b0;
key_rd <= 1'b1;
#400
key_wr <= 1'b1;
key_rd <= 1'b1;
#55_000_0000
key_wr <= 1'b1;
key_rd <= 1'b0;
#400
key_wr <= 1'b1;
key_rd <= 1'b1;
end
always #10 sys_clk = ~sys_clk;
defparam i2c_eeprom_inst.CNT_MAX = 5;
defparam i2c_eeprom_inst.i2c_rw_data_inst.CNT_WAIT_MAX = 1000;
i2c_eeprom i2c_eeprom_inst
(
.sys_clk (sys_clk),
.sys_rst_n (sys_rst_n),
.key_wr (key_wr),
.key_rd (key_rd),
.ds (ds ),
.oe (oe ),
.shcp (shcp ),
.stcp (stcp ),
.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
2.5 M24LC64.v (从官网上下载的仿真文件)
// *******************************************************************************************************
// ** **
// ** 24LC64.v - Microchip 24LC64 64K-BIT I2C SERIAL EEPROM (VCC = +2.5V TO +5.5V) **
// ** **
// *******************************************************************************************************
// ** **
// ** This information is distributed under license from Young Engineering. **
// ** COPYRIGHT (c) 2009 YOUNG ENGINEERING **
// ** ALL RIGHTS RESERVED **
// ** **
// ** **
// ** Young Engineering provides design expertise for the digital world **
// ** Started in 1990, Young Engineering offers products and services for your electronic design **
// ** project. We have the expertise in PCB, FPGA, ASIC, firmware, and software design. **
// ** From concept to prototype to production, we can help you. **
// ** **
// ** http://www.young-engineering.com/ **
// ** **
// *******************************************************************************************************
// ** This information is provided to you for your convenience and use with Microchip products only. **
// ** Microchip disclaims all liability arising from this information and its use. **
// ** **
// ** THIS INFORMATION IS PROVIDED "AS IS." MICROCHIP MAKES NO REPRESENTATION OR WARRANTIES OF **
// ** ANY KIND WHETHER EXPRESS OR IMPLIED, WRITTEN OR ORAL, STATUTORY OR OTHERWISE, RELATED TO **
// ** THE INFORMATION PROVIDED TO YOU, INCLUDING BUT NOT LIMITED TO ITS CONDITION, QUALITY, **
// ** PERFORMANCE, MERCHANTABILITY, NON-INFRINGEMENT, OR FITNESS FOR PURPOSE. **
// ** MICROCHIP IS NOT LIABLE, UNDER ANY CIRCUMSTANCES, FOR SPECIAL, INCIDENTAL OR CONSEQUENTIAL **
// ** DAMAGES, FOR ANY REASON WHATSOEVER. **
// ** **
// ** It is your responsibility to ensure that your application meets with your specifications. **
// ** **
// *******************************************************************************************************
// ** Revision : 1.4 **
// ** Modified Date : 02/04/2009 **
// ** Revision History: **
// ** **
// ** 10/01/2003: Initial design **
// ** 07/19/2004: Fixed the timing checks and the open-drain modeling for SDA. **
// ** 01/06/2006: Changed the legal information in the header **
// ** 12/04/2006: Corrected timing checks to reference proper clock edges **
// ** Added timing check for Tbuf (bus free time) **
// ** Reduced memory blocks to single, monolithic array **
// ** 02/04/2009: Added timing checks for tSU_WP and tHD_WP **
// ** **
// *******************************************************************************************************
// ** TABLE OF CONTENTS **
// *******************************************************************************************************
// **---------------------------------------------------------------------------------------------------**
// ** DECLARATIONS **
// **---------------------------------------------------------------------------------------------------**
// **---------------------------------------------------------------------------------------------------**
// ** INITIALIZATION **
// **---------------------------------------------------------------------------------------------------**
// **---------------------------------------------------------------------------------------------------**
// ** CORE LOGIC **
// **---------------------------------------------------------------------------------------------------**
// ** 1.01: START Bit Detection **
// ** 1.02: STOP Bit Detection **
// ** 1.03: Input Shift Register **
// ** 1.04: Input Bit Counter **
// ** 1.05: Control Byte Register **
// ** 1.06: Byte Address Register **
// ** 1.07: Write Data Buffer **
// ** 1.08: Acknowledge Generator **
// ** 1.09: Acknowledge Detect **
// ** 1.10: Write Cycle Timer **
// ** 1.11: Write Cycle Processor **
// ** 1.12: Read Data Multiplexor **
// ** 1.13: Read Data Processor **
// ** 1.14: SDA Data I/O Buffer **
// ** **
// **---------------------------------------------------------------------------------------------------**
// ** DEBUG LOGIC **
// **---------------------------------------------------------------------------------------------------**
// ** 2.01: Memory Data Bytes **
// ** 2.02: Write Data Buffer **
// ** **
// **---------------------------------------------------------------------------------------------------**
// ** TIMING CHECKS **
// **---------------------------------------------------------------------------------------------------**
// ** **
// *******************************************************************************************************
`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; // STOP 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: STOP 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