数字集成电路设计实践 IIC-Slave接口芯片的全流程设计

news/2024/9/20 6:45:46/文章来源:https://www.cnblogs.com/VeriMaverick/p/18374252

数字集成电路设计实践  IIC-Slave接口芯片的全流程设计

一、 芯片设计方案

IIC原理

1. I2C协议

I2C协议由Philips公司推出。

1.1. 端口名称及含义

标准I2C只有2根信号线。

SCL:Serial CLock:串行时钟线,由主机产生并分享给从机。

SDA:Serial DAta:串行数据线,连接在主从机之间。把发送信号的一方叫做发送机,接收信号的叫做接收机。发送/接收机是随时改变的。

I2C仅有两根线,比SPI要简单得多。但是其代价为,SDA线的通信格式比较复杂,且相对固定。

1.2. 传输协议格式

1.2.1. 起始(S)和终止条件(P)

一般不区分起始条件和重复起始条件(S和Sr)。S和P的定义如下图。

 

1.2.2. 有效信号时间

如果在SCL=1期间SDA变动,那么必然是S/P,如果想要传输完整的信号,那么信号在SCL=1期间必须保持稳定,只能在SCL=0期间变化。

1.3. 传输格式

SDA线上传输的每次必须为一个字节的信息,可以传输多个字节。一个字节的信息可以是从机地址或者传输的信息。在每个字节的信息后都跟着从机的一位应答信号(ACK或A,从机拉低低信号为有效应答)。注意,在数据位,主机控制了SDA线;但在最后一位应答位时,发送机必须放出SDA线,让接收机去控制,如果接收机成功地拉低了,就说明接收机成功响应了

I2C协议先传输MSB。或者说,I2C是big endian传输的。

在完整的传输过程中,第一个字节一般是 “主机发送7位地址+主机发送R/W+从机Ack” 的格式,如下图所示。

其中第一个字节的七位具有一些预留的格式。这些预留的格式保证了I2C可以与CBUS协议兼容。

1.4. 多个主机连接时的线与机制

I2C要求连接到总线的接口输出端都为OD门(MOS工艺)或OC门(BJT工艺)。比如下图是OD门连接方式。

在OD门连接中,先假设只有一个主机连接,即输出端为 “上拉电阻+NMOS” 的组合,那么其输出必然是:

  1. NMOS开通,输出被拉低到0。
  2. NMOS关断,输出被电阻上拉到1。

现在再考虑多个主机连接的情况,一旦有一个NMOS开通,那么其他的NMOS管即使关断也没有用,电平一样会被拉下来,也就是 “输出只要有一个低,那么结果就是低” ,构成了 “线与” 的功能。这个线与功能是I2C完成仲裁的基础。

当然,这里只是从理论上描述一下I2C的OD门运行机制。具体还要看芯片的制造工艺、芯片管脚的电路模型等等。

1.5. 多主机连接时的仲裁机制

多个主机申请控制时的仲裁机制:

1.5.1. 多个主机的时钟同步

首先是多个主机的时钟如何同步。因为各个主机在SCL上也是用线与连接的,所以SCL的高低是可以确定的。

        产生的同步SCL时钟的低电平周期由低电平时钟周期最长的器件决定,而高电平周期由高电平时钟周期最短的器件决定。比如在上图中,即使CLK1先达到了高,但是因为SCL未到达高,因此CLK1需要等待至SCL变高,然后开始计时高电平时间,这样几个周期下来就完成了时钟同步。

1.5.2. 主机申请数据传输时的仲裁机制

        如下图是SCL相同时的SDA仲裁。在DATA2还处于低电平的时候,DATA1率先到达高,因此其丢失仲裁。丢失仲裁后,DATA1的输出级与SDA暂时断开,以免影响到DATA2的传输。

 

二 、芯片前端设计

I2C的VerilogHDL代码实现

I2C代码来自Copyright (C) 2008 Steve Fielding and OPENCORES.ORG

其基本思想是:利用一个时钟周期远小于SDA、SCL变化的时钟clk的边沿作为判断时刻。将整个I2C接口分为两个功能模块:串行传输线SCL和SDA信号的处理(serialInterface)、接收到SDA信号后存进移位寄存器完成串并转换的过程(registerInterface)。

真正困难的是如何读取SDA线上的信号,代码module名称为serialInterface.v,其思想是把每个可能性都转化为三段式状态机,根据状态机的流转,去做到 “判断开始/结束”“读数据”“Ack/NAck” 等功能。

2.1.1. 关于I2C SDA总线在芯片中的Pad的解释

