代码参考正点原子
i2c_dri:主要是三段式状态机的编写
module iic_dri
#(
parameter SLAVE_ADDR = 7'b1010000 , //EEPROM从机地址
parameter CLK_FREQ = 26'd50_000_000, //模块输入的时钟频率
parameter I2C_FREQ = 18'd250_000 //IIC_SCL的时钟频率
)
(
input clk ,
input rst_n ,
//i2c interface
input i2c_exec , //I2C触发执行信号
input bit_ctrl , //字地址位控制(16b/8b),1代表字地址是16位的,0是8位的
input i2c_rh_wl , //I2C读写控制信号 ,1代表读数据,0代表写数据
input [15:0] i2c_addr , //I2C器件内地址
input [ 7:0] i2c_data_w , //I2C要写的数据
output reg [ 7:0] i2c_data_r , //I2C读出的数据
output reg i2c_done , //I2C一次操作完成
output reg i2c_ack , //I2C应答标志 0:应答 1:未应答
output reg scl , //I2C的SCL时钟信号
inout sda , //I2C的SDA信号
//user interface
output reg dri_clk //驱动I2C操作的驱动时钟
);
//localparam define
localparam st_idle = 8'b0000_0001; //空闲状态
localparam st_sladdr = 8'b0000_0010; //发送器件地址(slave address)
localparam st_addr16 = 8'b0000_0100; //发送16位字地址
localparam st_addr8 = 8'b0000_1000; //发送8位字地址
localparam st_data_wr = 8'b0001_0000; //写数据(8 bit)
localparam st_addr_rd = 8'b0010_0000; //发送器件地址读
localparam st_data_rd = 8'b0100_0000; //读数据(8 bit)
localparam st_stop = 8'b1000_0000; //结束I2C操作
//reg define
reg sda_dir ; //I2C数据(SDA)方向控制
reg sda_out ; //SDA输出信号
reg st_done ; //状态结束
reg wr_flag ; //写标志
reg [ 6:0] cnt ; //计数
reg [ 7:0] cur_state ; //状态机当前状态
reg [ 7:0] next_state; //状态机下一状态
reg [15:0] addr_t ; //地址
reg [ 7:0] data_r ; //读取的数据
reg [ 7:0] data_wr_t ; //I2C需写的数据的临时寄存
reg [ 9:0] clk_cnt ; //分频时钟计数
//wire define
wire sda_in ; //SDA输入信号
wire [8:0] clk_divide ; //模块驱动时钟的分频系数
//*****************************************************
//** main code
//*****************************************************
//SDA控制
assign sda = sda_dir ? sda_out : 1'bz; //SDA数据输出或高阻
assign sda_in = sda ; //SDA数据输入
assign clk_divide = (CLK_FREQ/I2C_FREQ) >> 2'd2;//模块驱动时钟的分频系数
//生成I2C的SCL的四倍频率的驱动时钟用于驱动i2c的操作
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
dri_clk <= 1'b0;
clk_cnt <= 10'd0;
end
else if(clk_cnt == clk_divide[8:1] - 1'd1) begin
clk_cnt <= 10'd0;
dri_clk <= ~dri_clk;
end
else
clk_cnt <= clk_cnt + 1'b1;
end
//(三段式状态机)同步时序描述状态转移
always @(posedge dri_clk or negedge rst_n) begin
if(!rst_n)
cur_state <= st_idle;
else
cur_state <= next_state;
end
//组合逻辑判断状态转移条件
always @(*) begin
next_state = st_idle; //清除锁存器
case(cur_state)
st_idle: begin //空闲状态
if(i2c_exec) begin
next_state = st_sladdr; //器件地址
end
else
next_state = st_idle;
end
st_sladdr: begin
if(st_done) begin
if(bit_ctrl) //判断是16位还是8位字地址
next_state = st_addr16; //字地址的高8位
else
next_state = st_addr8 ; //字地址的低8位
end
else
next_state = st_sladdr;
end
st_addr16: begin //写16位字地址
if(st_done) begin
next_state = st_addr8;
end
else begin
next_state = st_addr16;
end
end
st_addr8: begin //8位字地址
if(st_done) begin
if(wr_flag==1'b0) //读写判断
next_state = st_data_wr;
else
next_state = st_addr_rd;
end
else begin
next_state = st_addr8;
end
end
st_data_wr: begin //写数据(8 bit)
if(st_done)
next_state = st_stop;
else
next_state = st_data_wr;
end
st_addr_rd: begin //写地址以进行读数据
if(st_done) begin
next_state = st_data_rd;
end
else begin
next_state = st_addr_rd;
end
end
st_data_rd: begin //读取数据(8 bit)
if(st_done)
next_state = st_stop;
else
next_state = st_data_rd;
end
st_stop: begin //结束I2C操作
if(st_done)
next_state = st_idle;
else
next_state = st_stop ;
end
default: next_state= st_idle;
endcase
end
//时序电路描述状态输出
always @(posedge dri_clk or negedge rst_n) begin
//复位初始化
if(!rst_n) begin
scl <= 1'b1;
sda_out <= 1'b1;
sda_dir <= 1'b1;
i2c_done <= 1'b0;
i2c_ack <= 1'b0;
cnt <= 1'b0;
st_done <= 1'b0;
data_r <= 1'b0;
i2c_data_r<= 1'b0;
wr_flag <= 1'b0;
addr_t <= 1'b0;
data_wr_t <= 1'b0;
end
else begin
st_done <= 1'b0 ;
cnt <= cnt +1'b1 ;
case(cur_state)
st_idle: begin //空闲状态
scl <= 1'b1;
sda_out <= 1'b1; //当sda_dir为1时,sda_out就是sda
sda_dir <= 1'b1;
i2c_done<= 1'b0;
cnt <= 7'b0;
if(i2c_exec) begin
wr_flag <= i2c_rh_wl ;
addr_t <= i2c_addr ;
data_wr_t <= i2c_data_w;
i2c_ack <= 1'b0;
end
end
st_sladdr: begin //写地址(器件地址和字地址)
case(cnt)
7'd1 : sda_out <= 1'b0; //开始I2C 起始信号
7'd3 : scl <= 1'b0; //scl在3、5、7、9...时0 1 0 1变化,四分频
7'd4 : sda_out <= SLAVE_ADDR[6]; //传送器件地址
7'd5 : scl <= 1'b1;
7'd7 : scl <= 1'b0;
7'd8 : sda_out <= SLAVE_ADDR[5];
7'd9 : scl <= 1'b1;
7'd11: scl <= 1'b0;
7'd12: sda_out <= SLAVE_ADDR[4];
7'd13: scl <= 1'b1;
7'd15: scl <= 1'b0;
7'd16: sda_out <= SLAVE_ADDR[3];
7'd17: scl <= 1'b1;
7'd19: scl <= 1'b0;
7'd20: sda_out <= SLAVE_ADDR[2];
7'd21: scl <= 1'b1;
7'd23: scl <= 1'b0;
7'd24: sda_out <= SLAVE_ADDR[1];
7'd25: scl <= 1'b1;
7'd27: scl <= 1'b0;
7'd28: sda_out <= SLAVE_ADDR[0];
7'd29: scl <= 1'b1;
7'd31: scl <= 1'b0;
7'd32: sda_out <= 1'b0; //0:写 表示接着写
7'd33: scl <= 1'b1;
7'd35: scl <= 1'b0;
7'd36: begin
sda_dir <= 1'b0; //拉低释放总线
sda_out <= 1'b1;
end
7'd37: scl <= 1'b1;
7'd38: begin //从机应答
st_done <= 1'b1;
if(sda_in == 1'b1) //高电平表示未应答 0表示有效应答
i2c_ack <= 1'b1; //拉高应答标志位
end
7'd39: begin
scl <= 1'b0;
cnt <= 1'b0;
end
default : ;
endcase
end
st_addr16: begin
case(cnt)
7'd0 : begin
sda_dir <= 1'b1 ;
sda_out <= addr_t[15]; //传送字地址i2c_addr
end
7'd1 : scl <= 1'b1; //四分频
7'd3 : scl <= 1'b0;
7'd4 : sda_out <= addr_t[14];
7'd5 : scl <= 1'b1;
7'd7 : scl <= 1'b0;
7'd8 : sda_out <= addr_t[13];
7'd9 : scl <= 1'b1;
7'd11: scl <= 1'b0;
7'd12: sda_out <= addr_t[12];
7'd13: scl <= 1'b1;
7'd15: scl <= 1'b0;
7'd16: sda_out <= addr_t[11];
7'd17: scl <= 1'b1;
7'd19: scl <= 1'b0;
7'd20: sda_out <= addr_t[10];
7'd21: scl <= 1'b1;
7'd23: scl <= 1'b0;
7'd24: sda_out <= addr_t[9];
7'd25: scl <= 1'b1;
7'd27: scl <= 1'b0;
7'd28: sda_out <= addr_t[8];
7'd29: scl <= 1'b1;
7'd31: scl <= 1'b0;
7'd32: begin
sda_dir <= 1'b0;
sda_out <= 1'b1;
end
7'd33: scl <= 1'b1;
7'd34: begin //从机应答
st_done <= 1'b1;
if(sda_in == 1'b1) //高电平表示未应答
i2c_ack <= 1'b1; //拉高应答标志位
end
7'd35: begin
scl <= 1'b0;
cnt <= 1'b0;
end
default : ;
endcase
end
st_addr8: begin
case(cnt)
7'd0: begin
sda_dir <= 1'b1 ;
sda_out <= addr_t[7]; //字地址
end
7'd1 : scl <= 1'b1;
7'd3 : scl <= 1'b0;
7'd4 : sda_out <= addr_t[6]; //
7'd5 : scl <= 1'b1;
7'd7 : scl <= 1'b0;
7'd8 : sda_out <= addr_t[5];
7'd9 : scl <= 1'b1;
7'd11: scl <= 1'b0;
7'd12: sda_out <= addr_t[4];
7'd13: scl <= 1'b1;
7'd15: scl <= 1'b0;
7'd16: sda_out <= addr_t[3];
7'd17: scl <= 1'b1;
7'd19: scl <= 1'b0;
7'd20: sda_out <= addr_t[2];
7'd21: scl <= 1'b1;
7'd23: scl <= 1'b0;
7'd24: sda_out <= addr_t[1];
7'd25: scl <= 1'b1;
7'd27: scl <= 1'b0;
7'd28: sda_out <= addr_t[0];
7'd29: scl <= 1'b1;
7'd31: scl <= 1'b0;
7'd32: begin
sda_dir <= 1'b0;
sda_out <= 1'b1;
end
7'd33: scl <= 1'b1;
7'd34: begin //从机应答
st_done <= 1'b1;
if(sda_in == 1'b1) //高电平表示未应答
i2c_ack <= 1'b1; //拉高应答标志位
end
7'd35: begin
scl <= 1'b0;
cnt <= 1'b0;
end
default : ;
endcase
end
st_data_wr: begin //写数据(8 bit)
case(cnt)
7'd0: begin
sda_out <= data_wr_t[7]; //I2C写8位数据 data_wr_t要写的数据
sda_dir <= 1'b1;
end
7'd1 : scl <= 1'b1;
7'd3 : scl <= 1'b0;
7'd4 : sda_out <= data_wr_t[6];
7'd5 : scl <= 1'b1;
7'd7 : scl <= 1'b0;
7'd8 : sda_out <= data_wr_t[5];
7'd9 : scl <= 1'b1;
7'd11: scl <= 1'b0;
7'd12: sda_out <= data_wr_t[4];
7'd13: scl <= 1'b1;
7'd15: scl <= 1'b0;
7'd16: sda_out <= data_wr_t[3];
7'd17: scl <= 1'b1;
7'd19: scl <= 1'b0;
7'd20: sda_out <= data_wr_t[2];
7'd21: scl <= 1'b1;
7'd23: scl <= 1'b0;
7'd24: sda_out <= data_wr_t[1];
7'd25: scl <= 1'b1;
7'd27: scl <= 1'b0;
7'd28: sda_out <= data_wr_t[0];
7'd29: scl <= 1'b1;
7'd31: scl <= 1'b0;
7'd32: begin
sda_dir <= 1'b0;
sda_out <= 1'b1;
end
7'd33: scl <= 1'b1;
7'd34: begin //从机应答
st_done <= 1'b1;
if(sda_in == 1'b1) //高电平表示未应答
i2c_ack <= 1'b1; //拉高应答标志位
end
7'd35: begin
scl <= 1'b0;
cnt <= 1'b0;
end
default : ;
endcase
end
st_addr_rd: begin //写地址以进行读数据
case(cnt)
7'd0 : begin
sda_dir <= 1'b1;
sda_out <= 1'b1;
end
7'd1 : scl <= 1'b1;
7'd2 : sda_out <= 1'b0; //重新开始
7'd3 : scl <= 1'b0;
7'd4 : sda_out <= SLAVE_ADDR[6]; //传送器件地址
7'd5 : scl <= 1'b1;
7'd7 : scl <= 1'b0;
7'd8 : sda_out <= SLAVE_ADDR[5];
7'd9 : scl <= 1'b1;
7'd11: scl <= 1'b0;
7'd12: sda_out <= SLAVE_ADDR[4];
7'd13: scl <= 1'b1;
7'd15: scl <= 1'b0;
7'd16: sda_out <= SLAVE_ADDR[3];
7'd17: scl <= 1'b1;
7'd19: scl <= 1'b0;
7'd20: sda_out <= SLAVE_ADDR[2];
7'd21: scl <= 1'b1;
7'd23: scl <= 1'b0;
7'd24: sda_out <= SLAVE_ADDR[1];
7'd25: scl <= 1'b1;
7'd27: scl <= 1'b0;
7'd28: sda_out <= SLAVE_ADDR[0];
7'd29: scl <= 1'b1;
7'd31: scl <= 1'b0;
7'd32: sda_out <= 1'b1; //1:读
7'd33: scl <= 1'b1;
7'd35: scl <= 1'b0;
7'd36: begin
sda_dir <= 1'b0;
sda_out <= 1'b1;
end
7'd37: scl <= 1'b1;
7'd38: begin //从机应答
st_done <= 1'b1;
if(sda_in == 1'b1) //高电平表示未应答
i2c_ack <= 1'b1; //拉高应答标志位
end
7'd39: begin
scl <= 1'b0;
cnt <= 1'b0;
end
default : ;
endcase
end
st_data_rd: begin //读取数据(8 bit)
case(cnt)
7'd0: sda_dir <= 1'b0;
7'd1: begin
data_r[7] <= sda_in;
scl <= 1'b1;
end
7'd3: scl <= 1'b0;
7'd5: begin
data_r[6] <= sda_in ;
scl <= 1'b1 ;
end
7'd7: scl <= 1'b0;
7'd9: begin
data_r[5] <= sda_in;
scl <= 1'b1 ;
end
7'd11: scl <= 1'b0;
7'd13: begin
data_r[4] <= sda_in;
scl <= 1'b1 ;
end
7'd15: scl <= 1'b0;
7'd17: begin
data_r[3] <= sda_in;
scl <= 1'b1 ;
end
7'd19: scl <= 1'b0;
7'd21: begin
data_r[2] <= sda_in;
scl <= 1'b1 ;
end
7'd23: scl <= 1'b0;
7'd25: begin
data_r[1] <= sda_in;
scl <= 1'b1 ;
end
7'd27: scl <= 1'b0;
7'd29: begin
data_r[0] <= sda_in;
scl <= 1'b1 ;
end
7'd31: scl <= 1'b0;
7'd32: begin
sda_dir <= 1'b1;
sda_out <= 1'b1;
end
7'd33: scl <= 1'b1;
7'd34: st_done <= 1'b1; //非应答
7'd35: begin
scl <= 1'b0;
cnt <= 1'b0;
i2c_data_r <= data_r;
end
default : ;
endcase
end
st_stop: begin //结束I2C操作
case(cnt)
7'd0: begin
sda_dir <= 1'b1; //结束I2C
sda_out <= 1'b0;
end
7'd1 : scl <= 1'b1;
7'd3 : sda_out <= 1'b1;
7'd15: st_done <= 1'b1;
7'd16: begin
cnt <= 1'b0;
i2c_done <= 1'b1; //向上层模块传递I2C结束信号
end
default : ;
endcase
end
endcase
end
end
EEPROM读写测试模块:
module e2prom_rw(
input clk ,
input rst_n ,
output reg i2c_rh_wl,
output reg i2c_exec,
output reg [15:0] i2c_addr, //i2c器件内地址
output reg [7:0] i2c_data_w,
input [7:0] i2c_data_r,
input i2c_done,
input i2c_ack,
output reg rw_done, //E2PROM读写测试完成
output reg rw_result //E2PROM读写测试结果 0:失败 1:成功
);
//parameter define
//EEPROM写数据需要添加间隔时间,读数据则不需要
parameter WR_WAIT_TIME = 14'd50000; //5ms写入数据的间隔时间为5ms
parameter MAX_BYTE = 16'd256; //读写测试的字节个数
//reg define
reg [1:0] flow_cnt; //状态流控制
reg [13:0] wait_cnt; //延时计数器
//EEPROM读写测试,先写后读,并比较读出的值与输入的值是否一致
always @(posedge clk or negedge rst_n)begin
if(!rst_n) begin
flow_cnt <= 2'b0;
i2c_rh_wl <= 1'b0;
i2c_exec <= 1'b0;
i2c_addr <= 16'b0;
i2c_data_w <= 8'b0;
wait_cnt <= 14'b0;
rw_done <= 1'b0;
rw_result <= 1'b0;
end
else begin
i2c_exec <= 1'b0;
rw_done <= 1'b0;
case(flow_cnt)
2'd0 : begin
wait_cnt <= wait_cnt + 1'b1;
if(wait_cnt == (WR_WAIT_TIME - 1'b1))begin
wait_cnt <= 14'b0;
if(i2c_addr == MAX_BYTE)begin
i2c_addr <= 16'b0;
i2c_rh_wl <= 1'b1;
flow_cnt <= 2'd2;
end
else begin
flow_cnt <= flow_cnt + 2'b1;
i2c_exec <= 1'b1; //启动i2c
end
end
end
2'd1 : begin
if(i2c_done == 1'b1)begin
flow_cnt <= 2'd0;
i2c_addr <= i2c_addr + 16'b1; //0-255
i2c_data_w <= i2c_data_w + 8'b1; //0-255
end
end
2'd2 : begin
flow_cnt <= flow_cnt + 2'b1;
i2c_exec <= 1'b1;
end
2'd3 : begin
if(i2c_done == 1'b1)begin
//读出的值错误或者i2c未应答,读写测试失败
if((i2c_addr[7:0] != i2c_data_r) || (i2c_ack == 1'b1)) begin
rw_done <= 1'b1;
rw_result <=1'b0;
end
else if(i2c_addr == (MAX_BYTE - 16'b1))begin //读写测试成功
rw_done <= 1'b1;
rw_result <=1'b1;
end
else begin
flow_cnt <= 2'd2;
i2c_addr <= i2c_addr + 16'b1;
end
end
end
default : ;
endcase
end
读写结果判定:
reg rw_done_flag; //读写测试完成标志
reg [16:0] led_cnt ; //led计数
//*****************************************************
//** main code
//*****************************************************
//读写测试完成标志rw_done_flag
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
rw_done_flag <= 1'b0;
else if(rw_done)
rw_done_flag <= 1'b1;
end
//错误标志为1时PL_LED0闪烁,否则PL_LED0常亮
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
led_cnt <= 17'd0;
led <= 1'b0;
end
else begin
if(rw_done_flag) begin
if(rw_result) //读写测试正确
led <= 1'b1; //led灯常亮
else begin //读写测试错误
led_cnt <= led_cnt + 17'd1;
if(led_cnt == (L_TIME - 17'b1)) begin
led_cnt <= 17'd0;
led <= ~led; //led灯闪烁
end
else
led <= led;
end
end
else
led <= 1'b0; //读写测试完成之前,led灯熄灭
end
end
endmodule
top:
module top_e2prom(
input sys_clk , //系统时钟
input sys_rst_n , //系统复位
//eeprom interface
output iic_scl , //eeprom的时钟线scl
inout iic_sda , //eeprom的数据线sda
//user interface
output led //led显示eeprom读写测试结果
);
//parameter define
parameter SLAVE_ADDR = 7'b1010000 ; //器件地址(SLAVE_ADDR)
parameter BIT_CTRL = 1'b1 ; //字地址位控制参数(16b/8b)
parameter CLK_FREQ = 26'd50_000_000 ; //i2c_dri模块的驱动时钟频率(CLK_FREQ)
parameter I2C_FREQ = 18'd250_000 ; //I2C的SCL时钟频率
parameter L_TIME = 17'd125_000 ; //led闪烁时间参数
parameter MAX_BYTE = 16'd256 ; //读写测试的字节个数
//wire define
wire dri_clk ; //I2C操作时钟
wire i2c_exec ; //I2C触发控制
wire [15:0] i2c_addr ; //I2C操作地址
wire [ 7:0] i2c_data_w; //I2C写入的数据
wire i2c_done ; //I2C操作结束标志
wire i2c_ack ; //I2C应答标志 0:应答 1:未应答
wire i2c_rh_wl ; //I2C读写控制
wire [ 7:0] i2c_data_r; //I2C读出的数据
wire rw_done ; //E2PROM读写测试完成
wire rw_result ; //E2PROM读写测试结果 0:失败 1:成功
//*****************************************************
//** main code
//*****************************************************
//e2prom读写测试模块
e2prom_rw #(
.MAX_BYTE (MAX_BYTE ) //读写测试的字节个数
) u_e2prom_rw(
.clk (dri_clk ), //时钟信号
.rst_n (sys_rst_n ), //复位信号
//i2c interface
.i2c_exec (i2c_exec ), //I2C触发执行信号
.i2c_rh_wl (i2c_rh_wl ), //I2C读写控制信号
.i2c_addr (i2c_addr ), //I2C器件内地址
.i2c_data_w (i2c_data_w), //I2C要写的数据
.i2c_data_r (i2c_data_r), //I2C读出的数据
.i2c_done (i2c_done ), //I2C一次操作完成
.i2c_ack (i2c_ack ), //I2C应答标志
//user interface
.rw_done (rw_done ), //E2PROM读写测试完成
.rw_result (rw_result ) //E2PROM读写测试结果 0:失败 1:成功
);
//i2c驱动模块
iic_dri #(
.SLAVE_ADDR (SLAVE_ADDR), //EEPROM从机地址
.CLK_FREQ (CLK_FREQ ), //模块输入的时钟频率
.I2C_FREQ (I2C_FREQ ) //IIC_SCL的时钟频率
) u_i2c_dri(
.clk (sys_clk ),
.rst_n (sys_rst_n ),
//i2c interface
.i2c_exec (i2c_exec ), //I2C触发执行信号
.bit_ctrl (BIT_CTRL ), //器件地址位控制(16b/8b)
.i2c_rh_wl (i2c_rh_wl ), //I2C读写控制信号
.i2c_addr (i2c_addr ), //I2C器件内地址
.i2c_data_w (i2c_data_w), //I2C要写的数据
.i2c_data_r (i2c_data_r), //I2C读出的数据
.i2c_done (i2c_done ), //I2C一次操作完成
.i2c_ack (i2c_ack ), //I2C应答标志
.scl (iic_scl ), //I2C的SCL时钟信号
.sda (iic_sda ), //I2C的SDA信号
//user interface
.dri_clk (dri_clk ) //I2C操作时钟
);
//led指示模块
rw_result_led #(.L_TIME(L_TIME ) //控制led闪烁时间
) u_rw_result_led(
.clk (dri_clk ),
.rst_n (sys_rst_n ),
.rw_done (rw_done ),
.rw_result (rw_result ),
.led (led )
);
endmodule
tb:时钟激励
`timescale 1ns/1ns //定义仿真时间单位1ns和仿真时间精度为1ns
module tb_e2prom_top;
//parameter define
parameter T = 20 ; //时钟周期为20ns
parameter SLAVE_ADDR = 7'b1010000 ; //器件地址(SLAVE_ADDR)
parameter BIT_CTRL = 1'b1 ; //字地址位控制参数(16b/8b)
parameter CLK_FREQ = 26'd50_000_000 ; //i2c_dri模块的驱动时钟频率(CLK_FREQ),周期就是20纳秒
parameter I2C_FREQ = 18'd250_000 ; //I2C的SCL时钟频率,周期是4us
parameter L_TIME = 17'd1 ; //led闪烁时间参数
parameter MAX_BYTE = 16'd3 ; //读写测试的字节个数
//reg define
reg sys_clk ; //时钟信号
reg sys_rst_n; //复位信号
//wire define
wire iic_scl;
wire iic_sda;
wire led ;
//*****************************************************
//** main code
//*****************************************************
//给输入信号初始值
initial begin
sys_clk = 1'b0;
sys_rst_n = 1'b0; //复位
#(T+1) sys_rst_n = 1'b1; //在第21ns的时候复位信号信号拉高
end
//50Mhz的时钟,周期则为1/50Mhz=20ns,所以每10ns,电平取反一次
always #(T/2) sys_clk = ~sys_clk;
//将SDA数据线上拉
pullup(iic_sda);
//例化e2prom_top模块
top_e2prom #(
.MAX_BYTE (MAX_BYTE ) //读写测试的字节个数
) u_top_e2prom(
.sys_clk (sys_clk ), //系统时钟
.sys_rst_n (sys_rst_n), //系统复位
//eeprom interface
.iic_scl (iic_scl ), //eeprom的时钟线scl
.iic_sda (iic_sda ), //eeprom的数据线sda
//user interface
.led (led ) //led显示
);
//例化e2prom仿真模型
EEPROM_AT24C64 u_EEPROM_AT24C64(
.scl (iic_scl),
.sda (iic_sda)
);
endmodule
上拉电阻:
wire abc;
pullup(abc);
assign abc = enable ? 1’b0 : 1’bz;
enable为1的时候 abc信号为0
enable 为0的时候 assign语句的输出为高阻,但是因为pullup了abc,所以abc的值为1
EEPROM仿真模型:
`timescale 1ns/1ns
`define timeslice 1250
module EEPROM_AT24C64(
scl,
sda
);
input scl;
inout sda;
reg out_flag;
reg[7:0] memory[8191:0];
reg[12:0]address;
reg[7:0]memory_buf;
reg[7:0]sda_buf;
reg[7:0]shift;
reg[7:0]addr_byte_h;
reg[7:0]addr_byte_l;
reg[7:0]ctrl_byte;
reg[1:0]State;
integer i;
//---------------------------
parameter
r7 = 8'b1010_1111, w7 = 8'b1010_1110, //main7
r6 = 8'b1010_1101, w6 = 8'b1010_1100, //main6
r5 = 8'b1010_1011, w5 = 8'b1010_1010, //main5
r4 = 8'b1010_1001, w4 = 8'b1010_1000, //main4
r3 = 8'b1010_0111, w3 = 8'b1010_0110, //main3
r2 = 8'b1010_0101, w2 = 8'b1010_0100, //main2
r1 = 8'b1010_0011, w1 = 8'b1010_0010, //main1
r0 = 8'b1010_0001, w0 = 8'b1010_0000; //main0
assign sda = (out_flag == 1) ? sda_buf[7] : 1'bz;
initial
begin
addr_byte_h = 0;
addr_byte_l = 0;
ctrl_byte = 0;
out_flag = 0;
sda_buf = 0;
State = 2'b00;
memory_buf = 0;
address = 0;
shift = 0;
for(i=0;i<=8191;i=i+1)
memory[i] = 0;
end
always@(negedge sda)
begin
if(scl == 1)
begin
State = State + 1;
if(State == 2'b11)
disable write_to_eeprom;
end
end
always@(posedge sda)
begin
if(scl == 1)
stop_W_R;
else
begin
casex(State)
2'b01:begin
read_in;
if(ctrl_byte == w7 || ctrl_byte == w6
|| ctrl_byte == w5 || ctrl_byte == w4
|| ctrl_byte == w3 || ctrl_byte == w2
|| ctrl_byte == w1 || ctrl_byte == w0)
begin
State = 2'b10;
write_to_eeprom;
end
else
State = 2'b00;
end
2'b11:
read_from_eeprom;
default:
State = 2'b00;
endcase
end
end
task stop_W_R;
begin
State = 2'b00;
addr_byte_h = 0;
addr_byte_l = 0;
ctrl_byte = 0;
out_flag = 0;
sda_buf = 0;
end
endtask
task read_in;
begin
shift_in(ctrl_byte);
shift_in(addr_byte_h);
shift_in(addr_byte_l);
end
endtask
task write_to_eeprom;
begin
shift_in(memory_buf);
address = {addr_byte_h[4:0], addr_byte_l};
memory[address] = memory_buf;
State = 2'b00;
end
endtask
task read_from_eeprom;
begin
shift_in(ctrl_byte);
if(ctrl_byte == r7 || ctrl_byte == w6
|| ctrl_byte == r5 || ctrl_byte == r4
|| ctrl_byte == r3 || ctrl_byte == r2
|| ctrl_byte == r1 || ctrl_byte == r0)
begin
address = {addr_byte_h[4:0], addr_byte_l};
sda_buf = memory[address];
shift_out;
State = 2'b00;
end
end
endtask
task shift_in;
output[7:0]shift;
begin
@(posedge scl) shift[7] = sda;
@(posedge scl) shift[6] = sda;
@(posedge scl) shift[5] = sda;
@(posedge scl) shift[4] = sda;
@(posedge scl) shift[3] = sda;
@(posedge scl) shift[2] = sda;
@(posedge scl) shift[1] = sda;
@(posedge scl) shift[0] = sda;
@(negedge scl)
begin
#(`timeslice);
out_flag = 1;
sda_buf = 0;
end
@(negedge scl)
begin
#(`timeslice-250);
out_flag = 0;
end
end
endtask
task shift_out;
begin
out_flag = 1;
for(i=6; i>=0; i=i-1)
begin
@(negedge scl);
#`timeslice;
sda_buf = sda_buf << 1;
end
@(negedge scl) #`timeslice sda_buf[7] = 1;
@(negedge scl) #`timeslice out_flag = 0;
end
endtask
endmodule
i2c状态机的编写:
读数据:
i2c第一次写入地址0的字节数据0:
i2c第二次写入地址1的字节数据1:
i2c第三次写入地址2的字节数据2:
读数据的时候sda总线由输入变为输出状态,需要给从机一个非应答信号,不能给应答信号!给应答信号就不再是任意地址读,就变成了从某个地址连续读,不符合本次设计的任意地址读模式:
两个毛刺信号:(由EEPROM仿真模型引起的)
上板验证:
胡萝卜鸡...