两个不同的I2C Slave的比较 (by wind330)
工作过程中总是不断地运用现有的技术和代码,也不断地在丢弃有缺陷的技术。
I2C总线是平时接触比较多的一种低速IC配置总线,网络上也有很多现成的Core可以提供你修改及应用。
概述
本文对两个来自不同开源网站的I2C Slave IPCore进行对比及分析其优劣性,它们分别是,。重点说明它们将低速的I2C信号转换到FPGA内部高速处理时钟的过程的区别。
特性区别
OpenCores's I2C Slave
- 不支持CPU总线
- 支持256个寄存器配置(8bits地址线)
Gaisler's I2C Slave
- 支持APB总线
- 可配置允许Slave挂住时钟线(Master和Slave的速率同步)
从上面的特性可以看出,适合用于支持I2C配置的外部芯片;则适于用做SOC外设,同带I2C Master器件进行交互式通信。
电路中的亚稳态
这里有一段来自网络上的中文解释:
这是跨时钟设计中最基础的一个问题(宏观的问题是FIFO),按照我的观察,上论坛问 问题多的一般不明白这个,请一定要注意了。什么是亚稳态?数字电路中的简单双稳 态电路就是两个反相器首尾相连组成(加一些控制逻辑变成了锁存器,触发器),然 而并不像名字显示的,这种电路其实还有第三种半稳定态——就是当两个反相器都处 于中间值的情况——这称之为亚稳态。我们知道反相器在非逻辑值范围的反馈系数是 相当大的,一旦因为干扰或者噪音离开了这个中心点,就会很快地进入逻辑值范围 (稳态)。数学分析,从亚稳态进入稳态,正如放射元素的衰变,是一个指数的规律 (为什么是指数的规律?你要是想不明白,说明你还没有搞明白亚稳态)。那么,亚 稳态的危害到底是什么呢?消耗功率?其实不是(虽然亚稳态消耗很大的功率),亚 稳态的问题在于其电平并不处于有效逻辑电平范围内,而且在变化。这就导致与其相 连其他数字部件将其作出不同的判断(注意,不同),有的作为'1',有的作为'0',有 的也进入了亚稳态,数字部件就会逻辑混乱。那么究竟如何避免(或者减小)亚稳态 的危险呢?注意到亚稳态的触发器继续停留在亚稳态的几率按照指数减少,那么办法 就是等 ——等足够长的时间,直到这个几率变得小的实际上不会发生。到底需要有多 长呢?有的厂商有一个数据,有的没有,按照普通的做法,至少等一个时钟周期——这 也就是所谓的异步数据要用两个触发器打一下。参考链接 1
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使用过,在75M的处理时钟下,该Slave状态机会锁死在Hold状态挂住SCL信号(大概运行一天时间),当时觉得是外部信号太差造成的,所以没有太在意。如今再看才觉得可能是当时处理时钟频率过高,而本身I2C信号轻微毛刺没有被过滤,发生了状态机错误。
总结
通过阅读上述Core的代码,我们可以知道利用高速时钟信号处理低速的通信接口,需要去抖动处理并且根据建立/保持时间再调整滤波处理后的时钟,保证代码的鲁棒性。