IIC是一种两线式串行总线,由数据线SDA和时钟线SCL构成通信线路,既可用于发送数据,也可接受数据,是一种半双工通信协议。
总线上的主设备和从设备之间以字节为单位进行双向的数据传输。
多用于主机和从机在数据量不大且传输距离短的场合下的主从通信。主机启动总线,并产生时钟用于传送数据,此时任何接收数据的器件均被认为是从机。I2C器件一般采用开漏结构与总线相连,所以I2C_SCL和I2C_SDA均需接上拉电阻,也正因此,当总线空闲时,这两条线路都处于高电平状态,当连到总线上的任一器件输出低电平,都将使总线拉低。
总线上的每一个设备都可以作为主设备或从设备,而且每一个设备都会对应一个唯一的地址(可以从12C器件数据手册得知),主从设备之间就是通过这个地址来确定与哪个器件进行通信。
空闲状态
空闲状态,IIC两条总线被规定都处在高电平,此时各个器件的输出级场效应管均处在截止状态,即释放总线,由两条信号线各自的上拉电阻把电平拉高。
起始信号
在SCL保持高电平期间,SDA的电平被拉低,称为 I2C总线的起始信号,标志着一次数据传输的开始。起始信号由主机主动建立,在建立该信号之前I2C总线必须处于空闲状态。
停止信号
在SCL处在高电平期间。SDA被释放,被上拉到高电平,成为IIC总线停止信号,标志着一次数据传输终止。 停止信号由主机主动发送,结束后,IIC总线返回空闲状态。
数据传输
在IIC总线传输数据时,必须在SCL处在低电平期间才允许SDA数据改变,在SCL处在高电平期间,SDA被要求必须保持稳定,在SCL高电平期间进行采样,如果SDA处于高电平则为1,为低电平则为0
应答信号
12C总线上的所有数据都是以字节传送的,发送端每发送一个字节,就必须在第9个SCL脉冲期间释放SDA,由接收端反馈一个应答信号。应答信号为低电平时,称为有效应答位(ACK),表示接收端成功接收了该字节;应答信号为高电平时,称为非应答位(NACK),表示接收端接收该字节失败。对于反馈有效应答位ACK的要求是,接收端在第9个时钟脉冲之前的低电平期间将SDA拉低,并且确保在该时钟周期的高电平期间保持低电平。如果接收端是主控端,则在它收到最后一个字节后,发送一个NACK信号,以通知被控发送端结束数据发送,并释放SDA线,以便主控接收器发送停止信号。
byte写:起始位——>设备地址(4bit+3bit(2bit不关心+1bit块选择位)+1bit读/写)——>相应信号——>数据地址(8bits)——>相应信号——>8bits数据——>响应信号——>停止位
页写:起始位——>设备地址(4bit+3bit(2bit不关心+1bit块选择位)+1bit读/写)——>相应信号——>数据地址(8bits)——>相应信号——>8bits数据——>响应信号——>……——>>8bits数据——>响应信号——>停止位;
注意:页写中每一页最多写入16byte数据,如果超过的的内部指针会滚动,将之前写入的数据从头向后覆盖
当前地址读:byte写:起始位——>设备地址(4bit+3bit(2bit不关心+1bit块选择位)+1bit读/写)——>响应信号——>接收数据8bits——>不发送响应信号——>发送停止位
(注意当前地址读是在上一个读写操作的地址上+1,也就是上次读写地址的写一个地址)
随机读:起始位——>设备地址(4bit+3bit(2bit不关心+1bit块选择位)+1bit读/写)——>相应信号——>数据地址(8bits)——>起始位——>设备地址(4bit+3bit(2bit不关心+1bit块选择位)+1bit读/写)——>相应信号——>接收8bits数据——>不发送响应信号——>停止位
页读:起始位——>设备地址(4bit+3bit(2bit不关心+1bit块选择位)+1bit读/写)——>相应信号——>数据地址(8bits)——>起始位——>设备地址(4bit+3bit(2bit不关心+1bit块选择位)+1bit读/写)——>相应信号——>接收8bits数据——>发送响应信号——>……——>>8bits数据——>不发送响应信号——>停止位
这里还是采用主从状态机实现,通过接口模块,和控制接口模块来达到读写eeprom
控制模块(主状态机模块),仅考虑操作类型,读操作或者写操作,不关系数据如何发送,只需要发送操作指令,等待接口模块发送完成返回结束信号。
接口模块(从状态机),当操作指令到来后,只需要按照时序发送或者接收数据,之后返还一个结束信号
/**************************************功能介绍***********************************
Date :
Author : WZY.
Version :
Description: 接口命令(cmd)
0 1
bit0 (起始位) NO YES
bit1 (写数据) NO YES
bit2 (读数据) NO YES
bit3 (停止位) NO YES
bit4 (相应位) ACK NO_ACK
*********************************************************************************/
//---------<模块及端口声名>------------------------------------------------------
module iic_dirver#
(
parameter T = "100k",
SYSTEM_CLOCK = 50_000_000
)
(
input wire clk ,
input wire rst_n ,
//控制接口
input wire [4:0] cmd ,
input wire cmd_vld ,
output wire done ,
//数据接口
input wire [7:0] wr_data ,
output wire [7:0] rd_data ,
output wire rd_data_vld,
output wire iic_scl ,
inout wire iic_sda
);
//---------<参数定义>---------------------------------------------------------
//状态机参数定义
localparam IDLE = 7'b0000001,
START = 7'b0000010,
WRITE = 7'b0000100,
READ = 7'b0001000,
R_ACK = 7'b0010000,
S_ACK = 7'b0100000,
STOP = 7'b1000000;
//时间参数定义
parameter IICT = (T == "100k")?SYSTEM_CLOCK/100_000:
(T == "400k")?SYSTEM_CLOCK/400_000:
SYSTEM_CLOCK/3_400_000;
// parameter IICT = 500;
parameter IICT_1_4 = IICT >> 2,
IICT_1_2 = IICT >> 1,
IICT_3_4 = IICT - IICT_1_4;
//指令定义
`define START_BIT 5'b00001
`define WRITE_BIT 5'b00010
`define READ_BIT 5'b00100
`define STOP_BIT 5'b01000
`define ACK_BIT 5'b10000
//响应信号定义
`define ACK 0
`define NO_ACK 1
//---------<内部信号定义>-----------------------------------------------------
reg [6:0] cstate ;//现态
reg [6:0] nstate ;//次态
wire idle2start ;
wire idle2write ;
wire idle2read ;
wire start2write ;
wire write2r_ack ;
wire read2s_ack ;
wire r_ack2idle ;
wire r_ack2stop ;
wire s_ack2idle ;
wire s_ack2stop ;
wire stop2idle ;
//计数器参数定义
reg [8:0] cnt_1bit ;
wire add_cnt_1bit ;
wire end_cnt_1bit ;
reg [2:0] cnt_num ;
wire add_cnt_num ;
wire end_cnt_num ;
reg [3:0] num;
//命令信号寄存
reg [4:0] cmd_r ;
//存在信号
reg rev_ack ;
//三态门参数
reg OE;
wire din;
reg dout;
//数据寄存
reg [7:0] rd_data_r ;
reg [7:0] wr_data_r ;
//****************************************************************
// 数据寄存
//****************************************************************
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
wr_data_r <= 8'd0;
end
else if (cmd_vld) begin
wr_data_r <= wr_data;
end
end
//****************************************************************
// 状态机
//****************************************************************
//第一段:时序逻辑描述状态转移
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cstate <= IDLE;
end
else begin
cstate <= nstate;
end
end
//第二段:组合逻辑描述状态转移规律和状态转移条件
always @(*) begin
case(cstate)
IDLE : begin
if (idle2start) begin
nstate = START;
end
else if (idle2write) begin
nstate = WRITE;
end
else if (idle2read) begin
nstate = READ;
end
else begin
nstate = cstate;
end
end
START : begin
if (start2write) begin
nstate = WRITE;
end
else begin
nstate = cstate;
end
end
WRITE : begin
if (write2r_ack) begin
nstate = R_ACK;
end
else begin
nstate = cstate;
end
end
READ : begin
if (read2s_ack) begin
nstate = S_ACK;
end
else begin
nstate = cstate;
end
end
R_ACK : begin
if (r_ack2idle) begin
nstate = IDLE;
end
else if (r_ack2stop) begin
nstate = STOP;
end
else begin
nstate = cstate;
end
end
S_ACK : begin
if (s_ack2stop) begin
nstate = STOP;
end
else if (s_ack2idle) begin
nstate = IDLE;
end
else begin
nstate = cstate;
end
end
STOP : begin
if (stop2idle) begin
nstate = IDLE;
end
else begin
nstate = cstate;
end
end
default : ;
endcase
end
assign idle2start = cstate == IDLE && cmd_vld && (cmd & `START_BIT);//接收到命令信号并且命令中包含开始命令
assign idle2write = cstate == IDLE && cmd_vld && (cmd & `WRITE_BIT);//接收到命令信号并且命令中没有开始命令,有写命令
assign idle2read = cstate == IDLE && cmd_vld && (cmd & `READ_BIT );//接收到命令信号并且命令中没有开始命令,有读命令
assign start2write = cstate == START && end_cnt_num && (cmd_r & `WRITE_BIT);
assign write2r_ack = cstate == WRITE && end_cnt_num;
assign read2s_ack = cstate == READ && end_cnt_num;
assign r_ack2idle = cstate == R_ACK && end_cnt_num && !(cmd_r & `STOP_BIT);//接收到响应,并且命令中没有停止命令
assign r_ack2stop = cstate == R_ACK && end_cnt_num && (cmd_r & `STOP_BIT);//接收到相应信号,并且命令中有停止命令
assign s_ack2idle = cstate == S_ACK && end_cnt_num && !(cmd_r & `STOP_BIT);//等待1bit时间并且没有停止命令
assign s_ack2stop = cstate == S_ACK && end_cnt_num && (cmd_r & `STOP_BIT);//等待1bit时间并且有停止命令
assign stop2idle = cstate == STOP && end_cnt_num;
//第三段:描述输出,时序逻辑或组合逻辑皆可
//****************************************************************
// 命令寄存
//****************************************************************
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
cmd_r <= 0;
end
else if (cmd_vld) begin//每次接收到命令使能更新命令
cmd_r <= cmd;
end
end
//****************************************************************
// 计数器复用
//****************************************************************
//1bit计数器
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_1bit <= 9'd0;
end
else if(add_cnt_1bit)begin
if(end_cnt_1bit)begin
cnt_1bit <= 'd0;
end
else begin
cnt_1bit <= cnt_1bit + 1'b1;
end
end
end
assign add_cnt_1bit = cstate != IDLE;
assign end_cnt_1bit = add_cnt_1bit && cnt_1bit == IICT-1;
//计数器复用
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_num <= 'd0;
end
else if(add_cnt_num)begin
if(end_cnt_num)begin
cnt_num <= 'd0;
end
else begin
cnt_num <= cnt_num + 1'b1;
end
end
end
assign add_cnt_num = end_cnt_1bit;
assign end_cnt_num = add_cnt_num && cnt_num == num - 1;
always @(*) begin
case (cstate)
START : num = 1 ;
WRITE : num = 8 ;
READ : num = 8 ;
R_ACK : num = 1 ;
S_ACK : num = 1 ;
STOP : num = 1 ;
default: num = 1;
endcase
end
//****************************************************************
// 三态门
//****************************************************************
//OE使能控制
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
OE <= 0;
end
else if (idle2start||idle2write||start2write||r_ack2stop||read2s_ack||s_ack2stop) begin
OE <= 1;
end
else if (idle2read||write2r_ack||stop2idle||s_ack2idle||r_ack2idle) begin
OE <= 0;
end
end
assign iic_sda = OE?dout:1'bz;
assign din = iic_sda;
assign iic_scl = (cnt_1bit >= (IICT >>1))?1:0;//发送周期波形
// SDA信号控制
always @(*) begin
if (!rst_n) begin
dout = 1;
end
else case (cstate)
IDLE : begin
dout = 1;
end
START : begin
if (cnt_1bit == IICT_3_4) begin
dout = 0;
end
end
WRITE : begin
if (cnt_1bit == IICT_1_4) begin
dout = wr_data_r[7-cnt_num];
end
end
READ : begin
if (cnt_1bit == IICT_3_4) begin
rd_data_r[7-cnt_num] = din;
end
end
R_ACK: begin
if (cnt_1bit == IICT_3_4) begin
rev_ack <= din;
end
end
S_ACK: begin
if (cnt_1bit == IICT_1_4) begin
dout = 0;
end
if (cmd_r & `ACK_BIT) begin
dout = `NO_ACK;
end
else begin
dout = `ACK;
end
end
STOP : begin
if (cnt_1bit == IICT_3_4) begin
dout = 1;
end
end
default: dout = dout;
endcase
end
assign rd_data = rd_data_r;
//****************************************************************
// 使能信号
//***************************************************************
assign done = stop2idle||r_ack2idle||s_ack2idle;
assign rd_data_vld = s_ack2stop;
endmodule
/**************************************功能介绍***********************************
Date :
Author : WZY.
Version :
Description:
*********************************************************************************/
/*
1、带起始位、不带停止位的写操作,主要是每次开始传输时候的第一字节;
2、不带起始位、也不带停止位的写操作,主要是传输器件地址和连续写多个字节数据到从机中的操作,比如 EEPROM的页写操作;
3、不带起始位、带停止位的写操作,主要是写入数据时候的最后一段,也就是完整写的最后一字节;
4、不带起始位、也不带停止位的读操作,主要是从器件中连续读出多个字节数据的操作,比如EEPROM的连续地址读操作。
5、不带起始位、带停止位的读操作,主要是从器件中读出最后一个字节。
*/
//---------<模块及端口声名>------------------------------------------------------
module eeprom_control#
(
parameter ADDR_BIT = 8
)
(
input wire clk ,
input wire rst_n ,
//交互接口
input wire [6:0] device_id,//IIC从设备ID
input wire [ADDR_BIT-1:0] reg_adder,//读写地址
input wire reg_adder_vld,
input wire rd_en ,
input wire wr_en ,
//数据接口
input wire [7:0] wr_data ,
input wire wr_data_vld,
output wire [7:0] rd_data ,
output wire rd_data_vld,
output wire ready,
//iic接口
output wire iic_scl,
inout wire iic_sda
);
//---------<参数定义>---------------------------------------------------------
localparam IDLE = 6'b000001,
WR_REQ = 6'b000010,
WAIT_WR_DONE = 6'b000100,
RD_REQ = 6'b001000,
WAIT_RD_DONE = 6'b010000,
DONE = 6'b100000;
parameter WR_CTRL_BIT = 8'b10100000,
RD_CTRL_BIT = 8'b10100001;
`define START_BIT 5'b00001
`define WRITE_BIT 5'b00010
`define READ_BIT 5'b00100
`define STOP_BIT 5'b01000
`define ACK_BIT 5'b10000
//---------<内部信号定义>-----------------------------------------------------
//状态机参数定义
reg [5:0] cstate ;//现态
reg [5:0] nstate ;//次态
wire idle2wr_req ;
wire idle2rd_req ;
wire wr_req2wait_wr_done ;
wire wait_wr_done2done ;
wire wait_wr_done2wr_req ;
wire wait_wr_done2rd_req ;
wire rd_req2wait_rd_done ;
wire wait_rd_done2done ;
wire wait_rd_done2rd_req ;
wire done2idle ;
reg [2:0] cnt_cmd ;
wire add_cnt_cmd ;
wire end_cnt_cmd ;
reg [2:0] cmd_num;
wire done ;
reg [4:0] cmd ;
reg cmd_vld ;
reg [7:0] op_wr_data ;
reg rd_en_r ;
reg wr_en_r ;
//****************************************************************
// 同步
//****************************************************************
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
rd_en_r <= 0;
wr_en_r <= 0;
end
else begin
rd_en_r <= rd_en;
wr_en_r <= wr_en;
end
end
//****************************************************************
// 数据寄存
//****************************************************************
reg [ADDR_BIT-1:0] addr_r;
reg [7:0] wr_data_r;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
addr_r <= 0;
end
else if (reg_adder_vld) begin
addr_r <= reg_adder;
end
else begin
addr_r <= addr_r;
end
end
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
wr_data_r <= 0;
end
else if (wr_data_vld) begin
wr_data_r <= wr_data;
end
else begin
wr_data_r <= wr_data_r;
end
end
//****************************************************************
// 状态机
//****************************************************************
//第一段:时序逻辑描述状态转移
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cstate <= IDLE;
end
else begin
cstate <= nstate;
end
end
//第二段:组合逻辑描述状态转移规律和状态转移条件
always @(*) begin
case(cstate)
IDLE : begin
if (idle2wr_req) begin
nstate = WR_REQ;
end
else if (idle2rd_req) begin
nstate = RD_REQ;
end
else begin
nstate = cstate;
end
end
WR_REQ : begin
if (wr_req2wait_wr_done) begin
nstate = WAIT_WR_DONE;
end
else begin
nstate = cstate;
end
end
WAIT_WR_DONE : begin
if (wait_wr_done2done) begin
nstate = DONE;
end
else if (wait_wr_done2wr_req) begin
nstate = WR_REQ;
end
else begin
nstate = cstate;
end
end
RD_REQ : begin
if (rd_req2wait_rd_done) begin
nstate = WAIT_RD_DONE;
end
else begin
nstate = cstate;
end
end
WAIT_RD_DONE : begin
if (wait_rd_done2done) begin
nstate = DONE;
end
else if (wait_rd_done2rd_req) begin
nstate = RD_REQ;
end
else begin
nstate = cstate;
end
end
DONE : begin
if (done2idle) begin
nstate = IDLE;
end
else begin
nstate = cstate;
end
end
default : ;
endcase
end
assign idle2wr_req = cstate == IDLE && wr_en_r;
assign idle2rd_req = cstate == IDLE && rd_en_r;
assign wr_req2wait_wr_done = cstate == WR_REQ && 1;
assign wait_wr_done2done = cstate == WAIT_WR_DONE && end_cnt_cmd;
assign wait_wr_done2wr_req = cstate == WAIT_WR_DONE && done && !end_cnt_cmd;
assign rd_req2wait_rd_done = cstate == RD_REQ && 1;
assign wait_rd_done2done = cstate == WAIT_RD_DONE && end_cnt_cmd;
assign wait_rd_done2rd_req = cstate == WAIT_RD_DONE && done && !end_cnt_cmd;
assign done2idle = cstate == DONE && 1;
//****************************************************************
// 指令计数器
//****************************************************************
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_cmd <= 3'd0;
end
else if(add_cnt_cmd)begin
if(end_cnt_cmd)begin
cnt_cmd <= 3'd0;
end
else begin
cnt_cmd <= cnt_cmd + 1'b1;
end
end
else if (cstate == IDLE) begin
cnt_cmd <= 3'd0;
end
end
assign add_cnt_cmd = done;
assign end_cnt_cmd = add_cnt_cmd && cnt_cmd == cmd_num - 1;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
cmd_num <= 1;
end
else if (rd_en) begin
cmd_num <= 4;
end
else if (wr_en) begin
cmd_num <= 3;
end
else if (end_cnt_cmd) begin
cmd_num <= 1;
end
end
//****************************************************************
// 发送指令
//****************************************************************
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
TX(0,5'b0,8'h00);
end
else case (cstate)
WR_REQ: case (cnt_cmd)//线性序列机
0 : TX(1,(`START_BIT | `WRITE_BIT),WR_CTRL_BIT);//写器件地址
1 : TX(1,(`WRITE_BIT),addr_r);//写入数据地址
2 : TX(1,(`WRITE_BIT | `STOP_BIT),wr_data_r);//写入数据
default: TX(0,cmd,op_wr_data);
endcase
RD_REQ: case (cnt_cmd)
0 : TX(1,(`START_BIT | `WRITE_BIT),WR_CTRL_BIT);//写入器件地址(写)
1 : TX(1,(`WRITE_BIT),addr_r) ;//写入数据地址
2 : TX(1,(`START_BIT | `WRITE_BIT),RD_CTRL_BIT);//写入器件地址(读)
3 : TX(1,(`READ_BIT| `ACK_BIT | `STOP_BIT),8'h00);//读取数据
default: TX(0,cmd,op_wr_data);
endcase
default:TX(0,cmd,op_wr_data) ;
endcase
end
//****************************************************************
// 任务
//****************************************************************
task TX;
input task_cmd_vld;
input [4:0] task_cmd;
input [7:0] task_wr_data;
begin
cmd_vld = task_cmd_vld;
cmd = task_cmd;
op_wr_data = task_wr_data;
end
endtask
assign ready = cstate == IDLE;
//****************************************************************
// 子模块例化
//****************************************************************
iic_dirver#
(
.T ("100k"),
.SYSTEM_CLOCK(50_000_000)
)
iic_dirver_inst
(
/*input wire */ .clk (clk),
/*input wire */ .rst_n (rst_n),
/*input wire [4:0] */ .cmd (cmd),
/*input wire */ .cmd_vld (cmd_vld),
/*output wire */ .done (done),
/*input wire [7:0] */ .wr_data (op_wr_data),
/*output wire [7:0] */ .rd_data (rd_data),
/*output wire */ .rd_data_vld(rd_data_vld),
/*output wire */ .iic_scl (iic_scl),
/*inout wire */ .iic_sda (iic_sda)
);
endmodule
其他按键模块,串口模块不过多赘述
/**************************************功能介绍***********************************
Date :
Author : WZY.
Version :
Description:
*********************************************************************************/
//---------<模块及端口声名>------------------------------------------------------
module top(
input wire clk ,
input wire rst_n ,
input wire key_in ,
input wire rx ,
output wire tx ,
output wire iic_scl ,
inout wire iic_sda
);
//---------<参数定义>---------------------------------------------------------
wire [7:0] wr_data;
wire wr_data_vld;
wire [7:0] rd_data;
wire rd_data_vld;
wire key_debounce;
//---------<内部信号定义>-----------------------------------------------------
eeprom_control#
(
.ADDR_BIT (8)
)
eeprom_control_inst
(
/*input wire */ .clk (clk),
/*input wire */ .rst_n (rst_n),
/*input wire [6:0] */ .device_id (1010000),//IIC从设备ID
/*input wire [ADDR_BIT-1:0] */ .reg_adder (8'h00),//读写地址
/*input wire */ .reg_adder_vld (1),
/*input wire */ .rd_en (!key_debounce),
/*input wire */ .wr_en (wr_data_vld),
/*input wire [7:0] */ .wr_data (wr_data),
/*input wire */ .wr_data_vld (wr_data_vld),
/*output wire [7:0] */ .rd_data (rd_data),
/*output wire */ .rd_data_vld (rd_data_vld),
/*output wire */ .ready (),
/*output wire */ .iic_scl (iic_scl),
/*inout wire */ .iic_sda (iic_sda)
);
//模块例化
uart_rx#
(
.BPS (115200),
.CLK_FRE (50_000_000),
.CHECK_BIT ("NONE")//NONE 不校验 DDO奇校验 EVEN偶校验
) uart_rx_inst
(
/*input wire */ .clk (clk),
/*input wire */ .rst_n (rst_n),
/*input wire */ .rx (rx),
/*output wire */ .ready (),
/*output wire [7:0]*/ .rx_data (wr_data),
/*output wire */ .rx_data_vld (wr_data_vld)
);
//模块例化
uart_tx#
(
.BPS (115200),
.CLK_FRE (50_000_000),
.CHECK_BIT ("NONE")//NONE 不校验 DDO奇数 EVEN偶数
)uart_tx_inst
(
/*input wire */ .clk (clk),
/*input wire */ .rst_n (rst_n),
/*input wire */ .tx_data_vld (rd_data_vld),
/*input wire [7:0] */ .tx_data (rd_data),
/*output reg */ .tx (tx),
/*output wire */ .ready ()
);
key_debounce key_debounce_inst(
/*input wire */ .clk (clk),
/*input wire */ .rst_n (rst_n),
/*input wire [3:0] */ .key_in (key_in),
/*output wire [3:0] */ .key_out (key_debounce)
);
endmodule
// *******************************************************************************************************
// ** **
// ** 24LC04B.v - Microchip 24LC04B 4K-BIT I2C SERIAL EEPROM (VCC = +2.5V TO +5.5V) **
// ** **
// *******************************************************************************************************
// ** **
// ** This information is distributed under license from Young Engineering. **
// ** COPYRIGHT (c) 2003 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.3 **
// ** Modified Date : 12/04/2006 **
// ** Revision History: **
// ** **
// ** 02/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) **
// ** **
// *******************************************************************************************************
// ** 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 M24LC04B (A0, A1, A2, WP, SDA, SCL, RESET);
input A0; // unconnected pin
input A1; // unconnected pin
input A2; // unconnected pin
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
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 ADDR_Rcvd; // byte address 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 BlockSelect; // memory block select
wire RdWrBit; // read/write control bit
reg [08:00] StartAddress; // memory access starting address
reg [03:00] PageAddress; // memory page address
reg [07:00] WrDataByte [0:15]; // memory write data buffer
wire [07:00] RdDataByte; // memory read data
reg [15:00] WrCounter; // write buffer counter
reg [03:00] WrPointer; // write buffer pointer
reg [08:00] RdPointer; // read address pointer
reg WriteActive; // memory write cycle active
reg [07:00] MemoryBlock0 [0:255]; // EEPROM data memory array
reg [07:00] MemoryBlock1 [0:255]; // EEPROM data memory array
integer LoopIndex; // iterative loop index
integer tAA; // timing parameter
integer tWC; // timing parameter
// *******************************************************************************************************
// ** INITIALIZATION **
// *******************************************************************************************************
//----------------------------
//------写数据间隔改动----------
initial tAA = 0; // SCL to SDA output delay
initial tWC = 0; // memory write cycle time
// 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;
ADDR_Rcvd = 0;
MACK_Rcvd = 0;
end
initial begin
BitCounter = 0;
ControlByte = 0;
end
initial begin
WrCycle = 0;
RdCycle = 0;
WriteActive = 0;
end
// *******************************************************************************************************
// ** CORE LOGIC **
// *******************************************************************************************************
// -------------------------------------------------------------------------------------------------------
// 1.01: START Bit Detection
// -------------------------------------------------------------------------------------------------------
always @(negedge SDA) begin
if (SCL == 1) begin
START_Rcvd <= 1;
STOP_Rcvd <= 0;
CTRL_Rcvd <= 0;
ADDR_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;
ADDR_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:04] == 4'b1010)) 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 BlockSelect = ControlByte[01];
assign RdWrBit = ControlByte[00];
// -------------------------------------------------------------------------------------------------------
// 1.06: Byte Address Register
// -------------------------------------------------------------------------------------------------------
always @(negedge SCL) begin
if (CTRL_Rcvd & (BitCounter == 8)) begin
if (RdWrBit == 0) begin
StartAddress <= {BlockSelect,ShiftRegister[07:00]};
RdPointer <= {BlockSelect,ShiftRegister[07:00]};
ADDR_Rcvd <= 1;
end
WrCounter <= 0;
WrPointer <= 0;
CTRL_Rcvd <= 0;
end
end
// -------------------------------------------------------------------------------------------------------
// 1.07: Write Data Buffer
// -------------------------------------------------------------------------------------------------------
always @(negedge SCL) begin
if (ADDR_Rcvd & (BitCounter == 8)) begin
if ((WP == 0) & (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:04] == 4'b1010))) 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
if (StartAddress[08] == 0) begin
PageAddress = StartAddress[03:00] + LoopIndex;
MemoryBlock0[{StartAddress[07:04],PageAddress[03:00]}] = WrDataByte[LoopIndex[03:00]];
end
if (StartAddress[08] == 1) begin
PageAddress = StartAddress[03:00] + LoopIndex;
MemoryBlock1[{StartAddress[07:04],PageAddress[03:00]}] = WrDataByte[LoopIndex[03:00]];
end
end
end
// -------------------------------------------------------------------------------------------------------
// 1.12: Read Data Multiplexor
// -------------------------------------------------------------------------------------------------------
always @(negedge SCL) begin
if (BitCounter == 8) begin
if (WrCycle & ADDR_Rcvd) begin
RdPointer <= StartAddress + WrPointer + 1;
end
if (RdCycle) begin
RdPointer <= RdPointer + 1;
end
end
end
assign RdDataByte = RdPointer[08] ? MemoryBlock1[RdPointer[07:00]] : MemoryBlock0[RdPointer[07: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] MemoryByte0_00 = MemoryBlock0[00];
wire [07:00] MemoryByte0_01 = MemoryBlock0[01];
wire [07:00] MemoryByte0_02 = MemoryBlock0[02];
wire [07:00] MemoryByte0_03 = MemoryBlock0[03];
wire [07:00] MemoryByte0_04 = MemoryBlock0[04];
wire [07:00] MemoryByte0_05 = MemoryBlock0[05];
wire [07:00] MemoryByte0_06 = MemoryBlock0[06];
wire [07:00] MemoryByte0_07 = MemoryBlock0[07];
wire [07:00] MemoryByte0_08 = MemoryBlock0[08];
wire [07:00] MemoryByte0_09 = MemoryBlock0[09];
wire [07:00] MemoryByte0_0A = MemoryBlock0[10];
wire [07:00] MemoryByte0_0B = MemoryBlock0[11];
wire [07:00] MemoryByte0_0C = MemoryBlock0[12];
wire [07:00] MemoryByte0_0D = MemoryBlock0[13];
wire [07:00] MemoryByte0_0E = MemoryBlock0[14];
wire [07:00] MemoryByte0_0F = MemoryBlock0[15];
wire [07:00] MemoryByte1_00 = MemoryBlock1[00];
wire [07:00] MemoryByte1_01 = MemoryBlock1[01];
wire [07:00] MemoryByte1_02 = MemoryBlock1[02];
wire [07:00] MemoryByte1_03 = MemoryBlock1[03];
wire [07:00] MemoryByte1_04 = MemoryBlock1[04];
wire [07:00] MemoryByte1_05 = MemoryBlock1[05];
wire [07:00] MemoryByte1_06 = MemoryBlock1[06];
wire [07:00] MemoryByte1_07 = MemoryBlock1[07];
wire [07:00] MemoryByte1_08 = MemoryBlock1[08];
wire [07:00] MemoryByte1_09 = MemoryBlock1[09];
wire [07:00] MemoryByte1_0A = MemoryBlock1[10];
wire [07:00] MemoryByte1_0B = MemoryBlock1[11];
wire [07:00] MemoryByte1_0C = MemoryBlock1[12];
wire [07:00] MemoryByte1_0D = MemoryBlock1[13];
wire [07:00] MemoryByte1_0E = MemoryBlock1[14];
wire [07:00] MemoryByte1_0F = MemoryBlock1[15];
// -------------------------------------------------------------------------------------------------------
// 2.02: Write Data Buffer
// -------------------------------------------------------------------------------------------------------
wire [07:00] WriteData_0 = WrDataByte[00];
wire [07:00] WriteData_1 = WrDataByte[01];
wire [07:00] WriteData_2 = WrDataByte[02];
wire [07:00] WriteData_3 = WrDataByte[03];
wire [07:00] WriteData_4 = WrDataByte[04];
wire [07:00] WriteData_5 = WrDataByte[05];
wire [07:00] WriteData_6 = WrDataByte[06];
wire [07:00] WriteData_7 = WrDataByte[07];
wire [07:00] WriteData_8 = WrDataByte[08];
wire [07:00] WriteData_9 = WrDataByte[09];
wire [07:00] WriteData_A = WrDataByte[10];
wire [07:00] WriteData_B = WrDataByte[11];
wire [07:00] WriteData_C = WrDataByte[12];
wire [07:00] WriteData_D = WrDataByte[13];
wire [07:00] WriteData_E = WrDataByte[14];
wire [07:00] WriteData_F = WrDataByte[15];
// *******************************************************************************************************
// ** TIMING CHECKS **
// *******************************************************************************************************
wire TimingCheckEnable = (RESET == 0) & (SDA_OE == 0);
//--------------------------------
//-------仿真时时序约束需改动--------
//--------------------------------
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
// 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);
$hold (negedge SDA &&& TimingCheckEnable, negedge SCL, tHD_STA);
endspecify
endmodule
`timescale 1ns/1ns
module top_tb();
//激励信号定义
reg clk ;
reg rst_n ;
reg key_in ;
reg rx ;
//输出信号定义
wire tx ;
wire iic_scl ;
wire iic_sda ;
//时钟周期参数定义
parameter CYCLE = 20;
pullup(iic_sda);
//模块例化
top top_inst(
/*input wire */ .clk (clk),
/*input wire */ .rst_n (rst_n),
/*input wire */ .key_in (key_in),
/*input wire */ .rx (rx),
/*output wire */ .tx (tx),
/*output wire */ .iic_scl (iic_scl),
/*inout wire */ .iic_sda (iic_sda)
);
M24LC04B u_M24LC04B(
.A0(0),
.A1(0),
.A2(0),
.WP(0),
.SDA(iic_sda),
.SCL(iic_scl),
.RESET(~rst_n)
);
//产生时钟
initial clk = 1'b1;
always #(CYCLE/2) clk = ~clk;
//产生激励
initial begin
rst_n = 1'b1;
#(CYCLE*2);
rst_n = 1'b0;
key_in = 1'b0;
rx = 1'b0;
#(CYCLE*20);
rst_n = 1'b1;
#20
rx = 1 ;
#(CYCLE*434)
rx = 0 ; //开始位发送
#(CYCLE*434)
rx = 0 ; //发送数据1
#(CYCLE*434)
rx = 1 ; //发送数据0
#(CYCLE*434)
rx = 0 ; //发送数据1
#(CYCLE*434)
rx = 1 ; //发送数据0
#(CYCLE*434)
rx = 0 ; //发送数据1
#(CYCLE*434)
rx = 1 ; //发送数据0
#(CYCLE*434)
rx = 0 ; //发送数据1
#(CYCLE*434)
rx = 1 ; //发送数据0
#(CYCLE*5000)
key_in = 1;
#20;
key_in = 0;
#(CYCLE*434)
// rx = 1 ; //发送标志位0//偶校验
// #(CYCLE*10)
// rx = 1 ; //发送结束位
// #(CYCLE*434)
// rx = 1 ;
// #(CYCLE*434)
// rx = 0 ; //开始位发送
// #(CYCLE*434)
// rx = 0 ; //发送数据1
// #(CYCLE*434)
// rx = 1 ; //发送数据0
// #(CYCLE*434)
// rx = 0 ; //发送数据1
// #(CYCLE*434)
// rx = 1 ; //发送数据0
// #(CYCLE*434)
// rx = 0 ; //发送数据1
// #(CYCLE*434)
// rx = 1 ; //发送数据0
// #(CYCLE*434)
// rx = 0 ; //发送数据1
// #(CYCLE*434)
// rx = 1 ; //发送数据0
// #(CYCLE*434)
// // rx = 1 ; //发送标志位0//偶校验
// // #(CYCLE*10)
// rx = 1 ; //发送结束位
// #(CYCLE*1000)
// key_in = 1;
// #20;
// key_in = 0;
// #(CYCLE*434)
#1000000;
$stop;
end
endmodule
略(经检验效果成功)