Menu Close

I2C接口EEPROM 芯片控制器(Controller)设计以及芯片Verilog模型仿真

I2C接口在小颗非易失性存储芯片EEPROM上经常使用。复习I2C接口特点,速度规定及读写操作过程参考文章I2C接口基础知识一,I2C接口基础知识二。本文在I2C接口设计文章I2C 主设备分频器设计,I2C 主设备启动及读写控制Verilog程序设计的基础上,实现I2C EEPROM 芯片Controller的设计及联合I2C EEPROM 芯片Verilog模型仿真。之前的SPI Verilog模型的仿真验证( SPI EEPROM 芯片Verilog模型仿真,SPI Flash Verilog 程序 仿真 )比较简单,只是在test bench文件中模拟了接口协议。本次工程的仿真验证将会基于一个完整设计的I2C EEPROM 芯片Controller,并且如果想要在EEPROM芯片之外的工程中使用I2C Controller只需要稍加改造就可以实现。

1. I2C EEPROM芯片Controller设计

对于I2C EEPROM芯片Controller设计,本文采用了模块化的方法,将一个相对复杂的Controller分为了3个主要的子模块:I2C 时钟分频器模块,I2C读写操作实现模块和I2C命令调用模块。I2C 时钟分频器模块产生I2C操作的分频信号,用来控制发送时钟,或发送接收数据的频率。I2C读写操作实现模块使用I2C 时钟分频器模块的baudtick,并在I2C命令调用模块命令的作用下进行I2C读写操作。三个模块之间信号的交流如图1所示:

I2C接口EEPROM 芯片控制器(Controller)设计以及芯片Verilog模型仿真

图1

  • I2C命令调用模块(I2C_engine.v)

目前在I2C命令调用模块中没有进行调用,只是将相关的控制信号封装了一层,再传递出去,代码如下所示:


 

可以看出在I2C命令调用模块I2C_engine.v中除了例化I2C时钟分频器模块SCL_en.v和I2C读写操作实现模块i2c_phy.v之外,最重要的是将inout信号SDA和SCL进行合并:

assign SDA = sda_dev_out ? 1’bZ : 1’b0;
assign SCL = scl_dev_out ? 1’bZ : 1’b0;

 

  • I2C时钟分频器模块(SCL_en.v)

I2C的数据/时钟的发送和接收都是由I2C时钟分频模块输出的clk_en和clk_en_half控制的,这里完全使用参数化的设置,可以按照设计改变I2C的速度。代码如下:


 

其中参数CLK_F指的是当前整个模块的系统时钟频率,SPEED指的是I2C频率。这里也是以I2C 4倍频率的信号控制(四个相位分别用两个二倍频的clk_en和clk_en_half错沿对齐),示意图如图2所示:

图2

DIV_COE是计算出来的分频系数,通过赋值给div_f_r(分频计数的阈值)信号,在代码主体中用于分频产生clk_en和clk_en_half两个信号。除了可以通过DIV_COE设置I2C频率,还可以通过设置正确的div_addr = 32’hFFFF_0001和置高div_en信号将div_f_r赋值为div_in。对应代码如下所示:


 

主要产生clk_en和clk_en_half两个信号的分频部分如下:


 

  • I2C读写操作实现模块(i2c_phy.v)

I2C读写操作实现模块接收I2C命令调用模块传递的控制信号来进行I2C的读写操作,如图3所示。start,stop用于控制I2C读写操作实现模块发送start和stop位。i2c_op表示当前读或写操作。rd_last表示连续读的最后一个读操作。同样,I2C读写操作实现模块也传递给I2C命令调用模块一些反馈信号。i2c_op_rdy表示I2C命令调用模块传递过来的命令已经操作完成,比如start位已经发出,已经成功读出一个字节等。i2c_ack输出在写操作的时候slave设备回过来的ACK信号,注意在本工程中没有对i2c_ack信号进行判断,因为本模块也支持除I2C协议外的两线通信接口(two-wire interface,其通信协议中没有强制规定ACK必须是低电平,也可能是高电平),如果有需要可以自行添加判断ACK信号。

除此之外,可以从图3看到,I2C读写操作实现模块接收准备写的数据,并提供读操作获取的数据。时钟线主要由master发出,所以FPGA模块只输出SCL时钟,但是对于FPGA来说,可能接收数据,也可能发送数据。

图3

模块代码如下:


 

模块中主要实现了一个状态机i2c_st,关于其状态转移示意图如图4所示,注意图中大部分状态的转移条件都是由I2C时钟分频器模块提供的clk_en:

 

图4

在i2c_st状态机的状态0中判断当前模块需要执行什么操作,可能是start,stop,读还有写,操作分为如下:

    • 状态0可以跳到状态1拉出start波形

实现start波形的代码如下(部分截取):


 

start对应的波形如图5所示:

图5

从图5可以看出start波形是保持SCL为高电平,SDA由高电平变为低电平,也就是说在SCL为高电平期间,将SDA拉出下降沿。注意SDA和SCL的波形高低电平都是由clk_en触发的。在start位结束后,返回状态0,并将i2c_op_rdy置位。

    • 状态0跳到状态4拉出stop波形

生成stop波形的代码如下(部分截取):


 

对应的stop波形如图6所示。

图6

通过先将SCL置高,SDA保持为低电平,之后再只将SDA置位,就可以产生stop波形。也就是说,在SCL为高电平期间,将SDA拉出上升沿。同样的注意SDA和SCL的波形高低电平都是由clk_en触发的。在生成stop位后,返回状态0,并将i2c_op_rdy置位。

    • 状态0跳到状态7进行写操作

