VHDL学习经验Word文件下载.docx
《VHDL学习经验Word文件下载.docx》由会员分享,可在线阅读,更多相关《VHDL学习经验Word文件下载.docx(42页珍藏版)》请在冰豆网上搜索。
而当DataB作为有效输入时,DataB输出必须处于高阻态,对于该例子中即,当CE=1andRd=1时,输出DataB应处于高阻态。
二信号和变量常数、信号和变量是VHDL中最主要的对象,分别代表一定的物理意义。
常数对应于数字电路中的电源或地;
信号对应某条硬件连线;
变量通常指临时数据的局部存储。
信号和变量功能相近,用法上却有很大不同。
表1信号与变量主要区别信号变量赋值延迟至少有延时无,立即变化相关信息有,可以形成波形无,只有当前值进程敏感是否全局性具有全局性,可存在于多个进程中只能在某个进程或子程序中有效相互赋值关系信号不能给变量赋值变量可以给信号赋值对于变量赋值操作无延迟,初学者认为这个特性对VHDL设计非常有利,但这只是理论上的。
基于以下几点原因,我们建议,编程时还是应以信号为主,尽量减少变量的使用。
(1)变量赋值无延时是针对进程运行而言的,只是一个理想值,对于变量的操作往往被综合成为组合逻辑的形式,而硬件上的组合逻辑必然存在输入到输出延时。
当进程内关于变量的操作越多,其组合逻辑就会变得越大越复杂。
假设在一个进程内,有关于变量的3个级连操作,其输出延时分别为5ns,6ns,7ns,则其最快的时钟只能达到18ns。
相反,采用信号编程,在时钟控制下,往往综合成触发器的形式,特别是对于FPGA芯片而言,具有丰富的触发器结构,易形成流水作业,其时钟频率只受控于延时最大的那一级,而不会与变量一样层层累积。
假设某个设计为3级流水作业,其每一级延时分别为10ns,11ns,12ns,则其最快时钟可达12ns。
因此,采用信号反而更能提高设计的速度。
(2)由于变量不具备信息的相关性,只有当前值,因此也无法在仿真时观察其波形和状态改变情况,无法对设计的运行情况有效验证,而测试验证工作量往往会占到整个设计7080的工作量,采用信号则不会存在这类问题。
(3)变量有效范围只能局限在单个进程或子程序中,要想将其值带出与其余进程、子模块之间相互作用,必须借助信号,这在一定程度上会造成代码不够简洁,可读性下降等缺点。
当然,变量也具有其特殊的优点,特别是用来描述一些复杂的算法,如图像处理,多维数组变换等。
三位(矢量)与逻辑(矢量)bit或其矢量形式bit_vector只有0和1两种状态,数字电路中也只有0和1两种逻辑,因此会给初学者一个误区,认为采用位(矢量)则足够设计之用,而不必像std_logic那样出现X,U,W各种状态,增加编程难度。
但实际情况却并非如此,以一个最简单D型触发器设计为例process(clk)beginifclkeventandclk=1thenQ=D;
endif;
endprocess;
实际中clk对数据端D的输入有一定的时间限制,即在clk上升沿附近(建立时间和保持时间之内),D必须保持稳定,否则Q输出会出现亚稳态,如下图所示。
图1建立时间和保持时间当clk和D时序关系不满足时,由于bit只有0或1,系统只能随机的从0和1中给Q输出,这样的结果显然是不可信的;
而采用std_logic类型,则时序仿真时会输出为一个X,提醒用户建立保持时间存在问题,应重新安排D和clk之间时序关系。
此外,对于双向总线设计(前面已提及)、FPGA/CPLD上电配置等问题,如果没有Z,X等状态,根本无法进行设计和有效验证。
四关于进程进程(Process)是VHDL中最为重要的部分,大部分设计都会用到Process结构,因此掌握Process的使用显得尤为重要。
以下是初学和使用Process经常会出错的例子。
1.多余时钟的引入在设计时往往会遇到这种情况,需要对外部某个输入信号进行判断,当其出现上跳或下跳沿时,执行相应的操作,而该信号不像正常时钟那样具有固定占空比和周期,而是很随机,需要程序设计判断其上跳沿出现与否。
这时,很容易写出如下程序:
process(Ctl_a)-Ctl_a即为该输入信号beginifCtl_aeventandCtl_a=1then;
-执行相应操作endif;
由于出现第行这类语句,综合工具自动默认Ctl_a为时钟,某些FPGA更会强行将该输入约束到时钟引脚上。
而设计者的初衷只是想将其作为下位机的状态输入以进行判断。
上面的程序容易造成多时钟现象,增加设计的难度。
解决的办法可以如下,将Ctl_a增加一级状态Ctl_areg寄存,通过对Ctl_a和Ctl_areg状态判断上跳与否,改正程序如下:
process(clk)beginifclkeventandclk=1thenCtl_areg=Ctl_a;
-产生相邻状态ifCtl_areg=0andCtl_a=1then-上跳判断;
程序中第行用以产生两个相邻状态,第行对前后状态进行判断是否有上跳现象发生。
其中,需注意的是clk的时钟频率应明显快于Ctl_a信号的变化频率,以保证正确采样。
2.输出多驱动误用Process经常会引起输出多驱动源的发生,即在两个以上的进程内对同一信号赋值操作。
以下程序就出现了这类情况:
Proc_a:
process(clk)beginifclkeventandclk=1thenDout=Din_A;
endifendprocess;
Proc_b:
process(sel_en)beginifsel_en=1thenDout=Din_B;
进程Proc_a和Proc_b中都出现了对Dout的赋值语句,设计者原本的想法是,只要合理控制好clk和sel_en输入,使其不发生冲突,即clk上升沿时sel_en不为1;
sel_en为1时,不出现clk的上升沿,这样Proc_a,Proc_b两个进程就不会发生冲突。
但综合时,综合工具会将所有可能情况全部罗列进去,包括第行和第行同时成立的情况,此时对于Dout就有Din_A和Din_B两个输入驱动,Dout不知接收哪一个,因此该程序无法综合,改正的方法是只要将两个进程合并成一个即可。
由于进程在VHDL中的重要性,对此专门做了一个总结如下:
(1)一个进程中不允许出现两个时钟沿触发,(Xilinx公司CoolRunner系列CPLD支持单个时双钟的双触发沿除外)
(2)对同一信号赋值的语句应出现在单个进程内,不要在时钟沿之后加上else语句,如ifclkeventandclk=1then-else的结构,现有综合工具支持不了这种特殊的触发器结构(3)当出现多层IF语句嵌套时,最好采用CASE语句替代,一是减少多层嵌套带来的延时,二来可以增强程序的可读性(4)顺序语句如IF语句、CASE语句、LOOP语句、变量赋值语句等必须出现在进程、函数或子程序内部,而不能单独出现在进程之外(5)进程内部是顺序执行的,进程之间是并行运行的;
VHDL中的所有并行语句都可以理解为特殊的进程,只是不以Process结构出现,其输入信号和判断信号就是隐含的敏感表五关于VHDL学习中的几点说明与软件语言相比,VHDL最重要的特点就在于它的并行运行特性,当设计好的电路上电后,器件内部所有信号将同时并发工作,而不会以软件方式按照程序顺序执行,即使在进程内部也是趋向并行工作的。
例如以下程序:
process(clk)beginifclkeventandclk=1then=;
=;
综合的结果两个独立的D型触发器,虽然进程内部应按顺序执行,但是硬件实现后,只要采样到时钟上升沿,和状态会同时翻转,而不会先执行的变化,然后才会去执行的转变。
因此,VHDL学习过程中,应加强硬件概念的理解,没有硬件概念或是硬件概念不强,在设计时,往往会将VHDL设计以软件编程的方式来处理,而得出一些不可思议的结果。
作为一门硬件描述语言,VHDL几乎可以用来描述现有的大型系统数字电路、算法以及其它设计。
但是,限于目前综合工具的水平,VHDL中的许多语法还不能支持,例如:
dout=dinafter5ns;
综合时就无法达到如此精度,因此这条语句主要用来编写测试激励,而很少出现在设计实体中。
类似的情况还有很多,目前VHDL设计使用的也只是整个标准中的一部分,这也正是VHDL的“可综合子集”性质,它一定程度上限制了VHDL的广泛应用,但是随着综合技术的发展,这种情况会逐渐得以改善,VHDL也将在各个领域中发挥出愈来愈重要的作用。
介绍了在VHDL编程设计中,描述方法对电路结构的影响,不同的状态机描述方法,层次化设计的基本思想和原则,BlockRAM的结构、VHDL程序、宽度和深度组合形式,基于IPCore的BlockRAM设计,数字延迟锁相环(DLL,DelayLockedLoop)、全局时钟网络(GlobalClockNetworks)、DCM(数字时钟管理器,DigitalClockManager)的结构特点与应用。
重点是掌握在VHDL编程设计中,锁存避免、寄存器使用、括号使用、并行结构、资源共享、数值比较等描述方法对电路结构的影响,单进程状态机、多进程状态机的基本结构模型,层次化设计的基本结构与方法,BlockRAM的结构、编程、宽度和深度组合,基于IPCore的BlockRAM设计,使用DLL、全局时钟网络、DCM消除时钟时延、频率合成和时钟相位调整等方法。
VHDL设计是行为级的设计,所带来的问题是设计者的设计思考与实际电路结构是相脱节的。
设计者主要是根据VHDL的语法规则,对系统目标的逻辑行为进行描述,然后通过综合工具进行电路结构的综合、编译、优化,通过仿真工具进行逻辑功能仿真和系统时延的仿真。
实际设计过程中,由于每个设计工程师对语言规则、对电路行为的理解程度不同,每个人的编程风格不同,往往同样的系统功能,描述的方式是不一样的,综合出来的电路结构更是大相径庭。
因此,即使最后综合出的电路都能实现相同的逻辑功能,其电路的复杂程度和时延特性都会有很大的差别,甚至某些臃肿的电路还会产生难以预料的问题。
从这些问题出发,很有必要深入讨论在VHDL设计中如何简化电路结构,优化电路设计的问题。
用VHDL进行设计,最终综合出的电路的复杂程度,除取决于设计要求实现的功能的难度外,还受设计工程师对电路的描述方法和对设计的规划水平的影响。
最常见的使电路复杂化的原因之一是在设计中存在许多本不必要的类似LATCH的结构。
而且由于这些结构通常都由大量的触发器组成,不仅使电路更复杂,工作速度降低,而且由于时序配合的原因会导致不可预料的结果。
以下有2段设计,如果单从语法上来看是没有任何错误的,而且编译时都可以通过,但是如果从电路结构上考虑,它们都存在问题。
Exam1:
Process(A,B)BeginIfA=1thenQQ=1;
ZQ=0;
缺少Z的值Endcase;
Endprocess;
仔细观察,在Exam1和Exam2进程中的语句都有同一个毛病。
Exam1进程中的if语句仅仅指明了A在高电平
(1)的时候将B的值传到Q端,并没有指明A在低电平(0)的时候Q端应该是什么值。
综合工具在综合时,发现这种不完全的状态描述,会将其综合为锁存(latch)。
锁存的结构如图7.1.1所示。
在ISE中,综合工具XST会发出一个警告:
Found1-bitlatchforsignal。
锁存是由与或非逻辑组成的,而这种结构在系统中多半会埋下不稳定的种子。
再看看Exam2进程,有两个输出信号,信号的值在Case语句中被决定,但是Z的值只在C为0时有明确的说明,当C为高1时设计者并没有明确指出。
所以同样会使综合工具理解为锁存。
2描述寄存器代替锁存既然锁存存在不稳定,那么有必要找到一个好的替代,那就是寄存器。
寄存器由时钟触发,很大程度上抑制了毛刺。
所以,尽可能的使用寄存器而避免锁存。
下面是不同的D触发器的VHDL描述:
DFF:
process(clk)-D触发器BeginIfrising_edge(clk)thenQ=A;
Endif;
DFF_AR:
process(clk,reset)-带异步复位的D触发器BeginIfreset=1thenQ=0;
Elsifrising_edge(clk)thenQ=A;
DFF_AS:
process(clk,reset)-带异步置位的D触发器BeginIfreset=1thenQ=1;
DFF_SS:
process(clk,reset)-带同步置位的D触发器Beginifrising_edge(clk)thenifreset=1thenQ=1;
elseQ=A;
从上面的例子可以了解到不同的描述对电路结构的影响,在下面的一个例子中,有两种不同的加法描述,其电路结构也是不同的。
3使用括号描述想要的结构
(1)Out1=I1+I2+I3+I4;
(2)Out2=(I1+I2)+(I3+I4);
从结构上看,第一种描述中没有使用括号,电路结构为三层,加法一级一级的进行。
它的特点是,四个输入I1,I2,I3,I4到达加法器的路径不相等。
第二种描述中的使用了括号,电路结构分为两层,显然它的特点是四个输入信号到达加法器的路径是相等的。
这里并不能简单的评论两种结构的优劣,必须根据实际应用情况分析。
4并行结构在VHDL语言描述中,进程中的语句是顺序执行的。
如果要设计一个并行的电路,不要使用顺序语句,而要使用并行语句。
方法1Process(A,B,C)beginifC=0thenY=A;
ElseY=B;
方法2Y=AwhenC=0elseB;
如果电路结构是并行的,那么使用方法2描述要比方法1好。
5资源共享如果有可能的话,尽量使资源共享。
一块FPGA芯片中的资源是有限的。
如果不有效利用仅有的资源,将会导致资源不够,系统冗余过多,稳定性也变会差。
加法器的资源共享加法器的输出Z是A加上C或者B,A与C、B哪个输入相加由选择信号S决定,由于在设计中采用了多路选择器(Mux),在结构上可以得到两种形式。
一种方法是采用了两个16位的加法器,两个加法器的结果通过MUX由S选择输出。
而只采用了一个加法器,它将C、B信号通过MUX后的输出送到加法器的一端,另一端连接A。
两种结构都能达到设计的要求,显然,后者的设计设计占用的资源要小些。
在使用VHDL描述时,在process中比较容易做到资源共享,源程序如下:
process(A,B,C,S)beginifS=1thenZ=A+B;
ElseZ=A+C;
6integer和std_logic_vector在VHDL中,信号使用限制了位数的std在VHDL中,信号使用限制了位数的std_logic_vector(标准逻辑向量)型,不使用integer(整型)。
如果使用integer,最好在后面加上约束条件,例如:
a:
integerrange0to7;
。
如果使用了integer而没有对其进行位数的约束,那么在32位的PC机中,综合后将是一个32bit的信号,会造成大量的浪费。
_logic_vector(标准逻辑向量)型,不使用integer(整型)。
如果使用了integer而没有对其进行位数的约束,那么在32位的PC机中,综合后将是一个32bit的信号,会造成大量的浪费。
7.1.2不同的状态机描述1.状态机的选型按状态机的输出方式分类,有限状态机可分为Mealy型和Moore型。
从输出时序上看,状态机的输出是当前状态和所有输入信号的函数,它的输出是在输入变化后立即发生的,不依赖时钟的同步。
Moore型状态机输出则仅为当前状态的函数,状态机的输入发生变化还必须与状态机的时钟同步。
由于Mealy状态机的输出不与时钟同步,所以当在状态译码比较复杂的时候,很容易在输出端产生大量的毛刺,这种情况是无法避免的。
在一些特定的系统中,这些毛刺可能造成不可预料的结果。
但是,由于输入变化可能出现在时钟周期内的任何时刻,这就使得Mealy状态机对输入的响应可以比Moore状态机对输入的响应要早一个时钟周期。
Moore状态机的输出与时钟同步,可以在一定程度上剔除抖动。
从稳定性的角度来讲,建议使用Moore状态机以提高系统的稳定性。
在实际工程中,具体电路有具体的设计要求,可能适应于Moore或Mealy状态机,也可能两种都可以实现设计,所以设计者必须根据实际情况和经验综合决定采用哪种状态机。
2.状态编码状态编码,是指定义状态机现态和次态,一般有3种方式:
(1)方式1signalcurren_state:
std_logic_vector(1downto0)signalnext_state:
std_logic_vector(1downto0)方式1定义的状态有比较多的毛病。
首先,这种方式定义的状态为逻辑向量,缺乏具体的状态含义,程序的可读性较差,更重要的是,设计后期调试修改比较麻烦。
2)方式2Typemystateis(st0,st1,st2,st3);
Signalcurren_state,next_state:
mystate;
采用方式2定义的状态有具体的状态含义,可读性好,易于调试和修改。
使用attribute可以对状态进行手动编码,通过设置用户型编码方式,综合工具很容易根据手动状态编码进行高效的综合。
(3)方式3Signalcurren_state,next_state:
std_logic_vector(1downto0);
Constantst0:
std_logic_vector(1downto0):
=”00”;
Constantst1:
sta_logic_vector(1donwot0):
=”01”;
Constantst2:
=”10”;
Constantst3:
=”11”;
方式3比方式1要好,因为方式3的可读性比方式1要好。
但是方式3的修改没有方式2方便。
状态机的编码在ISE中有6种,其中常用的是顺序编码和一位热码。
在ISE中可以通过修改综合约束来指定状态机的编码。
方法如下:
综合前,在Processforcurrentsources窗口中选中Synthesize,单击鼠标右键,弹出下拉菜单,选中Properties(属性)。
在弹出的菜单中有3个选项栏,选中HDLoptions选项栏,如图7.1.6所示。
在Propertiesname(属性名)下的第一项便是FSMEncodingAlgorithm(有限状态机编码算法),在Value中打开下拉菜单,可以看到几种编码方式。
其中Auto(自动)为ISE的缺省值,在一般情况下,如果目标芯片是FPGA,那么XST会将状态机编码为One-Hot(一位热码)。
使用One-Hot(一位热码)的好处是,状态转换快,状态译码简单。
采用Sequential(顺序编码)的好处是,所需要的寄存器少,冗余状态相对要少,系统进入其他状态的机会就更少。
还有一个比较常用的编码方式,User(用户)编码方式。
在前面讲过,设计者是可以手动编码的,只要加上Attribute语句便可,这里给出一个例子:
需要提醒大家注意的是,HDLOptions中的设置的优先级要高于用户的attribute属性。
也就是说,如果使用了attribute语句进行了手动编码,但是在FSMEncodingAlgorithm中选择的是Gray(格雷)码,那么实际综合的效果是Gray(格雷)编码。
例子:
typeSTATE_TYPEis(S1,S2,S3,S4);
attributeENUM_ENCODING:
STRING;
attributeENUM_ENCODINGofSTATE_TYPE:
typeis1110110110110111;
上面的四个状态S1,S2,S3,S4其实也是一位热码,是用0作为热码。
选择User编码,那么综合后的实际状态机编码就是设计者在attribute中描述的状态码字。
3.多进程状态机在状态机的描述中,经典的应该属于3进程状态机,基本结构可以由下面的模型描述:
SYNC_PROC:
process(CLOCK,RESET)-同步进程beginif(RESET=1)then-现态=初始状态;
elsif(CLOCKevent