本教程使用最新的VHDL标准VHDL-2008,该标准可以提高设计人员的工作效率,并且适合用于数字设计入门课程。目前大多数编译器都支持VHDL-2008,但可能默认使用早期版本,所以在编译VHDL-2008之前,修改EDA设置。为了保持简洁准确的描述,VHDL教程使用拓展的巴科斯范式注释。尖括号<>内的内容为必选项,::=是“被定义为”的意思,竖线|表示任选一个,{}大括号内的内容为可重复0至无数次的项,[]内的内容为可选项。(不过不需要过度关注这些,每个讲解都带有例子)
一、注释、标识符和关键字
在VHDL中,单行注释前面带有(–)。VHDL-2008引入了多行注释,即以/*开头,以*/结束。
VHDL中的关键字和标识符不区分大小写。
标识符必须以字母开头,并且可以包含任意数量的字母、数字(0-9)和下划线。
末尾字符不能为下划线。
VHDL中定义的关键字不能作为标识符。
不允许出现两个连续的下划线。
例如:
合法标识符:Mux3、state_next
非法标识符:-Deal(起始不能为非英文字母)30Q(起始不能为数字)For(关键字不能作为标识符)
VHDL-2008定义了115个关键字,相比较其它编程语言算是很多了,但在学习时,不需要学习那么多,常用的一些知道即可。
二、类型
VHDL是一种强类型语言,每一个变量在使用前都必须声明类型。下图是一些相对常用的VHDL-2008关键字。
2.1 Std_logic
VHDL设计中常用的类型是std_logic。std_logic类型的信号或变量表示其值是单比特的。在电子电路中,使用电压来表示0和1的逻辑值。在VHDL规范中,这些逻辑值可以用std_logic值0或1来表示。
2.2 Boolean
逻辑比较操作的结果被预定义为boolean,与std_logic不一样的是,它的值为“真
“或”假“。当需要boolean时,VHDL-2008可以自动将一个std_logic转化为boolean类型。
2.3 Integer
Integer(整数)和std_logic_vector类型不一样,它的数值范围为
Integer通常用来定义参数,比如一个多比特的std_logic_vector信号的宽度。Integer类型通常不用来存储电路计算得到的数据。Integer类型采用十进制书写方式,使用数字0-9。例如,数值100就写成100(不需要加引号)
2.4 Std_logic_vector
VHDL使用std_logic_vector类型来表示多比特信号,它由std_logic构成的数组。要完全指定std_logic_vector类型,必须包含索引范围。
例如:std_logic_vector(2 downto 0)是一个3比特std_logic_vector,索引值是从2到0。
2.5 子类型
如果一个定义的std_logic_vector类型出现的次数很多,给它一个名字会方便编码,用户定义的子类型就可以实现这一功能。
如下:subtype <名称> is <子类型> ;
例如:subtype state_type is std_logic_vector( 2 downto 0 );
声明一个名叫 state_type的子类型,它的数据类型是3比特的std_logic_vector。
2.6 枚举类型
VHDL提供了如下语法来定义枚举数据类型:
type<名称> is( <枚举元素名称> {, <枚举元素名称>} );
例如:type state_type is ( SA, SB, SC, SD );
声明了一个state_type的新类型,其值可以为SA、SB、SC或SD。
2.7 数组和记录类型
VHDL提供两个可以将数据类型集合起来的复合类型:数组和记录。数组适合定义存储器,定义一个数组类型的语法如下:
type<数组名称> is array(<索引范围>) of <子类型>;
例如:type mem_type is array(0 to 255) of std_logic_vector(15 downto 0);
声明了一个新的数据类型,名字为 mem_type,它由256个16比特的类型为std_logic_vector(15 downto 0)的数构成的数组。
如果mem_type的类型为memory,那么memory(0)指的就是memory中的第一个字,类型为std_logic_vector(15 downto 0)。
VHDL中的记录类型与C语言中的struct有些类似,可以将相关的信号集合到一根信号总线上。记录类型的另一个作用就是在一个进程内用一个信号赋值语句对逻辑组合块的所有输出进行赋值。声明一个记录类型的语法如下:
type<记录名称> is record
<元素声明>
{<元素声明>}
end record;
其中,
<元素声明>::= <名称> {, <名称> } : <子类型> ;
例如:
type inst_type is
record
opcode : std_logic_vector(6 downto 0);
dst : std_logic_vector (2 downto 0);
src1 : std_logic_vector(2 downto 0);
src2 : std_logic_vector(2 downto 0);
end record;
声明了一个inst_type的记录类型,它包含4个元素,分别是opcode、dst、src1和src2
从记录中提取元素时,应该使用“.”。例如上面的记录类型,如果inst_type的类型为inst的信号,那么提取元素opcode需要使用这样的结构inst.opcode。
三 库、包集合和使用多文件
类型std_logic_vector和std_logic不是VHDL语言的一部分,而是IEEE正式认可的标准包集合。为了在设计中使用std_logic_vector和std_logic必须添加行“library ieee;”和“use ieee.std_logic_1164.all;”
四 设计实体
硬件设计的VHDL语言由具有一定层次的VHDL设计实体构成。设计实体定义了硬件快的接口以及内部结构。每个设计实体由两部分组成:实体声明和结构体。实体声明包含输入和输出信号的端口声明列表;结构体包含声明部分(各种声明)和结构体描述部分(包含一系列并行处理语句)。设计实体的逻辑由并发语句实现。如下:
entity <实体名称> is
[ generic( <类属常量> ); ]
port( <端口声明 > );
end <实体声明>;
architecture <应用名称或者叫结构名称> of <实体名称> is
<类型声明>
<常量声明>
<元件声明>
<内部信号声明>
begin
<并发语句>
end <应用名称或者叫结构名称>;
实体声明以关键字entity开始,它通过端口的声明列表来规定硬件设计内部和外部之间的接口。实体声明还可以通过改变类属常量的值来改变设计实体。可以使用关键字generic(英文意思是:通用的)在实体声明中声明类属常量,该声明可有可无(看设计需要)。
类属常量最常用于参数化一个设计实体的输入和输出的位宽。端口说明语句描述了一个设计实体的输入输出,端口的模式表示信号是输入(in)、输出(out)、可读的输出(buffer)和双向信号(inout)。
结构体以关键字architecture开始,它分为声明部分和描述部分。声明部分包含对结构体的声明,描述部分包含实现逻辑功能的并发语句。这两个部分由关键字begin分隔开。
声明部分可能包含类型、常数、元件和内部信号声明。
元件声明可以通过例化,在一个设计实体的结构体中使用另一个设计实体。元件声明的语句类似于实体声明:
component<元件声明>i
[ generic( <类属常量> );
port( <端口声明> );
end component;
五、操作符和表达式
第一行中的“∗∗”运算符是乘方运算符。逻辑运算符“not”是按位操作的,因此not 4b”1001″的结果是4b”0110″
第二三行中乘法(∗),除法(/), 模 (mod), 加(+),减法(-),和并置运算符用于位连接(&)
第四行包含移位和循环移位运算符。移位运算符 (sll,srl, sla, sra)以“s”开始,后跟方向(左移为“l”,右移“r”),如果是逻辑移位,则最后为“l”(logic)。如果是算数移位,那么最后为“a”(arithmetic)。关键字ror表示向右循环移位(rotate to the right),rol表示向左循环移位(rotate to the left)。
第五行包含比较运算符,它们操作的对象是类型相同的两个操作数,并产生类型为boolean的结果。“=”(等于),“/=”(不等于),<(小于),>(大于),<=(小于等于),>=(大于等于)
第六行中是逻辑运算符,and(与),or(或),nand(与非),nor(或非),xor(异或),xnor(异或非)。
第七行,VHDL-2008中的条件运算符“??”将std_logic的值转化为boolean。
六、并发语句
6.1并发条件赋值
<信号名字> <= <表达式> when<条件> else
{ <表达式> when<条件> else}
<表达式>;
例:
Enc <= “11” wheninput(3) = ’1’ else
“10” wheninput(2) = ’1’ else
“01” wheninput(1) = ’1’ else
“00”;
当input(3)等于1时,Enc被赋予值“1”。如果input(3)不等于1,而input(2)等于1时,Enc被赋予值“10”。如果input(3)不等于1,input(2)不等于1,而input(1)等于1时,Enc被赋予值“01”。在所有其它情况下,Enc被赋值为“00”。这里注意,这个结构有严格的执行顺序和优先级,仅有上一个语句不成立才会进入下一语句执行。
6.2 并发选择信号赋值
并发选择信号语句的形式如下:
with<信号名称1>select
<信号名称2> <= { <表达式 > when<选择>, }
<表达式> when others;
例:
with sel select
output <= in_a when2d”3″,
in_b when2d”1″ | 2d”2″,
in_c when others;
例子描述了一个四路复用器构建的组合逻辑电路。该电路具有in_a, in_b,in_c,和sel四个输入信号。如果sel等于2d”3″,则将in_a值赋给output,如果sel值等于2d”1″或2d”2″,那么把in_b值赋给output。其它情况,则将in_c赋给output。
注:条件信号赋值和选择信号赋值之间区别,选择信号赋值中的选择条件是不允许重叠的,而条件信号赋值中的条件是可以重叠的。条件信号赋值中的条件是按照顺序有优先级的。选择信号的赋值的选择条件必须是一个常数,而条件信号赋值的条件可以是任意一个表达式。
6.3 元件例化
元件例化语句形式如下
<元件实例名>: <元件> [ generic map( <关联列表> ) ] port map( <关联列表> );
上例中的generic map部分是可选的。如果存在这个语句,那么generic map可以改变被例化的设计实体中的generic所声明的常量。简单来讲,generic map可以覆盖掉被例化实体的generic声明。
总结:原文英文版(Digital Design Using VHDL A Systems Approach),大多数内容来自该书,但也作了部分的删减,也从其它教程中进行添加。下一期会把基本的语法结束,然后开始更新编码风格。最近的计划是时序和状态机(交通灯)
没有回复内容