原创 【博客大赛】VHDL中信号与变量的区别及赋值的讨论(强烈建议大家仔细阅读全文,相信会是大家对信号的使用有个更清晰的认识)

2013-12-6 11:44 7606 17 28 分类: FPGA/CPLD 文集: VHDL中信号与变量

VHDL中信号与变量的区别及赋值的讨论(强烈建议大家仔细阅读全文)

    相信大家在看许多介绍VHDL语言的参考书的时候都会对其中的一些关于讲解信号与变量的例子产生过疑问,也许自己也确实仿真过,但是结果可能与自己分析的不一致,赋值结果可能会迟一个时钟周期,或者早到一个时钟周期,此时有些人估计就会稀里糊涂的认为就是那样的了,却并不知道自己分析的错在哪儿,这里本人就用多个例子来介绍二者的区别以及赋值语句发生时刻的不同。大家可以跟着一起分析具体的执行过程,并且对结果进行仿真,看是否正确。

信号与变量的区别

1、声明形式与赋值符号不同

变量声明使用variable,赋值符号位:=

而信号声明用signal,复制符号为<=

2、有效域不同

信号的声明在结构体内部,进程、子程序及函数外部声明,而变量只能在进程,函数体,子程序内部进行声明。换句话说,信号的有效作用域为整个结构体,而变量只能在进程,函数体,子程序内部起作用,他们不能为多个进程所共用。

3、赋值操作及数据带入时刻不同

在进程中,信号赋值在进程结束时起作用(即整个进程执行到最后一条语句时进程接下来挂起时,数据才发生带入),而变量赋值是立即起作用的。如果在一个进程中多次为一个信号赋值时,只由最后一个值会起作用;而当为变量赋值时,变量值的改变是立即发生的。即变量将保持着当前值,直到被赋予新的值。

顺序语句只存在于进程和子程序内部。

4、应用场合不同

       在实际应用中,信号的行为更接近硬件电路的实际情况,因此应该更多地使用信号进行电路内部的数据传递,只有在描述一些用信号很难描述的算法时,才用到变量。(也有人建议在同一进程内部传递的数据用变量,而在进程间传递的数据建议用信号)但在以下几种情况只能使用变量:

   (1)loop语句中,若在一个循环体内需要多次对某一个数据操作,则必须用变量,因为对信号赋值进行多次赋值只在最后一次才会有效。

(2)数组的索引(index)只能用变量。如果使用信号则编译会报错。

信号的赋值

1 进程外部信号的赋值

(1)进程外部不能为同一信号多次赋值

在进程的外部,我们不能够为同一信号多次赋值

例1: (例2到6中的库文件声明和实体部分均与例1相同,为了节省空间就没有再列出)

library ieee;

use ieee.std_logic_1164.all;

use ieee.std_logic_unsigned.all;

entity test is

               port(a,b:in std_logic_vector(3 downto 0);

              s:buffer std_logic_vector(3 downto 0);

              y:out std_logic_vector(3 downto 0));

end entity test;

architecture one of test is

          begin

            s<=a;

            y<=s+1;

            s<=a+b;

        end architecture one;

在例1程序中,我们对信号s进行了多次赋值,经过仿真,系统报如下错误:ERROR: Signal “s” has multiple sources。

(2)进程外部对信号的赋值

进程外部几个信号的赋值语句是并行执行的。即信号赋值语句中的信号发生变化,便执行该条赋值语句。

例2:

architecture one of test is

               begin

            y<=s+1;

            s<=a+b;

end architecture one;

    在例2的程序中,如果a或b发生变化,则执行语句s<=a+b;因为a或b变化,影响使得s变化,则执行语句y<=s+1。因为这两条语句是并行的。仿真结果如图1所示。

1.jpg

图1信号在进程外部的赋值

在例2中y<=s+1; s<=a+b;这两句的先后顺序不影响结果。这一点也能体现并行语句的特点。

注意:仿真软件的功能仿真结果是不存在延时的,所以对于有些书中或是文档里说仿真结果某个信号出现延时的情况全都是错误的,

2、进程内部信号的普通赋值

这里所说的进程内部信号的普通赋值,是指信号赋值不需要通过时钟边沿来驱动。由于进程中的语句都是顺序语句,所以进程中的普通信号赋值都是顺序执行的,但有一点需要注意,就是如果在一个进程中多次为一个信号赋值时,只有最后一个值会起作用。什么意思呢?也就是说在进程内部多次为一个信号赋值时,只有最后一次赋值会起作用;如果还将此信号赋给其他信号,一律都按此信号的最后一次所赋的值赋给其它信号,而为此信号多次赋值的语句的位置可以任意(在顺序一定的前提下),对结果没有任何影响。

