wind330

两个不同的I2C Slave的比较

0
阅读(5327)

两个不同的I2C Slave的比较 (by wind330)


工作过程中总是不断地运用现有的技术和代码,也不断地在丢弃有缺陷的技术。

I2C总线是平时接触比较多的一种低速IC配置总线,网络上也有很多现成的Core可以提供你修改及应用。

 

概述

本文对两个来自不同开源网站的I2C Slave IPCore进行对比及分析其优劣性,它们分别是OpenCores' I2C SlaveGaisler's I2C Slave。重点说明它们将低速的I2C信号转换到FPGA内部高速处理时钟的过程的区别。

 

特性区别

OpenCores's I2C Slave

  • 不支持CPU总线
  • 支持256个寄存器配置(8bits地址线)

Gaisler's I2C Slave

  • 支持APB总线
  • 可配置允许Slave挂住时钟线(Master和Slave的速率同步)

从上面的特性可以看出,OpenCores' I2C Slave适合用于支持I2C配置的外部芯片;Gaisler's I2C Slave则适于用做SOC外设,同带I2C Master器件进行交互式通信。

 

电路中的亚稳态

这里有一段来自网络上的中文解释:

这是跨时钟设计中最基础的一个问题(宏观的问题是FIFO),按照我的观察,上论坛问
问题多的一般不明白这个,请一定要注意了。什么是亚稳态?数字电路中的简单双稳
态电路就是两个反相器首尾相连组成(加一些控制逻辑变成了锁存器,触发器),然
而并不像名字显示的,这种电路其实还有第三种半稳定态——就是当两个反相器都处
于中间值的情况——这称之为亚稳态。我们知道反相器在非逻辑值范围的反馈系数是
相当大的,一旦因为干扰或者噪音离开了这个中心点,就会很快地进入逻辑值范围
(稳态)。数学分析,从亚稳态进入稳态,正如放射元素的衰变,是一个指数的规律
(为什么是指数的规律?你要是想不明白,说明你还没有搞明白亚稳态)。那么,亚
稳态的危害到底是什么呢?消耗功率?其实不是(虽然亚稳态消耗很大的功率),亚
稳态的问题在于其电平并不处于有效逻辑电平范围内,而且在变化。这就导致与其相
连其他数字部件将其作出不同的判断(注意,不同),有的作为'1',有的作为'0',有
的也进入了亚稳态,数字部件就会逻辑混乱。那么究竟如何避免(或者减小)亚稳态
的危险呢?注意到亚稳态的触发器继续停留在亚稳态的几率按照指数减少,那么办法
就是等 ——等足够长的时间,直到这个几率变得小的实际上不会发生。到底需要有多
长呢?有的厂商有一个数据,有的没有,按照普通的做法,至少等一个时钟周期——这
也就是所谓的异步数据要用两个触发器打一下。

参考链接 1 2


I2C信号线的时钟域转换

I2C 的常用速率是100kbps或者400kbps,I2C边沿转换的时间可能长达1000ns(Standard-mode),I2C信号允许有50ns宽 度的抖动,所以高速对I2C信号直接两级寄存器消除无法亚稳态。假设当前的用50M高速时钟作为I2C Slave的处理时钟,首先我们需要滤除毛刺,代码如下:

// OpenCores' I2C Slave, Verilog
// debounce sda and scl
always @(posedge clk) begin
  if (rstSyncToClk == 1'b1) begin
    sdaPipe <= {`DEB_I2C_LEN{1'b1}};
    sdaDeb <= 1'b1;
    sclPipe <= {`DEB_I2C_LEN{1'b1}};
    sclDeb <= 1'b1;
  end  else begin
    sdaPipe <= {sdaPipe[`DEB_I2C_LEN-2:0], sdaIn};
    sclPipe <= {sclPipe[`DEB_I2C_LEN-2:0], scl};
    if (&sclPipe[`DEB_I2C_LEN-1:1] == 1'b1)
      sclDeb <= 1'b1;
    else if (|sclPipe[`DEB_I2C_LEN-1:1] == 1'b0)
      sclDeb <= 1'b0;
    if (&sdaPipe[`DEB_I2C_LEN-1:1] == 1'b1)
      sdaDeb <= 1'b1;
    else if (|sdaPipe[`DEB_I2C_LEN-1:1] == 1'b0)
      sdaDeb <= 1'b0;
  end
end
// Gaisler's I2C Slave, VHDL
----------------------------------------------------------------------------
-- Bus filtering
----------------------------------------------------------------------------
for i in 0 to 3 loop
  sclfilt(i) := r.i2ci(i+2).scl;
  sdafilt(i) := r.i2ci(i+2).sda;
end loop;  -- i
if sclfilt = "1111" then v.scl := '1'; end if;
if sclfilt = "0000" then v.scl := '0'; end if;
if sdafilt = "1111" then v.sda := '1'; end if;
if sdafilt = "0000" then v.sda := '0'; end if;

从 上述代码可以看到,两个I2C Slave都对I2C信号进行过滤消除抖动,不同的是Opencores' I2C Slave可以通过DEB_I2C_LEN可以通过过滤带宽,而Gaisler's I2C Slave,当处理速度达到80M时,将无法滤除50ns宽度的抖动,再加上建立/保持时间冲突引起的亚稳态,FPGA将无法处理I2C信号。在 Opencores' I2C Slave中,在48MHz的处理速度下DEB_I2C_LEN为10,这样可以过滤的脉冲宽度是208ns,但是我有一个疑问:处理时钟在采样I2C信 号的边沿跳变时,而边沿跳变长度达到最差情况的1000ns,是否会产生错误的I2C信号?例如在边沿的采样值为 20'b11111111110000000000,而I2C SCL刚好处在上升沿情况,如此,SCL多产生一个错误的上升沿。
 

使用情况

wind330使用过Gaisler's I2C Slave,在75M的处理时钟下,该Slave状态机会锁死在Hold状态挂住SCL信号(大概运行一天时间),当时觉得是外部信号太差造成的,所以没有太在意。如今再看OpenCores' I2C Slave才觉得可能是当时处理时钟频率过高,而本身I2C信号轻微毛刺没有被过滤,发生了状态机错误。

 

总结

通过阅读上述Core的代码,我们可以知道利用高速时钟信号处理低速的通信接口,需要去抖动处理并且根据建立/保持时间再调整滤波处理后的时钟,保证代码的鲁棒性。