I2C的SDA端口是一个双向接口,但是在芯片中,SDA只是一个Pad(意即只有一个管脚)。因此在芯片的输出管脚附近需要类似三态门的特殊的结构来让两个方向的信号互不干扰。

如上图所示,P是Pad,A是sdaOut,Y是sdaIn,PU是上拉使能,PU==1时允许上拉到电源。IE、OE本为输入/输出使能,但这里全部接高(即一直使能)。

如果从机希望把sda总线放掉,那么sdaOut必须在从机端口就被切断。如图中,当sdaOut<=0时,NMOS打开,输出被拉低,输出为0;当sdaOut<=1时,NMOS关闭,输出被PMOS拉高,为1。但是注意,此时的1是被电源和上拉电阻拉高的,因此可以再被拉低。这也就是说,只要sdaOut==1,sdaOut与SDA总线就是断开的。

如果需要多个从机,那么需要保证多个从机总共只有一个PU使能(OD门接法)。

关于sdaIn和sdaOut的关系,还可以从OD门的“线与”逻辑理解:

\[sdaOut=0\implies sda=sdaIn\cdot sdaOut=0 \]

\[sdaOut=1\implies sda=sdaIn\cdot sdaOut=sdaIn \]

下面很多代码以此为基础。这也是代码中始终没有出现过z高阻的理由。

以上说的是芯片中的一种特殊Pad。如果芯片没有用这种特殊Pad,那么就需要外挂上拉电阻。

因此使用I2C器件时一定注意查看Datasheet,确定是否需要上拉电阻。一种常见的上拉方式是:上拉4.7 K电阻到3.3 V电压。

2.1.2. serialInterface的流程图和代码解释

 

serialInterface.v就是控制从机对于SDA线的接收和发送。要接收或者发送的数据都暂存在register里面,由registerInterface控制传给serialInterface处理。

2.1.3. registerInterface的解释

控制着多个寄存器,这些寄存器每个都是一字节。寄存器的数量是不限制的,但是寄存器太多,地址就会变长,也会占用空间。对于本实验,甚至一个寄存器就足够了。

2.1.4. i2cSlave的连接和部分代码解释

以上是各个模块的连接方式。

此外,i2cSlave还有一些其它功能,比如说考虑实际延迟而做出的校正,比如对S/P的判断。下面解释一些代码块。

