两两三三网

I2C介绍及verilog实现(主机/从机可综合)

I2C介绍及verilog实现(主机/从机可综合)

I2C介绍及verilog实现(主机/从机可综合)

目录

1.简介

2.基本特征

3.物理连接

4.数据格式

4.1快速模式和低速模式(F/S)写

 4.2快速模式和低速模式(F/S)读

4.3高速模式(Hs)读/写

 4.4连续多次读/写

5.时序

5.1开始位和停止位

 5.2字节传输时序

5.3字节内传输顺序

6.功能描述及模块分析

7.具体设计

7.1主机模块

7.1.1设计思路

7.1.2master状态机

7.2.从机模块

8.I2C顶层接inout口处理

9.代码及仿真

9.1测试模型结构

 9.2测试结果


1.简介

I2C是介绍及一种只有2条线的串行通信协议。可用于IC内部通信,实现也可以用于IC间的主机通信,广泛用于开关电源、从机触控芯片、可综简单的介绍及显示芯片等。

2.基本特征

(1)2条通信线,实现SDA数据线,主机SCL时钟线。从机

(2)串行的可综8-bit双向数据传输,速率分为:

  • 低速模式/标准模式(standard-mode),介绍及100 kbit/s;
  • 快速模式(fast-mode),实现400 kbit/s;
  • 加强快速模式(Fast-mode Plus),主机1 Mbit/s;
  • 高速模式(high-speed),从机3.4 Mbit/s;

(3)在超快速模式下(UFm),可综最快可达5Mbit/s(单向传输)。

(4)可支持多mater,多slave。

3.物理连接

 在bus上可以接入多个主机、多个从机,需要接入上拉电阻,在无操作时,线上电压为高电平。

(本文介绍的所有内容仅牵涉单主机,多主机内容和参考附件1)

4.数据格式

在F/S模式和Hs模式下的数据格式不同,其中又分读数据格式、写数据格式。

  •  白心方框——从机传输给主机
  • 灰色方框——主机传输给从机
  • A       ——响应位
  • A       ——不响应位
  • S       ——开始位
  • P       ——停止位

4.1快速模式和低速模式(F/S)写

       数据传输从开始位开始,接着传输一个7位地址(slave address,这里不介绍10bits地址)和1bit读写位(0:写,1:读)。这里进行写操作,因此读写位为0。接着主机会接收来至从机的1bit响应位(1:未响应,0:响应ok)。若此时从机响应,则主机接着传输需要写入的数据(8 bits),未响应则进入停止位。每一字节传输后都需要接收一次响应,若从机响应再继续传输,若从机未响应,则主机直接进入停止位。最后,当数据传输完成后,主机进入停止位,完成传输。

        slave address用于寻找对应的芯片,例如,一个电路板上有多个开关电源,但我们只想用一个I2C主机控制,则可以将所有的SDA和SCL分别接到一起,并给不同的开关电源分配不同的地址。在主机发出开始位地址位时,所有的开关电源(从机)都会接收到该信号,并将接收的地址同设置好自身的地址对比,若相同,则在响应位拉低SDA,响应主机,否则保持高电平。

 4.2快速模式和低速模式(F/S)读

4.3高速模式(Hs)读/写

Hs模式和F/S模式的数据格式基本相同,只是在开始位后会先发送master code。发送完master code后不需要响应,接着进入Hs模式。传输完成后,可发送停止位或者发送再开始位(Sr)进入下一次传输。

master code为可理解成Hs的标志,传输完master code后就可认为主机进入了Hs模式,若从机支持Hs模式,也应该进入Hs模式。master code是一个8位数据(0000_1xxx),应该注意0000_1000是测试用的保留字段,不能作为master code。同时也应该注意,slave address需避开master code的特征(参考文献1)

 4.4连续多次读/写

5.时序

5.1开始位和停止位

从图中可看出,开始位需要满足的条件:SCL高电平时,SDA由高变低;停止位需要满足的条件:SCL为高电平时,SDA由低变高。(Sr和S的时序一致)

 5.2字节传输时序

结合5.1可看出,SDA电平变化不能在SCL为高电平时进行,否则可能会被误认为成开始位或者停止位。因此SDA只能在SCL低电平时改变。

5.3字节内传输顺序

 在字节内,先传输MSB。

