首页
📁归档
⏳时光机
📫留言
🚩友链
💰资助名单
推荐
🎧音乐
🏜️ 壁纸
❤ 捐助
Search
1
【NPN/PNP三极管】放大电路饱和失真和截止失真的区别
19,408 阅读
2
论文写作中如何把word里面所有数字和字母替换为新罗马字体
10,255 阅读
3
【高数】形心计算公式讲解大全
8,816 阅读
4
【概论】一阶矩、二阶矩原点矩,中心矩区别与概念
7,530 阅读
5
Vivado-FPGA Verilog烧写固化教程
7,014 阅读
🪶微语&随笔
励志美文
我的随笔
写作办公
📡电子&通信
嵌入式&系统
通信&信息处理
编程&脚本笔记
⌨️IC&系统
FPGA&ASIC
VLSI&IC验证
EDA&虚拟机
💻电子&计算机
IP&SOC设计
机器学习
软硬件算法
登录
18(共101篇)
找到
101
篇与
18
相关的结果
- 第 3 页
2023-03-08
天线设计2-电磁学基础与线形天线
第三章 辐射积分与辅助函数 求解辐射场的分析中,通常需要构造辅助函数来帮助求解。常见的辅助位函数为,磁矢量A- (也称为磁矢位)。 图片 远场辐射 很多天线的辐射场使用球面坐标来表示会较为方便,其一般形式是 图片 重要公式 图片 线形天线 Linear Wire Antenna 线形天线是最古老、最便宜、最简单、应用最广泛的天线。因此我们尝试从最小的天线结构和最简单的几何形状开始分析。我们以极小偶极子的辐射场特性为例,讲解辐射场的一般求解过程。 极小偶极子 Infinitesimal Dipole 极小偶极子对应于导线很短,很细,电流近似均匀分布的情况。 图片 图片 求解辐射场 图片 图片 这样我们就得到了极小偶极子的辐射电场和磁场。以上的分析在源以外的区域都是有效的。 功率密度和辐射电阻 利用前面计算的结果和坡印廷矢量的计算公式 图片 辐射场分布 前面所推导的极小偶极子的电磁场的分布 在源以外的区域都是适用的,但在不同的区域,场的一些特性可能有所区别。 近场 kr <<1 图片 中场 kr>1 图片 远场 kr>>1 在远场时,径向的电场几乎为0。能流密度垂直于径向,以辐射波为主。 从近场到远场的过渡动画: 动画 方向性 图片 一般远场辐射特性的求解过程 前面我们以极小偶极子为例,求解了极小偶极子的辐射特性。一般远场辐射特性的计算过程如下: 指定电流或者磁流密度 确定辅助场A 确定远场的E和H 计算(a)能流密度或(b)辐射强度 计算辐射功率 计算极化 计算归一化的功率图 计算辐射电阻和输入电阻 图片 有限长度偶极子 极小偶极子是一种将电流分割成无数小电流源的近似,实际情况中,需要求解的往往是有限长度的偶极子。我们假设电流在导线上的分布是正弦形式的,电流密度可以写成: 图片 图片 图片 图片 辐射电阻 根据前面辐射功率和辐射电阻的关系,可以得到 图片 图片 辐射电抗 图片 输入电阻 输入端看到的电阻与天线的辐射电阻未必是相同的。因为导线上的电流分布发生了变化。 图片 半波偶极子 图片 辐射阻抗 辐射电阻: 图片 图片
通信&信息处理
# 天线设计
刘航宇
3年前
0
1,199
2
天线设计-微带天线小型化技术
天线作为无线电通信的桥梁,是实现无线通信的关键。随着无线通信技术和电子技术的发展,日常生活中的无线电子产品变得越来越小,越来越薄,越来越轻,而功能变得越来越强大。天线的小型化跟不上电子设备小型化的步伐,经常成为无线电子产品体积缩小的瓶颈。理论上讲,天线的工作波长与天线尺寸成正比。换句话说,要想降低天线的工作频率就要增大天线的尺寸,这就使得天线的小型化成为了研究的难点,实现微带天线小型化设计的主要手段有: 1)提高介质基板的介电常数; 2)曲流技术; 3)短路加载技术; 4)附加有源网络; 5)应用用电磁带隙结构; 6)应用左手介质; 目录 1、提高介质基板的介电常数 曲流技术贴片表面开槽 接地板开槽 短路加载技术 附加有源网络 应用用电磁带隙结构 应用左手介质 1、提高介质基板的介电常数 图片 图片 曲流技术 我们知道增加天线的有效长度可以降低天线的谐振频率。曲流技术就是我们常说的表面开槽技术,它的实质就是增加了天线的有效长度,从而达到天线小型化的目的。 贴片表面开槽 图片 图(3-1)为表面开槽后的辐射贴片电流路径分布。从图中我们可以看出辐射贴片的电流路径在开槽处发生弯曲,有效地延长了电流路径,相当于天线的有效长度变大了。从而在不改变天线几何尺寸的情况下,降低了天线的谐振频率。 微带天线的辐射贴片表面上,各个共振模式的电流分布均不相同。如果在相同的共振模式下开槽,就会改变原有的共振模式的电流路径,延长电流路径,使得天线的共振波长变大。所所开凹槽的长度会影响天线的谐振频率,凹槽越长则天线的谐振频率越低。不过,所开凹槽的宽度不宜过大,太大会降低天线的辐射性能。 跟采用高介质常数基底的方法一样,辐射贴片表面开槽也有其弊端。表面开槽后天线会.产生垂直于主激发面的额外电流,从而增加了天线的交叉极化,使得天线的辐射效率降低。除此之外,开槽后天线的相对辐射面积就减小了,从而影响到天线的增益。 接地板开槽 图片 图片 短路加载技术 基本的矩形微带天线的为工作波长的二分之一,基本谐振模式为TM。,其电流在两个开路端之间成驻波分布。所以在两个开路端之间有--条零电位线。如果我们在此零电位线处让其接地,将微带天线的另一半舍去,就可以在开路和短路之间形成驻波分布,而不改变天线的内部场分布。这样一来天线的尺寸就减小 了一半,实现了天线小型化的目的。短路加载微带天线的方法有很多,包括短路面加载,短路片加载和短路探针加载。短路加载的数量,每个短路加载的面积以及天线的高度决定了短路加载的效果。图(3-3)分别给出了不同加载方式的示意图。 图片 图片 图(3-3a)是短路面加载的微带天线,它的尺寸缩小到了四分之一工作波长。所以此微带天线的尺寸比半波结构的微带天线尺寸减小了一-般。图(3-3a)所示的短路片加载微带天线和图(3-3b)所示的短路探针微带天线的结构比较相似,但实际上它们加载的结构带宽不一样。 附加有源网络 由于天线的辐射电阻会随着天线尺寸的减小而减小,所以天线尺寸的减小会降低天线的效率。除此之外,天线的带宽也常常因为天线尺寸的减小而降低。而天线性能的恶化会影响整个无线收发系统的性能,甚至使系统无法正常工作。 有源网络的放大作用和阻抗补偿技术可以用来弥补由天线尺寸减小而引起的天线性能下降的问题 有源天线具有以下优点: 1)频带宽; 2)高增益; 3)容易实现阻抗匹配;. 但是有源网络会影响天线的互易性。 应用用电磁带隙结构 电磁带隙结构(Electromagnetic B and-Gap)是周期结构的统称,包括光子带隙(Photonic Band Gap, PB)、频率选择表面(Frequency Select Surface, FS)以 及光子晶体(photonic Crystal,PC)等周期结构。电磁波与周期结构互相作用的时候,会出现--些如频率禁带、通带以及频率间隙等特性。 电磁带隙结构是微带辐射贴片的下方及周围、天线的基片内钻出或刻蚀出一系列间隔非常近的小孔(≤h/10),通过改变孔间距和孔的大小来改变有效介电常数。EBG 是人造的周期性结构,在此种结构中,一-定范围内的电磁波无法传播。将电磁带隙结构附加在天线辐射贴片的背面,可以抑制天线的表面波,从而实现天线小型化的目的。 目前,比较常见的电磁带隙结构有: 1)基底打孔型; 2)高阻抗表面型; 3)地面腐蚀型; 4)夹层式结构; 5)共面紧凑型; 应用左手介质 图片 图片 图片 如图(3-5)所示,将左手介质和右手介质相叠加,当电磁波在当中传播时,由于介质两边相位相反,左手介质会对右手介质进行相位补偿,相位变化会完全抵消。这样,由辐射贴片、右手介质、左手介质以及接地板所组成的微带天线的谐振方程不再依赖于d,和d,而只取决于d2/d,.由于传统的谐振腔谐振至少需要半个波长,而这种结构就突破了这个限制,从而很好地降低了天线的高度,实现了微带天线小型化的目的。
通信&信息处理
# 天线设计
刘航宇
3年前
0
3,566
7
AMBA--AHB总线协议介绍(一)
1、AHB总线概述 AHB:Advanced High-performance Bus,即高级高性能总线。AHB总线是SOC芯片中应用最为广泛的片上总线。下图是一个典型的基于AMBA AHB总线的微控制器系统: 图片 SOC架构 图片 基于AMBA AHB的设计中可以包含一个或多个总线主机,通常一个系统里至少包含一个处理器和一个测试接口;DMA和DSP作为总线主机同样是比较常见的。 典型的AHB总线设计包括一下几个部分: (1)AHB主机:主机可以通过提供地址和控制信息发起读写操作;同一时刻总线上只允许一个主机占用总线。 (2)AHB从机:从机需在给定的地址空间范围内响应总线上的读或写操作;从机通过信号将成功、失败、等待数据传输等信息返回至有效的主机。 (3)AHB仲裁器:总线仲裁器确保同一时刻只有一个主机被允许发起传输。所有的AHB总线都必须包含一个仲裁器,即使是在单主机总线系统中。 (4)AHB译码器:译码器的作用是对传输中的地址信号进行译码,并提供给从机一个选择信号。所有的AHB总线中都必须包含一个中央译码器。 2、总线互联 AMBA AHB总线协议被设计为一个使用中央多路选择器的互联方案,如下表: 图片 基于这种方案,所有的主机在需要发起传输时,都可以驱动地址和控制信号,由仲裁器决定哪一个主机的地址和控制信号(写传输时包含写数据信号)连通到总线上的所有从机。同时总线需要一个中央译码器,用于控制读数据和应答信号的多路数据选择器,该多路数据选择器用于选择传输中涉及的从机的相关信号。 3、AHB信号 AHB信号以字母H作为前缀,如下表所示 NameWidthSourceDescriptionHCLK1bit时钟源总线所有传输都基于此时钟,所有信号的时序都与时钟上升沿有关HRESETn1bit复位控制器总线复位信号,总线上唯一低电平有效的信号HADDR32bit主机32位地址总线HTRANS2bit主机表明当前传输的类型HSIZE2bit主机表明传输的大小,典型的是字节(8-bit)、 半字(16-bit)、字(32-bit), 协议允许最大传输大小是1024 bitHWRITE1bit主机传输写信号,高电平表示写传输,低电平表示读传输HBURST3bit主机有自己的HBURST信号有自己的HBURST信号HPROT4bit主机保护控制信号。主要应用于期望实现某些程度的保护级别的模块中HWDATA32bit主机写传输数据。要求最低位宽位32位HSELx1bit译码器从机选择信号,每个AHB从机都有自己的HSEL信号,该信号有效时,表明选中相应从机HRDATA32bit从机读数据总线,用于读操作期间从机向主机传输数据HREADY1bit从机该信号为高时,表明总线传输完成。也可以拉低该信号用以扩展传输HRESP2bit从机传输响应信号。提供四种响应OKAY、ERROR、RETRY、SPLITAMBA AHB也需要一些信号用于支持多主机总线操作,其中一些仲裁信号需要点对点连接: NameWidthSourceDescriptionHBUSREQx1bit主机主机x发送至仲裁器的总线请求信号,总线系统中最多有16个主机,每个主机都有一个HBUSREQ信号HLOCKx1bit主机该信号为高时,表示主机x发起锁定传输HGRANTx1bit仲裁器表明主机x在当前总线上拥有最高的优先级,当HREADY信号为高时,地址/控制信号的所有权将发生变化,故主机x需在HREADY与HGRANTx信号同时为高时,获得总线控制权HMASTER4bit仲裁器表明哪一个主机当前拥有地址/控制信号的所有权HMASTLOCK1bit仲裁器表明当前传输为锁定顺序传输,此信号与HMASTER具有相同时序HSPLITx16bit从机用于指示仲裁器,哪一个主机可以继续完成分块传输4、AMBA AHB操作概述 在一次AHB传输开始之前,主机必须被授予总线访问权。这个过程起始于主机向仲裁器发出一个总线请求,由仲裁器决定该主机何时被授予总线使用权。被授权的主机通过驱动地址和控制信号来发起一次AHB传输,这些信号提供了地址、传输方向、传输宽度等信息,以及会表明当前传输是否为突发传输的一部分。AHB支持两种形式的突发传输: (1)增量突发,在地址边界不进行回环; (2)回环突发,在特定地址边界回环 写数据总线用于将数据从主机发送到从机,读数据总线用于将数据从从机传输到主机。 每次数据传输包含: (1)一个地址和控制周期; (2)一个或多个数据周期 由于地址不支持扩展,所以所有的从机必须在地址周期内采样地址,而数据可以通过HREADY信号进行扩展延长,当HREADY信号为低,总线将插入等待状态,以此提供从机额外的采样数据或者提供数据的时间。 在传输中,从机使用应答信号HRESP[1:0]来表示传输状态: (1)OKAY:OKAY响应表示传输正常,且当HREADY信号为高时,表示传输成功; (2)ERROR:ERROR响应表示发生了传输错误,并且传输失败; (3)RETRY and SPLIT:RETRY和SPLIT都表示当前传输未能即刻完成,但是主机应继续尝试传输 一般而言,仲裁器授权另一个主机前,允许当前被授权主机完成突发传输。然而,为了避免过多的仲裁延迟(当前主机占用过多总线周期),总线可以打断一个突发传输,在这种情况下,主机必须重新申请总线,以完成后续的突发传输。 5、基本传输 一个AHB传输包含两个部分: (1)地址相位,仅包含一个时钟周期; (2)数据相位,可以使用HREADY信号,维持多个时钟周期 下图表示了一个无等待状态的简单传输: 图片 在无等待的简单传输中,主机在HCLK的上升沿驱动地址和控制信号到总线上,在下一个时钟上升沿时,从机对地址和控制信号进行采样,从机采样得到地址和控制信息后,可以驱动应答信号给与主机适当的响应,主机在第三个时钟的上升沿对应答信号进行采样。 这个简单的例子演示了在不同的时钟周期地址和数据相位是怎样产生的。实际上,当前传输的地址相位都对应于前一次传输的数据相位,这种地址和数据交叠现象,是总线能够进行流水线传输的基础,这样不仅能够获得更高的传输性能,也能为从机进行响应提供充足的时间。 从机可以在数据相位中,插入等待状态,以此获得更多得时间来完成传输: 图片 注意: (1)在写传输中,主机需要在整个扩展周期内保持写数据信号稳定 (2)在读传输中,从机没必要提供有效数据,知道传输结束时 当传输以这种方式做扩展时,将对下一个传输的地址相位产生一个扩展的副作用,如下图所示: 图片 该图表示了三个不相关的地址A、B、C上的传输,图中地址A和C的传输都没有等待状态,地址B的传输通过HREADY信号的拉低插入了一个等待的数据相位,这样导致了地址C传输的地址相位进行了扩展。 6、传输类型 AHB传输类型可以分为四种,通过HTRANS[1:0]的取值来划分: HTRANSTypeDescription00IDLE表明没有数据传输的要求。IDLE 用于主机被授予总线,但不希望进行数据传输的情况。对于IDLE传输,从机必须提供一个零等待的OKAY应答,并且忽略该传输01BUSYBUSY传输类型允许主机在突发传输中插入空闲周期。表明主机正在进行突发传输,但下次传输不能立即有效。当主机使用BUSY传输类型时,地址和控制信号必须对应突发中的下一次传输。从机必须提供一个零等待的OKAY应答,并且忽略该传输。10NONSEQ表示突发传输中的第一次传输或者非突发的单次传输,地址和控制信号与前次传输无关.11SEQ突发传输中剩余的传输时连续的,地址和前一次传输是相关的,当前地址值等于前一次传输的地址值加上传输大小(字节), 控制信息和前一次传输相同。在回环的突发传输中,传输的地址在地址边界处回环,回环值的大小等于传输大小(字节) 乘以传输次数(4、8或16) )下图是一个不同传输类型的例子: 图片 从图中可以看出: 第一个传输为以此突发传输的开始(T1),所以其传输类型为NONSEQ; 主机不能立刻执行突发传输中的第二次传输,所以使用了BUSY的传输类型(T2)来延迟下一次传输的开始,注意此时地址已经时下次传输的地址,控制信号和下次传输保持一致,主机只插入了一个BUSY,所以T3执行第二次传输; 主机执行第三次传输(T4),由于从机将HREADY信号拉低,插入了一个等待周期,引起地址相位的扩展; T6周期完成第三次传输,T7周期完成第四次传输(图中T8边沿)。 7、突发操作 AMBA AHB协议定义了4、8和16拍的突发,未定义长度的突发传输以及单次传输;协议支持增量和回环的突发方式。 增量突发方式访问连续地址空间,每次传输的地址是前一次传输地址增加一个增量偏移; 回环突发中,如果传输的其实地址并未和突发中的字节总数对齐,则突发传输地址将在达到边界处回环。例如:一个4拍回环突发的字(4字节)访问将在16字节的边界处回环,如果传输的起始地址为0x34,那么突发中将包含4个地址:0x34,0x38,0x3C,0x30。 AMBA AHB 有8种突发操作,使用信号HBRUST[2:0]表示: HTRANSTypeDescription000SINGLE单一传输001INCR未指定长度的增量突发010WRAP44拍回环突发011INCR44拍增量突发100WRAP88拍回环突发101INCR88拍增量突发110WRAP1616拍回环突发111INCR1616拍增量突发突发不能超过1K的地址边界,所以主机尽量不要发起将要跨过地址边界的定长的增量突发。 一次突发传输的数据总量,等于节拍数乘以每拍包含的字节数。所以突发传输必须将地址边界和数据大小对齐,例如,字传输必须对齐到字地址边界,即A[1:0]=00;半字传输必须对齐到半字地址边界,即A[0]=0。
IP&SOC设计
# SOC设计
刘航宇
3年前
0
1,703
2
2023-03-03
天线设计1-基本参数
第一章 导言 天线类型 图片 辐射机理 电磁波是如何产生,并最终与天线“分离”而在自由空间中传播的呢? 我们讨论以下几种辐射源的辐射原理。 单根导线 图片 图片 这一方程表示了电流和电荷之间的关系,也是电磁辐射的基本条件:要产生电磁辐射,需要有电流或电荷的加速(或减速)。 要产生电荷的加速或减速,需要导线弯曲、不连续、或者端接。就如同水流一样,当管道宽度变化,流速发生变化,在管道宽度变化的区域,就有水流的加速/减速。 为了定性的的了解辐射机理。考虑一个脉冲源连接到一个导线,导线与GND存在RC寄生参数,到导线通电时,导体中的电子被加速;在终端的电子被减速,即反射;从而在导线的两端和导线上产生辐射场。在这个过程中,电荷加速是外部源造成的,电场使电荷运动;电荷减速,则于是由于感应场有关的力造成的,例如导线两端的电荷积累。因此激励电场引起电荷加速,而导线阻抗不连续导致辐射的产生。 传输线 图片 考虑一个电压源连接到图1.11(a)所示的两根导线上。导线间的交变电压使得电荷加速或减速,交变电场感应出交变的磁场,反之亦然,因此,从天线端产生了电磁波,并传播到自由空间中。 结论:激发电场需要电荷,单维持电场不需要。(这就类似水波的产生) 偶极子 首先我们要接受电磁场传播的速度是有限的。两个偶极子在每T/2的时间内交换位置,每个T/2时间内,电场只能传播的距离。每次偶极子交换位置时,电场的极性发生了变化,因此传播的电场变成交变电场,产生交变磁场,从而形成了脱离源的电磁波。 图片 电流在细导线上的分布 讨论天线的辐射场时,需要知道电流的分布。 图片 图片 图片 图片 分析方法 在过去,分析复杂天线问题通常用积分方程方法、几何衍射理论来求解。此类方法用于线型天线较为方便。然而当辐射系统为多个波长时,低频的方法计算效率不高,最近广为关注和应用的GTD/UTD方法,它是几何光学的拓展,通过引入衍射机制,克服了几何光学的局限性。有限差分时域是另一种在散射方面受到广泛关注的方法,现已应用到天线辐射问题。有限元是一种在解决天线问题中获得巨大成功的方法。 遇到的挑战 目前仍有许多挑战和需要解决的问题,例如单片集成MIC技术和相控阵架构依然是最具挑战的问题。复杂问题的计算电磁学。创新的天线设计。多功能、多频带、超宽带、可重构天线等。 第二章 天线的基本参数和FOM 在描述天线性能前,需要定义一些参数,一些参数可能是相互关联的。书中的许多带引号的定义来源于 IEEE Standard Definitions of Terms for Antennas [IEEE Std 145-1993.Reaffirmed 2004(R2004)] Radiation Pattern 辐射图 在天线研究中,通常用球面坐标来表示电磁场会较为方便,因此首先介绍球面坐标系 图片 图片 图片 辐射波瓣(radiation lobe):以辐射强度较弱的区域为边界,将辐射图分割成几个区域。 最大的辐射波瓣称为主瓣(major lobe),其他则为次瓣(minor lobe)。副瓣(side lobe)通常表示功率水平最高的次瓣。后瓣(back lobe)方向与主瓣方向相反的次瓣。 图片 图片 图片 各向同性、定向、全向天线 图片 其中全向天线是定向天线的特殊类型。 主平面 对于线性极化的天线,通常用其主要平面图来描述其性能,包括: 电场平面(E-plane):包含最大电场矢量与最大辐射方向的平面。 磁场平面(H-plane):包含最大磁场矢量与最大辐射方向的平面。 大多数天线的通常的做法是让至少一个电磁平面与几何平面重合。例如图2.5中,可以定义XOZ平面为电场的主平面,而XOY为磁场的主平面。 图片 图片 Field Regions 场区 天线周围空间可分成三个区域: 图片 图2.8显示了,从近场到远场时,场的形状随距离的典型变化趋势。在近场中,场更加分散,几乎均匀,只有很小的变化,随着距离到辐射近场区,图案变得圆滑,逐渐形成波瓣。在远场区,形成了类似花瓣的图案。 图片 弧度和球面度 图片 图片 波束宽度 HPBW Half power beam width 半波束宽度 FNBW First Null Beam width 第一组零点之间的宽度 方向性 定义:在给定方向上的辐射强度与各项同性源的辐射强度之比。 图片 波束立体角 波束立体角定义:假如辐射强度是恒定的,且等于最大值,流过某一个立体角的功率等于天线辐射功率,那么该立体角称为波束立体角。 图片 图片 图片 传导效率和介电效率通常很难计算,可以通过实验测量,但也很难区分出二者,因此把两项合并成传导-介电效率 极化 辐射波的极化定义为:沿着传播方向观察电场的矢量箭头,随时间变化,绘制的轨迹图。 极化可以分成线性、圆形和椭圆。如果电场的矢量始终沿着一条直线变化,则该电场称为线性极化;但一般而言,电场矢量箭头的路径通常是椭圆形,这称为椭圆极化。圆形和线性实际上是椭圆的特赦情况。 假如有一个沿着负z轴方向传播的平面波。其电场可以写成: 图片 图片 输入阻抗 图片 重要公式 图片
通信&信息处理
# 天线设计
刘航宇
3年前
0
1,568
1
VLSI设计基础8-动态COMS
参考书:数字集成电路-电路、系统与设计,本文栏目对其重点进行精简化 目录 1. 综述 2. 存在的问题——信号完整性问题 3. 多米诺逻辑 4. 组合多米诺逻辑 静态CMOS:稳态时,通过低阻路径连接VDD或GND 互补CMOS:上下网络互补,上拉到VDD,下拉到GND。管子数为2N 传输管逻辑:上拉网络用其他代替,有比逻辑,存在VTH。管子数为N+1 动态CMOS:依靠高阻抗上的电容存储临时的信号。管子数为N+2 图片 1. 综述 结构如下, 图片 工作方式:工作分为两个阶段 预充电:CLK=0,Mp导通,对CL充电 求值:CLK=1,MN导通,OUT和GND之间存在低阻通路。 特点: 全电压摆幅 无比逻辑(同互补CMOS,异传输管逻辑) 噪声容限低。因为out在预充电阶段已经充电到VDD,即VDS已经满足>VOV,于是只要VIN>VTH,管子就会导通。 需要预充电和求值的时钟。 较快的开关速度。原因如下, 相对互补CMOS,缺少了上拉网络的一个门,相对负载是互补CMOS,负载是动态门的CL比较小 动态门没有短路电流(同一个时刻,只能一个导通),由下拉网络提供的所有电流都用于CL电容的放电 如果IN=0,则不存在输出延时(预充电完输出即为1);如果IN=1,则需要CL放电 晶体管重复利用,减小面积(多输出多米诺) 优点: 提高速度 减小面积(多输出多米诺;N+2个管子) 没有短路功耗 没有毛刺(因为一次只能翻转一次,CL放电完只能等效下一次预充电才能回到1) 2. 存在的问题——信号完整性问题 电荷泄露 来源:与CL相连的管子存在反偏二极管和亚阈值漏电。 图片 解决办法:使用泄露晶体管 反馈形式的伪NMOS型上拉器件。 该晶体管为了减小功耗和尺寸,一般选用尺寸较小(电阻值大)的管子。 图片 电荷分享 来源:下拉网络中存在的节点电容CA。当A=0-》1、B=0,则原本存储在电容CL上的电荷在CL和CA之间重新分配,造成输出电压有所下降 图片 ※需要满足A=0-》1、B=0才能进行电荷分享,否则当B=1的时候,求值过程中(CLK=1),CL存储的电荷将全部被释放掉,不存在点电荷分享现象 图片 3. 多米诺逻辑 多米诺逻辑即为前文所述的串联动态门,目的就是保证预充电时,输入均为0;求值时,输入只做0→1的翻转 $$ \text { 多米诺逻辑 }=n \text { 型动态门 }+\text { 反相器 } $$图片 初始状态均为0,求值的时候根据前一级输出确定下一级输入,从而求下一级输出。 特点: 求值层层传播,如多米诺骨牌 求值阶段的时间取决于逻辑深度(因为求值时候的特性,见上) 只能实现非反向逻辑 无比逻辑 节点需要在预充电充完电,求值的过程中,输入需要特别稳定。 速度非常快(因为当上一级的输入都是0时,下一级相当于无延迟传播) 输入电容小(和互补CMOS比,只有一个管子) 4. 组合多米诺逻辑 组合多米诺逻辑,并不需要在每个动态门之后加反相器,而是借助一个复合互补CMOS门将多个动态门组合起来。 图片 eg: 图片 图片
VLSI&IC验证
# VLSI
刘航宇
3年前
0
993
2
VLSI设计基础4-反相器
参考书:数字集成电路-电路、系统与设计,本文栏目对其重点进行精简化 目录 1. INV综述 2. INV的静态特性 3. 【重点】INV的动态特性——tp4. 【※重要】计算延时tp——计算、优化 5. 根据【延时】设计电路 3. 反相链的设计 6. 功耗 7. 利用【功耗】设计电路 1. INV综述 INV的数集模型 图片 由组成的图可以知道,INV是由两个CMOS管串联组成的。而CMOS的数集模型是理想开关+有限导通电阻or无限关断电阻,故INV的数集模型如下 图片 即两个电阻开关并联。 图中的CL表示晶体管的漏极电容、连线电容、扇出门的输入电容 INV的特性综述 图片 VTC(电压传输特性) 所有的工作点不是输出高电平就是输出低电平,如下图: 图片 静态CMOS反向中PMOS和NMOS的负载曲线,原点表示直流工作点 2. INV的静态特性 开关阈值(VM) 图片 图片 噪声门限 图片 图片 3. 【重点】INV的动态特性——tp 设计角度 最小的传播延时 这是厂家最希望的 满足性能的同时,做到面积最小 面积最小,意味着成本最低。这里的面积取决于W,也就是选择合适的 根据扇出设计反相器链的级数 对于时钟树、复位树等需要多扇出的、且要求tp小的,如何设计一个合理的高扇出的反相器链 输入信号的上升下降时间对传播延时的影响 计算计算CL CL =晶体管的漏极电容+连线电容+扇出门的输入电容. 图片 等效电阻Req $R_{e q}=\frac{1}{V_{D D} / 2} \int_{V_{D D} / 2}^{V_{D D}} \frac{V}{I_{D S A T(1+\lambda V)}} d V \approx \frac{3}{4} \frac{V_{D D}}{I_{D S A T}}\left(1+\frac{7}{9} \lambda V_{D D}\right)$ 4. 【※重要】计算延时tp——计算、优化 图片 图片 5. 根据【延时】设计电路 图片 图片 图片 性能最好(tp-min)的反相器 图片 图片 3. 反相链的设计 反相链结构图 图片 图片 图片 例题 图片 6. 功耗 功耗组成 动态功耗:来源于电容充放电 短路电流:当输入电压在某一个区域时,使得上下两管同时导通,从而形成短路电流。 漏电流:属于静态功耗 动态功耗 工作过程——能量分配 来源于电容充放电 图片 图片 公式 图片 短路电流引起功耗 图片 图片 图片 图片 静态功耗(漏电流) 图片 图片 7. 利用【功耗】设计电路 图片 【小贴士】:延迟最低的等效扇出系数为$f=\sqrt[N]{F}$
VLSI&IC验证
# VLSI
刘航宇
3年前
0
1,273
2
VLSI设计基础2-器件之MOS晶体管
参考书:数字集成电路-电路、系统与设计,本文栏目对其重点进行精简化 目录 MOS晶体管1. 数字电路的晶体管——最直观 2. MOS静态特性——稳定性(CMOS模电基础) 3. 数字电路手工分析模型——开关+Req 4. 【重点】MOS管的动态特性——性能(tp) 4. 寄生电阻(了解) 5.求tPHL例子(重点) MOS晶体管 1. 数字电路的晶体管——最直观 执行开关功能 非常小的寄生电容 非常高的集成度 相对简单的制造工艺 符号: 图片 2. MOS静态特性——稳定性(CMOS模电基础) 阈值电压 考虑体效应对于阈值电压的影响——偏执效应系数 图片 阈值电压与材料常数(氧化层厚度、费米电势、注入离子剂量等)有关 2.三个工作区: 截止—(亚阈值导电)—线性—饱和—(击穿) 沟长调制效应 图片 4. 速度饱和-重点 短沟道的饱和区范围更大,故常常工作在饱和区。 图片 图片 以下适用于NMOS,PMOS讨论需要取绝对值 图片 漏电流ID和VGS 长沟道,呈现平方关系 短沟道,不那么显著 3. 数字电路手工分析模型——开关+Req 常用开关模型——晶体管=开关+无穷大断开电阻Ron or 有限导通电阻Ron 【计算等效导通电阻Req】:2种方法 图片 例题与方法: 图片 图片 图片 图片 4. 【重点】MOS管的动态特性——性能(tp) 电容的分类 MOS管的动态响应取决于: 本征电容: 基本的MOS结构:结构电容 沟道电荷:沟道电容 漏源反向偏置的PN结耗尽电容:结电容 注意:除了结构电容外,其他两个电容是非线性、随电压变化的 寄生电容 (连线和负载引起) 略解本征电容 简单归类: 图片 小贴士:红色框框:结构电容;灰色框框:沟道电容;蓝色框框:结电容 两个覆盖(结构)电容: $\begin{gathered}C_O=C_{G C O}+C_{G D O}=2 C_o W \\ C_{G C O}=C_{G D O}=C_{o x} x_d W=C_o W\end{gathered}$ 覆盖电容是由于源漏横向扩散到栅氧下形成的寄生电容,故而有两个——栅源之间(CGSO)和栅漏之间(CGDO) 由于这个电容是由于扩散形成的,只要器件做成之后就电容大小就确定,于是结构电容是三类电容中唯一可以确定确切大小的 图片 三个沟道电容: 沟道电容,即栅到沟道之间的电容,称为CGC,即 (Gate Channel)。其中,$C_{G C}=C_{G C B}+C_{G C S}+C_{G C D}$ 即,栅至体、栅至源、栅至漏电容。 由于和沟道有关,又因为沟道形成和工作点有关,于是三个工作点下,CGC不同。 图片 图片 两个(PN)结(耗尽层)电容: PN结电容是由于源-体和漏-体之间反向偏置造成的。 由于工艺上面,我们是在体上“挖一个坑“放漏和源,故而他们之间存在着”立体“的关系。 故而需要关注”四周立体接触“,如图所示, 图片 图片 图片 我们关注的【本征电容】有哪些 我们研究电容是为了利用$\tau=R C$计算tp的值,故而我们在意的是输入和输出通路上的电容。 图片 输入电容——栅极电容 图片 2.输出电容——漏极电容 图片 4. 寄生电阻(了解) 源漏区的串联电阻。 图片 危害: 当晶体管尺寸进一步缩小,会使结变浅、接触孔变小。使得这个影响更加显著。 当给定一个电压,由于分压作用,会使得漏极电流变小。 改善: 源漏极铺一层低电阻材料(如钨或者钛) 5.求tPHL例子(重点) 图片
VLSI&IC验证
# VLSI
刘航宇
3年前
0
1,032
0
2023-02-23
VLSI设计基础1-数字IC引论:度量指标及版图基础
参考书:数字集成电路-电路、系统与设计,本文栏目对其重点进行精简化 目录 引论1. 数字设计中需解决的问题 2. 集成电路质量评价-重点 3. 数字IC基本概念-重点 4. IC全定制流程 版图基础1. CMOS版图 2. 设计规则检查 3.棍棒图 引论 1. 数字设计中需解决的问题 摩尔定律:技术突破才能推动摩尔定律 特征尺寸:28nm是传统制程和先进制程的分界点 存储器容量:存储器的容量增大,意味着功耗增大,意味着稳定性下降(发热)。如果想要实现更大容量的突破,需要寻找新技术或者新架构使功耗不能超过功耗红线 晶圆尺寸:晶圆尺寸增加,单位硅片数量增加,所需的技术越先进,最终成品芯片价格也越低 技术突破:大直径的硅片可以大大提高成品率 2. 集成电路质量评价-重点 图片 注: ① 是取决于制造工艺复杂行参数,常取值3 ②单位面积缺陷数常取值0.5~1个缺陷/cm² ③芯片成本 $=f(\text { 芯片面积 })^4$ 稳定性与功能性 噪声:电容耦合、电感耦合、地线耦合 ※※※【重点】性能——延时tp、工作频率性能常与时钟周期、时钟频率相关 重点:延时 图片 1、传播延时:输入和输入波形的50%翻转点之间的时间 如图: 定义传播时间tp为 $t_p=\frac{t_{p L H}+t_{p H L}}{2}$ 一般而言, ①TpLH和TpHL不会完全相等 ②如果要求传输延时<t,则意味着TpLH<t并且TpHL<t 图片 图片 2、上升时间tr 3、下降时间tf 功耗和能耗 取决的因素太多了。 常常有:瞬时功耗、峰值功耗(研究电源线尺寸)、平均功耗(研究冷却或者对电池的要求) 3. 数字IC基本概念-重点 电压传输特性VTC(DC传输曲线) 可接受的高电压、低电压区域:VIH和VIL定义为VTC增益=-1的点 噪声容限=min{NMH.NML} NMH=|VOH-VIH| NML=|VIL-VOL| 再生性 保证一个受干扰的信号经过若干个组合逻辑之后依旧回到一个额定电平(高或者低,不是不确定态) 抗干扰能力 方向性 6.扇入和扇出 扇入和扇出个数和一些延迟有关 4. IC全定制流程 图片 版图基础 1. CMOS版图 1、图编辑工具:virtuoso、max 2、工艺层的概念:将cmos使用中难以理解的掩膜转化为一组简单概念化的版图层 3、可伸缩的设计:将版图所有参数定义与$\lambda$,利用EDA工具使之在想要兼容的工艺间转换。如0.25转为0.18。早期的工艺中,这个缩放比例可以达到75%左右,随着如今器件尺寸的减小,该比例只有90%左右了。 不足:①由于不同工艺之间的非线性,线性缩放仅在有限尺寸范围内;②可缩放规则是保守的,结果会使得标准单元尺寸过大或者过小。 4、晶体管的尺寸由W/L指定。 给定一个工艺,最小线宽为2$\lambda$; 图片 5、在版图中,只要多晶硅穿过扩散区,就形成一个晶体管 随着工艺的发展,电源电压VDD呈现下降趋势。 2. 设计规则检查 设计规则检查工具:Calibre DRC 设定规则的目的:可以很容易的把电路的概念转换为硅上的几何关系。 Calibre的规则相当于是行业的标准了。 其规则是基于边(edge)的DRC/LVE工具,所有的计算都是基于边来计算的,其中”边“分为”内边“和”外边“ 常见的三个指令: internal:检查多边形的内边距 external:检查多边形的外边距 enclosure:检查多边形的交迭 3.棍棒图 1、要求:①将棍棒图转为管级电路图、并且写出输出表达式;②将管级电路图转化为棍棒图 2、特点: 仅用象征性的符号表示电路的拓扑结构 不需要标尺寸大小 棍棒图中棍棒的位置很重要 3、棍棒图中的串并联 以下为版图的: 串联: 图片 并联: 图片
VLSI&IC验证
# VLSI
刘航宇
3年前
0
730
2
2023-01-14
Design Compile(DC)使用简版
Design Compile是synopsys的综合软件,它的功能是把RTL级的代码转化为门级网表。综合包括转译(Translation),优化(Opitimization),映射(Mapping)三个过程。在转译的过程中,软件自动将源代码翻译成每条语句所对应的功能模块以及模块之间的拓扑结构,这一过程是在综合器内部生成电路的布尔函数的表达,不做任何的逻辑重组和优化。优化:基于所施加的一定时序和面积的约束条件,综合器按照一定的算法对转译结果作逻辑优化和重组。在映射过程中,根据所施加的一定的时序和面积的约束条件,综合器从目标工艺库中搜索符合条件的单元来构成实际电路。 图片 DC 又称为设计综合 将设计的RTL代码综合成门级网表的过程。 在 DC 流程中 一般要经过以下几个步骤,以项目A为例 做如下分析: 1】 在项目子目录下创建DC文件夹,在DC文件夹下分别创建db in lib_syn log netlist rpt和 script 文件夹 以及一个makefile 文件用来运行DC 脚本 。 2】 第二步就是复制相应工艺技术库文件到lib_syn ,一般有2种文件各3个分别包括了typical worst 和 best情况,一类是db,文件一类是lib 文件 也可以在lc_shell 下读取lib 得到相应的db文件。 3】 第三步将需要综合的设计RTL代码(Verilog 文件)复制到in 文件夹 4】 第四步在script 创建综合脚本,脚本创建过程将在后面介绍 5】 第五步编写运行脚本的makefile 文件 6】 第六步运行脚本而后查看综合报告,是否有违例现象出现,如果有修改脚本加以修复直到最终通过设计。 注意 另外的几个文件夹作用 db文件夹存放DC综合生成的项目db文件,综合网表输出到netlist 文件夹,综合程序运行报告存放在log文件夹中,而综合结果的数据报告则存放在rpt 文件夹中。 DC脚本的编写(A.scr) DC综合脚本基本上有几大部分组成 1】定义综合环境中命名规则(分别对net cell port 命名) define_name_rules verilog –casesensitive define_name_rules verilog –type net –allowed “a-z A-Z 0-9 _ ” \ -first_restricted “ _ 0-9 N ” \ -replacement_char “_ ” \ -prefix “n” define_name_rules verilog –type cell –allowed “a-z A-Z 0-9 _ ” \ -first_restricted “ _ 0-9 ” \ -replacement_char “_” \ -prefix “u” define_name_rules verilog –type port –allowed “a-z A-Z 0-9 _ ” \ -first_restricted “ _ 0-9 ” \ -replacement_char “_” \ -prefix “p”2】综合环境的建立 指明库所在的位置 Search_path = { lib_syn/db } 指定综合所需目标库一般选用最恶劣情况worst 库作目标库 target_library = { slow.db} 创建链接库,链接库中包括了一些已经做好的设计和子模块,又包括了当前设计的目标库是设计实例化时所用的库文件 link_library = { “ * ” , slow.db } + synthetic_library 在上述的环境建立所需的各类库中,一般有生产商提供目标库,库中的各类cell用于逻辑映射,链接库则包括了目标库,还包括其他一些以前设计实例基本单元,我们门级网表实例化元件和单元都来自于它。 3】RTL 代码的读入 read –format verilog ./in/ Encoder_32k.v read –format verilog ./in/ Encoder_DBLOCK.v read –format verilog ./in/ Encoder.v read –format verilog ./in/ Step_rom.v指明设计顶层 current_design = Encoder 展开设计分解原设计组 ungroup -all –flatten 设计唯一实例化 uniquify 4】综合环境约束 用户往往需要设置worst case 和 best case 的库来验证setup timing 修复 hold timing 不清楚命令使用和属性 可使用 man set_min_library 查看 set_min_library lib_syn/db/slow.db -min_version lib_syn/db/fast.db 编译操作条件的表述 set_operating_conditions -min slow –min_library slow \ -max fast –max_library fast 设置wire_load_model wire_load_model 负载模型的每一种模型定义,它定义相关的net_length 和 net fanout 属性 而wire_load_mode 则不同指的是不同logic margin 连线net的处理方式 一般我们只设置前者 set_wire_load_model -name “ Silterra18_w110 ” –min set_wire_load_model -name “ Silterra18_w110 ” –max设置模块输入驱动强度信息 man set_driving_cell 查看帮助 set_driving_cell -lib_cell BUFX1 –pin Y –library slow –dont_scale –no_design_rule all_input ( )5】设计时钟相关约束 create_clock clk -period 40 set_clock_latency 0.3 –rise { clk } set_clock_latency 0.3 –fall { clk } set_clock_uncertainty –setup 0.3 { clk } set_clock_uncertainty –hold 0.3 { clk }6】禁止改变门电路控制结构 芯片中的时钟和复位电路一般由门电路控制的,我们不希望DC在综合时候改变它的结构以保证时钟信号和复位信号的稳定性和可靠性需要设置,禁止对某些单元进行优化 set_dont_touch_network { clk, rstn } set_dont_touch { rstn }7】异步电路处理 任何跨越异步边界的路径我们都对其禁止时序分析 set_false_path -from { rstn } –to { clk } 8】设置其他可选约束和禁用单元 可选约束一般包括 set_max_fanout set_max_capacitance set_max_transition set_load 等 这些属性一般在技术库中已经设置了,只有技术库不能满足设计要求时才使用脚本增加约束选项 本脚本中只增加了 set_load 0.02 all_output ( ) Set_max_transition 2.5 current_design 输入输出直通buffer 插入,多重端口的连接插入buffer (选用) set_fix_multiple_port_nets –feedthrough 输出端口插入buffer , 隔离端口 (必须) set_isolate_ports –type buffer all_output ( ) 9】检查设计层次关系进行单元映射 check_design –one_level compile –map_effort medium10】修复hold时序 重新编译 set_fix_hold {clk } compile –only_hold_time 11】导出编译综合相关报告 核对网表命名规则修改相关信息 change_names –rules verilog –hierarchy –verbose 检查整体设计导出报告 check_design > ./rpt/adpcm.rpt 移除未连接的相关端口 remove_unconnected_ports find ( hierarchy cell , “ * ”) 导出设计面积报告 report_area > ./rpt/adpcm_area.txt 导出设计违例报告 report_constraint –all_violators > ./rpt/adpcm_cons.txt 导出setup时序违例的详细报告 report_timing –nworst 50 > ./rpt/adpcm_max_time.txt 导出hold 时序违例的详细报告 report_timing -delay min –nworst 20 > ./rpt/adpcm_min_time.txt 导出综合的设计中cell和reg_cel的报告 report_cell > ./rpt/adpcm_cell.txt report_cell {find (cell, “* _reg *”)} > ./rpt/adpcm_reg_cell.txt12】生成综合网表和pnr 所需的时序约束文件 write -hierarchy -output ./db/adpcm.db write -format verilog –hierarchy -output ./netlist/adpcm.sv write_sdf ./netlist/adpcm.sdf write_sdc ./netlist/adpcm.sdc exit13】compile-ultra 图片
EDA&虚拟机
# EDA&虚拟机
刘航宇
3年前
0
1,452
0
I2C协议及verilog实现-串口读写 EEPROM
I2C 基本概念 I2C 总线(I2C bus,Inter-IC bus)是一个双向的两线连续总线,提供集成电路(ICs)之间的通信线路。I2C 总线是一种串行扩展技术,最早由 Philips 公司推出,广泛应用于电视,录像机和音频设备。I2C 总线的意思是“完成集成电路或功能单元之间信息交换的规范或协议”。Philips 公司推出的 I2C 总线采用一条数据线(SDA),加一条时钟线(SCL)来完成数据的传输及外围器件的扩展。I2C 总线物理拓扑结构如图 30.1 所示。 图片 I2C 总线在物理连接上比较简单,分别由 SDA(串行数据线)和 SCL(串行时钟线)两条总线及上拉电阻组成。通信的原理是通过控制 SCL 和 SDA 的时序,使其满足 I2C 的总线协议从而进行数据的传输。 I2C 总线上的每一个设备都可以作为主设备或者从设备,而且每一个设备都会对应一个唯一的地址(可以从 I2C 器件数据手册得知),主从设备之间就是通过这个地址来确定与哪个器件进行通信。本次实验我们把 FPGA 作为主设备,把挂载在总线上的其他设备(如 EEPROM、PFC8563 等 I2C 器件)作为从设备。 I2C 总线数据传输速率在标准模式下可达 100kbit/s,快速模式下可达 400kbit/s,高速模式下可达 3.4Mbit/s。I2C 总线上的主设备与从设备之间以字节(8 位)为单位进行双向的数据传输。 目录 I2C 基本概念 I2C 协议时序介绍 inout信号原理 I2C 器件地址 I2C 存储器地址 I2C 单字节写时序 I2C 连续写时序(页写时序) I2C 单字节读时序 I2C 连续读时序(页读取) I2C 读写器件控制程序 本节小结 I2C 协议时序介绍 I2C 协议整体时序图如图 30.2 所示 图片 I2C 协议整体时序说明如下: (1) 总线空闲状态:SDA 为高电平,SCL 为高电平; (2) I2C 协议起始位:SCL 为高电平时,SDA 出现下降沿,产生一个起始位; (3) I2C 协议结束位:SCL 为高电平时,SDA 出现上升沿,产生一个结束位; (4) I2C 读写数据状态:主要包括数据的串行输出输入和数据接收方对数据发送方的响应信号。具体时序如图 30.3 所示。 图片 当 I2C 主机(后面简称主机)向 I2C 从机(后面简称从机)写入数据时,SDA 上的每一位数据在 SCL 的高电平期间被写入从机中。从主机角度来看,需要在 SCL 低电平期间改变要写入的数据。而当主机读取从机中数据时,从机在 SCL 低电平期间将数据输出到 SDA 总线上,在 SCL 的高电平期间保持数据稳定,从主机角度来看,需要在 SCL 的高电平期间将 SDA 线上的数据读取并存储。 每当一个字节的数据或命令传输完成时,数据接收方都会向发送方响应一位应答位。在响应应答位时,数据发出方将 SDA 总线设置为三态输入,由于 I2C 总线上都有上拉电阻,因此此时总线默认为高电平,若数据接收方正确接收到数据,则数据接收方将 SDA 总线拉低,以示正确应答。例如当主机向从机写入数据或命令时,每个字节都需要从机产生应答信号以告诉主机此次的数据或命令是否成功被写入。所以,当主机将一字节的数据或命令传出后,会将 SDA 信号设置为三态输入,等待从机应答(等待 SDA 被从机拉低为低电平),若从机正确应答,表明当前数据或命令传输成功,可以结束或开始下一个数据或命令的传输,否则表明数据或命令写入失败,主机就可以决定是否放弃写入或者重新发起写入。 inout信号原理 图片 图片 I2C 器件地址 每个 I2C 器件都有一个器件地址,有的器件地址在出厂时地址就设置好了,用户不可以更改(例如 OV7670 器件地址为固定的 0x42),有的确定了几位,剩下几位由硬件确定(比如常见的 I2C 接口的 EEPROM 存储器,留有 3 个控制地址的引脚,由用户自己在硬件设计时确定)。严格讲,主机不是直接向从机发送地址,而是主机往总线上发送地址,所有的从机都能接收到主机发出的地址,然后每个从机都将主机发出的地址与自己的地址比较,如 果匹配上了,这个从机就会向主机发出一个响应信号。主机收到响应信号后,开始向总线上发送数据,与这个从机的通讯就建立起来了。如果主机没有收到响应信号,则表示寻址失败。 通常情况下,主从器件的角色是确定的,也就是说从机一直工作在从机模式。不同器件定义地址的方式是不同的,有的是软件定义,有的是硬件定义。例如某些单片机的I2C 接口作为从机时,其器件地址是可以通过软件修改从机地址寄存器确定的。而对于一些其他器件,如 CMOS 图像传感器、EEPROM 存储器,其器件地址在出厂时就已经设定好了,具体值可以在对应的数据手册中查到。 对于 AT24C64 这样一颗 EEPROM 器件,其器件地址为 1010 加 3 位的片选信号。3位片选信号由硬件连接决定。例如 SOIC 封装的该芯片 PIN1、PIN2、PIN3 为片选地址。当硬件电路上分别将这三个 pin 连接到 GND 或 VCC 时,就可以设置不同的片选地址。I2C 协议在进行数据传输时,主机需要首先向总线上发出控制命令,其中,控制命令就包含了从机地址/片选信号+读写控制。然后等待从机响应。如图 30.4 所示为 I2C 控制命令传输的数据格式。 图片 I2C 传输时,按照从高到低的位序进行传输。控制字节的最低位为读写控制位,当该位为 0 时表示主机对从机进行写操作,当该位为 1 时表示主机对从机进行读操作。例如,当需要对片选地址为 100 的 AT24LC64 发起写操作,则控制字节应该为 1010_100_0。若进行读操作,则控制字节应该为 1010_100_1。 I2C 存储器地址 每个支持 I2C 协议的器件,内部总会有一些可供读写的寄存器或存储器,例如,对于我们用到的 EEPROM 存储器,内部就是顺序编址的一系列存储单元。对于我们常接触的 CMOS 摄像头如 OV7670(OV7670 的该接口叫 SCCB 接口,其实质也是一种特殊的 I2C协议,可以直接兼容 I2C 协议),其内部就是一系列编址的可供读写的寄存器。因此,我们要对一个器件中的存储单元(寄存器和存储器以下简称存储单元)进行读写,就必须要能够指定存储单元的地址。I2C 协议设计了有从机存储单元寻址地址段,该地址段为一个字或两个字节长度,在主机确认收到从机返回的控制字节响应后由主机发出。地址段长度视不同的器件类型,长度不同,例如同是 EEPROM 存储器,AT24C04 的址段长度为一个字节,而 AT24C64 的地址段长度为两个字节。具体是一个字节还是两个字节,与器件的存储单元数量有关。如图 30.5 和图 30.6 分别为 1 字节地址和 2 字节地址器件的地址分布图,其中 1 字节地址的器件是以内存为 1kbit 的 EEPROM 存储器 AT24C01 举例,2 字节地址的器件是以内存为 64kbit 的 EEPROM 存储器 AT24C64 举例的。 图片 I2C 单字节写时序 根据前面讲的,不同器件,I2C 器件地址字节不同,这样对于 I2C 单字节写时序就会有所差别,图 30.7 和图 30.8 分别为 1 字节地址段器件和 2 字节地址段器件单字节写时序图。 图片 图片 根据时序图,从主机角度来描述一次写入单字节数据过程如下: a. 主机设置 SDA 为输出; b. 主机发起起始信号; c. 主机传输器件地址字节,其中最低位为 0,表明为写操作; d. 主机设置 SDA 为三态门输入,读取从机应答信号; e. 读取应答信号成功,主机设置 SDA 为输出,传输 1 字节地址数据; f. 主机设置 SDA 为三态门输入,读取从机应答信号; g. 读取应答信号成功,对于两字节地址段器件,传输地址数据低字节,对于 1 字节地址段器件,主机设置 SDA 为输出,传输待写入的数据; h. 设置 SDA 为三态门输入,读取从机应答信号,对于两字节地址段器件,接着步骤 i;对于 1 字节地址段器件,直接跳转到步骤 k; i. 读取应答信号成功,主机设置 SDA 为输出,传输待写入的数据(对于两字节地址段器件); j. 设置 SDA 为三态门输入,读取从机应答信号(两字节地址段器件); k. 读取应答信号成功,主机产生 STOP 位,终止传输。 I2C 连续写时序(页写时序) 注:I2C 连续写时序仅部分器件支持。 连续写是主机连续写多个字节数据到从机,这个和单字节写操作类似,连续多字节写操作也是分为 1 字节地址段器件和 2 字节地址段器件的写操作,图 30.9 和图 30.10 分别为 1 字节地址段器件和 2 字节地址段器件连续多字节写时序图。 图片 根据时序图,从主机角度来描述一次写入多字节数据过程如下: a. 主机设置 SDA 为输出; b. 主机发起起始信号; c. 主机传输器件地址字节,其中最低位为 0,表明为写操作; d. 主机设置 SDA 为三态门输入,读取从机应答信号; e. 读取应答信号成功,主机设置 SDA 为输出,传输 1 字节地址数据; f. 主机设置 SDA 为三态门输入,读取从机应答信号; g. 读取应答信号成功后,主机设置 SDA 为输出,对于两字节地址段器件,传输低字节地址数据,对于 1 字节地址段器件,传输待写入的第 1 个数据 h. 设置 SDA 为三态门输入,读取从机应答信号,对于两字节地址段器件,接着步骤 i;对于 1 字节地址段器件,直接跳转到步骤 k; i. 读取应答信号成功后,主机设置 SDA 为输出,传输待写入的第 1 个数据(两字节地址段器件); j. 设置 SDA 为三态门输入,读取从机应答信号(两字节地址段器件); k. 读取应答信号成功后,主机设置 SDA 为输出,传输待写入的下一个数据; l. 设置 SDA 为三态门输入,读取从机应答信号;n 个数据被写完,转到步骤 m,若数据未被写完,转到步骤 k; m. 读取应答信号成功后,主机产生 STOP 位,终止传输。 注:对于 AT24Cxx 系列的 EEPROM 存储器,一次可写入的最大长度为 32 字节。 I2C 单字节读时序 同样的,I2C 读操作时序根据不同 I2C 器件具有不同的器件地址字节数,单字节读操作分为 1 字节地址段器件单节数据读操作和 2 字节地址段器件单节数据读操作。图30.11 和图 30.12 分别为 不同情况的时序图。 图片 根据时序图,从主机角度描述一次读数据过程,如下: a. 主机设置 SDA 为输出; b. 主机发起起始信号; c. 主机传输器件地址字节,其中最低位为 0,表明为写操作; d. 主机设置 SDA 为三态门输入,读取从机应答信号; e. 读取应答信号成功,主机设置 SDA 输出,传输 1 字节地址数据; f. 主机设置 SDA 为三态门输入,读取从机应答信号; g. 读取应答信号成功,主机设置 SDA 输出,对于两字节地址段器件,传输低字节地址数据;对于 1 字节地址段器件,无此步骤,直接跳转到步骤 h; h. 主机发起起始信号; i. 主机传输器件地址字节,其中最低位为 1,表明为读操作; j. 设置 SDA 为三态门输入,读取从机应答信号; k. 读取应答信号成功,主机设置 SDA 为三态门输入,读取 SDA 总线上的一个字节的数据; l. 产生无应答信号(高电平)(无需设置为输出高电平,因为总线会被自动拉高);m. 主机产生 STOP 位,终止传输。 I2C 连续读时序(页读取) 连续读是主机连续从从机读取多个字节数据,这个和单字节读操作类似,连续多字节读操作也是分为 1 字节地址段器件和 2 字节地址段器件的读操作,图 30.13 和图 30.14 分别为 1 字节地址段器件和 2 字节地址段器件连续多字节读时序图。 图片 根据时序图,从主机角度描述多字节数据读取过程如下: a. 主机设置 SDA 为输出 b. 主机发起起始信号 c. 主机传输器件地址字节,其中最低位为 0,表明为写操作。 d. 主机设置 SDA 为三态门输入,读取从机应答信号。 e. 读取应答信号成功,主机设置 SDA 输出,传输 1 字节地址数据 f. 主机设置 SDA 为三态门输入,读取从机应答信号。 g. 读取应答信号成功,主机设置 SDA 输出,对于两字节地址段器件,传输低字节地址数据;对于 1 字节地址段器件,无此步骤;直接跳转到步骤h; h. 主机发起起始信号; i. 主机传输器件地址字节,其中最低位为 1,表明为读操作; j. 设置 SDA 为三态门输入,读取从机应答信号; k. 设置 SDA 为三态门输入,读取 SDA 总线上的第 1 个字节的数据; l. 主机设置 SDA 输出,发送一位应答信号; m. 设置 SDA 为三态门输入,读取 SDA 总线上的下一个字节的数据;若 n 个字节数据读完成,跳转到步骤 n,若数据未读完,跳转到步骤 l;(对于 AT24Cxx,一次读取长度最大为 32 字节,即 n 不大于 32) n. 主机设置 SDA 输出,产生无应答信号(高电平)(无需设置为输出高电平,因为总线会被自动拉高); o. 主机产生 STOP 位,终止传输。 I2C 读写器件控制程序 通过上述的讲述,对 I2C 读写器件数据时序有了一定的了解,下面将开始进行控制程序的设计。根据上面 I2C 的基本概念中有关读写时 SDA 与 SCL 时序,不管对于从机还是主机,SDA 上的每一位数据在 SCL 的高电平期间保持不变,而数据的改变总是在 SCL的低电平期间发生。因此,我们可以选用 2 个标志位对时钟 SCL 的高电平和低电平进行标记,如下图所示:scl_high 对 SCL 高电平期间进行标志,scl_low 对 SCL 低电平期间进行标志。这样就可以在 scl_high 有效时读 SDA 数据,在 scl_low 有效时改变数据。scl_high和 scl_low 产生的时序图如图 30.15 所示。 图片 在本实验中,时钟信号 SCL 采用计数器方法产生,计数器最大计数值为系统时钟频率除以 SCL 时钟频率,即:SCL_CNT_M = SYS_CLOCK/SCL_CLOCK。对于 scl_high 和 scl_low则只需要分别在计数到四分之一的最大值和四分之三的最大值时产生标志位即可,具体的时钟信号 SCL 和标志信号 scl_high、scl_low 产生实现代码如下: //系统时钟采用 50MHz parameter SYS_CLOCK = 50_000_000; //SCL 总线时钟采用 400kHz parameter SCL_CLOCK = 400_000; //产生时钟 SCL 计数器最大值 localparam SCL_CNT_M = SYS_CLOCK/SCL_CLOCK; reg [15:0]scl_cnt; //SCL 时钟计数器 reg scl_vaild; //I2C 非空闲时期 reg scl_high; //SCL 时钟高电平中部标志位 reg scl_low; //SCL 时钟低电平中部标志位 //I2C 非空闲时期 scl_vaild 的产生 always@(posedge Clk or negedge Rst_n) begin if(!Rst_n) scl_vaild <= 1'b0; else if(Wr | Rd) scl_vaild <= 1'b1; else if(Done) scl_vaild <= 1'b0; else scl_vaild <= scl_vaild; end //scl 时钟计数器 always@(posedge Clk or negedge Rst_n) begin if(!Rst_n) scl_cnt <= 16'd0; else if(scl_vaild)begin if(scl_cnt == SCL_CNT_M - 1) scl_cnt <= 16'd0; else scl_cnt <= scl_cnt + 16'd1; end else scl_cnt <= 16'd0; end //scl 时钟,在计数器值到达最大值一半和 0 时翻转 always@(posedge Clk or negedge Rst_n) begin if(!Rst_n) Scl <= 1'b1; else if(scl_cnt == SCL_CNT_M >>1) Scl <= 1'b0; else if(scl_cnt == 16'd0) Scl <= 1'b1; else Scl <= Scl; end //scl 时钟高低电平中部标志位 always@(posedge Clk or negedge Rst_n) begin if(!Rst_n) scl_high <= 1'b0; else if(scl_cnt == (SCL_CNT_M>>2)) scl_high <= 1'b1; else scl_high <= 1'b0; end //scl 时钟低电平中部标志位 always@(posedge Clk or negedge Rst_n) begin if(!Rst_n) scl_low <= 1'b0; else if(scl_cnt == (SCL_CNT_M>>1)+(SCL_CNT_M>>2)) scl_low <= 1'b1; else scl_low <= 1'b0; end上述代码中 Wr 和 Rd 信号为 I2C 进行一次写和读操作的门控使能信号,Done 信号为一次I2C写和读操作完成标志位。(SCL_CNT_M>>2)和(SCL_CNT_M>>1)+(SCL_CNT_M>>2)分别为 1/2 的 SCL_CNT_M 和 3/4 的 SCL_CNT_M 的计数值。 在 SCL 时钟总线以及其高低电平标志位产生完成后,接下来的事情就是 SDA 数据线的产生,这个需要根据具体的读写操作时序完成。本实验主要采用状态机实现,根据上面讲述的读写数据的时序关系,设计了如图 30.16 所示的状态转移图,其状态机状态编码采用独热编码,若需要改变状态编码形式,只需改变程序中的 parameter 定义即可。 图片 根据上面 I2C 基本概念可知,不同的器件其寄存器地址字节数分为 1 字节或和 2 字节地址段,并且有些 I2C 器件是支持多字节的数据读写,所以在设计时考虑到该 I2C 控制器的通用性,我们将设计寄存器地址字节和读取数据个数均可自行设置的 I2C 控制器,用户可根据自己的实际应用情况设置选择与器件对应的寄存器地址字节数或是读写数据的字节数。寄存器地址字节数的可变主要是通过一个计数器对字节数进行计数,当计数值达到指定值后跳转到下一状态,具体的可参见代码。 在状态机中,从主机角度来看,SDA 数据线上在写控制、写数据、读控制状态过程是需要串行输出数据,而在读数据状态过程是需要串行输入数据。根据数据在时钟高电平期间保持不变,改变数据在低电平时期的规则,本设计对时钟信号的高低电平进行计数,从而在指定的计数值进行输出或读取数据实现数据的串行输出和串行输入。串行输出和串行输入数据采用任务的形式进行表示,便于在主状态机中多次的调用。图 30.17为计数的过程以及特定状态变化的时序图,这里的特定状态主要是指读/写控制、读/写地址和读/写数据状态。 图片 图 30.17 中计数器 halfbit_cnt 和数据接收方对发送的响应检测标志位 ack 以及串行输出、输入数据任务的具体代码如下: //sda 串行接收与发送时 scl 高低电平计数器 always@(posedge Clk or negedge Rst_n) begin if(!Rst_n) halfbit_cnt <= 8'd0; else if((main_state == WR_CTRL)|| (main_state == WR_WADDR)|| (main_state == WR_DATA)|| (main_state == RD_CTRL)|| (main_state == RD_DATA))begin if(scl_low | scl_high)begin if(halfbit_cnt == 8'd17) halfbit_cnt <= 8'd0; else halfbit_cnt <= halfbit_cnt + 8'd1; end else halfbit_cnt <= halfbit_cnt; end else halfbit_cnt <= 8'd0; end //数据接收方对发送的响应检测标志位 always@(posedge Clk or negedge Rst_n) begin if(!Rst_n) ack <= 1'b0; else if((halfbit_cnt == 8'd16)&&scl_high&&(Sda==1'b0)) ack <= 1'b1; else if((halfbit_cnt == 8'd17)&&scl_low) ack <= 1'b0; else ack <= ack; end //输出串行数据任务 task send_8bit_data; if(scl_high && (halfbit_cnt == 8'd16)) FF <= 1; else if(halfbit_cnt < 8'd17)begin sda_reg <= sda_data_out[7]; if(scl_low) sda_data_out <= {sda_data_out[6:0],1'b0}; else sda_data_out <= sda_data_out; end else ; endtask //串行数据输入任务 task receive_8bit_data; if(scl_low && (halfbit_cnt == 8'd15)) FF <= 1; else if((halfbit_cnt < 8'd15))begin if(scl_high) sda_data_in <= {sda_data_in[6:0],Sda}; else begin sda_data_in <= sda_data_in; end end else ; endtask对于计数器 halfbit_cnt 只在写控制、写数据、读控制、读数据状态下才进行计数,其他状态为零。代码中 FF 是进行串行输出或输入任务的标志位,当 FF 为 1 时表示退出任务,FF 为 0 时表示进入任务。这样便于在状态机中对任务的调用,以及在指定的时间退出任务。 接下来就是主状态机的设计,主状态机的状态转移图上面已经给出,具体转移过程是依据 I2C 读写时序进行的,代码如下: //主状态机 always@(posedge Clk or negedge Rst_n) begin if(!Rst_n)begin main_state <= IDLE; sda_reg <= 1'b1; W_flag <= 1'b0; R_flag <= 1'b0; Done <= 1'b0; waddr_cnt <= 2'd1; wdata_cnt <= 8'd1; rdata_cnt <= 8'd1; end else begin case(main_state) IDLE:begin sda_reg <= 1'b1; W_flag <= 1'b0; R_flag <= 1'b0; Done <= 1'b0; waddr_cnt <= 2'd1; wdata_cnt <= 8'd1; rdata_cnt <= 8'd1; if(Wr)begin main_state <= WR_START; W_flag <= 1'b1; end else if(Rd)begin main_state <= WR_START; R_flag <= 1'b1; end else main_state <= IDLE; end WR_START:begin if(scl_low)begin main_state <= WR_CTRL; sda_data_out <= wr_ctrl_word; FF <= 1'b0; end else if(scl_high)begin sda_reg <= 1'b0; main_state <= WR_START; end else main_state <= WR_START; end WR_CTRL:begin if(FF == 1'b0) send_8bit_data; else begin if(ack == 1'b1) begin//收到响应 if(scl_low)begin main_state <= WR_WADDR; FF <= 1'b0; if(Wdaddr_num == 2'b1) sda_data_out <= Word_addr[7:0]; else sda_data_out <= Word_addr[15:8]; end else main_state <= WR_CTRL; end else//未收到响应 main_state <= IDLE; end end WR_WADDR:begin if(FF == 1'b0) send_8bit_data; else begin if(ack == 1'b1) begin//收到响应 if(waddr_cnt == Wdaddr_num)begin if(W_flag && scl_low)begin main_state <= WR_DATA; sda_data_out <= Wr_data; waddr_cnt <= 2'd1; FF <= 1'b0; end else if(R_flag && scl_low)begin main_state <= RD_START; sda_reg <= 1'b1; end else main_state <= WR_WADDR; end else begin if(scl_low)begin waddr_cnt <= waddr_cnt + 2'd1; main_state <= WR_WADDR; sda_data_out <= Word_addr[7:0]; FF <= 1'b0; end else main_state <= WR_WADDR; end end else//未收到响应 main_state <= IDLE; end end WR_DATA:begin if(FF == 1'b0) send_8bit_data; else begin if(ack == 1'b1) begin//收到响应 if(wdata_cnt == Wrdata_num)begin if(scl_low)begin main_state <= STOP; sda_reg <= 1'b0; wdata_cnt <= 8'd1; end else main_state <= WR_DATA; end else begin if(scl_low)begin wdata_cnt <= wdata_cnt + 8'd1; main_state <= WR_DATA; sda_data_out <= Wr_data; FF <= 1'b0; end else main_state <= WR_DATA; end end else//未收到响应 main_state <= IDLE; end end RD_START:begin if(scl_low)begin main_state <= RD_CTRL; sda_data_out <= rd_ctrl_word; FF <= 1'b0; end else if(scl_high)begin main_state <= RD_START; sda_reg <= 1'b0; end else main_state <= RD_START; end RD_CTRL:begin if(FF == 1'b0) send_8bit_data; else begin if(ack == 1'b1) begin//收到响应 if(scl_low)begin main_state <= RD_DATA; FF <= 1'b0; end else main_state <= RD_CTRL; end else//未收到响应 main_state <= IDLE; end end RD_DATA:begin if(FF == 1'b0) receive_8bit_data; else begin if(rdata_cnt == Rddata_num)begin sda_reg <= 1'b1; if(scl_low)begin main_state <= STOP; sda_reg <= 1'b0; end else main_state <= RD_DATA; end else begin sda_reg <= 1'b0; if(scl_low)begin rdata_cnt <= rdata_cnt + 8'd1; main_state <= RD_DATA; FF <= 1'b0; end else main_state <= RD_DATA; end end end STOP:begin//结束操作 if(scl_high)begin sda_reg <= 1'b1; main_state <= IDLE; Done <= 1'b1; end else main_state <= STOP; end default: main_state <= IDLE; endcase end end主状态机完成后,I2C 控制器设计的大块就解决了,剩下的就是 SDA 数据线的输出了,该数据线采用三态使能输出,具体代码如下: assign Sda = sda_en ? sda_reg : 1'bz;对于使能信号 sda_en 按照上面的时序关系图可知,该信号在不同的状态,其高低电平变化的时刻是有差别的,比如在开始和结束状态,它是一直为高电平的,在写控制、写数据、读控制状态,它是在串行输出一字节数据期间(即 halfbit_cnt < 16 时)为高电平,之外的一个数据为位低电平,而在读数据状态时串行输入一字节数据期间(即halfbit_cnt < 16 时)为低电平电平,之外的一个数据位为高电平。具体代码如下: //SDA 三态使能信号 sda_en always@(*) begin case(main_state) IDLE: sda_en = 1'b0; WR_START,RD_START,STOP: sda_en = 1'b1; WR_CTRL,WR_WADDR,WR_DATA,RD_CTRL: if(halfbit_cnt < 16) sda_en = 1'b1; else sda_en = 1'b0; RD_DATA: if(halfbit_cnt < 16) sda_en = 1'b0; else sda_en = 1'b1; default: sda_en = 1'b0; endcase end本实验设计考虑到了多字节数据的读取情况,所以增加了数据读取和数据写入时的 有效标志位信号。主要是标志读取数据时数据有效时刻和写数据时提供待写入数据时刻。 具体代码如下: //写数据有效标志位 assign Wr_data_vaild = ((main_state==WR_WADDR)&& (waddr_cnt==Wdaddr_num)&& (W_flag && scl_low)&& (ack == 1'b1))|| ((main_state == WR_DATA)&& (ack == 1'b1)&&(scl_low)&& (wdata_cnt != Wrdata_num)); //读数据有效标志位前寄存器 assign rdata_vaild_r = (main_state == RD_DATA) &&(halfbit_cnt == 8'd15)&&scl_low; //读出数据有效标志位 always@(posedge Clk or negedge Rst_n) begin if(!Rst_n) Rd_data_vaild <= 1'b0; else if(rdata_vaild_r) Rd_data_vaild <= 1'b1; else Rd_data_vaild <= 1'b0; end //读出的有效数据 always@(posedge Clk or negedge Rst_n) begin if(!Rst_n) Rd_data <= 8'd0; else if(rdata_vaild_r) Rd_data <= sda_data_in; else Rd_data <= Rd_data; end到目前为止,整个 I2C 控制器的设计就完成了,接下来就进入仿真环节,我们需要EEPROM 的仿真模型进行仿真,这样能更好的检验 I2C 控制器设计的是否存在问题,以便后面进行优化改进。本实验采用的是镁光官网提供的 EEPROM 仿真模型,具体下载网 址 为 http://www.microchip.com/zh/design-centers/memory/serial-eeprom/verilog-ibis-models,我们选择两个具有代表性的模型进行仿真,分别为 1 字节寄存器地址段的 24LC04B 和 2 字节寄存器地址段的 24LC64 仿真模型,利用这两个模型对我们设计的 I2C 控制器进行仿真。如图 30.18 为仿真验证的结构框图。 图片 这里我们对 1 字节寄存器地址段的 24LC04B 和 2 字节寄存器地址段的 24LC64 仿真模型的器件地址{A3,A2,A1}分别设置为 3’b001 和 3’b000,仿真时为了能分别对不同型号的模型进行仿真,在编写的 testbench 文件中采用了申明方法去选择当前使用哪个模型进行仿真,具体代码如下: `timescale 1ns/1ns `define CLK_PERIOD 20 //仿真模型选择 //`define TEST_M24LC64 //24LC64 `define TEST_M24LC04 //24LC04 module I2C_tb; reg Clk; //系统时钟 reg Rst_n; //系统复位信号 reg [15:0] Word_addr; //I2C 器件寄存器地址 reg Wr; //I2C 器件写使能 reg [7:0] Wr_data; //I2C 器件写数据 wire Wr_data_vaild;//I2C 器件写数据有效标志位 reg Rd; //I2C 器件读使能 wire[7:0] Rd_data; //I2C 器件读数据 wire Rd_data_vaild;//I2C 器件读数据有效标志位 wire Scl; //I2C 时钟线 wire Sda; //I2C 数据线 wire Done; //对 I2C 器件读写完成标识位 localparam NUM = 6'd4; //单次读写数据字节数 `ifdef TEST_M24LC64 localparam DevAddr = 3'b000; //I2C 器件的器件地址 localparam WdAr_NUM= 2; //I2C 器件的存储器地址字节数 `elsif TEST_M24LC04 localparam DevAddr = 3'b001; //I2C 器件的器件地址 localparam WdAr_NUM= 1; //I2C 器件的存储器地址字节数 `endif I2C I2C( .Clk(Clk), .Rst_n(Rst_n), .Rddata_num(NUM), .Wrdata_num(NUM), .Wdaddr_num(WdAr_NUM), .Device_addr(DevAddr), .Word_addr(Word_addr), .Wr(Wr), .Wr_data(Wr_data), .Wr_data_vaild(Wr_data_vaild), .Rd(Rd), .Rd_data(Rd_data), .Rd_data_vaild(Rd_data_vaild), .Scl(Scl), .Sda(Sda), .Done(Done) ); `ifdef TEST_M24LC64 M24LC64 M24LC64( .A0(1'b0), .A1(1'b0), .A2(1'b0), .WP(1'b0), .SDA(Sda), .SCL(Scl), .RESET(!Rst_n) ); `elsif TEST_M24LC04 M24LC04B M24LC04( .A0(1'b1), .A1(1'b0), .A2(1'b0), .WP(1'b0), .SDA(Sda), .SCL(Scl), .RESET(!Rst_n) ); `endif //系统时钟产生 initial Clk = 1'b1; always #(`CLK_PERIOD/2)Clk = ~Clk; initial begin Rst_n = 0; Word_addr = 0; Wr = 0; Wr_data = 0; Rd = 0; #(`CLK_PERIOD*200 + 1) Rst_n = 1; #200; `ifdef TEST_M24LC64 //仿真验证 24LC64 模型 //写入 20 组数据 Word_addr = 0; Wr_data = 0; repeat(20)begin Wr = 1'b1; #(`CLK_PERIOD); Wr = 1'b0; repeat(NUM)begin //在写数据有效前给待写入数据 @(posedge Wr_data_vaild) Wr_data = Wr_data + 1; end @(posedge Done); #2000; Word_addr = Word_addr + NUM; end #2000; //读出刚写入的 20 组数据 Word_addr = 0; repeat(20)begin Rd = 1'b1; #(`CLK_PERIOD); Rd = 1'b0; @(posedge Done); #2000; Word_addr = Word_addr + NUM; end `elsif TEST_M24LC04 //仿真验证 24LC04 模型 //写入 20 组数据 Word_addr = 100; Wr_data = 100; repeat(20)begin Wr = 1'b1; #(`CLK_PERIOD); Wr = 1'b0; repeat(NUM)begin //在写数据有效前给待写入数据 @(posedge Wr_data_vaild) Wr_data = Wr_data + 1; end @(posedge Done); #2000; Word_addr = Word_addr + NUM; end #2000; //读出刚写入的 20 组数据 Word_addr = 100; repeat(20)begin Rd = 1'b1; #(`CLK_PERIOD); Rd = 1'b0; @(posedge Done); #2000; Word_addr = Word_addr + NUM; end `endif #5000; $stop; end endmodule在 testbench 文件中,通过对 EEPROM 模型进行 20 写操作,每次写字节数为NUM,然后对 EEPROM 模型在刚写入数据的地址段进行读操作,通过比较读出和写入的数据验证 I2C 控制器设计是否正确。这里分别通过申明选择 TEST_M24LC64 或TEST_M24LC04 来作为当前的仿真模型。如图 30.19 所示为 2 字节地址的 EEPROM模型 24LC64 仿真结果。图 30.20 和图 30.21 分别为型号 24LC64 仿真模型写操作时序和读操作时序放大后的波形图。 图片 图片 同样的方式选择 24LC04 型号 EEPROM 模型进行仿真,如图 30.22 所示为 1 字节地址的 EEPROM 模型 24LC604 仿真结果。图 30.23 和图 30.24 分别为型号24LC604 仿真模型写操作时序和读操作时序放大后的波形图。 图片 通过观察图 30.21 和图 30.24 的时序波形发现,在读操作时序结果中,读出的数据中某些位是高阻态,仔细观察波形可知,高阻态的位置正好是需要输出高电平的位置,这个的原因是 EEPROM 的仿真模型是完全与实际的器件是一样的,对于器件来说,只在输出 0 时将数据线拉低,而在高阻态或本应该为高电平的时刻都是设置为高阻态的,这个在仿真模型的代码中也有体现,具体体现这一点的代码如下: bufif1 (SDA, 1'b0, SDA_DriveEnableDlyd);图片 其中,器件地址包括器件的地址字节数和 3 位的器件地址,具体分配如下: 图片 功能码主要是区分是写数据操作还是读数据操作,为了方便,我们直接规定,功能码为 0xf1 表示写数据操作,0xf2 表示读数据操作;起始地址是我们要读写数据的第一个地址;数据字节数表示要写入或读取的数据的字节个数,后面的数据 1 到数据n 表示要写入的 n 个数据,对于读操作没有这部分。如图 30.25 为该实验整体的设计框图。 图片 有关串口发送和接收以及 fifo 模块在前面章节都已经进行了讲解,这里就不重复讲解了,这里重点讲解命令解析模块的设计,命令解析模块的主要作用是对串口接收的数据进行解析,通过对接收的数据进行解析分析判断出是进行何种操作。根据我们自己规定的数据协议,进行如下的设计,首先我们将串口发送的数据的前 4 个数据存入一个缓冲区数据内,通过对功能码识别判断是写操作还是读操作,如果是写操作,就将后面待写入的数据存入 fifo 中;同时将器件地址、地址字节数、起始地址、数据字节数赋值给 I2C 对应的信号线;如果是读操作,就在前 4 个字节接收完成后将器件地址、地址字节数、起始 地址、数据字节数赋值给 I2C 对应的信号线,同时给 I2C 控制器模块的读使能给一个时钟周期的门控信号,使能 I2C 的读操作。读出的数据同样也是先存放在另外一个 fifo 中,然后送给串口发送模块发出。具体实现代码如下: module cmd_analysis( Clk, Rst_n, Rx_done, Rx_data, Wfifo_req, Wfifo_data, Rddata_num, Wrdata_num, Wdaddr_num, Device_addr, Word_addr, Rd ); input Clk; //系统时钟 input Rst_n; //系统复位 input Rx_done; //串口接收一字节数据完成 input[7:0] Rx_data; //串口接收一字节数据 output reg Wfifo_req; //写 fifo 请求信号 output reg[7:0] Wfifo_data; //写 fifo 数据 output reg[5:0] Rddata_num; //I2C 总线连续读取数据字节数 output reg[5:0] Wrdata_num; //I2C 总线连续读取数据字节数 output reg[1:0] Wdaddr_num; //I2C 器件数据地址字节数 output reg[2:0] Device_addr; //EEPROM 器件地址 output reg[15:0] Word_addr; //EEPROM 寄存器地址 output reg Rd; //EEPROM 读请求信号 reg [7:0] buff_data[4:0];//串口接收数据缓存器 //串口接收数据计数器 reg [7:0]byte_cnt; always@(posedge Clk or negedge Rst_n) begin if(!Rst_n) byte_cnt <= 8'd0; else if(Rx_done && byte_cnt==8'd4)begin if(buff_data[1]==8'hf2) //读数据指令 byte_cnt <= 8'd0; else if(buff_data[1]==8'hf1) //写数据指令 byte_cnt <= byte_cnt + 8'd1; else byte_cnt <= 8'd0; //错误指令 end else if(Rx_done)begin if(byte_cnt == 8'd4 + buff_data[4]) byte_cnt <= 8'd0; else byte_cnt <= byte_cnt + 8'd1; end else byte_cnt <= byte_cnt; end //串口接收数据缓存器 always@(posedge Clk or negedge Rst_n) begin if(!Rst_n)begin buff_data[0] <= 8'h00; buff_data[1] <= 8'h00; buff_data[2] <= 8'h00; buff_data[3] <= 8'h00; buff_data[4] <= 8'h00; end else if(Rx_done && byte_cnt<5) buff_data[byte_cnt] <= Rx_data; else ; end //写 fifo 请求信号 Wfifo_req always@(posedge Clk or negedge Rst_n) begin if(!Rst_n) Wfifo_req <= 1'b0; else if(byte_cnt >8'd4 && Rx_done) Wfifo_req <= 1'b1; else Wfifo_req <= 1'b0; end //写 fifo 数据 Wfifo_data always@(posedge Clk or negedge Rst_n) begin if(!Rst_n) Wfifo_data <= 8'd0; else if(byte_cnt > 8'd4 && Rx_done) Wfifo_data <= Rx_data; else Wfifo_data <= Wfifo_data; end //EEPROM 读请求信号 Rd always@(posedge Clk or negedge Rst_n) begin if(!Rst_n) Rd <= 1'b0; else if(byte_cnt == 8'd4 && Rx_done && buff_data[1]==8'hf2) Rd <= 1'b1; else Rd <= 1'b0; end //指令完成标志位 reg cmd_flag; always@(posedge Clk or negedge Rst_n) begin if(!Rst_n) cmd_flag <= 1'b0; else if((byte_cnt == 8'd4)&& Rx_done) cmd_flag <= 1'b1; else cmd_flag <= 1'b0; end //EEPROM 读写数据、寄存器地址字节数,器件地址,寄存器地址 always@(posedge Clk or negedge Rst_n) begin if(!Rst_n)begin Rddata_num <= 6'd0; Wrdata_num <= 6'd0; Wdaddr_num <= 2'd0; Device_addr <= 3'd0; Word_addr <= 16'd0; end else if(cmd_flag == 1'b1)begin Rddata_num <= buff_data[4][5:0]; Wrdata_num <= buff_data[4][5:0]; Wdaddr_num <= buff_data[0][5:4]; Device_addr <= buff_data[0][2:0]; Word_addr <= {buff_data[2],buff_data[3]}; end else ; end endmodule下面编写 testbench 测试文件对设计的命令解析模块进行仿真验证,该仿真主要是分别模拟发送写数据操作和读数据操作指令,来观察相应的输出时序波形结果。通过仿真时序结果可以对该模块进行优化改进,具体的 testbench 文件如下: `timescale 1ns/1ns `define CLK_PERIOD 20 module cmd_analysis_tb; reg Clk; reg Rst_n; reg Rx_done; reg [7:0] Rx_data; wire Wfifo_req; wire[7:0] Wfifo_data; wire[5:0] Rddata_num; wire[5:0] Wrdata_num; wire[1:0] Wdaddr_num; wire[2:0] Device_addr; wire[15:0] Word_addr; wire Rd; reg [15:0] addr; reg [7:0] data_num; reg [7:0] wr_data; reg [39:0] wdata_cmd; reg [39:0] rdata_cmd; cmd_analysis cmd_analysis( .Clk(Clk), .Rst_n(Rst_n), .Rx_done(Rx_done), .Rx_data(Rx_data), .Wfifo_req(Wfifo_req), .Wfifo_data(Wfifo_data), .Rddata_num(Rddata_num), .Wrdata_num(Wrdata_num), .Wdaddr_num(Wdaddr_num), .Device_addr(Device_addr), .Word_addr(Word_addr), .Rd(Rd) ); //写 FIFO 模块例化 fifo_wr wr( .clock(Clk), .data(Wfifo_data), .rdreq(), .wrreq(Wfifo_req), .empty(), .full(), .q(), .usedw() ); //系统时钟产生 initial Clk = 1'b1; always #(`CLK_PERIOD/2)Clk = ~Clk; initial begin Rst_n = 0; Rx_done = 0; Rx_data = 0; addr = 0; data_num = 0; wr_data = 0; wdata_cmd = 0; rdata_cmd = 0; #(`CLK_PERIOD*200 + 1) Rst_n = 1; #200; addr = 0; data_num = 4; wr_data = 0; send_uart_data_wr; //写数据 #500; send_uart_data_rd; //读数据 #500; addr = 4; data_num = 8; wr_data = 20; send_uart_data_wr; //写数据 #500; send_uart_data_rd; //读数据 #500; $stop; end //串口发送写数据命令和待写入数据任务 task send_uart_data_wr; begin //写数据指令 wdata_cmd = {8'h21,8'hf1,addr[15:8],addr[7:0],data_num}; //发送写数据指令 repeat(5)begin Rx_done = 1; Rx_data = wdata_cmd[39:32]; #(`CLK_PERIOD) Rx_done = 0; #500; wdata_cmd = {wdata_cmd[31:0],8'h00}; end //待写入数据 Rx_data = wr_data; repeat(data_num)begin Rx_done = 1; Rx_data = Rx_data + 1; #(`CLK_PERIOD) Rx_done = 0; #500; end end endtask //串口发送读数据命令任务 task send_uart_data_rd; begin //读数据指令 rdata_cmd = {8'h21,8'hf2,addr[15:8],addr[7:0],data_num}; //发送读数据指令 repeat(5)begin Rx_done = 1; Rx_data = rdata_cmd[39:32]; #(`CLK_PERIOD) Rx_done = 0; #500; rdata_cmd = {rdata_cmd[31:0],8'h00}; end end endtask endmodule仿真过程主要是模拟命令解析模块接收到写或读 EEPROM 操作指令后的操作是否和我们设计想要达到的目标是否一致。这里的写或读数据指令均采用任务的形式,写数据指令下待写入的数据是采用给定一个值的基础上递增进行赋值的。命令解析模块仿真的结果如图 30.26 所示。 图片 根据波形时序图,在模拟发送写操作命令 0x21,0xf1,0x00,0x00,0x04,0x06,0x07,0x08,0x09 时,在接收完数据字节数这个数据后,后面收到的数据就存入到 fifo 中去了,与我们设计的是一致的,同理可以分析读数据操作命令,也是没有问题的,这样命令解析模块就设计完成了。 下面就是整个系统的设计,如图 30.25 整体设计框图中,在 I2C 写数据操作之前和读数据后分别加入了 fifo 模块,因为串口读写速度和 I2C 读写速度不一样,在这之间加入的 fifo 模块具有读写时钟不一致的特点,可以对数据进行一个缓存,这样就能解决前后速度不一样的问题,这里的两个 fifo 均设置的是读取数据在读请求前有效,这样设计的目的是为了与其他模块待输入数据与使能信号相匹配,里面的 fifo模块都是通过 QuartusII 软件生成的 IP 核,fifo 输入输出数据位宽均设置为 8位,深度设置为 64(多字节读取最多支持 32 字节,稍大于这个数就可以了)。在各模块均设计完成后,整个系统的顶层电路设计就显得比较简单了,根据设计的系统框图进行整合就可以了。整个系统设计的代码如下: module uart_eeprom( Clk, Rst_n, Uart_rx, Uart_tx, Sda, Scl ); parameter Baud_set = 3'd4;//波特率设置,这里设置为 115200 input Clk; //系统时钟 input Rst_n; //系统复位 input Uart_rx; //串口接收 output Uart_tx; //串口发送 inout Sda; //I2C 时钟线 output Scl; //I2C 数据线 wire [7:0] Rx_data; //串口接收一字节数据 wire Rx_done; //串口接收一字节数据完成 wire wfifo_req; //写 FIFO 模块写请求 wire [7:0] wfifo_data; //写 FIFO 模块写数据 wire [5:0] wfifo_usedw; //写 FIFO 模块已写数据量 wire [5:0] rfifo_usedw; //读 FIFO 模块可读数据量 wire rfifo_rdreq; //读 FIFO 模块读请求 wire [5:0] Rddata_num; //I2C 总线连续读取数据字节数 wire [5:0] Wrdata_num; //I2C 总线连续写取数据字节数 wire [1:0] Wdaddr_num; //EEPROM 数据地址字节数 wire [2:0] Device_addr; //EEPROM 地址 wire [15:0] Word_addr; //EEPROM 寄存器地址 wire Wr; //EEPROM 写使能 wire [7:0] Wr_data; //EEPROM 写数据 wire Wr_data_vaild;//EEPROM 写数据有效标志位 wire Rd; //EEPROM 读使能 wire [7:0] Rd_data; //EEPROM 读数据 wire Rd_data_vaild;//EEPROM 读数据有效标志位 wire Done; //EEPRO 读写完成标识位 wire tx_en; //串口发送使能 wire [7:0] tx_data; //串口待发送数据 wire tx_done ; //一次串口发送完成标志位 //串口接收模块例化 uart_byte_rx uart_rx( .Clk(Clk), .Rst_n(Rst_n), .Rs232_rx(Uart_rx), .baud_set(Baud_set), .Data_Byte(Rx_data), .Rx_Done(Rx_done) ); //指令解析模块例化 cmd_analysis cmd_analysis( .Clk(Clk), .Rst_n(Rst_n), .Rx_done(Rx_done), .Rx_data(Rx_data), .Wfifo_req(wfifo_req), .Wfifo_data(wfifo_data), .Rddata_num(Rddata_num), .Wrdata_num(Wrdata_num), .Wdaddr_num(Wdaddr_num), .Device_addr(Device_addr), .Word_addr(Word_addr), .Rd(Rd) ); //写缓存 fifo 模块例化 fifo_wr fifo_wr( .clock(Clk), .data(wfifo_data), .rdreq(Wr_data_vaild), .wrreq(wfifo_req), .empty(), .full(), .q(Wr_data), .usedw(wfifo_usedw) ); //EEPROM 写使能 assign Wr = (wfifo_usedw == Wrdata_num)&& (wfifo_usedw != 6'd0); //I2C 控制模块例化 I2C I2C( .Clk(Clk), .Rst_n(Rst_n), .Rddata_num(Rddata_num), .Wrdata_num(Wrdata_num), .Wdaddr_num(Wdaddr_num), .Device_addr(Device_addr), .Word_addr(Word_addr), .Wr(Wr), .Wr_data(Wr_data), .Wr_data_vaild(Wr_data_vaild), .Rd(Rd), .Rd_data(Rd_data), .Rd_data_vaild(Rd_data_vaild), .Scl(Scl), .Sda(Sda), .Done(Done) ); //读缓存 fifo 模块例化 fifo_rd fifo_rd( .clock(Clk), .data(Rd_data), .rdreq(rfifo_rdreq), .wrreq(Rd_data_vaild), .empty(), .full(), .q(tx_data), .usedw(rfifo_usedw) ); //串口发送使能 assign tx_en = ((rfifo_usedw == Rddata_num)&&Done)|| ((rfifo_usedw < Rddata_num)&& (rfifo_usedw >0)&&tx_done); //读 FIFO 模块读请求 assign rfifo_rdreq = tx_en; //串口发送模块例化 uart_byte_tx uart_tx( .Clk(Clk), .Rst_n(Rst_n), .send_en(tx_en), .baud_set(Baud_set), .Data_Byte(tx_data), .Rs232_Tx(Uart_tx), .Tx_Done(tx_done), .uart_state() ); endmodule下面对设计的整个系统进行编写 testbench 文件来仿真验证,整个仿真主要是通过串口发送模块模拟对该系统发送指令进行仿真验证,这里就只选用 M24LC64 这个仿真模型进行仿真,M24LC04 的仿真模型类似(读者可尝试对该模型进行仿真),编写的 testbench 文件具体代码如下: `timescale 1ns/1ns `define CLK_PERIOD 20 module uart_eeprom_tb; reg Clk; reg Rst_n; reg tx_en; reg [7:0] tx_data; wire tx_done; wire Uart_rx; wire Uart_tx; wire Sda; wire Scl; reg [15:0] addr; reg [7:0] data_num; reg [7:0] wr_data; reg [39:0] wdata_cmd; reg [39:0] rdata_cmd; localparam Baud_set = 3'd4; //波特率设置,这里设置为 115200 localparam DevAddr = 3'b000;//I2C 器件的器件地址 localparam WdAr_NUM = 2'd2; //I2C 器件的存储器地址字节数 //串口发送模块例化 uart_byte_tx uart_tx( .Clk(Clk), .Rst_n(Rst_n), .send_en(tx_en), .baud_set(Baud_set), .Data_Byte(tx_data), .Rs232_Tx(Uart_rx), .Tx_Done(tx_done), .uart_state() ); //串口读写 EEPROM 模块例化 uart_eeprom #(.Baud_set(Baud_set)) uart_eeprom( .Clk(Clk), .Rst_n(Rst_n), .Uart_rx(Uart_rx), .Uart_tx(Uart_tx), .Sda(Sda), .Scl(Scl) ); //EEPROM 模型例化 M24LC64 M24LC64( .A0(1'b0), .A1(1'b0), .A2(1'b0), .WP(1'b0), .SDA(Sda), .SCL(Scl), .RESET(!Rst_n) ); //系统时钟产生 initial Clk = 1'b1; always #(`CLK_PERIOD/2)Clk = ~Clk; initial begin Rst_n = 0; tx_data = 0; tx_en = 0; addr = 0; data_num = 0; wr_data = 0; wdata_cmd = 0; rdata_cmd = 0; #(`CLK_PERIOD*200 + 1) Rst_n = 1; #200; addr = 0; data_num = 4; wr_data = 0; send_uart_data_wr;//写数据 @(posedge uart_eeprom.I2C.Done); #500; send_uart_data_rd;//读数据 @(posedge uart_eeprom.I2C.Done); #500; addr = 4; data_num = 8; wr_data = 20; send_uart_data_wr;//写数据 @(posedge uart_eeprom.I2C.Done); #500; send_uart_data_rd;//读数据 @(posedge uart_eeprom.I2C.Done); //从 EEPROM 读出的数据串口发送出去,等待发送完成 repeat(data_num)begin @(posedge uart_eeprom.tx_done); end #5000; $stop; end //串口发送写数据命令和待写入数据任务 task send_uart_data_wr; begin //写数据指令 wdata_cmd = {{2'b00,WdAr_NUM,1'b0,DevAddr},8'hf1, addr[15:8],addr[7:0],data_num}; //发送写数据指令 repeat(5)begin tx_en = 1; tx_data = wdata_cmd[39:32]; #(`CLK_PERIOD) tx_en = 0; @(posedge tx_done) #100; wdata_cmd = {wdata_cmd[31:0],8'h00}; end //待写入数据 tx_data = wr_data; repeat(data_num)begin tx_en = 1; tx_data = tx_data + 1; #(`CLK_PERIOD) tx_en = 0; @(posedge tx_done) #100; end end endtask //串口发送读数据命令任务 task send_uart_data_rd; begin //读数据指令 rdata_cmd = {{2'b00,WdAr_NUM,1'b0,DevAddr},8'hf2, addr[15:8],addr[7:0],data_num}; //发送读数据指令 repeat(5)begin tx_en = 1; tx_data = rdata_cmd[39:32]; #(`CLK_PERIOD) tx_en = 0; @(posedge tx_done) #100; rdata_cmd = {rdata_cmd[31:0],8'h00}; end end endtask endmodule仿真过程主要是通过串口发送模块模拟对该系统发送读写指令进行仿真验证,分别进行了 2 次写和读指令的发送,读指令主要是对刚写入地址的数据进行读出,我们可以通过观察时序波形图,比较写入数据与读出数据是否一致来验证系统设计的正确性。如图 30.27 为系统仿真波形时序图。 图片 分别对一次写数据指令和一次读数据指令的波形进行放大后观察分析,如图 30.28 和图30.29 分别为一次对 EEPROM 写数据过程和读数据过程的波形时序图。 图片 这次写数据过程的仿真是通过串口发送模块模拟发送指令数据 0x21,0xf1,0x00,0x00,0x04,0x05,0x06,0x07,0x08,向 EEPROM 中起始地址为 0 开始写入 0x05,0x06,0x07,0x08 四个数据,可以通过观察图 30.28 的中 SDA 和 SCL 波形图分析得知,写入的数据确实是这四个数据,说明系统中串口写 EEPROM 部分是没有问题的。 读数据过程的仿真是通过串口发送模块模拟发送指令数据 0x21,0xf2,0x00,0x00,0x04,向 EEPROM 中起始地址为 0 开始读出四个数据,通过观察图 30.29 的中 SDA和 SCL 波形图分析得知,读出的四个数据为 0x05,0x06,0x07,0x08,与之前在这些地址写入的数据是一致的,说明这次的读数据时没有问题的,同样的方式经过多次的验证,串口读写 EEPROM 系统是可以正常工作的。仿真验证进行完成后,接下来就是进行板级验证,先是引脚的分配,本实验板级验证平台是 AC620 开发板,根据开发板的引脚分配表对本实验所用引脚进行分配。如图 30.30 是 AC620 开发板上 EEPROM 部分的硬件原理图,在引脚分配之前,是需要对这一块硬件电路有所了解的。 图片 细心的读者会发现,I2C 时钟线 SCL 和 I2C 数据线没有进行硬件上拉处理,与前面讲解的需要上拉处理不一样,可能会猜想是硬件设计的问题。这里我说明一下,硬件设计是没有问题的,因为对于 FPGA 是可以通过软件对引脚进行上拉处理的,这个也是本实验包含的一个知识点。通过 Quartus II 软件将管脚设置为上拉电阻(弱上拉)的方法,具体的步骤如下: 1.在菜单 Assignments 中选择 Pin Planner,也可以直接点击面板上引脚分配的图标 图片 2.这样进入引脚分配的界面,在弹出的 Pin Planner 界面的 All Pins 区域里点击鼠标右键,找到 Customize Columns。 图片 在弹出的 Customize Columns 对 话 框的 左 列表 框 选 择 Weak Pull-Up Resistor,再点击,把 Weak Pull-Up Resistor 添加到右列表框,这样在 Pin Planner 的 All Pins 区域里就有一列 Weak Pull-Up Resistor 的设置项。 图片 再把需要上拉的 SDA 和 SCL 在其对应的 Weak Pull-Up Resistor 列的位置双极鼠标左键,就会弹出一个 Off/On 的选项,选上 On 就可以了。最后完成后的设置如下: 图片 引脚分配和上拉设置完成后,对我们设计的系统顶层文件进行综合布局布线生成 sof 下载文件,然后下载到 AC620 实验平台进行板级验证。板级验证需要用到串口软件工具, 这里使用的是名叫格西烽火的串口工具,选择对应的开发板的串口号和波特率,系统设计的波特率在顶层文件 uart_eeprom 中可进行设置,这里我们设置的波特率为115200bps。 板级验证时,我们先往 EEPROM 里写入一些数据,也就是在串口发送 6 组数据,每组数据写入四个数据;写完后发送读数据命令,读出写入的数据,分两次读,一次读 20个数据,一次读 4 个数据,具体的发送读写指令操作和串口接收的数据如下图: 图片 从串口发送和接受的数据分析可得,从 EEPROM 读出的 20 个数据和写入的 20 个数据是一样的,这就说明,设计的整个系统是工作正常的,读者可以多次进行多组测试来验证设计的完整性,整个的测试还可以利用 Quartus II 软件中的 SignalTap II Logic Analyzer 工具对 SDA 和 SCL 的波形进行抓取分析系统设计的正确性,这里就不详细的说明了,读者可以自己进行尝试。整个串口读写 EEPROM 的系统设计就算完成了,里面还有很多需要完善的地方,读者可以在学习完成后,对本实验进行改进和拓展。 本节小结 本节主要从二线制 I2C 协议的基本知识和概念、 I2C 器件之间数据通信过程,比较全面的了解了 I2C 协议,并以此设计一个可进行读写操作的 I2C 控制器,结合前面设计的串口收发模块和 FIFO 模块设计了一个简易应用,实现了串口对 EEPROM 存储器的读写功能,并进行相应的板级验证。读者可以在此基础上进行一定的优化和拓展,AC620实验开发板上还有音频模块和时钟模块挂在在 I2C 总线上,读者可以利用这些模块实现更丰富的应用
FPGA&ASIC
# ASIC/FPGA
# 小实验
刘航宇
3年前
0
1,783
6
【LoRa】Chirp调制
线性调频信号的表征与特征 线性调频(Linear Frequency Modulation, LFM) 是一种不需要伪随机编码序列的扩展频谱调制技术。因为线性调频信号占用的频带宽度远大于信息带宽,所以也可以获得很大的系统处理增益。线性调频信号也称为鸟声(Chirp) 信号,因为其频谱带宽落于可听范围,听着像鸟声(英文单词Chirp为鸟叫的意思),所以又称Chirp护展频谱(Chirp SpreadSpectrum,CSS)技术。LFM技术在雷达、声纳技术中有广泛应用,例如,在雷达定位技术中,它可用来增大射频脉冲宽度、加大通信距离、提高平均发射功率,同时又保持足够的信号频谱宽度,不降低雷达的距离分辨率。 将CSS技术用于扩频通信的研究发展日益活跃,尤其随着超宽带(UWB) 技术的发展,将CSS技术与UWB的宽带低功率谱相结合形成的Chi rp-UWB通信,它利用Chi rp技术产生超宽带宽,具备二者优势,增强了抗干扰与抗噪声的能力。CSS技术已成为传感网络通信标准,IEEE802. 15中物理层候选标准。 FM、FSK、CSS信号比较 图片 线性调频(LFM)信号 图片 Chirp信号调制技术的产生与检测 Chirp通信信号-般形式 通信的二元数据也可用LFM信号,常称为Chirp信号来传输。最常用做法是用围绕着中心频率f的正向和负向频率斜升变化来代表二元信码“1”与“0” 接收端采用两个相应的匹配滤波器来检测。匹配滤波器 输出是一个峰值功率正比于时间带宽积FT的压缩脉冲, 通过取样判决可以恢复出信码“1”。代表信码“0”的负斜 率Chirp信号通过对应的负斜率匹配滤波器可得出与正斜 率匹配滤波器相同结论的压缩脉冲,通过取样判决确定 信码“0”。 图片 2.信号调制 图片 3.Chirp信号解调和检测 图片 LoRa调制 图片 图片 图片
通信&信息处理
# 信号处理
刘航宇
4年前
0
1,954
2
2022-12-25
【FPGA】【SPI】线性序列机与串行接口 ADC 驱动设计与验证
本章导读 模数转换器即 A/D 转换器,或简称 ADC(Analog to Digital Conver),通常是指一个将模拟信号转变为数字信号的电子元件。通常的模数转换器是把经过与标准量比较处理后的模拟量转换成以二进制数值表示的离散信号的转换器。 模数转换器的种类很多,按工作原理的不同,可分成间接 ADC 和直接 ADC。间接 ADC是先将输入模拟电压转换成时间或频率,然后再把这些中间量转换成数字量,常用的有双积分型 ADC。直接 ADC 则直接转换成数字量,常用的有并联比较型 ADC 和逐次逼近型 ADC。 并联比较型 ADC:采用各量级同时并行比较,各位输出码也是同时并行产生,所以转换速度快。并联比较型 ADC 的缺点是成本高、功耗大。 逐次逼近型 ADC:它产生一系列比较电压 VR,但它是逐个产生比较电压,逐次与输入电压分别比较,以逐渐逼近的方式进行模数转换。它比并联比较型 ADC 的转换速度慢,比双分积型 ADC 要快得多,属于中速 ADC 器件。 双积分型 ADC:它先对输入采样电压和基准电压进行两次积分,获得与采样电压平均值成正比的时间间隔,同时用计数器对标准时钟脉冲计数。它的优点是抗干扰能力强,稳定性好;主要缺点是转换速度低。 本节以 ADC128S022 为例介绍 ADC 的工作原理及时序图解释,并用线性序列机来描述时序图进而正确驱动此类设备。本实验中,直接使用 AD/DA 模块上的 DAC 模块的输出直接接到 ADC 模块的输入上,通过控制 DAC 输出不同电压值,然后比较 DAC 的输出与 ADC采样到的值的大小,来评估 ADC 驱动的正确性。 ADC 芯片概述及电路设计 ADC128S022 型 ADC 内部工作原理 在 AC620 开发板上使用的模数转换器为逐次逼近型的低功耗芯片 ADC128S022,其具有 8 通道以及 12 位的分辨率。电源采用独立的模拟供电以及数字供电,其中模拟电源 VA输入范围为 2.7V~5.25V,数字电源 VD 输入范围为 2.7V~VA。其与外部通信支持多种接口如:SPI、QSPI、MICROWIRE 以及通用的 DSP 接口。转换速度在 50kps~200kps,典型情况下当3V 供电时功耗为 1.2mW,5V 供电时为 7.5mW。 图片 本款 ADC 为 12 位分辨率,因此 1bit 代表的电压值即为 VA/4096。手册中同时指出,当模拟输入电压低于 VA/8192 时,输出数据即为 0000_0000_0000b。同理由于芯片本身内部构造当输出数 0000_0000_0000b 变为 0000_0000_0001b 时,实际输入电压变化为 VA/8192 而不是 VA/4096。当输入电压大于等于 VA-1.5* VA/4096,输出数据即为 1111_1111_1111b。 ADC128S022 型 ADC 芯片引脚功能 ADC128S022 芯片引脚及功能描述如表 25.1 所示。 图片 ADC128S022 电路设计 ADC128S022 部分电路图如图 25.2 所示。这里外接了一个抗混叠低通滤波器,其作用是为了避免输入信号的高频成分在 ADC 的基带中引起混叠。数字电源与模拟电源电压均为3.3V,且数字电源与模拟电源之间串联磁珠用于抑制电磁干扰。 图片 传输特性 图片 ADC128S022 型 ADC 接口时序 微控制器与 ADC128S022 的接口如图 25.3 所示,该接口使用标准的 SPI 接口,因此可以直接连接到微控制器的片上 SPI。对于 FPGA 来说,则可以使用逻辑按照 SPI 时序搭建控制电路,以实现对 ADC128S022 的控制。 图片 ADC128S022 通过 SPI 接口与控制器进行通信的时序图如图 所示。一个串行帧开始于CS̅̅̅的下降沿,结束于CS̅̅̅的上升沿。一帧包含 16 个上升沿 SCLK。ADC 的 DOUT 引脚当CS̅̅̅为高时代表空闲状态,当为低时为传输状态。也就是相当于CS̅̅̅可以充当输出使能。在CS̅̅̅为高时 SCLK 默认高。在头三个 SCLK 的循环,ADC 处于采样模式(track)。在接下来的 13 个循环为保持模式(hold),转换完成并且完成数据输出。下降沿 1~4 为前导零,5~16 为输出转换结果。如果在持续测量模式中,ADC 在 N(16) 个 SCLK 的上升沿会自动进去采样模式,在N16+4 个下降沿进入保持/转换模式。 图片 图片 进入采样模式有三种方式,第一种当 SCLK 为高电平时 CS 变为低,当 SCLK 第一个下降沿到来时便进入采样模式,如时序图所示;第二种当 SCLK 为低电平时CS变低,自动进入采样模式,CS的下降沿即视为 SCLK 的第一个下降沿;第三种 SCLK 与CS一同变为低,这样对于两信号上升沿没有时序约束,但对于其下降沿有要求。 在 DIN 的 8 个控制寄存器,每一位代表的含义如表 25.2 所示。 图片 其中 ADD[2:0]代表的输入通道选择如表 25.3 所示。 图片 图片 ADC128S022 接口时序设计 经查阅手册可知器件工作频率 SCLK 推荐范围为 0.8~3.2MHz,这里定义其工作频率为1.92MHz(周期为 520ns)。设置一个两倍于 SCLK 的采样时钟 SCLK2X,使用 50M 系统时钟十三分频而来即 SCLK2X 为 3.84MHz。针对 SCLK2X 进行计数来确定图 25.4 中各个信号的状态。结合前面 ADC 的接口时序图,按照线性序列机的设计思路,可以整理得到每个信号发生变化时对应的时刻以及此时对应的计数器的值。表25.4为依照线性序列机的设计思想, 整理得到的每个信号发生变化时对应的时刻以及此时对应的计数器的值。其中 CS_N 为芯片 状态标志信号,SCLK 为芯片时钟输入脚,DIN 为芯片串行数据输入,DOUT 为芯片串行数据输出。 图片 图片 线性序列机计数器的控制逻辑判断依据,如表 4.17.6 所示。 图片 以上就是通过线性序列机设计接口时序的一个典型案例,可以看到,线性序列机可以大大简化设计思路。线性序列机的设计思想就是使用一个计数器不断计数,由于每个计数值都会对应一个时间,那么当该时间符合需要操作信号的时刻时,就对该信号进行操作。这样,就能够轻松的设计出各种时序接口了。 基于线性序列机的 ADC 驱动设计 模块接口设计 ADC128S022 接口逻辑模块图如图 25.5 所示。 图片 每个端口功能描述如表 25.5 所示。 图片 图片 在每次使能转换的时候,寄存当前状态通道选择 Channel 的值,防止在转换过程中该值发生变化对转换过程产生影响。 reg [2:0]r_Channel; //通道选择内部寄存器 always@(posedge Clk or negedge Rst_n) if(!Rst_n) r_Channel <= 3'd0; else if(En_Conv) r_Channel <= Channel; else r_Channel <= r_Channel;生成使能信号,当输入使能信号有效后便将使能信号 en 置 1,当转换完成信号有效时便将其重新置 0。 reg en; //转换使能信号 always@(posedge Clk or negedge Rst_n) if(!Rst_n) en <= 1'b0; else if(En_Conv) en <= 1'b1; else if(Conv_Done) en <= 1'b0; else en <= en;在数据手册中SCLK的频率范围为0.8~3.2MHz。这里为了方便适配不同的频率需求率,设置了一个可调的计数器。根据表中可以看出,需要根据计数器的值周期性的产生 SCLK时钟信号,这里可以将计数器的值等倍数放大,形成过采样。这里产生一个两倍于 SCLK 的信号,命名为 SCLK2X。 首先编写分频计数器,时钟 SCLK2X 的计数器。 reg [7:0]DIV_CNT;//分频计数器 always@(posedge Clk or negedge Rst_n) if(!Rst_n) DIV_CNT <= 8'd0; else if(en)begin if(DIV_CNT == (DIV_PARAM - 1'b1)) //时钟分频设置 DIV_CNT <= 8'd0; else DIV_CNT <= DIV_CNT + 1'b1; end else DIV_CNT <= 8'd0;根据使能信号以及计数器状态生成 SCLK2X 时钟。 always@(posedge Clk or negedge Rst_n) if(!Rst_n) SCLK2X <= 1'b0; else if(en && (DIV_CNT == (DIV_PARAM - 1'b1))) SCLK2X <= 1'b1; else SCLK2X <= 1'b0;每当使能转换后,对 SCLK2X 时钟进行计数。 always@(posedge Clk or negedge Rst_n) if(!Rst_n) SCLK_GEN_CNT <= 6'd0; else if(SCLK2X && en)begin if(SCLK_GEN_CNT == 6'd33) SCLK_GEN_CNT <= 6'd0; else SCLK_GEN_CNT <= SCLK_GEN_CNT + 1'd1; end else SCLK_GEN_CNT <= SCLK_GEN_CNT;根据 SCLK2X 计数器的值来确认工作状态以及数据传输进程。 reg [11:0]r_data; //转换结果读取内部寄存器 reg [5:0]SCLK_GEN_CNT;//SCLK2X 计数器 always@(posedge Clk or negedge Rst_n) if(!Rst_n)begin SCLK <= 1'b1;CS_N <= 1'b1;DIN <= 1'b1; end else if(en) begin if(SCLK2X)begin case(SCLK_GEN_CNT) 6'd0:begin CS_N <= 1'b0; end 6'd1:begin SCLK <= 1'b0; DIN <= 1'b0; end 6'd2:begin SCLK <= 1'b1; end 6'd3:begin SCLK <= 1'b0; end 6'd4:begin SCLK <= 1'b1; end 6'd5:begin SCLK <= 1'b0; DIN <= r_Channel[2];end 6'd6:begin SCLK <= 1'b1; end 6'd7:begin SCLK <= 1'b0; DIN <= r_Channel[1];end 6'd8:begin SCLK <= 1'b1; end 6'd9:begin SCLK <= 1'b0; DIN <= r_Channel[0];end 6'd10,6'd12,6'd14,6'd16,6'd18,6'd20,6'd22,6'd24,6'd26,6'd28,6'd 30,6'd32: begin SCLK <= 1'b1; r_data <= {r_data[10:0], DOUT}; end 6'd11,6'd13,6'd15,6'd17,6'd19,6'd21,6'd23,6'd25,6'd27,6'd29,6'd 31: begin SCLK <= 1'b0; end 6'd33:begin CS_N <= 1'b1; end//将转换结果输出 default:begin CS_N <= 1'b1; end endcase end else ; end else CS_N <= 1'b1;一次转换结束的标志,即 en && SCLK2X && (SCLK_GEN_CNT == 6'd33)为真,并产生一个高脉冲的转换完成标志信号 Conv_Done。一次转换结束后将内部寄存器 r_data 的数据输出到输出端 Data 上。 always@(posedge Clk or negedge Rst_n) if(!Rst_n)begin Data <= 12'd0; Conv_Done <= 1'b0; end else if(en && SCLK2X && (SCLK_GEN_CNT == 6'd33))begin Data <= r_data; Conv_Done <= 1'b1; end else begin Data <= Data; Conv_Done <= 1'b0; endADC 工作状态,在手册中看出 CS_N 在工作时为低电平,因此直接将此信号作为工作状态指示。 assign ADC_State = CS_N;仿真及板级测试 为了测试模块功能,模拟 ADC 芯片的输出。这里用 Sin3e 产生一个正弦波文件,位宽12 位,个数为 4096,并以 sin_12bit.txt 保存当前工程下的 simulation 目录下的 modelsim 文件夹。 这样就需要将产生的数据,发送到 ADC 驱动模块的输入线 DOUT 上,这里使用系统任务$readmemb,其可以用来从文件中读取数据到存储器中。其格式有以下三种: $readmemb("<数据文件名>",<存贮器名>); $readmemb("<数据文件名>",<存贮器名>,<起始地址>); $readmemb("<数据文件名>",<存贮器名>,<起始地址>,<结束地址>。 首先定义文件存放位置。 `define sin_data_file "./sin_12bit.txt"定义 4096 个 12 位的存储单元,然后将波形数据读取到存储器中。 reg[11:0] memory[4095:0];//测试波形数据存储空间 initial $readmemh(`sin_data_file,memory);在测试时将这些数据整体循环三次,进行验证。其中 gene_DOUT(memory[address]);依次将存储器中存储的波形数据读出,按照 ADC 芯片转换结果输出方式的送到 DOUT 信号线上。 integer i; initial begin Rst_n = 0;Channel = 0; En_Conv = 0; DOUT = 0;address = 0; #101; Rst_n = 1; #100; Channel = 5; for(i=0;i<3;i=i+1)begin for(address=0;address<4095;address=address+1)begin En_Conv = 1; #20; En_Conv = 0; gene_DOUT(memory[address]); @(posedge Conv_Done); //等待转换完成信号 #200; end end #20000; $stop; end将存储空间的数据按照 ADC 的数据输出格式,发送到 DOUT 信号线上,供控制模块采集。 task gene_DOUT; input [15:0]vdata; reg [4:0]cnt; begin cnt = 0; wait(!CS_N); while(cnt<16)begin @(negedge SCLK) DOUT = vdata[15-cnt]; cnt = cnt + 1'b1; end end endtask全编译后进行仿真,可看出此次信号较多。这里首先查看与 ADC 相关的使能信号、状态标志信号以及 SCLK 时钟信号等。选中以上这些信号,可以看出,每当外界输入一个周期的 En_Conv 转换信号,模块内部使能信号 en 就置高,一直到转换完成标志信号 Conv_Done有效再置 0。当 SLK2X 计数器值为 33d 且当 SCLK2X 为高时,便输出一个时钟周期的高脉冲信号,标志着本次转换过程结束。同时 SCLK2X 时钟为 ADC 工作时钟 SCLK 的两倍。以上各信号符合既定的设计。 图片 DIN 为 FPGA 控制 ADC 通道选择的信号,这里选择的通道五也就是 ADD[2:0]=101b,可以看出图每个测量循环中发送 DIN 状态均一致。 图片 查看 DOUT 串行数据输出信号,在每个 SCLK 上升沿 DOUT 均会输出一位数据。在第一个转换过程中,DOUT 首先输出 4 个前导 0,然后依次输出 1000_0000_0000b(MSB),也就是在激励文件中例化的正弦波的第一个数据 800h。并在一次转换完成后,输出并行数据 Data。符合既定设计,可以自行分析第二个转换过程。 图片 查看 CS_N 与 ADC_State 工作状态标志信号,可看出当 ADC 处于转换时为低电平,空闲时为高电平,符合设计要求。 图片 将并行输出数据 Data 设置为模拟形式,主要参数如图 25.10 所示,其中波形所占高度可根据实际情况自行设计,此处暂定 100。 图片 重启仿真后可看出数据输入为 3 个周期的正弦波也就是采样正确,符合既定设计。这里如不重启仿真可能会出现如图 25.12 所示的波形影响观测,此为仿真软件本身问题,可不深究。 图片 为了进行板级仿真,新建测试 ADC_test 顶层文件,在顶层文件中例化 ADC 以及 DAC驱动,并设置好相关使能以及标志信号。再分别生成两个 ISSP 文件,其中驱动 DAC 电压数据的命名为 ISSP_DAC(源位宽为 16),采集 ADC 电压以及通道设置的为 ISSP_ADC(源位宽为 3,探针位宽为 12)。分配引脚并全编译后,下载程序,并启动 ISSP。 1.DAC 的 DA 输出端连接 ADC 的 A0 输入端。如图 25.13 所示,使得 DAC 输出电压为0,可测得电压为 0。 图片 2.DAC 的 DA 输出端连接 ADC 的 A0 输入端。此时更新 DB 的输出值,如图 25.14,可以看出 A0 测量数据保持 0 不变。 图片 3.DAC 的 DB 输出端接 ADC 的 A1 输入端。此时更新 DB 值,其理论输出电压为7FF/FFF 4.096=2.048V ,如图 25.15 所示,使能 A1 测量端可测得输入电压为2535/40963.3=2.04V,在误差允许范围内。 图片 DAC的DB输出端接ADC的 A1 输入端。此时更新 DB 值,其理论输出电压为 BD3/FFF 4.096=3.072V,如图 25.16 所示,使能 A1 测 量 端 可 测 得 输 入 电 压 为 3815/4096 3.3=2.07V,在误差允许范围内。 图片 同理可测试的其他 DAC 输出电压以及 ADC 输入测量电压值。此处换算时需注意,DAC电路输出电压范围为 0~4V,ADC 电路测量电压范围为 0~3.3V。 工程代码 图片 在我们设计的驱动模块中,需要给ADC芯片三个控制信号SCLK、CS_N、DIN,还需要有接收ADC转化结果的ADC_OUT。ADC芯片的选址信号通过Channel端给到ADC_driver中,然后通过ADC_DIN给到芯片中,用于选择采集模拟信号的通道。12位宽的Data输出给外部的FIFO缓存。 module adc128s022( Clk, Rst_n, Channel, Data, En_Conv, Conv_Done, ADC_State, DIV_PARAM, ADC_SCLK, ADC_DOUT, ADC_DIN, ADC_CS_N ); input Clk; //输入时钟 input Rst_n; //复位输入,低电平复位 input [2:0]Channel; //ADC转换通道选择 output reg [11:0]Data; //ADC转换结果 input En_Conv; //使能单次转换,该信号为单周期有效,高脉冲使能一次转换 output reg Conv_Done; //转换完成信号,完成转换后产生一个时钟周期的高脉冲 output ADC_State; //ADC工作状态,ADC处于转换时为低电平,空闲时为高电平 input [7:0]DIV_PARAM; //时钟分频设置,实际SCLK时钟 频率 = fclk / (DIV_PARAM * 2) output reg ADC_SCLK; //ADC 串行数据接口时钟信号 output reg ADC_CS_N; //ADC 串行数据接口使能信号 input ADC_DOUT; //ADC转换结果,由ADC输给FPGA output reg ADC_DIN; //ADC控制信号输出,由FPGA发送通道控制字给ADC reg [2:0]r_Channel; //通道选择内部寄存器 reg [11:0]r_data; //转换结果读取内部寄存器 reg [7:0]DIV_CNT;//分频计数器 reg SCLK2X;//2倍SCLK的采样时钟 reg [5:0]SCLK_GEN_CNT;//SCLK生成暨序列机计数器 reg en;//转换使能信号 //在每个使能转换的时候,寄存Channel的值,防止在转换过程中该值发生变化 always@(posedge Clk or negedge Rst_n) if(!Rst_n) r_Channel <= 3'd0; else if(En_Conv) r_Channel <= Channel; else r_Channel <= r_Channel; //产生使能转换信号 always@(posedge Clk or negedge Rst_n) if(!Rst_n) en <= 1'b0; else if(En_Conv) en <= 1'b1; else if(Conv_Done) en <= 1'b0; else en <= en; //生成2倍SCLK使能时钟计数器 always@(posedge Clk or negedge Rst_n) if(!Rst_n) DIV_CNT <= 8'd0; else if(en)begin if(DIV_CNT == (DIV_PARAM - 1'b1)) DIV_CNT <= 8'd0; else DIV_CNT <= DIV_CNT + 1'b1; end else DIV_CNT <= 8'd0; //生成2倍SCLK使能时钟 always@(posedge Clk or negedge Rst_n) if(!Rst_n) SCLK2X <= 1'b0; else if(en && (DIV_CNT == (DIV_PARAM - 1'b1))) SCLK2X <= 1'b1; else SCLK2X <= 1'b0; //生成序列计数器 always@(posedge Clk or negedge Rst_n) if(!Rst_n) SCLK_GEN_CNT <= 6'd0; else if(SCLK2X && en)begin if(SCLK_GEN_CNT == 6'd33) SCLK_GEN_CNT <= 6'd0; else SCLK_GEN_CNT <= SCLK_GEN_CNT + 1'd1; end else SCLK_GEN_CNT <= SCLK_GEN_CNT; //序列机实现ADC串行数据接口的数据发送和接收 always@(posedge Clk or negedge Rst_n) if(!Rst_n)begin ADC_SCLK <= 1'b1; ADC_CS_N <= 1'b1; ADC_DIN <= 1'b1; end else if(en) begin if(SCLK2X)begin case(SCLK_GEN_CNT) 6'd0:begin ADC_CS_N <= 1'b0; end 6'd1:begin ADC_SCLK <= 1'b0; ADC_DIN <= 1'b0; end 6'd2:begin ADC_SCLK <= 1'b1; end 6'd3:begin ADC_SCLK <= 1'b0; end 6'd4:begin ADC_SCLK <= 1'b1; end 6'd5:begin ADC_SCLK <= 1'b0; ADC_DIN <= r_Channel[2];end //addr[2] 6'd6:begin ADC_SCLK <= 1'b1; end 6'd7:begin ADC_SCLK <= 1'b0; ADC_DIN <= r_Channel[1];end //addr[1] 6'd8:begin ADC_SCLK <= 1'b1; end 6'd9:begin ADC_SCLK <= 1'b0; ADC_DIN <= r_Channel[0];end //addr[0] //每个上升沿,寄存ADC串行数据输出线上的转换结果 6'd10,6'd12,6'd14,6'd16,6'd18,6'd20,6'd22,6'd24,6'd26,6'd28,6'd30,6'd32: begin ADC_SCLK <= 1'b1; r_data <= {r_data[10:0], ADC_DOUT}; end //循环移位寄存DOUT上的12个数据 6'd11,6'd13,6'd15,6'd17,6'd19,6'd21,6'd23,6'd25,6'd27,6'd29,6'd31: begin ADC_SCLK <= 1'b0; end 6'd33:begin ADC_CS_N <= 1'b1; end default:begin ADC_CS_N <= 1'b1; end //将转换结果输出 endcase end else ; end else begin ADC_CS_N <= 1'b1; end //转换完成时,将转换结果输出到Data端口,同时产生一个时钟周期的高脉冲信号 always@(posedge Clk or negedge Rst_n) if(!Rst_n)begin Data <= 12'd0; Conv_Done <= 1'b0; end else if(en && SCLK2X && (SCLK_GEN_CNT == 6'd33))begin Data <= r_data; Conv_Done <= 1'b1; end else begin Data <= Data; Conv_Done <= 1'b0; end //产生ADC工作状态指示信号 assign ADC_State = ADC_CS_N; endmodule ADC工程 下载地址:https://wwek.lanzoub.com/iDiv00jdsyrc 提取码: 仿真结果 图片
FPGA&ASIC
# 小实验
刘航宇
4年前
0
1,809
2
上一页
1
2
3
4
...
9
下一页