例3:

               process(a,b,s)

begin

             y<=s+1;

s<=a;

                  s<=a+b;

   end process;

在例3的程序中,如果a或b发生变化(由高到低或由无到有),则执行此进程;对s的最后一次赋值语句为s<=a+b,所以将a+b的值赋给s;而y<=s+1这句赋值语句中s的值取得便是对s最后一次所赋的值a+b,而y<=s+1这句语句的位置对结果没有影响。仿真结果如图2所示。

2.jpg

图2 信号在进程内部的赋值(1)

例4:

             process(a,b,s)

begin

                   s<=a+b;

 s<=a;

                  y<=s+1;

 end process;

在例4的程序中,如果a或b发生变化,则执行此进程;对s的最后一次赋值语句为s<=a,所以将a的值赋给s(s始终保持与a一致);而y<=s+1这句赋值语句中s的值取得便是对s最后一次所赋的值a,而y<=s+1这句语句的位置对结果没有影响。仿真结果如图3所示。

3.jpg

图3 信号在进程内部的赋值(2)

在例3和例4中,y<=s+1放在三句赋值语句的什么位置上对结果没有影响。

3 进程内部信号的时钟边沿赋值

这里所说的进程内部信号的时钟赋值,是指信号赋值需要通过时钟边沿的驱动。由于时钟边沿触发的特殊性,即在时钟边沿发生时,对各个信号采样,然后将时钟边沿这一时刻各个信号的取值赋给所要赋值的对象,也就是将时钟到来前未发生变化的各个信号的取值赋给所要赋值的对象(换句话说就是指,当进程执行结束之后进程挂起时,数据发生带入)但这些信号的赋值仍是顺序执行的(因为在if语句中),所以有一点仍需要注意,就是如果在一个进程中通过时钟边沿触发多次为同一个信号赋值时,只有最后一个值会起作用。什么意思呢?也就是说在进程内部多次为同一个信号赋值时,只有最后一次赋值会起作用;如果还将此信号赋给其他信号,一律都按此信号的最后一次所赋的值赋给其它信号,而为此信号多次赋值的语句的位置可以任意,对结果没有任何影响。

例5:

              process(clk)

              begin

                 if clk='1'and clk'event then

                   s<=a+b;

                   s<=a;

                   y<=s+1;

                 end if;

  end process;

在例5的程序中,如果clk发生变化,则执行此进程,如果clk出现上升沿,则执行if语句中的几句赋值语句;对s的最后一次赋值语句为s<=a,所以将a的值赋给s;而y<=s+1这句赋值语句中s的值取得便是对s最后一次所赋的值a,而y<=s+1这句语句的位置对结果没有影响;各个赋值语句右侧的信号的取值为时钟上升沿那一刻的采样值,所以与例3例4不同,y<=s+1这句赋值语句所执行的是时钟采样那一时刻的s还没有发生变化时(未赋值变成新的a)的取值加1后再赋给y仿真结果如图4所示。

4.jpg

图4 信号在进程内部时钟边沿赋值(1)

对于仿真结果进行分析:

在第一个时钟上升沿到来之前,进程没有启动,所有的值都是原始值0,而当以一个时钟上升沿到来时,进程启动,执行金城内的语句;并且在进程执行完之后,重新挂起时赋值,也就是说,时钟上升沿到来之后一直到下一个时钟上升沿到来时,这段期间内进程一直是挂起的,数据发生带入,所以y=1;而在第二个时钟上升沿到来的时候,a的值应经发生过变化了,为5,所以当进程执行完挂起时,数据发生带入,s最后一次赋值有效为5;而y的执行是将上一时刻s的值赋给y,所以y仍为1。当第三个时钟沿上升沿到来时,进程再次启动,分析同第二次一样,但是由于上一时刻s已经变为5了,所以y=6。

例6:

              process(clk)

                  begin

                  if clk='1'and clk'event then

                   s<=a;

                   s<=a+b;

y<=s+1;

                  end if;

              end process;

end architecture one;

在例6的程序中,如果clk发生变化,则执行此进程,如果clk出现上升沿,则执行if语句中的几句赋值语句;对s的最后一次赋值语句为s<=a+b,所以将a+b的值赋给s;而y<=s+1这句赋值语句中s的值取得便是对s最后一次所赋的值a+b;各个赋值语句右侧的信号的取值为时钟上升沿那一刻的采样值,所以与例3例4不同,y<=s+1这句赋值语句所执行的是时钟采样那一时刻的s还没有发生变化时(未赋值变成新的a+b)的取值加1后再赋给y。仿真结果如图5所示。

