1. 并行语句概述
在VHDL中,各种并行语句在结构体中的执行是同步进行的,其执行方式与书写的顺序无关。
并行语句在结构体中的使用格式如下:
architecture 结构体名 of 实体名 is
[说明语句;]
begin
并行语句;
end 结构体名;
2. 并行语句的分类
2.1 并行信号赋值语句
VHDL中的并行语句主要包括一下三类:
- 简单信号赋值语句
- 条件信号赋值语句
- 选择信号赋值语句
赋值语句存在于结构体之中、进程之外,所有赋值语句与其他并行语句一样,在结构体内的执行是同时发生的,与它们的书写顺序没有关系。
赋值目标必须都是信号,每一信号的赋值语句都相当于一个缩写的进程语句,这条语句所有输入信号都被隐性地列入缩写的进程的敏感表中,所有的输入、读出、双向信号都在所在的结构体的严密检测下,任何变化都将启动赋值操作。
2.1.1 简单信号赋值语句
语法格式:
赋值目标 <= 表达式;
注意:信号赋值目标的数据类型必须与赋值符号右边表达式的数据类型一致。
示例:组合逻辑门
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
entity LogicGates is
Port (
A : in std_logic;
B : in std_logic;
Y : out std_logic;
Z : out std_logic
);
end LogicGates;
architecture Behavioral of LogicGates is
begin
Y <= A and B; -- 并行信号赋值,实现与门
Z <= A or B; -- 并行信号赋值,实现或门
end Behavioral;
说明:
- 信号
Y
和Z
分别实现与门和或门的功能。 - 赋值语句在架构内部直接编写,自动并行执行。
2.1.2 条件信号赋值语句
语法格式:
signal_name <= 表达式1 when 条件1 else
表达式2 when 条件2 else
...
表达式n;
条件信号赋值语句按书写顺序逐一判断赋值条件,当发现赋值条件为True时,则立即将表达式的值赋给赋值目标。
注意:该语句无论有多少个表达式需要赋值,它们的赋值对象即赋值目标只能是一个,而且只书写一次。
示例:3-8译码器
library IEEE;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
entity decoder3_8 is
port(
in_value:in std_logice_vector(2 downto 0);
out_value:out std_logice_vector(7 downto 0)
);
end entity decoder3_8;
architecture behavior of decoder3_8 is
begin
out_value <= "00000001" when in_value = "000" else
"00000010" when in_valie = "001" else
"00000100" when in_valie = "010" else
"00001000" when in_valie = "011" else
"00010000" when in_valie = "100" else
"00100000" when in_valie = "101" else
"01000000" when in_valie = "110" else
"1000000";
end behavior
该译码器是输出高电平有效译码器,当输入数据为000~111时,输出端out_value的out_value(0)至out_value(7)分别 获得唯一的高电平,从而对输入进行了有效译码。
2.1.3 选择信号赋值语句
语法格式:
with 选择表达式 select
信号名 <= 表达式1 when 值1;
表达式2 when 值2;
……
表达式n when others;
注意事项:
(1)当选择表达式等于某值时,将该值前面的表达式赋给目标信号;
(2)with select 语句不能在进程和子程序中使用
;
(3)语句对选择条件的判断是同时进行的,因此不允许选择条件重叠
;
(4)无论是否列举了选择表达式所有取值的可能,语句最后一个子句要求一定为when others,从而将所有条件包罗进去。
示例:基于选择信号的四选一多路选择器。
library ieee;
use ieee.std_logic_1164.all;
entity mux4_1 is
port(
a:in std_logic;
b:in std_logic;
c:in std_logic;
d:in std_logic;
sel:in std_logic_vector(1 downto 0);
out_value:out std_logic
);
architecture behavior of mux4_1 is
begin
with sel select
out_value <= a when "00",
b when "01",
c when "10",
d when "11",
'0' when others;
end behavior
说明:
- 使用
with-select-when
结构,根据选择信号sel的不同值,将对应的输入信号赋值给输出Y。 when others
确保覆盖所有未列出的选择信号组合,避免逻辑冒险。
2.2 进程语句
每个进程语句architecture
内部定义,并可以包含敏感列表或使用wait
语句来控制其执行时机。
一个architecture
可以包含一个或多个进程语句,各个进程语句是并行执行的,但在每一个进程语句中,组成进程的各条语句则是顺序执行的。
语法结构:
[进程名:]process [(敏感信号列表)]
[说明语句];
begin
顺序语句;
end process [进程名];
组成说明:
(1)每一个进程语句可以有一个进程名,主要用于区分不同进程,提高代码可读性;进程名是唯一的,不同的进程不能具有相同的进程名;同时,进程名不是必须的。
(2)敏感信号列表中可以是一个或多个敏感信号,只要其中一个或多个敏感信号发生变化,进程语句将会启动,从而引起进程内部顺序语句的执行。
(3)说明语句用于说明进程中需要使用的一些局部量,包括数据类型、
常数、变量、属性等,非必须。
(4)顺序语句描述该进程的行为,这些语句可以是信号赋值语句、变量赋值语句等。
注意事项:
(1)对于一个进程语句来说,有两种工作状态:等待状态和执行状态。进程语句的工作状态主要取决于敏感信号激励,当信号没有变化或表达式不满足时,进程处于等待状态;当敏感信号中的任意一个发生变化,并且表达式满足时,进程将会启动进入到工作状态。
(2)进程启动后,begin
和end process
之间的语句将从上到下顺序执行一次,然后进程挂起,等待下一次敏感信号表中的信号变化,如此循环往复。
(3)进程是根据相应的敏感信号独立运行的,所以多个进程之间具有并行性。
(4)一个进程中不允许出现两个时钟沿敏感信号。
(5)对同一个信号赋值应出现在一个进程内,不能在多个进程中对同一个信号赋值。
(6)顺序语句,如if
、case
、loop语句
、变量赋值语句
等必须出现在进程或子程序内部,而不能出现在进程和子程序之外。
2.2.1 敏感列表与wait语句
进程语句的执行时机主要通过敏感列表或wait
语句来控制。理解两者的区别和使用方法,对于正确描述硬件行为至关重要。
敏感列表
描述:敏感列表位于process关键字后,括号内列出触发进程执行的信号。当敏感列表中的任意信号发生变化时,进程被触发执行。
特点:
- 自动触发:无需显式的等待条件,信号变化自动触发。
- 与
wait
语句互斥:一个进程内不能同时使用敏感列表和wait
语句。 - 适用场景:适用于简单的时序逻辑和组合逻辑描述。
示例:
process (clk, reset)
begin
if reset = '1' then
Q <= '0';
elsif rising_edge(clk) then
Q <= D;
end if;
end process;
wait
语句
描述:wait
语句用于在进程内部定义等待条件,控制进程的执行时机。可以根据时间、信号变化或特定条件来暂停和继续进程。
特点:
- 灵活控制:可以结合时间、信号变化和条件来控制执行。
- 不需要敏感列表:
wait
语句替代了敏感列表的功能。 - 适用场景:适用于复杂的控制流程和多重触发条件。
语法:
process
begin
-- 顺序语句
wait for 10 ns; -- 等待固定时间
wait until rising_edge(clk); -- 等待时钟上升沿
wait on A, B; -- 等待信号A或B变化
wait; -- 停止进程执行
end process;
示例:
process
begin
wait until rising_edge(clk);
if reset = '1' then
Q <= '0';
else
Q <= D;
end if;
wait;
end process;
区别总结
特性 | 敏感列表 | wait 语句 |
---|---|---|
定义位置 | process 关键字后方的括号内 | 进程内部 |
使用方式 | 列出触发信号 | 使用wait 语句定义触发条件和时序 |
互斥性 | 不能与wait 语句同时使用 | 不能与敏感列表同时使用 |
灵活性 | 相对固定,仅监听列表中的信号变化 | 更加灵活,可结合时间、条件、信号变化 |
可读性 | 简洁,适合简单的触发条件 | 适合复杂的控制流程和多重触发条件 |
选择建议:
- 敏感列表适用于简单且明确的触发条件,如时钟边沿触发的同步逻辑。
wait
语句适用于需要更复杂控制逻辑的场景,如组合逻辑、延时控制等。
2.2.2 进程示例
功能:实现一个简单的有限状态机,控制状态转移和输出信号。
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
entity FSMExample is
Port (
clk : in std_logic;
reset : in std_logic;
start : in std_logic;
done : out std_logic
);
end FSMExample;
architecture Behavioral of FSMExample is
type state_type is (IDLE, ACTIVE, DONE);
signal current_state, next_state : state_type := IDLE;
signal done_reg : std_logic := '0';
begin
-- 状态寄存器
process (clk, reset)
begin
if reset = '1' then
current_state <= IDLE;
elsif rising_edge(clk) then
current_state <= next_state;
end if;
end process;
-- 下一个状态逻辑
process (current_state, start)
begin
case current_state is
when IDLE =>
if start = '1' then
next_state <= ACTIVE;
else
next_state <= IDLE;
end if;
when ACTIVE =>
-- 假设在ACTIVE状态后立即转移到DONE状态
next_state <= DONE;
when DONE =>
if start = '0' then
next_state <= IDLE;
done_reg <= '1'; -- 设置完成信号
else
next_state <= DONE;
done_reg <= '0';
end if;
when others =>
next_state <= IDLE;
end case;
end process;
-- 输出逻辑
done <= done_reg;
end Behavioral;
说明:
- 状态定义:定义了三个状态
IDLE
、ACTIVE
和DONE
。 - 状态寄存器:使用一个进程来更新当前状态,支持同步复位。
- 下一个状态逻辑:根据当前状态和输入信号
start
决定下一个状态,并更新输出信号done_reg
。 - 输出逻辑:将内部寄存器
done_reg
赋值给输出信号done
。
2.3 例化语句
在VHDL程序中可以直接将已经设计好的电路模块,封装为器件,然后在新的设计实体中例化该器件,构成层次化的设计。
器件可以是设计好的VHDL源文件,也可以是用Verilog设计的模块,还可以是IP核等。
例化语句由定义语句和映射语句两部组成,定义语句语法如下:
component 器件名
[generic (
GenericName1 : GenericType := DefaultValue1;
GenericName2 : GenericType := DefaultValue2;
...
)]
port (
PortName1 : PortDirection PortType;
PortName2 : PortDirection PortType;
...
);
end component;
组成部分
component
关键字:标识组件声明的开始。器件名
:组件的名称,用于在实例化时引用。generic
(可选):定义组件的泛型参数,允许在实例化时传递不同的值以改变组件的行为或特性。port
:定义组件的端口,包括端口名称、方向(in
、out
、inout
)和数据类型。end component;
:标识组件声明的结束。
映射语句语法如下:
例化名: 器件名
[generic map (
GenericName1 => GenericValue1,
GenericName2 => GenericValue2,
...
)]
port map (
PortName1 => SignalName1,
PortName2 => SignalName2,
...
);
组成部分
例化名
:实例的标签,用于唯一标识该实例。器件名
:引用组件声明中定义的组件名称。generic map
(可选):为组件的泛型参数提供具体的值。port map
:将组件的端口连接到设计中的信号或其他端口。
2.3.1 端口映射
端口映射用于将组件的端口连接到当前设计中的信号或端口。分为位置映射和命名映射。
-
位置映射:根据端口声明的顺序进行连接。
port map (signal_A,signal_B,signal_Y);
-
命名映射:通过明确指定端口名称进行连接,减少错误。
port map ( A => signal_A, B => signal_B, Y => signal_Y );
2.3.2 示例
采用原件例化方式实现如下电路:
底层与非门设计
library IEEE;
use ieee.std_logic_1164.all;
entity nand_ex is
port(
A:in std_logic;
B:in std_logic;
C:out std_logic
);
end entity nand;
architecture behavior of nand_ex is
begin
C = A nand B;
end behavior;
顶层设计
library IEEE;
use ieee.std_logic_1164.all;
entity top is
port(
A1:in std_logic;
B1:in std_logic;
C1:in std_logic;
out_value:out std_logic;
);
architecture behavior of top is
component nand_ex is
port(
A:in std_logic;
B:in std_logic;
C:out std_logic
);
end component nand_ex;
signal s1,s2: std_logic;
begin
U1:nand_ex port map(A1,B1,S1); --位置映射
U2:nand_ex port map(A=>C1,B=>D1,C=>S2); --命名映射
U3:nand_ex port map(S1,S2,out_value);
end behavior;
end;
2.4 生成语句
生成语句(Generate Statements)用于在architecture
内部根据一定的条件或循环生成对应的代码结构。它可以生成批量的组件实例化、信号赋值、进程或其他并行语句,从而减少重复性代码,提高代码的可读性和可维护性。
使用生成语句可以实现以下目标:
- 批量实例化:在需要多个相同或类似功能的硬件模块时,使用
for ... generate
语句批量生成组件实例或语句。 - 条件生成:在满足特定条件时,才生成相应的硬件结构,不满足条件时则不生成,使用
if ... generate
语句实现。
2.4.1 for … generate 语句
语法:
[标号:] for 循环变量 in 取值范围 generate
begin
并行语句(如组件实例化、信号赋值等)
end generate label;
- 标号:可选,用于标识生成语句或引用它。
- 循环变量:循环变量的值在每次循环时都将发生变化,其取值范围从最左边的值开始递增到取值范围最右边的值;循环变量每取一个值就要执行一次generate语句中的并行处理语句。
- 并行语句部分:可包括组件实例化、信号赋值、进程等。
示例:批量实例化与门
architecture Behavioral of MultiAndGates is
component AndGate
port (
A : in std_logic;
B : in std_logic;
Y : out std_logic
);
end component;
signal A_vec, B_vec : std_logic_vector(3 downto 0);
signal Y_vec : std_logic_vector(3 downto 0);
begin
gen_and_gates: for i in 0 to 3 generate
AndGate_inst: AndGate
port map (
A => A_vec(i),
B => B_vec(i),
Y => Y_vec(i)
);
end generate gen_and_gates;
end Behavioral;
说明:
for i in 0 to 3 generate
语句会生成4个与门实例,与门输入分别是A_vec(i)
和B_vec(i)
,输出为Y_vec(i)
。- 索引
i
从0到3,自动实例化4个功能相同的与门。
2.4.2 if … generate 语句
语法:
[标号:] if 条件表达式 generate
begin
并行语句;
end generate [标号];
或
[标号:] if 条件表达式1 generate
并行语句1;
elsif 条件表达式2 generate
并行语句2;
else
并行语句3;
end generate;
if condition generate
:当condition
为真时,包含或生成对应的并行语句;否则跳过。elsif another_condition generate
(可选):可扩展多个条件判断。else
(可选):当所有条件不满足时,执行默认情况。
示例:反相器
一个顶层模块(TopModule),它可以根据泛型参数INCLUDE_INV
的值,选择性地包含一个反相器(Inverter)组件。如果INCLUDE_INV
为true
,则输出信号Y将是输入信号A的反相;如果INCLUDE_INV
为false,则输出信号Y将直接等于输入信号A。
反相器代码:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
entity Inverter is
Port (
A : in std_logic;
Y : out std_logic
);
end Inverter;
architecture Behavioral of Inverter is
begin
Y <= not A;
end Behavioral;
顶层代码:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
entity TopModule is
generic (
INCLUDE_INV : boolean := true -- 泛型参数,决定是否包含反相器
);
Port (
A : in std_logic;
Y : out std_logic
);
end TopModule;
architecture Behavioral of TopModule is
-- 组件声明
component Inverter
Port (
A : in std_logic;
Y : out std_logic
);
end component;
-- 内部信号,用于连接组件或直接赋值
signal Y_internal : std_logic;
begin
-- 使用 if ... generate 语句条件性地实例化反相器
ConditionalInverter: if INCLUDE_INV generate
Inv_inst: Inverter
port map (
A => A,
Y => Y_internal
);
end generate ConditionalInverter;
-- 如果不包含反相器,直接将 A 赋值给 Y_internal
NoInverter: if not INCLUDE_INV generate
Y_internal <= A;
end generate NoInverter;
-- 将内部信号连接到输出
Y <= Y_internal;
end Behavioral;