去年0基础参加高云赛道跟着野火手搓了一个IIC,但是调用OV5640时一直不出图,又没学会仿真IIC,一直以为没有写出来,头痛炸裂半年的阴影。现在跟着小梅哥课程学习到可以仿真EEPROM器件。本实验采用的是镁光官网提供的 EEPROM 仿真模型,仿真模型为24LC04B.v 文件。
开发板:7a35t
EEPROM:器件id:1010_000
实验中系统时钟为50Mhz,SCL频率为250Khz。
仿真时先对8'hB1地址写入8'h11,再读出数据。
实验中i2c_sda的三态门控制关系如下:
对应代码:
下图中红框中为发送区域。两个箭头分别为开始和停止时刻:
下图中红框中为接收区域。两个箭头分别为开始和停止时刻:
i2c控制模块:
module i2c_ctrl
/*
#(
parameter DEVICE_ADDR =7'b1111_000 ,
parameter SYS_CLK_FREQ ='d50_000_000 ,
parameter SCL_FREQ = 'd250_000
)
*/
(
input sys_clk,
input sys_rst_n,
input wr_en,
input rd_en,
input i2c_start,
input addr_num, //1:两个字节的存储地址 0:一个字节的存储地址
input [15:0]byte_addr,
input [7:0]wr_data,
output reg i2c_clk, // i2c工作时钟 1Mhz
output reg i2c_end,
output reg [7:0]rd_data,
output reg i2c_scl, //250khz
inout i2c_sda
);
parameter DEVICE_ADDR =7'b1010_000 ;
parameter SYS_CLK_FREQ ='d50_000_000 ;
parameter SCL_FREQ = 'd250_000;
parameter CNT_CLK_MAX ='d25 ;// 输入时钟为50Mhz时,SCL为
parameter IDLE =4'd0 ;
parameter START =4'd1 ;
parameter SEND_D_A =4'd2 ;
parameter ACK_1 =4'd3 ;
parameter SEND_B_H =4'd4 ;
parameter ACK_2 =4'd5 ;
parameter SEND_B_L =4'd6 ;
parameter ACK_3 =4'd7 ;
parameter WR_DATA =4'd8 ;
parameter ACK_4 =4'd9 ;
parameter START_2 =4'd10 ;
parameter SEND_R_A =4'd11 ;
parameter ACK_5 =4'd12 ;
parameter RD_DATA =4'd13 ;
parameter N_ACK =4'd14 ;
parameter STOP =4'd15 ;
wire sda_en ;
wire sda_in ;
reg [7:0] cnt_clk ; //从0计数到24,对系统时钟50Mhz分频成为 1Mhz时钟
reg [3:0] state ;
reg [1:0] cnt_i2c_clk ; //从 0计数到3 i2c_scl的一个周期为 i2c_clk的 4 倍,所以计数4个i2c_clk 周期就是一个i2c_scl周期
reg cnt_i2c_clk_en ;
reg [3:0] cnt_bit ; //计数从 0 到 7 ,总共 8 个i2c_scl周期,即传送了 8 个bit数据
reg sda_out ; //等价于i2c_sda_reg
reg [7:0]rd_data_reg ;
reg ack ;
always @(posedge sys_clk or negedge sys_rst_n)
begin
if(~sys_rst_n)
cnt_clk<=8'd0 ;
else if(cnt_clk==CNT_CLK_MAX-1)
cnt_clk<=8'd0 ;
else
cnt_clk<=cnt_clk+8'd1 ;
end
always@(posedge sys_clk or negedge sys_rst_n)
begin
if(~sys_rst_n)
i2c_clk<=1'b1 ;
else if(cnt_clk==CNT_CLK_MAX-1)
i2c_clk<=~i2c_clk ;
else
i2c_clk<=i2c_clk ; //产生1Mhz时钟
end
always@(posedge i2c_clk or negedge sys_rst_n)
begin
if(~sys_rst_n)
state<=IDLE ;
else case(state)
IDLE:
begin
if(i2c_start==1'b1)
state<=START ;
else
state<=state ;
end
START:
begin
if(cnt_i2c_clk==2'd3)
state<=SEND_D_A ;
else
state<=state ;
end
SEND_D_A:
begin
if((cnt_i2c_clk==2'd3) && (cnt_bit==4'd7))
state<=ACK_1 ;
else
state<=state ;
end
ACK_1:
begin
if((cnt_i2c_clk==2'd3) && (ack==1'b0))
begin
if(addr_num)
state<=SEND_B_H ;
else
state<=SEND_B_L ;
end
else
state<=state ;
end
SEND_B_H:
begin
if((cnt_i2c_clk==2'd3) && (cnt_bit==4'd7))
state<=SEND_B_L ;
else
state<=state ;
end
ACK_2:
begin
if((cnt_i2c_clk==2'd3) && (ack==1'b0))
state<=SEND_B_L ;
else
state<=state ;
end
SEND_B_L:
begin
if((cnt_i2c_clk==2'd3) && (cnt_bit==4'd7))
state<=ACK_3 ;
else
state<=state ;
end
ACK_3:
begin
if((cnt_i2c_clk==2'd3) && (ack==1'b0))
begin
if(wr_en)
state<=WR_DATA ;
else if(rd_en)
state<=START_2 ;
else
state<=state ;
end
else
state<=state ;
end
WR_DATA:
begin
if((cnt_i2c_clk==2'd3) && (cnt_bit==4'd7))
state<=ACK_4;
else
state<=state ;
end
ACK_4:
begin
if((cnt_i2c_clk==2'd3) && (ack==1'b0))
state<=STOP ;
else
state<=state ;
end
START_2:
begin
if(cnt_i2c_clk==2'd3)
state<=SEND_R_A ;
else
state<=state ;
end
SEND_R_A:
begin
if((cnt_i2c_clk==2'd3) && (cnt_bit==4'd7))
state<=ACK_5;
else
state<=state ;
end
ACK_5:
begin
if((cnt_i2c_clk==2'd3) && (ack==1'b0))
state<=RD_DATA ;
else
state<=state ;
end
RD_DATA:
begin
if((cnt_i2c_clk==2'd3) && (cnt_bit==4'd7))
state<=N_ACK ;
else
state<=state ;
end
N_ACK:
begin
if(cnt_i2c_clk==2'd3) //?
state<=STOP ;
else
state<=state ;
end
STOP:
begin
if((cnt_i2c_clk==2'd3) && (cnt_bit==4'd3)) //对 SCL 计数 4 个周期 保证时间充足
state<=IDLE ;
else
state<=state ;
end
default : state<=IDLE ;
endcase
end
always@(posedge i2c_clk or negedge sys_rst_n ) //cnt_i2c_clk循环计数从0到3 4个i2c_clk周期为 1个scl周期
begin
if(~sys_rst_n)
cnt_i2c_clk<=2'd0 ;
else if(cnt_i2c_clk_en)
cnt_i2c_clk<=cnt_i2c_clk+1'b1 ;
end
//对cnt_i2c_clk_en 限制
always@(posedge i2c_clk or negedge sys_rst_n)
begin
if(~sys_rst_n)
cnt_i2c_clk_en<=1'b0 ;
else if((state==STOP) && (cnt_i2c_clk==2'd3) && (cnt_bit==4'd3))
cnt_i2c_clk_en<=1'b0 ;
else if(i2c_start==1'b1)
cnt_i2c_clk_en<=1'b1 ;
end
//规定什么时候对 cnt_bit 进行计数
always@(posedge i2c_clk or negedge sys_rst_n)
begin
if(~sys_rst_n)
cnt_bit<=4'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<=4'd0 ;
else if((cnt_i2c_clk==2'd3) && (cnt_bit== 4'd7))
cnt_bit<=4'd0 ;
else if((cnt_i2c_clk==2'd3) && (state!=IDLE)) //? 怎么让cnt_bit在STOP状态加到3
cnt_bit<=cnt_bit+1'b1 ;
end
always@(*)
begin
case(state)
IDLE:
sda_out<=1'b1 ;
START:
begin
if(cnt_i2c_clk==2'b0)
sda_out<=1'b1 ;
else
sda_out<=1'b0 ;
end
SEND_D_A:
begin
if(cnt_bit<=4'd6)
sda_out<=DEVICE_ADDR[6-cnt_bit] ;
else
sda_out<=1'b0 ; //写控制字
end
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:
begin
if(cnt_i2c_clk<=2'd1)
sda_out<=1'b1 ;
else
sda_out<=1'b0 ;
end
SEND_R_A:
begin
if(cnt_bit<=4'd6)
sda_out<=DEVICE_ADDR[6-cnt_bit] ;
else
sda_out<=1'b1;//写控制字
end
ACK_5:
sda_out<=1'b1 ;
RD_DATA:
sda_out<=1'b1 ;
N_ACK:
sda_out<=1'b1 ;
STOP:
begin
if((cnt_bit==4'd0) && (cnt_i2c_clk<=2'd2))
sda_out<=1'b0 ;
else
sda_out<=1'b1 ;
end
default: sda_out<=1'b1 ;
endcase
end
//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@(posedge i2c_clk)
begin
case(state)
ACK_1,ACK_2,ACK_3,ACK_4,ACK_5:
begin
if(cnt_i2c_clk==2'd0)
ack<=sda_in ;
else
ack<=ack ;
end
default : ack<=1'b1 ;
endcase
end
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;
assign sda_in=i2c_sda ; //sda_in 在三态门中作为 i2c_sda 的输入
assign i2c_sda=(sda_en)? sda_out: 1'bz ;
always@(posedge i2c_clk)
begin
case(state)
IDLE:
rd_data_reg<=8'd0 ;
RD_DATA:
begin
if(cnt_i2c_clk==2'd1)
rd_data_reg[7-cnt_bit]<=sda_in ; //先传输最高位
end
default :rd_data_reg<=rd_data_reg ;
endcase
end
always@(posedge i2c_clk)
begin
case(state)
IDLE:
i2c_scl<=1'b1 ;
START:
begin
if(cnt_i2c_clk<=2'd1) //?有问题
i2c_scl<=1'b1 ;
else
i2c_scl<=1'b0 ;
end
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:
begin
if((cnt_i2c_clk==2'd0) || (cnt_i2c_clk==2'd1))
i2c_scl<=1'b1 ;
else
i2c_scl<=1'b0 ;
end
STOP:
begin
if((cnt_i2c_clk==2'd3) && (cnt_bit==4'd0))
i2c_scl<=1'b0 ;
else
i2c_scl<=1'b1 ;
end
default : i2c_scl<=1'b1 ;
endcase
end
//assign i2c_sda=(sda_en)? sda_out: 1'bz ;
always@(posedge i2c_clk or negedge sys_rst_n)
begin
if(~sys_rst_n)
i2c_end<=1'b0 ;
else if((state==STOP) && (cnt_bit==4'd3) &&(cnt_i2c_clk==2'd3))
i2c_end<=1'b1;
else
i2c_end<=1'b0 ;
end
always@(posedge i2c_clk or negedge sys_rst_n)
begin
if(~sys_rst_n)
rd_data<=8'd0 ;
else if((state==RD_DATA) && (cnt_bit==4'd7) && (cnt_i2c_clk==2'd3))
rd_data<=rd_data_reg ;
else
rd_data<=rd_data ;
end
endmodule
测试模块:
`timescale 1ns / 1ps
module tb_i2c_ctrl;
// i2c_ctrl Inputs
reg sys_clk ;
reg sys_rst_n ;
reg wr_en ;
reg rd_en ;
reg i2c_start ;
reg addr_num = 0;
reg [15:0] byte_addr ;
reg [7:0] wr_data ;
// i2c_ctrl Outputs
wire i2c_clk ;
wire i2c_end ;
wire [7:0] rd_data ;
wire i2c_scl ;
// i2c_ctrl Bidirs
wire i2c_sda ;
pullup(i2c_sda);
always #10 sys_clk=~sys_clk;
initial
begin
sys_clk=1'b0;
sys_rst_n=1'b0;
wr_en=1'b0;
rd_en=1'b0;
#200;
sys_rst_n=1'b1;
write_byte(8'hB1,8'h11);
read_byte(8'hB1);
$stop;
end
task write_byte;
input [7:0]men_addr;
input [7:0]data;
begin
byte_addr=men_addr;
wr_data=data;
i2c_start=1'b1;
wr_en=1'b1;
@(posedge i2c_end);
i2c_start=1'b0;
wr_en=1'b0;
#50000;
end
endtask
task read_byte;
input [7:0]men_addr;
begin
byte_addr=men_addr;
i2c_start=1'b1;
rd_en=1'b1;
@(posedge i2c_end);
i2c_start=1'b0;
rd_en=1'b0;
#50000;
end
endtask
/*
always@(posedge sys_clk or negedge sys_rst_n)
begin
if(~sys_rst_n)
i2c_start<=1'b0;
else
i2c_start<=1'b1;
end
*/
/*
always@(posedge sys_clk or negedge sys_rst_n)
begin
if(~sys_rst_n)
begin
wr_en<=1'b1;
rd_en<=1'b0;
end
else
begin
wr_en<=wr_en;
rd_en<=rd_en;
end
end
*/
M24LC04B M24LC04B(
.A0(0),
.A1(0),
.A2(0),
.WP(0),
.SDA(i2c_sda),
.SCL(i2c_scl),
.RESET(sys_rst_n)
);
i2c_ctrl u_i2c_ctrl (
.sys_clk ( sys_clk ),
.sys_rst_n ( sys_rst_n ),
.wr_en ( wr_en ),
.rd_en ( rd_en ),
.i2c_start ( i2c_start ),
.addr_num ( addr_num ),
.byte_addr ( byte_addr [15:0] ),
.wr_data ( wr_data [7:0] ),
.i2c_clk ( i2c_clk ),
.i2c_end ( i2c_end ),
.rd_data ( rd_data [7:0] ),
.i2c_scl ( i2c_scl ),
.i2c_sda ( i2c_sda )
);
endmodule
为了不让时钟满天飞,我把i2c_ctrl做了一个改进,主要是把产生的1Mhz分频时钟改成使能时钟,测试结果一致,生成的i2c_scl仍然是250khz。代码如下:
module i2c_ctrl
/*
#(
parameter DEVICE_ADDR =7'b1111_000 ,
parameter SYS_CLK_FREQ ='d50_000_000 ,
parameter SCL_FREQ = 'd250_000
)
*/
(
input sys_clk,
input sys_rst_n,
input wr_en,
input rd_en,
input i2c_start,
input addr_num, //1:两个字节的存储地址 0:一个字节的存储地址
input [15:0]byte_addr,
input [7:0]wr_data,
output i2c_clk_en, // i2c工作时钟 1Mhz 使能时钟信号
output reg i2c_end,
output reg [7:0]rd_data,
output reg i2c_scl, //250khz
inout i2c_sda
);
parameter DEVICE_ADDR =7'b1010_000 ;
parameter SYS_CLK_FREQ ='d50_000_000 ;
parameter SCL_FREQ = 'd250_000;
//parameter CNT_CLK_MAX ='d25 ;// 输入时钟为50Mhz时,产生1Mhz 的i2c_clk_en使能时钟信号
parameter CNT_CLK_MAX ='d50 ;// 输入时钟为50Mhz时,产生1Mhz 的i2c_clk_en使能时钟信号
parameter IDLE =4'd0 ;
parameter START =4'd1 ;
parameter SEND_D_A =4'd2 ;
parameter ACK_1 =4'd3 ;
parameter SEND_B_H =4'd4 ;
parameter ACK_2 =4'd5 ;
parameter SEND_B_L =4'd6 ;
parameter ACK_3 =4'd7 ;
parameter WR_DATA =4'd8 ;
parameter ACK_4 =4'd9 ;
parameter START_2 =4'd10 ;
parameter SEND_R_A =4'd11 ;
parameter ACK_5 =4'd12 ;
parameter RD_DATA =4'd13 ;
parameter N_ACK =4'd14 ;
parameter STOP =4'd15 ;
wire sda_en ;
wire sda_in ;
reg [7:0] cnt_clk ; //从0计数到24,对系统时钟50Mhz分频成为 1Mhz时钟
reg [3:0] state ;
reg [1:0] cnt_i2c_clk ; //从 0计数到3 i2c_scl的一个周期为 i2c_clk的 4 倍,所以计数4个i2c_clk 周期就是一个i2c_scl周期
reg cnt_i2c_clk_en ;
reg [3:0] cnt_bit ; //计数从 0 到 7 ,总共 8 个i2c_scl周期,即传送了 8 个bit数据
reg sda_out ; //等价于i2c_sda_reg
reg [7:0]rd_data_reg ;
reg ack ;
always @(posedge sys_clk or negedge sys_rst_n)
begin
if(~sys_rst_n)
cnt_clk<=8'd0 ;
else if(cnt_clk==CNT_CLK_MAX-1)
cnt_clk<=8'd0 ;
else
cnt_clk<=cnt_clk+8'd1 ;
end
assign i2c_clk_en=(cnt_clk==CNT_CLK_MAX-1)? 1'b1 :1'b0;
/*
always@(posedge sys_clk or negedge sys_rst_n)
begin
if(~sys_rst_n)
i2c_clk<=1'b1 ;
else if(cnt_clk==CNT_CLK_MAX-1)
i2c_clk<=~i2c_clk ;
else
i2c_clk<=i2c_clk ; //产生1Mhz时钟
end
*/
always@(posedge sys_clk or negedge sys_rst_n)
begin
if(~sys_rst_n)
state<=IDLE ;
else if(i2c_clk_en)
begin
case(state)
IDLE:
begin
if(i2c_start==1'b1)
state<=START ;
else
state<=state ;
end
START:
begin
if(cnt_i2c_clk==2'd3)
state<=SEND_D_A ;
else
state<=state ;
end
SEND_D_A:
begin
if((cnt_i2c_clk==2'd3) && (cnt_bit==4'd7))
state<=ACK_1 ;
else
state<=state ;
end
ACK_1:
begin
if((cnt_i2c_clk==2'd3) && (ack==1'b0))
begin
if(addr_num)
state<=SEND_B_H ;
else
state<=SEND_B_L ;
end
else
state<=state ;
end
SEND_B_H:
begin
if((cnt_i2c_clk==2'd3) && (cnt_bit==4'd7))
state<=SEND_B_L ;
else
state<=state ;
end
ACK_2:
begin
if((cnt_i2c_clk==2'd3) && (ack==1'b0))
state<=SEND_B_L ;
else
state<=state ;
end
SEND_B_L:
begin
if((cnt_i2c_clk==2'd3) && (cnt_bit==4'd7))
state<=ACK_3 ;
else
state<=state ;
end
ACK_3:
begin
if((cnt_i2c_clk==2'd3) && (ack==1'b0))
begin
if(wr_en)
state<=WR_DATA ;
else if(rd_en)
state<=START_2 ;
else
state<=state ;
end
else
state<=state ;
end
WR_DATA:
begin
if((cnt_i2c_clk==2'd3) && (cnt_bit==4'd7))
state<=ACK_4;
else
state<=state ;
end
ACK_4:
begin
if((cnt_i2c_clk==2'd3) && (ack==1'b0))
state<=STOP ;
else
state<=state ;
end
START_2:
begin
if(cnt_i2c_clk==2'd3)
state<=SEND_R_A ;
else
state<=state ;
end
SEND_R_A:
begin
if((cnt_i2c_clk==2'd3) && (cnt_bit==4'd7))
state<=ACK_5;
else
state<=state ;
end
ACK_5:
begin
if((cnt_i2c_clk==2'd3) && (ack==1'b0))
state<=RD_DATA ;
else
state<=state ;
end
RD_DATA:
begin
if((cnt_i2c_clk==2'd3) && (cnt_bit==4'd7))
state<=N_ACK ;
else
state<=state ;
end
N_ACK:
begin
if(cnt_i2c_clk==2'd3) //?
state<=STOP ;
else
state<=state ;
end
STOP:
begin
if((cnt_i2c_clk==2'd3) && (cnt_bit==4'd3)) //对 SCL 计数 4 个周期 保证时间充足
state<=IDLE ;
else
state<=state ;
end
default : state<=IDLE ;
endcase
end
end
always@(posedge sys_clk or negedge sys_rst_n ) //cnt_i2c_clk循环计数从0到3 4个i2c_clk周期为 1个scl周期
begin
if(~sys_rst_n)
cnt_i2c_clk<=2'd0 ;
else if(i2c_clk_en)
begin
if(cnt_i2c_clk_en)
cnt_i2c_clk<=cnt_i2c_clk+1'b1 ;
end
end
//对cnt_i2c_clk_en 限制
always@(posedge sys_clk or negedge sys_rst_n)
begin
if(~sys_rst_n)
cnt_i2c_clk_en<=1'b0 ;
else if(i2c_clk_en)
begin
if((state==STOP) && (cnt_i2c_clk==2'd3) && (cnt_bit==4'd3))
cnt_i2c_clk_en<=1'b0 ;
else if(i2c_start==1'b1)
cnt_i2c_clk_en<=1'b1 ;
end
end
//规定什么时候对 cnt_bit 进行计数
always@(posedge sys_clk or negedge sys_rst_n)
begin
if(~sys_rst_n)
cnt_bit<=4'd0 ;
else if(i2c_clk_en)
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<=4'd0 ;
else if((cnt_i2c_clk==2'd3) && (cnt_bit== 4'd7))
cnt_bit<=4'd0 ;
else if((cnt_i2c_clk==2'd3) && (state!=IDLE)) //? 怎么让cnt_bit在STOP状态加到3
cnt_bit<=cnt_bit+1'b1 ;
end
always@(*)
begin
case(state)
IDLE:
sda_out<=1'b1 ;
START:
begin
if(cnt_i2c_clk==2'b0)
sda_out<=1'b1 ;
else
sda_out<=1'b0 ;
end
SEND_D_A:
begin
if(cnt_bit<=4'd6)
sda_out<=DEVICE_ADDR[6-cnt_bit] ;
else
sda_out<=1'b0 ; //写控制字
end
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:
begin
if(cnt_i2c_clk<=2'd1)
sda_out<=1'b1 ;
else
sda_out<=1'b0 ;
end
SEND_R_A:
begin
if(cnt_bit<=4'd6)
sda_out<=DEVICE_ADDR[6-cnt_bit] ;
else
sda_out<=1'b1;//写控制字
end
ACK_5:
sda_out<=1'b1 ;
RD_DATA:
sda_out<=1'b1 ;
N_ACK:
sda_out<=1'b1 ;
STOP:
begin
if((cnt_bit==4'd0) && (cnt_i2c_clk<=2'd2))
sda_out<=1'b0 ;
else
sda_out<=1'b1 ;
end
default: sda_out<=1'b1 ;
endcase
end
//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@(posedge sys_clk)
begin
if(i2c_clk_en)
begin
case(state)
ACK_1,ACK_2,ACK_3,ACK_4,ACK_5:
begin
if(cnt_i2c_clk==2'd0)
ack<=sda_in ;
else
ack<=ack ;
end
default : ack<=1'b1 ;
endcase
end
end
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;
assign sda_in=i2c_sda ; //sda_in 在三态门中作为 i2c_sda 的输入
assign i2c_sda=(sda_en)? sda_out: 1'bz ;
always@(posedge sys_clk)
begin
if(i2c_clk_en)
begin
case(state)
IDLE:
rd_data_reg<=8'd0 ;
RD_DATA:
begin
if(cnt_i2c_clk==2'd1)
rd_data_reg[7-cnt_bit]<=sda_in ; //先传输最高位
end
default :rd_data_reg<=rd_data_reg ;
endcase
end
end
always@(posedge sys_clk)
begin
if(i2c_clk_en)
begin
case(state)
IDLE:
i2c_scl<=1'b1 ;
START:
begin
if(cnt_i2c_clk<=2'd1) //?有问题
i2c_scl<=1'b1 ;
else
i2c_scl<=1'b0 ;
end
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:
begin
if((cnt_i2c_clk==2'd0) || (cnt_i2c_clk==2'd1))
i2c_scl<=1'b1 ;
else
i2c_scl<=1'b0 ;
end
STOP:
begin
if((cnt_i2c_clk==2'd3) && (cnt_bit==4'd0))
i2c_scl<=1'b0 ;
else
i2c_scl<=1'b1 ;
end
default : i2c_scl<=1'b1 ;
endcase
end
end
//assign i2c_sda=(sda_en)? sda_out: 1'bz ;
always@(posedge sys_clk or negedge sys_rst_n)
begin
if(~sys_rst_n)
i2c_end<=1'b0 ;
else if(i2c_clk_en)
if((state==STOP) && (cnt_bit==4'd3) &&(cnt_i2c_clk==2'd3))
i2c_end<=1'b1;
else
i2c_end<=1'b0 ;
end
always@(posedge sys_clk or negedge sys_rst_n)
begin
if(~sys_rst_n)
rd_data<=8'd0 ;
else if(i2c_clk_en)
if((state==RD_DATA) && (cnt_bit==4'd7) && (cnt_i2c_clk==2'd3))
rd_data<=rd_data_reg ;
else
rd_data<=rd_data ;
end
endmodule
改进之后的波形图:可以看到i2c_scl的一个周期为4us,符合预期。
改进后的测试代码:
`timescale 1ns / 1ps
module tb_i2c_ctrl;
// i2c_ctrl Inputs
reg sys_clk ;
reg sys_rst_n ;
reg wr_en ;
reg rd_en ;
reg i2c_start ;
reg addr_num = 0;
reg [15:0] byte_addr ;
reg [7:0] wr_data ;
// i2c_ctrl Outputs
wire i2c_clk_en ;
wire i2c_end ;
wire [7:0] rd_data ;
wire i2c_scl ;
// i2c_ctrl Bidirs
wire i2c_sda ;
pullup(i2c_sda);
always #10 sys_clk=~sys_clk;
initial
begin
sys_clk=1'b0;
sys_rst_n=1'b0;
wr_en=1'b0;
rd_en=1'b0;
#200;
sys_rst_n=1'b1;
write_byte(8'hB1,8'h11);
read_byte(8'hB1);
$stop;
end
task write_byte;
input [7:0]men_addr;
input [7:0]data;
begin
byte_addr=men_addr;
wr_data=data;
i2c_start=1'b1;
wr_en=1'b1;
@(posedge i2c_end);
i2c_start=1'b0;
wr_en=1'b0;
#50000;
end
endtask
task read_byte;
input [7:0]men_addr;
begin
byte_addr=men_addr;
i2c_start=1'b1;
rd_en=1'b1;
@(posedge i2c_end);
i2c_start=1'b0;
rd_en=1'b0;
#50000;
end
endtask
/*
always@(posedge sys_clk or negedge sys_rst_n)
begin
if(~sys_rst_n)
i2c_start<=1'b0;
else
i2c_start<=1'b1;
end
*/
/*
always@(posedge sys_clk or negedge sys_rst_n)
begin
if(~sys_rst_n)
begin
wr_en<=1'b1;
rd_en<=1'b0;
end
else
begin
wr_en<=wr_en;
rd_en<=rd_en;
end
end
*/
M24LC04B M24LC04B(
.A0(0),
.A1(0),
.A2(0),
.WP(0),
.SDA(i2c_sda),
.SCL(i2c_scl),
.RESET(sys_rst_n)
);
i2c_ctrl u_i2c_ctrl (
.sys_clk ( sys_clk ),
.sys_rst_n ( sys_rst_n ),
.wr_en ( wr_en ),
.rd_en ( rd_en ),
.i2c_start ( i2c_start ),
.addr_num ( addr_num ),
.byte_addr ( byte_addr [15:0] ),
.wr_data ( wr_data [7:0] ),
.i2c_clk_en ( i2c_clk_en ),
.i2c_end ( i2c_end ),
.rd_data ( rd_data [7:0] ),
.i2c_scl ( i2c_scl ),
.i2c_sda ( i2c_sda )
);
endmodule
2024.5.28
再改进一个版本,因为实际物理期间两次操作之间需要有一定延时,所以在原来的基础上增加一个延时
i2c_ctrl代码:
module i2c_ctrl
/*
#(
parameter DEVICE_ADDR =7'b1111_000 ,
parameter SYS_CLK_FREQ ='d50_000_000 ,
parameter SCL_FREQ = 'd250_000
)
*/
(
input sys_clk,
input sys_rst_n,
input wr_en,
input rd_en,
input i2c_start,
input [19:0]delay_time,//实际器件两次操作之间需要延时
input addr_num, //1:两个字节的存储地址 0:一个字节的存储地址
input [15:0]byte_addr,
input [7:0]wr_data,
output i2c_clk_en, // i2c工作时钟 1Mhz 使能时钟信号
output reg i2c_end,
output reg [7:0]rd_data,
output reg i2c_scl, //250khz
inout i2c_sda
);
parameter DEVICE_ADDR =7'b1010_000 ;
parameter SYS_CLK_FREQ ='d50_000_000 ;
parameter SCL_FREQ = 'd250_000;
//parameter CNT_CLK_MAX ='d25 ;// 输入时钟为50Mhz时,产生1Mhz 的i2c_clk_en使能时钟信号
parameter CNT_CLK_MAX ='d50 ;// 输入时钟为50Mhz时,产生1Mhz 的i2c_clk_en使能时钟信号
parameter IDLE =4'd0 ;
parameter START =4'd1 ;
parameter SEND_D_A =4'd2 ;
parameter ACK_1 =4'd3 ;
parameter SEND_B_H =4'd4 ;
parameter ACK_2 =4'd5 ;
parameter SEND_B_L =4'd6 ;
parameter ACK_3 =4'd7 ;
parameter WR_DATA =4'd8 ;
parameter ACK_4 =4'd9 ;
parameter START_2 =4'd10 ;
parameter SEND_R_A =4'd11 ;
parameter ACK_5 =4'd12 ;
parameter RD_DATA =4'd13 ;
parameter N_ACK =4'd14 ;
parameter STOP =4'd15 ;
wire sda_en ;
wire sda_in ;
reg [7:0] cnt_clk ; //从0计数到24,对系统时钟50Mhz分频成为 1Mhz时钟
reg [3:0] state ;
reg [1:0] cnt_i2c_clk ; //从 0计数到3 i2c_scl的一个周期为 i2c_clk的 4 倍,所以计数4个i2c_clk 周期就是一个i2c_scl周期
reg cnt_i2c_clk_en ;
reg [3:0] cnt_bit ; //计数从 0 到 7 ,总共 8 个i2c_scl周期,即传送了 8 个bit数据
reg sda_out ; //等价于i2c_sda_reg
reg [7:0]rd_data_reg ;
reg ack ;
reg [19:0]delay_count;//延时计数
wire p_i2c_start;//检测i2c_start上升沿
reg i2c_start_reg1;
reg delay_en;//延迟使能信号,该信号有效时进行计数延时
reg i2c_start_delay;//延时过后的开始信号
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
i2c_start_reg1<=1'b0;
else
i2c_start_reg1<=i2c_start;
end
assign p_i2c_start=(i2c_start && !i2c_start_reg1) ? 1'b1: 1'b0;
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
delay_en<=1'b0;
else if(p_i2c_start)
delay_en<=1'b1;
else if(delay_count==delay_time)
delay_en<=1'b0;
end
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
delay_count<=20'd0;
else if(delay_count==delay_time)
delay_count<=20'd0;
else if(delay_en)
delay_count<=delay_count+1'b1;
end
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
i2c_start_delay<=1'b0;
else if(delay_count==delay_time)
i2c_start_delay<=1'b1;
else if(i2c_end)
i2c_start_delay<=1'b0;
end
always @(posedge sys_clk or negedge sys_rst_n)
begin
if(~sys_rst_n)
cnt_clk<=8'd0 ;
else if(cnt_clk==CNT_CLK_MAX-1)
cnt_clk<=8'd0 ;
else
cnt_clk<=cnt_clk+8'd1 ;
end
assign i2c_clk_en=(cnt_clk==CNT_CLK_MAX-1)? 1'b1 :1'b0;
/*
always@(posedge sys_clk or negedge sys_rst_n)
begin
if(~sys_rst_n)
i2c_clk<=1'b1 ;
else if(cnt_clk==CNT_CLK_MAX-1)
i2c_clk<=~i2c_clk ;
else
i2c_clk<=i2c_clk ; //产生1Mhz时钟
end
*/
always@(posedge sys_clk or negedge sys_rst_n)
begin
if(~sys_rst_n)
state<=IDLE ;
else if(i2c_clk_en)
begin
case(state)
IDLE:
begin
if(i2c_start_delay==1'b1)
state<=START ;
else
state<=state ;
end
START:
begin
if(cnt_i2c_clk==2'd3)
state<=SEND_D_A ;
else
state<=state ;
end
SEND_D_A:
begin
if((cnt_i2c_clk==2'd3) && (cnt_bit==4'd7))
state<=ACK_1 ;
else
state<=state ;
end
ACK_1:
begin
if((cnt_i2c_clk==2'd3) && (ack==1'b0))
begin
if(addr_num)
state<=SEND_B_H ;
else
state<=SEND_B_L ;
end
else
state<=state ;
end
SEND_B_H:
begin
if((cnt_i2c_clk==2'd3) && (cnt_bit==4'd7))
state<=SEND_B_L ;
else
state<=state ;
end
ACK_2:
begin
if((cnt_i2c_clk==2'd3) && (ack==1'b0))
state<=SEND_B_L ;
else
state<=state ;
end
SEND_B_L:
begin
if((cnt_i2c_clk==2'd3) && (cnt_bit==4'd7))
state<=ACK_3 ;
else
state<=state ;
end
ACK_3:
begin
if((cnt_i2c_clk==2'd3) && (ack==1'b0))
begin
if(wr_en)
state<=WR_DATA ;
else if(rd_en)
state<=START_2 ;
else
state<=state ;
end
else
state<=state ;
end
WR_DATA:
begin
if((cnt_i2c_clk==2'd3) && (cnt_bit==4'd7))
state<=ACK_4;
else
state<=state ;
end
ACK_4:
begin
if((cnt_i2c_clk==2'd3) && (ack==1'b0))
state<=STOP ;
else
state<=state ;
end
START_2:
begin
if(cnt_i2c_clk==2'd3)
state<=SEND_R_A ;
else
state<=state ;
end
SEND_R_A:
begin
if((cnt_i2c_clk==2'd3) && (cnt_bit==4'd7))
state<=ACK_5;
else
state<=state ;
end
ACK_5:
begin
if((cnt_i2c_clk==2'd3) && (ack==1'b0))
state<=RD_DATA ;
else
state<=state ;
end
RD_DATA:
begin
if((cnt_i2c_clk==2'd3) && (cnt_bit==4'd7))
state<=N_ACK ;
else
state<=state ;
end
N_ACK:
begin
if(cnt_i2c_clk==2'd3) //?
state<=STOP ;
else
state<=state ;
end
STOP:
begin
if((cnt_i2c_clk==2'd3) && (cnt_bit==4'd3)) //对 SCL 计数 4 个周期 保证时间充足
state<=IDLE ;
else
state<=state ;
end
default : state<=IDLE ;
endcase
end
end
always@(posedge sys_clk or negedge sys_rst_n ) //cnt_i2c_clk循环计数从0到3 4个i2c_clk周期为 1个scl周期
begin
if(~sys_rst_n)
cnt_i2c_clk<=2'd0 ;
else if(i2c_clk_en)
begin
if(cnt_i2c_clk_en)
cnt_i2c_clk<=cnt_i2c_clk+1'b1 ;
end
end
//对cnt_i2c_clk_en 限制
always@(posedge sys_clk or negedge sys_rst_n)
begin
if(~sys_rst_n)
cnt_i2c_clk_en<=1'b0 ;
else if(i2c_clk_en)
begin
if((state==STOP) && (cnt_i2c_clk==2'd3) && (cnt_bit==4'd3))
cnt_i2c_clk_en<=1'b0 ;
else if(i2c_start_delay==1'b1)
cnt_i2c_clk_en<=1'b1 ;
end
end
//规定什么时候对 cnt_bit 进行计数
always@(posedge sys_clk or negedge sys_rst_n)
begin
if(~sys_rst_n)
cnt_bit<=4'd0 ;
else if(i2c_clk_en)
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<=4'd0 ;
else if((cnt_i2c_clk==2'd3) && (cnt_bit== 4'd7))
cnt_bit<=4'd0 ;
else if((cnt_i2c_clk==2'd3) && (state!=IDLE)) //? 怎么让cnt_bit在STOP状态加到3
cnt_bit<=cnt_bit+1'b1 ;
end
always@(*)
begin
case(state)
IDLE:
sda_out<=1'b1 ;
START:
begin
if(cnt_i2c_clk==2'b0)
sda_out<=1'b1 ;
else
sda_out<=1'b0 ;
end
SEND_D_A:
begin
if(cnt_bit<=4'd6)
sda_out<=DEVICE_ADDR[6-cnt_bit] ;
else
sda_out<=1'b0 ; //写控制字
end
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:
begin
if(cnt_i2c_clk<=2'd1)
sda_out<=1'b1 ;
else
sda_out<=1'b0 ;
end
SEND_R_A:
begin
if(cnt_bit<=4'd6)
sda_out<=DEVICE_ADDR[6-cnt_bit] ;
else
sda_out<=1'b1;//写控制字
end
ACK_5:
sda_out<=1'b1 ;
RD_DATA:
sda_out<=1'b1 ;
N_ACK:
sda_out<=1'b1 ;
STOP:
begin
if((cnt_bit==4'd0) && (cnt_i2c_clk<=2'd2))
sda_out<=1'b0 ;
else
sda_out<=1'b1 ;
end
default: sda_out<=1'b1 ;
endcase
end
//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@(posedge sys_clk)
begin
if(i2c_clk_en)
begin
case(state)
ACK_1,ACK_2,ACK_3,ACK_4,ACK_5:
begin
if(cnt_i2c_clk==2'd0)
ack<=sda_in ;
else
ack<=ack ;
end
default : ack<=1'b1 ;
endcase
end
end
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;
assign sda_in=i2c_sda ; //sda_in 在三态门中作为 i2c_sda 的输入
assign i2c_sda=(sda_en)? sda_out: 1'bz ;
always@(posedge sys_clk)
begin
if(i2c_clk_en)
begin
case(state)
IDLE:
rd_data_reg<=8'd0 ;
RD_DATA:
begin
if(cnt_i2c_clk==2'd1)
rd_data_reg[7-cnt_bit]<=sda_in ; //先传输最高位
end
default :rd_data_reg<=rd_data_reg ;
endcase
end
end
always@(posedge sys_clk)
begin
if(i2c_clk_en)
begin
case(state)
IDLE:
i2c_scl<=1'b1 ;
START:
begin
if(cnt_i2c_clk<=2'd1) //?有问题
i2c_scl<=1'b1 ;
else
i2c_scl<=1'b0 ;
end
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:
begin
if((cnt_i2c_clk==2'd0) || (cnt_i2c_clk==2'd1))
i2c_scl<=1'b1 ;
else
i2c_scl<=1'b0 ;
end
STOP:
begin
if((cnt_i2c_clk==2'd3) && (cnt_bit==4'd0))
i2c_scl<=1'b0 ;
else
i2c_scl<=1'b1 ;
end
default : i2c_scl<=1'b1 ;
endcase
end
end
//assign i2c_sda=(sda_en)? sda_out: 1'bz ;
always@(posedge sys_clk or negedge sys_rst_n)
begin
if(~sys_rst_n)
i2c_end<=1'b0 ;
else if(i2c_clk_en)
if((state==STOP) && (cnt_bit==4'd3) &&(cnt_i2c_clk==2'd3))
i2c_end<=1'b1;
else
i2c_end<=1'b0 ;
end
always@(posedge sys_clk or negedge sys_rst_n)
begin
if(~sys_rst_n)
rd_data<=8'd0 ;
else if(i2c_clk_en)
if((state==RD_DATA) && (cnt_bit==4'd7) && (cnt_i2c_clk==2'd3))
rd_data<=rd_data_reg ;
else
rd_data<=rd_data ;
end
endmodule