5.jpg

图5 信号在进程内部时钟边沿赋值(2)

在上两例中,y<=s+1;放在三句赋值语句的什么位置上对结果没有影响;if语句换成wait until clk='1'结果也一样。

进程里不可以有并行赋值语句

进程里不可以存在条件信号赋值语句和选择信号赋值语句等并行赋值语句。

例a:

library ieee;

use ieee.std_logic_1164.all;

entity mux21 is

              port(a,b:in std_logic;

             s:in std_logic;

               y:out std_logic);

end entity mux21;

architecture one of mux21 is

              begin

              process(s,a,b)

           begin

                  y<=a when s='0' else

                   b when s='1';

              end process;

end architecture one;

    例a中的进程里含有条件信号赋值语句,所以编译的时候会报错,如下:

ERROR:sequential signal assignment cannot contain conditional waveforms。

例b:

library ieee;

use ieee.std_logic_1164.all;

entity mux21 is

              port(a,b:in std_logic;

               s:in std_logic;

               y:out std_logic);

end entity mux21;

architecture one of mux21 is

              begin

              process(s,a,b)

               begin

                  with s select

                  y<=a when '0',

                  b when '1';

              end process;

end architecture one;

例b中的进程里含有有选择信号赋值语句,所以编译的时候会报错,如下:

ERROR: found illegal use of a selected signal assignment statement in process statement part。

 

文章评论11条评论)

登录后参与讨论

用户377235 2014-8-20 14:05

对信号和进程有了更深的理解,很不错的文章!

用户377235 2013-12-13 20:54

正好在学习VHDL,收藏了!

用户377235 2013-12-12 09:20

是一篇不错的博文,值得推荐。

用户377235 2013-12-12 09:19

信号分析起来是比较麻烦,因为是强触发!

1053683568_507245520 2013-12-3 09:30

谢谢博友的支持,后面我会加上姊妹篇的!

用户1725882 2013-12-3 09:24

怎么没有变量的分析了呢?

用户403664 2013-12-3 08:57

多谢博主!

用户1725879 2013-12-2 20:05

信号以前确实令人头疼,还好现在给解释了!

用户1725882 2013-12-2 19:43

这篇写的不错,以前总是在纠结信号到底是如何执行的,现在终于明白了,谢谢楼主!

用户1725879 2013-12-2 19:24

楼主的文章感觉思路,过程都写得很清楚,强烈支持!
相关推荐阅读
1053683568_507245520 2014-03-20 11:19
cadence16.6 软件绘制PCB板
   最近一直在学习cadence的使用,现在就对近来学习做个总结,虽然还没有达到熟练的程度,但是也希望可以给后来者做个借鉴吧。         本人的方向是硬件方向,马上面临着找工作的压力。...
1053683568_507245520 2014-01-03 21:02
【博客大赛】自己定制DPRAM的读写操作
DPRAM的读写操作 下面是本人写的DPRAM的读写操作,自己定制DPRAM,不用altera FPGA 内部自带的IP core,选择的存储器块类型是M9K(对于不同类型的存储器块,当同时对...
1053683568_507245520 2013-12-15 20:51
【博客大赛】我的EDN
我的EDN 每次进入EDN网站,看到大家在踊跃的分享自己的学习经历,经验,以及聊生活谈梦想,都会激发我的热情,激发我对生活的热爱,对未来的幻想,对自己的思索。当我懒惰时,我会进来看看,看看特权...
1053683568_507245520 2013-12-07 11:31
【博客大赛】利用FPGA控制VGA接口来使显示器显示彩条纹(里面包括调试中的查错检查方法)
利用FPGA控制VGA接口来使显示器显示彩条纹 最近一直在调关于通过VGA接口来控制显示器的程序,今天终于调试出结果来了,赶紧上来与大家分享一下。 1、VGA显示原理 常见的彩色显示器...
1053683568_507245520 2013-12-06 11:45
【博客大赛】利用VHDL产生奇分频器
利用VHDL产生占空比为50%的奇倍数分频电路 一般的介绍VHDL语言的书在对分频器进行举例描述的时候,都是举得偶数倍分频的例子,因为那相对来说比较简单(只需要设置一个中间信号变量,然后在分频...
1053683568_507245520 2013-12-06 11:45
【博客大赛】VHDL进程语句的并行执行分析
相信许多人在学习VHDL时,都会对进程如何执行的,以及进程之间的并行执行产生疑问,本文将以下面一个具体的例子来分析单个进程的执行,与进程之间的并行执行。 library ieee; use...
我要评论
11
17
关闭 站长推荐上一条 /2 下一条