课程学习重点提炼
- 书上有的,PPT上有的不做,除了超级核心的
- 非核心的不做
课程中的例子
38译码器
-- 38译码器 有3个使能端 3个输入 8个输出
--库:
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
-----------------------------------------------
--实体
ENTITY DOCODER38 IS
PORT{
A: IN STD_LOGIC_VECTOR(2 DOWNTO 0);
ST,STAL,STBL:IN STD_LOGIC;
Y : OUT STDD_LOGIC_VECTOR(7 DOWNTO 0);
};
END ENTITY DECODR38 ;
---------------------------------------------
--结构体
ARCHITECTURE ARCH_DOCODER38 OF DECODER38 IS
BEGIN
PROCESS(A)
BEGIN
IF ST="1" AND STAL="0" AND STBL= "0" THEN
CASE (A) IS
WHEN "000"=> Y<="11111110";
WHEN "001"=> Y<="11111101";
WHEN "010"=> Y<="11111011";
WHEN "011"=> Y<="11110111";
WHEN "100"=> Y<="11101111";
WHEN "101"=> Y<="11011111";
WHEN "110"=> Y<="10111111";
WHEN (OTHERS)=> Y<="01111111";
END CASE;
ELSE Y<="1111111";
END IF;
END PROCESS;
END ARCHITECTURE ARCH_DOCODER38;
逻辑位移实现38译码器
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.STD_LOGIC_UNSIGNED.ALL;
ENTITY decoder3to8 IS
PORT(input:IN STD-LOGIC-VECTOR(2 DOWNTO 0);
output:OUT BIT VECTOR(7 DOWNTO 0));
END decoder3to8;
ARCHITECTURE behave OF decoder3to8 IS
BEGIN
output <="00000001" SLL CONV_INTEGER(input);--被移位部分是常数
END behave;
2选1多路选择器
ENTITY mux21a IS
PORT(a,b,s:IN BIT;--注意分隔
y:OUT BIT);
END ENTITY mux21a;
ARCHITECTURE bhv OF mux21a IS
BEGIN
PROCESS(a,b,s)
BEGIN
IF (s='1') THEN y<=a;
ELSE y<=b;
END IF ;
END ARCHITECTURE bhv;
4选1多路选择器
4选1 IF实现
半加器
ENTITY H_ADDER IS
PORT{--电路模块端口声明
A : IN STD_LOGIC;
B : IN STD_LOGIC;
SO: IN STD_LOGIC;--结果
CO: IN STD_LOGIC--进位
};
END ENTITY H_ADDER;
ARCHITECTURE FH1 OF H_ADDER IS
BEGIN
SO <= A XOR B;--异或
CO <= A AND B;
END ARCHITECTURE FH1;
2-4线译码器
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.STD_LOGIC_UNSIGNED.ALL:-;
ENTITY decoder Is
PORT(input: IN STD_LOGIC_VECTOR(1 DOWNTO 0);
output: OUT STD_LOGIC-VECTOR(3 DOWNTO 0));
END decoder;
ARCHITECTURE qi OF decoder Is
BEGIN
WITH input SELECT
output<= "1110 "WHEN "00",
"1101 "WHEN "01
"1011 "WHEN "10"
"0111"WHEN "11"
"ZZZZ" WHEN OTHERS;
END qi;
8位奇偶校验器
模板总结
LIBRARY [库名];
USE [包文件].[程序包名].ALL;--常用 STD_LOGIC_1164 ; STD; WORK;
如果使用 package,需要下列内容:
--LIBRARY WORK;
--USE WORK.bag.ALL;
--======================================================
PACKAGE [程序包名] IS
--[数据类型说明];[子程序说明];[常量说明];[元件说明];[文件说明];[别名说明];[属性说明];[属性指定];
CONSTANT n2 : INTEGER ;--定义常数
----------
FUNCTION fun(a,b,c:REAL) --定义函数
RETURN REAL;
----------
COMPONENT co IS --定义元件go
GENERIC(t1,t2:TIME:=2 ns);
PORT(in1,in2);
END COMPONENT CO;
----------
END PACKAGE [程序包名];
--======================================================
ENTITY [实体名称] IS
GENERIC; --说明设计实体和其外部环境通信的对象,只能用整数类型
PORT(
[端口名称]:[端口方向] [类型标识];
);--定义模块端口
BEGIN
实体语句;
END ENTITY [实体名称];
--======================================================
ARCHITECTURE [结构体名称] OF [实体名称] IS
--结构体说明语句区,放在ARCH~BEGIN之间:SINGAL TYPE CONSTANT COMPONENT FUNCTION PROCEDURE
COMPONENT [组件名称]
PORT (
[端口名称]:[端口方向] [类型标识];
[端口名称]:[端口方向] [类型标识]);
END COMPONENT;
BEGIN
--功能描述语句区:begin和end之间 1. 块语句 2. 进程语句 3. 信号赋值语句 4.子程序调用语句 5.元件例化语句
PROCESS(敏感信号量表1,2,...)
VARIABLE [变量名]:[数据类型];--局部变量
BEGIN
if( )then
...
end if;
[块名]: BLOCK(条件)
BEGIN
...;--并发语句组
END BLOCK [块名];
END PROCESS;
--======================================================
---配置
CONFIGURATION [配置名] OF [实体名] IS
FOR 配置说明
END FOR;
END [配置名];
END [结构体名称];
重要知识点
实体( Entity):
用于描述该设计实体与外界的接口信号
端口名:每个外部引脚的名称,在实体中必须是唯一的。
端口模式:用来决定信号的流动方向。有输入(IN)、输出(OUT)、双向( INOUT)和缓冲( BUFFER) 4种类型,其默认(缺省)模式为输入模式。
端口类型:端口名的数据类型。在逻辑电路中一般只用到以下几种: BIT和BIT_ VECTOR、STD_ LOGIC和STD_ LOGIC_ VECTOR。
结构体( Architecture):
描述设计实体的内部结构和外部设计实体端口间的逻辑关系
结构体对基本设计单元的输入/输出关系可以用行为( behavior)描述、数据流( dataflow)描述、结构( structure)描述和混合( mixture)描述4种方式。
配置( Configuration):用于描述实体与结构体之间的连接关系,为大系统设计提供管理与组织。
**程序包( Package)**存放各设计模块都能共享的数据类型、常数和子程序等
库( Library) 存放已经编译的实体、结构体、程序包和配置信息。
顺序语句与并行语句
顺序:赋值语句 转向控制语句 等待语句 子程序调用语句 返回语句 空操作语句 IF_THEN_ELSE
并行:同时执行 :PROCESS
数据类型
BOOLEN(布尔):FALSE TRUE
BIT(位):0 1
BIT_VECTOR(7 downto 0):bit数组
CHARACTER:字符 'a' 'B'
INTEGER:整型 -21473647~+21473647
REAL:实数(浮点数) 仿真器中使用
TRING:字符串
TIME:时间 25 ms 仿真器使用
STD_LOGIC:标准逻辑位
U-未初始化的,X-强未知的,0-强0,1-强1 ,Z-高阻态,W-弱未知的,L-弱0,H-弱1,-忽
STD_LOGIC_VECTOR:标准逻辑矢量 一维数组
UNSIGNED:无符号数据类型 被转化为二进制 最左边是最高位
SINGED:有符号数据类型 最高位是符号位 补码表示 正数的补码 反码是本身,负数为取反加一 0+ 1-
数据对象
CONSTANT : 常量 CONSTANT 常量名:数据类型 = 表达式;
VARIABLE : 变量
SIGNAL 信号 类似于连线 SINGAL 信号名:数据类型:=初始值 --singal s1 : STD_LOGIC_VECTURE(3 DOWNTO 0);
--s1 <='1'&'0'&'1'&'1' 信号赋值
端口语句
PORT(端口名:端口模式 数据类型;
);
端口模式:
IN :输入端口
OUT :输出端口,
INOUT :双向端口,
BUFFER :缓冲端口,其功能与INOUT类似,区别在于当需要输入数据时,只允许内部回读输出的信号,即允许反
馈。BUFFER回读的信号不是由外部输入的,而是由内部产生、向外输出的信号。
赋值语句
变量赋值目标 := 赋值源
信号赋值目标 <= 赋值源
操作符
重要语法
//并行语句
条件赋值语句
赋值目标 <= 表达式 WHEN 赋值条件 ELSE
表达式 WHNE 赋值条件 ELSE
表达式;--注意这里没有ELSE
选择赋值语句
WITH 选择表达式 SELECT
赋值目标信号 <= 表达式 WHEN 选择值,
赋值目标信号 <= 表达式 WHEN 选择值,
赋值目标信号 <= 表达式 WHEN 选择值;
"zzzz" WHEN OTHERS;
---例如
WITH INPUT SELECT
"1101" WHEN "01",
"1011" WHEN "10",
"zzzz" WHEN OTHERS;
//顺序语句
转向控制语句
IF语句、CASE语句、LOOP语句、 NEXT语句、EXIT语句
CASE
CASE [表达式] IS
WHEN [选择值或标识符] => <顺序语句>; -- => 相当于 THEN 的意思,不做别的用途
WHEN [选择值或标识符] => <顺序语句>;
WHEN [选择值或标识符] => <顺序语句>;
WHEN OTHERS =>[顺序语句];
END CASE;
1) 单个普通数值,如5;
2) 数值选择范围,如( 1 TO 3),表示取值1、2或3;
3) 并列数值,如 2|5 ,表示取值为2或者5;
4) 混合方式,以上三种方式的混合。
-------
CASE (SEL) IS
WHEN "00"=> Y<=A;
WHEN OTHERS =>NULL;
END CASE;
IF 顺序赋值语句
IF 条件句 THEN
顺序语句1;
ELSIF 条件句 THEN
顺序语句2:
ELSE ---这句话是关键
顺序语句 n;
END IF;
LOOP
--单个loop
[loop标号] : LOOP
顺序语句;
END LOOP [loop标号];
L2:LOOP
a := a+1;
EXIT L2 WHEN a>10;
END loop L2;
--FOR_LOOP 变量在循环体中不断改变值
[循环标号] : FOR [循环变量] IN [范围] LOOP
顺序处理语句;
END LOOP [循环标号];
--WHILE循环 先判断条件
[循环标号] : WHILE [条件] LOOP
顺序处理语句;
END LOOP [循环标号];
VHDL例化语句
元件例化语句由两部分组成:
前部分 把一个现成的设计实体定义为一个元件;
后部分 是该元件与当前设计实体的连接说明。
在语句中**可插入类属语句(GENERIC)**和端口语句(PORT), 分别用于元件参数的代入、赋值和元件的输入、输出端 口信号的规定。
元件定义语句
--元件声明部分 相当于元器件的对外格式
COMPONENT 元件名 IS
GENERIC(类属表);
PORT(端口名表);
END component 文件名;
-- 元件例化部分,端口映射语句用于说明当前设计实体和被调用元件的连接关系
--组件端口名与实体端口名的两种关联方式:位置关联和名字关联
[元件标题] : [元件名称] PORT MAP([被调用组件端口名] => 连接实体端口名,[被调用组件端口名]=> 连接实体端口名,…)
u1 : h_adder PORT MAP(A => ain ,B=>bin,CO=>net2,SO=>net1);--名字关联
u2 : h_adder PORT MAP(net1 , cin , net3 , sum);--位置关联
u3 : or2a PORT MAP(a => net2 ,b=>net3,C=>cout );--名字关联
==重点代码==
---构建 与非门 实体
LIBRARY IEEE;
USE IEEE.STD_LOCIC_1164.ALL;
ENTITY nand2 IS
PORT (a,b : IN STD_LOGTC;
c : OUT STD_LOGIC);
END nand2;
ARCHITECTURE two OF nand2 IS
BEGIN
c < = a NAND b;
END ARCHITECTURE two;
--利用元件例化语句描述 4选1
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
ENTITY or4 IS
PORT (a1 , b1 , c1, d1 : IN STD_LOGIC;--4个输入
z1 : OUT STD_LOGIC );--1个输出
END or4;
ARCHITECTURE behave OF or4 IS
COMPONENT nand2
PORT ( a,b :IN STD_LOGIC;
c : OUT STD_LOCIC );--声明元器件的端口,参考元器件定义声明,上文
END COMPONENT;
SIGNAL s1,s2 : STD_LOGIC;--将2两个与非门结果与第三个与非门连接起来
BEGIN -- a b c
U1 : nan2 PORT MAP ( a1 ,b1, s1 ); --第一个与非门采用 位置关联方式
U2 :nan2 PORT MAP ( a => c1 , c = > s2, b = > d1) ;I --名字关联方式
U3 : nand2 PORT MAP ( sl,s2,c = > z1) ; --第三个与非门 混合关联方式
END ARCHITECTURE behave;
详细笔记一
一、VHDL简介
1.1 VHDL 的历史
VHDL 的 英 文 全 名 是 Very-High-Speed Integrated Circuit Hardware DescriptionLanguage,诞生于 1982 年。
1987 年底,VHDL 被 IEEE 和美国国防部确认为标准硬件描述语言。自 IEEE 公布了 VHDL 的标准版本 IEEE-1076(简称 87 版)之后,各 EDA 公司相继推出了自己的 VHDL 设计环境,或宣布自己的设计工具可以提供 VHDL 接口。此后 VHDL 在电子设计领域逐步取代了原有的各种非标准硬件描述语言。
1993 年,IEEE 对 VHDL 进行了修订,从更高的抽象层次和系统描述能力上扩展 VHDL 的内容,并公布了新版本的 VHDL,即 IEEE 标准的 1076-1993版本(简称 93 版)。
现在,VHDL 和 Verilog HDL 作为 IEEE 的工业标准硬件描述语言,在电子工程领域已成为事实上的通用硬件描述语言。
1.2 VHDL 的特点
VHDL 主要用于描述数字系统的结构、行为、功能和接口。除了含有许多具有硬件特征的语句外,VHDL 在语言形式、描述风格和句法上与一般的计算机高级语言十分相似。VHDL 的程序结构特点是将一项工程设计,或称设计实体(可以是一个元件、一个电路模块或一个系统)分成外部和内部两部分。
外部也可称为可视部分,它描述了此模块的端口,而内部可称为不可视部分,它涉及到实体的功能实现和算法完成。在对一个设计实体定义了外部端口后,一旦其内部开发完成,其他的设计就可以直接调用这个实体。这种将设计实体分成内外部分的概念是VHDL 系统设计的基本点。
应用 VHDL 进行工程设计有以下的优点:
1.行为描述
与其他的硬件描述语言相比,VHDL 具有更强的行为描述能力,强大的行为描述能力是避开具体的器件结构,从逻辑行为上描述和设计大规模电子系统的重要保证。
2.仿真模拟
VHDL 丰富的仿真语句和库函数,使得在任何系统的设计早期就能查验设计系统的功能可行性,随时可对设计进行仿真模拟。
3.大规模设计
一些大型的 FPGA 设计项目必须有多人甚至多个开发组共同并行工作才能实现。VHDL 语句的行为描述能力和程序结构决定了它具有支持大规模设计的分解和已有设计的再利用功能。
4.门级网表
对于用 VHDL 完成的一个确定的设计,可以利用 EDA 工具进行逻辑综合和优化,并自动把VHDL 描述设计转变成门级网表。
5.独立性
VHDL 对设计的描述具有相对独立性,设计者可以不懂硬件的结构,也不必对最终设计实现的目标器件有很深入地了解。
二、VHDL 程序基本结构
一般的 VHDL 程序可以由**实体(Entity)、结构体(Architecture)、配置(Configuration)、程序包和程序包体(Package)以及库(Library)**5 个部分组成,它们是 VHDL 程序的设计单元。
其中实体、配置和程序包属于初级设计单元,主要的功能是进行端口、行为、函数等的定义。结构体和程序包体是次级设计单元,包含了所有行为以及函数的实现代码。其中,程序包和程序包体又属于公用设计单元,即它们是被其他程序模块调用的。库则是一批程序包的集合。
图 1 所示为 VHDL 程序设计单元之间的关系。
图 1 VHDL 程序设计单元关系图
无论是复杂的还是简单的数字模块,用 VHDL 来描述都至少需要包括两个部分,即实体申明(Entity Declaration)和结构体(Architecture)。其中实体申明用于说明模块的端口,而结构体用于描述模块的功能。本节下面将详细介绍 VHDL 程序的各个设计单元。
2.1 实体的申明方法
实体是设计的基本模块和设计的初级单元,在分层次设计中,顶层有顶级实体,含在顶级实体中的较低层次的描述为低级实体,通过配置可把顶层实体和底层实体连接起来。可以将实体理解为电路图设计中的芯片符号(Symbol),符号规定了电路的符号名、接口和数据类型。由连线(或信号)将符号互连建立设计所需的电路图,互连线生成的网表,在设计实现之前一直是设计验证的仿真模型,并在设计验证后,由网表向布线工具提供所需的连接信息和层信息。
图 2 所示是传统设计中 R-S 触发器的符号图,用 VHDL 对其进行描述的代码如下:
ENTITY rsff IS
PORT (
Set, Reset : IN BIT;
Q, QB : BUFFER BIT );
END rsff;
图 2 R-S 触发器的 VHDL 实体描述和符号
补充: S:set 置位端(置位后,Q为1) R:reset 复位端, 低电平有效(复位后,Q为0)
Q^n+1:当前状态的下一工作周期的状态
Q^n:当前状态
![]()
![]()
实体语句用关键词 ENTITY 开头,实体名 rsff 是描述的符号名,在结束实体语句的 END rsff之间,实体语句可以用关键词 BEGIN 把实体语句分成两部分:即 BEGIN 之前是实体说明,BEGIN之后是实体语句。
在 ENTITY 语句的实体说明部分,常用 PORT 付语描述实体对外界连接的端口(数目、方向和数据类型)。实体 rsff 有 4 个端口,Set/Reset 是输入 IN 模式,Q/QB 是输出BUFFER(缓冲)模式,都为 BIT 类型。实体说明中还可说明数据类型、子程序和常量等数据信息,实体语句常用于描述设计经常用到的判断和检查信息。
实体描述的格式如下:
ENTITY 实体名 IS
[GENERIC(参数表);]
[PORT(端口表);]
[BEGIN
实体语句部分;]
END [ENTITY] [实体名];
其中,GENERIC 是用于说明设计实体和其外部环境通信的对象,规定端口的大小、实体中子元件的数目、实体的延时特性等。只能用整数类型表示,如整型、时间型等,其他类型的数据不能逻辑综合。格式如下:
GENERIC ([CONSTANT]属性名称:[IN]子类型标识[:=静态表达式],……);
PORT 关键字用于定义模块的端口,它的格式如下:
PORT( [SIGNAL] 端口名称:[方向]类型标识[BUS] [:=静态表达式],
[SIGNAL] 端口名称:[方向] 类型标识[BUS] [:=静态表达式],
…
[SIGNAL] 端口名称:[方向] 类型标识[BUS] [:=静态表达式]);
• SIGNAL:SIGNAL 是关键字,但是由于 PORT 之后必须是信号类,所以一般可以将 SIGNAL关键字省略。
• 端口名称:是该端口的标识,通常由英文字母和数字组成,但是必须是英文字母打头。
• 方向:定义了端口是输入还是输出,如 IN、OUT。表明端口方向的关键字如表1所示。
• 类型标识:说明流过该端口的数据类型,常用的数据类型有 BIT(位)、BIT_VECTOR(位向量)、BOOLEAN(布尔型)和 INTEGER(整数型)4 种。
• BUS 关键字:在该端口和多个输出端相连的情况下使用。
表 1 端口方向关键字说明表
2.2 结构体的描述方法
结构体描述实体的行为功能,一个实体可以有多个结构体。结构体是一个基本设计单元,它具体地指明了所设计模块的行为、元件及内部的连接关系,也就是说它定义了设计单元具体的功能。结构体对其基本设计单元的输入/输出关系可以用 3 种方式进行描述,即行为描述(基本设计单元的数学模型描述)、寄存器传输描述(数据流描述)和结构描述(逻辑元件连接描述)。
不同的描述方式只体现在描述语句上,而结构体的结构是完全一样的。由于结构体是对实体功能的具体描述,因此它一定要跟在实体的后面。通常,先编译实体之后才能对结构体进行编译。如果实体需要重新编译,那么相应结构体也应重新进行编译。
结构体的格式如下:
ARCHITECTURE 结构体名 OF 实体名 IS
[定义语句]
BEGIN
[并行处理语句]
END 结构体名;
定义语句用于对结构体内部所使用的信号、常数、数据类型和函数等进行定义。信号定义和端口说明的语句一样,应有信号名和数据类型的说明,但因它是内部连接用的信号,故没有也不需有方向的说明。并行处理语句具体地描述了结构体的行为及其连接关系,它们都是可以并行执行的。
以上面介绍的 R-S 触发器为例。假设已经有一个实现了与非功能的模块 nand2,用它实现R-S 触发器的原理图如图 3 所示。
图 3 R-S 触发器的实现原理图
对应以上原理图的结构体描述如下:
library IEEE;
use IEEE.std_logic_1164.all;
ENTITY rsff is
PORT (
set, reset: in bit;
q, qb: buffer
);
END ENTITY;
ARCHITECTURE arch_rsff OF rsff IS
COMPONENT nand2
PORT (
a, b : IN BIT;
c: OUT BIT);
END COMPONENT;
BEGIN
U1: nand2
PORT MAP (set, qb, q);
U2: nand2
PORT MAP (reset, q, qb);
END arch_rsff;
上面的代码中,以关键字 ARCHITECTURE 作为结构体的开头,结构体名为 arch_rsff,表示描述 rsff 实体的结构体 arch_rsff。ARCHITECTURE 和 BEGIN 之间是结构体说明区, BEGIN和 END 之间是结构体语句区。结构体说明区描述组件(COMPONENT)和局部信号,结构体语句中用的具体元件(上例是 nand2)均应在结构体说明中说明接口,以便将描述的信息通知给编辑器。
如果设计者希望将模块分为若干个相对比较独立的子模块进行描述,可以将一个结构体用几个子结构来构成。VHDL 结构体描述常常用到 3 种语句结构:PROCESS 语句结构、BLOCK 语句结构和子程序结构。
1).PROCESS 语句结构
进程语句是一种并发处理语句,在一个结构体中多个 PROCESS 语句可以同时并行运行(相当于多个 CPU 同时运作)。PROCESS 语句是 VHDL 语言中描述硬件系统并发行为的最基本语句。
PROCESS 语句归纳起来有如下几个特点:
• 它可以与其他进程并发运行,并可存取结构体或实体号中所定义的信号;
• 进程结构中的所有语句都是按顺序执行的;
• 为启动进程,在进行结构中必须包含一个显式的敏感信号量表或包含一个 WAIT 语句;
• 进程之间的通信是通过信号量传递来实现的。
PROCESS 语句的格式如下:
[进程名]:PROCESS(信号 1,信号 2,…)
BEGIN
…
END PROCESS;
一般情况下进程名可以被省略。进程申明关键字 PROCESS 后面括号内的信号是此进程的敏感信号,这些信号的变化会激活过程的执行。例如下面的代码就表示过程 main_proc 在信号clk 和 reset 变化时执行:
library IEEE;
use IEEE.std_logic_1164.all;
ENTITY counter is
PORT (
clk, reset: in bit;
c: out bit
);
END ENTITY;
ARCHITECTURE arch of counter is
BEGIN
main_proc:
PROCESS(clk, reset)
BEGIN
if (reset = '1') then
…
end if;
END PROCESS;
END
在进程中也可以定义一些变量,这些变量是局部量,只能在进程内部使用,它们的赋值是立即生效的。局部变量定义的格式如下:
VARIABLE 变量名:数据类型 [约束条件] [:=表达式];
下面的代码演示了定义一个局部变量并且使用它的方法:
library IEEE;
use IEEE.std_logic_1164.all;
ENTITY counter is
PORT (
a: in bit;
c: out bit
);
END ENTITY;
ARCHITECTURE arch of counter is
BEGIN
main_proc:
PROCESS(a)
VARIABLE item:array0(7 downto 0); --定义一个变量 item
BEGIN
item(7):=a;
c<=item(7);
END PROCESS;
END
2).BLOCK 语句结构
BLOCK 语句的格式如下:
块名:BLOCK(条件)
[参数 GENERIC 说明; [参数映射;] ]
[端口说明; [端口映射;] ]
[块说明语句]
BEGIN
并发语句组;
END BLOCK 块名;
BLOCK 放在结构体的并行语句组中,每一个 BLOCK 相当于一个子电路原理图。和 PROCESS语句不同,BLOCK 内的语句是并发执行的。只要 BLOCK 右边的条件满足,BLOCK 内的语句就被执行。如果省略条件,表示本 BLOCK 被无条件执行。下面是一个 BLOCK 语句的例子:
library IEEE;
use IEEE.std_logic_1164.all;
ENTITY test is
PORT (
a, b: in bit;
s, c: out bit
);
END ENTITY;
ARCHITECTURE arch of test is
BEGIN
block_demo: BLOCK(clk='1')
BEGIN
s<=a xor b;
c<=a and b;
END BLOCK block_demo;
END;
上面的程序表示当 clk 信号变为 1 时,并行执行 BLOCK 语句内的程序,即将 a 和 b 两个信号的异或结果赋给 s 信号,同时将 a 和 b 信号的与结果赋给 c 信号。
3).子程序结构
所谓子程序结构就是将一部分实现代码放到公用的程序(即程序包 Package)文件中实现。程序包中的代码以子程序的方式提供给 VHDL 程序调用,这样代码可以实现共享,同时还使得VHDL 程序的结构明了。
子程序在调用时首先要进行初始化,执行结束后子程序就终止,再调用时要再进行初始化。因此子程序内部的值不能保持,子程序返回以后才能被再调用,它是一个非重入的程序。
VHDL 中有两种类型的子程序—过程(Procedure)和函数(Function),下面分别介绍一下它们的格式。
• 过程过程的格式如下:
PROCEDURE 过程名(参数 1;参数 2;……) IS
定义语句;
BEGIN
顺序语句组;
END 过程名;
每个参数的说明格式如下:
参数名:方向 类型
方向一般为 3 种:IN、OUT、INOUT。如果方向为 IN 则可省略方向说明。
• 函数
函数的格式如下:
FUNCTION 函数名(参数 1;参数 2;……) RETURN 数据类型 IS
定义语句;
BEGIN
顺序语句组;
RETURN [返回变量名];
END 函数名;
在 VHDL 语言中,函数的所有参数都是输入参数,因此都是 IN 的方向,可以省略方向说明。
在下面介绍程序包(PACKAGE)的时候将会介绍函数、过程定义的例子,在此不再举例说明。
2.3 程序包和程序包体
程序包说明类似 C 语言中的 include 语句,用来罗列 VHDL 语言中所要用到的信号定义、常数定义、数据类型、元件语句、函数定义和过程定义等,它是一个可编译的设计单元,也是库结构中的一个层次。
程序包的结构如图 4 所示。
图 4 VHDL 程序包结构图
一个程序包由两大部分组成:程序包申明和程序包体。程序包体是一个可选项,也就是说,程序包可以仅仅由程序包标题构成。一般程序包标题列出所有项的名称,而程序包体具体给出各项的细节。
下面介绍一个包含与非函数的程序包的实现以及调用方法。实现与非函数程序包的代码如下:
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
-- 程序包申明
PACKAGE package_demo is
-- 函数申明
FUNCTION nand2(a, b : in bit)
RETURN bit;
END package_demo;
-- 程序包体
PACKAGE BODY package_demo is
-- 函数实现
FUNCTION nand2(a, b : in bit)
RETURN bit IS
VARIABLE ret: bit;
BEGIN
ret = not(a and b);
return ret;
END nand2;
END BODY;
上面的代码在程序包申明中申明了函数 nand2,然后在程序包体中具体实现了此函数的功能。下面举个例子来说明程序包的使用方法,即函数的调用方法。假设要得到如下的逻辑关系式:
可以用下面的代码描述:
library IEEE;
use IEEE.std_logic_1164.all;
use WORK.package_demo.all; -- 调用自定义的程序包
ENTITY test is
PORT (
A1, B1, A2, B2: in bit;
D: out bit
);
END ENTITY;
ARCHITECTURE arch of test is
BEGIN
-- 调用 nand 函数
D = nand2(A1, B1) and nand2(A2, B2);
END;
自定义的程序包必须首先进行编译,然后才能够编译调用此程序包的 VHDL 程序。自定义的程序包属于 WORK 库,所以申明调用的代码是:
use WORK.自定义程序包名称.all;
调用程序包中函数或者过程的方法和一般高级语言(如 C 语言)一样直接调用就可以了。
2.4 配置的申明方法
一个实体可以包含多个结构体,配置的作用就是根据需要选择实体的结构体。配置语句描述层与层之间的连接关系以及实体与结构之间的连接关系。设计者可以利用这种配置语句来选择不同的结构体,使其与要设计的实体相对应。在仿真某一个实体时,可以利用配置来选择不同的结构体,进行性能对比试验以得到性能最佳的结构体。
例如,设计一个二输入、四输出的译码器。如果一种结构中的基本单元采用反相器和三输入与门,而另一种结构中的基本元件都采用与非门。它们各自的结构体是不一样的,并且都放在各自不同的库中。那么现在要设计的译码器,就可以利用配置语句实现对两种不同构造的选择。
配置的基本格式如下:
CONFIGURATION 配置名 OF 实体名 IS
[语句说明]
END 配置名;
如果一个实体仅仅具有一个结构体,也需要定义其配置,但是可以写成一种最为简洁的格式:
CONFIGURATION 配置名 OF 实体名 IS
FOR 所选的构造体名
END FOR;
END 配置名;
如果一个模块比较复杂,含有多个子模块,使用低层次配置可以为每个子模块选择其结构体,代码如下:
CONFIGURATION 配置名 OF 实体名 IS
FOR 所选的构造体名
FOR 标号 1:元件名 1 USE CONFIGURATION WORK.配置体名 1;
END FOR;
FOR 标号 2:元件名 2 USE ENTITY WORK.实体名 2(构造体名 2);
END FOR;
…
END FOR;
END 配置名;
其中,低层次构造体名可用 ALL 或 OTHERS,构造体名 2 可以省略。
2.5 VHDL 程序的库
库(Library)是经编译后的数据的集合,它存放包集合申明、实体申明、构造体申明和配置定义。它的功能类似于 UNIX 和 MS-DOS 操作系统中的目录,在 VHDL 中,库的说明总是放在设计单元的最前面,这样在设计单元内的语句就可以使用库中的数据了。由此可见,使用库的好处是使设计者可以共享已经编译过的设计结果。在 VHDL 中可以存在多个不同的库,但是库和库之间是独立的,不能互相嵌套。
申明库的格式如下:
LIBRARY 库名;
在 VHDL 语言中存在的库大致可以归纳为 5 种:IEEE 库、STD 库、ASIC 矢量库、用户定义库和 WORK 库。
1).IEEE 库
在 IEEE 库中的“STD_LOGIC_1164”包集合是 IEEE 正式认可的标准包集合。现在有些公司提供的包集合如“STD_LOGIC_ARITH”、“STD_LOGIC_UNSIGNED”等,尽管没有得到 IEEE 的承认,但是仍汇集在 IEEE 库中。
2).STD 库
STD 库是 VHDL 的标准库,在库中存放有“STANDARD”包集合。由于它是 VHDL 的标准配置,因此设计者如要调用“STANDARD”中的数据可以不按标准格式说明。STD 库中还包含有“TEXTIO”包集合,在测试时使用。使用“TEXTIO”包集合中的数据时,应先说明库和包集合名:
LIBRARY STD;
USE STD.TEXTIO.ALL;
3).ASIC 矢量库
在 VHDL 中,为了进行门级仿真,各公司可提供面向 ASIC 的逻辑门库。在该库中存放着与逻辑门一一对应的实体。
4).WORK 库
WORK 库是现行作业库。设计者所描述的 VHDL 语句不需要任何说明,都将存放在 WORK 库中。在使用该库时无需进行任何说明。
5).用户定义库
将用户自身设计开发的包、实体等汇集在一起定义成一个库,就是用户定义库或称用户库。在使用用户定义库时同样要首先说明库名。
以上各种库中,除 WORK 库外,其他 4 类库在使用前都首先要进行说明,格式为:
USE 库名.包集合名.项目名;
如果项目名为 ALL,则表示包集合中的所有项目都要使用,例如:
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.ALL;
USE WORK.STD_ARITH.ALL;
…
库说明语句的作用范围从一个实体说明开始到它所属的构造体、配置结束为止。当一个源程序出现两个或两个以上的实体时,两条作为使用库的说明语句就在每个实体说明语句前重复书写。例如,在一个 VHDL 文件中定义两个实体,库的申明如下:
-- 第一个实体的库申明
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1644.ALL;
-- 第一个实体申明
ENTITY ent1 is
…
END ent1;
-- 第一个实体的结构体
ARCHITECTURE arch1 of ent1 is
…
END arch1;
-- 第一个实体的配置
CONFIGURATION cfg1 of ent1 is
…
END cfg1;
-- 第二个实体的库申明
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1644.ALL;
-- 第二个实体申明
ENTITY ent2 is
…
END ent2;
-- 第二个实体的结构体
ARCHITECTURE arch2 of ent2 is
三、VHDL 语言的数据类型和运算符
VHDL 和其他高级语言一样,具有多种数据类型。对大多数数据类型的定义两者是一致的(例如整数型),但是也有一些数据类型是 VHDL 所独有的。表 2 所示为 VHDL 支持的数据类型和它的数据对象。
表 2 VHDL 数据类型和数据对象表
注意:表 2 中带*号的数据类型表示不可以综合的类型或对象。
3.1 VHDL 语言的数据对象
VHDL 对象有 4 种,即信号(Signal)、变量(Variable)、常量(Constant)和文件(File)。
其中文件(File)是 VHDL’93 标准中新通过的,它是不可以综合的。下面介绍一下常量、信号和变量的申明方法。
1).信号(Signal)
信号用于将元件的装配端口连在一起形成模块,它的作用相当于连接元件的导线。信号是实体间动态数据交换的手段,信号申明格式如下:
SIGNAL signal_name : signal_type [ :=initial_value ] ;
在关键字 SIGNAL 后跟一个或者多个信号名,每个信号名将建立一个新信号,用冒号把信号名和信号的数据类型分隔开,信号数据类型规定信号包含的数据类型信息及初始化信号指定的初值。
实体说明部分、结构体说明和程序包说明都能声明信号,全局信号在程序包中声明,它们被所属的实体分享。
2).变量(Variable)
变量用于存储进程和子程序中的局部数据,变量的赋值是立即执行的,没有延时。变量的申明格式如下:
VARIABLE variable_name ,variable_name : variable_type[:= value];
关键字 VARIABLE 后跟着一个或多个变量名,每个变量名对应建立一个新变量。variable_type 字段定义了变量的数据类型,并且还可以指定一个可选的初值。此外,还需要注意的是只可以在进程说明部分和子程序说明部分声明变量。
和信号相比,变量有以下优点:
• 变量处理起来更快,因为变量赋值是立即发生的,而信号却必须为此事件作相应的处理。
• 变量用很少的存储器,相反为了做一个调度安排和处理信号属性,需要存储更多的信号信息。
• 变量比信号更容易实现同步处理。
3).常量
常量是为特定的数据类型值所赋予的名称,如果需要在多个具体元件中存放一个固定值就使用常量。例如可以如下定义常量 PI(π ):
CONSTANT PI: REAL:= 3.1416;
定义常量的格式如下:
CONSTANT constant_name,constant_name: type_name[:= value];
一般情况下,VHDL 中的常量是在程序包申明中进行申明,而在程序包体中指定具体的值。使用常量需要注意以下几个问题:
• 在程序包中说明的常量被全局化。
• 在实体说明部分的常量被那个实体中任何结构体引用。
• 在结构体中的常量能被其结构体内部任何语句采用,包括为进程语句采用。
• 在进程说明中说明的常量只能在进程中使用。
• 在数组和一些线性运算中经常用常量表,VHDL 的设计描述用常量表特别适于实现 ROM 网络的电路与函数设计。
3.2 VHDL 语言的数据类型
VHDL 的数据类型根据使用目的和场合,可以分为标准数据类型和用户定义的数据类型两种。
1).标准数据类型
VHDL 中定义的标准数据类型如表 3 所示。
==表 3 VHDL 中的标准数据类型==
其中,在数据类型后面,可以加上约束区间,比如:
INTEGER RANGE 100 downto 1;
BIT_VECTOR(3 downto 1) real range 2.0 to 30.0;
STD_LOGIC 和 STD_LOGIC_VECTOR 的逻辑数据取值可以有 9 种状态,如表 4 所示。
表 4 标准逻辑(向量)取值表
2).用户定义的数据类型
用户定义数据类型的格式如下:
TYPE 数据类型名 {,数据类型名} 数据类型定义;
数据类型定义放在语句的定义部分中,定义范围为从本定义行开始到本语句作用的最后。
一般用户定义的数据类型分为以下几种。
• 枚举类型(enumeration)
枚举类型的格式如下:
TYPE 数据类型名 IS (元素,元素,……)
例如,将一星期七天作为一个枚举,可以如下定义:
TYPE week IS (sun, mon, tue, wed, thu, fri, sat);
在枚举类型中,元素是有序列性的,第 1 个元素对应逻辑电路状态 000,第 2 个为状态 001,第 3 个为状态 010……后一个逻辑状态为前一个元素逻辑状态加 1。所以,上面的例子中,sun对应逻辑状态 000,mon 对应逻辑状态 001,……,sat 对应逻辑状态 110。
• 整数类型、实数类型(INTEGER,REAL)
这里的整数类型和实数类型其实是前面所述的标准整数类型和实数类型的子类,定义的格式如下:
TYPE 数据类型名 IS 数据类型定义 约束范围
例如:
TYPE current IS REAL RANGE -1E4 TO 1E4
定义了 current 类型实数的范围是-104到 104。
• 数组(ARRAY)
数组定义的格式如下:
TYPE 数据类型名 IS ARRAY 范围 OF 原数据类型名;
**注意:**如果 范围 这一项没有被指定,则使用整数数据类型。
下面通过例子说明数组的定义方法。
TYPE word IS ARRAY (1 TO 8) OF STD_LOGIC;
TYPE tmem IS ARRAY (0 TO 2, 3 DOWNTO 0) OF STD_LOGIC;
以上定义了 tmem 一种数组类型,可以定义一个此类新的常数,如下:
CONSTANT mem:tmem:= ( ('0', '0', '0', '0'),
('0', '0', '1', '0'),
('1', '1', '0', '0'));
当范围这一项需用整数类型以外的其他数据类型时(如枚举类型),则应在指定数据范围前加数据类型名。例如:
TYPE week IS (sun, mon, tue, wed, thu, fri, sat);
TYPE workdate IS ARRAY (week mon TO fri) OF STD_LOGIC;
如果要取得数组内的一个元素,格式如下:
数组名(下标)
例如,word(1)区的 word 数组序号为 1 的元素。
当数组类型定义中的范围用“(Natural Range <>)”或“(Positive Range <>)”代替时,表示本数组类型为非限定的类型,下标范围在信号或变量定义时再具体指定。
例如下面的代码中定义的 array0 就是非限定类型的数组。
library IEEE;
use IEEE.std_logic_1164.all;
entity tmyarray is
port(a:in std_logic;c:out std_logic);
end ;
architecture myarray of tmyarray is
type array0 is array (natural range <>) of std_logic;
begin
process(a)
variable item:array0(7 downto 0);--定义一个变量 item
begin
item(7):=a;
c<=item(7);
end process;
end;
• 时间(TIME)
定义时间的格式如下:
TYPE 数据类型名 IS 范围
UNITS 基本单位;
单位描述;
END UNITS
例如:
TYPE 数据类型名 IS 范围
UNITS 基本单位;
单位描述;
END UNITS
TYPE time IS RANGE -1E18 TO 1E18
UNITS fs;
ps=1000fs;
ns=1000ps;
us=1000ns;
ms=1000us;
sec=1000ms;
min=60sec;
hr=60min;
END UNITS;
• 记录(Record)
记录的定义格式是:
TYPE 数据类型名 IS RECORD
元素名:数据类型名;
元素名:数据类型名;
…
元素名:数据类型名;
END RECORD;
**注意:**引用记录数据类型中的元素应使用“.”,而不是数组的括号。
例如,定义一个窗口尺寸的记录,如下:
TYPE window IS RECORD
length:INTEGER;
width:INTEGER;
END RECORD;
当需要使用 window 类型记录的元素时,方法如下:
signal win: window;
win.length<=10;
• 用户定义的子类型
用户定义的子类型是用户对已定义的数据类型做一些范围限制而形成的一种数据类型。子类型的名称通常采用用户较容易理解的名字。子类型的定义格式为:
SUBTYPE 子类型名 IS 数据类型名[范围];
例如:
SUBTYPE digit IS INTEGER RANGE 0 TO 9;
SUBTYPE abus IS STD_LOGIC_VECTOR(7 DOWNTO 0);
signal a: STD_LOGIC_VECTOR (7 downto 0);
signal b: STD_LOGIC_VECTOR (15 downto 0);
signal c: abus;
a<=c; --正确
b<=c; --错误
在 VHDL 中,数据类型的定义是相当严格的,不同类型的数据是不能进行运算和直接代入的。为了实现正确的代入操作,必须将要代入的数据进行类型变换。
变 换 函 数 通 常 由 VHDL 语 言 的 包 集 合 提 供 。 例 如 在 “ STD_LOGIC_1164 ”、“STD_LOGIC_ARITH”、STD_LOGIC_UNSIGNED”的包集合中提供了如表 2-5 所示的数据类型变换函数。
表 5 类型变换函数表
有些数据,从数据本身是断定不出其类型的,如“01010001”,如果没有上下文,VHDL 编译器就无法知道它是字串型还是位数组类型。这时就要进行数据类型的限定。类型限定的格式如下:
类型名'(数据)
例如:
a<=std_logic_vector'("01010001");
这样,编译器知道“01010001”肯定是矢量型,而不是别的类型。
3.3 VHDL 语言的运算符
在 VHDL 语言中,常用的运算符有逻辑运算(Logic)、关系运算(Relational)、算术运算(Arithmetic)和移位运算(Shift),下面分别对它们进行介绍。
1).逻辑运算符
逻辑运算符可以对 bit 和 boolean 类型的值进行运算,也可对这些类型的一维数组进行运算。对数组型的运算,运算施加于数组中的每个元素,结果与原来数组长度相同。
逻辑判断的运算为“短路运算”,也就是说,条件表达式的左边成立时,就不再进行右边的判断。比如,IF (a=0) AND (b/a>2) THEN…这个判断运算,当 a=0 时,后面的判断不再继续,避免出现除数为 0 的运算。
VHDL 的逻辑运算符如表 6 所示。
表 6 VHDL 逻辑运算符
2).关系运算符
关系运算符两边必须为相同的类型,其结果为 boolean 类型。
等号(=)和不等号(/=)两边可以为任意类型的运算对象。其他关系运算符的运算对象必须为标量类型或离散类型的一维数组。对于复杂的运算对象,如数组,两个值相等意味着两个值的所有对应元素相等。VHDL 的关系运算符如表 7 所示。
表 7 VHDL 关系运算符
3).算术运算符
算术运算符包括一些基本的算术运算,使用算术运算符需要注意的是乘方(**)运算的右边必须为整数。VHDL 的算术运算符如表 8 所示。
表 8 VHDL 算术运算符
4).移位运算符
移位运算符为二元运算符,左边必须为一维数组,且元素类型为 bit 或 boolean 类型。右边运算数为整数,可以为负数,相当于反方向移位。一位移位与循环移位的语义示意如图 5 所示。
图 5 一位移位与循环移位示意图
逻辑左移时,最高位丢失,最低位补0;
逻辑右移时,最高位补0,最低位丢失;
算术左移时,依次左移一位,尾部补0,最高的符号位保持不变。
算术右移时,依次右移一位,尾部丢失,符号位右移后,原位置上复制一个符号位;
循环左移时,将最高位重新放置最低位
循环右移时,将最低位重新放置最高位
使用循环操作的一个目的就是把每一位连续放到最左边,以判定数据的符号。如果要把一个数字的高位部分和低位部分互换,采用一位操作就很方便,速度很快。
1010100010101
逻辑左移一位结果为 0101000101010
逻辑右移一位结果为 0101010001010
算术左移一位结果为 1101000101010
算术右移一位结果为 1101010001010
循环左移一位结果为 0101000101011
循环右移一位结果为 1101010001010
VHDL 的移位运算符如表 9 所示。
表 9 VHDL 移位运算符
除了上面介绍的,VHDL 中运算符还包括正号“+”、负号“-” 以及“&”。其中,连接符号(&)用于一维数组,这个数组的元素个数可以为 1,运算结果为右边数组连接在左边数组之后形成新数组,例如:
sel <= a & b;
假设 a 信号为“0”,b 信号为“1”,那么得到的 sel 信号就是“01”。
所有的 VHDL 运算符之间都有优先级的关系,各运算符优先级从最高到最低,顺序如表 10 所示(同一行优先级相同)。
表 10 运算符的优先顺序
四、 VHDL 语言的描述语句
使用 VHDL 进行数字电路描述时候,如果按照执行顺序对 VHDL 的程序进行分类,可以分为顺序(sequential)描述语句和并行(concurrent)描述语句。顺序语句描述的程序总是按照程序书写的顺序执行;而并行语句都是同时执行的,和程序的书写顺序无关。
4.1 VHDL 顺序语句描述方法
VHDL 中的顺序语句一般在进程中出现,或者以函数、过程的方式在进程中被调用。顺序语句所涉及到的系统行为有时序流、控制、条件和迭代等。
VHDL 中的顺序语句有 WAIT 语句、断言语句、IF 语句、CASE 语句、LOOP 语句、NEXT 语句、过程调用语句和 NULL 语句,下面就对它们进行详细介绍。
1).WAIT 语句
WAIT 语句允许把一个顺序执行的进程或子程序挂起,挂起的进程或子程序恢复的条件由 3种不同的方法指定。WAIT 语句可以有不同的格式,分别有不同的作用,例如 WAIT ON 表示等待到信号变化,WAIT UNTIL 表示等到一个表达式为真,而 WAIT FOR 表示等待一个固定的事件,如果仅仅写一个 WAIT 的话就表示无限期的等待。
WAIT 语句能用于多种不同的目的,常用于为综合工具指定时钟输入。另一用途是将进程的执行延时一段时间或者是为了动态地修改进程敏感表。
为了避免无休止的等待可以加一个超时付句,不管进行到哪儿或是条件有没有满足都允许执行超时处理。下面的代码就演示了 WAIT UNTIL 语句的使用方法和超时处理的方法:
WAIT UNTIL (sendB = '1') FOR 1 ns;
ASSERT (sendB = '1')
REPORT "sendB timed out at '1'"
SEVERITY ERROR;
2).断言语句
断言语句的功能是为设计者报告一个文本字符串。断言语句包含一个布尔表达式,表达式为真,该语句不做任何事;反之,它将输出一用户规定的字符串到标准输出终端。
断言语句规定输出字符串的严重程度为 4 个级别(NOTE、WARNING、ERROR 和 FAILURE),它们的意思分别是注意、警告、错误和失败,严重层次递增。
断言语句的格式如下:
ASSERT_STATEMENT ::=
ASSERT CONDITION
[REPORT EXPRESSION]
[SEVERITY EXPRESSION];
其中,关键字 ASSERT 后跟 CONDITION 布尔值表达式,它的条件决定 REPORT 付句规定的文字表达式输出不输出,如果是假,文字表达式输出,如果是真,该文字表达式不输出。
此外还有两个可选的付句,REPORT 付句允许设计者指定输出文字表达式的值,如果不指定 REPORT 语句,默认值是 ASSERTION VIOLATION,SEVERITY 付句允许设计者指定断言语句的严重级别,如果没指定 SEVERITY 付句,其默认值是 ERROR。
下面是一个断言语句的使用实例,它表示对输入时钟进行检查,如果其建立时间小于20ns,则输出 ERROR 信号:
PROCESS (clk,din)
VARIABLE last_d_change :TIME := 0 ns;
VARIABLE last_d_value :std_logic := 'X';
VARIABLE last_clk_value :std_logic := 'X';
BEGIN
IF (last_d_value /= din) THEN -- /= is not equal
last_d_change := NOW;
last_d_value := din;
END IF;
IF (last_clk_value /= clk) THEN
last_clk_value := clk;
IF (clk = '1') THEN
-- 断言语句
ASSERT (NOW - last_d_change >= 20 ns)
REPORT "setup violation"
SEVERITY WARNING;
END IF;
END IF;
END PROCESS;
3).IF 语句
IF 语句是根据所指定的条件来确定执行哪些语句,其格式如下:
IF condition THEN
sequence_of_statements
ELSIF condition THEN
sequence_of_statements
ELSE
sequence_of_statement
END IF;
IF 语句用关键字 IF 开头和用关键字 END IF 结尾,END IF 分开拼写。有两个可选付句(ELSIF付句和 ELSE 付句),ELSIF 付句可重复并允许有多个 ELSIF 付句,可选 ELSE 付句但只允许有一个 ELSE 付句。付句中的条件是一布尔表达式,如条件为真值,则下一语句被执行;如果条件不为真,那么接着执行跟在 ELSE 付句后的顺序语句。
下面举一个 IF 语句的使用例子,如下:
IF (day = sunday) THEN
weekend := TRUE;
ELSIF (day = saturday) THEN
weekend := TRUE;
ELSE
weekday := TRUE;
END IF;
以上代码的意义如下:有两个变量 weekend 和 weekday,每当 day 等于 saturday 或 sunday时变量 weekend 变为真,执行跟着的下一句并控制转到跟在 END IF 之后的语句,否则转到 ELSIF语句部分并检查 day 是否为 Saturday;当变量 day 等于 saturday,执行跟着的下一句并再次控制转到跟在 END IF 之后的语句;若 day 并不等于 sunday 或 saturday,执行 ELSE 语句部分。
4).CASE 语句
当单个表达式的值在多个起作用的项中选择时用 CASE 语句。CASE 语句的格式如下:
CASE expression IS
WHEN choice1 =>
sequence_of_statements
WHEN choice2 | choice3 =>
sequence_of_statements
…
WHEN OTHERS =>
sequence_of_statements
END CASE;
下面是一个使用 CASE 语句执行处理器指令的例子:
CASE instruction IS
WHEN load_accum =>
accum <= data;
WHEN store_out =>
data_out <= accum;
WHEN load|store =>
process_IO(addr);
WHEN OTHERS =>
process_error(instruction);
END CASE
5).循环语句
当需要重复操作时用循环语句,或者实现的模块需要很强的迭代能力时用循环语句:
[循环标示 :] [循环条件] LOOP
顺序处理语句
END LOOP[LOOP_label];
其中循环条件可以用 WHILE 语句或者 FOR 语句来描述。WHLIE 语句有一个循环控制的条件 condition,只要条件表达式为真,WHILE 循环语句就一直执行下去,除非要退出循环。例如:
WHILE (day = weekday) LOOP
day := get_next_day(day);
END LOOP
FOR 循环是根据预先的设定进行迭代,所指定的范围并不一定必须为整数值,也可以表示成一个子类型的指示或者一个范围语句,例如:
PROCESS (clk)
TYPE day_of_week IS (sun,mon,tue,wed,thur,fri,sat);
BEGIN
FOR i IN day_of_week LOOP
IF i = sat THEN
son <= mow_lawn;
ELSIF i = sun THEN
church <= family;
ELSE
dad <= go_to_work;
END IF;
END LOOP;
END PROCESS;
FOR LOOP 语句的指数值(i)由 FOR 语句局部地说明,这和进程、函数和过程中变量 I 不是一会事,它不需要显式地说明,由于 FOR LOOP 语句的虚拟性,循环指数要局部说明之。这样在进程、函数或过程中存在同名变量时,它们会被分别处理并由它们的内含寻址。
此外,关于循环需要特别注意的是,在某些编程语言中循环指数的值可由赋予内部循环值来改变,但是 VHDL 中是不允许对循环指数的任何赋值,这排除了在任何函数返回值中或在过程的输出与双向参量中存在循环指数。
6).NEXT 语句
如果必须在这次迭代或循环中停下正在执行的语句,而转向下一个迭代时,用 NEXT 语句。执行 NEXT 语句时,模块处理停在当前点并转到循环语句的开始。随着循环的第一个语句执行,循环变量增加一个迭代值,直到迭代的限制值,循环停止。
下面是一个 NEXT 语句使用的例子:
PROCESS(A,B)
CONSTANT max_limit :INTEGER := 255;
TYPE d_type IS ARRAY (0 to max_limit) OF BOOLEAN ;
VARIABLE done : d_type;
BEGIN
FOR i IN 0 TO max_limit LOOP
IF (done(i) = TRUE ) THEN
NEXT;
ELSE
done (i) := TRUE;
END IF;
q(i) <= a(i) AND b(i);
END LOOP;
END PROCESS;
7).EXIT 语句
EXIT 语句提供完全停下循环执行的能力。执行期间发生了明显的错误或者所有的进程已执行完毕就跳出循环,EXIT 语句允许退出或跳出循环语句。执行 EXIT 语句后 EXIT 语句后面的语句暂停执行,去执行循环语句后面的语句。
EXIT 语句的基本书写格式如下:
EXIT [循环标号][WHEN 条件]
循环标号一般在多重循环中用于标明循环层次,如果 EXIT 语句后面添加循环标号,它将会退出循环标号指定的循环。“WHEN 条件”项用于表明 EXIT 语句执行的条件,此条件为真时才推出循环。
EXIT 语句的使用实例如下:
PROCESS (a)
BEGIN
first_loop:FOR i IN 0 TO 100 LOOP
second_loop:FOR j IN 1 TO 10 LOOP
......
EXIT second_loop;
......
EXIT first_loop;
END LOOP;
END LOOP;
END PROCESS;
4.2 VHDL 并行语句描述方法
VHDL 不仅仅提供了一系列的顺序语句,同样也提供了很多并行语句。在 VHDL 中,并行语句主要包括以下几种:
• 进程(PROCESS)语句;
• 块(BLOCK)语句;
• 并发信号赋值;
• 条件信号赋值;
• 选择信号赋值。
其中进程语句和块语句已经在结构体的描述方法中介绍过了,在此不再累赘,后面主要介绍余下的 3 种并行语句。
1).并发信号赋值
信号赋值就是使用信号赋值操作符“<=”修改一个信号的状态,如果此语句是在一个进程中,那么它是一个顺序语句,反之如果它是在进程外面(和进程并列关系),那么它就是一个并行赋值的语句。
下面是一个信号赋值的例子,其中 c1、c2 是顺序赋值的,c2 在 c1 之后赋值;d1 和 d2是并行赋值的,它们同时被赋值:
ARCHITECTURE arch of demo is
BEGIN
-- 并行赋值
d1 <= din
d2 <= din
-- 进程
PROCESS(din)
BEGIN
-- 顺序赋值
c1 <= din
c2 <= din
END PROCESS;
END arch;
2).条件信号赋值
条件信号赋值的格式如下:
目的信号 <= 表达式 1 WHEN 条件 1 ELSE
表达式 2 WHEN 条件 2 ELSE
表达式 3 WHEN 条件 3 ELSE
…
表达式 n;
最后一个表达式 n 表示以上 n-1 个条件都不满足时自动选用此表达式,如果有条件满足,则条件对应的表达式会计算赋值给目的信号量。条件信号代入语句也是并发描述语句,它可以根据不同条件将不同的多个表达式之一的值代入信号量。
下面通过一个四选一选择器的实现方法来介绍条件信号代入语句的使用方法:
ENTITY mux4 IS
PORT (
din0, din1, din2, din3,sel0,sel1: in bit;
dout: out bit );
END mux4;
ARCHITECTURE arch of mux4 is
SIGNAL sel : bit_vector(1 downto 0);
BEGIN
sel <= sel1 & sel0;
dout <= din0 WHEN sel = “00” ELSE
din1 WHEN sel = “01” ELSE
din2 WHEN sel = “10” ELSE
din3 WHEN sel = “11” ELSE
‘X’;
END mux4;
downto 从高位到低位
to 从低位到高位
3).选择信号赋值
选择信号赋值类似于 CASE 语句,它的格式如下:
WITH 表达式 SELECT
目的信号量 <= 表达式 1 WHEN 条件 1;
表达式 2 WHEN 条件 2;
表达式 3 WHEN 条件 3;
…
表达式 n WHEN 条件 n;
如果使用选择信号赋值实现上面的四选一选择器,代码如下:
ENTITY mux4 IS
PORT (
din0, din1, din2, din3,sel0,sel1: in bit;
dout: out bit );
END mux4;
ARCHITECTURE arch of mux4 is
SIGNAL sel : bit_vector(1 downto 0);
BEGIN
sel <= sel1 & sel0;
WITH sel SELECT
dout <= din1 when “00”,
dout <= din1 when “01”,
dout <= din2 when “10”,
dout <= din3 when “1”,
‘X” WHEN OTHERS;
END mux4;
五、 VHDL 语言的预定义属性
在 VHDL 中,属性是指关于设计实体、结构体、类型、信号等项目的制定特征,利用属性可以使得 VHDL 代码更加简明扼要、易于理解。
VHDL 提供了下面 5 类预定义属性:值类属性、函数类属性、信号类属性、数据类型类属性和数据范围类属性。
5.1 值类预定义属性
值类属性返回有关数组类型、块和常用数据类型的特定值,值类属性还用于返回数组的长度或者类型的最低边界,值类属性分成 3 个子类。
1).值类型属性:返回类型的边界
值类型属性用来返回类型的边界,有 4 种预定义属性:
• T'LEFT 用于返回类型或者子类型的左边界;
• T'RIGHT 用于返回类型或者子类型的右边界;
• T'High 用于返回类型或者子类型的上限值;
• T'Low 用于返回类型或者子类型的下限值。
用字符“'”指定属性并后跟属性名,“'”前的对象是所附属性的对象,字首大写“T”指所附属性的对象是类型(TYPE),“'”字符标点符号(tick)是 VHDL 特有的标号。
2).值类数组属性:返回数组长度
值类数组属性只有一个,即 LENGTH,该属性返回指定数组范围的总长度,它用于带某种标量类型的数组范围和带标量类型范围的多维数组。
3).值类块属性:返回块的信息
用属性'STRUCTURE 和'BEHAVIOR 返回有关在块和结构体中块是如何建模的信息。在块和结构体中如不含元件具体装配语句,则属性'BEHAVIOR 将返回真值,如果块或者结构体中只含元件具体装配语句或被动进程,则属性'STRUCTUTE 将返回真值。
5.2 函数类预定义属性
函数类属性为设计者返回类型、数组和信号信息。用函数类属性时,函数调用由输入变元的值返回一个值,返回值为可枚举值的位置号码、在一个△时间内信号是否改变的指示或者一个数组的边界。函数类属性可细分为 3 个常见的类别。
1).函数类型属性:返回类型值
函数类型属性返回类型内部值的位置号码、返回特定类型输入值的左和右边的值,函数类型属性分为 6 种:
• 'POS(value)返回传入值的位置号码;
• 'VAL(value)返回从该位置号码传入的值;
• 'SUCC(value)返回输入值后类型中的下一个值;
• 'PRED(value)返回输入值前类型中的原先的值;
• 'LEFTOF(value)表示立即返回一个值到输入值的左边;
• 'RIGHTOF(value)表示立即返回一值到输入值的右边。函数类型属性主要用于从可枚举数或物理类型的数转换到整数类型。
2).函数数组属性:返回数组的边界
函数数组类属性返回数组类型的边界,分 4 类:
• 数组'LEFT(n)返回指数范围 n 的左边界;
• 数组'RIGHT(n)返回指数范围 n 的右边界;
• 数组'HIGH(n)返回指数范围 n 的上限值;
• 数组'LOW(n)返回指数范围 n 的下限值。值类数组属性只有一个即 LENGTH,该属性返回指定数组范围的总长度,它用于带某种标量类型的数组范围和带标量类型范围的多维数组。
3).函数信号属性:返回信号历史信息
函数信号属性用来返回有关信号行为功能的信息,例如报告究竟一个信号是否正好有值的变化,报告从上次事件中跳变过了多少时间以及该信号原来的值是什么。
函数信号属性有如下 5 类:
• S'EVENT,如果当前的△时间期间发生了事件返回真,否则返回假(信号是否有值的变化);
• S'ACTIVE,如果在当前的△时间期间做了事项处理返回真,否则返回假;
• S'LAST_EVENT,返回从信号原先事件的跳变至今所经历的时间;
• S'LAST_VALUE,返回在上一次事件之前 S 的原先值;
• S'LAST_ACTIVE,返回自信号原先一次的事项处理至今所经历的时间。
5.3 信号类预定义属性
信号类属性用于根据另一个信号创建一些专用的信号,由类专用信号为设计者返回有关所附属性的信号信息(在一指定时间范围内该信号是否已经稳定的信息、在信号上有无事项处理的信息和建立的信号的延迟形式)。
对这类信号是不能在子程序内部使用的,返回的信息和由某种函数属性所提供的功能非常类似,区别是这类专用信号用于正常信号能用的任何场合,包括在敏感表中。有如下的 4 类属性:
• S'DELAYED[(time)] 建立和参考信号同类型的信号,该信号后跟参考信号和延时可选时间表示式的时间。'DELAYED 属性为信号建立延迟的版本并附在该信号上,它和传输延时信号赋值的功能相同,但简单。
• S'STABLE[(time)] 在选择时间表达式指定的时间内参考信号无事件发生时,属性建立为真值的布尔信号。
• S'QUIET[(time)] 参考信号或所选时间表达式指定时间内没事项处理时,属性建立一个为真值的布尔信号。
• S'TRANSACTION 信号上有事件发生或为每个事项处理而翻转它的值时,该属性建立一个 BIT 类型的信号。
5.4 数据类型类预定义属性
数据类型类的属性只有一个 t'BASE 类型属性,它必须由另一个值或函数类型属性用该属性。这个属性将返回类型或者子类型的基本类型,这个属性只能作另一属性的前缀。
5.5 数据范围类预定义属性
数据范围类属性返回数组类型的范围值,并由所选的输入参数返回指定的指数范围,这种属性标记如下:a'RANGE[(n)];a'REVERSE_RANGE[(n)]。
属性 RANGE 将返回由参数 n 值指明的第 n 个范围和按指定排序的范围,'REVERSE_RANGE将返回按逆序的范围,属性'RANGE 和'REVERSE_RANGE 也用于控制循环语句的循环次数。
REVERSE_RANGE 属性的用法和 RANGE 属性相类似,只是它按逆序返回一范围而已。 比如假设'RANGE 属性是返回 0 到 15,那么'REVERSE_RANGE 属性返回 15 下降到 0。
未校准笔记2
笔记2
作者:江南烟雨
链接:https://zhuanlan.zhihu.com/p/271054292
带有异步复位端的D触发器
带有异步复位端的D触发器
1.原理:(D触发器是靠时钟的上升沿触发的,rst低电平使能)
rst=1时,无论时钟是什么状态,q均被置为低电平(0)
rst=0时,时钟为上升沿,输入的值传送给输出,即d的波形传送给q
时钟为下降沿,q被置为低电平(0)
2.代码:
LIBRARY ieee;
USE ieee.std_logic_1164.all;
ENTITY data_flip_flop IS
PORT(d,clk,rst : IN STD_LOGIC;
q : OUT STD_LOGIC);
END data_flip_flop;
ARCHITECTURE bhv OF data_flip_flop IS
BEGIN
PROCESS(clk,rst)
BEGIN
IF (rst = '1')THEN
q <= '0';
ELSIF (clk 'EVENT AND clk = '1')THEN
q <= d;
END IF;
END PROCESS;
END bhv;
3.代码解析:
LIBRARY ieee;
USE ieee.std_logic_1164.all;
库声明格式如上。lLIBRARY,USE是VHDL保留的关键字(“;”分号表示语句结束, “--”两个减号表示后续文字为注释,“.”小数点表示谁的谁,包含关系<描述的不太准确,见谅>)。第一句声明设计中使用ieee库,第二句声明使用ieee库的std_logic_1164包中的所有内容
库种类:库分为ieee库,std库,work库。 ieee库列举常用的包:std_logic_1164:逻辑系统;std_logic_arith:数据类型的转换;std_logic_signed;std_logic_unsigned。
重点来啦:std库与work库在程序中是默认可见的,不需声明,只有ieee库使用前需要声明。
ENTITY data_flip_flop IS
PORT(d,clk,rst : IN STD_LOGIC;
q : OUT STD_LOGIC);
END data_flip_flop;
如上为实体结构(用来描述电路的输入输出引脚)语法结构如下:
ENTITY entity_name IS
PORT(
port_name : signal_mode signal_type;
port_name a,b,c : signal_mode signal_type;
END entity_name;
signal_mode有四种(IN;OUT;INOUT;BUFFER)
IN,OUT是单向引脚,INOUT是双向引脚,BUFFER输出一个引脚,但是输出信号可供电路内部使用 注意啦:OUT不能供电路内部使用
信号模式
signal_type包括BIT;STD_LOGIC;INTEGER
bit只有0,1两种状态
std_logice包括高阻(Z)等其他状态.
详细说明如下:
BIT是一个逻辑型的数据类型,端口为BIT类型时,该端口的信号取值只可能是“1”或“0”(“1”或“0”是值逻辑值),当端口为BIT_VECTOR时,该端口的取值可能是一组二进制的值(如某一数据总线输出端口具有8位的总线宽度,那么这样的总线端口的数据类型可以被说明为BIT_VECTOR).
STD_LOGIC是IEEE在1993年制定的新的标准(IEEE STD1164),它具有9种不同的值
'U'——初始值
'X'——不定
'0'——0
'1'——1
'Z'——高阻
'W'——弱信号不定
'L'——弱信号0
'H'——弱信号1
'-'——不可能的情况
考虑到D触发器是时序逻辑电路,输入信号d可能为其他形式的,用STD_LOGIC ???
ARCHITECTURE architecture_name(bhv) OF entity_name(data_flip_flop) IS
[declarations]
BEGIN
[code]
END architecture_name(bhv);
ARCHITECTURE部分为构造体(用来描述电路行为和实现功能),如上为语法结构 声明(需要时写):对信号和常量等进行声明 代码(BEGIN-END):描述电路行为和实现功能
PROCESS(clk,rst)
BEGIN
IF (rst = '1')THEN
q <= '0';
ELSIF (clk 'EVENT AND clk = '1')THEN
q <= d;
END IF;
END PROCESS;
PROCESS(进程) PROCESS(clk,rst)中(clk,rst)为敏感信号列表:敏感信号中的某个信号发生变化,PROCESS中的代码就顺序执行一次。类似与C中的条件 循环代码,VHDL的条件为信号的变化,如时钟信号的上升下降沿的出现,程序就会执行一次
clk 'EVENT:如果clk发生了变化,返回值为布尔值TRUE,否则返回FALSE,此处判断是否出现上升沿。IF(clk 'EVENT AND clk ='1')为搭配
IF语句是用于顺序代码的,只能用于PROCESS,FUNCTION,PROCEDURE中。 没什么好讲的,记住就可以了。格式为
IF condition(rst = '1')THEN assignments(q <= '0');
ELSIF condition(clk 'EVENT AND clk = '1')THEN assignments(q <= d);
……
ELSIF assignments;
END IF;
"<="给信号赋值,如仿真图,只有“0”,“1”两种电平
":=" 给变量赋值
默认情况下,ENTITY中的所有端口都是信号
VHDL不区分大小写
4.仿真
代码仿真软件:quartus 2
reference:VHDL数字电路设计教程
作者:江南烟雨
链接:https://zhuanlan.zhihu.com/p/354062442
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
感谢还在看我文章的同学们,谢谢。感谢催更的朋友,不可思议,本来是随缘更新,老脸一红,既然有人催更,我也就更点。开了很多坑,我慢慢填,先填VHDL的坑。
我只能按我的思路写写基础入门的知识,快速入门,为了通俗易懂,不会和教科书一样有些繁琐的定义,写的不好见谅,小李在这儿鞠躬说声抱歉,能力有限,杠精绕路不喜欢请点×。
国内Verilog或systemVerilog用的更多,但是VHDL结构严谨,使用简单,更适合入门。想学好一门代码语言,需要找例程多练。
写了将近三小时,手残党伤不起,我会尽量更新的
VHDL(1)简单说明了VHDL的结构框架(VHDL不区分大小写,为了看的舒服点用小写喽)
library ieee; --最基础的库就是这俩,根据自己写的程序增加库
use ieee.std_logic_1164.all;
--实体名与存储文件名要一致
--实体以我来看,只是定义一个封装好的电路的的输入输出,不要管内部电路多么复杂,只看输入输出
entity (实体名) is --所谓实体名就是为要实现的电路小模块起个名字
port([输入管脚名] : in [管脚类型]; --输入输出的管脚可以是多个,具体要根据电路图写
[输出管脚名] : out [管教类型]);
end(实体名); --实体名可写可不写
--结构体是干嘛的呢?描述功能
architecture 结构体名称 of 实体名 is
--定义一些结构体内部所使用的信号,常数,函数,数据类型
begin
--结构体并行语句部分,也就是begin里的语句并列执行,
--就像打仗时士兵每行5个,一行一行冲刺,5个是一起冲刺
--begin可以看作一行,每行有几个士兵呢,这就看你写了几条语句了。
end(结构体名称); --结构体名可写可不写
接下来我会用例子来学习VHDL
门电路:
与非门电路如下图,相信大家数电都学过
真值表
电路图可能与数电中不太一样,这儿把它封装好了。
什么是封装呢?简单来说,只有输入输出线和一个方框,方框里面有输入输出端口。方框里还有对应的功能,需要在结构体中写程序。
有人又要问了,为啥输入输出线,端口不写成一个呢?其实就像电脑充电线。把线插到电脑,通过线给电脑充电。同样,把线和端口连在一起,可以通过线给方框中传入数据。在实体中的定义正是把线和端口连在一起。
好的,可以写程序啦,我会用上面提到的框架,想要熟悉框架的每一部分,移驾专栏
江南烟雨:VHDL学习笔记(1)12 赞同 · 7 评论文章
方法一:简单粗暴函数法(nand函数)
library ieee;
use ieee.std_logic_1164.all;
entity nand2 is --我就自己做主了,起名nand2,想起啥起啥名,最好可读性强点,要不找不着了
port(a,b : in std_logic;
y : out std_logic);
end nand2;
architecture bhv of nand2 is
begin
y<=a nand b; --VHDL中将或,与非等等功能集合成逻辑表达式,直接使用即可
end bhv;
与 and,或or,非not,与或nand,或非nor,异或xor等等
y是信号,只能用<=,信号是电子电路内部硬件连接的抽象。而且信号可以延时,赋值会立刻执行。而且在定义类型时std_logic只有九种值,具体见学习笔记(1)
方法二:繁琐细致穷举法(就是按照真值表一个一个列举)
library ieee;
use ieee.std_logic_1164.all;
entity nand2 is
port(a,b : in std_logic;
y : out std_logic);
end nand2;
architecture bhv of nand2 is
begin
p1:process(a,b)
variable comb : std_logic_vector(1 downto 0);
begin
comb :=a & b;
case comb is
when "00" => y <= '1';
when "01" => y <= '1';
when "10" => y <= '1';
when "11" => y <= '0';
when others => y <= 'X';
end case;
end process p1;
end bhv;
穷举法就是根据真值表,把每一种可能的情况一一列举出来,别忘了std_logic有9种值,so 穷举中需要额外补充others,y被赋为高阻态。
process的具体定义见**学习笔记(1)**,这里只说明为什么要用它。
process(a,b)中a和b是敏感信号,只要a或b改变,process中的代码就顺序执行一次,是不是有点像C语言的判断循环,而VHDL一句话解决。这是VHDL的优势点。
两个输入,与非电路真值表有4种情况,我们可以把a,b捆绑放在一起,与真值表进行比对,然后给y赋值。
怎么实现呢?
首先我们需要一个变量来存放捆绑后的a,b值,代码来说就是a&b,*&可不是与哦!!!它是并置连接符,用于位的连接,形成位矢量。通俗来说,就是捆绑a,b 放在一个变量中* 之后就靠真值表一一对应了。真值表对应的y的值就是ab与非后的值,写程序的目的是能自动根据规则(真值表),输出相应的y值
于是乎,需要在process的begin之前定义一个变量comb
*注注注意啦,变量不能再结构体中定义,也只能在进程定义*
*因为comb是变量,赋值的时候用* :=
接着是大家熟悉的case 语句,有点像C中的switch case语句
case comb is
when "00" => y <= '1';
when "01" => y <= '1';
when "10" => y <= '1';
when "11" => y <= '0';
when others => y <= 'X';
end case;
首先这也是个框架,蓝色的部分都是必须的用法,没什么好讲的,我讲讲思路,具体代表的意义和注意点
思路:将上一步的comb与真值表中的ab对比,给y赋值,others则赋值高阻态
意义:
- comb所代表的是条件表达式的一种。case的中文意思有案例的意思,所以case后面需要条件(单个数值,并列数值,数值范围)
- when的中文意思是当……时,当comb是(具体的值)时,将要对y进行选择,=>,这个符号姑且可以看作是选择 when "01" => 这里01用双引号
- 赋值语句大家很熟悉啦 y <='1'
- 把2. 3. 放在一行就完成啦,别忘了分号结束
注意点:
- case语句无序,所有条件表达式的值并行处理
- 条件表达式的值必须列举穷尽,不能重复,不能穷尽的表达式用others
reference:VHDL数字电路设计教程
笔记3
作者:王万霖
链接:https://zhuanlan.zhihu.com/p/26066023
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
本文最后更新于:2018年5月1日,本次更新包括了较小的修复和改进
大家好,本专题从一个软件程序员的角度来向大家介绍VHDL语言的语法。
需要注意,本专题并不涵盖所有VHDL语言的元素。只是通过程序员的角度向大家解释一门语言,有助于大家理解VHDL并快速上手。
程序结构
我们通过分析下面的VHDL程序,来了解VHDL语言的程序结构。下面的VHDL程序用于驱动一个LED灯。当button接收到逻辑1时,LED灯亮起:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity buttonled is
Port (button : in STD_LOGIC;
led : out STD_LOGIC);
end buttonled;
architecture Behavioral of buttonled is
begin
process (button)
begin
if(button = '1') then
led <= '1';
end if;
end process;
end Behavioral;
我们可以看到,示例中的VHDL程序分为三部分,它们是:
- 说明语句
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
在我们专栏的实验中,以上四行说明语句将在我们的每个实验的程序代码中看到。 它们类似于我们在程序设计语言中使用的预处理语句——#include或者using。IEEE组织在“标准 1164”中第一次提出了IEEE VHDL库和std_logic类型。std_logic类型是我们专栏实验中最重要的数据类型。
- 实体部分
entity buttonled is
Port (button : in STD_LOGIC;
led : out STD_LOGIC);
end buttonled;
VHDL语言是一门硬件描述语言,实体部分描述了我们设计的元件的外部特征,包括元件的端口(输入输出)等信息。它类似于我们声明一个函数,而代码中的button就是函数的参数,函数在执行过程中对led进行了操作。
- 结构体部分
architecture Behavioral of buttonled is
begin
process (button)
begin
if(button = '1') then
led <= '1';
end if;
end process;
end Behavioral;
结构体部分描述了我们的元件有什么样的特性,它类似于我们对一个函数的定义,比如buttonled这个元件的作用是如果button的值为1,就将led的值设为1。
示例:下面的代码描述了一个2- 输入或门
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
ENTITY orgate2 IS
PORT(a, b : IN STD_LOGIC;
y : OUT STD_LOGIC);
END orgate2;
ARCHITECTURE or2 OF orgate2 IS
BEGIN
PROCESS(a, b)
BEGIN
y <= a OR b;
END PROCESS;
END or2;
提示1:你可能会发现, VHDL语言本身并没有涉及到有关管脚定义。在我们的实验0中,管脚定义是我们通过查阅开发板的用户手册,在另外的位置给出的。
提示2:你会发现,在本专题中出现的VHDL程序,部分示例关键词是大写的,部分是小写的。对于VHDL语言中的关键词,是大小写不敏感的。
实体部分
VHDL中实体的语法格式如下:
entity <实体名称> is
[generic(<类属表>);]
[port(<端口表>);]
end [entity] <实体名称>;
其中,端口表说明了我们声明的元件所有输入/输出端口都是什么。
示例:对于一个2选1-多路选择器,一种可能的实体声明为:
entity mux21a is
port(a, b : in bit; --输入的两个信号
s : in bit; --选择信号,当s为0时选a,s为1时选b
y : out bit);--输出信号
end mux21a;
提示1:在VHDL中,以双横杠--开头的是注释语句。
提示2:你会发现在示例中,s的端口模式是IN,而y的端口模式是OUT。在VHDL中一共有四种可用的端口模式,用来描述一个端口:
- IN(输入):该端口是只读的。
- OUT(输出):该端口是只写的。
- INOUT(双向):该端口既允许数据流入,也允许数据流出。
- BUFFER(缓冲):和INOUT类似,但区别在于当需要读取数据时,只能读取内部产生的反馈信号。
变量和信号
不同于我们在程序设计编程语言中常见的变量,如果我们需要存储一些数据,我们会有变量(Variable)和信号(Signal)两种方式可供选择。
*示例:变量和信号
…………
process (a, b) --a和b都是signal
variable c : std_logic := '0';
begin
--程序开始时,信号a的值为'0',信号b的值为'1'
c := a OR b; --c=a|b;此时c的值为'1'
a <= '1'; --给信号a进行赋值,a的值在process完成后才能被改变
c := a AND b;--c=a&b;此时c的值为'0',因为a还没有被改变,其值仍为'0'
end process;
…………
- 变量是暂时存储数据的局部量,只能在类似process的语句内部使用。是我们为了表达中间结果时临时起的名字。在实际电路中变量并没有存储。变量使用=符号赋值。
- 信号是电路中真实存在的量,被真实存储且有实际的物理意义。对信号的赋值会延迟生效。在信号定义时,使用:=符号赋值。在语句中要改变信号时,使用<=符号赋值。
数据类型
VHDL语言本身提供了以下原生的数据类型:
另外,在IEEE.STD_LOGIC_1164中提供了两种我们经常使用的数据类型:std_logic和std_logic_vector。它们能允许我们将数据与电路中的信号对应起来。其中,std_logic是枚举类型,可能的取值为:
其中,我们的专栏截止到目前进度,经常使用的取值是'1'和'0',分别标识信号的逻辑1和逻辑0。
std_logic_vector是std_logic的数组类型。
*示例:以下代码定义了一个3位长的std_logic_vector数组类型:
signal a : std_logic_vector (2 downto 0) := "000";
signal b : std_logic_vector (0 to 2) := "000";
*提示:我们发现,定义单个std_logic类型时,应当使用单引号包裹元素;而定义std_logic_vector矢量类型时,则使用双引号包裹元素。
语句
类似我们的其他程序设计语言,VHDL语言中也提供了比如判断和选择等结构的支持。我们可以在语句内部使用它们。
*示例1:以下代码判断std_logic_vector是否大于12,若是,则LED灯亮起。
……
process (a) --a是std_logic_vector
if (a > "1100") --2(1100)=10(12)
led <= '1';
else
led <= '0';
end if;
end process;
……
其中,在VHDL中可用的关系运算符如下:
= /= < > <= >=
在VHDL中可用的逻辑运算符如下:
AND OR NAND NOR XOR
题图经本人授权使用,采用知识共享署名-相同方式分享3.0协议发布。标签图经Mozilla授权,采用知识共享署名-相同方式分享3.0协议发布。本文中所有出现的代码以Apache 2协议发布,作者标注为DGideas。感谢
对于高阻态的指正。STD_LOGIC取值表由GNU Free Documentation License授权。
下一篇:《实验1:许多灯》
笔记4 重点语法
-
今天看到一篇博文,将VHDL语法基本包括了,先转过来备忘:VHDL基本点【精解】
VHDL描述硬件实体
结构
举例
-
Entity()实体
Enitiy 实体名 is
PORT(端口名1,端口名N:方向:类型)
[端口说明]
End Entity;
Port的方向有: IN , OUT, INOUT, BUFFER, LINKAGE
In 信号只能被引用,不能被赋值;不可以出现在<= 或 : = 的左边
out 信号只能被赋值,不能被引用;不可以出现在<= 或 : = 的右边
buffer 信号可以被引用,也可以被赋值;可以出现在<= 或 : = 的两边
Entity的内部结构将由Architecture来描述
-
Arcthitecture(构造体)
Arcthitecture 构造体名 of 实体名 is
[定义语句] 内部信号、常数、元件、数据类型、函数等的定义
begin
[并行处理语句和block、process、function、procedure]
end 构造体名;
例如:
下面让我们来看一看如何将一个实体(FULLADD)构造为一个内部能实现功能的构造体(STRUCT):
-
其他设计单元
除了entity(实体)和architecture(构造体)外还有另外三个可以独立进行编译的设计单元
Package(包集合)属于库结构的一个层次,存放信号定义、常数定义、数据类型、元件语句、函数定义和过程定义。
Package Body: 具有独立对端口(port)的package
**configuration(**配置)描述层与层之间的连接关系以及实体与构造体之间关系
4、Library(库)
库: 数据的集合。内含各类包定义、实体、构造体等
-
STD库 --VHDL的标准库
-
IEEE库 -- VHDL的标准库的扩展
-
面向ASIC的库 --不同的工艺
-
不同公司自定义的库
-
普通用户自己的库 --当VHDL文件被编译后,编译的结果储存在特定的目录下,这个目录的逻辑名称即Library,此目录下的内容亦即是这个Library的内容。
-
Package(包)
-
这些结构之间的关系
VHDL对象、操作符、数据类型
1、VHDL中的对象(对客观实体的抽象和概括)有:
Constant(常量)在程序中不可以被赋值
Variable(变量)在程序中可以被赋值(用": ="),赋值后立即变化为新值。
Signal(信号)在程序中可以被赋值(用"<=") ,但不立即更新,当进程挂起后,才开始更新。
例如:
variable
x,y:integer;--定义了整数型的变量对象x,y
constant
Vcc:real;--定义了实数型的常量对象Vcc
signal
clk,reset:bit;--定义了位类型的信号对象clk,reset
注意:
1、variable只能定义在process和subprogram(包括function和procedure)中,不可定以在其外部。
2、signal不能定义在process和subprogram(包括function和procedure)中,只可定以在其外部。
对象的属性(类似于其它面向对象的编程语言如VB、VC、DELPHI):
用法格式:对象 ' 属性
例子:clk'event --表明信号clk的event属性
常用的属性:
Signal 对象的常用属性有:
event : 返回boolean值,信号发生变化时返回true
last_value:返回信号发生此次变化前的值
last_event:返回上一次信号发生变化到现在变化的间隔时间
delayed[(时延值)]: 使信号产生固定时间的延时并返回
stable[(时延值)]: 返回boolean, 信号在规定时间内没有变化返回true
transaction: 返回bit类型,信号每发生一次变化,返回值翻转一次
例子:A<=B'delayed(10 ns); --B延时10ns后赋给A;
if(B'Stable(10 ns)); --判断B在10ns中是否发生变化
应用:
信号的event和last_value属性经常用来确定信号的边沿
例子:
判断clk的上升沿
if ( (clk'event)and (clk='1') and(clk'last_value='0')) then
判断clk的下降沿
if ( (clk'event)and (clk='0') and(clk'last_value='1')) then
2、VHDL的基本(数据)类型
bit(位):
0
和1
bit-Vector(位矢量): 例如:
00110
Boolean " ture"和"false"
time 例如:1 us、100 ms,3 s
character 例如:'a'、'n'、'1'、 '0'
string 例如:"sdfsd"、"my design"
integer 32位例如:1、234、-2134234
real 范围-1.0E38~+1.0E38 例如:1.0、2.834、3.14、0.0
natural 自然数 和 positive 正整数
senverity level (常和assert语句配合使用)包含有:note、warning、error、failure
以上十种类型是VHDL中的标准类型,在编程中可以直接使用。使用这十种以外的类型,需要自行定义或指明所引用的Library(库)和Package(包)集合
例子:
(1)
(2)
(3)
信号Z有两个驱动A和B;Z必须定义为一种新的数据类型,否则Z将无法决定取值,语句视为非法。
(4)
(5)
要点:赋值语句中的方向应和声明中的方向一样
3、常用操作
连接操作符---&
集合操作---()
集合操作---序号
集合操作--采用others
- 定义自己的类型
通用格式
*TYPE* 类型名 IS 数据类型定义
用户可以定义的数据类型:
枚举类型enumberated、整数型integer、
实数型real、数组类型array、
纪录类型record、时间类型time、
文件类型file、存取类型access
(1)
- 枚举类型enumberated
- 格式
*type* 数据类型名 *is* (元素,元素…...);
- 例子
type week is (sun,mon,tue,thu,fri,sat);
type std_logic is ('1','0','x','z');
(2)
- 整数类integer和实数类real
- 格式
type 数据类型名 is 数据类型定义 约束范围;
- 例子
type week is integer range 1 to 7;
type current is real range -1E4 to 1E4
(3)
- 数组类型array
- 格式
*type* 数据类型名 *is array* 范围 *of* 元数据类型名
- 例子
type week is array (1 to 7) of integer;
type deweek is array (1 to 7) of week;
(4)
- 时间类型time
- 格式
*type* 数据类型名 *is* 范围
*units* 基本单位;
单位;
*end units*
- 例子
type time is range -1E18 to 1E18
units
us;
ms=1000 us;
sec=1000 ms;
min=60 sec;
end units
*注意*:引用时间时,有的编译器要求量 与单位 之间应有一个空格如:1 ns;不能写为1ns
(5)
- 纪录类型record
- 格式
*type* 数据类型名 *is* *record*
元素名:数据类型名;
元素名:数据类型名;
….
*end record;*
- 例子
type order is record
id:integer;
date:string;
security:boolean;
end record;
引用:signal flag:boolean;
signal order1:order;
order1<=(3423,"1999/07/07",true);
flag<=order1.security;
- IEEE 1164中定义的类型
(1)std_ulogic 是对位(bit)类型的扩展,只允许一个驱动源
(2)Std_logic同std_ulogic 一样有九个状态,允许一个或多个驱动源
与std_ulogic的区别
(3)Std_unlogic_vector和std_logic_vector
Std_unlogic、std_ulogic_vector
std_logic_vector和 std_unlogic_vector 类型
均定义在package(包) standard_logic_1164中
在使用这四种类形时应加以说明,
例如:library ieee;
use ieee.std_logic_1164.all;
-
VHDL中的操作符
(1)逻辑操作符有:
(2)逻辑操作符的应用例子
(3)关系运算符有
(4)关系运算符的应用
(5)数学运算符
***注意:***上述运算符应用于 integer,real,time 类型,不能用于vector(如果希望用于vector,可以使用库IEEE的std_logic_unsigned包,它对算术运算符进行了扩展)
(6)VHDL中的操作符应用要点
VHDL属于强类型,不同类型之间不能进行运算和赋值,可以进行数据类型转换
vector不表示number
array 不表示number
VHDL中的控制语句及模块
- 基本概念
- 并行处理(concurrent):语句的执行与书写顺序无关,并行块内的语句时同时执行的
- 顺序处理(sequential): 语句的执行按书写的先后次序,从前到后顺序执行。这种方式和其他普通编程语言(如c,pascal)是一样的。
2、并行和顺序处理在vhdl中的应用
(1)Architecture 中的语句及子模块之间是并行处理的
(2)子模块block中的语句是并行处理的
(3)子模块process中的语句是顺序处理的
- process例子-值的更新
分析:
当A、B、C、D中任一信号发生变化时,进程将开始执行,当执行 Z <= A and B 后, Z 的值不会立即变化 ;同理执行 Z <= C and D 后Z 的值也不会立即变化。当执行end process后, Z 的值才开始更新,同时系统挂起开始等待敏感信号。
- Process中敏感信号列表的普遍原则是:
在process中,其值被引用的信号应当出现在敏感信号列表中
反例:
可见不符和设计要求
(4)子模块subprogram中的function和procedure是顺序处理的
-
顺序执行语句(sequential statement)
Wait语句
assert语句
If 语句
case语句
for loop语句
while 语句
-
并行处理语句(concurrent statement)
并行操作中的信号赋值
- 信号赋值操作
- 符号"<="进行信号赋值操作的,
- 它可以用在顺序执行语句中,
- 也可以用在并行处理语句中
- *注意*
*用在并行处理语句中时,符号<=右边的值是此条语句的敏感信号,*即符号<=右边的值发生变化就会重新激发此条赋值语句,也即符号<=右边的值不变化时,此条赋值语句就不会执行。如果符号<=右边是常数则赋值语句一直执行。*
*用在顺序执行语句中时,没有以上说法。
- 选择信号带入语句格式
with 表达式 select
目的信号量 <= 表达式1 when 条件1,
表达式2 when 条件2,
…..
表达式n when 条件n;
- 选择信号带入语句例子
- 顺序执行语句和并行处理语句总结
1、顺序执行语句 wait、assert、if -else 、case、for-loop、while语句只能用在process、function 和 procedure 中;
2、并行处理语句(条件信号带入和选择信号带入)只能用在architecture、block中;
其它语句
例子:
*entity* testand2 *is*
port(ain,bin: in nit ; cout:out bit);
*end* testand2;
*architecture* behav *of* testand2 *is*
component and2
generic(rise:time); port(a,b: in nit ; c:out bit);
end component;
*begin*
c<=(a xor b) after (rise);
u0:and2 generic map(20 ns) port map(ain,bin,cout);
- end behav
-
笔记5 入门
文章目录
0、背景
明明已经掌握了verilog HDL,为什么还要掌握VHDL呢。因为,你如果不掌握VHDL,意味着,你将要丢失很多资料,不少书籍,不少资料还是用VHDL实现的,包括国外的数字信号处理。在开启这个VHDL文档之前,我其实在YouTube上面已经大概感受过VHDL了,这样非常有助于按照书籍来学习,直接看语法书籍,可能效果甚微。下面,我们只需要使用语法书籍按班就部的地学习即可。
1、VHDL入门体验
1.1 2选1选择器
library ieee;
use ieee.std_logic_1164.all; /*IEEE库使用说明*/
ENTITY mux21 IS /*器件mux21的外部接口信号书名,port相当于器件的引脚*/
port (a,b: in std_logic;
s: in std_logic;
y: out std_logic);
end entity mux21;
architecture one of mux21 is /*器件mux21的内部工作逻辑描述,即为实体描述的器件功能机构*/
begin
y<=a when s='0' else
b when s='1';
end architecture one;
12345678910111213141516171819
解释:
实体ENTITY说明:实体的电路意义相当于器件,在电路原理图上相当于元件符号。实体是一个完整的、独立的语言模块,它描述了接口信息,port所描述的就相当于电路器件的外部引脚。同时指明了端口a\b\s和y信号的数据类型是符合IEEE库中std_logic_1164程序包中的标准数据位,std_logic中所定义的数据类型。
**结构体(architecture)**说明:描述mux21的内部逻辑功能,在电路上相当于器件的内部电路结构。符号“<=”是信号赋值符,是信号传递的意思,“y<=a”表示将a获得的信号赋给y输出端.
需要指出来的是,实体和结构体分别是以“END ENTITY MUX21”和“END ARCHITECTURE ONE”语句结尾的,这是符合VHDL新版本 IEEE TSD 1076_1993的语法要求的,若根据VHDL 87版本 这两条结尾语句只需要写成end mux21 和end one即可。
一个可综合的VHDL描述的最小和最基本的逻辑结构中,IEEE标准库说明、实体和结构体是最基本的和不可缺少的三个部分。
1.2 锁存器的设计
锁存器的引脚D是数据输入端口,ENA是数据锁存使能控制端口,当ENA为高电平时,允许数据锁入;低电平时进制数据锁入,Q为数据输出端口。
也就是说Q始终是随着D的,C是锁存器的控制信号。
library ieee;
use ieee.std_logic_1164.all;
entity Latch_p is
port(D:in std_logic;
ena:in std_logic;
Q:out std_logic
);
end entity ;
architecture one of Latch_p IS
signal sig_save:std_logic;
begin
process (D,ena)
begin
if ena='1' then
sig_save<=D;
end if;
Q<=sig_save;
end process;
end architecture one;
12345678910111213141516171819202122
SIGNAL由信号赋值语句SIGNAL…定义了一个信号变量,它的功能是存储来自外部信号D的被锁存的数据位。
使用了一个进程语句结构PROCESS(D,ENA)–》END PROCESS
进程结构中的逻辑描述表明,如果使能控制信号ENA为高电平,则将数据输入端D的最新值传入信号变量sig_save,然后通过sig_save,将此值传给信号输出端Q;反之,若使能控制信号ENA为低电平,则将sig_save上的原值传给信号输出端Q,即保留原值不变。
程序中的IF—THEN语句结构所给出的描述方式,是令VHDL综合器引入时序元件的常用方法,其特点是当IF引导的逻辑表达式满足条件时,作数据传入操作:sig_save<=D,而当不满足条件时,不作任何描述就以END IF 结束IF–THEN语句
程序行“PROCESS(D,ENA)”中的(D,ENA)称为敏感信号表,这说明信号D和ENA中的任何信号发生变化时,都将引起本进程的执行
1.3 总结与注释
在VHDL中注释采用–
2、VHDL顺序执行
每一条顺序语句的执行(指的是仿真执行)顺序与他们的书写顺序基本是一致的,顺序语句只能出现在进程process和子程序中,子程序包括function和procedure(函数和过程)
但应该值得注意的是,这里的顺序是从仿真软件的运行或者顺应VHDL语法的编程逻辑思路而言的,其相应的硬件逻辑工作方式并未如此。关于这点,希望要注意区分VHDL语言的软件仿真行为及描述综合后的硬件行为间的差异。
在CHDL中,一个进程是由一系列顺序语句偶成的,而进程本身属于并行语句。
VHDL中有如下6类基本顺序语句:
赋值语句
流程控制语句
等待语句
子程序调用语句
返回语句
空操作语句
2.1 赋值语句
VHDL严格规定了,赋值目标与赋值源的数据类型必须严格一致。
变量赋值目标 := 赋值源;
信号赋值目标 < = 赋值源;
在信号赋值中,有2点需要注意,第一点是,当在同一进程中,同一信号赋值目标有多个赋值源时,信号赋值目标获得的是最后一个赋值源的赋值,其前面相同的赋值目标不作任何变化。
variable a,b:std_logic;
signal c1:std_logic_vector(1 to 4);
a:='1';
b:='0';
c1:="1100";--其中a\b\c1都属于标识符赋值目标
signal a,b:std_logic_vector (0 to 3);
signal i:integer range 0 to 3;
signal y,z:std_logic;
a<="1010";
b<="1000";
a(3)<=z;
variable a,b:std_logic_vector(1 to 4);
a(1 to 2):="10";
a(1 to 4):="1011";--段下标元素赋值目标
signal a,b,c,d:std_logic;
signal s:std_logic_vector(1 to 4);
variable e,f:std_logic;
variable g:std_logic_vector(1 to 2);
variable h:std_logic_vector(1 to 4);
s<=('0','1','0','0');
(a,b,c,d)<=s;
12345678910111213141516171819202122232425
2.2 流程控制语句
流程控制语句通过条件控制开关决定是否执行一条或几条语句,或重复执行一条或几条语句,或跳过一条或几条语句。
流程控制语句共有五种:
if
case
loop
next
exit
2.21 if语句
与第一种IF语句相比较,第二种IF语句差异仅仅在于当所测条件为false时候,并不直接跳到end if结束条件句的执行,而是转向else以下的另一段顺序语句进行执行。
library ieee;
use ieee.std_logic_1164.all;
entity mux2_2_1 is
port (din: in std_logic_vector(0 to 7);
output:out std_logic_vector(0 to 2) );
end mux2_2_1;
architecture behave of mux2_2_1 is
signal sint: std_logic_vector (4 to 0);
begin
process (din)
begin
if(din(7)='0')then
output<="000";--(din(7)='0')
elsif (din(6)='0') then
output <="100" ; --(din(7)='1') AND din(6)='0')
elsif (din(5)='0') then
output<="010";--(din(7)='1'AND (din(6)='1')AND(din(5)='0')
elsif (din(4)='0') then
output<="110";
elsif (din(3)='0') then
output<="001";
elsif (din(2)='0') then
output<="101";
elsif (din(1)='0') then
output<="011";
else
output<="111";
end if;
end process;
end behave;
123456789101112131415161718192021222324252627282930313233
2.22 case语句
除非所有条件句中的选择值能完整覆盖case语句中表达式的取值,否则最末一个条件句中的选择必须用“others”表示,它表示已给的所有条件句中未能列出其它可能的取值。关键词others只能出现一次,且作为最后一种条件取值。使用others的目的是为了使条件句中的所有选择值能够涵盖表达式的所有取值,以免综合器会插入不必要的锁存器。这一点对于定义为std_logic和std_logic_vector数据类型的值尤其重要,因为这些数据对象的取值除了1和0以外,还可能有其他的取值,如高阻态z\不确定态x等
library ieee;
use ieee.std_logic_1164.all;
entity mux4_1 is--声明一个实体
port(s1,s2:in std_logic;
a,b,c,d: in std_logic;
z:out std_logic);
end entity;
architecture activ of mux4_1 is
signal s: std_logic_vector (1 downto 0);
begin
s<=s1&s2;
process (s,a,b,c,d)--注意,这里必须要以s为敏感信号,而并非以s1和s2
begin
case s IS
when "00" => z<=a;
when "01" => z<=b;
when "10" => z<=c;
when "11" => z<=d;
when others => z<='X'; --注意,这里的X必须大写代表高阻态
end case;
end process;
end activ;
1234567891011121314151617181920212223242526
与if语句相比较,case语句组的程序可读性比较好,这是因为它把条件中所有可能出现的情况全部例了出来,可执行条件一目了然。而且case语句的执行过程不像if语句那样有一个逐项条件顺序比较的过程。case语句中的条件句的次序是不重要的,它的执行过程更接近于并行方式。一般的,综合后,对相同的逻辑功能,case语句比if的描述好用更多的硬件资源,不但如此,对于有的逻辑,case无法描述,只能用if语句来描述。
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
entity mux4_1 is
port (a,b:in std_logic_vector(7 downto 0);
opcode:in std_logic_vector(1 downto 0);
result: out std_logic_vector(7 downto 0));
end entity mux4_1;
architecture behave of mux4_1 is
constant plus : std_logic_vector (1 downto 0):=b"00";
constant minus : std_logic_vector (1 downto 0):=b"01";
constant equal : std_logic_vector (1 downto 0):=b"10";
constant not_equal : std_logic_vector (1 downto 0):=b"11";
begin
process (opcode,a,b)
begin
case opcode is
when plus => result <=a+b; --a\b 相加
when minus => result<= a-b; --a\b 相减
when equal => --a\b相等
if (a=b) then result <=x"01";
else result <=x"00";
end if;
when not_equal=> --a\b不相等时候
if (a/=b) then result<=x"01";
else result<= x"00";
end if;
end case;
end process;
end behave;
1234567891011121314151617181920212223242526272829303132
Q.E.D.