VHDL中信号赋值语句的讨论
发布时间:2019-10-23 14:48:29
发布时间:2019-10-23 14:48:29
VHDL中信号与变量赋值语句的讨论
一 信号与变量赋值语句的理论基础
信号声明在子程序、进程等外部,并可以在几个不同的进程里作用;而变量声明在子程序、进程等内部。
信号与变量的赋值不同:在进程中,信号赋值在进程结束时起作用,而变量赋值是立即起作用的。如果在一个进程中多次为一个信号赋值时,只由最后一个值会起作用;而当为变量赋值时,变量值的改变是立即发生的。即变量将保持着当前值,直到被赋予新的值。
顺序语句只存在于进程和子程序内部。
二 信号的赋值
(一) 进程外部信号的赋值
(1)进程外部不能为同一信号多次赋值
在进程的外部,我们不能够为同一信号多次赋值。
例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:
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
y<=s+1;
s<=a+b;
end architecture one;
在例2的程序中,如果a或b发生变化,则执行语句s<=a+b;因为a或b变化,影响使得s变化,则执行语句y<=s+1。仿真结果如图1所示。
图1信号在进程外部的赋值
在例2中y<=s+1; s<=a+b;这两句的先后顺序不影响结果。这一点也能体现并行语句的特点。
(二) 进程内部信号的普通赋值
这里所说的进程内部信号的普通赋值,是指信号赋值不需要通过时钟边沿来驱动。由于进程中的语句都是顺序语句,所以进程中的普通信号赋值都是顺序执行的,但有一点需要注意,就是如果在一个进程中多次为一个信号赋值时,只有最后一个值会起作用。什么意思呢?也就是说在进程内部多次为一个信号赋值时,只有最后一次赋值会起作用;如果还将此信号赋给其他信号,一律都按此信号的最后一次所赋的值赋给其它信号,而为此信号多次赋值的语句的位置可以任意(在顺序一定的前提下),对结果没有任何影响。
例3:
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
entity test is
port(clk:in std_logic;
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
process(a,b,s)
begin
y<=s+1;
s<=a;
s<=a+b;
end process;
end architecture one;
在例3的程序中,如果a或b发生变化,则执行此进程;对s的最后一次赋值语句为s<=a+b,所以将a+b的值赋给s;而y<=s+1这句赋值语句中s的值取得便是对s最后一次所赋的值a+b,而y<=s+1这句语句的位置对结果没有影响。仿真结果如图2所示。
图2 信号在进程内部的赋值(1)
例4:
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
entity test is
port(clk:in std_logic;
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
process(a,b,s)
begin
s<=a+b;
s<=a;
y<=s+1;
end process;
end architecture one;
在例4的程序中,如果a或b发生变化,则执行此进程;对s的最后一次赋值语句为s<=a,所以将a的值赋给s;而y<=s+1这句赋值语句中s的值取得便是对s最后一次所赋的值a,而y<=s+1这句语句的位置对结果没有影响。仿真结果如图3所示。
图3 信号在进程内部的赋值(2)
在例3和例4中,y<=s+1放在三句赋值语句的什么位置上对结果没有影响。
(三) 进程内部信号的时钟边沿赋值
这里所说的进程内部信号的时钟赋值,是指信号赋值需要通过时钟边沿的驱动。由于时钟边沿触发的特殊性,即在时钟边沿发生时,对各个信号采样,然后将时钟边沿这一时刻各个信号的取值赋给所要赋值的对象,也就是将时钟到来前未发生变化的各个信号的取值赋给所要赋值的对象。但这些信号的赋值仍是顺序执行的(因为在if语句中),所以有一点仍需要注意,就是如果在一个进程中通过时钟边沿触发多次为同一个信号赋值时,只有最后一个值会起作用。什么意思呢?也就是说在进程内部多次为同一个信号赋值时,只有最后一次赋值会起作用;如果还将此信号赋给其他信号,一律都按此信号的最后一次所赋的值赋给其它信号,而为此信号多次赋值的语句的位置可以任意,对结果没有任何影响。
例5:
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
entity test is
port(clk:in std_logic;
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
process(clk)
begin
if clk='1'and clk'event then
s<=a+b;
s<=a;
y<=s+1;
end if;
end process;
end architecture one;
在例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,所以y在波形上与例3例4中y的波形有1个时钟周期的延时。仿真结果如图4所示。
图4 信号在进程内部时钟边沿赋值(1)
例6:
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
entity test is
port(clk:in std_logic;
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
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,而y<=s+1这句语句的位置对结果没有影响;各个赋值语句右侧的信号的取值为时钟上升沿那一刻的采样值,所以与例3例4不同,y<=s+1这句赋值语句所执行的是时钟采样那一时刻的s还没有发生变化时(未赋值变成新的a+b时)的取值加1后再赋给y,所以y在波形上与例3例4中y的波形有1个时钟周期的延时。仿真结果如图5所示。
图5 信号在进程内部时钟边沿赋值(2)
在上两例中,y<=s+1;放在三句赋值语句的什么位置上对结果没有影响;if语句换成wait until clk='1'结果也一样。
三 变量的赋值
同信号不同,变量的赋值只能在进程内部完成。
(一) 变量的普通赋值
这里所说的变量的普通赋值,是指变量赋值不需要通过时钟边沿的驱动。由于进程中的语句都是顺序语句,所以进程中的变量赋值都是顺序执行的。但有一点需要注意,就是如果在一个进程中多次为一个变量赋值时,赋值会立即起作用,这就与C语言等普通的高级语言相类似了。什么意思呢?也就是说在进程内部多次为一个变量赋值时,赋值立即起作用;如果还将此变量赋给其他信号或变量,一律都按此变量的最近一次所赋的值赋给其它信号或变量,而为此变量多次赋值的语句的位置对结果产生影响。
例7
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
entity test is
port(clk:in std_logic;
a,b:in std_logic_vector(3 downto 0);
y:out std_logic_vector(3 downto 0));
end entity test;
architecture one of test is
begin
process(a,b)
variable s: std_logic_vector(3 downto 0);
begin
s:=a;
s:=a+b;
y<=s+1;
end process;
end architecture one;
在例7的程序中,由于s是变量,所以对s的赋值都是立即起作用的。变量与信号的赋值也都是顺序执行的,这些都与信号的赋值相类似。由于y<=s+1写在s:=a+b之后,所以将a+b的值赋给y。仿真结果如图6所示。
图6 变量的普通赋值(1)
例8:
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
entity test is
port(clk:in std_logic;
a,b:in std_logic_vector(3 downto 0);
y:out std_logic_vector(3 downto 0));
end entity test;
architecture one of test is
begin
process(a,b)
variable s: std_logic_vector(3 downto 0);
begin
s:=a;
y<=s+1;
s:=a+b;
end process;
end architecture one;
在例8的程序中,由于s是变量,所以对s的赋值都是立即起作用的。变量与信号的赋值也都是顺序执行的,这些都与信号的赋值相类似。由于y<=s+1写在s:=a之后,所以将a的值赋给y。仿真结果如图7所示。
图7 变量的普通赋值(2)
(二) 变量的时钟边沿赋值
这里所说的变量的时钟赋值,是指变量赋值需要通过时钟边沿的驱动。由于变量不同于信号,其赋值是立即进行的;所以如果一个变量在时钟边沿到来时多次赋值,然后再将其赋给其它信号或变量,则将其最后一次赋值所得到的值在赋给其它信号或变量。
例9:
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
entity test is
port(clk:in std_logic;
a,b:in std_logic_vector(3 downto 0);
y:out std_logic_vector(3 downto 0));
end entity test;
architecture one of test is
begin
process(clk)
variable s: std_logic_vector(3 downto 0);
begin
if clk='1'and clk'event then
s:=a;
y<=s+1;
s:=a+b;
end if;
end process;
end architecture one;
在例9的程序中,如果clk发生变化,则执行此进程,如果clk出现上升沿,则执行if语句中的几句赋值语句;对s的进行赋值s:=a,所以将a的值立即赋给s;而y<=s+1这句赋值语句紧接着s:=a的后面,所以将此时的s加1然后赋给y;由于变量与信号不同,其作用的范围仅限于进程,所以与例5例6不同,y<=s+1这句赋值语句所执行的是s已经发生变化时(赋值变成新的a时)的取值加1后再赋给y,所以y在波形没有例5例6中y的波形的1个时钟周期的延时。仿真结果如图8所示。
图8 变量时钟边沿赋值(1)
例10:
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
entity mux21 is
port(clk:in std_logic;
a,b:in std_logic_vector(3 downto 0);
y:out std_logic_vector(3 downto 0));
end entity mux21;
architecture one of mux21 is
begin
process(clk)
variable s: std_logic_vector(3 downto 0);
begin
if clk='1'and clk'event then
s:=a;
s:=a+b;
y<=s+1;
end if;
end process;
end architecture one;
在例10的程序中,如果clk发生变化,则执行此进程,如果clk出现上升沿,则执行if语句中的几句赋值语句;对s的进行赋值s:=a+b,所以将a+b的值立即赋给s;而y<=s+1这句赋值语句紧接着s:=a+b的后面,所以将此时的s加1然后赋给y;由于变量与信号不同,其作用的范围仅限于进程,所以与例5例6不同,y<=s+1这句赋值语句所执行的是s已经发生变化时(赋值变成新的a+b时)的取值加1后再赋给y,所以y在波形没有例5例6中y的波形的1个时钟周期的延时。仿真结果如图9所示。
图9 变量时钟边沿赋值(2)
上述例1~10的程序必须要有use ieee.std_logic_unsigned.all;,否则报错:can’t interpret subprogram call。
四 进程里不可以有并行赋值语句
进程里不可以存在条件信号赋值语句和选择信号赋值语句等并行赋值语句。
例11:
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;
例11中的进程里含有条件信号赋值语句,所以编译的时候会报错,如下:
ERROR:sequential signal assignment cannot contain conditional waveforms。
例12:
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;
例12中的进程里含有有选择信号赋值语句,所以编译的时候会报错,如下:
ERROR: found illegal use of a selected signal assignment statement in process statement part。