目录
一、IIC通信协议简介
二、IIC通信时序
1.时序概括
2.24LC04 IIC通信时序
三、实例
1.程序代码
2.仿真验证:
IIC(Inter-Integrated Circuit) ,简单说就是IC(芯片)之间通信的总线,IIC的意思是“完成集成电路或者功能单元之间信息交换的规范或协议”,该通信协议常应用与EEPROM的读写、芯片寄存器的配置等。I2C总线由数据线SDA和时钟线SCL构成通信线路,其通信原理是通过对SDA和SCL线高低电平时序的控制,来产生I2C总线协议所需要的信号进行数据的传递,SCL用于传输时钟控制信号,SDA用于传输数据,在总线空闲状态时,这两根线一般被上面所接的上拉电阻拉高,保持着高电平。由于只有SDA一根线进行数据传输,因此,同一时刻只能用于发送或接收数据,故IIC通信协议属于半双工的通信方式。IIC协议的通信速度分为三种模式:1标准模式的传输速率为100Kb/s;2快速模式为400Kb/s;3高速模式为3.4Mb/s。
空闲状态:SDA和SCL全为高电平
开始状态:当SCL为高电平时,SDA由高到低的跳变,表示数据传输的开始
传输数据有效状态:在数据传输时,SCL时钟线上的信号为高电平期间,SDA数据线上的数据
必须保持稳定,只有在SCL为低电平期间,SDA数据线上的高电平或低电
平状态才允许变化。即:数据在SCL的上升沿来之前就需准备好,并在下
降沿到来之前必须稳定。
应答状态:发端在SDA上每发送一个字节的数据,就在SCL第9个时钟期间释放该数据线SDA,
收端反馈一个应答信号。应答信号为低电平时,规定为有效应答(ACK简称应答位),
表示收端已经成功地接收了该字节;应答信号为高电平时,规定为非应答(NACK),
表示收端接收该字节没有成功。
结束状态:当SCL为高电平时,SDA由低到高的跳变,表示数据的结束。
1.24LC04简介
EEPROM即电可擦除可编程只读存储器,是一种常用的非易失性存储器(掉电后,数据不丢失)。24LC04,总容量是4Kbite(2*256*8bite),512B,因此需要的地址位宽是9位。通过IIC总线进行通信。下图是24LC04器件的原理图,其中A0,A1,A2是器件的地址,这里在硬件连接中全部设置为低电平,WP是写保护信号,SCL和SDA分别是上述提到的时钟信号和数据信号。
24LC04器件手册
主要注意SCL的时钟范围以及循环写之间的时间间隔。
2. 单字节写操作如下:
在数据传输期间,主机首先向从机发送7个bite的器件地址(高四位1010表示EEPROM,第三位由该器件的硬件电路决定),接着发送一个bite的写控制命令(即低电平),由从机反馈一个应答信号,如果主机检测到该应答信号有效,则进行下一个字节的发送。接着进入WORD ADDRESS,向从机发送写入的地址,如果地址超过两个字节,则需要发送两次,第一次发送高8位的地址,第二次发送低8位的地址,同样在每次发送完一个字节数据后,从机会反馈一个应答信号,如果该应答信号有效,则进入DATA,向从机发送写入的数据,发送完一个字节的数据后,从机响应一个应答信号,接着发送结束位,表示该次写操作完成。
3. 页写操作如下:
页写操作需注意,每次连续写入的数据不能超过单页的最大容量,24LC04单页最大的容量是16B,因此,一次最多写入16个字节。
4.单字节读操作如下:
在数据传输期间,主机首先向从机发送7个bite的器件地址,接着发送一个bite的写控制命令,由从机反馈一个应答信号,接着进行写地址的发送,同样地址字节发送结束后从机向主机发送一个应答信号,接着主机向从机发送7bite的器件地址加1个bite的读控制命令,从机反馈一个应答信号,接着从机向主机发送读出的一个字节的数据,主机向从机反馈一个非应答信号,接着发送结束位,表示该次单字节读操作完成。
5.连续字节读操作如下:
连续字节读操作和单字节读操作基本类似,只不过在数据读取的过程中从机向主机每发送一个字节读取的数据,主机回应一个应答信号,然后从机进行下一个字节数据的发送,直到主机发送非应答信号,接着发送一个结束信号,连续读字节操作完成。
该例子是学习了野火FPGA系列的教学视频,通过外部按键进行输入控制,向EEPROM中写入若干数据,然后再通过外部按键进行读操作,读取EEPROM中写入的数据。本篇文章仅进行EEPROM控制部分Verilog程序的编写,关于其他模块的,后续再进行更新。
下图是整个系统模块的示意图:
下图是EEPROM控制模块的示意图:
输入信号分别是:系统时钟信号 sys_clk
系统复位信号 sys_res
数据读写开始信号 iic_start (由读写操作模块提供)
写操作使能信号 wr_en (由读写操作模块提供)
写入的数据 wr_data (由读写操作模块提供)
读写数据的地址 byte_addr(由读写操作模块提供)
读操作使能信号 rd_en (由读写操作模块提供)
地址位控制信号 addr_num(高电平代表16位地址,低电平代表8位地址)
输出信号分别是:数据传输总线 sda
时钟传输总线 scl
读出来的数据 rd_data
一次读或写操作结束信号 iic_end
用于读写操作模块的时钟 iic_clk(系统时钟50分频后得到)
下图是该EEPROM控制模块的状态转换图:
IDLE:空闲状态
START:开始状态(占据一个scl时钟周期)
ST_SLADDR:传输器件地址(占据8个scl时钟周期,包括4位的EEPROM通信地址+3位的24LC04 的器件地址+1位的读写控制指令)
ACK1:应答信号1
ST_ADDR16:传输高八位地址(占据8个scl时钟周期)
ACK2:应答信号2
ST_ADDR8:传输低八位地址(占据8个scl时钟周期)
ACK3:应答信号3
WR_DATA:传输写入的数据(由于只是单字节写,占据8个scl时钟周期)
ACK4:应答信号4
START2:开始状态2 (占据一个scl时钟周期)
ST2_SLADDR:传输器件地址(占据8个scl时钟周期,包括4位的EEPROM通信地址+3位的24LC04 的器件地址+1位的读写控制指令)
ACK5:应答信号5
RD_DATA:读出数据(由于只是单字节读,占据8个scl时钟周期)
N_ACK:主机给从机发送无应答信号(占据一个scl时钟周期)
END:结合状态
下图是该模块的时序图:共分为三部分,iic_clk生成时序、写操作部分时序、读操作部分时序
1.iic_clk生成时序
2.写操作部分时序
3.读操作部分时序
module iic_control
(
input sys_clk ,
input sys_res ,
input iic_start ,
input wr_en ,
input [7:0] wr_data ,
input [15:0]byte_addr ,
input rd_en ,
input addr_num ,//高电平代表2B地址
inout sda ,
output reg scl ,
output reg [7:0] rd_data ,
output reg iic_end ,
output reg iic_clk
);
parameter DEVICE_ADDR = 7'b1010000;
parameter SYSCLK = 50_000_000;
parameter SCLCLK = 250_000;//sda,scl的时钟
parameter IICCLK = 1_000_000;//用于读写控制模块的时钟(一般为SCL时钟的4倍)
parameter CNT_IICCLK = (SYSCLK/IICCLK)/2;
parameter IDLE = 4'd0 ;
parameter START = 4'd1 ;
parameter ST_SLADDR = 4'd2 ;
parameter ACK1 = 4'd3 ;
parameter ST_ADDR16 = 4'd4 ;
parameter ACK2 = 4'd5 ;
parameter ST_ADDR8 = 4'd6 ;
parameter ACK3 = 4'd7 ;
parameter WR_DATA = 4'd8 ;
parameter ACK4 = 4'd9 ;
parameter END = 4'd10 ;
parameter START2 = 4'd11 ;
parameter ST2_SLADDR = 4'd12 ;
parameter ACK5 = 4'd13 ;
parameter RD_DATA = 4'd14 ;
parameter N_ACK = 4'd15 ;
reg [4:0] cnt_iic_clk ;
reg [3:0] state ;
reg [1:0] cnt_sel ;
reg en_cnt_sel ;
reg [2:0] cnt_sda_bite ;
reg ack ;
reg sda_out ;
reg sda_en ;
wire sda_in ;
reg [7:0] rd_data_reg ;
always@(posedge sys_clk or negedge sys_res)
if(!sys_res)
cnt_iic_clk <= 'd0;
else if(cnt_iic_clk == CNT_IICCLK-1)
cnt_iic_clk <= 'd0;
else
cnt_iic_clk <= cnt_iic_clk + 1'b1;
always@(posedge sys_clk or negedge sys_res)
if(!sys_res)
iic_clk <= 1'b0;
else if(cnt_iic_clk == CNT_IICCLK-1)
iic_clk <= ~iic_clk;
/
always@(posedge iic_clk or negedge sys_res)
if(!sys_res)
en_cnt_sel <= 1'b0;
else if((state == IDLE && iic_start == 1'b0)||(state == END && cnt_sda_bite == 'd3 && cnt_sel == 'd3))
en_cnt_sel <= 1'b0;
else
en_cnt_sel <= 1'b1;
always@(posedge iic_clk or negedge sys_res)
if(!sys_res)
cnt_sel <= 'd0;
else if(en_cnt_sel && cnt_sel == 'd3)
cnt_sel <= 'd0;
else if(en_cnt_sel)
cnt_sel <= cnt_sel + 1'b1;
else
cnt_sel <= 'd0;
always@(posedge iic_clk or negedge sys_res)
if(!sys_res)
cnt_sda_bite <= 'd0;
else if((state == ST_SLADDR || state == ST_ADDR16 || state == ST_ADDR8 ||state == WR_DATA || state == ST2_SLADDR || state == RD_DATA) && cnt_sel == 'd3 && cnt_sda_bite == 'd7 )
cnt_sda_bite <= 'd0;
else if((state == ST_SLADDR || state == ST_ADDR16 || state == ST_ADDR8 ||state == WR_DATA || state == ST2_SLADDR || state == RD_DATA) && cnt_sel == 'd3)
cnt_sda_bite <= cnt_sda_bite + 1'b1;
else if(state == END && cnt_sel == 'd3 && cnt_sda_bite == 'd3)
cnt_sda_bite <= 'd0;
else if(state == END && cnt_sel == 'd3)
cnt_sda_bite <= cnt_sda_bite + 1'b1;
else if(state == IDLE || state == START || state == START2 || state == ACK1 || state == ACK2 || state == ACK3 || state == ACK4 || state == ACK5 || state == N_ACK)
cnt_sda_bite <= 'd0;
else
cnt_sda_bite <= cnt_sda_bite;
always@(posedge iic_clk or negedge sys_res)
if(!sys_res)
state <= IDLE;
else
case(state)
IDLE : if(iic_start)
state <= START;
START : if(cnt_sel == 'd3)
state <= ST_SLADDR;
ST_SLADDR : if(cnt_sel == 'd3 && cnt_sda_bite == 'd7)
state <= ACK1;
ACK1 : if(cnt_sel == 'd3 && ack == 1'b0 && addr_num == 1'b1)
state <= ST_ADDR16;
else if(cnt_sel == 'd3 && ack == 1'b0)
state <= ST_ADDR8 ;
ST_ADDR16 : if(cnt_sel == 'd3 && cnt_sda_bite == 'd7)
state <= ACK2;
ACK2 : if(cnt_sel == 'd3 && ack == 1'b0)
state <= ST_ADDR8;
ST_ADDR8 : if(cnt_sel == 'd3 && cnt_sda_bite == 'd7)
state <= ACK3;
ACK3 : if(cnt_sel == 'd3 && ack == 1'b0 && wr_en)
state <= WR_DATA;
else if(cnt_sel == 'd3 && ack == 1'b0 && rd_en)
state <= START2;
WR_DATA : if(cnt_sel == 'd3 && cnt_sda_bite == 'd7)
state <= ACK4;
ACK4 : if(cnt_sel == 'd3 && ack == 1'b0)
state <= END;
START2 : if(cnt_sel == 'd3)
state <= ST2_SLADDR;
ST2_SLADDR: if(cnt_sel == 'd3 && cnt_sda_bite == 'd7)
state <= ACK5;
ACK5 : if(cnt_sel == 'd3 && ack == 1'b0)
state <= RD_DATA;
RD_DATA : if(cnt_sel == 'd3 && cnt_sda_bite == 'd7)
state <= N_ACK;
N_ACK : if(cnt_sel == 'd3 )
state <= END;
END : if(cnt_sel == 'd3 && cnt_sda_bite == 'd3)
state <= IDLE;
default: state <= IDLE;
endcase
/输出sda、scl
always@(posedge iic_clk or negedge sys_res)
if(!sys_res)
scl <= 1'b1;
else if(state == IDLE ||(state == START && cnt_sel == 'd0)|| (state == END && cnt_sel != 'd0))
scl <= 1'b1;
else if(cnt_sel == 'd2 || cnt_sel == 'd0)
scl <= ~scl;
always@(*)
case(state)
IDLE : sda_out <= 1'b1 ;
START : if(cnt_sel == 'd0)
sda_out <= 1'b1 ;
else
sda_out <= 1'b0 ;
ST_SLADDR : if(cnt_sda_bite <= 'd6)
sda_out <= DEVICE_ADDR[6-cnt_sda_bite];
else if(cnt_sda_bite == 'd7)
sda_out <= 1'b0;//低电平代表写
ST_ADDR16 : sda_out <= byte_addr[15-cnt_sda_bite];
ST_ADDR8 : sda_out <= byte_addr[7-cnt_sda_bite];
WR_DATA : sda_out <= wr_data[7-cnt_sda_bite];
END : if(cnt_sel <= 'd3 && cnt_sda_bite == 'd0)
sda_out <= 1'b0;
else
sda_out <= 1'b1;
START2 : if(cnt_sel <= 'd1)
sda_out <= 1'b1;
else
sda_out <= 1'b0;
ST2_SLADDR : if(cnt_sda_bite <= 'd6)
sda_out <= DEVICE_ADDR[6-cnt_sda_bite];
else if(cnt_sda_bite == 'd7)
sda_out <= 1'b1;//高电平代表读
N_ACK : sda_out <= 1'b1;//主机给从机的响应信号
RD_DATA,ACK1,ACK2,ACK3,ACK4,ACK5:sda_out <= 1'b1;
default:;
endcase
always@(*)
case(state)
IDLE,START,ST_SLADDR,ST_ADDR16,ST_ADDR8,WR_DATA,END,START2,ST2_SLADDR,N_ACK:
sda_en <= 1'b1;
ACK1,ACK2,ACK3,ACK4,ACK5,RD_DATA:
sda_en <= 1'b0;
default:;
endcase
always@(*)
case(state)
ACK1,ACK2,ACK3,ACK4,ACK5:
if(cnt_sel == 'd0)
ack <= sda_in;
else
ack <= ack;
default: ack <= 1'b1;
endcase
assign sda_in = sda;
assign sda = (sda_en==1'b1)?sda_out:1'bz;
always@(posedge iic_clk or negedge sys_res)
if(!sys_res)
iic_end <= 1'b0;
else if(state == END && cnt_sel == 'd3 && cnt_sda_bite == 'd3)
iic_end <= 1'b1;
else
iic_end <= 1'b0;
always@(*)
case(state)
IDLE:
rd_data_reg <= 'd0;
RD_DATA:
if(cnt_sel == 'd2)
rd_data_reg[7-cnt_sda_bite] <= sda_in;
default:rd_data_reg <= rd_data_reg;
endcase
always@(posedge iic_clk or negedge sys_res)
if(!sys_res)
rd_data <= 'd0;
else if(state == RD_DATA && cnt_sel == 'd3 && cnt_sda_bite == 'd7)
rd_data <= rd_data_reg;
endmodule
`timescale 1ns/1ns
module tb_iic_control();
reg clk ;
reg res ;
reg iic_start ;
reg wr_en ;
reg rd_en ;
wire [7:0] wr_data ;
wire [15:0] byte_addr ;
//wire addr_num ;
wire sda ;
wire scl ;
wire [7:0] rd_data ;
wire iic_end ;
wire iic_clk ;
reg flag_wr ;
initial begin
wr_en <= 1'b0;
rd_en <= 1'b0;
flag_wr <= 1'b0;
clk <= 1'b0;
res <= 1'b0;
#200 res <= 1'b1;
end
initial begin
iic_start <= 1'b0;
#500 iic_start <= 1'b1;
#1000 iic_start <= 1'b0;
#1000_000 iic_start <= 1'b1;
#1000 iic_start <= 1'b0;
end
always #10 clk <= ~clk;
always@(posedge iic_clk)
if(iic_start == 1'b1&&flag_wr == 1'b0)begin
wr_en <= 1'b1;
rd_en <= 1'b0;
flag_wr <= flag_wr;
end
else if(iic_start == 1'b1&&flag_wr == 1'b1)begin
wr_en <= 1'b0;
rd_en <= 1'b1;
flag_wr <= flag_wr;
end
else if(iic_end == 1'b1)begin
wr_en <= 1'b0;
rd_en <= 1'b0;
flag_wr <= ~flag_wr;
end
assign wr_data = 8'haa;
assign byte_addr = 16'h01_02;
//assign addr_num = 1'b1;
iic_control u_iic_control
(
.sys_clk (clk) ,
.sys_res (res) ,
.iic_start (iic_start) ,
.wr_en (wr_en ) ,
.wr_data (wr_data ) ,
.byte_addr (byte_addr ) ,
.rd_en (rd_en) ,
.addr_num (1'b1 ) ,//高电平代表2B地址
.sda (sda ) ,
.scl (scl ) ,
.rd_data (rd_data ) ,
.iic_end (iic_end ) ,
.iic_clk (iic_clk )
);
M24LC64 M24lc64_inst
(
.A0 (1'b0 ), //器件地址
.A1 (1'b0 ), //器件地址
.A2 (1'b0 ), //器件地址
.WP (1'b0 ), //写保护信号,高电平有效
.RESET (~res ), //复位信号,高电平有效
.SDA (sda ), //串行数据
.SCL (scl ) //串行时钟
);
endmodule
下边是一个24LC64的iic通信测试模板文件
`timescale 1ns/10ps
module M24LC64 (A0, A1, A2, WP, SDA, SCL, RESET);
input A0; // chip select bit
input A1; // chip select bit
input A2; // chip select bit
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
wire [02:00] ChipAddress; // hardwired chip address
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 ADHI_Rcvd; // byte address hi received flag
reg ADLO_Rcvd; // byte address lo 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 RdWrBit; // read/write control bit
reg [12:00] StartAddress; // memory access starting address
reg [04:00] PageAddress; // memory page address
reg [07:00] WrDataByte [0:31]; // memory write data buffer
wire [07:00] RdDataByte; // memory read data
reg [15:00] WrCounter; // write buffer counter
reg [04:00] WrPointer; // write buffer pointer
reg [12:00] RdPointer; // read address pointer
reg WriteActive; // memory write cycle active
reg [07:00] MemoryBlock [0:8191]; // EEPROM data memory array
integer LoopIndex; // iterative loop index
integer tAA; // timing parameter
integer tWC; // timing parameter
// *******************************************************************************************************
// ** INITIALIZATION **
// *******************************************************************************************************
//----------------------------
//------写数据间隔改动----------
initial tAA = 1000; // 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;
ADHI_Rcvd = 0;
ADLO_Rcvd = 0;
MACK_Rcvd = 0;
end
initial begin
BitCounter = 0;
ControlByte = 0;
end
initial begin
WrCycle = 0;
RdCycle = 0;
WriteActive = 0;
end
assign ChipAddress = {A2,A1,A0};
// *******************************************************************************************************
// ** CORE LOGIC **
// *******************************************************************************************************
// -------------------------------------------------------------------------------------------------------
// 1.01: START Bit Detection
// -------------------------------------------------------------------------------------------------------
always @(negedge SDA) begin
if (SCL == 1) begin
START_Rcvd <= 1;
STOP_Rcvd <= 0;
CTRL_Rcvd <= 0;
ADHI_Rcvd <= 0;
ADLO_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;
ADHI_Rcvd <= 0;
ADLO_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:01] == {4'b1010,ChipAddress[02:00]})) 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 RdWrBit = ControlByte[00];
// -------------------------------------------------------------------------------------------------------
// 1.06: Byte Address Register
// -------------------------------------------------------------------------------------------------------
always @(negedge SCL) begin
if (CTRL_Rcvd & (BitCounter == 8)) begin
if (RdWrBit == 0) begin
StartAddress[12:08] <= ShiftRegister[04:00];
RdPointer[12:08] <= ShiftRegister[04:00];
ADHI_Rcvd <= 1;
end
WrCounter <= 0;
WrPointer <= 0;
CTRL_Rcvd <= 0;
end
end
always @(negedge SCL) begin
if (ADHI_Rcvd & (BitCounter == 8)) begin
if (RdWrBit == 0) begin
StartAddress[07:00] <= ShiftRegister[07:00];
RdPointer[07:00] <= ShiftRegister[07:00];
ADLO_Rcvd <= 1;
end
WrCounter <= 0;
WrPointer <= 0;
ADHI_Rcvd <= 0;
end
end
// -------------------------------------------------------------------------------------------------------
// 1.07: Write Data Buffer
// -------------------------------------------------------------------------------------------------------
always @(negedge SCL) begin
if (ADLO_Rcvd & (BitCounter == 8)) begin
if (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:01] == {4'b1010,ChipAddress[02:00]}))) 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
PageAddress = StartAddress[04:00] + LoopIndex;
MemoryBlock[{StartAddress[12:05],PageAddress[04:00]}] = WrDataByte[LoopIndex[04:00]];
end
end
// -------------------------------------------------------------------------------------------------------
// 1.12: Read Data Multiplexor
// -------------------------------------------------------------------------------------------------------
always @(negedge SCL) begin
if (BitCounter == 8) begin
if (WrCycle & ADLO_Rcvd) begin
RdPointer <= StartAddress + WrPointer + 1;
end
if (RdCycle) begin
RdPointer <= RdPointer + 1;
end
end
end
assign RdDataByte = MemoryBlock[RdPointer[12: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] MemoryByte_000 = MemoryBlock[00];
wire [07:00] MemoryByte_001 = MemoryBlock[01];
wire [07:00] MemoryByte_002 = MemoryBlock[02];
wire [07:00] MemoryByte_003 = MemoryBlock[03];
wire [07:00] MemoryByte_004 = MemoryBlock[04];
wire [07:00] MemoryByte_005 = MemoryBlock[05];
wire [07:00] MemoryByte_006 = MemoryBlock[06];
wire [07:00] MemoryByte_007 = MemoryBlock[07];
wire [07:00] MemoryByte_008 = MemoryBlock[08];
wire [07:00] MemoryByte_009 = MemoryBlock[09];
wire [07:00] MemoryByte_00A = MemoryBlock[10];
wire [07:00] MemoryByte_00B = MemoryBlock[11];
wire [07:00] MemoryByte_00C = MemoryBlock[12];
wire [07:00] MemoryByte_00D = MemoryBlock[13];
wire [07:00] MemoryByte_00E = MemoryBlock[14];
wire [07:00] MemoryByte_00F = MemoryBlock[15];
// -------------------------------------------------------------------------------------------------------
// 2.02: Write Data Buffer
// -------------------------------------------------------------------------------------------------------
wire [07:00] WriteData_00 = WrDataByte[00];
wire [07:00] WriteData_01 = WrDataByte[01];
wire [07:00] WriteData_02 = WrDataByte[02];
wire [07:00] WriteData_03 = WrDataByte[03];
wire [07:00] WriteData_04 = WrDataByte[04];
wire [07:00] WriteData_05 = WrDataByte[05];
wire [07:00] WriteData_06 = WrDataByte[06];
wire [07:00] WriteData_07 = WrDataByte[07];
wire [07:00] WriteData_08 = WrDataByte[08];
wire [07:00] WriteData_09 = WrDataByte[09];
wire [07:00] WriteData_0A = WrDataByte[10];
wire [07:00] WriteData_0B = WrDataByte[11];
wire [07:00] WriteData_0C = WrDataByte[12];
wire [07:00] WriteData_0D = WrDataByte[13];
wire [07:00] WriteData_0E = WrDataByte[14];
wire [07:00] WriteData_0F = WrDataByte[15];
wire [07:00] WriteData_10 = WrDataByte[16];
wire [07:00] WriteData_11 = WrDataByte[17];
wire [07:00] WriteData_12 = WrDataByte[18];
wire [07:00] WriteData_13 = WrDataByte[19];
wire [07:00] WriteData_14 = WrDataByte[20];
wire [07:00] WriteData_15 = WrDataByte[21];
wire [07:00] WriteData_16 = WrDataByte[22];
wire [07:00] WriteData_17 = WrDataByte[23];
wire [07:00] WriteData_18 = WrDataByte[24];
wire [07:00] WriteData_19 = WrDataByte[25];
wire [07:00] WriteData_1A = WrDataByte[26];
wire [07:00] WriteData_1B = WrDataByte[27];
wire [07:00] WriteData_1C = WrDataByte[28];
wire [07:00] WriteData_1D = WrDataByte[29];
wire [07:00] WriteData_1E = WrDataByte[30];
wire [07:00] WriteData_1F = WrDataByte[31];
// *******************************************************************************************************
// ** TIMING CHECKS **
// *******************************************************************************************************
wire TimingCheckEnable = (RESET == 0) & (SDA_OE == 0);
wire StopTimingCheckEnable = TimingCheckEnable && SCL;
//--------------------------------
//-------仿真时时序约束需改动--------
//--------------------------------
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
tSU_WP = 600, // WP to SDA setup time
tHD_WP = 1300, // WP to SDA hold 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);
$setup (WP, posedge SDA &&& StopTimingCheckEnable, tSU_WP);
$hold (negedge SDA &&& TimingCheckEnable, negedge SCL, tHD_STA);
$hold (posedge SDA &&& StopTimingCheckEnable, WP, tHD_WP);
endspecify
endmodule
仿真结果:
由于24LC04仿真模型的问题,仿真结果中读出的数据并不理想,所有的高电平默认为高阻态,后期将整个系统下载到开发板上,在signal tap中抓取相应的信号,验证程序的可行性。
初次创作,难免文章中存在错误,希望读者能够及时纠正并给予私信,望大家共同进步!