6.功能描述及模块分析

关于I2C介绍网上资料很多,这里介绍了一些重点知识点,朋友可自行网上学习。下面开始说明verilog的具体实现。

(1)实现将fifo传过来的8bits数据转换成I2C串行数据输出的主机和接收数据从机

(2) 这里用到的fifo由vivado ip直接产生。主机使用一个时钟wclk,一个异步复位(不采用I2C协议的软复位)。wclk分频模块分频后用于产生SCL,分频系数可配。

(3)在设计从机时,可以使用SCL和SDA设计时序电路实现数据接收,但这种设计并不灵活,很难添加其它功能,因此在此设计仍采用单独时钟用于接收数据。

(4)通常slave会先将SDA和SCL先滤波,这里不设计(后面单独讲一下如何滤波)。

(5)计划分4个模块,一个fifo发送模块,一个fifo接收模块,一个I2C主机模块,一个I2C从机模块。

特征:

  • 支持I2C主机读写、I2C从机读写
  • 支持Hs、F/S模式
  • 支持分频系数可配
  • 支持读写连续帧
  • 从机被主机读时,若从机数据没准备好,可进入等待状态,同时拉低SCL,直到slave的txfifo有数据写入
  • 从机被写入数据时,若slave的rxfifo满时,可进入等待状态,直到rxfifo的数据被读出

7.具体设计

7.1主机模块

7.1.1设计思路

从数据格式可看出,一次完整的I2C传输至少包含起始位、停止位、响应位、数据传输位,Hs模式时还需要传输master code位,因此I2C的状态也至少包含以上这些状态。另外,由于时序特征,SDA只能在SCL低电平时改变,因此在设计时,可以再细分三个状态将1bit数据分三段传输,开始位和停止位可分为2段。该设计则是用一个计数器的方式控制SDA、SCL的电平,并为再细分。

7.1.2master状态机

关于状态机的具体设计,可参考如下图,该状态机基本可满足现阶段常用的I2C从机。这里不做具体分析,建议配合代码理解。

7.2.从机模块

这里同样给出从机的状态机

8.I2C顶层接inout口处理

如代码所示,在设计时,sda、scl分为输入和输出4条线,但芯片外部却只有2条线。这需要如何实现呢?这就牵涉inout类型端口处理。

我们看下面这张图,out信号可以通过en0使能信号控制输出。对应到i2c,总线是有上拉电阻的,所以不操作总线时,总线就是高电平,那么我们只需要在out输出低电平时打开使能即可,即en0 = ~out。对于输入,设想当out为0时,en1也打开,那就没办法接收slave传回的真正信号了,因为不管slave输出什么,此时in肯定时0。那么en1需要在out为高时打开,及en1 = out。这就是为什么有人说只要master和slave一个为低,总线也为低。其实协议的响应位也是考虑了这个因素,理解了这层意思,才能更好理解i2c协议,在设计时,也能考虑清楚各个状态机下的电平赋值。

在fpga设计中代码如下:

assign sda    = (sda_out)? 1'bz:sda_out;

assign sda_in  = (sda_out)?sda:1'b1;

在ic设计中,pad有专门的模块,对设计者来说只需要生成对应的en信号即可。具体实现方式和上图类似,只是不需要设计人员写代码,而是用专门的cell模块处理。

9.代码及仿真

详细代码较长,放在下载区:

I2C主机及从机Verilog代码实现.zip-硬件开发文档类资源-CSDN下载

9.1测试模型结构

通过vivado搭建仿真平台,生成fifo ip,代码结构如图(仿真时未使用master的rx):

 

 9.2测试结果

前8位为master code,然后进入hs模式,master传输6个数据,接着master读6个数据。 

 

测试代码如下:

`timescale 1ns/1psmodule i2c_test();//assign sda = (msda_out)? 1'bz:msda_out;//assign msda_in  = (msda_out)?sda:1'b1;reg       wclk         ;reg       rst_wclk_n   ;reg       i2c_en       ;wire      txfifo_empty ;wire [7:0]tx_data      ;wire      sda_out      ;wire      scl_out      ;wire      txfifo_rd_en ;wire [7:0]rx_data      ;  reg       wr_en ;reg [7:0]     din;reg       swr_en;reg [7:0]   sdin;wire [7:0] stx_fifo_dat;wire [7:0] s_rx_dat;//clockinitialbegin    wclk = 1'b0;    forever #50 wclk = ~wclk;end//resetinitialbegin    rst_wclk_n = 1'b0;    #5000;    rst_wclk_n = 1'b1;end//master controlinitialbegin    i2c_en = 1'b0 ;     #6000;            repeat(6)             begin              @(posedge wclk)begin                    din    <= $random%255;                    wr_en  <= 1'b1;                end                  end                    repeat(6)             begin              @(posedge wclk)begin                    sdin    <= $random%255;                    swr_en  <= 1'b1;                end                  end                       @(posedge wclk)            begin                i2c_en <= 1'b1;                wr_en   <= 1'b0;            end            #20000;            force i2c_master.scl_in = 1'b0;            #500;            release i2c_master.scl_in;endi2c_master i2c_master(      .wclk        (wclk         ),      .rst_wclk_n  (rst_wclk_n   ),      .i2c_en      (i2c_en       ),      .txfifo_empty(txfifo_empty ),      .hs_mode     (1'b1         ),      .data_wr_rd  (1'b1         ),      .master_code (8'b1001_0001 ),      .slave_addr  (8'b1010_1010 ),      .tx_data     (tx_data      ),      .i2c_comb_wr (1'b1         ),      .rx_byte_num (8'd4         ),      .div_num     (8'd100        ),      .hs_div      (8'd10        ),      .sda_in      (ssda_out     ),      .scl_in      (sscl_out     ),      .sda_out     (sda_out      ),      .scl_out     (scl_out      ),      .txfifo_rd_en(txfifo_rd_en ),      .rx_data     (rx_data      ),      .rx_wr_en_out(rx_wr_en_out ));i2c_slave i2c_slave(    .sscl_in           (scl_out           ),      .ssda_in           (sda_out           ),      .wclk              (wclk              ),      .rst_wclk_n        (rst_wclk_n        ),      .slave_en          (1'b1              ),      .stxfifo_empty     (stxfifo_empty     ),        // .srxfifo_full      (srxfifo_full      ),         .stx_fifo_dat      (stx_fifo_dat      ),         .rxfifo_almost_full(rxfifo_almost_full),              .master_code       (8'b1001_0001      ),       .slave_addr        (8'b1010_1011      ),       .sscl_out          (sscl_out          ),       .ssda_out          (ssda_out          ),       .rx_sdat            (s_rx_dat          ),       .srxfifo_en        (srxfifo_en        ),    .stx_fifo_rd_en    (stx_fifo_rd_en   ));i2c_txfifo master_txfifo (  .clk(wclk),            // input wire clk  .srst(~rst_wclk_n),    // input wire srst  .din(din),             // input wire [7 : 0] din  .wr_en(wr_en),         // input wire wr_en  .rd_en(txfifo_rd_en),  // input wire rd_en  .dout(tx_data),        // output wire [7 : 0] dout  .full(),               // output wire full  .empty(txfifo_empty)   // output wire empty);slave_rxfifo  slave_rxfifo(  .clk(wclk),                  // input wire clk  .srst(~rst_wclk_n),                // input wire srst  .din(s_rx_dat ),                  // input wire [7 : 0] din  .wr_en(srxfifo_en),              // input wire wr_en  .rd_en(rd_en),              // input wire rd_en  .dout(dout),                // output wire [7 : 0] dout  .full(srxfifo_full),                // output wire full  .almost_full(rxfifo_almost_full),  // output wire almost_full  .empty(empty)              // output wire empty);slave_txfifo slave_txfifo (  .clk(wclk),      // input wire clk  .srst(~rst_wclk_n),    // input wire srst  .din(sdin),      // input wire [7 : 0] din  .wr_en(swr_en),  // input wire wr_en  .rd_en(stx_fifo_rd_en),  // input wire rd_en  .dout(stx_fifo_dat),    // output wire [7 : 0] dout  .full(full),    // output wire full  .empty(stxfifo_empty)  // output wire empty);endmodule

附件1:I2C-bus specification and user manual(忘了免费下载地址了……🤦‍)

 

未经允许不得转载:两两三三网 » I2C介绍及verilog实现(主机/从机可综合)