verilog I2C_eeprom 手册分析及代码编写思路
.基本属性_EEPROM_24LC04B/24AA04
- 型号(EEPROM):24LC04B
- 时钟频率:100-400KHZ
- 两线串行接口,兼容I2C协议
- 电可擦除,断电数据不会丢失
- 两个block,每个block :256*8bit.总共内存4kbit
一.AC特性表
- TBUF :Bus free time: Time the bus must be free before a new transmission can start,两次传输之间需要等待1300~4700ns;
- 其它的保持时间和建立时间在时钟频率设定在100~400Khz时都是正确的
二.I2C协议数据读写时序图,起始停止图
1.数据总线和时钟线在空闲时都必须拉高
2.开始条件:时钟总线高电平时,拉低数据总线
3.数据允许变化:在时钟总线低电平时
4.数据保持稳定,采样数据:在时钟总线高电平时
5.停止条件:时钟总线高电平时,拉高数据总线
6.ACK:应答是低电平有效,应答由接收方传出
三.24AA04/24LC04B如何通过I2C协议与主机进行通信
该eeprom的设备地址:1010
1.写入EEPROM(字节写/页写)
(1) 字节写
- 发送起始位:SCLK高电平时,SDA拉低
- 写入控制字节:设备地址(1010)+任意两位(xx)+block选择(0/1)+读写控制(1/0)
- 等待从机应答:从机控制SDA拉低
- 写入字节地址:word address(eeprom 中block中的地址)
- 写入数据:SCLK低电平时,主机控制SDA写入数据
- 发送停止位:stop
每一bit都需要一个sclk周期完成
控制字节
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Fc0A9fnC-1642921692513)(C:\Users\DELL\Desktop\Revision\Protocol_Verilog\i2c_eeprom\doc\部分图\image-20220117215006419.png)]
(2)页写
- 发送起始位:SCLK高电平时,SDA拉低
- 写入控制字节:设备地址(1010)+任意两位(xx)+block选择(0/1)+读写控制(1/0)
- 等待从机应答:从机控制SDA拉低
- 写入字节地址:word address(eeprom 中block中的地址)
- 写入数据:SCLK低电平时,主机控制SDA写入数据
- .
- .
- .
- 写入第16个数据
- 发送停止位:stop
- 每一bit都需要一个sclk周期完成
2. 读取EEPROM的数据(当前地址读/随机读/顺序读)
(1).当前地址读
(2).随机读,顺序读
- 发送起始位:SCLK高电平时,SDA拉低
- 写入控制字节:设备地址(1010)+任意两位(xx)+block选择(0/1)+写控制(0)
- 等待从机应答:从机控制SDA拉低
- 写入字节地址:word address(eeprom 中block中的地址)
- 发送起始位:SCLK高电平时,SDA拉低
- 写入控制字节:设备地址(1010)+任意两位(xx)+block选择(0/1)+读控制(1)
- 读取数据:SCLK高电平时,读取数据SDA
- .
- .
- .
- 读取最后一个数据
- 发送停止位:stop
- 每一bit都需要一个sclk周期完成
四. 状态转移图以及代码
1.I2C接口(驱动)模块
(1).状态图
(2).接口代码
module eeprom_interface (
input clk ,
input rst_n ,
input [7:0] data_in ,
input req ,
input [3:0] command ,
output reg slack ,
output done ,
output [7:0] data_out ,
output reg sclk ,
inout sda
);
localparam IDLE = 8'b00000_001,
START = 8'b00000_010,
READ = 8'b00000_100,
WRITE = 8'b00001_000,
ACK = 8'b00010_000,
SACK = 8'b00100_000,
STOP = 8'b01000_000,
DONE = 8'b1000_0000;
localparam CMD_START = 4'b1000,
CMD_READ = 4'b0100,
CMD_WRITE = 4'b0010,
CMD_STOP = 4'b0001;
localparam T_CLK = 200_000 ,
T_CYCLE = 250 ,
T_CYCLE_HALF = 124 ,
T_LOW_HALF = 65 ,
T_HIGH_HALF = 189 ;
reg sda_out ;
wire sda_in ;
reg sda_out_en;
reg [7:0] data_out_r;
reg [7:0] state_c;
reg [7:0] state_n;
wire idle_start ;
wire idle_read ;
wire idle_write ;
wire start_read ;
wire start_write;
wire write_sack ;
wire read_ack ;
wire ack_stop ;
wire ack_done ;
wire sack_stop ;
wire sack_done ;
wire stop_done ;
wire done_idle ;
reg [7:0] clk_cnt ;
wire add_clk_cnt;
wire end_clk_cnt;
reg [3:0] bit_cnt ;
wire add_bit_cnt;
wire end_bit_cnt;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
state_c <= IDLE;
end
else begin
state_c <= state_n;
end
end
assign idle_start = state_c == IDLE && req && (command & CMD_START);
assign idle_read = state_c == IDLE && req && (command & CMD_READ);
assign idle_write = state_c == IDLE && req && (command & CMD_WRITE);
assign start_read = state_c == START && (command & CMD_READ) && end_bit_cnt;
assign start_write= state_c == START && (command & CMD_WRITE)&& end_bit_cnt;
assign write_sack = state_c == WRITE && end_bit_cnt;
assign read_ack = state_c == READ && end_bit_cnt;
assign ack_stop = state_c == ACK && (command & CMD_STOP) && end_bit_cnt;
assign ack_done = state_c == ACK && !((command & CMD_STOP))&& end_bit_cnt;
assign sack_stop = state_c == SACK && (command & CMD_STOP || ~slack) && end_bit_cnt;
assign sack_done = state_c == SACK && (!(command & CMD_STOP))&& end_bit_cnt && slack ;
assign stop_done = state_c == STOP && end_bit_cnt;
assign done_idle = state_c == DONE && 1'b1;
always @(*) begin
case(state_c)
IDLE :
if (idle_start) begin
state_n = START;
end
else if (idle_read) begin
state_n = READ;
end
else if (idle_write) begin
state_n = WRITE;
end
else begin
state_n = state_c;
end
START :
if (start_read) begin
state_n = READ;
end
else if (start_write) begin
state_n = WRITE;
end
else begin
state_n = state_c;
end
READ :
if (read_ack) begin
state_n = ACK;
end
else begin
state_n = state_c;
end
WRITE :
if (write_sack) begin
state_n = SACK;
end
else begin
state_n = state_c;
end
ACK :
if (ack_stop) begin
state_n = STOP;
end
else if (ack_done) begin
state_n = DONE;
end
else begin
state_n = state_c;
end
SACK :
if (sack_stop) begin
state_n = STOP;
end
else if (sack_done) begin
state_n = DONE;
end
else begin
state_n = state_c;
end
STOP :
if (stop_done) begin
state_n = DONE;
end
else begin
state_n = state_c;
end
DONE :
if (done_idle) begin
state_n = IDLE;
end
else begin
state_n = state_c;
end
default : state_n <= state_c ;
endcase
end
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
clk_cnt <= 0;
end
else if((state_c == IDLE) || (state_c == DONE))begin
clk_cnt <= 0;
end
else if(add_clk_cnt)begin
if(end_clk_cnt)begin
clk_cnt <= 0;
end
else begin
clk_cnt <= clk_cnt + 1;
end
end
else begin
clk_cnt <= clk_cnt;
end
end
assign add_clk_cnt = state_c != IDLE;
assign end_clk_cnt = add_clk_cnt && clk_cnt == T_CYCLE - 1;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
sclk <= 1'b1;
end
else if (idle_start || idle_write || idle_read) begin
sclk <= 1'b0;
end
else if(clk_cnt == T_CYCLE_HALF && add_clk_cnt)begin
sclk <= 1'b1;
end
else if (stop_done || ack_done || sack_done) begin
sclk <= 1'b1;
end
else if (end_clk_cnt) begin
sclk <= 1'b0;
end
end
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
bit_cnt <= 0;
end
else if(add_bit_cnt)begin
if(end_bit_cnt)begin
bit_cnt <= 0;
end
else begin
bit_cnt <= bit_cnt + 1;
end
end
else begin
bit_cnt <= bit_cnt;
end
end
assign add_bit_cnt = (state_c == START || state_c == READ || state_c == WRITE || state_c == READ||state_c == ACK||state_c == SACK||state_c == STOP) && end_clk_cnt;
assign end_bit_cnt = add_bit_cnt && bit_cnt == ((state_c == READ || state_c==WRITE)?7:0);
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
sda_out <= 1'b1;
end
else if(state_c == START && clk_cnt == T_LOW_HALF)begin
sda_out <= 1'b1;
end
else if(state_c == START && clk_cnt == T_HIGH_HALF)begin
sda_out <= 1'b0;
end
else if(state_c == WRITE && clk_cnt == T_LOW_HALF)begin
sda_out <= data_in[7 - bit_cnt];
end
else if (state_c == ACK && clk_cnt == T_LOW_HALF && !(command &CMD_STOP)) begin
sda_out <= 1'b0;
end
else if (state_c == ACK && clk_cnt == T_LOW_HALF && (command &CMD_STOP)) begin
sda_out <= 1'b1;
end
else if (state_c == STOP && clk_cnt == T_LOW_HALF) begin
sda_out <= 1'b0;
end
else if (state_c == STOP && clk_cnt == T_HIGH_HALF) begin
sda_out <= 1'b1;
end
end
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
sda_out_en <= 0;
end
else if(idle_start || idle_write || sack_stop || read_ack)begin
sda_out_en =1'b1;
end
else if(write_sack || idle_read )begin
sda_out_en = 1'b0;
end
else if (ack_done || sack_done || stop_done) begin
sda_out_en = 1'b0;
end
end
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
data_out_r <= 0;
slack <= 1'b1;
end
else if(state_c == READ && clk_cnt == T_HIGH_HALF)begin
data_out_r[7-bit_cnt] <= sda_in ;
end
else if (state_c == SACK && clk_cnt == T_HIGH_HALF ) begin
slack <= ~sda_in;
end
end
assign sda_in = sda;
assign sda = sda_out_en?sda_out:1'bz;
assign data_out = data_out_r ;
assign done = (stop_done || sack_done || ack_done )?1'b1:1'b0;
endmodule
2. I2C控制模块
(1).状态图
(2).控制模块代码模块
module eeprom_ctrl (
input clk ,
input rst_n ,
input [1:0] key_down ,
input slack ,
input done ,
input [7:0] data_rec ,
output reg [7:0] data_send ,
output reg req ,
output reg [3:0] command
);
localparam IDLE = 4'b0001,
READ = 4'b0010,
WRITE = 4'b0100,
DONE = 4'b1000;
localparam BYTE_WRITE = 3 ,
PAGE_WRITE = 18,
BYTE_READ = 4 ,
PAGE_READ = 19;
localparam WORD_ADDR = 8'b0001_0000;
localparam CMD_START = 4'b1010,
CMD_READ = 4'b0100,
CMD_WRITE = 4'b0010,
CMD_STOP = 4'b0001;
reg [7:0] data_rec_r;
reg [3:0] state_c;
reg [3:0] state_n;
reg r_req;
reg w_req;
reg [4:0] byte_cnt;
wire end_byte_cnt;
wire add_byte_cnt;
reg [4:0] byte_sel;
wire idle_read ;
wire idle_write;
wire read_done ;
wire read_idle ;
wire write_done;
wire write_idle;
wire done_idle ;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
state_c <= IDLE;
end
else begin
state_c <= state_n;
end
end
assign idle_read = state_c == IDLE && r_req;
assign idle_write = state_c == IDLE && w_req;
assign read_done = state_c == READ && (end_byte_cnt );
assign read_idle = state_c == READ && (~end_byte_cnt && ~slack);
assign write_done = state_c == WRITE && (end_byte_cnt );
assign write_idle = state_c == WRITE && (~end_byte_cnt && ~slack);
assign done_idle = state_c == DONE && 1'b1;
always @(*) begin
case(state_c)
IDLE :
if (idle_read) begin
state_n = READ;
end
else if (idle_write) begin
state_n = WRITE;
end
else begin
state_n = state_c;
end
READ :
if (read_done) begin
state_n = DONE;
end
else if (read_idle) begin
state_n = IDLE;
end
else begin
state_n = state_c;
end
WRITE :
if (write_done) begin
state_n = DONE;
end
else if (write_idle) begin
state_n = IDLE;
end
else begin
state_n = state_c;
end
DONE :
if (done_idle) begin
state_n = IDLE;
end
else begin
state_n = state_c;
end
default : state_n <= state_c ;
endcase
end
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
byte_cnt <= 0;
end
else if(add_byte_cnt)begin
if(end_byte_cnt)begin
byte_cnt <= 0;
end
else begin
byte_cnt <= byte_cnt + 1;
end
end
else begin
byte_cnt <= byte_cnt;
end
end
assign add_byte_cnt = done;
assign end_byte_cnt = add_byte_cnt && byte_cnt ==byte_sel - 1;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
r_req <= 0;
w_req <= 0;
end
else if(key_down[0])begin
r_req <= 1'b1;
end
else if(key_down[1])begin
w_req <= 1'b1;
end
else begin
r_req <= 0;
w_req <= 0;
end
end
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
byte_sel <= 0;
end
else if(key_down[0])begin
byte_sel <= BYTE_READ ;
end
else if(key_down[1])begin
byte_sel <= BYTE_WRITE;
end
end
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
data_send <= 0;
req <= 0;
command <= 0;
end
else if (state_c == IDLE) begin
data_send <= 0;
req <= 0;
command <= 0;
end
else if( end_byte_cnt) begin
req <= 1'b0;
end
else if(state_c == READ)begin
case(byte_cnt)
0 :
begin
req <= 1'b1;
data_send <= 8'b1010_1000;
command <= 4'b1010;
end
1 :
begin
req <= 1'b1;
data_send <= WORD_ADDR;
command <= 4'b0010;
end
2 :
begin
req <= 1'b1;
data_send <= 8'b1010_1001;
command <= 4'b1010;
end
3 :
begin
req <= 1'b1;
data_rec_r <= data_rec;
command <= 4'b0101;
end
default :
begin
req <= 1'b1;
data_rec_r <= data_rec;
command <= 4'b0100;
end
endcase
end
else if(state_c == WRITE)begin
case(byte_cnt)
0 :
begin
req <= 1'b1;
data_send <= 8'b1010_1000;
command <= 4'b1010;
end
1 :
begin
req <= 1'b1;
data_send <= WORD_ADDR;
command <= 4'b0010;
end
2 :
begin
req <= 1'b1;
data_send <= 8'b1010_1111;
command <= 4'b0011;
end
default :
begin
req <= 1'b1;
data_send <= 8'b1010_1111;
command <= 4'b0010;
end
endcase
end
end
endmodule
3.TOP顶层模块
(1).整体框架
(2).代码
module top (
input clk ,
input rst_n ,
input [1:0] key_in ,
output [7:0] seg_dig ,
output [5:0] seg_sel ,
output sclk ,
inout sda
);
wire [1:0] key_down;
wire [7:0] data_write;
wire [7:0] data_read;
wire [3:0] command;
wire done ;
wire req ;
wire slack ;
eeprom_interface u_eeprom_interface (
.clk (clk ) ,
.rst_n (rst_n ) ,
.data_in (data_write ) ,
.req (req ) ,
.command (command ) ,
.slack (slack ) ,
.done (done ) ,
.data_out (data_read ) ,
.sclk (sclk ) ,
.sda (sda )
);
eeprom_ctrl u_eeprom_ctrl(
.clk (clk ),
.rst_n (rst_n ),
.key_down (key_down ),
.slack (slack ),
.done (done ),
.data_rec (data_read ),
.data_send (data_write),
.req (req ) ,
.command (command )
);
key_debounce u_key_debounce (
.clk (clk ),
.rst_n (rst_n ),
.key_in (key_in ),
.key_out (key_down)
);
seg u_seg(
.clk (clk ) ,
.rst_n (rst_n ) ,
.data_in ({4'b0,4'b0,4'b0,4'b0,data_read[7:4],data_read[3:0]}) ,
.seg_dig (seg_dig) ,
.seg_sel (seg_sel)
);
endmodule
|