第49讲:基于I2C协议的EEPROM驱动控制
理论部分
I2C通讯协议(Inter-Integrated Circuit)是由Philips公司开发的一种简单、双向二线制同步串行总线,只需要两根线即可在连接于总线上的器件之间传送信息。
I2C通讯协议和通信接口在很多工程中有广泛的应用,如数据采集领域的串行AD,图像处理领域的摄像头配置,工业控制领域的X射线管配置等等。除此之外,由于I2C协议占用引脚特别少,硬件实现简单,可扩展性强,现在被广泛地使用在系统内多个集成电路(IC)间的通讯。
设计与实现
实验目标:01-10的写入和读取
i2c_ctrl
`timescale 1ns/1ns
module i2c_ctrl
#(
parameter DEVICE_ADDR = 7'b1010_000 , //i2c设备地址
parameter SYS_CLK_FREQ = 26'd50_000_000 , //输入系统时钟频率
parameter SCL_FREQ = 18'd250_000 //i2c设备scl时钟频率
)
(
input wire sys_clk , //输入系统时钟,50MHz
input wire sys_rst_n , //输入复位信号,低电平有效
input wire wr_en , //输入写使能信号
input wire rd_en , //输入读使能信号
input wire i2c_start , //输入i2c触发信号
input wire addr_num , //输入i2c字节地址字节数
input wire [15:0] byte_addr , //输入i2c字节地址
input wire [7:0] wr_data , //输入i2c设备数据
output reg i2c_clk , //i2c驱动时钟
output reg i2c_end , //i2c一次读/写操作完成
output reg [7:0] rd_data , //输出i2c设备读取数据
output reg i2c_scl , //输出至i2c设备的串行时钟信号scl
inout wire i2c_sda //输出至i2c设备的串行数据信号sda
);
// parameter define
parameter CNT_CLK_MAX = (SYS_CLK_FREQ/SCL_FREQ) >> 2'd3 ; //cnt_clk计数器计数最大值
parameter CNT_START_MAX = 8'd100; //cnt_start计数器计数最大值
parameter IDLE = 4'd00, //初始状态
START_1 = 4'd01, //开始状态1
SEND_D_ADDR = 4'd02, //设备地址写入状态 + 控制写
ACK_1 = 4'd03, //应答状态1
SEND_B_ADDR_H = 4'd04, //字节地址高八位写入状态
ACK_2 = 4'd05, //应答状态2
SEND_B_ADDR_L = 4'd06, //字节地址低八位写入状态
ACK_3 = 4'd07, //应答状态3
WR_DATA = 4'd08, //写数据状态
ACK_4 = 4'd09, //应答状态4
START_2 = 4'd10, //开始状态2
SEND_RD_ADDR = 4'd11, //设备地址写入状态 + 控制读
ACK_5 = 4'd12, //应答状态5
RD_DATA = 4'd13, //读数据状态
N_ACK = 4'd14, //非应答状态
STOP = 4'd15; //结束状态
// wire define
wire sda_in ; //sda输入数据寄存
wire sda_en ; //sda数据写入使能信号
// reg define
reg [7:0] cnt_clk ; //系统时钟计数器,控制生成clk_i2c时钟信号
reg [3:0] state ; //状态机状态
reg cnt_i2c_clk_en ; //cnt_i2c_clk计数器使能信号
reg [1:0] cnt_i2c_clk ; //clk_i2c时钟计数器,控制生成cnt_bit信号
reg [2:0] cnt_bit ; //sda比特计数器
reg ack ; //应答信号
reg i2c_sda_reg ; //sda数据缓存
reg [7:0] rd_data_reg ; //自i2c设备读出数据
// cnt_clk:系统时钟计数器,控制生成clk_i2c时钟信号
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'b1)
cnt_clk <= 8'd0;
else
cnt_clk <= cnt_clk + 1'b1;
// i2c_clk:i2c驱动时钟
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'b1)
i2c_clk <= ~i2c_clk;
// cnt_i2c_clk_en:cnt_i2c_clk计数器使能信号
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 == 3))
cnt_i2c_clk_en <= 1'b0;
else if(i2c_start == 1'b1)
cnt_i2c_clk_en <= 1'b1;
// cnt_i2c_clk:i2c_clk时钟计数器,控制生成cnt_bit信号
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;
// cnt_bit:sda比特计数器
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_1) || (state == START_2)
|| (state == ACK_1) || (state == ACK_2) || (state == ACK_3)
|| (state == ACK_4) || (state == ACK_5) || (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;
// state:状态机状态跳转
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_1;
else
state <= state;
START_1:
if(cnt_i2c_clk == 3)
state <= SEND_D_ADDR;
else
state <= state;
SEND_D_ADDR:
if((cnt_bit == 3'd7) &&(cnt_i2c_clk == 3))
state <= ACK_1;
else
state <= state;
ACK_1:
if((cnt_i2c_clk == 3) && (ack == 1'b0))
begin
if(addr_num == 1'b1)
state <= SEND_B_ADDR_H;
else
state <= SEND_B_ADDR_L;
end
else
state <= state;
SEND_B_ADDR_H:
if((cnt_bit == 3'd7) &&(cnt_i2c_clk == 3))
state <= ACK_2;
else
state <= state;
ACK_2:
if((cnt_i2c_clk == 3) && (ack == 1'b0))
state <= SEND_B_ADDR_L;
else
state <= state;
SEND_B_ADDR_L:
if((cnt_bit == 3'd7) && (cnt_i2c_clk == 3))
state <= ACK_3;
else
state <= state;
ACK_3:
if((cnt_i2c_clk == 3) && (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 == 3))
state <= ACK_4;
else
state <= state;
ACK_4:
if((cnt_i2c_clk == 3) && (ack == 1'b0))
state <= STOP;
else
state <= state;
START_2:
if(cnt_i2c_clk == 3)
state <= SEND_RD_ADDR;
else
state <= state;
SEND_RD_ADDR:
if((cnt_bit == 3'd7) &&(cnt_i2c_clk == 3))
state <= ACK_5;
else
state <= state;
ACK_5:
if((cnt_i2c_clk == 3) && (ack == 1'b0))
state <= RD_DATA;
else
state <= state;
RD_DATA:
if((cnt_bit == 3'd7) &&(cnt_i2c_clk == 3))
state <= N_ACK;
else
state <= state;
N_ACK:
if(cnt_i2c_clk == 3)
state <= STOP;
else
state <= state;
STOP:
if((cnt_bit == 3'd3) &&(cnt_i2c_clk == 3))
state <= IDLE;
else
state <= state;
default: state <= IDLE;
endcase
// ack:应答信号
always@(*)
case (state)
IDLE,START_1,SEND_D_ADDR,SEND_B_ADDR_H,SEND_B_ADDR_L,
WR_DATA,START_2,SEND_RD_ADDR,RD_DATA,N_ACK:
ack <= 1'b1;
ACK_1,ACK_2,ACK_3,ACK_4,ACK_5:
if(cnt_i2c_clk == 2'd0)
ack <= sda_in;
else
ack <= ack;
default: ack <= 1'b1;
endcase
// i2c_scl:输出至i2c设备的串行时钟信号scl
always@(*)
case (state)
IDLE:
i2c_scl <= 1'b1;
START_1:
if(cnt_i2c_clk == 2'd3)
i2c_scl <= 1'b0;
else
i2c_scl <= 1'b1;
SEND_D_ADDR,ACK_1,SEND_B_ADDR_H,ACK_2,SEND_B_ADDR_L,
ACK_3,WR_DATA,ACK_4,START_2,SEND_RD_ADDR,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
// i2c_sda_reg:sda数据缓存
always@(*)
case (state)
IDLE:
begin
i2c_sda_reg <= 1'b1;
rd_data_reg <= 8'd0;
end
START_1:
if(cnt_i2c_clk <= 2'd0)
i2c_sda_reg <= 1'b1;
else
i2c_sda_reg <= 1'b0;
SEND_D_ADDR:
if(cnt_bit <= 3'd6)
i2c_sda_reg <= DEVICE_ADDR[6 - cnt_bit];
else
i2c_sda_reg <= 1'b0;
ACK_1:
i2c_sda_reg <= 1'b1;
SEND_B_ADDR_H:
i2c_sda_reg <= byte_addr[15 - cnt_bit];
ACK_2:
i2c_sda_reg <= 1'b1;
SEND_B_ADDR_L:
i2c_sda_reg <= byte_addr[7 - cnt_bit];
ACK_3:
i2c_sda_reg <= 1'b1;
WR_DATA:
i2c_sda_reg <= wr_data[7 - cnt_bit];
ACK_4:
i2c_sda_reg <= 1'b1;
START_2:
if(cnt_i2c_clk <= 2'd1)
i2c_sda_reg <= 1'b1;
else
i2c_sda_reg <= 1'b0;
SEND_RD_ADDR:
if(cnt_bit <= 3'd6)
i2c_sda_reg <= DEVICE_ADDR[6 - cnt_bit];
else
i2c_sda_reg <= 1'b1;
ACK_5:
i2c_sda_reg <= 1'b1;
RD_DATA:
if(cnt_i2c_clk == 2'd2)
rd_data_reg[7 - cnt_bit] <= sda_in;
else
rd_data_reg <= rd_data_reg;
N_ACK:
i2c_sda_reg <= 1'b1;
STOP:
if((cnt_bit == 3'd0) && (cnt_i2c_clk < 2'd3))
i2c_sda_reg <= 1'b0;
else
i2c_sda_reg <= 1'b1;
default:
begin
i2c_sda_reg <= 1'b1;
rd_data_reg <= rd_data_reg;
end
endcase
// rd_data:自i2c设备读出数据
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;
// i2c_end:一次读/写结束信号
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 == 3))
i2c_end <= 1'b1;
else
i2c_end <= 1'b0;
// sda_in:sda输入数据寄存
assign sda_in = i2c_sda;
// sda_en:sda数据写入使能信号
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;
// i2c_sda:输出至i2c设备的串行数据信号sda
assign i2c_sda = (sda_en == 1'b1) ? i2c_sda_reg : 1'bz;
endmodule
i2c_rw_data
`timescale 1ns/1ns
module i2c_rw_data
(
input wire sys_clk , //输入系统时钟,频率50MHz
input wire i2c_clk , //输入i2c驱动时钟,频率1MHz
input wire sys_rst_n , //输入复位信号,低有效
input wire write , //输入写触发信号
input wire read , //输入读触发信号
input wire i2c_end , //一次i2c读/写结束信号
input wire [7:0] rd_data , //输入自i2c设备读出的数据
output reg wr_en , //输出写使能信号
output reg rd_en , //输出读使能信号
output reg i2c_start , //输出i2c读/写触发信号
output reg [15:0] byte_addr , //输出i2c设备读/写地址
output reg [7:0] wr_data , //输出写入i2c设备的数据
output wire [7:0] fifo_rd_data //输出自fifo中读出的数据
);
// parameter define
parameter DATA_NUM = 8'd10 , //读/写操作读出或写入的数据个数
CNT_START_MAX = 16'd4000 , //cnt_start计数器计数最大值
CNT_WR_RD_MAX = 8'd200 , //cnt_wr/cnt_rd计数器计数最大值
CNT_WAIT_MAX = 28'd500_000 ; //cnt_wait计数器计数最大值
// wire define
wire [7:0] data_num ; //fifo中数据个数
// reg define
reg [7:0] cnt_wr ; //写触发有效信号保持时间计数器
reg write_valid ; //写触发有效信号
reg [7:0] cnt_rd ; //读触发有效信号保持时间计数器
reg read_valid ; //读触发有效信号
reg [15:0] cnt_start ; //单字节数据读/写时间间隔计数
reg [7:0] wr_i2c_data_num ; //写入i2c设备的数据个数
reg [7:0] rd_i2c_data_num ; //读出i2c设备的数据个数
reg fifo_rd_valid ; //fifo读有效信号
reg [27:0] cnt_wait ; //fifo读使能信号间时间间隔计数
reg fifo_rd_en ; //fifo读使能信号
reg [7:0] rd_data_num ; //读出fifo数据个数
//cnt_wr:写触发有效信号保持时间计数器,计数写触发有效信号保持时钟周期数
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_wr <= 8'd0;
else if(write_valid == 1'b0)
cnt_wr <= 8'd0;
else if(write_valid == 1'b1)
cnt_wr <= cnt_wr + 1'b1;
//write_valid:写触发有效信号
//由于写触发信号保持时间为一个系统时钟周期(20ns),
//不能被i2c驱动时钟i2c_scl正确采集,延长写触发信号生成写触发有效信号
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
write_valid <= 1'b0;
else if(cnt_wr == (CNT_WR_RD_MAX - 1'b1))
write_valid <= 1'b0;
else if(write == 1'b1)
write_valid <= 1'b1;
//cnt_rd:读触发有效信号保持时间计数器,计数读触发有效信号保持时钟周期数
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_rd <= 8'd0;
else if(read_valid == 1'b0)
cnt_rd <= 8'd0;
else if(read_valid == 1'b1)
cnt_rd <= cnt_rd + 1'b1;
//read_valid:读触发有效信号
//由于读触发信号保持时间为一个系统时钟周期(20ns),
//不能被i2c驱动时钟i2c_scl正确采集,延长读触发信号生成读触发有效信号
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
read_valid <= 1'b0;
else if(cnt_rd == (CNT_WR_RD_MAX - 1'b1))
read_valid <= 1'b0;
else if(read == 1'b1)
read_valid <= 1'b1;
//cnt_start:单字节数据读/写操作时间间隔计数
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;
//i2c_start:i2c读/写触发信号
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;
//wr_en:输出写使能信号
always@(posedge i2c_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
wr_en <= 1'b0;
else if((wr_i2c_data_num == DATA_NUM - 1)
&& (i2c_end == 1'b1) && (wr_en == 1'b1))
wr_en <= 1'b0;
else if(write_valid == 1'b1)
wr_en <= 1'b1;
//wr_i2c_data_num:写入i2c设备的数据个数
always@(posedge i2c_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
wr_i2c_data_num <= 8'd0;
else if(wr_en == 1'b0)
wr_i2c_data_num <= 8'd0;
else if((wr_en == 1'b1) && (i2c_end == 1'b1))
wr_i2c_data_num <= wr_i2c_data_num + 1'b1;
//rd_en:输出读使能信号
always@(posedge i2c_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
rd_en <= 1'b0;
else if((rd_i2c_data_num == DATA_NUM - 1)
&& (i2c_end == 1'b1) && (rd_en == 1'b1))
rd_en <= 1'b0;
else if(read_valid == 1'b1)
rd_en <= 1'b1;
//rd_i2c_data_num:写入i2c设备的数据个数
always@(posedge i2c_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
rd_i2c_data_num <= 8'd0;
else if(rd_en == 1'b0)
rd_i2c_data_num <= 8'd0;
else if((rd_en == 1'b1) && (i2c_end == 1'b1))
rd_i2c_data_num <= rd_i2c_data_num + 1'b1;
//byte_addr:输出读/写地址
always@(posedge i2c_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
byte_addr <= 16'h00_5A;
else if((wr_en == 1'b0) && (rd_en == 1'b0))
byte_addr <= 16'h00_5A;
else if(((wr_en == 1'b1) || (rd_en == 1'b1)) && (i2c_end == 1'b1))
byte_addr <= byte_addr + 1'b1;
//wr_data:输出待写入i2c设备数据
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_rd_valid:fifo读有效信号
always@(posedge i2c_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
fifo_rd_valid <= 1'b0;
else if((rd_data_num == DATA_NUM)
&& (cnt_wait == (CNT_WAIT_MAX - 1'b1)))
fifo_rd_valid <= 1'b0;
else if(data_num == DATA_NUM)
fifo_rd_valid <= 1'b1;
//cnt_wait:fifo读使能信号间时间间隔计数,计数两fifo读使能间的时间间隔
always@(posedge i2c_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_wait <= 28'd0;
else if(fifo_rd_valid == 1'b0)
cnt_wait <= 28'd0;
else if(cnt_wait == (CNT_WAIT_MAX - 1'b1))
cnt_wait <= 28'd0;
else if(fifo_rd_valid == 1'b1)
cnt_wait <= cnt_wait + 1'b1;
//fifo_rd_en:fifo读使能信号
always@(posedge i2c_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
fifo_rd_en <= 1'b0;
else if((cnt_wait == (CNT_WAIT_MAX - 1'b1))
&& (rd_data_num < DATA_NUM))
fifo_rd_en <= 1'b1;
else
fifo_rd_en <= 1'b0;
//rd_data_num:自fifo中读出数据个数计数
always@(posedge i2c_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
rd_data_num <= 8'd0;
else if(fifo_rd_valid == 1'b0)
rd_data_num <= 8'd0;
else if(fifo_rd_en == 1'b1)
rd_data_num <= rd_data_num + 1'b1;
//------------- fifo_read_inst -------------
fifo_data fifo_read_inst
(
.clock (i2c_clk ), //输入时钟信号,频率1MHz,1bit
.data (rd_data ), //输入写入数据,1bit
.rdreq (fifo_rd_en ), //输入数据读请求,1bit
.wrreq (i2c_end && rd_en ), //输入数据写请求,1bit
.q (fifo_rd_data ), //输出读出数据,1bit
.usedw (data_num ) //输出fifo内数据个数,1bit
);
endmodule
eeprom_byte_rd_wr
`timescale 1ns/1ns
module eeprom_byte_rd_wr
(
input wire sys_clk , //输入工作时钟,频率50MHz
input wire sys_rst_n , //输入复位信号,低电平有效
input wire key_wr , //按键写
input wire key_rd , //按键读
inout wire sda , //串行数据
output wire scl , //串行时钟
output wire stcp , //输出数据存储器时钟
output wire shcp , //移位寄存器的时钟输入
output wire ds , //串行数据输入
output wire oe //使能信号
);
//wire define
wire read ; //读数据
wire write ; //写数据
wire [7:0] po_data ; //fifo输出数据
wire [7:0] rd_data ; //eeprom读出数据
wire wr_en ;
wire rd_en ;
wire i2c_end ;
wire i2c_start ;
wire [7:0] wr_data ;
wire [15:0] byte_addr ;
wire i2c_clk ;
//------------- key_wr_inst -------------
key_filter key_wr_inst
(
.sys_clk (sys_clk ), //系统时钟50Mhz
.sys_rst_n (sys_rst_n ), //全局复位
.key_in (key_wr ), //按键输入信号
.key_flag (write ) //key_flag为1时表示按键有效,0表示按键无效
);
//------------- key_rd_inst -------------
key_filter key_rd_inst
(
.sys_clk (sys_clk ), //系统时钟50Mhz
.sys_rst_n (sys_rst_n ), //全局复位
.key_in (key_rd ), //按键输入信号
.key_flag (read ) //key_flag为1时表示按键有效,0表示按键无效
);
//------------- i2c_rw_data_inst -------------
i2c_rw_data i2c_rw_data_inst
(
.sys_clk (sys_clk ), //输入系统时钟,频率50MHz
.i2c_clk (i2c_clk ), //输入i2c驱动时钟,频率1MHz
.sys_rst_n (sys_rst_n ), //输入复位信号,低有效
.write (write ), //输入写触发信号
.read (read ), //输入读触发信号
.i2c_end (i2c_end ), //一次i2c读/写结束信号
.rd_data (rd_data ), //输入自i2c设备读出的数据
.wr_en (wr_en ), //输出写使能信号
.rd_en (rd_en ), //输出读使能信号
.i2c_start (i2c_start ), //输出i2c读/写触发信号
.byte_addr (byte_addr ), //输出i2c设备读/写地址
.wr_data (wr_data ), //输出写入i2c设备的数据
.fifo_rd_data(po_data ) //输出自fifo中读出的数据
);
//------------- i2c_ctrl_inst -------------
i2c_ctrl
#(
.DEVICE_ADDR (7'b1010_011 ), //i2c设备器件地址
.SYS_CLK_FREQ (26'd50_000_000 ), //i2c_ctrl模块系统时钟频率
.SCL_FREQ (18'd250_000 ) //i2c的SCL时钟频率
)
i2c_ctrl_inst
(
.sys_clk (sys_clk ), //输入系统时钟,50MHz
.sys_rst_n (sys_rst_n ), //输入复位信号,低电平有效
.wr_en (wr_en ), //输入写使能信号
.rd_en (rd_en ), //输入读使能信号
.i2c_start (i2c_start ), //输入i2c触发信号
.addr_num (1'b1 ), //输入i2c字节地址字节数
.byte_addr (byte_addr ), //输入i2c字节地址
.wr_data (wr_data ), //输入i2c设备数据
.rd_data (rd_data ), //输出i2c设备读取数据
.i2c_end (i2c_end ), //i2c一次读/写操作完成
.i2c_clk (i2c_clk ), //i2c驱动时钟
.i2c_scl (scl ), //输出至i2c设备的串行时钟信号scl
.i2c_sda (sda ) //输出至i2c设备的串行数据信号sda
);
//------------- seg7_dynamic_inst -------------
seg_595_dynamic seg_595_dynamic_inst
(
.sys_clk (sys_clk ), //系统时钟,频率50MHz
.sys_rst_n (sys_rst_n ), //复位信号,低有效
.data (po_data ), //数码管要显示的值
.point ( ), //小数点显示,高电平有效
.seg_en (1'b1 ), //数码管使能信号,高电平有效
.sign ( ), //符号位,高电平显示负号
.stcp (stcp ), //数据存储器时钟
.shcp (shcp ), //移位寄存器时钟
.ds (ds ), //串行数据输入
.oe (oe ) //使能信号
);
endmodule
tb_eeprom_byte_rd_wr
`timescale 1ns/1ns
module tb_eeprom_byte_rd_wr();
//wire define
wire scl ;
wire sda ;
wire stcp;
wire shcp;
wire ds ;
wire oe ;
//reg define
reg clk ;
reg rst_n ;
reg key_wr;
reg key_rd;
//时钟、复位信号
initial
begin
clk = 1'b1 ;
rst_n <= 1'b0 ;
key_wr <= 1'b1 ;
key_rd <= 1'b1 ;
#200
rst_n <= 1'b1 ;
#1000
key_wr <= 1'b0 ;
key_rd <= 1'b1 ;
#400
key_wr <= 1'b1 ;
key_rd <= 1'b1 ;
#20000000
key_wr <= 1'b1 ;
key_rd <= 1'b0 ;
#400
key_wr <= 1'b1 ;
key_rd <= 1'b1 ;
#40000000
$stop;
end
always #10 clk = ~clk;
defparam eeprom_byte_rd_wr_inst.key_wr_inst.CNT_MAX = 5;
defparam eeprom_byte_rd_wr_inst.key_rd_inst.CNT_MAX = 5;
defparam eeprom_byte_rd_wr_inst.i2c_rw_data_inst.CNT_WAIT_MAX = 1000;
//-------------eeprom_byte_rd_wr_inst-------------
eeprom_byte_rd_wr eeprom_byte_rd_wr_inst
(
.sys_clk (clk ), //输入工作时钟,频率50MHz
.sys_rst_n (rst_n ), //输入复位信号,低电平有效
.key_wr (key_wr ), //按键写
.key_rd (key_rd ), //按键读
.sda (sda ), //串行数据
.scl (scl ), //串行时钟
.stcp (stcp ), //输出数据存储寄时钟
.shcp (shcp ), //移位寄存器的时钟输入
.ds (ds ), //串行数据输入
.oe (oe )
);
//-------------eeprom_inst-------------
M24LC64 M24lc64_inst
(
.A0 (1'b0 ), //器件地址
.A1 (1'b0 ), //器件地址
.A2 (1'b0 ), //器件地址
.WP (1'b0 ), //写保护信号,高电平有效
.RESET (~rst_n ), //复位信号,高电平有效
.SDA (sda ), //串行数据
.SCL (scl ) //串行时钟
);
endmodule