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