在I2C_phy.v模块中一次完整的写操作写一个字节,代码如下(部分截取):


 

上示代码中还附带了状态0,因为在状态0中信号sft_count被置为7。状态7和8配合将i2c_wr_val中的8个bit按clk_en和clk_en_half的节奏发送,信号sft_count作为i2c_wr_val 的bit索引使用。

对应的波形图如图7所示:

图7

从图7可以看出,状态7和状态8配合发送一个bit,不断重复,将一个完整字节的bit 7 – bit 0依次发送,与I2C协议的先发MSB是一致的。在状态9,10和11中再多发出一个时钟周期,等待I2C协议接收方回复ACK。在写操作完成后,返回状态0,并将i2c_op_rdy置位。

    • 状态0跳到状态12进行读操作

读操作对应代码如下(部分截取)。在I2C_phy.v模块中,一次完整的读操作也只读出1个字节。如果需要读出多个字节,需要由上层控制重复读操作。和写操作类似,读操作需要使用信号sft_count来索引读出的bit,并组合成一个完整的字节。sft_count在状态0被置为7。


 

图8所示为读操作的时序图,可以看到状态11和状态12配合读出一个bit,在读完一个字节后,状态14,15发出ACK,状态16和17返回状态0,并将i2c_op_rdy置位。注意在状态15中还判断了是否读最后一个字节(rd_last_r),如果读的是最后一个字节,返回NACK。如果是连续读中间的一个字节,返回ACK,符合I2C协议规定。

图8

图9所示为连续读中间的一个字节,可以看到SDA发出的是ACK。

图9

图10所示为读最后一个字节,SDA发出的是NACK。

图10

 

2. I2C EEPROM芯片Verilog模型仿真

与之前使用SPI EEPROM芯片Verilog模型仿真类似,在仿真中验证I2C controller,如果仿真正确可以再进行编译,进一步的调试。本文选择的是Microchip芯片AT24C02D。

  • 在Microchip公司选择芯片的步骤如下所示:
    • 在Microchip官网主菜单Tools and Resources下选择Documentation,如图11所示:

图11

 

    • 在界面左侧点击打开Other Resources > Verilog Models,如图12所示,选择本文将要使用的I2C接口EEPROM芯片AT24C02D:

图12

AT24C02D.v文件下载(已打包成zip):

[content_control]

AT24C02D.zip

[/content_control]

 

  • 添加仿真test bench文件tb_i2c.v调度I2C controller来与AT24C02D芯片沟通

图13所示为I2C test bench tb_i2c.v模块给I2C命令调用模块提供命令调用信号。在仿真时,start,stop,i2c_op,rd_last等信号都由tb_i2c.v模块提供。

 

 

图13

tb_i2c.v代码如下:


 

因为tb_i2c.v用来控制读写EEPROM芯片AT24C02D,参考AT24C02的芯片技术手册,AT24C02D EEPROM芯片容量为2 Kbit,也就是256个字节(256 X 8 bit)。页写最多可以写8个字节,超过8个字节会写错,回卷到之前的值(rollover)。每次写操作过后,需要等待EEPROM 5 ms的时间。模块中主要实现了一个状态机st,关于其状态转移示意图如图14所示

图14

tb_i2c.v模块操作可以分解如下:

    • 写操作(状态0-5)


 

EEPROM页写操作示意图如图15所示:

图15

对应上述状态后,如图16所示:

图16

状态5将stop信号置低,注意状态6在等待了5 ms后才重新开始下一次的操作。

仿真图如图17示,可以看出I2C波形符合设计,从地址0开始,写了8个byte的数,分别为8’h00-8’h07。

图17

    • 读操作(状态6-13)


 

EEPROM随机读 + 连续读 操作示意图如图18所示:

 

图18

对应上述状态后,如图19所示:

 

图19

状态13将stop信号置低。仿真图如图20所示,可以看出I2C波形符合随机读 + 连续读的设计,从地址0开始,读出了8个byte的数,分别为8’h00-8’h07。

图20

 

    • 改变I2C速度(14-15)


 

在状态14改变div_in,置位使能div_en,以及在tb_i2c.v模块中将div_addr设置为32’hFFFF_0001,使得将相关信号可以传递近SCL_en.v模块,改变分频的计数,从而改变clk_en和clk_en_half的频率, 如下SCL_en代码(部分截取)所示:


 

仿真图如图21所示,可以明显看出原本I2C的速度是通过设置传入SCL_en.v模块的参数SPEED,进而设置DIV_COE得到的100k,之后在状态14修改成400k的I2C速度。

图21

 

3. 总结

本文设计了一个通用的两线制协议(包括I2C) controller,并使用芯片产家提供的芯片Verilog模型配合controller测试。仿真图结果与对应的芯片操作时序图一致,也证明了I2C controller设计正确,包括芯片编程等待的时间5 ms也很好的模拟了出来。总的来说,使用芯片Verilog模型仿真是一种很好的验证手段,可以在没有真实EEPROM器件的时候,验证controller 的正确性。

 

参考代码下载:

[content_control]

test_i2c.zip

[/content_control]

Posted in FPGA, FPGA 教材教案, FPGA开发板, Verilog, Vivado, Vivado, 元器件, 存储器, 开发工具, 开发工具, 开发板, 开发语言, 教材与教案, 文章, 编程语言, 资料区