首页
📁归档
⏳时光机
📫留言
🚩友链
💰资助名单
推荐
🎧音乐
🏜️ 壁纸
❤ 捐助
Search
1
【NPN/PNP三极管】放大电路饱和失真和截止失真的区别
19,174 阅读
2
论文写作中如何把word里面所有数字和字母替换为新罗马字体
10,136 阅读
3
【高数】形心计算公式讲解大全
8,750 阅读
4
【概论】一阶矩、二阶矩原点矩,中心矩区别与概念
7,447 阅读
5
Vivado-FPGA Verilog烧写固化教程
6,813 阅读
🪶微语&随笔
励志美文
我的随笔
写作办公
📡电子&通信
嵌入式&系统
通信&信息处理
编程&脚本笔记
⌨️IC&系统
FPGA&ASIC
VLSI&IC验证
EDA&虚拟机
💻电子&计算机
IP&SOC设计
机器学习
软硬件算法
登录
55(共79篇)
找到
79
篇与
55
相关的结果
- 第 2 页
数字锁相环(DPLL)研究与设计
前言 工程代码下载 锁相环的原理和组成 数字锁相环的原理和组成 数字鉴相器设计DPD 数字振荡器(DCO) 数字缓冲器(DB) 数字锁相环(DPLL)的实现 电路硬件与性能评估 前言 随着数字电路技术的发展,数字锁相环在调制解调、频率合成、FM 立体声解码、彩色副载波同步、图象处理等各个方面得到了广泛的应用。数字锁相环不仅吸收了数字电路可靠性高、体积小、价格低等优点,还解决了模拟锁相环的直流零点漂移、器件饱和及易受电源和环境温度变化等缺点,此外还具有对离散样值的实时处理能力,已成为锁相技术发展的方向。 所谓数字PLL,就是指应用于数字系统的PLL,也就是说数字PLL中的各个模块都是以数字器件来实现的,是一个数字的电路。 数字锁相环的优点是电路最简单有效,可采用没有压控的晶振,降低了成本,提高了晶振的稳定性。但缺点是和模拟锁相环一样,一旦失去基准频率,输出频率立刻跳回振荡器本身的频率;另外还有一个缺点,就是当进行频率调整的时候,输出频率会产生抖动,频差越大,抖动会越大于密,不利于某些场合的应用。随着大规模、超高速的数字集成电路的发展,为数字锁相环路的研究与应用提供了广阔空间。由于晶体振荡器和数字调整技术的加盟,可以在不降低振荡器的频率稳定度的情况下,加大频率的跟踪范围,从而提高整个环路工作的稳定性与可靠性。 简单的说有两个不同来源的信号:一个信号是参考信号,这个信号一般是由芯片的晶振得到的信号,它具有信号的稳定性较好等优点,但是其频率是固定不变的。另一个信号是由芯片或者模块内部的压控振荡器得到的。这种由压控振荡器得到的信号可以是某范围内的任意频率的信号,但是这种信号的稳定型较差,容易受到外界干扰。 那么在实际使用过程中,我们需要一种频率能够变化的,同时质量较好的信号;或者对于一块芯片,我们需要不同的模块的内部时钟(这种时钟可以是压控振荡器产生)都能参考一个总的时钟来进行同步,从而避免两个模块内部时钟的差异而产生的数据传输的漂移等问题。因此,如何将压控振荡器得到的信号能够具有晶振信号的信号质量呢? 那就是通过PLL锁相环来实现,如图1所示。只要压控振荡器产生的时钟(下称输入信号)是参考信号的整数倍(或者整除倍),那么就能将输入信号先进行分频,后得到与参考信号频率相同的时钟,将分频后的信号和参考信号进行比较,从而使分频后的信号和参考信号保持相同的稳定的频率和相位。被分频后的信号稳定,也就是间接的表示输入信号的稳定。从而我们得到了一个频率在一定范围内可变的稳定的信号。 有上述可以看出,锁相环具有以下功能: (1)能够将一个信号和另一参考信号同步; (2)当这个信号是输出信号分频后得到的信号,PLL就能够得到参考信号的倍频信号(实际上倍频器很多都是利用了这个功能); (3)当输入信号频率可变、分频系数可变时,PLL就能够得到在频率一定范围内稳定信号。 图片 工程代码下载 DPLL 下载地址:https://wwek.lanzoub.com/iJLd5102ig6j 提取码: 锁相环的原理和组成 锁相环(PLL)的作用我们已经大概了解了,其最主要功能的实现,是在于如何将两个频率不同、相位差始终在变化的信号,变成两个相同频率、相同相位的信号。 这里引入一个概念,首先我们都知道,对于三角函数,只有两个同频率的三角函数才能比较其相位差。但这里的相位差是指两个正弦函数的初始相位差。而实际上根据三角函数的欧拉定义的理解来看,我们可以把三角函数看做是在某个圆上逆时针运动的点到x轴的距离。那么频率就是点在圆上运动的角速度,频率越大,其运动的角速度越大。相位就是点在圆上的位置,而初始相位就是点在圆上开始运动时的位置。当两个点的运动角速度相同时,我们可以得到两个点的初始位置差,就是两个正弦函数频率相同时,得到初始相位差。这个差值在运动过程中一直是不变的。但是当两个点运动角速度不同时,我们去看它的初始位置差是没有意义的,因为两个点的位置差是一直在变的,而初始位置差只是一个开始的位置差,是个不变的量,所以说对于频率不同的三角函数,我们讨论起初始相位差是没有意义的。但是不代表不能比较某一时刻两个点的位置。也就是相位差,相位是存在的。 图片 现在我们假设两个点在圆上赛跑,如图3所示,我们想让这两个点角速度相等。那么有一个办法就是以一个点为参考,参考点角速度不变,另一个点是速度可变点。每过一段时间,观察另一个点到参考点的位置,是在前,还是在后。如果在前,就让另一个点速度慢一点;如果在后,就让另一个点速度快一点。就这样不断调整另一个点的角速度,直到每次观察两个点都处于相同的位置。这样我们就可以认为这两个点达到了相同的速度。这种方法就是利用反馈调节来实现两个信号的同频同相。也就是锁相环(PLL)的实现原理。 首先通过一个鉴相器来得到两个信号之间的相位差。并根据相位差输出电压信号。然后通过滤波器稳压后得到稳定的电压信号,该信号驱动压控振荡器得到新的频率的信号。当两个信号存在相位差时,电压信号就会改变,从而使受控信号不断变化。直到当两个信号没有相位差时,电压信号不再改变,从而使受控信号保持当前频率,这时,受控信号不再变化了,就叫做受控信号被锁定了。 由上所述,一个锁相环由鉴相器、滤波器、振荡器三部分组成。外部输入是参考信号,内部输入和总的输出是受控信号。 数字锁相环的原理和组成 在数字电路中,原来模拟信号正弦波、余弦波的频率和相位变成了0和1的脉冲信号,那么我们如何理解数字信号中的频率和相位呢?对于脉冲信号来说,我们可以把频率理解为在某固定时间内脉冲出现的个数,为了方便表示,我们把上升沿的出现视为脉冲的出现,把相邻两个脉冲出现的时间t求倒数,就得到了该信号在这个时刻处的信号频率。而对于相位,相位差就是指,存在两个脉冲信号,以一个脉冲信号为参考,在其出现脉冲后,到另一个信号出现脉冲之间的时间差就是相位差,当另一个信号脉冲晚于参考信号脉冲出现的时间,称之为另一个信号的相位滞后于参考信号。当另一个信号的脉冲出现在参考信号之前,称之为另一个信号的相位提前于参考信号。 上述是一种较为简单的描述方式,适合初识脉冲信号的读者理解。而实际上,对于脉冲信号的频率、相位等问题,严格来说这样理解有一点点问题,但是对于我们来搭建数字锁相环DPLL来说足够了。其实这种三角函数和信号之间的转化,其根本的原理来源于傅里叶变换,从而我们对一个时间域上的信号(例如脉冲信号)可以进行频率域(其代表的三角函数的合成)上的分析。 我们知道了在数字电路中,脉冲信号也有了频率和相位的属性。那么我们的参考信号是以来时钟源的固定频率的信号,因为信号的质量比较好,所以该信号两个脉冲之间的时间差均是相同的,误差很小。我们在参考信号出现上升沿时,观察受控信号此时的状态。如果受控信号为高电平,我们就认为此时受控信号超前于参考信号;反之,如果受控信号是低电平,则认为此时的受控信号滞后于参考信号。当出现超前状态时,鉴相器会输出一个超前信号,超前信号会作用于振荡器,使得振荡器发出的受控信号频率降低。而滞后信号会使振荡器发出的受控信号频率升高,从而实现受控信号频率的反馈调节。 如图4所示,当参考信号出现上升沿时,受控信号为低电平,此时输出一个滞后信号。(由于模块只在时钟为上升沿时触发,所以超前信号的触发延迟了半个时钟周期) 图片 由此我们能够大概了解了数字锁相环中如何看待脉冲信号的频率和相位,如何处理得到相位差以及相位差如何在锁相环中起作用来实现信号频率的反馈控制。同模拟的锁相环(PLL)类似,数字锁相环(DPLL)也是由:数字鉴相器(Digital Phase Detector)、数字缓冲器(Digital Buffer)、数字振荡器(Digital Controlled Oscillator)三个模块构成,其外部输入为参考信号,内部输入和输出为受控信号。下面我们就来具体讨论如何用verilog实现各个模块。 数字鉴相器设计DPD 实现一个数字锁相环(DPLL),最重要的部分就是实现数字鉴相器(DPD)和数字振荡器(DF)。并且,这两个模块并不是独立存在的,而是说,数字振荡器的实现方式和数字振荡器的实现方式相互影响。所以只有两个模块共同设计,才能较好的实现一个数字锁相环的功能。 首先我们来具体讨论一下一个数字鉴相器应该具有那些功能和特性: 顾名思义,数字鉴相器就是能够鉴别两个数字信号相位的差别,并通过信号将这种差别表示出来。由上文我们已经知道了,对于两个矩形方波信号,其相位差可以看做是两个信号先后出现上升沿(或下降沿)之间的时间差。为了方便表示,假设以其中一个信号作为参考信号,另一个信号为受控信号,当参考信号出现上升沿(或下降沿)时,观察另一个信号是否已经出现了上升沿(或下降沿)。 如果还未出现上升沿(或下降沿),则叫做“受控信号滞后于参考信号”,或者简称“滞后”;如果已经出现了上升沿(或下降沿),则叫做“受控信号提前于参考信号”,或者简称“提前”。 而判断上升沿(或下降沿)是否已经出现,方法就是看当参考信号出现上升沿时,受控信号是1还是0:当受控信号为0,表示上升沿还没出现,所以是“滞后”;当受控信号为1,表示上升沿已经出现,所以是“提前”。对于下降沿也是按照同样的方法考虑。 图片 目前为止,我们已经有两个输入,参考信号和受控信号;两个输出,滞后信号和提前信号。如何通过verilog实现上述的输入输出关系呢?首先先讲异或与门,通过图4的描述,我们可以很容易看出来:滞后信号是参考信号与受控信号先异或,异或的结果和受控信号相与得到;提前信号是参考信号与受控信号先异或,异或的结果和参考信号相与得到。再加上一个RST的复位信号,我们可以得到如下图5电路: 图片 根据这个关系,来调节受控信号的频率,从而使受控信号的频率和参考信号最终相同。 再考虑,如果按照上述方法调节,当受控信号和参考信号频率相差很大时,就会出现刚开始有一段时间,受控信号的频率是不断变化,不可预知的。这样的调节效果实时性并不好,需要时间来稳定。因此读者想到,如果能够在参考信号出现上升沿时,就让受控信号也出现上升沿,相当于两个人在赛跑时,当一个人从起点出发时,无论另一个人在哪,强制让另一个人也回到原点,两个人一起从原点出发。这样就能使受控信号和参考信号强制达到相同的频率,只是此时受控信号的占空比不是50%。然后再根据滞后和提前信号,调节受控信号的占空比,从而最终达到50%的占空比。 按照这种方法,鉴相器就需要一个信号输出来表示上升沿的出现。再考虑到电路中的总的时钟源,我们这里采用触发的方法来实现。同时将上述的异或与门加入到代码中可以得到数字鉴相器的代码。但是在实际运用过程中发现,可能存在着受控信号先出现上升沿,从而过早的出现了提前或者滞后信号,导致数字振荡器的计数器上限呈现一个周期变化的不可控的数值的情况。为了避免这种情况,需要仔细考虑参考信号和受控信号如何生成提前和滞后信号这个问题,而不是简单的用异或来实现。如图6表示这种关系。 图片 按照上述代码写出来的数字鉴相器,具有更好的性能。根据这个表格,通过类似状态机的方法,来实现提前信号和滞后信号的输出。 数字振荡器(DCO) 现在我们已经构造出来了一个数字鉴相器,接下来我们将继续探讨如何实现一个数字振荡器(DCO)。 实现一个固定脉冲频率的信号,我们可以通过已知的时钟源,分频得到一定频率范围内的脉冲。具体实现方法就是通过计数器的方式,当出现时钟脉冲时,计数器+1,计数器上限就是分频系数,当计数器的数小于上限的1/2时,输出1,当计数器的数大于上限的1/2时,输出0,当计数器的数超过上限时,计数器归零。这样就能实现对时钟源的分频。 根据上述方法,只要改变计数器的上下限,就能改变分频系数,从而改变输出信号的频率。再参考上文受控信号和滞后提前信号的关系,我们就能通过根据滞后提前信号,改变计数器上下限,来实现对受控信号频率的控制。当计数器上限增加时,分频系数增加,频率减小;当计数器上限减小时,分频系数减小,频率增加;因此有: 滞后信号——>受控信号的频率小——>增加受控信号的频率——>计数器上限减小 提前信号——>受控信号的频率大——>减小受控信号的频率——>计数器上限增加 此外根据上述对上升沿触发同步的说法,当出现上升沿触发信号时,受控信号应强制产生上升沿,即受控信号强制从该脉冲周期的开始处开始,即计数器的数回到0从新开始计数。 综上所述,再加上复位信号,一个数字振荡器的所有构成就有了。到这里,一个数字锁相环(DPLL)其实就已经能够实现了,因为数字滤波器(DB)只是让受控信号的抗干扰能力更强,如图所示是仿真后的结果: 图片 数字缓冲器(DB) 下面再介绍一下数字缓冲器,来使受控信号的抗干扰能力更强。前面我们知道了,持续一个时钟周期的提前信号或者滞后信号能够使数字振荡器的计数器上限加一或者减一。当我的预设的数字振荡器的计数器上限与实际的参考信号的频率对应的计数器上限两个数值相差很大时,就有可能出现锁相环调节时间过长等现象。为了解决这种情况,如果能够让原来持续一个周期的提前信号或滞后信号成倍数的增加,变成持续n个周期的提前信号或者滞后信号,就能够使数字振荡器的计数器上限修改更快,从而更快的到达参考频率附近。但是相应的,受控信号的频率精度就会降低。也就是说,牺牲精度,追求速度。 同时考虑另外一种情况,如果我对速度要求不高,但是对于精度要求较高,同时在信号传输过程中可能存在干扰,导致接收到的提前信号或滞后信号不是完全真实的信号,此时就可以通过一个累加器,只有接受到n个周期的提前信号,或者滞后信号,才对数字振荡器输出一个进位信号或者借位信号,此时数字振荡器的计数器上限才只加减1,这样就能有效的提高精度,减少信号干扰带来的影响。但是这种做法牺牲了数字锁相换锁定的时间。 综上所述,一个时钟周期的提前或滞后信号,对应n个时钟周期的借位或进位信号,是提高锁定速度,降低锁定精度。想法,n个时钟周期的提前或滞后信号,对应一个时钟周期的借位或进位信号,是提高锁定精度,降低锁定速度。因此在实际运用中,应该按照自己的工程需要,合理选择比值。 上述过程的实现方法,是通过一个计数器,当接收到一个提前或滞后信号时,计数器加a,当输出一个进位或借位信号时,计数器减b,调节a和b的比值,就能实现上述过程。 数字缓冲器的仿真效果: 1、分时效果 图片 2、倍时效果 图片 数字锁相环(DPLL)的实现 所有的子模块都已经实现了,剩下的数字锁相环的实现,根据实际的要求,将上述几个模块进行例化就行。例化后的测试结果如图9所示,可以看到受控信号逐渐与参考信号对齐达到锁相环效果。 图片 为了方便起见,对输出信号进行2分频,再次观察输出结果,输出相当于2倍频了,成功完成PD、DCO、Divider等模块正确设计。 图片 电路硬件与性能评估 图11为电路硬件图从图中可以看出各模块的连接关系,每个模块由基本门电路构成。通过性能优化后的的电路如图12所示。 图片 利用SMIC180nm工艺进行电路综合, 时序报告:周期2ns 图片 面积报告:2119um2 图片 功耗报告:uw级别 图片
FPGA&ASIC
# ASIC/FPGA
刘航宇
3年前
1
3,551
3
Microsemi Libero SOC使用示例—建立点灯工程
嵌入式开发中的Hello World,点灯是再也基础不过的实验了,通过点灯实验,可以了解芯片GPIO的控制和开发环境IDE新建工程的流程,对于FPGA来说,每个IO口几乎一样,所以本篇文章主要学习一下如何基于Microsemi Libero集成开发环境建立一个示例工程,让一个LED以500ms的频率闪烁,以Microsemi SmartFusion系列FPGA——A2F200M3F为例,Microsemi其他系列FPGA芯片过程类似。 准备工作软件准备: 硬件准备: 新建工程的主要步骤1.新建工程 2.添加设计文件 3.仿真验证 4.管脚分配 5.程序下载 Microsemi FPGA的Flash结构 准备工作 工欲利其事,必先利其器,充分的准备工作很有必要。 软件准备: Microsemi Libero SoC集成开发环境,并已经成功注册,软件版本推荐V11.8或更高版本。 硬件准备: Microsemi FPGA开发板,主控芯片A2F200M3F-PQ208,其他型号芯片类似。 Flash Pro 4或Flash Pro5下载器,用于给FPGA芯片下载程序和调试。 新建工程的主要步骤 新建工程,选择芯片型号等 新建设计,使用Verilog编写点灯模块。 仿真验证,对编写的点灯模块进行时序仿真,来验证是否满足设计需求。 综合、管脚分配、布局、布线。 生成程序文件,连接开发板,使用FlashPro下载程序到芯片内,观察现象是否和设计的一致。 1.新建工程 和大多数IDE一样,选择Project -> New Project,新建一个工程。 image.png图片 输入工程名称LED_Blink,选择工程存放的路径,工程名称和路径不要有中文字符和空格,选择源文件的类型Verilog或者VHDL。 image.png图片 选择芯片型号,这里选择Microsemi SmartFusion系列下的A2F200M3F芯片,PQ208封装,把鼠标放在所选芯片上,可以查看芯片的详细参数:封装、速度等级、温度范围,内核电压、Flash ROM大小、用户IO数目、RAM大小、Flash ROM大小,ARM Cortex-M3 SoC的外设配置等详细的参数。 图片 选择IO的电平标准,不同的电平标准,高低电平的电压范围是不同的,这里选择默认的LVTTL。 图片 是否创建MSS模块,MSS里有PLL和ARM Cortex-M3的使用,以后用到PLL和ARM核时再添加,这里先不选择,以后需要也可以再创建。 图片 是否导入已经存在的HDL文件,如果已经有一些写好的模块,可以在这里直接导入。 图片 是否导入已经存在的管脚约束文件,这里选择不添加,我们会在后面通过图形化工具来指定管脚。 图片 到这里,工程就创建完成了,然后会在存储路径下生成一个和工程名称一样的文件夹,工程相关的所以文件都存放在这里。主要包括以下几个文件夹: 图片 具体每个文件夹存放的是什么文件,我们在以后的文章再详细介绍。以上的工程配置在创建完工程之后,也可以再次更改,可以通过Project->Project Setting查看或更改配置: 图片 或者通过点击如下图标来进入配置界面: 图片 弹出如下窗口,和新建工程是一样的,可以更改FPGA的型号,但只限于同一个系列内。 2.添加设计文件 Microsemi Libero开发环境支持HDL方式和SmarDesign方式来创建设计,HDL方式支持VerilogHDL和VHDL两种硬件描述语言,而SmartDesign方式和Xilinx的Schematic原理图方式是一样的,是通过图形化的方式来对各个模块之间的连接方式进行编辑,两种方式都可以完成设计。由于本实验功能简单,所以以使用Verilog文件为例。 创建Verilog文件 创建Verilog文件有多种方式,可以直接双击左侧菜单中的Create Design->Create HDL 图片 或者点击File->New->HDL,这两种方式都可以创建一个Verilog设计文件,这里选择Verilog文件。 图片 输入模块名称:led_driver,不用添加.v后缀名,Libero软件会自动添加。 源代码: module led_driver( //input input clk, //clk=2MHz input rst_n, //0=reset //output output reg led ); parameter T_500MS = 999999; //1M reg [31:0] cnt; always @ (posedge clk) begin if(!rst_n) cnt <= 32'b0; else if(cnt >= T_500MS) cnt <= 32'b0; else //cnt < T_500MS cnt <= cnt + 32'b1; end always @ (posedge clk) begin if(!rst_n) led <= 1'b1; else if(cnt >= T_500MS) led <= ~led; end endmodule可以看到,代码非常的简单,定义一个计数器,系统时钟为2MHz=500ns,500ms=1M个时钟周期,当计数到500ms时,LED翻转闪烁。 3.仿真验证 编写完成,之后,点击对号进行语法检查,如果没有语法错误就可以进行时序仿真了。 新建Testbench文件 底部切换到Design Hierarchy选项卡,在led模块上右键选择Create Testbechch创建仿真文件,选择HDL格式。 图片 给创建的testbench文件名一般为模块名后加_tb,这里为:led_driver_tb,因为我们的板子外部晶体为2M,所以这里系统时钟周期为500ns,这个也可以在文件中更改。 图片 点击OK之后,可以看到,Libero软件已经为我们生成了一些基本代码,包括输入端口的定义,系统时钟的产生,输入信号的初始化等等。我们只需要再增加几行即可。 `timescale 1ns/100ps module led_driver_tb; parameter SYSCLK_PERIOD = 500;// 2MHZ reg SYSCLK; reg NSYSRESET; wire led; //add output reg initial begin SYSCLK = 1'b0; NSYSRESET = 1'b0; end initial begin #(SYSCLK_PERIOD * 10 ) NSYSRESET = 1'b0; //add system reset #(SYSCLK_PERIOD * 100 ) NSYSRESET = 1'b1; //add system set end always @(SYSCLK) //generate system clock #(SYSCLK_PERIOD / 2.0) SYSCLK <= !SYSCLK; led_driver led_driver_0 ( // Inputs .clk(SYSCLK), .rst_n(NSYSRESET), // Outputs .led(led ) //add port // Inouts ); endmodule仿真代码也非常简单,输入信号初始化,NSYSRESET在10个时钟周期之后拉低,100个时钟周期之后拉高。 使用ModelSim进行时序仿真 仿真代码语法检查无误后,可以进行ModelSim自动仿真,在安装Libero时,已经默认安装了ModelSim仿真软件,并和Libero进行了关联。直接双击Simulate,Libero会自动打开ModelSim。 图片 可以看到输入输出信号,已经为我们添加好了: 图片 先点击复位按钮,复位系统,然后设置要运行的时间,由于设计的是500ms闪烁一次,这里我们先运行2s,即2000ms,在ModelSim中2秒已经算是很长的时间了,然后点击时间右边的运行按钮,耐心等待,停止之后就会看到led按500ms变化一次的波形了,如下图所示,可以再添加一个cnt信号到波形观察窗口,可以看到cnt周期性的变化。 图片 使用2个光标的精确测量,可以看出,led每隔500ms翻转一次,说明程序功能是正确的。 4.管脚分配 与STM32等MCU不同,FPGA的引脚配置非常灵活,如STM32只有固定的几个引脚才能作为定时器PWM输出,而FPGA通过管脚分配可以设置任意一个IO口输出PWM,而且使用起来非常灵活,这也是FPGA和MCU的一个区别,当然其他的功能,如串口外设,SPI外设等等,都可以根据需要自己用HDL代码来实现,非常方便。 时序仿真正常之后,就可以进行管脚分配了,即把模块的输入输出端口,真正的分配到芯片实际的引脚上,毕竟我们的代码是要运行在真正的芯片上的。 打开引脚配置图形化界面 双击Create/Edit I/O Attributes,打开图形化配置界面,在打开之前,Libero会先进行综合(Synthesize)、编译(Complie),当都运行通过时,才会打开配置界面。 图片 分配管脚 管脚可视化配置工具使用起来非常简单:引脚号指定、IO的电平标准,内部上下拉等等,非常直观。把时钟、复位、LED这些管脚分配到开发板原理图中对应的引脚,在分配完成之后,可以点击左上角的commit and check进行检查。 图片 在分配完成之后,为了以后方便查看已经分配的引脚,可以导出一个pdc引脚约束文件,选择Designer窗口下的File->Export->Constraint File,会导出一个led_driver.pdc文件,保存在工程目录下的constraint文件夹。 图片 一些特殊管脚的处理 SmartFusion系列的FPGA芯片,在分配个别引脚,如35-39、43-47这些引脚时,直接不能分配,这些引脚属于MSS_FIO特殊引脚,具体怎么配置为通用IO,可以查看下一篇文章。而新一代的SmartFusion 2系列的FPGA芯片则没有这种情况。 5.程序下载 管脚分配完成之后,连接FlashPro下载器和开发板的JTAG接口,关闭Designer窗口,选择Program Device,耐心等待几分钟,如果连接正常,会在右侧输出编程信息:擦除、验证、编程等操作,下载完成之后,就会看到板子上的LED闪烁起来了。 Microsemi FPGA的Flash结构 和Altera、Xilinx不同,Microsemi FPGA在下载程序时,并不是下载程序到SPI Flash,而是直接下载到FPGA内部的。目前,FPGA 市场占有率最高的两大公司Xilinx和Altera 生产的 FPGA 都是基于 SRAM 工艺的,需要在使用时外接一个片外存储器以保存程序。上电时,FPGA 将外部存储器中的数据读入片内 RAM,完成配置后,进入工作状态;掉电后 FPGA 恢复为白片,内部逻辑消失。这样 FPGA 不仅能反复使用,还无需专门的 FPGA编程器,只需通用的 EPROM、PROM 编程器即可。而Microsemi的SmartFusion、SmartFusion2、ProASICS3、ProASIC3E系列基于Flash结构,具备反复擦写和掉电后内容非易失性, 因此基于Flash结构的FPGA同时具备了SRAM结构的灵活性和反熔丝结构的可靠性,这种技术是最近几年发展起来的新型FPGA实现工艺,目前实现的成本还偏高,没有得到大规模的应用。 示例工程下载 基于Libero V11.8.2.4的工程下载: LED_Blink.rar 下载地址:https://wcc-blog.oss-cn-beijing.aliyuncs.com/Libero/Libero-2/LED_Blink.rar 提取码:
嵌入式&系统
FPGA&ASIC
IP&SOC设计
# ASIC/FPGA
# 嵌入式
# SOC设计
刘航宇
3年前
0
1,774
2
2023-04-17
MIMO波束赋形技术简介
目录 前言 波束赋形分类 阵列天线 混合波束赋形 总结 参考文献: 前言 在MIMO系统中,波束赋形技术通过调整每个天线阵元上的信号进行加权求和,使天线波束指向某个特定的方向,即将天线能量集中指向某个特定的用户。 d6fb0a3cc8e32c0af0b75df51c853948.png图片 波束赋形分类 根据波束赋形发生位置的不同,波束赋形技术分为模拟波束赋形(AnalogBeamforming, ABF)技术和数字波束赋形(Digital Beamforming, DBF)技术。 4a28f76d72a4df8e47e74e783c80ae1f.png图片 在数字基带之前即时域范围内形成波束,称作数字波束赋形; 在模拟基带之前即频域范围内形成波束,称作模拟波束赋形。 数字波束赋形结构中,每根天线对应的一条射频(RF)链路,产生波束时多条RF链路共同参与,因此可以实现多个数据流共同传输。数字波束赋形使用复杂的硬件结构,可以灵活的调整相位和幅度,产生准确的波束。对于天线数量众多时,导致整个结构的硬件实现非常复杂,成本很高。 模拟波束赋形技术使用成本比较低的模拟移相器,只能调整相位而不能调整幅度,产生波束不一定准确。模拟波束赋形,具有简单的硬件结构,实现成本低,没有多条RF链路,只能传输单数据流 阵列天线 阵列天线实现功能是对多列电磁波进行叠加,不同天线位置会产生不同的电磁波辐射,因此,波束赋形技术与天线位置和摆放有密切关系。 阵列天线包括线阵天线和面阵天线两种 afef214955805f1abe5b1b4a20eee39f.png图片 线阵天线是指所有天线阵元分布在一条直线上,或者所有天线阵元分布在一个圆周上,阵元与阵元的间隔可以是等距的或不等距的; 面阵天线是指所有天线阵元以某个点为中心分布在一个矩形面上,或者所有天线阵元分布在一个圆面上,同样,阵元与阵元的间隔可以是等距的或不等距的。 对于F大线数量较多的情况,天线阵列可能会扩展到三维空间,也是未米人线架构设计的一个方向。 混合波束赋形 数字波束赋形可以产生精确的波束,但是每根天线映射一条RF链路,从硬件实现和成本考虑,该技术适用于天线数量较少的系统。 对于天线数量较多的系统,可以使用实现成本较低的模拟波束赋形,可能导致波束不准确,增益效果不是很好。 7970fc6d2b411b56dab8bc2ab3ff71e9.png图片 因此,对于大规模MIMO系统,结合两者优点,提出了一种混合波束赋形技术,希望在满足硬件条件下,使其增益尽可能达到全数字波束赋形的效果。 在较小的面积内拥有大量天线单元使实现高波束成形增益变得切实可行。具有高度方向性的波束有助于抵消较高工作频率下增加的路径损耗,因为波束将功率控制在特定方向上。 2e760e3d1161bacec0599f6b4fe0d4d9.png图片 总结 Simulink和Matlab联合仿真,能够设计并且仿真单个天线,天线阵列,MIMO波束成型系统。对于雷达、5G等方向,有着重要意义。当然,工具不仅仅只有这一个,ADS也能设计从射频波束混合系统,到天线阵列的仿真。 参考文献: [1]使用Matlab进行5G开发
通信&信息处理
# 通信&射频
刘航宇
3年前
0
1,526
2
【电路基础】ASIC角度练习JK触发器&RS触发器
由于本电路极其简单,原理不做解释 目录 JK触发器真值表 硬件描述语言 JK触发器时序 JK触发器电路图 JK触发器性能--SMIC180nm工艺 RS触发器真值表 RS硬件描述语言 RS触发器时序 RS触发器电路图 RS触发器性能--SMIC180nm工艺 JK触发器 真值表 图片 硬件描述语言 代码 //边沿JK触发器-时序逻辑 //作者:刘航宇 2023/4/15 //Email:hyliu@ee.ac.cn module jk_trigger(clk,j,k,q,qb); input clk,j,k; output q,qb; reg q; wire qb; always @(posedge clk) begin case ({j,k}) 2'b00: q<=1; //jk=00,保持 2'b01: q<=1'b0; //jk=01,则触发器置0 2'b10: q<=1'b1; //jk=10,则触发器置1 2'b11: q<=~q; //11,翻转 //组合逻辑中,为避免生成锁存器,好的代码风格是if语句都加上else,case语句都加上default。 //时序逻辑中,“若无必要,尽量不加else和default”——以减小数据翻转机会,低功耗。 //故此处不写default endcase end assign qb = ~q; endmodule测试文件 //jk触发器测试文件 `timescale 1ns/1ps module jk_trigger_tb; reg j,k,clk;//输入reg是因为要initial wire q,qb; always begin #5 clk = ~clk; end //初始化 //下面这个产生fsdb是Synopsys VCS&Makefile脚本会用到,如果你用Medelsim仿真请删掉这个initial语句以免报错 initial begin $fsdbDumpfile("tb.fsdb");//这个是产生名为tb.fsdb的文件 $fsdbDumpvars; end initial begin clk = 0; j = 1'b0; k = 1'b0;//保持 #30 begin j=1'b0;k=1'b1; end //置0 #20 begin j=1'b1;k=1'b0; end //置1 #20 begin j=1'b0;k=1'b0; end //保持 #20 begin j=1'b1;k=1'b1; end //翻转 #200 $finish; end jk_trigger u1(.j(j),.k(k),.clk(clk),.q(q),.qb(qb)); endmoduleJK触发器时序 上升沿触发,可以看到时序完全正确 图片 JK触发器电路图 图片 之所以这样综合电路综合出一个D触发器,是考虑标准单元库的面积与时序的折中,标准单元相当于基本晶体管搭建而成,比如反相器占用2个晶体管,与非门占用4个晶体管,具体不在赘叙。 图片 图片 JK触发器性能--SMIC180nm工艺 图片 RS触发器 真值表 图片 RS硬件描述语言 代码 //边沿JK触发器-时序逻辑 //作者:刘航宇 2023/4/15 //Email:hyliu@ee.ac.cn module rs_trigger( input wire clk,r,s, output reg q, output wire qb ); always @(posedge clk) begin case ({r,s}) 2'b00: q<=q; //r,s同时为低电平,触发器保持状态不变 2'b01: q<=1'b1; //触发器置1 2'b10: q<=1'b0; //触发器置0 2'b11: q<=1'bx; //不定态 endcase end assign qb = ~q; endmodule测试代码 `timescale 1ns/1ps module rs_trigger_tb(); reg clk,r,s; wire q,qb; always begin #5 clk = ~clk; end //初始化 initial begin clk = 0; r = 1'b0; s = 1'b0;//保持 #30 r=1'b0;s=1'b1; //置1 #20 r=1'b1;s=1'b0; //置0 #20 r=1'b0;s=1'b0; //保持 #20 r=1'b1;s=1'b1; //禁止 #200 $stop; end rs_trigger u2(.clk(clk),.r(r),.s(s),.q(q),.qb(qb)); endmoduleRS触发器时序 上升沿触发,可以看到时序完全正确 图片 RS触发器电路图 image.png图片 image.png图片 image.png图片 RS触发器性能--SMIC180nm工艺 image.png图片
FPGA&ASIC
# ASIC/FPGA
刘航宇
3年前
0
1,437
2
【硬件算法进阶】Verilog实现802.3 CRC-32校验运算电路
循环冗余校验(Cyclic Redundancy Check,CRC)是通信中常用的差错检测编码方式,其基本工作原理是根据输入的信息位(信息码元),按照给定的生成多项式产生校验位(校验码元),并一起传送到接收端。在接收端,接收电路按照相同的规则对接收数据进行计算并生成本地的校验位,然后与收到的校验位进行对比,如果二者不同,则说明传输过程中发生了错误,否则说明传输是正确的。带有CRC校验结果的数据帧结构如表1-2所示。 image.png图片 CRC检验位生成与检测工作包括以下基本步骤。 image.png图片 图1-6是一个并行CRC-32校验运算电路。图中的d[7:0]是输入的用户数据,它是按照字节的方式输入的。load_ini是在对一个新的数据包开始校验计算之前对电路进行初始化的控制信号,经过初始化后,电路内部32比特寄存器的值改变为全1。calc是电路运算指示信号,在整个数据帧输入和CRC校验结果输出的过程中其都应该保持有效(高电平有效)。d_valid为1时表示当前输入的是需要进行校验运算的有效数据。crc[7:0]是电路输出的CRC校验运算结果,它是按照字节方式,在有效数据输入完成后开始输出的,一共有4个有效字节。crc_reg[31:0]是内部寄存器的值,具体使用时不需要该输出。 image.png图片 并行计算的思想,输入数据S要并行输入到G(x)系数为1的支路中,输入数据从输入端按高到低逐bit输入,就可以实现。 假如被除数是2位的数据S[1:0]=01,多项式是10011,x4 +x+1。在CRC校验里面,习惯省略最高位的1,多项式用0011表示。那么S除以0011的模二运算数字电路结构为: image.png图片 其中d1~ d4是寄存器输入;q1~q4是寄存器输出。寄存器需要赋初值,一般赋全1或全0。 d1=S[1]^q4; d2= S[1]^ q1^q4; d3=q2; d4=q3。 经过一次移位后: q1=d1= S[1]^q4; q2= d2= S[1]^ q1^q4; q3= d3=q2; q4= d4=q3。 此时有: d1=S[0]^q3; d2= S[0]^ S[1]^ q4^q3; d3= S[1]^ q1^q4; d4= q2。 令c[3:0]={q4,q3,q2,q1},d[3:0]={d4,d3,d2,d1},那么d就是最终的运算结果表达式,如下 d[3]=c[1]; d[2]= S[1]^ c[0]^c[3]; d[1]= S[0]^ S[1]^ c[3]^ c[2]; d[0]= S[0]^ c[2]。 令c的初值为0,则01对0011的模二除法的余数为0011。 再比如多项式为x5 +x3 +x+1,简记式为01011,其数字电路结构为: image.png图片 输入数据S要全部输入完,寄存器得到的结果才是最后的结果。同理可推导出其他多项式和输入数据的情况。 对于循环检验,这里举个例子,如果数据是10bit*100个包,则每次输入10bit得到校验码后,该检验码为下次数据计算时寄存器D的初值,如此反复计算得到最后的检验码添加到整个数据后面即可,而不需要每个数据包后面都添加检验码。 下面是以太网循环冗余校验电路的设计代码: module crc32_8023( clk, reset, d, load_init, calc, d_valid, crc_reg, crc ); input clk; input reset; input [7:0] d; input load_init; input calc; input d_valid; output reg [31:0] crc_reg; output reg [7:0] crc; wire [2:0] ctl; wire [31:0] next_crc; wire [31:0] i; assign i = crc_reg; assign ctl = {load_init,calc,d_valid}; always @(posedge clk or posedge reset) begin if(reset) crc_reg <= 32'hffffffff; else begin case (ctl) //{load_init,calc,d_vaild} 3'b000,3'b010: begin crc_reg <= crc_reg; crc <= crc;end 3'b001: begin crc_reg <= {crc_reg[23:0],8'hff}; crc <= ~{crc_reg[16],crc_reg[17],crc_reg[18],crc_reg[19],crc_reg[20],crc_reg[21],crc_reg[22],crc_reg[23]}; //crc <= ~ crc_reg[16:23]; end 3'b011: begin crc_reg <= next_crc[31:0]; crc <= ~{next_crc[24],next_crc[25],next_crc[26],next_crc[27],next_crc[28],next_crc[29],next_crc[30],next_crc[31]}; //crc <= ~ next_crc[24:31]; end 3'b100,3'b110: begin crc_reg <= 32'hffffffff; crc <= crc; end 3'b101: begin crc_reg <= 32'hffffffff; crc <= ~{crc_reg[16],crc_reg[17],crc_reg[18],crc_reg[19],crc_reg[20],crc_reg[21],crc_reg[22],crc_reg[23]}; //crc <= ~ crc_reg[16:23]; end 3'b111: begin crc_reg <= 32'hffffffff; crc <= ~{next_crc[24],next_crc[25],next_crc[26],next_crc[27],next_crc[28],next_crc[29],next_crc[30],next_crc[31]}; //crc <= ~ next_crc[24:31]; end endcase end end assign next_crc[0] = d[7]^i[24]^d[1]^i[30]; //d+i=31 assign next_crc[1] = d[6]^d[0]^d[7]^d[1]^i[24]^i[25]^i[30]^i[31]; assign next_crc[2] = d[5]^d[6]^d[0]^d[7]^d[1]^i[24]^i[25]^i[26]^i[30]^i[31]; assign next_crc[3] = d[4]^d[5]^d[6]^d[0]^i[25]^i[26]^i[27]^i[31]; assign next_crc[4] = d[3]^d[4]^d[5]^d[7]^d[1]^i[24]^i[26]^i[27]^i[28]^i[30]; assign next_crc[5] = d[0]^d[1]^d[2]^d[3]^d[4]^d[6]^d[7]^i[24]^i[25]^i[27]^i[28]^i[29]^i[30]^i[31]; assign next_crc[6] = d[0]^d[1]^d[2]^d[3]^d[5]^d[6]^i[25]^i[26]^i[28]^i[29]^i[30]^i[31]; assign next_crc[7] = d[0]^d[2]^d[4]^d[5]^d[7]^i[24]^i[26]^i[27]^i[29]^i[31]; assign next_crc[8] = d[3]^d[4]^d[6]^d[7]^i[24]^i[25]^i[27]^i[28]^i[0]; //每项多出i[i],i=0、1、2...23 assign next_crc[9] = d[2]^d[3]^d[5]^d[6]^i[1]^i[25]^i[26]^i[28]^i[29]; assign next_crc[10] =d[2]^d[4]^d[5]^d[7]^i[2]^i[24]^i[26]^ i[27]^i[29]; assign next_crc[11] =i[3]^d[3]^i[28]^d[4]^i[27]^d[6]^i[25]^d[7]^i[24]; assign next_crc[12] =d[1]^d[2]^d[3]^d[5]^d[6]^d[7]^i[4]^i[24]^i[25]^i[26]^i[28]^i[29]^i[30]; assign next_crc[13] =d[0]^d[1]^d[2]^d[4]^d[5]^d[6]^i[5]^i[25]^i[26]^i[27]^i[29]^i[30]^i[31]; assign next_crc[14] =d[0]^d[1]^d[3]^d[4]^d[5]^i[6]^i[26]^i[27]^i[28]^i[30]^i[31]; assign next_crc[15] =d[0]^d[2]^d[3]^d[4]^i[7]^i[27]^i[28]^i[29]^i[31]; assign next_crc[16] =d[2]^d[3]^d[7]^i[8]^i[24]^i[28]^i[29]; assign next_crc[17] =d[1]^d[2]^d[6]^i[9]^i[25]^i[29]^i[30]; assign next_crc[18] =d[0]^d[1]^d[5]^i[10]^i[26]^i[30]^i[31]; assign next_crc[19] =d[0]^d[4]^i[11]^i[27]^i[31]; assign next_crc[20] =d[3]^i[12]^i[28]; assign next_crc[21] =d[2]^i[13]^i[29]; assign next_crc[22] =d[7]^i[14]^i[24]; assign next_crc[23] =d[1]^d[6]^d[7]^i[15]^i[24]^i[25]^i[30]; assign next_crc[24] =d[0]^d[5]^d[6]^i[16]^i[25]^i[26]^i[31]; assign next_crc[25] =d[4]^d[5]^i[17]^i[26]^i[27]; assign next_crc[26] =d[1]^d[3]^d[4]^d[7]^i[18]^i[28]^i[27]^i[24]^i[30]; assign next_crc[27] =d[0]^d[2]^d[3]^d[6]^i[19]^i[29]^i[28]^i[25]^i[31]; assign next_crc[28] =d[1]^d[2]^d[5]^i[20]^i[30]^i[29]^i[26]; assign next_crc[29] =d[0]^d[1]^d[4]^i[21]^i[31]^i[30]^i[27]; assign next_crc[30] =d[0]^d[3]^i[22]^i[31]^i[28]; assign next_crc[31] =d[2]^i[23]^i[29]; endmodule测试代码 `timescale 1ns/1ns module crc_test(); reg clk, reset; reg [7:0] d; reg load_init; reg calc; reg data_valid; wire [31:0] crc_reg; wire [7:0] crc; initial begin clk=0; reset=0; load_init=0; calc=0; data_valid=0; d=0; end always begin #10 clk=1; #10 clk=0; end always begin crc_reset; crc_cal; end task crc_reset; begin reset=1; repeat(2)@(posedge clk); #5; reset=0; repeat(2)@(posedge clk); end endtask task crc_cal; begin repeat(5) @ (posedge clk); //通过losd_init=1 对CRC计算电路进行初始化 #5; load_init= 1; repeat(1)@ (posedge clk); //设置1oad_init=0,data_valid= 1,calc=1 //开始对输人数据进行CRC校验运算 #5; load_init= 0; data_valid=1; calc=1; d=8'haa; repeat(1)@ (posedge clk); #5; data_valid=1; calc=1; d=8'hbb; repeat(1)@ (posedge clk); #5; data_valid=1; calc=1; d=8'hcc; repeat(1)@ (posedge clk); #5; data_valid=1; calc=1; d=8'hdd; repeat(1)@ (posedge clk); //设置load_init=0,data_valid=1,calc=0 //停止对数据进行CRC校验运算,开始输出 //计算结果 #5; data_valid=1; calc=0; d=8'haa; repeat(1)@ (posedge clk); #5; data_valid=1; calc=0; d=8'hbb; repeat(1)@ (posedge clk); #5; data_valid=1; calc=0; d=8'hee; repeat(1)@ (posedge clk); #5; data_valid=1; calc=0; d=8'hdd; repeat(1)@ (posedge clk); #5; data_valid=0; repeat(10)@ (posedge clk); end endtask crc32_8023 my_crc_test(.clk(clk),.reset(reset),.d(d),.load_init(load_init),.calc(calc),.d_valid(data_valid),.crc_reg(crc_reg),.crc(crc)); endmodule图1-7是电路的仿真结果。图中①是电路进行CRC校验计算之前对电路进行初始化操作的过程,经过初始化之后,crc_reg内部数值为全1。②是对输入数据aa-> bb-> cc-> dd进行运算操作的过程,此时calc和data_valid均为1。③是输出计算结果的过程,CRC校验运算结果a7、01、b4和55先后被输出。 图片 图片 在接收方向上,可以采用相同的电路进行校验检查,判断是否在传输过程中发生了差错。具体工作时,可以边接收用户数据边进行校验运算,当一个完整的MAC帧接收完成后(此时接收数据帧中的校验结果也参加了校验运算),如果当前校验电路的crc_reg值为0xC704DD7B(对于以太网中使用的CRC-32校验,无论原始数据是什么,正确接收时校验和都是此固定数值),说明没有发生错误,否则说明MAC帧有错。 CRC-32校验值的作用是用于检测数据传输或存储中的错误。发送数据时,会根据数据内容生成简短的校验和,并将其与数据一起发送。接收数据时,将再次生成校验和并将其与发送的校验和进行比较。如果两者相等,则没有数据损坏。如果两者不相等,则说明数据在传输或存储过程中发生了改变,可能是由于噪声、干扰、故障或恶意篡改等原因造成的。 CRC-32校验值可以有效地检测出数据中的随机错误,但是不能保证检测出所有的错误。例如,如果数据中有偶数个比特发生了翻转,那么CRC-32校验值可能不会改变,从而无法发现错误。因此,CRC-32校验值只能作为一种辅助的错误检测手段,不能完全依赖它来保证数据的正确性和完整性。 相关工具 如果不理解推导过程的话,可以由相关工具帮忙计算出结果和得到Verilog代码: CRC校验Verilog代码生成链接:http://outputlogic.com/?page_id=321 CRC校验计算工具链接:http://www.ip33.com/crc.html,这个工具只能计算16bit为一个数据包的数据,如果数据包为10bit等之类的就不太适用 在线计算器使用举例 报文 : 1011001 (0x59) 生成多项式 : g(x) = x^4 + x^3 + 1 CRC : 1010 ( 0xa) CRC计算结果截图: image.png图片 参考文献 Verilog HDL算法与电路设计-乔庐峰
FPGA&ASIC
软硬件算法
# ASIC/FPGA
# 硬件算法
刘航宇
3年前
0
2,081
3
3~4月的花花,浅浅的记录一下~
最近在长安区遇到的3~4月的花花,浅浅的记录一下~ 图片 图片 图片 图片 图片 图片 图片 图片 图片 图片 图片 图片 图片 图片 图片
我的随笔
# 随笔
刘航宇
3年前
1
494
4
2023-03-27
电子信息类领域情诗
图片 你是我心中的NE555 你让我充满了电流和电压 你是我灵魂的数字芯片 你让我拥有了逻辑和智慧 你的眼睛如LED般光彩 你的微笑如LCD般温暖 你的声音如DAC般动听 你的气息如FPGA般灵活 我想和你一起创造 电路、程序、系统、应用 我想和你一起分享 数据、信号、信息、感情 你是我的输入和输出 你是我的模拟和数字 你是我的并行和串行 你是我的高频和低频 图片
我的随笔
# 随笔
刘航宇
3年前
0
517
2
使用Verilog实现与仿真AMBA--AHB总线协议(三)
1、AHB从机 AHB从机应答来自系统主主机发起的传输。从机使用从译码器输出的 HSELx 信号来决定它什么时候作应答。其它传输需要的信号,如地址与控制信息由主机产生。 下图为一个AHB从机的接口框图: 图片 下面是一个AHB接口的SRAM控制器的程序: //------------------------------------------------------------------------------ // File : ahb_sram.v // Editor : VSCode, Tab Size(4) // Description : Top Module of AHB SRAM Controller. // //------------------------------------------------------------------------------ `timescale 1ns / 1ps module ahb_sram #( parameter SYNC_RESET = 1, parameter AHB_DWIDTH = 32, parameter AHB_AWIDTH = 32, parameter SIZE_IN_BYTES = 2048, parameter ADD_WIDTH = $clog2(SIZE_IN_BYTES) ) ( //---------------------------------- // IO Declarations //---------------------------------- // Inputs input HCLK, input HRESETN, input HSEL, input HREADYIN, input [1:0] HTRANS, input [2:0] HBURST, input [2:0] HSIZE, input [AHB_DWIDTH-1:0] HWDATA, input [AHB_AWIDTH-1:0] HADDR, input HWRITE, // Outputs output [AHB_DWIDTH-1:0] HRDATA, output [1:0] HRESP, output HREADYOUT ); //---------------------------------- // Variable Declarations //---------------------------------- wire [ADD_WIDTH-1:0] HADDR_cal; wire [2:0] ahbsram_size; wire [ADD_WIDTH-1:0] ahbsram_addr; wire [31:0] ahbsram_wdata; wire ahbsram_write; wire [31:0] sramahb_rdata; wire sramahb_ack; //---------------------------------- // Start of Main Code //---------------------------------- assign HADDR_cal = HADDR[ADD_WIDTH-1:0]; // Instantiations ahb_sram_if #( .AHB_DWIDTH (AHB_DWIDTH), .AHB_AWIDTH (AHB_AWIDTH), .ADD_WIDTH (ADD_WIDTH), .SYNC_RESET (SYNC_RESET) ) u_ahb_sram_if ( .HCLK (HCLK), .HRESETN (HRESETN), .HSEL (HSEL), .HTRANS (HTRANS), .HBURST (HBURST), .HWRITE (HWRITE), .HWDATA (HWDATA), .HSIZE (HSIZE), .HADDR (HADDR_cal), .HREADYIN (HREADYIN), // From SRAM Control signals .sramahb_ack (sramahb_ack), .sramahb_rdata (sramahb_rdata), // Outputs .HREADYOUT (HREADYOUT), .HRESP (HRESP), // To SRAM Control signals .ahbsram_req (ahbsram_req), .ahbsram_write (ahbsram_write), .ahbsram_wdata (ahbsram_wdata), .ahbsram_size (ahbsram_size), .ahbsram_addr (ahbsram_addr), .HRDATA (HRDATA) ); sram_ctrl_if #( .ADD_WIDTH (ADD_WIDTH), .SYNC_RESET (SYNC_RESET) ) u_sram_ctrl_if ( .HCLK (HCLK), .HRESETN (HRESETN), // From AHB Interface signals .ahbsram_req (ahbsram_req), .ahbsram_write (ahbsram_write), .ahbsram_wdata (ahbsram_wdata), .ahbsram_size (ahbsram_size), .ahbsram_addr (ahbsram_addr), // Outputs // To AHB Interface signals .sramahb_ack (sramahb_ack), .sramahb_rdata (sramahb_rdata) ); endmodule //------------------------------------------------------------------------------ // File : ahb_sram_if.v // Editor : VSCode, Tab Size(4) // Description : // //------------------------------------------------------------------------------ `timescale 1ns / 1ps module ahb_sram_if #( parameter AHB_DWIDTH = 32, parameter AHB_AWIDTH = 32, parameter ADD_WIDTH = 11, parameter SYNC_RESET = 0 ) ( //---------------------------------- // IO Declarations //---------------------------------- // Inputs input HCLK, input HRESETN, input HSEL, input HREADYIN, input [1:0] HTRANS, input [2:0] HBURST, input [2:0] HSIZE, input [ADD_WIDTH-1:0] HADDR, input [AHB_DWIDTH-1:0] HWDATA, input HWRITE, input sramahb_ack, input [AHB_DWIDTH-1:0] sramahb_rdata, // Outputs output HREADYOUT, output [1:0] HRESP, output reg [AHB_DWIDTH-1:0] HRDATA, output ahbsram_req, output ahbsram_write, output [AHB_AWIDTH-1:0] ahbsram_wdata, output [2:0] ahbsram_size, output [ADD_WIDTH-1:0] ahbsram_addr ); //---------------------------------- // Local Parameter Declarations //---------------------------------- // State Machine parameters localparam IDLE = 2'b00; localparam AHB_WR = 2'b01; localparam AHB_RD = 2'b10; parameter RESP_OKAY = 2'b00; parameter RESP_ERROR = 2'b01; // AHB HTRANS definition parameter TRN_IDLE = 2'b00; parameter TRN_BUSY = 2'b01; parameter TRN_SEQ = 2'b11; parameter TRN_NONSEQ = 2'b10; parameter SINGLE = 3'b000; parameter INCR = 3'b001; parameter WRAP4 = 3'b010; parameter INCR4 = 3'b011; parameter WRAP8 = 3'b100; parameter INCR8 = 3'b101; parameter WRAP16 = 3'b110; parameter INCR16 = 3'b111; //---------------------------------- // Variable Declarations //---------------------------------- reg [1:0] HTRANS_d; reg [2:0] HBURST_d; reg [2:0] HSIZE_d; reg [ADD_WIDTH-1:0] HADDR_d; reg [AHB_DWIDTH-1:0] HWDATA_d; reg HWRITE_d; reg HSEL_d; reg HREADYIN_d; reg [1:0] ahbcurr_state; reg [1:0] ahbnext_state; reg latchahbcmd; reg ahbsram_req_int; reg ahbsram_req_d1; reg [AHB_DWIDTH-1:0] HWDATA_cal; reg [4:0] burst_count; reg [4:0] burst_count_reg; reg [4:0] count; wire aresetn; wire sresetn; //---------------------------------- // Start of Main Code //---------------------------------- assign aresetn = (SYNC_RESET==1) ? 1'b1 : HRESETN; assign sresetn = (SYNC_RESET==1) ? HRESETN : 1'b1; // Generation of valid AHB Command which triggers the AHB Slave State Machine assign validahbcmd = HREADYIN & HSEL & (HTRANS == TRN_NONSEQ); // Generation of HRESP assign HRESP = RESP_OKAY; always @(*) begin HWDATA_cal = HWDATA; end // Latch all the AHB signals always @(posedge HCLK or negedge aresetn) begin if ((aresetn == 1'b0) || (sresetn == 1'b0)) begin HADDR_d <= {20{1'b0}}; HWDATA_d <= {32{1'b0}}; HTRANS_d <= 2'b00; HSIZE_d <= 2'b00; HBURST_d <= 3'b000; HWRITE_d <= 1'b0; HSEL_d <= 1'b0; HREADYIN_d <= 1'b0; end else if (HREADYIN == 1'b1 & HSEL == 1'b1 & HREADYOUT == 1'b1) begin HADDR_d <= HADDR; HTRANS_d <= HTRANS; HSIZE_d <= HSIZE; HBURST_d <= HBURST; HWRITE_d <= HWRITE; HWDATA_d <= HWDATA_cal; HSEL_d <= HSEL; HREADYIN_d <= HREADYIN; end end // Current State generation always @(posedge HCLK or negedge aresetn) begin if ((aresetn == 1'b0) || (sresetn == 1'b0)) begin ahbcurr_state <= IDLE; end else begin ahbcurr_state <= ahbnext_state; end end // Next State and output decoder logic always @(*) begin latchahbcmd = 1'b0; ahbsram_req_int = 1'b0; ahbnext_state = ahbcurr_state; case (ahbcurr_state) IDLE : begin if (HREADYIN == 1'b1 && HSEL == 1'b1 && ((HTRANS == TRN_NONSEQ) || HTRANS == TRN_SEQ)) begin latchahbcmd = 1'b1; if (HWRITE == 1'b1) begin ahbnext_state = AHB_WR; end else begin ahbnext_state = AHB_RD; end end else begin ahbnext_state = IDLE; end end AHB_WR : begin latchahbcmd = 1'b0; ahbsram_req_int = 1'b1; if (sramahb_ack == 1'b1) begin if (count == burst_count_reg) begin ahbnext_state = IDLE; end else begin ahbsram_req_int = 1'b0; end end end AHB_RD : begin latchahbcmd = 1'b0; ahbsram_req_int = 1'b1; if (sramahb_ack == 1'b1) begin ahbnext_state = IDLE; end end default : begin ahbnext_state = IDLE; end endcase end // LOGIC FOR BURST COUNT always @(*) begin burst_count = burst_count_reg; if (HSEL == 1'b1 && HTRANS == TRN_NONSEQ && HREADYIN == 1'b1 && HREADYOUT == 1'b1) begin case (HBURST) SINGLE : burst_count = 5'b00001; WRAP4,INCR4 : burst_count = 5'b00100; WRAP8,INCR8 : burst_count = 5'b01000; WRAP16,INCR16 : burst_count = 5'b10000; default : burst_count = 4'b0001; endcase end end always @(posedge HCLK or negedge aresetn) begin if ((aresetn == 1'b0) || (sresetn == 1'b0)) begin burst_count_reg <= 'h0; end else begin burst_count_reg <= burst_count; end end always @(posedge HCLK or negedge aresetn) begin if ((aresetn == 1'b0) || (sresetn == 1'b0)) begin count <= 5'h0; end else begin if (count == burst_count_reg) begin count <= 5'h0; end else if (ahbsram_req == 1'b1) begin count <= count + 1'b1; end else begin count <= count; end end end assign HREADYOUT = !ahbsram_req_int; // Generation of signals required for SRAM assign ahbsram_write = ahbsram_req ? HWRITE_d : 1'b0; assign ahbsram_wdata = HWDATA; assign ahbsram_addr = ahbsram_req ? HADDR_d : HADDR_d; assign ahbsram_size = ahbsram_req ? HSIZE_d : HSIZE_d; always @(posedge HCLK or negedge aresetn) begin if ((aresetn == 1'b0) || (sresetn == 1'b0)) begin ahbsram_req_d1 <= 1'b0; end else begin ahbsram_req_d1 <= ahbsram_req_int; end end // Generate the request to the SRAM contol logic when there is AHB read or write request assign ahbsram_req = ahbsram_req_int & !ahbsram_req_d1; // HRDATA generation always @(*) begin if (HREADYOUT && HREADYIN) begin HRDATA = sramahb_rdata; end else begin HRDATA = sramahb_rdata; end end endmodule //------------------------------------------------------------------------------ // File : sram_ctrl_if.v // Editor : VSCode, Tab Size(4) // Description : // //------------------------------------------------------------------------------ `timescale 1ns / 1ps module sram_ctrl_if #( parameter AHB_DWIDTH = 32, parameter ADD_WIDTH = 11, parameter SYNC_RESET = 0 ) ( //---------------------------------- // IO Declarations //---------------------------------- // Inputs input HCLK, input HRESETN, input ahbsram_req, input ahbsram_write, input [2:0] ahbsram_size, input [ADD_WIDTH-1:0] ahbsram_addr, input [AHB_DWIDTH-1:0] ahbsram_wdata, // Outputs output sramahb_ack, output reg [AHB_DWIDTH-1: 0] sramahb_rdata ); //---------------------------------- // Local Parameter Declarations //---------------------------------- // State Machine parameters localparam S_IDLE = 2'b00; localparam S_WR = 2'b01; localparam S_RD = 2'b10; //---------------------------------- // Variable Declarations //---------------------------------- reg [3:0] sram_wen_mem; reg [1:0] sramcurr_state; reg [1:0] sramnext_state; reg sram_wen; reg sram_ren; reg sramahb_ack_int; reg sram_ren_d; reg sram_done; wire [AHB_DWIDTH-1:0] ram_rdata; wire aresetn; wire sresetn; //---------------------------------- // Start of Main Code //---------------------------------- assign aresetn = (SYNC_RESET == 1) ? 1'b1 : HRESETN; assign sresetn = (SYNC_RESET == 1) ? HRESETN : 1'b1; // Current State generation always @(posedge HCLK or negedge aresetn) begin if ((aresetn == 1'b0) || (sresetn == 1'b0)) begin sramcurr_state <= S_IDLE; end else begin sramcurr_state <= sramnext_state; end end // Next State and output decoder logic always @(*) begin sramahb_ack_int = 1'b0; sram_wen = 1'b0; sram_ren = 1'b0; sramnext_state = sramcurr_state; case (sramcurr_state) S_IDLE : begin if (ahbsram_req == 1'b1) begin if (ahbsram_write == 1'b1) begin sramnext_state = S_WR; sram_wen = 1'b1; end else begin sram_ren = 1'b1; sramnext_state = S_RD; end end end S_WR : begin if (sram_done == 1'b1) begin sramnext_state = S_IDLE; sramahb_ack_int = 1'b1; end end S_RD : begin if (sram_done == 1'b1) begin sramnext_state = S_IDLE; sramahb_ack_int = 1'b1; end end default : begin sramnext_state = S_IDLE; end endcase end always @(*) begin sram_wen_mem = 4'b0000; if (ahbsram_size == 3'b010) begin sram_wen_mem = {4{sram_wen}}; end else if (ahbsram_size == 3'b001) begin case (ahbsram_addr[1]) 1'b0 : begin sram_wen_mem[0] = sram_wen; sram_wen_mem[1] = sram_wen; sram_wen_mem[2] = 1'b0; sram_wen_mem[3] = 1'b0; end 1'b1 : begin sram_wen_mem[0] = 1'b0; sram_wen_mem[1] = 1'b0; sram_wen_mem[2] = sram_wen; sram_wen_mem[3] = sram_wen; end endcase end else if (ahbsram_size == 3'b000) begin case (ahbsram_addr[1:0]) 2'b00 : begin sram_wen_mem[0] = sram_wen; sram_wen_mem[1] = 1'b0; sram_wen_mem[2] = 1'b0; sram_wen_mem[3] = 1'b0; end 2'b01 : begin sram_wen_mem[0] = 1'b0; sram_wen_mem[1] = sram_wen; sram_wen_mem[2] = 1'b0; sram_wen_mem[3] = 1'b0; end 2'b10 : begin sram_wen_mem[0] = 1'b0; sram_wen_mem[1] = 1'b0; sram_wen_mem[2] = sram_wen; sram_wen_mem[3] = 1'b0; end 2'b11 : begin sram_wen_mem[0] = 1'b0; sram_wen_mem[1] = 1'b0; sram_wen_mem[2] = 1'b0; sram_wen_mem[3] = sram_wen; end endcase end else begin sram_wen_mem = {4{sram_wen}}; end end // SRAM Instantiations sram_model #( .SYNC_RESET (SYNC_RESET), .ADDR_WIDTH (ADD_WIDTH) ) u_sram_model ( .writedata (ahbsram_wdata), .readdata (ram_rdata[31:0]), .wren (sram_wen_mem), .rden (sram_ren), .writeaddr (ahbsram_addr[ADD_WIDTH-1:2]), .readaddr (ahbsram_addr[ADD_WIDTH-1:2]), .clk (HCLK), .resetn (HRESETN) ); always @(posedge HCLK or negedge aresetn) begin if ((aresetn == 1'b0) || (sresetn == 1'b0)) begin sramahb_rdata <= 32'h0; end else if (sram_ren_d == 1'b1) begin sramahb_rdata <= ram_rdata; end else begin sramahb_rdata <= sramahb_rdata; end end always @(posedge HCLK or negedge aresetn) begin if ((aresetn == 1'b0) || (sresetn == 1'b0)) begin sram_ren_d <= 32'h0; end else begin sram_ren_d <= sram_ren; end end // Generate the SRAM done when the SRAM wren/rden is done always @(posedge HCLK or negedge aresetn) begin if ((aresetn == 1'b0) || (sresetn == 1'b0)) begin sram_done <= 1'b0; end else if (sram_wen || sram_ren) begin sram_done <= 1'b1; end else begin sram_done <= 1'b0; end end // Generate the SRAM ack assign sramahb_ack = sramahb_ack_int; endmodule //------------------------------------------------------------------------------ // File : sram_model.v // Editor : GVIM, Tab Size(4) // Description : FPGA Block Ram/Onchip SRAM. // //------------------------------------------------------------------------------ `timescale 1ns / 1ps module sram_model #( parameter SYNC_RESET = 0, parameter ADDR_WIDTH = 16 ) ( //---------------------------------- // IO Declarations //---------------------------------- input [31:0] writedata, output [31:0] readdata, input [3:0] wren, input rden, input [ADDR_WIDTH-1:2] writeaddr, input [ADDR_WIDTH-1:2] readaddr, input clk, input resetn ); //---------------------------------- //--Local Parameter Declarations //---------------------------------- localparam AWT = ((1<<(ADDR_WIDTH-2))-1); //---------------------------------- // Variable Declarations //---------------------------------- reg [7:0] bram0[AWT:0]; reg [7:0] bram1[AWT:0]; reg [7:0] bram2[AWT:0]; reg [7:0] bram3[AWT:0]; reg [ADDR_WIDTH-3:0] addr_q1; reg rden_r = 1'b0; wire [31:0] readdata_i; wire aresetn; wire sresetn; //---------------------------------- // Start of Main Code //---------------------------------- assign aresetn = (SYNC_RESET == 1) ? 1'b1 : resetn; assign sresetn = (SYNC_RESET == 1) ? resetn : 1'b1; always @(posedge clk) begin rden_r <= rden; end // Infer Block RAM always @(posedge clk) begin if (wren[0]) bram0[writeaddr] <= writedata[7:0]; if (wren[1]) bram1[writeaddr] <= writedata[15:8]; if (wren[2]) bram2[writeaddr] <= writedata[23:16]; if (wren[3]) bram3[writeaddr] <= writedata[31:24]; end always @(posedge clk) begin addr_q1 <= readaddr[ADDR_WIDTH-1:2]; end assign readdata_i = {bram3[addr_q1],bram2[addr_q1],bram1[addr_q1],bram0[addr_q1]}; assign readdata = rden_r ? readdata_i : {32{1'b0}}; endmodule下面是测试用例代码: //------------------------------------------------------------------------------ // File : top_tb.v // Editor : VSCode, Tab Size(4) // Description : Testbench of AHB SRAM Controller. // //------------------------------------------------------------------------------ `timescale 1ns / 1ps module top_tb(); //---------------------------------- // Local Parameter Declarations //---------------------------------- localparam AHB_CLK_PERIOD = 5; // Assuming AHB CLK to be 100MHz localparam SIZE_IN_BYTES =2048; //---------------------------------- // Variable Declarations //---------------------------------- reg HCLK = 0; wire HWRITE; wire [1:0] HTRANS; wire [2:0] HSIZE; wire [2:0] HBURST; wire HREADYIN; wire [31:0] HADDR; wire [31:0] HWDATA; wire HREADYOUT; wire [1:0] HRESP; wire [31:0] HRDATA; reg HRESETn; //---------------------------------- // Start of Main Code //---------------------------------- //----------------------------------------------------------------------- // Generate HCLK //----------------------------------------------------------------------- always #AHB_CLK_PERIOD HCLK <= ~HCLK; //----------------------------------------------------------------------- // Generate HRESETn //----------------------------------------------------------------------- initial begin HRESETn = 1'b0; repeat(5) @(posedge HCLK); HRESETn = 1'b1; end ahb_master #( .START_ADDR (32'h0), .DEPTH_IN_BYTES (SIZE_IN_BYTES) ) u_ahb_master ( .HRESETn (HRESETn), .HCLK (HCLK), .HADDR (HADDR), .HTRANS (HTRANS), .HWRITE (HWRITE), .HSIZE (HSIZE), .HBURST (HBURST), .HWDATA (HWDATA), .HRDATA (HRDATA), .HRESP (HRESP), .HREADY (HREADYOUT) ); ahb_sram # ( .AHB_AWIDTH (32), .AHB_DWIDTH (32), .SIZE_IN_BYTES (SIZE_IN_BYTES), .SYNC_RESET (1) ) u_ahb_sram ( .HCLK (HCLK), .HRESETN (HRESETn), .HSEL (1'b1), .HWRITE (HWRITE), .HADDR (HADDR), .HWDATA (HWDATA), .HRDATA (HRDATA), .HSIZE (HSIZE), .HTRANS (HTRANS), .HBURST (HBURST), .HRESP (HRESP), .HREADYIN (1'b1), .HREADYOUT (HREADYOUT) ); `ifdef VCS initial begin $fsdbDumpfile("top_tb.fsdb"); $fsdbDumpvars; end initial begin `ifdef DUMP_VPD $vcdpluson(); `endif end `endif endmodule这个模块主要是产生了时钟和复位信号,例化了待测模块,以及一个AHB Master的功能模型。具体见下面的代码: //------------------------------------------------------------------------------ // File : ahb_master.v // Editor : VSCode, Tab Size(4) // Description : AHB Master Simulation Model. // //------------------------------------------------------------------------------ `timescale 1ns / 1ps `define SINGLE_TEST `define BURST_TEST module ahb_master #( //---------------------------------- // Paramter Declarations //---------------------------------- parameter START_ADDR = 0, parameter DEPTH_IN_BYTES = 32'h100, parameter END_ADDR = START_ADDR+DEPTH_IN_BYTES-1 ) ( //---------------------------------- // IO Declarations //---------------------------------- input wire HRESETn, input wire HCLK, output reg [31:0] HADDR, output reg [1:0] HTRANS, output reg HWRITE, output reg [2:0] HSIZE, output reg [2:0] HBURST, output reg [31:0] HWDATA, input wire [31:0] HRDATA, input wire [1:0] HRESP, input wire HREADY ); //---------------------------------- // Variable Declarations //---------------------------------- reg [31:0] data_burst[0:1023]; //---------------------------------- // Start of Main Code //---------------------------------- initial begin HADDR = 0; HTRANS = 0; HWRITE = 0; HSIZE = 0; HBURST = 0; HWDATA = 0; while(HRESETn === 1'bx) @(posedge HCLK); while(HRESETn === 1'b1) @(posedge HCLK); while(HRESETn === 1'b0) @(posedge HCLK); `ifdef SINGLE_TEST repeat(3) @(posedge HCLK); memory_test(START_ADDR, END_ADDR, 1); memory_test(START_ADDR, END_ADDR, 2); memory_test(START_ADDR, END_ADDR, 4); `endif `ifdef BURST_TEST repeat(5) @(posedge HCLK); memory_test_burst(START_ADDR, END_ADDR, 1); memory_test_burst(START_ADDR, END_ADDR, 2); memory_test_burst(START_ADDR, END_ADDR, 4); memory_test_burst(START_ADDR, END_ADDR, 6); memory_test_burst(START_ADDR, END_ADDR, 8); memory_test_burst(START_ADDR, END_ADDR, 10); memory_test_burst(START_ADDR, END_ADDR, 16); memory_test_burst(START_ADDR, END_ADDR, 32); memory_test_burst(START_ADDR, END_ADDR, 64); memory_test_burst(START_ADDR, END_ADDR, 128); memory_test_burst(START_ADDR, END_ADDR, 255); repeat(5) @(posedge HCLK); `endif $finish(2); end //----------------------------------------------------------------------- // Single transfer test //----------------------------------------------------------------------- task memory_test; input [31:0] start; // start address input [31:0] finish; // end address input [2:0] size; // data size: 1, 2, 4 integer i; integer error; reg [31:0] data; reg [31:0] gen; reg [31:0] got; reg [31:0] reposit[START_ADDR:END_ADDR]; begin $display("%m: read-after-write test with %d-byte access", size); error = 0; gen = $random(7); for (i = start; i < (finish-size+1); i = i + size) begin gen = $random & ~32'b0; data = align(i, gen, size); ahb_write(i, size, data); ahb_read(i, size, got); got = align(i, got, size); if (got !== data) begin $display("[%10d] %m A:%x D:%x, but %x expected", $time, i, got, data); error = error + 1; end end if (error == 0) $display("[%10d] %m OK: from %x to %x", $time, start, finish); $display("%m read-all-after-write-all with %d-byte access", size); error = 0; gen = $random(1); for (i = start; i < (finish-size+1); i = i + size) begin gen = {$random} & ~32'b0; data = align(i, gen, size); reposit[i] = data; ahb_write(i, size, data); end for (i = start; i < (finish-size+1); i = i + size) begin data = reposit[i]; ahb_read(i, size, got); got = align(i, got, size); if (got !== data) begin $display("[%10d] %m A:%x D:%x, but %x expected", $time, i, got, data); error = error + 1; end end if (error == 0) $display("[%10d] %m OK: from %x to %x", $time, start, finish); end endtask //----------------------------------------------------------------------- // Burst transfer test //----------------------------------------------------------------------- task memory_test_burst; input [31:0] start; // start address input [31:0] finish; // end address input [7:0] leng; // burst length integer i; integer j; integer k; integer r; integer error; reg [31:0] data; reg [31:0] gen; reg [31:0] got; reg [31:0] reposit[0:1023]; integer seed; begin $display("%m: read-all-after-write-all burst test with %d-beat access", leng); error = 0; seed = 111; gen = $random(seed); k = 0; if (finish > (start+leng*4)) begin for (i = start; i < (finish-(leng*4)+1); i = i + leng*4) begin for (j = 0; j < leng; j = j + 1) begin data_burst[j] = $random; reposit[j+k*leng] = data_burst[j]; end @(posedge HCLK); ahb_write_burst(i, leng); k = k + 1; end gen = $random(seed); k = 0; for (i = start; i < (finish-(leng*4)+1); i = i + leng*4) begin @(posedge HCLK); ahb_read_burst(i, leng); for (j = 0; j < leng; j = j + 1) begin if (data_burst[j] != reposit[j+k*leng]) begin error = error+1; $display("%m A=%hh D=%hh, but %hh expected",i+j*leng, data_burst[j], reposit[j+k*leng]); end end k = k + 1; r = $random & 8'h0F; repeat(r) @(posedge HCLK); end if (error == 0) $display("%m %d-length burst read-after-write OK: from %hh to %hh",leng, start, finish); end else begin $display("%m %d-length burst read-after-write from %hh to %hh ???",leng, start, finish); end end endtask //----------------------------------------------------------------------- // As AMBA AHB bus uses non-justified data bus scheme, data should be // aligned according to the address. //----------------------------------------------------------------------- function [31:0] align; input [ 1:0] addr; input [31:0] data; input [ 2:0] size; // num of bytes begin `ifdef BIG_ENDIAN case (size) 1 : case (addr[1:0]) 0 : align = data & 32'hFF00_0000; 1 : align = data & 32'h00FF_0000; 2 : align = data & 32'h0000_FF00; 3 : align = data & 32'h0000_00FF; endcase 2 : case (addr[1]) 0 : align = data & 32'hFFFF_0000; 1 : align = data & 32'h0000_FFFF; endcase 4 : align = data&32'hFFFF_FFFF; default : $display($time,,"%m ERROR %d-byte not supported for size", size); endcase `else case (size) 1 : case (addr[1:0]) 0 : align = data & 32'h0000_00FF; 1 : align = data & 32'h0000_FF00; 2 : align = data & 32'h00FF_0000; 3 : align = data & 32'hFF00_0000; endcase 2 : case (addr[1]) 0 : align = data & 32'h0000_FFFF; 1 : align = data & 32'hFFFF_0000; endcase 4 : align = data&32'hFFFF_FFFF; default : $display($time,,"%m ERROR %d-byte not supported for size", size); endcase `endif end endfunction `include "ahb_transaction_tasks.v" endmodule这个模块主要是实现了两个testcase,一个是测试single传输,另一个测试burst传输。AHB读写传输任务被封装在ahb_transaction_tasks.v文件中: //------------------------------------------------------------------------------ // File : ahb_transaction_tasks.v // Editor : VSCode, Tab Size(4) // Description : AHB Transaction Tasks. // //------------------------------------------------------------------------------ `ifndef __AHB_TRANSACTION_TASKS_V__ `define __AHB_TRANSACTION_TASKS_V__ //----------------------------------------------------------------------- // AHB Read Task //----------------------------------------------------------------------- task ahb_read; input [31:0] address; input [2:0] size; output [31:0] data; begin @(posedge HCLK); HADDR <= #1 address; HTRANS <= #1 2'b10; // NONSEQ; HBURST <= #1 3'b000; // SINGLE; HWRITE <= #1 1'b0; // READ; case (size) 1 : HSIZE <= #1 3'b000; // BYTE; 2 : HSIZE <= #1 3'b001; // HWORD; 4 : HSIZE <= #1 3'b010; // WORD; default : $display($time,, "ERROR: unsupported transfer size: %d-byte", size); endcase @(posedge HCLK); while (HREADY !== 1'b1) @(posedge HCLK); HTRANS <= #1 2'b0; // IDLE @(posedge HCLK); while (HREADY === 0) @(posedge HCLK); data = HRDATA; // must be blocking if (HRESP != 2'b00) $display($time,, "ERROR: non OK response for read"); @(posedge HCLK); end endtask //----------------------------------------------------------------------- // AHB Write Task //----------------------------------------------------------------------- task ahb_write; input [31:0] address; input [2:0] size; input [31:0] data; begin @(posedge HCLK); HADDR <= #1 address; HTRANS <= #1 2'b10; // NONSEQ HBURST <= #1 3'b000; // SINGLE HWRITE <= #1 1'b1; // WRITE case (size) 1 : HSIZE <= #1 3'b000; // BYTE 2 : HSIZE <= #1 3'b001; // HWORD 4 : HSIZE <= #1 3'b010; // WORD default : $display($time,, "ERROR: unsupported transfer size: %d-byte", size); endcase @(posedge HCLK); while (HREADY !== 1) @(posedge HCLK); HWDATA <= #1 data; HTRANS <= #1 2'b0; // IDLE @(posedge HCLK); while (HREADY === 0) @(posedge HCLK); if (HRESP != 2'b00) $display($time,, "ERROR: non OK response write"); @(posedge HCLK); end endtask //----------------------------------------------------------------------- // AHB Read Burst Task //----------------------------------------------------------------------- task ahb_read_burst; input [31:0] addr; input [31:0] leng; integer i; integer ln; integer k; begin k = 0; @(posedge HCLK); HADDR <= #1 addr; addr = addr + 4; HTRANS <= #1 2'b10; // NONSEQ if (leng >= 16) begin HBURST <= #1 3'b111; // INCR16 ln = 16; end else if (leng >= 8) begin HBURST <= #1 3'b101; // INCR8 ln = 8; end else if (leng >= 4) begin HBURST <= #1 3'b011; // INCR4 ln = 4; end else begin HBURST <= #1 3'b001; // INCR ln = leng; end HWRITE <= #1 1'b0; // READ HSIZE <= #1 3'b010; // WORD @(posedge HCLK); while (HREADY == 1'b0) @(posedge HCLK); while (leng > 0) begin for (i = 0; i < ln-1; i = i + 1) begin HADDR <= #1 addr; addr = addr + 4; HTRANS <= #1 2'b11; // SEQ; @(posedge HCLK); while (HREADY == 1'b0) @(posedge HCLK); data_burst[k%1024] <= HRDATA; k = k + 1; end leng = leng - ln; if (leng == 0) begin HADDR <= #1 0; HTRANS <= #1 0; HBURST <= #1 0; HWRITE <= #1 0; HSIZE <= #1 0; end else begin HADDR <= #1 addr; addr = addr + 4; HTRANS <= #1 2'b10; // NONSEQ if (leng >= 16) begin HBURST <= #1 3'b111; // INCR16 ln = 16; end else if (leng >= 8) begin HBURST <= #1 3'b101; // INCR8 ln = 8; end else if (leng >= 4) begin HBURST <= #1 3'b011; // INCR4 ln = 4; end else begin HBURST <= #1 3'b001; // INCR1 ln = leng; end @(posedge HCLK); while (HREADY == 0) @(posedge HCLK); data_burst[k%1024] = HRDATA; // must be blocking k = k + 1; end end @(posedge HCLK); while (HREADY == 0) @(posedge HCLK); data_burst[k%1024] = HRDATA; // must be blocking end endtask //----------------------------------------------------------------------- // AHB Write Burst Task // It takes suitable burst first and then incremental. //----------------------------------------------------------------------- task ahb_write_burst; input [31:0] addr; input [31:0] leng; integer i; integer j; integer ln; begin j = 0; ln = 0; @(posedge HCLK); while (leng > 0) begin HADDR <= #1 addr; addr = addr + 4; HTRANS <= #1 2'b10; // NONSEQ if (leng >= 16) begin HBURST <= #1 3'b111; // INCR16 ln = 16; end else if (leng >= 8) begin HBURST <= #1 3'b101; // INCR8 ln = 8; end else if (leng >= 4) begin HBURST <= #1 3'b011; // INCR4 ln = 4; end else begin HBURST <= #1 3'b001; // INCR ln = leng; end HWRITE <= #1 1'b1; // WRITE HSIZE <= #1 3'b010; // WORD for (i = 0; i < ln-1; i = i + 1) begin @(posedge HCLK); while (HREADY == 1'b0) @(posedge HCLK); HWDATA <= #1 data_burst[(j+i)%1024]; HADDR <= #1 addr; addr = addr + 4; HTRANS <= #1 2'b11; // SEQ; while (HREADY == 1'b0) @(posedge HCLK); end @(posedge HCLK); while (HREADY == 0) @(posedge HCLK); HWDATA <= #1 data_burst[(j+i)%1024]; if (ln == leng) begin HADDR <= #1 0; HTRANS <= #1 0; HBURST <= #1 0; HWRITE <= #1 0; HSIZE <= #1 0; end leng = leng - ln; j = j + ln; end @(posedge HCLK); while (HREADY == 0) @(posedge HCLK); if (HRESP != 2'b00) begin // OKAY $display($time,, "ERROR: non OK response write"); end `ifdef DEBUG $display($time,, "INFO: write(%x, %d, %x)", addr, size, data); `endif HWDATA <= #1 0; @(posedge HCLK); end endtask `endif使用VCS仿真,打印信息如下: 图片 图片 整体仿真波形如下: 图片 不喜欢用DVE的话,也可以选择用Verdi查看波形,这种比较简单的调试,DVE也足够用了。下面贴一张突发传输的细节图: 图片 图片
IP&SOC设计
# SOC设计
刘航宇
3年前
0
601
1
HFSS软件笔记
目录 一、HFSS中的边界条件(Boundaries) 二、HFSS中的激励方式(Excitation) 三、求解类型和求解设置 四、HFSS中的变量和Optimetrics模块的使用 五、HFSS的数据后处理 六、天线问题的数据后处理 # HFSS软件学习笔记 一、HFSS中的边界条件(Boundaries) 边界条件定义了求解区域的边界以及不同物体交界处的电磁场特性,是求解麦克斯韦方程的基础。 只有在假定场矢量是单值、有界、并且沿空间连续分布的前提下,微分形式的麦克斯韦方程组才是有效的;而在求解区域的边界、不同介质的交界处和场源处,场矢量是不连续的,那么场的导数也就失去了意义。边界条件就是定义跨越不连续边界处的电磁场的特性,因此,正确地理解、定义并设置边界条件,是正确使用HFSS仿真分析电磁场场特性的前提。 边界条件的类型: 理想导体边界(Perfect E) 电场矢量垂直于物体表面,有两种边界被自动设为理想导体边界条件: 1、任何与背景相关联的物体表面将被自动定义为理想导体边界,并命名为outer边界 2、材料设为PEC(理想电导体)的物体表面被自动定义为理想导体边界,并命名为smental 理想磁边界/自然边界(Perfect H) 电场矢量与物体表面相切,磁场矢量与物体表面垂直。 自然边界(Natural):当理想导体边界和理想磁边界出现交叠时,理想磁边界也称为自然边界 注意:在理想导体边界上叠加理想磁边界将去掉理想导体边界的特性,相当于在理想导体表面开个口,允许电场穿过。 有限导体边界(Finite Conductivity) 有耗导体/非理性导体边界条件,电场垂直于物体表面,在电磁波的传播方向上电场会愈来愈小 用户需要设置的参数:导电率和导磁率 注意:当物体的材料设置为非理想导体(如铜、铝等金属材料)时,其表面自动定义为有限导体边界条件。 辐射边界(Radiation) 用于模拟开放的自由空间,模拟波辐射到空间的无限远处的情况,常用于天线问题的分析。当结构中包含辐射边界条件时,HFSS会自动计算结构的远区场。(使用Perfect H边界条件模拟开放空间时,不会计算远区场) 辐射边界条件是自由空间的近似,这种近似的准确程度取决于波的传播方向与辐射边界之间的角度,以及辐射源与边界之间的距离。辐射边界在各个方向上距离辐射体一般不小于1/4个波长。 对称边界(Symmetry) 模拟理想电壁或理想磁壁对称面,应用对称边界可以构造结构时仅构造一部分,减小结构的尺寸和设计的复杂性,缩短计算时间。 定义对称平面时,需要遵循以下原则: 1、对称平面必须暴露在背景中 2、对称面必须定义在平面表面上,不能定义在曲面上 3、在一个问题上最多只能定义三个正交对称面 决定对称面的类型: 1、如果电场垂直于对称面且对称,使用理想电壁对称面 2、如果磁场垂直于对称面且对称,使用理想磁壁对称面 此外使用对称边界条件需要设置阻抗乘法器: 1、理想电壁对称面将结构分为两部分时,只有一半的电压值和一半的能量被计算,由Zpu=U*U/P计算出的阻抗也只有真实值的一半,所以需要定义2倍的阻抗乘法器。 2、理想磁壁对称面将结构分为两部分时,只有一半的能量被计算,而电压保持不变,由Zpu=U*U/P计算出的阻抗是真实值的2倍,所以需要定义0.5倍的阻抗乘法器。图片 图片 在这里插入图片描述 阻抗边界(Impedance) 用于模拟已知阻抗的边界表面,如薄膜电阻表面;表面的阻抗Zs=Rs+jXs。 阻抗的计算: number of "Square"=Length(in direction of current flow)/Width Impedance per Square=Desired Lumped Impedance/number of square 集总RLC边界(Lumped RLC) 类似于阻抗边界条件,利用用户提供的R、L、C值计算出对应的阻抗值 与阻抗边界不同的是,集总RLC边界不需要提供以Ohms/Square为单位的电阻和电抗,而是要给出R、L和C的真实值;之后HFSS就能确定任意频率下集总RLC边界以Ohms/Square为单位的阻抗。 分层阻抗边界条件(Layered Impedance) 分层阻抗边界条件是用多层结构将物体表面模拟为一个阻抗表面,其效果与阻抗边界条件相同; 与阻抗边界条件不同的是,对于分层阻抗边界条件,HFSS是根据输入的分层结构数据和表面粗糙度来计算表面电阻和表面电抗的。 分层边界条件不支持快速扫频。 无限地平面(Infinite Ground Plane) 在设置理想导体边界、有限导体边界或阻抗边界时有"Infinite Ground Plane"复选框。 将有限大的边界表面模拟成无限大地平面的作用,设置无限大平面边界后,在后处理中会影响近区、远区辐射场的计算。 定义无限大平面时,需要满足以下条件: 1、必须暴露在背景上 2、必须定义在平面上、 3、无限大平面和对称面的总数不超过3个 4、所有无限大地平面和对称面必须相互垂直 主从边界(Master and slave) 简称为关联边界条件LBC,主要用于模拟平面周期性结构表面,例如阵列天线。 包括主边界条件(Master)和从边界条件(Slave),总是成对出现,且主边界表面和从边界表面的形状、大小和方向完全相同,主边界表面和从边界表面上的电场存在一定的相位差,该相位差就是周期性结构相邻单元之间存在的相位差。 定义主从边界表面时,用户需要正确设置U、V坐标系,保证主从边界表面大小和方向完全一致。 理想匹配层(PML) 理想匹配层,是能够完全吸收入射电磁波的假想各项异性材料边界。理想匹配层有两种典型的应用:一是用于外场问题中的自由空间截断,二是用于导波问题中的吸收负载。 对于导波的吸收负载,理想匹配层模拟导波结构均匀地延申到无穷远处。 对于自由空间截断地情况,理想匹配层地作用类似于辐射边界条件,PML表面能够完全吸收入射过来地电磁波。和辐射边界条件相比,理想匹配层因为能够完全吸收入射的电磁波,零反射,因此计算结果更精确;同时理想匹配层表面可以距离辐射体更近(差不过十分之一个波长即可),不需要像辐射边界表面一般需要距离辐射体大约四分之一个波长。二、HFSS中的激励方式(Excitation) HFSS中,激励是一种定义在三维物体表面或者二维物体上的激励源,这种激励源可以是电磁波激励、电压源或者电流源,激励端口是一种允许能量进入或流出几何结构的特殊边界条件类型。 激励类型: 波端口(Wave Port) 默认情况下,所有三维物体和背景之间的接触面都是理想导体边界,没有能量可以进出;波端口设置在背景上,用作模型的激励源并提供一个能量进入/流出的窗口。波端口一般设置在背景平面上,不允许端口平面弯曲。 波端口模式(modes):对于给定横截面的波导或传输线,特定频率下有一系列的解满足相应的边界条件和麦克斯韦方程组,每个解都称之为一种模式,或者说一种波形。通常,模式是根据电场和磁场沿导波系统传输方向上有无分量这一情况来命名的,假设导波系统沿z轴放置,上述分量是指z向的电场分量Ez和磁场分量Hz。 对于Ez=0、Hz=0一类的模,称之为横电磁模,即TEM模; 对于Ez=0、Hz不为0一类模,称之为横电模,即TE模; 对于Ez不为0、Hz=0一类的模,称之为横磁模,即TM模。 端口校准:波端口必须被校准以确保一致的结果;校准的目的有两个,确定场的方向、设置电压的积分路径。 端口平移(Deembed):是指平移端口的位置,查看其对计算结果的影响;选中使用端口平移功能,只影响数据后处理,HFSS不会重新进行仿真计算。HFSS端口平移中正数表示参考平面向模型内部移动,负数则是向外延申。 终端线(Terminal):对于终端驱动的求解类型,终端的S参数反映的是波端口节点电压和电流的线性叠加,通过波端口处的节点电流和电压可以计算出端口的阻抗和S参数矩阵。 集总端口(Lumped Por) 集总端口激励和波端口激励是HFSS中最常用的两种激励方式。 集总端口激励类似于传统的波端口,与波端口不同的是集总端口可以设置在物体模型内部,且用户需要设定端口阻抗;集总端口直接在端口处计算S参数,设定的端口阻抗即为集总端口上S参数的参考阻抗;另外集总端口不计算端口处的传播常数,因此集总端口无法进行端口平移操作。 集总端口激励的尺寸大小要比波端口小 Floquet端口(Floquet Port) 与波端口的求解方式类似,Floquet端口求解的反射和传输系数能够以S参数的形式显示。使用Floquet端口激励并结合周期性边界,能够像传统波导端口激励一样轻松的分析周期型结构的电磁特性,从而避免了场求解器复杂的后处理过程。 入射波(Incident Wave) 是用户设置的朝某一特定方向传播的电磁波,其等相位面与传播方向垂直;入射波照射到器件表面和器件表面的夹角称为入射角。入射波激励常用于雷达反射截面(RCS)问题的计算。 需要设置的参数有:波的传播方向(Poynting Vector)、电场的强度和方向。 电压源激励(Voltage) 电压源激励定义在两层导体之间的平面上,用理想电压源来表示该平面上的电场激励。 电压源激励时需要设置的参数有:电压的幅度、相位和电场的方向。 注意:电压源激励所在的平面必须远小于工作波长,且平面上的电场是恒定电场;电压源激励是理想的源,没有内阻,因此后处理时不会输出S参数。 电流源激励(Current) 电流源激励定义于导体表面或者导体表面的缝隙上,用理想电流源来表示该平面上激励。 电流源激励需要设定的参数有:导体表面缝隙的电流幅度、相位和方向。 注意:电流源激励所在的平面/缝隙必须小于工作波长,且平面/缝隙上的电流是恒定的;电流源激励是理想的源,没有内阻,因此后处理时不会输出S参数。 磁偏置激励(Magnetic Bias) 创建一个铁氧体材料时,必须通过设置磁偏置激励来定义网格的内部偏置场;该偏置场使得铁氧体中的磁性偶极子规则排列,产生一个非零的磁矩。 如果应用的偏置场时均匀的,张量坐标系可以通过旋转全局坐标系来设置 如果应用的偏置场时非均匀的,不允许旋转全局坐标来设置张量坐标系三、求解类型和求解设置 1、HFSS中有三种求解类型:模式驱动求解(Driven Model)、终端驱动求解(Driven Terminal)和本征模求解(Eigenmode) 模式驱动求解类型:以模式为基础计算S参数,根据导波内各模式场的入射功率和反射功率来计算S参数矩阵的解。 终端驱动求解类型:以终端为基础计算导体传输线端口的S参数;此时,根据传输线终端的电压和电流来计算S参数矩阵的解。 本征模式求解类型:本征模式求解器主要用于谐振问题的设计与分析,可以用于计算谐振结构的谐振频率和谐振频率处对应的场,也可以用于计算谐振腔体的无载Q值。 应用本征模式求解时注意: 不需要设置激励方式 不能定义辐射边界条件 不能进行扫频分析 不能包含铁氧体材料 只有场解结果,没有S参数求解结果2、自适应网格剖分:在分析对象内部搜索误差最大的区域并进行网格的细化,每次网格细化过程中网格增加百分比由用户事先设置,完成一次细化过程后,重新计算并搜索误差最大的区域,然后判断误差是否满足设置的收敛条件。如果满足收敛条件,则完成网格剖分;如果不满足收敛条件,继续下一次网格细化过程,直到满足收敛条件或者达到设置的最大迭代次数为止。 3、求解频率(网格自适应剖分频率)的选择 HFSS计算时自适应网格剖分是在用户设定的单一频点上进行的,网格剖分完成后,同一个求解设置项下其他频点的求解都是基于前面设定频点上所完成的网格划分。自适应频率设置越高,网格剖分就越细,网格个数就越多,计算结果也相应地更加准确,但同时计算过程中所占用地计算机内存也就越高,计算所花费地时间也越长。 下面给出几个常用问题类别的自适应频率的选择: 点频或窄带问题:对于点频或者窄带问题,自适应网格剖分直接选择工作频率。 宽带问题:对于宽带问题,应该选择最高频率作为自适应网格剖分频率。 滤波器问题:对于滤波器问题,由于阻带内电场只存在于断口处,所以自适应频率选择在通带内的高频段。 快速扫频问题:对于快速扫频问题,典型的做法就是选择中心频率作为自适应频率。 高速数字信号:对于高速数字信号完整性分析问题,需要借助转折频率(Knee Frequency)来决定自适应网格剖分频率 4、扫频分析 离散扫频(Discrete):是在频带内的指定频点处计算S参数和场解。例如,指定频带范围为1~2GHz、步长为0.25GHz,则会计算在1GHz、1.25GHz、1.5GHz、1.75GHz、2GHz频点处的S参数和场解。默认情况下,使用离散扫频只保存最后计算频率点的场解。如果希望保存指定的所有频率点的场解,需要选中设置对话框中Save Fields复选框。 快速扫频(Fast):采用ALPS算法,在很宽的频带范围内搜寻处传输函数的全部零、极点。快速扫频适用于谐振问题和高Q值问题的分析,可以得到场在谐振点附近行为的精确描述。使用快速扫频,一般选择频带中心频率作为自适应网格剖分频率进行网格剖分,计算出该频点的S参数和场分布,然后使用基于ALPS算法的求解器从中心频率处的S参数解和场解来外推整个频带范围的S参数和场解。使用快速扫频,计算时只会求解中心频点处的场解,但在数据后处理时整个扫频范围内的任意频点的场都可以显示。 插值扫频(Interpolating):插值扫频使用二分法来计算整个频段内的S参数和场解。使用插值扫频,HFSS自适应选择场解的频率点,并计算相邻两个频点之间的解的误差,找出最大误差,当两点之间的最大误差达到指定的误差收敛标准或者达到了设定的最大频点数目后,扫描完成;其他频率点上的S参数和场解由内插给出。 四、HFSS中的变量和Optimetrics模块的使用 HFSS不仅能够提供常规的电磁分析,还能够提供优化分析、参数扫描分析、灵敏度分析和统计分析等功能。这些功能都集中在HFSS中的Optimetrics模块中。要使用Optimetrics模块的这些分析和设计功能。首先需要定义和添加相关变量。 1、HFSS中变量的定义和使用 (1)HFSS中有两种类型的变量:工程变量(Project Variables)和设计变量/本地变量(Local Variables) 工程变量和设计变量的区别: 工程变量前面有一个"$"前缀,以和本地变量区分 工程变量作用区间是整个Project,本地变量作用区间是所在的Design 物体模型尺寸、物体的材料属性(工程变量)等都可以使用变量来表示。 (2)变量的定义 变量名:可以由数字、字母或下划线组成。每个变量在定义时都必须赋一个初始值,变量值可以是数值、数学表达式或者数学函数,也可以是数组、矩阵或者行列式。 添加/删除变量:工程变量和设计变量操作不同 添加和删除工程变量:Project > Project Variables 或者 [Project Tree] Project > Project Variables 打开 Project Properties 对话框 添加和删除设计变量:HFSS > Design Properties 或者 [Project Tree] Design > Design Properties 打开 Design Properties 对话框 在设计过程中,也可以直接输入未定义的变量代替设计参数,输入未定义的变量后,HFSS会自动弹出添加变量的对话框 2、Optimetrics模块的功能介绍 Optimetrics是集成在HFSS中的优化设计模块,该模块通过自动分析设计参数的变化对求解结果的影响,HFSS中Optimetrics模块能够提供如下分析设计功能: 参数扫描分析(Parametric) 参数扫描分析功能可以用来分析物体的性能随着指定变量的变化而变化的关系,在优化设计之前一般使用参数扫描分析功能来确定被优化变量的合理变化区间 参数扫描分析步骤: 首先需要定义变量并添加求解设置项 HFSS > Optimetrices > Add Parametric...弹出 Setup Sweep Analysis 对话框,添加扫描变量 或选中Project Manager 中的 Optimetrics,单击右键 Add > Parametric,弹出 Setup Sweep Analysis 对话框,添加扫描变量 设置好扫面变量后,点击”Analyze“就可以进行参数扫描分析 查看分析结果 优化设计(Optimization) 优化设计是HFSS软件结合Optimetrics模块根据特定的优化算法在所有可能的设计变化中寻找出一个满足设计要求的值的过程 优化设计的过程: 首先需要明确设计要求或设计目标 然后用户根据设计要求创建初始结构模型(Nominal Design)、定义设计变量并构造目标函数 最后指定优化算法进行优化。 图片 图片 在这里插入图片描述 调谐分析(Tuning) 调谐分析功能是改变变量值的同时实时显示对求解结果的影响程度 HFSS中的调谐分析功能是用户在手动改变变量值得同时能实时显示求解结果 图片 图片 在这里插入图片描述 灵敏度分析(Sensitivity) 灵敏度定义为电磁特性/求解结果的变化与电路参数的变化的比值,使用HFSS进行电磁分析时S参数是很常用的一个分析结果。灵敏度分析功能是用来分析设计参数的微小变化对求解结果的影响程度 统计分析(Statistical) 统计分析功能是利用统计学的观点来研究设计参数容差对求解结果的影响,常用的方法是蒙特卡洛法 图片 图片 在这里插入图片描述 五、HFSS的数据后处理 使用HFSS进行电磁问题的求解分析过程中以及完成求解分析之后,利用数据后处理功能能够直观地给出问题地各种求解信息和求解结果。 1、求解信息数据(Solution Data) HFSS > Results > Solution Data 命令,或者右键单机工程树Results节点,从弹出菜单中选择Solution Data命令,可以打开求解信息对话框,显示各种求解信息。 2、Results数值结果 (1)显示方式 HFSS后处理模块能够以多种方式来显示分析数值结果,这些数值结果地显示方式包括:(右击Results > Create Model Solution Data Report) Rectangular Plot:直角坐标图形显示 Rectangular Stacked Plot Polar Plot:极坐标图像显示 Data Table:数据列表显示 Smith Chart: 史密斯圆图显示 3D Rectangular Plot:三维直角坐标 3D Polar Plot:三维球坐标图形显示 Radiation Pattern:辐射方向图 (2)参数类型 模式驱动求解: Output Variables:用户自定义的输出变量 S Parameter:散射参数 Y Parameter:导纳参数 Z Parameter:阻抗参数 VSWR:电压驻波比 Gamma:传播常数 Port Zo:端口特征阻抗 Active S Parameter Active Y Parameter Active Z Parameter Active VSWR 终端驱动求解: Output Variables:用户自定义的输出变量 S Parameter:散射参数 Y Parameter:导纳参数 Z Parameter:阻抗参数 VSWR:电压驻波比 Power:功率 Voltage Transform matrix:电压传输矩阵 Terminal Port Zo:端口特征阻抗 Active S Parameter Active Y Parameter Active Z Parameter Active VSWR (3)输出变量 右键单击工程树下的Result节点,从弹出菜单中选择Output Variables命令,便可打开输出变量的定义对话框 3、Field Overlays场分布图 在HFSS求解完成之后可以通过右击 Field Overlays 来查看电场、磁场、电流密度、坡印廷矢量等场分布图。 (1)电场E Mag_E:电场幅度瞬时值 ComplexMag_E:电场幅度有效值 Vector_E:电场矢量 (2)磁场H Mag_H:磁场幅度瞬时值 ComplexMag_H:磁场幅度有效值 Vector_H:磁场矢量 (3)电流密度J Mag_Jvol:体电流密度瞬时值 ComplexMag_Jvol:体电流密度有效值 Vector_Jvol:体电流密度矢量 Mag_Jsurf:面电流密度瞬时值 ComplexMag_Jsurf:面电流密度有效值 Vector_Jsurf:面电流密度矢量 (4)其他 Vector_RealPoynting:坡印廷矢量 Local\_SAR和Average\_SAR:局部SAR值和平均SAR值 六、天线问题的数据后处理 1、天线方向图 创建天线的方向图:Results > Create Model Solution Data Report > 3D Polar Plot 天线的辐射场在固定距离上随球坐标系的角坐标 θ 、φ 分布的图形被称为辐射方向图,简称方向图。方向图通常在远区场确定。用辐射场强表示的方向图称为场强方向图,用辐射功率密度表示的方向图称为功率方向图。 2、天线性能参数 右击Radiation,创建好查看天线性能参数:右击天线辐射方向图 > Compute Antenna Parameters Incident Power:输入功率 HFSS中输入功率是指定义的端口激励功率 Acceptable Power:净输入功率 净输入功率是指世纪流入天线端口的输入功率,如果分别使用 Pacc 和 Pinc 表示净输入功率和输入功率,对于只有一个传输模式的单端口天线,有: 图片 Radiated Power:辐射功率 辐射功率是指经由天线辐射到自由空间里的电磁能量,天线的辐射功率可以用坡印廷矢量的曲面积分来计算: 图片 Radiation Efficiency:辐射效率 辐射效率是辐射功率和净输入功率的比值 图片 Max U:最大辐射强度 辐射强度U是指每单位立体角内天线辐射出的功率,Max U是辐射强度的最大值 图片 η 自由空间中的波阻抗为 377Ω,r 为远区场点与天线之间的距离 Peak Directivity:方向性系数 天线的方向性系数是指在相同的辐射功率和相同的距离的情况下,天线在最大辐射方向上的辐射功率密度与无方向性天线在该方向上的辐射功率密度的比值图片 Peak Gain:天线增益 天线增益是指在相同的净输入功率和相同距离的情况下,天线在最大辐射方向上的辐射功率密度与无方向性天线在该方向上的辐射功率密度的比值图片 Peak Realized Gain:最大实际增益 天线的最大实际增益是指在相同的输入功率和相同距离的情况下,天线在最大辐射方向上的辐射功率密度与无方向性天线在该方向上的辐射功率密度的比值 Front to back Ration:前后向比 又称为轴比(Axis Ratio),指方向图中前后瓣的最大比值,代表天线的极化程度 3、天线阵的处理 由相同的天线单元构成的天线阵的方向图等于单个天线单元的方向图与阵因子的乘积。其中,阵因子取决于天线单元之间的振幅、相位差和相对位置,与天线的类型、尺寸无关。在HFSS中,可以定义天线阵元排列结构和激励方式,然后通过仿真分析分析单个天线单元的方向图等天线参数和阵因子来仿真分析整个天线阵列的方向图等天线参数。HFSS支持两种天线阵列类型:规则排列的均匀天线阵列(Regular Uniform Array)和用户自定义排列(Custom Array)。 其中用户自定义阵列:允许用户使用文本文件自定义阵因子信息,然后导入到HFSS软件中,HFSS计算得到阵因子。用户自定义阵列允许更大的灵活性,可以构造天线阵元在空间任意分布的天线阵列。
通信&信息处理
# 天线设计
刘航宇
3年前
0
5,129
2
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,180
2
VLSI设计基础10-时序逻辑电路设计(二)
参考书:数字集成电路-电路、系统与设计,本文栏目对其重点进行精简化 目录 1. 时序基础概念 2. 时序分析的分类4. 时序约束参数(重点) 5. 各种信号路径、时序路径 6. 时钟参数两大条件(重点) 2. 时间偏差与抖动(重点)1. 时钟偏差(Clock Skew) 2. 时钟抖动(Clock Jitter) 1. 时序基础概念 时序分析的目的 对数字系统进行时序检查,判断电路是否可以正常工作(常面临建立时间和保持时间等问题),判断电路的性能等。 常常分析电压、温度、工艺(工艺角)等参数进行分析。 2. 时序分析的分类 静态时序分析(STA) 主要研究对象:建立时间、保持时间、传播延时 常用于分析同步时序电路(源时钟和目的时钟相同) 时序分析模型: 同步时钟/异步时钟 D触发器分割组合逻辑 图片 一般不需要进行太复杂的仿真,仅需要计算就可以进行分析,运行速度快。 不依赖于激励,根据穷尽信号路径上的器件就可以进行计算 常用方法是使用查找表——①输入跳变时间②输出负载(电容)→①传播延时②输出跳变(下一级的输入跳变)。 动态时序分析(DTA) 指门级仿真 主要用于异步逻辑、多周期路径 在FPGA中,将RTL代码综合利用综合工具综合成门级网络进行仿真,其中各种门级器件的逻辑是厂家提供的。 【时钟】沿 发送沿:发送数据的源时钟活动沿 捕获沿:接收数据的目的时钟的活动沿 源时钟:用于发送数据的时钟 目的时钟:用于接受数据的时钟 小贴士:在同步电路中,源时钟和目的时钟是同一个 4. 时序约束参数(重点) 即:建立时间tsu、保持时间thold、传播延时tc-q,同时我们引入污染时间tcd 建立时间: 对于捕获沿到来之前,数据需要保持稳定的时间 间接约束了组合逻辑的最大延时 保持时间: 对于捕获沿到来之后,数据需要保持稳定的时间 间接约束了组合逻辑的最小延时 传播时间(延时): 即 最大延时 时间 捕获沿50%(数据输入沿50%【注意:数据输入沿其实就是捕获沿!!!】)到数据稳定输出(输出数据50%)的时间 根据器件不同,可以分为组合逻辑传播延时tlogic和寄存器传播延时tc-q,详细见后文。 污染时间: 可以理解为 最短延时 时间——理想状态下 从输入“扰动”到输出“扰动”的时间,下文进行解释。 根据器件不同,可以分为组合逻辑污染延时tlogic,cd和寄存器污染延时tc-q,cd,详细见后文 所谓理想状态,指的是数据没有跳变时间,即数据跳变是瞬间完成的,数据跳变的90%、50%、10%是在同一个时间。 根据以上理想状态的定义,可认为一有扰动,数据就跳变完成。 计算污染时间和传播时间 图片 现对图中四个时间进行解释: 图片 图片 5. 各种信号路径、时序路径 信号的路径主要分为三个 图片 时钟路径 源时钟路径&目的时钟路径 图片 数据路径 图片 数据起点: 对于时序逻辑电路,为某时序单元的时钟引脚 对于组合逻辑电路,为某逻辑单元的数据输入端口 数据终点: 对于组合逻辑电路、时序逻辑电路都一样,均为某单元的数据输出端口 异步路径(如异步复位) 根据路径可将分析类型分为 同步分析:时钟路径+数据路径 异步分析:时钟路径+异步路径 6. 时钟参数两大条件(重点) 周期条件 图片 保持时间条件 图片 图片 2. 时间偏差与抖动(重点) 理想时钟: 从时钟沿到各个单元的时钟端口的延时相等(即路径均匀); 同一个时刻,各个单元的时钟端的时钟相位相等。 实际时钟: 时钟偏差:各个时钟端口的时钟的周期没有改变,但是相位可能略有差别。 时钟抖动:时钟的周期存在一些差别,或长或短。 1. 时钟偏差(Clock Skew) 定义与成因 指同一个时钟域之间,时钟信号到达各个寄存器的最大时间差 产生原因: 时钟源到达各个端点的路径长度不同 各个端口的负载不同 时钟网络中插入的缓存器不等 计算【全局偏差、局部偏差】 图片 全局时钟偏差 图片 局部时钟偏差 图片 时钟偏差分类(正负) 正偏差 正偏差,即时钟延迟方向与数据流方向一致,如图所示。 图片 负偏差 正偏差,即时钟延迟方向与数据流方向相反,如图所示。 图片 利用时间偏差修补建立时间 图片 【周期T】时钟偏差对于周期的影响 前文提到,负偏差使得实际逻辑计算的时间减小,为了填补裕量,只能增加时间周期,而提高时间周期会使得电路的性能下降。 图片 2. 时钟抖动(Clock Jitter) 定义与计算 定义:芯片某一给定点上,时钟周期宽度发生变化,或缩短或变宽 计算: 图片 图片 【周期T】时钟抖动对于周期的影响 因为时钟抖动是难以预料的,在确定时钟周期的时候,我们应该考虑最坏的情况,即$T-2 t_{\text {jitter }}>t_{c-q}+t_{\text {logic }}+t_{s u}$ 即上图所示的③-④。因为这意味着周期T需要增加tjitter,性能降低 总结 图片 图片
VLSI&IC验证
# VLSI
刘航宇
3年前
0
1,422
1
2023-02-24
VLSI设计基础9-时序逻辑电路设计(一)
参考书:数字集成电路-电路、系统与设计,本文栏目对其重点进行精简化 目录 1. 时序电路的相关概念1. 时序电路 2. 静态/动态存储器 3. 时序参数(重点) 4. 时序约束条件(重点) 5. 锁存器和寄存器 静态锁存器/寄存器1. 静态锁存器1. 电路图与分析 2. 时序参数分析 2. 静态寄存器——边沿触发寄存器 3. 动态锁存器/寄存器 4. 流水线和多周期路径(重点) 1. 时序电路的相关概念 1. 时序电路 框架: 时序电路=组合电路+存储电路 结果:取决于当前的输入和过去的状态。 2. 静态/动态存储器 静态存储器: 上电就保持存储状态 通过正反馈,有意将输入和输出连接 如果长时间不用可以用门控时钟关闭(起到降低功耗的作用) 图片 动态存储器: 简单而言,即传输门+反相器,如下图。 利用寄生电容存储高低电平 只能存储较短的时间(ms级别)(电容小等原因使得存储时间短) 需要周期性刷新来补偿泄露电荷(参见(9)动态门) 结构简单,有较高性能(管子少,RC小)和较低功耗(不存在静态功耗) 图片 3. 时序参数(重点) 图片 为何这么说? 因为只有当满足了建立时间,此时时钟CLK才可以翻转,即此时的数据才是输入端输入的数据 所以输入端数据的50%即为时钟的50% 图片 图片 小贴士: 建立时间tsu和保持时间thold都是针对时钟的有效沿而言的。 即,输入数据50%的时间对时钟有效沿50%的时间;对于传播延时 ,输入信号50%(即时钟信号)时间对输出信号50%时间 4. 时序约束条件(重点) 图片 1.时钟周期T 图片 2.保持时间thold 图片 5. 锁存器和寄存器 图片 静态锁存器/寄存器 1. 静态锁存器 1. 电路图与分析 以正锁存器为例,电路图如下, 图片 电路结构:以传输门为主体的二选一选择器;CLK=1,Q=D;而CLK=0,通过正反馈,输出端和输入端连接。 2. 时序参数分析 1.5中提到,分析正锁存器时,需要注意的时钟边沿是时钟的下降沿。 这是因为,在CLK=1时,Q=D,属于透明传输,即输出随输入随时变化。其信号传播路径:D-①-②-③-Q 当CLK=1->0时,锁存器将要对输入数据进行所存。因此需要考虑建立时间tsu。 如果要让数据可以正确锁存下来,需要④⑤之间的节点的电平等于③⑤之间节点的电平,即 $$ D_{(4)(5)}=D_{(3)(5)} $$2. 静态寄存器——边沿触发寄存器 边沿触发寄存器,又称主-从寄存器,由主、从两个锁存器组成 电路图与分析 图片 图片 图片 主-从寄存器,由主锁存器——负锁存器、从锁存器——正锁存器组成。 图片 时序参数分析 图片 图片 图片 寄存器复位——同步/异步 同步复位(RST=0时复位) 图片 异步复位(clr=0时复位) 图片 3. 动态锁存器/寄存器 动态锁存器 电路图: 图片 使用电容电荷表示一个逻辑信号。 但是值的保存时间有限(ms)(漏电时间),需要周期性进行刷新。 动态寄存器 电路图 图片 工作原理: 图片 时间参数分析: 图片 存在的问题 图片 图片 优化——提高噪声容限 图片 3.C2MOS寄存器 图片 图片 4. 流水线和多周期路径(重点) 流水线 电路的工作速度取决于时序电路间的组合逻辑 可以通过将复杂、延迟大的组合逻辑(如计算lg(AB))通过寄存器进行分割,提高做工效率。 需要注意时序设计。每增加一个寄存器,结果就落后一拍! 图片 多周期路径 主要是写RTL代码时需要注意。 如果一个组合逻辑计算需要多个时间周期完成,可以通过一个cnt进行移位(移位运算量小)。当满足某个条件时,寄存器en=1,输出结果。 如:某运算需要经过四个周期完成,可以声明cnt=4'b0001,每个时钟进行左移,当cnt[3]==1'b1时,en=1,输出结果。
VLSI&IC验证
# VLSI
刘航宇
3年前
0
816
2
上一页
1
2
3
...
7
下一页