2.1.4.1. i2cSlave.v接口

 1 registerInterface u_registerInterface(2   .clk(clk),3   .addr(regAddr),4   .dataIn(dataToRegIF),5   .writeEn(writeEn),6   .dataOut(dataFromRegIF),7   .myReg0(myReg0),8   .myReg1(myReg1),9   .myReg2(myReg2),
10   .myReg3(myReg3),
11   .myReg4(myReg4),
12   .myReg5(myReg5),
13   .myReg6(myReg6),
14   .myReg7(myReg7)
15 );
16 
17 serialInterface u_serialInterface (
18   .clk(clk), 
19   .rst(rstSyncToClk | startEdgeDet), 
20   .dataIn(dataFromRegIF), 
21   .dataOut(dataToRegIF), 
22   .writeEn(writeEn),
23   .regAddr(regAddr), 
24   .scl(sclDelayed[`SCL_DEL_LEN-1]), 
25   .sdaIn(sdaDeb), 
26   .sdaOut(sdaOut), 
27   .startStopDetState(startStopDetState),
28   .clearStartStopDet(clearStartStopDet) 
29 );

2.1.4.2. rst复位功能

1 // sync rst rsing edge to clk
2 always @(posedge clk or posedge rst) begin
3   if (rst == 1'b1)
4     rstPipe <= 2'b11;
5   else
6     rstPipe <= {rstPipe[0], 1'b0};
7 end
8 
9 assign rstSyncToClk = rstPipe[1];

rst并不是一直使得电路reset的。如果rst保持很长时间的1,那么也只会复位一次。

容易画出电路的时序图。可以发现,rstSynToClk只在至多两个posedge clk时刻保持了高电平。这使得电路能够复位,但又不会在rst信号不撤去的时候一直复位导致电路无法运行。

2.1.4.3. debounce防抖逻辑

抖动,就是指上升/下降沿的时刻出现偏差。

根据代码i2cSlave_define.v的解释:

The rise time of SCL and SDA can be up to 1000ns (in standard mode), so it is essential to debounce the inputs.

也就是说,SCL和SDA的沿时间太长了,这使得在边沿变化过程中也可能遇到posedge clk,从而容易出现错误。我们希望在SCL/SDA的电平稳定了之后再进行采样。

debounce电路,就是常说的去抖滤波,主要用在芯片的PAD输入信号,或者模拟电路输出给数字电路的信号上。在本实验中,即SCL和SDA。

 1 // debounce sda and scl2 always @(posedge clk or posedge rstSyncToClk) begin3   if (rstSyncToClk == 1'b1) begin4     sdaPipe <= {`DEB_I2C_LEN{1'b1}};5     sdaDeb <= 1'b1;6     sclPipe <= {`DEB_I2C_LEN{1'b1}};7     sclDeb <= 1'b1;8   end9   else begin
10     sdaPipe <= {sdaPipe[`DEB_I2C_LEN-2:0], sdaIn};
11     sclPipe <= {sclPipe[`DEB_I2C_LEN-2:0], scl};
12     if (&sclPipe[`DEB_I2C_LEN-1:1] == 1'b1)
13       sclDeb <= 1'b1;
14     else if (|sclPipe[`DEB_I2C_LEN-1:1] == 1'b0)
15       sclDeb <= 1'b0;
16     if (&sdaPipe[`DEB_I2C_LEN-1:1] == 1'b1)
17       sdaDeb <= 1'b1;
18     else if (|sdaPipe[`DEB_I2C_LEN-1:1] == 1'b0)
19       sdaDeb <= 1'b0;
20   end
21 end

显然,debounce只有在信号电平变化之时才需要。在代码中,设立一个reg,称作xxxPipe,预设全为1。然后该数字序列会随着时钟一直刷新,直到整个序列变得一致为止,这说明电平已经稳定了。

而xxxDeb代表了debounce后给出的电平判断结果。

2.1.4.4. 信号延时

 1 // delay scl and sda2 // sclDelayed is used as a delayed sampling clock3 // sdaDelayed is only used for start stop detection4 // Because sda hold time from scl falling is 0nS5 // sda must be delayed with respect to scl to avoid incorrect6 // detection of start/stop at scl falling edge. 7 always @(posedge clk or posedge rstSyncToClk) begin8   if (rstSyncToClk == 1'b1) begin9     sclDelayed <= {`SCL_DEL_LEN{1'b1}};
10     sdaDelayed <= {`SDA_DEL_LEN{1'b1}};
11   end
12   else begin
13     sclDelayed <= {sclDelayed[`SCL_DEL_LEN-2:0], sclDeb};
14     sdaDelayed <= {sdaDelayed[`SDA_DEL_LEN-2:0], sdaDeb};
15   end
16 end

延时的道理很简单,把debounce后的信号电平放到序列的最后一位,随着时间逐渐左移,这样就完成了延时。

在i2cSlave_define.v文件中,可以看到: \(t_{debounce}=t_{delay,scl}=2.5t_{delay,sda}\)

正如代码注释所说,scl做延时是为了比sda延时更多;sda做延时是为了检测S/P。在scl和sda都进行了debounce之后,波形如图下图所示:

原来sda的边沿与scl边沿可能贴得很近,但是由于不同的延时,sclDeb和sdaDeb的边沿差距很大,这样S/P的判断就很难出现错误了。

2.1.4.5. 判断S/P

 1 // start stop detection2 always @(posedge clk or posedge rstSyncToClk) begin3   if (rstSyncToClk == 1'b1) begin4     startStopDetState <= `NULL_DET;5     startEdgeDet <= 1'b0;6   end7   else begin8     if (sclDeb == 1'b1 && sdaDelayed[`SDA_DEL_LEN-2] == 1'b0 && sdaDelayed[`SDA_DEL_LEN-1] == 1'b1)9       startEdgeDet <= 1'b1;
10     else
11       startEdgeDet <= 1'b0;
12     if (clearStartStopDet == 1'b1)
13       startStopDetState <= `NULL_DET;
14     else if (sclDeb == 1'b1) begin
15       if (sdaDelayed[`SDA_DEL_LEN-2] == 1'b1 && sdaDelayed[`SDA_DEL_LEN-1] == 1'b0) 
16         startStopDetState <= `STOP_DET;
17       else if (sdaDelayed[`SDA_DEL_LEN-2] == 1'b0 && sdaDelayed[`SDA_DEL_LEN-1] == 1'b1)
18         startStopDetState <= `START_DET;
19     end
20   end
21 end

因为给serialInterface的rst=rstSynToClk | startEdgeDet,所以在startEdgeDet<=1时serialInterface会reset,从机状态置为START。

i2cSlave_define.v注释:

SDA should have a minimum of 100ns of set up time, with respect to SCL rising edge. But with the very slow edge speeds used in I2C it is better to err on the side of caution.

i2cSlave.v注释:

Because sda hold time from scl falling is 0ns, sda must be delayed with respect to scl to avoid incorrect detection of start/stop at scl falling edge.

在sclDeb==1时可以检测S/P。假如说主机给入的是上图所示的信号,那么在SCL下降时刻SDA基本也就开始变化了,因此这样不太稳妥,容易导致在SCL为高有效时采不到正确的数据。因此,在I2C从机内部把SCL延迟地比SDA多一些,就会解决这个问题。

同时因为SDA信号被寄存器存下来了,因此后面可以慢慢用来判断S/P,而不用担心边沿过去了。

2.1.5. i2cSlaveTop的解释

i2cSlaveTop.v就是完全把i2cSlave的功能封装起来。除了I/O配置、实例运用,没有加入新的功能。

2.1.6. 代码的Verilog仿真

在仿真中,认为方波边沿时间为0,对于数字电路,这个假设是合理的,因此后面的仿真一直如此。

在实际的芯片配置和PCB连接中,sdaIn和sdaOut是用OD门连在一起的。但是在代码仿真里不需要考虑这些,它们是完全独立的信号。

进行代码仿真(Xilinx Vivado):

在图中,sdaIn==1’bz的时间是等待从机Ack的时间(虽然因为两条线是分开的,sdaIn是多少其实无所谓的,不过用来区分开每个字节也不错)。

可以看到,内容依次为:

传输位数

传输的方向以及字节内容

传输代表的意义

/

scl=1期间,sdaIn下降

S(开始信号)

8

主机向从机写0111_1000

主机寻从机址011_1100(7’h3c),R/W=0(W)

1

主机等待从机应答

从机给出应答Ack

8

主机向从机写0000_0010

主机寻寄存器址0000_0010(8’h02),即myReg02

1

主机等待从机应答

从机给出应答Ack

8

主机向从机写1010_0110

主机向从机寄存器myReg02写数据1010_0110

1

主机等待从机应答

从机给出应答Ack

8

主机向从机写0011_0110

主机向从机寄存器myReg03写数据0011_0110

1

主机等待从机应答

从机给出应答Ack

/

scl=1期间,sdaIn上升

P(结束信号)

不限

任务结束,从机空闲

/

scl=1期间,sdaIn下降

S(开始信号)

8

主机向从机写0111_1000

主机寻从机址011_1100(7’h3c),R/W=0(W)

1

主机等待从机应答

从机给出应答Ack

8

从机向主机写1010_0110

从机向主机发送myReg02数据(Addr复位时默认置8’h02)

1

从机等待主机应答

主机给出不应答Nak,代表从机发送终止

注意:后面即使再用主机给出Ack去尝试激活从机继续发送数据,那也没用了。从机发送被主机终止,这个任务就已经结束了,只是主机还没有给出结束信号。

 

       可以大概计算一下各信号宽度。I2C标准的传输速率为100 Kb/s(S)、400 Kb/s(F)、3.4 Mb/s(Hs),其实传输速率指的就是SCL时钟频率,因为每个SCL周期传输1 bit数据,分别计算其每位宽度为10 μs、2.5 μs、294.12 ns。在图示代码中用200ns作scl半周期,运行完全没有问题,因为clk半周期设为5 ns,相比与scl、sda来说太小了。所以速率的问题应该不用担心。

      标准传输速率其实表示的只是芯片能容许的最大SCL传输速率,如果实际SCL速率比标称速率高就可能无法正常传输,因为此时可能不满足时钟clk频率远大于SCL频率这一条件。实际上,SCL总是由主机提供给从机,所以只要从机的标称速率能达到主机提供的SCL速率,从机接收(接收数据也是主机给的,必然与SCL速率吻合)或发送的数据率都能匹配SCL速率。

      I2C标准的clk时钟频率好像没有标准的要求,只要比SCL、SDA频率高得多即可。为了和整个芯片的200 MHz内置频率匹配,可采用四分频,即50MHz的频率,也比较接近。经过测试,在20ns的clk周期下,scl周期设为1 μs,是完全没有问题的。一般I2C传输速率都在 百K~几M 频率范围内,而提供给从机模块的时钟最小也有几十MHz,因此此模块在任何情况下基本都能工作。

这里I2C也不太纠结速率,也不太纠结速率是否标准,因此上述数据就可以了。

 

状态机:

数据通道:

 

 

 

 

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/785474.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

反序列化刷题(二)

反序列化刷题 web259(SSRF) 1、explod(,,"hello,ju,hey"):把字符串以逗号为判断点分为若干个数组,hello ju hey 2、array_pop($x):删除数组中的最后一个元素1、$_SERVER[HTTP_X_FORWARDED_FOR]用来获取数据包的IP地址;我们目标要ip=127.0.0.1; 这里可以用x-fo…

POD基本操作

一.Pod生命周期 在Kubernetes中,Pod的生命周期经历了几个重要的阶段。下面是Pod生命周期的详细介绍: 1.Pending(待处理):调度: Pod被创建后,首先进入“Pending”状态。此时,Kubernetes的调度器(Scheduler)会选择一个合适的节点来运行Pod。资源分配: 在调度器选择了节点…

C++ SPFA算法解析

前言 将了解 C++ 求最短路中 SPFA 的算法 SPFA SPFA的一些说明 SPFA:适用于权值有负值,且没有负圈的图的单源最短路径,论文中的复杂度O(kE),k为每个节点进入Queue的次数,且k一般<=2,但此处的复杂度证明是有问题的,其实SPFA的最坏情况应该是O(VE).! 引例: 输入格式 给…

.Net8 部署到IIS 10 上需要注意的点

现在大部分项目都上云了,而且是linux的系统,这部分我下一篇再讲,这次讲一下如何部署到iis10,首先项目点击发布-》目标框架.net 8 部署模式是独立,目标运行时是win-x64, 你也可以选择部署模式为依赖框架,目标运行时选择可移植,但是这样的话要注意IIS的应用程序池选择启用…

单文件抽奖小工具(不放回抽)

单文件抽奖小工具(不放回抽) 创建时间:2024-08-12 一、HTML 部分 这段 HTML 代码构建了抽奖小工具的页面结构。引入了 jQuery 库用于后续的 JavaScript 操作,定义了两个音频元素用于播放抽奖相关音效。h1 标签显示“抽奖”标题,span 标签用于显示时间,wrapDiv 包含了抽奖…

【C#】.NET报错:所生成项目的处理器框架“MSIL”与引用“xxx”的处理器架构“AMD64”不匹配

一、现象所生成项目的处理器架构“MSIL”与引用“System.Data.SQLite, Version=1.0.60.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139, processorArchitecture=x86”的处理器架构“AMD64”不匹配。这种不匹配可能会导致运行时失败。请考虑通过配置管理器更改您的项目的…

算法与数据结构——数据结构的分类

数据结构的分类 常见的数据结构包括数组、链表、栈、队列、哈希表、树、堆、图,它们可以从“逻辑结构”和“物理结构”两个维度进行分类 逻辑结构:线性与非线性 逻辑结构揭示了数据元素之间的逻辑关系。在数组和链表中,数据按照一定顺序排列,体现了数据之间的线性关系;而在…

Python 实现批量数字二维码生成器

Python 实现批量数字二维码生成器 创建时间:2024-08-09 一、背景 手动逐个生成特定格式和内容的二维码是一项繁琐且耗时的任务。虽然有写二维码工具也可以制作,但是往往有一些限制,为了能够高效、批量生成自定义二维码的需求,开发了这个基于 Python 的数字二维码生成器应用…

【工程应用十一】基于PatchMatch算法的图像修复研究(inpaint)。

这个东西是个非常古老的算法了,大概是2008年的东西,参考资料也有很多,不过基本上都是重复的。最近受一个朋友的需求,前后大概用了二十多天时间去研究,也有所成果,在这里简单的予以记录。图像修复这个东西目前流行的基本都是用深度学习来弄了,而且深度学习的效果还是非常…

jmeter基本操作

发送一个post请求 1、创建一个线程2、新建一个http请求编辑http请求的内容接口断言: 响应参数:{"code":"200","msg":"登录成功!","model":{}}查看结果:保存,运行 a、保存:b、运行红色表示错误 绿色表示成功 查看请求后…

Blazor开发框架Known-V2.0.9

V2.0.9 Known是基于Blazor的企业级快速开发框架,低代码,跨平台,开箱即用,一处代码,多处运行。本次版本主要是修复一些BUG和表格页面功能增强。官网:http://known.pumantech.com Gitee: https://gitee.com/known/Known Github:https://github.com/known/Known概述基于C#…

antd5版本修改Table组件滚动条样式

因为项目需求,要将Table组件的样式修改为UI图所给的效果,但是怎么写都不生效 最后发现在 .ant-table的样式中设了scrollbar-color,只要把这里的样式设为scrollbar-color: auto; 然后再改.ant-table-body里面滚动条的样式,就可以了.ant-table-body{&::-webkit-scrollbar…