|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
VHDL学习理解 8 t! W$ v+ O& f- V8 W
1 \3 z c8 S: I) j+ A, x1 n
一. 关于端口1 l& q! h, ?% w0 p& e" d: v
VHDL共定义了5种类型的端口,分别是In, Out,Inout, Buffer及Linkage,实际设计时只会用到前四种。In和Out 端口的使用相对简单。这里,我们主要讲述关于buffer和inout使用时的注意事项。& |, H" U+ m8 L. _8 [
与Out 端口比,Buffer端口具有回读功能,也即内部反馈,但在设计时最好不要使用buffer,因为buffer类型的端口不能连接到其他类型的端口上,无法把包含该类型端口的设计作为子模块元件例化,不利于大型设计和程序的可读性。若设计时需要实现某个输出的回读功能,可以通过增加中间信号作为缓冲,由该信号完成回读功能。' E e" @3 a* {+ Y8 f6 k+ n( q
双向端口Inout是四种端口类型中最为特殊的一种,最难以学习和掌握,为此专门提供一个简单程序进行阐述,部分程序如下:
. v' E5 H8 ]. l5 i) @8 y) A... …2 _1 [7 y1 I6 }' C( ]. @
① DataB<=Din when CE=’1’ and Rd=’0’ else* K) A2 S8 H7 \9 d* M
② (others=>’Z’);
! S4 }/ l! l6 ~③ Dout<=DataB when CE=’1’ and Rd=’1’ else: x; a) r5 p A
④ ( others=>’1’ );. M3 {2 v# }# V0 R# [
… …, G! }- e. w9 ~) O6 }1 Z
& Y2 A7 Z. e. k7 q8 i* I) ]
程序中DataB为双向端口,编程时应注意的是,当DataB作为输出且空闲时,必须将其设为高阻态挂起,即有类似第②行的语句,否则实现后会造成端口死锁。而当DataB作为有效输入时, DataB输出必须处于高阻态,对于该例子中即,当 CE=’1’ and Rd=’1’时,输出DataB应处于高阻态。; O5 l' |7 K* T
3 _ G/ s* A( c9 D' X- f
: w* E% S$ ?8 X' v( K/ R二.信号和变量( Y& {( g7 @" v8 s- M
常数、信号和变量是VHDL中最主要的对象,分别代表一定的物理意义。常数对应于数字电路中的电源或地;信号对应某条硬件连线;变量通常指临时数据的局部存储。信号和变量功能相近,用法上却有很大不同。
$ K9 M! P# `- v4 e7 S5 C+ u
3 p9 V8 P( M# g
0 Z, X9 T/ T4 e7 F/ `: i; H8 r# Q: S表1 信号与变量主要区别# z- z! J$ o! H* r/ j
信号 变量
# {; F9 p3 ~1 c* G9 D9 h9 D赋值延迟 至少有△延时 无,立即变化
: C" D2 o4 I- n& l/ u相关信息 有,可以形成波形 无,只有当前值1 T2 C' h$ z% k! `
进程敏感 是 否
- v% s0 l! a, c3 a全局性 具有全局性,可存在于多个进程中 只能在某个进程或子程序中有效
' ]8 m. g1 v) F! w* o相互赋值关系 信号不能给变量赋值 变量可以给信号赋值5 _. Z9 ~& ^/ e4 @. e/ D' e' @+ N7 G
对于变量赋值操作无延迟,初学者认为这个特性对VHDL设计非常有利,但这只是理论上的。基于以下几点原因,我们建议,编程时还是应以信号为主,尽量减少变量的使用。
; X' q; b+ \& Q: H# r4 }1 D5 [! _# ^4 A, l: j" [
(1)变量赋值无延时是针对进程运行而言的,只是一个理想值,对于变量的操作往往被综合成为组合逻辑的形式,而硬件上的组合逻辑必然存在输入到输出延时。当进程内关于变量的操作越多,其组合逻辑就会变得越大越复杂。假设在一个进程内,有关于变量的3个 级连操作,其输出延时分别为5ns,6ns,7ns,则其最快的时钟只能达到18ns。相反,采用信号编程,在时钟控制下,往往综合成触发器的形式,特别是对于FPGA芯片而言,具有丰富的触发器结构,易形成流水作业,其时钟频率只受控于延时最大的那一级,而不会与变量一样层层累积。假设某个设计为3级流水作业,其每一级延时分别为10ns,11ns,12ns,则其最快时钟可达12ns。因此,采用信号反而更能提高设计的速度。
$ a9 j& r& C' C% M: F& o$ ^
2 r k$ \/ E& l( g2 O6 n, T5 Z& R(2)由于变量不具备信息的相关性,只有当前值,因此也无法在仿真时观察其波形和状态改变情况,无法对设计的运行情况有效验证,而测试验证工作量往往会占到整个设计70%~80%的工作量,采用信号则不会存在这类问题。
; r/ e# m5 F8 `4 B' ?- O! r0 Z% a! H
(3)变量有效范围只能局限在单个进程或子程序中,要想将其值带出与其余进程、子模块之间相互作用,必须借助信号,这在一定程度上会造成代码不够简洁,可读性下降等缺点。$ H( Z7 R5 F) }0 _1 W% ]
当然,变量也具有其特殊的优点,特别是用来描述一些复杂的算法,如图像处理,多维数组变换等。# A e8 }- a$ V- S
& o7 X: p- U' h8 r9 W% V; c# |2 v. c$ J
9 P0 v, h7 `. x+ d' z, ~! B三.位(矢量)与逻辑(矢量)9 S6 P& ^2 ~; |1 I2 y/ J& w
bit 或其矢量形式bit_vector只有’0’和’1’两种状态,数字电路中也只有’0’和’1’两种逻辑,因此会给初学者一个误区,认为采用位(矢量)则足够设计之用,而不必像std_logic那样出现’X’,’U’,’W’各种状态,增加编程难度。但实际情况却并非如此,以一个最简单D型触发器设计为例
9 N7 Q8 J" _% r* j5 G4 Q5 T… …
1 b8 c/ O# y7 G# {① process(clk)
; H" U, X9 i8 `, C5 g7 `5 y' ^0 @② begin
+ g& z# H( M3 J' `③ if clk’event and clk=’1’ then# v$ e: x* A/ X i5 s! k
④ Q<=D;6 F u! m1 ]2 p4 \, F) K
⑤ end if;
9 U* a& O2 W$ R⑥ end process;2 A" s% L3 m% U6 r2 ?# T- X4 E
… …
( C: s [) g& W6 L8 k实际中clk对数据端D的输入有一定的时间限制,即在clk上升沿附近(建立时间和保持时间之内),D必须保持稳定,否则Q输出会出现亚稳态,如下图所示。% V3 F ~+ ~ V
% ~1 i2 p" V6 b$ E( M图1 建立时间和保持时间
9 Z* f3 l# E- q3 I# g当clk和D时序关系不满足时,由于bit只有’0’或’1’,系统只能随机的从’0’和’1’中给Q输出,这样的结果显然是不可信的;而采用std_logic类型,则时序仿真时会输出为一个’X’,提醒用户建立保持时间存在问题,应重新安排D和clk之间时序关系。5 e w/ s5 i5 f6 l
此外,对于双向总线设计(前面已提及)、 FPGA/cpld上电配置等问题,如果没有’Z’,’X’等状态,根本无法进行设计和有效验证。
2 r9 c9 B9 j) ~( i
4 ^: L6 @2 l' l7 v9 {; K9 ]' R" w
4 y H+ W4 e% t, C1 i8 \四.关于进程
: Y# d% | b+ n0 P$ P# Y进程(Process)是VHDL中最为重要的部分,大部分设计都会用到Process结构,因此掌握Process的使用显得尤为重要。以下是初学和使用Process经常会出错的例子。
& ?5 x) i, M2 \1 w
$ Y1 F/ H' G$ w% J" c# T/ [/ T# e1. 多余时钟的引入
7 A6 z2 G9 E; l! h1 _在设计时往往会遇到这种情况,需要对外部某个输入信号进行判断,当其出现上跳或下跳沿时,执行相应的操作,而该信号不像正常时钟那样具有固定占空比和周期,而是很随机,需要程序设计判断其上跳沿出现与否。这时,很容易写出如下程序:1 c5 o4 p, L; [8 {6 K0 m
① process(Ctl_a) -- Ctl_a即为该输入信号7 }* S6 u0 k5 ]8 N
② begin
! \. e; W# f5 r1 d# O4 o, R( w③ if Ctl_a’event and Ctl_a=’1’ then
! x. l4 ?/ U g1 g( F7 U2 Z④ … … ; --执行相应操作$ E0 [9 w6 |. h1 [3 w) ]* f) b
⑤ end if ;' t! k' ^! I+ E
⑥ end process;
# x/ |6 O- O3 \* ]7 P8 _由于出现第③行这类语句,综合工具自动默认Ctl_a为时钟,某些FPGA更会强行将该输入约束到时钟引脚上。而设计者的初衷只是想将其作为下位机的状态输入以进行判断。上面的程序容易造成多时钟现象,增加设计的难度。解决的办法可以如下,将Ctl_a增加一级状态Ctl_areg寄存,通过对Ctl_a和 Ctl_areg状态判断上跳与否,改正程序如下:
]0 h" C* Z, H: \; d9 O& y4 i4 |① process(clk)0 p4 n0 h' t- z0 H: S
② begin5 \ r8 `0 E8 s. E$ W
③ if clk’event and clk=’1’ then
" `1 J+ u2 I: a% Z6 X& [, N% _' R) w④ Ctl_areg<=Ctl_a;--产生相邻状态
9 B/ r% v; |- E$ g⑤ if Ctl_areg=’0’ and Ctl_a=’1’ then--上跳判断5 F; \8 ^4 a% s' e5 p: \
⑥ … … ; --执行相应操作) E% i$ U; _9 g M% O0 Z
⑦ end if;
* J, ~$ f; J( m0 g⑧ end if;
" V: q# ]2 M0 {9 C⑨ end process;" `3 R) a6 O/ ~9 Y
程序中第④行用以产生两个相邻状态,第⑤行对前后状态进行判断是否有上跳现象发生。其中,需注意的是clk的时钟频率应明显快于Ctl_a信号的变化频率,以保证正确采样。
, x6 t0 k6 r |, Y' }, k+ y- P- x+ V$ p) L J0 C U
2. 输出多驱动' T* V& o' ?4 p$ b" J: [ J2 k
误用Process经常会引起输出多驱动源的发生,即在两个以上的进程内对同一信号赋值操作。以下程序就出现了这类情况:
. e; I. X5 ~1 a O0 I% b⑴ Proc_a: process(clk)0 N- T& u2 S; \' N1 `8 I- p+ A
⑵ begin3 l d! }* v1 J1 \6 u' K( n
⑶ if clk’event and clk=’1’ then
" F" s1 ?7 W3 `, p/ [7 C/ M⑷ Dout<=Din_A;- r4 b. r# Y% b% f* C1 x# o* _; `
⑸ end if
1 M# Y4 T- f8 E7 [. g! I- t! ^⑹ end process;;
3 X5 j' o" n& V' K4 [2 @+ V⑺0 i. O* _1 b- ]2 C! b
⑻ Proc_b:process(sel_en)
7 R/ z" p1 V$ z4 U7 n, H⑼ begin
. |, D J# H9 r0 O⑽ if sel_en=’1’ then2 \4 g# r6 B1 x; N
⑾ Dout<=Din_B;
9 E! I3 y# n" q! X/ [1 }6 ]⑿ end if;
5 i2 y" C& J2 L4 Y$ n⒀ end process;# e: f! B+ m+ ]$ N/ P; x
进程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不知接收哪一个,因此该程序无法综合,改正的方法是只要将两个进程合并成一个即可。+ k$ Z) ^8 M' t: ?
由于进程在VHDL中的重要性,对此专门做了一个总结如下:! R( s7 A7 N# D$ m2 G
(1)一个进程中不允许出现两个时钟沿触发,(Xilinx公司CoolRunner系列CPLD支持单个时双钟的双触发沿除外)% A8 X: @( R: Z' M- E! E. O! K
(2)对同一信号赋值的语句应出现在单个进程内,不要在时钟沿之后加上else语句,如
8 X+ }* d! g: x( u/ Iif clk’event and clk=’1’ then - else … 的结构,现有综合工具支持不了这种特殊的触发器结构
' b! }: S* Q& Y1 Q(3)当出现多层IF语句嵌套时,最好采用CASE语句替代,一是减少多层嵌套带来的延时,二来可以增强程序的可读性0 i3 L# I3 Z, |- Z& e, T: I
(4)顺序语句如IF语句、CASE语句、LOOP语句、变量赋值语句等必须出现在进程、函数或子程序内部,而不能单独出现在进程之外& X: m' u8 m) j+ l
(5)进程内部是顺序执行的,进程之间是并行运行的;VHDL中的所有并行语句都可以理解为特殊的进程,只是不以Process结构出现,其输入信号和判断信号就是隐含的敏感表/ ?+ C- K) I3 e _& {) F
& }5 r$ k6 T% B5 X' w% N- t! m# T2 k( _) z! H0 j
五.关于VHDL学习中的几点说明' @1 |# r# E6 u
与软件语言相比,VHDL最重要的特点就在于它的并行运行特性,当设计好的电路上电后,器件内部所有信号将同时并发工作,而不会以软件方式按照程序顺序执行,即使在进程内部也是趋向并行工作的。例如以下程序:" r, j r# X9 ^
① process(clk)+ _6 d$ ~5 S; Y9 V# z+ e# [
② begin
5 l0 F. j6 h% [( V) K③ if clk’event and clk=’1’ then
: v; b" K8 y5 v9 W& u0 M④ <= ;) S! l8 u' m% d# f5 L, G, O' J
⑤ <= ;
& ]/ G/ r" y- U2 C⑥ end if;;
2 x9 r" y/ [, Y( M {7 u6 b⑦ end process;) b2 ?( I* F( v. e# q
综合的结果两个独立的D型触发器,虽然进程内部应按顺序执行,但是硬件实现后,只要采样到时钟上升沿, 和 状态会同时翻转,而不会先执行的变化,然后才会去执行的转变。因此,VHDL学习过程中,应加强硬件概念的理解,没有硬件概念或是硬件概念不强,在设计时,往往会将VHDL设计以软件编程的方式来处理,而得出一些不可思议的结果。
# [# E+ X3 Y4 o7 D1 }7 |! g作为一门硬件描述语言,VHDL几乎可以用来描述现有的大型系统数字电路、算法以及其它设计。但是,限于目前综合工具的水平,VHDL中的许多语法还不能支持,例如:
. F: h9 e# a( a3 d. p z3 s, edout<=din after 5 ns; ' A- j' s V% E: M8 b
综合时就无法达到如此精度,因此这条语句主要用来编写测试激励,而很少出现在设计实体中。类似的情况还有很多,目前VHDL设计使用的也只是整个标准中的一部分,这也正是VHDL的“可综合子集”性质,它一定程度上限制了VHDL的广泛应用,但是随着综合技术的发展,这种情况会逐渐得以改善,VHDL也将在各个领域中发挥出愈来愈重要的作用。$ j/ b7 W6 @+ p2 N) w% J& e: d
|
|