|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
VHDL学习理解
5 D/ u: E' O3 @5 B0 x; _6 U- K" M/ ~: s* ]
一. 关于端口+ G4 L- X8 @, M' R2 j4 y) a. d
VHDL共定义了5种类型的端口,分别是In, Out,Inout, Buffer及Linkage,实际设计时只会用到前四种。In和Out 端口的使用相对简单。这里,我们主要讲述关于buffer和inout使用时的注意事项。
" x. }+ E5 e6 [3 B8 Y与Out 端口比,Buffer端口具有回读功能,也即内部反馈,但在设计时最好不要使用buffer,因为buffer类型的端口不能连接到其他类型的端口上,无法把包含该类型端口的设计作为子模块元件例化,不利于大型设计和程序的可读性。若设计时需要实现某个输出的回读功能,可以通过增加中间信号作为缓冲,由该信号完成回读功能。
" l/ n+ R. c) ^ R$ t双向端口Inout是四种端口类型中最为特殊的一种,最难以学习和掌握,为此专门提供一个简单程序进行阐述,部分程序如下:
6 Q& T& d" B/ T7 _9 e# u... …* N% s8 _' d5 s# ^+ N: R( T
① DataB<=Din when CE=’1’ and Rd=’0’ else0 Y. [, y# o9 U, S
② (others=>’Z’);- M& w0 {6 [1 {3 ^
③ Dout<=DataB when CE=’1’ and Rd=’1’ else* y. H+ t# ?( `% N* ]. R# }
④ ( others=>’1’ );
- t ?! o+ f) A; S… …
# a) D; G9 m) [
1 F/ K; b! E9 q) H* m) e, b; Q程序中DataB为双向端口,编程时应注意的是,当DataB作为输出且空闲时,必须将其设为高阻态挂起,即有类似第②行的语句,否则实现后会造成端口死锁。而当DataB作为有效输入时, DataB输出必须处于高阻态,对于该例子中即,当 CE=’1’ and Rd=’1’时,输出DataB应处于高阻态。( W& n7 }; z: G3 T- v
9 q' X s$ g9 Y X7 O1 a& w' @! t7 M( {) j; _' t" m
二.信号和变量
4 c, b- |& H' R/ x常数、信号和变量是VHDL中最主要的对象,分别代表一定的物理意义。常数对应于数字电路中的电源或地;信号对应某条硬件连线;变量通常指临时数据的局部存储。信号和变量功能相近,用法上却有很大不同。
+ @9 O( b" Y* U! \" x% i2 B" ]+ t$ s$ R& ?. _
6 ?7 n+ D1 p5 m. q2 X
表1 信号与变量主要区别
- |* p0 z2 m0 S' \! m, u信号 变量
$ O, U* P' y2 I赋值延迟 至少有△延时 无,立即变化7 k' v: n* H2 W: Q' Z8 M
相关信息 有,可以形成波形 无,只有当前值; {# Z: R' a7 W
进程敏感 是 否
( v. J8 G* i) f _. { @全局性 具有全局性,可存在于多个进程中 只能在某个进程或子程序中有效
# J4 u9 i; T) V4 U2 N' {5 ~相互赋值关系 信号不能给变量赋值 变量可以给信号赋值 n2 f- Q, [% j& y0 `
对于变量赋值操作无延迟,初学者认为这个特性对VHDL设计非常有利,但这只是理论上的。基于以下几点原因,我们建议,编程时还是应以信号为主,尽量减少变量的使用。
% Z2 v5 ?5 e2 m* i
. U; l- X; ]: q% X(1)变量赋值无延时是针对进程运行而言的,只是一个理想值,对于变量的操作往往被综合成为组合逻辑的形式,而硬件上的组合逻辑必然存在输入到输出延时。当进程内关于变量的操作越多,其组合逻辑就会变得越大越复杂。假设在一个进程内,有关于变量的3个 级连操作,其输出延时分别为5ns,6ns,7ns,则其最快的时钟只能达到18ns。相反,采用信号编程,在时钟控制下,往往综合成触发器的形式,特别是对于FPGA芯片而言,具有丰富的触发器结构,易形成流水作业,其时钟频率只受控于延时最大的那一级,而不会与变量一样层层累积。假设某个设计为3级流水作业,其每一级延时分别为10ns,11ns,12ns,则其最快时钟可达12ns。因此,采用信号反而更能提高设计的速度。' C f) w: w4 _
* C. J/ l; H/ o4 e9 W0 P* \
(2)由于变量不具备信息的相关性,只有当前值,因此也无法在仿真时观察其波形和状态改变情况,无法对设计的运行情况有效验证,而测试验证工作量往往会占到整个设计70%~80%的工作量,采用信号则不会存在这类问题。/ ]% B2 o9 u, n
( b, A* V4 P+ }! G' J: O
(3)变量有效范围只能局限在单个进程或子程序中,要想将其值带出与其余进程、子模块之间相互作用,必须借助信号,这在一定程度上会造成代码不够简洁,可读性下降等缺点。, L1 M! _( r* L+ }: X& z* t7 H
当然,变量也具有其特殊的优点,特别是用来描述一些复杂的算法,如图像处理,多维数组变换等。0 H' X9 D$ ~2 U; P, Q- b* C4 D, d/ Q
8 s3 T; U. J6 R2 m( X
: I/ d( m1 c- g2 s) r2 `0 c; j" G" l三.位(矢量)与逻辑(矢量)# S# r1 J5 R7 H4 s z' H
bit 或其矢量形式bit_vector只有’0’和’1’两种状态,数字电路中也只有’0’和’1’两种逻辑,因此会给初学者一个误区,认为采用位(矢量)则足够设计之用,而不必像std_logic那样出现’X’,’U’,’W’各种状态,增加编程难度。但实际情况却并非如此,以一个最简单D型触发器设计为例
d" I) u0 V3 y/ ~1 e( W! V… …7 l6 y1 C0 l% @2 k
① process(clk)
$ A# _' Z" \- j: o0 |2 ]② begin; r$ `$ {" q6 Q
③ if clk’event and clk=’1’ then
6 b% h0 J1 p; h; w6 |" V0 Y4 t# k④ Q<=D;0 v+ k& g$ A7 C; P: O; F
⑤ end if;
, @* T# L/ Q2 g1 n ]3 Y* c: {! v⑥ end process;, G4 q+ K5 z: e! R) @1 }' p
… …
' S2 X1 D& X5 Y实际中clk对数据端D的输入有一定的时间限制,即在clk上升沿附近(建立时间和保持时间之内),D必须保持稳定,否则Q输出会出现亚稳态,如下图所示。/ ?9 e; i8 a" g- X
1 g; U5 U% E( F/ F( o图1 建立时间和保持时间6 ]; r9 e3 e; y+ H( F5 F
当clk和D时序关系不满足时,由于bit只有’0’或’1’,系统只能随机的从’0’和’1’中给Q输出,这样的结果显然是不可信的;而采用std_logic类型,则时序仿真时会输出为一个’X’,提醒用户建立保持时间存在问题,应重新安排D和clk之间时序关系。
1 B s$ d7 n8 s) B* R' g; A( ^此外,对于双向总线设计(前面已提及)、 FPGA/cpld上电配置等问题,如果没有’Z’,’X’等状态,根本无法进行设计和有效验证。0 c1 \/ Z, @3 R" e. p
! ~8 J0 O& z; m% G( S' Q0 c1 w& C3 e$ E
四.关于进程# {. R N: F7 M/ n- g
进程(Process)是VHDL中最为重要的部分,大部分设计都会用到Process结构,因此掌握Process的使用显得尤为重要。以下是初学和使用Process经常会出错的例子。
- Y; B2 ]3 j8 z" I4 s+ w! w
/ H7 [4 M s+ X. u4 |" n6 B7 W7 p1. 多余时钟的引入
$ A% f3 {3 }& M ~. T7 J在设计时往往会遇到这种情况,需要对外部某个输入信号进行判断,当其出现上跳或下跳沿时,执行相应的操作,而该信号不像正常时钟那样具有固定占空比和周期,而是很随机,需要程序设计判断其上跳沿出现与否。这时,很容易写出如下程序:
0 ]- S! N( g/ G① process(Ctl_a) -- Ctl_a即为该输入信号
6 b( Y7 {! A" j( ^② begin
# K b1 M, @" C/ P' n9 |③ if Ctl_a’event and Ctl_a=’1’ then; B, s' t- l7 V5 E
④ … … ; --执行相应操作
/ S4 r3 c8 y4 H7 V- G. D! n⑤ end if ;
+ K' I8 G' Q# v' _" i2 P, J⑥ end process;
5 @( ?9 `1 }+ F由于出现第③行这类语句,综合工具自动默认Ctl_a为时钟,某些FPGA更会强行将该输入约束到时钟引脚上。而设计者的初衷只是想将其作为下位机的状态输入以进行判断。上面的程序容易造成多时钟现象,增加设计的难度。解决的办法可以如下,将Ctl_a增加一级状态Ctl_areg寄存,通过对Ctl_a和 Ctl_areg状态判断上跳与否,改正程序如下:
$ s9 y% S4 m- A" }; i/ }① process(clk)1 B4 l& ?. q$ [* ?) I: J H
② begin4 u, s, m: s4 M' w9 l2 l
③ if clk’event and clk=’1’ then g- Z8 ~- `) i1 `
④ Ctl_areg<=Ctl_a;--产生相邻状态
- x* t/ ^# t$ u) U F4 u5 P) N⑤ if Ctl_areg=’0’ and Ctl_a=’1’ then--上跳判断
* M9 \, a( l/ R# \! T' J& d* S⑥ … … ; --执行相应操作2 I0 F- [- j1 B' _! ^
⑦ end if;
$ ?8 C3 C+ B1 n$ n' G⑧ end if;. ~3 w5 r0 P: |4 {& L- b* _
⑨ end process;. j9 f2 q& V' z( d, ~
程序中第④行用以产生两个相邻状态,第⑤行对前后状态进行判断是否有上跳现象发生。其中,需注意的是clk的时钟频率应明显快于Ctl_a信号的变化频率,以保证正确采样。2 ]& i z% ~: O2 `- Q9 _7 U
! o; e( R4 e: k' _+ S o; G
2. 输出多驱动
1 F6 i2 [0 e3 i0 L" c5 G! F: R误用Process经常会引起输出多驱动源的发生,即在两个以上的进程内对同一信号赋值操作。以下程序就出现了这类情况:8 M- x9 ?) I3 H `
⑴ Proc_a: process(clk)% V' O, }- e4 C1 b h5 D( F, s6 _
⑵ begin
8 U" x% d& ~3 J3 B& ]: q8 m6 m9 @⑶ if clk’event and clk=’1’ then
6 v+ u" p3 h5 D7 I5 t% T# k8 f⑷ Dout<=Din_A;+ z! d( m8 c4 \- d9 p, T; P
⑸ end if% z& A* F6 m* L5 P( J
⑹ end process;;
3 E, F/ [! N+ r2 V/ n4 U) Z⑺
- ^/ d/ g: f! E% x0 T⑻ Proc_b:process(sel_en)7 u1 j5 t3 J# `( Q& x
⑼ begin. [/ O/ ~+ R' A1 v: t8 q
⑽ if sel_en=’1’ then
# u: z# M, \+ h( i3 ?/ ]⑾ Dout<=Din_B;
' [' c( ^( B3 y( [3 K ]⑿ end if;
2 R# J$ M7 @; U" M! m6 H. u⒀ end process;" W/ G& i; c" p( O3 D/ B# I( p
进程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不知接收哪一个,因此该程序无法综合,改正的方法是只要将两个进程合并成一个即可。 M+ p6 p+ f: b
由于进程在VHDL中的重要性,对此专门做了一个总结如下:
& G6 B! }* U. Y(1)一个进程中不允许出现两个时钟沿触发,(Xilinx公司CoolRunner系列CPLD支持单个时双钟的双触发沿除外)% `: ?8 _. z! @$ C
(2)对同一信号赋值的语句应出现在单个进程内,不要在时钟沿之后加上else语句,如
' p0 B& \: m1 ^+ h( yif clk’event and clk=’1’ then - else … 的结构,现有综合工具支持不了这种特殊的触发器结构' q. q. j' D0 Z5 G
(3)当出现多层IF语句嵌套时,最好采用CASE语句替代,一是减少多层嵌套带来的延时,二来可以增强程序的可读性- z: i3 R# \% A( d& T x4 O
(4)顺序语句如IF语句、CASE语句、LOOP语句、变量赋值语句等必须出现在进程、函数或子程序内部,而不能单独出现在进程之外* X7 k. Q- |8 t2 B4 P: K
(5)进程内部是顺序执行的,进程之间是并行运行的;VHDL中的所有并行语句都可以理解为特殊的进程,只是不以Process结构出现,其输入信号和判断信号就是隐含的敏感表
) l; M2 K# X, [2 k" J) y
/ v {; I( J) Y* O! `. a- S; f' ?! p' [
五.关于VHDL学习中的几点说明1 c0 i" k5 Y/ [0 F
与软件语言相比,VHDL最重要的特点就在于它的并行运行特性,当设计好的电路上电后,器件内部所有信号将同时并发工作,而不会以软件方式按照程序顺序执行,即使在进程内部也是趋向并行工作的。例如以下程序:
& h. D1 M8 i& t& t① process(clk)
! J# \* m; s' ?) Z② begin
* A) y) B' K! D5 j③ if clk’event and clk=’1’ then' |& ^ R) f( H% I: t& q, S
④ <= ;& O. A2 K! }0 M V' O# J9 C
⑤ <= ;
. y+ X# v! _7 G0 x# t# t, y+ z' q⑥ end if;;& |6 f% ?: J* s0 e$ K' O5 l8 x
⑦ end process;0 e9 O! \, C+ Q( J
综合的结果两个独立的D型触发器,虽然进程内部应按顺序执行,但是硬件实现后,只要采样到时钟上升沿, 和 状态会同时翻转,而不会先执行的变化,然后才会去执行的转变。因此,VHDL学习过程中,应加强硬件概念的理解,没有硬件概念或是硬件概念不强,在设计时,往往会将VHDL设计以软件编程的方式来处理,而得出一些不可思议的结果。7 H) U" }: @: S# j0 c
作为一门硬件描述语言,VHDL几乎可以用来描述现有的大型系统数字电路、算法以及其它设计。但是,限于目前综合工具的水平,VHDL中的许多语法还不能支持,例如:3 K9 j, q0 f/ X" s, s6 t: R8 m
dout<=din after 5 ns;
) Z* B/ r, Y( l' h2 R, z综合时就无法达到如此精度,因此这条语句主要用来编写测试激励,而很少出现在设计实体中。类似的情况还有很多,目前VHDL设计使用的也只是整个标准中的一部分,这也正是VHDL的“可综合子集”性质,它一定程度上限制了VHDL的广泛应用,但是随着综合技术的发展,这种情况会逐渐得以改善,VHDL也将在各个领域中发挥出愈来愈重要的作用。
* v! r7 M8 P$ C( A |
|