熟悉一个协议一定要知道这个协议应该用到什么地方,IIC协议作为飞利浦公司定义的一个慢速传输协议,常用于:
1、芯片寄存器的配置;
2、eeprom的读写;
本次实验我们将使用eeprom来讲解IIC协议以及FPGA代码的编写,本次实验所用到的软硬件环境如下:
硬件:黑金A7102开发板
软件:vivado 19.1
黑金A7102开发板上eeprom的型号位:24LC04,是单字节地址的eeprom。再比如小梅哥AC620开发板上的eeprom为双字节地址的器件。为了让同学们了解两者,本次实验我们以24LC64为例进行实验,然后更改为单字节的24LC04并在板上验证。
1、总线空闲状态:SDA为高电平,SCL为高电平
2、起始位:SCL为高电平时,SDA产生一个下降沿
3、结束位:SCL为高电平时,SDA产生一个上升沿
IIC总线的三种模式如下:
标准模式:100Kbit/s
快速模式:400Kbit/s
高速模式:3.4Mbit/s
24LC64芯片的单字节写命令时序图如下:
连续字节写命令如下:
24LC64芯片的单字节读命令时序图如下:
连续字节读命令如下:
同理这里也给出24LC04命令的读写时序图,单字节写命令时序图如下:
连续字节写命令如下:
24LC04芯片的单字节读命令时序图如下:
连续字节读命令如下:
该eeprom控制器的系统框图主要由以下四部分构成:
顶层模块:
详细大家从引脚的定义便可以明白该信号的作用,如果实在明白不了可以进群拿到现成的工程代码,进行仿真观察。
sclk:主时钟信号——50Mhz
rst_n:复位信号低电平有效
wr_reg:写请求信号,单时钟脉冲
rd_req:读请求信号,单时钟脉冲
wr_data:写数据
addr:读写eeprom地址
rd_valid:读数据有效信号,单时钟脉冲
rd_data:读出来的eeprom数据
busy:控制器内部忙信号,1为忙
iic_sck:IIC协议时钟线
iic_sda:IIC协议的数据线
该模块作为顶层模块,主要包括以下三种模块:
1、eeprom_read模块:eeprom的读模块
2、eeprom_write模块:eeprom的写模块
3、ctrl模块:eeprom的读写判决模块
eeprom_read模块的系统框图如下:
信号的含义如下:
rd_flag:读触发信号,高电平有效
rd_done:读操作结束信号
eeprom_write模块的系统框图如下:
信号的含义如下:
wr_flag:写触发信号,高电平有效
wr_done:写操作完成信号
ctrl模块的系统框图如下:
信号的含义如下::
wr_data_r:数据寄存信号
addr_r:地址寄存信号
eeprom_write模块的时序图设计如下:
该时序图知识近似设计,结合该时序图于代码可以直接理解该模块的编写。
eeprom_read模块的时序图设计如下:
ctrl模块的状态机为:
这里将给出整个控制器的逻辑代码,如下:
top模块的代码如下:
`timescale 1ns / 1ps
// *********************************************************************************
// Project Name : OSXXXX
// Author : zhangningning
// Email : [email protected]
// Website :
// Module Name : top.v
// Create Time : 2020-01-14 11:23:02
// Editor : sublime text3, tab size (4)
// CopyRight(c) : All Rights Reserved
//
// *********************************************************************************
// Modification History:
// Date By Version Change Description
// -----------------------------------------------------------------------
// XXXX zhangningning 1.0 Original
//
// *********************************************************************************
module top(
input sclk ,
input rst_n ,
input wr_req ,
input rd_req ,
input [15:0] addr ,
output wire [ 7:0] rd_data ,
output wire rd_valid ,
input [ 7:0] wr_data ,
output wire busy ,
output wire iic_sck ,
inout iic_sda
);
//========================================================================================\
//**************Define Parameter and Internal Signals**********************************
//========================================================================================/
wire rd_done ;
wire wr_done ;
wire wr_flag ;
wire rd_flag ;
wire [15:0] addr_r ;
wire [ 7:0] wr_data_r ;
wire iic_sck_wr ;
wire iic_sck_rd ;
//========================================================================================\
//************** Main Code **********************************
//========================================================================================/
ctrl ctrl_inst(
.sclk (sclk ),
.rst_n (rst_n ),
.wr_req (wr_req ),
.rd_req (rd_req ),
.rd_done (rd_done ),
.wr_done (wr_done ),
.addr (addr ),
.iic_sck_wr (iic_sck_wr ),
.iic_sck_rd (iic_sck_rd ),
.wr_data (wr_data ),
.wr_flag (wr_flag ),
.rd_flag (rd_flag ),
.addr_r (addr_r ),
.wr_data_r (wr_data_r ),
.busy (busy ),
.iic_sck (iic_sck )
);
eeprom_write eeprom_write_inst(
.sclk (sclk ),
.rst_n (rst_n ),
.wr_flag (wr_flag ),
.wr_data (wr_data_r ),
.addr (addr_r ),
.wr_done (wr_done ),
.iic_sck (iic_sck_wr ),
.iic_sda (iic_sda )
);
eeprom_read eeprom_read_inst(
.sclk (sclk ),
.rst_n (rst_n ),
.rd_flag (rd_flag ),
.rd_data (rd_data ),
.rd_valid (rd_valid ),
.addr (addr_r ),
.rd_done (rd_done ),
.iic_sck (iic_sck_rd ),
.iic_sda (iic_sda )
);
endmodule
eeprom_write模块的代码如下:
`timescale 1ns / 1ps
// *********************************************************************************
// Project Name : OSXXXX
// Author : zhangningning
// Email : [email protected]
// Website :
// Module Name : eeprom_write.v
// Create Time : 2020-01-14 09:51:46
// Editor : sublime text3, tab size (4)
// CopyRight(c) : All Rights Reserved
//
// *********************************************************************************
// Modification History:
// Date By Version Change Description
// -----------------------------------------------------------------------
// XXXX zhangningning 1.0 Original
//
// *********************************************************************************
module eeprom_write(
input sclk ,
input rst_n ,
input wr_flag ,
input [ 7:0] wr_data ,
input [15:0] addr ,
output reg wr_done ,
output reg iic_sck ,
inout iic_sda
);
//========================================================================================\
//**************Define Parameter and Internal Signals**********************************
//========================================================================================/
parameter DIV_CNT = 249 ;
parameter DEVICE_ADDR = 8'hA0 ;
reg wr_busy ;
reg [ 7:0] cnt_250 ;
reg [ 5:0] cnt_bit ;
reg sda_oe ;
reg iic_sda_r ;
//========================================================================================\
//************** Main Code **********************************
//========================================================================================/
assign iic_sda = (sda_oe && !iic_sda_r) ? 1'b0:1'bz;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
wr_busy <= 1'b0;
else if(wr_flag == 1'b1)
wr_busy <= 1'b1;
else if(wr_done == 1'b1)
wr_busy <= 1'b0;
always @(posedge sclk)
if(rst_n == 1'b0)
cnt_250 <= 8'd0;
else if(cnt_250 == DIV_CNT || wr_done == 1'b1)
cnt_250 <= 8'd0;
else if(wr_busy == 1'b1)
cnt_250 <= cnt_250 + 1'b1;
else
cnt_250 <= cnt_250;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
cnt_bit <= 5'd0;
else if(wr_done == 1'b1)
cnt_bit <= 5'd0;
else if(cnt_250 == DIV_CNT)
cnt_bit <= cnt_bit + 1'b1;
else
cnt_bit <= cnt_bit;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
iic_sck <= 1'b1;
else if(cnt_bit != 'd37 && cnt_250 == DIV_CNT/2)
iic_sck <= 1'b0;
else if(cnt_250 == 'd0)
iic_sck <= 1'b1;
else
iic_sck <= iic_sck;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
sda_oe <= 1'b1;
else if((cnt_bit == 8 || cnt_bit == 17 || cnt_bit == 26 || cnt_bit == 35) && cnt_250 == (DIV_CNT/2 + DIV_CNT/4))
sda_oe <= 1'b0;
else if((cnt_bit == 9 || cnt_bit == 18 || cnt_bit == 27|| cnt_bit == 36) && cnt_250 == (DIV_CNT/2 + DIV_CNT/4))
sda_oe <= 1'b1;
else
sda_oe <= sda_oe;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
iic_sda_r <= 1'b1;
else case(cnt_bit)
0 : if(cnt_250 == (DIV_CNT/4))
iic_sda_r <= 1'b0;
else if(cnt_250 == (DIV_CNT/2 + DIV_CNT/4))
iic_sda_r <= DEVICE_ADDR[7-cnt_bit];
else
iic_sda_r <= iic_sda_r;
1,2,3,4,5,6 : if(cnt_250 == (DIV_CNT/2 + DIV_CNT/4))
iic_sda_r <= DEVICE_ADDR[7-cnt_bit];
else
iic_sda_r <= iic_sda_r;
7 : if(cnt_250 == (DIV_CNT/2 + DIV_CNT/4))
iic_sda_r <= 1'b0;
else
iic_sda_r <= iic_sda_r;
8 : if(cnt_250 == (DIV_CNT/2 + DIV_CNT/4))
iic_sda_r <= 1'b0;
else
iic_sda_r <= iic_sda_r;
12,13,14,15,16 : if(cnt_250 == (DIV_CNT/2 + DIV_CNT/4))
iic_sda_r <= addr[24-cnt_bit];
else
iic_sda_r <= iic_sda_r;
17 : if(cnt_250 == (DIV_CNT/2 + DIV_CNT/4))
iic_sda_r <= 1'b0;
else
iic_sda_r <= iic_sda_r;
18,19,20,21,22,23,24,25 :
if(cnt_250 == (DIV_CNT/2 + DIV_CNT/4))
iic_sda_r <= addr[25-cnt_bit];
else
iic_sda_r <= iic_sda_r;
26 : if(cnt_250 == (DIV_CNT/2 + DIV_CNT/4))
iic_sda_r <= 1'b0;
else
iic_sda_r <= iic_sda_r;
27,28,29,30,31,32,33,34 :
if(cnt_250 == (DIV_CNT/2 + DIV_CNT/4))
iic_sda_r <= wr_data[34-cnt_bit];
else
iic_sda_r <= iic_sda_r;
35 : if(cnt_250 == (DIV_CNT/2 + DIV_CNT/4))
iic_sda_r <= 1'b0;
else
iic_sda_r <= iic_sda_r;
36 : iic_sda_r <= iic_sda_r;
37 : if(cnt_250 == (DIV_CNT/4))
iic_sda_r <= 1'b1;
default : iic_sda_r <= 1'b1;
endcase
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
wr_done <= 1'b0;
else if(cnt_bit == 'd37 && cnt_250 == (DIV_CNT/2))
wr_done <= 1'b1;
else
wr_done <= 1'b0;
endmodule
eeprom_read模块的代码如下:
`timescale 1ns / 1ps
// *********************************************************************************
// Project Name : OSXXXX
// Author : zhangningning
// Email : [email protected]
// Website :
// Module Name : eeprom_read.v
// Create Time : 2020-01-14 09:52:00
// Editor : sublime text3, tab size (4)
// CopyRight(c) : All Rights Reserved
//
// *********************************************************************************
// Modification History:
// Date By Version Change Description
// -----------------------------------------------------------------------
// XXXX zhangningning 1.0 Original
//
// *********************************************************************************
module eeprom_read(
input sclk ,
input rst_n ,
input rd_flag ,
output reg [ 7:0] rd_data ,
output reg rd_valid ,
input [15:0] addr ,
output reg rd_done ,
output reg iic_sck ,
inout iic_sda
);
//========================================================================================\
//**************Define Parameter and Internal Signals**********************************
//========================================================================================/
parameter DIV_CNT = 249 ;
parameter DEVICE_ADDR = 8'hA0 ;
reg rd_busy ;
reg [ 7:0] cnt_250 ;
reg [ 5:0] cnt_bit ;
reg sda_oe ;
reg iic_sda_r ;
//========================================================================================\
//************** Main Code **********************************
//========================================================================================/
assign iic_sda = (sda_oe && !iic_sda_r) ? 1'b0:1'bz;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
rd_busy <= 1'b0;
else if(rd_flag == 1'b1)
rd_busy <= 1'b1;
else if(rd_done == 1'b1)
rd_busy <= 1'b0;
always @(posedge sclk)
if(rst_n == 1'b0)
cnt_250 <= 8'd0;
else if(cnt_250 == DIV_CNT || rd_done == 1'b1)
cnt_250 <= 8'd0;
else if(rd_busy == 1'b1)
cnt_250 <= cnt_250 + 1'b1;
else
cnt_250 <= cnt_250;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
cnt_bit <= 5'd0;
else if(rd_done == 1'b1)
cnt_bit <= 5'd0;
else if(cnt_250 == DIV_CNT)
cnt_bit <= cnt_bit + 1'b1;
else
cnt_bit <= cnt_bit;
always @(posedge sclk or negedge rst_n)//这里需要改
if(rst_n == 1'b0)
iic_sck <= 1'b1;
else if(cnt_bit != 'd47 && cnt_250 == DIV_CNT/2)
iic_sck <= 1'b0;
else if(cnt_250 == 'd0)
iic_sck <= 1'b1;
else
iic_sck <= iic_sck;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
sda_oe <= 1'b1;
else if((cnt_bit == 8 || cnt_bit == 17 || cnt_bit == 26 || cnt_bit == 36) && cnt_250 == (DIV_CNT/2 + DIV_CNT/4))
sda_oe <= 1'b0;
else if((cnt_bit == 9 || cnt_bit == 18 || cnt_bit == 27 || cnt_bit == 45) && cnt_250 == (DIV_CNT/2 + DIV_CNT/4))
sda_oe <= 1'b1;
else
sda_oe <= sda_oe;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
iic_sda_r <= 1'b1;
else case(cnt_bit)
0 : if(cnt_250 == (DIV_CNT/4))
iic_sda_r <= 1'b0;
else if(cnt_250 == (DIV_CNT/2 + DIV_CNT/4))
iic_sda_r <= DEVICE_ADDR[7-cnt_bit];
else
iic_sda_r <= iic_sda_r;
1,2,3,4,5,6 : if(cnt_250 == (DIV_CNT/2 + DIV_CNT/4))
iic_sda_r <= DEVICE_ADDR[7-cnt_bit];
else
iic_sda_r <= iic_sda_r;
7 : if(cnt_250 == (DIV_CNT/2 + DIV_CNT/4))
iic_sda_r <= 1'b0;
else
iic_sda_r <= iic_sda_r;
8 : if(cnt_250 == (DIV_CNT/2 + DIV_CNT/4))
iic_sda_r <= 1'b0;
else
iic_sda_r <= iic_sda_r;
9,10,11 : iic_sda_r <= iic_sda_r;
12,13,14,15,16 : if(cnt_250 == (DIV_CNT/2 + DIV_CNT/4))
iic_sda_r <= addr[24-cnt_bit];
else
iic_sda_r <= iic_sda_r;
17 : if(cnt_250 == (DIV_CNT/2 + DIV_CNT/4))
iic_sda_r <= 1'b0;
else
iic_sda_r <= iic_sda_r;
18,19,20,21,22,23,24,25 :
if(cnt_250 == (DIV_CNT/2 + DIV_CNT/4))
iic_sda_r <= addr[25-cnt_bit];
else
iic_sda_r <= iic_sda_r;
26 : if(cnt_250 == (DIV_CNT/2 + DIV_CNT/4))
iic_sda_r <= 1'b0;
else
iic_sda_r <= iic_sda_r;
27 : if(cnt_250 == (DIV_CNT/2 + DIV_CNT/4))
iic_sda_r <= 1'b1;
else
iic_sda_r <= iic_sda_r;
28 : if(cnt_250 == (DIV_CNT/4))
iic_sda_r <= 1'b0;
else if(cnt_250 == (DIV_CNT/2 + DIV_CNT/4))
iic_sda_r <= DEVICE_ADDR[35-cnt_bit];
else
iic_sda_r <= iic_sda_r;
29,30,31,32,33,34 :
if(cnt_250 == (DIV_CNT/2 + DIV_CNT/4))
iic_sda_r <= DEVICE_ADDR[35-cnt_bit];
else
iic_sda_r <= iic_sda_r;
35 : if(cnt_250 == (DIV_CNT/2 + DIV_CNT/4))
iic_sda_r <= 1'b1;
else
iic_sda_r <= iic_sda_r;
36 : if(cnt_250 == (DIV_CNT/2 + DIV_CNT/4))
iic_sda_r <= 1'b0;
else
iic_sda_r <= iic_sda_r;
37,38,39,40,41,42,43,44 :
iic_sda_r <= iic_sda_r;
45 : if(cnt_250 == (DIV_CNT/2 + DIV_CNT/4))
iic_sda_r <= 1'b1;
else
iic_sda_r <= iic_sda_r;
46 : if(cnt_250 == (DIV_CNT/2 + DIV_CNT/4))
iic_sda_r <= 1'b0;
else
iic_sda_r <= iic_sda_r;
47 : if(cnt_250 == (DIV_CNT/4))
iic_sda_r <= 1'b1;
default : iic_sda_r <= 1'b1;
endcase
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
rd_done <= 1'b0;
else if(cnt_bit == 'd47 && cnt_250 == (DIV_CNT/2))
rd_done <= 1'b1;
else
rd_done <= 1'b0;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
rd_data <= 8'd0;
else if(cnt_bit >= 'd38 && cnt_bit <= 'd45 && cnt_250 == (DIV_CNT/4))
rd_data <= {rd_data[6:0],iic_sda};
else if(rd_done == 1'b1)
rd_data <= 8'd0;
else
rd_data <= rd_data;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
rd_valid <= 1'b0;
else if(cnt_bit == 'd45 && cnt_250 == (DIV_CNT/4))
rd_valid <= 1'b1;
else
rd_valid <= 1'b0;
endmodule
ctrl模块的代码如下:
`timescale 1ns / 1ps
// *********************************************************************************
// Project Name : OSXXXX
// Author : zhangningning
// Email : [email protected]
// Website :
// Module Name : ctrl.v
// Create Time : 2020-01-14 10:38:04
// Editor : sublime text3, tab size (4)
// CopyRight(c) : All Rights Reserved
//
// *********************************************************************************
// Modification History:
// Date By Version Change Description
// -----------------------------------------------------------------------
// XXXX zhangningning 1.0 Original
//
// *********************************************************************************
module ctrl(
input sclk ,
input rst_n ,
input wr_req ,
input rd_req ,
input rd_done ,
input wr_done ,
input [15:0] addr ,
input [ 7:0] wr_data ,
input iic_sck_wr ,
input iic_sck_rd ,
output reg wr_flag ,
output reg rd_flag ,
output reg [15:0] addr_r ,
output reg [ 7:0] wr_data_r ,
output reg busy ,
output reg iic_sck
);
//========================================================================================\
//**************Define Parameter and Internal Signals**********************************
//========================================================================================/
parameter IDLE = 3'b001 ;
parameter WRITE = 3'b010 ;
parameter READ = 3'b100 ;
reg [ 2:0] state ;
//========================================================================================\
//************** Main Code **********************************
//========================================================================================/
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
addr_r <= 16'd0;
else if(wr_req == 1'b1 || rd_req == 1'b1)
addr_r <= addr;
else
addr_r <= addr_r;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
wr_data_r <= 8'd0;
else if(wr_req == 1'b1)
wr_data_r <= wr_data;
else
wr_data_r <= wr_data;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
state <= IDLE;
else case(state)
IDLE : if(wr_req == 1'b1)
state <= WRITE;
else if(rd_req == 1'b1)
state <= READ;
else
state <= state;
WRITE : if(wr_done == 1'b1)
state <= IDLE;
else
state <= state;
READ : if(rd_done == 1'b1)
state <= IDLE;
else
state <= state;
default : state <= IDLE;
endcase
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)begin
wr_flag <= 1'b0;
rd_flag <= 1'b0;
busy <= 1'b0;
iic_sck <= 1'b1;
end
else case(state)
IDLE : if(wr_req == 1'b1)begin
wr_flag <= 1'b1;
busy <= 1'b1;
end
else if(rd_req == 1'b1)begin
rd_flag <= 1'b1;
busy <= 1'b1;
end
else begin
wr_flag <= 1'b0;
rd_flag <= 1'b0;
busy <= 1'b0;
iic_sck <= 1'b1;
end
WRITE : begin
wr_flag <= 1'b0;
iic_sck <= iic_sck_wr;
end
READ : begin
rd_flag <= 1'b0;
iic_sck <= iic_sck_rd;
end
default : begin
wr_flag <= 1'b0;
rd_flag <= 1'b0;
busy <= 1'b0;
iic_sck <= 1'b1;
end
endcase
endmodule
因为有eeprom的仿真模型文件,所以我们使用仿真模型文件进行仿真测试,
24LC64仿真模型文件:
// *******************************************************************************************************
// ** **
// ** 24LC64.v - Microchip 24LC64 64K-BIT I2C SERIAL EEPROM (VCC = +2.5V TO +5.5V) **
// ** **
// *******************************************************************************************************
// ** **
// ** This information is distributed under license from Young Engineering. **
// ** COPYRIGHT (c) 2009 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.4 **
// ** Modified Date : 02/04/2009 **
// ** Revision History: **
// ** **
// ** 10/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) **
// ** Reduced memory blocks to single, monolithic array **
// ** 02/04/2009: Added timing checks for tSU_WP and tHD_WP **
// ** **
// *******************************************************************************************************
// ** 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 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 = 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;
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
eeprom写模块的测试模块:
`timescale 1ns / 1ps
`define CLOCK 20
// *********************************************************************************
// Project Name : OSXXXX
// Author : zhangningning
// Email : [email protected]
// Website :
// Module Name : eeprom_write_tb.v
// Create Time : 2020-01-13 19:06:55
// Editor : sublime text3, tab size (4)
// CopyRight(c) : All Rights Reserved
//
// *********************************************************************************
// Modification History:
// Date By Version Change Description
// -----------------------------------------------------------------------
// XXXX zhangningning 1.0 Original
//
// *********************************************************************************
module eeprom_write_tb();
reg sclk ;
reg rst_n ;
reg wr_flag ;
reg [ 7:0] wr_data ;
reg [15:0] addr ;
wire wr_done ;
wire iic_sck ;
wire iic_sda ;
pullup (iic_sda);
initial begin
sclk <= 1'b0;
rst_n = 1'b0;
wr_flag = 1'b0;
wr_data = 8'd0;
addr = 16'd0;
#(100*`CLOCK);
rst_n <= 1'b1;
#(100*`CLOCK);
wr_flag = 1'b1;
wr_data = 8'haa;
addr = 16'd0;
#(`CLOCK);
wr_flag = 1'b0;
@(posedge wr_done);
#(100*`CLOCK);
wr_flag = 1'b1;
wr_data = 8'h55;
addr = 16'd1;
#(`CLOCK);
wr_flag = 1'b0;
@(posedge wr_done);
#(100*`CLOCK);
$stop;
end
always #(`CLOCK/2) sclk <= ~sclk;
eeprom_write eeprom_write_inst(
.sclk (sclk ),
.rst_n (rst_n ),
.wr_flag (wr_flag ),
.wr_data (wr_data ),
.addr (addr ),
.wr_done (wr_done ),
.iic_sck (iic_sck ),
.iic_sda (iic_sda )
);
M24LC64 M24LC64_inst(
.A0 (1'b0 ),
.A1 (1'b0 ),
.A2 (1'b0 ),
.WP (1'b0 ),
.SDA (iic_sda ),
.SCL (iic_sck ),
.RESET (1'b0 )
);
endmodule
eeprom读模块的测试模块:
`timescale 1ns / 1ps
`define CLOCK 20
// *********************************************************************************
// Project Name : OSXXXX
// Author : zhangningning
// Email : [email protected]
// Website :
// Module Name : eeprom_read_tb.v
// Create Time : 2020-01-13 21:20:28
// Editor : sublime text3, tab size (4)
// CopyRight(c) : All Rights Reserved
//
// *********************************************************************************
// Modification History:
// Date By Version Change Description
// -----------------------------------------------------------------------
// XXXX zhangningning 1.0 Original
//
// *********************************************************************************
module eeprom_read_tb();
reg sclk ;
reg rst_n ;
reg rd_flag ;
reg [15:0] addr ;
reg iic_sck ;
wire iic_sda ;
wire [ 7:0] rd_data ;
wire rd_valid ;
wire rd_done ;
wire iic_sck_wr ;
wire iic_sck_rd ;
wire wr_done ;
reg wr_flag ;
reg [ 7:0] wr_data ;
pullup (iic_sda);
initial begin
sclk <= 1'b0;
rst_n = 1'b0;
rd_flag = 1'b0;
addr = 16'd0;
#(100*`CLOCK);
rst_n <= 1'b1;
#(100*`CLOCK);
wr_flag = 1'b1;
wr_data = 8'haa;
addr = 16'd0;
#(`CLOCK);
wr_flag = 1'b0;
@(posedge wr_done);
#(100*`CLOCK);
wr_flag = 1'b1;
wr_data = 8'h55;
addr = 16'd1;
#(`CLOCK);
wr_flag = 1'b0;
@(posedge wr_done);
#(100*`CLOCK);
#(100*`CLOCK);
rd_flag = 1'b1;
addr = 16'd0;
#(`CLOCK);
rd_flag = 1'b0;
@(posedge rd_done);
#(100*`CLOCK);
rd_flag = 1'b1;
addr = 16'd1;
#(`CLOCK);
rd_flag = 1'b0;
@(posedge rd_done);
#(100*`CLOCK);
$stop;
end
always #(`CLOCK/2) sclk <= ~sclk;
always @(*)
if(eeprom_write_inst.wr_busy == 1'b1)
iic_sck <= iic_sck_wr;
else if(eeprom_read_inst.rd_busy == 1'b1)
iic_sck <= iic_sck_rd;
eeprom_write eeprom_write_inst(
.sclk (sclk ),
.rst_n (rst_n ),
.wr_flag (wr_flag ),
.wr_data (wr_data ),
.addr (addr ),
.wr_done (wr_done ),
.iic_sck (iic_sck_wr ),
.iic_sda (iic_sda )
);
eeprom_read eeprom_read_inst(
.sclk (sclk ),
.rst_n (rst_n ),
.rd_flag (rd_flag ),
.rd_data (rd_data ),
.rd_valid (rd_valid ),
.addr (addr ),
.rd_done (rd_done ),
.iic_sck (iic_sck_rd ),
.iic_sda (iic_sda )
);
M24LC64 M24LC64_inst(
.A0 (1'b0 ),
.A1 (1'b0 ),
.A2 (1'b0 ),
.WP (1'b0 ),
.SDA (iic_sda ),
.SCL (iic_sck ),
.RESET (1'b0 )
);
endmodule
整个eeprom控制器的测试模块:
`timescale 1ns / 1ps
`define CLOCK 20
// *********************************************************************************
// Project Name : OSXXXX
// Author : zhangningning
// Email : [email protected]
// Website :
// Module Name : top_tb.v
// Create Time : 2020-01-14 15:45:08
// Editor : sublime text3, tab size (4)
// CopyRight(c) : All Rights Reserved
//
// *********************************************************************************
// Modification History:
// Date By Version Change Description
// -----------------------------------------------------------------------
// XXXX zhangningning 1.0 Original
//
// *********************************************************************************
module top_tb();
reg sclk ;
reg rst_n ;
reg wr_req ;
reg rd_req ;
reg [15:0] addr ;
wire busy ;
wire [ 7:0] rd_data ;
wire rd_valid ;
reg [ 7:0] wr_data ;
wire iic_sck ;
wire iic_sda ;
pullup (iic_sda);
initial begin
sclk <= 1'b0;
rst_n = 1'b0;
wr_req = 1'b0;
rd_req = 1'b0;
#(100*`CLOCK);
rst_n = 1'b1;
#(100*`CLOCK);
#(100*`CLOCK);
wr_req = 1'b1;
wr_data = 8'haa;
addr = 16'd0;
#(`CLOCK);
wr_req = 1'b0;
@(negedge busy);
#(100*`CLOCK);
wr_req = 1'b1;
wr_data = 8'h55;
addr = 16'd1;
#(`CLOCK);
wr_req = 1'b0;
@(negedge busy);
#(100*`CLOCK);
rd_req = 1'b1;
addr = 16'd0;
#(`CLOCK);
rd_req = 1'b0;
@(negedge busy);
#(100*`CLOCK);
rd_req = 1'b1;
addr = 16'd1;
#(`CLOCK);
rd_req = 1'b0;
@(negedge busy);
#(100*`CLOCK);
$stop;
end
always #(`CLOCK/2) sclk <= ~sclk;
top top_inst(
.sclk (sclk ),
.rst_n (rst_n ),
.wr_req (wr_req ),
.rd_req (rd_req ),
.addr (addr ),
.rd_data (rd_data ),
.rd_valid (rd_valid ),
.wr_data (wr_data ),
.busy (busy ),
.iic_sck (iic_sck ),
.iic_sda (iic_sda )
);
M24LC64 M24LC64_inst(
.A0 (1'b0 ),
.A1 (1'b0 ),
.A2 (1'b0 ),
.WP (1'b0 ),
.SDA (iic_sda ),
.SCL (iic_sck ),
.RESET (1'b0 )
);
endmodule
相信大家从上面的代码中可以学会eeprom控制器的编写,具体的modelsim测试与上板测试将在下一篇博客中讲解。创作不易,认为文章有帮助的同学们可以收藏点赞支持。(工程也都在群中)对文章有什么看法或者需要更近一步交流的同学,可以加入下面的群: